bonefish+mmlr:

As (our) gcc unfortunately uses atexit() to clean up lazily initialized static
variables inside functions we have to ensure that we do the right thing with
unloadable shared objects. In case a shared object was unloaded that installed
an atexit() hook the application would crash on exit. We now implement a
callback into libroot that is used to call all the atexit() hooks of a
component that is to be unloaded. Most prominently this fixes the media_server
crash at shutdown.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23486 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz
2008-01-13 12:08:34 +00:00
parent c4bf6940f5
commit 8c2a9d7433
5 changed files with 107 additions and 10 deletions
+2
View File
@@ -41,6 +41,8 @@ struct rld_export {
status_t (*reinit_after_fork)();
void (*call_atexit_hooks_for_range)(addr_t start, addr_t size);
const struct user_space_program_args *program_args;
};
+6
View File
@@ -13,6 +13,8 @@
void initialize_before(image_id imageID);
void _call_atexit_hooks_for_range(addr_t start, addr_t size);
void __init_exit_stack_lock();
struct rld_export *__gRuntimeLoader = NULL;
// This little bugger is set to something meaningful by the runtime loader
@@ -48,6 +50,10 @@ initialize_before(image_id imageID)
__libc_argc = __gRuntimeLoader->program_args->arg_count;
__libc_argv = __gRuntimeLoader->program_args->args;
__gRuntimeLoader->call_atexit_hooks_for_range
= _call_atexit_hooks_for_range;
__init_exit_stack_lock();
__init_time();
__init_fork();
__init_heap();
+92 -9
View File
@@ -13,13 +13,91 @@
#include <stdlib.h>
#include <signal.h>
#include <limits.h>
#include <SupportDefs.h>
extern void _IO_cleanup(void);
extern void _thread_do_exit_notification(void);
static void (*sExitStack[ATEXIT_MAX])(void) = {0};
static int32 sExitStackIndex = 0;
struct exit_stack_info {
void (*exit_stack[ATEXIT_MAX])(void);
int32 stack_size;
sem_id lock;
vint32 lock_count;
thread_id lock_owner;
size_t recursion_count;
};
static struct exit_stack_info sExitStackInfo = { {}, 0, -1, 0, -1, 0 };
void
__init_exit_stack_lock()
{
sExitStackInfo.lock = create_sem(0, "exit stack lock");
if (sExitStackInfo.lock < 0)
debugger("failed to create exit stack lock");
}
static void
_exit_stack_lock()
{
thread_id self = find_thread(NULL);
if (self != sExitStackInfo.lock_owner) {
if (atomic_add(&sExitStackInfo.lock_count, 1) > 0)
acquire_sem(sExitStackInfo.lock);
sExitStackInfo.lock_owner = self;
}
sExitStackInfo.recursion_count++;
}
static void
_exit_stack_unlock()
{
if (sExitStackInfo.lock_owner != find_thread(NULL))
debugger("exit stack lock not owned");
if (sExitStackInfo.recursion_count-- == 1) {
sExitStackInfo.lock_owner = -1;
if (atomic_add(&sExitStackInfo.lock_count, -1) == 1)
release_sem(sExitStackInfo.lock);
}
}
void
_call_atexit_hooks_for_range(addr_t start, addr_t size)
{
int32 index;
int32 insertIndex = -1;
_exit_stack_lock();
for (index = sExitStackInfo.stack_size - 1; index > 0; index--) {
addr_t function = (addr_t)sExitStackInfo.exit_stack[index];
if (function >= start && function < start + size) {
(*sExitStackInfo.exit_stack[index])();
sExitStackInfo.exit_stack[index] = NULL;
insertIndex = index;
}
}
if (insertIndex >= 0) {
for (index = insertIndex + 1;
index < sExitStackInfo.stack_size;
index++) {
if (sExitStackInfo.exit_stack[index] != NULL) {
sExitStackInfo.exit_stack[insertIndex++]
= sExitStackInfo.exit_stack[index];
}
}
sExitStackInfo.stack_size = insertIndex;
}
_exit_stack_unlock();
}
void
@@ -36,13 +114,16 @@ int
atexit(void (*func)(void))
{
// push the function pointer onto the exit stack
int32 index = atomic_add(&sExitStackIndex, 1);
int result = -1;
_exit_stack_lock();
if (index >= ATEXIT_MAX)
return -1;
if (sExitStackInfo.stack_size < ATEXIT_MAX) {
sExitStackInfo.exit_stack[sExitStackInfo.stack_size++] = func;
result = 0;
}
sExitStack[index] = func;
return 0;
_exit_stack_unlock();
return result;
}
@@ -53,8 +134,10 @@ exit(int status)
_thread_do_exit_notification();
// unwind the exit stack, calling the registered functions
while (sExitStackIndex-- > 0)
(*sExitStack[sExitStackIndex])();
_exit_stack_lock();
while (--sExitStackInfo.stack_size >= 0)
(*sExitStackInfo.exit_stack[sExitStackInfo.stack_size])();
_exit_stack_unlock();
// close all open files
_IO_cleanup();
+5
View File
@@ -1458,6 +1458,11 @@ unload_library(image_id imageID, bool addOn)
if (status == B_OK) {
while ((image = sDisposableImages.head) != NULL) {
// call image fini here...
if (gRuntimeLoader.call_atexit_hooks_for_range) {
gRuntimeLoader.call_atexit_hooks_for_range(
image->regions[0].start, image->regions[0].size);
}
if (image->term_routine)
((init_term_function)image->term_routine)(image->id);
+2 -1
View File
@@ -36,7 +36,8 @@ struct rld_export gRuntimeLoader = {
test_executable,
get_next_image_dependency,
elf_reinit_after_fork
elf_reinit_after_fork,
NULL // call_atexit_hooks_for_range
};