Loading...
--- dyld/dyld-1340/common/MachOFile.cpp
+++ dyld/dyld-1125.5/common/MachOFile.cpp
@@ -52,41 +52,18 @@
}
#endif
-
-
-
#include "Defines.h"
#include <mach-o/nlist.h>
-#if !BUILDING_DYLD
- #include <vector>
-#endif // !BUILDING_DYLD
-
-#include "Architecture.h"
#include "Array.h"
-#include "Header.h"
#include "MachOFile.h"
-#include "Platform.h"
#include "SupportedArchs.h"
-#include "Universal.h"
#include "CodeSigningTypes.h"
-
-#include "ObjC.h"
#if (BUILDING_DYLD || BUILDING_LIBDYLD) && !TARGET_OS_EXCLAVEKIT
#include <subsystem.h>
#endif
-
-#if !BUILDING_DYLD
-#include "ObjCVisitor.h"
-#endif
-
-using mach_o::GradedArchitectures;
-using mach_o::Header;
-using mach_o::Platform;
-using mach_o::Universal;
-using mach_o::Version32;
namespace dyld3 {
@@ -268,13 +245,268 @@
this->forEachSlice(diag, fileLen, false, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
if ( needComma )
strlcat(strBuf, ",", 256);
- strlcat(strBuf, mach_o::Architecture(sliceCpuType, sliceCpuSubType).name(), 256);
+ strlcat(strBuf, MachOFile::archName(sliceCpuType, sliceCpuSubType), 256);
needComma = true;
});
return strBuf;
}
+bool FatFile::isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const GradedArchs& archs, bool isOSBinary,
+ uint64_t& sliceOffset, uint64_t& sliceLen, bool& missingSlice) const
+{
+ missingSlice = false;
+ if ( (this->magic != OSSwapBigToHostInt32(FAT_MAGIC)) && (this->magic != OSSwapBigToHostInt32(FAT_MAGIC_64)) )
+ return false;
+
+ __block int bestGrade = 0;
+ forEachSlice(diag, fileLen, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
+ if (int sliceGrade = archs.grade(sliceCpuType, sliceCpuSubType, isOSBinary)) {
+ if ( sliceGrade > bestGrade ) {
+ sliceOffset = (char*)sliceStart - (char*)this;
+ sliceLen = sliceSize;
+ bestGrade = sliceGrade;
+ }
+ }
+ });
+ if ( diag.hasError() )
+ return false;
+
+ if ( bestGrade == 0 )
+ missingSlice = true;
+
+ return (bestGrade != 0);
+}
+
+
+//////////////////////////// GradedArchs ////////////////////////////////////////
+
+
+#define GRADE_i386 CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL, false
+#define GRADE_x86_64 CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, false
+#define GRADE_x86_64h CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H, false
+#define GRADE_armv7 CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7, false
+#define GRADE_armv7s CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S, false
+#define GRADE_armv7k CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K, false
+#define GRADE_armv6m CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6M, false
+#define GRADE_armv7m CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7M, false
+#define GRADE_armv7em CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7EM, false
+#define GRADE_armv8m CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V8M, false
+#define GRADE_arm64 CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL, false
+#define GRADE_arm64e CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64E, false
+#define GRADE_arm64e_pb CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64E, true
+#define GRADE_arm64_32 CPU_TYPE_ARM64_32, CPU_SUBTYPE_ARM64_32_V8, false
+
+const GradedArchs GradedArchs::i386 = GradedArchs({GRADE_i386, 1});
+const GradedArchs GradedArchs::x86_64 = GradedArchs({GRADE_x86_64, 1});
+const GradedArchs GradedArchs::x86_64h = GradedArchs({GRADE_x86_64h, 2}, {GRADE_x86_64, 1});
+const GradedArchs GradedArchs::arm64 = GradedArchs({GRADE_arm64, 1});
+#if SUPPORT_ARCH_arm64e
+const GradedArchs GradedArchs::arm64e_keysoff = GradedArchs({GRADE_arm64e, 2}, {GRADE_arm64, 1});
+const GradedArchs GradedArchs::arm64e_keysoff_pb = GradedArchs({GRADE_arm64e_pb, 2}, {GRADE_arm64, 1});
+const GradedArchs GradedArchs::arm64e = GradedArchs({GRADE_arm64e, 1});
+const GradedArchs GradedArchs::arm64e_pb = GradedArchs({GRADE_arm64e_pb, 1});
+#endif
+const GradedArchs GradedArchs::armv7 = GradedArchs({GRADE_armv7, 1});
+const GradedArchs GradedArchs::armv7s = GradedArchs({GRADE_armv7s, 2}, {GRADE_armv7, 1});
+const GradedArchs GradedArchs::armv7k = GradedArchs({GRADE_armv7k, 1});
+const GradedArchs GradedArchs::armv7m = GradedArchs({GRADE_armv7m, 1});
+const GradedArchs GradedArchs::armv7em = GradedArchs({GRADE_armv7em, 1});
+
+
+#if SUPPORT_ARCH_arm64_32
+const GradedArchs GradedArchs::arm64_32 = GradedArchs({GRADE_arm64_32, 1});
+#endif
+#if BUILDING_LIBDYLD || BUILDING_UNIT_TESTS
+const GradedArchs GradedArchs::launch_AS = GradedArchs({GRADE_arm64e, 3}, {GRADE_arm64, 2}, {GRADE_x86_64, 1});
+const GradedArchs GradedArchs::launch_AS_Sim = GradedArchs({GRADE_arm64, 2}, {GRADE_x86_64, 1});
+const GradedArchs GradedArchs::launch_Intel_h = GradedArchs({GRADE_x86_64h, 3}, {GRADE_x86_64, 2}, {GRADE_i386, 1});
+const GradedArchs GradedArchs::launch_Intel = GradedArchs({GRADE_x86_64, 2}, {GRADE_i386, 1});
+const GradedArchs GradedArchs::launch_Intel_Sim = GradedArchs({GRADE_x86_64, 2}, {GRADE_i386, 1});
+#endif
+
+int GradedArchs::grade(uint32_t cputype, uint32_t cpusubtype, bool isOSBinary) const
+{
+ for (const auto& p : _orderedCpuTypes) {
+ if (p.type == 0) { break; }
+ if ( (p.type == cputype) && (p.subtype == (cpusubtype & ~CPU_SUBTYPE_MASK)) ) {
+ if ( p.osBinary ) {
+ if ( isOSBinary )
+ return p.grade;
+ }
+ else {
+ return p.grade;
+ }
+ }
+ }
+ return 0;
+}
+
+const char* GradedArchs::name() const
+{
+ return MachOFile::archName(_orderedCpuTypes[0].type, _orderedCpuTypes[0].subtype);
+}
+
+void GradedArchs::forEachArch(bool platformBinariesOnly, void (^handler)(const char*)) const
+{
+ for (const auto& p : _orderedCpuTypes) {
+ if (p.type == 0)
+ break;
+ if ( p.osBinary && !platformBinariesOnly )
+ continue;
+ handler(MachOFile::archName(p.type, p.subtype));
+ }
+}
+
+bool GradedArchs::checksOSBinary() const
+{
+ for (const auto& p : _orderedCpuTypes) {
+ if (p.type == 0) { return false; }
+ if ( p.osBinary ) { return true; }
+ }
+ __builtin_unreachable();
+}
+
+bool GradedArchs::supports64() const
+{
+ return (_orderedCpuTypes.front().type & CPU_ARCH_ABI64) != 0;
+}
+
+#if __x86_64__
+static bool isHaswell()
+{
+ // FIXME: figure out a commpage way to check this
+ struct host_basic_info info;
+ mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
+ mach_port_t hostPort = mach_host_self();
+ kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count);
+ mach_port_deallocate(mach_task_self(), hostPort);
+ return (result == KERN_SUCCESS) && (info.cpu_subtype == CPU_SUBTYPE_X86_64_H);
+}
+#endif
+
+const GradedArchs& GradedArchs::forCurrentOS(bool keysOff, bool osBinariesOnly)
+{
+#if __arm64e__
+ if ( osBinariesOnly )
+ return (keysOff ? arm64e_keysoff_pb : arm64e_pb);
+ else
+ return (keysOff ? arm64e_keysoff : arm64e);
+#elif __ARM64_ARCH_8_32__
+ return arm64_32;
+#elif __arm64__
+ return arm64;
+#elif __ARM_ARCH_7K__
+ return armv7k;
+#elif __ARM_ARCH_7S__
+ return armv7s;
+#elif __ARM_ARCH_7A__
+ return armv7;
+#elif __x86_64__
+ #if TARGET_OS_SIMULATOR
+ return x86_64;
+ #else
+ return isHaswell() ? x86_64h : x86_64;
+ #endif
+#elif __i386__
+ return i386;
+#else
+ #error unknown platform
+#endif
+}
+
+#if BUILDING_LIBDYLD || BUILDING_UNIT_TESTS
+const GradedArchs& GradedArchs::launchCurrentOS(const char* simArches)
+{
+#if TARGET_OS_SIMULATOR
+ // on Apple Silicon, there is both an arm64 and an x86_64 (under rosetta) simulators
+ // You cannot tell if you are running under rosetta, so CoreSimulator sets SIMULATOR_ARCHS
+ if ( strcmp(simArches, "arm64 x86_64") == 0 )
+ return launch_AS_Sim;
+ else
+ return x86_64;
+#elif TARGET_OS_OSX
+ #if __arm64__
+ return launch_AS;
+ #else
+ return isHaswell() ? launch_Intel_h : launch_Intel;
+ #endif
+#else
+ // all other platforms use same grading for executables as dylibs
+ return forCurrentOS(true, false);
+#endif
+}
+#endif // BUILDING_LIBDYLD
+
+const GradedArchs& GradedArchs::forName(const char* archName, bool keysOff)
+{
+ if (strcmp(archName, "x86_64h") == 0 )
+ return x86_64h;
+ else if (strcmp(archName, "x86_64") == 0 )
+ return x86_64;
+#if SUPPORT_ARCH_arm64e
+ else if (strcmp(archName, "arm64e") == 0 )
+ return keysOff ? arm64e_keysoff : arm64e;
+#endif
+ else if (strcmp(archName, "arm64") == 0 )
+ return arm64;
+ else if (strcmp(archName, "armv7k") == 0 )
+ return armv7k;
+ else if (strcmp(archName, "armv7s") == 0 )
+ return armv7s;
+ else if (strcmp(archName, "armv7") == 0 )
+ return armv7;
+ else if (strcmp(archName, "armv7m") == 0 )
+ return armv7m;
+ else if (strcmp(archName, "armv7em") == 0 )
+ return armv7em;
+#if SUPPORT_ARCH_arm64_32
+ else if (strcmp(archName, "arm64_32") == 0 )
+ return arm64_32;
+#endif
+ else if (strcmp(archName, "i386") == 0 )
+ return i386;
+ assert(0 && "unknown arch name");
+}
+
+
+
//////////////////////////// MachOFile ////////////////////////////////////////
+
+
+const MachOFile::ArchInfo MachOFile::_s_archInfos[] = {
+ { "x86_64", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL },
+ { "x86_64h", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H },
+ { "i386", CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL },
+ { "arm64", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL },
+#if SUPPORT_ARCH_arm64e
+ { "arm64e", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64E },
+#endif
+#if SUPPORT_ARCH_arm64_32
+ { "arm64_32", CPU_TYPE_ARM64_32, CPU_SUBTYPE_ARM64_32_V8 },
+#endif
+ { "armv7k", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K },
+ { "armv7s", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S },
+ { "armv7", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7 },
+ { "armv6m", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6M },
+ { "armv7m", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7M },
+ { "armv7em", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7EM },
+ { "armv8m", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V8M },
+};
+
+const MachOFile::PlatformInfo MachOFile::_s_platformInfos[] = {
+ { "macOS", Platform::macOS, LC_VERSION_MIN_MACOSX },
+ { "iOS", Platform::iOS, LC_VERSION_MIN_IPHONEOS },
+ { "tvOS", Platform::tvOS, LC_VERSION_MIN_TVOS },
+ { "watchOS", Platform::watchOS, LC_VERSION_MIN_WATCHOS },
+ { "bridgeOS", Platform::bridgeOS, LC_BUILD_VERSION },
+ { "MacCatalyst", Platform::iOSMac, LC_BUILD_VERSION },
+ { "iOS-sim", Platform::iOS_simulator, LC_BUILD_VERSION },
+ { "tvOS-sim", Platform::tvOS_simulator, LC_BUILD_VERSION },
+ { "watchOS-sim", Platform::watchOS_simulator, LC_BUILD_VERSION },
+ { "driverKit", Platform::driverKit, LC_BUILD_VERSION },
+};
+
+
bool MachOFile::is64() const
{
@@ -318,16 +550,280 @@
bool MachOFile::isArch(const char* aName) const
{
- return (strcmp(aName, mach_o::Architecture(this->cputype, this->cpusubtype).name()) == 0);
+ return (strcmp(aName, archName(this->cputype, this->cpusubtype)) == 0);
+}
+
+const char* MachOFile::archName(uint32_t cputype, uint32_t cpusubtype)
+{
+ for (const ArchInfo& info : _s_archInfos) {
+ if ( (cputype == info.cputype) && ((cpusubtype & ~CPU_SUBTYPE_MASK) == info.cpusubtype) ) {
+ return info.name;
+ }
+ }
+ return "unknown";
+}
+
+bool MachOFile::cpuTypeFromArchName(const char* archName, cpu_type_t* cputype, cpu_subtype_t* cpusubtype)
+{
+ for (const ArchInfo& info : _s_archInfos) {
+ if ( strcmp(archName, info.name) == 0 ) {
+ *cputype = info.cputype;
+ *cpusubtype = info.cpusubtype;
+ return true;
+ }
+ }
+ return false;
}
const char* MachOFile::archName() const
{
- return mach_o::Architecture(this->cputype, this->cpusubtype).name();
+ return archName(this->cputype, this->cpusubtype);
+}
+
+static void appendDigit(char*& s, unsigned& num, unsigned place, bool& startedPrinting)
+{
+ if ( num >= place ) {
+ unsigned dig = (num/place);
+ *s++ = '0' + dig;
+ num -= (dig*place);
+ startedPrinting = true;
+ }
+ else if ( startedPrinting ) {
+ *s++ = '0';
+ }
+}
+
+static void appendNumber(char*& s, unsigned num)
+{
+ assert(num < 99999);
+ bool startedPrinting = false;
+ appendDigit(s, num, 10000, startedPrinting);
+ appendDigit(s, num, 1000, startedPrinting);
+ appendDigit(s, num, 100, startedPrinting);
+ appendDigit(s, num, 10, startedPrinting);
+ appendDigit(s, num, 1, startedPrinting);
+ if ( !startedPrinting )
+ *s++ = '0';
+}
+
+void MachOFile::packedVersionToString(uint32_t packedVersion, char versionString[32])
+{
+ // sprintf(versionString, "%d.%d.%d", (packedVersion >> 16), ((packedVersion >> 8) & 0xFF), (packedVersion & 0xFF));
+ char* s = versionString;
+ appendNumber(s, (packedVersion >> 16));
+ *s++ = '.';
+ appendNumber(s, (packedVersion >> 8) & 0xFF);
+ if ( (packedVersion & 0xFF) != 0 ) {
+ *s++ = '.';
+ appendNumber(s, (packedVersion & 0xFF));
+ }
+ *s++ = '\0';
+}
+
+bool MachOFile::builtForPlatform(Platform reqPlatform, bool onlyOnePlatform) const
+{
+ __block bool foundRequestedPlatform = false;
+ __block bool foundOtherPlatform = false;
+ forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+ if ( platform == reqPlatform )
+ foundRequestedPlatform = true;
+ else
+ foundOtherPlatform = true;
+ });
+ // if checking that this binary is built for exactly one platform, fail if more
+ if ( foundOtherPlatform && onlyOnePlatform )
+ return false;
+ if ( foundRequestedPlatform )
+ return true;
+
+ // binary has no explict load command to mark platform
+ // could be an old macOS binary, look at arch
+ if ( !foundOtherPlatform && (reqPlatform == Platform::macOS) ) {
+ if ( this->cputype == CPU_TYPE_X86_64 )
+ return true;
+ if ( this->cputype == CPU_TYPE_I386 )
+ return true;
+ }
+
+#if BUILDING_DYLDINFO
+ // Allow offline tools to analyze binaries dyld doesn't load, ie, those with platforms
+ if ( !foundOtherPlatform && (reqPlatform == Platform::unknown) )
+ return true;
+#endif
+
+ return false;
+}
+
+bool MachOFile::loadableIntoProcess(Platform processPlatform, const char* path, bool internalInstall) const
+{
+ if ( this->builtForPlatform(processPlatform) )
+ return true;
+
+ // Some host macOS dylibs can be loaded into simulator processes
+ if ( MachOFile::isSimulatorPlatform(processPlatform) && this->builtForPlatform(Platform::macOS)) {
+ static const char* 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",
+ };
+ for (const char* libPath : macOSHost) {
+ if (strcmp(libPath, path) == 0)
+ return true;
+ }
+ }
+
+ // If this is being called on main executable where we expect a macOS program, Catalyst programs are also runnable
+ if ( (this->filetype == MH_EXECUTE) && (processPlatform == Platform::macOS) && this->builtForPlatform(Platform::iOSMac, true) )
+ return true;
+#if (TARGET_OS_OSX && TARGET_CPU_ARM64)
+ if ( (this->filetype == MH_EXECUTE) && (processPlatform == Platform::macOS) && this->builtForPlatform(Platform::iOS, true) )
+ return true;
+#endif
+
+
+ bool iOSonMac = (processPlatform == Platform::iOSMac);
+#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::iOSMac) )
+ 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 MachOFile::isZippered() const
+{
+ __block bool macOS = false;
+ __block bool iOSMac = false;
+ forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+ if ( platform == Platform::macOS )
+ macOS = true;
+ else if ( platform == Platform::iOSMac )
+ iOSMac = true;
+ });
+ return macOS && iOSMac;
}
bool MachOFile::inDyldCache() const {
return (this->flags & MH_DYLIB_IN_CACHE);
+}
+
+Platform MachOFile::currentPlatform()
+{
+#if TARGET_OS_SIMULATOR
+ #if TARGET_OS_WATCH
+ return Platform::watchOS_simulator;
+ #elif TARGET_OS_TV
+ return Platform::tvOS_simulator;
+ #else
+ return Platform::iOS_simulator;
+ #endif
+#elif TARGET_OS_BRIDGE
+ return Platform::bridgeOS;
+#elif TARGET_OS_WATCH
+ return Platform::watchOS;
+#elif TARGET_OS_TV
+ return Platform::tvOS;
+#elif TARGET_OS_IOS
+ return Platform::iOS;
+#elif TARGET_OS_OSX
+ return Platform::macOS;
+#elif TARGET_OS_DRIVERKIT
+ return Platform::driverKit;
+#else
+ #error unknown platform
+#endif
+}
+
+Platform MachOFile::basePlatform(dyld3::Platform reqPlatform) {
+ switch(reqPlatform) {
+ case Platform::unknown: return Platform::unknown;
+ case Platform::macOS: return Platform::macOS;
+ case Platform::iOS: return Platform::iOS;
+ case Platform::tvOS: return Platform::tvOS;
+ case Platform::watchOS: return Platform::watchOS;
+ case Platform::bridgeOS: return Platform::bridgeOS;
+ case Platform::iOSMac: return Platform::iOS;
+ case Platform::iOS_simulator: return Platform::iOS;
+ case Platform::tvOS_simulator: return Platform::tvOS;
+ case Platform::watchOS_simulator: return Platform::watchOS;
+ case Platform::driverKit: return Platform::driverKit;
+ default: return Platform::unknown;
+ }
+}
+
+
+const char* MachOFile::currentArchName()
+{
+#if __ARM_ARCH_7K__
+ return "armv7k";
+#elif __ARM_ARCH_7A__
+ return "armv7";
+#elif __ARM_ARCH_7S__
+ return "armv7s";
+#elif __arm64e__
+ return "arm64e";
+#elif __arm64__
+#if __LP64__
+ return "arm64";
+#else
+ return "arm64_32";
+#endif
+#elif __x86_64__
+ return isHaswell() ? "x86_64h" : "x86_64";
+#elif __i386__
+ return "i386";
+#else
+ #error unknown arch
+#endif
+}
+
+bool MachOFile::isSimulatorPlatform(Platform platform, Platform* basePlatform)
+{
+ switch ( platform ) {
+ case Platform::iOS_simulator:
+ if ( basePlatform )
+ *basePlatform = Platform::iOS;
+ return true;
+ case Platform::watchOS_simulator:
+ if ( basePlatform )
+ *basePlatform = Platform::watchOS;
+ return true;
+ case Platform::tvOS_simulator:
+ if ( basePlatform )
+ *basePlatform = Platform::tvOS;
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool MachOFile::isBuiltForSimulator() const
+{
+ __block bool result = false;
+ this->forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+ switch ( platform ) {
+ case Platform::iOS_simulator:
+ case Platform::watchOS_simulator:
+ case Platform::tvOS_simulator:
+ result = true;
+ break;
+ default:
+ break;
+ }
+ });
+ return result;
}
bool MachOFile::isDyld() const
@@ -399,6 +895,95 @@
{
return (this->filetype == MH_PRELOAD);
}
+
+const char* MachOFile::platformName(Platform reqPlatform)
+{
+ for (const PlatformInfo& info : _s_platformInfos) {
+ if ( info.platform == reqPlatform )
+ return info.name;
+ }
+ return "unknown";
+}
+
+void MachOFile::forEachSupportedPlatform(void (^handler)(Platform platform, uint32_t minOS, uint32_t sdk)) const
+{
+ Diagnostics diag;
+ __block bool foundPlatform = false;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ const build_version_command* buildCmd = (build_version_command *)cmd;
+ const version_min_command* versCmd = (version_min_command*)cmd;
+ uint32_t sdk;
+ switch ( cmd->cmd ) {
+ case LC_BUILD_VERSION:
+ handler((Platform)(buildCmd->platform), buildCmd->minos, buildCmd->sdk);
+ foundPlatform = true;
+ break;
+ case LC_VERSION_MIN_MACOSX:
+ sdk = versCmd->sdk;
+ // The original LC_VERSION_MIN_MACOSX did not have an sdk field, assume sdk is same as minOS for those old binaries
+ if ( sdk == 0 )
+ sdk = versCmd->version;
+ handler(Platform::macOS, versCmd->version, sdk);
+ foundPlatform = true;
+ break;
+ case LC_VERSION_MIN_IPHONEOS:
+ if ( (this->cputype == CPU_TYPE_X86_64) || (this->cputype == CPU_TYPE_I386) )
+ handler(Platform::iOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
+ else
+ handler(Platform::iOS, versCmd->version, versCmd->sdk);
+ foundPlatform = true;
+ break;
+ case LC_VERSION_MIN_TVOS:
+ if ( this->cputype == CPU_TYPE_X86_64 )
+ handler(Platform::tvOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
+ else
+ handler(Platform::tvOS, versCmd->version, versCmd->sdk);
+ foundPlatform = true;
+ break;
+ case LC_VERSION_MIN_WATCHOS:
+ if ( (this->cputype == CPU_TYPE_X86_64) || (this->cputype == CPU_TYPE_I386) )
+ handler(Platform::watchOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
+ else
+ handler(Platform::watchOS, versCmd->version, versCmd->sdk);
+ foundPlatform = true;
+ break;
+ }
+ });
+ if ( !foundPlatform ) {
+ // old binary with no explicit platform
+#if (BUILDING_DYLD || BUILDING_CLOSURE_UTIL) && TARGET_OS_OSX
+ if ( this->cputype == CPU_TYPE_X86_64 )
+ handler(Platform::macOS, 0x000A0500, 0x000A0500); // 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 ( this->cputype == CPU_TYPE_ARM64 )
+ handler(Platform::macOS, 0x000B0000, 0x000B0000); // guess it is a macOS 11.0 binary
+#endif
+ }
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+}
+
+void MachOFile::forEachSupportedBuildTool(void (^handler)(Platform platform, uint32_t tool, uint32_t version)) const
+{
+ Diagnostics diag;
+ forEachLoadCommand(diag, ^(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);
+ }
+ }
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+}
+
bool MachOFile::isMachO(Diagnostics& diag, uint64_t fileSize) const
{
@@ -540,12 +1125,12 @@
bool MachOFile::hasObjC() const
{
__block bool result = false;
- forEachSection(^(const Header::SectionInfo& info, bool& stop) {
- if ( (info.sectionName == "__objc_imageinfo") && info.segmentName.starts_with("__DATA") ) {
+ forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
+ if ( (strcmp(info.sectName, "__objc_imageinfo") == 0) && (strncmp(info.segInfo.segName, "__DATA", 6) == 0) ) {
result = true;
stop = true;
}
- if ( (this->cputype == CPU_TYPE_I386) && (info.sectionName == "__image_info") && (info.segmentName == "__OBJC") ) {
+ if ( (this->cputype == CPU_TYPE_I386) && (strcmp(info.sectName, "__image_info") == 0) && (strcmp(info.segInfo.segName, "__OBJC") == 0) ) {
result = true;
stop = true;
}
@@ -553,19 +1138,11 @@
return result;
}
-bool MachOFile::hasConstObjCSection() const
-{
- return hasSection("__DATA_CONST", "__objc_selrefs")
- || hasSection("__DATA_CONST", "__objc_classrefs")
- || hasSection("__DATA_CONST", "__objc_protorefs")
- || hasSection("__DATA_CONST", "__objc_superrefs");
-}
-
bool MachOFile::hasSection(const char* segName, const char* sectName) const
{
__block bool result = false;
- forEachSection(^(const Header::SectionInfo& info, bool& stop) {
- if ( (info.segmentName == segName) && (info.sectionName == sectName) ) {
+ forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
+ if ( (strcmp(info.segInfo.segName, segName) == 0) && (strcmp(info.sectName, sectName) == 0) ) {
result = true;
stop = true;
}
@@ -573,11 +1150,68 @@
return result;
}
+const char* MachOFile::installName() const
+{
+ const char* name;
+ uint32_t compatVersion;
+ uint32_t currentVersion;
+ if ( getDylibInstallName(&name, &compatVersion, ¤tVersion) )
+ return name;
+ return nullptr;
+}
+
+bool MachOFile::getDylibInstallName(const char** installName, uint32_t* compatVersion, uint32_t* currentVersion) const
+{
+ Diagnostics diag;
+ __block bool found = false;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( (cmd->cmd == LC_ID_DYLIB) || (cmd->cmd == LC_ID_DYLINKER) ) {
+ const dylib_command* dylibCmd = (dylib_command*)cmd;
+ *compatVersion = dylibCmd->dylib.compatibility_version;
+ *currentVersion = dylibCmd->dylib.current_version;
+ *installName = (char*)dylibCmd + dylibCmd->dylib.name.offset;
+ found = true;
+ stop = true;
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+ return found;
+}
+
+bool MachOFile::getUuid(uuid_t uuid) const
+{
+ Diagnostics diag;
+ __block bool found = false;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_UUID ) {
+ const uuid_command* uc = (const uuid_command*)cmd;
+ memcpy(uuid, uc->uuid, sizeof(uuid_t));
+ found = true;
+ stop = true;
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+ if ( !found )
+ bzero(uuid, sizeof(uuid_t));
+ return found;
+}
+
+UUID MachOFile::uuid() const {
+ Diagnostics diag;
+ __block UUID result;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_UUID ) {
+ const uuid_command* uc = (const uuid_command*)cmd;
+ result = UUID(uc->uuid);
+ stop = true;
+ }
+ });
+ diag.assertNoError();
+ return result;
+}
+
void MachOFile::forEachDependentDylib(void (^callback)(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop)) const
{
- if ( this->isDyld() )
- return;
-
Diagnostics diag;
__block unsigned count = 0;
__block bool stopped = false;
@@ -598,32 +1232,21 @@
break;
}
});
- (void)count;
- (void)stopped;
#if !BUILDING_SHARED_CACHE_UTIL && !BUILDING_DYLDINFO && !BUILDING_UNIT_TESTS
// 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)
- const Header* hdr = (const Header*)this;
#if TARGET_OS_EXCLAVEKIT
- if ( !this->isDylib() || (strncmp(hdr->installName(), "/System/ExclaveKit/usr/lib/system/", 34) != 0) )
+ if ( !this->isDylib() || (strncmp(this->installName(), "/System/ExclaveKit/usr/lib/system/", 34) != 0) )
callback("/System/ExclaveKit/usr/lib/libSystem.dylib", false, false, false, 0x00010000, 0x00010000, stopped);
#else
- if ( hdr->builtForPlatform(Platform::driverKit, true) ) {
- if ( !this->isDylib() || (strncmp(hdr->installName(), "/System/DriverKit/usr/lib/system/", 33) != 0) )
+ 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", false, false, false, 0x00010000, 0x00010000, stopped);
}
- else if ( hdr->builtForPlatform(Platform::macOS_exclaveKit, true)
- || hdr->builtForPlatform(Platform::iOS_exclaveKit, true)
- || hdr->builtForPlatform(Platform::tvOS_exclaveKit, true)
- || hdr->builtForPlatform(Platform::watchOS_exclaveKit, true)
- || hdr->builtForPlatform(Platform::visionOS_exclaveKit, true) ) {
- // do nothing for ExclaveKit dylibs
- // FIXME: only allow this behavior on internal builds
- }
else {
- if ( !this->isDylib() || (strncmp(hdr->installName(), "/usr/lib/system/", 16) != 0) )
+ if ( !this->isDylib() || (strncmp(this->installName(), "/usr/lib/system/", 16) != 0) )
callback("/usr/lib/libSystem.B.dylib", false, false, false, 0x00010000, 0x00010000, stopped);
}
#endif // TARGET_OS_EXCLAVEKIT
@@ -631,6 +1254,88 @@
#endif // !BUILDING_SHARED_CACHE_UTIL && !BUILDING_DYLDINFO && !BUILDING_UNIT_TESTS
diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
}
+
+void MachOFile::forDyldEnv(void (^callback)(const char* envVar, bool& stop)) const
+{
+ Diagnostics diag;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_DYLD_ENVIRONMENT ) {
+ const dylinker_command* envCmd = (dylinker_command*)cmd;
+ const char* keyEqualsValue = (char*)envCmd + envCmd->name.offset;
+ // 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);
+ }
+ }
+ }
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+}
+
+bool MachOFile::enforceCompatVersion() const
+{
+ __block bool result = true;
+ forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+ switch ( platform ) {
+ case Platform::macOS:
+ if ( minOS >= 0x000A0E00 ) // macOS 10.14
+ result = false;
+ break;
+ case Platform::iOS:
+ case Platform::tvOS:
+ case Platform::iOS_simulator:
+ case Platform::tvOS_simulator:
+ if ( minOS >= 0x000C0000 ) // iOS 12.0
+ result = false;
+ break;
+ case Platform::watchOS:
+ case Platform::watchOS_simulator:
+ if ( minOS >= 0x00050000 ) // watchOS 5.0
+ result = false;
+ break;
+ case Platform::bridgeOS:
+ if ( minOS >= 0x00030000 ) // bridgeOS 3.0
+ result = false;
+ break;
+ case Platform::driverKit:
+ case Platform::iOSMac:
+ result = false;
+ break;
+ case Platform::unknown:
+ break;
+ }
+ });
+ return result;
+}
+
+const thread_command* MachOFile::unixThreadLoadCommand() const {
+ Diagnostics diag;
+ __block const thread_command* command = nullptr;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_UNIXTHREAD ) {
+ command = (const thread_command*)cmd;
+ stop = true;
+ }
+ });
+ return command;
+}
+
+const linkedit_data_command* MachOFile::chainedFixupsCmd() const {
+ Diagnostics diag;
+ __block const linkedit_data_command* command = nullptr;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_DYLD_CHAINED_FIXUPS ) {
+ command = (const linkedit_data_command*)cmd;
+ stop = true;
+ }
+ });
+ return command;
+}
+
uint32_t MachOFile::entryAddrRegisterIndexForThreadCmd() const
{
@@ -666,14 +1371,249 @@
return use64BitEntryRegs() ? regs64[index] : regs32[index];
}
-void MachOFile::forEachSection(void (^callback)(const Header::SectionInfo&, bool& stop)) const
-{
- ((const Header*)this)->forEachSection(callback);
-}
-
-void MachOFile::forEachSection(void (^callback)(const Header::SegmentInfo&, const Header::SectionInfo&, bool& stop)) const
-{
- ((const Header*)this)->forEachSection(callback);
+
+bool MachOFile::getEntry(uint64_t& offset, bool& usesCRT) const
+{
+ Diagnostics diag;
+ offset = 0;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_MAIN ) {
+ entry_point_command* mainCmd = (entry_point_command*)cmd;
+ usesCRT = false;
+ offset = mainCmd->entryoff;
+ stop = true;
+ }
+ else if ( cmd->cmd == LC_UNIXTHREAD ) {
+ stop = true;
+ usesCRT = true;
+ uint64_t startAddress = entryAddrFromThreadCmd((thread_command*)cmd);
+ offset = startAddress - preferredLoadAddress();
+ }
+ });
+ return (offset != 0);
+}
+
+
+void MachOFile::forEachSegment(void (^callback)(const SegmentInfo& info, bool& stop)) const
+{
+ Diagnostics diag;
+ const bool intel32 = (this->cputype == CPU_TYPE_I386);
+ __block uint32_t segIndex = 0;
+ forEachLoadCommand(diag, ^(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;
+ uint8_t p2align = 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 > p2align )
+ p2align = sect->align;
+ }
+ SegmentInfo info;
+ info.fileOffset = segCmd->fileoff;
+ info.fileSize = segCmd->filesize;
+ info.vmAddr = segCmd->vmaddr;
+ info.vmSize = segCmd->vmsize;
+ info.sizeOfSections = sizeOfSections;
+ info.segName = segCmd->segname;
+ info.loadCommandOffset = (uint32_t)((uint8_t*)segCmd - (uint8_t*)this);
+ info.protections = segCmd->initprot;
+ info.textRelocs = false;
+ info.readOnlyData = ((segCmd->flags & SG_READ_ONLY) != 0);
+ info.isProtected = (segCmd->flags & SG_PROTECTED_VERSION_1) ? 1 : 0;
+ info.hasZeroFill = (segCmd->initprot == 3) && (segCmd->filesize < segCmd->vmsize);
+ info.p2align = p2align;
+ info.segIndex = segIndex;
+ callback(info, stop);
+ ++segIndex;
+ }
+ else if ( cmd->cmd == LC_SEGMENT ) {
+ const segment_command* segCmd = (segment_command*)cmd;
+ uint64_t sizeOfSections = segCmd->vmsize;
+ uint8_t p2align = 0;
+ bool hasTextRelocs = false;
+ 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 > p2align )
+ p2align = sect->align;
+ if ( sect->flags & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC) )
+ hasTextRelocs = true;
+ }
+ SegmentInfo info;
+ info.fileOffset = segCmd->fileoff;
+ info.fileSize = segCmd->filesize;
+ info.vmAddr = segCmd->vmaddr;
+ info.vmSize = segCmd->vmsize;
+ info.sizeOfSections = sizeOfSections;
+ info.segName = segCmd->segname;
+ info.loadCommandOffset = (uint32_t)((uint8_t*)segCmd - (uint8_t*)this);
+ info.protections = segCmd->initprot;
+ info.textRelocs = intel32 && !info.writable() && hasTextRelocs;
+ info.readOnlyData = ((segCmd->flags & SG_READ_ONLY) != 0);
+ info.isProtected = (segCmd->flags & SG_PROTECTED_VERSION_1) ? 1 : 0;
+ info.hasZeroFill = (segCmd->initprot == 3) && (segCmd->filesize < segCmd->vmsize);
+ info.p2align = p2align;
+ info.segIndex = segIndex;
+ callback(info, stop);
+ ++segIndex;
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+}
+
+uint64_t MachOFile::preferredLoadAddress() const
+{
+ __block uint64_t textVmAddr = 0;
+ forEachSegment(^(const SegmentInfo& info, bool& stop) {
+ if ( strcmp(info.segName, "__TEXT") == 0 ) {
+ textVmAddr = info.vmAddr;
+ stop = true;
+ }
+ });
+ return textVmAddr;
+}
+
+void MachOFile::forEachSection(void (^callback)(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop)) const
+{
+ Diagnostics diag;
+ BLOCK_ACCCESSIBLE_ARRAY(char, sectNameCopy, 20); // read as: char sectNameCopy[20];
+ const bool intel32 = (this->cputype == CPU_TYPE_I386);
+ __block uint32_t segIndex = 0;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ SectionInfo sectInfo;
+ if ( cmd->cmd == LC_SEGMENT_64 ) {
+ const segment_command_64* segCmd = (segment_command_64*)cmd;
+ uint64_t sizeOfSections = segCmd->vmsize;
+ uint8_t p2align = 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 > p2align )
+ p2align = sect->align;
+ }
+ sectInfo.segInfo.fileOffset = segCmd->fileoff;
+ sectInfo.segInfo.fileSize = segCmd->filesize;
+ sectInfo.segInfo.vmAddr = segCmd->vmaddr;
+ sectInfo.segInfo.vmSize = segCmd->vmsize;
+ sectInfo.segInfo.sizeOfSections = sizeOfSections;
+ sectInfo.segInfo.segName = segCmd->segname;
+ sectInfo.segInfo.loadCommandOffset = (uint32_t)((uint8_t*)segCmd - (uint8_t*)this);
+ sectInfo.segInfo.protections = segCmd->initprot;
+ sectInfo.segInfo.textRelocs = false;
+ sectInfo.segInfo.readOnlyData = ((segCmd->flags & SG_READ_ONLY) != 0);
+ sectInfo.segInfo.isProtected = (segCmd->flags & SG_PROTECTED_VERSION_1) ? 1 : 0;
+ sectInfo.segInfo.p2align = p2align;
+ sectInfo.segInfo.segIndex = segIndex;
+ for (const section_64* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) {
+ const char* sectName = sect->sectname;
+ if ( sectName[15] != '\0' ) {
+ strlcpy(sectNameCopy, sectName, 17);
+ sectName = sectNameCopy;
+ }
+ bool malformedSectionRange = (sect->addr < segCmd->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, segCmd->vmaddr + segCmd->filesize);
+ sectInfo.sectName = sectName;
+ sectInfo.sectFileOffset = sect->offset;
+ sectInfo.sectFlags = sect->flags;
+ sectInfo.sectAddr = sect->addr;
+ sectInfo.sectSize = sect->size;
+ sectInfo.sectAlignP2 = sect->align;
+ sectInfo.reserved1 = sect->reserved1;
+ sectInfo.reserved2 = sect->reserved2;
+ callback(sectInfo, malformedSectionRange, stop);
+ }
+ ++segIndex;
+ }
+ else if ( cmd->cmd == LC_SEGMENT ) {
+ const segment_command* segCmd = (segment_command*)cmd;
+ uint64_t sizeOfSections = segCmd->vmsize;
+ uint8_t p2align = 0;
+ bool hasTextRelocs = false;
+ 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 > p2align )
+ p2align = sect->align;
+ if ( sect->flags & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC) )
+ hasTextRelocs = true;
+ }
+ sectInfo.segInfo.fileOffset = segCmd->fileoff;
+ sectInfo.segInfo.fileSize = segCmd->filesize;
+ sectInfo.segInfo.vmAddr = segCmd->vmaddr;
+ sectInfo.segInfo.vmSize = segCmd->vmsize;
+ sectInfo.segInfo.sizeOfSections = sizeOfSections;
+ sectInfo.segInfo.segName = segCmd->segname;
+ sectInfo.segInfo.loadCommandOffset = (uint32_t)((uint8_t*)segCmd - (uint8_t*)this);
+ sectInfo.segInfo.protections = segCmd->initprot;
+ sectInfo.segInfo.textRelocs = intel32 && !sectInfo.segInfo.writable() && hasTextRelocs;
+ sectInfo.segInfo.readOnlyData = ((segCmd->flags & SG_READ_ONLY) != 0);
+ sectInfo.segInfo.isProtected = (segCmd->flags & SG_PROTECTED_VERSION_1) ? 1 : 0;
+ sectInfo.segInfo.p2align = p2align;
+ sectInfo.segInfo.segIndex = segIndex;
+ for (const section* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) {
+ const char* sectName = sect->sectname;
+ if ( sectName[15] != '\0' ) {
+ strlcpy(sectNameCopy, sectName, 17);
+ sectName = sectNameCopy;
+ }
+ bool malformedSectionRange = (sect->addr < segCmd->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, segCmd->vmaddr + segCmd->filesize);
+ sectInfo.sectName = sectName;
+ sectInfo.sectFileOffset = sect->offset;
+ sectInfo.sectFlags = sect->flags;
+ sectInfo.sectAddr = sect->addr;
+ sectInfo.sectSize = sect->size;
+ sectInfo.sectAlignP2 = sect->align;
+ sectInfo.reserved1 = sect->reserved1;
+ sectInfo.reserved2 = sect->reserved2;
+ callback(sectInfo, malformedSectionRange, stop);
+ }
+ ++segIndex;
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+}
+
+void MachOFile::forEachInterposingSection(Diagnostics& diag, void (^handler)(uint64_t vmOffset, uint64_t vmSize, bool& stop)) const
+{
+ const unsigned ptrSize = pointerSize();
+ const unsigned entrySize = 2 * ptrSize;
+ forEachSection(^(const MachOFile::SectionInfo& info, bool malformedSectionRange, bool &stop) {
+ if ( ((info.sectFlags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(info.sectName, "__interpose") == 0) && ((strncmp(info.segInfo.segName, "__DATA", 6) == 0) || strncmp(info.segInfo.segName, "__AUTH", 6) == 0)) ) {
+ if ( info.sectSize % entrySize != 0 ) {
+ diag.error("interposing section %s/%s has bad size", info.segInfo.segName, info.sectName);
+ stop = true;
+ return;
+ }
+ if ( malformedSectionRange ) {
+ diag.error("interposing section %s/%s extends beyond the end of the segment", info.segInfo.segName, info.sectName);
+ stop = true;
+ return;
+ }
+ if ( (info.sectAddr % ptrSize) != 0 ) {
+ diag.error("interposing section %s/%s is not pointer aligned", info.segInfo.segName, info.sectName);
+ stop = true;
+ return;
+ }
+ handler(info.sectAddr - preferredLoadAddress(), info.sectSize, stop);
+ }
+ });
+}
+
+bool MachOFile::isRestricted() const
+{
+ __block bool result = false;
+ forEachSection(^(const MachOFile::SectionInfo& info, bool malformedSectionRange, bool &stop) {
+ if ( (strcmp(info.segInfo.segName, "__RESTRICT") == 0) && (strcmp(info.sectName, "__restrict") == 0) ) {
+ result = true;
+ stop = true;
+ }
+ });
+ return result;
}
bool MachOFile::hasWeakDefs() const
@@ -686,7 +1626,12 @@
return (this->flags & MH_BINDS_TO_WEAK);
}
-#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS || BUILDING_UNIT_TESTS || BUILDING_DYLD_SYMBOLS_CACHE
+bool MachOFile::hasThreadLocalVariables() const
+{
+ return (this->flags & MH_HAS_TLV_DESCRIPTORS);
+}
+
+#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
static bool endsWith(const char* str, const char* suffix)
{
size_t strLen = strlen(str);
@@ -694,6 +1639,20 @@
if ( strLen < suffixLen )
return false;
return (strcmp(&str[strLen-suffixLen], suffix) == 0);
+}
+
+bool MachOFile::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));
}
static bool startsWith(const char* buffer, const char* valueToFind) {
@@ -742,125 +1701,45 @@
return false;
}
+// HACK: Remove this function. Its only here until we can handle cache overflow
+static bool platformExcludesSharedCache_sim(const char* installName) {
+ if ( startsWith(installName, "/System/Library/PrivateFrameworks/iWorkImport.framework/") )
+ return true;
+ if ( startsWith(installName, "/System/Library/PrivateFrameworks/News") )
+ return true;
+ if ( strcmp(installName, "/System/Library/PrivateFrameworks/StocksUI.framework/StocksUI") == 0 )
+ return true;
+ if ( strcmp(installName, "/System/Library/PrivateFrameworks/NewsUI.framework/NewsUI") == 0 )
+ return true;
+ if ( strcmp(installName, "/System/Library/PrivateFrameworks/CompassUI.framework/CompassUI") == 0 )
+ return true;
+ if ( strcmp(installName, "/System/Library/PrivateFrameworks/WeatherUI.framework/WeatherUI") == 0 )
+ return true;
+ if ( strcmp(installName, "/System/Library/PrivateFrameworks/NewsUI2.framework/NewsUI2") == 0 )
+ return true;
+ if ( strcmp(installName, "/System/Library/PrivateFrameworks/MLCompilerOS.framework/MLCompilerOS") == 0 )
+ return true;
+ if ( strcmp(installName, "/System/Library/PrivateFrameworks/HomeKitDaemon.framework/HomeKitDaemon") == 0 )
+ return true;
+ if ( strcmp(installName, "/System/Library/PrivateFrameworks/HomeKitDaemonLegacy.framework/HomeKitDaemonLegacy") == 0 )
+ return true;
+ return false;
+}
+
// Returns true if the current platform requires that this install name be excluded from the shared cache
// Note that this overrides any exclusion from anywhere else.
static bool platformExcludesSharedCache(Platform platform, const char* installName) {
- if ( (platform == Platform::macOS) || (platform == Platform::macCatalyst) || (platform == Platform::zippered) )
+ if ( MachOFile::isSimulatorPlatform(platform) )
+ return platformExcludesSharedCache_sim(installName);
+ if ( (platform == dyld3::Platform::macOS) || (platform == dyld3::Platform::iOSMac) )
return platformExcludesSharedCache_macOS(installName);
// Everything else is based on iOS so just use that value
return platformExcludesSharedCache_iOS(installName);
}
-#if !BUILDING_DYLD
-
-bool MachOFile::addendsExceedPatchTableLimit(Diagnostics& diag, mach_o::Fixups fixups) const
-{
- // rdar://122906481 (Shared cache builder - explicitly model dylibs without a need for a patch table)
- if ( strcmp(((const Header*)this)->installName(), "/usr/lib/libswiftPrespecialized.dylib") == 0 )
- return false;
-
- const bool is64bit = is64();
- const uint64_t tooLargeRegularAddend = 1 << 23;
- const uint64_t tooLargeAuthAddend = 1 << 5;
- __block bool addendTooLarge = false;
- if ( this->hasChainedFixups() ) {
-
- // with chained fixups, addends can be in the import table or embedded in a bind pointer
- __block std::vector<uint64_t> targetAddends;
- fixups.forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
- if ( is64bit )
- addend &= 0x00FFFFFFFFFFFFFF; // ignore TBI
- targetAddends.push_back(addend);
- });
- // check each pointer for embedded addend
- fixups.withChainStarts(diag, ^(const dyld_chained_starts_in_image* starts) {
- fixups.forEachFixupInAllChains(diag, starts, false, ^(mach_o::ChainedFixupPointerOnDisk* fixupLoc, uint64_t fixupSegmentOffset, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
- switch (segInfo->pointer_format) {
- case DYLD_CHAINED_PTR_ARM64E:
- case DYLD_CHAINED_PTR_ARM64E_USERLAND:
- if ( fixupLoc->arm64e.bind.bind ) {
- uint32_t ordinal = fixupLoc->arm64e.bind.ordinal;
- uint64_t addend = (ordinal < targetAddends.size()) ? targetAddends[ordinal] : 0;
- if ( fixupLoc->arm64e.bind.auth ) {
- if ( addend >= tooLargeAuthAddend ) {
- addendTooLarge = true;
- stop = true;
- }
- } else {
- addend += fixupLoc->arm64e.signExtendedAddend();
- if ( addend >= tooLargeRegularAddend ) {
- addendTooLarge = true;
- stop = true;
- }
- }
- }
- break;
- case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
- if ( fixupLoc->arm64e.bind24.bind ) {
- uint32_t ordinal = fixupLoc->arm64e.bind24.ordinal;
- uint64_t addend = (ordinal < targetAddends.size()) ? targetAddends[ordinal] : 0;
- if ( fixupLoc->arm64e.bind24.auth ) {
- if ( addend >= tooLargeAuthAddend ) {
- addendTooLarge = true;
- stop = true;
- }
- } else {
- addend += fixupLoc->arm64e.signExtendedAddend();
- if ( addend >= tooLargeRegularAddend ) {
- addendTooLarge = true;
- stop = true;
- }
- }
- }
- break;
- case DYLD_CHAINED_PTR_64:
- case DYLD_CHAINED_PTR_64_OFFSET: {
- if ( fixupLoc->generic64.rebase.bind ) {
- uint32_t ordinal = fixupLoc->generic64.bind.ordinal;
- uint64_t addend = (ordinal < targetAddends.size()) ? targetAddends[ordinal] : 0;
- addend += fixupLoc->generic64.bind.addend;
- if ( addend >= tooLargeRegularAddend ) {
- addendTooLarge = true;
- stop = true;
- }
- }
- break;
- }
- case DYLD_CHAINED_PTR_32:
- if ( fixupLoc->generic32.bind.bind ) {
- uint32_t ordinal = fixupLoc->generic32.bind.ordinal;
- uint64_t addend = (ordinal < targetAddends.size()) ? targetAddends[ordinal] : 0;
- addend += fixupLoc->generic32.bind.addend;
- if ( addend >= tooLargeRegularAddend ) {
- addendTooLarge = true;
- stop = true;
- }
- }
- break;
- }
- });
- });
- }
- else {
- // scan bind opcodes for large addend
- auto handler = ^(const mach_o::Fixups::BindTargetInfo &info, bool &stop) {
- uint64_t addend = info.addend;
- if ( is64bit )
- addend &= 0x00FFFFFFFFFFFFFF; // ignore TBI
- if ( addend >= tooLargeRegularAddend ) {
- addendTooLarge = true;
- stop = true;
- }
- };
- fixups.forEachBindTarget_Opcodes(diag, true, handler, handler);
- }
-
- return addendTooLarge;
-}
-
-bool MachOFile::canBePlacedInDyldCache(const char* path, bool checkObjC, void (^failureReason)(const char* format, ...)) const
-{
- if ( !Header::isSharedCacheEligiblePath(path) ) {
+bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const
+{
+ if ( !isSharedCacheEligiblePath(path) ) {
// Dont spam the user with an error about paths when we know these are never eligible.
return false;
}
@@ -872,7 +1751,7 @@
}
- const char* dylibName = ((const Header*)this)->installName();
+ const char* dylibName = installName();
if ( dylibName[0] != '/' ) {
failureReason("install name not an absolute path");
// Don't continue as we don't want to spam the log with errors we don't need.
@@ -891,9 +1770,15 @@
return false;
}
- mach_o::PlatformAndVersions pvs = ((const Header*)this)->platformAndVersions();
- bool platformExcludedFile = platformExcludesSharedCache(pvs.platform, dylibName);
-
+ __block bool platformExcludedFile = false;
+ forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+ if ( platformExcludedFile )
+ return;
+ if ( platformExcludesSharedCache(platform, dylibName) ) {
+ platformExcludedFile = true;
+ return;
+ }
+ });
if ( platformExcludedFile ) {
failureReason("install name is not shared cache eligible on platform");
return false;
@@ -907,26 +1792,19 @@
// don't put debug variants into dyld cache
if ( endsWith(path, "_profile.dylib") || endsWith(path, "_debug.dylib") || endsWith(path, "_asan.dylib")
- || endsWith(path, "_profile") || endsWith(path, "_debug") || endsWith(path, "_asan")
- || endsWith(path, "/CoreADI") ) {
+ || endsWith(path, "_profile") || endsWith(path, "_debug") || endsWith(path, "/CoreADI") ) {
failureReason("Variant image");
return false;
}
// dylib must have extra info for moving DATA and TEXT segments apart
__block bool hasExtraInfo = false;
- __block bool hasSplitSegMarker = false;
__block bool hasDyldInfo = false;
__block bool hasExportTrie = false;
__block Diagnostics diag;
forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
- if ( cmd->cmd == LC_SEGMENT_SPLIT_INFO ) {
- const linkedit_data_command* sigCmd = (linkedit_data_command*)cmd;
- if ( sigCmd->datasize == 0 )
- hasSplitSegMarker = true;
- else
- hasExtraInfo = true;
- }
+ if ( cmd->cmd == LC_SEGMENT_SPLIT_INFO )
+ hasExtraInfo = true;
if ( cmd->cmd == LC_DYLD_INFO_ONLY )
hasDyldInfo = true;
if ( cmd->cmd == LC_DYLD_EXPORTS_TRIE )
@@ -941,10 +1819,7 @@
if ( ignorePath == path )
return false;
}
- if ( hasSplitSegMarker )
- failureReason("Dylib explicitly linked with '-not_for_dyld_shared_cache'");
- else
- failureReason("Missing split seg info");
+ failureReason("Missing split seg info");
return false;
}
if ( !hasDyldInfo && !hasExportTrie ) {
@@ -953,30 +1828,29 @@
}
// dylib can only depend on other dylibs in the shared cache
- __block const char* badDep = nullptr;
+ __block bool allDepPathsAreGood = true;
forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
// Skip weak links. They are allowed to be missing
if ( isWeak )
return;
- if ( !Header::isSharedCacheEligiblePath(loadPath) ) {
- badDep = loadPath;
+ if ( !isSharedCacheEligiblePath(loadPath) ) {
+ allDepPathsAreGood = false;
stop = true;
}
});
- if ( badDep != nullptr ) {
- failureReason("Depends on dylibs ineligible for dyld cache '%s'. (cache dylibs must start /usr/lib or /System/Library or similar)",
- badDep);
+ if ( !allDepPathsAreGood ) {
+ failureReason("Depends on dylibs ineligable for dyld cache");
return false;
}
// dylibs with interposing info cannot be in cache
- if ( ((const Header*)this)->hasInterposingTuples() ) {
+ if ( hasInterposingTuples() ) {
failureReason("Has interposing tuples");
return false;
}
// Temporarily kick out swift binaries out of dyld cache on watchOS simulators as they have missing split seg
- if ( (this->cputype == CPU_TYPE_I386) && ((const Header*)this)->builtForPlatform(Platform::watchOS_simulator) ) {
+ if ( (this->cputype == CPU_TYPE_I386) && builtForPlatform(Platform::watchOS_simulator) ) {
if ( strncmp(dylibName, "/usr/lib/swift/", 15) == 0 ) {
failureReason("i386 swift binary");
return false;
@@ -1002,22 +1876,12 @@
if ( layout.isSwiftLibrary() && splitSeg.isV1() )
return;
- // arm64e requires signed class ROs
- if ( isArch("arm64e") ) {
- if ( std::optional<uint32_t> flags = layout.getObjcInfoFlags(); flags.has_value() ) {
- if ( (flags.value() & mach_o::ObjCImageInfo::OBJC_IMAGE_SIGNED_CLASS_RO) == 0 ) {
- failureReason("arm64e binaries must have signed Objective-C class_ro_t pointers");
- return;
- }
- }
- }
-
if ( splitSeg.isV1() ) {
// Split seg v1 can only support 1 __DATA, and no other writable segments
__block bool foundBadSegment = false;
- ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& info, bool& stop) {
- if ( info.initProt == (VM_PROT_READ | VM_PROT_WRITE) ) {
- if ( info.segmentName == "__DATA" )
+ forEachSegment(^(const SegmentInfo& info, bool& stop) {
+ if ( info.protections == (VM_PROT_READ | VM_PROT_WRITE) ) {
+ if ( strcmp(info.segName, "__DATA") == 0 )
return;
failureReason("RW segments other than __DATA requires split seg v2");
@@ -1032,7 +1896,101 @@
// <rdar://problem/57769033> dyld_cache_patchable_location only supports addend in range 0..31
// rdar://96164956 (dyld needs to support arbitrary addends in cache patch table)
- bool addendTooLarge = addendsExceedPatchTableLimit(diag, fixups);
+ const bool is64bit = is64();
+ __block bool addendTooLarge = false;
+ const uint64_t tooLargeRegularAddend = 1 << 23;
+ const uint64_t tooLargeAuthAddend = 1 << 5;
+ if ( this->hasChainedFixups() ) {
+
+ // with chained fixups, addends can be in the import table or embedded in a bind pointer
+ __block std::vector<uint64_t> targetAddends;
+ fixups.forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
+ if ( is64bit )
+ addend &= 0x00FFFFFFFFFFFFFF; // ignore TBI
+ targetAddends.push_back(addend);
+ });
+ // check each pointer for embedded addend
+ fixups.withChainStarts(diag, ^(const dyld_chained_starts_in_image* starts) {
+ fixups.forEachFixupInAllChains(diag, starts, false, ^(mach_o::ChainedFixupPointerOnDisk* fixupLoc, uint64_t fixupSegmentOffset, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
+ switch (segInfo->pointer_format) {
+ case DYLD_CHAINED_PTR_ARM64E:
+ case DYLD_CHAINED_PTR_ARM64E_USERLAND:
+ if ( fixupLoc->arm64e.bind.bind ) {
+ uint64_t ordinal = fixupLoc->arm64e.bind.ordinal;
+ uint64_t addend = (ordinal < targetAddends.size()) ? targetAddends[ordinal] : 0;
+ if ( fixupLoc->arm64e.bind.auth ) {
+ if ( addend >= tooLargeAuthAddend ) {
+ addendTooLarge = true;
+ stop = true;
+ }
+ } else {
+ addend += fixupLoc->arm64e.signExtendedAddend();
+ if ( addend >= tooLargeRegularAddend ) {
+ addendTooLarge = true;
+ stop = true;
+ }
+ }
+ }
+ break;
+ case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
+ if ( fixupLoc->arm64e.bind24.bind ) {
+ uint64_t ordinal = fixupLoc->arm64e.bind24.ordinal;
+ uint64_t addend = (ordinal < targetAddends.size()) ? targetAddends[ordinal] : 0;
+ if ( fixupLoc->arm64e.bind24.auth ) {
+ if ( addend >= tooLargeAuthAddend ) {
+ addendTooLarge = true;
+ stop = true;
+ }
+ } else {
+ addend += fixupLoc->arm64e.signExtendedAddend();
+ if ( addend >= tooLargeRegularAddend ) {
+ addendTooLarge = true;
+ stop = true;
+ }
+ }
+ }
+ break;
+ case DYLD_CHAINED_PTR_64:
+ case DYLD_CHAINED_PTR_64_OFFSET: {
+ if ( fixupLoc->generic64.rebase.bind ) {
+ uint64_t ordinal = fixupLoc->generic64.bind.ordinal;
+ uint64_t addend = (ordinal < targetAddends.size()) ? targetAddends[ordinal] : 0;
+ addend += fixupLoc->generic64.bind.addend;
+ if ( addend >= tooLargeRegularAddend ) {
+ addendTooLarge = true;
+ stop = true;
+ }
+ }
+ break;
+ }
+ case DYLD_CHAINED_PTR_32:
+ if ( fixupLoc->generic32.bind.bind ) {
+ uint64_t ordinal = fixupLoc->generic32.bind.ordinal;
+ uint64_t addend = (ordinal < targetAddends.size()) ? targetAddends[ordinal] : 0;
+ addend += fixupLoc->generic32.bind.addend;
+ if ( addend >= tooLargeRegularAddend ) {
+ addendTooLarge = true;
+ stop = true;
+ }
+ }
+ break;
+ }
+ });
+ });
+ }
+ else {
+ // scan bind opcodes for large addend
+ auto handler = ^(const mach_o::Fixups::BindTargetInfo &info, bool &stop) {
+ uint64_t addend = info.addend;
+ if ( is64bit )
+ addend &= 0x00FFFFFFFFFFFFFF; // ignore TBI
+ if ( addend >= tooLargeRegularAddend ) {
+ addendTooLarge = true;
+ stop = true;
+ }
+ };
+ fixups.forEachBindTarget_Opcodes(diag, true, handler, handler);
+ }
if ( addendTooLarge ) {
failureReason("bind addend too large");
return;
@@ -1040,7 +1998,7 @@
if ( (isArch("x86_64") || isArch("x86_64h")) ) {
__block bool rebasesOk = true;
- uint64_t startVMAddr = ((const Header*)this)->preferredLoadAddress();
+ uint64_t startVMAddr = preferredLoadAddress();
uint64_t endVMAddr = startVMAddr + mappedSize();
fixups.forEachRebase(diag, ^(uint64_t runtimeOffset, uint64_t rebasedValue, bool &stop) {
// We allow TBI for x86_64 dylibs, but then require that the remainder of the offset
@@ -1120,189 +2078,20 @@
passedLinkeditChecks = true;
});
- if ( !passedLinkeditChecks )
- return false;
-
-#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
-
- // Make sure we don't have cheaper roots patching (binds in the __objc_classlist) with bind opcodes
- if ( checkObjC && hasOpcodeFixups() && hasSection("__DATA_CONST", "__objc_classlist") ) {
- this->withFileLayout(diag, ^(const mach_o::Layout& layout) {
- mach_o::Fixups fixups(layout);
-
- __block uint64_t classListStartOffset = 0;
- __block uint64_t classListEndOffset = 0;
- this->forEachSection(^(const Header::SectionInfo& sectInfo, bool& stop) {
- if ( sectInfo.segmentName != "__DATA_CONST" )
- return;
- if ( sectInfo.sectionName != "__objc_classlist" )
- return;
-
- classListStartOffset = sectInfo.address - layout.textUnslidVMAddr();
- classListEndOffset = classListStartOffset + sectInfo.size;
- stop = true;
- });
-
- fixups.forEachBindLocation_Opcodes(diag, ^(uint64_t runtimeOffset, uint32_t segmentIndex, unsigned int targetIndex, bool &stop) {
- if ( (runtimeOffset < classListStartOffset) || (runtimeOffset > classListEndOffset) )
- return;
- passedLinkeditChecks = false;
- failureReason("has opcode fixups and objc cheaper roots. Likely has unaligned fixups");
- stop = true;
- }, ^(uint64_t runtimeOffset, uint32_t segmentIndex, unsigned int overrideBindTargetIndex, bool &stop) {
- });
- });
-
- if ( !passedLinkeditChecks )
- return false;
- }
-
- // Check there are no pointer based objc method lists in CONST segments
- if ( checkObjC ) {
- typedef std::pair<VMAddress, VMAddress> Range;
- __block std::vector<Range> constRanges;
- ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& info, bool& stop) {
- if ( info.vmsize == 0 )
- return;
- if ( info.segmentName == "__DATA_CONST" || info.segmentName == "__AUTH_CONST" )
- constRanges.push_back({ VMAddress(info.vmaddr), VMAddress(info.vmaddr + info.vmsize) });
- });
-
- if ( !constRanges.empty() ) {
- __block objc_visitor::Visitor objcVisitor = this->makeObjCVisitor(diag);
- if ( diag.hasError() )
- return false;
-
- // Returns true if the method list is bad, ie, a pointer based method list in _CONST segment
- auto isConstPointerBasedMethodList = ^(const objc_visitor::MethodList& methodList) {
- if ( (methodList.numMethods() == 0) || methodList.usesRelativeOffsets() )
- return false;
-
- VMAddress methodListVMAddr = methodList.getVMAddress().value();
- for ( const Range& range : constRanges ) {
- if ( (methodListVMAddr >= range.first) && (methodListVMAddr < range.second) )
- return true;
- }
-
- return false;
- };
-
- __block bool hasPointerMethodList = false;
- objcVisitor.forEachClassAndMetaClass(^(const objc_visitor::Class& objcClass, bool& stopClass) {
- if ( isConstPointerBasedMethodList(objcClass.getBaseMethods(objcVisitor)) ) {
- failureReason("has pointer based objc class method list in _CONST segment");
- hasPointerMethodList = true;
- stopClass = true;
- }
- });
- if ( hasPointerMethodList )
- return false;
-
- objcVisitor.forEachCategory(^(const objc_visitor::Category& objcCategory, bool& stopCategory) {
- if ( isConstPointerBasedMethodList(objcCategory.getInstanceMethods(objcVisitor)) ) {
- failureReason("has pointer based objc category instance method list in _CONST segment");
- hasPointerMethodList = true;
- stopCategory = true;
- }
- if ( isConstPointerBasedMethodList(objcCategory.getClassMethods(objcVisitor)) ) {
- failureReason("has pointer based objc category class method list in _CONST segment");
- hasPointerMethodList = true;
- stopCategory = true;
- }
- });
- if ( hasPointerMethodList )
- return false;
- }
- }
-#endif // BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
-
- return true;
-}
-
-#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
-objc_visitor::Visitor MachOFile::makeObjCVisitor(Diagnostics& diag) const
-{
- VMAddress dylibBaseAddress(((const Header*)this)->preferredLoadAddress());
-
- __block std::vector<metadata_visitor::Segment> segments;
- __block std::vector<uint64_t> bindTargets;
- this->withFileLayout(diag, ^(const mach_o::Layout &layout) {
- for ( uint32_t segIndex = 0; segIndex != layout.segments.size(); ++segIndex ) {
- const auto& layoutSegment = layout.segments[segIndex];
- metadata_visitor::Segment segment {
- .startVMAddr = VMAddress(layoutSegment.vmAddr),
- .endVMAddr = VMAddress(layoutSegment.vmAddr + layoutSegment.vmSize),
- .bufferStart = (uint8_t*)layoutSegment.buffer,
- .onDiskDylibChainedPointerFormat = 0,
- .segIndex = segIndex
- };
- segments.push_back(std::move(segment));
- }
-
- // Add chained fixup info to each segment, if we have it
- if ( this->hasChainedFixups() ) {
- mach_o::Fixups fixups(layout);
- fixups.withChainStarts(diag, ^(const dyld_chained_starts_in_image* starts) {
- mach_o::Fixups::forEachFixupChainSegment(diag, starts,
- ^(const dyld_chained_starts_in_segment *segInfo, uint32_t segIndex, bool &stop) {
- segments[segIndex].onDiskDylibChainedPointerFormat = segInfo->pointer_format;
- });
- });
- }
-
- // ObjC patching needs the bind targets for interposable references to the classes
- // build targets table
- if ( this->hasChainedFixupsLoadCommand() ) {
- mach_o::Fixups fixups(layout);
- fixups.forEachBindTarget_ChainedFixups(diag, ^(const mach_o::Fixups::BindTargetInfo &info, bool &stop) {
- if ( info.libOrdinal != BIND_SPECIAL_DYLIB_SELF ) {
- bindTargets.push_back(0);
- return;
- }
-
- mach_o::Layout::FoundSymbol foundInfo;
- if ( !layout.findExportedSymbol(diag, info.symbolName, info.weakImport, foundInfo) ) {
- bindTargets.push_back(0);
- return;
- }
-
- // We only support header offsets in this dylib, as we are looking for self binds
- // which are likely only to classes
- if ( (foundInfo.kind != mach_o::Layout::FoundSymbol::Kind::headerOffset)
- || (foundInfo.foundInDylib.value() != this) ) {
- bindTargets.push_back(0);
- return;
- }
-
- uint64_t vmAddr = layout.textUnslidVMAddr() + foundInfo.value;
- bindTargets.push_back(vmAddr);
- });
- }
- });
-
- std::optional<VMAddress> selectorStringsBaseAddress;
- objc_visitor::Visitor objcVisitor(dylibBaseAddress, this,
- std::move(segments), selectorStringsBaseAddress,
- std::move(bindTargets));
-
- return objcVisitor;
-}
-#endif // BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
-
-#endif // !BUILDING_DYLD
-
+ return passedLinkeditChecks;
+}
// Returns true if the executable path is eligible for a PrebuiltLoader on the given platform.
-bool MachOFile::canHavePrebuiltExecutableLoader(Platform platform, const std::string_view& path,
+bool MachOFile::canHavePrebuiltExecutableLoader(dyld3::Platform platform, const std::string_view& path,
void (^failureReason)(const char*)) const
{
// For now we can't build prebuilt loaders for the simulator
- if ( platform.isSimulator() ) {
+ if ( isSimulatorPlatform(platform) ) {
// Don't spam with tons of messages about executables
return false;
}
- if ( (platform == Platform::macOS) || (platform == Platform::macCatalyst) ) {
+ if ( (platform == dyld3::Platform::macOS) || (platform == dyld3::Platform::iOSMac) ) {
// We no longer support ROSP, so skip all paths which start with the special prefix
if ( path.starts_with("/System/Library/Templates/Data/") ) {
// Dont spam the user with an error about paths when we know these are never eligible.
@@ -1332,9 +2121,15 @@
failureReason("path not eligible");
return false;
}
- }
-
- if ( !this->hasLoadCommand(LC_CODE_SIGNATURE) ) {
+ } else {
+ // On embedded, only staged apps are excluded. They will run from a different location at runtime
+ if ( path.find("/staged_system_apps/") != std::string::npos ) {
+ // Dont spam the user with an error about paths when we know these are never eligible.
+ return false;
+ }
+ }
+
+ if ( !hasCodeSignature() ) {
failureReason("missing code signature");
return false;
}
@@ -1367,8 +2162,8 @@
// xnu kernel cannot have a page zero
__block bool foundPageZero = false;
- ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo &segmentInfo, bool &stop) {
- if ( segmentInfo.segmentName == "__PAGEZERO" ) {
+ forEachSegment(^(const SegmentInfo &segmentInfo, bool &stop) {
+ if ( strcmp(segmentInfo.segName, "__PAGEZERO") == 0 ) {
foundPageZero = true;
stop = true;
}
@@ -1424,7 +2219,7 @@
}
// dylibs with interposing info cannot be in cache
- if ( ((const Header*)this)->hasInterposingTuples() ) {
+ if ( hasInterposingTuples() ) {
failureReason("Has interposing tuples");
return false;
}
@@ -1432,8 +2227,8 @@
// Only x86_64 is allowed to have RWX segments
if ( !isArch("x86_64") && !isArch("x86_64h") ) {
__block bool foundBadSegment = false;
- ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo &info, bool &stop) {
- if ( (info.initProt & (VM_PROT_WRITE | VM_PROT_EXECUTE)) == (VM_PROT_WRITE | VM_PROT_EXECUTE) ) {
+ forEachSegment(^(const SegmentInfo &info, bool &stop) {
+ if ( (info.protections & (VM_PROT_WRITE | VM_PROT_EXECUTE)) == (VM_PROT_WRITE | VM_PROT_EXECUTE) ) {
failureReason("Segments are not allowed to be both writable and executable");
foundBadSegment = true;
stop = true;
@@ -1445,9 +2240,7 @@
return true;
}
-#endif // BUILDING_APP_CACHE_UTIL
-
-#if BUILDING_APP_CACHE_UTIL || BUILDING_DYLDINFO
+
bool MachOFile::usesClassicRelocationsInKernelCollection() const {
// The xnu x86_64 static executable needs to do the i386->x86_64 transition
// so will be emitted with classic relocations
@@ -1456,7 +2249,146 @@
}
return false;
}
-#endif // BUILDING_APP_CACHE_UTIL || BUILDING_DYLDINFO
+#endif
+
+#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
+static bool platformExcludesPrebuiltClosure_macOS(const char* path) {
+ // We no longer support ROSP, so skip all paths which start with the special prefix
+ if ( startsWith(path, "/System/Library/Templates/Data/") )
+ return true;
+
+ // anything inside a .app bundle is specific to app, so should not get a prebuilt closure
+ if ( strstr(path, ".app/") != NULL )
+ return true;
+
+ return false;
+}
+
+static bool platformExcludesPrebuiltClosure_iOS(const char* path) {
+ if ( strcmp(path, "/System/Library/Caches/com.apple.xpc/sdk.dylib") == 0 )
+ return true;
+ if ( strcmp(path, "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib") == 0 )
+ return true;
+ return false;
+}
+
+// Returns true if the current platform requires that this install name be excluded from the shared cache
+// Note that this overrides any exclusion from anywhere else.
+static bool platformExcludesPrebuiltClosure(Platform platform, const char* path) {
+ if ( MachOFile::isSimulatorPlatform(platform) )
+ return false;
+ if ( (platform == dyld3::Platform::macOS) || (platform == dyld3::Platform::iOSMac) )
+ return platformExcludesPrebuiltClosure_macOS(path);
+ // Everything else is based on iOS so just use that value
+ return platformExcludesPrebuiltClosure_iOS(path);
+}
+
+bool MachOFile::canHavePrecomputedDlopenClosure(const char* path, void (^failureReason)(const char*)) const
+{
+ __block bool retval = true;
+
+ // only dylibs can go in cache
+ if ( (this->filetype != MH_DYLIB) && (this->filetype != MH_BUNDLE) ) {
+ retval = false;
+ failureReason("not MH_DYLIB or MH_BUNDLE");
+ }
+
+ // flat namespace files cannot go in cache
+ if ( (this->flags & MH_TWOLEVEL) == 0 ) {
+ retval = false;
+ failureReason("not built with two level namespaces");
+ }
+
+ // can only depend on other dylibs with absolute paths
+ __block bool allDepPathsAreGood = true;
+ forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+ if ( loadPath[0] != '/' ) {
+ allDepPathsAreGood = false;
+ stop = true;
+ }
+ });
+ if ( !allDepPathsAreGood ) {
+ retval = false;
+ failureReason("depends on dylibs that are not absolute paths");
+ }
+
+ __block bool platformExcludedFile = false;
+ forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+ if ( platformExcludedFile )
+ return;
+ if ( platformExcludesPrebuiltClosure(platform, path) ) {
+ platformExcludedFile = true;
+ return;
+ }
+ });
+ if ( platformExcludedFile ) {
+ failureReason("file cannot get a prebuilt closure on this platform");
+ return false;
+ }
+
+ // dylibs with interposing info cannot have dlopen closure pre-computed
+ if ( hasInterposingTuples() ) {
+ retval = false;
+ failureReason("has interposing tuples");
+ }
+
+ // special system dylib overrides cannot have closure pre-computed
+ if ( strncmp(path, "/usr/lib/system/introspection/", 30) == 0 ) {
+ retval = false;
+ failureReason("override of OS dylib");
+ }
+
+ return retval;
+}
+#endif
+
+bool MachOFile::hasInterposingTuples() const
+{
+ __block bool hasInterposing = false;
+ Diagnostics diag;
+ forEachInterposingSection(diag, ^(uint64_t vmOffset, uint64_t vmSize, bool &stop) {
+ hasInterposing = true;
+ stop = true;
+ });
+ return hasInterposing;
+}
+
+bool MachOFile::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const
+{
+ if ( const encryption_info_command* encCmd = findFairPlayEncryptionLoadCommand() ) {
+ if ( encCmd->cryptid == 1 ) {
+ // Note: cryptid is 0 in just-built apps. The AppStore sets cryptid to 1
+ textOffset = encCmd->cryptoff;
+ size = encCmd->cryptsize;
+ return true;
+ }
+ }
+ textOffset = 0;
+ size = 0;
+ return false;
+}
+
+bool MachOFile::canBeFairPlayEncrypted() const
+{
+ return (findFairPlayEncryptionLoadCommand() != nullptr);
+}
+
+const encryption_info_command* MachOFile::findFairPlayEncryptionLoadCommand() const
+{
+ __block const encryption_info_command* result = nullptr;
+ Diagnostics diag;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( (cmd->cmd == LC_ENCRYPTION_INFO) || (cmd->cmd == LC_ENCRYPTION_INFO_64) ) {
+ result = (encryption_info_command*)cmd;
+ stop = true;
+ }
+ });
+ if ( diag.noError() )
+ return result;
+ else
+ return nullptr;
+}
+
bool MachOFile::hasLoadCommand(uint32_t cmdNum) const
{
@@ -1469,6 +2401,18 @@
}
});
return hasLC;
+}
+
+bool MachOFile::allowsAlternatePlatform() const
+{
+ __block bool result = false;
+ forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
+ if ( (strcmp(info.sectName, "__allow_alt_plat") == 0) && (strncmp(info.segInfo.segName, "__DATA", 6) == 0) ) {
+ result = true;
+ stop = true;
+ }
+ });
+ return result;
}
bool MachOFile::hasChainedFixups() const
@@ -1555,12 +2499,6 @@
chainEnd = true;
else
chain = (ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.arm64e.rebase.next*stride);
- break;
- case DYLD_CHAINED_PTR_ARM64E_SHARED_CACHE:
- if ( chainContent.cache64e.regular.next == 0 )
- chainEnd = true;
- else
- chain = (ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.cache64e.regular.next*stride);
break;
case DYLD_CHAINED_PTR_64:
case DYLD_CHAINED_PTR_64_OFFSET:
@@ -1758,65 +2696,62 @@
return result;
}
-static void getArchNames(const GradedArchitectures& archs, bool isOSBinary, char buffer[256])
+static void getArchNames(const GradedArchs& archs, bool isOSBinary, char buffer[256])
{
buffer[0] = '\0';
- archs.forEachArch(isOSBinary, ^(mach_o::Architecture arch) {
+ archs.forEachArch(isOSBinary, ^(const char* archName) {
if ( buffer[0] != '\0' )
strlcat(buffer, "' or '", 256);
- strlcat(buffer, arch.name(), 256);
- });
-}
-
-const MachOFile* MachOFile::compatibleSlice(Diagnostics& diag, uint64_t& sliceOffsetOut, uint64_t& sliceLenOut, const void* fileContent, size_t contentSize, const char* path, Platform platform, bool isOSBinary, const GradedArchitectures& archs, bool internalInstall)
-{
- const Header* hdr = nullptr;
- std::span<const uint8_t> content = { (const uint8_t*)fileContent, contentSize };
- if ( const Universal* uni = Universal::isUniversal(content) ) {
- if ( mach_o::Error err = uni->valid(content.size()) ) {
- diag.error("%s", err.message());
+ strlcat(buffer, archName, 256);
+ });
+}
+
+const MachOFile* MachOFile::compatibleSlice(Diagnostics& diag, const void* fileContent, size_t contentSize, const char* path, Platform platform, bool isOSBinary, const GradedArchs& archs, bool internalInstall)
+{
+ const MachOFile* mf = nullptr;
+ if ( const dyld3::FatFile* ff = dyld3::FatFile::isFatFile(fileContent) ) {
+ uint64_t sliceOffset;
+ uint64_t sliceLen;
+ bool missingSlice;
+ if ( ff->isFatFileWithSlice(diag, contentSize, archs, isOSBinary, sliceOffset, sliceLen, missingSlice) ) {
+ mf = (MachOFile*)((long)fileContent + sliceOffset);
+ }
+ else {
+ BLOCK_ACCCESSIBLE_ARRAY(char, gradedArchsBuf, 256);
+ getArchNames(archs, isOSBinary, gradedArchsBuf);
+
+ char strBuf[256];
+ diag.error("fat file, but missing compatible architecture (have '%s', need '%s')", ff->archNames(strBuf, contentSize), gradedArchsBuf);
return nullptr;
}
- Universal::Slice slice;
- if ( uni->bestSlice(archs, isOSBinary, slice) ) {
- hdr = (const Header*)slice.buffer.data();
- sliceLenOut = slice.buffer.size();
- sliceOffsetOut = slice.buffer.data() - content.data();
- }
- else {
- char strBuf[256];
- char gradedArchsBuf[256];
- getArchNames(archs, isOSBinary, gradedArchsBuf);
- diag.error("fat file, but missing compatible architecture (have '%s', need '%s')", uni->archNames(strBuf), gradedArchsBuf);
- return nullptr;
- }
}
else {
- hdr = (const Header*)fileContent;
- sliceLenOut = contentSize;
- sliceOffsetOut = 0;
- }
-
- std::span<const uint8_t> contents{(uint8_t*)hdr, (size_t)sliceLenOut};
- if ( !Header::isMachO(contents) ) {
- diag.error("slice is not valid mach-o file");
+ mf = (MachOFile*)fileContent;
+ }
+
+ if ( !mf->hasMachOMagic() || !mf->isMachO(diag, contentSize) ) {
+ if ( diag.noError() )
+ diag.error("not a mach-o file");
return nullptr;
}
- if ( !archs.isCompatible(hdr->arch(), isOSBinary) ) {
- char gradedArchsBuf[256];
+ if ( archs.grade(mf->cputype, mf->cpusubtype, isOSBinary) == 0 ) {
+ BLOCK_ACCCESSIBLE_ARRAY(char, gradedArchsBuf, 256);
getArchNames(archs, isOSBinary, gradedArchsBuf);
- diag.error("mach-o file, but is an incompatible architecture (have '%s', need '%s')", hdr->archName(), gradedArchsBuf);
+ diag.error("mach-o file, but is an incompatible architecture (have '%s', need '%s')", mf->archName(), gradedArchsBuf);
return nullptr;
}
- if ( !hdr->loadableIntoProcess(platform, path, internalInstall) ) {
- Platform havePlatform = hdr->platformAndVersions().platform;
- diag.error("mach-o file (%s), but incompatible platform (have '%s', need '%s')", path, havePlatform.name().c_str(), platform.name().c_str());
+ if ( !mf->loadableIntoProcess(platform, path, internalInstall) ) {
+ __block Platform havePlatform = Platform::unknown;
+ mf->forEachSupportedPlatform(^(Platform aPlat, uint32_t minOS, uint32_t sdk) {
+ havePlatform = aPlat;
+ });
+ diag.error("mach-o file (%s), but incompatible platform (have '%s', need '%s')", path, MachOFile::platformName(havePlatform), MachOFile::platformName(platform));
return nullptr;
}
- return (const MachOFile*)hdr;
+ return mf;
}
const uint8_t* MachOFile::trieWalk(Diagnostics& diag, const uint8_t* start, const uint8_t* end, const char* symbol)
@@ -1907,6 +2842,19 @@
return nullptr;
}
+void MachOFile::forEachRPath(void (^callback)(const char* rPath, bool& stop)) const
+{
+ Diagnostics diag;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_RPATH ) {
+ const char* rpath = (char*)cmd + ((struct rpath_command*)cmd)->path.offset;
+ callback(rpath, stop);
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+}
+
+
bool MachOFile::inCodeSection(uint32_t runtimeOffset) const
{
// only needed for arm64e code to know to sign pointers
@@ -1914,10 +2862,10 @@
return false;
__block bool result = false;
- uint64_t baseAddress = ((const Header*)this)->preferredLoadAddress();
- this->forEachSection(^(const Header::SectionInfo& sectInfo, bool& stop) {
- if ( ((sectInfo.address-baseAddress) <= runtimeOffset) && (runtimeOffset < (sectInfo.address+sectInfo.size-baseAddress)) ) {
- result = ( (sectInfo.flags & S_ATTR_PURE_INSTRUCTIONS) || (sectInfo.flags & S_ATTR_SOME_INSTRUCTIONS) );
+ uint64_t baseAddress = this->preferredLoadAddress();
+ this->forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+ if ( ((sectInfo.sectAddr-baseAddress) <= runtimeOffset) && (runtimeOffset < (sectInfo.sectAddr+sectInfo.sectSize-baseAddress)) ) {
+ result = ( (sectInfo.sectFlags & S_ATTR_PURE_INSTRUCTIONS) || (sectInfo.sectFlags & S_ATTR_SOME_INSTRUCTIONS) );
stop = true;
}
});
@@ -1937,6 +2885,22 @@
if ( allDepsAreNormalPtr != nullptr )
*allDepsAreNormalPtr = allDepsAreNormal;
return count;
+}
+
+bool MachOFile::hasPlusLoadMethod(Diagnostics& diag) const
+{
+ __block bool result = false;
+
+ // in new objc runtime compiler puts classes/categories with +load method in specical section
+ forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
+ if ( strncmp(info.segInfo.segName, "__DATA", 6) != 0 )
+ return;
+ if ( (strcmp(info.sectName, "__objc_nlclslist") == 0) || (strcmp(info.sectName, "__objc_nlcatlist") == 0)) {
+ result = true;
+ stop = true;
+ }
+ });
+ return result;
}
uint32_t MachOFile::getFixupsLoadCommandFileOffset() const
@@ -1984,8 +2948,8 @@
if ( result )
return true;
- forEachSection(^(const Header::SectionInfo& info, bool& stop) {
- if ( (info.flags & SECTION_TYPE) != S_INIT_FUNC_OFFSETS )
+ forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
+ if ( (info.sectFlags & SECTION_TYPE) != S_INIT_FUNC_OFFSETS )
return;
result = true;
stop = true;
@@ -1997,26 +2961,67 @@
void MachOFile::forEachInitializerPointerSection(Diagnostics& diag, void (^callback)(uint32_t sectionOffset, uint32_t sectionSize, bool& stop)) const
{
const unsigned ptrSize = pointerSize();
- const uint64_t baseAddress = ((const Header*)this)->preferredLoadAddress();
- forEachSection(^(const Header::SectionInfo& info, bool& sectStop) {
- if ( (info.flags & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS ) {
- if ( (info.size % ptrSize) != 0 ) {
- diag.error("initializer section %.*s/%.*s has bad size",
- (int)info.segmentName.size(), info.segmentName.data(),
- (int)info.sectionName.size(), info.sectionName.data());
+ const uint64_t baseAddress = preferredLoadAddress();
+ forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& sectStop) {
+ if ( (info.sectFlags & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS ) {
+ if ( (info.sectSize % ptrSize) != 0 ) {
+ diag.error("initializer section %s/%s has bad size", info.segInfo.segName, info.sectName);
sectStop = true;
return;
}
- if ( (info.address % ptrSize) != 0 ) {
- diag.error("initializer section %.*s/%.*s is not pointer aligned",
- (int)info.segmentName.size(), info.segmentName.data(),
- (int)info.sectionName.size(), info.sectionName.data());
+ if ( malformedSectionRange ) {
+ diag.error("initializer section %s/%s extends beyond its segment", info.segInfo.segName, info.sectName);
sectStop = true;
return;
}
- callback((uint32_t)(info.address - baseAddress), (uint32_t)info.size, sectStop);
- }
- });
+ if ( (info.sectAddr % ptrSize) != 0 ) {
+ diag.error("initializer section %s/%s is not pointer aligned", info.segInfo.segName, info.sectName);
+ sectStop = true;
+ return;
+ }
+ callback((uint32_t)(info.sectAddr - baseAddress), (uint32_t)info.sectSize, sectStop);
+ }
+ });
+}
+
+bool MachOFile::hasCodeSignature() const
+{
+ return this->hasLoadCommand(LC_CODE_SIGNATURE);
+}
+
+bool MachOFile::hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const
+{
+ fileOffset = 0;
+ size = 0;
+
+ Diagnostics diag;
+ forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+ if ( cmd->cmd == LC_CODE_SIGNATURE ) {
+ const linkedit_data_command* sigCmd = (linkedit_data_command*)cmd;
+ fileOffset = sigCmd->dataoff;
+ size = sigCmd->datasize;
+ stop = true;
+ }
+ });
+ diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
+
+ // early exist if no LC_CODE_SIGNATURE
+ if ( fileOffset == 0 )
+ return false;
+
+ // <rdar://problem/13622786> ignore code signatures in macOS binaries built with pre-10.9 tools
+ if ( (this->cputype == CPU_TYPE_X86_64) || (this->cputype == CPU_TYPE_I386) ) {
+ __block bool foundPlatform = false;
+ __block bool badSignature = false;
+ forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+ foundPlatform = true;
+ if ( (platform == Platform::macOS) && (sdk < 0x000A0900) )
+ badSignature = true;
+ });
+ return foundPlatform && !badSignature;
+ }
+
+ return true;
}
uint64_t MachOFile::mappedSize() const
@@ -2033,20 +3038,20 @@
__block uint64_t lowestVmAddr = 0xFFFFFFFFFFFFFFFFULL;
__block uint64_t highestVmAddr = 0;
__block uint64_t sumVmSizes = 0;
- ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& segmentInfo, bool& stop) {
- if ( segmentInfo.segmentName == "__PAGEZERO" )
+ forEachSegment(^(const SegmentInfo& segmentInfo, bool& stop) {
+ if ( strcmp(segmentInfo.segName, "__PAGEZERO") == 0 )
return;
- if ( segmentInfo.writable() && (segmentInfo.fileSize != segmentInfo.vmsize) )
+ if ( segmentInfo.writable() && (segmentInfo.fileSize != segmentInfo.vmSize) )
writeExpansion = true; // zerofill at end of __DATA
- if ( segmentInfo.vmsize == 0 ) {
+ if ( segmentInfo.vmSize == 0 ) {
// Always zero fill if we have zero-sized segments
writeExpansion = true;
}
- if ( segmentInfo.vmaddr < lowestVmAddr )
- lowestVmAddr = segmentInfo.vmaddr;
- if ( segmentInfo.vmaddr+segmentInfo.vmsize > highestVmAddr )
- highestVmAddr = segmentInfo.vmaddr + segmentInfo.vmsize;
- sumVmSizes += segmentInfo.vmsize;
+ if ( segmentInfo.vmAddr < lowestVmAddr )
+ lowestVmAddr = segmentInfo.vmAddr;
+ if ( segmentInfo.vmAddr+segmentInfo.vmSize > highestVmAddr )
+ highestVmAddr = segmentInfo.vmAddr+segmentInfo.vmSize;
+ sumVmSizes += segmentInfo.vmSize;
});
uint64_t totalVmSpace = (highestVmAddr - lowestVmAddr);
// LINKEDIT vmSize is not required to be a multiple of page size. Round up if that is the case
@@ -2056,8 +3061,8 @@
// The aux KC may have __DATA first, in which case we always want to vm_copy to the right place
bool hasOutOfOrderSegments = false;
-#if BUILDING_APP_CACHE_UTIL || BUILDING_DYLDINFO
- uint64_t textSegVMAddr = ((const Header*)this)->preferredLoadAddress();
+#if BUILDING_APP_CACHE_UTIL
+ uint64_t textSegVMAddr = preferredLoadAddress();
hasOutOfOrderSegments = textSegVMAddr != lowestVmAddr;
#endif
@@ -2065,11 +3070,21 @@
hasZeroFill = writeExpansion || hasHole || hasOutOfOrderSegments;
}
+uint32_t MachOFile::segmentCount() const
+{
+ __block uint32_t count = 0;
+ forEachSegment(^(const SegmentInfo& info, bool& stop) {
+ ++count;
+ });
+ return count;
+}
+
+
void MachOFile::forEachDOFSection(Diagnostics& diag, void (^callback)(uint32_t offset)) const
{
- forEachSection(^(const Header::SegmentInfo& segInfo, const Header::SectionInfo& info, bool &stop) {
- if ( (info.flags & SECTION_TYPE) == S_DTRACE_DOF ) {
- callback((uint32_t)(info.address - segInfo.vmaddr));
+ forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool &stop) {
+ if ( ( (info.sectFlags & SECTION_TYPE) == S_DTRACE_DOF ) && !malformedSectionRange ) {
+ callback((uint32_t)(info.sectAddr - info.segInfo.vmAddr));
}
});
}
@@ -2079,11 +3094,11 @@
__block uint64_t textUnslidVMAddr = 0;
__block uint64_t linkeditUnslidVMAddr = 0;
__block uint64_t linkeditFileOffset = 0;
- ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& info, bool& stop) {
- if ( info.segmentName == "__TEXT" ) {
- textUnslidVMAddr = info.vmaddr;
- } else if ( info.segmentName == "__LINKEDIT" ) {
- linkeditUnslidVMAddr = info.vmaddr;
+ forEachSegment(^(const SegmentInfo& info, bool& stop) {
+ if ( strcmp(info.segName, "__TEXT") == 0 ) {
+ textUnslidVMAddr = info.vmAddr;
+ } else if ( strcmp(info.segName, "__LINKEDIT") == 0 ) {
+ linkeditUnslidVMAddr = info.vmAddr;
linkeditFileOffset = info.fileOffset;
stop = true;
}
@@ -2188,7 +3203,7 @@
// Note: The kernel sometimes chooses sha1 on watchOS, and sometimes sha256.
// Embed all of them so that we just need to match any of them
- const bool isWatchOS = ((const Header*)this)->builtForPlatform(Platform::watchOS);
+ const bool isWatchOS = this->builtForPlatform(Platform::watchOS);
const bool isMainExecutable = this->isMainExecutable();
auto hashRankFn = isWatchOS ? &hash_rank_watchOS_dylibs : &hash_rank;
@@ -2305,7 +3320,6 @@
this->authenticated = 0;
this->key = 0;
this->usesAddrDiversity = 0;
- this->padding = 0;
}
MachOFile::PointerMetaData::PointerMetaData(const ChainedFixupPointerOnDisk* fixupLoc, uint16_t pointer_format)
@@ -2315,7 +3329,6 @@
this->authenticated = 0;
this->key = 0;
this->usesAddrDiversity = 0;
- this->padding = 0;
switch ( pointer_format ) {
case DYLD_CHAINED_PTR_ARM64E:
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
@@ -2332,17 +3345,6 @@
this->high8 = fixupLoc->arm64e.rebase.high8;
}
break;
- case DYLD_CHAINED_PTR_ARM64E_SHARED_CACHE:
- this->authenticated = fixupLoc->cache64e.auth.auth;
- if ( this->authenticated ) {
- this->key = fixupLoc->cache64e.auth.keyIsData ? 2 : 0; // true -> DA (2), false -> IA (0)
- this->usesAddrDiversity = fixupLoc->cache64e.auth.addrDiv;
- this->diversity = fixupLoc->cache64e.auth.diversity;
- }
- else {
- this->high8 = fixupLoc->cache64e.regular.high8;
- }
- break;
case DYLD_CHAINED_PTR_64:
case DYLD_CHAINED_PTR_64_OFFSET:
if ( fixupLoc->generic64.bind.bind == 0 )
@@ -2360,7 +3362,7 @@
&& (this->usesAddrDiversity == other.usesAddrDiversity);
}
-#if !SUPPORT_VM_LAYOUT || BUILDING_UNIT_TESTS || BUILDING_DYLD_SYMBOLS_CACHE
+#if !SUPPORT_VM_LAYOUT
bool MachOFile::getLinkeditLayout(Diagnostics& diag, mach_o::LinkeditLayout& layout) const
{
// Note, in file layout all linkedit offsets are just file offsets.
@@ -2559,25 +3561,25 @@
return;
}
- uint32_t numSegments = ((const Header*)this)->segmentCount();
+ uint32_t numSegments = this->segmentCount();
BLOCK_ACCCESSIBLE_ARRAY(mach_o::SegmentLayout, segmentLayout, numSegments);
- ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo &info, bool &stop) {
+ this->forEachSegment(^(const SegmentInfo &info, bool &stop) {
mach_o::SegmentLayout segment;
- segment.vmAddr = info.vmaddr;
- segment.vmSize = info.vmsize;
+ segment.vmAddr = info.vmAddr;
+ segment.vmSize = info.vmSize;
segment.fileOffset = info.fileOffset;
segment.fileSize = info.fileSize;
segment.buffer = (uint8_t*)this + info.fileOffset;
- segment.protections = info.initProt;
+ segment.protections = info.protections;
segment.kind = mach_o::SegmentLayout::Kind::unknown;
- if ( info.segmentName == "__TEXT" ) {
+ if ( !strcmp(info.segName, "__TEXT") ) {
segment.kind = mach_o::SegmentLayout::Kind::text;
- } else if ( info.segmentName == "__LINKEDIT" ) {
+ } else if ( !strcmp(info.segName, "__LINKEDIT") ) {
segment.kind = mach_o::SegmentLayout::Kind::linkedit;
}
- segmentLayout[info.segmentIndex] = segment;
+ segmentLayout[info.segIndex] = segment;
});
mach_o::Layout layout(this, { &segmentLayout[0], &segmentLayout[numSegments] }, linkedit);
@@ -2585,10 +3587,59 @@
}
#endif // !SUPPORT_VM_LAYOUT
+bool MachOFile::hasObjCMessageReferences() const {
+
+ __block bool foundSection = false;
+ forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+ if ( strncmp(sectInfo.segInfo.segName, "__DATA", 6) != 0 )
+ return;
+ if ( strcmp(sectInfo.sectName, "__objc_msgrefs") != 0 )
+ return;
+ foundSection = true;
+ stop = true;
+ });
+ return foundSection;
+}
+
+uint32_t MachOFile::loadCommandsFreeSpace() const
+{
+ __block uint32_t firstSectionFileOffset = 0;
+ __block uint32_t firstSegmentFileOffset = 0;
+ forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+ firstSectionFileOffset = sectInfo.sectFileOffset;
+ firstSegmentFileOffset = (uint32_t)sectInfo.segInfo.fileOffset;
+ stop = true;
+ });
+
+ uint32_t headerSize = (this->magic == MH_MAGIC_64) ? sizeof(mach_header_64) : sizeof(mach_header);
+ uint32_t existSpaceUsed = this->sizeofcmds + headerSize;
+ return firstSectionFileOffset - firstSegmentFileOffset - existSpaceUsed;
+}
+
+bool MachOFile::findObjCDataSection(const char *sectionName, uint64_t& sectionRuntimeOffset, uint64_t& sectionSize) const
+{
+ uint64_t baseAddress = preferredLoadAddress();
+
+ __block bool foundSection = false;
+ forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+ if ( (strcmp(sectInfo.segInfo.segName, "__DATA") != 0) &&
+ (strcmp(sectInfo.segInfo.segName, "__DATA_CONST") != 0) &&
+ (strcmp(sectInfo.segInfo.segName, "__DATA_DIRTY") != 0) )
+ return;
+ if ( strcmp(sectInfo.sectName, sectionName) != 0 )
+ return;
+ foundSection = true;
+ sectionRuntimeOffset = sectInfo.sectAddr - baseAddress;
+ sectionSize = sectInfo.sectSize;
+ stop = true;
+ });
+ return foundSection;
+}
+
bool MachOFile::enforceFormat(Malformed kind) const
{
// TODO: Add a mapping from generic releases to platform versions
-#if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL
+#if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL || BUILDING_RUN_STATIC
// HACK: If we are the kernel, we have a different format to enforce
if ( isFileSet() ) {
bool result = false;
@@ -2662,131 +3713,127 @@
#endif
__block bool result = false;
- mach_o::PlatformAndVersions pvs = ((const Header*)this)->platformAndVersions();
- pvs.unzip(^(mach_o::PlatformAndVersions p) {
- if ( p.platform == Platform::macOS ) {
+ forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+ switch (platform) {
+ case Platform::macOS:
switch (kind) {
- case Malformed::linkeditOrder:
- case Malformed::linkeditAlignment:
- case Malformed::dyldInfoAndlocalRelocs:
- // enforce these checks on new binaries only
- if (p.sdk.value() >= 0x000A0E00) // macOS 10.14
- result = true;
- break;
- case Malformed::segmentOrder:
- case Malformed::linkeditPermissions:
- case Malformed::textPermissions:
- case Malformed::executableData:
- case Malformed::writableData:
- case Malformed::codeSigAlignment:
- // enforce these checks on new binaries only
- if (p.sdk.value() >= 0x000A0F00) // macOS 10.15
- result = true;
- break;
- case Malformed::sectionsAddrRangeWithinSegment:
- // enforce these checks on new binaries only
- if (p.sdk.value() >= 0x000A1000) // macOS 10.16
- result = true;
- break;
- case Malformed::noLinkedDylibs:
- case Malformed::loaderPathsAreReal:
- case Malformed::mainExecInDyldCache:
- case Malformed::zerofillSwiftMetadata:
- case Malformed::sdkOnOrAfter2021:
- // enforce these checks on new binaries only
- if (p.sdk.value() >= 0x000D0000) // macOS 13.0
- result = true;
- break;
- case Malformed::noUUID:
- case Malformed::sdkOnOrAfter2022:
- if (p.sdk.value() >= 0x000E0000) // macOS 14.0 FIXME
- result = true;
- break;
- }
- } else if ( p.platform == Platform::iOS || p.platform == Platform::tvOS || p.platform == Platform::macCatalyst ) {
+ case Malformed::linkeditOrder:
+ case Malformed::linkeditAlignment:
+ case Malformed::dyldInfoAndlocalRelocs:
+ // enforce these checks on new binaries only
+ if (sdk >= 0x000A0E00) // macOS 10.14
+ result = true;
+ break;
+ case Malformed::segmentOrder:
+ case Malformed::linkeditPermissions:
+ case Malformed::textPermissions:
+ case Malformed::executableData:
+ case Malformed::writableData:
+ case Malformed::codeSigAlignment:
+ // enforce these checks on new binaries only
+ if (sdk >= 0x000A0F00) // macOS 10.15
+ result = true;
+ break;
+ case Malformed::sectionsAddrRangeWithinSegment:
+ // enforce these checks on new binaries only
+ if (sdk >= 0x000A1000) // macOS 10.16
+ result = true;
+ break;
+ case Malformed::noLinkedDylibs:
+ case Malformed::loaderPathsAreReal:
+ case Malformed::mainExecInDyldCache:
+ case Malformed::zerofillSwiftMetadata:
+ case Malformed::sdkOnOrAfter2021:
+ // enforce these checks on new binaries only
+ if (sdk >= 0x000D0000) // macOS 13.0
+ result = true;
+ break;
+ case Malformed::noUUID:
+ case Malformed::sdkOnOrAfter2022:
+ if (sdk >= 0x000E0000) // macOS 14.0 FIXME
+ result = true;
+ break;
+ }
+ break;
+ case Platform::iOS:
+ case Platform::tvOS:
+ case Platform::iOSMac:
switch (kind) {
- case Malformed::linkeditOrder:
- case Malformed::dyldInfoAndlocalRelocs:
- case Malformed::textPermissions:
- case Malformed::executableData:
- case Malformed::writableData:
+ case Malformed::linkeditOrder:
+ case Malformed::dyldInfoAndlocalRelocs:
+ case Malformed::textPermissions:
+ case Malformed::executableData:
+ case Malformed::writableData:
+ result = true;
+ break;
+ case Malformed::linkeditAlignment:
+ case Malformed::segmentOrder:
+ case Malformed::linkeditPermissions:
+ case Malformed::codeSigAlignment:
+ // enforce these checks on new binaries only
+ if (sdk >= 0x000D0000) // iOS 13
result = true;
- break;
- case Malformed::linkeditAlignment:
- case Malformed::segmentOrder:
- case Malformed::linkeditPermissions:
- case Malformed::codeSigAlignment:
- // enforce these checks on new binaries only
- if (p.sdk.value() >= 0x000D0000) // iOS 13
- result = true;
- break;
- case Malformed::sectionsAddrRangeWithinSegment:
- // enforce these checks on new binaries only
- if (p.sdk.value() >= 0x000E0000) // iOS 14
- result = true;
- break;
- case Malformed::noLinkedDylibs:
- case Malformed::loaderPathsAreReal:
- case Malformed::mainExecInDyldCache:
- case Malformed::zerofillSwiftMetadata:
- case Malformed::sdkOnOrAfter2021:
- // enforce these checks on new binaries only
- if (p.sdk.value() >= 0x00100000) // iOS 16
- result = true;
- break;
- case Malformed::noUUID:
- case Malformed::sdkOnOrAfter2022:
- if (p.sdk.value() >= 0x00110000) // iOS 17.0 FIXME
- result = true;
- break;
- }
- } else if ( p.platform == Platform::watchOS ) {
+ break;
+ case Malformed::sectionsAddrRangeWithinSegment:
+ // enforce these checks on new binaries only
+ if (sdk >= 0x000E0000) // iOS 14
+ result = true;
+ break;
+ case Malformed::noLinkedDylibs:
+ case Malformed::loaderPathsAreReal:
+ case Malformed::mainExecInDyldCache:
+ case Malformed::zerofillSwiftMetadata:
+ case Malformed::sdkOnOrAfter2021:
+ // enforce these checks on new binaries only
+ if (sdk >= 0x00100000) // iOS 16
+ result = true;
+ break;
+ case Malformed::noUUID:
+ case Malformed::sdkOnOrAfter2022:
+ if (sdk >= 0x00110000) // iOS 17.0 FIXME
+ result = true;
+ break;
+ }
+ break;
+ case Platform::watchOS:
switch (kind) {
- case Malformed::linkeditOrder:
- case Malformed::dyldInfoAndlocalRelocs:
- case Malformed::textPermissions:
- case Malformed::executableData:
- case Malformed::writableData:
+ case Malformed::linkeditOrder:
+ case Malformed::dyldInfoAndlocalRelocs:
+ case Malformed::textPermissions:
+ case Malformed::executableData:
+ case Malformed::writableData:
+ result = true;
+ break;
+ case Malformed::linkeditAlignment:
+ case Malformed::segmentOrder:
+ case Malformed::linkeditPermissions:
+ case Malformed::codeSigAlignment:
+ case Malformed::sectionsAddrRangeWithinSegment:
+ case Malformed::noLinkedDylibs:
+ case Malformed::loaderPathsAreReal:
+ case Malformed::mainExecInDyldCache:
+ case Malformed::zerofillSwiftMetadata:
+ case Malformed::sdkOnOrAfter2021:
+ // enforce these checks on new binaries only
+ if (sdk >= 0x00090000) // watchOS 9
result = true;
- break;
- case Malformed::linkeditAlignment:
- case Malformed::segmentOrder:
- case Malformed::linkeditPermissions:
- case Malformed::codeSigAlignment:
- case Malformed::sectionsAddrRangeWithinSegment:
- case Malformed::noLinkedDylibs:
- case Malformed::loaderPathsAreReal:
- case Malformed::mainExecInDyldCache:
- case Malformed::zerofillSwiftMetadata:
- case Malformed::sdkOnOrAfter2021:
- // enforce these checks on new binaries only
- if (p.sdk.value() >= 0x00090000) // watchOS 9
- result = true;
- break;
- case Malformed::noUUID:
- case Malformed::sdkOnOrAfter2022:
- if (p.sdk.value() >= 0x000A0000) // watchOS 10 FIXME
- result = true;
- break;
- }
- } else if ( p.platform == Platform::driverKit ) {
+ break;
+ case Malformed::noUUID:
+ case Malformed::sdkOnOrAfter2022:
+ if (sdk >= 0x000A0000) // watchOS 10 FIXME
+ result = true;
+ break;
+ }
+ break;
+ case Platform::driverKit:
result = true;
- } else if ( p.platform == Platform::visionOS || p.platform == Platform::visionOS_simulator ) {
- result = true; // do all checks by default
- if ( kind == Malformed::sdkOnOrAfter2022 ) {
- if (p.sdk.value() < 0x00020000) // visionOS 2.0 FIXME
- result = false;
- }
- }
-
- // if binary is so old, there is no platform info, don't enforce malformed errors
- else if ( p.platform.empty() ) {
- result = false;
- } else {
+ break;
+ default:
result = true;
- }
- });
-
+ break;
+ }
+ });
+ // if binary is so old, there is no platform info, don't enforce malformed errors
return result;
}
@@ -2859,47 +3906,43 @@
__block bool badSize = false;
__block bool hasTEXT = false;
__block bool hasLINKEDIT = false;
- ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& info, bool& stop) {
- if ( info.segmentName == "__TEXT" ) {
- if ( (info.initProt != (VM_PROT_READ|VM_PROT_EXECUTE)) && enforceFormat(Malformed::textPermissions) ) {
+ forEachSegment(^(const SegmentInfo& info, bool& stop) {
+ if ( strcmp(info.segName, "__TEXT") == 0 ) {
+ if ( (info.protections != (VM_PROT_READ|VM_PROT_EXECUTE)) && enforceFormat(Malformed::textPermissions) ) {
diag.error("in '%s' __TEXT segment permissions is not 'r-x'", path);
badPermissions = true;
stop = true;
}
hasTEXT = true;
}
- else if ( info.segmentName == "__LINKEDIT" ) {
- if ( (info.initProt != VM_PROT_READ) && enforceFormat(Malformed::linkeditPermissions) ) {
+ else if ( strcmp(info.segName, "__LINKEDIT") == 0 ) {
+ if ( (info.protections != VM_PROT_READ) && enforceFormat(Malformed::linkeditPermissions) ) {
diag.error("in '%s' __LINKEDIT segment permissions is not 'r--'", path);
badPermissions = true;
stop = true;
}
hasLINKEDIT = true;
}
- else if ( (info.initProt & 0xFFFFFFF8) != 0 ) {
- diag.error("in '%s' %.*s segment permissions has invalid bits set", path,
- (int)info.segmentName.size(), info.segmentName.data());
+ else if ( (info.protections & 0xFFFFFFF8) != 0 ) {
+ diag.error("in '%s' %s segment permissions has invalid bits set", path, info.segName);
badPermissions = true;
stop = true;
}
if ( greaterThanAddOrOverflow(info.fileOffset, info.fileSize, fileLen) ) {
- diag.error("in '%s' %.*s segment content extends beyond end of file", path,
- (int)info.segmentName.size(), info.segmentName.data());
+ diag.error("in '%s' %s segment content extends beyond end of file", path, info.segName);
badSize = true;
stop = true;
}
if ( is64() ) {
- if ( info.vmaddr + info.vmsize < info.vmaddr ) {
- diag.error("in '%s' %.*s segment vm range wraps", path,
- (int)info.segmentName.size(), info.segmentName.data());
+ if ( info.vmAddr+info.vmSize < info.vmAddr ) {
+ diag.error("in '%s' %s segment vm range wraps", path, info.segName);
badSize = true;
stop = true;
}
}
else {
- if ( (uint32_t)(info.vmaddr + info.vmsize) < (uint32_t)(info.vmaddr) ) {
- diag.error("in '%s' %.*s segment vm range wraps", path,
- (int)info.segmentName.size(), info.segmentName.data());
+ if ( (uint32_t)(info.vmAddr+info.vmSize) < (uint32_t)(info.vmAddr) ) {
+ diag.error("in '%s' %s segment vm range wraps", path, info.segName);
badSize = true;
stop = true;
}
@@ -2918,18 +3961,16 @@
// check for overlapping segments
__block bool badSegments = false;
- ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& info1, bool& stop1) {
- uint64_t seg1vmEnd = info1.vmaddr + info1.vmsize;
+ forEachSegment(^(const SegmentInfo& info1, bool& stop1) {
+ uint64_t seg1vmEnd = info1.vmAddr + info1.vmSize;
uint64_t seg1FileEnd = info1.fileOffset + info1.fileSize;
- ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& info2, bool& stop2) {
- if ( info1.segmentIndex == info2.segmentIndex )
+ forEachSegment(^(const SegmentInfo& info2, bool& stop2) {
+ if ( info1.segIndex == info2.segIndex )
return;
- uint64_t seg2vmEnd = info2.vmaddr + info2.vmsize;
+ uint64_t seg2vmEnd = info2.vmAddr + info2.vmSize;
uint64_t seg2FileEnd = info2.fileOffset + info2.fileSize;
- if ( ((info2.vmaddr <= info1.vmaddr) && (seg2vmEnd > info1.vmaddr) && (seg1vmEnd > info1.vmaddr )) || ((info2.vmaddr >= info1.vmaddr ) && (info2.vmaddr < seg1vmEnd) && (seg2vmEnd > info2.vmaddr)) ) {
- diag.error("in '%s' segment %.*s vm range overlaps segment %.*s", path,
- (int)info1.segmentName.size(), info1.segmentName.data(),
- (int)info2.segmentName.size(), info2.segmentName.data());
+ if ( ((info2.vmAddr <= info1.vmAddr) && (seg2vmEnd > info1.vmAddr) && (seg1vmEnd > info1.vmAddr )) || ((info2.vmAddr >= info1.vmAddr ) && (info2.vmAddr < seg1vmEnd) && (seg2vmEnd > info2.vmAddr)) ) {
+ diag.error("in '%s' segment %s vm range overlaps segment %s", path, info1.segName, info2.segName);
badSegments = true;
stop1 = true;
stop2 = true;
@@ -2939,24 +3980,20 @@
// HACK: Split shared caches might put the __TEXT in a SubCache, then the __DATA in a later SubCache.
// The file offsets are in to each SubCache file, which means that they might overlap
// For now we have no choice but to disable this error
- diag.error("in '%s' segment %.*s file content overlaps segment %.*s", path,
- (int)info1.segmentName.size(), info1.segmentName.data(),
- (int)info2.segmentName.size(), info2.segmentName.data());
+ diag.error("in '%s' segment %s file content overlaps segment %s", path, info1.segName, info2.segName);
badSegments = true;
stop1 = true;
stop2 = true;
}
}
- if ( (info1.segmentIndex < info2.segmentIndex) && !stop1 ) {
- if ( (info1.vmaddr > info2.vmaddr) || ((info1.fileOffset > info2.fileOffset ) && (info1.fileOffset != 0) && (info2.fileOffset != 0)) ){
+ if ( (info1.segIndex < info2.segIndex) && !stop1 ) {
+ if ( (info1.vmAddr > info2.vmAddr) || ((info1.fileOffset > info2.fileOffset ) && (info1.fileOffset != 0) && (info2.fileOffset != 0)) ){
if ( !inDyldCache() && enforceFormat(Malformed::segmentOrder) && !isStaticExecutable() ) {
// <rdar://80084852> whitelist go libraries __DWARF segments
- if ( info1.segmentName != "__DWARF" && info2.segmentName != "__DWARF" ) {
+ if ( (strcmp(info1.segName, "__DWARF") != 0 && strcmp(info2.segName, "__DWARF") != 0) ) {
// dyld cache __DATA_* segments are moved around
// The static kernel also has segments with vmAddr's before __TEXT
- diag.error("in '%s' segment load commands out of order with respect to layout for %.*s and %.*s", path,
- (int)info1.segmentName.size(), info1.segmentName.data(),
- (int)info2.segmentName.size(), info2.segmentName.data());
+ diag.error("in '%s' segment load commands out of order with respect to layout for %s and %s", path, info1.segName, info2.segName);
badSegments = true;
stop1 = true;
stop2 = true;
@@ -2987,7 +4024,7 @@
}
else if ( sect->addr+sect->size > seg->vmaddr+seg->vmsize ) {
bool ignoreError = !enforceFormat(Malformed::sectionsAddrRangeWithinSegment);
-#if BUILDING_APP_CACHE_UTIL || BUILDING_DYLDINFO
+#if BUILDING_APP_CACHE_UTIL
if ( (seg->vmsize == 0) && !strcmp(seg->segname, "__CTF") )
ignoreError = true;
#endif
@@ -3022,5 +4059,37 @@
return !badSections;
}
+void MachOFile::forEachSingletonPatch(Diagnostics& diag, void (^handler)(SingletonPatchKind kind,
+ uint64_t runtimeOffset)) const
+{
+ uint32_t ptrSize = this->pointerSize();
+ uint32_t elementSize = (2 * ptrSize);
+ uint64_t loadAddress = this->preferredLoadAddress();
+ this->forEachSection(^(const SectionInfo §Info, bool malformedSectionRange, bool &stop) {
+ if ( strcmp(sectInfo.sectName, "__const_cfobj2") != 0 )
+ return;
+ stop = true;
+
+ if ( (sectInfo.sectSize % elementSize) != 0 ) {
+ diag.error("Incorrect patching size (%lld). Should be a multiple of (2 * ptrSize)", sectInfo.sectSize);
+ return;
+ }
+
+ if ( sectInfo.reserved2 != elementSize ) {
+ // ld64 must have rejected one or more of the elements in the section, so
+ // didn't set the reserved2 to let us patch
+ diag.error("reserved2 is unsupported value %d. Expected %d",
+ sectInfo.reserved2, elementSize);
+ return;
+ }
+
+ for ( uint64_t offset = 0; offset != sectInfo.sectSize; offset += elementSize ) {
+ uint64_t targetRuntimeOffset = (sectInfo.sectAddr + offset) - loadAddress;
+ handler(SingletonPatchKind::cfObj2, targetRuntimeOffset);
+ }
+ });
+}
+
+
} // namespace dyld3