| |
| /*--------------------------------------------------------------------*/ |
| /*--- Reading of syms & debug info from ELF .so/executable files. ---*/ |
| /*--- readelf.c ---*/ |
| /*--------------------------------------------------------------------*/ |
| |
| /* |
| This file is part of Valgrind, a dynamic binary instrumentation |
| framework. |
| |
| Copyright (C) 2000-2008 Julian Seward |
| jseward@acm.org |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU General Public License as |
| published by the Free Software Foundation; either version 2 of the |
| License, or (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 02111-1307, USA. |
| |
| The GNU General Public License is contained in the file COPYING. |
| */ |
| /* |
| Stabs reader greatly improved by Nick Nethercote, Apr 02. |
| This module was also extensively hacked on by Jeremy Fitzhardinge |
| and Tom Hughes. |
| */ |
| |
| #include "pub_core_basics.h" |
| #include "pub_core_vki.h" |
| #include "pub_core_libcbase.h" |
| #include "pub_core_libcprint.h" |
| #include "pub_core_libcassert.h" |
| #include "pub_core_libcfile.h" |
| #include "pub_core_aspacemgr.h" /* for mmaping debuginfo files */ |
| #include "pub_core_machine.h" /* VG_ELF_CLASS */ |
| #include "pub_core_options.h" |
| #include "pub_core_oset.h" |
| #include "pub_core_tooliface.h" /* VG_(needs) */ |
| #include "pub_core_xarray.h" |
| #include "priv_misc.h" /* dinfo_zalloc/free/strdup */ |
| #include "priv_d3basics.h" |
| #include "priv_tytypes.h" |
| #include "priv_storage.h" |
| #include "priv_readelf.h" /* self */ |
| #include "priv_readdwarf.h" /* 'cos ELF contains DWARF */ |
| #include "priv_readdwarf3.h" |
| #include "priv_readstabs.h" /* and stabs, if we're unlucky */ |
| |
| /* --- !!! --- EXTERNAL HEADERS start --- !!! --- */ |
| #include <elf.h> |
| /* --- !!! --- EXTERNAL HEADERS end --- !!! --- */ |
| |
| /*------------------------------------------------------------*/ |
| /*--- 32/64-bit parameterisation ---*/ |
| /*------------------------------------------------------------*/ |
| |
| /* For all the ELF macros and types which specify '32' or '64', |
| select the correct variant for this platform and give it |
| an 'XX' name. Then use the 'XX' variant consistently in |
| the rest of this file. |
| */ |
| #if VG_WORDSIZE == 4 |
| # define ElfXX_Ehdr Elf32_Ehdr |
| # define ElfXX_Shdr Elf32_Shdr |
| # define ElfXX_Phdr Elf32_Phdr |
| # define ElfXX_Sym Elf32_Sym |
| # define ElfXX_Word Elf32_Word |
| # define ElfXX_Addr Elf32_Addr |
| # define ElfXX_Dyn Elf32_Dyn |
| # define ELFXX_ST_BIND ELF32_ST_BIND |
| # define ELFXX_ST_TYPE ELF32_ST_TYPE |
| |
| #elif VG_WORDSIZE == 8 |
| # define ElfXX_Ehdr Elf64_Ehdr |
| # define ElfXX_Shdr Elf64_Shdr |
| # define ElfXX_Phdr Elf64_Phdr |
| # define ElfXX_Sym Elf64_Sym |
| # define ElfXX_Word Elf64_Word |
| # define ElfXX_Addr Elf64_Addr |
| # define ElfXX_Dyn Elf64_Dyn |
| # define ELFXX_ST_BIND ELF64_ST_BIND |
| # define ELFXX_ST_TYPE ELF64_ST_TYPE |
| |
| #else |
| # error "VG_WORDSIZE should be 4 or 8" |
| #endif |
| |
| |
| /*------------------------------------------------------------*/ |
| /*--- ---*/ |
| /*--- Read symbol table and line info from ELF files. ---*/ |
| /*--- ---*/ |
| /*------------------------------------------------------------*/ |
| |
| /* readelf.c parses ELF files and acquires symbol table info from |
| them. It calls onwards to readdwarf.c to read DWARF2/3 line number |
| and call frame info found. */ |
| |
| |
| /* Identify an ELF object file by peering at the first few bytes of |
| it. */ |
| |
| Bool ML_(is_elf_object_file)( void* image, SizeT n_image ) |
| { |
| ElfXX_Ehdr* ehdr = (ElfXX_Ehdr*)image; |
| Int ok = 1; |
| |
| if (n_image < sizeof(ElfXX_Ehdr)) |
| return False; |
| |
| ok &= (ehdr->e_ident[EI_MAG0] == 0x7F |
| && ehdr->e_ident[EI_MAG1] == 'E' |
| && ehdr->e_ident[EI_MAG2] == 'L' |
| && ehdr->e_ident[EI_MAG3] == 'F'); |
| ok &= (ehdr->e_ident[EI_CLASS] == VG_ELF_CLASS |
| && ehdr->e_ident[EI_DATA] == VG_ELF_DATA2XXX |
| && ehdr->e_ident[EI_VERSION] == EV_CURRENT); |
| ok &= (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN); |
| ok &= (ehdr->e_machine == VG_ELF_MACHINE); |
| ok &= (ehdr->e_version == EV_CURRENT); |
| ok &= (ehdr->e_shstrndx != SHN_UNDEF); |
| ok &= (ehdr->e_shoff != 0 && ehdr->e_shnum != 0); |
| ok &= (ehdr->e_phoff != 0 && ehdr->e_phnum != 0); |
| |
| if (ok) |
| return True; |
| else |
| return False; |
| } |
| |
| |
| /* Show a raw ELF symbol, given its in-image address and name. */ |
| |
| static |
| void show_raw_elf_symbol ( Int i, |
| ElfXX_Sym* sym, Char* sym_name, Addr sym_svma, |
| Bool ppc64_linux_format ) |
| { |
| HChar* space = ppc64_linux_format ? " " : ""; |
| VG_(printf)("raw symbol [%4d]: ", i); |
| switch (ELFXX_ST_BIND(sym->st_info)) { |
| case STB_LOCAL: VG_(printf)("LOC "); break; |
| case STB_GLOBAL: VG_(printf)("GLO "); break; |
| case STB_WEAK: VG_(printf)("WEA "); break; |
| case STB_LOPROC: VG_(printf)("lop "); break; |
| case STB_HIPROC: VG_(printf)("hip "); break; |
| default: VG_(printf)("??? "); break; |
| } |
| switch (ELFXX_ST_TYPE(sym->st_info)) { |
| case STT_NOTYPE: VG_(printf)("NOT "); break; |
| case STT_OBJECT: VG_(printf)("OBJ "); break; |
| case STT_FUNC: VG_(printf)("FUN "); break; |
| case STT_SECTION: VG_(printf)("SEC "); break; |
| case STT_FILE: VG_(printf)("FIL "); break; |
| case STT_LOPROC: VG_(printf)("lop "); break; |
| case STT_HIPROC: VG_(printf)("hip "); break; |
| default: VG_(printf)("??? "); break; |
| } |
| VG_(printf)(": svma %#010lx, %ssz %4ld %s\n", |
| sym_svma, space, sym->st_size + 0UL, |
| ( sym->st_name ? sym_name : (Char*)"NONAME" ) ); |
| } |
| |
| |
| /* Decide whether SYM is something we should collect, and if so, copy |
| relevant info to the _OUT arguments. For {x86,amd64,ppc32}-linux |
| this is straightforward - the name, address, size are copied out |
| unchanged. |
| |
| There is a bit of a kludge re data symbols (see KLUDGED BSS CHECK |
| below): we assume that the .bss is mapped immediately after .data, |
| and so accept any data symbol which exists in the range [start of |
| .data, size of .data + size of .bss). I don't know if this is |
| really correct/justifiable, or not. |
| |
| For ppc64-linux it's more complex. If the symbol is seen to be in |
| the .opd section, it is taken to be a function descriptor, and so |
| a dereference is attempted, in order to get hold of the real entry |
| point address. Also as part of the dereference, there is an attempt |
| to calculate the TOC pointer (R2 value) associated with the symbol. |
| |
| To support the ppc64-linux pre-"dotless" ABI (prior to gcc 4.0.0), |
| if the symbol is seen to be outside the .opd section and its name |
| starts with a dot, an .opd deference is not attempted, and no TOC |
| pointer is calculated, but the the leading dot is removed from the |
| name. |
| |
| As a result, on ppc64-linux, the caller of this function may have |
| to piece together the real size, address, name of the symbol from |
| multiple calls to this function. Ugly and confusing. |
| */ |
| static |
| Bool get_elf_symbol_info ( |
| /* INPUTS */ |
| struct _DebugInfo* di, /* containing DebugInfo */ |
| ElfXX_Sym* sym, /* ELF symbol */ |
| Char* sym_name, /* name */ |
| Addr sym_svma, /* address as stated in the object file */ |
| UChar* opd_img, /* oimage of .opd sec (ppc64-linux only) */ |
| OffT opd_bias, /* for biasing AVMAs found in .opd */ |
| /* OUTPUTS */ |
| Char** sym_name_out, /* name we should record */ |
| Addr* sym_avma_out, /* addr we should record */ |
| Int* sym_size_out, /* symbol size */ |
| Addr* sym_tocptr_out, /* ppc64-linux only: R2 value to be |
| used on entry */ |
| Bool* from_opd_out, /* ppc64-linux only: did we deref an |
| .opd entry? */ |
| Bool* is_text_out /* is this a text symbol? */ |
| ) |
| { |
| Bool plausible, is_in_opd; |
| Bool in_text, in_data, in_sdata, in_bss; |
| |
| /* Set defaults */ |
| *sym_name_out = sym_name; |
| *sym_avma_out = sym_svma; /* we will bias this shortly */ |
| *is_text_out = True; |
| *sym_size_out = (Int)sym->st_size; |
| *sym_tocptr_out = 0; /* unknown/inapplicable */ |
| *from_opd_out = False; |
| |
| /* Figure out if we're interested in the symbol. Firstly, is it of |
| the right flavour? */ |
| plausible |
| = (ELFXX_ST_BIND(sym->st_info) == STB_GLOBAL |
| || ELFXX_ST_BIND(sym->st_info) == STB_LOCAL |
| || ELFXX_ST_BIND(sym->st_info) == STB_WEAK |
| ) |
| && |
| (ELFXX_ST_TYPE(sym->st_info) == STT_FUNC |
| || ELFXX_ST_TYPE(sym->st_info) == STT_OBJECT |
| ); |
| |
| /* Now bias sym_avma_out accordingly */ |
| #if 0 |
| /* This works, but seems a bit crude */ |
| if (ELFXX_ST_TYPE(sym->st_info) == STT_OBJECT) { |
| *is_text_out = False; |
| *sym_avma_out += di->data_bias; |
| } else { |
| *is_text_out = True; |
| *sym_avma_out += di->text_bias; |
| } |
| #else |
| /* Try to figure out exactly which section the symbol is from and |
| bias accordingly. Screws up if the previously deduced section |
| svma address ranges are wrong. */ |
| if (di->text_present |
| && di->text_size > 0 |
| && sym_svma >= di->text_svma |
| && sym_svma < di->text_svma + di->text_size) { |
| *is_text_out = True; |
| *sym_avma_out += di->text_bias; |
| } else |
| if (di->data_present |
| && di->data_size > 0 |
| && sym_svma >= di->data_svma |
| && sym_svma < di->data_svma + di->data_size) { |
| *is_text_out = False; |
| *sym_avma_out += di->data_bias; |
| } else |
| if (di->sdata_present |
| && di->sdata_size > 0 |
| && sym_svma >= di->sdata_svma |
| && sym_svma < di->sdata_svma + di->sdata_size) { |
| *is_text_out = False; |
| *sym_avma_out += di->sdata_bias; |
| } else |
| if (di->bss_present |
| && di->bss_size > 0 |
| && sym_svma >= di->bss_svma |
| && sym_svma < di->bss_svma + di->bss_size) { |
| *is_text_out = False; |
| *sym_avma_out += di->bss_bias; |
| } else { |
| /* Assume it's in .text. Is this a good idea? */ |
| *is_text_out = True; |
| *sym_avma_out += di->text_bias; |
| } |
| #endif |
| |
| # if defined(VGP_ppc64_linux) |
| /* Allow STT_NOTYPE in the very special case where we're running on |
| ppc64-linux and the symbol is one which the .opd-chasing hack |
| below will chase. */ |
| if (!plausible |
| && *is_text_out |
| && ELFXX_ST_TYPE(sym->st_info) == STT_NOTYPE |
| && sym->st_size > 0 |
| && di->opd_present |
| && di->opd_size > 0 |
| && *sym_avma_out >= di->opd_avma |
| && *sym_avma_out < di->opd_avma + di->opd_size) |
| plausible = True; |
| # endif |
| |
| if (!plausible) |
| return False; |
| |
| /* Ignore if nameless, or zero-sized. */ |
| if (sym->st_name == (ElfXX_Word)0 |
| || /* VG_(strlen)(sym_name) == 0 */ |
| /* equivalent but cheaper ... */ |
| sym_name[0] == 0 |
| || sym->st_size == 0) { |
| TRACE_SYMTAB(" ignore -- size=0: %s\n", sym_name); |
| return False; |
| } |
| |
| /* This seems to significantly reduce the number of junk |
| symbols, and particularly reduces the number of |
| overlapping address ranges. Don't ask me why ... */ |
| if ((Int)sym->st_value == 0) { |
| TRACE_SYMTAB( " ignore -- valu=0: %s\n", sym_name); |
| return False; |
| } |
| |
| /* If it's apparently in a GOT or PLT, it's really a reference to a |
| symbol defined elsewhere, so ignore it. */ |
| if (di->got_present |
| && di->got_size > 0 |
| && *sym_avma_out >= di->got_avma |
| && *sym_avma_out < di->got_avma + di->got_size) { |
| TRACE_SYMTAB(" ignore -- in GOT: %s\n", sym_name); |
| return False; |
| } |
| if (di->plt_present |
| && di->plt_size > 0 |
| && *sym_avma_out >= di->plt_avma |
| && *sym_avma_out < di->plt_avma + di->plt_size) { |
| TRACE_SYMTAB(" ignore -- in PLT: %s\n", sym_name); |
| return False; |
| } |
| |
| /* ppc64-linux nasty hack: if the symbol is in an .opd section, |
| then really what we have is the address of a function |
| descriptor. So use the first word of that as the function's |
| text. |
| |
| See thread starting at |
| http://gcc.gnu.org/ml/gcc-patches/2004-08/msg00557.html |
| */ |
| is_in_opd = False; |
| |
| if (di->opd_present |
| && di->opd_size > 0 |
| && *sym_avma_out >= di->opd_avma |
| && *sym_avma_out < di->opd_avma + di->opd_size) { |
| # if !defined(VGP_ppc64_linux) |
| TRACE_SYMTAB(" ignore -- in OPD: %s\n", sym_name); |
| return False; |
| # else |
| Int offset_in_opd; |
| ULong* fn_descr; |
| Bool details = 1||False; |
| |
| if (details) |
| TRACE_SYMTAB("opdXXX: opd_bias %p, sym_svma_out %p\n", |
| (void*)(opd_bias), (void*)*sym_avma_out); |
| |
| if (!VG_IS_8_ALIGNED(*sym_avma_out)) { |
| TRACE_SYMTAB(" ignore -- not 8-aligned: %s\n", sym_name); |
| return False; |
| } |
| |
| /* *sym_avma_out is a vma pointing into the .opd section. We |
| know the vma of the opd section start, so we can figure out |
| how far into the opd section this is. */ |
| |
| offset_in_opd = (Addr)(*sym_avma_out) - (Addr)(di->opd_avma); |
| if (offset_in_opd < 0 || offset_in_opd >= di->opd_size) { |
| TRACE_SYMTAB(" ignore -- invalid OPD offset: %s\n", sym_name); |
| return False; |
| } |
| |
| /* Now we want to know what's at that offset in the .opd |
| section. We can't look in the running image since it won't |
| necessarily have been mapped. But we can consult the oimage. |
| opd_img is the start address of the .opd in the oimage. |
| Hence: */ |
| |
| fn_descr = (ULong*)(opd_img + offset_in_opd); |
| |
| if (details) |
| TRACE_SYMTAB("opdXXY: offset %d, fn_descr %p\n", |
| offset_in_opd, fn_descr); |
| if (details) |
| TRACE_SYMTAB("opdXXZ: *fn_descr %p\n", (void*)(fn_descr[0])); |
| |
| /* opd_bias is the what we have to add to SVMAs found in .opd to |
| get plausible .text AVMAs for the entry point, and .data |
| AVMAs (presumably) for the TOC locations. We use the caller |
| supplied value (which is di->text_bias) for both of these. |
| Not sure why that is correct - it seems to work, and sounds |
| OK for fn_descr[0], but surely we need to use the data bias |
| and not the text bias for fn_descr[1] ? Oh Well. |
| */ |
| *sym_avma_out = fn_descr[0] + opd_bias; |
| *sym_tocptr_out = fn_descr[1] + opd_bias; |
| *from_opd_out = True; |
| is_in_opd = True; |
| |
| /* Do a final sanity check: if the symbol falls outside the |
| DebugInfo's mapped range, ignore it. Since *sym_avma_out has |
| been updated, that can be achieved simply by falling through |
| to the test below. */ |
| |
| # endif /* ppc64-linux nasty hack */ |
| } |
| |
| /* Here's yet another ppc64-linux hack. Get rid of leading dot if |
| the symbol is outside .opd. */ |
| # if defined(VGP_ppc64_linux) |
| if (di->opd_size > 0 |
| && !is_in_opd |
| && sym_name[0] == '.') { |
| vg_assert(!(*from_opd_out)); |
| *sym_name_out = &sym_name[1]; |
| } |
| # endif |
| |
| /* If no part of the symbol falls within the mapped range, |
| ignore it. */ |
| |
| in_text |
| = di->text_present |
| && di->text_size > 0 |
| && !(*sym_avma_out + *sym_size_out <= di->text_avma |
| || *sym_avma_out >= di->text_avma + di->text_size); |
| |
| in_data |
| = di->data_present |
| && di->data_size > 0 |
| && !(*sym_avma_out + *sym_size_out <= di->data_avma |
| || *sym_avma_out >= di->data_avma + di->data_size); |
| |
| in_sdata |
| = di->sdata_present |
| && di->sdata_size > 0 |
| && !(*sym_avma_out + *sym_size_out <= di->sdata_avma |
| || *sym_avma_out >= di->sdata_avma + di->sdata_size); |
| |
| in_bss |
| = di->bss_present |
| && di->bss_size > 0 |
| && !(*sym_avma_out + *sym_size_out <= di->bss_avma |
| || *sym_avma_out >= di->bss_avma + di->bss_size); |
| |
| |
| if (*is_text_out) { |
| /* This used to reject any symbol falling outside the text |
| segment ("if (!in_text) ..."). Now it is relaxed slightly, |
| to reject only symbols which fall outside the area mapped |
| r-x. This is in accordance with r7427. See |
| "Comment_Regarding_Text_Range_Checks" in storage.c for |
| background. */ |
| Bool in_rx; |
| vg_assert(di->have_rx_map); |
| in_rx = (!(*sym_avma_out + *sym_size_out <= di->rx_map_avma |
| || *sym_avma_out >= di->rx_map_avma + di->rx_map_size)); |
| if (in_text) |
| vg_assert(in_rx); |
| if (!in_rx) { |
| TRACE_SYMTAB( |
| "ignore -- %#lx .. %#lx outside .text svma range %#lx .. %#lx\n", |
| *sym_avma_out, *sym_avma_out + *sym_size_out, |
| di->text_avma, |
| di->text_avma + di->text_size); |
| return False; |
| } |
| } else { |
| if (!(in_data || in_sdata || in_bss)) { |
| TRACE_SYMTAB( |
| "ignore -- %#lx .. %#lx outside .data / .sdata / .bss svma ranges\n", |
| *sym_avma_out, *sym_avma_out + *sym_size_out); |
| return False; |
| } |
| } |
| |
| # if defined(VGP_ppc64_linux) |
| /* It's crucial that we never add symbol addresses in the .opd |
| section. This would completely mess up function redirection and |
| intercepting. This assert ensures that any symbols that make it |
| into the symbol table on ppc64-linux don't point into .opd. */ |
| if (di->opd_present && di->opd_size > 0) { |
| vg_assert(*sym_avma_out + *sym_size_out <= di->opd_avma |
| || *sym_avma_out >= di->opd_avma + di->opd_size); |
| } |
| # endif |
| |
| /* Acquire! */ |
| return True; |
| } |
| |
| |
| /* Read an ELF symbol table (normal or dynamic). This one is for the |
| "normal" case ({x86,amd64,ppc32}-linux). */ |
| static |
| __attribute__((unused)) /* not referred to on all targets */ |
| void read_elf_symtab__normal( |
| struct _DebugInfo* di, UChar* tab_name, |
| ElfXX_Sym* symtab_img, SizeT symtab_szB, |
| UChar* strtab_img, SizeT strtab_szB, |
| UChar* opd_img /* ppc64-linux only */ |
| ) |
| { |
| Word i; |
| Addr sym_svma, sym_avma_really; |
| Char *sym_name, *sym_name_really; |
| Int sym_size; |
| Addr sym_tocptr; |
| Bool from_opd, is_text; |
| DiSym risym; |
| ElfXX_Sym *sym; |
| |
| if (strtab_img == NULL || symtab_img == NULL) { |
| Char buf[80]; |
| vg_assert(VG_(strlen)(tab_name) < 40); |
| VG_(sprintf)(buf, " object doesn't have a %s", tab_name); |
| ML_(symerr)(di, False, buf); |
| return; |
| } |
| |
| TRACE_SYMTAB("\n--- Reading (ELF, standard) %s (%ld entries) ---\n", |
| tab_name, symtab_szB/sizeof(ElfXX_Sym) ); |
| |
| /* Perhaps should start at i = 1; ELF docs suggest that entry |
| 0 always denotes 'unknown symbol'. */ |
| for (i = 1; i < (Word)(symtab_szB/sizeof(ElfXX_Sym)); i++) { |
| sym = & symtab_img[i]; |
| sym_name = (UChar*)(strtab_img + sym->st_name); |
| sym_svma = sym->st_value; |
| |
| if (di->trace_symtab) |
| show_raw_elf_symbol(i, sym, sym_name, sym_svma, False); |
| |
| if (get_elf_symbol_info(di, sym, sym_name, sym_svma, |
| opd_img, di->text_bias, |
| &sym_name_really, |
| &sym_avma_really, |
| &sym_size, |
| &sym_tocptr, |
| &from_opd, &is_text)) { |
| |
| risym.addr = sym_avma_really; |
| risym.size = sym_size; |
| risym.name = ML_(addStr) ( di, sym_name_really, -1 ); |
| risym.tocptr = sym_tocptr; |
| risym.isText = is_text; |
| vg_assert(risym.name != NULL); |
| vg_assert(risym.tocptr == 0); /* has no role except on ppc64-linux */ |
| ML_(addSym) ( di, &risym ); |
| |
| if (di->trace_symtab) { |
| VG_(printf)(" rec(%c) [%4ld]: " |
| " val %#010lx, sz %4d %s\n", |
| is_text ? 't' : 'd', |
| i, |
| risym.addr, |
| (Int)risym.size, |
| (HChar*)risym.name |
| ); |
| } |
| |
| } |
| } |
| } |
| |
| |
| /* Read an ELF symbol table (normal or dynamic). This one is for |
| ppc64-linux, which requires special treatment. */ |
| |
| typedef |
| struct { |
| Addr addr; |
| UChar* name; |
| } |
| TempSymKey; |
| |
| typedef |
| struct { |
| TempSymKey key; |
| Addr tocptr; |
| Int size; |
| Bool from_opd; |
| Bool is_text; |
| } |
| TempSym; |
| |
| static Word cmp_TempSymKey ( TempSymKey* key1, TempSym* elem2 ) { |
| if (key1->addr < elem2->key.addr) return -1; |
| if (key1->addr > elem2->key.addr) return 1; |
| return (Word)VG_(strcmp)(key1->name, elem2->key.name); |
| } |
| |
| static |
| __attribute__((unused)) /* not referred to on all targets */ |
| void read_elf_symtab__ppc64_linux( |
| struct _DebugInfo* di, UChar* tab_name, |
| ElfXX_Sym* symtab_img, SizeT symtab_szB, |
| UChar* strtab_img, SizeT strtab_szB, |
| UChar* opd_img /* ppc64-linux only */ |
| ) |
| { |
| Word i; |
| Int old_size; |
| Addr sym_svma, sym_avma_really; |
| Char *sym_name, *sym_name_really; |
| Int sym_size; |
| Addr sym_tocptr, old_tocptr; |
| Bool from_opd, modify_size, modify_tocptr, is_text; |
| DiSym risym; |
| ElfXX_Sym *sym; |
| OSet *oset; |
| TempSymKey key; |
| TempSym *elem; |
| TempSym *prev; |
| |
| if (strtab_img == NULL || symtab_img == NULL) { |
| Char buf[80]; |
| vg_assert(VG_(strlen)(tab_name) < 40); |
| VG_(sprintf)(buf, " object doesn't have a %s", tab_name); |
| ML_(symerr)(di, False, buf); |
| return; |
| } |
| |
| TRACE_SYMTAB("\n--- Reading (ELF, ppc64-linux) %s (%ld entries) ---\n", |
| tab_name, symtab_szB/sizeof(ElfXX_Sym) ); |
| |
| oset = VG_(OSetGen_Create)( offsetof(TempSym,key), |
| (OSetCmp_t)cmp_TempSymKey, |
| ML_(dinfo_zalloc), "di.respl.1", |
| ML_(dinfo_free) ); |
| vg_assert(oset); |
| |
| /* Perhaps should start at i = 1; ELF docs suggest that entry |
| 0 always denotes 'unknown symbol'. */ |
| for (i = 1; i < (Word)(symtab_szB/sizeof(ElfXX_Sym)); i++) { |
| sym = & symtab_img[i]; |
| sym_name = (Char*)(strtab_img + sym->st_name); |
| sym_svma = sym->st_value; |
| |
| if (di->trace_symtab) |
| show_raw_elf_symbol(i, sym, sym_name, sym_svma, True); |
| |
| if (get_elf_symbol_info(di, sym, sym_name, sym_svma, |
| opd_img, di->text_bias, |
| &sym_name_really, |
| &sym_avma_really, |
| &sym_size, |
| &sym_tocptr, |
| &from_opd, &is_text)) { |
| |
| /* Check if we've seen this (name,addr) key before. */ |
| key.addr = sym_avma_really; |
| key.name = sym_name_really; |
| prev = VG_(OSetGen_Lookup)( oset, &key ); |
| |
| if (prev) { |
| |
| /* Seen it before. Fold in whatever new info we can. */ |
| modify_size = False; |
| modify_tocptr = False; |
| old_size = 0; |
| old_tocptr = 0; |
| |
| if (prev->from_opd && !from_opd |
| && (prev->size == 24 || prev->size == 16) |
| && sym_size != prev->size) { |
| /* Existing one is an opd-redirect, with a bogus size, |
| so the only useful new fact we have is the real size |
| of the symbol. */ |
| modify_size = True; |
| old_size = prev->size; |
| prev->size = sym_size; |
| } |
| else |
| if (!prev->from_opd && from_opd |
| && (sym_size == 24 || sym_size == 16)) { |
| /* Existing one is non-opd, new one is opd. What we |
| can acquire from the new one is the TOC ptr to be |
| used. Since the existing sym is non-toc, it |
| shouldn't currently have an known TOC ptr. */ |
| vg_assert(prev->tocptr == 0); |
| modify_tocptr = True; |
| old_tocptr = prev->tocptr; |
| prev->tocptr = sym_tocptr; |
| } |
| else { |
| /* ignore. can we do better here? */ |
| } |
| |
| /* Only one or the other is possible (I think) */ |
| vg_assert(!(modify_size && modify_tocptr)); |
| |
| if (modify_size && di->trace_symtab) { |
| VG_(printf)(" modify (old sz %4d) " |
| " val %#010lx, toc %#010lx, sz %4d %s\n", |
| old_size, |
| prev->key.addr, |
| prev->tocptr, |
| (Int) prev->size, |
| (HChar*)prev->key.name |
| ); |
| } |
| if (modify_tocptr && di->trace_symtab) { |
| VG_(printf)(" modify (upd tocptr) " |
| " val %#010lx, toc %#010lx, sz %4d %s\n", |
| prev->key.addr, |
| prev->tocptr, |
| (Int) prev->size, |
| (HChar*)prev->key.name |
| ); |
| } |
| |
| } else { |
| |
| /* A new (name,addr) key. Add and continue. */ |
| elem = VG_(OSetGen_AllocNode)(oset, sizeof(TempSym)); |
| vg_assert(elem); |
| elem->key = key; |
| elem->tocptr = sym_tocptr; |
| elem->size = sym_size; |
| elem->from_opd = from_opd; |
| elem->is_text = is_text; |
| VG_(OSetGen_Insert)(oset, elem); |
| if (di->trace_symtab) { |
| VG_(printf)(" to-oset [%4ld]: " |
| " val %#010lx, toc %#010lx, sz %4d %s\n", |
| i, |
| elem->key.addr, |
| elem->tocptr, |
| (Int) elem->size, |
| (HChar*)elem->key.name |
| ); |
| } |
| |
| } |
| } |
| } |
| |
| /* All the syms that matter are in the oset. Now pull them out, |
| build a "standard" symbol table, and nuke the oset. */ |
| |
| i = 0; |
| VG_(OSetGen_ResetIter)( oset ); |
| |
| while ( (elem = VG_(OSetGen_Next)(oset)) ) { |
| risym.addr = elem->key.addr; |
| risym.size = elem->size; |
| risym.name = ML_(addStr) ( di, elem->key.name, -1 ); |
| risym.tocptr = elem->tocptr; |
| risym.isText = elem->is_text; |
| vg_assert(risym.name != NULL); |
| |
| ML_(addSym) ( di, &risym ); |
| if (di->trace_symtab) { |
| VG_(printf)(" rec(%c) [%4ld]: " |
| " val %#010lx, toc %#010lx, sz %4d %s\n", |
| risym.isText ? 't' : 'd', |
| i, |
| risym.addr, |
| risym.tocptr, |
| (Int) risym.size, |
| (HChar*)risym.name |
| ); |
| } |
| i++; |
| } |
| |
| VG_(OSetGen_Destroy)( oset ); |
| } |
| |
| |
| /* |
| * This routine for calculating the CRC for a separate debug file |
| * is GPLed code borrowed from GNU binutils. |
| */ |
| static UInt |
| calc_gnu_debuglink_crc32(UInt crc, const UChar *buf, Int len) |
| { |
| static const UInt crc32_table[256] = |
| { |
| 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, |
| 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, |
| 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, |
| 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, |
| 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, |
| 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, |
| 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, |
| 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, |
| 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, |
| 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, |
| 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, |
| 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, |
| 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, |
| 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, |
| 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, |
| 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, |
| 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, |
| 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, |
| 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, |
| 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, |
| 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, |
| 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, |
| 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, |
| 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, |
| 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, |
| 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, |
| 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, |
| 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, |
| 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, |
| 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, |
| 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, |
| 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, |
| 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, |
| 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, |
| 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, |
| 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, |
| 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, |
| 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, |
| 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, |
| 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, |
| 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, |
| 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, |
| 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, |
| 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, |
| 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, |
| 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, |
| 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, |
| 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, |
| 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, |
| 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, |
| 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, |
| 0x2d02ef8d |
| }; |
| const UChar *end; |
| |
| crc = ~crc & 0xffffffff; |
| for (end = buf + len; buf < end; ++ buf) |
| crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8); |
| return ~crc & 0xffffffff;; |
| } |
| |
| /* |
| * Try and open a separate debug file, ignoring any where the CRC does |
| * not match the value from the main object file. |
| */ |
| static |
| Addr open_debug_file( Char* name, UInt crc, /*OUT*/UWord* size ) |
| { |
| SysRes fd, sres; |
| struct vg_stat stat_buf; |
| UInt calccrc; |
| |
| fd = VG_(open)(name, VKI_O_RDONLY, 0); |
| if (fd.isError) |
| return 0; |
| |
| if (VG_(fstat)(fd.res, &stat_buf) != 0) { |
| VG_(close)(fd.res); |
| return 0; |
| } |
| |
| if (VG_(clo_verbosity) > 1) |
| VG_(message)(Vg_DebugMsg, "Reading debug info from %s ..", name); |
| |
| *size = stat_buf.st_size; |
| |
| sres = VG_(am_mmap_file_float_valgrind) |
| ( *size, VKI_PROT_READ, fd.res, 0 ); |
| |
| VG_(close)(fd.res); |
| |
| if (sres.isError) |
| return 0; |
| |
| calccrc = calc_gnu_debuglink_crc32(0, (UChar*)sres.res, *size); |
| if (calccrc != crc) { |
| SysRes res = VG_(am_munmap_valgrind)(sres.res, *size); |
| vg_assert(!res.isError); |
| if (VG_(clo_verbosity) > 1) |
| VG_(message)(Vg_DebugMsg, |
| ".. CRC mismatch (computed %08x wanted %08x)", calccrc, crc); |
| return 0; |
| } |
| |
| return sres.res; |
| } |
| |
| /* |
| * Try to find a separate debug file for a given object file. |
| */ |
| static |
| Addr find_debug_file( struct _DebugInfo* di, |
| Char* objpath, Char* debugname, |
| UInt crc, /*OUT*/UWord* size ) |
| { |
| Char *objdir = ML_(dinfo_strdup)("di.fdf.1", objpath); |
| Char *objdirptr; |
| Char *debugpath; |
| Addr addr = 0; |
| |
| if ((objdirptr = VG_(strrchr)(objdir, '/')) != NULL) |
| *objdirptr = '\0'; |
| |
| debugpath = ML_(dinfo_zalloc)( |
| "di.fdf.2", |
| VG_(strlen)(objdir) + VG_(strlen)(debugname) + 32); |
| |
| VG_(sprintf)(debugpath, "%s/%s", objdir, debugname); |
| |
| if ((addr = open_debug_file(debugpath, crc, size)) == 0) { |
| VG_(sprintf)(debugpath, "%s/.debug/%s", objdir, debugname); |
| if ((addr = open_debug_file(debugpath, crc, size)) == 0) { |
| VG_(sprintf)(debugpath, "/usr/lib/debug%s/%s", objdir, debugname); |
| addr = open_debug_file(debugpath, crc, size); |
| } |
| } |
| |
| if (addr) { |
| TRACE_SYMTAB("\n"); |
| TRACE_SYMTAB("------ Found a debuginfo file: %s\n", debugpath); |
| } |
| |
| ML_(dinfo_free)(debugpath); |
| ML_(dinfo_free)(objdir); |
| |
| return addr; |
| } |
| |
| |
| static Bool contained_within ( Addr outer, UWord n_outer, |
| Addr inner, UWord n_inner ) |
| { |
| if (n_outer == 0 || n_inner == 0) |
| return False; |
| /* Simplistic .. assumes no wraparound (reasonably enough) */ |
| if (inner >= outer && inner+n_inner <= outer+n_outer) |
| return True; |
| return False; |
| } |
| |
| static void* INDEX_BIS ( void* base, Word index, Word scale ) { |
| return (void*)( ((UChar*)base) + index * scale ); |
| } |
| |
| static Addr round_Addr_upwards ( Addr a, UInt align ) |
| { |
| if (align > 0) { |
| vg_assert(-1 != VG_(log2)(align)); |
| a = VG_ROUNDUP(a, align); |
| } |
| return a; |
| } |
| |
| |
| /* Find the file offset corresponding to SVMA by using the program |
| headers. This is taken from binutils-2.17/binutils/readelf.c |
| offset_from_vma(). */ |
| static |
| Word file_offset_from_svma ( /*OUT*/Bool* ok, |
| Addr svma, |
| ElfXX_Phdr* phdr_img, |
| Word phdr_nent, |
| Word phdr_ent_szB ) |
| { |
| Word i; |
| ElfXX_Phdr* seg; |
| for (i = 0; i < phdr_nent; i++) { |
| seg = INDEX_BIS( phdr_img, i, phdr_ent_szB ); |
| if (seg->p_type != PT_LOAD) |
| continue; |
| if (svma >= (seg->p_vaddr & -seg->p_align) |
| && svma + 1 <= seg->p_vaddr + seg->p_filesz) { |
| *ok = True; |
| return svma - seg->p_vaddr + seg->p_offset; |
| } |
| } |
| *ok = False; |
| return 0; |
| } |
| |
| /* The central function for reading ELF debug info. For the |
| object/exe specified by the DebugInfo, find ELF sections, then read |
| the symbols, line number info, file name info, CFA (stack-unwind |
| info) and anything else we want, into the tables within the |
| supplied DebugInfo. |
| */ |
| Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di ) |
| { |
| Bool res, ok; |
| SysRes fd, sres; |
| Word i; |
| |
| /* Image addresses for the ELF file we're working with. */ |
| Addr oimage = 0; |
| UWord n_oimage = 0; |
| |
| /* Ditto for any ELF debuginfo file that we might happen to load. */ |
| Addr dimage = 0; |
| UWord n_dimage = 0; |
| |
| /* ELF header for the main file. Should == oimage since is at |
| start of file. */ |
| ElfXX_Ehdr* ehdr_img = NULL; |
| |
| /* Program header table image addr, # entries, entry size */ |
| ElfXX_Phdr* phdr_img = NULL; |
| UWord phdr_nent = 0; |
| UWord phdr_ent_szB = 0; |
| |
| /* Section header image addr, # entries, entry size. Also the |
| associated string table. */ |
| ElfXX_Shdr* shdr_img = NULL; |
| UWord shdr_nent = 0; |
| UWord shdr_ent_szB = 0; |
| UChar* shdr_strtab_img = NULL; |
| |
| /* To do with figuring out where .sbss is relative to .bss. A |
| kludge at the best of times. */ |
| SizeT sbss_size; |
| Addr sbss_svma; |
| UInt bss_align; |
| UInt sbss_align; |
| UInt data_align; |
| SizeT bss_totsize; |
| Addr gen_bss_lowest_svma; |
| |
| vg_assert(di); |
| vg_assert(di->have_rx_map == True); |
| vg_assert(di->have_rw_map == True); |
| vg_assert(di->rx_map_size > 0); |
| vg_assert(di->rw_map_size > 0); |
| vg_assert(di->have_dinfo == False); |
| vg_assert(di->filename); |
| vg_assert(!di->memname); |
| vg_assert(!di->symtab); |
| vg_assert(!di->loctab); |
| vg_assert(!di->cfsi); |
| vg_assert(!di->cfsi_exprs); |
| vg_assert(!di->strchunks); |
| vg_assert(!di->soname); |
| |
| /* If these don't hold true, it means that m_syswrap/m_aspacemgr |
| managed to do a mapping where the start isn't page aligned. |
| Which sounds pretty bogus to me. */ |
| vg_assert(VG_IS_PAGE_ALIGNED(di->rx_map_avma)); |
| vg_assert(VG_IS_PAGE_ALIGNED(di->rw_map_avma)); |
| |
| /* ---------------------------------------------------------- |
| At this point, there is very little information in the |
| DebugInfo. We only know that something that looks like an ELF |
| file has been mapped rx-ishly as recorded with the di->*rx_map* |
| fields and has also been mapped rw-ishly as recorded with the |
| di->*rw_map* fields. First we examine the file's ELF Program |
| Header, and, by comparing that against the di->*r{w,x}_map* |
| info, try to figure out the AVMAs for the sections we care |
| about, that should have been mapped: text, data, sdata, bss got, |
| plt, and toc. |
| ---------------------------------------------------------- */ |
| |
| oimage = (Addr)NULL; |
| if (VG_(clo_verbosity) > 1 || VG_(clo_trace_redir)) |
| VG_(message)(Vg_DebugMsg, "Reading syms from %s (%#lx)", |
| di->filename, di->rx_map_avma ); |
| |
| /* mmap the object image aboard, so that we can read symbols and |
| line number info out of it. It will be munmapped immediately |
| thereafter; it is only aboard transiently. */ |
| |
| fd = VG_(open)(di->filename, VKI_O_RDONLY, 0); |
| if (fd.isError) { |
| ML_(symerr)(di, True, "Can't open .so/.exe to read symbols?!"); |
| return False; |
| } |
| |
| { Long n_oimageLL = VG_(fsize)(fd.res); |
| if (n_oimageLL <= 0) { |
| ML_(symerr)(di, True, "Can't stat .so/.exe (to determine its size)?!"); |
| VG_(close)(fd.res); |
| return False; |
| } |
| n_oimage = (UWord)(ULong)n_oimageLL; |
| } |
| |
| sres = VG_(am_mmap_file_float_valgrind) |
| ( n_oimage, VKI_PROT_READ, fd.res, 0 ); |
| |
| VG_(close)(fd.res); |
| |
| if (sres.isError) { |
| VG_(message)(Vg_UserMsg, "warning: mmap failed on %s", di->filename ); |
| VG_(message)(Vg_UserMsg, " no symbols or debug info loaded" ); |
| return False; |
| } |
| |
| oimage = sres.res; |
| /* Check against wraparound. am_mmap_file_float_valgrind should |
| not produce a wrapped-around mapping. */ |
| vg_assert(n_oimage > 0); |
| vg_assert(oimage + n_oimage > oimage); |
| |
| if (0) { |
| VG_(printf)("read_elf_debug_info: OIMAGE = %p - %p\n", |
| (void*)oimage, (void*)(oimage + (UWord)n_oimage)); |
| } |
| |
| /* Ok, the object image is safely in oimage[0 .. n_oimage-1]. Now |
| verify that it is a valid ELF .so or executable image. */ |
| res = False; |
| ok = (n_oimage >= sizeof(ElfXX_Ehdr)); |
| ehdr_img = (ElfXX_Ehdr*)oimage; |
| |
| if (ok) |
| ok &= ML_(is_elf_object_file)(ehdr_img, n_oimage); |
| |
| if (!ok) { |
| ML_(symerr)(di, True, "Invalid ELF Header"); |
| goto out; |
| } |
| |
| /* Find where the program and section header tables are, and give |
| up if either is missing or outside the image (bogus). */ |
| phdr_img = (ElfXX_Phdr*)( ((UChar*)ehdr_img) + ehdr_img->e_phoff ); |
| phdr_nent = ehdr_img->e_phnum; |
| phdr_ent_szB = ehdr_img->e_phentsize; |
| |
| shdr_img = (ElfXX_Shdr*)( ((UChar*)ehdr_img) + ehdr_img->e_shoff ); |
| shdr_nent = ehdr_img->e_shnum; |
| shdr_ent_szB = ehdr_img->e_shentsize; |
| |
| TRACE_SYMTAB("------ Basic facts about the object ------\n"); |
| TRACE_SYMTAB("object: img %p n_oimage %ld\n", |
| (void*)oimage, n_oimage); |
| TRACE_SYMTAB("phdr: img %p nent %ld ent_szB %ld\n", |
| phdr_img, phdr_nent, phdr_ent_szB); |
| TRACE_SYMTAB("shdr: img %p nent %ld ent_szB %ld\n", |
| shdr_img, shdr_nent, shdr_ent_szB); |
| |
| if (phdr_nent == 0 |
| || !contained_within( |
| oimage, n_oimage, |
| (Addr)phdr_img, phdr_nent * phdr_ent_szB)) { |
| ML_(symerr)(di, True, "Missing or invalid ELF Program Header Table"); |
| goto out; |
| } |
| |
| if (shdr_nent == 0 |
| || !contained_within( |
| oimage, n_oimage, |
| (Addr)shdr_img, shdr_nent * shdr_ent_szB)) { |
| ML_(symerr)(di, True, "Missing or invalid ELF Section Header Table"); |
| goto out; |
| } |
| |
| /* Also find the section header's string table, and validate. */ |
| /* checked previously by is_elf_object_file: */ |
| vg_assert( ehdr_img->e_shstrndx != SHN_UNDEF ); |
| |
| shdr_strtab_img |
| = (UChar*)( ((UChar*)ehdr_img) |
| + shdr_img[ehdr_img->e_shstrndx].sh_offset); |
| if (!contained_within( oimage, n_oimage, |
| (Addr)shdr_strtab_img, |
| 1/*bogus, but we don't know the real size*/ )) { |
| ML_(symerr)(di, True, "Invalid ELF Section Header String Table"); |
| goto out; |
| } |
| |
| TRACE_SYMTAB("shdr: string table at %p\n", shdr_strtab_img ); |
| |
| /* Do another amazingly tedious thing: find out the .soname for |
| this object. Apparently requires looking through the program |
| header table. */ |
| TRACE_SYMTAB("\n"); |
| TRACE_SYMTAB("------ Looking for the soname ------\n"); |
| vg_assert(di->soname == NULL); |
| { |
| ElfXX_Addr prev_svma = 0; |
| |
| for (i = 0; i < phdr_nent; i++) { |
| ElfXX_Phdr* phdr = INDEX_BIS( phdr_img, i, phdr_ent_szB ); |
| |
| /* Make sure the PT_LOADable entries are in order */ |
| if (phdr->p_type == PT_LOAD) { |
| TRACE_SYMTAB("PT_LOAD in order?: %#lx %#lx\n", |
| prev_svma + 0UL, |
| phdr->p_vaddr + 0UL); |
| if (phdr->p_vaddr < prev_svma) { |
| ML_(symerr)(di, True, |
| "ELF Program Headers are not in ascending order"); |
| goto out; |
| } |
| prev_svma = phdr->p_vaddr; |
| } |
| |
| /* Try to get the soname. If there isn't one, use "NONE". |
| The seginfo needs to have some kind of soname in order to |
| facilitate writing redirect functions, since all redirect |
| specifications require a soname (pattern). */ |
| if (phdr->p_type == PT_DYNAMIC && di->soname == NULL) { |
| ElfXX_Dyn* dyn_img = (ElfXX_Dyn*)( ((UChar*)ehdr_img) |
| + phdr->p_offset); |
| Word stroff = -1; |
| UChar* strtab = NULL; |
| Word j; |
| for (j = 0; dyn_img[j].d_tag != DT_NULL; j++) { |
| switch (dyn_img[j].d_tag) { |
| case DT_SONAME: { |
| stroff = dyn_img[j].d_un.d_val; |
| break; |
| } |
| case DT_STRTAB: { |
| Bool ok2 = False; |
| Word offset = file_offset_from_svma( |
| &ok2, |
| dyn_img[j].d_un.d_ptr, |
| phdr_img, |
| phdr_nent, phdr_ent_szB |
| ); |
| if (ok2 && strtab == NULL) { |
| vg_assert(offset >= 0 && offset <= n_oimage); |
| strtab = ((UChar*)ehdr_img) + offset; |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| if (stroff != -1 && strtab != NULL) { |
| TRACE_SYMTAB("Found soname = %s\n", strtab+stroff); |
| di->soname = ML_(dinfo_strdup)("di.redi.1", strtab+stroff); |
| } |
| } |
| } /* for (i = 0; i < phdr_nent; i++) ... */ |
| } /* look for the soname */ |
| |
| /* If, after looking at all the program headers, we still didn't |
| find a soname, add a fake one. */ |
| if (di->soname == NULL) { |
| TRACE_SYMTAB("No soname found; using (fake) \"NONE\"\n"); |
| di->soname = "NONE"; |
| } |
| |
| /*SizeT*/ sbss_size = 0; |
| /*Addr */ sbss_svma = 0; |
| /*UInt */ bss_align = 0; |
| /*UInt */ sbss_align = 0; |
| |
| /* UInt */ data_align = 0; |
| /* SizeT */ bss_totsize = 0; |
| /* Addr */ gen_bss_lowest_svma = ~((Addr)0); |
| |
| /* Now read the section table. */ |
| TRACE_SYMTAB("\n"); |
| TRACE_SYMTAB("------ Examining the section headers " |
| "and program headers ------\n"); |
| TRACE_SYMTAB("rx: at %#lx are mapped foffsets %ld .. %ld\n", |
| di->rx_map_avma, |
| di->rx_map_foff, di->rx_map_foff + di->rx_map_size - 1 ); |
| TRACE_SYMTAB("rw: at %#lx are mapped foffsets %ld .. %ld\n", |
| di->rw_map_avma, |
| di->rw_map_foff, di->rw_map_foff + di->rw_map_size - 1 ); |
| |
| for (i = 0; i < shdr_nent; i++) { |
| ElfXX_Shdr* shdr = INDEX_BIS( shdr_img, i, shdr_ent_szB ); |
| UChar* name = shdr_strtab_img + shdr->sh_name; |
| Addr svma = shdr->sh_addr; |
| OffT foff = shdr->sh_offset; |
| UWord size = shdr->sh_size; |
| UInt alyn = shdr->sh_addralign; |
| Bool bits = !(shdr->sh_type == SHT_NOBITS); |
| Bool inrx = foff >= di->rx_map_foff |
| && foff < di->rx_map_foff + di->rx_map_size; |
| Bool inrw = foff >= di->rw_map_foff |
| && foff < di->rw_map_foff + di->rw_map_size; |
| |
| TRACE_SYMTAB(" [sec %2ld] %s %s al%2u foff %6ld .. %6ld " |
| " svma %p name \"%s\"\n", |
| i, inrx ? "rx" : " ", inrw ? "rw" : " ", alyn, |
| foff, foff+size-1, (void*)svma, name ); |
| |
| /* Check for sane-sized segments. SHT_NOBITS sections have zero |
| size in the file. */ |
| if ((foff >= n_oimage) || (foff + (bits ? size : 0) > n_oimage)) { |
| ML_(symerr)(di, True, "ELF Section extends beyond image end"); |
| goto out; |
| } |
| |
| /* Check for a sane alignment value. */ |
| if (alyn > 0 && -1 == VG_(log2)(alyn)) { |
| ML_(symerr)(di, True, "ELF Section contains invalid " |
| ".sh_addralign value"); |
| goto out; |
| } |
| |
| # define BAD(_secname) \ |
| do { ML_(symerr)(di, True, \ |
| "Can't make sense of " _secname \ |
| " section mapping"); \ |
| goto out; \ |
| } while (0) |
| |
| /* Find avma-s for: .text .data .sdata .bss .plt .got .opd |
| and .eh_frame */ |
| |
| /* Accept .text where mapped as rx (code) */ |
| if (0 == VG_(strcmp)(name, ".text")) { |
| if (inrx && size > 0 && !di->text_present) { |
| di->text_present = True; |
| di->text_svma = svma; |
| di->text_avma = di->rx_map_avma + foff - di->rx_map_foff; |
| di->text_size = size; |
| di->text_bias = di->text_avma - svma; |
| TRACE_SYMTAB("acquiring .text svma = %#lx .. %#lx\n", |
| di->text_svma, |
| di->text_svma + di->text_size - 1); |
| TRACE_SYMTAB("acquiring .text avma = %#lx .. %#lx\n", |
| di->text_avma, |
| di->text_avma + di->text_size - 1); |
| TRACE_SYMTAB("acquiring .text bias = %#lx\n", di->text_bias); |
| } else { |
| BAD(".text"); |
| } |
| } |
| |
| /* Accept .data where mapped as rw (data), even if zero-sized */ |
| if (0 == VG_(strcmp)(name, ".data")) { |
| if (inrw && size >= 0 && !di->data_present) { |
| if (alyn > data_align) |
| data_align = alyn; |
| di->data_present = True; |
| di->data_svma = svma; |
| di->data_avma = di->rw_map_avma + foff - di->rw_map_foff; |
| di->data_size = size; |
| di->data_bias = di->data_avma - svma; |
| TRACE_SYMTAB("acquiring .data svma = %#lx .. %#lx\n", |
| di->data_svma, |
| di->data_svma + di->data_size - 1); |
| TRACE_SYMTAB("acquiring .data avma = %#lx .. %#lx\n", |
| di->data_avma, |
| di->data_avma + di->data_size - 1); |
| TRACE_SYMTAB("acquiring .data bias = %#lx\n", di->data_bias); |
| } else { |
| BAD(".data"); |
| } |
| } |
| |
| /* Accept .sdata where mapped as rw (data) */ |
| if (0 == VG_(strcmp)(name, ".sdata")) { |
| if (inrw && size > 0 && !di->sdata_present) { |
| if (alyn > data_align) |
| data_align = alyn; |
| di->sdata_present = True; |
| di->sdata_svma = svma; |
| di->sdata_avma = di->rw_map_avma + foff - di->rw_map_foff; |
| di->sdata_size = size; |
| di->sdata_bias = di->sdata_avma - svma; |
| TRACE_SYMTAB("acquiring .sdata svma = %#lx .. %#lx\n", |
| di->sdata_svma, |
| di->sdata_svma + di->sdata_size - 1); |
| TRACE_SYMTAB("acquiring .sdata avma = %#lx .. %#lx\n", |
| di->sdata_avma, |
| di->sdata_avma + di->sdata_size - 1); |
| TRACE_SYMTAB("acquiring .sdata bias = %#lx\n", di->sdata_bias); |
| } else { |
| BAD(".sdata"); |
| } |
| } |
| |
| /* Accept .bss where mapped as rw (data), even if zero-sized */ |
| if (0 == VG_(strcmp)(name, ".bss")) { |
| if (inrw && size >= 0 && !di->bss_present) { |
| bss_totsize += round_Addr_upwards(size, alyn); |
| if (svma < gen_bss_lowest_svma) |
| gen_bss_lowest_svma = svma; |
| TRACE_SYMTAB("increasing total bss-like size to %ld\n", |
| bss_totsize); |
| di->bss_present = True; |
| di->bss_svma = svma; |
| di->bss_avma = di->rw_map_avma + foff - di->rw_map_foff; |
| di->bss_size = size; |
| di->bss_bias = di->bss_avma - svma; |
| bss_align = alyn; |
| TRACE_SYMTAB("acquiring .bss svma = %#lx .. %#lx\n", |
| di->bss_svma, |
| di->bss_svma + di->bss_size - 1); |
| TRACE_SYMTAB("acquiring .bss avma = %#lx .. %#lx\n", |
| di->bss_avma, |
| di->bss_avma + di->bss_size - 1); |
| TRACE_SYMTAB("acquiring .bss bias = %#lx\n", di->bss_bias); |
| } else |
| |
| /* Now one from the wtf?! department ... */ |
| if (inrx && (!inrw) && size >= 0 && !di->bss_present) { |
| /* File contains a .bss, but it got mapped as rx only. |
| This is very strange. For now, just pretend we didn't |
| see it :-) */ |
| di->bss_present = False; |
| di->bss_svma = 0; |
| di->bss_avma = 0; |
| di->bss_size = 0; |
| di->bss_bias = 0; |
| bss_align = 0; |
| if (!VG_(clo_xml)) { |
| VG_(message)(Vg_UserMsg, "Warning: the following file's .bss is " |
| "mapped r-x only - ignoring .bss syms"); |
| VG_(message)(Vg_UserMsg, " %s", di->filename |
| ? di->filename |
| : (UChar*)"(null?!)" ); |
| } |
| } else |
| |
| if ((!inrw) && (!inrx) && size > 0 && !di->bss_present) { |
| /* File contains a .bss, but it didn't get mapped. Ignore. */ |
| di->bss_present = False; |
| di->bss_svma = 0; |
| di->bss_avma = 0; |
| di->bss_size = 0; |
| di->bss_bias = 0; |
| bss_align = 0; |
| } else { |
| BAD(".bss"); |
| } |
| } |
| |
| /* Accept .sbss where mapped as rw (data) */ |
| if (0 == VG_(strcmp)(name, ".sbss")) { |
| if (inrw && size > 0 && sbss_size == 0) { |
| bss_totsize += round_Addr_upwards(size, alyn); |
| if (svma < gen_bss_lowest_svma) |
| gen_bss_lowest_svma = svma; |
| TRACE_SYMTAB("increasing total bss-like size to %ld\n", |
| bss_totsize); |
| sbss_size = size; |
| sbss_svma = svma; |
| sbss_align = alyn; |
| } else { |
| BAD(".sbss"); |
| } |
| } |
| |
| /* Accept .dynbss where mapped as rw (data) */ |
| if (0 == VG_(strcmp)(name, ".dynbss")) { |
| if (inrw && size > 0 /* && sbss_size == 0*/) { |
| bss_totsize += round_Addr_upwards(size, alyn); |
| if (svma < gen_bss_lowest_svma) |
| gen_bss_lowest_svma = svma; |
| TRACE_SYMTAB("increasing total bss-like size to %ld\n", |
| bss_totsize); |
| } else { |
| BAD(".dynbss"); |
| } |
| } |
| |
| /* Accept .got where mapped as rw (data) */ |
| if (0 == VG_(strcmp)(name, ".got")) { |
| if (inrw && size > 0 && !di->got_present) { |
| di->got_present = True; |
| di->got_avma = di->rw_map_avma + foff - di->rw_map_foff; |
| di->got_size = size; |
| TRACE_SYMTAB("acquiring .got avma = %#lx\n", di->got_avma); |
| } else { |
| BAD(".got"); |
| } |
| } |
| |
| /* Accept .got.plt where mapped as rw (data) */ |
| if (0 == VG_(strcmp)(name, ".got.plt")) { |
| if (inrw && size > 0 && !di->gotplt_present) { |
| di->gotplt_present = True; |
| di->gotplt_avma = di->rw_map_avma + foff - di->rw_map_foff; |
| di->gotplt_size = size; |
| TRACE_SYMTAB("acquiring .got.plt avma = %#lx\n", di->gotplt_avma); |
| } else if (size != 0) { |
| BAD(".got.plt"); |
| } |
| } |
| |
| /* PLT is different on different platforms, it seems. */ |
| # if defined(VGP_x86_linux) || defined(VGP_amd64_linux) |
| /* Accept .plt where mapped as rx (code) */ |
| if (0 == VG_(strcmp)(name, ".plt")) { |
| if (inrx && size > 0 && !di->plt_present) { |
| di->plt_present = True; |
| di->plt_avma = di->rx_map_avma + foff - di->rx_map_foff; |
| di->plt_size = size; |
| TRACE_SYMTAB("acquiring .plt avma = %#lx\n", di->plt_avma); |
| } else { |
| BAD(".plt"); |
| } |
| } |
| # elif defined(VGP_ppc32_linux) |
| /* Accept .plt where mapped as rw (data) */ |
| if (0 == VG_(strcmp)(name, ".plt")) { |
| if (inrw && size > 0 && !di->plt_present) { |
| di->plt_present = True; |
| di->plt_avma = di->rw_map_avma + foff - di->rw_map_foff; |
| di->plt_size = size; |
| TRACE_SYMTAB("acquiring .plt avma = %#lx\n", di->plt_avma); |
| } else { |
| BAD(".plt"); |
| } |
| } |
| # elif defined(VGP_ppc64_linux) |
| /* Accept .plt where mapped as rw (data), or unmapped */ |
| if (0 == VG_(strcmp)(name, ".plt")) { |
| if (inrw && size > 0 && !di->plt_present) { |
| di->plt_present = True; |
| di->plt_avma = di->rw_map_avma + foff - di->rw_map_foff; |
| di->plt_size = size; |
| TRACE_SYMTAB("acquiring .plt avma = %#lx\n", di->plt_avma); |
| } else |
| if ((!inrw) && (!inrx) && size > 0 && !di->plt_present) { |
| /* File contains a .plt, but it didn't get mapped. |
| Presumably it is not required on this platform. At |
| least don't reject the situation as invalid. */ |
| di->plt_present = True; |
| di->plt_avma = 0; |
| di->plt_size = 0; |
| } else { |
| BAD(".plt"); |
| } |
| } |
| # else |
| # error "Unsupported platform" |
| # endif |
| |
| /* Accept .opd where mapped as rw (data) */ |
| if (0 == VG_(strcmp)(name, ".opd")) { |
| if (inrw && size > 0 && !di->opd_present) { |
| di->opd_present = True; |
| di->opd_avma = di->rw_map_avma + foff - di->rw_map_foff; |
| di->opd_size = size; |
| TRACE_SYMTAB("acquiring .opd avma = %#lx\n", di->opd_avma); |
| } else { |
| BAD(".opd"); |
| } |
| } |
| |
| /* Accept .eh_frame where mapped as rx (code). This seems to be |
| the common case. However, if that doesn't pan out, try for |
| rw (data) instead. */ |
| if (0 == VG_(strcmp)(name, ".eh_frame")) { |
| if (inrx && size > 0 && !di->ehframe_present) { |
| di->ehframe_present = True; |
| di->ehframe_avma = di->rx_map_avma + foff - di->rx_map_foff; |
| di->ehframe_size = size; |
| TRACE_SYMTAB("acquiring .eh_frame avma = %#lx\n", di->ehframe_avma); |
| } else |
| if (inrw && size > 0 && !di->ehframe_present) { |
| di->ehframe_present = True; |
| di->ehframe_avma = di->rw_map_avma + foff - di->rw_map_foff; |
| di->ehframe_size = size; |
| TRACE_SYMTAB("acquiring .eh_frame avma = %#lx\n", di->ehframe_avma); |
| } else { |
| BAD(".eh_frame"); |
| } |
| } |
| |
| # undef BAD |
| |
| } |
| |
| /* Kludge: ignore all previous computations for .bss avma range, |
| and simply assume that .bss immediately follows .data/.sdata.*/ |
| if (1) { |
| SizeT data_al = round_Addr_upwards(di->data_avma, data_align) |
| - di->data_avma; |
| TRACE_SYMTAB("data_al = %ld\n", data_al); |
| bss_totsize += data_al; |
| di->bss_svma = gen_bss_lowest_svma; |
| di->bss_size = bss_totsize; |
| di->bss_avma = di->data_avma + (di->bss_svma - di->data_svma); |
| di->bss_bias = di->data_bias; |
| TRACE_SYMTAB("kludged .bss svma = %#lx .. %#lx\n", |
| di->bss_svma, di->bss_svma + di->bss_size - 1); |
| TRACE_SYMTAB("kludged .bss avma = %#lx .. %#lx\n", |
| di->bss_avma, di->bss_avma + di->bss_size - 1); |
| TRACE_SYMTAB("kludged .bss bias = %#lx\n", di->bss_bias); |
| } |
| |
| if (0) VG_(printf)("YYYY text_: avma %#lx size %ld bias %#lx\n", |
| di->text_avma, di->text_size, di->text_bias); |
| |
| if (VG_(clo_verbosity) > 2 || VG_(clo_trace_redir)) |
| VG_(message)(Vg_DebugMsg, " svma %#010lx, avma %#010lx", |
| di->text_avma - di->text_bias, |
| di->text_avma ); |
| |
| TRACE_SYMTAB("\n"); |
| TRACE_SYMTAB("------ Finding image addresses " |
| "for debug-info sections ------\n"); |
| |
| /* Find interesting sections, read the symbol table(s), read any debug |
| information */ |
| { |
| /* IMAGE addresses: pointers to start of sections in the |
| transiently loaded oimage, not in the fragments of the file |
| mapped in by the guest's dynamic linker. */ |
| UChar* strtab_img = NULL; /* .strtab */ |
| ElfXX_Sym* symtab_img = NULL; /* .symtab */ |
| UChar* dynstr_img = NULL; /* .dynstr */ |
| ElfXX_Sym* dynsym_img = NULL; /* .dynsym */ |
| UChar* debuglink_img = NULL; /* .gnu_debuglink */ |
| UChar* stab_img = NULL; /* .stab (stabs) */ |
| UChar* stabstr_img = NULL; /* .stabstr (stabs) */ |
| UChar* debug_line_img = NULL; /* .debug_line (dwarf2) */ |
| UChar* debug_info_img = NULL; /* .debug_info (dwarf2) */ |
| UChar* debug_abbv_img = NULL; /* .debug_abbrev (dwarf2) */ |
| UChar* debug_str_img = NULL; /* .debug_str (dwarf2) */ |
| UChar* debug_ranges_img = NULL; /* .debug_ranges (dwarf2) */ |
| UChar* debug_loc_img = NULL; /* .debug_loc (dwarf2) */ |
| UChar* dwarf1d_img = NULL; /* .debug (dwarf1) */ |
| UChar* dwarf1l_img = NULL; /* .line (dwarf1) */ |
| UChar* ehframe_img = NULL; /* .eh_frame (dwarf2) */ |
| UChar* opd_img = NULL; /* .opd (dwarf2, |
| ppc64-linux) */ |
| /* Section sizes, in bytes */ |
| SizeT strtab_sz = 0; |
| SizeT symtab_sz = 0; |
| SizeT dynstr_sz = 0; |
| SizeT dynsym_sz = 0; |
| SizeT debuglink_sz = 0; |
| SizeT stab_sz = 0; |
| SizeT stabstr_sz = 0; |
| SizeT debug_line_sz = 0; |
| SizeT debug_info_sz = 0; |
| SizeT debug_abbv_sz = 0; |
| SizeT debug_str_sz = 0; |
| SizeT debug_ranges_sz = 0; |
| SizeT debug_loc_sz = 0; |
| SizeT dwarf1d_sz = 0; |
| SizeT dwarf1l_sz = 0; |
| SizeT ehframe_sz = 0; |
| SizeT opd_sz_unused = 0; |
| |
| /* Find all interesting sections */ |
| |
| /* What FIND does: it finds the section called SEC_NAME. The |
| size of it is assigned to SEC_SIZE. The address of the |
| section in the transiently loaded oimage is assigned to |
| SEC_FILEA. Even for sections which are marked loadable, the |
| client's ld.so may not have loaded them yet, so there is no |
| guarantee that we can safely prod around in any such area). |
| Because the entire object file is transiently mapped aboard |
| for inspection, it's always safe to inspect that area. */ |
| |
| for (i = 0; i < ehdr_img->e_shnum; i++) { |
| |
| # define FIND(sec_name, sec_size, sec_img) \ |
| do { ElfXX_Shdr* shdr \ |
| = INDEX_BIS( shdr_img, i, shdr_ent_szB ); \ |
| if (0 == VG_(strcmp)(sec_name, shdr_strtab_img \ |
| + shdr->sh_name)) { \ |
| Bool nobits; \ |
| sec_img = (void*)(oimage + shdr->sh_offset); \ |
| sec_size = shdr->sh_size; \ |
| nobits = shdr->sh_type == SHT_NOBITS; \ |
| TRACE_SYMTAB( "%18s: img %p .. %p\n", \ |
| sec_name, (UChar*)sec_img, \ |
| ((UChar*)sec_img) + sec_size - 1); \ |
| /* SHT_NOBITS sections have zero size in the file. */ \ |
| if ( shdr->sh_offset \ |
| + (nobits ? 0 : sec_size) > n_oimage ) { \ |
| ML_(symerr)(di, True, \ |
| " section beyond image end?!"); \ |
| goto out; \ |
| } \ |
| } \ |
| } while (0); |
| |
| /* NAME SIZE IMAGE addr */ |
| FIND(".dynsym", dynsym_sz, dynsym_img) |
| FIND(".dynstr", dynstr_sz, dynstr_img) |
| FIND(".symtab", symtab_sz, symtab_img) |
| FIND(".strtab", strtab_sz, strtab_img) |
| |
| FIND(".gnu_debuglink", debuglink_sz, debuglink_img) |
| |
| FIND(".stab", stab_sz, stab_img) |
| FIND(".stabstr", stabstr_sz, stabstr_img) |
| |
| FIND(".debug_line", debug_line_sz, debug_line_img) |
| FIND(".debug_info", debug_info_sz, debug_info_img) |
| FIND(".debug_abbrev", debug_abbv_sz, debug_abbv_img) |
| FIND(".debug_str", debug_str_sz, debug_str_img) |
| FIND(".debug_ranges", debug_ranges_sz, debug_ranges_img) |
| FIND(".debug_loc", debug_loc_sz, debug_loc_img) |
| |
| FIND(".debug", dwarf1d_sz, dwarf1d_img) |
| FIND(".line", dwarf1l_sz, dwarf1l_img) |
| FIND(".eh_frame", ehframe_sz, ehframe_img) |
| |
| FIND(".opd", opd_sz_unused, opd_img) |
| |
| # undef FIND |
| } |
| |
| /* Did we find a debuglink section? */ |
| if (debuglink_img != NULL) { |
| UInt crc_offset = VG_ROUNDUP(VG_(strlen)(debuglink_img)+1, 4); |
| UInt crc; |
| |
| vg_assert(crc_offset + sizeof(UInt) <= debuglink_sz); |
| |
| /* Extract the CRC from the debuglink section */ |
| crc = *(UInt *)(debuglink_img + crc_offset); |
| |
| /* See if we can find a matching debug file */ |
| dimage = find_debug_file( di, di->filename, debuglink_img, |
| crc, &n_dimage ); |
| |
| if (dimage != 0 |
| && n_dimage >= sizeof(ElfXX_Ehdr) |
| && ML_(is_elf_object_file)((void*)dimage, n_dimage)) { |
| |
| /* Pull out and validate program header and section header info */ |
| ElfXX_Ehdr* ehdr_dimg = (ElfXX_Ehdr*)dimage; |
| ElfXX_Phdr* phdr_dimg = (ElfXX_Phdr*)( ((UChar*)ehdr_dimg) |
| + ehdr_dimg->e_phoff ); |
| UWord phdr_dnent = ehdr_dimg->e_phnum; |
| UWord phdr_dent_szB = ehdr_dimg->e_phentsize; |
| ElfXX_Shdr* shdr_dimg = (ElfXX_Shdr*)( ((UChar*)ehdr_dimg) |
| + ehdr_dimg->e_shoff ); |
| UWord shdr_dnent = ehdr_dimg->e_shnum; |
| UWord shdr_dent_szB = ehdr_dimg->e_shentsize; |
| UChar* shdr_strtab_dimg = NULL; |
| |
| Bool need_symtab, need_stabs, need_dwarf2, need_dwarf1; |
| |
| if (phdr_dnent == 0 |
| || !contained_within( |
| dimage, n_dimage, |
| (Addr)phdr_dimg, phdr_dnent * phdr_dent_szB)) { |
| ML_(symerr)(di, True, |
| "Missing or invalid ELF Program Header Table" |
| " (debuginfo file)"); |
| goto out; |
| } |
| |
| if (shdr_dnent == 0 |
| || !contained_within( |
| dimage, n_dimage, |
| (Addr)shdr_dimg, shdr_dnent * shdr_dent_szB)) { |
| ML_(symerr)(di, True, |
| "Missing or invalid ELF Section Header Table" |
| " (debuginfo file)"); |
| goto out; |
| } |
| |
| /* Also find the section header's string table, and validate. */ |
| /* checked previously by is_elf_object_file: */ |
| vg_assert( ehdr_dimg->e_shstrndx != SHN_UNDEF ); |
| |
| shdr_strtab_dimg |
| = (UChar*)( ((UChar*)ehdr_dimg) |
| + shdr_dimg[ehdr_dimg->e_shstrndx].sh_offset); |
| if (!contained_within( |
| dimage, n_dimage, |
| (Addr)shdr_strtab_dimg, |
| 1/*bogus, but we don't know the real size*/ )) { |
| ML_(symerr)(di, True, |
| "Invalid ELF Section Header String Table" |
| " (debuginfo file)"); |
| goto out; |
| } |
| |
| need_symtab = (NULL == symtab_img); |
| need_stabs = (NULL == stab_img); |
| need_dwarf2 = (NULL == debug_info_img); |
| need_dwarf1 = (NULL == dwarf1d_img); |
| |
| for (i = 0; i < ehdr_dimg->e_phnum; i++) { |
| ElfXX_Phdr* phdr |
| = INDEX_BIS( (void*)(dimage + ehdr_dimg->e_phoff), |
| i, phdr_ent_szB ); |
| if (phdr->p_type == PT_LOAD) { |
| //offset_dimage = di->text_avma - phdr->p_vaddr; |
| // FIXME: update di->text_bias at this point? |
| // or can we assume the SVMAs in the debuginfo |
| // file (hence, the biases) are the same as |
| // established from the main file? |
| break; |
| } |
| } |
| |
| /* Same deal as previous FIND, except only do it for those |
| sections for which we didn't find anything useful in |
| the main file. */ |
| |
| /* Find all interesting sections */ |
| for (i = 0; i < ehdr_dimg->e_shnum; i++) { |
| |
| # define FIND(condition, sec_name, sec_size, sec_img) \ |
| do { ElfXX_Shdr* shdr \ |
| = INDEX_BIS( shdr_dimg, i, shdr_dent_szB ); \ |
| if (condition \ |
| && 0 == VG_(strcmp)(sec_name, \ |
| shdr_strtab_dimg + shdr->sh_name)) { \ |
| Bool nobits; \ |
| if (0 != sec_img) \ |
| VG_(core_panic)("repeated section!\n"); \ |
| sec_img = (void*)(dimage + shdr->sh_offset); \ |
| sec_size = shdr->sh_size; \ |
| nobits = shdr->sh_type == SHT_NOBITS; \ |
| TRACE_SYMTAB( "%18s: dimg %p .. %p\n", \ |
| sec_name, \ |
| (UChar*)sec_img, \ |
| ((UChar*)sec_img) + sec_size - 1); \ |
| /* SHT_NOBITS sections have zero size in the file. */ \ |
| if ( shdr->sh_offset \ |
| + (nobits ? 0 : sec_size) > n_dimage ) { \ |
| ML_(symerr)(di, True, \ |
| " section beyond image end?!"); \ |
| goto out; \ |
| } \ |
| } \ |
| } while (0); |
| |
| /* NEEDED? NAME SIZE IMAGE addr */ |
| FIND(need_symtab, ".symtab", symtab_sz, symtab_img) |
| FIND(need_symtab, ".strtab", strtab_sz, strtab_img) |
| FIND(need_stabs, ".stab", stab_sz, stab_img) |
| FIND(need_stabs, ".stabstr", stabstr_sz, stabstr_img) |
| FIND(need_dwarf2, ".debug_line", debug_line_sz, debug_line_img) |
| FIND(need_dwarf2, ".debug_info", debug_info_sz, debug_info_img) |
| FIND(need_dwarf2, ".debug_abbrev", debug_abbv_sz, debug_abbv_img) |
| FIND(need_dwarf2, ".debug_str", debug_str_sz, debug_str_img) |
| FIND(need_dwarf2, ".debug_ranges", debug_ranges_sz, |
| debug_ranges_img) |
| FIND(need_dwarf2, ".debug_loc", debug_loc_sz, debug_loc_img) |
| FIND(need_dwarf1, ".debug", dwarf1d_sz, dwarf1d_img) |
| FIND(need_dwarf1, ".line", dwarf1l_sz, dwarf1l_img) |
| |
| # undef FIND |
| } |
| } |
| } |
| |
| /* Check some sizes */ |
| vg_assert((dynsym_sz % sizeof(ElfXX_Sym)) == 0); |
| vg_assert((symtab_sz % sizeof(ElfXX_Sym)) == 0); |
| |
| /* Read symbols */ |
| { |
| void (*read_elf_symtab)(struct _DebugInfo*,UChar*, |
| ElfXX_Sym*,SizeT, |
| UChar*,SizeT, |
| UChar*); |
| # if defined(VGP_ppc64_linux) |
| read_elf_symtab = read_elf_symtab__ppc64_linux; |
| # else |
| read_elf_symtab = read_elf_symtab__normal; |
| # endif |
| read_elf_symtab(di, "symbol table", |
| symtab_img, symtab_sz, |
| strtab_img, strtab_sz, |
| opd_img); |
| |
| read_elf_symtab(di, "dynamic symbol table", |
| dynsym_img, dynsym_sz, |
| dynstr_img, dynstr_sz, |
| opd_img); |
| } |
| |
| /* Read .eh_frame (call-frame-info) if any */ |
| if (ehframe_img) { |
| vg_assert(ehframe_sz == di->ehframe_size); |
| ML_(read_callframe_info_dwarf3)( di, ehframe_img ); |
| } |
| |
| /* Read the stabs and/or dwarf2 debug information, if any. It |
| appears reading stabs stuff on amd64-linux doesn't work, so |
| we ignore it. */ |
| # if !defined(VGP_amd64_linux) |
| if (stab_img && stabstr_img) { |
| ML_(read_debuginfo_stabs) ( di, di->text_bias, stab_img, stab_sz, |
| stabstr_img, stabstr_sz ); |
| } |
| # endif |
| /* jrs 2006-01-01: icc-8.1 has been observed to generate |
| binaries without debug_str sections. Don't preclude |
| debuginfo reading for that reason, but, in |
| read_unitinfo_dwarf2, do check that debugstr is non-NULL |
| before using it. */ |
| if (debug_info_img && debug_abbv_img && debug_line_img |
| /* && debug_str_img */) { |
| |
| /* The old reader: line numbers and unwind info only */ |
| ML_(read_debuginfo_dwarf3) ( di, |
| debug_info_img, debug_info_sz, |
| debug_abbv_img, debug_abbv_sz, |
| debug_line_img, debug_line_sz, |
| debug_str_img, debug_str_sz ); |
| |
| /* The new reader: read the DIEs in .debug_info to acquire |
| information on variable types and locations. But only if |
| the tool asks for it, or the user requests it on the |
| command line. */ |
| if (VG_(needs).var_info /* the tool requires it */ |
| || VG_(clo_read_var_info) /* the user asked for it */) { |
| ML_(new_dwarf3_reader)( |
| di, debug_info_img, debug_info_sz, |
| debug_abbv_img, debug_abbv_sz, |
| debug_line_img, debug_line_sz, |
| debug_str_img, debug_str_sz, |
| debug_ranges_img, debug_ranges_sz, |
| debug_loc_img, debug_loc_sz |
| ); |
| } |
| } |
| if (dwarf1d_img && dwarf1l_img) { |
| ML_(read_debuginfo_dwarf1) ( di, dwarf1d_img, dwarf1d_sz, |
| dwarf1l_img, dwarf1l_sz ); |
| } |
| } |
| res = True; |
| |
| out: { |
| SysRes m_res; |
| |
| /* Last, but not least, heave the image(s) back overboard. */ |
| if (dimage) { |
| m_res = VG_(am_munmap_valgrind) ( dimage, n_dimage ); |
| vg_assert(!m_res.isError); |
| } |
| m_res = VG_(am_munmap_valgrind) ( oimage, n_oimage ); |
| vg_assert(!m_res.isError); |
| return res; |
| } |
| } |
| |
| |
| /*--------------------------------------------------------------------*/ |
| /*--- end ---*/ |
| /*--------------------------------------------------------------------*/ |