X-Git-Url: https://git.saurik.com/apple/dyld.git/blobdiff_plain/412ebb8e3cc35d457058c31310d89ef96b7c416d..refs/heads/master:/src/threadLocalVariables.c?ds=inline diff --git a/src/threadLocalVariables.c b/src/threadLocalVariables.c index 2586455..bb3c5ff 100644 --- a/src/threadLocalVariables.c +++ b/src/threadLocalVariables.c @@ -34,7 +34,7 @@ #include #include -#include "dyld_priv.h" +#include #if __LP64__ @@ -49,40 +49,21 @@ typedef struct section macho_section; #endif -#ifndef S_THREAD_LOCAL_REGULAR -#define S_THREAD_LOCAL_REGULAR 0x11 -#endif - -#ifndef S_THREAD_LOCAL_ZEROFILL -#define S_THREAD_LOCAL_ZEROFILL 0x12 -#endif - -#ifndef S_THREAD_LOCAL_VARIABLES -#define S_THREAD_LOCAL_VARIABLES 0x13 -#endif - -#ifndef S_THREAD_LOCAL_VARIABLE_POINTERS -#define S_THREAD_LOCAL_VARIABLE_POINTERS 0x14 -#endif -#ifndef S_THREAD_LOCAL_INIT_FUNCTION_POINTERS -#define S_THREAD_LOCAL_INIT_FUNCTION_POINTERS 0x15 -#endif +typedef void (*TermFunc)(void*); -#ifndef MH_HAS_TLV_DESCRIPTORS - #define MH_HAS_TLV_DESCRIPTORS 0x800000 -#endif -#if __i386__ || __x86_64__ -typedef struct TLVHandler { - struct TLVHandler *next; - dyld_tlv_state_change_handler handler; - enum dyld_tlv_states state; -} TLVHandler; +#if __has_feature(tls) || __arm64__ || __arm__ -// lock-free prepend-only linked list -static TLVHandler * volatile tlv_handlers = NULL; +// +// Info about thread-local variable storage. +// +typedef struct { + size_t info_size; // sizeof(dyld_tlv_info) + void * tlv_addr; // Base address of TLV storage + size_t tlv_size; // Byte size of TLV storage +} dyld_tlv_info; struct TLVDescriptor @@ -143,31 +124,17 @@ static const struct mach_header* tlv_get_image_for_key(pthread_key_t key) } -static void -tlv_notify(enum dyld_tlv_states state, void *buffer) -{ - if (!tlv_handlers) return; - - // Always use malloc_size() to ensure allocated and deallocated states - // send the same size. tlv_free() doesn't have anything else recorded. - dyld_tlv_info info = { sizeof(info), buffer, malloc_size(buffer) }; - - for (TLVHandler *h = tlv_handlers; h != NULL; h = h->next) { - if (h->state == state && h->handler) { - h->handler(h->state, &info); - } - } -} - - // called lazily when TLV is first accessed __attribute__((visibility("hidden"))) void* tlv_allocate_and_initialize_for_key(pthread_key_t key) { const struct mach_header* mh = tlv_get_image_for_key(key); + if ( mh == NULL ) + return NULL; // if data structures are screwed up, don't crash + // first pass, find size and template uint8_t* start = NULL; - unsigned long size; + unsigned long size = 0; intptr_t slide = 0; bool slideComputed = false; bool hasInitializers = false; @@ -206,6 +173,9 @@ void* tlv_allocate_and_initialize_for_key(pthread_key_t key) } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } + // no thread local storage in image: should never happen + if ( size == 0 ) + return NULL; // allocate buffer and fill with template void* buffer = malloc(size); @@ -213,9 +183,6 @@ void* tlv_allocate_and_initialize_for_key(pthread_key_t key) // set this thread's value for key to be the new buffer. pthread_setspecific(key, buffer); - - // send tlv state notifications - tlv_notify(dyld_tlv_state_allocated, buffer); // second pass, run initializers if ( hasInitializers ) { @@ -229,9 +196,9 @@ void* tlv_allocate_and_initialize_for_key(pthread_key_t key) if ( (sect->flags & SECTION_TYPE) == S_THREAD_LOCAL_INIT_FUNCTION_POINTERS ) { typedef void (*InitFunc)(void); InitFunc* funcs = (InitFunc*)(sect->addr + slide); - const uint32_t count = sect->size / sizeof(uintptr_t); - for (uint32_t i=count; i > 0; --i) { - InitFunc func = funcs[i-1]; + const size_t count = sect->size / sizeof(uintptr_t); + for (size_t j=count; j > 0; --j) { + InitFunc func = funcs[j-1]; func(); } } @@ -248,7 +215,6 @@ void* tlv_allocate_and_initialize_for_key(pthread_key_t key) static void tlv_free(void *storage) { - tlv_notify(dyld_tlv_state_deallocated, storage); free(storage); } @@ -297,50 +263,13 @@ static void tlv_initialize_descriptors(const struct mach_header* mh) } } -// called by dyld when a image is loaded -static const char* tlv_load_notification(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) -{ - // this is called on all images, even those without TLVs, so we want - // this to be fast. The linker sets MH_HAS_TLV_DESCRIPTORS so we don't - // have to search images just to find the don't have TLVs. - for (uint32_t i=0; i < infoCount; ++i) { - if ( info[i].imageLoadAddress->flags & MH_HAS_TLV_DESCRIPTORS ) - tlv_initialize_descriptors(info[i].imageLoadAddress); - } - return NULL; -} - - -void dyld_register_tlv_state_change_handler(enum dyld_tlv_states state, dyld_tlv_state_change_handler handler) -{ - TLVHandler *h = malloc(sizeof(TLVHandler)); - h->state = state; - h->handler = Block_copy(handler); - - TLVHandler *old; - do { - old = tlv_handlers; - h->next = old; - } while (! OSAtomicCompareAndSwapPtrBarrier(old, h, (void * volatile *)&tlv_handlers)); -} - -void dyld_enumerate_tlv_storage(dyld_tlv_state_change_handler handler) +static void tlv_load_notification(const struct mach_header* mh, intptr_t slide) { - pthread_mutex_lock(&tlv_live_image_lock); - unsigned int count = tlv_live_image_used_count; - void *list[count]; - for (unsigned int i = 0; i < count; ++i) { - list[i] = pthread_getspecific(tlv_live_images[i].key); - } - pthread_mutex_unlock(&tlv_live_image_lock); - - for (unsigned int i = 0; i < count; ++i) { - if (list[i]) { - dyld_tlv_info info = { sizeof(info), list[i], malloc_size(list[i]) }; - handler(dyld_tlv_state_allocated, &info); - } - } + // This is called on all images, even those without TLVs. So we want this to be fast. + // The linker sets MH_HAS_TLV_DESCRIPTORS so we don't have to search images just to find the don't have TLVs. + if ( mh->flags & MH_HAS_TLV_DESCRIPTORS ) + tlv_initialize_descriptors(mh); } @@ -360,7 +289,6 @@ void dyld_enumerate_tlv_storage(dyld_tlv_state_change_handler handler) // destructor key to come before the deallocation key. // -typedef void (*TermFunc)(void*); struct TLVTerminatorListEntry { TermFunc termFunc; @@ -391,10 +319,10 @@ void _tlv_atexit(TermFunc func, void* objAddr) pthread_setspecific(tlv_terminators_key, list); } else { - if ( list->allocCount == list->allocCount ) { + if ( list->useCount == list->allocCount ) { // handle resizing allocation uint32_t newAllocCount = list->allocCount * 2; - uint32_t newAllocSize = offsetof(struct TLVTerminatorList, entries[newAllocCount]); + size_t newAllocSize = offsetof(struct TLVTerminatorList, entries[newAllocCount]); struct TLVTerminatorList* newlist = (struct TLVTerminatorList*)malloc(newAllocSize); newlist->allocCount = newAllocCount; newlist->useCount = list->useCount; @@ -411,18 +339,52 @@ void _tlv_atexit(TermFunc func, void* objAddr) } } -// called by pthreads when the current thread is going way and -// _tlv_atexit() has been called on the thread. -static void tlv_finalize(void* storage) -{ - struct TLVTerminatorList* list = (struct TLVTerminatorList*)storage; - for(uint32_t i=0; i < list->useCount; ++i) { - struct TLVTerminatorListEntry* entry = &list->entries[i]; +static void tlv_finalize_list(struct TLVTerminatorList* list) { + // destroy in reverse order of construction + for(uint32_t i=list->useCount; i > 0 ; --i) { + struct TLVTerminatorListEntry* entry = &list->entries[i-1]; if ( entry->termFunc != NULL ) { (*entry->termFunc)(entry->objAddr); } + + // If a new tlv was added via tlv_atexit, then we need to immediately + // destroy it + struct TLVTerminatorList* newlist = (struct TLVTerminatorList*)pthread_getspecific(tlv_terminators_key); + if ( newlist != NULL ) { + // Set the list to NULL so that if yet another tlv is registered, we put it in a new list + pthread_setspecific(tlv_terminators_key, NULL); + tlv_finalize_list(newlist); + } + } + free(list); +} + +// called by pthreads when the current thread is going away and +// _tlv_atexit() has been called on the thread. +static void tlv_finalize(void* storage) +{ + // Note, on entry to this function, _tlv_exit set the list to NULL. libpthread + // also set it to NULL before calling us. If new thread locals are added to our + // tlv_terminators_key, then they will be on a new list, but the list we have here + // is one we own and need to destroy it + tlv_finalize_list((struct TLVTerminatorList*)storage); +} + +// +// called by exit() before it calls cxa_finalize() so that thread_local +// objects are destroyed before global objects. +// Note this is only called on macOS, and by libc. +// iOS only destroys tlv's when each thread is destroyed and libpthread calls +// tlv_finalize as that is the pointer we provided when we created the key +void _tlv_exit() +{ + void* termFuncs = pthread_getspecific(tlv_terminators_key); + if ( termFuncs != NULL ) { + // Clear the value so that calls to tlv_atexit during tlv_finalize + // will go on to a new list to destroy. + pthread_setspecific(tlv_terminators_key, NULL); + tlv_finalize(termFuncs); } - free(storage); } @@ -435,7 +397,8 @@ void tlv_initializer() (void)pthread_key_create(&tlv_terminators_key, &tlv_finalize); // register with dyld for notification when images are loaded - dyld_register_image_state_change_handler(dyld_image_state_bound, true, tlv_load_notification); + _dyld_register_func_for_add_image(tlv_load_notification); + } @@ -446,16 +409,14 @@ void _tlv_bootstrap() } -// __i386__ || __x86_64__ -#else -// !(__i386__ || __x86_64__) +#else -void dyld_register_tlv_state_change_handler(enum dyld_tlv_states state, dyld_tlv_state_change_handler handler) +void _tlv_exit() { } -void dyld_enumerate_tlv_storage(dyld_tlv_state_change_handler handler) +void _tlv_atexit(TermFunc func, void* objAddr) { } @@ -465,7 +426,7 @@ void tlv_initializer() } -// !(__i386__ || __x86_64__) -#endif + +#endif // __has_feature(tls)