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"
41 // this was in dyld_priv.h but it is no longer exported
43 const struct dyld_all_image_infos
* _dyld_get_all_image_infos();
48 void withRemoteBuffer(task_t task
, vm_address_t remote_address
, size_t remote_size
, bool allow_truncation
, kern_return_t
*kr
, void (^block
)(void *buffer
)) {
49 kern_return_t r
= KERN_SUCCESS
;
50 mach_vm_address_t local_address
= 0;
51 mach_vm_address_t local_size
= remote_size
;
53 vm_prot_t cur_protection
, max_protection
;
54 r
= mach_vm_remap(mach_task_self(),
58 VM_FLAGS_ANYWHERE
| VM_FLAGS_RETURN_DATA_ADDR
| VM_FLAGS_RESILIENT_CODESIGN
,
61 TRUE
, // Copy semantics: changes to this memory by the target process will not be visible in this process
65 //Do this here to allow chaining of multiple embedded blocks with a single error out;
69 if (r
== KERN_SUCCESS
) {
70 // We got someting, call the block and then exit
71 block(reinterpret_cast<void *>(local_address
));
72 vm_deallocate(mach_task_self(), local_address
, local_size
);
75 if (!allow_truncation
) {
78 // We did not get something, but we are allowed to truncate and try again
79 uint64_t trunc_size
= ((remote_address
+ local_size
- 1) & PAGE_MASK
) + 1;
80 if (local_size
<= trunc_size
) {
81 //Even if we truncate it will be in the same page, time to accept defeat
84 local_size
-= trunc_size
;
90 void withRemoteObject(task_t task
, vm_address_t remote_address
, kern_return_t
*kr
, void (^block
)(T t
))
92 withRemoteBuffer(task
, remote_address
, sizeof(T
), false, kr
, ^(void *buffer
) {
93 block(*reinterpret_cast<T
*>(buffer
));
99 // Opaque object returned by _dyld_process_info_create()
102 struct __attribute__((visibility("hidden"))) dyld_process_info_base
{
103 static dyld_process_info_base
* make(task_t task
, const dyld_all_image_infos_64
& allImageInfo
, const dyld_image_info_64 imageArray
[], kern_return_t
* kr
);
104 static dyld_process_info_base
* makeSuspended(task_t task
, kern_return_t
* kr
);
106 uint32_t& retainCount() const { return _retainCount
; }
107 dyld_process_cache_info
* cacheInfo() const { return (dyld_process_cache_info
*)(((char*)this) + _cacheInfoOffset
); }
108 dyld_process_state_info
* stateInfo() const { return (dyld_process_state_info
*)(((char*)this) + _stateInfoOffset
); }
109 void forEachImage(void (^callback
)(uint64_t machHeaderAddress
, const uuid_t uuid
, const char* path
)) const;
110 void forEachSegment(uint64_t machHeaderAddress
, void (^callback
)(uint64_t segmentAddress
, uint64_t segmentSize
, const char* segmentName
)) const;
115 uint64_t loadAddress
;
117 uint32_t segmentStartIndex
;
118 uint32_t segmentsCount
;
127 dyld_process_info_base(unsigned imageCount
, size_t totalSize
);
128 void* operator new (size_t, void* buf
) { return buf
; }
130 static bool inCache(uint64_t addr
) { return (addr
> SHARED_REGION_BASE
) && (addr
< SHARED_REGION_BASE
+SHARED_REGION_SIZE
); }
131 kern_return_t
addImage(task_t task
, bool sameCacheAsThisProcess
, uint64_t imageAddress
, uint64_t imagePath
, const char* imagePathLocal
);
132 kern_return_t
addDyldImage(task_t task
, uint64_t dyldAddress
, uint64_t dyldPathAddress
, const char* localPath
);
134 bool invalid() { return ((char*)_stringRevBumpPtr
< (char*)_curSegment
); }
135 const char* copyPath(task_t task
, uint64_t pathAddr
, kern_return_t
* kr
);
136 const char* addString(const char*);
137 const char* copySegmentName(const char*);
139 void addInfoFromLoadCommands(const mach_header
* mh
, uint64_t addressInTask
, size_t size
);
141 void inspectLocalImageLoadCommands(uint64_t imageAddress
, void* func
);
142 kern_return_t
inspectRemoteImageLoadCommands(task_t task
, uint64_t imageAddress
, void* func
);
144 mutable uint32_t _retainCount
;
145 const uint32_t _cacheInfoOffset
;
146 const uint32_t _stateInfoOffset
;
147 const uint32_t _imageInfosOffset
;
148 const uint32_t _segmentInfosOffset
;
149 ImageInfo
* const _firstImage
;
150 ImageInfo
* _curImage
;
151 SegmentInfo
* const _firstSegment
;
152 SegmentInfo
* _curSegment
;
153 uint32_t _curSegmentIndex
;
154 char* _stringRevBumpPtr
;
156 // dyld_process_cache_info cacheInfo;
157 // dyld_process_state_info stateInfo;
158 // ImageInfo images[];
159 // SegmentInfo segments[];
163 dyld_process_info_base::dyld_process_info_base(unsigned imageCount
, size_t totalSize
)
164 : _retainCount(1), _cacheInfoOffset(sizeof(dyld_process_info_base
)),
165 _stateInfoOffset(sizeof(dyld_process_info_base
) + sizeof(dyld_process_cache_info
)),
166 _imageInfosOffset(sizeof(dyld_process_info_base
) + sizeof(dyld_process_cache_info
) + sizeof(dyld_process_state_info
)),
167 _segmentInfosOffset(sizeof(dyld_process_info_base
) + sizeof(dyld_process_cache_info
) + sizeof(dyld_process_state_info
) + imageCount
*sizeof(ImageInfo
)),
168 _firstImage((ImageInfo
*)(((uint8_t*)this) + _imageInfosOffset
)),
169 _curImage((ImageInfo
*)(((uint8_t*)this) + _imageInfosOffset
)),
170 _firstSegment((SegmentInfo
*)(((uint8_t*)this) + _segmentInfosOffset
)),
171 _curSegment((SegmentInfo
*)(((uint8_t*)this) + _segmentInfosOffset
)),
173 _stringRevBumpPtr((char*)(this)+totalSize
)
178 dyld_process_info_base
* dyld_process_info_base::make(task_t task
, const dyld_all_image_infos_64
& allImageInfo
, const dyld_image_info_64 imageArray
[], kern_return_t
* kr
)
180 // figure out how many path strings will need to be copied and their size
181 const dyld_all_image_infos
* myInfo
= _dyld_get_all_image_infos();
182 bool sameCacheAsThisProcess
= !allImageInfo
.processDetachedFromSharedRegion
183 && !myInfo
->processDetachedFromSharedRegion
184 && ((memcmp(myInfo
->sharedCacheUUID
, allImageInfo
.sharedCacheUUID
, 16) == 0)
185 && (myInfo
->sharedCacheSlide
== allImageInfo
.sharedCacheSlide
));
186 unsigned countOfPathsNeedingCopying
= 0;
187 if ( sameCacheAsThisProcess
) {
188 for (int i
=0; i
< allImageInfo
.infoArrayCount
; ++i
) {
189 if ( !inCache(imageArray
[i
].imageFilePath
) )
190 ++countOfPathsNeedingCopying
;
194 countOfPathsNeedingCopying
= allImageInfo
.infoArrayCount
+1;
196 unsigned imageCountWithDyld
= allImageInfo
.infoArrayCount
+1;
198 // allocate result object
199 size_t allocationSize
= sizeof(dyld_process_info_base
)
200 + sizeof(dyld_process_cache_info
)
201 + sizeof(dyld_process_state_info
)
202 + sizeof(ImageInfo
)*(imageCountWithDyld
)
203 + sizeof(SegmentInfo
)*imageCountWithDyld
*5
204 + countOfPathsNeedingCopying
*PATH_MAX
;
205 void* storage
= malloc(allocationSize
);
206 dyld_process_info_base
* obj
= new (storage
) dyld_process_info_base(imageCountWithDyld
, allocationSize
); // placement new()
209 dyld_process_cache_info
* cacheInfo
= obj
->cacheInfo();
210 memcpy(cacheInfo
->cacheUUID
, allImageInfo
.sharedCacheUUID
, 16);
211 cacheInfo
->cacheBaseAddress
= allImageInfo
.sharedCacheBaseAddress
;
212 cacheInfo
->privateCache
= allImageInfo
.processDetachedFromSharedRegion
;
213 // if no cache is used, allImageInfo has all zeros for cache UUID
214 cacheInfo
->noCache
= true;
215 for (int i
=0; i
< 16; ++i
) {
216 if ( cacheInfo
->cacheUUID
[i
] != 0 ) {
217 cacheInfo
->noCache
= false;
221 dyld_process_state_info
* stateInfo
= obj
->stateInfo();
222 stateInfo
->timestamp
= allImageInfo
.infoArrayChangeTimestamp
;
223 stateInfo
->imageCount
= imageCountWithDyld
;
224 stateInfo
->initialImageCount
= (uint32_t)(allImageInfo
.initialImageCount
+1);
225 if ( allImageInfo
.infoArray
!= 0 )
226 stateInfo
->dyldState
= dyld_process_state_dyld_initialized
;
227 if ( allImageInfo
.libSystemInitialized
!= 0 ) {
228 stateInfo
->dyldState
= dyld_process_state_libSystem_initialized
;
229 if ( allImageInfo
.initialImageCount
!= allImageInfo
.infoArrayCount
)
230 stateInfo
->dyldState
= dyld_process_state_program_running
;
232 if ( allImageInfo
.errorMessage
!= 0 )
233 stateInfo
->dyldState
= allImageInfo
.terminationFlags
? dyld_process_state_terminated_before_inits
: dyld_process_state_dyld_terminated
;
235 // fill in info for dyld
236 if ( allImageInfo
.dyldPath
!= 0 ) {
237 if ( kern_return_t r
= obj
->addDyldImage(task
, allImageInfo
.dyldImageLoadAddress
, allImageInfo
.dyldPath
, NULL
) ) {
244 // fill in info for each image
245 for (uint32_t i
=0; i
< allImageInfo
.infoArrayCount
; ++i
) {
246 if ( kern_return_t r
= obj
->addImage(task
, sameCacheAsThisProcess
, imageArray
[i
].imageLoadAddress
, imageArray
[i
].imageFilePath
, NULL
) ) {
253 // sanity check internal data did not overflow
254 if ( obj
->invalid() )
264 dyld_process_info_base
* dyld_process_info_base::makeSuspended(task_t task
, kern_return_t
* kr
)
267 kern_return_t result
= pid_for_task(task
, &pid
);
268 if ( result
!= KERN_SUCCESS
) {
274 __block
unsigned imageCount
= 0; // main executable and dyld
275 __block
uint64_t mainExecutableAddress
= 0;
276 __block
uint64_t dyldAddress
= 0;
277 char dyldPathBuffer
[PATH_MAX
+1];
278 char mainExecutablePathBuffer
[PATH_MAX
+1];
279 __block
char * dyldPath
= &dyldPathBuffer
[0];
280 __block
char * mainExecutablePath
= &mainExecutablePathBuffer
[0];
282 for (mach_vm_address_t address
= 0; ; address
+= size
) {
283 vm_region_basic_info_data_64_t info
;
284 mach_port_t objectName
;
285 unsigned int infoCount
= VM_REGION_BASIC_INFO_COUNT_64
;
286 result
= mach_vm_region(task
, &address
, &size
, VM_REGION_BASIC_INFO
,
287 (vm_region_info_t
)&info
, &infoCount
, &objectName
);
288 if ( result
!= KERN_SUCCESS
)
290 if ( info
.protection
!= (VM_PROT_READ
|VM_PROT_EXECUTE
) )
292 // read start of vm region to verify it is a mach header
293 withRemoteObject(task
, address
, NULL
, ^(mach_header_64 mhBuffer
){
294 if ( (mhBuffer
.magic
!= MH_MAGIC
) && (mhBuffer
.magic
!= MH_MAGIC_64
) )
296 // now know the region is the start of a mach-o file
297 if ( mhBuffer
.filetype
== MH_EXECUTE
) {
298 mainExecutableAddress
= address
;
299 int len
= proc_regionfilename(pid
, mainExecutableAddress
, mainExecutablePath
, PATH_MAX
);
301 mainExecutablePath
[len
] = '\0';
305 else if ( mhBuffer
.filetype
== MH_DYLINKER
) {
306 dyldAddress
= address
;
307 int len
= proc_regionfilename(pid
, dyldAddress
, dyldPath
, PATH_MAX
);
309 dyldPath
[len
] = '\0';
314 //fprintf(stderr, "vm region: addr=0x%llX, size=0x%llX, prot=0x%X\n", (uint64_t)address, (uint64_t)size, info.protection);
316 //fprintf(stderr, "dyld: addr=0x%llX, path=%s\n", dyldAddress, dyldPathBuffer);
317 //fprintf(stderr, "app: addr=0x%llX, path=%s\n", mainExecutableAddress, mainExecutablePathBuffer);
319 // allocate result object
320 size_t allocationSize
= sizeof(dyld_process_info_base
)
321 + sizeof(dyld_process_cache_info
)
322 + sizeof(dyld_process_state_info
)
323 + sizeof(ImageInfo
)*(imageCount
)
324 + sizeof(SegmentInfo
)*imageCount
*5
325 + imageCount
*PATH_MAX
;
326 void* storage
= malloc(allocationSize
);
327 dyld_process_info_base
* obj
= new (storage
) dyld_process_info_base(imageCount
, allocationSize
); // placement new()
330 dyld_process_cache_info
* cacheInfo
= obj
->cacheInfo();
331 bzero(cacheInfo
->cacheUUID
, 16);
332 cacheInfo
->cacheBaseAddress
= 0;
333 cacheInfo
->noCache
= true;
334 cacheInfo
->privateCache
= false;
336 dyld_process_state_info
* stateInfo
= obj
->stateInfo();
337 stateInfo
->timestamp
= 0;
338 stateInfo
->imageCount
= imageCount
;
339 stateInfo
->initialImageCount
= imageCount
;
340 stateInfo
->dyldState
= dyld_process_state_not_started
;
342 // fill in info for dyld
343 if ( dyldAddress
!= 0 ) {
344 if ( kern_return_t r
= obj
->addDyldImage(task
, dyldAddress
, 0, dyldPath
) ) {
352 // fill in info for each image
353 if ( mainExecutableAddress
!= 0 ) {
354 if ( kern_return_t r
= obj
->addImage(task
, false, mainExecutableAddress
, 0, mainExecutablePath
) ) {
367 const char* dyld_process_info_base::addString(const char* str
)
369 size_t len
= strlen(str
) + 1;
370 _stringRevBumpPtr
-= len
;
371 strcpy(_stringRevBumpPtr
, str
);
372 return _stringRevBumpPtr
;
375 const char* dyld_process_info_base::copyPath(task_t task
, uint64_t stringAddressInTask
, kern_return_t
* kr
)
377 __block
const char* retval
= NULL
;
378 withRemoteBuffer(task
, stringAddressInTask
, PATH_MAX
+8, true, kr
, ^(void *buffer
) {
379 retval
= addString(static_cast<const char *>(buffer
));
384 kern_return_t
dyld_process_info_base::addImage(task_t task
, bool sameCacheAsThisProcess
, uint64_t imageAddress
, uint64_t imagePath
, const char* imagePathLocal
)
386 _curImage
->loadAddress
= imageAddress
;
387 _curImage
->segmentStartIndex
= _curSegmentIndex
;
388 if ( imagePathLocal
!= NULL
) {
389 _curImage
->path
= addString(imagePathLocal
);
391 else if ( sameCacheAsThisProcess
&& inCache(imagePath
) ) {
392 _curImage
->path
= (const char*)imagePath
;
396 _curImage
->path
= copyPath(task
, imagePath
, &kr
);
400 if ( sameCacheAsThisProcess
&& inCache(imageAddress
) ) {
401 addInfoFromLoadCommands((mach_header
*)imageAddress
, imageAddress
, 32*1024);
404 __block kern_return_t kr
= KERN_SUCCESS
;
405 withRemoteObject(task
, imageAddress
, &kr
, ^(mach_header_64 mhBuffer
) {
406 size_t headerPagesSize
= sizeof(mach_header_64
) + mhBuffer
.sizeofcmds
;
407 withRemoteBuffer(task
, imageAddress
, headerPagesSize
, false, &kr
, ^(void * buffer
) {
408 addInfoFromLoadCommands((mach_header
*)buffer
, imageAddress
, headerPagesSize
);
411 if (kr
!= KERN_SUCCESS
) {
415 _curImage
->segmentsCount
= _curSegmentIndex
- _curImage
->segmentStartIndex
;
421 kern_return_t
dyld_process_info_base::addDyldImage(task_t task
, uint64_t dyldAddress
, uint64_t dyldPathAddress
, const char* localPath
)
423 __block kern_return_t kr
= KERN_SUCCESS
;
424 _curImage
->loadAddress
= dyldAddress
;
425 _curImage
->segmentStartIndex
= _curSegmentIndex
;
426 if ( localPath
!= NULL
) {
427 _curImage
->path
= addString(localPath
);
430 _curImage
->path
= copyPath(task
, dyldPathAddress
, &kr
);
435 withRemoteObject(task
, dyldAddress
, &kr
, ^(mach_header_64 mhBuffer
) {
436 size_t headerPagesSize
= sizeof(mach_header_64
) + mhBuffer
.sizeofcmds
;
437 withRemoteBuffer(task
, dyldAddress
, headerPagesSize
, false, &kr
, ^(void * buffer
) {
438 addInfoFromLoadCommands((mach_header
*)buffer
, dyldAddress
, headerPagesSize
);
441 if (kr
!= KERN_SUCCESS
) {
445 _curImage
->segmentsCount
= _curSegmentIndex
- _curImage
->segmentStartIndex
;
451 void dyld_process_info_base::addInfoFromLoadCommands(const mach_header
* mh
, uint64_t addressInTask
, size_t size
)
453 const load_command
* startCmds
= NULL
;
454 if ( mh
->magic
== MH_MAGIC_64
)
455 startCmds
= (load_command
*)((char *)mh
+ sizeof(mach_header_64
));
456 else if ( mh
->magic
== MH_MAGIC
)
457 startCmds
= (load_command
*)((char *)mh
+ sizeof(mach_header
));
459 return; // not a mach-o file, or wrong endianness
461 const load_command
* const cmdsEnd
= (load_command
*)((char*)startCmds
+ mh
->sizeofcmds
);
462 const load_command
* cmd
= startCmds
;
463 for(uint32_t i
= 0; i
< mh
->ncmds
; ++i
) {
464 const load_command
* nextCmd
= (load_command
*)((char *)cmd
+ cmd
->cmdsize
);
465 if ( (cmd
->cmdsize
< 8) || (nextCmd
> cmdsEnd
) || (nextCmd
< startCmds
) ) {
466 return; // malformed load command
468 if ( cmd
->cmd
== LC_UUID
) {
469 const uuid_command
* uuidCmd
= (uuid_command
*)cmd
;
470 memcpy(_curImage
->uuid
, uuidCmd
->uuid
, 16);
472 else if ( cmd
->cmd
== LC_SEGMENT
) {
473 const segment_command
* segCmd
= (segment_command
*)cmd
;
474 _curSegment
->name
= copySegmentName(segCmd
->segname
);
475 _curSegment
->addr
= segCmd
->vmaddr
;
476 _curSegment
->size
= segCmd
->vmsize
;
480 else if ( cmd
->cmd
== LC_SEGMENT_64
) {
481 const segment_command_64
* segCmd
= (segment_command_64
*)cmd
;
482 _curSegment
->name
= copySegmentName(segCmd
->segname
);
483 _curSegment
->addr
= segCmd
->vmaddr
;
484 _curSegment
->size
= segCmd
->vmsize
;
492 const char* dyld_process_info_base::copySegmentName(const char* name
)
494 // don't copy names of standard segments into string pool
495 static const char* stdSegNames
[] = {"__TEXT", "__DATA", "__LINKEDIT", "__DATA_DIRTY", "__DATA_CONST", "__OBJC", NULL
};
496 for (const char** s
=stdSegNames
; *s
!= NULL
; ++s
) {
497 if ( strcmp(name
, *s
) == 0 )
500 // copy custom segment names into string pool
501 return addString(name
);
504 void dyld_process_info_base::forEachImage(void (^callback
)(uint64_t machHeaderAddress
, const uuid_t uuid
, const char* path
)) const
506 for (const ImageInfo
* p
= _firstImage
; p
< _curImage
; ++p
) {
507 callback(p
->loadAddress
, p
->uuid
, p
->path
);
511 void dyld_process_info_base::forEachSegment(uint64_t machHeaderAddress
, void (^callback
)(uint64_t segmentAddress
, uint64_t segmentSize
, const char* segmentName
)) const
513 for (const ImageInfo
* p
= _firstImage
; p
< _curImage
; ++p
) {
514 if ( p
->loadAddress
== machHeaderAddress
) {
516 for (int i
=0; i
< p
->segmentsCount
; ++i
) {
517 const SegmentInfo
* seg
= &_firstSegment
[p
->segmentStartIndex
+i
];
518 if ( strcmp(seg
->name
, "__TEXT") == 0 ) {
519 slide
= machHeaderAddress
- seg
->addr
;
523 for (int i
=0; i
< p
->segmentsCount
; ++i
) {
524 const SegmentInfo
* seg
= &_firstSegment
[p
->segmentStartIndex
+i
];
525 callback(seg
->addr
+ slide
, seg
->size
, seg
->name
);
536 // Implementation that works with existing dyld data structures
537 static dyld_process_info
_dyld_process_info_create_inner(task_t task
, uint64_t timestamp
, kern_return_t
* kr
)
542 task_dyld_info_data_t task_dyld_info
;
543 mach_msg_type_number_t count
= TASK_DYLD_INFO_COUNT
;
544 if ( kern_return_t r
= task_info(task
, TASK_DYLD_INFO
, (task_info_t
)&task_dyld_info
, &count
) ) {
550 //The kernel will return MACH_VM_MIN_ADDRESS for an executable that has not had dyld loaded
551 if (task_dyld_info
.all_image_info_addr
== MACH_VM_MIN_ADDRESS
)
554 if ( task_dyld_info
.all_image_info_size
> sizeof(dyld_all_image_infos_64
) )
557 // read all_image_infos struct
558 dyld_all_image_infos_64 allImageInfo64
;
559 mach_vm_size_t readSize
= task_dyld_info
.all_image_info_size
;
560 if ( kern_return_t r
= mach_vm_read_overwrite(task
, task_dyld_info
.all_image_info_addr
, task_dyld_info
.all_image_info_size
, (vm_address_t
)&allImageInfo64
, &readSize
) ) {
565 if ( allImageInfo64
.infoArrayCount
== 0 ) {
566 // could be task was launch suspended or still launching, wait a moment to see
567 usleep(1000 * 50); // 50ms
568 if ( kern_return_t r
= mach_vm_read_overwrite(task
, task_dyld_info
.all_image_info_addr
, task_dyld_info
.all_image_info_size
, (vm_address_t
)&allImageInfo64
, &readSize
) ) {
573 // if infoArrayCount is still zero, then target was most likely launched suspended
574 if ( allImageInfo64
.infoArrayCount
== 0 )
575 return dyld_process_info_base::makeSuspended(task
, kr
);
578 // bail out of dyld is too old
579 if ( allImageInfo64
.version
< 15 ) {
581 *kr
= KERN_INVALID_HOST
;
585 // normalize by expanding 32-bit all_image_infos into 64-bit one
586 uint32_t imageCount
= allImageInfo64
.infoArrayCount
;
587 size_t imageArraySize
= imageCount
* sizeof(dyld_image_info_64
);
588 if ( task_dyld_info
.all_image_info_format
== TASK_DYLD_ALL_IMAGE_INFO_32
) {
589 const dyld_all_image_infos_32
* allImageInfo32
= (dyld_all_image_infos_32
*)&allImageInfo64
;
590 dyld_all_image_infos_64 info64
;
591 bzero(&info64
, sizeof(info64
));
592 info64
.version
= allImageInfo32
->version
;
593 info64
.infoArrayCount
= allImageInfo32
->infoArrayCount
;
594 info64
.infoArray
= allImageInfo32
->infoArray
;
595 info64
.processDetachedFromSharedRegion
= allImageInfo32
->processDetachedFromSharedRegion
;
596 info64
.libSystemInitialized
= allImageInfo32
->libSystemInitialized
;
597 info64
.dyldImageLoadAddress
= allImageInfo32
->dyldImageLoadAddress
;
598 info64
.initialImageCount
= allImageInfo32
->initialImageCount
;
599 info64
.uuidArrayCount
= allImageInfo32
->uuidArrayCount
;
600 info64
.uuidArray
= allImageInfo32
->uuidArray
;
601 info64
.dyldAllImageInfosAddress
= allImageInfo32
->dyldAllImageInfosAddress
;
602 info64
.sharedCacheSlide
= allImageInfo32
->sharedCacheSlide
;
603 info64
.infoArrayChangeTimestamp
= allImageInfo32
->infoArrayChangeTimestamp
;
604 info64
.sharedCacheBaseAddress
= allImageInfo32
->sharedCacheBaseAddress
;
605 info64
.dyldPath
= allImageInfo32
->dyldPath
;
606 memcpy((void*)(info64
.sharedCacheUUID
), (void*)(allImageInfo32
->sharedCacheUUID
), 16);
607 allImageInfo64
= info64
;
608 imageCount
= allImageInfo64
.infoArrayCount
;
609 imageArraySize
= imageCount
* sizeof(dyld_image_info_32
);
612 // don't do any (more) work if target process's dyld timestamp has not changed since previous query
613 if ( (timestamp
!= 0) && (timestamp
== allImageInfo64
.infoArrayChangeTimestamp
) ) {
619 // For the moment we are going to truncate any image list longer than 8192 because some programs do
620 // terrible things that corrupt their own image lists and we need to stop clients from crashing
621 // reading them. We can try to do something more advanced in the future. rdar://27446361
622 imageCount
= MIN(imageCount
, 8192);
625 if ( allImageInfo64
.infoArray
== 0 ) {
626 // dyld is in middle of updating image list, try again
629 dyld_image_info_64 imageArray64
[imageCount
];
630 if ( kern_return_t r
= mach_vm_read_overwrite(task
, allImageInfo64
.infoArray
, imageArraySize
, (vm_address_t
)&imageArray64
, &readSize
) ) {
631 // if image array moved, try whole thing again
637 // normalize by expanding 32-bit image_infos into 64-bit ones
638 if ( task_dyld_info
.all_image_info_format
== TASK_DYLD_ALL_IMAGE_INFO_32
) {
639 const dyld_image_info_32
* imageArray32
= (dyld_image_info_32
*)&imageArray64
;
640 dyld_image_info_64 tempArray
[imageCount
];
641 for (uint32_t i
=0; i
< imageCount
; ++i
) {
642 tempArray
[i
].imageLoadAddress
= imageArray32
[i
].imageLoadAddress
;
643 tempArray
[i
].imageFilePath
= imageArray32
[i
].imageFilePath
;
644 tempArray
[i
].imageFileModDate
= imageArray32
[i
].imageFileModDate
;
646 memcpy(imageArray64
, tempArray
, sizeof(dyld_image_info_64
)*imageCount
);
649 // create object based on local copy of all image infos and image array
650 dyld_process_info result
= dyld_process_info_base::make(task
, allImageInfo64
, imageArray64
, kr
);
652 // verify nothing has changed by re-reading all_image_infos struct and checking timestamp
653 if ( result
!= NULL
) {
654 dyld_all_image_infos_64 allImageInfo64again
;
655 readSize
= task_dyld_info
.all_image_info_size
;
656 if ( kern_return_t r
= mach_vm_read_overwrite(task
, task_dyld_info
.all_image_info_addr
, task_dyld_info
.all_image_info_size
, (vm_address_t
)&allImageInfo64again
, &readSize
) ) {
662 uint64_t doneTimeStamp
= allImageInfo64again
.infoArrayChangeTimestamp
;
663 if ( task_dyld_info
.all_image_info_format
== TASK_DYLD_ALL_IMAGE_INFO_32
) {
664 const dyld_all_image_infos_32
* allImageInfo32
= (dyld_all_image_infos_32
*)&allImageInfo64again
;
665 doneTimeStamp
= allImageInfo32
->infoArrayChangeTimestamp
;
667 if ( allImageInfo64
.infoArrayChangeTimestamp
!= doneTimeStamp
) {
668 // image list has changed since we started reading it
669 // throw out what we have and start over
679 dyld_process_info
_dyld_process_info_create(task_t task
, uint64_t timestamp
, kern_return_t
* kr
)
681 // Other process may be loading and unloading as we read its memory, which can cause a read failure
682 // <rdar://problem30067343&29567679> Retry if something fails
683 for (int i
=0; i
< 100; ++i
) {
684 if ( dyld_process_info result
= _dyld_process_info_create_inner( task
, timestamp
, kr
) )
691 void _dyld_process_info_get_state(dyld_process_info info
, dyld_process_state_info
* stateInfo
)
693 *stateInfo
= *info
->stateInfo();
696 void _dyld_process_info_get_cache(dyld_process_info info
, dyld_process_cache_info
* cacheInfo
)
698 *cacheInfo
= *info
->cacheInfo();
701 void _dyld_process_info_retain(dyld_process_info info
)
703 info
->retainCount() += 1;
706 void _dyld_process_info_release(dyld_process_info info
)
708 info
->retainCount() -= 1;
709 if ( info
->retainCount() == 0 )
713 void _dyld_process_info_for_each_image(dyld_process_info info
, void (^callback
)(uint64_t machHeaderAddress
, const uuid_t uuid
, const char* path
))
715 info
->forEachImage(callback
);
719 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
))
721 info
->forEachSegment(machHeaderAddress
, callback
);