mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-05 03:07:56 +00:00
Changes to support XCOFF: - allow modules to have 'member names' as well as file names. A member name is a "foo.o" name inside a "bar.a"; necessary as AIX keeps all its dynamic libraries in .a files. - rename the type RiLoc to DiLoc (this holds a line number indication). No idea why it was called RiLoc in the first place. - trace changes in type SysRes - implement VG_(di_aix5_notify_segchange) git-svn-id: svn://svn.valgrind.org/valgrind/trunk@6266
1254 lines
46 KiB
C
1254 lines
46 KiB
C
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- 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.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( 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.res);
|
|
if (n_oimage < 0) {
|
|
ML_(symerr)("Can't stat .so/.exe (to determine its size)?!");
|
|
VG_(close)(fd.res);
|
|
return False;
|
|
}
|
|
|
|
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", si->filename );
|
|
VG_(message)(Vg_UserMsg, " no symbols or debug info loaded" );
|
|
return False;
|
|
}
|
|
|
|
oimage = sres.res;
|
|
|
|
/* 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 ---*/
|
|
/*--------------------------------------------------------------------*/
|