You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
811 lines
26 KiB
811 lines
26 KiB
|
|
/*--------------------------------------------------------------------*/ |
|
/*--- Ptrcheck: a pointer-use checker. ---*/ |
|
/*--- Provides stuff shared between sg_ and h_ subtools. ---*/ |
|
/*--- pc_common.c ---*/ |
|
/*--------------------------------------------------------------------*/ |
|
|
|
/* |
|
This file is part of Ptrcheck, a Valgrind tool for checking pointer |
|
use in programs. |
|
|
|
Copyright (C) 2008-2017 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. |
|
|
|
Neither the names of the U.S. Department of Energy nor the |
|
University of California nor the names of its contributors may be |
|
used to endorse or promote products derived from this software |
|
without prior written permission. |
|
*/ |
|
|
|
#include "pub_tool_basics.h" |
|
#include "pub_tool_libcbase.h" |
|
#include "pub_tool_libcprint.h" |
|
#include "pub_tool_xarray.h" |
|
#include "pub_tool_mallocfree.h" |
|
#include "pub_tool_libcassert.h" |
|
#include "pub_tool_options.h" |
|
#include "pub_tool_replacemalloc.h" |
|
#include "pub_tool_execontext.h" |
|
#include "pub_tool_tooliface.h" // CorePart |
|
#include "pub_tool_threadstate.h" // VG_(get_running_tid) |
|
#include "pub_tool_debuginfo.h" |
|
|
|
#include "pc_common.h" // self, & Seg |
|
|
|
#include "h_main.h" // NONPTR, BOTTOM, UNKNOWN |
|
|
|
|
|
////////////////////////////////////////////////////////////// |
|
// // |
|
// Command line options // |
|
// // |
|
////////////////////////////////////////////////////////////// |
|
|
|
Bool h_clo_partial_loads_ok = True; /* user visible */ |
|
/* Bool h_clo_lossage_check = False; */ /* dev flag only */ |
|
Bool sg_clo_enable_sg_checks = True; /* user visible */ |
|
|
|
Bool pc_process_cmd_line_options(const HChar* arg) |
|
{ |
|
if VG_BOOL_CLO(arg, "--partial-loads-ok", h_clo_partial_loads_ok) {} |
|
/* else if VG_BOOL_CLO(arg, "--lossage-check", h_clo_lossage_check) {} */ |
|
else if VG_BOOL_CLO(arg, "--enable-sg-checks", sg_clo_enable_sg_checks) {} |
|
else |
|
return VG_(replacement_malloc_process_cmd_line_option)(arg); |
|
|
|
return True; |
|
} |
|
|
|
void pc_print_usage(void) |
|
{ |
|
VG_(printf)( |
|
" --partial-loads-ok=no|yes same as for Memcheck [yes]\n" |
|
" --enable-sg-checks=no|yes enable stack & global array checking? [yes]\n" |
|
); |
|
} |
|
|
|
void pc_print_debug_usage(void) |
|
{ |
|
VG_(printf)( |
|
" (none)\n" |
|
//" --lossage-check=no|yes gather stats for quality control [no]\n" |
|
); |
|
} |
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////// |
|
// // |
|
// Error management -- storage // |
|
// // |
|
////////////////////////////////////////////////////////////// |
|
|
|
/* What kind of error it is. */ |
|
typedef |
|
enum { |
|
XE_SorG=1202, // sg: stack or global array inconsistency |
|
XE_Heap, // h: mismatched ptr/addr segments on load/store |
|
XE_Arith, // h: bad arithmetic between two segment pointers |
|
XE_SysParam // h: block straddling >1 segment passed to syscall |
|
} |
|
XErrorTag; |
|
|
|
typedef |
|
enum { |
|
XS_SorG=2021, |
|
XS_Heap, |
|
XS_Arith, |
|
XS_SysParam |
|
} |
|
XSuppTag; |
|
|
|
typedef |
|
struct { |
|
XErrorTag tag; |
|
union { |
|
struct { |
|
Addr addr; |
|
SSizeT sszB; /* -ve is write, +ve is read */ |
|
HChar expect[128]; |
|
HChar actual[128]; |
|
HChar delta[32]; // text showing relation to expected |
|
} SorG; |
|
struct { |
|
Addr addr; |
|
SSizeT sszB; /* -ve is write, +ve is read */ |
|
Seg* vseg; |
|
XArray* descr1; /* XArray* of HChar */ |
|
XArray* descr2; /* XArray* of HChar */ |
|
const HChar* datasym; |
|
PtrdiffT datasymoff; |
|
} Heap; |
|
struct { |
|
Seg* seg1; |
|
Seg* seg2; |
|
const HChar* opname; // user-understandable text name |
|
} Arith; |
|
struct { |
|
CorePart part; |
|
Addr lo; |
|
Addr hi; |
|
Seg* seglo; |
|
Seg* seghi; |
|
} SysParam; |
|
} XE; |
|
} |
|
XError; |
|
|
|
|
|
void sg_record_error_SorG ( ThreadId tid, |
|
Addr addr, SSizeT sszB, |
|
HChar* expect, HChar* actual, HChar* delta ) |
|
{ |
|
XError xe; |
|
VG_(memset)(&xe, 0, sizeof(xe)); |
|
xe.tag = XE_SorG; |
|
xe.XE.SorG.addr = addr; |
|
xe.XE.SorG.sszB = sszB; |
|
VG_(strncpy)( &xe.XE.SorG.expect[0], |
|
expect, sizeof(xe.XE.SorG.expect) ); |
|
VG_(strncpy)( &xe.XE.SorG.actual[0], |
|
actual, sizeof(xe.XE.SorG.actual) ); |
|
VG_(strncpy)( &xe.XE.SorG.delta[0], |
|
delta, sizeof(xe.XE.SorG.delta) ); |
|
xe.XE.SorG.expect[ sizeof(xe.XE.SorG.expect)-1 ] = 0; |
|
xe.XE.SorG.actual[ sizeof(xe.XE.SorG.actual)-1 ] = 0; |
|
xe.XE.SorG.delta[ sizeof(xe.XE.SorG.delta)-1 ] = 0; |
|
VG_(maybe_record_error)( tid, XE_SorG, 0, NULL, &xe ); |
|
} |
|
|
|
void h_record_heap_error( Addr a, SizeT size, Seg* vseg, Bool is_write ) |
|
{ |
|
XError xe; |
|
tl_assert(size > 0); |
|
VG_(memset)(&xe, 0, sizeof(xe)); |
|
xe.tag = XE_Heap; |
|
xe.XE.Heap.addr = a; |
|
xe.XE.Heap.sszB = is_write ? -size : size; |
|
xe.XE.Heap.vseg = vseg; |
|
VG_(maybe_record_error)( VG_(get_running_tid)(), XE_Heap, |
|
/*a*/0, /*str*/NULL, /*extra*/(void*)&xe); |
|
} |
|
|
|
void h_record_arith_error( Seg* seg1, Seg* seg2, HChar* opname ) |
|
{ |
|
XError xe; |
|
VG_(memset)(&xe, 0, sizeof(xe)); |
|
xe.tag = XE_Arith; |
|
xe.XE.Arith.seg1 = seg1; |
|
xe.XE.Arith.seg2 = seg2; |
|
xe.XE.Arith.opname = opname; |
|
VG_(maybe_record_error)( VG_(get_running_tid)(), XE_Arith, |
|
/*a*/0, /*str*/NULL, /*extra*/(void*)&xe); |
|
} |
|
|
|
void h_record_sysparam_error( ThreadId tid, CorePart part, const HChar* s, |
|
Addr lo, Addr hi, Seg* seglo, Seg* seghi ) |
|
{ |
|
XError xe; |
|
VG_(memset)(&xe, 0, sizeof(xe)); |
|
xe.tag = XE_SysParam; |
|
xe.XE.SysParam.part = part; |
|
xe.XE.SysParam.lo = lo; |
|
xe.XE.SysParam.hi = hi; |
|
xe.XE.SysParam.seglo = seglo; |
|
xe.XE.SysParam.seghi = seghi; |
|
VG_(maybe_record_error)( tid, XE_SysParam, /*a*/(Addr)0, /*str*/s, |
|
/*extra*/(void*)&xe); |
|
} |
|
|
|
|
|
Bool pc_eq_Error ( VgRes res, const Error* e1, const Error* e2 ) |
|
{ |
|
XError *xe1, *xe2; |
|
tl_assert(VG_(get_error_kind)(e1) == VG_(get_error_kind)(e2)); |
|
//tl_assert(VG_(get_error_string)(e1) == NULL); |
|
//tl_assert(VG_(get_error_string)(e2) == NULL); |
|
|
|
xe1 = (XError*)VG_(get_error_extra)(e1); |
|
xe2 = (XError*)VG_(get_error_extra)(e2); |
|
tl_assert(xe1); |
|
tl_assert(xe2); |
|
|
|
if (xe1->tag != xe2->tag) |
|
return False; |
|
|
|
switch (xe1->tag) { |
|
case XE_SorG: |
|
return //xe1->XE.SorG.addr == xe2->XE.SorG.addr |
|
//&& |
|
xe1->XE.SorG.sszB == xe2->XE.SorG.sszB |
|
&& 0 == VG_(strncmp)( &xe1->XE.SorG.expect[0], |
|
&xe2->XE.SorG.expect[0], |
|
sizeof(xe1->XE.SorG.expect) ) |
|
&& 0 == VG_(strncmp)( &xe1->XE.SorG.actual[0], |
|
&xe2->XE.SorG.actual[0], |
|
sizeof(xe1->XE.SorG.actual) ); |
|
case XE_Heap: |
|
case XE_Arith: |
|
case XE_SysParam: |
|
return True; |
|
default: |
|
VG_(tool_panic)("eq_Error: unrecognised error kind"); |
|
} |
|
} |
|
|
|
|
|
////////////////////////////////////////////////////////////// |
|
// // |
|
// Error management -- printing // |
|
// // |
|
////////////////////////////////////////////////////////////// |
|
|
|
/* This is the "this error is due to be printed shortly; so have a |
|
look at it any print any preamble you want" function. Which, in |
|
Ptrcheck, we don't use. Hence a no-op. |
|
*/ |
|
void pc_before_pp_Error ( const Error* err ) { |
|
} |
|
|
|
/* Do a printf-style operation on either the XML or normal output |
|
channel, depending on the setting of VG_(clo_xml). |
|
*/ |
|
static void emit_WRK ( const HChar* format, va_list vargs ) |
|
{ |
|
if (VG_(clo_xml)) { |
|
VG_(vprintf_xml)(format, vargs); |
|
} else { |
|
VG_(vmessage)(Vg_UserMsg, format, vargs); |
|
} |
|
} |
|
static void emit ( const HChar* format, ... ) PRINTF_CHECK(1, 2); |
|
static void emit ( const HChar* format, ... ) |
|
{ |
|
va_list vargs; |
|
va_start(vargs, format); |
|
emit_WRK(format, vargs); |
|
va_end(vargs); |
|
} |
|
static void emiN ( const HChar* format, ... ) /* With NO FORMAT CHECK */ |
|
{ |
|
va_list vargs; |
|
va_start(vargs, format); |
|
emit_WRK(format, vargs); |
|
va_end(vargs); |
|
} |
|
|
|
|
|
static const HChar* readwrite(SSizeT sszB) |
|
{ |
|
return ( sszB < 0 ? "write" : "read" ); |
|
} |
|
|
|
static Word Word__abs ( Word w ) { |
|
return w < 0 ? -w : w; |
|
} |
|
|
|
void pc_pp_Error ( const Error* err ) |
|
{ |
|
const Bool xml = VG_(clo_xml); /* a shorthand, that's all */ |
|
|
|
XError *xe = (XError*)VG_(get_error_extra)(err); |
|
tl_assert(xe); |
|
|
|
if (xml) |
|
emit( " <kind>%s</kind>\n", pc_get_error_name(err)); |
|
|
|
switch (VG_(get_error_kind)(err)) { |
|
|
|
//---------------------------------------------------------- |
|
case XE_SorG: |
|
|
|
if (xml) { |
|
|
|
emit( " <what>Invalid %s of size %ld</what>\n", |
|
xe->XE.SorG.sszB < 0 ? "write" : "read", |
|
Word__abs(xe->XE.SorG.sszB) ); |
|
VG_(pp_ExeContext)( VG_(get_error_where)(err) ); |
|
|
|
emit( " <auxwhat>Address %#lx expected vs actual:</auxwhat>\n", |
|
xe->XE.SorG.addr ); |
|
emiN( " <auxwhat>Expected: %pS</auxwhat>\n", |
|
&xe->XE.SorG.expect[0] ); |
|
emiN( " <auxwhat>Actual: %pS</auxwhat>\n", |
|
&xe->XE.SorG.actual[0] ); |
|
|
|
} else { |
|
|
|
emit( "Invalid %s of size %ld\n", |
|
xe->XE.SorG.sszB < 0 ? "write" : "read", |
|
Word__abs(xe->XE.SorG.sszB) ); |
|
VG_(pp_ExeContext)( VG_(get_error_where)(err) ); |
|
|
|
emit( " Address %#lx expected vs actual:\n", xe->XE.SorG.addr ); |
|
emit( " Expected: %s\n", &xe->XE.SorG.expect[0] ); |
|
emit( " Actual: %s\n", &xe->XE.SorG.actual[0] ); |
|
if (xe->XE.SorG.delta[0] != 0) |
|
emit(" Actual: is %s Expected\n", &xe->XE.SorG.delta[0]); |
|
} |
|
break; |
|
|
|
//---------------------------------------------------------- |
|
case XE_Heap: { |
|
const HChar *place, *legit, *how_invalid; |
|
Addr a = xe->XE.Heap.addr; |
|
Seg* vseg = xe->XE.Heap.vseg; |
|
|
|
tl_assert(is_known_segment(vseg) || NONPTR == vseg); |
|
|
|
if (NONPTR == vseg) { |
|
// Access via a non-pointer |
|
|
|
if (xml) { |
|
|
|
emit( " <what>Invalid %s of size %ld</what>\n", |
|
readwrite(xe->XE.Heap.sszB), |
|
Word__abs(xe->XE.Heap.sszB) ); |
|
VG_(pp_ExeContext)( VG_(get_error_where)(err) ); |
|
|
|
emit( " <auxwhat>Address %#lx is not derived from " |
|
"any known block</auxwhat>\n", a ); |
|
|
|
} else { |
|
|
|
emit( "Invalid %s of size %ld\n", |
|
readwrite(xe->XE.Heap.sszB), |
|
Word__abs(xe->XE.Heap.sszB) ); |
|
VG_(pp_ExeContext)( VG_(get_error_where)(err) ); |
|
|
|
emit( " Address %#lx is not derived from " |
|
"any known block\n", a ); |
|
|
|
} |
|
|
|
} else { |
|
// Access via a pointer, but outside its range. |
|
Int cmp; |
|
UWord miss_size; |
|
Seg__cmp(vseg, a, &cmp, &miss_size); |
|
if (cmp < 0) place = "before"; |
|
else if (cmp == 0) place = "inside"; |
|
else place = "after"; |
|
how_invalid = ( ( Seg__is_freed(vseg) && 0 != cmp ) |
|
? "Doubly-invalid" : "Invalid" ); |
|
legit = ( Seg__is_freed(vseg) ? "once-" : "" ); |
|
|
|
if (xml) { |
|
|
|
emit( " <what>%s %s of size %ld</what>\n", |
|
how_invalid, |
|
readwrite(xe->XE.Heap.sszB), |
|
Word__abs(xe->XE.Heap.sszB) ); |
|
VG_(pp_ExeContext)( VG_(get_error_where)(err) ); |
|
|
|
emit( " <auxwhat>Address %#lx is %lu bytes %s " |
|
"the accessing pointer's</auxwhat>\n", |
|
a, miss_size, place ); |
|
emit( " <auxwhat>%slegitimate range, " |
|
"a block of size %lu %s</auxwhat>\n", |
|
legit, Seg__size(vseg), |
|
Seg__is_freed(vseg) ? "free'd" : "alloc'd" ); |
|
VG_(pp_ExeContext)(Seg__where(vseg)); |
|
|
|
} else { |
|
|
|
emit( "%s %s of size %ld\n", |
|
how_invalid, |
|
readwrite(xe->XE.Heap.sszB), |
|
Word__abs(xe->XE.Heap.sszB) ); |
|
VG_(pp_ExeContext)( VG_(get_error_where)(err) ); |
|
|
|
emit( " Address %#lx is %lu bytes %s the accessing pointer's\n", |
|
a, miss_size, place ); |
|
emit( " %slegitimate range, a block of size %lu %s\n", |
|
legit, Seg__size(vseg), |
|
Seg__is_freed(vseg) ? "free'd" : "alloc'd" ); |
|
VG_(pp_ExeContext)(Seg__where(vseg)); |
|
|
|
} |
|
} |
|
|
|
/* If we have a better description of the address, show it. |
|
Note that in XML mode, it will already by nicely wrapped up |
|
in tags, either <auxwhat> or <xauxwhat>, so we can just emit |
|
it verbatim. */ |
|
if (xml) { |
|
|
|
if (xe->XE.Heap.descr1) |
|
emiN( " %pS\n", |
|
(HChar*)VG_(indexXA)( xe->XE.Heap.descr1, 0 ) ); |
|
if (xe->XE.Heap.descr2) |
|
emiN( " %pS\n", |
|
(HChar*)VG_(indexXA)( xe->XE.Heap.descr2, 0 ) ); |
|
if (xe->XE.Heap.datasym[0] != 0) |
|
emiN( " <auxwhat>Address 0x%llx is %llu bytes " |
|
"inside data symbol \"%pS\"</auxwhat>\n", |
|
(ULong)xe->XE.Heap.addr, |
|
(ULong)xe->XE.Heap.datasymoff, |
|
xe->XE.Heap.datasym ); |
|
|
|
} else { |
|
|
|
if (xe->XE.Heap.descr1) |
|
emit( " %s\n", |
|
(HChar*)VG_(indexXA)( xe->XE.Heap.descr1, 0 ) ); |
|
if (xe->XE.Heap.descr2) |
|
emit( " %s\n", |
|
(HChar*)VG_(indexXA)( xe->XE.Heap.descr2, 0 ) ); |
|
if (xe->XE.Heap.datasym[0] != 0) |
|
emit( " Address 0x%llx is %llu bytes " |
|
"inside data symbol \"%s\"\n", |
|
(ULong)xe->XE.Heap.addr, |
|
(ULong)xe->XE.Heap.datasymoff, |
|
xe->XE.Heap.datasym ); |
|
|
|
} |
|
break; |
|
} |
|
|
|
//---------------------------------------------------------- |
|
case XE_Arith: { |
|
Seg* seg1 = xe->XE.Arith.seg1; |
|
Seg* seg2 = xe->XE.Arith.seg2; |
|
const HChar* which; |
|
|
|
tl_assert(BOTTOM != seg1); |
|
tl_assert(BOTTOM != seg2 && UNKNOWN != seg2); |
|
|
|
if (xml) { |
|
|
|
emit( " <what>Invalid arguments to %s</what>\n", |
|
xe->XE.Arith.opname ); |
|
VG_(pp_ExeContext)( VG_(get_error_where)(err) ); |
|
|
|
if (seg1 != seg2) { |
|
if (NONPTR == seg1) { |
|
emit( " <auxwhat>First arg not a pointer</auxwhat>\n" ); |
|
} else if (UNKNOWN == seg1) { |
|
emit( " <auxwhat>First arg may be a pointer</auxwhat>\n" ); |
|
} else { |
|
emit( " <auxwhat>First arg derived from address %#lx of " |
|
"%lu-byte block alloc'd</auxwhat>\n", |
|
Seg__addr(seg1), Seg__size(seg1) ); |
|
VG_(pp_ExeContext)(Seg__where(seg1)); |
|
} |
|
which = "Second arg"; |
|
} else { |
|
which = "Both args"; |
|
} |
|
if (NONPTR == seg2) { |
|
emit( " <auxwhat>%s not a pointer</auxwhat>\n", which ); |
|
} else { |
|
emit( " <auxwhat>%s derived from address %#lx of " |
|
"%lu-byte block alloc'd</auxwhat>\n", |
|
which, Seg__addr(seg2), Seg__size(seg2) ); |
|
VG_(pp_ExeContext)(Seg__where(seg2)); |
|
} |
|
|
|
} else { |
|
|
|
emit( "Invalid arguments to %s\n", |
|
xe->XE.Arith.opname ); |
|
VG_(pp_ExeContext)( VG_(get_error_where)(err) ); |
|
|
|
if (seg1 != seg2) { |
|
if (NONPTR == seg1) { |
|
emit( " First arg not a pointer\n" ); |
|
} else if (UNKNOWN == seg1) { |
|
emit( " First arg may be a pointer\n" ); |
|
} else { |
|
emit( " First arg derived from address %#lx of " |
|
"%lu-byte block alloc'd\n", |
|
Seg__addr(seg1), Seg__size(seg1) ); |
|
VG_(pp_ExeContext)(Seg__where(seg1)); |
|
} |
|
which = "Second arg"; |
|
} else { |
|
which = "Both args"; |
|
} |
|
if (NONPTR == seg2) { |
|
emit( " %s not a pointer\n", which ); |
|
} else { |
|
emit( " %s derived from address %#lx of " |
|
"%lu-byte block alloc'd\n", |
|
which, Seg__addr(seg2), Seg__size(seg2) ); |
|
VG_(pp_ExeContext)(Seg__where(seg2)); |
|
} |
|
|
|
} |
|
|
|
break; |
|
} |
|
|
|
//---------------------------------------------------------- |
|
case XE_SysParam: { |
|
Addr lo = xe->XE.SysParam.lo; |
|
Addr hi = xe->XE.SysParam.hi; |
|
Seg* seglo = xe->XE.SysParam.seglo; |
|
Seg* seghi = xe->XE.SysParam.seghi; |
|
const HChar* s = VG_(get_error_string) (err); |
|
const HChar* what; |
|
|
|
tl_assert(BOTTOM != seglo && BOTTOM != seghi); |
|
|
|
if (Vg_CoreSysCall == xe->XE.SysParam.part) |
|
what = "Syscall param "; |
|
else VG_(tool_panic)("bad CorePart"); |
|
|
|
if (seglo == seghi) { |
|
// freed block |
|
tl_assert(is_known_segment(seglo)); |
|
tl_assert(Seg__is_freed(seglo)); // XXX what if it's now recycled? |
|
|
|
if (xml) { |
|
|
|
emit( " <what>%s%s contains unaddressable byte(s)</what>\n", |
|
what, s ); |
|
VG_(pp_ExeContext)( VG_(get_error_where)(err) ); |
|
|
|
emit( " <auxwhat>Address %#lx is %lu bytes inside a " |
|
"%lu-byte block free'd</auxwhat>\n", |
|
lo, lo-Seg__addr(seglo), Seg__size(seglo) ); |
|
VG_(pp_ExeContext)(Seg__where(seglo)); |
|
|
|
} else { |
|
|
|
emit( " %s%s contains unaddressable byte(s)\n", |
|
what, s ); |
|
VG_(pp_ExeContext)( VG_(get_error_where)(err) ); |
|
|
|
emit( " Address %#lx is %lu bytes inside a " |
|
"%lu-byte block free'd\n", |
|
lo, lo-Seg__addr(seglo), Seg__size(seglo) ); |
|
VG_(pp_ExeContext)(Seg__where(seglo)); |
|
|
|
} |
|
|
|
} else { |
|
// mismatch |
|
|
|
if (xml) { |
|
|
|
emit( " <what>%s%s is non-contiguous</what>\n", |
|
what, s ); |
|
VG_(pp_ExeContext)( VG_(get_error_where)(err) ); |
|
|
|
if (UNKNOWN == seglo) { |
|
emit( " <auxwhat>First byte is " |
|
"not inside a known block</auxwhat>\n" ); |
|
} else { |
|
emit( " <auxwhat>First byte (%#lx) is %lu bytes inside a " |
|
"%lu-byte block alloc'd</auxwhat>\n", |
|
lo, lo-Seg__addr(seglo), Seg__size(seglo) ); |
|
VG_(pp_ExeContext)(Seg__where(seglo)); |
|
} |
|
|
|
if (UNKNOWN == seghi) { |
|
emit( " <auxwhat>Last byte is " |
|
"not inside a known block</auxwhat>\n" ); |
|
} else { |
|
emit( " <auxwhat>Last byte (%#lx) is %lu bytes inside a " |
|
"%lu-byte block alloc'd</auxwhat>\n", |
|
hi, hi-Seg__addr(seghi), Seg__size(seghi) ); |
|
VG_(pp_ExeContext)(Seg__where(seghi)); |
|
} |
|
|
|
} else { |
|
|
|
emit( "%s%s is non-contiguous\n", |
|
what, s ); |
|
VG_(pp_ExeContext)( VG_(get_error_where)(err) ); |
|
|
|
if (UNKNOWN == seglo) { |
|
emit( " First byte is not inside a known block\n" ); |
|
} else { |
|
emit( " First byte (%#lx) is %lu bytes inside a " |
|
"%lu-byte block alloc'd\n", |
|
lo, lo-Seg__addr(seglo), Seg__size(seglo) ); |
|
VG_(pp_ExeContext)(Seg__where(seglo)); |
|
} |
|
|
|
if (UNKNOWN == seghi) { |
|
emit( " Last byte is not inside a known block\n" ); |
|
} else { |
|
emit( " Last byte (%#lx) is %lu bytes inside a " |
|
"%lu-byte block alloc'd\n", |
|
hi, hi-Seg__addr(seghi), Seg__size(seghi) ); |
|
VG_(pp_ExeContext)(Seg__where(seghi)); |
|
} |
|
|
|
} |
|
|
|
} |
|
break; |
|
} |
|
|
|
default: |
|
VG_(tool_panic)("pp_Error: unrecognised error kind"); |
|
} |
|
} |
|
|
|
|
|
UInt pc_update_Error_extra ( const Error* err ) |
|
{ |
|
XError *xe = (XError*)VG_(get_error_extra)(err); |
|
tl_assert(xe); |
|
switch (xe->tag) { |
|
case XE_SorG: |
|
break; |
|
case XE_Heap: { |
|
Bool have_descr; |
|
|
|
xe->XE.Heap.datasymoff = 0; |
|
xe->XE.Heap.datasym = NULL; |
|
|
|
tl_assert(!xe->XE.Heap.descr1); |
|
tl_assert(!xe->XE.Heap.descr2); |
|
|
|
xe->XE.Heap.descr1 |
|
= VG_(newXA)( VG_(malloc), "pc.update_extra.Heap.descr1", |
|
VG_(free), sizeof(HChar) ); |
|
xe->XE.Heap.descr2 |
|
= VG_(newXA)( VG_(malloc), "pc.update_extra.Heap.descr1", |
|
VG_(free), sizeof(HChar) ); |
|
|
|
xe->XE.Heap.datasymoff = 0; |
|
|
|
have_descr |
|
= VG_(get_data_description)( xe->XE.Heap.descr1, |
|
xe->XE.Heap.descr2, |
|
xe->XE.Heap.addr ); |
|
|
|
/* If there's nothing in descr1/2, free it. 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)( xe->XE.Heap.descr1, 0 )) |
|
|| !have_descr) { |
|
VG_(deleteXA)( xe->XE.Heap.descr1 ); |
|
xe->XE.Heap.descr1 = NULL; |
|
} |
|
if (0 == VG_(strlen)( VG_(indexXA)( xe->XE.Heap.descr2, 0 )) |
|
|| !have_descr) { |
|
VG_(deleteXA)( xe->XE.Heap.descr2 ); |
|
xe->XE.Heap.descr2 = NULL; |
|
} |
|
|
|
/* If Dwarf3 info produced nothing useful, see at least if |
|
we can fish something useful out of the ELF symbol info. */ |
|
if (!have_descr) { |
|
const HChar *name; |
|
if (VG_(get_datasym_and_offset)( |
|
xe->XE.Heap.addr, &name, |
|
&xe->XE.Heap.datasymoff ) |
|
) { |
|
xe->XE.Heap.datasym = |
|
VG_(strdup)("pc.update_extra.Heap.datasym", name); |
|
} |
|
} |
|
break; |
|
} |
|
case XE_Arith: |
|
break; |
|
case XE_SysParam: |
|
break; |
|
default: |
|
VG_(tool_panic)("update_extra"); |
|
} |
|
return sizeof(XError); |
|
} |
|
|
|
Bool pc_is_recognised_suppression ( const HChar* name, Supp *su ) |
|
{ |
|
SuppKind skind; |
|
|
|
if (VG_STREQ(name, "SorG")) skind = XS_SorG; |
|
else if (VG_STREQ(name, "Heap")) skind = XS_Heap; |
|
else if (VG_STREQ(name, "Arith")) skind = XS_Arith; |
|
else if (VG_STREQ(name, "SysParam")) skind = XS_SysParam; |
|
else |
|
return False; |
|
|
|
VG_(set_supp_kind)(su, skind); |
|
return True; |
|
} |
|
|
|
Bool pc_read_extra_suppression_info ( Int fd, HChar** bufpp, |
|
SizeT* nBufp, Int* lineno, |
|
Supp* su ) |
|
{ |
|
Bool eof; |
|
if (VG_(get_supp_kind)(su) == XS_SysParam) { |
|
eof = VG_(get_line) ( fd, bufpp, nBufp, lineno ); |
|
if (eof) return False; |
|
VG_(set_supp_string)(su, VG_(strdup)("pc.common.presi.1", *bufpp)); |
|
} |
|
return True; |
|
} |
|
|
|
Bool pc_error_matches_suppression (const Error* err, const Supp* su) |
|
{ |
|
ErrorKind ekind = VG_(get_error_kind)(err); |
|
switch (VG_(get_supp_kind)(su)) { |
|
case XS_SorG: return ekind == XE_SorG; |
|
case XS_Heap: return ekind == XE_Heap; |
|
case XS_Arith: return ekind == XE_Arith; |
|
case XS_SysParam: return ekind == XE_SysParam; |
|
default: |
|
VG_(printf)("Error:\n" |
|
" unknown suppression type %d\n", |
|
VG_(get_supp_kind)(su)); |
|
VG_(tool_panic)("unknown suppression type in " |
|
"pc_error_matches_suppression"); |
|
} |
|
} |
|
|
|
const HChar* pc_get_error_name ( const Error* err ) |
|
{ |
|
XError *xe = (XError*)VG_(get_error_extra)(err); |
|
tl_assert(xe); |
|
switch (xe->tag) { |
|
case XE_SorG: return "SorG"; |
|
case XE_Heap: return "Heap"; |
|
case XE_Arith: return "Arith"; |
|
case XE_SysParam: return "SysParam"; |
|
default: VG_(tool_panic)("get_error_name: unexpected type"); |
|
} |
|
} |
|
|
|
SizeT pc_get_extra_suppression_info ( const Error* err, |
|
/*OUT*/HChar* buf, Int nBuf ) |
|
{ |
|
ErrorKind ekind = VG_(get_error_kind)(err); |
|
tl_assert(buf); |
|
tl_assert(nBuf >= 1); |
|
|
|
if (XE_SysParam == ekind) { |
|
const HChar* errstr = VG_(get_error_string)(err); |
|
tl_assert(errstr); |
|
return VG_(snprintf)(buf, nBuf, "%s", errstr); |
|
} else { |
|
buf[0] = '\0'; |
|
return 0; |
|
} |
|
} |
|
|
|
SizeT pc_print_extra_suppression_use ( const Supp* su, |
|
/*OUT*/HChar* buf, Int nBuf ) |
|
{ |
|
tl_assert(nBuf >= 1); |
|
buf[0] = '\0'; |
|
return 0; |
|
} |
|
|
|
void pc_update_extra_suppression_use (const Error* err, const Supp* su) |
|
{ |
|
return; |
|
} |
|
|
|
/*--------------------------------------------------------------------*/ |
|
/*--- end pc_common.c ---*/ |
|
/*--------------------------------------------------------------------*/
|
|
|