1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2016 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
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
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.
22 * @APPLE_LICENSE_HEADER_END@
30 #include <sys/param.h>
31 #include <mach/shared_region.h>
32 #include <mach/mach_vm.h>
33 #include <mach/vm_region.h>
34 #include <libkern/OSAtomic.h>
36 #include "dyld_process_info.h"
37 #include "dyld_process_info_internal.h"
38 #include "dyld_images.h"
39 #include "dyld_priv.h"
43 // this was in dyld_priv.h but it is no longer exported
45 const struct dyld_all_image_infos
* _dyld_get_all_image_infos();
48 RemoteBuffer::RemoteBuffer() : _localAddress(0), _size(0) {}
49 bool RemoteBuffer::map(task_t task
, mach_vm_address_t remote_address
, bool shared
) {
50 vm_prot_t cur_protection
= VM_PROT_NONE
;
51 vm_prot_t max_protection
= VM_PROT_NONE
;
57 _kr
= mach_vm_remap(mach_task_self(),
61 VM_FLAGS_ANYWHERE
| VM_FLAGS_RETURN_DATA_ADDR
| (shared
? 0 : VM_FLAGS_RESILIENT_CODESIGN
),
68 dyld3::kdebug_trace_dyld_marker(DBG_DYLD_DEBUGGING_VM_REMAP
, _localAddress
, (uint64_t)_size
, _kr
, remote_address
);
69 if (shared
&& (cur_protection
!= (VM_PROT_READ
|VM_PROT_WRITE
))) {
70 if (_kr
== KERN_SUCCESS
&& _localAddress
!= 0) {
71 _kr
= vm_deallocate(mach_task_self(), _localAddress
, _size
);
72 dyld3::kdebug_trace_dyld_marker(DBG_DYLD_DEBUGGING_VM_UNMAP
, _localAddress
, (uint64_t)_size
, _kr
, 0);
75 _kr
= KERN_PROTECTION_FAILURE
;
77 return (_kr
== KERN_SUCCESS
);
80 RemoteBuffer::RemoteBuffer(task_t task
, mach_vm_address_t remote_address
, size_t remote_size
, bool shared
, bool allow_truncation
)
81 : _localAddress(0), _size(remote_size
), _kr(KERN_SUCCESS
) {
82 // Try the initial map
83 if (map(task
, remote_address
, shared
)) return;
84 // It failed, try to calculate the largest size that can fit in the same page as the remote_address
85 uint64_t newSize
= PAGE_SIZE
- remote_address%PAGE_SIZE
;;
86 // If truncation is allowed and the newSize is different than the original size try that
87 if (!allow_truncation
&& newSize
!= _size
) return;
89 if (map(task
, remote_address
, shared
)) return;
90 // That did not work, null out the buffer
94 RemoteBuffer::~RemoteBuffer() {
96 _kr
= vm_deallocate(mach_task_self(), _localAddress
, _size
);
97 dyld3::kdebug_trace_dyld_marker(DBG_DYLD_DEBUGGING_VM_UNMAP
, _localAddress
, (uint64_t)_size
, _kr
, 0);
100 void *RemoteBuffer::getLocalAddress() { return (void *)_localAddress
; }
101 size_t RemoteBuffer::getSize() { return _size
; }
102 kern_return_t
RemoteBuffer::getKernelReturn() { return _kr
; }
104 void withRemoteBuffer(task_t task
, mach_vm_address_t remote_address
, size_t remote_size
, bool shared
, bool allow_truncation
, kern_return_t
*kr
, void (^block
)(void *buffer
, size_t size
)) {
105 kern_return_t krSink
= KERN_SUCCESS
;
109 RemoteBuffer
buffer(task
, remote_address
, remote_size
, shared
, allow_truncation
);
110 *kr
= buffer
.getKernelReturn();
111 if (*kr
== KERN_SUCCESS
) {
112 block(buffer
.getLocalAddress(), buffer
.getSize());
118 // Opaque object returned by _dyld_process_info_create()
121 struct __attribute__((visibility("hidden"))) dyld_process_info_deleter
{ // deleter
122 // dyld_process_info_deleter() {};
123 // dyld_process_info_deleter(const dyld_process_info_deleter&) { }
124 // dyld_process_info_deleter(dyld_process_info_deleter&) {}
125 // dyld_process_info_deleter(dyld_process_info_deleter&&) {}
126 void operator()(dyld_process_info_base
* p
) const {
133 static dyld_process_info_deleter deleter
;
134 typedef std::unique_ptr
<dyld_process_info_base
, dyld_process_info_deleter
> dyld_process_info_ptr
;
136 struct __attribute__((visibility("hidden"))) dyld_process_info_base
{
137 template<typename T1
, typename T2
>
138 static dyld_process_info_ptr
make(task_t task
, const T1
& allImageInfo
, uint64_t timestamp
, kern_return_t
* kr
);
140 static dyld_process_info_ptr
makeSuspended(task_t task
, const T
& allImageInfo
, kern_return_t
* kr
);
142 std::atomic
<uint32_t>& retainCount() const { return _retainCount
; }
143 dyld_process_cache_info
* cacheInfo() const { return (dyld_process_cache_info
*)(((char*)this) + _cacheInfoOffset
); }
144 dyld_process_state_info
* stateInfo() const { return (dyld_process_state_info
*)(((char*)this) + _stateInfoOffset
); }
145 void forEachImage(void (^callback
)(uint64_t machHeaderAddress
, const uuid_t uuid
, const char* path
)) const;
146 void forEachSegment(uint64_t machHeaderAddress
, void (^callback
)(uint64_t segmentAddress
, uint64_t segmentSize
, const char* segmentName
)) const;
155 uint32_t newCount
= --_retainCount
;
157 if ( newCount
== 0 ) {
165 uint64_t loadAddress
;
167 uint32_t segmentStartIndex
;
168 uint32_t segmentsCount
;
177 dyld_process_info_base(unsigned imageCount
, size_t totalSize
);
178 void* operator new (size_t, void* buf
) { return buf
; }
180 static bool inCache(uint64_t addr
) { return (addr
> SHARED_REGION_BASE
) && (addr
< SHARED_REGION_BASE
+SHARED_REGION_SIZE
); }
181 kern_return_t
addImage(task_t task
, bool sameCacheAsThisProcess
, uint64_t imageAddress
, uint64_t imagePath
, const char* imagePathLocal
);
183 kern_return_t
addDyldImage(task_t task
, uint64_t dyldAddress
, uint64_t dyldPathAddress
, const char* localPath
);
185 bool invalid() { return ((char*)_stringRevBumpPtr
< (char*)_curSegment
); }
186 const char* copyPath(task_t task
, uint64_t pathAddr
, kern_return_t
* kr
);
187 const char* addString(const char*, size_t);
188 const char* copySegmentName(const char*);
190 void addInfoFromLoadCommands(const mach_header
* mh
, uint64_t addressInTask
, size_t size
);
191 kern_return_t
addInfoFromRemoteLoadCommands(task_t task
, uint64_t remoteMH
);
193 void inspectLocalImageLoadCommands(uint64_t imageAddress
, void* func
);
194 kern_return_t
inspectRemoteImageLoadCommands(task_t task
, uint64_t imageAddress
, void* func
);
196 mutable std::atomic
<uint32_t> _retainCount
;
197 const uint32_t _cacheInfoOffset
;
198 const uint32_t _stateInfoOffset
;
199 const uint32_t _imageInfosOffset
;
200 const uint32_t _segmentInfosOffset
;
201 ImageInfo
* const _firstImage
;
202 ImageInfo
* _curImage
;
203 SegmentInfo
* const _firstSegment
;
204 SegmentInfo
* _curSegment
;
205 uint32_t _curSegmentIndex
;
206 char* _stringRevBumpPtr
;
208 // dyld_process_cache_info cacheInfo;
209 // dyld_process_state_info stateInfo;
210 // ImageInfo images[];
211 // SegmentInfo segments[];
215 dyld_process_info_base::dyld_process_info_base(unsigned imageCount
, size_t totalSize
)
216 : _retainCount(1), _cacheInfoOffset(sizeof(dyld_process_info_base
)),
217 _stateInfoOffset(sizeof(dyld_process_info_base
) + sizeof(dyld_process_cache_info
)),
218 _imageInfosOffset(sizeof(dyld_process_info_base
) + sizeof(dyld_process_cache_info
) + sizeof(dyld_process_state_info
)),
219 _segmentInfosOffset(sizeof(dyld_process_info_base
) + sizeof(dyld_process_cache_info
) + sizeof(dyld_process_state_info
) + imageCount
*sizeof(ImageInfo
)),
220 _firstImage((ImageInfo
*)(((uint8_t*)this) + _imageInfosOffset
)),
221 _curImage((ImageInfo
*)(((uint8_t*)this) + _imageInfosOffset
)),
222 _firstSegment((SegmentInfo
*)(((uint8_t*)this) + _segmentInfosOffset
)),
223 _curSegment((SegmentInfo
*)(((uint8_t*)this) + _segmentInfosOffset
)),
225 _stringRevBumpPtr((char*)(this)+totalSize
)
229 template<typename T1
, typename T2
>
230 dyld_process_info_ptr
dyld_process_info_base::make(task_t task
, const T1
& allImageInfo
, uint64_t timestamp
, kern_return_t
* kr
)
232 __block dyld_process_info_ptr result
= nullptr;
234 // bail out of dyld is too old
235 if ( allImageInfo
.version
< 15 ) {
240 // Check if the process is suspended
241 if (allImageInfo
.infoArrayChangeTimestamp
== 0) {
242 result
= dyld_process_info_base::makeSuspended
<T1
>(task
, allImageInfo
, kr
);
243 // If we have a result return it, otherwise rescan
245 // If it returned the process is suspended and there is nothing more to do
246 return std::move(result
);
248 // Check to see if the process change timestamp is greater than 0, if not then sleep to let the process
249 // finish initializing
250 if (allImageInfo
.infoArrayChangeTimestamp
== 0) {
251 usleep(1000 * 50); // 50ms
256 // Test to see if there are no changes and we can exit early
257 if (timestamp
!= 0 && timestamp
== allImageInfo
.infoArrayChangeTimestamp
) {
262 for(uint32_t i
= 0; i
< 10; ++i
) {
263 uint64_t currentTimestamp
= allImageInfo
.infoArrayChangeTimestamp
;
264 mach_vm_address_t infoArray
= allImageInfo
.infoArray
;
265 if (currentTimestamp
== 0) continue;
266 if (infoArray
== 0) {
267 // Check if the task is suspended mid dylib load and exit early
268 mach_task_basic_info ti
;
269 mach_msg_type_number_t count
= MACH_TASK_BASIC_INFO_COUNT
;
270 if ((*kr
= task_info(task
, MACH_TASK_BASIC_INFO
, (task_info_t
)&ti
, &count
))) {
274 // The task is suspended, exit
275 if (ti
.suspend_count
!= 0) {
276 // Not exactly correct, but conveys that operation may succeed in the future
277 *kr
= KERN_RESOURCE_SHORTAGE
;
283 // For the moment we are going to truncate any image list longer than 8192 because some programs do
284 // terrible things that corrupt their own image lists and we need to stop clients from crashing
285 // reading them. We can try to do something more advanced in the future. rdar://27446361
286 uint32_t imageCount
= allImageInfo
.infoArrayCount
;
287 imageCount
= MIN(imageCount
, 8192);
288 size_t imageArraySize
= imageCount
* sizeof(T2
);
290 withRemoteBuffer(task
, infoArray
, imageArraySize
, false, false, kr
, ^(void *buffer
, size_t size
) {
291 // figure out how many path strings will need to be copied and their size
292 T2
* imageArray
= (T2
*)buffer
;
293 const dyld_all_image_infos
* myInfo
= _dyld_get_all_image_infos();
294 bool sameCacheAsThisProcess
= !allImageInfo
.processDetachedFromSharedRegion
295 && !myInfo
->processDetachedFromSharedRegion
296 && ((memcmp(myInfo
->sharedCacheUUID
, &allImageInfo
.sharedCacheUUID
[0], 16) == 0)
297 && (myInfo
->sharedCacheSlide
== allImageInfo
.sharedCacheSlide
));
298 unsigned countOfPathsNeedingCopying
= 0;
299 if ( sameCacheAsThisProcess
) {
300 for (uint32_t i
=0; i
< imageCount
; ++i
) {
301 if ( !inCache(imageArray
[i
].imageFilePath
) )
302 ++countOfPathsNeedingCopying
;
306 countOfPathsNeedingCopying
= imageCount
+1;
308 unsigned imageCountWithDyld
= imageCount
+1;
310 // allocate result object
311 size_t allocationSize
= sizeof(dyld_process_info_base
)
312 + sizeof(dyld_process_cache_info
)
313 + sizeof(dyld_process_state_info
)
314 + sizeof(ImageInfo
)*(imageCountWithDyld
)
315 + sizeof(SegmentInfo
)*imageCountWithDyld
*5
316 + countOfPathsNeedingCopying
*PATH_MAX
;
317 void* storage
= malloc(allocationSize
);
318 auto info
= dyld_process_info_ptr(new (storage
) dyld_process_info_base(imageCountWithDyld
, allocationSize
), deleter
);
319 //info = new (storage) dyld_process_info_base(imageCountWithDyld, allocationSize); // placement new()
322 dyld_process_cache_info
* cacheInfo
= info
->cacheInfo();
323 memcpy(cacheInfo
->cacheUUID
, &allImageInfo
.sharedCacheUUID
[0], 16);
324 cacheInfo
->cacheBaseAddress
= allImageInfo
.sharedCacheBaseAddress
;
325 cacheInfo
->privateCache
= allImageInfo
.processDetachedFromSharedRegion
;
326 // if no cache is used, allImageInfo has all zeros for cache UUID
327 cacheInfo
->noCache
= true;
328 for (int i
=0; i
< 16; ++i
) {
329 if ( cacheInfo
->cacheUUID
[i
] != 0 ) {
330 cacheInfo
->noCache
= false;
334 dyld_process_state_info
* stateInfo
= info
->stateInfo();
335 stateInfo
->timestamp
= currentTimestamp
;
336 stateInfo
->imageCount
= imageCountWithDyld
;
337 stateInfo
->initialImageCount
= (uint32_t)(allImageInfo
.initialImageCount
+1);
338 stateInfo
->dyldState
= dyld_process_state_dyld_initialized
;
340 if ( allImageInfo
.libSystemInitialized
!= 0 ) {
341 stateInfo
->dyldState
= dyld_process_state_libSystem_initialized
;
342 if ( allImageInfo
.initialImageCount
!= imageCount
) {
343 stateInfo
->dyldState
= dyld_process_state_program_running
;
346 if ( allImageInfo
.errorMessage
!= 0 ) {
347 stateInfo
->dyldState
= allImageInfo
.terminationFlags
? dyld_process_state_terminated_before_inits
: dyld_process_state_dyld_terminated
;
349 // fill in info for dyld
350 if ( allImageInfo
.dyldPath
!= 0 ) {
351 if ((*kr
= info
->addDyldImage(task
, allImageInfo
.dyldImageLoadAddress
, allImageInfo
.dyldPath
, NULL
))) {
356 // fill in info for each image
357 for (uint32_t i
=0; i
< imageCount
; ++i
) {
358 if ((*kr
= info
->addImage(task
, sameCacheAsThisProcess
, imageArray
[i
].imageLoadAddress
, imageArray
[i
].imageFilePath
, NULL
))) {
363 // sanity check internal data did not overflow
364 if ( info
->invalid() ) {
370 result
= std::move(info
);
376 return std::move(result
);
380 dyld_process_info_ptr
dyld_process_info_base::makeSuspended(task_t task
, const T
& allImageInfo
, kern_return_t
* kr
)
383 if ((*kr
= pid_for_task(task
, &pid
))) {
387 mach_task_basic_info ti
;
388 mach_msg_type_number_t count
= MACH_TASK_BASIC_INFO_COUNT
;
389 if ((*kr
= task_info(task
, MACH_TASK_BASIC_INFO
, (task_info_t
)&ti
, &count
))) {
393 // The task is not suspended, exit
394 if (ti
.suspend_count
== 0) {
398 __block
unsigned imageCount
= 0; // main executable and dyld
399 __block
uint64_t mainExecutableAddress
= 0;
400 __block
uint64_t dyldAddress
= 0;
401 char dyldPathBuffer
[PATH_MAX
+1];
402 char mainExecutablePathBuffer
[PATH_MAX
+1];
403 __block
char * dyldPath
= &dyldPathBuffer
[0];
404 __block
char * mainExecutablePath
= &mainExecutablePathBuffer
[0];
406 for (mach_vm_address_t address
= 0; ; address
+= size
) {
407 vm_region_basic_info_data_64_t info
;
408 mach_port_t objectName
;
409 unsigned int infoCount
= VM_REGION_BASIC_INFO_COUNT_64
;
410 if (kern_return_t r
= mach_vm_region(task
, &address
, &size
, VM_REGION_BASIC_INFO
,
411 (vm_region_info_t
)&info
, &infoCount
, &objectName
)) {
414 if ( info
.protection
!= (VM_PROT_READ
|VM_PROT_EXECUTE
) )
416 // read start of vm region to verify it is a mach header
417 withRemoteObject(task
, address
, false, NULL
, ^(mach_header_64 mhBuffer
){
418 if ( (mhBuffer
.magic
!= MH_MAGIC
) && (mhBuffer
.magic
!= MH_MAGIC_64
) )
420 // now know the region is the start of a mach-o file
421 if ( mhBuffer
.filetype
== MH_EXECUTE
) {
422 mainExecutableAddress
= address
;
423 int len
= proc_regionfilename(pid
, mainExecutableAddress
, mainExecutablePath
, PATH_MAX
);
425 mainExecutablePath
[len
] = '\0';
429 else if ( mhBuffer
.filetype
== MH_DYLINKER
) {
430 dyldAddress
= address
;
431 int len
= proc_regionfilename(pid
, dyldAddress
, dyldPath
, PATH_MAX
);
433 dyldPath
[len
] = '\0';
438 //fprintf(stderr, "vm region: addr=0x%llX, size=0x%llX, prot=0x%X\n", (uint64_t)address, (uint64_t)size, info.protection);
440 //fprintf(stderr, "dyld: addr=0x%llX, path=%s\n", dyldAddress, dyldPathBuffer);
441 //fprintf(stderr, "app: addr=0x%llX, path=%s\n", mainExecutableAddress, mainExecutablePathBuffer);
443 // allocate result object
444 size_t allocationSize
= sizeof(dyld_process_info_base
)
445 + sizeof(dyld_process_cache_info
)
446 + sizeof(dyld_process_state_info
)
447 + sizeof(ImageInfo
)*(imageCount
)
448 + sizeof(SegmentInfo
)*imageCount
*5
449 + imageCount
*PATH_MAX
;
450 void* storage
= malloc(allocationSize
);
451 auto obj
= dyld_process_info_ptr(new (storage
) dyld_process_info_base(imageCount
, allocationSize
), deleter
);
453 dyld_process_cache_info
* cacheInfo
= obj
->cacheInfo();
454 bzero(cacheInfo
->cacheUUID
, 16);
455 cacheInfo
->cacheBaseAddress
= 0;
456 cacheInfo
->noCache
= true;
457 cacheInfo
->privateCache
= false;
459 dyld_process_state_info
* stateInfo
= obj
->stateInfo();
460 stateInfo
->timestamp
= 0;
461 stateInfo
->imageCount
= imageCount
;
462 stateInfo
->initialImageCount
= imageCount
;
463 stateInfo
->dyldState
= dyld_process_state_not_started
;
465 // fill in info for dyld
466 if ( dyldAddress
!= 0 ) {
467 if ((*kr
= obj
->addDyldImage(task
, dyldAddress
, 0, dyldPath
))) {
472 // fill in info for each image
473 if ( mainExecutableAddress
!= 0 ) {
474 if ((*kr
= obj
->addImage(task
, false, mainExecutableAddress
, 0, mainExecutablePath
))) {
479 if (allImageInfo
.infoArrayChangeTimestamp
!= 0) {
483 count
= MACH_TASK_BASIC_INFO_COUNT
;
484 if ((*kr
= task_info(task
, MACH_TASK_BASIC_INFO
, (task_info_t
)&ti
, &count
))) {
488 // The task is not suspended, exit
489 if (ti
.suspend_count
== 0) {
498 const char* dyld_process_info_base::addString(const char* str
, size_t maxlen
)
500 size_t len
= strnlen(str
, maxlen
) + 1;
501 _stringRevBumpPtr
-= len
;
502 strlcpy(_stringRevBumpPtr
, str
, len
);
503 return _stringRevBumpPtr
;
506 const char* dyld_process_info_base::copyPath(task_t task
, uint64_t stringAddressInTask
, kern_return_t
* kr
)
508 __block
const char* retval
= NULL
;
509 withRemoteBuffer(task
, stringAddressInTask
, PATH_MAX
, false, true, kr
, ^(void *buffer
, size_t size
) {
510 retval
= addString(static_cast<const char *>(buffer
), size
);
515 kern_return_t
dyld_process_info_base::addImage(task_t task
, bool sameCacheAsThisProcess
, uint64_t imageAddress
, uint64_t imagePath
, const char* imagePathLocal
)
517 kern_return_t kr
= KERN_SUCCESS
;
518 _curImage
->loadAddress
= imageAddress
;
519 _curImage
->segmentStartIndex
= _curSegmentIndex
;
520 if ( imagePathLocal
!= NULL
) {
521 _curImage
->path
= addString(imagePathLocal
, PATH_MAX
);
523 else if ( sameCacheAsThisProcess
&& inCache(imagePath
) ) {
524 _curImage
->path
= (const char*)imagePath
;
527 _curImage
->path
= copyPath(task
, imagePath
, &kr
);
528 if ( kr
!= KERN_SUCCESS
)
531 if ( sameCacheAsThisProcess
&& inCache(imageAddress
) ) {
532 addInfoFromLoadCommands((mach_header
*)imageAddress
, imageAddress
, 32*1024);
535 kr
= addInfoFromRemoteLoadCommands(task
, imageAddress
);
536 if ( kr
!= KERN_SUCCESS
)
539 _curImage
->segmentsCount
= _curSegmentIndex
- _curImage
->segmentStartIndex
;
545 kern_return_t
dyld_process_info_base::addInfoFromRemoteLoadCommands(task_t task
, uint64_t remoteMH
) {
546 __block kern_return_t kr
= KERN_SUCCESS
;
547 __block
size_t headerPagesSize
= 0;
548 __block
bool done
= false;
550 //Since the minimum we can reasonably map is a page, map that.
551 withRemoteBuffer(task
, remoteMH
, PAGE_SIZE
, false, false, &kr
, ^(void * buffer
, size_t size
) {
552 const mach_header
* mh
= (const mach_header
*)buffer
;
553 headerPagesSize
= sizeof(mach_header
) + mh
->sizeofcmds
;
554 if (headerPagesSize
<= PAGE_SIZE
) {
555 addInfoFromLoadCommands(mh
, remoteMH
, size
);
560 //The load commands did not fit in the first page, but now we know the size, so remap and try again
562 if (kr
!= KERN_SUCCESS
) {
565 withRemoteBuffer(task
, remoteMH
, headerPagesSize
, false, false, &kr
, ^(void * buffer
, size_t size
) {
566 addInfoFromLoadCommands((mach_header
*)buffer
, remoteMH
, size
);
573 kern_return_t
dyld_process_info_base::addDyldImage(task_t task
, uint64_t dyldAddress
, uint64_t dyldPathAddress
, const char* localPath
)
575 __block kern_return_t kr
= KERN_SUCCESS
;
576 _curImage
->loadAddress
= dyldAddress
;
577 _curImage
->segmentStartIndex
= _curSegmentIndex
;
578 if ( localPath
!= NULL
) {
579 _curImage
->path
= addString(localPath
, PATH_MAX
);
582 _curImage
->path
= copyPath(task
, dyldPathAddress
, &kr
);
583 if ( kr
!= KERN_SUCCESS
)
587 kr
= addInfoFromRemoteLoadCommands(task
, dyldAddress
);
588 if ( kr
!= KERN_SUCCESS
)
591 _curImage
->segmentsCount
= _curSegmentIndex
- _curImage
->segmentStartIndex
;
597 void dyld_process_info_base::addInfoFromLoadCommands(const mach_header
* mh
, uint64_t addressInTask
, size_t size
)
599 const load_command
* startCmds
= NULL
;
600 if ( mh
->magic
== MH_MAGIC_64
)
601 startCmds
= (load_command
*)((char *)mh
+ sizeof(mach_header_64
));
602 else if ( mh
->magic
== MH_MAGIC
)
603 startCmds
= (load_command
*)((char *)mh
+ sizeof(mach_header
));
605 return; // not a mach-o file, or wrong endianness
607 const load_command
* const cmdsEnd
= (load_command
*)((char*)startCmds
+ mh
->sizeofcmds
);
608 const load_command
* cmd
= startCmds
;
609 for(uint32_t i
= 0; i
< mh
->ncmds
; ++i
) {
610 const load_command
* nextCmd
= (load_command
*)((char *)cmd
+ cmd
->cmdsize
);
611 if ( (cmd
->cmdsize
< 8) || (nextCmd
> cmdsEnd
) || (nextCmd
< startCmds
) ) {
612 return; // malformed load command
614 if ( cmd
->cmd
== LC_UUID
) {
615 const uuid_command
* uuidCmd
= (uuid_command
*)cmd
;
616 memcpy(_curImage
->uuid
, uuidCmd
->uuid
, 16);
618 else if ( cmd
->cmd
== LC_SEGMENT
) {
619 const segment_command
* segCmd
= (segment_command
*)cmd
;
620 _curSegment
->name
= copySegmentName(segCmd
->segname
);
621 _curSegment
->addr
= segCmd
->vmaddr
;
622 _curSegment
->size
= segCmd
->vmsize
;
626 else if ( cmd
->cmd
== LC_SEGMENT_64
) {
627 const segment_command_64
* segCmd
= (segment_command_64
*)cmd
;
628 _curSegment
->name
= copySegmentName(segCmd
->segname
);
629 _curSegment
->addr
= segCmd
->vmaddr
;
630 _curSegment
->size
= segCmd
->vmsize
;
638 const char* dyld_process_info_base::copySegmentName(const char* name
)
640 // don't copy names of standard segments into string pool
641 static const char* stdSegNames
[] = {"__TEXT", "__DATA", "__LINKEDIT", "__DATA_DIRTY", "__DATA_CONST", "__OBJC", NULL
};
642 for (const char** s
=stdSegNames
; *s
!= NULL
; ++s
) {
643 if ( strcmp(name
, *s
) == 0 )
646 // copy custom segment names into string pool
647 return addString(name
, 16);
650 void dyld_process_info_base::forEachImage(void (^callback
)(uint64_t machHeaderAddress
, const uuid_t uuid
, const char* path
)) const
652 for (const ImageInfo
* p
= _firstImage
; p
< _curImage
; ++p
) {
653 callback(p
->loadAddress
, p
->uuid
, p
->path
);
657 void dyld_process_info_base::forEachSegment(uint64_t machHeaderAddress
, void (^callback
)(uint64_t segmentAddress
, uint64_t segmentSize
, const char* segmentName
)) const
659 for (const ImageInfo
* p
= _firstImage
; p
< _curImage
; ++p
) {
660 if ( p
->loadAddress
== machHeaderAddress
) {
662 for (uint32_t i
=0; i
< p
->segmentsCount
; ++i
) {
663 const SegmentInfo
* seg
= &_firstSegment
[p
->segmentStartIndex
+i
];
664 if ( strcmp(seg
->name
, "__TEXT") == 0 ) {
665 slide
= machHeaderAddress
- seg
->addr
;
669 for (uint32_t i
=0; i
< p
->segmentsCount
; ++i
) {
670 const SegmentInfo
* seg
= &_firstSegment
[p
->segmentStartIndex
+i
];
671 callback(seg
->addr
+ slide
, seg
->size
, seg
->name
);
678 dyld_process_info
_dyld_process_info_create(task_t task
, uint64_t timestamp
, kern_return_t
* kr
)
680 __block dyld_process_info result
= nullptr;
681 kern_return_t krSink
= KERN_SUCCESS
;
687 task_dyld_info_data_t task_dyld_info
;
688 mach_msg_type_number_t count
= TASK_DYLD_INFO_COUNT
;
689 if ( kern_return_t r
= task_info(task
, TASK_DYLD_INFO
, (task_info_t
)&task_dyld_info
, &count
) ) {
694 //The kernel will return MACH_VM_MIN_ADDRESS for an executable that has not had dyld loaded
695 if (task_dyld_info
.all_image_info_addr
== MACH_VM_MIN_ADDRESS
)
698 if ( task_dyld_info
.all_image_info_size
> sizeof(dyld_all_image_infos_64
) )
701 // We use a true shared memory buffer here, that way by making sure that libdyld in both processes
702 // reads and writes the the timestamp atomically we can make sure we get a coherent view of the
704 // That also means that we *MUST* directly read the memory, which is why we template the make() call
705 withRemoteBuffer(task
, task_dyld_info
.all_image_info_addr
, task_dyld_info
.all_image_info_size
, true, false, kr
, ^(void *buffer
, size_t size
) {
706 dyld_process_info_ptr base
;
707 if (task_dyld_info
.all_image_info_format
== TASK_DYLD_ALL_IMAGE_INFO_32
) {
708 const dyld_all_image_infos_32
* info
= (const dyld_all_image_infos_32
*)buffer
;
709 base
= dyld_process_info_base::make
<dyld_all_image_infos_32
, dyld_image_info_32
>(task
, *info
, timestamp
, kr
);
711 const dyld_all_image_infos_64
* info
= (const dyld_all_image_infos_64
*)buffer
;
712 base
= dyld_process_info_base::make
<dyld_all_image_infos_64
, dyld_image_info_64
>(task
, *info
, timestamp
, kr
);
715 result
= base
.release();
721 void _dyld_process_info_get_state(dyld_process_info info
, dyld_process_state_info
* stateInfo
)
723 *stateInfo
= *info
->stateInfo();
726 void _dyld_process_info_get_cache(dyld_process_info info
, dyld_process_cache_info
* cacheInfo
)
728 *cacheInfo
= *info
->cacheInfo();
731 void _dyld_process_info_retain(dyld_process_info object
)
733 const_cast<dyld_process_info_base
*>(object
)->retain();
736 void _dyld_process_info_release(dyld_process_info object
)
738 const_cast<dyld_process_info_base
*>(object
)->release();
741 void _dyld_process_info_for_each_image(dyld_process_info info
, void (^callback
)(uint64_t machHeaderAddress
, const uuid_t uuid
, const char* path
))
743 info
->forEachImage(callback
);
747 void _dyld_process_info_for_each_segment(dyld_process_info info
, uint64_t machHeaderAddress
, void (^callback
)(uint64_t segmentAddress
, uint64_t segmentSize
, const char* segmentName
))
749 info
->forEachSegment(machHeaderAddress
, callback
);