Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

options/rtld: Call fini functions on exit #1092

Merged
merged 1 commit into from
Oct 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading