#include <mach-o/dyld.h>
#include <pthread.h>
#include <stdlib.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
};
#define UNLOCK_KEYMGR_LIST_MUTEX() \
(pthread_mutex_unlock (__keymgr_global.keymgr_mutex) \
? (abort (), 0) : 0)
#if defined(__ppc__) || defined(__i386__)
void _init_keymgr (void)
{
}
#endif
#if defined (__ppc64__)
static inline void *
compare_and_swap (void * volatile * p, void *old, void *new)
{
void *result;
asm ("0: ldarx %0,0,%2\n"
" cmpd%I3 %0,%3\n"
" bne 1f\n"
" stdcx. %4,0,%2\n"
" bne- 0b\n"
"1:"
: "=&r" (result), "+m"(*p)
: "b" (p), "rI" (old), "r" (new)
: "cr0");
return result;
}
static inline void
atomic_list_insert (Tkey_Data * volatile *list_base,
Tkey_Data * * next,
Tkey_Data * current)
{
Tkey_Data *temp;
asm ("0: ldarx %0,0,%3\n"
" std%U1%X1 %0,%1\n"
" sync\n"
" stdcx. %4,0,%3\n"
" bne- 0b"
: "=&r" (temp), "=m" (*next), "+m" (*list_base)
: "b" (list_base), "r" (current));
}
#elif defined (__ppc__)
static inline void *
compare_and_swap (void * volatile * p, void *old, void *new)
{
void *result;
asm ("0: lwarx %0,0,%2\n"
" cmpw%I3 %0,%3\n"
" bne 1f\n"
" stwcx. %4,0,%2\n"
" bne- 0b\n"
"1:"
: "=&r" (result), "+m"(*p)
: "b" (p), "rI" (old), "r" (new)
: "cr0");
return result;
}
static inline void
atomic_list_insert (Tkey_Data * volatile *list_base,
Tkey_Data * * next,
Tkey_Data * current)
{
Tkey_Data *temp;
asm ("0: lwarx %0,0,%3\n"
" stw%U1%X1 %0,%1\n"
" sync\n"
" stwcx. %4,0,%3\n"
" bne- 0b"
: "=&r" (temp), "=m" (*next), "+m" (*list_base)
: "b" (list_base), "r" (current));
}
#elif defined (__i386__)
static inline void *
compare_and_swap (void * volatile * p, void *old, void *new)
{
void *result;
asm ("lock ; cmpxchgl %3,%1"
: "=a" (result), "+m"(*p)
: "0" (old), "r" (new));
return result;
}
static inline void
atomic_list_insert (Tkey_Data * volatile *list_base,
Tkey_Data * * next,
Tkey_Data * current)
{
do {
*next = *list_base;
} while (compare_and_swap ((void * volatile *)list_base,
*next, current) != *next);
}
#else
#error architecture not supported
#endif
static Tkey_Data *
find_key_data (unsigned int key)
{
Tkey_Data * keyArray;
for (keyArray = __keymgr_global.keymgr_globals;
keyArray != NULL;
keyArray = keyArray->next)
if (keyArray->handle == key)
break;
return keyArray;
}
static Tkey_Data *
get_key_element (unsigned int key, TnodeKind kind)
{
Tkey_Data *keyArray;
keyArray = find_key_data (key);
if (keyArray != NULL && keyArray->node_kind != kind)
keyArray = NULL;
return keyArray;
}
static int
get_or_create_key_element (unsigned int key, TnodeKind kind,
Tkey_Data **result)
{
Tkey_Data *keyArray;
keyArray = find_key_data (key);
if (keyArray == NULL)
{
keyArray = (Tkey_Data *) malloc (sizeof (Tkey_Data));
if (keyArray == NULL)
return ENOMEM;
if (keyArray != NULL
&& kind == NODE_PROCESSWIDE_PTR)
{
pthread_mutexattr_t attr;
int errnum;
keyArray->refcount = 0;
keyArray->flags = 0;
errnum = pthread_mutexattr_init (&attr);
if (errnum == ESUCCESS)
{
pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ERRORCHECK);
errnum = pthread_mutex_init (&keyArray->thread_lock, &attr);
pthread_mutexattr_destroy (&attr);
}
if (errnum != ESUCCESS)
{
free (keyArray);
return errnum;
}
}
keyArray->handle = key;
keyArray->ptr = NULL;
keyArray->node_kind = kind;
atomic_list_insert (&__keymgr_global.keymgr_globals,
&keyArray->next,
keyArray);
}
else if (keyArray->node_kind != kind)
return EINVAL;
*result = keyArray;
return ESUCCESS;
}
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 *);
switch (key)
{
case KEYMGR_EH_CONTEXT_KEY:
destructor = free;
break;
default:
destructor = NULL;
break;
}
if ((errnum = pthread_key_create (&pthread_key, destructor)) != 0)
return errnum;
ptr = compare_and_swap (&keyArray->ptr, NULL, (void *)pthread_key);
if (ptr != NULL)
pthread_key_delete (pthread_key);
}
if (ptr != NULL)
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;
}
struct mach_header;
extern char *getsectdatafromheader ();
extern void __cxa_finalize (void *);
struct __live_images {
unsigned long this_size;
struct mach_header *mh;
unsigned long 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 (struct mach_header *mh,
unsigned long vm_slide)
{
#ifdef __ppc__
unsigned long 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 (struct mach_header *mh,
unsigned long vm_slide)
{
#ifdef __ppc__
unsigned long 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
((long) 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);
}