]>
Commit | Line | Data |
---|---|---|
bc3b7c8c A |
1 | /* |
2 | * Copyright (c) 2019 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | #include <sys/types.h> | |
25 | #include <mach/mach.h> | |
26 | #include <assert.h> | |
27 | #include <limits.h> | |
28 | #include <stdlib.h> | |
29 | #include <string.h> | |
30 | #include <unistd.h> | |
31 | #include <mach-o/reloc.h> | |
32 | #include <mach-o/nlist.h> | |
33 | #include <TargetConditionals.h> | |
34 | ||
35 | #include "MachOAnalyzerSet.h" | |
36 | #include "DyldSharedCache.h" | |
37 | ||
38 | #if BUILDING_DYLD | |
39 | namespace dyld { void log(const char*, ...); } | |
40 | #endif | |
41 | ||
42 | namespace dyld3 { | |
43 | ||
44 | static bool hasHigh8(uint64_t addend) | |
45 | { | |
46 | // distinguish negative addend from TBI | |
47 | if ( (addend >> 56) == 0 ) | |
48 | return false; | |
49 | return ( (addend >> 48) != 0xFFFF ); | |
50 | } | |
51 | ||
52 | void MachOAnalyzerSet::WrappedMachO::forEachBind(Diagnostics& diag, FixUpHandler fixUpHandler, CachePatchHandler patchHandler) const | |
53 | { | |
54 | const bool is64 = _mh->is64(); | |
55 | __block int lastLibOrdinal = 256; | |
56 | __block const char* lastSymbolName = nullptr; | |
57 | __block uint64_t lastAddend = 0; | |
58 | __block FixupTarget target; | |
59 | __block PointerMetaData pmd; | |
60 | _mh->forEachBind(diag, ^(uint64_t runtimeOffset, int libOrdinal, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop) { | |
61 | if ( (symbolName == lastSymbolName) && (libOrdinal == lastLibOrdinal) && (addend == lastAddend) ) { | |
62 | // same symbol lookup as last location | |
63 | fixUpHandler(runtimeOffset, pmd, target, stop); | |
64 | } | |
65 | else if ( this->findSymbolFrom(diag, libOrdinal, symbolName, weakImport, lazyBind, addend, patchHandler, target) ) { | |
66 | pmd.high8 = 0; | |
67 | if ( is64 && (target.addend != 0) ) { | |
68 | if ( hasHigh8(target.addend) ) { | |
69 | pmd.high8 = (target.addend >> 56); | |
70 | target.offsetInImage &= 0x00FFFFFFFFFFFFFFULL; | |
71 | target.addend &= 0x00FFFFFFFFFFFFFFULL; | |
72 | } | |
73 | } | |
74 | if ( !target.skippableWeakDef ) { | |
75 | fixUpHandler(runtimeOffset, pmd, target, stop); | |
76 | lastSymbolName = symbolName; | |
77 | lastLibOrdinal = libOrdinal; | |
78 | lastAddend = addend; | |
79 | } | |
80 | } | |
81 | else { | |
82 | // call handler with missing symbol before stopping | |
83 | if ( target.kind == FixupTarget::Kind::bindMissingSymbol ) | |
84 | fixUpHandler(runtimeOffset, pmd, target, stop); | |
85 | stop = true; | |
86 | } | |
87 | }, ^(const char* symbolName) { | |
88 | }); | |
89 | } | |
90 | ||
91 | MachOAnalyzerSet::PointerMetaData::PointerMetaData() | |
92 | { | |
93 | this->diversity = 0; | |
94 | this->high8 = 0; | |
95 | this->authenticated = 0; | |
96 | this->key = 0; | |
97 | this->usesAddrDiversity = 0; | |
98 | } | |
99 | ||
100 | MachOAnalyzerSet::PointerMetaData::PointerMetaData(const MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, uint16_t pointer_format) | |
101 | { | |
102 | this->diversity = 0; | |
103 | this->high8 = 0; | |
104 | this->authenticated = 0; | |
105 | this->key = 0; | |
106 | this->usesAddrDiversity = 0; | |
107 | switch ( pointer_format ) { | |
108 | case DYLD_CHAINED_PTR_ARM64E: | |
109 | case DYLD_CHAINED_PTR_ARM64E_KERNEL: | |
110 | case DYLD_CHAINED_PTR_ARM64E_USERLAND: | |
111 | case DYLD_CHAINED_PTR_ARM64E_FIRMWARE: | |
112 | case DYLD_CHAINED_PTR_ARM64E_USERLAND24: | |
113 | this->authenticated = fixupLoc->arm64e.authRebase.auth; | |
114 | if ( this->authenticated ) { | |
115 | this->key = fixupLoc->arm64e.authRebase.key; | |
116 | this->usesAddrDiversity = fixupLoc->arm64e.authRebase.addrDiv; | |
117 | this->diversity = fixupLoc->arm64e.authRebase.diversity; | |
118 | } | |
119 | else if ( fixupLoc->arm64e.bind.bind == 0 ) { | |
120 | this->high8 = fixupLoc->arm64e.rebase.high8; | |
121 | } | |
122 | break; | |
123 | case DYLD_CHAINED_PTR_64: | |
124 | case DYLD_CHAINED_PTR_64_OFFSET: | |
125 | if ( fixupLoc->generic64.bind.bind == 0 ) | |
126 | this->high8 = fixupLoc->generic64.rebase.high8; | |
127 | break; | |
128 | } | |
129 | } | |
130 | ||
131 | ||
132 | void MachOAnalyzerSet::WrappedMachO::forEachFixup(Diagnostics& diag, FixUpHandler fixup, CachePatchHandler patcher) const | |
133 | { | |
134 | uint16_t fmPointerFormat; | |
135 | uint32_t fmStartsCount; | |
136 | const uint32_t* fmStarts; | |
137 | const MachOAnalyzer* ma = _mh; | |
138 | const uint64_t prefLoadAddr = ma->preferredLoadAddress(); | |
139 | if ( ma->hasChainedFixups() ) { | |
140 | // build targets table | |
141 | STACK_ALLOC_OVERFLOW_SAFE_ARRAY(FixupTarget, targets, 512); | |
142 | ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) { | |
143 | targets.default_constuct_back(); | |
144 | FixupTarget& foundTarget = targets.back(); | |
145 | if ( !this->findSymbolFrom(diag, libOrdinal, symbolName, weakImport, false, addend, patcher, foundTarget) ) { | |
146 | // call handler with missing symbol before stopping | |
147 | if ( foundTarget.kind == FixupTarget::Kind::bindMissingSymbol ) | |
148 | fixup(0, PointerMetaData(), foundTarget, stop); | |
149 | stop = true; | |
150 | } | |
151 | }); | |
152 | if ( diag.hasError() ) | |
153 | return; | |
154 | ||
155 | // walk all chains | |
156 | ma->withChainStarts(diag, ma->chainStartsOffset(), ^(const dyld_chained_starts_in_image* startsInfo) { | |
157 | ma->forEachFixupInAllChains(diag, startsInfo, false, ^(MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, | |
158 | const dyld_chained_starts_in_segment* segInfo, bool& fixupsStop) { | |
159 | uint64_t fixupOffset = (uint8_t*)fixupLoc - (uint8_t*)ma; | |
160 | uint64_t targetOffset; | |
161 | uint32_t bindOrdinal; | |
162 | int64_t embeddedAddend; | |
163 | PointerMetaData pmd(fixupLoc, segInfo->pointer_format); | |
164 | if ( fixupLoc->isBind(segInfo->pointer_format, bindOrdinal, embeddedAddend) ) { | |
165 | if ( bindOrdinal < targets.count() ) { | |
166 | if ( embeddedAddend == 0 ) { | |
167 | if ( hasHigh8(targets[bindOrdinal].addend) ) { | |
168 | FixupTarget targetWithoutHigh8 = targets[bindOrdinal]; | |
169 | pmd.high8 = (targetWithoutHigh8.addend >> 56); | |
170 | targetWithoutHigh8.offsetInImage &= 0x00FFFFFFFFFFFFFFULL; | |
171 | targetWithoutHigh8.addend &= 0x00FFFFFFFFFFFFFFULL; | |
172 | fixup(fixupOffset, pmd, targetWithoutHigh8, fixupsStop); | |
173 | } | |
174 | else { | |
175 | fixup(fixupOffset, pmd, targets[bindOrdinal], fixupsStop); | |
176 | } | |
177 | } | |
178 | else { | |
179 | // pointer on disk encodes extra addend, make pseudo target for that | |
180 | FixupTarget targetWithAddend = targets[bindOrdinal]; | |
181 | targetWithAddend.addend += embeddedAddend; | |
182 | targetWithAddend.offsetInImage += embeddedAddend; | |
183 | fixup(fixupOffset, pmd, targetWithAddend, fixupsStop); | |
184 | } | |
185 | } | |
186 | else { | |
187 | diag.error("out of range bind ordinal %d (max %lu)", bindOrdinal, targets.count()); | |
188 | fixupsStop = true; | |
189 | } | |
190 | } | |
191 | else if ( fixupLoc->isRebase(segInfo->pointer_format, prefLoadAddr, targetOffset) ) { | |
192 | FixupTarget rebaseTarget; | |
193 | rebaseTarget.kind = FixupTarget::Kind::rebase; | |
194 | rebaseTarget.foundInImage = *this; | |
195 | rebaseTarget.offsetInImage = targetOffset & 0x00FFFFFFFFFFFFFFULL; | |
196 | rebaseTarget.isLazyBindRebase = false; // FIXME | |
197 | fixup(fixupOffset, pmd, rebaseTarget, fixupsStop); | |
198 | } | |
199 | }); | |
200 | }); | |
201 | } | |
202 | else if ( ma->hasFirmwareChainStarts(&fmPointerFormat, &fmStartsCount, &fmStarts) ) { | |
203 | // This is firmware which only has rebases, the chain starts info is in a section (not LINKEDIT) | |
204 | ma->forEachFixupInAllChains(diag, fmPointerFormat, fmStartsCount, fmStarts, ^(MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, bool& stop) { | |
205 | uint64_t fixupOffset = (uint8_t*)fixupLoc - (uint8_t*)ma; | |
206 | PointerMetaData pmd(fixupLoc, fmPointerFormat); | |
207 | uint64_t targetOffset; | |
208 | fixupLoc->isRebase(fmPointerFormat, prefLoadAddr, targetOffset); | |
209 | FixupTarget rebaseTarget; | |
210 | rebaseTarget.kind = FixupTarget::Kind::rebase; | |
211 | rebaseTarget.foundInImage = *this; | |
212 | rebaseTarget.offsetInImage = targetOffset & 0x00FFFFFFFFFFFFFFULL; | |
213 | rebaseTarget.isLazyBindRebase = false; | |
214 | fixup(fixupOffset, pmd, rebaseTarget, stop); | |
215 | }); | |
216 | } | |
217 | else { | |
218 | // process all rebase opcodes | |
219 | const bool is64 = ma->is64(); | |
220 | ma->forEachRebase(diag, ^(uint64_t runtimeOffset, bool isLazyPointerRebase, bool& stop) { | |
221 | uint64_t* loc = (uint64_t*)((uint8_t*)ma + runtimeOffset); | |
222 | uint64_t locValue = is64 ? *loc : *((uint32_t*)loc); | |
223 | FixupTarget rebaseTarget; | |
224 | PointerMetaData pmd; | |
225 | if ( is64 ) | |
226 | pmd.high8 = (locValue >> 56); | |
227 | rebaseTarget.kind = FixupTarget::Kind::rebase; | |
228 | rebaseTarget.foundInImage = *this; | |
229 | rebaseTarget.offsetInImage = (locValue & 0x00FFFFFFFFFFFFFFULL) - prefLoadAddr; | |
230 | rebaseTarget.isLazyBindRebase = isLazyPointerRebase; | |
231 | fixup(runtimeOffset, pmd, rebaseTarget, stop); | |
232 | }); | |
233 | if ( diag.hasError() ) | |
234 | return; | |
235 | ||
236 | // process all bind opcodes | |
237 | this->forEachBind(diag, fixup, patcher); | |
238 | } | |
239 | if ( diag.hasError() ) | |
240 | return; | |
241 | ||
242 | // main executable may define operator new/delete symbols that overrides weak-defs but have no fixups | |
243 | if ( ma->isMainExecutable() && ma->hasWeakDefs() ) { | |
244 | _set->wmo_findExtraSymbolFrom(this, patcher); | |
245 | } | |
246 | } | |
247 | ||
248 | ||
249 | bool MachOAnalyzerSet::wmo_findSymbolFrom(const WrappedMachO* fromWmo, Diagnostics& diag, int libOrdinal, const char* symbolName, bool weakImport, | |
250 | bool lazyBind, uint64_t addend, CachePatchHandler patcher, FixupTarget& target) const | |
251 | { | |
252 | target.libOrdinal = libOrdinal; | |
253 | if ( libOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) { | |
254 | __block bool found = false; | |
255 | this->mas_forEachImage(^(const WrappedMachO& anImage, bool hidden, bool& stop) { | |
256 | // when an image is hidden (RTLD_LOCAL) it can still look up symbols in itself | |
257 | if ( hidden && (fromWmo->_mh != anImage._mh) ) | |
258 | return; | |
259 | if ( anImage.findSymbolIn(diag, symbolName, addend, target) ) { | |
260 | stop = true; | |
261 | found = true; | |
262 | } | |
263 | }); | |
264 | if ( found ) | |
265 | return true; | |
266 | // see if missing symbol resolver can find something | |
267 | if ( fromWmo->missingSymbolResolver(weakImport, lazyBind, symbolName, "flat namespace", fromWmo->path(), target) ) | |
268 | return true; | |
269 | // fill out target info about missing symbol | |
270 | target.kind = FixupTarget::Kind::bindMissingSymbol; | |
271 | target.requestedSymbolName = symbolName; | |
272 | target.foundSymbolName = nullptr; | |
273 | target.foundInImage = WrappedMachO(); // no image it should be in | |
274 | ||
275 | diag.error("symbol '%s' not found, expected in flat namespace by '%s'", symbolName, fromWmo->path()); | |
276 | return false; | |
277 | } | |
278 | else if ( libOrdinal == BIND_SPECIAL_DYLIB_WEAK_LOOKUP ) { | |
279 | if ( this->mas_fromImageWeakDefLookup(*fromWmo, symbolName, addend, patcher, target) ) { | |
280 | target.weakCoalesced = true; | |
281 | return true; | |
282 | } | |
283 | ||
284 | if ( !fromWmo->_mh->hasChainedFixups() ) { | |
285 | // support old binaries where symbols have been stripped and have weak_bind to itself | |
286 | target.skippableWeakDef = true; | |
287 | return true; | |
288 | } | |
289 | // see if missing symbol resolver can find something | |
290 | if ( fromWmo->missingSymbolResolver(weakImport, lazyBind, symbolName, "flat namespace", fromWmo->path(), target) ) | |
291 | return true; | |
292 | // fill out target info about missing symbol | |
293 | target.kind = FixupTarget::Kind::bindMissingSymbol; | |
294 | target.requestedSymbolName = symbolName; | |
295 | target.foundSymbolName = nullptr; | |
296 | target.foundInImage = WrappedMachO(); // no image it should be in | |
297 | ||
298 | diag.error("symbol '%s' not found, expected to be weak-def coalesced in '%s'", symbolName, fromWmo->path()); | |
299 | return false; | |
300 | } | |
301 | else { | |
302 | int depIndex = libOrdinal - 1; | |
303 | bool missingWeakDylib = false; | |
304 | WrappedMachO depHelper; | |
305 | const WrappedMachO* targetImage = nullptr; | |
306 | if ( libOrdinal == BIND_SPECIAL_DYLIB_SELF ) { | |
307 | targetImage = fromWmo; | |
308 | } | |
309 | else if ( libOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) { | |
310 | this->mas_mainExecutable(depHelper); | |
311 | targetImage = &depHelper; | |
312 | } | |
313 | else if ( fromWmo->dependent(depIndex, depHelper, missingWeakDylib) ) { | |
314 | targetImage = &depHelper; | |
315 | } | |
316 | else { | |
317 | diag.error("unknown library ordinal %d in %s", libOrdinal, fromWmo->path()); | |
318 | return false; | |
319 | } | |
320 | // use two-level namespace target image | |
321 | if ( !missingWeakDylib && targetImage->findSymbolIn(diag, symbolName, addend, target) ) | |
322 | return true; | |
323 | ||
324 | // see if missing symbol resolver can find something | |
325 | const char* expectedInPath = missingWeakDylib ? "missing dylib" : targetImage->path(); | |
326 | if ( fromWmo->missingSymbolResolver(weakImport, lazyBind, symbolName, expectedInPath, fromWmo->path(), target) ) | |
327 | return true; | |
328 | ||
329 | // fill out target info about missing symbol | |
330 | target.kind = FixupTarget::Kind::bindMissingSymbol; | |
331 | target.requestedSymbolName = symbolName; | |
332 | target.foundSymbolName = nullptr; | |
333 | target.foundInImage = *targetImage; // no image it is expected to be in | |
334 | ||
335 | // symbol not found and not weak or lazy so error out | |
336 | diag.error("symbol '%s' not found, expected in '%s', needed by '%s'", symbolName, expectedInPath, fromWmo->path()); | |
337 | return false; | |
338 | } | |
339 | return false; | |
340 | } | |
341 | ||
342 | // These are mangled symbols for all the variants of operator new and delete | |
343 | // which a main executable can define (non-weak) and override the | |
344 | // weak-def implementation in the OS. | |
345 | static const char* const sTreatAsWeak[] = { | |
346 | "__Znwm", "__ZnwmRKSt9nothrow_t", | |
347 | "__Znam", "__ZnamRKSt9nothrow_t", | |
348 | "__ZdlPv", "__ZdlPvRKSt9nothrow_t", "__ZdlPvm", | |
349 | "__ZdaPv", "__ZdaPvRKSt9nothrow_t", "__ZdaPvm", | |
350 | "__ZnwmSt11align_val_t", "__ZnwmSt11align_val_tRKSt9nothrow_t", | |
351 | "__ZnamSt11align_val_t", "__ZnamSt11align_val_tRKSt9nothrow_t", | |
352 | "__ZdlPvSt11align_val_t", "__ZdlPvSt11align_val_tRKSt9nothrow_t", "__ZdlPvmSt11align_val_t", | |
353 | "__ZdaPvSt11align_val_t", "__ZdaPvSt11align_val_tRKSt9nothrow_t", "__ZdaPvmSt11align_val_t" | |
354 | }; | |
355 | ||
356 | void MachOAnalyzerSet::wmo_findExtraSymbolFrom(const WrappedMachO* fromWmo, CachePatchHandler patcher) const | |
357 | { | |
358 | for (const char* weakSymbolName : sTreatAsWeak) { | |
359 | Diagnostics exportDiag; | |
360 | FixupTarget dummyTarget; | |
361 | // pretend main executable does have a use of this operator new/delete and look up the impl | |
362 | // this has the side effect of adding a cache patch if there is an impl outside the cache | |
363 | wmo_findSymbolFrom(fromWmo, exportDiag, -3, weakSymbolName, true, false, 0, patcher, dummyTarget); | |
364 | } | |
365 | ||
366 | } | |
367 | ||
368 | bool MachOAnalyzerSet::WrappedMachO::findSymbolIn(Diagnostics& diag, const char* symbolName, uint64_t addend, FixupTarget& target) const | |
369 | { | |
370 | const MachOAnalyzer* ma = _mh; | |
371 | ||
372 | // if exports trie location not computed yet, do it now | |
373 | ExportsTrie exportsTrie = this->getExportsTrie(); | |
374 | ||
375 | target.foundSymbolName = nullptr; | |
376 | if ( exportsTrie.start ) { | |
377 | if ( const uint8_t* node = this->_mh->trieWalk(diag, exportsTrie.start, exportsTrie.end, symbolName)) { | |
378 | const uint8_t* p = node; | |
379 | const uint64_t flags = this->_mh->read_uleb128(diag, p, exportsTrie.end); | |
380 | if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { | |
381 | // re-export from another dylib, lookup there | |
382 | const uint64_t libOrdinal = ma->read_uleb128(diag, p, exportsTrie.end); | |
383 | const char* importedName = (char*)p; | |
384 | if ( importedName[0] == '\0' ) | |
385 | importedName = symbolName; | |
386 | const int depIndex = (int)(libOrdinal - 1); | |
387 | bool missingWeakDylib; | |
388 | WrappedMachO depHelper; | |
389 | if ( this->dependent(depIndex, depHelper, missingWeakDylib) && !missingWeakDylib ) { | |
390 | if ( depHelper.findSymbolIn(diag, importedName, addend, target) ) { | |
391 | target.requestedSymbolName = symbolName; | |
392 | return true; | |
393 | } | |
394 | } | |
395 | if ( !missingWeakDylib ) | |
396 | diag.error("re-export ordinal %lld out of range for %s", libOrdinal, symbolName); | |
397 | return false; | |
398 | } | |
399 | target.kind = FixupTarget::Kind::bindToImage; | |
400 | target.requestedSymbolName = symbolName; | |
401 | target.foundSymbolName = symbolName; | |
402 | target.foundInImage = *this; | |
403 | target.isWeakDef = false; | |
404 | target.addend = addend; | |
405 | uint64_t trieValue = ma->read_uleb128(diag, p, exportsTrie.end); | |
406 | switch ( flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) { | |
407 | case EXPORT_SYMBOL_FLAGS_KIND_REGULAR: | |
408 | target.offsetInImage = trieValue + addend; | |
409 | if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) { | |
410 | // for now, just return address of resolver helper stub | |
411 | // FIXME handle running resolver | |
412 | (void)this->_mh->read_uleb128(diag, p, exportsTrie.end); | |
413 | } | |
414 | if ( flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ) | |
415 | target.isWeakDef = true; | |
416 | break; | |
417 | case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: | |
418 | // no type checking that client expected TLV yet | |
419 | target.offsetInImage = trieValue; | |
420 | break; | |
421 | case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: | |
422 | target.kind = FixupTarget::Kind::bindAbsolute; | |
423 | target.offsetInImage = trieValue + addend; | |
424 | break; | |
425 | default: | |
426 | diag.error("unsupported exported symbol kind. flags=%llu at node offset=0x%0lX", flags, (long)(node-exportsTrie.start)); | |
427 | return false; | |
428 | } | |
429 | return true; | |
430 | } | |
431 | } | |
432 | else { | |
433 | ma->forEachGlobalSymbol(diag, ^(const char* n_name, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop) { | |
434 | if ( strcmp(n_name, symbolName) == 0 ) { | |
435 | target.kind = FixupTarget::Kind::bindToImage; | |
436 | target.foundSymbolName = symbolName; | |
437 | target.requestedSymbolName = symbolName; | |
438 | target.foundInImage = *this; | |
439 | target.offsetInImage = n_value - ma->preferredLoadAddress() + addend; | |
440 | target.addend = addend; | |
441 | stop = true; | |
442 | } | |
443 | }); | |
444 | if ( target.foundSymbolName ) | |
445 | return true; | |
446 | } | |
447 | ||
448 | // symbol not exported from this image | |
449 | // if this is a dylib and has re-exported dylibs, search those too | |
450 | if ( (ma->filetype == MH_DYLIB) && ((ma->flags & MH_NO_REEXPORTED_DYLIBS) == 0) ) { | |
451 | __block unsigned depIndex = 0; | |
452 | ma->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) { | |
453 | if ( isReExport ) { | |
454 | bool missingWeakDylib; | |
455 | WrappedMachO child; | |
456 | if ( this->dependent(depIndex, child, missingWeakDylib) && !missingWeakDylib ) { | |
457 | if ( child.findSymbolIn(diag, symbolName, addend, target) ) | |
458 | stop = true; | |
459 | } | |
460 | } | |
461 | ++depIndex; | |
462 | }); | |
463 | } | |
464 | ||
465 | return (target.foundSymbolName != nullptr); | |
466 | } | |
467 | ||
468 | ||
469 | MachOAnalyzerSet::ExportsTrie MachOAnalyzerSet::wmo_getExportsTrie(const WrappedMachO* wmo) const | |
470 | { | |
471 | const uint8_t* start = nullptr; | |
472 | const uint8_t* end = nullptr; | |
473 | uint32_t runtimeOffset; | |
474 | uint32_t size; | |
475 | if ( wmo->_mh->hasExportTrie(runtimeOffset, size) ) { | |
476 | start = (uint8_t*)wmo->_mh + runtimeOffset; | |
477 | end = start + size; | |
478 | } | |
479 | return { start, end }; | |
480 | } | |
481 | ||
482 | ||
483 | // scan all weak-def images in load order | |
484 | // return first non-weak defintion found | |
485 | // otherwise first weak definition found | |
486 | bool MachOAnalyzerSet::mas_fromImageWeakDefLookup(const WrappedMachO& fromWmo, const char* symbolName, uint64_t addend, CachePatchHandler patcher, FixupTarget& target) const | |
487 | { | |
488 | // walk all images in load order, looking only at ones with weak-defs | |
489 | const DyldSharedCache* dyldCache = (DyldSharedCache*)mas_dyldCache(); | |
490 | __block bool foundImpl = false; | |
491 | this->mas_forEachImage(^(const WrappedMachO& anImage, bool hidden, bool& stop) { | |
492 | if ( !anImage._mh->hasWeakDefs() ) | |
493 | return; | |
494 | // when an image is hidden (RTLD_LOCAL) it can still look up symbols in itself | |
495 | if ( hidden && (fromWmo._mh != anImage._mh) ) | |
496 | return; | |
497 | FixupTarget tempTarget; | |
498 | Diagnostics diag; | |
499 | if ( anImage.findSymbolIn(diag, symbolName, addend, tempTarget) ) { | |
500 | // ignore symbol re-exports, we will find the real definition later in forEachImage() | |
501 | if ( anImage._mh != tempTarget.foundInImage._mh ) | |
502 | return; | |
503 | if ( foundImpl && anImage._mh->inDyldCache() && (anImage._mh != target.foundInImage._mh) ) { | |
504 | // we have already found the target, but now we see something in the dyld cache | |
505 | // that also implements this symbol, so we need to change all caches uses of that | |
506 | // to use the found one instead | |
507 | uint32_t cachedDylibIndex = 0; | |
508 | if ( dyldCache->findMachHeaderImageIndex(anImage._mh, cachedDylibIndex) ) { | |
509 | uintptr_t exportCacheOffset = (uint8_t*)tempTarget.foundInImage._mh + tempTarget.offsetInImage - (uint8_t*)dyldCache; | |
510 | patcher(cachedDylibIndex, (uint32_t)exportCacheOffset, target); | |
511 | } | |
512 | } | |
513 | if ( !foundImpl ) { | |
514 | // this is first found, so copy this to result | |
515 | target = tempTarget; | |
516 | foundImpl = true; | |
517 | } | |
518 | else if ( target.isWeakDef && !tempTarget.isWeakDef ) { | |
519 | // we found a non-weak impl later on, switch to it | |
520 | target = tempTarget; | |
521 | } | |
522 | } | |
523 | }); | |
524 | return foundImpl; | |
525 | } | |
526 | ||
527 | ||
528 | } // dyld3 | |
529 | ||
530 |