At request of Ulrich Drepper, call __libc_freeres() after final __NR_exit
so as to free memory allocated by glibc.  This reduces the leaks reported
in glibc, but causes a stack of read/write-after-free errors which have
to be suppressed :-(


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@507 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/vg_clientfuncs.c b/coregrind/vg_clientfuncs.c
index 9e498e0..80bdae6 100644
--- a/coregrind/vg_clientfuncs.c
+++ b/coregrind/vg_clientfuncs.c
@@ -552,6 +552,23 @@
    return -1;
 }
 
+
+/* ---------------------------------------------------------------------
+   Hook for running __libc_freeres once the program exits.
+   ------------------------------------------------------------------ */
+
+void VG_(__libc_freeres_wrapper)( void )
+{
+   int res;
+   extern void __libc_freeres(void);
+   __libc_freeres();
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__LIBC_FREERES_DONE, 0, 0, 0, 0);
+   /*NOTREACHED*/
+   vg_assert(12345+54321 == 999999);
+}
+
+
 /*--------------------------------------------------------------------*/
 /*--- end                                         vg_clientfuncs.c ---*/
 /*--------------------------------------------------------------------*/
diff --git a/coregrind/vg_include.h b/coregrind/vg_include.h
index 31ba8e9..0d38c92 100644
--- a/coregrind/vg_include.h
+++ b/coregrind/vg_include.h
@@ -519,6 +519,9 @@
 #define VG_USERREQ__SET_FHSTACK_ENTRY       0x3027
 #define VG_USERREQ__GET_FHSTACK_ENTRY       0x3028
 
+/* Denote the finish of VG_(__libc_freeres_wrapper). */
+#define VG_USERREQ__LIBC_FREERES_DONE       0x3029
+
 /* Cosmetic ... */
 #define VG_USERREQ__GET_PTHREAD_TRACE_LEVEL 0x3101
 /* Log a pthread error from client-space.  Cosmetic. */
@@ -529,6 +532,10 @@
 #define VG_USERREQ__SIGNAL_RETURNS          0x4001
 */
 
+/* The scheduler does need to know the address of it so it can be
+   called at program exit. */
+extern void VG_(__libc_freeres_wrapper)( void );
+
 
 /* ---------------------------------------------------------------------
    Constants pertaining to the simulated CPU state, VG_(baseBlock),
diff --git a/coregrind/vg_scheduler.c b/coregrind/vg_scheduler.c
index 823c859..43b0189 100644
--- a/coregrind/vg_scheduler.c
+++ b/coregrind/vg_scheduler.c
@@ -1337,6 +1337,16 @@
          if (trc == VG_TRC_EBP_JMP_CLIENTREQ) {
             UInt reqno = *(UInt*)(VG_(threads)[tid].m_eax);
             /* VG_(printf)("request 0x%x\n", reqno); */
+
+            /* Are we really absolutely totally quitting? */
+            if (reqno == VG_USERREQ__LIBC_FREERES_DONE) {
+               if (0 || VG_(clo_trace_syscalls) || VG_(clo_trace_sched)) {
+                  VG_(message)(Vg_DebugMsg, 
+                     "__libc_freeres() done; really quitting!");
+               }
+               return VgSrc_ExitSyscall;
+            }
+
             do_client_request(tid);
             /* Following the request, we try and continue with the
                same thread if still runnable.  If not, go back to
@@ -1362,9 +1372,22 @@
             }
 #           endif
 
-            /* Is the client exiting for good? */
-            if (VG_(threads)[tid].m_eax == __NR_exit)
-               return VgSrc_ExitSyscall;
+            /* Deal with calling __libc_freeres() at exit.  When the
+               client does __NR_exit, it's exiting for good.  So we
+               then run VG_(__libc_freeres_wrapper).  That quits by
+               doing VG_USERREQ__LIBC_FREERES_DONE, and at that point
+               we really exit.  To be safe we nuke all other threads
+               currently running. */
+            if (VG_(threads)[tid].m_eax == __NR_exit) {
+               if (0 || VG_(clo_trace_syscalls) || VG_(clo_trace_sched)) {
+                  VG_(message)(Vg_DebugMsg, 
+                     "Caught __NR_exit; running __libc_freeres()");
+               }
+               VG_(nuke_all_threads_except) ( tid );
+               VG_(threads)[tid].m_eip = (UInt)(&VG_(__libc_freeres_wrapper));
+	       vg_assert(VG_(threads)[tid].status == VgTs_Runnable);
+               goto stage1; /* party on, dudes (but not for much longer :) */
+            }
 
             /* Trap syscalls to __NR_sched_yield and just have this
                thread yield instead.  Not essential, just an
diff --git a/glibc-2.2.supp b/glibc-2.2.supp
index a69f837..0b044db 100644
--- a/glibc-2.2.supp
+++ b/glibc-2.2.supp
@@ -17,6 +17,30 @@
 # }
 
 
+#-------- Suppress errors appearing as a result of calling
+#-------- __libc_freeres()
+
+{
+   __twalk/*(Addr4)
+   Addr4
+   fun:__twalk
+}
+
+{
+   do_release_shlib/__twalk(Addr4)
+   Addr4
+   fun:do_release_shlib
+   fun:__twalk
+}
+
+{
+   __libc_freeres/free_mem/free(Free)
+   Free
+   fun:free
+   fun:free_mem
+   fun:__libc_freeres
+}
+
 #-------- Threading bugs?
 
 {
diff --git a/vg_clientfuncs.c b/vg_clientfuncs.c
index 9e498e0..80bdae6 100644
--- a/vg_clientfuncs.c
+++ b/vg_clientfuncs.c
@@ -552,6 +552,23 @@
    return -1;
 }
 
+
+/* ---------------------------------------------------------------------
+   Hook for running __libc_freeres once the program exits.
+   ------------------------------------------------------------------ */
+
+void VG_(__libc_freeres_wrapper)( void )
+{
+   int res;
+   extern void __libc_freeres(void);
+   __libc_freeres();
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__LIBC_FREERES_DONE, 0, 0, 0, 0);
+   /*NOTREACHED*/
+   vg_assert(12345+54321 == 999999);
+}
+
+
 /*--------------------------------------------------------------------*/
 /*--- end                                         vg_clientfuncs.c ---*/
 /*--------------------------------------------------------------------*/
diff --git a/vg_include.h b/vg_include.h
index 31ba8e9..0d38c92 100644
--- a/vg_include.h
+++ b/vg_include.h
@@ -519,6 +519,9 @@
 #define VG_USERREQ__SET_FHSTACK_ENTRY       0x3027
 #define VG_USERREQ__GET_FHSTACK_ENTRY       0x3028
 
+/* Denote the finish of VG_(__libc_freeres_wrapper). */
+#define VG_USERREQ__LIBC_FREERES_DONE       0x3029
+
 /* Cosmetic ... */
 #define VG_USERREQ__GET_PTHREAD_TRACE_LEVEL 0x3101
 /* Log a pthread error from client-space.  Cosmetic. */
@@ -529,6 +532,10 @@
 #define VG_USERREQ__SIGNAL_RETURNS          0x4001
 */
 
+/* The scheduler does need to know the address of it so it can be
+   called at program exit. */
+extern void VG_(__libc_freeres_wrapper)( void );
+
 
 /* ---------------------------------------------------------------------
    Constants pertaining to the simulated CPU state, VG_(baseBlock),
diff --git a/vg_scheduler.c b/vg_scheduler.c
index 823c859..43b0189 100644
--- a/vg_scheduler.c
+++ b/vg_scheduler.c
@@ -1337,6 +1337,16 @@
          if (trc == VG_TRC_EBP_JMP_CLIENTREQ) {
             UInt reqno = *(UInt*)(VG_(threads)[tid].m_eax);
             /* VG_(printf)("request 0x%x\n", reqno); */
+
+            /* Are we really absolutely totally quitting? */
+            if (reqno == VG_USERREQ__LIBC_FREERES_DONE) {
+               if (0 || VG_(clo_trace_syscalls) || VG_(clo_trace_sched)) {
+                  VG_(message)(Vg_DebugMsg, 
+                     "__libc_freeres() done; really quitting!");
+               }
+               return VgSrc_ExitSyscall;
+            }
+
             do_client_request(tid);
             /* Following the request, we try and continue with the
                same thread if still runnable.  If not, go back to
@@ -1362,9 +1372,22 @@
             }
 #           endif
 
-            /* Is the client exiting for good? */
-            if (VG_(threads)[tid].m_eax == __NR_exit)
-               return VgSrc_ExitSyscall;
+            /* Deal with calling __libc_freeres() at exit.  When the
+               client does __NR_exit, it's exiting for good.  So we
+               then run VG_(__libc_freeres_wrapper).  That quits by
+               doing VG_USERREQ__LIBC_FREERES_DONE, and at that point
+               we really exit.  To be safe we nuke all other threads
+               currently running. */
+            if (VG_(threads)[tid].m_eax == __NR_exit) {
+               if (0 || VG_(clo_trace_syscalls) || VG_(clo_trace_sched)) {
+                  VG_(message)(Vg_DebugMsg, 
+                     "Caught __NR_exit; running __libc_freeres()");
+               }
+               VG_(nuke_all_threads_except) ( tid );
+               VG_(threads)[tid].m_eip = (UInt)(&VG_(__libc_freeres_wrapper));
+	       vg_assert(VG_(threads)[tid].status == VgTs_Runnable);
+               goto stage1; /* party on, dudes (but not for much longer :) */
+            }
 
             /* Trap syscalls to __NR_sched_yield and just have this
                thread yield instead.  Not essential, just an