9a9b91c940
It's finally done.. Signed-off-by: kaguya <vpshinomiya@protonmail.com>
89 lines
2.4 KiB
C++
89 lines
2.4 KiB
C++
|
|
// for memcpy()
|
|
#include <string.h>
|
|
|
|
#include <bits/ensure.h>
|
|
#include <mlibc/allocator.hpp>
|
|
|
|
#include <frg/eternal.hpp>
|
|
#include <frg/vector.hpp>
|
|
|
|
struct ExitHandler {
|
|
void (*function)(void *);
|
|
void *argument;
|
|
void *dsoHandle;
|
|
};
|
|
|
|
using ExitQueue = frg::vector<ExitHandler, MemoryAllocator>;
|
|
|
|
ExitQueue &getExitQueue() {
|
|
// use frg::eternal to prevent the compiler from scheduling the destructor
|
|
// by generating a call to __cxa_atexit().
|
|
static frg::eternal<ExitQueue> singleton(getAllocator());
|
|
return singleton.get();
|
|
}
|
|
|
|
extern "C" int __cxa_atexit(void (*function)(void *), void *argument, void *handle) {
|
|
ExitHandler handler;
|
|
handler.function = function;
|
|
handler.argument = argument;
|
|
handler.dsoHandle = handle;
|
|
getExitQueue().push(handler);
|
|
return 0;
|
|
}
|
|
|
|
extern "C" void __dlapi_exit();
|
|
|
|
extern "C" void __cxa_finalize(void *dso) {
|
|
ExitQueue &eq = getExitQueue();
|
|
for (size_t i = eq.size(); i > 0; i--) {
|
|
auto &handler = eq[i - 1];
|
|
if (!handler.function)
|
|
continue;
|
|
|
|
if (!dso || handler.dsoHandle == dso) {
|
|
handler.function(handler.argument);
|
|
handler.function = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
// In static builds, these should be provided by the crtbegin.o/crtend.o that
|
|
// is linked into the executable.
|
|
#ifndef MLIBC_STATIC_BUILD
|
|
// This is referenced by the compiler when generating constructors for global
|
|
// C++ objects so that it can call __cxa_finalize with a unique argument.
|
|
extern "C" { [[gnu::visibility("hidden")]] void *__dso_handle; }
|
|
#else
|
|
extern "C" void *__dso_handle;
|
|
#endif
|
|
|
|
[[gnu::destructor]] void __mlibc_do_destructors() {
|
|
// In normal programs this call to __cxa_finalize is provided by libgcc.
|
|
__cxa_finalize(&__dso_handle);
|
|
}
|
|
|
|
void __mlibc_do_finalize() {
|
|
// Invoke any handlers registered with atexit (NOT associated with a DSO).
|
|
// Note that we deliberately do not invoke other handlers here, since
|
|
// that would destroy mlibc's global objects including stdout and flushing
|
|
// open FILEs, but we'd like those to be available to [[gnu::destructor]]
|
|
// functions which we invoke below.
|
|
ExitQueue &eq = getExitQueue();
|
|
for (size_t i = eq.size(); i > 0; i--) {
|
|
auto &handler = eq[i - 1];
|
|
if (!handler.function)
|
|
continue;
|
|
|
|
if (!handler.dsoHandle) {
|
|
handler.function(handler.argument);
|
|
handler.function = nullptr;
|
|
}
|
|
}
|
|
|
|
// Call fini/fini_array functions of each loaded object. This is necessary
|
|
// to implement [[gnu::destructor]]. Note that C++ applications will call
|
|
// __cxa_finalize from here.
|
|
__dlapi_exit();
|
|
}
|