-/*
- * 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 <stdint.h>
-#include <string.h>
-#include <assert.h>
-#include <uuid/uuid.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/uio.h>
-#include <sys/param.h>
-#include <sys/sysctl.h>
-#include <sys/resource.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <rootless.h>
-#include <dirent.h>
-#include <mach/mach.h>
-#include <mach/machine.h>
-#include <mach-o/loader.h>
-#include <mach-o/nlist.h>
-#include <mach-o/fat.h>
-#include <mach-o/reloc.h>
-#include <mach-o/dyld_priv.h>
-#include <CommonCrypto/CommonDigest.h>
-
-#if !DYLD_IN_PROCESS
-#include <dlfcn.h>
-#endif
-
-#include "MachOParser.h"
-#include "Logging.h"
-#include "CodeSigningTypes.h"
-#include "DyldSharedCache.h"
-#include "Trie.hpp"
-
-#if DYLD_IN_PROCESS
- #include "APIs.h"
-#else
- #include "StringUtils.h"
-#endif
-
-
-
-#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 LC_BUILD_VERSION
- #define LC_BUILD_VERSION 0x32 /* build for platform min OS version */
-
- /*
- * The build_version_command contains the min OS version on which this
- * binary was built to run for its platform. The list of known platforms and
- * tool values following it.
- */
- struct build_version_command {
- uint32_t cmd; /* LC_BUILD_VERSION */
- uint32_t cmdsize; /* sizeof(struct build_version_command) plus */
- /* ntools * sizeof(struct build_tool_version) */
- uint32_t platform; /* platform */
- uint32_t minos; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
- uint32_t sdk; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
- uint32_t ntools; /* number of tool entries following this */
- };
-
- struct build_tool_version {
- uint32_t tool; /* enum for the tool */
- uint32_t version; /* version number of the tool */
- };
-
- /* Known values for the platform field above. */
- #define PLATFORM_MACOS 1
- #define PLATFORM_IOS 2
- #define PLATFORM_TVOS 3
- #define PLATFORM_WATCHOS 4
- #define PLATFORM_BRIDGEOS 5
-
- /* Known values for the tool field above. */
- #define TOOL_CLANG 1
- #define TOOL_SWIFT 2
- #define TOOL_LD 3
-#endif
-
-
-namespace dyld3 {
-
-
-bool FatUtil::isFatFile(const void* fileStart)
-{
- const fat_header* fileStartAsFat = (fat_header*)fileStart;
- return ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) );
-}
-
-/// Returns true if (addLHS + addRHS) > b, or if the add overflowed
-template<typename T>
-static bool greaterThanAddOrOverflow(uint32_t addLHS, uint32_t addRHS, T b) {
- return (addLHS > b) || (addRHS > (b-addLHS));
-}
-
-/// Returns true if (addLHS + addRHS) > b, or if the add overflowed
-template<typename T>
-static bool greaterThanAddOrOverflow(uint64_t addLHS, uint64_t addRHS, T b) {
- return (addLHS > b) || (addRHS > (b-addLHS));
-}
-
-void FatUtil::forEachSlice(Diagnostics& diag, const void* fileContent, size_t fileLen, void (^callback)(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSize, bool& stop))
-{
- const fat_header* fh = (fat_header*)fileContent;
- if ( fh->magic != OSSwapBigToHostInt32(FAT_MAGIC) ) {
- diag.error("not a fat file");
- return;
- }
-
- if ( OSSwapBigToHostInt32(fh->nfat_arch) > ((4096 - sizeof(fat_header)) / sizeof(fat_arch)) ) {
- diag.error("fat header too large: %u entries", OSSwapBigToHostInt32(fh->nfat_arch));
- }
- const fat_arch* const archs = (fat_arch*)(((char*)fh)+sizeof(fat_header));
- bool stop = false;
- for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->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*)fileContent+offset, len, stop);
- if ( stop )
- break;
- }
-}
-
-#if !DYLD_IN_PROCESS
-bool FatUtil::isFatFileWithSlice(Diagnostics& diag, const void* fileContent, size_t fileLen, const std::string& archName, size_t& sliceOffset, size_t& sliceLen, bool& missingSlice)
-{
- missingSlice = false;
- if ( !isFatFile(fileContent) )
- return false;
-
- __block bool found = false;
- forEachSlice(diag, fileContent, fileLen, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSize, bool& stop) {
- std::string sliceArchName = MachOParser::archName(sliceCpuType, sliceCpuSubType);
- if ( sliceArchName == archName ) {
- sliceOffset = (char*)sliceStart - (char*)fileContent;
- 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 && (archName == "x86_64h") )
- return isFatFileWithSlice(diag, fileContent, fileLen, "x86_64", sliceOffset, sliceLen, missingSlice);
-
- return found;
-}
-
-#endif
-
-MachOParser::MachOParser(const mach_header* mh, bool dyldCacheIsRaw)
-{
-#if DYLD_IN_PROCESS
- // assume all in-process mach_headers are real loaded images
- _data = (long)mh;
-#else
- if (mh == nullptr)
- return;
- _data = (long)mh;
- if ( (mh->flags & 0x80000000) == 0 ) {
- // asssume out-of-process mach_header not in a dyld cache are raw mapped files
- _data |= 1;
- }
- // out-of-process mach_header in a dyld cache are not raw, but cache may be raw
- if ( dyldCacheIsRaw )
- _data |= 2;
-#endif
-}
-
-const mach_header* MachOParser::header() const
-{
- return (mach_header*)(_data & -4);
-}
-
-// "raw" means the whole mach-o file was mapped as one contiguous region
-// not-raw means the the mach-o file was mapped like dyld does - with zero fill expansion
-bool MachOParser::isRaw() const
-{
- return (_data & 1);
-}
-
-// A raw dyld cache is when the whole dyld cache file is mapped in one contiguous region
-// not-raw manes the dyld cache was mapped as it is at runtime with padding between regions
-bool MachOParser::inRawCache() const
-{
- return (_data & 2);
-}
-
-uint32_t MachOParser::fileType() const
-{
- return header()->filetype;
-}
-
-bool MachOParser::inDyldCache() const
-{
- return (header()->flags & 0x80000000);
-}
-
-bool MachOParser::hasThreadLocalVariables() const
-{
- return (header()->flags & MH_HAS_TLV_DESCRIPTORS);
-}
-
-Platform MachOParser::platform() const
-{
- Platform platform;
- uint32_t minOS;
- uint32_t sdk;
- if ( getPlatformAndVersion(&platform, &minOS, &sdk) )
- return platform;
-
- // old binary with no explict load command to mark platform, look at arch
- switch ( header()->cputype ) {
- case CPU_TYPE_X86_64:
- case CPU_TYPE_I386:
- return Platform::macOS;
- case CPU_TYPE_ARM64:
- case CPU_TYPE_ARM:
- return Platform::iOS;
- }
- return Platform::macOS;
-}
-
-
-#if !DYLD_IN_PROCESS
-
-const MachOParser::ArchInfo MachOParser::_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 },
- { "arm64e", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_E },
- { "armv7k", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K },
- { "armv7s", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S },
- { "armv7", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7 }
-};
-
-bool MachOParser::isValidMachO(Diagnostics& diag, const std::string& archName, Platform platform, const void* fileContent, size_t fileLength, const std::string& pathOpened, bool ignoreMainExecutables)
-{
- // must start with mach-o magic value
- const mach_header* mh = (const mach_header*)fileContent;
- if ( (mh->magic != MH_MAGIC) && (mh->magic != MH_MAGIC_64) ) {
- diag.warning("could not use '%s' because it is not a mach-o file", pathOpened.c_str());
- return false;
- }
-
- // must match requested architecture if specified
- if (!archName.empty() && !isArch(mh, archName)) {
- // except when looking for x86_64h, fallback to x86_64
- if ( (archName != "x86_64h") || !isArch(mh, "x86_64") ) {
- diag.warning("could not use '%s' because it does not contain required architecture %s", pathOpened.c_str(), archName.c_str());
- return false;
- }
- }
-
- // must be a filetype dyld can load
- switch ( mh->filetype ) {
- case MH_EXECUTE:
- if ( ignoreMainExecutables )
- return false;
- break;
- case MH_DYLIB:
- case MH_BUNDLE:
- break;
- default:
- diag.warning("could not use '%s' because it is not a dylib, bundle, or executable", pathOpened.c_str());
- return false;
- }
-
- // must be from a file - not in the dyld shared cache
- if ( mh->flags & 0x80000000 ) {
- diag.warning("could not use '%s' because the high bit of mach_header flags is reserved for images in dyld cache", pathOpened.c_str());
- return false;
- }
-
- // validate load commands structure
- MachOParser parser(mh);
- if ( !parser.validLoadCommands(diag, fileLength) )
- return false;
-
- // must match requested platform
- if ( parser.platform() != platform ) {
- diag.warning("could not use '%s' because it was built for a different platform", pathOpened.c_str());
- return false;
- }
-
- // cannot be a static executable
- if ( (mh->filetype == MH_EXECUTE) && !parser.isDynamicExecutable() ) {
- diag.warning("could not use '%s' because it is a static executable", pathOpened.c_str());
- return false;
- }
-
- // validate dylib loads
- if ( !parser.validEmbeddedPaths(diag) )
- return false;
-
- // validate segments
- if ( !parser.validSegments(diag, fileLength) )
- return false;
-
- // validate LINKEDIT layout
- if ( !parser.validLinkeditLayout(diag) )
- return false;
-
- return true;
-}
-
-
-bool MachOParser::validLoadCommands(Diagnostics& diag, size_t fileLen)
-{
- // check load command don't exceed file length
- if ( header()->sizeofcmds + sizeof(mach_header_64) > fileLen ) {
- diag.warning("load commands exceed length of file");
- return false;
- }
- // walk all load commands and sanity check them
- Diagnostics walkDiag;
- LinkEditInfo lePointers;
- getLinkEditLoadCommands(walkDiag, lePointers);
- if ( walkDiag.hasError() ) {
- diag.warning("%s", walkDiag.errorMessage().c_str());
- return false;
- }
-
- // check load commands fit in TEXT segment
- __block bool overflowText = false;
- forEachSegment(^(const char* segName, uint32_t segFileOffset, uint32_t segFileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
- if ( strcmp(segName, "__TEXT") == 0 ) {
- if ( header()->sizeofcmds + sizeof(mach_header_64) > segFileSize ) {
- diag.warning("load commands exceed length of __TEXT segment");
- overflowText = true;
- }
- stop = true;
- }
- });
- if ( overflowText )
- return false;
-
- return true;
-}
-
-bool MachOParser::validEmbeddedPaths(Diagnostics& diag)
-{
- __block int index = 1;
- __block bool allGood = true;
- __block bool foundInstallName = false;
- forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
- const dylib_command* dylibCmd;
- const rpath_command* rpathCmd;
- switch ( cmd->cmd ) {
- case LC_ID_DYLIB:
- foundInstallName = true;
- // fall through
- case LC_LOAD_DYLIB:
- case LC_LOAD_WEAK_DYLIB:
- case LC_REEXPORT_DYLIB:
- case LC_LOAD_UPWARD_DYLIB:
- dylibCmd = (dylib_command*)cmd;
- if ( dylibCmd->dylib.name.offset > cmd->cmdsize ) {
- diag.warning("load command #%d name offset (%u) outside its size (%u)", index, dylibCmd->dylib.name.offset, cmd->cmdsize);
- stop = true;
- allGood = false;
- }
- else {
- bool foundEnd = false;
- const char* start = (char*)dylibCmd + dylibCmd->dylib.name.offset;
- const char* end = (char*)dylibCmd + cmd->cmdsize;
- for (const char* s=start; s < end; ++s) {
- if ( *s == '\0' ) {
- foundEnd = true;
- break;
- }
- }
- if ( !foundEnd ) {
- diag.warning("load command #%d string extends beyond end of load command", index);
- stop = true;
- allGood = false;
- }
- }
- break;
- case LC_RPATH:
- rpathCmd = (rpath_command*)cmd;
- if ( rpathCmd->path.offset > cmd->cmdsize ) {
- diag.warning("load command #%d path offset (%u) outside its size (%u)", index, rpathCmd->path.offset, cmd->cmdsize);
- stop = true;
- allGood = false;
- }
- else {
- bool foundEnd = false;
- const char* start = (char*)rpathCmd + rpathCmd->path.offset;
- const char* end = (char*)rpathCmd + cmd->cmdsize;
- for (const char* s=start; s < end; ++s) {
- if ( *s == '\0' ) {
- foundEnd = true;
- break;
- }
- }
- if ( !foundEnd ) {
- diag.warning("load command #%d string extends beyond end of load command", index);
- stop = true;
- allGood = false;
- }
- }
- break;
- }
- ++index;
- });
-
- if ( header()->filetype == MH_DYLIB ) {
- if ( !foundInstallName ) {
- diag.warning("MH_DYLIB is missing LC_ID_DYLIB");
- allGood = false;
- }
- }
- else {
- if ( foundInstallName ) {
- diag.warning("LC_ID_DYLIB found in non-MH_DYLIB");
- allGood = false;
- }
- }
-
- return allGood;
-}
-
-bool MachOParser::validSegments(Diagnostics& diag, size_t fileLen)
-{
- // check segment load command size
- __block bool badSegmentLoadCommand = false;
- forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
- if ( cmd->cmd == LC_SEGMENT_64 ) {
- const segment_command_64* seg = (segment_command_64*)cmd;
- int32_t sectionsSpace = cmd->cmdsize - sizeof(segment_command_64);
- if ( sectionsSpace < 0 ) {
- diag.warning("load command size too small for LC_SEGMENT_64");
- badSegmentLoadCommand = true;
- stop = true;
- }
- else if ( (sectionsSpace % sizeof(section_64)) != 0 ) {
- diag.warning("segment load command size 0x%X will not fit whole number of sections", cmd->cmdsize);
- badSegmentLoadCommand = true;
- stop = true;
- }
- else if ( sectionsSpace != (seg->nsects * sizeof(section_64)) ) {
- diag.warning("load command size 0x%X does not match nsects %d", cmd->cmdsize, seg->nsects);
- badSegmentLoadCommand = true;
- stop = true;
- } else if (greaterThanAddOrOverflow(seg->fileoff, seg->filesize, fileLen)) {
- diag.warning("segment load command content extends beyond end of file");
- badSegmentLoadCommand = true;
- stop = true;
- } else if ( (seg->filesize > seg->vmsize) && ((seg->vmsize != 0) || ((seg->flags & SG_NORELOC) == 0)) ) {
- // <rdar://problem/19986776> dyld should support non-allocatable __LLVM segment
- diag.warning("segment filesize exceeds vmsize");
- badSegmentLoadCommand = true;
- stop = true;
- }
- }
- else if ( cmd->cmd == LC_SEGMENT ) {
- const segment_command* seg = (segment_command*)cmd;
- int32_t sectionsSpace = cmd->cmdsize - sizeof(segment_command);
- if ( sectionsSpace < 0 ) {
- diag.warning("load command size too small for LC_SEGMENT");
- badSegmentLoadCommand = true;
- stop = true;
- }
- else if ( (sectionsSpace % sizeof(section)) != 0 ) {
- diag.warning("segment load command size 0x%X will not fit whole number of sections", cmd->cmdsize);
- badSegmentLoadCommand = true;
- stop = true;
- }
- else if ( sectionsSpace != (seg->nsects * sizeof(section)) ) {
- diag.warning("load command size 0x%X does not match nsects %d", cmd->cmdsize, seg->nsects);
- badSegmentLoadCommand = true;
- stop = true;
- } else if ( (seg->filesize > seg->vmsize) && ((seg->vmsize != 0) || ((seg->flags & SG_NORELOC) == 0)) ) {
- // <rdar://problem/19986776> dyld should support non-allocatable __LLVM segment
- diag.warning("segment filesize exceeds vmsize");
- badSegmentLoadCommand = true;
- stop = true;
- }
- }
- });
- if ( badSegmentLoadCommand )
- return false;
-
- // check mapping permissions of segments
- __block bool badPermissions = false;
- __block bool badSize = false;
- __block bool hasTEXT = false;
- __block bool hasLINKEDIT = false;
- forEachSegment(^(const char* segName, uint32_t segFileOffset, uint32_t segFileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
- if ( strcmp(segName, "__TEXT") == 0 ) {
- if ( protections != (VM_PROT_READ|VM_PROT_EXECUTE) ) {
- diag.warning("__TEXT segment permissions is not 'r-x'");
- badPermissions = true;
- stop = true;
- }
- hasTEXT = true;
- }
- else if ( strcmp(segName, "__LINKEDIT") == 0 ) {
- if ( protections != VM_PROT_READ ) {
- diag.warning("__LINKEDIT segment permissions is not 'r--'");
- badPermissions = true;
- stop = true;
- }
- hasLINKEDIT = true;
- }
- else if ( (protections & 0xFFFFFFF8) != 0 ) {
- diag.warning("%s segment permissions has invalid bits set", segName);
- badPermissions = true;
- stop = true;
- }
- if (greaterThanAddOrOverflow(segFileOffset, segFileSize, fileLen)) {
- diag.warning("%s segment content extends beyond end of file", segName);
- badSize = true;
- stop = true;
- }
- if ( is64() ) {
- if ( vmAddr+vmSize < vmAddr ) {
- diag.warning("%s segment vm range wraps", segName);
- badSize = true;
- stop = true;
- }
- }
- else {
- if ( (uint32_t)(vmAddr+vmSize) < (uint32_t)(vmAddr) ) {
- diag.warning("%s segment vm range wraps", segName);
- badSize = true;
- stop = true;
- }
- }
- });
- if ( badPermissions || badSize )
- return false;
- if ( !hasTEXT ) {
- diag.warning("missing __TEXT segment");
- return false;
- }
- if ( !hasLINKEDIT ) {
- diag.warning("missing __LINKEDIT segment");
- return false;
- }
-
- // check for overlapping segments
- __block bool badSegments = false;
- forEachSegment(^(const char* seg1Name, uint32_t seg1FileOffset, uint32_t seg1FileSize, uint64_t seg1vmAddr, uint64_t seg1vmSize, uint8_t seg1Protections, uint32_t seg1Index, uint64_t seg1SizeOfSections, uint8_t seg1Align, bool& stop1) {
- uint64_t seg1vmEnd = seg1vmAddr + seg1vmSize;
- uint32_t seg1FileEnd = seg1FileOffset + seg1FileSize;
- forEachSegment(^(const char* seg2Name, uint32_t seg2FileOffset, uint32_t seg2FileSize, uint64_t seg2vmAddr, uint64_t seg2vmSize, uint8_t seg2Protections, uint32_t seg2Index, uint64_t seg2SizeOfSections, uint8_t seg2Align, bool& stop2) {
- if ( seg1Index == seg2Index )
- return;
- uint64_t seg2vmEnd = seg2vmAddr + seg2vmSize;
- uint32_t seg2FileEnd = seg2FileOffset + seg2FileSize;
- if ( ((seg2vmAddr <= seg1vmAddr) && (seg2vmEnd > seg1vmAddr) && (seg1vmEnd > seg1vmAddr)) || ((seg2vmAddr >= seg1vmAddr) && (seg2vmAddr < seg1vmEnd) && (seg2vmEnd > seg2vmAddr)) ) {
- diag.warning("segment %s vm range overlaps segment %s", seg1Name, seg2Name);
- badSegments = true;
- stop1 = true;
- stop2 = true;
- }
- if ( ((seg2FileOffset <= seg1FileOffset) && (seg2FileEnd > seg1FileOffset) && (seg1FileEnd > seg1FileOffset)) || ((seg2FileOffset >= seg1FileOffset) && (seg2FileOffset < seg1FileEnd) && (seg2FileEnd > seg2FileOffset)) ) {
- diag.warning("segment %s file content overlaps segment %s", seg1Name, seg2Name);
- badSegments = true;
- stop1 = true;
- stop2 = true;
- }
- // check for out of order segments
- if ( (seg1Index < seg2Index) && !stop1 ) {
- if ( (seg1vmAddr > seg2vmAddr) || ((seg1FileOffset > seg2FileOffset) && (seg1FileOffset != 0) && (seg2FileOffset != 0)) ){
- diag.warning("segment load commands out of order with respect to layout for %s and %s", seg1Name, seg2Name);
- badSegments = true;
- stop1 = true;
- stop2 = true;
- }
- }
- });
- });
- if ( badSegments )
- return false;
-
- // check sections are within segment
- __block bool badSections = false;
- forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
- if ( cmd->cmd == LC_SEGMENT_64 ) {
- const segment_command_64* seg = (segment_command_64*)cmd;
- const section_64* const sectionsStart = (section_64*)((char*)seg + sizeof(struct segment_command_64));
- const section_64* const sectionsEnd = §ionsStart[seg->nsects];
- for (const section_64* sect=sectionsStart; (sect < sectionsEnd); ++sect) {
- if ( (int64_t)(sect->size) < 0 ) {
- diag.warning("section %s size too large 0x%llX", sect->sectname, sect->size);
- badSections = true;
- }
- else if ( sect->addr < seg->vmaddr ) {
- diag.warning("section %s start address 0x%llX is before containing segment's address 0x%0llX", sect->sectname, sect->addr, seg->vmaddr);
- badSections = true;
- }
- else if ( sect->addr+sect->size > seg->vmaddr+seg->vmsize ) {
- diag.warning("section %s end address 0x%llX is beyond containing segment's end address 0x%0llX", sect->sectname, sect->addr+sect->size, seg->vmaddr+seg->vmsize);
- badSections = true;
- }
- }
- }
- else if ( cmd->cmd == LC_SEGMENT ) {
- const segment_command* seg = (segment_command*)cmd;
- const section* const sectionsStart = (section*)((char*)seg + sizeof(struct segment_command));
- const section* const sectionsEnd = §ionsStart[seg->nsects];
- for (const section* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) {
- if ( (int64_t)(sect->size) < 0 ) {
- diag.warning("section %s size too large 0x%X", sect->sectname, sect->size);
- badSections = true;
- }
- else if ( sect->addr < seg->vmaddr ) {
- diag.warning("section %s start address 0x%X is before containing segment's address 0x%0X", sect->sectname, sect->addr, seg->vmaddr);
- badSections = true;
- }
- else if ( sect->addr+sect->size > seg->vmaddr+seg->vmsize ) {
- diag.warning("section %s end address 0x%X is beyond containing segment's end address 0x%0X", sect->sectname, sect->addr+sect->size, seg->vmaddr+seg->vmsize);
- badSections = true;
- }
- }
- }
- });
-
- return !badSections;
-}
-
-struct LinkEditContent
-{
- const char* name;
- uint32_t stdOrder;
- uint32_t fileOffsetStart;
- uint32_t size;
-};
-
-
-
-bool MachOParser::validLinkeditLayout(Diagnostics& diag)
-{
- LinkEditInfo leInfo;
- getLinkEditPointers(diag, leInfo);
- if ( diag.hasError() )
- return false;
- const bool is64Bit = is64();
- const uint32_t pointerSize = (is64Bit ? 8 : 4);
-
- // build vector of all blobs in LINKEDIT
- std::vector<LinkEditContent> blobs;
- if ( leInfo.dyldInfo != nullptr ) {
- if ( leInfo.dyldInfo->rebase_size != 0 )
- blobs.push_back({"rebase opcodes", 1, leInfo.dyldInfo->rebase_off, leInfo.dyldInfo->rebase_size});
- if ( leInfo.dyldInfo->bind_size != 0 )
- blobs.push_back({"bind opcodes", 2, leInfo.dyldInfo->bind_off, leInfo.dyldInfo->bind_size});
- if ( leInfo.dyldInfo->weak_bind_size != 0 )
- blobs.push_back({"weak bind opcodes", 3, leInfo.dyldInfo->weak_bind_off, leInfo.dyldInfo->weak_bind_size});
- if ( leInfo.dyldInfo->lazy_bind_size != 0 )
- blobs.push_back({"lazy bind opcodes", 4, leInfo.dyldInfo->lazy_bind_off, leInfo.dyldInfo->lazy_bind_size});
- if ( leInfo.dyldInfo->export_size!= 0 )
- blobs.push_back({"exports trie", 5, leInfo.dyldInfo->export_off, leInfo.dyldInfo->export_size});
- }
- if ( leInfo.dynSymTab != nullptr ) {
- if ( leInfo.dynSymTab->nlocrel != 0 )
- blobs.push_back({"local relocations", 6, leInfo.dynSymTab->locreloff, static_cast<uint32_t>(leInfo.dynSymTab->nlocrel*sizeof(relocation_info))});
- if ( leInfo.dynSymTab->nextrel != 0 )
- blobs.push_back({"external relocations", 11, leInfo.dynSymTab->extreloff, static_cast<uint32_t>(leInfo.dynSymTab->nextrel*sizeof(relocation_info))});
- if ( leInfo.dynSymTab->nindirectsyms != 0 )
- blobs.push_back({"indirect symbol table", 12, leInfo.dynSymTab->indirectsymoff, leInfo.dynSymTab->nindirectsyms*4});
- }
- if ( leInfo.splitSegInfo != nullptr ) {
- if ( leInfo.splitSegInfo->datasize != 0 )
- blobs.push_back({"shared cache info", 6, leInfo.splitSegInfo->dataoff, leInfo.splitSegInfo->datasize});
- }
- if ( leInfo.functionStarts != nullptr ) {
- if ( leInfo.functionStarts->datasize != 0 )
- blobs.push_back({"function starts", 7, leInfo.functionStarts->dataoff, leInfo.functionStarts->datasize});
- }
- if ( leInfo.dataInCode != nullptr ) {
- if ( leInfo.dataInCode->datasize != 0 )
- blobs.push_back({"data in code", 8, leInfo.dataInCode->dataoff, leInfo.dataInCode->datasize});
- }
- if ( leInfo.symTab != nullptr ) {
- if ( leInfo.symTab->nsyms != 0 )
- blobs.push_back({"symbol table", 10, leInfo.symTab->symoff, static_cast<uint32_t>(leInfo.symTab->nsyms*(is64Bit ? sizeof(nlist_64) : sizeof(struct nlist)))});
- if ( leInfo.symTab->strsize != 0 )
- blobs.push_back({"symbol table strings", 20, leInfo.symTab->stroff, leInfo.symTab->strsize});
- }
- if ( leInfo.codeSig != nullptr ) {
- if ( leInfo.codeSig->datasize != 0 )
- blobs.push_back({"code signature", 21, leInfo.codeSig->dataoff, leInfo.codeSig->datasize});
- }
-
- // check for bad combinations
- if ( (leInfo.dyldInfo != nullptr) && (leInfo.dyldInfo->cmd == LC_DYLD_INFO_ONLY) && (leInfo.dynSymTab != nullptr) ) {
- if ( leInfo.dynSymTab->nlocrel != 0 ) {
- diag.error("malformed mach-o contains LC_DYLD_INFO_ONLY and local relocations");
- return false;
- }
- if ( leInfo.dynSymTab->nextrel != 0 ) {
- diag.error("malformed mach-o contains LC_DYLD_INFO_ONLY and external relocations");
- return false;
- }
- }
- if ( (leInfo.dyldInfo == nullptr) && (leInfo.dynSymTab == nullptr) ) {
- diag.error("malformed mach-o misssing LC_DYLD_INFO and LC_DYSYMTAB");
- return false;
- }
- if ( blobs.empty() ) {
- diag.error("malformed mach-o misssing LINKEDIT");
- return false;
- }
-
- // sort vector by file offset and error on overlaps
- std::sort(blobs.begin(), blobs.end(), [&](const LinkEditContent& a, const LinkEditContent& b) {
- return a.fileOffsetStart < b.fileOffsetStart;
- });
- uint32_t prevEnd = (uint32_t)(leInfo.layout.segments[leInfo.layout.linkeditSegIndex].fileOffset);
- const char* prevName = "start of LINKEDIT";
- for (const LinkEditContent& blob : blobs) {
- if ( blob.fileOffsetStart < prevEnd ) {
- diag.error("LINKEDIT overlap of %s and %s", prevName, blob.name);
- return false;
- }
- prevEnd = blob.fileOffsetStart + blob.size;
- prevName = blob.name;
- }
- const LinkEditContent& lastBlob = blobs.back();
- uint32_t linkeditFileEnd = (uint32_t)(leInfo.layout.segments[leInfo.layout.linkeditSegIndex].fileOffset + leInfo.layout.segments[leInfo.layout.linkeditSegIndex].fileSize);
- if (greaterThanAddOrOverflow(lastBlob.fileOffsetStart, lastBlob.size, linkeditFileEnd)) {
- diag.error("LINKEDIT content '%s' extends beyond end of segment", lastBlob.name);
- return false;
- }
-
- // sort vector by order and warn on non standard order or mis-alignment
- std::sort(blobs.begin(), blobs.end(), [&](const LinkEditContent& a, const LinkEditContent& b) {
- return a.stdOrder < b.stdOrder;
- });
- prevEnd = (uint32_t)(leInfo.layout.segments[leInfo.layout.linkeditSegIndex].fileOffset);
- prevName = "start of LINKEDIT";
- for (const LinkEditContent& blob : blobs) {
- if ( ((blob.fileOffsetStart & (pointerSize-1)) != 0) && (blob.stdOrder != 20) ) // ok for "symbol table strings" to be mis-aligned
- diag.warning("mis-aligned LINKEDIT content '%s'", blob.name);
- if ( blob.fileOffsetStart < prevEnd ) {
- diag.warning("LINKEDIT out of order %s", blob.name);
- }
- prevEnd = blob.fileOffsetStart;
- prevName = blob.name;
- }
-
- // Check for invalid symbol table sizes
- if ( leInfo.symTab != nullptr ) {
- if ( leInfo.symTab->nsyms > 0x10000000 ) {
- diag.error("malformed mach-o image: symbol table too large");
- return false;
- }
- if ( leInfo.dynSymTab != nullptr ) {
- // validate indirect symbol table
- if ( leInfo.dynSymTab->nindirectsyms != 0 ) {
- if ( leInfo.dynSymTab->nindirectsyms > 0x10000000 ) {
- diag.error("malformed mach-o image: indirect symbol table too large");
- return false;
- }
- }
- if ( (leInfo.dynSymTab->nlocalsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->ilocalsym > leInfo.symTab->nsyms) ) {
- diag.error("malformed mach-o image: indirect symbol table local symbol count exceeds total symbols");
- return false;
- }
- if ( leInfo.dynSymTab->ilocalsym + leInfo.dynSymTab->nlocalsym < leInfo.dynSymTab->ilocalsym ) {
- diag.error("malformed mach-o image: indirect symbol table local symbol count wraps");
- return false;
- }
- if ( (leInfo.dynSymTab->nextdefsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->iextdefsym > leInfo.symTab->nsyms) ) {
- diag.error("malformed mach-o image: indirect symbol table extern symbol count exceeds total symbols");
- return false;
- }
- if ( leInfo.dynSymTab->iextdefsym + leInfo.dynSymTab->nextdefsym < leInfo.dynSymTab->iextdefsym ) {
- diag.error("malformed mach-o image: indirect symbol table extern symbol count wraps");
- return false;
- }
- if ( (leInfo.dynSymTab->nundefsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->iundefsym > leInfo.symTab->nsyms) ) {
- diag.error("malformed mach-o image: indirect symbol table undefined symbol count exceeds total symbols");
- return false;
- }
- if ( leInfo.dynSymTab->iundefsym + leInfo.dynSymTab->nundefsym < leInfo.dynSymTab->iundefsym ) {
- diag.error("malformed mach-o image: indirect symbol table undefined symbol count wraps");
- return false;
- }
- }
- }
-
- return true;
-}
-
-bool MachOParser::isArch(const mach_header* mh, const std::string& archName)
-{
- for (const ArchInfo& info : _s_archInfos) {
- if ( archName == info.name ) {
- return ( (mh->cputype == info.cputype) && ((mh->cpusubtype & ~CPU_SUBTYPE_MASK) == info.cpusubtype) );
- }
- }
- return false;
-}
-
-
-std::string MachOParser::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 MachOParser::cpuTypeFromArchName(const std::string& archName)
-{
- for (const ArchInfo& info : _s_archInfos) {
- if ( archName == info.name ) {
- return info.cputype;
- }
- }
- return 0;
-}
-
-uint32_t MachOParser::cpuSubtypeFromArchName(const std::string& archName)
-{
- for (const ArchInfo& info : _s_archInfos) {
- if ( archName == info.name ) {
- return info.cpusubtype;
- }
- }
- return 0;
-}
-
-std::string MachOParser::archName() const
-{
- return archName(header()->cputype, header()->cpusubtype);
-}
-
-std::string MachOParser::platformName(Platform platform)
-{
- switch ( platform ) {
- case Platform::unknown:
- return "unknown";
- case Platform::macOS:
- return "macOS";
- case Platform::iOS:
- return "iOS";
- case Platform::tvOS:
- return "tvOS";
- case Platform::watchOS:
- return "watchOS";
- case Platform::bridgeOS:
- return "bridgeOS";
- }
- return "unknown platform";
-}
-
-std::string MachOParser::versionString(uint32_t packedVersion)
-{
- char buff[64];
- sprintf(buff, "%d.%d.%d", (packedVersion >> 16), ((packedVersion >> 8) & 0xFF), (packedVersion & 0xFF));
- return buff;
-}
-
-#else
-
-bool MachOParser::isMachO(Diagnostics& diag, const void* fileContent, size_t mappedLength)
-{
- // sanity check length
- if ( mappedLength < 4096 ) {
- diag.error("file too short");
- return false;
- }
-
- // must start with mach-o magic value
- const mach_header* mh = (const mach_header*)fileContent;
-#if __LP64__
- const uint32_t requiredMagic = MH_MAGIC_64;
-#else
- const uint32_t requiredMagic = MH_MAGIC;
-#endif
- if ( mh->magic != requiredMagic ) {
- diag.error("not a mach-o file");
- return false;
- }
-
-#if __x86_64__
- const uint32_t requiredCPU = CPU_TYPE_X86_64;
-#elif __i386__
- const uint32_t requiredCPU = CPU_TYPE_I386;
-#elif __arm__
- const uint32_t requiredCPU = CPU_TYPE_ARM;
-#elif __arm64__
- const uint32_t requiredCPU = CPU_TYPE_ARM64;
-#else
- #error unsupported architecture
-#endif
- if ( mh->cputype != requiredCPU ) {
- diag.error("wrong cpu type");
- return false;
- }
-
- return true;
-}
-
-bool MachOParser::wellFormedMachHeaderAndLoadCommands(const mach_header* mh)
-{
- const load_command* startCmds = nullptr;
- if ( mh->magic == MH_MAGIC_64 )
- startCmds = (load_command*)((char *)mh + sizeof(mach_header_64));
- else if ( mh->magic == MH_MAGIC )
- startCmds = (load_command*)((char *)mh + sizeof(mach_header));
- else
- return false; // not a mach-o file, or wrong endianness
-
- const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds);
- const load_command* cmd = startCmds;
- for(uint32_t i = 0; i < mh->ncmds; ++i) {
- const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize);
- if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds)) {
- return false;
- }
- cmd = nextCmd;
- }
- return true;
-}
-
-#endif
-
-Platform MachOParser::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
-}
-
-
-bool MachOParser::valid(Diagnostics& diag)
-{
-#if DYLD_IN_PROCESS
- // only images loaded by dyld to be parsed
- const mach_header* inImage = dyld3::dyld_image_header_containing_address(header());
- if ( inImage != header() ) {
- diag.error("only dyld loaded images can be parsed by MachOParser");
- return false;
- }
-#else
-
-#endif
- return true;
-}
-
-
-void MachOParser::forEachLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& stop)) const
-{
- bool stop = false;
- const load_command* startCmds = nullptr;
- if ( header()->magic == MH_MAGIC_64 )
- startCmds = (load_command*)((char *)header() + sizeof(mach_header_64));
- else if ( header()->magic == MH_MAGIC )
- startCmds = (load_command*)((char *)header() + sizeof(mach_header));
- else {
- diag.error("file does not start with MH_MAGIC[_64]");
- return; // not a mach-o file, or wrong endianness
- }
- const load_command* const cmdsEnd = (load_command*)((char*)startCmds + header()->sizeofcmds);
- const load_command* cmd = startCmds;
- for(uint32_t i = 0; i < header()->ncmds; ++i) {
- const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize);
- if ( cmd->cmdsize < 8 ) {
- diag.error("malformed load command #%d, size too small %d", i, cmd->cmdsize);
- return;
- }
- if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
- diag.error("malformed load command #%d, size too large 0x%X", i, cmd->cmdsize);
- return;
- }
- callback(cmd, stop);
- if ( stop )
- return;
- cmd = nextCmd;
- }
-}
-
-UUID MachOParser::uuid() const
-{
- uuid_t uuid;
- getUuid(uuid);
- return uuid;
-}
-
-bool MachOParser::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;
-}
-
-uint64_t MachOParser::preferredLoadAddress() const
-{
- __block uint64_t result = 0;
- forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
- if ( strcmp(segName, "__TEXT") == 0 ) {
- result = vmAddr;
- stop = true;
- }
- });
- return result;
-}
-
-bool MachOParser::getPlatformAndVersion(Platform* platform, uint32_t* minOS, uint32_t* sdk) const
-{
- Diagnostics diag;
- __block bool found = false;
- forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
- const version_min_command* versCmd;
- switch ( cmd->cmd ) {
- case LC_VERSION_MIN_IPHONEOS:
- versCmd = (version_min_command*)cmd;
- *platform = Platform::iOS;
- *minOS = versCmd->version;
- *sdk = versCmd->sdk;
- found = true;
- stop = true;
- break;
- case LC_VERSION_MIN_MACOSX:
- versCmd = (version_min_command*)cmd;
- *platform = Platform::macOS;
- *minOS = versCmd->version;
- *sdk = versCmd->sdk;
- found = true;
- stop = true;
- break;
- case LC_VERSION_MIN_TVOS:
- versCmd = (version_min_command*)cmd;
- *platform = Platform::tvOS;
- *minOS = versCmd->version;
- *sdk = versCmd->sdk;
- found = true;
- stop = true;
- break;
- case LC_VERSION_MIN_WATCHOS:
- versCmd = (version_min_command*)cmd;
- *platform = Platform::watchOS;
- *minOS = versCmd->version;
- *sdk = versCmd->sdk;
- found = true;
- stop = true;
- break;
- case LC_BUILD_VERSION: {
- const build_version_command* buildCmd = (build_version_command *)cmd;
- *minOS = buildCmd->minos;
- *sdk = buildCmd->sdk;
-
- switch(buildCmd->platform) {
- /* Known values for the platform field above. */
- case PLATFORM_MACOS:
- *platform = Platform::macOS;
- break;
- case PLATFORM_IOS:
- *platform = Platform::iOS;
- break;
- case PLATFORM_TVOS:
- *platform = Platform::tvOS;
- break;
- case PLATFORM_WATCHOS:
- *platform = Platform::watchOS;
- break;
- case PLATFORM_BRIDGEOS:
- *platform = Platform::bridgeOS;
- break;
- }
- found = true;
- stop = true;
- } break;
- }
- });
- diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
- return found;
-}
-
-
-bool MachOParser::isSimulatorBinary() const
-{
- Platform platform;
- uint32_t minOS;
- uint32_t sdk;
- switch ( header()->cputype ) {
- case CPU_TYPE_I386:
- case CPU_TYPE_X86_64:
- if ( getPlatformAndVersion(&platform, &minOS, &sdk) ) {
- return (platform != Platform::macOS);
- }
- break;
- }
- return false;
-}
-
-
-bool MachOParser::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;
-}
-
-const char* MachOParser::installName() const
-{
- assert(header()->filetype == MH_DYLIB);
- const char* result;
- uint32_t ignoreVersion;
- assert(getDylibInstallName(&result, &ignoreVersion, &ignoreVersion));
- return result;
-}
-
-
-uint32_t MachOParser::dependentDylibCount() const
-{
- __block uint32_t count = 0;
- forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
- ++count;
- });
- return count;
-}
-
-const char* MachOParser::dependentDylibLoadPath(uint32_t depIndex) const
-{
- __block const char* foundLoadPath = nullptr;
- __block uint32_t curDepIndex = 0;
- forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
- if ( curDepIndex == depIndex ) {
- foundLoadPath = loadPath;
- stop = true;
- }
- ++curDepIndex;
- });
- return foundLoadPath;
-}
-
-
-void MachOParser::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;
- assert(dylibCmd->dylib.name.offset < cmd->cmdsize);
- 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 MachOParser::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
-}
-
-/*
- struct LayoutInfo {
-#if DYLD_IN_PROCESS
- uintptr_t slide;
- uintptr_t textUnslidVMAddr;
- uintptr_t linkeditUnslidVMAddr;
- uint32_t linkeditFileOffset;
-#else
- uint32_t segmentCount;
- uint32_t linkeditSegIndex;
- struct {
- uint64_t mappingOffset;
- uint64_t fileOffset;
- uint64_t segUnslidAddress;
- uint64_t segSize;
- } segments[16];
-#endif
- };
-*/
-
-#if !DYLD_IN_PROCESS
-const uint8_t* MachOParser::getContentForVMAddr(const LayoutInfo& info, uint64_t addr) const
-{
- for (uint32_t i=0; i < info.segmentCount; ++i) {
- if ( (addr >= info.segments[i].segUnslidAddress) && (addr < (info.segments[i].segUnslidAddress+info.segments[i].segSize)) )
- return (uint8_t*)header() + info.segments[i].mappingOffset + (addr - info.segments[i].segUnslidAddress);
- }
- // value is outside this image. could be pointer into another image
- if ( inDyldCache() ) {
- return (uint8_t*)header() + info.segments[0].mappingOffset + (addr - info.segments[0].segUnslidAddress);
- }
- assert(0 && "address not found in segment");
- return nullptr;
-}
-#endif
-
-const uint8_t* MachOParser::getLinkEditContent(const LayoutInfo& info, uint32_t fileOffset) const
-{
-#if DYLD_IN_PROCESS
- uint32_t offsetInLinkedit = fileOffset - info.linkeditFileOffset;
- uintptr_t linkeditStartAddr = info.linkeditUnslidVMAddr + info.slide;
- return (uint8_t*)(linkeditStartAddr + offsetInLinkedit);
-#else
- uint32_t offsetInLinkedit = fileOffset - (uint32_t)(info.segments[info.linkeditSegIndex].fileOffset);
- const uint8_t* linkeditStart = (uint8_t*)header() + info.segments[info.linkeditSegIndex].mappingOffset;
- return linkeditStart + offsetInLinkedit;
-#endif
-}
-
-
-void MachOParser::getLayoutInfo(LayoutInfo& result) const
-{
-#if DYLD_IN_PROCESS
- // image loaded by dyld, just record the addr and file offset of TEXT and LINKEDIT segments
- result.slide = getSlide();
- forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
- if ( strcmp(segName, "__TEXT") == 0 ) {
- result.textUnslidVMAddr = (uintptr_t)vmAddr;
- }
- else if ( strcmp(segName, "__LINKEDIT") == 0 ) {
- result.linkeditUnslidVMAddr = (uintptr_t)vmAddr;
- result.linkeditFileOffset = fileOffset;
- }
- });
-#else
- bool inCache = inDyldCache();
- bool intel32 = (header()->cputype == CPU_TYPE_I386);
- result.segmentCount = 0;
- result.linkeditSegIndex = 0xFFFFFFFF;
- __block uint64_t textSegAddr = 0;
- __block uint64_t textSegFileOffset = 0;
- forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
- auto& segInfo = result.segments[result.segmentCount];
- if ( strcmp(segName, "__TEXT") == 0 ) {
- textSegAddr = vmAddr;
- textSegFileOffset = fileOffset;
- }
- __block bool textRelocsAllowed = false;
- if ( intel32 ) {
- forEachSection(^(const char* curSegName, uint32_t segIndex, uint64_t segVMAddr, const char* sectionName, uint32_t sectFlags,
- uint64_t sectAddr, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& sectStop) {
- if ( strcmp(curSegName, segName) == 0 ) {
- if ( sectFlags & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC) ) {
- textRelocsAllowed = true;
- sectStop = true;
- }
- }
- });
- }
- if ( inCache ) {
- if ( inRawCache() ) {
- // whole cache file mapped somewhere (padding not expanded)
- // vmaddrs are useless. only file offset make sense
- segInfo.mappingOffset = fileOffset - textSegFileOffset;
- }
- else {
- // cache file was loaded by dyld into shared region
- // vmaddrs of segments are correct except for ASLR slide
- segInfo.mappingOffset = vmAddr - textSegAddr;
- }
- }
- else {
- // individual mach-o file mapped in one region, so mappingOffset == fileOffset
- segInfo.mappingOffset = fileOffset;
- }
- segInfo.fileOffset = fileOffset;
- segInfo.fileSize = fileSize;
- segInfo.segUnslidAddress = vmAddr;
- segInfo.segSize = vmSize;
- segInfo.writable = ((protections & VM_PROT_WRITE) == VM_PROT_WRITE);
- segInfo.executable = ((protections & VM_PROT_EXECUTE) == VM_PROT_EXECUTE);
- segInfo.textRelocsAllowed = textRelocsAllowed;
- if ( strcmp(segName, "__LINKEDIT") == 0 ) {
- result.linkeditSegIndex = result.segmentCount;
- }
- ++result.segmentCount;
- if ( result.segmentCount > 127 )
- stop = true;
- });
-#endif
-}
-
-
-void MachOParser::forEachSection(void (^callback)(const char* segName, const char* sectionName, uint32_t flags,
- const void* content, size_t size, bool illegalSectionSize, bool& stop)) const
-{
- forEachSection(^(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr,
- const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop) {
- callback(segName, sectionName, flags, content, (size_t)size, illegalSectionSize, stop);
- });
-}
-
-void MachOParser::forEachSection(void (^callback)(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr,
- const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2,
- bool illegalSectionSize, bool& stop)) const
-{
- Diagnostics diag;
- //fprintf(stderr, "forEachSection() mh=%p\n", header());
- LayoutInfo layout;
- getLayoutInfo(layout);
- forEachSection(^(const char* segName, uint32_t segIndex, uint64_t segVMAddr, const char* sectionName, uint32_t sectFlags,
- uint64_t sectAddr, uint64_t sectSize, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop) {
- #if DYLD_IN_PROCESS
- const uint8_t* segContentStart = (uint8_t*)(segVMAddr + layout.slide);
- #else
- const uint8_t* segContentStart = (uint8_t*)header() + layout.segments[segIndex].mappingOffset;
- #endif
- const void* contentAddr = segContentStart + (sectAddr - segVMAddr);
- callback(segName, sectionName, sectFlags, sectAddr, contentAddr, sectSize, alignP2, reserved1, reserved2, illegalSectionSize, stop);
- });
-
-}
-
-// this iterator just walks the segment/section array. It does interpret addresses
-void MachOParser::forEachSection(void (^callback)(const char* segName, uint32_t segIndex, uint64_t segVMAddr, const char* sectionName, uint32_t sectFlags,
- uint64_t sectAddr, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop)) const
-{
- Diagnostics diag;
- //fprintf(stderr, "forEachSection() mh=%p\n", header());
- __block uint32_t segIndex = 0;
- forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
- if ( cmd->cmd == LC_SEGMENT_64 ) {
- const segment_command_64* seg = (segment_command_64*)cmd;
- const section_64* const sectionsStart = (section_64*)((char*)seg + sizeof(struct segment_command_64));
- const section_64* const sectionsEnd = §ionsStart[seg->nsects];
- 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 illegalSectionSize = (sect->addr < seg->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, seg->vmaddr + seg->filesize);
- callback(seg->segname, segIndex, seg->vmaddr, sectName, sect->flags, sect->addr, sect->size, sect->align, sect->reserved1, sect->reserved2, illegalSectionSize, stop);
- }
- ++segIndex;
- }
- else if ( cmd->cmd == LC_SEGMENT ) {
- const segment_command* seg = (segment_command*)cmd;
- const section* const sectionsStart = (section*)((char*)seg + sizeof(struct segment_command));
- const section* const sectionsEnd = §ionsStart[seg->nsects];
- 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 illegalSectionSize = (sect->addr < seg->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, seg->vmaddr + seg->filesize);
- callback(seg->segname, segIndex, seg->vmaddr, sectName, sect->flags, sect->addr, sect->size, sect->align, sect->reserved1, sect->reserved2, illegalSectionSize, stop);
- }
- ++segIndex;
- }
- });
- diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
-}
-
-void MachOParser::forEachGlobalSymbol(Diagnostics& diag, void (^callback)(const char* symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop)) const
-{
- LinkEditInfo leInfo;
- getLinkEditPointers(diag, leInfo);
- if ( diag.hasError() )
- return;
-
- const bool is64Bit = is64();
- if ( leInfo.symTab != nullptr ) {
- uint32_t globalsStartIndex = 0;
- uint32_t globalsCount = leInfo.symTab->nsyms;
- if ( leInfo.dynSymTab != nullptr ) {
- globalsStartIndex = leInfo.dynSymTab->iextdefsym;
- globalsCount = leInfo.dynSymTab->nextdefsym;
- }
- uint32_t maxStringOffset = leInfo.symTab->strsize;
- const char* stringPool = (char*)getLinkEditContent(leInfo.layout, leInfo.symTab->stroff);
- const struct nlist* symbols = (struct nlist*) (getLinkEditContent(leInfo.layout, leInfo.symTab->symoff));
- const struct nlist_64* symbols64 = (struct nlist_64*)(getLinkEditContent(leInfo.layout, leInfo.symTab->symoff));
- bool stop = false;
- for (uint32_t i=0; (i < globalsCount) && !stop; ++i) {
- if ( is64Bit ) {
- const struct nlist_64& sym = symbols64[globalsStartIndex+i];
- if ( sym.n_un.n_strx > maxStringOffset )
- continue;
- if ( (sym.n_type & N_EXT) && ((sym.n_type & N_TYPE) == N_SECT) && ((sym.n_type & N_STAB) == 0) )
- callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop);
- }
- else {
- const struct nlist& sym = symbols[globalsStartIndex+i];
- if ( sym.n_un.n_strx > maxStringOffset )
- continue;
- if ( (sym.n_type & N_EXT) && ((sym.n_type & N_TYPE) == N_SECT) && ((sym.n_type & N_STAB) == 0) )
- callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop);
- }
- }
- }
-}
-
-void MachOParser::forEachLocalSymbol(Diagnostics& diag, void (^callback)(const char* symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop)) const
-{
- LinkEditInfo leInfo;
- getLinkEditPointers(diag, leInfo);
- if ( diag.hasError() )
- return;
-
- const bool is64Bit = is64();
- if ( leInfo.symTab != nullptr ) {
- uint32_t localsStartIndex = 0;
- uint32_t localsCount = leInfo.symTab->nsyms;
- if ( leInfo.dynSymTab != nullptr ) {
- localsStartIndex = leInfo.dynSymTab->ilocalsym;
- localsCount = leInfo.dynSymTab->nlocalsym;
- }
- uint32_t maxStringOffset = leInfo.symTab->strsize;
- const char* stringPool = (char*)getLinkEditContent(leInfo.layout, leInfo.symTab->stroff);
- const struct nlist* symbols = (struct nlist*) (getLinkEditContent(leInfo.layout, leInfo.symTab->symoff));
- const struct nlist_64* symbols64 = (struct nlist_64*)(getLinkEditContent(leInfo.layout, leInfo.symTab->symoff));
- bool stop = false;
- for (uint32_t i=0; (i < localsCount) && !stop; ++i) {
- if ( is64Bit ) {
- const struct nlist_64& sym = symbols64[localsStartIndex+i];
- if ( sym.n_un.n_strx > maxStringOffset )
- continue;
- if ( ((sym.n_type & N_EXT) == 0) && ((sym.n_type & N_TYPE) == N_SECT) && ((sym.n_type & N_STAB) == 0) )
- callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop);
- }
- else {
- const struct nlist& sym = symbols[localsStartIndex+i];
- if ( sym.n_un.n_strx > maxStringOffset )
- continue;
- if ( ((sym.n_type & N_EXT) == 0) && ((sym.n_type & N_TYPE) == N_SECT) && ((sym.n_type & N_STAB) == 0) )
- callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop);
- }
- }
- }
-}
-
-
-bool MachOParser::findExportedSymbol(Diagnostics& diag, const char* symbolName, void* extra, FoundSymbol& foundInfo, DependentFinder findDependent) const
-{
- LinkEditInfo leInfo;
- getLinkEditPointers(diag, leInfo);
- if ( diag.hasError() )
- return false;
- if ( leInfo.dyldInfo != nullptr ) {
- const uint8_t* trieStart = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->export_off);
- const uint8_t* trieEnd = trieStart + leInfo.dyldInfo->export_size;
- const uint8_t* node = trieWalk(diag, trieStart, trieEnd, symbolName);
- if ( node == nullptr ) {
- // symbol not exported from this image. Seach any re-exported dylibs
- __block unsigned depIndex = 0;
- __block bool foundInReExportedDylib = false;
- forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
- if ( isReExport && findDependent ) {
- const mach_header* depMH;
- void* depExtra;
- if ( findDependent(depIndex, loadPath, extra, &depMH, &depExtra) ) {
- bool depInRawCache = inRawCache() && (depMH->flags & 0x80000000);
- MachOParser dep(depMH, depInRawCache);
- if ( dep.findExportedSymbol(diag, symbolName, depExtra, foundInfo, findDependent) ) {
- stop = true;
- foundInReExportedDylib = true;
- }
- }
- else {
- fprintf(stderr, "could not find re-exported dylib %s\n", loadPath);
- }
- }
- ++depIndex;
- });
- return foundInReExportedDylib;
- }
- const uint8_t* p = node;
- const uint64_t flags = read_uleb128(diag, p, trieEnd);
- if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
- if ( !findDependent )
- return false;
- // re-export from another dylib, lookup there
- const uint64_t ordinal = read_uleb128(diag, p, trieEnd);
- const char* importedName = (char*)p;
- if ( importedName[0] == '\0' )
- importedName = symbolName;
- assert(ordinal >= 1);
- if (ordinal > dependentDylibCount()) {
- diag.error("ordinal %lld out of range for %s", ordinal, symbolName);
- return false;
- }
- uint32_t depIndex = (uint32_t)(ordinal-1);
- const mach_header* depMH;
- void* depExtra;
- if ( findDependent(depIndex, dependentDylibLoadPath(depIndex), extra, &depMH, &depExtra) ) {
- bool depInRawCache = inRawCache() && (depMH->flags & 0x80000000);
- MachOParser depParser(depMH, depInRawCache);
- return depParser.findExportedSymbol(diag, importedName, depExtra, foundInfo, findDependent);
- }
- else {
- diag.error("dependent dylib %lld not found for re-exported symbol %s", ordinal, symbolName);
- return false;
- }
- }
- foundInfo.kind = FoundSymbol::Kind::headerOffset;
- foundInfo.isThreadLocal = false;
- foundInfo.foundInDylib = header();
- foundInfo.foundExtra = extra;
- foundInfo.value = read_uleb128(diag, p, trieEnd);
- foundInfo.resolverFuncOffset = 0;
- foundInfo.foundSymbolName = symbolName;
- if ( diag.hasError() )
- return false;
- switch ( flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) {
- case EXPORT_SYMBOL_FLAGS_KIND_REGULAR:
- if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) {
- foundInfo.kind = FoundSymbol::Kind::headerOffset;
- foundInfo.resolverFuncOffset = (uint32_t)read_uleb128(diag, p, trieEnd);
- }
- else {
- foundInfo.kind = FoundSymbol::Kind::headerOffset;
- }
- break;
- case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL:
- foundInfo.isThreadLocal = true;
- break;
- case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE:
- foundInfo.kind = FoundSymbol::Kind::absolute;
- break;
- default:
- diag.error("unsupported exported symbol kind. flags=%llu at node offset=0x%0lX", flags, (long)(node-trieStart));
- return false;
- }
- return true;
- }
- else {
- // this is an old binary (before macOS 10.6), scan the symbol table
- foundInfo.foundInDylib = nullptr;
- uint64_t baseAddress = preferredLoadAddress();
- forEachGlobalSymbol(diag, ^(const char* aSymbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop) {
- if ( strcmp(aSymbolName, symbolName) == 0 ) {
- foundInfo.kind = FoundSymbol::Kind::headerOffset;
- foundInfo.isThreadLocal = false;
- foundInfo.foundInDylib = header();
- foundInfo.foundExtra = extra;
- foundInfo.value = n_value - baseAddress;
- foundInfo.resolverFuncOffset = 0;
- foundInfo.foundSymbolName = symbolName;
- stop = true;
- }
- });
- return (foundInfo.foundInDylib != nullptr);
- }
-}
-
-
-void MachOParser::getLinkEditLoadCommands(Diagnostics& diag, LinkEditInfo& result) const
-{
- result.dyldInfo = nullptr;
- result.symTab = nullptr;
- result.dynSymTab = nullptr;
- result.splitSegInfo = nullptr;
- result.functionStarts = nullptr;
- result.dataInCode = nullptr;
- result.codeSig = nullptr;
- __block bool hasUUID = false;
- __block bool hasVersion = false;
- __block bool hasEncrypt = false;
- forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
- switch ( cmd->cmd ) {
- case LC_DYLD_INFO:
- case LC_DYLD_INFO_ONLY:
- if ( cmd->cmdsize != sizeof(dyld_info_command) )
- diag.error("LC_DYLD_INFO load command size wrong");
- else if ( result.dyldInfo != nullptr )
- diag.error("multiple LC_DYLD_INFO load commands");
- result.dyldInfo = (dyld_info_command*)cmd;
- break;
- case LC_SYMTAB:
- if ( cmd->cmdsize != sizeof(symtab_command) )
- diag.error("LC_SYMTAB load command size wrong");
- else if ( result.symTab != nullptr )
- diag.error("multiple LC_SYMTAB load commands");
- result.symTab = (symtab_command*)cmd;
- break;
- case LC_DYSYMTAB:
- if ( cmd->cmdsize != sizeof(dysymtab_command) )
- diag.error("LC_DYSYMTAB load command size wrong");
- else if ( result.dynSymTab != nullptr )
- diag.error("multiple LC_DYSYMTAB load commands");
- result.dynSymTab = (dysymtab_command*)cmd;
- break;
- case LC_SEGMENT_SPLIT_INFO:
- if ( cmd->cmdsize != sizeof(linkedit_data_command) )
- diag.error("LC_SEGMENT_SPLIT_INFO load command size wrong");
- else if ( result.splitSegInfo != nullptr )
- diag.error("multiple LC_SEGMENT_SPLIT_INFO load commands");
- result.splitSegInfo = (linkedit_data_command*)cmd;
- break;
- case LC_FUNCTION_STARTS:
- if ( cmd->cmdsize != sizeof(linkedit_data_command) )
- diag.error("LC_FUNCTION_STARTS load command size wrong");
- else if ( result.functionStarts != nullptr )
- diag.error("multiple LC_FUNCTION_STARTS load commands");
- result.functionStarts = (linkedit_data_command*)cmd;
- break;
- case LC_DATA_IN_CODE:
- if ( cmd->cmdsize != sizeof(linkedit_data_command) )
- diag.error("LC_DATA_IN_CODE load command size wrong");
- else if ( result.dataInCode != nullptr )
- diag.error("multiple LC_DATA_IN_CODE load commands");
- result.dataInCode = (linkedit_data_command*)cmd;
- break;
- case LC_CODE_SIGNATURE:
- if ( cmd->cmdsize != sizeof(linkedit_data_command) )
- diag.error("LC_CODE_SIGNATURE load command size wrong");
- else if ( result.codeSig != nullptr )
- diag.error("multiple LC_CODE_SIGNATURE load commands");
- result.codeSig = (linkedit_data_command*)cmd;
- break;
- case LC_UUID:
- if ( cmd->cmdsize != sizeof(uuid_command) )
- diag.error("LC_UUID load command size wrong");
- else if ( hasUUID )
- diag.error("multiple LC_UUID load commands");
- hasUUID = true;
- break;
- case LC_VERSION_MIN_IPHONEOS:
- case LC_VERSION_MIN_MACOSX:
- case LC_VERSION_MIN_TVOS:
- case LC_VERSION_MIN_WATCHOS:
- if ( cmd->cmdsize != sizeof(version_min_command) )
- diag.error("LC_VERSION_* load command size wrong");
- else if ( hasVersion )
- diag.error("multiple LC_VERSION_MIN_* load commands");
- hasVersion = true;
- break;
- case LC_BUILD_VERSION:
- if ( cmd->cmdsize != (sizeof(build_version_command) + ((build_version_command*)cmd)->ntools * sizeof(build_tool_version)) )
- diag.error("LC_BUILD_VERSION load command size wrong");
- else if ( hasVersion )
- diag.error("multiple LC_BUILD_VERSION load commands");
- hasVersion = true;
- break;
- case LC_ENCRYPTION_INFO:
- if ( cmd->cmdsize != sizeof(encryption_info_command) )
- diag.error("LC_ENCRYPTION_INFO load command size wrong");
- else if ( hasEncrypt )
- diag.error("multiple LC_ENCRYPTION_INFO load commands");
- else if ( is64() )
- diag.error("LC_ENCRYPTION_INFO found in 64-bit mach-o");
- hasEncrypt = true;
- break;
- case LC_ENCRYPTION_INFO_64:
- if ( cmd->cmdsize != sizeof(encryption_info_command_64) )
- diag.error("LC_ENCRYPTION_INFO_64 load command size wrong");
- else if ( hasEncrypt )
- diag.error("multiple LC_ENCRYPTION_INFO_64 load commands");
- else if ( !is64() )
- diag.error("LC_ENCRYPTION_INFO_64 found in 32-bit mach-o");
- hasEncrypt = true;
- break;
- }
- });
- if ( diag.noError() && (result.dynSymTab != nullptr) && (result.symTab == nullptr) )
- diag.error("LC_DYSYMTAB but no LC_SYMTAB load command");
-
-}
-
-void MachOParser::getLinkEditPointers(Diagnostics& diag, LinkEditInfo& result) const
-{
- getLinkEditLoadCommands(diag, result);
- if ( diag.noError() )
- getLayoutInfo(result.layout);
-}
-
-void MachOParser::forEachSegment(void (^callback)(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop)) const
-{
- Diagnostics diag;
- forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
- if ( cmd->cmd == LC_SEGMENT_64 ) {
- const segment_command_64* seg = (segment_command_64*)cmd;
- callback(seg->segname, (uint32_t)seg->fileoff, (uint32_t)seg->filesize, seg->vmaddr, seg->vmsize, seg->initprot, stop);
- }
- else if ( cmd->cmd == LC_SEGMENT ) {
- const segment_command* seg = (segment_command*)cmd;
- callback(seg->segname, seg->fileoff, seg->filesize, seg->vmaddr, seg->vmsize, seg->initprot, stop);
- }
- });
- diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
-}
-
-const uint8_t* MachOParser::trieWalk(Diagnostics& diag, const uint8_t* start, const uint8_t* end, const char* symbol)
-{
- uint32_t visitedNodeOffsets[128];
- int visitedNodeOffsetCount = 0;
- visitedNodeOffsets[visitedNodeOffsetCount++] = 0;
- const uint8_t* p = start;
- while ( p < end ) {
- uint64_t terminalSize = *p++;
- if ( terminalSize > 127 ) {
- // except for re-export-with-rename, all terminal sizes fit in one byte
- --p;
- terminalSize = read_uleb128(diag, p, end);
- if ( diag.hasError() )
- return nullptr;
- }
- if ( (*symbol == '\0') && (terminalSize != 0) ) {
- return p;
- }
- const uint8_t* children = p + terminalSize;
- if ( children > end ) {
- diag.error("malformed trie node, terminalSize=0x%llX extends past end of trie\n", terminalSize);
- return nullptr;
- }
- uint8_t childrenRemaining = *children++;
- p = children;
- uint64_t nodeOffset = 0;
- for (; childrenRemaining > 0; --childrenRemaining) {
- const char* ss = symbol;
- bool wrongEdge = false;
- // scan whole edge to get to next edge
- // if edge is longer than target symbol name, don't read past end of symbol name
- char c = *p;
- while ( c != '\0' ) {
- if ( !wrongEdge ) {
- if ( c != *ss )
- wrongEdge = true;
- ++ss;
- }
- ++p;
- c = *p;
- }
- if ( wrongEdge ) {
- // advance to next child
- ++p; // skip over zero terminator
- // skip over uleb128 until last byte is found
- while ( (*p & 0x80) != 0 )
- ++p;
- ++p; // skip over last byte of uleb128
- if ( p > end ) {
- diag.error("malformed trie node, child node extends past end of trie\n");
- return nullptr;
- }
- }
- else {
- // the symbol so far matches this edge (child)
- // so advance to the child's node
- ++p;
- nodeOffset = read_uleb128(diag, p, end);
- if ( diag.hasError() )
- return nullptr;
- if ( (nodeOffset == 0) || ( &start[nodeOffset] > end) ) {
- diag.error("malformed trie child, nodeOffset=0x%llX out of range\n", nodeOffset);
- return nullptr;
- }
- symbol = ss;
- break;
- }
- }
- if ( nodeOffset != 0 ) {
- if ( nodeOffset > (end-start) ) {
- diag.error("malformed trie child, nodeOffset=0x%llX out of range\n", nodeOffset);
- return nullptr;
- }
- for (int i=0; i < visitedNodeOffsetCount; ++i) {
- if ( visitedNodeOffsets[i] == nodeOffset ) {
- diag.error("malformed trie child, cycle to nodeOffset=0x%llX\n", nodeOffset);
- return nullptr;
- }
- }
- visitedNodeOffsets[visitedNodeOffsetCount++] = (uint32_t)nodeOffset;
- if ( visitedNodeOffsetCount >= 128 ) {
- diag.error("malformed trie too deep\n");
- return nullptr;
- }
- p = &start[nodeOffset];
- }
- else
- p = end;
- }
- return nullptr;
-}
-
-
-uint64_t MachOParser::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 MachOParser::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 |= (-1LL) << bit;
- return result;
-}
-
-bool MachOParser::is64() const
-{
-#if DYLD_IN_PROCESS
- return (sizeof(void*) == 8);
-#else
- return (header()->magic == MH_MAGIC_64);
-#endif
-}
-
-
-
-
-bool MachOParser::findClosestSymbol(uint64_t targetUnslidAddress, const char** symbolName, uint64_t* symbolUnslidAddr) const
-{
- Diagnostics diag;
- __block uint64_t closestNValueSoFar = 0;
- __block const char* closestNameSoFar = nullptr;
- forEachGlobalSymbol(diag, ^(const char* aSymbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop) {
- if ( n_value <= targetUnslidAddress ) {
- if ( (closestNameSoFar == nullptr) || (closestNValueSoFar < n_value) ) {
- closestNValueSoFar = n_value;
- closestNameSoFar = aSymbolName;
- }
- }
- });
- forEachLocalSymbol(diag, ^(const char* aSymbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop) {
- if ( n_value <= targetUnslidAddress ) {
- if ( (closestNameSoFar == nullptr) || (closestNValueSoFar < n_value) ) {
- closestNValueSoFar = n_value;
- closestNameSoFar = aSymbolName;
- }
- }
- });
- if ( closestNameSoFar == nullptr ) {
- return false;
- }
-
- *symbolName = closestNameSoFar;
- *symbolUnslidAddr = closestNValueSoFar;
- return true;
-}
-
-
-#if DYLD_IN_PROCESS
-
-bool MachOParser::findClosestSymbol(const void* addr, const char** symbolName, const void** symbolAddress) const
-{
- uint64_t slide = getSlide();
- uint64_t symbolUnslidAddr;
- if ( findClosestSymbol((uint64_t)addr - slide, symbolName, &symbolUnslidAddr) ) {
- *symbolAddress = (const void*)(long)(symbolUnslidAddr + slide);
- return true;
- }
- return false;
-}
-
-intptr_t MachOParser::getSlide() const
-{
- Diagnostics diag;
- __block intptr_t slide = 0;
- forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
-#if __LP64__
- if ( cmd->cmd == LC_SEGMENT_64 ) {
- const segment_command_64* seg = (segment_command_64*)cmd;
- if ( strcmp(seg->segname, "__TEXT") == 0 ) {
- slide = ((uint64_t)header()) - seg->vmaddr;
- stop = true;
- }
- }
-#else
- if ( cmd->cmd == LC_SEGMENT ) {
- const segment_command* seg = (segment_command*)cmd;
- if ( strcmp(seg->segname, "__TEXT") == 0 ) {
- slide = ((uint32_t)header()) - seg->vmaddr;
- stop = true;
- }
- }
-#endif
- });
- diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
- return slide;
-}
-
-// this is only used by dlsym() at runtime. All other binding is done when the closure is built.
-bool MachOParser::hasExportedSymbol(const char* symbolName, DependentFinder finder, void** result) const
-{
- typedef void* (*ResolverFunc)(void);
- ResolverFunc resolver;
- Diagnostics diag;
- FoundSymbol foundInfo;
- if ( findExportedSymbol(diag, symbolName, (void*)header(), foundInfo, finder) ) {
- switch ( foundInfo.kind ) {
- case FoundSymbol::Kind::headerOffset:
- *result = (uint8_t*)foundInfo.foundInDylib + foundInfo.value;
- break;
- case FoundSymbol::Kind::absolute:
- *result = (void*)(long)foundInfo.value;
- break;
- case FoundSymbol::Kind::resolverOffset:
- // foundInfo.value contains "stub".
- // in dlsym() we want to call resolver function to get final function address
- resolver = (ResolverFunc)((uint8_t*)foundInfo.foundInDylib + foundInfo.resolverFuncOffset);
- *result = (*resolver)();
- break;
- }
- return true;
- }
- return false;
-}
-
-const char* MachOParser::segmentName(uint32_t targetSegIndex) const
-{
- __block const char* result = nullptr;
- __block uint32_t segIndex = 0;
- forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
- if ( segIndex == targetSegIndex ) {
- result = segName;
- stop = true;
- }
- ++segIndex;
- });
- return result;
-}
-
-#else
-
-
-bool MachOParser::uses16KPages() const
-{
- return (header()->cputype == CPU_TYPE_ARM64);
-}
-
-
-bool MachOParser::isEncrypted() const
-{
- __block bool result = false;
- Diagnostics diag;
- forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
- if ( cmd->cmd == LC_SEGMENT_64 ) {
- const segment_command_64* segCmd = (segment_command_64*)cmd;
- if ( segCmd->flags & SG_PROTECTED_VERSION_1 ) {
- result = true;
- stop = true;
- }
- }
- else if ( cmd->cmd == LC_SEGMENT ) {
- const segment_command* segCmd = (segment_command*)cmd;
- if ( segCmd->flags & SG_PROTECTED_VERSION_1 ) {
- result = true;
- stop = true;
- }
- }
- else if ( (cmd->cmd == LC_ENCRYPTION_INFO) || (cmd->cmd == LC_ENCRYPTION_INFO_64) ) {
- const encryption_info_command* encCmd = (encryption_info_command*)cmd;
- if ( encCmd->cryptid != 0 ) {
- result = true;
- stop = true;
- }
- }
- });
- return result;
-}
-
-bool MachOParser::hasWeakDefs() const
-{
- return (header()->flags & (MH_WEAK_DEFINES|MH_BINDS_TO_WEAK));
-}
-
-bool MachOParser::hasObjC() const
-{
- __block bool result = false;
- forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, const void* content, size_t size, bool illegalSectionSize, bool& stop) {
- if ( (strncmp(sectionName, "__objc_imageinfo", 16) == 0) && (strncmp(segmentName, "__DATA", 6) == 0) ) {
- result = true;
- stop = true;
- }
- if ( (header()->cputype == CPU_TYPE_I386) && (strcmp(sectionName, "__image_info") == 0) && (strcmp(segmentName, "__OBJC") == 0) ) {
- result = true;
- stop = true;
- }
- });
- return result;
-}
-
-bool MachOParser::hasPlusLoadMethod(Diagnostics& diag) const
-{
-#if 1
- __block bool result = false;
- forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop) {
- if ( ( (flags & SECTION_TYPE) == S_CSTRING_LITERALS ) ) {
- if (illegalSectionSize) {
- diag.error("cstring section %s/%s extends beyond the end of the segment", segmentName, sectionName);
- return;
- }
- const char* s = (char*)content;
- const char* end = s + size;
- while ( s < end ) {
- if ( strcmp(s, "load") == 0 ) {
- result = true;
- stop = true;
- return;
- }
- while (*s != '\0' )
- ++s;
- ++s;
- }
- }
- });
- return result;
-#else
- LayoutInfo layout;
- getLayoutInfo(layout);
-
- __block bool hasSwift = false;
- __block const void* classList = nullptr;
- __block size_t classListSize = 0;
- __block const void* objcData = nullptr;
- __block size_t objcDataSize = 0;
- __block const void* objcConstData = nullptr;
- __block size_t objcConstDataSize = 0;
- forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool& stop) {
- if ( (strcmp(sectionName, "__objc_classlist") == 0) && (strncmp(segmentName, "__DATA", 6) == 0) ) {
- classList = content;
- classListSize = size;
- }
- if ( (strcmp(sectionName, "__objc_imageinfo") == 0) && (strncmp(segmentName, "__DATA", 6) == 0) ) {
- const uint32_t* info = (uint32_t*)content;
- uint8_t swiftVersion = (info[1] >> 8) & 0xFF;
- if ( swiftVersion != 0 )
- hasSwift = true;
- }
- });
- if ( classList == nullptr )
- return false;
- // FIXME: might be objc and swift intermixed
- if ( hasSwift )
- return true;
- const bool p64 = is64();
- const uint32_t pointerSize = (p64 ? 8 : 4);
- const uint64_t* classArray64 = (uint64_t*)classList;
- const uint32_t* classArray32 = (uint32_t*)classList;
- const uint32_t classListCount = (uint32_t)(classListSize/pointerSize);
- for (uint32_t i=0; i < classListCount; ++i) {
- if ( p64 ) {
- uint64_t classObjAddr = classArray64[i];
- const uint64_t* classObjContent = (uint64_t*)getContentForVMAddr(layout, classObjAddr);
- uint64_t classROAddr = classObjContent[4];
- uint64_t metaClassObjAddr = classObjContent[0];
- const uint64_t* metaClassObjContent = (uint64_t*)getContentForVMAddr(layout, metaClassObjAddr);
- uint64_t metaClassROObjAddr = metaClassObjContent[4];
- const uint64_t* metaClassROObjContent = (uint64_t*)getContentForVMAddr(layout, metaClassROObjAddr);
- uint64_t metaClassMethodListAddr = metaClassROObjContent[4];
- if ( metaClassMethodListAddr != 0 ) {
- const uint64_t* metaClassMethodListContent = (uint64_t*)getContentForVMAddr(layout, metaClassMethodListAddr);
- const uint32_t methodListCount = ((uint32_t*)metaClassMethodListContent)[1];
- for (uint32_t m=0; m < methodListCount; ++m) {
- uint64_t methodNameAddr = metaClassMethodListContent[m*3+1];
- const char* methodNameContent = (char*)getContentForVMAddr(layout, methodNameAddr);
- if ( strcmp(methodNameContent, "load") == 0 ) {
- return true;
- }
- }
- }
- }
- else {
-
- }
- }
-
- return false;
-#endif
-}
-
-bool MachOParser::getCDHash(uint8_t cdHash[20])
-{
- Diagnostics diag;
- LinkEditInfo leInfo;
- getLinkEditPointers(diag, leInfo);
- if ( diag.hasError() || (leInfo.codeSig == nullptr) )
- return false;
-
- return cdHashOfCodeSignature(getLinkEditContent(leInfo.layout, leInfo.codeSig->dataoff), leInfo.codeSig->datasize, cdHash);
- }
-
-bool MachOParser::usesLibraryValidation() const
-{
- Diagnostics diag;
- LinkEditInfo leInfo;
- getLinkEditPointers(diag, leInfo);
- if ( diag.hasError() || (leInfo.codeSig == nullptr) )
- return false;
-
- const CS_CodeDirectory* cd = (const CS_CodeDirectory*)findCodeDirectoryBlob(getLinkEditContent(leInfo.layout, leInfo.codeSig->dataoff), leInfo.codeSig->datasize);
- if ( cd == nullptr )
- return false;
-
- // check for CS_REQUIRE_LV in CS_CodeDirectory.flags
- return (htonl(cd->flags) & CS_REQUIRE_LV);
- }
-
-
-bool MachOParser::isRestricted() const
-{
- __block bool result = false;
- forEachSection(^(const char* segName, const char* sectionName, uint32_t flags, const void* content, size_t size, bool illegalSectionSize, bool& stop) {
- if ( (strcmp(segName, "__RESTRICT") == 0) && (strcmp(sectionName, "__restrict") == 0) ) {
- result = true;
- stop = true;
- }
-
- });
- return result;
-}
-
-bool MachOParser::hasCodeSignature(uint32_t& fileOffset, uint32_t& size)
-{
- fileOffset = 0;
- size = 0;
-
- // <rdar://problem/13622786> ignore code signatures in macOS binaries built with pre-10.9 tools
- Platform platform;
- uint32_t minOS;
- uint32_t sdk;
- if ( getPlatformAndVersion(&platform, &minOS, &sdk) ) {
- // if have LC_VERSION_MIN_MACOSX and it says SDK < 10.9, so ignore code signature
- if ( (platform == Platform::macOS) && (sdk < 0x000A0900) )
- return false;
- }
- else {
- switch ( header()->cputype ) {
- case CPU_TYPE_I386:
- case CPU_TYPE_X86_64:
- // old binary with no LC_VERSION_*, assume intel binaries are old macOS binaries (ignore code signature)
- return false;
- }
- }
-
- 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
- return (fileOffset != 0);
-}
-
-bool MachOParser::getEntry(uint32_t& offset, bool& usesCRT)
-{
- 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 = (uint32_t)mainCmd->entryoff;
- stop = true;
- }
- else if ( cmd->cmd == LC_UNIXTHREAD ) {
- stop = true;
- usesCRT = true;
- const uint32_t* regs32 = (uint32_t*)(((char*)cmd) + 16);
- const uint64_t* regs64 = (uint64_t*)(((char*)cmd) + 16);
- uint64_t startAddress = 0;
- switch ( header()->cputype ) {
- case CPU_TYPE_I386:
- startAddress = regs32[10]; // i386_thread_state_t.eip
- break;
- case CPU_TYPE_X86_64:
- startAddress = regs64[16]; // x86_thread_state64_t.rip
- break;
- case CPU_TYPE_ARM:
- startAddress = regs32[15]; // arm_thread_state_t.__pc
- break;
- case CPU_TYPE_ARM64:
- startAddress = regs64[32]; // arm_thread_state64_t.__pc
- break;
- }
- offset = (uint32_t)(startAddress - preferredLoadAddress());
- }
- });
- diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
- // FIXME: validate offset is into executable segment
- return (offset != 0);
-}
-
-bool MachOParser::canBePlacedInDyldCache(const std::string& path) const {
- std::set<std::string> reasons;
- return canBePlacedInDyldCache(path, reasons);
-}
-
-bool MachOParser::canBePlacedInDyldCache(const std::string& path, std::set<std::string>& reasons) const
-{
- bool retval = true;
- // only dylibs can go in cache
- if ( fileType() != MH_DYLIB ) {
- reasons.insert("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
- const char* dylibName = installName();
- if ( (strncmp(dylibName, "/usr/lib/", 9) != 0) && (strncmp(dylibName, "/System/Library/", 16) != 0) ) {
- retval = false;
- reasons.insert("Not in '/usr/lib/' or '/System/Library/'");
- }
-
- // flat namespace files cannot go in cache
- if ( (header()->flags & MH_TWOLEVEL) == 0 ) {
- retval = false;
- reasons.insert("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;
- reasons.insert("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;
- reasons.insert("Missing split seg info");
- }
- if ( !hasDyldInfo ) {
- retval = false;
- reasons.insert("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;
- reasons.insert("Depends on cache inelegible dylibs");
- }
-
- // dylibs with interposing info cannot be in cache
- __block bool hasInterposing = false;
- forEachInterposingTuple(diag, ^(uint32_t segIndex, uint64_t replacementSegOffset, uint64_t replaceeSegOffset, uint64_t replacementContent, bool& stop) {
- hasInterposing = true;
- });
- if ( hasInterposing ) {
- retval = false;
- reasons.insert("Has interposing tuples");
- }
-
- return retval;
-}
-
-bool MachOParser::isDynamicExecutable() const
-{
- if ( 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 MachOParser::isSlideable() const
-{
- if ( header()->filetype == MH_DYLIB )
- return true;
- if ( header()->filetype == MH_BUNDLE )
- return true;
- if ( (header()->filetype == MH_EXECUTE) && (header()->flags & MH_PIE) )
- return true;
-
- return false;
-}
-
-
-
-bool MachOParser::hasInitializer(Diagnostics& diag) const
-{
- __block bool result = false;
- forEachInitializer(diag, ^(uint32_t offset) {
- result = true;
- });
- return result;
-}
-
-void MachOParser::forEachInitializer(Diagnostics& diag, void (^callback)(uint32_t offset)) const
-{
- __block uint64_t textSegAddrStart = 0;
- __block uint64_t textSegAddrEnd = 0;
-
- forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
- if ( strcmp(segName, "__TEXT") == 0 ) {
- textSegAddrStart = vmAddr;
- textSegAddrEnd = vmAddr + vmSize;
- stop = true;
- }
- });
- if ( textSegAddrStart == textSegAddrEnd ) {
- diag.error("no __TEXT segment");
- return;
- }
-
- // if dylib linked with -init linker option, that initializer is first
- forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
- if ( cmd->cmd == LC_ROUTINES ) {
- const routines_command* routines = (routines_command*)cmd;
- uint64_t dashInit = routines->init_address;
- if ( (textSegAddrStart < dashInit) && (dashInit < textSegAddrEnd) )
- callback((uint32_t)(dashInit - textSegAddrStart));
- else
- diag.error("-init does not point within __TEXT segment");
- }
- else if ( cmd->cmd == LC_ROUTINES_64 ) {
- const routines_command_64* routines = (routines_command_64*)cmd;
- uint64_t dashInit = routines->init_address;
- if ( (textSegAddrStart < dashInit) && (dashInit < textSegAddrEnd) )
- callback((uint32_t)(dashInit - textSegAddrStart));
- else
- diag.error("-init does not point within __TEXT segment");
- }
- });
-
- // next any function pointers in mod-init section
- bool p64 = is64();
- unsigned pointerSize = p64 ? 8 : 4;
- forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, const void* content, size_t size, bool illegalSectionSize, bool& stop) {
- if ( (flags & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS ) {
- if ( (size % pointerSize) != 0 ) {
- diag.error("initializer section %s/%s has bad size", segmentName, sectionName);
- stop = true;
- return;
- }
- if ( illegalSectionSize ) {
- diag.error("initializer section %s/%s extends beyond the end of the segment", segmentName, sectionName);
- stop = true;
- return;
- }
- if ( ((long)content % pointerSize) != 0 ) {
- diag.error("initializer section %s/%s is not pointer aligned", segmentName, sectionName);
- stop = true;
- return;
- }
- if ( p64 ) {
- const uint64_t* initsStart = (uint64_t*)content;
- const uint64_t* initsEnd = (uint64_t*)((uint8_t*)content + size);
- for (const uint64_t* p=initsStart; p < initsEnd; ++p) {
- uint64_t anInit = *p;
- if ( (anInit <= textSegAddrStart) || (anInit > textSegAddrEnd) ) {
- diag.error("initializer 0x%0llX does not point within __TEXT segment", anInit);
- stop = true;
- break;
- }
- callback((uint32_t)(anInit - textSegAddrStart));
- }
- }
- else {
- const uint32_t* initsStart = (uint32_t*)content;
- const uint32_t* initsEnd = (uint32_t*)((uint8_t*)content + size);
- for (const uint32_t* p=initsStart; p < initsEnd; ++p) {
- uint32_t anInit = *p;
- if ( (anInit <= textSegAddrStart) || (anInit > textSegAddrEnd) ) {
- diag.error("initializer 0x%0X does not point within __TEXT segment", anInit);
- stop = true;
- break;
- }
- callback(anInit - (uint32_t)textSegAddrStart);
- }
- }
- }
- });
-}
-
-void MachOParser::forEachDOFSection(Diagnostics& diag, void (^callback)(uint32_t offset)) const
-{
- forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, const void* content, size_t size, bool illegalSectionSize, bool& stop) {
- if ( ( (flags & SECTION_TYPE) == S_DTRACE_DOF ) && !illegalSectionSize ) {
- callback((uint32_t)((uintptr_t)content - (uintptr_t)header()));
- }
- });
-}
-
-
-uint32_t MachOParser::segmentCount() const
-{
- __block uint32_t count = 0;
- forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
- ++count;
- });
- return count;
-}
-
-void MachOParser::forEachSegment(void (^callback)(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop)) const
-{
- Diagnostics diag;
- __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;
- }
- callback(segCmd->segname, (uint32_t)segCmd->fileoff, (uint32_t)segCmd->filesize, segCmd->vmaddr, segCmd->vmsize, segCmd->initprot, segIndex, sizeOfSections, p2align, stop);
- ++segIndex;
- }
- else if ( cmd->cmd == LC_SEGMENT ) {
- const segment_command* segCmd = (segment_command*)cmd;
- uint64_t sizeOfSections = segCmd->vmsize;
- uint8_t p2align = 0;
- const section* const sectionsStart = (section*)((char*)segCmd + sizeof(struct segment_command));
- const section* const sectionsEnd = §ionsStart[segCmd->nsects];
- for (const section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
- sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
- if ( sect->align > p2align )
- p2align = sect->align;
- }
- callback(segCmd->segname, (uint32_t)segCmd->fileoff, (uint32_t)segCmd->filesize, segCmd->vmaddr, segCmd->vmsize, segCmd->initprot, segIndex, sizeOfSections, p2align, stop);
- ++segIndex;
- }
- });
- diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
-}
-
-void MachOParser::forEachExportedSymbol(Diagnostics diag, void (^handler)(const char* symbolName, uint64_t imageOffset, bool isReExport, bool& stop)) const
-{
- LinkEditInfo leInfo;
- getLinkEditPointers(diag, leInfo);
- if ( diag.hasError() )
- return;
-
- if ( leInfo.dyldInfo != nullptr ) {
- const uint8_t* trieStart = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->export_off);
- const uint8_t* trieEnd = trieStart + leInfo.dyldInfo->export_size;
- std::vector<ExportInfoTrie::Entry> exports;
- if ( !ExportInfoTrie::parseTrie(trieStart, trieEnd, exports) ) {
- diag.error("malformed exports trie");
- return;
- }
- bool stop = false;
- for (const ExportInfoTrie::Entry& exp : exports) {
- bool isReExport = (exp.info.flags & EXPORT_SYMBOL_FLAGS_REEXPORT);
- handler(exp.name.c_str(), exp.info.address, isReExport, stop);
- if ( stop )
- break;
- }
- }
-}
-
-bool MachOParser::invalidRebaseState(Diagnostics& diag, const char* opcodeName, const MachOParser::LinkEditInfo& leInfo,
- bool segIndexSet, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type) const
-{
- if ( !segIndexSet ) {
- diag.error("%s missing preceding REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", opcodeName);
- return true;
- }
- if ( segmentIndex >= leInfo.layout.segmentCount ) {
- diag.error("%s segment index %d too large", opcodeName, segmentIndex);
- return true;
- }
- if ( segmentOffset > (leInfo.layout.segments[segmentIndex].segSize-pointerSize) ) {
- diag.error("%s current segment offset 0x%08llX beyond segment size (0x%08llX)", opcodeName, segmentOffset, leInfo.layout.segments[segmentIndex].segSize);
- return true;
- }
- switch ( type ) {
- case REBASE_TYPE_POINTER:
- if ( !leInfo.layout.segments[segmentIndex].writable ) {
- diag.error("%s pointer rebase is in non-writable segment", opcodeName);
- return true;
- }
- if ( leInfo.layout.segments[segmentIndex].executable ) {
- diag.error("%s pointer rebase is in executable segment", opcodeName);
- return true;
- }
- break;
- case REBASE_TYPE_TEXT_ABSOLUTE32:
- case REBASE_TYPE_TEXT_PCREL32:
- if ( !leInfo.layout.segments[segmentIndex].textRelocsAllowed ) {
- diag.error("%s text rebase is in segment that does not support text relocations", opcodeName);
- return true;
- }
- if ( leInfo.layout.segments[segmentIndex].writable ) {
- diag.error("%s text rebase is in writable segment", opcodeName);
- return true;
- }
- if ( !leInfo.layout.segments[segmentIndex].executable ) {
- diag.error("%s pointer rebase is in non-executable segment", opcodeName);
- return true;
- }
- break;
- default:
- diag.error("%s unknown rebase type %d", opcodeName, type);
- return true;
- }
- return false;
-}
-
-void MachOParser::forEachRebase(Diagnostics& diag, void (^handler)(uint32_t segIndex, uint64_t segOffset, uint8_t type, bool& stop)) const
-{
- LinkEditInfo leInfo;
- getLinkEditPointers(diag, leInfo);
- if ( diag.hasError() )
- return;
-
- if ( leInfo.dyldInfo != nullptr ) {
- // work around linker bug that laid down rebase opcodes for lazy pointer section when -bind_at_load used
- __block int lpSegIndex = 0;
- __block uint64_t lpSegOffsetStart = 0;
- __block uint64_t lpSegOffsetEnd = 0;
- bool hasWeakBinds = (leInfo.dyldInfo->weak_bind_size != 0);
- if ( leInfo.dyldInfo->lazy_bind_size == 0 ) {
- __block uint64_t lpAddr = 0;
- __block uint64_t lpSize = 0;
- forEachSection(^(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& sectStop) {
- if ( (flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) {
- lpAddr = addr;
- lpSize = size;
- sectStop = true;
- }
- });
- forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& segStop) {
- if ( (vmAddr <= lpAddr) && (vmAddr+vmSize >= lpAddr+lpSize) ) {
- lpSegOffsetStart = lpAddr - vmAddr;
- lpSegOffsetEnd = lpSegOffsetStart + lpSize;
- segStop = true;
- return;
- }
- ++lpSegIndex;
- });
- }
- // don't remove rebase if there is a weak-bind at pointer location
- bool (^weakBindAt)(uint64_t segOffset) = ^(uint64_t segOffset) {
- if ( !hasWeakBinds )
- return false;
- __block bool result = false;
- Diagnostics weakDiag;
- forEachWeakDef(weakDiag, ^(bool strongDef, uint32_t dataSegIndex, uint64_t dataSegOffset, uint64_t addend, const char* symbolName, bool& weakStop) {
- if ( segOffset == dataSegOffset ) {
- result = true;
- weakStop = true;
- }
- });
- return result;
- };
-
-
- const uint8_t* p = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->rebase_off);
- const uint8_t* end = p + leInfo.dyldInfo->rebase_size;
- const uint32_t pointerSize = (is64() ? 8 : 4);
- uint8_t type = 0;
- int segIndex = 0;
- uint64_t segOffset = 0;
- uint64_t count;
- uint64_t skip;
- bool segIndexSet = false;
- bool stop = false;
- while ( !stop && diag.noError() && (p < end) ) {
- uint8_t immediate = *p & REBASE_IMMEDIATE_MASK;
- uint8_t opcode = *p & REBASE_OPCODE_MASK;
- ++p;
- switch (opcode) {
- case REBASE_OPCODE_DONE:
- stop = true;
- break;
- case REBASE_OPCODE_SET_TYPE_IMM:
- type = immediate;
- break;
- case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
- segIndex = immediate;
- segOffset = read_uleb128(diag, p, end);
- segIndexSet = true;
- break;
- case REBASE_OPCODE_ADD_ADDR_ULEB:
- segOffset += read_uleb128(diag, p, end);
- break;
- case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
- segOffset += immediate*pointerSize;
- break;
- case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
- for (int i=0; i < immediate; ++i) {
- if ( invalidRebaseState(diag, "REBASE_OPCODE_DO_REBASE_IMM_TIMES", leInfo, segIndexSet, pointerSize, segIndex, segOffset, type) )
- return;
- if ( (segIndex != lpSegIndex) || (segOffset > lpSegOffsetEnd) || (segOffset < lpSegOffsetStart) || weakBindAt(segOffset) )
- handler(segIndex, segOffset, type, stop);
- segOffset += pointerSize;
- }
- break;
- case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
- count = read_uleb128(diag, p, end);
- for (uint32_t i=0; i < count; ++i) {
- if ( invalidRebaseState(diag, "REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo, segIndexSet, pointerSize, segIndex, segOffset, type) )
- return;
- if ( (segIndex != lpSegIndex) || (segOffset > lpSegOffsetEnd) || (segOffset < lpSegOffsetStart) || weakBindAt(segOffset) )
- handler(segIndex, segOffset, type, stop);
- segOffset += pointerSize;
- }
- break;
- case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
- if ( invalidRebaseState(diag, "REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo, segIndexSet, pointerSize, segIndex, segOffset, type) )
- return;
- handler(segIndex, segOffset, type, stop);
- segOffset += read_uleb128(diag, p, end) + pointerSize;
- break;
- case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
- count = read_uleb128(diag, p, end);
- if ( diag.hasError() )
- break;
- skip = read_uleb128(diag, p, end);
- for (uint32_t i=0; i < count; ++i) {
- if ( invalidRebaseState(diag, "REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB", leInfo, segIndexSet, pointerSize, segIndex, segOffset, type) )
- return;
- handler(segIndex, segOffset, type, stop);
- segOffset += skip + pointerSize;
- }
- break;
- default:
- diag.error("unknown rebase opcode 0x%02X", opcode);
- }
- }
- }
- else {
- // old binary
- const relocation_info* const relocsStart = (relocation_info*)getLinkEditContent(leInfo.layout, leInfo.dynSymTab->locreloff);
- const relocation_info* const relocsEnd = &relocsStart[leInfo.dynSymTab->nlocrel];
- bool stop = false;
- const uint8_t relocSize = (is64() ? 3 : 2);
- for (const relocation_info* reloc=relocsStart; (reloc < relocsEnd) && !stop; ++reloc) {
- if ( reloc->r_length != relocSize ) {
- diag.error("local relocation has wrong r_length");
- break;
- }
- if ( reloc->r_type != 0 ) { // 0 == X86_64_RELOC_UNSIGNED == GENERIC_RELOC_VANILLA == ARM64_RELOC_UNSIGNED
- diag.error("local relocation has wrong r_type");
- break;
- }
- doLocalReloc(diag, reloc->r_address, stop, handler);
- }
- // then process indirect symbols
- forEachIndirectPointer(diag, ^(uint32_t segIndex, uint64_t segOffset, bool bind, int bindLibOrdinal,
- const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& indStop) {
- if ( !bind && !bindLazy )
- handler(segIndex, segOffset, REBASE_TYPE_POINTER, indStop);
- });
- }
-}
-
-bool MachOParser::doLocalReloc(Diagnostics& diag, uint32_t r_address, bool& stop, void (^handler)(uint32_t segIndex, uint64_t segOffset, uint8_t type, bool& stop)) const
-{
- bool firstWritable = (header()->cputype == CPU_TYPE_X86_64);
- __block uint64_t relocBaseAddress = 0;
- __block bool baseFound = false;
- __block uint32_t segIndex = 0;
- forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool &stopSeg) {
- if ( !baseFound ) {
- if ( !firstWritable || (protections & VM_PROT_WRITE) ) {
- baseFound = true;
- relocBaseAddress = vmAddr;
- }
- }
- if ( baseFound && (vmAddr < relocBaseAddress+r_address) && (relocBaseAddress+r_address < vmAddr+vmSize) ) {
- uint8_t type = REBASE_TYPE_POINTER;
- uint64_t segOffset = relocBaseAddress + r_address - vmAddr;
- handler(segIndex, segOffset, type, stop);
- stopSeg = true;
- }
- ++segIndex;
- });
-
- return false;
-}
-
-int MachOParser::libOrdinalFromDesc(uint16_t n_desc) const
-{
- // -flat_namespace is always flat lookup
- if ( (header()->flags & MH_TWOLEVEL) == 0 )
- return BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
-
- // extract byte from undefined symbol entry
- int libIndex = GET_LIBRARY_ORDINAL(n_desc);
- switch ( libIndex ) {
- case SELF_LIBRARY_ORDINAL:
- return BIND_SPECIAL_DYLIB_SELF;
-
- case DYNAMIC_LOOKUP_ORDINAL:
- return BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
-
- case EXECUTABLE_ORDINAL:
- return BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE;
- }
-
- return libIndex;
-}
-
-bool MachOParser::doExternalReloc(Diagnostics& diag, uint32_t r_address, uint32_t r_symbolnum, LinkEditInfo& leInfo, bool& stop,
- void (^handler)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, int libOrdinal,
- uint64_t addend, const char* symbolName, bool weakImport, bool lazy, bool& stop)) const
-{
- const bool firstWritable = (header()->cputype == CPU_TYPE_X86_64);
- const bool is64Bit = is64();
- __block uint64_t relocBaseAddress = 0;
- __block bool baseFound = false;
- __block uint32_t segIndex = 0;
- forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool &stopSeg) {
- if ( !baseFound ) {
- if ( !firstWritable || (protections & VM_PROT_WRITE) ) {
- baseFound = true;
- relocBaseAddress = vmAddr;
- }
- }
- if ( baseFound && (vmAddr < relocBaseAddress+r_address) && (relocBaseAddress+r_address < vmAddr+vmSize) ) {
- uint8_t type = BIND_TYPE_POINTER;
- uint64_t segOffset = relocBaseAddress + r_address - vmAddr;
- const void* symbolTable = getLinkEditContent(leInfo.layout, leInfo.symTab->symoff);
- const struct nlist_64* symbols64 = (nlist_64*)symbolTable;
- const struct nlist* symbols32 = (struct nlist*)symbolTable;
- const char* stringPool = (char*)getLinkEditContent(leInfo.layout, leInfo.symTab->stroff);
- uint32_t symCount = leInfo.symTab->nsyms;
- uint32_t poolSize = leInfo.symTab->strsize;
- if ( r_symbolnum < symCount ) {
- uint16_t n_desc = is64Bit ? symbols64[r_symbolnum].n_desc : symbols32[r_symbolnum].n_desc;
- uint32_t libOrdinal = libOrdinalFromDesc(n_desc);
- uint32_t strOffset = is64Bit ? symbols64[r_symbolnum].n_un.n_strx : symbols32[r_symbolnum].n_un.n_strx;
- if ( strOffset < poolSize ) {
- const char* symbolName = stringPool + strOffset;
- bool weakImport = (n_desc & N_WEAK_REF);
- bool lazy = false;
- uint64_t addend = is64Bit ? (*((uint64_t*)((char*)header()+fileOffset+segOffset))) : (*((uint32_t*)((char*)header()+fileOffset+segOffset)));
- handler(segIndex, segOffset, type, libOrdinal, addend, symbolName, weakImport, lazy, stop);
- stopSeg = true;
- }
- }
- }
- ++segIndex;
- });
-
- return false;
-}
-
-bool MachOParser::invalidBindState(Diagnostics& diag, const char* opcodeName, const LinkEditInfo& leInfo, bool segIndexSet, bool libraryOrdinalSet,
- uint32_t dylibCount, int libOrdinal, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, const char* symbolName) const
-{
- if ( !segIndexSet ) {
- diag.error("%s missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", opcodeName);
- return true;
- }
- if ( segmentIndex >= leInfo.layout.segmentCount ) {
- diag.error("%s segment index %d too large", opcodeName, segmentIndex);
- return true;
- }
- if ( segmentOffset > (leInfo.layout.segments[segmentIndex].segSize-pointerSize) ) {
- diag.error("%s current segment offset 0x%08llX beyond segment size (0x%08llX)", opcodeName, segmentOffset, leInfo.layout.segments[segmentIndex].segSize);
- return true;
- }
- if ( symbolName == NULL ) {
- diag.error("%s missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM", opcodeName);
- return true;
- }
- if ( !libraryOrdinalSet ) {
- diag.error("%s missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL", opcodeName);
- return true;
- }
- if ( libOrdinal > (int)dylibCount ) {
- diag.error("%s has library ordinal too large (%d) max (%d)", opcodeName, libOrdinal, dylibCount);
- return true;
- }
- if ( libOrdinal < -2 ) {
- diag.error("%s has unknown library special ordinal (%d)", opcodeName, libOrdinal);
- return true;
- }
- switch ( type ) {
- case BIND_TYPE_POINTER:
- if ( !leInfo.layout.segments[segmentIndex].writable ) {
- diag.error("%s pointer bind is in non-writable segment", opcodeName);
- return true;
- }
- if ( leInfo.layout.segments[segmentIndex].executable ) {
- diag.error("%s pointer bind is in executable segment", opcodeName);
- return true;
- }
- break;
- case BIND_TYPE_TEXT_ABSOLUTE32:
- case BIND_TYPE_TEXT_PCREL32:
- if ( !leInfo.layout.segments[segmentIndex].textRelocsAllowed ) {
- diag.error("%s text bind is in segment that does not support text relocations", opcodeName);
- return true;
- }
- if ( leInfo.layout.segments[segmentIndex].writable ) {
- diag.error("%s text bind is in writable segment", opcodeName);
- return true;
- }
- if ( !leInfo.layout.segments[segmentIndex].executable ) {
- diag.error("%s pointer bind is in non-executable segment", opcodeName);
- return true;
- }
- break;
- default:
- diag.error("%s unknown bind type %d", opcodeName, type);
- return true;
- }
- return false;
-}
-
-void MachOParser::forEachBind(Diagnostics& diag, void (^handler)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type,
- int libOrdinal, uint64_t addend, const char* symbolName, bool weakImport, bool lazy, bool& stop)) const
-{
- LinkEditInfo leInfo;
- getLinkEditPointers(diag, leInfo);
- if ( diag.hasError() )
- return;
- const uint32_t dylibCount = dependentDylibCount();
-
- if ( leInfo.dyldInfo != nullptr ) {
- // process bind opcodes
- const uint8_t* p = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->bind_off);
- const uint8_t* end = p + leInfo.dyldInfo->bind_size;
- const uint32_t pointerSize = (is64() ? 8 : 4);
- uint8_t type = 0;
- uint64_t segmentOffset = 0;
- uint8_t segmentIndex = 0;
- const char* symbolName = NULL;
- int libraryOrdinal = 0;
- bool segIndexSet = false;
- bool libraryOrdinalSet = false;
-
- int64_t addend = 0;
- uint64_t count;
- uint64_t skip;
- bool weakImport = false;
- bool done = false;
- bool stop = false;
- while ( !done && !stop && diag.noError() && (p < end) ) {
- uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
- uint8_t opcode = *p & BIND_OPCODE_MASK;
- ++p;
- switch (opcode) {
- case BIND_OPCODE_DONE:
- done = true;
- break;
- case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
- libraryOrdinal = immediate;
- libraryOrdinalSet = true;
- break;
- case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
- libraryOrdinal = (int)read_uleb128(diag, p, end);
- libraryOrdinalSet = true;
- break;
- case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
- // the special ordinals are negative numbers
- if ( immediate == 0 )
- libraryOrdinal = 0;
- else {
- int8_t signExtended = BIND_OPCODE_MASK | immediate;
- libraryOrdinal = signExtended;
- }
- libraryOrdinalSet = true;
- break;
- case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
- weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 );
- symbolName = (char*)p;
- while (*p != '\0')
- ++p;
- ++p;
- break;
- case BIND_OPCODE_SET_TYPE_IMM:
- type = immediate;
- break;
- case BIND_OPCODE_SET_ADDEND_SLEB:
- addend = read_sleb128(diag, p, end);
- break;
- case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
- segmentIndex = immediate;
- segmentOffset = read_uleb128(diag, p, end);
- segIndexSet = true;
- break;
- case BIND_OPCODE_ADD_ADDR_ULEB:
- segmentOffset += read_uleb128(diag, p, end);
- break;
- case BIND_OPCODE_DO_BIND:
- if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
- return;
- handler(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, weakImport, false, stop);
- segmentOffset += pointerSize;
- break;
- case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
- if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB", leInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
- return;
- handler(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, weakImport, false, stop);
- segmentOffset += read_uleb128(diag, p, end) + pointerSize;
- break;
- case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
- if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED", leInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
- return;
- handler(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, weakImport, false, stop);
- segmentOffset += immediate*pointerSize + pointerSize;
- break;
- case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
- count = read_uleb128(diag, p, end);
- skip = read_uleb128(diag, p, end);
- for (uint32_t i=0; i < count; ++i) {
- if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB", leInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
- return;
- handler(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, weakImport, false, stop);
- segmentOffset += skip + pointerSize;
- }
- break;
- default:
- diag.error("bad bind opcode 0x%02X", *p);
- }
- }
- if ( diag.hasError() || stop )
- return;
- // process lazy bind opcodes
- if ( leInfo.dyldInfo->lazy_bind_size != 0 ) {
- p = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->lazy_bind_off);
- end = p + leInfo.dyldInfo->lazy_bind_size;
- type = BIND_TYPE_POINTER;
- segmentOffset = 0;
- segmentIndex = 0;
- symbolName = NULL;
- libraryOrdinal = 0;
- segIndexSet = false;
- libraryOrdinalSet= false;
- addend = 0;
- weakImport = false;
- stop = false;
- while ( !stop && diag.noError() && (p < end) ) {
- uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
- uint8_t opcode = *p & BIND_OPCODE_MASK;
- ++p;
- switch (opcode) {
- case BIND_OPCODE_DONE:
- // this opcode marks the end of each lazy pointer binding
- break;
- case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
- libraryOrdinal = immediate;
- libraryOrdinalSet = true;
- break;
- case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
- libraryOrdinal = (int)read_uleb128(diag, p, end);
- libraryOrdinalSet = true;
- break;
- case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
- // the special ordinals are negative numbers
- if ( immediate == 0 )
- libraryOrdinal = 0;
- else {
- int8_t signExtended = BIND_OPCODE_MASK | immediate;
- libraryOrdinal = signExtended;
- }
- libraryOrdinalSet = true;
- break;
- case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
- weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 );
- symbolName = (char*)p;
- while (*p != '\0')
- ++p;
- ++p;
- break;
- case BIND_OPCODE_SET_ADDEND_SLEB:
- addend = read_sleb128(diag, p, end);
- break;
- case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
- segmentIndex = immediate;
- segmentOffset = read_uleb128(diag, p, end);
- segIndexSet = true;
- break;
- case BIND_OPCODE_DO_BIND:
- if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
- return;
- handler(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, weakImport, true, stop);
- segmentOffset += pointerSize;
- break;
- case BIND_OPCODE_SET_TYPE_IMM:
- case BIND_OPCODE_ADD_ADDR_ULEB:
- case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
- case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
- case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
- default:
- diag.error("bad lazy bind opcode 0x%02X", opcode);
- break;
- }
- }
- }
- }
- else {
- // old binary, first process relocation
- const relocation_info* const relocsStart = (relocation_info*)getLinkEditContent(leInfo.layout, leInfo.dynSymTab->extreloff);
- const relocation_info* const relocsEnd = &relocsStart[leInfo.dynSymTab->nextrel];
- bool stop = false;
- const uint8_t relocSize = (is64() ? 3 : 2);
- for (const relocation_info* reloc=relocsStart; (reloc < relocsEnd) && !stop; ++reloc) {
- if ( reloc->r_length != relocSize ) {
- diag.error("external relocation has wrong r_length");
- break;
- }
- if ( reloc->r_type != 0 ) { // 0 == X86_64_RELOC_UNSIGNED == GENERIC_RELOC_VANILLA == ARM64_RELOC_UNSIGNED
- diag.error("external relocation has wrong r_type");
- break;
- }
- doExternalReloc(diag, reloc->r_address, reloc->r_symbolnum, leInfo, stop, handler);
- }
- // then process indirect symbols
- forEachIndirectPointer(diag, ^(uint32_t segIndex, uint64_t segOffset, bool bind, int bindLibOrdinal,
- const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& indStop) {
- if ( bind )
- handler(segIndex, segOffset, (selfModifyingStub ? BIND_TYPE_IMPORT_JMP_REL32 : BIND_TYPE_POINTER), bindLibOrdinal, 0, bindSymbolName, bindWeakImport, bindLazy, indStop);
- });
- }
-}
-
-
-void MachOParser::forEachWeakDef(Diagnostics& diag, void (^handler)(bool strongDef, uint32_t dataSegIndex, uint64_t dataSegOffset,
- uint64_t addend, const char* symbolName, bool& stop)) const
-{
- LinkEditInfo leInfo;
- getLinkEditPointers(diag, leInfo);
- if ( diag.hasError() )
- return;
-
- const uint32_t dylibCount = dependentDylibCount();
- if ( leInfo.dyldInfo != nullptr ) {
- // process weak bind opcodes
- const uint8_t* p = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->weak_bind_off);
- const uint8_t* end = p + leInfo.dyldInfo->weak_bind_size;
- const uint32_t pointerSize = (is64() ? 8 : 4);
- uint8_t type = 0;
- uint64_t segmentOffset = 0;
- uint8_t segmentIndex = 0;
- const char* symbolName = NULL;
- int64_t addend = 0;
- uint64_t count;
- uint64_t skip;
- bool segIndexSet = false;
- bool done = false;
- bool stop = false;
- while ( !done && !stop && diag.noError() && (p < end) ) {
- uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
- uint8_t opcode = *p & BIND_OPCODE_MASK;
- ++p;
- switch (opcode) {
- case BIND_OPCODE_DONE:
- done = true;
- break;
- case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
- case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
- case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
- diag.error("unexpected dylib ordinal in weak binding info");
- return;
- case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
- symbolName = (char*)p;
- while (*p != '\0')
- ++p;
- ++p;
- if ( (immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) != 0 )
- handler(true, 0, 0, 0, symbolName, stop);
- break;
- case BIND_OPCODE_SET_TYPE_IMM:
- type = immediate;
- break;
- case BIND_OPCODE_SET_ADDEND_SLEB:
- addend = read_sleb128(diag, p, end);
- break;
- case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
- segmentIndex = immediate;
- segmentOffset = read_uleb128(diag, p, end);
- segIndexSet = true;
- break;
- case BIND_OPCODE_ADD_ADDR_ULEB:
- segmentOffset += read_uleb128(diag, p, end);
- break;
- case BIND_OPCODE_DO_BIND:
- if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, true, dylibCount, -2, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
- return;
- handler(false, segmentIndex, segmentOffset, addend, symbolName, stop);
- segmentOffset += pointerSize;
- break;
- case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
- if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, true, dylibCount, -2, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
- return;
- handler(false, segmentIndex, segmentOffset, addend, symbolName, stop);
- segmentOffset += read_uleb128(diag, p, end) + pointerSize;
- break;
- case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
- if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, true, dylibCount, -2, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
- return;
- handler(false, segmentIndex, segmentOffset, addend, symbolName, stop);
- segmentOffset += immediate*pointerSize + pointerSize;
- break;
- case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
- count = read_uleb128(diag, p, end);
- skip = read_uleb128(diag, p, end);
- for (uint32_t i=0; i < count; ++i) {
- if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, true, dylibCount, -2, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
- return;
- handler(false, segmentIndex, segmentOffset, addend, symbolName, stop);
- segmentOffset += skip + pointerSize;
- }
- break;
- default:
- diag.error("bad weak bind opcode 0x%02X", *p);
- }
- }
- if ( diag.hasError() || stop )
- return;
- }
- else {
- // old binary
- //assert(0 && "weak defs not supported for old binaries yet");
- }
-}
-
-
-
-void MachOParser::forEachIndirectPointer(Diagnostics& diag, void (^handler)(uint32_t dataSegIndex, uint64_t dataSegOffset, bool bind, int bindLibOrdinal,
- const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& stop)) const
-{
- LinkEditInfo leInfo;
- getLinkEditPointers(diag, leInfo);
- if ( diag.hasError() )
- return;
-
- // find lazy and non-lazy pointer sections
- const bool is64Bit = is64();
- const uint32_t* const indirectSymbolTable = (uint32_t*)getLinkEditContent(leInfo.layout, leInfo.dynSymTab->indirectsymoff);
- const uint32_t indirectSymbolTableCount = leInfo.dynSymTab->nindirectsyms;
- const uint32_t pointerSize = is64Bit ? 8 : 4;
- const void* symbolTable = getLinkEditContent(leInfo.layout, leInfo.symTab->symoff);
- const struct nlist_64* symbols64 = (nlist_64*)symbolTable;
- const struct nlist* symbols32 = (struct nlist*)symbolTable;
- const char* stringPool = (char*)getLinkEditContent(leInfo.layout, leInfo.symTab->stroff);
- uint32_t symCount = leInfo.symTab->nsyms;
- uint32_t poolSize = leInfo.symTab->strsize;
- __block bool stop = false;
- forEachSection(^(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content,
- uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& sectionStop) {
- uint8_t sectionType = (flags & SECTION_TYPE);
- if ( (sectionType != S_LAZY_SYMBOL_POINTERS) && (sectionType != S_NON_LAZY_SYMBOL_POINTERS) && (sectionType != S_SYMBOL_STUBS) )
- return;
- bool selfModifyingStub = (sectionType == S_SYMBOL_STUBS) && (flags & S_ATTR_SELF_MODIFYING_CODE) && (reserved2 == 5) && (header()->cputype == CPU_TYPE_I386);
- if ( (flags & S_ATTR_SELF_MODIFYING_CODE) && !selfModifyingStub ) {
- diag.error("S_ATTR_SELF_MODIFYING_CODE section type only valid in old i386 binaries");
- sectionStop = true;
- return;
- }
- uint32_t elementSize = selfModifyingStub ? reserved2 : pointerSize;
- uint32_t elementCount = (uint32_t)(size/elementSize);
- if (greaterThanAddOrOverflow(reserved1, elementCount, indirectSymbolTableCount)) {
- diag.error("section %s overflows indirect symbol table", sectionName);
- sectionStop = true;
- return;
- }
- __block uint32_t index = 0;
- __block uint32_t segIndex = 0;
- __block uint64_t sectionSegOffset;
- forEachSegment(^(const char* segmentName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool &segStop) {
- if ( (vmAddr <= addr) && (addr < vmAddr+vmSize) ) {
- sectionSegOffset = addr - vmAddr;
- segIndex = index;
- segStop = true;
- }
- ++index;
- });
-
- for (int i=0; (i < elementCount) && !stop; ++i) {
- uint32_t symNum = indirectSymbolTable[reserved1 + i];
- if ( symNum == INDIRECT_SYMBOL_ABS )
- continue;
- uint64_t segOffset = sectionSegOffset+i*elementSize;
- if ( symNum == INDIRECT_SYMBOL_LOCAL ) {
- handler(segIndex, segOffset, false, 0, "", false, false, false, stop);
- continue;
- }
- if ( symNum > symCount ) {
- diag.error("indirect symbol[%d] = %d which is invalid symbol index", reserved1 + i, symNum);
- sectionStop = true;
- return;
- }
- uint16_t n_desc = is64Bit ? symbols64[symNum].n_desc : symbols32[symNum].n_desc;
- uint32_t libOrdinal = libOrdinalFromDesc(n_desc);
- uint32_t strOffset = is64Bit ? symbols64[symNum].n_un.n_strx : symbols32[symNum].n_un.n_strx;
- if ( strOffset > poolSize ) {
- diag.error("symbol[%d] string offset out of range", reserved1 + i);
- sectionStop = true;
- return;
- }
- const char* symbolName = stringPool + strOffset;
- bool weakImport = (n_desc & N_WEAK_REF);
- bool lazy = (sectionType == S_LAZY_SYMBOL_POINTERS);
- handler(segIndex, segOffset, true, libOrdinal, symbolName, weakImport, lazy, selfModifyingStub, stop);
- }
- sectionStop = stop;
- });
-}
-
-void MachOParser::forEachInterposingTuple(Diagnostics& diag, void (^handler)(uint32_t segIndex, uint64_t replacementSegOffset, uint64_t replaceeSegOffset, uint64_t replacementContent, bool& stop)) const
-{
- const bool is64Bit = is64();
- const unsigned entrySize = is64Bit ? 16 : 8;
- const unsigned pointerSize = is64Bit ? 8 : 4;
- forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& secStop) {
- if ( ((flags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(sectionName, "__interpose") == 0) && (strcmp(segmentName, "__DATA") == 0)) ) {
- if ( (size % entrySize) != 0 ) {
- diag.error("interposing section %s/%s has bad size", segmentName, sectionName);
- secStop = true;
- return;
- }
- if ( illegalSectionSize ) {
- diag.error("interposing section %s/%s extends beyond the end of the segment", segmentName, sectionName);
- secStop = true;
- return;
- }
- if ( ((long)content % pointerSize) != 0 ) {
- diag.error("interposing section %s/%s is not pointer aligned", segmentName, sectionName);
- secStop = true;
- return;
- }
- __block uint32_t sectionSegIndex = 0;
- __block uint64_t sectionSegOffset = 0;
- forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& segStop) {
- if ( (vmAddr <= addr) && (addr < vmAddr+vmSize) ) {
- sectionSegIndex = segIndex;
- sectionSegOffset = addr - vmAddr;
- segStop = true;
- }
- });
- if ( sectionSegIndex == 0 ) {
- diag.error("interposing section %s/%s is not in a segment", segmentName, sectionName);
- secStop = true;
- return;
- }
- uint32_t offset = 0;
- bool tupleStop = false;
- for (int i=0; i < (size/entrySize); ++i) {
- uint64_t replacementContent = is64Bit ? (*(uint64_t*)((char*)content + offset)) : (*(uint32_t*)((char*)content + offset));
- handler(sectionSegIndex, sectionSegOffset+offset, sectionSegOffset+offset+pointerSize, replacementContent, tupleStop);
- offset += entrySize;
- if ( tupleStop )
- break;
- }
- }
- });
-}
-
-
-const void* MachOParser::content(uint64_t vmOffset)
-{
- __block const void* result = nullptr;
- __block uint32_t firstSegFileOffset = 0;
- __block uint64_t firstSegVmAddr = 0;
- if ( isRaw() ) {
- forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool &stop) {
- if ( firstSegFileOffset == 0) {
- if ( fileSize == 0 )
- return; // skip __PAGEZERO
- firstSegFileOffset = fileOffset;
- firstSegVmAddr = vmAddr;
- }
- uint64_t segVmOffset = vmAddr - firstSegVmAddr;
- if ( (vmOffset >= segVmOffset) && (vmOffset < segVmOffset+vmSize) ) {
- result = (char*)(header()) + (fileOffset - firstSegFileOffset) + (vmOffset - segVmOffset);
- stop = true;
- }
- });
- }
- else if ( inRawCache() ) {
- forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool &stop) {
- if ( firstSegFileOffset == 0 ) {
- firstSegFileOffset = fileOffset;
- firstSegVmAddr = vmAddr;
- }
- uint64_t segVmOffset = vmAddr - firstSegVmAddr;
- if ( (vmOffset >= segVmOffset) && (vmOffset < segVmOffset+vmSize) ) {
- result = (char*)(header()) + (fileOffset - firstSegFileOffset) + (vmOffset - segVmOffset);
- stop = true;
- }
- });
- }
- else {
- // non-raw cache is easy
- result = (char*)(header()) + vmOffset;
- }
- return result;
-}
-
-#endif // !DYLD_IN_PROCESS
-
-bool MachOParser::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size)
-{
- textOffset = 0;
- size = 0;
- Diagnostics diag;
- forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
- if ( (cmd->cmd == LC_ENCRYPTION_INFO) || (cmd->cmd == LC_ENCRYPTION_INFO_64) ) {
- const encryption_info_command* encCmd = (encryption_info_command*)cmd;
- if ( encCmd->cryptid == 1 ) {
- // Note: cryptid is 0 in just-built apps. The iTunes App Store sets cryptid to 1
- textOffset = encCmd->cryptoff;
- size = encCmd->cryptsize;
- }
- stop = true;
- }
- });
- diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
- return (textOffset != 0);
-}
-
-bool MachOParser::cdHashOfCodeSignature(const void* codeSigStart, size_t codeSignLen, uint8_t cdHash[20])
-{
- const CS_CodeDirectory* cd = (const CS_CodeDirectory*)findCodeDirectoryBlob(codeSigStart, codeSignLen);
- if ( cd == nullptr )
- return false;
-
- uint32_t cdLength = htonl(cd->length);
- if ( cd->hashType == CS_HASHTYPE_SHA256 ) {
- uint8_t digest[CC_SHA256_DIGEST_LENGTH];
- CC_SHA256(cd, cdLength, digest);
- // cd-hash of sigs that use SHA256 is the first 20 bytes of the SHA256 of the code digest
- memcpy(cdHash, digest, 20);
- return true;
- }
- else if ( cd->hashType == CS_HASHTYPE_SHA1 ) {
- // compute hash directly into return buffer
- CC_SHA1(cd, cdLength, cdHash);
- return true;
- }
-
- return false;
-}
-
-const void* MachOParser::findCodeDirectoryBlob(const void* codeSigStart, size_t codeSignLen)
-{
- // verify min length of overall code signature
- if ( codeSignLen < sizeof(CS_SuperBlob) )
- return nullptr;
-
- // verify magic at start
- const CS_SuperBlob* codeSuperBlob = (CS_SuperBlob*)codeSigStart;
- if ( codeSuperBlob->magic != htonl(CSMAGIC_EMBEDDED_SIGNATURE) )
- return nullptr;
-
- // verify count of sub-blobs not too large
- uint32_t subBlobCount = htonl(codeSuperBlob->count);
- if ( (codeSignLen-sizeof(CS_SuperBlob))/sizeof(CS_BlobIndex) < subBlobCount )
- return nullptr;
-
- // walk each sub blob, looking at ones with type CSSLOT_CODEDIRECTORY
- for (uint32_t i=0; i < subBlobCount; ++i) {
- if ( codeSuperBlob->index[i].type != htonl(CSSLOT_CODEDIRECTORY) )
- continue;
- uint32_t cdOffset = htonl(codeSuperBlob->index[i].offset);
- // verify offset is not out of range
- if ( cdOffset > (codeSignLen - sizeof(CS_CodeDirectory)) )
- return nullptr;
- const CS_CodeDirectory* cd = (CS_CodeDirectory*)((uint8_t*)codeSuperBlob + cdOffset);
- uint32_t cdLength = htonl(cd->length);
- // verify code directory length not out of range
- if ( cdLength > (codeSignLen - cdOffset) )
- return nullptr;
- if ( cd->magic == htonl(CSMAGIC_CODEDIRECTORY) )
- return cd;
- }
- return nullptr;
-}
-
-
-
-
-} // namespace dyld3
-