/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2005-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <vector>
#include <set>
+#include <map>
#include <algorithm>
-#include <ext/hash_map>
-#include <ext/hash_set>
#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 <typename A> class File;
-
-
-//
-// An ExportAtom has no content. It exists so that the linker can track which imported
-// symbols came from which dynamic libraries.
-//
-template <typename A>
-class ExportAtom : public ld::Atom
-{
-public:
- ExportAtom(const File<A>& 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 bool translationUnitSource(const char** dir, const char** nm) const
- { return false; }
- 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<A>& _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 <typename A>
-class ImportAtom : public ld::Atom
-{
-public:
- ImportAtom(File<A>& f, std::vector<const char*>& imports);
-
- // overrides of ld::Atom
- virtual ld::File* file() const { return &_file; }
- virtual bool translationUnitSource(const char** dir, const char** nm) const
- { return false; }
- 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<A>& _file;
- mutable std::vector<ld::Fixup> _undefs;
-};
-
-template <typename A>
-ImportAtom<A>::ImportAtom(File<A>& f, std::vector<const char*>& 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<const char*>::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 <typename A>
-class File : public ld::dylib::File
+class File final : public generic::dylib::File<A>
{
+ using Base = generic::dylib::File<A>;
+
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, uint32_t ordinal, bool linkingFlatNamespace,
+ time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace,
bool linkingMainExecutable, bool hoistImplicitPublicDylibs,
- ld::MacVersionMin macMin, ld::IPhoneVersionMin iPhoneMin,
- bool logAllFiles);
- 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; }
-
- // 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<const char*>* 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;
-
-
-protected:
-
- struct ReExportChain { ReExportChain* prev; File<A>* file; };
-
- void assertNoReExportCycles(ReExportChain*);
+ Options::Platform platform, uint32_t linkMinOSVersion, bool allowWeakImports,
+ bool allowSimToMacOSX, bool addVers, bool buildingForSimulator,
+ bool logAllFiles, const char* installPath,
+ 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<A>;
- friend class ImportAtom<A>;
+ using P = typename A::P;
+ using E = typename A::P::E;
+ using pint_t = typename A::P::uint_t;
- class CStringEquals
- {
- public:
- bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
- };
- struct AtomAndWeak { ld::Atom* atom; bool weak; bool tlv; pint_t address; };
- typedef __gnu_cxx::hash_map<const char*, AtomAndWeak, __gnu_cxx::hash<const char*>, CStringEquals> NameToAtomMap;
- typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> NameSet;
-
- struct Dependent { const char* path; File<A>* dylib; bool reExport; };
-
- bool containsOrReExports(const char* name, bool* weakDef, bool* tlv, pint_t* defAddress) const;
- bool isPublicLocation(const char* pth);
- void addSymbol(const char* name, bool weak, bool tlv, pint_t address);
- void addDyldFastStub();
- void buildExportHashTableFromExportInfo(const macho_dyld_info_command<P>* dyldInfo,
+ void addDyldFastStub();
+ void buildExportHashTableFromExportInfo(const macho_dyld_info_command<P>* dyldInfo,
const uint8_t* fileContent);
- void buildExportHashTableFromSymbolTable(const macho_dysymtab_command<P>* dynamicInfo,
+ void buildExportHashTableFromSymbolTable(const macho_dysymtab_command<P>* dynamicInfo,
const macho_nlist<P>* symbolTable, const char* strings,
const uint8_t* fileContent);
- static const char* objCInfoSegmentName();
- static const char* objCInfoSectionName();
-
- const ld::MacVersionMin _macVersionMin;
- const ld::IPhoneVersionMin _iPhoneVersionMin;
- bool _linkingFlat;
- bool _implicitlyLinkPublicDylibs;
- ld::File::ObjcConstraint _objcContraint;
- ld::Section _importProxySection;
- ld::Section _flatDummySection;
- std::vector<Dependent> _dependentDylibs;
- std::vector<const char*> _allowableClients;
- mutable NameToAtomMap _atoms;
- NameSet _ignoreExports;
- const char* _parentUmbrella;
- ImportAtom<A>* _importAtom;
- bool _noRexports;
- bool _hasWeakExports;
- bool _deadStrippable;
- bool _hasPublicInstallName;
- mutable bool _providedAtom;
- bool _explictReExportFound;
-
- static bool _s_logHashtable;
-};
+ void addSymbol(const char* name, bool weakDef = false, bool tlv = false, pint_t address = 0);
+ static const char* objCInfoSegmentName();
+ static const char* objCInfoSectionName();
-template <typename A>
-bool File<A>::_s_logHashtable = false;
+
+ uint64_t _fileLength;
+ uint32_t _linkeditStartOffset;
+
+};
template <> const char* File<x86_64>::objCInfoSegmentName() { return "__DATA"; }
template <> const char* File<arm>::objCInfoSegmentName() { return "__DATA"; }
template <typename A> const char* File<A>::objCInfoSectionName() { return "__image_info"; }
template <typename A>
-File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, time_t mTime, uint32_t ord,
- bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs,
- ld::MacVersionMin macMin, ld::IPhoneVersionMin iPhoneMin, bool logAllFiles)
- : ld::dylib::File(strdup(pth), mTime, ord),
- _macVersionMin(macMin), _iPhoneVersionMin(iPhoneMin),
- _linkingFlat(linkingFlatNamespace), _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs),
- _objcContraint(ld::File::objcConstraintNone),
- _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)
+File<A>::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<P>* header = (const macho_header<P>*)fileContent;
const uint32_t cmd_count = header->ncmds();
// 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) ) {
// 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);
+ 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<P>* dynamicInfo = NULL;
- const macho_dyld_info_command<P>* dyldInfo = NULL;
- const macho_nlist<P>* symbolTable = NULL;
- const char* strings = NULL;
+ const macho_dysymtab_command<P>* dynamicInfo = nullptr;
+ const macho_dyld_info_command<P>* dyldInfo = nullptr;
+ const macho_nlist<P>* symbolTable = nullptr;
+ const macho_symtab_command<P>* symtab = nullptr;
+ const char* strings = nullptr;
bool compressedLinkEdit = false;
uint32_t dependentLibCount = 0;
+ Options::Platform lcPlatform = Options::kPlatformUnknown;
const macho_load_command<P>* cmd = cmds;
for (uint32_t i = 0; i < cmd_count; ++i) {
macho_dylib_command<P>* dylibID;
- const macho_symtab_command<P>* symtab;
+ uint32_t cmdLength = cmd->cmdsize();
switch (cmd->cmd()) {
case LC_SYMTAB:
symtab = (macho_symtab_command<P>*)cmd;
symbolTable = (const macho_nlist<P>*)((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", path);
break;
case LC_DYSYMTAB:
dynamicInfo = (macho_dysymtab_command<P>*)cmd;
break;
case LC_ID_DYLIB:
dylibID = (macho_dylib_command<P>*)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<P>*)cmd)->umbrella());
+ this->_parentUmbrella = strdup(((macho_sub_framework_command<P>*)cmd)->umbrella());
break;
case LC_SUB_CLIENT:
- _allowableClients.push_back(strdup(((macho_sub_client_command<P>*)cmd)->client()));
+ this->_allowableClients.push_back(strdup(((macho_sub_client_command<P>*)cmd)->client()));
+ // <rdar://problem/20627554> Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked
+ this->_hasPublicInstallName = false;
+ break;
+ case LC_RPATH:
+ this->_rpaths.push_back(strdup(((macho_rpath_command<P>*)cmd)->path()));
+ break;
+ case LC_VERSION_MIN_MACOSX:
+ case LC_VERSION_MIN_IPHONEOS:
+ case LC_VERSION_MIN_WATCHOS:
+ #if SUPPORT_APPLE_TV
+ case LC_VERSION_MIN_TVOS:
+ #endif
+ this->_minVersionInDylib = (ld::MacVersionMin)((macho_version_min_command<P>*)cmd)->version();
+ this->_platformInDylib = cmd->cmd();
+ lcPlatform = Options::platformForLoadCommand(this->_platformInDylib);
+ break;
+ case LC_CODE_SIGNATURE:
break;
case macho_segment_command<P>::CMD:
// check for Objective-C info
- if ( strcmp(((macho_segment_command<P>*)cmd)->segname(), objCInfoSegmentName()) == 0 ) {
+ if ( strncmp(((macho_segment_command<P>*)cmd)->segname(), objCInfoSegmentName(), 6) == 0 ) {
const macho_segment_command<P>* segment = (macho_segment_command<P>*)cmd;
const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segment + sizeof(macho_segment_command<P>));
const macho_section<P>* const sectionsEnd = §ionsStart[segment->nsects()];
// };
// #define OBJC_IMAGE_SUPPORTS_GC 2
// #define OBJC_IMAGE_GC_ONLY 4
+ // #define OBJC_IMAGE_IS_SIMULATED 32
//
const uint32_t* contents = (uint32_t*)(&fileContent[sect->offset()]);
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 )
+ this->_objcConstraint = ld::File::objcConstraintRetainReleaseForSimulator;
else
- _objcContraint = ld::File::objcConstraintRetainRelease;
+ 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);
}
}
}
}
+ // Construct bitcode if there is a bitcode bundle section in the dylib
+ // Record the size of the section because the content is not checked
+ else if ( strcmp(((macho_segment_command<P>*)cmd)->segname(), "__LLVM") == 0 ) {
+ const macho_section<P>* const sect = (macho_section<P>*)((char*)cmd + sizeof(macho_segment_command<P>));
+ if ( strncmp(sect->sectname(), "__bundle", 8) == 0 )
+ this->_bitcode = std::unique_ptr<ld::Bitcode>(new ld::Bitcode(NULL, sect->size()));
+ }
+ else if ( strcmp(((macho_segment_command<P>*)cmd)->segname(), "__LINKEDIT") == 0 ) {
+ _linkeditStartOffset = ((macho_segment_command<P>*)cmd)->fileoff();
+ }
}
- cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize());
+ cmd = (const macho_load_command<P>*)(((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
+ if (lcPlatform == Options::kPlatformUnknown &&
+ (std::is_same<A, arm>::value || std::is_same<A, arm64>::value))
+ lcPlatform = Options::kPlatformiOS;
+
+ // check cross-linking
+ if ( lcPlatform != platform ) {
+ this->_wrongOS = true;
+ if ( this->_addVersionLoadCommand && !indirectDylib && !ignoreMismatchPlatform ) {
+ if ( buildingForSimulator ) {
+ if ( !this->_allowSimToMacOSXLinking ) {
+ switch (platform) {
+ case Options::kPlatformOSX:
+ case Options::kPlatformiOS:
+ if ( lcPlatform == Options::kPlatformUnknown )
+ break;
+ // fall through if the Platform is not Unknown
+ case Options::kPlatformWatchOS:
+ // 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 ( 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;
+ #endif
+ case Options::kPlatformUnknown:
+ // skip if the target platform is unknown
+ break;
+ }
+ }
+ }
+ else {
+ switch (platform) {
+ case Options::kPlatformOSX:
+ case Options::kPlatformiOS:
+ if ( lcPlatform == Options::kPlatformUnknown )
+ break;
+ // fall through if the Platform is not Unknown
+ case Options::kPlatformWatchOS:
+ // 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 ( 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;
+ #endif
+ case Options::kPlatformUnknown:
+ // skip if the target platform is unknown
+ break;
+ }
+ }
+ }
}
// 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<P>* dylibCmd = (macho_dylib_command<P>*)cmd;
switch (cmd->cmd()) {
case LC_LOAD_DYLIB:
case LC_LOAD_WEAK_DYLIB:
if ( compressedLinkEdit && !linkingFlatNamespace )
break;
case LC_REEXPORT_DYLIB:
- Dependent entry;
- entry.path = strdup(((macho_dylib_command<P>*)cmd)->name());
- entry.dylib = NULL;
- entry.reExport = (cmd->cmd() == LC_REEXPORT_DYLIB);
- _dependentDylibs.push_back(entry);
+ ++reExportDylibCount;
+ 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<P>*)(((char*)cmd)+cmd->cmdsize());
+ cmd = (const macho_load_command<P>*)(((char*)cmd)+cmdLength);
}
// verify MH_NO_REEXPORTED_DYLIBS bit was correct
if ( compressedLinkEdit && !linkingFlatNamespace ) {
- assert(_dependentDylibs.size() != 0);
+ if ( reExportDylibCount == 0 )
+ 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;
switch (cmd->cmd()) {
case LC_SUB_UMBRELLA:
frameworkLeafName = ((macho_sub_umbrella_command<P>*)cmd)->sub_umbrella();
- for (typename std::vector<Dependent>::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<P>*)cmd)->sub_library();
- for (typename std::vector<Dependent>::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<P>*)(((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<const char*> importNames;
for (const macho_nlist<P>* sym=start; sym < end; ++sym) {
importNames.push_back(&strings[sym->n_strx()]);
}
- _importAtom = new ImportAtom<A>(*this, importNames);
+ this->_importAtom = new generic::dylib::ImportAtom<A>(*this, importNames);
}
-
+
// build hash table
- if ( dyldInfo != NULL )
+ if ( dyldInfo != nullptr )
buildExportHashTableFromExportInfo(dyldInfo, fileContent);
else
buildExportHashTableFromSymbolTable(dynamicInfo, symbolTable, strings, fileContent);
munmap((caddr_t)fileContent, fileLength);
}
-
template <typename A>
-void File<A>::buildExportHashTableFromSymbolTable(const macho_dysymtab_command<P>* dynamicInfo,
- const macho_nlist<P>* symbolTable, const char* strings,
- const uint8_t* fileContent)
+void File<A>::buildExportHashTableFromSymbolTable(const macho_dysymtab_command<P>* dynamicInfo,
+ const macho_nlist<P>* 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<P>* start = &symbolTable[dynamicInfo->iextdefsym()];
const macho_nlist<P>* end = &start[dynamicInfo->nextdefsym()];
- _atoms.resize(dynamicInfo->nextdefsym()); // set initial bucket count
+ this->_atoms.reserve(dynamicInfo->nextdefsym()); // set initial bucket count
for (const macho_nlist<P>* 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.resize(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<const dylib_table_of_contents*>(fileContent + dynamicInfo->tocoff());
for (int32_t i = 0; i < count; ++i) {
const uint32_t index = E::get32(toc[i].symbol_index);
const macho_nlist<P>* sym = &symbolTable[index];
}
// special case old libSystem
- if ( (_dylibInstallPath != NULL) && (strcmp(_dylibInstallPath, "/usr/lib/libSystem.B.dylib") == 0) )
+ if ( (this->_dylibInstallPath != nullptr) && (strcmp(this->_dylibInstallPath, "/usr/lib/libSystem.B.dylib") == 0) )
addDyldFastStub();
}
template <typename A>
-void File<A>::buildExportHashTableFromExportInfo(const macho_dyld_info_command<P>* dyldInfo,
- const uint8_t* fileContent)
+void File<A>::buildExportHashTableFromExportInfo(const macho_dyld_info_command<P>* 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<mach_o::trie::Entry> list;
parseTrie(start, end, list);
- for (std::vector<mach_o::trie::Entry>::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<x86_64>::addDyldFastStub()
-{
- addSymbol("dyld_stub_binder", false, false, 0);
-}
-
-template <>
-void File<x86>::addDyldFastStub()
-{
- addSymbol("dyld_stub_binder", false, false, 0);
-}
-
-template <typename A>
-void File<A>::addDyldFastStub()
-{
- // do nothing
-}
-
template <typename A>
void File<A>::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address)
{
- if ( weakDef ) {
- assert(_hasWeakExports);
- }
- //fprintf(stderr, "addSymbol() %s\n", name);
// symbols that start with $ld$ are meta-data to the static linker
// <rdar://problem/5182537> 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$ <action> $ <condition> $ <symbol-name>
const char* symAction = &name[4];
const char* symCond = strchr(symAction, '$');
- if ( symCond != NULL ) {
+ if ( symCond != nullptr ) {
char curOSVers[16];
- if ( _macVersionMin != ld::macVersionUnset ) {
- sprintf(curOSVers, "$os%d.%d$", (_macVersionMin >> 16), ((_macVersionMin >> 8) & 0xFF));
- }
- else if ( _iPhoneVersionMin != ld::iPhoneVersionUnset ) {
- sprintf(curOSVers, "$os%d.%d$", (_iPhoneVersionMin >> 16), ((_iPhoneVersionMin >> 8) & 0xFF));
- }
- else {
- assert(0 && "targeting neither macosx nor iphoneos");
- }
+ 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 ) {
+ this->_dylibInstallPath = symName;
+ this->_installPathOverride = true;
+ // <rdar://problem/14448206> CoreGraphics redirects to ApplicationServices, but with wrong compat version
+ 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 ) {
+ this->_dylibCompatibilityVersion = Options::parseVersionNumber32(symName);
return;
}
else {
}
}
}
- }
+ }
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.weak = 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 <typename A>
-bool File<A>::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 <typename A>
-bool File<A>::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;
-
- typename NameToAtomMap::const_iterator pos = _atoms.find(name);
- if ( pos != _atoms.end() ) {
- return pos->second.weak;
- }
- else {
- // look in children that I re-export
- for (typename std::vector<Dependent>::const_iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) {
- if ( it->reExport ) {
- //fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s, looking in child %s\n", name, this->path(), (*it)->getInstallPath());
- typename NameToAtomMap::iterator cpos = it->dylib->_atoms.find(name);
- if ( cpos != it->dylib->_atoms.end() )
- return cpos->second.weak;
- }
- }
- }
- return false;
-}
-
-template <typename A>
-bool File<A>::containsOrReExports(const char* name, bool* weakDef, bool* tlv, pint_t* defAddress) const
-{
- // check myself
- typename NameToAtomMap::iterator pos = _atoms.find(name);
- if ( pos != _atoms.end() ) {
- *weakDef = pos->second.weak;
- *tlv = pos->second.tlv;
- *defAddress = pos->second.address;
- return true;
- }
-
- // check dylibs I re-export
- for (typename std::vector<Dependent>::const_iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) {
- if ( it->reExport && !it->dylib->implicitlyLinked() ) {
- if ( it->dylib->containsOrReExports(name, weakDef, tlv, defAddress) )
- return true;
- }
- }
-
- return false;
-}
-
-template <typename A>
-bool File<A>::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.weak, &bucket.tlv, &bucket.address) ) {
- bucket.atom = new ExportAtom<A>(*this, name, bucket.weak, 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 <typename A>
-bool File<A>::isPublicLocation(const char* pth)
+template <>
+void File<x86_64>::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 <typename A>
-void File<A>::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs)
+template <>
+void File<x86>::addDyldFastStub()
{
- const static bool log = false;
- if ( log ) fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath());
- if ( _linkingFlat ) {
- for (typename std::vector<Dependent>::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); it++) {
- it->dylib = (File<A>*)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<Dependent>::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<A>*)handler->findDylib(it->path, this->path());
- if ( it->dylib->hasPublicInstallName() ) {
- // 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<A>*)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);
+ addSymbol("dyld_stub_binder");
}
template <typename A>
-void File<A>::assertNoReExportCycles(ReExportChain* prev)
+void File<A>::addDyldFastStub()
{
- // recursively check my re-exported dylibs
- ReExportChain chain;
- chain.prev = prev;
- chain.file = this;
- for (typename std::vector<Dependent>::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); it++) {
- if ( it->reExport ) {
- ld::File* child = it->dylib;
- // check child is not already in chain
- for (ReExportChain* p = prev; p != NULL; p = p->prev) {
- if ( p->file == child ) {
- throwf("cycle in dylib re-exports with %s and %s", child->path(), this->path());
- }
- }
- if ( it->dylib != NULL )
- it->dylib->assertNoReExportCycles(&chain);
- }
- }
+ // do nothing
}
-
-struct ParserOptions {
- bool linkingFlat;
- bool linkingMain;
- bool addImplictDylibs;
- ld::MacVersionMin macOSMin;
- ld::IPhoneVersionMin iphoneOSMin;
-};
-
-
template <typename A>
class Parser
{
public:
- typedef typename A::P P;
-
- static bool validFile(const uint8_t* fileContent, bool executableOrDyliborBundle);
- static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength,
- const char* path, time_t mTime,
- uint32_t ordinal, const Options& opts) {
- return new File<A>(fileContent, fileLength, path, mTime,
- ordinal, opts.flatNamespace(),
- opts.linkingMainExecutable(),
- opts.implicitlyLinkIndirectPublicDylibs(),
- opts.macosxVersionMin(),
- opts.iphoneOSVersionMin(),
- opts.logAllFiles());
- }
+ 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<A>(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<ppc>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle)
+bool Parser<x86>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch, uint32_t subType)
{
- const macho_header<P>* header = (const macho_header<P>*)fileContent;
+ const auto* header = reinterpret_cast<const macho_header<P>*>(fileContent);
if ( header->magic() != MH_MAGIC )
return false;
- if ( header->cputype() != CPU_TYPE_POWERPC )
+ if ( header->cputype() != CPU_TYPE_I386 )
return false;
switch ( header->filetype() ) {
case MH_DYLIB:
}
template <>
-bool Parser<ppc64>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle)
+bool Parser<x86_64>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch, uint32_t subType)
{
- const macho_header<P>* header = (const macho_header<P>*)fileContent;
+ const auto* header = reinterpret_cast<const macho_header<P>*>(fileContent);
if ( header->magic() != MH_MAGIC_64 )
return false;
- if ( header->cputype() != CPU_TYPE_POWERPC64 )
+ if ( header->cputype() != CPU_TYPE_X86_64 )
return false;
switch ( header->filetype() ) {
case MH_DYLIB:
}
template <>
-bool Parser<x86>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle)
+bool Parser<arm>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch, uint32_t subType)
{
- const macho_header<P>* header = (const macho_header<P>*)fileContent;
+ const auto* header = reinterpret_cast<const macho_header<P>*>(fileContent);
if ( header->magic() != MH_MAGIC )
return false;
- if ( header->cputype() != CPU_TYPE_I386 )
+ if ( header->cputype() != CPU_TYPE_ARM )
+ return false;
+ if ( subTypeMustMatch && (header->cpusubtype() != subType) )
return false;
switch ( header->filetype() ) {
case MH_DYLIB:
}
}
+
+
template <>
-bool Parser<x86_64>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle)
+bool Parser<arm64>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle, bool subTypeMustMatch, uint32_t subType)
{
- const macho_header<P>* header = (const macho_header<P>*)fileContent;
+ const auto* header = reinterpret_cast<const macho_header<P>*>(fileContent);
if ( header->magic() != MH_MAGIC_64 )
return false;
- if ( header->cputype() != CPU_TYPE_X86_64 )
+ if ( header->cputype() != CPU_TYPE_ARM64 )
return false;
switch ( header->filetype() ) {
case MH_DYLIB:
}
}
+
+bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult)
+{
+ if ( Parser<x86_64>::validFile(fileContent, false) ) {
+ *result = CPU_TYPE_X86_64;
+ const auto* header = reinterpret_cast<const macho_header<Pointer64<LittleEndian>>*>(fileContent);
+ *subResult = header->cpusubtype();
+ return true;
+ }
+ if ( Parser<x86>::validFile(fileContent, false) ) {
+ *result = CPU_TYPE_I386;
+ *subResult = CPU_SUBTYPE_X86_ALL;
+ return true;
+ }
+ if ( Parser<arm>::validFile(fileContent, false) ) {
+ *result = CPU_TYPE_ARM;
+ const auto* header = reinterpret_cast<const macho_header<Pointer32<LittleEndian>>*>(fileContent);
+ *subResult = header->cpusubtype();
+ return true;
+ }
+ if ( Parser<arm64>::validFile(fileContent, false) ) {
+ *result = CPU_TYPE_ARM64;
+ *subResult = CPU_SUBTYPE_ARM64_ALL;
+ return true;
+ }
+ return false;
+}
+
template <>
-bool Parser<arm>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle)
+const char* Parser<x86>::fileKind(const uint8_t* fileContent)
{
- const macho_header<P>* header = (const macho_header<P>*)fileContent;
+ const auto* header = reinterpret_cast<const macho_header<P>*>(fileContent);
if ( header->magic() != MH_MAGIC )
- return false;
+ return nullptr;
+ if ( header->cputype() != CPU_TYPE_I386 )
+ return nullptr;
+ return "i386";
+}
+
+template <>
+const char* Parser<x86_64>::fileKind(const uint8_t* fileContent)
+{
+ const auto* header = reinterpret_cast<const macho_header<P>*>(fileContent);
+ if ( header->magic() != MH_MAGIC_64 )
+ return nullptr;
+ if ( header->cputype() != CPU_TYPE_X86_64 )
+ return nullptr;
+ return "x86_64";
+}
+
+template <>
+const char* Parser<arm>::fileKind(const uint8_t* fileContent)
+{
+ const auto* header = reinterpret_cast<const macho_header<P>*>(fileContent);
+ if ( header->magic() != MH_MAGIC )
+ return nullptr;
if ( header->cputype() != CPU_TYPE_ARM )
- return false;
- switch ( header->filetype() ) {
- case MH_DYLIB:
- case MH_DYLIB_STUB:
- return true;
- case MH_BUNDLE:
- if ( executableOrDyliborBundle )
- return true;
- else
- throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)";
- case MH_EXECUTE:
- if ( executableOrDyliborBundle )
- return true;
- else
- throw "can't link with a main executable";
- default:
- return false;
+ 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;
+ }
}
+ return "arm???";
}
+#if SUPPORT_ARCH_arm64
+template <>
+const char* Parser<arm64>::fileKind(const uint8_t* fileContent)
+{
+ const auto* header = reinterpret_cast<const macho_header<P>*>(fileContent);
+ if ( header->magic() != MH_MAGIC_64 )
+ return nullptr;
+ if ( header->cputype() != CPU_TYPE_ARM64 )
+ return nullptr;
+ return "arm64";
+}
+#endif
+
+
+//
+// used by linker is error messages to describe mismatched files
+//
+const char* archName(const uint8_t* fileContent)
+{
+ if ( Parser<x86_64>::validFile(fileContent, true) ) {
+ return Parser<x86_64>::fileKind(fileContent);
+ }
+ if ( Parser<x86>::validFile(fileContent, true) ) {
+ return Parser<x86>::fileKind(fileContent);
+ }
+ if ( Parser<arm>::validFile(fileContent, true) ) {
+ return Parser<arm>::fileKind(fileContent);
+ }
+#if SUPPORT_ARCH_arm64
+ if ( Parser<arm64>::validFile(fileContent, false) ) {
+ return Parser<arm64>::fileKind(fileContent);
+ }
+#endif
+ 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, uint32_t ordinal, bool bundleLoader)
+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<x86_64>::validFile(fileContent, bundleLoader) )
- return Parser<x86_64>::parse(fileContent, fileLength, path, modTime, ordinal, opts);
+ if ( Parser<x86_64>::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) )
+ return Parser<x86_64>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
break;
+#endif
+#if SUPPORT_ARCH_i386
case CPU_TYPE_I386:
- if ( Parser<x86>::validFile(fileContent, bundleLoader) )
- return Parser<x86>::parse(fileContent, fileLength, path, modTime, ordinal, opts);
+ if ( Parser<x86>::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) )
+ return Parser<x86>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
break;
+#endif
+#if SUPPORT_ARCH_arm_any
case CPU_TYPE_ARM:
- if ( Parser<arm>::validFile(fileContent, bundleLoader) )
- return Parser<arm>::parse(fileContent, fileLength, path, modTime, ordinal, opts);
- break;
- case CPU_TYPE_POWERPC:
- if ( Parser<ppc>::validFile(fileContent, bundleLoader) )
- return Parser<ppc>::parse(fileContent, fileLength, path, modTime, ordinal, opts);
+ if ( Parser<arm>::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) )
+ return Parser<arm>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
break;
- case CPU_TYPE_POWERPC64:
- if ( Parser<ppc64>::validFile(fileContent, bundleLoader) )
- return Parser<ppc64>::parse(fileContent, fileLength, path, modTime, ordinal, opts);
+#endif
+#if SUPPORT_ARCH_arm64
+ case CPU_TYPE_ARM64:
+ if ( Parser<arm64>::validFile(fileContent, bundleLoader, subTypeMustMatch, opts.subArchitecture()) )
+ return Parser<arm64>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
break;
+#endif
}
- return NULL;
+ return nullptr;
}
}; // namespace dylib
}; // namespace mach_o
-
-