]> git.saurik.com Git - apple/dyld.git/blobdiff - src/dyld_process_info_notify.cpp
dyld-635.2.tar.gz
[apple/dyld.git] / src / dyld_process_info_notify.cpp
index ec9bce4d65dd8ae0550dbb5cc01b3f0ffaa912d1..571e67790010961a0f75c17e79b9527a28b4786d 100644 (file)
 #include <mach/shared_region.h>
 #include <mach/mach_vm.h>
 #include <libkern/OSAtomic.h>
+#include <execinfo.h>
+
 
 #include "dyld_process_info.h"
 #include "dyld_process_info_internal.h"
 #include "dyld_images.h"
 #include "dyld_priv.h"
 
-#include "LaunchCache.h"
 #include "Loading.h"
 #include "AllImages.h"
 
+extern "C" int _dyld_func_lookup(const char* name, void** address);
 
 typedef void (^Notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path);
 typedef void (^NotifyExit)();
 typedef void (^NotifyMain)();
 
-
 //
 // Object used for monitoring another processes dyld loads
 //
 struct __attribute__((visibility("hidden"))) dyld_process_info_notify_base
 {
-    static dyld_process_info_notify_base* make(task_t task, dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, kern_return_t* kr);
-                                                                               ~dyld_process_info_notify_base();
-
-    bool                incRetainCount() const;
-    bool                decRetainCount() const;
+                        dyld_process_info_notify_base(dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, task_t task, kern_return_t* kr);
+                        ~dyld_process_info_notify_base();
+    bool                enabled() const;
+    void                retain();
+    void                release();
 
        void                            setNotifyMain(NotifyMain notifyMain) const { _notifyMain = notifyMain; }
 
     // override new and delete so we don't need to link with libc++
     static void*        operator new(size_t sz) { return malloc(sz); }
-    static void         operator delete(void* p) { return free(p); }
+    static void         operator delete(void* p) { free(p); }
 
 private:
-                        dyld_process_info_notify_base(dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, task_t task);
-    kern_return_t       makePorts();
-    kern_return_t       pokeSendPortIntoTarget();
-       kern_return_t           unpokeSendPortInTarget();
-    void                               setMachSourceOnQueue();
-
-       mutable int32_t         _retainCount;
-    dispatch_queue_t    _queue;
-    Notify              _notify;
-    NotifyExit          _notifyExit;
-       mutable NotifyMain      _notifyMain;
-       task_t                          _targetTask;
-       dispatch_source_t       _machSource;
-    uint64_t            _portAddressInTarget;
-    mach_port_t         _sendPortInTarget;          // target is process being watched for image loading/unloading
-    mach_port_t         _receivePortInMonitor;      // monitor is process being notified of image loading/unloading
+    void                handleEvent();
+    void                teardown();
+    void                replyToMonitoredProcess(mach_msg_header_t& header);
+
+    RemoteBuffer                    _remoteAllImageInfoBuffer;
+    uint32_t*                       _notifyMachPorts;
+    uint32_t                        _notifySlot;
+    mutable std::atomic<int32_t>    _retainCount;
+    dispatch_queue_t                _queue;
+    Notify                          _notify;
+    NotifyExit                      _notifyExit;
+       mutable NotifyMain              _notifyMain;
+       task_t                          _targetTask;
+       dispatch_source_t               _machSource;
+    mach_port_t                     _sendPortInTarget;          // target is process being watched for image loading/unloading
+    mach_port_t                     _receivePortInMonitor;      // monitor is process being notified of image loading/unloading
+    std::atomic<bool>               _disabled;
 };
 
 
-dyld_process_info_notify_base::dyld_process_info_notify_base(dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, task_t task)
-    : _retainCount(1), _queue(queue), _notify(notify), _notifyExit(notifyExit), _notifyMain(NULL), _targetTask(task), _machSource(NULL), _portAddressInTarget(0), _sendPortInTarget(0), _receivePortInMonitor(0)
+dyld_process_info_notify_base::dyld_process_info_notify_base(dispatch_queue_t queue, Notify notify, NotifyExit notifyExit,
+                                                             task_t task, kern_return_t* kr) :
+        _notifyMachPorts(nullptr), _notifySlot(0), _retainCount(1), _queue(queue), _notify(notify), _notifyExit(notifyExit),
+        _notifyMain(nullptr), _targetTask(task), _machSource(nullptr), _sendPortInTarget(0), _receivePortInMonitor(0),
+        _disabled(false)
 {
+    assert(_disabled == false);
     dispatch_retain(_queue);
-}
-
-dyld_process_info_notify_base::~dyld_process_info_notify_base()
-{
-       if ( _machSource ) {
-        dispatch_source_cancel(_machSource);
-               dispatch_release(_machSource);
-               _machSource = NULL;
-       }
-       if ( _portAddressInTarget ) {
-               unpokeSendPortInTarget();
-               _portAddressInTarget = 0;
-       }
-       if ( _sendPortInTarget ) {
-               _sendPortInTarget = 0;
-       }
-    dispatch_release(_queue);
-       if ( _receivePortInMonitor != 0 ) {
-               mach_port_deallocate(mach_task_self(), _receivePortInMonitor);
-               _receivePortInMonitor = 0;
-       }
-}
-
-bool dyld_process_info_notify_base::incRetainCount() const
-{
-    int32_t newCount = OSAtomicIncrement32(&_retainCount);
-    return ( newCount == 1 );
-}
-
-bool dyld_process_info_notify_base::decRetainCount() const
-{
-    int32_t newCount = OSAtomicDecrement32(&_retainCount);
-    return ( newCount == 0 );
-}
+    // Allocate a port to listen on in this monitoring task
+    mach_port_options_t options = { .flags = MPO_IMPORTANCE_RECEIVER | MPO_CONTEXT_AS_GUARD | MPO_STRICT,
+        .mpl = { MACH_PORT_QLIMIT_DEFAULT }};
+    if ((*kr = mach_port_construct(mach_task_self(), &options, (mach_port_context_t)this, &_receivePortInMonitor))) {
+        teardown();
+        return;
+    }
+    if (_targetTask == mach_task_self()) {
+        _sendPortInTarget = _receivePortInMonitor;
+        (void)mach_port_insert_right(_targetTask, _sendPortInTarget, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND);
+    } else {
+        // Insert a deadname right into the port to trigger notifications
+        kern_return_t r = KERN_NAME_EXISTS;
+        while (r == KERN_NAME_EXISTS) {
+            _sendPortInTarget = MACH_PORT_NULL;
+            //FIXME file radar
+            r = mach_port_allocate(_targetTask, MACH_PORT_RIGHT_DEAD_NAME, &_sendPortInTarget);
+            if (r != KERN_SUCCESS) {
+                *kr = r;
+                return;
+            }
+            (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+            r = mach_port_insert_right(_targetTask, _sendPortInTarget, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND);
+        }
+        if (r != KERN_SUCCESS) {
+            *kr = r;
+            return;
+        }
 
+        // Notify us if the target dies
+        mach_port_t previous = MACH_PORT_NULL;
+        if ((*kr = mach_port_request_notification(_targetTask, _sendPortInTarget, MACH_NOTIFY_DEAD_NAME, 0, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous))) {
+            (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+            (void)mach_port_destruct(mach_task_self(), _receivePortInMonitor, 0, (mach_port_context_t)this);
+            teardown();
+            return;
+        }
+        // This is a new port, if there is a previous notifier attached then something is wrong... abort.
+        if (previous != MACH_PORT_NULL) {
+            (void)mach_port_deallocate(mach_task_self(), previous);
+            (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+            (void)mach_port_destruct(mach_task_self(), _receivePortInMonitor, 0, (mach_port_context_t)this);
+            teardown();
+            return;
+        }
+    }
 
-dyld_process_info_notify_base* dyld_process_info_notify_base::make(task_t task, dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, kern_return_t* kr)
-{
-    dyld_process_info_notify_base* obj = new dyld_process_info_notify_base(queue, notify, notifyExit, task);
+    // Setup the event handler for the port
+    _machSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, _receivePortInMonitor, 0, _queue);
+    if (_machSource == nullptr) {
+        (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+        (void)mach_port_destruct(mach_task_self(), _receivePortInMonitor, 0, (mach_port_context_t)this);
+        teardown();
+        return;
+    }
+    dispatch_source_set_event_handler(_machSource, ^{
+        handleEvent();
+    });
+    dispatch_source_set_cancel_handler(_machSource, ^{
+        if ( _receivePortInMonitor != 0 ) {
+            (void)mach_port_destruct(mach_task_self(), _receivePortInMonitor, 0, (mach_port_context_t)this);
+            _receivePortInMonitor = 0;
+        }
+    });
+    dispatch_activate(_machSource);
 
-    if ( kern_return_t r = obj->makePorts() ) {
-               if ( kr != NULL )
-                       *kr = r;
-        goto fail;
-       }
+    // get location on all_image_infos in the target task
+    task_dyld_info_data_t taskDyldInfo;
+    mach_msg_type_number_t taskDyldInfoCount = TASK_DYLD_INFO_COUNT;
+    if ((*kr = task_info(_targetTask, TASK_DYLD_INFO, (task_info_t)&taskDyldInfo, &taskDyldInfoCount))) {
+        (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+        teardown();
+        return;
+    }
+    // Poke the portname of our port into the target task
+    _remoteAllImageInfoBuffer = RemoteBuffer(_targetTask, taskDyldInfo.all_image_info_addr, taskDyldInfo.all_image_info_size, true, false);
+    *kr = _remoteAllImageInfoBuffer.getKernelReturn();
+    if (*kr) {
+        (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+        teardown();
+        return;
+    }
 
-    obj->setMachSourceOnQueue();
+    static_assert(sizeof(mach_port_t) == sizeof(uint32_t), "machport size not 32-bits");
+    if ( taskDyldInfo.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) {
+        _notifyMachPorts = (uint32_t *)((uint8_t *)_remoteAllImageInfoBuffer.getLocalAddress() + offsetof(dyld_all_image_infos_32,notifyMachPorts));
+    } else {
+        _notifyMachPorts = (uint32_t *)((uint8_t *)_remoteAllImageInfoBuffer.getLocalAddress() + offsetof(dyld_all_image_infos_64,notifyMachPorts));
+    }
 
-    if ( kern_return_t r = obj->pokeSendPortIntoTarget() ) {
-               if ( kr != NULL )
-                       *kr = r;
-        goto fail;
-       }
+#if 0
+    //If all the slots are filled we will sleep and retry a few times before giving up
+    for (uint32_t i=0; i<10; ++i) {
+        for (_notifySlot=0; _notifySlot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++_notifySlot) {
+            if (OSAtomicCompareAndSwap32(0, _sendPortInTarget, (volatile int32_t*)&_notifyMachPorts[_notifySlot])) {
+                break;
+            }
+        }
+        if (_notifySlot == DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT) {
+            // all the slots are filled, sleep and try again
+            usleep(1000 * 50); // 50ms
+        } else {
+            // if _notifySlot is set we are done
+            break;
+        }
+    }
+#else
+    for (_notifySlot=0; _notifySlot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++_notifySlot) {
+        if (OSAtomicCompareAndSwap32(0, _sendPortInTarget, (volatile int32_t*)&_notifyMachPorts[_notifySlot])) {
+            break;
+        }
+    }
+#endif
 
-       if ( kr != NULL )
-               *kr = KERN_SUCCESS;
-    return obj;
+    if (_notifySlot == DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT) {
+        (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+        teardown();
+        *kr = KERN_UREFS_OVERFLOW;
+        return;
+    }
 
-fail:
-    delete obj;
-    return NULL;
+    *kr = KERN_SUCCESS;
 }
 
-
-kern_return_t dyld_process_info_notify_base::makePorts()
-{
-    // Allocate a port to listen on in this monitoring task
-    if ( kern_return_t r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &_receivePortInMonitor) )
-        return r;
-
-    // Add send rights for replying
-    if ( kern_return_t r = mach_port_insert_right(mach_task_self(), _receivePortInMonitor, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND) )
-        return r;
-
-    // Allocate a name in the target. We need a new name to add send rights to
-    if ( kern_return_t r = mach_port_allocate(_targetTask, MACH_PORT_RIGHT_DEAD_NAME, &_sendPortInTarget) )
-        return r;
-
-    // Deallocate the dead name
-    if ( kern_return_t r = mach_port_mod_refs(_targetTask, _sendPortInTarget, MACH_PORT_RIGHT_DEAD_NAME, -1) )
-        return r;
-
-    // Make the dead name a send right to our listening port
-    if ( kern_return_t r = mach_port_insert_right(_targetTask, _sendPortInTarget, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND) )
-        return r;
-
-    // Notify us if the target dies
-    mach_port_t previous = MACH_PORT_NULL;
-    if ( kern_return_t r = mach_port_request_notification(_targetTask, _sendPortInTarget, MACH_NOTIFY_DEAD_NAME, 0, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous))
-        return r;
-
-    //fprintf(stderr, "_sendPortInTarget=%d, _receivePortInMonitor=%d\n", _sendPortInTarget, _receivePortInMonitor);
-    return KERN_SUCCESS;
+dyld_process_info_notify_base::~dyld_process_info_notify_base() {
+    if (!_disabled) {
+        fprintf(stderr, "dyld: ~dyld_process_info_notify_base called while still enabled\n");
+    }
+    dispatch_release(_queue);
 }
 
-
-
-void dyld_process_info_notify_base::setMachSourceOnQueue()
-{
-       NotifyExit exitHandler = _notifyExit;
-       _machSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, _receivePortInMonitor, 0, _queue);
-    dispatch_source_set_event_handler(_machSource, ^{
-        // This event handler block has an implicit reference to "this"
-        // if incrementing the count goes to one, that means the object may have already been destroyed
-        if ( incRetainCount() )
-            return;
-        uint8_t messageBuffer[DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE];
-        mach_msg_header_t* h = (mach_msg_header_t*)messageBuffer;
-
-        kern_return_t r = mach_msg(h, MACH_RCV_MSG, 0, sizeof(messageBuffer), _receivePortInMonitor, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
-        if ( r == KERN_SUCCESS ) {
-            //fprintf(stderr, "received message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size);
-                       if ( h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_LOAD_ID || h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID ) {
-                               // run notifier block for each [un]load image
-                               const dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)messageBuffer;
-                               const dyld_process_info_image_entry* entries = (dyld_process_info_image_entry*)&messageBuffer[header->imagesOffset];
-                               const char* const stringPool = (char*)&messageBuffer[header->stringsOffset];
-                               for (unsigned i=0; i < header->imageCount; ++i) {
-                                       bool isUnload = (h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID);
-                                       _notify(isUnload, header->timestamp, entries[i].loadAddress, entries[i].uuid, stringPool + entries[i].pathStringOffset);
-                               }
-                               // reply to dyld, so it can continue
-                               mach_msg_header_t replyHeader;
-                               replyHeader.msgh_bits        = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND);
-                               replyHeader.msgh_id          = 0;
-                               replyHeader.msgh_local_port  = MACH_PORT_NULL;
-                               replyHeader.msgh_remote_port = h->msgh_remote_port;
-                               replyHeader.msgh_reserved    = 0;
-                               replyHeader.msgh_size        = sizeof(replyHeader);
-                               mach_msg(&replyHeader, MACH_SEND_MSG | MACH_SEND_TIMEOUT, replyHeader.msgh_size, 0, MACH_PORT_NULL, 100, MACH_PORT_NULL);
-                       }
-                       else if ( h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_MAIN_ID ) {
-                               if ( _notifyMain != NULL )  {
-                                       _notifyMain();
-                               }
-                               // reply to dyld, so it can continue
-                               mach_msg_header_t replyHeader;
-                               replyHeader.msgh_bits        = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND);
-                               replyHeader.msgh_id          = 0;
-                               replyHeader.msgh_local_port  = MACH_PORT_NULL;
-                               replyHeader.msgh_remote_port = h->msgh_remote_port;
-                               replyHeader.msgh_reserved    = 0;
-                               replyHeader.msgh_size        = sizeof(replyHeader);
-                               mach_msg(&replyHeader, MACH_SEND_MSG | MACH_SEND_TIMEOUT, replyHeader.msgh_size, 0, MACH_PORT_NULL, 100, MACH_PORT_NULL);
-                       }
-                       else if ( h->msgh_id == MACH_NOTIFY_PORT_DELETED ) {
-                               mach_port_t deadPort = ((mach_port_deleted_notification_t *)h)->not_port;
-                               //fprintf(stderr, "received message id=MACH_NOTIFY_PORT_DELETED, size=%d, deadPort=%d\n", h->msgh_size, deadPort);
-                               if ( deadPort == _sendPortInTarget ) {
-                                       // target process died.  Clean up ports
-                                       _sendPortInTarget = 0;
-                                       mach_port_deallocate(mach_task_self(), _receivePortInMonitor);
-                                       _receivePortInMonitor = 0;
-                                       _portAddressInTarget = 0;
-                                       // notify that target is gone
-                                       exitHandler();
-                               }
-                       }
-                       else {
-                               fprintf(stderr, "received unknown message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size);
-                       }
+void dyld_process_info_notify_base::teardown() {
+    if (!_disabled) {
+        _disabled = true;
+        // The connection to the target is dead.  Clean up ports
+        if ( _remoteAllImageInfoBuffer.getLocalAddress() != 0 && _notifySlot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT) {
+            mach_port_t extractedPort = MACH_PORT_NULL;
+            mach_msg_type_name_t extractedPortType;
+            kern_return_t kr = mach_port_extract_right(_targetTask, _sendPortInTarget, MACH_MSG_TYPE_COPY_SEND, &extractedPort, &extractedPortType);
+            if (kr == KERN_SUCCESS) {
+                if (extractedPort == _receivePortInMonitor) {
+                    if (OSAtomicCompareAndSwap32(_sendPortInTarget, 0, (volatile int32_t*)&_notifyMachPorts[_notifySlot])) {
+                        (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+                    }
+                }
+                (void)mach_port_deallocate(mach_task_self(), extractedPort);
+            }
         }
-        if ( decRetainCount() )
-            delete this;
-   });
-    dispatch_resume(_machSource);
+        _sendPortInTarget = 0;
+        if ( _machSource ) {
+            dispatch_source_cancel(_machSource);
+            dispatch_release(_machSource);
+            _machSource = NULL;
+        }
+        if (_notifyExit) {
+            dispatch_async(_queue, ^{
+                _notifyExit();
+            });
+        }
+    }
 }
 
-
-kern_return_t dyld_process_info_notify_base::pokeSendPortIntoTarget()
+bool dyld_process_info_notify_base::enabled() const
 {
-    // get location on all_image_infos in target task
-    task_dyld_info_data_t taskDyldInfo;
-    mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
-    kern_return_t r = task_info(_targetTask, TASK_DYLD_INFO, (task_info_t)&taskDyldInfo, &count);
-    if ( r )
-        return  r;
-
-    // remap the page containing all_image_infos into this process r/w
-    mach_vm_address_t mappedAddress = 0;
-    mach_vm_size_t    mappedSize = taskDyldInfo.all_image_info_size;
-    vm_prot_t curProt = VM_PROT_NONE;
-    vm_prot_t maxProt = VM_PROT_NONE;
-    r = mach_vm_remap(mach_task_self(), &mappedAddress, mappedSize, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR,
-                                       _targetTask, taskDyldInfo.all_image_info_addr, false, &curProt, &maxProt, VM_INHERIT_NONE);
-    if ( r )
-        return r;
-    if ( curProt != (VM_PROT_READ|VM_PROT_WRITE) )
-        return KERN_PROTECTION_FAILURE;
-
-    // atomically set port into all_image_info_struct
-    static_assert(sizeof(mach_port_t) == sizeof(uint32_t), "machport size not 32-bits");
-
-    mach_vm_address_t mappedAddressToPokePort = 0;
-    if ( taskDyldInfo.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 )
-        mappedAddressToPokePort = mappedAddress + offsetof(dyld_all_image_infos_32,notifyMachPorts);
-    else
-        mappedAddressToPokePort = mappedAddress + offsetof(dyld_all_image_infos_64,notifyMachPorts);
-
-    // use first available slot
-       bool slotFound = false;
-       for (int slotIndex=0; slotIndex < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slotIndex) {
-               if ( OSAtomicCompareAndSwap32Barrier(0, _sendPortInTarget, (volatile int32_t*)mappedAddressToPokePort) ) {
-                       slotFound = true;
-                       break;
-               }
-               mappedAddressToPokePort += sizeof(uint32_t);
-     }
-       if ( !slotFound ) {
-               mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize);
-               return KERN_UREFS_OVERFLOW;
-       }
-    _portAddressInTarget = taskDyldInfo.all_image_info_addr + mappedAddressToPokePort - mappedAddress;
-    //fprintf(stderr, "poked port %d into target at address 0x%llX\n", _sendPortInTarget, _portAddressInTarget);
-    mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize);
-    return r;
+    return !_disabled;
 }
 
+void dyld_process_info_notify_base::retain()
+{
+    _retainCount++;
+}
 
-
-kern_return_t dyld_process_info_notify_base::unpokeSendPortInTarget()
+void dyld_process_info_notify_base::release()
 {
-    // remap the page containing all_image_infos into this process r/w
-    mach_vm_address_t mappedAddress = 0;
-    mach_vm_size_t    mappedSize = sizeof(mach_port_t);
-    vm_prot_t curProt = VM_PROT_NONE;
-    vm_prot_t maxProt = VM_PROT_NONE;
-    kern_return_t r = mach_vm_remap(mach_task_self(), &mappedAddress, mappedSize, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR,
-                                                                       _targetTask, _portAddressInTarget, false, &curProt, &maxProt, VM_INHERIT_NONE);
-    if ( r )
-        return r;
-    if ( curProt != (VM_PROT_READ|VM_PROT_WRITE) )
-        return KERN_PROTECTION_FAILURE;
-
-    OSAtomicCompareAndSwap32Barrier(_sendPortInTarget, 0, (volatile int32_t*)mappedAddress);
-
-    //fprintf(stderr, "cleared port %d from target\n", _sendPortInTarget);
-    mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize);
-    return r;
+    uint32_t newCount = --_retainCount;
+    
+    if ( newCount == 0 ) {
+        teardown();
+    }
+    dispatch_async(_queue, ^{
+        delete this;
+    });
 }
 
+void dyld_process_info_notify_base::replyToMonitoredProcess(mach_msg_header_t& header) {
+    mach_msg_header_t replyHeader;
+    replyHeader.msgh_bits        = MACH_MSGH_BITS_SET(MACH_MSGH_BITS_REMOTE(header.msgh_bits), 0, 0, 0);
+    replyHeader.msgh_id          = 0;
+    replyHeader.msgh_local_port  = MACH_PORT_NULL;
+    replyHeader.msgh_remote_port  = header.msgh_remote_port;
+    replyHeader.msgh_reserved    = 0;
+    replyHeader.msgh_size        = sizeof(replyHeader);
+    kern_return_t r = mach_msg(&replyHeader, MACH_SEND_MSG, replyHeader.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
+    if (r == KERN_SUCCESS) {
+        header.msgh_remote_port = MACH_PORT_NULL;
+    } else {
+        teardown();
+    }
+}
 
+void dyld_process_info_notify_base::handleEvent() {
+    // References object may still exist even after the ports are dead. Disable event dispatching
+    // if the ports have been torn down.
+    if (_disabled) {
+        return;
+    }
+    // This event handler block has an implicit reference to "this"
+    // if incrementing the count goes to one, that means the object may have already been destroyed
+    uint8_t messageBuffer[DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE] = {};
+    mach_msg_header_t* h = (mach_msg_header_t*)messageBuffer;
+
+    kern_return_t r = mach_msg(h, MACH_RCV_MSG | MACH_RCV_VOUCHER| MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0), 0, sizeof(messageBuffer)-sizeof(mach_msg_audit_trailer_t), _receivePortInMonitor, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+    if ( r == KERN_SUCCESS && !(h->msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
+        //fprintf(stderr, "received message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size);
+
+        if ( h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_LOAD_ID || h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID ) {
+            // run notifier block for each [un]load image
+            const dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)messageBuffer;
+            if (sizeof(*header) <= h->msgh_size
+                && header->imagesOffset <= h->msgh_size
+                && header->stringsOffset <= h->msgh_size
+                && (header->imageCount * sizeof(dyld_process_info_image_entry)) <= (h->msgh_size - header->imagesOffset)) {
+                const dyld_process_info_image_entry* entries = (dyld_process_info_image_entry*)&messageBuffer[header->imagesOffset];
+                const char* const stringPool = (char*)&messageBuffer[header->stringsOffset];
+                for (unsigned i=0; i < header->imageCount; ++i) {
+                    bool isUnload = (h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID);
+                    if (entries[i].pathStringOffset <= h->msgh_size - header->stringsOffset) {
+                        //fprintf(stderr, "Notifying about: %s\n", stringPool + entries[i].pathStringOffset);
+                        _notify(isUnload, header->timestamp, entries[i].loadAddress, entries[i].uuid, stringPool + entries[i].pathStringOffset);
+                    } else {
+                        teardown();
+                        break;
+                    }
+                }
+                // reply to dyld, so it can continue
+                replyToMonitoredProcess(*h);
+            } else {
+                teardown();
+            }
+        }
+        else if ( h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_MAIN_ID ) {
+            if (h->msgh_size != sizeof(mach_msg_header_t)) {
+                teardown();
+            } else if ( _notifyMain != NULL )  {
+                _notifyMain();
+            }
+            replyToMonitoredProcess(*h);
+        } else if ( h->msgh_id == MACH_NOTIFY_PORT_DELETED ) {
+            mach_port_t deadPort = ((mach_port_deleted_notification_t *)h)->not_port;
+            // Validate this notification came from the kernel
+            const mach_msg_audit_trailer_t *audit_tlr = (mach_msg_audit_trailer_t *)((uint8_t *)h + round_msg(h->msgh_size));
+            if (audit_tlr->msgh_trailer_type == MACH_MSG_TRAILER_FORMAT_0
+                && audit_tlr->msgh_trailer_size >= sizeof(mach_msg_audit_trailer_t)
+                // We cannot link to libbsm, so we are hardcoding the audit token offset (5)
+                // And the value the represents the kernel (0)
+                && audit_tlr->msgh_audit.val[5] == 0
+                && deadPort == _sendPortInTarget ) {
+                teardown();
+            }
+        }
+        else {
+            fprintf(stderr, "dyld: received unknown message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size);
+        }
+    }
+    mach_msg_destroy(h);
+}
 
 dyld_process_info_notify _dyld_process_info_notify(task_t task, dispatch_queue_t queue,
                                                    void (^notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path),
                                                    void (^notifyExit)(),
                                                    kern_return_t* kr)
 {
-    return dyld_process_info_notify_base::make(task, queue, notify, notifyExit, kr);
+    kern_return_t krSink = KERN_SUCCESS;
+    if (kr == nullptr) {
+        kr = &krSink;
+    }
+    *kr = KERN_SUCCESS;
+    
+    dyld_process_info_notify result = new dyld_process_info_notify_base(queue, notify, notifyExit, task, kr);
+    if (result->enabled())
+        return result;
+    const_cast<dyld_process_info_notify_base*>(result)->release();
+    return nullptr;
 }
 
 void _dyld_process_info_notify_main(dyld_process_info_notify object, void (^notifyMain)())
@@ -344,181 +384,60 @@ void _dyld_process_info_notify_main(dyld_process_info_notify object, void (^noti
 
 void _dyld_process_info_notify_retain(dyld_process_info_notify object)
 {
-    object->incRetainCount();
+    const_cast<dyld_process_info_notify_base*>(object)->retain();
 }
 
 void _dyld_process_info_notify_release(dyld_process_info_notify object)
 {
-    // Note if _machSource is currently handling a message, the retain count will not be zero
-    // and object will instead be deleted when handling is done.
-    if ( object->decRetainCount() )
-        delete object;
+    const_cast<dyld_process_info_notify_base*>(object)->release();
 }
 
+static void (*sNotifyMonitoringDyldMain)() = nullptr;
+static void (*sNotifyMonitoringDyld)(bool unloading, unsigned imageCount, const struct mach_header* loadAddresses[],
+                                     const char* imagePaths[]) = nullptr;
 
-
-
-
-
-
-namespace dyld3 {
-
-
-static mach_port_t sNotifyReplyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
-static bool        sZombieNotifiers[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
-
-static void notifyMonitoringDyld(bool unloading, unsigned portSlot, const launch_cache::DynArray<loader::ImageInfo>& imageInfos)
+void setNotifyMonitoringDyldMain(void (*func)())
 {
-    if ( sZombieNotifiers[portSlot] )
-        return;
+    sNotifyMonitoringDyldMain = func;
+}
 
-    unsigned entriesSize = (unsigned)imageInfos.count()*sizeof(dyld_process_info_image_entry);
-    unsigned pathsSize = 0;
-    for (uintptr_t i=0; i < imageInfos.count(); ++i) {
-        launch_cache::Image image(imageInfos[i].imageData);
-        pathsSize += (strlen(image.path()) + 1);
-    }
-    unsigned totalSize = (sizeof(dyld_process_info_notify_header) + MAX_TRAILER_SIZE + entriesSize + pathsSize + 127) & -128;   // align
-    if ( totalSize > DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE ) {
-        // Putting all image paths into one message would make buffer too big.
-        // Instead split into two messages.  Recurse as needed until paths fit in buffer.
-        unsigned imageHalfCount = (unsigned)imageInfos.count()/2;
-        const launch_cache::DynArray<loader::ImageInfo> firstHalf(imageHalfCount, (loader::ImageInfo*)&imageInfos[0]);
-        const launch_cache::DynArray<loader::ImageInfo> secondHalf(imageInfos.count() - imageHalfCount, (loader::ImageInfo*)&imageInfos[imageHalfCount]);
-        notifyMonitoringDyld(unloading, portSlot, firstHalf);
-        notifyMonitoringDyld(unloading, portSlot, secondHalf);
-        return;
-    }
-    // build buffer to send
-    dyld_all_image_infos*  allImageInfo = gAllImages.oldAllImageInfo();
-    uint8_t    buffer[totalSize];
-    dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)buffer;
-    header->version          = 1;
-    header->imageCount       = (uint32_t)imageInfos.count();
-    header->imagesOffset     = sizeof(dyld_process_info_notify_header);
-    header->stringsOffset    = sizeof(dyld_process_info_notify_header) + entriesSize;
-    header->timestamp        = allImageInfo->infoArrayChangeTimestamp;
-    dyld_process_info_image_entry* entries = (dyld_process_info_image_entry*)&buffer[header->imagesOffset];
-    char* const pathPoolStart = (char*)&buffer[header->stringsOffset];
-    char* pathPool = pathPoolStart;
-    for (uintptr_t i=0; i < imageInfos.count(); ++i) {
-        launch_cache::Image image(imageInfos[i].imageData);
-        strcpy(pathPool, image.path());
-        uint32_t len = (uint32_t)strlen(pathPool);
-        memcpy(entries->uuid, image.uuid(), sizeof(uuid_t));
-        entries->loadAddress = (uint64_t)imageInfos[i].loadAddress;
-        entries->pathStringOffset = (uint32_t)(pathPool - pathPoolStart);
-        entries->pathLength  = len;
-        pathPool += (len +1);
-        ++entries;
-    }
-    // lazily alloc reply port
-    if ( sNotifyReplyPorts[portSlot] == 0 ) {
-        if ( !mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sNotifyReplyPorts[portSlot]) )
-            mach_port_insert_right(mach_task_self(), sNotifyReplyPorts[portSlot], sNotifyReplyPorts[portSlot], MACH_MSG_TYPE_MAKE_SEND);
-        //log("allocated reply port %d\n", sNotifyReplyPorts[portSlot]);
-    }
-    //log("found port to send to\n");
-    mach_msg_header_t* h = (mach_msg_header_t*)buffer;
-    h->msgh_bits         = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND); // MACH_MSG_TYPE_MAKE_SEND_ONCE
-    h->msgh_id           = unloading ? DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID : DYLD_PROCESS_INFO_NOTIFY_LOAD_ID;
-    h->msgh_local_port   = sNotifyReplyPorts[portSlot];
-    h->msgh_remote_port  = allImageInfo->notifyPorts[portSlot];
-    h->msgh_reserved     = 0;
-    h->msgh_size         = (mach_msg_size_t)sizeof(buffer);
-    //log("sending to port[%d]=%d, size=%d, reply port=%d, id=0x%X\n", portSlot, allImageInfo->notifyPorts[portSlot], h->msgh_size, sNotifyReplyPorts[portSlot], h->msgh_id);
-    kern_return_t sendResult = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT, h->msgh_size, h->msgh_size, sNotifyReplyPorts[portSlot], 2000, MACH_PORT_NULL);
-    //log("send result = 0x%X, msg_id=%d, msg_size=%d\n", sendResult, h->msgh_id, h->msgh_size);
-    if ( sendResult == MACH_SEND_INVALID_DEST ) {
-        // sender is not responding, detatch
-        //log("process requesting notification gone. deallocation send port %d and receive port %d\n", allImageInfo->notifyPorts[portSlot], sNotifyReplyPorts[portSlot]);
-        mach_port_deallocate(mach_task_self(), allImageInfo->notifyPorts[portSlot]);
-        mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[portSlot]);
-        allImageInfo->notifyPorts[portSlot] = 0;
-        sNotifyReplyPorts[portSlot] = 0;
-    }
-    else if ( sendResult == MACH_RCV_TIMED_OUT ) {
-        // client took too long, ignore him from now on
-        sZombieNotifiers[portSlot] = true;
-        mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[portSlot]);
-        sNotifyReplyPorts[portSlot] = 0;
-    }
+void setNotifyMonitoringDyld(void (*func)(bool unloading, unsigned imageCount,
+                                          const struct mach_header* loadAddresses[],
+                                          const char* imagePaths[]))
+{
+    sNotifyMonitoringDyld = func;
 }
 
+namespace dyld3 {
+
 void AllImages::notifyMonitorMain()
 {
-    dyld_all_image_infos* allImageInfo = gAllImages.oldAllImageInfo();
-    for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
-        if ( (allImageInfo->notifyPorts[slot] != 0 ) && !sZombieNotifiers[slot] ) {
-            if ( sNotifyReplyPorts[slot] == 0 ) {
-                if ( !mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sNotifyReplyPorts[slot]) )
-                    mach_port_insert_right(mach_task_self(), sNotifyReplyPorts[slot], sNotifyReplyPorts[slot], MACH_MSG_TYPE_MAKE_SEND);
-                //dyld::log("allocated reply port %d\n", sNotifyReplyPorts[slot]);
-            }
-            //dyld::log("found port to send to\n");
-            uint8_t messageBuffer[sizeof(mach_msg_header_t) + MAX_TRAILER_SIZE];
-            mach_msg_header_t* h = (mach_msg_header_t*)messageBuffer;
-            h->msgh_bits         = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND); // MACH_MSG_TYPE_MAKE_SEND_ONCE
-            h->msgh_id           = DYLD_PROCESS_INFO_NOTIFY_MAIN_ID;
-            h->msgh_local_port   = sNotifyReplyPorts[slot];
-            h->msgh_remote_port  = allImageInfo->notifyPorts[slot];
-            h->msgh_reserved     = 0;
-            h->msgh_size         = (mach_msg_size_t)sizeof(messageBuffer);
-            //dyld::log("sending to port[%d]=%d, size=%d, reply port=%d, id=0x%X\n", slot, allImageInfo->notifyPorts[slot], h->msgh_size, sNotifyReplyPorts[slot], h->msgh_id);
-            kern_return_t sendResult = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT, h->msgh_size, h->msgh_size, sNotifyReplyPorts[slot], 2000, MACH_PORT_NULL);
-            //dyld::log("send result = 0x%X, msg_id=%d, msg_size=%d\n", sendResult, h->msgh_id, h->msgh_size);
-            if ( sendResult == MACH_SEND_INVALID_DEST ) {
-                // sender is not responding, detatch
-                //dyld::log("process requesting notification gone. deallocation send port %d and receive port %d\n", allImageInfo->notifyPorts[slot], sNotifyReplyPorts[slot]);
-                mach_port_deallocate(mach_task_self(), allImageInfo->notifyPorts[slot]);
-                mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
-                allImageInfo->notifyPorts[slot] = 0;
-                sNotifyReplyPorts[slot] = 0;
-            }
-            else if ( sendResult == MACH_RCV_TIMED_OUT ) {
-                // client took too long, ignore him from now on
-                sZombieNotifiers[slot] = true;
-                mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
-                sNotifyReplyPorts[slot] = 0;
-            }
-        }
-    }
+    assert(sNotifyMonitoringDyldMain != nullptr);
+    sNotifyMonitoringDyldMain();
 }
 
-void AllImages::notifyMonitorLoads(const launch_cache::DynArray<loader::ImageInfo>& newImages)
+void AllImages::notifyMonitorLoads(const Array<LoadedImage>& newImages)
 {
-    // notify each monitoring process
-    dyld_all_image_infos* allImageInfo = gAllImages.oldAllImageInfo();
-    for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
-        if ( allImageInfo->notifyPorts[slot] != 0 ) {
-             notifyMonitoringDyld(false, slot, newImages);
-        }
-        else if ( sNotifyReplyPorts[slot] != 0 ) {
-            // monitoring process detached from this process, so release reply port
-            //dyld::log("deallocated reply port %d\n", sNotifyReplyPorts[slot]);
-            mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
-            sNotifyReplyPorts[slot] = 0;
-            sZombieNotifiers[slot] = false;
-        }
+    assert(sNotifyMonitoringDyld != nullptr);
+    const struct mach_header* loadAddresses[newImages.count()];
+    const char* loadPaths[newImages.count()];
+    for(uint32_t i = 0; i<newImages.count(); ++i) {
+        loadAddresses[i] = newImages[i].loadedAddress();
+        loadPaths[i] = newImages[i].image()->path();
     }
+    sNotifyMonitoringDyld(false, (unsigned)newImages.count(), loadAddresses, loadPaths);
 }
 
-void AllImages::notifyMonitorUnloads(const launch_cache::DynArray<loader::ImageInfo>& unloadingImages)
+void AllImages::notifyMonitorUnloads(const Array<LoadedImage>& unloadingImages)
 {
-    // notify each monitoring process
-    dyld_all_image_infos* allImageInfo = gAllImages.oldAllImageInfo();
-    for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
-        if ( allImageInfo->notifyPorts[slot] != 0 ) {
-             notifyMonitoringDyld(true, slot, unloadingImages);
-        }
-        else if ( sNotifyReplyPorts[slot] != 0 ) {
-            // monitoring process detached from this process, so release reply port
-            //dyld::log("deallocated reply port %d\n", sNotifyReplyPorts[slot]);
-            mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
-            sNotifyReplyPorts[slot] = 0;
-            sZombieNotifiers[slot] = false;
-        }
+    assert(sNotifyMonitoringDyld != nullptr);
+    const struct mach_header* loadAddresses[unloadingImages.count()];
+    const char* loadPaths[unloadingImages.count()];
+    for(uint32_t i = 0; i<unloadingImages.count(); ++i) {
+        loadAddresses[i] = unloadingImages[i].loadedAddress();
+        loadPaths[i] = unloadingImages[i].image()->path();
     }
+    sNotifyMonitoringDyld(true, (unsigned)unloadingImages.count(), loadAddresses, loadPaths);
 }
 
 } // namespace dyld3