1169689Skan/* __cxa_atexit backwards-compatibility support for Darwin. 2169689Skan Copyright (C) 2006 Free Software Foundation, Inc. 3169689Skan 4169689SkanThis file is part of GCC. 5169689Skan 6169689SkanGCC is free software; you can redistribute it and/or modify it under 7169689Skanthe terms of the GNU General Public License as published by the Free 8169689SkanSoftware Foundation; either version 2, or (at your option) any later 9169689Skanversion. 10169689Skan 11169689SkanIn addition to the permissions in the GNU General Public License, the 12169689SkanFree Software Foundation gives you unlimited permission to link the 13169689Skancompiled version of this file into combinations with other programs, 14169689Skanand to distribute those combinations without any restriction coming 15169689Skanfrom the use of this file. (The General Public License restrictions 16169689Skando apply in other respects; for example, they cover modification of 17169689Skanthe file, and distribution when not linked into a combine 18169689Skanexecutable.) 19169689Skan 20169689SkanGCC is distributed in the hope that it will be useful, but WITHOUT ANY 21169689SkanWARRANTY; without even the implied warranty of MERCHANTABILITY or 22169689SkanFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 23169689Skanfor more details. 24169689Skan 25169689SkanYou should have received a copy of the GNU General Public License 26169689Skanalong with GCC; see the file COPYING. If not, write to the Free 27169689SkanSoftware Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 28169689Skan02110-1301, USA. */ 29169689Skan 30169689Skan/* Don't do anything if we are compiling for a kext multilib. */ 31169689Skan#ifdef __PIC__ 32169689Skan 33169689Skan/* It is incorrect to include config.h here, because this file is being 34169689Skan compiled for the target, and hence definitions concerning only the host 35169689Skan do not apply. */ 36169689Skan 37169689Skan#include "tconfig.h" 38169689Skan#include "tsystem.h" 39169689Skan 40169689Skan#include <dlfcn.h> 41169689Skan#include <stdbool.h> 42169689Skan#include <stdlib.h> 43169689Skan#include <string.h> 44169689Skan 45169689Skan/* This file works around two different problems. 46169689Skan 47169689Skan The first problem is that there is no __cxa_atexit on Mac OS versions 48169689Skan before 10.4. It fixes this by providing a complete atexit and 49169689Skan __cxa_atexit emulation called from the regular atexit. 50169689Skan 51169689Skan The second problem is that on all shipping versions of Mac OS, 52169689Skan __cxa_finalize and exit() don't work right: they don't run routines 53169689Skan that were registered while other atexit routines are running. This 54169689Skan is worked around by wrapping each atexit/__cxa_atexit routine with 55169689Skan our own routine which ensures that any __cxa_atexit calls while it 56169689Skan is running are honoured. 57169689Skan 58169689Skan There are still problems which this does not solve. Before 10.4, 59169689Skan shared objects linked with previous compilers won't have their 60169689Skan atexit calls properly interleaved with code compiled with newer 61169689Skan compilers. Also, atexit routines registered from shared objects 62169689Skan linked with previous compilers won't get the bug fix. */ 63169689Skan 64169689Skantypedef int (*cxa_atexit_p)(void (*func) (void*), void* arg, const void* dso); 65169689Skantypedef void (*cxa_finalize_p)(const void *dso); 66169689Skantypedef int (*atexit_p)(void (*func)(void)); 67169689Skan 68169689Skan/* These are from "keymgr.h". */ 69169689Skanextern void *_keymgr_get_and_lock_processwide_ptr (unsigned key); 70169689Skanextern int _keymgr_get_and_lock_processwide_ptr_2 (unsigned, void **); 71169689Skanextern int _keymgr_set_and_unlock_processwide_ptr (unsigned key, void *ptr); 72169689Skan 73169689Skanextern void *__keymgr_global[]; 74169689Skantypedef struct _Sinfo_Node { 75169689Skan unsigned int size ; /*size of this node*/ 76169689Skan unsigned short major_version ; /*API major version.*/ 77169689Skan unsigned short minor_version ; /*API minor version.*/ 78169689Skan } _Tinfo_Node ; 79169689Skan 80169689Skan#ifdef __ppc__ 81169689Skan#define CHECK_KEYMGR_ERROR(e) \ 82169689Skan (((_Tinfo_Node *)__keymgr_global[2])->major_version >= 4 ? (e) : 0) 83169689Skan#else 84169689Skan#define CHECK_KEYMGR_ERROR(e) (e) 85169689Skan#endif 86169689Skan 87169689Skan/* Our globals are stored under this keymgr index. */ 88169689Skan#define KEYMGR_ATEXIT_LIST 14 89169689Skan 90169689Skan/* The different kinds of callback routines. */ 91169689Skantypedef void (*atexit_callback)(void); 92169689Skantypedef void (*cxa_atexit_callback)(void *); 93169689Skan 94169689Skan/* This structure holds a routine to call. There may be extra fields 95169689Skan at the end of the structure that this code doesn't know about. */ 96169689Skanstruct one_atexit_routine 97169689Skan{ 98169689Skan union { 99169689Skan atexit_callback ac; 100169689Skan cxa_atexit_callback cac; 101169689Skan } callback; 102169689Skan /* has_arg is 0/2/4 if 'ac' is live, 1/3/5 if 'cac' is live. 103169689Skan Higher numbers indicate a later version of the structure that this 104169689Skan code doesn't understand and will ignore. */ 105169689Skan int has_arg; 106169689Skan void * arg; 107169689Skan}; 108169689Skan 109169689Skanstruct atexit_routine_list 110169689Skan{ 111169689Skan struct atexit_routine_list * next; 112169689Skan struct one_atexit_routine r; 113169689Skan}; 114169689Skan 115169689Skan/* The various possibilities for status of atexit(). */ 116169689Skanenum atexit_status { 117169689Skan atexit_status_unknown = 0, 118169689Skan atexit_status_missing = 1, 119169689Skan atexit_status_broken = 2, 120169689Skan atexit_status_working = 16 121169689Skan}; 122169689Skan 123169689Skanstruct keymgr_atexit_list 124169689Skan{ 125169689Skan /* Version of this list. This code knows only about version 0. 126169689Skan If the version is higher than 0, this code may add new atexit routines 127169689Skan but should not attempt to run the list. */ 128169689Skan short version; 129169689Skan /* 1 if an atexit routine is currently being run by this code, 0 130169689Skan otherwise. */ 131169689Skan char running_routines; 132169689Skan /* Holds a value from 'enum atexit_status'. */ 133169689Skan unsigned char atexit_status; 134169689Skan /* The list of atexit and cxa_atexit routines registered. If 135169689Skan atexit_status_missing it contains all routines registered while 136169689Skan linked with this code. If atexit_status_broken it contains all 137169689Skan routines registered during cxa_finalize while linked with this 138169689Skan code. */ 139169689Skan struct atexit_routine_list *l; 140169689Skan /* &__cxa_atexit; set if atexit_status >= atexit_status_broken. */ 141169689Skan cxa_atexit_p cxa_atexit_f; 142169689Skan /* &__cxa_finalize; set if atexit_status >= atexit_status_broken. */ 143169689Skan cxa_finalize_p cxa_finalize_f; 144169689Skan /* &atexit; set if atexit_status >= atexit_status_working 145169689Skan or atexit_status == atexit_status_missing. */ 146169689Skan atexit_p atexit_f; 147169689Skan}; 148169689Skan 149169689Skan/* Return 0 if __cxa_atexit has the bug it has in Mac OS 10.4: it 150169689Skan fails to call routines registered while an atexit routine is 151169689Skan running. Return 1 if it works properly, and -1 if an error occurred. */ 152169689Skan 153169689Skanstruct atexit_data 154169689Skan{ 155169689Skan int result; 156169689Skan cxa_atexit_p cxa_atexit; 157169689Skan}; 158169689Skan 159169689Skanstatic void cxa_atexit_check_2 (void *arg) 160169689Skan{ 161169689Skan ((struct atexit_data *)arg)->result = 1; 162169689Skan} 163169689Skan 164169689Skanstatic void cxa_atexit_check_1 (void *arg) 165169689Skan{ 166169689Skan struct atexit_data * aed = arg; 167169689Skan if (aed->cxa_atexit (cxa_atexit_check_2, arg, arg) != 0) 168169689Skan aed->result = -1; 169169689Skan} 170169689Skan 171169689Skanstatic int 172169689Skancheck_cxa_atexit (cxa_atexit_p cxa_atexit, cxa_finalize_p cxa_finalize) 173169689Skan{ 174169689Skan struct atexit_data aed = { 0, cxa_atexit }; 175169689Skan 176169689Skan /* We re-use &aed as the 'dso' parameter, since it's a unique address. */ 177169689Skan if (cxa_atexit (cxa_atexit_check_1, &aed, &aed) != 0) 178169689Skan return -1; 179169689Skan cxa_finalize (&aed); 180169689Skan if (aed.result == 0) 181169689Skan { 182169689Skan /* Call __cxa_finalize again to make sure that cxa_atexit_check_2 183169689Skan is removed from the list before AED goes out of scope. */ 184169689Skan cxa_finalize (&aed); 185169689Skan aed.result = 0; 186169689Skan } 187169689Skan return aed.result; 188169689Skan} 189169689Skan 190169689Skan#ifdef __ppc__ 191169689Skan/* This comes from Csu. It works only before 10.4. The prototype has 192169689Skan been altered a bit to avoid casting. */ 193169689Skanextern int _dyld_func_lookup(const char *dyld_func_name, 194169689Skan void *address) __attribute__((visibility("hidden"))); 195169689Skan 196169689Skanstatic void our_atexit (void); 197169689Skan 198169689Skan/* We're running on 10.3.9. Find the address of the system atexit() 199169689Skan function. So easy to say, so hard to do. */ 200169689Skanstatic atexit_p 201169689Skanfind_atexit_10_3 (void) 202169689Skan{ 203169689Skan unsigned int (*dyld_image_count_fn)(void); 204169689Skan const char *(*dyld_get_image_name_fn)(unsigned int image_index); 205169689Skan const void *(*dyld_get_image_header_fn)(unsigned int image_index); 206169689Skan const void *(*NSLookupSymbolInImage_fn)(const void *image, 207169689Skan const char *symbolName, 208169689Skan unsigned int options); 209169689Skan void *(*NSAddressOfSymbol_fn)(const void *symbol); 210169689Skan unsigned i, count; 211169689Skan 212169689Skan /* Find some dyld functions. */ 213169689Skan _dyld_func_lookup("__dyld_image_count", &dyld_image_count_fn); 214169689Skan _dyld_func_lookup("__dyld_get_image_name", &dyld_get_image_name_fn); 215169689Skan _dyld_func_lookup("__dyld_get_image_header", &dyld_get_image_header_fn); 216169689Skan _dyld_func_lookup("__dyld_NSLookupSymbolInImage", &NSLookupSymbolInImage_fn); 217169689Skan _dyld_func_lookup("__dyld_NSAddressOfSymbol", &NSAddressOfSymbol_fn); 218169689Skan 219169689Skan /* If any of these don't exist, that's an error. */ 220169689Skan if (! dyld_image_count_fn || ! dyld_get_image_name_fn 221169689Skan || ! dyld_get_image_header_fn || ! NSLookupSymbolInImage_fn 222169689Skan || ! NSAddressOfSymbol_fn) 223169689Skan return NULL; 224169689Skan 225169689Skan count = dyld_image_count_fn (); 226169689Skan for (i = 0; i < count; i++) 227169689Skan { 228169689Skan const char * path = dyld_get_image_name_fn (i); 229169689Skan const void * image; 230169689Skan const void * symbol; 231169689Skan 232169689Skan if (strcmp (path, "/usr/lib/libSystem.B.dylib") != 0) 233169689Skan continue; 234169689Skan image = dyld_get_image_header_fn (i); 235169689Skan if (! image) 236169689Skan return NULL; 237169689Skan /* '4' is NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR. */ 238169689Skan symbol = NSLookupSymbolInImage_fn (image, "_atexit", 4); 239169689Skan if (! symbol) 240169689Skan return NULL; 241169689Skan return NSAddressOfSymbol_fn (symbol); 242169689Skan } 243169689Skan return NULL; 244169689Skan} 245169689Skan#endif 246169689Skan 247169689Skan/* Create (if necessary), find, lock, fill in, and return our globals. 248169689Skan Return NULL on error, in which case the globals will not be locked. 249169689Skan The caller should call keymgr_set_and_unlock. */ 250169689Skanstatic struct keymgr_atexit_list * 251169689Skanget_globals (void) 252169689Skan{ 253169689Skan struct keymgr_atexit_list * r; 254169689Skan 255169689Skan#ifdef __ppc__ 256169689Skan /* 10.3.9 doesn't have _keymgr_get_and_lock_processwide_ptr_2 so the 257169689Skan PPC side can't use it. On 10.4 this just means the error gets 258169689Skan reported a little later when 259169689Skan _keymgr_set_and_unlock_processwide_ptr finds that the key was 260169689Skan never locked. */ 261169689Skan r = _keymgr_get_and_lock_processwide_ptr (KEYMGR_ATEXIT_LIST); 262169689Skan#else 263169689Skan void * rr; 264169689Skan if (_keymgr_get_and_lock_processwide_ptr_2 (KEYMGR_ATEXIT_LIST, &rr)) 265169689Skan return NULL; 266169689Skan r = rr; 267169689Skan#endif 268169689Skan 269169689Skan if (r == NULL) 270169689Skan { 271169689Skan r = calloc (sizeof (struct keymgr_atexit_list), 1); 272169689Skan if (! r) 273169689Skan return NULL; 274169689Skan } 275169689Skan 276169689Skan if (r->atexit_status == atexit_status_unknown) 277169689Skan { 278169689Skan void *handle; 279169689Skan 280169689Skan handle = dlopen ("/usr/lib/libSystem.B.dylib", RTLD_NOLOAD); 281169689Skan if (!handle) 282169689Skan { 283169689Skan#ifdef __ppc__ 284169689Skan r->atexit_status = atexit_status_missing; 285169689Skan r->atexit_f = find_atexit_10_3 (); 286169689Skan if (! r->atexit_f) 287169689Skan goto error; 288169689Skan if (r->atexit_f (our_atexit)) 289169689Skan goto error; 290169689Skan#else 291169689Skan goto error; 292169689Skan#endif 293169689Skan } 294169689Skan else 295169689Skan { 296169689Skan int chk_result; 297169689Skan 298169689Skan r->cxa_atexit_f = (cxa_atexit_p)dlsym (handle, "__cxa_atexit"); 299169689Skan r->cxa_finalize_f = (cxa_finalize_p)dlsym (handle, "__cxa_finalize"); 300169689Skan if (! r->cxa_atexit_f || ! r->cxa_finalize_f) 301169689Skan goto error; 302169689Skan 303169689Skan chk_result = check_cxa_atexit (r->cxa_atexit_f, r->cxa_finalize_f); 304169689Skan if (chk_result == -1) 305169689Skan goto error; 306169689Skan else if (chk_result == 0) 307169689Skan r->atexit_status = atexit_status_broken; 308169689Skan else 309169689Skan { 310169689Skan r->atexit_f = (atexit_p)dlsym (handle, "atexit"); 311169689Skan if (! r->atexit_f) 312169689Skan goto error; 313169689Skan r->atexit_status = atexit_status_working; 314169689Skan } 315169689Skan } 316169689Skan } 317169689Skan 318169689Skan return r; 319169689Skan 320169689Skan error: 321169689Skan _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, r); 322169689Skan return NULL; 323169689Skan} 324169689Skan 325169689Skan/* Add TO_ADD to ATEXIT_LIST. ATEXIT_LIST may be NULL but is 326169689Skan always the result of calling _keymgr_get_and_lock_processwide_ptr and 327169689Skan so KEYMGR_ATEXIT_LIST is known to be locked; this routine is responsible 328169689Skan for unlocking it. */ 329169689Skan 330169689Skanstatic int 331169689Skanadd_routine (struct keymgr_atexit_list * g, 332169689Skan const struct one_atexit_routine * to_add) 333169689Skan{ 334169689Skan struct atexit_routine_list * s 335169689Skan = malloc (sizeof (struct atexit_routine_list)); 336169689Skan int result; 337169689Skan 338169689Skan if (!s) 339169689Skan { 340169689Skan _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g); 341169689Skan return -1; 342169689Skan } 343169689Skan s->r = *to_add; 344169689Skan s->next = g->l; 345169689Skan g->l = s; 346169689Skan result = _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g); 347169689Skan return CHECK_KEYMGR_ERROR (result) == 0 ? 0 : -1; 348169689Skan} 349169689Skan 350169689Skan/* This runs the routines in G->L up to STOP. */ 351169689Skanstatic struct keymgr_atexit_list * 352169689Skanrun_routines (struct keymgr_atexit_list *g, 353169689Skan struct atexit_routine_list *stop) 354169689Skan{ 355169689Skan for (;;) 356169689Skan { 357169689Skan struct atexit_routine_list * cur = g->l; 358169689Skan if (! cur || cur == stop) 359169689Skan break; 360169689Skan g->l = cur->next; 361169689Skan _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g); 362169689Skan 363169689Skan switch (cur->r.has_arg) { 364169689Skan case 0: case 2: case 4: 365169689Skan cur->r.callback.ac (); 366169689Skan break; 367169689Skan case 1: case 3: case 5: 368169689Skan cur->r.callback.cac (cur->r.arg); 369169689Skan break; 370169689Skan default: 371169689Skan /* Don't understand, so don't call it. */ 372169689Skan break; 373169689Skan } 374169689Skan free (cur); 375169689Skan 376169689Skan g = _keymgr_get_and_lock_processwide_ptr (KEYMGR_ATEXIT_LIST); 377169689Skan if (! g) 378169689Skan break; 379169689Skan } 380169689Skan return g; 381169689Skan} 382169689Skan 383169689Skan/* Call the routine described by ROUTINE_PARAM and then call any 384169689Skan routines added to KEYMGR_ATEXIT_LIST while that routine was 385169689Skan running, all with in_cxa_finalize set. */ 386169689Skan 387169689Skanstatic void 388169689Skancxa_atexit_wrapper (void* routine_param) 389169689Skan{ 390169689Skan struct one_atexit_routine * routine = routine_param; 391169689Skan struct keymgr_atexit_list *g; 392169689Skan struct atexit_routine_list * base = NULL; 393169689Skan char prev_running = 0; 394169689Skan 395169689Skan g = _keymgr_get_and_lock_processwide_ptr (KEYMGR_ATEXIT_LIST); 396169689Skan if (g) 397169689Skan { 398169689Skan prev_running = g->running_routines; 399169689Skan g->running_routines = 1; 400169689Skan base = g->l; 401169689Skan _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g); 402169689Skan } 403169689Skan 404169689Skan if (routine->has_arg) 405169689Skan routine->callback.cac (routine->arg); 406169689Skan else 407169689Skan routine->callback.ac (); 408169689Skan 409169689Skan if (g) 410169689Skan g = _keymgr_get_and_lock_processwide_ptr (KEYMGR_ATEXIT_LIST); 411169689Skan if (g) 412169689Skan g = run_routines (g, base); 413169689Skan if (g) 414169689Skan { 415169689Skan g->running_routines = prev_running; 416169689Skan _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g); 417169689Skan } 418169689Skan} 419169689Skan 420169689Skan#ifdef __ppc__ 421169689Skan/* This code is used while running on 10.3.9, when __cxa_atexit doesn't 422169689Skan exist in the system library. 10.3.9 only supported regular PowerPC, 423169689Skan so this code isn't necessary on x86 or ppc64. */ 424169689Skan 425169689Skan/* This routine is called from the system atexit(); it runs everything 426169689Skan registered on the KEYMGR_ATEXIT_LIST. */ 427169689Skan 428169689Skanstatic void 429169689Skanour_atexit (void) 430169689Skan{ 431169689Skan struct keymgr_atexit_list *g; 432169689Skan char prev_running; 433169689Skan 434169689Skan g = _keymgr_get_and_lock_processwide_ptr (KEYMGR_ATEXIT_LIST); 435169689Skan if (! g || g->version != 0 || g->atexit_status != atexit_status_missing) 436169689Skan return; 437169689Skan 438169689Skan prev_running = g->running_routines; 439169689Skan g->running_routines = 1; 440169689Skan g = run_routines (g, NULL); 441169689Skan if (! g) 442169689Skan return; 443169689Skan g->running_routines = prev_running; 444169689Skan _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g); 445169689Skan} 446169689Skan#endif 447169689Skan 448169689Skan/* This is our wrapper around atexit and __cxa_atexit. It will return 449169689Skan nonzero if an error occurs, and otherwise: 450169689Skan - if in_cxa_finalize is set, or running on 10.3.9, add R to 451169689Skan KEYMGR_ATEXIT_LIST; or 452169689Skan - call the system __cxa_atexit to add cxa_atexit_wrapper with an argument 453169689Skan that indicates how cxa_atexit_wrapper should call R. */ 454169689Skan 455169689Skanstatic int 456169689Skanatexit_common (const struct one_atexit_routine *r, const void *dso) 457169689Skan{ 458169689Skan struct keymgr_atexit_list *g = get_globals (); 459169689Skan 460169689Skan if (! g) 461169689Skan return -1; 462169689Skan 463169689Skan if (g->running_routines || g->atexit_status == atexit_status_missing) 464169689Skan return add_routine (g, r); 465169689Skan 466169689Skan if (g->atexit_status >= atexit_status_working) 467169689Skan { 468169689Skan int result; 469169689Skan if (r->has_arg) 470169689Skan { 471169689Skan cxa_atexit_p cxa_atexit = g->cxa_atexit_f; 472169689Skan result = _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, 473169689Skan g); 474169689Skan if (CHECK_KEYMGR_ERROR (result)) 475169689Skan return -1; 476169689Skan return cxa_atexit (r->callback.cac, r->arg, dso); 477169689Skan } 478169689Skan else 479169689Skan { 480169689Skan atexit_p atexit_f = g->atexit_f; 481169689Skan result = _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, 482169689Skan g); 483169689Skan if (CHECK_KEYMGR_ERROR (result)) 484169689Skan return -1; 485169689Skan return atexit_f (r->callback.ac); 486169689Skan } 487169689Skan } 488169689Skan else 489169689Skan { 490169689Skan cxa_atexit_p cxa_atexit = g->cxa_atexit_f; 491169689Skan struct one_atexit_routine *alloced; 492169689Skan int result; 493169689Skan 494169689Skan result = _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ATEXIT_LIST, g); 495169689Skan if (CHECK_KEYMGR_ERROR (result)) 496169689Skan return -1; 497169689Skan 498169689Skan alloced = malloc (sizeof (struct one_atexit_routine)); 499169689Skan if (! alloced) 500169689Skan return -1; 501169689Skan *alloced = *r; 502169689Skan return cxa_atexit (cxa_atexit_wrapper, alloced, dso); 503169689Skan } 504169689Skan} 505169689Skan 506169689Skan/* These are the actual replacement routines; they just funnel into 507169689Skan atexit_common. */ 508169689Skan 509169689Skanint __cxa_atexit (cxa_atexit_callback func, void* arg, 510169689Skan const void* dso) __attribute__((visibility("hidden"))); 511169689Skan 512169689Skanint 513169689Skan__cxa_atexit (cxa_atexit_callback func, void* arg, const void* dso) 514169689Skan{ 515169689Skan struct one_atexit_routine r; 516169689Skan r.callback.cac = func; 517169689Skan r.has_arg = 1; 518169689Skan r.arg = arg; 519169689Skan return atexit_common (&r, dso); 520169689Skan} 521169689Skan 522169689Skanint atexit (atexit_callback func) __attribute__((visibility("hidden"))); 523169689Skan 524169689Skan/* Use __dso_handle to allow even bundles that call atexit() to be unloaded 525169689Skan on 10.4. */ 526169689Skanextern void __dso_handle; 527169689Skan 528169689Skanint 529169689Skanatexit (atexit_callback func) 530169689Skan{ 531169689Skan struct one_atexit_routine r; 532169689Skan r.callback.ac = func; 533169689Skan r.has_arg = 0; 534169689Skan return atexit_common (&r, &__dso_handle); 535169689Skan} 536169689Skan 537169689Skan#endif /* __PIC__ */ 538