1/*
2 * Copyright 2004-2010, Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Author(s):
6 *		Daniel Reinhold, danielre@users.sf.net
7 *		Axel D��rfler, axeld@pinc-software.de
8 *		Ingo Weinhold, ingo_weinhold@gmx.de
9 */
10
11
12#include <SupportDefs.h>
13
14#include <limits.h>
15#include <signal.h>
16#include <stdio.h>
17#include <stdlib.h>
18
19#include <new>
20
21#include <util/DoublyLinkedList.h>
22#include <util/SinglyLinkedList.h>
23
24#include <libroot_private.h>
25#include <locks.h>
26#include <runtime_loader.h>
27#include <syscalls.h>
28
29
30extern "C" void _IO_cleanup(void);
31extern "C" void _thread_do_exit_work(void);
32
33
34struct AtExitInfoBlock;
35
36struct AtExitInfo : SinglyLinkedListLinkImpl<AtExitInfo> {
37	AtExitInfoBlock*	block;
38	void				(*hook)(void*);
39	void*				data;
40	void*				dsoHandle;
41};
42
43typedef SinglyLinkedList<AtExitInfo> AtExitInfoList;
44
45
46struct AtExitInfoBlock : DoublyLinkedListLinkImpl<AtExitInfoBlock> {
47	AtExitInfoBlock()
48		:
49		fFirstUnused(0)
50	{
51	}
52
53	bool IsEmpty() const
54	{
55		return fFirstUnused == ATEXIT_MAX && fFreeList.IsEmpty();
56	}
57
58	AtExitInfo* AllocateInfo()
59	{
60		// Handle the likely case -- the block is not fully used yet -- first.
61		// Grab the next info from the array.
62		if (fFirstUnused < ATEXIT_MAX) {
63			AtExitInfo* info = &fInfos[fFirstUnused++];
64			info->block = this;
65			return info;
66		}
67
68		// The block was fully used, but there might be infos in the free list.
69		return fFreeList.RemoveHead();
70	}
71
72	void FreeInfo(AtExitInfo* info)
73	{
74		fFreeList.Add(info);
75	}
76
77private:
78	AtExitInfo		fInfos[ATEXIT_MAX];
79	uint32			fFirstUnused;
80	AtExitInfoList	fFreeList;
81};
82
83typedef DoublyLinkedList<AtExitInfoBlock> AtExitInfoBlockList;
84
85
86struct DSOPredicate {
87	DSOPredicate(void* dsoHandle)
88		:
89		fDSOHandle(dsoHandle)
90	{
91	}
92
93	inline bool operator()(const AtExitInfo* info) const
94	{
95		return info->dsoHandle == fDSOHandle;
96	}
97
98private:
99	void*	fDSOHandle;
100};
101
102
103struct AddressRangePredicate {
104	AddressRangePredicate(addr_t start, size_t size)
105		:
106		fStart(start),
107		fEnd(start + size - 1)
108	{
109	}
110
111	inline bool operator()(const AtExitInfo* info) const
112	{
113		addr_t address = (addr_t)info->hook;
114		return info->dsoHandle == NULL && address >= fStart && address <= fEnd;
115			// Note: We ignore hooks associated with an image (the same one
116			// likely), since those will be called anyway when __cxa_finalize()
117			// is invoked for that image.
118	}
119
120private:
121	addr_t	fStart;
122	addr_t	fEnd;
123};
124
125
126static AtExitInfoBlock sInitialAtExitInfoBlock;
127static AtExitInfoBlockList sAtExitInfoBlocks;
128static AtExitInfoList sAtExitInfoStack;
129static recursive_lock sAtExitLock = RECURSIVE_LOCK_INITIALIZER("at exit lock");
130
131
132static void inline
133_exit_stack_lock()
134{
135	recursive_lock_lock(&sAtExitLock);
136}
137
138
139static void inline
140_exit_stack_unlock()
141{
142	recursive_lock_unlock(&sAtExitLock);
143}
144
145
146template<typename Predicate>
147static void
148call_exit_hooks(const Predicate& predicate)
149{
150	_exit_stack_lock();
151
152	AtExitInfo* previousInfo = NULL;
153	AtExitInfo* info = sAtExitInfoStack.Head();
154	while (info != NULL) {
155		AtExitInfo* nextInfo = sAtExitInfoStack.GetNext(info);
156
157		if (predicate(info)) {
158			// remove info from stack
159			sAtExitInfoStack.Remove(previousInfo, info);
160
161			// call the hook
162			info->hook(info->data);
163
164			// return the info to the block
165			if (info->block->IsEmpty())
166				sAtExitInfoBlocks.Add(info->block);
167
168			info->block->FreeInfo(info);
169		} else
170			previousInfo = info;
171
172		info = nextInfo;
173	}
174
175	_exit_stack_unlock();
176}
177
178
179// #pragma mark -- C++ ABI
180
181
182/*!	exit() hook registration function (mandated by the C++ ABI).
183	\param hook Hook function to be called.
184	\param data The data to be passed to the hook.
185	\param dsoHandle If non-NULL, the hook is associated with the respective
186		loaded shared object (aka image) -- the hook will be called either on
187		exit() or earlier when the shared object is unloaded. If NULL, the hook
188		is called only on exit().
189	\return \c 0 on success, another value on failure.
190 */
191extern "C" int
192__cxa_atexit(void (*hook)(void*), void* data, void* dsoHandle)
193{
194	if (hook == NULL)
195		return -1;
196
197	_exit_stack_lock();
198
199	// We need to allocate an info. Get an info block from which to allocate.
200	AtExitInfoBlock* block = sAtExitInfoBlocks.Head();
201	if (block == NULL) {
202		// might be the first call -- check the initial block
203		if (!sInitialAtExitInfoBlock.IsEmpty()) {
204			block = &sInitialAtExitInfoBlock;
205		} else {
206			// no empty block -- let's hope libroot is initialized sufficiently
207			// for the heap to work
208			block = new(std::nothrow) AtExitInfoBlock;
209			if (block == NULL) {
210				_exit_stack_unlock();
211				return -1;
212			}
213		}
214
215		sAtExitInfoBlocks.Add(block);
216	}
217
218	// allocate the info
219	AtExitInfo* info = block->AllocateInfo();
220
221	// If the block is empty now, remove it from the list.
222	if (block->IsEmpty())
223		sAtExitInfoBlocks.Remove(block);
224
225	// init and add the info
226	info->hook = hook;
227	info->data = data;
228	info->dsoHandle = dsoHandle;
229
230	sAtExitInfoStack.Add(info);
231
232	_exit_stack_unlock();
233
234	return 0;
235}
236
237
238/*!	exit() hook calling function (mandated by the C++ ABI).
239
240	Calls the exit() hooks associated with a certain shared object handle,
241	respectively calls all hooks when a NULL handle is given. All called
242	hooks are removed.
243
244	\param dsoHandle If non-NULL, all hooks associated with that handle are
245		called. If NULL, all hooks are called.
246 */
247extern "C" void
248__cxa_finalize(void* dsoHandle)
249{
250	if (dsoHandle == NULL) {
251		// call all hooks
252		_exit_stack_lock();
253
254		while (AtExitInfo* info = sAtExitInfoStack.RemoveHead()) {
255			// call the hook
256			info->hook(info->data);
257
258			// return the info to the block
259			if (info->block->IsEmpty())
260				sAtExitInfoBlocks.Add(info->block);
261
262			info->block->FreeInfo(info);
263		}
264
265		_exit_stack_unlock();
266	} else {
267		// call all hooks for the respective DSO
268		call_exit_hooks(DSOPredicate(dsoHandle));
269	}
270}
271
272
273// #pragma mark - private API
274
275
276void
277_call_atexit_hooks_for_range(addr_t start, addr_t size)
278{
279	call_exit_hooks(AddressRangePredicate(start, size));
280}
281
282
283// #pragma mark - public API
284
285
286void
287abort()
288{
289	fprintf(stderr, "Abort\n");
290
291	// If there's no handler installed for SIGABRT, call debugger().
292	struct sigaction signalAction;
293	if (sigaction(SIGABRT, NULL, &signalAction) == 0
294			&& signalAction.sa_handler == SIG_DFL) {
295		debugger("abort() called");
296	}
297
298	raise(SIGABRT);
299	exit(EXIT_FAILURE);
300}
301
302
303int
304atexit(void (*func)(void))
305{
306	return __cxa_atexit((void (*)(void*))func, NULL, NULL);
307}
308
309
310void
311exit(int status)
312{
313	// BeOS on exit notification for the main thread
314	_thread_do_exit_work();
315
316	// unwind the exit stack, calling the registered functions
317	__cxa_finalize(NULL);
318
319	// close all open files
320	_IO_cleanup();
321
322	__gRuntimeLoader->call_termination_hooks();
323
324	// exit with status code
325	_kern_exit_team(status);
326}
327