Implemented __cxa_atexit() and __cxa_finalize() which are required by the
Itanium ABI. Whether __cxa_atexit() really works for associating hooks with DSOs has not been tested yet, as our compiler doesn't use the function. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39298 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
6d1078eb7a
commit
85f9496a24
@ -1,6 +1,7 @@
|
|||||||
SubDir HAIKU_TOP src system libroot posix stdlib ;
|
SubDir HAIKU_TOP src system libroot posix stdlib ;
|
||||||
|
|
||||||
UsePrivateHeaders drivers libroot runtime_loader shared ;
|
UsePrivateHeaders drivers libroot runtime_loader shared ;
|
||||||
|
UsePrivateHeaders kernel ; # for <util/*>
|
||||||
UsePrivateSystemHeaders ;
|
UsePrivateSystemHeaders ;
|
||||||
|
|
||||||
MergeObject posix_stdlib.o :
|
MergeObject posix_stdlib.o :
|
||||||
|
@ -1,19 +1,25 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2004-2009, Haiku Inc. All rights reserved.
|
* Copyright 2004-2010, Haiku Inc. All rights reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
*
|
||||||
* Author(s):
|
* Author(s):
|
||||||
* Daniel Reinhold, danielre@users.sf.net
|
* Daniel Reinhold, danielre@users.sf.net
|
||||||
* Axel Dörfler, axeld@pinc-software.de
|
* Axel Dörfler, axeld@pinc-software.de
|
||||||
|
* Ingo Weinhold, ingo_weinhold@gmx.de
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <SupportDefs.h>
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <signal.h>
|
|
||||||
|
#include <new>
|
||||||
|
|
||||||
|
#include <util/DoublyLinkedList.h>
|
||||||
|
#include <util/SinglyLinkedList.h>
|
||||||
|
|
||||||
#include <libroot_private.h>
|
#include <libroot_private.h>
|
||||||
#include <locks.h>
|
#include <locks.h>
|
||||||
@ -24,28 +30,237 @@
|
|||||||
extern "C" void _IO_cleanup(void);
|
extern "C" void _IO_cleanup(void);
|
||||||
extern "C" void _thread_do_exit_work(void);
|
extern "C" void _thread_do_exit_work(void);
|
||||||
|
|
||||||
struct exit_stack_info {
|
|
||||||
void (*exit_stack[ATEXIT_MAX])(void);
|
struct AtExitInfoBlock;
|
||||||
int32 stack_size;
|
|
||||||
recursive_lock lock;
|
struct AtExitInfo : SinglyLinkedListLinkImpl<AtExitInfo> {
|
||||||
|
AtExitInfoBlock* block;
|
||||||
|
void (*hook)(void*);
|
||||||
|
void* data;
|
||||||
|
void* dsoHandle;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef SinglyLinkedList<AtExitInfo> AtExitInfoList;
|
||||||
|
|
||||||
|
|
||||||
|
struct AtExitInfoBlock : DoublyLinkedListLinkImpl<AtExitInfoBlock> {
|
||||||
|
bool IsEmpty() const
|
||||||
|
{
|
||||||
|
return fFirstUnused == ATEXIT_MAX && fFreeList.IsEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
AtExitInfo* AllocateInfo()
|
||||||
|
{
|
||||||
|
// Handle the likely case -- the block is not fully used yet -- first.
|
||||||
|
// Grab the next info from the array.
|
||||||
|
if (fFirstUnused < ATEXIT_MAX) {
|
||||||
|
AtExitInfo* info = &fInfos[fFirstUnused++];
|
||||||
|
info->block = this;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The block was fully used, but there might be infos in the free list.
|
||||||
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static struct exit_stack_info sExitStackInfo
|
struct AddressRangePredicate {
|
||||||
= { {}, 0, RECURSIVE_LOCK_INITIALIZER("exit stack lock") };
|
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;
|
||||||
|
// Note: We ignore hooks associated with an image (the same one
|
||||||
|
// likely), since those will be called anyway when __cxa_finalize()
|
||||||
|
// is invoked for that image.
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
addr_t fStart;
|
||||||
|
addr_t fEnd;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static AtExitInfoBlock sInitialAtExistInfoBlock;
|
||||||
|
static AtExitInfoBlockList sAtExitInfoBlocks;
|
||||||
|
static AtExitInfoList sAtExitInfoStack;
|
||||||
|
static recursive_lock sAtExitLock = RECURSIVE_LOCK_INITIALIZER("at exit lock");
|
||||||
|
|
||||||
|
|
||||||
static void inline
|
static void inline
|
||||||
_exit_stack_lock()
|
_exit_stack_lock()
|
||||||
{
|
{
|
||||||
recursive_lock_lock(&sExitStackInfo.lock);
|
recursive_lock_lock(&sAtExitLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void inline
|
static void inline
|
||||||
_exit_stack_unlock()
|
_exit_stack_unlock()
|
||||||
{
|
{
|
||||||
recursive_lock_unlock(&sExitStackInfo.lock);
|
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)) {
|
||||||
|
// remove info from stack
|
||||||
|
sAtExitInfoStack.Remove(previousInfo, info);
|
||||||
|
|
||||||
|
// call the hook
|
||||||
|
info->hook(info->data);
|
||||||
|
|
||||||
|
// return the info to the block
|
||||||
|
if (info->block->IsEmpty())
|
||||||
|
sAtExitInfoBlocks.Add(info->block);
|
||||||
|
|
||||||
|
info->block->FreeInfo(info);
|
||||||
|
} else
|
||||||
|
previousInfo = info;
|
||||||
|
|
||||||
|
info = nextInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
_exit_stack_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -- C++ ABI
|
||||||
|
|
||||||
|
|
||||||
|
/*! exit() hook registration function (mandated by the C++ ABI).
|
||||||
|
\param hook Hook function to be called.
|
||||||
|
\param data The data to be passed to the hook.
|
||||||
|
\param dsoHandle If non-NULL, the hook is associated with the respective
|
||||||
|
loaded shared object (aka image) -- the hook will be called either on
|
||||||
|
exit() or earlier when the shared object is unloaded. If NULL, the hook
|
||||||
|
is called only on exit().
|
||||||
|
\return \c 0 on success, another value on failure.
|
||||||
|
*/
|
||||||
|
extern "C" int
|
||||||
|
__cxa_atexit(void (*hook)(void*), void* data, void* dsoHandle)
|
||||||
|
{
|
||||||
|
if (hook == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
_exit_stack_lock();
|
||||||
|
|
||||||
|
// We need to allocate an info. Get an info block from which to allocate.
|
||||||
|
AtExitInfoBlock* block = sAtExitInfoBlocks.Head();
|
||||||
|
if (block == NULL) {
|
||||||
|
// might be the first call -- check the initial block
|
||||||
|
if (!sInitialAtExistInfoBlock.IsEmpty()) {
|
||||||
|
block = &sInitialAtExistInfoBlock;
|
||||||
|
} else {
|
||||||
|
// no empty block -- let's hope libroot is initialized sufficiently
|
||||||
|
// for the heap to work
|
||||||
|
block = new(std::nothrow) AtExitInfoBlock;
|
||||||
|
if (block == NULL) {
|
||||||
|
_exit_stack_unlock();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sAtExitInfoBlocks.Add(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate the info
|
||||||
|
AtExitInfo* info = block->AllocateInfo();
|
||||||
|
|
||||||
|
// If the block is empty now, remove it from the list.
|
||||||
|
if (block->IsEmpty())
|
||||||
|
sAtExitInfoBlocks.Remove(block);
|
||||||
|
|
||||||
|
// init and add the info
|
||||||
|
info->hook = hook;
|
||||||
|
info->data = data;
|
||||||
|
info->dsoHandle = dsoHandle;
|
||||||
|
|
||||||
|
sAtExitInfoStack.Add(info);
|
||||||
|
|
||||||
|
_exit_stack_unlock();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! exit() hook calling function (mandated by the C++ ABI).
|
||||||
|
|
||||||
|
Calls the exit() hooks associated with a certain shared object handle,
|
||||||
|
respectively calls all hooks when a NULL handle is given. All called
|
||||||
|
hooks are removed.
|
||||||
|
|
||||||
|
\param dsoHandle If non-NULL, all hooks associated with that handle are
|
||||||
|
called. If NULL, all hooks are called.
|
||||||
|
*/
|
||||||
|
extern "C" void
|
||||||
|
__cxa_finalize(void* dsoHandle)
|
||||||
|
{
|
||||||
|
if (dsoHandle == NULL) {
|
||||||
|
// call all hooks
|
||||||
|
_exit_stack_lock();
|
||||||
|
|
||||||
|
while (AtExitInfo* info = sAtExitInfoStack.RemoveHead()) {
|
||||||
|
// call the hook
|
||||||
|
info->hook(info->data);
|
||||||
|
|
||||||
|
// return the info to the block
|
||||||
|
if (info->block->IsEmpty())
|
||||||
|
sAtExitInfoBlocks.Add(info->block);
|
||||||
|
|
||||||
|
info->block->FreeInfo(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
_exit_stack_unlock();
|
||||||
|
} else {
|
||||||
|
// call all hooks for the respective DSO
|
||||||
|
call_exit_hooks(DSOPredicate(dsoHandle));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -55,32 +270,7 @@ _exit_stack_unlock()
|
|||||||
void
|
void
|
||||||
_call_atexit_hooks_for_range(addr_t start, addr_t size)
|
_call_atexit_hooks_for_range(addr_t start, addr_t size)
|
||||||
{
|
{
|
||||||
int32 index;
|
call_exit_hooks(AddressRangePredicate(start, size));
|
||||||
int32 insertIndex = -1;
|
|
||||||
|
|
||||||
_exit_stack_lock();
|
|
||||||
for (index = sExitStackInfo.stack_size - 1; index >= 0; index--) {
|
|
||||||
addr_t function = (addr_t)sExitStackInfo.exit_stack[index];
|
|
||||||
if (function >= start && function < start + size) {
|
|
||||||
(*sExitStackInfo.exit_stack[index])();
|
|
||||||
sExitStackInfo.exit_stack[index] = NULL;
|
|
||||||
insertIndex = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (insertIndex >= 0) {
|
|
||||||
for (index = insertIndex + 1;
|
|
||||||
index < sExitStackInfo.stack_size;
|
|
||||||
index++) {
|
|
||||||
if (sExitStackInfo.exit_stack[index] != NULL) {
|
|
||||||
sExitStackInfo.exit_stack[insertIndex++]
|
|
||||||
= sExitStackInfo.exit_stack[index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sExitStackInfo.stack_size = insertIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
_exit_stack_unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -100,17 +290,7 @@ abort()
|
|||||||
int
|
int
|
||||||
atexit(void (*func)(void))
|
atexit(void (*func)(void))
|
||||||
{
|
{
|
||||||
// push the function pointer onto the exit stack
|
return __cxa_atexit((void (*)(void*))func, NULL, NULL);
|
||||||
int result = -1;
|
|
||||||
_exit_stack_lock();
|
|
||||||
|
|
||||||
if (sExitStackInfo.stack_size < ATEXIT_MAX) {
|
|
||||||
sExitStackInfo.exit_stack[sExitStackInfo.stack_size++] = func;
|
|
||||||
result = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
_exit_stack_unlock();
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -121,10 +301,7 @@ exit(int status)
|
|||||||
_thread_do_exit_work();
|
_thread_do_exit_work();
|
||||||
|
|
||||||
// unwind the exit stack, calling the registered functions
|
// unwind the exit stack, calling the registered functions
|
||||||
_exit_stack_lock();
|
__cxa_finalize(NULL);
|
||||||
while (--sExitStackInfo.stack_size >= 0)
|
|
||||||
(*sExitStackInfo.exit_stack[sExitStackInfo.stack_size])();
|
|
||||||
_exit_stack_unlock();
|
|
||||||
|
|
||||||
// close all open files
|
// close all open files
|
||||||
_IO_cleanup();
|
_IO_cleanup();
|
||||||
@ -134,4 +311,3 @@ exit(int status)
|
|||||||
// exit with status code
|
// exit with status code
|
||||||
_kern_exit_team(status);
|
_kern_exit_team(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user