/* Copyright (C) 1989, 92-97, 1998, 1999, Free Software Foundation, Inc. This file is part of GNU CC. GNU CC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU CC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * This file added by Apple Computer Inc. for its OS X * environment. */ /*#if defined(MACOSX) && !defined(APPLE_KERNEL_EXTENSION) */ #ifdef PART_OF_SYSTEM_FRAMEWORK #include #endif /* * keymgr - Create and maintain process-wide global data known to * all threads across all dynamic libraries. * */ #include "keymgr.h" #include #include /* * __keymgr_global - contains pointers to keymgr data that needs to be * global and known across all dlls/plugins. The array elements are defined * as follows: * Element #0: A pthreads semaphore for accessing the keymgr global data. * This would replace keymgr static semaphore. * * Element #1: Pointer to keymgr global list. This pointer was previously * contained in __eh_global_dataptr. * * Element #2: Pointer to keymgr information node. This is a new pointer. * initially the information node would contain two fields: * The number of words in the information node and * an API version number. * */ extern void *__eh_global_dataptr ; /*from system framework, being deprecated.*/ extern void *__keymgr_global[3] ; /*also from system framework*/ #define GET_KEYMGR_LIST_ROOT() ((_Tkey_Data *)__keymgr_global[1]) #define SET_KEYMGR_LIST_ROOT(ptr) (__keymgr_global[1] = ((void *) (ptr))) #define GET_KEYMGR_LIST_MUTEX_PTR() ((pthread_mutex_t *) __keymgr_global[0]) #define SET_KEYMGR_LIST_MUTEX_PTR(ptr) (__keymgr_global[0] = (void *)(ptr)) #define INIT_KEYMGR_LIST_MUTEX() (SET_KEYMGR_LIST_MUTEX_PTR(malloc(sizeof(pthread_mutex_t))), \ (*GET_KEYMGR_LIST_MUTEX_PTR() = _local_mutex_initializer)) #define LOCK_KEYMGR_LIST_MUTEX() (pthread_mutex_lock(GET_KEYMGR_LIST_MUTEX_PTR()) ? \ (abort(), 0) : 0 ) #define UNLOCK_KEYMGR_LIST_MUTEX() (pthread_mutex_unlock(GET_KEYMGR_LIST_MUTEX_PTR()) ? \ (abort(), 0) : 0 ) #define LOCK_KEYMGR_INIT() (pthread_mutex_lock(&_keymgr_init_lock) ? (abort(), 0) : 0 ) #define UNLOCK_KEYMGR_INIT() (pthread_mutex_unlock(&_keymgr_init_lock) ? (abort(), 0) : 0 ) #define KEYMGR_CREATE_THREAD_DATA_KEY(thread_key) \ ((pthread_key_create(&thread_key,NULL) != 0) ? \ (abort(),0) : 0) /* Version of the above which specifies free () as the destructor. */ #define KEYMGR_CREATE_MALLOCED_THREAD_DATA_KEY(thread_key) \ ((pthread_key_create(&thread_key, free) != 0) ? \ (abort(),0) : 0) #define KEYMGR_SET_THREAD_DATA(key,data) \ ((pthread_setspecific((key), (data)) != 0) ? \ (abort(),0) : 0) #define KEYMGR_GET_THREAD_DATA(key) (pthread_getspecific(key)) #define INIT_KEYMGR_NODE_LOCK(node) (node->thread_lock = _local_mutex_initializer) #define LOCK_KEYMGR_NODE(node) \ ((pthread_mutex_lock(&(node->thread_lock)) ? \ (abort(), 0) : 0 ),((node)->flags |= NM_LOCKED),\ ((node)->owning_thread = pthread_self()),node->refcount++) #define UNLOCK_KEYMGR_NODE(node) \ (((node)->flags &= ~NM_LOCKED),((node)->refcount--),\ (pthread_mutex_unlock(&(node->thread_lock)) ? \ (abort(), 0) : 0 )) #define LOCK_IS_MINE(node) (((node)->flags & NM_LOCKED)&& \ ((node)->owning_thread == pthread_self())) #define GET_KEYMGR_INFO_NODE_PTR() ((_Tinfo_Node *)__keymgr_global[2]) #define SET_KEYMGR_INFO_NODE_PTR(ptr) (__keymgr_global[2] = (void *)(ptr)) #define CHECK_KEYMGR_INIT() if (!_keymgr_init_mutex) _init_keymgr() ; #define KEYMGR_NODE_FLAGS(node,pflags) ((node)->flags & (pflags)) static int _keymgr_init_mutex = 0 ; static pthread_mutex_t _keyArray_lock=PTHREAD_MUTEX_INITIALIZER ; static pthread_mutex_t *_keyArray_lock_ptr=&_keyArray_lock ; static pthread_mutex_t _local_mutex_initializer = PTHREAD_MUTEX_INITIALIZER ; static pthread_mutex_t _keymgr_init_lock = PTHREAD_MUTEX_INITIALIZER ; /* * Base node kind for keymgr node list. * Also used to represent per thread variables (NODE_THREAD_SPECIFIC_DATA) */ typedef struct _Skey_data { struct _Skey_data *next ; unsigned int handle ; /*key id of variable.*/ void *ptr ; /*Pointer to variable data (or variable data).*/ int node_kind ; /*What kind of variable is this?*/ pthread_mutex_t thread_lock ; /*Semaphore for this specific variable.*/ pthread_t owning_thread ; /*the thread that owns, if tracking enabled.*/ unsigned int refcount ; /*If recursion has been enabled, reference count.*/ unsigned int flags ; /*Flags controlling behavior of variable.*/ } _Tkey_Data ; typedef struct _Sinfo_Node { unsigned int size ; /*size of this node*/ unsigned short major_version ; /*API major version.*/ unsigned short minor_version ; /*API minor version.*/ } _Tinfo_Node ; /* * Initialize keymgr and its related semaphore. * It is very important that the client be running * single threaded until this initialization completes. * Thus, threads should not be spawned at module initialization time. */ void _init_keymgr(void) { _Tinfo_Node *ti ; unsigned long address ; void *module ; /* this semaphore is just an accelerator to prevent repeated calls to pthreads.*/ if (_keymgr_init_mutex) return ; /* * If keymgr is living in system framework, it must be fully bound * before being used. Because calling dyld could cause another * user initialization routine to run, we don't set the initialization * semaphore until we get back. Once we are back, we are gauranteed not to * recurse. We then only have to worry about other threads. */ #ifdef PART_OF_SYSTEM_FRAMEWORK _dyld_lookup_and_bind_fully("__init_keymgr", &address, &module); /* * Check to see if a recursion occured and ran the initialization. */ if (_keymgr_init_mutex) return ; /*Lock out other threads.*/ LOCK_KEYMGR_INIT() ; /* * Check and see if another thread has already done the job. */ if (_keymgr_init_mutex) { UNLOCK_KEYMGR_INIT() ; return ; } #endif /*PART_OF_SYSTEM_FRAMEWORK*/ _keymgr_init_mutex++ ; /* * now we check to see if another instance has already initialized keymgr. * If keymgr is in the system framework, this will always be false. */ if (GET_KEYMGR_INFO_NODE_PTR()) { #ifdef PART_OF_SYSTEM_FRAMEWORK UNLOCK_KEYMGR_INIT() ; #endif return ; } INIT_KEYMGR_LIST_MUTEX() ; LOCK_KEYMGR_LIST_MUTEX() ; ti = malloc(sizeof(_Tinfo_Node)) ; ti->size = sizeof(_Tinfo_Node) ; ti->major_version = KEYMGR_API_REV_MAJOR ; ti->minor_version = KEYMGR_API_REV_MINOR ; SET_KEYMGR_INFO_NODE_PTR(ti) ; UNLOCK_KEYMGR_LIST_MUTEX() ; #ifdef PART_OF_SYSTEM_FRAMEWORK UNLOCK_KEYMGR_INIT() ; #endif } /* * Try to get module initialization to initialize keymgr before * anyone uses it. If not, the api calls check for lack of initialization * force initialization to occur anyway. Initialization invoked * by the API calls is more dangerous since there is a small * window where two threads could simultaneously enter the * initialization region. In general, to be safe, * this initialization should be run before multi-threading * is started. */ #pragma CALL_ON_LOAD _init_keymgr /* * These routines provide customized locking and * unlocking services. If enhanced locking is chosen * then recursion can be allowed or disallowed. */ static void _keymgr_lock(_Tkey_Data *node) { if (KEYMGR_NODE_FLAGS(node,NM_ENHANCED_LOCKING)) { if (LOCK_IS_MINE(node)) { if (KEYMGR_NODE_FLAGS(node,NM_RECURSION_ILLEGAL)) { abort() ; } else if (KEYMGR_NODE_FLAGS(node,NM_ALLOW_RECURSION)) { if(node->refcount) { node->refcount++ ; return ; } } else ; } } else ; LOCK_KEYMGR_NODE(node) ; } static void _keymgr_unlock(_Tkey_Data *node) { if (node->refcount > 1) { node->refcount-- ; return ; } UNLOCK_KEYMGR_NODE(node) ; } /* * These routines are the layer 1 (lowest level) routines. * No layer 1 routine should perform any locking/unlocking. */ static _Tkey_Data *_keymgr_get_key_element(unsigned int key) { _Tkey_Data *keyArray ; for (keyArray = GET_KEYMGR_LIST_ROOT() ; keyArray != NULL ; keyArray = keyArray->next) { if (keyArray->handle == key) break ; } return(keyArray) ; } static _Tkey_Data *_keymgr_create_key_element(unsigned int key, void *ptr, TnodeKind kind) { _Tkey_Data *keyArray ; keyArray = (_Tkey_Data *) malloc(sizeof(_Tkey_Data)) ; keyArray->handle = key ; keyArray->ptr = ptr ; keyArray->node_kind = kind ; keyArray->owning_thread = pthread_self() ; keyArray->refcount = 0; keyArray->flags = 0 ; INIT_KEYMGR_NODE_LOCK(keyArray) ; keyArray->next = GET_KEYMGR_LIST_ROOT() ; SET_KEYMGR_LIST_ROOT(keyArray) ; return(keyArray) ; } static _Tkey_Data *_keymgr_set_key_element(unsigned int key, void *ptr, TnodeKind kind) { _Tkey_Data *keyArray ; keyArray = _keymgr_get_key_element(key) ; if (keyArray == NULL) { keyArray = _keymgr_create_key_element(key,ptr,kind) ; } else if (keyArray->node_kind != kind) { abort() ; } else { keyArray->ptr = ptr ; } return(keyArray) ; } static _Tkey_Data *_keymgr_get_or_create_key_element(unsigned int key, TnodeKind kind) { _Tkey_Data *keyArray ; LOCK_KEYMGR_LIST_MUTEX() ; keyArray = _keymgr_get_key_element(key) ; if (keyArray == NULL) { keyArray = _keymgr_create_key_element(key, NULL, kind) ; } else if (kind && (keyArray->node_kind != kind)) abort() ; else ; UNLOCK_KEYMGR_LIST_MUTEX() ; return(keyArray) ; } /* * External interfaces to keymgr. * The purpose of these routines is to store * runtime information in a global place * accessible from all threads and all dlls. * Thread safety is guaranteed. * _keymgr_get_key must be called first. It locks all keys * and returns the value of the specified key. * Next either _keymgr_unlock_keys can be called to unlock * the keys and allow them to change in value or * _keymgr_set_key can be called to set the value * of the key and then unlock all keys. The key number itself * is determined by the user. To prevent collisions, register * your key via a define in keymgr.h. The first use of * a key causes a memory location for the key value * to be reserved. As long as no key number collision * occurs, this interface allows different versions * of the runtime to exists together. */ static void *_keymgr_get_and_lock_key(unsigned int key, TnodeKind kind) { _Tkey_Data *keyArray ; void *retptr ; keyArray = _keymgr_get_or_create_key_element(key,kind) ; _keymgr_lock(keyArray) ; retptr = keyArray->ptr ; return(retptr) ; } static void _keymgr_unlock_key(unsigned int key) { _Tkey_Data *keyArray ; LOCK_KEYMGR_LIST_MUTEX() ; keyArray = _keymgr_get_key_element(key) ; UNLOCK_KEYMGR_LIST_MUTEX() ; if (keyArray) _keymgr_unlock(keyArray) ; } static void *_keymgr_set_and_unlock_key(unsigned int key, void *ptr, TnodeKind kind) { _Tkey_Data *keyArray ; void * retptr ; LOCK_KEYMGR_LIST_MUTEX() ; keyArray = _keymgr_set_key_element(key, ptr, kind) ; retptr = keyArray->ptr ; UNLOCK_KEYMGR_LIST_MUTEX() ; _keymgr_unlock(keyArray) ; return(retptr) ; } /* * The following routines allow the user to store per thread * data using a key of the user's choice. * * _keymgr_init_per_thread_data creates a pthread's key * if it doesn't exist already. The data associated with * the pthreads' key is initialized to NULL. The pthread key * is associated with the specified keymgr key. The key must have * been locked by a _keymgr_get_and_lock_key operation. * The key will be returned unlocked. * * _keymgr_get_per_thread_data lock's the user's key * and maps it to a pthreads' key. The pthreads' data associated * with that key is returned. The user key is then unlocked. * * _keymgr_set_per_thread_data lock's the user's key * and maps it to a pthreads' key. The argument data is then * used to update the data associated with the pthreads' key. * Then the user's key is unlocked. * */ static void _keymgr_init_per_thread_data(unsigned int key) { void * pthread_data ; pthread_key_t pthread_key ; _Tkey_Data *keyArray ; /* * Caller insures that node has been created already. */ LOCK_KEYMGR_LIST_MUTEX() ; keyArray = _keymgr_get_key_element(key) ; UNLOCK_KEYMGR_LIST_MUTEX() ; /* * The caller has already created a node, if it didn't * exist, and has locked it in all cases. */ pthread_key = (pthread_key_t) keyArray->ptr ; if (pthread_key == NULL) { switch (key) { case KEYMGR_EH_CONTEXT_KEY: KEYMGR_CREATE_MALLOCED_THREAD_DATA_KEY (pthread_key); break; default: KEYMGR_CREATE_THREAD_DATA_KEY(pthread_key) ; break; } _keymgr_set_and_unlock_key(key,(void *) pthread_key, NODE_THREAD_SPECIFIC_DATA) ; } else { /* * since we are done messing around with the key itself, * we can unlock it. */ _keymgr_unlock(keyArray) ; /* Now we set the data associated with the key to NULL.*/ KEYMGR_SET_THREAD_DATA(pthread_key, NULL) ; } } void * _keymgr_get_per_thread_data(unsigned int key) { pthread_key_t pthread_key ; void * pthread_data ; _init_keymgr() ; /*confirm keymgr has been initialized.*/ pthread_key = (pthread_key_t) _keymgr_get_and_lock_key(key,NODE_THREAD_SPECIFIC_DATA) ; /* * If the user key has not had a pthread's key associated * with it, do so now. */ if (pthread_key == NULL) { _keymgr_init_per_thread_data(key) ; /*unlocks keys.*/ pthread_data = NULL ; } /* * The key can be unlocked because now we are just * dealing with the current thread's data. */ else { _keymgr_unlock_key(key) ; pthread_data = KEYMGR_GET_THREAD_DATA(pthread_key) ; } return(pthread_data) ; } void _keymgr_set_per_thread_data(unsigned int key, void *keydata) { pthread_key_t pthread_key ; _init_keymgr() ; /*confirm keymgr has been initialized.*/ pthread_key = (pthread_key_t) _keymgr_get_and_lock_key(key,NODE_THREAD_SPECIFIC_DATA) ; if (pthread_key == NULL) { _keymgr_init_per_thread_data(key) ; } /* * The key can be unlocked because now we are just * dealing with the current thread's data. */ _keymgr_unlock_key(key) ; KEYMGR_SET_THREAD_DATA(pthread_key, keydata) ; } /* * These routines provide management of a process wide, thread safe, * persistent pointer. If a pointer is created by a bundle/plug-in * and placed in here, it will persist for the life of the process, * even after the bundle has been unloaded. This is especially useful * for data shared across plugins and across repeated plugin loads and * unloads. */ void *_keymgr_get_and_lock_processwide_ptr(unsigned int key) { void * retptr ; _init_keymgr() ; /*confirm keymgr has been initialized.*/ retptr = _keymgr_get_and_lock_key(key,NODE_PROCESSWIDE_PTR) ; return(retptr) ; } void _keymgr_set_and_unlock_processwide_ptr(unsigned int key, void *ptr) { _init_keymgr() ; /*confirm keymgr has been initialized.*/ _keymgr_set_and_unlock_key(key,ptr,NODE_PROCESSWIDE_PTR) ; } void _keymgr_unlock_processwide_ptr(unsigned int key) { _init_keymgr() ; /*confirm keymgr has been initialized.*/ _keymgr_unlock_key(key) ; } void _keymgr_set_lockmode_processwide_ptr(unsigned int key, unsigned int mode) { _Tkey_Data *keyArray ; _init_keymgr() ; /*confirm keymgr has been initialized.*/ keyArray = _keymgr_get_or_create_key_element(key,NODE_PROCESSWIDE_PTR) ; keyArray->flags = (keyArray->flags & ~NM_ENHANCED_LOCKING) | mode ; } unsigned int _keymgr_get_lockmode_processwide_ptr(unsigned int key) { _Tkey_Data *keyArray ; _init_keymgr() ; /*confirm keymgr has been initialized.*/ keyArray = _keymgr_get_or_create_key_element(key,NODE_PROCESSWIDE_PTR) ; return(keyArray->flags) ; } int _keymgr_get_lock_count_processwide_ptr(unsigned int key) { _Tkey_Data *keyArray ; _init_keymgr() ; /*confirm keymgr has been initialized.*/ keyArray = _keymgr_get_or_create_key_element(key,NODE_PROCESSWIDE_PTR) ; return(keyArray->refcount) ; } /*********************************************/ /* We *could* include here but since all we care about is the name of the mach_header struct, this will do instead. */ struct mach_header; extern char *getsectdatafromheader(); /* The following two macros are required for the inclusion of "gcc/frame.h". FRAME_SECTION_DESCRIPTOR should be a bunch of field declarations with which we extend the OBJECT structure. FIRST_PSEUDO_REGISTER can be 1 since we don't use it here. */ #define FRAME_SECTION_DESCRIPTOR long section_size; #define FIRST_PSEUDO_REGISTER 1 #include "gcc/frame.h" static const char __DWARF2_UNWIND_SECTION_TYPE[] = "__TEXT"; static const char __DWARF2_UNWIND_SECTION_NAME[] = "__dwarf2_unwind"; /* Called by dyld when an image is added to the executable. If it has a dwarf2_unwind section, register it so the C++ runtime can get at it. All of this is protected by dyld thread locks. */ static void dwarf2_unwind_dyld_add_image_hook (struct mach_header *mh, unsigned long vm_slide) { unsigned long sz; char *fde; /* See if the image has a __TEXT __dwarf2_unwind section. */ fde = getsectdatafromheader (mh, __DWARF2_UNWIND_SECTION_TYPE, __DWARF2_UNWIND_SECTION_NAME, &sz); if (0) printf ("add_image_hook: mach_header %08x vm_slide %08x " "\n", mh, vm_slide, fde); if (fde != 0) { struct object *obp; obp = (struct object *) calloc (1, sizeof (struct object) + 8); obp->pc_begin = obp->pc_end = 0; obp->fde_array = 0; obp->count = 0; obp->section_size = sz; obp->fde_begin = (struct dwarf_fde *) (fde + vm_slide); obp->next = (struct object *) _keymgr_get_and_lock_processwide_ptr (KEYMGR_ZOE_IMAGE_LIST); _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ZOE_IMAGE_LIST, obp); } } static void dwarf2_unwind_dyld_remove_image_hook (struct mach_header *mh, unsigned long vm_slide) { unsigned long sz; char *fde; /* See if the image has a __TEXT __dwarf2_unwind section. */ fde = getsectdatafromheader (mh, __DWARF2_UNWIND_SECTION_TYPE, __DWARF2_UNWIND_SECTION_NAME, &sz); if (fde != 0) { struct object *objlist, **obp; objlist = (struct object *) _keymgr_get_and_lock_processwide_ptr (KEYMGR_ZOE_IMAGE_LIST); for (obp = &objlist; *obp; obp = &(*obp)->next) if ((char *)(*obp)->fde_begin == fde + vm_slide) { struct object *p = *obp; *obp = p->next; if (p->pc_begin) free (p->fde_array); free (p); break; } _keymgr_set_and_unlock_processwide_ptr (KEYMGR_ZOE_IMAGE_LIST, objlist); } } /* __keymgr_dwarf2_register_sections is called by crt1. */ void __keymgr_dwarf2_register_sections (void) { _dyld_register_func_for_add_image (dwarf2_unwind_dyld_add_image_hook); _dyld_register_func_for_remove_image (dwarf2_unwind_dyld_remove_image_hook); } /*#endif MACOSX*/