blob: ca830040f7e52e5c10d73f2e47ba09c6daea1d37 [file] [log] [blame]
/*--------------------------------------------------------------------*/
/*--- Top level management of symbols and debugging information. ---*/
/*--- debuginfo.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Valgrind, a dynamic binary instrumentation
framework.
Copyright (C) 2000-2007 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_threadstate.h"
#include "pub_core_debuginfo.h" /* self */
#include "pub_core_demangle.h"
#include "pub_core_libcbase.h"
#include "pub_core_libcassert.h"
#include "pub_core_libcprint.h"
#include "pub_core_mallocfree.h"
#include "pub_core_options.h"
#include "pub_core_redir.h" // VG_(redir_notify_{new,delete}_SegInfo)
#include "pub_core_aspacemgr.h"
#include "pub_core_machine.h" // VG_PLAT_USES_PPCTOC
#include "pub_core_xarray.h"
#include "priv_storage.h"
#include "priv_readdwarf.h"
#include "priv_readstabs.h"
#if defined(VGO_linux)
# include "priv_readelf.h"
#elif defined(VGO_aix5)
# include "pub_core_debuglog.h"
# include "pub_core_libcproc.h"
# include "pub_core_libcfile.h"
# include "priv_readxcoff.h"
#endif
/*------------------------------------------------------------*/
/*--- The _svma / _avma / _image / _bias naming scheme ---*/
/*------------------------------------------------------------*/
/* JRS 11 Jan 07: I find the different kinds of addresses involved in
debuginfo reading confusing. Recently I arrived at some
terminology which makes it clearer (to me, at least). There are 3
kinds of address used in the debuginfo reading process:
stated VMAs - the address where (eg) a .so says a symbol is, that
is, what it tells you if you consider the .so in
isolation
actual VMAs - the address where (eg) said symbol really wound up
after the .so was mapped into memory
image addresses - pointers into the copy of the .so (etc)
transiently mmaped aboard whilst we read its info
Additionally I use the term 'bias' to denote the difference
between stated and actual VMAs for a given entity.
This terminology is not used consistently, but a start has been
made. readelf.c and the call-frame info reader in readdwarf.c now
use it. Specifically, various variables and structure fields have
been annotated with _avma / _svma / _image / _bias. In places _img
is used instead of _image for the sake of brevity.
*/
/*------------------------------------------------------------*/
/*--- Root structure ---*/
/*------------------------------------------------------------*/
/* The root structure for the entire symbol table system. It is a
linked list of SegInfos. Note that this entire mechanism assumes
that what we read from /proc/self/maps doesn't contain overlapping
address ranges, and as a result the SegInfos in this list describe
disjoint address ranges.
*/
static SegInfo* segInfo_list = NULL;
/*------------------------------------------------------------*/
/*--- Notification (acquire/discard) helpers ---*/
/*------------------------------------------------------------*/
/* Allocate and zero out a new SegInfo record. */
static
SegInfo* alloc_SegInfo(Addr start, SizeT size, OffT foffset,
const UChar* filename,
const UChar* memname)
{
Bool traceme;
SegInfo* si;
vg_assert(filename);
si = VG_(arena_calloc)(VG_AR_SYMTAB, 1, sizeof(SegInfo));
si->text_start_avma = start;
si->text_size = size;
si->foffset = foffset;
si->filename = VG_(arena_strdup)(VG_AR_SYMTAB, filename);
si->memname = memname
? VG_(arena_strdup)(VG_AR_SYMTAB, memname)
: NULL;
/* Everything else -- pointers, sizes, arrays -- is zeroed by calloc.
Now set up the debugging-output flags. */
traceme
= VG_(string_match)( VG_(clo_trace_symtab_patt), filename )
|| (memname && VG_(string_match)( VG_(clo_trace_symtab_patt),
memname ));
if (traceme) {
si->trace_symtab = VG_(clo_trace_symtab);
si->trace_cfi = VG_(clo_trace_cfi);
si->ddump_syms = VG_(clo_debug_dump_syms);
si->ddump_line = VG_(clo_debug_dump_line);
si->ddump_frames = VG_(clo_debug_dump_frames);
}
return si;
}
/* Free a SegInfo, and also all the stuff hanging off it. */
static void free_SegInfo ( SegInfo* si )
{
struct strchunk *chunk, *next;
vg_assert(si != NULL);
if (si->filename) VG_(arena_free)(VG_AR_SYMTAB, si->filename);
if (si->symtab) VG_(arena_free)(VG_AR_SYMTAB, si->symtab);
if (si->loctab) VG_(arena_free)(VG_AR_SYMTAB, si->loctab);
if (si->cfsi) VG_(arena_free)(VG_AR_SYMTAB, si->cfsi);
if (si->cfsi_exprs) VG_(deleteXA)(si->cfsi_exprs);
for (chunk = si->strchunks; chunk != NULL; chunk = next) {
next = chunk->next;
VG_(arena_free)(VG_AR_SYMTAB, chunk);
}
VG_(arena_free)(VG_AR_SYMTAB, si);
}
/* 'si' is a member of segInfo_list. Find it, remove it from the
list, notify m_redir that this has happened, and free all storage
reachable from it.
*/
static void discard_SegInfo ( SegInfo* si )
{
# if defined(VGP_ppc32_aix5)
HChar* reason = "__unload";
# elif defined(VGP_ppc64_aix5)
HChar* reason = "kunload64";
# else
HChar* reason = "munmap";
# endif
SegInfo** prev_next_ptr = &segInfo_list;
SegInfo* curr = segInfo_list;
while (curr) {
if (curr == si) {
// Found it; remove from list and free it.
if (VG_(clo_verbosity) > 1 || VG_(clo_trace_redir))
VG_(message)(Vg_DebugMsg,
"Discarding syms at %p-%p in %s due to %s()",
si->text_start_avma,
si->text_start_avma + si->text_size,
curr->filename ? curr->filename : (UChar*)"???",
reason);
vg_assert(*prev_next_ptr == curr);
*prev_next_ptr = curr->next;
VG_(redir_notify_delete_SegInfo)( curr );
free_SegInfo(curr);
return;
}
prev_next_ptr = &curr->next;
curr = curr->next;
}
// Not found.
}
/* Repeatedly scan segInfo_list, looking for segInfos intersecting
[start,start+length), and call discard_SegInfo to get rid of them.
This modifies the list, hence the multiple iterations.
JRS 20060401: I don't understand that last sentence. */
static void discard_syms_in_range ( Addr start, SizeT length )
{
Bool found;
SegInfo* curr;
while (True) {
found = False;
curr = segInfo_list;
while (True) {
if (curr == NULL)
break;
if (start+length - 1 < curr->text_start_avma
|| curr->text_start_avma + curr->text_size - 1 < start) {
/* no overlap */
} else {
found = True;
break;
}
curr = curr->next;
}
if (!found) break;
discard_SegInfo( curr );
}
}
/* Create a new SegInfo with the specific address/length/vma offset,
then snarf whatever info we can from the given filename into it. */
static
SegInfo* acquire_syms_for_range(
/* ALL */ Addr seg_addr,
/* ALL */ SizeT seg_len,
/* ELF only */ OffT seg_offset,
/* ALL */ const UChar* seg_filename,
/* XCOFF only */ const UChar* seg_memname,
/* XCOFF only */ Addr data_addr,
/* XCOFF only */ SizeT data_len,
/* XCOFF only */ Bool is_mainexe
)
{
Bool ok;
SegInfo* si = alloc_SegInfo(seg_addr, seg_len, seg_offset,
seg_filename, seg_memname);
# if defined(VGO_linux)
ok = ML_(read_elf_debug_info) ( si );
# elif defined(VGO_aix5)
ok = ML_(read_xcoff_debug_info) ( si, data_addr, data_len, is_mainexe );
# else
# error Unknown OS
# endif
if (!ok) {
// Something went wrong (eg. bad ELF file).
free_SegInfo( si );
si = NULL;
} else {
// Prepend si to segInfo_list
si->next = segInfo_list;
segInfo_list = si;
ML_(canonicaliseTables) ( si );
/* notify m_redir about it */
VG_(redir_notify_new_SegInfo)( si );
}
return si;
}
/*--------------------------------------------------------------*/
/*--- ---*/
/*--- TOP LEVEL: NOTIFICATION (ACQUIRE/DISCARD INFO) (LINUX) ---*/
/*--- ---*/
/*--------------------------------------------------------------*/
#if defined(VGO_linux)
/* The debug info system is driven by notifications that a text
segment has been mapped in, or unmapped. When that happens it
tries to acquire/discard whatever info is available for the
corresponding object. This section contains the notification
handlers. */
/* Notify the debuginfo system about a new mapping. This is the way
new debug information gets loaded. If allow_SkFileV is True, it
will try load debug info if the mapping at 'a' belongs to Valgrind;
whereas normally (False) it will not do that. This allows us to
carefully control when the thing will read symbols from the
Valgrind executable itself. */
void VG_(di_notify_mmap)( Addr a, Bool allow_SkFileV )
{
NSegment const * seg;
HChar* filename;
Bool ok;
/* If this mapping is at the beginning of a file, isn't part of
Valgrind, is at least readable and seems to contain an object
file, then try reading symbols from it.
Getting this heuristic right is critical. On x86-linux, objects
are typically mapped twice:
1b8fb000-1b8ff000 r-xp 00000000 08:02 4471477 vgpreload_memcheck.so
1b8ff000-1b900000 rw-p 00004000 08:02 4471477 vgpreload_memcheck.so
whereas ppc32-linux mysteriously does this:
118a6000-118ad000 r-xp 00000000 08:05 14209428 vgpreload_memcheck.so
118ad000-118b6000 ---p 00007000 08:05 14209428 vgpreload_memcheck.so
118b6000-118bd000 rwxp 00000000 08:05 14209428 vgpreload_memcheck.so
The third mapping should not be considered to have executable
code in. Therefore a test which works for both is: r and x and
NOT w. Reading symbols from the rwx segment -- which overlaps
the r-x segment in the file -- causes the redirection mechanism
to redirect to addresses in that third segment, which is wrong
and causes crashes.
------
JRS 28 Dec 05: unfortunately icc 8.1 on x86 has been seen to
produce executables with a single rwx segment rather than a
(r-x,rw-) pair. That means the rules have to be modified thusly:
x86-linux: consider if r and x
all others: consider if r and x and NOT w
*/
# if defined(VGP_x86_linux)
Bool require_no_W = False;
# else
Bool require_no_W = True;
# endif
seg = VG_(am_find_nsegment)(a);
vg_assert(seg);
filename = VG_(am_get_filename)( (NSegment*)seg );
if (!filename)
return;
filename = VG_(arena_strdup)( VG_AR_SYMTAB, filename );
ok = (seg->kind == SkFileC || (seg->kind == SkFileV && allow_SkFileV))
&& seg->offset == 0
&& seg->fnIdx != -1
&& seg->hasR
&& seg->hasX
&& (require_no_W ? (!seg->hasW) : True)
&& ML_(is_elf_object_file)( (const void*)seg->start );
if (!ok) {
VG_(arena_free)(VG_AR_SYMTAB, filename);
return;
}
/* Dump any info previously associated with the range. */
discard_syms_in_range( seg->start, seg->end + 1 - seg->start );
/* .. and acquire new info. */
acquire_syms_for_range( seg->start, seg->end + 1 - seg->start,
seg->offset, filename,
/* XCOFF only */ NULL, 0, 0, False );
/* acquire_syms_for_range makes its own copy of filename, so is
safe to free it. */
VG_(arena_free)(VG_AR_SYMTAB, filename);
}
/* Unmap is simpler - throw away any SegInfos intersecting
[a, a+len). */
void VG_(di_notify_munmap)( Addr a, SizeT len )
{
discard_syms_in_range(a, len);
}
/* Uh, this doesn't do anything at all. IIRC glibc (or ld.so, I don't
remember) does a bunch of mprotects on itself, and if we follow
through here, it causes the debug info for that object to get
discarded. */
void VG_(di_notify_mprotect)( Addr a, SizeT len, UInt prot )
{
Bool exe_ok = toBool(prot & VKI_PROT_EXEC);
# if defined(VGP_x86_linux)
exe_ok = exe_ok || toBool(prot & VKI_PROT_READ);
# endif
if (0 && !exe_ok)
discard_syms_in_range(a, len);
}
#endif /* defined(VGO_linux) */
/*-------------------------------------------------------------*/
/*--- ---*/
/*--- TOP LEVEL: NOTIFICATION (ACQUIRE/DISCARD INFO) (AIX5) ---*/
/*--- ---*/
/*-------------------------------------------------------------*/
#if defined(VGO_aix5)
/* The supplied parameters describe a code segment and its associated
data segment, that have recently been mapped in -- so we need to
read debug info for it -- or conversely, have recently been dumped,
in which case the relevant debug info has to be unloaded. */
void VG_(di_aix5_notify_segchange)(
Addr code_start,
Word code_len,
Addr data_start,
Word data_len,
UChar* file_name,
UChar* mem_name,
Bool is_mainexe,
Bool acquire )
{
SegInfo* si;
if (acquire) {
acquire_syms_for_range(
/* ALL */ code_start,
/* ALL */ code_len,
/* ELF only */ 0,
/* ALL */ file_name,
/* XCOFF only */ mem_name,
/* XCOFF only */ data_start,
/* XCOFF only */ data_len,
/* XCOFF only */ is_mainexe
);
} else {
/* Dump all the segInfos whose text segments intersect
code_start/code_len. */
while (True) {
for (si = segInfo_list; si; si = si->next) {
if (code_start + code_len <= si->text_start_avma
|| si->text_start_avma + si->text_size <= code_start)
continue; /* no overlap */
else
break;
}
if (si == NULL)
break;
/* Need to delete 'si' */
discard_SegInfo(si);
}
}
}
#endif /* defined(VGO_aix5) */
/*------------------------------------------------------------*/
/*--- ---*/
/*--- TOP LEVEL: QUERYING EXISTING DEBUG INFO ---*/
/*--- ---*/
/*------------------------------------------------------------*/
/*------------------------------------------------------------*/
/*--- Use of symbol table & location info to create ---*/
/*--- plausible-looking stack dumps. ---*/
/*------------------------------------------------------------*/
/* Search all symtabs that we know about to locate ptr. If found, set
*psi to the relevant SegInfo, and *symno to the symtab entry number
within that. If not found, *psi is set to NULL. */
static void search_all_symtabs ( Addr ptr, /*OUT*/SegInfo** psi,
/*OUT*/Int* symno,
Bool match_anywhere_in_fun )
{
Int sno;
SegInfo* si;
for (si = segInfo_list; si != NULL; si = si->next) {
if (si->text_start_avma <= ptr
&& ptr < si->text_start_avma + si->text_size) {
sno = ML_(search_one_symtab) ( si, ptr, match_anywhere_in_fun );
if (sno == -1) goto not_found;
*symno = sno;
*psi = si;
return;
}
}
not_found:
*psi = NULL;
}
/* Search all loctabs that we know about to locate ptr. If found, set
*psi to the relevant SegInfo, and *locno to the loctab entry number
within that. If not found, *psi is set to NULL.
*/
static void search_all_loctabs ( Addr ptr, /*OUT*/SegInfo** psi,
/*OUT*/Int* locno )
{
Int lno;
SegInfo* si;
for (si = segInfo_list; si != NULL; si = si->next) {
if (si->text_start_avma <= ptr
&& ptr < si->text_start_avma + si->text_size) {
lno = ML_(search_one_loctab) ( si, ptr );
if (lno == -1) goto not_found;
*locno = lno;
*psi = si;
return;
}
}
not_found:
*psi = NULL;
}
/* The whole point of this whole big deal: map a code address to a
plausible symbol name. Returns False if no idea; otherwise True.
Caller supplies buf and nbuf. If demangle is False, don't do
demangling, regardless of VG_(clo_demangle) -- probably because the
call has come from VG_(get_fnname_nodemangle)(). */
static
Bool get_fnname ( Bool demangle, Addr a, Char* buf, Int nbuf,
Bool match_anywhere_in_fun, Bool show_offset)
{
SegInfo* si;
Int sno;
Int offset;
search_all_symtabs ( a, &si, &sno, match_anywhere_in_fun );
if (si == NULL)
return False;
if (demangle) {
VG_(demangle) ( True/*do C++ demangle*/,
si->symtab[sno].name, buf, nbuf );
} else {
VG_(strncpy_safely) ( buf, si->symtab[sno].name, nbuf );
}
offset = a - si->symtab[sno].addr;
if (show_offset && offset != 0) {
Char buf2[12];
Char* symend = buf + VG_(strlen)(buf);
Char* end = buf + nbuf;
Int len;
len = VG_(sprintf)(buf2, "%c%d",
offset < 0 ? '-' : '+',
offset < 0 ? -offset : offset);
vg_assert(len < (Int)sizeof(buf2));
if (len < (end - symend)) {
Char *cp = buf2;
VG_(memcpy)(symend, cp, len+1);
}
}
return True;
}
/* ppc64-linux only: find the TOC pointer (R2 value) that should be in
force at the entry point address of the function containing
guest_code_addr. Returns 0 if not known. */
Addr VG_(get_tocptr) ( Addr guest_code_addr )
{
SegInfo* si;
Int sno;
search_all_symtabs ( guest_code_addr,
&si, &sno, True/*match_anywhere_in_fun*/ );
if (si == NULL)
return 0;
else
return si->symtab[sno].tocptr;
}
/* This is available to tools... always demangle C++ names,
match anywhere in function, but don't show offsets. */
Bool VG_(get_fnname) ( Addr a, Char* buf, Int nbuf )
{
return get_fnname ( /*demangle*/True, a, buf, nbuf,
/*match_anywhere_in_fun*/True,
/*show offset?*/False );
}
/* This is available to tools... always demangle C++ names,
match anywhere in function, and show offset if nonzero. */
Bool VG_(get_fnname_w_offset) ( Addr a, Char* buf, Int nbuf )
{
return get_fnname ( /*demangle*/True, a, buf, nbuf,
/*match_anywhere_in_fun*/True,
/*show offset?*/True );
}
/* This is available to tools... always demangle C++ names,
only succeed if 'a' matches first instruction of function,
and don't show offsets. */
Bool VG_(get_fnname_if_entry) ( Addr a, Char* buf, Int nbuf )
{
return get_fnname ( /*demangle*/True, a, buf, nbuf,
/*match_anywhere_in_fun*/False,
/*show offset?*/False );
}
/* This is only available to core... don't demangle C++ names,
match anywhere in function, and don't show offsets. */
Bool VG_(get_fnname_nodemangle) ( Addr a, Char* buf, Int nbuf )
{
return get_fnname ( /*demangle*/False, a, buf, nbuf,
/*match_anywhere_in_fun*/True,
/*show offset?*/False );
}
/* This is only available to core... don't demangle C++ names, but do
do Z-demangling, match anywhere in function, and don't show
offsets. */
Bool VG_(get_fnname_Z_demangle_only) ( Addr a, Char* buf, Int nbuf )
{
# define N_TMPBUF 4096 /* arbitrary, 4096 == ERRTXT_LEN */
Char tmpbuf[N_TMPBUF];
Bool ok;
vg_assert(nbuf > 0);
ok = get_fnname ( /*demangle*/False, a, tmpbuf, N_TMPBUF,
/*match_anywhere_in_fun*/True,
/*show offset?*/False );
tmpbuf[N_TMPBUF-1] = 0; /* paranoia */
if (!ok)
return False;
/* We have something, at least. Try to Z-demangle it. */
VG_(demangle)( False/*don't do C++ demangling*/, tmpbuf, buf, nbuf);
buf[nbuf-1] = 0; /* paranoia */
return True;
# undef N_TMPBUF
}
/* Map a code address to the name of a shared object file or the executable.
Returns False if no idea; otherwise True. Doesn't require debug info.
Caller supplies buf and nbuf. */
Bool VG_(get_objname) ( Addr a, Char* buf, Int nbuf )
{
Int used;
SegInfo* si;
vg_assert(nbuf > 0);
for (si = segInfo_list; si != NULL; si = si->next) {
if (si->text_start_avma <= a
&& a < si->text_start_avma + si->text_size) {
VG_(strncpy_safely)(buf, si->filename, nbuf);
if (si->memname) {
used = VG_(strlen)(buf);
if (used < nbuf)
VG_(strncpy_safely)(&buf[used], "(", nbuf-used);
used = VG_(strlen)(buf);
if (used < nbuf)
VG_(strncpy_safely)(&buf[used], si->memname, nbuf-used);
used = VG_(strlen)(buf);
if (used < nbuf)
VG_(strncpy_safely)(&buf[used], ")", nbuf-used);
}
buf[nbuf-1] = 0;
return True;
}
}
return False;
}
/* Map a code address to its SegInfo. Returns NULL if not found. Doesn't
require debug info. */
SegInfo* VG_(find_seginfo) ( Addr a )
{
SegInfo* si;
for (si = segInfo_list; si != NULL; si = si->next) {
if (si->text_start_avma <= a
&& a < si->text_start_avma + si->text_size) {
return si;
}
}
return NULL;
}
/* Map a code address to a filename. Returns True if successful. */
Bool VG_(get_filename)( Addr a, Char* filename, Int n_filename )
{
SegInfo* si;
Int locno;
search_all_loctabs ( a, &si, &locno );
if (si == NULL)
return False;
VG_(strncpy_safely)(filename, si->loctab[locno].filename, n_filename);
return True;
}
/* Map a code address to a line number. Returns True if successful. */
Bool VG_(get_linenum)( Addr a, UInt* lineno )
{
SegInfo* si;
Int locno;
search_all_loctabs ( a, &si, &locno );
if (si == NULL)
return False;
*lineno = si->loctab[locno].lineno;
return True;
}
/* Map a code address to a filename/line number/dir name info.
See prototype for detailed description of behaviour.
*/
Bool VG_(get_filename_linenum) ( Addr a,
/*OUT*/Char* filename, Int n_filename,
/*OUT*/Char* dirname, Int n_dirname,
/*OUT*/Bool* dirname_available,
/*OUT*/UInt* lineno )
{
SegInfo* si;
Int locno;
vg_assert( (dirname == NULL && dirname_available == NULL)
||
(dirname != NULL && dirname_available != NULL) );
search_all_loctabs ( a, &si, &locno );
if (si == NULL) {
if (dirname_available) {
*dirname_available = False;
*dirname = 0;
}
return False;
}
VG_(strncpy_safely)(filename, si->loctab[locno].filename, n_filename);
*lineno = si->loctab[locno].lineno;
if (dirname) {
/* caller wants directory info too .. */
vg_assert(n_dirname > 0);
if (si->loctab[locno].dirname) {
/* .. and we have some */
*dirname_available = True;
VG_(strncpy_safely)(dirname, si->loctab[locno].dirname,
n_dirname);
} else {
/* .. but we don't have any */
*dirname_available = False;
*dirname = 0;
}
}
return True;
}
/* Map a function name to its entry point and toc pointer. Is done by
sequential search of all symbol tables, so is very slow. To
mitigate the worst performance effects, you may specify a soname
pattern, and only objects matching that pattern are searched.
Therefore specify "*" to search all the objects. On TOC-afflicted
platforms, a symbol is deemed to be found only if it has a nonzero
TOC pointer. */
Bool VG_(lookup_symbol_SLOW)(UChar* sopatt, UChar* name, Addr* pEnt, Addr* pToc)
{
Bool require_pToc = False;
Int i;
SegInfo* si;
Bool debug = False;
# if defined(VG_PLAT_USES_PPCTOC)
require_pToc = True;
# endif
for (si = segInfo_list; si; si = si->next) {
if (debug)
VG_(printf)("lookup_symbol_SLOW: considering %s\n", si->soname);
if (!VG_(string_match)(sopatt, si->soname)) {
if (debug)
VG_(printf)(" ... skip\n");
continue;
}
for (i = 0; i < si->symtab_used; i++) {
if (0==VG_(strcmp)(name, si->symtab[i].name)
&& (require_pToc ? si->symtab[i].tocptr : True)) {
*pEnt = si->symtab[i].addr;
*pToc = si->symtab[i].tocptr;
return True;
}
}
}
return False;
}
/* VG_(describe_IP): print into buf info on code address, function
name and filename. */
/* Copy str into buf starting at n, but not going past buf[n_buf-1]
and always ensuring that buf is zero-terminated. */
static Int putStr ( Int n, Int n_buf, Char* buf, Char* str )
{
vg_assert(n_buf > 0);
vg_assert(n >= 0 && n < n_buf);
for (; n < n_buf-1 && *str != 0; n++,str++)
buf[n] = *str;
vg_assert(n >= 0 && n < n_buf);
buf[n] = '\0';
return n;
}
/* Same as putStr, but escaping chars for XML output, and
also not adding more than count chars to n_buf. */
static Int putStrEsc ( Int n, Int n_buf, Int count, Char* buf, Char* str )
{
Char alt[2];
vg_assert(n_buf > 0);
vg_assert(count >= 0 && count < n_buf);
vg_assert(n >= 0 && n < n_buf);
for (; *str != 0; str++) {
vg_assert(count >= 0);
if (count <= 0)
goto done;
switch (*str) {
case '&':
if (count < 5) goto done;
n = putStr( n, n_buf, buf, "&amp;");
count -= 5;
break;
case '<':
if (count < 4) goto done;
n = putStr( n, n_buf, buf, "&lt;");
count -= 4;
break;
case '>':
if (count < 4) goto done;
n = putStr( n, n_buf, buf, "&gt;");
count -= 4;
break;
default:
if (count < 1) goto done;
alt[0] = *str;
alt[1] = 0;
n = putStr( n, n_buf, buf, alt );
count -= 1;
break;
}
}
done:
vg_assert(count >= 0); /* should not go -ve in loop */
vg_assert(n >= 0 && n < n_buf);
return n;
}
Char* VG_(describe_IP)(Addr eip, Char* buf, Int n_buf)
{
# define APPEND(_str) \
n = putStr(n, n_buf, buf, _str)
# define APPEND_ESC(_count,_str) \
n = putStrEsc(n, n_buf, (_count), buf, (_str))
# define BUF_LEN 4096
UInt lineno;
UChar ibuf[50];
Int n = 0;
static UChar buf_fn[BUF_LEN];
static UChar buf_obj[BUF_LEN];
static UChar buf_srcloc[BUF_LEN];
static UChar buf_dirname[BUF_LEN];
Bool know_dirinfo = False;
Bool know_fnname = VG_(clo_sym_offsets)
? VG_(get_fnname_w_offset) (eip, buf_fn, BUF_LEN)
: VG_(get_fnname) (eip, buf_fn, BUF_LEN);
Bool know_objname = VG_(get_objname)(eip, buf_obj, BUF_LEN);
Bool know_srcloc = VG_(get_filename_linenum)(
eip,
buf_srcloc, BUF_LEN,
buf_dirname, BUF_LEN, &know_dirinfo,
&lineno
);
if (VG_(clo_xml)) {
Bool human_readable = True;
HChar* maybe_newline = human_readable ? "\n " : "";
HChar* maybe_newline2 = human_readable ? "\n " : "";
/* Print in XML format, dumping in as much info as we know.
Ensure all tags are balanced even if the individual strings
are too long. Allocate 1/10 of BUF_LEN to the object name,
6/10s to the function name, 1/10 to the directory name and
1/10 to the file name, leaving 1/10 for all the fixed-length
stuff. */
APPEND("<frame>");
VG_(sprintf)(ibuf,"<ip>0x%llX</ip>", (ULong)eip);
APPEND(maybe_newline);
APPEND(ibuf);
if (know_objname) {
APPEND(maybe_newline);
APPEND("<obj>");
APPEND_ESC(1*BUF_LEN/10, buf_obj);
APPEND("</obj>");
}
if (know_fnname) {
APPEND(maybe_newline);
APPEND("<fn>");
APPEND_ESC(6*BUF_LEN/10, buf_fn);
APPEND("</fn>");
}
if (know_srcloc) {
if (know_dirinfo) {
APPEND(maybe_newline);
APPEND("<dir>");
APPEND_ESC(1*BUF_LEN/10, buf_dirname);
APPEND("</dir>");
}
APPEND(maybe_newline);
APPEND("<file>");
APPEND_ESC(1*BUF_LEN/10, buf_srcloc);
APPEND("</file>");
APPEND(maybe_newline);
APPEND("<line>");
VG_(sprintf)(ibuf,"%d",lineno);
APPEND(ibuf);
APPEND("</line>");
}
APPEND(maybe_newline2);
APPEND("</frame>");
} else {
/* Print for humans to read */
VG_(sprintf)(ibuf,"0x%llX: ", (ULong)eip);
APPEND(ibuf);
if (know_fnname) {
APPEND(buf_fn);
if (!know_srcloc && know_objname) {
APPEND(" (in ");
APPEND(buf_obj);
APPEND(")");
}
} else if (know_objname && !know_srcloc) {
APPEND("(within ");
APPEND(buf_obj);
APPEND(")");
} else {
APPEND("???");
}
if (know_srcloc) {
APPEND(" (");
APPEND(buf_srcloc);
APPEND(":");
VG_(sprintf)(ibuf,"%d",lineno);
APPEND(ibuf);
APPEND(")");
}
}
return buf;
# undef APPEND
# undef APPEND_ESC
# undef BUF_LEN
}
/*------------------------------------------------------------*/
/*--- For unwinding the stack using --- */
/*--- pre-summarised DWARF3 .eh_frame info ---*/
/*------------------------------------------------------------*/
/* Gather up all the constant pieces of info needed to evaluate
a CfiExpr into one convenient struct. */
typedef
struct {
Addr ipHere;
Addr spHere;
Addr fpHere;
Addr min_accessible;
Addr max_accessible;
}
CfiExprEvalContext;
/* Evaluate the CfiExpr rooted at ix in exprs given the context eec.
*ok is set to False on failure, but not to True on success. The
caller must set it to True before calling. */
static
UWord evalCfiExpr ( XArray* exprs, Int ix,
CfiExprEvalContext* eec, Bool* ok )
{
UWord wL, wR;
Addr a;
CfiExpr* e = VG_(indexXA)( exprs, ix );
switch (e->tag) {
case Cex_Binop:
wL = evalCfiExpr( exprs, e->Cex.Binop.ixL, eec, ok );
if (!(*ok)) return 0;
wR = evalCfiExpr( exprs, e->Cex.Binop.ixR, eec, ok );
if (!(*ok)) return 0;
switch (e->Cex.Binop.op) {
case Cop_Add: return wL + wR;
case Cop_Sub: return wL - wR;
case Cop_And: return wL & wR;
case Cop_Mul: return wL * wR;
default: goto unhandled;
}
/*NOTREACHED*/
case Cex_CfiReg:
switch (e->Cex.CfiReg.reg) {
case Creg_IP: return (Addr)eec->ipHere;
case Creg_SP: return (Addr)eec->spHere;
case Creg_FP: return (Addr)eec->fpHere;
default: goto unhandled;
}
/*NOTREACHED*/
case Cex_Const:
return e->Cex.Const.con;
case Cex_Deref:
a = evalCfiExpr( exprs, e->Cex.Deref.ixAddr, eec, ok );
if (!(*ok)) return 0;
if (a < eec->min_accessible
|| (a + sizeof(UWord) - 1) > eec->max_accessible) {
*ok = False;
return 0;
}
/* let's hope it doesn't trap! */
return * ((UWord*)a);
default:
goto unhandled;
}
/*NOTREACHED*/
unhandled:
VG_(printf)("\n\nevalCfiExpr: unhandled\n");
ML_(ppCfiExpr)( exprs, ix );
VG_(printf)("\n");
vg_assert(0);
/*NOTREACHED*/
return 0;
}
/* The main function for DWARF2/3 CFI-based stack unwinding.
Given an IP/SP/FP triple, produce the IP/SP/FP values for the
previous frame, if possible. */
/* Returns True if OK. If not OK, *{ip,sp,fp}P are not changed. */
/* NOTE: this function may rearrange the order of entries in the
SegInfo list. */
Bool VG_(use_CF_info) ( /*MOD*/Addr* ipP,
/*MOD*/Addr* spP,
/*MOD*/Addr* fpP,
Addr min_accessible,
Addr max_accessible )
{
Bool ok;
Int i;
SegInfo* si;
DiCfSI* cfsi = NULL;
Addr cfa, ipHere, spHere, fpHere, ipPrev, spPrev, fpPrev;
CfiExprEvalContext eec;
static UInt n_search = 0;
static UInt n_steps = 0;
n_search++;
if (0) VG_(printf)("search for %p\n", *ipP);
for (si = segInfo_list; si != NULL; si = si->next) {
n_steps++;
/* Use the per-SegInfo summary address ranges to skip
inapplicable SegInfos quickly. */
if (si->cfsi_used == 0)
continue;
if (*ipP < si->cfsi_minaddr || *ipP > si->cfsi_maxaddr)
continue;
i = ML_(search_one_cfitab)( si, *ipP );
if (i != -1) {
vg_assert(i >= 0 && i < si->cfsi_used);
cfsi = &si->cfsi[i];
break;
}
}
if (cfsi == NULL)
return False;
if (0 && ((n_search & 0xFFFFF) == 0))
VG_(printf)("%u %u\n", n_search, n_steps);
/* Start of performance-enhancing hack: once every 16 (chosen
hackily after profiling) successful searches, move the found
SegInfo one step closer to the start of the list. This makes
future searches cheaper. For starting konqueror on amd64, this
in fact reduces the total amount of searching done by the above
find-the-right-SegInfo loop by more than a factor of 20. */
if ((n_search & 0xF) == 0) {
/* Move si one step closer to the start of the list. */
SegInfo* si0 = segInfo_list;
SegInfo* si1 = NULL;
SegInfo* si2 = NULL;
SegInfo* tmp;
while (True) {
if (si0 == NULL) break;
if (si0 == si) break;
si2 = si1;
si1 = si0;
si0 = si0->next;
}
if (si0 == si && si0 != NULL && si1 != NULL && si2 != NULL) {
/* si0 points to si, si1 to its predecessor, and si2 to si1's
predecessor. Swap si0 and si1, that is, move si0 one step
closer to the start of the list. */
tmp = si0->next;
si2->next = si0;
si0->next = si1;
si1->next = tmp;
}
}
/* End of performance-enhancing hack. */
if (0) {
VG_(printf)("found cfisi: ");
ML_(ppDiCfSI)(si->cfsi_exprs, cfsi);
}
ipPrev = spPrev = fpPrev = 0;
ipHere = *ipP;
spHere = *spP;
fpHere = *fpP;
/* First compute the CFA. */
cfa = 0;
switch (cfsi->cfa_how) {
case CFIC_SPREL:
cfa = cfsi->cfa_off + spHere;
break;
case CFIC_FPREL:
cfa = cfsi->cfa_off + fpHere;
break;
case CFIC_EXPR:
if (0) {
VG_(printf)("CFIC_EXPR: ");
ML_(ppCfiExpr)(si->cfsi_exprs, cfsi->cfa_off);
VG_(printf)("\n");
}
eec.ipHere = ipHere;
eec.spHere = spHere;
eec.fpHere = fpHere;
eec.min_accessible = min_accessible;
eec.max_accessible = max_accessible;
ok = True;
cfa = evalCfiExpr(si->cfsi_exprs, cfsi->cfa_off, &eec, &ok );
if (!ok) return False;
break;
default:
vg_assert(0);
}
/* Now we know the CFA, use it to roll back the registers we're
interested in. */
# define COMPUTE(_prev, _here, _how, _off) \
do { \
switch (_how) { \
case CFIR_UNKNOWN: \
return False; \
case CFIR_SAME: \
_prev = _here; break; \
case CFIR_MEMCFAREL: { \
Addr a = cfa + (Word)_off; \
if (a < min_accessible \
|| a+sizeof(Addr) > max_accessible) \
return False; \
_prev = *(Addr*)a; \
break; \
} \
case CFIR_CFAREL: \
_prev = cfa + (Word)_off; \
break; \
case CFIR_EXPR: \
if (0) \
ML_(ppCfiExpr)(si->cfsi_exprs,_off); \
eec.ipHere = ipHere; \
eec.spHere = spHere; \
eec.fpHere = fpHere; \
eec.min_accessible = min_accessible; \
eec.max_accessible = max_accessible; \
ok = True; \
_prev = evalCfiExpr(si->cfsi_exprs, _off, &eec, &ok ); \
if (!ok) return False; \
break; \
default: \
vg_assert(0); \
} \
} while (0)
COMPUTE(ipPrev, ipHere, cfsi->ra_how, cfsi->ra_off);
COMPUTE(spPrev, spHere, cfsi->sp_how, cfsi->sp_off);
COMPUTE(fpPrev, fpHere, cfsi->fp_how, cfsi->fp_off);
# undef COMPUTE
*ipP = ipPrev;
*spP = spPrev;
*fpP = fpPrev;
return True;
}
/*------------------------------------------------------------*/
/*--- SegInfo accessor functions ---*/
/*------------------------------------------------------------*/
const SegInfo* VG_(next_seginfo)(const SegInfo* si)
{
if (si == NULL)
return segInfo_list;
return si->next;
}
Addr VG_(seginfo_start)(const SegInfo* si)
{
return si->text_start_avma;
}
SizeT VG_(seginfo_size)(const SegInfo* si)
{
return si->text_size;
}
const UChar* VG_(seginfo_soname)(const SegInfo* si)
{
return si->soname;
}
const UChar* VG_(seginfo_filename)(const SegInfo* si)
{
return si->filename;
}
ULong VG_(seginfo_sym_offset)(const SegInfo* si)
{
return si->text_bias;
}
VgSectKind VG_(seginfo_sect_kind)(Addr a)
{
SegInfo* si;
VgSectKind ret = Vg_SectUnknown;
for(si = segInfo_list; si != NULL; si = si->next) {
if (a >= si->text_start_avma
&& a < si->text_start_avma + si->text_size) {
if (0)
VG_(printf)(
"addr=%p si=%p %s got=%p %d plt=%p %d data=%p %d bss=%p %d\n",
a, si, si->filename,
si->got_start_avma, si->got_size,
si->plt_start_avma, si->plt_size,
si->data_start_avma, si->data_size,
si->bss_start_avma, si->bss_size);
ret = Vg_SectText;
if (a >= si->data_start_avma && a < si->data_start_avma + si->data_size)
ret = Vg_SectData;
else
if (a >= si->bss_start_avma && a < si->bss_start_avma + si->bss_size)
ret = Vg_SectBSS;
else
if (a >= si->plt_start_avma && a < si->plt_start_avma + si->plt_size)
ret = Vg_SectPLT;
else
if (a >= si->got_start_avma && a < si->got_start_avma + si->got_size)
ret = Vg_SectGOT;
}
}
return ret;
}
Int VG_(seginfo_syms_howmany) ( const SegInfo *si )
{
return si->symtab_used;
}
void VG_(seginfo_syms_getidx) ( const SegInfo *si,
Int idx,
/*OUT*/Addr* addr,
/*OUT*/Addr* tocptr,
/*OUT*/UInt* size,
/*OUT*/HChar** name )
{
vg_assert(idx >= 0 && idx < si->symtab_used);
if (addr) *addr = si->symtab[idx].addr;
if (tocptr) *tocptr = si->symtab[idx].tocptr;
if (size) *size = si->symtab[idx].size;
if (name) *name = (HChar*)si->symtab[idx].name;
}
/*--------------------------------------------------------------------*/
/*--- end ---*/
/*--------------------------------------------------------------------*/