| /* elf.c -- Get debug data from an ELF file for backtraces. |
| Copyright (C) 2012-2016 Free Software Foundation, Inc. |
| Written by Ian Lance Taylor, Google. |
| |
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the following conditions are |
| met: |
| |
| (1) Redistributions of source code must retain the above copyright |
| notice, this list of conditions and the following disclaimer. |
| |
| (2) Redistributions in binary form must reproduce the above copyright |
| notice, this list of conditions and the following disclaimer in |
| the documentation and/or other materials provided with the |
| distribution. |
| |
| (3) The name of the author may not be used to |
| endorse or promote products derived from this software without |
| specific prior written permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
| INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
| STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
| IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| POSSIBILITY OF SUCH DAMAGE. */ |
| |
| #include "config.h" |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/types.h> |
| |
| #ifdef HAVE_DL_ITERATE_PHDR |
| #include <link.h> |
| #endif |
| |
| #include "backtrace.h" |
| #include "internal.h" |
| |
| #ifndef HAVE_DL_ITERATE_PHDR |
| |
| /* Dummy version of dl_iterate_phdr for systems that don't have it. */ |
| |
| #define dl_phdr_info x_dl_phdr_info |
| #define dl_iterate_phdr x_dl_iterate_phdr |
| |
| struct dl_phdr_info { |
| uintptr_t dlpi_addr; |
| const char* dlpi_name; |
| }; |
| |
| static int |
| dl_iterate_phdr(int (*callback)(struct dl_phdr_info*, |
| size_t, void*) ATTRIBUTE_UNUSED, |
| void* data ATTRIBUTE_UNUSED) { |
| return 0; |
| } |
| |
| #endif /* ! defined (HAVE_DL_ITERATE_PHDR) */ |
| |
| /* The configure script must tell us whether we are 32-bit or 64-bit |
| ELF. We could make this code test and support either possibility, |
| but there is no point. This code only works for the currently |
| running executable, which means that we know the ELF mode at |
| configure mode. */ |
| |
| #if BACKTRACE_ELF_SIZE != 32 && BACKTRACE_ELF_SIZE != 64 |
| #error "Unknown BACKTRACE_ELF_SIZE" |
| #endif |
| |
| /* <link.h> might #include <elf.h> which might define our constants |
| with slightly different values. Undefine them to be safe. */ |
| |
| #undef EI_NIDENT |
| #undef EI_MAG0 |
| #undef EI_MAG1 |
| #undef EI_MAG2 |
| #undef EI_MAG3 |
| #undef EI_CLASS |
| #undef EI_DATA |
| #undef EI_VERSION |
| #undef ELF_MAG0 |
| #undef ELF_MAG1 |
| #undef ELF_MAG2 |
| #undef ELF_MAG3 |
| #undef ELFCLASS32 |
| #undef ELFCLASS64 |
| #undef ELFDATA2LSB |
| #undef ELFDATA2MSB |
| #undef EV_CURRENT |
| #undef ET_DYN |
| #undef SHN_LORESERVE |
| #undef SHN_XINDEX |
| #undef SHN_UNDEF |
| #undef SHT_SYMTAB |
| #undef SHT_STRTAB |
| #undef SHT_DYNSYM |
| #undef STT_OBJECT |
| #undef STT_FUNC |
| |
| /* Basic types. */ |
| |
| typedef uint16_t b_elf_half; /* Elf_Half. */ |
| typedef uint32_t b_elf_word; /* Elf_Word. */ |
| typedef int32_t b_elf_sword; /* Elf_Sword. */ |
| |
| #if BACKTRACE_ELF_SIZE == 32 |
| |
| typedef uint32_t b_elf_addr; /* Elf_Addr. */ |
| typedef uint32_t b_elf_off; /* Elf_Off. */ |
| |
| typedef uint32_t b_elf_wxword; /* 32-bit Elf_Word, 64-bit ELF_Xword. */ |
| |
| #else |
| |
| typedef uint64_t b_elf_addr; /* Elf_Addr. */ |
| typedef uint64_t b_elf_off; /* Elf_Off. */ |
| typedef uint64_t b_elf_xword; /* Elf_Xword. */ |
| typedef int64_t b_elf_sxword; /* Elf_Sxword. */ |
| |
| typedef uint64_t b_elf_wxword; /* 32-bit Elf_Word, 64-bit ELF_Xword. */ |
| |
| #endif |
| |
| /* Data structures and associated constants. */ |
| |
| #define EI_NIDENT 16 |
| |
| typedef struct { |
| unsigned char e_ident[EI_NIDENT]; /* ELF "magic number" */ |
| b_elf_half e_type; /* Identifies object file type */ |
| b_elf_half e_machine; /* Specifies required architecture */ |
| b_elf_word e_version; /* Identifies object file version */ |
| b_elf_addr e_entry; /* Entry point virtual address */ |
| b_elf_off e_phoff; /* Program header table file offset */ |
| b_elf_off e_shoff; /* Section header table file offset */ |
| b_elf_word e_flags; /* Processor-specific flags */ |
| b_elf_half e_ehsize; /* ELF header size in bytes */ |
| b_elf_half e_phentsize; /* Program header table entry size */ |
| b_elf_half e_phnum; /* Program header table entry count */ |
| b_elf_half e_shentsize; /* Section header table entry size */ |
| b_elf_half e_shnum; /* Section header table entry count */ |
| b_elf_half e_shstrndx; /* Section header string table index */ |
| } b_elf_ehdr; /* Elf_Ehdr. */ |
| |
| #define EI_MAG0 0 |
| #define EI_MAG1 1 |
| #define EI_MAG2 2 |
| #define EI_MAG3 3 |
| #define EI_CLASS 4 |
| #define EI_DATA 5 |
| #define EI_VERSION 6 |
| |
| #define ELFMAG0 0x7f |
| #define ELFMAG1 'E' |
| #define ELFMAG2 'L' |
| #define ELFMAG3 'F' |
| |
| #define ELFCLASS32 1 |
| #define ELFCLASS64 2 |
| |
| #define ELFDATA2LSB 1 |
| #define ELFDATA2MSB 2 |
| |
| #define EV_CURRENT 1 |
| |
| #define ET_DYN 3 |
| |
| typedef struct { |
| b_elf_word sh_name; /* Section name, index in string tbl */ |
| b_elf_word sh_type; /* Type of section */ |
| b_elf_wxword sh_flags; /* Miscellaneous section attributes */ |
| b_elf_addr sh_addr; /* Section virtual addr at execution */ |
| b_elf_off sh_offset; /* Section file offset */ |
| b_elf_wxword sh_size; /* Size of section in bytes */ |
| b_elf_word sh_link; /* Index of another section */ |
| b_elf_word sh_info; /* Additional section information */ |
| b_elf_wxword sh_addralign; /* Section alignment */ |
| b_elf_wxword sh_entsize; /* Entry size if section holds table */ |
| } b_elf_shdr; /* Elf_Shdr. */ |
| |
| #define SHN_UNDEF 0x0000 /* Undefined section */ |
| #define SHN_LORESERVE 0xFF00 /* Begin range of reserved indices */ |
| #define SHN_XINDEX 0xFFFF /* Section index is held elsewhere */ |
| |
| #define SHT_SYMTAB 2 |
| #define SHT_STRTAB 3 |
| #define SHT_DYNSYM 11 |
| |
| #if BACKTRACE_ELF_SIZE == 32 |
| |
| typedef struct { |
| b_elf_word st_name; /* Symbol name, index in string tbl */ |
| b_elf_addr st_value; /* Symbol value */ |
| b_elf_word st_size; /* Symbol size */ |
| unsigned char st_info; /* Symbol binding and type */ |
| unsigned char st_other; /* Visibility and other data */ |
| b_elf_half st_shndx; /* Symbol section index */ |
| } b_elf_sym; /* Elf_Sym. */ |
| |
| #else /* BACKTRACE_ELF_SIZE != 32 */ |
| |
| typedef struct { |
| b_elf_word st_name; /* Symbol name, index in string tbl */ |
| unsigned char st_info; /* Symbol binding and type */ |
| unsigned char st_other; /* Visibility and other data */ |
| b_elf_half st_shndx; /* Symbol section index */ |
| b_elf_addr st_value; /* Symbol value */ |
| b_elf_xword st_size; /* Symbol size */ |
| } b_elf_sym; /* Elf_Sym. */ |
| |
| #endif /* BACKTRACE_ELF_SIZE != 32 */ |
| |
| #define STT_OBJECT 1 |
| #define STT_FUNC 2 |
| |
| /* An index of ELF sections we care about. */ |
| |
| enum debug_section { |
| DEBUG_INFO, |
| DEBUG_LINE, |
| DEBUG_ABBREV, |
| DEBUG_RANGES, |
| DEBUG_STR, |
| DEBUG_MAX |
| }; |
| |
| /* Names of sections, indexed by enum elf_section. */ |
| |
| static const char* const debug_section_names[DEBUG_MAX] = { |
| ".debug_info", |
| ".debug_line", |
| ".debug_abbrev", |
| ".debug_ranges", |
| ".debug_str" |
| }; |
| |
| /* Information we gather for the sections we care about. */ |
| |
| struct debug_section_info { |
| /* Section file offset. */ |
| off_t offset; |
| /* Section size. */ |
| size_t size; |
| /* Section contents, after read from file. */ |
| const unsigned char* data; |
| }; |
| |
| /* Information we keep for an ELF symbol. */ |
| |
| struct elf_symbol { |
| /* The name of the symbol. */ |
| const char* name; |
| /* The address of the symbol. */ |
| uintptr_t address; |
| /* The size of the symbol. */ |
| size_t size; |
| }; |
| |
| /* Information to pass to elf_syminfo. */ |
| |
| struct elf_syminfo_data { |
| /* Symbols for the next module. */ |
| struct elf_syminfo_data* next; |
| /* The ELF symbols, sorted by address. */ |
| struct elf_symbol* symbols; |
| /* The number of symbols. */ |
| size_t count; |
| }; |
| |
| /* A dummy callback function used when we can't find any debug info. */ |
| |
| static int |
| elf_nodebug(struct backtrace_state* state ATTRIBUTE_UNUSED, |
| uintptr_t pc ATTRIBUTE_UNUSED, |
| backtrace_full_callback callback ATTRIBUTE_UNUSED, |
| backtrace_error_callback error_callback, void* data) { |
| error_callback(data, "no debug info in ELF executable", -1); |
| return 0; |
| } |
| |
| /* A dummy callback function used when we can't find a symbol |
| table. */ |
| |
| static void |
| elf_nosyms(struct backtrace_state* state ATTRIBUTE_UNUSED, |
| uintptr_t addr ATTRIBUTE_UNUSED, |
| backtrace_syminfo_callback callback ATTRIBUTE_UNUSED, |
| backtrace_error_callback error_callback, void* data) { |
| error_callback(data, "no symbol table in ELF executable", -1); |
| } |
| |
| /* Compare struct elf_symbol for qsort. */ |
| |
| static int |
| elf_symbol_compare(const void* v1, const void* v2) { |
| const struct elf_symbol* e1 = (const struct elf_symbol*) v1; |
| const struct elf_symbol* e2 = (const struct elf_symbol*) v2; |
| |
| if (e1->address < e2->address) { |
| return -1; |
| } else if (e1->address > e2->address) { |
| return 1; |
| } else { |
| return 0; |
| } |
| } |
| |
| /* Compare an ADDR against an elf_symbol for bsearch. We allocate one |
| extra entry in the array so that this can look safely at the next |
| entry. */ |
| |
| static int |
| elf_symbol_search(const void* vkey, const void* ventry) { |
| const uintptr_t* key = (const uintptr_t*) vkey; |
| const struct elf_symbol* entry = (const struct elf_symbol*) ventry; |
| uintptr_t addr = 0; |
| |
| addr = *key; |
| |
| if (addr < entry->address) { |
| return -1; |
| } else if (addr >= entry->address + entry->size) { |
| return 1; |
| } else { |
| return 0; |
| } |
| } |
| |
| /* Initialize the symbol table info for elf_syminfo. */ |
| |
| static int |
| elf_initialize_syminfo(struct backtrace_state* state, |
| uintptr_t base_address, |
| const unsigned char* symtab_data, size_t symtab_size, |
| const unsigned char* strtab, size_t strtab_size, |
| backtrace_error_callback error_callback, |
| void* data, struct elf_syminfo_data* sdata) { |
| size_t sym_count = 0; |
| const b_elf_sym* sym = NULL; |
| size_t elf_symbol_count = 0; |
| size_t elf_symbol_size = 0; |
| struct elf_symbol* elf_symbols = NULL; |
| size_t i = 0; |
| unsigned int j = 0; |
| |
| sym_count = symtab_size / sizeof(b_elf_sym); |
| |
| /* We only care about function symbols. Count them. */ |
| sym = (const b_elf_sym*) symtab_data; |
| elf_symbol_count = 0; |
| |
| for (i = 0; i < sym_count; ++i, ++sym) { |
| int info = 0; |
| |
| info = sym->st_info & 0xf; |
| |
| if ((info == STT_FUNC || info == STT_OBJECT) && sym->st_shndx != SHN_UNDEF) { |
| ++elf_symbol_count; |
| } |
| } |
| |
| elf_symbol_size = elf_symbol_count * sizeof(struct elf_symbol); |
| elf_symbols = ((struct elf_symbol*) |
| backtrace_alloc(state, elf_symbol_size, error_callback, data)); |
| |
| if (elf_symbols == NULL) { |
| return 0; |
| } |
| |
| sym = (const b_elf_sym*) symtab_data; |
| j = 0; |
| |
| for (i = 0; i < sym_count; ++i, ++sym) { |
| int info = 0; |
| |
| info = sym->st_info & 0xf; |
| |
| if (info != STT_FUNC && info != STT_OBJECT) { |
| continue; |
| } |
| |
| if (sym->st_shndx == SHN_UNDEF) { |
| continue; |
| } |
| |
| if (sym->st_name >= strtab_size) { |
| error_callback(data, "symbol string index out of range", 0); |
| backtrace_free(state, elf_symbols, elf_symbol_size, error_callback, data); |
| return 0; |
| } |
| |
| elf_symbols[j].name = (const char*) strtab + sym->st_name; |
| elf_symbols[j].address = sym->st_value + base_address; |
| elf_symbols[j].size = sym->st_size; |
| ++j; |
| } |
| |
| backtrace_qsort(elf_symbols, elf_symbol_count, sizeof(struct elf_symbol), |
| elf_symbol_compare); |
| |
| sdata->next = NULL; |
| sdata->symbols = elf_symbols; |
| sdata->count = elf_symbol_count; |
| |
| return 1; |
| } |
| |
| /* Add EDATA to the list in STATE. */ |
| |
| static void |
| elf_add_syminfo_data(struct backtrace_state* state, |
| struct elf_syminfo_data* edata) { |
| if (!state->threaded) { |
| struct elf_syminfo_data** pp; |
| |
| for (pp = (struct elf_syminfo_data**)(void*) &state->syminfo_data; |
| *pp != NULL; |
| pp = &(*pp)->next) { |
| ; |
| } |
| *pp = edata; |
| } else { |
| while (1) { |
| struct elf_syminfo_data** pp; |
| |
| pp = (struct elf_syminfo_data**)(void*) &state->syminfo_data; |
| |
| while (1) { |
| struct elf_syminfo_data* p; |
| |
| p = backtrace_atomic_load_pointer(pp); |
| |
| if (p == NULL) { |
| break; |
| } |
| |
| pp = &p->next; |
| } |
| |
| if (__sync_bool_compare_and_swap(pp, NULL, edata)) { |
| break; |
| } |
| } |
| } |
| } |
| |
| /* Return the symbol name and value for an ADDR. */ |
| |
| static void |
| elf_syminfo(struct backtrace_state* state, uintptr_t addr, |
| backtrace_syminfo_callback callback, |
| backtrace_error_callback error_callback ATTRIBUTE_UNUSED, |
| void* data) { |
| struct elf_syminfo_data* edata; |
| struct elf_symbol* sym = NULL; |
| |
| if (!state->threaded) { |
| for (edata = (struct elf_syminfo_data*) state->syminfo_data; |
| edata != NULL; |
| edata = edata->next) { |
| sym = ((struct elf_symbol*) |
| bsearch(&addr, edata->symbols, edata->count, sizeof(struct elf_symbol), elf_symbol_search)); |
| |
| if (sym != NULL) { |
| break; |
| } |
| } |
| } else { |
| struct elf_syminfo_data** pp; |
| |
| pp = (struct elf_syminfo_data**)(void*) &state->syminfo_data; |
| |
| while (1) { |
| edata = backtrace_atomic_load_pointer(pp); |
| |
| if (edata == NULL) { |
| break; |
| } |
| |
| sym = ((struct elf_symbol*) |
| bsearch(&addr, edata->symbols, edata->count, sizeof(struct elf_symbol), elf_symbol_search)); |
| |
| if (sym != NULL) { |
| break; |
| } |
| |
| pp = &edata->next; |
| } |
| } |
| |
| if (sym == NULL) { |
| callback(data, addr, NULL, 0, 0); |
| } else { |
| callback(data, addr, sym->name, sym->address, sym->size); |
| } |
| } |
| |
| /* Add the backtrace data for one ELF file. Returns 1 on success, |
| 0 on failure (in both cases descriptor is closed) or -1 if exe |
| is non-zero and the ELF file is ET_DYN, which tells the caller that |
| elf_add will need to be called on the descriptor again after |
| base_address is determined. */ |
| |
| static int |
| elf_add(struct backtrace_state* state, int descriptor, uintptr_t base_address, |
| backtrace_error_callback error_callback, void* data, |
| fileline* fileline_fn, int* found_sym, int* found_dwarf, int exe) { |
| struct backtrace_view ehdr_view; |
| b_elf_ehdr ehdr; |
| off_t shoff; |
| unsigned int shnum = 0; |
| unsigned int shstrndx = 0; |
| struct backtrace_view shdrs_view; |
| int shdrs_view_valid = 0; |
| const b_elf_shdr* shdrs = NULL; |
| const b_elf_shdr* shstrhdr = NULL; |
| size_t shstr_size = 0; |
| off_t shstr_off; |
| struct backtrace_view names_view; |
| int names_view_valid = 0; |
| const char* names = NULL; |
| unsigned int symtab_shndx = 0; |
| unsigned int dynsym_shndx = 0; |
| unsigned int i = 0; |
| struct debug_section_info sections[DEBUG_MAX]; |
| struct backtrace_view symtab_view; |
| int symtab_view_valid = 0; |
| struct backtrace_view strtab_view; |
| int strtab_view_valid = 0; |
| off_t min_offset; |
| off_t max_offset; |
| struct backtrace_view debug_view; |
| int debug_view_valid = 0; |
| |
| *found_sym = 0; |
| *found_dwarf = 0; |
| |
| shdrs_view_valid = 0; |
| names_view_valid = 0; |
| symtab_view_valid = 0; |
| strtab_view_valid = 0; |
| debug_view_valid = 0; |
| |
| if (!backtrace_get_view(state, descriptor, 0, sizeof ehdr, error_callback, |
| data, &ehdr_view)) { |
| goto fail; |
| } |
| |
| memcpy(&ehdr, ehdr_view.data, sizeof ehdr); |
| |
| backtrace_release_view(state, &ehdr_view, error_callback, data); |
| |
| if (ehdr.e_ident[EI_MAG0] != ELFMAG0 |
| || ehdr.e_ident[EI_MAG1] != ELFMAG1 |
| || ehdr.e_ident[EI_MAG2] != ELFMAG2 |
| || ehdr.e_ident[EI_MAG3] != ELFMAG3) { |
| error_callback(data, "executable file is not ELF", 0); |
| goto fail; |
| } |
| |
| if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) { |
| error_callback(data, "executable file is unrecognized ELF version", 0); |
| goto fail; |
| } |
| |
| #if BACKTRACE_ELF_SIZE == 32 |
| #define BACKTRACE_ELFCLASS ELFCLASS32 |
| #else |
| #define BACKTRACE_ELFCLASS ELFCLASS64 |
| #endif |
| |
| if (ehdr.e_ident[EI_CLASS] != BACKTRACE_ELFCLASS) { |
| error_callback(data, "executable file is unexpected ELF class", 0); |
| goto fail; |
| } |
| |
| if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB |
| && ehdr.e_ident[EI_DATA] != ELFDATA2MSB) { |
| error_callback(data, "executable file has unknown endianness", 0); |
| goto fail; |
| } |
| |
| /* If the executable is ET_DYN, it is either a PIE, or we are running |
| directly a shared library with .interp. We need to wait for |
| dl_iterate_phdr in that case to determine the actual base_address. */ |
| if (exe && ehdr.e_type == ET_DYN) { |
| return -1; |
| } |
| |
| shoff = ehdr.e_shoff; |
| shnum = ehdr.e_shnum; |
| shstrndx = ehdr.e_shstrndx; |
| |
| if ((shnum == 0 || shstrndx == SHN_XINDEX) |
| && shoff != 0) { |
| struct backtrace_view shdr_view; |
| const b_elf_shdr* shdr; |
| |
| if (!backtrace_get_view(state, descriptor, shoff, sizeof shdr, |
| error_callback, data, &shdr_view)) { |
| goto fail; |
| } |
| |
| shdr = (const b_elf_shdr*) shdr_view.data; |
| |
| if (shnum == 0) { |
| shnum = shdr->sh_size; |
| } |
| |
| if (shstrndx == SHN_XINDEX) { |
| shstrndx = shdr->sh_link; |
| |
| /* Versions of the GNU binutils between 2.12 and 2.18 did |
| not handle objects with more than SHN_LORESERVE sections |
| correctly. All large section indexes were offset by |
| 0x100. There is more information at |
| http://sourceware.org/bugzilla/show_bug.cgi?id-5900 . |
| Fortunately these object files are easy to detect, as the |
| GNU binutils always put the section header string table |
| near the end of the list of sections. Thus if the |
| section header string table index is larger than the |
| number of sections, then we know we have to subtract |
| 0x100 to get the real section index. */ |
| if (shstrndx >= shnum && shstrndx >= SHN_LORESERVE + 0x100) { |
| shstrndx -= 0x100; |
| } |
| } |
| |
| backtrace_release_view(state, &shdr_view, error_callback, data); |
| } |
| |
| /* To translate PC to file/line when using DWARF, we need to find |
| the .debug_info and .debug_line sections. */ |
| |
| /* Read the section headers, skipping the first one. */ |
| if (!backtrace_get_view(state, descriptor, shoff + sizeof(b_elf_shdr), |
| (shnum - 1) * sizeof(b_elf_shdr), |
| error_callback, data, &shdrs_view)) { |
| goto fail; |
| } |
| |
| shdrs_view_valid = 1; |
| shdrs = (const b_elf_shdr*) shdrs_view.data; |
| |
| /* Read the section names. */ |
| shstrhdr = &shdrs[shstrndx - 1]; |
| shstr_size = shstrhdr->sh_size; |
| shstr_off = shstrhdr->sh_offset; |
| |
| if (!backtrace_get_view(state, descriptor, shstr_off, shstr_size, |
| error_callback, data, &names_view)) { |
| goto fail; |
| } |
| |
| names_view_valid = 1; |
| names = (const char*) names_view.data; |
| |
| symtab_shndx = 0; |
| dynsym_shndx = 0; |
| |
| memset(sections, 0, sizeof sections); |
| |
| /* Look for the symbol table. */ |
| for (i = 1; i < shnum; ++i) { |
| const b_elf_shdr* shdr = NULL; |
| unsigned int sh_name = 0; |
| const char* name = NULL; |
| int j = 0; |
| |
| shdr = &shdrs[i - 1]; |
| |
| if (shdr->sh_type == SHT_SYMTAB) { |
| symtab_shndx = i; |
| } else if (shdr->sh_type == SHT_DYNSYM) { |
| dynsym_shndx = i; |
| } |
| |
| sh_name = shdr->sh_name; |
| |
| if (sh_name >= shstr_size) { |
| error_callback(data, "ELF section name out of range", 0); |
| goto fail; |
| } |
| |
| name = names + sh_name; |
| |
| for (j = 0; j < (int) DEBUG_MAX; ++j) { |
| if (strcmp(name, debug_section_names[j]) == 0) { |
| sections[j].offset = shdr->sh_offset; |
| sections[j].size = shdr->sh_size; |
| break; |
| } |
| } |
| } |
| |
| if (symtab_shndx == 0) { |
| symtab_shndx = dynsym_shndx; |
| } |
| |
| if (symtab_shndx != 0) { |
| const b_elf_shdr* symtab_shdr = NULL; |
| unsigned int strtab_shndx = 0; |
| const b_elf_shdr* strtab_shdr = NULL; |
| struct elf_syminfo_data* sdata = NULL; |
| |
| symtab_shdr = &shdrs[symtab_shndx - 1]; |
| strtab_shndx = symtab_shdr->sh_link; |
| |
| if (strtab_shndx >= shnum) { |
| error_callback(data, |
| "ELF symbol table strtab link out of range", 0); |
| goto fail; |
| } |
| |
| strtab_shdr = &shdrs[strtab_shndx - 1]; |
| |
| if (!backtrace_get_view(state, descriptor, symtab_shdr->sh_offset, |
| symtab_shdr->sh_size, error_callback, data, |
| &symtab_view)) { |
| goto fail; |
| } |
| |
| symtab_view_valid = 1; |
| |
| if (!backtrace_get_view(state, descriptor, strtab_shdr->sh_offset, |
| strtab_shdr->sh_size, error_callback, data, |
| &strtab_view)) { |
| goto fail; |
| } |
| |
| strtab_view_valid = 1; |
| |
| sdata = ((struct elf_syminfo_data*) |
| backtrace_alloc(state, sizeof * sdata, error_callback, data)); |
| |
| if (sdata == NULL) { |
| goto fail; |
| } |
| |
| if (!elf_initialize_syminfo(state, base_address, |
| symtab_view.data, symtab_shdr->sh_size, |
| strtab_view.data, strtab_shdr->sh_size, |
| error_callback, data, sdata)) { |
| backtrace_free(state, sdata, sizeof * sdata, error_callback, data); |
| goto fail; |
| } |
| |
| /* We no longer need the symbol table, but we hold on to the |
| string table permanently. */ |
| backtrace_release_view(state, &symtab_view, error_callback, data); |
| |
| *found_sym = 1; |
| |
| elf_add_syminfo_data(state, sdata); |
| } |
| |
| /* FIXME: Need to handle compressed debug sections. */ |
| |
| backtrace_release_view(state, &shdrs_view, error_callback, data); |
| shdrs_view_valid = 0; |
| backtrace_release_view(state, &names_view, error_callback, data); |
| names_view_valid = 0; |
| |
| /* Read all the debug sections in a single view, since they are |
| probably adjacent in the file. We never release this view. */ |
| |
| min_offset = 0; |
| max_offset = 0; |
| |
| for (i = 0; i < (int) DEBUG_MAX; ++i) { |
| off_t end; |
| |
| if (sections[i].size == 0) { |
| continue; |
| } |
| |
| if (min_offset == 0 || sections[i].offset < min_offset) { |
| min_offset = sections[i].offset; |
| } |
| |
| end = sections[i].offset + sections[i].size; |
| |
| if (end > max_offset) { |
| max_offset = end; |
| } |
| } |
| |
| if (min_offset == 0 || max_offset == 0) { |
| if (descriptor != -1) { |
| if (!backtrace_close(descriptor, error_callback, data)) { |
| goto fail; |
| } |
| } |
| |
| return 1; |
| } |
| |
| if (!backtrace_get_view(state, descriptor, min_offset, |
| max_offset - min_offset, |
| error_callback, data, &debug_view)) { |
| goto fail; |
| } |
| |
| debug_view_valid = 1; |
| |
| if (descriptor != -1) { |
| /* We've read all we need from the executable. */ |
| if (!backtrace_close(descriptor, error_callback, data)) { |
| goto fail; |
| } |
| } |
| |
| descriptor = -1; |
| |
| for (i = 0; i < (int) DEBUG_MAX; ++i) { |
| if (sections[i].size == 0) { |
| sections[i].data = NULL; |
| } else |
| sections[i].data = ((const unsigned char*) debug_view.data |
| + (sections[i].offset - min_offset)); |
| } |
| |
| if (!backtrace_dwarf_add(state, base_address, |
| sections[DEBUG_INFO].data, |
| sections[DEBUG_INFO].size, |
| sections[DEBUG_LINE].data, |
| sections[DEBUG_LINE].size, |
| sections[DEBUG_ABBREV].data, |
| sections[DEBUG_ABBREV].size, |
| sections[DEBUG_RANGES].data, |
| sections[DEBUG_RANGES].size, |
| sections[DEBUG_STR].data, |
| sections[DEBUG_STR].size, |
| ehdr.e_ident[EI_DATA] == ELFDATA2MSB, |
| error_callback, data, fileline_fn)) { |
| goto fail; |
| } |
| |
| *found_dwarf = 1; |
| |
| return 1; |
| |
| fail: |
| |
| if (shdrs_view_valid) { |
| backtrace_release_view(state, &shdrs_view, error_callback, data); |
| } |
| |
| if (names_view_valid) { |
| backtrace_release_view(state, &names_view, error_callback, data); |
| } |
| |
| if (symtab_view_valid) { |
| backtrace_release_view(state, &symtab_view, error_callback, data); |
| } |
| |
| if (strtab_view_valid) { |
| backtrace_release_view(state, &strtab_view, error_callback, data); |
| } |
| |
| if (debug_view_valid) { |
| backtrace_release_view(state, &debug_view, error_callback, data); |
| } |
| |
| if (descriptor != -1) { |
| backtrace_close(descriptor, error_callback, data); |
| } |
| |
| return 0; |
| } |
| |
| /* Data passed to phdr_callback. */ |
| |
| struct phdr_data { |
| struct backtrace_state* state; |
| backtrace_error_callback error_callback; |
| void* data; |
| fileline* fileline_fn; |
| int* found_sym; |
| int* found_dwarf; |
| int exe_descriptor; |
| }; |
| |
| /* Callback passed to dl_iterate_phdr. Load debug info from shared |
| libraries. */ |
| |
| static int |
| #ifdef __i386__ |
| __attribute__((__force_align_arg_pointer__)) |
| #endif |
| phdr_callback(struct dl_phdr_info* info, size_t size ATTRIBUTE_UNUSED, |
| void* pdata) { |
| struct phdr_data* pd = (struct phdr_data*) pdata; |
| int descriptor = 0; |
| int does_not_exist = 0; |
| fileline elf_fileline_fn = NULL; |
| int found_dwarf = 0; |
| |
| /* There is not much we can do if we don't have the module name, |
| unless executable is ET_DYN, where we expect the very first |
| phdr_callback to be for the PIE. */ |
| if (info->dlpi_name == NULL || info->dlpi_name[0] == '\0') { |
| if (pd->exe_descriptor == -1) { |
| return 0; |
| } |
| |
| descriptor = pd->exe_descriptor; |
| pd->exe_descriptor = -1; |
| } else { |
| if (pd->exe_descriptor != -1) { |
| backtrace_close(pd->exe_descriptor, pd->error_callback, pd->data); |
| pd->exe_descriptor = -1; |
| } |
| |
| descriptor = backtrace_open(info->dlpi_name, pd->error_callback, |
| pd->data, &does_not_exist); |
| |
| if (descriptor < 0) { |
| return 0; |
| } |
| } |
| |
| if (elf_add(pd->state, descriptor, info->dlpi_addr, pd->error_callback, |
| pd->data, &elf_fileline_fn, pd->found_sym, &found_dwarf, 0)) { |
| if (found_dwarf) { |
| *pd->found_dwarf = 1; |
| *pd->fileline_fn = elf_fileline_fn; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* Initialize the backtrace data we need from an ELF executable. At |
| the ELF level, all we need to do is find the debug info |
| sections. */ |
| |
| int |
| backtrace_initialize(struct backtrace_state* state, int descriptor, |
| backtrace_error_callback error_callback, |
| void* data, fileline* fileline_fn) { |
| int ret = 0; |
| int found_sym = 0; |
| int found_dwarf = 0; |
| fileline elf_fileline_fn = elf_nodebug; |
| struct phdr_data pd; |
| |
| ret = elf_add(state, descriptor, 0, error_callback, data, &elf_fileline_fn, |
| &found_sym, &found_dwarf, 1); |
| |
| if (!ret) { |
| return 0; |
| } |
| |
| pd.state = state; |
| pd.error_callback = error_callback; |
| pd.data = data; |
| pd.fileline_fn = &elf_fileline_fn; |
| pd.found_sym = &found_sym; |
| pd.found_dwarf = &found_dwarf; |
| pd.exe_descriptor = ret < 0 ? descriptor : -1; |
| |
| dl_iterate_phdr(phdr_callback, (void*) &pd); |
| |
| if (!state->threaded) { |
| if (found_sym) { |
| state->syminfo_fn = elf_syminfo; |
| } else if (state->syminfo_fn == NULL) { |
| state->syminfo_fn = elf_nosyms; |
| } |
| } else { |
| if (found_sym) { |
| backtrace_atomic_store_pointer(&state->syminfo_fn, elf_syminfo); |
| } else |
| (void) __sync_bool_compare_and_swap(&state->syminfo_fn, NULL, |
| elf_nosyms); |
| } |
| |
| if (!state->threaded) { |
| if (state->fileline_fn == NULL || state->fileline_fn == elf_nodebug) { |
| *fileline_fn = elf_fileline_fn; |
| } |
| } else { |
| fileline current_fn = NULL; |
| |
| current_fn = backtrace_atomic_load_pointer(&state->fileline_fn); |
| |
| if (current_fn == NULL || current_fn == elf_nodebug) { |
| *fileline_fn = elf_fileline_fn; |
| } |
| } |
| |
| return 1; |
| } |