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