diff options
Diffstat (limited to 'coregrind/m_debuginfo/.svn/text-base/readelf.c.svn-base')
-rw-r--r-- | coregrind/m_debuginfo/.svn/text-base/readelf.c.svn-base | 2054 |
1 files changed, 2054 insertions, 0 deletions
diff --git a/coregrind/m_debuginfo/.svn/text-base/readelf.c.svn-base b/coregrind/m_debuginfo/.svn/text-base/readelf.c.svn-base new file mode 100644 index 0000000..e0eef8c --- /dev/null +++ b/coregrind/m_debuginfo/.svn/text-base/readelf.c.svn-base @@ -0,0 +1,2054 @@ + +/*--------------------------------------------------------------------*/ +/*--- 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-2009 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_debuginfo.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 */ + Bool symtab_in_debug, /* symbol table is in the debug file */ + UChar* opd_img, /* oimage of .opd sec (ppc64-linux only) */ + PtrdiffT 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; +# if defined(VGP_ppc64_linux) + Bool is_in_opd; +# endif + Bool in_text, in_data, in_sdata, in_rodata, in_bss, in_sbss; + Addr text_svma, data_svma, sdata_svma, rodata_svma, bss_svma, sbss_svma; + PtrdiffT text_bias, data_bias, sdata_bias, rodata_bias, bss_bias, sbss_bias; + + /* 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 + ); + + /* Work out the svma and bias for each section as it will appear in + addresses in the symbol table. */ + if (symtab_in_debug) { + text_svma = di->text_debug_svma; + text_bias = di->text_debug_bias; + data_svma = di->data_debug_svma; + data_bias = di->data_debug_bias; + sdata_svma = di->sdata_debug_svma; + sdata_bias = di->sdata_debug_bias; + rodata_svma = di->rodata_debug_svma; + rodata_bias = di->rodata_debug_bias; + bss_svma = di->bss_debug_svma; + bss_bias = di->bss_debug_bias; + sbss_svma = di->sbss_debug_svma; + sbss_bias = di->sbss_debug_bias; + } else { + text_svma = di->text_svma; + text_bias = di->text_bias; + data_svma = di->data_svma; + data_bias = di->data_bias; + sdata_svma = di->sdata_svma; + sdata_bias = di->sdata_bias; + rodata_svma = di->rodata_svma; + rodata_bias = di->rodata_bias; + bss_svma = di->bss_svma; + bss_bias = di->bss_bias; + sbss_svma = di->sbss_svma; + sbss_bias = di->sbss_bias; + } + + /* Now bias sym_avma_out accordingly by figuring 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 >= text_svma + && sym_svma < text_svma + di->text_size) { + *is_text_out = True; + *sym_avma_out += text_bias; + } else + if (di->data_present + && di->data_size > 0 + && sym_svma >= data_svma + && sym_svma < data_svma + di->data_size) { + *is_text_out = False; + *sym_avma_out += data_bias; + } else + if (di->sdata_present + && di->sdata_size > 0 + && sym_svma >= sdata_svma + && sym_svma < sdata_svma + di->sdata_size) { + *is_text_out = False; + *sym_avma_out += sdata_bias; + } else + if (di->rodata_present + && di->rodata_size > 0 + && sym_svma >= rodata_svma + && sym_svma < rodata_svma + di->rodata_size) { + *is_text_out = False; + *sym_avma_out += rodata_bias; + } else + if (di->bss_present + && di->bss_size > 0 + && sym_svma >= bss_svma + && sym_svma < bss_svma + di->bss_size) { + *is_text_out = False; + *sym_avma_out += bss_bias; + } else + if (di->sbss_present + && di->sbss_size > 0 + && sym_svma >= sbss_svma + && sym_svma < sbss_svma + di->sbss_size) { + *is_text_out = False; + *sym_avma_out += sbss_bias; + } else { + /* Assume it's in .text. Is this a good idea? */ + *is_text_out = True; + *sym_avma_out += text_bias; + } + +# 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 + */ +# if defined(VGP_ppc64_linux) + is_in_opd = False; +# endif + + 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_rodata + = di->rodata_present + && di->rodata_size > 0 + && !(*sym_avma_out + *sym_size_out <= di->rodata_avma + || *sym_avma_out >= di->rodata_avma + di->rodata_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); + + in_sbss + = di->sbss_present + && di->sbss_size > 0 + && !(*sym_avma_out + *sym_size_out <= di->sbss_avma + || *sym_avma_out >= di->sbss_avma + di->sbss_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_rodata || in_bss || in_sbss)) { + TRACE_SYMTAB( + "ignore -- %#lx .. %#lx outside .data / .sdata / .rodata / .bss / .sbss 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 anysymbols 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, + Bool symtab_in_debug, + 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, + symtab_in_debug, + 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, + Bool symtab_in_debug, + 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; + 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, + symtab_in_debug, + 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; + + 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; + 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 idx, Word scale ) { + return (void*)( ((UChar*)base) + idx * scale ); +} + + +/* 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; + + /* SVMAs covered by rx and rw segments and corresponding bias. */ + Addr rx_svma_base = 0; + Addr rx_svma_limit = 0; + PtrdiffT rx_bias = 0; + Addr rw_svma_base = 0; + Addr rw_svma_limit = 0; + PtrdiffT rw_bias = 0; + + 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. + ---------------------------------------------------------- */ + + res = False; + + 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; + if (rx_svma_limit == 0 + && phdr->p_offset >= di->rx_map_foff + && phdr->p_offset < di->rx_map_foff + di->rx_map_size + && phdr->p_offset + phdr->p_filesz <= di->rx_map_foff + di->rx_map_size) { + rx_svma_base = phdr->p_vaddr; + rx_svma_limit = phdr->p_vaddr + phdr->p_memsz; + rx_bias = di->rx_map_avma - di->rx_map_foff + phdr->p_offset - phdr->p_vaddr; + } + else if (rw_svma_limit == 0 + && phdr->p_offset >= di->rw_map_foff + && phdr->p_offset < di->rw_map_foff + di->rw_map_size + && phdr->p_offset + phdr->p_filesz <= di->rw_map_foff + di->rw_map_size) { + rw_svma_base = phdr->p_vaddr; + rw_svma_limit = phdr->p_vaddr + phdr->p_memsz; + rw_bias = di->rw_map_avma - di->rw_map_foff + phdr->p_offset - 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"; + } + + vg_assert(rx_svma_limit != 0); + vg_assert(rw_svma_limit != 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("rx: contains svmas %#lx .. %#lx with bias %#lx\n", + rx_svma_base, rx_svma_limit - 1, rx_bias ); + 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 ); + TRACE_SYMTAB("rw: contains svmas %#lx .. %#lx with bias %#lx\n", + rw_svma_base, rw_svma_limit - 1, rw_bias ); + + 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 = svma >= rx_svma_base && svma < rx_svma_limit; + Bool inrw = svma >= rw_svma_base && svma < rw_svma_limit; + + 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 .rodata .bss .sbss .plt .got .opd + and .eh_frame */ + + /* Accept .text where mapped as rx (code), even if zero-sized */ + if (0 == VG_(strcmp)(name, ".text")) { + if (inrx && size >= 0 && !di->text_present) { + di->text_present = True; + di->text_svma = svma; + di->text_avma = svma + rx_bias; + di->text_size = size; + di->text_bias = rx_bias; + di->text_debug_svma = svma; + di->text_debug_bias = rx_bias; + 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) { + di->data_present = True; + di->data_svma = svma; + di->data_avma = svma + rw_bias; + di->data_size = size; + di->data_bias = rw_bias; + di->data_debug_svma = svma; + di->data_debug_bias = rw_bias; + 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) { + di->sdata_present = True; + di->sdata_svma = svma; + di->sdata_avma = svma + rw_bias; + di->sdata_size = size; + di->sdata_bias = rw_bias; + di->sdata_debug_svma = svma; + di->sdata_debug_bias = rw_bias; + 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 .rodata where mapped as rx (data), even if zero-sized */ + if (0 == VG_(strcmp)(name, ".rodata")) { + if (inrx && size >= 0 && !di->rodata_present) { + di->rodata_present = True; + di->rodata_svma = svma; + di->rodata_avma = svma + rx_bias; + di->rodata_size = size; + di->rodata_bias = rx_bias; + di->rodata_debug_svma = svma; + di->rodata_debug_bias = rw_bias; + TRACE_SYMTAB("acquiring .rodata svma = %#lx .. %#lx\n", + di->rodata_svma, + di->rodata_svma + di->rodata_size - 1); + TRACE_SYMTAB("acquiring .rodata avma = %#lx .. %#lx\n", + di->rodata_avma, + di->rodata_avma + di->rodata_size - 1); + TRACE_SYMTAB("acquiring .rodata bias = %#lx\n", di->rodata_bias); + } else { + BAD(".rodata"); + } + } + + /* Accept .bss where mapped as rw (data), even if zero-sized */ + if (0 == VG_(strcmp)(name, ".bss")) { + if (inrw && size >= 0 && !di->bss_present) { + di->bss_present = True; + di->bss_svma = svma; + di->bss_avma = svma + rw_bias; + di->bss_size = size; + di->bss_bias = rw_bias; + di->bss_debug_svma = svma; + di->bss_debug_bias = rw_bias; + 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; + di->bss_debug_svma = 0; + di->bss_debug_bias = 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; + } else { + BAD(".bss"); + } + } + + /* Accept .sbss where mapped as rw (data) */ + if (0 == VG_(strcmp)(name, ".sbss")) { + if (inrw && size > 0 && !di->sbss_present) { + di->sbss_present = True; + di->sbss_svma = svma; + di->sbss_avma = svma + rw_bias; + di->sbss_size = size; + di->sbss_bias = rw_bias; + di->sbss_debug_svma = svma; + di->sbss_debug_bias = rw_bias; + TRACE_SYMTAB("acquiring .sbss svma = %#lx .. %#lx\n", + di->sbss_svma, + di->sbss_svma + di->sbss_size - 1); + TRACE_SYMTAB("acquiring .sbss avma = %#lx .. %#lx\n", + di->sbss_avma, + di->sbss_avma + di->sbss_size - 1); + TRACE_SYMTAB("acquiring .sbss bias = %#lx\n", di->sbss_bias); + } else { + BAD(".sbss"); + } + } + + /* 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 = svma + rw_bias; + 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 = svma + rw_bias; + 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 = svma + rx_bias; + 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 = svma + rw_bias; + 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 = svma + rw_bias; + 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 = svma + rw_bias; + 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 = svma + rx_bias; + 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 = svma + rw_bias; + di->ehframe_size = size; + TRACE_SYMTAB("acquiring .eh_frame avma = %#lx\n", di->ehframe_avma); + } else { + BAD(".eh_frame"); + } + } + +# undef BAD + + } + + 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; + + /* SVMAs covered by rx and rw segments and corresponding bias. */ + Addr rx_dsvma_base = 0; + Addr rx_dsvma_limit = 0; + PtrdiffT rx_dbias = 0; + Addr rw_dsvma_base = 0; + Addr rw_dsvma_limit = 0; + PtrdiffT rw_dbias = 0; + + 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) { + if (rx_dsvma_limit == 0 + && phdr->p_offset >= di->rx_map_foff + && phdr->p_offset < di->rx_map_foff + di->rx_map_size + && phdr->p_offset + phdr->p_filesz <= di->rx_map_foff + di->rx_map_size) { + rx_dsvma_base = phdr->p_vaddr; + rx_dsvma_limit = phdr->p_vaddr + phdr->p_memsz; + rx_dbias = di->rx_map_avma - di->rx_map_foff + phdr->p_offset - phdr->p_vaddr; + } + else if (rw_dsvma_limit == 0 + && phdr->p_offset >= di->rw_map_foff + && phdr->p_offset < di->rw_map_foff + di->rw_map_size + && phdr->p_offset + phdr->p_filesz <= di->rw_map_foff + di->rw_map_size) { + rw_dsvma_base = phdr->p_vaddr; + rw_dsvma_limit = phdr->p_vaddr + phdr->p_memsz; + rw_dbias = di->rw_map_avma - di->rw_map_foff + phdr->p_offset - phdr->p_vaddr; + } + } + } + + /* Find all interesting sections */ + for (i = 0; i < ehdr_dimg->e_shnum; i++) { + + /* Find debug svma and bias information for sections + we found in the main file. */ + +# define FIND(sec, seg) \ + do { ElfXX_Shdr* shdr \ + = INDEX_BIS( shdr_dimg, i, shdr_dent_szB ); \ + if (di->sec##_present \ + && 0 == VG_(strcmp)("." #sec, \ + shdr_strtab_dimg + shdr->sh_name)) { \ + vg_assert(di->sec##_size == shdr->sh_size); \ + vg_assert(di->sec##_avma + shdr->sh_addr + seg##_dbias); \ + di->sec##_debug_svma = shdr->sh_addr; \ + di->sec##_debug_bias = seg##_dbias; \ + TRACE_SYMTAB("acquiring ." #sec " debug svma = %#lx .. %#lx\n", \ + di->sec##_debug_svma, \ + di->sec##_debug_svma + di->sec##_size - 1); \ + TRACE_SYMTAB("acquiring ." #sec " debug bias = %#lx\n", \ + di->sec##_debug_bias); \ + } \ + } while (0); + + /* SECTION SEGMENT */ + FIND(text, rx) + FIND(data, rw) + FIND(sdata, rw) + FIND(rodata, rw) + FIND(bss, rw) + FIND(sbss, rw) + +# undef FIND + + /* Same deal as previous FIND, except only do it for those + sections for which we didn't find anything useful in + the main file. */ + +# 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, + Bool,UChar*); + Bool symtab_in_debug; +# if defined(VGP_ppc64_linux) + read_elf_symtab = read_elf_symtab__ppc64_linux; +# else + read_elf_symtab = read_elf_symtab__normal; +# endif + symtab_in_debug = (Addr)symtab_img >= dimage + && (Addr)symtab_img < dimage + n_dimage; + read_elf_symtab(di, "symbol table", + symtab_img, symtab_sz, + strtab_img, strtab_sz, + symtab_in_debug, opd_img); + + read_elf_symtab(di, "dynamic symbol table", + dynsym_img, dynsym_sz, + dynstr_img, dynstr_sz, + False, 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, 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 ---*/ +/*--------------------------------------------------------------------*/ |