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:
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user