diff options
Diffstat (limited to 'tools/perf/util/symbol.c')
| -rw-r--r-- | tools/perf/util/symbol.c | 132 | 
1 files changed, 123 insertions, 9 deletions
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index e2ba8858f3e..50958bbeb26 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -323,6 +323,7 @@ struct dso *dso__new(const char *name)  		dso->sorted_by_name = 0;  		dso->has_build_id = 0;  		dso->kernel = DSO_TYPE_USER; +		dso->needs_swap = DSO_SWAP__UNSET;  		INIT_LIST_HEAD(&dso->node);  	} @@ -1156,6 +1157,33 @@ static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)  	return -1;  } +static int dso__swap_init(struct dso *dso, unsigned char eidata) +{ +	static unsigned int const endian = 1; + +	dso->needs_swap = DSO_SWAP__NO; + +	switch (eidata) { +	case ELFDATA2LSB: +		/* We are big endian, DSO is little endian. */ +		if (*(unsigned char const *)&endian != 1) +			dso->needs_swap = DSO_SWAP__YES; +		break; + +	case ELFDATA2MSB: +		/* We are little endian, DSO is big endian. */ +		if (*(unsigned char const *)&endian != 0) +			dso->needs_swap = DSO_SWAP__YES; +		break; + +	default: +		pr_err("unrecognized DSO data encoding %d\n", eidata); +		return -EINVAL; +	} + +	return 0; +} +  static int dso__load_sym(struct dso *dso, struct map *map, const char *name,  			 int fd, symbol_filter_t filter, int kmodule,  			 int want_symtab) @@ -1187,6 +1215,9 @@ static int dso__load_sym(struct dso *dso, struct map *map, const char *name,  		goto out_elf_end;  	} +	if (dso__swap_init(dso, ehdr.e_ident[EI_DATA])) +		goto out_elf_end; +  	/* Always reject images with a mismatched build-id: */  	if (dso->has_build_id) {  		u8 build_id[BUILD_ID_SIZE]; @@ -1272,7 +1303,7 @@ static int dso__load_sym(struct dso *dso, struct map *map, const char *name,  		if (opdsec && sym.st_shndx == opdidx) {  			u32 offset = sym.st_value - opdshdr.sh_addr;  			u64 *opd = opddata->d_buf + offset; -			sym.st_value = *opd; +			sym.st_value = DSO__SWAP(dso, u64, *opd);  			sym.st_shndx = elf_addr_to_index(elf, sym.st_value);  		} @@ -1447,14 +1478,31 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size)  		goto out;  	} -	sec = elf_section_by_name(elf, &ehdr, &shdr, -				  ".note.gnu.build-id", NULL); -	if (sec == NULL) { +	/* +	 * Check following sections for notes: +	 *   '.note.gnu.build-id' +	 *   '.notes' +	 *   '.note' (VDSO specific) +	 */ +	do { +		sec = elf_section_by_name(elf, &ehdr, &shdr, +					  ".note.gnu.build-id", NULL); +		if (sec) +			break; +  		sec = elf_section_by_name(elf, &ehdr, &shdr,  					  ".notes", NULL); -		if (sec == NULL) -			goto out; -	} +		if (sec) +			break; + +		sec = elf_section_by_name(elf, &ehdr, &shdr, +					  ".note", NULL); +		if (sec) +			break; + +		return err; + +	} while (0);  	data = elf_getdata(sec, NULL);  	if (data == NULL) @@ -1559,11 +1607,62 @@ out:  	return err;  } +static int filename__read_debuglink(const char *filename, +				    char *debuglink, size_t size) +{ +	int fd, err = -1; +	Elf *elf; +	GElf_Ehdr ehdr; +	GElf_Shdr shdr; +	Elf_Data *data; +	Elf_Scn *sec; +	Elf_Kind ek; + +	fd = open(filename, O_RDONLY); +	if (fd < 0) +		goto out; + +	elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); +	if (elf == NULL) { +		pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); +		goto out_close; +	} + +	ek = elf_kind(elf); +	if (ek != ELF_K_ELF) +		goto out_close; + +	if (gelf_getehdr(elf, &ehdr) == NULL) { +		pr_err("%s: cannot get elf header.\n", __func__); +		goto out_close; +	} + +	sec = elf_section_by_name(elf, &ehdr, &shdr, +				  ".gnu_debuglink", NULL); +	if (sec == NULL) +		goto out_close; + +	data = elf_getdata(sec, NULL); +	if (data == NULL) +		goto out_close; + +	/* the start of this section is a zero-terminated string */ +	strncpy(debuglink, data->d_buf, size); + +	elf_end(elf); + +out_close: +	close(fd); +out: +	return err; +} +  char dso__symtab_origin(const struct dso *dso)  {  	static const char origin[] = {  		[SYMTAB__KALLSYMS]	      = 'k',  		[SYMTAB__JAVA_JIT]	      = 'j', +		[SYMTAB__DEBUGLINK]           = 'l',  		[SYMTAB__BUILD_ID_CACHE]      = 'B',  		[SYMTAB__FEDORA_DEBUGINFO]    = 'f',  		[SYMTAB__UBUNTU_DEBUGINFO]    = 'u', @@ -1631,10 +1730,22 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)  	 */  	want_symtab = 1;  restart: -	for (dso->symtab_type = SYMTAB__BUILD_ID_CACHE; +	for (dso->symtab_type = SYMTAB__DEBUGLINK;  	     dso->symtab_type != SYMTAB__NOT_FOUND;  	     dso->symtab_type++) {  		switch (dso->symtab_type) { +		case SYMTAB__DEBUGLINK: { +			char *debuglink; +			strncpy(name, dso->long_name, size); +			debuglink = name + dso->long_name_len; +			while (debuglink != name && *debuglink != '/') +				debuglink--; +			if (*debuglink == '/') +				debuglink++; +			filename__read_debuglink(dso->long_name, debuglink, +						 size - (debuglink - name)); +			} +			break;  		case SYMTAB__BUILD_ID_CACHE:  			/* skip the locally configured cache if a symfs is given */  			if (symbol_conf.symfs[0] || @@ -2786,8 +2897,11 @@ int machine__load_vmlinux_path(struct machine *machine, enum map_type type,  struct map *dso__new_map(const char *name)  { +	struct map *map = NULL;  	struct dso *dso = dso__new(name); -	struct map *map = map__new2(0, dso, MAP__FUNCTION); + +	if (dso) +		map = map__new2(0, dso, MAP__FUNCTION);  	return map;  }  |