X-Git-Url: https://git.saurik.com/apple/ld64.git/blobdiff_plain/afe874b1634377ecb27057ee76deb04915bb34d7..9543cb2f21e50a417dc8cf37eb7173f353536979:/src/other/machochecker.cpp?ds=inline diff --git a/src/other/machochecker.cpp b/src/other/machochecker.cpp index 4841c7c..aec6ebe 100644 --- a/src/other/machochecker.cpp +++ b/src/other/machochecker.cpp @@ -33,7 +33,9 @@ #include #include -#include +#include + +#include "configure.h" #include "MachOFileAbstraction.hpp" #include "Architectures.hpp" @@ -108,13 +110,21 @@ private: typedef typename A::P::E E; typedef typename A::P::uint_t pint_t; - class CStringEquals + // utility classes for using std::unordered_map with c-strings + struct CStringHash { + size_t operator()(const char* __s) const { + size_t __h = 0; + for ( ; *__s; ++__s) + __h = 5 * __h + *__s; + return __h; + }; + }; + struct CStringEquals { - public: bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } }; - typedef __gnu_cxx::hash_set, CStringEquals> StringSet; + typedef std::unordered_set StringSet; MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path); void checkMachHeader(); @@ -254,11 +264,34 @@ bool MachOChecker::validFile(const uint8_t* fileContent) return false; } +#if SUPPORT_ARCH_arm64 +template <> +bool MachOChecker::validFile(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_ARM64 ) + return false; + switch (header->filetype()) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + case MH_DYLINKER: + return true; + } + return false; +} +#endif + template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } +#if SUPPORT_ARCH_arm64 +template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } +#endif template <> @@ -291,9 +324,13 @@ arm::P::uint_t MachOChecker::getInitialStackPointer(const macho_thread_comm return threadInfo->thread_register(13); } - - - +#if SUPPORT_ARCH_arm64 +template <> +arm64::P::uint_t MachOChecker::getInitialStackPointer(const macho_thread_command* threadInfo) +{ + throw "LC_UNIXTHREAD not supported for arm64"; +} +#endif template <> ppc::P::uint_t MachOChecker::getEntryPoint(const macho_thread_command* threadInfo) @@ -325,6 +362,13 @@ arm::P::uint_t MachOChecker::getEntryPoint(const macho_thread_commandthread_register(15); } +#if SUPPORT_ARCH_arm64 +template <> +arm64::P::uint_t MachOChecker::getEntryPoint(const macho_thread_command* threadInfo) +{ + throw "LC_UNIXTHREAD not supported for arm64"; +} +#endif template MachOChecker::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path) @@ -388,6 +432,7 @@ void MachOChecker::checkLoadCommands() // check that all load commands fit within the load command space file const macho_encryption_info_command

* encryption_info = NULL; const macho_thread_command

* threadInfo = NULL; + const macho_entry_point_command

* entryPoint = NULL; const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength; const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header

) + fHeader->sizeofcmds(); const uint32_t cmd_count = fHeader->ncmds(); @@ -424,14 +469,19 @@ void MachOChecker::checkLoadCommands() case LC_LOAD_UPWARD_DYLIB: case LC_VERSION_MIN_MACOSX: case LC_VERSION_MIN_IPHONEOS: - case LC_FUNCTION_STARTS: case LC_RPATH: + case LC_FUNCTION_STARTS: + case LC_DYLD_ENVIRONMENT: + case LC_DATA_IN_CODE: + case LC_DYLIB_CODE_SIGN_DRS: + case LC_SOURCE_VERSION: break; case LC_DYLD_INFO: case LC_DYLD_INFO_ONLY: fDyldInfo = (macho_dyld_info_command

*)cmd; break; case LC_ENCRYPTION_INFO: + case LC_ENCRYPTION_INFO_64: encryption_info = (macho_encryption_info_command

*)cmd; break; case LC_SUB_UMBRELLA: @@ -439,6 +489,11 @@ void MachOChecker::checkLoadCommands() if ( fHeader->flags() & MH_NO_REEXPORTED_DYLIBS ) throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags should not be set in an image with LC_SUB_LIBRARY or LC_SUB_UMBRELLA"; break; + case LC_MAIN: + if ( fHeader->filetype() != MH_EXECUTE ) + throw "LC_MAIN can only be used in MH_EXECUTE file types"; + entryPoint = (macho_entry_point_command

*)cmd; + break; case LC_UNIXTHREAD: if ( fHeader->filetype() != MH_EXECUTE ) throw "LC_UNIXTHREAD can only be used in MH_EXECUTE file types"; @@ -590,7 +645,11 @@ void MachOChecker::checkLoadCommands() if ( (initialPC < fTEXTSegment->vmaddr()) || (initialPC >= (fTEXTSegment->vmaddr()+fTEXTSegment->vmsize())) ) throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialPC); } - + else if ( entryPoint != NULL ) { + pint_t initialOffset = entryPoint->entryoff(); + if ( (initialOffset < fTEXTSegment->fileoff()) || (initialOffset >= (fTEXTSegment->fileoff()+fTEXTSegment->filesize())) ) + throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialOffset); + } // checks for executables bool isStaticExecutable = false; @@ -607,7 +666,7 @@ void MachOChecker::checkLoadCommands() cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } if ( isStaticExecutable ) { - if ( fHeader->flags() != MH_NOUNDEFS ) + if ( (fHeader->flags() != MH_NOUNDEFS) && (fHeader->flags() != (MH_NOUNDEFS|MH_PIE)) ) throw "invalid bits in mach_header flags for static executable"; } } @@ -661,7 +720,7 @@ void MachOChecker::checkLoadCommands() break; case LC_DYSYMTAB: { - if ( isStaticExecutable ) + if ( isStaticExecutable &&! fSlidableImage ) throw "LC_DYSYMTAB should not be used in static executable"; foundDynamicSymTab = true; fDynamicSymbolTable = (macho_dysymtab_command

*)cmd; @@ -725,6 +784,32 @@ void MachOChecker::checkLoadCommands() throw "function starts data size not a multiple of pointer size"; } break; + case LC_DATA_IN_CODE: + { + const macho_linkedit_data_command

* info = (macho_linkedit_data_command

*)cmd; + if ( info->dataoff() < linkEditSegment->fileoff() ) + throw "data-in-code data not in __LINKEDIT"; + if ( (info->dataoff()+info->datasize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "data-in-code data not in __LINKEDIT"; + if ( (info->dataoff() % sizeof(pint_t)) != 0 ) + throw "data-in-code data table not pointer aligned"; + if ( (info->datasize() % sizeof(pint_t)) != 0 ) + throw "data-in-code data size not a multiple of pointer size"; + } + break; + case LC_DYLIB_CODE_SIGN_DRS: + { + const macho_linkedit_data_command

* info = (macho_linkedit_data_command

*)cmd; + if ( info->dataoff() < linkEditSegment->fileoff() ) + throw "dependent dylib DR data not in __LINKEDIT"; + if ( (info->dataoff()+info->datasize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "dependent dylib DR data not in __LINKEDIT"; + if ( (info->dataoff() % sizeof(pint_t)) != 0 ) + throw "dependent dylib DR data table not pointer aligned"; + if ( (info->datasize() % sizeof(pint_t)) != 0 ) + throw "dependent dylib DR data size not a multiple of pointer size"; + } + break; } cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } @@ -943,6 +1028,15 @@ arm::P::uint_t MachOChecker::relocBase() return fFirstSegment->vmaddr(); } +#if SUPPORT_ARCH_arm64 +template <> +arm64::P::uint_t MachOChecker::relocBase() +{ + return fFirstWritableSegment->vmaddr(); +} +#endif + + template bool MachOChecker::addressInWritableSegment(pint_t address) @@ -1039,6 +1133,7 @@ void MachOChecker::checkExternalReloation(const macho_relocation_info

// FIX: check r_symbol } +#if SUPPORT_ARCH_arm_any template <> void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) { @@ -1054,6 +1149,15 @@ void MachOChecker::checkExternalReloation(const macho_relocation_info

* r throw "external relocation address not in writable segment"; // FIX: check r_symbol } +#endif + +#if SUPPORT_ARCH_arm64 +template <> +void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) +{ + throw "external relocations not used for arm64"; +} +#endif template <> @@ -1066,9 +1170,6 @@ void MachOChecker::checkLocalReloation(const macho_relocation_info

* relo } else { - // ignore pair relocs - if ( reloc->r_type() == PPC_RELOC_PAIR ) - return; // FIX if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) throwf("local relocation address 0x%08X not in writable segment", reloc->r_address()); @@ -1112,6 +1213,7 @@ void MachOChecker::checkLocalReloation(const macho_relocation_info

* r throw "local relocation address not in writable segment"; } +#if SUPPORT_ARCH_arm_any template <> void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) { @@ -1132,6 +1234,16 @@ void MachOChecker::checkLocalReloation(const macho_relocation_info

* relo throw "local relocation address not in writable segment"; } } +#endif + +#if SUPPORT_ARCH_arm64 +template <> +void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) +{ + throw "local relocations not used for arm64"; +} +#endif + template void MachOChecker::checkRelocations() @@ -1276,6 +1388,7 @@ bool MachOChecker::hasTextRelocInRange(pint_t rangeStart, pint_t rangeEnd) } } } + return false; } template @@ -1521,12 +1634,14 @@ static void check(const char* path) else throw "in universal file, x86_64 slice does not contain x86_64 mach-o"; break; +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: if ( MachOChecker::validFile(p + offset) ) MachOChecker::make(p + offset, size, path); else throw "in universal file, arm slice does not contain arm mach-o"; break; +#endif default: throwf("in universal file, unknown architecture slice 0x%x\n", cputype); } @@ -1544,9 +1659,16 @@ static void check(const char* path) else if ( MachOChecker::validFile(p) ) { MachOChecker::make(p, length, path); } +#if SUPPORT_ARCH_arm_any else if ( MachOChecker::validFile(p) ) { MachOChecker::make(p, length, path); } +#endif +#if SUPPORT_ARCH_arm64 + else if ( MachOChecker::validFile(p) ) { + MachOChecker::make(p, length, path); + } +#endif else { throw "not a known file type"; }