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
30 extern "C" void _IO_cleanup(void);
31 extern "C" void _thread_do_exit_work(void);
32
33
34 struct AtExitInfoBlock;
35
36 struct AtExitInfo : SinglyLinkedListLinkImpl<AtExitInfo> {
37 AtExitInfoBlock* block;
38 void (*hook)(void*);
39 void* data;
40 void* dsoHandle;
41 };
42
43 typedef SinglyLinkedList<AtExitInfo> AtExitInfoList;
44
45
46 struct AtExitInfoBlock : DoublyLinkedListLinkImpl<AtExitInfoBlock> {
AtExitInfoBlockAtExitInfoBlock47 AtExitInfoBlock()
48 :
49 fFirstUnused(0)
50 {
51 }
52
IsEmptyAtExitInfoBlock53 bool IsEmpty() const
54 {
55 return fFirstUnused == ATEXIT_MAX && fFreeList.IsEmpty();
56 }
57
AllocateInfoAtExitInfoBlock58 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
FreeInfoAtExitInfoBlock72 void FreeInfo(AtExitInfo* info)
73 {
74 fFreeList.Add(info);
75 }
76
77 private:
78 AtExitInfo fInfos[ATEXIT_MAX];
79 uint32 fFirstUnused;
80 AtExitInfoList fFreeList;
81 };
82
83 typedef DoublyLinkedList<AtExitInfoBlock> AtExitInfoBlockList;
84
85
86 struct DSOPredicate {
DSOPredicateDSOPredicate87 DSOPredicate(void* dsoHandle)
88 :
89 fDSOHandle(dsoHandle)
90 {
91 }
92
operator ()DSOPredicate93 inline bool operator()(const AtExitInfo* info) const
94 {
95 return info->dsoHandle == fDSOHandle;
96 }
97
98 private:
99 void* fDSOHandle;
100 };
101
102
103 struct AddressRangePredicate {
AddressRangePredicateAddressRangePredicate104 AddressRangePredicate(addr_t start, size_t size)
105 :
106 fStart(start),
107 fEnd(start + size - 1)
108 {
109 }
110
operator ()AddressRangePredicate111 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
120 private:
121 addr_t fStart;
122 addr_t fEnd;
123 };
124
125
126 static AtExitInfoBlock sInitialAtExitInfoBlock;
127 static AtExitInfoBlockList sAtExitInfoBlocks;
128 static AtExitInfoList sAtExitInfoStack;
129 static recursive_lock sAtExitLock = RECURSIVE_LOCK_INITIALIZER("at exit lock");
130
131
132 static void inline
_exit_stack_lock()133 _exit_stack_lock()
134 {
135 recursive_lock_lock(&sAtExitLock);
136 }
137
138
139 static void inline
_exit_stack_unlock()140 _exit_stack_unlock()
141 {
142 recursive_lock_unlock(&sAtExitLock);
143 }
144
145
146 template<typename Predicate>
147 static void
call_exit_hooks(const Predicate & predicate)148 call_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 */
191 extern "C" int
__cxa_atexit(void (* hook)(void *),void * data,void * dsoHandle)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 */
247 extern "C" void
__cxa_finalize(void * dsoHandle)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
276 void
_call_atexit_hooks_for_range(addr_t start,addr_t size)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
286 void
abort()287 abort()
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
303 int
atexit(void (* func)(void))304 atexit(void (*func)(void))
305 {
306 return __cxa_atexit((void (*)(void*))func, NULL, NULL);
307 }
308
309
310 void
exit(int status)311 exit(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