/*--------------------------------------------------------------------*/ /*--- Obtaining information about an address. ---*/ /*--- m_addrinfo.c ---*/ /*--------------------------------------------------------------------*/ /* This file is part of Valgrind, a dynamic binary instrumentation framework. Copyright (C) 2008-2013 OpenWorks Ltd info@open-works.co.uk 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. */ #include "pub_core_basics.h" #include "pub_core_libcassert.h" #include "pub_core_libcbase.h" #include "pub_core_libcprint.h" #include "pub_core_xarray.h" #include "pub_core_debuginfo.h" #include "pub_core_execontext.h" #include "pub_core_addrinfo.h" #include "pub_core_mallocfree.h" #include "pub_core_machine.h" #include "pub_core_options.h" void VG_(describe_addr) ( Addr a, /*OUT*/AddrInfo* ai ) { ThreadId tid; Addr stack_min, stack_max; VgSectKind sect; /* -- Perhaps the variable type/location data describes it? -- */ ai->Addr.Variable.descr1 = VG_(newXA)( VG_(malloc), "mc.da.descr1", VG_(free), sizeof(HChar) ); ai->Addr.Variable.descr2 = VG_(newXA)( VG_(malloc), "mc.da.descr2", VG_(free), sizeof(HChar) ); (void) VG_(get_data_description)( ai->Addr.Variable.descr1, ai->Addr.Variable.descr2, a ); /* If there's nothing in descr1/2, free them. Why is it safe to to VG_(indexXA) at zero here? Because VG_(get_data_description) guarantees to zero terminate descr1/2 regardless of the outcome of the call. So there's always at least one element in each XA after the call. */ if (0 == VG_(strlen)( VG_(indexXA)( ai->Addr.Variable.descr1, 0 ))) { VG_(deleteXA)( ai->Addr.Variable.descr1 ); ai->Addr.Variable.descr1 = NULL; } if (0 == VG_(strlen)( VG_(indexXA)( ai->Addr.Variable.descr2, 0 ))) { VG_(deleteXA)( ai->Addr.Variable.descr2 ); ai->Addr.Variable.descr2 = NULL; } /* Assume (assert) that VG_(get_data_description) fills in descr1 before it fills in descr2 */ if (ai->Addr.Variable.descr1 == NULL) vg_assert(ai->Addr.Variable.descr2 == NULL); /* So did we get lucky? */ if (ai->Addr.Variable.descr1 != NULL) { ai->tag = Addr_Variable; return; } /* -- Have a look at the low level data symbols - perhaps it's in there. -- */ VG_(memset)( &ai->Addr.DataSym.name, 0, sizeof(ai->Addr.DataSym.name)); if (VG_(get_datasym_and_offset)( a, &ai->Addr.DataSym.name[0], sizeof(ai->Addr.DataSym.name)-1, &ai->Addr.DataSym.offset )) { ai->tag = Addr_DataSym; vg_assert( ai->Addr.DataSym.name [ sizeof(ai->Addr.DataSym.name)-1 ] == 0); return; } /* -- Perhaps it's on a thread's stack? -- */ VG_(thread_stack_reset_iter)(&tid); while ( VG_(thread_stack_next)(&tid, &stack_min, &stack_max) ) { if (stack_min - VG_STACK_REDZONE_SZB <= a && a <= stack_max) { ai->tag = Addr_Stack; ai->Addr.Stack.tid = tid; return; } } /* -- Maybe it is in one of the m_mallocfree.c arenas. -- */ { AddrArenaInfo aai; VG_(describe_arena_addr) ( a, &aai ); if (aai.name != NULL) { ai->tag = Addr_Block; if (aai.aid == VG_AR_CLIENT) ai->Addr.Block.block_kind = aai.free ? Block_ClientArenaFree : Block_ClientArenaMallocd; else ai->Addr.Block.block_kind = aai.free ? Block_ValgrindArenaFree : Block_ValgrindArenaMallocd; ai->Addr.Block.block_desc = aai.name; ai->Addr.Block.block_szB = aai.block_szB; ai->Addr.Block.rwoffset = aai.rwoffset; ai->Addr.Block.allocated_at = VG_(null_ExeContext)(); ai->Addr.Block.freed_at = VG_(null_ExeContext)(); return; } } /* -- last ditch attempt at classification -- */ vg_assert( sizeof(ai->Addr.SectKind.objname) > 4 ); VG_(memset)( &ai->Addr.SectKind.objname, 0, sizeof(ai->Addr.SectKind.objname)); VG_(strcpy)( ai->Addr.SectKind.objname, "???" ); sect = VG_(DebugInfo_sect_kind)( &ai->Addr.SectKind.objname[0], sizeof(ai->Addr.SectKind.objname)-1, a); if (sect != Vg_SectUnknown) { ai->tag = Addr_SectKind; ai->Addr.SectKind.kind = sect; vg_assert( ai->Addr.SectKind.objname [ sizeof(ai->Addr.SectKind.objname)-1 ] == 0); return; } /* -- Clueless ... -- */ ai->tag = Addr_Unknown; return; } void VG_(clear_addrinfo) ( AddrInfo* ai) { switch (ai->tag) { case Addr_Unknown: break; case Addr_Stack: break; case Addr_Block: break; case Addr_DataSym: break; case Addr_Variable: if (ai->Addr.Variable.descr1 != NULL) { VG_(deleteXA)( ai->Addr.Variable.descr1 ); ai->Addr.Variable.descr1 = NULL; } if (ai->Addr.Variable.descr2 != NULL) { VG_(deleteXA)( ai->Addr.Variable.descr2 ); ai->Addr.Variable.descr2 = NULL; } break; case Addr_SectKind: break; default: VG_(core_panic)("VG_(clear_addrinfo)"); } ai->tag = Addr_Undescribed; } static Bool is_arena_BlockKind(BlockKind bk) { switch (bk) { case Block_Mallocd: case Block_Freed: case Block_MempoolChunk: case Block_UserG: return False; case Block_ClientArenaMallocd: case Block_ClientArenaFree: case Block_ValgrindArenaMallocd: case Block_ValgrindArenaFree: return True; default: vg_assert (0); } } static void pp_addrinfo_WRK ( Addr a, AddrInfo* ai, Bool mc, Bool maybe_gcc ) { const HChar* xpre = VG_(clo_xml) ? " <auxwhat>" : " "; const HChar* xpost = VG_(clo_xml) ? "</auxwhat>" : ""; vg_assert (!maybe_gcc || mc); // maybe_gcc can only be given in mc mode. switch (ai->tag) { case Addr_Unknown: if (maybe_gcc) { VG_(emit)( "%sAddress 0x%llx is just below the stack ptr. " "To suppress, use: --workaround-gcc296-bugs=yes%s\n", xpre, (ULong)a, xpost ); } else { VG_(emit)( "%sAddress 0x%llx " "is not stack'd, malloc'd or %s%s\n", xpre, (ULong)a, mc ? "(recently) free'd" : "on a free list", xpost ); } break; case Addr_Stack: VG_(emit)( "%sAddress 0x%llx is on thread %d's stack%s\n", xpre, (ULong)a, ai->Addr.Stack.tid, xpost ); break; case Addr_Block: { SizeT block_szB = ai->Addr.Block.block_szB; PtrdiffT rwoffset = ai->Addr.Block.rwoffset; SizeT delta; const HChar* relative; if (rwoffset < 0) { delta = (SizeT)(-rwoffset); relative = "before"; } else if (rwoffset >= block_szB) { delta = rwoffset - block_szB; relative = "after"; } else { delta = rwoffset; relative = "inside"; } if (is_arena_BlockKind (ai->Addr.Block.block_kind)) VG_(emit)( "%sAddress 0x%lx is %'lu bytes %s a%s block of size %'lu" " in arena \"%s\"%s\n", xpre, a, delta, relative, ai->Addr.Block.block_kind==Block_ClientArenaMallocd || ai->Addr.Block.block_kind==Block_ValgrindArenaMallocd ? "" : "n unallocated", block_szB, ai->Addr.Block.block_desc, // arena name xpost ); else VG_(emit)( "%sAddress 0x%lx is %'lu bytes %s a %s of size %'lu %s%s\n", xpre, a, delta, relative, ai->Addr.Block.block_desc, block_szB, ai->Addr.Block.block_kind==Block_Mallocd ? "alloc'd" : ai->Addr.Block.block_kind==Block_Freed ? "free'd" : "client-defined", xpost ); if (ai->Addr.Block.block_kind==Block_Mallocd) { VG_(pp_ExeContext)(ai->Addr.Block.allocated_at); tl_assert (ai->Addr.Block.freed_at == VG_(null_ExeContext)()); } else if (ai->Addr.Block.block_kind==Block_Freed) { VG_(pp_ExeContext)(ai->Addr.Block.freed_at); if (ai->Addr.Block.allocated_at != VG_(null_ExeContext)()) { VG_(emit)( "%s block was alloc'd at%s\n", xpre, xpost ); VG_(pp_ExeContext)(ai->Addr.Block.allocated_at); } } else if (ai->Addr.Block.block_kind==Block_MempoolChunk || ai->Addr.Block.block_kind==Block_UserG) { // client-defined VG_(pp_ExeContext)(ai->Addr.Block.allocated_at); tl_assert (ai->Addr.Block.freed_at == VG_(null_ExeContext)()); /* Nb: cannot have a freed_at, as a freed client-defined block has a Block_Freed block_kind. */ } else { // Client or Valgrind arena. At least currently, we never // have stacktraces for these. tl_assert (ai->Addr.Block.allocated_at == VG_(null_ExeContext)()); tl_assert (ai->Addr.Block.freed_at == VG_(null_ExeContext)()); } break; } case Addr_DataSym: VG_(emit)( "%sAddress 0x%llx is %llu bytes " "inside data symbol \"%pS\"%s\n", xpre, (ULong)a, (ULong)ai->Addr.DataSym.offset, ai->Addr.DataSym.name, xpost ); break; case Addr_Variable: /* Note, no need for XML tags here, because descr1/2 will already have <auxwhat> or <xauxwhat>s on them, in XML mode. */ if (ai->Addr.Variable.descr1) VG_(emit)( "%s%s\n", VG_(clo_xml) ? " " : " ", (HChar*)VG_(indexXA)(ai->Addr.Variable.descr1, 0) ); if (ai->Addr.Variable.descr2) VG_(emit)( "%s%s\n", VG_(clo_xml) ? " " : " ", (HChar*)VG_(indexXA)(ai->Addr.Variable.descr2, 0) ); break; case Addr_SectKind: VG_(emit)( "%sAddress 0x%llx is in the %pS segment of %pS%s\n", xpre, (ULong)a, VG_(pp_SectKind)(ai->Addr.SectKind.kind), ai->Addr.SectKind.objname, xpost ); break; default: VG_(tool_panic)("mc_pp_AddrInfo"); } } void VG_(pp_addrinfo) ( Addr a, AddrInfo* ai ) { pp_addrinfo_WRK (a, ai, False /*mc*/, False /*maybe_gcc*/); } void VG_(pp_addrinfo_mc) ( Addr a, AddrInfo* ai, Bool maybe_gcc ) { pp_addrinfo_WRK (a, ai, True /*mc*/, maybe_gcc); } /*--------------------------------------------------------------------*/ /*--- end m_addrinfo.c ---*/ /*--------------------------------------------------------------------*/