X-Git-Url: https://git.saurik.com/apple/ld64.git/blobdiff_plain/eaf282aaf65b222563e6b3db98e12d720fb161bf..7f09b9353af9897bf18933788d6a59c152c29edd:/src/ld/parsers/macho_dylib_file.cpp diff --git a/src/ld/parsers/macho_dylib_file.cpp b/src/ld/parsers/macho_dylib_file.cpp index 0ba962c..56b327a 100644 --- a/src/ld/parsers/macho_dylib_file.cpp +++ b/src/ld/parsers/macho_dylib_file.cpp @@ -34,219 +34,59 @@ #include #include #include -#include -#include #include "Architectures.hpp" #include "Bitcode.hpp" #include "MachOFileAbstraction.hpp" #include "MachOTrie.hpp" +#include "generic_dylib_file.hpp" #include "macho_dylib_file.h" #include "../code-sign-blobs/superblob.h" namespace mach_o { namespace dylib { - -// forward reference -template class File; - - -// -// An ExportAtom has no content. It exists so that the linker can track which imported -// symbols came from which dynamic libraries. -// -template -class ExportAtom : public ld::Atom -{ -public: - ExportAtom(const File& f, const char* nm, bool weakDef, - bool tlv, typename A::P::uint_t address) - : ld::Atom(f._importProxySection, ld::Atom::definitionProxy, - (weakDef? ld::Atom::combineByName : ld::Atom::combineNever), - ld::Atom::scopeLinkageUnit, - (tlv ? ld::Atom::typeTLV : ld::Atom::typeUnclassified), - symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), - _file(f), _name(nm), _address(address) {} - // overrides of ld::Atom - virtual const ld::File* file() const { return &_file; } - virtual const char* name() const { return _name; } - virtual uint64_t size() const { return 0; } - virtual uint64_t objectAddress() const { return _address; } - virtual void copyRawContent(uint8_t buffer[]) const { } - virtual void setScope(Scope) { } - -protected: - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; - - virtual ~ExportAtom() {} - - const File& _file; - const char* _name; - pint_t _address; -}; - - - -// -// An ImportAtom has no content. It exists so that when linking a main executable flat-namespace -// the imports of all flat dylibs are checked -// -template -class ImportAtom : public ld::Atom -{ -public: - ImportAtom(File& f, std::vector& imports); - - // overrides of ld::Atom - virtual ld::File* file() const { return &_file; } - virtual const char* name() const { return "import-atom"; } - virtual uint64_t size() const { return 0; } - virtual uint64_t objectAddress() const { return 0; } - virtual void copyRawContent(uint8_t buffer[]) const { } - virtual void setScope(Scope) { } - virtual ld::Fixup::iterator fixupsBegin() const { return &_undefs[0]; } - virtual ld::Fixup::iterator fixupsEnd() const { return &_undefs[_undefs.size()]; } - -protected: - typedef typename A::P P; - - virtual ~ImportAtom() {} - - - File& _file; - mutable std::vector _undefs; -}; - -template -ImportAtom::ImportAtom(File& f, std::vector& imports) -: ld::Atom(f._flatDummySection, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, - ld::Atom::typeUnclassified, symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), _file(f) -{ - for(std::vector::iterator it=imports.begin(); it != imports.end(); ++it) { - _undefs.push_back(ld::Fixup(0, ld::Fixup::k1of1, ld::Fixup::kindNone, false, strdup(*it))); - } -} - - - // // The reader for a dylib extracts all exported symbols names from the memory-mapped // dylib, builds a hash table, then unmaps the file. This is an important memory // savings for large dylibs. // template -class File : public ld::dylib::File +class File final : public generic::dylib::File { + using Base = generic::dylib::File; + public: - static bool validFile(const uint8_t* fileContent, bool executableOrDylib); + static bool validFile(const uint8_t* fileContent, bool executableOrDylib, bool subTypeMustMatch=false); File(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, - Options::Platform platform, uint32_t linkMinOSVersion, bool allowSimToMacOSX, - bool addVers, bool buildingForSimulator, + Options::Platform platform, uint32_t linkMinOSVersion, bool allowWeakImports, + bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, bool logAllFiles, const char* installPath, - bool indirectDylib, bool ignoreMismatchPlatform); - virtual ~File() {} - - // overrides of ld::File - virtual bool forEachAtom(ld::File::AtomHandler&) const; - virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const; - virtual ld::File::ObjcConstraint objCConstraint() const { return _objcContraint; } - virtual uint8_t swiftVersion() const { return _swiftVersion; } - virtual uint32_t minOSVersion() const { return _minVersionInDylib; } - virtual uint32_t platformLoadCommand() const { return _platformInDylib; } - - // overrides of ld::dylib::File - virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool); - virtual bool providedExportAtom() const { return _providedAtom; } - virtual const char* parentUmbrella() const { return _parentUmbrella; } - virtual const std::vector* allowableClients() const { return _allowableClients.size() != 0 ? &_allowableClients : NULL; } - virtual bool hasWeakExternals() const { return _hasWeakExports; } - virtual bool deadStrippable() const { return _deadStrippable; } - virtual bool hasPublicInstallName() const{ return _hasPublicInstallName; } - virtual bool hasWeakDefinition(const char* name) const; - virtual bool allSymbolsAreWeakImported() const; - virtual bool installPathVersionSpecific() const { return _installPathOverride; } - virtual bool appExtensionSafe() const { return _appExtensionSafe; }; - virtual ld::Bitcode* getBitcode() const { return _bitcode.get(); } - -protected: - virtual void assertNoReExportCycles(ReExportChain*) const; + bool indirectDylib, bool ignoreMismatchPlatform, bool usingBitcode); + virtual ~File() noexcept {} private: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - - friend class ExportAtom; - friend class ImportAtom; - - struct CStringHash { - std::size_t operator()(const char* __s) const { - unsigned long __h = 0; - for ( ; *__s; ++__s) - __h = 5 * __h + *__s; - return size_t(__h); - }; - }; - struct AtomAndWeak { ld::Atom* atom; bool weakDef; bool tlv; uint64_t address; }; - typedef std::unordered_map NameToAtomMap; - typedef std::unordered_set NameSet; - - struct Dependent { const char* path; File* dylib; bool reExport; }; + using P = typename A::P; + using E = typename A::P::E; + using pint_t = typename A::P::uint_t; - virtual std::pair hasWeakDefinitionImpl(const char* name) const; - virtual bool containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& defAddress) const; - bool isPublicLocation(const char* pth); - bool wrongOS() { return _wrongOS; } - void addSymbol(const char* name, bool weak, bool tlv, pint_t address); - void addDyldFastStub(); - void buildExportHashTableFromExportInfo(const macho_dyld_info_command

* dyldInfo, + void addDyldFastStub(); + void buildExportHashTableFromExportInfo(const macho_dyld_info_command

* dyldInfo, const uint8_t* fileContent); - void buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, + void buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, const macho_nlist

* symbolTable, const char* strings, const uint8_t* fileContent); - static uint32_t parseVersionNumber32(const char* versionString); - static const char* objCInfoSegmentName(); - static const char* objCInfoSectionName(); + void addSymbol(const char* name, bool weakDef = false, bool tlv = false, pint_t address = 0); + static const char* objCInfoSegmentName(); + static const char* objCInfoSectionName(); - const Options::Platform _platform; - const uint32_t _linkMinOSVersion; - const bool _allowSimToMacOSXLinking; - const bool _addVersionLoadCommand; - bool _linkingFlat; - bool _implicitlyLinkPublicDylibs; - ld::File::ObjcConstraint _objcContraint; - uint8_t _swiftVersion; - ld::Section _importProxySection; - ld::Section _flatDummySection; - std::vector _dependentDylibs; - std::vector _allowableClients; - mutable NameToAtomMap _atoms; - NameSet _ignoreExports; - const char* _parentUmbrella; - ImportAtom* _importAtom; - bool _noRexports; - bool _hasWeakExports; - bool _deadStrippable; - bool _hasPublicInstallName; - mutable bool _providedAtom; - bool _explictReExportFound; - bool _wrongOS; - bool _installPathOverride; - bool _indirectDylibsProcessed; - bool _appExtensionSafe; - uint32_t _minVersionInDylib; - uint32_t _platformInDylib; - std::unique_ptr _bitcode; - - static bool _s_logHashtable; -}; -template -bool File::_s_logHashtable = false; + uint64_t _fileLength; + uint32_t _linkeditStartOffset; + +}; template <> const char* File::objCInfoSegmentName() { return "__DATA"; } template <> const char* File::objCInfoSegmentName() { return "__DATA"; } @@ -257,22 +97,13 @@ template <> const char* File::objCInfoSectionName() { return "__objc_imagei template const char* File::objCInfoSectionName() { return "__image_info"; } template -File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, time_t mTime, ld::File::Ordinal ord, - bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, - Options::Platform platform, uint32_t linkMinOSVersion, bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, - bool logAllFiles, const char* targetInstallPath, bool indirectDylib, bool ignoreMismatchPlatform) - : ld::dylib::File(strdup(pth), mTime, ord), - _platform(platform), _linkMinOSVersion(linkMinOSVersion), _allowSimToMacOSXLinking(allowSimToMacOSX), _addVersionLoadCommand(addVers), - _linkingFlat(linkingFlatNamespace), _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs), - _objcContraint(ld::File::objcConstraintNone), _swiftVersion(0), - _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true), - _flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true), - _parentUmbrella(NULL), _importAtom(NULL), - _noRexports(false), _hasWeakExports(false), - _deadStrippable(false), _hasPublicInstallName(false), - _providedAtom(false), _explictReExportFound(false), _wrongOS(false), _installPathOverride(false), - _indirectDylibsProcessed(false), _appExtensionSafe(false), - _minVersionInDylib(0), _platformInDylib(Options::kPlatformUnknown) +File::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime, + ld::File::Ordinal ord, bool linkingFlatNamespace, bool linkingMainExecutable, + bool hoistImplicitPublicDylibs, Options::Platform platform, uint32_t linkMinOSVersion, bool allowWeakImports, + bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, bool logAllFiles, + const char* targetInstallPath, bool indirectDylib, bool ignoreMismatchPlatform, bool usingBitcode) + : Base(strdup(path), mTime, ord, platform, linkMinOSVersion, allowWeakImports, linkingFlatNamespace, + hoistImplicitPublicDylibs, allowSimToMacOSX, addVers), _fileLength(fileLength), _linkeditStartOffset(0) { const macho_header

* header = (const macho_header

*)fileContent; const uint32_t cmd_count = header->ncmds(); @@ -281,7 +112,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, // write out path for -t option if ( logAllFiles ) - printf("%s\n", pth); + printf("%s\n", path); // a "blank" stub has zero load commands if ( (header->filetype() == MH_DYLIB_STUB) && (cmd_count == 0) ) { @@ -292,32 +123,33 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, // optimize the case where we know there is no reason to look at indirect dylibs - _noRexports = (header->flags() & MH_NO_REEXPORTED_DYLIBS) - || (header->filetype() == MH_BUNDLE) - || (header->filetype() == MH_EXECUTE); // bundles and exectuables can be used via -bundle_loader - _hasWeakExports = (header->flags() & MH_WEAK_DEFINES); - _deadStrippable = (header->flags() & MH_DEAD_STRIPPABLE_DYLIB); - _appExtensionSafe = (header->flags() & MH_APP_EXTENSION_SAFE); + this->_noRexports = (header->flags() & MH_NO_REEXPORTED_DYLIBS) + || (header->filetype() == MH_BUNDLE) + || (header->filetype() == MH_EXECUTE); // bundles and exectuables can be used via -bundle_loader + this->_hasWeakExports = (header->flags() & MH_WEAK_DEFINES); + this->_deadStrippable = (header->flags() & MH_DEAD_STRIPPABLE_DYLIB); + this->_appExtensionSafe = (header->flags() & MH_APP_EXTENSION_SAFE); // pass 1: get pointers, and see if this dylib uses compressed LINKEDIT format - const macho_dysymtab_command

* dynamicInfo = NULL; - const macho_dyld_info_command

* dyldInfo = NULL; - const macho_nlist

* symbolTable = NULL; - const char* strings = NULL; + const macho_dysymtab_command

* dynamicInfo = nullptr; + const macho_dyld_info_command

* dyldInfo = nullptr; + const macho_nlist

* symbolTable = nullptr; + const macho_symtab_command

* symtab = nullptr; + const char* strings = nullptr; bool compressedLinkEdit = false; uint32_t dependentLibCount = 0; Options::Platform lcPlatform = Options::kPlatformUnknown; const macho_load_command

* cmd = cmds; for (uint32_t i = 0; i < cmd_count; ++i) { macho_dylib_command

* dylibID; - const macho_symtab_command

* symtab; + uint32_t cmdLength = cmd->cmdsize(); switch (cmd->cmd()) { case LC_SYMTAB: symtab = (macho_symtab_command

*)cmd; symbolTable = (const macho_nlist

*)((char*)header + symtab->symoff()); strings = (char*)header + symtab->stroff(); if ( (symtab->stroff() + symtab->strsize()) > fileLength ) - throwf("mach-o string pool extends beyond end of file in %s", pth); + throwf("mach-o string pool extends beyond end of file in %s", path); break; case LC_DYSYMTAB: dynamicInfo = (macho_dysymtab_command

*)cmd; @@ -329,27 +161,34 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, break; case LC_ID_DYLIB: dylibID = (macho_dylib_command

*)cmd; - _dylibInstallPath = strdup(dylibID->name()); - _dylibTimeStamp = dylibID->timestamp(); - _dylibCurrentVersion = dylibID->current_version(); - _dylibCompatibilityVersion = dylibID->compatibility_version(); - _hasPublicInstallName = isPublicLocation(_dylibInstallPath); + if ( dylibID->name_offset() > cmdLength ) + throwf("malformed mach-o: LC_ID_DYLIB load command has offset (%u) outside its size (%u)", dylibID->name_offset(), cmdLength); + if ( (dylibID->name_offset() + strlen(dylibID->name()) + 1) > cmdLength ) + throwf("malformed mach-o: LC_ID_DYLIB load command string extends beyond end of load command"); + this->_dylibInstallPath = strdup(dylibID->name()); + this->_dylibTimeStamp = dylibID->timestamp(); + this->_dylibCurrentVersion = dylibID->current_version(); + this->_dylibCompatibilityVersion = dylibID->compatibility_version(); + this->_hasPublicInstallName = this->isPublicLocation(this->_dylibInstallPath); break; case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: ++dependentLibCount; break; case LC_REEXPORT_DYLIB: - _explictReExportFound = true; + this->_explictReExportFound = true; ++dependentLibCount; break; case LC_SUB_FRAMEWORK: - _parentUmbrella = strdup(((macho_sub_framework_command

*)cmd)->umbrella()); + this->_parentUmbrella = strdup(((macho_sub_framework_command

*)cmd)->umbrella()); break; case LC_SUB_CLIENT: - _allowableClients.push_back(strdup(((macho_sub_client_command

*)cmd)->client())); + this->_allowableClients.push_back(strdup(((macho_sub_client_command

*)cmd)->client())); // Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked - _hasPublicInstallName = false; + this->_hasPublicInstallName = false; + break; + case LC_RPATH: + this->_rpaths.push_back(strdup(((macho_rpath_command

*)cmd)->path())); break; case LC_VERSION_MIN_MACOSX: case LC_VERSION_MIN_IPHONEOS: @@ -357,9 +196,9 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, #if SUPPORT_APPLE_TV case LC_VERSION_MIN_TVOS: #endif - _minVersionInDylib = (ld::MacVersionMin)((macho_version_min_command

*)cmd)->version(); - _platformInDylib = cmd->cmd(); - lcPlatform = Options::platformForLoadCommand(_platformInDylib); + this->_minVersionInDylib = (ld::MacVersionMin)((macho_version_min_command

*)cmd)->version(); + this->_platformInDylib = cmd->cmd(); + lcPlatform = Options::platformForLoadCommand(this->_platformInDylib); break; case LC_CODE_SIGNATURE: break; @@ -383,17 +222,17 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, if ( (sect->size() >= 8) && (contents[0] == 0) ) { uint32_t flags = E::get32(contents[1]); if ( (flags & 4) == 4 ) - _objcContraint = ld::File::objcConstraintGC; + this->_objcConstraint = ld::File::objcConstraintGC; else if ( (flags & 2) == 2 ) - _objcContraint = ld::File::objcConstraintRetainReleaseOrGC; + this->_objcConstraint = ld::File::objcConstraintRetainReleaseOrGC; else if ( (flags & 32) == 32 ) - _objcContraint = ld::File::objcConstraintRetainReleaseForSimulator; + this->_objcConstraint = ld::File::objcConstraintRetainReleaseForSimulator; else - _objcContraint = ld::File::objcConstraintRetainRelease; - _swiftVersion = ((flags >> 8) & 0xFF); + this->_objcConstraint = ld::File::objcConstraintRetainRelease; + this->_swiftVersion = ((flags >> 8) & 0xFF); } else if ( sect->size() > 0 ) { - warning("can't parse %s/%s section in %s", objCInfoSegmentName(), objCInfoSectionName(), this->path()); + warning("can't parse %s/%s section in %s", objCInfoSegmentName(), objCInfoSectionName(), path); } } } @@ -403,12 +242,15 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, else if ( strcmp(((macho_segment_command

*)cmd)->segname(), "__LLVM") == 0 ) { const macho_section

* const sect = (macho_section

*)((char*)cmd + sizeof(macho_segment_command

)); if ( strncmp(sect->sectname(), "__bundle", 8) == 0 ) - _bitcode = std::unique_ptr(new ld::Bitcode(NULL, sect->size())); + this->_bitcode = std::unique_ptr(new ld::Bitcode(NULL, sect->size())); + } + else if ( strcmp(((macho_segment_command

*)cmd)->segname(), "__LINKEDIT") == 0 ) { + _linkeditStartOffset = ((macho_segment_command

*)cmd)->fileoff(); } } - cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + cmd = (const macho_load_command

*)(((char*)cmd)+cmdLength); if ( cmd > cmdsEnd ) - throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, pth); + throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path); } // arm/arm64 objects are default to ios platform if not set. // rdar://problem/21746314 @@ -418,10 +260,10 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, // check cross-linking if ( lcPlatform != platform ) { - _wrongOS = true; - if ( _addVersionLoadCommand && !indirectDylib && !ignoreMismatchPlatform ) { + this->_wrongOS = true; + if ( this->_addVersionLoadCommand && !indirectDylib && !ignoreMismatchPlatform ) { if ( buildingForSimulator ) { - if ( !_allowSimToMacOSXLinking ) { + if ( !this->_allowSimToMacOSXLinking ) { switch (platform) { case Options::kPlatformOSX: case Options::kPlatformiOS: @@ -429,18 +271,28 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, break; // fall through if the Platform is not Unknown case Options::kPlatformWatchOS: - // WatchOS errors on cross-linking all the time. - throwf("building for %s simulator, but linking against dylib built for %s,", + // WatchOS errors on cross-linking when building for bitcode + if ( usingBitcode ) + throwf("building for %s simulator, but linking against dylib built for %s,", Options::platformName(platform), Options::platformName(lcPlatform)); + else + warning("URGENT: building for %s simulator, but linking against dylib (%s) built for %s. " + "Note: This will be an error in the future.", + Options::platformName(platform), path, + Options::platformName(lcPlatform)); break; #if SUPPORT_APPLE_TV case Options::kPlatform_tvOS: // tvOS is a warning temporarily. rdar://problem/21746965 - if (platform == Options::kPlatform_tvOS) + if ( usingBitcode ) + throwf("building for %s simulator, but linking against dylib built for %s,", + Options::platformName(platform), + Options::platformName(lcPlatform)); + else warning("URGENT: building for %s simulator, but linking against dylib (%s) built for %s. " "Note: This will be an error in the future.", - Options::platformName(platform), path(), + Options::platformName(platform), path, Options::platformName(lcPlatform)); break; #endif @@ -458,18 +310,28 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, break; // fall through if the Platform is not Unknown case Options::kPlatformWatchOS: - // WatchOS errors on cross-linking all the time. - throwf("building for %s, but linking against dylib built for %s,", + // WatchOS errors on cross-linking when building for bitcode + if ( usingBitcode ) + throwf("building for %s, but linking against dylib built for %s,", Options::platformName(platform), Options::platformName(lcPlatform)); + else + warning("URGENT: building for %s, but linking against dylib (%s) built for %s. " + "Note: This will be an error in the future.", + Options::platformName(platform), path, + Options::platformName(lcPlatform)); break; #if SUPPORT_APPLE_TV case Options::kPlatform_tvOS: // tvOS is a warning temporarily. rdar://problem/21746965 - if (platform == Options::kPlatform_tvOS) + if ( usingBitcode ) + throwf("building for %s, but linking against dylib built for %s,", + Options::platformName(platform), + Options::platformName(lcPlatform)); + else warning("URGENT: building for %s, but linking against dylib (%s) built for %s. " "Note: This will be an error in the future.", - Options::platformName(platform), path(), + Options::platformName(platform), path, Options::platformName(lcPlatform)); break; #endif @@ -484,15 +346,17 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, // figure out if we need to examine dependent dylibs // with compressed LINKEDIT format, MH_NO_REEXPORTED_DYLIBS can be trusted bool processDependentLibraries = true; - if ( compressedLinkEdit && _noRexports && !linkingFlatNamespace) + if ( compressedLinkEdit && this->_noRexports && !linkingFlatNamespace) processDependentLibraries = false; if ( processDependentLibraries ) { // pass 2 builds list of all dependent libraries - _dependentDylibs.reserve(dependentLibCount); + this->_dependentDylibs.reserve(dependentLibCount); cmd = cmds; unsigned int reExportDylibCount = 0; for (uint32_t i = 0; i < cmd_count; ++i) { + uint32_t cmdLength = cmd->cmdsize(); + const macho_dylib_command

* dylibCmd = (macho_dylib_command

*)cmd; switch (cmd->cmd()) { case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: @@ -501,20 +365,22 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, break; case LC_REEXPORT_DYLIB: ++reExportDylibCount; - Dependent entry; - entry.path = strdup(((macho_dylib_command

*)cmd)->name()); - entry.dylib = NULL; - entry.reExport = (cmd->cmd() == LC_REEXPORT_DYLIB); - if ( (targetInstallPath == NULL) || (strcmp(targetInstallPath, entry.path) != 0) ) - _dependentDylibs.push_back(entry); + if ( dylibCmd->name_offset() > cmdLength ) + throwf("malformed mach-o: LC_*_DYLIB load command has offset (%u) outside its size (%u)", dylibCmd->name_offset(), cmdLength); + if ( (dylibCmd->name_offset() + strlen(dylibCmd->name()) + 1) > cmdLength ) + throwf("malformed mach-o: LC_*_DYLIB load command string extends beyond end of load command"); + const char *path = strdup(dylibCmd->name()); + bool reExport = (cmd->cmd() == LC_REEXPORT_DYLIB); + if ( (targetInstallPath == nullptr) || (strcmp(targetInstallPath, path) != 0) ) + this->_dependentDylibs.emplace_back(path, reExport); break; } - cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + cmd = (const macho_load_command

*)(((char*)cmd)+cmdLength); } // verify MH_NO_REEXPORTED_DYLIBS bit was correct if ( compressedLinkEdit && !linkingFlatNamespace ) { if ( reExportDylibCount == 0 ) - throwf("malformed dylib has MH_NO_REEXPORTED_DYLIBS flag but no LC_REEXPORT_DYLIB load commands: %s", pth); + throwf("malformed dylib has MH_NO_REEXPORTED_DYLIBS flag but no LC_REEXPORT_DYLIB load commands: %s", path); } // pass 3 add re-export info cmd = cmds; @@ -524,44 +390,65 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, switch (cmd->cmd()) { case LC_SUB_UMBRELLA: frameworkLeafName = ((macho_sub_umbrella_command

*)cmd)->sub_umbrella(); - for (typename std::vector::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) { - const char* dylibName = it->path; + for (auto &dep : this->_dependentDylibs) { + const char* dylibName = dep.path; const char* lastSlash = strrchr(dylibName, '/'); - if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) - it->reExport = true; + if ( (lastSlash != nullptr) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) + dep.reExport = true; } break; case LC_SUB_LIBRARY: dylibBaseName = ((macho_sub_library_command

*)cmd)->sub_library(); - for (typename std::vector::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) { - const char* dylibName = it->path; + for (auto &dep : this->_dependentDylibs) { + const char* dylibName = dep.path; const char* lastSlash = strrchr(dylibName, '/'); const char* leafStart = &lastSlash[1]; - if ( lastSlash == NULL ) + if ( lastSlash == nullptr ) leafStart = dylibName; const char* firstDot = strchr(leafStart, '.'); int len = strlen(leafStart); - if ( firstDot != NULL ) + if ( firstDot != nullptr ) len = firstDot - leafStart; if ( strncmp(leafStart, dylibBaseName, len) == 0 ) - it->reExport = true; + dep.reExport = true; } break; } cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); } } - + + // if framework, capture framework name + if ( this->_dylibInstallPath != NULL ) { + const char* lastSlash = strrchr(this->_dylibInstallPath, '/'); + if ( lastSlash != NULL ) { + const char* leafName = lastSlash+1; + char frname[strlen(leafName)+32]; + strcpy(frname, leafName); + strcat(frname, ".framework/"); + + if ( strstr(this->_dylibInstallPath, frname) != NULL ) + this->_frameworkName = leafName; + } + } + // validate minimal load commands - if ( (_dylibInstallPath == NULL) && ((header->filetype() == MH_DYLIB) || (header->filetype() == MH_DYLIB_STUB)) ) - throwf("dylib %s missing LC_ID_DYLIB load command", pth); - if ( dyldInfo == NULL ) { - if ( symbolTable == NULL ) + if ( (this->_dylibInstallPath == nullptr) && ((header->filetype() == MH_DYLIB) || (header->filetype() == MH_DYLIB_STUB)) ) + throwf("dylib %s missing LC_ID_DYLIB load command", path); + if ( dyldInfo == nullptr ) { + if ( symbolTable == nullptr ) throw "binary missing LC_SYMTAB load command"; - if ( dynamicInfo == NULL ) + if ( dynamicInfo == nullptr ) throw "binary missing LC_DYSYMTAB load command"; } - + + if ( symtab != nullptr ) { + if ( symtab->symoff() < _linkeditStartOffset ) + throwf("malformed mach-o, symbol table not in __LINKEDIT"); + if ( symtab->stroff() < _linkeditStartOffset ) + throwf("malformed mach-o, symbol table strings not in __LINKEDIT"); + } + // if linking flat and this is a flat dylib, create one atom that references all imported symbols if ( linkingFlatNamespace && linkingMainExecutable && ((header->flags() & MH_TWOLEVEL) == 0) ) { std::vector importNames; @@ -571,11 +458,11 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, for (const macho_nlist

* sym=start; sym < end; ++sym) { importNames.push_back(&strings[sym->n_strx()]); } - _importAtom = new ImportAtom(*this, importNames); + this->_importAtom = new generic::dylib::ImportAtom(*this, importNames); } // build hash table - if ( dyldInfo != NULL ) + if ( dyldInfo != nullptr ) buildExportHashTableFromExportInfo(dyldInfo, fileContent); else buildExportHashTableFromSymbolTable(dynamicInfo, symbolTable, strings, fileContent); @@ -584,49 +471,27 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, munmap((caddr_t)fileContent, fileLength); } - -// -// Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz -// template -uint32_t File::parseVersionNumber32(const char* versionString) -{ - uint32_t x = 0; - uint32_t y = 0; - uint32_t z = 0; - char* end; - x = strtoul(versionString, &end, 10); - if ( *end == '.' ) { - y = strtoul(&end[1], &end, 10); - if ( *end == '.' ) { - z = strtoul(&end[1], &end, 10); - } - } - if ( (*end != '\0') || (x > 0xffff) || (y > 0xff) || (z > 0xff) ) - throwf("malformed 32-bit x.y.z version number: %s", versionString); - - return (x << 16) | ( y << 8 ) | z; -} - -template -void File::buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, - const macho_nlist

* symbolTable, const char* strings, - const uint8_t* fileContent) +void File::buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, + const macho_nlist

* symbolTable, + const char* strings, const uint8_t* fileContent) { if ( dynamicInfo->tocoff() == 0 ) { - if ( _s_logHashtable ) fprintf(stderr, "ld: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), this->path()); + if ( this->_s_logHashtable ) + fprintf(stderr, "ld: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), this->path()); const macho_nlist

* start = &symbolTable[dynamicInfo->iextdefsym()]; const macho_nlist

* end = &start[dynamicInfo->nextdefsym()]; - _atoms.reserve(dynamicInfo->nextdefsym()); // set initial bucket count + this->_atoms.reserve(dynamicInfo->nextdefsym()); // set initial bucket count for (const macho_nlist

* sym=start; sym < end; ++sym) { this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, false, sym->n_value()); } } else { int32_t count = dynamicInfo->ntoc(); - _atoms.reserve(count); // set initial bucket count - if ( _s_logHashtable ) fprintf(stderr, "ld: building hashtable of %u entries for %s\n", count, this->path()); - const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)(fileContent + dynamicInfo->tocoff()); + this->_atoms.reserve(count); // set initial bucket count + if ( this->_s_logHashtable ) + fprintf(stderr, "ld: building hashtable of %u entries for %s\n", count, this->path()); + const auto* toc = reinterpret_cast(fileContent + dynamicInfo->tocoff()); for (int32_t i = 0; i < count; ++i) { const uint32_t index = E::get32(toc[i].symbol_index); const macho_nlist

* sym = &symbolTable[index]; @@ -635,84 +500,72 @@ void File::buildExportHashTableFromSymbolTable(const macho_dysymtab_command

_dylibInstallPath != nullptr) && (strcmp(this->_dylibInstallPath, "/usr/lib/libSystem.B.dylib") == 0) ) addDyldFastStub(); } template -void File::buildExportHashTableFromExportInfo(const macho_dyld_info_command

* dyldInfo, - const uint8_t* fileContent) +void File::buildExportHashTableFromExportInfo(const macho_dyld_info_command

* dyldInfo, + const uint8_t* fileContent) { - if ( _s_logHashtable ) fprintf(stderr, "ld: building hashtable from export info in %s\n", this->path()); + if ( this->_s_logHashtable ) + fprintf(stderr, "ld: building hashtable from export info in %s\n", this->path()); if ( dyldInfo->export_size() > 0 ) { const uint8_t* start = fileContent + dyldInfo->export_off(); const uint8_t* end = &start[dyldInfo->export_size()]; + if ( (dyldInfo->export_off() + dyldInfo->export_size()) > _fileLength ) + throwf("malformed mach-o dylib, exports trie extends beyond end of file, "); std::vector list; parseTrie(start, end, list); - for (std::vector::iterator it=list.begin(); it != list.end(); ++it) - this->addSymbol(it->name, - it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION, - (it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL, - it->address); + for (const auto &entry : list) + this->addSymbol(entry.name, + entry.flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION, + (entry.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL, + entry.address); } } - -template <> -void File::addDyldFastStub() -{ - addSymbol("dyld_stub_binder", false, false, 0); -} - -template <> -void File::addDyldFastStub() -{ - addSymbol("dyld_stub_binder", false, false, 0); -} - -template -void File::addDyldFastStub() -{ - // do nothing -} - template void File::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address) { - //fprintf(stderr, "addSymbol() %s\n", name); // symbols that start with $ld$ are meta-data to the static linker // need way for ld and dyld to see different exported symbols in a dylib - if ( strncmp(name, "$ld$", 4) == 0 ) { + if ( strncmp(name, "$ld$", 4) == 0 ) { // $ld$ $ $ const char* symAction = &name[4]; const char* symCond = strchr(symAction, '$'); - if ( symCond != NULL ) { + if ( symCond != nullptr ) { char curOSVers[16]; - sprintf(curOSVers, "$os%d.%d$", (_linkMinOSVersion >> 16), ((_linkMinOSVersion >> 8) & 0xFF)); + sprintf(curOSVers, "$os%d.%d$", (this->_linkMinOSVersion >> 16), ((this->_linkMinOSVersion >> 8) & 0xFF)); if ( strncmp(symCond, curOSVers, strlen(curOSVers)) == 0 ) { const char* symName = strchr(&symCond[1], '$'); - if ( symName != NULL ) { + if ( symName != nullptr ) { ++symName; if ( strncmp(symAction, "hide$", 5) == 0 ) { - if ( _s_logHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->path()); - _ignoreExports.insert(strdup(symName)); + if ( this->_s_logHashtable ) + fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->path()); + this->_ignoreExports.insert(strdup(symName)); return; } else if ( strncmp(symAction, "add$", 4) == 0 ) { - this->addSymbol(symName, weakDef, false, 0); + this->addSymbol(symName, weakDef); return; } + else if ( strncmp(symAction, "weak$", 5) == 0 ) { + if ( !this->_allowWeakImports ) + this->_ignoreExports.insert(strdup(symName)); + } else if ( strncmp(symAction, "install_name$", 13) == 0 ) { - _dylibInstallPath = symName; - _installPathOverride = true; + this->_dylibInstallPath = symName; + this->_installPathOverride = true; // CoreGraphics redirects to ApplicationServices, but with wrong compat version - if ( strcmp(_dylibInstallPath, "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices") == 0 ) - _dylibCompatibilityVersion = parseVersionNumber32("1.0"); + if ( strcmp(this->_dylibInstallPath, "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices") == 0 ) + this->_dylibCompatibilityVersion = Options::parseVersionNumber32("1.0"); return; } else if ( strncmp(symAction, "compatibility_version$", 22) == 0 ) { - _dylibCompatibilityVersion = parseVersionNumber32(symName); + this->_dylibCompatibilityVersion = Options::parseVersionNumber32(symName); return; } else { @@ -720,297 +573,67 @@ void File::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address } } } - } + } else { warning("bad symbol condition: %s in dylib %s", name, this->path()); } } - - // add symbol as possible export if we are not supposed to ignore it - if ( _ignoreExports.count(name) == 0 ) { - AtomAndWeak bucket; - bucket.atom = NULL; - bucket.weakDef = weakDef; - bucket.tlv = tlv; - bucket.address = address; - if ( _s_logHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->path()); - _atoms[strdup(name)] = bucket; - } -} - -template -bool File::forEachAtom(ld::File::AtomHandler& handler) const -{ - handler.doFile(*this); - // if doing flatnamespace and need all this dylib's imports resolve - // add atom which references alls undefines in this dylib - if ( _importAtom != NULL ) { - handler.doAtom(*_importAtom); - return true; - } - return false; -} - - -template -std::pair File::hasWeakDefinitionImpl(const char* name) const -{ - const auto pos = _atoms.find(name); - if ( pos != _atoms.end() ) - return std::make_pair(true, pos->second.weakDef); - - // look in children that I re-export - for (const auto &dep : _dependentDylibs) { - if ( dep.reExport ) { - auto ret = dep.dylib->hasWeakDefinitionImpl(name); - if ( ret.first ) - return ret; - } - } - return std::make_pair(false, false); -} - - -template -bool File::hasWeakDefinition(const char* name) const -{ - // if supposed to ignore this export, then pretend I don't have it - if ( _ignoreExports.count(name) != 0 ) - return false; - - return hasWeakDefinitionImpl(name).second; -} - - -// If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB -template -bool File::allSymbolsAreWeakImported() const -{ - bool foundNonWeakImport = false; - bool foundWeakImport = false; - //fprintf(stderr, "%s:\n", this->path()); - for (typename NameToAtomMap::const_iterator it = _atoms.begin(); it != _atoms.end(); ++it) { - const ld::Atom* atom = it->second.atom; - if ( atom != NULL ) { - if ( atom->weakImported() ) - foundWeakImport = true; - else - foundNonWeakImport = true; - //fprintf(stderr, " weak_import=%d, name=%s\n", atom->weakImported(), it->first); - } - } - - // don't automatically weak link dylib with no imports - // so at least one weak import symbol and no non-weak-imported symbols must be found - return foundWeakImport && !foundNonWeakImport; -} - - -template -bool File::containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& defAddress) const -{ - if ( _ignoreExports.count(name) != 0 ) - return false; - - // check myself - const auto pos = _atoms.find(name); - if ( pos != _atoms.end() ) { - weakDef = pos->second.weakDef; - tlv = pos->second.tlv; - defAddress = pos->second.address; - return true; - } - - // check dylibs I re-export - for (const auto &dep : _dependentDylibs) { - if ( dep.reExport && !dep.dylib->implicitlyLinked() ) { - if ( dep.dylib->containsOrReExports(name, weakDef, tlv, defAddress) ) - return true; - } - } - - return false; -} - - -template -bool File::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& handler) const -{ - // if supposed to ignore this export, then pretend I don't have it - if ( _ignoreExports.count(name) != 0 ) - return false; - - - AtomAndWeak bucket; - if ( this->containsOrReExports(name, bucket.weakDef, bucket.tlv, bucket.address) ) { - bucket.atom = new ExportAtom(*this, name, bucket.weakDef, bucket.tlv, bucket.address); - _atoms[name] = bucket; - _providedAtom = true; - if ( _s_logHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->path()); - // call handler with new export atom - handler.doAtom(*bucket.atom); - return true; + // add symbol as possible export if we are not supposed to ignore it + if ( this->_ignoreExports.count(name) == 0 ) { + typename Base::AtomAndWeak bucket = { nullptr, weakDef, tlv, address }; + if ( this->_s_logHashtable ) + fprintf(stderr, " adding %s to hash table for %s\n", name, this->path()); + this->_atoms[strdup(name)] = bucket; } - - return false; } - - -template -bool File::isPublicLocation(const char* pth) +template <> +void File::addDyldFastStub() { - // -no_implicit_dylibs disables this optimization - if ( ! _implicitlyLinkPublicDylibs ) - return false; - - // /usr/lib is a public location - if ( (strncmp(pth, "/usr/lib/", 9) == 0) && (strchr(&pth[9], '/') == NULL) ) - return true; - - // /System/Library/Frameworks/ is a public location - if ( strncmp(pth, "/System/Library/Frameworks/", 27) == 0 ) { - const char* frameworkDot = strchr(&pth[27], '.'); - // but only top level framework - // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true - // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false - // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false - // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false - if ( frameworkDot != NULL ) { - int frameworkNameLen = frameworkDot - &pth[27]; - if ( strncmp(&pth[strlen(pth)-frameworkNameLen-1], &pth[26], frameworkNameLen+1) == 0 ) - return true; - } - } - - return false; + addSymbol("dyld_stub_binder"); } -template -void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs) +template <> +void File::addDyldFastStub() { - // only do this once - if ( _indirectDylibsProcessed ) - return; - const static bool log = false; - if ( log ) fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath()); - if ( _linkingFlat ) { - for (typename std::vector::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); it++) { - it->dylib = (File*)handler->findDylib(it->path, this->path()); - } - } - else if ( _noRexports ) { - // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do - } - else { - // two-level, might have re-exports - for (typename std::vector::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); it++) { - if ( it->reExport ) { - if ( log ) fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->installPath(), it->path); - // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child - it->dylib = (File*)handler->findDylib(it->path, this->path()); - if ( it->dylib->hasPublicInstallName() && !it->dylib->wrongOS() ) { - // promote this child to be automatically added as a direct dependent if this already is - if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(it->path,it->dylib->installPath()) == 0) ) { - if ( log ) fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", it->dylib->installPath()); - it->dylib->setImplicitlyLinked(); - } - else if ( it->dylib->explicitlyLinked() || it->dylib->implicitlyLinked() ) { - if ( log ) fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n"); - } - else { - if ( log ) fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->installPath(), it->path); - } - } - else { - // add all child's symbols to me - if ( log ) fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->installPath(), it->path); - } - } - else if ( !_explictReExportFound ) { - // see if child contains LC_SUB_FRAMEWORK with my name - it->dylib = (File*)handler->findDylib(it->path, this->path()); - const char* parentUmbrellaName = it->dylib->parentUmbrella(); - if ( parentUmbrellaName != NULL ) { - const char* parentName = this->path(); - const char* lastSlash = strrchr(parentName, '/'); - if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], parentUmbrellaName) == 0) ) { - // add all child's symbols to me - it->reExport = true; - if ( log ) fprintf(stderr, "processIndirectLibraries() umbrella=%s will re-export child=%s\n", this->installPath(), it->path); - } - } - } - } - } - - // check for re-export cycles - ReExportChain chain; - chain.prev = NULL; - chain.file = this; - this->assertNoReExportCycles(&chain); - - _indirectDylibsProcessed = true; + addSymbol("dyld_stub_binder"); } template -void File::assertNoReExportCycles(ReExportChain* prev) const +void File::addDyldFastStub() { - // recursively check my re-exported dylibs - ReExportChain chain; - chain.prev = prev; - chain.file = this; - for (const auto &dep : _dependentDylibs) { - if ( dep.reExport ) { - ld::File* child = dep.dylib; - // check child is not already in chain - for (ReExportChain* p = prev; p != nullptr; p = p->prev) { - if ( p->file == child ) { - throwf("cycle in dylib re-exports with %s and %s", child->path(), this->path()); - } - } - if ( dep.dylib != nullptr ) - dep.dylib->assertNoReExportCycles(&chain); - } - } + // do nothing } - template class Parser { public: - typedef typename A::P P; - - static bool validFile(const uint8_t* fileContent, bool executableOrDyliborBundle); - static const char* fileKind(const uint8_t* fileContent); - static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, - const char* path, time_t mTime, - ld::File::Ordinal ordinal, const Options& opts, bool indirectDylib) { - return new File(fileContent, fileLength, path, mTime, - ordinal, opts.flatNamespace(), - opts.linkingMainExecutable(), - opts.implicitlyLinkIndirectPublicDylibs(), - opts.platform(), - opts.minOSversion(), - opts.allowSimulatorToLinkWithMacOSX(), - opts.addVersionLoadCommand(), - opts.targetIOSSimulator(), - opts.logAllFiles(), - opts.installPath(), - indirectDylib, - opts.outputKind() == Options::kPreload); - } + using P = typename A::P; + + static bool validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch=false, uint32_t subType=0); + static const char* fileKind(const uint8_t* fileContent); + static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t mTime, ld::File::Ordinal ordinal, const Options& opts, + bool indirectDylib) + { + return new File(fileContent, fileLength, path, mTime, ordinal, opts.flatNamespace(), + opts.linkingMainExecutable(), opts.implicitlyLinkIndirectPublicDylibs(), + opts.platform(), opts.minOSversion(), opts.allowWeakImports(), + opts.allowSimulatorToLinkWithMacOSX(), opts.addVersionLoadCommand(), + opts.targetIOSSimulator(), opts.logAllFiles(), opts.installPath(), + indirectDylib, opts.outputKind() == Options::kPreload, opts.bundleBitcode()); + } }; template <> -bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch, uint32_t subType) { - const macho_header

* header = (const macho_header

*)fileContent; + const auto* header = reinterpret_cast*>(fileContent); if ( header->magic() != MH_MAGIC ) return false; if ( header->cputype() != CPU_TYPE_I386 ) @@ -1035,9 +658,9 @@ bool Parser::validFile(const uint8_t* fileContent, bool executableOrDylibor } template <> -bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch, uint32_t subType) { - const macho_header

* header = (const macho_header

*)fileContent; + const auto* header = reinterpret_cast*>(fileContent); if ( header->magic() != MH_MAGIC_64 ) return false; if ( header->cputype() != CPU_TYPE_X86_64 ) @@ -1062,13 +685,15 @@ bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyli } template <> -bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch, uint32_t subType) { - const macho_header

* header = (const macho_header

*)fileContent; + const auto* header = reinterpret_cast*>(fileContent); if ( header->magic() != MH_MAGIC ) return false; if ( header->cputype() != CPU_TYPE_ARM ) return false; + if ( subTypeMustMatch && (header->cpusubtype() != subType) ) + return false; switch ( header->filetype() ) { case MH_DYLIB: case MH_DYLIB_STUB: @@ -1091,9 +716,9 @@ bool Parser::validFile(const uint8_t* fileContent, bool executableOrDylibor template <> -bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch, uint32_t subType) { - const macho_header

* header = (const macho_header

*)fileContent; + const auto* header = reinterpret_cast*>(fileContent); if ( header->magic() != MH_MAGIC_64 ) return false; if ( header->cputype() != CPU_TYPE_ARM64 ) @@ -1122,7 +747,7 @@ bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* { if ( Parser::validFile(fileContent, false) ) { *result = CPU_TYPE_X86_64; - const macho_header >* header = (const macho_header >*)fileContent; + const auto* header = reinterpret_cast>*>(fileContent); *subResult = header->cpusubtype(); return true; } @@ -1133,7 +758,7 @@ bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* } if ( Parser::validFile(fileContent, false) ) { *result = CPU_TYPE_ARM; - const macho_header >* header = (const macho_header >*)fileContent; + const auto* header = reinterpret_cast>*>(fileContent); *subResult = header->cpusubtype(); return true; } @@ -1148,34 +773,34 @@ bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* template <> const char* Parser::fileKind(const uint8_t* fileContent) { - const macho_header

* header = (const macho_header

*)fileContent; + const auto* header = reinterpret_cast*>(fileContent); if ( header->magic() != MH_MAGIC ) - return NULL; + return nullptr; if ( header->cputype() != CPU_TYPE_I386 ) - return NULL; + return nullptr; return "i386"; } template <> const char* Parser::fileKind(const uint8_t* fileContent) { - const macho_header

* header = (const macho_header

*)fileContent; + const auto* header = reinterpret_cast*>(fileContent); if ( header->magic() != MH_MAGIC_64 ) - return NULL; + return nullptr; if ( header->cputype() != CPU_TYPE_X86_64 ) - return NULL; + return nullptr; return "x86_64"; } template <> const char* Parser::fileKind(const uint8_t* fileContent) { - const macho_header

* header = (const macho_header

*)fileContent; + const auto* header = reinterpret_cast*>(fileContent); if ( header->magic() != MH_MAGIC ) - return NULL; + return nullptr; if ( header->cputype() != CPU_TYPE_ARM ) - return NULL; - for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { + return nullptr; + for (const auto* t = archInfoArray; t->archName != nullptr; ++t) { if ( (t->cpuType == CPU_TYPE_ARM) && ((cpu_subtype_t)header->cpusubtype() == t->cpuSubType) ) { return t->archName; } @@ -1187,15 +812,16 @@ const char* Parser::fileKind(const uint8_t* fileContent) template <> const char* Parser::fileKind(const uint8_t* fileContent) { - const macho_header

* header = (const macho_header

*)fileContent; + const auto* header = reinterpret_cast*>(fileContent); if ( header->magic() != MH_MAGIC_64 ) - return NULL; + return nullptr; if ( header->cputype() != CPU_TYPE_ARM64 ) - return NULL; + return nullptr; return "arm64"; } #endif + // // used by linker is error messages to describe mismatched files // @@ -1215,48 +841,47 @@ const char* archName(const uint8_t* fileContent) return Parser::fileKind(fileContent); } #endif - return NULL; + return nullptr; } // // main function used by linker to instantiate ld::Files // -ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, - const char* path, time_t modTime, const Options& opts, ld::File::Ordinal ordinal, - bool bundleLoader, bool indirectDylib) +ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t modTime, const Options& opts, ld::File::Ordinal ordinal, + bool bundleLoader, bool indirectDylib) { + bool subTypeMustMatch = opts.enforceDylibSubtypesMatch(); switch ( opts.architecture() ) { #if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: - if ( Parser::validFile(fileContent, bundleLoader) ) + if ( Parser::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) ) return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; #endif #if SUPPORT_ARCH_i386 case CPU_TYPE_I386: - if ( Parser::validFile(fileContent, bundleLoader) ) + if ( Parser::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) ) return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; #endif #if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: - if ( Parser::validFile(fileContent, bundleLoader) ) + if ( Parser::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) ) return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; #endif #if SUPPORT_ARCH_arm64 case CPU_TYPE_ARM64: - if ( Parser::validFile(fileContent, bundleLoader) ) + if ( Parser::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) ) return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; #endif } - return NULL; + return nullptr; } }; // namespace dylib }; // namespace mach_o - -