Loading...
--- dyld/dyld-1162/mach_o/Header.cpp
+++ dyld/dyld-1335/mach_o/Header.cpp
@@ -33,35 +33,93 @@
#include <unistd.h>
#endif
+#include <mach-o/fixup-chains.h>
+
#include "Array.h"
#include "Header.h"
#include "Architecture.h"
#include "Misc.h"
#include "Policy.h"
#include "LoggingStub.h"
+#include "TargetPolicy.h"
+#include "Version32.h"
+
+#if TARGET_OS_EXCLAVEKIT
+#ifndef VM_PROT_READ
+ #define VM_PROT_READ 1
+#endif
+
+#ifndef VM_PROT_WRITE
+ #define VM_PROT_WRITE 2
+#endif
+
+#ifndef VM_PROT_EXECUTE
+ #define VM_PROT_EXECUTE 4
+#endif
+#endif // TARGET_OS_EXCLAVEKIT
using mach_o::Architecture;
using mach_o::Platform;
using mach_o::PlatformAndVersions;
using mach_o::Policy;
+using mach_o::Version32;
namespace mach_o {
+
//
-// MARK: --- DependentDylibAttributes ---
+// MARK: --- LinkedDylibAttributes ---
//
-constinit const DependentDylibAttributes DependentDylibAttributes::regular;
-constinit const DependentDylibAttributes DependentDylibAttributes::justWeakLink(DYLIB_USE_WEAK_LINK);
-constinit const DependentDylibAttributes DependentDylibAttributes::justUpward(DYLIB_USE_UPWARD);
-constinit const DependentDylibAttributes DependentDylibAttributes::justReExport(DYLIB_USE_REEXPORT);
-constinit const DependentDylibAttributes DependentDylibAttributes::justDelayInit(DYLIB_USE_DELAYED_INIT);
+constinit const LinkedDylibAttributes LinkedDylibAttributes::regular;
+constinit const LinkedDylibAttributes LinkedDylibAttributes::justWeakLink(DYLIB_USE_WEAK_LINK);
+constinit const LinkedDylibAttributes LinkedDylibAttributes::justUpward(DYLIB_USE_UPWARD);
+constinit const LinkedDylibAttributes LinkedDylibAttributes::justReExport(DYLIB_USE_REEXPORT);
+constinit const LinkedDylibAttributes LinkedDylibAttributes::justDelayInit(DYLIB_USE_DELAYED_INIT);
+
+void LinkedDylibAttributes::toString(char buf[64]) const
+{
+ buf[0] = '\0';
+ if ( weakLink )
+ strlcpy(buf, "weak-link", 64);
+ if ( upward ) {
+ if ( buf[0] != '\0' )
+ strlcat(buf, ",", 64);
+ strlcat(buf, "upward", 64);
+ }
+ if ( delayInit ) {
+ if ( buf[0] != '\0' )
+ strlcat(buf, ",", 64);
+ strlcat(buf, "delay-init", 64);
+ }
+ if ( reExport ) {
+ if ( buf[0] != '\0' )
+ strlcat(buf, ",", 64);
+ strlcat(buf, "re-export", 64);
+ }
+}
//
// MARK: --- methods that read mach_header ---
//
+
+bool Header::isSharedCacheEligiblePath(const char* dylibName) {
+ return ( (strncmp(dylibName, "/usr/lib/", 9) == 0)
+ || (strncmp(dylibName, "/System/Library/", 16) == 0)
+ || (strncmp(dylibName, "/System/iOSSupport/usr/lib/", 27) == 0)
+ || (strncmp(dylibName, "/System/iOSSupport/System/Library/", 34) == 0)
+ || (strncmp(dylibName, "/Library/Apple/usr/lib/", 23) == 0)
+ || (strncmp(dylibName, "/Library/Apple/System/Library/", 30) == 0)
+ || (strncmp(dylibName, "/System/DriverKit/", 18) == 0)
+ || (strncmp(dylibName, "/System/Cryptexes/OS/usr/lib/", 29) == 0)
+ || (strncmp(dylibName, "/System/Cryptexes/OS/System/Library/", 36) == 0)
+ || (strncmp(dylibName, "/System/Cryptexes/OS/System/iOSSupport/usr/lib/", 47) == 0)
+ || (strncmp(dylibName, "/System/Cryptexes/OS/System/iOSSupport/System/Library/", 54) == 0)
+ || (strncmp(dylibName, "/System/ExclaveKit/usr/lib/", 27) == 0)
+ || (strncmp(dylibName, "/System/ExclaveKit/System/Library/", 34) == 0));
+}
bool Header::hasMachOMagic() const
{
@@ -89,6 +147,11 @@
return 8;
else
return 4;
+}
+
+uint32_t Header::ncmds() const
+{
+ return mh.ncmds;
}
bool Header::uses16KPages() const
@@ -128,9 +191,10 @@
bool Header::isDyldManaged() const
{
switch ( mh.filetype ) {
+ case MH_DYLIB:
case MH_BUNDLE:
+ return true;
case MH_EXECUTE:
- case MH_DYLIB:
return ((mh.flags & MH_DYLDLINK) != 0);
default:
break;
@@ -143,6 +207,16 @@
return (mh.filetype == MH_DYLIB);
}
+bool Header::isDylibOrStub() const
+{
+ return (mh.filetype == MH_DYLIB) || (mh.filetype == MH_DYLIB_STUB);
+}
+
+bool Header::isDylibStub() const
+{
+ return (mh.filetype == MH_DYLIB_STUB);
+}
+
bool Header::isBundle() const
{
return (mh.filetype == MH_BUNDLE);
@@ -187,6 +261,11 @@
return (mh.filetype == MH_PRELOAD);
}
+bool Header::usesTwoLevelNamespace() const
+{
+ return (mh.flags & MH_TWOLEVEL);
+}
+
bool Header::hasWeakDefs() const
{
return (mh.flags & MH_WEAK_DEFINES);
@@ -207,9 +286,10 @@
if ( content.size() < sizeof(mach_header) )
return nullptr;
- const Header* mh = (const Header*)content.data();
- if ( mh->hasMachOMagic() )
- return mh;
+ if ( const Header* mh = (const Header*)content.data() ) {
+ if ( mh->hasMachOMagic() )
+ return mh;
+ }
return nullptr;
}
@@ -244,6 +324,11 @@
return (this->mh.flags & MH_SIM_SUPPORT) != 0;
}
+bool Header::noDynamicAccess() const
+{
+ return (this->mh.flags & MH_NO_DYNAMIC_ACCESS) != 0;
+}
+
//
// MARK: --- methods for validating mach-o content ---
@@ -255,13 +340,17 @@
__block PlatformAndVersions pvs;
forEachPlatformLoadCommand(^(Platform platform, Version32 minOS, Version32 sdk) {
Error err = pvs.zip({ platform, minOS, sdk });
- assert(err.noError());
+ // .zip() will not combine platforms if it is an invalid combo
});
return pvs;
}
Error Header::validSemanticsPlatform() const
{
+ // Kernel Collections (MH_FILESET) don't have a platform. Skip them
+ if ( isFileSet() )
+ return Error::none();
+
// should be one platform load command (exception is zippered dylibs)
__block PlatformAndVersions pvs;
__block Error badPlatform;
@@ -277,10 +366,16 @@
if ( badPlatform )
return std::move(badPlatform);
-#if BUILDING_MACHO_WRITER
- if ( pvs.platform.empty() )
+ if ( pvs.platform.empty() && gHeaderAllowEmptyPlatform )
return Error::none(); // allow empty platform in static linker
-#endif
+
+ // preloads usually don't have a platform
+ if ( isPreload() )
+ return Error::none();
+
+ // static executables also may not have one
+ if ( isStaticExecutable() )
+ return Error::none();
return pvs.platform.valid();
}
@@ -288,7 +383,7 @@
Error Header::valid(uint64_t fileSize) const
{
if ( fileSize < sizeof(mach_header) )
- return Error("file is too short");
+ return Error("file is too small (length=%llu)", fileSize);
if ( !hasMachOMagic() )
return Error("not a mach-o file (start is no MH_MAGIC[_64])");
@@ -308,7 +403,7 @@
if ( Error err = validSemanticsInstallName(policy) )
return err;
- if ( Error err = validSemanticsDependents(policy) )
+ if ( Error err = validSemanticsLinkedDylibs(policy) )
return err;
if ( Error err = validSemanticsRPath(policy) )
@@ -335,7 +430,7 @@
const char* str = (char*)cmd + strOffset;
const char* end = (char*)cmd + cmd->cmdsize;
- for ( const char* s = str; s < end; ++s ) {
+ for ( const char* s = end-1; s >= str; --s ) {
if ( *s == '\0' ) {
return Error::none();
}
@@ -351,10 +446,11 @@
return Error("load commands length (%llu) exceeds length of file (%llu)", headerAndLCSize, fileSize);
}
- // check for reconized filetype
+ // check for recognized filetype
switch ( mh.filetype ) {
case MH_EXECUTE:
case MH_DYLIB:
+ case MH_DYLIB_STUB:
case MH_DYLINKER:
case MH_BUNDLE:
case MH_KEXT_BUNDLE:
@@ -478,6 +574,14 @@
case LC_FILESET_ENTRY:
fileSetCmd = (fileset_entry_command*)cmd;
lcError = stringOverflow(cmd, index, fileSetCmd->entry_id.offset);
+ break;
+ case LC_FUNCTION_VARIANTS:
+ if ( cmd->cmdsize != sizeof(linkedit_data_command) )
+ lcError = Error("load command #%d LC_FUNCTION_VARIANTS size wrong", index);
+ break;
+ case LC_FUNCTION_VARIANT_FIXUPS:
+ if ( cmd->cmdsize != sizeof(linkedit_data_command) )
+ lcError = Error("load command #%d LC_FUNCTION_VARIANT_FIXUPS size wrong", index);
break;
default:
if ( cmd->cmd & LC_REQ_DYLD )
@@ -530,11 +634,12 @@
});
if ( uuidCount > 1 )
return Error("too many LC_UUID load commands");
- if ( (uuidCount == 0) && policy.enforceHasUUID() )
- return Error("missing LC_UUID load command");
-
+ if ( (uuidCount == 0) && policy.enforceHasUUID() ) {
+ return Error("missing LC_UUID load command");
+ }
return Error::none();
}
+
Error Header::validSemanticsInstallName(const Policy& policy) const
{
@@ -550,7 +655,7 @@
if ( foundCount > 1 )
return Error("multiple LC_ID_DYLIB found");
- if ( this->isDylib() ) {
+ if ( this->isDylibOrStub() ) {
if ( installName == nullptr )
return Error("MH_DYLIB is missing LC_ID_DYLIB");
#if 0 // FIXME: need path plumbed down
@@ -578,7 +683,7 @@
return Error::none();
}
-Error Header::validSemanticsDependents(const Policy& policy) const
+Error Header::validSemanticsLinkedDylibs(const Policy& policy) const
{
// gather info
__block Error dupDepError;
@@ -587,7 +692,7 @@
const char** depPaths = depPathsBuffer;
const bool enforceNoDupDylibs = policy.enforceNoDuplicateDylibs();
const bool hasWarningHandler = mach_o::hasWarningHandler();
- // don't use forEachDependentDylib, because it synthesizes libSystem.dylib
+ // don't use forEachLinkedDylib, because it synthesizes libSystem.dylib
forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
switch ( cmd->cmd ) {
case LC_LOAD_DYLIB:
@@ -600,10 +705,10 @@
for ( int i = 0; i < depCount; ++i ) {
if ( strcmp(loadPath, depPaths[i]) == 0 ) {
if ( enforceNoDupDylibs ) {
- dupDepError = Error("duplicate dependent dylib '%s'", loadPath);
+ dupDepError = Error("duplicate linked dylib '%s'", loadPath);
stop = true;
} else
- warning(this, "duplicate dependent dylib are deprecated ('%s')", loadPath);
+ warning(this, "duplicate linked dylib are deprecated ('%s')", loadPath);
}
}
depPaths[depCount] = loadPath;
@@ -618,24 +723,9 @@
// all new binaries must link with something
if ( this->isDyldManaged() && policy.enforceHasLinkedDylibs() && (depCount == 0) ) {
// except for dylibs in libSystem.dylib which are ok to link with nothing (they are on bottom)
- const char* installName = this->installName();
- bool isLibSystem = false;
- if (installName != nullptr) {
- if ( this->builtForPlatform(Platform::driverKit, true) ) {
- const char* libSystemDir = "/System/DriverKit/usr/lib/system/";
- if ( strncmp(installName, libSystemDir, strlen(libSystemDir)) != 0 )
- isLibSystem = true;
- } else if ( this->platformAndVersions().platform.isExclaveKit() ) {
- const char* libSystemDir = "/System/ExclaveKit/usr/lib/system/";
- if ( strncmp(installName, libSystemDir, strlen(libSystemDir)) != 0 )
- isLibSystem = true;
- } else {
- const char* libSystemDir = "/usr/lib/system/";
- if ( strncmp(installName, libSystemDir, strlen(libSystemDir)) != 0 )
- isLibSystem = true;
- }
- }
- if ( !isLibSystem )
+ CString installName = this->installName();
+ CString libSystemDir = platformAndVersions().platform.libSystemDir();
+ if ( !installName.starts_with(libSystemDir) )
return Error("missing LC_LOAD_DYLIB (must link with at least libSystem.dylib)");
}
return Error::none();
@@ -679,8 +769,12 @@
return Error("segment '%s' load command content extends beyond end of file", seg->segname);
// <rdar://problem/19986776> dyld should support non-allocatable __LLVM segment
- if ( !isObjectFile() ) {
- if ( (seg->filesize > seg->vmsize) && ((seg->vmsize != 0) || ((seg->flags & SG_NORELOC) == 0)) )
+ // <rdar://problem/144216621> dyld should support non-allocatable __DWARF segment (golang)
+ if ( !isObjectFile() && (seg->filesize > seg->vmsize) ) {
+ // non-mapped segments must have vmsize==0 and initprot==0
+ if ( (seg->vmsize == 0) && (seg->initprot == 0) )
+ return Error::none(); // no more checks of segment because it is not mapped
+ else
return Error("segment '%s' filesize exceeds vmsize", seg->segname);
}
@@ -688,7 +782,7 @@
if ( (seg->initprot & 0xFFFFFFF8) != 0 ) {
return Error("%s segment permissions has invalid bits set (0x%08X)", seg->segname, seg->initprot);
}
- if ( policy.enforceTextSegmentPermissions() ) {
+ if ( this->isDyldManaged() && policy.enforceTextSegmentPermissions() ) {
if ( (strcmp(seg->segname, "__TEXT") == 0) && (seg->initprot != (VM_PROT_READ | VM_PROT_EXECUTE)) )
return Error("__TEXT segment permissions is not 'r-x'");
}
@@ -696,17 +790,26 @@
if ( (strcmp(seg->segname, "__LINKEDIT") == 0) && (seg->initprot != VM_PROT_READ) )
return Error("__LINKEDIT segment permissions is not 'r--'");
}
- if ( policy.enforceDataSegmentPermissions() ) {
+ // dylib stubs don't have LC_SEGMENT_SPLIT_INFO, so they trip on SG_READ_ONLY check because
+ // the split seg info exception can't be applied
+ if ( !isDylibStub() && policy.enforceDataSegmentPermissions() ) {
if ( (strcmp(seg->segname, "__DATA") == 0) && (seg->initprot != (VM_PROT_READ | VM_PROT_WRITE)) )
return Error("__DATA segment permissions is not 'rw-'");
if ( strcmp(seg->segname, "__DATA_CONST") == 0 ) {
if ( seg->initprot != (VM_PROT_READ | VM_PROT_WRITE) )
return Error("__DATA_CONST segment permissions is not 'rw-'");
if ( (seg->flags & SG_READ_ONLY) == 0 ) {
- if ( this->isDylib() && this->hasSplitSegInfo() ) {
+ bool isSplitSegMarker = false;
+ if ( this->isDylibOrStub() && this->hasSplitSegInfo(isSplitSegMarker) && !isSplitSegMarker ) {
+ // dylibs intended for dyld cache are allowed to not have SG_READ_ONLY set
+ }
+ else if ( this->inDyldCache() ) {
// dylibs in dyld cache are allowed to not have SG_READ_ONLY set
}
- else {
+ else if ( this->isStaticExecutable() ) {
+ // static excutables don't use dyld so have no way to make DATA_CONST read-only
+ }
+ else if ( policy.enforceDataConstSegmentPermissions() ) {
return Error("__DATA_CONST segment missing SG_READ_ONLY flag");
}
}
@@ -717,28 +820,32 @@
if ( (seg->vmaddr + seg->vmsize) < seg->vmaddr )
return Error("'%s' segment vm range wraps", seg->segname);
- // check sections are within its segment
- const SC* const sectionsStart = (SC*)((char*)seg + sizeof(SG));
- const SC* const sectionsEnd = §ionsStart[seg->nsects];
- for ( const SC* sect = sectionsStart; (sect < sectionsEnd); ++sect ) {
- if ( (int64_t)(sect->size) < 0 ) {
- return Error("section '%s' size too large 0x%lX", sect->sectname, (size_t)sect->size);
- }
- else if ( sect->addr < seg->vmaddr ) {
- return Error("section '%s' start address 0x%lX is before containing segment's address 0x%0lX", sect->sectname, (size_t)sect->addr, (size_t)seg->vmaddr);
- }
- else if ( policy.enforceSectionsInSegment() && (sect->addr + sect->size > seg->vmaddr + seg->vmsize) ) {
- return Error("section '%s' end address 0x%lX is beyond containing segment's end address 0x%0lX", sect->sectname, (size_t)(sect->addr + sect->size), (size_t)(seg->vmaddr + seg->vmsize));
+ // dylib stubs have no section data
+ if ( !isDylibStub() ) {
+ // check sections are within its segment
+ const SC* const sectionsStart = (SC*)((char*)seg + sizeof(SG));
+ const SC* const sectionsEnd = §ionsStart[seg->nsects];
+ for ( const SC* sect = sectionsStart; (sect < sectionsEnd); ++sect ) {
+ if ( (int64_t)(sect->size) < 0 ) {
+ return Error("section '%s' size too large 0x%lX", sect->sectname, (size_t)sect->size);
+ }
+ else if ( sect->addr < seg->vmaddr ) {
+ return Error("section '%s' start address 0x%lX is before containing segment's address 0x%0lX", sect->sectname, (size_t)sect->addr, (size_t)seg->vmaddr);
+ }
+ else if ( policy.enforceSectionsInSegment() && (sect->addr + sect->size > seg->vmaddr + seg->vmsize) ) {
+ return Error("section '%s' end address 0x%lX is beyond containing segment's end address 0x%0lX", sect->sectname, (size_t)(sect->addr + sect->size), (size_t)(seg->vmaddr + seg->vmsize));
+ }
}
}
return Error::none();
}
-
#endif // !TARGET_OS_EXCLAVEKIT
-struct Interval
+
+struct VIS_HIDDEN Interval
{
bool overlaps(const Interval& other) const;
+ uint64_t size() const { return end - start; }
uint64_t start;
uint64_t end;
};
@@ -755,14 +862,13 @@
{
Interval vm;
Interval file;
- const char* name;
+ CString name;
};
STACK_ALLOC_OVERFLOW_SAFE_ARRAY(SegRange, ranges, 12);
__block Error lcError;
__block bool hasTEXT = false;
__block bool hasLINKEDIT = false;
__block uint64_t segmentIndexText = 0;
- __block uint64_t segmentIndexLinkedit = 0;
forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
if ( cmd->cmd == LC_SEGMENT_64 ) {
const segment_command_64* seg64 = (segment_command_64*)cmd;
@@ -772,7 +878,6 @@
}
else if ( strcmp(seg64->segname, "__LINKEDIT") == 0 ) {
hasLINKEDIT = true;
- segmentIndexLinkedit = ranges.count();
}
lcError = validSegment<segment_command_64, section_64>(policy, fileSize, seg64);
ranges.push_back({ { seg64->vmaddr, seg64->vmaddr + seg64->vmsize }, { seg64->fileoff, seg64->fileoff + seg64->filesize }, seg64->segname });
@@ -785,7 +890,6 @@
}
else if ( strcmp(seg32->segname, "__LINKEDIT") == 0 ) {
hasLINKEDIT = true;
- segmentIndexLinkedit = ranges.count();
}
lcError = validSegment<segment_command, section>(policy, fileSize, seg32);
ranges.push_back({ { seg32->vmaddr, seg32->vmaddr + seg32->vmsize }, { seg32->fileoff, seg32->fileoff + seg32->filesize }, seg32->segname });
@@ -799,50 +903,56 @@
// dynamic binaries have further restrictions
if ( isDyldManaged() ) {
if ( hasTEXT ) {
- if ( ranges[segmentIndexText].file.start != 0 )
+ if ( !this->inDyldCache() && (ranges[segmentIndexText].file.start != 0) )
return Error("__TEXT segment fileoffset is not zero");
const uint32_t headerAndLCSize = machHeaderSize() + mh.sizeofcmds;
- if ( ranges[segmentIndexText].file.end < headerAndLCSize )
- return Error("load commands do not fit in __TEXT segment");
+ if ( ranges[segmentIndexText].file.size() < headerAndLCSize )
+ return Error("load commands do not fit in __TEXT segment filesize");
+ if ( ranges[segmentIndexText].vm.size() < headerAndLCSize )
+ return Error("load commands do not fit in __TEXT segment vmsize");
}
else {
return Error("missing __TEXT segment");
}
+ (void)hasLINKEDIT;
// FIXME: LINKEDIT checks need to move to Analyzer
//if ( !hasLINKEDIT )
// return Error("missing __LINKEDIT segment");
}
// check for overlapping segments, by looking at every possible pair of segments
+ const bool checkNames = isDyldManaged() && policy.enforceUniqueSegmentNames();
for ( const SegRange& r1 : ranges ) {
for ( const SegRange& r2 : ranges ) {
if ( &r1 == &r2 )
continue;
if ( r1.vm.overlaps(r2.vm) )
- return Error("vm range of segment '%s' overlaps segment '%s'", r1.name, r2.name);
- if ( r1.file.overlaps(r2.file) )
- return Error("file range of segment '%s' overlaps segment '%s'", r1.name, r2.name);
+ return Error("vm range of segment '%s' overlaps segment '%s'", r1.name.c_str(), r2.name.c_str());
+ // can't compare file offsets for segments in dyld cache because they may be offsets into different files
+ if ( !this->inDyldCache() && r1.file.overlaps(r2.file) )
+ return Error("file range of segment '%s' overlaps segment '%s'", r1.name.c_str(), r2.name.c_str());
+ if ( checkNames && (r1.name == r2.name) )
+ return Error("duplicate segment name '%s'", r1.name.c_str());
}
}
// check segment load command order matches file content order which matches vm order
// skip dyld cache because segments are moved around too much
- if ( policy.enforceSegmentOrderMatchesLoadCmds() && !inDyldCache() ) {
- const SegRange* last = nullptr;
- for ( const SegRange& r : ranges ) {
- if ( last != nullptr ) {
- if ( (r.file.start < last->file.start) && (r.file.start != r.file.end) )
- return Error("segment '%s' file offset out of order", r.name);
- if ( r.vm.start < last->vm.start ) {
- if ( isFileSet() && (strcmp(r.name, "__PRELINK_INFO") == 0) ) {
- // __PRELINK_INFO may have no vmaddr set
- }
- else {
- return Error("segment '%s' vm address out of order", r.name);
- }
+ if ( policy.enforceSegmentOrderMatchesLoadCmds() && !inDyldCache() && (ranges.count() > 1) ) {
+ for (int i=1; i < ranges.count()-1; ++i) {
+ const SegRange& a = ranges[i-1];
+ const SegRange& b = ranges[i];
+ if ( (b.file.start < a.file.start) && (b.file.start != b.file.end) )
+ return Error("segment '%s' file offset out of order", a.name.c_str());
+ if ( b.vm.start < a.vm.start ) {
+ if ( isFileSet() && (b.name == "__PRELINK_INFO") ) {
+ // __PRELINK_INFO may have no vmaddr set
+ } else if ( arch().usesx86_64Instructions() && isDynamicExecutable() && !isPIE() ) {
+ // rdar://149897776 (allow out of VM address order segments on legacy x86 binaries)
+ } else {
+ return Error("segment '%s' vm address out of order", b.name.c_str());
}
}
- last = &r;
}
}
@@ -988,6 +1098,14 @@
return !hasLoadCommand(LC_LOAD_DYLINKER);
}
+bool Header::isDylinker() const {
+ if ( mh.filetype != MH_DYLINKER ) {
+ return false;
+ }
+ return true;
+}
+
+
//
// MARK: --- methods that read Platform load commands ---
//
@@ -1035,24 +1153,26 @@
break;
}
});
-#ifdef BUILDING_MACHO_WRITER
- // no implicit platforms in static linker
- // but for object files only, we need to support linking against old macos dylibs
- if ( isObjectFile() )
+
+ if ( foundPlatform )
return;
+
+ if ( !gHeaderAddImplicitPlatform ) {
+ // no implicit platforms in static linker
+ // but for object files and -preload files only, we need to support linking against old macos dylibs
+ if ( isObjectFile() || isPreload() || isKextBundle() || isStaticExecutable() )
+ return;
+ }
+
+ // old binary with no explicit platform
+#if TARGET_OS_OSX
+ if ( (mh.cputype == CPU_TYPE_X86_64) || (mh.cputype == CPU_TYPE_I386) )
+ handler(Platform::macOS, Version32(10, 5), Version32(10, 5)); // guess it is a macOS 10.5 binary
+ // <rdar://problem/75343399>
+ // The Go linker emits non-standard binaries without a platform and we have to live with it.
+ if ( mh.cputype == CPU_TYPE_ARM64 )
+ handler(Platform::macOS, Version32(11, 0), Version32(11, 0)); // guess it is a macOS 11.0 binary
#endif
-
- if ( !foundPlatform ) {
- // old binary with no explicit platform
-#if TARGET_OS_OSX
- if ( (mh.cputype == CPU_TYPE_X86_64) | (mh.cputype == CPU_TYPE_I386) )
- handler(Platform::macOS, Version32(10, 5), Version32(10, 5)); // guess it is a macOS 10.5 binary
- // <rdar://problem/75343399>
- // The Go linker emits non-standard binaries without a platform and we have to live with it.
- if ( mh.cputype == CPU_TYPE_ARM64 )
- handler(Platform::macOS, Version32(11, 0), Version32(11, 0)); // guess it is a macOS 11.0 binary
-#endif
- }
}
bool Header::builtForPlatform(Platform reqPlatform, bool onlyOnePlatform) const
@@ -1073,10 +1193,29 @@
return match;
}
-bool Header::isZippered() const
-{
- return platformAndVersions().platform == Platform::zippered;
-}
+bool Header::builtForSimulator() const {
+ return platformAndVersions().platform.isSimulator();
+}
+
+void Header::forEachBuildTool(void (^handler)(Platform platform, uint32_t tool, uint32_t version)) const
+{
+ forEachLoadCommandSafe(^(const load_command* cmd, bool &stop) {
+ switch ( cmd->cmd ) {
+ case LC_BUILD_VERSION: {
+ const build_version_command* buildCmd = (build_version_command *)cmd;
+ for ( uint32_t i = 0; i != buildCmd->ntools; ++i ) {
+ uint32_t offset = sizeof(build_version_command) + (i * sizeof(build_tool_version));
+ if ( offset >= cmd->cmdsize )
+ break;
+
+ const build_tool_version* firstTool = (const build_tool_version*)(&buildCmd[1]);
+ handler(Platform(buildCmd->platform), firstTool[i].tool, firstTool[i].version);
+ }
+ }
+ }
+ });
+}
+
bool Header::allowsAlternatePlatform() const
{
@@ -1104,7 +1243,7 @@
{
__block bool found = false;
forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
- if ( cmd->cmd == LC_ID_DYLIB ) {
+ if ( cmd->cmd == LC_ID_DYLIB || cmd->cmd == LC_ID_DYLINKER) {
const dylib_command* dylibCmd = (dylib_command*)cmd;
*compatVersion = Version32(dylibCmd->dylib.compatibility_version);
*currentVersion = Version32(dylibCmd->dylib.current_version);
@@ -1132,8 +1271,23 @@
return found;
}
-
-const char* Header::dependentDylibLoadPath(uint32_t depIndex) const
+bool Header::sourceVersion(Version64 &version) const
+{
+ __block bool found = false;
+ forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_SOURCE_VERSION ) {
+ const source_version_command* svc = (const source_version_command*)cmd;
+ version = Version64(svc->version);
+ found = true;
+ stop = true;
+ }
+ });
+
+ return found;
+}
+
+
+const char* Header::linkedDylibLoadPath(uint32_t depIndex) const
{
__block uint32_t curIndex = 0;
__block const char* result = nullptr;
@@ -1153,14 +1307,14 @@
return result;
}
-uint32_t Header::dependentDylibCount(bool* allDepsAreNormal) const
+uint32_t Header::linkedDylibCount(bool* allDepsAreNormal) const
{
if ( allDepsAreNormal != nullptr )
*allDepsAreNormal = true;
__block unsigned count = 0;
- this->forEachDependentDylib(^(const char* loadPath, DependentDylibAttributes kind, Version32 , Version32 , bool& stop) {
+ this->forEachLinkedDylib(^(const char* loadPath, LinkedDylibAttributes kind, Version32 , Version32 , bool , bool& stop) {
if ( allDepsAreNormal != nullptr ) {
- if ( kind != DependentDylibAttributes::regular )
+ if ( kind != LinkedDylibAttributes::regular )
*allDepsAreNormal = false; // record if any linkages were weak, re-export, upward, or delay-init
}
++count;
@@ -1168,9 +1322,9 @@
return count;
}
-DependentDylibAttributes Header::loadCommandToDylibKind(const dylib_command* dylibCmd)
-{
- DependentDylibAttributes attr;
+LinkedDylibAttributes Header::loadCommandToDylibKind(const dylib_command* dylibCmd)
+{
+ LinkedDylibAttributes attr;
const dylib_use_command* dylib2Cmd = (dylib_use_command*)dylibCmd;
if ( (dylib2Cmd->marker == 0x1a741800) && (dylib2Cmd->nameoff == sizeof(dylib_use_command)) ) {
attr.raw = (uint8_t)dylib2Cmd->flags;
@@ -1192,9 +1346,13 @@
assert(0 && "not a dylib load command");
}
-void Header::forEachDependentDylib(void (^callback)(const char* loadPath, DependentDylibAttributes kind,
- Version32 compatVersion, Version32 curVersion, bool& stop)) const
-{
+void Header::forEachLinkedDylib(void (^callback)(const char* loadPath, LinkedDylibAttributes kind,
+ Version32 compatVersion, Version32 curVersion,
+ bool synthesizedLink, bool& stop)) const
+{
+ if ( this->isDylinker() )
+ return;
+
__block unsigned count = 0;
__block bool stopped = false;
forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
@@ -1205,7 +1363,7 @@
case LC_LOAD_UPWARD_DYLIB: {
const dylib_command* dylibCmd = (dylib_command*)cmd;
const char* loadPath = (char*)dylibCmd + dylibCmd->dylib.name.offset;
- callback(loadPath, loadCommandToDylibKind(dylibCmd), Version32(dylibCmd->dylib.compatibility_version), Version32(dylibCmd->dylib.current_version), stop);
+ callback(loadPath, loadCommandToDylibKind(dylibCmd), Version32(dylibCmd->dylib.compatibility_version), Version32(dylibCmd->dylib.current_version), false, stop);
++count;
if ( stop )
stopped = true;
@@ -1213,24 +1371,23 @@
}
}
});
-#if BUILDING_DYLD
+
// everything must link with something
if ( (count == 0) && !stopped ) {
// The dylibs that make up libSystem can link with nothing
// except for dylibs in libSystem.dylib which are ok to link with nothing (they are on bottom)
if ( this->builtForPlatform(Platform::driverKit, true) ) {
if ( !this->isDylib() || (strncmp(this->installName(), "/System/DriverKit/usr/lib/system/", 33) != 0) )
- callback("/System/DriverKit/usr/lib/libSystem.B.dylib", DependentDylibAttributes::regular, Version32(1, 0), Version32(1, 0), stopped);
+ callback("/System/DriverKit/usr/lib/libSystem.B.dylib", LinkedDylibAttributes::regular, Version32(1, 0), Version32(1, 0), true, stopped);
} else if ( this->platformAndVersions().platform.isExclaveKit() ) {
if ( !this->isDylib() || (strncmp(this->installName(), "/System/ExclaveKit/usr/lib/system/", 34) != 0) )
- callback("/System/ExclaveKit/usr/lib/libSystem.dylib", DependentDylibAttributes::regular, Version32(1, 0), Version32(1, 0), stopped);
+ callback("/System/ExclaveKit/usr/lib/libSystem.dylib", LinkedDylibAttributes::regular, Version32(1, 0), Version32(1, 0), true, stopped);
}
else {
if ( !this->isDylib() || (strncmp(this->installName(), "/usr/lib/system/", 16) != 0) )
- callback("/usr/lib/libSystem.B.dylib", DependentDylibAttributes::regular, Version32(1, 0), Version32(1, 0), stopped);
- }
- }
-#endif
+ callback("/usr/lib/libSystem.B.dylib", LinkedDylibAttributes::regular, Version32(1, 0), Version32(1, 0), true, stopped);
+ }
+ }
}
void Header::forDyldEnv(void (^callback)(const char* envVar, bool& stop)) const
@@ -1242,10 +1399,8 @@
// only process variables that start with DYLD_ and end in _PATH
if ( (strncmp(keyEqualsValue, "DYLD_", 5) == 0) ) {
const char* equals = strchr(keyEqualsValue, '=');
- if ( equals != NULL ) {
- if ( strncmp(&equals[-5], "_PATH", 5) == 0 ) {
- callback(keyEqualsValue, stop);
- }
+ if ( equals != nullptr ) {
+ callback(keyEqualsValue, stop);
}
}
}
@@ -1277,8 +1432,9 @@
}
break;
case CPU_TYPE_ARM64:
+ case CPU_TYPE_ARM64_32:
if ( flavor == 6 ) { // ARM_THREAD_STATE64
- addr = regs64[32]; // arm_thread_state64_t.__pc
+ memcpy((uint32_t*)&addr, (uint32_t*)®s64[32], sizeof(uint64_t)); // arm_thread_state64_t.__pc
return true;
}
break;
@@ -1323,7 +1479,29 @@
stop = true;
}
});
- // FIXME: may need to ignore codesigs from pre 10.9 macOS binaries
+
+ // <rdar://problem/13622786> ignore code signatures in macOS binaries built with pre-10.9 tools
+ if ( mh.cputype == CPU_TYPE_X86_64 ) {
+ PlatformAndVersions pvs = platformAndVersions();
+ if ( (pvs.platform == Platform::macOS) && (pvs.sdk < Version32(10,9)) )
+ return false;
+ }
+
+ return result;
+}
+
+bool Header::hasLinkerOptimizationHints(uint32_t& fileOffset, uint32_t& size) const
+{
+ __block bool result = false;
+ forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_LINKER_OPTIMIZATION_HINT ) {
+ linkedit_data_command* sigCmd = (linkedit_data_command*)cmd;
+ fileOffset = sigCmd->dataoff;
+ size = sigCmd->datasize;
+ result = true;
+ stop = true;
+ }
+ });
return result;
}
@@ -1342,11 +1520,30 @@
return result;
}
-bool Header::hasSplitSegInfo() const
+bool Header::hasClassicRelocations(uint32_t& nLocRel, uint32_t& nExtRel) const
{
__block bool result = false;
+ nLocRel = 0;
+ nExtRel = 0;
+ forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_DYSYMTAB) {
+ dysymtab_command* dySymCmd = (dysymtab_command*)cmd;
+ nLocRel = dySymCmd->nlocrel;
+ nExtRel = dySymCmd->nextrel;
+ result = (nLocRel != 0 || nExtRel != 0);
+ stop = true;
+ }
+ });
+ return result;
+}
+
+bool Header::hasSplitSegInfo(bool& isSplitSegMarker) const
+{
+ __block bool result = false;
forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
if ( cmd->cmd == LC_SEGMENT_SPLIT_INFO) {
+ linkedit_data_command* sigCmd = (linkedit_data_command*)cmd;
+ isSplitSegMarker = (sigCmd->datasize == 0);
result = true;
stop = true;
}
@@ -1369,6 +1566,741 @@
return result;
}
+bool Header::hasFunctionsVariantTable(uint64_t& runtimeOffset) const
+{
+ __block uint32_t fileOffset = 0;
+ forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_FUNCTION_VARIANTS ) {
+ const linkedit_data_command* leDataCmd = (linkedit_data_command*)cmd;
+ fileOffset = leDataCmd->dataoff;
+ stop = true;
+ }
+ });
+ if ( fileOffset == 0 )
+ return false;
+
+ runtimeOffset = fileOffset + zerofillExpansionAmount();
+ return true;
+}
+
+
+CString Header::libOrdinalName(int libOrdinal) const
+{
+ switch ( libOrdinal) {
+ case BIND_SPECIAL_DYLIB_SELF:
+ return "<this-image>";
+ case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE:
+ return "<main-executable>";
+ case BIND_SPECIAL_DYLIB_FLAT_LOOKUP:
+ return "<flat-namespace>";
+ case BIND_SPECIAL_DYLIB_WEAK_LOOKUP:
+ return "<weak-def-coalesce>";
+ default:
+ if ( (libOrdinal > 0) && (libOrdinal <= this->linkedDylibCount()) )
+ return this->linkedDylibLoadPath(libOrdinal-1);
+ break;
+ }
+ return "<invalid-lib-ordinal>";
+}
+
+const load_command* Header::findLoadCommand(uint32_t& index, bool (^predicate)(const load_command* lc)) const
+{
+ index = 0;
+ __block const load_command* lc = nullptr;
+ __block uint32_t currentIndex = 0;
+ forEachLoadCommandSafe(^(const load_command *cmd, bool &stop) {
+ if ( predicate(cmd) ) {
+ index = currentIndex;
+ lc = cmd;
+ stop = true;
+ }
+
+ ++currentIndex;
+ });
+
+ return lc;
+}
+
+void Header::findLoadCommandRange(uint32_t& index, uint32_t& endIndex, bool (^predicate)(const load_command* lc)) const
+{
+ index = UINT32_MAX;
+ endIndex = UINT32_MAX;
+ __block uint32_t currentIndex = 0;
+ forEachLoadCommandSafe(^(const load_command *cmd, bool &stop) {
+ if ( predicate(cmd) ) {
+ if ( index == UINT32_MAX ) { // first match, set indices
+ index = currentIndex;
+ endIndex = index+1;
+ } else {
+ assert(endIndex == currentIndex); // range must be contiguous
+ ++endIndex;
+ }
+ } else if (index != UINT32_MAX) {
+ // not a match, so stop looking if the first match had already been found
+ stop = true;
+ }
+
+ ++currentIndex;
+ });
+
+ // clear index if none matched
+ if ( index == UINT32_MAX ) {
+ index = 0;
+ endIndex = 0;
+ }
+}
+
+
+const dylib_command* Header::isDylibLoadCommand(const load_command* lc)
+{
+ switch (lc->cmd) {
+ case LC_LOAD_DYLIB:
+ case LC_LOAD_WEAK_DYLIB:
+ case LC_LOAD_UPWARD_DYLIB:
+ case LC_REEXPORT_DYLIB:
+ return (dylib_command*)lc;
+ default:
+ return nullptr;
+ }
+}
+
+uint32_t Header::sizeForLinkedDylibCommand(const char *path, LinkedDylibAttributes depAttrs, uint32_t& traditionalCmd) const
+{
+ traditionalCmd = 0;
+ if ( depAttrs == LinkedDylibAttributes::regular )
+ traditionalCmd = LC_LOAD_DYLIB;
+ else if ( depAttrs == LinkedDylibAttributes::justWeakLink )
+ traditionalCmd = LC_LOAD_WEAK_DYLIB;
+ else if ( depAttrs == LinkedDylibAttributes::justUpward )
+ traditionalCmd = LC_LOAD_UPWARD_DYLIB;
+ else if ( depAttrs == LinkedDylibAttributes::justReExport )
+ traditionalCmd = LC_REEXPORT_DYLIB;
+
+ if ( traditionalCmd )
+ return pointerAligned((uint32_t)(sizeof(dylib_command) + strlen(path) + 1));
+
+ // use new style load command
+ return pointerAligned((uint32_t)(sizeof(dylib_use_command) + strlen(path) + 1));
+}
+
+
+const thread_command* Header::unixThreadLoadCommand() const {
+ __block const thread_command* command = nullptr;
+ forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_UNIXTHREAD ) {
+ command = (const thread_command*)cmd;
+ stop = true;
+ }
+ });
+ return command;
+}
+
+
+const char* Header::protectionString(uint32_t flags, char str[8])
+{
+ str[0] = (flags & VM_PROT_READ) ? 'r' : '.';
+ str[1] = (flags & VM_PROT_WRITE) ? 'w' : '.';
+ str[2] = (flags & VM_PROT_EXECUTE) ? 'x' : '.';
+ str[3] = '\0';
+ return str;
+}
+
+static const char* sectionAttributes(uint32_t sectFlags)
+{
+ if ( sectFlags == 0 )
+ return "";
+
+ switch ( sectFlags & SECTION_TYPE ) {
+ case S_ZEROFILL:
+ return "S_ZEROFILL";
+ case S_CSTRING_LITERALS:
+ return "S_CSTRING_LITERALS";
+ case S_NON_LAZY_SYMBOL_POINTERS:
+ return "S_NON_LAZY_SYMBOL_POINTERS";
+ case S_THREAD_LOCAL_VARIABLES:
+ return "S_THREAD_LOCAL_VARIABLES";
+ case S_INIT_FUNC_OFFSETS:
+ return "S_INIT_FUNC_OFFSETS";
+ case S_SYMBOL_STUBS:
+ return "S_SYMBOL_STUBS";
+ }
+
+ if ( sectFlags == S_ATTR_PURE_INSTRUCTIONS )
+ return "S_ATTR_PURE_INSTRUCTIONS";
+ if ( sectFlags == (S_ATTR_PURE_INSTRUCTIONS|S_ATTR_SOME_INSTRUCTIONS) )
+ return "S_ATTR_PURE_INSTRUCTIONS|S_ATTR_SOME_INSTRUCTIONS";
+
+ return "";
+}
+
+void Header::printLoadCommands(FILE* out, unsigned indentLevel) const
+{
+ char indentBuffer[indentLevel+2];
+ for (unsigned i=0; i < indentLevel; ++i)
+ indentBuffer[i] = ' ';
+ indentBuffer[indentLevel] = '\0';
+ const char* indent = indentBuffer;
+ __block uint32_t lcIndex = 0;
+ this->forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
+ fprintf(out, "%sLoad command #%d\n", indent, lcIndex);
+ const dylib_command* dylibCmd;
+ const segment_command* seg32Cmd;
+ const segment_command_64* seg64Cmd;
+ const linkedit_data_command* leDataCmd;
+ const symtab_command* symTabCmd;
+ const dysymtab_command* dynSymCmd;
+ const uuid_command* uuidCmd;
+ const version_min_command* versMinCmd;
+ const build_version_command* buildVerCmd;
+ const rpath_command* rpathCmd;
+ const dylinker_command* dyldEnvCmd;
+ const entry_point_command* mainCmd;
+ const sub_client_command* subClientCmd;
+ const routines_command* inits32Cmd;
+ const routines_command_64* inits64Cmd;
+ const encryption_info_command* encrypt32Cmd;
+ const encryption_info_command_64* encrypt64Cmd;
+ const dyld_info_command* dyldInfoCmd;
+ const target_triple_command* tripleCmd;
+ char versStr[64];
+ char maxProtStr[8];
+ char initProtStr[8];
+ char linkAttrStr[64];
+ uuid_string_t uuidStr;
+ const section_64* sections64;
+ const section* sections32;
+ LinkedDylibAttributes linkAttrs;
+ const uint8_t* bytes;
+ const uint32_t* words;
+ uint64_t vers64;
+ switch (cmd->cmd) {
+ case LC_SEGMENT:
+ seg32Cmd = (segment_command*)cmd;
+ fprintf(out, "%s cmd: LC_SEGMENT\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s segname: \"%.*s\"\n", indent, (int)name16(seg32Cmd->segname).size(), name16(seg32Cmd->segname).data());
+ fprintf(out, "%s vmaddr: 0x%08X\n", indent, seg32Cmd->vmaddr);
+ fprintf(out, "%s vmsize: 0x%08X\n", indent, seg32Cmd->vmsize);
+ fprintf(out, "%s fileoff: 0x%08X\n", indent, seg32Cmd->fileoff);
+ fprintf(out, "%s filesize: 0x%08X\n", indent, seg32Cmd->filesize);
+ fprintf(out, "%s maxprot: %s\n", indent, Header::protectionString(seg32Cmd->maxprot, maxProtStr));
+ fprintf(out, "%s initprot: %s\n", indent, Header::protectionString(seg32Cmd->initprot, initProtStr));
+ if ( seg32Cmd->flags & SG_READ_ONLY )
+ fprintf(out, "%s flags: SG_READ_ONLY\n",indent);
+ fprintf(out, "%s nsects: %d\n", indent, seg32Cmd->nsects);
+ sections32 = (section*)((char*)seg32Cmd + sizeof(struct segment_command));
+ for (int i=0; i < seg32Cmd->nsects; ++i) {
+ fprintf(out, "%s sections[%d].sectname: \"%.*s\"\n", indent, i, (int)name16(sections32[i].sectname).size(), name16(sections32[i].sectname).data());
+ fprintf(out, "%s sections[%d].addr: 0x%08X\n", indent, i, sections32[i].addr);
+ fprintf(out, "%s sections[%d].size: 0x%08X\n", indent, i, sections32[i].size);
+ if ( sections32[i].offset != 0 )
+ fprintf(out, "%s sections[%d].offset: 0x%08X\n", indent, i, sections32[i].offset);
+ fprintf(out, "%s sections[%d].align: 2^%d\n", indent, i, sections32[i].align);
+ if ( sections32[i].flags != 0 )
+ fprintf(out, "%s sections[%d].type: %s\n", indent, i, sectionAttributes(sections32[i].flags));
+ if ( sections32[i].reserved1 != 0 )
+ fprintf(out, "%s sections[%d].reserved1: %d\n", indent, i, sections32[i].reserved1);
+ if ( sections32[i].reserved2 != 0 )
+ fprintf(out, "%s sections[%d].reserved2: %d\n", indent, i, sections32[i].reserved2);
+ }
+ break;
+ case LC_SYMTAB:
+ symTabCmd = (symtab_command*)cmd;
+ fprintf(out, "%s cmd: LC_SYMTAB\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s symoff: 0x%X\n", indent, symTabCmd->symoff);
+ fprintf(out, "%s nsyms: %d\n", indent, symTabCmd->nsyms);
+ fprintf(out, "%s stroff: 0x%X\n", indent, symTabCmd->stroff);
+ fprintf(out, "%s strsize: 0x%X\n", indent, symTabCmd->strsize);
+ break;
+ case LC_UNIXTHREAD:
+ words = (uint32_t*)cmd;
+ fprintf(out, "%s cmd: LC_UNIXTHREAD\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ if ( words[2] == 6 ) { // ARM_THREAD_STATE64
+ fprintf(out, "%s pc: 0x%llX\n", indent, *(uint64_t*)(&words[68]));
+ }
+ else if ( words[2] == 4 ) { // x86_THREAD_STATE64
+ fprintf(out, "%s pc: 0x%llX\n", indent, *(uint64_t*)(&words[36]));
+ }
+ else if ( words[2] == 1 ) { // x86_THREAD_STATE
+ fprintf(out, "%s pc: 0x%X\n", indent, words[14]);
+ }
+ break;
+ case LC_DYSYMTAB:
+ dynSymCmd = (dysymtab_command*)cmd;
+ fprintf(out, "%s cmd: LC_DYSYMTAB\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s ilocalsym: %d\n", indent, dynSymCmd->ilocalsym);
+ fprintf(out, "%s nlocalsym: %d\n", indent, dynSymCmd->nlocalsym);
+ fprintf(out, "%s iextdefsym: %d\n", indent, dynSymCmd->iextdefsym);
+ fprintf(out, "%s nextdefsym: %d\n", indent, dynSymCmd->nextdefsym);
+ fprintf(out, "%s iundefsym: %d\n", indent, dynSymCmd->iundefsym);
+ fprintf(out, "%s nundefsym: %d\n", indent, dynSymCmd->nundefsym);
+ fprintf(out, "%s indirectsymoff: 0x%08X\n", indent, dynSymCmd->indirectsymoff);
+ fprintf(out, "%s nindirectsyms: %d\n", indent, dynSymCmd->nindirectsyms);
+ fprintf(out, "%s ilocalsym: %d\n", indent, dynSymCmd->ilocalsym);
+ break;
+ case LC_LOAD_DYLIB:
+ dylibCmd = (dylib_command*)cmd;
+ linkAttrs = loadCommandToDylibKind(dylibCmd);
+ fprintf(out, "%s cmd: LC_LOAD_DYLIB\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s name: \"%s\"\n", indent, (char*)dylibCmd + dylibCmd->dylib.name.offset);
+ if ( linkAttrs.raw != 0 ) {
+ linkAttrs.toString(linkAttrStr);
+ fprintf(out, "%s attrs: %s\n", indent, linkAttrStr);
+ }
+ fprintf(out, "%s cur-vers: %s\n", indent, Version32(dylibCmd->dylib.current_version).toString(versStr));
+ fprintf(out, "%s compat-vers: %s\n", indent, Version32(dylibCmd->dylib.compatibility_version).toString(versStr));
+ break;
+ case LC_ID_DYLIB:
+ dylibCmd = (dylib_command*)cmd;
+ fprintf(out, "%s cmd: LC_ID_DYLIB\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s name: \"%s\"\n", indent, (char*)dylibCmd + dylibCmd->dylib.name.offset);
+ fprintf(out, "%s cur-vers: %s\n", indent, Version32(dylibCmd->dylib.current_version).toString(versStr));
+ fprintf(out, "%s compat-vers: %s\n", indent, Version32(dylibCmd->dylib.compatibility_version).toString(versStr));
+ break;
+ case LC_LOAD_DYLINKER:
+ dylibCmd = (dylib_command*)cmd;
+ fprintf(out, "%s cmd: LC_LOAD_DYLINKER\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s name: \"%s\"\n", indent, (char*)dylibCmd + dylibCmd->dylib.name.offset);
+ break;
+ case LC_ID_DYLINKER:
+ dylibCmd = (dylib_command*)cmd;
+ fprintf(out, "%s cmd: LC_ID_DYLINKER\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s name: \"%s\"\n", indent, (char*)dylibCmd + dylibCmd->dylib.name.offset);
+ break;
+ case LC_ROUTINES:
+ inits32Cmd = (routines_command*)cmd;
+ fprintf(out, "%s cmd: LC_ROUTINES\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s init_address: 0x%X\n", indent, inits32Cmd->init_address);
+ break;
+ case LC_SUB_FRAMEWORK:
+ subClientCmd = (sub_client_command*)cmd;
+ fprintf(out, "%s cmd: LC_SUB_FRAMEWORK\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s umbrella: \"%s\"\n", indent, (char*)subClientCmd + subClientCmd->client.offset);
+ break;
+ case LC_SUB_UMBRELLA:
+ subClientCmd = (sub_client_command*)cmd;
+ fprintf(out, "%s cmd: LC_SUB_UMBRELLA\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s sub_umbrella: \"%s\"\n", indent, (char*)subClientCmd + subClientCmd->client.offset);
+ break;
+ case LC_SUB_CLIENT:
+ subClientCmd = (sub_client_command*)cmd;
+ fprintf(out, "%s cmd: LC_SUB_CLIENT\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s client: \"%s\"\n", indent, (char*)subClientCmd + subClientCmd->client.offset);
+ break;
+ case LC_SUB_LIBRARY:
+ subClientCmd = (sub_client_command*)cmd;
+ fprintf(out, "%s cmd: LC_SUB_LIBRARY\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s sub_library: \"%s\"\n", indent, (char*)subClientCmd + subClientCmd->client.offset);
+ break;
+ case LC_LOAD_WEAK_DYLIB:
+ dylibCmd = (dylib_command*)cmd;
+ fprintf(out, "%s cmd: LC_LOAD_WEAK_DYLIB\n",indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s name: \"%s\"\n", indent, (char*)dylibCmd + dylibCmd->dylib.name.offset);
+ fprintf(out, "%s cur-vers: %s\n", indent, Version32(dylibCmd->dylib.current_version).toString(versStr));
+ fprintf(out, "%s compat-vers: %s\n", indent, Version32(dylibCmd->dylib.compatibility_version).toString(versStr));
+ break;
+ case LC_SEGMENT_64:
+ seg64Cmd = (segment_command_64*)cmd;
+ fprintf(out, "%s cmd: LC_SEGMENT_64\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s segname: \"%.*s\"\n", indent, (int)name16(seg64Cmd->segname).size(), name16(seg64Cmd->segname).data());
+ fprintf(out, "%s vmaddr: 0x%016llX\n", indent, seg64Cmd->vmaddr);
+ fprintf(out, "%s vmsize: 0x%016llX\n", indent, seg64Cmd->vmsize);
+ fprintf(out, "%s fileoff: 0x%08llX\n", indent, seg64Cmd->fileoff);
+ fprintf(out, "%s filesize: 0x%08llX\n", indent, seg64Cmd->filesize);
+ fprintf(out, "%s maxprot: %s\n", indent, Header::protectionString(seg64Cmd->initprot, maxProtStr));
+ fprintf(out, "%s initprot: %s\n", indent, Header::protectionString(seg64Cmd->initprot, initProtStr));
+ if ( seg64Cmd->flags & SG_READ_ONLY )
+ fprintf(out, "%s flags: SG_READ_ONLY\n",indent);
+ fprintf(out, "%s nsects: %d\n", indent, seg64Cmd->nsects);
+ sections64 = (section_64*)((char*)seg64Cmd + sizeof(struct segment_command_64));
+ for (int i=0; i < seg64Cmd->nsects; ++i) {
+ fprintf(out, "%s sections[%d].sectname: \"%.*s\"\n", indent, i, (int)name16(sections64[i].sectname).size(), name16(sections64[i].sectname).data());
+ fprintf(out, "%s sections[%d].addr: 0x%016llX\n", indent, i, sections64[i].addr);
+ fprintf(out, "%s sections[%d].size: 0x%016llX\n", indent, i, sections64[i].size);
+ if ( sections64[i].offset != 0 )
+ fprintf(out, "%s sections[%d].offset: 0x%08X\n", indent, i, sections64[i].offset);
+ fprintf(out, "%s sections[%d].align: 2^%d\n", indent, i, sections64[i].align);
+ if ( sections64[i].flags != 0 )
+ fprintf(out, "%s sections[%d].type: %s\n", indent, i, sectionAttributes(sections64[i].flags));
+ if ( sections64[i].reserved1 != 0 )
+ fprintf(out, "%s sections[%d].reserved1: %d\n", indent, i, sections64[i].reserved1);
+ if ( sections64[i].reserved2 != 0 )
+ fprintf(out, "%s sections[%d].reserved2: %d\n", indent, i, sections64[i].reserved2);
+ }
+ break;
+ case LC_ROUTINES_64:
+ inits64Cmd = (routines_command_64*)cmd;
+ fprintf(out, "%s cmd: LC_ROUTINES\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s init_address: 0x%llX\n", indent, inits64Cmd->init_address);
+ break;
+ case LC_UUID:
+ uuidCmd = (uuid_command*)cmd;
+ uuid_unparse_upper(uuidCmd->uuid, uuidStr);
+ fprintf(out, "%s cmd: LC_UUID\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s uuid: %s\n", indent, uuidStr);
+ break;
+ case LC_RPATH:
+ rpathCmd = (rpath_command*)cmd;
+ fprintf(out, "%s cmd: LC_RPATH\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s rpath:\"%s\"\n", indent, (char*)cmd + rpathCmd->path.offset);
+ break;
+ case LC_CODE_SIGNATURE:
+ leDataCmd = (linkedit_data_command*)cmd;
+ fprintf(out, "%s cmd: LC_CODE_SIGNATURE\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s dataoff: 0x%X\n", indent, leDataCmd->dataoff);
+ fprintf(out, "%s datasize: 0x%X\n", indent, leDataCmd->datasize);
+ break;
+ case LC_SEGMENT_SPLIT_INFO:
+ leDataCmd = (linkedit_data_command*)cmd;
+ fprintf(out, "%s cmd: LC_SEGMENT_SPLIT_INFO\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s dataoff: 0x%X\n", indent, leDataCmd->dataoff);
+ fprintf(out, "%s datasize: 0x%X\n", indent, leDataCmd->datasize);
+ break;
+ case LC_REEXPORT_DYLIB:
+ dylibCmd = (dylib_command*)cmd;
+ fprintf(out, "%s cmd: LC_REEXPORT_DYLIB\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s name: \"%s\"\n", indent, (char*)dylibCmd + dylibCmd->dylib.name.offset);
+ fprintf(out, "%s cur-vers: %s\n", indent, Version32(dylibCmd->dylib.current_version).toString(versStr));
+ fprintf(out, "%s compat-vers: %s\n", indent, Version32(dylibCmd->dylib.compatibility_version).toString(versStr));
+ break;
+ case LC_ENCRYPTION_INFO:
+ encrypt32Cmd = (encryption_info_command*)cmd;
+ fprintf(out, "%s cmd: LC_ENCRYPTION_INFO\n",indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s cryptoff: 0x%X\n", indent, encrypt32Cmd->cryptoff);
+ fprintf(out, "%s cryptsize: 0x%X\n", indent, encrypt32Cmd->cryptsize);
+ fprintf(out, "%s cryptid: 0x%X\n", indent, encrypt32Cmd->cryptid);
+ break;
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ dyldInfoCmd = (dyld_info_command*)cmd;
+ fprintf(out, "%s cmd: LC_DYLD_INFO%s\n", indent, (cmd->cmd == LC_DYLD_INFO_ONLY ? "_ONLY" : ""));
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s rebase_off: 0x%X\n", indent, dyldInfoCmd->rebase_off);
+ fprintf(out, "%s rebase_size: 0x%X\n", indent, dyldInfoCmd->rebase_size);
+ fprintf(out, "%s bind_off: 0x%X\n", indent, dyldInfoCmd->bind_off);
+ fprintf(out, "%s bind_size: 0x%X\n", indent, dyldInfoCmd->bind_size);
+ fprintf(out, "%s weak_bind_off: 0x%X\n", indent, dyldInfoCmd->weak_bind_off);
+ fprintf(out, "%s weak_bind_size: 0x%X\n", indent, dyldInfoCmd->weak_bind_size);
+ fprintf(out, "%s lazy_bind_off: 0x%X\n", indent, dyldInfoCmd->lazy_bind_off);
+ fprintf(out, "%s lazy_bind_size: 0x%X\n", indent, dyldInfoCmd->lazy_bind_size);
+ fprintf(out, "%s export_off: 0x%X\n", indent, dyldInfoCmd->export_off);
+ fprintf(out, "%s export_size: 0x%X\n", indent, dyldInfoCmd->export_size);
+ break;
+ case LC_LOAD_UPWARD_DYLIB:
+ dylibCmd = (dylib_command*)cmd;
+ fprintf(out, "%s cmd: LC_LOAD_UPWARD_DYLIB\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s name: \"%s\"\n", indent, (char*)dylibCmd + dylibCmd->dylib.name.offset);
+ fprintf(out, "%s cur-vers: %s\n", indent, Version32(dylibCmd->dylib.current_version).toString(versStr));
+ fprintf(out, "%s compat-vers: %s\n", indent, Version32(dylibCmd->dylib.compatibility_version).toString(versStr));
+ break;
+ case LC_VERSION_MIN_MACOSX:
+ versMinCmd = (version_min_command*)cmd;
+ fprintf(out, "%s cmd: LC_VERSION_MIN_MACOSX\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s min-OS: %s\n", indent, Version32(versMinCmd->version).toString(versStr));
+ fprintf(out, "%s sdk: %s\n", indent, Version32(versMinCmd->sdk).toString(versStr));
+ break;
+ case LC_VERSION_MIN_IPHONEOS:
+ versMinCmd = (version_min_command*)cmd;
+ fprintf(out, "%s cmd: LC_VERSION_MIN_IPHONEOS\n",indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s min-OS: %s\n", indent, Version32(versMinCmd->version).toString(versStr));
+ fprintf(out, "%s sdk: %s\n", indent, Version32(versMinCmd->sdk).toString(versStr));
+ break;
+ case LC_FUNCTION_STARTS:
+ leDataCmd = (linkedit_data_command*)cmd;
+ fprintf(out, "%s cmd: LC_FUNCTION_STARTS\n",indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s dataoff: 0x%X\n", indent, leDataCmd->dataoff);
+ fprintf(out, "%s datasize: 0x%X\n", indent, leDataCmd->datasize);
+ break;
+ case LC_DYLD_ENVIRONMENT:
+ dyldEnvCmd = (dylinker_command*)cmd;
+ fprintf(out, "%s cmd: LC_DYLD_ENVIRONMENT\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s name:\"%s\"\n", indent, (char*)dyldEnvCmd + dyldEnvCmd->name.offset);
+ break;
+ case LC_MAIN:
+ mainCmd = (entry_point_command*)cmd;
+ fprintf(out, "%s cmd: LC_MAIN\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s entryoff: 0x%llX\n", indent, mainCmd->entryoff);
+ if ( mainCmd->stacksize != 0 )
+ fprintf(out, "%s stacksize: 0x%llX\n", indent, mainCmd->stacksize);
+ break;
+ case LC_DATA_IN_CODE:
+ leDataCmd = (linkedit_data_command*)cmd;
+ fprintf(out, "%s cmd: LC_DATA_IN_CODE\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s dataoff: 0x%X\n", indent, leDataCmd->dataoff);
+ fprintf(out, "%s datasize: 0x%X\n", indent, leDataCmd->datasize);
+ break;
+ case LC_SOURCE_VERSION:
+ memcpy(&vers64, ((uint8_t*)cmd)+8, 8); // in 32-bit arches load command may not be 8-byte aligned, so can't use source_version_command.version.
+ fprintf(out, "%s cmd: LC_SOURCE_VERSION\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s version: %s\n", indent, Version64(vers64).toString(versStr));
+ break;
+ case LC_ENCRYPTION_INFO_64:
+ encrypt64Cmd = (encryption_info_command_64*)cmd;
+ fprintf(out, "%s cmd: LC_ENCRYPTION_INFO\n",indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s cryptoff: 0x%X\n", indent, encrypt64Cmd->cryptoff);
+ fprintf(out, "%s cryptsize: 0x%X\n", indent, encrypt64Cmd->cryptsize);
+ fprintf(out, "%s cryptid: 0x%X\n", indent, encrypt64Cmd->cryptid);
+ break;
+ case LC_VERSION_MIN_TVOS:
+ versMinCmd = (version_min_command*)cmd;
+ fprintf(out, "%s cmd: LC_VERSION_MIN_TVOS\n",indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s min-OS: %s\n", indent, Version32(versMinCmd->version).toString(versStr));
+ fprintf(out, "%s sdk: %s\n", indent, Version32(versMinCmd->sdk).toString(versStr));
+ break;
+ case LC_VERSION_MIN_WATCHOS:
+ versMinCmd = (version_min_command*)cmd;
+ fprintf(out, "%s cmd: LC_VERSION_MIN_WATCHOS\n",indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s min-OS: %s\n", indent, Version32(versMinCmd->version).toString(versStr));
+ fprintf(out, "%s sdk: %s\n", indent, Version32(versMinCmd->sdk).toString(versStr));
+ break;
+ case LC_BUILD_VERSION:
+ buildVerCmd = (build_version_command*)cmd;
+ fprintf(out, "%s cmd: LC_BUILD_VERSION\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s platform: %s\n", indent, Platform(buildVerCmd->platform).name().c_str());
+ fprintf(out, "%s min-OS: %s\n", indent, Version32(buildVerCmd->minos).toString(versStr));
+ fprintf(out, "%s sdk: %s\n", indent, Version32(buildVerCmd->sdk).toString(versStr));
+ if ( buildVerCmd->ntools != 0 ) {
+ const build_tool_version* toolVersions = (build_tool_version*)((uint8_t*)cmd + sizeof(build_version_command));
+ for (uint32_t i=0; i < buildVerCmd->ntools; ++i ) {
+ const char* toolName = "unknown";
+ switch ( toolVersions[i].tool ) {
+ case TOOL_CLANG:
+ toolName = "clang";
+ break;
+ case TOOL_SWIFT:
+ toolName = "swiftc";
+ break;
+ case TOOL_LD:
+ toolName = "ld";
+ break;
+ case TOOL_LLD:
+ toolName = "lld";
+ break;
+ }
+ fprintf(out, "%s tool[%d].name: %s\n", indent, i, toolName);
+ fprintf(out, "%s tool[%d].version: %s\n", indent, i, Version32(toolVersions[i].version).toString(versStr));
+ }
+ }
+ break;
+ case LC_DYLD_EXPORTS_TRIE:
+ leDataCmd = (linkedit_data_command*)cmd;
+ fprintf(out, "%s cmd: LC_DYLD_EXPORTS_TRIE\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s dataoff: 0x%X\n", indent, leDataCmd->dataoff);
+ fprintf(out, "%s datasize: 0x%X\n", indent, leDataCmd->datasize);
+ break;
+ case LC_DYLD_CHAINED_FIXUPS:
+ leDataCmd = (linkedit_data_command*)cmd;
+ fprintf(out, "%s cmd: LC_DYLD_CHAINED_FIXUPS\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s dataoff: 0x%X\n", indent, leDataCmd->dataoff);
+ fprintf(out, "%s datasize: 0x%X\n", indent, leDataCmd->datasize);
+ break;
+ case LC_FUNCTION_VARIANTS:
+ leDataCmd = (linkedit_data_command*)cmd;
+ fprintf(out, "%s cmd: LC_FUNCTION_VARIANTS\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s dataoff: 0x%X\n", indent, leDataCmd->dataoff);
+ fprintf(out, "%s datasize: 0x%X\n", indent, leDataCmd->datasize);
+ break;
+ case LC_FUNCTION_VARIANT_FIXUPS:
+ leDataCmd = (linkedit_data_command*)cmd;
+ fprintf(out, "%s cmd: LC_FUNCTION_VARIANT_FIXUPS\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s dataoff: 0x%X\n", indent, leDataCmd->dataoff);
+ fprintf(out, "%s datasize: 0x%X\n", indent, leDataCmd->datasize);
+ break;
+ case LC_TARGET_TRIPLE:
+ tripleCmd = (target_triple_command*)cmd;
+ fprintf(out, "%s cmd: LC_TARGET_TRIPLE\n", indent);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s triple:\"%s\"\n", indent, (char*)cmd + tripleCmd->triple.offset);
+ break;
+ default:
+ bytes = (uint8_t*)cmd + 8;
+ fprintf(out, "%s cmd: 0x%X\n", indent, cmd->cmd);
+ fprintf(out, "%s cmdsize: 0x%X\n", indent, cmd->cmdsize);
+ fprintf(out, "%s bytes: ", indent);
+ for (int i=0; i < cmd->cmdsize; ++i) {
+ if ( ((i & 0xF) == 0) && (i != 0) )
+ fprintf(out, "%s ", indent);
+ fprintf(out, "%02X ", bytes[i]);
+ if ( (i & 0xF) == 0xF )
+ fprintf(out, "\n");
+ }
+ if ( (cmd->cmdsize & 0xF) != 0x0 )
+ fprintf(out, "\n");
+ break;
+ }
+ ++lcIndex;
+ });
+
+
+}
+
+bool Header::loadableIntoProcess(Platform processPlatform, CString path, bool internalInstall) const
+{
+ if ( this->builtForPlatform(processPlatform) )
+ return true;
+
+ // Some host macOS dylibs can be loaded into simulator processes
+ if (processPlatform.isSimulator() && this->builtForPlatform(Platform::macOS)) {
+ static constinit CString const macOSHost[] = {
+ "/usr/lib/system/libsystem_kernel.dylib",
+ "/usr/lib/system/libsystem_platform.dylib",
+ "/usr/lib/system/libsystem_pthread.dylib",
+ "/usr/lib/system/libsystem_platform_debug.dylib",
+ "/usr/lib/system/libsystem_pthread_debug.dylib",
+ "/usr/lib/system/host/liblaunch_sim.dylib",
+ };
+ if ( std::ranges::any_of(macOSHost, [&](const CString& libPath) { return libPath == path; }) ) {
+ return true;
+ }
+ }
+
+ // If this is being called on main executable where we expect a macOS program, Catalyst programs are also runnable
+ if ( this->isMainExecutable() && (processPlatform == Platform::macOS) && this->builtForPlatform(Platform::macCatalyst, true) )
+ return true;
+#if (TARGET_OS_OSX && TARGET_CPU_ARM64)
+ if ( this->isMainExecutable() && (processPlatform == Platform::macOS) && this->builtForPlatform(Platform::iOS, true) )
+ return true;
+#endif
+
+ // allow iOS executables to use visionOS dylibs
+ if ( (processPlatform == Platform::iOS) && this->builtForPlatform(Platform::visionOS, true) )
+ return true;
+
+ // allow iOS_Sim executables to use visionOS_Sim dylibs
+ if ( (processPlatform == Platform::iOS_simulator) && this->builtForPlatform(Platform::visionOS_simulator, true) )
+ return true;
+
+ bool iOSonMac = (processPlatform == Platform::macCatalyst);
+#if (TARGET_OS_OSX && TARGET_CPU_ARM64)
+ // allow iOS binaries in iOSApp
+ if ( processPlatform == Platform::iOS ) {
+ // can load Catalyst binaries into iOS process
+ if ( this->builtForPlatform(Platform::macCatalyst) )
+ return true;
+ iOSonMac = true;
+ }
+#endif
+ // macOS dylibs can be loaded into iOSMac processes
+ if ( iOSonMac && this->builtForPlatform(Platform::macOS, true) )
+ return true;
+
+ return false;
+}
+
+bool Header::hasPlusLoadMethod() const
+{
+ __block bool result = false;
+ // in new objc runtime compiler puts classes/categories with +load method in specical section
+ forEachSection(^(const Header::SectionInfo& info, bool& stop) {
+ if ( !info.segmentName.starts_with("__DATA") )
+ return;
+ if ( (info.sectionName == "__objc_nlclslist") || (info.sectionName == "__objc_nlcatlist") ) {
+ result = true;
+ stop = true;
+ }
+ });
+ return result;
+}
+
+// returns empty string if no triple specified
+CString Header::targetTriple() const
+{
+ __block CString result;
+ forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_TARGET_TRIPLE ) {
+ const char* targetTriple = (char*)cmd + ((struct target_triple_command*)cmd)->triple.offset;
+ result = targetTriple;
+ }
+ });
+ return result;
+}
+
+void Header::forEachSingletonPatch(void (^handler)(uint64_t runtimeOffset)) const
+{
+ uint32_t ptrSize = pointerSize();
+ uint32_t elementSize = (2 * ptrSize);
+ uint64_t loadAddress = preferredLoadAddress();
+ forEachSection(^(const SectionInfo §Info, bool &stop) {
+ if ( sectInfo.sectionName != "__const_cfobj2" )
+ return;
+ stop = true;
+
+ if ( (sectInfo.size % elementSize) != 0 ) {
+ return;
+ }
+
+ if ( sectInfo.reserved2 != elementSize ) {
+ // The linker must have rejected one or more of the elements in the section, so
+ // didn't set the reserved2 to let us patch
+ return;
+ }
+
+ for ( uint64_t offset = 0; offset != sectInfo.size; offset += elementSize ) {
+ uint64_t targetRuntimeOffset = (sectInfo.address + offset) - loadAddress;
+ handler(targetRuntimeOffset);
+ }
+ });
+}
+
+bool Header::hasObjCMessageReferences() const {
+ uint64_t sectionRuntimeOffset, sectionSize;
+ return findObjCDataSection("__objc_msgrefs", sectionRuntimeOffset, sectionSize);
+}
+
+bool Header::findObjCDataSection(CString sectionName, uint64_t& sectionRuntimeOffset, uint64_t& sectionSize) const
+{
+ uint64_t baseAddress = preferredLoadAddress();
+
+ __block bool foundSection = false;
+ forEachSection(^(const Header::SectionInfo& sectInfo, bool& stop) {
+ if ( !sectInfo.segmentName.starts_with("__DATA") )
+ return;
+ if ( sectInfo.sectionName != sectionName )
+ return;
+ foundSection = true;
+ sectionRuntimeOffset = sectInfo.address - baseAddress;
+ sectionSize = sectInfo.size;
+ stop = true;
+ });
+ return foundSection;
+}
uint32_t Header::segmentCount() const
{
@@ -1384,23 +2316,32 @@
return count;
}
+uint32_t Header::loadCommandsFreeSpace() const
+{
+ __block uint32_t firstSectionFileOffset = UINT32_MAX;
+ __block uint32_t firstSegmentFileOffset = UINT32_MAX;
+ forEachSection(^(const SectionInfo& sectInfo, bool &stop) {
+ firstSectionFileOffset = sectInfo.fileOffset;
+ firstSegmentFileOffset = segmentFileOffset(sectInfo.segIndex);
+ stop = true;
+ });
+
+ // no segment load commands yet, so explicit size limit
+ if ( firstSegmentFileOffset == UINT32_MAX )
+ return UINT32_MAX;
+
+ uint32_t headerSize = (mh.magic == MH_MAGIC_64) ? sizeof(mach_header_64) : sizeof(mach_header);
+ uint32_t existSpaceUsed = mh.sizeofcmds + headerSize;
+ return firstSectionFileOffset - firstSegmentFileOffset - existSpaceUsed;
+}
+
uint64_t Header::preferredLoadAddress() const
{
__block uint64_t textVmAddr = 0;
- forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
- if ( cmd->cmd == LC_SEGMENT_64 ) {
- const segment_command_64* segCmd = (segment_command_64*)cmd;
- if ( strcmp(segCmd->segname, "__TEXT") == 0 ) {
- textVmAddr = segCmd->vmaddr;
- stop = true;
- }
- }
- else if ( cmd->cmd == LC_SEGMENT ) {
- const segment_command* segCmd = (segment_command*)cmd;
- if ( strcmp(segCmd->segname, "__TEXT") == 0 ) {
- textVmAddr = segCmd->vmaddr;
- stop = true;
- }
+ forEachSegment(^(const SegmentInfo &info, bool &stop) {
+ if ( info.segmentName == "__TEXT" ) {
+ textVmAddr = info.vmaddr;
+ stop = true;
}
});
return textVmAddr;
@@ -1429,10 +2370,25 @@
return result;
}
+bool Header::hasDiscontiguousSegments() const
+{
+ uint64_t loadAddr = preferredLoadAddress();
+ __block bool res = false;
+ this->forEachSegment(^(const SegmentInfo& info, bool& stop) {
+ if ( info.maxProt == 0 )
+ return;
+ if ( info.vmaddr < loadAddr ) {
+ res = true;
+ stop = true;
+ }
+ });
+ return res;
+}
+
std::string_view Header::segmentName(uint32_t segIndex) const
{
- __block std::string_view result;
- __block uint32_t segCount = 0;
+ __block std::string_view result;
+ __block uint32_t segCount = 0;
this->forEachSegment(^(const SegmentInfo& info, bool& stop) {
if ( segIndex == segCount ) {
result = info.segmentName;
@@ -1443,65 +2399,236 @@
return result;
}
-// LC_SEGMENT stores names as char[16] potentially without a null terminator. This returns a string_view for the given name
-static std::string_view name16(const char name[16])
+uint64_t Header::segmentVmSize(uint32_t segIndex) const
+{
+ __block uint64_t result = 0;
+ __block uint32_t segCount = 0;
+ this->forEachSegment(^(const SegmentInfo& info, bool& stop) {
+ if ( segIndex == segCount ) {
+ result = info.vmsize;
+ stop = true;
+ }
+ ++segCount;
+ });
+ return result;
+}
+
+uint64_t Header::segmentVmAddr(uint32_t segIndex) const
+{
+ __block uint64_t result = 0;
+ __block uint32_t segCount = 0;
+ this->forEachSegment(^(const SegmentInfo& info, bool& stop) {
+ if ( segIndex == segCount ) {
+ result = info.vmaddr;
+ stop = true;
+ }
+ ++segCount;
+ });
+ return result;
+}
+
+uint32_t Header::segmentFileOffset(uint32_t segIndex) const
+{
+ __block uint32_t result = 0;
+ __block uint32_t segCount = 0;
+ this->forEachSegment(^(const SegmentInfo& info, bool& stop) {
+ if ( segIndex == segCount ) {
+ result = info.fileOffset;
+ stop = true;
+ }
+ ++segCount;
+ });
+ return result;
+}
+
+// LC_SEGMENT stores names as char[16] potentially without a null terminator.
+std::string_view Header::name16(const char name[16])
{
size_t length = strnlen(name, 16);
- return std::string_view(name, length);
+ if ( length < 16 )
+ return std::string_view(name, length);
+
+ return std::string_view(name, 16);
}
void Header::forEachSegment(void (^callback)(const SegmentInfo& infos, bool& stop)) const
{
+ __block uint16_t segmentIndex = 0;
forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
if ( cmd->cmd == LC_SEGMENT_64 ) {
const segment_command_64* segCmd = (segment_command_64*)cmd;
- SegmentInfo segInfo { .segmentName=name16(segCmd->segname), .vmaddr=segCmd->vmaddr, .vmsize=segCmd->vmsize,
- .fileOffset=(uint32_t)segCmd->fileoff, .fileSize=(uint32_t)segCmd->filesize, .flags=segCmd->flags, .perms=(uint8_t)segCmd->initprot };
+ SegmentInfo segInfo {
+ .segmentName=name16(segCmd->segname), .vmaddr=segCmd->vmaddr, .vmsize=segCmd->vmsize,
+ .fileOffset=(uint32_t)segCmd->fileoff, .fileSize=(uint32_t)segCmd->filesize, .flags=segCmd->flags,
+ .segmentIndex=segmentIndex, .maxProt=(uint8_t)segCmd->maxprot, .initProt=(uint8_t)segCmd->initprot,
+ };
callback(segInfo, stop);
+ ++segmentIndex;
}
else if ( cmd->cmd == LC_SEGMENT ) {
const segment_command* segCmd = (segment_command*)cmd;
- SegmentInfo segInfo { .segmentName=name16(segCmd->segname), .vmaddr=segCmd->vmaddr, .vmsize=segCmd->vmsize,
- .fileOffset=segCmd->fileoff, .fileSize=segCmd->filesize, .flags=segCmd->flags, .perms=(uint8_t)segCmd->initprot };
+ SegmentInfo segInfo {
+ .segmentName=name16(segCmd->segname), .vmaddr=segCmd->vmaddr, .vmsize=segCmd->vmsize,
+ .fileOffset=segCmd->fileoff, .fileSize=segCmd->filesize, .flags=segCmd->flags,
+ .segmentIndex=segmentIndex, .maxProt=(uint8_t)segCmd->maxprot, .initProt=(uint8_t)segCmd->initprot,
+ };
callback(segInfo, stop);
+ ++segmentIndex;
+ }
+ });
+}
+
+void Header::forEachSegment(void (^callback)(const SegmentInfo& infos, uint64_t sizeOfSections, uint32_t maxAlignOfSections, bool& stop)) const
+{
+ __block uint16_t segmentIndex = 0;
+ forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_SEGMENT_64 ) {
+ const segment_command_64* segCmd = (segment_command_64*)cmd;
+ uint64_t sizeOfSections = segCmd->vmsize;
+ uint32_t maxAlignOfSections = 0;
+ const section_64* const sectionsStart = (section_64*)((char*)segCmd + sizeof(struct segment_command_64));
+ const section_64* const sectionsEnd = §ionsStart[segCmd->nsects];
+ for (const section_64* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+ sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
+ if ( sect->align > maxAlignOfSections )
+ maxAlignOfSections = sect->align;
+ }
+ SegmentInfo segInfo {
+ .segmentName=name16(segCmd->segname), .vmaddr=segCmd->vmaddr, .vmsize=segCmd->vmsize,
+ .fileOffset=(uint32_t)segCmd->fileoff, .fileSize=(uint32_t)segCmd->filesize, .flags=segCmd->flags,
+ .segmentIndex=segmentIndex, .maxProt=(uint8_t)segCmd->maxprot, .initProt=(uint8_t)segCmd->initprot,
+ };
+ callback(segInfo, sizeOfSections, maxAlignOfSections, stop);
+ ++segmentIndex;
+ }
+ else if ( cmd->cmd == LC_SEGMENT ) {
+ const segment_command* segCmd = (segment_command*)cmd;
+ uint64_t sizeOfSections = segCmd->vmsize;
+ uint32_t maxAlignOfSections = 0;
+ const section* const sectionsStart = (section*)((char*)segCmd + sizeof(struct segment_command));
+ const section* const sectionsEnd = §ionsStart[segCmd->nsects];
+ for (const section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+ sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
+ if ( sect->align > maxAlignOfSections )
+ maxAlignOfSections = sect->align;
+ }
+ SegmentInfo segInfo {
+ .segmentName=name16(segCmd->segname), .vmaddr=segCmd->vmaddr, .vmsize=segCmd->vmsize,
+ .fileOffset=segCmd->fileoff, .fileSize=segCmd->filesize, .flags=segCmd->flags,
+ .segmentIndex=segmentIndex, .maxProt=(uint8_t)segCmd->maxprot, .initProt=(uint8_t)segCmd->initprot,
+ };
+ callback(segInfo, sizeOfSections, maxAlignOfSections, stop);
+ ++segmentIndex;
}
});
}
void Header::forEachSection(void (^callback)(const SectionInfo&, bool& stop)) const
{
- __block uint64_t prefLoadAddr = 0;
+ __block uint16_t segmentIndex = 0;
forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
if ( cmd->cmd == LC_SEGMENT_64 ) {
const segment_command_64* segCmd = (segment_command_64*)cmd;
- if ( strcmp(segCmd->segname, "__TEXT") == 0 )
- prefLoadAddr = segCmd->vmaddr;
const section_64* const sectionsStart = (section_64*)((char*)segCmd + sizeof(struct segment_command_64));
const section_64* const sectionsEnd = §ionsStart[segCmd->nsects];
-
+ std::string_view segName = name16(segCmd->segname);
for ( const section_64* sect = sectionsStart; !stop && (sect < sectionsEnd); ++sect ) {
- std::string_view sectName = name16(sect->sectname);
- std::string_view segName = name16(sect->segname);
- SectionInfo info = { segName, sectName, (uint32_t)segCmd->initprot, sect->flags, sect->align, sect->addr - prefLoadAddr, sect->size, sect->offset,
- sect->reloff, sect->nreloc, sect->reserved1, sect->reserved2};
+ std::string_view segNameFromSectLC = name16(sect->segname);
+ SectionInfo info = {
+ // Note: use of segCmd->segname is for bin compat for copy protection in rdar://146096183
+ .sectionName=name16(sect->sectname), .segmentName=segNameFromSectLC.empty() ? segName : segNameFromSectLC, .segIndex=segmentIndex,
+ .segMaxProt=(uint32_t)segCmd->maxprot, .segInitProt=(uint32_t)segCmd->initprot,
+ .flags=sect->flags, .alignment=sect->align, .address=sect->addr, .size=sect->size, .fileOffset=sect->offset,
+ .relocsOffset=sect->reloff, .relocsCount=sect->nreloc, .reserved1=sect->reserved1, .reserved2=sect->reserved2
+ };
callback(info, stop);
}
+ ++segmentIndex;
}
else if ( cmd->cmd == LC_SEGMENT ) {
const segment_command* segCmd = (segment_command*)cmd;
- if ( strcmp(segCmd->segname, "__TEXT") == 0 )
- prefLoadAddr = segCmd->vmaddr;
const section* const sectionsStart = (section*)((char*)segCmd + sizeof(struct segment_command));
const section* const sectionsEnd = §ionsStart[segCmd->nsects];
for ( const section* sect = sectionsStart; !stop && (sect < sectionsEnd); ++sect ) {
- std::string_view sectName = name16(sect->sectname);
- std::string_view segName = name16(sect->segname);
- SectionInfo info = { segName, sectName, (uint32_t)segCmd->initprot, sect->flags, sect->align, sect->addr - prefLoadAddr, sect->size, sect->offset,
- sect->reloff, sect->nreloc, sect->reserved1, sect->reserved2};
+ SectionInfo info = {
+ .sectionName=name16(sect->sectname), .segmentName=name16(sect->segname), .segIndex=segmentIndex,
+ .segMaxProt=(uint32_t)segCmd->maxprot, .segInitProt=(uint32_t)segCmd->initprot,
+ .flags=sect->flags, .alignment=sect->align, .address=sect->addr, .size=sect->size, .fileOffset=sect->offset,
+ .relocsOffset=sect->reloff, .relocsCount=sect->nreloc, .reserved1=sect->reserved1, .reserved2=sect->reserved2
+ };
callback(info, stop);
}
- }
- });
+ ++segmentIndex;
+ }
+ });
+}
+
+void Header::forEachSection(void (^callback)(const SegmentInfo&, const SectionInfo&, bool& stop)) const
+{
+ __block uint16_t segmentIndex = 0;
+ forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_SEGMENT_64 ) {
+ const segment_command_64* segCmd = (segment_command_64*)cmd;
+ const section_64* const sectionsStart = (section_64*)((char*)segCmd + sizeof(struct segment_command_64));
+ const section_64* const sectionsEnd = §ionsStart[segCmd->nsects];
+ SegmentInfo segInfo {
+ .segmentName=name16(segCmd->segname), .vmaddr=segCmd->vmaddr, .vmsize=segCmd->vmsize,
+ .fileOffset=(uint32_t)segCmd->fileoff, .fileSize=(uint32_t)segCmd->filesize, .flags=segCmd->flags,
+ .segmentIndex=segmentIndex, .maxProt=(uint8_t)segCmd->maxprot, .initProt=(uint8_t)segCmd->initprot,
+ };
+ for ( const section_64* sect = sectionsStart; !stop && (sect < sectionsEnd); ++sect ) {
+ SectionInfo info = {
+ .sectionName=name16(sect->sectname), .segmentName=name16(sect->segname), .segIndex=segmentIndex,
+ .segMaxProt=(uint32_t)segCmd->maxprot, .segInitProt=(uint32_t)segCmd->initprot,
+ .flags=sect->flags, .alignment=sect->align, .address=sect->addr, .size=sect->size, .fileOffset=sect->offset,
+ .relocsOffset=sect->reloff, .relocsCount=sect->nreloc, .reserved1=sect->reserved1, .reserved2=sect->reserved2
+ };
+ callback(segInfo, info, stop);
+ }
+ ++segmentIndex;
+ }
+ else if ( cmd->cmd == LC_SEGMENT ) {
+ const segment_command* segCmd = (segment_command*)cmd;
+ const section* const sectionsStart = (section*)((char*)segCmd + sizeof(struct segment_command));
+ const section* const sectionsEnd = §ionsStart[segCmd->nsects];
+ SegmentInfo segInfo {
+ .segmentName=name16(segCmd->segname), .vmaddr=segCmd->vmaddr, .vmsize=segCmd->vmsize,
+ .fileOffset=segCmd->fileoff, .fileSize=segCmd->filesize, .flags=segCmd->flags,
+ .segmentIndex=segmentIndex, .maxProt=(uint8_t)segCmd->maxprot, .initProt=(uint8_t)segCmd->initprot,
+ };
+ for ( const section* sect = sectionsStart; !stop && (sect < sectionsEnd); ++sect ) {
+ SectionInfo info = {
+ .sectionName=name16(sect->sectname), .segmentName=name16(sect->segname), .segIndex=segmentIndex,
+ .segMaxProt=(uint32_t)segCmd->maxprot, .segInitProt=(uint32_t)segCmd->initprot,
+ .flags=sect->flags, .alignment=sect->align, .address=sect->addr, .size=sect->size, .fileOffset=sect->offset,
+ .relocsOffset=sect->reloff, .relocsCount=sect->nreloc, .reserved1=sect->reserved1, .reserved2=sect->reserved2
+ };
+ callback(segInfo, info, stop);
+ }
+ ++segmentIndex;
+ }
+ });
+}
+
+std::span<const uint8_t> Header::findSectionContent(CString segName, CString sectName, bool useVmOffset) const
+{
+ __block std::span<const uint8_t> result;
+ this->forEachSection(^(const SectionInfo& info, bool& stop) {
+ if ( (info.segmentName == segName) && (info.sectionName == sectName) ) {
+ const uint8_t* sectionContent = nullptr;
+ if ( useVmOffset ) {
+ // dyld loaded image, find section based on vmaddr
+ sectionContent = (uint8_t*)this + (info.address - this->preferredLoadAddress());
+ }
+ else {
+ // file mapped image, use file offsets to get content
+ sectionContent = (uint8_t*)this + info.fileOffset;
+ }
+ stop = true;
+ result = std::span<const uint8_t>(sectionContent, (size_t)info.size);
+ }
+ });
+ return result;
}
// add any LINKEDIT content file-offset in load commands to this to get content
@@ -1526,13 +2653,11 @@
// need to find LINKEDIT and TEXT to compute difference of file offsets vs vm offsets
__block uint64_t result = 0;
__block uint64_t textVmAddr = 0;
- __block uint64_t textFileOffset = 0;
forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
if ( cmd->cmd == LC_SEGMENT_64 ) {
const segment_command_64* segCmd = (segment_command_64*)cmd;
if ( strcmp(segCmd->segname, "__TEXT") == 0 ) {
textVmAddr = segCmd->vmaddr;
- textFileOffset = segCmd->fileoff;
}
else if ( strcmp(segCmd->segname, "__LINKEDIT") == 0 ) {
uint64_t vmOffsetToLinkedit = segCmd->vmaddr - textVmAddr;
@@ -1545,11 +2670,10 @@
const segment_command* segCmd = (segment_command*)cmd;
if ( strcmp(segCmd->segname, "__TEXT") == 0 ) {
textVmAddr = segCmd->vmaddr;
- textFileOffset = segCmd->fileoff;
}
else if ( strcmp(segCmd->segname, "__LINKEDIT") == 0 ) {
uint64_t vmOffsetToLinkedit = segCmd->vmaddr - textVmAddr;
- uint64_t fileOffsetToLinkedit = segCmd->fileoff - textFileOffset;
+ uint64_t fileOffsetToLinkedit = segCmd->fileoff;
result = vmOffsetToLinkedit - fileOffsetToLinkedit;
stop = true;
}
@@ -1571,28 +2695,44 @@
return result;
}
-bool Header::isRestricted() const
+// checks if a section exists.
+// use `segNamePrefix` to just match segment name prefix (e.g. "__DATA" matches "__DATA_CONST")
+bool Header::hasSection(CString segName, CString sectName, bool segNamePrefix) const
{
__block bool result = false;
this->forEachSection(^(const SectionInfo& info, bool& stop) {
- if ( (info.segmentName == "__RESTRICT") && (info.sectionName == "__restrict") ) {
- result = true;
- stop = true;
+ if ( info.sectionName == sectName ) {
+ if ( (segNamePrefix && info.segmentName.starts_with(segName)) || (info.segmentName == segName)) {
+ result = true;
+ stop = true;
+ }
}
});
return result;
}
+bool Header::isRestricted() const
+{
+ return this->hasSection("__RESTRICT", "__restrict");
+}
+
bool Header::hasInterposingTuples() const
{
__block bool hasInterposing = false;
+ this->forEachInterposingSection(^(const SectionInfo& info, bool& stop) {
+ hasInterposing = true;
+ stop = true;
+ });
+ return hasInterposing;
+}
+
+void Header::forEachInterposingSection(void (^callback)(const SectionInfo&, bool& stop)) const
+{
this->forEachSection(^(const SectionInfo& info, bool& stop) {
if ( ((info.flags & SECTION_TYPE) == S_INTERPOSING) || ((info.sectionName == "__interpose") && (info.segmentName.starts_with("__DATA") || info.segmentName.starts_with("__AUTH"))) ) {
- hasInterposing = true;
- stop = true;
- }
- });
- return hasInterposing;
+ callback(info, stop);
+ }
+ });
}
bool Header::hasObjC() const
@@ -1627,11 +2767,6 @@
return hasEncryptionInfo(cryptId, textOffset, size) && cryptId == 1;
}
-bool Header::canBeFairPlayEncrypted() const
-{
- return (findFairPlayEncryptionLoadCommand() != nullptr);
-}
-
const encryption_info_command* Header::findFairPlayEncryptionLoadCommand() const
{
__block const encryption_info_command* result = nullptr;
@@ -1673,6 +2808,13 @@
}
});
}
+
+bool Header::hasFunctionVariantFixups() const
+{
+ return hasLoadCommand(LC_FUNCTION_VARIANT_FIXUPS);
+}
+
+
void Header::forEachLinkerOption(void (^callback)(const char* opt, bool& stop)) const
{
@@ -1710,10 +2852,44 @@
return result;
}
+uint32_t Header::threadLoadCommandsSize(const Architecture& arch)
+{
+ uint32_t cmdSize = sizeof(thread_command);
+ if ( arch.sameCpu(Architecture::arm64) )
+ cmdSize = Header::pointerAligned(true, 16 + 34 * 8); // base size + ARM_EXCEPTION_STATE64_COUNT * 8
+ else if ( arch.sameCpu(Architecture::arm64_32) )
+ cmdSize = Header::pointerAligned(false, 16 + 34 * 8); // base size + ARM_EXCEPTION_STATE64_COUNT * 8
+ else if ( arch.sameCpu(Architecture::x86_64) )
+ cmdSize = Header::pointerAligned(true, 16 + 42 * 4); // base size + x86_THREAD_STATE64_COUNT * 4
+ else if ( arch.usesArmOrThumbInstructions() )
+ cmdSize = Header::pointerAligned(false, 16 + 17 * 4); // base size + ARM_THREAD_STATE_COUNT * 4
+ else
+ assert(0 && "unsupported arch for thread load command");
+ return cmdSize;
+}
+
+uint32_t Header::threadLoadCommandsSize() const
+{
+ return Header::threadLoadCommandsSize(arch());
+}
uint32_t Header::headerAndLoadCommandsSize() const
{
return machHeaderSize() + mh.sizeofcmds;
+}
+
+uint32_t Header::pointerAligned(bool is64, uint32_t value)
+{
+ // mach-o requires all load command sizes to be a multiple the pointer size
+ if ( is64 )
+ return ((value + 7) & (-8));
+ else
+ return ((value + 3) & (-4));
+}
+
+uint32_t Header::pointerAligned(uint32_t value) const
+{
+ return Header::pointerAligned(is64(), value);
}
uint32_t Header::fileSize() const
@@ -1728,7 +2904,9 @@
stop = true;
}
});
- return size;
+ if ( size != 0 )
+ return size;
+ return headerAndLoadCommandsSize();
}
// compute file size from LINKEDIT fileoffset + filesize
@@ -1751,864 +2929,132 @@
return size;
}
-//
-// MARK: --- methods that create and modify ---
-//
-
-#if BUILDING_MACHO_WRITER
-
-Header* Header::make(std::span<uint8_t> buffer, uint32_t filetype, uint32_t flags, Architecture arch, bool addImplicitTextSegment)
-{
- const size_t minHeaderAlignment = filetype == MH_OBJECT ? 8 : getpagesize();
- assert(((uint64_t)buffer.data() & (minHeaderAlignment - 1)) == 0);
- assert(buffer.size() >= sizeof(mach_header_64));
- bzero(buffer.data(), buffer.size());
- Header& header = *(Header*)buffer.data();
- mach_header& mh = header.mh;
- if ( arch.isBigEndian() ) {
- mh.magic = arch.is64() ? MH_CIGAM_64 : MH_CIGAM;
- mh.filetype = OSSwapBigToHostInt32(filetype);
- mh.ncmds = 0;
- mh.sizeofcmds = OSSwapBigToHostInt32(MH_NOUNDEFS | MH_DYLDLINK | MH_TWOLEVEL);
- mh.flags = OSSwapBigToHostInt32(flags);
- arch.set(mh);
- return &header; // can only construct mach_header for big-endian
- }
- else {
- mh.magic = arch.is64() ? MH_MAGIC_64 : MH_MAGIC;
- mh.filetype = filetype;
- mh.ncmds = 0;
- mh.sizeofcmds = 0;
- mh.flags = flags;
- arch.set(mh);
- }
- if ( addImplicitTextSegment && (filetype != MH_OBJECT) ) {
- SegmentInfo segInfo { .segmentName="__TEXT", .vmaddr=0, .vmsize=0x1000, .fileOffset=0, .fileSize=0x1000, .perms=(VM_PROT_READ | VM_PROT_EXECUTE) };
- header.addSegment(segInfo, std::array { "__text" });
- }
-
- return &header;
-}
-
-void Header::save(char savedPath[PATH_MAX]) const
-{
- ::strcpy(savedPath, "/tmp/mocko-XXXXXX");
- int fd = ::mkstemp(savedPath);
- if ( fd != -1 ) {
- ::pwrite(fd, this, sizeof(Header), 0);
- ::close(fd);
- }
-}
-
-uint32_t Header::pointerAligned(uint32_t value) const
-{
- // mach-o requires all load command sizes to be a multiple the pointer size
- if ( is64() )
- return ((value + 7) & (-8));
- else
- return ((value + 3) & (-4));
-}
-
-load_command* Header::firstLoadCommand()
-{
- if ( mh.magic == MH_MAGIC )
- return (load_command*)((uint8_t*)this + sizeof(mach_header));
- else
- return (load_command*)((uint8_t*)this + sizeof(mach_header_64));
-}
-
-// creates space for a new load command, but does not fill in its payload
-load_command* Header::appendLoadCommand(uint32_t cmd, uint32_t cmdSize)
-{
- load_command* thisCmd = (load_command*)((uint8_t*)firstLoadCommand() + mh.sizeofcmds);
- thisCmd->cmd = cmd;
- thisCmd->cmdsize = cmdSize;
- mh.ncmds += 1;
- mh.sizeofcmds += cmdSize;
-
- return thisCmd;
-}
-
-// copies a new load command from another
-void Header::appendLoadCommand(const load_command* lc)
-{
- load_command* thisCmd = (load_command*)((uint8_t*)firstLoadCommand() + mh.sizeofcmds);
- ::memcpy(thisCmd, lc, lc->cmdsize);
- mh.ncmds += 1;
- mh.sizeofcmds += lc->cmdsize;
-}
-
-void Header::addBuildVersion(Platform platform, Version32 minOS, Version32 sdk, std::span<const build_tool_version> tools)
-{
- assert(platform != Platform::zippered && "can't add a build command for Platform::zippered, it must be split");
- uint32_t lcSize = (uint32_t)(sizeof(build_version_command) + tools.size() * sizeof(build_tool_version));
- build_version_command* bv = (build_version_command*)appendLoadCommand(LC_BUILD_VERSION, lcSize);
- bv->platform = platform.value();
- bv->minos = minOS.value();
- bv->sdk = sdk.value();
- bv->ntools = (uint32_t)tools.size();
- if ( bv->ntools != 0 )
- memcpy((uint8_t*)bv + sizeof(build_version_command), &tools[0], tools.size() * sizeof(build_tool_version));
-}
-
-void Header::addMinVersion(Platform platform, Version32 minOS, Version32 sdk)
-{
- version_min_command vc;
- vc.cmdsize = sizeof(version_min_command);
- vc.version = minOS.value();
- vc.sdk = sdk.value();
- if ( platform == Platform::macOS )
- vc.cmd = LC_VERSION_MIN_MACOSX;
- else if ( (platform == Platform::iOS) || (platform == Platform::iOS_simulator) )
- vc.cmd = LC_VERSION_MIN_IPHONEOS;
- else if ( (platform == Platform::watchOS) || (platform == Platform::watchOS_simulator) )
- vc.cmd = LC_VERSION_MIN_WATCHOS;
- else if ( (platform == Platform::tvOS) || (platform == Platform::tvOS_simulator) )
- vc.cmd = LC_VERSION_MIN_TVOS;
- else
- assert(0 && "unknown platform");
- appendLoadCommand((load_command*)&vc);
-}
-
-void Header::setHasThreadLocalVariables()
-{
- assert(mh.filetype != MH_OBJECT);
- mh.flags |= MH_HAS_TLV_DESCRIPTORS;
-}
-
-void Header::setHasWeakDefs()
-{
- assert(mh.filetype != MH_OBJECT);
- mh.flags |= MH_WEAK_DEFINES;
-}
-
-void Header::setUsesWeakDefs()
-{
- assert(mh.filetype != MH_OBJECT);
- mh.flags |= MH_BINDS_TO_WEAK;
-}
-
-void Header::setAppExtensionSafe()
-{
- assert(mh.filetype == MH_DYLIB);
- mh.flags |= MH_APP_EXTENSION_SAFE;
-}
-
-void Header::setSimSupport()
-{
- assert(mh.filetype == MH_DYLIB);
- mh.flags |= MH_SIM_SUPPORT;
-}
-
-void Header::setNoReExportedDylibs()
-{
- assert(mh.filetype == MH_DYLIB);
- mh.flags |= MH_NO_REEXPORTED_DYLIBS;
-}
-
-void Header::addPlatformInfo(Platform platform, Version32 minOS, Version32 sdk, std::span<const build_tool_version> tools)
-{
- Architecture arch(&mh);
- Policy policy(arch, { platform, minOS, sdk }, mh.filetype);
- switch ( policy.useBuildVersionLoadCommand() ) {
- case Policy::preferUse:
- case Policy::mustUse:
- // three macOS dylibs under libSystem need to be built with old load commands to support old simulator runtimes
- if ( isSimSupport() && (platform == Platform::macOS) && ((arch == Architecture::x86_64) || (arch == Architecture::i386)) )
- addMinVersion(platform, minOS, sdk);
- else
- addBuildVersion(platform, minOS, sdk, tools);
+struct OldThreadsStartSection
+{
+ uint32_t padding : 31,
+ stride8 : 1;
+ uint32_t chain_starts[1];
+};
+
+// ld64 can't sometimes determine the size of __thread_starts accurately,
+// because these sections have to be given a size before everything is laid out,
+// and you don't know the actual size of the chains until everything is
+// laid out. In order to account for this, the linker puts trailing 0xFFFFFFFF at
+// the end of the section, that must be ignored when walking the chains. This
+// patch adjust the section size accordingly.
+static uint32_t adjustStartsCount(uint32_t startsCount, const uint32_t* starts) {
+ for ( int i = startsCount; i > 0; --i )
+ {
+ if ( starts[i - 1] == 0xFFFFFFFF )
+ startsCount--;
+ else
break;
- case Policy::preferDontUse:
- case Policy::mustNotUse:
- addMinVersion(platform, minOS, sdk);
- break;
- }
-}
-
-void Header::addNullUUID()
-{
- uuid_command uc;
- uc.cmd = LC_UUID;
- uc.cmdsize = sizeof(uuid_command);
- bzero(uc.uuid, 16);
- appendLoadCommand((load_command*)&uc);
-}
-
-void Header::addUniqueUUID(uuid_t copyOfUUID)
-{
- uuid_command uc;
- uc.cmd = LC_UUID;
- uc.cmdsize = sizeof(uuid_command);
- uuid_generate_random(uc.uuid);
- appendLoadCommand((load_command*)&uc);
- if ( copyOfUUID )
- memcpy(copyOfUUID, uc.uuid, sizeof(uuid_t));
-}
-
-void Header::updateUUID(uuid_t uuid)
-{
- __block bool found = false;
- forEachLoadCommandSafe(^(const load_command *cmd, bool &stop) {
- if ( cmd->cmd == LC_UUID ) {
- memcpy(((uuid_command*)cmd)->uuid, uuid, 16);
- found = true;
- stop = true;
- }
- });
- assert(found && "updateUUID called without a LC_UUID command");
-}
-
-void Header::addSegment(const SegmentInfo& info, std::span<const char* const> sectionNames)
-{
- if ( is64() ) {
- uint32_t lcSize = (uint32_t)(sizeof(segment_command_64) + sectionNames.size() * sizeof(section_64));
- segment_command_64* sc = (segment_command_64*)appendLoadCommand(LC_SEGMENT_64, lcSize);
- strncpy(sc->segname, info.segmentName.data(), 16);
- sc->vmaddr = info.vmaddr;
- sc->vmsize = info.vmsize;
- sc->fileoff = info.fileOffset;
- sc->filesize = info.fileSize;
- sc->initprot = info.perms;
- sc->maxprot = info.perms;
- sc->nsects = (uint32_t)sectionNames.size();
- sc->flags = info.flags;
- section_64* const sect = (section_64*)((uint8_t*)sc + sizeof(struct segment_command_64));
- uint32_t sectionIndex = 0;
- for ( const char* sectName : sectionNames ) {
- strncpy(sect[sectionIndex].segname, info.segmentName.data(), 16);
- strncpy(sect[sectionIndex].sectname, sectName, 16);
- ++sectionIndex;
- }
- }
- else {
- uint32_t lcSize = (uint32_t)(sizeof(segment_command) + sectionNames.size() * sizeof(section));
- segment_command* sc = (segment_command*)appendLoadCommand(LC_SEGMENT, lcSize);
- strncpy(sc->segname, info.segmentName.data(), 16);
- sc->vmaddr = (uint32_t)info.vmaddr;
- sc->vmsize = (uint32_t)info.vmsize;
- sc->fileoff = info.fileOffset;
- sc->filesize = info.fileSize;
- sc->initprot = info.perms;
- sc->maxprot = info.perms;
- sc->nsects = (uint32_t)sectionNames.size();
- sc->flags = info.flags;
- section* const sect = (section*)((uint8_t*)sc + sizeof(struct segment_command));
- uint32_t sectionIndex = 0;
- for ( const char* sectName : sectionNames ) {
- strncpy(sect[sectionIndex].segname, info.segmentName.data(), 16);
- strncpy(sect[sectionIndex].sectname, sectName, 16);
- ++sectionIndex;
- }
- }
-}
-
-void Header::updateSection(const SectionInfo& info)
-{
- forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
- if ( cmd->cmd == LC_SEGMENT_64 ) {
- segment_command_64* segCmd = (segment_command_64*)cmd;
- if (info.segmentName == segCmd->segname) {
- section_64* const sectionsStart = (section_64*)((char*)segCmd + sizeof(struct segment_command_64));
- section_64* const sectionsEnd = §ionsStart[segCmd->nsects];
- for ( section_64* sect=sectionsStart; sect < sectionsEnd; ++sect ) {
- if ( strncmp(info.sectionName.data(), sect->sectname, 16) == 0 ) {
- sect->addr = info.address;
- sect->size = info.size;
- sect->offset = info.fileOffset;
- sect->align = info.alignment;
- sect->reloff = info.relocsOffset;
- sect->nreloc = info.relocsCount;
- sect->flags = info.flags;
- sect->reserved1 = info.reserved1;
- sect->reserved2 = info.reserved2;
- sect->reserved3 = 0;
- stop = true;
- return;
+ }
+ return startsCount;
+}
+
+bool Header::hasFirmwareChainStarts(uint16_t* pointerFormat, uint32_t* startsCount, const uint32_t** starts) const
+{
+ if ( !this->isPreload() && !this->isStaticExecutable() )
+ return false;
+
+ __block bool result = false;
+ this->forEachSection(^(const SectionInfo& info, bool& stop) {
+ if ( (info.sectionName == "__chain_starts") && (info.segmentName == "__TEXT") ) {
+ const dyld_chained_starts_offsets* startsSect = (dyld_chained_starts_offsets*)((uint8_t*)this + info.fileOffset);
+ if ( pointerFormat != nullptr )
+ *pointerFormat = startsSect->pointer_format;
+ if ( startsCount != nullptr )
+ *startsCount = (info.size >= 4) ? startsSect->starts_count : 0;
+ if ( starts != nullptr )
+ *starts = &startsSect->chain_starts[0];
+ result = true;
+ stop = true;
+ }
+ else if ( (info.sectionName == "__thread_starts") && (info.segmentName == "__TEXT") ) {
+ const OldThreadsStartSection* sect = (OldThreadsStartSection*)((uint8_t*)this + info.fileOffset);
+ if ( pointerFormat != nullptr )
+ *pointerFormat = sect->stride8 ? DYLD_CHAINED_PTR_ARM64E : DYLD_CHAINED_PTR_ARM64E_FIRMWARE;
+ if ( startsCount != nullptr )
+ *startsCount = adjustStartsCount((uint32_t)(info.size/4) - 1, sect->chain_starts);
+ if ( starts != nullptr )
+ *starts = sect->chain_starts;
+ result = true;
+ stop = true;
+ }
+ });
+ return result;
+}
+
+bool Header::hasFirmwareRebaseRuns() const
+{
+ return forEachFirmwareRebaseRuns(nullptr);
+}
+
+// content of __TEXT,__rebase_info section
+struct RebaseInfo
+{
+ uint32_t startAddress;
+ uint8_t runs[]; // value of even indexes is how many pointers in a row are rebases, value of odd indexes times 4 is memory to skip over
+ // two zero values in a row signals the end of the run
+};
+
+
+bool Header::forEachFirmwareRebaseRuns(void (^handler)(uint32_t offset, bool& stop)) const
+{
+ if ( !this->isPreload() && !this->isStaticExecutable() )
+ return false;
+
+ __block const RebaseInfo* rr = nullptr;
+ __block const RebaseInfo* end = nullptr;
+ this->forEachSection(^(const SectionInfo& info, bool& sectStop) {
+ if ( (info.sectionName == "__rebase_info") && (info.segmentName == "__TEXT") ) {
+ sectStop = true;
+ rr = (RebaseInfo*)((uint8_t*)this + info.fileOffset);
+ end = (RebaseInfo*)((uint8_t*)rr + info.size);
+ }
+ });
+ if ( rr == nullptr )
+ return false;
+
+ if ( handler != nullptr ) {
+ bool stop = false;
+ while ( (rr < end) && !stop ) {
+ uint32_t address = rr->startAddress;
+ int index = 0;
+ bool done = false;
+ while ( !done && !stop ) {
+ uint8_t count = rr->runs[index];
+ if ( count == 0 ) {
+ // two 0x00 in a row mean the run is complete
+ if ( rr->runs[index+1] == 0 ) {
+ ++index;
+ done = true;
}
}
- }
- }
- else if ( cmd->cmd == LC_SEGMENT ) {
- segment_command* segCmd = (segment_command*)cmd;
- if (info.segmentName == segCmd->segname) {
- section* const sectionsStart = (section*)((char*)segCmd + sizeof(struct segment_command));
- section* const sectionsEnd = §ionsStart[segCmd->nsects];
- for ( section* sect=sectionsStart; sect < sectionsEnd; ++sect ) {
- if ( strncmp(info.sectionName.data(), sect->sectname, 16) == 0 ) {
- sect->addr = (uint32_t)info.address;
- sect->size = (uint32_t)info.size;
- sect->offset = info.fileOffset;
- sect->align = info.alignment;
- sect->reloff = info.relocsOffset;
- sect->nreloc = info.relocsCount;
- sect->flags = info.flags;
- sect->reserved1 = info.reserved1;
- sect->reserved2 = info.reserved2;
- stop = true;
- return;
+ else {
+ if ( index & 1 ) {
+ // odd runs index => how much to jump forward
+ address += ((count-1) * 4);
+ }
+ else {
+ // even runs index => how many pointers in a row that need rebasing
+ for (int i=0; i < count; ++i) {
+ handler(address, stop);
+ address += 4;
+ }
}
}
- }
- }
- });
-}
-
-void Header::updateSegment(const SegmentInfo& info)
-{
- forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
- if ( cmd->cmd == LC_SEGMENT_64 ) {
- segment_command_64* segCmd = (segment_command_64*)cmd;
- if (info.segmentName == segCmd->segname) {
- segCmd->vmaddr = info.vmaddr;
- segCmd->vmsize = info.vmsize;
- segCmd->fileoff = info.fileOffset;
- segCmd->filesize = info.fileSize;
- segCmd->initprot = info.perms;
- segCmd->maxprot = info.perms;
- stop = true;
- return;
- }
- }
- else if ( cmd->cmd == LC_SEGMENT ) {
- segment_command* segCmd = (segment_command*)cmd;
- if (info.segmentName == segCmd->segname) {
- segCmd->vmaddr = (uint32_t)info.vmaddr;
- segCmd->vmsize = (uint32_t)info.vmsize;
- segCmd->fileoff = info.fileOffset;
- segCmd->filesize = info.fileSize;
- segCmd->initprot = info.perms;
- segCmd->maxprot = info.perms;
- stop = true;
- return;
- }
- }
- });
-}
-
-
-void Header::addInstallName(const char* name, Version32 compatVers, Version32 currentVersion)
-{
- uint32_t alignedSize = pointerAligned((uint32_t)(sizeof(dylib_command) + strlen(name) + 1));
- dylib_command* ic = (dylib_command*)appendLoadCommand(LC_ID_DYLIB, alignedSize);
- ic->dylib.name.offset = sizeof(dylib_command);
- ic->dylib.current_version = currentVersion.value();
- ic->dylib.compatibility_version = compatVers.value();
- strcpy((char*)ic + ic->dylib.name.offset, name);
-}
-
-void Header::addDependentDylib(const char* path, DependentDylibAttributes depAttrs, Version32 compatVers, Version32 currentVersion)
-{
- uint32_t cmd = 0;
- if ( depAttrs == DependentDylibAttributes::regular )
- cmd = LC_LOAD_DYLIB;
- else if ( depAttrs == DependentDylibAttributes::justWeakLink )
- cmd = LC_LOAD_WEAK_DYLIB;
- else if ( depAttrs == DependentDylibAttributes::justUpward )
- cmd = LC_LOAD_UPWARD_DYLIB;
- else if ( depAttrs == DependentDylibAttributes::justReExport )
- cmd = LC_REEXPORT_DYLIB;
-
- if ( cmd ) {
- // make traditional load command
- uint32_t alignedSize = pointerAligned((uint32_t)(sizeof(dylib_command) + strlen(path) + 1));
- dylib_command* dc = (dylib_command*)appendLoadCommand(cmd, alignedSize);
- dc->dylib.name.offset = sizeof(dylib_command);
- dc->dylib.current_version = currentVersion.value();
- dc->dylib.compatibility_version = compatVers.value();
- dc->dylib.timestamp = 2; // needs to be some constant value that is different than dylib id load command;
- strcpy((char*)dc + dc->dylib.name.offset, path);
- }
- else {
- // make new style load command with extra flags field
- if ( depAttrs.weakLink )
- cmd = LC_LOAD_WEAK_DYLIB;
- else
- cmd = LC_LOAD_DYLIB;
- uint32_t alignedSize = pointerAligned((uint32_t)(sizeof(dylib_use_command) + strlen(path) + 1));
- dylib_use_command* dc = (dylib_use_command*)appendLoadCommand(cmd, alignedSize);
- dc->nameoff = sizeof(dylib_use_command);
- dc->current_version = currentVersion.value();
- dc->compat_version = 0x00010000; // unused, but looks like 1.0 to old tools
- dc->marker = 0x1a741800; // magic value that means dylib_use_command
- dc->flags = depAttrs.raw;
- strcpy((char*)dc + dc->nameoff, path);
- }
-}
-
-void Header::addLibSystem()
-{
- addDependentDylib("/usr/lib/libSystem.B.dylib");
-}
-
-void Header::addDylibId(CString name, Version32 compatVers, Version32 currentVersion)
-{
- uint32_t alignedSize = pointerAligned((uint32_t)(sizeof(dylib_command) + name.size() + 1));
- dylib_command* dc = (dylib_command*)appendLoadCommand(LC_ID_DYLIB, alignedSize);
- dc->dylib.name.offset = sizeof(dylib_command);
- dc->dylib.timestamp = 1; // needs to be some constant value that is different than dependent dylib
- dc->dylib.current_version = currentVersion.value();
- dc->dylib.compatibility_version = compatVers.value();
- strcpy((char*)dc + dc->dylib.name.offset, name.c_str());
-}
-
-void Header::addDyldID()
-{
- const char* path = "/usr/lib/dyld";
- uint32_t alignedSize = pointerAligned((uint32_t)(sizeof(dylinker_command) + strlen(path) + 1));
- dylinker_command* dc = (dylinker_command*)appendLoadCommand(LC_ID_DYLINKER, alignedSize);
- dc->name.offset = sizeof(dylinker_command);
- strcpy((char*)dc + dc->name.offset, path);
-}
-
-void Header::addDynamicLinker()
-{
- const char* path = "/usr/lib/dyld";
- uint32_t alignedSize = pointerAligned((uint32_t)(sizeof(dylinker_command) + strlen(path) + 1));
- dylinker_command* dc = (dylinker_command*)appendLoadCommand(LC_LOAD_DYLINKER, alignedSize);
- dc->name.offset = sizeof(dylinker_command);
- strcpy((char*)dc + dc->name.offset, path);
-}
-
-void Header::addFairPlayEncrypted(uint32_t offset, uint32_t size)
-{
- if ( is64() ) {
- encryption_info_command_64 en64;
- en64.cmd = LC_ENCRYPTION_INFO_64;
- en64.cmdsize = sizeof(encryption_info_command_64);
- en64.cryptoff = offset;
- en64.cryptsize = size;
- en64.cryptid = 0;
- en64.pad = 0;
- appendLoadCommand((load_command*)&en64);
- }
- else {
- encryption_info_command en32;
- en32.cmd = LC_ENCRYPTION_INFO;
- en32.cmdsize = sizeof(encryption_info_command);
- en32.cryptoff = offset;
- en32.cryptsize = size;
- en32.cryptid = 0;
- appendLoadCommand((load_command*)&en32);
- }
-}
-
-void Header::addRPath(const char* path)
-{
- uint32_t alignedSize = pointerAligned((uint32_t)(sizeof(rpath_command) + strlen(path) + 1));
- rpath_command* rc = (rpath_command*)appendLoadCommand(LC_RPATH, alignedSize);
- rc->path.offset = sizeof(rpath_command);
- strcpy((char*)rc + rc->path.offset, path);
-}
-
-void Header::addDyldEnvVar(const char* path)
-{
- uint32_t alignedSize = pointerAligned((uint32_t)(sizeof(dylinker_command) + strlen(path) + 1));
- dylinker_command* dc = (dylinker_command*)appendLoadCommand(LC_DYLD_ENVIRONMENT, alignedSize);
- dc->name.offset = sizeof(dylinker_command);
- strcpy((char*)dc + dc->name.offset, path);
-}
-
-void Header::addAllowableClient(const char* clientName)
-{
- uint32_t alignedSize = pointerAligned((uint32_t)(sizeof(sub_client_command) + strlen(clientName) + 1));
- sub_client_command* ac = (sub_client_command*)appendLoadCommand(LC_SUB_CLIENT, alignedSize);
- ac->client.offset = sizeof(sub_client_command);
- strcpy((char*)ac + ac->client.offset, clientName);
-}
-
-void Header::addUmbrellaName(const char* umbrellaName)
-{
- uint32_t alignedSize = pointerAligned((uint32_t)(sizeof(sub_framework_command) + strlen(umbrellaName) + 1));
- sub_framework_command* ac = (sub_framework_command*)appendLoadCommand(LC_SUB_FRAMEWORK, alignedSize);
- ac->umbrella.offset = sizeof(sub_framework_command);
- strcpy((char*)ac + ac->umbrella.offset, umbrellaName);
-}
-
-void Header::addSourceVersion(Version64 vers)
-{
- source_version_command svc;
- svc.cmd = LC_SOURCE_VERSION;
- svc.cmdsize = sizeof(source_version_command);
- svc.version = vers.value();
- appendLoadCommand((load_command*)&svc);
-}
-
-void Header::setMain(uint32_t offset)
-{
- entry_point_command ec;
- ec.cmd = LC_MAIN;
- ec.cmdsize = sizeof(entry_point_command);
- ec.entryoff = offset;
- ec.stacksize = 0;
- appendLoadCommand((load_command*)&ec);
-}
-
-void Header::setCustomStackSize(uint64_t stackSize) {
- __block bool found = false;
- forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
- if (cmd->cmd == LC_MAIN) {
- entry_point_command* ec = (entry_point_command*)cmd;
- ec->stacksize = stackSize;
- found = true;
- stop = true;
- }
- });
- assert(found);
-}
-
-void Header::setUnixEntry(uint64_t startAddr)
-{
- // FIXME: support other archs
- if ( (mh.cputype == CPU_TYPE_ARM64) || (mh.cputype == CPU_TYPE_ARM64_32) ) {
- uint32_t lcSize = 288;
- uint32_t* words = (uint32_t*)appendLoadCommand(LC_UNIXTHREAD, lcSize);
- words[2] = 6; // flavor = ARM_THREAD_STATE64
- words[3] = 68; // count = ARM_EXCEPTION_STATE64_COUNT
- bzero(&words[4], lcSize-16);
- *(uint64_t*)(&words[68]) = startAddr; // register pc = startAddr
- }
- else if ( mh.cputype == CPU_TYPE_X86_64 ) {
- uint32_t lcSize = 184;
- uint32_t* words = (uint32_t*)appendLoadCommand(LC_UNIXTHREAD, lcSize);
- words[2] = 4; // flavor = x86_THREAD_STATE64
- words[3] = 42; // count = x86_THREAD_STATE64_COUNT
- bzero(&words[4], lcSize-16);
- *(uint64_t*)(&words[36]) = startAddr; // register pc = startAddr
- }
- else {
- assert(0 && "arch not supported");
- }
-}
-
-void Header::addCodeSignature(uint32_t fileOffset, uint32_t fileSize)
-{
- linkedit_data_command lc;
- lc.cmd = LC_CODE_SIGNATURE;
- lc.cmdsize = sizeof(linkedit_data_command);
- lc.dataoff = fileOffset;
- lc.datasize = fileSize;
- appendLoadCommand((load_command*)&lc);
-}
-
-void Header::setBindOpcodesInfo(uint32_t rebaseOffset, uint32_t rebaseSize,
- uint32_t bindsOffset, uint32_t bindsSize,
- uint32_t weakBindsOffset, uint32_t weakBindsSize,
- uint32_t lazyBindsOffset, uint32_t lazyBindsSize,
- uint32_t exportTrieOffset, uint32_t exportTrieSize)
-{
- dyld_info_command lc;
- lc.cmd = LC_DYLD_INFO_ONLY;
- lc.cmdsize = sizeof(dyld_info_command);
- lc.rebase_off = rebaseOffset;
- lc.rebase_size = rebaseSize;
- lc.bind_off = bindsOffset;
- lc.bind_size = bindsSize;
- lc.weak_bind_off = weakBindsOffset;
- lc.weak_bind_size = weakBindsSize;
- lc.lazy_bind_off = lazyBindsOffset;
- lc.lazy_bind_size = lazyBindsSize;
- lc.export_off = exportTrieOffset;
- lc.export_size = exportTrieSize;
- appendLoadCommand((load_command*)&lc);
-}
-
-void Header::setChainedFixupsInfo(uint32_t cfOffset, uint32_t cfSize)
-{
- linkedit_data_command lc;
- lc.cmd = LC_DYLD_CHAINED_FIXUPS;
- lc.cmdsize = sizeof(linkedit_data_command);
- lc.dataoff = cfOffset;
- lc.datasize = cfSize;
- appendLoadCommand((load_command*)&lc);
-}
-
-void Header::setExportTrieInfo(uint32_t offset, uint32_t size)
-{
- linkedit_data_command lc;
- lc.cmd = LC_DYLD_EXPORTS_TRIE;
- lc.cmdsize = sizeof(linkedit_data_command);
- lc.dataoff = offset;
- lc.datasize = size;
- appendLoadCommand((load_command*)&lc);
-}
-
-void Header::setSplitSegInfo(uint32_t offset, uint32_t size)
-{
- linkedit_data_command lc;
- lc.cmd = LC_SEGMENT_SPLIT_INFO;
- lc.cmdsize = sizeof(linkedit_data_command);
- lc.dataoff = offset;
- lc.datasize = size;
- appendLoadCommand((load_command*)&lc);
-}
-
-void Header::setDataInCode(uint32_t offset, uint32_t size)
-{
- linkedit_data_command lc;
- lc.cmd = LC_DATA_IN_CODE;
- lc.cmdsize = sizeof(linkedit_data_command);
- lc.dataoff = offset;
- lc.datasize = size;
- appendLoadCommand((load_command*)&lc);
-}
-
-void Header::setFunctionStarts(uint32_t offset, uint32_t size)
-{
- linkedit_data_command lc;
- lc.cmd = LC_FUNCTION_STARTS;
- lc.cmdsize = sizeof(linkedit_data_command);
- lc.dataoff = offset;
- lc.datasize = size;
- appendLoadCommand((load_command*)&lc);
-}
-
-void Header::setAtomInfo(uint32_t offset, uint32_t size)
-{
- linkedit_data_command lc;
- lc.cmd = LC_ATOM_INFO;
- lc.cmdsize = sizeof(linkedit_data_command);
- lc.dataoff = offset;
- lc.datasize = size;
- appendLoadCommand((load_command*)&lc);
-}
-
-void Header::setSymbolTable(uint32_t nlistOffset, uint32_t nlistCount, uint32_t stringPoolOffset, uint32_t stringPoolSize,
- uint32_t localsCount, uint32_t globalsCount, uint32_t undefCount, uint32_t indOffset, uint32_t indCount)
-{
- symtab_command stc;
- stc.cmd = LC_SYMTAB;
- stc.cmdsize = sizeof(symtab_command);
- stc.symoff = nlistOffset;
- stc.nsyms = nlistCount;
- stc.stroff = stringPoolOffset;
- stc.strsize = stringPoolSize;
- appendLoadCommand((load_command*)&stc);
-
- dysymtab_command dstc;
- bzero(&dstc, sizeof(dstc));
- dstc.cmd = LC_DYSYMTAB;
- dstc.cmdsize = sizeof(dysymtab_command);
- dstc.ilocalsym = 0;
- dstc.nlocalsym = localsCount;
- dstc.iextdefsym = localsCount;
- dstc.nextdefsym = globalsCount;
- dstc.iundefsym = localsCount+globalsCount;
- dstc.nundefsym = undefCount;
- dstc.indirectsymoff = indOffset;
- dstc.nindirectsyms = indCount;
- appendLoadCommand((load_command*)&dstc);
-}
-
-void Header::addLinkerOption(std::span<uint8_t> buffer, uint32_t count)
-{
- uint32_t cmdSize = pointerAligned(sizeof(linker_option_command) + (uint32_t)buffer.size());
-
- linker_option_command* lc = (linker_option_command*)appendLoadCommand(LC_LINKER_OPTION, cmdSize);
- lc->cmd = LC_LINKER_OPTION;
- lc->cmdsize = cmdSize;
- lc->count = count;
- memcpy((uint8_t*)(lc + 1), buffer.data(), buffer.size());
-}
-
-Header::LinkerOption Header::LinkerOption::make(std::span<CString> opts)
-{
- LinkerOption out;
- out.count = (uint32_t)opts.size();
- assert(out.count == opts.size());
- for ( CString option : opts ) {
- if ( option.empty() )
- continue;
- size_t previousSize = out.buffer.size();
- out.buffer.resize(previousSize + option.size() + 1);
- option.strcpy((char*)out.buffer.data() + previousSize);
- }
- return out;
-}
-
-load_command* Header::findLoadCommand(uint32_t cmdNum)
-{
- __block load_command* result = nullptr;
- forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
- if ( cmd->cmd == cmdNum ) {
- result = (load_command*)cmd;
- stop = true;
- }
- });
- return result;
-}
-
-void Header::removeLoadCommand(void (^callback)(const load_command* cmd, bool& remove, bool& stop))
-{
- bool stop = false;
- const load_command* startCmds = nullptr;
- if ( mh.magic == MH_MAGIC_64 )
- startCmds = (load_command*)((char*)this + sizeof(mach_header_64));
- else if ( mh.magic == MH_MAGIC )
- startCmds = (load_command*)((char*)this + sizeof(mach_header));
- else if ( hasMachOBigEndianMagic() )
- return; // can't process big endian mach-o
- else {
- //const uint32_t* h = (uint32_t*)this;
- //diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]);
- return; // not a mach-o file
- }
- const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh.sizeofcmds);
- auto cmd = (load_command*)startCmds;
- const uint32_t origNcmds = mh.ncmds;
- unsigned bytesRemaining = mh.sizeofcmds;
- for ( uint32_t i = 0; i < origNcmds; ++i ) {
- bool remove = false;
- auto nextCmd = (load_command*)((char*)cmd + cmd->cmdsize);
- if ( cmd->cmdsize < 8 ) {
- //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, mh.ncmds, cmd, this, cmd->cmdsize);
- return;
- }
- if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
- //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, mh.ncmds, cmd, this, cmd->cmdsize, cmdsEnd);
- return;
- }
- callback(cmd, remove, stop);
- if ( remove ) {
- mh.sizeofcmds -= cmd->cmdsize;
- ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining);
- mh.ncmds--;
- }
- else {
- bytesRemaining -= cmd->cmdsize;
- cmd = nextCmd;
- }
- if ( stop )
- break;
- }
- if ( cmd )
- ::bzero(cmd, bytesRemaining);
-}
-
-uint32_t Header::relocatableHeaderAndLoadCommandsSize(bool is64, uint32_t sectionCount, uint32_t platformsCount, std::span<const Header::LinkerOption> linkerOptions)
-{
- uint32_t size = 0;
- if ( is64 ) {
- size += sizeof(mach_header_64);
- size += sizeof(segment_command_64);
- size += sizeof(section_64) * sectionCount;
- }
- else {
- size += sizeof(mach_header);
- size += sizeof(segment_command);
- size += sizeof(section) * sectionCount;
- }
- size += sizeof(symtab_command);
- size += sizeof(dysymtab_command);
- size += sizeof(build_version_command) * platformsCount;
- size += sizeof(linkedit_data_command);
-
- for ( Header::LinkerOption opt : linkerOptions ) {
- size += opt.lcSize();
- }
- return size;
-}
-
-void Header::setRelocatableSectionCount(uint32_t sectionCount)
-{
- assert(mh.filetype == MH_OBJECT);
- if ( is64() ) {
- uint32_t lcSize = (uint32_t)(sizeof(segment_command_64) + sectionCount * sizeof(section_64));
- segment_command_64* sc = (segment_command_64*)appendLoadCommand(LC_SEGMENT_64, lcSize);
- sc->segname[0] = '\0'; // MH_OBJECT has one segment with no name
- sc->vmaddr = 0;
- sc->vmsize = 0; // adjusted in updateRelocatableSegmentSize()
- sc->fileoff = 0;
- sc->filesize = 0; // adjusted in updateRelocatableSegmentSize()
- sc->initprot = 7;
- sc->maxprot = 7;
- sc->nsects = sectionCount;
- // section info to be filled in later by setRelocatableSectionInfo()
- bzero((uint8_t*)sc + sizeof(segment_command_64), sectionCount * sizeof(section_64));
- }
- else {
- uint32_t lcSize = (uint32_t)(sizeof(segment_command) + sectionCount * sizeof(section));
- segment_command* sc = (segment_command*)appendLoadCommand(LC_SEGMENT, lcSize);
- sc->segname[0] = '\0'; // MH_OBJECT has one segment with no name
- sc->vmaddr = 0;
- sc->vmsize = 0x1000; // FIXME: need dynamic segment layout
- sc->fileoff = 0;
- sc->filesize = 0x1000;
- sc->initprot = 7;
- sc->maxprot = 7;
- sc->nsects = sectionCount;
- // section info to be filled in later by setRelocatableSectionInfo()
- bzero((uint8_t*)sc + sizeof(segment_command), sectionCount * sizeof(struct section));
- }
-}
-
-void Header::updateRelocatableSegmentSize(uint64_t vmSize, uint32_t fileSize)
-{
- forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
- if ( cmd->cmd == LC_SEGMENT ) {
- segment_command* sc = (segment_command*)cmd;
- sc->vmsize = (uint32_t)vmSize;
- sc->filesize = fileSize;
- stop = true;
- }
- else if ( cmd->cmd == LC_SEGMENT_64 ) {
- segment_command_64* sc = (segment_command_64*)cmd;
- sc->vmsize = vmSize;
- sc->filesize = fileSize;
- stop = true;
- }
- });
-}
-
-
-void Header::setRelocatableSectionInfo(uint32_t sectionIndex, const char* segName, const char* sectName,
- uint32_t flags, uint64_t address, uint64_t size, uint32_t fileOffset,
- uint16_t alignment, uint32_t relocsOffset, uint32_t relocsCount)
-{
- __block struct section* section32 = nullptr;
- __block struct section_64* section64 = nullptr;
- forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
- if ( cmd->cmd == LC_SEGMENT ) {
- struct section* sections = (struct section*)((uint8_t*)cmd + sizeof(segment_command));
- section32 = §ions[sectionIndex];
- stop = true;
- }
- else if ( cmd->cmd == LC_SEGMENT_64 ) {
- struct section_64* sections = (struct section_64*)((uint8_t*)cmd + sizeof(segment_command_64));
- section64 = §ions[sectionIndex];
- stop = true;
- }
- });
- if ( section64 != nullptr ) {
- strncpy(section64->segname, segName, 16);
- strncpy(section64->sectname, sectName, 16);
- section64->addr = address;
- section64->size = size;
- section64->offset = fileOffset;
- section64->align = alignment;
- section64->reloff = relocsOffset;
- section64->nreloc = relocsCount;
- section64->flags = flags;
- section64->reserved1 = 0;
- section64->reserved2 = 0;
- section64->reserved3 = 0;
- }
- else if ( section32 != nullptr ) {
- strncpy(section32->segname, segName, 16);
- strncpy(section32->sectname, sectName, 16);
- section32->addr = (uint32_t)address;
- section32->size = (uint32_t)size;
- section32->offset = fileOffset;
- section32->align = alignment;
- section32->reloff = relocsOffset;
- section32->nreloc = relocsCount;
- section32->flags = flags;
- section32->reserved1 = 0;
- section32->reserved2 = 0;
- }
-}
-
-#endif // BUILDING_MACHO_WRITER
+ ++index;
+ }
+ // 4-byte align for next run
+ index = (index+3) & (-4);
+ rr = (RebaseInfo*)(&rr->runs[index]);
+ }
+ }
+
+ return true;
+}
} // namespace dyld3