2 * Copyright (c) 2017 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 #include <sys/types.h>
27 #include <sys/errno.h>
29 #include <mach/mach.h>
34 #include <mach-o/reloc.h>
35 #include <mach-o/nlist.h>
36 #include <CommonCrypto/CommonDigest.h>
40 #include "MachOLoaded.h"
41 #include "MachOFile.h"
42 #include "MachOFile.h"
43 #include "CodeSigningTypes.h"
46 #ifndef LC_BUILD_VERSION
47 #define LC_BUILD_VERSION 0x32 /* build for platform min OS version */
50 * The build_version_command contains the min OS version on which this
51 * binary was built to run for its platform. The list of known platforms and
52 * tool values following it.
54 struct build_version_command
{
55 uint32_t cmd
; /* LC_BUILD_VERSION */
56 uint32_t cmdsize
; /* sizeof(struct build_version_command) plus */
57 /* ntools * sizeof(struct build_tool_version) */
58 uint32_t platform
; /* platform */
59 uint32_t minos
; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
60 uint32_t sdk
; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
61 uint32_t ntools
; /* number of tool entries following this */
64 struct build_tool_version
{
65 uint32_t tool
; /* enum for the tool */
66 uint32_t version
; /* version number of the tool */
69 /* Known values for the platform field above. */
70 #define PLATFORM_MACOS 1
71 #define PLATFORM_IOS 2
72 #define PLATFORM_TVOS 3
73 #define PLATFORM_WATCHOS 4
74 #define PLATFORM_BRIDGEOS 5
76 /* Known values for the tool field above. */
87 void MachOLoaded::getLinkEditLoadCommands(Diagnostics
& diag
, LinkEditInfo
& result
) const
89 result
.dyldInfo
= nullptr;
90 result
.symTab
= nullptr;
91 result
.dynSymTab
= nullptr;
92 result
.splitSegInfo
= nullptr;
93 result
.functionStarts
= nullptr;
94 result
.dataInCode
= nullptr;
95 result
.codeSig
= nullptr;
96 __block
bool hasUUID
= false;
97 __block
bool hasMinVersion
= false;
98 __block
bool hasEncrypt
= false;
99 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
100 switch ( cmd
->cmd
) {
102 case LC_DYLD_INFO_ONLY
:
103 if ( cmd
->cmdsize
!= sizeof(dyld_info_command
) )
104 diag
.error("LC_DYLD_INFO load command size wrong");
105 else if ( result
.dyldInfo
!= nullptr )
106 diag
.error("multiple LC_DYLD_INFO load commands");
107 result
.dyldInfo
= (dyld_info_command
*)cmd
;
110 if ( cmd
->cmdsize
!= sizeof(symtab_command
) )
111 diag
.error("LC_SYMTAB load command size wrong");
112 else if ( result
.symTab
!= nullptr )
113 diag
.error("multiple LC_SYMTAB load commands");
114 result
.symTab
= (symtab_command
*)cmd
;
117 if ( cmd
->cmdsize
!= sizeof(dysymtab_command
) )
118 diag
.error("LC_DYSYMTAB load command size wrong");
119 else if ( result
.dynSymTab
!= nullptr )
120 diag
.error("multiple LC_DYSYMTAB load commands");
121 result
.dynSymTab
= (dysymtab_command
*)cmd
;
123 case LC_SEGMENT_SPLIT_INFO
:
124 if ( cmd
->cmdsize
!= sizeof(linkedit_data_command
) )
125 diag
.error("LC_SEGMENT_SPLIT_INFO load command size wrong");
126 else if ( result
.splitSegInfo
!= nullptr )
127 diag
.error("multiple LC_SEGMENT_SPLIT_INFO load commands");
128 result
.splitSegInfo
= (linkedit_data_command
*)cmd
;
130 case LC_FUNCTION_STARTS
:
131 if ( cmd
->cmdsize
!= sizeof(linkedit_data_command
) )
132 diag
.error("LC_FUNCTION_STARTS load command size wrong");
133 else if ( result
.functionStarts
!= nullptr )
134 diag
.error("multiple LC_FUNCTION_STARTS load commands");
135 result
.functionStarts
= (linkedit_data_command
*)cmd
;
137 case LC_DATA_IN_CODE
:
138 if ( cmd
->cmdsize
!= sizeof(linkedit_data_command
) )
139 diag
.error("LC_DATA_IN_CODE load command size wrong");
140 else if ( result
.dataInCode
!= nullptr )
141 diag
.error("multiple LC_DATA_IN_CODE load commands");
142 result
.dataInCode
= (linkedit_data_command
*)cmd
;
144 case LC_CODE_SIGNATURE
:
145 if ( cmd
->cmdsize
!= sizeof(linkedit_data_command
) )
146 diag
.error("LC_CODE_SIGNATURE load command size wrong");
147 else if ( result
.codeSig
!= nullptr )
148 diag
.error("multiple LC_CODE_SIGNATURE load commands");
149 result
.codeSig
= (linkedit_data_command
*)cmd
;
152 if ( cmd
->cmdsize
!= sizeof(uuid_command
) )
153 diag
.error("LC_UUID load command size wrong");
155 diag
.error("multiple LC_UUID load commands");
158 case LC_VERSION_MIN_IPHONEOS
:
159 case LC_VERSION_MIN_MACOSX
:
160 case LC_VERSION_MIN_TVOS
:
161 case LC_VERSION_MIN_WATCHOS
:
162 if ( cmd
->cmdsize
!= sizeof(version_min_command
) )
163 diag
.error("LC_VERSION_* load command size wrong");
164 else if ( hasMinVersion
)
165 diag
.error("multiple LC_VERSION_MIN_* load commands");
166 hasMinVersion
= true;
168 case LC_BUILD_VERSION
:
169 if ( cmd
->cmdsize
!= (sizeof(build_version_command
) + ((build_version_command
*)cmd
)->ntools
* sizeof(build_tool_version
)) )
170 diag
.error("LC_BUILD_VERSION load command size wrong");
171 else if ( hasMinVersion
)
172 diag
.error("LC_BUILD_VERSION cannot coexist LC_VERSION_MIN_* with load commands");
174 case LC_ENCRYPTION_INFO
:
175 if ( cmd
->cmdsize
!= sizeof(encryption_info_command
) )
176 diag
.error("LC_ENCRYPTION_INFO load command size wrong");
177 else if ( hasEncrypt
)
178 diag
.error("multiple LC_ENCRYPTION_INFO load commands");
180 diag
.error("LC_ENCRYPTION_INFO found in 64-bit mach-o");
183 case LC_ENCRYPTION_INFO_64
:
184 if ( cmd
->cmdsize
!= sizeof(encryption_info_command_64
) )
185 diag
.error("LC_ENCRYPTION_INFO_64 load command size wrong");
186 else if ( hasEncrypt
)
187 diag
.error("multiple LC_ENCRYPTION_INFO_64 load commands");
189 diag
.error("LC_ENCRYPTION_INFO_64 found in 32-bit mach-o");
194 if ( diag
.noError() && (result
.dynSymTab
!= nullptr) && (result
.symTab
== nullptr) )
195 diag
.error("LC_DYSYMTAB but no LC_SYMTAB load command");
198 void MachOLoaded::getLinkEditPointers(Diagnostics
& diag
, LinkEditInfo
& result
) const
200 getLinkEditLoadCommands(diag
, result
);
201 if ( diag
.noError() )
202 getLayoutInfo(result
.layout
);
205 void MachOLoaded::getLayoutInfo(LayoutInfo
& result
) const
207 forEachSegment(^(const SegmentInfo
& info
, bool& stop
) {
208 if ( strcmp(info
.segName
, "__TEXT") == 0 ) {
209 result
.textUnslidVMAddr
= (uintptr_t)info
.vmAddr
;
210 result
.slide
= (uintptr_t)(((uint64_t)this) - info
.vmAddr
);
212 else if ( strcmp(info
.segName
, "__LINKEDIT") == 0 ) {
213 result
.linkeditUnslidVMAddr
= (uintptr_t)info
.vmAddr
;
214 result
.linkeditFileOffset
= (uint32_t)info
.fileOffset
;
215 result
.linkeditFileSize
= (uint32_t)info
.fileSize
;
216 result
.linkeditSegIndex
= info
.segIndex
;
221 bool MachOLoaded::hasExportTrie(uint32_t& runtimeOffset
, uint32_t& size
) const
227 getLinkEditPointers(diag
, leInfo
);
228 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
229 if ( diag
.hasError() )
231 if ( leInfo
.dyldInfo
!= nullptr ) {
232 uint32_t offsetInLinkEdit
= leInfo
.dyldInfo
->export_off
- leInfo
.layout
.linkeditFileOffset
;
233 runtimeOffset
= offsetInLinkEdit
+ (uint32_t)(leInfo
.layout
.linkeditUnslidVMAddr
- leInfo
.layout
.textUnslidVMAddr
);
234 size
= leInfo
.dyldInfo
->export_size
;
242 // this is only used by dlsym() at runtime. All other binding is done when the closure is built.
243 bool MachOLoaded::hasExportedSymbol(const char* symbolName
, DependentToMachOLoaded finder
, void** result
,
244 bool* resultPointsToInstructions
) const
246 typedef void* (*ResolverFunc
)(void);
247 ResolverFunc resolver
;
249 FoundSymbol foundInfo
;
250 if ( findExportedSymbol(diag
, symbolName
, foundInfo
, finder
) ) {
251 switch ( foundInfo
.kind
) {
252 case FoundSymbol::Kind::headerOffset
: {
253 *result
= (uint8_t*)foundInfo
.foundInDylib
+ foundInfo
.value
;
254 *resultPointsToInstructions
= false;
255 int64_t slide
= foundInfo
.foundInDylib
->getSlide();
256 foundInfo
.foundInDylib
->forEachSection(^(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) {
257 uint64_t sectStartAddr
= sectInfo
.sectAddr
+ slide
;
258 uint64_t sectEndAddr
= sectStartAddr
+ sectInfo
.sectSize
;
259 if ( ((uint64_t)*result
>= sectStartAddr
) && ((uint64_t)*result
< sectEndAddr
) ) {
260 *resultPointsToInstructions
= (sectInfo
.sectFlags
& S_ATTR_PURE_INSTRUCTIONS
) || (sectInfo
.sectFlags
& S_ATTR_SOME_INSTRUCTIONS
);
266 case FoundSymbol::Kind::absolute
:
267 *result
= (void*)(long)foundInfo
.value
;
268 *resultPointsToInstructions
= false;
270 case FoundSymbol::Kind::resolverOffset
:
271 // foundInfo.value contains "stub".
272 // in dlsym() we want to call resolver function to get final function address
273 resolver
= (ResolverFunc
)((uint8_t*)foundInfo
.foundInDylib
+ foundInfo
.resolverFuncOffset
);
274 *result
= (*resolver
)();
275 // FIXME: Set this properly
276 *resultPointsToInstructions
= true;
283 #endif // BUILDING_LIBDYLD
285 bool MachOLoaded::findExportedSymbol(Diagnostics
& diag
, const char* symbolName
, FoundSymbol
& foundInfo
, DependentToMachOLoaded findDependent
) const
288 getLinkEditPointers(diag
, leInfo
);
289 if ( diag
.hasError() )
291 if ( leInfo
.dyldInfo
!= nullptr ) {
292 const uint8_t* trieStart
= getLinkEditContent(leInfo
.layout
, leInfo
.dyldInfo
->export_off
);
293 const uint8_t* trieEnd
= trieStart
+ leInfo
.dyldInfo
->export_size
;
294 const uint8_t* node
= trieWalk(diag
, trieStart
, trieEnd
, symbolName
);
295 if ( node
== nullptr ) {
296 // symbol not exported from this image. Seach any re-exported dylibs
297 __block
unsigned depIndex
= 0;
298 __block
bool foundInReExportedDylib
= false;
299 forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool& stop
) {
300 if ( isReExport
&& findDependent
) {
301 if ( const MachOLoaded
* depMH
= findDependent(this, depIndex
) ) {
302 if ( depMH
->findExportedSymbol(diag
, symbolName
, foundInfo
, findDependent
) ) {
304 foundInReExportedDylib
= true;
310 return foundInReExportedDylib
;
312 const uint8_t* p
= node
;
313 const uint64_t flags
= read_uleb128(diag
, p
, trieEnd
);
314 if ( flags
& EXPORT_SYMBOL_FLAGS_REEXPORT
) {
315 if ( !findDependent
)
317 // re-export from another dylib, lookup there
318 const uint64_t ordinal
= read_uleb128(diag
, p
, trieEnd
);
319 const char* importedName
= (char*)p
;
320 if ( importedName
[0] == '\0' )
321 importedName
= symbolName
;
322 if ( (ordinal
== 0) || (ordinal
> dependentDylibCount()) ) {
323 diag
.error("re-export ordinal %lld out of range for %s", ordinal
, symbolName
);
326 uint32_t depIndex
= (uint32_t)(ordinal
-1);
327 if ( const MachOLoaded
* depMH
= findDependent(this, depIndex
) ) {
328 return depMH
->findExportedSymbol(diag
, importedName
, foundInfo
, findDependent
);
331 diag
.error("dependent dylib %lld not found for re-exported symbol %s", ordinal
, symbolName
);
335 foundInfo
.kind
= FoundSymbol::Kind::headerOffset
;
336 foundInfo
.isThreadLocal
= false;
337 foundInfo
.isWeakDef
= false;
338 foundInfo
.foundInDylib
= this;
339 foundInfo
.value
= read_uleb128(diag
, p
, trieEnd
);
340 foundInfo
.resolverFuncOffset
= 0;
341 foundInfo
.foundSymbolName
= symbolName
;
342 if ( diag
.hasError() )
344 switch ( flags
& EXPORT_SYMBOL_FLAGS_KIND_MASK
) {
345 case EXPORT_SYMBOL_FLAGS_KIND_REGULAR
:
346 if ( flags
& EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER
) {
347 foundInfo
.kind
= FoundSymbol::Kind::headerOffset
;
348 foundInfo
.resolverFuncOffset
= (uint32_t)read_uleb128(diag
, p
, trieEnd
);
351 foundInfo
.kind
= FoundSymbol::Kind::headerOffset
;
353 if ( flags
& EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION
)
354 foundInfo
.isWeakDef
= true;
356 case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL
:
357 foundInfo
.isThreadLocal
= true;
359 case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
:
360 foundInfo
.kind
= FoundSymbol::Kind::absolute
;
363 diag
.error("unsupported exported symbol kind. flags=%llu at node offset=0x%0lX", flags
, (long)(node
-trieStart
));
369 // this is an old binary (before macOS 10.6), scan the symbol table
370 foundInfo
.foundInDylib
= nullptr;
371 forEachGlobalSymbol(diag
, ^(const char* aSymbolName
, uint64_t n_value
, uint8_t n_type
, uint8_t n_sect
, uint16_t n_desc
, bool& stop
) {
372 if ( strcmp(aSymbolName
, symbolName
) == 0 ) {
373 foundInfo
.kind
= FoundSymbol::Kind::headerOffset
;
374 foundInfo
.isThreadLocal
= false;
375 foundInfo
.foundInDylib
= this;
376 foundInfo
.value
= n_value
- leInfo
.layout
.textUnslidVMAddr
;
377 foundInfo
.resolverFuncOffset
= 0;
378 foundInfo
.foundSymbolName
= symbolName
;
382 if ( foundInfo
.foundInDylib
== nullptr ) {
383 // symbol not exported from this image. Search any re-exported dylibs
384 __block
unsigned depIndex
= 0;
385 forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool& stop
) {
386 if ( isReExport
&& findDependent
) {
387 if ( const MachOLoaded
* depMH
= findDependent(this, depIndex
) ) {
388 if ( depMH
->findExportedSymbol(diag
, symbolName
, foundInfo
, findDependent
) ) {
396 return (foundInfo
.foundInDylib
!= nullptr);
400 intptr_t MachOLoaded::getSlide() const
403 __block
intptr_t slide
= 0;
404 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
405 if ( cmd
->cmd
== LC_SEGMENT_64
) {
406 const segment_command_64
* seg
= (segment_command_64
*)cmd
;
407 if ( strcmp(seg
->segname
, "__TEXT") == 0 ) {
408 slide
= (uintptr_t)(((uint64_t)this) - seg
->vmaddr
);
412 else if ( cmd
->cmd
== LC_SEGMENT
) {
413 const segment_command
* seg
= (segment_command
*)cmd
;
414 if ( strcmp(seg
->segname
, "__TEXT") == 0 ) {
415 slide
= (uintptr_t)(((uint64_t)this) - seg
->vmaddr
);
420 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
424 const uint8_t* MachOLoaded::getLinkEditContent(const LayoutInfo
& info
, uint32_t fileOffset
) const
426 uint32_t offsetInLinkedit
= fileOffset
- info
.linkeditFileOffset
;
427 uintptr_t linkeditStartAddr
= info
.linkeditUnslidVMAddr
+ info
.slide
;
428 return (uint8_t*)(linkeditStartAddr
+ offsetInLinkedit
);
432 void MachOLoaded::forEachGlobalSymbol(Diagnostics
& diag
, void (^callback
)(const char* symbolName
, uint64_t n_value
, uint8_t n_type
, uint8_t n_sect
, uint16_t n_desc
, bool& stop
)) const
435 getLinkEditPointers(diag
, leInfo
);
436 if ( diag
.hasError() )
439 const bool is64Bit
= is64();
440 if ( leInfo
.symTab
!= nullptr ) {
441 uint32_t globalsStartIndex
= 0;
442 uint32_t globalsCount
= leInfo
.symTab
->nsyms
;
443 if ( leInfo
.dynSymTab
!= nullptr ) {
444 globalsStartIndex
= leInfo
.dynSymTab
->iextdefsym
;
445 globalsCount
= leInfo
.dynSymTab
->nextdefsym
;
447 uint32_t maxStringOffset
= leInfo
.symTab
->strsize
;
448 const char* stringPool
= (char*)getLinkEditContent(leInfo
.layout
, leInfo
.symTab
->stroff
);
449 const struct nlist
* symbols
= (struct nlist
*) (getLinkEditContent(leInfo
.layout
, leInfo
.symTab
->symoff
));
450 const struct nlist_64
* symbols64
= (struct nlist_64
*)symbols
;
452 for (uint32_t i
=0; (i
< globalsCount
) && !stop
; ++i
) {
454 const struct nlist_64
& sym
= symbols64
[globalsStartIndex
+i
];
455 if ( sym
.n_un
.n_strx
> maxStringOffset
)
457 if ( (sym
.n_type
& N_EXT
) && ((sym
.n_type
& N_TYPE
) == N_SECT
) && ((sym
.n_type
& N_STAB
) == 0) )
458 callback(&stringPool
[sym
.n_un
.n_strx
], sym
.n_value
, sym
.n_type
, sym
.n_sect
, sym
.n_desc
, stop
);
461 const struct nlist
& sym
= symbols
[globalsStartIndex
+i
];
462 if ( sym
.n_un
.n_strx
> maxStringOffset
)
464 if ( (sym
.n_type
& N_EXT
) && ((sym
.n_type
& N_TYPE
) == N_SECT
) && ((sym
.n_type
& N_STAB
) == 0) )
465 callback(&stringPool
[sym
.n_un
.n_strx
], sym
.n_value
, sym
.n_type
, sym
.n_sect
, sym
.n_desc
, stop
);
471 void MachOLoaded::forEachLocalSymbol(Diagnostics
& diag
, void (^callback
)(const char* symbolName
, uint64_t n_value
, uint8_t n_type
, uint8_t n_sect
, uint16_t n_desc
, bool& stop
)) const
474 getLinkEditPointers(diag
, leInfo
);
475 if ( diag
.hasError() )
478 const bool is64Bit
= is64();
479 if ( leInfo
.symTab
!= nullptr ) {
480 uint32_t localsStartIndex
= 0;
481 uint32_t localsCount
= leInfo
.symTab
->nsyms
;
482 if ( leInfo
.dynSymTab
!= nullptr ) {
483 localsStartIndex
= leInfo
.dynSymTab
->ilocalsym
;
484 localsCount
= leInfo
.dynSymTab
->nlocalsym
;
486 uint32_t maxStringOffset
= leInfo
.symTab
->strsize
;
487 const char* stringPool
= (char*)getLinkEditContent(leInfo
.layout
, leInfo
.symTab
->stroff
);
488 const struct nlist
* symbols
= (struct nlist
*) (getLinkEditContent(leInfo
.layout
, leInfo
.symTab
->symoff
));
489 const struct nlist_64
* symbols64
= (struct nlist_64
*)(getLinkEditContent(leInfo
.layout
, leInfo
.symTab
->symoff
));
491 for (uint32_t i
=0; (i
< localsCount
) && !stop
; ++i
) {
493 const struct nlist_64
& sym
= symbols64
[localsStartIndex
+i
];
494 if ( sym
.n_un
.n_strx
> maxStringOffset
)
496 if ( ((sym
.n_type
& N_EXT
) == 0) && ((sym
.n_type
& N_TYPE
) == N_SECT
) && ((sym
.n_type
& N_STAB
) == 0) )
497 callback(&stringPool
[sym
.n_un
.n_strx
], sym
.n_value
, sym
.n_type
, sym
.n_sect
, sym
.n_desc
, stop
);
500 const struct nlist
& sym
= symbols
[localsStartIndex
+i
];
501 if ( sym
.n_un
.n_strx
> maxStringOffset
)
503 if ( ((sym
.n_type
& N_EXT
) == 0) && ((sym
.n_type
& N_TYPE
) == N_SECT
) && ((sym
.n_type
& N_STAB
) == 0) )
504 callback(&stringPool
[sym
.n_un
.n_strx
], sym
.n_value
, sym
.n_type
, sym
.n_sect
, sym
.n_desc
, stop
);
510 uint32_t MachOLoaded::dependentDylibCount() const
512 __block
uint32_t count
= 0;
513 forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool& stop
) {
519 const char* MachOLoaded::dependentDylibLoadPath(uint32_t depIndex
) const
521 __block
const char* foundLoadPath
= nullptr;
522 __block
uint32_t curDepIndex
= 0;
523 forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool& stop
) {
524 if ( curDepIndex
== depIndex
) {
525 foundLoadPath
= loadPath
;
530 return foundLoadPath
;
533 const char* MachOLoaded::segmentName(uint32_t targetSegIndex
) const
535 __block
const char* result
= nullptr;
536 forEachSegment(^(const SegmentInfo
& info
, bool& stop
) {
537 if ( targetSegIndex
== info
.segIndex
) {
538 result
= info
.segName
;
545 bool MachOLoaded::findClosestFunctionStart(uint64_t address
, uint64_t* functionStartAddress
) const
549 getLinkEditPointers(diag
, leInfo
);
550 if ( diag
.hasError() )
552 if ( leInfo
.functionStarts
== nullptr )
555 const uint8_t* starts
= getLinkEditContent(leInfo
.layout
, leInfo
.functionStarts
->dataoff
);
556 const uint8_t* startsEnd
= starts
+ leInfo
.functionStarts
->datasize
;
558 uint64_t lastAddr
= (uint64_t)(long)this;
559 uint64_t runningAddr
= lastAddr
;
560 while (diag
.noError()) {
561 uint64_t value
= read_uleb128(diag
, starts
, startsEnd
);
564 lastAddr
= runningAddr
;
565 runningAddr
+= value
;
566 //fprintf(stderr, " addr=0x%08llX\n", runningAddr);
567 if ( runningAddr
> address
) {
568 *functionStartAddress
= lastAddr
;
576 bool MachOLoaded::findClosestSymbol(uint64_t address
, const char** symbolName
, uint64_t* symbolAddr
) const
580 getLinkEditPointers(diag
, leInfo
);
581 if ( diag
.hasError() )
583 if ( (leInfo
.symTab
== nullptr) || (leInfo
.dynSymTab
== nullptr) )
585 uint64_t targetUnslidAddress
= address
- leInfo
.layout
.slide
;
587 uint32_t maxStringOffset
= leInfo
.symTab
->strsize
;
588 const char* stringPool
= (char*)getLinkEditContent(leInfo
.layout
, leInfo
.symTab
->stroff
);
589 const struct nlist
* symbols
= (struct nlist
*) (getLinkEditContent(leInfo
.layout
, leInfo
.symTab
->symoff
));
591 const struct nlist_64
* symbols64
= (struct nlist_64
*)symbols
;
592 const struct nlist_64
* bestSymbol
= nullptr;
593 // first walk all global symbols
594 const struct nlist_64
* const globalsStart
= &symbols64
[leInfo
.dynSymTab
->iextdefsym
];
595 const struct nlist_64
* const globalsEnd
= &globalsStart
[leInfo
.dynSymTab
->nextdefsym
];
596 for (const struct nlist_64
* s
= globalsStart
; s
< globalsEnd
; ++s
) {
597 if ( (s
->n_type
& N_TYPE
) == N_SECT
) {
598 if ( bestSymbol
== nullptr ) {
599 if ( s
->n_value
<= targetUnslidAddress
)
602 else if ( (s
->n_value
<= targetUnslidAddress
) && (bestSymbol
->n_value
< s
->n_value
) ) {
607 // next walk all local symbols
608 const struct nlist_64
* const localsStart
= &symbols64
[leInfo
.dynSymTab
->ilocalsym
];
609 const struct nlist_64
* const localsEnd
= &localsStart
[leInfo
.dynSymTab
->nlocalsym
];
610 for (const struct nlist_64
* s
= localsStart
; s
< localsEnd
; ++s
) {
611 if ( ((s
->n_type
& N_TYPE
) == N_SECT
) && ((s
->n_type
& N_STAB
) == 0) ) {
612 if ( bestSymbol
== nullptr ) {
613 if ( s
->n_value
<= targetUnslidAddress
)
616 else if ( (s
->n_value
<= targetUnslidAddress
) && (bestSymbol
->n_value
< s
->n_value
) ) {
621 if ( bestSymbol
!= NULL
) {
622 *symbolAddr
= bestSymbol
->n_value
+ leInfo
.layout
.slide
;
623 if ( bestSymbol
->n_un
.n_strx
< maxStringOffset
)
624 *symbolName
= &stringPool
[bestSymbol
->n_un
.n_strx
];
629 const struct nlist
* bestSymbol
= nullptr;
630 // first walk all global symbols
631 const struct nlist
* const globalsStart
= &symbols
[leInfo
.dynSymTab
->iextdefsym
];
632 const struct nlist
* const globalsEnd
= &globalsStart
[leInfo
.dynSymTab
->nextdefsym
];
633 for (const struct nlist
* s
= globalsStart
; s
< globalsEnd
; ++s
) {
634 if ( (s
->n_type
& N_TYPE
) == N_SECT
) {
635 if ( bestSymbol
== nullptr ) {
636 if ( s
->n_value
<= targetUnslidAddress
)
639 else if ( (s
->n_value
<= targetUnslidAddress
) && (bestSymbol
->n_value
< s
->n_value
) ) {
644 // next walk all local symbols
645 const struct nlist
* const localsStart
= &symbols
[leInfo
.dynSymTab
->ilocalsym
];
646 const struct nlist
* const localsEnd
= &localsStart
[leInfo
.dynSymTab
->nlocalsym
];
647 for (const struct nlist
* s
= localsStart
; s
< localsEnd
; ++s
) {
648 if ( ((s
->n_type
& N_TYPE
) == N_SECT
) && ((s
->n_type
& N_STAB
) == 0) ) {
649 if ( bestSymbol
== nullptr ) {
650 if ( s
->n_value
<= targetUnslidAddress
)
653 else if ( (s
->n_value
<= targetUnslidAddress
) && (bestSymbol
->n_value
< s
->n_value
) ) {
658 if ( bestSymbol
!= nullptr ) {
660 if ( bestSymbol
->n_desc
& N_ARM_THUMB_DEF
)
661 *symbolAddr
= (bestSymbol
->n_value
| 1) + leInfo
.layout
.slide
;
663 *symbolAddr
= bestSymbol
->n_value
+ leInfo
.layout
.slide
;
665 *symbolAddr
= bestSymbol
->n_value
+ leInfo
.layout
.slide
;
667 if ( bestSymbol
->n_un
.n_strx
< maxStringOffset
)
668 *symbolName
= &stringPool
[bestSymbol
->n_un
.n_strx
];
676 const void* MachOLoaded::findSectionContent(const char* segName
, const char* sectName
, uint64_t& size
) const
678 __block
const void* result
= nullptr;
679 forEachSection(^(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) {
680 if ( (strcmp(sectInfo
.sectName
, sectName
) == 0) && (strcmp(sectInfo
.segInfo
.segName
, segName
) == 0) ) {
681 size
= sectInfo
.sectSize
;
682 result
= (void*)(sectInfo
.sectAddr
+ getSlide());
689 bool MachOLoaded::intersectsRange(uintptr_t start
, uintptr_t length
) const
691 __block
bool result
= false;
692 uintptr_t slide
= getSlide();
693 forEachSegment(^(const SegmentInfo
& info
, bool& stop
) {
694 if ( (info
.vmAddr
+info
.vmSize
+slide
>= start
) && (info
.vmAddr
+slide
< start
+length
) )
700 const uint8_t* MachOLoaded::trieWalk(Diagnostics
& diag
, const uint8_t* start
, const uint8_t* end
, const char* symbol
)
702 uint32_t visitedNodeOffsets
[128];
703 int visitedNodeOffsetCount
= 0;
704 visitedNodeOffsets
[visitedNodeOffsetCount
++] = 0;
705 const uint8_t* p
= start
;
707 uint64_t terminalSize
= *p
++;
708 if ( terminalSize
> 127 ) {
709 // except for re-export-with-rename, all terminal sizes fit in one byte
711 terminalSize
= read_uleb128(diag
, p
, end
);
712 if ( diag
.hasError() )
715 if ( (*symbol
== '\0') && (terminalSize
!= 0) ) {
718 const uint8_t* children
= p
+ terminalSize
;
719 if ( children
> end
) {
720 //diag.error("malformed trie node, terminalSize=0x%llX extends past end of trie\n", terminalSize);
723 uint8_t childrenRemaining
= *children
++;
725 uint64_t nodeOffset
= 0;
726 for (; childrenRemaining
> 0; --childrenRemaining
) {
727 const char* ss
= symbol
;
728 bool wrongEdge
= false;
729 // scan whole edge to get to next edge
730 // if edge is longer than target symbol name, don't read past end of symbol name
732 while ( c
!= '\0' ) {
742 // advance to next child
743 ++p
; // skip over zero terminator
744 // skip over uleb128 until last byte is found
745 while ( (*p
& 0x80) != 0 )
747 ++p
; // skip over last byte of uleb128
749 diag
.error("malformed trie node, child node extends past end of trie\n");
754 // the symbol so far matches this edge (child)
755 // so advance to the child's node
757 nodeOffset
= read_uleb128(diag
, p
, end
);
758 if ( diag
.hasError() )
760 if ( (nodeOffset
== 0) || ( &start
[nodeOffset
] > end
) ) {
761 diag
.error("malformed trie child, nodeOffset=0x%llX out of range\n", nodeOffset
);
768 if ( nodeOffset
!= 0 ) {
769 if ( nodeOffset
> (uint64_t)(end
-start
) ) {
770 diag
.error("malformed trie child, nodeOffset=0x%llX out of range\n", nodeOffset
);
773 for (int i
=0; i
< visitedNodeOffsetCount
; ++i
) {
774 if ( visitedNodeOffsets
[i
] == nodeOffset
) {
775 diag
.error("malformed trie child, cycle to nodeOffset=0x%llX\n", nodeOffset
);
779 visitedNodeOffsets
[visitedNodeOffsetCount
++] = (uint32_t)nodeOffset
;
780 if ( visitedNodeOffsetCount
>= 128 ) {
781 diag
.error("malformed trie too deep\n");
784 p
= &start
[nodeOffset
];
792 bool MachOLoaded::cdHashOfCodeSignature(const void* codeSigStart
, size_t codeSignLen
, uint8_t cdHash
[20]) const
794 const CS_CodeDirectory
* cd
= (const CS_CodeDirectory
*)findCodeDirectoryBlob(codeSigStart
, codeSignLen
);
798 uint32_t cdLength
= htonl(cd
->length
);
799 if ( cd
->hashType
== CS_HASHTYPE_SHA384
) {
800 uint8_t digest
[CC_SHA384_DIGEST_LENGTH
];
801 CC_SHA384(cd
, cdLength
, digest
);
802 // cd-hash of sigs that use SHA384 is the first 20 bytes of the SHA384 of the code digest
803 memcpy(cdHash
, digest
, 20);
806 else if ( (cd
->hashType
== CS_HASHTYPE_SHA256
) || (cd
->hashType
== CS_HASHTYPE_SHA256_TRUNCATED
) ) {
807 uint8_t digest
[CC_SHA256_DIGEST_LENGTH
];
808 CC_SHA256(cd
, cdLength
, digest
);
809 // cd-hash of sigs that use SHA256 is the first 20 bytes of the SHA256 of the code digest
810 memcpy(cdHash
, digest
, 20);
813 else if ( cd
->hashType
== CS_HASHTYPE_SHA1
) {
814 // compute hash directly into return buffer
815 CC_SHA1(cd
, cdLength
, cdHash
);
823 // Note, this has to match the kernel
824 static const uint32_t hashPriorities
[] = {
826 CS_HASHTYPE_SHA256_TRUNCATED
,
831 static unsigned int hash_rank(const CS_CodeDirectory
*cd
)
833 uint32_t type
= cd
->hashType
;
834 for (uint32_t n
= 0; n
< sizeof(hashPriorities
) / sizeof(hashPriorities
[0]); ++n
) {
835 if (hashPriorities
[n
] == type
)
844 // Note, this has to match the kernel
845 static const uint32_t hashPriorities_watchOS
[] = {
849 static unsigned int hash_rank_watchOS(const CS_CodeDirectory
*cd
)
851 uint32_t type
= cd
->hashType
;
852 for (uint32_t n
= 0; n
< sizeof(hashPriorities_watchOS
) / sizeof(hashPriorities_watchOS
[0]); ++n
) {
853 if (hashPriorities_watchOS
[n
] == type
)
861 const void* MachOLoaded::findCodeDirectoryBlob(const void* codeSigStart
, size_t codeSignLen
) const
863 // verify min length of overall code signature
864 if ( codeSignLen
< sizeof(CS_SuperBlob
) )
867 // verify magic at start
868 const CS_SuperBlob
* codeSuperBlob
= (CS_SuperBlob
*)codeSigStart
;
869 if ( codeSuperBlob
->magic
!= htonl(CSMAGIC_EMBEDDED_SIGNATURE
) )
872 // verify count of sub-blobs not too large
873 uint32_t subBlobCount
= htonl(codeSuperBlob
->count
);
874 if ( (codeSignLen
-sizeof(CS_SuperBlob
))/sizeof(CS_BlobIndex
) < subBlobCount
)
877 // Note: The kernel currently always uses sha1 for watchOS, even if other hashes are available.
878 const bool isWatchOS
= this->supportsPlatform(Platform::watchOS
);
879 auto hashRankFn
= isWatchOS
? &hash_rank_watchOS
: &hash_rank
;
881 // walk each sub blob, looking at ones with type CSSLOT_CODEDIRECTORY
882 const CS_CodeDirectory
* bestCd
= nullptr;
883 for (uint32_t i
=0; i
< subBlobCount
; ++i
) {
884 if ( codeSuperBlob
->index
[i
].type
== htonl(CSSLOT_CODEDIRECTORY
) ) {
885 // Ok, this is the regular code directory
886 } else if ( codeSuperBlob
->index
[i
].type
>= htonl(CSSLOT_ALTERNATE_CODEDIRECTORIES
) && codeSuperBlob
->index
[i
].type
<= htonl(CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT
)) {
887 // Ok, this is the alternative code directory
891 uint32_t cdOffset
= htonl(codeSuperBlob
->index
[i
].offset
);
892 // verify offset is not out of range
893 if ( cdOffset
> (codeSignLen
- sizeof(CS_CodeDirectory
)) )
895 const CS_CodeDirectory
* cd
= (CS_CodeDirectory
*)((uint8_t*)codeSuperBlob
+ cdOffset
);
896 uint32_t cdLength
= htonl(cd
->length
);
897 // verify code directory length not out of range
898 if ( cdLength
> (codeSignLen
- cdOffset
) )
900 if ( cd
->magic
== htonl(CSMAGIC_CODEDIRECTORY
) ) {
901 if ( !bestCd
|| (hashRankFn(cd
) > hashRankFn(bestCd
)) )
909 // Regular pointer which needs to fit in 51-bits of value.
910 // C++ RTTI uses the top bit, so we'll allow the whole top-byte
911 // and the signed-extended bottom 43-bits to be fit in to 51-bits.
912 uint64_t MachOLoaded::ChainedFixupPointerOnDisk::signExtend51(uint64_t value51
)
914 uint64_t top8Bits
= value51
& 0x007F80000000000ULL
;
915 uint64_t bottom43Bits
= value51
& 0x000007FFFFFFFFFFULL
;
916 uint64_t newValue
= (top8Bits
<< 13) | (((intptr_t)(bottom43Bits
<< 21) >> 21) & 0x00FFFFFFFFFFFFFF);
920 uint64_t MachOLoaded::ChainedFixupPointerOnDisk::PlainRebase::signExtendedTarget() const
922 return signExtend51(this->target
);
925 uint64_t MachOLoaded::ChainedFixupPointerOnDisk::PlainBind::signExtendedAddend() const
927 uint64_t addend19
= this->addend
;
928 if ( addend19
& 0x40000 )
929 return addend19
| 0xFFFFFFFFFFFC0000ULL
;
934 const char* MachOLoaded::ChainedFixupPointerOnDisk::keyName(uint8_t keyBits
)
936 static const char* names
[] = {
937 "IA", "IB", "DA", "DB"
940 return names
[keyBits
];
943 const char* MachOLoaded::ChainedFixupPointerOnDisk::AuthRebase::keyName() const
945 return ChainedFixupPointerOnDisk::keyName(this->key
);
948 const char* MachOLoaded::ChainedFixupPointerOnDisk::AuthBind::keyName() const
950 return ChainedFixupPointerOnDisk::keyName(this->key
);
954 uint64_t MachOLoaded::ChainedFixupPointerOnDisk::signPointer(void* loc
, uint64_t target
) const
956 #if __has_feature(ptrauth_calls)
957 uint64_t discriminator
= authBind
.diversity
;
958 if ( authBind
.addrDiv
)
959 discriminator
= __builtin_ptrauth_blend_discriminator(loc
, discriminator
);
960 switch ( authBind
.key
) {
962 return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target
, 0, discriminator
);
964 return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target
, 1, discriminator
);
966 return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target
, 2, discriminator
);
968 return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target
, 3, discriminator
);