#include <mach-o/dyld.h>
#include <pthread.h>
#include <errno.h>
#include <stdlib.h>
#include <libkern/OSAtomic.h>
#include <stdint.h>
#include "keymgr.h"
#ifndef ESUCCESS
#define ESUCCESS 0
#endif
typedef enum node_kinds {
NODE_THREAD_SPECIFIC_DATA=1,
NODE_PROCESSWIDE_PTR
} TnodeKind;
enum {
NM_ENHANCED_LOCKING=3
};
typedef struct Skey_data {
struct Skey_data * next;
unsigned int handle;
unsigned char node_kind;
unsigned char flags;
unsigned short refcount;
void *ptr;
pthread_mutex_t thread_lock;
} Tkey_Data;
typedef struct Sinfo_Node {
unsigned int size;
unsigned short major_version;
unsigned short minor_version;
} Tinfo_Node;
static const Tinfo_Node keymgr_info = {
sizeof (Tinfo_Node),
KEYMGR_API_REV_MAJOR,
KEYMGR_API_REV_MINOR
};
struct {
void *unused;
Tkey_Data * volatile keymgr_globals;
const Tinfo_Node *keymgr_info;
} __attribute__((aligned (32))) __keymgr_global = {
NULL,
NULL,
&keymgr_info
};
#if defined(__ppc__)
void _init_keymgr (void)
{
}
#endif
static Tkey_Data *
get_key_element (unsigned int key, TnodeKind kind)
{
Tkey_Data *keyArray;
for (keyArray = __keymgr_global.keymgr_globals;
keyArray != NULL;
keyArray = keyArray->next)
if (keyArray->handle == key)
{
if (keyArray->node_kind == kind)
return keyArray;
else
return NULL;
}
return NULL;
}
static int
get_or_create_key_element (unsigned int key, TnodeKind kind,
Tkey_Data **result)
{
Tkey_Data *searchEnd = NULL;
Tkey_Data *newEntry = NULL;
for (;;)
{
Tkey_Data *keyArrayStart = __keymgr_global.keymgr_globals;
Tkey_Data *keyArray;
for (keyArray = keyArrayStart;
keyArray != searchEnd;
keyArray = keyArray->next)
if (keyArray->handle == key)
{
if (newEntry)
{
if (kind == NODE_PROCESSWIDE_PTR)
if (pthread_mutex_destroy (&newEntry->thread_lock)
!= ESUCCESS)
abort ();
free (newEntry);
}
if (keyArray->node_kind != kind)
return EINVAL;
*result = keyArray;
return ESUCCESS;
}
if (! newEntry)
{
newEntry = (Tkey_Data *) malloc (sizeof (Tkey_Data));
if (newEntry == NULL)
return ENOMEM;
if (kind == NODE_PROCESSWIDE_PTR)
{
pthread_mutexattr_t attr;
int errnum;
newEntry->refcount = 0;
newEntry->flags = 0;
errnum = pthread_mutexattr_init (&attr);
if (errnum == ESUCCESS)
{
pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ERRORCHECK);
errnum = pthread_mutex_init (&newEntry->thread_lock, &attr);
pthread_mutexattr_destroy (&attr);
}
if (errnum != ESUCCESS)
{
free (newEntry);
return errnum;
}
}
newEntry->handle = key;
newEntry->ptr = NULL;
newEntry->node_kind = kind;
}
newEntry->next = keyArrayStart;
#ifdef __ppc64__
if (OSAtomicCompareAndSwap64Barrier (
(int64_t) keyArrayStart,
(int64_t) newEntry,
(int64_t *) &__keymgr_global.keymgr_globals))
#elif defined (__i386__) || defined(__ppc__)
if (OSAtomicCompareAndSwap32Barrier (
(int32_t) keyArrayStart,
(int32_t) newEntry,
(int32_t *) &__keymgr_global.keymgr_globals))
#else
#error unknown pointer size
#endif
{
*result = newEntry;
return ESUCCESS;
}
searchEnd = keyArrayStart;
}
}
void *
_keymgr_get_per_thread_data (unsigned int key)
{
Tkey_Data * keyArray;
void * key_data;
keyArray = get_key_element (key, NODE_THREAD_SPECIFIC_DATA);
if (keyArray == NULL)
return NULL;
key_data = keyArray->ptr;
if (key_data == NULL)
return NULL;
return pthread_getspecific ((pthread_key_t) key_data);
}
int
_keymgr_set_per_thread_data (unsigned int key, void *keydata)
{
volatile Tkey_Data * keyArray;
pthread_key_t pthread_key;
void *ptr;
int errnum;
errnum = get_or_create_key_element (key, NODE_THREAD_SPECIFIC_DATA,
(Tkey_Data **)&keyArray);
if (errnum != ESUCCESS)
return errnum;
ptr = keyArray->ptr;
if (ptr == NULL)
{
void (*destructor)(void *);
bool neededInit;
switch (key)
{
case KEYMGR_EH_CONTEXT_KEY:
case KEYMGR_EH_GLOBALS_KEY:
destructor = free;
break;
default:
destructor = NULL;
break;
}
if ((errnum = pthread_key_create (&pthread_key, destructor)) != 0)
return errnum;
#ifdef __ppc64__
neededInit = OSAtomicCompareAndSwap64 ((int64_t) NULL,
(int64_t) pthread_key,
(int64_t *) &keyArray->ptr);
#elif defined(__ppc__) || defined (__i386__)
neededInit = OSAtomicCompareAndSwap32 ((int32_t) NULL,
(int32_t) pthread_key,
(int32_t *) &keyArray->ptr);
#else
#error unknown pointer size
#endif
if (!neededInit)
pthread_key_delete (pthread_key);
ptr = keyArray->ptr;
}
pthread_key = (pthread_key_t) ptr;
return pthread_setspecific (pthread_key, keydata);
}
int
_keymgr_get_and_lock_processwide_ptr_2 (unsigned int key, void ** result)
{
Tkey_Data *keyArray;
int errnum;
errnum = get_or_create_key_element (key, NODE_PROCESSWIDE_PTR, &keyArray);
if (errnum != ESUCCESS)
return errnum;
if ((errnum = pthread_mutex_lock (&keyArray->thread_lock)) != ESUCCESS)
return errnum;
keyArray->refcount++;
*result = keyArray->ptr;
return ESUCCESS;
}
void *
_keymgr_get_and_lock_processwide_ptr (unsigned int key)
{
void *result = NULL;
_keymgr_get_and_lock_processwide_ptr_2 (key, &result);
return result;
}
static int
unlock_node (Tkey_Data *node)
{
int result;
node->refcount--;
result = pthread_mutex_unlock (&node->thread_lock);
if (result != ESUCCESS)
node->refcount++;
return result;
}
int
_keymgr_set_and_unlock_processwide_ptr (unsigned int key, void *ptr)
{
Tkey_Data *keyArray;
int result;
result = get_or_create_key_element (key, NODE_PROCESSWIDE_PTR, &keyArray);
if (result != ESUCCESS)
return result;
keyArray->ptr = ptr;
return unlock_node (keyArray);
}
int
_keymgr_unlock_processwide_ptr (unsigned int key)
{
Tkey_Data *keyArray;
keyArray = get_key_element (key, NODE_PROCESSWIDE_PTR);
if (keyArray == NULL)
return EINVAL;
return unlock_node (keyArray);
}
int
_keymgr_set_lockmode_processwide_ptr (unsigned int key, unsigned int mode)
{
Tkey_Data *keyArray;
pthread_mutexattr_t attr;
int type;
int result;
result = get_or_create_key_element (key, NODE_PROCESSWIDE_PTR, &keyArray);
if (result != ESUCCESS)
return result;
if (mode == keyArray->flags)
return ESUCCESS;
result = pthread_mutexattr_init (&attr);
if (result != ESUCCESS)
return result;
if (mode == NM_ALLOW_RECURSION)
type = PTHREAD_MUTEX_RECURSIVE;
else
type = PTHREAD_MUTEX_ERRORCHECK;
pthread_mutexattr_settype (&attr, type);
result = pthread_mutex_destroy (&keyArray->thread_lock);
if (result == ESUCCESS)
result = pthread_mutex_init (&keyArray->thread_lock, &attr);
pthread_mutexattr_destroy (&attr);
if (result == ESUCCESS)
keyArray->flags = mode;
return result;
}
unsigned int
_keymgr_get_lockmode_processwide_ptr (unsigned int key)
{
Tkey_Data *keyArray;
keyArray = get_key_element (key, NODE_PROCESSWIDE_PTR);
if (keyArray == NULL)
return 0;
return keyArray->flags;
}
int
_keymgr_get_lock_count_processwide_ptr (unsigned int key)
{
Tkey_Data *keyArray;
keyArray = get_key_element (key, NODE_PROCESSWIDE_PTR);
if (keyArray == NULL)
return 0;
return keyArray->refcount;
}
#include <mach-o/getsect.h>
#include <atexit.h>
struct __live_images {
unsigned long this_size;
const struct mach_header *mh;
intptr_t vm_slide;
void (*destructor)(struct __live_images *);
struct __live_images *next;
unsigned long examined_p;
void *fde;
void *object_info;
unsigned long info[2];
};
enum {
EXAMINED_IMAGE_MASK = 1,
ALLOCED_IMAGE_MASK = 2,
IMAGE_IS_TEXT_MASK = 4,
DESTRUCTOR_MAY_BE_CALLED_LIVE = 8
};
#ifdef __ppc__
struct old_object
{
void *pc_begin;
void *pc_end;
struct dwarf_fde *fde_begin;
struct dwarf_fde **fde_array;
size_t count;
struct old_object *next;
long section_size;
};
static const char __DWARF2_UNWIND_SECTION_TYPE[] = "__TEXT";
static const char __DWARF2_UNWIND_SECTION_NAME[] = "__dwarf2_unwind";
#endif
static void dwarf2_unwind_dyld_add_image_hook (const struct mach_header *mh,
intptr_t vm_slide)
{
#ifdef __ppc__
uint32_t sz;
char *fde;
fde = getsectdatafromheader (mh, __DWARF2_UNWIND_SECTION_TYPE,
__DWARF2_UNWIND_SECTION_NAME, &sz);
if (fde != 0)
{
struct old_object *obp;
obp = (struct old_object *) calloc (1, sizeof (struct old_object) + 8);
if (obp == NULL)
return;
obp->section_size = sz;
obp->fde_begin = (struct dwarf_fde *) (fde + vm_slide);
if (_keymgr_get_and_lock_processwide_ptr_2 (KEYMGR_ZOE_IMAGE_LIST,
(void **) &obp->next) == 0)
_keymgr_set_and_unlock_processwide_ptr (KEYMGR_ZOE_IMAGE_LIST, obp);
}
#endif
{
struct __live_images *l = (struct __live_images *) calloc (1, sizeof (*l));
if (l == NULL)
return;
l->mh = mh;
l->vm_slide = vm_slide;
l->this_size = sizeof (*l);
if (_keymgr_get_and_lock_processwide_ptr_2 (KEYMGR_GCC3_LIVE_IMAGE_LIST,
(void **) &l->next) == 0)
_keymgr_set_and_unlock_processwide_ptr (KEYMGR_GCC3_LIVE_IMAGE_LIST, l);
}
}
static void
dwarf2_unwind_dyld_remove_image_hook (const struct mach_header *mh,
intptr_t vm_slide)
{
#ifdef __ppc__
uint32_t sz;
char *fde;
fde = getsectdatafromheader (mh, __DWARF2_UNWIND_SECTION_TYPE,
__DWARF2_UNWIND_SECTION_NAME, &sz);
if (fde != 0)
{
struct old_object *objlist, **obp;
if (_keymgr_get_and_lock_processwide_ptr_2 (KEYMGR_ZOE_IMAGE_LIST,
(void **) &objlist) != 0)
goto get_zoe_failed;
for (obp = &objlist; *obp; obp = &(*obp)->next)
if ((char *)(*obp)->fde_begin == fde + vm_slide)
{
struct old_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);
get_zoe_failed:
;
}
#endif
__cxa_finalize (mh);
{
struct __live_images *top, **lip, *destroy = NULL;
void (*prev_destructor)(struct __live_images *) = NULL;
int was_in_object = 0;
if (_keymgr_get_and_lock_processwide_ptr_2 (KEYMGR_GCC3_LIVE_IMAGE_LIST,
(void **) &top) != 0)
goto get_live_image_failed;
lip = ⊤
while (*lip != NULL)
{
if ((*lip)->destructor
&& ((*lip)->examined_p & DESTRUCTOR_MAY_BE_CALLED_LIVE))
{
if (! was_in_object && (*lip)->destructor != prev_destructor)
{
prev_destructor = (*lip)->destructor;
was_in_object = ((_dyld_get_image_header_containing_address
(prev_destructor))
== mh);
}
if ((*lip)->destructor == prev_destructor && was_in_object)
(*lip)->destructor (*lip);
}
if ((*lip)->mh == mh && (*lip)->vm_slide == vm_slide)
{
destroy = *lip;
*lip = destroy->next;
if (destroy->this_size != sizeof (*destroy))
abort ();
continue;
}
lip = &(*lip)->next;
}
_keymgr_set_and_unlock_processwide_ptr (KEYMGR_GCC3_LIVE_IMAGE_LIST, top);
if (destroy != NULL)
{
if (destroy->destructor != NULL)
(*destroy->destructor) (destroy);
free (destroy);
}
get_live_image_failed:
;
}
}
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);
}