| |
| /*--------------------------------------------------------------------*/ |
| /*--- 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-2006 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_mallocfree.h" |
| #include "pub_core_options.h" |
| #include "pub_core_oset.h" |
| #include "pub_core_tooliface.h" /* VG_(needs) */ |
| #include "priv_storage.h" |
| #include "priv_readelf.h" /* self */ |
| #include "priv_readdwarf.h" /* 'cos ELF contains DWARF */ |
| #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)( const void* buf ) |
| { |
| ElfXX_Ehdr* ehdr = (ElfXX_Ehdr*)buf; |
| Int ok = 1; |
| |
| 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_addr, |
| 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)(": val %010p, %ssz %4d %s\n", |
| sym_addr, space, sym->st_size, |
| ( 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. |
| |
| 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 _SegInfo* si, /* containing SegInfo */ |
| ElfXX_Sym* sym, /* ELF symbol */ |
| Char* sym_name, /* name */ |
| Addr sym_addr, /* declared address */ |
| UChar* opd_filea, /* oimage of .opd sec (ppc64-linux only) */ |
| OffT opd_offset, /* base address assumed in oimage */ |
| /* OUTPUTS */ |
| Char** sym_name_out, /* name we should record */ |
| Addr* sym_addr_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 plausible, is_in_opd; |
| |
| /* Set defaults */ |
| *sym_name_out = sym_name; |
| *sym_addr_out = sym_addr; |
| *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 |
| || (VG_(needs).data_syms |
| && ELFXX_ST_TYPE(sym->st_info) == STT_OBJECT) |
| ); |
| |
| # 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 |
| && ELFXX_ST_TYPE(sym->st_info) == STT_NOTYPE |
| && sym->st_size > 0 |
| && si->opd_start_vma != 0 |
| && sym_addr >= si->opd_start_vma |
| && sym_addr < si->opd_start_vma + si->opd_size) |
| plausible = True; |
| # endif |
| |
| if (!plausible) |
| return False; |
| |
| /* Ignore if nameless, or zero-sized. */ |
| if (sym->st_name == (ElfXX_Word)NULL |
| || /* 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 (si->got_start_vma != 0 |
| && sym_addr >= si->got_start_vma |
| && sym_addr < si->got_start_vma + si->got_size) { |
| TRACE_SYMTAB(" ignore -- in GOT: %s\n", sym_name); |
| return False; |
| } |
| if (si->plt_start_vma != 0 |
| && sym_addr >= si->plt_start_vma |
| && sym_addr < si->plt_start_vma + si->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 (si->opd_start_vma != 0 |
| && sym_addr >= si->opd_start_vma |
| && sym_addr < si->opd_start_vma + si->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; |
| |
| if (0) VG_(printf)("opdXXX: opd_offset %p, sym_addr %p\n", |
| (void*)(opd_offset), (void*)sym_addr); |
| |
| if (!VG_IS_8_ALIGNED(sym_addr)) { |
| TRACE_SYMTAB(" ignore -- not 8-aligned: %s\n", sym_name); |
| return False; |
| } |
| |
| /* sym_addr 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_addr - (Addr)(si->opd_start_vma); |
| if (offset_in_opd < 0 || offset_in_opd >= si->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_filea is the start address of the .opd in the oimage. |
| Hence: */ |
| |
| fn_descr = (ULong*)(opd_filea + offset_in_opd); |
| |
| if (0) VG_(printf)("opdXXY: offset %d, fn_descr %p\n", |
| offset_in_opd, fn_descr); |
| if (0) VG_(printf)("opdXXZ: *fn_descr %p\n", (void*)(fn_descr[0])); |
| |
| /* opd_offset is the difference between si->start (where the |
| library got mapped) and the address space used for addresses |
| within the library file. */ |
| |
| sym_addr = fn_descr[0] + opd_offset; |
| *sym_addr_out = sym_addr; |
| *sym_tocptr_out = fn_descr[1] + opd_offset; |
| *from_opd_out = True; |
| is_in_opd = True; |
| |
| /* Do a final sanity check: if the symbol falls outside the |
| SegInfo's mapped range, ignore it. Since sym_addr 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 (si->opd_start_vma != 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. */ |
| if (*sym_addr_out + *sym_size_out <= si->start |
| || *sym_addr_out >= si->start+si->size) { |
| TRACE_SYMTAB( "ignore -- %p .. %p outside mapped range %p .. %p\n", |
| *sym_addr_out, *sym_addr_out + *sym_size_out, |
| si->start, si->start+si->size); |
| 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 (si->opd_start_vma != 0) { |
| vg_assert(*sym_addr_out + *sym_size_out <= si->opd_start_vma |
| || *sym_addr_out >= si->opd_start_vma + si->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 _SegInfo* si, UChar* tab_name, |
| ElfXX_Sym* o_symtab, UInt o_symtab_sz, OffT o_symtab_offset, |
| UChar* o_strtab, UInt o_strtab_sz, |
| UChar* opd_filea, OffT opd_offset /* ppc64-linux only */ |
| ) |
| { |
| Int i; |
| Addr sym_addr, sym_addr_really; |
| Char *sym_name, *sym_name_really; |
| Int sym_size; |
| Addr sym_tocptr; |
| Bool from_opd; |
| DiSym risym; |
| ElfXX_Sym *sym; |
| |
| if (o_strtab == NULL || o_symtab == NULL) { |
| Char buf[80]; |
| vg_assert(VG_(strlen)(tab_name) < 40); |
| VG_(sprintf)(buf, " object doesn't have a %s", tab_name); |
| ML_(symerr)(buf); |
| return; |
| } |
| |
| TRACE_SYMTAB("\nReading (ELF, standard) %s (%d entries)\n", tab_name, |
| o_symtab_sz/sizeof(ElfXX_Sym) ); |
| |
| /* Perhaps should start at i = 1; ELF docs suggest that entry |
| 0 always denotes 'unknown symbol'. */ |
| for (i = 1; i < (Int)(o_symtab_sz/sizeof(ElfXX_Sym)); i++) { |
| sym = & o_symtab[i]; |
| sym_name = (Char*)(o_strtab + sym->st_name); |
| sym_addr = o_symtab_offset + sym->st_value; |
| |
| if (VG_(clo_trace_symtab)) |
| show_raw_elf_symbol(i, sym, sym_name, sym_addr, False); |
| |
| if (get_elf_symbol_info(si, sym, sym_name, sym_addr, |
| opd_filea, opd_offset, |
| &sym_name_really, |
| &sym_addr_really, |
| &sym_size, |
| &sym_tocptr, |
| &from_opd)) { |
| |
| risym.addr = sym_addr_really; |
| risym.size = sym_size; |
| risym.name = ML_(addStr) ( si, sym_name_really, -1 ); |
| risym.tocptr = sym_tocptr; |
| vg_assert(risym.name != NULL); |
| vg_assert(risym.tocptr == 0); /* has no role except on ppc64-linux */ |
| ML_(addSym) ( si, &risym ); |
| |
| if (VG_(clo_trace_symtab)) { |
| VG_(printf)(" record [%4d]: " |
| " val %010p, sz %4d %s\n", |
| i, (void*)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; |
| } |
| 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 void* oset_malloc ( SizeT szB ) { |
| return VG_(arena_malloc)(VG_AR_SYMTAB, szB); |
| } |
| static void oset_free ( void* p ) { |
| VG_(arena_free)(VG_AR_SYMTAB, p); |
| } |
| |
| static |
| __attribute__((unused)) /* not referred to on all targets */ |
| void read_elf_symtab__ppc64_linux( |
| struct _SegInfo* si, UChar* tab_name, |
| ElfXX_Sym* o_symtab, UInt o_symtab_sz, OffT o_symtab_offset, |
| UChar* o_strtab, UInt o_strtab_sz, |
| UChar* opd_filea, OffT opd_offset /* ppc64-linux only */ |
| ) |
| { |
| Int i, old_size; |
| Addr sym_addr, sym_addr_really; |
| Char *sym_name, *sym_name_really; |
| Int sym_size; |
| Addr sym_tocptr, old_tocptr; |
| Bool from_opd, modify_size, modify_tocptr; |
| DiSym risym; |
| ElfXX_Sym *sym; |
| OSet *oset; |
| TempSymKey key; |
| TempSym *elem; |
| TempSym *prev; |
| |
| if (o_strtab == NULL || o_symtab == NULL) { |
| Char buf[80]; |
| vg_assert(VG_(strlen)(tab_name) < 40); |
| VG_(sprintf)(buf, " object doesn't have a %s", tab_name); |
| ML_(symerr)(buf); |
| return; |
| } |
| |
| TRACE_SYMTAB("\nReading (ELF, ppc64-linux) %s (%d entries)\n", tab_name, |
| o_symtab_sz/sizeof(ElfXX_Sym) ); |
| |
| oset = VG_(OSet_Create)( offsetof(TempSym,key), |
| (OSetCmp_t)cmp_TempSymKey, |
| oset_malloc, oset_free ); |
| vg_assert(oset); |
| |
| /* Perhaps should start at i = 1; ELF docs suggest that entry |
| 0 always denotes 'unknown symbol'. */ |
| for (i = 1; i < (Int)(o_symtab_sz/sizeof(ElfXX_Sym)); i++) { |
| sym = & o_symtab[i]; |
| sym_name = (Char*)(o_strtab + sym->st_name); |
| sym_addr = o_symtab_offset + sym->st_value; |
| |
| if (VG_(clo_trace_symtab)) |
| show_raw_elf_symbol(i, sym, sym_name, sym_addr, True); |
| |
| if (get_elf_symbol_info(si, sym, sym_name, sym_addr, |
| opd_filea, opd_offset, |
| &sym_name_really, |
| &sym_addr_really, |
| &sym_size, |
| &sym_tocptr, |
| &from_opd)) { |
| |
| /* Check if we've seen this (name,addr) key before. */ |
| key.addr = sym_addr_really; |
| key.name = sym_name_really; |
| prev = VG_(OSet_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 && VG_(clo_trace_symtab)) { |
| VG_(printf)(" modify (old sz %4d) " |
| " val %010p, toc %010p, sz %4d %s\n", |
| old_size, |
| (void*) prev->key.addr, |
| (void*) prev->tocptr, |
| (Int) prev->size, |
| (HChar*)prev->key.name |
| ); |
| } |
| if (modify_tocptr && VG_(clo_trace_symtab)) { |
| VG_(printf)(" modify (upd tocptr) " |
| " val %010p, toc %010p, sz %4d %s\n", |
| (void*) prev->key.addr, |
| (void*) prev->tocptr, |
| (Int) prev->size, |
| (HChar*)prev->key.name |
| ); |
| } |
| |
| } else { |
| |
| /* A new (name,addr) key. Add and continue. */ |
| elem = VG_(OSet_AllocNode)(oset, sizeof(TempSym)); |
| vg_assert(elem); |
| elem->key = key; |
| elem->tocptr = sym_tocptr; |
| elem->size = sym_size; |
| elem->from_opd = from_opd; |
| VG_(OSet_Insert)(oset, elem); |
| if (VG_(clo_trace_symtab)) { |
| VG_(printf)(" to-oset [%4d]: " |
| " val %010p, toc %010p, sz %4d %s\n", |
| i, (void*) elem->key.addr, |
| (void*) 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_(OSet_ResetIter)( oset ); |
| |
| while ( (elem = VG_(OSet_Next)(oset)) ) { |
| risym.addr = elem->key.addr; |
| risym.size = elem->size; |
| risym.name = ML_(addStr) ( si, elem->key.name, -1 ); |
| risym.tocptr = elem->tocptr; |
| vg_assert(risym.name != NULL); |
| |
| ML_(addSym) ( si, &risym ); |
| if (VG_(clo_trace_symtab)) { |
| VG_(printf)(" record [%4d]: " |
| " val %010p, toc %010p, sz %4d %s\n", |
| i, (void*) risym.addr, |
| (void*) risym.tocptr, |
| (Int) risym.size, |
| (HChar*)risym.name |
| ); |
| } |
| i++; |
| } |
| |
| VG_(OSet_Destroy)( oset, NULL ); |
| } |
| |
| |
| /* |
| * 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, UInt* size ) |
| { |
| SysRes fd, sres; |
| struct vki_stat stat_buf; |
| UInt calccrc; |
| |
| fd = VG_(open)(name, VKI_O_RDONLY, 0); |
| if (fd.isError) |
| return 0; |
| |
| if (VG_(fstat)(fd.val, &stat_buf) != 0) { |
| VG_(close)(fd.val); |
| 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.val, 0 ); |
| |
| VG_(close)(fd.val); |
| |
| if (sres.isError) |
| return 0; |
| |
| calccrc = calc_gnu_debuglink_crc32(0, (UChar*)sres.val, *size); |
| if (calccrc != crc) { |
| SysRes res = VG_(am_munmap_valgrind)(sres.val, *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.val; |
| } |
| |
| /* |
| * Try to find a separate debug file for a given object file. |
| */ |
| static |
| Addr find_debug_file( Char* objpath, Char* debugname, UInt crc, UInt* size ) |
| { |
| Char *objdir = VG_(arena_strdup)(VG_AR_SYMTAB, objpath); |
| Char *objdirptr; |
| Char *debugpath; |
| Addr addr = 0; |
| |
| if ((objdirptr = VG_(strrchr)(objdir, '/')) != NULL) |
| *objdirptr = '\0'; |
| |
| debugpath = VG_(arena_malloc)(VG_AR_SYMTAB, VG_(strlen)(objdir) + VG_(strlen)(debugname) + 16); |
| |
| 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); |
| } |
| } |
| |
| VG_(arena_free)(VG_AR_SYMTAB, debugpath); |
| VG_(arena_free)(VG_AR_SYMTAB, objdir); |
| |
| return addr; |
| } |
| |
| |
| /* The central function for reading ELF debug info. For the |
| object/exe specified by the SegInfo, 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 SegInfo. |
| */ |
| Bool ML_(read_elf_debug_info) ( struct _SegInfo* si ) |
| { |
| Bool res; |
| ElfXX_Ehdr* ehdr; /* The ELF header */ |
| ElfXX_Shdr* shdr; /* The section table */ |
| UChar* sh_strtab; /* The section table's string table */ |
| SysRes fd, sres; |
| Int i; |
| Bool ok; |
| Addr oimage; |
| UInt n_oimage; |
| OffT offset_oimage = 0; |
| Addr dimage = 0; |
| UInt n_dimage = 0; |
| OffT offset_dimage = 0; |
| |
| oimage = (Addr)NULL; |
| if (VG_(clo_verbosity) > 1 || VG_(clo_trace_redir)) |
| VG_(message)(Vg_DebugMsg, "Reading syms from %s (%p)", |
| si->filename, si->start ); |
| |
| /* 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)(si->filename, VKI_O_RDONLY, 0); |
| if (fd.isError) { |
| ML_(symerr)("Can't open .so/.exe to read symbols?!"); |
| return False; |
| } |
| |
| n_oimage = VG_(fsize)(fd.val); |
| if (n_oimage < 0) { |
| ML_(symerr)("Can't stat .so/.exe (to determine its size)?!"); |
| VG_(close)(fd.val); |
| return False; |
| } |
| |
| sres = VG_(am_mmap_file_float_valgrind) |
| ( n_oimage, VKI_PROT_READ, fd.val, 0 ); |
| |
| VG_(close)(fd.val); |
| |
| if (sres.isError) { |
| VG_(message)(Vg_UserMsg, "warning: mmap failed on %s", si->filename ); |
| VG_(message)(Vg_UserMsg, " no symbols or debug info loaded" ); |
| return False; |
| } |
| |
| oimage = sres.val; |
| |
| /* 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 = (ElfXX_Ehdr*)oimage; |
| |
| if (ok) |
| ok &= ML_(is_elf_object_file)(ehdr); |
| |
| if (!ok) { |
| ML_(symerr)("Invalid ELF header, or missing stringtab/sectiontab."); |
| goto out; |
| } |
| |
| /* Walk the LOAD headers in the phdr and update the SegInfo to |
| include them all, so that this segment also contains data and |
| bss memory. Also computes correct symbol offset value for this |
| ELF file. */ |
| if (ehdr->e_phoff + ehdr->e_phnum*sizeof(ElfXX_Phdr) > n_oimage) { |
| ML_(symerr)("ELF program header is beyond image end?!"); |
| goto out; |
| } |
| { |
| Bool offset_set = False; |
| ElfXX_Addr prev_addr = 0; |
| Addr baseaddr = 0; |
| |
| vg_assert(si->soname == NULL); |
| |
| for (i = 0; i < ehdr->e_phnum; i++) { |
| ElfXX_Phdr *o_phdr; |
| ElfXX_Addr mapped, mapped_end; |
| |
| o_phdr = &((ElfXX_Phdr *)(oimage + ehdr->e_phoff))[i]; |
| |
| /* 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 (o_phdr->p_type == PT_DYNAMIC && si->soname == NULL) { |
| const ElfXX_Dyn *dyn = (const ElfXX_Dyn *)(oimage + o_phdr->p_offset); |
| Int stroff = -1; |
| Char *strtab = NULL; |
| Int j; |
| |
| for(j = 0; dyn[j].d_tag != DT_NULL; j++) { |
| switch(dyn[j].d_tag) { |
| case DT_SONAME: |
| stroff = dyn[j].d_un.d_val; |
| break; |
| |
| case DT_STRTAB: |
| strtab = (Char *)oimage + dyn[j].d_un.d_ptr - baseaddr; |
| break; |
| } |
| } |
| |
| if (stroff != -1 && strtab != 0) { |
| TRACE_SYMTAB("soname=%s\n", strtab+stroff); |
| si->soname = VG_(arena_strdup)(VG_AR_SYMTAB, strtab+stroff); |
| } |
| } |
| |
| if (o_phdr->p_type != PT_LOAD) |
| continue; |
| |
| if (!offset_set) { |
| offset_set = True; |
| offset_oimage = si->start - o_phdr->p_vaddr; |
| baseaddr = o_phdr->p_vaddr; |
| } |
| |
| // Make sure the Phdrs are in order |
| if (o_phdr->p_vaddr < prev_addr) { |
| ML_(symerr)("ELF Phdrs are out of order!?"); |
| goto out; |
| } |
| prev_addr = o_phdr->p_vaddr; |
| |
| // Get the data and bss start/size if appropriate |
| mapped = o_phdr->p_vaddr + offset_oimage; |
| mapped_end = mapped + o_phdr->p_memsz; |
| if (si->data_start_vma == 0 && |
| (o_phdr->p_flags & (PF_R|PF_W|PF_X)) == (PF_R|PF_W)) { |
| si->data_start_vma = mapped; |
| si->data_size = o_phdr->p_filesz; |
| si->bss_start_vma = mapped + o_phdr->p_filesz; |
| if (o_phdr->p_memsz > o_phdr->p_filesz) |
| si->bss_size = o_phdr->p_memsz - o_phdr->p_filesz; |
| else |
| si->bss_size = 0; |
| } |
| |
| mapped = mapped & ~(VKI_PAGE_SIZE-1); |
| mapped_end = (mapped_end + VKI_PAGE_SIZE - 1) & ~(VKI_PAGE_SIZE-1); |
| |
| if (VG_(needs).data_syms && |
| (mapped >= si->start && mapped <= (si->start+si->size)) && |
| (mapped_end > (si->start+si->size))) { |
| UInt newsz = mapped_end - si->start; |
| if (newsz > si->size) { |
| if (0) |
| VG_(printf)("extending mapping %p..%p %d -> ..%p %d\n", |
| si->start, si->start+si->size, si->size, |
| si->start+newsz, newsz); |
| |
| si->size = newsz; |
| } |
| } |
| } |
| } |
| |
| si->offset = offset_oimage; |
| |
| /* If, after looking at all the program headers, we still didn't |
| find a soname, add a fake one. */ |
| if (si->soname == NULL) { |
| TRACE_SYMTAB("soname(fake)=\"NONE\"\n"); |
| si->soname = "NONE"; |
| } |
| |
| TRACE_SYMTAB("shoff = %d, shnum = %d, size = %d, n_vg_oimage = %d\n", |
| ehdr->e_shoff, ehdr->e_shnum, sizeof(ElfXX_Shdr), n_oimage ); |
| |
| if (ehdr->e_shoff + ehdr->e_shnum*sizeof(ElfXX_Shdr) > n_oimage) { |
| ML_(symerr)("ELF section header is beyond image end?!"); |
| goto out; |
| } |
| |
| shdr = (ElfXX_Shdr*)(oimage + ehdr->e_shoff); |
| sh_strtab = (UChar*)(oimage + shdr[ehdr->e_shstrndx].sh_offset); |
| |
| /* Find interesting sections, read the symbol table(s), read any debug |
| information */ |
| { |
| /* Pointers to start of sections (in the oimage, not in the |
| running image) */ |
| UChar* o_strtab = NULL; /* .strtab */ |
| ElfXX_Sym* o_symtab = NULL; /* .symtab */ |
| UChar* o_dynstr = NULL; /* .dynstr */ |
| ElfXX_Sym* o_dynsym = NULL; /* .dynsym */ |
| Char* debuglink = NULL; /* .gnu_debuglink */ |
| UChar* stab = NULL; /* .stab (stabs) */ |
| UChar* stabstr = NULL; /* .stabstr (stabs) */ |
| UChar* debug_line = NULL; /* .debug_line (dwarf2) */ |
| UChar* debug_info = NULL; /* .debug_info (dwarf2) */ |
| UChar* debug_abbv = NULL; /* .debug_abbrev (dwarf2) */ |
| UChar* debug_str = NULL; /* .debug_str (dwarf2) */ |
| UChar* dwarf1d = NULL; /* .debug (dwarf1) */ |
| UChar* dwarf1l = NULL; /* .line (dwarf1) */ |
| UChar* ehframe = NULL; /* .eh_frame (dwarf2) */ |
| UChar* opd_filea = NULL; /* .opd (dwarf2, ppc64-linux) */ |
| UChar* dummy_filea = NULL; |
| |
| OffT o_symtab_offset = offset_oimage; |
| OffT o_dynsym_offset = offset_oimage; |
| OffT debug_offset = offset_oimage; |
| OffT opd_offset = offset_oimage; |
| |
| /* Section sizes, in bytes */ |
| UInt o_strtab_sz = 0; |
| UInt o_symtab_sz = 0; |
| UInt o_dynstr_sz = 0; |
| UInt o_dynsym_sz = 0; |
| UInt debuglink_sz = 0; |
| UInt stab_sz = 0; |
| UInt stabstr_sz = 0; |
| UInt debug_line_sz = 0; |
| UInt debug_info_sz = 0; |
| UInt debug_abbv_sz = 0; |
| UInt debug_str_sz = 0; |
| UInt dwarf1d_sz = 0; |
| UInt dwarf1l_sz = 0; |
| UInt ehframe_sz = 0; |
| |
| /* Section virtual addresses */ |
| Addr dummy_vma = 0; |
| Addr ehframe_vma = 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 that it will |
| appear in the running image is assigned to SEC_VMA (note, |
| this will be meaningless for sections which are not marked |
| loadable. 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) |
| The address of the section in the transiently loaded oimage |
| is assigned to SEC_FILEA. Because the entire object file is |
| transiently mapped aboard for inspection, it's always safe to |
| inspect that area. */ |
| |
| for (i = 0; i < ehdr->e_shnum; i++) { |
| |
| # define FIND(sec_name, sec_size, sec_filea, sec_vma) \ |
| if (0 == VG_(strcmp)(sec_name, sh_strtab + shdr[i].sh_name)) { \ |
| Bool nobits; \ |
| sec_vma = (Addr)(offset_oimage + shdr[i].sh_addr); \ |
| sec_filea = (void*)(oimage + shdr[i].sh_offset); \ |
| sec_size = shdr[i].sh_size; \ |
| nobits = shdr[i].sh_type == SHT_NOBITS; \ |
| TRACE_SYMTAB( "%18s: filea %p .. %p, vma %p .. %p\n", \ |
| sec_name, (UChar*)sec_filea, \ |
| ((UChar*)sec_filea) + sec_size - 1, \ |
| sec_vma, sec_vma + sec_size - 1); \ |
| /* SHT_NOBITS sections have zero size in the file. */ \ |
| if ( shdr[i].sh_offset + (nobits ? 0 : sec_size) > n_oimage ) { \ |
| ML_(symerr)(" section beyond image end?!"); \ |
| goto out; \ |
| } \ |
| } |
| |
| /* Nb: must find where .got and .plt sections will be in the |
| * executable image, not in the object image transiently loaded. */ |
| /* NAME SIZE ADDR_IN_OIMAGE ADDR_WHEN_MAPPED */ |
| FIND(".dynsym", o_dynsym_sz, o_dynsym, dummy_vma) |
| FIND(".dynstr", o_dynstr_sz, o_dynstr, dummy_vma) |
| FIND(".symtab", o_symtab_sz, o_symtab, dummy_vma) |
| FIND(".strtab", o_strtab_sz, o_strtab, dummy_vma) |
| |
| FIND(".gnu_debuglink", debuglink_sz, debuglink, dummy_vma) |
| |
| FIND(".stab", stab_sz, stab, dummy_vma) |
| FIND(".stabstr", stabstr_sz, stabstr, dummy_vma) |
| |
| FIND(".debug_line", debug_line_sz, debug_line, dummy_vma) |
| FIND(".debug_info", debug_info_sz, debug_info, dummy_vma) |
| FIND(".debug_abbrev", debug_abbv_sz, debug_abbv, dummy_vma) |
| FIND(".debug_str", debug_str_sz, debug_str, dummy_vma) |
| |
| FIND(".debug", dwarf1d_sz, dwarf1d, dummy_vma) |
| FIND(".line", dwarf1l_sz, dwarf1l, dummy_vma) |
| FIND(".eh_frame", ehframe_sz, ehframe, ehframe_vma) |
| |
| FIND(".got", si->got_size, dummy_filea, si->got_start_vma) |
| FIND(".plt", si->plt_size, dummy_filea, si->plt_start_vma) |
| FIND(".opd", si->opd_size, opd_filea, si->opd_start_vma) |
| |
| # undef FIND |
| } |
| |
| /* Did we find a debuglink section? */ |
| if (debuglink != NULL) { |
| UInt crc_offset = VG_ROUNDUP(VG_(strlen)(debuglink)+1, 4); |
| UInt crc; |
| |
| vg_assert(crc_offset + sizeof(UInt) <= debuglink_sz); |
| |
| /* Extract the CRC from the debuglink section */ |
| crc = *(UInt *)(debuglink + crc_offset); |
| |
| /* See if we can find a matching debug file */ |
| if ((dimage = find_debug_file(si->filename, debuglink, crc, &n_dimage)) != 0) { |
| ehdr = (ElfXX_Ehdr*)dimage; |
| |
| if (n_dimage >= sizeof(ElfXX_Ehdr) |
| && ML_(is_elf_object_file(ehdr)) |
| && ehdr->e_phoff + ehdr->e_phnum*sizeof(ElfXX_Phdr) <= n_dimage |
| && ehdr->e_shoff + ehdr->e_shnum*sizeof(ElfXX_Shdr) <= n_dimage) |
| { |
| Bool need_symtab = (NULL == o_symtab); |
| |
| for (i = 0; i < ehdr->e_phnum; i++) { |
| ElfXX_Phdr *o_phdr = &((ElfXX_Phdr *)(dimage + ehdr->e_phoff))[i]; |
| if (o_phdr->p_type == PT_LOAD) { |
| offset_dimage = si->start - o_phdr->p_vaddr; |
| break; |
| } |
| } |
| |
| debug_offset = offset_dimage; |
| if (need_symtab) |
| o_symtab_offset = offset_dimage; |
| |
| shdr = (ElfXX_Shdr*)(dimage + ehdr->e_shoff); |
| sh_strtab = (UChar*)(dimage + shdr[ehdr->e_shstrndx].sh_offset); |
| |
| /* Same deal as previous FIND, except simpler - doesn't |
| look for vma, only oimage address. */ |
| |
| /* Find all interesting sections */ |
| for (i = 0; i < ehdr->e_shnum; i++) { |
| |
| # define FIND(condition, sec_name, sec_size, sec_filea) \ |
| if (condition \ |
| && 0 == VG_(strcmp)(sec_name, sh_strtab + shdr[i].sh_name)) { \ |
| Bool nobits; \ |
| if (0 != sec_filea) \ |
| VG_(core_panic)("repeated section!\n"); \ |
| sec_filea = (void*)(dimage + shdr[i].sh_offset); \ |
| sec_size = shdr[i].sh_size; \ |
| nobits = shdr[i].sh_type == SHT_NOBITS; \ |
| TRACE_SYMTAB( "%18s: filea %p .. %p\n", \ |
| sec_name, (UChar*)sec_filea, \ |
| ((UChar*)sec_filea) + sec_size - 1); \ |
| /* SHT_NOBITS sections have zero size in the file. */ \ |
| if ( shdr[i].sh_offset + (nobits ? 0 : sec_size) > n_dimage ) { \ |
| ML_(symerr)(" section beyond image end?!"); \ |
| goto out; \ |
| } \ |
| } |
| |
| FIND(need_symtab, ".symtab", o_symtab_sz, o_symtab) |
| FIND(need_symtab, ".strtab", o_strtab_sz, o_strtab) |
| FIND(1, ".stab", stab_sz, stab) |
| FIND(1, ".stabstr", stabstr_sz, stabstr) |
| FIND(1, ".debug_line", debug_line_sz, debug_line) |
| FIND(1, ".debug_info", debug_info_sz, debug_info) |
| FIND(1, ".debug_abbrev", debug_abbv_sz, debug_abbv) |
| FIND(1, ".debug_str", debug_str_sz, debug_str) |
| FIND(1, ".debug", dwarf1d_sz, dwarf1d) |
| FIND(1, ".line", dwarf1l_sz, dwarf1l) |
| |
| # undef FIND |
| } |
| } |
| } |
| } |
| |
| /* Check some sizes */ |
| vg_assert((o_dynsym_sz % sizeof(ElfXX_Sym)) == 0); |
| vg_assert((o_symtab_sz % sizeof(ElfXX_Sym)) == 0); |
| |
| /* Read symbols */ |
| { |
| void (*read_elf_symtab)(struct _SegInfo*,UChar*,ElfXX_Sym*, |
| UInt,OffT,UChar*,UInt,UChar*,OffT); |
| # 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(si, "symbol table", |
| o_symtab, o_symtab_sz, o_symtab_offset, |
| o_strtab, o_strtab_sz, opd_filea, opd_offset); |
| |
| read_elf_symtab(si, "dynamic symbol table", |
| o_dynsym, o_dynsym_sz, o_dynsym_offset, |
| o_dynstr, o_dynstr_sz, opd_filea, opd_offset); |
| } |
| |
| /* Read .eh_frame (call-frame-info) if any */ |
| if (ehframe) { |
| ML_(read_callframe_info_dwarf2) ( si, ehframe, ehframe_sz, ehframe_vma ); |
| } |
| |
| /* 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 && stabstr) { |
| ML_(read_debuginfo_stabs) ( si, debug_offset, stab, stab_sz, |
| stabstr, 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 && debug_abbv && debug_line /* && debug_str */) { |
| ML_(read_debuginfo_dwarf2) ( si, debug_offset, |
| debug_info, debug_info_sz, |
| debug_abbv, |
| debug_line, debug_line_sz, |
| debug_str ); |
| } |
| if (dwarf1d && dwarf1l) { |
| ML_(read_debuginfo_dwarf1) ( si, dwarf1d, dwarf1d_sz, |
| dwarf1l, 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 ---*/ |
| /*--------------------------------------------------------------------*/ |