diff --git a/Userland/Libraries/LibC/stdlib.cpp b/Userland/Libraries/LibC/stdlib.cpp index e9bccaa120..eb393b9bdc 100644 --- a/Userland/Libraries/LibC/stdlib.cpp +++ b/Userland/Libraries/LibC/stdlib.cpp @@ -345,6 +345,8 @@ static T c_str_to_floating_point(char const* str, char** endptr) extern "C" { +void (*__call_fini_functions)(); + void exit(int status) { __cxa_finalize(nullptr); @@ -352,8 +354,7 @@ void exit(int status) if (secure_getenv("LIBC_DUMP_MALLOC_STATS")) serenity_dump_malloc_stats(); - extern void _fini(); - _fini(); + __call_fini_functions(); fflush(nullptr); #ifndef _DYNAMIC_LOADER diff --git a/Userland/Libraries/LibELF/DynamicLinker.cpp b/Userland/Libraries/LibELF/DynamicLinker.cpp index 0313e91120..c4edef1d25 100644 --- a/Userland/Libraries/LibELF/DynamicLinker.cpp +++ b/Userland/Libraries/LibELF/DynamicLinker.cpp @@ -38,12 +38,16 @@ namespace ELF { static HashMap> s_loaders; static DeprecatedString s_main_program_path; + +// Dependencies have to always be added after the object that depends on them in `s_global_objects`. +// This is needed for calling the destructors in the correct order. static OrderedHashMap> s_global_objects; using EntryPointFunction = int (*)(int, char**, char**); using LibCExitFunction = void (*)(int); using DlIteratePhdrCallbackFunction = int (*)(struct dl_phdr_info*, size_t, void*); using DlIteratePhdrFunction = int (*)(DlIteratePhdrCallbackFunction, void*); +using CallFiniFunctionsFunction = void (*)(); extern "C" [[noreturn]] void _invoke_entry(int argc, char** argv, char** envp, EntryPointFunction entry); @@ -65,6 +69,7 @@ static Result __dlclose(void* handle); static Result __dlopen(char const* filename, int flags); static Result __dlsym(void* handle, char const* symbol_name); static Result __dladdr(void const* addr, Dl_info* info); +static void __call_fini_functions(); Optional DynamicLinker::lookup_global_symbol(StringView name) { @@ -305,6 +310,10 @@ static void initialize_libc(DynamicObject& libc) VERIFY(res.has_value()); *((DlAddrFunction*)res.value().address.as_ptr()) = __dladdr; + res = libc.lookup_symbol("__call_fini_functions"sv); + VERIFY(res.has_value()); + *((CallFiniFunctionsFunction*)res.value().address.as_ptr()) = __call_fini_functions; + res = libc.lookup_symbol("__libc_init"sv); VERIFY(res.has_value()); typedef void libc_init_func(); @@ -384,11 +393,9 @@ static Result link_main_library(DeprecatedString const& pa auto loaders = collect_loaders_for_library(path); - for (auto& loader : loaders) { - auto dynamic_object = loader->map(); - if (dynamic_object) - s_global_objects.set(dynamic_object->filepath(), *dynamic_object); - } + // Verify that all objects are already mapped + for (auto& loader : loaders) + VERIFY(!loader->map()); for (auto& loader : loaders) { bool success = loader->link(flags); @@ -594,6 +601,36 @@ static Result __dladdr(void const* addr, Dl_info* info) return {}; } +static void __call_fini_functions() +{ + typedef void (*FiniFunc)(); + + for (auto& it : s_global_objects) { + auto object = it.value; + + if (object->has_fini_array_section()) { + auto fini_array_section = object->fini_array_section(); + + FiniFunc* fini_begin = (FiniFunc*)(fini_array_section.address().as_ptr()); + FiniFunc* fini_end = fini_begin + fini_array_section.entry_count(); + while (fini_begin != fini_end) { + --fini_end; + + // Android sources claim that these can be -1, to be ignored. + // 0 deffiniely shows up. Apparently 0/-1 are valid? Confusing. + if (!*fini_end || ((FlatPtr)*fini_end == (FlatPtr)-1)) + continue; + (*fini_end)(); + } + } + + if (object->has_fini_section()) { + auto fini_function = object->fini_section_function(); + (fini_function)(); + } + } +} + static void read_environment_variables() { for (char** env = s_envp; *env; ++env) { diff --git a/Userland/Libraries/LibELF/DynamicLoader.cpp b/Userland/Libraries/LibELF/DynamicLoader.cpp index 8d99e490c6..c84457ac12 100644 --- a/Userland/Libraries/LibELF/DynamicLoader.cpp +++ b/Userland/Libraries/LibELF/DynamicLoader.cpp @@ -803,7 +803,7 @@ void DynamicLoader::call_object_init_functions() typedef void (*InitFunc)(); if (m_dynamic_object->has_init_section()) { - auto init_function = (InitFunc)(m_dynamic_object->init_section().address().as_ptr()); + auto init_function = m_dynamic_object->init_section_function(); (init_function)(); } diff --git a/Userland/Libraries/LibELF/DynamicObject.cpp b/Userland/Libraries/LibELF/DynamicObject.cpp index a7a377e524..52b38c24da 100644 --- a/Userland/Libraries/LibELF/DynamicObject.cpp +++ b/Userland/Libraries/LibELF/DynamicObject.cpp @@ -377,6 +377,12 @@ DynamicObject::InitializationFunction DynamicObject::init_section_function() con return (InitializationFunction)init_section().address().as_ptr(); } +DynamicObject::FinalizationFunction DynamicObject::fini_section_function() const +{ + VERIFY(has_fini_section()); + return (FinalizationFunction)fini_section().address().as_ptr(); +} + char const* DynamicObject::name_for_dtag(ElfW(Sword) d_tag) { switch (d_tag) { diff --git a/Userland/Libraries/LibELF/DynamicObject.h b/Userland/Libraries/LibELF/DynamicObject.h index 6276e771b3..d6785f779d 100644 --- a/Userland/Libraries/LibELF/DynamicObject.h +++ b/Userland/Libraries/LibELF/DynamicObject.h @@ -245,14 +245,19 @@ public: Symbol symbol(unsigned) const; typedef void (*InitializationFunction)(); + typedef void (*FinalizationFunction)(); typedef ElfW(Addr) (*IfuncResolver)(); bool has_init_section() const { return m_init_offset != 0; } bool has_init_array_section() const { return m_init_array_offset != 0; } Section init_section() const; InitializationFunction init_section_function() const; - Section fini_section() const; Section init_array_section() const; + + bool has_fini_section() const { return m_fini_offset != 0; } + bool has_fini_array_section() const { return m_fini_array_offset != 0; } + Section fini_section() const; + FinalizationFunction fini_section_function() const; Section fini_array_section() const; HashSection hash_section() const