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