X-Git-Url: https://git.saurik.com/apple/ld64.git/blobdiff_plain/60ce07c1e7dbeedd94a57ba21c14ff07c4ada4db..82b4b32b1851b4bba2473356c2208bd34561ee02:/src/ld/Options.cpp diff --git a/src/ld/Options.cpp b/src/ld/Options.cpp index c06e1f3..d36bc02 100644 --- a/src/ld/Options.cpp +++ b/src/ld/Options.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005-2010 Apple Inc. All rights reserved. + * Copyright (c) 2005-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -26,15 +26,29 @@ #include #include #include +#include #include #include +#include +#include +#include +#include +#include +#include #include +#include +#include -#include "configure.h" #include "Options.h" #include "Architectures.hpp" #include "MachOFileAbstraction.hpp" +#include "Snapshot.h" + + +// from FunctionNameDemangle.h +extern "C" size_t fnd_get_demangled_name(const char *mangledName, char *outputBuffer, size_t length); + // upward dependency on lto::version() namespace lto { @@ -43,16 +57,29 @@ namespace lto { // magic to place command line in crash reports const int crashreporterBufferSize = 2000; -extern "C" char* __crashreporter_info__; static char crashreporterBuffer[crashreporterBufferSize]; -char* __crashreporter_info__ = crashreporterBuffer; +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 + #include + // hack until ld does not need to build on 10.6 anymore + struct crashreporter_annotations_t gCRAnnotations + __attribute__((section("__DATA," CRASHREPORTER_ANNOTATIONS_SECTION))) + = { CRASHREPORTER_ANNOTATIONS_VERSION, 0, 0, 0, 0, 0, 0 }; +#else + extern "C" char* __crashreporter_info__; + __attribute__((used)) + char* __crashreporter_info__ = crashreporterBuffer; +#endif + static bool sEmitWarnings = true; +static bool sFatalWarnings = false; static const char* sWarningsSideFilePath = NULL; static FILE* sWarningsSideFile = NULL; +static int sWarningsCount = 0; void warning(const char* format, ...) { + ++sWarningsCount; if ( sEmitWarnings ) { va_list list; if ( sWarningsSideFilePath != NULL ) { @@ -85,12 +112,30 @@ void throwf(const char* format, ...) throw t; } + +bool Options::FileInfo::checkFileExists(const Options& options, const char *p) +{ + struct stat statBuffer; + if (p == NULL) + p = path; + if ( stat(p, &statBuffer) == 0 ) { + if (p != path) path = strdup(p); + modTime = statBuffer.st_mtime; + return true; + } + if ( options.dumpDependencyInfo() ) + options.dumpDependency(Options::depNotFound, p); +// fprintf(stderr, "not found: %s\n", p); + return false; +} + + Options::Options(int argc, const char* argv[]) : fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fArchitectureName("unknown"), fOutputKind(kDynamicExecutable), - fHasPreferredSubType(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false), + fHasPreferredSubType(false), fArchSupportsThumb2(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false), fNeedsModuleTable(false), fIgnoreOtherArchFiles(false), fErrorOnOtherArchFiles(false), fForceSubtypeAll(false), fInterposeMode(kInterposeNone), fDeadStrip(false), fNameSpace(kTwoLevelNameSpace), - fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName("start"), + fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName(NULL), fBaseAddress(0), fMaxAddress(0x7FFFFFFFFFFFFFFFLL), fBaseWritableAddress(0), fSplitSegs(false), fExportMode(kExportDefault), fLibrarySearchMode(kSearchDylibAndArchiveInEachDir), @@ -99,41 +144,57 @@ Options::Options(int argc, const char* argv[]) fClientName(NULL), fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL), fBundleLoader(NULL), fDtraceScriptName(NULL), fSegAddrTablePath(NULL), fMapPath(NULL), - fDyldInstallPath("/usr/lib/dyld"), fTempLtoObjectPath(NULL), - fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fExecutableStack(false), + fDyldInstallPath("/usr/lib/dyld"), fLtoCachePath(NULL), fTempLtoObjectPath(NULL), fOverridePathlibLTO(NULL), fLtoCpu(NULL), + fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fSourceVersion(0), fSDKVersion(0), fExecutableStack(false), fNonExecutableHeap(false), fDisableNonExecutableHeap(false), fMinimumHeaderPad(32), fSegmentAlignment(4096), fCommonsMode(kCommonsIgnoreDylibs), fUUIDMode(kUUIDContent), fLocalSymbolHandling(kLocalSymbolsAll), fWarnCommons(false), fVerbose(false), fKeepRelocations(false), fWarnStabs(false), fTraceDylibSearching(false), fPause(false), fStatistics(false), fPrintOptions(false), - fSharedRegionEligible(false), fPrintOrderFileStatistics(false), + fSharedRegionEligible(false), fSharedRegionEligibleForceOff(false), fPrintOrderFileStatistics(false), fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), fPIEOnCommandLine(false), fDisablePositionIndependentExecutable(false), fMaxMinimumHeaderPad(false), - fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false), - fUsingLazyDylibLinking(false), fEncryptable(true), + fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false), fKextsUseStubs(false), + fUsingLazyDylibLinking(false), fEncryptable(true), fEncryptableForceOn(false), fEncryptableForceOff(false), fOrderData(true), fMarkDeadStrippableDylib(false), fMakeCompressedDyldInfo(true), fMakeCompressedDyldInfoForceOff(false), fNoEHLabels(false), - fAllowCpuSubtypeMismatches(false), fUseSimplifiedDylibReExports(false), + fAllowCpuSubtypeMismatches(false), fEnforceDylibSubtypesMatch(false), fUseSimplifiedDylibReExports(false), fObjCABIVersion2Override(false), fObjCABIVersion1Override(false), fCanUseUpwardDylib(false), fFullyLoadArchives(false), fLoadAllObjcObjectsFromArchives(false), fFlatNamespace(false), fLinkingMainExecutable(false), fForFinalLinkedImage(false), fForStatic(false), fForDyld(false), fMakeTentativeDefinitionsReal(false), fWhyLoad(false), fRootSafe(false), fSetuidSafe(false), fImplicitlyLinkPublicDylibs(true), fAddCompactUnwindEncoding(true), fWarnCompactUnwind(false), fRemoveDwarfUnwindIfCompactExists(false), - fAutoOrderInitializers(true), fOptimizeZeroFill(true), fLogObjectFiles(false), - fLogAllFiles(false), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), + fAutoOrderInitializers(true), fOptimizeZeroFill(true), fMergeZeroFill(false), fLogObjectFiles(false), + fLogAllFiles(false), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), fTraceEmitJSON(false), fOutputSlidable(false), fWarnWeakExports(false), fObjcGcCompaction(false), fObjCGc(false), fObjCGcOnly(false), fDemangle(false), fTLVSupport(false), -#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1070 - fVersionLoadCommand(false), fFunctionStartsLoadCommand(false), -#else - fVersionLoadCommand(true), fFunctionStartsLoadCommand(true), -#endif - fCanReExportSymbols(false), fObjcCategoryMerging(true), - fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), - fMacVersionMin(ld::macVersionUnset), fIPhoneVersionMin(ld::iPhoneVersionUnset), - fSaveTempFiles(false) + fVersionLoadCommand(false), fVersionLoadCommandForcedOn(false), + fVersionLoadCommandForcedOff(false), fFunctionStartsLoadCommand(false), + fFunctionStartsForcedOn(false), fFunctionStartsForcedOff(false), + fDataInCodeInfoLoadCommand(false), fDataInCodeInfoLoadCommandForcedOn(false), fDataInCodeInfoLoadCommandForcedOff(false), + fCanReExportSymbols(false), fObjcCategoryMerging(true), fPageAlignDataAtoms(false), + fNeedsThreadLoadCommand(false), fEntryPointLoadCommand(false), + fEntryPointLoadCommandForceOn(false), fEntryPointLoadCommandForceOff(false), + fSourceVersionLoadCommand(false), + fSourceVersionLoadCommandForceOn(false), fSourceVersionLoadCommandForceOff(false), + fTargetIOSSimulator(false), fExportDynamic(false), fAbsoluteSymbols(false), + fAllowSimulatorToLinkWithMacOSX(false), fKeepDwarfUnwind(true), + fKeepDwarfUnwindForcedOn(false), fKeepDwarfUnwindForcedOff(false), + fVerboseOptimizationHints(false), fIgnoreOptimizationHints(false), + fGenerateDtraceDOF(true), fAllowBranchIslands(true), fTraceSymbolLayout(false), + fMarkAppExtensionSafe(false), fCheckAppExtensionSafe(false), fForceLoadSwiftLibs(false), + fSharedRegionEncodingV2(false), fUseDataConstSegment(false), + fUseDataConstSegmentForceOn(false), fUseDataConstSegmentForceOff(false), fUseTextExecSegment(false), + fBundleBitcode(false), fHideSymbols(false), fVerifyBitcode(false), + fReverseMapUUIDRename(false), fDeDupe(true), fVerboseDeDupe(false), + fReverseMapPath(NULL), fLTOCodegenOnly(false), + fIgnoreAutoLink(false), fAllowDeadDups(false), fAllowWeakImports(true), fBitcodeKind(kBitcodeProcess), + fPlatform(kPlatformUnknown), fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), + fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), fWatchOSVersionMin(ld::wOSVersionUnset), + fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL), + fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1), fTraceFileDescriptor(-1), fMaxDefaultCommonAlign(0) { this->checkForClassic(argc, argv); this->parsePreCommandLineEnvironmentSettings(); @@ -141,13 +202,27 @@ Options::Options(int argc, const char* argv[]) this->parsePostCommandLineEnvironmentSettings(); this->reconfigureDefaults(); this->checkIllegalOptionCombinations(); + + if ( this->dumpDependencyInfo() ) { + this->dumpDependency(depOutputFile, fOutputFile); + if ( fMapPath != NULL ) + this->dumpDependency(depOutputFile, fMapPath); + } } Options::~Options() { -} + if ( fDependencyFileDescriptor != -1 ) + ::close(fDependencyFileDescriptor); + if ( fTraceFileDescriptor != -1 ) + ::close(fTraceFileDescriptor); +} +bool Options::errorBecauseOfWarnings() const +{ + return (sFatalWarnings && (sWarningsCount > 0)); +} const char* Options::installPath() const @@ -175,11 +250,9 @@ bool Options::interposable(const char* name) const } - - bool Options::printWhyLive(const char* symbolName) const { - return ( fWhyLive.find(symbolName) != fWhyLive.end() ); + return fWhyLive.contains(symbolName); } @@ -209,7 +282,11 @@ bool Options::allGlobalsAreDeadStripRoots() const // switch ( fOutputKind ) { case Options::kDynamicExecutable: + // Add the -export_dynamic flag + return fExportDynamic; case Options::kStaticExecutable: + // Support the -export_dynamic flag for xnu + return fExportDynamic; case Options::kPreload: // by default unused globals in a main executable are stripped return false; @@ -239,7 +316,6 @@ const char* Options::executablePath() return fExecutablePath; } - uint32_t Options::initialSegProtection(const char* segName) const { for(std::vector::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) { @@ -247,12 +323,15 @@ uint32_t Options::initialSegProtection(const char* segName) const return it->init; } } - if ( strcmp(segName, "__PAGEZERO") == 0 ) { - return 0; + if ( strcmp(segName, "__TEXT") == 0 ) { + return ( fUseTextExecSegment ? VM_PROT_READ : (VM_PROT_READ | VM_PROT_EXECUTE) ); } - else if ( strcmp(segName, "__TEXT") == 0 ) { + else if ( strcmp(segName, "__TEXT_EXEC") == 0 ) { return VM_PROT_READ | VM_PROT_EXECUTE; } + else if ( strcmp(segName, "__PAGEZERO") == 0 ) { + return 0; + } else if ( strcmp(segName, "__LINKEDIT") == 0 ) { return VM_PROT_READ; } @@ -264,7 +343,8 @@ uint32_t Options::initialSegProtection(const char* segName) const uint32_t Options::maxSegProtection(const char* segName) const { // iPhoneOS always uses same protection for max and initial - if ( fIPhoneVersionMin != ld::iPhoneVersionUnset ) + // simulator apps need to use MacOSX max-prot + if ( (fPlatform != kPlatformOSX) && !fTargetIOSSimulator ) return initialSegProtection(segName); for(std::vector::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) { @@ -318,6 +398,9 @@ bool Options::hasCustomSectionAlignment(const char* segName, const char* sectNam if ( (strcmp(it->segmentName, segName) == 0) && (strcmp(it->sectionName, sectName) == 0) ) return true; } + if ( fEncryptable && (strcmp(sectName, "__oslogstring") == 0) && (strcmp(segName, "__TEXT") == 0) ) + return true; + return false; } @@ -327,9 +410,23 @@ uint8_t Options::customSectionAlignment(const char* segName, const char* sectNam if ( (strcmp(it->segmentName, segName) == 0) && (strcmp(it->sectionName, sectName) == 0) ) return it->alignment; } + if ( fEncryptable && (strcmp(sectName, "__oslogstring") == 0) && (strcmp(segName, "__TEXT") == 0) ) + return __builtin_ctz(fSegmentAlignment); + return 0; } +bool Options::segmentOrderAfterFixedAddressSegment(const char* segName) const +{ + bool nowPinned = false; + for (std::vector::const_iterator it=fSegmentOrder.begin(); it != fSegmentOrder.end(); ++it) { + if ( strcmp(*it, segName) == 0 ) + return nowPinned; + if ( hasCustomSegmentAddress(*it) ) + nowPinned = true; + } + return false; +} bool Options::hasExportedSymbolOrder() { @@ -366,7 +463,7 @@ void Options::loadSymbolOrderFile(const char* fileOfExports, NameToOrder& orderM ::close(fd); - // parse into symbols and add to hash_set + // parse into symbols and add to unordered_set unsigned int count = 0; char * const end = &p[stat_buf.st_size]; enum { lineStart, inSymbol, inComment } state = lineStart; @@ -440,6 +537,11 @@ bool Options::forceNotWeakNonWildcard(const char* symbolName) const return fForceNotWeakSymbols.containsNonWildcard(symbolName); } +bool Options::forceCoalesce(const char* symbolName) const +{ + return fForceCoalesceSymbols.contains(symbolName); +} + bool Options::shouldExport(const char* symbolName) const { @@ -474,228 +576,125 @@ bool Options::keepLocalSymbol(const char* symbolName) const throw "internal error"; } +const std::vector* Options::sectionOrder(const char* segName) const +{ + for (std::vector::const_iterator it=fSectionOrder.begin(); it != fSectionOrder.end(); ++it) { + if ( strcmp(it->segmentName, segName) == 0 ) + return &it->sectionOrder; + } + return NULL; +} -void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype) +uint32_t Options::minOSversion() const { - fArchitecture = type; - fSubArchitecture = subtype; - switch ( type ) { - case CPU_TYPE_POWERPC: - switch ( subtype ) { - case CPU_SUBTYPE_POWERPC_750: - fArchitectureName = "ppc750"; - fHasPreferredSubType = true; - break; - case CPU_SUBTYPE_POWERPC_7400: - fArchitectureName = "ppc7400"; - fHasPreferredSubType = true; - break; - case CPU_SUBTYPE_POWERPC_7450: - fArchitectureName = "ppc7450"; - fHasPreferredSubType = true; - break; - case CPU_SUBTYPE_POWERPC_970: - fArchitectureName = "ppc970"; - fHasPreferredSubType = true; - break; - case CPU_SUBTYPE_POWERPC_ALL: - fArchitectureName = "ppc"; - fHasPreferredSubType = false; - break; - default: - assert(0 && "unknown ppc subtype"); - fArchitectureName = "ppc"; - break; - } - if ( (fMacVersionMin == ld::macVersionUnset) && (fOutputKind != Options::kObjectFile) ) { - #ifdef DEFAULT_MACOSX_MIN_VERSION - warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); - setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); - #else - warning("-macosx_version_min not specificed, assuming 10.6"); - fMacVersionMin = ld::mac10_6; - #endif - } - break; - case CPU_TYPE_POWERPC64: - fArchitectureName = "ppc64"; - if ( (fMacVersionMin == ld::macVersionUnset) && (fOutputKind != Options::kObjectFile) ) { - warning("-macosx_version_min not specificed, assuming 10.5"); - fMacVersionMin = ld::mac10_5; - } - break; - case CPU_TYPE_I386: - fArchitectureName = "i386"; - if ( (fMacVersionMin == ld::macVersionUnset) && (fOutputKind != Options::kObjectFile) ) { - #ifdef DEFAULT_MACOSX_MIN_VERSION - warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); - setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); - #else - warning("-macosx_version_min not specificed, assuming 10.6"); - fMacVersionMin = ld::mac10_6; - #endif - } - if ( !fMakeCompressedDyldInfo && (fMacVersionMin >= ld::mac10_6) && !fMakeCompressedDyldInfoForceOff ) - fMakeCompressedDyldInfo = true; - break; - case CPU_TYPE_X86_64: - fArchitectureName = "x86_64"; - if ( (fMacVersionMin == ld::macVersionUnset) && (fOutputKind != Options::kObjectFile) ) { - #ifdef DEFAULT_MACOSX_MIN_VERSION - warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); - setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); - #else - warning("-macosx_version_min not specificed, assuming 10.6"); - fMacVersionMin = ld::mac10_6; - #endif - } - if ( !fMakeCompressedDyldInfo && (fMacVersionMin >= ld::mac10_6) && !fMakeCompressedDyldInfoForceOff ) - fMakeCompressedDyldInfo = true; - break; - case CPU_TYPE_ARM: - switch ( subtype ) { - case CPU_SUBTYPE_ARM_V4T: - fArchitectureName = "armv4t"; - fHasPreferredSubType = true; - break; - case CPU_SUBTYPE_ARM_V5TEJ: - fArchitectureName = "armv5"; - fHasPreferredSubType = true; - break; - case CPU_SUBTYPE_ARM_V6: - fArchitectureName = "armv6"; - fHasPreferredSubType = true; - break; - case CPU_SUBTYPE_ARM_V7: - fArchitectureName = "armv7"; - fHasPreferredSubType = true; - break; + switch (fPlatform) { + case kPlatformiOS: + return iOSVersionMin(); + case kPlatformOSX: + return macosxVersionMin(); + case kPlatformWatchOS: + return watchOSVersionMin(); +#if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + return iOSVersionMin(); +#endif default: - assert(0 && "unknown arm subtype"); - fArchitectureName = "arm"; break; + } + return 0; +} + +void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype, Options::Platform platform) +{ + for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { + if ( (type == t->cpuType) && (subtype == t->cpuSubType) ) { + fArchitecture = type; + fSubArchitecture = subtype; + fArchitectureName = t->archName; + fHasPreferredSubType = t->isSubType; + fArchSupportsThumb2 = t->supportsThumb2; + fPlatform = platform; + switch ( type ) { + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + if ( (fPlatform == kPlatformOSX) && (fOutputKind != Options::kObjectFile) && (fMacVersionMin == ld::macVersionUnset) ) { + #ifdef DEFAULT_MACOSX_MIN_VERSION + warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION); + setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); + #else + warning("-macosx_version_min not specified, assuming 10.6"); + fMacVersionMin = ld::mac10_6; + #endif + } + break; + case CPU_TYPE_ARM: + case CPU_TYPE_ARM64: + if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) { + #if defined(DEFAULT_IPHONEOS_MIN_VERSION) + warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION); + setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); + #else + warning("-ios_version_min not specified, assuming 6.0"); + setIOSVersionMin("6.0"); + #endif + } + break; + } + fLinkSnapshot.recordArch(fArchitectureName); + // only use compressed LINKEDIT for: + // Mac OS X 10.6 or later + // iOS 3.1 or later + if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff ) + fMakeCompressedDyldInfo = true; + // Mac OS X 10.5 and iPhoneOS 2.0 support LC_REEXPORT_DYLIB + if ( minOS(ld::mac10_5, ld::iOS_2_0) ) + fUseSimplifiedDylibReExports = true; + return; } - if ( (fMacVersionMin == ld::macVersionUnset) && (fIPhoneVersionMin == ld::iPhoneVersionUnset) && (fOutputKind != Options::kObjectFile) ) { -#if defined(DEFAULT_IPHONEOS_MIN_VERSION) - warning("-ios_version_min not specificed, assuming " DEFAULT_IPHONEOS_MIN_VERSION); - setIPhoneVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); -#elif defined(DEFAULT_MACOSX_MIN_VERSION) - warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); - setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); -#else - warning("-macosx_version_min not specificed, assuming 10.6"); - fMacVersionMin = ld::mac10_6; -#endif - } - if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iPhone3_1) && !fMakeCompressedDyldInfoForceOff ) - fMakeCompressedDyldInfo = true; - break; - default: - fArchitectureName = "unknown architecture"; - break; } + fArchitectureName = "unknown architecture"; +} + +bool Options::armUsesZeroCostExceptions() const +{ + return ( (fArchitecture == CPU_TYPE_ARM) && (fSubArchitecture == CPU_SUBTYPE_ARM_V7K) ); } void Options::parseArch(const char* arch) { if ( arch == NULL ) throw "-arch must be followed by an architecture string"; - fArchitectureName = arch; - if ( strcmp(arch, "ppc") == 0 ) { - fArchitecture = CPU_TYPE_POWERPC; - fSubArchitecture = CPU_SUBTYPE_POWERPC_ALL; - } - else if ( strcmp(arch, "ppc64") == 0 ) { - fArchitecture = CPU_TYPE_POWERPC64; - fSubArchitecture = CPU_SUBTYPE_POWERPC_ALL; - } - else if ( strcmp(arch, "i386") == 0 ) { - fArchitecture = CPU_TYPE_I386; - fSubArchitecture = CPU_SUBTYPE_I386_ALL; - } - else if ( strcmp(arch, "x86_64") == 0 ) { - fArchitecture = CPU_TYPE_X86_64; - fSubArchitecture = CPU_SUBTYPE_X86_64_ALL; - } - else if ( strcmp(arch, "arm") == 0 ) { - fArchitecture = CPU_TYPE_ARM; - fSubArchitecture = CPU_SUBTYPE_ARM_ALL; - } - // compatibility support for cpu-sub-types - else if ( strcmp(arch, "ppc750") == 0 ) { - fArchitecture = CPU_TYPE_POWERPC; - fSubArchitecture = CPU_SUBTYPE_POWERPC_750; - fHasPreferredSubType = true; - } - else if ( strcmp(arch, "ppc7400") == 0 ) { - fArchitecture = CPU_TYPE_POWERPC; - fSubArchitecture = CPU_SUBTYPE_POWERPC_7400; - fHasPreferredSubType = true; - } - else if ( strcmp(arch, "ppc7450") == 0 ) { - fArchitecture = CPU_TYPE_POWERPC; - fSubArchitecture = CPU_SUBTYPE_POWERPC_7450; - fHasPreferredSubType = true; - } - else if ( strcmp(arch, "ppc970") == 0 ) { - fArchitecture = CPU_TYPE_POWERPC; - fSubArchitecture = CPU_SUBTYPE_POWERPC_970; - fHasPreferredSubType = true; - } - else if ( strcmp(arch, "armv6") == 0 ) { - fArchitecture = CPU_TYPE_ARM; - fSubArchitecture = CPU_SUBTYPE_ARM_V6; - fHasPreferredSubType = true; - } - else if ( strcmp(arch, "armv5") == 0 ) { - fArchitecture = CPU_TYPE_ARM; - fSubArchitecture = CPU_SUBTYPE_ARM_V5TEJ; - fHasPreferredSubType = true; - } - else if ( strcmp(arch, "armv4t") == 0 ) { - fArchitecture = CPU_TYPE_ARM; - fSubArchitecture = CPU_SUBTYPE_ARM_V4T; - fHasPreferredSubType = true; - } - else if ( strcmp(arch, "xscale") == 0 ) { - fArchitecture = CPU_TYPE_ARM; - fSubArchitecture = CPU_SUBTYPE_ARM_XSCALE; - fHasPreferredSubType = true; - } - else if ( strcmp(arch, "armv7") == 0 ) { - fArchitecture = CPU_TYPE_ARM; - fSubArchitecture = CPU_SUBTYPE_ARM_V7; - fHasPreferredSubType = true; + for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { + if ( strcmp(t->archName,arch) == 0 ) { + fArchitectureName = arch; + fArchitecture = t->cpuType; + fSubArchitecture = t->cpuSubType; + fHasPreferredSubType = t->isSubType; + fArchSupportsThumb2 = t->supportsThumb2; + return; + } } - else - throwf("unknown/unsupported architecture name for: -arch %s", arch); + throwf("unknown/unsupported architecture name for: -arch %s", arch); } bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) const { - struct stat statBuffer; char possiblePath[strlen(dir)+strlen(rootName)+strlen(format)+8]; sprintf(possiblePath, format, dir, rootName); - bool found = (stat(possiblePath, &statBuffer) == 0); + bool found = result.checkFileExists(*this, possiblePath); if ( fTraceDylibSearching ) printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), possiblePath); - if ( found ) { - result.path = strdup(possiblePath); - result.fileLen = statBuffer.st_size; - result.modTime = statBuffer.st_mtime; - return true; - } - return false; + return found; } -Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) +Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) const { FileInfo result; const int rootNameLen = strlen(rootName); // if rootName ends in .o there is no .a vs .dylib choice if ( (rootNameLen > 3) && (strcmp(&rootName[rootNameLen-2], ".o") == 0) ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; @@ -704,19 +703,34 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) } } else { - bool lookForDylibs = ( fOutputKind != Options::kDyld); + bool lookForDylibs = false; + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: // + lookForDylibs = true; + break; + case Options::kStaticExecutable: + case Options::kDyld: + case Options::kPreload: + case Options::kKextBundle: + lookForDylibs = false; + break; + } switch ( fLibrarySearchMode ) { case kSearchAllDirsForDylibsThenAllDirsForArchives: // first look in all directories for just for dylibs if ( lookForDylibs ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; - if ( checkForFile("%s/lib%s.dylib", dir, rootName, result) ) + auto path = std::string(dir) + "/lib" + rootName + ".dylib"; + if ( findFile(path, {".tbd"}, result) ) return result; } - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; @@ -726,7 +740,7 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) } // next look in all directories for just for archives if ( !dylibsOnly ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; @@ -738,11 +752,12 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) case kSearchDylibAndArchiveInEachDir: // look in each directory for just for a dylib then for an archive - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; - if ( lookForDylibs && checkForFile("%s/lib%s.dylib", dir, rootName, result) ) + auto path = std::string(dir) + "/lib" + rootName + ".dylib"; + if ( lookForDylibs && findFile(path, {".tbd"}, result) ) return result; if ( lookForDylibs && checkForFile("%s/lib%s.so", dir, rootName, result) ) return result; @@ -755,7 +770,7 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) throwf("library not found for -l%s", rootName); } -Options::FileInfo Options::findFramework(const char* frameworkName) +Options::FileInfo Options::findFramework(const char* frameworkName) const { if ( frameworkName == NULL ) throw "-framework missing next argument"; @@ -771,40 +786,19 @@ Options::FileInfo Options::findFramework(const char* frameworkName) return findFramework(name, suffix); } -Options::FileInfo Options::findFramework(const char* rootName, const char* suffix) +Options::FileInfo Options::findFramework(const char* rootName, const char* suffix) const { - struct stat statBuffer; - for (std::vector::iterator it = fFrameworkSearchPaths.begin(); - it != fFrameworkSearchPaths.end(); - it++) { - // ??? Shouldn't we be using String here and just initializing it? - // ??? Use str.c_str () to pull out the string for the stat call. - const char* dir = *it; - char possiblePath[PATH_MAX]; - strcpy(possiblePath, dir); - strcat(possiblePath, "/"); - strcat(possiblePath, rootName); - strcat(possiblePath, ".framework/"); - strcat(possiblePath, rootName); - if ( suffix != NULL ) { + for (const auto* path : fFrameworkSearchPaths) { + auto possiblePath = std::string(path).append("/").append(rootName).append(".framework/").append(rootName); + if ( suffix != nullptr ) { char realPath[PATH_MAX]; // no symlink in framework to suffix variants, so follow main symlink - if ( realpath(possiblePath, realPath) != NULL ) { - strcpy(possiblePath, realPath); - strcat(possiblePath, suffix); - } + if ( realpath(possiblePath.c_str(), realPath) != nullptr ) + possiblePath = std::string(realPath).append(suffix); } - bool found = (stat(possiblePath, &statBuffer) == 0); - if ( fTraceDylibSearching ) - printf("[Logging for XBS]%sfound framework: '%s'\n", - (found ? " " : " not "), possiblePath); - if ( found ) { - FileInfo result; - result.path = strdup(possiblePath); - result.fileLen = statBuffer.st_size; - result.modTime = statBuffer.st_mtime; + FileInfo result; + if ( findFile(possiblePath, {".tbd"}, result) ) return result; - } } // try without suffix if ( suffix != NULL ) @@ -813,78 +807,153 @@ Options::FileInfo Options::findFramework(const char* rootName, const char* suffi throwf("framework not found %s", rootName); } -Options::FileInfo Options::findFile(const char* path) const +static std::string replace_extension(const std::string &path, const std::string &ext) +{ + auto result = path; + auto lastSlashIdx = result.find_last_of('/'); + auto lastDotIdx = result.find_last_of('.'); + if (lastDotIdx != std::string::npos && lastDotIdx > lastSlashIdx) + result.erase(lastDotIdx, std::string::npos); + if ( ext.size() > 0 && ext[0] == '.' ) + result.append(ext); + else + result.append('.' + ext); + return result; +} + +bool Options::findFile(const std::string &path, const std::vector &tbdExtensions, FileInfo& result) const +{ + FileInfo tbdInfo; + for ( const auto &ext : tbdExtensions ) { + auto newPath = replace_extension(path, ext); + bool found = tbdInfo.checkFileExists(*this, newPath.c_str()); + if ( fTraceDylibSearching ) + printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), newPath.c_str()); + if ( found ) + break; + } + + FileInfo dylibInfo; + { + bool found = dylibInfo.checkFileExists(*this, path.c_str()); + if ( fTraceDylibSearching ) + printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), path.c_str()); + } + + // There is only a text-based stub file or a dynamic library file. + if ( tbdInfo.missing() != dylibInfo.missing() ) { + result = tbdInfo.missing() ? dylibInfo : tbdInfo; + } + // There are both - a text-based stub file and a dynamic library file. + else if ( !tbdInfo.missing() && !dylibInfo.missing() ) { + // Check if we should prefer the text-based stub file (installapi). + if (tapi::LinkerInterfaceFile::shouldPreferTextBasedStubFile(tbdInfo.path)) { + result = tbdInfo; + } + // If the files are still in sync we can use and should use the text-based stub file. + else if (tapi::LinkerInterfaceFile::areEquivalent(tbdInfo.path, dylibInfo.path)) { + result = tbdInfo; + } + // Otherwise issue a warning and fall-back to the dynamic library file. + else { + warning("text-based stub file %s and library file %s are out of sync. Falling back to library file for linking.", tbdInfo.path, dylibInfo.path); + result = dylibInfo; + } + } else { + return false; + } + + return true; +} + +static bool startsWith(const std::string& str, const std::string& prefix) +{ + return (str.compare(0, prefix.length(), prefix) == 0); +} + +static std::string getDirPath(const std::string& path) +{ + std::string::size_type lastSlashPos = path.find_last_of('/'); + if ( lastSlashPos == std::string::npos ) + return "./"; + else + return path.substr(0, lastSlashPos+1); +} + +Options::FileInfo Options::findFile(const std::string &path, const ld::dylib::File* fromDylib) const { FileInfo result; - struct stat statBuffer; - // if absolute path and not a .o file, the use SDK prefix - if ( (path[0] == '/') && (strcmp(&path[strlen(path)-2], ".o") != 0) ) { - const int pathLen = strlen(path); - for (std::vector::const_iterator it = fSDKPaths.begin(); it != fSDKPaths.end(); it++) { - // ??? Shouldn't we be using String here? - const char* sdkPathDir = *it; - const int sdkPathDirLen = strlen(sdkPathDir); - char possiblePath[sdkPathDirLen+pathLen+4]; - strcpy(possiblePath, sdkPathDir); - if ( possiblePath[sdkPathDirLen-1] == '/' ) - possiblePath[sdkPathDirLen-1] = '\0'; - strcat(possiblePath, path); - if ( stat(possiblePath, &statBuffer) == 0 ) { - result.path = strdup(possiblePath); - result.fileLen = statBuffer.st_size; - result.modTime = statBuffer.st_mtime; + // if absolute path and not a .o file, then use SDK prefix + if ( (path[0] == '/') && (strcmp(&path[path.size()-2], ".o") != 0) ) { + for (const auto* sdkPathDir : fSDKPaths) { + auto possiblePath = std::string(sdkPathDir) + path; + if ( findFile(possiblePath, {".tbd"}, result) ) return result; - } } } - // try raw path - if ( stat(path, &statBuffer) == 0 ) { - result.path = strdup(path); - result.fileLen = statBuffer.st_size; - result.modTime = statBuffer.st_mtime; - return result; - } - // try @executable_path substitution - if ( (strncmp(path, "@executable_path/", 17) == 0) && (fExecutablePath != NULL) ) { - char newPath[strlen(fExecutablePath) + strlen(path)]; - strcpy(newPath, fExecutablePath); - char* addPoint = strrchr(newPath,'/'); - if ( addPoint != NULL ) - strcpy(&addPoint[1], &path[17]); - else - strcpy(newPath, &path[17]); - if ( stat(newPath, &statBuffer) == 0 ) { - result.path = strdup(newPath); - result.fileLen = statBuffer.st_size; - result.modTime = statBuffer.st_mtime; - return result; + // expand @ variables + if ( path[0] == '@' ) { + if ( startsWith(path, "@executable_path/") && (fExecutablePath != nullptr) ) { + std::string exeBasedPath = getDirPath(fExecutablePath) + &path[17]; + if ( findFile(exeBasedPath, {".tbd"}, result) ) + return result; + } + else if ( startsWith(path, "@loader_path/") && (fromDylib != nullptr) ) { + char absPath[PATH_MAX]; + if ( realpath(fromDylib->path(), absPath) != NULL ) { + std::string loaderBasedPath = getDirPath(fromDylib->path()) + &path[13]; + if ( findFile(loaderBasedPath, {".tbd"}, result) ) + return result; + } + } + else if ( startsWith(path, "@rpath/") ) { + // first search any LC_RPATH supplied by dyld that re-exports dylib to be found + if ( fromDylib != nullptr ) { + for (const char* rp : fromDylib->rpaths() ) { + std::string rpath = rp; + // handle dylib that has LC_RPATH = @loader_path/blah + if ( startsWith(rpath, "@loader_path/") ) { + char absPath[PATH_MAX]; + if ( realpath(fromDylib->path(), absPath) != NULL ) + rpath = getDirPath(absPath) + &rpath[13]; + else + rpath = getDirPath(fromDylib->path()) + &rpath[13]; + } + std::string rpathBasedPath = rpath + "/" + &path[6]; + if ( findFile(rpathBasedPath, {".tbd"}, result) ) + return result; + } + } } } + // try raw path + if ( findFile(path, {".tbd"}, result) ) + return result; + // not found - throwf("file not found: %s", path); + throwf("file not found: %s", path.c_str()); } -Options::FileInfo Options::findFileUsingPaths(const char* path) const +// search for indirect dylib first using -F and -L paths first +Options::FileInfo Options::findIndirectDylib(const std::string& installName, const ld::dylib::File* fromDylib) const { FileInfo result; - const char* lastSlash = strrchr(path, '/'); - const char* leafName = (lastSlash == NULL) ? path : &lastSlash[1]; + auto lastSlashPos = installName.find_last_of('/'); + auto pos = ( lastSlashPos != std::string::npos ) ? lastSlashPos + 1 : 0; + auto leafName = installName.substr(pos); // Is this in a framework? // /path/Foo.framework/Foo ==> true (Foo) // /path/Foo.framework/Frameworks/Bar.framework/Bar ==> true (Bar) // /path/Foo.framework/Resources/Bar ==> false bool isFramework = false; - if ( lastSlash != NULL ) { - char frameworkDir[strlen(leafName) + 20]; - strcpy(frameworkDir, "/"); - strcat(frameworkDir, leafName); - strcat(frameworkDir, ".framework/"); - if ( strstr(path, frameworkDir) != NULL ) + if ( lastSlashPos != std::string::npos ) { + auto frameworkDir = std::string("/").append(leafName).append(".framework/"); + if ( installName.rfind(frameworkDir) != std::string::npos ) isFramework = true; } @@ -893,44 +962,35 @@ Options::FileInfo Options::findFileUsingPaths(const char* path) const // don't need to try variations, just paths. We do need to add the additional bits // onto the framework path though. if ( isFramework ) { - for (std::vector::const_iterator it = fFrameworkSearchPaths.begin(); - it != fFrameworkSearchPaths.end(); - it++) { - const char* dir = *it; - char possiblePath[PATH_MAX]; - strcpy(possiblePath, dir); - strcat(possiblePath, "/"); - strcat(possiblePath, leafName); - strcat(possiblePath, ".framework"); - - //fprintf(stderr,"Finding Framework: %s/%s, leafName=%s\n", possiblePath, leafName, leafName); - if ( checkForFile("%s/%s", possiblePath, leafName, result) ) + auto endPos = installName.rfind(".framework"); + auto beginPos = installName.find_last_of('/', endPos); + auto leafPath = installName.substr(beginPos); + for (const auto* dir : fFrameworkSearchPaths) { + auto possiblePath = dir + leafPath; + if ( findFile(possiblePath, {".tbd"}, result) ) return result; } - } - else { + } else { // if this is a .dylib inside a framework, do not search -L paths - // ld64's re-export cycle detection logic prevents use of X11 libGL on Leopard - int leafLen = strlen(leafName); - bool embeddedDylib = ( (leafLen > 6) - && (strcmp(&leafName[leafLen-6], ".dylib") == 0) - && (strstr(path, ".framework/") != NULL) ); + // ld64's re-export cycle detection logic prevents use of X11 libGL on Leopard + bool embeddedDylib = ( (leafName.size() > 6) + && (leafName.find(".dylib", leafName.size()-6) != std::string::npos) + && (installName.find(".framework/") != std::string::npos) ); if ( !embeddedDylib ) { - for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); - it != fLibrarySearchPaths.end(); - it++) { - const char* dir = *it; + for (const auto* dir : fLibrarySearchPaths) { //fprintf(stderr,"Finding Library: %s/%s\n", dir, leafName); - if ( checkForFile("%s/%s", dir, leafName, result) ) + std::string possiblePath = dir + std::string("/") + leafName; + if ( findFile(possiblePath, {".tbd"}, result) ) return result; } } } // If we didn't find it fall back to findFile. - return findFile(path); + return findFile(installName, fromDylib); } - + + void Options::parseSegAddrTable(const char* segAddrPath, const char* installPth) { @@ -984,7 +1044,7 @@ void Options::parseSegAddrTable(const char* segAddrPath, const char* installPth) fclose(file); } -void Options::loadFileList(const char* fileOfPaths) +void Options::loadFileList(const char* fileOfPaths, ld::File::Ordinal baseOrdinal) { FILE* file; const char* comma = strrchr(fileOfPaths, ','); @@ -1000,16 +1060,21 @@ void Options::loadFileList(const char* fileOfPaths) realFileOfPaths[realFileOfPathsLen] = '\0'; file = fopen(realFileOfPaths, "r"); if ( file == NULL ) - throwf("-filelist file not found: %s\n", realFileOfPaths); + throwf("-filelist file '%s' could not be opened, errno=%d (%s)\n", realFileOfPaths, errno, strerror(errno)); + if ( this->dumpDependencyInfo() ) + this->dumpDependency(Options::depFileList, realFileOfPaths); } } else { file = fopen(fileOfPaths, "r"); if ( file == NULL ) - throwf("-filelist file not found: %s\n", fileOfPaths); + throwf("-filelist file '%s' could not be opened, errno=%d (%s)\n", fileOfPaths, errno, strerror(errno)); + if ( this->dumpDependencyInfo() ) + this->dumpDependency(Options::depFileList, fileOfPaths); } char path[PATH_MAX]; + ld::File::Ordinal previousOrdinal = baseOrdinal; while ( fgets(path, PATH_MAX, file) != NULL ) { path[PATH_MAX-1] = '\0'; char* eol = strchr(path, '\n'); @@ -1020,10 +1085,34 @@ void Options::loadFileList(const char* fileOfPaths) strcpy(builtPath, prefix); strcat(builtPath, "/"); strcat(builtPath, path); - fInputFiles.push_back(findFile(builtPath)); + if (fPipelineFifo != NULL) { + FileInfo info = FileInfo(builtPath); + info.ordinal = previousOrdinal.nextFileListOrdinal(); + previousOrdinal = info.ordinal; + info.fromFileList = true; + fInputFiles.push_back(info); + } else { + FileInfo info = findFile(builtPath); + info.ordinal = previousOrdinal.nextFileListOrdinal(); + previousOrdinal = info.ordinal; + info.fromFileList = true; + fInputFiles.push_back(info); + } } else { - fInputFiles.push_back(findFile(path)); + if (fPipelineFifo != NULL) { + FileInfo info = FileInfo(path); + info.ordinal = previousOrdinal.nextFileListOrdinal(); + previousOrdinal = info.ordinal; + info.fromFileList = true; + fInputFiles.push_back(info); + } else { + FileInfo info = findFile(path); + info.ordinal = previousOrdinal.nextFileListOrdinal(); + previousOrdinal = info.ordinal; + info.fromFileList = true; + fInputFiles.push_back(info); + } } } fclose(file); @@ -1054,19 +1143,40 @@ void Options::SetWithWildcards::insert(const char* symbol) fRegular.insert(symbol); } -bool Options::SetWithWildcards::contains(const char* symbol) const +bool Options::SetWithWildcards::contains(const char* symbol, bool* matchBecauseOfWildcard) const { + if ( matchBecauseOfWildcard != NULL ) + *matchBecauseOfWildcard = false; // first look at hash table on non-wildcard symbols if ( fRegular.find(symbol) != fRegular.end() ) return true; // next walk list of wild card symbols looking for a match for(std::vector::const_iterator it = fWildCard.begin(); it != fWildCard.end(); ++it) { - if ( wildCardMatch(*it, symbol) ) + if ( wildCardMatch(*it, symbol) ) { + if ( matchBecauseOfWildcard != NULL ) + *matchBecauseOfWildcard = true; return true; + } } return false; } +// Support "foo.o:_bar" to mean symbol _bar in file foo.o +bool Options::SetWithWildcards::containsWithPrefix(const char* symbol, const char* file, bool& wildCardMatch) const +{ + wildCardMatch = false; + if ( contains(symbol, &wildCardMatch) ) + return true; + if ( file == NULL ) + return false; + const char* s = strrchr(file, '/'); + if ( s != NULL ) + file = s+1; + char buff[strlen(file)+strlen(symbol)+2]; + sprintf(buff, "%s:%s", file, symbol); + return contains(buff, &wildCardMatch); +} + bool Options::SetWithWildcards::containsNonWildcard(const char* symbol) const { // look at hash table on non-wildcard symbols @@ -1074,6 +1184,23 @@ bool Options::SetWithWildcards::containsNonWildcard(const char* symbol) const } +std::vector Options::exportsData() const +{ + return fExportSymbols.data(); +} + + +std::vector Options::SetWithWildcards::data() const +{ + std::vector data; + for (NameSet::iterator it=regularBegin(); it != regularEnd(); ++it) { + data.push_back(*it); + } + for (std::vector::const_iterator it=fWildCard.begin(); it != fWildCard.end(); ++it) { + data.push_back(*it); + } + return data; +} bool Options::SetWithWildcards::inCharRange(const char*& p, unsigned char c) const { @@ -1154,9 +1281,12 @@ void Options::loadExportFile(const char* fileOfExports, const char* option, SetW if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) throwf("can't read %s file: %s", option, fileOfExports); + if ( this->dumpDependencyInfo() ) + this->dumpDependency(Options::depMisc, fileOfExports); + ::close(fd); - // parse into symbols and add to hash_set + // parse into symbols and add to unordered_set char * const end = &p[stat_buf.st_size]; enum { lineStart, inSymbol, inComment } state = lineStart; char* symbolStart = NULL; @@ -1225,6 +1355,8 @@ void Options::parseAliasFile(const char* fileOfAliases) throwf("can't read alias file: %s", fileOfAliases); p[stat_buf.st_size] = '\n'; ::close(fd); + if ( this->dumpDependencyInfo() ) + this->dumpDependency(Options::depMisc, fileOfAliases); // parse into symbols and add to fAliases AliasPair pair; @@ -1334,44 +1466,60 @@ Options::Treatment Options::parseTreatment(const char* treatment) void Options::setMacOSXVersionMin(const char* version) { - if ( version == NULL ) - throw "-macosx_version_min argument missing"; - - if ( (strncmp(version, "10.", 3) == 0) && isdigit(version[3]) ) { - unsigned int minorVersion = version[3] - '0'; - fMacVersionMin = (ld::MacVersionMin)(0x000A0000 | (minorVersion << 8)); + uint32_t value; + if ( !parsePackedVersion32(version, value) ) { + throwf("-macosx_version_min value malformed: '%s'", version); } - else { - warning("unknown option to -macosx_version_min, not 10.x"); + fMacVersionMin = (ld::MacVersionMin)value; + fPlatform = kPlatformOSX; +} + +void Options::setIOSVersionMin(const char* version) +{ + uint32_t value; + if ( !parsePackedVersion32(version, value) ) { + throwf("-ios_version_min value malformed: '%s'", version); } + fIOSVersionMin = (ld::IOSVersionMin)value; + fPlatform = kPlatformiOS; } -void Options::setIPhoneVersionMin(const char* version) + +void Options::setWatchOSVersionMin(const char* version) { - if ( version == NULL ) - throw "-ios_version_min argument missing"; - if ( ! isdigit(version[0]) ) - throw "-ios_version_min argument is not a number"; - if ( version[1] != '.' ) - throw "-ios_version_min argument is missing period as second character"; - if ( ! isdigit(version[2]) ) - throw "-ios_version_min argument is not a number"; - - unsigned int majorVersion = version[0] - '0'; - unsigned int minorVersion = version[2] - '0'; - fIPhoneVersionMin = (ld::IPhoneVersionMin)((majorVersion << 16) | (minorVersion << 8)); + uint32_t value; + if ( !parsePackedVersion32(version, value) ) { + throwf("-watchos_version_min value malformed: '%s'", version); + } + fWatchOSVersionMin = (ld::WatchOSVersionMin)value; + fPlatform = kPlatformWatchOS; } -bool Options::minOS(ld::MacVersionMin requiredMacMin, ld::IPhoneVersionMin requirediPhoneOSMin) + +bool Options::minOS(ld::MacVersionMin requiredMacMin, ld::IOSVersionMin requirediPhoneOSMin) { if ( fMacVersionMin != ld::macVersionUnset ) { return ( fMacVersionMin >= requiredMacMin ); } + else if ( fWatchOSVersionMin != ld::wOSVersionUnset ) { + // Hack until we fully track watch and ios versions seperately + return ( (fWatchOSVersionMin + 0x00070000) >= requirediPhoneOSMin); + } else { - return ( fIPhoneVersionMin >= requirediPhoneOSMin); + return ( fIOSVersionMin >= requirediPhoneOSMin ); } } +bool Options::min_iOS(ld::IOSVersionMin requirediOSMin) +{ + if ( fWatchOSVersionMin != ld::wOSVersionUnset ) { + // Hack until we fully track watch and ios versions seperately + return ( (fWatchOSVersionMin + 0x00070000) >= requirediOSMin); + } + else { + return ( fIOSVersionMin >= requirediOSMin ); + } +} void Options::setWeakReferenceMismatchTreatment(const char* treatment) { @@ -1451,16 +1599,67 @@ uint32_t Options::parseProtection(const char* prot) } +// +// Parses number of form A[.B[.B[.D[.E]]]] into a uint64_t where the bits are a24.b10.c10.d10.e10 +// +uint64_t Options::parseVersionNumber64(const char* versionString) +{ + uint64_t a = 0; + uint64_t b = 0; + uint64_t c = 0; + uint64_t d = 0; + uint64_t e = 0; + char* end; + a = strtoul(versionString, &end, 10); + if ( *end == '.' ) { + b = strtoul(&end[1], &end, 10); + if ( *end == '.' ) { + c = strtoul(&end[1], &end, 10); + if ( *end == '.' ) { + d = strtoul(&end[1], &end, 10); + if ( *end == '.' ) { + e = strtoul(&end[1], &end, 10); + } + } + } + } + if ( (*end != '\0') || (a > 0xFFFFFF) || (b > 0x3FF) || (c > 0x3FF) || (d > 0x3FF) || (e > 0x3FF) ) + throwf("malformed 64-bit a.b.c.d.e version number: %s", versionString); + + return (a << 40) | ( b << 30 ) | ( c << 20 ) | ( d << 10 ) | e; +} + + +uint32_t Options::currentVersion32() const +{ + // warn if it does not fit into 32 bit vers number + uint32_t a = (fDylibCurrentVersion >> 40) & 0xFFFF; + uint32_t b = (fDylibCurrentVersion >> 30) & 0xFF; + uint32_t c = (fDylibCurrentVersion >> 20) & 0xFF; + uint64_t rep32 = ((uint64_t)a << 40) | ((uint64_t)b << 30) | ((uint64_t)c << 20); + if ( rep32 != fDylibCurrentVersion ) { + warning("truncating -current_version to fit in 32-bit space used by old mach-o format"); + a = (fDylibCurrentVersion >> 40) & 0xFFFFFF; + if ( a > 0xFFFF ) + a = 0xFFFF; + b = (fDylibCurrentVersion >> 30) & 0x3FF; + if ( b > 0xFF ) + b = 0xFF; + c = (fDylibCurrentVersion >> 20) & 0x3FF; + if ( c > 0xFF ) + c = 0xFF; + } + return (a << 16) | ( b << 8 ) | c; +} // // Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz // -// -uint32_t Options::parseVersionNumber(const char* versionString) +uint32_t Options::parseVersionNumber32(const char* versionString) { - unsigned long x = 0; - unsigned long y = 0; - unsigned long z = 0; + uint32_t x = 0; + uint32_t y = 0; + uint32_t z = 0; char* end; x = strtoul(versionString, &end, 10); if ( *end == '.' ) { @@ -1470,7 +1669,7 @@ uint32_t Options::parseVersionNumber(const char* versionString) } } if ( (*end != '\0') || (x > 0xffff) || (y > 0xff) || (z > 0xff) ) - throwf("malformed version number: %s", versionString); + throwf("malformed 32-bit x.y.z version number: %s", versionString); return (x << 16) | ( y << 8 ) | z; } @@ -1559,6 +1758,19 @@ void Options::parseOrderFile(const char* path, bool cstring) // order files override auto-ordering fAutoOrderInitializers = false; + // ld64 should prefer OrderFiles from the SDK over the ones in / + for (const char* sdkPath : fSDKPaths) { + char fullPath[PATH_MAX]; + strlcpy(fullPath, sdkPath, PATH_MAX); + strlcat(fullPath, "/", PATH_MAX); + strlcat(fullPath, path, PATH_MAX); + struct stat statBuffer; + if ( stat(fullPath, &statBuffer) == 0 ) { + path = strdup(fullPath); + break; + } + } + // read in whole file int fd = ::open(path, O_RDONLY, 0); if ( fd == -1 ) @@ -1572,6 +1784,8 @@ void Options::parseOrderFile(const char* path, bool cstring) throwf("can't read order file: %s", path); ::close(fd); p[stat_buf.st_size] = '\n'; + if ( this->dumpDependencyInfo() ) + this->dumpDependency(Options::depMisc, path); // parse into vector of pairs char * const end = &p[stat_buf.st_size+1]; @@ -1598,18 +1812,12 @@ void Options::parseOrderFile(const char* path, bool cstring) *last = '\0'; --last; } + // if there is an architecture prefix, only use this symbol it if matches current arch if ( strncmp(symbolStart, "ppc:", 4) == 0 ) { - if ( fArchitecture == CPU_TYPE_POWERPC ) - symbolStart = &symbolStart[4]; - else - symbolStart = NULL; + symbolStart = NULL; } - // if there is an architecture prefix, only use this symbol it if matches current arch else if ( strncmp(symbolStart, "ppc64:", 6) == 0 ) { - if ( fArchitecture == CPU_TYPE_POWERPC64 ) - symbolStart = &symbolStart[6]; - else - symbolStart = NULL; + symbolStart = NULL; } else if ( strncmp(symbolStart, "i386:", 5) == 0 ) { if ( fArchitecture == CPU_TYPE_I386 ) @@ -1629,6 +1837,12 @@ void Options::parseOrderFile(const char* path, bool cstring) else symbolStart = NULL; } + else if ( strncmp(symbolStart, "arm64:", 6) == 0 ) { + if ( fArchitecture == CPU_TYPE_ARM64 ) + symbolStart = &symbolStart[6]; + else + symbolStart = NULL; + } if ( symbolStart != NULL ) { char* objFileName = NULL; char* colon = strstr(symbolStart, ".o:"); @@ -1637,6 +1851,14 @@ void Options::parseOrderFile(const char* path, bool cstring) objFileName = symbolStart; symbolStart = &colon[3]; } + else { + colon = strstr(symbolStart, ".o):"); + if ( colon != NULL ) { + colon[3] = '\0'; + objFileName = symbolStart; + symbolStart = &colon[4]; + } + } // trim leading spaces while ( isspace(*symbolStart) ) ++symbolStart; @@ -1703,10 +1925,84 @@ void Options::addSection(const char* segment, const char* section, const char* p ::close(fd); // record section to create - ExtraSection info = { segment, section, path, (uint8_t*)p, stat_buf.st_size }; + ExtraSection info = { segment, section, path, (uint8_t*)p, (uint64_t)stat_buf.st_size }; fExtraSections.push_back(info); } +void Options::addSectionRename(const char* srcSegment, const char* srcSection, const char* dstSegment, const char* dstSection) +{ + if ( strlen(srcSegment) > 16 ) + throw "-rename_section segment name max 16 chars"; + if ( strlen(srcSection) > 16 ) + throw "-rename_section section name max 16 chars"; + if ( strlen(dstSegment) > 16 ) + throw "-rename_section segment name max 16 chars"; + if ( strlen(dstSection) > 16 ) + throw "-rename_section section name max 16 chars"; + + SectionRename info; + info.fromSegment = srcSegment; + info.fromSection = srcSection; + info.toSegment = dstSegment; + info.toSection = dstSection; + + fSectionRenames.push_back(info); +} + + +void Options::addSegmentRename(const char* srcSegment, const char* dstSegment) +{ + if ( strlen(srcSegment) > 16 ) + throw "-rename_segment segment name max 16 chars"; + if ( strlen(dstSegment) > 16 ) + throw "-rename_segment segment name max 16 chars"; + + SegmentRename info; + info.fromSegment = srcSegment; + info.toSegment = dstSegment; + + fSegmentRenames.push_back(info); +} + + + +void Options::addSymbolMove(const char* dstSegment, const char* symbolList, + std::vector& list, const char* optionName) +{ + if ( strlen(dstSegment) > 16 ) + throwf("%s segment name max 16 chars", optionName); + + SymbolsMove tmp; + list.push_back(tmp); + SymbolsMove& info = list.back(); + info.toSegment = dstSegment; + loadExportFile(symbolList, optionName, info.symbols); +} + +bool Options::moveRwSymbol(const char* symName, const char* filePath, const char*& seg, bool& wildCardMatch) const +{ + for (std::vector::const_iterator it=fSymbolsMovesData.begin(); it != fSymbolsMovesData.end(); ++it) { + const SymbolsMove& info = *it; + if ( info.symbols.containsWithPrefix(symName, filePath, wildCardMatch)) { + seg = info.toSegment; + return true; + } + } + return false; +} + +bool Options::moveRoSymbol(const char* symName, const char* filePath, const char*& seg, bool& wildCardMatch) const +{ + for (std::vector::const_iterator it=fSymbolsMovesCode.begin(); it != fSymbolsMovesCode.end(); ++it) { + const SymbolsMove& info = *it; + if ( info.symbols.containsWithPrefix(symName, filePath, wildCardMatch)) { + seg = info.toSegment; + return true; + } + } + return false; +} + void Options::addSectionAlignment(const char* segment, const char* section, const char* alignmentStr) { if ( strlen(segment) > 16 ) @@ -1758,9 +2054,257 @@ void Options::warnObsolete(const char* arg) } +void Options::cannotBeUsedWithBitcode(const char* arg) +{ + if ( fBundleBitcode ) + throwf("%s and -bitcode_bundle (Xcode setting ENABLE_BITCODE=YES) cannot be used together", arg); +} +std::string Options::getVersionString32(uint32_t ver) const +{ + if (ver == 0 || ver >= 0x10000000) + return "0.0.0"; + + unsigned microVersion = ver & 0xFF; + unsigned minorVersion = (ver >> 8) & 0xFF; + unsigned majorVersion = (ver >> 16) & 0xFF; + std::stringstream versionString; + versionString << majorVersion << "." << minorVersion << "." << microVersion; + return versionString.str(); +} -// +std::string Options::getVersionString64(uint64_t ver) const +{ + uint64_t a = (ver >> 40) & 0xFFFFFF; + uint64_t b = (ver >> 30) & 0x3FF; + uint64_t c = (ver >> 20) & 0x3FF; + uint64_t d = (ver >> 10) & 0x3FF; + uint64_t e = ver & 0x3FF; + std::stringstream versionString; + versionString << a << "." << b << "." << c << "." << d << "." << e; + return versionString.str(); +} + +// Convert X.Y[.Z] to 32-bit value xxxxyyzz +bool Options::parsePackedVersion32(const std::string& versionStr, uint32_t &result) +{ + result = 0; + + if ( versionStr.empty() ) + return false; + + size_t pos = versionStr.find('.'); + if ( pos == std::string::npos ) + return false; + + std::string majorStr = versionStr.substr(0, pos); + std::string rest = versionStr.substr(pos+1); + + try { + size_t majorEnd; + int majorValue = std::stoi(majorStr, &majorEnd); + if ( majorEnd != majorStr.size() ) + return false; + if ( majorValue < 0 ) + return false; + if ( majorValue > 65535 ) + return false; + + std::string minorStr; + std::string microStr; + pos = rest.find('.'); + if ( pos == std::string::npos ) { + minorStr = rest; + } + else { + minorStr = rest.substr(0, pos); + microStr = rest.substr(pos+1); + } + + size_t minorEnd; + int minorValue = std::stoi(minorStr, &minorEnd); + if ( minorEnd != minorStr.size() ) + return false; + if ( minorValue < 0 ) + return false; + if ( minorValue > 255 ) + return false; + + int microValue = 0; + if ( !microStr.empty() ) { + size_t microEnd; + microValue = std::stoi(microStr, µEnd); + if ( microEnd != microStr.size() ) + return false; + if ( microValue < 0 ) + return false; + if ( microValue > 255 ) + return false; + } + + result = (majorValue << 16) | (minorValue << 8) | microValue; + + return true; + } + catch (...) { + // std::stoi() throws exception on malformed input + return false; + } +} + +std::string Options::getSDKVersionStr() const +{ + return getVersionString32(fSDKVersion); +} + +std::string Options::getPlatformStr() const +{ + switch (fPlatform) { + case Options::kPlatformOSX: + return "MacOSX"; + case Options::kPlatformiOS: + if (targetIOSSimulator()) + return "iPhoneSimulator"; + else + return "iPhoneOS"; + case Options::kPlatformWatchOS: + if (targetIOSSimulator()) + return "watchOS Simulator"; + else + return "watchOS"; +#if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + if (targetIOSSimulator()) + return "AppleTVSimulator"; + else + return "AppleTVOS"; + break; +#endif + case Options::kPlatformUnknown: + return "Unknown"; + } +} + +std::vector Options::writeBitcodeLinkOptions() const +{ + std::vector linkCommand; + switch ( fOutputKind ) { + case Options::kDynamicLibrary: + linkCommand.push_back("-dylib"); + linkCommand.push_back("-compatibility_version"); + if ( fDylibCompatVersion != 0 ) { + linkCommand.push_back(getVersionString32(fDylibCompatVersion)); + } else { + linkCommand.push_back(getVersionString32(currentVersion32())); + } + if ( fDylibCurrentVersion != 0 ) { + linkCommand.push_back("-current_version"); + linkCommand.push_back(getVersionString64(fDylibCurrentVersion)); + } + linkCommand.push_back("-install_name"); + linkCommand.push_back(installPath()); + break; + case Options::kDynamicExecutable: + linkCommand.push_back("-execute"); + break; + case Options::kObjectFile: + linkCommand.push_back("-r"); + break; + default: + throwf("could not write bitcode options file output kind\n"); + } + + if (!fImplicitlyLinkPublicDylibs) + linkCommand.push_back("-no_implicit_dylibs"); + + // Add deployment target. + // Platform is allowed to be unknown for "ld -r". + switch (fPlatform) { + case Options::kPlatformOSX: + linkCommand.push_back("-macosx_version_min"); + linkCommand.push_back(getVersionString32((unsigned)fMacVersionMin)); + break; + case Options::kPlatformiOS: + if (targetIOSSimulator()) + linkCommand.push_back("-ios_simulator_version_min"); + else + linkCommand.push_back("-ios_version_min"); + linkCommand.push_back(getVersionString32((unsigned)fIOSVersionMin)); + break; + case Options::kPlatformWatchOS: + if (targetIOSSimulator()) + linkCommand.push_back("-watchos_simulator_version_min"); + else + linkCommand.push_back("-watchos_version_min"); + linkCommand.push_back(getVersionString32((unsigned)fIOSVersionMin)); + break; +#if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + if (targetIOSSimulator()) + linkCommand.push_back("-tvos_simulator_version_min"); + else + linkCommand.push_back("-tvos_version_min"); + linkCommand.push_back(getVersionString32((unsigned)fIOSVersionMin)); + break; +#endif + case Options::kPlatformUnknown: + if ( fOutputKind != Options::kObjectFile ) { + throwf("platform is unknown for final bitcode bundle," + "deployment target and min version is required for -bitcode_bundle"); + } + break; + } + + + // entry name + if ( fEntryName ) { + linkCommand.push_back("-e"); + linkCommand.push_back(fEntryName); + } + + // Write rpaths + if (!fRPaths.empty()) { + for (std::vector::const_iterator it=fRPaths.begin(); it != fRPaths.end(); ++it) { + linkCommand.push_back("-rpath"); + linkCommand.push_back(*it); + } + } + + // Other bitcode compatiable options + if ( fObjCABIVersion1Override ) { + linkCommand.push_back("-objc_abi_version"); + linkCommand.push_back("1"); + } else if ( fObjCABIVersion2Override ) { + linkCommand.push_back("-objc_abi_version"); + linkCommand.push_back("2"); + } + if ( fExecutablePath ) { + linkCommand.push_back("-executable_path"); + linkCommand.push_back(fExecutablePath); + } + if ( fDeadStrip ) + linkCommand.push_back("-dead_strip"); + if ( fExportDynamic ) + linkCommand.push_back("-export_dynamic"); + if ( fMarkAppExtensionSafe && fCheckAppExtensionSafe ) + linkCommand.push_back("-application_extension"); + + if ( fSourceVersionLoadCommandForceOn ) + linkCommand.push_back("-add_source_version"); + if ( fSourceVersion != 0 ) { + linkCommand.push_back("-source_version"); + linkCommand.push_back(getVersionString64(fSourceVersion)); + } + + // linker flag added by swift driver + // rdar://problem/20108072 + if ( !fObjcCategoryMerging ) + linkCommand.push_back("-no_objc_category_merging"); + + return linkCommand; +} + +// // Process all command line arguments. // // The only error checking done here is that each option is valid and if it has arguments @@ -1773,6 +2317,9 @@ void Options::warnObsolete(const char* arg) // void Options::parse(int argc, const char* argv[]) { + // Store the original args in the link snapshot. + fLinkSnapshot.recordRawArgs(argc, argv); + // pass one builds search list from -L and -F options this->buildSearchPaths(argc, argv); @@ -1784,12 +2331,17 @@ void Options::parse(int argc, const char* argv[]) const char* arg = argv[i]; if ( arg[0] == '-' ) { + // by default, copy one arg to the snapshot link command, and do no file copying + int snapshotArgIndex = i; + int snapshotArgCount = -1; // -1 means compute count based on change in index + int snapshotFileArgIndex = -1; // -1 means no data file parameter to arg // Since we don't care about the files passed, just the option names, we do this here. if (fPrintOptions) fprintf (stderr, "[Logging ld64 options]\t%s\n", arg); if ( (arg[1] == 'L') || (arg[1] == 'F') ) { + snapshotArgCount = 0; // stripped out of link snapshot if (arg[2] == '\0') ++i; // previously handled by buildSearchPaths() @@ -1812,15 +2364,18 @@ void Options::parse(int argc, const char* argv[]) if ( (fOutputKind != kObjectFile) && (fOutputKind != kKextBundle) ) { fOutputKind = kStaticExecutable; } + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-dylib") == 0 ) { fOutputKind = kDynamicLibrary; } else if ( strcmp(arg, "-bundle") == 0 ) { fOutputKind = kDynamicBundle; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-dylinker") == 0 ) { fOutputKind = kDyld; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-execute") == 0 ) { if ( fOutputKind != kStaticExecutable ) @@ -1828,47 +2383,103 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-preload") == 0 ) { fOutputKind = kPreload; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-r") == 0 ) { fOutputKind = kObjectFile; } else if ( strcmp(arg, "-kext") == 0 ) { fOutputKind = kKextBundle; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-o") == 0 ) { + snapshotArgCount = 0; fOutputFile = argv[++i]; + fLinkSnapshot.setSnapshotName(fOutputFile); } else if ( strncmp(arg, "-lazy-l", 7) == 0 ) { + snapshotArgCount = 0; FileInfo info = findLibrary(&arg[7], true); info.options.fLazyLoad = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); fUsingLazyDylibLinking = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-lto_library") == 0 ) { + snapshotFileArgIndex = 1; + fOverridePathlibLTO = argv[++i]; + if ( fOverridePathlibLTO == NULL ) + throw "missing argument to -lto_library"; + } + else if ( strcmp(arg, "-cache_path_lto") == 0 ) { + fLtoCachePath = argv[++i]; + if ( fLtoCachePath == NULL ) + throw "missing argument to -cache_path_lto"; + } + else if ( strcmp(arg, "-prune_interval_lto") == 0 ) { + const char* value = argv[++i]; + if ( value == NULL ) + throw "missing argument to -prune_interval_lto"; + char* endptr; + fLtoPruneInterval = strtoul(value, &endptr, 10); + if ( *endptr != '\0') + throw "invalid argument for -prune_interval_lto"; + } + else if ( strcmp(arg, "-prune_after_lto") == 0 ) { + const char* value = argv[++i]; + if ( value == NULL ) + throw "missing argument to -prune_after_lto"; + char* endptr; + fLtoPruneAfter = strtoul(value, &endptr, 10); + if ( *endptr != '\0') + throw "invalid argument for -prune_after_lto"; + } + else if ( strcmp(arg, "-max_relative_cache_size_lto") == 0 ) { + const char* value = argv[++i]; + if ( value == NULL ) + throw "missing argument to -max_relative_cache_size_lto"; + char* endptr; + fLtoMaxCacheSize = strtoul(value, &endptr, 10); + if ( *endptr != '\0') + throw "invalid argument for -max_relative_cache_size_lto"; + if (fLtoMaxCacheSize > 100) + throw "Expect a value between 0 and 100 for -max_relative_cache_size_lto"; } else if ( (arg[1] == 'l') && (strncmp(arg,"-lazy_",6) !=0) ) { - addLibrary(findLibrary(&arg[2])); + snapshotArgCount = 0; + FileInfo info = findLibrary(&arg[2]); + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + addLibrary(info); } // This causes a dylib to be weakly bound at // link time. This corresponds to weak_import. else if ( strncmp(arg, "-weak-l", 7) == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findLibrary(&arg[7]); info.options.fWeakImport = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } // Avoid lazy binding. else if ( strcmp(arg, "-bind_at_load") == 0 ) { fBindAtLoad = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-twolevel_namespace") == 0 ) { fNameSpace = kTwoLevelNameSpace; } else if ( strcmp(arg, "-flat_namespace") == 0 ) { fNameSpace = kFlatNameSpace; + cannotBeUsedWithBitcode(arg); } // Also sets a bit to ensure dyld causes everything // in the namespace to be flat. // ??? Deprecate else if ( strcmp(arg, "-force_flat_namespace") == 0 ) { fNameSpace = kForceFlatNameSpace; + cannotBeUsedWithBitcode(arg); } // Similar to --whole-archive. else if ( strcmp(arg, "-all_load") == 0 ) { @@ -1885,6 +2496,7 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-force_load") == 0 ) { FileInfo info = findFile(argv[++i]); info.options.fForceLoad = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } // Library versioning. @@ -1893,32 +2505,37 @@ void Options::parse(int argc, const char* argv[]) const char* vers = argv[++i]; if ( vers == NULL ) throw "-dylib_compatibility_version missing "; - fDylibCompatVersion = parseVersionNumber(vers); + fDylibCompatVersion = parseVersionNumber32(vers); } else if ( (strcmp(arg, "-dylib_current_version") == 0) || (strcmp(arg, "-current_version") == 0)) { const char* vers = argv[++i]; if ( vers == NULL ) throw "-dylib_current_version missing "; - fDylibCurrentVersion = parseVersionNumber(vers); + fDylibCurrentVersion = parseVersionNumber64(vers); } else if ( strcmp(arg, "-sectorder") == 0 ) { if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) throw "-sectorder missing
"; + snapshotFileArgIndex = 3; parseSectionOrderFile(argv[i+1], argv[i+2], argv[i+3]); i += 3; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-order_file") == 0 ) { + snapshotFileArgIndex = 1; parseOrderFile(argv[++i], false); } else if ( strcmp(arg, "-order_file_statistics") == 0 ) { fPrintOrderFileStatistics = true; + cannotBeUsedWithBitcode(arg); } // ??? Deprecate segcreate. // -sectcreate puts whole files into a section in the output. else if ( (strcmp(arg, "-sectcreate") == 0) || (strcmp(arg, "-segcreate") == 0) ) { if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) throw "-sectcreate missing
"; + snapshotFileArgIndex = 3; addSection(argv[i+1], argv[i+2], argv[i+3]); i += 3; } @@ -1927,7 +2544,7 @@ void Options::parse(int argc, const char* argv[]) || (strcmp(arg, "-dylinker_install_name") == 0) || (strcmp(arg, "-install_name") == 0)) { fDylibInstallName = argv[++i]; - if ( fDylibInstallName == NULL ) + if ( fDylibInstallName == NULL ) throw "-install_name missing "; } // Sets the base address of the output. @@ -1941,19 +2558,23 @@ void Options::parse(int argc, const char* argv[]) warning("-seg1addr not %lld byte aligned, rounding up", fSegmentAlignment); fBaseAddress = temp; } + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-e") == 0 ) { fEntryName = argv[++i]; } // Same as -@ from the FSF linker. else if ( strcmp(arg, "-filelist") == 0 ) { + snapshotArgCount = 0; const char* path = argv[++i]; if ( (path == NULL) || (path[0] == '-') ) throw "-filelist missing "; - loadFileList(path); + ld::File::Ordinal baseOrdinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + loadFileList(path, baseOrdinal); } else if ( strcmp(arg, "-keep_private_externs") == 0 ) { - fKeepPrivateExterns = true; + cannotBeUsedWithBitcode(arg); + fKeepPrivateExterns = true; } else if ( strcmp(arg, "-final_output") == 0 ) { fFinalName = argv[++i]; @@ -1970,22 +2591,27 @@ void Options::parse(int argc, const char* argv[]) // do nothing, -interposable_list overrides -interposable" break; } + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-interposable_list") == 0 ) { + snapshotFileArgIndex = 1; fInterposeMode = kInterposeSome; loadExportFile(argv[++i], "-interposable_list", fInterposeList); + cannotBeUsedWithBitcode(arg); } // Default for -interposable/-multi_module/-single_module. else if ( strcmp(arg, "-single_module") == 0 ) { fInterposeMode = kInterposeNone; } else if ( strcmp(arg, "-exported_symbols_list") == 0 ) { + snapshotFileArgIndex = 1; if ( fExportMode == kDontExportSome ) throw "can't use -exported_symbols_list and -unexported_symbols_list"; fExportMode = kExportSome; loadExportFile(argv[++i], "-exported_symbols_list", fExportSymbols); } else if ( strcmp(arg, "-unexported_symbols_list") == 0 ) { + snapshotFileArgIndex = 1; if ( fExportMode == kExportSome ) throw "can't use -unexported_symbols_list and -exported_symbols_list"; fExportMode = kDontExportSome; @@ -2004,49 +2630,73 @@ void Options::parse(int argc, const char* argv[]) fDontExportSymbols.insert(argv[++i]); } else if ( strcmp(arg, "-non_global_symbols_no_strip_list") == 0 ) { + snapshotFileArgIndex = 1; if ( fLocalSymbolHandling == kLocalSymbolsSelectiveExclude ) throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list"; fLocalSymbolHandling = kLocalSymbolsSelectiveInclude; loadExportFile(argv[++i], "-non_global_symbols_no_strip_list", fLocalSymbolsIncluded); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-non_global_symbols_strip_list") == 0 ) { + snapshotFileArgIndex = 1; if ( fLocalSymbolHandling == kLocalSymbolsSelectiveInclude ) throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list"; fLocalSymbolHandling = kLocalSymbolsSelectiveExclude; loadExportFile(argv[++i], "-non_global_symbols_strip_list", fLocalSymbolsExcluded); + cannotBeUsedWithBitcode(arg); } // ??? Deprecate else if ( strcmp(arg, "-no_arch_warnings") == 0 ) { fIgnoreOtherArchFiles = true; } else if ( strcmp(arg, "-force_cpusubtype_ALL") == 0 ) { - fForceSubtypeAll = true; + fForceSubtypeAll = true; + fAllowCpuSubtypeMismatches = true; + cannotBeUsedWithBitcode(arg); } // Similar to -weak-l but uses the absolute path name to the library. else if ( strcmp(arg, "-weak_library") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findFile(argv[++i]); info.options.fWeakImport = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-lazy_library") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findFile(argv[++i]); info.options.fLazyLoad = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); fUsingLazyDylibLinking = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-framework") == 0 ) { - addLibrary(findFramework(argv[++i])); + snapshotArgCount = 0; + FileInfo info = findFramework(argv[++i]); + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); + addLibrary(info); } else if ( strcmp(arg, "-weak_framework") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findFramework(argv[++i]); info.options.fWeakImport = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } else if ( strcmp(arg, "-lazy_framework") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findFramework(argv[++i]); info.options.fLazyLoad = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); fUsingLazyDylibLinking = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-search_paths_first") == 0 ) { // previously handled by buildSearchPaths() @@ -2055,7 +2705,8 @@ void Options::parse(int argc, const char* argv[]) // previously handled by buildSearchPaths() } else if ( strcmp(arg, "-undefined") == 0 ) { - setUndefinedTreatment(argv[++i]); + setUndefinedTreatment(argv[++i]); + cannotBeUsedWithBitcode(arg); } // Debugging output flag. else if ( strcmp(arg, "-arch_multiple") == 0 ) { @@ -2073,10 +2724,12 @@ void Options::parse(int argc, const char* argv[]) case kWarning: fWarnTextRelocs = true; fAllowTextRelocs = true; + cannotBeUsedWithBitcode(arg); break; case kSuppress: fWarnTextRelocs = false; fAllowTextRelocs = true; + cannotBeUsedWithBitcode(arg); break; case kError: fWarnTextRelocs = false; @@ -2091,7 +2744,7 @@ void Options::parse(int argc, const char* argv[]) // Warn, error or make strong a mismatch between weak // and non-weak references. else if ( strcmp(arg, "-weak_reference_mismatches") == 0 ) { - setWeakReferenceMismatchTreatment(argv[++i]); + setWeakReferenceMismatchTreatment(argv[++i]); } // For a deployment target of 10.3 and earlier ld64 will // prebind an executable with 0s in all addresses that @@ -2099,6 +2752,7 @@ void Options::parse(int argc, const char* argv[]) // later. Prebinding is less useful on 10.4 and greater. else if ( strcmp(arg, "-prebind") == 0 ) { fPrebind = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-noprebind") == 0 ) { warnObsolete(arg); @@ -2119,7 +2773,10 @@ void Options::parse(int argc, const char* argv[]) // This should probably be deprecated when we respect -L and -F // when searching for libraries. else if ( strcmp(arg, "-dylib_file") == 0 ) { + // ignore for snapshot because a stub dylib will be created in the snapshot + snapshotArgCount = 0; addDylibOverride(argv[++i]); + cannotBeUsedWithBitcode(arg); } // What to expand @executable_path to if found in dependent dylibs else if ( strcmp(arg, "-executable_path") == 0 ) { @@ -2150,6 +2807,7 @@ void Options::parse(int argc, const char* argv[]) warning("alignment for -segalign %s is not a power of two, using 0x%X", size, p2aligned); fSegmentAlignment = p2aligned; } + cannotBeUsedWithBitcode(arg); } // Puts a specified segment at a particular address that must // be a multiple of the segment alignment. @@ -2159,26 +2817,31 @@ void Options::parse(int argc, const char* argv[]) if ( (seg.name == NULL) || (argv[i+1] == NULL) ) throw "-segaddr missing segName Adddress"; seg.address = parseAddress(argv[++i]); - uint64_t temp = seg.address & (-4096); // page align - if ( (seg.address != temp) ) - warning("-segaddr %s not page aligned, rounding down", seg.name); + uint64_t temp = ((seg.address+fSegmentAlignment-1) & (-fSegmentAlignment)); + if ( seg.address != temp ) + warning("-segaddr %s not %lld byte aligned", seg.name, fSegmentAlignment); fCustomSegmentAddresses.push_back(seg); + cannotBeUsedWithBitcode(arg); } // ??? Deprecate when we deprecate split-seg. else if ( strcmp(arg, "-segs_read_only_addr") == 0 ) { fBaseAddress = parseAddress(argv[++i]); + cannotBeUsedWithBitcode(arg); } // ??? Deprecate when we deprecate split-seg. else if ( strcmp(arg, "-segs_read_write_addr") == 0 ) { fBaseWritableAddress = parseAddress(argv[++i]); fSplitSegs = true; + cannotBeUsedWithBitcode(arg); } // ??? Deprecate when we get rid of basing at build time. else if ( strcmp(arg, "-seg_addr_table") == 0 ) { + snapshotFileArgIndex = 1; const char* name = argv[++i]; if ( name == NULL ) throw "-seg_addr_table missing argument"; fSegAddrTablePath = name; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-seg_addr_table_filename") == 0 ) { warnObsolete(arg); @@ -2191,7 +2854,11 @@ void Options::parse(int argc, const char* argv[]) throw "-segprot missing segName max-prot init-prot"; seg.max = parseProtection(argv[++i]); seg.init = parseProtection(argv[++i]); - fCustomSegmentProtections.push_back(seg); + if ( strcmp(seg.name, "__LINKEDIT") == 0 ) + warning("-segprot cannot be used to modify __LINKEDIT protections"); + else + fCustomSegmentProtections.push_back(seg); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-pagezero_size") == 0 ) { const char* size = argv[++i]; @@ -2202,33 +2869,35 @@ void Options::parse(int argc, const char* argv[]) if ( (fZeroPageSize != temp) ) warning("-pagezero_size not page aligned, rounding down"); fZeroPageSize = temp; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-stack_addr") == 0 ) { const char* address = argv[++i]; if ( address == NULL ) throw "-stack_addr missing
"; fStackAddr = parseAddress(address); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-stack_size") == 0 ) { const char* size = argv[++i]; if ( size == NULL ) throw "-stack_size missing
"; fStackSize = parseAddress(size); - uint64_t temp = fStackSize & (-4096); // page align - if ( (fStackSize != temp) ) - warning("-stack_size not page aligned, rounding down"); } else if ( strcmp(arg, "-allow_stack_execute") == 0 ) { fExecutableStack = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-allow_heap_execute") == 0 ) { fDisableNonExecutableHeap = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-sectalign") == 0 ) { if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) throw "-sectalign missing
"; addSectionAlignment(argv[i+1], argv[i+2], argv[i+3]); i += 3; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-sectorder_detail") == 0 ) { warnObsolete(arg); @@ -2238,10 +2907,12 @@ void Options::parse(int argc, const char* argv[]) i += 2; } else if ( strcmp(arg, "-bundle_loader") == 0 ) { + snapshotFileArgIndex = 1; fBundleLoader = argv[++i]; if ( (fBundleLoader == NULL) || (fBundleLoader[0] == '-') ) throw "-bundle_loader missing "; FileInfo info = findFile(fBundleLoader); + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); info.options.fBundleLoader = true; fInputFiles.push_back(info); } @@ -2253,11 +2924,73 @@ void Options::parse(int argc, const char* argv[]) } // Use this flag to set default behavior for deployement targets. else if ( strcmp(arg, "-macosx_version_min") == 0 ) { - setMacOSXVersionMin(argv[++i]); - } - else if ( (strcmp(arg, "-iphoneos_version_min") == 0) || (strcmp(arg, "-ios_version_min") == 0) ) { - setIPhoneVersionMin(argv[++i]); + const char* macVers = argv[++i]; + if ( macVers == NULL ) + throw "-macosx_version_min missing version argument"; + const char* envMacVers = getenv("MACOSX_DEPLOYMENT_TARGET"); + const char* enviPhoneVers = getenv("IPHONEOS_DEPLOYMENT_TARGET"); + if ( (envMacVers != NULL) && (enviPhoneVers != NULL) ) { + // when conflicting deployments set, break tie by looking at syslibroot + warning("both MACOSX_DEPLOYMENT_TARGET and IPHONEOS_DEPLOYMENT_TARGET are set"); + if ( !fSDKPaths.empty() ) { + const char* sysrootPath = fSDKPaths.back(); + const char* lastSlash = strrchr(sysrootPath, '/'); + if ( strstr(lastSlash, "Simulator") != NULL ) + setIOSVersionMin(enviPhoneVers); + else + setMacOSXVersionMin(macVers); + } + else { + setMacOSXVersionMin(macVers); + } + } + else { + setMacOSXVersionMin(macVers); + } } + else if ( (strcmp(arg, "-ios_version_min") == 0) || (strcmp(arg, "-iphoneos_version_min") == 0) ) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-ios_version_min missing version argument"; + setIOSVersionMin(vers); + } + else if ( strcmp(arg, "-ios_simulator_version_min") == 0 ) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-ios_simulator_version_min missing version argument"; + setIOSVersionMin(vers); + fTargetIOSSimulator = true; + } + else if ( strcmp(arg, "-watchos_version_min") == 0 ) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-watchos_version_min missing version argument"; + setWatchOSVersionMin(vers); + } + else if ( strcmp(arg, "-watchos_simulator_version_min") == 0 ) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-watchos_simulator_version_min missing version argument"; + setWatchOSVersionMin(vers); + fTargetIOSSimulator = true; + } + #if SUPPORT_APPLE_TV + else if ( strcmp(arg, "-tvos_version_min") == 0 ) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-tvos_version_min missing version argument"; + setIOSVersionMin(vers); + fPlatform = kPlatform_tvOS; + } + else if ( strcmp(arg, "-tvos_simulator_version_min") == 0 ) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-tvos_simulator_version_min missing version argument"; + setIOSVersionMin(vers); + fPlatform = kPlatform_tvOS; + fTargetIOSSimulator = true; + } + #endif else if ( strcmp(arg, "-multiply_defined") == 0 ) { //warnObsolete(arg); ++i; @@ -2298,12 +3031,14 @@ void Options::parse(int argc, const char* argv[]) if ( name == NULL ) throw "-u missing argument"; fInitialUndefines.push_back(name); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-U") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) throw "-U missing argument"; fAllowedUndefined.insert(name); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-s") == 0 ) { warnObsolete(arg); @@ -2342,6 +3077,9 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-w") == 0 ) { // previously handled by buildSearchPaths() } + else if ( strcmp(arg, "-fatal_warnings") == 0 ) { + // previously handled by buildSearchPaths() + } else if ( strcmp(arg, "-arch_errors_fatal") == 0 ) { fErrorOnOtherArchFiles = true; } @@ -2353,9 +3091,15 @@ void Options::parse(int argc, const char* argv[]) if ( size == NULL ) throw "-headerpad missing argument"; fMinimumHeaderPad = parseAddress(size); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-headerpad_max_install_names") == 0 ) { - fMaxMinimumHeaderPad = true; + // ignore -headerpad_max_install_names when compiling with bitcode + // rdar://problem/20748962 + if ( fBundleBitcode ) + warning("-headerpad_max_install_names is ignored when used with -bitcode_bundle (Xcode setting ENABLE_BITCODE=YES)"); + else + fMaxMinimumHeaderPad = true; } else if ( strcmp(arg, "-t") == 0 ) { fLogAllFiles = true; @@ -2372,6 +3116,7 @@ void Options::parse(int argc, const char* argv[]) if ( name == NULL ) throw "-umbrella missing argument"; fUmbrellaName = name; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-allowable_client") == 0 ) { const char* name = argv[++i]; @@ -2380,6 +3125,7 @@ void Options::parse(int argc, const char* argv[]) throw "-allowable_client missing argument"; fAllowableClients.push_back(name); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-client_name") == 0 ) { const char* name = argv[++i]; @@ -2388,30 +3134,35 @@ void Options::parse(int argc, const char* argv[]) throw "-client_name missing argument"; fClientName = name; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-sub_umbrella") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) throw "-sub_umbrella missing argument"; fSubUmbellas.push_back(name); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-sub_library") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) throw "-sub_library missing argument"; fSubLibraries.push_back(name); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-init") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) throw "-init missing argument"; fInitFunctionName = name; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-dot") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) throw "-dot missing argument"; fDotOutputFile = name; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-warn_commons") == 0 ) { fWarnCommons = true; @@ -2441,20 +3192,29 @@ void Options::parse(int argc, const char* argv[]) // previously handled by buildSearchPaths() } else if ( strcmp(arg, "-syslibroot") == 0 ) { + snapshotArgCount = 0; ++i; // previously handled by buildSearchPaths() } + else if ( strcmp(arg, "-bitcode_bundle") == 0 ) { + snapshotArgCount = 0; + // previously handled by buildSearchPaths() + } else if ( strcmp(arg, "-no_uuid") == 0 ) { fUUIDMode = kUUIDNone; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-random_uuid") == 0 ) { fUUIDMode = kUUIDRandom; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-dtrace") == 0 ) { + snapshotFileArgIndex = 1; const char* name = argv[++i]; if ( name == NULL ) throw "-dtrace missing argument"; fDtraceScriptName = name; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-root_safe") == 0 ) { fRootSafe = true; @@ -2471,25 +3231,64 @@ void Options::parse(int argc, const char* argv[]) if ( pair.alias == NULL ) throw "missing argument to -alias"; fAliases.push_back(pair); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-alias_list") == 0 ) { + snapshotFileArgIndex = 1; parseAliasFile(argv[++i]); - } - // put this last so that it does not interfer with other options starting with 'i' - else if ( strncmp(arg, "-i", 2) == 0 ) { - const char* colon = strchr(arg, ':'); - if ( colon == NULL ) - throwf("unknown option: %s", arg); - Options::AliasPair pair; - char* temp = new char[colon-arg]; - strlcpy(temp, &arg[2], colon-arg-1); - pair.realName = &colon[1]; - pair.alias = temp; - fAliases.push_back(pair); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-save-temps") == 0 ) { fSaveTempFiles = true; } + else if ( strcmp(arg, "-bitcode_hide_symbols") == 0 ) { + fHideSymbols = true; + } + else if ( strcmp(arg, "-bitcode_verify") == 0 ) { + fVerifyBitcode = true; + } + else if ( strcmp(arg, "-bitcode_symbol_map") == 0) { + fReverseMapPath = argv[++i]; + if ( fReverseMapPath == NULL ) + throw "missing argument to -bitcode_symbol_map"; + struct stat statbuf; + ::stat(fReverseMapPath, &statbuf); + if (S_ISDIR(statbuf.st_mode)) { + char tempPath[PATH_MAX]; + sprintf(tempPath, "%s/XXXXXX", fReverseMapPath); + int tempFile = ::mkstemp(tempPath); + if (tempFile == -1) + throwf("could not write file to symbol map directory: %s", fReverseMapPath); + ::close(tempFile); + fReverseMapTempPath = std::string(tempPath); + fReverseMapUUIDRename = true; + } else + fReverseMapTempPath = std::string(fReverseMapPath); + } + else if ( strcmp(argv[i], "-flto-codegen-only") == 0) { + fLTOCodegenOnly = true; + } + else if ( strcmp(argv[i], "-ignore_auto_link") == 0) { + fIgnoreAutoLink = true; + } + else if ( strcmp(argv[i], "-allow_dead_duplicates") == 0) { + fAllowDeadDups = true; + } + else if ( strcmp(argv[i], "-bitcode_process_mode") == 0 ) { + const char* bitcode_type = argv[++i]; + if ( bitcode_type == NULL ) + throw "missing argument to -bitcode_process_mode"; + else if ( strcmp(bitcode_type, "strip") == 0 ) + fBitcodeKind = kBitcodeStrip; + else if ( strcmp(bitcode_type, "marker") == 0 ) + fBitcodeKind = kBitcodeMarker; + else if ( strcmp(bitcode_type, "data") == 0 ) + fBitcodeKind = kBitcodeAsData; + else if ( strcmp(bitcode_type, "bitcode") == 0 ) + fBitcodeKind = kBitcodeProcess; + else + throw "unknown argument to -bitcode_process_mode {strip,marker,data,bitcode}"; + } else if ( strcmp(arg, "-rpath") == 0 ) { const char* path = argv[++i]; if ( path == NULL ) @@ -2513,39 +3312,65 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-no_pie") == 0 ) { fDisablePositionIndependentExecutable = true; + cannotBeUsedWithBitcode(arg); } else if ( strncmp(arg, "-reexport-l", 11) == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findLibrary(&arg[11], true); info.options.fReExport = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-reexport_library") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findFile(argv[++i]); info.options.fReExport = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-reexport_framework") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findFramework(argv[++i]); info.options.fReExport = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strncmp(arg, "-upward-l", 9) == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findLibrary(&arg[9], true); info.options.fUpward = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-upward_library") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findFile(argv[++i]); info.options.fUpward = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-upward_framework") == 0 ) { + // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) + snapshotArgCount = 0; FileInfo info = findFramework(argv[++i]); info.options.fUpward = true; + info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-dead_strip_dylibs") == 0 ) { fDeadStripDylibs = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_implicit_dylibs") == 0 ) { fImplicitlyLinkPublicDylibs = false; @@ -2554,22 +3379,38 @@ void Options::parse(int argc, const char* argv[]) // ignore } else if ( strcmp(arg, "-no_encryption") == 0 ) { - fEncryptable = false; + fEncryptableForceOff = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-encryptable") == 0 ) { + fEncryptableForceOn = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_compact_unwind") == 0 ) { fAddCompactUnwindEncoding = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-mllvm") == 0 ) { const char* opts = argv[++i]; if ( opts == NULL ) throw "missing argument to -mllvm"; fLLVMOptions.push_back(opts); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-mcpu") == 0 ) { + const char* cpu = argv[++i]; + if ( cpu == NULL ) + throw "missing argument to -mcpu"; + fLtoCpu = cpu; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_order_inits") == 0 ) { fAutoOrderInitializers = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_order_data") == 0 ) { fOrderData = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-seg_page_size") == 0 ) { SegmentSize seg; @@ -2581,28 +3422,38 @@ void Options::parse(int argc, const char* argv[]) if ( (seg.size != temp) ) warning("-seg_page_size %s not 4K aligned, rounding down", seg.name); fCustomSegmentSizes.push_back(seg); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-mark_dead_strippable_dylib") == 0 ) { fMarkDeadStrippableDylib = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-exported_symbols_order") == 0 ) { + snapshotFileArgIndex = 1; loadSymbolOrderFile(argv[++i], fExportSymbolsOrder); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_compact_linkedit") == 0 ) { - fMakeCompressedDyldInfo = false; - fMakeCompressedDyldInfoForceOff = true; + warnObsolete("-no_compact_linkedit"); } else if ( strcmp(arg, "-no_eh_labels") == 0 ) { fNoEHLabels = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-warn_compact_unwind") == 0 ) { fWarnCompactUnwind = true; } else if ( strcmp(arg, "-allow_sub_type_mismatches") == 0 ) { fAllowCpuSubtypeMismatches = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_zero_fill_sections") == 0 ) { fOptimizeZeroFill = false; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-merge_zero_fill_sections") == 0 ) { + fMergeZeroFill = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-objc_abi_version") == 0 ) { const char* version = argv[++i]; @@ -2624,6 +3475,7 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-objc_gc_compaction") == 0 ) { fObjcGcCompaction = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-objc_gc") == 0 ) { fObjCGc = true; @@ -2631,6 +3483,7 @@ void Options::parse(int argc, const char* argv[]) warning("-objc_gc overriding -objc_gc_only"); fObjCGcOnly = false; } + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-objc_gc_only") == 0 ) { fObjCGcOnly = true; @@ -2638,21 +3491,37 @@ void Options::parse(int argc, const char* argv[]) warning("-objc_gc_only overriding -objc_gc"); fObjCGc = false; } + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-demangle") == 0 ) { fDemangle = true; } else if ( strcmp(arg, "-version_load_command") == 0 ) { - fVersionLoadCommand = true; + fVersionLoadCommandForcedOn = true; + fVersionLoadCommandForcedOff = false; } else if ( strcmp(arg, "-no_version_load_command") == 0 ) { - fVersionLoadCommand = false; + fVersionLoadCommandForcedOff = true; + fVersionLoadCommandForcedOn = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-function_starts") == 0 ) { - fFunctionStartsLoadCommand = true; + fFunctionStartsForcedOn = true; + fFunctionStartsForcedOff = false; } else if ( strcmp(arg, "-no_function_starts") == 0 ) { - fFunctionStartsLoadCommand = false; + fFunctionStartsForcedOff = true; + fFunctionStartsForcedOn = false; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-no_data_in_code_info") == 0 ) { + fDataInCodeInfoLoadCommandForcedOff = true; + fDataInCodeInfoLoadCommandForcedOn = false; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-data_in_code_info") == 0 ) { + fDataInCodeInfoLoadCommandForcedOn = true; + fDataInCodeInfoLoadCommandForcedOff = false; } else if ( strcmp(arg, "-object_path_lto") == 0 ) { fTempLtoObjectPath = argv[++i]; @@ -2663,24 +3532,31 @@ void Options::parse(int argc, const char* argv[]) fObjcCategoryMerging = false; } else if ( strcmp(arg, "-force_symbols_weak_list") == 0 ) { + snapshotFileArgIndex = 1; loadExportFile(argv[++i], "-force_symbols_weak_list", fForceWeakSymbols); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-force_symbols_not_weak_list") == 0 ) { + snapshotFileArgIndex = 1; loadExportFile(argv[++i], "-force_symbols_not_weak_list", fForceNotWeakSymbols); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-force_symbol_weak") == 0 ) { const char* symbol = argv[++i]; if ( symbol == NULL ) throw "-force_symbol_weak missing "; fForceWeakSymbols.insert(symbol); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-force_symbol_not_weak") == 0 ) { const char* symbol = argv[++i]; if ( symbol == NULL ) throw "-force_symbol_not_weak missing "; fForceNotWeakSymbols.insert(symbol); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-reexported_symbols_list") == 0 ) { + snapshotFileArgIndex = 1; if ( fExportMode == kExportSome ) throw "can't use -exported_symbols_list and -reexported_symbols_list"; loadExportFile(argv[++i], "-reexported_symbols_list", fReExportSymbols); @@ -2692,13 +3568,307 @@ void Options::parse(int argc, const char* argv[]) if ( strchr(envarg, '=') == NULL ) throw "-dyld_env missing ENV=VALUE"; fDyldEnvironExtras.push_back(envarg); + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-page_align_data_atoms") == 0 ) { + fPageAlignDataAtoms = true; + cannotBeUsedWithBitcode(arg); + } + else if (strcmp(arg, "-debug_snapshot") == 0) { + fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG); + fSnapshotRequested = true; + cannotBeUsedWithBitcode(arg); + } + else if (strcmp(arg, "-snapshot_dir") == 0) { + const char* path = argv[++i]; + if ( path == NULL ) + throw "-snapshot_dir missing path"; + fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG); + fLinkSnapshot.setSnapshotPath(path); + fSnapshotRequested = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-new_main") == 0 ) { + fEntryPointLoadCommandForceOn = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-no_new_main") == 0 ) { + fEntryPointLoadCommandForceOff = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-source_version") == 0 ) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-source_version missing "; + fSourceVersion = parseVersionNumber64(vers); + } + else if ( strcmp(arg, "-add_source_version") == 0 ) { + fSourceVersionLoadCommandForceOn = true; + } + else if ( strcmp(arg, "-no_source_version") == 0 ) { + fSourceVersionLoadCommandForceOff = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-sdk_version") == 0 ) { + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-sdk_version missing "; + fSDKVersion = parseVersionNumber32(vers); + } + else if ( strcmp(arg, "-dependent_dr_info") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-no_dependent_dr_info") == 0 ) { + warnObsolete(arg); + } + else if ( strcmp(arg, "-kexts_use_stubs") == 0 ) { + fKextsUseStubs = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(argv[i], "-dependency_info") == 0 ) { + snapshotArgCount = 0; + ++i; + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-export_dynamic") == 0 ) { + fExportDynamic = true; + } + else if ( strcmp(arg, "-force_symbols_coalesce_list") == 0 ) { + snapshotFileArgIndex = 1; + loadExportFile(argv[++i], "-force_symbols_coalesce_list", fForceCoalesceSymbols); + } + else if ( strcmp(arg, "-add_linker_option") == 0 ) { + // ex: -add_linker_option '-framework Foundation' + const char* optString = argv[++i]; + if ( optString == NULL ) + throw "-add_linker_option missing