]> git.saurik.com Git - apple/dyld.git/blob - dyld3/MachOAnalyzer.cpp
dyld-832.7.1.tar.gz
[apple/dyld.git] / dyld3 / MachOAnalyzer.cpp
1 /*
2 * Copyright (c) 2017 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <sys/errno.h>
27 #include <sys/fcntl.h>
28 #include <sys/mman.h>
29 #include <mach/mach.h>
30 #include <assert.h>
31 #include <limits.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <mach-o/reloc.h>
36 #include <mach-o/x86_64/reloc.h>
37 #include <mach-o/nlist.h>
38 #include <TargetConditionals.h>
39
40 #include "MachOAnalyzer.h"
41 #include "CodeSigningTypes.h"
42 #include "Array.h"
43
44 // FIXME: We should get this from cctools
45 #define DYLD_CACHE_ADJ_V2_FORMAT 0x7F
46
47 namespace dyld3 {
48
49
50 const MachOAnalyzer* MachOAnalyzer::validMainExecutable(Diagnostics& diag, const mach_header* mh, const char* path, uint64_t sliceLength,
51 const GradedArchs& archs, Platform platform)
52 {
53 const MachOAnalyzer* result = (const MachOAnalyzer*)mh;
54 if ( !result->validMachOForArchAndPlatform(diag, (size_t)sliceLength, path, archs, platform, true) )
55 return nullptr;
56 if ( !result->isDynamicExecutable() )
57 return nullptr;
58
59 return result;
60 }
61
62 bool MachOAnalyzer::loadFromBuffer(Diagnostics& diag, const closure::FileSystem& fileSystem,
63 const char* path, const GradedArchs& archs, Platform platform,
64 closure::LoadedFileInfo& info)
65 {
66 // if fat, remap just slice needed
67 bool fatButMissingSlice;
68 const FatFile* fh = (FatFile*)info.fileContent;
69 uint64_t sliceOffset = info.sliceOffset;
70 uint64_t sliceLen = info.sliceLen;
71 if ( fh->isFatFileWithSlice(diag, info.fileContentLen, archs, info.isOSBinary, sliceOffset, sliceLen, fatButMissingSlice) ) {
72 // unmap anything before slice
73 fileSystem.unloadPartialFile(info, sliceOffset, sliceLen);
74 // Update the info to keep track of the new slice offset.
75 info.sliceOffset = sliceOffset;
76 info.sliceLen = sliceLen;
77 }
78 else if ( diag.hasError() ) {
79 // We must have generated an error in the fat file parsing so use that error
80 fileSystem.unloadFile(info);
81 return false;
82 }
83 else if ( fatButMissingSlice ) {
84 diag.error("missing compatible arch in %s", path);
85 fileSystem.unloadFile(info);
86 return false;
87 }
88
89 const MachOAnalyzer* mh = (MachOAnalyzer*)info.fileContent;
90
91 // validate is mach-o of requested arch and platform
92 if ( !mh->validMachOForArchAndPlatform(diag, (size_t)info.sliceLen, path, archs, platform, info.isOSBinary) ) {
93 fileSystem.unloadFile(info);
94 return false;
95 }
96
97 // if has zero-fill expansion, re-map
98 mh = mh->remapIfZeroFill(diag, fileSystem, info);
99
100 // on error, remove mappings and return nullptr
101 if ( diag.hasError() ) {
102 fileSystem.unloadFile(info);
103 return false;
104 }
105
106 // now that LINKEDIT is at expected offset, finish validation
107 mh->validLinkedit(diag, path);
108
109 // on error, remove mappings and return nullptr
110 if ( diag.hasError() ) {
111 fileSystem.unloadFile(info);
112 return false;
113 }
114
115 return true;
116 }
117
118
119 closure::LoadedFileInfo MachOAnalyzer::load(Diagnostics& diag, const closure::FileSystem& fileSystem,
120 const char* path, const GradedArchs& archs, Platform platform, char realerPath[MAXPATHLEN])
121 {
122 // FIXME: This should probably be an assert, but if we happen to have a diagnostic here then something is wrong
123 // above us and we should quickly return instead of doing unnecessary work.
124 if (diag.hasError())
125 return closure::LoadedFileInfo();
126
127 closure::LoadedFileInfo info;
128 if (!fileSystem.loadFile(path, info, realerPath, ^(const char *format, ...) {
129 va_list list;
130 va_start(list, format);
131 diag.error(format, list);
132 va_end(list);
133 })) {
134 return closure::LoadedFileInfo();
135 }
136
137 // If we now have an error, but succeeded, then we must have tried multiple paths, one of which errored, but
138 // then succeeded on a later path. So clear the error.
139 if (diag.hasError())
140 diag.clearError();
141
142 bool loaded = loadFromBuffer(diag, fileSystem, path, archs, platform, info);
143 if (!loaded)
144 return {};
145 return info;
146 }
147
148 // for use with already mmap()ed file
149 bool MachOAnalyzer::isOSBinary(int fd, uint64_t sliceOffset, uint64_t sliceSize) const
150 {
151 #ifdef F_GETSIGSINFO
152 if ( fd == -1 )
153 return false;
154
155 uint32_t sigOffset;
156 uint32_t sigSize;
157 if ( !this->hasCodeSignature(sigOffset, sigSize) )
158 return false;
159
160 // register code signature
161 fsignatures_t sigreg;
162 sigreg.fs_file_start = sliceOffset; // start of mach-o slice in fat file
163 sigreg.fs_blob_start = (void*)(long)sigOffset; // start of CD in mach-o file
164 sigreg.fs_blob_size = sigSize; // size of CD
165 if ( ::fcntl(fd, F_ADDFILESIGS_RETURN, &sigreg) == -1 )
166 return false;
167
168 // ask if code signature is for something in the OS
169 fgetsigsinfo siginfo = { (off_t)sliceOffset, GETSIGSINFO_PLATFORM_BINARY, 0 };
170 if ( ::fcntl(fd, F_GETSIGSINFO, &siginfo) == -1 )
171 return false;
172
173 return (siginfo.fg_sig_is_platform);
174 #else
175 return false;
176 #endif
177 }
178
179 // for use when just the fat_header has been read
180 bool MachOAnalyzer::sliceIsOSBinary(int fd, uint64_t sliceOffset, uint64_t sliceSize)
181 {
182 if ( fd == -1 )
183 return false;
184
185 // need to mmap() slice so we can find the code signature
186 void* mappedSlice = ::mmap(nullptr, sliceSize, PROT_READ, MAP_PRIVATE, fd, sliceOffset);
187 if ( mappedSlice == MAP_FAILED )
188 return false;
189
190 const MachOAnalyzer* ma = (MachOAnalyzer*)mappedSlice;
191 bool result = ma->isOSBinary(fd, sliceOffset, sliceSize);
192 ::munmap(mappedSlice, sliceSize);
193
194 return result;
195 }
196
197
198
199 #if DEBUG
200 // only used in debug builds of cache builder to verify segment moves are valid
201 void MachOAnalyzer::validateDyldCacheDylib(Diagnostics& diag, const char* path) const
202 {
203 validLinkedit(diag, path);
204 validSegments(diag, path, 0xffffffff);
205 }
206 #endif
207
208 uint64_t MachOAnalyzer::mappedSize() const
209 {
210 uint64_t vmSpace;
211 bool hasZeroFill;
212 analyzeSegmentsLayout(vmSpace, hasZeroFill);
213 return vmSpace;
214 }
215
216 bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics& diag, size_t sliceLength, const char* path, const GradedArchs& archs, Platform reqPlatform, bool isOSBinary) const
217 {
218 // must start with mach-o magic value
219 if ( (this->magic != MH_MAGIC) && (this->magic != MH_MAGIC_64) ) {
220 diag.error("could not use '%s' because it is not a mach-o file: 0x%08X 0x%08X", path, this->magic, this->cputype);
221 return false;
222 }
223
224 if ( !archs.grade(this->cputype, this->cpusubtype, isOSBinary) ) {
225 diag.error("could not use '%s' because it is not a compatible arch", path);
226 return false;
227 }
228
229 // must be a filetype dyld can load
230 switch ( this->filetype ) {
231 case MH_EXECUTE:
232 case MH_DYLIB:
233 case MH_BUNDLE:
234 break;
235 #if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL || BUILDING_RUN_STATIC
236 // Allow offline tools to analyze binaries dyld doesn't load
237 case MH_DYLINKER:
238 case MH_KEXT_BUNDLE:
239 case MH_FILESET:
240 break;
241 #endif
242 default:
243 diag.error("could not use '%s' because it is not a dylib, bundle, or executable, filetype=0x%08X", path, this->filetype);
244 return false;
245 }
246
247 // validate load commands structure
248 if ( !this->validLoadCommands(diag, path, sliceLength) ) {
249 return false;
250 }
251
252 // filter out static executables
253 if ( (this->filetype == MH_EXECUTE) && !isDynamicExecutable() ) {
254 #if !BUILDING_DYLDINFO && !BUILDING_APP_CACHE_UTIL
255 // dyldinfo should be able to inspect static executables such as the kernel
256 diag.error("could not use '%s' because it is a static executable", path);
257 return false;
258 #endif
259 }
260
261 // HACK: If we are asking for no platform, then make sure the binary doesn't have one
262 #if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL
263 if ( isFileSet() ) {
264 // A statically linked kernel collection should contain a 0 platform
265 __block bool foundPlatform = false;
266 __block bool foundBadPlatform = false;
267 forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
268 foundPlatform = true;
269 if ( platform != Platform::unknown ) {
270 foundBadPlatform = true;
271 }
272 });
273 if (!foundPlatform) {
274 diag.error("could not use '%s' because we expected it to have a platform", path);
275 return false;
276 }
277 if (foundBadPlatform) {
278 diag.error("could not use '%s' because is has the wrong platform", path);
279 return false;
280 }
281 } else if ( reqPlatform == Platform::unknown ) {
282 // Unfortunately the static kernel has a platform, but kext's don't, so we can't
283 // verify the platform of the kernel.
284 if ( !isStaticExecutable() ) {
285 __block bool foundPlatform = false;
286 forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
287 foundPlatform = true;
288 });
289 if (foundPlatform) {
290 diag.error("could not use '%s' because we expected it to have no platform", path);
291 return false;
292 }
293 }
294 } else
295 #endif
296 if ( !this->loadableIntoProcess(reqPlatform, path) ) {
297 diag.error("could not use '%s' because it was not built for platform %s", path, MachOFile::platformName(reqPlatform));
298 return false;
299 }
300
301 // validate dylib loads
302 if ( !validEmbeddedPaths(diag, reqPlatform, path) )
303 return false;
304
305 // validate segments
306 if ( !validSegments(diag, path, sliceLength) )
307 return false;
308
309 // validate entry
310 if ( this->filetype == MH_EXECUTE ) {
311 if ( !validMain(diag, path) )
312 return false;
313 }
314
315 // further validations done in validLinkedit()
316
317 return true;
318 }
319
320 bool MachOAnalyzer::validLinkedit(Diagnostics& diag, const char* path) const
321 {
322 // validate LINKEDIT layout
323 if ( !validLinkeditLayout(diag, path) )
324 return false;
325
326 if ( hasLoadCommand(LC_DYLD_CHAINED_FIXUPS) ) {
327 if ( !validChainedFixupsInfo(diag, path) )
328 return false;
329 }
330 #if SUPPORT_ARCH_arm64e
331 else if ( (this->cputype == CPU_TYPE_ARM64) && (this->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E) ) {
332 if ( !validChainedFixupsInfoOldArm64e(diag, path) )
333 return false;
334 }
335 #endif
336 else {
337 // validate rebasing info
338 if ( !validRebaseInfo(diag, path) )
339 return false;
340
341 // validate binding info
342 if ( !validBindInfo(diag, path) )
343 return false;
344 }
345
346 return true;
347 }
348
349 bool MachOAnalyzer::validLoadCommands(Diagnostics& diag, const char* path, size_t fileLen) const
350 {
351 // check load command don't exceed file length
352 if ( this->sizeofcmds + machHeaderSize() > fileLen ) {
353 diag.error("in '%s' load commands exceed length of file", path);
354 return false;
355 }
356
357 // walk all load commands and sanity check them
358 Diagnostics walkDiag;
359 forEachLoadCommand(walkDiag, ^(const load_command* cmd, bool& stop) {});
360 if ( walkDiag.hasError() ) {
361 #if BUILDING_CACHE_BUILDER
362 diag.error("in '%s' %s", path, walkDiag.errorMessage().c_str());
363 #else
364 diag.error("in '%s' %s", path, walkDiag.errorMessage());
365 #endif
366 return false;
367 }
368
369 // check load commands fit in TEXT segment
370 __block bool foundTEXT = false;
371 forEachSegment(^(const SegmentInfo& info, bool& stop) {
372 if ( strcmp(info.segName, "__TEXT") == 0 ) {
373 foundTEXT = true;
374 if ( this->sizeofcmds + machHeaderSize() > info.fileSize ) {
375 diag.error("in '%s' load commands exceed length of __TEXT segment", path);
376 }
377 if ( info.fileOffset != 0 ) {
378 diag.error("in '%s' __TEXT segment not start of mach-o", path);
379 }
380 stop = true;
381 }
382 });
383 if ( !diag.noError() && !foundTEXT ) {
384 diag.error("in '%s' __TEXT segment not found", path);
385 return false;
386 }
387
388 return true;
389 }
390
391 const MachOAnalyzer* MachOAnalyzer::remapIfZeroFill(Diagnostics& diag, const closure::FileSystem& fileSystem, closure::LoadedFileInfo& info) const
392 {
393 uint64_t vmSpaceRequired;
394 bool hasZeroFill;
395 analyzeSegmentsLayout(vmSpaceRequired, hasZeroFill);
396
397 if ( hasZeroFill ) {
398 vm_address_t newMappedAddr;
399 if ( ::vm_allocate(mach_task_self(), &newMappedAddr, (size_t)vmSpaceRequired, VM_FLAGS_ANYWHERE) != 0 ) {
400 diag.error("vm_allocate failure");
401 return nullptr;
402 }
403
404 // re-map each segment read-only, with runtime layout
405 #if BUILDING_APP_CACHE_UTIL
406 // The auxKC is mapped with __DATA first, so we need to get either the __DATA or __TEXT depending on what is earliest
407 __block uint64_t baseAddress = ~0ULL;
408 forEachSegment(^(const SegmentInfo& info, bool& stop) {
409 baseAddress = std::min(baseAddress, info.vmAddr);
410 });
411 uint64_t textSegVMAddr = preferredLoadAddress();
412 #else
413 uint64_t baseAddress = preferredLoadAddress();
414 #endif
415
416 forEachSegment(^(const SegmentInfo& segmentInfo, bool& stop) {
417 if ( (segmentInfo.fileSize != 0) && (segmentInfo.vmSize != 0) ) {
418 kern_return_t r = vm_copy(mach_task_self(), (vm_address_t)((long)info.fileContent+segmentInfo.fileOffset), (vm_size_t)segmentInfo.fileSize, (vm_address_t)(newMappedAddr+segmentInfo.vmAddr-baseAddress));
419 if ( r != KERN_SUCCESS ) {
420 diag.error("vm_copy() failure");
421 stop = true;
422 }
423 }
424 });
425 if ( diag.noError() ) {
426 // remove original mapping and return new mapping
427 fileSystem.unloadFile(info);
428
429 // make the new mapping read-only
430 ::vm_protect(mach_task_self(), newMappedAddr, (vm_size_t)vmSpaceRequired, false, VM_PROT_READ);
431
432 #if BUILDING_APP_CACHE_UTIL
433 if ( textSegVMAddr != baseAddress ) {
434 info.unload = [](const closure::LoadedFileInfo& info) {
435 // Unloading binaries where __DATA is first requires working out the real range of the binary
436 // The fileContent points at the mach_header, not the actaul start of the file content, unfortunately.
437 const MachOAnalyzer* ma = (const MachOAnalyzer*)info.fileContent;
438 __block uint64_t baseAddress = ~0ULL;
439 ma->forEachSegment(^(const SegmentInfo& info, bool& stop) {
440 baseAddress = std::min(baseAddress, info.vmAddr);
441 });
442 uint64_t textSegVMAddr = ma->preferredLoadAddress();
443
444 uint64_t basePointerOffset = textSegVMAddr - baseAddress;
445 uint8_t* bufferStart = (uint8_t*)info.fileContent - basePointerOffset;
446 ::vm_deallocate(mach_task_self(), (vm_address_t)bufferStart, (size_t)info.fileContentLen);
447 };
448
449 // And update the file content to the new location
450 info.fileContent = (const void*)(newMappedAddr + textSegVMAddr - baseAddress);
451 info.fileContentLen = vmSpaceRequired;
452 return (const MachOAnalyzer*)info.fileContent;
453 }
454 #endif
455
456 // Set vm_deallocate as the unload method.
457 info.unload = [](const closure::LoadedFileInfo& info) {
458 ::vm_deallocate(mach_task_self(), (vm_address_t)info.fileContent, (size_t)info.fileContentLen);
459 };
460
461 // And update the file content to the new location
462 info.fileContent = (const void*)newMappedAddr;
463 info.fileContentLen = vmSpaceRequired;
464 return (const MachOAnalyzer*)info.fileContent;
465 }
466 else {
467 // new mapping failed, return old mapping with an error in diag
468 ::vm_deallocate(mach_task_self(), newMappedAddr, (size_t)vmSpaceRequired);
469 return nullptr;
470 }
471 }
472
473 return this;
474 }
475
476 void MachOAnalyzer::analyzeSegmentsLayout(uint64_t& vmSpace, bool& hasZeroFill) const
477 {
478 __block bool writeExpansion = false;
479 __block uint64_t lowestVmAddr = 0xFFFFFFFFFFFFFFFFULL;
480 __block uint64_t highestVmAddr = 0;
481 __block uint64_t sumVmSizes = 0;
482 forEachSegment(^(const SegmentInfo& segmentInfo, bool& stop) {
483 if ( strcmp(segmentInfo.segName, "__PAGEZERO") == 0 )
484 return;
485 if ( segmentInfo.writable() && (segmentInfo.fileSize != segmentInfo.vmSize) )
486 writeExpansion = true; // zerofill at end of __DATA
487 if ( segmentInfo.vmSize == 0 ) {
488 // Always zero fill if we have zero-sized segments
489 writeExpansion = true;
490 }
491 if ( segmentInfo.vmAddr < lowestVmAddr )
492 lowestVmAddr = segmentInfo.vmAddr;
493 if ( segmentInfo.vmAddr+segmentInfo.vmSize > highestVmAddr )
494 highestVmAddr = segmentInfo.vmAddr+segmentInfo.vmSize;
495 sumVmSizes += segmentInfo.vmSize;
496 });
497 uint64_t totalVmSpace = (highestVmAddr - lowestVmAddr);
498 // LINKEDIT vmSize is not required to be a multiple of page size. Round up if that is the case
499 const uint64_t pageSize = uses16KPages() ? 0x4000 : 0x1000;
500 totalVmSpace = (totalVmSpace + (pageSize - 1)) & ~(pageSize - 1);
501 bool hasHole = (totalVmSpace != sumVmSizes); // segments not contiguous
502
503 // The aux KC may have __DATA first, in which case we always want to vm_copy to the right place
504 bool hasOutOfOrderSegments = false;
505 #if BUILDING_APP_CACHE_UTIL
506 uint64_t textSegVMAddr = preferredLoadAddress();
507 hasOutOfOrderSegments = textSegVMAddr != lowestVmAddr;
508 #endif
509
510 vmSpace = totalVmSpace;
511 hasZeroFill = writeExpansion || hasHole || hasOutOfOrderSegments;
512 }
513
514 bool MachOAnalyzer::enforceFormat(Malformed kind) const
515 {
516 #if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL || BUILDING_RUN_STATIC
517 // HACK: If we are the kernel, we have a different format to enforce
518 if ( isFileSet() ) {
519 bool result = false;
520 switch (kind) {
521 case Malformed::linkeditOrder:
522 case Malformed::linkeditAlignment:
523 case Malformed::dyldInfoAndlocalRelocs:
524 result = true;
525 break;
526 case Malformed::segmentOrder:
527 // The aux KC has __DATA first
528 result = false;
529 break;
530 case Malformed::linkeditPermissions:
531 case Malformed::executableData:
532 case Malformed::writableData:
533 case Malformed::codeSigAlignment:
534 case Malformed::sectionsAddrRangeWithinSegment:
535 result = true;
536 break;
537 case Malformed::textPermissions:
538 // The kernel has its own __TEXT_EXEC for executable memory
539 result = false;
540 break;
541 }
542 return result;
543 }
544
545 if ( isStaticExecutable() ) {
546 bool result = false;
547 switch (kind) {
548 case Malformed::linkeditOrder:
549 case Malformed::linkeditAlignment:
550 case Malformed::dyldInfoAndlocalRelocs:
551 result = true;
552 break;
553 case Malformed::segmentOrder:
554 result = false;
555 break;
556 case Malformed::linkeditPermissions:
557 case Malformed::executableData:
558 case Malformed::codeSigAlignment:
559 case Malformed::textPermissions:
560 case Malformed::sectionsAddrRangeWithinSegment:
561 result = true;
562 break;
563 case Malformed::writableData:
564 // The kernel has __DATA_CONST marked as r/o
565 result = false;
566 break;
567 }
568 return result;
569 }
570
571 #endif
572
573 __block bool result = false;
574 forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
575 switch (platform) {
576 case Platform::macOS:
577 switch (kind) {
578 case Malformed::linkeditOrder:
579 case Malformed::linkeditAlignment:
580 case Malformed::dyldInfoAndlocalRelocs:
581 // enforce these checks on new binaries only
582 if (sdk >= 0x000A0E00) // macOS 10.14
583 result = true;
584 break;
585 case Malformed::segmentOrder:
586 case Malformed::linkeditPermissions:
587 case Malformed::textPermissions:
588 case Malformed::executableData:
589 case Malformed::writableData:
590 case Malformed::codeSigAlignment:
591 // enforce these checks on new binaries only
592 if (sdk >= 0x000A0F00) // macOS 10.15
593 result = true;
594 break;
595 case Malformed::sectionsAddrRangeWithinSegment:
596 // enforce these checks on new binaries only
597 if (sdk >= 0x000A1000) // macOS 10.16
598 result = true;
599 break;
600 }
601 break;
602 case Platform::iOS:
603 switch (kind) {
604 case Malformed::linkeditOrder:
605 case Malformed::dyldInfoAndlocalRelocs:
606 case Malformed::textPermissions:
607 case Malformed::executableData:
608 case Malformed::writableData:
609 result = true;
610 break;
611 case Malformed::linkeditAlignment:
612 case Malformed::segmentOrder:
613 case Malformed::linkeditPermissions:
614 case Malformed::codeSigAlignment:
615 // enforce these checks on new binaries only
616 if (sdk >= 0x000D0000) // iOS 13
617 result = true;
618 break;
619 case Malformed::sectionsAddrRangeWithinSegment:
620 // enforce these checks on new binaries only
621 if (sdk >= 0x000E0000) // iOS 14
622 result = true;
623 break;
624 }
625 break;
626 default:
627 result = true;
628 break;
629 }
630 });
631 // if binary is so old, there is no platform info, don't enforce malformed errors
632 return result;
633 }
634
635 bool MachOAnalyzer::validEmbeddedPaths(Diagnostics& diag, Platform platform, const char* path) const
636 {
637 __block int index = 1;
638 __block bool allGood = true;
639 __block bool foundInstallName = false;
640 __block int dependentsCount = 0;
641 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
642 const dylib_command* dylibCmd;
643 const rpath_command* rpathCmd;
644 switch ( cmd->cmd ) {
645 case LC_ID_DYLIB:
646 foundInstallName = true;
647 // fall through
648 [[clang::fallthrough]];
649 case LC_LOAD_DYLIB:
650 case LC_LOAD_WEAK_DYLIB:
651 case LC_REEXPORT_DYLIB:
652 case LC_LOAD_UPWARD_DYLIB:
653 dylibCmd = (dylib_command*)cmd;
654 if ( dylibCmd->dylib.name.offset > cmd->cmdsize ) {
655 diag.error("in '%s' load command #%d name offset (%u) outside its size (%u)", path, index, dylibCmd->dylib.name.offset, cmd->cmdsize);
656 stop = true;
657 allGood = false;
658 }
659 else {
660 bool foundEnd = false;
661 const char* start = (char*)dylibCmd + dylibCmd->dylib.name.offset;
662 const char* end = (char*)dylibCmd + cmd->cmdsize;
663 for (const char* s=start; s < end; ++s) {
664 if ( *s == '\0' ) {
665 foundEnd = true;
666 break;
667 }
668 }
669 if ( !foundEnd ) {
670 diag.error("in '%s' load command #%d string extends beyond end of load command", path, index);
671 stop = true;
672 allGood = false;
673 }
674 }
675 if ( cmd->cmd != LC_ID_DYLIB )
676 ++dependentsCount;
677 break;
678 case LC_RPATH:
679 rpathCmd = (rpath_command*)cmd;
680 if ( rpathCmd->path.offset > cmd->cmdsize ) {
681 diag.error("in '%s' load command #%d path offset (%u) outside its size (%u)", path, index, rpathCmd->path.offset, cmd->cmdsize);
682 stop = true;
683 allGood = false;
684 }
685 else {
686 bool foundEnd = false;
687 const char* start = (char*)rpathCmd + rpathCmd->path.offset;
688 const char* end = (char*)rpathCmd + cmd->cmdsize;
689 for (const char* s=start; s < end; ++s) {
690 if ( *s == '\0' ) {
691 foundEnd = true;
692 break;
693 }
694 }
695 if ( !foundEnd ) {
696 diag.error("in '%s' load command #%d string extends beyond end of load command", path, index);
697 stop = true;
698 allGood = false;
699 }
700 }
701 break;
702 }
703 ++index;
704 });
705 if ( !allGood )
706 return false;
707
708 if ( this->filetype == MH_DYLIB ) {
709 if ( !foundInstallName ) {
710 diag.error("in '%s' MH_DYLIB is missing LC_ID_DYLIB", path);
711 return false;
712 }
713 }
714 else {
715 if ( foundInstallName ) {
716 diag.error("in '%s' LC_ID_DYLIB found in non-MH_DYLIB", path);
717 return false;
718 }
719 }
720
721 if ( (dependentsCount == 0) && (this->filetype == MH_EXECUTE) && isDynamicExecutable() ) {
722 diag.error("in '%s' missing LC_LOAD_DYLIB (must link with at least libSystem.dylib)", path);
723 return false;
724 }
725
726 return true;
727 }
728
729 bool MachOAnalyzer::validSegments(Diagnostics& diag, const char* path, size_t fileLen) const
730 {
731 // check segment load command size
732 __block bool badSegmentLoadCommand = false;
733 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
734 if ( cmd->cmd == LC_SEGMENT_64 ) {
735 const segment_command_64* seg = (segment_command_64*)cmd;
736 int32_t sectionsSpace = cmd->cmdsize - sizeof(segment_command_64);
737 if ( sectionsSpace < 0 ) {
738 diag.error("in '%s' load command size too small for LC_SEGMENT_64", path);
739 badSegmentLoadCommand = true;
740 stop = true;
741 }
742 else if ( (sectionsSpace % sizeof(section_64)) != 0 ) {
743 diag.error("in '%s' segment load command size 0x%X will not fit whole number of sections", path, cmd->cmdsize);
744 badSegmentLoadCommand = true;
745 stop = true;
746 }
747 else if ( sectionsSpace != (seg->nsects * sizeof(section_64)) ) {
748 diag.error("in '%s' load command size 0x%X does not match nsects %d", path, cmd->cmdsize, seg->nsects);
749 badSegmentLoadCommand = true;
750 stop = true;
751 }
752 else if ( greaterThanAddOrOverflow(seg->fileoff, seg->filesize, fileLen) ) {
753 diag.error("in '%s' segment load command content extends beyond end of file", path);
754 badSegmentLoadCommand = true;
755 stop = true;
756 }
757 else if ( (seg->filesize > seg->vmsize) && ((seg->vmsize != 0) || ((seg->flags & SG_NORELOC) == 0)) ) {
758 // <rdar://problem/19986776> dyld should support non-allocatable __LLVM segment
759 diag.error("in '%s' segment filesize exceeds vmsize", path);
760 badSegmentLoadCommand = true;
761 stop = true;
762 }
763 }
764 else if ( cmd->cmd == LC_SEGMENT ) {
765 const segment_command* seg = (segment_command*)cmd;
766 int32_t sectionsSpace = cmd->cmdsize - sizeof(segment_command);
767 if ( sectionsSpace < 0 ) {
768 diag.error("in '%s' load command size too small for LC_SEGMENT", path);
769 badSegmentLoadCommand = true;
770 stop = true;
771 }
772 else if ( (sectionsSpace % sizeof(section)) != 0 ) {
773 diag.error("in '%s' segment load command size 0x%X will not fit whole number of sections", path, cmd->cmdsize);
774 badSegmentLoadCommand = true;
775 stop = true;
776 }
777 else if ( sectionsSpace != (seg->nsects * sizeof(section)) ) {
778 diag.error("in '%s' load command size 0x%X does not match nsects %d", path, cmd->cmdsize, seg->nsects);
779 badSegmentLoadCommand = true;
780 stop = true;
781 }
782 else if ( (seg->filesize > seg->vmsize) && ((seg->vmsize != 0) || ((seg->flags & SG_NORELOC) == 0)) ) {
783 // <rdar://problem/19986776> dyld should support non-allocatable __LLVM segment
784 diag.error("in '%s' segment filesize exceeds vmsize", path);
785 badSegmentLoadCommand = true;
786 stop = true;
787 }
788 }
789 });
790 if ( badSegmentLoadCommand )
791 return false;
792
793 // check mapping permissions of segments
794 __block bool badPermissions = false;
795 __block bool badSize = false;
796 __block bool hasTEXT = false;
797 __block bool hasLINKEDIT = false;
798 forEachSegment(^(const SegmentInfo& info, bool& stop) {
799 if ( strcmp(info.segName, "__TEXT") == 0 ) {
800 if ( (info.protections != (VM_PROT_READ|VM_PROT_EXECUTE)) && enforceFormat(Malformed::textPermissions) ) {
801 diag.error("in '%s' __TEXT segment permissions is not 'r-x'", path);
802 badPermissions = true;
803 stop = true;
804 }
805 hasTEXT = true;
806 }
807 else if ( strcmp(info.segName, "__LINKEDIT") == 0 ) {
808 if ( (info.protections != VM_PROT_READ) && enforceFormat(Malformed::linkeditPermissions) ) {
809 diag.error("in '%s' __LINKEDIT segment permissions is not 'r--'", path);
810 badPermissions = true;
811 stop = true;
812 }
813 hasLINKEDIT = true;
814 }
815 else if ( (info.protections & 0xFFFFFFF8) != 0 ) {
816 diag.error("in '%s' %s segment permissions has invalid bits set", path, info.segName);
817 badPermissions = true;
818 stop = true;
819 }
820 if ( greaterThanAddOrOverflow(info.fileOffset, info.fileSize, fileLen) ) {
821 diag.error("in '%s' %s segment content extends beyond end of file", path, info.segName);
822 badSize = true;
823 stop = true;
824 }
825 if ( is64() ) {
826 if ( info.vmAddr+info.vmSize < info.vmAddr ) {
827 diag.error("in '%s' %s segment vm range wraps", path, info.segName);
828 badSize = true;
829 stop = true;
830 }
831 }
832 else {
833 if ( (uint32_t)(info.vmAddr+info.vmSize) < (uint32_t)(info.vmAddr) ) {
834 diag.error("in '%s' %s segment vm range wraps", path, info.segName);
835 badSize = true;
836 stop = true;
837 }
838 }
839 });
840 if ( badPermissions || badSize )
841 return false;
842 if ( !hasTEXT ) {
843 diag.error("in '%s' missing __TEXT segment", path);
844 return false;
845 }
846 if ( !hasLINKEDIT ) {
847 diag.error("in '%s' missing __LINKEDIT segment", path);
848 return false;
849 }
850
851 // check for overlapping segments
852 __block bool badSegments = false;
853 forEachSegment(^(const SegmentInfo& info1, bool& stop1) {
854 uint64_t seg1vmEnd = info1.vmAddr + info1.vmSize;
855 uint64_t seg1FileEnd = info1.fileOffset + info1.fileSize;
856 forEachSegment(^(const SegmentInfo& info2, bool& stop2) {
857 if ( info1.segIndex == info2.segIndex )
858 return;
859 uint64_t seg2vmEnd = info2.vmAddr + info2.vmSize;
860 uint64_t seg2FileEnd = info2.fileOffset + info2.fileSize;
861 if ( ((info2.vmAddr <= info1.vmAddr) && (seg2vmEnd > info1.vmAddr) && (seg1vmEnd > info1.vmAddr )) || ((info2.vmAddr >= info1.vmAddr ) && (info2.vmAddr < seg1vmEnd) && (seg2vmEnd > info2.vmAddr)) ) {
862 diag.error("in '%s' segment %s vm range overlaps segment %s", path, info1.segName, info2.segName);
863 badSegments = true;
864 stop1 = true;
865 stop2 = true;
866 }
867 if ( ((info2.fileOffset <= info1.fileOffset) && (seg2FileEnd > info1.fileOffset) && (seg1FileEnd > info1.fileOffset)) || ((info2.fileOffset >= info1.fileOffset) && (info2.fileOffset < seg1FileEnd) && (seg2FileEnd > info2.fileOffset )) ) {
868 diag.error("in '%s' segment %s file content overlaps segment %s", path, info1.segName, info2.segName);
869 badSegments = true;
870 stop1 = true;
871 stop2 = true;
872 }
873 if ( (info1.segIndex < info2.segIndex) && !stop1 ) {
874 if ( (info1.vmAddr > info2.vmAddr) || ((info1.fileOffset > info2.fileOffset ) && (info1.fileOffset != 0) && (info2.fileOffset != 0)) ){
875 if ( !inDyldCache() && enforceFormat(Malformed::segmentOrder) && !isStaticExecutable() ) {
876 // dyld cache __DATA_* segments are moved around
877 // The static kernel also has segments with vmAddr's before __TEXT
878 diag.error("in '%s' segment load commands out of order with respect to layout for %s and %s", path, info1.segName, info2.segName);
879 badSegments = true;
880 stop1 = true;
881 stop2 = true;
882 }
883 }
884 }
885 });
886 });
887 if ( badSegments )
888 return false;
889
890 // check sections are within segment
891 __block bool badSections = false;
892 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
893 if ( cmd->cmd == LC_SEGMENT_64 ) {
894 const segment_command_64* seg = (segment_command_64*)cmd;
895 const section_64* const sectionsStart = (section_64*)((char*)seg + sizeof(struct segment_command_64));
896 const section_64* const sectionsEnd = &sectionsStart[seg->nsects];
897 for (const section_64* sect=sectionsStart; (sect < sectionsEnd); ++sect) {
898 if ( (int64_t)(sect->size) < 0 ) {
899 diag.error("in '%s' section '%s' size too large 0x%llX", path, sect->sectname, sect->size);
900 badSections = true;
901 }
902 else if ( sect->addr < seg->vmaddr ) {
903 diag.error("in '%s' section '%s' start address 0x%llX is before containing segment's address 0x%0llX", path, sect->sectname, sect->addr, seg->vmaddr);
904 badSections = true;
905 }
906 else if ( sect->addr+sect->size > seg->vmaddr+seg->vmsize ) {
907 bool ignoreError = !enforceFormat(Malformed::sectionsAddrRangeWithinSegment);
908 #if BUILDING_APP_CACHE_UTIL
909 if ( (seg->vmsize == 0) && !strcmp(seg->segname, "__CTF") )
910 ignoreError = true;
911 #endif
912 if ( !ignoreError ) {
913 diag.error("in '%s' section '%s' end address 0x%llX is beyond containing segment's end address 0x%0llX", path, sect->sectname, sect->addr+sect->size, seg->vmaddr+seg->vmsize);
914 badSections = true;
915 }
916 }
917 }
918 }
919 else if ( cmd->cmd == LC_SEGMENT ) {
920 const segment_command* seg = (segment_command*)cmd;
921 const section* const sectionsStart = (section*)((char*)seg + sizeof(struct segment_command));
922 const section* const sectionsEnd = &sectionsStart[seg->nsects];
923 for (const section* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) {
924 if ( (int64_t)(sect->size) < 0 ) {
925 diag.error("in '%s' section %s size too large 0x%X", path, sect->sectname, sect->size);
926 badSections = true;
927 }
928 else if ( sect->addr < seg->vmaddr ) {
929 diag.error("in '%s' section %s start address 0x%X is before containing segment's address 0x%0X", path, sect->sectname, sect->addr, seg->vmaddr);
930 badSections = true;
931 }
932 else if ( sect->addr+sect->size > seg->vmaddr+seg->vmsize ) {
933 diag.error("in '%s' section %s end address 0x%X is beyond containing segment's end address 0x%0X", path, sect->sectname, sect->addr+sect->size, seg->vmaddr+seg->vmsize);
934 badSections = true;
935 }
936 }
937 }
938 });
939
940 return !badSections;
941 }
942
943
944 bool MachOAnalyzer::validMain(Diagnostics& diag, const char* path) const
945 {
946 const char* executableTextSegmentName = "__TEXT";
947 #if BUILDING_APP_CACHE_UTIL
948 // The kernel has __start in __TEXT_EXEC, or for x86_64 it's __HIB
949 if ( isStaticExecutable() ) {
950 if ( isArch("x86_64") || isArch("x86_64h") )
951 executableTextSegmentName = "__HIB";
952 else
953 executableTextSegmentName = "__TEXT_EXEC";
954 }
955 #endif
956
957 __block uint64_t textSegStartAddr = 0;
958 __block uint64_t textSegStartSize = 0;
959 forEachSegment(^(const SegmentInfo& info, bool& stop) {
960 if ( strcmp(info.segName, executableTextSegmentName) == 0 ) {
961 textSegStartAddr = info.vmAddr;
962 textSegStartSize = info.vmSize;
963 stop = true;
964 }
965 });
966
967 __block int mainCount = 0;
968 __block int threadCount = 0;
969 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
970 entry_point_command* mainCmd;
971 uint64_t startAddress;
972 switch (cmd->cmd) {
973 case LC_MAIN:
974 ++mainCount;
975 mainCmd = (entry_point_command*)cmd;
976 if ( mainCmd->entryoff >= textSegStartSize ) {
977 startAddress = preferredLoadAddress() + mainCmd->entryoff;
978 __block bool foundSegment = false;
979 forEachSegment(^(const SegmentInfo& info, bool& stopSegment) {
980 // Skip segments which don't contain this address
981 if ( (startAddress < info.vmAddr) || (startAddress >= info.vmAddr+info.vmSize) )
982 return;
983 foundSegment = true;
984 if ( (info.protections & VM_PROT_EXECUTE) == 0 )
985 diag.error("LC_MAIN points to non-executable segment");
986 stopSegment = true;
987 });
988 if (!foundSegment)
989 diag.error("LC_MAIN entryoff is out of range");
990 stop = true;
991 }
992 break;
993 case LC_UNIXTHREAD:
994 ++threadCount;
995 startAddress = entryAddrFromThreadCmd((thread_command*)cmd);
996 if ( startAddress == 0 ) {
997 diag.error("LC_UNIXTHREAD not valid for arch %s", archName());
998 stop = true;
999 }
1000 #if BUILDING_DYLDINFO
1001 else if ( isStaticExecutable() ) {
1002 __block bool foundSegment = false;
1003 forEachSegment(^(const SegmentInfo& info, bool& stopSegment) {
1004 // Skip segments which don't contain this address
1005 if ( (startAddress < info.vmAddr) || (startAddress >= info.vmAddr+info.vmSize) )
1006 return;
1007 foundSegment = true;
1008 if ( (info.protections & VM_PROT_EXECUTE) == 0 )
1009 diag.error("LC_UNIXTHREAD points to non-executable segment");
1010 stopSegment = true;
1011 });
1012 if (!foundSegment)
1013 diag.error("LC_UNIXTHREAD entry is out of range");
1014 stop = true;
1015 }
1016 #endif
1017 else if ( (startAddress < textSegStartAddr) || (startAddress >= textSegStartAddr+textSegStartSize) ) {
1018 diag.error("LC_UNIXTHREAD entry not in %s segment", executableTextSegmentName);
1019 stop = true;
1020 }
1021 break;
1022 }
1023 });
1024 if ( diag.hasError() )
1025 return false;
1026
1027 if ( this->builtForPlatform(Platform::driverKit) ) {
1028 if ( mainCount + threadCount == 0 )
1029 return true;
1030 diag.error("no LC_MAIN allowed for driverkit");
1031 return false;
1032 }
1033
1034 if ( mainCount+threadCount == 1 )
1035 return true;
1036
1037 if ( mainCount + threadCount == 0 )
1038 diag.error("missing LC_MAIN or LC_UNIXTHREAD");
1039 else
1040 diag.error("only one LC_MAIN or LC_UNIXTHREAD is allowed");
1041 return false;
1042 }
1043
1044
1045 namespace {
1046 struct LinkEditContentChunk
1047 {
1048 const char* name;
1049 uint32_t alignment;
1050 uint32_t fileOffsetStart;
1051 uint32_t size;
1052
1053 static int compareByFileOffset(const void* l, const void* r) {
1054 if ( ((LinkEditContentChunk*)l)->fileOffsetStart < ((LinkEditContentChunk*)r)->fileOffsetStart )
1055 return -1;
1056 else
1057 return 1;
1058 }
1059 };
1060 } // anonymous namespace
1061
1062
1063
1064 bool MachOAnalyzer::validLinkeditLayout(Diagnostics& diag, const char* path) const
1065 {
1066 LinkEditInfo leInfo;
1067 getLinkEditPointers(diag, leInfo);
1068 if ( diag.hasError() )
1069 return false;
1070 const uint32_t ptrSize = pointerSize();
1071
1072 // build vector of all blobs in LINKEDIT
1073 LinkEditContentChunk blobs[32];
1074 LinkEditContentChunk* bp = blobs;
1075 if ( leInfo.dyldInfo != nullptr ) {
1076 if ( leInfo.dyldInfo->rebase_size != 0 )
1077 *bp++ = {"rebase opcodes", ptrSize, leInfo.dyldInfo->rebase_off, leInfo.dyldInfo->rebase_size};
1078 if ( leInfo.dyldInfo->bind_size != 0 )
1079 *bp++ = {"bind opcodes", ptrSize, leInfo.dyldInfo->bind_off, leInfo.dyldInfo->bind_size};
1080 if ( leInfo.dyldInfo->weak_bind_size != 0 )
1081 *bp++ = {"weak bind opcodes", ptrSize, leInfo.dyldInfo->weak_bind_off, leInfo.dyldInfo->weak_bind_size};
1082 if ( leInfo.dyldInfo->lazy_bind_size != 0 )
1083 *bp++ = {"lazy bind opcodes", ptrSize, leInfo.dyldInfo->lazy_bind_off, leInfo.dyldInfo->lazy_bind_size};
1084 if ( leInfo.dyldInfo->export_size!= 0 )
1085 *bp++ = {"exports trie", ptrSize, leInfo.dyldInfo->export_off, leInfo.dyldInfo->export_size};
1086 }
1087 if ( leInfo.exportsTrie != nullptr ) {
1088 if ( leInfo.exportsTrie->datasize != 0 )
1089 *bp++ = {"exports trie", ptrSize, leInfo.exportsTrie->dataoff, leInfo.exportsTrie->datasize};
1090 }
1091 if ( leInfo.chainedFixups != nullptr ) {
1092 if ( leInfo.chainedFixups->datasize != 0 )
1093 *bp++ = {"chained fixups", ptrSize, leInfo.chainedFixups->dataoff, leInfo.chainedFixups->datasize};
1094 }
1095
1096 if ( leInfo.dynSymTab != nullptr ) {
1097 if ( leInfo.dynSymTab->nlocrel != 0 )
1098 *bp++ = {"local relocations", ptrSize, leInfo.dynSymTab->locreloff, static_cast<uint32_t>(leInfo.dynSymTab->nlocrel*sizeof(relocation_info))};
1099 if ( leInfo.dynSymTab->nextrel != 0 )
1100 *bp++ = {"external relocations", ptrSize, leInfo.dynSymTab->extreloff, static_cast<uint32_t>(leInfo.dynSymTab->nextrel*sizeof(relocation_info))};
1101 if ( leInfo.dynSymTab->nindirectsyms != 0 )
1102 *bp++ = {"indirect symbol table", 4, leInfo.dynSymTab->indirectsymoff, leInfo.dynSymTab->nindirectsyms*4};
1103 }
1104 if ( leInfo.splitSegInfo != nullptr ) {
1105 if ( leInfo.splitSegInfo->datasize != 0 )
1106 *bp++ = {"shared cache info", ptrSize, leInfo.splitSegInfo->dataoff, leInfo.splitSegInfo->datasize};
1107 }
1108 if ( leInfo.functionStarts != nullptr ) {
1109 if ( leInfo.functionStarts->datasize != 0 )
1110 *bp++ = {"function starts", ptrSize, leInfo.functionStarts->dataoff, leInfo.functionStarts->datasize};
1111 }
1112 if ( leInfo.dataInCode != nullptr ) {
1113 if ( leInfo.dataInCode->datasize != 0 )
1114 *bp++ = {"data in code", ptrSize, leInfo.dataInCode->dataoff, leInfo.dataInCode->datasize};
1115 }
1116 if ( leInfo.symTab != nullptr ) {
1117 if ( leInfo.symTab->nsyms != 0 )
1118 *bp++ = {"symbol table", ptrSize, leInfo.symTab->symoff, static_cast<uint32_t>(leInfo.symTab->nsyms*(ptrSize == 8 ? sizeof(nlist_64) : sizeof(struct nlist)))};
1119 if ( leInfo.symTab->strsize != 0 )
1120 *bp++ = {"symbol table strings", 1, leInfo.symTab->stroff, leInfo.symTab->strsize};
1121 }
1122 if ( leInfo.codeSig != nullptr ) {
1123 if ( leInfo.codeSig->datasize != 0 )
1124 *bp++ = {"code signature", ptrSize, leInfo.codeSig->dataoff, leInfo.codeSig->datasize};
1125 }
1126
1127 // check for bad combinations
1128 if ( (leInfo.dyldInfo != nullptr) && (leInfo.dyldInfo->cmd == LC_DYLD_INFO_ONLY) && (leInfo.dynSymTab != nullptr) ) {
1129 if ( (leInfo.dynSymTab->nlocrel != 0) && enforceFormat(Malformed::dyldInfoAndlocalRelocs) ) {
1130 diag.error("in '%s' malformed mach-o contains LC_DYLD_INFO_ONLY and local relocations", path);
1131 return false;
1132 }
1133 if ( leInfo.dynSymTab->nextrel != 0 ) {
1134 diag.error("in '%s' malformed mach-o contains LC_DYLD_INFO_ONLY and external relocations", path);
1135 return false;
1136 }
1137 }
1138
1139 bool checkMissingDyldInfo = true;
1140 #if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL
1141 checkMissingDyldInfo = !isFileSet() && !isStaticExecutable() && !isKextBundle();
1142 #endif
1143 if ( (leInfo.dyldInfo == nullptr) && (leInfo.dynSymTab == nullptr) && checkMissingDyldInfo ) {
1144 diag.error("in '%s' malformed mach-o misssing LC_DYLD_INFO and LC_DYSYMTAB", path);
1145 return false;
1146 }
1147
1148 // FIXME: Remove this hack
1149 #if BUILDING_APP_CACHE_UTIL
1150 if ( isFileSet() )
1151 return true;
1152 #endif
1153
1154 const unsigned long blobCount = bp - blobs;
1155 if ( blobCount == 0 ) {
1156 diag.error("in '%s' malformed mach-o missing LINKEDIT", path);
1157 return false;
1158 }
1159
1160 uint32_t linkeditFileEnd = leInfo.layout.linkeditFileOffset + leInfo.layout.linkeditFileSize;
1161
1162
1163 // sort blobs by file-offset and error on overlaps
1164 ::qsort(blobs, blobCount, sizeof(LinkEditContentChunk), &LinkEditContentChunk::compareByFileOffset);
1165 uint32_t prevEnd = leInfo.layout.linkeditFileOffset;
1166 const char* prevName = "start of LINKEDIT";
1167 for (unsigned long i=0; i < blobCount; ++i) {
1168 const LinkEditContentChunk& blob = blobs[i];
1169 if ( blob.fileOffsetStart < prevEnd ) {
1170 diag.error("in '%s' LINKEDIT overlap of %s and %s", path, prevName, blob.name);
1171 return false;
1172 }
1173 if (greaterThanAddOrOverflow(blob.fileOffsetStart, blob.size, linkeditFileEnd)) {
1174 diag.error("in '%s' LINKEDIT content '%s' extends beyond end of segment", path, blob.name);
1175 return false;
1176 }
1177 if ( (blob.fileOffsetStart & (blob.alignment-1)) != 0 ) {
1178 // <rdar://problem/51115705> relax code sig alignment for pre iOS13
1179 Malformed kind = (strcmp(blob.name, "code signature") == 0) ? Malformed::codeSigAlignment : Malformed::linkeditAlignment;
1180 if ( enforceFormat(kind) )
1181 diag.error("in '%s' mis-aligned LINKEDIT content '%s'", path, blob.name);
1182 }
1183 prevEnd = blob.fileOffsetStart + blob.size;
1184 prevName = blob.name;
1185 }
1186
1187 // Check for invalid symbol table sizes
1188 if ( leInfo.symTab != nullptr ) {
1189 if ( leInfo.symTab->nsyms > 0x10000000 ) {
1190 diag.error("in '%s' malformed mach-o image: symbol table too large", path);
1191 return false;
1192 }
1193 if ( leInfo.dynSymTab != nullptr ) {
1194 // validate indirect symbol table
1195 if ( leInfo.dynSymTab->nindirectsyms != 0 ) {
1196 if ( leInfo.dynSymTab->nindirectsyms > 0x10000000 ) {
1197 diag.error("in '%s' malformed mach-o image: indirect symbol table too large", path);
1198 return false;
1199 }
1200 }
1201 if ( (leInfo.dynSymTab->nlocalsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->ilocalsym > leInfo.symTab->nsyms) ) {
1202 diag.error("in '%s' malformed mach-o image: indirect symbol table local symbol count exceeds total symbols", path);
1203 return false;
1204 }
1205 if ( leInfo.dynSymTab->ilocalsym + leInfo.dynSymTab->nlocalsym < leInfo.dynSymTab->ilocalsym ) {
1206 diag.error("in '%s' malformed mach-o image: indirect symbol table local symbol count wraps", path);
1207 return false;
1208 }
1209 if ( (leInfo.dynSymTab->nextdefsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->iextdefsym > leInfo.symTab->nsyms) ) {
1210 diag.error("in '%s' malformed mach-o image: indirect symbol table extern symbol count exceeds total symbols", path);
1211 return false;
1212 }
1213 if ( leInfo.dynSymTab->iextdefsym + leInfo.dynSymTab->nextdefsym < leInfo.dynSymTab->iextdefsym ) {
1214 diag.error("in '%s' malformed mach-o image: indirect symbol table extern symbol count wraps", path);
1215 return false;
1216 }
1217 if ( (leInfo.dynSymTab->nundefsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->iundefsym > leInfo.symTab->nsyms) ) {
1218 diag.error("in '%s' malformed mach-o image: indirect symbol table undefined symbol count exceeds total symbols", path);
1219 return false;
1220 }
1221 if ( leInfo.dynSymTab->iundefsym + leInfo.dynSymTab->nundefsym < leInfo.dynSymTab->iundefsym ) {
1222 diag.error("in '%s' malformed mach-o image: indirect symbol table undefined symbol count wraps", path);
1223 return false;
1224 }
1225 }
1226 }
1227
1228 return true;
1229 }
1230
1231
1232
1233 bool MachOAnalyzer::invalidRebaseState(Diagnostics& diag, const char* opcodeName, const char* path, const LinkEditInfo& leInfo, const SegmentInfo segments[],
1234 bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind) const
1235 {
1236 if ( !segIndexSet ) {
1237 diag.error("in '%s' %s missing preceding REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", path, opcodeName);
1238 return true;
1239 }
1240 if ( segmentIndex >= leInfo.layout.linkeditSegIndex ) {
1241 diag.error("in '%s' %s segment index %d too large", path, opcodeName, segmentIndex);
1242 return true;
1243 }
1244 if ( segmentOffset > (segments[segmentIndex].vmSize-ptrSize) ) {
1245 diag.error("in '%s' %s current segment offset 0x%08llX beyond segment size (0x%08llX)", path, opcodeName, segmentOffset, segments[segmentIndex].vmSize);
1246 return true;
1247 }
1248 switch ( kind ) {
1249 case Rebase::pointer32:
1250 case Rebase::pointer64:
1251 if ( !segments[segmentIndex].writable() && enforceFormat(Malformed::writableData) ) {
1252 diag.error("in '%s' %s pointer rebase is in non-writable segment", path, opcodeName);
1253 return true;
1254 }
1255 if ( segments[segmentIndex].executable() && enforceFormat(Malformed::executableData) ) {
1256 diag.error("in '%s' %s pointer rebase is in executable segment", path, opcodeName);
1257 return true;
1258 }
1259 break;
1260 case Rebase::textAbsolute32:
1261 case Rebase::textPCrel32:
1262 if ( !segments[segmentIndex].textRelocs ) {
1263 diag.error("in '%s' %s text rebase is in segment that does not support text relocations", path, opcodeName);
1264 return true;
1265 }
1266 if ( segments[segmentIndex].writable() ) {
1267 diag.error("in '%s' %s text rebase is in writable segment", path, opcodeName);
1268 return true;
1269 }
1270 if ( !segments[segmentIndex].executable() ) {
1271 diag.error("in '%s' %s pointer rebase is in non-executable segment", path, opcodeName);
1272 return true;
1273 }
1274 break;
1275 case Rebase::unknown:
1276 diag.error("in '%s' %s unknown rebase type", path, opcodeName);
1277 return true;
1278 }
1279 return false;
1280 }
1281
1282
1283 void MachOAnalyzer::getAllSegmentsInfos(Diagnostics& diag, SegmentInfo segments[]) const
1284 {
1285 forEachSegment(^(const SegmentInfo& info, bool& stop) {
1286 segments[info.segIndex] = info;
1287 });
1288 }
1289
1290
1291 bool MachOAnalyzer::validRebaseInfo(Diagnostics& diag, const char* path) const
1292 {
1293 forEachRebase(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
1294 bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind, bool& stop) {
1295 if ( invalidRebaseState(diag, opcodeName, path, leInfo, segments, segIndexSet, ptrSize, segmentIndex, segmentOffset, kind) )
1296 stop = true;
1297 });
1298 return diag.noError();
1299 }
1300
1301
1302 void MachOAnalyzer::forEachTextRebase(Diagnostics& diag, void (^handler)(uint64_t runtimeOffset, bool& stop)) const
1303 {
1304 __block bool startVmAddrSet = false;
1305 __block uint64_t startVmAddr = 0;
1306 forEachRebase(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
1307 bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind, bool& stop) {
1308 if ( kind != Rebase::textAbsolute32 )
1309 return;
1310 if ( !startVmAddrSet ) {
1311 for (int i=0; i <= segmentIndex; ++i) {
1312 if ( strcmp(segments[i].segName, "__TEXT") == 0 ) {
1313 startVmAddr = segments[i].vmAddr;
1314 startVmAddrSet = true;
1315 break;
1316 }
1317 }
1318 }
1319 uint64_t rebaseVmAddr = segments[segmentIndex].vmAddr + segmentOffset;
1320 uint64_t runtimeOffset = rebaseVmAddr - startVmAddr;
1321 handler(runtimeOffset, stop);
1322 });
1323 }
1324
1325 void MachOAnalyzer::forEachRebase(Diagnostics& diag, void (^callback)(uint64_t runtimeOffset, bool isLazyPointerRebase, bool& stop)) const
1326 {
1327 __block bool startVmAddrSet = false;
1328 __block uint64_t startVmAddr = 0;
1329 __block uint64_t lpVmAddr = 0;
1330 __block uint64_t lpEndVmAddr = 0;
1331 __block uint64_t shVmAddr = 0;
1332 __block uint64_t shEndVmAddr = 0;
1333 forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool &stop) {
1334 if ( (info.sectFlags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) {
1335 lpVmAddr = info.sectAddr;
1336 lpEndVmAddr = info.sectAddr + info.sectSize;
1337 }
1338 else if ( (info.sectFlags & S_ATTR_PURE_INSTRUCTIONS) && (strcmp(info.sectName, "__stub_helper") == 0) ) {
1339 shVmAddr = info.sectAddr;
1340 shEndVmAddr = info.sectAddr + info.sectSize;
1341 }
1342 });
1343 forEachRebase(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
1344 bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind, bool& stop) {
1345 switch ( kind ) {
1346 case Rebase::unknown:
1347 return;
1348 case Rebase::pointer32:
1349 case Rebase::pointer64:
1350 // We only handle these kinds for now.
1351 break;
1352 case Rebase::textPCrel32:
1353 case Rebase::textAbsolute32:
1354 return;
1355 }
1356 if ( !startVmAddrSet ) {
1357 for (int i=0; i < segmentIndex; ++i) {
1358 if ( strcmp(segments[i].segName, "__TEXT") == 0 ) {
1359 startVmAddr = segments[i].vmAddr;
1360 startVmAddrSet = true;
1361 break;
1362 }
1363 }
1364 }
1365 uint64_t rebaseVmAddr = segments[segmentIndex].vmAddr + segmentOffset;
1366 bool isLazyPointerRebase = false;
1367 if ( (rebaseVmAddr >= lpVmAddr) && (rebaseVmAddr < lpEndVmAddr) ) {
1368 // rebase is in lazy pointer section
1369 uint64_t lpValue = 0;
1370 if ( ptrSize == 8 )
1371 lpValue = *((uint64_t*)(rebaseVmAddr-startVmAddr+(uint8_t*)this));
1372 else
1373 lpValue = *((uint32_t*)(rebaseVmAddr-startVmAddr+(uint8_t*)this));
1374 if ( (lpValue >= shVmAddr) && (lpValue < shEndVmAddr) ) {
1375 // content is into stub_helper section
1376 uint64_t lpTargetImageOffset = lpValue - startVmAddr;
1377 const uint8_t* helperContent = (uint8_t*)this + lpTargetImageOffset;
1378 bool isLazyStub = contentIsRegularStub(helperContent);
1379 // ignore rebases for normal lazy pointers, but leave rebase for resolver helper stub
1380 if ( isLazyStub )
1381 isLazyPointerRebase = true;
1382 }
1383 else {
1384 // if lazy pointer does not point into stub_helper, then it points to weak-def symbol and we need rebase
1385 }
1386 }
1387 uint64_t runtimeOffset = rebaseVmAddr - startVmAddr;
1388 callback(runtimeOffset, isLazyPointerRebase, stop);
1389 });
1390 }
1391
1392
1393
1394 void MachOAnalyzer::forEachRebase(Diagnostics& diag, bool ignoreLazyPointers, void (^handler)(uint64_t runtimeOffset, bool& stop)) const
1395 {
1396 forEachRebase(diag, ^(uint64_t runtimeOffset, bool isLazyPointerRebase, bool& stop) {
1397 if ( isLazyPointerRebase && ignoreLazyPointers )
1398 return;
1399 handler(runtimeOffset, stop);
1400 });
1401 }
1402
1403 bool MachOAnalyzer::hasStompedLazyOpcodes() const
1404 {
1405 // if first eight bytes of lazy opcodes are zeros, then the opcodes have been stomped
1406 bool result = false;
1407 uint32_t size;
1408 if ( const uint8_t* p = (uint8_t*)getLazyBindOpcodes(size) ) {
1409 if ( size > 8 ) {
1410 uint64_t content;
1411 memcpy(&content, p, 8);
1412 if ( content == 0 )
1413 result = true;
1414 }
1415 }
1416
1417 return result;
1418 }
1419
1420 bool MachOAnalyzer::contentIsRegularStub(const uint8_t* helperContent) const
1421 {
1422 switch (this->cputype) {
1423 case CPU_TYPE_X86_64:
1424 return ( (helperContent[0] == 0x68) && (helperContent[5] == 0xE9) ); // push $xxx / JMP pcRel
1425 case CPU_TYPE_I386:
1426 return ( (helperContent[0] == 0x68) && (helperContent[5] == 0xFF) && (helperContent[2] == 0x26) ); // push $xxx / JMP *pcRel
1427 case CPU_TYPE_ARM:
1428 return ( (helperContent[0] == 0x00) && (helperContent[1] == 0xC0) && (helperContent[2] == 0x9F) && (helperContent[3] == 0xE5) ); // ldr ip, [pc, #0]
1429 case CPU_TYPE_ARM64:
1430 return ( (helperContent[0] == 0x50) && (helperContent[1] == 0x00) && (helperContent[2] == 0x00) && (helperContent[3] == 0x18) ); // ldr w16, L0
1431
1432 }
1433 return false;
1434 }
1435
1436 static int relocSorter(const void* l, const void* r) {
1437 if ( ((relocation_info*)l)->r_address < ((relocation_info*)r)->r_address )
1438 return -1;
1439 else
1440 return 1;
1441 }
1442
1443
1444 void MachOAnalyzer::forEachRebase(Diagnostics& diag,
1445 void (^handler)(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
1446 bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset,
1447 Rebase kind, bool& stop)) const
1448 {
1449 LinkEditInfo leInfo;
1450 getLinkEditPointers(diag, leInfo);
1451 if ( diag.hasError() )
1452 return;
1453
1454 BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
1455 getAllSegmentsInfos(diag, segmentsInfo);
1456 if ( diag.hasError() )
1457 return;
1458
1459 const Rebase pointerRebaseKind = is64() ? Rebase::pointer64 : Rebase::pointer32;
1460
1461 if ( leInfo.dyldInfo != nullptr ) {
1462 const uint8_t* const start = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->rebase_off);
1463 const uint8_t* const end = start + leInfo.dyldInfo->rebase_size;
1464 const uint8_t* p = start;
1465 const uint32_t ptrSize = pointerSize();
1466 Rebase kind = Rebase::unknown;
1467 int segIndex = 0;
1468 uint64_t segOffset = 0;
1469 uint64_t count;
1470 uint64_t skip;
1471 bool segIndexSet = false;
1472 bool stop = false;
1473 while ( !stop && diag.noError() && (p < end) ) {
1474 uint8_t immediate = *p & REBASE_IMMEDIATE_MASK;
1475 uint8_t opcode = *p & REBASE_OPCODE_MASK;
1476 ++p;
1477 switch (opcode) {
1478 case REBASE_OPCODE_DONE:
1479 if ( (end - p) > 8 )
1480 diag.error("rebase opcodes terminated early at offset %d of %d", (int)(p-start), (int)(end-start));
1481 stop = true;
1482 break;
1483 case REBASE_OPCODE_SET_TYPE_IMM:
1484 switch ( immediate ) {
1485 case REBASE_TYPE_POINTER:
1486 kind = pointerRebaseKind;
1487 break;
1488 case REBASE_TYPE_TEXT_ABSOLUTE32:
1489 kind = Rebase::textAbsolute32;
1490 break;
1491 case REBASE_TYPE_TEXT_PCREL32:
1492 kind = Rebase::textPCrel32;
1493 break;
1494 default:
1495 kind = Rebase::unknown;
1496 break;
1497 }
1498 break;
1499 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
1500 segIndex = immediate;
1501 segOffset = read_uleb128(diag, p, end);
1502 segIndexSet = true;
1503 break;
1504 case REBASE_OPCODE_ADD_ADDR_ULEB:
1505 segOffset += read_uleb128(diag, p, end);
1506 break;
1507 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
1508 segOffset += immediate*ptrSize;
1509 break;
1510 case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
1511 for (int i=0; i < immediate; ++i) {
1512 handler("REBASE_OPCODE_DO_REBASE_IMM_TIMES", leInfo, segmentsInfo, segIndexSet, ptrSize, segIndex, segOffset, kind, stop);
1513 segOffset += ptrSize;
1514 if ( stop )
1515 break;
1516 }
1517 break;
1518 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
1519 count = read_uleb128(diag, p, end);
1520 for (uint32_t i=0; i < count; ++i) {
1521 handler("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo, segmentsInfo, segIndexSet, ptrSize, segIndex, segOffset, kind, stop);
1522 segOffset += ptrSize;
1523 if ( stop )
1524 break;
1525 }
1526 break;
1527 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
1528 handler("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo, segmentsInfo, segIndexSet, ptrSize, segIndex, segOffset, kind, stop);
1529 segOffset += read_uleb128(diag, p, end) + ptrSize;
1530 break;
1531 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
1532 count = read_uleb128(diag, p, end);
1533 if ( diag.hasError() )
1534 break;
1535 skip = read_uleb128(diag, p, end);
1536 for (uint32_t i=0; i < count; ++i) {
1537 handler("REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB", leInfo, segmentsInfo, segIndexSet, ptrSize, segIndex, segOffset, kind, stop);
1538 segOffset += skip + ptrSize;
1539 if ( stop )
1540 break;
1541 }
1542 break;
1543 default:
1544 diag.error("unknown rebase opcode 0x%02X", opcode);
1545 }
1546 }
1547 return;
1548 }
1549
1550 if ( leInfo.chainedFixups != nullptr ) {
1551 // binary uses chained fixups, so do nothing
1552 // The kernel collections need to support both chained and classic relocations
1553 // If we are anything other than a kernel collection, then return here as we won't have
1554 // anything else to do.
1555 if ( !isFileSet() )
1556 return;
1557 }
1558
1559 if ( leInfo.dynSymTab != nullptr ) {
1560 // old binary, walk relocations
1561 const uint64_t relocsStartAddress = localRelocBaseAddress(segmentsInfo, leInfo.layout.linkeditSegIndex);
1562 const relocation_info* const relocsStart = (relocation_info*)getLinkEditContent(leInfo.layout, leInfo.dynSymTab->locreloff);
1563 const relocation_info* const relocsEnd = &relocsStart[leInfo.dynSymTab->nlocrel];
1564 bool stop = false;
1565 const uint8_t relocSize = (is64() ? 3 : 2);
1566 const uint8_t ptrSize = pointerSize();
1567 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(relocation_info, relocs, 2048);
1568 for (const relocation_info* reloc=relocsStart; (reloc < relocsEnd) && !stop; ++reloc) {
1569 if ( reloc->r_length != relocSize ) {
1570 bool shouldEmitError = true;
1571 #if BUILDING_APP_CACHE_UTIL
1572 if ( usesClassicRelocationsInKernelCollection() && (reloc->r_length == 2) && (relocSize == 3) )
1573 shouldEmitError = false;
1574 #endif
1575 if ( shouldEmitError ) {
1576 diag.error("local relocation has wrong r_length");
1577 break;
1578 }
1579 }
1580 if ( reloc->r_type != 0 ) { // 0 == X86_64_RELOC_UNSIGNED == GENERIC_RELOC_VANILLA == ARM64_RELOC_UNSIGNED
1581 diag.error("local relocation has wrong r_type");
1582 break;
1583 }
1584 relocs.push_back(*reloc);
1585 }
1586 if ( !relocs.empty() ) {
1587 ::qsort(&relocs[0], relocs.count(), sizeof(relocation_info), &relocSorter);
1588 for (relocation_info reloc : relocs) {
1589 uint32_t addrOff = reloc.r_address;
1590 uint32_t segIndex = 0;
1591 uint64_t segOffset = 0;
1592 uint64_t addr = 0;
1593 #if BUILDING_APP_CACHE_UTIL
1594 // xnu for x86_64 has __HIB mapped before __DATA, so offsets appear to be
1595 // negative
1596 if ( isStaticExecutable() || isFileSet() ) {
1597 addr = relocsStartAddress + (int32_t)addrOff;
1598 } else {
1599 addr = relocsStartAddress + addrOff;
1600 }
1601 #else
1602 addr = relocsStartAddress + addrOff;
1603 #endif
1604 if ( segIndexAndOffsetForAddress(addr, segmentsInfo, leInfo.layout.linkeditSegIndex, segIndex, segOffset) ) {
1605 Rebase kind = (reloc.r_length == 2) ? Rebase::pointer32 : Rebase::pointer64;
1606 if ( this->cputype == CPU_TYPE_I386 ) {
1607 if ( segmentsInfo[segIndex].executable() )
1608 kind = Rebase::textAbsolute32;
1609 }
1610 handler("local relocation", leInfo, segmentsInfo, true, ptrSize, segIndex, segOffset, kind, stop);
1611 }
1612 else {
1613 diag.error("local relocation has out of range r_address");
1614 break;
1615 }
1616 }
1617 }
1618 // then process indirect symbols
1619 forEachIndirectPointer(diag, ^(uint64_t address, bool bind, int bindLibOrdinal,
1620 const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& indStop) {
1621 if ( bind )
1622 return;
1623 uint32_t segIndex = 0;
1624 uint64_t segOffset = 0;
1625 if ( segIndexAndOffsetForAddress(address, segmentsInfo, leInfo.layout.linkeditSegIndex, segIndex, segOffset) ) {
1626 handler("local relocation", leInfo, segmentsInfo, true, ptrSize, segIndex, segOffset, pointerRebaseKind, indStop);
1627 }
1628 else {
1629 diag.error("local relocation has out of range r_address");
1630 indStop = true;
1631 }
1632 });
1633 }
1634 }
1635
1636 bool MachOAnalyzer::segIndexAndOffsetForAddress(uint64_t addr, const SegmentInfo segmentsInfos[], uint32_t segCount, uint32_t& segIndex, uint64_t& segOffset) const
1637 {
1638 for (uint32_t i=0; i < segCount; ++i) {
1639 if ( (segmentsInfos[i].vmAddr <= addr) && (addr < segmentsInfos[i].vmAddr+segmentsInfos[i].vmSize) ) {
1640 segIndex = i;
1641 segOffset = addr - segmentsInfos[i].vmAddr;
1642 return true;
1643 }
1644 }
1645 return false;
1646 }
1647
1648 uint64_t MachOAnalyzer::localRelocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const
1649 {
1650 if ( isArch("x86_64") || isArch("x86_64h") ) {
1651 #if BUILDING_APP_CACHE_UTIL
1652 if ( isKextBundle() ) {
1653 // for kext bundles the reloc base address starts at __TEXT segment
1654 return segmentsInfos[0].vmAddr;
1655 }
1656 #endif
1657 // for all other kinds, the x86_64 reloc base address starts at first writable segment (usually __DATA)
1658 for (uint32_t i=0; i < segCount; ++i) {
1659 if ( segmentsInfos[i].writable() )
1660 return segmentsInfos[i].vmAddr;
1661 }
1662 }
1663 return segmentsInfos[0].vmAddr;
1664 }
1665
1666 uint64_t MachOAnalyzer::externalRelocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const
1667 {
1668 // Dyld caches are too large for a raw r_address, so everything is an offset from the base address
1669 if ( inDyldCache() ) {
1670 return preferredLoadAddress();
1671 }
1672
1673 #if BUILDING_APP_CACHE_UTIL
1674 if ( isKextBundle() ) {
1675 // for kext bundles the reloc base address starts at __TEXT segment
1676 return preferredLoadAddress();
1677 }
1678 #endif
1679
1680 if ( isArch("x86_64") || isArch("x86_64h") ) {
1681 // for x86_64 reloc base address starts at first writable segment (usually __DATA)
1682 for (uint32_t i=0; i < segCount; ++i) {
1683 if ( segmentsInfos[i].writable() )
1684 return segmentsInfos[i].vmAddr;
1685 }
1686 }
1687 // For everyone else we start at 0
1688 return 0;
1689 }
1690
1691
1692
1693 void MachOAnalyzer::forEachIndirectPointer(Diagnostics& diag, void (^handler)(uint64_t pointerAddress, bool bind, int bindLibOrdinal, const char* bindSymbolName,
1694 bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& stop)) const
1695 {
1696 LinkEditInfo leInfo;
1697 getLinkEditPointers(diag, leInfo);
1698 if ( diag.hasError() )
1699 return;
1700
1701 // find lazy and non-lazy pointer sections
1702 const bool is64Bit = is64();
1703 const uint32_t* const indirectSymbolTable = (uint32_t*)getLinkEditContent(leInfo.layout, leInfo.dynSymTab->indirectsymoff);
1704 const uint32_t indirectSymbolTableCount = leInfo.dynSymTab->nindirectsyms;
1705 const uint32_t ptrSize = pointerSize();
1706 const void* symbolTable = getLinkEditContent(leInfo.layout, leInfo.symTab->symoff);
1707 const struct nlist_64* symbols64 = (nlist_64*)symbolTable;
1708 const struct nlist* symbols32 = (struct nlist*)symbolTable;
1709 const char* stringPool = (char*)getLinkEditContent(leInfo.layout, leInfo.symTab->stroff);
1710 uint32_t symCount = leInfo.symTab->nsyms;
1711 uint32_t poolSize = leInfo.symTab->strsize;
1712 __block bool stop = false;
1713
1714 // Old kexts put S_LAZY_SYMBOL_POINTERS on the __got section, even if they didn't have indirect symbols to prcess.
1715 // In that case, skip the loop as there shouldn't be anything to process
1716 if ( (indirectSymbolTableCount == 0) && isKextBundle() )
1717 return;
1718
1719 forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& sectInfo, bool malformedSectionRange, bool& sectionStop) {
1720 uint8_t sectionType = (sectInfo.sectFlags & SECTION_TYPE);
1721 bool selfModifyingStub = (sectionType == S_SYMBOL_STUBS) && (sectInfo.sectFlags & S_ATTR_SELF_MODIFYING_CODE) && (sectInfo.reserved2 == 5) && (this->cputype == CPU_TYPE_I386);
1722 if ( (sectionType != S_LAZY_SYMBOL_POINTERS) && (sectionType != S_NON_LAZY_SYMBOL_POINTERS) && !selfModifyingStub )
1723 return;
1724 if ( (flags & S_ATTR_SELF_MODIFYING_CODE) && !selfModifyingStub ) {
1725 diag.error("S_ATTR_SELF_MODIFYING_CODE section type only valid in old i386 binaries");
1726 sectionStop = true;
1727 return;
1728 }
1729 uint32_t elementSize = selfModifyingStub ? sectInfo.reserved2 : ptrSize;
1730 uint32_t elementCount = (uint32_t)(sectInfo.sectSize/elementSize);
1731 if ( greaterThanAddOrOverflow(sectInfo.reserved1, elementCount, indirectSymbolTableCount) ) {
1732 diag.error("section %s overflows indirect symbol table", sectInfo.sectName);
1733 sectionStop = true;
1734 return;
1735 }
1736
1737 for (uint32_t i=0; (i < elementCount) && !stop; ++i) {
1738 uint32_t symNum = indirectSymbolTable[sectInfo.reserved1 + i];
1739 if ( symNum == INDIRECT_SYMBOL_ABS )
1740 continue;
1741 if ( symNum == INDIRECT_SYMBOL_LOCAL ) {
1742 handler(sectInfo.sectAddr+i*elementSize, false, 0, "", false, false, false, stop);
1743 continue;
1744 }
1745 if ( symNum > symCount ) {
1746 diag.error("indirect symbol[%d] = %d which is invalid symbol index", sectInfo.reserved1 + i, symNum);
1747 sectionStop = true;
1748 return;
1749 }
1750 uint16_t n_desc = is64Bit ? symbols64[symNum].n_desc : symbols32[symNum].n_desc;
1751 uint8_t n_type = is64Bit ? symbols64[symNum].n_type : symbols32[symNum].n_type;
1752 uint32_t libOrdinal = libOrdinalFromDesc(n_desc);
1753 uint32_t strOffset = is64Bit ? symbols64[symNum].n_un.n_strx : symbols32[symNum].n_un.n_strx;
1754 if ( strOffset > poolSize ) {
1755 diag.error("symbol[%d] string offset out of range", sectInfo.reserved1 + i);
1756 sectionStop = true;
1757 return;
1758 }
1759 const char* symbolName = stringPool + strOffset;
1760 bool weakImport = (n_desc & N_WEAK_REF);
1761 bool lazy = (sectionType == S_LAZY_SYMBOL_POINTERS);
1762 // Handle defined weak def symbols which need to get a special ordinal
1763 if ( ((n_type & N_TYPE) == N_SECT) && ((n_type & N_EXT) != 0) && ((n_desc & N_WEAK_DEF) != 0) )
1764 libOrdinal = BIND_SPECIAL_DYLIB_WEAK_LOOKUP;
1765 handler(sectInfo.sectAddr+i*elementSize, true, libOrdinal, symbolName, weakImport, lazy, selfModifyingStub, stop);
1766 }
1767 sectionStop = stop;
1768 });
1769 }
1770
1771 int MachOAnalyzer::libOrdinalFromDesc(uint16_t n_desc) const
1772 {
1773 // -flat_namespace is always flat lookup
1774 if ( (this->flags & MH_TWOLEVEL) == 0 )
1775 return BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
1776
1777 // extract byte from undefined symbol entry
1778 int libIndex = GET_LIBRARY_ORDINAL(n_desc);
1779 switch ( libIndex ) {
1780 case SELF_LIBRARY_ORDINAL:
1781 return BIND_SPECIAL_DYLIB_SELF;
1782
1783 case DYNAMIC_LOOKUP_ORDINAL:
1784 return BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
1785
1786 case EXECUTABLE_ORDINAL:
1787 return BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE;
1788 }
1789
1790 return libIndex;
1791 }
1792
1793 bool MachOAnalyzer::validBindInfo(Diagnostics& diag, const char* path) const
1794 {
1795 forEachBind(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
1796 bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
1797 uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset,
1798 uint8_t type, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop) {
1799 if ( invalidBindState(diag, opcodeName, path, leInfo, segments, segIndexSet, libraryOrdinalSet, dylibCount,
1800 libOrdinal, ptrSize, segmentIndex, segmentOffset, type, symbolName) ) {
1801 stop = true;
1802 }
1803 }, ^(const char* symbolName) {
1804 });
1805 return diag.noError();
1806 }
1807
1808 bool MachOAnalyzer::invalidBindState(Diagnostics& diag, const char* opcodeName, const char* path, const LinkEditInfo& leInfo, const SegmentInfo segments[],
1809 bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal, uint32_t ptrSize,
1810 uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, const char* symbolName) const
1811 {
1812 if ( !segIndexSet ) {
1813 diag.error("in '%s' %s missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", path, opcodeName);
1814 return true;
1815 }
1816 if ( segmentIndex >= leInfo.layout.linkeditSegIndex ) {
1817 diag.error("in '%s' %s segment index %d too large", path, opcodeName, segmentIndex);
1818 return true;
1819 }
1820 if ( segmentOffset > (segments[segmentIndex].vmSize-ptrSize) ) {
1821 diag.error("in '%s' %s current segment offset 0x%08llX beyond segment size (0x%08llX)", path, opcodeName, segmentOffset, segments[segmentIndex].vmSize);
1822 return true;
1823 }
1824 if ( symbolName == NULL ) {
1825 diag.error("in '%s' %s missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM", path, opcodeName);
1826 return true;
1827 }
1828 if ( !libraryOrdinalSet ) {
1829 diag.error("in '%s' %s missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL", path, opcodeName);
1830 return true;
1831 }
1832 if ( libOrdinal > (int)dylibCount ) {
1833 diag.error("in '%s' %s has library ordinal too large (%d) max (%d)", path, opcodeName, libOrdinal, dylibCount);
1834 return true;
1835 }
1836 if ( libOrdinal < BIND_SPECIAL_DYLIB_WEAK_LOOKUP ) {
1837 diag.error("in '%s' %s has unknown library special ordinal (%d)", path, opcodeName, libOrdinal);
1838 return true;
1839 }
1840 switch ( type ) {
1841 case BIND_TYPE_POINTER:
1842 if ( !segments[segmentIndex].writable() ) {
1843 diag.error("in '%s' %s pointer bind is in non-writable segment", path, opcodeName);
1844 return true;
1845 }
1846 if ( segments[segmentIndex].executable() && enforceFormat(Malformed::executableData) ) {
1847 diag.error("in '%s' %s pointer bind is in executable segment", path, opcodeName);
1848 return true;
1849 }
1850 break;
1851 case BIND_TYPE_TEXT_ABSOLUTE32:
1852 case BIND_TYPE_TEXT_PCREL32: {
1853 // Text relocations are permitted in x86_64 kexts
1854 bool forceAllowTextRelocs = false;
1855 #if BUILDING_APP_CACHE_UTIL
1856 if ( isKextBundle() && (isArch("x86_64") || isArch("x86_64h")) )
1857 forceAllowTextRelocs = true;
1858 #endif
1859 if ( !forceAllowTextRelocs && !segments[segmentIndex].textRelocs ) {
1860 diag.error("in '%s' %s text bind is in segment that does not support text relocations", path, opcodeName);
1861 return true;
1862 }
1863 if ( segments[segmentIndex].writable() ) {
1864 diag.error("in '%s' %s text bind is in writable segment", path, opcodeName);
1865 return true;
1866 }
1867 if ( !segments[segmentIndex].executable() ) {
1868 diag.error("in '%s' %s pointer bind is in non-executable segment", path, opcodeName);
1869 return true;
1870 }
1871 break;
1872 }
1873 default:
1874 diag.error("in '%s' %s unknown bind type %d", path, opcodeName, type);
1875 return true;
1876 }
1877 return false;
1878 }
1879
1880 void MachOAnalyzer::forEachBind(Diagnostics& diag, void (^handler)(uint64_t runtimeOffset, int libOrdinal, uint8_t type, const char* symbolName,
1881 bool weakImport, bool lazyBind, uint64_t addend, bool& stop),
1882 void (^strongHandler)(const char* symbolName)) const
1883 {
1884 __block bool startVmAddrSet = false;
1885 __block uint64_t startVmAddr = 0;
1886 forEachBind(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
1887 bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
1888 uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset,
1889 uint8_t type, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop) {
1890 if ( !startVmAddrSet ) {
1891 for (int i=0; i <= segmentIndex; ++i) {
1892 if ( strcmp(segments[i].segName, "__TEXT") == 0 ) {
1893 startVmAddr = segments[i].vmAddr;
1894 startVmAddrSet = true;
1895 break;
1896 }
1897 }
1898 }
1899 uint64_t bindVmOffset = segments[segmentIndex].vmAddr + segmentOffset;
1900 uint64_t runtimeOffset = bindVmOffset - startVmAddr;
1901 handler(runtimeOffset, libOrdinal, type, symbolName, weakImport, lazyBind, addend, stop);
1902 }, ^(const char* symbolName) {
1903 strongHandler(symbolName);
1904 });
1905 }
1906
1907 void MachOAnalyzer::forEachBind(Diagnostics& diag, void (^handler)(uint64_t runtimeOffset, int libOrdinal, const char* symbolName,
1908 bool weakImport, bool lazyBind, uint64_t addend, bool& stop),
1909 void (^strongHandler)(const char* symbolName)) const
1910 {
1911 forEachBind(diag, ^(uint64_t runtimeOffset, int libOrdinal, uint8_t type, const char* symbolName,
1912 bool weakImport, bool lazyBind, uint64_t addend, bool &stop) {
1913 handler(runtimeOffset, libOrdinal, symbolName, weakImport, lazyBind, addend, stop);
1914 }, strongHandler);
1915 }
1916
1917 void MachOAnalyzer::forEachBind(Diagnostics& diag,
1918 void (^handler)(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
1919 bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
1920 uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type,
1921 const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop),
1922 void (^strongHandler)(const char* symbolName)) const
1923 {
1924 const uint32_t ptrSize = this->pointerSize();
1925 bool stop = false;
1926
1927 LinkEditInfo leInfo;
1928 getLinkEditPointers(diag, leInfo);
1929 if ( diag.hasError() )
1930 return;
1931
1932 BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
1933 getAllSegmentsInfos(diag, segmentsInfo);
1934 if ( diag.hasError() )
1935 return;
1936
1937
1938
1939 const uint32_t dylibCount = dependentDylibCount();
1940
1941 if ( leInfo.dyldInfo != nullptr ) {
1942 // process bind opcodes
1943 const uint8_t* p = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->bind_off);
1944 const uint8_t* end = p + leInfo.dyldInfo->bind_size;
1945 uint8_t type = 0;
1946 uint64_t segmentOffset = 0;
1947 uint8_t segmentIndex = 0;
1948 const char* symbolName = NULL;
1949 int libraryOrdinal = 0;
1950 bool segIndexSet = false;
1951 bool libraryOrdinalSet = false;
1952
1953 int64_t addend = 0;
1954 uint64_t count;
1955 uint64_t skip;
1956 bool weakImport = false;
1957 while ( !stop && diag.noError() && (p < end) ) {
1958 uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
1959 uint8_t opcode = *p & BIND_OPCODE_MASK;
1960 ++p;
1961 switch (opcode) {
1962 case BIND_OPCODE_DONE:
1963 stop = true;
1964 break;
1965 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
1966 libraryOrdinal = immediate;
1967 libraryOrdinalSet = true;
1968 break;
1969 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
1970 libraryOrdinal = (int)read_uleb128(diag, p, end);
1971 libraryOrdinalSet = true;
1972 break;
1973 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
1974 // the special ordinals are negative numbers
1975 if ( immediate == 0 )
1976 libraryOrdinal = 0;
1977 else {
1978 int8_t signExtended = BIND_OPCODE_MASK | immediate;
1979 libraryOrdinal = signExtended;
1980 }
1981 libraryOrdinalSet = true;
1982 break;
1983 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
1984 weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 );
1985 symbolName = (char*)p;
1986 while (*p != '\0')
1987 ++p;
1988 ++p;
1989 break;
1990 case BIND_OPCODE_SET_TYPE_IMM:
1991 type = immediate;
1992 break;
1993 case BIND_OPCODE_SET_ADDEND_SLEB:
1994 addend = read_sleb128(diag, p, end);
1995 break;
1996 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
1997 segmentIndex = immediate;
1998 segmentOffset = read_uleb128(diag, p, end);
1999 segIndexSet = true;
2000 break;
2001 case BIND_OPCODE_ADD_ADDR_ULEB:
2002 segmentOffset += read_uleb128(diag, p, end);
2003 break;
2004 case BIND_OPCODE_DO_BIND:
2005 handler("BIND_OPCODE_DO_BIND", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
2006 ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, false, addend, stop);
2007 segmentOffset += ptrSize;
2008 break;
2009 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
2010 handler("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
2011 ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, false, addend, stop);
2012 segmentOffset += read_uleb128(diag, p, end) + ptrSize;
2013 break;
2014 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
2015 handler("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
2016 ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, false, addend, stop);
2017 segmentOffset += immediate*ptrSize + ptrSize;
2018 break;
2019 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
2020 count = read_uleb128(diag, p, end);
2021 skip = read_uleb128(diag, p, end);
2022 for (uint32_t i=0; i < count; ++i) {
2023 handler("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
2024 ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, false, addend, stop);
2025 segmentOffset += skip + ptrSize;
2026 if ( stop )
2027 break;
2028 }
2029 break;
2030 default:
2031 diag.error("bad bind opcode 0x%02X", *p);
2032 }
2033 }
2034 if ( diag.hasError() )
2035 return;
2036
2037 // process lazy bind opcodes
2038 uint32_t lazyDoneCount = 0;
2039 uint32_t lazyBindCount = 0;
2040 if ( leInfo.dyldInfo->lazy_bind_size != 0 ) {
2041 p = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->lazy_bind_off);
2042 end = p + leInfo.dyldInfo->lazy_bind_size;
2043 type = BIND_TYPE_POINTER;
2044 segmentOffset = 0;
2045 segmentIndex = 0;
2046 symbolName = NULL;
2047 libraryOrdinal = 0;
2048 segIndexSet = false;
2049 libraryOrdinalSet= false;
2050 addend = 0;
2051 weakImport = false;
2052 stop = false;
2053 while ( !stop && diag.noError() && (p < end) ) {
2054 uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
2055 uint8_t opcode = *p & BIND_OPCODE_MASK;
2056 ++p;
2057 switch (opcode) {
2058 case BIND_OPCODE_DONE:
2059 // this opcode marks the end of each lazy pointer binding
2060 ++lazyDoneCount;
2061 break;
2062 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
2063 libraryOrdinal = immediate;
2064 libraryOrdinalSet = true;
2065 break;
2066 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
2067 libraryOrdinal = (int)read_uleb128(diag, p, end);
2068 libraryOrdinalSet = true;
2069 break;
2070 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
2071 // the special ordinals are negative numbers
2072 if ( immediate == 0 )
2073 libraryOrdinal = 0;
2074 else {
2075 int8_t signExtended = BIND_OPCODE_MASK | immediate;
2076 libraryOrdinal = signExtended;
2077 }
2078 libraryOrdinalSet = true;
2079 break;
2080 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
2081 weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 );
2082 symbolName = (char*)p;
2083 while (*p != '\0')
2084 ++p;
2085 ++p;
2086 break;
2087 case BIND_OPCODE_SET_ADDEND_SLEB:
2088 addend = read_sleb128(diag, p, end);
2089 break;
2090 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
2091 segmentIndex = immediate;
2092 segmentOffset = read_uleb128(diag, p, end);
2093 segIndexSet = true;
2094 break;
2095 case BIND_OPCODE_DO_BIND:
2096 handler("BIND_OPCODE_DO_BIND", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
2097 ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, true, addend, stop);
2098 segmentOffset += ptrSize;
2099 ++lazyBindCount;
2100 break;
2101 case BIND_OPCODE_SET_TYPE_IMM:
2102 case BIND_OPCODE_ADD_ADDR_ULEB:
2103 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
2104 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
2105 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
2106 default:
2107 diag.error("bad lazy bind opcode 0x%02X", opcode);
2108 break;
2109 }
2110 }
2111 if ( lazyDoneCount > lazyBindCount+7 ) {
2112 // diag.error("lazy bind opcodes missing binds");
2113 }
2114 }
2115 if ( diag.hasError() )
2116 return;
2117
2118 // process weak bind info
2119 if ( leInfo.dyldInfo->weak_bind_size != 0 ) {
2120 p = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->weak_bind_off);
2121 end = p + leInfo.dyldInfo->weak_bind_size;
2122 type = BIND_TYPE_POINTER;
2123 segmentOffset = 0;
2124 segmentIndex = 0;
2125 symbolName = NULL;
2126 libraryOrdinal = BIND_SPECIAL_DYLIB_WEAK_LOOKUP;
2127 segIndexSet = false;
2128 libraryOrdinalSet= true;
2129 addend = 0;
2130 weakImport = false;
2131 stop = false;
2132 while ( !stop && diag.noError() && (p < end) ) {
2133 uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
2134 uint8_t opcode = *p & BIND_OPCODE_MASK;
2135 ++p;
2136 switch (opcode) {
2137 case BIND_OPCODE_DONE:
2138 stop = true;
2139 break;
2140 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
2141 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
2142 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
2143 diag.error("unexpected dylib ordinal in weak_bind");
2144 break;
2145 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
2146 weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 );
2147 symbolName = (char*)p;
2148 while (*p != '\0')
2149 ++p;
2150 ++p;
2151 if ( immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION ) {
2152 strongHandler(symbolName);
2153 }
2154 break;
2155 case BIND_OPCODE_SET_TYPE_IMM:
2156 type = immediate;
2157 break;
2158 case BIND_OPCODE_SET_ADDEND_SLEB:
2159 addend = read_sleb128(diag, p, end);
2160 break;
2161 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
2162 segmentIndex = immediate;
2163 segmentOffset = read_uleb128(diag, p, end);
2164 segIndexSet = true;
2165 break;
2166 case BIND_OPCODE_ADD_ADDR_ULEB:
2167 segmentOffset += read_uleb128(diag, p, end);
2168 break;
2169 case BIND_OPCODE_DO_BIND:
2170 handler("BIND_OPCODE_DO_BIND", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
2171 ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, false, addend, stop);
2172 segmentOffset += ptrSize;
2173 break;
2174 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
2175 handler("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
2176 ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, false, addend, stop);
2177 segmentOffset += read_uleb128(diag, p, end) + ptrSize;
2178 break;
2179 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
2180 handler("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
2181 ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, false, addend, stop);
2182 segmentOffset += immediate*ptrSize + ptrSize;
2183 break;
2184 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
2185 count = read_uleb128(diag, p, end);
2186 skip = read_uleb128(diag, p, end);
2187 for (uint32_t i=0; i < count; ++i) {
2188 handler("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
2189 ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, false, addend, stop);
2190 segmentOffset += skip + ptrSize;
2191 if ( stop )
2192 break;
2193 }
2194 break;
2195 default:
2196 diag.error("bad bind opcode 0x%02X", *p);
2197 }
2198 }
2199 }
2200 }
2201 else if ( leInfo.chainedFixups != nullptr ) {
2202 // binary uses chained fixups, so do nothing
2203 }
2204 else if ( leInfo.dynSymTab != nullptr ) {
2205 // old binary, process external relocations
2206 const uint64_t relocsStartAddress = externalRelocBaseAddress(segmentsInfo, leInfo.layout.linkeditSegIndex);
2207 const relocation_info* const relocsStart = (relocation_info*)getLinkEditContent(leInfo.layout, leInfo.dynSymTab->extreloff);
2208 const relocation_info* const relocsEnd = &relocsStart[leInfo.dynSymTab->nextrel];
2209 bool is64Bit = is64() ;
2210 const uint8_t relocSize = (is64Bit ? 3 : 2);
2211 const void* symbolTable = getLinkEditContent(leInfo.layout, leInfo.symTab->symoff);
2212 const struct nlist_64* symbols64 = (nlist_64*)symbolTable;
2213 const struct nlist* symbols32 = (struct nlist*)symbolTable;
2214 const char* stringPool = (char*)getLinkEditContent(leInfo.layout, leInfo.symTab->stroff);
2215 uint32_t symCount = leInfo.symTab->nsyms;
2216 uint32_t poolSize = leInfo.symTab->strsize;
2217 for (const relocation_info* reloc=relocsStart; (reloc < relocsEnd) && !stop; ++reloc) {
2218 bool isBranch = false;
2219 #if BUILDING_APP_CACHE_UTIL
2220 if ( isKextBundle() ) {
2221 // kext's may have other kinds of relocations, eg, branch relocs. Skip them
2222 if ( isArch("x86_64") || isArch("x86_64h") ) {
2223 if ( reloc->r_type == X86_64_RELOC_BRANCH ) {
2224 if ( reloc->r_length != 2 ) {
2225 diag.error("external relocation has wrong r_length");
2226 break;
2227 }
2228 if ( reloc->r_pcrel != true ) {
2229 diag.error("external relocation should be pcrel");
2230 break;
2231 }
2232 isBranch = true;
2233 }
2234 }
2235 }
2236 #endif
2237
2238 if ( !isBranch ) {
2239 if ( reloc->r_length != relocSize ) {
2240 diag.error("external relocation has wrong r_length");
2241 break;
2242 }
2243 if ( reloc->r_type != 0 ) { // 0 == X86_64_RELOC_UNSIGNED == GENERIC_RELOC_VANILLA == ARM64_RELOC_UNSIGNED
2244 diag.error("external relocation has wrong r_type");
2245 break;
2246 }
2247 }
2248 uint32_t segIndex = 0;
2249 uint64_t segOffset = 0;
2250 if ( segIndexAndOffsetForAddress(relocsStartAddress+reloc->r_address, segmentsInfo, leInfo.layout.linkeditSegIndex, segIndex, segOffset) ) {
2251 uint32_t symbolIndex = reloc->r_symbolnum;
2252 if ( symbolIndex > symCount ) {
2253 diag.error("external relocation has out of range r_symbolnum");
2254 break;
2255 }
2256 else {
2257 uint32_t strOffset = is64Bit ? symbols64[symbolIndex].n_un.n_strx : symbols32[symbolIndex].n_un.n_strx;
2258 uint16_t n_desc = is64Bit ? symbols64[symbolIndex].n_desc : symbols32[symbolIndex].n_desc;
2259 uint8_t n_type = is64Bit ? symbols64[symbolIndex].n_type : symbols32[symbolIndex].n_type;
2260 uint32_t libOrdinal = libOrdinalFromDesc(n_desc);
2261 if ( strOffset >= poolSize ) {
2262 diag.error("external relocation has r_symbolnum=%d which has out of range n_strx", symbolIndex);
2263 break;
2264 }
2265 else {
2266 const char* symbolName = stringPool + strOffset;
2267 bool weakImport = (n_desc & N_WEAK_REF);
2268 const uint8_t* content = (uint8_t*)this + segmentsInfo[segIndex].vmAddr - leInfo.layout.textUnslidVMAddr + segOffset;
2269 uint64_t addend = (reloc->r_length == 3) ? *((uint64_t*)content) : *((uint32_t*)content);
2270 // Handle defined weak def symbols which need to get a special ordinal
2271 if ( ((n_type & N_TYPE) == N_SECT) && ((n_type & N_EXT) != 0) && ((n_desc & N_WEAK_DEF) != 0) )
2272 libOrdinal = BIND_SPECIAL_DYLIB_WEAK_LOOKUP;
2273 uint8_t type = isBranch ? BIND_TYPE_TEXT_PCREL32 : BIND_TYPE_POINTER;
2274 handler("external relocation", leInfo, segmentsInfo, true, true, dylibCount, libOrdinal,
2275 ptrSize, segIndex, segOffset, type, symbolName, weakImport, false, addend, stop);
2276 }
2277 }
2278 }
2279 else {
2280 diag.error("local relocation has out of range r_address");
2281 break;
2282 }
2283 }
2284 // then process indirect symbols
2285 forEachIndirectPointer(diag, ^(uint64_t address, bool bind, int bindLibOrdinal,
2286 const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& indStop) {
2287 if ( !bind )
2288 return;
2289 uint32_t segIndex = 0;
2290 uint64_t segOffset = 0;
2291 if ( segIndexAndOffsetForAddress(address, segmentsInfo, leInfo.layout.linkeditSegIndex, segIndex, segOffset) ) {
2292 handler("indirect symbol", leInfo, segmentsInfo, true, true, dylibCount, bindLibOrdinal,
2293 ptrSize, segIndex, segOffset, BIND_TYPE_POINTER, bindSymbolName, bindWeakImport, bindLazy, 0, indStop);
2294 }
2295 else {
2296 diag.error("indirect symbol has out of range address");
2297 indStop = true;
2298 }
2299 });
2300 }
2301
2302 }
2303
2304 bool MachOAnalyzer::validChainedFixupsInfo(Diagnostics& diag, const char* path) const
2305 {
2306 LinkEditInfo leInfo;
2307 getLinkEditPointers(diag, leInfo);
2308 if ( diag.hasError() )
2309 return false;
2310
2311 BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
2312 getAllSegmentsInfos(diag, segmentsInfo);
2313 if ( diag.hasError() )
2314 return false;
2315
2316 // validate dyld_chained_fixups_header
2317 const dyld_chained_fixups_header* chainsHeader = (dyld_chained_fixups_header*)getLinkEditContent(leInfo.layout, leInfo.chainedFixups->dataoff);
2318 if ( chainsHeader->fixups_version != 0 ) {
2319 diag.error("chained fixups, unknown header version");
2320 return false;
2321 }
2322 if ( chainsHeader->starts_offset >= leInfo.chainedFixups->datasize ) {
2323 diag.error("chained fixups, starts_offset exceeds LC_DYLD_CHAINED_FIXUPS size");
2324 return false;
2325 }
2326 if ( chainsHeader->imports_offset > leInfo.chainedFixups->datasize ) {
2327 diag.error("chained fixups, imports_offset exceeds LC_DYLD_CHAINED_FIXUPS size");
2328 return false;
2329 }
2330
2331 uint32_t formatEntrySize;
2332 switch ( chainsHeader->imports_format ) {
2333 case DYLD_CHAINED_IMPORT:
2334 formatEntrySize = sizeof(dyld_chained_import);
2335 break;
2336 case DYLD_CHAINED_IMPORT_ADDEND:
2337 formatEntrySize = sizeof(dyld_chained_import_addend);
2338 break;
2339 case DYLD_CHAINED_IMPORT_ADDEND64:
2340 formatEntrySize = sizeof(dyld_chained_import_addend64);
2341 break;
2342 default:
2343 diag.error("chained fixups, unknown imports_format");
2344 return false;
2345 }
2346 if ( greaterThanAddOrOverflow(chainsHeader->imports_offset, (formatEntrySize * chainsHeader->imports_count), chainsHeader->symbols_offset) ) {
2347 diag.error("chained fixups, imports array overlaps symbols");
2348 return false;
2349 }
2350 if ( chainsHeader->symbols_format != 0 ) {
2351 diag.error("chained fixups, symbols_format unknown");
2352 return false;
2353 }
2354
2355 // validate dyld_chained_starts_in_image
2356 const dyld_chained_starts_in_image* startsInfo = (dyld_chained_starts_in_image*)((uint8_t*)chainsHeader + chainsHeader->starts_offset);
2357 if ( startsInfo->seg_count != leInfo.layout.linkeditSegIndex+1 ) {
2358 // We can have fewer segments than the count, so long as those we are missing have no relocs
2359 // This can happen because __CTF is inserted by ctf_insert after linking, and between __DATA and __LINKEDIT, but has no relocs
2360 // ctf_insert updates the load commands to put __CTF between __DATA and __LINKEDIT, but doesn't update the chained fixups data structures
2361 if ( startsInfo->seg_count > (leInfo.layout.linkeditSegIndex + 1) ) {
2362 diag.error("chained fixups, seg_count exceeds number of segments");
2363 return false;
2364 }
2365
2366 // We can have fewer segments than the count, so long as those we are missing have no relocs
2367 uint32_t numNoRelocSegments = 0;
2368 uint32_t numExtraSegments = (leInfo.layout.lastSegIndex + 1) - startsInfo->seg_count;
2369 for (unsigned i = 0; i != numExtraSegments; ++i) {
2370 // Check each extra segment before linkedit
2371 const SegmentInfo& segInfo = segmentsInfo[leInfo.layout.linkeditSegIndex - (i + 1)];
2372 if ( segInfo.vmSize == 0 )
2373 ++numNoRelocSegments;
2374 }
2375
2376 if ( numNoRelocSegments != numExtraSegments ) {
2377 diag.error("chained fixups, seg_count does not match number of segments");
2378 return false;
2379 }
2380 }
2381 const uint64_t baseAddress = preferredLoadAddress();
2382 uint32_t maxValidPointerSeen = 0;
2383 uint16_t pointer_format_for_all = 0;
2384 bool pointer_format_found = false;
2385 const uint8_t* endOfStarts = (uint8_t*)chainsHeader + chainsHeader->imports_offset;
2386 for (uint32_t i=0; i < startsInfo->seg_count; ++i) {
2387 uint32_t segInfoOffset = startsInfo->seg_info_offset[i];
2388 // 0 offset means this segment has no fixups
2389 if ( segInfoOffset == 0 )
2390 continue;
2391 const dyld_chained_starts_in_segment* segInfo = (dyld_chained_starts_in_segment*)((uint8_t*)startsInfo + segInfoOffset);
2392 if ( segInfo->size > (endOfStarts - (uint8_t*)segInfo) ) {
2393 diag.error("chained fixups, dyld_chained_starts_in_segment for segment #%d overruns imports table", i);
2394 return false;
2395 }
2396
2397 // validate dyld_chained_starts_in_segment
2398 if ( (segInfo->page_size != 0x1000) && (segInfo->page_size != 0x4000) ) {
2399 diag.error("chained fixups, page_size not 4KB or 16KB in segment #%d", i);
2400 return false;
2401 }
2402 if ( segInfo->pointer_format > 12 ) {
2403 diag.error("chained fixups, unknown pointer_format in segment #%d", i);
2404 return false;
2405 }
2406 if ( !pointer_format_found ) {
2407 pointer_format_for_all = segInfo->pointer_format;
2408 pointer_format_found = true;
2409 }
2410 if ( segInfo->pointer_format != pointer_format_for_all) {
2411 diag.error("chained fixups, pointer_format not same for all segments %d and %d", segInfo->pointer_format, pointer_format_for_all);
2412 return false;
2413 }
2414 if ( segInfo->segment_offset != (segmentsInfo[i].vmAddr - baseAddress) ) {
2415 diag.error("chained fixups, segment_offset does not match vmaddr from LC_SEGMENT in segment #%d", i);
2416 return false;
2417 }
2418 if ( segInfo->max_valid_pointer != 0 ) {
2419 if ( maxValidPointerSeen == 0 ) {
2420 // record max_valid_pointer values seen
2421 maxValidPointerSeen = segInfo->max_valid_pointer;
2422 }
2423 else if ( maxValidPointerSeen != segInfo->max_valid_pointer ) {
2424 diag.error("chained fixups, different max_valid_pointer values seen in different segments");
2425 return false;
2426 }
2427 }
2428 // validate starts table in segment
2429 if ( offsetof(dyld_chained_starts_in_segment, page_start[segInfo->page_count]) > segInfo->size ) {
2430 diag.error("chained fixups, page_start array overflows size");
2431 return false;
2432 }
2433 uint32_t maxOverflowIndex = (uint32_t)(segInfo->size - offsetof(dyld_chained_starts_in_segment, page_start[segInfo->page_count]))/sizeof(uint16_t);
2434 for (int pageIndex=0; pageIndex < segInfo->page_count; ++pageIndex) {
2435 uint16_t offsetInPage = segInfo->page_start[pageIndex];
2436 if ( offsetInPage == DYLD_CHAINED_PTR_START_NONE )
2437 continue;
2438 if ( (offsetInPage & DYLD_CHAINED_PTR_START_MULTI) == 0 ) {
2439 // this is the offset into the page where the first fixup is
2440 if ( offsetInPage > segInfo->page_size ) {
2441 diag.error("chained fixups, in segment #%d page_start[%d]=0x%04X exceeds page size", i, pageIndex, offsetInPage);
2442 }
2443 }
2444 else {
2445 // this is actually an index into chain_starts[]
2446 uint32_t overflowIndex = offsetInPage & ~DYLD_CHAINED_PTR_START_MULTI;
2447 // now verify all starts are within the page and in ascending order
2448 uint16_t lastOffsetInPage = 0;
2449 do {
2450 if ( overflowIndex > maxOverflowIndex ) {
2451 diag.error("chain overflow index out of range %d (max=%d) in segment %s", overflowIndex, maxOverflowIndex, segmentName(i));
2452 return false;
2453 }
2454 offsetInPage = (segInfo->page_start[overflowIndex] & ~DYLD_CHAINED_PTR_START_LAST);
2455 if ( offsetInPage > segInfo->page_size ) {
2456 diag.error("chained fixups, in segment #%d overflow page_start[%d]=0x%04X exceeds page size", i, overflowIndex, offsetInPage);
2457 return false;
2458 }
2459 if ( (offsetInPage <= lastOffsetInPage) && (lastOffsetInPage != 0) ) {
2460 diag.error("chained fixups, in segment #%d overflow page_start[%d]=0x%04X is before previous at 0x%04X\n", i, overflowIndex, offsetInPage, lastOffsetInPage);
2461 return false;
2462 }
2463 lastOffsetInPage = offsetInPage;
2464 ++overflowIndex;
2465 } while ( (segInfo->page_start[overflowIndex] & DYLD_CHAINED_PTR_START_LAST) == 0 );
2466 }
2467 }
2468
2469 }
2470 // validate import table size can fit
2471 if ( chainsHeader->imports_count != 0 ) {
2472 uint32_t maxBindOrdinal = 0;
2473 switch (pointer_format_for_all) {
2474 case DYLD_CHAINED_PTR_32:
2475 maxBindOrdinal = 0x0FFFFF; // 20-bits
2476 break;
2477 case DYLD_CHAINED_PTR_ARM64E:
2478 case DYLD_CHAINED_PTR_ARM64E_USERLAND:
2479 case DYLD_CHAINED_PTR_ARM64E_OFFSET:
2480 maxBindOrdinal = 0x00FFFF; // 16-bits
2481 break;
2482 case DYLD_CHAINED_PTR_64:
2483 case DYLD_CHAINED_PTR_64_OFFSET:
2484 case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
2485 maxBindOrdinal = 0xFFFFFF; // 24 bits
2486 break;
2487 }
2488 if ( chainsHeader->imports_count >= maxBindOrdinal ) {
2489 diag.error("chained fixups, imports_count (%d) exceeds max of %d", chainsHeader->imports_count, maxBindOrdinal);
2490 return false;
2491 }
2492 }
2493
2494 // validate max_valid_pointer is larger than last segment
2495 if ( (maxValidPointerSeen != 0) && !inDyldCache() ) {
2496 uint64_t lastSegmentLastVMAddr = segmentsInfo[leInfo.layout.linkeditSegIndex-1].vmAddr + segmentsInfo[leInfo.layout.linkeditSegIndex-1].vmSize;
2497 if ( maxValidPointerSeen < lastSegmentLastVMAddr ) {
2498 diag.error("chained fixups, max_valid_pointer too small for image");
2499 return false;
2500 }
2501 }
2502
2503 return diag.noError();
2504 }
2505
2506 bool MachOAnalyzer::validChainedFixupsInfoOldArm64e(Diagnostics& diag, const char* path) const
2507 {
2508 __block uint32_t maxTargetCount = 0;
2509 __block uint32_t currentTargetCount = 0;
2510 parseOrgArm64eChainedFixups(diag,
2511 ^(uint32_t totalTargets, bool& stop) {
2512 maxTargetCount = totalTargets;
2513 },
2514 ^(const LinkEditInfo& leInfo, const SegmentInfo segments[], bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal, uint8_t type, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
2515 if ( symbolName == NULL ) {
2516 diag.error("in '%s' missing BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM", path);
2517 }
2518 else if ( !libraryOrdinalSet ) {
2519 diag.error("in '%s' missing BIND_OPCODE_SET_DYLIB_ORDINAL", path);
2520 }
2521 else if ( libOrdinal > (int)dylibCount ) {
2522 diag.error("in '%s' has library ordinal too large (%d) max (%d)", path, libOrdinal, dylibCount);
2523 }
2524 else if ( libOrdinal < BIND_SPECIAL_DYLIB_WEAK_LOOKUP ) {
2525 diag.error("in '%s' has unknown library special ordinal (%d)", path, libOrdinal);
2526 }
2527 else if ( type != BIND_TYPE_POINTER ) {
2528 diag.error("in '%s' unknown bind type %d", path, type);
2529 }
2530 else if ( currentTargetCount > maxTargetCount ) {
2531 diag.error("in '%s' chained target counts exceeds BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB", path);
2532 }
2533 ++currentTargetCount;
2534 if ( diag.hasError() )
2535 stop = true;
2536 },
2537 ^(const LinkEditInfo& leInfo, const SegmentInfo segments[], uint8_t segmentIndex, bool segIndexSet, uint64_t segmentOffset, uint16_t format, bool& stop) {
2538 if ( !segIndexSet ) {
2539 diag.error("in '%s' missing BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", path);
2540 }
2541 else if ( segmentIndex >= leInfo.layout.linkeditSegIndex ) {
2542 diag.error("in '%s' segment index %d too large", path, segmentIndex);
2543 }
2544 else if ( segmentOffset > (segments[segmentIndex].vmSize-8) ) {
2545 diag.error("in '%s' current segment offset 0x%08llX beyond segment size (0x%08llX)", path, segmentOffset, segments[segmentIndex].vmSize);
2546 }
2547 else if ( !segments[segmentIndex].writable() ) {
2548 diag.error("in '%s' pointer bind is in non-writable segment", path);
2549 }
2550 else if ( segments[segmentIndex].executable() ) {
2551 diag.error("in '%s' pointer bind is in executable segment", path);
2552 }
2553 if ( diag.hasError() )
2554 stop = true;
2555 }
2556 );
2557
2558 return diag.noError();
2559 }
2560
2561
2562
2563 void MachOAnalyzer::parseOrgArm64eChainedFixups(Diagnostics& diag, void (^targetCount)(uint32_t totalTargets, bool& stop),
2564 void (^addTarget)(const LinkEditInfo& leInfo, const SegmentInfo segments[], bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal, uint8_t type, const char* symbolName, uint64_t addend, bool weakImport, bool& stop),
2565 void (^addChainStart)(const LinkEditInfo& leInfo, const SegmentInfo segments[], uint8_t segmentIndex, bool segIndexSet, uint64_t segmentOffset, uint16_t format, bool& stop)) const
2566 {
2567 bool stop = false;
2568
2569 LinkEditInfo leInfo;
2570 getLinkEditPointers(diag, leInfo);
2571 if ( diag.hasError() )
2572 return;
2573
2574 BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
2575 getAllSegmentsInfos(diag, segmentsInfo);
2576 if ( diag.hasError() )
2577 return;
2578
2579 const uint32_t dylibCount = dependentDylibCount();
2580
2581 if ( leInfo.dyldInfo != nullptr ) {
2582 // process bind opcodes
2583 const uint8_t* p = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->bind_off);
2584 const uint8_t* end = p + leInfo.dyldInfo->bind_size;
2585 uint8_t type = 0;
2586 uint64_t segmentOffset = 0;
2587 uint8_t segmentIndex = 0;
2588 const char* symbolName = NULL;
2589 int libraryOrdinal = 0;
2590 bool segIndexSet = false;
2591 bool libraryOrdinalSet = false;
2592 uint64_t targetTableCount;
2593 uint64_t addend = 0;
2594 bool weakImport = false;
2595 while ( !stop && diag.noError() && (p < end) ) {
2596 uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
2597 uint8_t opcode = *p & BIND_OPCODE_MASK;
2598 ++p;
2599 switch (opcode) {
2600 case BIND_OPCODE_DONE:
2601 stop = true;
2602 break;
2603 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
2604 libraryOrdinal = immediate;
2605 libraryOrdinalSet = true;
2606 break;
2607 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
2608 libraryOrdinal = (int)read_uleb128(diag, p, end);
2609 libraryOrdinalSet = true;
2610 break;
2611 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
2612 // the special ordinals are negative numbers
2613 if ( immediate == 0 )
2614 libraryOrdinal = 0;
2615 else {
2616 int8_t signExtended = BIND_OPCODE_MASK | immediate;
2617 libraryOrdinal = signExtended;
2618 }
2619 libraryOrdinalSet = true;
2620 break;
2621 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
2622 weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 );
2623 symbolName = (char*)p;
2624 while (*p != '\0')
2625 ++p;
2626 ++p;
2627 break;
2628 case BIND_OPCODE_SET_TYPE_IMM:
2629 type = immediate;
2630 break;
2631 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
2632 segmentIndex = immediate;
2633 segmentOffset = read_uleb128(diag, p, end);
2634 segIndexSet = true;
2635 break;
2636 case BIND_OPCODE_SET_ADDEND_SLEB:
2637 addend = read_sleb128(diag, p, end);
2638 break;
2639 case BIND_OPCODE_DO_BIND:
2640 if ( addTarget )
2641 addTarget(leInfo, segmentsInfo, libraryOrdinalSet, dylibCount, libraryOrdinal, type, symbolName, addend, weakImport, stop);
2642 break;
2643 case BIND_OPCODE_THREADED:
2644 switch (immediate) {
2645 case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB:
2646 targetTableCount = read_uleb128(diag, p, end);
2647 if ( targetTableCount > 65535 ) {
2648 diag.error("BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB size too large");
2649 stop = true;
2650 }
2651 else {
2652 if ( targetCount )
2653 targetCount((uint32_t)targetTableCount, stop);
2654 }
2655 break;
2656 case BIND_SUBOPCODE_THREADED_APPLY:
2657 if ( addChainStart )
2658 addChainStart(leInfo, segmentsInfo, segmentIndex, segIndexSet, segmentOffset, DYLD_CHAINED_PTR_ARM64E, stop);
2659 break;
2660 default:
2661 diag.error("bad BIND_OPCODE_THREADED sub-opcode 0x%02X", immediate);
2662 }
2663 break;
2664 default:
2665 diag.error("bad bind opcode 0x%02X", immediate);
2666 }
2667 }
2668 if ( diag.hasError() )
2669 return;
2670 }
2671 }
2672
2673 void MachOAnalyzer::forEachChainedFixupTarget(Diagnostics& diag, void (^callback)(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop)) const
2674 {
2675 LinkEditInfo leInfo;
2676 getLinkEditPointers(diag, leInfo);
2677 if ( diag.hasError() )
2678 return;
2679
2680 BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
2681 getAllSegmentsInfos(diag, segmentsInfo);
2682 if ( diag.hasError() )
2683 return;
2684
2685 bool stop = false;
2686 if ( leInfo.dyldInfo != nullptr ) {
2687 parseOrgArm64eChainedFixups(diag, nullptr, ^(const LinkEditInfo& leInfo2, const SegmentInfo segments[], bool libraryOrdinalSet, uint32_t dylibCount,
2688 int libOrdinal, uint8_t type, const char* symbolName, uint64_t fixAddend, bool weakImport, bool& stopChain) {
2689 callback(libOrdinal, symbolName, fixAddend, weakImport, stopChain);
2690 }, nullptr);
2691 }
2692 else if ( leInfo.chainedFixups != nullptr ) {
2693 const dyld_chained_fixups_header* header = (dyld_chained_fixups_header*)getLinkEditContent(leInfo.layout, leInfo.chainedFixups->dataoff);
2694 if ( (header->imports_offset > leInfo.chainedFixups->datasize) || (header->symbols_offset > leInfo.chainedFixups->datasize) ) {
2695 diag.error("malformed import table");
2696 return;
2697 }
2698 const dyld_chained_import* imports;
2699 const dyld_chained_import_addend* importsA32;
2700 const dyld_chained_import_addend64* importsA64;
2701 const char* symbolsPool = (char*)header + header->symbols_offset;
2702 uint32_t maxSymbolOffset = leInfo.chainedFixups->datasize - header->symbols_offset;
2703 int libOrdinal;
2704 switch (header->imports_format) {
2705 case DYLD_CHAINED_IMPORT:
2706 imports = (dyld_chained_import*)((uint8_t*)header + header->imports_offset);
2707 for (uint32_t i=0; i < header->imports_count && !stop; ++i) {
2708 const char* symbolName = &symbolsPool[imports[i].name_offset];
2709 if ( imports[i].name_offset > maxSymbolOffset ) {
2710 diag.error("malformed import table, string overflow");
2711 return;
2712 }
2713 uint8_t libVal = imports[i].lib_ordinal;
2714 if ( libVal > 0xF0 )
2715 libOrdinal = (int8_t)libVal;
2716 else
2717 libOrdinal = libVal;
2718 callback(libOrdinal, symbolName, 0, imports[i].weak_import, stop);
2719 }
2720 break;
2721 case DYLD_CHAINED_IMPORT_ADDEND:
2722 importsA32 = (dyld_chained_import_addend*)((uint8_t*)header + header->imports_offset);
2723 for (uint32_t i=0; i < header->imports_count && !stop; ++i) {
2724 const char* symbolName = &symbolsPool[importsA32[i].name_offset];
2725 if ( importsA32[i].name_offset > maxSymbolOffset ) {
2726 diag.error("malformed import table, string overflow");
2727 return;
2728 }
2729 uint8_t libVal = importsA32[i].lib_ordinal;
2730 if ( libVal > 0xF0 )
2731 libOrdinal = (int8_t)libVal;
2732 else
2733 libOrdinal = libVal;
2734 callback(libOrdinal, symbolName, importsA32[i].addend, importsA32[i].weak_import, stop);
2735 }
2736 break;
2737 case DYLD_CHAINED_IMPORT_ADDEND64:
2738 importsA64 = (dyld_chained_import_addend64*)((uint8_t*)header + header->imports_offset);
2739 for (uint32_t i=0; i < header->imports_count && !stop; ++i) {
2740 const char* symbolName = &symbolsPool[importsA64[i].name_offset];
2741 if ( importsA64[i].name_offset > maxSymbolOffset ) {
2742 diag.error("malformed import table, string overflow");
2743 return;
2744 }
2745 uint16_t libVal = importsA64[i].lib_ordinal;
2746 if ( libVal > 0xFFF0 )
2747 libOrdinal = (int16_t)libVal;
2748 else
2749 libOrdinal = libVal;
2750 callback(libOrdinal, symbolName, importsA64[i].addend, importsA64[i].weak_import, stop);
2751 }
2752 break;
2753 default:
2754 diag.error("unknown imports format");
2755 return;
2756 }
2757 }
2758 }
2759
2760 uint32_t MachOAnalyzer::segmentCount() const
2761 {
2762 __block uint32_t count = 0;
2763 forEachSegment(^(const SegmentInfo& info, bool& stop) {
2764 ++count;
2765 });
2766 return count;
2767 }
2768
2769 bool MachOAnalyzer::hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const
2770 {
2771 fileOffset = 0;
2772 size = 0;
2773
2774 Diagnostics diag;
2775 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
2776 if ( cmd->cmd == LC_CODE_SIGNATURE ) {
2777 const linkedit_data_command* sigCmd = (linkedit_data_command*)cmd;
2778 fileOffset = sigCmd->dataoff;
2779 size = sigCmd->datasize;
2780 stop = true;
2781 }
2782 });
2783 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
2784
2785 // early exist if no LC_CODE_SIGNATURE
2786 if ( fileOffset == 0 )
2787 return false;
2788
2789 // <rdar://problem/13622786> ignore code signatures in macOS binaries built with pre-10.9 tools
2790 if ( (this->cputype == CPU_TYPE_X86_64) || (this->cputype == CPU_TYPE_I386) ) {
2791 __block bool foundPlatform = false;
2792 __block bool badSignature = false;
2793 forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
2794 foundPlatform = true;
2795 if ( (platform == Platform::macOS) && (sdk < 0x000A0900) )
2796 badSignature = true;
2797 });
2798 return foundPlatform && !badSignature;
2799 }
2800
2801 return true;
2802 }
2803
2804 bool MachOAnalyzer::hasProgramVars(Diagnostics& diag, uint32_t& progVarsOffset) const
2805 {
2806 if ( this->filetype != MH_EXECUTE )
2807 return false;
2808
2809 // macOS 10.8+ program uses LC_MAIN and ProgramVars are in libdyld.dylib
2810 // macOS 10.6 -> 10.7 ProgramVars are in __program_vars section in main executable
2811 // macOS 10.5 ProgramVars are in __dyld section in main executable and 7 pointers in size
2812 // macOS 10.4 and earlier ProgramVars need to be looked up by name in nlist of main executable
2813
2814 uint64_t offset;
2815 bool usesCRT;
2816 if ( getEntry(offset, usesCRT) && usesCRT ) {
2817 // is pre-10.8 program
2818 uint64_t sectionSize;
2819 if ( const void* progVarsSection = findSectionContent("__DATA", "__program_vars", sectionSize) ) {
2820 progVarsOffset = (uint32_t)((uint8_t*)progVarsSection - (uint8_t*)this);
2821 return true;
2822 }
2823 else if ( const void* dyldSection = findSectionContent("__DATA", "__dyld", sectionSize) ) {
2824 if ( sectionSize >= 7*pointerSize() ) {
2825 progVarsOffset = (uint32_t)((uint8_t*)dyldSection - (uint8_t*)this) + 2*pointerSize();
2826 return true;
2827 }
2828 }
2829 diag.error("pre-macOS 10.5 binaries not supported");
2830 return true;
2831 }
2832 return false;
2833 }
2834
2835 // Convert from a (possibly) live pointer to a vmAddr
2836 uint64_t MachOAnalyzer::VMAddrConverter::convertToVMAddr(uint64_t value) const {
2837 if ( contentRebased ) {
2838 if ( value == 0 )
2839 return 0;
2840 // The value may have been signed. Strip the signature if that is the case
2841 #if __has_feature(ptrauth_calls)
2842 value = (uint64_t)__builtin_ptrauth_strip((void*)value, ptrauth_key_asia);
2843 #endif
2844 value -= slide;
2845 return value;
2846 }
2847 if ( chainedPointerFormat != 0 ) {
2848 auto* chainedValue = (MachOAnalyzer::ChainedFixupPointerOnDisk*)&value;
2849 uint64_t targetRuntimeOffset;
2850 if ( chainedValue->isRebase(chainedPointerFormat, preferredLoadAddress, targetRuntimeOffset) ) {
2851 value = preferredLoadAddress + targetRuntimeOffset;
2852 }
2853 return value;
2854 }
2855
2856 #if !(BUILDING_LIBDYLD || BUILDING_DYLD)
2857 typedef MachOAnalyzer::VMAddrConverter VMAddrConverter;
2858 if ( sharedCacheChainedPointerFormat != VMAddrConverter::SharedCacheFormat::none ) {
2859 switch ( sharedCacheChainedPointerFormat ) {
2860 case VMAddrConverter::SharedCacheFormat::none:
2861 assert(false);
2862 case VMAddrConverter::SharedCacheFormat::v2_x86_64_tbi: {
2863 const uint64_t deltaMask = 0x00FFFF0000000000;
2864 const uint64_t valueMask = ~deltaMask;
2865 const uint64_t valueAdd = preferredLoadAddress;
2866 value = (value & valueMask);
2867 if ( value != 0 ) {
2868 value += valueAdd;
2869 }
2870 break;
2871 }
2872 case VMAddrConverter::SharedCacheFormat::v3: {
2873 // Just use the chained pointer format for arm64e
2874 auto* chainedValue = (MachOAnalyzer::ChainedFixupPointerOnDisk*)&value;
2875 uint64_t targetRuntimeOffset;
2876 if ( chainedValue->isRebase(DYLD_CHAINED_PTR_ARM64E, preferredLoadAddress,
2877 targetRuntimeOffset) ) {
2878 value = preferredLoadAddress + targetRuntimeOffset;
2879 }
2880 break;
2881 }
2882 }
2883 return value;
2884 }
2885 #endif
2886
2887 return value;
2888 }
2889
2890 MachOAnalyzer::VMAddrConverter MachOAnalyzer::makeVMAddrConverter(bool contentRebased) const {
2891 MachOAnalyzer::VMAddrConverter vmAddrConverter;
2892 vmAddrConverter.preferredLoadAddress = preferredLoadAddress();
2893 vmAddrConverter.slide = getSlide();
2894 vmAddrConverter.chainedPointerFormat = hasChainedFixups() ? chainedPointerFormat() : 0;
2895 vmAddrConverter.contentRebased = contentRebased;
2896 return vmAddrConverter;
2897 }
2898
2899 bool MachOAnalyzer::hasInitializer(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, const void* dyldCache) const
2900 {
2901 __block bool result = false;
2902 forEachInitializer(diag, vmAddrConverter, ^(uint32_t offset) {
2903 result = true;
2904 }, dyldCache);
2905 return result;
2906 }
2907
2908 void MachOAnalyzer::forEachInitializerPointerSection(Diagnostics& diag, void (^callback)(uint32_t sectionOffset, uint32_t sectionSize, const uint8_t* content, bool& stop)) const
2909 {
2910 const unsigned ptrSize = pointerSize();
2911 const uint64_t baseAddress = preferredLoadAddress();
2912 const uint64_t slide = (uint64_t)this - baseAddress;
2913 forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& sectStop) {
2914 if ( (info.sectFlags & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS ) {
2915 if ( (info.sectSize % ptrSize) != 0 ) {
2916 diag.error("initializer section %s/%s has bad size", info.segInfo.segName, info.sectName);
2917 sectStop = true;
2918 return;
2919 }
2920 if ( malformedSectionRange ) {
2921 diag.error("initializer section %s/%s extends beyond its segment", info.segInfo.segName, info.sectName);
2922 sectStop = true;
2923 return;
2924 }
2925 const uint8_t* content = (uint8_t*)(info.sectAddr + slide);
2926 if ( ((long)content % ptrSize) != 0 ) {
2927 diag.error("initializer section %s/%s is not pointer aligned", info.segInfo.segName, info.sectName);
2928 sectStop = true;
2929 return;
2930 }
2931 callback((uint32_t)(info.sectAddr - baseAddress), (uint32_t)info.sectSize, content, sectStop);
2932 }
2933 });
2934 }
2935
2936 struct VIS_HIDDEN SegmentRanges
2937 {
2938 struct SegmentRange {
2939 uint64_t vmAddrStart;
2940 uint64_t vmAddrEnd;
2941 uint32_t fileSize;
2942 };
2943
2944 bool contains(uint64_t vmAddr) const {
2945 for (const SegmentRange& range : segments) {
2946 if ( (range.vmAddrStart <= vmAddr) && (vmAddr < range.vmAddrEnd) )
2947 return true;
2948 }
2949 return false;
2950 }
2951
2952 private:
2953 SegmentRange localAlloc[1];
2954
2955 public:
2956 dyld3::OverflowSafeArray<SegmentRange> segments { localAlloc, sizeof(localAlloc) / sizeof(localAlloc[0]) };
2957 };
2958
2959 void MachOAnalyzer::forEachInitializer(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, void (^callback)(uint32_t offset), const void* dyldCache) const
2960 {
2961 __block SegmentRanges executableSegments;
2962 forEachSegment(^(const SegmentInfo& info, bool& stop) {
2963 if ( (info.protections & VM_PROT_EXECUTE) != 0 ) {
2964 executableSegments.segments.push_back({ info.vmAddr, info.vmAddr + info.vmSize, (uint32_t)info.fileSize });
2965 }
2966 });
2967
2968 if (executableSegments.segments.empty()) {
2969 diag.error("no exeutable segments");
2970 return;
2971 }
2972
2973 uint64_t loadAddress = preferredLoadAddress();
2974 intptr_t slide = getSlide();
2975
2976 // if dylib linked with -init linker option, that initializer is first
2977 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
2978 if ( cmd->cmd == LC_ROUTINES ) {
2979 const routines_command* routines = (routines_command*)cmd;
2980 uint64_t dashInit = routines->init_address;
2981 if ( executableSegments.contains(dashInit) )
2982 callback((uint32_t)(dashInit - loadAddress));
2983 else
2984 diag.error("-init does not point within __TEXT segment");
2985 }
2986 else if ( cmd->cmd == LC_ROUTINES_64 ) {
2987 const routines_command_64* routines = (routines_command_64*)cmd;
2988 uint64_t dashInit = routines->init_address;
2989 if ( executableSegments.contains(dashInit) )
2990 callback((uint32_t)(dashInit - loadAddress));
2991 else
2992 diag.error("-init does not point within __TEXT segment");
2993 }
2994 });
2995
2996 // next any function pointers in mod-init section
2997 const unsigned ptrSize = pointerSize();
2998 forEachInitializerPointerSection(diag, ^(uint32_t sectionOffset, uint32_t sectionSize, const uint8_t* content, bool& stop) {
2999 if ( ptrSize == 8 ) {
3000 const uint64_t* initsStart = (uint64_t*)content;
3001 const uint64_t* initsEnd = (uint64_t*)((uint8_t*)content + sectionSize);
3002 for (const uint64_t* p=initsStart; p < initsEnd; ++p) {
3003 uint64_t anInit = vmAddrConverter.convertToVMAddr(*p);
3004 if ( !executableSegments.contains(anInit) ) {
3005 diag.error("initializer 0x%0llX does not point within executable segment", anInit);
3006 stop = true;
3007 break;
3008 }
3009 callback((uint32_t)(anInit - loadAddress));
3010 }
3011 }
3012 else {
3013 const uint32_t* initsStart = (uint32_t*)content;
3014 const uint32_t* initsEnd = (uint32_t*)((uint8_t*)content + sectionSize);
3015 for (const uint32_t* p=initsStart; p < initsEnd; ++p) {
3016 uint32_t anInit = (uint32_t)vmAddrConverter.convertToVMAddr(*p);
3017 if ( !executableSegments.contains(anInit) ) {
3018 diag.error("initializer 0x%0X does not point within executable segment", anInit);
3019 stop = true;
3020 break;
3021 }
3022 callback(anInit - (uint32_t)loadAddress);
3023 }
3024 }
3025 });
3026
3027 forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
3028 if ( (info.sectFlags & SECTION_TYPE) != S_INIT_FUNC_OFFSETS )
3029 return;
3030 const uint8_t* content = (uint8_t*)(info.sectAddr + slide);
3031 if ( info.segInfo.writable() ) {
3032 diag.error("initializer offsets section %s/%s must be in read-only segment", info.segInfo.segName, info.sectName);
3033 stop = true;
3034 return;
3035 }
3036 if ( (info.sectSize % 4) != 0 ) {
3037 diag.error("initializer offsets section %s/%s has bad size", info.segInfo.segName, info.sectName);
3038 stop = true;
3039 return;
3040 }
3041 if ( malformedSectionRange ) {
3042 diag.error("initializer offsets section %s/%s extends beyond the end of the segment", info.segInfo.segName, info.sectName);
3043 stop = true;
3044 return;
3045 }
3046 if ( (info.sectAddr % 4) != 0 ) {
3047 diag.error("initializer offsets section %s/%s is not 4-byte aligned", info.segInfo.segName, info.sectName);
3048 stop = true;
3049 return;
3050 }
3051 const uint32_t* initsStart = (uint32_t*)content;
3052 const uint32_t* initsEnd = (uint32_t*)((uint8_t*)content + info.sectSize);
3053 for (const uint32_t* p=initsStart; p < initsEnd; ++p) {
3054 uint32_t anInitOffset = *p;
3055 if ( !executableSegments.contains(loadAddress + anInitOffset) ) {
3056 diag.error("initializer 0x%08X does not an offset to an executable segment", anInitOffset);
3057 stop = true;
3058 break;
3059 }
3060 callback(anInitOffset);
3061 }
3062 });
3063 }
3064
3065 bool MachOAnalyzer::hasTerminators(Diagnostics& diag, const VMAddrConverter& vmAddrConverter) const
3066 {
3067 __block bool result = false;
3068 forEachTerminator(diag, vmAddrConverter, ^(uint32_t offset) {
3069 result = true;
3070 });
3071 return result;
3072 }
3073
3074 void MachOAnalyzer::forEachTerminator(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, void (^callback)(uint32_t offset)) const
3075 {
3076 __block SegmentRanges executableSegments;
3077 forEachSegment(^(const SegmentInfo& info, bool& stop) {
3078 if ( (info.protections & VM_PROT_EXECUTE) != 0 ) {
3079 executableSegments.segments.push_back({ info.vmAddr, info.vmAddr + info.vmSize, (uint32_t)info.fileSize });
3080 }
3081 });
3082
3083 if (executableSegments.segments.empty()) {
3084 diag.error("no exeutable segments");
3085 return;
3086 }
3087
3088 uint64_t loadAddress = preferredLoadAddress();
3089 intptr_t slide = getSlide();
3090
3091 // next any function pointers in mod-term section
3092 const unsigned ptrSize = pointerSize();
3093 forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
3094 if ( (info.sectFlags & SECTION_TYPE) == S_MOD_TERM_FUNC_POINTERS ) {
3095 const uint8_t* content;
3096 content = (uint8_t*)(info.sectAddr + slide);
3097 if ( (info.sectSize % ptrSize) != 0 ) {
3098 diag.error("terminator section %s/%s has bad size", info.segInfo.segName, info.sectName);
3099 stop = true;
3100 return;
3101 }
3102 if ( malformedSectionRange ) {
3103 diag.error("terminator section %s/%s extends beyond its segment", info.segInfo.segName, info.sectName);
3104 stop = true;
3105 return;
3106 }
3107 if ( ((long)content % ptrSize) != 0 ) {
3108 diag.error("terminator section %s/%s is not pointer aligned", info.segInfo.segName, info.sectName);
3109 stop = true;
3110 return;
3111 }
3112 if ( ptrSize == 8 ) {
3113 const uint64_t* initsStart = (uint64_t*)content;
3114 const uint64_t* initsEnd = (uint64_t*)((uint8_t*)content + info.sectSize);
3115 for (const uint64_t* p=initsStart; p < initsEnd; ++p) {
3116 uint64_t anInit = vmAddrConverter.convertToVMAddr(*p);
3117 if ( !executableSegments.contains(anInit) ) {
3118 diag.error("terminator 0x%0llX does not point within executable segment", anInit);
3119 stop = true;
3120 break;
3121 }
3122 callback((uint32_t)(anInit - loadAddress));
3123 }
3124 }
3125 else {
3126 const uint32_t* initsStart = (uint32_t*)content;
3127 const uint32_t* initsEnd = (uint32_t*)((uint8_t*)content + info.sectSize);
3128 for (const uint32_t* p=initsStart; p < initsEnd; ++p) {
3129 uint32_t anInit = (uint32_t)vmAddrConverter.convertToVMAddr(*p);
3130 if ( !executableSegments.contains(anInit) ) {
3131 diag.error("terminator 0x%0X does not point within executable segment", anInit);
3132 stop = true;
3133 break;
3134 }
3135 callback(anInit - (uint32_t)loadAddress);
3136 }
3137 }
3138 }
3139 });
3140 }
3141
3142
3143
3144 void MachOAnalyzer::forEachRPath(void (^callback)(const char* rPath, bool& stop)) const
3145 {
3146 Diagnostics diag;
3147 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
3148 if ( cmd->cmd == LC_RPATH ) {
3149 const char* rpath = (char*)cmd + ((struct rpath_command*)cmd)->path.offset;
3150 callback(rpath, stop);
3151 }
3152 });
3153 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
3154 }
3155
3156
3157 bool MachOAnalyzer::hasObjC() const
3158 {
3159 __block bool result = false;
3160 forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
3161 if ( (strcmp(info.sectName, "__objc_imageinfo") == 0) && (strncmp(info.segInfo.segName, "__DATA", 6) == 0) ) {
3162 result = true;
3163 stop = true;
3164 }
3165 if ( (this->cputype == CPU_TYPE_I386) && (strcmp(info.sectName, "__image_info") == 0) && (strcmp(info.segInfo.segName, "__OBJC") == 0) ) {
3166 result = true;
3167 stop = true;
3168 }
3169 });
3170 return result;
3171 }
3172
3173 bool MachOAnalyzer::usesObjCGarbageCollection() const
3174 {
3175 __block bool result = false;
3176 forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
3177 if ( (strcmp(info.sectName, "__objc_imageinfo") == 0) && (strncmp(info.segInfo.segName, "__DATA", 6) == 0) ) {
3178 const uint64_t slide = (uint64_t)this - preferredLoadAddress();
3179 const uint32_t* flags = (uint32_t*)(info.sectAddr + slide);
3180 if ( flags[1] & 4 )
3181 result = true;
3182 stop = true;
3183 }
3184 });
3185 return result;
3186 }
3187
3188
3189 bool MachOAnalyzer::hasPlusLoadMethod(Diagnostics& diag) const
3190 {
3191 __block bool result = false;
3192 if ( (this->cputype == CPU_TYPE_I386) && this->builtForPlatform(Platform::macOS) ) {
3193 // old objc runtime has no special section for +load methods, scan for string
3194 int64_t slide = getSlide();
3195 forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
3196 if ( ( (info.sectFlags & SECTION_TYPE) == S_CSTRING_LITERALS ) ) {
3197 if ( malformedSectionRange ) {
3198 diag.error("cstring section %s/%s extends beyond the end of the segment", info.segInfo.segName, info.sectName);
3199 stop = true;
3200 return;
3201 }
3202 const uint8_t* content = (uint8_t*)(info.sectAddr + slide);
3203 const char* s = (char*)content;
3204 const char* end = s + info.sectSize;
3205 while ( s < end ) {
3206 if ( strcmp(s, "load") == 0 ) {
3207 result = true;
3208 stop = true;
3209 return;
3210 }
3211 while (*s != '\0' )
3212 ++s;
3213 ++s;
3214 }
3215 }
3216 });
3217 }
3218 else {
3219 // in new objc runtime compiler puts classes/categories with +load method in specical section
3220 forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
3221 if ( strncmp(info.segInfo.segName, "__DATA", 6) != 0 )
3222 return;
3223 if ( (strcmp(info.sectName, "__objc_nlclslist") == 0) || (strcmp(info.sectName, "__objc_nlcatlist") == 0)) {
3224 result = true;
3225 stop = true;
3226 }
3227 });
3228 }
3229 return result;
3230 }
3231
3232 bool MachOAnalyzer::isSwiftLibrary() const
3233 {
3234 struct objc_image_info {
3235 int32_t version;
3236 uint32_t flags;
3237 };
3238
3239 int64_t slide = getSlide();
3240 __block bool result = false;
3241 forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
3242 if ( (strncmp(sectInfo.sectName, "__objc_imageinfo", 16) == 0) && (strncmp(sectInfo.segInfo.segName, "__DATA", 6) == 0) ) {
3243 objc_image_info* info = (objc_image_info*)((uint8_t*)sectInfo.sectAddr + slide);
3244 uint32_t swiftVersion = ((info->flags >> 8) & 0xFF);
3245 if ( swiftVersion )
3246 result = true;
3247 stop = true;
3248 }
3249 });
3250 return result;
3251 }
3252
3253 const void* MachOAnalyzer::getRebaseOpcodes(uint32_t& size) const
3254 {
3255 Diagnostics diag;
3256 LinkEditInfo leInfo;
3257 getLinkEditPointers(diag, leInfo);
3258 if ( diag.hasError() || (leInfo.dyldInfo == nullptr) )
3259 return nullptr;
3260
3261 size = leInfo.dyldInfo->rebase_size;
3262 return getLinkEditContent(leInfo.layout, leInfo.dyldInfo->rebase_off);
3263 }
3264
3265 const void* MachOAnalyzer::getBindOpcodes(uint32_t& size) const
3266 {
3267 Diagnostics diag;
3268 LinkEditInfo leInfo;
3269 getLinkEditPointers(diag, leInfo);
3270 if ( diag.hasError() || (leInfo.dyldInfo == nullptr) )
3271 return nullptr;
3272
3273 size = leInfo.dyldInfo->bind_size;
3274 return getLinkEditContent(leInfo.layout, leInfo.dyldInfo->bind_off);
3275 }
3276
3277 const void* MachOAnalyzer::getLazyBindOpcodes(uint32_t& size) const
3278 {
3279 Diagnostics diag;
3280 LinkEditInfo leInfo;
3281 getLinkEditPointers(diag, leInfo);
3282 if ( diag.hasError() || (leInfo.dyldInfo == nullptr) )
3283 return nullptr;
3284
3285 size = leInfo.dyldInfo->lazy_bind_size;
3286 return getLinkEditContent(leInfo.layout, leInfo.dyldInfo->lazy_bind_off);
3287 }
3288
3289 const void* MachOAnalyzer::getSplitSeg(uint32_t& size) const
3290 {
3291 Diagnostics diag;
3292 LinkEditInfo leInfo;
3293 getLinkEditPointers(diag, leInfo);
3294 if ( diag.hasError() || (leInfo.splitSegInfo == nullptr) )
3295 return nullptr;
3296
3297 size = leInfo.splitSegInfo->datasize;
3298 return getLinkEditContent(leInfo.layout, leInfo.splitSegInfo->dataoff);
3299 }
3300
3301 bool MachOAnalyzer::hasSplitSeg() const {
3302 uint32_t splitSegSize = 0;
3303 const void* splitSegStart = getSplitSeg(splitSegSize);
3304 return splitSegStart != nullptr;
3305 }
3306
3307 bool MachOAnalyzer::isSplitSegV1() const {
3308 uint32_t splitSegSize = 0;
3309 const void* splitSegStart = getSplitSeg(splitSegSize);
3310 if (!splitSegStart)
3311 return false;
3312
3313 return (*(const uint8_t*)splitSegStart) != DYLD_CACHE_ADJ_V2_FORMAT;
3314 }
3315
3316 bool MachOAnalyzer::isSplitSegV2() const {
3317 uint32_t splitSegSize = 0;
3318 const void* splitSegStart = getSplitSeg(splitSegSize);
3319 if (!splitSegStart)
3320 return false;
3321
3322 return (*(const uint8_t*)splitSegStart) == DYLD_CACHE_ADJ_V2_FORMAT;
3323 }
3324
3325
3326 uint64_t MachOAnalyzer::segAndOffsetToRuntimeOffset(uint8_t targetSegIndex, uint64_t targetSegOffset) const
3327 {
3328 __block uint64_t textVmAddr = 0;
3329 __block uint64_t result = 0;
3330 forEachSegment(^(const SegmentInfo& info, bool& stop) {
3331 if ( strcmp(info.segName, "__TEXT") == 0 )
3332 textVmAddr = info.vmAddr;
3333 if ( info.segIndex == targetSegIndex ) {
3334 result = (info.vmAddr - textVmAddr) + targetSegOffset;
3335 }
3336 });
3337 return result;
3338 }
3339
3340 bool MachOAnalyzer::hasLazyPointers(uint32_t& runtimeOffset, uint32_t& size) const
3341 {
3342 size = 0;
3343 forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool &stop) {
3344 if ( (info.sectFlags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) {
3345 runtimeOffset = (uint32_t)(info.sectAddr - preferredLoadAddress());
3346 size = (uint32_t)info.sectSize;
3347 stop = true;
3348 }
3349 });
3350 return (size != 0);
3351 }
3352
3353 uint64_t MachOAnalyzer::preferredLoadAddress() const
3354 {
3355 __block uint64_t textVmAddr = 0;
3356 forEachSegment(^(const SegmentInfo& info, bool& stop) {
3357 if ( strcmp(info.segName, "__TEXT") == 0 ) {
3358 textVmAddr = info.vmAddr;
3359 stop = true;
3360 }
3361 });
3362 return textVmAddr;
3363 }
3364
3365
3366 bool MachOAnalyzer::getEntry(uint64_t& offset, bool& usesCRT) const
3367 {
3368 Diagnostics diag;
3369 offset = 0;
3370 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
3371 if ( cmd->cmd == LC_MAIN ) {
3372 entry_point_command* mainCmd = (entry_point_command*)cmd;
3373 usesCRT = false;
3374 offset = mainCmd->entryoff;
3375 stop = true;
3376 }
3377 else if ( cmd->cmd == LC_UNIXTHREAD ) {
3378 stop = true;
3379 usesCRT = true;
3380 uint64_t startAddress = entryAddrFromThreadCmd((thread_command*)cmd);
3381 offset = startAddress - preferredLoadAddress();
3382 }
3383 });
3384 return (offset != 0);
3385 }
3386
3387
3388 void MachOAnalyzer::forEachInterposingSection(Diagnostics& diag, void (^handler)(uint64_t vmOffset, uint64_t vmSize, bool& stop)) const
3389 {
3390 const unsigned ptrSize = pointerSize();
3391 const unsigned entrySize = 2 * ptrSize;
3392 forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool &stop) {
3393 if ( ((info.sectFlags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(info.sectName, "__interpose") == 0) && (strcmp(info.segInfo.segName, "__DATA") == 0)) ) {
3394 if ( info.sectSize % entrySize != 0 ) {
3395 diag.error("interposing section %s/%s has bad size", info.segInfo.segName, info.sectName);
3396 stop = true;
3397 return;
3398 }
3399 if ( malformedSectionRange ) {
3400 diag.error("interposing section %s/%s extends beyond the end of the segment", info.segInfo.segName, info.sectName);
3401 stop = true;
3402 return;
3403 }
3404 if ( (info.sectAddr % ptrSize) != 0 ) {
3405 diag.error("interposing section %s/%s is not pointer aligned", info.segInfo.segName, info.sectName);
3406 stop = true;
3407 return;
3408 }
3409 handler(info.sectAddr - preferredLoadAddress(), info.sectSize, stop);
3410 }
3411 });
3412 }
3413
3414 void MachOAnalyzer::forEachDOFSection(Diagnostics& diag, void (^callback)(uint32_t offset)) const
3415 {
3416 forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool &stop) {
3417 if ( ( (info.sectFlags & SECTION_TYPE) == S_DTRACE_DOF ) && !malformedSectionRange ) {
3418 callback((uint32_t)(info.sectAddr - info.segInfo.vmAddr));
3419 }
3420 });
3421 }
3422
3423 void MachOAnalyzer::forEachCDHash(void (^handler)(const uint8_t cdHash[20])) const
3424 {
3425 Diagnostics diag;
3426 LinkEditInfo leInfo;
3427 getLinkEditPointers(diag, leInfo);
3428 if ( diag.hasError() || (leInfo.codeSig == nullptr) )
3429 return;
3430
3431 forEachCDHashOfCodeSignature(getLinkEditContent(leInfo.layout, leInfo.codeSig->dataoff),
3432 leInfo.codeSig->datasize,
3433 handler);
3434 }
3435
3436 bool MachOAnalyzer::isRestricted() const
3437 {
3438 __block bool result = false;
3439 forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool &stop) {
3440 if ( (strcmp(info.segInfo.segName, "__RESTRICT") == 0) && (strcmp(info.sectName, "__restrict") == 0) ) {
3441 result = true;
3442 stop = true;
3443 }
3444 });
3445 return result;
3446 }
3447
3448 bool MachOAnalyzer::usesLibraryValidation() const
3449 {
3450 Diagnostics diag;
3451 LinkEditInfo leInfo;
3452 getLinkEditPointers(diag, leInfo);
3453 if ( diag.hasError() || (leInfo.codeSig == nullptr) )
3454 return false;
3455
3456 // check for CS_REQUIRE_LV in CS_CodeDirectory.flags
3457 __block bool requiresLV = false;
3458 forEachCodeDirectoryBlob(getLinkEditContent(leInfo.layout, leInfo.codeSig->dataoff),
3459 leInfo.codeSig->datasize,
3460 ^(const void *cdBuffer) {
3461 const CS_CodeDirectory* cd = (const CS_CodeDirectory*)cdBuffer;
3462 requiresLV |= (htonl(cd->flags) & CS_REQUIRE_LV);
3463 });
3464
3465 return requiresLV;
3466 }
3467
3468 bool MachOAnalyzer::canHavePrecomputedDlopenClosure(const char* path, void (^failureReason)(const char*)) const
3469 {
3470 if (!MachOFile::canHavePrecomputedDlopenClosure(path, failureReason))
3471 return false;
3472
3473 // prebuilt closures use the cdhash of the dylib to verify that the dylib is still the same
3474 // at runtime as when the shared cache processed it. We must have a code signature to record this information
3475 uint32_t codeSigFileOffset;
3476 uint32_t codeSigSize;
3477 if ( !hasCodeSignature(codeSigFileOffset, codeSigSize) ) {
3478 failureReason("no code signature");
3479 return false;
3480 }
3481
3482 __block bool retval = true;
3483
3484 // images that use dynamic_lookup, bundle_loader, or have weak-defs cannot have dlopen closure pre-computed
3485 Diagnostics diag;
3486 auto checkBind = ^(int libOrdinal, bool& stop) {
3487 switch (libOrdinal) {
3488 case BIND_SPECIAL_DYLIB_WEAK_LOOKUP:
3489 failureReason("has weak externals");
3490 retval = false;
3491 stop = true;
3492 break;
3493 case BIND_SPECIAL_DYLIB_FLAT_LOOKUP:
3494 failureReason("has dynamic_lookup binds");
3495 retval = false;
3496 stop = true;
3497 break;
3498 case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE:
3499 failureReason("has reference to main executable (bundle loader)");
3500 retval = false;
3501 stop = true;
3502 break;
3503 }
3504 };
3505
3506 if (hasChainedFixups()) {
3507 forEachChainedFixupTarget(diag, ^(int libOrdinal, const char *symbolName, uint64_t addend, bool weakImport, bool &stop) {
3508 checkBind(libOrdinal, stop);
3509 });
3510 } else {
3511 forEachBind(diag, ^(uint64_t runtimeOffset, int libOrdinal, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop) {
3512 checkBind(libOrdinal, stop);
3513 },
3514 ^(const char* symbolName) {
3515 });
3516 }
3517
3518 return retval;
3519 }
3520
3521
3522 bool MachOAnalyzer::hasUnalignedPointerFixups() const
3523 {
3524 // only look at 64-bit architectures
3525 if ( pointerSize() == 4 )
3526 return false;
3527
3528 __block Diagnostics diag;
3529 __block bool result = false;
3530 if ( hasChainedFixups() ) {
3531 withChainStarts(diag, chainStartsOffset(), ^(const dyld_chained_starts_in_image* startsInfo) {
3532 forEachFixupInAllChains(diag, startsInfo, false, ^(MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& fixupsStop) {
3533 if ( ((long)(fixupLoc) & 7) != 0 ) {
3534 result = true;
3535 fixupsStop = true;
3536 }
3537 });
3538 });
3539 }
3540 else {
3541 forEachBind(diag, ^(uint64_t runtimeOffset, int libOrdinal, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop) {
3542 if ( (runtimeOffset & 7) != 0 ) {
3543 result = true;
3544 stop = true;
3545 }
3546 },
3547 ^(const char* symbolName) {
3548 });
3549 forEachRebase(diag, true, ^(uint64_t runtimeOffset, bool& stop) {
3550 if ( (runtimeOffset & 7) != 0 ) {
3551 result = true;
3552 stop = true;
3553 }
3554 });
3555 }
3556
3557 return result;
3558 }
3559
3560 void MachOAnalyzer::recurseTrie(Diagnostics& diag, const uint8_t* const start, const uint8_t* p, const uint8_t* const end,
3561 OverflowSafeArray<char>& cummulativeString, int curStrOffset, bool& stop, ExportsCallback callback) const
3562 {
3563 if ( p >= end ) {
3564 diag.error("malformed trie, node past end");
3565 return;
3566 }
3567 const uint64_t terminalSize = read_uleb128(diag, p, end);
3568 const uint8_t* children = p + terminalSize;
3569 if ( terminalSize != 0 ) {
3570 uint64_t imageOffset = 0;
3571 uint64_t flags = read_uleb128(diag, p, end);
3572 uint64_t other = 0;
3573 const char* importName = nullptr;
3574 if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
3575 other = read_uleb128(diag, p, end); // dylib ordinal
3576 importName = (char*)p;
3577 }
3578 else {
3579 imageOffset = read_uleb128(diag, p, end);
3580 if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER )
3581 other = read_uleb128(diag, p, end);
3582 else
3583 other = 0;
3584 }
3585 if ( diag.hasError() )
3586 return;
3587 callback(cummulativeString.begin(), imageOffset, flags, other, importName, stop);
3588 if ( stop )
3589 return;
3590 }
3591 if ( children > end ) {
3592 diag.error("malformed trie, terminalSize extends beyond trie data");
3593 return;
3594 }
3595 const uint8_t childrenCount = *children++;
3596 const uint8_t* s = children;
3597 for (uint8_t i=0; i < childrenCount; ++i) {
3598 int edgeStrLen = 0;
3599 while (*s != '\0') {
3600 cummulativeString.resize(curStrOffset+edgeStrLen + 1);
3601 cummulativeString[curStrOffset+edgeStrLen] = *s++;
3602 ++edgeStrLen;
3603 if ( s > end ) {
3604 diag.error("malformed trie node, child node extends past end of trie\n");
3605 return;
3606 }
3607 }
3608 cummulativeString.resize(curStrOffset+edgeStrLen + 1);
3609 cummulativeString[curStrOffset+edgeStrLen] = *s++;
3610 uint64_t childNodeOffset = read_uleb128(diag, s, end);
3611 if (childNodeOffset == 0) {
3612 diag.error("malformed trie, childNodeOffset==0");
3613 return;
3614 }
3615 recurseTrie(diag, start, start+childNodeOffset, end, cummulativeString, curStrOffset+edgeStrLen, stop, callback);
3616 if ( diag.hasError() || stop )
3617 return;
3618 }
3619 }
3620
3621 void MachOAnalyzer::forEachExportedSymbol(Diagnostics& diag, ExportsCallback callback) const
3622 {
3623 LinkEditInfo leInfo;
3624 getLinkEditPointers(diag, leInfo);
3625 if ( diag.hasError() )
3626 return;
3627 uint64_t trieSize;
3628 if ( const uint8_t* trieStart = getExportsTrie(leInfo, trieSize) ) {
3629 const uint8_t* trieEnd = trieStart + trieSize;
3630 // We still emit empty export trie load commands just as a placeholder to show we have
3631 // no exports. In that case, don't start recursing as we'll immediately think we ran
3632 // of the end of the buffer
3633 if ( trieSize == 0 )
3634 return;
3635 bool stop = false;
3636 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(char, cummulativeString, 4096);
3637 recurseTrie(diag, trieStart, trieStart, trieEnd, cummulativeString, 0, stop, callback);
3638 }
3639 }
3640
3641 bool MachOAnalyzer::canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const
3642 {
3643 if (!MachOFile::canBePlacedInDyldCache(path, failureReason))
3644 return false;
3645
3646 // arm64e requires split seg v2 as the split seg code can't handle chained fixups for split seg v1
3647 if ( isArch("arm64e") ) {
3648 uint32_t splitSegSize = 0;
3649 const uint8_t* infoStart = (const uint8_t*)getSplitSeg(splitSegSize);
3650 if ( *infoStart != DYLD_CACHE_ADJ_V2_FORMAT ) {
3651 failureReason("chained fixups requires split seg v2");
3652 return false;
3653 }
3654 }
3655
3656 // <rdar://problem/57769033> dyld_cache_patchable_location only supports addend in range 0..31
3657 const bool is64bit = is64();
3658 __block Diagnostics diag;
3659 __block bool addendTooLarge = false;
3660 if ( this->hasChainedFixups() ) {
3661 // with chained fixups, addends can be in the import table or embedded in a bind pointer
3662 forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
3663 if ( is64bit )
3664 addend &= 0x00FFFFFFFFFFFFFF; // ignore TBI
3665 if ( addend > 31 ) {
3666 addendTooLarge = true;
3667 stop = true;
3668 }
3669 });
3670 // check each pointer for embedded addend
3671 withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) {
3672 forEachFixupInAllChains(diag, starts, false, ^(ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
3673 switch (segInfo->pointer_format) {
3674 case DYLD_CHAINED_PTR_ARM64E:
3675 case DYLD_CHAINED_PTR_ARM64E_USERLAND:
3676 case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
3677 if ( fixupLoc->arm64e.bind.bind && !fixupLoc->arm64e.authBind.auth ) {
3678 if ( fixupLoc->arm64e.bind.addend > 31 ) {
3679 addendTooLarge = true;
3680 stop = true;
3681 }
3682 }
3683 break;
3684 case DYLD_CHAINED_PTR_64:
3685 case DYLD_CHAINED_PTR_64_OFFSET:
3686 if ( fixupLoc->generic64.rebase.bind ) {
3687 if ( fixupLoc->generic64.bind.addend > 31 ) {
3688 addendTooLarge = true;
3689 stop = true;
3690 }
3691 }
3692 break;
3693 case DYLD_CHAINED_PTR_32:
3694 if ( fixupLoc->generic32.bind.bind ) {
3695 if ( fixupLoc->generic32.bind.addend > 31 ) {
3696 addendTooLarge = true;
3697 stop = true;
3698 }
3699 }
3700 break;
3701 }
3702 });
3703 });
3704 }
3705 else {
3706 // scan bind opcodes for large addend
3707 forEachBind(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo* segments, bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
3708 uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop) {
3709 if ( is64bit )
3710 addend &= 0x00FFFFFFFFFFFFFF; // ignore TBI
3711 if ( addend > 31 ) {
3712 addendTooLarge = true;
3713 stop = true;
3714 }
3715 },
3716 ^(const char* symbolName) {
3717 });
3718 }
3719 if ( addendTooLarge ) {
3720 failureReason("bind addend too large");
3721 return false;
3722 }
3723
3724 if ( !(isArch("x86_64") || isArch("x86_64h")) )
3725 return true;
3726
3727 if ( hasChainedFixups() )
3728 return true;
3729
3730 // evict swift dylibs with split seg v1 info
3731 if ( this->isSwiftLibrary() && this->isSplitSegV1() )
3732 return false;
3733
3734 __block bool rebasesOk = true;
3735 uint64_t startVMAddr = preferredLoadAddress();
3736 uint64_t endVMAddr = startVMAddr + mappedSize();
3737 forEachRebase(diag, false, ^(uint64_t runtimeOffset, bool &stop) {
3738 // We allow TBI for x86_64 dylibs, but then require that the remainder of the offset
3739 // is a 32-bit offset from the mach-header.
3740 uint64_t value = *(uint64_t*)((uint8_t*)this + runtimeOffset);
3741 value &= 0x00FFFFFFFFFFFFFFULL;
3742 if ( (value < startVMAddr) || (value >= endVMAddr) ) {
3743 failureReason("rebase value out of range of dylib");
3744 rebasesOk = false;
3745 stop = true;
3746 return;
3747 }
3748
3749 // Also error if the rebase location is anything other than 4/8 byte aligned
3750 if ( (runtimeOffset & 0x3) != 0 ) {
3751 failureReason("rebase value is not 4-byte aligned");
3752 rebasesOk = false;
3753 stop = true;
3754 return;
3755 }
3756 });
3757 return rebasesOk;
3758 }
3759
3760 #if BUILDING_APP_CACHE_UTIL
3761 bool MachOAnalyzer::canBePlacedInKernelCollection(const char* path, void (^failureReason)(const char*)) const
3762 {
3763 if (!MachOFile::canBePlacedInKernelCollection(path, failureReason))
3764 return false;
3765
3766 // App caches reguire that everything be built with split seg v2
3767 // This is because v1 can't move anything other than __TEXT and __DATA
3768 // but kernels have __TEXT_EXEC and other segments
3769 if ( isKextBundle() ) {
3770 // x86_64 kext's might not have split seg
3771 if ( !isArch("x86_64") && !isArch("x86_64h") ) {
3772 if ( !isSplitSegV2() ) {
3773 failureReason("Missing split seg v2");
3774 return false;
3775 }
3776 }
3777 } else if ( isStaticExecutable() ) {
3778 // The kernel must always have split seg V2
3779 if ( !isSplitSegV2() ) {
3780 failureReason("Missing split seg v2");
3781 return false;
3782 }
3783
3784 // The kernel should have __TEXT and __TEXT_EXEC
3785 __block bool foundText = false;
3786 __block bool foundTextExec = false;
3787 __block bool foundHIB = false;
3788 __block uint64_t hibernateVMAddr = 0;
3789 __block uint64_t hibernateVMSize = 0;
3790 forEachSegment(^(const SegmentInfo &segmentInfo, bool &stop) {
3791 if ( strcmp(segmentInfo.segName, "__TEXT") == 0 ) {
3792 foundText = true;
3793 }
3794 if ( strcmp(segmentInfo.segName, "__TEXT_EXEC") == 0 ) {
3795 foundTextExec = true;
3796 }
3797 if ( strcmp(segmentInfo.segName, "__HIB") == 0 ) {
3798 foundHIB = true;
3799 hibernateVMAddr = segmentInfo.vmAddr;
3800 hibernateVMSize = segmentInfo.vmSize;
3801 }
3802 });
3803 if (!foundText) {
3804 failureReason("Expected __TEXT segment");
3805 return false;
3806 }
3807 if ( foundTextExec && foundHIB ) {
3808 failureReason("Expected __TEXT_EXEC or __HIB segment, but found both");
3809 return false;
3810 }
3811 if ( !foundTextExec && !foundHIB ) {
3812 failureReason("Expected __TEXT_EXEC or __HIB segment, but found neither");
3813 return false;
3814 }
3815
3816 // The hibernate segment should be mapped before the base address
3817 if ( foundHIB ) {
3818 uint64_t baseAddress = preferredLoadAddress();
3819 if ( greaterThanAddOrOverflow(hibernateVMAddr, hibernateVMSize, baseAddress) ) {
3820 failureReason("__HIB segment should be mapped before base address");
3821 return false;
3822 }
3823 }
3824 }
3825
3826 // Don't allow kext's to have load addresses
3827 if ( isKextBundle() && (preferredLoadAddress() != 0) ) {
3828 failureReason("Has load address");
3829 return false;
3830 }
3831
3832 if (hasChainedFixups()) {
3833 if ( usesClassicRelocationsInKernelCollection() ) {
3834 failureReason("Cannot use fixup chains with binary expecting classic relocations");
3835 return false;
3836 }
3837
3838 __block bool fixupsOk = true;
3839 __block Diagnostics diag;
3840 withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) {
3841 forEachFixupInAllChains(diag, starts, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc,
3842 const dyld_chained_starts_in_segment* segInfo, bool& stop) {
3843 // We only support inputs from a few pointer format types, so that we don't need to handle them all later
3844 switch (segInfo->pointer_format) {
3845 case DYLD_CHAINED_PTR_ARM64E:
3846 case DYLD_CHAINED_PTR_64:
3847 case DYLD_CHAINED_PTR_32:
3848 case DYLD_CHAINED_PTR_32_CACHE:
3849 case DYLD_CHAINED_PTR_32_FIRMWARE:
3850 failureReason("unsupported chained fixups pointer format");
3851 fixupsOk = false;
3852 stop = true;
3853 return;
3854 case DYLD_CHAINED_PTR_64_OFFSET:
3855 // arm64 kernel and kexts use this format
3856 break;
3857 case DYLD_CHAINED_PTR_ARM64E_KERNEL:
3858 // arm64e kexts use this format
3859 break;
3860 case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
3861 case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
3862 failureReason("unsupported chained fixups pointer format");
3863 fixupsOk = false;
3864 stop = true;
3865 return;
3866 default:
3867 failureReason("unknown chained fixups pointer format");
3868 fixupsOk = false;
3869 stop = true;
3870 return;
3871 }
3872
3873 uint64_t vmOffset = (uint8_t*)fixupLoc - (uint8_t*)this;
3874 // Error if the fixup location is anything other than 4/8 byte aligned
3875 if ( (vmOffset & 0x3) != 0 ) {
3876 failureReason("fixup value is not 4-byte aligned");
3877 fixupsOk = false;
3878 stop = true;
3879 return;
3880 }
3881
3882 // We also must only need 30-bits for the chain format of the resulting cache
3883 if ( vmOffset >= (1 << 30) ) {
3884 failureReason("fixup value does not fit in 30-bits");
3885 fixupsOk = false;
3886 stop = true;
3887 return;
3888 }
3889 });
3890 });
3891 if (!fixupsOk)
3892 return false;
3893 } else {
3894 // x86_64 xnu will have unaligned text/data fixups and fixups inside __HIB __text.
3895 // We allow these as xnu is emitted with classic relocations
3896 bool canHaveUnalignedFixups = usesClassicRelocationsInKernelCollection();
3897 canHaveUnalignedFixups |= ( isArch("x86_64") || isArch("x86_64h") );
3898 __block bool rebasesOk = true;
3899 Diagnostics diag;
3900 forEachRebase(diag, false, ^(uint64_t runtimeOffset, bool &stop) {
3901 // Error if the rebase location is anything other than 4/8 byte aligned
3902 if ( !canHaveUnalignedFixups && ((runtimeOffset & 0x3) != 0) ) {
3903 failureReason("rebase value is not 4-byte aligned");
3904 rebasesOk = false;
3905 stop = true;
3906 return;
3907 }
3908
3909 #if BUILDING_APP_CACHE_UTIL
3910 // xnu for x86_64 has __HIB mapped before __DATA, so offsets appear to be
3911 // negative. Adjust the fixups so that we don't think they are out of
3912 // range of the number of bits we have
3913 if ( isStaticExecutable() ) {
3914 __block uint64_t baseAddress = ~0ULL;
3915 forEachSegment(^(const SegmentInfo& info, bool& stop) {
3916 baseAddress = std::min(baseAddress, info.vmAddr);
3917 });
3918 uint64_t textSegVMAddr = preferredLoadAddress();
3919 runtimeOffset = (textSegVMAddr + runtimeOffset) - baseAddress;
3920 }
3921 #endif
3922
3923 // We also must only need 30-bits for the chain format of the resulting cache
3924 if ( runtimeOffset >= (1 << 30) ) {
3925 failureReason("rebase value does not fit in 30-bits");
3926 rebasesOk = false;
3927 stop = true;
3928 return;
3929 }
3930 });
3931 if (!rebasesOk)
3932 return false;
3933
3934 __block bool bindsOk = true;
3935 forEachBind(diag, ^(uint64_t runtimeOffset, int libOrdinal, uint8_t type, const char *symbolName,
3936 bool weakImport, bool lazyBind, uint64_t addend, bool &stop) {
3937
3938 // Don't validate branch fixups as we'll turn then in to direct jumps instead
3939 if ( type == BIND_TYPE_TEXT_PCREL32 )
3940 return;
3941
3942 // Error if the bind location is anything other than 4/8 byte aligned
3943 if ( !canHaveUnalignedFixups && ((runtimeOffset & 0x3) != 0) ) {
3944 failureReason("bind value is not 4-byte aligned");
3945 bindsOk = false;
3946 stop = true;
3947 return;
3948 }
3949
3950 // We also must only need 30-bits for the chain format of the resulting cache
3951 if ( runtimeOffset >= (1 << 30) ) {
3952 failureReason("bind value does not fit in 30-bits");
3953 rebasesOk = false;
3954 stop = true;
3955 return;
3956 }
3957 }, ^(const char *symbolName) {
3958 });
3959 if (!bindsOk)
3960 return false;
3961 }
3962
3963 return true;
3964 }
3965
3966 #endif
3967
3968 bool MachOAnalyzer::usesClassicRelocationsInKernelCollection() const {
3969 // The xnu x86_64 static executable needs to do the i386->x86_64 transition
3970 // so will be emitted with classic relocations
3971 if ( isArch("x86_64") || isArch("x86_64h") ) {
3972 return isStaticExecutable() || isFileSet();
3973 }
3974 return false;
3975 }
3976
3977 uint64_t MachOAnalyzer::chainStartsOffset() const
3978 {
3979 const dyld_chained_fixups_header* header = chainedFixupsHeader();
3980 // old arm64e binary has no dyld_chained_fixups_header
3981 if ( header == nullptr )
3982 return 0;
3983 return header->starts_offset + ((uint8_t*)header - (uint8_t*)this);
3984 }
3985
3986 const dyld_chained_fixups_header* MachOAnalyzer::chainedFixupsHeader() const
3987 {
3988 Diagnostics diag;
3989 LinkEditInfo leInfo;
3990 getLinkEditPointers(diag, leInfo);
3991 if ( diag.hasError() || (leInfo.chainedFixups == nullptr) )
3992 return nullptr;
3993
3994 return (dyld_chained_fixups_header*)getLinkEditContent(leInfo.layout, leInfo.chainedFixups->dataoff);
3995 }
3996
3997 uint16_t MachOAnalyzer::chainedPointerFormat(const dyld_chained_fixups_header* header)
3998 {
3999 const dyld_chained_starts_in_image* startsInfo = (dyld_chained_starts_in_image*)((uint8_t*)header + header->starts_offset);
4000 for (uint32_t i=0; i < startsInfo->seg_count; ++i) {
4001 uint32_t segInfoOffset = startsInfo->seg_info_offset[i];
4002 // 0 offset means this segment has no fixups
4003 if ( segInfoOffset == 0 )
4004 continue;
4005 const dyld_chained_starts_in_segment* segInfo = (dyld_chained_starts_in_segment*)((uint8_t*)startsInfo + segInfoOffset);
4006 if ( segInfo->page_count != 0 )
4007 return segInfo->pointer_format;
4008 }
4009 return 0; // no chains (perhaps no __DATA segment)
4010 }
4011
4012 uint16_t MachOAnalyzer::chainedPointerFormat() const
4013 {
4014 const dyld_chained_fixups_header* header = chainedFixupsHeader();
4015 if ( header != nullptr ) {
4016 // get pointer format from chain info struct in LINKEDIT
4017 return chainedPointerFormat(header);
4018 }
4019 assert(this->cputype == CPU_TYPE_ARM64 && (this->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E) && "chainedPointerFormat() called on non-chained binary");
4020 return DYLD_CHAINED_PTR_ARM64E;
4021 }
4022
4023 #if (BUILDING_DYLD || BUILDING_LIBDYLD) && !__arm64e__
4024 #define SUPPORT_OLD_ARM64E_FORMAT 0
4025 #else
4026 #define SUPPORT_OLD_ARM64E_FORMAT 1
4027 #endif
4028
4029 // find dyld_chained_starts_in_image* in image
4030 // if old arm64e binary, synthesize dyld_chained_starts_in_image*
4031 void MachOAnalyzer::withChainStarts(Diagnostics& diag, uint64_t startsStructOffsetHint, void (^callback)(const dyld_chained_starts_in_image*)) const
4032 {
4033 if ( startsStructOffsetHint != 0 ) {
4034 // we have a pre-computed offset into LINKEDIT for dyld_chained_starts_in_image
4035 callback((dyld_chained_starts_in_image*)((uint8_t*)this + startsStructOffsetHint));
4036 return;
4037 }
4038
4039 LinkEditInfo leInfo;
4040 getLinkEditPointers(diag, leInfo);
4041 if ( diag.hasError() )
4042 return;
4043
4044 if ( leInfo.chainedFixups != nullptr ) {
4045 // find dyld_chained_starts_in_image from dyld_chained_fixups_header
4046 const dyld_chained_fixups_header* header = (dyld_chained_fixups_header*)getLinkEditContent(leInfo.layout, leInfo.chainedFixups->dataoff);
4047 callback((dyld_chained_starts_in_image*)((uint8_t*)header + header->starts_offset));
4048 }
4049 #if SUPPORT_OLD_ARM64E_FORMAT
4050 // don't want this code in non-arm64e dyld because it causes a stack protector which dereferences a GOT pointer before GOT is set up
4051 else if ( (leInfo.dyldInfo != nullptr) && (this->cputype == CPU_TYPE_ARM64) && (this->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E) ) {
4052 // old arm64e binary, create a dyld_chained_starts_in_image for caller
4053 uint64_t baseAddress = preferredLoadAddress();
4054 BLOCK_ACCCESSIBLE_ARRAY(uint8_t, buffer, leInfo.dyldInfo->bind_size + 512);
4055 dyld_chained_starts_in_image* header = (dyld_chained_starts_in_image*)buffer;
4056 header->seg_count = leInfo.layout.linkeditSegIndex;
4057 for (uint32_t i=0; i < header->seg_count; ++i)
4058 header->seg_info_offset[i] = 0;
4059 __block uint8_t curSegIndex = 0;
4060 __block dyld_chained_starts_in_segment* curSeg = (dyld_chained_starts_in_segment*)(&(header->seg_info_offset[header->seg_count]));
4061 parseOrgArm64eChainedFixups(diag, nullptr, nullptr, ^(const LinkEditInfo& leInfo2, const SegmentInfo segments[], uint8_t segmentIndex,
4062 bool segIndexSet, uint64_t segmentOffset, uint16_t format, bool& stop) {
4063 uint32_t pageIndex = (uint32_t)(segmentOffset/0x1000);
4064 if ( segmentIndex != curSegIndex ) {
4065 if ( curSegIndex == 0 ) {
4066 header->seg_info_offset[segmentIndex] = (uint32_t)((uint8_t*)curSeg - buffer);
4067 }
4068 else {
4069 header->seg_info_offset[segmentIndex] = (uint32_t)((uint8_t*)(&curSeg->page_start[curSeg->page_count]) - buffer);
4070 curSeg = (dyld_chained_starts_in_segment*)((uint8_t*)header+header->seg_info_offset[segmentIndex]);
4071 }
4072 curSeg->page_count = 0;
4073 curSegIndex = segmentIndex;
4074 }
4075 while ( curSeg->page_count != pageIndex ) {
4076 curSeg->page_start[curSeg->page_count] = 0xFFFF;
4077 curSeg->page_count++;
4078 }
4079 curSeg->size = (uint32_t)((uint8_t*)(&curSeg->page_start[pageIndex]) - (uint8_t*)curSeg);
4080 curSeg->page_size = 0x1000; // old arm64e encoding used 4KB pages
4081 curSeg->pointer_format = DYLD_CHAINED_PTR_ARM64E;
4082 curSeg->segment_offset = segments[segmentIndex].vmAddr - baseAddress;
4083 curSeg->max_valid_pointer = 0;
4084 curSeg->page_count = pageIndex+1;
4085 curSeg->page_start[pageIndex] = segmentOffset & 0xFFF;
4086 //fprintf(stderr, "segment_offset=0x%llX, vmAddr=0x%llX\n", curSeg->segment_offset, segments[segmentIndex].vmAddr );
4087 //printf("segIndex=%d, segOffset=0x%08llX, page_start[%d]=0x%04X, page_start[%d]=0x%04X\n",
4088 // segmentIndex, segmentOffset, pageIndex, curSeg->page_start[pageIndex], pageIndex-1, pageIndex ? curSeg->page_start[pageIndex-1] : 0);
4089 });
4090 callback(header);
4091 }
4092 #endif
4093 else {
4094 diag.error("image does not use chained fixups");
4095 }
4096 }
4097
4098 struct OldThreadsStartSection
4099 {
4100 uint32_t padding : 31,
4101 stride8 : 1;
4102 uint32_t chain_starts[1];
4103 };
4104
4105 bool MachOAnalyzer::hasFirmwareChainStarts(uint16_t* pointerFormat, uint32_t* startsCount, const uint32_t** starts) const
4106 {
4107 if ( !this->isPreload() && !this->isStaticExecutable() )
4108 return false;
4109
4110 uint64_t sectionSize;
4111 if (const dyld_chained_starts_offsets* sect = (dyld_chained_starts_offsets*)this->findSectionContent("__TEXT", "__chain_starts", sectionSize) ) {
4112 *pointerFormat = sect->pointer_format;
4113 *startsCount = sect->starts_count;
4114 *starts = &sect->chain_starts[0];
4115 return true;
4116 }
4117 if (const OldThreadsStartSection* sect = (OldThreadsStartSection*)this->findSectionContent("__TEXT", "__thread_starts", sectionSize) ) {
4118 *pointerFormat = sect->stride8 ? DYLD_CHAINED_PTR_ARM64E : DYLD_CHAINED_PTR_ARM64E_FIRMWARE;
4119 *startsCount = (uint32_t)(sectionSize/4) - 1;
4120 *starts = sect->chain_starts;
4121 return true;
4122 }
4123 return false;
4124 }
4125
4126
4127 MachOAnalyzer::ObjCInfo MachOAnalyzer::getObjCInfo() const
4128 {
4129 __block ObjCInfo result;
4130 result.selRefCount = 0;
4131 result.classDefCount = 0;
4132 result.protocolDefCount = 0;
4133
4134 const uint32_t ptrSize = pointerSize();
4135 forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
4136 if ( strncmp(sectInfo.segInfo.segName, "__DATA", 6) == 0 ) {
4137 if ( strcmp(sectInfo.sectName, "__objc_selrefs") == 0 )
4138 result.selRefCount += (sectInfo.sectSize/ptrSize);
4139 else if ( strcmp(sectInfo.sectName, "__objc_classlist") == 0 )
4140 result.classDefCount += (sectInfo.sectSize/ptrSize);
4141 else if ( strcmp(sectInfo.sectName, "__objc_protolist") == 0 )
4142 result.protocolDefCount += (sectInfo.sectSize/ptrSize);
4143 }
4144 else if ( (this->cputype == CPU_TYPE_I386) && (strcmp(sectInfo.segInfo.segName, "__OBJC") == 0) ) {
4145 if ( strcmp(sectInfo.sectName, "__message_refs") == 0 )
4146 result.selRefCount += (sectInfo.sectSize/4);
4147 else if ( strcmp(sectInfo.sectName, "__class") == 0 )
4148 result.classDefCount += (sectInfo.sectSize/48);
4149 else if ( strcmp(sectInfo.sectName, "__protocol") == 0 )
4150 result.protocolDefCount += (sectInfo.sectSize/20);
4151 }
4152 });
4153
4154 return result;
4155 }
4156
4157 uint64_t MachOAnalyzer::ObjCClassInfo::getReadOnlyDataField(ObjCClassInfo::ReadOnlyDataField field, uint32_t pointerSize) const {
4158 if (pointerSize == 8) {
4159 typedef uint64_t PtrTy;
4160 struct class_ro_t {
4161 uint32_t flags;
4162 uint32_t instanceStart;
4163 // Note there is 4-bytes of alignment padding between instanceSize and ivarLayout
4164 // on 64-bit archs, but no padding on 32-bit archs.
4165 // This union is a way to model that.
4166 union {
4167 uint32_t instanceSize;
4168 PtrTy pad;
4169 } instanceSize;
4170 PtrTy ivarLayoutVMAddr;
4171 PtrTy nameVMAddr;
4172 PtrTy baseMethodsVMAddr;
4173 PtrTy baseProtocolsVMAddr;
4174 PtrTy ivarsVMAddr;
4175 PtrTy weakIvarLayoutVMAddr;
4176 PtrTy basePropertiesVMAddr;
4177 };
4178 const class_ro_t* classData = (const class_ro_t*)(dataVMAddr + vmAddrConverter.slide);
4179 switch (field) {
4180 case ObjCClassInfo::ReadOnlyDataField::name:
4181 return vmAddrConverter.convertToVMAddr(classData->nameVMAddr);
4182 case ObjCClassInfo::ReadOnlyDataField::baseProtocols:
4183 return vmAddrConverter.convertToVMAddr(classData->baseProtocolsVMAddr);
4184 case ObjCClassInfo::ReadOnlyDataField::baseMethods:
4185 return vmAddrConverter.convertToVMAddr(classData->baseMethodsVMAddr);
4186 case ObjCClassInfo::ReadOnlyDataField::baseProperties:
4187 return vmAddrConverter.convertToVMAddr(classData->basePropertiesVMAddr);
4188 case ObjCClassInfo::ReadOnlyDataField::flags:
4189 return classData->flags;
4190 }
4191 } else {
4192 typedef uint32_t PtrTy;
4193 struct class_ro_t {
4194 uint32_t flags;
4195 uint32_t instanceStart;
4196 // Note there is 4-bytes of alignment padding between instanceSize and ivarLayout
4197 // on 64-bit archs, but no padding on 32-bit archs.
4198 // This union is a way to model that.
4199 union {
4200 uint32_t instanceSize;
4201 PtrTy pad;
4202 } instanceSize;
4203 PtrTy ivarLayoutVMAddr;
4204 PtrTy nameVMAddr;
4205 PtrTy baseMethodsVMAddr;
4206 PtrTy baseProtocolsVMAddr;
4207 PtrTy ivarsVMAddr;
4208 PtrTy weakIvarLayoutVMAddr;
4209 PtrTy basePropertiesVMAddr;
4210 };
4211 const class_ro_t* classData = (const class_ro_t*)(dataVMAddr + vmAddrConverter.slide);
4212 switch (field) {
4213 case ObjCClassInfo::ReadOnlyDataField::name:
4214 return vmAddrConverter.convertToVMAddr(classData->nameVMAddr);
4215 case ObjCClassInfo::ReadOnlyDataField::baseProtocols:
4216 return vmAddrConverter.convertToVMAddr(classData->baseProtocolsVMAddr);
4217 case ObjCClassInfo::ReadOnlyDataField::baseMethods:
4218 return vmAddrConverter.convertToVMAddr(classData->baseMethodsVMAddr);
4219 case ObjCClassInfo::ReadOnlyDataField::baseProperties:
4220 return vmAddrConverter.convertToVMAddr(classData->basePropertiesVMAddr);
4221 case ObjCClassInfo::ReadOnlyDataField::flags:
4222 return classData->flags;
4223 }
4224 }
4225 }
4226
4227 const char* MachOAnalyzer::getPrintableString(uint64_t stringVMAddr, MachOAnalyzer::PrintableStringResult& result,
4228 SectionCache* sectionCache,
4229 bool (^sectionHandler)(const SectionInfo& sectionInfo)) const {
4230 if ( sectionCache != nullptr ) {
4231 // Make sure the string is pointing in to one of the supported sections
4232 __block const dyld3::MachOAnalyzer::SectionInfo* nameSectionInfo = nullptr;
4233 for (const dyld3::MachOAnalyzer::SectionInfo& sectionInfo : sectionCache->sectionInfos) {
4234 if ( stringVMAddr < sectionInfo.sectAddr ) {
4235 continue;
4236 }
4237 if ( stringVMAddr >= ( sectionInfo.sectAddr + sectionInfo.sectSize) ) {
4238 continue;
4239 }
4240 nameSectionInfo = &sectionInfo;
4241 break;
4242 }
4243
4244 if ( nameSectionInfo != nullptr ) {
4245 // The section handler may also reject this section
4246 if ( sectionHandler != nullptr ) {
4247 if (!sectionHandler(*nameSectionInfo)) {
4248 result = PrintableStringResult::UnknownSection;
4249 return nullptr;
4250 }
4251 }
4252
4253 result = PrintableStringResult::CanPrint;
4254 return (const char*)(stringVMAddr + getSlide());
4255 }
4256 }
4257
4258 // If the name isn't in the cache then find the section its in
4259
4260 uint32_t fairplayTextOffsetStart;
4261 uint32_t fairplayTextOffsetEnd;
4262 uint32_t fairplaySize;
4263 if ( isFairPlayEncrypted(fairplayTextOffsetStart, fairplaySize) ) {
4264 fairplayTextOffsetEnd = fairplayTextOffsetStart + fairplaySize;
4265 } else {
4266 fairplayTextOffsetEnd = 0;
4267 }
4268
4269 result = PrintableStringResult::UnknownSection;
4270 forEachSection(^(const MachOAnalyzer::SectionInfo &sectInfo, bool malformedSectionRange, bool &stop) {
4271 if ( stringVMAddr < sectInfo.sectAddr ) {
4272 return;
4273 }
4274 if ( stringVMAddr >= ( sectInfo.sectAddr + sectInfo.sectSize) ) {
4275 return;
4276 }
4277
4278 // We can't scan this section if its protected
4279 if ( sectInfo.segInfo.isProtected ) {
4280 result = PrintableStringResult::ProtectedSection;
4281 stop = true;
4282 return;
4283 }
4284
4285 // We can't scan this section if it overlaps with the fairplay range
4286 if ( fairplayTextOffsetEnd < sectInfo.sectFileOffset ) {
4287 // Fairplay range ends before section
4288 } else if ( fairplayTextOffsetStart > (sectInfo.sectFileOffset + sectInfo.sectSize) ) {
4289 // Fairplay range starts after section
4290 } else {
4291 // Must overlap
4292 result = PrintableStringResult::FairPlayEncrypted;
4293 stop = true;
4294 return;
4295 }
4296
4297 // The section handler may also reject this section
4298 if ( sectionHandler != nullptr ) {
4299 if (!sectionHandler(sectInfo)) {
4300 result = PrintableStringResult::UnknownSection;
4301 stop = true;
4302 return;
4303 }
4304 }
4305 // Cache this section for later.
4306 if ( sectionCache != nullptr ) {
4307 sectionCache->sectionInfos.push_back(sectInfo);
4308 }
4309 result = PrintableStringResult::CanPrint;
4310 stop = true;
4311 });
4312
4313 #if BUILDING_SHARED_CACHE_UTIL || BUILDING_DYLDINFO
4314 // The shared cache coalesces strings in to their own section.
4315 // Assume its a valid pointer
4316 if (result == PrintableStringResult::UnknownSection) {
4317 result = PrintableStringResult::CanPrint;
4318 return (const char*)(stringVMAddr + getSlide());
4319 }
4320 #endif
4321
4322 if (result == PrintableStringResult::CanPrint)
4323 return (const char*)(stringVMAddr + getSlide());
4324 return nullptr;
4325 }
4326
4327 bool MachOAnalyzer::SectionCache::findSectionForVMAddr(uint64_t vmAddr, bool (^sectionHandler)(const SectionInfo& sectionInfo)) {
4328
4329 // Make sure the string is pointing in to one of the supported sections
4330 __block const dyld3::MachOAnalyzer::SectionInfo* foundSectionInfo = nullptr;
4331 for (const dyld3::MachOAnalyzer::SectionInfo& sectionInfo : sectionInfos) {
4332 if ( vmAddr < sectionInfo.sectAddr ) {
4333 continue;
4334 }
4335 if ( vmAddr >= ( sectionInfo.sectAddr + sectionInfo.sectSize) ) {
4336 continue;
4337 }
4338 foundSectionInfo = &sectionInfo;
4339 break;
4340 }
4341
4342 if ( foundSectionInfo != nullptr ) {
4343 // The section handler may also reject this section
4344 if ( sectionHandler != nullptr ) {
4345 if (!sectionHandler(*foundSectionInfo)) {
4346 return false;
4347 }
4348 }
4349
4350 // Found a section, so return true
4351 return true;
4352 }
4353
4354 // If the name isn't in the cache then find the section its in
4355
4356 uint32_t fairplayTextOffsetStart;
4357 uint32_t fairplayTextOffsetEnd;
4358 uint32_t fairplaySize;
4359 if ( ma->isFairPlayEncrypted(fairplayTextOffsetStart, fairplaySize) ) {
4360 fairplayTextOffsetEnd = fairplayTextOffsetStart + fairplaySize;
4361 } else {
4362 fairplayTextOffsetEnd = 0;
4363 }
4364
4365 __block bool foundValidSection = false;
4366 ma->forEachSection(^(const MachOAnalyzer::SectionInfo &sectInfo, bool malformedSectionRange, bool &stop) {
4367 if ( vmAddr < sectInfo.sectAddr ) {
4368 return;
4369 }
4370 if ( vmAddr >= ( sectInfo.sectAddr + sectInfo.sectSize) ) {
4371 return;
4372 }
4373
4374 // We can't scan this section if it overlaps with the fairplay range
4375 if ( fairplayTextOffsetEnd < sectInfo.sectFileOffset ) {
4376 // Fairplay range ends before section
4377 } else if ( fairplayTextOffsetStart > (sectInfo.sectFileOffset + sectInfo.sectSize) ) {
4378 // Fairplay range starts after section
4379 } else {
4380 // Must overlap
4381 stop = true;
4382 return;
4383 }
4384
4385 // The section handler may also reject this section
4386 if ( sectionHandler != nullptr ) {
4387 if (!sectionHandler(sectInfo)) {
4388 stop = true;
4389 return;
4390 }
4391 }
4392 // Cache this section for later.
4393 sectionInfos.push_back(sectInfo);
4394 foundValidSection = true;
4395 stop = true;
4396 });
4397
4398 return foundValidSection;
4399 }
4400
4401 void MachOAnalyzer::forEachObjCClass(Diagnostics& diag, const VMAddrConverter& vmAddrConverter,
4402 void (^handler)(Diagnostics& diag, uint64_t classVMAddr,
4403 uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
4404 const ObjCClassInfo& objcClass, bool isMetaClass)) const {
4405 const uint64_t ptrSize = pointerSize();
4406 intptr_t slide = getSlide();
4407
4408 forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
4409 if ( strncmp(sectInfo.segInfo.segName, "__DATA", 6) != 0 )
4410 return;
4411 if ( strcmp(sectInfo.sectName, "__objc_classlist") != 0 )
4412 return;
4413 const uint8_t* classList = (uint8_t*)(sectInfo.sectAddr + slide);
4414 uint64_t classListSize = sectInfo.sectSize;
4415
4416 if ( (classListSize % ptrSize) != 0 ) {
4417 diag.error("Invalid objc class section size");
4418 return;
4419 }
4420
4421 if ( ptrSize == 8 ) {
4422 typedef uint64_t PtrTy;
4423
4424 for (uint64_t i = 0; i != classListSize; i += sizeof(PtrTy)) {
4425 uint64_t classVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(classList + i));
4426 parseObjCClass(diag, vmAddrConverter, classVMAddr, ^(Diagnostics& classDiag, uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, const ObjCClassInfo& objcClass) {
4427 handler(classDiag, classVMAddr, classSuperclassVMAddr, classDataVMAddr, objcClass, false);
4428 if (classDiag.hasError())
4429 return;
4430
4431 // Then parse and call for the metaclass
4432 uint64_t isaVMAddr = objcClass.isaVMAddr;
4433 parseObjCClass(classDiag, vmAddrConverter, isaVMAddr, ^(Diagnostics& metaclassDiag, uint64_t metaclassSuperclassVMAddr, uint64_t metaclassDataVMAddr, const ObjCClassInfo& objcMetaclass) {
4434 handler(metaclassDiag, isaVMAddr, metaclassSuperclassVMAddr, metaclassDataVMAddr, objcMetaclass, true);
4435 });
4436 });
4437 if (diag.hasError())
4438 return;
4439 }
4440 } else {
4441 typedef uint32_t PtrTy;
4442
4443 for (uint64_t i = 0; i != classListSize; i += sizeof(PtrTy)) {
4444 uint64_t classVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(classList + i));
4445 parseObjCClass(diag, vmAddrConverter, classVMAddr, ^(Diagnostics& classDiag, uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, const ObjCClassInfo& objcClass) {
4446 handler(classDiag, classVMAddr, classSuperclassVMAddr, classDataVMAddr, objcClass, false);
4447 if (classDiag.hasError())
4448 return;
4449
4450 // Then parse and call for the metaclass
4451 uint64_t isaVMAddr = objcClass.isaVMAddr;
4452 parseObjCClass(classDiag, vmAddrConverter, isaVMAddr, ^(Diagnostics& metaclassDiag, uint64_t metaclassSuperclassVMAddr, uint64_t metaclassDataVMAddr, const ObjCClassInfo& objcMetaclass) {
4453 handler(metaclassDiag, isaVMAddr, metaclassSuperclassVMAddr, metaclassDataVMAddr, objcMetaclass, true);
4454 });
4455 });
4456 if (diag.hasError())
4457 return;
4458 }
4459 }
4460 });
4461 }
4462
4463 void MachOAnalyzer::parseObjCClass(Diagnostics& diag, const VMAddrConverter& vmAddrConverter,
4464 uint64_t classVMAddr,
4465 void (^handler)(Diagnostics& diag,
4466 uint64_t classSuperclassVMAddr,
4467 uint64_t classDataVMAddr,
4468 const ObjCClassInfo& objcClass)) const {
4469 const uint64_t ptrSize = pointerSize();
4470 intptr_t slide = getSlide();
4471
4472 uint64_t classSuperclassVMAddr = 0;
4473 uint64_t classDataVMAddr = 0;
4474 ObjCClassInfo objcClass;
4475
4476 if ( ptrSize == 8 ) {
4477 struct objc_class_t {
4478 uint64_t isaVMAddr;
4479 uint64_t superclassVMAddr;
4480 uint64_t methodCacheBuckets;
4481 uint64_t methodCacheProperties;
4482 uint64_t dataVMAddrAndFastFlags;
4483 };
4484 // This matches "struct TargetClassMetadata" from Metadata.h in Swift
4485 struct swift_class_metadata_t : objc_class_t {
4486 uint32_t swiftClassFlags;
4487 };
4488 enum : uint64_t {
4489 FAST_DATA_MASK = 0x00007ffffffffff8ULL
4490 };
4491 classSuperclassVMAddr = classVMAddr + offsetof(objc_class_t, superclassVMAddr);
4492 classDataVMAddr = classVMAddr + offsetof(objc_class_t, dataVMAddrAndFastFlags);
4493
4494 // First call the handler on the class
4495 const objc_class_t* classPtr = (const objc_class_t*)(classVMAddr + slide);
4496 const swift_class_metadata_t* swiftClassPtr = (const swift_class_metadata_t*)classPtr;
4497 objcClass.isaVMAddr = vmAddrConverter.convertToVMAddr(classPtr->isaVMAddr);
4498 objcClass.superclassVMAddr = vmAddrConverter.convertToVMAddr(classPtr->superclassVMAddr);
4499 objcClass.methodCacheVMAddr = classPtr->methodCacheProperties == 0 ? 0 : vmAddrConverter.convertToVMAddr(classPtr->methodCacheProperties);
4500 objcClass.dataVMAddr = vmAddrConverter.convertToVMAddr(classPtr->dataVMAddrAndFastFlags) & FAST_DATA_MASK;
4501 objcClass.vmAddrConverter = vmAddrConverter;
4502 objcClass.isSwiftLegacy = classPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_LEGACY;
4503 objcClass.isSwiftStable = classPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_STABLE;
4504 // The Swift class flags are only present if the class is swift
4505 objcClass.swiftClassFlags = (objcClass.isSwiftLegacy || objcClass.isSwiftStable) ? swiftClassPtr->swiftClassFlags : 0;
4506 } else {
4507 struct objc_class_t {
4508 uint32_t isaVMAddr;
4509 uint32_t superclassVMAddr;
4510 uint32_t methodCacheBuckets;
4511 uint32_t methodCacheProperties;
4512 uint32_t dataVMAddrAndFastFlags;
4513 };
4514 // This matches "struct TargetClassMetadata" from Metadata.h in Swift
4515 struct swift_class_metadata_t : objc_class_t {
4516 uint32_t swiftClassFlags;
4517 };
4518 enum : uint32_t {
4519 FAST_DATA_MASK = 0xfffffffcUL
4520 };
4521 classSuperclassVMAddr = classVMAddr + offsetof(objc_class_t, superclassVMAddr);
4522 classDataVMAddr = classVMAddr + offsetof(objc_class_t, dataVMAddrAndFastFlags);
4523
4524 // First call the handler on the class
4525 const objc_class_t* classPtr = (const objc_class_t*)(classVMAddr + slide);
4526 const swift_class_metadata_t* swiftClassPtr = (const swift_class_metadata_t*)classPtr;
4527 objcClass.isaVMAddr = vmAddrConverter.convertToVMAddr(classPtr->isaVMAddr);
4528 objcClass.superclassVMAddr = vmAddrConverter.convertToVMAddr(classPtr->superclassVMAddr);
4529 objcClass.methodCacheVMAddr = classPtr->methodCacheProperties == 0 ? 0 : vmAddrConverter.convertToVMAddr(classPtr->methodCacheProperties);
4530 objcClass.dataVMAddr = vmAddrConverter.convertToVMAddr(classPtr->dataVMAddrAndFastFlags) & FAST_DATA_MASK;
4531 objcClass.vmAddrConverter = vmAddrConverter;
4532 objcClass.isSwiftLegacy = classPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_LEGACY;
4533 objcClass.isSwiftStable = classPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_STABLE;
4534 // The Swift class flags are only present if the class is swift
4535 objcClass.swiftClassFlags = (objcClass.isSwiftLegacy || objcClass.isSwiftStable) ? swiftClassPtr->swiftClassFlags : 0;
4536 }
4537
4538 handler(diag, classSuperclassVMAddr, classDataVMAddr, objcClass);
4539 }
4540
4541 void MachOAnalyzer::forEachObjCCategory(Diagnostics& diag, const VMAddrConverter& vmAddrConverter,
4542 void (^handler)(Diagnostics& diag, uint64_t categoryVMAddr,
4543 const dyld3::MachOAnalyzer::ObjCCategory& objcCategory)) const {
4544 const uint64_t ptrSize = pointerSize();
4545 intptr_t slide = getSlide();
4546
4547 forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
4548 if ( strncmp(sectInfo.segInfo.segName, "__DATA", 6) != 0 )
4549 return;
4550 if ( strcmp(sectInfo.sectName, "__objc_catlist") != 0 )
4551 return;
4552 const uint8_t* categoryList = (uint8_t*)(sectInfo.sectAddr + slide);
4553 uint64_t categoryListSize = sectInfo.sectSize;
4554
4555 if ( (categoryListSize % ptrSize) != 0 ) {
4556 diag.error("Invalid objc category section size");
4557 return;
4558 }
4559
4560 if ( ptrSize == 8 ) {
4561 typedef uint64_t PtrTy;
4562 struct objc_category_t {
4563 PtrTy nameVMAddr;
4564 PtrTy clsVMAddr;
4565 PtrTy instanceMethodsVMAddr;
4566 PtrTy classMethodsVMAddr;
4567 PtrTy protocolsVMAddr;
4568 PtrTy instancePropertiesVMAddr;
4569 };
4570 for (uint64_t i = 0; i != categoryListSize; i += sizeof(PtrTy)) {
4571 uint64_t categoryVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(categoryList + i));
4572
4573 const objc_category_t* categoryPtr = (const objc_category_t*)(categoryVMAddr + slide);
4574 ObjCCategory objCCategory;
4575 objCCategory.nameVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->nameVMAddr);
4576 objCCategory.clsVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->clsVMAddr);
4577 objCCategory.instanceMethodsVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->instanceMethodsVMAddr);
4578 objCCategory.classMethodsVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->classMethodsVMAddr);
4579 objCCategory.protocolsVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->protocolsVMAddr);
4580 objCCategory.instancePropertiesVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->instancePropertiesVMAddr);
4581 handler(diag, categoryVMAddr, objCCategory);
4582 if (diag.hasError())
4583 return;
4584 }
4585 } else {
4586 typedef uint32_t PtrTy;
4587 struct objc_category_t {
4588 PtrTy nameVMAddr;
4589 PtrTy clsVMAddr;
4590 PtrTy instanceMethodsVMAddr;
4591 PtrTy classMethodsVMAddr;
4592 PtrTy protocolsVMAddr;
4593 PtrTy instancePropertiesVMAddr;
4594 };
4595 for (uint64_t i = 0; i != categoryListSize; i += sizeof(PtrTy)) {
4596 uint64_t categoryVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(categoryList + i));
4597
4598 const objc_category_t* categoryPtr = (const objc_category_t*)(categoryVMAddr + slide);
4599 ObjCCategory objCCategory;
4600 objCCategory.nameVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->nameVMAddr);
4601 objCCategory.clsVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->clsVMAddr);
4602 objCCategory.instanceMethodsVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->instanceMethodsVMAddr);
4603 objCCategory.classMethodsVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->classMethodsVMAddr);
4604 objCCategory.protocolsVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->protocolsVMAddr);
4605 objCCategory.instancePropertiesVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->instancePropertiesVMAddr);
4606 handler(diag, categoryVMAddr, objCCategory);
4607 if (diag.hasError())
4608 return;
4609 }
4610 }
4611 });
4612 }
4613
4614 void MachOAnalyzer::forEachObjCProtocol(Diagnostics& diag, const VMAddrConverter& vmAddrConverter,
4615 void (^handler)(Diagnostics& diag, uint64_t categoryVMAddr,
4616 const dyld3::MachOAnalyzer::ObjCProtocol& objCProtocol)) const {
4617 const uint64_t ptrSize = pointerSize();
4618 intptr_t slide = getSlide();
4619
4620 forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
4621 if ( strncmp(sectInfo.segInfo.segName, "__DATA", 6) != 0 )
4622 return;
4623 if ( strcmp(sectInfo.sectName, "__objc_protolist") != 0 )
4624 return;
4625 const uint8_t* protocolList = (uint8_t*)(sectInfo.sectAddr + slide);
4626 uint64_t protocolListSize = sectInfo.sectSize;
4627
4628 if ( (protocolListSize % ptrSize) != 0 ) {
4629 diag.error("Invalid objc protocol section size");
4630 return;
4631 }
4632
4633 if ( ptrSize == 8 ) {
4634 typedef uint64_t PtrTy;
4635 struct protocol_t {
4636 PtrTy isaVMAddr;
4637 PtrTy nameVMAddr;
4638 PtrTy protocolsVMAddr;
4639 PtrTy instanceMethodsVMAddr;
4640 PtrTy classMethodsVMAddr;
4641 PtrTy optionalInstanceMethodsVMAddr;
4642 PtrTy optionalClassMethodsVMAddr;
4643 PtrTy instancePropertiesVMAddr;
4644 uint32_t size;
4645 uint32_t flags;
4646 // Fields below this point are not always present on disk.
4647 PtrTy extendedMethodTypesVMAddr;
4648 PtrTy demangledNameVMAddr;
4649 PtrTy classPropertiesVMAddr;
4650 };
4651 for (uint64_t i = 0; i != protocolListSize; i += sizeof(PtrTy)) {
4652 uint64_t protocolVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(protocolList + i));
4653
4654 const protocol_t* protocolPtr = (const protocol_t*)(protocolVMAddr + slide);
4655 ObjCProtocol objCProtocol;
4656 objCProtocol.isaVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->isaVMAddr);
4657 objCProtocol.nameVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->nameVMAddr);
4658 objCProtocol.protocolsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->protocolsVMAddr);
4659 objCProtocol.instanceMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->instanceMethodsVMAddr);
4660 objCProtocol.classMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->classMethodsVMAddr);
4661 objCProtocol.optionalInstanceMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->optionalInstanceMethodsVMAddr);
4662 objCProtocol.optionalClassMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->optionalClassMethodsVMAddr);
4663
4664 handler(diag, protocolVMAddr, objCProtocol);
4665 if (diag.hasError())
4666 return;
4667 }
4668 } else {
4669 typedef uint32_t PtrTy;
4670 struct protocol_t {
4671 PtrTy isaVMAddr;
4672 PtrTy nameVMAddr;
4673 PtrTy protocolsVMAddr;
4674 PtrTy instanceMethodsVMAddr;
4675 PtrTy classMethodsVMAddr;
4676 PtrTy optionalInstanceMethodsVMAddr;
4677 PtrTy optionalClassMethodsVMAddr;
4678 PtrTy instancePropertiesVMAddr;
4679 uint32_t size;
4680 uint32_t flags;
4681 // Fields below this point are not always present on disk.
4682 PtrTy extendedMethodTypesVMAddr;
4683 PtrTy demangledNameVMAddr;
4684 PtrTy classPropertiesVMAddr;
4685 };
4686 for (uint64_t i = 0; i != protocolListSize; i += sizeof(PtrTy)) {
4687 uint64_t protocolVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(protocolList + i));
4688
4689 const protocol_t* protocolPtr = (const protocol_t*)(protocolVMAddr + slide);
4690 ObjCProtocol objCProtocol;
4691 objCProtocol.isaVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->isaVMAddr);
4692 objCProtocol.nameVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->nameVMAddr);
4693 objCProtocol.protocolsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->protocolsVMAddr);
4694 objCProtocol.instanceMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->instanceMethodsVMAddr);
4695 objCProtocol.classMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->classMethodsVMAddr);
4696 objCProtocol.optionalInstanceMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->optionalInstanceMethodsVMAddr);
4697 objCProtocol.optionalClassMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->optionalClassMethodsVMAddr);
4698
4699 handler(diag, protocolVMAddr, objCProtocol);
4700 if (diag.hasError())
4701 return;
4702 }
4703 }
4704 });
4705 }
4706
4707 void MachOAnalyzer::forEachObjCMethod(uint64_t methodListVMAddr, const VMAddrConverter& vmAddrConverter,
4708 void (^handler)(uint64_t methodVMAddr, const ObjCMethod& method),
4709 bool* isRelativeMethodList) const {
4710 if ( methodListVMAddr == 0 )
4711 return;
4712
4713 const uint64_t ptrSize = pointerSize();
4714 intptr_t slide = getSlide();
4715
4716 if ( ptrSize == 8 ) {
4717 typedef uint64_t PtrTy;
4718 struct method_list_t {
4719 uint32_t entsize;
4720 uint32_t count;
4721 PtrTy methodArrayBase; // Note this is the start the array method_t[0]
4722
4723 uint32_t getEntsize() const {
4724 return entsize & ObjCMethodList::methodListSizeMask;
4725 }
4726
4727 bool usesDirectOffsetsToSelectors() const {
4728 return (entsize & 0x40000000) != 0;
4729 }
4730
4731 bool usesRelativeOffsets() const {
4732 return (entsize & 0x80000000) != 0;
4733 }
4734 };
4735
4736 struct method_t {
4737 PtrTy nameVMAddr; // SEL
4738 PtrTy typesVMAddr; // const char *
4739 PtrTy impVMAddr; // IMP
4740 };
4741
4742 struct relative_method_t {
4743 int32_t nameOffset; // SEL*
4744 int32_t typesOffset; // const char *
4745 int32_t impOffset; // IMP
4746 };
4747
4748 const method_list_t* methodList = (const method_list_t*)(methodListVMAddr + slide);
4749 if ( methodList == nullptr )
4750 return;
4751 bool relativeMethodListsAreOffsetsToSelectors = methodList->usesDirectOffsetsToSelectors();
4752 uint64_t methodListArrayBaseVMAddr = methodListVMAddr + offsetof(method_list_t, methodArrayBase);
4753 for (unsigned i = 0; i != methodList->count; ++i) {
4754 uint64_t methodEntryOffset = i * methodList->getEntsize();
4755 uint64_t methodVMAddr = methodListArrayBaseVMAddr + methodEntryOffset;
4756 ObjCMethod method;
4757 if ( methodList->usesRelativeOffsets() ) {
4758 const relative_method_t* methodPtr = (const relative_method_t*)(methodVMAddr + slide);
4759 if ( relativeMethodListsAreOffsetsToSelectors ) {
4760 method.nameVMAddr = methodVMAddr + offsetof(relative_method_t, nameOffset) + methodPtr->nameOffset;
4761 } else {
4762 PtrTy* nameLocation = (PtrTy*)((uint8_t*)&methodPtr->nameOffset + methodPtr->nameOffset);
4763 method.nameVMAddr = vmAddrConverter.convertToVMAddr(*nameLocation);
4764 }
4765 method.typesVMAddr = methodVMAddr + offsetof(relative_method_t, typesOffset) + methodPtr->typesOffset;
4766 method.impVMAddr = methodVMAddr + offsetof(relative_method_t, impOffset) + methodPtr->impOffset;
4767 method.nameLocationVMAddr = methodVMAddr + offsetof(relative_method_t, nameOffset) + methodPtr->nameOffset;
4768 } else {
4769 const method_t* methodPtr = (const method_t*)(methodVMAddr + slide);
4770 method.nameVMAddr = vmAddrConverter.convertToVMAddr(methodPtr->nameVMAddr);
4771 method.typesVMAddr = vmAddrConverter.convertToVMAddr(methodPtr->typesVMAddr);
4772 method.impVMAddr = vmAddrConverter.convertToVMAddr(methodPtr->impVMAddr);
4773 method.nameLocationVMAddr = methodVMAddr + offsetof(method_t, nameVMAddr);
4774 }
4775 handler(methodVMAddr, method);
4776 }
4777
4778 if ( isRelativeMethodList != nullptr )
4779 *isRelativeMethodList = methodList->usesRelativeOffsets();
4780 } else {
4781 typedef uint32_t PtrTy;
4782 struct method_list_t {
4783 uint32_t entsize;
4784 uint32_t count;
4785 PtrTy methodArrayBase; // Note this is the start the array method_t[0]
4786
4787 uint32_t getEntsize() const {
4788 return entsize & ObjCMethodList::methodListSizeMask;
4789 }
4790
4791 bool usesDirectOffsetsToSelectors() const {
4792 return (entsize & 0x40000000) != 0;
4793 }
4794
4795 bool usesRelativeOffsets() const {
4796 return (entsize & 0x80000000) != 0;
4797 }
4798 };
4799
4800 struct method_t {
4801 PtrTy nameVMAddr; // SEL
4802 PtrTy typesVMAddr; // const char *
4803 PtrTy impVMAddr; // IMP
4804 };
4805
4806 struct relative_method_t {
4807 int32_t nameOffset; // SEL*
4808 int32_t typesOffset; // const char *
4809 int32_t impOffset; // IMP
4810 };
4811
4812 const method_list_t* methodList = (const method_list_t*)(methodListVMAddr + slide);
4813 if ( methodList == nullptr )
4814 return;
4815 bool relativeMethodListsAreOffsetsToSelectors = methodList->usesDirectOffsetsToSelectors();
4816 uint64_t methodListArrayBaseVMAddr = methodListVMAddr + offsetof(method_list_t, methodArrayBase);
4817 for (unsigned i = 0; i != methodList->count; ++i) {
4818 uint64_t methodEntryOffset = i * methodList->getEntsize();
4819 uint64_t methodVMAddr = methodListArrayBaseVMAddr + methodEntryOffset;
4820 ObjCMethod method;
4821 if ( methodList->usesRelativeOffsets() ) {
4822 const relative_method_t* methodPtr = (const relative_method_t*)(methodVMAddr + slide);
4823 if ( relativeMethodListsAreOffsetsToSelectors ) {
4824 method.nameVMAddr = methodVMAddr + offsetof(relative_method_t, nameOffset) + methodPtr->nameOffset;
4825 } else {
4826 PtrTy* nameLocation = (PtrTy*)((uint8_t*)&methodPtr->nameOffset + methodPtr->nameOffset);
4827 method.nameVMAddr = vmAddrConverter.convertToVMAddr(*nameLocation);
4828 }
4829 method.typesVMAddr = methodVMAddr + offsetof(relative_method_t, typesOffset) + methodPtr->typesOffset;
4830 method.impVMAddr = methodVMAddr + offsetof(relative_method_t, impOffset) + methodPtr->impOffset;
4831 method.nameLocationVMAddr = methodVMAddr + offsetof(relative_method_t, nameOffset) + methodPtr->nameOffset;
4832 } else {
4833 const method_t* methodPtr = (const method_t*)(methodVMAddr + slide);
4834 method.nameVMAddr = vmAddrConverter.convertToVMAddr(methodPtr->nameVMAddr);
4835 method.typesVMAddr = vmAddrConverter.convertToVMAddr(methodPtr->typesVMAddr);
4836 method.impVMAddr = vmAddrConverter.convertToVMAddr(methodPtr->impVMAddr);
4837 method.nameLocationVMAddr = methodVMAddr + offsetof(method_t, nameVMAddr);
4838 }
4839 handler(methodVMAddr, method);
4840 }
4841
4842 if ( isRelativeMethodList != nullptr )
4843 *isRelativeMethodList = methodList->usesRelativeOffsets();
4844 }
4845 }
4846
4847 void MachOAnalyzer::forEachObjCProperty(uint64_t propertyListVMAddr, const VMAddrConverter& vmAddrConverter,
4848 void (^handler)(uint64_t propertyVMAddr, const ObjCProperty& property)) const {
4849 if ( propertyListVMAddr == 0 )
4850 return;
4851
4852 const uint64_t ptrSize = pointerSize();
4853 intptr_t slide = getSlide();
4854
4855 if ( ptrSize == 8 ) {
4856 typedef uint64_t PtrTy;
4857 struct property_list_t {
4858 uint32_t entsize;
4859 uint32_t count;
4860 PtrTy propertyArrayBase; // Note this is the start the array property_t[0]
4861
4862 uint32_t getEntsize() const {
4863 return (entsize) & ~(uint32_t)3;
4864 }
4865 };
4866
4867 struct property_t {
4868 PtrTy nameVMAddr; // SEL
4869 PtrTy attributesVMAddr; // const char *
4870 };
4871
4872 const property_list_t* propertyList = (const property_list_t*)(propertyListVMAddr + slide);
4873 uint64_t propertyListArrayBaseVMAddr = propertyListVMAddr + offsetof(property_list_t, propertyArrayBase);
4874 for (unsigned i = 0; i != propertyList->count; ++i) {
4875 uint64_t propertyEntryOffset = i * propertyList->getEntsize();
4876 uint64_t propertyVMAddr = propertyListArrayBaseVMAddr + propertyEntryOffset;
4877 const property_t* propertyPtr = (const property_t*)(propertyVMAddr + slide);
4878 ObjCProperty property;
4879 property.nameVMAddr = vmAddrConverter.convertToVMAddr(propertyPtr->nameVMAddr);
4880 property.attributesVMAddr = vmAddrConverter.convertToVMAddr(propertyPtr->attributesVMAddr);
4881 handler(propertyVMAddr, property);
4882 }
4883 } else {
4884 typedef uint32_t PtrTy;
4885 struct property_list_t {
4886 uint32_t entsize;
4887 uint32_t count;
4888 PtrTy propertyArrayBase; // Note this is the start the array property_t[0]
4889
4890 uint32_t getEntsize() const {
4891 return (entsize) & ~(uint32_t)3;
4892 }
4893 };
4894
4895 struct property_t {
4896 PtrTy nameVMAddr; // SEL
4897 PtrTy attributesVMAddr; // const char *
4898 };
4899
4900 const property_list_t* propertyList = (const property_list_t*)(propertyListVMAddr + slide);
4901 uint64_t propertyListArrayBaseVMAddr = propertyListVMAddr + offsetof(property_list_t, propertyArrayBase);
4902 for (unsigned i = 0; i != propertyList->count; ++i) {
4903 uint64_t propertyEntryOffset = i * propertyList->getEntsize();
4904 uint64_t propertyVMAddr = propertyListArrayBaseVMAddr + propertyEntryOffset;
4905 const property_t* propertyPtr = (const property_t*)(propertyVMAddr + slide);
4906 ObjCProperty property;
4907 property.nameVMAddr = vmAddrConverter.convertToVMAddr(propertyPtr->nameVMAddr);
4908 property.attributesVMAddr = vmAddrConverter.convertToVMAddr(propertyPtr->attributesVMAddr);
4909 handler(propertyVMAddr, property);
4910 }
4911 }
4912 }
4913
4914 void MachOAnalyzer::forEachObjCProtocol(uint64_t protocolListVMAddr, const VMAddrConverter& vmAddrConverter,
4915 void (^handler)(uint64_t protocolRefVMAddr, const ObjCProtocol&)) const
4916 {
4917 if ( protocolListVMAddr == 0 )
4918 return;
4919
4920 auto ptrSize = pointerSize();
4921 intptr_t slide = getSlide();
4922
4923 if ( ptrSize == 8 ) {
4924 typedef uint64_t PtrTy;
4925 struct protocol_ref_t {
4926 PtrTy refVMAddr;
4927 };
4928 struct protocol_list_t {
4929 PtrTy count;
4930 protocol_ref_t array[];
4931 };
4932 struct protocol_t {
4933 PtrTy isaVMAddr;
4934 PtrTy nameVMAddr;
4935 PtrTy protocolsVMAddr;
4936 PtrTy instanceMethodsVMAddr;
4937 PtrTy classMethodsVMAddr;
4938 PtrTy optionalInstanceMethodsVMAddr;
4939 PtrTy optionalClassMethodsVMAddr;
4940 PtrTy instancePropertiesVMAddr;
4941 uint32_t size;
4942 uint32_t flags;
4943 // Fields below this point are not always present on disk.
4944 PtrTy extendedMethodTypesVMAddr;
4945 PtrTy demangledNameVMAddr;
4946 PtrTy classPropertiesVMAddr;
4947 };
4948
4949 const protocol_list_t* protoList = (const protocol_list_t*)(protocolListVMAddr + slide);
4950 for (PtrTy i = 0; i != protoList->count; ++i) {
4951 uint64_t protocolVMAddr = vmAddrConverter.convertToVMAddr(protoList->array[i].refVMAddr);
4952
4953 const protocol_t* protocolPtr = (const protocol_t*)(protocolVMAddr + slide);
4954 ObjCProtocol objCProtocol;
4955 objCProtocol.isaVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->isaVMAddr);
4956 objCProtocol.nameVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->nameVMAddr);
4957 objCProtocol.protocolsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->protocolsVMAddr);
4958 objCProtocol.instanceMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->instanceMethodsVMAddr);
4959 objCProtocol.classMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->classMethodsVMAddr);
4960 objCProtocol.optionalInstanceMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->optionalInstanceMethodsVMAddr);
4961 objCProtocol.optionalClassMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->optionalClassMethodsVMAddr);
4962
4963 handler(protocolVMAddr, objCProtocol);
4964 }
4965 } else {
4966 typedef uint32_t PtrTy;
4967 struct protocol_ref_t {
4968 PtrTy refVMAddr;
4969 };
4970 struct protocol_list_t {
4971 PtrTy count;
4972 protocol_ref_t array[];
4973 };
4974 struct protocol_t {
4975 PtrTy isaVMAddr;
4976 PtrTy nameVMAddr;
4977 PtrTy protocolsVMAddr;
4978 PtrTy instanceMethodsVMAddr;
4979 PtrTy classMethodsVMAddr;
4980 PtrTy optionalInstanceMethodsVMAddr;
4981 PtrTy optionalClassMethodsVMAddr;
4982 PtrTy instancePropertiesVMAddr;
4983 uint32_t size;
4984 uint32_t flags;
4985 // Fields below this point are not always present on disk.
4986 PtrTy extendedMethodTypesVMAddr;
4987 PtrTy demangledNameVMAddr;
4988 PtrTy classPropertiesVMAddr;
4989 };
4990
4991 const protocol_list_t* protoList = (const protocol_list_t*)(protocolListVMAddr + slide);
4992 for (PtrTy i = 0; i != protoList->count; ++i) {
4993 uint64_t protocolVMAddr = vmAddrConverter.convertToVMAddr(protoList->array[i].refVMAddr);
4994
4995 const protocol_t* protocolPtr = (const protocol_t*)(protocolVMAddr + slide);
4996 ObjCProtocol objCProtocol;
4997 objCProtocol.isaVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->isaVMAddr);
4998 objCProtocol.nameVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->nameVMAddr);
4999 objCProtocol.protocolsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->protocolsVMAddr);
5000 objCProtocol.instanceMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->instanceMethodsVMAddr);
5001 objCProtocol.classMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->classMethodsVMAddr);
5002 objCProtocol.optionalInstanceMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->optionalInstanceMethodsVMAddr);
5003 objCProtocol.optionalClassMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->optionalClassMethodsVMAddr);
5004
5005 handler(protocolVMAddr, objCProtocol);
5006 }
5007 }
5008 }
5009
5010
5011 void MachOAnalyzer::forEachObjCSelectorReference(Diagnostics& diag, const VMAddrConverter& vmAddrConverter,
5012 void (^handler)(uint64_t selRefVMAddr, uint64_t selRefTargetVMAddr)) const {
5013 const uint64_t ptrSize = pointerSize();
5014 intptr_t slide = getSlide();
5015
5016 forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
5017 if ( strncmp(sectInfo.segInfo.segName, "__DATA", 6) != 0 )
5018 return;
5019 if ( strcmp(sectInfo.sectName, "__objc_selrefs") != 0 )
5020 return;
5021 uint64_t selRefSectionVMAddr = sectInfo.sectAddr;
5022 const uint8_t* selRefs = (uint8_t*)(selRefSectionVMAddr + slide);
5023 uint64_t selRefsSize = sectInfo.sectSize;
5024
5025 if ( (selRefsSize % ptrSize) != 0 ) {
5026 diag.error("Invalid sel ref section size");
5027 return;
5028 }
5029
5030 if ( ptrSize == 8 ) {
5031 typedef uint64_t PtrTy;
5032 for (uint64_t i = 0; i != selRefsSize; i += sizeof(PtrTy)) {
5033 uint64_t selRefVMAddr = selRefSectionVMAddr + i;
5034 uint64_t selRefTargetVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(selRefs + i));
5035 handler(selRefVMAddr, selRefTargetVMAddr);
5036 if (diag.hasError()) {
5037 stop = true;
5038 return;
5039 }
5040 }
5041 } else {
5042 typedef uint32_t PtrTy;
5043 for (uint64_t i = 0; i != selRefsSize; i += sizeof(PtrTy)) {
5044 uint64_t selRefVMAddr = selRefSectionVMAddr + i;
5045 uint64_t selRefTargetVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(selRefs + i));
5046 handler(selRefVMAddr, selRefTargetVMAddr);
5047 if (diag.hasError()) {
5048 stop = true;
5049 return;
5050 }
5051 }
5052 }
5053 });
5054 }
5055
5056 void MachOAnalyzer::forEachObjCMethodName(void (^handler)(const char* methodName)) const {
5057 intptr_t slide = getSlide();
5058 forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
5059 if ( strcmp(sectInfo.segInfo.segName, "__TEXT") != 0 )
5060 return;
5061 if ( strcmp(sectInfo.sectName, "__objc_methname") != 0 )
5062 return;
5063 if ( sectInfo.segInfo.isProtected || ( (sectInfo.sectFlags & SECTION_TYPE) != S_CSTRING_LITERALS ) ) {
5064 stop = true;
5065 return;
5066 }
5067 if ( malformedSectionRange ) {
5068 stop = true;
5069 return;
5070 }
5071
5072 const char* content = (const char*)(sectInfo.sectAddr + slide);
5073 uint64_t sectionSize = sectInfo.sectSize;
5074
5075 const char* s = (const char*)content;
5076 const char* end = s + sectionSize;
5077 while ( s < end ) {
5078 handler(s);
5079 s += strlen(s) + 1;
5080 }
5081 });
5082 }
5083
5084
5085 bool MachOAnalyzer::hasObjCMessageReferences() const {
5086
5087 __block bool foundSection = false;
5088 forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
5089 if ( strncmp(sectInfo.segInfo.segName, "__DATA", 6) != 0 )
5090 return;
5091 if ( strcmp(sectInfo.sectName, "__objc_msgrefs") != 0 )
5092 return;
5093 foundSection = true;
5094 stop = true;
5095 });
5096 return foundSection;
5097 }
5098
5099 const MachOAnalyzer::ObjCImageInfo* MachOAnalyzer::objcImageInfo() const {
5100 int64_t slide = getSlide();
5101
5102 __block bool foundInvalidObjCImageInfo = false;
5103 __block const ObjCImageInfo* imageInfo = nullptr;
5104 forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& sectionInfo, bool malformedSectionRange, bool& stop) {
5105 if ( strncmp(sectionInfo.segInfo.segName, "__DATA", 6) != 0 )
5106 return;
5107 if (strcmp(sectionInfo.sectName, "__objc_imageinfo") != 0)
5108 return;
5109 if ( malformedSectionRange ) {
5110 stop = true;
5111 return;
5112 }
5113 if ( sectionInfo.sectSize != 8 ) {
5114 stop = true;
5115 return;
5116 }
5117 imageInfo = (const ObjCImageInfo*)(sectionInfo.sectAddr + slide);
5118 if ( (imageInfo->flags & ObjCImageInfo::dyldPreoptimized) != 0 ) {
5119 foundInvalidObjCImageInfo = true;
5120 stop = true;
5121 return;
5122 }
5123 stop = true;
5124 });
5125 if ( foundInvalidObjCImageInfo )
5126 return nullptr;
5127 return imageInfo;
5128 }
5129
5130 uint32_t MachOAnalyzer::loadCommandsFreeSpace() const
5131 {
5132 __block uint32_t firstSectionFileOffset = 0;
5133 __block uint32_t firstSegmentFileOffset = 0;
5134 forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
5135 firstSectionFileOffset = sectInfo.sectFileOffset;
5136 firstSegmentFileOffset = (uint32_t)sectInfo.segInfo.fileOffset;
5137 stop = true;
5138 });
5139
5140 uint32_t headerSize = (this->magic == MH_MAGIC_64) ? sizeof(mach_header_64) : sizeof(mach_header);
5141 uint32_t existSpaceUsed = this->sizeofcmds + headerSize;
5142 return firstSectionFileOffset - firstSegmentFileOffset - existSpaceUsed;
5143 }
5144
5145 void MachOAnalyzer::forEachWeakDef(Diagnostics& diag,
5146 void (^handler)(const char* symbolName, uint64_t imageOffset, bool isFromExportTrie)) const {
5147 uint64_t baseAddress = preferredLoadAddress();
5148 forEachGlobalSymbol(diag, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) {
5149 if ( (n_desc & N_WEAK_DEF) != 0 ) {
5150 handler(symbolName, n_value - baseAddress, false);
5151 }
5152 });
5153 forEachExportedSymbol(diag, ^(const char *symbolName, uint64_t imageOffset, uint64_t flags, uint64_t other, const char *importName, bool &stop) {
5154 if ( (flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ) == 0 )
5155 return;
5156 // Skip resolvers and re-exports
5157 if ( (flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) != 0 )
5158 return;
5159 if ( (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) != 0 )
5160 return;
5161 handler(symbolName, imageOffset, true);
5162 });
5163 }
5164
5165
5166 } // dyld3
5167
5168