]>
Commit | Line | Data |
---|---|---|
0959b6d4 A |
1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- |
2 | * | |
412ebb8e | 3 | * Copyright (c) 2004-2010 Apple Inc. All rights reserved. |
0959b6d4 A |
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 | #define __STDC_LIMIT_MACROS | |
26 | #include <stdint.h> | |
2fd3f4e8 | 27 | #include <stdlib.h> |
0959b6d4 A |
28 | #include <errno.h> |
29 | #include <fcntl.h> | |
30 | #include <mach/mach.h> | |
31 | #include <mach-o/fat.h> | |
32 | #include <sys/types.h> | |
33 | #include <sys/stat.h> | |
34 | #include <sys/mman.h> | |
0be5d81c A |
35 | #include <sys/param.h> |
36 | #include <sys/mount.h> | |
6cae9b63 | 37 | #include <sys/sysctl.h> |
bac542e6 | 38 | #include <libkern/OSAtomic.h> |
a9a4db61 | 39 | #include <string_view> |
0959b6d4 | 40 | |
cf998323 A |
41 | #include <atomic> |
42 | ||
6cae9b63 A |
43 | #include "Tracing.h" |
44 | ||
0959b6d4 A |
45 | #include "ImageLoader.h" |
46 | ||
47 | ||
bac542e6 | 48 | uint32_t ImageLoader::fgImagesUsedFromSharedCache = 0; |
0959b6d4 | 49 | uint32_t ImageLoader::fgImagesWithUsedPrebinding = 0; |
39a8cd10 A |
50 | uint32_t ImageLoader::fgImagesRequiringCoalescing = 0; |
51 | uint32_t ImageLoader::fgImagesHasWeakDefinitions = 0; | |
0959b6d4 A |
52 | uint32_t ImageLoader::fgTotalRebaseFixups = 0; |
53 | uint32_t ImageLoader::fgTotalBindFixups = 0; | |
bac542e6 A |
54 | uint32_t ImageLoader::fgTotalBindSymbolsResolved = 0; |
55 | uint32_t ImageLoader::fgTotalBindImageSearches = 0; | |
0959b6d4 A |
56 | uint32_t ImageLoader::fgTotalLazyBindFixups = 0; |
57 | uint32_t ImageLoader::fgTotalPossibleLazyBindFixups = 0; | |
bac542e6 A |
58 | uint32_t ImageLoader::fgTotalSegmentsMapped = 0; |
59 | uint64_t ImageLoader::fgTotalBytesMapped = 0; | |
0959b6d4 | 60 | uint64_t ImageLoader::fgTotalLoadLibrariesTime; |
9f83892a A |
61 | uint64_t ImageLoader::fgTotalObjCSetupTime = 0; |
62 | uint64_t ImageLoader::fgTotalDebuggerPausedTime = 0; | |
63 | uint64_t ImageLoader::fgTotalRebindCacheTime = 0; | |
0959b6d4 A |
64 | uint64_t ImageLoader::fgTotalRebaseTime; |
65 | uint64_t ImageLoader::fgTotalBindTime; | |
39a8cd10 A |
66 | uint64_t ImageLoader::fgTotalWeakBindTime; |
67 | uint64_t ImageLoader::fgTotalDOF; | |
0959b6d4 | 68 | uint64_t ImageLoader::fgTotalInitTime; |
bac542e6 | 69 | uint16_t ImageLoader::fgLoadOrdinal = 0; |
9f83892a | 70 | uint32_t ImageLoader::fgSymbolTrieSearchs = 0; |
412ebb8e | 71 | std::vector<ImageLoader::InterposeTuple>ImageLoader::fgInterposingTuples; |
39a8cd10 | 72 | uintptr_t ImageLoader::fgNextPIEDylibAddress = 0; |
0959b6d4 A |
73 | |
74 | ||
39a8cd10 A |
75 | |
76 | ImageLoader::ImageLoader(const char* path, unsigned int libCount) | |
cf998323 | 77 | : fPath(path), fRealPath(NULL), fDevice(0), fInode(0), fLastModified(0), |
2fd3f4e8 | 78 | fPathHash(0), fDlopenReferenceCount(0), fInitializerRecursiveLock(NULL), |
cf998323 A |
79 | fLoadOrder(fgLoadOrdinal++), fDepth(0), fObjCMappedNotified(false), fState(0), fLibraryCount(libCount), |
80 | fMadeReadOnly(false), fAllLibraryChecksumsAndLoadAddressesMatch(false), fLeaveMapped(false), fNeverUnload(false), | |
39a8cd10 | 81 | fHideSymbols(false), fMatchByInstallName(false), |
412ebb8e A |
82 | fInterposed(false), fRegisteredDOF(false), fAllLazyPointersBound(false), |
83 | fBeingRemoved(false), fAddFuncNotified(false), | |
832b6fce | 84 | fPathOwnedByImage(false), fIsReferencedDownward(false), |
2fd3f4e8 | 85 | fWeakSymbolsBound(false) |
0959b6d4 | 86 | { |
bc3b7c8c A |
87 | #if __x86_64__ |
88 | fAotPath = NULL; | |
89 | #endif | |
bac542e6 A |
90 | if ( fPath != NULL ) |
91 | fPathHash = hash(fPath); | |
2fd3f4e8 A |
92 | if ( libCount > 512 ) |
93 | dyld::throwf("too many dependent dylibs in %s", path); | |
0959b6d4 A |
94 | } |
95 | ||
96 | ||
39a8cd10 | 97 | void ImageLoader::deleteImage(ImageLoader* image) |
0959b6d4 | 98 | { |
39a8cd10 | 99 | delete image; |
0959b6d4 A |
100 | } |
101 | ||
102 | ||
103 | ImageLoader::~ImageLoader() | |
104 | { | |
832b6fce A |
105 | if ( fRealPath != NULL ) |
106 | delete [] fRealPath; | |
bac542e6 | 107 | if ( fPathOwnedByImage && (fPath != NULL) ) |
0959b6d4 | 108 | delete [] fPath; |
bc3b7c8c A |
109 | #if __x86_64__ |
110 | if ( fAotPath != NULL ) | |
111 | delete [] fAotPath; | |
112 | #endif | |
bac542e6 A |
113 | } |
114 | ||
39a8cd10 A |
115 | void ImageLoader::setFileInfo(dev_t device, ino_t inode, time_t modDate) |
116 | { | |
117 | fDevice = device; | |
118 | fInode = inode; | |
119 | fLastModified = modDate; | |
120 | } | |
121 | ||
bac542e6 A |
122 | void ImageLoader::setMapped(const LinkContext& context) |
123 | { | |
124 | fState = dyld_image_state_mapped; | |
9f83892a | 125 | context.notifySingle(dyld_image_state_mapped, this, NULL); // note: can throw exception |
bac542e6 A |
126 | } |
127 | ||
bac542e6 A |
128 | int ImageLoader::compare(const ImageLoader* right) const |
129 | { | |
130 | if ( this->fDepth == right->fDepth ) { | |
131 | if ( this->fLoadOrder == right->fLoadOrder ) | |
132 | return 0; | |
133 | else if ( this->fLoadOrder < right->fLoadOrder ) | |
134 | return -1; | |
135 | else | |
136 | return 1; | |
137 | } | |
138 | else { | |
139 | if ( this->fDepth < right->fDepth ) | |
140 | return -1; | |
141 | else | |
142 | return 1; | |
143 | } | |
0959b6d4 A |
144 | } |
145 | ||
0959b6d4 A |
146 | void ImageLoader::setPath(const char* path) |
147 | { | |
cf998323 | 148 | if ( fPathOwnedByImage && (fPath != NULL) ) |
0959b6d4 | 149 | delete [] fPath; |
0959b6d4 A |
150 | fPath = new char[strlen(path)+1]; |
151 | strcpy((char*)fPath, path); | |
bac542e6 A |
152 | fPathOwnedByImage = true; // delete fPath when this image is destructed |
153 | fPathHash = hash(fPath); | |
cf998323 A |
154 | if ( fRealPath != NULL ) { |
155 | delete [] fRealPath; | |
156 | fRealPath = NULL; | |
157 | } | |
bac542e6 A |
158 | } |
159 | ||
160 | void ImageLoader::setPathUnowned(const char* path) | |
161 | { | |
162 | if ( fPathOwnedByImage && (fPath != NULL) ) { | |
163 | delete [] fPath; | |
164 | } | |
165 | fPath = path; | |
cf998323 | 166 | fPathOwnedByImage = false; |
0959b6d4 A |
167 | fPathHash = hash(fPath); |
168 | } | |
169 | ||
832b6fce A |
170 | void ImageLoader::setPaths(const char* path, const char* realPath) |
171 | { | |
172 | this->setPath(path); | |
173 | fRealPath = new char[strlen(realPath)+1]; | |
174 | strcpy((char*)fRealPath, realPath); | |
175 | } | |
176 | ||
cf998323 A |
177 | |
178 | const char* ImageLoader::getRealPath() const | |
179 | { | |
180 | if ( fRealPath != NULL ) | |
832b6fce A |
181 | return fRealPath; |
182 | else | |
cf998323 | 183 | return fPath; |
832b6fce A |
184 | } |
185 | ||
0959b6d4 A |
186 | uint32_t ImageLoader::hash(const char* path) |
187 | { | |
188 | // this does not need to be a great hash | |
189 | // it is just used to reduce the number of strcmp() calls | |
190 | // of existing images when loading a new image | |
191 | uint32_t h = 0; | |
192 | for (const char* s=path; *s != '\0'; ++s) | |
193 | h = h*5 + *s; | |
194 | return h; | |
195 | } | |
196 | ||
197 | bool ImageLoader::matchInstallPath() const | |
198 | { | |
199 | return fMatchByInstallName; | |
200 | } | |
201 | ||
202 | void ImageLoader::setMatchInstallPath(bool match) | |
203 | { | |
204 | fMatchByInstallName = match; | |
205 | } | |
206 | ||
207 | bool ImageLoader::statMatch(const struct stat& stat_buf) const | |
208 | { | |
209 | return ( (this->fDevice == stat_buf.st_dev) && (this->fInode == stat_buf.st_ino) ); | |
210 | } | |
211 | ||
9f83892a | 212 | const char* ImageLoader::shortName(const char* fullName) |
0959b6d4 A |
213 | { |
214 | // try to return leaf name | |
9f83892a A |
215 | if ( fullName != NULL ) { |
216 | const char* s = strrchr(fullName, '/'); | |
0959b6d4 A |
217 | if ( s != NULL ) |
218 | return &s[1]; | |
219 | } | |
9f83892a A |
220 | return fullName; |
221 | } | |
222 | ||
223 | const char* ImageLoader::getShortName() const | |
224 | { | |
225 | return shortName(fPath); | |
0959b6d4 A |
226 | } |
227 | ||
0959b6d4 A |
228 | void ImageLoader::setLeaveMapped() |
229 | { | |
230 | fLeaveMapped = true; | |
0959b6d4 A |
231 | } |
232 | ||
233 | void ImageLoader::setHideExports(bool hide) | |
234 | { | |
235 | fHideSymbols = hide; | |
236 | } | |
237 | ||
238 | bool ImageLoader::hasHiddenExports() const | |
239 | { | |
240 | return fHideSymbols; | |
241 | } | |
242 | ||
243 | bool ImageLoader::isLinked() const | |
244 | { | |
bac542e6 | 245 | return (fState >= dyld_image_state_bound); |
0959b6d4 A |
246 | } |
247 | ||
bac542e6 | 248 | time_t ImageLoader::lastModified() const |
0959b6d4 A |
249 | { |
250 | return fLastModified; | |
251 | } | |
252 | ||
253 | bool ImageLoader::containsAddress(const void* addr) const | |
254 | { | |
39a8cd10 A |
255 | for(unsigned int i=0, e=segmentCount(); i < e; ++i) { |
256 | const uint8_t* start = (const uint8_t*)segActualLoadAddress(i); | |
257 | const uint8_t* end = (const uint8_t*)segActualEndAddress(i); | |
258 | if ( (start <= addr) && (addr < end) && !segUnaccessible(i) ) | |
0959b6d4 A |
259 | return true; |
260 | } | |
261 | return false; | |
262 | } | |
263 | ||
bac542e6 A |
264 | bool ImageLoader::overlapsWithAddressRange(const void* start, const void* end) const |
265 | { | |
39a8cd10 A |
266 | for(unsigned int i=0, e=segmentCount(); i < e; ++i) { |
267 | const uint8_t* segStart = (const uint8_t*)segActualLoadAddress(i); | |
268 | const uint8_t* segEnd = (const uint8_t*)segActualEndAddress(i); | |
412ebb8e A |
269 | if ( strcmp(segName(i), "__UNIXSTACK") == 0 ) { |
270 | // __UNIXSTACK never slides. This is the only place that cares | |
271 | // and checking for that segment name in segActualLoadAddress() | |
272 | // is too expensive. | |
273 | segStart -= getSlide(); | |
274 | segEnd -= getSlide(); | |
275 | } | |
bac542e6 A |
276 | if ( (start <= segStart) && (segStart < end) ) |
277 | return true; | |
278 | if ( (start <= segEnd) && (segEnd < end) ) | |
279 | return true; | |
280 | if ( (segStart < start) && (end < segEnd) ) | |
281 | return true; | |
282 | } | |
283 | return false; | |
284 | } | |
285 | ||
286 | void ImageLoader::getMappedRegions(MappedRegion*& regions) const | |
0959b6d4 | 287 | { |
39a8cd10 | 288 | for(unsigned int i=0, e=segmentCount(); i < e; ++i) { |
0959b6d4 | 289 | MappedRegion region; |
39a8cd10 A |
290 | region.address = segActualLoadAddress(i); |
291 | region.size = segSize(i); | |
bac542e6 | 292 | *regions++ = region; |
0959b6d4 A |
293 | } |
294 | } | |
295 | ||
296 | ||
2fd3f4e8 A |
297 | |
298 | bool ImageLoader::dependsOn(ImageLoader* image) { | |
299 | for(unsigned int i=0; i < libraryCount(); ++i) { | |
300 | if ( libImage(i) == image ) | |
301 | return true; | |
302 | } | |
303 | return false; | |
304 | } | |
305 | ||
306 | ||
bac542e6 | 307 | static bool notInImgageList(const ImageLoader* image, const ImageLoader** dsiStart, const ImageLoader** dsiCur) |
0959b6d4 | 308 | { |
bac542e6 A |
309 | for (const ImageLoader** p = dsiStart; p < dsiCur; ++p) |
310 | if ( *p == image ) | |
311 | return false; | |
312 | return true; | |
0959b6d4 A |
313 | } |
314 | ||
9f83892a A |
315 | bool ImageLoader::findExportedSymbolAddress(const LinkContext& context, const char* symbolName, |
316 | const ImageLoader* requestorImage, int requestorOrdinalOfDef, | |
317 | bool runResolver, const ImageLoader** foundIn, uintptr_t* address) const | |
318 | { | |
319 | const Symbol* sym = this->findExportedSymbol(symbolName, true, foundIn); | |
320 | if ( sym != NULL ) { | |
321 | *address = (*foundIn)->getExportedSymbolAddress(sym, context, requestorImage, runResolver); | |
322 | return true; | |
323 | } | |
324 | return false; | |
325 | } | |
326 | ||
0959b6d4 | 327 | |
0be5d81c | 328 | // private method that handles circular dependencies by only search any image once |
bac542e6 A |
329 | const ImageLoader::Symbol* ImageLoader::findExportedSymbolInDependentImagesExcept(const char* name, |
330 | const ImageLoader** dsiStart, const ImageLoader**& dsiCur, const ImageLoader** dsiEnd, const ImageLoader** foundIn) const | |
0959b6d4 A |
331 | { |
332 | const ImageLoader::Symbol* sym; | |
333 | // search self | |
bac542e6 | 334 | if ( notInImgageList(this, dsiStart, dsiCur) ) { |
9f83892a | 335 | sym = this->findExportedSymbol(name, false, this->getPath(), foundIn); |
0959b6d4 A |
336 | if ( sym != NULL ) |
337 | return sym; | |
bac542e6 | 338 | *dsiCur++ = this; |
0959b6d4 A |
339 | } |
340 | ||
341 | // search directly dependent libraries | |
39a8cd10 A |
342 | for(unsigned int i=0; i < libraryCount(); ++i) { |
343 | ImageLoader* dependentImage = libImage(i); | |
bac542e6 | 344 | if ( (dependentImage != NULL) && notInImgageList(dependentImage, dsiStart, dsiCur) ) { |
9f83892a | 345 | sym = dependentImage->findExportedSymbol(name, false, libPath(i), foundIn); |
0959b6d4 A |
346 | if ( sym != NULL ) |
347 | return sym; | |
348 | } | |
349 | } | |
350 | ||
351 | // search indirectly dependent libraries | |
39a8cd10 A |
352 | for(unsigned int i=0; i < libraryCount(); ++i) { |
353 | ImageLoader* dependentImage = libImage(i); | |
bac542e6 A |
354 | if ( (dependentImage != NULL) && notInImgageList(dependentImage, dsiStart, dsiCur) ) { |
355 | *dsiCur++ = dependentImage; | |
9f83892a | 356 | sym = dependentImage->findExportedSymbolInDependentImagesExcept(name, dsiStart, dsiCur, dsiEnd, foundIn); |
0959b6d4 A |
357 | if ( sym != NULL ) |
358 | return sym; | |
359 | } | |
360 | } | |
361 | ||
362 | return NULL; | |
363 | } | |
364 | ||
365 | ||
bac542e6 | 366 | const ImageLoader::Symbol* ImageLoader::findExportedSymbolInDependentImages(const char* name, const LinkContext& context, const ImageLoader** foundIn) const |
0be5d81c | 367 | { |
9f83892a | 368 | unsigned int imageCount = context.imageCount()+2; |
bac542e6 A |
369 | const ImageLoader* dontSearchImages[imageCount]; |
370 | dontSearchImages[0] = this; // don't search this image | |
371 | const ImageLoader** cur = &dontSearchImages[1]; | |
372 | return this->findExportedSymbolInDependentImagesExcept(name, &dontSearchImages[0], cur, &dontSearchImages[imageCount], foundIn); | |
0be5d81c A |
373 | } |
374 | ||
bac542e6 | 375 | const ImageLoader::Symbol* ImageLoader::findExportedSymbolInImageOrDependentImages(const char* name, const LinkContext& context, const ImageLoader** foundIn) const |
0be5d81c | 376 | { |
9f83892a | 377 | unsigned int imageCount = context.imageCount()+2; |
bac542e6 A |
378 | const ImageLoader* dontSearchImages[imageCount]; |
379 | const ImageLoader** cur = &dontSearchImages[0]; | |
380 | return this->findExportedSymbolInDependentImagesExcept(name, &dontSearchImages[0], cur, &dontSearchImages[imageCount], foundIn); | |
0be5d81c A |
381 | } |
382 | ||
412ebb8e A |
383 | // this is called by initializeMainExecutable() to interpose on the initial set of images |
384 | void ImageLoader::applyInterposing(const LinkContext& context) | |
385 | { | |
6cae9b63 | 386 | dyld3::ScopedTimer timer(DBG_DYLD_TIMING_APPLY_INTERPOSING, 0, 0, 0); |
412ebb8e A |
387 | if ( fgInterposingTuples.size() != 0 ) |
388 | this->recursiveApplyInterposing(context); | |
389 | } | |
0959b6d4 | 390 | |
19894a12 A |
391 | |
392 | uintptr_t ImageLoader::interposedAddress(const LinkContext& context, uintptr_t address, const ImageLoader* inImage, const ImageLoader* onlyInImage) | |
393 | { | |
394 | //dyld::log("interposedAddress(0x%08llX), tupleCount=%lu\n", (uint64_t)address, fgInterposingTuples.size()); | |
395 | for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { | |
396 | //dyld::log(" interposedAddress: replacee=0x%08llX, replacement=0x%08llX, neverImage=%p, onlyImage=%p, inImage=%p\n", | |
397 | // (uint64_t)it->replacee, (uint64_t)it->replacement, it->neverImage, it->onlyImage, inImage); | |
398 | // replace all references to 'replacee' with 'replacement' | |
797cc951 | 399 | if ( (address == it->replacee) && (it->neverImage != inImage) && ((it->onlyImage == NULL) || (it->onlyImage == inImage)) ) { |
19894a12 A |
400 | if ( context.verboseInterposing ) { |
401 | dyld::log("dyld interposing: replace 0x%lX with 0x%lX\n", it->replacee, it->replacement); | |
402 | } | |
403 | return it->replacement; | |
404 | } | |
405 | } | |
406 | return address; | |
407 | } | |
408 | ||
6cae9b63 | 409 | void ImageLoader::applyInterposingToDyldCache(const LinkContext& context) { |
6cae9b63 A |
410 | if (!context.dyldCache) |
411 | return; | |
cf998323 A |
412 | if (!context.dyldCache->header.builtFromChainedFixups) |
413 | return; | |
6cae9b63 A |
414 | if (fgInterposingTuples.empty()) |
415 | return; | |
797cc951 A |
416 | |
417 | // make the cache writable for this block | |
418 | DyldSharedCache::DataConstScopedWriter patcher(context.dyldCache, mach_task_self(), (context.verboseMapping ? &dyld::log : nullptr)); | |
419 | ||
6cae9b63 A |
420 | // For each of the interposed addresses, see if any of them are in the shared cache. If so, find |
421 | // that image and apply its patch table to all uses. | |
422 | uintptr_t cacheStart = (uintptr_t)context.dyldCache; | |
423 | for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { | |
424 | if ( context.verboseInterposing ) | |
425 | dyld::log("dyld: interpose: Trying to interpose address 0x%08llx\n", (uint64_t)it->replacee); | |
426 | uint32_t imageIndex; | |
427 | uint32_t cacheOffsetOfReplacee = (uint32_t)(it->replacee - cacheStart); | |
428 | if (!context.dyldCache->addressInText(cacheOffsetOfReplacee, &imageIndex)) | |
429 | continue; | |
430 | dyld3::closure::ImageNum imageInCache = imageIndex+1; | |
431 | if ( context.verboseInterposing ) | |
432 | dyld::log("dyld: interpose: Found shared cache image %d for 0x%08llx\n", imageInCache, (uint64_t)it->replacee); | |
cf998323 | 433 | context.dyldCache->forEachPatchableExport(imageIndex, ^(uint32_t cacheOffsetOfImpl, const char* exportName) { |
6cae9b63 A |
434 | // Skip patching anything other than this symbol |
435 | if (cacheOffsetOfImpl != cacheOffsetOfReplacee) | |
436 | return; | |
cf998323 A |
437 | if ( context.verboseInterposing ) { |
438 | const dyld3::closure::Image* image = context.dyldCache->cachedDylibsImageArray()->imageForNum(imageInCache); | |
6cae9b63 | 439 | dyld::log("dyld: interpose: Patching uses of symbol %s in shared cache binary at %s\n", exportName, image->path()); |
cf998323 | 440 | } |
6cae9b63 | 441 | uintptr_t newLoc = it->replacement; |
cf998323 | 442 | context.dyldCache->forEachPatchableUseOfExport(imageIndex, cacheOffsetOfImpl, ^(dyld_cache_patchable_location patchLocation) { |
6cae9b63 A |
443 | uintptr_t* loc = (uintptr_t*)(cacheStart+patchLocation.cacheOffset); |
444 | #if __has_feature(ptrauth_calls) | |
445 | if ( patchLocation.authenticated ) { | |
cf998323 A |
446 | dyld3::MachOLoaded::ChainedFixupPointerOnDisk ptr = *(dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)loc; |
447 | ptr.arm64e.authRebase.auth = true; | |
448 | ptr.arm64e.authRebase.addrDiv = patchLocation.usesAddressDiversity; | |
449 | ptr.arm64e.authRebase.diversity = patchLocation.discriminator; | |
450 | ptr.arm64e.authRebase.key = patchLocation.key; | |
451 | *loc = ptr.arm64e.signPointer(loc, newLoc + DyldSharedCache::getAddend(patchLocation)); | |
6cae9b63 A |
452 | if ( context.verboseInterposing ) |
453 | dyld::log("dyld: interpose: *%p = %p (JOP: diversity 0x%04X, addr-div=%d, key=%s)\n", | |
cf998323 | 454 | loc, (void*)*loc, patchLocation.discriminator, patchLocation.usesAddressDiversity, DyldSharedCache::keyName(patchLocation)); |
6cae9b63 A |
455 | return; |
456 | } | |
457 | #endif | |
458 | if ( context.verboseInterposing ) | |
cf998323 A |
459 | dyld::log("dyld: interpose: *%p = 0x%0llX (dyld cache patch) to %s\n", loc, newLoc + DyldSharedCache::getAddend(patchLocation), exportName); |
460 | *loc = newLoc + (uintptr_t)DyldSharedCache::getAddend(patchLocation); | |
6cae9b63 A |
461 | }); |
462 | }); | |
463 | } | |
6cae9b63 A |
464 | } |
465 | ||
19894a12 A |
466 | void ImageLoader::addDynamicInterposingTuples(const struct dyld_interpose_tuple array[], size_t count) |
467 | { | |
468 | for(size_t i=0; i < count; ++i) { | |
469 | ImageLoader::InterposeTuple tuple; | |
470 | tuple.replacement = (uintptr_t)array[i].replacement; | |
471 | tuple.neverImage = NULL; | |
472 | tuple.onlyImage = this; | |
473 | tuple.replacee = (uintptr_t)array[i].replacee; | |
474 | // chain to any existing interpositions | |
475 | for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { | |
476 | if ( (it->replacee == tuple.replacee) && (it->onlyImage == this) ) { | |
477 | tuple.replacee = it->replacement; | |
478 | } | |
479 | } | |
480 | ImageLoader::fgInterposingTuples.push_back(tuple); | |
481 | } | |
482 | } | |
483 | ||
6cae9b63 A |
484 | // <rdar://problem/29099600> dyld should tell the kernel when it is doing root fix-ups |
485 | void ImageLoader::vmAccountingSetSuspended(const LinkContext& context, bool suspend) | |
486 | { | |
bc3b7c8c | 487 | #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR |
6cae9b63 A |
488 | static bool sVmAccountingSuspended = false; |
489 | if ( suspend == sVmAccountingSuspended ) | |
490 | return; | |
491 | if ( context.verboseBind ) | |
492 | dyld::log("set vm.footprint_suspend=%d\n", suspend); | |
493 | int newValue = suspend ? 1 : 0; | |
494 | int oldValue = 0; | |
495 | size_t newlen = sizeof(newValue); | |
496 | size_t oldlen = sizeof(oldValue); | |
497 | int ret = sysctlbyname("vm.footprint_suspend", &oldValue, &oldlen, &newValue, newlen); | |
498 | if ( context.verboseBind && (ret != 0) ) | |
499 | dyld::log("vm.footprint_suspend => %d, errno=%d\n", ret, errno); | |
500 | sVmAccountingSuspended = suspend; | |
501 | #endif | |
502 | } | |
503 | ||
19894a12 | 504 | |
9f83892a | 505 | void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool preflightOnly, bool neverUnload, const RPathChain& loaderRPaths, const char* imagePath) |
0959b6d4 | 506 | { |
9f83892a | 507 | //dyld::log("ImageLoader::link(%s) refCount=%d, neverUnload=%d\n", imagePath, fDlopenReferenceCount, fNeverUnload); |
bac542e6 | 508 | |
412ebb8e | 509 | // clear error strings |
9f83892a | 510 | (*context.setErrorStrings)(0, NULL, NULL, NULL); |
412ebb8e | 511 | |
bac542e6 | 512 | uint64_t t0 = mach_absolute_time(); |
9f83892a A |
513 | this->recursiveLoadLibraries(context, preflightOnly, loaderRPaths, imagePath); |
514 | context.notifyBatch(dyld_image_state_dependents_mapped, preflightOnly); | |
515 | ||
bac542e6 A |
516 | // we only do the loading step for preflights |
517 | if ( preflightOnly ) | |
518 | return; | |
6cae9b63 | 519 | |
bac542e6 A |
520 | uint64_t t1 = mach_absolute_time(); |
521 | context.clearAllDepths(); | |
bc3b7c8c | 522 | this->updateDepth(context.imageCount()); |
bac542e6 | 523 | |
6cae9b63 A |
524 | __block uint64_t t2, t3, t4, t5; |
525 | { | |
526 | dyld3::ScopedTimer(DBG_DYLD_TIMING_APPLY_FIXUPS, 0, 0, 0); | |
527 | t2 = mach_absolute_time(); | |
cf998323 | 528 | this->recursiveRebaseWithAccounting(context); |
6cae9b63 A |
529 | context.notifyBatch(dyld_image_state_rebased, false); |
530 | ||
531 | t3 = mach_absolute_time(); | |
532 | if ( !context.linkingMainExecutable ) | |
533 | this->recursiveBindWithAccounting(context, forceLazysBound, neverUnload); | |
534 | ||
535 | t4 = mach_absolute_time(); | |
536 | if ( !context.linkingMainExecutable ) | |
537 | this->weakBind(context); | |
538 | t5 = mach_absolute_time(); | |
539 | } | |
412ebb8e | 540 | |
412ebb8e A |
541 | // interpose any dynamically loaded images |
542 | if ( !context.linkingMainExecutable && (fgInterposingTuples.size() != 0) ) { | |
6cae9b63 | 543 | dyld3::ScopedTimer timer(DBG_DYLD_TIMING_APPLY_INTERPOSING, 0, 0, 0); |
412ebb8e A |
544 | this->recursiveApplyInterposing(context); |
545 | } | |
6cae9b63 | 546 | |
cf998323 A |
547 | // now that all fixups are done, make __DATA_CONST segments read-only |
548 | if ( !context.linkingMainExecutable ) | |
549 | this->recursiveMakeDataReadOnly(context); | |
550 | ||
551 | if ( !context.linkingMainExecutable ) | |
552 | context.notifyBatch(dyld_image_state_bound, false); | |
553 | uint64_t t6 = mach_absolute_time(); | |
554 | ||
555 | if ( context.registerDOFs != NULL ) { | |
556 | std::vector<DOFInfo> dofs; | |
557 | this->recursiveGetDOFSections(context, dofs); | |
558 | context.registerDOFs(dofs); | |
559 | } | |
560 | uint64_t t7 = mach_absolute_time(); | |
561 | ||
412ebb8e | 562 | // clear error strings |
9f83892a | 563 | (*context.setErrorStrings)(0, NULL, NULL, NULL); |
412ebb8e | 564 | |
bac542e6 | 565 | fgTotalLoadLibrariesTime += t1 - t0; |
0959b6d4 A |
566 | fgTotalRebaseTime += t3 - t2; |
567 | fgTotalBindTime += t4 - t3; | |
39a8cd10 | 568 | fgTotalWeakBindTime += t5 - t4; |
412ebb8e | 569 | fgTotalDOF += t7 - t6; |
bac542e6 A |
570 | |
571 | // done with initial dylib loads | |
39a8cd10 | 572 | fgNextPIEDylibAddress = 0; |
bac542e6 A |
573 | } |
574 | ||
575 | ||
576 | void ImageLoader::printReferenceCounts() | |
577 | { | |
2fd3f4e8 | 578 | dyld::log(" dlopen=%d for %s\n", fDlopenReferenceCount, getPath() ); |
0959b6d4 A |
579 | } |
580 | ||
581 | ||
bac542e6 A |
582 | bool ImageLoader::decrementDlopenReferenceCount() |
583 | { | |
584 | if ( fDlopenReferenceCount == 0 ) | |
585 | return true; | |
586 | --fDlopenReferenceCount; | |
587 | return false; | |
588 | } | |
589 | ||
19894a12 A |
590 | |
591 | // <rdar://problem/14412057> upward dylib initializers can be run too soon | |
592 | // To handle dangling dylibs which are upward linked but not downward, all upward linked dylibs | |
593 | // have their initialization postponed until after the recursion through downward dylibs | |
594 | // has completed. | |
595 | void ImageLoader::processInitializers(const LinkContext& context, mach_port_t thisThread, | |
596 | InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& images) | |
597 | { | |
9f83892a | 598 | uint32_t maxImageCount = context.imageCount()+2; |
19894a12 A |
599 | ImageLoader::UninitedUpwards upsBuffer[maxImageCount]; |
600 | ImageLoader::UninitedUpwards& ups = upsBuffer[0]; | |
601 | ups.count = 0; | |
602 | // Calling recursive init on all images in images list, building a new list of | |
603 | // uninitialized upward dependencies. | |
604 | for (uintptr_t i=0; i < images.count; ++i) { | |
cf998323 | 605 | images.imagesAndPaths[i].first->recursiveInitialization(context, thisThread, images.imagesAndPaths[i].second, timingInfo, ups); |
19894a12 A |
606 | } |
607 | // If any upward dependencies remain, init them. | |
608 | if ( ups.count > 0 ) | |
609 | processInitializers(context, thisThread, timingInfo, ups); | |
610 | } | |
611 | ||
612 | ||
412ebb8e | 613 | void ImageLoader::runInitializers(const LinkContext& context, InitializerTimingList& timingInfo) |
0959b6d4 | 614 | { |
bac542e6 | 615 | uint64_t t1 = mach_absolute_time(); |
19894a12 A |
616 | mach_port_t thisThread = mach_thread_self(); |
617 | ImageLoader::UninitedUpwards up; | |
618 | up.count = 1; | |
cf998323 | 619 | up.imagesAndPaths[0] = { this, this->getPath() }; |
19894a12 | 620 | processInitializers(context, thisThread, timingInfo, up); |
9f83892a | 621 | context.notifyBatch(dyld_image_state_initialized, false); |
19894a12 | 622 | mach_port_deallocate(mach_task_self(), thisThread); |
bac542e6 A |
623 | uint64_t t2 = mach_absolute_time(); |
624 | fgTotalInitTime += (t2 - t1); | |
0959b6d4 A |
625 | } |
626 | ||
bac542e6 A |
627 | |
628 | void ImageLoader::bindAllLazyPointers(const LinkContext& context, bool recursive) | |
0959b6d4 | 629 | { |
bac542e6 A |
630 | if ( ! fAllLazyPointersBound ) { |
631 | fAllLazyPointersBound = true; | |
632 | ||
633 | if ( recursive ) { | |
634 | // bind lower level libraries first | |
39a8cd10 A |
635 | for(unsigned int i=0; i < libraryCount(); ++i) { |
636 | ImageLoader* dependentImage = libImage(i); | |
637 | if ( dependentImage != NULL ) | |
638 | dependentImage->bindAllLazyPointers(context, recursive); | |
bac542e6 A |
639 | } |
640 | } | |
2028a915 | 641 | // bind lazies in this image |
797cc951 A |
642 | DyldSharedCache::DataConstLazyScopedWriter patcher(context.dyldCache, mach_task_self(), context.verboseMapping ? &dyld::log : nullptr); |
643 | this->doBindJustLazies(context, patcher); | |
bac542e6 | 644 | } |
0959b6d4 A |
645 | } |
646 | ||
647 | ||
0959b6d4 A |
648 | bool ImageLoader::allDependentLibrariesAsWhenPreBound() const |
649 | { | |
650 | return fAllLibraryChecksumsAndLoadAddressesMatch; | |
651 | } | |
652 | ||
653 | ||
2fd3f4e8 A |
654 | void ImageLoader::markedUsedRecursive(const std::vector<DynamicReference>& dynamicReferences) |
655 | { | |
656 | // already visited here | |
657 | if ( fMarkedInUse ) | |
658 | return; | |
659 | fMarkedInUse = true; | |
660 | ||
661 | // clear mark on all statically dependent dylibs | |
662 | for(unsigned int i=0; i < libraryCount(); ++i) { | |
663 | ImageLoader* dependentImage = libImage(i); | |
664 | if ( dependentImage != NULL ) { | |
665 | dependentImage->markedUsedRecursive(dynamicReferences); | |
666 | } | |
667 | } | |
668 | ||
669 | // clear mark on all dynamically dependent dylibs | |
670 | for (std::vector<ImageLoader::DynamicReference>::const_iterator it=dynamicReferences.begin(); it != dynamicReferences.end(); ++it) { | |
671 | if ( it->from == this ) | |
672 | it->to->markedUsedRecursive(dynamicReferences); | |
673 | } | |
674 | ||
675 | } | |
676 | ||
bc3b7c8c A |
677 | unsigned int ImageLoader::updateDepth(unsigned int maxDepth) |
678 | { | |
679 | STACK_ALLOC_ARRAY(ImageLoader*, danglingUpwards, maxDepth); | |
680 | unsigned int depth = this->recursiveUpdateDepth(maxDepth, danglingUpwards); | |
681 | for (auto& danglingUpward : danglingUpwards) { | |
682 | if ( danglingUpward->fDepth != 0) | |
683 | continue; | |
684 | danglingUpward->recursiveUpdateDepth(maxDepth, danglingUpwards); | |
685 | } | |
686 | return depth; | |
687 | } | |
688 | ||
689 | unsigned int ImageLoader::recursiveUpdateDepth(unsigned int maxDepth, dyld3::Array<ImageLoader*>& danglingUpwards) | |
bac542e6 A |
690 | { |
691 | // the purpose of this phase is to make the images sortable such that | |
692 | // in a sort list of images, every image that an image depends on | |
693 | // occurs in the list before it. | |
694 | if ( fDepth == 0 ) { | |
695 | // break cycles | |
696 | fDepth = maxDepth; | |
697 | ||
698 | // get depth of dependents | |
699 | unsigned int minDependentDepth = maxDepth; | |
39a8cd10 A |
700 | for(unsigned int i=0; i < libraryCount(); ++i) { |
701 | ImageLoader* dependentImage = libImage(i); | |
bc3b7c8c A |
702 | if ( dependentImage != NULL ) { |
703 | if ( libIsUpward(i) ) { | |
704 | if ( dependentImage->fDepth == 0) { | |
705 | if ( !danglingUpwards.contains(dependentImage) ) | |
706 | danglingUpwards.push_back(dependentImage); | |
707 | } | |
708 | } else { | |
709 | unsigned int d = dependentImage->recursiveUpdateDepth(maxDepth, danglingUpwards); | |
710 | if ( d < minDependentDepth ) | |
711 | minDependentDepth = d; | |
712 | } | |
713 | } | |
714 | // <rdar://problem/60878811> make sure need to re-bind propagates up | |
715 | if ( dependentImage != NULL ) { | |
716 | if ( fAllLibraryChecksumsAndLoadAddressesMatch && !dependentImage->fAllLibraryChecksumsAndLoadAddressesMatch ) { | |
717 | fAllLibraryChecksumsAndLoadAddressesMatch = false; | |
718 | } | |
bac542e6 A |
719 | } |
720 | } | |
bac542e6 A |
721 | // make me less deep then all my dependents |
722 | fDepth = minDependentDepth - 1; | |
bc3b7c8c | 723 | |
bac542e6 | 724 | } |
bac542e6 A |
725 | return fDepth; |
726 | } | |
727 | ||
728 | ||
9f83892a | 729 | void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath) |
0959b6d4 | 730 | { |
bac542e6 | 731 | if ( fState < dyld_image_state_dependents_mapped ) { |
0959b6d4 | 732 | // break cycles |
bac542e6 | 733 | fState = dyld_image_state_dependents_mapped; |
0959b6d4 A |
734 | |
735 | // get list of libraries this image needs | |
39a8cd10 | 736 | DependentLibraryInfo libraryInfos[fLibraryCount]; |
bac542e6 A |
737 | this->doGetDependentLibraries(libraryInfos); |
738 | ||
739 | // get list of rpaths that this image adds | |
740 | std::vector<const char*> rpathsFromThisImage; | |
741 | this->getRPaths(context, rpathsFromThisImage); | |
742 | const RPathChain thisRPaths(&loaderRPaths, &rpathsFromThisImage); | |
0959b6d4 A |
743 | |
744 | // try to load each | |
745 | bool canUsePrelinkingInfo = true; | |
39a8cd10 A |
746 | for(unsigned int i=0; i < fLibraryCount; ++i){ |
747 | ImageLoader* dependentLib; | |
748 | bool depLibReExported = false; | |
bac542e6 | 749 | DependentLibraryInfo& requiredLibInfo = libraryInfos[i]; |
39a8cd10 A |
750 | if ( preflightOnly && context.inSharedCache(requiredLibInfo.name) ) { |
751 | // <rdar://problem/5910137> dlopen_preflight() on image in shared cache leaves it loaded but not objc initialized | |
752 | // in preflight mode, don't even load dylib that are in the shared cache because they will never be unloaded | |
412ebb8e | 753 | setLibImage(i, NULL, false, false); |
39a8cd10 A |
754 | continue; |
755 | } | |
0959b6d4 | 756 | try { |
9f83892a | 757 | unsigned cacheIndex; |
cf998323 | 758 | dependentLib = context.loadLibrary(requiredLibInfo.name, true, this->getPath(), &thisRPaths, cacheIndex); |
39a8cd10 | 759 | if ( dependentLib == this ) { |
0959b6d4 | 760 | // found circular reference, perhaps DYLD_LIBARY_PATH is causing this rdar://problem/3684168 |
cf998323 | 761 | dependentLib = context.loadLibrary(requiredLibInfo.name, false, NULL, NULL, cacheIndex); |
39a8cd10 | 762 | if ( dependentLib != this ) |
bac542e6 | 763 | dyld::warn("DYLD_ setting caused circular dependency in %s\n", this->getPath()); |
0959b6d4 | 764 | } |
bac542e6 | 765 | if ( fNeverUnload ) |
39a8cd10 | 766 | dependentLib->setNeverUnload(); |
832b6fce | 767 | if ( requiredLibInfo.upward ) { |
832b6fce A |
768 | } |
769 | else { | |
832b6fce A |
770 | dependentLib->fIsReferencedDownward = true; |
771 | } | |
9f83892a | 772 | LibraryInfo actualInfo = dependentLib->doGetLibraryInfo(requiredLibInfo.info); |
39a8cd10 A |
773 | depLibReExported = requiredLibInfo.reExported; |
774 | if ( ! depLibReExported ) { | |
775 | // for pre-10.5 binaries that did not use LC_REEXPORT_DYLIB | |
776 | depLibReExported = dependentLib->isSubframeworkOf(context, this) || this->hasSubLibrary(context, dependentLib); | |
bac542e6 | 777 | } |
0959b6d4 | 778 | // check found library version is compatible |
832b6fce | 779 | // <rdar://problem/89200806> 0xFFFFFFFF is wildcard that matches any version |
6cae9b63 A |
780 | if ( (requiredLibInfo.info.minVersion != 0xFFFFFFFF) && (actualInfo.minVersion < requiredLibInfo.info.minVersion) |
781 | && ((dyld3::MachOFile*)(dependentLib->machHeader()))->enforceCompatVersion() ) { | |
412ebb8e | 782 | // record values for possible use by CrashReporter or Finder |
bac542e6 A |
783 | dyld::throwf("Incompatible library version: %s requires version %d.%d.%d or later, but %s provides version %d.%d.%d", |
784 | this->getShortName(), requiredLibInfo.info.minVersion >> 16, (requiredLibInfo.info.minVersion >> 8) & 0xff, requiredLibInfo.info.minVersion & 0xff, | |
39a8cd10 | 785 | dependentLib->getShortName(), actualInfo.minVersion >> 16, (actualInfo.minVersion >> 8) & 0xff, actualInfo.minVersion & 0xff); |
0959b6d4 | 786 | } |
412ebb8e | 787 | // prebinding for this image disabled if any dependent library changed |
9f83892a A |
788 | //if ( !depLibCheckSumsMatch ) |
789 | // canUsePrelinkingInfo = false; | |
412ebb8e A |
790 | // prebinding for this image disabled unless both this and dependent are in the shared cache |
791 | if ( !dependentLib->inSharedCache() || !this->inSharedCache() ) | |
0959b6d4 | 792 | canUsePrelinkingInfo = false; |
412ebb8e | 793 | |
0959b6d4 A |
794 | //if ( context.verbosePrebinding ) { |
795 | // if ( !requiredLib.checksumMatches ) | |
bac542e6 | 796 | // fprintf(stderr, "dyld: checksum mismatch, (%u v %u) for %s referencing %s\n", |
39a8cd10 A |
797 | // requiredLibInfo.info.checksum, actualInfo.checksum, this->getPath(), dependentLib->getPath()); |
798 | // if ( dependentLib->getSlide() != 0 ) | |
799 | // fprintf(stderr, "dyld: dependent library slid for %s referencing %s\n", this->getPath(), dependentLib->getPath()); | |
0959b6d4 A |
800 | //} |
801 | } | |
802 | catch (const char* msg) { | |
803 | //if ( context.verbosePrebinding ) | |
39a8cd10 | 804 | // fprintf(stderr, "dyld: exception during processing for %s referencing %s\n", this->getPath(), dependentLib->getPath()); |
bac542e6 A |
805 | if ( requiredLibInfo.required ) { |
806 | fState = dyld_image_state_mapped; | |
412ebb8e | 807 | // record values for possible use by CrashReporter or Finder |
9f83892a A |
808 | if ( strstr(msg, "Incompatible library version") != NULL ) |
809 | (*context.setErrorStrings)(DYLD_EXIT_REASON_DYLIB_WRONG_VERSION, this->getPath(), requiredLibInfo.name, NULL); | |
412ebb8e | 810 | else if ( strstr(msg, "architecture") != NULL ) |
9f83892a A |
811 | (*context.setErrorStrings)(DYLD_EXIT_REASON_DYLIB_WRONG_ARCH, this->getPath(), requiredLibInfo.name, NULL); |
812 | else if ( strstr(msg, "file system sandbox") != NULL ) | |
813 | (*context.setErrorStrings)(DYLD_EXIT_REASON_FILE_SYSTEM_SANDBOX, this->getPath(), requiredLibInfo.name, NULL); | |
814 | else if ( strstr(msg, "code signature") != NULL ) | |
815 | (*context.setErrorStrings)(DYLD_EXIT_REASON_CODE_SIGNATURE, this->getPath(), requiredLibInfo.name, NULL); | |
816 | else if ( strstr(msg, "malformed") != NULL ) | |
817 | (*context.setErrorStrings)(DYLD_EXIT_REASON_MALFORMED_MACHO, this->getPath(), requiredLibInfo.name, NULL); | |
412ebb8e | 818 | else |
9f83892a | 819 | (*context.setErrorStrings)(DYLD_EXIT_REASON_DYLIB_MISSING, this->getPath(), requiredLibInfo.name, NULL); |
19894a12 A |
820 | const char* newMsg = dyld::mkstringf("Library not loaded: %s\n Referenced from: %s\n Reason: %s", requiredLibInfo.name, this->getRealPath(), msg); |
821 | free((void*)msg); // our free() will do nothing if msg is a string literal | |
822 | throw newMsg; | |
0959b6d4 | 823 | } |
19894a12 | 824 | free((void*)msg); // our free() will do nothing if msg is a string literal |
0959b6d4 | 825 | // ok if weak library not found |
39a8cd10 | 826 | dependentLib = NULL; |
0959b6d4 A |
827 | canUsePrelinkingInfo = false; // this disables all prebinding, we may want to just slam import vectors for this lib to zero |
828 | } | |
412ebb8e | 829 | setLibImage(i, dependentLib, depLibReExported, requiredLibInfo.upward); |
0959b6d4 A |
830 | } |
831 | fAllLibraryChecksumsAndLoadAddressesMatch = canUsePrelinkingInfo; | |
832 | ||
833 | // tell each to load its dependents | |
39a8cd10 A |
834 | for(unsigned int i=0; i < libraryCount(); ++i) { |
835 | ImageLoader* dependentImage = libImage(i); | |
bc3b7c8c | 836 | if ( dependentImage != NULL ) { |
9f83892a | 837 | dependentImage->recursiveLoadLibraries(context, preflightOnly, thisRPaths, libraryInfos[i].name); |
0959b6d4 A |
838 | } |
839 | } | |
0959b6d4 A |
840 | // do deep prebind check |
841 | if ( fAllLibraryChecksumsAndLoadAddressesMatch ) { | |
39a8cd10 A |
842 | for(unsigned int i=0; i < libraryCount(); ++i){ |
843 | ImageLoader* dependentImage = libImage(i); | |
bc3b7c8c | 844 | if ( dependentImage != NULL ) { |
39a8cd10 | 845 | if ( !dependentImage->allDependentLibrariesAsWhenPreBound() ) |
0959b6d4 A |
846 | fAllLibraryChecksumsAndLoadAddressesMatch = false; |
847 | } | |
848 | } | |
849 | } | |
bc3b7c8c | 850 | |
bac542e6 A |
851 | // free rpaths (getRPaths() malloc'ed each string) |
852 | for(std::vector<const char*>::iterator it=rpathsFromThisImage.begin(); it != rpathsFromThisImage.end(); ++it) { | |
853 | const char* str = *it; | |
854 | free((void*)str); | |
855 | } | |
856 | ||
0959b6d4 A |
857 | } |
858 | } | |
859 | ||
cf998323 A |
860 | |
861 | void ImageLoader::recursiveRebaseWithAccounting(const LinkContext& context) | |
862 | { | |
863 | this->recursiveRebase(context); | |
864 | vmAccountingSetSuspended(context, false); | |
865 | } | |
866 | ||
0959b6d4 A |
867 | void ImageLoader::recursiveRebase(const LinkContext& context) |
868 | { | |
bac542e6 | 869 | if ( fState < dyld_image_state_rebased ) { |
0959b6d4 | 870 | // break cycles |
bac542e6 | 871 | fState = dyld_image_state_rebased; |
0959b6d4 A |
872 | |
873 | try { | |
874 | // rebase lower level libraries first | |
39a8cd10 A |
875 | for(unsigned int i=0; i < libraryCount(); ++i) { |
876 | ImageLoader* dependentImage = libImage(i); | |
877 | if ( dependentImage != NULL ) | |
878 | dependentImage->recursiveRebase(context); | |
0959b6d4 A |
879 | } |
880 | ||
881 | // rebase this image | |
882 | doRebase(context); | |
bac542e6 A |
883 | |
884 | // notify | |
9f83892a | 885 | context.notifySingle(dyld_image_state_rebased, this, NULL); |
0959b6d4 A |
886 | } |
887 | catch (const char* msg) { | |
888 | // this image is not rebased | |
bac542e6 | 889 | fState = dyld_image_state_dependents_mapped; |
412ebb8e | 890 | CRSetCrashLogMessage2(NULL); |
bac542e6 | 891 | throw; |
0959b6d4 A |
892 | } |
893 | } | |
894 | } | |
895 | ||
412ebb8e A |
896 | void ImageLoader::recursiveApplyInterposing(const LinkContext& context) |
897 | { | |
898 | if ( ! fInterposed ) { | |
899 | // break cycles | |
900 | fInterposed = true; | |
901 | ||
902 | try { | |
903 | // interpose lower level libraries first | |
904 | for(unsigned int i=0; i < libraryCount(); ++i) { | |
905 | ImageLoader* dependentImage = libImage(i); | |
906 | if ( dependentImage != NULL ) | |
907 | dependentImage->recursiveApplyInterposing(context); | |
908 | } | |
909 | ||
910 | // interpose this image | |
911 | doInterpose(context); | |
912 | } | |
913 | catch (const char* msg) { | |
914 | // this image is not interposed | |
915 | fInterposed = false; | |
916 | throw; | |
917 | } | |
918 | } | |
919 | } | |
0959b6d4 | 920 | |
cf998323 A |
921 | void ImageLoader::recursiveMakeDataReadOnly(const LinkContext& context) |
922 | { | |
923 | if ( ! fMadeReadOnly ) { | |
924 | // break cycles | |
925 | fMadeReadOnly = true; | |
926 | ||
927 | try { | |
928 | // handle lower level libraries first | |
929 | for(unsigned int i=0; i < libraryCount(); ++i) { | |
930 | ImageLoader* dependentImage = libImage(i); | |
931 | if ( dependentImage != NULL ) | |
932 | dependentImage->recursiveMakeDataReadOnly(context); | |
933 | } | |
934 | ||
935 | // if this image has __DATA_CONST, make that segment read-only | |
936 | makeDataReadOnly(); | |
937 | } | |
938 | catch (const char* msg) { | |
939 | fMadeReadOnly = false; | |
940 | throw; | |
941 | } | |
942 | } | |
943 | } | |
944 | ||
945 | ||
6cae9b63 A |
946 | void ImageLoader::recursiveBindWithAccounting(const LinkContext& context, bool forceLazysBound, bool neverUnload) |
947 | { | |
bc3b7c8c | 948 | this->recursiveBind(context, forceLazysBound, neverUnload, nullptr); |
6cae9b63 A |
949 | vmAccountingSetSuspended(context, false); |
950 | } | |
0959b6d4 | 951 | |
bc3b7c8c | 952 | void ImageLoader::recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload, const ImageLoader* parent) |
0959b6d4 | 953 | { |
bac542e6 A |
954 | // Normally just non-lazy pointers are bound immediately. |
955 | // The exceptions are: | |
956 | // 1) DYLD_BIND_AT_LAUNCH will cause lazy pointers to be bound immediately | |
957 | // 2) some API's (e.g. RTLD_NOW) can cause lazy pointers to be bound immediately | |
958 | if ( fState < dyld_image_state_bound ) { | |
0959b6d4 | 959 | // break cycles |
bac542e6 A |
960 | fState = dyld_image_state_bound; |
961 | ||
0959b6d4 A |
962 | try { |
963 | // bind lower level libraries first | |
39a8cd10 A |
964 | for(unsigned int i=0; i < libraryCount(); ++i) { |
965 | ImageLoader* dependentImage = libImage(i); | |
bc3b7c8c A |
966 | if ( dependentImage != NULL ) { |
967 | const ImageLoader* reExportParent = nullptr; | |
968 | if ( libReExported(i) ) | |
969 | reExportParent = this; | |
970 | dependentImage->recursiveBind(context, forceLazysBound, neverUnload, reExportParent); | |
971 | } | |
0959b6d4 A |
972 | } |
973 | // bind this image | |
bc3b7c8c | 974 | this->doBind(context, forceLazysBound, parent); |
bac542e6 A |
975 | // mark if lazys are also bound |
976 | if ( forceLazysBound || this->usablePrebinding(context) ) | |
977 | fAllLazyPointersBound = true; | |
2fd3f4e8 A |
978 | // mark as never-unload if requested |
979 | if ( neverUnload ) | |
980 | this->setNeverUnload(); | |
bac542e6 | 981 | |
9f83892a | 982 | context.notifySingle(dyld_image_state_bound, this, NULL); |
0959b6d4 A |
983 | } |
984 | catch (const char* msg) { | |
985 | // restore state | |
bac542e6 | 986 | fState = dyld_image_state_rebased; |
412ebb8e | 987 | CRSetCrashLogMessage2(NULL); |
bac542e6 | 988 | throw; |
0959b6d4 A |
989 | } |
990 | } | |
991 | } | |
992 | ||
6cae9b63 | 993 | |
cf998323 | 994 | |
6cae9b63 A |
995 | // These are mangled symbols for all the variants of operator new and delete |
996 | // which a main executable can define (non-weak) and override the | |
997 | // weak-def implementation in the OS. | |
cf998323 | 998 | static const char* const sTreatAsWeak[] = { |
6cae9b63 A |
999 | "__Znwm", "__ZnwmRKSt9nothrow_t", |
1000 | "__Znam", "__ZnamRKSt9nothrow_t", | |
1001 | "__ZdlPv", "__ZdlPvRKSt9nothrow_t", "__ZdlPvm", | |
1002 | "__ZdaPv", "__ZdaPvRKSt9nothrow_t", "__ZdaPvm", | |
1003 | "__ZnwmSt11align_val_t", "__ZnwmSt11align_val_tRKSt9nothrow_t", | |
1004 | "__ZnamSt11align_val_t", "__ZnamSt11align_val_tRKSt9nothrow_t", | |
1005 | "__ZdlPvSt11align_val_t", "__ZdlPvSt11align_val_tRKSt9nothrow_t", "__ZdlPvmSt11align_val_t", | |
1006 | "__ZdaPvSt11align_val_t", "__ZdaPvSt11align_val_tRKSt9nothrow_t", "__ZdaPvmSt11align_val_t" | |
1007 | }; | |
1008 | ||
a9a4db61 A |
1009 | size_t ImageLoader::HashCString::hash(const char* v) { |
1010 | // FIXME: Use hash<string_view> when it has the correct visibility markup | |
1011 | return std::hash<std::string_view>{}(v); | |
1012 | } | |
1013 | ||
1014 | bool ImageLoader::EqualCString::equal(const char* s1, const char* s2) { | |
1015 | return strcmp(s1, s2) == 0; | |
1016 | } | |
6cae9b63 | 1017 | |
39a8cd10 | 1018 | void ImageLoader::weakBind(const LinkContext& context) |
0959b6d4 | 1019 | { |
a9a4db61 A |
1020 | |
1021 | if (!context.useNewWeakBind) { | |
1022 | weakBindOld(context); | |
1023 | return; | |
1024 | } | |
1025 | ||
39a8cd10 A |
1026 | if ( context.verboseWeakBind ) |
1027 | dyld::log("dyld: weak bind start:\n"); | |
2fd3f4e8 | 1028 | uint64_t t1 = mach_absolute_time(); |
a9a4db61 | 1029 | |
39a8cd10 A |
1030 | // get set of ImageLoaders that participate in coalecsing |
1031 | ImageLoader* imagesNeedingCoalescing[fgImagesRequiringCoalescing]; | |
9f83892a A |
1032 | unsigned imageIndexes[fgImagesRequiringCoalescing]; |
1033 | int count = context.getCoalescedImages(imagesNeedingCoalescing, imageIndexes); | |
39a8cd10 A |
1034 | |
1035 | // count how many have not already had weakbinding done | |
1036 | int countNotYetWeakBound = 0; | |
412ebb8e | 1037 | int countOfImagesWithWeakDefinitionsNotInSharedCache = 0; |
39a8cd10 | 1038 | for(int i=0; i < count; ++i) { |
9f83892a | 1039 | if ( ! imagesNeedingCoalescing[i]->weakSymbolsBound(imageIndexes[i]) ) |
39a8cd10 | 1040 | ++countNotYetWeakBound; |
9f83892a A |
1041 | if ( ! imagesNeedingCoalescing[i]->inSharedCache() ) |
1042 | ++countOfImagesWithWeakDefinitionsNotInSharedCache; | |
39a8cd10 A |
1043 | } |
1044 | ||
a9a4db61 A |
1045 | // don't need to do any coalescing if only one image has overrides, or all have already been done |
1046 | if ( (countOfImagesWithWeakDefinitionsNotInSharedCache > 0) && (countNotYetWeakBound > 0) ) { | |
1047 | if (!context.weakDefMapInitialized) { | |
1048 | // Initialize the weak def map as the link context doesn't run static initializers | |
1049 | new (&context.weakDefMap) dyld3::Map<const char*, std::pair<const ImageLoader*, uintptr_t>, ImageLoader::HashCString, ImageLoader::EqualCString>(); | |
1050 | context.weakDefMapInitialized = true; | |
1051 | } | |
797cc951 A |
1052 | |
1053 | // We might have to patch the shared cache __DATA_CONST. In that case, we'll create just a single | |
1054 | // patcher when needed. | |
1055 | DyldSharedCache::DataConstLazyScopedWriter patcher(context.dyldCache, mach_task_self(), context.verboseMapping ? &dyld::log : nullptr); | |
1056 | ||
bc3b7c8c | 1057 | #if TARGET_OS_OSX |
a9a4db61 A |
1058 | // only do alternate algorithm for dlopen(). Use traditional algorithm for launch |
1059 | if ( !context.linkingMainExecutable ) { | |
1060 | // Don't take the memory hit of weak defs on the launch path until we hit a dlopen with more weak symbols to bind | |
1061 | if (!context.weakDefMapProcessedLaunchDefs) { | |
1062 | context.weakDefMapProcessedLaunchDefs = true; | |
1063 | ||
1064 | // Walk the nlist for all binaries from launch and fill in the map with any other weak defs | |
1065 | for (int i=0; i < count; ++i) { | |
1066 | const ImageLoader* image = imagesNeedingCoalescing[i]; | |
1067 | // skip images without defs. We've processed launch time refs already | |
1068 | if ( !image->hasCoalescedExports() ) | |
1069 | continue; | |
1070 | // Only process binaries which have had their weak symbols bound, ie, not the new ones we are processing now | |
1071 | // from this dlopen | |
1072 | if ( !image->weakSymbolsBound(imageIndexes[i]) ) | |
1073 | continue; | |
1074 | ||
1075 | Diagnostics diag; | |
1076 | const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)image->machHeader(); | |
bc3b7c8c A |
1077 | ma->forEachWeakDef(diag, ^(const char *symbolName, uint64_t imageOffset, bool isFromExportTrie) { |
1078 | uintptr_t targetAddr = (uintptr_t)ma + (uintptr_t)imageOffset; | |
a9a4db61 A |
1079 | if ( isFromExportTrie ) { |
1080 | // Avoid duplicating the string if we already have the symbol name | |
1081 | if ( context.weakDefMap.find(symbolName) != context.weakDefMap.end() ) | |
1082 | return; | |
1083 | symbolName = strdup(symbolName); | |
1084 | } | |
1085 | context.weakDefMap.insert({ symbolName, { image, targetAddr } }); | |
1086 | }); | |
1087 | } | |
1088 | } | |
1089 | ||
1090 | // Walk the nlist for all binaries in dlopen and fill in the map with any other weak defs | |
1091 | for (int i=0; i < count; ++i) { | |
1092 | const ImageLoader* image = imagesNeedingCoalescing[i]; | |
1093 | if ( image->weakSymbolsBound(imageIndexes[i]) ) | |
1094 | continue; | |
1095 | // skip images without defs. We'll process refs later | |
1096 | if ( !image->hasCoalescedExports() ) | |
1097 | continue; | |
1098 | Diagnostics diag; | |
1099 | const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)image->machHeader(); | |
bc3b7c8c A |
1100 | ma->forEachWeakDef(diag, ^(const char *symbolName, uint64_t imageOffset, bool isFromExportTrie) { |
1101 | uintptr_t targetAddr = (uintptr_t)ma + (uintptr_t)imageOffset; | |
a9a4db61 A |
1102 | if ( isFromExportTrie ) { |
1103 | // Avoid duplicating the string if we already have the symbol name | |
1104 | if ( context.weakDefMap.find(symbolName) != context.weakDefMap.end() ) | |
1105 | return; | |
1106 | symbolName = strdup(symbolName); | |
1107 | } | |
1108 | context.weakDefMap.insert({ symbolName, { image, targetAddr } }); | |
1109 | }); | |
1110 | } | |
1111 | // for all images that need weak binding | |
1112 | for (int i=0; i < count; ++i) { | |
1113 | ImageLoader* imageBeingFixedUp = imagesNeedingCoalescing[i]; | |
1114 | if ( imageBeingFixedUp->weakSymbolsBound(imageIndexes[i]) ) | |
1115 | continue; // weak binding already completed | |
1116 | bool imageBeingFixedUpInCache = imageBeingFixedUp->inSharedCache(); | |
1117 | ||
1118 | if ( context.verboseWeakBind ) | |
1119 | dyld::log("dyld: checking for weak symbols in %s\n", imageBeingFixedUp->getPath()); | |
1120 | // for all symbols that need weak binding in this image | |
1121 | ImageLoader::CoalIterator coalIterator; | |
1122 | imageBeingFixedUp->initializeCoalIterator(coalIterator, i, imageIndexes[i]); | |
1123 | while ( !imageBeingFixedUp->incrementCoalIterator(coalIterator) ) { | |
1124 | const char* nameToCoalesce = coalIterator.symbolName; | |
1125 | uintptr_t targetAddr = 0; | |
1126 | const ImageLoader* targetImage; | |
1127 | // Seatch the map for a previous definition to use | |
1128 | auto weakDefIt = context.weakDefMap.find(nameToCoalesce); | |
1129 | if ( (weakDefIt != context.weakDefMap.end()) && (weakDefIt->second.first != nullptr) ) { | |
1130 | // Found a previous defition | |
1131 | targetImage = weakDefIt->second.first; | |
1132 | targetAddr = weakDefIt->second.second; | |
1133 | } else { | |
1134 | // scan all images looking for definition to use | |
1135 | for (int j=0; j < count; ++j) { | |
1136 | const ImageLoader* anImage = imagesNeedingCoalescing[j]; | |
1137 | bool anImageInCache = anImage->inSharedCache(); | |
1138 | // <rdar://problem/47986398> Don't look at images in dyld cache because cache is | |
1139 | // already coalesced. Only images outside cache can potentially override something in cache. | |
1140 | if ( anImageInCache && imageBeingFixedUpInCache ) | |
1141 | continue; | |
1142 | ||
1143 | //dyld::log("looking for %s in %s\n", nameToCoalesce, anImage->getPath()); | |
1144 | const ImageLoader* foundIn; | |
1145 | const Symbol* sym = anImage->findExportedSymbol(nameToCoalesce, false, &foundIn); | |
1146 | if ( sym != NULL ) { | |
1147 | targetAddr = foundIn->getExportedSymbolAddress(sym, context); | |
1148 | targetImage = foundIn; | |
1149 | if ( context.verboseWeakBind ) | |
1150 | dyld::log("dyld: found weak %s at 0x%lX in %s\n", nameToCoalesce, targetAddr, foundIn->getPath()); | |
1151 | break; | |
1152 | } | |
1153 | } | |
1154 | } | |
1155 | if ( (targetAddr != 0) && (coalIterator.image != targetImage) ) { | |
797cc951 A |
1156 | if ( coalIterator.image->inSharedCache() ) |
1157 | patcher.makeWriteable(); | |
a9a4db61 A |
1158 | coalIterator.image->updateUsesCoalIterator(coalIterator, targetAddr, (ImageLoader*)targetImage, 0, context); |
1159 | if (weakDefIt == context.weakDefMap.end()) { | |
1160 | if (targetImage->neverUnload()) { | |
1161 | // Add never unload defs to the map for next time | |
1162 | context.weakDefMap.insert({ nameToCoalesce, { targetImage, targetAddr } }); | |
1163 | if ( context.verboseWeakBind ) { | |
1164 | dyld::log("dyld: weak binding adding %s to map\n", nameToCoalesce); | |
1165 | } | |
1166 | } else { | |
1167 | // Add a placeholder for unloadable symbols which makes us fall back to the regular search | |
1168 | context.weakDefMap.insert({ nameToCoalesce, { targetImage, targetAddr } }); | |
1169 | if ( context.verboseWeakBind ) { | |
1170 | dyld::log("dyld: weak binding adding unloadable placeholder %s to map\n", nameToCoalesce); | |
1171 | } | |
1172 | } | |
1173 | } | |
1174 | if ( context.verboseWeakBind ) | |
1175 | dyld::log("dyld: adjusting uses of %s in %s to use definition from %s\n", nameToCoalesce, coalIterator.image->getPath(), targetImage->getPath()); | |
1176 | } | |
1177 | } | |
1178 | imageBeingFixedUp->setWeakSymbolsBound(imageIndexes[i]); | |
1179 | } | |
1180 | } | |
1181 | else | |
bc3b7c8c | 1182 | #endif // TARGET_OS_OSX |
a9a4db61 A |
1183 | { |
1184 | // make symbol iterators for each | |
1185 | ImageLoader::CoalIterator iterators[count]; | |
1186 | ImageLoader::CoalIterator* sortedIts[count]; | |
1187 | for(int i=0; i < count; ++i) { | |
1188 | imagesNeedingCoalescing[i]->initializeCoalIterator(iterators[i], i, imageIndexes[i]); | |
1189 | sortedIts[i] = &iterators[i]; | |
1190 | if ( context.verboseWeakBind ) | |
1191 | dyld::log("dyld: weak bind load order %d/%d for %s\n", i, count, imagesNeedingCoalescing[i]->getIndexedPath(imageIndexes[i])); | |
1192 | } | |
1193 | ||
1194 | // walk all symbols keeping iterators in sync by | |
1195 | // only ever incrementing the iterator with the lowest symbol | |
1196 | int doneCount = 0; | |
1197 | while ( doneCount != count ) { | |
1198 | //for(int i=0; i < count; ++i) | |
1199 | // dyld::log("sym[%d]=%s ", sortedIts[i]->loadOrder, sortedIts[i]->symbolName); | |
1200 | //dyld::log("\n"); | |
1201 | // increment iterator with lowest symbol | |
1202 | if ( sortedIts[0]->image->incrementCoalIterator(*sortedIts[0]) ) | |
1203 | ++doneCount; | |
1204 | // re-sort iterators | |
1205 | for(int i=1; i < count; ++i) { | |
1206 | int result = strcmp(sortedIts[i-1]->symbolName, sortedIts[i]->symbolName); | |
1207 | if ( result == 0 ) | |
1208 | sortedIts[i-1]->symbolMatches = true; | |
1209 | if ( result > 0 ) { | |
1210 | // new one is bigger then next, so swap | |
1211 | ImageLoader::CoalIterator* temp = sortedIts[i-1]; | |
1212 | sortedIts[i-1] = sortedIts[i]; | |
1213 | sortedIts[i] = temp; | |
1214 | } | |
1215 | if ( result < 0 ) | |
1216 | break; | |
1217 | } | |
1218 | // process all matching symbols just before incrementing the lowest one that matches | |
1219 | if ( sortedIts[0]->symbolMatches && !sortedIts[0]->done ) { | |
1220 | const char* nameToCoalesce = sortedIts[0]->symbolName; | |
1221 | // pick first symbol in load order (and non-weak overrides weak) | |
1222 | uintptr_t targetAddr = 0; | |
1223 | ImageLoader* targetImage = NULL; | |
1224 | unsigned targetImageIndex = 0; | |
1225 | for(int i=0; i < count; ++i) { | |
1226 | if ( strcmp(iterators[i].symbolName, nameToCoalesce) == 0 ) { | |
1227 | if ( context.verboseWeakBind ) | |
1228 | dyld::log("dyld: weak bind, found %s weak=%d in %s \n", nameToCoalesce, iterators[i].weakSymbol, iterators[i].image->getIndexedPath((unsigned)iterators[i].imageIndex)); | |
1229 | if ( iterators[i].weakSymbol ) { | |
1230 | if ( targetAddr == 0 ) { | |
1231 | targetAddr = iterators[i].image->getAddressCoalIterator(iterators[i], context); | |
1232 | if ( targetAddr != 0 ) { | |
1233 | targetImage = iterators[i].image; | |
1234 | targetImageIndex = (unsigned)iterators[i].imageIndex; | |
1235 | } | |
1236 | } | |
1237 | } | |
1238 | else { | |
1239 | targetAddr = iterators[i].image->getAddressCoalIterator(iterators[i], context); | |
1240 | if ( targetAddr != 0 ) { | |
1241 | targetImage = iterators[i].image; | |
1242 | targetImageIndex = (unsigned)iterators[i].imageIndex; | |
1243 | // strong implementation found, stop searching | |
1244 | break; | |
1245 | } | |
1246 | } | |
1247 | } | |
1248 | } | |
1249 | // tell each to bind to this symbol (unless already bound) | |
1250 | if ( targetAddr != 0 ) { | |
1251 | if ( context.verboseWeakBind ) { | |
1252 | dyld::log("dyld: weak binding all uses of %s to copy from %s\n", | |
1253 | nameToCoalesce, targetImage->getIndexedShortName(targetImageIndex)); | |
1254 | } | |
1255 | for(int i=0; i < count; ++i) { | |
1256 | if ( strcmp(iterators[i].symbolName, nameToCoalesce) == 0 ) { | |
1257 | if ( context.verboseWeakBind ) { | |
1258 | dyld::log("dyld: weak bind, setting all uses of %s in %s to 0x%lX from %s\n", | |
1259 | nameToCoalesce, iterators[i].image->getIndexedShortName((unsigned)iterators[i].imageIndex), | |
1260 | targetAddr, targetImage->getIndexedShortName(targetImageIndex)); | |
1261 | } | |
797cc951 A |
1262 | if ( ! iterators[i].image->weakSymbolsBound(imageIndexes[i]) ) { |
1263 | if ( iterators[i].image->inSharedCache() ) | |
1264 | patcher.makeWriteable(); | |
a9a4db61 | 1265 | iterators[i].image->updateUsesCoalIterator(iterators[i], targetAddr, targetImage, targetImageIndex, context); |
797cc951 | 1266 | } |
a9a4db61 A |
1267 | iterators[i].symbolMatches = false; |
1268 | } | |
1269 | } | |
1270 | if (targetImage->neverUnload()) { | |
1271 | // Add never unload defs to the map for next time | |
1272 | context.weakDefMap.insert({ nameToCoalesce, { targetImage, targetAddr } }); | |
1273 | if ( context.verboseWeakBind ) { | |
1274 | dyld::log("dyld: weak binding adding %s to map\n", | |
1275 | nameToCoalesce); | |
1276 | } | |
1277 | } | |
1278 | } | |
1279 | ||
1280 | } | |
1281 | } | |
1282 | ||
1283 | for (int i=0; i < count; ++i) { | |
1284 | if ( imagesNeedingCoalescing[i]->weakSymbolsBound(imageIndexes[i]) ) | |
1285 | continue; // skip images already processed | |
1286 | ||
1287 | if ( imagesNeedingCoalescing[i]->usesChainedFixups() ) { | |
1288 | // during binding of references to weak-def symbols, the dyld cache was patched | |
1289 | // but if main executable has non-weak override of operator new or delete it needs is handled here | |
1290 | for (const char* weakSymbolName : sTreatAsWeak) { | |
1291 | const ImageLoader* dummy; | |
797cc951 | 1292 | imagesNeedingCoalescing[i]->resolveWeak(context, weakSymbolName, true, false, &dummy, patcher); |
a9a4db61 A |
1293 | } |
1294 | } | |
1295 | #if __arm64e__ | |
1296 | else { | |
1297 | // support traditional arm64 app on an arm64e device | |
1298 | // look for weak def symbols in this image which may override the cache | |
1299 | ImageLoader::CoalIterator coaler; | |
1300 | imagesNeedingCoalescing[i]->initializeCoalIterator(coaler, i, 0); | |
1301 | imagesNeedingCoalescing[i]->incrementCoalIterator(coaler); | |
1302 | while ( !coaler.done ) { | |
1303 | const ImageLoader* dummy; | |
1304 | // a side effect of resolveWeak() is to patch cache | |
797cc951 | 1305 | imagesNeedingCoalescing[i]->resolveWeak(context, coaler.symbolName, true, false, &dummy, patcher); |
a9a4db61 A |
1306 | imagesNeedingCoalescing[i]->incrementCoalIterator(coaler); |
1307 | } | |
1308 | } | |
1309 | #endif | |
1310 | } | |
1311 | ||
1312 | // mark all as having all weak symbols bound | |
1313 | for(int i=0; i < count; ++i) { | |
1314 | imagesNeedingCoalescing[i]->setWeakSymbolsBound(imageIndexes[i]); | |
1315 | } | |
1316 | } | |
1317 | } | |
1318 | ||
1319 | uint64_t t2 = mach_absolute_time(); | |
1320 | fgTotalWeakBindTime += t2 - t1; | |
1321 | ||
1322 | if ( context.verboseWeakBind ) | |
1323 | dyld::log("dyld: weak bind end\n"); | |
1324 | } | |
1325 | ||
1326 | ||
1327 | void ImageLoader::weakBindOld(const LinkContext& context) | |
1328 | { | |
1329 | if ( context.verboseWeakBind ) | |
1330 | dyld::log("dyld: weak bind start:\n"); | |
1331 | uint64_t t1 = mach_absolute_time(); | |
1332 | // get set of ImageLoaders that participate in coalecsing | |
1333 | ImageLoader* imagesNeedingCoalescing[fgImagesRequiringCoalescing]; | |
1334 | unsigned imageIndexes[fgImagesRequiringCoalescing]; | |
1335 | int count = context.getCoalescedImages(imagesNeedingCoalescing, imageIndexes); | |
1336 | ||
1337 | // count how many have not already had weakbinding done | |
1338 | int countNotYetWeakBound = 0; | |
1339 | int countOfImagesWithWeakDefinitionsNotInSharedCache = 0; | |
1340 | for(int i=0; i < count; ++i) { | |
1341 | if ( ! imagesNeedingCoalescing[i]->weakSymbolsBound(imageIndexes[i]) ) | |
1342 | ++countNotYetWeakBound; | |
1343 | if ( ! imagesNeedingCoalescing[i]->inSharedCache() ) | |
1344 | ++countOfImagesWithWeakDefinitionsNotInSharedCache; | |
1345 | } | |
1346 | ||
39a8cd10 | 1347 | // don't need to do any coalescing if only one image has overrides, or all have already been done |
412ebb8e | 1348 | if ( (countOfImagesWithWeakDefinitionsNotInSharedCache > 0) && (countNotYetWeakBound > 0) ) { |
797cc951 A |
1349 | // We might have to patch the shared cache __DATA_CONST. In that case, we'll create just a single |
1350 | // patcher when needed. | |
1351 | DyldSharedCache::DataConstLazyScopedWriter patcher(context.dyldCache, mach_task_self(), context.verboseMapping ? &dyld::log : nullptr); | |
1352 | ||
bc3b7c8c | 1353 | #if TARGET_OS_OSX |
cf998323 A |
1354 | // only do alternate algorithm for dlopen(). Use traditional algorithm for launch |
1355 | if ( !context.linkingMainExecutable ) { | |
1356 | // for all images that need weak binding | |
1357 | for (int i=0; i < count; ++i) { | |
1358 | ImageLoader* imageBeingFixedUp = imagesNeedingCoalescing[i]; | |
1359 | if ( imageBeingFixedUp->weakSymbolsBound(imageIndexes[i]) ) | |
1360 | continue; // weak binding already completed | |
1361 | bool imageBeingFixedUpInCache = imageBeingFixedUp->inSharedCache(); | |
1362 | ||
1363 | if ( context.verboseWeakBind ) | |
1364 | dyld::log("dyld: checking for weak symbols in %s\n", imageBeingFixedUp->getPath()); | |
1365 | // for all symbols that need weak binding in this image | |
1366 | ImageLoader::CoalIterator coalIterator; | |
1367 | imageBeingFixedUp->initializeCoalIterator(coalIterator, i, imageIndexes[i]); | |
1368 | while ( !imageBeingFixedUp->incrementCoalIterator(coalIterator) ) { | |
1369 | const char* nameToCoalesce = coalIterator.symbolName; | |
1370 | uintptr_t targetAddr = 0; | |
1371 | const ImageLoader* targetImage; | |
1372 | // scan all images looking for definition to use | |
1373 | for (int j=0; j < count; ++j) { | |
1374 | const ImageLoader* anImage = imagesNeedingCoalescing[j]; | |
1375 | bool anImageInCache = anImage->inSharedCache(); | |
1376 | // <rdar://problem/47986398> Don't look at images in dyld cache because cache is | |
1377 | // already coalesced. Only images outside cache can potentially override something in cache. | |
1378 | if ( anImageInCache && imageBeingFixedUpInCache ) | |
1379 | continue; | |
1380 | ||
1381 | //dyld::log("looking for %s in %s\n", nameToCoalesce, anImage->getPath()); | |
1382 | const ImageLoader* foundIn; | |
1383 | const Symbol* sym = anImage->findExportedSymbol(nameToCoalesce, false, &foundIn); | |
1384 | if ( sym != NULL ) { | |
1385 | if ( (foundIn->getExportedSymbolInfo(sym) & ImageLoader::kWeakDefinition) == 0 ) { | |
1386 | // found non-weak def, use it and stop looking | |
1387 | targetAddr = foundIn->getExportedSymbolAddress(sym, context); | |
1388 | targetImage = foundIn; | |
1389 | if ( context.verboseWeakBind ) | |
1390 | dyld::log("dyld: found strong %s at 0x%lX in %s\n", nameToCoalesce, targetAddr, foundIn->getPath()); | |
1391 | break; | |
1392 | } | |
1393 | else { | |
1394 | // found weak-def, only use if no weak found yet | |
1395 | if ( targetAddr == 0 ) { | |
1396 | targetAddr = foundIn->getExportedSymbolAddress(sym, context); | |
1397 | targetImage = foundIn; | |
1398 | if ( context.verboseWeakBind ) | |
1399 | dyld::log("dyld: found weak %s at 0x%lX in %s\n", nameToCoalesce, targetAddr, foundIn->getPath()); | |
1400 | } | |
1401 | } | |
1402 | } | |
1403 | } | |
1404 | if ( (targetAddr != 0) && (coalIterator.image != targetImage) ) { | |
797cc951 A |
1405 | if ( coalIterator.image->inSharedCache() ) |
1406 | patcher.makeWriteable(); | |
cf998323 A |
1407 | coalIterator.image->updateUsesCoalIterator(coalIterator, targetAddr, (ImageLoader*)targetImage, 0, context); |
1408 | if ( context.verboseWeakBind ) | |
1409 | dyld::log("dyld: adjusting uses of %s in %s to use definition from %s\n", nameToCoalesce, coalIterator.image->getPath(), targetImage->getPath()); | |
1410 | } | |
1411 | } | |
1412 | imageBeingFixedUp->setWeakSymbolsBound(imageIndexes[i]); | |
1413 | } | |
1414 | } | |
1415 | else | |
bc3b7c8c | 1416 | #endif // TARGET_OS_OSX |
cf998323 | 1417 | { |
39a8cd10 A |
1418 | // make symbol iterators for each |
1419 | ImageLoader::CoalIterator iterators[count]; | |
1420 | ImageLoader::CoalIterator* sortedIts[count]; | |
1421 | for(int i=0; i < count; ++i) { | |
9f83892a | 1422 | imagesNeedingCoalescing[i]->initializeCoalIterator(iterators[i], i, imageIndexes[i]); |
39a8cd10 A |
1423 | sortedIts[i] = &iterators[i]; |
1424 | if ( context.verboseWeakBind ) | |
9f83892a | 1425 | dyld::log("dyld: weak bind load order %d/%d for %s\n", i, count, imagesNeedingCoalescing[i]->getIndexedPath(imageIndexes[i])); |
0959b6d4 | 1426 | } |
a9a4db61 A |
1427 | |
1428 | // walk all symbols keeping iterators in sync by | |
1429 | // only ever incrementing the iterator with the lowest symbol | |
39a8cd10 A |
1430 | int doneCount = 0; |
1431 | while ( doneCount != count ) { | |
1432 | //for(int i=0; i < count; ++i) | |
1433 | // dyld::log("sym[%d]=%s ", sortedIts[i]->loadOrder, sortedIts[i]->symbolName); | |
1434 | //dyld::log("\n"); | |
1435 | // increment iterator with lowest symbol | |
1436 | if ( sortedIts[0]->image->incrementCoalIterator(*sortedIts[0]) ) | |
a9a4db61 | 1437 | ++doneCount; |
39a8cd10 A |
1438 | // re-sort iterators |
1439 | for(int i=1; i < count; ++i) { | |
1440 | int result = strcmp(sortedIts[i-1]->symbolName, sortedIts[i]->symbolName); | |
1441 | if ( result == 0 ) | |
1442 | sortedIts[i-1]->symbolMatches = true; | |
1443 | if ( result > 0 ) { | |
1444 | // new one is bigger then next, so swap | |
1445 | ImageLoader::CoalIterator* temp = sortedIts[i-1]; | |
1446 | sortedIts[i-1] = sortedIts[i]; | |
1447 | sortedIts[i] = temp; | |
1448 | } | |
1449 | if ( result < 0 ) | |
1450 | break; | |
1451 | } | |
1452 | // process all matching symbols just before incrementing the lowest one that matches | |
1453 | if ( sortedIts[0]->symbolMatches && !sortedIts[0]->done ) { | |
1454 | const char* nameToCoalesce = sortedIts[0]->symbolName; | |
1455 | // pick first symbol in load order (and non-weak overrides weak) | |
1456 | uintptr_t targetAddr = 0; | |
1457 | ImageLoader* targetImage = NULL; | |
9f83892a | 1458 | unsigned targetImageIndex = 0; |
39a8cd10 A |
1459 | for(int i=0; i < count; ++i) { |
1460 | if ( strcmp(iterators[i].symbolName, nameToCoalesce) == 0 ) { | |
1461 | if ( context.verboseWeakBind ) | |
9f83892a | 1462 | dyld::log("dyld: weak bind, found %s weak=%d in %s \n", nameToCoalesce, iterators[i].weakSymbol, iterators[i].image->getIndexedPath((unsigned)iterators[i].imageIndex)); |
39a8cd10 A |
1463 | if ( iterators[i].weakSymbol ) { |
1464 | if ( targetAddr == 0 ) { | |
1465 | targetAddr = iterators[i].image->getAddressCoalIterator(iterators[i], context); | |
9f83892a | 1466 | if ( targetAddr != 0 ) { |
39a8cd10 | 1467 | targetImage = iterators[i].image; |
9f83892a A |
1468 | targetImageIndex = (unsigned)iterators[i].imageIndex; |
1469 | } | |
39a8cd10 A |
1470 | } |
1471 | } | |
1472 | else { | |
1473 | targetAddr = iterators[i].image->getAddressCoalIterator(iterators[i], context); | |
1474 | if ( targetAddr != 0 ) { | |
1475 | targetImage = iterators[i].image; | |
9f83892a | 1476 | targetImageIndex = (unsigned)iterators[i].imageIndex; |
39a8cd10 A |
1477 | // strong implementation found, stop searching |
1478 | break; | |
1479 | } | |
1480 | } | |
1481 | } | |
1482 | } | |
39a8cd10 A |
1483 | // tell each to bind to this symbol (unless already bound) |
1484 | if ( targetAddr != 0 ) { | |
9f83892a A |
1485 | if ( context.verboseWeakBind ) { |
1486 | dyld::log("dyld: weak binding all uses of %s to copy from %s\n", | |
1487 | nameToCoalesce, targetImage->getIndexedShortName(targetImageIndex)); | |
1488 | } | |
39a8cd10 A |
1489 | for(int i=0; i < count; ++i) { |
1490 | if ( strcmp(iterators[i].symbolName, nameToCoalesce) == 0 ) { | |
9f83892a A |
1491 | if ( context.verboseWeakBind ) { |
1492 | dyld::log("dyld: weak bind, setting all uses of %s in %s to 0x%lX from %s\n", | |
1493 | nameToCoalesce, iterators[i].image->getIndexedShortName((unsigned)iterators[i].imageIndex), | |
1494 | targetAddr, targetImage->getIndexedShortName(targetImageIndex)); | |
1495 | } | |
797cc951 A |
1496 | if ( ! iterators[i].image->weakSymbolsBound(imageIndexes[i]) ) { |
1497 | if ( iterators[i].image->inSharedCache() ) | |
1498 | patcher.makeWriteable(); | |
9f83892a | 1499 | iterators[i].image->updateUsesCoalIterator(iterators[i], targetAddr, targetImage, targetImageIndex, context); |
797cc951 | 1500 | } |
a9a4db61 | 1501 | iterators[i].symbolMatches = false; |
39a8cd10 A |
1502 | } |
1503 | } | |
1504 | } | |
6cae9b63 | 1505 | |
39a8cd10 A |
1506 | } |
1507 | } | |
6cae9b63 | 1508 | |
6cae9b63 | 1509 | for (int i=0; i < count; ++i) { |
cf998323 A |
1510 | if ( imagesNeedingCoalescing[i]->weakSymbolsBound(imageIndexes[i]) ) |
1511 | continue; // skip images already processed | |
1512 | ||
6cae9b63 A |
1513 | if ( imagesNeedingCoalescing[i]->usesChainedFixups() ) { |
1514 | // during binding of references to weak-def symbols, the dyld cache was patched | |
1515 | // but if main executable has non-weak override of operator new or delete it needs is handled here | |
cf998323 A |
1516 | for (const char* weakSymbolName : sTreatAsWeak) { |
1517 | const ImageLoader* dummy; | |
797cc951 | 1518 | imagesNeedingCoalescing[i]->resolveWeak(context, weakSymbolName, true, false, &dummy, patcher); |
6cae9b63 A |
1519 | } |
1520 | } | |
cf998323 | 1521 | #if __arm64e__ |
6cae9b63 | 1522 | else { |
cf998323 | 1523 | // support traditional arm64 app on an arm64e device |
6cae9b63 | 1524 | // look for weak def symbols in this image which may override the cache |
797cc951 | 1525 | patcher.makeWriteable(); |
6cae9b63 A |
1526 | ImageLoader::CoalIterator coaler; |
1527 | imagesNeedingCoalescing[i]->initializeCoalIterator(coaler, i, 0); | |
1528 | imagesNeedingCoalescing[i]->incrementCoalIterator(coaler); | |
1529 | while ( !coaler.done ) { | |
6cae9b63 A |
1530 | const ImageLoader* dummy; |
1531 | // a side effect of resolveWeak() is to patch cache | |
797cc951 | 1532 | imagesNeedingCoalescing[i]->resolveWeak(context, coaler.symbolName, true, false, &dummy, patcher); |
cf998323 | 1533 | imagesNeedingCoalescing[i]->incrementCoalIterator(coaler); |
6cae9b63 A |
1534 | } |
1535 | } | |
6cae9b63 | 1536 | #endif |
cf998323 | 1537 | } |
6cae9b63 | 1538 | |
39a8cd10 A |
1539 | // mark all as having all weak symbols bound |
1540 | for(int i=0; i < count; ++i) { | |
9f83892a | 1541 | imagesNeedingCoalescing[i]->setWeakSymbolsBound(imageIndexes[i]); |
39a8cd10 | 1542 | } |
cf998323 | 1543 | } |
0959b6d4 | 1544 | } |
6cae9b63 | 1545 | |
2fd3f4e8 A |
1546 | uint64_t t2 = mach_absolute_time(); |
1547 | fgTotalWeakBindTime += t2 - t1; | |
a9a4db61 | 1548 | |
39a8cd10 A |
1549 | if ( context.verboseWeakBind ) |
1550 | dyld::log("dyld: weak bind end\n"); | |
0959b6d4 | 1551 | } |
39a8cd10 A |
1552 | |
1553 | ||
0959b6d4 | 1554 | |
bac542e6 A |
1555 | void ImageLoader::recursiveGetDOFSections(const LinkContext& context, std::vector<DOFInfo>& dofs) |
1556 | { | |
1557 | if ( ! fRegisteredDOF ) { | |
1558 | // break cycles | |
1559 | fRegisteredDOF = true; | |
1560 | ||
1561 | // gather lower level libraries first | |
39a8cd10 A |
1562 | for(unsigned int i=0; i < libraryCount(); ++i) { |
1563 | ImageLoader* dependentImage = libImage(i); | |
1564 | if ( dependentImage != NULL ) | |
1565 | dependentImage->recursiveGetDOFSections(context, dofs); | |
bac542e6 A |
1566 | } |
1567 | this->doGetDOFSections(context, dofs); | |
1568 | } | |
1569 | } | |
1570 | ||
2fd3f4e8 A |
1571 | void ImageLoader::setNeverUnloadRecursive() { |
1572 | if ( ! fNeverUnload ) { | |
1573 | // break cycles | |
1574 | fNeverUnload = true; | |
1575 | ||
1576 | // gather lower level libraries first | |
1577 | for(unsigned int i=0; i < libraryCount(); ++i) { | |
1578 | ImageLoader* dependentImage = libImage(i); | |
1579 | if ( dependentImage != NULL ) | |
1580 | dependentImage->setNeverUnloadRecursive(); | |
1581 | } | |
1582 | } | |
1583 | } | |
bac542e6 A |
1584 | |
1585 | void ImageLoader::recursiveSpinLock(recursive_lock& rlock) | |
1586 | { | |
1587 | // try to set image's ivar fInitializerRecursiveLock to point to this lock_info | |
1588 | // keep trying until success (spin) | |
cf998323 A |
1589 | #pragma clang diagnostic push |
1590 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" | |
bac542e6 A |
1591 | while ( ! OSAtomicCompareAndSwapPtrBarrier(NULL, &rlock, (void**)&fInitializerRecursiveLock) ) { |
1592 | // if fInitializerRecursiveLock already points to a different lock_info, if it is for | |
1593 | // the same thread we are on, the increment the lock count, otherwise continue to spin | |
1594 | if ( (fInitializerRecursiveLock != NULL) && (fInitializerRecursiveLock->thread == rlock.thread) ) | |
1595 | break; | |
1596 | } | |
cf998323 A |
1597 | #pragma clang diagnostic pop |
1598 | ++(fInitializerRecursiveLock->count); | |
bac542e6 A |
1599 | } |
1600 | ||
1601 | void ImageLoader::recursiveSpinUnLock() | |
1602 | { | |
1603 | if ( --(fInitializerRecursiveLock->count) == 0 ) | |
1604 | fInitializerRecursiveLock = NULL; | |
1605 | } | |
0959b6d4 | 1606 | |
9f83892a A |
1607 | void ImageLoader::InitializerTimingList::addTime(const char* name, uint64_t time) |
1608 | { | |
1609 | for (int i=0; i < count; ++i) { | |
1610 | if ( strcmp(images[i].shortName, name) == 0 ) { | |
1611 | images[i].initTime += time; | |
1612 | return; | |
1613 | } | |
1614 | } | |
1615 | images[count].initTime = time; | |
1616 | images[count].shortName = name; | |
1617 | ++count; | |
1618 | } | |
0959b6d4 | 1619 | |
9f83892a | 1620 | void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize, |
19894a12 | 1621 | InitializerTimingList& timingInfo, UninitedUpwards& uninitUps) |
0959b6d4 | 1622 | { |
bac542e6 A |
1623 | recursive_lock lock_info(this_thread); |
1624 | recursiveSpinLock(lock_info); | |
bac542e6 A |
1625 | |
1626 | if ( fState < dyld_image_state_dependents_initialized-1 ) { | |
1627 | uint8_t oldState = fState; | |
0959b6d4 | 1628 | // break cycles |
bac542e6 | 1629 | fState = dyld_image_state_dependents_initialized-1; |
0959b6d4 A |
1630 | try { |
1631 | // initialize lower level libraries first | |
39a8cd10 A |
1632 | for(unsigned int i=0; i < libraryCount(); ++i) { |
1633 | ImageLoader* dependentImage = libImage(i); | |
832b6fce | 1634 | if ( dependentImage != NULL ) { |
19894a12 A |
1635 | // don't try to initialize stuff "above" me yet |
1636 | if ( libIsUpward(i) ) { | |
cf998323 | 1637 | uninitUps.imagesAndPaths[uninitUps.count] = { dependentImage, libPath(i) }; |
19894a12 A |
1638 | uninitUps.count++; |
1639 | } | |
1640 | else if ( dependentImage->fDepth >= fDepth ) { | |
9f83892a | 1641 | dependentImage->recursiveInitialization(context, this_thread, libPath(i), timingInfo, uninitUps); |
832b6fce | 1642 | } |
832b6fce | 1643 | } |
0959b6d4 A |
1644 | } |
1645 | ||
1646 | // record termination order | |
1647 | if ( this->needsTermination() ) | |
1648 | context.terminationRecorder(this); | |
cf998323 | 1649 | |
832b6fce | 1650 | // let objc know we are about to initialize this image |
412ebb8e | 1651 | uint64_t t1 = mach_absolute_time(); |
bac542e6 A |
1652 | fState = dyld_image_state_dependents_initialized; |
1653 | oldState = fState; | |
9f83892a | 1654 | context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo); |
412ebb8e | 1655 | |
0959b6d4 | 1656 | // initialize this image |
412ebb8e | 1657 | bool hasInitializers = this->doInitialization(context); |
2fd3f4e8 | 1658 | |
832b6fce | 1659 | // let anyone know we finished initializing this image |
bac542e6 A |
1660 | fState = dyld_image_state_initialized; |
1661 | oldState = fState; | |
9f83892a | 1662 | context.notifySingle(dyld_image_state_initialized, this, NULL); |
412ebb8e A |
1663 | |
1664 | if ( hasInitializers ) { | |
1665 | uint64_t t2 = mach_absolute_time(); | |
9f83892a | 1666 | timingInfo.addTime(this->getShortName(), t2-t1); |
412ebb8e | 1667 | } |
0959b6d4 A |
1668 | } |
1669 | catch (const char* msg) { | |
1670 | // this image is not initialized | |
bac542e6 | 1671 | fState = oldState; |
bac542e6 | 1672 | recursiveSpinUnLock(); |
bac542e6 | 1673 | throw; |
0be5d81c | 1674 | } |
0959b6d4 | 1675 | } |
0959b6d4 | 1676 | |
bac542e6 | 1677 | recursiveSpinUnLock(); |
0959b6d4 A |
1678 | } |
1679 | ||
1680 | ||
1681 | static void printTime(const char* msg, uint64_t partTime, uint64_t totalTime) | |
1682 | { | |
1683 | static uint64_t sUnitsPerSecond = 0; | |
1684 | if ( sUnitsPerSecond == 0 ) { | |
1685 | struct mach_timebase_info timeBaseInfo; | |
6cae9b63 A |
1686 | if ( mach_timebase_info(&timeBaseInfo) != KERN_SUCCESS ) |
1687 | return; | |
1688 | sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer; | |
0959b6d4 A |
1689 | } |
1690 | if ( partTime < sUnitsPerSecond ) { | |
19894a12 A |
1691 | uint32_t milliSecondsTimesHundred = (uint32_t)((partTime*100000)/sUnitsPerSecond); |
1692 | uint32_t milliSeconds = (uint32_t)(milliSecondsTimesHundred/100); | |
1693 | uint32_t percentTimesTen = (uint32_t)((partTime*1000)/totalTime); | |
0959b6d4 | 1694 | uint32_t percent = percentTimesTen/10; |
9f83892a A |
1695 | if ( milliSeconds >= 100 ) |
1696 | dyld::log("%s: %u.%02u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimesHundred-milliSeconds*100, percent, percentTimesTen-percent*10); | |
1697 | else if ( milliSeconds >= 10 ) | |
1698 | dyld::log("%s: %u.%02u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimesHundred-milliSeconds*100, percent, percentTimesTen-percent*10); | |
1699 | else | |
1700 | dyld::log("%s: %u.%02u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimesHundred-milliSeconds*100, percent, percentTimesTen-percent*10); | |
0959b6d4 A |
1701 | } |
1702 | else { | |
19894a12 | 1703 | uint32_t secondsTimeTen = (uint32_t)((partTime*10)/sUnitsPerSecond); |
577cc7d1 | 1704 | uint32_t seconds = secondsTimeTen/10; |
19894a12 | 1705 | uint32_t percentTimesTen = (uint32_t)((partTime*1000)/totalTime); |
0959b6d4 | 1706 | uint32_t percent = percentTimesTen/10; |
bac542e6 | 1707 | dyld::log("%s: %u.%u seconds (%u.%u%%)\n", msg, seconds, secondsTimeTen-seconds*10, percent, percentTimesTen-percent*10); |
0959b6d4 A |
1708 | } |
1709 | } | |
1710 | ||
1711 | static char* commatize(uint64_t in, char* out) | |
1712 | { | |
bac542e6 A |
1713 | uint64_t div10 = in / 10; |
1714 | uint8_t delta = in - div10*10; | |
1715 | char* s = &out[32]; | |
1716 | int digitCount = 1; | |
1717 | *s = '\0'; | |
1718 | *(--s) = '0' + delta; | |
1719 | in = div10; | |
1720 | while ( in != 0 ) { | |
1721 | if ( (digitCount % 3) == 0 ) | |
1722 | *(--s) = ','; | |
1723 | div10 = in / 10; | |
1724 | delta = in - div10*10; | |
1725 | *(--s) = '0' + delta; | |
1726 | in = div10; | |
1727 | ++digitCount; | |
0959b6d4 | 1728 | } |
bac542e6 A |
1729 | return s; |
1730 | } | |
1731 | ||
0959b6d4 | 1732 | |
412ebb8e | 1733 | void ImageLoader::printStatistics(unsigned int imageCount, const InitializerTimingList& timingInfo) |
0959b6d4 | 1734 | { |
9f83892a A |
1735 | uint64_t totalTime = fgTotalLoadLibrariesTime + fgTotalRebaseTime + fgTotalBindTime + fgTotalWeakBindTime + fgTotalDOF + fgTotalInitTime; |
1736 | ||
1737 | uint64_t totalDyldTime = totalTime - fgTotalDebuggerPausedTime - fgTotalRebindCacheTime; | |
1738 | printTime("Total pre-main time", totalDyldTime, totalDyldTime); | |
1739 | printTime(" dylib loading time", fgTotalLoadLibrariesTime-fgTotalDebuggerPausedTime, totalDyldTime); | |
1740 | printTime(" rebase/binding time", fgTotalRebaseTime+fgTotalBindTime+fgTotalWeakBindTime-fgTotalRebindCacheTime, totalDyldTime); | |
1741 | printTime(" ObjC setup time", fgTotalObjCSetupTime, totalDyldTime); | |
1742 | printTime(" initializer time", fgTotalInitTime-fgTotalObjCSetupTime, totalDyldTime); | |
1743 | dyld::log(" slowest intializers :\n"); | |
1744 | for (uintptr_t i=0; i < timingInfo.count; ++i) { | |
1745 | uint64_t t = timingInfo.images[i].initTime; | |
1746 | if ( t*50 < totalDyldTime ) | |
1747 | continue; | |
1748 | dyld::log("%30s ", timingInfo.images[i].shortName); | |
1749 | if ( strncmp(timingInfo.images[i].shortName, "libSystem.", 10) == 0 ) | |
1750 | t -= fgTotalObjCSetupTime; | |
1751 | printTime("", t, totalDyldTime); | |
1752 | } | |
1753 | dyld::log("\n"); | |
1754 | } | |
1755 | ||
1756 | void ImageLoader::printStatisticsDetails(unsigned int imageCount, const InitializerTimingList& timingInfo) | |
1757 | { | |
1758 | uint64_t totalTime = fgTotalLoadLibrariesTime + fgTotalRebaseTime + fgTotalBindTime + fgTotalWeakBindTime + fgTotalDOF + fgTotalInitTime; | |
0959b6d4 A |
1759 | char commaNum1[40]; |
1760 | char commaNum2[40]; | |
1761 | ||
9f83892a A |
1762 | printTime(" total time", totalTime, totalTime); |
1763 | dyld::log(" total images loaded: %d (%u from dyld shared cache)\n", imageCount, fgImagesUsedFromSharedCache); | |
cf998323 | 1764 | dyld::log(" total segments mapped: %u, into %llu pages\n", fgTotalSegmentsMapped, fgTotalBytesMapped/4096); |
9f83892a A |
1765 | printTime(" total images loading time", fgTotalLoadLibrariesTime, totalTime); |
1766 | printTime(" total load time in ObjC", fgTotalObjCSetupTime, totalTime); | |
1767 | printTime(" total debugger pause time", fgTotalDebuggerPausedTime, totalTime); | |
1768 | printTime(" total dtrace DOF registration time", fgTotalDOF, totalTime); | |
1769 | dyld::log(" total rebase fixups: %s\n", commatize(fgTotalRebaseFixups, commaNum1)); | |
1770 | printTime(" total rebase fixups time", fgTotalRebaseTime, totalTime); | |
1771 | dyld::log(" total binding fixups: %s\n", commatize(fgTotalBindFixups, commaNum1)); | |
bac542e6 A |
1772 | if ( fgTotalBindSymbolsResolved != 0 ) { |
1773 | uint32_t avgTimesTen = (fgTotalBindImageSearches * 10) / fgTotalBindSymbolsResolved; | |
1774 | uint32_t avgInt = fgTotalBindImageSearches / fgTotalBindSymbolsResolved; | |
1775 | uint32_t avgTenths = avgTimesTen - (avgInt*10); | |
1776 | dyld::log("total binding symbol lookups: %s, average images searched per symbol: %u.%u\n", | |
1777 | commatize(fgTotalBindSymbolsResolved, commaNum1), avgInt, avgTenths); | |
1778 | } | |
9f83892a A |
1779 | printTime(" total binding fixups time", fgTotalBindTime, totalTime); |
1780 | printTime(" total weak binding fixups time", fgTotalWeakBindTime, totalTime); | |
1781 | printTime(" total redo shared cached bindings time", fgTotalRebindCacheTime, totalTime); | |
1782 | dyld::log(" total bindings lazily fixed up: %s of %s\n", commatize(fgTotalLazyBindFixups, commaNum1), commatize(fgTotalPossibleLazyBindFixups, commaNum2)); | |
1783 | printTime(" total time in initializers and ObjC +load", fgTotalInitTime-fgTotalObjCSetupTime, totalTime); | |
412ebb8e | 1784 | for (uintptr_t i=0; i < timingInfo.count; ++i) { |
9f83892a A |
1785 | uint64_t t = timingInfo.images[i].initTime; |
1786 | if ( t*1000 < totalTime ) | |
1787 | continue; | |
1788 | dyld::log("%42s ", timingInfo.images[i].shortName); | |
1789 | if ( strncmp(timingInfo.images[i].shortName, "libSystem.", 10) == 0 ) | |
1790 | t -= fgTotalObjCSetupTime; | |
1791 | printTime("", t, totalTime); | |
412ebb8e A |
1792 | } |
1793 | ||
0959b6d4 A |
1794 | } |
1795 | ||
1796 | ||
1797 | // | |
1798 | // copy path and add suffix to result | |
1799 | // | |
1800 | // /path/foo.dylib _debug => /path/foo_debug.dylib | |
1801 | // foo.dylib _debug => foo_debug.dylib | |
1802 | // foo _debug => foo_debug | |
1803 | // /path/bar _debug => /path/bar_debug | |
1804 | // /path/bar.A.dylib _debug => /path/bar.A_debug.dylib | |
1805 | // | |
1806 | void ImageLoader::addSuffix(const char* path, const char* suffix, char* result) | |
1807 | { | |
1808 | strcpy(result, path); | |
1809 | ||
1810 | char* start = strrchr(result, '/'); | |
1811 | if ( start != NULL ) | |
1812 | start++; | |
1813 | else | |
1814 | start = result; | |
1815 | ||
1816 | char* dot = strrchr(start, '.'); | |
1817 | if ( dot != NULL ) { | |
1818 | strcpy(dot, suffix); | |
1819 | strcat(&dot[strlen(suffix)], &path[dot-result]); | |
1820 | } | |
1821 | else { | |
1822 | strcat(result, suffix); | |
1823 | } | |
1824 | } | |
1825 | ||
1826 | ||
9f83892a A |
1827 | // |
1828 | // This function is the hotspot of symbol lookup. It was pulled out of findExportedSymbol() | |
1829 | // to enable it to be re-written in assembler if needed. | |
1830 | // | |
1831 | const uint8_t* ImageLoader::trieWalk(const uint8_t* start, const uint8_t* end, const char* s) | |
1832 | { | |
1833 | //dyld::log("trieWalk(%p, %p, %s)\n", start, end, s); | |
1834 | ++fgSymbolTrieSearchs; | |
1835 | const uint8_t* p = start; | |
1836 | while ( p != NULL ) { | |
1837 | uintptr_t terminalSize = *p++; | |
1838 | if ( terminalSize > 127 ) { | |
1839 | // except for re-export-with-rename, all terminal sizes fit in one byte | |
1840 | --p; | |
1841 | terminalSize = read_uleb128(p, end); | |
1842 | } | |
1843 | if ( (*s == '\0') && (terminalSize != 0) ) { | |
1844 | //dyld::log("trieWalk(%p) returning %p\n", start, p); | |
1845 | return p; | |
1846 | } | |
1847 | const uint8_t* children = p + terminalSize; | |
1848 | if ( children > end ) { | |
1849 | dyld::log("trieWalk() malformed trie node, terminalSize=0x%lx extends past end of trie\n", terminalSize); | |
1850 | return NULL; | |
1851 | } | |
1852 | //dyld::log("trieWalk(%p) sym=%s, terminalSize=%lu, children=%p\n", start, s, terminalSize, children); | |
1853 | uint8_t childrenRemaining = *children++; | |
1854 | p = children; | |
1855 | uintptr_t nodeOffset = 0; | |
1856 | for (; childrenRemaining > 0; --childrenRemaining) { | |
1857 | const char* ss = s; | |
1858 | //dyld::log("trieWalk(%p) child str=%s\n", start, (char*)p); | |
1859 | bool wrongEdge = false; | |
1860 | // scan whole edge to get to next edge | |
1861 | // if edge is longer than target symbol name, don't read past end of symbol name | |
1862 | char c = *p; | |
1863 | while ( c != '\0' ) { | |
1864 | if ( !wrongEdge ) { | |
1865 | if ( c != *ss ) | |
1866 | wrongEdge = true; | |
1867 | ++ss; | |
1868 | } | |
1869 | ++p; | |
1870 | c = *p; | |
1871 | } | |
1872 | if ( wrongEdge ) { | |
1873 | // advance to next child | |
1874 | ++p; // skip over zero terminator | |
1875 | // skip over uleb128 until last byte is found | |
1876 | while ( (*p & 0x80) != 0 ) | |
1877 | ++p; | |
1878 | ++p; // skip over last byte of uleb128 | |
1879 | if ( p > end ) { | |
1880 | dyld::log("trieWalk() malformed trie node, child node extends past end of trie\n"); | |
1881 | return NULL; | |
1882 | } | |
1883 | } | |
1884 | else { | |
1885 | // the symbol so far matches this edge (child) | |
1886 | // so advance to the child's node | |
1887 | ++p; | |
1888 | nodeOffset = read_uleb128(p, end); | |
1889 | if ( (nodeOffset == 0) || ( &start[nodeOffset] > end) ) { | |
1890 | dyld::log("trieWalk() malformed trie child, nodeOffset=0x%lx out of range\n", nodeOffset); | |
1891 | return NULL; | |
1892 | } | |
1893 | s = ss; | |
1894 | //dyld::log("trieWalk() found matching edge advancing to node 0x%lx\n", nodeOffset); | |
1895 | break; | |
1896 | } | |
1897 | } | |
1898 | if ( nodeOffset != 0 ) | |
1899 | p = &start[nodeOffset]; | |
1900 | else | |
1901 | p = NULL; | |
1902 | } | |
1903 | //dyld::log("trieWalk(%p) return NULL\n", start); | |
1904 | return NULL; | |
1905 | } | |
1906 | ||
1907 | ||
1908 | ||
1909 | uintptr_t ImageLoader::read_uleb128(const uint8_t*& p, const uint8_t* end) | |
1910 | { | |
1911 | uint64_t result = 0; | |
1912 | int bit = 0; | |
1913 | do { | |
1914 | if (p == end) | |
1915 | dyld::throwf("malformed uleb128"); | |
1916 | ||
1917 | uint64_t slice = *p & 0x7f; | |
1918 | ||
1919 | if (bit > 63) | |
1920 | dyld::throwf("uleb128 too big for uint64, bit=%d, result=0x%0llX", bit, result); | |
1921 | else { | |
1922 | result |= (slice << bit); | |
1923 | bit += 7; | |
1924 | } | |
1925 | } while (*p++ & 0x80); | |
10b92d3b | 1926 | return (uintptr_t)result; |
9f83892a A |
1927 | } |
1928 | ||
1929 | ||
1930 | intptr_t ImageLoader::read_sleb128(const uint8_t*& p, const uint8_t* end) | |
1931 | { | |
1932 | int64_t result = 0; | |
1933 | int bit = 0; | |
1934 | uint8_t byte; | |
1935 | do { | |
1936 | if (p == end) | |
1937 | throw "malformed sleb128"; | |
1938 | byte = *p++; | |
1939 | result |= (((int64_t)(byte & 0x7f)) << bit); | |
1940 | bit += 7; | |
1941 | } while (byte & 0x80); | |
1942 | // sign extend negative numbers | |
bc3b7c8c | 1943 | if ( ((byte & 0x40) != 0) && (bit < 64) ) |
6cae9b63 | 1944 | result |= (~0ULL) << bit; |
10b92d3b | 1945 | return (intptr_t)result; |
9f83892a A |
1946 | } |
1947 | ||
bc3b7c8c A |
1948 | void ImageLoader::forEachReExportDependent( void (^callback)(const ImageLoader*, bool& stop)) const |
1949 | { | |
1950 | bool stop = false; | |
1951 | for (unsigned int i=0; i < libraryCount(); ++i) { | |
1952 | if ( libReExported(i) ) { | |
1953 | if ( ImageLoader* dependentImage = libImage(i) ) { | |
1954 | callback(dependentImage, stop); | |
1955 | } | |
1956 | } | |
1957 | if (stop) | |
1958 | break; | |
1959 | } | |
1960 | } | |
1961 | ||
9f83892a | 1962 | |
2fd3f4e8 A |
1963 | VECTOR_NEVER_DESTRUCTED_IMPL(ImageLoader::InterposeTuple); |
1964 | VECTOR_NEVER_DESTRUCTED_IMPL(ImagePair); | |
1965 | ||
0959b6d4 A |
1966 | |
1967 |