]>
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> | |
cf998323 A |
41 | #include <mach-o/dyld_images.h> |
42 | ||
43 | #include "dyld2.h" | |
39a8cd10 | 44 | #include "ImageLoaderMachOCompressed.h" |
6cae9b63 A |
45 | #include "Closure.h" |
46 | #include "Array.h" | |
39a8cd10 | 47 | |
cf998323 A |
48 | #ifndef BIND_SUBOPCODE_THREADED_SET_JOP |
49 | #define BIND_SUBOPCODE_THREADED_SET_JOP 0x0F | |
6cae9b63 A |
50 | #endif |
51 | ||
39a8cd10 A |
52 | // relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables |
53 | #if __LP64__ | |
54 | #define RELOC_SIZE 3 | |
55 | #define LC_SEGMENT_COMMAND LC_SEGMENT_64 | |
56 | #define LC_ROUTINES_COMMAND LC_ROUTINES_64 | |
57 | struct macho_segment_command : public segment_command_64 {}; | |
58 | struct macho_section : public section_64 {}; | |
59 | struct macho_routines_command : public routines_command_64 {}; | |
60 | #else | |
61 | #define RELOC_SIZE 2 | |
62 | #define LC_SEGMENT_COMMAND LC_SEGMENT | |
63 | #define LC_ROUTINES_COMMAND LC_ROUTINES | |
64 | struct macho_segment_command : public segment_command {}; | |
65 | struct macho_section : public section {}; | |
66 | struct macho_routines_command : public routines_command {}; | |
67 | #endif | |
68 | ||
10b92d3b | 69 | |
39a8cd10 A |
70 | |
71 | // create image for main executable | |
72 | ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, | |
73 | unsigned int segCount, unsigned int libCount, const LinkContext& context) | |
74 | { | |
75 | ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, path, segCount, libCount); | |
76 | ||
77 | // set slide for PIE programs | |
78 | image->setSlide(slide); | |
79 | ||
80 | // for PIE record end of program, to know where to start loading dylibs | |
412ebb8e | 81 | if ( slide != 0 ) |
39a8cd10 | 82 | fgNextPIEDylibAddress = (uintptr_t)image->getEnd(); |
df9d6cf7 A |
83 | |
84 | image->disableCoverageCheck(); | |
39a8cd10 | 85 | image->instantiateFinish(context); |
412ebb8e | 86 | image->setMapped(context); |
df9d6cf7 | 87 | |
39a8cd10 A |
88 | if ( context.verboseMapping ) { |
89 | dyld::log("dyld: Main executable mapped %s\n", path); | |
90 | for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) { | |
91 | const char* name = image->segName(i); | |
92 | if ( (strcmp(name, "__PAGEZERO") == 0) || (strcmp(name, "__UNIXSTACK") == 0) ) | |
93 | dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segPreferredLoadAddress(i), image->segPreferredLoadAddress(i)+image->segSize(i)); | |
94 | else | |
95 | dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segActualLoadAddress(i), image->segActualEndAddress(i)); | |
96 | } | |
97 | } | |
98 | ||
99 | return image; | |
100 | } | |
101 | ||
102 | // create image by mapping in a mach-o file | |
df9d6cf7 | 103 | ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(const char* path, int fd, const uint8_t* fileData, size_t lenFileData, |
39a8cd10 | 104 | uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, |
412ebb8e | 105 | unsigned int segCount, unsigned int libCount, |
df9d6cf7 A |
106 | const struct linkedit_data_command* codeSigCmd, |
107 | const struct encryption_info_command* encryptCmd, | |
108 | const LinkContext& context) | |
39a8cd10 A |
109 | { |
110 | ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart((macho_header*)fileData, path, segCount, libCount); | |
111 | ||
112 | try { | |
113 | // record info about file | |
114 | image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); | |
115 | ||
412ebb8e | 116 | // if this image is code signed, let kernel validate signature before mapping any pages from image |
2fd3f4e8 | 117 | image->loadCodeSignature(codeSigCmd, fd, offsetInFat, context); |
412ebb8e | 118 | |
df9d6cf7 A |
119 | // Validate that first data we read with pread actually matches with code signature |
120 | image->validateFirstPages(codeSigCmd, fd, fileData, lenFileData, offsetInFat, context); | |
121 | ||
39a8cd10 A |
122 | // mmap segments |
123 | image->mapSegments(fd, offsetInFat, lenInFat, info.st_size, context); | |
124 | ||
df9d6cf7 A |
125 | // if framework is FairPlay encrypted, register with kernel |
126 | image->registerEncryption(encryptCmd, context); | |
127 | ||
2fd3f4e8 A |
128 | // probe to see if code signed correctly |
129 | image->crashIfInvalidCodeSignature(); | |
130 | ||
39a8cd10 A |
131 | // finish construction |
132 | image->instantiateFinish(context); | |
133 | ||
134 | // if path happens to be same as in LC_DYLIB_ID load command use that, otherwise malloc a copy of the path | |
135 | const char* installName = image->getInstallPath(); | |
136 | if ( (installName != NULL) && (strcmp(installName, path) == 0) && (path[0] == '/') ) | |
137 | image->setPathUnowned(installName); | |
bc3b7c8c | 138 | #if TARGET_OS_OSX |
39a8cd10 A |
139 | // <rdar://problem/6563887> app crashes when libSystem cannot be found |
140 | else if ( (installName != NULL) && (strcmp(path, "/usr/lib/libgcc_s.1.dylib") == 0) && (strcmp(installName, "/usr/lib/libSystem.B.dylib") == 0) ) | |
141 | image->setPathUnowned("/usr/lib/libSystem.B.dylib"); | |
142 | #endif | |
832b6fce A |
143 | else if ( (path[0] != '/') || (strstr(path, "../") != NULL) ) { |
144 | // rdar://problem/10733082 Fix up @rpath based paths during introspection | |
39a8cd10 A |
145 | // rdar://problem/5135363 turn relative paths into absolute paths so gdb, Symbolication can later find them |
146 | char realPath[MAXPATHLEN]; | |
832b6fce A |
147 | if ( fcntl(fd, F_GETPATH, realPath) == 0 ) |
148 | image->setPaths(path, realPath); | |
39a8cd10 A |
149 | else |
150 | image->setPath(path); | |
151 | } | |
cf998323 A |
152 | else { |
153 | // <rdar://problem/46682306> always try to realpath dylibs since they may have been dlopen()ed using a symlink path | |
154 | if ( installName != NULL ) { | |
155 | char realPath[MAXPATHLEN]; | |
156 | if ( (fcntl(fd, F_GETPATH, realPath) == 0) && (strcmp(path, realPath) != 0) ) | |
157 | image->setPaths(path, realPath); | |
158 | else | |
159 | image->setPath(path); | |
160 | } | |
161 | else { | |
162 | image->setPath(path); | |
163 | } | |
164 | } | |
39a8cd10 | 165 | |
412ebb8e A |
166 | // make sure path is stable before recording in dyld_all_image_infos |
167 | image->setMapped(context); | |
168 | ||
39a8cd10 A |
169 | } |
170 | catch (...) { | |
171 | // ImageLoader::setMapped() can throw an exception to block loading of image | |
172 | // <rdar://problem/6169686> Leaked fSegmentsArray and image segments during failed dlopen_preflight | |
173 | delete image; | |
174 | throw; | |
175 | } | |
176 | ||
177 | return image; | |
178 | } | |
179 | ||
180 | // create image by using cached mach-o file | |
412ebb8e A |
181 | ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromCache(const macho_header* mh, const char* path, long slide, |
182 | const struct stat& info, unsigned int segCount, | |
183 | unsigned int libCount, const LinkContext& context) | |
39a8cd10 A |
184 | { |
185 | ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, path, segCount, libCount); | |
186 | try { | |
187 | // record info about file | |
188 | image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); | |
189 | ||
190 | // remember this is from shared cache and cannot be unloaded | |
191 | image->fInSharedCache = true; | |
192 | image->setNeverUnload(); | |
412ebb8e | 193 | image->setSlide(slide); |
df9d6cf7 | 194 | image->disableCoverageCheck(); |
39a8cd10 A |
195 | |
196 | // segments already mapped in cache | |
197 | if ( context.verboseMapping ) { | |
198 | dyld::log("dyld: Using shared cached for %s\n", path); | |
199 | for(unsigned int i=0; i < image->fSegmentsCount; ++i) { | |
200 | dyld::log("%18s at 0x%08lX->0x%08lX\n", image->segName(i), image->segActualLoadAddress(i), image->segActualEndAddress(i)); | |
201 | } | |
202 | } | |
203 | ||
204 | image->instantiateFinish(context); | |
cf998323 A |
205 | |
206 | #if TARGET_OS_SIMULATOR | |
207 | char realPath[MAXPATHLEN] = { 0 }; | |
208 | if ( dyld::gLinkContext.rootPaths == NULL ) | |
209 | throw "root path is not set"; | |
210 | strlcpy(realPath, dyld::gLinkContext.rootPaths[0], MAXPATHLEN); | |
211 | strlcat(realPath, path, MAXPATHLEN); | |
212 | image->setPaths(path, realPath); | |
213 | #endif | |
412ebb8e | 214 | image->setMapped(context); |
39a8cd10 A |
215 | } |
216 | catch (...) { | |
217 | // ImageLoader::setMapped() can throw an exception to block loading of image | |
218 | // <rdar://problem/6169686> Leaked fSegmentsArray and image segments during failed dlopen_preflight | |
219 | delete image; | |
220 | throw; | |
221 | } | |
222 | ||
223 | return image; | |
224 | } | |
225 | ||
226 | // create image by copying an in-memory mach-o file | |
227 | ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, | |
228 | unsigned int segCount, unsigned int libCount, const LinkContext& context) | |
229 | { | |
230 | ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, moduleName, segCount, libCount); | |
231 | try { | |
232 | // map segments | |
233 | if ( mh->filetype == MH_EXECUTE ) | |
234 | throw "can't load another MH_EXECUTE"; | |
235 | ||
236 | // vmcopy segments | |
237 | image->mapSegments((const void*)mh, len, context); | |
238 | ||
239 | // for compatibility, never unload dylibs loaded from memory | |
240 | image->setNeverUnload(); | |
241 | ||
df9d6cf7 A |
242 | image->disableCoverageCheck(); |
243 | ||
39a8cd10 A |
244 | // bundle loads need path copied |
245 | if ( moduleName != NULL ) | |
246 | image->setPath(moduleName); | |
247 | ||
248 | image->instantiateFinish(context); | |
412ebb8e | 249 | image->setMapped(context); |
39a8cd10 A |
250 | } |
251 | catch (...) { | |
252 | // ImageLoader::setMapped() can throw an exception to block loading of image | |
253 | // <rdar://problem/6169686> Leaked fSegmentsArray and image segments during failed dlopen_preflight | |
254 | delete image; | |
255 | throw; | |
256 | } | |
257 | ||
258 | return image; | |
259 | } | |
260 | ||
261 | ||
262 | ImageLoaderMachOCompressed::ImageLoaderMachOCompressed(const macho_header* mh, const char* path, unsigned int segCount, | |
263 | uint32_t segOffsets[], unsigned int libCount) | |
cf998323 | 264 | : ImageLoaderMachO(mh, path, segCount, segOffsets, libCount), fDyldInfo(NULL), fChainedFixups(NULL), fExportsTrie(NULL) |
39a8cd10 A |
265 | { |
266 | } | |
267 | ||
268 | ImageLoaderMachOCompressed::~ImageLoaderMachOCompressed() | |
269 | { | |
270 | // don't do clean up in ~ImageLoaderMachO() because virtual call to segmentCommandOffsets() won't work | |
271 | destroy(); | |
272 | } | |
273 | ||
274 | ||
275 | ||
276 | // construct ImageLoaderMachOCompressed using "placement new" with SegmentMachO objects array at end | |
277 | ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateStart(const macho_header* mh, const char* path, | |
278 | unsigned int segCount, unsigned int libCount) | |
279 | { | |
280 | size_t size = sizeof(ImageLoaderMachOCompressed) + segCount * sizeof(uint32_t) + libCount * sizeof(ImageLoader*); | |
281 | ImageLoaderMachOCompressed* allocatedSpace = static_cast<ImageLoaderMachOCompressed*>(malloc(size)); | |
282 | if ( allocatedSpace == NULL ) | |
283 | throw "malloc failed"; | |
284 | uint32_t* segOffsets = ((uint32_t*)(((uint8_t*)allocatedSpace) + sizeof(ImageLoaderMachOCompressed))); | |
285 | bzero(&segOffsets[segCount], libCount*sizeof(void*)); // zero out lib array | |
286 | return new (allocatedSpace) ImageLoaderMachOCompressed(mh, path, segCount, segOffsets, libCount); | |
287 | } | |
288 | ||
289 | ||
290 | // common code to finish initializing object | |
291 | void ImageLoaderMachOCompressed::instantiateFinish(const LinkContext& context) | |
292 | { | |
293 | // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide | |
df9d6cf7 | 294 | this->parseLoadCmds(context); |
39a8cd10 A |
295 | } |
296 | ||
297 | uint32_t* ImageLoaderMachOCompressed::segmentCommandOffsets() const | |
298 | { | |
299 | return ((uint32_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed))); | |
300 | } | |
301 | ||
302 | ||
303 | ImageLoader* ImageLoaderMachOCompressed::libImage(unsigned int libIndex) const | |
304 | { | |
305 | const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); | |
412ebb8e A |
306 | // mask off low bits |
307 | return (ImageLoader*)(images[libIndex] & (-4)); | |
39a8cd10 A |
308 | } |
309 | ||
310 | bool ImageLoaderMachOCompressed::libReExported(unsigned int libIndex) const | |
311 | { | |
312 | const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); | |
313 | // re-export flag is low bit | |
314 | return ((images[libIndex] & 1) != 0); | |
315 | } | |
316 | ||
412ebb8e A |
317 | bool ImageLoaderMachOCompressed::libIsUpward(unsigned int libIndex) const |
318 | { | |
319 | const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); | |
bc3b7c8c | 320 | // upward flag is second bit |
412ebb8e A |
321 | return ((images[libIndex] & 2) != 0); |
322 | } | |
323 | ||
39a8cd10 | 324 | |
412ebb8e | 325 | void ImageLoaderMachOCompressed::setLibImage(unsigned int libIndex, ImageLoader* image, bool reExported, bool upward) |
39a8cd10 A |
326 | { |
327 | uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); | |
328 | uintptr_t value = (uintptr_t)image; | |
329 | if ( reExported ) | |
330 | value |= 1; | |
412ebb8e A |
331 | if ( upward ) |
332 | value |= 2; | |
39a8cd10 A |
333 | images[libIndex] = value; |
334 | } | |
335 | ||
336 | ||
39a8cd10 A |
337 | void ImageLoaderMachOCompressed::rebaseAt(const LinkContext& context, uintptr_t addr, uintptr_t slide, uint8_t type) |
338 | { | |
832b6fce A |
339 | if ( context.verboseRebase ) { |
340 | dyld::log("dyld: rebase: %s:*0x%08lX += 0x%08lX\n", this->getShortName(), (uintptr_t)addr, slide); | |
341 | } | |
39a8cd10 A |
342 | //dyld::log("0x%08lX type=%d\n", addr, type); |
343 | uintptr_t* locationToFix = (uintptr_t*)addr; | |
344 | switch (type) { | |
345 | case REBASE_TYPE_POINTER: | |
346 | *locationToFix += slide; | |
347 | break; | |
348 | case REBASE_TYPE_TEXT_ABSOLUTE32: | |
349 | *locationToFix += slide; | |
350 | break; | |
351 | default: | |
352 | dyld::throwf("bad rebase type %d", type); | |
353 | } | |
354 | } | |
355 | ||
356 | void ImageLoaderMachOCompressed::throwBadRebaseAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, | |
357 | const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos) | |
358 | { | |
9f83892a | 359 | dyld::throwf("malformed rebase opcodes (%ld/%ld): address 0x%08lX is outside of segment %s (0x%08lX -> 0x%08lX)", |
39a8cd10 A |
360 | (intptr_t)(pos-startOpcodes), (intptr_t)(endOpcodes-startOpcodes), address, segName(segmentIndex), |
361 | segActualLoadAddress(segmentIndex), segmentEndAddress); | |
362 | } | |
363 | ||
9f83892a | 364 | void ImageLoaderMachOCompressed::rebase(const LinkContext& context, uintptr_t slide) |
39a8cd10 | 365 | { |
cf998323 A |
366 | // binary uses chained fixups where are applied during binding |
367 | if ( fDyldInfo == NULL ) | |
368 | return; | |
369 | ||
412ebb8e | 370 | CRSetCrashLogMessage2(this->getPath()); |
39a8cd10 A |
371 | const uint8_t* const start = fLinkEditBase + fDyldInfo->rebase_off; |
372 | const uint8_t* const end = &start[fDyldInfo->rebase_size]; | |
373 | const uint8_t* p = start; | |
374 | ||
cf998323 A |
375 | if ( start == end ) |
376 | return; | |
377 | ||
378 | uint32_t ignore; | |
379 | bool bindingBecauseOfRoot = this->overridesCachedDylib(ignore); | |
380 | vmAccountingSetSuspended(context, bindingBecauseOfRoot); | |
381 | ||
39a8cd10 A |
382 | try { |
383 | uint8_t type = 0; | |
384 | int segmentIndex = 0; | |
385 | uintptr_t address = segActualLoadAddress(0); | |
9f83892a | 386 | uintptr_t segmentStartAddress = segActualLoadAddress(0); |
39a8cd10 | 387 | uintptr_t segmentEndAddress = segActualEndAddress(0); |
19894a12 A |
388 | uintptr_t count; |
389 | uintptr_t skip; | |
39a8cd10 A |
390 | bool done = false; |
391 | while ( !done && (p < end) ) { | |
392 | uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; | |
393 | uint8_t opcode = *p & REBASE_OPCODE_MASK; | |
394 | ++p; | |
395 | switch (opcode) { | |
396 | case REBASE_OPCODE_DONE: | |
397 | done = true; | |
398 | break; | |
399 | case REBASE_OPCODE_SET_TYPE_IMM: | |
400 | type = immediate; | |
401 | break; | |
402 | case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
403 | segmentIndex = immediate; | |
19894a12 A |
404 | if ( segmentIndex >= fSegmentsCount ) |
405 | dyld::throwf("REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", | |
406 | segmentIndex, fSegmentsCount-1); | |
9f83892a A |
407 | #if TEXT_RELOC_SUPPORT |
408 | if ( !segWriteable(segmentIndex) && !segHasRebaseFixUps(segmentIndex) && !segHasBindFixUps(segmentIndex) ) | |
409 | #else | |
410 | if ( !segWriteable(segmentIndex) ) | |
411 | #endif | |
412 | dyld::throwf("REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is not a writable segment (%s)", | |
413 | segmentIndex, segName(segmentIndex)); | |
414 | segmentStartAddress = segActualLoadAddress(segmentIndex); | |
39a8cd10 | 415 | segmentEndAddress = segActualEndAddress(segmentIndex); |
9f83892a | 416 | address = segmentStartAddress + read_uleb128(p, end); |
39a8cd10 A |
417 | break; |
418 | case REBASE_OPCODE_ADD_ADDR_ULEB: | |
419 | address += read_uleb128(p, end); | |
420 | break; | |
421 | case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: | |
422 | address += immediate*sizeof(uintptr_t); | |
423 | break; | |
424 | case REBASE_OPCODE_DO_REBASE_IMM_TIMES: | |
425 | for (int i=0; i < immediate; ++i) { | |
9f83892a | 426 | if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) |
39a8cd10 A |
427 | throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); |
428 | rebaseAt(context, address, slide, type); | |
429 | address += sizeof(uintptr_t); | |
430 | } | |
431 | fgTotalRebaseFixups += immediate; | |
432 | break; | |
433 | case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: | |
434 | count = read_uleb128(p, end); | |
435 | for (uint32_t i=0; i < count; ++i) { | |
9f83892a | 436 | if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) |
39a8cd10 A |
437 | throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); |
438 | rebaseAt(context, address, slide, type); | |
439 | address += sizeof(uintptr_t); | |
440 | } | |
441 | fgTotalRebaseFixups += count; | |
442 | break; | |
443 | case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: | |
9f83892a | 444 | if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) |
39a8cd10 A |
445 | throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); |
446 | rebaseAt(context, address, slide, type); | |
447 | address += read_uleb128(p, end) + sizeof(uintptr_t); | |
448 | ++fgTotalRebaseFixups; | |
449 | break; | |
450 | case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: | |
451 | count = read_uleb128(p, end); | |
452 | skip = read_uleb128(p, end); | |
453 | for (uint32_t i=0; i < count; ++i) { | |
9f83892a | 454 | if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) |
39a8cd10 A |
455 | throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); |
456 | rebaseAt(context, address, slide, type); | |
457 | address += skip + sizeof(uintptr_t); | |
458 | } | |
459 | fgTotalRebaseFixups += count; | |
460 | break; | |
461 | default: | |
6cae9b63 | 462 | dyld::throwf("bad rebase opcode %d", *(p-1)); |
39a8cd10 A |
463 | } |
464 | } | |
465 | } | |
466 | catch (const char* msg) { | |
467 | const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); | |
468 | free((void*)msg); | |
469 | throw newMsg; | |
470 | } | |
412ebb8e | 471 | CRSetCrashLogMessage2(NULL); |
39a8cd10 A |
472 | } |
473 | ||
9f83892a | 474 | const ImageLoader::Symbol* ImageLoaderMachOCompressed::findShallowExportedSymbol(const char* symbol, const ImageLoader** foundIn) const |
412ebb8e A |
475 | { |
476 | //dyld::log("Compressed::findExportedSymbol(%s) in %s\n", symbol, this->getShortName()); | |
cf998323 A |
477 | uint32_t trieFileOffset = fDyldInfo ? fDyldInfo->export_off : fExportsTrie->dataoff; |
478 | uint32_t trieFileSize = fDyldInfo ? fDyldInfo->export_size : fExportsTrie->datasize; | |
479 | if ( trieFileSize == 0 ) | |
412ebb8e A |
480 | return NULL; |
481 | #if LOG_BINDINGS | |
482 | dyld::logBindings("%s: %s\n", this->getShortName(), symbol); | |
483 | #endif | |
484 | ++ImageLoaderMachO::fgSymbolTrieSearchs; | |
cf998323 A |
485 | const uint8_t* start = &fLinkEditBase[trieFileOffset]; |
486 | const uint8_t* end = &start[trieFileSize]; | |
412ebb8e A |
487 | const uint8_t* foundNodeStart = this->trieWalk(start, end, symbol); |
488 | if ( foundNodeStart != NULL ) { | |
489 | const uint8_t* p = foundNodeStart; | |
19894a12 | 490 | const uintptr_t flags = read_uleb128(p, end); |
412ebb8e A |
491 | // found match, return pointer to terminal part of node |
492 | if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { | |
493 | // re-export from another dylib, lookup there | |
19894a12 | 494 | const uintptr_t ordinal = read_uleb128(p, end); |
412ebb8e A |
495 | const char* importedName = (char*)p; |
496 | if ( importedName[0] == '\0' ) | |
497 | importedName = symbol; | |
498 | if ( (ordinal > 0) && (ordinal <= libraryCount()) ) { | |
19894a12 | 499 | const ImageLoader* reexportedFrom = libImage((unsigned int)ordinal-1); |
cf998323 A |
500 | // Missing weak-dylib |
501 | if ( reexportedFrom == NULL ) | |
502 | return NULL; | |
412ebb8e | 503 | //dyld::log("Compressed::findExportedSymbol(), %s -> %s/%s\n", symbol, reexportedFrom->getShortName(), importedName); |
9f83892a A |
504 | const char* reExportLibPath = libPath((unsigned int)ordinal-1); |
505 | return reexportedFrom->findExportedSymbol(importedName, true, reExportLibPath, foundIn); | |
412ebb8e A |
506 | } |
507 | else { | |
508 | //dyld::throwf("bad mach-o binary, library ordinal (%u) invalid (max %u) for re-exported symbol %s in %s", | |
509 | // ordinal, libraryCount(), symbol, this->getPath()); | |
510 | } | |
511 | } | |
39a8cd10 | 512 | else { |
412ebb8e A |
513 | //dyld::log("findExportedSymbol(%s) in %s found match, returning %p\n", symbol, this->getShortName(), p); |
514 | if ( foundIn != NULL ) | |
515 | *foundIn = (ImageLoader*)this; | |
516 | // return pointer to terminal part of node | |
517 | return (Symbol*)foundNodeStart; | |
39a8cd10 | 518 | } |
412ebb8e A |
519 | } |
520 | return NULL; | |
39a8cd10 A |
521 | } |
522 | ||
523 | ||
524 | bool ImageLoaderMachOCompressed::containsSymbol(const void* addr) const | |
525 | { | |
cf998323 A |
526 | uint32_t trieFileOffset = fDyldInfo ? fDyldInfo->export_off : fExportsTrie->dataoff; |
527 | uint32_t trieFileSize = fDyldInfo ? fDyldInfo->export_size : fExportsTrie->datasize; | |
528 | const uint8_t* start = &fLinkEditBase[trieFileOffset]; | |
529 | const uint8_t* end = &start[trieFileSize]; | |
39a8cd10 A |
530 | return ( (start <= addr) && (addr < end) ); |
531 | } | |
532 | ||
533 | ||
2fd3f4e8 | 534 | uintptr_t ImageLoaderMachOCompressed::exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const |
39a8cd10 | 535 | { |
cf998323 A |
536 | uint32_t trieFileOffset = fDyldInfo ? fDyldInfo->export_off : fExportsTrie->dataoff; |
537 | uint32_t trieFileSize = fDyldInfo ? fDyldInfo->export_size : fExportsTrie->datasize; | |
39a8cd10 | 538 | const uint8_t* exportNode = (uint8_t*)symbol; |
cf998323 A |
539 | const uint8_t* exportTrieStart = fLinkEditBase + trieFileOffset; |
540 | const uint8_t* exportTrieEnd = exportTrieStart + trieFileSize; | |
39a8cd10 A |
541 | if ( (exportNode < exportTrieStart) || (exportNode > exportTrieEnd) ) |
542 | throw "symbol is not in trie"; | |
412ebb8e | 543 | //dyld::log("exportedSymbolAddress(): node=%p, nodeOffset=0x%04X in %s\n", symbol, (int)((uint8_t*)symbol - exportTrieStart), this->getShortName()); |
19894a12 | 544 | uintptr_t flags = read_uleb128(exportNode, exportTrieEnd); |
2fd3f4e8 A |
545 | switch ( flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) { |
546 | case EXPORT_SYMBOL_FLAGS_KIND_REGULAR: | |
547 | if ( runResolver && (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) { | |
548 | // this node has a stub and resolver, run the resolver to get target address | |
549 | uintptr_t stub = read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; // skip over stub | |
550 | // <rdar://problem/10657737> interposing dylibs have the stub address as their replacee | |
19894a12 A |
551 | uintptr_t interposedStub = interposedAddress(context, stub, requestor); |
552 | if ( interposedStub != stub ) | |
553 | return interposedStub; | |
554 | // stub was not interposed, so run resolver | |
2fd3f4e8 A |
555 | typedef uintptr_t (*ResolverProc)(void); |
556 | ResolverProc resolver = (ResolverProc)(read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData); | |
cf998323 A |
557 | #if __has_feature(ptrauth_calls) |
558 | resolver = (ResolverProc)__builtin_ptrauth_sign_unauthenticated(resolver, ptrauth_key_asia, 0); | |
559 | #endif | |
2fd3f4e8 A |
560 | uintptr_t result = (*resolver)(); |
561 | if ( context.verboseBind ) | |
562 | dyld::log("dyld: resolver at %p returned 0x%08lX\n", resolver, result); | |
cf998323 A |
563 | #if __has_feature(ptrauth_calls) |
564 | result = (uintptr_t)__builtin_ptrauth_strip((void*)result, ptrauth_key_asia); | |
565 | #endif | |
2fd3f4e8 | 566 | return result; |
832b6fce | 567 | } |
2fd3f4e8 A |
568 | return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; |
569 | case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: | |
570 | if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) | |
19894a12 | 571 | dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, symbol); |
2fd3f4e8 A |
572 | return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; |
573 | case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: | |
574 | if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) | |
19894a12 | 575 | dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, symbol); |
2fd3f4e8 A |
576 | return read_uleb128(exportNode, exportTrieEnd); |
577 | default: | |
19894a12 | 578 | dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, symbol); |
412ebb8e | 579 | } |
39a8cd10 A |
580 | } |
581 | ||
582 | bool ImageLoaderMachOCompressed::exportedSymbolIsWeakDefintion(const Symbol* symbol) const | |
583 | { | |
cf998323 A |
584 | uint32_t trieFileOffset = fDyldInfo ? fDyldInfo->export_off : fExportsTrie->dataoff; |
585 | uint32_t trieFileSize = fDyldInfo ? fDyldInfo->export_size : fExportsTrie->datasize; | |
39a8cd10 | 586 | const uint8_t* exportNode = (uint8_t*)symbol; |
cf998323 A |
587 | const uint8_t* exportTrieStart = fLinkEditBase + trieFileOffset; |
588 | const uint8_t* exportTrieEnd = exportTrieStart + trieFileSize; | |
39a8cd10 A |
589 | if ( (exportNode < exportTrieStart) || (exportNode > exportTrieEnd) ) |
590 | throw "symbol is not in trie"; | |
19894a12 | 591 | uintptr_t flags = read_uleb128(exportNode, exportTrieEnd); |
39a8cd10 A |
592 | return ( flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ); |
593 | } | |
594 | ||
595 | ||
596 | const char* ImageLoaderMachOCompressed::exportedSymbolName(const Symbol* symbol) const | |
597 | { | |
598 | throw "NSNameOfSymbol() not supported with compressed LINKEDIT"; | |
599 | } | |
600 | ||
601 | unsigned int ImageLoaderMachOCompressed::exportedSymbolCount() const | |
602 | { | |
603 | throw "NSSymbolDefinitionCountInObjectFileImage() not supported with compressed LINKEDIT"; | |
604 | } | |
605 | ||
606 | const ImageLoader::Symbol* ImageLoaderMachOCompressed::exportedSymbolIndexed(unsigned int index) const | |
607 | { | |
608 | throw "NSSymbolDefinitionNameInObjectFileImage() not supported with compressed LINKEDIT"; | |
609 | } | |
610 | ||
611 | unsigned int ImageLoaderMachOCompressed::importedSymbolCount() const | |
612 | { | |
613 | throw "NSSymbolReferenceCountInObjectFileImage() not supported with compressed LINKEDIT"; | |
614 | } | |
615 | ||
616 | const ImageLoader::Symbol* ImageLoaderMachOCompressed::importedSymbolIndexed(unsigned int index) const | |
617 | { | |
618 | throw "NSSymbolReferenceCountInObjectFileImage() not supported with compressed LINKEDIT"; | |
619 | } | |
620 | ||
621 | const char* ImageLoaderMachOCompressed::importedSymbolName(const Symbol* symbol) const | |
622 | { | |
623 | throw "NSSymbolReferenceNameInObjectFileImage() not supported with compressed LINKEDIT"; | |
624 | } | |
625 | ||
626 | ||
627 | ||
412ebb8e A |
628 | uintptr_t ImageLoaderMachOCompressed::resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import, |
629 | bool runResolver, const ImageLoader** foundIn) | |
39a8cd10 A |
630 | { |
631 | const Symbol* sym; | |
632 | if ( context.flatExportFinder(symbolName, &sym, foundIn) ) { | |
2fd3f4e8 A |
633 | if ( *foundIn != this ) |
634 | context.addDynamicReference(this, const_cast<ImageLoader*>(*foundIn)); | |
412ebb8e | 635 | return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver); |
39a8cd10 A |
636 | } |
637 | // if a bundle is loaded privately the above will not find its exports | |
638 | if ( this->isBundle() && this->hasHiddenExports() ) { | |
639 | // look in self for needed symbol | |
9f83892a | 640 | sym = this->findShallowExportedSymbol(symbolName, foundIn); |
39a8cd10 | 641 | if ( sym != NULL ) |
412ebb8e | 642 | return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver); |
39a8cd10 A |
643 | } |
644 | if ( weak_import ) { | |
645 | // definition can't be found anywhere, ok because it is weak, just return 0 | |
646 | return 0; | |
647 | } | |
19894a12 | 648 | throwSymbolNotFound(context, symbolName, this->getPath(), "", "flat namespace"); |
39a8cd10 A |
649 | } |
650 | ||
651 | ||
6cae9b63 | 652 | static void patchCacheUsesOf(const ImageLoader::LinkContext& context, const dyld3::closure::Image* overriddenImage, |
797cc951 A |
653 | uint32_t cacheOffsetOfImpl, const char* symbolName, uintptr_t newImpl, |
654 | DyldSharedCache::DataConstLazyScopedWriter& patcher) | |
6cae9b63 | 655 | { |
797cc951 A |
656 | patcher.makeWriteable(); |
657 | ||
6cae9b63 | 658 | uintptr_t cacheStart = (uintptr_t)context.dyldCache; |
cf998323 A |
659 | uint32_t imageIndex = overriddenImage->imageNum() - (uint32_t)context.dyldCache->cachedDylibsImageArray()->startImageNum(); |
660 | context.dyldCache->forEachPatchableUseOfExport(imageIndex, cacheOffsetOfImpl, ^(dyld_cache_patchable_location patchLocation) { | |
6cae9b63 A |
661 | uintptr_t* loc = (uintptr_t*)(cacheStart+patchLocation.cacheOffset); |
662 | #if __has_feature(ptrauth_calls) | |
663 | if ( patchLocation.authenticated ) { | |
664 | dyld3::MachOLoaded::ChainedFixupPointerOnDisk fixupInfo; | |
cf998323 A |
665 | fixupInfo.arm64e.authRebase.auth = true; |
666 | fixupInfo.arm64e.authRebase.addrDiv = patchLocation.usesAddressDiversity; | |
667 | fixupInfo.arm64e.authRebase.diversity = patchLocation.discriminator; | |
668 | fixupInfo.arm64e.authRebase.key = patchLocation.key; | |
669 | uintptr_t newValue = fixupInfo.arm64e.signPointer(loc, newImpl + DyldSharedCache::getAddend(patchLocation)); | |
6cae9b63 A |
670 | if ( *loc != newValue ) { |
671 | *loc = newValue; | |
672 | if ( context.verboseBind ) | |
673 | dyld::log("dyld: cache fixup: *%p = %p (JOP: diversity 0x%04X, addr-div=%d, key=%s) to %s\n", | |
cf998323 | 674 | loc, (void*)newValue, patchLocation.discriminator, patchLocation.usesAddressDiversity, DyldSharedCache::keyName(patchLocation), symbolName); |
6cae9b63 A |
675 | } |
676 | return; | |
677 | } | |
678 | #endif | |
cf998323 | 679 | uintptr_t newValue = newImpl + (uintptr_t)DyldSharedCache::getAddend(patchLocation); |
6cae9b63 A |
680 | if ( *loc != newValue ) { |
681 | *loc = newValue; | |
682 | if ( context.verboseBind ) | |
683 | dyld::log("dyld: cache fixup: *%p = %p to %s\n", loc, (void*)newValue, symbolName); | |
684 | } | |
685 | }); | |
686 | } | |
cf998323 | 687 | |
6cae9b63 A |
688 | |
689 | ||
690 | uintptr_t ImageLoaderMachOCompressed::resolveWeak(const LinkContext& context, const char* symbolName, bool weak_import, | |
797cc951 A |
691 | bool runResolver, const ImageLoader** foundIn, |
692 | DyldSharedCache::DataConstLazyScopedWriter& patcher) | |
6cae9b63 A |
693 | { |
694 | const Symbol* sym; | |
cf998323 A |
695 | CoalesceNotifier notifier = nullptr; |
696 | __block uintptr_t foundOutsideCache = 0; | |
697 | __block const char* foundOutsideCachePath = nullptr; | |
698 | __block uintptr_t lastFoundInCache = 0; | |
797cc951 | 699 | |
cf998323 A |
700 | if ( this->usesChainedFixups() ) { |
701 | notifier = ^(const Symbol* implSym, const ImageLoader* implIn, const mach_header* implMh) { | |
702 | // This block is only called in dyld2 mode when a non-cached image is search for which weak-def implementation to use | |
703 | // As a side effect of that search we notice any implementations outside and inside the cache, | |
704 | // and use that to trigger patching the cache to use the implementation outside the cache. | |
705 | uintptr_t implAddr = implIn->getExportedSymbolAddress(implSym, context, nullptr, false, symbolName); | |
706 | if ( ((dyld3::MachOLoaded*)implMh)->inDyldCache() ) { | |
707 | if ( foundOutsideCache != 0 ) { | |
708 | // have an implementation in cache and and earlier one not in the cache, patch cache to use earlier one | |
709 | lastFoundInCache = implAddr; | |
710 | uint32_t imageIndex; | |
711 | if ( context.dyldCache->findMachHeaderImageIndex(implMh, imageIndex) ) { | |
712 | const dyld3::closure::Image* overriddenImage = context.dyldCache->cachedDylibsImageArray()->imageForNum(imageIndex+1); | |
713 | uint32_t cacheOffsetOfImpl = (uint32_t)((uintptr_t)implAddr - (uintptr_t)context.dyldCache); | |
714 | if ( context.verboseWeakBind ) | |
715 | dyld::log("dyld: weak bind, patching dyld cache uses of %s to use 0x%lX in %s\n", symbolName, foundOutsideCache, foundOutsideCachePath); | |
797cc951 | 716 | patchCacheUsesOf(context, overriddenImage, cacheOffsetOfImpl, symbolName, foundOutsideCache, patcher); |
cf998323 | 717 | } |
6cae9b63 A |
718 | } |
719 | } | |
cf998323 A |
720 | else { |
721 | // record first non-cache implementation | |
722 | if ( foundOutsideCache == 0 ) { | |
723 | foundOutsideCache = implAddr; | |
724 | foundOutsideCachePath = implIn->getPath(); | |
725 | } | |
726 | } | |
727 | }; | |
728 | } | |
729 | ||
6cae9b63 A |
730 | if ( context.coalescedExportFinder(symbolName, &sym, foundIn, notifier) ) { |
731 | if ( *foundIn != this ) | |
732 | context.addDynamicReference(this, const_cast<ImageLoader*>(*foundIn)); | |
733 | return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver); | |
734 | } | |
735 | // if a bundle is loaded privately the above will not find its exports | |
736 | if ( this->isBundle() && this->hasHiddenExports() ) { | |
737 | // look in self for needed symbol | |
738 | sym = this->findShallowExportedSymbol(symbolName, foundIn); | |
739 | if ( sym != NULL ) | |
740 | return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver); | |
741 | } | |
742 | if ( weak_import ) { | |
743 | // definition can't be found anywhere, ok because it is weak, just return 0 | |
744 | return 0; | |
745 | } | |
746 | throwSymbolNotFound(context, symbolName, this->getPath(), "", "weak"); | |
747 | } | |
748 | ||
749 | ||
9f83892a A |
750 | uintptr_t ImageLoaderMachOCompressed::resolveTwolevel(const LinkContext& context, const char* symbolName, const ImageLoader* definedInImage, |
751 | const ImageLoader* requestorImage, unsigned requestorOrdinalOfDef, bool weak_import, bool runResolver, | |
752 | const ImageLoader** foundIn) | |
39a8cd10 A |
753 | { |
754 | // two level lookup | |
9f83892a A |
755 | uintptr_t address; |
756 | if ( definedInImage->findExportedSymbolAddress(context, symbolName, requestorImage, requestorOrdinalOfDef, runResolver, foundIn, &address) ) | |
757 | return address; | |
758 | ||
39a8cd10 A |
759 | if ( weak_import ) { |
760 | // definition can't be found anywhere, ok because it is weak, just return 0 | |
761 | return 0; | |
762 | } | |
763 | ||
19894a12 A |
764 | // nowhere to be found, check if maybe this image is too new for this OS |
765 | char versMismatch[256]; | |
766 | versMismatch[0] = '\0'; | |
767 | uint32_t imageMinOS = this->minOSVersion(); | |
768 | // dyld is always built for the current OS, so we can get the current OS version | |
769 | // from the load command in dyld itself. | |
770 | extern const mach_header __dso_handle; | |
771 | uint32_t dyldMinOS = ImageLoaderMachO::minOSVersion(&__dso_handle); | |
772 | if ( imageMinOS > dyldMinOS ) { | |
bc3b7c8c | 773 | #if TARGET_OS_OSX |
19894a12 A |
774 | const char* msg = dyld::mkstringf(" (which was built for Mac OS X %d.%d)", imageMinOS >> 16, (imageMinOS >> 8) & 0xFF); |
775 | #else | |
776 | const char* msg = dyld::mkstringf(" (which was built for iOS %d.%d)", imageMinOS >> 16, (imageMinOS >> 8) & 0xFF); | |
777 | #endif | |
778 | strcpy(versMismatch, msg); | |
779 | ::free((void*)msg); | |
780 | } | |
9f83892a | 781 | throwSymbolNotFound(context, symbolName, this->getPath(), versMismatch, definedInImage->getPath()); |
39a8cd10 A |
782 | } |
783 | ||
784 | ||
785 | uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const char* symbolName, | |
19894a12 | 786 | uint8_t symboFlags, long libraryOrdinal, const ImageLoader** targetImage, |
797cc951 | 787 | DyldSharedCache::DataConstLazyScopedWriter& patcher, |
412ebb8e | 788 | LastLookup* last, bool runResolver) |
39a8cd10 A |
789 | { |
790 | *targetImage = NULL; | |
791 | ||
792 | // only clients that benefit from caching last lookup pass in a LastLookup struct | |
793 | if ( last != NULL ) { | |
794 | if ( (last->ordinal == libraryOrdinal) | |
795 | && (last->flags == symboFlags) | |
796 | && (last->name == symbolName) ) { | |
797 | *targetImage = last->foundIn; | |
798 | return last->result; | |
799 | } | |
800 | } | |
801 | ||
802 | bool weak_import = (symboFlags & BIND_SYMBOL_FLAGS_WEAK_IMPORT); | |
803 | uintptr_t symbolAddress; | |
804 | if ( context.bindFlat || (libraryOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP) ) { | |
412ebb8e | 805 | symbolAddress = this->resolveFlat(context, symbolName, weak_import, runResolver, targetImage); |
39a8cd10 | 806 | } |
6cae9b63 | 807 | else if ( libraryOrdinal == BIND_SPECIAL_DYLIB_WEAK_LOOKUP ) { |
797cc951 | 808 | symbolAddress = this->resolveWeak(context, symbolName, weak_import, runResolver, targetImage, patcher); |
6cae9b63 | 809 | } |
39a8cd10 A |
810 | else { |
811 | if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) { | |
812 | *targetImage = context.mainExecutable; | |
813 | } | |
814 | else if ( libraryOrdinal == BIND_SPECIAL_DYLIB_SELF ) { | |
815 | *targetImage = this; | |
816 | } | |
817 | else if ( libraryOrdinal <= 0 ) { | |
19894a12 | 818 | dyld::throwf("bad mach-o binary, unknown special library ordinal (%ld) too big for symbol %s in %s", |
39a8cd10 A |
819 | libraryOrdinal, symbolName, this->getPath()); |
820 | } | |
821 | else if ( (unsigned)libraryOrdinal <= libraryCount() ) { | |
19894a12 | 822 | *targetImage = libImage((unsigned int)libraryOrdinal-1); |
39a8cd10 A |
823 | } |
824 | else { | |
19894a12 | 825 | dyld::throwf("bad mach-o binary, library ordinal (%ld) too big (max %u) for symbol %s in %s", |
39a8cd10 A |
826 | libraryOrdinal, libraryCount(), symbolName, this->getPath()); |
827 | } | |
828 | if ( *targetImage == NULL ) { | |
829 | if ( weak_import ) { | |
830 | // if target library not loaded and reference is weak or library is weak return 0 | |
831 | symbolAddress = 0; | |
832 | } | |
833 | else { | |
bc3b7c8c A |
834 | // Try get the path from the load commands |
835 | if ( const char* depPath = libPath((unsigned int)libraryOrdinal-1) ) { | |
836 | dyld::throwf("can't resolve symbol %s in %s because dependent dylib %s could not be loaded", | |
837 | symbolName, this->getPath(), depPath); | |
838 | } else { | |
839 | dyld::throwf("can't resolve symbol %s in %s because dependent dylib #%ld could not be loaded", | |
840 | symbolName, this->getPath(), libraryOrdinal); | |
841 | } | |
39a8cd10 A |
842 | } |
843 | } | |
844 | else { | |
9f83892a | 845 | symbolAddress = resolveTwolevel(context, symbolName, *targetImage, this, (unsigned)libraryOrdinal, weak_import, runResolver, targetImage); |
39a8cd10 A |
846 | } |
847 | } | |
9f83892a | 848 | |
39a8cd10 A |
849 | // save off lookup results if client wants |
850 | if ( last != NULL ) { | |
851 | last->ordinal = libraryOrdinal; | |
852 | last->flags = symboFlags; | |
853 | last->name = symbolName; | |
854 | last->foundIn = *targetImage; | |
855 | last->result = symbolAddress; | |
856 | } | |
857 | ||
858 | return symbolAddress; | |
859 | } | |
860 | ||
6cae9b63 A |
861 | uintptr_t ImageLoaderMachOCompressed::bindAt(const LinkContext& context, ImageLoaderMachOCompressed* image, |
862 | uintptr_t addr, uint8_t type, const char* symbolName, | |
863 | uint8_t symbolFlags, intptr_t addend, long libraryOrdinal, | |
864 | ExtraBindData *extraBindData, | |
797cc951 A |
865 | const char* msg, DyldSharedCache::DataConstLazyScopedWriter& patcher, |
866 | LastLookup* last, bool runResolver) | |
39a8cd10 A |
867 | { |
868 | const ImageLoader* targetImage; | |
869 | uintptr_t symbolAddress; | |
870 | ||
871 | // resolve symbol | |
6cae9b63 A |
872 | if (type == BIND_TYPE_THREADED_REBASE) { |
873 | symbolAddress = 0; | |
874 | targetImage = nullptr; | |
875 | } else | |
797cc951 | 876 | symbolAddress = image->resolve(context, symbolName, symbolFlags, libraryOrdinal, &targetImage, patcher, last, runResolver); |
39a8cd10 A |
877 | |
878 | // do actual update | |
6cae9b63 | 879 | return image->bindLocation(context, image->imageBaseAddress(), addr, symbolAddress, type, symbolName, addend, image->getPath(), targetImage ? targetImage->getPath() : NULL, msg, extraBindData, image->fSlide); |
39a8cd10 A |
880 | } |
881 | ||
9f83892a | 882 | |
39a8cd10 A |
883 | void ImageLoaderMachOCompressed::throwBadBindingAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, |
884 | const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos) | |
885 | { | |
9f83892a | 886 | dyld::throwf("malformed binding opcodes (%ld/%ld): address 0x%08lX is outside segment %s (0x%08lX -> 0x%08lX)", |
39a8cd10 A |
887 | (intptr_t)(pos-startOpcodes), (intptr_t)(endOpcodes-startOpcodes), address, segName(segmentIndex), |
888 | segActualLoadAddress(segmentIndex), segmentEndAddress); | |
889 | } | |
890 | ||
bc3b7c8c | 891 | void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLazysBound, const ImageLoader* reExportParent) |
39a8cd10 | 892 | { |
412ebb8e A |
893 | CRSetCrashLogMessage2(this->getPath()); |
894 | ||
39a8cd10 A |
895 | // if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind |
896 | // note: flat-namespace binaries need to have imports rebound (even if correctly prebound) | |
897 | if ( this->usablePrebinding(context) ) { | |
898 | // don't need to bind | |
6cae9b63 | 899 | // except weak which may now be inline with the regular binds |
16b475fc | 900 | if ( this->participatesInCoalescing() && (fDyldInfo != nullptr) ) { |
6cae9b63 | 901 | // run through all binding opcodes |
797cc951 A |
902 | DyldSharedCache::DataConstLazyScopedWriter patcher(context.dyldCache, mach_task_self(), context.verboseMapping ? &dyld::log : nullptr); |
903 | auto* patcherPtr = &patcher; | |
6cae9b63 A |
904 | eachBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image, |
905 | uintptr_t addr, uint8_t type, const char* symbolName, | |
906 | uint8_t symbolFlags, intptr_t addend, long libraryOrdinal, | |
907 | ExtraBindData *extraBindData, | |
908 | const char* msg, LastLookup* last, bool runResolver) { | |
909 | if ( libraryOrdinal != BIND_SPECIAL_DYLIB_WEAK_LOOKUP ) | |
910 | return (uintptr_t)0; | |
911 | return ImageLoaderMachOCompressed::bindAt(ctx, image, addr, type, symbolName, symbolFlags, | |
912 | addend, libraryOrdinal, extraBindData, | |
797cc951 | 913 | msg, *patcherPtr, last, runResolver); |
6cae9b63 A |
914 | }); |
915 | } | |
39a8cd10 A |
916 | } |
917 | else { | |
9f83892a A |
918 | uint64_t t0 = mach_absolute_time(); |
919 | ||
6cae9b63 A |
920 | uint32_t ignore; |
921 | bool bindingBecauseOfRoot = ( this->overridesCachedDylib(ignore) || this->inSharedCache() ); | |
922 | vmAccountingSetSuspended(context, bindingBecauseOfRoot); | |
923 | ||
cf998323 | 924 | if ( fChainedFixups != NULL ) { |
797cc951 | 925 | DyldSharedCache::DataConstLazyScopedWriter patcher(context.dyldCache, mach_task_self(), context.verboseMapping ? &dyld::log : nullptr); |
cf998323 | 926 | const dyld_chained_fixups_header* fixupsHeader = (dyld_chained_fixups_header*)(fLinkEditBase + fChainedFixups->dataoff); |
797cc951 | 927 | doApplyFixups(context, fixupsHeader, patcher); |
cf998323 | 928 | } |
16b475fc | 929 | else if ( fDyldInfo != nullptr ) { |
cf998323 A |
930 | #if TEXT_RELOC_SUPPORT |
931 | // if there are __TEXT fixups, temporarily make __TEXT writable | |
932 | if ( fTextSegmentBinds ) | |
933 | this->makeTextSegmentWritable(context, true); | |
934 | #endif | |
935 | ||
797cc951 A |
936 | // make the cache writable for this block |
937 | DyldSharedCache::DataConstLazyScopedWriter patcher(context.dyldCache, mach_task_self(), context.verboseMapping ? &dyld::log : nullptr); | |
938 | auto* patcherPtr = &patcher; | |
939 | if ( this->inSharedCache() ) | |
940 | patcher.makeWriteable(); | |
941 | ||
cf998323 A |
942 | // run through all binding opcodes |
943 | eachBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image, | |
944 | uintptr_t addr, uint8_t type, const char* symbolName, | |
945 | uint8_t symbolFlags, intptr_t addend, long libraryOrdinal, | |
946 | ExtraBindData *extraBindData, | |
947 | const char* msg, LastLookup* last, bool runResolver) { | |
948 | return ImageLoaderMachOCompressed::bindAt(ctx, image, addr, type, symbolName, symbolFlags, | |
949 | addend, libraryOrdinal, extraBindData, | |
797cc951 | 950 | msg, *patcherPtr, last, runResolver); |
cf998323 A |
951 | }); |
952 | ||
953 | #if TEXT_RELOC_SUPPORT | |
954 | // if there were __TEXT fixups, restore write protection | |
955 | if ( fTextSegmentBinds ) | |
956 | this->makeTextSegmentWritable(context, false); | |
957 | #endif | |
958 | ||
959 | // if this image is in the shared cache, but depends on something no longer in the shared cache, | |
960 | // there is no way to reset the lazy pointers, so force bind them now | |
961 | if ( forceLazysBound || fInSharedCache ) | |
797cc951 | 962 | this->doBindJustLazies(context, patcher); |
cf998323 A |
963 | |
964 | // this image is in cache, but something below it is not. If | |
965 | // this image has lazy pointer to a resolver function, then | |
966 | // the stub may have been altered to point to a shared lazy pointer. | |
967 | if ( fInSharedCache ) | |
968 | this->updateOptimizedLazyPointers(context); | |
969 | } | |
9f83892a A |
970 | |
971 | uint64_t t1 = mach_absolute_time(); | |
972 | ImageLoader::fgTotalRebindCacheTime += (t1-t0); | |
39a8cd10 | 973 | } |
6cae9b63 | 974 | |
6cae9b63 A |
975 | // See if this dylib overrides something in the dyld cache |
976 | uint32_t dyldCacheOverrideImageNum; | |
cf998323 | 977 | if ( context.dyldCache && context.dyldCache->header.builtFromChainedFixups && overridesCachedDylib(dyldCacheOverrideImageNum) ) { |
797cc951 A |
978 | |
979 | // make the cache writable for this block | |
980 | DyldSharedCache::DataConstLazyScopedWriter patcher(context.dyldCache, mach_task_self(), context.verboseMapping ? &dyld::log : nullptr); | |
981 | auto* patcherPtr = &patcher; | |
982 | ||
6cae9b63 A |
983 | // need to patch all other places in cache that point to the overridden dylib, to point to this dylib instead |
984 | const dyld3::closure::Image* overriddenImage = context.dyldCache->cachedDylibsImageArray()->imageForNum(dyldCacheOverrideImageNum); | |
cf998323 | 985 | uint32_t imageIndex = dyldCacheOverrideImageNum - (uint32_t)context.dyldCache->cachedDylibsImageArray()->startImageNum(); |
bc3b7c8c | 986 | //dyld::log("doBind() found override of %s\n", this->getPath()); |
cf998323 | 987 | context.dyldCache->forEachPatchableExport(imageIndex, ^(uint32_t cacheOffsetOfImpl, const char* exportName) { |
6cae9b63 | 988 | uintptr_t newImpl = 0; |
bc3b7c8c A |
989 | const ImageLoader* foundIn = nullptr; |
990 | if ( this->findExportedSymbolAddress(context, exportName, NULL, 0, false, &foundIn, &newImpl) ) { | |
991 | //dyld::log(" patchCacheUsesOf(%s) found in %s\n", exportName, foundIn->getPath()); | |
797cc951 | 992 | patchCacheUsesOf(context, overriddenImage, cacheOffsetOfImpl, exportName, newImpl, *patcherPtr); |
bc3b7c8c A |
993 | } |
994 | else { | |
995 | // <rdar://problem/59196856> allow patched impls to move between re-export sibling dylibs | |
996 | if ( reExportParent != nullptr ) { | |
997 | reExportParent->forEachReExportDependent(^(const ImageLoader* reExportedDep, bool& stop) { | |
998 | uintptr_t siblingImpl = 0; | |
999 | const ImageLoader* foundInSibling = nullptr; | |
1000 | if ( reExportedDep->findExportedSymbolAddress(context, exportName, NULL, 0, false, &foundInSibling, &siblingImpl) ) { | |
1001 | stop = true; | |
1002 | //dyld::log(" patchCacheUsesOf(%s) found in sibling %s\n", exportName, foundInSibling->getPath()); | |
797cc951 | 1003 | patchCacheUsesOf(context, overriddenImage, cacheOffsetOfImpl, exportName, siblingImpl, *patcherPtr); |
bc3b7c8c A |
1004 | } |
1005 | }); | |
1006 | } | |
1007 | } | |
6cae9b63 A |
1008 | }); |
1009 | } | |
cf998323 | 1010 | |
39a8cd10 A |
1011 | // set up dyld entry points in image |
1012 | // do last so flat main executables will have __dyld or __program_vars set up | |
1013 | this->setupLazyPointerHandler(context); | |
412ebb8e | 1014 | CRSetCrashLogMessage2(NULL); |
39a8cd10 A |
1015 | } |
1016 | ||
1017 | ||
797cc951 | 1018 | void ImageLoaderMachOCompressed::doBindJustLazies(const LinkContext& context, DyldSharedCache::DataConstLazyScopedWriter& patcher) |
39a8cd10 | 1019 | { |
6cae9b63 A |
1020 | eachLazyBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image, |
1021 | uintptr_t addr, uint8_t type, const char* symbolName, | |
1022 | uint8_t symbolFlags, intptr_t addend, long libraryOrdinal, | |
1023 | ExtraBindData *extraBindData, | |
1024 | const char* msg, LastLookup* last, bool runResolver) { | |
1025 | return ImageLoaderMachOCompressed::bindAt(ctx, image, addr, type, symbolName, symbolFlags, | |
1026 | addend, libraryOrdinal, extraBindData, | |
797cc951 | 1027 | msg, patcher, last, runResolver); |
6cae9b63 | 1028 | }); |
39a8cd10 A |
1029 | } |
1030 | ||
797cc951 A |
1031 | void ImageLoaderMachOCompressed::doApplyFixups(const LinkContext& context, const dyld_chained_fixups_header* fixupsHeader, |
1032 | DyldSharedCache::DataConstLazyScopedWriter& patcher) | |
cf998323 A |
1033 | { |
1034 | const dyld3::MachOLoaded* ml = (dyld3::MachOLoaded*)machHeader(); | |
1035 | const dyld_chained_starts_in_image* starts = (dyld_chained_starts_in_image*)((uint8_t*)fixupsHeader + fixupsHeader->starts_offset); | |
1036 | ||
1037 | // build table of resolved targets for each symbol ordinal | |
1038 | STACK_ALLOC_OVERFLOW_SAFE_ARRAY(const void*, targetAddrs, 128); | |
1039 | targetAddrs.reserve(fixupsHeader->imports_count); | |
16b475fc | 1040 | __block Diagnostics diag; |
cf998323 A |
1041 | const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)ml; |
1042 | ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) { | |
1043 | const ImageLoader* targetImage; | |
1044 | uint8_t symbolFlags = weakImport ? BIND_SYMBOL_FLAGS_WEAK_IMPORT : 0; | |
16b475fc | 1045 | try { |
797cc951 | 1046 | uintptr_t symbolAddress = this->resolve(context, symbolName, symbolFlags, libOrdinal, &targetImage, patcher, NULL, true); |
16b475fc A |
1047 | targetAddrs.push_back((void*)(symbolAddress + addend)); |
1048 | } | |
1049 | catch (const char* msg) { | |
1050 | stop = true; | |
1051 | diag.error("%s", msg); | |
1052 | } | |
cf998323 | 1053 | }); |
16b475fc A |
1054 | if ( diag.hasError() ) |
1055 | throw strdup(diag.errorMessage()); | |
cf998323 A |
1056 | |
1057 | auto logFixups = ^(void* loc, void* newValue) { | |
1058 | dyld::log("dyld: fixup: %s:%p = %p\n", this->getShortName(), loc, newValue); | |
1059 | }; | |
1060 | if ( !context.verboseBind ) | |
1061 | logFixups = nullptr; | |
1062 | ||
1063 | ml->fixupAllChainedFixups(diag, starts, fSlide, targetAddrs, logFixups); | |
16b475fc A |
1064 | if ( diag.hasError() ) |
1065 | throw strdup(diag.errorMessage()); | |
cf998323 A |
1066 | } |
1067 | ||
6cae9b63 | 1068 | void ImageLoaderMachOCompressed::registerInterposing(const LinkContext& context) |
10b92d3b | 1069 | { |
6cae9b63 A |
1070 | // mach-o files advertise interposing by having a __DATA __interpose section |
1071 | struct InterposeData { uintptr_t replacement; uintptr_t replacee; }; | |
cf998323 | 1072 | |
797cc951 A |
1073 | // FIDME: It seems wrong to need a patcher here, but resolve may call resolveWeak and patch the cache. |
1074 | // That would require weak symbols in the interposing section though, which may not be supported. | |
1075 | DyldSharedCache::DataConstLazyScopedWriter patcher(context.dyldCache, mach_task_self(), context.verboseMapping ? &dyld::log : nullptr); | |
1076 | auto* patcherPtr = &patcher; | |
1077 | ||
cf998323 A |
1078 | __block Diagnostics diag; |
1079 | const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)fMachOData; | |
1080 | ma->forEachInterposingSection(diag, ^(uint64_t vmOffset, uint64_t vmSize, bool& stopSections) { | |
1081 | if ( ma->hasChainedFixups() ) { | |
1082 | const uint16_t pointerFormat = ma->chainedPointerFormat(); | |
1083 | const uint8_t* sectionStart = fMachOData+vmOffset; | |
1084 | const uint8_t* sectionEnd = fMachOData+vmOffset+vmSize; | |
1085 | ma->withChainStarts(diag, ma->chainStartsOffset(), ^(const dyld_chained_starts_in_image* startsInfo) { | |
1086 | __block uintptr_t lastRebaseTarget = 0; | |
1087 | ma->forEachFixupInAllChains(diag, startsInfo, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stopFixups) { | |
1088 | if ( ((uint8_t*)fixupLoc < sectionStart) || ((uint8_t*)fixupLoc >= sectionEnd) ) | |
1089 | return; | |
1090 | uint64_t rebaseTargetRuntimeOffset; | |
1091 | uint32_t bindOrdinal; | |
bc3b7c8c | 1092 | int64_t ptrAddend; |
cf998323 A |
1093 | if ( fixupLoc->isRebase(pointerFormat, 0, rebaseTargetRuntimeOffset) ) { |
1094 | //dyld::log("interpose rebase at fixup at %p to 0x%0llX\n", fixupLoc, rebaseTargetRuntimeOffset); | |
1095 | lastRebaseTarget = (uintptr_t)(fMachOData+rebaseTargetRuntimeOffset); | |
1096 | } | |
bc3b7c8c | 1097 | else if ( fixupLoc->isBind(pointerFormat, bindOrdinal, ptrAddend) ) { |
cf998323 A |
1098 | //dyld::log("interpose bind fixup at %p to bind ordinal %d\n", fixupLoc, bindOrdinal); |
1099 | __block uint32_t targetBindIndex = 0; | |
1100 | ma->forEachChainedFixupTarget(diag, ^(int libraryOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) { | |
1101 | if ( targetBindIndex == bindOrdinal ) { | |
1102 | //dyld::log("interpose bind fixup at %p is to %s libOrdinal=%d\n", fixupLoc, symbolName, libraryOrdinal); | |
1103 | LastLookup* last = NULL; | |
1104 | const ImageLoader* targetImage; | |
1105 | uintptr_t targetBindAddress = 0; | |
1106 | try { | |
797cc951 | 1107 | targetBindAddress = this->resolve(context, symbolName, 0, libraryOrdinal, &targetImage, *patcherPtr, last, false); |
cf998323 A |
1108 | } |
1109 | catch (const char* msg) { | |
1110 | if ( !weakImport ) | |
1111 | throw msg; | |
1112 | targetBindAddress = 0; | |
6cae9b63 | 1113 | } |
cf998323 A |
1114 | //dyld::log("interpose bind fixup at %p is bound to 0x%lX\n", fixupLoc, targetBindAddress); |
1115 | // <rdar://problem/25686570> ignore interposing on a weak function that does not exist | |
1116 | if ( targetBindAddress == 0 ) | |
1117 | return; | |
1118 | ImageLoader::InterposeTuple tuple; | |
1119 | tuple.replacement = lastRebaseTarget; | |
1120 | tuple.neverImage = this; | |
1121 | tuple.onlyImage = NULL; | |
1122 | tuple.replacee = targetBindAddress; | |
1123 | // <rdar://problem/7937695> verify that replacement is in this image | |
1124 | if ( this->containsAddress((void*)tuple.replacement) ) { | |
1125 | if ( context.verboseInterposing ) | |
1126 | dyld::log("dyld: interposing 0x%lx with 0x%lx\n", tuple.replacee, tuple.replacement); | |
1127 | // chain to any existing interpositions | |
1128 | for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { | |
1129 | if ( it->replacee == tuple.replacee ) { | |
1130 | tuple.replacee = it->replacement; | |
1131 | } | |
6cae9b63 | 1132 | } |
cf998323 | 1133 | ImageLoader::fgInterposingTuples.push_back(tuple); |
6cae9b63 A |
1134 | } |
1135 | } | |
cf998323 | 1136 | ++targetBindIndex; |
6cae9b63 | 1137 | }); |
cf998323 A |
1138 | } |
1139 | }); | |
1140 | }); | |
1141 | } | |
1142 | else { | |
1143 | // traditional (non-chained) fixups | |
1144 | const size_t count = (size_t)(vmSize / sizeof(InterposeData)); | |
1145 | const InterposeData* interposeArray = (InterposeData*)(fMachOData+vmOffset); | |
1146 | if ( context.verboseInterposing ) | |
1147 | dyld::log("dyld: found %lu interposing tuples in %s\n", count, getPath()); | |
1148 | for (size_t j=0; j < count; ++j) { | |
1149 | uint64_t bindOffset = ((uint8_t*)&(interposeArray[j].replacee)) - fMachOData; | |
1150 | ma->forEachBind(diag, ^(uint64_t runtimeOffset, int libOrdinal, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stopBinds) { | |
1151 | if ( bindOffset != runtimeOffset ) | |
1152 | return; | |
1153 | stopBinds = true; | |
1154 | LastLookup* last = NULL; | |
1155 | const ImageLoader* targetImage; | |
1156 | uintptr_t targetBindAddress = 0; | |
1157 | try { | |
797cc951 | 1158 | targetBindAddress = this->resolve(context, symbolName, 0, libOrdinal, &targetImage, *patcherPtr, last, false); |
cf998323 A |
1159 | } |
1160 | catch (const char* msg) { | |
1161 | if ( !weakImport ) | |
1162 | throw msg; | |
1163 | targetBindAddress = 0; | |
1164 | } | |
1165 | ImageLoader::InterposeTuple tuple; | |
1166 | tuple.replacement = interposeArray[j].replacement; | |
1167 | tuple.neverImage = this; | |
1168 | tuple.onlyImage = NULL; | |
1169 | tuple.replacee = targetBindAddress; | |
1170 | // <rdar://problem/25686570> ignore interposing on a weak function that does not exist | |
1171 | if ( tuple.replacee == 0 ) | |
1172 | return; | |
1173 | // <rdar://problem/7937695> verify that replacement is in this image | |
1174 | if ( this->containsAddress((void*)tuple.replacement) ) { | |
1175 | if ( context.verboseInterposing ) | |
1176 | dyld::log("dyld: interposing 0x%lx with 0x%lx\n", tuple.replacee, tuple.replacement); | |
1177 | // chain to any existing interpositions | |
1178 | for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { | |
1179 | if ( it->replacee == tuple.replacee ) { | |
1180 | tuple.replacee = it->replacement; | |
6cae9b63 A |
1181 | } |
1182 | } | |
cf998323 | 1183 | ImageLoader::fgInterposingTuples.push_back(tuple); |
6cae9b63 | 1184 | } |
bc3b7c8c A |
1185 | }, ^(const char* symbolName){ |
1186 | }); | |
6cae9b63 | 1187 | } |
6cae9b63 | 1188 | } |
cf998323 | 1189 | }); |
10b92d3b | 1190 | } |
6cae9b63 A |
1191 | |
1192 | bool ImageLoaderMachOCompressed::usesChainedFixups() const | |
1193 | { | |
cf998323 | 1194 | return ((dyld3::MachOLoaded*)machHeader())->hasChainedFixups(); |
6cae9b63 A |
1195 | } |
1196 | ||
1197 | struct ThreadedBindData { | |
1198 | ThreadedBindData(const char* symbolName, int64_t addend, long libraryOrdinal, uint8_t symbolFlags, uint8_t type) | |
1199 | : symbolName(symbolName), addend(addend), libraryOrdinal(libraryOrdinal), symbolFlags(symbolFlags), type(type) { } | |
1200 | ||
1201 | std::tuple<const char*, int64_t, long, bool, uint8_t> pack() const { | |
1202 | return std::make_tuple(symbolName, addend, libraryOrdinal, symbolFlags, type); | |
1203 | } | |
1204 | ||
1205 | const char* symbolName = nullptr; | |
1206 | int64_t addend = 0; | |
1207 | long libraryOrdinal = 0; | |
1208 | uint8_t symbolFlags = 0; | |
1209 | uint8_t type = 0; | |
1210 | }; | |
10b92d3b | 1211 | |
cf998323 A |
1212 | void ImageLoaderMachOCompressed::makeDataReadOnly() const |
1213 | { | |
1214 | #if !TEXT_RELOC_SUPPORT | |
1215 | if ( fReadOnlyDataSegment && !this->ImageLoader::inSharedCache() ) { | |
1216 | for (unsigned int i=0; i < fSegmentsCount; ++i) { | |
1217 | if ( segIsReadOnlyData(i) ) { | |
1218 | uintptr_t start = segActualLoadAddress(i); | |
1219 | uintptr_t size = segSize(i); | |
bc3b7c8c A |
1220 | #if defined(__x86_64__) && !TARGET_OS_SIMULATOR |
1221 | if ( dyld::isTranslated() ) { | |
1222 | // <rdar://problem/48325338> can't mprotect non-16KB segments | |
1223 | if ( ((size & 0x3FFF) != 0) || ((start & 0x3FFF) != 0) ) | |
1224 | continue; | |
1225 | } | |
1226 | #endif | |
cf998323 A |
1227 | ::mprotect((void*)start, size, PROT_READ); |
1228 | //dyld::log("make read-only 0x%09lX -> 0x%09lX\n", (long)start, (long)(start+size)); | |
1229 | } | |
1230 | } | |
1231 | } | |
1232 | #endif | |
1233 | } | |
1234 | ||
1235 | ||
39a8cd10 A |
1236 | void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handler handler) |
1237 | { | |
6cae9b63 | 1238 | try { |
39a8cd10 | 1239 | uint8_t type = 0; |
9f83892a | 1240 | int segmentIndex = -1; |
39a8cd10 | 1241 | uintptr_t address = segActualLoadAddress(0); |
9f83892a | 1242 | uintptr_t segmentStartAddress = segActualLoadAddress(0); |
39a8cd10 A |
1243 | uintptr_t segmentEndAddress = segActualEndAddress(0); |
1244 | const char* symbolName = NULL; | |
1245 | uint8_t symboFlags = 0; | |
9f83892a | 1246 | bool libraryOrdinalSet = false; |
19894a12 | 1247 | long libraryOrdinal = 0; |
39a8cd10 | 1248 | intptr_t addend = 0; |
19894a12 A |
1249 | uintptr_t count; |
1250 | uintptr_t skip; | |
6cae9b63 A |
1251 | uintptr_t segOffset = 0; |
1252 | ||
1253 | dyld3::OverflowSafeArray<ThreadedBindData> ordinalTable; | |
1254 | bool useThreadedRebaseBind = false; | |
1255 | ExtraBindData extraBindData; | |
39a8cd10 A |
1256 | LastLookup last = { 0, 0, NULL, 0, NULL }; |
1257 | const uint8_t* const start = fLinkEditBase + fDyldInfo->bind_off; | |
1258 | const uint8_t* const end = &start[fDyldInfo->bind_size]; | |
1259 | const uint8_t* p = start; | |
1260 | bool done = false; | |
1261 | while ( !done && (p < end) ) { | |
1262 | uint8_t immediate = *p & BIND_IMMEDIATE_MASK; | |
1263 | uint8_t opcode = *p & BIND_OPCODE_MASK; | |
1264 | ++p; | |
1265 | switch (opcode) { | |
1266 | case BIND_OPCODE_DONE: | |
1267 | done = true; | |
1268 | break; | |
1269 | case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: | |
1270 | libraryOrdinal = immediate; | |
9f83892a | 1271 | libraryOrdinalSet = true; |
39a8cd10 A |
1272 | break; |
1273 | case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: | |
1274 | libraryOrdinal = read_uleb128(p, end); | |
9f83892a | 1275 | libraryOrdinalSet = true; |
39a8cd10 A |
1276 | break; |
1277 | case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: | |
1278 | // the special ordinals are negative numbers | |
1279 | if ( immediate == 0 ) | |
1280 | libraryOrdinal = 0; | |
1281 | else { | |
1282 | int8_t signExtended = BIND_OPCODE_MASK | immediate; | |
1283 | libraryOrdinal = signExtended; | |
1284 | } | |
9f83892a | 1285 | libraryOrdinalSet = true; |
39a8cd10 A |
1286 | break; |
1287 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: | |
1288 | symbolName = (char*)p; | |
1289 | symboFlags = immediate; | |
1290 | while (*p != '\0') | |
1291 | ++p; | |
1292 | ++p; | |
1293 | break; | |
1294 | case BIND_OPCODE_SET_TYPE_IMM: | |
1295 | type = immediate; | |
1296 | break; | |
1297 | case BIND_OPCODE_SET_ADDEND_SLEB: | |
1298 | addend = read_sleb128(p, end); | |
1299 | break; | |
1300 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
1301 | segmentIndex = immediate; | |
9f83892a A |
1302 | if ( (segmentIndex >= fSegmentsCount) || (segmentIndex < 0) ) |
1303 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is out of range (0..%d)", | |
19894a12 | 1304 | segmentIndex, fSegmentsCount-1); |
9f83892a A |
1305 | #if TEXT_RELOC_SUPPORT |
1306 | if ( !segWriteable(segmentIndex) && !segHasRebaseFixUps(segmentIndex) && !segHasBindFixUps(segmentIndex) ) | |
1307 | #else | |
1308 | if ( !segWriteable(segmentIndex) ) | |
1309 | #endif | |
1310 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is not writable", segmentIndex); | |
1311 | segOffset = read_uleb128(p, end); | |
1312 | if ( segOffset > segSize(segmentIndex) ) | |
1313 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(segmentIndex)); | |
1314 | segmentStartAddress = segActualLoadAddress(segmentIndex); | |
1315 | address = segmentStartAddress + segOffset; | |
39a8cd10 A |
1316 | segmentEndAddress = segActualEndAddress(segmentIndex); |
1317 | break; | |
1318 | case BIND_OPCODE_ADD_ADDR_ULEB: | |
1319 | address += read_uleb128(p, end); | |
1320 | break; | |
1321 | case BIND_OPCODE_DO_BIND: | |
6cae9b63 A |
1322 | if (!useThreadedRebaseBind) { |
1323 | if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) | |
1324 | throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); | |
1325 | if ( symbolName == NULL ) | |
1326 | dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM"); | |
1327 | if ( segmentIndex == -1 ) | |
1328 | dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB"); | |
1329 | if ( !libraryOrdinalSet ) | |
1330 | dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*"); | |
1331 | handler(context, this, address, type, symbolName, symboFlags, addend, libraryOrdinal, | |
1332 | &extraBindData, "", &last, false); | |
1333 | address += sizeof(intptr_t); | |
1334 | } else { | |
1335 | ordinalTable.push_back(ThreadedBindData(symbolName, addend, libraryOrdinal, symboFlags, type)); | |
1336 | } | |
39a8cd10 A |
1337 | break; |
1338 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: | |
9f83892a | 1339 | if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) |
39a8cd10 | 1340 | throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); |
9f83892a A |
1341 | if ( symbolName == NULL ) |
1342 | dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM"); | |
1343 | if ( segmentIndex == -1 ) | |
1344 | dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB"); | |
1345 | if ( !libraryOrdinalSet ) | |
1346 | dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*"); | |
6cae9b63 A |
1347 | handler(context, this, address, type, symbolName, symboFlags, addend, libraryOrdinal, |
1348 | &extraBindData, "", &last, false); | |
39a8cd10 A |
1349 | address += read_uleb128(p, end) + sizeof(intptr_t); |
1350 | break; | |
1351 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: | |
9f83892a | 1352 | if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) |
39a8cd10 | 1353 | throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); |
9f83892a A |
1354 | if ( symbolName == NULL ) |
1355 | dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM"); | |
1356 | if ( segmentIndex == -1 ) | |
1357 | dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB"); | |
1358 | if ( !libraryOrdinalSet ) | |
1359 | dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*"); | |
6cae9b63 A |
1360 | handler(context, this, address, type, symbolName, symboFlags, addend, libraryOrdinal, |
1361 | &extraBindData, "", &last, false); | |
39a8cd10 A |
1362 | address += immediate*sizeof(intptr_t) + sizeof(intptr_t); |
1363 | break; | |
1364 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: | |
9f83892a A |
1365 | if ( symbolName == NULL ) |
1366 | dyld::throwf("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM"); | |
1367 | if ( segmentIndex == -1 ) | |
1368 | dyld::throwf("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB"); | |
39a8cd10 | 1369 | count = read_uleb128(p, end); |
9f83892a A |
1370 | if ( !libraryOrdinalSet ) |
1371 | dyld::throwf("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*"); | |
39a8cd10 A |
1372 | skip = read_uleb128(p, end); |
1373 | for (uint32_t i=0; i < count; ++i) { | |
9f83892a | 1374 | if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) |
39a8cd10 | 1375 | throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); |
6cae9b63 A |
1376 | handler(context, this, address, type, symbolName, symboFlags, addend, libraryOrdinal, |
1377 | &extraBindData, "", &last, false); | |
39a8cd10 A |
1378 | address += skip + sizeof(intptr_t); |
1379 | } | |
6cae9b63 A |
1380 | break; |
1381 | case BIND_OPCODE_THREADED: | |
1382 | if (sizeof(intptr_t) != 8) { | |
1383 | dyld::throwf("BIND_OPCODE_THREADED require 64-bit"); | |
1384 | return; | |
1385 | } | |
1386 | // Note the immediate is a sub opcode | |
1387 | switch (immediate) { | |
1388 | case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB: | |
1389 | count = read_uleb128(p, end); | |
1390 | ordinalTable.clear(); | |
1391 | // FIXME: ld64 wrote the wrong value here and we need to offset by 1 for now. | |
1392 | ordinalTable.reserve(count + 1); | |
1393 | useThreadedRebaseBind = true; | |
1394 | break; | |
1395 | case BIND_SUBOPCODE_THREADED_APPLY: { | |
1396 | uint64_t delta = 0; | |
1397 | do { | |
1398 | address = segmentStartAddress + segOffset; | |
1399 | uint64_t value = *(uint64_t*)address; | |
1400 | ||
1401 | ||
1402 | bool isRebase = (value & (1ULL << 62)) == 0; | |
1403 | if (isRebase) { | |
1404 | { | |
1405 | // Call the bind handler which knows about our bind type being set to rebase | |
1406 | handler(context, this, address, BIND_TYPE_THREADED_REBASE, nullptr, 0, 0, 0, | |
1407 | nullptr, "", &last, false); | |
1408 | } | |
1409 | } else { | |
1410 | // the ordinal is bits [0..15] | |
1411 | uint16_t ordinal = value & 0xFFFF; | |
1412 | if (ordinal >= ordinalTable.count()) { | |
cf998323 A |
1413 | dyld::throwf("bind ordinal (%d) is out of range (max=%lu) for disk pointer 0x%16llX at segIndex=%d, segOffset=0x%0lX in %s", |
1414 | ordinal, ordinalTable.count(),value, segmentIndex, segOffset, this->getPath()); | |
6cae9b63 A |
1415 | return; |
1416 | } | |
1417 | std::tie(symbolName, addend, libraryOrdinal, symboFlags, type) = ordinalTable[ordinal].pack(); | |
1418 | if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) | |
1419 | throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); | |
1420 | { | |
1421 | handler(context, this, address, BIND_TYPE_THREADED_BIND, | |
1422 | symbolName, symboFlags, addend, libraryOrdinal, | |
1423 | nullptr, "", &last, false); | |
1424 | } | |
1425 | } | |
1426 | ||
1427 | // The delta is bits [51..61] | |
1428 | // And bit 62 is to tell us if we are a rebase (0) or bind (1) | |
1429 | value &= ~(1ULL << 62); | |
1430 | delta = ( value & 0x3FF8000000000000 ) >> 51; | |
1431 | segOffset += delta * sizeof(intptr_t); | |
1432 | } while ( delta != 0 ); | |
1433 | break; | |
1434 | } | |
1435 | ||
1436 | default: | |
1437 | dyld::throwf("bad threaded bind subopcode 0x%02X", *p); | |
1438 | } | |
1439 | break; | |
39a8cd10 A |
1440 | default: |
1441 | dyld::throwf("bad bind opcode %d in bind info", *p); | |
1442 | } | |
1443 | } | |
1444 | } | |
1445 | catch (const char* msg) { | |
1446 | const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); | |
1447 | free((void*)msg); | |
1448 | throw newMsg; | |
1449 | } | |
1450 | } | |
1451 | ||
1452 | void ImageLoaderMachOCompressed::eachLazyBind(const LinkContext& context, bind_handler handler) | |
1453 | { | |
1454 | try { | |
1455 | uint8_t type = BIND_TYPE_POINTER; | |
9f83892a | 1456 | int segmentIndex = -1; |
39a8cd10 | 1457 | uintptr_t address = segActualLoadAddress(0); |
9f83892a | 1458 | uintptr_t segmentStartAddress = segActualLoadAddress(0); |
39a8cd10 | 1459 | uintptr_t segmentEndAddress = segActualEndAddress(0); |
9f83892a | 1460 | uintptr_t segOffset; |
39a8cd10 A |
1461 | const char* symbolName = NULL; |
1462 | uint8_t symboFlags = 0; | |
19894a12 | 1463 | long libraryOrdinal = 0; |
39a8cd10 A |
1464 | intptr_t addend = 0; |
1465 | const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off; | |
1466 | const uint8_t* const end = &start[fDyldInfo->lazy_bind_size]; | |
1467 | const uint8_t* p = start; | |
1468 | bool done = false; | |
1469 | while ( !done && (p < end) ) { | |
1470 | uint8_t immediate = *p & BIND_IMMEDIATE_MASK; | |
1471 | uint8_t opcode = *p & BIND_OPCODE_MASK; | |
1472 | ++p; | |
1473 | switch (opcode) { | |
1474 | case BIND_OPCODE_DONE: | |
1475 | // there is BIND_OPCODE_DONE at end of each lazy bind, don't stop until end of whole sequence | |
1476 | break; | |
1477 | case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: | |
1478 | libraryOrdinal = immediate; | |
1479 | break; | |
1480 | case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: | |
1481 | libraryOrdinal = read_uleb128(p, end); | |
1482 | break; | |
1483 | case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: | |
1484 | // the special ordinals are negative numbers | |
1485 | if ( immediate == 0 ) | |
1486 | libraryOrdinal = 0; | |
1487 | else { | |
1488 | int8_t signExtended = BIND_OPCODE_MASK | immediate; | |
1489 | libraryOrdinal = signExtended; | |
1490 | } | |
1491 | break; | |
1492 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: | |
1493 | symbolName = (char*)p; | |
1494 | symboFlags = immediate; | |
1495 | while (*p != '\0') | |
1496 | ++p; | |
1497 | ++p; | |
1498 | break; | |
1499 | case BIND_OPCODE_SET_TYPE_IMM: | |
1500 | type = immediate; | |
1501 | break; | |
1502 | case BIND_OPCODE_SET_ADDEND_SLEB: | |
1503 | addend = read_sleb128(p, end); | |
1504 | break; | |
1505 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
1506 | segmentIndex = immediate; | |
9f83892a A |
1507 | if ( (segmentIndex >= fSegmentsCount) || (segmentIndex < 0) ) |
1508 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is out of range (0..%d)", | |
19894a12 | 1509 | segmentIndex, fSegmentsCount-1); |
9f83892a A |
1510 | if ( !segWriteable(segmentIndex) ) |
1511 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is not writable", segmentIndex); | |
1512 | segOffset = read_uleb128(p, end); | |
1513 | if ( segOffset > segSize(segmentIndex) ) | |
1514 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(segmentIndex)); | |
1515 | segmentStartAddress = segActualLoadAddress(segmentIndex); | |
39a8cd10 | 1516 | segmentEndAddress = segActualEndAddress(segmentIndex); |
9f83892a | 1517 | address = segmentStartAddress + segOffset; |
39a8cd10 A |
1518 | break; |
1519 | case BIND_OPCODE_ADD_ADDR_ULEB: | |
1520 | address += read_uleb128(p, end); | |
1521 | break; | |
1522 | case BIND_OPCODE_DO_BIND: | |
9f83892a A |
1523 | if ( segmentIndex == -1 ) |
1524 | dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB"); | |
1525 | if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) | |
39a8cd10 | 1526 | throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); |
9f83892a A |
1527 | if ( symbolName == NULL ) |
1528 | dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM"); | |
6cae9b63 A |
1529 | handler(context, this, address, type, symbolName, symboFlags, addend, libraryOrdinal, |
1530 | NULL, "forced lazy ", NULL, false); | |
39a8cd10 A |
1531 | address += sizeof(intptr_t); |
1532 | break; | |
1533 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: | |
1534 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: | |
1535 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: | |
1536 | default: | |
1537 | dyld::throwf("bad lazy bind opcode %d", *p); | |
1538 | } | |
1539 | } | |
1540 | } | |
1541 | ||
1542 | catch (const char* msg) { | |
1543 | const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); | |
1544 | free((void*)msg); | |
1545 | throw newMsg; | |
1546 | } | |
1547 | } | |
1548 | ||
1549 | // A program built targeting 10.5 will have hybrid stubs. When used with weak symbols | |
1550 | // the classic lazy loader is used even when running on 10.6 | |
797cc951 A |
1551 | uintptr_t ImageLoaderMachOCompressed::doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context, |
1552 | DyldSharedCache::DataConstLazyScopedWriter& patcher) | |
39a8cd10 A |
1553 | { |
1554 | // only works with compressed LINKEDIT if classic symbol table is also present | |
1555 | const macho_nlist* symbolTable = NULL; | |
1556 | const char* symbolTableStrings = NULL; | |
1557 | const dysymtab_command* dynSymbolTable = NULL; | |
1558 | const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; | |
1559 | const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; | |
1560 | const struct load_command* cmd = cmds; | |
1561 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
1562 | switch (cmd->cmd) { | |
1563 | case LC_SYMTAB: | |
1564 | { | |
1565 | const struct symtab_command* symtab = (struct symtab_command*)cmd; | |
1566 | symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff]; | |
1567 | symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]); | |
1568 | } | |
1569 | break; | |
1570 | case LC_DYSYMTAB: | |
1571 | dynSymbolTable = (struct dysymtab_command*)cmd; | |
1572 | break; | |
1573 | } | |
1574 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
1575 | } | |
1576 | // no symbol table => no lookup by address | |
1577 | if ( (symbolTable == NULL) || (dynSymbolTable == NULL) ) | |
1578 | dyld::throwf("classic lazy binding used with compressed LINKEDIT at %p in image %s", lazyPointer, this->getPath()); | |
1579 | ||
1580 | // scan for all lazy-pointer sections | |
1581 | const bool twoLevel = this->usesTwoLevelNameSpace(); | |
1582 | const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff]; | |
1583 | cmd = cmds; | |
1584 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
1585 | switch (cmd->cmd) { | |
1586 | case LC_SEGMENT_COMMAND: | |
1587 | { | |
1588 | const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; | |
1589 | const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); | |
1590 | const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; | |
1591 | for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { | |
1592 | const uint8_t type = sect->flags & SECTION_TYPE; | |
1593 | uint32_t symbolIndex = INDIRECT_SYMBOL_LOCAL; | |
1594 | if ( type == S_LAZY_SYMBOL_POINTERS ) { | |
19894a12 | 1595 | const size_t pointerCount = sect->size / sizeof(uintptr_t); |
39a8cd10 A |
1596 | uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); |
1597 | if ( (lazyPointer >= symbolPointers) && (lazyPointer < &symbolPointers[pointerCount]) ) { | |
1598 | const uint32_t indirectTableOffset = sect->reserved1; | |
19894a12 | 1599 | const size_t lazyIndex = lazyPointer - symbolPointers; |
39a8cd10 A |
1600 | symbolIndex = indirectTable[indirectTableOffset + lazyIndex]; |
1601 | } | |
1602 | } | |
1603 | if ( (symbolIndex != INDIRECT_SYMBOL_ABS) && (symbolIndex != INDIRECT_SYMBOL_LOCAL) ) { | |
1604 | const macho_nlist* symbol = &symbolTable[symbolIndex]; | |
1605 | const char* symbolName = &symbolTableStrings[symbol->n_un.n_strx]; | |
1606 | int libraryOrdinal = GET_LIBRARY_ORDINAL(symbol->n_desc); | |
1607 | if ( !twoLevel || context.bindFlat ) | |
1608 | libraryOrdinal = BIND_SPECIAL_DYLIB_FLAT_LOOKUP; | |
1609 | uintptr_t ptrToBind = (uintptr_t)lazyPointer; | |
6cae9b63 | 1610 | uintptr_t symbolAddr = bindAt(context, this, ptrToBind, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, |
797cc951 | 1611 | NULL, "lazy ", patcher, NULL); |
39a8cd10 A |
1612 | ++fgTotalLazyBindFixups; |
1613 | return symbolAddr; | |
1614 | } | |
1615 | } | |
1616 | } | |
1617 | break; | |
1618 | } | |
1619 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
1620 | } | |
1621 | dyld::throwf("lazy pointer not found at address %p in image %s", lazyPointer, this->getPath()); | |
1622 | } | |
1623 | ||
1624 | ||
9f83892a | 1625 | |
412ebb8e A |
1626 | uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, |
1627 | void (*lock)(), void (*unlock)()) | |
39a8cd10 | 1628 | { |
412ebb8e A |
1629 | // <rdar://problem/8663923> race condition with flat-namespace lazy binding |
1630 | if ( this->usesTwoLevelNameSpace() ) { | |
1631 | // two-level namespace lookup does not require lock because dependents can't be unloaded before this image | |
1632 | } | |
1633 | else { | |
1634 | // acquire dyld global lock | |
1635 | if ( lock != NULL ) | |
1636 | lock(); | |
1637 | } | |
797cc951 A |
1638 | |
1639 | DyldSharedCache::DataConstLazyScopedWriter patcher(context.dyldCache, mach_task_self(), context.verboseMapping ? &dyld::log : nullptr); | |
412ebb8e | 1640 | |
39a8cd10 A |
1641 | const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off; |
1642 | const uint8_t* const end = &start[fDyldInfo->lazy_bind_size]; | |
9f83892a A |
1643 | uint8_t segIndex; |
1644 | uintptr_t segOffset; | |
1645 | int libraryOrdinal; | |
1646 | const char* symbolName; | |
1647 | bool doneAfterBind; | |
1648 | uintptr_t result; | |
1649 | do { | |
1650 | if ( ! getLazyBindingInfo(lazyBindingInfoOffset, start, end, &segIndex, &segOffset, &libraryOrdinal, &symbolName, &doneAfterBind) ) | |
1651 | dyld::throwf("bad lazy bind info"); | |
1652 | ||
1653 | if ( segIndex >= fSegmentsCount ) | |
1654 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", | |
1655 | segIndex, fSegmentsCount-1); | |
1656 | if ( segOffset > segSize(segIndex) ) | |
1657 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(segIndex)); | |
1658 | uintptr_t address = segActualLoadAddress(segIndex) + segOffset; | |
6cae9b63 | 1659 | result = bindAt(context, this, address, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, |
797cc951 | 1660 | NULL, "lazy ", patcher, NULL, true); |
9f83892a A |
1661 | // <rdar://problem/24140465> Some old apps had multiple lazy symbols bound at once |
1662 | } while (!doneAfterBind && !context.strictMachORequired); | |
39a8cd10 | 1663 | |
412ebb8e A |
1664 | if ( !this->usesTwoLevelNameSpace() ) { |
1665 | // release dyld global lock | |
1666 | if ( unlock != NULL ) | |
1667 | unlock(); | |
1668 | } | |
39a8cd10 A |
1669 | return result; |
1670 | } | |
1671 | ||
9f83892a | 1672 | void ImageLoaderMachOCompressed::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder, unsigned) |
39a8cd10 A |
1673 | { |
1674 | it.image = this; | |
1675 | it.symbolName = " "; | |
1676 | it.loadOrder = loadOrder; | |
1677 | it.weakSymbol = false; | |
1678 | it.symbolMatches = false; | |
1679 | it.done = false; | |
1680 | it.curIndex = 0; | |
cf998323 | 1681 | it.endIndex = (this->fDyldInfo ? this->fDyldInfo->weak_bind_size : 0); |
39a8cd10 A |
1682 | it.address = 0; |
1683 | it.type = 0; | |
1684 | it.addend = 0; | |
1685 | } | |
1686 | ||
1687 | ||
1688 | bool ImageLoaderMachOCompressed::incrementCoalIterator(CoalIterator& it) | |
1689 | { | |
1690 | if ( it.done ) | |
1691 | return false; | |
1692 | ||
cf998323 | 1693 | if ( (this->fDyldInfo == nullptr) || (this->fDyldInfo->weak_bind_size == 0) ) { |
39a8cd10 A |
1694 | /// hmmm, ld set MH_WEAK_DEFINES or MH_BINDS_TO_WEAK, but there is no weak binding info |
1695 | it.done = true; | |
1696 | it.symbolName = "~~~"; | |
1697 | return true; | |
1698 | } | |
1699 | const uint8_t* start = fLinkEditBase + fDyldInfo->weak_bind_off; | |
1700 | const uint8_t* p = start + it.curIndex; | |
1701 | const uint8_t* end = fLinkEditBase + fDyldInfo->weak_bind_off + this->fDyldInfo->weak_bind_size; | |
19894a12 A |
1702 | uintptr_t count; |
1703 | uintptr_t skip; | |
9f83892a | 1704 | uintptr_t segOffset; |
39a8cd10 A |
1705 | while ( p < end ) { |
1706 | uint8_t immediate = *p & BIND_IMMEDIATE_MASK; | |
1707 | uint8_t opcode = *p & BIND_OPCODE_MASK; | |
1708 | ++p; | |
1709 | switch (opcode) { | |
1710 | case BIND_OPCODE_DONE: | |
1711 | it.done = true; | |
1712 | it.curIndex = p - start; | |
1713 | it.symbolName = "~~~"; // sorts to end | |
1714 | return true; | |
1715 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: | |
1716 | it.symbolName = (char*)p; | |
1717 | it.weakSymbol = ((immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) == 0); | |
1718 | it.symbolMatches = false; | |
1719 | while (*p != '\0') | |
1720 | ++p; | |
1721 | ++p; | |
1722 | it.curIndex = p - start; | |
1723 | return false; | |
1724 | case BIND_OPCODE_SET_TYPE_IMM: | |
1725 | it.type = immediate; | |
1726 | break; | |
1727 | case BIND_OPCODE_SET_ADDEND_SLEB: | |
1728 | it.addend = read_sleb128(p, end); | |
1729 | break; | |
1730 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
19894a12 A |
1731 | if ( immediate >= fSegmentsCount ) |
1732 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", | |
1733 | immediate, fSegmentsCount-1); | |
9f83892a A |
1734 | #if __arm__ |
1735 | // <rdar://problem/23138428> iOS app compatibility | |
1736 | if ( !segWriteable(immediate) && it.image->isPositionIndependentExecutable() ) | |
1737 | #elif TEXT_RELOC_SUPPORT | |
1738 | // <rdar://problem/23479396&23590867> i386 OS X app compatibility | |
1739 | if ( !segWriteable(immediate) && !segHasRebaseFixUps(immediate) && !segHasBindFixUps(immediate) | |
1740 | && (!it.image->isExecutable() || it.image->isPositionIndependentExecutable()) ) | |
1741 | #else | |
1742 | if ( !segWriteable(immediate) ) | |
1743 | #endif | |
1744 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB targets segment %s which is not writable", segName(immediate)); | |
1745 | segOffset = read_uleb128(p, end); | |
1746 | if ( segOffset > segSize(immediate) ) | |
1747 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(immediate)); | |
1748 | it.address = segActualLoadAddress(immediate) + segOffset; | |
39a8cd10 A |
1749 | break; |
1750 | case BIND_OPCODE_ADD_ADDR_ULEB: | |
1751 | it.address += read_uleb128(p, end); | |
1752 | break; | |
1753 | case BIND_OPCODE_DO_BIND: | |
1754 | it.address += sizeof(intptr_t); | |
1755 | break; | |
1756 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: | |
1757 | it.address += read_uleb128(p, end) + sizeof(intptr_t); | |
1758 | break; | |
1759 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: | |
1760 | it.address += immediate*sizeof(intptr_t) + sizeof(intptr_t); | |
1761 | break; | |
1762 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: | |
1763 | count = read_uleb128(p, end); | |
1764 | skip = read_uleb128(p, end); | |
1765 | for (uint32_t i=0; i < count; ++i) { | |
1766 | it.address += skip + sizeof(intptr_t); | |
1767 | } | |
1768 | break; | |
1769 | default: | |
2fd3f4e8 | 1770 | dyld::throwf("bad weak bind opcode '%d' found after processing %d bytes in '%s'", *p, (int)(p-start), this->getPath()); |
39a8cd10 A |
1771 | } |
1772 | } | |
1773 | /// hmmm, BIND_OPCODE_DONE is missing... | |
1774 | it.done = true; | |
1775 | it.symbolName = "~~~"; | |
1776 | //dyld::log("missing BIND_OPCODE_DONE for image %s\n", this->getPath()); | |
1777 | return true; | |
1778 | } | |
1779 | ||
1780 | uintptr_t ImageLoaderMachOCompressed::getAddressCoalIterator(CoalIterator& it, const LinkContext& context) | |
1781 | { | |
1782 | //dyld::log("looking for %s in %s\n", it.symbolName, this->getPath()); | |
1783 | const ImageLoader* foundIn = NULL; | |
9f83892a | 1784 | const ImageLoader::Symbol* sym = this->findShallowExportedSymbol(it.symbolName, &foundIn); |
39a8cd10 A |
1785 | if ( sym != NULL ) { |
1786 | //dyld::log("sym=%p, foundIn=%p\n", sym, foundIn); | |
1787 | return foundIn->getExportedSymbolAddress(sym, context, this); | |
1788 | } | |
1789 | return 0; | |
1790 | } | |
1791 | ||
1792 | ||
9f83892a | 1793 | void ImageLoaderMachOCompressed::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, unsigned targetIndex, const LinkContext& context) |
39a8cd10 A |
1794 | { |
1795 | // <rdar://problem/6570879> weak binding done too early with inserted libraries | |
1796 | if ( this->getState() < dyld_image_state_bound ) | |
1797 | return; | |
1798 | ||
cf998323 A |
1799 | if ( fDyldInfo == nullptr ) |
1800 | return; | |
1801 | ||
39a8cd10 A |
1802 | const uint8_t* start = fLinkEditBase + fDyldInfo->weak_bind_off; |
1803 | const uint8_t* p = start + it.curIndex; | |
1804 | const uint8_t* end = fLinkEditBase + fDyldInfo->weak_bind_off + this->fDyldInfo->weak_bind_size; | |
1805 | ||
1806 | uint8_t type = it.type; | |
1807 | uintptr_t address = it.address; | |
1808 | const char* symbolName = it.symbolName; | |
1809 | intptr_t addend = it.addend; | |
19894a12 A |
1810 | uintptr_t count; |
1811 | uintptr_t skip; | |
9f83892a | 1812 | uintptr_t segOffset; |
39a8cd10 A |
1813 | bool done = false; |
1814 | bool boundSomething = false; | |
1815 | while ( !done && (p < end) ) { | |
1816 | uint8_t immediate = *p & BIND_IMMEDIATE_MASK; | |
1817 | uint8_t opcode = *p & BIND_OPCODE_MASK; | |
1818 | ++p; | |
1819 | switch (opcode) { | |
1820 | case BIND_OPCODE_DONE: | |
1821 | done = true; | |
1822 | break; | |
1823 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: | |
1824 | done = true; | |
1825 | break; | |
1826 | case BIND_OPCODE_SET_TYPE_IMM: | |
1827 | type = immediate; | |
1828 | break; | |
1829 | case BIND_OPCODE_SET_ADDEND_SLEB: | |
1830 | addend = read_sleb128(p, end); | |
1831 | break; | |
1832 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: | |
19894a12 A |
1833 | if ( immediate >= fSegmentsCount ) |
1834 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", | |
1835 | immediate, fSegmentsCount-1); | |
9f83892a A |
1836 | #if __arm__ |
1837 | // <rdar://problem/23138428> iOS app compatibility | |
1838 | if ( !segWriteable(immediate) && it.image->isPositionIndependentExecutable() ) | |
1839 | #elif TEXT_RELOC_SUPPORT | |
1840 | // <rdar://problem/23479396&23590867> i386 OS X app compatibility | |
1841 | if ( !segWriteable(immediate) && !segHasRebaseFixUps(immediate) && !segHasBindFixUps(immediate) | |
1842 | && (!it.image->isExecutable() || it.image->isPositionIndependentExecutable()) ) | |
1843 | #else | |
1844 | if ( !segWriteable(immediate) ) | |
1845 | #endif | |
1846 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB targets segment %s which is not writable", segName(immediate)); | |
1847 | segOffset = read_uleb128(p, end); | |
1848 | if ( segOffset > segSize(immediate) ) | |
1849 | dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(immediate)); | |
1850 | address = segActualLoadAddress(immediate) + segOffset; | |
39a8cd10 A |
1851 | break; |
1852 | case BIND_OPCODE_ADD_ADDR_ULEB: | |
1853 | address += read_uleb128(p, end); | |
1854 | break; | |
1855 | case BIND_OPCODE_DO_BIND: | |
6cae9b63 | 1856 | bindLocation(context, this->imageBaseAddress(), address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ", NULL, fSlide); |
39a8cd10 A |
1857 | boundSomething = true; |
1858 | address += sizeof(intptr_t); | |
1859 | break; | |
1860 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: | |
6cae9b63 | 1861 | bindLocation(context, this->imageBaseAddress(), address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ", NULL, fSlide); |
39a8cd10 A |
1862 | boundSomething = true; |
1863 | address += read_uleb128(p, end) + sizeof(intptr_t); | |
1864 | break; | |
1865 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: | |
6cae9b63 | 1866 | bindLocation(context, this->imageBaseAddress(), address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ", NULL, fSlide); |
39a8cd10 A |
1867 | boundSomething = true; |
1868 | address += immediate*sizeof(intptr_t) + sizeof(intptr_t); | |
1869 | break; | |
1870 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: | |
1871 | count = read_uleb128(p, end); | |
1872 | skip = read_uleb128(p, end); | |
1873 | for (uint32_t i=0; i < count; ++i) { | |
6cae9b63 | 1874 | bindLocation(context, this->imageBaseAddress(), address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ", NULL, fSlide); |
39a8cd10 A |
1875 | boundSomething = true; |
1876 | address += skip + sizeof(intptr_t); | |
1877 | } | |
1878 | break; | |
1879 | default: | |
1880 | dyld::throwf("bad bind opcode %d in weak binding info", *p); | |
1881 | } | |
2fd3f4e8 A |
1882 | } |
1883 | // C++ weak coalescing cannot be tracked by reference counting. Error on side of never unloading. | |
1884 | if ( boundSomething && (targetImage != this) ) | |
1885 | context.addDynamicReference(this, targetImage); | |
39a8cd10 A |
1886 | } |
1887 | ||
6cae9b63 A |
1888 | uintptr_t ImageLoaderMachOCompressed::interposeAt(const LinkContext& context, ImageLoaderMachOCompressed* image, |
1889 | uintptr_t addr, uint8_t type, const char*, | |
1890 | uint8_t, intptr_t, long, | |
1891 | ExtraBindData *extraBindData, | |
1892 | const char*, LastLookup*, bool runResolver) | |
412ebb8e A |
1893 | { |
1894 | if ( type == BIND_TYPE_POINTER ) { | |
1895 | uintptr_t* fixupLocation = (uintptr_t*)addr; | |
19894a12 | 1896 | uintptr_t curValue = *fixupLocation; |
6cae9b63 | 1897 | uintptr_t newValue = interposedAddress(context, curValue, image); |
9f83892a | 1898 | if ( newValue != curValue) { |
19894a12 | 1899 | *fixupLocation = newValue; |
9f83892a | 1900 | } |
412ebb8e A |
1901 | } |
1902 | return 0; | |
1903 | } | |
1904 | ||
1905 | void ImageLoaderMachOCompressed::doInterpose(const LinkContext& context) | |
1906 | { | |
1907 | if ( context.verboseInterposing ) | |
1908 | dyld::log("dyld: interposing %lu tuples onto image: %s\n", fgInterposingTuples.size(), this->getPath()); | |
1909 | ||
cf998323 | 1910 | const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)fMachOData; |
16b475fc | 1911 | if ( !ma->hasChainedFixups() && (fDyldInfo != nullptr) ) { |
cf998323 A |
1912 | // Note: all binds that happen as part of normal loading and fixups will have interposing applied. |
1913 | // There is only two cases where we need to parse bind opcodes and apply interposing: | |
1914 | ||
797cc951 A |
1915 | // make the cache writable for this block |
1916 | DyldSharedCache::DataConstLazyScopedWriter patcher(context.dyldCache, mach_task_self(), context.verboseMapping ? &dyld::log : nullptr); | |
1917 | if ( ma->inDyldCache() ) | |
1918 | patcher.makeWriteable(); | |
1919 | ||
cf998323 A |
1920 | // 1) Lazy pointers are either not bound yet, or in dyld cache they are prebound (to uninterposed target) |
1921 | eachLazyBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image, | |
1922 | uintptr_t addr, uint8_t type, const char* symbolName, | |
1923 | uint8_t symbolFlags, intptr_t addend, long libraryOrdinal, | |
1924 | ExtraBindData *extraBindData, | |
1925 | const char* msg, LastLookup* last, bool runResolver) { | |
1926 | return ImageLoaderMachOCompressed::interposeAt(ctx, image, addr, type, symbolName, symbolFlags, | |
1927 | addend, libraryOrdinal, extraBindData, | |
1928 | msg, last, runResolver); | |
1929 | }); | |
1930 | ||
1931 | // 2) non-lazy pointers in the dyld cache need to be interposed | |
1932 | if ( ma->inDyldCache() ) { | |
1933 | eachBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image, | |
1934 | uintptr_t addr, uint8_t type, const char* symbolName, | |
1935 | uint8_t symbolFlags, intptr_t addend, long libraryOrdinal, | |
1936 | ExtraBindData *extraBindData, | |
1937 | const char* msg, LastLookup* last, bool runResolver) { | |
1938 | return ImageLoaderMachOCompressed::interposeAt(ctx, image, addr, type, symbolName, symbolFlags, | |
1939 | addend, libraryOrdinal, extraBindData, | |
1940 | msg, last, runResolver); | |
1941 | }); | |
1942 | } | |
1943 | ||
1944 | } | |
412ebb8e | 1945 | } |
39a8cd10 A |
1946 | |
1947 | ||
6cae9b63 A |
1948 | uintptr_t ImageLoaderMachOCompressed::dynamicInterposeAt(const LinkContext& context, ImageLoaderMachOCompressed* image, |
1949 | uintptr_t addr, uint8_t type, const char* symbolName, | |
1950 | uint8_t, intptr_t, long, | |
1951 | ExtraBindData *extraBindData, | |
1952 | const char*, LastLookup*, bool runResolver) | |
19894a12 A |
1953 | { |
1954 | if ( type == BIND_TYPE_POINTER ) { | |
1955 | uintptr_t* fixupLocation = (uintptr_t*)addr; | |
1956 | uintptr_t value = *fixupLocation; | |
1957 | // don't apply interposing to table entries. | |
1958 | if ( (context.dynamicInterposeArray <= (void*)addr) && ((void*)addr < &context.dynamicInterposeArray[context.dynamicInterposeCount]) ) | |
1959 | return 0; | |
1960 | for(size_t i=0; i < context.dynamicInterposeCount; ++i) { | |
1961 | if ( value == (uintptr_t)context.dynamicInterposeArray[i].replacee ) { | |
1962 | if ( context.verboseInterposing ) { | |
1963 | dyld::log("dyld: dynamic interposing: at %p replace %p with %p in %s\n", | |
6cae9b63 | 1964 | fixupLocation, context.dynamicInterposeArray[i].replacee, context.dynamicInterposeArray[i].replacement, image->getPath()); |
19894a12 A |
1965 | } |
1966 | *fixupLocation = (uintptr_t)context.dynamicInterposeArray[i].replacement; | |
1967 | } | |
1968 | } | |
1969 | } | |
1970 | return 0; | |
1971 | } | |
1972 | ||
1973 | void ImageLoaderMachOCompressed::dynamicInterpose(const LinkContext& context) | |
1974 | { | |
1975 | if ( context.verboseInterposing ) | |
1976 | dyld::log("dyld: dynamic interposing %lu tuples onto image: %s\n", context.dynamicInterposeCount, this->getPath()); | |
1977 | ||
1978 | // update already bound references to symbols | |
6cae9b63 A |
1979 | eachBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image, |
1980 | uintptr_t addr, uint8_t type, const char* symbolName, | |
1981 | uint8_t symbolFlags, intptr_t addend, long libraryOrdinal, | |
1982 | ExtraBindData *extraBindData, | |
1983 | const char* msg, LastLookup* last, bool runResolver) { | |
1984 | return ImageLoaderMachOCompressed::dynamicInterposeAt(ctx, image, addr, type, symbolName, symbolFlags, | |
1985 | addend, libraryOrdinal, extraBindData, | |
1986 | msg, last, runResolver); | |
1987 | }); | |
1988 | eachLazyBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image, | |
1989 | uintptr_t addr, uint8_t type, const char* symbolName, | |
1990 | uint8_t symbolFlags, intptr_t addend, long libraryOrdinal, | |
1991 | ExtraBindData *extraBindData, | |
1992 | const char* msg, LastLookup* last, bool runResolver) { | |
1993 | return ImageLoaderMachOCompressed::dynamicInterposeAt(ctx, image, addr, type, symbolName, symbolFlags, | |
1994 | addend, libraryOrdinal, extraBindData, | |
1995 | msg, last, runResolver); | |
1996 | }); | |
19894a12 | 1997 | } |
39a8cd10 | 1998 | |
39a8cd10 A |
1999 | const char* ImageLoaderMachOCompressed::findClosestSymbol(const void* addr, const void** closestAddr) const |
2000 | { | |
9f83892a | 2001 | return ImageLoaderMachO::findClosestSymbol((mach_header*)fMachOData, addr, closestAddr); |
39a8cd10 A |
2002 | } |
2003 | ||
2004 | ||
2005 | #if PREBOUND_IMAGE_SUPPORT | |
2006 | void ImageLoaderMachOCompressed::resetPreboundLazyPointers(const LinkContext& context) | |
2007 | { | |
2008 | // no way to back off a prebound compress image | |
2009 | } | |
2010 | #endif | |
2011 | ||
412ebb8e A |
2012 | |
2013 | #if __arm__ || __x86_64__ | |
2fd3f4e8 | 2014 | void ImageLoaderMachOCompressed::updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr, const LinkContext& context) |
412ebb8e A |
2015 | { |
2016 | #if __arm__ | |
2017 | uint32_t* instructions = (uint32_t*)stub; | |
2018 | // sanity check this is a stub we understand | |
2019 | if ( (instructions[0] != 0xe59fc004) || (instructions[1] != 0xe08fc00c) || (instructions[2] != 0xe59cf000) ) | |
2020 | return; | |
2021 | ||
2022 | void** lazyPointerAddr = (void**)(instructions[3] + (stub + 12)); | |
2023 | #endif | |
2024 | #if __x86_64__ | |
2025 | // sanity check this is a stub we understand | |
2026 | if ( (stub[0] != 0xFF) || (stub[1] != 0x25) ) | |
2027 | return; | |
2028 | int32_t ripOffset = *((int32_t*)(&stub[2])); | |
2029 | void** lazyPointerAddr = (void**)(ripOffset + stub + 6); | |
2030 | #endif | |
2031 | ||
2032 | // if stub does not use original lazy pointer (meaning it was optimized by update_dyld_shared_cache) | |
2033 | if ( lazyPointerAddr != originalLazyPointerAddr ) { | |
2fd3f4e8 A |
2034 | // <rdar://problem/12928448> only de-optimization lazy pointers if they are part of shared cache not loaded (because overridden) |
2035 | const ImageLoader* lazyPointerImage = context.findImageContainingAddress(lazyPointerAddr); | |
2036 | if ( lazyPointerImage != NULL ) | |
2037 | return; | |
2038 | ||
412ebb8e A |
2039 | // copy newly re-bound lazy pointer value to shared lazy pointer |
2040 | *lazyPointerAddr = *originalLazyPointerAddr; | |
2fd3f4e8 A |
2041 | |
2042 | if ( context.verboseBind ) | |
2043 | dyld::log("dyld: alter bind: %s: *0x%08lX = 0x%08lX \n", | |
2044 | this->getShortName(), (long)lazyPointerAddr, (long)*originalLazyPointerAddr); | |
412ebb8e A |
2045 | } |
2046 | } | |
2047 | #endif | |
2048 | ||
2049 | ||
2050 | // <rdar://problem/8890875> overriding shared cache dylibs with resolvers fails | |
2051 | void ImageLoaderMachOCompressed::updateOptimizedLazyPointers(const LinkContext& context) | |
2052 | { | |
2053 | #if __arm__ || __x86_64__ | |
9f83892a | 2054 | // find stubs and indirect symbol table |
412ebb8e | 2055 | const struct macho_section* stubsSection = NULL; |
412ebb8e A |
2056 | const dysymtab_command* dynSymbolTable = NULL; |
2057 | const macho_header* mh = (macho_header*)fMachOData; | |
2058 | const uint32_t cmd_count = mh->ncmds; | |
2059 | const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; | |
2060 | const struct load_command* cmd = cmds; | |
2061 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
2062 | if (cmd->cmd == LC_SEGMENT_COMMAND) { | |
2063 | const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; | |
2064 | const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); | |
2065 | const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; | |
2066 | for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { | |
2067 | const uint8_t type = sect->flags & SECTION_TYPE; | |
2068 | if ( type == S_SYMBOL_STUBS ) | |
2069 | stubsSection = sect; | |
412ebb8e A |
2070 | } |
2071 | } | |
2072 | else if ( cmd->cmd == LC_DYSYMTAB ) { | |
2073 | dynSymbolTable = (struct dysymtab_command*)cmd; | |
2074 | } | |
2075 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
2076 | } | |
412ebb8e A |
2077 | if ( dynSymbolTable == NULL ) |
2078 | return; | |
9f83892a A |
2079 | const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff]; |
2080 | if ( stubsSection == NULL ) | |
412ebb8e | 2081 | return; |
9f83892a A |
2082 | const uint32_t stubsSize = stubsSection->reserved2; |
2083 | const uint32_t stubsCount = (uint32_t)(stubsSection->size / stubsSize); | |
412ebb8e | 2084 | const uint32_t stubsIndirectTableOffset = stubsSection->reserved1; |
412ebb8e A |
2085 | if ( (stubsIndirectTableOffset+stubsCount) > dynSymbolTable->nindirectsyms ) |
2086 | return; | |
9f83892a A |
2087 | uint8_t* const stubsAddr = (uint8_t*)(stubsSection->addr + this->fSlide); |
2088 | ||
2089 | // for each lazy pointer section | |
2090 | cmd = cmds; | |
2091 | for (uint32_t i = 0; i < cmd_count; ++i) { | |
2092 | if (cmd->cmd == LC_SEGMENT_COMMAND) { | |
2093 | const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; | |
2094 | const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); | |
2095 | const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; | |
2096 | for (const struct macho_section* lazyPointerSection=sectionsStart; lazyPointerSection < sectionsEnd; ++lazyPointerSection) { | |
2097 | const uint8_t type = lazyPointerSection->flags & SECTION_TYPE; | |
2098 | if ( type != S_LAZY_SYMBOL_POINTERS ) | |
2099 | continue; | |
2100 | const uint32_t lazyPointersCount = (uint32_t)(lazyPointerSection->size / sizeof(void*)); | |
2101 | const uint32_t lazyPointersIndirectTableOffset = lazyPointerSection->reserved1; | |
2102 | if ( (lazyPointersIndirectTableOffset+lazyPointersCount) > dynSymbolTable->nindirectsyms ) | |
2103 | continue; | |
2104 | void** const lazyPointersAddr = (void**)(lazyPointerSection->addr + this->fSlide); | |
2105 | // for each lazy pointer | |
2106 | for(uint32_t lpIndex=0; lpIndex < lazyPointersCount; ++lpIndex) { | |
2107 | const uint32_t lpSymbolIndex = indirectTable[lazyPointersIndirectTableOffset+lpIndex]; | |
2108 | // find matching stub and validate it uses this lazy pointer | |
2109 | for(uint32_t stubIndex=0; stubIndex < stubsCount; ++stubIndex) { | |
2110 | if ( indirectTable[stubsIndirectTableOffset+stubIndex] == lpSymbolIndex ) { | |
2111 | this->updateAlternateLazyPointer(stubsAddr+stubIndex*stubsSize, &lazyPointersAddr[lpIndex], context); | |
2112 | break; | |
2113 | } | |
2114 | } | |
2115 | } | |
2116 | ||
2117 | } | |
2118 | } | |
2119 | cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); | |
412ebb8e | 2120 | } |
9f83892a | 2121 | |
412ebb8e A |
2122 | #endif |
2123 | } | |
2124 | ||
2125 | ||
df9d6cf7 A |
2126 | void ImageLoaderMachOCompressed::registerEncryption(const encryption_info_command* encryptCmd, const LinkContext& context) |
2127 | { | |
bc3b7c8c | 2128 | #if (__arm__ || __arm64__) && !TARGET_OS_SIMULATOR |
df9d6cf7 A |
2129 | if ( encryptCmd == NULL ) |
2130 | return; | |
cf998323 | 2131 | // fMachOData not set up yet, need to manually find mach_header |
df9d6cf7 A |
2132 | const mach_header* mh = NULL; |
2133 | for(unsigned int i=0; i < fSegmentsCount; ++i) { | |
2134 | if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) ) { | |
2135 | mh = (mach_header*)segActualLoadAddress(i); | |
cf998323 A |
2136 | void* start = ((uint8_t*)mh) + encryptCmd->cryptoff; |
2137 | size_t len = encryptCmd->cryptsize; | |
2138 | uint32_t cputype = mh->cputype; | |
2139 | uint32_t cpusubtype = mh->cpusubtype; | |
2140 | uint32_t cryptid = encryptCmd->cryptid; | |
2141 | if (context.verboseMapping) { | |
2142 | dyld::log(" 0x%08lX->0x%08lX configured for FairPlay decryption\n", (long)start, (long)start+len); | |
2143 | } | |
2144 | int result = mremap_encrypted(start, len, cryptid, cputype, cpusubtype); | |
2145 | if ( result != 0 ) { | |
2146 | dyld::throwf("mremap_encrypted() => %d, errno=%d for %s\n", result, errno, this->getPath()); | |
2147 | } | |
2148 | return; | |
df9d6cf7 A |
2149 | } |
2150 | } | |
df9d6cf7 A |
2151 | #endif |
2152 | } | |
2153 | ||
2154 | ||
2155 |