#include <SupportDefs.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <new>
#include <util/DoublyLinkedList.h>
#include <util/SinglyLinkedList.h>
#include <libroot_private.h>
#include <locks.h>
#include <runtime_loader.h>
#include <syscalls.h>
extern "C" void _IO_cleanup(void);
extern "C" void _thread_do_exit_work(void);
struct AtExitInfoBlock;
struct AtExitInfo : SinglyLinkedListLinkImpl<AtExitInfo> {
AtExitInfoBlock* block;
void (*hook)(void*);
void* data;
void* dsoHandle;
};
typedef SinglyLinkedList<AtExitInfo> AtExitInfoList;
struct AtExitInfoBlock : DoublyLinkedListLinkImpl<AtExitInfoBlock> {
AtExitInfoBlock()
:
fFirstUnused(0)
{
}
bool IsEmpty() const
{
return fFirstUnused == ATEXIT_MAX && fFreeList.IsEmpty();
}
AtExitInfo* AllocateInfo()
{
if (fFirstUnused < ATEXIT_MAX) {
AtExitInfo* info = &fInfos[fFirstUnused++];
info->block = this;
return info;
}
return fFreeList.RemoveHead();
}
void FreeInfo(AtExitInfo* info)
{
fFreeList.Add(info);
}
private:
AtExitInfo fInfos[ATEXIT_MAX];
uint32 fFirstUnused;
AtExitInfoList fFreeList;
};
typedef DoublyLinkedList<AtExitInfoBlock> AtExitInfoBlockList;
struct DSOPredicate {
DSOPredicate(void* dsoHandle)
:
fDSOHandle(dsoHandle)
{
}
inline bool operator()(const AtExitInfo* info) const
{
return info->dsoHandle == fDSOHandle;
}
private:
void* fDSOHandle;
};
struct AddressRangePredicate {
AddressRangePredicate(addr_t start, size_t size)
:
fStart(start),
fEnd(start + size - 1)
{
}
inline bool operator()(const AtExitInfo* info) const
{
addr_t address = (addr_t)info->hook;
return info->dsoHandle == NULL && address >= fStart && address <= fEnd;
}
private:
addr_t fStart;
addr_t fEnd;
};
static AtExitInfoBlock sInitialAtExitInfoBlock;
static AtExitInfoBlockList sAtExitInfoBlocks;
static AtExitInfoList sAtExitInfoStack;
static recursive_lock sAtExitLock = RECURSIVE_LOCK_INITIALIZER("at exit lock");
static void inline
_exit_stack_lock()
{
recursive_lock_lock(&sAtExitLock);
}
static void inline
_exit_stack_unlock()
{
recursive_lock_unlock(&sAtExitLock);
}
template<typename Predicate>
static void
call_exit_hooks(const Predicate& predicate)
{
_exit_stack_lock();
AtExitInfo* previousInfo = NULL;
AtExitInfo* info = sAtExitInfoStack.Head();
while (info != NULL) {
AtExitInfo* nextInfo = sAtExitInfoStack.GetNext(info);
if (predicate(info)) {
sAtExitInfoStack.Remove(previousInfo, info);
info->hook(info->data);
if (info->block->IsEmpty())
sAtExitInfoBlocks.Add(info->block);
info->block->FreeInfo(info);
} else
previousInfo = info;
info = nextInfo;
}
_exit_stack_unlock();
}
extern "C" int
__cxa_atexit(void (*hook)(void*), void* data, void* dsoHandle)
{
if (hook == NULL)
return -1;
_exit_stack_lock();
AtExitInfoBlock* block = sAtExitInfoBlocks.Head();
if (block == NULL) {
if (!sInitialAtExitInfoBlock.IsEmpty()) {
block = &sInitialAtExitInfoBlock;
} else {
block = new(std::nothrow) AtExitInfoBlock;
if (block == NULL) {
_exit_stack_unlock();
return -1;
}
}
sAtExitInfoBlocks.Add(block);
}
AtExitInfo* info = block->AllocateInfo();
if (block->IsEmpty())
sAtExitInfoBlocks.Remove(block);
info->hook = hook;
info->data = data;
info->dsoHandle = dsoHandle;
sAtExitInfoStack.Add(info);
_exit_stack_unlock();
return 0;
}
extern "C" void
__cxa_finalize(void* dsoHandle)
{
if (dsoHandle == NULL) {
_exit_stack_lock();
while (AtExitInfo* info = sAtExitInfoStack.RemoveHead()) {
info->hook(info->data);
if (info->block->IsEmpty())
sAtExitInfoBlocks.Add(info->block);
info->block->FreeInfo(info);
}
_exit_stack_unlock();
} else {
call_exit_hooks(DSOPredicate(dsoHandle));
}
}
void
_call_atexit_hooks_for_range(addr_t start, addr_t size)
{
call_exit_hooks(AddressRangePredicate(start, size));
}
void
abort()
{
fprintf(stderr, "Abort\n");
struct sigaction signalAction;
if (sigaction(SIGABRT, NULL, &signalAction) == 0
&& signalAction.sa_handler == SIG_DFL) {
debugger("abort() called");
}
raise(SIGABRT);
exit(EXIT_FAILURE);
}
int
atexit(void (*func)(void))
{
return __cxa_atexit((void (*)(void*))func, NULL, NULL);
}
void
exit(int status)
{
__cxa_finalize(NULL);
_thread_do_exit_work();
_IO_cleanup();
__gRuntimeLoader->call_termination_hooks();
_kern_exit_team(status);
}