]> git.saurik.com Git - apple/dyld.git/blob - interlinked-dylibs/BindAllImages.cpp
dyld-421.2.tar.gz
[apple/dyld.git] / interlinked-dylibs / BindAllImages.cpp
1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
2 *
3 * Copyright (c) 2014 Apple Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
12 * file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25 #include "mega-dylib-utils.h"
26 #include "MachOFileAbstraction.hpp"
27 #include "Trie.hpp"
28 #include "Logging.h"
29
30 #include <dirent.h>
31 #include <sys/errno.h>
32 #include <sys/fcntl.h>
33 #include <mach-o/loader.h>
34 #include <mach-o/fat.h>
35 #include <assert.h>
36
37 #include <fstream>
38 #include <iostream>
39 #include <string>
40 #include <algorithm>
41 #include <unordered_map>
42 #include <unordered_set>
43
44 #include "dyld_cache_config.h"
45
46 #if !NEW_CACHE_FILE_FORMAT
47 #include "CacheFileAbstraction.hpp"
48 #endif
49
50 #ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
51 #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
52 #endif
53
54
55 namespace {
56
57 template <typename P>
58 class BindInfo {
59 public:
60 BindInfo(void* cacheBuffer, macho_header<P>* mh);
61
62 void setReExports(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo);
63 void setDependentDylibs(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo);
64 void bind(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR);
65
66 static void bindAllImagesInCache(void* cacheBuffer, const std::unordered_map<std::string, void*>& dylibPathToMachHeader, std::vector<void*>& pointersForASLR);
67
68 void addExportsToGlobalMap(std::unordered_map<std::string, BindInfo<P>*>& reverseMap);
69
70 private:
71 typedef typename P::uint_t pint_t;
72 typedef typename P::E E;
73
74 struct SymbolInfo {
75 SymbolInfo() { }
76 pint_t address = 0;
77 bool isResolver = false;
78 bool isAbsolute = false;
79 bool isSymbolReExport = false;
80 bool isThreadLocal = false;
81 int reExportDylibIndex = 0;
82 std::string reExportName;
83 };
84
85 void bindImmediates(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR);
86 void bindLazyPointers(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR);
87
88 void bindLocation(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, int libraryOrdinal,
89 int64_t addend, const char* symbolName, bool lazyPointer, bool weakImport,
90 const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR);
91
92 bool findExportedSymbolAddress(const char* symbolName, const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo,
93 pint_t* address, BindInfo<P>** foundIn, bool* isResolverSymbol, bool* isAbsolute);
94 pint_t findBlessedLazyPointerFor(const std::string& resolverSymbolName);
95 void switchStubToUseSharedLazyPointer(const std::string& resolverSymbolName, pint_t lpVMAddr);
96 void switchArmStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr);
97 void switchArm64StubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr);
98
99 typedef std::unordered_map<std::string, std::unordered_set<BindInfo<P>*>> ResolverClientsMap;
100 typedef std::unordered_map<std::string, pint_t> ResolverToBlessedLazyPointerMap;
101
102 void* _cacheBuffer;
103 macho_header<P>* _mh;
104 const uint8_t* _linkeditBias;
105 const char* _installName;
106 const macho_symtab_command<P>* _symTabCmd;
107 const macho_dysymtab_command<P>* _dynSymTabCmd;
108 const macho_dyld_info_command<P>* _dyldInfo;
109 std::vector<std::string> _dependentPaths;
110 std::vector<uint64_t> _segSizes;
111 std::vector<uint64_t> _segCacheOffsets;
112 std::vector<const macho_segment_command<P>*>_segCmds;
113 std::unordered_map<std::string, SymbolInfo> _exports;
114 std::vector<std::string> _reExportedDylibNames;
115 std::vector<BindInfo<P>*> _reExportedDylibs;
116 std::vector<BindInfo<P>*> _dependentDylibs;
117 pint_t _baseAddress;
118 ResolverClientsMap _resolverClients;
119 ResolverToBlessedLazyPointerMap _resolverBlessedMap;
120 };
121
122
123 template <typename P>
124 BindInfo<P>::BindInfo(void* cacheBuffer, macho_header<P>* mh)
125 : _cacheBuffer(cacheBuffer), _mh(mh), _linkeditBias((uint8_t*)cacheBuffer), _symTabCmd(nullptr), _dynSymTabCmd(nullptr), _dyldInfo(nullptr), _baseAddress(0)
126 {
127 macho_segment_command<P>* segCmd;
128 macho_dylib_command<P>* dylibCmd;
129 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
130 const uint32_t cmd_count = mh->ncmds();
131 unsigned segIndex = 0;
132 const macho_load_command<P>* cmd = cmds;
133 for (uint32_t i = 0; i < cmd_count; ++i) {
134 switch (cmd->cmd()) {
135 case LC_ID_DYLIB:
136 dylibCmd = (macho_dylib_command<P>*)cmd;
137 _installName = dylibCmd->name();
138 break;
139 case LC_SYMTAB:
140 _symTabCmd = (macho_symtab_command<P>*)cmd;
141 break;
142 case LC_DYSYMTAB:
143 _dynSymTabCmd = (macho_dysymtab_command<P>*)cmd;
144 break;
145 case LC_DYLD_INFO:
146 case LC_DYLD_INFO_ONLY:
147 _dyldInfo = (macho_dyld_info_command<P>*)cmd;
148 break;
149 case LC_REEXPORT_DYLIB:
150 dylibCmd = (macho_dylib_command<P>*)cmd;
151 _dependentPaths.push_back(dylibCmd->name());
152 _reExportedDylibNames.push_back(dylibCmd->name());
153 break;
154 case LC_LOAD_DYLIB:
155 case LC_LOAD_WEAK_DYLIB:
156 case LC_LOAD_UPWARD_DYLIB:
157 dylibCmd = (macho_dylib_command<P>*)cmd;
158 _dependentPaths.push_back(dylibCmd->name());
159 break;
160 case macho_segment_command<P>::CMD:
161 segCmd = (macho_segment_command<P>*)cmd;
162 _segCmds.push_back(segCmd);
163 _segSizes.push_back(segCmd->vmsize());
164 _segCacheOffsets.push_back(segCmd->fileoff());
165 if ( segIndex == 0 )
166 _baseAddress = (pint_t)segCmd->vmaddr();
167 ++segIndex;
168 break;
169 }
170 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
171 }
172
173 // if no export info, no _exports map to build
174 if ( _dyldInfo->export_size() == 0 )
175 return;
176
177 std::vector<ExportInfoTrie::Entry> exports;
178 const uint8_t* exportsStart = &_linkeditBias[_dyldInfo->export_off()];
179 const uint8_t* exportsEnd = &exportsStart[_dyldInfo->export_size()];
180 if ( !ExportInfoTrie::parseTrie(exportsStart, exportsEnd, exports) ) {
181 terminate("malformed exports trie in %s", _installName);
182 }
183
184 for(const ExportInfoTrie::Entry& entry : exports) {
185 _exports[entry.name].address = (pint_t)entry.info.address + _baseAddress;
186 switch ( entry.info.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) {
187 case EXPORT_SYMBOL_FLAGS_KIND_REGULAR:
188 if ( (entry.info.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) {
189 _exports[entry.name].isResolver = true;
190 }
191 if ( entry.info.flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
192 SymbolInfo& info = _exports[entry.name];
193 info.isSymbolReExport = true;
194 info.reExportDylibIndex = (int)entry.info.other;
195 if ( !entry.info.importName.empty())
196 info.reExportName = entry.info.importName;
197 }
198 break;
199 case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL:
200 _exports[entry.name].isThreadLocal = true;
201 break;
202 case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE:
203 _exports[entry.name].isAbsolute = true;
204 _exports[entry.name].address = (pint_t)entry.info.address;
205 break;
206 default:
207 terminate("non-regular symbol binding not supported for %s in %s", entry.name.c_str(), _installName);
208 break;
209 }
210 }
211
212 }
213
214 template <typename P>
215 void BindInfo<P>::bind(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR)
216 {
217 bindImmediates(dylibPathToBindInfo, pointersForASLR);
218 bindLazyPointers(dylibPathToBindInfo, pointersForASLR);
219 // weak bind info is processed at launch time
220 }
221
222
223 template <typename P>
224 void BindInfo<P>::setReExports(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo)
225 {
226 for (const std::string& depName : _reExportedDylibNames) {
227 auto pos = dylibPathToBindInfo.find(depName);
228 if ( pos == dylibPathToBindInfo.end() ) {
229 terminate("can't find re-exported dylib '%s' needed by '%s'", depName.c_str(), _installName);
230 }
231 _reExportedDylibs.push_back(pos->second);
232 }
233 }
234
235 template <typename P>
236 void BindInfo<P>::setDependentDylibs(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo)
237 {
238 for (const std::string& depName : _dependentPaths) {
239 auto pos = dylibPathToBindInfo.find(depName);
240 if ( pos == dylibPathToBindInfo.end() ) {
241 terminate("can't find dependent dylib '%s' needed by '%s'", depName.c_str(), _installName);
242 }
243 _dependentDylibs.push_back(pos->second);
244 }
245 }
246
247
248 template <typename P>
249 void BindInfo<P>::bindImmediates(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR)
250 {
251 const uint8_t* p = &_linkeditBias[_dyldInfo->bind_off()];
252 const uint8_t* end = &p[_dyldInfo->bind_size()];
253
254 uint8_t type = 0;
255 uint64_t segmentOffset = 0;
256 uint8_t segmentIndex = 0;
257 const char* symbolName = NULL;
258 int libraryOrdinal = 0;
259 int64_t addend = 0;
260 uint64_t count;
261 uint64_t skip;
262 bool weakImport = false;
263 bool done = false;
264 while ( !done && (p < end) ) {
265 uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
266 uint8_t opcode = *p & BIND_OPCODE_MASK;
267 ++p;
268 switch (opcode) {
269 case BIND_OPCODE_DONE:
270 done = true;
271 break;
272 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
273 libraryOrdinal = immediate;
274 break;
275 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
276 libraryOrdinal = (int)read_uleb128(p, end);
277 break;
278 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
279 // the special ordinals are negative numbers
280 if ( immediate == 0 )
281 libraryOrdinal = 0;
282 else {
283 int8_t signExtended = BIND_OPCODE_MASK | immediate;
284 libraryOrdinal = signExtended;
285 }
286 break;
287 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
288 weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 );
289 symbolName = (char*)p;
290 while (*p != '\0')
291 ++p;
292 ++p;
293 break;
294 case BIND_OPCODE_SET_TYPE_IMM:
295 type = immediate;
296 break;
297 case BIND_OPCODE_SET_ADDEND_SLEB:
298 addend = read_sleb128(p, end);
299 break;
300 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
301 segmentIndex = immediate;
302 segmentOffset = read_uleb128(p, end);
303 break;
304 case BIND_OPCODE_ADD_ADDR_ULEB:
305 segmentOffset += read_uleb128(p, end);
306 break;
307 case BIND_OPCODE_DO_BIND:
308 bindLocation(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, dylibPathToBindInfo, pointersForASLR);
309 segmentOffset += sizeof(pint_t);
310 break;
311 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
312 bindLocation(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, dylibPathToBindInfo, pointersForASLR);
313 segmentOffset += read_uleb128(p, end) + sizeof(pint_t);
314 break;
315 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
316 bindLocation(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, dylibPathToBindInfo, pointersForASLR);
317 segmentOffset += immediate*sizeof(pint_t) + sizeof(pint_t);
318 break;
319 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
320 count = read_uleb128(p, end);
321 skip = read_uleb128(p, end);
322 for (uint32_t i=0; i < count; ++i) {
323 bindLocation(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, dylibPathToBindInfo, pointersForASLR);
324 segmentOffset += skip + sizeof(pint_t);
325 }
326 break;
327 default:
328 terminate("bad bind opcode 0x%02X in %s", *p, _installName);
329 }
330 }
331
332 }
333
334 template <typename P>
335 void BindInfo<P>::bindLazyPointers(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR)
336 {
337 const uint8_t* p = &_linkeditBias[_dyldInfo->lazy_bind_off()];
338 const uint8_t* end = &p[_dyldInfo->lazy_bind_size()];
339
340 uint8_t type = BIND_TYPE_POINTER;
341 uint64_t segmentOffset = 0;
342 uint8_t segmentIndex = 0;
343 const char* symbolName = NULL;
344 int libraryOrdinal = 0;
345 int64_t addend = 0;
346 bool weakImport = false;
347 while ( p < end ) {
348 uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
349 uint8_t opcode = *p & BIND_OPCODE_MASK;
350 ++p;
351 switch (opcode) {
352 case BIND_OPCODE_DONE:
353 // this opcode marks the end of each lazy pointer binding
354 break;
355 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
356 libraryOrdinal = immediate;
357 break;
358 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
359 libraryOrdinal = (int)read_uleb128(p, end);
360 break;
361 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
362 // the special ordinals are negative numbers
363 if ( immediate == 0 )
364 libraryOrdinal = 0;
365 else {
366 int8_t signExtended = BIND_OPCODE_MASK | immediate;
367 libraryOrdinal = signExtended;
368 }
369 break;
370 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
371 weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 );
372 symbolName = (char*)p;
373 while (*p != '\0')
374 ++p;
375 ++p;
376 break;
377 case BIND_OPCODE_SET_ADDEND_SLEB:
378 addend = read_sleb128(p, end);
379 break;
380 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
381 segmentIndex = immediate;
382 segmentOffset = read_uleb128(p, end);
383 break;
384 case BIND_OPCODE_DO_BIND:
385 bindLocation(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, true, weakImport, dylibPathToBindInfo, pointersForASLR);
386 segmentOffset += sizeof(pint_t);
387 break;
388 case BIND_OPCODE_SET_TYPE_IMM:
389 case BIND_OPCODE_ADD_ADDR_ULEB:
390 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
391 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
392 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
393 default:
394 terminate("bad lazy bind opcode 0x%02X in %s", opcode, _installName);
395 }
396 }
397
398 }
399
400
401 template <typename P>
402 void BindInfo<P>::bindLocation(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, int libraryOrdinal,
403 int64_t addend, const char* symbolName, bool lazyPointer, bool weakImport,
404 const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR)
405 {
406 //printf("bindLocation: seg=%d, segOffset=0x%08llX, type=%d, lib=%d, addend=%lld, symbol=%s\n", segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName);
407 if ( segmentIndex > _segSizes.size() )
408 terminate("bad segment index in bind info in %s", _installName);
409
410 if ( segmentOffset > _segSizes[segmentIndex] )
411 terminate("bad segment offset in bind info in %s", _installName);
412
413 BindInfo<P>* targetBinder = nullptr;
414 std::string depName;
415 switch ( libraryOrdinal ) {
416 case BIND_SPECIAL_DYLIB_FLAT_LOOKUP:
417 terminate("dynamic lookup linkage not allowed in dyld shared cache in %s", _installName);
418 break;
419
420 case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE:
421 terminate("linkage to main executable not allowed in dyld shared cache in %s", _installName);
422 break;
423
424 case BIND_SPECIAL_DYLIB_SELF:
425 targetBinder = this;
426 break;
427
428 default:
429 if ( libraryOrdinal < 0 )
430 terminate("bad mach-o binary, special library ordinal not allowd in dyld shared cache in %s", _installName);
431 if ( (unsigned)libraryOrdinal > _dependentPaths.size() )
432 terminate("bad mach-o binary, library ordinal too big in %s", _installName);
433 depName = _dependentPaths[libraryOrdinal-1];
434 auto pos = dylibPathToBindInfo.find(depName);
435 if ( pos != dylibPathToBindInfo.end() )
436 targetBinder = pos->second;
437 break;
438 }
439
440 pint_t targetSymbolAddress;
441 bool isResolverSymbol = false;
442 bool isAbsolute = false;
443 BindInfo<P>* foundIn;
444 if ( weakImport && (targetBinder == nullptr) ) {
445 targetSymbolAddress = 0;
446 foundIn = nullptr;
447 }
448 else {
449 if (targetBinder == nullptr)
450 terminate("could not bind symbol '%s' used in '%s' because installname '%s' not found", symbolName, _installName, depName.c_str());
451 if ( ! targetBinder->findExportedSymbolAddress(symbolName, dylibPathToBindInfo, &targetSymbolAddress, &foundIn, &isResolverSymbol, &isAbsolute) )
452 terminate("could not bind symbol '%s' used in: %s expected in: %s", symbolName, _installName, targetBinder->_installName);
453 }
454
455 //if ( isResolverSymbol )
456 // fprintf(stderr, "found resolver based symbol '%s' in %s\n", symbolName, targetBinder->_installName);
457 // don't bind lazy pointers to resolvers in shared cache
458 if ( lazyPointer && isResolverSymbol ) {
459 // instead find common lazy pointer that can be re-used by all clients
460 pint_t lpVMAddr = targetBinder->findBlessedLazyPointerFor(symbolName);
461 // switch stub to use shared lazy pointer to reduce dirty pages
462 this->switchStubToUseSharedLazyPointer(symbolName, lpVMAddr);
463 return;
464 }
465
466
467 // do actual update
468 uint8_t* mappedAddr = (uint8_t*)_cacheBuffer + _segCacheOffsets[segmentIndex] + segmentOffset;
469 pint_t* mappedAddrP = (pint_t*)mappedAddr;
470 pint_t newValue = (pint_t)(targetSymbolAddress + addend);
471 switch ( type ) {
472 case BIND_TYPE_POINTER:
473 // only write new value if it will change it
474 // this reduces pages dirtied
475 if ( P::getP(*mappedAddrP) != newValue )
476 P::setP(*mappedAddrP, newValue);
477 break;
478
479 case BIND_TYPE_TEXT_ABSOLUTE32:
480 case BIND_TYPE_TEXT_PCREL32:
481 terminate("text relocs not supported for shared cache binding in %s", _installName);
482 break;
483
484 default:
485 terminate("bad bind type (%d) in %s", type, _installName);
486 }
487 if ( !isAbsolute )
488 pointersForASLR.push_back(mappedAddr);
489 }
490
491
492 template <typename P>
493 bool BindInfo<P>::findExportedSymbolAddress(const char* symbolName, const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo,
494 pint_t* address, BindInfo<P>** foundIn, bool* isResolverSymbol, bool* isAbsolute)
495 {
496 auto pos = _exports.find(symbolName);
497 if ( pos != _exports.end() ) {
498 if ( pos->second.isSymbolReExport ) {
499 const char* importName = symbolName;
500 if ( !pos->second.reExportName.empty() )
501 importName = pos->second.reExportName.c_str();
502 std::string& depPath = _dependentPaths[pos->second.reExportDylibIndex-1];
503 auto pos2 = dylibPathToBindInfo.find(depPath);
504 if ( pos2 != dylibPathToBindInfo.end() ) {
505 BindInfo<P>* reExportFrom = pos2->second;
506 return reExportFrom->findExportedSymbolAddress(importName, dylibPathToBindInfo, address, foundIn, isResolverSymbol, isAbsolute);
507 }
508 else {
509 verboseLog("findExportedSymbolAddress(%s) => ???\n", symbolName);
510 }
511 }
512 *address = pos->second.address;
513 *foundIn = this;
514 *isResolverSymbol = pos->second.isResolver;
515 *isAbsolute = pos->second.isAbsolute;
516 //verboseLog("findExportedSymbolAddress(%s) => 0x0%llX\n", symbolName, (uint64_t)*address);
517 return true;
518 }
519
520 for (BindInfo<P>* dep : _reExportedDylibs) {
521 if ( dep->findExportedSymbolAddress(symbolName, dylibPathToBindInfo, address, foundIn, isResolverSymbol, isAbsolute) )
522 return true;
523 }
524 return false;
525 }
526
527 template <typename P>
528 void BindInfo<P>::addExportsToGlobalMap(std::unordered_map<std::string, BindInfo<P>*>& reverseMap)
529 {
530 for (const auto& expEntry : _exports) {
531 const std::string& symName = expEntry.first;
532 auto pos = reverseMap.find(symName);
533 if ( pos == reverseMap.end() ) {
534 reverseMap[symName] = this;
535 }
536 else {
537 BindInfo<P>* other = pos->second;
538 if ( expEntry.second.isSymbolReExport )
539 continue;
540 if ( other->_exports[symName].isSymbolReExport )
541 continue;
542 //warning("symbol '%s' exported from %s and %s\n", symName.c_str(), this->_installName, other->_installName);
543 }
544 }
545 }
546
547 template <typename P>
548 typename P::uint_t BindInfo<P>::findBlessedLazyPointerFor(const std::string& resolverSymbolName)
549 {
550 static const bool log = false;
551
552 // check if this has already been looked up
553 auto pos1 = _resolverBlessedMap.find(resolverSymbolName);
554 if ( pos1 != _resolverBlessedMap.end() ) {
555 return pos1->second;
556 }
557
558 // if this symbol is re-exported from another dylib, look there
559 bool thisDylibImplementsResolver = false;
560 auto pos = _exports.find(resolverSymbolName);
561 if ( pos != _exports.end() ) {
562 const SymbolInfo& info = pos->second;
563 if ( info.isSymbolReExport ) {
564 std::string reImportName = resolverSymbolName;
565 if ( !info.reExportName.empty() )
566 reImportName = info.reExportName;
567 if ( info.reExportDylibIndex > _dependentDylibs.size() ) {
568 warning("dylib index for re-exported symbol %s too large (%d) in %s", resolverSymbolName.c_str(), info.reExportDylibIndex, _installName);
569 }
570 else {
571 BindInfo<P>* reExportedFrom = _dependentDylibs[info.reExportDylibIndex-1];
572 if ( log ) verboseLog( "following re-export of %s in %s, to %s in %s", resolverSymbolName.c_str(), _installName, reImportName.c_str(), reExportedFrom->_installName);
573 pint_t lp = reExportedFrom->findBlessedLazyPointerFor(reImportName);
574 if ( lp != 0 ) {
575 _resolverBlessedMap[resolverSymbolName] = lp;
576 return lp;
577 }
578 }
579 }
580 if ( info.isResolver )
581 thisDylibImplementsResolver = true;
582 }
583
584 // lookup in lazy pointer section
585 if ( thisDylibImplementsResolver ) {
586 const uint32_t* const indirectTable = (uint32_t*)&_linkeditBias[_dynSymTabCmd->indirectsymoff()];
587 const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
588 const char* symbolStringPool = (char*)(&_linkeditBias[_symTabCmd->stroff()]);
589
590 for (const macho_segment_command<P>* seg : _segCmds) {
591 const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)seg + sizeof(macho_segment_command<P>));
592 const macho_section<P>* const sectionsEnd = &sectionsStart[seg->nsects()];
593 for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
594 uint8_t sectionType = sect->flags() & SECTION_TYPE;
595 if ( sectionType == S_LAZY_SYMBOL_POINTERS) {
596 uint32_t elementCount = (uint32_t)(sect->size() / sizeof(pint_t));
597 const uint32_t indirectTableOffset = sect->reserved1();
598 pint_t vmlocation = (pint_t)sect->addr();
599 for (uint32_t j=0; j < elementCount; ++j, vmlocation += sizeof(pint_t)) {
600 uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]);
601 switch ( symbolIndex ) {
602 case INDIRECT_SYMBOL_ABS:
603 case INDIRECT_SYMBOL_LOCAL:
604 break;
605 default:
606 const macho_nlist<P>* aSymbol = &symbolTable[symbolIndex];
607 const char* aName = &symbolStringPool[aSymbol->n_strx()];
608 if ( resolverSymbolName == aName) {
609 if ( log ) verboseLog("found shared lazy pointer at 0x%llX for %s in %s in %s", (uint64_t)vmlocation, aName, sect->sectname(), _installName);
610 _resolverBlessedMap[resolverSymbolName] = vmlocation;
611 return vmlocation;
612 }
613 break;
614 }
615 }
616 }
617 }
618 }
619 }
620
621 if ( log ) verboseLog( "not found shared lazy pointer for %s in %s, checking re-export dylibs", resolverSymbolName.c_str(), _installName);
622 for (BindInfo<P>* reExportedDylib : _reExportedDylibs ) {
623 pint_t result = reExportedDylib->findBlessedLazyPointerFor(resolverSymbolName);
624 if ( result != 0 ) {
625 _resolverBlessedMap[resolverSymbolName] = result;
626 return result;
627 }
628 }
629
630 if ( log ) verboseLog( "NOT found shared lazy pointer for %s in %s", resolverSymbolName.c_str(), _installName);
631 return 0;
632 }
633
634 template <typename P>
635 void BindInfo<P>::switchStubToUseSharedLazyPointer(const std::string& resolverSymbolName, pint_t lpVMAddr)
636 {
637 // find named stub
638 const uint32_t* const indirectTable = (uint32_t*)&_linkeditBias[_dynSymTabCmd->indirectsymoff()];
639 const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
640 const char* symbolStringPool = (char*)(&_linkeditBias[_symTabCmd->stroff()]);
641 for (const macho_segment_command<P>* seg : _segCmds) {
642 macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
643 macho_section<P>* const sectionsEnd = &sectionsStart[seg->nsects()];
644 for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
645 if ( ((sect->flags() & SECTION_TYPE) == S_SYMBOL_STUBS) && (sect->size() != 0) ) {
646 pint_t stubsVMStart = (pint_t)sect->addr();
647 uint8_t* stubsMappingStart = ((uint8_t*)_cacheBuffer) + sect->offset();
648 const uint32_t indirectTableOffset = sect->reserved1();
649 const uint32_t stubSize = sect->reserved2();
650 uint32_t elementCount = (uint32_t)(sect->size() / stubSize);
651 pint_t stubVMAddr = stubsVMStart;
652 uint8_t* stubMappedAddr = stubsMappingStart;
653 for (uint32_t j=0; j < elementCount; ++j, stubMappedAddr += stubSize, stubVMAddr += stubSize) {
654 uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]);
655 switch ( symbolIndex ) {
656 case INDIRECT_SYMBOL_ABS:
657 case INDIRECT_SYMBOL_LOCAL:
658 break;
659 default:
660 {
661 const macho_nlist<P>* aSymbol = &symbolTable[symbolIndex];
662 const char* stubName = &symbolStringPool[aSymbol->n_strx()];
663 if ( resolverSymbolName == stubName ) {
664 switch (_mh->cputype()) {
665 case CPU_TYPE_ARM:
666 switchArmStubsLazyPointer(stubMappedAddr, stubVMAddr, stubSize, lpVMAddr);
667 break;
668 default:
669 //warning("shared resolver lazy pointer to %s not implemented for this arch", resolverSymbolName.c_str());
670 break;
671 }
672 }
673 }
674 break;
675 }
676 }
677 }
678 }
679 }
680 }
681
682 template <typename P>
683 void BindInfo<P>::switchArmStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr)
684 {
685 if ( stubSize != 16 ) {
686 warning("could not optimize ARM stub to resolver function in %s because it is wrong size\n", _installName);
687 return;
688 }
689 uint32_t* instructions = (uint32_t*)stubMappedAddress;
690 if ( (E::get32(instructions[0]) != 0xe59fc004)
691 || (E::get32(instructions[1]) != 0xe08fc00c)
692 || (E::get32(instructions[2]) != 0xe59cf000)
693 ) {
694 warning("could not optimize ARM stub to resolver function in %s because instructions are not as expected", _installName);
695 return;
696 }
697 // last .long in stub is: lazyPtr - (stub+8)
698 // alter to point to more optimal lazy pointer
699 uint32_t betterOffset = (uint32_t)(lpVMAddr - (stubVMAddress + 12));
700 E::set32(instructions[3], betterOffset);
701 }
702
703
704 template <typename P>
705 void BindInfo<P>::bindAllImagesInCache(void* cacheBuffer, const std::unordered_map<std::string, void*>& dylibPathToMachHeader, std::vector<void*>& pointersForASLR)
706 {
707 // build BindInfo object for each dylib
708 std::unordered_map<macho_header<P>*, BindInfo<P>*> headersToBindInfo;
709 std::unordered_map<std::string, BindInfo<P>*> dylibPathToBindInfo;
710 for (const auto& entry: dylibPathToMachHeader) {
711 macho_header<P>* mh = (macho_header<P>*)entry.second;
712 if ( headersToBindInfo.count(mh) == 0 )
713 headersToBindInfo[mh] = new BindInfo<P>(cacheBuffer, mh);
714 dylibPathToBindInfo[entry.first] = headersToBindInfo[mh];
715 }
716
717 // chain re-exported dylibs
718 for (const auto& entry: headersToBindInfo) {
719 entry.second->setDependentDylibs(dylibPathToBindInfo);
720 entry.second->setReExports(dylibPathToBindInfo);
721 }
722
723 // bind each dylib
724 for (const auto& entry: headersToBindInfo) {
725 entry.second->bind(dylibPathToBindInfo, pointersForASLR);
726 }
727
728 // look for exported symbol collisions
729 std::unordered_map<std::string, BindInfo<P>*> reverseMap;
730 for (const auto& entry: headersToBindInfo) {
731 entry.second->addExportsToGlobalMap(reverseMap);
732 }
733
734 // clean up
735 for (const auto& entry: headersToBindInfo) {
736 delete entry.second;
737 }
738 }
739
740
741 } // anonymous namespace
742
743
744 void SharedCache::bindAllImagesInCache(const std::unordered_map<std::string, void*>& dylibPathToMachHeader, std::vector<void*>& pointersForASLR)
745 {
746 switch ( _arch.arch ) {
747 case CPU_TYPE_ARM:
748 case CPU_TYPE_I386:
749 BindInfo<Pointer32<LittleEndian>>::bindAllImagesInCache(_buffer.get(), dylibPathToMachHeader, pointersForASLR);
750 break;
751 case CPU_TYPE_X86_64:
752 case CPU_TYPE_ARM64:
753 BindInfo<Pointer64<LittleEndian>>::bindAllImagesInCache(_buffer.get(), dylibPathToMachHeader, pointersForASLR);
754 break;
755 default:
756 terminate("unsupported arch 0x%08X", _arch.arch);
757 }
758 }
759
760
761