]>
Commit | Line | Data |
---|---|---|
39a8cd10 A |
1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- |
2 | * | |
3 | * Copyright (c) 2008 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 | ||
df9d6cf7 A |
26 | #if __arm__ || __arm64__ |
27 | #include <System/sys/mman.h> | |
28 | #else | |
29 | #include <sys/mman.h> | |
30 | #endif | |
39a8cd10 A |
31 | #include <string.h> |
32 | #include <fcntl.h> | |
33 | #include <errno.h> | |
34 | #include <sys/types.h> | |
35 | #include <sys/fcntl.h> | |
36 | #include <sys/stat.h> | |
39a8cd10 A |
37 | #include <sys/param.h> |
38 | #include <mach/mach.h> | |
39 | #include <mach/thread_status.h> | |
40 | #include <mach-o/loader.h> | |
39a8cd10 A |
41 | #include "ImageLoaderMachOCompressed.h" |
42 | #include "mach-o/dyld_images.h" | |
43 | ||
2fd3f4e8 A |
44 | #ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE |
45 | #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 | |
46 | #endif | |
39a8cd10 A |
47 | |
48 | // relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables | |
49 | #if __LP64__ | |
50 | #define RELOC_SIZE 3 | |
51 | #define LC_SEGMENT_COMMAND LC_SEGMENT_64 | |
52 | #define LC_ROUTINES_COMMAND LC_ROUTINES_64 | |
53 | struct macho_segment_command : public segment_command_64 {}; | |
54 | struct macho_section : public section_64 {}; | |
55 | struct macho_routines_command : public routines_command_64 {}; | |
56 | #else | |
57 | #define RELOC_SIZE 2 | |
58 | #define LC_SEGMENT_COMMAND LC_SEGMENT | |
59 | #define LC_ROUTINES_COMMAND LC_ROUTINES | |
60 | struct macho_segment_command : public segment_command {}; | |
61 | struct macho_section : public section {}; | |
62 | struct macho_routines_command : public routines_command {}; | |
63 | #endif | |
64 | ||
65 | ||
66 | static uintptr_t read_uleb128(const uint8_t*& p, const uint8_t* end) | |
67 | { | |
68 | uint64_t result = 0; | |
69 | int bit = 0; | |
70 | do { | |
71 | if (p == end) | |
72 | dyld::throwf("malformed uleb128"); | |
73 | ||
74 | uint64_t slice = *p & 0x7f; | |
75 | ||
412ebb8e A |
76 | if (bit > 63) |
77 | dyld::throwf("uleb128 too big for uint64, bit=%d, result=0x%0llX", bit, result); | |
39a8cd10 A |
78 | else { |
79 | result |= (slice << bit); | |
80 | bit += 7; | |
81 | } | |
412ebb8e | 82 | } while (*p++ & 0x80); |
39a8cd10 A |
83 | return result; |
84 | } | |
85 | ||
86 | ||
87 | static intptr_t read_sleb128(const uint8_t*& p, const uint8_t* end) | |
88 | { | |
89 | int64_t result = 0; | |
90 | int bit = 0; | |
91 | uint8_t byte; | |
92 | do { | |
93 | if (p == end) | |
94 | throw "malformed sleb128"; | |
95 | byte = *p++; | |
832b6fce | 96 | result |= (((int64_t)(byte & 0x7f)) << bit); |
39a8cd10 A |
97 | bit += 7; |
98 | } while (byte & 0x80); | |
99 | // sign extend negative numbers | |
100 | if ( (byte & 0x40) != 0 ) | |
101 | result |= (-1LL) << bit; | |
102 | return result; | |
103 | } | |
104 | ||
105 | ||
106 | // create image for main executable | |
107 | ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, | |
108 | unsigned int segCount, unsigned int libCount, const LinkContext& context) | |
109 | { | |
110 | ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, path, segCount, libCount); | |
111 | ||
112 | // set slide for PIE programs | |
113 | image->setSlide(slide); | |
114 | ||
115 | // for PIE record end of program, to know where to start loading dylibs | |
412ebb8e | 116 | if ( slide != 0 ) |
39a8cd10 | 117 | fgNextPIEDylibAddress = (uintptr_t)image->getEnd(); |
df9d6cf7 A |
118 | |
119 | image->disableCoverageCheck(); | |
39a8cd10 | 120 | image->instantiateFinish(context); |
412ebb8e | 121 | image->setMapped(context); |
df9d6cf7 | 122 | |
39a8cd10 A |
123 | if ( context.verboseMapping ) { |
124 | dyld::log("dyld: Main executable mapped %s\n", path); | |
125 | for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) { | |
126 | const char* name = image->segName(i); | |
127 | if ( (strcmp(name, "__PAGEZERO") == 0) || (strcmp(name, "__UNIXSTACK") == 0) ) | |
128 | dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segPreferredLoadAddress(i), image->segPreferredLoadAddress(i)+image->segSize(i)); | |
129 | else | |
130 | dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segActualLoadAddress(i), image->segActualEndAddress(i)); | |
131 | } | |
132 | } | |
133 | ||
134 | return image; | |
135 | } | |
136 | ||
137 | // create image by mapping in a mach-o file | |
df9d6cf7 | 138 | ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(const char* path, int fd, const uint8_t* fileData, size_t lenFileData, |
39a8cd10 | 139 | uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, |
412ebb8e | 140 | unsigned int segCount, unsigned int libCount, |
df9d6cf7 A |
141 | const struct linkedit_data_command* codeSigCmd, |
142 | const struct encryption_info_command* encryptCmd, | |
143 | const LinkContext& context) | |
39a8cd10 A |
144 | { |
145 | ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart((macho_header*)fileData, path, segCount, libCount); | |
146 | ||
147 | try { | |
148 | // record info about file | |
149 | image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); | |
150 | ||
412ebb8e | 151 | // if this image is code signed, let kernel validate signature before mapping any pages from image |
2fd3f4e8 | 152 | image->loadCodeSignature(codeSigCmd, fd, offsetInFat, context); |
412ebb8e | 153 | |
df9d6cf7 A |
154 | // Validate that first data we read with pread actually matches with code signature |
155 | image->validateFirstPages(codeSigCmd, fd, fileData, lenFileData, offsetInFat, context); | |
156 | ||
39a8cd10 A |
157 | // mmap segments |
158 | image->mapSegments(fd, offsetInFat, lenInFat, info.st_size, context); | |
159 | ||
df9d6cf7 A |
160 | // if framework is FairPlay encrypted, register with kernel |
161 | image->registerEncryption(encryptCmd, context); | |
162 | ||
2fd3f4e8 A |
163 | // probe to see if code signed correctly |
164 | image->crashIfInvalidCodeSignature(); | |
165 | ||
39a8cd10 A |
166 | // finish construction |
167 | image->instantiateFinish(context); | |
168 | ||
169 | // if path happens to be same as in LC_DYLIB_ID load command use that, otherwise malloc a copy of the path | |
170 | const char* installName = image->getInstallPath(); | |
171 | if ( (installName != NULL) && (strcmp(installName, path) == 0) && (path[0] == '/') ) | |
172 | image->setPathUnowned(installName); | |
173 | #if __MAC_OS_X_VERSION_MIN_REQUIRED | |
174 | // <rdar://problem/6563887> app crashes when libSystem cannot be found | |
175 | else if ( (installName != NULL) && (strcmp(path, "/usr/lib/libgcc_s.1.dylib") == 0) && (strcmp(installName, "/usr/lib/libSystem.B.dylib") == 0) ) | |
176 | image->setPathUnowned("/usr/lib/libSystem.B.dylib"); | |
177 | #endif | |
832b6fce A |
178 | else if ( (path[0] != '/') || (strstr(path, "../") != NULL) ) { |
179 | // rdar://problem/10733082 Fix up @rpath based paths during introspection | |
39a8cd10 A |
180 | // rdar://problem/5135363 turn relative paths into absolute paths so gdb, Symbolication can later find them |
181 | char realPath[MAXPATHLEN]; | |
832b6fce A |
182 | if ( fcntl(fd, F_GETPATH, realPath) == 0 ) |
183 | image->setPaths(path, realPath); | |
39a8cd10 A |
184 | else |
185 | image->setPath(path); | |
186 | } | |
187 | else | |
188 | image->setPath(path); | |
189 | ||
412ebb8e A |
190 | // make sure path is stable before recording in dyld_all_image_infos |
191 | image->setMapped(context); | |
192 | ||
39a8cd10 A |
193 | // pre-fetch content of __DATA and __LINKEDIT segment for faster launches |
194 | // don't do this on prebound images or if prefetching is disabled | |
195 | if ( !context.preFetchDisabled && !image->isPrebindable()) { | |
196 | image->preFetchDATA(fd, offsetInFat, context); | |
197 | image->markSequentialLINKEDIT(context); | |
198 | } | |
199 | } | |
200 | catch (...) { | |
201 | // ImageLoader::setMapped() can throw an exception to block loading of image | |
202 | // <rdar://problem/6169686> Leaked fSegmentsArray and image segments during failed dlopen_preflight | |
203 | delete image; | |
204 | throw; | |
205 | } | |
206 | ||
207 | return image; | |
208 | } | |
209 | ||
210 | // create image by using cached mach-o file | |
412ebb8e A |
211 | ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromCache(const macho_header* mh, const char* path, long slide, |
212 | const struct stat& info, unsigned int segCount, | |
213 | unsigned int libCount, const LinkContext& context) | |
39a8cd10 A |
214 | { |
215 | ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, path, segCount, libCount); | |
216 | try { | |
217 | // record info about file | |
218 | image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); | |
219 | ||
220 | // remember this is from shared cache and cannot be unloaded | |
221 | image->fInSharedCache = true; | |
222 | image->setNeverUnload(); | |
412ebb8e | 223 | image->setSlide(slide); |
df9d6cf7 | 224 | image->disableCoverageCheck(); |
39a8cd10 A |
225 | |
226 | // segments already mapped in cache | |
227 | if ( context.verboseMapping ) { | |
228 | dyld::log("dyld: Using shared cached for %s\n", path); | |
229 | for(unsigned int i=0; i < image->fSegmentsCount; ++i) { | |
230 | dyld::log("%18s at 0x%08lX->0x%08lX\n", image->segName(i), image->segActualLoadAddress(i), image->segActualEndAddress(i)); | |
231 | } | |
232 | } | |
233 | ||
234 | image->instantiateFinish(context); | |
412ebb8e | 235 | image->setMapped(context); |
39a8cd10 A |
236 | } |
237 | catch (...) { | |
238 | // ImageLoader::setMapped() can throw an exception to block loading of image | |
239 | // <rdar://problem/6169686> Leaked fSegmentsArray and image segments during failed dlopen_preflight | |
240 | delete image; | |
241 | throw; | |
242 | } | |
243 | ||
244 | return image; | |
245 | } | |
246 | ||
247 | // create image by copying an in-memory mach-o file | |
248 | ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, | |
249 | unsigned int segCount, unsigned int libCount, const LinkContext& context) | |
250 | { | |
251 | ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, moduleName, segCount, libCount); | |
252 | try { | |
253 | // map segments | |
254 | if ( mh->filetype == MH_EXECUTE ) | |
255 | throw "can't load another MH_EXECUTE"; | |
256 | ||
257 | // vmcopy segments | |
258 | image->mapSegments((const void*)mh, len, context); | |
259 | ||
260 | // for compatibility, never unload dylibs loaded from memory | |
261 | image->setNeverUnload(); | |
262 | ||
df9d6cf7 A |
263 | image->disableCoverageCheck(); |
264 | ||
39a8cd10 A |
265 | // bundle loads need path copied |
266 | if ( moduleName != NULL ) | |
267 | image->setPath(moduleName); | |
268 | ||
269 | image->instantiateFinish(context); | |
412ebb8e | 270 | image->setMapped(context); |
39a8cd10 A |
271 | } |
272 | catch (...) { | |
273 | // ImageLoader::setMapped() can throw an exception to block loading of image | |
274 | // <rdar://problem/6169686> Leaked fSegmentsArray and image segments during failed dlopen_preflight | |
275 | delete image; | |
276 | throw; | |
277 | } | |
278 | ||
279 | return image; | |
280 | } | |
281 | ||
282 | ||
283 | ImageLoaderMachOCompressed::ImageLoaderMachOCompressed(const macho_header* mh, const char* path, unsigned int segCount, | |
284 | uint32_t segOffsets[], unsigned int libCount) | |
285 | : ImageLoaderMachO(mh, path, segCount, segOffsets, libCount), fDyldInfo(NULL) | |
286 | { | |
287 | } | |
288 | ||
289 | ImageLoaderMachOCompressed::~ImageLoaderMachOCompressed() | |
290 | { | |
291 | // don't do clean up in ~ImageLoaderMachO() because virtual call to segmentCommandOffsets() won't work | |
292 | destroy(); | |
293 | } | |
294 | ||
295 | ||
296 | ||
297 | // construct ImageLoaderMachOCompressed using "placement new" with SegmentMachO objects array at end | |
298 | ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateStart(const macho_header* mh, const char* path, | |
299 | unsigned int segCount, unsigned int libCount) | |
300 | { | |
301 | size_t size = sizeof(ImageLoaderMachOCompressed) + segCount * sizeof(uint32_t) + libCount * sizeof(ImageLoader*); | |
302 | ImageLoaderMachOCompressed* allocatedSpace = static_cast<ImageLoaderMachOCompressed*>(malloc(size)); | |
303 | if ( allocatedSpace == NULL ) | |
304 | throw "malloc failed"; | |
305 | uint32_t* segOffsets = ((uint32_t*)(((uint8_t*)allocatedSpace) + sizeof(ImageLoaderMachOCompressed))); | |
306 | bzero(&segOffsets[segCount], libCount*sizeof(void*)); // zero out lib array | |
307 | return new (allocatedSpace) ImageLoaderMachOCompressed(mh, path, segCount, segOffsets, libCount); | |
308 | } | |
309 | ||
310 | ||
311 | // common code to finish initializing object | |
312 | void ImageLoaderMachOCompressed::instantiateFinish(const LinkContext& context) | |
313 | { | |
314 | // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide | |
df9d6cf7 | 315 | this->parseLoadCmds(context); |
39a8cd10 A |
316 | } |
317 | ||
318 | uint32_t* ImageLoaderMachOCompressed::segmentCommandOffsets() const | |
319 | { | |
320 | return ((uint32_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed))); | |
321 | } | |
322 | ||
323 | ||
324 | ImageLoader* ImageLoaderMachOCompressed::libImage(unsigned int libIndex) const | |
325 | { | |
326 | const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); | |
412ebb8e A |
327 | // mask off low bits |
328 | return (ImageLoader*)(images[libIndex] & (-4)); | |
39a8cd10 A |
329 | } |
330 | ||
331 | bool ImageLoaderMachOCompressed::libReExported(unsigned int libIndex) const | |
332 | { | |
333 | const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); | |
334 | // re-export flag is low bit | |
335 | return ((images[libIndex] & 1) != 0); | |
336 | } | |
337 | ||
412ebb8e A |
338 | bool ImageLoaderMachOCompressed::libIsUpward(unsigned int libIndex) const |
339 | { | |
340 | const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); | |
341 | // re-export flag is second bit | |
342 | return ((images[libIndex] & 2) != 0); | |
343 | } | |
344 | ||
39a8cd10 | 345 | |
412ebb8e | 346 | void ImageLoaderMachOCompressed::setLibImage(unsigned int libIndex, ImageLoader* image, bool reExported, bool upward) |
39a8cd10 A |
347 | { |
348 | uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); | |
349 | uintptr_t value = (uintptr_t)image; | |
350 | if ( reExported ) | |
351 | value |= 1; | |
412ebb8e A |
352 | if ( upward ) |
353 | value |= 2; | |
39a8cd10 A |
354 | images[libIndex] = value; |
355 | } | |
356 | ||
357 | ||
358 | void ImageLoaderMachOCompressed::markFreeLINKEDIT(const LinkContext& context) | |
359 | { | |
360 | // mark that we are done with rebase and bind info | |
361 | markLINKEDIT(context, MADV_FREE); | |
362 | } | |
363 | ||
364 | void ImageLoaderMachOCompressed::markSequentialLINKEDIT(const LinkContext& context) | |
365 | { | |
366 | // mark the rebase and bind info and using sequential access | |
367 | markLINKEDIT(context, MADV_SEQUENTIAL); | |
368 | } | |
369 | ||
370 | void ImageLoaderMachOCompressed::markLINKEDIT(const LinkContext& context, int advise) | |
371 | { | |
372 | // if not loaded at preferred address, mark rebase info | |
373 | uintptr_t start = 0; | |
374 | if ( (fSlide != 0) && (fDyldInfo->rebase_size != 0) ) | |
375 | start = (uintptr_t)fLinkEditBase + fDyldInfo->rebase_off; | |
376 | else if ( fDyldInfo->bind_off != 0 ) | |
377 | start = (uintptr_t)fLinkEditBase + fDyldInfo->bind_off; | |
378 | else | |
379 | return; // no binding info to prefetch | |
380 | ||
381 | // end is at end of bind info | |
382 | uintptr_t end = 0; | |
383 | if ( fDyldInfo->bind_off != 0 ) | |
384 | end = (uintptr_t)fLinkEditBase + fDyldInfo->bind_off + fDyldInfo->bind_size; | |
385 | else if ( fDyldInfo->rebase_off != 0 ) | |
386 | end = (uintptr_t)fLinkEditBase + fDyldInfo->rebase_off + fDyldInfo->rebase_size; | |
387 | else | |
388 | return; | |
389 | ||
390 | ||
391 | // round to whole pages | |
19894a12 A |
392 | start = dyld_page_trunc(start); |
393 | end = dyld_page_round(end); | |
39a8cd10 A |
394 | |
395 | // do nothing if only one page of rebase/bind info | |
19894a12 | 396 | if ( (end-start) <= dyld_page_size ) |
39a8cd10 A |
397 | return; |
398 | ||
399 | // tell kernel about our access to these pages | |
400 | madvise((void*)start, end-start, advise); | |
401 | if ( context.verboseMapping ) { | |
402 | const char* adstr = "sequential"; | |
403 | if ( advise == MADV_FREE ) | |
404 | adstr = "free"; | |
412ebb8e | 405 | dyld::log("%18s %s 0x%0lX -> 0x%0lX for %s\n", "__LINKEDIT", adstr, start, end-1, this->getPath()); |
39a8cd10 A |
406 | } |
407 | } | |
408 | ||
409 | ||
410 | ||
411 | void ImageLoaderMachOCompressed::rebaseAt(const LinkContext& context, uintptr_t addr, uintptr_t slide, uint8_t type) | |
412 | { | |
832b6fce A |
413 | if ( context.verboseRebase ) { |
414 | dyld::log("dyld: rebase: %s:*0x%08lX += 0x%08lX\n", this->getShortName(), (uintptr_t)addr, slide); | |
415 | } | |
39a8cd10 A |
416 | //dyld::log("0x%08lX type=%d\n", addr, type); |
417 | uintptr_t* locationToFix = (uintptr_t*)addr; | |
418 | switch (type) { | |
419 | case REBASE_TYPE_POINTER: | |
420 | *locationToFix += slide; | |
421 | break; | |
422 | case REBASE_TYPE_TEXT_ABSOLUTE32: | |
423 | *locationToFix += slide; | |
424 | break; | |
425 | default: | |
426 | dyld::throwf("bad rebase type %d", type); | |
427 | } | |
428 | } | |
429 | ||
430 | void ImageLoaderMachOCompressed::throwBadRebaseAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, | |
431 | const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos) | |
432 | { | |
433 | dyld::throwf("malformed rebase opcodes (%ld/%ld): address 0x%08lX is beyond end of segment %s (0x%08lX -> 0x%08lX)", | |
434 | (intptr_t)(pos-startOpcodes), (intptr_t)(endOpcodes-startOpcodes), address, segName(segmentIndex), | |
435 | segActualLoadAddress(segmentIndex), segmentEndAddress); | |
436 | } | |
437 | ||
438 | void ImageLoaderMachOCompressed::rebase(const LinkContext& context) | |
439 | { | |
412ebb8e | 440 | CRSetCrashLogMessage2(this->getPath()); |
39a8cd10 A |
441 | const uintptr_t slide = this->fSlide; |
442 | const uint8_t* const start = fLinkEditBase + fDyldInfo->rebase_off; | |
443 | const uint8_t* const end = &start[fDyldInfo->rebase_size]; | |
444 | const uint8_t* p = start; | |
445 | ||
446 | try { | |
447 | uint8_t type = 0; | |
448 | int segmentIndex = 0; | |
449 | uintptr_t address = segActualLoadAddress(0); | |
450 | uintptr_t segmentEndAddress = segActualEndAddress(0); | |
19894a12 A |
451 | uintptr_t count; |
452 | uintptr_t skip; | |
39a8cd10 A |
453 | bool done = false; |
454 | while ( !done && (p < end) ) { | |
455 | uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; | |
456 | uint8_t opcode = *p & REBASE_OPCODE_MASK; | |
457 | ++p; | |
458 | switch (opcode) { | |
459 | case REBASE_OPCODE_DONE: | |
460 | done = true; | |
461 | break; | |
462 | case REBASE_OPCODE_SET_TYPE_IMM: | |
463 | type = immediate; | |
464 | break; | |
465 | case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
466 | segmentIndex = immediate; | |
19894a12 A |
467 | if ( segmentIndex >= fSegmentsCount ) |
468 | dyld::throwf("REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", | |
469 | segmentIndex, fSegmentsCount-1); | |
39a8cd10 A |
470 | address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end); |
471 | segmentEndAddress = segActualEndAddress(segmentIndex); | |
472 | break; | |
473 | case REBASE_OPCODE_ADD_ADDR_ULEB: | |
474 | address += read_uleb128(p, end); | |
475 | break; | |
476 | case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: | |
477 | address += immediate*sizeof(uintptr_t); | |
478 | break; | |
479 | case REBASE_OPCODE_DO_REBASE_IMM_TIMES: | |
480 | for (int i=0; i < immediate; ++i) { | |
481 | if ( address >= segmentEndAddress ) | |
482 | throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); | |
483 | rebaseAt(context, address, slide, type); | |
484 | address += sizeof(uintptr_t); | |
485 | } | |
486 | fgTotalRebaseFixups += immediate; | |
487 | break; | |
488 | case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: | |
489 | count = read_uleb128(p, end); | |
490 | for (uint32_t i=0; i < count; ++i) { | |
491 | if ( address >= segmentEndAddress ) | |
492 | throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); | |
493 | rebaseAt(context, address, slide, type); | |
494 | address += sizeof(uintptr_t); | |
495 | } | |
496 | fgTotalRebaseFixups += count; | |
497 | break; | |
498 | case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: | |
499 | if ( address >= segmentEndAddress ) | |
500 | throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); | |
501 | rebaseAt(context, address, slide, type); | |
502 | address += read_uleb128(p, end) + sizeof(uintptr_t); | |
503 | ++fgTotalRebaseFixups; | |
504 | break; | |
505 | case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: | |
506 | count = read_uleb128(p, end); | |
507 | skip = read_uleb128(p, end); | |
508 | for (uint32_t i=0; i < count; ++i) { | |
509 | if ( address >= segmentEndAddress ) | |
510 | throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); | |
511 | rebaseAt(context, address, slide, type); | |
512 | address += skip + sizeof(uintptr_t); | |
513 | } | |
514 | fgTotalRebaseFixups += count; | |
515 | break; | |
516 | default: | |
517 | dyld::throwf("bad rebase opcode %d", *p); | |
518 | } | |
519 | } | |
520 | } | |
521 | catch (const char* msg) { | |
522 | const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); | |
523 | free((void*)msg); | |
524 | throw newMsg; | |
525 | } | |
412ebb8e | 526 | CRSetCrashLogMessage2(NULL); |
39a8cd10 A |
527 | } |
528 | ||
412ebb8e A |
529 | // |
530 | // This function is the hotspot of symbol lookup. It was pulled out of findExportedSymbol() | |
531 | // to enable it to be re-written in assembler if needed. | |
532 | // | |
533 | const uint8_t* ImageLoaderMachOCompressed::trieWalk(const uint8_t* start, const uint8_t* end, const char* s) | |
39a8cd10 | 534 | { |
39a8cd10 | 535 | const uint8_t* p = start; |
412ebb8e | 536 | while ( p != NULL ) { |
19894a12 | 537 | uintptr_t terminalSize = *p++; |
412ebb8e A |
538 | if ( terminalSize > 127 ) { |
539 | // except for re-export-with-rename, all terminal sizes fit in one byte | |
540 | --p; | |
541 | terminalSize = read_uleb128(p, end); | |
542 | } | |
39a8cd10 | 543 | if ( (*s == '\0') && (terminalSize != 0) ) { |
412ebb8e A |
544 | //dyld::log("trieWalk(%p) returning %p\n", start, p); |
545 | return p; | |
39a8cd10 | 546 | } |
412ebb8e A |
547 | const uint8_t* children = p + terminalSize; |
548 | //dyld::log("trieWalk(%p) sym=%s, terminalSize=%d, children=%p\n", start, s, terminalSize, children); | |
549 | uint8_t childrenRemaining = *children++; | |
550 | p = children; | |
19894a12 | 551 | uintptr_t nodeOffset = 0; |
412ebb8e | 552 | for (; childrenRemaining > 0; --childrenRemaining) { |
39a8cd10 | 553 | const char* ss = s; |
412ebb8e | 554 | //dyld::log("trieWalk(%p) child str=%s\n", start, (char*)p); |
39a8cd10 | 555 | bool wrongEdge = false; |
39a8cd10 A |
556 | // scan whole edge to get to next edge |
557 | // if edge is longer than target symbol name, don't read past end of symbol name | |
412ebb8e A |
558 | char c = *p; |
559 | while ( c != '\0' ) { | |
39a8cd10 | 560 | if ( !wrongEdge ) { |
412ebb8e | 561 | if ( c != *ss ) |
39a8cd10 | 562 | wrongEdge = true; |
412ebb8e | 563 | ++ss; |
39a8cd10 | 564 | } |
412ebb8e A |
565 | ++p; |
566 | c = *p; | |
39a8cd10 A |
567 | } |
568 | if ( wrongEdge ) { | |
569 | // advance to next child | |
412ebb8e A |
570 | ++p; // skip over zero terminator |
571 | // skip over uleb128 until last byte is found | |
572 | while ( (*p & 0x80) != 0 ) | |
573 | ++p; | |
574 | ++p; // skil over last byte of uleb128 | |
39a8cd10 A |
575 | } |
576 | else { | |
412ebb8e | 577 | // the symbol so far matches this edge (child) |
39a8cd10 | 578 | // so advance to the child's node |
412ebb8e A |
579 | ++p; |
580 | nodeOffset = read_uleb128(p, end); | |
39a8cd10 | 581 | s = ss; |
412ebb8e | 582 | //dyld::log("trieWalk() found matching edge advancing to node 0x%x\n", nodeOffset); |
39a8cd10 A |
583 | break; |
584 | } | |
585 | } | |
412ebb8e A |
586 | if ( nodeOffset != 0 ) |
587 | p = &start[nodeOffset]; | |
588 | else | |
589 | p = NULL; | |
590 | } | |
591 | //dyld::log("trieWalk(%p) return NULL\n", start); | |
592 | return NULL; | |
593 | } | |
594 | ||
595 | ||
596 | const ImageLoader::Symbol* ImageLoaderMachOCompressed::findExportedSymbol(const char* symbol, const ImageLoader** foundIn) const | |
597 | { | |
598 | //dyld::log("Compressed::findExportedSymbol(%s) in %s\n", symbol, this->getShortName()); | |
599 | if ( fDyldInfo->export_size == 0 ) | |
600 | return NULL; | |
601 | #if LOG_BINDINGS | |
602 | dyld::logBindings("%s: %s\n", this->getShortName(), symbol); | |
603 | #endif | |
604 | ++ImageLoaderMachO::fgSymbolTrieSearchs; | |
605 | const uint8_t* start = &fLinkEditBase[fDyldInfo->export_off]; | |
606 | const uint8_t* end = &start[fDyldInfo->export_size]; | |
607 | const uint8_t* foundNodeStart = this->trieWalk(start, end, symbol); | |
608 | if ( foundNodeStart != NULL ) { | |
609 | const uint8_t* p = foundNodeStart; | |
19894a12 | 610 | const uintptr_t flags = read_uleb128(p, end); |
412ebb8e A |
611 | // found match, return pointer to terminal part of node |
612 | if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { | |
613 | // re-export from another dylib, lookup there | |
19894a12 | 614 | const uintptr_t ordinal = read_uleb128(p, end); |
412ebb8e A |
615 | const char* importedName = (char*)p; |
616 | if ( importedName[0] == '\0' ) | |
617 | importedName = symbol; | |
618 | if ( (ordinal > 0) && (ordinal <= libraryCount()) ) { | |
19894a12 | 619 | const ImageLoader* reexportedFrom = libImage((unsigned int)ordinal-1); |
412ebb8e A |
620 | //dyld::log("Compressed::findExportedSymbol(), %s -> %s/%s\n", symbol, reexportedFrom->getShortName(), importedName); |
621 | return reexportedFrom->findExportedSymbol(importedName, true, foundIn); | |
622 | } | |
623 | else { | |
624 | //dyld::throwf("bad mach-o binary, library ordinal (%u) invalid (max %u) for re-exported symbol %s in %s", | |
625 | // ordinal, libraryCount(), symbol, this->getPath()); | |
626 | } | |
627 | } | |
39a8cd10 | 628 | else { |
412ebb8e A |
629 | //dyld::log("findExportedSymbol(%s) in %s found match, returning %p\n", symbol, this->getShortName(), p); |
630 | if ( foundIn != NULL ) | |
631 | *foundIn = (ImageLoader*)this; | |
632 | // return pointer to terminal part of node | |
633 | return (Symbol*)foundNodeStart; | |
39a8cd10 | 634 | } |
412ebb8e A |
635 | } |
636 | return NULL; | |
39a8cd10 A |
637 | } |
638 | ||
639 | ||
640 | bool ImageLoaderMachOCompressed::containsSymbol(const void* addr) const | |
641 | { | |
642 | const uint8_t* start = &fLinkEditBase[fDyldInfo->export_off]; | |
643 | const uint8_t* end = &start[fDyldInfo->export_size]; | |
644 | return ( (start <= addr) && (addr < end) ); | |
645 | } | |
646 | ||
647 | ||
2fd3f4e8 | 648 | uintptr_t ImageLoaderMachOCompressed::exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const |
39a8cd10 A |
649 | { |
650 | const uint8_t* exportNode = (uint8_t*)symbol; | |
651 | const uint8_t* exportTrieStart = fLinkEditBase + fDyldInfo->export_off; | |
652 | const uint8_t* exportTrieEnd = exportTrieStart + fDyldInfo->export_size; | |
653 | if ( (exportNode < exportTrieStart) || (exportNode > exportTrieEnd) ) | |
654 | throw "symbol is not in trie"; | |
412ebb8e | 655 | //dyld::log("exportedSymbolAddress(): node=%p, nodeOffset=0x%04X in %s\n", symbol, (int)((uint8_t*)symbol - exportTrieStart), this->getShortName()); |
19894a12 | 656 | uintptr_t flags = read_uleb128(exportNode, exportTrieEnd); |
2fd3f4e8 A |
657 | switch ( flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) { |
658 | case EXPORT_SYMBOL_FLAGS_KIND_REGULAR: | |
659 | if ( runResolver && (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) { | |
660 | // this node has a stub and resolver, run the resolver to get target address | |
661 | uintptr_t stub = read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; // skip over stub | |
662 | // <rdar://problem/10657737> interposing dylibs have the stub address as their replacee | |
19894a12 A |
663 | uintptr_t interposedStub = interposedAddress(context, stub, requestor); |
664 | if ( interposedStub != stub ) | |
665 | return interposedStub; | |
666 | // stub was not interposed, so run resolver | |
2fd3f4e8 A |
667 | typedef uintptr_t (*ResolverProc)(void); |
668 | ResolverProc resolver = (ResolverProc)(read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData); | |
669 | uintptr_t result = (*resolver)(); | |
670 | if ( context.verboseBind ) | |
671 | dyld::log("dyld: resolver at %p returned 0x%08lX\n", resolver, result); | |
672 | return result; | |
832b6fce | 673 | } |
2fd3f4e8 A |
674 | return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; |
675 | case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: | |
676 | if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) | |
19894a12 | 677 | dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, symbol); |
2fd3f4e8 A |
678 | return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; |
679 | case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: | |
680 | if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) | |
19894a12 | 681 | dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, symbol); |
2fd3f4e8 A |
682 | return read_uleb128(exportNode, exportTrieEnd); |
683 | default: | |
19894a12 | 684 | dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, symbol); |
412ebb8e | 685 | } |
39a8cd10 A |
686 | } |
687 | ||
688 | bool ImageLoaderMachOCompressed::exportedSymbolIsWeakDefintion(const Symbol* symbol) const | |
689 | { | |
690 | const uint8_t* exportNode = (uint8_t*)symbol; | |
691 | const uint8_t* exportTrieStart = fLinkEditBase + fDyldInfo->export_off; | |
692 | const uint8_t* exportTrieEnd = exportTrieStart + fDyldInfo->export_size; | |
693 | if ( (exportNode < exportTrieStart) || (exportNode > exportTrieEnd) ) | |
694 | throw "symbol is not in trie"; | |
19894a12 | 695 | uintptr_t flags = read_uleb128(exportNode, exportTrieEnd); |
39a8cd10 A |
696 | return ( flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ); |
697 | } | |
698 | ||
699 | ||
700 | const char* ImageLoaderMachOCompressed::exportedSymbolName(const Symbol* symbol) const | |
701 | { | |
702 | throw "NSNameOfSymbol() not supported with compressed LINKEDIT"; | |
703 | } | |
704 | ||
705 | unsigned int ImageLoaderMachOCompressed::exportedSymbolCount() const | |
706 | { | |
707 | throw "NSSymbolDefinitionCountInObjectFileImage() not supported with compressed LINKEDIT"; | |
708 | } | |
709 | ||
710 | const ImageLoader::Symbol* ImageLoaderMachOCompressed::exportedSymbolIndexed(unsigned int index) const | |
711 | { | |
712 | throw "NSSymbolDefinitionNameInObjectFileImage() not supported with compressed LINKEDIT"; | |
713 | } | |
714 | ||
715 | unsigned int ImageLoaderMachOCompressed::importedSymbolCount() const | |
716 | { | |
717 | throw "NSSymbolReferenceCountInObjectFileImage() not supported with compressed LINKEDIT"; | |
718 | } | |
719 | ||
720 | const ImageLoader::Symbol* ImageLoaderMachOCompressed::importedSymbolIndexed(unsigned int index) const | |
721 | { | |
722 | throw "NSSymbolReferenceCountInObjectFileImage() not supported with compressed LINKEDIT"; | |
723 | } | |
724 | ||
725 | const char* ImageLoaderMachOCompressed::importedSymbolName(const Symbol* symbol) const | |
726 | { | |
727 | throw "NSSymbolReferenceNameInObjectFileImage() not supported with compressed LINKEDIT"; | |
728 | } | |
729 | ||
730 | ||
731 | ||
412ebb8e A |
732 | uintptr_t ImageLoaderMachOCompressed::resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import, |
733 | bool runResolver, const ImageLoader** foundIn) | |
39a8cd10 A |
734 | { |
735 | const Symbol* sym; | |
736 | if ( context.flatExportFinder(symbolName, &sym, foundIn) ) { | |
2fd3f4e8 A |
737 | if ( *foundIn != this ) |
738 | context.addDynamicReference(this, const_cast<ImageLoader*>(*foundIn)); | |
412ebb8e | 739 | return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver); |
39a8cd10 A |
740 | } |
741 | // if a bundle is loaded privately the above will not find its exports | |
742 | if ( this->isBundle() && this->hasHiddenExports() ) { | |
743 | // look in self for needed symbol | |
744 | sym = this->ImageLoaderMachO::findExportedSymbol(symbolName, false, foundIn); | |
745 | if ( sym != NULL ) | |
412ebb8e | 746 | return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver); |
39a8cd10 A |
747 | } |
748 | if ( weak_import ) { | |
749 | // definition can't be found anywhere, ok because it is weak, just return 0 | |
750 | return 0; | |
751 | } | |
19894a12 | 752 | throwSymbolNotFound(context, symbolName, this->getPath(), "", "flat namespace"); |
39a8cd10 A |
753 | } |
754 | ||
755 | ||
756 | uintptr_t ImageLoaderMachOCompressed::resolveTwolevel(const LinkContext& context, const ImageLoader* targetImage, bool weak_import, | |
412ebb8e | 757 | const char* symbolName, bool runResolver, const ImageLoader** foundIn) |
39a8cd10 A |
758 | { |
759 | // two level lookup | |
760 | const Symbol* sym = targetImage->findExportedSymbol(symbolName, true, foundIn); | |
761 | if ( sym != NULL ) { | |
412ebb8e | 762 | return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver); |
39a8cd10 A |
763 | } |
764 | ||
765 | if ( weak_import ) { | |
766 | // definition can't be found anywhere, ok because it is weak, just return 0 | |
767 | return 0; | |
768 | } | |
769 | ||
19894a12 A |
770 | // nowhere to be found, check if maybe this image is too new for this OS |
771 | char versMismatch[256]; | |
772 | versMismatch[0] = '\0'; | |
773 | uint32_t imageMinOS = this->minOSVersion(); | |
774 | // dyld is always built for the current OS, so we can get the current OS version | |
775 | // from the load command in dyld itself. | |
776 | extern const mach_header __dso_handle; | |
777 | uint32_t dyldMinOS = ImageLoaderMachO::minOSVersion(&__dso_handle); | |
778 | if ( imageMinOS > dyldMinOS ) { | |
779 | #if __MAC_OS_X_VERSION_MIN_REQUIRED | |
780 | const char* msg = dyld::mkstringf(" (which was built for Mac OS X %d.%d)", imageMinOS >> 16, (imageMinOS >> 8) & 0xFF); | |
781 | #else | |
782 | const char* msg = dyld::mkstringf(" (which was built for iOS %d.%d)", imageMinOS >> 16, (imageMinOS >> 8) & 0xFF); | |
783 | #endif | |
784 | strcpy(versMismatch, msg); | |
785 | ::free((void*)msg); | |
786 | } | |
787 | throwSymbolNotFound(context, symbolName, this->getPath(), versMismatch, targetImage->getPath()); | |
39a8cd10 A |
788 | } |
789 | ||
790 | ||
791 | uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const char* symbolName, | |
19894a12 | 792 | uint8_t symboFlags, long libraryOrdinal, const ImageLoader** targetImage, |
412ebb8e | 793 | LastLookup* last, bool runResolver) |
39a8cd10 A |
794 | { |
795 | *targetImage = NULL; | |
796 | ||
797 | // only clients that benefit from caching last lookup pass in a LastLookup struct | |
798 | if ( last != NULL ) { | |
799 | if ( (last->ordinal == libraryOrdinal) | |
800 | && (last->flags == symboFlags) | |
801 | && (last->name == symbolName) ) { | |
802 | *targetImage = last->foundIn; | |
803 | return last->result; | |
804 | } | |
805 | } | |
806 | ||
807 | bool weak_import = (symboFlags & BIND_SYMBOL_FLAGS_WEAK_IMPORT); | |
808 | uintptr_t symbolAddress; | |
809 | if ( context.bindFlat || (libraryOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP) ) { | |
412ebb8e | 810 | symbolAddress = this->resolveFlat(context, symbolName, weak_import, runResolver, targetImage); |
39a8cd10 A |
811 | } |
812 | else { | |
813 | if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) { | |
814 | *targetImage = context.mainExecutable; | |
815 | } | |
816 | else if ( libraryOrdinal == BIND_SPECIAL_DYLIB_SELF ) { | |
817 | *targetImage = this; | |
818 | } | |
819 | else if ( libraryOrdinal <= 0 ) { | |
19894a12 | 820 | dyld::throwf("bad mach-o binary, unknown special library ordinal (%ld) too big for symbol %s in %s", |
39a8cd10 A |
821 | libraryOrdinal, symbolName, this->getPath()); |
822 | } | |
823 | else if ( (unsigned)libraryOrdinal <= libraryCount() ) { | |
19894a12 | 824 | *targetImage = libImage((unsigned int)libraryOrdinal-1); |
39a8cd10 A |
825 | } |
826 | else { | |
19894a12 | 827 | dyld::throwf("bad mach-o binary, library ordinal (%ld) too big (max %u) for symbol %s in %s", |
39a8cd10 A |
828 | libraryOrdinal, libraryCount(), symbolName, this->getPath()); |
829 | } | |
830 | if ( *targetImage == NULL ) { | |
831 | if ( weak_import ) { | |
832 | // if target library not loaded and reference is weak or library is weak return 0 | |
833 | symbolAddress = 0; | |
834 | } | |
835 | else { | |
19894a12 | 836 | dyld::throwf("can't resolve symbol %s in %s because dependent dylib #%ld could not be loaded", |
39a8cd10 A |
837 | symbolName, this->getPath(), libraryOrdinal); |
838 | } | |
839 | } | |
840 | else { | |
412ebb8e | 841 | symbolAddress = resolveTwolevel(context, *targetImage, weak_import, symbolName, runResolver, targetImage); |
39a8cd10 A |
842 | } |
843 | } | |
844 | ||
845 | // save off lookup results if client wants | |
846 | if ( last != NULL ) { | |
847 | last->ordinal = libraryOrdinal; | |
848 | last->flags = symboFlags; | |
849 | last->name = symbolName; | |
850 | last->foundIn = *targetImage; | |
851 | last->result = symbolAddress; | |
852 | } | |
853 | ||
854 | return symbolAddress; | |
855 | } | |
856 | ||
857 | uintptr_t ImageLoaderMachOCompressed::bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, | |
19894a12 | 858 | uint8_t symboFlags, intptr_t addend, long libraryOrdinal, const char* msg, |
412ebb8e | 859 | LastLookup* last, bool runResolver) |
39a8cd10 A |
860 | { |
861 | const ImageLoader* targetImage; | |
862 | uintptr_t symbolAddress; | |
863 | ||
864 | // resolve symbol | |
412ebb8e | 865 | symbolAddress = this->resolve(context, symbolName, symboFlags, libraryOrdinal, &targetImage, last, runResolver); |
39a8cd10 A |
866 | |
867 | // do actual update | |
868 | return this->bindLocation(context, addr, symbolAddress, targetImage, type, symbolName, addend, msg); | |
869 | } | |
870 | ||
871 | void ImageLoaderMachOCompressed::throwBadBindingAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, | |
872 | const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos) | |
873 | { | |
874 | dyld::throwf("malformed binding opcodes (%ld/%ld): address 0x%08lX is beyond end of segment %s (0x%08lX -> 0x%08lX)", | |
875 | (intptr_t)(pos-startOpcodes), (intptr_t)(endOpcodes-startOpcodes), address, segName(segmentIndex), | |
876 | segActualLoadAddress(segmentIndex), segmentEndAddress); | |
877 | } | |
878 | ||
879 | ||
880 | void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLazysBound) | |
881 | { | |
412ebb8e A |
882 | CRSetCrashLogMessage2(this->getPath()); |
883 | ||
39a8cd10 A |
884 | // if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind |
885 | // note: flat-namespace binaries need to have imports rebound (even if correctly prebound) | |
886 | if ( this->usablePrebinding(context) ) { | |
887 | // don't need to bind | |
888 | } | |
889 | else { | |
412ebb8e A |
890 | |
891 | #if TEXT_RELOC_SUPPORT | |
892 | // if there are __TEXT fixups, temporarily make __TEXT writable | |
893 | if ( fTextSegmentBinds ) | |
894 | this->makeTextSegmentWritable(context, true); | |
895 | #endif | |
896 | ||
39a8cd10 A |
897 | // run through all binding opcodes |
898 | eachBind(context, &ImageLoaderMachOCompressed::bindAt); | |
899 | ||
412ebb8e A |
900 | #if TEXT_RELOC_SUPPORT |
901 | // if there were __TEXT fixups, restore write protection | |
902 | if ( fTextSegmentBinds ) | |
903 | this->makeTextSegmentWritable(context, false); | |
904 | #endif | |
905 | ||
906 | // if this image is in the shared cache, but depends on something no longer in the shared cache, | |
39a8cd10 | 907 | // there is no way to reset the lazy pointers, so force bind them now |
412ebb8e | 908 | if ( forceLazysBound || fInSharedCache ) |
39a8cd10 | 909 | this->doBindJustLazies(context); |
412ebb8e A |
910 | |
911 | // this image is in cache, but something below it is not. If | |
912 | // this image has lazy pointer to a resolver function, then | |
913 | // the stub may have been altered to point to a shared lazy pointer. | |
914 | if ( fInSharedCache ) | |
915 | this->updateOptimizedLazyPointers(context); | |
916 | ||
917 | // tell kernel we are done with chunks of LINKEDIT | |
918 | if ( !context.preFetchDisabled ) | |
919 | this->markFreeLINKEDIT(context); | |
39a8cd10 A |
920 | } |
921 | ||
922 | // set up dyld entry points in image | |
923 | // do last so flat main executables will have __dyld or __program_vars set up | |
924 | this->setupLazyPointerHandler(context); | |
412ebb8e | 925 | CRSetCrashLogMessage2(NULL); |
39a8cd10 A |
926 | } |
927 | ||
928 | ||
929 | void ImageLoaderMachOCompressed::doBindJustLazies(const LinkContext& context) | |
930 | { | |
931 | eachLazyBind(context, &ImageLoaderMachOCompressed::bindAt); | |
932 | } | |
933 | ||
934 | void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handler handler) | |
935 | { | |
936 | try { | |
937 | uint8_t type = 0; | |
938 | int segmentIndex = 0; | |
939 | uintptr_t address = segActualLoadAddress(0); | |
940 | uintptr_t segmentEndAddress = segActualEndAddress(0); | |
941 | const char* symbolName = NULL; | |
942 | uint8_t symboFlags = 0; | |
19894a12 | 943 | long libraryOrdinal = 0; |
39a8cd10 | 944 | intptr_t addend = 0; |
19894a12 A |
945 | uintptr_t count; |
946 | uintptr_t skip; | |
39a8cd10 A |
947 | LastLookup last = { 0, 0, NULL, 0, NULL }; |
948 | const uint8_t* const start = fLinkEditBase + fDyldInfo->bind_off; | |
949 | const uint8_t* const end = &start[fDyldInfo->bind_size]; | |
950 | const uint8_t* p = start; | |
951 | bool done = false; | |
952 | while ( !done && (p < end) ) { | |
953 | uint8_t immediate = *p & BIND_IMMEDIATE_MASK; | |
954 | uint8_t opcode = *p & BIND_OPCODE_MASK; | |
955 | ++p; | |
956 | switch (opcode) { | |
957 | case BIND_OPCODE_DONE: | |
958 | done = true; | |
959 | break; | |
960 | case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: | |
961 | libraryOrdinal = immediate; | |
962 | break; | |
963 | case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: | |
964 | libraryOrdinal = read_uleb128(p, end); | |
965 | break; | |
966 | case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: | |
967 | // the special ordinals are negative numbers | |
968 | if ( immediate == 0 ) | |
969 | libraryOrdinal = 0; | |
970 | else { | |
971 | int8_t signExtended = BIND_OPCODE_MASK | immediate; | |
972 | libraryOrdinal = signExtended; | |
973 | } | |
974 | break; | |
975 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: | |
976 | symbolName = (char*)p; | |
977 | symboFlags = immediate; | |
978 | while (*p != '\0') | |
979 | ++p; | |
980 | ++p; | |
981 | break; | |
982 | case BIND_OPCODE_SET_TYPE_IMM: | |
983 | type = immediate; | |
984 | break; | |
985 | case BIND_OPCODE_SET_ADDEND_SLEB: | |
986 | addend = read_sleb128(p, end); | |
987 | break; | |
988 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
989 | segmentIndex = immediate; | |
19894a12 A |
990 | if ( segmentIndex >= fSegmentsCount ) |
991 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", | |
992 | segmentIndex, fSegmentsCount-1); | |
39a8cd10 A |
993 | address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end); |
994 | segmentEndAddress = segActualEndAddress(segmentIndex); | |
995 | break; | |
996 | case BIND_OPCODE_ADD_ADDR_ULEB: | |
997 | address += read_uleb128(p, end); | |
998 | break; | |
999 | case BIND_OPCODE_DO_BIND: | |
1000 | if ( address >= segmentEndAddress ) | |
1001 | throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); | |
412ebb8e | 1002 | (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); |
39a8cd10 A |
1003 | address += sizeof(intptr_t); |
1004 | break; | |
1005 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: | |
1006 | if ( address >= segmentEndAddress ) | |
1007 | throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); | |
412ebb8e | 1008 | (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); |
39a8cd10 A |
1009 | address += read_uleb128(p, end) + sizeof(intptr_t); |
1010 | break; | |
1011 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: | |
1012 | if ( address >= segmentEndAddress ) | |
1013 | throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); | |
412ebb8e | 1014 | (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); |
39a8cd10 A |
1015 | address += immediate*sizeof(intptr_t) + sizeof(intptr_t); |
1016 | break; | |
1017 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: | |
1018 | count = read_uleb128(p, end); | |
1019 | skip = read_uleb128(p, end); | |
1020 | for (uint32_t i=0; i < count; ++i) { | |
1021 | if ( address >= segmentEndAddress ) | |
1022 | throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); | |
412ebb8e | 1023 | (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); |
39a8cd10 A |
1024 | address += skip + sizeof(intptr_t); |
1025 | } | |
1026 | break; | |
1027 | default: | |
1028 | dyld::throwf("bad bind opcode %d in bind info", *p); | |
1029 | } | |
1030 | } | |
1031 | } | |
1032 | catch (const char* msg) { | |
1033 | const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); | |
1034 | free((void*)msg); | |
1035 | throw newMsg; | |
1036 | } | |
1037 | } | |
1038 | ||
1039 | void ImageLoaderMachOCompressed::eachLazyBind(const LinkContext& context, bind_handler handler) | |
1040 | { | |
1041 | try { | |
1042 | uint8_t type = BIND_TYPE_POINTER; | |
1043 | int segmentIndex = 0; | |
1044 | uintptr_t address = segActualLoadAddress(0); | |
1045 | uintptr_t segmentEndAddress = segActualEndAddress(0); | |
1046 | const char* symbolName = NULL; | |
1047 | uint8_t symboFlags = 0; | |
19894a12 | 1048 | long libraryOrdinal = 0; |
39a8cd10 A |
1049 | intptr_t addend = 0; |
1050 | const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off; | |
1051 | const uint8_t* const end = &start[fDyldInfo->lazy_bind_size]; | |
1052 | const uint8_t* p = start; | |
1053 | bool done = false; | |
1054 | while ( !done && (p < end) ) { | |
1055 | uint8_t immediate = *p & BIND_IMMEDIATE_MASK; | |
1056 | uint8_t opcode = *p & BIND_OPCODE_MASK; | |
1057 | ++p; | |
1058 | switch (opcode) { | |
1059 | case BIND_OPCODE_DONE: | |
1060 | // there is BIND_OPCODE_DONE at end of each lazy bind, don't stop until end of whole sequence | |
1061 | break; | |
1062 | case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: | |
1063 | libraryOrdinal = immediate; | |
1064 | break; | |
1065 | case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: | |
1066 | libraryOrdinal = read_uleb128(p, end); | |
1067 | break; | |
1068 | case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: | |
1069 | // the special ordinals are negative numbers | |
1070 | if ( immediate == 0 ) | |
1071 | libraryOrdinal = 0; | |
1072 | else { | |
1073 | int8_t signExtended = BIND_OPCODE_MASK | immediate; | |
1074 | libraryOrdinal = signExtended; | |
1075 | } | |
1076 | break; | |
1077 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: | |
1078 | symbolName = (char*)p; | |
1079 | symboFlags = immediate; | |
1080 | while (*p != '\0') | |
1081 | ++p; | |
1082 | ++p; | |
1083 | break; | |
1084 | case BIND_OPCODE_SET_TYPE_IMM: | |
1085 | type = immediate; | |
1086 | break; | |
1087 | case BIND_OPCODE_SET_ADDEND_SLEB: | |
1088 | addend = read_sleb128(p, end); | |
1089 | break; | |
1090 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
1091 | segmentIndex = immediate; | |
19894a12 A |
1092 | if ( segmentIndex >= fSegmentsCount ) |
1093 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", | |
1094 | segmentIndex, fSegmentsCount-1); | |
39a8cd10 A |
1095 | address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end); |
1096 | segmentEndAddress = segActualEndAddress(segmentIndex); | |
1097 | break; | |
1098 | case BIND_OPCODE_ADD_ADDR_ULEB: | |
1099 | address += read_uleb128(p, end); | |
1100 | break; | |
1101 | case BIND_OPCODE_DO_BIND: | |
1102 | if ( address >= segmentEndAddress ) | |
1103 | throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); | |
2fd3f4e8 | 1104 | (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "forced lazy ", NULL, false); |
39a8cd10 A |
1105 | address += sizeof(intptr_t); |
1106 | break; | |
1107 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: | |
1108 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: | |
1109 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: | |
1110 | default: | |
1111 | dyld::throwf("bad lazy bind opcode %d", *p); | |
1112 | } | |
1113 | } | |
1114 | } | |
1115 | ||
1116 | catch (const char* msg) { | |
1117 | const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); | |
1118 | free((void*)msg); | |
1119 | throw newMsg; | |
1120 | } | |
1121 | } | |
1122 | ||
1123 | // A program built targeting 10.5 will have hybrid stubs. When used with weak symbols | |
1124 | // the classic lazy loader is used even when running on 10.6 | |
1125 | uintptr_t ImageLoaderMachOCompressed::doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) | |
1126 | { | |
1127 | // only works with compressed LINKEDIT if classic symbol table is also present | |
1128 | const macho_nlist* symbolTable = NULL; | |
1129 | const char* symbolTableStrings = NULL; | |
1130 | const dysymtab_command* dynSymbolTable = NULL; | |
1131 | const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; | |
1132 | const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; | |
1133 | const struct load_command* cmd = cmds; | |
1134 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
1135 | switch (cmd->cmd) { | |
1136 | case LC_SYMTAB: | |
1137 | { | |
1138 | const struct symtab_command* symtab = (struct symtab_command*)cmd; | |
1139 | symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff]; | |
1140 | symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]); | |
1141 | } | |
1142 | break; | |
1143 | case LC_DYSYMTAB: | |
1144 | dynSymbolTable = (struct dysymtab_command*)cmd; | |
1145 | break; | |
1146 | } | |
1147 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
1148 | } | |
1149 | // no symbol table => no lookup by address | |
1150 | if ( (symbolTable == NULL) || (dynSymbolTable == NULL) ) | |
1151 | dyld::throwf("classic lazy binding used with compressed LINKEDIT at %p in image %s", lazyPointer, this->getPath()); | |
1152 | ||
1153 | // scan for all lazy-pointer sections | |
1154 | const bool twoLevel = this->usesTwoLevelNameSpace(); | |
1155 | const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff]; | |
1156 | cmd = cmds; | |
1157 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
1158 | switch (cmd->cmd) { | |
1159 | case LC_SEGMENT_COMMAND: | |
1160 | { | |
1161 | const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; | |
1162 | const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); | |
1163 | const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; | |
1164 | for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { | |
1165 | const uint8_t type = sect->flags & SECTION_TYPE; | |
1166 | uint32_t symbolIndex = INDIRECT_SYMBOL_LOCAL; | |
1167 | if ( type == S_LAZY_SYMBOL_POINTERS ) { | |
19894a12 | 1168 | const size_t pointerCount = sect->size / sizeof(uintptr_t); |
39a8cd10 A |
1169 | uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); |
1170 | if ( (lazyPointer >= symbolPointers) && (lazyPointer < &symbolPointers[pointerCount]) ) { | |
1171 | const uint32_t indirectTableOffset = sect->reserved1; | |
19894a12 | 1172 | const size_t lazyIndex = lazyPointer - symbolPointers; |
39a8cd10 A |
1173 | symbolIndex = indirectTable[indirectTableOffset + lazyIndex]; |
1174 | } | |
1175 | } | |
1176 | if ( (symbolIndex != INDIRECT_SYMBOL_ABS) && (symbolIndex != INDIRECT_SYMBOL_LOCAL) ) { | |
1177 | const macho_nlist* symbol = &symbolTable[symbolIndex]; | |
1178 | const char* symbolName = &symbolTableStrings[symbol->n_un.n_strx]; | |
1179 | int libraryOrdinal = GET_LIBRARY_ORDINAL(symbol->n_desc); | |
1180 | if ( !twoLevel || context.bindFlat ) | |
1181 | libraryOrdinal = BIND_SPECIAL_DYLIB_FLAT_LOOKUP; | |
1182 | uintptr_t ptrToBind = (uintptr_t)lazyPointer; | |
1183 | uintptr_t symbolAddr = bindAt(context, ptrToBind, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL); | |
1184 | ++fgTotalLazyBindFixups; | |
1185 | return symbolAddr; | |
1186 | } | |
1187 | } | |
1188 | } | |
1189 | break; | |
1190 | } | |
1191 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
1192 | } | |
1193 | dyld::throwf("lazy pointer not found at address %p in image %s", lazyPointer, this->getPath()); | |
1194 | } | |
1195 | ||
1196 | ||
412ebb8e A |
1197 | uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, |
1198 | void (*lock)(), void (*unlock)()) | |
39a8cd10 | 1199 | { |
412ebb8e A |
1200 | // <rdar://problem/8663923> race condition with flat-namespace lazy binding |
1201 | if ( this->usesTwoLevelNameSpace() ) { | |
1202 | // two-level namespace lookup does not require lock because dependents can't be unloaded before this image | |
1203 | } | |
1204 | else { | |
1205 | // acquire dyld global lock | |
1206 | if ( lock != NULL ) | |
1207 | lock(); | |
1208 | } | |
1209 | ||
39a8cd10 A |
1210 | const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off; |
1211 | const uint8_t* const end = &start[fDyldInfo->lazy_bind_size]; | |
412ebb8e A |
1212 | if ( lazyBindingInfoOffset > fDyldInfo->lazy_bind_size ) { |
1213 | dyld::throwf("fast lazy bind offset out of range (%u, max=%u) in image %s", | |
1214 | lazyBindingInfoOffset, fDyldInfo->lazy_bind_size, this->getPath()); | |
1215 | } | |
39a8cd10 A |
1216 | |
1217 | uint8_t type = BIND_TYPE_POINTER; | |
1218 | uintptr_t address = 0; | |
1219 | const char* symbolName = NULL; | |
1220 | uint8_t symboFlags = 0; | |
19894a12 | 1221 | long libraryOrdinal = 0; |
39a8cd10 A |
1222 | bool done = false; |
1223 | uintptr_t result = 0; | |
1224 | const uint8_t* p = &start[lazyBindingInfoOffset]; | |
1225 | while ( !done && (p < end) ) { | |
1226 | uint8_t immediate = *p & BIND_IMMEDIATE_MASK; | |
1227 | uint8_t opcode = *p & BIND_OPCODE_MASK; | |
1228 | ++p; | |
1229 | switch (opcode) { | |
1230 | case BIND_OPCODE_DONE: | |
1231 | done = true; | |
1232 | break; | |
1233 | case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: | |
1234 | libraryOrdinal = immediate; | |
1235 | break; | |
1236 | case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: | |
1237 | libraryOrdinal = read_uleb128(p, end); | |
1238 | break; | |
1239 | case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: | |
1240 | // the special ordinals are negative numbers | |
1241 | if ( immediate == 0 ) | |
1242 | libraryOrdinal = 0; | |
1243 | else { | |
1244 | int8_t signExtended = BIND_OPCODE_MASK | immediate; | |
1245 | libraryOrdinal = signExtended; | |
1246 | } | |
1247 | break; | |
1248 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: | |
1249 | symbolName = (char*)p; | |
1250 | symboFlags = immediate; | |
1251 | while (*p != '\0') | |
1252 | ++p; | |
1253 | ++p; | |
1254 | break; | |
1255 | case BIND_OPCODE_SET_TYPE_IMM: | |
1256 | type = immediate; | |
1257 | break; | |
1258 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
19894a12 A |
1259 | if ( immediate >= fSegmentsCount ) |
1260 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", | |
1261 | immediate, fSegmentsCount-1); | |
39a8cd10 A |
1262 | address = segActualLoadAddress(immediate) + read_uleb128(p, end); |
1263 | break; | |
1264 | case BIND_OPCODE_DO_BIND: | |
412ebb8e A |
1265 | |
1266 | ||
1267 | result = this->bindAt(context, address, type, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL, true); | |
39a8cd10 A |
1268 | break; |
1269 | case BIND_OPCODE_SET_ADDEND_SLEB: | |
1270 | case BIND_OPCODE_ADD_ADDR_ULEB: | |
1271 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: | |
1272 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: | |
1273 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: | |
1274 | default: | |
1275 | dyld::throwf("bad lazy bind opcode %d", *p); | |
1276 | } | |
1277 | } | |
412ebb8e A |
1278 | |
1279 | if ( !this->usesTwoLevelNameSpace() ) { | |
1280 | // release dyld global lock | |
1281 | if ( unlock != NULL ) | |
1282 | unlock(); | |
1283 | } | |
39a8cd10 A |
1284 | return result; |
1285 | } | |
1286 | ||
1287 | void ImageLoaderMachOCompressed::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder) | |
1288 | { | |
1289 | it.image = this; | |
1290 | it.symbolName = " "; | |
1291 | it.loadOrder = loadOrder; | |
1292 | it.weakSymbol = false; | |
1293 | it.symbolMatches = false; | |
1294 | it.done = false; | |
1295 | it.curIndex = 0; | |
1296 | it.endIndex = this->fDyldInfo->weak_bind_size; | |
1297 | it.address = 0; | |
1298 | it.type = 0; | |
1299 | it.addend = 0; | |
1300 | } | |
1301 | ||
1302 | ||
1303 | bool ImageLoaderMachOCompressed::incrementCoalIterator(CoalIterator& it) | |
1304 | { | |
1305 | if ( it.done ) | |
1306 | return false; | |
1307 | ||
1308 | if ( this->fDyldInfo->weak_bind_size == 0 ) { | |
1309 | /// hmmm, ld set MH_WEAK_DEFINES or MH_BINDS_TO_WEAK, but there is no weak binding info | |
1310 | it.done = true; | |
1311 | it.symbolName = "~~~"; | |
1312 | return true; | |
1313 | } | |
1314 | const uint8_t* start = fLinkEditBase + fDyldInfo->weak_bind_off; | |
1315 | const uint8_t* p = start + it.curIndex; | |
1316 | const uint8_t* end = fLinkEditBase + fDyldInfo->weak_bind_off + this->fDyldInfo->weak_bind_size; | |
19894a12 A |
1317 | uintptr_t count; |
1318 | uintptr_t skip; | |
39a8cd10 A |
1319 | while ( p < end ) { |
1320 | uint8_t immediate = *p & BIND_IMMEDIATE_MASK; | |
1321 | uint8_t opcode = *p & BIND_OPCODE_MASK; | |
1322 | ++p; | |
1323 | switch (opcode) { | |
1324 | case BIND_OPCODE_DONE: | |
1325 | it.done = true; | |
1326 | it.curIndex = p - start; | |
1327 | it.symbolName = "~~~"; // sorts to end | |
1328 | return true; | |
1329 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: | |
1330 | it.symbolName = (char*)p; | |
1331 | it.weakSymbol = ((immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) == 0); | |
1332 | it.symbolMatches = false; | |
1333 | while (*p != '\0') | |
1334 | ++p; | |
1335 | ++p; | |
1336 | it.curIndex = p - start; | |
1337 | return false; | |
1338 | case BIND_OPCODE_SET_TYPE_IMM: | |
1339 | it.type = immediate; | |
1340 | break; | |
1341 | case BIND_OPCODE_SET_ADDEND_SLEB: | |
1342 | it.addend = read_sleb128(p, end); | |
1343 | break; | |
1344 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
19894a12 A |
1345 | if ( immediate >= fSegmentsCount ) |
1346 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", | |
1347 | immediate, fSegmentsCount-1); | |
39a8cd10 A |
1348 | it.address = segActualLoadAddress(immediate) + read_uleb128(p, end); |
1349 | break; | |
1350 | case BIND_OPCODE_ADD_ADDR_ULEB: | |
1351 | it.address += read_uleb128(p, end); | |
1352 | break; | |
1353 | case BIND_OPCODE_DO_BIND: | |
1354 | it.address += sizeof(intptr_t); | |
1355 | break; | |
1356 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: | |
1357 | it.address += read_uleb128(p, end) + sizeof(intptr_t); | |
1358 | break; | |
1359 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: | |
1360 | it.address += immediate*sizeof(intptr_t) + sizeof(intptr_t); | |
1361 | break; | |
1362 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: | |
1363 | count = read_uleb128(p, end); | |
1364 | skip = read_uleb128(p, end); | |
1365 | for (uint32_t i=0; i < count; ++i) { | |
1366 | it.address += skip + sizeof(intptr_t); | |
1367 | } | |
1368 | break; | |
1369 | default: | |
2fd3f4e8 | 1370 | dyld::throwf("bad weak bind opcode '%d' found after processing %d bytes in '%s'", *p, (int)(p-start), this->getPath()); |
39a8cd10 A |
1371 | } |
1372 | } | |
1373 | /// hmmm, BIND_OPCODE_DONE is missing... | |
1374 | it.done = true; | |
1375 | it.symbolName = "~~~"; | |
1376 | //dyld::log("missing BIND_OPCODE_DONE for image %s\n", this->getPath()); | |
1377 | return true; | |
1378 | } | |
1379 | ||
1380 | uintptr_t ImageLoaderMachOCompressed::getAddressCoalIterator(CoalIterator& it, const LinkContext& context) | |
1381 | { | |
1382 | //dyld::log("looking for %s in %s\n", it.symbolName, this->getPath()); | |
1383 | const ImageLoader* foundIn = NULL; | |
1384 | const ImageLoader::Symbol* sym = this->findExportedSymbol(it.symbolName, &foundIn); | |
1385 | if ( sym != NULL ) { | |
1386 | //dyld::log("sym=%p, foundIn=%p\n", sym, foundIn); | |
1387 | return foundIn->getExportedSymbolAddress(sym, context, this); | |
1388 | } | |
1389 | return 0; | |
1390 | } | |
1391 | ||
1392 | ||
1393 | void ImageLoaderMachOCompressed::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, const LinkContext& context) | |
1394 | { | |
1395 | // <rdar://problem/6570879> weak binding done too early with inserted libraries | |
1396 | if ( this->getState() < dyld_image_state_bound ) | |
1397 | return; | |
1398 | ||
1399 | const uint8_t* start = fLinkEditBase + fDyldInfo->weak_bind_off; | |
1400 | const uint8_t* p = start + it.curIndex; | |
1401 | const uint8_t* end = fLinkEditBase + fDyldInfo->weak_bind_off + this->fDyldInfo->weak_bind_size; | |
1402 | ||
1403 | uint8_t type = it.type; | |
1404 | uintptr_t address = it.address; | |
1405 | const char* symbolName = it.symbolName; | |
1406 | intptr_t addend = it.addend; | |
19894a12 A |
1407 | uintptr_t count; |
1408 | uintptr_t skip; | |
39a8cd10 A |
1409 | bool done = false; |
1410 | bool boundSomething = false; | |
1411 | while ( !done && (p < end) ) { | |
1412 | uint8_t immediate = *p & BIND_IMMEDIATE_MASK; | |
1413 | uint8_t opcode = *p & BIND_OPCODE_MASK; | |
1414 | ++p; | |
1415 | switch (opcode) { | |
1416 | case BIND_OPCODE_DONE: | |
1417 | done = true; | |
1418 | break; | |
1419 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: | |
1420 | done = true; | |
1421 | break; | |
1422 | case BIND_OPCODE_SET_TYPE_IMM: | |
1423 | type = immediate; | |
1424 | break; | |
1425 | case BIND_OPCODE_SET_ADDEND_SLEB: | |
1426 | addend = read_sleb128(p, end); | |
1427 | break; | |
1428 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
19894a12 A |
1429 | if ( immediate >= fSegmentsCount ) |
1430 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", | |
1431 | immediate, fSegmentsCount-1); | |
39a8cd10 A |
1432 | address = segActualLoadAddress(immediate) + read_uleb128(p, end); |
1433 | break; | |
1434 | case BIND_OPCODE_ADD_ADDR_ULEB: | |
1435 | address += read_uleb128(p, end); | |
1436 | break; | |
1437 | case BIND_OPCODE_DO_BIND: | |
1438 | bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak "); | |
1439 | boundSomething = true; | |
1440 | address += sizeof(intptr_t); | |
1441 | break; | |
1442 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: | |
1443 | bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak "); | |
1444 | boundSomething = true; | |
1445 | address += read_uleb128(p, end) + sizeof(intptr_t); | |
1446 | break; | |
1447 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: | |
1448 | bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak "); | |
1449 | boundSomething = true; | |
1450 | address += immediate*sizeof(intptr_t) + sizeof(intptr_t); | |
1451 | break; | |
1452 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: | |
1453 | count = read_uleb128(p, end); | |
1454 | skip = read_uleb128(p, end); | |
1455 | for (uint32_t i=0; i < count; ++i) { | |
1456 | bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak "); | |
1457 | boundSomething = true; | |
1458 | address += skip + sizeof(intptr_t); | |
1459 | } | |
1460 | break; | |
1461 | default: | |
1462 | dyld::throwf("bad bind opcode %d in weak binding info", *p); | |
1463 | } | |
2fd3f4e8 A |
1464 | } |
1465 | // C++ weak coalescing cannot be tracked by reference counting. Error on side of never unloading. | |
1466 | if ( boundSomething && (targetImage != this) ) | |
1467 | context.addDynamicReference(this, targetImage); | |
39a8cd10 A |
1468 | } |
1469 | ||
412ebb8e | 1470 | uintptr_t ImageLoaderMachOCompressed::interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*, |
19894a12 | 1471 | uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver) |
412ebb8e A |
1472 | { |
1473 | if ( type == BIND_TYPE_POINTER ) { | |
1474 | uintptr_t* fixupLocation = (uintptr_t*)addr; | |
19894a12 A |
1475 | uintptr_t curValue = *fixupLocation; |
1476 | uintptr_t newValue = interposedAddress(context, curValue, this); | |
1477 | if ( newValue != curValue) | |
1478 | *fixupLocation = newValue; | |
412ebb8e A |
1479 | } |
1480 | return 0; | |
1481 | } | |
1482 | ||
1483 | void ImageLoaderMachOCompressed::doInterpose(const LinkContext& context) | |
1484 | { | |
1485 | if ( context.verboseInterposing ) | |
1486 | dyld::log("dyld: interposing %lu tuples onto image: %s\n", fgInterposingTuples.size(), this->getPath()); | |
1487 | ||
1488 | // update prebound symbols | |
1489 | eachBind(context, &ImageLoaderMachOCompressed::interposeAt); | |
1490 | eachLazyBind(context, &ImageLoaderMachOCompressed::interposeAt); | |
1491 | } | |
39a8cd10 A |
1492 | |
1493 | ||
19894a12 A |
1494 | uintptr_t ImageLoaderMachOCompressed::dynamicInterposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, |
1495 | uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver) | |
1496 | { | |
1497 | if ( type == BIND_TYPE_POINTER ) { | |
1498 | uintptr_t* fixupLocation = (uintptr_t*)addr; | |
1499 | uintptr_t value = *fixupLocation; | |
1500 | // don't apply interposing to table entries. | |
1501 | if ( (context.dynamicInterposeArray <= (void*)addr) && ((void*)addr < &context.dynamicInterposeArray[context.dynamicInterposeCount]) ) | |
1502 | return 0; | |
1503 | for(size_t i=0; i < context.dynamicInterposeCount; ++i) { | |
1504 | if ( value == (uintptr_t)context.dynamicInterposeArray[i].replacee ) { | |
1505 | if ( context.verboseInterposing ) { | |
1506 | dyld::log("dyld: dynamic interposing: at %p replace %p with %p in %s\n", | |
1507 | fixupLocation, context.dynamicInterposeArray[i].replacee, context.dynamicInterposeArray[i].replacement, this->getPath()); | |
1508 | } | |
1509 | *fixupLocation = (uintptr_t)context.dynamicInterposeArray[i].replacement; | |
1510 | } | |
1511 | } | |
1512 | } | |
1513 | return 0; | |
1514 | } | |
1515 | ||
1516 | void ImageLoaderMachOCompressed::dynamicInterpose(const LinkContext& context) | |
1517 | { | |
1518 | if ( context.verboseInterposing ) | |
1519 | dyld::log("dyld: dynamic interposing %lu tuples onto image: %s\n", context.dynamicInterposeCount, this->getPath()); | |
1520 | ||
1521 | // update already bound references to symbols | |
1522 | eachBind(context, &ImageLoaderMachOCompressed::dynamicInterposeAt); | |
1523 | eachLazyBind(context, &ImageLoaderMachOCompressed::dynamicInterposeAt); | |
1524 | } | |
39a8cd10 A |
1525 | |
1526 | ||
1527 | const char* ImageLoaderMachOCompressed::findClosestSymbol(const void* addr, const void** closestAddr) const | |
1528 | { | |
1529 | // called by dladdr() | |
1530 | // only works with compressed LINKEDIT if classic symbol table is also present | |
1531 | const macho_nlist* symbolTable = NULL; | |
1532 | const char* symbolTableStrings = NULL; | |
1533 | const dysymtab_command* dynSymbolTable = NULL; | |
1534 | const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; | |
1535 | const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; | |
1536 | const struct load_command* cmd = cmds; | |
1537 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
1538 | switch (cmd->cmd) { | |
1539 | case LC_SYMTAB: | |
1540 | { | |
1541 | const struct symtab_command* symtab = (struct symtab_command*)cmd; | |
1542 | symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff]; | |
1543 | symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]); | |
1544 | } | |
1545 | break; | |
1546 | case LC_DYSYMTAB: | |
1547 | dynSymbolTable = (struct dysymtab_command*)cmd; | |
1548 | break; | |
1549 | } | |
1550 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
1551 | } | |
1552 | // no symbol table => no lookup by address | |
1553 | if ( (symbolTable == NULL) || (dynSymbolTable == NULL) ) | |
1554 | return NULL; | |
1555 | ||
1556 | uintptr_t targetAddress = (uintptr_t)addr - fSlide; | |
1557 | const struct macho_nlist* bestSymbol = NULL; | |
1558 | // first walk all global symbols | |
1559 | const struct macho_nlist* const globalsStart = &symbolTable[dynSymbolTable->iextdefsym]; | |
1560 | const struct macho_nlist* const globalsEnd= &globalsStart[dynSymbolTable->nextdefsym]; | |
1561 | for (const struct macho_nlist* s = globalsStart; s < globalsEnd; ++s) { | |
1562 | if ( (s->n_type & N_TYPE) == N_SECT ) { | |
1563 | if ( bestSymbol == NULL ) { | |
1564 | if ( s->n_value <= targetAddress ) | |
1565 | bestSymbol = s; | |
1566 | } | |
1567 | else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { | |
1568 | bestSymbol = s; | |
1569 | } | |
1570 | } | |
1571 | } | |
1572 | // next walk all local symbols | |
1573 | const struct macho_nlist* const localsStart = &symbolTable[dynSymbolTable->ilocalsym]; | |
1574 | const struct macho_nlist* const localsEnd= &localsStart[dynSymbolTable->nlocalsym]; | |
1575 | for (const struct macho_nlist* s = localsStart; s < localsEnd; ++s) { | |
1576 | if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) { | |
1577 | if ( bestSymbol == NULL ) { | |
1578 | if ( s->n_value <= targetAddress ) | |
1579 | bestSymbol = s; | |
1580 | } | |
1581 | else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { | |
1582 | bestSymbol = s; | |
1583 | } | |
1584 | } | |
1585 | } | |
1586 | if ( bestSymbol != NULL ) { | |
412ebb8e A |
1587 | #if __arm__ |
1588 | if (bestSymbol->n_desc & N_ARM_THUMB_DEF) | |
1589 | *closestAddr = (void*)((bestSymbol->n_value | 1) + fSlide); | |
1590 | else | |
1591 | *closestAddr = (void*)(bestSymbol->n_value + fSlide); | |
1592 | #else | |
39a8cd10 | 1593 | *closestAddr = (void*)(bestSymbol->n_value + fSlide); |
412ebb8e | 1594 | #endif |
39a8cd10 A |
1595 | return &symbolTableStrings[bestSymbol->n_un.n_strx]; |
1596 | } | |
1597 | return NULL; | |
1598 | } | |
1599 | ||
1600 | ||
1601 | #if PREBOUND_IMAGE_SUPPORT | |
1602 | void ImageLoaderMachOCompressed::resetPreboundLazyPointers(const LinkContext& context) | |
1603 | { | |
1604 | // no way to back off a prebound compress image | |
1605 | } | |
1606 | #endif | |
1607 | ||
412ebb8e A |
1608 | |
1609 | #if __arm__ || __x86_64__ | |
2fd3f4e8 | 1610 | void ImageLoaderMachOCompressed::updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr, const LinkContext& context) |
412ebb8e A |
1611 | { |
1612 | #if __arm__ | |
1613 | uint32_t* instructions = (uint32_t*)stub; | |
1614 | // sanity check this is a stub we understand | |
1615 | if ( (instructions[0] != 0xe59fc004) || (instructions[1] != 0xe08fc00c) || (instructions[2] != 0xe59cf000) ) | |
1616 | return; | |
1617 | ||
1618 | void** lazyPointerAddr = (void**)(instructions[3] + (stub + 12)); | |
1619 | #endif | |
1620 | #if __x86_64__ | |
1621 | // sanity check this is a stub we understand | |
1622 | if ( (stub[0] != 0xFF) || (stub[1] != 0x25) ) | |
1623 | return; | |
1624 | int32_t ripOffset = *((int32_t*)(&stub[2])); | |
1625 | void** lazyPointerAddr = (void**)(ripOffset + stub + 6); | |
1626 | #endif | |
1627 | ||
1628 | // if stub does not use original lazy pointer (meaning it was optimized by update_dyld_shared_cache) | |
1629 | if ( lazyPointerAddr != originalLazyPointerAddr ) { | |
2fd3f4e8 A |
1630 | // <rdar://problem/12928448> only de-optimization lazy pointers if they are part of shared cache not loaded (because overridden) |
1631 | const ImageLoader* lazyPointerImage = context.findImageContainingAddress(lazyPointerAddr); | |
1632 | if ( lazyPointerImage != NULL ) | |
1633 | return; | |
1634 | ||
412ebb8e A |
1635 | // copy newly re-bound lazy pointer value to shared lazy pointer |
1636 | *lazyPointerAddr = *originalLazyPointerAddr; | |
2fd3f4e8 A |
1637 | |
1638 | if ( context.verboseBind ) | |
1639 | dyld::log("dyld: alter bind: %s: *0x%08lX = 0x%08lX \n", | |
1640 | this->getShortName(), (long)lazyPointerAddr, (long)*originalLazyPointerAddr); | |
412ebb8e A |
1641 | } |
1642 | } | |
1643 | #endif | |
1644 | ||
1645 | ||
1646 | // <rdar://problem/8890875> overriding shared cache dylibs with resolvers fails | |
1647 | void ImageLoaderMachOCompressed::updateOptimizedLazyPointers(const LinkContext& context) | |
1648 | { | |
1649 | #if __arm__ || __x86_64__ | |
1650 | // find stubs and lazy pointer sections | |
1651 | const struct macho_section* stubsSection = NULL; | |
1652 | const struct macho_section* lazyPointerSection = NULL; | |
1653 | const dysymtab_command* dynSymbolTable = NULL; | |
1654 | const macho_header* mh = (macho_header*)fMachOData; | |
1655 | const uint32_t cmd_count = mh->ncmds; | |
1656 | const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; | |
1657 | const struct load_command* cmd = cmds; | |
1658 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
1659 | if (cmd->cmd == LC_SEGMENT_COMMAND) { | |
1660 | const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; | |
1661 | const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); | |
1662 | const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; | |
1663 | for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { | |
1664 | const uint8_t type = sect->flags & SECTION_TYPE; | |
1665 | if ( type == S_SYMBOL_STUBS ) | |
1666 | stubsSection = sect; | |
1667 | else if ( type == S_LAZY_SYMBOL_POINTERS ) | |
1668 | lazyPointerSection = sect; | |
1669 | } | |
1670 | } | |
1671 | else if ( cmd->cmd == LC_DYSYMTAB ) { | |
1672 | dynSymbolTable = (struct dysymtab_command*)cmd; | |
1673 | } | |
1674 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
1675 | } | |
1676 | ||
1677 | // sanity check | |
1678 | if ( dynSymbolTable == NULL ) | |
1679 | return; | |
1680 | if ( (stubsSection == NULL) || (lazyPointerSection == NULL) ) | |
1681 | return; | |
1682 | const uint32_t stubsCount = stubsSection->size / stubsSection->reserved2; | |
1683 | const uint32_t lazyPointersCount = lazyPointerSection->size / sizeof(void*); | |
1684 | if ( stubsCount != lazyPointersCount ) | |
1685 | return; | |
1686 | const uint32_t stubsIndirectTableOffset = stubsSection->reserved1; | |
1687 | const uint32_t lazyPointersIndirectTableOffset = lazyPointerSection->reserved1; | |
1688 | if ( (stubsIndirectTableOffset+stubsCount) > dynSymbolTable->nindirectsyms ) | |
1689 | return; | |
1690 | if ( (lazyPointersIndirectTableOffset+lazyPointersCount) > dynSymbolTable->nindirectsyms ) | |
1691 | return; | |
1692 | ||
1693 | // walk stubs and lazy pointers | |
1694 | const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff]; | |
1695 | void** const lazyPointersStartAddr = (void**)(lazyPointerSection->addr + this->fSlide); | |
1696 | uint8_t* const stubsStartAddr = (uint8_t*)(stubsSection->addr + this->fSlide); | |
1697 | uint8_t* stub = stubsStartAddr; | |
1698 | void** lpa = lazyPointersStartAddr; | |
1699 | for(uint32_t i=0; i < stubsCount; ++i, stub += stubsSection->reserved2, ++lpa) { | |
1700 | // sanity check symbol index of stub and lazy pointer match | |
1701 | if ( indirectTable[stubsIndirectTableOffset+i] != indirectTable[lazyPointersIndirectTableOffset+i] ) | |
1702 | continue; | |
2fd3f4e8 | 1703 | this->updateAlternateLazyPointer(stub, lpa, context); |
412ebb8e A |
1704 | } |
1705 | ||
1706 | #endif | |
1707 | } | |
1708 | ||
1709 | ||
df9d6cf7 A |
1710 | void ImageLoaderMachOCompressed::registerEncryption(const encryption_info_command* encryptCmd, const LinkContext& context) |
1711 | { | |
1712 | #if __arm__ || __arm64__ | |
1713 | if ( encryptCmd == NULL ) | |
1714 | return; | |
1715 | const mach_header* mh = NULL; | |
1716 | for(unsigned int i=0; i < fSegmentsCount; ++i) { | |
1717 | if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) ) { | |
1718 | mh = (mach_header*)segActualLoadAddress(i); | |
1719 | break; | |
1720 | } | |
1721 | } | |
1722 | void* start = ((uint8_t*)mh) + encryptCmd->cryptoff; | |
1723 | size_t len = encryptCmd->cryptsize; | |
1724 | uint32_t cputype = mh->cputype; | |
1725 | uint32_t cpusubtype = mh->cpusubtype; | |
1726 | uint32_t cryptid = encryptCmd->cryptid; | |
1727 | if (context.verboseMapping) { | |
1728 | dyld::log(" 0x%08lX->0x%08lX configured for FairPlay decryption\n", (long)start, (long)start+len); | |
1729 | } | |
1730 | int result = mremap_encrypted(start, len, cryptid, cputype, cpusubtype); | |
1731 | if ( result != 0 ) { | |
1732 | dyld::throwf("mremap_encrypted() => %d, errno=%d for %s\n", result, errno, this->getPath()); | |
1733 | } | |
1734 | #endif | |
1735 | } | |
1736 | ||
1737 | ||
1738 |