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