]>
Commit | Line | Data |
---|---|---|
9f83892a A |
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 <libproc.h> | |
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> | |
35 | ||
36 | #include "dyld_process_info.h" | |
37 | #include "dyld_process_info_internal.h" | |
38 | #include "dyld_images.h" | |
39 | #include "dyld_priv.h" | |
40 | ||
10b92d3b A |
41 | // this was in dyld_priv.h but it is no longer exported |
42 | extern "C" { | |
43 | const struct dyld_all_image_infos* _dyld_get_all_image_infos(); | |
44 | } | |
45 | ||
46 | namespace { | |
47 | ||
e1ecafb2 | 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, size_t size)) { |
10b92d3b A |
49 | kern_return_t r = KERN_SUCCESS; |
50 | mach_vm_address_t local_address = 0; | |
51 | mach_vm_address_t local_size = remote_size; | |
52 | while (1) { | |
53 | vm_prot_t cur_protection, max_protection; | |
54 | r = mach_vm_remap(mach_task_self(), | |
55 | &local_address, | |
56 | local_size, | |
57 | 0, // mask | |
58 | VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR | VM_FLAGS_RESILIENT_CODESIGN, | |
59 | task, | |
60 | remote_address, | |
61 | TRUE, // Copy semantics: changes to this memory by the target process will not be visible in this process | |
62 | &cur_protection, | |
63 | &max_protection, | |
64 | VM_INHERIT_DEFAULT); | |
65 | //Do this here to allow chaining of multiple embedded blocks with a single error out; | |
66 | if (kr != NULL) { | |
67 | *kr = r; | |
68 | } | |
69 | if (r == KERN_SUCCESS) { | |
70 | // We got someting, call the block and then exit | |
e1ecafb2 | 71 | block(reinterpret_cast<void *>(local_address), local_size); |
10b92d3b A |
72 | vm_deallocate(mach_task_self(), local_address, local_size); |
73 | break; | |
74 | } | |
75 | if (!allow_truncation) { | |
76 | break; | |
77 | } | |
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 | |
82 | break; | |
83 | } else { | |
84 | local_size -= trunc_size; | |
85 | } | |
86 | } | |
87 | } | |
88 | ||
89 | template<typename T> | |
90 | void withRemoteObject(task_t task, vm_address_t remote_address, kern_return_t *kr, void (^block)(T t)) | |
91 | { | |
e1ecafb2 | 92 | withRemoteBuffer(task, remote_address, sizeof(T), false, kr, ^(void *buffer, size_t size) { |
10b92d3b A |
93 | block(*reinterpret_cast<T *>(buffer)); |
94 | }); | |
95 | } | |
96 | }; | |
9f83892a A |
97 | |
98 | // | |
99 | // Opaque object returned by _dyld_process_info_create() | |
100 | // | |
101 | ||
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); | |
105 | ||
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; | |
111 | ||
112 | private: | |
113 | struct ImageInfo { | |
114 | uuid_t uuid; | |
115 | uint64_t loadAddress; | |
116 | const char* path; | |
117 | uint32_t segmentStartIndex; | |
118 | uint32_t segmentsCount; | |
119 | }; | |
120 | ||
121 | struct SegmentInfo { | |
122 | const char* name; | |
123 | uint64_t addr; | |
124 | uint64_t size; | |
125 | }; | |
126 | ||
127 | dyld_process_info_base(unsigned imageCount, size_t totalSize); | |
128 | void* operator new (size_t, void* buf) { return buf; } | |
129 | ||
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); | |
133 | ||
134 | bool invalid() { return ((char*)_stringRevBumpPtr < (char*)_curSegment); } | |
135 | const char* copyPath(task_t task, uint64_t pathAddr, kern_return_t* kr); | |
e1ecafb2 | 136 | const char* addString(const char*, size_t); |
9f83892a A |
137 | const char* copySegmentName(const char*); |
138 | ||
139 | void addInfoFromLoadCommands(const mach_header* mh, uint64_t addressInTask, size_t size); | |
140 | ||
141 | void inspectLocalImageLoadCommands(uint64_t imageAddress, void* func); | |
142 | kern_return_t inspectRemoteImageLoadCommands(task_t task, uint64_t imageAddress, void* func); | |
143 | ||
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; | |
155 | ||
156 | // dyld_process_cache_info cacheInfo; | |
157 | // dyld_process_state_info stateInfo; | |
158 | // ImageInfo images[]; | |
159 | // SegmentInfo segments[]; | |
160 | // char stringPool[] | |
161 | }; | |
162 | ||
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)), | |
172 | _curSegmentIndex(0), | |
173 | _stringRevBumpPtr((char*)(this)+totalSize) | |
174 | { | |
175 | } | |
176 | ||
177 | ||
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) | |
179 | { | |
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(); | |
10b92d3b A |
182 | bool sameCacheAsThisProcess = !allImageInfo.processDetachedFromSharedRegion |
183 | && !myInfo->processDetachedFromSharedRegion | |
184 | && ((memcmp(myInfo->sharedCacheUUID, allImageInfo.sharedCacheUUID, 16) == 0) | |
185 | && (myInfo->sharedCacheSlide == allImageInfo.sharedCacheSlide)); | |
9f83892a A |
186 | unsigned countOfPathsNeedingCopying = 0; |
187 | if ( sameCacheAsThisProcess ) { | |
188 | for (int i=0; i < allImageInfo.infoArrayCount; ++i) { | |
189 | if ( !inCache(imageArray[i].imageFilePath) ) | |
190 | ++countOfPathsNeedingCopying; | |
191 | } | |
192 | } | |
193 | else { | |
194 | countOfPathsNeedingCopying = allImageInfo.infoArrayCount+1; | |
195 | } | |
196 | unsigned imageCountWithDyld = allImageInfo.infoArrayCount+1; | |
197 | ||
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() | |
207 | ||
208 | // fill in base info | |
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; | |
218 | } | |
219 | } | |
220 | ||
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; | |
231 | } | |
232 | if ( allImageInfo.errorMessage != 0 ) | |
233 | stateInfo->dyldState = allImageInfo.terminationFlags ? dyld_process_state_terminated_before_inits : dyld_process_state_dyld_terminated; | |
234 | ||
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) ) { | |
238 | if ( kr != NULL ) | |
239 | *kr = r; | |
240 | goto fail; | |
241 | } | |
242 | } | |
10b92d3b | 243 | |
9f83892a A |
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) ) { | |
247 | if ( kr != NULL ) | |
248 | *kr = r; | |
249 | goto fail; | |
250 | } | |
251 | } | |
252 | ||
253 | // sanity check internal data did not overflow | |
254 | if ( obj->invalid() ) | |
255 | goto fail; | |
256 | ||
257 | return obj; | |
258 | ||
259 | fail: | |
260 | free(obj); | |
261 | return NULL; | |
262 | } | |
263 | ||
264 | dyld_process_info_base* dyld_process_info_base::makeSuspended(task_t task, kern_return_t* kr) | |
265 | { | |
266 | pid_t pid; | |
267 | kern_return_t result = pid_for_task(task, &pid); | |
268 | if ( result != KERN_SUCCESS ) { | |
269 | if ( kr != NULL ) | |
270 | *kr = result; | |
271 | return NULL; | |
272 | } | |
273 | ||
10b92d3b A |
274 | __block unsigned imageCount = 0; // main executable and dyld |
275 | __block uint64_t mainExecutableAddress = 0; | |
276 | __block uint64_t dyldAddress = 0; | |
9f83892a A |
277 | char dyldPathBuffer[PATH_MAX+1]; |
278 | char mainExecutablePathBuffer[PATH_MAX+1]; | |
10b92d3b A |
279 | __block char * dyldPath = &dyldPathBuffer[0]; |
280 | __block char * mainExecutablePath = &mainExecutablePathBuffer[0]; | |
9f83892a A |
281 | mach_vm_size_t size; |
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 ) | |
289 | break; | |
10b92d3b A |
290 | if ( info.protection != (VM_PROT_READ|VM_PROT_EXECUTE) ) |
291 | continue; | |
d113e8b5 | 292 | // read start of vm region to verify it is a mach header |
10b92d3b A |
293 | withRemoteObject(task, address, NULL, ^(mach_header_64 mhBuffer){ |
294 | if ( (mhBuffer.magic != MH_MAGIC) && (mhBuffer.magic != MH_MAGIC_64) ) | |
295 | return; | |
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); | |
300 | if ( len != 0 ) { | |
301 | mainExecutablePath[len] = '\0'; | |
302 | } | |
303 | ++imageCount; | |
304 | } | |
305 | else if ( mhBuffer.filetype == MH_DYLINKER ) { | |
306 | dyldAddress = address; | |
307 | int len = proc_regionfilename(pid, dyldAddress, dyldPath, PATH_MAX); | |
308 | if ( len != 0 ) { | |
309 | dyldPath[len] = '\0'; | |
310 | } | |
311 | ++imageCount; | |
312 | } | |
313 | }); | |
9f83892a | 314 | //fprintf(stderr, "vm region: addr=0x%llX, size=0x%llX, prot=0x%X\n", (uint64_t)address, (uint64_t)size, info.protection); |
9f83892a A |
315 | } |
316 | //fprintf(stderr, "dyld: addr=0x%llX, path=%s\n", dyldAddress, dyldPathBuffer); | |
317 | //fprintf(stderr, "app: addr=0x%llX, path=%s\n", mainExecutableAddress, mainExecutablePathBuffer); | |
318 | ||
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() | |
328 | ||
329 | // fill in base info | |
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; | |
335 | ||
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; | |
341 | ||
342 | // fill in info for dyld | |
343 | if ( dyldAddress != 0 ) { | |
10b92d3b | 344 | if ( kern_return_t r = obj->addDyldImage(task, dyldAddress, 0, dyldPath) ) { |
9f83892a A |
345 | if ( kr != NULL ) |
346 | *kr = r; | |
347 | free(obj); | |
348 | return NULL; | |
349 | } | |
350 | } | |
351 | ||
352 | // fill in info for each image | |
353 | if ( mainExecutableAddress != 0 ) { | |
10b92d3b | 354 | if ( kern_return_t r = obj->addImage(task, false, mainExecutableAddress, 0, mainExecutablePath) ) { |
9f83892a A |
355 | if ( kr != NULL ) |
356 | *kr = r; | |
357 | free(obj); | |
358 | return NULL; | |
359 | } | |
360 | } | |
361 | ||
362 | return obj; | |
363 | } | |
364 | ||
365 | ||
366 | ||
e1ecafb2 | 367 | const char* dyld_process_info_base::addString(const char* str, size_t maxlen) |
9f83892a | 368 | { |
e1ecafb2 | 369 | size_t len = strnlen(str, maxlen) + 1; |
9f83892a | 370 | _stringRevBumpPtr -= len; |
e1ecafb2 | 371 | strlcpy(_stringRevBumpPtr, str, len); |
9f83892a A |
372 | return _stringRevBumpPtr; |
373 | } | |
374 | ||
375 | const char* dyld_process_info_base::copyPath(task_t task, uint64_t stringAddressInTask, kern_return_t* kr) | |
376 | { | |
10b92d3b | 377 | __block const char* retval = NULL; |
e1ecafb2 A |
378 | withRemoteBuffer(task, stringAddressInTask, PATH_MAX, true, kr, ^(void *buffer, size_t size) { |
379 | retval = addString(static_cast<const char *>(buffer), size); | |
10b92d3b | 380 | }); |
e1ecafb2 | 381 | |
10b92d3b | 382 | return retval; |
9f83892a A |
383 | } |
384 | ||
9f83892a A |
385 | kern_return_t dyld_process_info_base::addImage(task_t task, bool sameCacheAsThisProcess, uint64_t imageAddress, uint64_t imagePath, const char* imagePathLocal) |
386 | { | |
387 | _curImage->loadAddress = imageAddress; | |
388 | _curImage->segmentStartIndex = _curSegmentIndex; | |
389 | if ( imagePathLocal != NULL ) { | |
e1ecafb2 | 390 | _curImage->path = addString(imagePathLocal, PATH_MAX); |
9f83892a A |
391 | } |
392 | else if ( sameCacheAsThisProcess && inCache(imagePath) ) { | |
393 | _curImage->path = (const char*)imagePath; | |
394 | } | |
395 | else { | |
396 | kern_return_t kr; | |
397 | _curImage->path = copyPath(task, imagePath, &kr); | |
398 | if ( kr ) | |
399 | return kr; | |
400 | } | |
401 | if ( sameCacheAsThisProcess && inCache(imageAddress) ) { | |
402 | addInfoFromLoadCommands((mach_header*)imageAddress, imageAddress, 32*1024); | |
403 | } | |
404 | else { | |
10b92d3b A |
405 | __block kern_return_t kr = KERN_SUCCESS; |
406 | withRemoteObject(task, imageAddress, &kr, ^(mach_header_64 mhBuffer) { | |
407 | size_t headerPagesSize = sizeof(mach_header_64) + mhBuffer.sizeofcmds; | |
e1ecafb2 A |
408 | withRemoteBuffer(task, imageAddress, headerPagesSize, false, &kr, ^(void * buffer, size_t size) { |
409 | addInfoFromLoadCommands((mach_header*)buffer, imageAddress, size); | |
10b92d3b A |
410 | }); |
411 | }); | |
412 | if (kr != KERN_SUCCESS) { | |
413 | return kr; | |
9f83892a | 414 | } |
9f83892a A |
415 | } |
416 | _curImage->segmentsCount = _curSegmentIndex - _curImage->segmentStartIndex; | |
417 | _curImage++; | |
418 | return KERN_SUCCESS; | |
419 | } | |
420 | ||
421 | ||
422 | kern_return_t dyld_process_info_base::addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath) | |
423 | { | |
10b92d3b | 424 | __block kern_return_t kr = KERN_SUCCESS; |
9f83892a A |
425 | _curImage->loadAddress = dyldAddress; |
426 | _curImage->segmentStartIndex = _curSegmentIndex; | |
427 | if ( localPath != NULL ) { | |
e1ecafb2 | 428 | _curImage->path = addString(localPath, PATH_MAX); |
9f83892a A |
429 | } |
430 | else { | |
431 | _curImage->path = copyPath(task, dyldPathAddress, &kr); | |
432 | if ( kr ) | |
433 | return kr; | |
434 | } | |
435 | ||
10b92d3b A |
436 | withRemoteObject(task, dyldAddress, &kr, ^(mach_header_64 mhBuffer) { |
437 | size_t headerPagesSize = sizeof(mach_header_64) + mhBuffer.sizeofcmds; | |
e1ecafb2 A |
438 | withRemoteBuffer(task, dyldAddress, headerPagesSize, false, &kr, ^(void * buffer, size_t size) { |
439 | addInfoFromLoadCommands((mach_header*)buffer, dyldAddress, size); | |
10b92d3b A |
440 | }); |
441 | }); | |
442 | if (kr != KERN_SUCCESS) { | |
443 | return kr; | |
9f83892a | 444 | } |
10b92d3b | 445 | |
9f83892a A |
446 | _curImage->segmentsCount = _curSegmentIndex - _curImage->segmentStartIndex; |
447 | _curImage++; | |
448 | return KERN_SUCCESS; | |
449 | } | |
450 | ||
451 | ||
452 | void dyld_process_info_base::addInfoFromLoadCommands(const mach_header* mh, uint64_t addressInTask, size_t size) | |
453 | { | |
454 | const load_command* startCmds = NULL; | |
455 | if ( mh->magic == MH_MAGIC_64 ) | |
456 | startCmds = (load_command*)((char *)mh + sizeof(mach_header_64)); | |
457 | else if ( mh->magic == MH_MAGIC ) | |
458 | startCmds = (load_command*)((char *)mh + sizeof(mach_header)); | |
459 | else | |
460 | return; // not a mach-o file, or wrong endianness | |
10b92d3b | 461 | |
9f83892a A |
462 | const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds); |
463 | const load_command* cmd = startCmds; | |
464 | for(uint32_t i = 0; i < mh->ncmds; ++i) { | |
465 | const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize); | |
466 | if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) { | |
467 | return; // malformed load command | |
468 | } | |
469 | if ( cmd->cmd == LC_UUID ) { | |
470 | const uuid_command* uuidCmd = (uuid_command*)cmd; | |
471 | memcpy(_curImage->uuid, uuidCmd->uuid, 16); | |
472 | } | |
473 | else if ( cmd->cmd == LC_SEGMENT ) { | |
474 | const segment_command* segCmd = (segment_command*)cmd; | |
475 | _curSegment->name = copySegmentName(segCmd->segname); | |
476 | _curSegment->addr = segCmd->vmaddr; | |
477 | _curSegment->size = segCmd->vmsize; | |
478 | _curSegment++; | |
479 | _curSegmentIndex++; | |
480 | } | |
481 | else if ( cmd->cmd == LC_SEGMENT_64 ) { | |
482 | const segment_command_64* segCmd = (segment_command_64*)cmd; | |
483 | _curSegment->name = copySegmentName(segCmd->segname); | |
484 | _curSegment->addr = segCmd->vmaddr; | |
485 | _curSegment->size = segCmd->vmsize; | |
486 | _curSegment++; | |
487 | _curSegmentIndex++; | |
488 | } | |
489 | cmd = nextCmd; | |
490 | } | |
491 | } | |
492 | ||
493 | const char* dyld_process_info_base::copySegmentName(const char* name) | |
494 | { | |
495 | // don't copy names of standard segments into string pool | |
496 | static const char* stdSegNames[] = {"__TEXT", "__DATA", "__LINKEDIT", "__DATA_DIRTY", "__DATA_CONST", "__OBJC", NULL }; | |
497 | for (const char** s=stdSegNames; *s != NULL; ++s) { | |
498 | if ( strcmp(name, *s) == 0 ) | |
499 | return *s; | |
500 | } | |
501 | // copy custom segment names into string pool | |
e1ecafb2 | 502 | return addString(name, 16); |
9f83892a A |
503 | } |
504 | ||
505 | void dyld_process_info_base::forEachImage(void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)) const | |
506 | { | |
507 | for (const ImageInfo* p = _firstImage; p < _curImage; ++p) { | |
508 | callback(p->loadAddress, p->uuid, p->path); | |
509 | } | |
510 | } | |
511 | ||
512 | void dyld_process_info_base::forEachSegment(uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)) const | |
513 | { | |
514 | for (const ImageInfo* p = _firstImage; p < _curImage; ++p) { | |
515 | if ( p->loadAddress == machHeaderAddress ) { | |
516 | uint64_t slide = 0; | |
517 | for (int i=0; i < p->segmentsCount; ++i) { | |
518 | const SegmentInfo* seg = &_firstSegment[p->segmentStartIndex+i]; | |
519 | if ( strcmp(seg->name, "__TEXT") == 0 ) { | |
520 | slide = machHeaderAddress - seg->addr; | |
521 | break; | |
522 | } | |
523 | } | |
524 | for (int i=0; i < p->segmentsCount; ++i) { | |
525 | const SegmentInfo* seg = &_firstSegment[p->segmentStartIndex+i]; | |
526 | callback(seg->addr + slide, seg->size, seg->name); | |
527 | } | |
528 | break; | |
529 | } | |
530 | } | |
531 | } | |
532 | ||
533 | ||
534 | ||
535 | ||
536 | ||
537 | // Implementation that works with existing dyld data structures | |
d113e8b5 | 538 | static dyld_process_info _dyld_process_info_create_inner(task_t task, uint64_t timestamp, kern_return_t* kr) |
9f83892a A |
539 | { |
540 | if ( kr != NULL ) | |
541 | *kr = KERN_SUCCESS; | |
542 | ||
543 | task_dyld_info_data_t task_dyld_info; | |
544 | mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; | |
545 | if ( kern_return_t r = task_info(task, TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) ) { | |
546 | if ( kr != NULL ) | |
547 | *kr = r; | |
548 | return NULL; | |
549 | } | |
550 | ||
551 | //The kernel will return MACH_VM_MIN_ADDRESS for an executable that has not had dyld loaded | |
552 | if (task_dyld_info.all_image_info_addr == MACH_VM_MIN_ADDRESS) | |
553 | return NULL; | |
554 | ||
555 | if ( task_dyld_info.all_image_info_size > sizeof(dyld_all_image_infos_64) ) | |
556 | return NULL; | |
557 | ||
558 | // read all_image_infos struct | |
559 | dyld_all_image_infos_64 allImageInfo64; | |
560 | mach_vm_size_t readSize = task_dyld_info.all_image_info_size; | |
561 | 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) ) { | |
562 | if ( kr != NULL ) | |
563 | *kr = r; | |
564 | return NULL; | |
565 | } | |
566 | if ( allImageInfo64.infoArrayCount == 0 ) { | |
567 | // could be task was launch suspended or still launching, wait a moment to see | |
568 | usleep(1000 * 50); // 50ms | |
569 | 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) ) { | |
570 | if ( kr != NULL ) | |
571 | *kr = r; | |
572 | return NULL; | |
573 | } | |
574 | // if infoArrayCount is still zero, then target was most likely launched suspended | |
575 | if ( allImageInfo64.infoArrayCount == 0 ) | |
576 | return dyld_process_info_base::makeSuspended(task, kr); | |
577 | } | |
578 | ||
579 | // bail out of dyld is too old | |
580 | if ( allImageInfo64.version < 15 ) { | |
581 | if ( kr != NULL ) | |
582 | *kr = KERN_INVALID_HOST; | |
583 | return NULL; | |
584 | } | |
585 | ||
586 | // normalize by expanding 32-bit all_image_infos into 64-bit one | |
587 | uint32_t imageCount = allImageInfo64.infoArrayCount; | |
588 | size_t imageArraySize = imageCount * sizeof(dyld_image_info_64); | |
589 | if ( task_dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) { | |
590 | const dyld_all_image_infos_32* allImageInfo32 = (dyld_all_image_infos_32*)&allImageInfo64; | |
591 | dyld_all_image_infos_64 info64; | |
592 | bzero(&info64, sizeof(info64)); | |
593 | info64.version = allImageInfo32->version; | |
594 | info64.infoArrayCount = allImageInfo32->infoArrayCount; | |
595 | info64.infoArray = allImageInfo32->infoArray; | |
596 | info64.processDetachedFromSharedRegion = allImageInfo32->processDetachedFromSharedRegion; | |
597 | info64.libSystemInitialized = allImageInfo32->libSystemInitialized; | |
598 | info64.dyldImageLoadAddress = allImageInfo32->dyldImageLoadAddress; | |
599 | info64.initialImageCount = allImageInfo32->initialImageCount; | |
600 | info64.uuidArrayCount = allImageInfo32->uuidArrayCount; | |
601 | info64.uuidArray = allImageInfo32->uuidArray; | |
602 | info64.dyldAllImageInfosAddress = allImageInfo32->dyldAllImageInfosAddress; | |
603 | info64.sharedCacheSlide = allImageInfo32->sharedCacheSlide; | |
604 | info64.infoArrayChangeTimestamp = allImageInfo32->infoArrayChangeTimestamp; | |
605 | info64.sharedCacheBaseAddress = allImageInfo32->sharedCacheBaseAddress; | |
606 | info64.dyldPath = allImageInfo32->dyldPath; | |
607 | memcpy((void*)(info64.sharedCacheUUID), (void*)(allImageInfo32->sharedCacheUUID), 16); | |
608 | allImageInfo64 = info64; | |
609 | imageCount = allImageInfo64.infoArrayCount; | |
610 | imageArraySize = imageCount * sizeof(dyld_image_info_32); | |
611 | } | |
612 | ||
613 | // don't do any (more) work if target process's dyld timestamp has not changed since previous query | |
614 | if ( (timestamp != 0) && (timestamp == allImageInfo64.infoArrayChangeTimestamp) ) { | |
615 | if ( kr != NULL ) | |
616 | *kr = KERN_SUCCESS; | |
617 | return NULL; | |
618 | } | |
619 | ||
620 | // For the moment we are going to truncate any image list longer than 8192 because some programs do | |
621 | // terrible things that corrupt their own image lists and we need to stop clients from crashing | |
622 | // reading them. We can try to do something more advanced in the future. rdar://27446361 | |
623 | imageCount = MIN(imageCount, 8192); | |
624 | ||
625 | // read image array | |
d113e8b5 A |
626 | if ( allImageInfo64.infoArray == 0 ) { |
627 | // dyld is in middle of updating image list, try again | |
628 | return NULL; | |
629 | } | |
9f83892a A |
630 | dyld_image_info_64 imageArray64[imageCount]; |
631 | if ( kern_return_t r = mach_vm_read_overwrite(task, allImageInfo64.infoArray, imageArraySize, (vm_address_t)&imageArray64, &readSize) ) { | |
d113e8b5 A |
632 | // if image array moved, try whole thing again |
633 | if ( kr != NULL ) { | |
9f83892a | 634 | *kr = r; |
d113e8b5 | 635 | } |
9f83892a A |
636 | return NULL; |
637 | } | |
638 | // normalize by expanding 32-bit image_infos into 64-bit ones | |
639 | if ( task_dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) { | |
640 | const dyld_image_info_32* imageArray32 = (dyld_image_info_32*)&imageArray64; | |
641 | dyld_image_info_64 tempArray[imageCount]; | |
642 | for (uint32_t i=0; i < imageCount; ++i) { | |
643 | tempArray[i].imageLoadAddress = imageArray32[i].imageLoadAddress; | |
644 | tempArray[i].imageFilePath = imageArray32[i].imageFilePath; | |
645 | tempArray[i].imageFileModDate = imageArray32[i].imageFileModDate; | |
646 | } | |
647 | memcpy(imageArray64, tempArray, sizeof(dyld_image_info_64)*imageCount); | |
648 | } | |
649 | ||
650 | // create object based on local copy of all image infos and image array | |
d113e8b5 A |
651 | dyld_process_info result = dyld_process_info_base::make(task, allImageInfo64, imageArray64, kr); |
652 | ||
653 | // verify nothing has changed by re-reading all_image_infos struct and checking timestamp | |
654 | if ( result != NULL ) { | |
655 | dyld_all_image_infos_64 allImageInfo64again; | |
656 | readSize = task_dyld_info.all_image_info_size; | |
657 | 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) ) { | |
658 | if ( kr != NULL ) | |
659 | *kr = r; | |
660 | free((void*)result); | |
661 | return NULL; | |
662 | } | |
663 | uint64_t doneTimeStamp = allImageInfo64again.infoArrayChangeTimestamp; | |
664 | if ( task_dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) { | |
665 | const dyld_all_image_infos_32* allImageInfo32 = (dyld_all_image_infos_32*)&allImageInfo64again; | |
666 | doneTimeStamp = allImageInfo32->infoArrayChangeTimestamp; | |
667 | } | |
668 | if ( allImageInfo64.infoArrayChangeTimestamp != doneTimeStamp ) { | |
669 | // image list has changed since we started reading it | |
670 | // throw out what we have and start over | |
671 | free((void*)result); | |
672 | result = nullptr; | |
673 | } | |
674 | } | |
675 | ||
676 | return result; | |
9f83892a A |
677 | } |
678 | ||
679 | ||
d113e8b5 A |
680 | dyld_process_info _dyld_process_info_create(task_t task, uint64_t timestamp, kern_return_t* kr) |
681 | { | |
682 | // Other process may be loading and unloading as we read its memory, which can cause a read failure | |
683 | // <rdar://problem30067343&29567679> Retry if something fails | |
684 | for (int i=0; i < 100; ++i) { | |
685 | if ( dyld_process_info result = _dyld_process_info_create_inner( task, timestamp, kr) ) | |
686 | return result; | |
687 | ||
688 | } | |
689 | return NULL; | |
690 | } | |
691 | ||
9f83892a A |
692 | void _dyld_process_info_get_state(dyld_process_info info, dyld_process_state_info* stateInfo) |
693 | { | |
694 | *stateInfo = *info->stateInfo(); | |
695 | } | |
696 | ||
697 | void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_info* cacheInfo) | |
698 | { | |
699 | *cacheInfo = *info->cacheInfo(); | |
700 | } | |
701 | ||
702 | void _dyld_process_info_retain(dyld_process_info info) | |
703 | { | |
704 | info->retainCount() += 1; | |
705 | } | |
706 | ||
707 | void _dyld_process_info_release(dyld_process_info info) | |
708 | { | |
709 | info->retainCount() -= 1; | |
710 | if ( info->retainCount() == 0 ) | |
711 | free((void*)info); | |
712 | } | |
713 | ||
714 | void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)) | |
715 | { | |
716 | info->forEachImage(callback); | |
717 | } | |
718 | ||
719 | ||
720 | 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 | { | |
722 | info->forEachSegment(machHeaderAddress, callback); | |
723 | } | |
724 | ||
725 | ||
726 |