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
Notes: sideshowbarker 2024-07-17 06:35:23 +09:00
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" {
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

View file

@ -38,12 +38,16 @@ namespace ELF {
static HashMap<DeprecatedString, NonnullRefPtr<ELF::DynamicLoader>> 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<DeprecatedString, NonnullRefPtr<ELF::DynamicObject>> 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<void, DlErrorMessage> __dlclose(void* handle);
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> __dladdr(void const* addr, Dl_info* info);
static void __call_fini_functions();
Optional<DynamicObject::SymbolLookupResult> 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<void, DlErrorMessage> 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<void, DlErrorMessage> __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) {

View file

@ -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)();
}

View file

@ -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) {

View file

@ -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