Blob Blame History Raw
commit b5dde4a2ff59cc87390a33d85c7bf0ad6443cb6c
Author: iraisr <iraisr@a5019735-40e9-0310-863c-91ae7b9d1cf9>
Date:   Wed Mar 30 17:53:03 2016 +0000

    Run __gnu_cxx::__freeres() cleanup function available
    from libstdc++ when available, similar to existing __libc_freeres().
    New option --run-cxx-freeres=<yes|no> can be used to change whether
    this cleanup function is called or not.
    
    Note that __gnu_cxx::__freeres() is currently available
    only in gcc 6. It is not yet decided what to do about
    libstdc++ from gcc 5.
    Tracked under https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69945
    for libstdc++.
    
    Fixes BZ#345307 (partially).
    
    
    git-svn-id: svn://svn.valgrind.org/valgrind/trunk@15840 a5019735-40e9-0310-863c-91ae7b9d1cf9

diff --git a/coregrind/m_clientstate.c b/coregrind/m_clientstate.c
index 7cbc7c7..296d658 100644
--- a/coregrind/m_clientstate.c
+++ b/coregrind/m_clientstate.c
@@ -106,9 +106,9 @@ HChar* VG_(name_of_launcher) = NULL;
 Int VG_(fd_soft_limit) = -1;
 Int VG_(fd_hard_limit) = -1;
 
-/* Useful addresses extracted from the client */
-/* Where is the __libc_freeres_wrapper routine we made? */
-Addr VG_(client___libc_freeres_wrapper) = 0;
+/* Useful addresses extracted from the client. */
+/* Where is the freeres_wrapper routine we made? */
+Addr VG_(client_freeres_wrapper) = 0;
 
 /* x86-linux only: where is glibc's _dl_sysinfo_int80 function?
    Finding it isn't essential, but knowing where it is does sometimes
diff --git a/coregrind/m_main.c b/coregrind/m_main.c
index 54997bd..8cfaf82 100644
--- a/coregrind/m_main.c
+++ b/coregrind/m_main.c
@@ -36,6 +36,7 @@
 #include "pub_core_clientstate.h"
 #include "pub_core_aspacemgr.h"
 #include "pub_core_aspacehl.h"
+#include "pub_core_clreq.h"
 #include "pub_core_commandline.h"
 #include "pub_core_debuglog.h"
 #include "pub_core_errormgr.h"
@@ -176,6 +177,8 @@ static void usage_NORETURN ( Bool debug_help )
 "    --vgdb-shadow-registers=no|yes   let gdb see the shadow registers [no]\n"
 "    --vgdb-prefix=<prefix>    prefix for vgdb FIFOs [%s]\n"
 "    --run-libc-freeres=no|yes free up glibc memory at exit on Linux? [yes]\n"
+"    --run-cxx-freeres=no|yes  free up libstdc++ memory at exit on Linux\n"
+"                              and Solaris? [yes]\n"
 "    --sim-hints=hint1,hint2,...  activate unusual sim behaviours [none] \n"
 "         where hint is one of:\n"
 "           lax-ioctls lax-doors fuse-compatible enable-outer\n"
@@ -644,6 +647,7 @@ void main_process_cmd_line_options ( /*OUT*/Bool* logging_to_fd,
       else if VG_BOOL_CLO(arg, "--show-emwarns",   VG_(clo_show_emwarns)) {}
 
       else if VG_BOOL_CLO(arg, "--run-libc-freeres", VG_(clo_run_libc_freeres)) {}
+      else if VG_BOOL_CLO(arg, "--run-cxx-freeres",  VG_(clo_run_cxx_freeres)) {}
       else if VG_BOOL_CLO(arg, "--show-below-main",  VG_(clo_show_below_main)) {}
       else if VG_BOOL_CLO(arg, "--time-stamp",       VG_(clo_time_stamp)) {}
       else if VG_BOOL_CLO(arg, "--track-fds",        VG_(clo_track_fds)) {}
@@ -2560,8 +2564,8 @@ Int valgrind_main ( Int argc, HChar **argv, HChar **envp )
    So don't. 
 
    The final_tidyup call makes a bit of a nonsense of the ExitProcess
-   case, since it will run the libc_freeres function, thus allowing
-   other lurking threads to run again.  Hmm. */
+   case, since it will run __gnu_cxx::__freeres and libc_freeres functions,
+   thus allowing other lurking threads to run again.  Hmm. */
 
 static 
 void shutdown_actions_NORETURN( ThreadId tid, 
@@ -2584,8 +2588,8 @@ void shutdown_actions_NORETURN( ThreadId tid,
       // jrs: Huh?  but they surely are already gone
       VG_(reap_threads)(tid);
 
-      // Clean the client up before the final report
-      // this causes the libc_freeres function to run
+      // Clean the client up before the final report.
+      // This causes __gnu_cxx::__freeres and libc_freeres functions to run.
       final_tidyup(tid);
 
       /* be paranoid */
@@ -2600,9 +2604,9 @@ void shutdown_actions_NORETURN( ThreadId tid,
       // that none of the other threads ever run again.
       vg_assert( VG_(count_living_threads)() >= 1 );
 
-      // Clean the client up before the final report
-      // this causes the libc_freeres function to run
-      // perhaps this is unsafe, as per comment above
+      // Clean the client up before the final report.
+      // This causes __gnu_cxx::__freeres and libc_freeres functions to run.
+      // Perhaps this is unsafe, as per comment above.
       final_tidyup(tid);
 
       /* be paranoid */
@@ -2742,63 +2746,141 @@ void shutdown_actions_NORETURN( ThreadId tid,
 /* -------------------- */
 
 /* Final clean-up before terminating the process.  
-   Clean up the client by calling __libc_freeres() (if requested) 
-   This is Linux-specific?
-   GrP fixme glibc-specific, anyway
+   Clean up the client by calling __gnu_cxx::__freeres() (if requested)
+   and __libc_freeres() (if requested).
 */
 static void final_tidyup(ThreadId tid)
 {
-#if !defined(VGO_darwin)
-   Addr __libc_freeres_wrapper = VG_(client___libc_freeres_wrapper);
+#if defined(VGO_linux) || defined(VGO_solaris)
+   Addr freeres_wrapper = VG_(client_freeres_wrapper);
 
    vg_assert(VG_(is_running_thread)(tid));
-   
-   if ( !VG_(needs).libc_freeres ||
-        !VG_(clo_run_libc_freeres) ||
-        0 == __libc_freeres_wrapper )
-      return;			/* can't/won't do it */
+
+   if (freeres_wrapper == 0) {
+      return; /* can't do it */
+   }
+
+   Vg_FreeresToRun to_run = 0;
+   if (VG_(needs).cxx_freeres && VG_(clo_run_cxx_freeres)) {
+      to_run |= VG_RUN__GNU_CXX__FREERES;
+   }
+
+   if (VG_(needs).libc_freeres && VG_(clo_run_libc_freeres)) {
+      to_run |= VG_RUN__LIBC_FREERES;
+   }
+
+   if (to_run == 0) {
+      return; /* won't do it */
+   }
 
 #  if defined(VGP_ppc64be_linux)
-   Addr r2 = VG_(get_tocptr)( __libc_freeres_wrapper );
+   Addr r2 = VG_(get_tocptr)(freeres_wrapper);
    if (r2 == 0) {
       VG_(message)(Vg_UserMsg, 
-                   "Caught __NR_exit, but can't run __libc_freeres()\n");
+                   "Caught __NR_exit, but can't run __gnu_cxx::__freeres()\n");
       VG_(message)(Vg_UserMsg, 
-                   "   since cannot establish TOC pointer for it.\n");
+                   "   or __libc_freeres() since cannot establish TOC pointer "
+                   "for it.\n");
       return;
    }
 #  endif
 
    if (VG_(clo_verbosity) > 2  ||
        VG_(clo_trace_syscalls) ||
-       VG_(clo_trace_sched))
-      VG_(message)(Vg_DebugMsg, 
-		   "Caught __NR_exit; running __libc_freeres()\n");
+       VG_(clo_trace_sched)) {
+
+      vg_assert(to_run > 0);
+      vg_assert(to_run <= (VG_RUN__GNU_CXX__FREERES | VG_RUN__LIBC_FREERES));
+
+      const HChar *msgs[] = {"__gnu_cxx::__freeres()", "__libc_freeres()",
+                             "__gnu_cxx::__freeres and __libc_freeres()"};
+      VG_(message)(Vg_DebugMsg,
+                   "Caught __NR_exit; running %s wrapper\n", msgs[to_run - 1]);
+   }
       
-   /* set thread context to point to libc_freeres_wrapper */
-   /* ppc64be-linux note: __libc_freeres_wrapper gives us the real
+   /* set thread context to point to freeres_wrapper */
+   /* ppc64be-linux note: freeres_wrapper gives us the real
       function entry point, not a fn descriptor, so can use it
       directly.  However, we need to set R2 (the toc pointer)
       appropriately. */
-   VG_(set_IP)(tid, __libc_freeres_wrapper);
+   VG_(set_IP)(tid, freeres_wrapper);
 #  if defined(VGP_ppc64be_linux)
    VG_(threads)[tid].arch.vex.guest_GPR2 = r2;
 #  elif  defined(VGP_ppc64le_linux)
    /* setting GPR2 but not really needed, GPR12 is needed */
-   VG_(threads)[tid].arch.vex.guest_GPR2  = __libc_freeres_wrapper;
-   VG_(threads)[tid].arch.vex.guest_GPR12 = __libc_freeres_wrapper;
+   VG_(threads)[tid].arch.vex.guest_GPR2  = freeres_wrapper;
+   VG_(threads)[tid].arch.vex.guest_GPR12 = freeres_wrapper;
 #  endif
    /* mips-linux note: we need to set t9 */
 #  if defined(VGP_mips32_linux) || defined(VGP_mips64_linux)
-   VG_(threads)[tid].arch.vex.guest_r25 = __libc_freeres_wrapper;
+   VG_(threads)[tid].arch.vex.guest_r25 = freeres_wrapper;
 #  endif
 
+   /* Pass a parameter to freeres_wrapper(). */
+#  if defined(VGA_x86)
+   Addr sp = VG_(threads)[tid].arch.vex.guest_ESP;
+   sp = sp - sizeof(UWord);
+   *((UWord *) sp) = to_run;
+   VG_TRACK(post_mem_write, Vg_CoreClientReq, tid, sp, sizeof(UWord));
+   VG_(threads)[tid].arch.vex.guest_ESP = sp;
+   VG_TRACK(post_reg_write, Vg_CoreClientReq, tid,
+            offsetof(VexGuestX86State, guest_ESP),
+            sizeof(VG_(threads)[tid].arch.vex.guest_ESP));
+#  elif defined(VGA_amd64)
+   VG_(threads)[tid].arch.vex.guest_RDI = to_run;
+   VG_TRACK(post_reg_write, Vg_CoreClientReq, tid,
+            offsetof(VexGuestAMD64State, guest_RDI),
+            sizeof(VG_(threads)[tid].arch.vex.guest_RDI));
+#   elif defined(VGA_arm)
+   VG_(threads)[tid].arch.vex.guest_R0 = to_run;
+   VG_TRACK(post_reg_write, Vg_CoreClientReq, tid,
+            offsetof(VexGuestARMState, guest_R0),
+            sizeof(VG_(threads)[tid].arch.vex.guest_R0));
+#  elif defined(VGA_arm64)
+   VG_(threads)[tid].arch.vex.guest_X0 = to_run;
+   VG_TRACK(post_reg_write, Vg_CoreClientReq, tid,
+            offsetof(VexGuestARM64State, guest_X0),
+            sizeof(VG_(threads)[tid].arch.vex.guest_X0));
+#  elif defined(VGA_mips32)
+   VG_(threads)[tid].arch.vex.guest_r4 = to_run;
+   VG_TRACK(post_reg_write, Vg_CoreClientReq, tid,
+            offsetof(VexGuestMIPS32State, guest_r4),
+            sizeof(VG_(threads)[tid].arch.vex.guest_r4));
+#  elif defined(VGA_mips64)
+   VG_(threads)[tid].arch.vex.guest_r4 = to_run;
+   VG_TRACK(post_reg_write, Vg_CoreClientReq, tid,
+            offsetof(VexGuestMIPS64State, guest_r4),
+            sizeof(VG_(threads)[tid].arch.vex.guest_r4));
+#  elif defined(VGA_ppc32)
+   VG_(threads)[tid].arch.vex.guest_GPR3 = to_run;
+   VG_TRACK(post_reg_write, Vg_CoreClientReq, tid,
+            offsetof(VexGuestPPC32State, guest_GPR3),
+            sizeof(VG_(threads)[tid].arch.vex.guest_GPR3));
+#  elif defined(VGA_ppc64be) || defined(VGA_ppc64le)
+   VG_(threads)[tid].arch.vex.guest_GPR3 = to_run;
+   VG_TRACK(post_reg_write, Vg_CoreClientReq, tid,
+            offsetof(VexGuestPPC64State, guest_GPR3),
+            sizeof(VG_(threads)[tid].arch.vex.guest_GPR3));
+#  elif defined(VGA_s390x)
+   VG_(threads)[tid].arch.vex.guest_r2 = to_run;
+   VG_TRACK(post_reg_write, Vg_CoreClientReq, tid,
+            offsetof(VexGuestS390XState, guest_r2),
+            sizeof(VG_(threads)[tid].arch.vex.guest_r2));
+#  elif defined(VGA_tilegx)
+   VG_(threads)[tid].arch.vex.guest_r0 = to_run;
+   VG_TRACK(post_reg_write, Vg_CoreClientReq, tid,
+            offsetof(VexGuestTILEGXState, guest_r0),
+            sizeof(VG_(threads)[tid].arch.vex.guest_r0));
+#else
+   I_die_here : architecture missing in m_main.c
+#endif
+
    /* Block all blockable signals by copying the real block state into
-      the thread's block state*/
+      the thread's block state */
    VG_(sigprocmask)(VKI_SIG_BLOCK, NULL, &VG_(threads)[tid].sig_mask);
    VG_(threads)[tid].tmp_sig_mask = VG_(threads)[tid].sig_mask;
 
-   /* and restore handlers to default */
+   /* and restore handlers to default. */
    VG_(set_default_handler)(VKI_SIGSEGV);
    VG_(set_default_handler)(VKI_SIGBUS);
    VG_(set_default_handler)(VKI_SIGILL);
@@ -2806,11 +2888,11 @@ static void final_tidyup(ThreadId tid)
 
    // We were exiting, so assert that...
    vg_assert(VG_(is_exiting)(tid));
-   // ...but now we're not again
+   // ...but now we're not again.
    VG_(threads)[tid].exitreason = VgSrc_None;
 
-   // run until client thread exits - ideally with LIBC_FREERES_DONE,
-   // but exit/exitgroup/signal will do
+   // Run until client thread exits - ideally with FREERES_DONE,
+   // but exit/exitgroup/signal will do.
    VG_(scheduler)(tid);
 
    vg_assert(VG_(is_exiting)(tid));
diff --git a/coregrind/m_options.c b/coregrind/m_options.c
index 7e920e0..83d6018 100644
--- a/coregrind/m_options.c
+++ b/coregrind/m_options.c
@@ -121,6 +121,7 @@ Bool   VG_(clo_read_inline_info) = False; // Or should be put it to True by defa
 Bool   VG_(clo_read_var_info)  = False;
 XArray *VG_(clo_req_tsyms);  // array of strings
 Bool   VG_(clo_run_libc_freeres) = True;
+Bool   VG_(clo_run_cxx_freeres) = True;
 Bool   VG_(clo_track_fds)      = False;
 Bool   VG_(clo_show_below_main)= False;
 Bool   VG_(clo_show_emwarns)   = False;
diff --git a/coregrind/m_redir.c b/coregrind/m_redir.c
index dae47f1..62cb45a 100644
--- a/coregrind/m_redir.c
+++ b/coregrind/m_redir.c
@@ -49,7 +49,7 @@
 #include "pub_core_machine.h"      // VG_(fnptr_to_fnentry)
 #include "pub_core_aspacemgr.h"    // VG_(am_find_nsegment)
 #include "pub_core_xarray.h"
-#include "pub_core_clientstate.h"  // VG_(client___libc_freeres_wrapper)
+#include "pub_core_clientstate.h"  // VG_(client_freeres_wrapper)
 #include "pub_core_demangle.h"     // VG_(maybe_Z_demangle)
 #include "pub_core_libcproc.h"     // VG_(libdir)
 
@@ -1688,7 +1688,7 @@ void handle_maybe_load_notifier( const HChar* soname,
       return;
 
    if (VG_(strcmp)(symbol, VG_STRINGIFY(VG_NOTIFY_ON_LOAD(freeres))) == 0)
-      VG_(client___libc_freeres_wrapper) = addr;
+      VG_(client_freeres_wrapper) = addr;
    else
    if (VG_(strcmp)(symbol, VG_STRINGIFY(VG_NOTIFY_ON_LOAD(ifunc_wrapper))) == 0)
       iFuncWrapper = addr;
diff --git a/coregrind/m_scheduler/scheduler.c b/coregrind/m_scheduler/scheduler.c
index 1234d56..9aa854d 100644
--- a/coregrind/m_scheduler/scheduler.c
+++ b/coregrind/m_scheduler/scheduler.c
@@ -1744,12 +1744,13 @@ static Bool os_client_request(ThreadId tid, UWord *args)
    vg_assert(VG_(is_running_thread)(tid));
 
    switch(args[0]) {
-   case VG_USERREQ__LIBC_FREERES_DONE:
+   case VG_USERREQ__FREERES_DONE:
       /* This is equivalent to an exit() syscall, but we don't set the
 	 exitcode (since it might already be set) */
       if (0 || VG_(clo_trace_syscalls) || VG_(clo_trace_sched))
          VG_(message)(Vg_DebugMsg, 
-                      "__libc_freeres() done; really quitting!\n");
+                      "__gnu_cxx::__freeres() and __libc_freeres() wrapper "
+                      "done; really quitting!\n");
       VG_(threads)[tid].exitreason = VgSrc_ExitThread;
       break;
 
diff --git a/coregrind/m_tooliface.c b/coregrind/m_tooliface.c
index 6971a47..38bc7c2 100644
--- a/coregrind/m_tooliface.c
+++ b/coregrind/m_tooliface.c
@@ -88,6 +88,7 @@ VgNeeds VG_(needs) = {
    .core_errors          = False,
    .tool_errors          = False,
    .libc_freeres         = False,
+   .cxx_freeres          = False,
    .superblock_discards  = False,
    .command_line_options = False,
    .client_requests      = False,
@@ -216,6 +217,7 @@ Bool VG_(sanity_check_needs)(const HChar** failmsg)
 
 // These ones don't require any tool-supplied functions
 NEEDS(libc_freeres)
+NEEDS(cxx_freeres)
 NEEDS(core_errors)
 NEEDS(var_info)
 
diff --git a/coregrind/pub_core_clientstate.h b/coregrind/pub_core_clientstate.h
index 215dfb2..ddd1c09 100644
--- a/coregrind/pub_core_clientstate.h
+++ b/coregrind/pub_core_clientstate.h
@@ -90,9 +90,9 @@ extern HChar* VG_(name_of_launcher);
 extern Int VG_(fd_soft_limit);
 extern Int VG_(fd_hard_limit);
 
-/* Useful addresses extracted from the client */
-/* Where is the __libc_freeres_wrapper routine we made? */
-extern Addr VG_(client___libc_freeres_wrapper);
+/* Useful addresses extracted from the client. */
+/* Where is the freeres_wrapper routine we made? */
+extern Addr VG_(client_freeres_wrapper);
 
 /* x86-linux only: where is ld.so's _dl_sysinfo_int80 function?
    Finding it isn't essential, but knowing where it is does sometimes
diff --git a/coregrind/pub_core_clreq.h b/coregrind/pub_core_clreq.h
index 45c48c3..ce1493c 100644
--- a/coregrind/pub_core_clreq.h
+++ b/coregrind/pub_core_clreq.h
@@ -41,8 +41,8 @@
 // used to be many more internal client requests.
 typedef
    enum { 
-      /* Denote the finish of __libc_freeres_wrapper().  Also causes exit. */
-      VG_USERREQ__LIBC_FREERES_DONE = 0x3029,
+      /* Denote the finish of freeres_wrapper().  Also causes exit. */
+      VG_USERREQ__FREERES_DONE = 0x3029,
 
       /* Get the tool's malloc-wrapping functions */
       VG_USERREQ__GET_MALLOCFUNCS   = 0x3030,
@@ -55,6 +55,18 @@ typedef
 
    } Vg_InternalClientRequest;
 
+
+/* Which freeres functions to run in the freeres_wrapper().
+   It is possible to run both. */
+typedef enum {
+   /* Run __gnu_cxx::__freeres(). */
+   VG_RUN__GNU_CXX__FREERES = 1,
+
+   /* Run __libc_freeres(). */
+   VG_RUN__LIBC_FREERES = 2
+
+} Vg_FreeresToRun;
+
 // Function for printing from code within Valgrind, but which runs on the
 // sim'd CPU.  Must be a function rather than macros so that va_list can
 // be used.
diff --git a/coregrind/pub_core_options.h b/coregrind/pub_core_options.h
index 2a45c6b..7a887fc 100644
--- a/coregrind/pub_core_options.h
+++ b/coregrind/pub_core_options.h
@@ -281,6 +281,13 @@ extern Bool  VG_(clo_track_fds);
    cannot be overridden from the command line. */
 extern Bool  VG_(clo_run_libc_freeres);
 
+/* Should we run __gnu_cxx::__freeres at exit for C++ programs?
+   Default: YES.  Note this is subservient to VG_(needs).cxx_freeres;
+   if the latter says False, then the setting of VG_(clo_run_cxx_freeres)
+   is ignored.  Ie if a tool says no, I don't want this to run, that
+   cannot be overridden from the command line. */
+extern Bool  VG_(clo_run_cxx_freeres);
+
 /* Should we show VEX emulation warnings?  Default: NO */
 extern Bool VG_(clo_show_emwarns);
 
diff --git a/coregrind/pub_core_tooliface.h b/coregrind/pub_core_tooliface.h
index 41da986..83758a8 100644
--- a/coregrind/pub_core_tooliface.h
+++ b/coregrind/pub_core_tooliface.h
@@ -81,6 +81,7 @@ extern VgDetails VG_(details);
 typedef
    struct {
       Bool libc_freeres;
+      Bool cxx_freeres;
       Bool core_errors;
       Bool tool_errors;
       Bool superblock_discards;
diff --git a/coregrind/vg_preloaded.c b/coregrind/vg_preloaded.c
index 2ea7a7a..2f53a7d 100644
--- a/coregrind/vg_preloaded.c
+++ b/coregrind/vg_preloaded.c
@@ -47,29 +47,47 @@
 #include "pub_core_debuginfo.h"  // Needed for pub_core_redir.h
 #include "pub_core_redir.h"      // For VG_NOTIFY_ON_LOAD
 
-#if defined(VGO_linux)
+#if defined(VGO_linux) || defined(VGO_solaris)
 
 /* ---------------------------------------------------------------------
-   Hook for running __libc_freeres once the program exits.
+   Hook for running __gnu_cxx::__freeres() and __libc_freeres() once
+   the program exits.
    ------------------------------------------------------------------ */
 
-void VG_NOTIFY_ON_LOAD(freeres)( void );
-void VG_NOTIFY_ON_LOAD(freeres)( void )
+void VG_NOTIFY_ON_LOAD(freeres)(Vg_FreeresToRun to_run);
+void VG_NOTIFY_ON_LOAD(freeres)(Vg_FreeresToRun to_run)
 {
 #  if !defined(__UCLIBC__) \
       && !defined(VGPV_arm_linux_android) \
       && !defined(VGPV_x86_linux_android) \
       && !defined(VGPV_mips32_linux_android) \
       && !defined(VGPV_arm64_linux_android)
+
+   /* g++ mangled __gnu_cxx::__freeres yields -> _ZN9__gnu_cxx9__freeresEv */
+   extern void _ZN9__gnu_cxx9__freeresEv(void) __attribute__((weak));
+   if (((to_run & VG_RUN__GNU_CXX__FREERES) != 0) &&
+       (_ZN9__gnu_cxx9__freeresEv != NULL)) {
+      _ZN9__gnu_cxx9__freeresEv();
+   }
+
+#  if defined(VGO_linux)
+   /* __libc_freeres() not yet available on Solaris. */
    extern void __libc_freeres(void);
-   __libc_freeres();
+   if ((to_run & VG_RUN__LIBC_FREERES) != 0) {
+      __libc_freeres();
+   }
 #  endif
-   VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__LIBC_FREERES_DONE, 
-                                   0, 0, 0, 0, 0);
+#  endif
+
+   VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__FREERES_DONE, 0, 0, 0, 0, 0);
    /*NOTREACHED*/
    *(volatile int *)0 = 'x';
 }
 
+#endif // VGO_linux || VGO_solaris
+
+#if defined(VGO_linux)
+
 /* ---------------------------------------------------------------------
    Wrapper for indirect functions which need to be redirected.
    ------------------------------------------------------------------ */
diff --git a/docs/xml/manual-core.xml b/docs/xml/manual-core.xml
index 758e2f4..7628836 100644
--- a/docs/xml/manual-core.xml
+++ b/docs/xml/manual-core.xml
@@ -1930,6 +1930,37 @@ need to use them.</para>
     </listitem>
   </varlistentry>
 
+  <varlistentry id="opt.run-cxx-freeres" xreflabel="--run-cxx-freeres">
+    <term>
+      <option><![CDATA[--run-cxx-freeres=<yes|no> [default: yes] ]]></option>
+    </term>
+    <listitem>
+      <para>This option is only relevant when running Valgrind on Linux
+            or Solaris C++ programs.</para>
+
+      <para>The GNU Standard C++ library (<function>libstdc++.so</function>),
+      which is used by all C++ programs compiled with g++, may allocate memory
+      for its own uses. Usually it doesn't bother to free that memory when
+      the program ends&mdash;there would be no point, since the kernel reclaims
+      all process resources when a process exits anyway, so it would
+      just slow things down.</para>
+
+      <para>The gcc authors realised that this behaviour causes leak
+      checkers, such as Valgrind, to falsely report leaks in libstdc++, when
+      a leak check is done at exit.  In order to avoid this, they
+      provided a routine called <function>__gnu_cxx::__freeres</function>
+      specifically to make libstdc++ release all memory it has allocated.
+      Memcheck therefore tries to run
+      <function>__gnu_cxx::__freeres</function> at exit.</para>
+
+      <para>For the sake of flexibility and unforeseen problems with
+      <function>__gnu_cxx::__freeres</function>, option
+      <option>--run-cxx-freeres=no</option> exists,
+      although at the cost of possibly falsely reporting space leaks in
+      <filename>libstdc++.so</filename>.</para>
+    </listitem>
+  </varlistentry>
+
   <varlistentry id="opt.sim-hints" xreflabel="--sim-hints">
     <term>
       <option><![CDATA[--sim-hints=hint1,hint2,... ]]></option>
diff --git a/exp-dhat/dh_main.c b/exp-dhat/dh_main.c
index e636ccb..3476cd9 100644
--- a/exp-dhat/dh_main.c
+++ b/exp-dhat/dh_main.c
@@ -1358,6 +1358,7 @@ static void dh_pre_clo_init(void)
 //zz
    // Needs.
    VG_(needs_libc_freeres)();
+   VG_(needs_cxx_freeres)();
    VG_(needs_command_line_options)(dh_process_cmd_line_option,
                                    dh_print_usage,
                                    dh_print_debug_usage);
diff --git a/include/pub_tool_tooliface.h b/include/pub_tool_tooliface.h
index 8df00b5..f7805e9 100644
--- a/include/pub_tool_tooliface.h
+++ b/include/pub_tool_tooliface.h
@@ -263,6 +263,9 @@ extern void VG_(details_bug_reports_to)   ( const HChar* bug_reports_to );
 /* Should __libc_freeres() be run?  Bugs in it can crash the tool. */
 extern void VG_(needs_libc_freeres) ( void );
 
+/* Should __gnu_cxx::__freeres() be run?  Bugs in it can crash the tool. */
+extern void VG_(needs_cxx_freeres) ( void );
+
 /* Want to have errors detected by Valgrind's core reported?  Includes:
    - pthread API errors (many;  eg. unlocking a non-locked mutex) 
      [currently disabled]
diff --git a/massif/ms_main.c b/massif/ms_main.c
index 66f9be9..628a37b 100644
--- a/massif/ms_main.c
+++ b/massif/ms_main.c
@@ -2569,6 +2569,7 @@ static void ms_pre_clo_init(void)
 
    // Needs.
    VG_(needs_libc_freeres)();
+   VG_(needs_cxx_freeres)();
    VG_(needs_command_line_options)(ms_process_cmd_line_option,
                                    ms_print_usage,
                                    ms_print_debug_usage);
diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c
index 99421f5..5464a06 100644
--- a/memcheck/mc_main.c
+++ b/memcheck/mc_main.c
@@ -7745,6 +7745,7 @@ static void mc_pre_clo_init(void)
                                    MC_(print_extra_suppression_use),
                                    MC_(update_extra_suppression_use));
    VG_(needs_libc_freeres)        ();
+   VG_(needs_cxx_freeres)         ();
    VG_(needs_command_line_options)(mc_process_cmd_line_options,
                                    mc_print_usage,
                                    mc_print_debug_usage);
diff --git a/none/tests/cmdline1.stdout.exp b/none/tests/cmdline1.stdout.exp
index cc25a16..0faec69 100644
--- a/none/tests/cmdline1.stdout.exp
+++ b/none/tests/cmdline1.stdout.exp
@@ -90,6 +90,8 @@ usage: valgrind [options] prog-and-args
     --vgdb-shadow-registers=no|yes   let gdb see the shadow registers [no]
     --vgdb-prefix=<prefix>    prefix for vgdb FIFOs [.../vgdb-pipe]
     --run-libc-freeres=no|yes free up glibc memory at exit on Linux? [yes]
+    --run-cxx-freeres=no|yes  free up libstdc++ memory at exit on Linux
+                              and Solaris? [yes]
     --sim-hints=hint1,hint2,...  activate unusual sim behaviours [none] 
          where hint is one of:
            lax-ioctls lax-doors fuse-compatible enable-outer
diff --git a/none/tests/cmdline2.stdout.exp b/none/tests/cmdline2.stdout.exp
index 580fa19..b124f20 100644
--- a/none/tests/cmdline2.stdout.exp
+++ b/none/tests/cmdline2.stdout.exp
@@ -90,6 +90,8 @@ usage: valgrind [options] prog-and-args
     --vgdb-shadow-registers=no|yes   let gdb see the shadow registers [no]
     --vgdb-prefix=<prefix>    prefix for vgdb FIFOs [.../vgdb-pipe]
     --run-libc-freeres=no|yes free up glibc memory at exit on Linux? [yes]
+    --run-cxx-freeres=no|yes  free up libstdc++ memory at exit on Linux
+                              and Solaris? [yes]
     --sim-hints=hint1,hint2,...  activate unusual sim behaviours [none] 
          where hint is one of:
            lax-ioctls lax-doors fuse-compatible enable-outer
commit 315cb2823de2e3006ec454b278a9f9e474520f57
Author: iraisr <iraisr@a5019735-40e9-0310-863c-91ae7b9d1cf9>
Date:   Fri Apr 8 21:29:15 2016 +0000

    Follow up to bug 345307.
    Passing parameter on x86 architecture was wrong in final_tidyup().
    
    
    git-svn-id: svn://svn.valgrind.org/valgrind/trunk@15854 a5019735-40e9-0310-863c-91ae7b9d1cf9

diff --git a/coregrind/m_main.c b/coregrind/m_main.c
index 8cfaf82..80f347e 100644
--- a/coregrind/m_main.c
+++ b/coregrind/m_main.c
@@ -2819,9 +2819,9 @@ static void final_tidyup(ThreadId tid)
    /* Pass a parameter to freeres_wrapper(). */
 #  if defined(VGA_x86)
    Addr sp = VG_(threads)[tid].arch.vex.guest_ESP;
-   sp = sp - sizeof(UWord);
    *((UWord *) sp) = to_run;
    VG_TRACK(post_mem_write, Vg_CoreClientReq, tid, sp, sizeof(UWord));
+   sp = sp - sizeof(UWord);
    VG_(threads)[tid].arch.vex.guest_ESP = sp;
    VG_TRACK(post_reg_write, Vg_CoreClientReq, tid,
             offsetof(VexGuestX86State, guest_ESP),