diff --git a/headers/private/kernel/user_runtime.h b/headers/private/kernel/user_runtime.h index e9b367cf58..e8a61b2fab 100644 --- a/headers/private/kernel/user_runtime.h +++ b/headers/private/kernel/user_runtime.h @@ -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; }; diff --git a/src/system/libroot/libroot_init.c b/src/system/libroot/libroot_init.c index c66dcfb9ac..dab4072388 100644 --- a/src/system/libroot/libroot_init.c +++ b/src/system/libroot/libroot_init.c @@ -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(); diff --git a/src/system/libroot/posix/stdlib/exit.c b/src/system/libroot/posix/stdlib/exit.c index 465302acdc..c1bf1832bb 100644 --- a/src/system/libroot/posix/stdlib/exit.c +++ b/src/system/libroot/posix/stdlib/exit.c @@ -13,13 +13,91 @@ #include #include #include +#include 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(); diff --git a/src/system/runtime_loader/elf.cpp b/src/system/runtime_loader/elf.cpp index 819fbe9ac6..b1535e5245 100644 --- a/src/system/runtime_loader/elf.cpp +++ b/src/system/runtime_loader/elf.cpp @@ -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); diff --git a/src/system/runtime_loader/export.c b/src/system/runtime_loader/export.c index 25b3815950..795a0aea9d 100644 --- a/src/system/runtime_loader/export.c +++ b/src/system/runtime_loader/export.c @@ -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 };