|  | /* 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); | 
|  | } |