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 // Opaque object returned by _dyld_process_info_create()
46 struct __attribute__((visibility("hidden"))) dyld_process_info_base
{
47 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
);
48 static dyld_process_info_base
* makeSuspended(task_t task
, kern_return_t
* kr
);
50 uint32_t& retainCount() const { return _retainCount
; }
51 dyld_process_cache_info
* cacheInfo() const { return (dyld_process_cache_info
*)(((char*)this) + _cacheInfoOffset
); }
52 dyld_process_state_info
* stateInfo() const { return (dyld_process_state_info
*)(((char*)this) + _stateInfoOffset
); }
53 void forEachImage(void (^callback
)(uint64_t machHeaderAddress
, const uuid_t uuid
, const char* path
)) const;
54 void forEachSegment(uint64_t machHeaderAddress
, void (^callback
)(uint64_t segmentAddress
, uint64_t segmentSize
, const char* segmentName
)) const;
61 uint32_t segmentStartIndex
;
62 uint32_t segmentsCount
;
71 dyld_process_info_base(unsigned imageCount
, size_t totalSize
);
72 void* operator new (size_t, void* buf
) { return buf
; }
74 static bool inCache(uint64_t addr
) { return (addr
> SHARED_REGION_BASE
) && (addr
< SHARED_REGION_BASE
+SHARED_REGION_SIZE
); }
75 kern_return_t
addImage(task_t task
, bool sameCacheAsThisProcess
, uint64_t imageAddress
, uint64_t imagePath
, const char* imagePathLocal
);
76 kern_return_t
addDyldImage(task_t task
, uint64_t dyldAddress
, uint64_t dyldPathAddress
, const char* localPath
);
78 bool invalid() { return ((char*)_stringRevBumpPtr
< (char*)_curSegment
); }
79 const char* copyPath(task_t task
, uint64_t pathAddr
, kern_return_t
* kr
);
80 const char* addString(const char*);
81 const char* copySegmentName(const char*);
83 void addInfoFromLoadCommands(const mach_header
* mh
, uint64_t addressInTask
, size_t size
);
85 void inspectLocalImageLoadCommands(uint64_t imageAddress
, void* func
);
86 kern_return_t
inspectRemoteImageLoadCommands(task_t task
, uint64_t imageAddress
, void* func
);
88 mutable uint32_t _retainCount
;
89 const uint32_t _cacheInfoOffset
;
90 const uint32_t _stateInfoOffset
;
91 const uint32_t _imageInfosOffset
;
92 const uint32_t _segmentInfosOffset
;
93 ImageInfo
* const _firstImage
;
95 SegmentInfo
* const _firstSegment
;
96 SegmentInfo
* _curSegment
;
97 uint32_t _curSegmentIndex
;
98 char* _stringRevBumpPtr
;
100 // dyld_process_cache_info cacheInfo;
101 // dyld_process_state_info stateInfo;
102 // ImageInfo images[];
103 // SegmentInfo segments[];
107 dyld_process_info_base::dyld_process_info_base(unsigned imageCount
, size_t totalSize
)
108 : _retainCount(1), _cacheInfoOffset(sizeof(dyld_process_info_base
)),
109 _stateInfoOffset(sizeof(dyld_process_info_base
) + sizeof(dyld_process_cache_info
)),
110 _imageInfosOffset(sizeof(dyld_process_info_base
) + sizeof(dyld_process_cache_info
) + sizeof(dyld_process_state_info
)),
111 _segmentInfosOffset(sizeof(dyld_process_info_base
) + sizeof(dyld_process_cache_info
) + sizeof(dyld_process_state_info
) + imageCount
*sizeof(ImageInfo
)),
112 _firstImage((ImageInfo
*)(((uint8_t*)this) + _imageInfosOffset
)),
113 _curImage((ImageInfo
*)(((uint8_t*)this) + _imageInfosOffset
)),
114 _firstSegment((SegmentInfo
*)(((uint8_t*)this) + _segmentInfosOffset
)),
115 _curSegment((SegmentInfo
*)(((uint8_t*)this) + _segmentInfosOffset
)),
117 _stringRevBumpPtr((char*)(this)+totalSize
)
122 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
)
124 // figure out how many path strings will need to be copied and their size
125 const dyld_all_image_infos
* myInfo
= _dyld_get_all_image_infos();
126 bool sameCacheAsThisProcess
= ((memcmp(myInfo
->sharedCacheUUID
, allImageInfo
.sharedCacheUUID
, 16) == 0) && (myInfo
->sharedCacheSlide
== allImageInfo
.sharedCacheSlide
));
127 unsigned countOfPathsNeedingCopying
= 0;
128 if ( sameCacheAsThisProcess
) {
129 for (int i
=0; i
< allImageInfo
.infoArrayCount
; ++i
) {
130 if ( !inCache(imageArray
[i
].imageFilePath
) )
131 ++countOfPathsNeedingCopying
;
135 countOfPathsNeedingCopying
= allImageInfo
.infoArrayCount
+1;
137 unsigned imageCountWithDyld
= allImageInfo
.infoArrayCount
+1;
139 // allocate result object
140 size_t allocationSize
= sizeof(dyld_process_info_base
)
141 + sizeof(dyld_process_cache_info
)
142 + sizeof(dyld_process_state_info
)
143 + sizeof(ImageInfo
)*(imageCountWithDyld
)
144 + sizeof(SegmentInfo
)*imageCountWithDyld
*5
145 + countOfPathsNeedingCopying
*PATH_MAX
;
146 void* storage
= malloc(allocationSize
);
147 dyld_process_info_base
* obj
= new (storage
) dyld_process_info_base(imageCountWithDyld
, allocationSize
); // placement new()
150 dyld_process_cache_info
* cacheInfo
= obj
->cacheInfo();
151 memcpy(cacheInfo
->cacheUUID
, allImageInfo
.sharedCacheUUID
, 16);
152 cacheInfo
->cacheBaseAddress
= allImageInfo
.sharedCacheBaseAddress
;
153 cacheInfo
->privateCache
= allImageInfo
.processDetachedFromSharedRegion
;
154 // if no cache is used, allImageInfo has all zeros for cache UUID
155 cacheInfo
->noCache
= true;
156 for (int i
=0; i
< 16; ++i
) {
157 if ( cacheInfo
->cacheUUID
[i
] != 0 ) {
158 cacheInfo
->noCache
= false;
162 dyld_process_state_info
* stateInfo
= obj
->stateInfo();
163 stateInfo
->timestamp
= allImageInfo
.infoArrayChangeTimestamp
;
164 stateInfo
->imageCount
= imageCountWithDyld
;
165 stateInfo
->initialImageCount
= (uint32_t)(allImageInfo
.initialImageCount
+1);
166 if ( allImageInfo
.infoArray
!= 0 )
167 stateInfo
->dyldState
= dyld_process_state_dyld_initialized
;
168 if ( allImageInfo
.libSystemInitialized
!= 0 ) {
169 stateInfo
->dyldState
= dyld_process_state_libSystem_initialized
;
170 if ( allImageInfo
.initialImageCount
!= allImageInfo
.infoArrayCount
)
171 stateInfo
->dyldState
= dyld_process_state_program_running
;
173 if ( allImageInfo
.errorMessage
!= 0 )
174 stateInfo
->dyldState
= allImageInfo
.terminationFlags
? dyld_process_state_terminated_before_inits
: dyld_process_state_dyld_terminated
;
176 // fill in info for dyld
177 if ( allImageInfo
.dyldPath
!= 0 ) {
178 if ( kern_return_t r
= obj
->addDyldImage(task
, allImageInfo
.dyldImageLoadAddress
, allImageInfo
.dyldPath
, NULL
) ) {
185 // fill in info for each image
186 for (uint32_t i
=0; i
< allImageInfo
.infoArrayCount
; ++i
) {
187 if ( kern_return_t r
= obj
->addImage(task
, sameCacheAsThisProcess
, imageArray
[i
].imageLoadAddress
, imageArray
[i
].imageFilePath
, NULL
) ) {
194 // sanity check internal data did not overflow
195 if ( obj
->invalid() )
205 dyld_process_info_base
* dyld_process_info_base::makeSuspended(task_t task
, kern_return_t
* kr
)
208 kern_return_t result
= pid_for_task(task
, &pid
);
209 if ( result
!= KERN_SUCCESS
) {
215 unsigned imageCount
= 0; // main executable and dyld
216 uint64_t mainExecutableAddress
= 0;
217 uint64_t dyldAddress
= 0;
218 char dyldPathBuffer
[PATH_MAX
+1];
219 char mainExecutablePathBuffer
[PATH_MAX
+1];
221 for (mach_vm_address_t address
= 0; ; address
+= size
) {
222 vm_region_basic_info_data_64_t info
;
223 mach_port_t objectName
;
224 unsigned int infoCount
= VM_REGION_BASIC_INFO_COUNT_64
;
225 result
= mach_vm_region(task
, &address
, &size
, VM_REGION_BASIC_INFO
,
226 (vm_region_info_t
)&info
, &infoCount
, &objectName
);
227 if ( result
!= KERN_SUCCESS
)
229 if ( info
.protection
== (VM_PROT_READ
|VM_PROT_EXECUTE
) ) {
230 // read start of vm region to verify it is a mach header
231 mach_vm_size_t readSize
= sizeof(mach_header_64
);
232 mach_header_64 mhBuffer
;
233 if ( mach_vm_read_overwrite(task
, address
, sizeof(mach_header_64
), (vm_address_t
)&mhBuffer
, &readSize
) != KERN_SUCCESS
)
235 if ( (mhBuffer
.magic
!= MH_MAGIC
) && (mhBuffer
.magic
!= MH_MAGIC_64
) )
237 // now know the region is the start of a mach-o file
238 if ( mhBuffer
.filetype
== MH_EXECUTE
) {
239 mainExecutableAddress
= address
;
240 int len
= proc_regionfilename(pid
, mainExecutableAddress
, mainExecutablePathBuffer
, PATH_MAX
);
242 mainExecutablePathBuffer
[len
] = '\0';
245 else if ( mhBuffer
.filetype
== MH_DYLINKER
) {
246 dyldAddress
= address
;
247 int len
= proc_regionfilename(pid
, dyldAddress
, dyldPathBuffer
, PATH_MAX
);
249 dyldPathBuffer
[len
] = '\0';
252 //fprintf(stderr, "vm region: addr=0x%llX, size=0x%llX, prot=0x%X\n", (uint64_t)address, (uint64_t)size, info.protection);
255 //fprintf(stderr, "dyld: addr=0x%llX, path=%s\n", dyldAddress, dyldPathBuffer);
256 //fprintf(stderr, "app: addr=0x%llX, path=%s\n", mainExecutableAddress, mainExecutablePathBuffer);
258 // allocate result object
259 size_t allocationSize
= sizeof(dyld_process_info_base
)
260 + sizeof(dyld_process_cache_info
)
261 + sizeof(dyld_process_state_info
)
262 + sizeof(ImageInfo
)*(imageCount
)
263 + sizeof(SegmentInfo
)*imageCount
*5
264 + imageCount
*PATH_MAX
;
265 void* storage
= malloc(allocationSize
);
266 dyld_process_info_base
* obj
= new (storage
) dyld_process_info_base(imageCount
, allocationSize
); // placement new()
269 dyld_process_cache_info
* cacheInfo
= obj
->cacheInfo();
270 bzero(cacheInfo
->cacheUUID
, 16);
271 cacheInfo
->cacheBaseAddress
= 0;
272 cacheInfo
->noCache
= true;
273 cacheInfo
->privateCache
= false;
275 dyld_process_state_info
* stateInfo
= obj
->stateInfo();
276 stateInfo
->timestamp
= 0;
277 stateInfo
->imageCount
= imageCount
;
278 stateInfo
->initialImageCount
= imageCount
;
279 stateInfo
->dyldState
= dyld_process_state_not_started
;
281 // fill in info for dyld
282 if ( dyldAddress
!= 0 ) {
283 if ( kern_return_t r
= obj
->addDyldImage(task
, dyldAddress
, 0, dyldPathBuffer
) ) {
291 // fill in info for each image
292 if ( mainExecutableAddress
!= 0 ) {
293 if ( kern_return_t r
= obj
->addImage(task
, false, mainExecutableAddress
, 0, mainExecutablePathBuffer
) ) {
306 const char* dyld_process_info_base::addString(const char* str
)
308 size_t len
= strlen(str
) + 1;
309 _stringRevBumpPtr
-= len
;
310 strcpy(_stringRevBumpPtr
, str
);
311 return _stringRevBumpPtr
;
314 const char* dyld_process_info_base::copyPath(task_t task
, uint64_t stringAddressInTask
, kern_return_t
* kr
)
316 char temp
[PATH_MAX
+8]; // +8 is to allow '\0' at temp[PATH_MAX]
317 mach_vm_size_t readSize
= PATH_MAX
;
318 if ( ((stringAddressInTask
& 0xFFF) + PATH_MAX
) < 4096 ) {
319 // string fits within page, only one vm_read needed
320 if ( kern_return_t r
= mach_vm_read_overwrite(task
, stringAddressInTask
, PATH_MAX
, (vm_address_t
)&temp
, &readSize
) ) {
327 // string may cross page boundary, split into two reads
328 size_t firstLen
= 4096 - (stringAddressInTask
& 0xFFF);
330 if ( kern_return_t r
= mach_vm_read_overwrite(task
, stringAddressInTask
, firstLen
, (vm_address_t
)&temp
, &readSize
) ) {
335 temp
[firstLen
] = '\0';
336 if ( strlen(temp
) >= firstLen
) {
337 readSize
= PATH_MAX
-firstLen
;
338 if ( kern_return_t r
= mach_vm_read_overwrite(task
, stringAddressInTask
+firstLen
, PATH_MAX
-firstLen
, (vm_address_t
)&temp
+firstLen
, &readSize
) ) {
342 temp
[PATH_MAX
] = '\0'; // truncate any string that is too long
348 return addString(temp
);
352 kern_return_t
dyld_process_info_base::addImage(task_t task
, bool sameCacheAsThisProcess
, uint64_t imageAddress
, uint64_t imagePath
, const char* imagePathLocal
)
354 _curImage
->loadAddress
= imageAddress
;
355 _curImage
->segmentStartIndex
= _curSegmentIndex
;
356 if ( imagePathLocal
!= NULL
) {
357 _curImage
->path
= addString(imagePathLocal
);
359 else if ( sameCacheAsThisProcess
&& inCache(imagePath
) ) {
360 _curImage
->path
= (const char*)imagePath
;
364 _curImage
->path
= copyPath(task
, imagePath
, &kr
);
368 if ( sameCacheAsThisProcess
&& inCache(imageAddress
) ) {
369 addInfoFromLoadCommands((mach_header
*)imageAddress
, imageAddress
, 32*1024);
372 mach_vm_size_t readSize
= sizeof(mach_header_64
);
373 mach_header_64 mhBuffer
;
374 if ( kern_return_t r
= mach_vm_read_overwrite(task
, imageAddress
, sizeof(mach_header_64
), (vm_address_t
)&mhBuffer
, &readSize
) ) {
377 size_t headerPagesSize
= (sizeof(mach_header_64
) + mhBuffer
.sizeofcmds
+ 4095) & (-4096);
378 vm_address_t localCopyBuffer
;
379 unsigned int localCopyBufferSize
;
380 if ( kern_return_t r
= mach_vm_read(task
, imageAddress
, headerPagesSize
, &localCopyBuffer
, &localCopyBufferSize
) ) {
383 addInfoFromLoadCommands((mach_header
*)localCopyBuffer
, imageAddress
, localCopyBufferSize
);
384 vm_deallocate(mach_task_self(), localCopyBuffer
, localCopyBufferSize
);
386 _curImage
->segmentsCount
= _curSegmentIndex
- _curImage
->segmentStartIndex
;
392 kern_return_t
dyld_process_info_base::addDyldImage(task_t task
, uint64_t dyldAddress
, uint64_t dyldPathAddress
, const char* localPath
)
395 _curImage
->loadAddress
= dyldAddress
;
396 _curImage
->segmentStartIndex
= _curSegmentIndex
;
397 if ( localPath
!= NULL
) {
398 _curImage
->path
= addString(localPath
);
401 _curImage
->path
= copyPath(task
, dyldPathAddress
, &kr
);
406 mach_vm_size_t readSize
= sizeof(mach_header_64
);
407 mach_header_64 mhBuffer
;
408 if ( kern_return_t r
= mach_vm_read_overwrite(task
, dyldAddress
, sizeof(mach_header_64
), (vm_address_t
)&mhBuffer
, &readSize
) ) {
411 size_t headerPagesSize
= (sizeof(mach_header_64
) + mhBuffer
.sizeofcmds
+ 4095) & (-4096);
412 vm_address_t localCopyBuffer
;
413 unsigned int localCopyBufferSize
;
414 if ( kern_return_t r
= mach_vm_read(task
, dyldAddress
, headerPagesSize
, &localCopyBuffer
, &localCopyBufferSize
) ) {
417 addInfoFromLoadCommands((mach_header
*)localCopyBuffer
, dyldAddress
, localCopyBufferSize
);
418 vm_deallocate(mach_task_self(), localCopyBuffer
, localCopyBufferSize
);
419 _curImage
->segmentsCount
= _curSegmentIndex
- _curImage
->segmentStartIndex
;
425 void dyld_process_info_base::addInfoFromLoadCommands(const mach_header
* mh
, uint64_t addressInTask
, size_t size
)
427 const load_command
* startCmds
= NULL
;
428 if ( mh
->magic
== MH_MAGIC_64
)
429 startCmds
= (load_command
*)((char *)mh
+ sizeof(mach_header_64
));
430 else if ( mh
->magic
== MH_MAGIC
)
431 startCmds
= (load_command
*)((char *)mh
+ sizeof(mach_header
));
433 return; // not a mach-o file, or wrong endianness
435 const load_command
* const cmdsEnd
= (load_command
*)((char*)startCmds
+ mh
->sizeofcmds
);
436 const load_command
* cmd
= startCmds
;
437 for(uint32_t i
= 0; i
< mh
->ncmds
; ++i
) {
438 const load_command
* nextCmd
= (load_command
*)((char *)cmd
+ cmd
->cmdsize
);
439 if ( (cmd
->cmdsize
< 8) || (nextCmd
> cmdsEnd
) || (nextCmd
< startCmds
) ) {
440 return; // malformed load command
442 if ( cmd
->cmd
== LC_UUID
) {
443 const uuid_command
* uuidCmd
= (uuid_command
*)cmd
;
444 memcpy(_curImage
->uuid
, uuidCmd
->uuid
, 16);
446 else if ( cmd
->cmd
== LC_SEGMENT
) {
447 const segment_command
* segCmd
= (segment_command
*)cmd
;
448 _curSegment
->name
= copySegmentName(segCmd
->segname
);
449 _curSegment
->addr
= segCmd
->vmaddr
;
450 _curSegment
->size
= segCmd
->vmsize
;
454 else if ( cmd
->cmd
== LC_SEGMENT_64
) {
455 const segment_command_64
* segCmd
= (segment_command_64
*)cmd
;
456 _curSegment
->name
= copySegmentName(segCmd
->segname
);
457 _curSegment
->addr
= segCmd
->vmaddr
;
458 _curSegment
->size
= segCmd
->vmsize
;
466 const char* dyld_process_info_base::copySegmentName(const char* name
)
468 // don't copy names of standard segments into string pool
469 static const char* stdSegNames
[] = {"__TEXT", "__DATA", "__LINKEDIT", "__DATA_DIRTY", "__DATA_CONST", "__OBJC", NULL
};
470 for (const char** s
=stdSegNames
; *s
!= NULL
; ++s
) {
471 if ( strcmp(name
, *s
) == 0 )
474 // copy custom segment names into string pool
475 return addString(name
);
478 void dyld_process_info_base::forEachImage(void (^callback
)(uint64_t machHeaderAddress
, const uuid_t uuid
, const char* path
)) const
480 for (const ImageInfo
* p
= _firstImage
; p
< _curImage
; ++p
) {
481 callback(p
->loadAddress
, p
->uuid
, p
->path
);
485 void dyld_process_info_base::forEachSegment(uint64_t machHeaderAddress
, void (^callback
)(uint64_t segmentAddress
, uint64_t segmentSize
, const char* segmentName
)) const
487 for (const ImageInfo
* p
= _firstImage
; p
< _curImage
; ++p
) {
488 if ( p
->loadAddress
== machHeaderAddress
) {
490 for (int i
=0; i
< p
->segmentsCount
; ++i
) {
491 const SegmentInfo
* seg
= &_firstSegment
[p
->segmentStartIndex
+i
];
492 if ( strcmp(seg
->name
, "__TEXT") == 0 ) {
493 slide
= machHeaderAddress
- seg
->addr
;
497 for (int i
=0; i
< p
->segmentsCount
; ++i
) {
498 const SegmentInfo
* seg
= &_firstSegment
[p
->segmentStartIndex
+i
];
499 callback(seg
->addr
+ slide
, seg
->size
, seg
->name
);
510 // Implementation that works with existing dyld data structures
511 static dyld_process_info
_dyld_process_info_create_inner(task_t task
, uint64_t timestamp
, kern_return_t
* kr
)
516 task_dyld_info_data_t task_dyld_info
;
517 mach_msg_type_number_t count
= TASK_DYLD_INFO_COUNT
;
518 if ( kern_return_t r
= task_info(task
, TASK_DYLD_INFO
, (task_info_t
)&task_dyld_info
, &count
) ) {
524 //The kernel will return MACH_VM_MIN_ADDRESS for an executable that has not had dyld loaded
525 if (task_dyld_info
.all_image_info_addr
== MACH_VM_MIN_ADDRESS
)
528 if ( task_dyld_info
.all_image_info_size
> sizeof(dyld_all_image_infos_64
) )
531 // read all_image_infos struct
532 dyld_all_image_infos_64 allImageInfo64
;
533 mach_vm_size_t readSize
= task_dyld_info
.all_image_info_size
;
534 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
) ) {
539 if ( allImageInfo64
.infoArrayCount
== 0 ) {
540 // could be task was launch suspended or still launching, wait a moment to see
541 usleep(1000 * 50); // 50ms
542 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
) ) {
547 // if infoArrayCount is still zero, then target was most likely launched suspended
548 if ( allImageInfo64
.infoArrayCount
== 0 )
549 return dyld_process_info_base::makeSuspended(task
, kr
);
552 // bail out of dyld is too old
553 if ( allImageInfo64
.version
< 15 ) {
555 *kr
= KERN_INVALID_HOST
;
559 // normalize by expanding 32-bit all_image_infos into 64-bit one
560 uint32_t imageCount
= allImageInfo64
.infoArrayCount
;
561 size_t imageArraySize
= imageCount
* sizeof(dyld_image_info_64
);
562 if ( task_dyld_info
.all_image_info_format
== TASK_DYLD_ALL_IMAGE_INFO_32
) {
563 const dyld_all_image_infos_32
* allImageInfo32
= (dyld_all_image_infos_32
*)&allImageInfo64
;
564 dyld_all_image_infos_64 info64
;
565 bzero(&info64
, sizeof(info64
));
566 info64
.version
= allImageInfo32
->version
;
567 info64
.infoArrayCount
= allImageInfo32
->infoArrayCount
;
568 info64
.infoArray
= allImageInfo32
->infoArray
;
569 info64
.processDetachedFromSharedRegion
= allImageInfo32
->processDetachedFromSharedRegion
;
570 info64
.libSystemInitialized
= allImageInfo32
->libSystemInitialized
;
571 info64
.dyldImageLoadAddress
= allImageInfo32
->dyldImageLoadAddress
;
572 info64
.initialImageCount
= allImageInfo32
->initialImageCount
;
573 info64
.uuidArrayCount
= allImageInfo32
->uuidArrayCount
;
574 info64
.uuidArray
= allImageInfo32
->uuidArray
;
575 info64
.dyldAllImageInfosAddress
= allImageInfo32
->dyldAllImageInfosAddress
;
576 info64
.sharedCacheSlide
= allImageInfo32
->sharedCacheSlide
;
577 info64
.infoArrayChangeTimestamp
= allImageInfo32
->infoArrayChangeTimestamp
;
578 info64
.sharedCacheBaseAddress
= allImageInfo32
->sharedCacheBaseAddress
;
579 info64
.dyldPath
= allImageInfo32
->dyldPath
;
580 memcpy((void*)(info64
.sharedCacheUUID
), (void*)(allImageInfo32
->sharedCacheUUID
), 16);
581 allImageInfo64
= info64
;
582 imageCount
= allImageInfo64
.infoArrayCount
;
583 imageArraySize
= imageCount
* sizeof(dyld_image_info_32
);
586 // don't do any (more) work if target process's dyld timestamp has not changed since previous query
587 if ( (timestamp
!= 0) && (timestamp
== allImageInfo64
.infoArrayChangeTimestamp
) ) {
593 // For the moment we are going to truncate any image list longer than 8192 because some programs do
594 // terrible things that corrupt their own image lists and we need to stop clients from crashing
595 // reading them. We can try to do something more advanced in the future. rdar://27446361
596 imageCount
= MIN(imageCount
, 8192);
599 if ( allImageInfo64
.infoArray
== 0 ) {
600 // dyld is in middle of updating image list, try again
603 dyld_image_info_64 imageArray64
[imageCount
];
604 if ( kern_return_t r
= mach_vm_read_overwrite(task
, allImageInfo64
.infoArray
, imageArraySize
, (vm_address_t
)&imageArray64
, &readSize
) ) {
605 // if image array moved, try whole thing again
611 // normalize by expanding 32-bit image_infos into 64-bit ones
612 if ( task_dyld_info
.all_image_info_format
== TASK_DYLD_ALL_IMAGE_INFO_32
) {
613 const dyld_image_info_32
* imageArray32
= (dyld_image_info_32
*)&imageArray64
;
614 dyld_image_info_64 tempArray
[imageCount
];
615 for (uint32_t i
=0; i
< imageCount
; ++i
) {
616 tempArray
[i
].imageLoadAddress
= imageArray32
[i
].imageLoadAddress
;
617 tempArray
[i
].imageFilePath
= imageArray32
[i
].imageFilePath
;
618 tempArray
[i
].imageFileModDate
= imageArray32
[i
].imageFileModDate
;
620 memcpy(imageArray64
, tempArray
, sizeof(dyld_image_info_64
)*imageCount
);
623 // create object based on local copy of all image infos and image array
624 dyld_process_info result
= dyld_process_info_base::make(task
, allImageInfo64
, imageArray64
, kr
);
626 // verify nothing has changed by re-reading all_image_infos struct and checking timestamp
627 if ( result
!= NULL
) {
628 dyld_all_image_infos_64 allImageInfo64again
;
629 readSize
= task_dyld_info
.all_image_info_size
;
630 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
) ) {
636 uint64_t doneTimeStamp
= allImageInfo64again
.infoArrayChangeTimestamp
;
637 if ( task_dyld_info
.all_image_info_format
== TASK_DYLD_ALL_IMAGE_INFO_32
) {
638 const dyld_all_image_infos_32
* allImageInfo32
= (dyld_all_image_infos_32
*)&allImageInfo64again
;
639 doneTimeStamp
= allImageInfo32
->infoArrayChangeTimestamp
;
641 if ( allImageInfo64
.infoArrayChangeTimestamp
!= doneTimeStamp
) {
642 // image list has changed since we started reading it
643 // throw out what we have and start over
653 dyld_process_info
_dyld_process_info_create(task_t task
, uint64_t timestamp
, kern_return_t
* kr
)
655 // Other process may be loading and unloading as we read its memory, which can cause a read failure
656 // <rdar://problem30067343&29567679> Retry if something fails
657 for (int i
=0; i
< 100; ++i
) {
658 if ( dyld_process_info result
= _dyld_process_info_create_inner( task
, timestamp
, kr
) )
665 void _dyld_process_info_get_state(dyld_process_info info
, dyld_process_state_info
* stateInfo
)
667 *stateInfo
= *info
->stateInfo();
670 void _dyld_process_info_get_cache(dyld_process_info info
, dyld_process_cache_info
* cacheInfo
)
672 *cacheInfo
= *info
->cacheInfo();
675 void _dyld_process_info_retain(dyld_process_info info
)
677 info
->retainCount() += 1;
680 void _dyld_process_info_release(dyld_process_info info
)
682 info
->retainCount() -= 1;
683 if ( info
->retainCount() == 0 )
687 void _dyld_process_info_for_each_image(dyld_process_info info
, void (^callback
)(uint64_t machHeaderAddress
, const uuid_t uuid
, const char* path
))
689 info
->forEachImage(callback
);
693 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
))
695 info
->forEachSegment(machHeaderAddress
, callback
);