/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * * Copyright (c) 2005-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ld.hpp" #include "Options.h" #include "Architectures.hpp" #include "MachOFileAbstraction.hpp" #include "Mangling.h" #include "Snapshot.h" #include "macho_relocatable_file.h" #include "ResponseFiles.h" #define VAL(x) #x #define STRINGIFY(x) VAL(x) // upward dependency on lto::version() namespace lto { extern const char* version(); extern unsigned static_api_version(); extern unsigned runtime_api_version(); } // magic to place command line in crash reports const int crashreporterBufferSize = 2000; static char crashreporterBuffer[crashreporterBufferSize]; #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 ) { if ( sWarningsSideFile == NULL ) sWarningsSideFile = fopen(sWarningsSideFilePath, "a"); } va_start(list, format); fprintf(stderr, "ld: warning: "); vfprintf(stderr, format, list); fprintf(stderr, "\n"); if ( sWarningsSideFile != NULL ) { fprintf(sWarningsSideFile, "ld: warning: "); vfprintf(sWarningsSideFile, format, list); fprintf(sWarningsSideFile, "\n"); fflush(sWarningsSideFile); } va_end(list); } } __attribute__((noreturn)) void throwf(const char* format, ...) { va_list list; char* p; va_start(list, format); vasprintf(&p, format, list); va_end(list); const char* t = p; throw t; } bool Options::FileInfo::checkFileExists(const Options& options, const char *p) { if (isInlined) { modTime = 0; return true; } 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; } options.addDependency(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), fFallbackArchitecture(0), fFallbackSubArchitecture(0), fArchitectureName("unknown"), fOutputKind(kDynamicExecutable), fArchThumb2Support(Thumb2Support::none), fBindAtLoad(false), fKeepPrivateExterns(false), fIgnoreOtherArchFiles(false), fErrorOnOtherArchFiles(false), fForceSubtypeAll(false), fInterposeMode(kInterposeNone), fDeadStrip(false), fRemoveSwiftReflectionMetadataSections(false), fRemoveDwarfUnwindSections(false), fNameSpace(kTwoLevelNameSpace), fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName(NULL), fBaseAddress(0), fMaxAddress(0xFFFFFFFFFFFFFFFFULL), fBaseWritableAddress(0), fExportMode(kExportDefault), fLibrarySearchMode(kSearchDylibAndArchiveInEachDir), fUndefinedTreatment(kUndefinedError), fMessagesPrefixedWithArchitecture(true), fWeakReferenceMismatchTreatment(kWeakReferenceMismatchNonWeak), fClientName(NULL), fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL), fBundleLoader(NULL), fDtraceScriptName(NULL), fMapPath(NULL), fDyldInstallPath("/usr/lib/dyld"), fLtoCachePath(NULL), fLTOSoftloadRuntimeSymbols(false), fLTOSoftloadRuntimeSymbolsForceOn(false), fLTOSoftloadRuntimeSymbolsForceOff(false), fTempLtoObjectPath(NULL), fOverridePathlibLTO(NULL), fLtoCpu(NULL), fToolchainPath(NULL),fOrderFilePath(NULL), fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fSourceVersion(0), fSDKVersion(0), fImplicitPageZero(false), fExecutableStack(false), fNonExecutableHeap(false), fDisableNonExecutableHeap(false), fMinimumHeaderPad(32), fSegmentAlignment(LD_PAGE_SIZE), fForceAlignment(false), fCommonsMode(kCommonsIgnoreDylibs), fUUIDMode(kUUIDContent), fLocalSymbolHandling(kLocalSymbolsAll), fWarnCommons(false), fVerbose(false), fKeepRelocations(false), fWarnStabs(false), fTraceDylibSearching(false), fPause(false), fStatistics(false), fPrintOptions(false), fSharedRegionEligible(false), fSharedRegionEligibleForceOff(false), fPrintOrderFileStatistics(false), fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), fPIEOnCommandLine(false), fDisablePositionIndependentExecutable(false), fMaxMinimumHeaderPad(false), fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false), fKextsUseStubs(false), fEncryptable(true), fEncryptableForceOn(false), fEncryptableForceOff(false), fMarkDeadStrippableDylib(false), fMakeCompressedDyldInfo(true), fMakeCompressedDyldInfoForceOff(false), fMakeThreadedStartsSection(false), fNoEHLabels(false), fAllowCpuSubtypeMismatches(false), fEnforceDylibSubtypesMatch(false), fAllowCpuSubtypeMismatchesForceOn(false), fAllowCpuSubtypeMismatchesForceOff(false), fWarnOnSwiftABIVersionMismatches(false), fWarnOnClassROSigningMismatches(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), fSearchInSparseFrameworks(false), fImplicitlyLinkPublicDylibs(true), fAddCompactUnwindEncoding(true), fWarnCompactUnwind(false), fRemoveDwarfUnwindIfCompactExists(false), fAutoOrderInitializers(true), fOptimizeZeroFill(true), fMergeZeroFill(false), fLogAllFiles(false), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), fTraceEmitJSON(false), fOutputSlidable(false), fWarnWeakExports(false), fNoWeakExports(false), fDemangle(false), fTLVSupport(false), fVersionLoadCommand(false), fVersionLoadCommandForcedOn(false), fVersionLoadCommandForcedOff(false), fForceLegacyVersionLoadCommands(false), fFunctionStartsLoadCommand(false), fFunctionStartsForcedOn(false), fFunctionStartsForcedOff(false), fDataInCodeInfoLoadCommand(false), fDataInCodeInfoLoadCommandForcedOn(false), fDataInCodeInfoLoadCommandForcedOff(false), fCanReExportSymbols(false), fObjcCategoryMerging(true), fPageAlignDataAtoms(false), fNeedsThreadLoadCommand(false), fEntryPointLoadCommand(false), fSourceVersionLoadCommand(false), fSourceVersionLoadCommandForceOn(false), fSourceVersionLoadCommandForceOff(false), fExportDynamic(false), fAbsoluteSymbols(false), fAllowSimulatorToLinkWithMacOSX(false), fSimulatorSupportDylib(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), fConstSelectorRefs(false), fConstSelectorRefsForceOn(false), fConstSelectorRefsForceOff(false), fConstClassRefs(false), fUseTextExecSegment(false), fBundleBitcode(false), fHideSymbols(false), fVerifyBitcode(false), fReverseMapUUIDRename(false), fDeDupe(true), fVerboseDeDupe(false), fMakeInitializersIntoOffsets(false), fUseLinkedListBinding(false), fMakeChainedFixupsForceOn(false), fMakeChainedFixupsForceOff(false), fMakeChainedFixups(false), fMakeChainedFixupsSection(false), fMakeRebaseSection(false), fNoLazyBinding(false), fDebugVariant(false), fReverseMapPath(NULL), fLTOCodegenOnly(false), fIgnoreAutoLink(false), fAllowDeadDups(false), fAllowWeakImports(true), fInitializersTreatment(Options::kInvalid), fZeroModTimeInDebugMap(false), fBitcodeKind(kBitcodeProcess), fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), fPlatfromVersionCmdFound(false), fInternalSDK(false), fWarnUnusedDylibs(false), fWarnUnusedDylibsForceOn(false), fWarnUnusedDylibsForceOff(false), fAdHocSign(false), fAdHocSignForceOn(false), fAdHocSignForceOff(false), fPlatformMismatchesAreWarning(false), fForceObjCRelativeMethodListsOn(false), fForceObjCRelativeMethodListsOff(false), fUseObjCRelativeMethodLists(false), fObjcSmallStubs(false), fRunHugePass(true), fForceLdClassic(false), fLdPrimeFallback(false), fOptLevel(OptimizationLevel::unspecified), fSaveTempFiles(false), fLinkSnapshot(this), fSnapshotRequested(false), fPipelineFifo(NULL), fDependencyInfoPath(NULL), fBuildContextName(NULL), fTraceFileDescriptor(-1), fMaxDefaultCommonAlign(0), fUnalignedPointerTreatment(kUnalignedPointerIgnore), fPreferTAPIFile(false), fOSOPrefixPath(NULL) { this->expandResponseFiles(argc, argv); this->setupCrashReportInfo(argc, argv); this->parsePreCommandLineEnvironmentSettings(); this->parse(argc, argv); this->parsePostCommandLineEnvironmentSettings(); this->reconfigureDefaults(); this->checkIllegalOptionCombinations(); this->addDependency(depOutputFile, fOutputFile); if ( fMapPath != NULL ) this->addDependency(depOutputFile, fMapPath); } Options::~Options() { if ( fTraceFileDescriptor != -1 ) ::close(fTraceFileDescriptor); } bool Options::errorBecauseOfWarnings() const { return (sFatalWarnings && (sWarningsCount > 0)); } const char* Options::installPath() const { if ( fDylibInstallName != NULL ) return fDylibInstallName; else if ( fFinalName != NULL ) return fFinalName; else return fOutputFile; } void Options::addInterpose(const char* name) const { if ( fInterposeMode == kInterposeNone ) fInterposeMode = kInterposeSome; fInterposeList.insert(name, kDisallowWildcards); } bool Options::interposable(const char* name) const { switch ( fInterposeMode ) { case kInterposeNone: return false; case kInterposeAllExternal: return true; case kInterposeSome: return fInterposeList.contains(name); } throw "internal error"; } bool Options::printWhyLive(const char* symbolName) const { return fWhyLive.contains(symbolName); } const char* Options::dotOutputFile() const { return fDotOutputFile; } bool Options::hasWildCardExportRestrictList() const { // has -exported_symbols_list which contains some wildcards return ((fExportMode == kExportSome) && fExportSymbols.hasWildCards()); } bool Options::hasWeakBitTweaks() const { // has -exported_symbols_list which contains some wildcards return (!fForceWeakSymbols.empty() || !fForceNotWeakSymbols.empty()); } bool Options::allGlobalsAreDeadStripRoots() const { // -exported_symbols_list means globals are not exported by default if ( fExportMode == kExportSome ) return false; // 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; case Options::kDynamicLibrary: case Options::kDynamicBundle: case Options::kObjectFile: case Options::kDyld: case Options::kKextBundle: return true; } return false; } bool Options::dyldLoadsOutput() const { switch ( fOutputKind ) { case Options::kStaticExecutable: case Options::kPreload: case Options::kObjectFile: case Options::kKextBundle: return false; case Options::kDynamicExecutable: case Options::kDynamicLibrary: case Options::kDynamicBundle: case Options::kDyld: return true; } } bool Options::dyldOrKernelLoadsOutput() const { switch ( fOutputKind ) { case Options::kStaticExecutable: return fKernel; case Options::kPreload: case Options::kObjectFile: return false; case Options::kDynamicExecutable: case Options::kDynamicLibrary: case Options::kDynamicBundle: case Options::kDyld: case Options::kKextBundle: return true; } } bool Options::keepRelocations() { return fKeepRelocations; } bool Options::warnStabs() { return fWarnStabs; } 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) { if ( strcmp(it->name, segName) == 0 ) { return it->init; } } if ( strcmp(segName, "__TEXT") == 0 ) { return ( fUseTextExecSegment ? VM_PROT_READ : (VM_PROT_READ | VM_PROT_EXECUTE) ); } 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; } // all others default to read-write return VM_PROT_READ | VM_PROT_WRITE; } bool Options::readOnlyDataSegment(const char* name) const { // set SG_READ_ONLY on segment // but not for shared cache dylibs (because __objc_const is not read-only) return ( fUseDataConstSegment && (strcmp(name, "__DATA_CONST") == 0) && (!fSharedRegionEligible || (fOutputKind == Options::kDyld)) ); } uint32_t Options::maxSegProtection(const char* segName) const { // allow macOS binaries to set alternate max-prot if ( fArchitecture == CPU_TYPE_X86_64 ) { for (const Options::SegmentProtect& info : fCustomSegmentProtections) { if ( strcmp(info.name, segName) == 0 ) { return info.max; } } } // i386 uses text-relocations so, need max prot to be rwx. All others have max==init if ( fArchitecture != CPU_TYPE_I386 ) return initialSegProtection(segName); if ( strcmp(segName, "__PAGEZERO") == 0 ) { return 0; } // all others default to all return VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; } uint64_t Options::segPageSize(const char* segName) const { for(std::vector::const_iterator it=fCustomSegmentSizes.begin(); it != fCustomSegmentSizes.end(); ++it) { if ( strcmp(it->name, segName) == 0 ) return it->size; } return fSegmentAlignment; } uint64_t Options::customSegmentAddress(const char* segName) const { for(std::vector::const_iterator it=fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) { if ( strcmp(it->name, segName) == 0 ) return it->address; } // if custom stack in use, model as segment with custom address if ( (fStackSize != 0) && (strcmp("__UNIXSTACK", segName) == 0) ) return fStackAddr - fStackSize; return 0; } bool Options::hasCustomSegmentAddress(const char* segName) const { for(std::vector::const_iterator it=fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) { if ( strcmp(it->name, segName) == 0 ) return true; } // if custom stack in use, model as segment with custom address if ( (fStackSize != 0) && (strcmp("__UNIXSTACK", segName) == 0) ) return true; return false; } bool Options::hasCustomSectionAlignment(const char* segName, const char* sectName) const { for (std::vector::const_iterator it = fSectionAlignments.begin(); it != fSectionAlignments.end(); ++it) { 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; } uint8_t Options::customSectionAlignment(const char* segName, const char* sectName) const { for (std::vector::const_iterator it = fSectionAlignments.begin(); it != fSectionAlignments.end(); ++it) { 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; } void Options::loadSymbolOrderFile(const char* fileOfExports, NameToOrder& orderMapping) { // read in whole file int fd = ::open(fileOfExports, O_RDONLY, 0); if ( fd == -1 ) throwf("can't open -exported_symbols_order file: %s", fileOfExports); struct stat stat_buf; ::fstat(fd, &stat_buf); char* p = (char*)malloc(stat_buf.st_size); if ( p == NULL ) throwf("can't process -exported_symbols_order file: %s", fileOfExports); if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) throwf("can't read -exported_symbols_order file: %s", fileOfExports); ::close(fd); // 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; char* symbolStart = NULL; for (char* s = p; s < end; ++s ) { switch ( state ) { case lineStart: if ( *s =='#' ) { state = inComment; } else if ( !isspace(*s) ) { state = inSymbol; symbolStart = s; } break; case inSymbol: if ( (*s == '\n') || (*s == '\r') ) { *s = '\0'; // removing any trailing spaces char* last = s-1; while ( isspace(*last) ) { *last = '\0'; --last; } orderMapping[symbolStart] = ++count; symbolStart = NULL; state = lineStart; } break; case inComment: if ( (*s == '\n') || (*s == '\r') ) state = lineStart; break; } } if ( state == inSymbol ) { warning("missing line-end at end of file \"%s\"", fileOfExports); int len = end-symbolStart+1; char* temp = new char[len]; strlcpy(temp, symbolStart, len); // remove any trailing spaces char* last = &temp[len-2]; while ( isspace(*last) ) { *last = '\0'; --last; } orderMapping[temp] = ++count; } // Note: we do not free() the malloc buffer, because the strings are used by the export-set hash table } void Options::loadImplictZipperFile(const char *path, std::vector& paths) { // read in whole file int fd = ::open(path, O_RDONLY, 0); if ( fd == -1 ) return; // Early exist if the file is not present struct stat stat_buf; ::fstat(fd, &stat_buf); char* p = (char*)malloc(stat_buf.st_size); if ( p == NULL ) throwf("can't process auto zipper file: %s", path); if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) throwf("can't process auto zipper file: %s", path); ::close(fd); // parse into paths and add to a vector char * const end = &p[stat_buf.st_size]; enum { lineStart, inInstallname, inComment } state = lineStart; char* installnameStart = NULL; for (char* s = p; s < end; ++s ) { switch ( state ) { case lineStart: if ( *s =='#' ) { state = inComment; } else if ( !isspace(*s) ) { state = inInstallname; installnameStart = s; } break; case inInstallname: if ( (*s == '\n') || (*s == '\r') ) { *s = '\0'; // removing any trailing spaces char* last = s-1; while ( isspace(*last) ) { *last = '\0'; --last; } paths.push_back(installnameStart); state = lineStart; } break; case inComment: if ( (*s == '\n') || (*s == '\r') ) state = lineStart; break; } } if ( state == inInstallname ) { warning("missing line-end at end of file \"%s\"", path); int len = end-installnameStart+1; char* temp = new char[len]; strlcpy(temp, installnameStart, len); // remove any trailing spaces char* last = &temp[len-2]; while ( isspace(*last) ) { *last = '\0'; --last; } paths.push_back(installnameStart); } // Note: we do not free() the malloc buffer, because the strings are returned out via paths } bool Options::forceWeak(const char* symbolName) const { return fForceWeakSymbols.contains(symbolName); } bool Options::forceNotWeak(const char* symbolName) const { return fForceNotWeakSymbols.contains(symbolName); } bool Options::forceWeakNonWildCard(const char* symbolName) const { return fForceWeakSymbols.containsNonWildcard(symbolName); } 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 { switch (fExportMode) { case kExportSome: return fExportSymbols.contains(symbolName); case kDontExportSome: return ! fDontExportSymbols.contains(symbolName); case kExportDefault: return true; } throw "internal error"; } bool Options::shouldReExport(const char* symbolName) const { return fReExportSymbols.contains(symbolName); } bool Options::keepLocalSymbol(const char* symbolName) const { switch (fLocalSymbolHandling) { case kLocalSymbolsAll: return true; case kLocalSymbolsNone: return false; case kLocalSymbolsSelectiveInclude: return fLocalSymbolsIncluded.contains(symbolName); case kLocalSymbolsSelectiveExclude: return ! fLocalSymbolsExcluded.contains(symbolName); } 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::setInferredArch(cpu_type_t type, cpu_subtype_t subtype) { assert(fArchitecture == 0); for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { if ( (type == t->cpuType) && (subtype == t->cpuSubType) ) { fArchitecture = type; fSubArchitecture = subtype; fArchitectureName = t->archName; fArchThumb2Support = t->thumb2Support; #if SUPPORT_ARCH_arm64e if ( (fArchitecture == CPU_TYPE_ARM64) && (fSubArchitecture == CPU_SUBTYPE_ARM64E) ) { fSupportsAuthenticatedPointers = true; } #endif break; } } if ( fArchitecture == 0 ) { // some arch not in our table fArchitecture = type; fSubArchitecture = subtype; fArchitectureName = "unknown"; } fLinkSnapshot.recordArch(fArchitectureName); } bool Options::armFirmwareVariant() const { if ( fArchitecture != CPU_TYPE_ARM ) return false; switch ( fSubArchitecture ) { case CPU_SUBTYPE_ARM_V6M: case CPU_SUBTYPE_ARM_V7M: case CPU_SUBTYPE_ARM_V7EM: case CPU_SUBTYPE_ARM_V8M_MAIN: case CPU_SUBTYPE_ARM_V8M_BASE: case CPU_SUBTYPE_ARM_V8_1M_MAIN: return true; default: break; } return false; } bool Options::armUsesZeroCostExceptions() const { return ( (fArchitecture == CPU_TYPE_ARM) && (fSubArchitecture == CPU_SUBTYPE_ARM_V7K) ); } void Options::selectFallbackArch(const char *arch) { // Should have the format "desired_arch:fallback_arch", for example "arm64_32:armv7k" to allow an armv7k // slice to substitute for arm64_32 if the latter isn't present. if (const char* fallbackEnv = getenv("LD_DYLIB_ARCH_FALLBACK") ) { std::string fallback(fallbackEnv); auto delimPos = fallback.find(':'); // Check we've got a potentially valid fallback string and that it's this architecture we're falling back from. if ( delimPos != std::string::npos && fallback.substr(0, delimPos) == arch ) { std::string fallbackTo = fallback.substr(delimPos + 1); for (const ArchInfo *t = archInfoArray; t->archName != nullptr; ++t) { if ( fallbackTo == t->archName ) { fFallbackArchitecture = t->cpuType; fFallbackSubArchitecture = t->cpuSubType; } } } } // let x86_64h fallback and use x86_64 slice if ( fFallbackArchitecture == 0 && (fArchitecture == CPU_TYPE_X86_64) && (fSubArchitecture == CPU_SUBTYPE_X86_64_H) ) { fFallbackArchitecture = CPU_TYPE_X86_64; fFallbackSubArchitecture = CPU_SUBTYPE_X86_ALL; } } void Options::parseArch(const char* arch) { if ( arch == NULL ) throw "-arch must be followed by an architecture string"; for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { if ( strcmp(t->archName,arch) == 0 ) { fArchitectureName = arch; fArchitecture = t->cpuType; fSubArchitecture = t->cpuSubType; fArchThumb2Support = t->thumb2Support; selectFallbackArch(arch); return; } } throwf("unknown/unsupported architecture name for: -arch %s", arch); } static std::string addSuffix(const std::string &path, const std::string &suffix) { auto result = path; auto lastSlashIdx = result.find_last_of('/'); auto lastDotIdx = result.find_last_of('.'); if ( lastDotIdx != std::string::npos && lastSlashIdx != std::string::npos && lastDotIdx > lastSlashIdx ) { // /path/foo.dylib _debug => /path/foo_debug.dylib result.insert(lastDotIdx, suffix); } else if ( lastDotIdx != std::string::npos && lastSlashIdx == std::string::npos ) { // foo.dylib _debug => foo_debug.dylib result.insert(lastDotIdx, suffix); } else { // /path.foo/bar _debug => /path.foo/bar_debug // /path/bar _debug => /path/bar_debug result.append(suffix); } return result; } bool Options::checkForFileWithSuffix(const char* possiblePath, FileInfo& result) const { bool found = result.checkFileExists(*this, possiblePath); if ( fTraceDylibSearching ) printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), possiblePath); return found; } bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) const { char possiblePath[strlen(dir)+strlen(rootName)+strlen(format)+8]; sprintf(possiblePath, format, dir, rootName); for ( const char* imageSuffix : fImageSuffixes ) { auto possiblePathWithSuffix = addSuffix(possiblePath, imageSuffix); if ( checkForFileWithSuffix(possiblePathWithSuffix.c_str(), result) ) { return true; } } return checkForFileWithSuffix(possiblePath, result); } Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) const { FileInfo result(*this); 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::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; if ( checkForFile("%s/%s", dir, rootName, result) ) return result; } } else { 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::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; auto path = std::string(dir) + "/lib" + rootName + ".dylib"; if ( findFile(path, {".tbd"}, result) ) return result; } for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; if ( checkForFile("%s/lib%s.so", dir, rootName, result) ) return result; } } // next look in all directories for just for archives if ( !dylibsOnly ) { for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; if ( checkForFile("%s/lib%s.a", dir, rootName, result) ) return result; } } break; case kSearchDylibAndArchiveInEachDir: // look in each directory for just for a dylib then for an archive for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; 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; if ( !dylibsOnly && checkForFile("%s/lib%s.a", dir, rootName, result) ) return result; } break; } } throwf("library not found for -l%s", rootName); } LibraryOptions::ArchiveLoadMode Options::getArchiveLoadMode() const { if ( fFullyLoadArchives ) return LibraryOptions::ArchiveLoadMode::forceLoad; if ( fLoadAllObjcObjectsFromArchives ) return LibraryOptions::ArchiveLoadMode::objc; return LibraryOptions::ArchiveLoadMode::parseObjects; } Options::FileInfo Options::findFramework(const char* frameworkName) const { if ( frameworkName == NULL ) throw "-framework missing next argument"; char temp[strlen(frameworkName)+1]; strcpy(temp, frameworkName); const char* name = temp; const char* suffix = NULL; char* comma = strchr(temp, ','); if ( comma != NULL ) { *comma = '\0'; suffix = &comma[1]; } return findFramework(name, suffix); } Options::FileInfo Options::findFramework(const char* rootName, const char* suffix, bool useCurrentVersion) const { for (const auto* path : fFrameworkSearchPaths) { auto possiblePath = std::string(path).append("/").append(rootName).append(".framework/"); if ( useCurrentVersion ) possiblePath.append("Versions/Current/"); possiblePath.append(rootName); if ( suffix != nullptr ) { char realPath[PATH_MAX]; // no symlink in framework to suffix variants, so follow main symlink if any if ( realpath(possiblePath.c_str(), realPath) != nullptr ) possiblePath = std::string(realPath).append(suffix); else possiblePath.append(suffix); } FileInfo result(*this); if ( findFile(possiblePath, {".tbd"}, result) ) return result; } // try looking into Versions/Current if ( useCurrentVersion == false && fSearchInSparseFrameworks ) { return findFramework(rootName, suffix, true); } // try without suffix if ( suffix != NULL ) return findFramework(rootName, NULL); else throwf("framework not found %s", rootName); } 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 || lastSlashIdx == std::string::npos ) ) result.erase(lastDotIdx, std::string::npos); if ( ext.size() > 0 && ext[0] == '.' ) result.append(ext); else result.append('.' + ext); return result; } void Options::addTAPIInterface(tapi::LinkerInterfaceFile* interface, const char *path) const { #if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 6) || (TAPI_API_VERSION_MAJOR > 1)) for (auto &name : interface->inlinedFrameworkNames()) fTAPIFiles.emplace_back(interface, path, name.c_str()); #endif } bool Options::findFileWithSuffix(const std::string &path, const std::vector &tbdExtensions, FileInfo& result) const { FileInfo tbdInfo(*this); 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(*this); { 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 (env var or in B&I). if (fPreferTAPIFile) { result = tbdInfo; // Check if provided path points directly to the TBD file. } else if (path == tbdInfo.path) { result = tbdInfo; // Use the TBD whenever its not from a locally installed SDK. } else if (strstr(tbdInfo.path, ".sdk/") == NULL ) { result = tbdInfo; } // Otherwise fall-back to the dynamic library file. else { // rdar://102474464 (Xcode.Internal.sdk should remove binary dylibs and introduce tbd post processing to replace them) if ( strstr(tbdInfo.path, "/SDKs/Xcode.Internal") == NULL ) warning("text-based stub file %s and library file %s unexpectedly found. Falling back to library file for linking.", tbdInfo.path, dylibInfo.path); result = dylibInfo; } } else { return false; } return true; } bool Options::findFile(const std::string &path, const std::vector &tbdExtensions, FileInfo& result) const { for ( const char* imageSuffix : fImageSuffixes ) { auto pathWithSuffix = addSuffix(path, imageSuffix); if ( findFileWithSuffix(pathWithSuffix, tbdExtensions, result) ) { return true; } } return findFileWithSuffix(path, tbdExtensions, result); } 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(*this); // if absolute path and not a .o file, then use SDK prefix bool pathIsObjectFile = (strcmp(&path[path.size()-2], ".o") == 0); bool pathIsStaticLibrary = (strcmp(&path[path.size()-2], ".a") == 0); if ( (path[0] == '/') && !pathIsObjectFile ) { for (const auto* sdkPathDir : fSDKPaths) { auto possiblePath = std::string(sdkPathDir) + path; if ( findFile(possiblePath, {".tbd"}, result) ) 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; } } } } // find inlined TBD file before raw path. // rdar://problem/35864452 if (hasInlinedTAPIFile(path)) { FileInfo inlinedFile(*this, path.c_str()); inlinedFile.isInlined = true; return inlinedFile; } // try raw path if ( pathIsObjectFile || pathIsStaticLibrary ) { // don't look for .tbd files for .o or .a files if ( findFile(path, { }, result) ) return result; } else { if ( findFile(path, {".tbd"}, result) ) return result; } // not found throwf("file not found: %s", path.c_str()); } bool Options::hasInlinedTAPIFile(const std::string &path) const { for (const auto &dylib : fTAPIFiles) { if (dylib.getInstallName() == path) return true; } return false; } tapi::LinkerInterfaceFile* Options::findTAPIFile(const std::string &path) const { #if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 6) || (TAPI_API_VERSION_MAJOR > 1)) tapi::LinkerInterfaceFile* interface = nullptr; std::string TBDPath; // create parsing options. tapi::ParsingFlags flags = tapi::ParsingFlags::None; if (enforceDylibSubtypesMatch()) flags |= tapi::ParsingFlags::ExactCpuSubType; if (!allowWeakImports()) flags |= tapi::ParsingFlags::DisallowWeakImports; __block uint32_t linkMinOSVersion = 0; //FIXME handle this correctly once we have multi-platform TAPI. platforms().forEach(^(ld::Platform platform, uint32_t minVersion, uint32_t sdkVersion, bool &stop) { if (linkMinOSVersion == 0) linkMinOSVersion = minVersion; if (platform == ld::Platform::macOS) linkMinOSVersion = minVersion; }); // Search through all the inlined framework. for (const auto &dylib : fTAPIFiles) { if (dylib.getInstallName() == path) { // If the install name matches, parse the framework. std::string errorMessage; auto file = dylib.getInterfaceFile()->getInlinedFramework(path.c_str(), architecture(), subArchitecture(), flags, tapi::PackedVersion32(linkMinOSVersion), errorMessage); if (!file) throw strdup(errorMessage.c_str()); if (!interface) { // If this is the first inlined framework found, record the information. interface = file; TBDPath = dylib.getTAPIFilePath(); } else { // If we found other inlined framework already, check to see if their versions are the same. // If not the same, emit an warning and record the newer one. Otherwise, just use the current one. if (interface->getCurrentVersion() == file->getCurrentVersion()) continue; warning("Inlined framework/dylib mismatch: %s (%s and %s)", path.c_str(), TBDPath.c_str(), dylib.getTAPIFilePath().c_str()); if (interface->getCurrentVersion() < file->getCurrentVersion()) { interface = file; TBDPath = dylib.getTAPIFilePath(); } } } } return interface; #else return nullptr; #endif } // 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(*this); 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 ( lastSlashPos != std::string::npos ) { auto frameworkDir = std::string("/").append(leafName).append(".framework/"); if ( installName.rfind(frameworkDir) != std::string::npos ) isFramework = true; } // These are abbreviated versions of the routines findFramework and findLibrary above // because we already know the final name of the file that we're looking for and so // don't need to try variations, just paths. We do need to add the additional bits // onto the framework path though. if ( isFramework ) { 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 { // 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 bool embeddedDylib = ( (leafName.size() > 6) && (leafName.find(".dylib", leafName.size()-6) != std::string::npos) && (installName.find(".framework/") != std::string::npos) ); if ( !embeddedDylib ) { for (const auto* dir : fLibrarySearchPaths) { //fprintf(stderr,"Finding Library: %s/%s\n", dir, leafName); 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(installName, fromDylib); } void Options::loadFileList(const char* fileOfPaths, ld::File::Ordinal baseOrdinal) { FILE* file; const char* comma = strrchr(fileOfPaths, ','); const char* prefix = NULL; if ( comma != NULL ) { // -filelist fails with comma in path file = fopen(fileOfPaths, "r"); if ( file == NULL ) { prefix = comma+1; int realFileOfPathsLen = comma-fileOfPaths; char realFileOfPaths[realFileOfPathsLen+1]; strncpy(realFileOfPaths,fileOfPaths, realFileOfPathsLen); realFileOfPaths[realFileOfPathsLen] = '\0'; file = fopen(realFileOfPaths, "r"); if ( file == NULL ) throwf("-filelist file '%s' could not be opened, errno=%d (%s)\n", realFileOfPaths, errno, strerror(errno)); this->addDependency(Options::depFileList, realFileOfPaths); } else this->addDependency(Options::depFileList, fileOfPaths); } else { file = fopen(fileOfPaths, "r"); if ( file == NULL ) throwf("-filelist file '%s' could not be opened, errno=%d (%s)\n", fileOfPaths, errno, strerror(errno)); this->addDependency(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'); if ( eol != NULL ) *eol = '\0'; if ( prefix != NULL ) { char builtPath[strlen(prefix)+strlen(path)+2]; strcpy(builtPath, prefix); strcat(builtPath, "/"); strcat(builtPath, path); if (fPipelineFifo != NULL) { FileInfo info = FileInfo(*this, 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 { if (fPipelineFifo != NULL) { FileInfo info = FileInfo(*this, 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); } void Options::SetWithWildcards::remove(const NameSet& toBeRemoved) { for(NameSet::const_iterator it=toBeRemoved.begin(); it != toBeRemoved.end(); ++it) { const char* symbolName = *it; NameSet::iterator pos = fRegular.find(symbolName); if ( pos != fRegular.end() ) fRegular.erase(pos); } } bool Options::SetWithWildcards::hasWildCards(const char* symbol) { // an exported symbol name containing *, ?, or [ requires wildcard matching return ( strpbrk(symbol, "*?[") != NULL ); } void Options::SetWithWildcards::insert(const char* symbol, SymbolMatchingMode match_mode) { if ( match_mode == kAllowWildcards && hasWildCards(symbol) ) fWildCard.push_back(symbol); else fRegular.insert(symbol); } 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 ( 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 return ( fRegular.find(symbol) != fRegular.end() ); } bool Options::SetWithWildcards::inCharRange(const char*& p, unsigned char c) const { ++p; // find end const char* b = p; while ( *p != '\0' ) { if ( *p == ']') { const char* e = p; // found beginining [ and ending ] unsigned char last = '\0'; for ( const char* s = b; s < e; ++s ) { if ( *s == '-' ) { unsigned char next = *(++s); if ( (last <= c) && (c <= next) ) return true; ++s; } else { if ( *s == c ) return true; last = *s; } } return false; } ++p; } return false; } bool Options::SetWithWildcards::wildCardMatch(const char* pattern, const char* symbol) const { const char* s = symbol; for (const char* p = pattern; *p != '\0'; ++p) { switch ( *p ) { case '*': if ( p[1] == '\0' ) return true; for (const char* t = s; *t != '\0'; ++t) { if ( wildCardMatch(&p[1], t) ) return true; } return false; case '?': if ( *s == '\0' ) return false; ++s; break; case '[': if ( ! inCharRange(p, *s) ) return false; ++s; break; default: if ( *s != *p ) return false; ++s; } } return (*s == '\0'); } void Options::loadExportFile(const char* fileOfExports, const char* option, SetWithWildcards& set, SymbolMatchingMode match_mode) { if ( fileOfExports == NULL ) throwf("missing file after %s", option); // read in whole file int fd = ::open(fileOfExports, O_RDONLY, 0); if ( fd == -1 ) throwf("can't open %s file: %s", option, fileOfExports); struct stat stat_buf; ::fstat(fd, &stat_buf); char* p = (char*)malloc(stat_buf.st_size); if ( p == NULL ) throwf("can't process %s file: %s", option, fileOfExports); if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) throwf("can't read %s file: %s", option, fileOfExports); this->addDependency(Options::depMisc, fileOfExports); ::close(fd); // 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; for (char* s = p; s < end; ++s ) { switch ( state ) { case lineStart: if ( *s =='#' ) { state = inComment; } else if ( !isspace(*s) ) { state = inSymbol; symbolStart = s; } break; case inSymbol: if ( (*s == '\n') || (*s == '\r') ) { *s = '\0'; // removing any trailing spaces char* last = s-1; while ( isspace(*last) ) { *last = '\0'; --last; } set.insert(symbolStart, match_mode); symbolStart = NULL; state = lineStart; } break; case inComment: if ( (*s == '\n') || (*s == '\r') ) state = lineStart; break; } } if ( state == inSymbol ) { warning("missing line-end at end of file \"%s\"", fileOfExports); int len = end-symbolStart+1; char* temp = new char[len]; strlcpy(temp, symbolStart, len); // remove any trailing spaces char* last = &temp[len-2]; while ( isspace(*last) ) { *last = '\0'; --last; } set.insert(temp, match_mode); } // Note: we do not free() the malloc buffer, because the strings are used by the export-set hash table } void Options::parseAliasFile(const char* fileOfAliases) { // read in whole file int fd = ::open(fileOfAliases, O_RDONLY, 0); if ( fd == -1 ) throwf("can't open alias file: %s", fileOfAliases); struct stat stat_buf; ::fstat(fd, &stat_buf); char* p = (char*)malloc(stat_buf.st_size+1); if ( p == NULL ) throwf("can't process alias file: %s", fileOfAliases); if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) throwf("can't read alias file: %s", fileOfAliases); p[stat_buf.st_size] = '\n'; ::close(fd); this->addDependency(Options::depMisc, fileOfAliases); // parse into symbols and add to fAliases AliasPair pair; char * const end = &p[stat_buf.st_size+1]; enum { lineStart, inRealName, inBetween, inAliasName, inComment } state = lineStart; int lineNumber = 1; for (char* s = p; s < end; ++s ) { switch ( state ) { case lineStart: if ( *s =='#' ) { state = inComment; } else if ( !isspace(*s) ) { state = inRealName; pair.realName = s; } break; case inRealName: if ( *s == '\n' ) { warning("line needs two symbols but has only one at line #%d in \"%s\"", lineNumber, fileOfAliases); ++lineNumber; state = lineStart; } else if ( isspace(*s) ) { *s = '\0'; state = inBetween; } break; case inBetween: if ( *s == '\n' ) { warning("line needs two symbols but has only one at line #%d in \"%s\"", lineNumber, fileOfAliases); ++lineNumber; state = lineStart; } else if ( ! isspace(*s) ) { state = inAliasName; pair.alias = s; } break; case inAliasName: if ( *s =='#' ) { *s = '\0'; // removing any trailing spaces char* last = s-1; while ( isspace(*last) ) { *last = '\0'; --last; } fAliases.push_back(pair); state = inComment; } else if ( *s == '\n' ) { *s = '\0'; // removing any trailing spaces char* last = s-1; while ( isspace(*last) ) { *last = '\0'; --last; } fAliases.push_back(pair); state = lineStart; } break; case inComment: if ( *s == '\n' ) state = lineStart; break; } } // Note: we do not free() the malloc buffer, because the strings therein are used by fAliases } void Options::setUndefinedTreatment(const char* treatment) { if ( treatment == NULL ) throw "-undefined missing [ warning | error | suppress | dynamic_lookup ]"; if ( strcmp(treatment, "warning") == 0 ) fUndefinedTreatment = kUndefinedWarning; else if ( strcmp(treatment, "error") == 0 ) fUndefinedTreatment = kUndefinedError; else if ( strcmp(treatment, "suppress") == 0 ) fUndefinedTreatment = kUndefinedSuppress; else if ( strcmp(treatment, "dynamic_lookup") == 0 ) fUndefinedTreatment = kUndefinedDynamicLookup; else throw "invalid option to -undefined [ warning | error | suppress | dynamic_lookup ]"; } Options::Treatment Options::parseTreatment(const char* treatment) { if ( treatment == NULL ) return kNULL; if ( strcmp(treatment, "warning") == 0 ) return kWarning; else if ( strcmp(treatment, "error") == 0 ) return kError; else if ( strcmp(treatment, "suppress") == 0 ) return kSuppress; else return kInvalid; } void Options::setWeakReferenceMismatchTreatment(const char* treatment) { if ( treatment == NULL ) throw "-weak_reference_mismatches missing [ error | weak | non-weak ]"; if ( strcmp(treatment, "error") == 0 ) fWeakReferenceMismatchTreatment = kWeakReferenceMismatchError; else if ( strcmp(treatment, "weak") == 0 ) fWeakReferenceMismatchTreatment = kWeakReferenceMismatchWeak; else if ( strcmp(treatment, "non-weak") == 0 ) fWeakReferenceMismatchTreatment = kWeakReferenceMismatchNonWeak; else throw "invalid option to -weak_reference_mismatches [ error | weak | non-weak ]"; } Options::CommonsMode Options::parseCommonsTreatment(const char* mode) { if ( mode == NULL ) throw "-commons missing [ ignore_dylibs | use_dylibs | error ]"; if ( strcmp(mode, "ignore_dylibs") == 0 ) return kCommonsIgnoreDylibs; else if ( strcmp(mode, "use_dylibs") == 0 ) return kCommonsOverriddenByDylibs; else if ( strcmp(mode, "error") == 0 ) return kCommonsConflictsDylibsError; else throw "invalid option to -commons [ ignore_dylibs | use_dylibs | error ]"; } void Options::addDylibOverride(const char* paths) { if ( paths == NULL ) throw "-dylib_file must followed by two colon separated paths"; const char* colon = strchr(paths, ':'); if ( colon == NULL ) throw "-dylib_file must followed by two colon separated paths"; int len = colon-paths; char* target = new char[len+2]; strncpy(target, paths, len); target[len] = '\0'; DylibOverride entry; entry.installName = target; entry.useInstead = &colon[1]; fDylibOverrides.push_back(entry); } uint64_t Options::parseAddress(const char* addr) { char* endptr; uint64_t result = strtoull(addr, &endptr, 16); return result; } uint32_t Options::parseProtection(const char* prot) { uint32_t result = 0; for(const char* p = prot; *p != '\0'; ++p) { switch(tolower(*p)) { case 'r': result |= VM_PROT_READ; break; case 'w': result |= VM_PROT_WRITE; break; case 'x': result |= VM_PROT_EXECUTE; break; case '-': break; default: throwf("unknown -segprot letter in %s", prot); } } return result; } // // 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::parseVersionNumber32(const char* versionString) { uint32_t x = 0; uint32_t y = 0; uint32_t z = 0; char* end; x = strtoul(versionString, &end, 10); if ( *end == '.' ) { y = strtoul(&end[1], &end, 10); if ( *end == '.' ) { z = strtoul(&end[1], &end, 10); } } if ( (*end != '\0') || (x > 0xffff) || (y > 0xff) || (z > 0xff) ) throwf("malformed 32-bit x.y.z version number: %s", versionString); return (x << 16) | ( y << 8 ) | z; } static const char* cstringSymbolName(const char* orderFileString) { char* result; asprintf(&result, "cstring=%s", orderFileString); // convert escaped characters char* d = result; for(const char* s=result; *s != '\0'; ++s, ++d) { if ( *s == '\\' ) { ++s; switch ( *s ) { case 'n': *d = '\n'; break; case 't': *d = '\t'; break; case 'v': *d = '\v'; break; case 'b': *d = '\b'; break; case 'r': *d = '\r'; break; case 'f': *d = '\f'; break; case 'a': *d = '\a'; break; case '\\': *d = '\\'; break; case '?': *d = '\?'; break; case '\'': *d = '\r'; break; case '\"': *d = '\"'; break; case 'x': // hexadecimal value of char { ++s; char value = 0; while ( isxdigit(*s) ) { value *= 16; if ( isdigit(*s) ) value += (*s-'0'); else value += ((toupper(*s)-'A') + 10); ++s; } *d = value; } break; default: if ( isdigit(*s) ) { // octal value of char char value = 0; while ( isdigit(*s) ) { value = (value << 3) + (*s-'0'); ++s; } *d = value; } } } else { *d = *s; } } *d = '\0'; return result; } 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 ) throwf("can't open order file: %s", path); struct stat stat_buf; ::fstat(fd, &stat_buf); char* p = (char*)malloc(stat_buf.st_size+1); if ( p == NULL ) throwf("can't process order file: %s", path); if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) throwf("can't read order file: %s", path); ::close(fd); p[stat_buf.st_size] = '\n'; this->addDependency(Options::depMisc, path); fOrderFilePath = strdup(path); // parse into vector of pairs char * const end = &p[stat_buf.st_size+1]; enum { lineStart, inSymbol, inComment } state = lineStart; char* symbolStart = NULL; for (char* s = p; s < end; ++s ) { switch ( state ) { case lineStart: if ( *s =='#' ) { state = inComment; } else if ( !isspace(*s) || cstring ) { state = inSymbol; symbolStart = s; } break; case inSymbol: if ( (*s == '\n') || (!cstring && (*s == '#')) ) { bool wasComment = (*s == '#'); *s = '\0'; // removing any trailing spaces char* last = s-1; while ( isspace(*last) ) { *last = '\0'; --last; } // if there is an architecture prefix, only use this symbol it if matches current arch if ( strncmp(symbolStart, "ppc:", 4) == 0 ) { symbolStart = NULL; } else if ( strncmp(symbolStart, "ppc64:", 6) == 0 ) { symbolStart = NULL; } else if ( strncmp(symbolStart, "i386:", 5) == 0 ) { if ( fArchitecture == CPU_TYPE_I386 ) symbolStart = &symbolStart[5]; else symbolStart = NULL; } else if ( strncmp(symbolStart, "x86_64:", 7) == 0 ) { if ( fArchitecture == CPU_TYPE_X86_64 ) symbolStart = &symbolStart[7]; else symbolStart = NULL; } else if ( strncmp(symbolStart, "arm:", 4) == 0 ) { if ( fArchitecture == CPU_TYPE_ARM ) symbolStart = &symbolStart[4]; 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:"); if ( colon != NULL ) { colon[2] = '\0'; 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; Options::OrderedSymbol pair; if ( cstring ) pair.symbolName = cstringSymbolName(symbolStart); else pair.symbolName = symbolStart; pair.objectFileName = objFileName; fOrderedSymbols.push_back(pair); } symbolStart = NULL; if ( wasComment ) state = inComment; else state = lineStart; } break; case inComment: if ( *s == '\n' ) state = lineStart; break; } } // Note: we do not free() the malloc buffer, because the strings are used by the fOrderedSymbols } void Options::parseSectionOrderFile(const char* segment, const char* section, const char* path) { if ( (strcmp(section, "__cstring") == 0) && (strcmp(segment, "__TEXT") == 0) ) { parseOrderFile(path, true); } else if ( (strncmp(section, "__literal",9) == 0) && (strcmp(segment, "__TEXT") == 0) ) { warning("sorting of __literal[4,8,16] sections not supported"); } else { // ignore section information and append all symbol names to global order file parseOrderFile(path, false); } } void Options::addSection(const char* segment, const char* section, const char* path) { if ( strlen(segment) > 16 ) throw "-seccreate segment name max 16 chars"; if ( strlen(section) > 16 ) { char* tmp = strdup(section); tmp[16] = '\0'; warning("-seccreate section name (%s) truncated to 16 chars (%s)\n", section, tmp); section = tmp; } uint64_t size = 0; uint8_t* p = nullptr; // path == NULL is to support -add_empty_section // if ( path != NULL ) { // read in whole file int fd = ::open(path, O_RDONLY, 0); if ( fd == -1 ) throwf("can't open -sectcreate file: %s", path); struct stat stat_buf; ::fstat(fd, &stat_buf); size = stat_buf.st_size; // ld fails mapping if the file passed is `/dev/null` if ( stat_buf.st_size > 0 ) { p = (uint8_t*)::mmap(NULL, size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); if ( p == MAP_FAILED ) throwf("can't read -sectcreate file: %s, errno=%d", path, errno); } ::close(fd); } // record section to create ExtraSection info = { segment, section, path, p, 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, SymbolMatchingMode match_mode) { 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, match_mode); } bool Options::moveRwSymbol(std::string_view symName, const char* filePath, const char*& seg, bool& wildCardMatch) const { auto nameStr = std::string(symName); auto *name = nameStr.c_str(); for (std::vector::const_iterator it=fSymbolsMovesData.begin(); it != fSymbolsMovesData.end(); ++it) { const SymbolsMove& info = *it; if ( info.symbols.containsWithPrefix(name, filePath, wildCardMatch)) { seg = info.toSegment; return true; } } return false; } bool Options::moveAXMethodList(const char* className) const { for (const SymbolsMove& sm : fSymbolsMovesAXMethodLists) { bool wildcard; if ( sm.symbols.containsWithPrefix(className, NULL, wildcard) ) return true; } return false; } bool Options::moveRoSymbol(std::string_view symName, const char* filePath, const char*& seg, bool& wildCardMatch) const { auto nameStr = std::string(symName); auto *name = nameStr.c_str(); for (std::vector::const_iterator it=fSymbolsMovesCode.begin(); it != fSymbolsMovesCode.end(); ++it) { const SymbolsMove& info = *it; if ( info.symbols.containsWithPrefix(name, 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 ) throw "-sectalign segment name max 16 chars"; if ( strlen(section) > 16 ) throw "-sectalign section name max 16 chars"; // argument to -sectalign is a hexadecimal number char* endptr; unsigned long value = strtoul(alignmentStr, &endptr, 16); if ( *endptr != '\0') throw "argument for -sectalign is not a hexadecimal number"; if ( value > 0x8000 ) throw "argument for -sectalign must be less than or equal to 0x8000"; if ( value == 0 ) { warning("zero is not a valid -sectalign"); value = 1; } // alignment is power of 2 (e.g. page alignment = 12) uint8_t alignment = (uint8_t)__builtin_ctz(value); if ( (unsigned long)(1 << alignment) != value ) { warning("alignment for -sectalign %s %s is not a power of two, using 0x%X", segment, section, 1 << alignment); } SectionAlignment info = { segment, section, alignmentStr, alignment }; fSectionAlignments.push_back(info); } void Options::addLibrary(const FileInfo& info) { // if this library has already been added, don't add again (archives are automatically repeatedly searched) for (FileInfo& otherInfo : fInputFiles) { if ( strcmp(info.path, otherInfo.path) == 0 ) { // if dylib is specified again but weak, record that it should be weak if ( info.options.fWeakImport ) otherInfo.options.fWeakImport = true; return; } } // add to list fInputFiles.push_back(info); } void Options::warnObsolete(const char* arg) { warning("option %s is obsolete and being ignored", 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; if ( microVersion == 0 ) versionString << majorVersion << "." << minorVersion; else 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 = (uint32_t)((majorValue << 16) | (minorValue << 8) | microValue); return true; } catch (...) { // std::stoi() throws exception on malformed input return false; } } std::string Options::getSDKVersionStr() const { std::string retval; auto appendString = [&](const std::string& string) { if (retval.empty()) { retval = string; } else { retval += "/"; retval += string; } }; platforms().forEach(^(ld::Platform platform, uint32_t minVersion, uint32_t sdkVersion, bool &stop) { appendString(getVersionString32(sdkVersion)); }); return retval; } std::vector Options::writeBitcodeLinkOptions() const { __block 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". platforms().forEach(^(ld::Platform platform, uint32_t minVersion, uint32_t sdkVersion, bool &stop) { std::string platName = nameFromPlatform(platform); std::transform(platName.begin(), platName.end(), platName.begin(), [](unsigned char c) -> unsigned char { if (c == ' ') return '-'; return std::tolower(c); }); linkCommand.push_back("-platform_version"); linkCommand.push_back(platName.c_str()); linkCommand.push_back(getVersionString32(minVersion)); linkCommand.push_back(getVersionString32(sdkVersion)); }); // 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 ( fRemoveSwiftReflectionMetadataSections ) linkCommand.push_back("-remove_swift_reflection_metadata_sections"); 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)); } for (const SectionAlignment §align : fSectionAlignments) for (const char *arg : {"-sectalign", sectalign.segmentName, sectalign.sectionName, sectalign.alignmentStr}) linkCommand.push_back(arg); // linker flag added by swift driver // rdar://problem/20108072 if ( !fObjcCategoryMerging ) linkCommand.push_back("-no_objc_category_merging"); return linkCommand; } const char* Options::checkForNullArgument(const char* argument_name, const char* arg, bool allowDashArg) const { if ( arg == NULL ) throwf("missing argument for %s", argument_name); if ( !allowDashArg && (arg[0] == '-') ) warning("argument missing after %s", argument_name); return arg; } const char* Options::checkForNullVersionArgument(const char* argument_name, const char* arg) const { if ( arg == NULL ) throwf("%s missing version argument", argument_name); return arg; } static const ld::PlatformInfo* isPlatformOption(const char* arg) { __block const ld::PlatformInfo* result = nullptr; forEachSupportedPlatform(^(const ld::PlatformInfo& info, bool& stop) { if ( strcmp(arg, info.commandLineArg) == 0 ) { result = &info; stop = true; } }); return result; } static const char* removeDoubleSlash(const char* path) { if ( strstr(path, "//") == nullptr ) return path; char fixedPath[strlen(path)+1]; char* t = fixedPath; bool lastWasSlash = false; for (const char* s=path; *s != '\0'; ++s) { if ( *s == '/' ) { if ( !lastWasSlash ) *t++ = *s; lastWasSlash = true; } else { *t++ = *s; lastWasSlash = false; } } *t = '\0'; return strdup(fixedPath); } // // Process all command line arguments. // // The only error checking done here is that each option is valid and if it has arguments // that they too are valid. // // The general rule is "last option wins", i.e. if both -bundle and -dylib are specified, // whichever was last on the command line is used. // // Error check for invalid combinations of options is done in checkIllegalOptionCombinations() // 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); // reduce re-allocations fInputFiles.reserve(32); // pass two parse all other options for(int i=1; i < argc; ++i) { 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); // grandfather in old arg name if ( strcmp(arg, "-iphoneos_version_min") == 0 ) arg = "-ios_version_min"; if ( strcmp(arg, "-macosx_version_min") == 0 ) arg = "-macos_version_min"; if ( (strcmp(arg, "-iosmac_version_min") == 0) || (strcmp(arg, "-uikitformac_version_min") == 0) ) arg = "-maccatalyst_version_min"; if ( (arg[1] == 'L') || (arg[1] == 'F') ) { snapshotArgCount = 0; // stripped out of link snapshot if (arg[2] == '\0') ++i; // previously handled by buildSearchPaths() } // The one gnu style option we have to keep compatibility // with gcc. Might as well have the single hyphen one as well. else if ( (strcmp(arg, "--help") == 0) || (strcmp(arg, "-help") == 0)) { fprintf (stdout, "ld64: For information on command line options please use 'man ld'.\n"); exit (0); } else if ( strcmp(arg, "-arch") == 0 ) { const char* arch = argv[++i]; parseArch(arch); fLinkSnapshot.recordArch(arch); } else if ( strcmp(arg, "-dynamic") == 0 ) { // default } else if ( strcmp(arg, "-static") == 0 ) { fForStatic = true; 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 ) fOutputKind = kDynamicExecutable; } 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, "-kernel") == 0 ) { fKernel = true; } else if ( strcmp(arg, "-kext_objects_dir") == 0 ) { warnObsolete(arg); ++i; } else if ( strcmp(arg, "-no_kext_objects") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-o") == 0 ) { snapshotArgCount = 0; fOutputFile = checkForNullArgument(arg, argv[++i]); fLinkSnapshot.setOutputPath(fOutputFile); // If the file exists, and it's a directory, throw an error. struct stat statbuf; int ret = ::stat(fOutputFile, &statbuf); if ( ret == 0 && S_ISDIR(statbuf.st_mode) ) { throwf("can't write output file to '%s' because that path is a directory", fOutputFile); } } else if ( strcmp(arg, "-ld_classic") == 0 ) { // force use of ld-classic - nothing to do, because ld is the driver now fForceLdClassic = true; } else if ( strcmp(arg, "-fallback_ldprime") == 0 ) { fLdPrimeFallback = true; } else if ( strcmp(arg, "-ld64") == 0 ) { // force use of ld-classic - nothing to do, because ld is the driver now warning("-ld64 is deprecated, use -ld_classic instead"); } else if ( strcmp(arg, "-no_warn_duplicate_libraries") == 0 ) { // there're no duplicate library warnings in ld64, nothing to do } else if ( strcmp(arg, "-warn_duplicate_libraries") == 0 ) { // there're no duplicate library warnings in ld64, nothing to do } else if ( strncmp(arg, "-lazy-l", 7) == 0 ) { snapshotArgCount = 0; FileInfo info = findLibrary(&arg[7], true); info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); cannotBeUsedWithBitcode(arg); warning("-lazy-l is deprecated, changing to regular link"); } 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, "-lto_softload_runtime_symbols") == 0 ) { fLTOSoftloadRuntimeSymbolsForceOn = true; } else if ( strcmp(arg, "-no_lto_softload_runtime_symbols") == 0 ) { fLTOSoftloadRuntimeSymbolsForceOff = true; } 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; fLtoPruneIntervalOverwrite = true; 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) && (strcmp(arg,"-load_hidden") != 0) ) { snapshotArgCount = 0; try { FileInfo info(*this); // Make -l accept a space before the argument if (arg[2] == '\0') { if (i + 1 >= argc) { throw "missing -l argument"; } info = findLibrary(argv[++i]); } else { info = findLibrary(&arg[2]); } info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } catch (const char* message) { // see if this is a Csu .o file only need for when targeting old OS versions __block bool isPreMinOS = false; __block ld::Platform thisPlatform = ld::Platform::unknown; __block uint32_t thisPlatformMinOS = 0; fPlatforms.forEach(^(ld::Platform aPlatform, uint32_t minVersion, uint32_t sdkVersion, bool& stop) { const ld::PlatformInfo& info = platformInfo(aPlatform); if ( minVersion < info.minimumOsVersion ) { isPreMinOS = true; thisPlatform = aPlatform; thisPlatformMinOS = info.minimumOsVersion; } }); if ( !isPreMinOS ) throw; if ( strncmp(arg, "-l", 2) != 0 ) throw; if ( strncmp(&arg[strlen(arg)-2], ".o", 2) != 0 ) throw; // throw away this .o and use newer min OS vers warning("Csu support file %s not found, changing to target %s %s where it is not needed", arg, nameFromPlatform(thisPlatform), getVersionString32(thisPlatformMinOS).c_str()); fPlatforms.updateMinVersion(thisPlatform, thisPlatformMinOS); } } // This causes a dylib to be weakly bound at // link time. This corresponds to weak_import. else if ( strncmp(arg, "-weak-l", 7) == 0 ) { snapshotArgCount = 0; FileInfo info = findLibrary(&arg[7]); info.options.fWeakImport = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } else if ( strncmp(arg, "-needed-l", 9) == 0 ) { snapshotArgCount = 0; FileInfo info = findLibrary(&arg[9]); info.options.fNeeded = 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 ) { warning("-force_flat_namespace is no longer supported, using -flat_namespace instead"); fNameSpace = kForceFlatNameSpace; cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-all_load") == 0 ) { // previously handled by buildSearchPaths() } else if ( strcmp(arg, "-noall_load") == 0) { warnObsolete(arg); } else if ( strcmp(arg, "-ObjC") == 0 ) { // previously handled by buildSearchPaths() } // Similar to -all_load, but for the following archive only. else if ( strcmp(arg, "-force_load") == 0 ) { const char* path = checkForNullArgument(arg, argv[++i]); FileInfo info = findFile(path); info.options.fStaticLibMode = LibraryOptions::ArchiveLoadMode::forceLoad; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } else if ( strcmp(arg, "-load_hidden") == 0 ) { const char* path = checkForNullArgument(arg, argv[++i]); FileInfo info = findFile(path); info.options.fLoadHidden = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } else if ( strncmp(arg, "-hidden-l", 9) == 0 ) { snapshotArgCount = 0; FileInfo info = findLibrary(&arg[9]); info.options.fLoadHidden = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } // Library versioning. else if ( (strcmp(arg, "-dylib_compatibility_version") == 0) || (strcmp(arg, "-compatibility_version") == 0)) { const char* vers = argv[++i]; if ( vers == NULL ) throw "-dylib_compatibility_version missing "; 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 = 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; } else if ( (strcmp(arg, "-add_empty_section") == 0) ) { if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) ) throw "-add_empty_section missing
"; snapshotFileArgIndex = 2; addSection(argv[i+1], argv[i+2], NULL); i += 2; } // Since we have a full path in binary/library names we need to be able to override it. else if ( (strcmp(arg, "-dylib_install_name") == 0) || (strcmp(arg, "-dylinker_install_name") == 0) || (strcmp(arg, "-install_name") == 0)) { const char* installName = argv[++i]; if ( installName == nullptr ) throw "-install_name missing "; fDylibInstallName = removeDoubleSlash(installName); if ( fDylibInstallName != installName ) warning("double slash removed from -install_name (%s)", installName); } // Sets the base address of the output. else if ( (strcmp(arg, "-seg1addr") == 0) || (strcmp(arg, "-image_base") == 0) ) { const char* address = argv[++i]; if ( address == NULL ) throwf("%s missing
", arg); fBaseAddress = parseAddress(address); uint64_t temp = ((fBaseAddress+fSegmentAlignment-1) & (-fSegmentAlignment)); if ( fBaseAddress != temp ) { 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 "; ld::File::Ordinal baseOrdinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); loadFileList(path, baseOrdinal); } else if ( strcmp(arg, "-keep_private_externs") == 0 ) { cannotBeUsedWithBitcode(arg); fKeepPrivateExterns = true; } else if ( strcmp(arg, "-final_output") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) throw "-final_output missing "; // some projects end up with double slash in -final_output path fFinalName = removeDoubleSlash(name); } // Ensure that all calls to exported symbols go through lazy pointers. Multi-module // just ensures that this happens for cross object file boundaries. else if ( (strcmp(arg, "-interposable") == 0) || (strcmp(arg, "-multi_module") == 0)) { switch ( fInterposeMode ) { case kInterposeNone: case kInterposeAllExternal: fInterposeMode = kInterposeAllExternal; break; case kInterposeSome: // 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, kAllowWildcards); 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, kAllowWildcards); } 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; loadExportFile(argv[++i], "-unexported_symbols_list", fDontExportSymbols, kAllowWildcards); } else if ( strcmp(arg, "-exported_symbol") == 0 ) { if ( fExportMode == kDontExportSome ) throw "can't use -exported_symbol and -unexported_symbols"; fExportMode = kExportSome; const char* symbol = checkForNullArgument(arg, argv[++i]); fExportSymbols.insert(symbol, kAllowWildcards); } else if ( strcmp(arg, "-unexported_symbol") == 0 ) { if ( fExportMode == kExportSome ) throw "can't use -unexported_symbol and -exported_symbol"; fExportMode = kDontExportSome; const char* symbol = checkForNullArgument(arg, argv[++i]); fDontExportSymbols.insert(symbol, kAllowWildcards); } else if ( strcmp(arg, "-no_exported_symbols") == 0 ) { if ( fExportMode != kExportDefault ) throw "can't use -no_exported_symbols and -[un]exported_symbol[s_list]"; fExportMode = kExportSome; } 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, kAllowWildcards); 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, kAllowWildcards); cannotBeUsedWithBitcode(arg); } // ??? Deprecate else if ( strcmp(arg, "-no_arch_warnings") == 0 ) { fIgnoreOtherArchFiles = true; } else if ( strcmp(arg, "-force_cpusubtype_ALL") == 0 ) { 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 ) { const char* path = checkForNullArgument(arg, argv[++i]); FileInfo info = findFile(path); info.options.fWeakImport = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-lazy_library") == 0 ) { const char* path = checkForNullArgument(arg, argv[++i]); FileInfo info = findFile(path); info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); cannotBeUsedWithBitcode(arg); warning("-lazy_library is deprecated, changing to regular link"); } else if ( strcmp(arg, "-needed_library") == 0 ) { snapshotArgCount = 0; const char* path = checkForNullArgument(arg, argv[++i]); FileInfo info = findFile(path); info.options.fNeeded = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } else if ( strcmp(arg, "-search_in_sparse_frameworks") == 0 ) { fSearchInSparseFrameworks = true; } else if ( strcmp(arg, "-image_suffix") == 0 ) { fImageSuffixes.push_back(checkForNullArgument(arg, argv[++i])); } else if ( strcmp(arg, "-framework") == 0 ) { FileInfo info = findFramework(argv[++i]); info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } else if ( strcmp(arg, "-weak_framework") == 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 ) { FileInfo info = findFramework(argv[++i]); info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); cannotBeUsedWithBitcode(arg); warning("-lazy_framework is deprecated, changing to regular link"); } else if ( strcmp(arg, "-needed_framework") == 0 ) { FileInfo info = findFramework(argv[++i]); info.options.fNeeded = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); } else if ( strcmp(arg, "-search_paths_first") == 0 ) { // previously handled by buildSearchPaths() } else if ( strcmp(arg, "-search_dylibs_first") == 0 ) { // previously handled by buildSearchPaths() } else if ( strcmp(arg, "-undefined") == 0 ) { setUndefinedTreatment(argv[++i]); cannotBeUsedWithBitcode(arg); } // Debugging output flag. else if ( strcmp(arg, "-arch_multiple") == 0 ) { fMessagesPrefixedWithArchitecture = true; } // Specify what to do with relocations in read only // sections like .text. Could be errors, warnings, // or suppressed. Currently we do nothing with the // flag. else if ( strcmp(arg, "-read_only_relocs") == 0 ) { switch ( parseTreatment(argv[++i]) ) { case kNULL: case kInvalid: throw "-read_only_relocs missing [ warning | error | suppress ]"; case kWarning: fWarnTextRelocs = true; fAllowTextRelocs = true; cannotBeUsedWithBitcode(arg); break; case kSuppress: fWarnTextRelocs = false; fAllowTextRelocs = true; cannotBeUsedWithBitcode(arg); break; case kError: fWarnTextRelocs = false; fAllowTextRelocs = false; break; } } else if ( strcmp(arg, "-unaligned_pointers") == 0 ) { switch ( parseTreatment(argv[++i]) ) { case kNULL: case kInvalid: throw "-unaligned_pointers missing [ warning | error | suppress ]"; case kWarning: fUnalignedPointerTreatment = Options::kUnalignedPointerWarning; cannotBeUsedWithBitcode(arg); break; case kSuppress: fUnalignedPointerTreatment = Options::kUnalignedPointerIgnore; cannotBeUsedWithBitcode(arg); break; case kError: fUnalignedPointerTreatment = Options::kUnalignedPointerError; break; } } else if ( strcmp(arg, "-sect_diff_relocs") == 0 ) { warnObsolete(arg); ++i; } // Warn, error or make strong a mismatch between weak // and non-weak references. else if ( strcmp(arg, "-weak_reference_mismatches") == 0 ) { setWeakReferenceMismatchTreatment(argv[++i]); } // For a deployment target of 10.3 and earlier ld64 will // prebind an executable with 0s in all addresses that // are prebound. This can then be fixed up by update_prebinding // later. Prebinding is less useful on 10.4 and greater. else if ( strcmp(arg, "-prebind") == 0 ) { warnObsolete(arg); cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-noprebind") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-prebind_allow_overlap") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-prebind_all_twolevel_modules") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-noprebind_all_twolevel_modules") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-nofixprebinding") == 0 ) { warnObsolete(arg); } // 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 ) { fExecutablePath = argv[++i]; if ( (fExecutablePath == NULL) || (fExecutablePath[0] == '-') ) throw "-executable_path missing "; // if a directory was passed, add / to end // ld64 can't find @executable _path relative dylibs from our umbrella frameworks struct stat statBuffer; if ( stat(fExecutablePath, &statBuffer) == 0 ) { if ( (statBuffer.st_mode & S_IFMT) == S_IFDIR ) { char* pathWithSlash = new char[strlen(fExecutablePath)+2]; strcpy(pathWithSlash, fExecutablePath); strcat(pathWithSlash, "/"); fExecutablePath = pathWithSlash; } } } // Aligns all segments to the power of 2 boundary specified. else if ( strcmp(arg, "-segalign") == 0 ) { const char* size = argv[++i]; if ( size == NULL ) throw "-segalign missing "; fSegmentAlignment = parseAddress(size); uint8_t alignment = (uint8_t)__builtin_ctz(fSegmentAlignment); uint32_t p2aligned = (1 << alignment); if ( p2aligned != fSegmentAlignment ) { warning("alignment for -segalign %s is not a power of two, using 0x%X", size, p2aligned); fSegmentAlignment = p2aligned; } fForceAlignment = true; cannotBeUsedWithBitcode(arg); } // Puts a specified segment at a particular address that must // be a multiple of the segment alignment. else if ( strcmp(arg, "-segaddr") == 0 ) { SegmentStart seg; seg.name = argv[++i]; if ( (seg.name == NULL) || (argv[i+1] == NULL) ) throw "-segaddr missing segName Adddress"; seg.address = parseAddress(argv[++i]); 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 ) { warnObsolete(arg); ++i; cannotBeUsedWithBitcode(arg); } // ??? Deprecate when we deprecate split-seg. else if ( strcmp(arg, "-segs_read_write_addr") == 0 ) { warnObsolete(arg); ++i; cannotBeUsedWithBitcode(arg); } // ??? Deprecate when we get rid of basing at build time. else if ( strcmp(arg, "-seg_addr_table") == 0 ) { warnObsolete(arg); snapshotFileArgIndex = 1; const char* name = argv[++i]; if ( name == NULL ) throw "-seg_addr_table missing argument"; cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-seg_addr_table_filename") == 0 ) { warnObsolete(arg); ++i; } else if ( strcmp(arg, "-segprot") == 0 ) { SegmentProtect seg; seg.name = argv[++i]; if ( (seg.name == NULL) || (argv[i+1] == NULL) || (argv[i+2] == NULL) ) throw "-segprot missing segName max-prot init-prot"; seg.max = parseProtection(argv[++i]); seg.init = parseProtection(argv[++i]); 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]; if ( size == NULL ) throw "-pagezero_size missing "; fZeroPageSize = parseAddress(size); uint64_t temp = fZeroPageSize & (-4096); // page align if ( (fZeroPageSize != temp) ) warning("-pagezero_size not page aligned, rounding down"); fZeroPageSize = temp; cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-implicit_pagezero") == 0 ) { fImplicitPageZero = true; fZeroPageSize = ULLONG_MAX; } 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); } else if ( strcmp(arg, "-allow_stack_execute") == 0 ) { fExecutableStack = true; // Only Intel hardware supports executable stacks. bool supportsExecutableStacks = ((fArchitecture == CPU_TYPE_I386) || (fArchitecture == CPU_TYPE_X86_64)); if (!supportsExecutableStacks) throw "the target architecture doesn't support executable stacks"; 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; } else if ( strcmp(arg, "-sectorder_detail") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-sectobjectsymbols") == 0 ) { warnObsolete(arg); 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); } else if ( strcmp(arg, "-private_bundle") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-twolevel_namespace_hints") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-platform_version") == 0 ) { const char* platformStr = argv[++i]; if (platformStr == nullptr) { throwf("platform and versions must be specified"); } ld::Platform plat = ld::platformFromName(platformStr); if ( plat == ld::Platform::unknown ) throwf("%s passed unknown platform name '%s'", arg, platformStr); const char* versStr = checkForNullVersionArgument(arg, argv[++i]); if ( versStr == NULL ) throwf("%s missing min version argument", arg); uint32_t minVersValue; if ( !parsePackedVersion32(versStr, minVersValue) ) { throwf("%s min version malformed: '%s'", arg, versStr); } const char* SDKVersStr = checkForNullVersionArgument(arg, argv[++i]); if ( SDKVersStr == NULL ) throwf("%s missing sdk version argument", arg); uint32_t SDKValue; if ( !parsePackedVersion32(SDKVersStr, SDKValue) ) { throwf("%s sdk version malformed: '%s'", arg, SDKVersStr); } if ( fPlatforms.contains(plat) ) { std::string existingVersionStr = getVersionString32(fPlatforms.minOS(plat)); warning("passed two min versions (%s, %s) for platform %s. Using %s.", existingVersionStr.c_str(), versStr, nameFromPlatform(plat), versStr); fPlatforms.updateMinVersion(plat, minVersValue); fPlatforms.updateSDKVersion(plat, SDKValue); } else { // free-standing always overrides anything the compiler may have added if ( plat == ld::Platform::freestanding ) fPlatforms.clear(); fPlatforms.insert(ld::PlatformVersion(plat, minVersValue, SDKValue)); } fPlatfromVersionCmdFound = true; } else if ( const ld::PlatformInfo* info = isPlatformOption(arg) ) { const char* versStr = checkForNullVersionArgument(arg, argv[++i]); if ( versStr == NULL ) throwf("%s missing version argument", arg); uint32_t value; if ( !parsePackedVersion32(versStr, value) ) { throwf("%s value malformed: '%s'", arg, versStr); } if ( value < info->minimumOsVersion ) { if ( info->platform == ld::Platform::iOSMac ) { value = info->minimumOsVersion; warning("changing %s minOS version from %s to %s", info->printName, versStr, getVersionString32(info->minimumOsVersion).c_str()); } else { warning("building for %s %s is deprecated", info->printName, versStr); } } if (fPlatforms.contains(info->platform)) { std::string existingVersionStr = getVersionString32(fPlatforms.minOS(info->platform)); warning("passed two min versions (%s, %s) for platform %s. Using %s.", existingVersionStr.c_str(), versStr, info->printName, versStr); fPlatforms.updateMinVersion(info->platform, value); } else { fPlatforms.insert(ld::PlatformVersion(info->platform, value)); } } else if ( strcmp(arg, "-multiply_defined") == 0 ) { //warnObsolete(arg); ++i; } else if ( strcmp(arg, "-multiply_defined_unused") == 0 ) { warnObsolete(arg); ++i; } else if ( strcmp(arg, "-nomultidefs") == 0 ) { warnObsolete(arg); } // Display each file in which the argument symbol appears and whether // the file defines or references it. This option takes an argument // as -y note that there is no space. else if ( strncmp(arg, "-y", 2) == 0 ) { warnObsolete("-y"); } // Same output as -y, but output number of undefined symbols only. else if ( strcmp(arg, "-Y") == 0 ) { //warnObsolete(arg); ++i; } // This option affects all objects linked into the final result. else if ( strcmp(arg, "-m") == 0 ) { warnObsolete(arg); } else if ( (strcmp(arg, "-why_load") == 0) || (strcmp(arg, "-whyload") == 0) ) { fWhyLoad = true; } else if ( strcmp(arg, "-why_live") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) throw "-why_live missing symbol name argument"; fWhyLive.insert(name, kAllowWildcards); } else if ( strcmp(arg, "-u") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) throw "-u missing argument"; fInitialUndefines.push_back(name); } 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); fLocalSymbolHandling = kLocalSymbolsNone; fDebugInfoStripping = Options::kDebugInfoNone; } else if ( strcmp(arg, "-x") == 0 ) { fLocalSymbolHandling = kLocalSymbolsNone; } else if ( strcmp(arg, "-S") == 0 ) { fDebugInfoStripping = Options::kDebugInfoNone; } else if ( strcmp(arg, "-X") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-Si") == 0 ) { warnObsolete(arg); fDebugInfoStripping = Options::kDebugInfoFull; } else if ( strcmp(arg, "-b") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-Sn") == 0 ) { warnObsolete(arg); fDebugInfoStripping = Options::kDebugInfoFull; } else if ( strcmp(arg, "-Sp") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-dead_strip") == 0 ) { fDeadStrip = true; } else if ( strcmp(arg, "-remove_swift_reflection_metadata_sections") == 0 ) { fRemoveSwiftReflectionMetadataSections = true; } else if ( strcmp(arg, "-no_dead_strip_inits_and_terms") == 0 ) { warnObsolete(arg); fDeadStrip = true; } 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; } else if ( strcmp(arg, "-M") == 0 ) { // FIX FIX } else if ( strcmp(arg, "-headerpad") == 0 ) { const char* size = argv[++i]; if ( size == NULL ) throw "-headerpad missing argument"; fMinimumHeaderPad = parseAddress(size); cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-headerpad_max_install_names") == 0 ) { // 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; } else if ( strcmp(arg, "-A") == 0 ) { warnObsolete(arg); ++i; } else if ( strcmp(arg, "-umbrella") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) throw "-umbrella missing argument"; fUmbrellaName = name; cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-allowable_client") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) throw "-allowable_client missing argument"; fAllowableClients.push_back(name); cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-client_name") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) 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; } else if ( strcmp(arg, "-commons") == 0 ) { fCommonsMode = parseCommonsTreatment(argv[++i]); } else if ( strcmp(arg, "-keep_relocs") == 0 ) { fKeepRelocations = true; } else if ( strcmp(arg, "-warn_stabs") == 0 ) { fWarnStabs = true; } else if ( strcmp(arg, "-pause") == 0 ) { fPause = true; } else if ( strcmp(arg, "-print_statistics") == 0 ) { fStatistics = true; } else if ( strcmp(arg, "-d") == 0 ) { fMakeTentativeDefinitionsReal = true; } else if ( strcmp(arg, "-v") == 0 ) { // previously handled by buildSearchPaths() } else if ( strcmp(arg, "-Z") == 0 ) { // 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; } else if ( strcmp(arg, "-setuid_safe") == 0 ) { fSetuidSafe = true; } else if ( strcmp(arg, "-alias") == 0 ) { Options::AliasPair pair; pair.realName = checkForNullArgument(arg, argv[++i]); pair.alias = checkForNullArgument(arg, argv[++i]); fAliases.push_back(pair); cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-alias_list") == 0 ) { snapshotFileArgIndex = 1; parseAliasFile(argv[++i]); 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 = checkForNullArgument(arg, argv[++i]); struct stat statbuf; int ret = ::stat(fReverseMapPath, &statbuf); if ( ret == 0 && 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(arg, "-flto-codegen-only") == 0) { fLTOCodegenOnly = true; } else if ( strcmp(arg, "-ignore_auto_link") == 0) { fIgnoreAutoLink = true; } else if ( strcmp(arg, "-allow_dead_duplicates") == 0) { fAllowDeadDups = true; } else if ( strcmp(arg, "-bitcode_process_mode") == 0 ) { const char* bitcode_type = checkForNullArgument(arg, argv[++i]); 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 = checkForNullArgument(arg, argv[++i]); fRPaths.push_back(path); } else if ( strcmp(arg, "-read_only_stubs") == 0 ) { fReadOnlyx86Stubs = true; } else if ( strcmp(arg, "-slow_stubs") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-map") == 0 ) { fMapPath = checkForNullArgument(arg, argv[++i]); } else if ( strcmp(arg, "-pie") == 0 ) { fPositionIndependentExecutable = true; fPIEOnCommandLine = true; } else if ( strcmp(arg, "-no_pie") == 0 ) { fDisablePositionIndependentExecutable = true; cannotBeUsedWithBitcode(arg); } else if ( strncmp(arg, "-reexport-l", 11) == 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 ) { const char* path = checkForNullArgument(arg, argv[++i]); FileInfo info = findFile(path); 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 ) { 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 ) { const char* path = checkForNullArgument(arg, argv[++i]); FileInfo info = findFile(path); info.options.fUpward = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-upward_framework") == 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; } else if ( strcmp(arg, "-new_linker") == 0 ) { // ignore } else if ( strcmp(arg, "-no_encryption") == 0 ) { 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, "-no_dwarf_unwind") == 0 ) { fRemoveDwarfUnwindSections = true; cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-mllvm") == 0 ) { const char* opts = checkForNullArgument(arg, argv[++i], true); fLLVMOptions.push_back(opts); cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-mcpu") == 0 ) { const char* cpu = checkForNullArgument(arg, argv[++i]); fLtoCpu = cpu; cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_order_inits") == 0 ) { fAutoOrderInitializers = false; cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-seg_page_size") == 0 ) { SegmentSize seg; seg.name = argv[++i]; if ( (seg.name == NULL) || (argv[i+1] == NULL) ) throw "-seg_page_size missing segName Adddress"; seg.size = parseAddress(argv[++i]); uint64_t temp = seg.size & (-4096); // page align 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 ) { warnObsolete("-exported_symbols_order"); ++i; cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_compact_linkedit") == 0 ) { 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 ) { fAllowCpuSubtypeMismatchesForceOn = 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 = checkForNullVersionArgument(arg, argv[++i]); if ( strcmp(version, "2") == 0 ) { fObjCABIVersion1Override = false; fObjCABIVersion2Override = true; } else if ( strcmp(version, "1") == 0 ) { fObjCABIVersion1Override = true; fObjCABIVersion2Override = false; } else warning("ignoring unrecognized argument (%s) to -objc_abi_version", version); } else if ( strcmp(arg, "-warn_unused_dylibs") == 0 ) { fWarnUnusedDylibsForceOn = true; } else if ( strcmp(arg, "-no_warn_unused_dylibs") == 0 ) { fWarnUnusedDylibsForceOff = true; } else if ( strcmp(arg, "-objc_relative_method_lists") == 0 ) { fForceObjCRelativeMethodListsOn = true; } else if ( strcmp(arg, "-no_objc_relative_method_lists") == 0 ) { fForceObjCRelativeMethodListsOff = true; } else if ( strcmp(arg, "-warn_weak_exports") == 0 ) { fWarnWeakExports = true; } else if ( strcmp(arg, "-no_weak_exports") == 0 ) { fNoWeakExports = true; } else if ( strcmp(arg, "-objc_gc_compaction") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-objc_gc") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-objc_gc_only") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-objc_stubs_small") == 0 ) { fObjcSmallStubs = true; } else if ( strcmp(arg, "-objc_stubs_fast") == 0 ) { fObjcSmallStubs = false; } else if ( strcmp(arg, "-no_huge") == 0 ) { // rdar://113231351 (ld64: add -no_huge option to disable the x86 huge pass) fRunHugePass = false; } else if ( strcmp(arg, "-demangle") == 0 ) { fDemangle = true; } else if ( strcmp(arg, "-version_load_command") == 0 ) { fVersionLoadCommandForcedOn = true; fVersionLoadCommandForcedOff = false; } else if ( strcmp(arg, "-function_starts") == 0 ) { fFunctionStartsForcedOn = true; fFunctionStartsForcedOff = false; } else if ( strcmp(arg, "-no_function_starts") == 0 ) { 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 = checkForNullArgument(arg, argv[++i]); } else if ( strcmp(arg, "-no_objc_category_merging") == 0 ) { fObjcCategoryMerging = false; } else if ( strcmp(arg, "-force_symbols_weak_list") == 0 ) { snapshotFileArgIndex = 1; loadExportFile(argv[++i], "-force_symbols_weak_list", fForceWeakSymbols, kAllowWildcards); cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-force_symbols_not_weak_list") == 0 ) { snapshotFileArgIndex = 1; loadExportFile(argv[++i], "-force_symbols_not_weak_list", fForceNotWeakSymbols, kAllowWildcards); 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, kAllowWildcards); 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, kAllowWildcards); 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, kAllowWildcards); } else if ( strcmp(arg, "-dyld_env") == 0 ) { const char* envarg = argv[++i]; if ( envarg == NULL ) throw "-dyld_env missing ENV=VALUE"; 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, "-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(arg, "-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, kAllowWildcards); } 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