Skip to content

Commit

Permalink
Merge pull request #1092 from Qwinci/fini
Browse files Browse the repository at this point in the history
options/rtld: Call fini functions on exit
  • Loading branch information
mintsuki authored Oct 13, 2024
2 parents a89e933 + 39f889d commit 2f19345
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 16 deletions.
13 changes: 11 additions & 2 deletions options/internal/gcc/initfini.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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__;
Expand All @@ -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]();
}
}
31 changes: 30 additions & 1 deletion options/lsb/generic/dso_exit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
98 changes: 91 additions & 7 deletions options/rtld/generic/linker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ extern frg::manual_box<frg::vector<frg::string_view, MemoryAllocator>> 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
Expand Down Expand Up @@ -102,7 +104,8 @@ uintptr_t alignUp(uintptr_t address, size_t align) {

ObjectRepository::ObjectRepository()
: loadedObjects{getAllocator()},
_nameMap{frg::hash<frg::string_view>{}, getAllocator()} {}
_nameMap{frg::hash<frg::string_view>{}, getAllocator()},
_destructQueue{getAllocator()} {}

SharedObject *ObjectRepository::injectObjectFromDts(frg::string_view name,
frg::string<MemoryAllocator> path, uintptr_t base_address,
Expand Down Expand Up @@ -151,6 +154,9 @@ SharedObject *ObjectRepository::injectStaticObject(frg::string_view name,
object->initArray = reinterpret_cast<InitFuncPtr*>(__init_array_start);
object->initArraySize = static_cast<size_t>((uintptr_t)__init_array_end -
(uintptr_t)__init_array_start);
object->finiArray = reinterpret_cast<InitFuncPtr*>(__fini_array_start);
object->finiArraySize = static_cast<size_t>((uintptr_t)__fini_array_end -
(uintptr_t)__fini_array_start);
object->preInitArray = reinterpret_cast<InitFuncPtr*>(__preinit_array_start);
object->preInitArraySize = static_cast<size_t>((uintptr_t)__preinit_array_end -
(uintptr_t)__preinit_array_start);
Expand Down Expand Up @@ -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.
// --------------------------------------------------------
Expand Down Expand Up @@ -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.
Expand All @@ -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:
Expand Down Expand Up @@ -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;
Expand All @@ -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
// --------------------------------------------------------
Expand Down Expand Up @@ -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) {
Expand All @@ -1522,8 +1604,10 @@ void Loader::initObjects() {
}

for(auto object : _initQueue) {
if(!object->wasInitialized)
if(!object->wasInitialized) {
doInitialize(object);
repository->addObjectToDestructQueue(object);
}
}
}

Expand Down
13 changes: 12 additions & 1 deletion options/rtld/generic/linker.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<SharedObject *, MemoryAllocator> loadedObjects;

Expand All @@ -86,6 +89,9 @@ struct ObjectRepository {

frg::hash_map<frg::string_view, SharedObject *,
frg::hash<frg::string_view>, MemoryAllocator> _nameMap;

// Used for destructing the objects, stores all the objects in the order they are initialized.
frg::vector<SharedObject *, MemoryAllocator> _destructQueue;
};

// --------------------------------------------------------
Expand Down Expand Up @@ -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;


Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -372,7 +383,7 @@ struct Loader {
void _processRelocations(Relocation &rel);

public:
void initObjects();
void initObjects(ObjectRepository *repository);

private:
void _scheduleInit(SharedObject *object);
Expand Down
20 changes: 15 additions & 5 deletions options/rtld/generic/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -453,9 +455,13 @@ extern "C" void *interpreterMain(uintptr_t *entry_stack) {
auto ldso = initialRepository->injectObjectFromDts(ldso_soname,
frg::string<MemoryAllocator> { getAllocator() },
ldso_base, _DYNAMIC, 1);
ldso->phdrPointer = phdr_pointer;
ldso->phdrCount = phdr_count;
ldso->phdrEntrySize = phdr_entry_size;

auto ldso_ehdr = reinterpret_cast<elf_ehdr *>(__ehdr_start);
auto ldso_phdr = reinterpret_cast<elf_phdr *>(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,
Expand Down Expand Up @@ -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 "
Expand Down Expand Up @@ -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") ]]
Expand Down Expand Up @@ -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();
Expand Down
Empty file added tests/rtld/destroy/meson.build
Empty file.
9 changes: 9 additions & 0 deletions tests/rtld/destroy/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include <unistd.h>

__attribute__((destructor)) void destroy() {
_exit(0);
}

int main() {
return 1;
}
1 change: 1 addition & 0 deletions tests/rtld/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ rtld_test_cases = [
'rtld_next',
'soname',
'preinit',
'destroy',
'scope1',
'scope2',
'scope3',
Expand Down

0 comments on commit 2f19345

Please sign in to comment.