Loading...
--- dyld/dyld-655.1.1/dyld3/MachOFile.cpp
+++ /dev/null
@@ -1,985 +0,0 @@
-/*
- * Copyright (c) 2017 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include <stdlib.h>
-#include <assert.h>
-#include <string.h>
-#include <stdio.h>
-#include <TargetConditionals.h>
-#include <mach/host_info.h>
-#include <mach/mach.h>
-#include <mach/mach_host.h>
-
-#include "MachOFile.h"
-#include "SupportedArchs.h"
-
-#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
- #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
-#endif
-
-#ifndef CPU_SUBTYPE_ARM64_E
- #define CPU_SUBTYPE_ARM64_E 2
-#endif
-
-#ifndef CPU_SUBTYPE_ARM64_32_V8
- #define CPU_SUBTYPE_ARM64_32_V8 1
-#endif
-
-#ifndef CPU_TYPE_ARM64_32
- #ifndef CPU_ARCH_ABI64_32
- #define CPU_ARCH_ABI64_32 0x02000000
- #endif
- #define CPU_TYPE_ARM64_32 (CPU_TYPE_ARM | CPU_ARCH_ABI64_32)
-#endif
-
-namespace dyld3 {
-
-//////////////////////////// FatFile ////////////////////////////////////////
-
-const FatFile* FatFile::isFatFile(const void* fileStart)
-{
- const FatFile* fileStartAsFat = (FatFile*)fileStart;
- if ( (fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC)) || (fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC_64)) )
- return fileStartAsFat;
- else
- return nullptr;
-}
-
-void FatFile::forEachSlice(Diagnostics& diag, uint64_t fileLen, void (^callback)(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop)) const
-{
- if ( this->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
- if ( OSSwapBigToHostInt32(nfat_arch) > ((4096 - sizeof(fat_header)) / sizeof(fat_arch)) ) {
- diag.error("fat header too large: %u entries", OSSwapBigToHostInt32(nfat_arch));
- return;
- }
- bool stop = false;
- const fat_arch* const archs = (fat_arch*)(((char*)this)+sizeof(fat_header));
- for (uint32_t i=0; i < OSSwapBigToHostInt32(nfat_arch); ++i) {
- uint32_t cpuType = OSSwapBigToHostInt32(archs[i].cputype);
- uint32_t cpuSubType = OSSwapBigToHostInt32(archs[i].cpusubtype);
- uint32_t offset = OSSwapBigToHostInt32(archs[i].offset);
- uint32_t len = OSSwapBigToHostInt32(archs[i].size);
- if ( greaterThanAddOrOverflow(offset, len, fileLen) ) {
- diag.error("slice %d extends beyond end of file", i);
- return;
- }
- callback(cpuType, cpuSubType, (uint8_t*)this+offset, len, stop);
- if ( stop )
- break;
- }
- }
- else if ( this->magic == OSSwapBigToHostInt32(FAT_MAGIC_64) ) {
- if ( OSSwapBigToHostInt32(nfat_arch) > ((4096 - sizeof(fat_header)) / sizeof(fat_arch)) ) {
- diag.error("fat header too large: %u entries", OSSwapBigToHostInt32(nfat_arch));
- return;
- }
- bool stop = false;
- const fat_arch_64* const archs = (fat_arch_64*)(((char*)this)+sizeof(fat_header));
- for (uint32_t i=0; i < OSSwapBigToHostInt32(nfat_arch); ++i) {
- uint32_t cpuType = OSSwapBigToHostInt32(archs[i].cputype);
- uint32_t cpuSubType = OSSwapBigToHostInt32(archs[i].cpusubtype);
- uint64_t offset = OSSwapBigToHostInt64(archs[i].offset);
- uint64_t len = OSSwapBigToHostInt64(archs[i].size);
- if ( greaterThanAddOrOverflow(offset, len, fileLen) ) {
- diag.error("slice %d extends beyond end of file", i);
- return;
- }
- callback(cpuType, cpuSubType, (uint8_t*)this+offset, len, stop);
- if ( stop )
- break;
- }
- }
- else {
- diag.error("not a fat file");
- }
-}
-
-bool FatFile::isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const char* archName, 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 bool found = false;
- forEachSlice(diag, fileLen, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
- const char* sliceArchName = MachOFile::archName(sliceCpuType, sliceCpuSubType);
- if ( strcmp(sliceArchName, archName) == 0 ) {
- sliceOffset = (char*)sliceStart - (char*)this;
- sliceLen = sliceSize;
- found = true;
- stop = true;
- }
- });
- if ( diag.hasError() )
- return false;
-
- if ( !found )
- missingSlice = true;
-
- // when looking for x86_64h fallback to x86_64
- if ( !found && (strcmp(archName, "x86_64h") == 0) )
- return isFatFileWithSlice(diag, fileLen, "x86_64", sliceOffset, sliceLen, missingSlice);
-
- return found;
-}
-
-
-//////////////////////////// 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_ARM64_E },
-#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 }
-};
-
-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 },
- { "iOSMac", 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 },
-};
-
-
-bool MachOFile::is64() const
-{
- return (this->magic == MH_MAGIC_64);
-}
-
-uint32_t MachOFile::pointerSize() const
-{
- if (this->magic == MH_MAGIC_64)
- return 8;
- else
- return 4;
-}
-
-bool MachOFile::uses16KPages() const
-{
- switch (this->cputype) {
- case CPU_TYPE_ARM64:
- case CPU_TYPE_ARM:
- case CPU_TYPE_ARM64_32:
- return true;
- default:
- return false;
- }
-}
-
-bool MachOFile::isArch(const char* aName) const
-{
- 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";
-}
-
-uint32_t MachOFile::cpuTypeFromArchName(const char* archName)
-{
- for (const ArchInfo& info : _s_archInfos) {
- if ( strcmp(archName, info.name) == 0 ) {
- return info.cputype;
- }
- }
- return 0;
-}
-
-uint32_t MachOFile::cpuSubtypeFromArchName(const char* archName)
-{
- for (const ArchInfo& info : _s_archInfos) {
- if ( strcmp(archName, info.name) == 0 ) {
- return info.cpusubtype;
- }
- }
- return 0;
-}
-
-const char* MachOFile::archName() const
-{
- 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);
- *s++ = '.';
- appendNumber(s, (packedVersion & 0xFF));
- *s++ = '\0';
-}
-
-bool MachOFile::supportsPlatform(Platform reqPlatform) 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 ( foundRequestedPlatform )
- return true;
-
- // we did find some platform info, but not requested, so return false
- if ( foundOtherPlatform )
- return false;
-
- // binary has no explict load command to mark platform
- // could be an old macOS binary, look at arch
- if ( reqPlatform == Platform::macOS ) {
- if ( this->cputype == CPU_TYPE_X86_64 )
- return true;
- if ( this->cputype == CPU_TYPE_I386 )
- return true;
- }
-
- return false;
-}
-
-Platform MachOFile::currentPlatform()
-{
-#if 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_MAC
- return Platform::macOS;
-#else
- #error unknown platform
-#endif
-}
-
-#if __x86_64__
-static bool isHaswell()
-{
- // FIXME: figure out a commpage way to check this
- static bool sAlreadyDetermined = false;
- static bool sHaswell = false;
- if ( !sAlreadyDetermined ) {
- 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);
- sHaswell = (result == KERN_SUCCESS) && (info.cpu_subtype == CPU_SUBTYPE_X86_64_H);
- sAlreadyDetermined = true;
- }
- return sHaswell;
-}
-#endif
-
-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::isDylib() const
-{
- return (this->filetype == MH_DYLIB);
-}
-
-bool MachOFile::isBundle() const
-{
- return (this->filetype == MH_BUNDLE);
-}
-
-bool MachOFile::isMainExecutable() const
-{
- return (this->filetype == MH_EXECUTE);
-}
-
-bool MachOFile::isDynamicExecutable() const
-{
- if ( this->filetype != MH_EXECUTE )
- return false;
-
- // static executables do not have dyld load command
- __block bool hasDyldLoad = false;
- Diagnostics diag;
- forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
- if ( cmd->cmd == LC_LOAD_DYLINKER ) {
- hasDyldLoad = true;
- stop = true;
- }
- });
- return hasDyldLoad;
-}
-
-bool MachOFile::isPIE() const
-{
- return (this->flags & MH_PIE);
-}
-
-const char* MachOFile::platformName(Platform reqPlatform)
-{
- for (const PlatformInfo& info : _s_platformInfos) {
- if ( info.platform == reqPlatform )
- return info.name;
- }
- return "unknown platform";
-}
-
-void MachOFile::forEachSupportedPlatform(void (^handler)(Platform platform, uint32_t minOS, uint32_t sdk)) const
-{
- Diagnostics diag;
- 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;
- switch ( cmd->cmd ) {
- case LC_BUILD_VERSION:
- handler((Platform)(buildCmd->platform), buildCmd->minos, buildCmd->sdk);
- break;
- case LC_VERSION_MIN_MACOSX:
- handler(Platform::macOS, versCmd->version, versCmd->sdk);
- 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);
- 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);
- 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);
- break;
- }
- });
- diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
-}
-
-
-bool MachOFile::isMachO(Diagnostics& diag, uint64_t fileSize) const
-{
- if ( !hasMachOMagic() ) {
- diag.error("file does not start with MH_MAGIC[_64]");
- return false;
- }
- if ( this->sizeofcmds + sizeof(mach_header_64) > fileSize ) {
- diag.error("load commands exceed length of first segment");
- return false;
- }
- forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { });
- return diag.noError();
-}
-
-bool MachOFile::hasMachOMagic() const
-{
- return ( (this->magic == MH_MAGIC) || (this->magic == MH_MAGIC_64) );
-}
-
-void MachOFile::forEachLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& stop)) const
-{
- bool stop = false;
- const load_command* startCmds = nullptr;
- if ( this->magic == MH_MAGIC_64 )
- startCmds = (load_command*)((char *)this + sizeof(mach_header_64));
- else if ( this->magic == MH_MAGIC )
- startCmds = (load_command*)((char *)this + sizeof(mach_header));
- else {
- const uint32_t* h = (uint32_t*)this;
- diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]);
- return; // not a mach-o file
- }
- const load_command* const cmdsEnd = (load_command*)((char*)startCmds + this->sizeofcmds);
- const load_command* cmd = startCmds;
- for (uint32_t i = 0; i < this->ncmds; ++i) {
- const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize);
- if ( cmd->cmdsize < 8 ) {
- diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, this->ncmds, cmd, this, cmd->cmdsize);
- return;
- }
- if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
- diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, this->ncmds, cmd, this, cmd->cmdsize, cmdsEnd);
- return;
- }
- callback(cmd, stop);
- if ( stop )
- return;
- cmd = nextCmd;
- }
-}
-
-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 ) {
- 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;
-}
-
-void MachOFile::forEachDependentDylib(void (^callback)(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop)) const
-{
- Diagnostics diag;
- forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
- switch ( cmd->cmd ) {
- case LC_LOAD_DYLIB:
- case LC_LOAD_WEAK_DYLIB:
- case LC_REEXPORT_DYLIB:
- case LC_LOAD_UPWARD_DYLIB: {
- const dylib_command* dylibCmd = (dylib_command*)cmd;
- const char* loadPath = (char*)dylibCmd + dylibCmd->dylib.name.offset;
- callback(loadPath, (cmd->cmd == LC_LOAD_WEAK_DYLIB), (cmd->cmd == LC_REEXPORT_DYLIB), (cmd->cmd == LC_LOAD_UPWARD_DYLIB),
- dylibCmd->dylib.compatibility_version, dylibCmd->dylib.current_version, stop);
- }
- break;
- }
- });
- 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::iOSMac:
- result = false;
- break;
- case Platform::unknown:
- break;
- }
- });
- return result;
-}
-
-
-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.protections = segCmd->initprot;
- info.textRelocs = false;
- 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.protections = segCmd->initprot;
- info.textRelocs = intel32 && !info.writable() && hasTextRelocs;
- 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
-}
-
-void MachOFile::forEachSection(void (^callback)(const SectionInfo& sectInfo, bool malformedSectionRange, 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) {
- 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.protections = segCmd->initprot;
- sectInfo.segInfo.textRelocs = false;
- sectInfo.segInfo.p2align = p2align;
- sectInfo.segInfo.segIndex = segIndex;
- for (const section_64* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) {
- const char* sectName = sect->sectname;
- char sectNameCopy[20];
- 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.protections = segCmd->initprot;
- sectInfo.segInfo.textRelocs = intel32 && !sectInfo.segInfo.writable() && hasTextRelocs;
- sectInfo.segInfo.p2align = p2align;
- sectInfo.segInfo.segIndex = segIndex;
- for (const section* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) {
- const char* sectName = sect->sectname;
- char sectNameCopy[20];
- 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
-}
-
-bool MachOFile::hasWeakDefs() const
-{
- return (this->flags & MH_WEAK_DEFINES);
-}
-
-bool MachOFile::hasThreadLocalVariables() const
-{
- return (this->flags & MH_HAS_TLV_DESCRIPTORS);
-}
-
-static bool endsWith(const char* str, const char* suffix)
-{
- size_t strLen = strlen(str);
- size_t suffixLen = strlen(suffix);
- if ( strLen < suffixLen )
- return false;
- return (strcmp(&str[strLen-suffixLen], suffix) == 0);
-}
-
-bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const
-{
- // only dylibs can go in cache
- if ( this->filetype != MH_DYLIB ) {
- failureReason("Not MH_DYLIB");
- return false; // cannot continue, installName() will assert() if not a dylib
- }
-
- // only dylibs built for /usr/lib or /System/Library can go in cache
- bool retval = true;
- const char* dylibName = installName();
- if ( dylibName[0] != '/' ) {
- retval = false;
- failureReason("install name not an absolute path");
- }
- else if ( (strncmp(dylibName, "/usr/lib/", 9) != 0) && (strncmp(dylibName, "/System/Library/", 16) != 0) ) {
- retval = false;
- failureReason("Not in '/usr/lib/' or '/System/Library/'");
- }
-
- // flat namespace files cannot go in cache
- if ( (this->flags & MH_TWOLEVEL) == 0 ) {
- retval = false;
- failureReason("Not built with two level namespaces");
- }
-
- // don't put debug variants into dyld cache
- if ( endsWith(path, "_profile.dylib") || endsWith(path, "_debug.dylib") || endsWith(path, "_profile") || endsWith(path, "_debug") || endsWith(path, "/CoreADI") ) {
- retval = false;
- failureReason("Variant image");
- }
-
- // dylib must have extra info for moving DATA and TEXT segments apart
- __block bool hasExtraInfo = false;
- __block bool hasDyldInfo = false;
- Diagnostics diag;
- forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
- if ( cmd->cmd == LC_SEGMENT_SPLIT_INFO )
- hasExtraInfo = true;
- if ( cmd->cmd == LC_DYLD_INFO_ONLY )
- hasDyldInfo = true;
- });
- if ( !hasExtraInfo ) {
- retval = false;
- failureReason("Missing split seg info");
- }
- if ( !hasDyldInfo ) {
- retval = false;
- failureReason("Old binary, missing dyld info");
- }
-
- // dylib can only depend on other dylibs in the shared cache
- __block bool allDepPathsAreGood = true;
- forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
- if ( (strncmp(loadPath, "/usr/lib/", 9) != 0) && (strncmp(loadPath, "/System/Library/", 16) != 0) ) {
- allDepPathsAreGood = false;
- stop = true;
- }
- });
- if ( !allDepPathsAreGood ) {
- retval = false;
- failureReason("Depends on dylibs ineligable for dyld cache");
- }
-
- // dylibs with interposing info cannot be in cache
- __block bool hasInterposing = false;
- forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool &stop) {
- if ( ((info.sectFlags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(info.sectName, "__interpose") == 0) && (strcmp(info.segInfo.segName, "__DATA") == 0)) )
- hasInterposing = true;
- });
- if ( hasInterposing ) {
- retval = false;
- failureReason("Has interposing tuples");
- }
-
- return retval;
-}
-
-
-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::hasChainedFixups() const
-{
-#if SUPPORT_ARCH_arm64e
- // for now only arm64e uses chained fixups
- return ( strcmp(archName(), "arm64e") == 0 );
-#else
- return false;
-#endif
-}
-
-uint64_t MachOFile::read_uleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end)
-{
- uint64_t result = 0;
- int bit = 0;
- do {
- if ( p == end ) {
- diag.error("malformed uleb128");
- break;
- }
- uint64_t slice = *p & 0x7f;
-
- if ( bit > 63 ) {
- diag.error("uleb128 too big for uint64");
- break;
- }
- else {
- result |= (slice << bit);
- bit += 7;
- }
- }
- while (*p++ & 0x80);
- return result;
-}
-
-
-int64_t MachOFile::read_sleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end)
-{
- int64_t result = 0;
- int bit = 0;
- uint8_t byte = 0;
- do {
- if ( p == end ) {
- diag.error("malformed sleb128");
- break;
- }
- byte = *p++;
- result |= (((int64_t)(byte & 0x7f)) << bit);
- bit += 7;
- } while (byte & 0x80);
- // sign extend negative numbers
- if ( (byte & 0x40) != 0 )
- result |= (~0ULL) << bit;
- return result;
-}
-
-
-} // namespace dyld3
-
-
-
-
-