This diff fixes a bug in libc's __cxa_finalize() implementation. The
IA64 C++ ABI says that __cxa_finalize(NULL) should run all remaining
handlers registered with __cxa_atexit(); however, the current handling
of this results in a garbage argument being passed instead.
E.g., this sample program should print 0x8675309:
#include <stdio.h>
extern void *__dso_handle;
extern void __cxa_atexit(void (*)(void *), void *, void *);
static void foo(void *arg) {
printf("%p\n", arg);
}
int main() {
__cxa_atexit(foo, (void *) 0x8675309, &__dso_handle);
return 0;
}
This doesn't seem to affect GCC (at least not in base) because it
generates trampoline code so the argument is unneeded anyway, but
Clang (LLVM) makes use of it for registering global destructors,
causing unpredictable run-time errors when the process exits.
(I'm told there's no risk in calling a no-argument function with a
superfluous void pointer argument, so the atexit.[ch] code could be
further simplified by simply always passing an argument to the handler
even if fn_dso == NULL.)
ok?
Index: lib/libc/stdlib/atexit.c
===================================================================
RCS file: /cvs/src/lib/libc/stdlib/atexit.c,v
retrieving revision 1.14
diff -u -p lib/libc/stdlib/atexit.c
--- lib/libc/stdlib/atexit.c 5 Sep 2007 20:47:47 -0000 1.14
+++ lib/libc/stdlib/atexit.c 24 Feb 2011 04:44:53 -0000
@@ -147,7 +147,7 @@ __cxa_finalize(void *dso)
p->fns[n].fn_ptr.cxa_func = NULL;
mprotect(p, pgsize, PROT_READ);
}
- if (dso != NULL)
+ if (fn.fn_dso != NULL)
(*fn.fn_ptr.cxa_func)(fn.fn_arg);
else
(*fn.fn_ptr.std_func)();