From 39f889d591d984e0311e67d5422804aa6fc30806 Mon Sep 17 00:00:00 2001 From: Qwinci <32550582+Qwinci@users.noreply.github.com> Date: Wed, 17 Jul 2024 14:49:10 +0300 Subject: [PATCH] options/rtld: Call fini functions on exit --- options/internal/gcc/initfini.cpp | 13 +++- options/lsb/generic/dso_exit.cpp | 31 +++++++++- options/rtld/generic/linker.cpp | 98 ++++++++++++++++++++++++++++--- options/rtld/generic/linker.hpp | 13 +++- options/rtld/generic/main.cpp | 20 +++++-- tests/rtld/destroy/meson.build | 0 tests/rtld/destroy/test.c | 9 +++ tests/rtld/meson.build | 1 + 8 files changed, 169 insertions(+), 16 deletions(-) create mode 100644 tests/rtld/destroy/meson.build create mode 100644 tests/rtld/destroy/test.c diff --git a/options/internal/gcc/initfini.cpp b/options/internal/gcc/initfini.cpp index 61f41a747..6db45b68f 100644 --- a/options/internal/gcc/initfini.cpp +++ b/options/internal/gcc/initfini.cpp @@ -10,6 +10,8 @@ typedef void (*InitPtr)(); extern InitPtr __CTOR_LIST__ [[ gnu::visibility("hidden") ]] []; extern InitPtr __CTOR_END__ [[ gnu::visibility("hidden") ]] []; +extern InitPtr __DTOR_LIST__ [[ gnu::visibility("hidden") ]] []; +extern InitPtr __DTOR_END__ [[ gnu::visibility("hidden") ]] []; extern "C" [[ gnu::visibility("hidden") ]] void __mlibc_do_ctors() { const size_t n = __CTOR_END__ - __CTOR_LIST__; @@ -24,6 +26,13 @@ extern "C" [[ gnu::visibility("hidden") ]] void __mlibc_do_ctors() { } extern "C" [[ gnu::visibility("hidden") ]] void __mlibc_do_dtors() { - mlibc::sys_libc_log("__mlibc_do_dtors() called"); -} + const size_t n = __DTOR_END__ - __DTOR_LIST__; + if(!n) + return; + + size_t num = frg::min(n - 1, size_t(__DTOR_LIST__[0])); + for(size_t i = num; i >= 1; i--) { + __DTOR_LIST__[i](); + } +} diff --git a/options/lsb/generic/dso_exit.cpp b/options/lsb/generic/dso_exit.cpp index b8b239d2c..8a99a2018 100644 --- a/options/lsb/generic/dso_exit.cpp +++ b/options/lsb/generic/dso_exit.cpp @@ -32,11 +32,40 @@ extern "C" int __cxa_atexit(void (*function)(void *), void *argument, void *hand return 0; } +extern "C" void __dlapi_exit(); + +extern "C" void __cxa_finalize(void *dso) { + ExitQueue &eq = getExitQueue(); + if(!dso) { + for(size_t i = eq.size(); i > 0; i--) { + auto handler = &eq[i - 1]; + if(!handler->function) + continue; + + handler->function(handler->argument); + handler->function = nullptr; + } + }else { + for(size_t i = eq.size(); i > 0; i--) { + auto handler = &eq[i - 1]; + if(handler->dsoHandle != dso || !handler->function) + continue; + + handler->function(handler->argument); + handler->function = nullptr; + } + } +} + void __mlibc_do_finalize() { ExitQueue &eq = getExitQueue(); for(size_t i = eq.size(); i > 0; i--) { auto handler = &eq[i - 1]; + if(handler->dsoHandle || !handler->function) + continue; handler->function(handler->argument); + handler->function = nullptr; } -} + __dlapi_exit(); +} diff --git a/options/rtld/generic/linker.cpp b/options/rtld/generic/linker.cpp index dfe2b61a7..894ad4583 100644 --- a/options/rtld/generic/linker.cpp +++ b/options/rtld/generic/linker.cpp @@ -54,6 +54,8 @@ extern frg::manual_box> preloads; #if MLIBC_STATIC_BUILD extern "C" size_t __init_array_start[]; extern "C" size_t __init_array_end[]; +extern "C" size_t __fini_array_start[]; +extern "C" size_t __fini_array_end[]; extern "C" size_t __preinit_array_start[]; extern "C" size_t __preinit_array_end[]; #endif @@ -102,7 +104,8 @@ uintptr_t alignUp(uintptr_t address, size_t align) { ObjectRepository::ObjectRepository() : loadedObjects{getAllocator()}, - _nameMap{frg::hash{}, getAllocator()} {} + _nameMap{frg::hash{}, getAllocator()}, + _destructQueue{getAllocator()} {} SharedObject *ObjectRepository::injectObjectFromDts(frg::string_view name, frg::string path, uintptr_t base_address, @@ -151,6 +154,9 @@ SharedObject *ObjectRepository::injectStaticObject(frg::string_view name, object->initArray = reinterpret_cast(__init_array_start); object->initArraySize = static_cast((uintptr_t)__init_array_end - (uintptr_t)__init_array_start); + object->finiArray = reinterpret_cast(__fini_array_start); + object->finiArraySize = static_cast((uintptr_t)__fini_array_end - + (uintptr_t)__fini_array_start); object->preInitArray = reinterpret_cast(__preinit_array_start); object->preInitArraySize = static_cast((uintptr_t)__preinit_array_end - (uintptr_t)__preinit_array_start); @@ -356,6 +362,19 @@ SharedObject *ObjectRepository::findLoadedObject(frg::string_view name) { return nullptr; } +void ObjectRepository::addObjectToDestructQueue(SharedObject *object) { + _destructQueue.push_back(object); +} + +void doDestruct(SharedObject *object); + +void ObjectRepository::destructObjects() { + for (size_t i = _destructQueue.size(); i > 0; i--) { + doDestruct(_destructQueue[i - 1]); + } + _destructQueue.clear(); +} + // -------------------------------------------------------- // ObjectRepository: Fetching methods. // -------------------------------------------------------- @@ -699,13 +718,24 @@ void ObjectRepository::_parseDynamic(SharedObject *object) { if(dynamic->d_un.d_ptr != 0) object->initPtr = (InitFuncPtr)(object->baseAddress + dynamic->d_un.d_ptr); break; + case DT_FINI: + if(dynamic->d_un.d_ptr != 0) + object->finiPtr = (InitFuncPtr)(object->baseAddress + dynamic->d_un.d_ptr); + break; case DT_INIT_ARRAY: if(dynamic->d_un.d_ptr != 0) object->initArray = (InitFuncPtr *)(object->baseAddress + dynamic->d_un.d_ptr); break; + case DT_FINI_ARRAY: + if(dynamic->d_un.d_ptr != 0) + object->finiArray = (InitFuncPtr *)(object->baseAddress + dynamic->d_un.d_ptr); + break; case DT_INIT_ARRAYSZ: object->initArraySize = dynamic->d_un.d_val; break; + case DT_FINI_ARRAYSZ: + object->finiArraySize = dynamic->d_un.d_val; + break; case DT_PREINIT_ARRAY: if(dynamic->d_un.d_ptr != 0) { // Only the main object is allowed pre-initializers. @@ -730,7 +760,6 @@ void ObjectRepository::_parseDynamic(SharedObject *object) { break; // ignore unimportant tags case DT_NEEDED: // we handle this later - case DT_FINI: case DT_FINI_ARRAY: case DT_FINI_ARRAYSZ: case DT_RELA: case DT_RELASZ: case DT_RELAENT: case DT_RELACOUNT: case DT_REL: case DT_RELSZ: case DT_RELENT: case DT_RELCOUNT: case DT_RELR: case DT_RELRSZ: case DT_RELRENT: @@ -924,9 +953,22 @@ void doInitialize(SharedObject *object) { __ensure(object->wasLinked); __ensure(!object->wasInitialized); - // if the object has dependencies we initialize them first - for(size_t i = 0; i < object->dependencies.size(); i++) - __ensure(object->dependencies[i]->wasInitialized); + // If the object has dependencies we initialize them first + // except in the case of a circular dependency. + for(auto dep : object->dependencies) { + bool circular = false; + for(auto dep2 : dep->dependencies) { + if(dep2 == object) { + circular = true; + break; + } + } + + if(circular) + continue; + + __ensure(dep->wasInitialized); + } if(verbose) mlibc::infoLogger() << "rtld: Initialize " << object->name << frg::endlog; @@ -947,6 +989,46 @@ void doInitialize(SharedObject *object) { object->wasInitialized = true; } +void doDestruct(SharedObject *object) { + if(!object->wasInitialized || object->wasDestroyed) + return; + + // If the object has dependencies they are destroyed after this object + // except in the case of a circular dependency. + for(auto dep : object->dependencies) { + bool circular = false; + for(auto dep2 : dep->dependencies) { + if(dep2 == object) { + circular = true; + break; + } + } + + if(circular) + continue; + + __ensure(!dep->wasDestroyed); + } + + if(verbose) + mlibc::infoLogger() << "rtld: Destruct " << object->name << frg::endlog; + + if(verbose) + mlibc::infoLogger() << "rtld: Running DT_FINI_ARRAY functions" << frg::endlog; + __ensure((object->finiArraySize % sizeof(InitFuncPtr)) == 0); + for(size_t i = object->finiArraySize / sizeof(InitFuncPtr); i > 0; i--) + object->finiArray[i - 1](); + + if(verbose) + mlibc::infoLogger() << "rtld: Running DT_FINI function" << frg::endlog; + if(object->finiPtr != nullptr) + object->finiPtr(); + + if(verbose) + mlibc::infoLogger() << "rtld: Object destruction complete" << frg::endlog; + object->wasDestroyed = true; +} + // -------------------------------------------------------- // RuntimeTlsMap // -------------------------------------------------------- @@ -1500,7 +1582,7 @@ void Loader::_buildTlsMaps() { } } -void Loader::initObjects() { +void Loader::initObjects(ObjectRepository *repository) { initTlsObjects(mlibc::get_current_tcb(), _linkBfs, true); if (_mainExecutable && _mainExecutable->preInitArray) { @@ -1522,8 +1604,10 @@ void Loader::initObjects() { } for(auto object : _initQueue) { - if(!object->wasInitialized) + if(!object->wasInitialized) { doInitialize(object); + repository->addObjectToDestructQueue(object); + } } } diff --git a/options/rtld/generic/linker.hpp b/options/rtld/generic/linker.hpp index b58ad6e3f..6e6cd4dac 100644 --- a/options/rtld/generic/linker.hpp +++ b/options/rtld/generic/linker.hpp @@ -69,6 +69,9 @@ struct ObjectRepository { SharedObject *findLoadedObject(frg::string_view name); + void addObjectToDestructQueue(SharedObject *object); + void destructObjects(); + // Used by dl_iterate_phdr: stores objects in the order they are loaded. frg::vector loadedObjects; @@ -86,6 +89,9 @@ struct ObjectRepository { frg::hash_map, MemoryAllocator> _nameMap; + + // Used for destructing the objects, stores all the objects in the order they are initialized. + frg::vector _destructQueue; }; // -------------------------------------------------------- @@ -148,9 +154,12 @@ struct SharedObject { // object initialization information InitFuncPtr initPtr = nullptr; + InitFuncPtr finiPtr = nullptr; InitFuncPtr *initArray = nullptr; + InitFuncPtr *finiArray = nullptr; InitFuncPtr *preInitArray = nullptr; size_t initArraySize = 0; + size_t finiArraySize = 0; size_t preInitArraySize = 0; @@ -190,6 +199,8 @@ struct SharedObject { bool onInitStack; bool wasInitialized; + bool wasDestroyed = false; + // PHDR related stuff, we only set these for the main executable void *phdrPointer = nullptr; size_t phdrEntrySize = 0; @@ -372,7 +383,7 @@ struct Loader { void _processRelocations(Relocation &rel); public: - void initObjects(); + void initObjects(ObjectRepository *repository); private: void _scheduleInit(SharedObject *object); diff --git a/options/rtld/generic/main.cpp b/options/rtld/generic/main.cpp index 9219ef0ff..db6979e2e 100644 --- a/options/rtld/generic/main.cpp +++ b/options/rtld/generic/main.cpp @@ -303,6 +303,8 @@ extern "C" void *interpreterMain(uintptr_t *entry_stack) { case DT_RELRSZ: case DT_RELRENT: case DT_PLTGOT: + case DT_FLAGS: + case DT_FLAGS_1: continue; default: mlibc::panicLogger() << "rtld: unexpected dynamic entry " << ent->d_tag << " in program interpreter" << frg::endlog; @@ -453,9 +455,13 @@ extern "C" void *interpreterMain(uintptr_t *entry_stack) { auto ldso = initialRepository->injectObjectFromDts(ldso_soname, frg::string { getAllocator() }, ldso_base, _DYNAMIC, 1); - ldso->phdrPointer = phdr_pointer; - ldso->phdrCount = phdr_count; - ldso->phdrEntrySize = phdr_entry_size; + + auto ldso_ehdr = reinterpret_cast(__ehdr_start); + auto ldso_phdr = reinterpret_cast(ldso_base + ldso_ehdr->e_phoff); + + ldso->phdrPointer = ldso_phdr; + ldso->phdrCount = ldso_ehdr->e_phnum; + ldso->phdrEntrySize = ldso_ehdr->e_phentsize; // TODO: support non-zero base addresses? executableSO = initialRepository->injectObjectFromPhdrs(execfn, @@ -491,7 +497,7 @@ extern "C" void *interpreterMain(uintptr_t *entry_stack) { globalDebugInterface.state = 0; dl_debug_state(); - linker.initObjects(); + linker.initObjects(initialRepository.get()); if(logEntryExit) mlibc::infoLogger() << "Leaving ld.so, jump to " @@ -522,6 +528,10 @@ const mlibc::RtldConfig &__dlapi_get_config() { return rtldConfig; } +extern "C" [[ gnu::visibility("default") ]] void __dlapi_exit() { + initialRepository->destructObjects(); +} + #if __MLIBC_POSIX_OPTION extern "C" [[ gnu::visibility("default") ]] @@ -594,7 +604,7 @@ void *__dlapi_open(const char *file, int flags, void *returnAddress) { Loader linker{object->localScope, nullptr, false, rts}; linker.linkObjects(object); - linker.initObjects(); + linker.initObjects(initialRepository.get()); } dl_debug_state(); diff --git a/tests/rtld/destroy/meson.build b/tests/rtld/destroy/meson.build new file mode 100644 index 000000000..e69de29bb diff --git a/tests/rtld/destroy/test.c b/tests/rtld/destroy/test.c new file mode 100644 index 000000000..62da031cb --- /dev/null +++ b/tests/rtld/destroy/test.c @@ -0,0 +1,9 @@ +#include + +__attribute__((destructor)) void destroy() { + _exit(0); +} + +int main() { + return 1; +} diff --git a/tests/rtld/meson.build b/tests/rtld/meson.build index 2f417e0a0..13f465cb2 100644 --- a/tests/rtld/meson.build +++ b/tests/rtld/meson.build @@ -6,6 +6,7 @@ rtld_test_cases = [ 'rtld_next', 'soname', 'preinit', + 'destroy', 'scope1', 'scope2', 'scope3',