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 #include "MachOProxy.h"
48 #if !NEW_CACHE_FILE_FORMAT
49 #include "CacheFileAbstraction.hpp"
52 #ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
53 #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
62 BindInfo(void* cacheBuffer
, const std::map
<const MachOProxy
*, std::vector
<SharedCache::SegmentInfo
>>& segmentMap
, macho_header
<P
>* mh
, MachOProxy
* proxy
);
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
);
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
);
71 typedef typename
P::uint_t pint_t
;
72 typedef typename
P::E E
;
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
);
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
);
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
);
88 typedef std::unordered_map
<std::string
, std::unordered_set
<BindInfo
<P
>*>> ResolverClientsMap
;
89 typedef std::unordered_map
<std::string
, pint_t
> ResolverToBlessedLazyPointerMap
;
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
;
108 ResolverClientsMap _resolverClients
;
109 ResolverToBlessedLazyPointerMap _resolverBlessedMap
;
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
)
116 , _segmentMap(segmentMap
)
118 , _linkeditBias((uint8_t*)cacheBuffer
)
119 , _symTabCmd(nullptr)
120 , _dynSymTabCmd(nullptr)
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()) {
133 dylibCmd
= (macho_dylib_command
<P
>*)cmd
;
134 _installName
= dylibCmd
->name();
137 _symTabCmd
= (macho_symtab_command
<P
>*)cmd
;
140 _dynSymTabCmd
= (macho_dysymtab_command
<P
>*)cmd
;
143 case LC_DYLD_INFO_ONLY
:
144 _dyldInfo
= (macho_dyld_info_command
<P
>*)cmd
;
146 case LC_REEXPORT_DYLIB
:
147 dylibCmd
= (macho_dylib_command
<P
>*)cmd
;
148 _dependentPaths
.push_back(dylibCmd
->name());
149 _reExportedDylibNames
.push_back(dylibCmd
->name());
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());
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());
163 _baseAddress
= (pint_t
)segCmd
->vmaddr();
167 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
170 // if no export info, no _exports map to build
171 if ( _dyldInfo
->export_size() == 0 )
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
);
182 template <typename P
>
183 void BindInfo
<P
>::bind(const std::unordered_map
<std::string
, BindInfo
<P
>*>& dylibPathToBindInfo
, std::vector
<void*>& pointersForASLR
)
185 bindImmediates(dylibPathToBindInfo
, pointersForASLR
);
186 bindLazyPointers(dylibPathToBindInfo
, pointersForASLR
);
187 // weak bind info is processed at launch time
191 template <typename P
>
192 void BindInfo
<P
>::setReExports(const std::unordered_map
<std::string
, BindInfo
<P
>*>& dylibPathToBindInfo
)
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
);
199 _reExportedDylibs
.push_back(pos
->second
);
203 template <typename P
>
204 void BindInfo
<P
>::setDependentDylibs(const std::unordered_map
<std::string
, BindInfo
<P
>*>& dylibPathToBindInfo
)
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
);
211 _dependentDylibs
.push_back(pos
->second
);
216 template <typename P
>
217 void BindInfo
<P
>::bindImmediates(const std::unordered_map
<std::string
, BindInfo
<P
>*>& dylibPathToBindInfo
, std::vector
<void*>& pointersForASLR
)
219 const uint8_t* p
= &_linkeditBias
[_dyldInfo
->bind_off()];
220 const uint8_t* end
= &p
[_dyldInfo
->bind_size()];
223 uint64_t segmentOffset
= 0;
224 uint8_t segmentIndex
= 0;
225 const char* symbolName
= NULL
;
226 int libraryOrdinal
= 0;
230 bool weakImport
= false;
232 while ( !done
&& (p
< end
) ) {
233 uint8_t immediate
= *p
& BIND_IMMEDIATE_MASK
;
234 uint8_t opcode
= *p
& BIND_OPCODE_MASK
;
237 case BIND_OPCODE_DONE
:
240 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
:
241 libraryOrdinal
= immediate
;
243 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
:
244 libraryOrdinal
= (int)read_uleb128(p
, end
);
246 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM
:
247 // the special ordinals are negative numbers
248 if ( immediate
== 0 )
251 int8_t signExtended
= BIND_OPCODE_MASK
| immediate
;
252 libraryOrdinal
= signExtended
;
255 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
:
256 weakImport
= ( (immediate
& BIND_SYMBOL_FLAGS_WEAK_IMPORT
) != 0 );
257 symbolName
= (char*)p
;
262 case BIND_OPCODE_SET_TYPE_IMM
:
265 case BIND_OPCODE_SET_ADDEND_SLEB
:
266 addend
= read_sleb128(p
, end
);
268 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
269 segmentIndex
= immediate
;
270 segmentOffset
= read_uleb128(p
, end
);
272 case BIND_OPCODE_ADD_ADDR_ULEB
:
273 segmentOffset
+= read_uleb128(p
, end
);
275 case BIND_OPCODE_DO_BIND
:
276 bindLocation(segmentIndex
, segmentOffset
, type
, libraryOrdinal
, addend
, symbolName
, false, weakImport
, dylibPathToBindInfo
, pointersForASLR
);
277 segmentOffset
+= sizeof(pint_t
);
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
);
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
);
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
);
296 terminate("bad bind opcode 0x%02X in %s", *p
, _installName
);
302 template <typename P
>
303 void BindInfo
<P
>::bindLazyPointers(const std::unordered_map
<std::string
, BindInfo
<P
>*>& dylibPathToBindInfo
, std::vector
<void*>& pointersForASLR
)
305 const uint8_t* p
= &_linkeditBias
[_dyldInfo
->lazy_bind_off()];
306 const uint8_t* end
= &p
[_dyldInfo
->lazy_bind_size()];
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;
314 bool weakImport
= false;
316 uint8_t immediate
= *p
& BIND_IMMEDIATE_MASK
;
317 uint8_t opcode
= *p
& BIND_OPCODE_MASK
;
320 case BIND_OPCODE_DONE
:
321 // this opcode marks the end of each lazy pointer binding
323 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
:
324 libraryOrdinal
= immediate
;
326 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
:
327 libraryOrdinal
= (int)read_uleb128(p
, end
);
329 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM
:
330 // the special ordinals are negative numbers
331 if ( immediate
== 0 )
334 int8_t signExtended
= BIND_OPCODE_MASK
| immediate
;
335 libraryOrdinal
= signExtended
;
338 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
:
339 weakImport
= ( (immediate
& BIND_SYMBOL_FLAGS_WEAK_IMPORT
) != 0 );
340 symbolName
= (char*)p
;
345 case BIND_OPCODE_SET_ADDEND_SLEB
:
346 addend
= read_sleb128(p
, end
);
348 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
349 segmentIndex
= immediate
;
350 segmentOffset
= read_uleb128(p
, end
);
352 case BIND_OPCODE_DO_BIND
:
353 bindLocation(segmentIndex
, segmentOffset
, type
, libraryOrdinal
, addend
, symbolName
, true, weakImport
, dylibPathToBindInfo
, pointersForASLR
);
354 segmentOffset
+= sizeof(pint_t
);
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
:
362 terminate("bad lazy bind opcode 0x%02X in %s", opcode
, _installName
);
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
)
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
);
378 if ( segmentOffset
> _segSizes
[segmentIndex
] )
379 terminate("bad segment offset in bind info in %s", _installName
);
381 BindInfo
<P
>* targetBinder
= nullptr;
383 switch ( libraryOrdinal
) {
384 case BIND_SPECIAL_DYLIB_FLAT_LOOKUP
:
385 terminate("dynamic lookup linkage not allowed in dyld shared cache in %s", _installName
);
388 case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE
:
389 terminate("linkage to main executable not allowed in dyld shared cache in %s", _installName
);
392 case BIND_SPECIAL_DYLIB_SELF
:
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
;
408 pint_t targetSymbolAddress
;
409 bool isResolverSymbol
= false;
410 bool isAbsolute
= false;
411 BindInfo
<P
>* foundIn
;
412 if ( weakImport
&& (targetBinder
== nullptr) ) {
413 targetSymbolAddress
= 0;
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
);
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
);
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
);
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
);
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
);
452 terminate("bad bind type (%d) in %s", type
, _installName
);
455 pointersForASLR
.push_back(mappedAddr
);
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
)
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
);
476 verboseLog("findExportedSymbolAddress(%s) => ???\n", symbolName
);
480 *address
= (pint_t
)_proxy
->addressOf(symbolName
, _segmentMap
);
482 *isResolverSymbol
= info
->isResolver
;
483 *isAbsolute
= info
->isAbsolute
;
484 //verboseLog("findExportedSymbolAddress(%s) => 0x0%llX\n", symbolName, (uint64_t)*address);
488 for (BindInfo
<P
>* dep
: _reExportedDylibs
) {
489 if ( dep
->findExportedSymbolAddress(symbolName
, dylibPathToBindInfo
, address
, foundIn
, isResolverSymbol
, isAbsolute
) )
495 template <typename P
>
496 typename
P::uint_t BindInfo
<P
>::findBlessedLazyPointerFor(const std::string
& resolverSymbolName
)
498 static const bool log
= false;
500 // check if this has already been looked up
501 auto pos1
= _resolverBlessedMap
.find(resolverSymbolName
);
502 if ( pos1
!= _resolverBlessedMap
.end() ) {
506 // if this symbol is re-exported from another dylib, look there
507 bool thisDylibImplementsResolver
= false;
508 const auto info
= _proxy
->symbolInfo(resolverSymbolName
);
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
);
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
);
522 _resolverBlessedMap
[resolverSymbolName
] = lp
;
527 if (info
->isResolver
)
528 thisDylibImplementsResolver
= true;
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()]);
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
= §ionsStart
[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
:
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
;
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
);
572 _resolverBlessedMap
[resolverSymbolName
] = result
;
577 if ( log
) verboseLog( "NOT found shared lazy pointer for %s in %s", resolverSymbolName
.c_str(), _installName
);
581 template <typename P
>
582 void BindInfo
<P
>::switchStubToUseSharedLazyPointer(const std::string
& resolverSymbolName
, pint_t lpVMAddr
)
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
= §ionsStart
[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
:
608 const macho_nlist
<P
>* aSymbol
= &symbolTable
[symbolIndex
];
609 const char* stubName
= &symbolStringPool
[aSymbol
->n_strx()];
610 if ( resolverSymbolName
== stubName
) {
611 switch (_mh
->cputype()) {
613 switchArmStubsLazyPointer(stubMappedAddr
, stubVMAddr
, stubSize
, lpVMAddr
);
616 //warning("shared resolver lazy pointer to %s not implemented for this arch", resolverSymbolName.c_str());
629 template <typename P
>
630 void BindInfo
<P
>::switchArmStubsLazyPointer(uint8_t* stubMappedAddress
, pint_t stubVMAddress
, uint32_t stubSize
, pint_t lpVMAddr
)
632 if ( stubSize
!= 16 ) {
633 warning("could not optimize ARM stub to resolver function in %s because it is wrong size\n", _installName
);
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)
641 warning("could not optimize ARM stub to resolver function in %s because instructions are not as expected", _installName
);
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
);
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
)
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
);
662 dylibPathToBindInfo
[dylib
->installName
] = headersToBindInfo
[mh
];
663 for (const auto& alias
: dylib
->installNameAliases
) {
664 dylibPathToBindInfo
[alias
] = headersToBindInfo
[mh
];
668 // chain re-exported dylibs
669 for (const auto& entry
: headersToBindInfo
) {
670 entry
.second
->setDependentDylibs(dylibPathToBindInfo
);
671 entry
.second
->setReExports(dylibPathToBindInfo
);
675 for (const auto& entry
: headersToBindInfo
) {
676 entry
.second
->bind(dylibPathToBindInfo
, pointersForASLR
);
680 for (const auto& entry
: headersToBindInfo
) {
686 } // anonymous namespace
688 void SharedCache::bindAllImagesInCache(const std::vector
<MachOProxy
*> dylibs
, const std::map
<const MachOProxy
*, std::vector
<SegmentInfo
>>& segmentMap
, std::vector
<void*>& pointersForASLR
)
690 switch ( _arch
.arch
) {
693 BindInfo
<Pointer32
<LittleEndian
>>::bindAllImagesInCache(_buffer
.get(), dylibs
, segmentMap
, pointersForASLR
);
695 case CPU_TYPE_X86_64
:
697 BindInfo
<Pointer64
<LittleEndian
>>::bindAllImagesInCache(_buffer
.get(), dylibs
, segmentMap
, pointersForASLR
);
700 terminate("unsupported arch 0x%08X", _arch
.arch
);