diff options
Diffstat (limited to 'coregrind/m_debuginfo/debuginfo.c')
-rw-r--r-- | coregrind/m_debuginfo/debuginfo.c | 3391 |
1 files changed, 3391 insertions, 0 deletions
diff --git a/coregrind/m_debuginfo/debuginfo.c b/coregrind/m_debuginfo/debuginfo.c new file mode 100644 index 0000000..b4e2e63 --- /dev/null +++ b/coregrind/m_debuginfo/debuginfo.c @@ -0,0 +1,3391 @@ + +/*--------------------------------------------------------------------*/ +/*--- Top level management of symbols and debugging information. ---*/ +/*--- debuginfo.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_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_libcfile.h" +#include "pub_core_seqmatch.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 "pub_core_oset.h" +#include "pub_core_stacktrace.h" // VG_(get_StackTrace) XXX: circular dependency + +#include "priv_misc.h" /* dinfo_zalloc/free */ +#include "priv_d3basics.h" /* ML_(pp_GX) */ +#include "priv_tytypes.h" +#include "priv_storage.h" +#include "priv_readdwarf.h" +#include "priv_readstabs.h" +#if defined(VGO_linux) +# include "priv_readelf.h" +# include "priv_readdwarf3.h" +# include "priv_readpdb.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. +*/ + + +/*------------------------------------------------------------*/ +/*--- fwdses ---*/ +/*------------------------------------------------------------*/ + +static void cfsi_cache__invalidate ( void ); + + +/*------------------------------------------------------------*/ +/*--- Root structure ---*/ +/*------------------------------------------------------------*/ + +/* The root structure for the entire debug info system. It is a + linked list of DebugInfos. */ +static DebugInfo* debugInfo_list = NULL; + + +/* Find 'di' in the debugInfo_list and move it one step closer the the + front of the list, so as to make subsequent searches for it + cheaper. When used in a controlled way, makes a major improvement + in some DebugInfo-search-intensive situations, most notably stack + unwinding on amd64-linux. */ +static void move_DebugInfo_one_step_forward ( DebugInfo* di ) +{ + DebugInfo *di0, *di1, *di2; + if (di == debugInfo_list) + return; /* already at head of list */ + vg_assert(di != NULL); + di0 = debugInfo_list; + di1 = NULL; + di2 = NULL; + while (True) { + if (di0 == NULL || di0 == di) break; + di2 = di1; + di1 = di0; + di0 = di0->next; + } + vg_assert(di0 == di); + if (di0 != NULL && di1 != NULL && di2 != NULL) { + DebugInfo* tmp; + /* di0 points to di, di1 to its predecessor, and di2 to di1's + predecessor. Swap di0 and di1, that is, move di0 one step + closer to the start of the list. */ + vg_assert(di2->next == di1); + vg_assert(di1->next == di0); + tmp = di0->next; + di2->next = di0; + di0->next = di1; + di1->next = tmp; + } + else + if (di0 != NULL && di1 != NULL && di2 == NULL) { + /* it's second in the list. */ + vg_assert(debugInfo_list == di1); + vg_assert(di1->next == di0); + di1->next = di0->next; + di0->next = di1; + debugInfo_list = di0; + } +} + + +/*------------------------------------------------------------*/ +/*--- Notification (acquire/discard) helpers ---*/ +/*------------------------------------------------------------*/ + +/* Gives out unique abstract handles for allocated DebugInfos. See + comment in priv_storage.h, declaration of struct _DebugInfo, for + details. */ +static ULong handle_counter = 1; + +/* Allocate and zero out a new DebugInfo record. */ +static +DebugInfo* alloc_DebugInfo( const UChar* filename, + const UChar* memname ) +{ + Bool traceme; + DebugInfo* di; + + vg_assert(filename); + + di = ML_(dinfo_zalloc)("di.debuginfo.aDI.1", sizeof(DebugInfo)); + di->handle = handle_counter++; + di->filename = ML_(dinfo_strdup)("di.debuginfo.aDI.2", filename); + di->memname = memname ? ML_(dinfo_strdup)("di.debuginfo.aDI.3", 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) { + di->trace_symtab = VG_(clo_trace_symtab); + di->trace_cfi = VG_(clo_trace_cfi); + di->ddump_syms = VG_(clo_debug_dump_syms); + di->ddump_line = VG_(clo_debug_dump_line); + di->ddump_frames = VG_(clo_debug_dump_frames); + } + + return di; +} + + +/* Free a DebugInfo, and also all the stuff hanging off it. */ +static void free_DebugInfo ( DebugInfo* di ) +{ + Word i, j, n; + struct strchunk *chunk, *next; + TyEnt* ent; + GExpr* gexpr; + + vg_assert(di != NULL); + if (di->filename) ML_(dinfo_free)(di->filename); + if (di->symtab) ML_(dinfo_free)(di->symtab); + if (di->loctab) ML_(dinfo_free)(di->loctab); + if (di->cfsi) ML_(dinfo_free)(di->cfsi); + if (di->cfsi_exprs) VG_(deleteXA)(di->cfsi_exprs); + if (di->fpo) ML_(dinfo_free)(di->fpo); + + for (chunk = di->strchunks; chunk != NULL; chunk = next) { + next = chunk->next; + ML_(dinfo_free)(chunk); + } + + /* Delete the two admin arrays. These lists exist primarily so + that we can visit each object exactly once when we need to + delete them. */ + if (di->admin_tyents) { + n = VG_(sizeXA)(di->admin_tyents); + for (i = 0; i < n; i++) { + ent = (TyEnt*)VG_(indexXA)(di->admin_tyents, i); + /* Dump anything hanging off this ent */ + ML_(TyEnt__make_EMPTY)(ent); + } + VG_(deleteXA)(di->admin_tyents); + di->admin_tyents = NULL; + } + + if (di->admin_gexprs) { + n = VG_(sizeXA)(di->admin_gexprs); + for (i = 0; i < n; i++) { + gexpr = *(GExpr**)VG_(indexXA)(di->admin_gexprs, i); + ML_(dinfo_free)(gexpr); + } + VG_(deleteXA)(di->admin_gexprs); + di->admin_gexprs = NULL; + } + + /* Dump the variable info. This is kinda complex: we must take + care not to free items which reside in either the admin lists + (as we have just freed them) or which reside in the DebugInfo's + string table. */ + if (di->varinfo) { + for (i = 0; i < VG_(sizeXA)(di->varinfo); i++) { + OSet* scope = *(OSet**)VG_(indexXA)(di->varinfo, i); + if (!scope) continue; + /* iterate over all entries in 'scope' */ + VG_(OSetGen_ResetIter)(scope); + while (True) { + DiAddrRange* arange = VG_(OSetGen_Next)(scope); + if (!arange) break; + /* for each var in 'arange' */ + vg_assert(arange->vars); + for (j = 0; j < VG_(sizeXA)( arange->vars ); j++) { + DiVariable* var = (DiVariable*)VG_(indexXA)(arange->vars,j); + vg_assert(var); + /* Nothing to free in var: all the pointer fields refer + to stuff either on an admin list, or in + .strchunks */ + } + VG_(deleteXA)(arange->vars); + /* Don't free arange itself, as OSetGen_Destroy does + that */ + } + VG_(OSetGen_Destroy)(scope); + } + VG_(deleteXA)(di->varinfo); + } + + ML_(dinfo_free)(di); +} + + +/* 'si' is a member of debugInfo_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_DebugInfo ( DebugInfo* di ) +{ +# if defined(VGP_ppc32_aix5) + HChar* reason = "__unload"; +# elif defined(VGP_ppc64_aix5) + HChar* reason = "kunload64"; +# else + HChar* reason = "munmap"; +# endif + + DebugInfo** prev_next_ptr = &debugInfo_list; + DebugInfo* curr = debugInfo_list; + + while (curr) { + if (curr == di) { + /* Found it; remove from list and free it. */ + if (curr->have_dinfo + && (VG_(clo_verbosity) > 1 || VG_(clo_trace_redir))) + VG_(message)(Vg_DebugMsg, + "Discarding syms at %#lx-%#lx in %s due to %s()", + di->text_avma, + di->text_avma + di->text_size, + curr->filename ? curr->filename : (UChar*)"???", + reason); + vg_assert(*prev_next_ptr == curr); + *prev_next_ptr = curr->next; + if (curr->have_dinfo) + VG_(redir_notify_delete_DebugInfo)( curr ); + free_DebugInfo(curr); + return; + } + prev_next_ptr = &curr->next; + curr = curr->next; + } + + /* Not found. */ +} + + +/* Repeatedly scan debugInfo_list, looking for DebugInfos with text + AVMAs intersecting [start,start+length), and call discard_DebugInfo + to get rid of them. This modifies the list, hence the multiple + iterations. Returns True iff any such DebugInfos were found. +*/ +static Bool discard_syms_in_range ( Addr start, SizeT length ) +{ + Bool anyFound = False; + Bool found; + DebugInfo* curr; + + while (True) { + found = False; + + curr = debugInfo_list; + while (True) { + if (curr == NULL) + break; + if (curr->text_present + && curr->text_size > 0 + && (start+length - 1 < curr->text_avma + || curr->text_avma + curr->text_size - 1 < start)) { + /* no overlap */ + } else { + found = True; + break; + } + curr = curr->next; + } + + if (!found) break; + anyFound = True; + discard_DebugInfo( curr ); + } + + return anyFound; +} + + +/* Does [s1,+len1) overlap [s2,+len2) ? Note: does not handle + wraparound at the end of the address space -- just asserts in that + case. */ +static Bool ranges_overlap (Addr s1, SizeT len1, Addr s2, SizeT len2 ) +{ + Addr e1, e2; + if (len1 == 0 || len2 == 0) + return False; + e1 = s1 + len1 - 1; + e2 = s2 + len2 - 1; + /* Assert that we don't have wraparound. If we do it would imply + that file sections are getting mapped around the end of the + address space, which sounds unlikely. */ + vg_assert(s1 <= e1); + vg_assert(s2 <= e2); + if (e1 < s2 || e2 < s1) return False; + return True; +} + + +/* Do the basic rx_ and rw_ mappings of the two DebugInfos overlap in + any way? */ +static Bool do_DebugInfos_overlap ( DebugInfo* di1, DebugInfo* di2 ) +{ + vg_assert(di1); + vg_assert(di2); + + if (di1->have_rx_map && di2->have_rx_map + && ranges_overlap(di1->rx_map_avma, di1->rx_map_size, + di2->rx_map_avma, di2->rx_map_size)) + return True; + + if (di1->have_rx_map && di2->have_rw_map + && ranges_overlap(di1->rx_map_avma, di1->rx_map_size, + di2->rw_map_avma, di2->rw_map_size)) + return True; + + if (di1->have_rw_map && di2->have_rx_map + && ranges_overlap(di1->rw_map_avma, di1->rw_map_size, + di2->rx_map_avma, di2->rx_map_size)) + return True; + + if (di1->have_rw_map && di2->have_rw_map + && ranges_overlap(di1->rw_map_avma, di1->rw_map_size, + di2->rw_map_avma, di2->rw_map_size)) + return True; + + return False; +} + + +/* Discard all elements of debugInfo_list whose .mark bit is set. +*/ +static void discard_marked_DebugInfos ( void ) +{ + DebugInfo* curr; + + while (True) { + + curr = debugInfo_list; + while (True) { + if (!curr) + break; + if (curr->mark) + break; + curr = curr->next; + } + + if (!curr) break; + discard_DebugInfo( curr ); + + } +} + + +/* Discard any elements of debugInfo_list which overlap with diRef. + Clearly diRef must have its rx_ and rw_ mapping information set to + something sane. */ +#if defined(VGO_aix5) +__attribute__((unused)) +#endif +static void discard_DebugInfos_which_overlap_with ( DebugInfo* diRef ) +{ + DebugInfo* di; + /* Mark all the DebugInfos in debugInfo_list that need to be + deleted. First, clear all the mark bits; then set them if they + overlap with siRef. Since siRef itself is in this list we at + least expect its own mark bit to be set. */ + for (di = debugInfo_list; di; di = di->next) { + di->mark = do_DebugInfos_overlap( di, diRef ); + if (di == diRef) { + vg_assert(di->mark); + di->mark = False; + } + } + discard_marked_DebugInfos(); +} + + +/* Find the existing DebugInfo for (memname,filename) or if not found, + create one. In the latter case memname and filename are strdup'd + into VG_AR_DINFO, and the new DebugInfo is added to + debugInfo_list. */ +static +DebugInfo* find_or_create_DebugInfo_for ( UChar* filename, UChar* memname ) +{ + DebugInfo* di; + vg_assert(filename); + for (di = debugInfo_list; di; di = di->next) { + vg_assert(di->filename); + if (0==VG_(strcmp)(di->filename, filename) + && ( (memname && di->memname) + ? 0==VG_(strcmp)(memname, di->memname) + : True )) + break; + } + if (!di) { + di = alloc_DebugInfo(filename, memname); + vg_assert(di); + di->next = debugInfo_list; + debugInfo_list = di; + } + return di; +} + + +/* Debuginfo reading for 'di' has just been successfully completed. + Check that the invariants stated in + "Comment_on_IMPORTANT_CFSI_REPRESENTATIONAL_INVARIANTS" in + priv_storage.h are observed. */ +static void check_CFSI_related_invariants ( DebugInfo* di ) +{ + DebugInfo* di2 = NULL; + vg_assert(di); + /* This fn isn't called until after debuginfo for this object has + been successfully read. And that shouldn't happen until we have + both a r-x and rw- mapping for the object. Hence: */ + vg_assert(di->have_rx_map); + vg_assert(di->have_rw_map); + /* degenerate case: r-x section is empty */ + if (di->rx_map_size == 0) { + vg_assert(di->cfsi == NULL); + return; + } + /* normal case: r-x section is nonempty */ + /* invariant (0) */ + vg_assert(di->rx_map_size > 0); + /* invariant (1) */ + for (di2 = debugInfo_list; di2; di2 = di2->next) { + if (di2 == di) + continue; + if (di2->rx_map_size == 0) + continue; + vg_assert(di->rx_map_avma + di->rx_map_size <= di2->rx_map_avma + || di2->rx_map_avma + di2->rx_map_size <= di->rx_map_avma); + } + di2 = NULL; + /* invariant (2) */ + if (di->cfsi) { + vg_assert(di->cfsi_minavma <= di->cfsi_maxavma); /* duh! */ + vg_assert(di->cfsi_minavma >= di->rx_map_avma); + vg_assert(di->cfsi_maxavma < di->rx_map_avma + di->rx_map_size); + } + /* invariants (3) and (4) */ + if (di->cfsi) { + Word i; + vg_assert(di->cfsi_used > 0); + vg_assert(di->cfsi_size > 0); + for (i = 0; i < di->cfsi_used; i++) { + DiCfSI* cfsi = &di->cfsi[i]; + vg_assert(cfsi->len > 0); + vg_assert(cfsi->base >= di->cfsi_minavma); + vg_assert(cfsi->base + cfsi->len - 1 <= di->cfsi_maxavma); + if (i > 0) { + DiCfSI* cfsip = &di->cfsi[i-1]; + vg_assert(cfsip->base + cfsip->len <= cfsi->base); + } + } + } else { + vg_assert(di->cfsi_used == 0); + vg_assert(di->cfsi_size == 0); + } +} + + +/*--------------------------------------------------------------*/ +/*--- ---*/ +/*--- TOP LEVEL: INITIALISE THE DEBUGINFO SYSTEM ---*/ +/*--- ---*/ +/*--------------------------------------------------------------*/ + +void VG_(di_initialise) ( void ) +{ + /* There's actually very little to do here, since everything + centers around the DebugInfos in debugInfo_list, they are + created and destroyed on demand, and each one is treated more or + less independently. */ + vg_assert(debugInfo_list == NULL); + + /* flush the CFI fast query cache. */ + cfsi_cache__invalidate(); +} + + +/*--------------------------------------------------------------*/ +/*--- ---*/ +/*--- 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. + + If a call to VG_(di_notify_mmap) causes debug info to be read, then + the returned ULong is an abstract handle which can later be used to + refer to the debuginfo read as a result of this specific mapping, + in later queries to m_debuginfo. In this case the handle value + will be one or above. If the returned value is zero, no debug info + was read. */ + +ULong VG_(di_notify_mmap)( Addr a, Bool allow_SkFileV ) +{ + NSegment const * seg; + HChar* filename; + Bool ok, is_rx_map, is_rw_map; + DebugInfo* di; + ULong di_handle; + SysRes fd; + Int nread; + HChar buf1k[1024]; + Bool debug = False; + SysRes statres; + struct vg_stat statbuf; + + /* In short, figure out if this mapping is of interest to us, and + if so, try to guess what ld.so is doing and when/if we should + read debug info. */ + seg = VG_(am_find_nsegment)(a); + vg_assert(seg); + + if (debug) + VG_(printf)("di_notify_mmap-1: %#lx-%#lx %c%c%c\n", + seg->start, seg->end, + seg->hasR ? 'r' : '-', + seg->hasW ? 'w' : '-',seg->hasX ? 'x' : '-' ); + + /* guaranteed by aspacemgr-linux.c, sane_NSegment() */ + vg_assert(seg->end > seg->start); + + /* Ignore non-file mappings */ + if ( ! (seg->kind == SkFileC + || (seg->kind == SkFileV && allow_SkFileV)) ) + return 0; + + /* If the file doesn't have a name, we're hosed. Give up. */ + filename = VG_(am_get_filename)( (NSegment*)seg ); + if (!filename) + return 0; + + if (debug) + VG_(printf)("di_notify_mmap-2: %s\n", filename); + + /* Only try to read debug information from regular files. */ + statres = VG_(stat)(filename, &statbuf); + + /* stat dereferences symlinks, so we don't expect it to succeed and + yet produce something that is a symlink. */ + vg_assert(statres.isError || ! VKI_S_ISLNK(statbuf.st_mode)); + + /* Don't let the stat call fail silently. Filter out some known + sources of noise before complaining, though. */ + if (statres.isError) { + DebugInfo fake_di; + Bool quiet = VG_(strstr)(filename, "/var/run/nscd/") != NULL; + if (!quiet && VG_(clo_verbosity) > 1) { + VG_(memset)(&fake_di, 0, sizeof(fake_di)); + fake_di.filename = filename; + ML_(symerr)(&fake_di, True, "failed to stat64/stat this file"); + } + return 0; + } + + /* Finally, the point of all this stattery: if it's not a regular file, + don't try to read debug info from it. */ + if (! VKI_S_ISREG(statbuf.st_mode)) + return 0; + + /* no uses of statbuf below here. */ + + /* Now we have to guess if this is a text-like mapping, a data-like + mapping, neither or both. The rules are: + + text if: x86-linux r and x + other-linux r and x and not w + + data if: x86-linux r and w + other-linux r and w and not x + + Background: 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 + */ + is_rx_map = False; + is_rw_map = False; +# if defined(VGP_x86_linux) + is_rx_map = seg->hasR && seg->hasX; + is_rw_map = seg->hasR && seg->hasW; +# elif defined(VGP_amd64_linux) \ + || defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux) + is_rx_map = seg->hasR && seg->hasX && !seg->hasW; + is_rw_map = seg->hasR && seg->hasW && !seg->hasX; +# else +# error "Unknown platform" +# endif + + if (debug) + VG_(printf)("di_notify_mmap-3: is_rx_map %d, is_rw_map %d\n", + (Int)is_rx_map, (Int)is_rw_map); + + /* If it is neither text-ish nor data-ish, we're not interested. */ + if (!(is_rx_map || is_rw_map)) + return 0; + + /* Peer at the first few bytes of the file, to see if it is an ELF */ + /* object file. Ignore the file if we do not have read permission. */ + VG_(memset)(buf1k, 0, sizeof(buf1k)); + fd = VG_(open)( filename, VKI_O_RDONLY, 0 ); + if (fd.isError) { + if (fd.err != VKI_EACCES) + { + DebugInfo fake_di; + VG_(memset)(&fake_di, 0, sizeof(fake_di)); + fake_di.filename = filename; + ML_(symerr)(&fake_di, True, "can't open file to inspect ELF header"); + } + return 0; + } + nread = VG_(read)( fd.res, buf1k, sizeof(buf1k) ); + VG_(close)( fd.res ); + + if (nread == 0) + return 0; + if (nread < 0) { + DebugInfo fake_di; + VG_(memset)(&fake_di, 0, sizeof(fake_di)); + fake_di.filename = filename; + ML_(symerr)(&fake_di, True, "can't read file to inspect ELF header"); + return 0; + } + vg_assert(nread > 0 && nread <= sizeof(buf1k) ); + + /* We're only interested in mappings of ELF object files. */ + if (!ML_(is_elf_object_file)( buf1k, (SizeT)nread )) + return 0; + + /* See if we have a DebugInfo for this filename. If not, + create one. */ + di = find_or_create_DebugInfo_for( filename, NULL/*membername*/ ); + vg_assert(di); + + if (is_rx_map) { + /* We have a text-like mapping. Note the details. */ + if (!di->have_rx_map) { + di->have_rx_map = True; + di->rx_map_avma = a; + di->rx_map_size = seg->end + 1 - seg->start; + di->rx_map_foff = seg->offset; + } else { + /* FIXME: complain about a second text-like mapping */ + } + } + + if (is_rw_map) { + /* We have a data-like mapping. Note the details. */ + if (!di->have_rw_map) { + di->have_rw_map = True; + di->rw_map_avma = a; + di->rw_map_size = seg->end + 1 - seg->start; + di->rw_map_foff = seg->offset; + } else { + /* FIXME: complain about a second data-like mapping */ + } + } + + /* If we don't have an rx and rw mapping, or if we already have + debuginfo for this mapping for whatever reason, go no + further. */ + if ( ! (di->have_rx_map && di->have_rw_map && !di->have_dinfo) ) + return 0; + + /* Ok, so, finally, let's try to read the debuginfo. */ + vg_assert(di->filename); + TRACE_SYMTAB("\n"); + TRACE_SYMTAB("------ start ELF OBJECT " + "------------------------------\n"); + TRACE_SYMTAB("------ name = %s\n", di->filename); + TRACE_SYMTAB("\n"); + + /* We're going to read symbols and debug info for the avma + ranges [rx_map_avma, +rx_map_size) and [rw_map_avma, + +rw_map_size). First get rid of any other DebugInfos which + overlap either of those ranges (to avoid total confusion). */ + discard_DebugInfos_which_overlap_with( di ); + + /* .. and acquire new info. */ + ok = ML_(read_elf_debug_info)( di ); + + if (ok) { + + TRACE_SYMTAB("\n------ Canonicalising the " + "acquired info ------\n"); + /* invalidate the CFI unwind cache. */ + cfsi_cache__invalidate(); + /* prepare read data for use */ + ML_(canonicaliseTables)( di ); + /* notify m_redir about it */ + TRACE_SYMTAB("\n------ Notifying m_redir ------\n"); + VG_(redir_notify_new_DebugInfo)( di ); + /* Note that we succeeded */ + di->have_dinfo = True; + tl_assert(di->handle > 0); + di_handle = di->handle; + /* Check invariants listed in + Comment_on_IMPORTANT_REPRESENTATIONAL_INVARIANTS in + priv_storage.h. */ + check_CFSI_related_invariants(di); + + } else { + TRACE_SYMTAB("\n------ ELF reading failed ------\n"); + /* Something went wrong (eg. bad ELF file). Should we delete + this DebugInfo? No - it contains info on the rw/rx + mappings, at least. */ + di_handle = 0; + vg_assert(di->have_dinfo == False); + } + + TRACE_SYMTAB("\n"); + TRACE_SYMTAB("------ name = %s\n", di->filename); + TRACE_SYMTAB("------ end ELF OBJECT " + "------------------------------\n"); + TRACE_SYMTAB("\n"); + + return di_handle; +} + + +/* Unmap is simpler - throw away any SegInfos intersecting + [a, a+len). */ +void VG_(di_notify_munmap)( Addr a, SizeT len ) +{ + Bool anyFound; + if (0) VG_(printf)("DISCARD %#lx %#lx\n", a, a+len); + anyFound = discard_syms_in_range(a, len); + if (anyFound) + cfsi_cache__invalidate(); +} + + +/* 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) { + Bool anyFound = discard_syms_in_range(a, len); + if (anyFound) + cfsi_cache__invalidate(); + } +} + +/*--------- PDB (windows debug info) reading --------- */ + +/* this should really return ULong, as per VG_(di_notify_mmap). */ +void VG_(di_notify_pdb_debuginfo)( Int fd_obj, Addr avma_obj, + SizeT total_size, + PtrdiffT unknown_purpose__reloc ) +{ + Int r, sz_exename; + ULong obj_mtime, pdb_mtime; + Char exename[VKI_PATH_MAX]; + Char* pdbname = NULL; + Char* dot; + SysRes sres; + Int fd_pdbimage; + SizeT n_pdbimage; + struct vg_stat stat_buf; + + if (VG_(clo_verbosity) > 0) { + VG_(message)(Vg_UserMsg, ""); + VG_(message)(Vg_UserMsg, + "LOAD_PDB_DEBUGINFO(fd=%d, avma=%#lx, total_size=%lu, " + "uu_reloc=%#lx)", + fd_obj, avma_obj, total_size, unknown_purpose__reloc + ); + } + + /* 'fd' refers to the .exe/.dll we're dealing with. Get its modification + time into obj_mtime. */ + r = VG_(fstat)(fd_obj, &stat_buf); + if (r == -1) + goto out; /* stat failed ?! */ + vg_assert(r == 0); + obj_mtime = stat_buf.st_mtime; + + /* and get its name into exename[]. */ + vg_assert(VKI_PATH_MAX > 100); /* to ensure /proc/self/fd/%d is safe */ + VG_(memset)(exename, 0, sizeof(exename)); + VG_(sprintf)(exename, "/proc/self/fd/%d", fd_obj); + /* convert exename from a symlink to real name .. overwrites the + old contents of the buffer. Ick. */ + sz_exename = VG_(readlink)(exename, exename, sizeof(exename)-2 ); + if (sz_exename == -1) + goto out; /* readlink failed ?! */ + vg_assert(sz_exename >= 0 && sz_exename < sizeof(exename)); + vg_assert(exename[sizeof(exename)-1] == 0); + + if (VG_(clo_verbosity) > 0) { + VG_(message)(Vg_UserMsg, "LOAD_PDB_DEBUGINFO: objname: %s", exename); + } + + /* Try to find a matching PDB file from which to read debuginfo. + Windows PE files have symbol tables and line number information, + but MSVC doesn't seem to use them. */ + /* Why +5 ? Because in the worst case, we could find a dot as the + last character of pdbname, and we'd then put "pdb" right after + it, hence extending it a bit. */ + pdbname = ML_(dinfo_zalloc)("di.debuginfo.lpd1", sz_exename+5); + VG_(strcpy)(pdbname, exename); + vg_assert(pdbname[sz_exename+5-1] == 0); + dot = VG_(strrchr)(pdbname, '.'); + if (!dot) + goto out; /* there's no dot in the exe's name ?! */ + if (dot[1] == 0) + goto out; /* hmm, path ends in "." */ + + if ('A' <= dot[1] && dot[1] <= 'Z') + VG_(strcpy)(dot, ".PDB"); + else + VG_(strcpy)(dot, ".pdb"); + + vg_assert(pdbname[sz_exename+5-1] == 0); + + /* See if we can find it, and check it's in-dateness. */ + sres = VG_(stat)(pdbname, &stat_buf); + if (sres.isError) { + VG_(message)(Vg_UserMsg, "Warning: Missing or un-stat-able %s", + pdbname); + if (VG_(clo_verbosity) > 0) + VG_(message)(Vg_UserMsg, "LOAD_PDB_DEBUGINFO: missing: %s", pdbname); + goto out; + } + pdb_mtime = stat_buf.st_mtime; + if (pdb_mtime < obj_mtime ) { + /* PDB file is older than PE file - ignore it or we will either + (a) print wrong stack traces or more likely (b) crash. */ + VG_(message)(Vg_UserMsg, "Warning: Ignoring %s since it is older than %s", + pdbname, exename); + goto out; + } + + sres = VG_(open)(pdbname, VKI_O_RDONLY, 0); + if (sres.isError) { + VG_(message)(Vg_UserMsg, "Warning: Can't open %s", pdbname); + goto out; + } + + /* Looks promising; go on to try and read stuff from it. */ + fd_pdbimage = sres.res; + n_pdbimage = stat_buf.st_size; + sres = VG_(am_mmap_file_float_valgrind)( n_pdbimage, VKI_PROT_READ, + fd_pdbimage, 0 ); + if (sres.isError) { + VG_(close)(fd_pdbimage); + goto out; + } + + if (VG_(clo_verbosity) > 0) + VG_(message)(Vg_UserMsg, "LOAD_PDB_DEBUGINFO: pdbname: %s", pdbname); + + /* play safe; always invalidate the CFI cache. I don't know if + this is necessary, but anyway .. */ + cfsi_cache__invalidate(); + /* dump old info for this range, if any */ + discard_syms_in_range( avma_obj, total_size ); + + { void* pdbimage = (void*)sres.res; + DebugInfo* di = find_or_create_DebugInfo_for(exename, NULL/*membername*/ ); + + /* this di must be new, since we just nuked any old stuff in the range */ + vg_assert(di && !di->have_rx_map && !di->have_rw_map); + vg_assert(!di->have_dinfo); + + /* don't set up any of the di-> fields; let + ML_(read_pdb_debug_info) do it. */ + ML_(read_pdb_debug_info)( di, avma_obj, unknown_purpose__reloc, + pdbimage, n_pdbimage, pdbname, pdb_mtime ); + // JRS fixme: take notice of return value from read_pdb_debug_info, + // and handle failure + vg_assert(di->have_dinfo); // fails if PDB read failed + VG_(am_munmap_valgrind)( (Addr)pdbimage, n_pdbimage ); + VG_(close)(fd_pdbimage); + } + + out: + if (pdbname) ML_(dinfo_free)(pdbname); +} + +#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. */ + +ULong 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 ) +{ + ULong hdl = 0; + + /* play safe; always invalidate the CFI cache. Not + that it should be used on AIX, but still .. */ + cfsi_cache__invalidate(); + + if (acquire) { + + Bool ok; + DebugInfo* di; + di = find_or_create_DebugInfo_for( file_name, mem_name ); + vg_assert(di); + + if (code_len > 0) { + di->text_present = True; + di->text_svma = 0; /* don't know yet */ + di->text_bias = 0; /* don't know yet */ + di->text_avma = code_start; + di->text_size = code_len; + } + if (data_len > 0) { + di->data_present = True; + di->data_svma = 0; /* don't know yet */ + di->data_bias = 0; /* don't know yet */ + di->data_avma = data_start; + di->data_size = data_len; + } + + /* These need to be filled in in order to keep various + assertions in storage.c happy. In particular see + "Comment_Regarding_Text_Range_Checks" in that file. */ + di->have_rx_map = True; + di->rx_map_avma = code_start; + di->rx_map_size = code_len; + di->have_rw_map = True; + di->rw_map_avma = data_start; + di->rw_map_size = data_len; + + ok = ML_(read_xcoff_debug_info) ( di, is_mainexe ); + + if (ok) { + /* prepare read data for use */ + ML_(canonicaliseTables)( di ); + /* notify m_redir about it */ + VG_(redir_notify_new_DebugInfo)( di ); + /* Note that we succeeded */ + di->have_dinfo = True; + hdl = di->handle; + vg_assert(hdl > 0); + /* Check invariants listed in + Comment_on_IMPORTANT_REPRESENTATIONAL_INVARIANTS in + priv_storage.h. */ + check_CFSI_related_invariants(di); + } else { + /* Something went wrong (eg. bad XCOFF file). */ + discard_DebugInfo( di ); + di = NULL; + } + + } else { + + /* Dump all the debugInfos whose text segments intersect + code_start/code_len. */ + /* CFI cache is always invalidated at start of this routine. + Hence it's safe to ignore the return value of + discard_syms_in_range. */ + if (code_len > 0) + (void)discard_syms_in_range( code_start, code_len ); + + } + + return hdl; +} + + +#endif /* defined(VGO_aix5) */ + + +/*------------------------------------------------------------*/ +/*--- ---*/ +/*--- TOP LEVEL: QUERYING EXISTING DEBUG INFO ---*/ +/*--- ---*/ +/*------------------------------------------------------------*/ + +void VG_(di_discard_ALL_debuginfo)( void ) +{ + DebugInfo *di, *di2; + di = debugInfo_list; + while (di) { + di2 = di->next; + VG_(printf)("XXX rm %p\n", di); + free_DebugInfo( di ); + di = di2; + } +} + + +/*------------------------------------------------------------*/ +/*--- 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 + *pdi to the relevant DebugInfo, and *symno to the symtab entry + *number within that. If not found, *psi is set to NULL. + If findText==True, only text symbols are searched for. + If findText==False, only data symbols are searched for. +*/ +static void search_all_symtabs ( Addr ptr, /*OUT*/DebugInfo** pdi, + /*OUT*/Word* symno, + Bool match_anywhere_in_sym, + Bool findText ) +{ + Word sno; + DebugInfo* di; + Bool inRange; + + for (di = debugInfo_list; di != NULL; di = di->next) { + + if (findText) { + inRange = di->text_present + && di->text_size > 0 + && di->text_avma <= ptr + && ptr < di->text_avma + di->text_size; + } else { + inRange = (di->data_present + && di->data_size > 0 + && di->data_avma <= ptr + && ptr < di->data_avma + di->data_size) + || + (di->sdata_present + && di->sdata_size > 0 + && di->sdata_avma <= ptr + && ptr < di->sdata_avma + di->sdata_size) + || + (di->bss_present + && di->bss_size > 0 + && di->bss_avma <= ptr + && ptr < di->bss_avma + di->bss_size) + || + (di->sbss_present + && di->sbss_size > 0 + && di->sbss_avma <= ptr + && ptr < di->sbss_avma + di->sbss_size) + || + (di->rodata_present + && di->rodata_size > 0 + && di->rodata_avma <= ptr + && ptr < di->rodata_avma + di->rodata_size); + } + + if (!inRange) continue; + + sno = ML_(search_one_symtab) ( + di, ptr, match_anywhere_in_sym, findText ); + if (sno == -1) goto not_found; + *symno = sno; + *pdi = di; + return; + + } + not_found: + *pdi = NULL; +} + + +/* Search all loctabs that we know about to locate ptr. If found, set + *pdi to the relevant DebugInfo, and *locno to the loctab entry + *number within that. If not found, *pdi is set to NULL. */ +static void search_all_loctabs ( Addr ptr, /*OUT*/DebugInfo** pdi, + /*OUT*/Word* locno ) +{ + Word lno; + DebugInfo* di; + for (di = debugInfo_list; di != NULL; di = di->next) { + if (di->text_present + && di->text_size > 0 + && di->text_avma <= ptr + && ptr < di->text_avma + di->text_size) { + lno = ML_(search_one_loctab) ( di, ptr ); + if (lno == -1) goto not_found; + *locno = lno; + *pdi = di; + return; + } + } + not_found: + *pdi = 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 do_cxx_demangling is False, don't do + C++ demangling, regardless of VG_(clo_demangle) -- probably because the + call has come from VG_(get_fnname_raw)(). findText + indicates whether we're looking for a text symbol or a data symbol + -- caller must choose one kind or the other. */ +static +Bool get_sym_name ( Bool do_cxx_demangling, Bool do_z_demangling, + Bool do_below_main_renaming, + Addr a, Char* buf, Int nbuf, + Bool match_anywhere_in_sym, Bool show_offset, + Bool findText, /*OUT*/PtrdiffT* offsetP ) +{ + DebugInfo* di; + Word sno; + PtrdiffT offset; + + search_all_symtabs ( a, &di, &sno, match_anywhere_in_sym, findText ); + if (di == NULL) + return False; + + VG_(demangle) ( do_cxx_demangling, do_z_demangling, + di->symtab[sno].name, buf, nbuf ); + + /* Do the below-main hack */ + // To reduce the endless nuisance of multiple different names + // for "the frame below main()" screwing up the testsuite, change all + // known incarnations of said into a single name, "(below main)", if + // --show-below-main=yes. + if ( do_below_main_renaming && ! VG_(clo_show_below_main) && + Vg_FnNameBelowMain == VG_(get_fnname_kind)(buf) ) + { + VG_(strncpy_safely)(buf, "(below main)", nbuf); + } + offset = a - di->symtab[sno].addr; + if (offsetP) *offsetP = offset; + + 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%ld", + 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); + } + } + + buf[nbuf-1] = 0; /* paranoia */ + + 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 ) +{ + DebugInfo* si; + Word sno; + search_all_symtabs ( guest_code_addr, + &si, &sno, + True/*match_anywhere_in_fun*/, + True/*consider text symbols only*/ ); + 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_sym_name ( /*C++-demangle*/True, /*Z-demangle*/True, + /*below-main-renaming*/True, + a, buf, nbuf, + /*match_anywhere_in_fun*/True, + /*show offset?*/False, + /*text syms only*/True, + /*offsetP*/NULL ); +} + +/* 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_sym_name ( /*C++-demangle*/True, /*Z-demangle*/True, + /*below-main-renaming*/True, + a, buf, nbuf, + /*match_anywhere_in_fun*/True, + /*show offset?*/True, + /*text syms only*/True, + /*offsetP*/NULL ); +} + +/* 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_sym_name ( /*C++-demangle*/True, /*Z-demangle*/True, + /*below-main-renaming*/True, + a, buf, nbuf, + /*match_anywhere_in_fun*/False, + /*show offset?*/False, + /*text syms only*/True, + /*offsetP*/NULL ); +} + +/* This is only available to core... don't C++-demangle, don't Z-demangle, + don't rename below-main, match anywhere in function, and don't show + offsets. */ +Bool VG_(get_fnname_raw) ( Addr a, Char* buf, Int nbuf ) +{ + return get_sym_name ( /*C++-demangle*/False, /*Z-demangle*/False, + /*below-main-renaming*/False, + a, buf, nbuf, + /*match_anywhere_in_fun*/True, + /*show offset?*/False, + /*text syms only*/True, + /*offsetP*/NULL ); +} + +/* This is only available to core... don't demangle C++ names, but do + do Z-demangling and below-main-renaming, match anywhere in function, and + don't show offsets. */ +Bool VG_(get_fnname_no_cxx_demangle) ( Addr a, Char* buf, Int nbuf ) +{ + return get_sym_name ( /*C++-demangle*/False, /*Z-demangle*/True, + /*below-main-renaming*/True, + a, buf, nbuf, + /*match_anywhere_in_fun*/True, + /*show offset?*/False, + /*text syms only*/True, + /*offsetP*/NULL ); +} + +Vg_FnNameKind VG_(get_fnname_kind) ( Char* name ) +{ + if (VG_STREQ("main", name)) { + return Vg_FnNameMain; + + } else if ( +#if defined(VGO_linux) + VG_STREQ("__libc_start_main", name) || // glibc glibness + VG_STREQ("generic_start_main", name) || // Yellow Dog doggedness +#elif defined(VGO_aix5) + VG_STREQ("__start", name) || // AIX aches +#else +# error Unknown OS +#endif + 0) { + return Vg_FnNameBelowMain; + + } else { + return Vg_FnNameNormal; + } +} + +Vg_FnNameKind VG_(get_fnname_kind_from_IP) ( Addr ip ) +{ + // We don't need a big buffer; all the special names are small. + #define BUFLEN 50 + Char buf[50]; + + // We don't demangle, because it's faster not to, and the special names + // we're looking for won't be demangled. + if (VG_(get_fnname_raw) ( ip, buf, BUFLEN )) { + buf[BUFLEN-1] = '\0'; // paranoia + return VG_(get_fnname_kind)(buf); + } else { + return Vg_FnNameNormal; // Don't know the name, treat it as normal. + } +} + +/* Looks up data_addr in the collection of data symbols, and if found + puts its name (or as much as will fit) into dname[0 .. n_dname-1], + which is guaranteed to be zero terminated. Also data_addr's offset + from the symbol start is put into *offset. */ +Bool VG_(get_datasym_and_offset)( Addr data_addr, + /*OUT*/Char* dname, Int n_dname, + /*OUT*/PtrdiffT* offset ) +{ + Bool ok; + vg_assert(n_dname > 1); + ok = get_sym_name ( /*C++-demangle*/False, /*Z-demangle*/False, + /*below-main-renaming*/False, + data_addr, dname, n_dname, + /*match_anywhere_in_sym*/True, + /*show offset?*/False, + /*data syms only please*/False, + offset ); + if (!ok) + return False; + dname[n_dname-1] = 0; + return True; +} + +/* 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; + DebugInfo* di; + const NSegment *seg; + HChar* filename; + vg_assert(nbuf > 0); + /* Look in the debugInfo_list to find the name. In most cases we + expect this to produce a result. */ + for (di = debugInfo_list; di != NULL; di = di->next) { + if (di->text_present + && di->text_size > 0 + && di->text_avma <= a + && a < di->text_avma + di->text_size) { + VG_(strncpy_safely)(buf, di->filename, nbuf); + if (di->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], di->memname, nbuf-used); + used = VG_(strlen)(buf); + if (used < nbuf) + VG_(strncpy_safely)(&buf[used], ")", nbuf-used); + } + buf[nbuf-1] = 0; + return True; + } + } + /* Last-ditch fallback position: if we don't find the address in + the debugInfo_list, ask the address space manager whether it + knows the name of the file associated with this mapping. This + allows us to print the names of exe/dll files in the stack trace + when running programs under wine. */ + if ( (seg = VG_(am_find_nsegment(a))) != NULL + && (filename = VG_(am_get_filename)(seg)) != NULL ) { + VG_(strncpy_safely)(buf, filename, nbuf); + return True; + } + return False; +} + +/* Map a code address to its DebugInfo. Returns NULL if not found. Doesn't + require debug info. */ +DebugInfo* VG_(find_seginfo) ( Addr a ) +{ + DebugInfo* di; + for (di = debugInfo_list; di != NULL; di = di->next) { + if (di->text_present + && di->text_size > 0 + && di->text_avma <= a + && a < di->text_avma + di->text_size) { + return di; + } + } + return NULL; +} + +/* Map a code address to a filename. Returns True if successful. */ +Bool VG_(get_filename)( Addr a, Char* filename, Int n_filename ) +{ + DebugInfo* si; + Word 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 ) +{ + DebugInfo* si; + Word 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 ) +{ + DebugInfo* si; + Word 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; + DebugInfo* si; + Bool debug = False; +# if defined(VG_PLAT_USES_PPCTOC) + require_pToc = True; +# endif + for (si = debugInfo_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, "&"); + count -= 5; + break; + case '<': + if (count < 4) goto done; + n = putStr( n, n_buf, buf, "<"); + count -= 4; + break; + case '>': + if (count < 4) goto done; + n = putStr( n, n_buf, buf, ">"); + 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 */ + // + // Possible forms: + // + // 0x80483BF: really (a.c:20) + // 0x80483BF: really (in /foo/a.out) + // 0x80483BF: really (in ???) + // 0x80483BF: ??? (in /foo/a.out) + // 0x80483BF: ??? (a.c:20) + // 0x80483BF: ??? + // + VG_(sprintf)(ibuf,"0x%llX: ", (ULong)eip); + APPEND(ibuf); + if (know_fnname) { + APPEND(buf_fn); + } else { + APPEND("???"); + } + if (know_srcloc) { + APPEND(" ("); + APPEND(buf_srcloc); + APPEND(":"); + VG_(sprintf)(ibuf,"%d",lineno); + APPEND(ibuf); + APPEND(")"); + } else if (know_objname) { + APPEND(" (in "); + APPEND(buf_obj); + APPEND(")"); + } else if (know_fnname) { + // Nb: do this in two steps because "??)" is a trigraph! + APPEND(" (in ???"); + APPEND(")"); + } + + } + return buf; + +# undef APPEND +# undef APPEND_ESC +# undef BUF_LEN +} + + +/*--------------------------------------------------------------*/ +/*--- ---*/ +/*--- TOP LEVEL: FOR UNWINDING THE STACK USING ---*/ +/*--- 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; +} + + +/* Search all the DebugInfos in the entire system, to find the DiCfSI + that pertains to 'ip'. + + If found, set *diP to the DebugInfo in which it resides, and + *ixP to the index in that DebugInfo's cfsi array. + + If not found, set *diP to (DebugInfo*)1 and *ixP to zero. +*/ +__attribute__((noinline)) +static void find_DiCfSI ( /*OUT*/DebugInfo** diP, + /*OUT*/Word* ixP, + Addr ip ) +{ + DebugInfo* di; + Word i = -1; + + static UWord n_search = 0; + static UWord n_steps = 0; + n_search++; + + if (0) VG_(printf)("search for %#lx\n", ip); + + for (di = debugInfo_list; di != NULL; di = di->next) { + Word j; + n_steps++; + + /* Use the per-DebugInfo summary address ranges to skip + inapplicable DebugInfos quickly. */ + if (di->cfsi_used == 0) + continue; + if (ip < di->cfsi_minavma || ip > di->cfsi_maxavma) + continue; + + /* It might be in this DebugInfo. Search it. */ + j = ML_(search_one_cfitab)( di, ip ); + vg_assert(j >= -1 && j < (Word)di->cfsi_used); + + if (j != -1) { + i = j; + break; /* found it */ + } + } + + if (i == -1) { + + /* we didn't find it. */ + *diP = (DebugInfo*)1; + *ixP = 0; + + } else { + + /* found it. */ + /* ensure that di is 4-aligned (at least), so it can't possibly + be equal to (DebugInfo*)1. */ + vg_assert(di && VG_IS_4_ALIGNED(di)); + vg_assert(i >= 0 && i < di->cfsi_used); + *diP = di; + *ixP = i; + + /* Start of performance-enhancing hack: once every 64 (chosen + hackily after profiling) successful searches, move the found + DebugInfo 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-DebugInfo loop by more than + a factor of 20. */ + if ((n_search & 0xF) == 0) { + /* Move di one step closer to the start of the list. */ + move_DebugInfo_one_step_forward( di ); + } + /* End of performance-enhancing hack. */ + + if (0 && ((n_search & 0x7FFFF) == 0)) + VG_(printf)("find_DiCfSI: %lu searches, " + "%lu DebugInfos looked at\n", + n_search, n_steps); + + } + +} + + +/* Now follows a mechanism for caching queries to find_DiCfSI, since + they are extremely frequent on amd64-linux, during stack unwinding. + + Each cache entry binds an ip value to a (di, ix) pair. Possible + values: + + di is non-null, ix >= 0 ==> cache slot in use, "di->cfsi[ix]" + di is (DebugInfo*)1 ==> cache slot in use, no associated di + di is NULL ==> cache slot not in use + + Hence simply zeroing out the entire cache invalidates all + entries. + + Why not map ip values directly to DiCfSI*'s? Because this would + cause problems if/when the cfsi array is moved due to resizing. + Instead we cache .cfsi array index value, which should be invariant + across resizing. (That said, I don't think the current + implementation will resize whilst during queries, since the DiCfSI + records are added all at once, when the debuginfo for an object is + read, and is not changed ever thereafter. */ + +#define N_CFSI_CACHE 511 + +typedef + struct { Addr ip; DebugInfo* di; Word ix; } + CFSICacheEnt; + +static CFSICacheEnt cfsi_cache[N_CFSI_CACHE]; + +static void cfsi_cache__invalidate ( void ) { + VG_(memset)(&cfsi_cache, 0, sizeof(cfsi_cache)); +} + + +/* 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 + DebugInfo list. */ +Bool VG_(use_CF_info) ( /*MOD*/Addr* ipP, + /*MOD*/Addr* spP, + /*MOD*/Addr* fpP, + Addr min_accessible, + Addr max_accessible ) +{ + Bool ok; + DebugInfo* di; + DiCfSI* cfsi = NULL; + Addr cfa, ipHere, spHere, fpHere, ipPrev, spPrev, fpPrev; + + CfiExprEvalContext eec; + + static UWord n_q = 0, n_m = 0; + n_q++; + if (0 && 0 == (n_q & 0x1FFFFF)) + VG_(printf)("QQQ %lu %lu\n", n_q, n_m); + + { UWord hash = (*ipP) % N_CFSI_CACHE; + CFSICacheEnt* ce = &cfsi_cache[hash]; + + if (LIKELY(ce->ip == *ipP) && LIKELY(ce->di != NULL)) { + /* found an entry in the cache .. */ + } else { + /* not found in cache. Search and update. */ + n_m++; + ce->ip = *ipP; + find_DiCfSI( &ce->di, &ce->ix, *ipP ); + } + + if (UNLIKELY(ce->di == (DebugInfo*)1)) { + /* no DiCfSI for this address */ + cfsi = NULL; + di = NULL; + } else { + /* found a DiCfSI for this address */ + di = ce->di; + cfsi = &di->cfsi[ ce->ix ]; + } + } + + if (UNLIKELY(cfsi == NULL)) + return False; /* no info. Nothing we can do. */ + + if (0) { + VG_(printf)("found cfisi: "); + ML_(ppDiCfSI)(di->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)(di->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(di->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 > max_accessible-sizeof(Addr)) \ + return False; \ + _prev = *(Addr*)a; \ + break; \ + } \ + case CFIR_CFAREL: \ + _prev = cfa + (Word)_off; \ + break; \ + case CFIR_EXPR: \ + if (0) \ + ML_(ppCfiExpr)(di->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(di->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; +} + + +/*--------------------------------------------------------------*/ +/*--- ---*/ +/*--- TOP LEVEL: FOR UNWINDING THE STACK USING ---*/ +/*--- MSVC FPO INFO ---*/ +/*--- ---*/ +/*--------------------------------------------------------------*/ + +Bool VG_(use_FPO_info) ( /*MOD*/Addr* ipP, + /*MOD*/Addr* spP, + /*MOD*/Addr* fpP, + Addr min_accessible, + Addr max_accessible ) +{ + Word i; + DebugInfo* di; + FPO_DATA* fpo = NULL; + Addr spHere; + + static UWord n_search = 0; + static UWord n_steps = 0; + n_search++; + + if (0) VG_(printf)("search FPO for %#lx\n", *ipP); + + for (di = debugInfo_list; di != NULL; di = di->next) { + n_steps++; + + /* Use the per-DebugInfo summary address ranges to skip + inapplicable DebugInfos quickly. */ + if (di->fpo == NULL) + continue; + if (*ipP < di->fpo_minavma || *ipP > di->fpo_maxavma) + continue; + + i = ML_(search_one_fpotab)( di, *ipP ); + if (i != -1) { + Word j; + if (0) { + /* debug printing only */ + VG_(printf)("look for %#lx size %ld i %ld\n", + *ipP, di->fpo_size, i); + for (j = 0; j < di->fpo_size; j++) + VG_(printf)("[%02ld] %#x %d\n", + j, di->fpo[j].ulOffStart, di->fpo[j].cbProcSize); + } + vg_assert(i >= 0 && i < di->fpo_size); + fpo = &di->fpo[i]; + break; + } + } + + if (fpo == NULL) + return False; + + if (0 && ((n_search & 0x7FFFF) == 0)) + VG_(printf)("VG_(use_FPO_info): %lu searches, " + "%lu DebugInfos looked at\n", + n_search, n_steps); + + + /* Start of performance-enhancing hack: once every 64 (chosen + hackily after profiling) successful searches, move the found + DebugInfo 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-DebugInfo loop by more than a factor of 20. */ + if ((n_search & 0x3F) == 0) { + /* Move si one step closer to the start of the list. */ + //move_DebugInfo_one_step_forward( di ); + } + /* End of performance-enhancing hack. */ + + if (0) { + VG_(printf)("found fpo: "); + //ML_(ppFPO)(fpo); + } + + /* + Stack layout is: + %esp-> + 4*.cbRegs {%edi, %esi, %ebp, %ebx} + 4*.cdwLocals + return_pc + 4*.cdwParams + prior_%esp-> + + Typical code looks like: + sub $4*.cdwLocals,%esp + Alternative to above for >=4KB (and sometimes for smaller): + mov $size,%eax + call __chkstk # WinNT performs page-by-page probe! + __chkstk is much like alloc(), except that on return + %eax= 5+ &CALL. Thus it could be used as part of + Position Independent Code to locate the Global Offset Table. + push %ebx + push %ebp + push %esi + Other once-only instructions often scheduled >here<. + push %edi + + If the pc is within the first .cbProlog bytes of the function, + then you must disassemble to see how many registers have been pushed, + because instructions in the prolog may be scheduled for performance. + The order of PUSH is always %ebx, %ebp, %esi, %edi, with trailing + registers not pushed when .cbRegs < 4. This seems somewhat strange + because %ebp is the register whose usage you want to minimize, + yet it is in the first half of the PUSH list. + + I don't know what happens when the compiler constructs an outgoing CALL. + %esp could move if outgoing parameters are PUSHed, and this affects + traceback for errors during the PUSHes. */ + + spHere = *spP; + + *ipP = *(Addr *)(spHere + 4*(fpo->cbRegs + fpo->cdwLocals)); + *spP = spHere + 4*(fpo->cbRegs + fpo->cdwLocals + 1 + fpo->cdwParams); + *fpP = *(Addr *)(spHere + 4*2); + return True; +} + + +/*--------------------------------------------------------------*/ +/*--- ---*/ +/*--- TOP LEVEL: GENERATE DESCRIPTION OF DATA ADDRESSES ---*/ +/*--- FROM DWARF3 DEBUG INFO ---*/ +/*--- ---*/ +/*--------------------------------------------------------------*/ + +/* Evaluate the location expression/list for var, to see whether or + not data_addr falls within the variable. If so also return the + offset of data_addr from the start of the variable. Note that + regs, which supplies ip,sp,fp values, will be NULL for global + variables, and non-NULL for local variables. */ +static Bool data_address_is_in_var ( /*OUT*/PtrdiffT* offset, + XArray* /* TyEnt */ tyents, + DiVariable* var, + RegSummary* regs, + Addr data_addr, + const DebugInfo* di ) +{ + MaybeULong mul; + SizeT var_szB; + GXResult res; + Bool show = False; + + vg_assert(var->name); + vg_assert(var->gexpr); + + /* Figure out how big the variable is. */ + mul = ML_(sizeOfType)(tyents, var->typeR); + /* If this var has a type whose size is unknown, zero, or + impossibly large, it should never have been added. ML_(addVar) + should have rejected it. */ + vg_assert(mul.b == True); + vg_assert(mul.ul > 0); + if (sizeof(void*) == 4) vg_assert(mul.ul < (1ULL << 32)); + /* After this point, we assume we can truncate mul.ul to a host word + safely (without loss of info). */ + + var_szB = (SizeT)mul.ul; /* NB: truncate to host word */ + + if (show) { + VG_(printf)("VVVV: data_address_%#lx_is_in_var: %s :: ", + data_addr, var->name ); + ML_(pp_TyEnt_C_ishly)( tyents, var->typeR ); + VG_(printf)("\n"); + } + + /* ignore zero-sized vars; they can never match anything. */ + if (var_szB == 0) { + if (show) + VG_(printf)("VVVV: -> Fail (variable is zero sized)\n"); + return False; + } + + res = ML_(evaluate_GX)( var->gexpr, var->fbGX, regs, di ); + + if (show) { + VG_(printf)("VVVV: -> "); + ML_(pp_GXResult)( res ); + VG_(printf)("\n"); + } + + if (res.kind == GXR_Value + && res.word <= data_addr + && data_addr < res.word + var_szB) { + *offset = data_addr - res.word; + return True; + } else { + return False; + } +} + + +/* Format the acquired information into dname1[0 .. n_dname-1] and + dname2[0 .. n_dname-1] in an understandable way. Not so easy. + If frameNo is -1, this is assumed to be a global variable; else + a local variable. */ +static void format_message ( /*OUT*/Char* dname1, + /*OUT*/Char* dname2, + Int n_dname, + Addr data_addr, + DiVariable* var, + PtrdiffT var_offset, + PtrdiffT residual_offset, + XArray* /*UChar*/ described, + Int frameNo, + ThreadId tid ) +{ + Bool have_descr, have_srcloc; + UChar* vo_plural = var_offset == 1 ? "" : "s"; + UChar* ro_plural = residual_offset == 1 ? "" : "s"; + + vg_assert(frameNo >= -1); + vg_assert(dname1 && dname2 && n_dname > 1); + vg_assert(described); + vg_assert(var && var->name); + have_descr = VG_(sizeXA)(described) > 0 + && *(UChar*)VG_(indexXA)(described,0) != '\0'; + have_srcloc = var->fileName && var->lineNo > 0; + + dname1[0] = dname2[0] = '\0'; + + /* ------ local cases ------ */ + + if ( frameNo >= 0 && (!have_srcloc) && (!have_descr) ) { + /* no srcloc, no description: + Location 0x7fefff6cf is 543 bytes inside local var "a", + in frame #1 of thread 1 + */ + VG_(snprintf)( + dname1, n_dname, + "Location 0x%lx is %lu byte%s inside local var \"%s\",", + data_addr, var_offset, vo_plural, var->name ); + VG_(snprintf)( + dname2, n_dname, + "in frame #%d of thread %d", frameNo, (Int)tid); + } + else + if ( frameNo >= 0 && have_srcloc && (!have_descr) ) { + /* no description: + Location 0x7fefff6cf is 543 bytes inside local var "a" + declared at dsyms7.c:17, in frame #1 of thread 1 + */ + VG_(snprintf)( + dname1, n_dname, + "Location 0x%lx is %lu byte%s inside local var \"%s\"", + data_addr, var_offset, vo_plural, var->name ); + VG_(snprintf)( + dname2, n_dname, + "declared at %s:%d, in frame #%d of thread %d", + var->fileName, var->lineNo, frameNo, (Int)tid); + } + else + if ( frameNo >= 0 && (!have_srcloc) && have_descr ) { + /* no srcloc: + Location 0x7fefff6cf is 2 bytes inside a[3].xyzzy[21].c2 + in frame #1 of thread 1 + */ + VG_(snprintf)( + dname1, n_dname, + "Location 0x%lx is %lu byte%s inside %s%s", + data_addr, residual_offset, ro_plural, var->name, + (char*)(VG_(indexXA)(described,0)) ); + VG_(snprintf)( + dname2, n_dname, + "in frame #%d of thread %d", frameNo, (Int)tid); + } + else + if ( frameNo >= 0 && have_srcloc && have_descr ) { + /* Location 0x7fefff6cf is 2 bytes inside a[3].xyzzy[21].c2, + declared at dsyms7.c:17, in frame #1 of thread 1 */ + VG_(snprintf)( + dname1, n_dname, + "Location 0x%lx is %lu byte%s inside %s%s,", + data_addr, residual_offset, ro_plural, var->name, + (char*)(VG_(indexXA)(described,0)) ); + VG_(snprintf)( + dname2, n_dname, + "declared at %s:%d, in frame #%d of thread %d", + var->fileName, var->lineNo, frameNo, (Int)tid); + } + else + /* ------ global cases ------ */ + if ( frameNo >= -1 && (!have_srcloc) && (!have_descr) ) { + /* no srcloc, no description: + Location 0x7fefff6cf is 543 bytes inside global var "a" + */ + VG_(snprintf)( + dname1, n_dname, + "Location 0x%lx is %lu byte%s inside global var \"%s\"", + data_addr, var_offset, vo_plural, var->name ); + } + else + if ( frameNo >= -1 && have_srcloc && (!have_descr) ) { + /* no description: + Location 0x7fefff6cf is 543 bytes inside global var "a" + declared at dsyms7.c:17 + */ + VG_(snprintf)( + dname1, n_dname, + "Location 0x%lx is %lu byte%s inside global var \"%s\"", + data_addr, var_offset, vo_plural, var->name ); + VG_(snprintf)( + dname2, n_dname, + "declared at %s:%d", + var->fileName, var->lineNo); + } + else + if ( frameNo >= -1 && (!have_srcloc) && have_descr ) { + /* no srcloc: + Location 0x7fefff6cf is 2 bytes inside a[3].xyzzy[21].c2, + a global variable + */ + VG_(snprintf)( + dname1, n_dname, + "Location 0x%lx is %lu byte%s inside %s%s,", + data_addr, residual_offset, ro_plural, var->name, + (char*)(VG_(indexXA)(described,0)) ); + VG_(snprintf)( + dname2, n_dname, + "a global variable"); + } + else + if ( frameNo >= -1 && have_srcloc && have_descr ) { + /* Location 0x7fefff6cf is 2 bytes inside a[3].xyzzy[21].c2, + a global variable declared at dsyms7.c:17 */ + VG_(snprintf)( + dname1, n_dname, + "Location 0x%lx is %lu byte%s inside %s%s,", + data_addr, residual_offset, ro_plural, var->name, + (char*)(VG_(indexXA)(described,0)) ); + VG_(snprintf)( + dname2, n_dname, + "a global variable declared at %s:%d", + var->fileName, var->lineNo); + } + else + vg_assert(0); + + dname1[n_dname-1] = dname2[n_dname-1] = 0; +} + +/* Determine if data_addr is a local variable in the frame + characterised by (ip,sp,fp), and if so write its description into + dname{1,2}[0..n_dname-1], and return True. If not, return + False. */ +static +Bool consider_vars_in_frame ( /*OUT*/Char* dname1, + /*OUT*/Char* dname2, + Int n_dname, + Addr data_addr, + Addr ip, Addr sp, Addr fp, + /* shown to user: */ + ThreadId tid, Int frameNo ) +{ + Word i; + DebugInfo* di; + RegSummary regs; + Bool debug = False; + + static UInt n_search = 0; + static UInt n_steps = 0; + n_search++; + if (debug) + VG_(printf)("QQQQ: cvif: ip,sp,fp %#lx,%#lx,%#lx\n", ip,sp,fp); + /* first, find the DebugInfo that pertains to 'ip'. */ + for (di = debugInfo_list; di; di = di->next) { + n_steps++; + /* text segment missing? unlikely, but handle it .. */ + if (!di->text_present || di->text_size == 0) + continue; + /* Ok. So does this text mapping bracket the ip? */ + if (di->text_avma <= ip && ip < di->text_avma + di->text_size) + break; + } + + /* Didn't find it. Strange -- means ip is a code address outside + of any mapped text segment. Unlikely but not impossible -- app + could be generating code to run. */ + if (!di) + return False; + + if (0 && ((n_search & 0x1) == 0)) + VG_(printf)("consider_vars_in_frame: %u searches, " + "%u DebugInfos looked at\n", + n_search, n_steps); + /* Start of performance-enhancing hack: once every ??? (chosen + hackily after profiling) successful searches, move the found + DebugInfo one step closer to the start of the list. This makes + future searches cheaper. */ + if ((n_search & 0xFFFF) == 0) { + /* Move si one step closer to the start of the list. */ + move_DebugInfo_one_step_forward( di ); + } + /* End of performance-enhancing hack. */ + + /* any var info at all? */ + if (!di->varinfo) + return False; + + /* Work through the scopes from most deeply nested outwards, + looking for code address ranges that bracket 'ip'. The + variables on each such address range found are in scope right + now. Don't descend to level zero as that is the global + scope. */ + regs.ip = ip; + regs.sp = sp; + regs.fp = fp; + + /* "for each scope, working outwards ..." */ + for (i = VG_(sizeXA)(di->varinfo) - 1; i >= 1; i--) { + XArray* vars; + Word j; + DiAddrRange* arange; + OSet* this_scope + = *(OSet**)VG_(indexXA)( di->varinfo, i ); + if (debug) + VG_(printf)("QQQQ: considering scope %ld\n", (Word)i); + if (!this_scope) + continue; + /* Find the set of variables in this scope that + bracket the program counter. */ + arange = VG_(OSetGen_LookupWithCmp)( + this_scope, &ip, + ML_(cmp_for_DiAddrRange_range) + ); + if (!arange) + continue; + /* stay sane */ + vg_assert(arange->aMin <= arange->aMax); + /* It must bracket the ip we asked for, else + ML_(cmp_for_DiAddrRange_range) is somehow broken. */ + vg_assert(arange->aMin <= ip && ip <= arange->aMax); + /* It must have an attached XArray of DiVariables. */ + vars = arange->vars; + vg_assert(vars); + /* But it mustn't cover the entire address range. We only + expect that to happen for the global scope (level 0), which + we're not looking at here. Except, it may cover the entire + address range, but in that case the vars array must be + empty. */ + vg_assert(! (arange->aMin == (Addr)0 + && arange->aMax == ~(Addr)0 + && VG_(sizeXA)(vars) > 0) ); + for (j = 0; j < VG_(sizeXA)( vars ); j++) { + DiVariable* var = (DiVariable*)VG_(indexXA)( vars, j ); + PtrdiffT offset; + if (debug) + VG_(printf)("QQQQ: var:name=%s %#lx-%#lx %#lx\n", + var->name,arange->aMin,arange->aMax,ip); + if (data_address_is_in_var( &offset, di->admin_tyents, + var, ®s, + data_addr, di )) { + PtrdiffT residual_offset = 0; + XArray* described = ML_(describe_type)( &residual_offset, + di->admin_tyents, + var->typeR, offset ); + format_message( dname1, dname2, n_dname, + data_addr, var, offset, residual_offset, + described, frameNo, tid ); + VG_(deleteXA)( described ); + return True; + } + } + } + + return False; +} + +/* Try to form some description of data_addr by looking at the DWARF3 + debug info we have. This considers all global variables, and all + frames in the stacks of all threads. Result (or as much as will + fit) is put into into dname{1,2}[0 .. n_dname-1] and is guaranteed + to be zero terminated. */ +Bool VG_(get_data_description)( /*OUT*/Char* dname1, + /*OUT*/Char* dname2, + Int n_dname, + Addr data_addr ) +{ +# define N_FRAMES 8 + Addr ips[N_FRAMES], sps[N_FRAMES], fps[N_FRAMES]; + UInt n_frames; + + Addr stack_min, stack_max; + ThreadId tid; + Bool found; + DebugInfo* di; + Word j; + + vg_assert(n_dname > 1); + dname1[n_dname-1] = dname2[n_dname-1] = 0; + + if (0) VG_(printf)("get_data_description: dataaddr %#lx\n", data_addr); + /* First, see if data_addr is (or is part of) a global variable. + Loop over the DebugInfos we have. Check data_addr against the + outermost scope of all of them, as that should be a global + scope. */ + for (di = debugInfo_list; di != NULL; di = di->next) { + OSet* global_scope; + Word gs_size; + Addr zero; + DiAddrRange* global_arange; + Word i; + XArray* vars; + + /* text segment missing? unlikely, but handle it .. */ + if (!di->text_present || di->text_size == 0) + continue; + /* any var info at all? */ + if (!di->varinfo) + continue; + /* perhaps this object didn't contribute any vars at all? */ + if (VG_(sizeXA)( di->varinfo ) == 0) + continue; + global_scope = *(OSet**)VG_(indexXA)( di->varinfo, 0 ); + vg_assert(global_scope); + gs_size = VG_(OSetGen_Size)( global_scope ); + /* The global scope might be completely empty if this + compilation unit declared locals but nothing global. */ + if (gs_size == 0) + continue; + /* But if it isn't empty, then it must contain exactly one + element, which covers the entire address range. */ + vg_assert(gs_size == 1); + /* Fish out the global scope and check it is as expected. */ + zero = 0; + global_arange + = VG_(OSetGen_Lookup)( global_scope, &zero ); + /* The global range from (Addr)0 to ~(Addr)0 must exist */ + vg_assert(global_arange); + vg_assert(global_arange->aMin == (Addr)0 + && global_arange->aMax == ~(Addr)0); + /* Any vars in this range? */ + if (!global_arange->vars) + continue; + /* Ok, there are some vars in the global scope of this + DebugInfo. Wade through them and see if the data addresses + of any of them bracket data_addr. */ + vars = global_arange->vars; + for (i = 0; i < VG_(sizeXA)( vars ); i++) { + PtrdiffT offset; + DiVariable* var = (DiVariable*)VG_(indexXA)( vars, i ); + vg_assert(var->name); + /* Note we use a NULL RegSummary* here. It can't make any + sense for a global variable to have a location expression + which depends on a SP/FP/IP value. So don't supply any. + This means, if the evaluation of the location + expression/list requires a register, we have to let it + fail. */ + if (data_address_is_in_var( &offset, di->admin_tyents, var, + NULL/* RegSummary* */, + data_addr, di )) { + PtrdiffT residual_offset = 0; + XArray* described = ML_(describe_type)( &residual_offset, + di->admin_tyents, + var->typeR, offset ); + format_message( dname1, dname2, n_dname, + data_addr, var, offset, residual_offset, + described, -1/*frameNo*/, tid ); + VG_(deleteXA)( described ); + dname1[n_dname-1] = dname2[n_dname-1] = 0; + return True; + } + } + } + + /* Ok, well it's not a global variable. So now let's snoop around + in the stacks of all the threads. First try to figure out which + thread's stack data_addr is in. */ + + /* --- KLUDGE --- Try examining the top frame of all thread stacks. + This finds variables which are not stack allocated but are not + globally visible either; specifically it appears to pick up + variables which are visible only within a compilation unit. + These will have the address range of the compilation unit and + tend to live at Scope level 1. */ + VG_(thread_stack_reset_iter)(&tid); + while ( VG_(thread_stack_next)(&tid, &stack_min, &stack_max) ) { + if (stack_min >= stack_max) + continue; /* ignore obviously stupid cases */ + if (consider_vars_in_frame( dname1, dname2, n_dname, + data_addr, + VG_(get_IP)(tid), + VG_(get_SP)(tid), + VG_(get_FP)(tid), tid, 0 )) { + dname1[n_dname-1] = dname2[n_dname-1] = 0; + return True; + } + } + /* --- end KLUDGE --- */ + + /* Perhaps it's on a thread's stack? */ + found = False; + VG_(thread_stack_reset_iter)(&tid); + while ( VG_(thread_stack_next)(&tid, &stack_min, &stack_max) ) { + if (stack_min >= stack_max) + continue; /* ignore obviously stupid cases */ + if (stack_min - VG_STACK_REDZONE_SZB <= data_addr + && data_addr <= stack_max) { + found = True; + break; + } + } + if (!found) { + dname1[n_dname-1] = dname2[n_dname-1] = 0; + return False; + } + + /* We conclude data_addr is in thread tid's stack. Unwind the + stack to get a bunch of (ip,sp,fp) triples describing the + frames, and for each frame, consider the local variables. */ + n_frames = VG_(get_StackTrace)( tid, ips, N_FRAMES, + sps, fps, 0/*first_ip_delta*/ ); + + /* As a result of KLUDGE above, starting the loop at j = 0 + duplicates examination of the top frame and so isn't necessary. + Oh well. */ + vg_assert(n_frames >= 0 && n_frames <= N_FRAMES); + for (j = 0; j < n_frames; j++) { + if (consider_vars_in_frame( dname1, dname2, n_dname, + data_addr, + ips[j], + sps[j], fps[j], tid, j )) { + dname1[n_dname-1] = dname2[n_dname-1] = 0; + return True; + } + /* Now, it appears that gcc sometimes appears to produce + location lists whose ranges don't actually cover the call + instruction, even though the address of the variable in + question is passed as a parameter in the call. AFAICS this + is simply a bug in gcc - how can the variable be claimed not + exist in memory (on the stack) for the duration of a call in + which its address is passed? But anyway, in the particular + case I investigated (memcheck/tests/varinfo6.c, call to croak + on line 2999, local var budget declared at line 3115 + appearing not to exist across the call to mainSort on line + 3143, "gcc.orig (GCC) 3.4.4 20050721 (Red Hat 3.4.4-2)" on + amd64), the variable's location list does claim it exists + starting at the first byte of the first instruction after the + call instruction. So, call consider_vars_in_frame a second + time, but this time add 1 to the IP. GDB handles this + example with no difficulty, which leads me to believe that + either (1) I misunderstood something, or (2) GDB has an + equivalent kludge. */ + if (j > 0 /* this is a non-innermost frame */ + && consider_vars_in_frame( dname1, dname2, n_dname, + data_addr, + ips[j] + 1, + sps[j], fps[j], tid, j )) { + dname1[n_dname-1] = dname2[n_dname-1] = 0; + return True; + } + } + + /* We didn't find anything useful. */ + dname1[n_dname-1] = dname2[n_dname-1] = 0; + return False; +# undef N_FRAMES +} + + +////////////////////////////////////////////////////////////////// +// // +// Support for other kinds of queries to the Dwarf3 var info // +// // +////////////////////////////////////////////////////////////////// + +/* Figure out if the variable 'var' has a location that is linearly + dependent on a stack pointer value, or a frame pointer value, and + if it is, add a description of it to 'blocks'. Otherwise ignore + it. If 'arrays_only' is True, also ignore it unless it has an + array type. */ + +static +void analyse_deps ( /*MOD*/XArray* /* of FrameBlock */ blocks, + XArray* /* TyEnt */ tyents, + Addr ip, const DebugInfo* di, DiVariable* var, + Bool arrays_only ) +{ + GXResult res_sp_6k, res_sp_7k, res_fp_6k, res_fp_7k; + RegSummary regs; + MaybeULong mul; + Bool isVec; + TyEnt* ty; + + Bool debug = False; + if (0&&debug) + VG_(printf)("adeps: var %s\n", var->name ); + + /* Figure out how big the variable is. */ + mul = ML_(sizeOfType)(tyents, var->typeR); + /* If this var has a type whose size is unknown, zero, or + impossibly large, it should never have been added. ML_(addVar) + should have rejected it. */ + vg_assert(mul.b == True); + vg_assert(mul.ul > 0); + if (sizeof(void*) == 4) vg_assert(mul.ul < (1ULL << 32)); + /* After this point, we assume we can truncate mul.ul to a host word + safely (without loss of info). */ + + /* skip if non-array and we're only interested in arrays */ + ty = ML_(TyEnts__index_by_cuOff)( tyents, NULL, var->typeR ); + vg_assert(ty); + vg_assert(ty->tag == Te_UNKNOWN || ML_(TyEnt__is_type)(ty)); + if (ty->tag == Te_UNKNOWN) + return; /* perhaps we should complain in this case? */ + isVec = ty->tag == Te_TyArray; + if (arrays_only && !isVec) + return; + + if (0) {ML_(pp_TyEnt_C_ishly)(tyents, var->typeR); + VG_(printf)(" %s\n", var->name);} + + /* Do some test evaluations of the variable's location expression, + in order to guess whether it is sp-relative, fp-relative, or + none. A crude hack, which can be interpreted roughly as finding + the first derivative of the location expression w.r.t. the + supplied frame and stack pointer values. */ + regs.fp = 0; + regs.ip = ip; + regs.sp = 6 * 1024; + res_sp_6k = ML_(evaluate_GX)( var->gexpr, var->fbGX, ®s, di ); + + regs.fp = 0; + regs.ip = ip; + regs.sp = 7 * 1024; + res_sp_7k = ML_(evaluate_GX)( var->gexpr, var->fbGX, ®s, di ); + + regs.fp = 6 * 1024; + regs.ip = ip; + regs.sp = 0; + res_fp_6k = ML_(evaluate_GX)( var->gexpr, var->fbGX, ®s, di ); + + regs.fp = 7 * 1024; + regs.ip = ip; + regs.sp = 0; + res_fp_7k = ML_(evaluate_GX)( var->gexpr, var->fbGX, ®s, di ); + + vg_assert(res_sp_6k.kind == res_sp_7k.kind); + vg_assert(res_sp_6k.kind == res_fp_6k.kind); + vg_assert(res_sp_6k.kind == res_fp_7k.kind); + + if (res_sp_6k.kind == GXR_Value) { + StackBlock block; + GXResult res; + UWord sp_delta = res_sp_7k.word - res_sp_6k.word; + UWord fp_delta = res_fp_7k.word - res_fp_6k.word; + tl_assert(sp_delta == 0 || sp_delta == 1024); + tl_assert(fp_delta == 0 || fp_delta == 1024); + + if (sp_delta == 0 && fp_delta == 0) { + /* depends neither on sp nor fp, so it can't be a stack + local. Ignore it. */ + } + else + if (sp_delta == 1024 && fp_delta == 0) { + regs.sp = regs.fp = 0; + regs.ip = ip; + res = ML_(evaluate_GX)( var->gexpr, var->fbGX, ®s, di ); + tl_assert(res.kind == GXR_Value); + if (debug) + VG_(printf)(" %5ld .. %5ld (sp) %s\n", + res.word, res.word + ((UWord)mul.ul) - 1, var->name); + block.base = res.word; + block.szB = (SizeT)mul.ul; + block.spRel = True; + block.isVec = isVec; + VG_(memset)( &block.name[0], 0, sizeof(block.name) ); + if (var->name) + VG_(strncpy)( &block.name[0], var->name, sizeof(block.name)-1 ); + block.name[ sizeof(block.name)-1 ] = 0; + VG_(addToXA)( blocks, &block ); + } + else + if (sp_delta == 0 && fp_delta == 1024) { + regs.sp = regs.fp = 0; + regs.ip = ip; + res = ML_(evaluate_GX)( var->gexpr, var->fbGX, ®s, di ); + tl_assert(res.kind == GXR_Value); + if (debug) + VG_(printf)(" %5ld .. %5ld (FP) %s\n", + res.word, res.word + ((UWord)mul.ul) - 1, var->name); + block.base = res.word; + block.szB = (SizeT)mul.ul; + block.spRel = False; + block.isVec = isVec; + VG_(memset)( &block.name[0], 0, sizeof(block.name) ); + if (var->name) + VG_(strncpy)( &block.name[0], var->name, sizeof(block.name)-1 ); + block.name[ sizeof(block.name)-1 ] = 0; + VG_(addToXA)( blocks, &block ); + } + else { + vg_assert(0); + } + } +} + + +/* Get an XArray of StackBlock which describe the stack (auto) blocks + for this ip. The caller is expected to free the XArray at some + point. If 'arrays_only' is True, only array-typed blocks are + returned; otherwise blocks of all types are returned. */ + +void* /* really, XArray* of StackBlock */ + VG_(di_get_stack_blocks_at_ip)( Addr ip, Bool arrays_only ) +{ + /* This is a derivation of consider_vars_in_frame() above. */ + Word i; + DebugInfo* di; + RegSummary regs; + Bool debug = False; + + XArray* res = VG_(newXA)( ML_(dinfo_zalloc), "di.debuginfo.dgsbai.1", + ML_(dinfo_free), + sizeof(StackBlock) ); + + static UInt n_search = 0; + static UInt n_steps = 0; + n_search++; + if (debug) + VG_(printf)("QQQQ: dgsbai: ip %#lx\n", ip); + /* first, find the DebugInfo that pertains to 'ip'. */ + for (di = debugInfo_list; di; di = di->next) { + n_steps++; + /* text segment missing? unlikely, but handle it .. */ + if (!di->text_present || di->text_size == 0) + continue; + /* Ok. So does this text mapping bracket the ip? */ + if (di->text_avma <= ip && ip < di->text_avma + di->text_size) + break; + } + + /* Didn't find it. Strange -- means ip is a code address outside + of any mapped text segment. Unlikely but not impossible -- app + could be generating code to run. */ + if (!di) + return res; /* currently empty */ + + if (0 && ((n_search & 0x1) == 0)) + VG_(printf)("VG_(di_get_stack_blocks_at_ip): %u searches, " + "%u DebugInfos looked at\n", + n_search, n_steps); + /* Start of performance-enhancing hack: once every ??? (chosen + hackily after profiling) successful searches, move the found + DebugInfo one step closer to the start of the list. This makes + future searches cheaper. */ + if ((n_search & 0xFFFF) == 0) { + /* Move si one step closer to the start of the list. */ + move_DebugInfo_one_step_forward( di ); + } + /* End of performance-enhancing hack. */ + + /* any var info at all? */ + if (!di->varinfo) + return res; /* currently empty */ + + /* Work through the scopes from most deeply nested outwards, + looking for code address ranges that bracket 'ip'. The + variables on each such address range found are in scope right + now. Don't descend to level zero as that is the global + scope. */ + regs.ip = ip; + regs.sp = 0; + regs.fp = 0; + + /* "for each scope, working outwards ..." */ + for (i = VG_(sizeXA)(di->varinfo) - 1; i >= 1; i--) { + XArray* vars; + Word j; + DiAddrRange* arange; + OSet* this_scope + = *(OSet**)VG_(indexXA)( di->varinfo, i ); + if (debug) + VG_(printf)("QQQQ: considering scope %ld\n", (Word)i); + if (!this_scope) + continue; + /* Find the set of variables in this scope that + bracket the program counter. */ + arange = VG_(OSetGen_LookupWithCmp)( + this_scope, &ip, + ML_(cmp_for_DiAddrRange_range) + ); + if (!arange) + continue; + /* stay sane */ + vg_assert(arange->aMin <= arange->aMax); + /* It must bracket the ip we asked for, else + ML_(cmp_for_DiAddrRange_range) is somehow broken. */ + vg_assert(arange->aMin <= ip && ip <= arange->aMax); + /* It must have an attached XArray of DiVariables. */ + vars = arange->vars; + vg_assert(vars); + /* But it mustn't cover the entire address range. We only + expect that to happen for the global scope (level 0), which + we're not looking at here. Except, it may cover the entire + address range, but in that case the vars array must be + empty. */ + vg_assert(! (arange->aMin == (Addr)0 + && arange->aMax == ~(Addr)0 + && VG_(sizeXA)(vars) > 0) ); + for (j = 0; j < VG_(sizeXA)( vars ); j++) { + DiVariable* var = (DiVariable*)VG_(indexXA)( vars, j ); + if (debug) + VG_(printf)("QQQQ: var:name=%s %#lx-%#lx %#lx\n", + var->name,arange->aMin,arange->aMax,ip); + analyse_deps( res, di->admin_tyents, ip, + di, var, arrays_only ); + } + } + + return res; +} + + +/* Get an array of GlobalBlock which describe the global blocks owned + by the shared object characterised by the given di_handle. Asserts + if the handle is invalid. The caller is responsible for freeing + the array at some point. If 'arrays_only' is True, only + array-typed blocks are returned; otherwise blocks of all types are + returned. */ + +void* /* really, XArray* of GlobalBlock */ + VG_(di_get_global_blocks_from_dihandle) ( ULong di_handle, + Bool arrays_only ) +{ + /* This is a derivation of consider_vars_in_frame() above. */ + + DebugInfo* di; + XArray* gvars; /* XArray* of GlobalBlock */ + Word nScopes, scopeIx; + + /* The first thing to do is find the DebugInfo that + pertains to 'di_handle'. */ + tl_assert(di_handle > 0); + for (di = debugInfo_list; di; di = di->next) { + if (di->handle == di_handle) + break; + } + + /* If this fails, we were unable to find any DebugInfo with the + given handle. This is considered an error on the part of the + caller. */ + tl_assert(di != NULL); + + /* we'll put the collected variables in here. */ + gvars = VG_(newXA)( ML_(dinfo_zalloc), "di.debuginfo.dggbfd.1", + ML_(dinfo_free), sizeof(GlobalBlock) ); + tl_assert(gvars); + + /* any var info at all? */ + if (!di->varinfo) + return gvars; + + /* we'll iterate over all the variables we can find, even if + it seems senseless to visit stack-allocated variables */ + /* Iterate over all scopes */ + nScopes = VG_(sizeXA)( di->varinfo ); + for (scopeIx = 0; scopeIx < nScopes; scopeIx++) { + + /* Iterate over each (code) address range at the current scope */ + DiAddrRange* range; + OSet* /* of DiAddrInfo */ scope + = *(OSet**)VG_(indexXA)( di->varinfo, scopeIx ); + tl_assert(scope); + VG_(OSetGen_ResetIter)(scope); + while ( (range = VG_(OSetGen_Next)(scope)) ) { + + /* Iterate over each variable in the current address range */ + Word nVars, varIx; + tl_assert(range->vars); + nVars = VG_(sizeXA)( range->vars ); + for (varIx = 0; varIx < nVars; varIx++) { + + Bool isVec; + GXResult res; + MaybeULong mul; + GlobalBlock gb; + TyEnt* ty; + DiVariable* var = VG_(indexXA)( range->vars, varIx ); + tl_assert(var->name); + if (0) VG_(printf)("at depth %ld var %s ", scopeIx, var->name ); + + /* Now figure out if this variable has a constant address + (that is, independent of FP, SP, phase of moon, etc), + and if so, what the address is. Any variable with a + constant address is deemed to be a global so we collect + it. */ + if (0) { VG_(printf)("EVAL: "); ML_(pp_GX)(var->gexpr); + VG_(printf)("\n"); } + res = ML_(evaluate_trivial_GX)( var->gexpr, di ); + + /* Not a constant address => not interesting */ + if (res.kind != GXR_Value) { + if (0) VG_(printf)("FAIL\n"); + continue; + } + + /* Ok, it's a constant address. See if we want to collect + it. */ + if (0) VG_(printf)("%#lx\n", res.word); + + /* Figure out how big the variable is. */ + mul = ML_(sizeOfType)(di->admin_tyents, var->typeR); + + /* If this var has a type whose size is unknown, zero, or + impossibly large, it should never have been added. + ML_(addVar) should have rejected it. */ + vg_assert(mul.b == True); + vg_assert(mul.ul > 0); + if (sizeof(void*) == 4) vg_assert(mul.ul < (1ULL << 32)); + /* After this point, we assume we can truncate mul.ul to a + host word safely (without loss of info). */ + + /* skip if non-array and we're only interested in + arrays */ + ty = ML_(TyEnts__index_by_cuOff)( di->admin_tyents, NULL, + var->typeR ); + vg_assert(ty); + vg_assert(ty->tag == Te_UNKNOWN || ML_(TyEnt__is_type)(ty)); + if (ty->tag == Te_UNKNOWN) + continue; /* perhaps we should complain in this case? */ + + isVec = ty->tag == Te_TyArray; + if (arrays_only && !isVec) continue; + + /* Ok, so collect it! */ + tl_assert(var->name); + tl_assert(di->soname); + if (0) VG_(printf)("XXXX %s %s %d\n", var->name, + var->fileName?(HChar*)var->fileName + :"??",var->lineNo); + VG_(memset)(&gb, 0, sizeof(gb)); + gb.addr = res.word; + gb.szB = (SizeT)mul.ul; + gb.isVec = isVec; + VG_(strncpy)(&gb.name[0], var->name, sizeof(gb.name)-1); + VG_(strncpy)(&gb.soname[0], di->soname, sizeof(gb.soname)-1); + tl_assert(gb.name[ sizeof(gb.name)-1 ] == 0); + tl_assert(gb.soname[ sizeof(gb.soname)-1 ] == 0); + + VG_(addToXA)( gvars, &gb ); + + } /* for (varIx = 0; varIx < nVars; varIx++) */ + + } /* while ( (range = VG_(OSetGen_Next)(scope)) ) */ + + } /* for (scopeIx = 0; scopeIx < nScopes; scopeIx++) */ + + return gvars; +} + + +/*------------------------------------------------------------*/ +/*--- DebugInfo accessor functions ---*/ +/*------------------------------------------------------------*/ + +const DebugInfo* VG_(next_seginfo)(const DebugInfo* di) +{ + if (di == NULL) + return debugInfo_list; + return di->next; +} + +Addr VG_(seginfo_get_text_avma)(const DebugInfo* di) +{ + return di->text_present ? di->text_avma : 0; +} + +SizeT VG_(seginfo_get_text_size)(const DebugInfo* di) +{ + return di->text_present ? di->text_size : 0; +} + +Addr VG_(seginfo_get_plt_avma)(const DebugInfo* di) +{ + return di->plt_present ? di->plt_avma : 0; +} + +SizeT VG_(seginfo_get_plt_size)(const DebugInfo* di) +{ + return di->plt_present ? di->plt_size : 0; +} + +Addr VG_(seginfo_get_gotplt_avma)(const DebugInfo* di) +{ + return di->gotplt_present ? di->gotplt_avma : 0; +} + +SizeT VG_(seginfo_get_gotplt_size)(const DebugInfo* di) +{ + return di->gotplt_present ? di->gotplt_size : 0; +} + +const UChar* VG_(seginfo_soname)(const DebugInfo* di) +{ + return di->soname; +} + +const UChar* VG_(seginfo_filename)(const DebugInfo* di) +{ + return di->filename; +} + +PtrdiffT VG_(seginfo_get_text_bias)(const DebugInfo* di) +{ + return di->text_present ? di->text_bias : 0; +} + +Int VG_(seginfo_syms_howmany) ( const DebugInfo *si ) +{ + return si->symtab_used; +} + +void VG_(seginfo_syms_getidx) ( const DebugInfo *si, + Int idx, + /*OUT*/Addr* avma, + /*OUT*/Addr* tocptr, + /*OUT*/UInt* size, + /*OUT*/HChar** name, + /*OUT*/Bool* isText ) +{ + vg_assert(idx >= 0 && idx < si->symtab_used); + if (avma) *avma = 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; + if (isText) *isText = si->symtab[idx].isText; +} + + +/*------------------------------------------------------------*/ +/*--- SectKind query functions ---*/ +/*------------------------------------------------------------*/ + +/* Convert a VgSectKind to a string, which must be copied if you want + to change it. */ +const HChar* VG_(pp_SectKind)( VgSectKind kind ) +{ + switch (kind) { + case Vg_SectUnknown: return "Unknown"; + case Vg_SectText: return "Text"; + case Vg_SectData: return "Data"; + case Vg_SectBSS: return "BSS"; + case Vg_SectGOT: return "GOT"; + case Vg_SectPLT: return "PLT"; + case Vg_SectOPD: return "OPD"; + case Vg_SectGOTPLT: return "GOTPLT"; + default: vg_assert(0); + } +} + +/* Given an address 'a', make a guess of which section of which object + it comes from. If name is non-NULL, then the last n_name-1 + characters of the object's name is put in name[0 .. n_name-2], and + name[n_name-1] is set to zero (guaranteed zero terminated). */ + +VgSectKind VG_(seginfo_sect_kind)( /*OUT*/UChar* name, SizeT n_name, + Addr a) +{ + DebugInfo* di; + VgSectKind res = Vg_SectUnknown; + + for (di = debugInfo_list; di != NULL; di = di->next) { + + if (0) + VG_(printf)( + "addr=%#lx di=%p %s got=%#lx,%ld plt=%#lx,%ld data=%#lx,%ld bss=%#lx,%ld\n", + a, di, di->filename, + di->got_avma, di->got_size, + di->plt_avma, di->plt_size, + di->data_avma, di->data_size, + di->bss_avma, di->bss_size); + + if (di->text_present + && di->text_size > 0 + && a >= di->text_avma && a < di->text_avma + di->text_size) { + res = Vg_SectText; + break; + } + if (di->data_present + && di->data_size > 0 + && a >= di->data_avma && a < di->data_avma + di->data_size) { + res = Vg_SectData; + break; + } + if (di->sdata_present + && di->sdata_size > 0 + && a >= di->sdata_avma && a < di->sdata_avma + di->sdata_size) { + res = Vg_SectData; + break; + } + if (di->bss_present + && di->bss_size > 0 + && a >= di->bss_avma && a < di->bss_avma + di->bss_size) { + res = Vg_SectBSS; + break; + } + if (di->sbss_present + && di->sbss_size > 0 + && a >= di->sbss_avma && a < di->sbss_avma + di->sbss_size) { + res = Vg_SectBSS; + break; + } + if (di->plt_present + && di->plt_size > 0 + && a >= di->plt_avma && a < di->plt_avma + di->plt_size) { + res = Vg_SectPLT; + break; + } + if (di->got_present + && di->got_size > 0 + && a >= di->got_avma && a < di->got_avma + di->got_size) { + res = Vg_SectGOT; + break; + } + if (di->gotplt_present + && di->gotplt_size > 0 + && a >= di->gotplt_avma && a < di->gotplt_avma + di->gotplt_size) { + res = Vg_SectGOTPLT; + break; + } + if (di->opd_present + && di->opd_size > 0 + && a >= di->opd_avma && a < di->opd_avma + di->opd_size) { + res = Vg_SectOPD; + break; + } + /* we could also check for .eh_frame, if anyone really cares */ + } + + vg_assert( (di == NULL && res == Vg_SectUnknown) + || (di != NULL && res != Vg_SectUnknown) ); + + if (name) { + + vg_assert(n_name >= 8); + + if (di && di->filename) { + Int i, j; + Int fnlen = VG_(strlen)(di->filename); + Int start_at = 1 + fnlen - n_name; + if (start_at < 0) start_at = 0; + vg_assert(start_at < fnlen); + i = start_at; j = 0; + while (True) { + vg_assert(j >= 0 && j < n_name); + vg_assert(i >= 0 && i <= fnlen); + name[j] = di->filename[i]; + if (di->filename[i] == 0) break; + i++; j++; + } + vg_assert(i == fnlen); + } else { + VG_(snprintf)(name, n_name, "%s", "???"); + } + + name[n_name-1] = 0; + } + + return res; + +} + + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ |