LibC+LibELF: Correctly call destructors on exit()

We currently don't call any DT_FINI_ARRAY functions, so change that.

The call to `_fini` in `exit` is unnecessary, as we now call the
function referenced by DT_FINI in `__call_fini_functions`.
This commit is contained in:
Sönke Holz 2023-10-03 20:57:46 +02:00 committed by Daniel Bertalan
parent dcff48356f
commit 0bff1f61b6
5 changed files with 58 additions and 9 deletions

View file

@ -345,6 +345,8 @@ static T c_str_to_floating_point(char const* str, char** endptr)
extern "C" { extern "C" {
void (*__call_fini_functions)();
void exit(int status) void exit(int status)
{ {
__cxa_finalize(nullptr); __cxa_finalize(nullptr);
@ -352,8 +354,7 @@ void exit(int status)
if (secure_getenv("LIBC_DUMP_MALLOC_STATS")) if (secure_getenv("LIBC_DUMP_MALLOC_STATS"))
serenity_dump_malloc_stats(); serenity_dump_malloc_stats();
extern void _fini(); __call_fini_functions();
_fini();
fflush(nullptr); fflush(nullptr);
#ifndef _DYNAMIC_LOADER #ifndef _DYNAMIC_LOADER

View file

@ -38,12 +38,16 @@ namespace ELF {
static HashMap<DeprecatedString, NonnullRefPtr<ELF::DynamicLoader>> s_loaders; static HashMap<DeprecatedString, NonnullRefPtr<ELF::DynamicLoader>> s_loaders;
static DeprecatedString s_main_program_path; 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<DeprecatedString, NonnullRefPtr<ELF::DynamicObject>> s_global_objects; static OrderedHashMap<DeprecatedString, NonnullRefPtr<ELF::DynamicObject>> s_global_objects;
using EntryPointFunction = int (*)(int, char**, char**); using EntryPointFunction = int (*)(int, char**, char**);
using LibCExitFunction = void (*)(int); using LibCExitFunction = void (*)(int);
using DlIteratePhdrCallbackFunction = int (*)(struct dl_phdr_info*, size_t, void*); using DlIteratePhdrCallbackFunction = int (*)(struct dl_phdr_info*, size_t, void*);
using DlIteratePhdrFunction = int (*)(DlIteratePhdrCallbackFunction, void*); using DlIteratePhdrFunction = int (*)(DlIteratePhdrCallbackFunction, void*);
using CallFiniFunctionsFunction = void (*)();
extern "C" [[noreturn]] void _invoke_entry(int argc, char** argv, char** envp, EntryPointFunction entry); extern "C" [[noreturn]] void _invoke_entry(int argc, char** argv, char** envp, EntryPointFunction entry);
@ -65,6 +69,7 @@ static Result<void, DlErrorMessage> __dlclose(void* handle);
static Result<void*, DlErrorMessage> __dlopen(char const* filename, int flags); static Result<void*, DlErrorMessage> __dlopen(char const* filename, int flags);
static Result<void*, DlErrorMessage> __dlsym(void* handle, char const* symbol_name); static Result<void*, DlErrorMessage> __dlsym(void* handle, char const* symbol_name);
static Result<void, DlErrorMessage> __dladdr(void const* addr, Dl_info* info); static Result<void, DlErrorMessage> __dladdr(void const* addr, Dl_info* info);
static void __call_fini_functions();
Optional<DynamicObject::SymbolLookupResult> DynamicLinker::lookup_global_symbol(StringView name) Optional<DynamicObject::SymbolLookupResult> DynamicLinker::lookup_global_symbol(StringView name)
{ {
@ -305,6 +310,10 @@ static void initialize_libc(DynamicObject& libc)
VERIFY(res.has_value()); VERIFY(res.has_value());
*((DlAddrFunction*)res.value().address.as_ptr()) = __dladdr; *((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); res = libc.lookup_symbol("__libc_init"sv);
VERIFY(res.has_value()); VERIFY(res.has_value());
typedef void libc_init_func(); typedef void libc_init_func();
@ -384,11 +393,9 @@ static Result<void, DlErrorMessage> link_main_library(DeprecatedString const& pa
auto loaders = collect_loaders_for_library(path); auto loaders = collect_loaders_for_library(path);
for (auto& loader : loaders) { // Verify that all objects are already mapped
auto dynamic_object = loader->map(); for (auto& loader : loaders)
if (dynamic_object) VERIFY(!loader->map());
s_global_objects.set(dynamic_object->filepath(), *dynamic_object);
}
for (auto& loader : loaders) { for (auto& loader : loaders) {
bool success = loader->link(flags); bool success = loader->link(flags);
@ -594,6 +601,36 @@ static Result<void, DlErrorMessage> __dladdr(void const* addr, Dl_info* info)
return {}; 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() static void read_environment_variables()
{ {
for (char** env = s_envp; *env; ++env) { for (char** env = s_envp; *env; ++env) {

View file

@ -803,7 +803,7 @@ void DynamicLoader::call_object_init_functions()
typedef void (*InitFunc)(); typedef void (*InitFunc)();
if (m_dynamic_object->has_init_section()) { 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)(); (init_function)();
} }

View file

@ -377,6 +377,12 @@ DynamicObject::InitializationFunction DynamicObject::init_section_function() con
return (InitializationFunction)init_section().address().as_ptr(); 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) char const* DynamicObject::name_for_dtag(ElfW(Sword) d_tag)
{ {
switch (d_tag) { switch (d_tag) {

View file

@ -245,14 +245,19 @@ public:
Symbol symbol(unsigned) const; Symbol symbol(unsigned) const;
typedef void (*InitializationFunction)(); typedef void (*InitializationFunction)();
typedef void (*FinalizationFunction)();
typedef ElfW(Addr) (*IfuncResolver)(); typedef ElfW(Addr) (*IfuncResolver)();
bool has_init_section() const { return m_init_offset != 0; } bool has_init_section() const { return m_init_offset != 0; }
bool has_init_array_section() const { return m_init_array_offset != 0; } bool has_init_array_section() const { return m_init_array_offset != 0; }
Section init_section() const; Section init_section() const;
InitializationFunction init_section_function() const; InitializationFunction init_section_function() const;
Section fini_section() const;
Section init_array_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; Section fini_array_section() const;
HashSection hash_section() const HashSection hash_section() const