1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
3 * Copyright (c) 2014 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
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
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.
22 * @APPLE_LICENSE_HEADER_END@
25 #include "mega-dylib-utils.h"
26 #include "MachOFileAbstraction.hpp"
31 #include <sys/errno.h>
32 #include <sys/fcntl.h>
33 #include <mach-o/loader.h>
34 #include <mach-o/fat.h>
41 #include <unordered_map>
42 #include <unordered_set>
44 #include "dyld_cache_config.h"
46 #if !NEW_CACHE_FILE_FORMAT
47 #include "CacheFileAbstraction.hpp"
50 #ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
51 #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
60 BindInfo(void* cacheBuffer
, macho_header
<P
>* mh
);
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
);
66 static void bindAllImagesInCache(void* cacheBuffer
, const std::unordered_map
<std::string
, void*>& dylibPathToMachHeader
, std::vector
<void*>& pointersForASLR
);
68 void addExportsToGlobalMap(std::unordered_map
<std::string
, BindInfo
<P
>*>& reverseMap
);
71 typedef typename
P::uint_t pint_t
;
72 typedef typename
P::E E
;
77 bool isResolver
= false;
78 bool isAbsolute
= false;
79 bool isSymbolReExport
= false;
80 bool isThreadLocal
= false;
81 int reExportDylibIndex
= 0;
82 std::string reExportName
;
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
);
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
);
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
);
99 typedef std::unordered_map
<std::string
, std::unordered_set
<BindInfo
<P
>*>> ResolverClientsMap
;
100 typedef std::unordered_map
<std::string
, pint_t
> ResolverToBlessedLazyPointerMap
;
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
;
118 ResolverClientsMap _resolverClients
;
119 ResolverToBlessedLazyPointerMap _resolverBlessedMap
;
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)
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()) {
136 dylibCmd
= (macho_dylib_command
<P
>*)cmd
;
137 _installName
= dylibCmd
->name();
140 _symTabCmd
= (macho_symtab_command
<P
>*)cmd
;
143 _dynSymTabCmd
= (macho_dysymtab_command
<P
>*)cmd
;
146 case LC_DYLD_INFO_ONLY
:
147 _dyldInfo
= (macho_dyld_info_command
<P
>*)cmd
;
149 case LC_REEXPORT_DYLIB
:
150 dylibCmd
= (macho_dylib_command
<P
>*)cmd
;
151 _dependentPaths
.push_back(dylibCmd
->name());
152 _reExportedDylibNames
.push_back(dylibCmd
->name());
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());
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());
166 _baseAddress
= (pint_t
)segCmd
->vmaddr();
170 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
173 // if no export info, no _exports map to build
174 if ( _dyldInfo
->export_size() == 0 )
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
);
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;
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
;
199 case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL
:
200 _exports
[entry
.name
].isThreadLocal
= true;
202 case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
:
203 _exports
[entry
.name
].isAbsolute
= true;
204 _exports
[entry
.name
].address
= (pint_t
)entry
.info
.address
;
207 terminate("non-regular symbol binding not supported for %s in %s", entry
.name
.c_str(), _installName
);
214 template <typename P
>
215 void BindInfo
<P
>::bind(const std::unordered_map
<std::string
, BindInfo
<P
>*>& dylibPathToBindInfo
, std::vector
<void*>& pointersForASLR
)
217 bindImmediates(dylibPathToBindInfo
, pointersForASLR
);
218 bindLazyPointers(dylibPathToBindInfo
, pointersForASLR
);
219 // weak bind info is processed at launch time
223 template <typename P
>
224 void BindInfo
<P
>::setReExports(const std::unordered_map
<std::string
, BindInfo
<P
>*>& dylibPathToBindInfo
)
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
);
231 _reExportedDylibs
.push_back(pos
->second
);
235 template <typename P
>
236 void BindInfo
<P
>::setDependentDylibs(const std::unordered_map
<std::string
, BindInfo
<P
>*>& dylibPathToBindInfo
)
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
);
243 _dependentDylibs
.push_back(pos
->second
);
248 template <typename P
>
249 void BindInfo
<P
>::bindImmediates(const std::unordered_map
<std::string
, BindInfo
<P
>*>& dylibPathToBindInfo
, std::vector
<void*>& pointersForASLR
)
251 const uint8_t* p
= &_linkeditBias
[_dyldInfo
->bind_off()];
252 const uint8_t* end
= &p
[_dyldInfo
->bind_size()];
255 uint64_t segmentOffset
= 0;
256 uint8_t segmentIndex
= 0;
257 const char* symbolName
= NULL
;
258 int libraryOrdinal
= 0;
262 bool weakImport
= false;
264 while ( !done
&& (p
< end
) ) {
265 uint8_t immediate
= *p
& BIND_IMMEDIATE_MASK
;
266 uint8_t opcode
= *p
& BIND_OPCODE_MASK
;
269 case BIND_OPCODE_DONE
:
272 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
:
273 libraryOrdinal
= immediate
;
275 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
:
276 libraryOrdinal
= (int)read_uleb128(p
, end
);
278 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM
:
279 // the special ordinals are negative numbers
280 if ( immediate
== 0 )
283 int8_t signExtended
= BIND_OPCODE_MASK
| immediate
;
284 libraryOrdinal
= signExtended
;
287 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
:
288 weakImport
= ( (immediate
& BIND_SYMBOL_FLAGS_WEAK_IMPORT
) != 0 );
289 symbolName
= (char*)p
;
294 case BIND_OPCODE_SET_TYPE_IMM
:
297 case BIND_OPCODE_SET_ADDEND_SLEB
:
298 addend
= read_sleb128(p
, end
);
300 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
301 segmentIndex
= immediate
;
302 segmentOffset
= read_uleb128(p
, end
);
304 case BIND_OPCODE_ADD_ADDR_ULEB
:
305 segmentOffset
+= read_uleb128(p
, end
);
307 case BIND_OPCODE_DO_BIND
:
308 bindLocation(segmentIndex
, segmentOffset
, type
, libraryOrdinal
, addend
, symbolName
, false, weakImport
, dylibPathToBindInfo
, pointersForASLR
);
309 segmentOffset
+= sizeof(pint_t
);
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
);
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
);
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
);
328 terminate("bad bind opcode 0x%02X in %s", *p
, _installName
);
334 template <typename P
>
335 void BindInfo
<P
>::bindLazyPointers(const std::unordered_map
<std::string
, BindInfo
<P
>*>& dylibPathToBindInfo
, std::vector
<void*>& pointersForASLR
)
337 const uint8_t* p
= &_linkeditBias
[_dyldInfo
->lazy_bind_off()];
338 const uint8_t* end
= &p
[_dyldInfo
->lazy_bind_size()];
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;
346 bool weakImport
= false;
348 uint8_t immediate
= *p
& BIND_IMMEDIATE_MASK
;
349 uint8_t opcode
= *p
& BIND_OPCODE_MASK
;
352 case BIND_OPCODE_DONE
:
353 // this opcode marks the end of each lazy pointer binding
355 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
:
356 libraryOrdinal
= immediate
;
358 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
:
359 libraryOrdinal
= (int)read_uleb128(p
, end
);
361 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM
:
362 // the special ordinals are negative numbers
363 if ( immediate
== 0 )
366 int8_t signExtended
= BIND_OPCODE_MASK
| immediate
;
367 libraryOrdinal
= signExtended
;
370 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
:
371 weakImport
= ( (immediate
& BIND_SYMBOL_FLAGS_WEAK_IMPORT
) != 0 );
372 symbolName
= (char*)p
;
377 case BIND_OPCODE_SET_ADDEND_SLEB
:
378 addend
= read_sleb128(p
, end
);
380 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
381 segmentIndex
= immediate
;
382 segmentOffset
= read_uleb128(p
, end
);
384 case BIND_OPCODE_DO_BIND
:
385 bindLocation(segmentIndex
, segmentOffset
, type
, libraryOrdinal
, addend
, symbolName
, true, weakImport
, dylibPathToBindInfo
, pointersForASLR
);
386 segmentOffset
+= sizeof(pint_t
);
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
:
394 terminate("bad lazy bind opcode 0x%02X in %s", opcode
, _installName
);
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
)
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
);
410 if ( segmentOffset
> _segSizes
[segmentIndex
] )
411 terminate("bad segment offset in bind info in %s", _installName
);
413 BindInfo
<P
>* targetBinder
= nullptr;
415 switch ( libraryOrdinal
) {
416 case BIND_SPECIAL_DYLIB_FLAT_LOOKUP
:
417 terminate("dynamic lookup linkage not allowed in dyld shared cache in %s", _installName
);
420 case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE
:
421 terminate("linkage to main executable not allowed in dyld shared cache in %s", _installName
);
424 case BIND_SPECIAL_DYLIB_SELF
:
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
;
440 pint_t targetSymbolAddress
;
441 bool isResolverSymbol
= false;
442 bool isAbsolute
= false;
443 BindInfo
<P
>* foundIn
;
444 if ( weakImport
&& (targetBinder
== nullptr) ) {
445 targetSymbolAddress
= 0;
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
);
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
);
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
);
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
);
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
);
485 terminate("bad bind type (%d) in %s", type
, _installName
);
488 pointersForASLR
.push_back(mappedAddr
);
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
)
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
);
509 verboseLog("findExportedSymbolAddress(%s) => ???\n", symbolName
);
512 *address
= pos
->second
.address
;
514 *isResolverSymbol
= pos
->second
.isResolver
;
515 *isAbsolute
= pos
->second
.isAbsolute
;
516 //verboseLog("findExportedSymbolAddress(%s) => 0x0%llX\n", symbolName, (uint64_t)*address);
520 for (BindInfo
<P
>* dep
: _reExportedDylibs
) {
521 if ( dep
->findExportedSymbolAddress(symbolName
, dylibPathToBindInfo
, address
, foundIn
, isResolverSymbol
, isAbsolute
) )
527 template <typename P
>
528 void BindInfo
<P
>::addExportsToGlobalMap(std::unordered_map
<std::string
, BindInfo
<P
>*>& reverseMap
)
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;
537 BindInfo
<P
>* other
= pos
->second
;
538 if ( expEntry
.second
.isSymbolReExport
)
540 if ( other
->_exports
[symName
].isSymbolReExport
)
542 //warning("symbol '%s' exported from %s and %s\n", symName.c_str(), this->_installName, other->_installName);
547 template <typename P
>
548 typename
P::uint_t BindInfo
<P
>::findBlessedLazyPointerFor(const std::string
& resolverSymbolName
)
550 static const bool log
= false;
552 // check if this has already been looked up
553 auto pos1
= _resolverBlessedMap
.find(resolverSymbolName
);
554 if ( pos1
!= _resolverBlessedMap
.end() ) {
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
);
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
);
575 _resolverBlessedMap
[resolverSymbolName
] = lp
;
580 if ( info
.isResolver
)
581 thisDylibImplementsResolver
= true;
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()]);
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
= §ionsStart
[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
:
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
;
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
);
625 _resolverBlessedMap
[resolverSymbolName
] = result
;
630 if ( log
) verboseLog( "NOT found shared lazy pointer for %s in %s", resolverSymbolName
.c_str(), _installName
);
634 template <typename P
>
635 void BindInfo
<P
>::switchStubToUseSharedLazyPointer(const std::string
& resolverSymbolName
, pint_t lpVMAddr
)
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
= §ionsStart
[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
:
661 const macho_nlist
<P
>* aSymbol
= &symbolTable
[symbolIndex
];
662 const char* stubName
= &symbolStringPool
[aSymbol
->n_strx()];
663 if ( resolverSymbolName
== stubName
) {
664 switch (_mh
->cputype()) {
666 switchArmStubsLazyPointer(stubMappedAddr
, stubVMAddr
, stubSize
, lpVMAddr
);
669 //warning("shared resolver lazy pointer to %s not implemented for this arch", resolverSymbolName.c_str());
682 template <typename P
>
683 void BindInfo
<P
>::switchArmStubsLazyPointer(uint8_t* stubMappedAddress
, pint_t stubVMAddress
, uint32_t stubSize
, pint_t lpVMAddr
)
685 if ( stubSize
!= 16 ) {
686 warning("could not optimize ARM stub to resolver function in %s because it is wrong size\n", _installName
);
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)
694 warning("could not optimize ARM stub to resolver function in %s because instructions are not as expected", _installName
);
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
);
704 template <typename P
>
705 void BindInfo
<P
>::bindAllImagesInCache(void* cacheBuffer
, const std::unordered_map
<std::string
, void*>& dylibPathToMachHeader
, std::vector
<void*>& pointersForASLR
)
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
];
717 // chain re-exported dylibs
718 for (const auto& entry
: headersToBindInfo
) {
719 entry
.second
->setDependentDylibs(dylibPathToBindInfo
);
720 entry
.second
->setReExports(dylibPathToBindInfo
);
724 for (const auto& entry
: headersToBindInfo
) {
725 entry
.second
->bind(dylibPathToBindInfo
, pointersForASLR
);
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
);
735 for (const auto& entry
: headersToBindInfo
) {
741 } // anonymous namespace
744 void SharedCache::bindAllImagesInCache(const std::unordered_map
<std::string
, void*>& dylibPathToMachHeader
, std::vector
<void*>& pointersForASLR
)
746 switch ( _arch
.arch
) {
749 BindInfo
<Pointer32
<LittleEndian
>>::bindAllImagesInCache(_buffer
.get(), dylibPathToMachHeader
, pointersForASLR
);
751 case CPU_TYPE_X86_64
:
753 BindInfo
<Pointer64
<LittleEndian
>>::bindAllImagesInCache(_buffer
.get(), dylibPathToMachHeader
, pointersForASLR
);
756 terminate("unsupported arch 0x%08X", _arch
.arch
);