dyld-551.3.tar.gz
[apple/dyld.git] / src / dyld_process_info_notify.cpp
1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2016 Apple Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
12 * file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <limits.h>
28 #include <stdio.h>
29 #include <mach/shared_region.h>
30 #include <mach/mach_vm.h>
31 #include <libkern/OSAtomic.h>
32
33 #include "dyld_process_info.h"
34 #include "dyld_process_info_internal.h"
35 #include "dyld_images.h"
36 #include "dyld_priv.h"
37
38 #include "LaunchCache.h"
39 #include "Loading.h"
40 #include "AllImages.h"
41
42
43 typedef void (^Notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path);
44 typedef void (^NotifyExit)();
45 typedef void (^NotifyMain)();
46
47
48 //
49 // Object used for monitoring another processes dyld loads
50 //
51 struct __attribute__((visibility("hidden"))) dyld_process_info_notify_base
52 {
53 static dyld_process_info_notify_base* make(task_t task, dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, kern_return_t* kr);
54 ~dyld_process_info_notify_base();
55
56 bool incRetainCount() const;
57 bool decRetainCount() const;
58
59 void setNotifyMain(NotifyMain notifyMain) const { _notifyMain = notifyMain; }
60
61 // override new and delete so we don't need to link with libc++
62 static void* operator new(size_t sz) { return malloc(sz); }
63 static void operator delete(void* p) { return free(p); }
64
65 private:
66 dyld_process_info_notify_base(dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, task_t task);
67 kern_return_t makePorts();
68 kern_return_t pokeSendPortIntoTarget();
69 kern_return_t unpokeSendPortInTarget();
70 void setMachSourceOnQueue();
71
72 mutable int32_t _retainCount;
73 dispatch_queue_t _queue;
74 Notify _notify;
75 NotifyExit _notifyExit;
76 mutable NotifyMain _notifyMain;
77 task_t _targetTask;
78 dispatch_source_t _machSource;
79 uint64_t _portAddressInTarget;
80 mach_port_t _sendPortInTarget; // target is process being watched for image loading/unloading
81 mach_port_t _receivePortInMonitor; // monitor is process being notified of image loading/unloading
82 };
83
84
85 dyld_process_info_notify_base::dyld_process_info_notify_base(dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, task_t task)
86 : _retainCount(1), _queue(queue), _notify(notify), _notifyExit(notifyExit), _notifyMain(NULL), _targetTask(task), _machSource(NULL), _portAddressInTarget(0), _sendPortInTarget(0), _receivePortInMonitor(0)
87 {
88 dispatch_retain(_queue);
89 }
90
91 dyld_process_info_notify_base::~dyld_process_info_notify_base()
92 {
93 if ( _machSource ) {
94 dispatch_source_cancel(_machSource);
95 dispatch_release(_machSource);
96 _machSource = NULL;
97 }
98 if ( _portAddressInTarget ) {
99 unpokeSendPortInTarget();
100 _portAddressInTarget = 0;
101 }
102 if ( _sendPortInTarget ) {
103 _sendPortInTarget = 0;
104 }
105 dispatch_release(_queue);
106 if ( _receivePortInMonitor != 0 ) {
107 mach_port_deallocate(mach_task_self(), _receivePortInMonitor);
108 _receivePortInMonitor = 0;
109 }
110 }
111
112 bool dyld_process_info_notify_base::incRetainCount() const
113 {
114 int32_t newCount = OSAtomicIncrement32(&_retainCount);
115 return ( newCount == 1 );
116 }
117
118 bool dyld_process_info_notify_base::decRetainCount() const
119 {
120 int32_t newCount = OSAtomicDecrement32(&_retainCount);
121 return ( newCount == 0 );
122 }
123
124
125 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)
126 {
127 dyld_process_info_notify_base* obj = new dyld_process_info_notify_base(queue, notify, notifyExit, task);
128
129 if ( kern_return_t r = obj->makePorts() ) {
130 if ( kr != NULL )
131 *kr = r;
132 goto fail;
133 }
134
135 obj->setMachSourceOnQueue();
136
137 if ( kern_return_t r = obj->pokeSendPortIntoTarget() ) {
138 if ( kr != NULL )
139 *kr = r;
140 goto fail;
141 }
142
143 if ( kr != NULL )
144 *kr = KERN_SUCCESS;
145 return obj;
146
147 fail:
148 delete obj;
149 return NULL;
150 }
151
152
153 kern_return_t dyld_process_info_notify_base::makePorts()
154 {
155 // Allocate a port to listen on in this monitoring task
156 if ( kern_return_t r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &_receivePortInMonitor) )
157 return r;
158
159 // Add send rights for replying
160 if ( kern_return_t r = mach_port_insert_right(mach_task_self(), _receivePortInMonitor, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND) )
161 return r;
162
163 // Allocate a name in the target. We need a new name to add send rights to
164 if ( kern_return_t r = mach_port_allocate(_targetTask, MACH_PORT_RIGHT_DEAD_NAME, &_sendPortInTarget) )
165 return r;
166
167 // Deallocate the dead name
168 if ( kern_return_t r = mach_port_mod_refs(_targetTask, _sendPortInTarget, MACH_PORT_RIGHT_DEAD_NAME, -1) )
169 return r;
170
171 // Make the dead name a send right to our listening port
172 if ( kern_return_t r = mach_port_insert_right(_targetTask, _sendPortInTarget, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND) )
173 return r;
174
175 // Notify us if the target dies
176 mach_port_t previous = MACH_PORT_NULL;
177 if ( kern_return_t r = mach_port_request_notification(_targetTask, _sendPortInTarget, MACH_NOTIFY_DEAD_NAME, 0, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous))
178 return r;
179
180 //fprintf(stderr, "_sendPortInTarget=%d, _receivePortInMonitor=%d\n", _sendPortInTarget, _receivePortInMonitor);
181 return KERN_SUCCESS;
182 }
183
184
185
186 void dyld_process_info_notify_base::setMachSourceOnQueue()
187 {
188 NotifyExit exitHandler = _notifyExit;
189 _machSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, _receivePortInMonitor, 0, _queue);
190 dispatch_source_set_event_handler(_machSource, ^{
191 // This event handler block has an implicit reference to "this"
192 // if incrementing the count goes to one, that means the object may have already been destroyed
193 if ( incRetainCount() )
194 return;
195 uint8_t messageBuffer[DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE];
196 mach_msg_header_t* h = (mach_msg_header_t*)messageBuffer;
197
198 kern_return_t r = mach_msg(h, MACH_RCV_MSG, 0, sizeof(messageBuffer), _receivePortInMonitor, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
199 if ( r == KERN_SUCCESS ) {
200 //fprintf(stderr, "received message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size);
201 if ( h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_LOAD_ID || h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID ) {
202 // run notifier block for each [un]load image
203 const dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)messageBuffer;
204 const dyld_process_info_image_entry* entries = (dyld_process_info_image_entry*)&messageBuffer[header->imagesOffset];
205 const char* const stringPool = (char*)&messageBuffer[header->stringsOffset];
206 for (unsigned i=0; i < header->imageCount; ++i) {
207 bool isUnload = (h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID);
208 _notify(isUnload, header->timestamp, entries[i].loadAddress, entries[i].uuid, stringPool + entries[i].pathStringOffset);
209 }
210 // reply to dyld, so it can continue
211 mach_msg_header_t replyHeader;
212 replyHeader.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND);
213 replyHeader.msgh_id = 0;
214 replyHeader.msgh_local_port = MACH_PORT_NULL;
215 replyHeader.msgh_remote_port = h->msgh_remote_port;
216 replyHeader.msgh_reserved = 0;
217 replyHeader.msgh_size = sizeof(replyHeader);
218 mach_msg(&replyHeader, MACH_SEND_MSG | MACH_SEND_TIMEOUT, replyHeader.msgh_size, 0, MACH_PORT_NULL, 100, MACH_PORT_NULL);
219 }
220 else if ( h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_MAIN_ID ) {
221 if ( _notifyMain != NULL ) {
222 _notifyMain();
223 }
224 // reply to dyld, so it can continue
225 mach_msg_header_t replyHeader;
226 replyHeader.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND);
227 replyHeader.msgh_id = 0;
228 replyHeader.msgh_local_port = MACH_PORT_NULL;
229 replyHeader.msgh_remote_port = h->msgh_remote_port;
230 replyHeader.msgh_reserved = 0;
231 replyHeader.msgh_size = sizeof(replyHeader);
232 mach_msg(&replyHeader, MACH_SEND_MSG | MACH_SEND_TIMEOUT, replyHeader.msgh_size, 0, MACH_PORT_NULL, 100, MACH_PORT_NULL);
233 }
234 else if ( h->msgh_id == MACH_NOTIFY_PORT_DELETED ) {
235 mach_port_t deadPort = ((mach_port_deleted_notification_t *)h)->not_port;
236 //fprintf(stderr, "received message id=MACH_NOTIFY_PORT_DELETED, size=%d, deadPort=%d\n", h->msgh_size, deadPort);
237 if ( deadPort == _sendPortInTarget ) {
238 // target process died. Clean up ports
239 _sendPortInTarget = 0;
240 mach_port_deallocate(mach_task_self(), _receivePortInMonitor);
241 _receivePortInMonitor = 0;
242 _portAddressInTarget = 0;
243 // notify that target is gone
244 exitHandler();
245 }
246 }
247 else {
248 fprintf(stderr, "received unknown message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size);
249 }
250 }
251 if ( decRetainCount() )
252 delete this;
253 });
254 dispatch_resume(_machSource);
255 }
256
257
258 kern_return_t dyld_process_info_notify_base::pokeSendPortIntoTarget()
259 {
260 // get location on all_image_infos in target task
261 task_dyld_info_data_t taskDyldInfo;
262 mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
263 kern_return_t r = task_info(_targetTask, TASK_DYLD_INFO, (task_info_t)&taskDyldInfo, &count);
264 if ( r )
265 return r;
266
267 // remap the page containing all_image_infos into this process r/w
268 mach_vm_address_t mappedAddress = 0;
269 mach_vm_size_t mappedSize = taskDyldInfo.all_image_info_size;
270 vm_prot_t curProt = VM_PROT_NONE;
271 vm_prot_t maxProt = VM_PROT_NONE;
272 r = mach_vm_remap(mach_task_self(), &mappedAddress, mappedSize, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR,
273 _targetTask, taskDyldInfo.all_image_info_addr, false, &curProt, &maxProt, VM_INHERIT_NONE);
274 if ( r )
275 return r;
276 if ( curProt != (VM_PROT_READ|VM_PROT_WRITE) )
277 return KERN_PROTECTION_FAILURE;
278
279 // atomically set port into all_image_info_struct
280 static_assert(sizeof(mach_port_t) == sizeof(uint32_t), "machport size not 32-bits");
281
282 mach_vm_address_t mappedAddressToPokePort = 0;
283 if ( taskDyldInfo.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 )
284 mappedAddressToPokePort = mappedAddress + offsetof(dyld_all_image_infos_32,notifyMachPorts);
285 else
286 mappedAddressToPokePort = mappedAddress + offsetof(dyld_all_image_infos_64,notifyMachPorts);
287
288 // use first available slot
289 bool slotFound = false;
290 for (int slotIndex=0; slotIndex < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slotIndex) {
291 if ( OSAtomicCompareAndSwap32Barrier(0, _sendPortInTarget, (volatile int32_t*)mappedAddressToPokePort) ) {
292 slotFound = true;
293 break;
294 }
295 mappedAddressToPokePort += sizeof(uint32_t);
296 }
297 if ( !slotFound ) {
298 mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize);
299 return KERN_UREFS_OVERFLOW;
300 }
301 _portAddressInTarget = taskDyldInfo.all_image_info_addr + mappedAddressToPokePort - mappedAddress;
302 //fprintf(stderr, "poked port %d into target at address 0x%llX\n", _sendPortInTarget, _portAddressInTarget);
303 mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize);
304 return r;
305 }
306
307
308
309 kern_return_t dyld_process_info_notify_base::unpokeSendPortInTarget()
310 {
311 // remap the page containing all_image_infos into this process r/w
312 mach_vm_address_t mappedAddress = 0;
313 mach_vm_size_t mappedSize = sizeof(mach_port_t);
314 vm_prot_t curProt = VM_PROT_NONE;
315 vm_prot_t maxProt = VM_PROT_NONE;
316 kern_return_t r = mach_vm_remap(mach_task_self(), &mappedAddress, mappedSize, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR,
317 _targetTask, _portAddressInTarget, false, &curProt, &maxProt, VM_INHERIT_NONE);
318 if ( r )
319 return r;
320 if ( curProt != (VM_PROT_READ|VM_PROT_WRITE) )
321 return KERN_PROTECTION_FAILURE;
322
323 OSAtomicCompareAndSwap32Barrier(_sendPortInTarget, 0, (volatile int32_t*)mappedAddress);
324
325 //fprintf(stderr, "cleared port %d from target\n", _sendPortInTarget);
326 mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize);
327 return r;
328 }
329
330
331
332 dyld_process_info_notify _dyld_process_info_notify(task_t task, dispatch_queue_t queue,
333 void (^notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path),
334 void (^notifyExit)(),
335 kern_return_t* kr)
336 {
337 return dyld_process_info_notify_base::make(task, queue, notify, notifyExit, kr);
338 }
339
340 void _dyld_process_info_notify_main(dyld_process_info_notify object, void (^notifyMain)())
341 {
342 object->setNotifyMain(notifyMain);
343 }
344
345 void _dyld_process_info_notify_retain(dyld_process_info_notify object)
346 {
347 object->incRetainCount();
348 }
349
350 void _dyld_process_info_notify_release(dyld_process_info_notify object)
351 {
352 // Note if _machSource is currently handling a message, the retain count will not be zero
353 // and object will instead be deleted when handling is done.
354 if ( object->decRetainCount() )
355 delete object;
356 }
357
358
359
360
361
362
363
364 namespace dyld3 {
365
366
367 static mach_port_t sNotifyReplyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
368 static bool sZombieNotifiers[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
369
370 static void notifyMonitoringDyld(bool unloading, unsigned portSlot, const launch_cache::DynArray<loader::ImageInfo>& imageInfos)
371 {
372 if ( sZombieNotifiers[portSlot] )
373 return;
374
375 unsigned entriesSize = (unsigned)imageInfos.count()*sizeof(dyld_process_info_image_entry);
376 unsigned pathsSize = 0;
377 for (uintptr_t i=0; i < imageInfos.count(); ++i) {
378 launch_cache::Image image(imageInfos[i].imageData);
379 pathsSize += (strlen(image.path()) + 1);
380 }
381 unsigned totalSize = (sizeof(dyld_process_info_notify_header) + MAX_TRAILER_SIZE + entriesSize + pathsSize + 127) & -128; // align
382 if ( totalSize > DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE ) {
383 // Putting all image paths into one message would make buffer too big.
384 // Instead split into two messages. Recurse as needed until paths fit in buffer.
385 unsigned imageHalfCount = (unsigned)imageInfos.count()/2;
386 const launch_cache::DynArray<loader::ImageInfo> firstHalf(imageHalfCount, (loader::ImageInfo*)&imageInfos[0]);
387 const launch_cache::DynArray<loader::ImageInfo> secondHalf(imageInfos.count() - imageHalfCount, (loader::ImageInfo*)&imageInfos[imageHalfCount]);
388 notifyMonitoringDyld(unloading, portSlot, firstHalf);
389 notifyMonitoringDyld(unloading, portSlot, secondHalf);
390 return;
391 }
392 // build buffer to send
393 dyld_all_image_infos* allImageInfo = gAllImages.oldAllImageInfo();
394 uint8_t buffer[totalSize];
395 dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)buffer;
396 header->version = 1;
397 header->imageCount = (uint32_t)imageInfos.count();
398 header->imagesOffset = sizeof(dyld_process_info_notify_header);
399 header->stringsOffset = sizeof(dyld_process_info_notify_header) + entriesSize;
400 header->timestamp = allImageInfo->infoArrayChangeTimestamp;
401 dyld_process_info_image_entry* entries = (dyld_process_info_image_entry*)&buffer[header->imagesOffset];
402 char* const pathPoolStart = (char*)&buffer[header->stringsOffset];
403 char* pathPool = pathPoolStart;
404 for (uintptr_t i=0; i < imageInfos.count(); ++i) {
405 launch_cache::Image image(imageInfos[i].imageData);
406 strcpy(pathPool, image.path());
407 uint32_t len = (uint32_t)strlen(pathPool);
408 memcpy(entries->uuid, image.uuid(), sizeof(uuid_t));
409 entries->loadAddress = (uint64_t)imageInfos[i].loadAddress;
410 entries->pathStringOffset = (uint32_t)(pathPool - pathPoolStart);
411 entries->pathLength = len;
412 pathPool += (len +1);
413 ++entries;
414 }
415 // lazily alloc reply port
416 if ( sNotifyReplyPorts[portSlot] == 0 ) {
417 if ( !mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sNotifyReplyPorts[portSlot]) )
418 mach_port_insert_right(mach_task_self(), sNotifyReplyPorts[portSlot], sNotifyReplyPorts[portSlot], MACH_MSG_TYPE_MAKE_SEND);
419 //log("allocated reply port %d\n", sNotifyReplyPorts[portSlot]);
420 }
421 //log("found port to send to\n");
422 mach_msg_header_t* h = (mach_msg_header_t*)buffer;
423 h->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND); // MACH_MSG_TYPE_MAKE_SEND_ONCE
424 h->msgh_id = unloading ? DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID : DYLD_PROCESS_INFO_NOTIFY_LOAD_ID;
425 h->msgh_local_port = sNotifyReplyPorts[portSlot];
426 h->msgh_remote_port = allImageInfo->notifyPorts[portSlot];
427 h->msgh_reserved = 0;
428 h->msgh_size = (mach_msg_size_t)sizeof(buffer);
429 //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);
430 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);
431 //log("send result = 0x%X, msg_id=%d, msg_size=%d\n", sendResult, h->msgh_id, h->msgh_size);
432 if ( sendResult == MACH_SEND_INVALID_DEST ) {
433 // sender is not responding, detatch
434 //log("process requesting notification gone. deallocation send port %d and receive port %d\n", allImageInfo->notifyPorts[portSlot], sNotifyReplyPorts[portSlot]);
435 mach_port_deallocate(mach_task_self(), allImageInfo->notifyPorts[portSlot]);
436 mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[portSlot]);
437 allImageInfo->notifyPorts[portSlot] = 0;
438 sNotifyReplyPorts[portSlot] = 0;
439 }
440 else if ( sendResult == MACH_RCV_TIMED_OUT ) {
441 // client took too long, ignore him from now on
442 sZombieNotifiers[portSlot] = true;
443 mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[portSlot]);
444 sNotifyReplyPorts[portSlot] = 0;
445 }
446 }
447
448 void AllImages::notifyMonitorMain()
449 {
450 dyld_all_image_infos* allImageInfo = gAllImages.oldAllImageInfo();
451 for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
452 if ( (allImageInfo->notifyPorts[slot] != 0 ) && !sZombieNotifiers[slot] ) {
453 if ( sNotifyReplyPorts[slot] == 0 ) {
454 if ( !mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sNotifyReplyPorts[slot]) )
455 mach_port_insert_right(mach_task_self(), sNotifyReplyPorts[slot], sNotifyReplyPorts[slot], MACH_MSG_TYPE_MAKE_SEND);
456 //dyld::log("allocated reply port %d\n", sNotifyReplyPorts[slot]);
457 }
458 //dyld::log("found port to send to\n");
459 uint8_t messageBuffer[sizeof(mach_msg_header_t) + MAX_TRAILER_SIZE];
460 mach_msg_header_t* h = (mach_msg_header_t*)messageBuffer;
461 h->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND); // MACH_MSG_TYPE_MAKE_SEND_ONCE
462 h->msgh_id = DYLD_PROCESS_INFO_NOTIFY_MAIN_ID;
463 h->msgh_local_port = sNotifyReplyPorts[slot];
464 h->msgh_remote_port = allImageInfo->notifyPorts[slot];
465 h->msgh_reserved = 0;
466 h->msgh_size = (mach_msg_size_t)sizeof(messageBuffer);
467 //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);
468 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);
469 //dyld::log("send result = 0x%X, msg_id=%d, msg_size=%d\n", sendResult, h->msgh_id, h->msgh_size);
470 if ( sendResult == MACH_SEND_INVALID_DEST ) {
471 // sender is not responding, detatch
472 //dyld::log("process requesting notification gone. deallocation send port %d and receive port %d\n", allImageInfo->notifyPorts[slot], sNotifyReplyPorts[slot]);
473 mach_port_deallocate(mach_task_self(), allImageInfo->notifyPorts[slot]);
474 mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
475 allImageInfo->notifyPorts[slot] = 0;
476 sNotifyReplyPorts[slot] = 0;
477 }
478 else if ( sendResult == MACH_RCV_TIMED_OUT ) {
479 // client took too long, ignore him from now on
480 sZombieNotifiers[slot] = true;
481 mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
482 sNotifyReplyPorts[slot] = 0;
483 }
484 }
485 }
486 }
487
488 void AllImages::notifyMonitorLoads(const launch_cache::DynArray<loader::ImageInfo>& newImages)
489 {
490 // notify each monitoring process
491 dyld_all_image_infos* allImageInfo = gAllImages.oldAllImageInfo();
492 for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
493 if ( allImageInfo->notifyPorts[slot] != 0 ) {
494 notifyMonitoringDyld(false, slot, newImages);
495 }
496 else if ( sNotifyReplyPorts[slot] != 0 ) {
497 // monitoring process detached from this process, so release reply port
498 //dyld::log("deallocated reply port %d\n", sNotifyReplyPorts[slot]);
499 mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
500 sNotifyReplyPorts[slot] = 0;
501 sZombieNotifiers[slot] = false;
502 }
503 }
504 }
505
506 void AllImages::notifyMonitorUnloads(const launch_cache::DynArray<loader::ImageInfo>& unloadingImages)
507 {
508 // notify each monitoring process
509 dyld_all_image_infos* allImageInfo = gAllImages.oldAllImageInfo();
510 for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
511 if ( allImageInfo->notifyPorts[slot] != 0 ) {
512 notifyMonitoringDyld(true, slot, unloadingImages);
513 }
514 else if ( sNotifyReplyPorts[slot] != 0 ) {
515 // monitoring process detached from this process, so release reply port
516 //dyld::log("deallocated reply port %d\n", sNotifyReplyPorts[slot]);
517 mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
518 sNotifyReplyPorts[slot] = 0;
519 sZombieNotifiers[slot] = false;
520 }
521 }
522 }
523
524 } // namespace dyld3
525
526
527
528