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> { 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 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 { 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 98 private: 99 void* fDSOHandle; 100 }; 101 102 103 struct 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 120 private: 121 addr_t fStart; 122 addr_t fEnd; 123 }; 124 125 126 static AtExitInfoBlock sInitialAtExistInfoBlock; 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 133 _exit_stack_lock() 134 { 135 recursive_lock_lock(&sAtExitLock); 136 } 137 138 139 static void inline 140 _exit_stack_unlock() 141 { 142 recursive_lock_unlock(&sAtExitLock); 143 } 144 145 146 template<typename Predicate> 147 static void 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 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 (!sInitialAtExistInfoBlock.IsEmpty()) { 204 block = &sInitialAtExistInfoBlock; 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 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 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 287 abort() 288 { 289 fprintf(stderr, "Abort\n"); 290 291 raise(SIGABRT); 292 exit(EXIT_FAILURE); 293 } 294 295 296 int 297 atexit(void (*func)(void)) 298 { 299 return __cxa_atexit((void (*)(void*))func, NULL, NULL); 300 } 301 302 303 void 304 exit(int status) 305 { 306 // BeOS on exit notification for the main thread 307 _thread_do_exit_work(); 308 309 // unwind the exit stack, calling the registered functions 310 __cxa_finalize(NULL); 311 312 // close all open files 313 _IO_cleanup(); 314 315 __gRuntimeLoader->call_termination_hooks(); 316 317 // exit with status code 318 _kern_exit_team(status); 319 } 320