]> git.saurik.com Git - apple/dyld.git/blobdiff - src/threadLocalVariables.c
dyld-851.27.tar.gz
[apple/dyld.git] / src / threadLocalVariables.c
index 2586455ed2f6462b725b73b1a7b5a3011af114f8..bb3c5ff10a83b6eef68f593e0e24a5c54530ea63 100644 (file)
@@ -34,7 +34,7 @@
 #include <mach-o/loader.h>
 #include <libkern/OSAtomic.h>
 
-#include "dyld_priv.h"
+#include <mach-o/dyld_priv.h>
 
 
 #if __LP64__
        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);
+}
+
+// <rdar://problem/13741816>
+// 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)