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)();
|
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;
|
const struct user_space_program_args *program_args;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
|
|
||||||
void initialize_before(image_id imageID);
|
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;
|
struct rld_export *__gRuntimeLoader = NULL;
|
||||||
// This little bugger is set to something meaningful by the runtime loader
|
// 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_argc = __gRuntimeLoader->program_args->arg_count;
|
||||||
__libc_argv = __gRuntimeLoader->program_args->args;
|
__libc_argv = __gRuntimeLoader->program_args->args;
|
||||||
|
|
||||||
|
__gRuntimeLoader->call_atexit_hooks_for_range
|
||||||
|
= _call_atexit_hooks_for_range;
|
||||||
|
|
||||||
|
__init_exit_stack_lock();
|
||||||
__init_time();
|
__init_time();
|
||||||
__init_fork();
|
__init_fork();
|
||||||
__init_heap();
|
__init_heap();
|
||||||
|
|||||||
@@ -13,13 +13,91 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
|
||||||
extern void _IO_cleanup(void);
|
extern void _IO_cleanup(void);
|
||||||
extern void _thread_do_exit_notification(void);
|
extern void _thread_do_exit_notification(void);
|
||||||
|
|
||||||
static void (*sExitStack[ATEXIT_MAX])(void) = {0};
|
struct exit_stack_info {
|
||||||
static int32 sExitStackIndex = 0;
|
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
|
void
|
||||||
@@ -36,13 +114,16 @@ int
|
|||||||
atexit(void (*func)(void))
|
atexit(void (*func)(void))
|
||||||
{
|
{
|
||||||
// push the function pointer onto the exit stack
|
// push the function pointer onto the exit stack
|
||||||
int32 index = atomic_add(&sExitStackIndex, 1);
|
int result = -1;
|
||||||
|
_exit_stack_lock();
|
||||||
|
|
||||||
if (index >= ATEXIT_MAX)
|
if (sExitStackInfo.stack_size < ATEXIT_MAX) {
|
||||||
return -1;
|
sExitStackInfo.exit_stack[sExitStackInfo.stack_size++] = func;
|
||||||
|
result = 0;
|
||||||
|
}
|
||||||
|
|
||||||
sExitStack[index] = func;
|
_exit_stack_unlock();
|
||||||
return 0;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -53,8 +134,10 @@ exit(int status)
|
|||||||
_thread_do_exit_notification();
|
_thread_do_exit_notification();
|
||||||
|
|
||||||
// unwind the exit stack, calling the registered functions
|
// unwind the exit stack, calling the registered functions
|
||||||
while (sExitStackIndex-- > 0)
|
_exit_stack_lock();
|
||||||
(*sExitStack[sExitStackIndex])();
|
while (--sExitStackInfo.stack_size >= 0)
|
||||||
|
(*sExitStackInfo.exit_stack[sExitStackInfo.stack_size])();
|
||||||
|
_exit_stack_unlock();
|
||||||
|
|
||||||
// close all open files
|
// close all open files
|
||||||
_IO_cleanup();
|
_IO_cleanup();
|
||||||
|
|||||||
@@ -1458,6 +1458,11 @@ unload_library(image_id imageID, bool addOn)
|
|||||||
if (status == B_OK) {
|
if (status == B_OK) {
|
||||||
while ((image = sDisposableImages.head) != NULL) {
|
while ((image = sDisposableImages.head) != NULL) {
|
||||||
// call image fini here...
|
// 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)
|
if (image->term_routine)
|
||||||
((init_term_function)image->term_routine)(image->id);
|
((init_term_function)image->term_routine)(image->id);
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ struct rld_export gRuntimeLoader = {
|
|||||||
test_executable,
|
test_executable,
|
||||||
get_next_image_dependency,
|
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