| /* Copyright (c) 2011-2015 Linux Perf Authors | 
 |  * | 
 |  * This source code is licensed under the GNU General Public License Version 2. | 
 |  * See the file LICENSE-gpl-2.0.txt for more details. */ | 
 |  | 
 | #include <fcntl.h> | 
 | #include <stdio.h> | 
 | #include <errno.h> | 
 | #include <string.h> | 
 | #include <unistd.h> | 
 | #include <inttypes.h> | 
 | #include <stdbool.h> | 
 |  | 
 | #include <gelf.h> | 
 | #include <libelf.h> | 
 | #include "xlib.h" | 
 | #include "elf.h" | 
 |  | 
 | #define pr_err(args...) fprintf(stderr, args) | 
 | #define pr_debug2(args...) pr_err(args) | 
 |  | 
 | #define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP | 
 |  | 
 | enum map_type { | 
 | 	MAP__FUNCTION = 0, | 
 | 	MAP__VARIABLE, | 
 | }; | 
 |  | 
 | static inline char *bfd_demangle(void *v, const char *c, int i) | 
 | { | 
 | 	return NULL; | 
 | } | 
 |  | 
 | #ifndef NT_GNU_BUILD_ID | 
 | #define NT_GNU_BUILD_ID 3 | 
 | #endif | 
 |  | 
 | /** | 
 |  * elf_symtab__for_each_symbol - iterate thru all the symbols | 
 |  * | 
 |  * @syms: struct elf_symtab instance to iterate | 
 |  * @idx: uint32_t idx | 
 |  * @sym: GElf_Sym iterator | 
 |  */ | 
 | #define elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) \ | 
 | 	for (idx = 0, gelf_getsym(syms, idx, &sym);\ | 
 | 	     idx < nr_syms; \ | 
 | 	     idx++, gelf_getsym(syms, idx, &sym)) | 
 |  | 
 | static inline uint8_t elf_sym__type(const GElf_Sym *sym) | 
 | { | 
 | 	return GELF_ST_TYPE(sym->st_info); | 
 | } | 
 |  | 
 | #ifndef STT_GNU_IFUNC | 
 | #define STT_GNU_IFUNC 10 | 
 | #endif | 
 |  | 
 | static inline int elf_sym__is_function(const GElf_Sym *sym) | 
 | { | 
 | 	return (elf_sym__type(sym) == STT_FUNC || | 
 | 		elf_sym__type(sym) == STT_GNU_IFUNC) && | 
 | 	       sym->st_name != 0 && | 
 | 	       sym->st_shndx != SHN_UNDEF; | 
 | } | 
 |  | 
 | static inline bool elf_sym__is_object(const GElf_Sym *sym) | 
 | { | 
 | 	return elf_sym__type(sym) == STT_OBJECT && | 
 | 		sym->st_name != 0 && | 
 | 		sym->st_shndx != SHN_UNDEF; | 
 | } | 
 |  | 
 | static inline int elf_sym__is_label(const GElf_Sym *sym) | 
 | { | 
 | 	return elf_sym__type(sym) == STT_NOTYPE && | 
 | 		sym->st_name != 0 && | 
 | 		sym->st_shndx != SHN_UNDEF && | 
 | 		sym->st_shndx != SHN_ABS; | 
 | } | 
 |  | 
 | static bool elf_sym__is_a(GElf_Sym *sym, enum map_type type) | 
 | { | 
 | 	switch (type) { | 
 | 	case MAP__FUNCTION: | 
 | 		return elf_sym__is_function(sym); | 
 | 	case MAP__VARIABLE: | 
 | 		return elf_sym__is_object(sym); | 
 | 	default: | 
 | 		return false; | 
 | 	} | 
 | } | 
 |  | 
 | static inline const char *elf_sym__name(const GElf_Sym *sym, | 
 | 					const Elf_Data *symstrs) | 
 | { | 
 | 	return symstrs->d_buf + sym->st_name; | 
 | } | 
 |  | 
 | static inline const char *elf_sec__name(const GElf_Shdr *shdr, | 
 | 					const Elf_Data *secstrs) | 
 | { | 
 | 	return secstrs->d_buf + shdr->sh_name; | 
 | } | 
 |  | 
 | static inline int elf_sec__is_text(const GElf_Shdr *shdr, | 
 | 					const Elf_Data *secstrs) | 
 | { | 
 | 	return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; | 
 | } | 
 |  | 
 | static inline bool elf_sec__is_data(const GElf_Shdr *shdr, | 
 | 				    const Elf_Data *secstrs) | 
 | { | 
 | 	return strstr(elf_sec__name(shdr, secstrs), "data") != NULL; | 
 | } | 
 |  | 
 | static bool elf_sec__is_a(GElf_Shdr *shdr, Elf_Data *secstrs, | 
 | 			  enum map_type type) | 
 | { | 
 | 	switch (type) { | 
 | 	case MAP__FUNCTION: | 
 | 		return elf_sec__is_text(shdr, secstrs); | 
 | 	case MAP__VARIABLE: | 
 | 		return elf_sec__is_data(shdr, secstrs); | 
 | 	default: | 
 | 		return false; | 
 | 	} | 
 | } | 
 |  | 
 | static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr) | 
 | { | 
 | 	Elf_Scn *sec = NULL; | 
 | 	GElf_Shdr shdr; | 
 | 	size_t cnt = 1; | 
 |  | 
 | 	while ((sec = elf_nextscn(elf, sec)) != NULL) { | 
 | 		gelf_getshdr(sec, &shdr); | 
 |  | 
 | 		if ((addr >= shdr.sh_addr) && | 
 | 		    (addr < (shdr.sh_addr + shdr.sh_size))) | 
 | 			return cnt; | 
 |  | 
 | 		++cnt; | 
 | 	} | 
 |  | 
 | 	return -1; | 
 | } | 
 |  | 
 | Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | 
 | 			     GElf_Shdr *shp, const char *name, size_t *idx) | 
 | { | 
 | 	Elf_Scn *sec = NULL; | 
 | 	size_t cnt = 1; | 
 |  | 
 | 	/* Elf is corrupted/truncated, avoid calling elf_strptr. */ | 
 | 	if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) | 
 | 		return NULL; | 
 |  | 
 | 	while ((sec = elf_nextscn(elf, sec)) != NULL) { | 
 | 		char *str; | 
 |  | 
 | 		gelf_getshdr(sec, shp); | 
 | 		str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); | 
 | 		if (str && !strcmp(name, str)) { | 
 | 			if (idx) | 
 | 				*idx = cnt; | 
 | 			return sec; | 
 | 		} | 
 | 		++cnt; | 
 | 	} | 
 |  | 
 | 	return NULL; | 
 | } | 
 |  | 
 | #define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \ | 
 | 	for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \ | 
 | 	     idx < nr_entries; \ | 
 | 	     ++idx, pos = gelf_getrel(reldata, idx, &pos_mem)) | 
 |  | 
 | #define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \ | 
 | 	for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \ | 
 | 	     idx < nr_entries; \ | 
 | 	     ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) | 
 |  | 
 |  | 
 | /* | 
 |  * Align offset to 4 bytes as needed for note name and descriptor data. | 
 |  */ | 
 | #define NOTE_ALIGN(n) (((n) + 3) & -4U) | 
 |  | 
 | static int elf_read_build_id(Elf *elf, void *bf, size_t size) | 
 | { | 
 | 	int err = -1; | 
 | 	GElf_Ehdr ehdr; | 
 | 	GElf_Shdr shdr; | 
 | 	Elf_Data *data; | 
 | 	Elf_Scn *sec; | 
 | 	Elf_Kind ek; | 
 | 	void *ptr; | 
 |  | 
 | 	if (size < BUILD_ID_SIZE) | 
 | 		goto out; | 
 |  | 
 | 	ek = elf_kind(elf); | 
 | 	if (ek != ELF_K_ELF) | 
 | 		goto out; | 
 |  | 
 | 	if (gelf_getehdr(elf, &ehdr) == NULL) { | 
 | 		pr_err("%s: cannot get elf header.\n", __func__); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * 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) | 
 | 			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) | 
 | 		goto out; | 
 |  | 
 | 	ptr = data->d_buf; | 
 | 	while (ptr < (data->d_buf + data->d_size)) { | 
 | 		GElf_Nhdr *nhdr = ptr; | 
 | 		size_t namesz = NOTE_ALIGN(nhdr->n_namesz), | 
 | 		       descsz = NOTE_ALIGN(nhdr->n_descsz); | 
 | 		const char *name; | 
 |  | 
 | 		ptr += sizeof(*nhdr); | 
 | 		name = ptr; | 
 | 		ptr += namesz; | 
 | 		if (nhdr->n_type == NT_GNU_BUILD_ID && | 
 | 		    nhdr->n_namesz == sizeof("GNU")) { | 
 | 			if (memcmp(name, "GNU", sizeof("GNU")) == 0) { | 
 | 				size_t sz = min(size, descsz); | 
 | 				memcpy(bf, ptr, sz); | 
 | 				memset(bf + sz, 0, size - sz); | 
 | 				err = descsz; | 
 | 				break; | 
 | 			} | 
 | 		} | 
 | 		ptr += descsz; | 
 | 	} | 
 |  | 
 | out: | 
 | 	return err; | 
 | } | 
 |  | 
 | int filename__read_build_id(const char *filename, void *bf, size_t size) | 
 | { | 
 | 	int fd, err = -1; | 
 | 	Elf *elf; | 
 |  | 
 | 	if (size < BUILD_ID_SIZE) | 
 | 		goto out; | 
 |  | 
 | 	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; | 
 | 	} | 
 |  | 
 | 	err = elf_read_build_id(elf, bf, size); | 
 |  | 
 | 	elf_end(elf); | 
 | out_close: | 
 | 	close(fd); | 
 | out: | 
 | 	return err; | 
 | } | 
 |  | 
 | void symbol__elf_init(void) | 
 | { | 
 | 	elf_version(EV_CURRENT); | 
 | } |