2 /*--------------------------------------------------------------------*/
3 /*--- Ptrcheck: a pointer-use checker. ---*/
4 /*--- Provides stuff shared between sg_ and h_ subtools. ---*/
5 /*--- pc_common.c ---*/
6 /*--------------------------------------------------------------------*/
9 This file is part of Ptrcheck, a Valgrind tool for checking pointer
12 Copyright (C) 2008-2010 OpenWorks Ltd
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU General Public License as
17 published by the Free Software Foundation; either version 2 of the
18 License, or (at your option) any later version.
20 This program is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 General Public License for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
30 The GNU General Public License is contained in the file COPYING.
32 Neither the names of the U.S. Department of Energy nor the
33 University of California nor the names of its contributors may be
34 used to endorse or promote products derived from this software
35 without prior written permission.
38 #include "pub_tool_basics.h"
39 #include "pub_tool_libcbase.h"
40 #include "pub_tool_libcprint.h"
41 #include "pub_tool_xarray.h"
42 #include "pub_tool_mallocfree.h"
43 #include "pub_tool_libcassert.h"
44 #include "pub_tool_options.h"
45 #include "pub_tool_replacemalloc.h"
46 #include "pub_tool_execontext.h"
47 #include "pub_tool_tooliface.h" // CorePart
48 #include "pub_tool_threadstate.h" // VG_(get_running_tid)
49 #include "pub_tool_debuginfo.h"
51 #include "pc_common.h" // self, & Seg
53 #include "h_main.h" // NONPTR, BOTTOM, UNKNOWN
56 //////////////////////////////////////////////////////////////
58 // Command line options //
60 //////////////////////////////////////////////////////////////
62 Bool h_clo_partial_loads_ok = True; /* user visible */
63 /* Bool h_clo_lossage_check = False; */ /* dev flag only */
64 Bool sg_clo_enable_sg_checks = True; /* user visible */
66 Bool pc_process_cmd_line_options(Char* arg)
68 if VG_BOOL_CLO(arg, "--partial-loads-ok", h_clo_partial_loads_ok) {}
69 /* else if VG_BOOL_CLO(arg, "--lossage-check", h_clo_lossage_check) {} */
70 else if VG_BOOL_CLO(arg, "--enable-sg-checks", sg_clo_enable_sg_checks) {}
72 return VG_(replacement_malloc_process_cmd_line_option)(arg);
77 void pc_print_usage(void)
80 " --partial-loads-ok=no|yes same as for Memcheck [yes]\n"
81 " --enable-sg-checks=no|yes enable stack & global array checking? [yes]\n"
85 void pc_print_debug_usage(void)
89 //" --lossage-check=no|yes gather stats for quality control [no]\n"
95 //////////////////////////////////////////////////////////////
97 // Error management -- storage //
99 //////////////////////////////////////////////////////////////
101 /* What kind of error it is. */
104 XE_SorG=1202, // sg: stack or global array inconsistency
105 XE_Heap, // h: mismatched ptr/addr segments on load/store
106 XE_Arith, // h: bad arithmetic between two segment pointers
107 XE_SysParam // h: block straddling >1 segment passed to syscall
126 SSizeT sszB; /* -ve is write, +ve is read */
129 HChar delta[32]; // text showing relation to expected
133 SSizeT sszB; /* -ve is write, +ve is read */
135 XArray* descr1; /* XArray* of HChar */
136 XArray* descr2; /* XArray* of HChar */
143 const HChar* opname; // user-understandable text name
157 void sg_record_error_SorG ( ThreadId tid,
158 Addr addr, SSizeT sszB,
159 HChar* expect, HChar* actual, HChar* delta )
162 VG_(memset)(&xe, 0, sizeof(xe));
164 xe.XE.SorG.addr = addr;
165 xe.XE.SorG.sszB = sszB;
166 VG_(strncpy)( &xe.XE.SorG.expect[0],
167 expect, sizeof(xe.XE.SorG.expect) );
168 VG_(strncpy)( &xe.XE.SorG.actual[0],
169 actual, sizeof(xe.XE.SorG.actual) );
170 VG_(strncpy)( &xe.XE.SorG.delta[0],
171 delta, sizeof(xe.XE.SorG.delta) );
172 xe.XE.SorG.expect[ sizeof(xe.XE.SorG.expect)-1 ] = 0;
173 xe.XE.SorG.actual[ sizeof(xe.XE.SorG.actual)-1 ] = 0;
174 xe.XE.SorG.delta[ sizeof(xe.XE.SorG.delta)-1 ] = 0;
175 VG_(maybe_record_error)( tid, XE_SorG, 0, NULL, &xe );
178 void h_record_heap_error( Addr a, SizeT size, Seg* vseg, Bool is_write )
182 VG_(memset)(&xe, 0, sizeof(xe));
185 xe.XE.Heap.sszB = is_write ? -size : size;
186 xe.XE.Heap.vseg = vseg;
187 VG_(maybe_record_error)( VG_(get_running_tid)(), XE_Heap,
188 /*a*/0, /*str*/NULL, /*extra*/(void*)&xe);
191 void h_record_arith_error( Seg* seg1, Seg* seg2, HChar* opname )
194 VG_(memset)(&xe, 0, sizeof(xe));
196 xe.XE.Arith.seg1 = seg1;
197 xe.XE.Arith.seg2 = seg2;
198 xe.XE.Arith.opname = opname;
199 VG_(maybe_record_error)( VG_(get_running_tid)(), XE_Arith,
200 /*a*/0, /*str*/NULL, /*extra*/(void*)&xe);
203 void h_record_sysparam_error( ThreadId tid, CorePart part, Char* s,
204 Addr lo, Addr hi, Seg* seglo, Seg* seghi )
207 VG_(memset)(&xe, 0, sizeof(xe));
208 xe.tag = XE_SysParam;
209 xe.XE.SysParam.part = part;
210 xe.XE.SysParam.lo = lo;
211 xe.XE.SysParam.hi = hi;
212 xe.XE.SysParam.seglo = seglo;
213 xe.XE.SysParam.seghi = seghi;
214 VG_(maybe_record_error)( tid, XE_SysParam, /*a*/(Addr)0, /*str*/s,
215 /*extra*/(void*)&xe);
219 Bool pc_eq_Error ( VgRes res, Error* e1, Error* e2 )
222 tl_assert(VG_(get_error_kind)(e1) == VG_(get_error_kind)(e2));
223 //tl_assert(VG_(get_error_string)(e1) == NULL);
224 //tl_assert(VG_(get_error_string)(e2) == NULL);
226 xe1 = (XError*)VG_(get_error_extra)(e1);
227 xe2 = (XError*)VG_(get_error_extra)(e2);
231 if (xe1->tag != xe2->tag)
236 return //xe1->XE.SorG.addr == xe2->XE.SorG.addr
238 xe1->XE.SorG.sszB == xe2->XE.SorG.sszB
239 && 0 == VG_(strncmp)( &xe1->XE.SorG.expect[0],
240 &xe2->XE.SorG.expect[0],
241 sizeof(xe1->XE.SorG.expect) )
242 && 0 == VG_(strncmp)( &xe1->XE.SorG.actual[0],
243 &xe2->XE.SorG.actual[0],
244 sizeof(xe1->XE.SorG.actual) );
250 VG_(tool_panic)("eq_Error: unrecognised error kind");
255 //////////////////////////////////////////////////////////////
257 // Error management -- printing //
259 //////////////////////////////////////////////////////////////
261 /* This is the "this error is due to be printed shortly; so have a
262 look at it any print any preamble you want" function. Which, in
263 Ptrcheck, we don't use. Hence a no-op.
265 void pc_before_pp_Error ( Error* err ) {
268 /* Do a printf-style operation on either the XML or normal output
269 channel, depending on the setting of VG_(clo_xml).
271 static void emit_WRK ( HChar* format, va_list vargs )
274 VG_(vprintf_xml)(format, vargs);
276 VG_(vmessage)(Vg_UserMsg, format, vargs);
279 static void emit ( HChar* format, ... ) PRINTF_CHECK(1, 2);
280 static void emit ( HChar* format, ... )
283 va_start(vargs, format);
284 emit_WRK(format, vargs);
287 static void emiN ( HChar* format, ... ) /* With NO FORMAT CHECK */
290 va_start(vargs, format);
291 emit_WRK(format, vargs);
296 static Char* readwrite(SSizeT sszB)
298 return ( sszB < 0 ? "write" : "read" );
301 static Word Word__abs ( Word w ) {
302 return w < 0 ? -w : w;
305 void pc_pp_Error ( Error* err )
307 const Bool xml = VG_(clo_xml); /* a shorthand, that's all */
309 XError *xe = (XError*)VG_(get_error_extra)(err);
312 switch (VG_(get_error_kind)(err)) {
314 //----------------------------------------------------------
319 emit( " <kind>SorG</kind>\n");
320 emit( " <what>Invalid %s of size %ld</what>\n",
321 xe->XE.SorG.sszB < 0 ? "write" : "read",
322 Word__abs(xe->XE.SorG.sszB) );
323 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
325 emit( " <auxwhat>Address %#lx expected vs actual:</auxwhat>\n",
327 emiN( " <auxwhat>Expected: %t</auxwhat>\n",
328 &xe->XE.SorG.expect[0] );
329 emiN( " <auxwhat>Actual: %t</auxwhat>\n",
330 &xe->XE.SorG.actual[0] );
334 emit( "Invalid %s of size %ld\n",
335 xe->XE.SorG.sszB < 0 ? "write" : "read",
336 Word__abs(xe->XE.SorG.sszB) );
337 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
339 emit( " Address %#lx expected vs actual:\n", xe->XE.SorG.addr );
340 emit( " Expected: %s\n", &xe->XE.SorG.expect[0] );
341 emit( " Actual: %s\n", &xe->XE.SorG.actual[0] );
342 if (xe->XE.SorG.delta[0] != 0)
343 emit(" Actual: is %s Expected\n", &xe->XE.SorG.delta[0]);
347 //----------------------------------------------------------
349 Char *place, *legit, *how_invalid;
350 Addr a = xe->XE.Heap.addr;
351 Seg* vseg = xe->XE.Heap.vseg;
353 tl_assert(is_known_segment(vseg) || NONPTR == vseg);
355 if (NONPTR == vseg) {
356 // Access via a non-pointer
360 emit( " <kind>Heap</kind>\n");
361 emit( " <what>Invalid %s of size %ld</what>\n",
362 readwrite(xe->XE.Heap.sszB),
363 Word__abs(xe->XE.Heap.sszB) );
364 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
366 emit( " <auxwhat>Address %#lx is not derived from "
367 "any known block</auxwhat>\n", a );
371 emit( "Invalid %s of size %ld\n",
372 readwrite(xe->XE.Heap.sszB),
373 Word__abs(xe->XE.Heap.sszB) );
374 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
376 emit( " Address %#lx is not derived from "
377 "any known block\n", a );
382 // Access via a pointer, but outside its range.
385 Seg__cmp(vseg, a, &cmp, &miss_size);
386 if (cmp < 0) place = "before";
387 else if (cmp == 0) place = "inside";
388 else place = "after";
389 how_invalid = ( ( Seg__is_freed(vseg) && 0 != cmp )
390 ? "Doubly-invalid" : "Invalid" );
391 legit = ( Seg__is_freed(vseg) ? "once-" : "" );
395 emit( " <kind>Heap</kind>\n");
396 emit( " <what>%s %s of size %ld</what>\n",
398 readwrite(xe->XE.Heap.sszB),
399 Word__abs(xe->XE.Heap.sszB) );
400 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
402 emit( " <auxwhat>Address %#lx is %lu bytes %s "
403 "the accessing pointer's</auxwhat>\n",
404 a, miss_size, place );
405 emit( " <auxwhat>%slegitimate range, "
406 "a block of size %lu %s</auxwhat>\n",
407 legit, Seg__size(vseg),
408 Seg__is_freed(vseg) ? "free'd" : "alloc'd" );
409 VG_(pp_ExeContext)(Seg__where(vseg));
413 emit( "%s %s of size %ld\n",
415 readwrite(xe->XE.Heap.sszB),
416 Word__abs(xe->XE.Heap.sszB) );
417 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
419 emit( " Address %#lx is %lu bytes %s the accessing pointer's\n",
420 a, miss_size, place );
421 emit( " %slegitimate range, a block of size %lu %s\n",
422 legit, Seg__size(vseg),
423 Seg__is_freed(vseg) ? "free'd" : "alloc'd" );
424 VG_(pp_ExeContext)(Seg__where(vseg));
429 /* If we have a better description of the address, show it.
430 Note that in XML mode, it will already by nicely wrapped up
431 in tags, either <auxwhat> or <xauxwhat>, so we can just emit
435 if (xe->XE.Heap.descr1)
437 (HChar*)VG_(indexXA)( xe->XE.Heap.descr1, 0 ) );
438 if (xe->XE.Heap.descr2)
440 (HChar*)VG_(indexXA)( xe->XE.Heap.descr2, 0 ) );
441 if (xe->XE.Heap.datasym[0] != 0)
442 emiN( " <auxwhat>Address 0x%llx is %llu bytes "
443 "inside data symbol \"%t\"</auxwhat>\n",
444 (ULong)xe->XE.Heap.addr,
445 (ULong)xe->XE.Heap.datasymoff,
446 xe->XE.Heap.datasym );
450 if (xe->XE.Heap.descr1)
452 (HChar*)VG_(indexXA)( xe->XE.Heap.descr1, 0 ) );
453 if (xe->XE.Heap.descr2)
455 (HChar*)VG_(indexXA)( xe->XE.Heap.descr2, 0 ) );
456 if (xe->XE.Heap.datasym[0] != 0)
457 emit( " Address 0x%llx is %llu bytes "
458 "inside data symbol \"%s\"\n",
459 (ULong)xe->XE.Heap.addr,
460 (ULong)xe->XE.Heap.datasymoff,
461 xe->XE.Heap.datasym );
467 //----------------------------------------------------------
469 Seg* seg1 = xe->XE.Arith.seg1;
470 Seg* seg2 = xe->XE.Arith.seg2;
473 tl_assert(BOTTOM != seg1);
474 tl_assert(BOTTOM != seg2 && UNKNOWN != seg2);
478 emit( " <kind>Arith</kind>\n");
479 emit( " <what>Invalid arguments to %s</what>\n",
480 xe->XE.Arith.opname );
481 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
484 if (NONPTR == seg1) {
485 emit( " <auxwhat>First arg not a pointer</auxwhat>\n" );
486 } else if (UNKNOWN == seg1) {
487 emit( " <auxwhat>First arg may be a pointer</auxwhat>\n" );
489 emit( " <auxwhat>First arg derived from address %#lx of "
490 "%lu-byte block alloc'd</auxwhat>\n",
491 Seg__addr(seg1), Seg__size(seg1) );
492 VG_(pp_ExeContext)(Seg__where(seg1));
494 which = "Second arg";
498 if (NONPTR == seg2) {
499 emit( " <auxwhat>%s not a pointer</auxwhat>\n", which );
501 emit( " <auxwhat>%s derived from address %#lx of "
502 "%lu-byte block alloc'd</auxwhat>\n",
503 which, Seg__addr(seg2), Seg__size(seg2) );
504 VG_(pp_ExeContext)(Seg__where(seg2));
509 emit( "Invalid arguments to %s\n",
510 xe->XE.Arith.opname );
511 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
514 if (NONPTR == seg1) {
515 emit( " First arg not a pointer\n" );
516 } else if (UNKNOWN == seg1) {
517 emit( " First arg may be a pointer\n" );
519 emit( " First arg derived from address %#lx of "
520 "%lu-byte block alloc'd\n",
521 Seg__addr(seg1), Seg__size(seg1) );
522 VG_(pp_ExeContext)(Seg__where(seg1));
524 which = "Second arg";
528 if (NONPTR == seg2) {
529 emit( " %s not a pointer\n", which );
531 emit( " %s derived from address %#lx of "
532 "%lu-byte block alloc'd\n",
533 which, Seg__addr(seg2), Seg__size(seg2) );
534 VG_(pp_ExeContext)(Seg__where(seg2));
542 //----------------------------------------------------------
544 Addr lo = xe->XE.SysParam.lo;
545 Addr hi = xe->XE.SysParam.hi;
546 Seg* seglo = xe->XE.SysParam.seglo;
547 Seg* seghi = xe->XE.SysParam.seghi;
548 Char* s = VG_(get_error_string) (err);
551 tl_assert(BOTTOM != seglo && BOTTOM != seghi);
553 if (Vg_CoreSysCall == xe->XE.SysParam.part)
554 what = "Syscall param ";
555 else VG_(tool_panic)("bad CorePart");
557 if (seglo == seghi) {
559 tl_assert(is_known_segment(seglo));
560 tl_assert(Seg__is_freed(seglo)); // XXX what if it's now recycled?
564 emit( " <kind>SysParam</kind>\n");
565 emit( " <what>%s%s contains unaddressable byte(s)</what>\n",
567 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
569 emit( " <auxwhat>Address %#lx is %ld bytes inside a "
570 "%ld-byte block free'd</auxwhat>\n",
571 lo, lo-Seg__addr(seglo), Seg__size(seglo) );
572 VG_(pp_ExeContext)(Seg__where(seglo));
576 emit( " %s%s contains unaddressable byte(s)\n",
578 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
580 emit( " Address %#lx is %ld bytes inside a "
581 "%ld-byte block free'd\n",
582 lo, lo-Seg__addr(seglo), Seg__size(seglo) );
583 VG_(pp_ExeContext)(Seg__where(seglo));
592 emit( " <kind>SysParam</kind>\n");
593 emit( " <what>%s%s is non-contiguous</what>\n",
595 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
597 if (UNKNOWN == seglo) {
598 emit( " <auxwhat>First byte is "
599 "not inside a known block</auxwhat>\n" );
601 emit( " <auxwhat>First byte (%#lx) is %ld bytes inside a "
602 "%ld-byte block alloc'd</auxwhat>\n",
603 lo, lo-Seg__addr(seglo), Seg__size(seglo) );
604 VG_(pp_ExeContext)(Seg__where(seglo));
607 if (UNKNOWN == seghi) {
608 emit( " <auxwhat>Last byte is "
609 "not inside a known block</auxwhat>\n" );
611 emit( " <auxwhat>Last byte (%#lx) is %ld bytes inside a "
612 "%ld-byte block alloc'd</auxwhat>\n",
613 hi, hi-Seg__addr(seghi), Seg__size(seghi) );
614 VG_(pp_ExeContext)(Seg__where(seghi));
619 emit( "%s%s is non-contiguous\n",
621 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
623 if (UNKNOWN == seglo) {
624 emit( " First byte is not inside a known block\n" );
626 emit( " First byte (%#lx) is %ld bytes inside a "
627 "%ld-byte block alloc'd\n",
628 lo, lo-Seg__addr(seglo), Seg__size(seglo) );
629 VG_(pp_ExeContext)(Seg__where(seglo));
632 if (UNKNOWN == seghi) {
633 emit( " Last byte is not inside a known block\n" );
635 emit( " Last byte (%#lx) is %ld bytes inside a "
636 "%ld-byte block alloc'd\n",
637 hi, hi-Seg__addr(seghi), Seg__size(seghi) );
638 VG_(pp_ExeContext)(Seg__where(seghi));
648 VG_(tool_panic)("pp_Error: unrecognised error kind");
653 UInt pc_update_Error_extra ( Error* err )
655 XError *xe = (XError*)VG_(get_error_extra)(err);
663 tl_assert(sizeof(xe->XE.Heap.datasym) > 0);
664 xe->XE.Heap.datasymoff = 0;
665 xe->XE.Heap.datasym[0] = 0;
667 tl_assert(!xe->XE.Heap.descr1);
668 tl_assert(!xe->XE.Heap.descr2);
671 = VG_(newXA)( VG_(malloc), "pc.update_extra.Heap.descr1",
672 VG_(free), sizeof(HChar) );
674 = VG_(newXA)( VG_(malloc), "pc.update_extra.Heap.descr1",
675 VG_(free), sizeof(HChar) );
677 VG_(memset)(&xe->XE.Heap.datasym, 0, sizeof(xe->XE.Heap.datasym));
678 xe->XE.Heap.datasymoff = 0;
681 = VG_(get_data_description)( xe->XE.Heap.descr1,
685 /* If there's nothing in descr1/2, free it. Why is it safe to
686 to VG_(indexXA) at zero here? Because
687 VG_(get_data_description) guarantees to zero terminate
688 descr1/2 regardless of the outcome of the call. So there's
689 always at least one element in each XA after the call.
691 if (0 == VG_(strlen)( VG_(indexXA)( xe->XE.Heap.descr1, 0 ))
693 VG_(deleteXA)( xe->XE.Heap.descr1 );
694 xe->XE.Heap.descr1 = NULL;
696 if (0 == VG_(strlen)( VG_(indexXA)( xe->XE.Heap.descr2, 0 ))
698 VG_(deleteXA)( xe->XE.Heap.descr2 );
699 xe->XE.Heap.descr2 = NULL;
702 /* If Dwarf3 info produced nothing useful, see at least if
703 we can fish something useful out of the ELF symbol info. */
705 if (VG_(get_datasym_and_offset)(
706 xe->XE.Heap.addr, &xe->XE.Heap.datasym[0],
707 sizeof(xe->XE.Heap.datasym)-1,
708 &xe->XE.Heap.datasymoff )
710 tl_assert(xe->XE.Heap.datasym[sizeof(xe->XE.Heap.datasym)-1]
721 VG_(tool_panic)("update_extra");
723 return sizeof(XError);
726 Bool pc_is_recognised_suppression ( Char* name, Supp *su )
730 if (VG_STREQ(name, "SorG")) skind = XS_SorG;
731 else if (VG_STREQ(name, "Heap")) skind = XS_Heap;
732 else if (VG_STREQ(name, "Arith")) skind = XS_Arith;
733 else if (VG_STREQ(name, "SysParam")) skind = XS_SysParam;
737 VG_(set_supp_kind)(su, skind);
741 Bool pc_read_extra_suppression_info ( Int fd, Char** bufpp,
742 SizeT* nBufp, Supp* su )
745 if (VG_(get_supp_kind)(su) == XS_SysParam) {
746 eof = VG_(get_line) ( fd, bufpp, nBufp, NULL );
747 if (eof) return False;
748 VG_(set_supp_string)(su, VG_(strdup)("pc.common.presi.1", *bufpp));
753 Bool pc_error_matches_suppression (Error* err, Supp* su)
755 ErrorKind ekind = VG_(get_error_kind)(err);
756 switch (VG_(get_supp_kind)(su)) {
757 case XS_SorG: return ekind == XE_SorG;
758 case XS_Heap: return ekind == XE_Heap;
759 case XS_Arith: return ekind == XE_Arith;
760 case XS_SysParam: return ekind == XE_SysParam;
762 VG_(printf)("Error:\n"
763 " unknown suppression type %d\n",
764 VG_(get_supp_kind)(su));
765 VG_(tool_panic)("unknown suppression type in "
766 "pc_error_matches_suppression");
770 Char* pc_get_error_name ( Error* err )
772 XError *xe = (XError*)VG_(get_error_extra)(err);
775 case XE_SorG: return "SorG";
776 case XE_Heap: return "Heap";
777 case XE_Arith: return "Arith";
778 case XE_SysParam: return "SysParam";
779 default: VG_(tool_panic)("get_error_name: unexpected type");
783 Bool pc_get_extra_suppression_info ( Error* err,
784 /*OUT*/Char* buf, Int nBuf )
786 ErrorKind ekind = VG_(get_error_kind )(err);
788 tl_assert(nBuf >= 16); // stay sane
789 if (XE_SysParam == ekind) {
790 Char* errstr = VG_(get_error_string)(err);
792 VG_(snprintf)(buf, nBuf-1, "%s", errstr);
800 /*--------------------------------------------------------------------*/
801 /*--- end pc_common.c ---*/
802 /*--------------------------------------------------------------------*/