Loading...
--- dyld/dyld-733.6/src/threadLocalVariables.c
+++ dyld/dyld-635.2/src/threadLocalVariables.c
@@ -34,7 +34,7 @@
#include <mach-o/loader.h>
#include <libkern/OSAtomic.h>
-#include <mach-o/dyld_priv.h>
+#include "dyld_priv.h"
#if __LP64__
@@ -49,6 +49,30 @@
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
+
+#ifndef MH_HAS_TLV_DESCRIPTORS
+ #define MH_HAS_TLV_DESCRIPTORS 0x800000
+#endif
+
typedef void (*TermFunc)(void*);
@@ -56,14 +80,14 @@
#if __has_feature(tls) || __arm64__ || __arm__
-//
-// 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;
+typedef struct TLVHandler {
+ struct TLVHandler *next;
+ dyld_tlv_state_change_handler handler;
+ enum dyld_tlv_states state;
+} TLVHandler;
+
+// lock-free prepend-only linked list
+static TLVHandler * volatile tlv_handlers = NULL;
struct TLVDescriptor
@@ -121,6 +145,23 @@
}
pthread_mutex_unlock(&tlv_live_image_lock);
return result;
+}
+
+
+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);
+ }
+ }
}
@@ -183,6 +224,9 @@
// 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 ) {
@@ -215,6 +259,7 @@
static void
tlv_free(void *storage)
{
+ tlv_notify(dyld_tlv_state_deallocated, storage);
free(storage);
}
@@ -270,6 +315,39 @@
// 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);
+}
+
+
+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)
+{
+ 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);
+ }
+ }
}
@@ -339,52 +417,29 @@
}
}
-static void tlv_finalize_list(struct TLVTerminatorList* 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)
+{
+ struct TLVTerminatorList* list = (struct TLVTerminatorList*)storage;
// 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);
+ free(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);
- }
+ void* termFuncs = pthread_getspecific(tlv_terminators_key);
+ if ( termFuncs != NULL )
+ tlv_finalize(termFuncs);
}
@@ -412,6 +467,16 @@
#else
+
+
+void dyld_register_tlv_state_change_handler(enum dyld_tlv_states state, dyld_tlv_state_change_handler handler)
+{
+}
+
+void dyld_enumerate_tlv_storage(dyld_tlv_state_change_handler handler)
+{
+}
+
void _tlv_exit()
{
}