X-Git-Url: https://git.saurik.com/apple/ld64.git/blobdiff_plain/dd9e569f384b2c941bcc73d89d1468756788ee5b..82b4b32b1851b4bba2473356c2208bd34561ee02:/src/ld/Options.cpp?ds=sidebyside diff --git a/src/ld/Options.cpp b/src/ld/Options.cpp index 8106e93..d36bc02 100644 --- a/src/ld/Options.cpp +++ b/src/ld/Options.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -119,12 +120,12 @@ bool Options::FileInfo::checkFileExists(const Options& options, const char *p) p = path; if ( stat(p, &statBuffer) == 0 ) { if (p != path) path = strdup(p); - fileLen = statBuffer.st_size; modTime = statBuffer.st_mtime; return true; } if ( options.dumpDependencyInfo() ) options.dumpDependency(Options::depNotFound, p); +// fprintf(stderr, "not found: %s\n", p); return false; } @@ -143,7 +144,7 @@ Options::Options(int argc, const char* argv[]) fClientName(NULL), fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL), fBundleLoader(NULL), fDtraceScriptName(NULL), fSegAddrTablePath(NULL), fMapPath(NULL), - fDyldInstallPath("/usr/lib/dyld"), fTempLtoObjectPath(NULL), fOverridePathlibLTO(NULL), fLtoCpu(NULL), + fDyldInstallPath("/usr/lib/dyld"), fLtoCachePath(NULL), fTempLtoObjectPath(NULL), fOverridePathlibLTO(NULL), fLtoCpu(NULL), fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fSourceVersion(0), fSDKVersion(0), fExecutableStack(false), fNonExecutableHeap(false), fDisableNonExecutableHeap(false), fMinimumHeaderPad(32), fSegmentAlignment(4096), @@ -157,7 +158,7 @@ Options::Options(int argc, const char* argv[]) fUsingLazyDylibLinking(false), fEncryptable(true), fEncryptableForceOn(false), fEncryptableForceOff(false), fOrderData(true), fMarkDeadStrippableDylib(false), fMakeCompressedDyldInfo(true), fMakeCompressedDyldInfoForceOff(false), fNoEHLabels(false), - fAllowCpuSubtypeMismatches(false), fUseSimplifiedDylibReExports(false), + fAllowCpuSubtypeMismatches(false), fEnforceDylibSubtypesMatch(false), fUseSimplifiedDylibReExports(false), fObjCABIVersion2Override(false), fObjCABIVersion1Override(false), fCanUseUpwardDylib(false), fFullyLoadArchives(false), fLoadAllObjcObjectsFromArchives(false), fFlatNamespace(false), fLinkingMainExecutable(false), fForFinalLinkedImage(false), fForStatic(false), @@ -165,7 +166,7 @@ Options::Options(int argc, const char* argv[]) fSetuidSafe(false), fImplicitlyLinkPublicDylibs(true), fAddCompactUnwindEncoding(true), fWarnCompactUnwind(false), fRemoveDwarfUnwindIfCompactExists(false), fAutoOrderInitializers(true), fOptimizeZeroFill(true), fMergeZeroFill(false), fLogObjectFiles(false), - fLogAllFiles(false), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), + fLogAllFiles(false), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), fTraceEmitJSON(false), fOutputSlidable(false), fWarnWeakExports(false), fObjcGcCompaction(false), fObjCGc(false), fObjCGcOnly(false), fDemangle(false), fTLVSupport(false), @@ -185,14 +186,15 @@ Options::Options(int argc, const char* argv[]) fGenerateDtraceDOF(true), fAllowBranchIslands(true), fTraceSymbolLayout(false), fMarkAppExtensionSafe(false), fCheckAppExtensionSafe(false), fForceLoadSwiftLibs(false), fSharedRegionEncodingV2(false), fUseDataConstSegment(false), - fUseDataConstSegmentForceOn(false), fUseDataConstSegmentForceOff(false), + fUseDataConstSegmentForceOn(false), fUseDataConstSegmentForceOff(false), fUseTextExecSegment(false), fBundleBitcode(false), fHideSymbols(false), fVerifyBitcode(false), - fReverseMapUUIDRename(false), fReverseMapPath(NULL), fLTOCodegenOnly(false), - fIgnoreAutoLink(false), fAllowDeadDups(false), fBitcodeKind(kBitcodeProcess), + fReverseMapUUIDRename(false), fDeDupe(true), fVerboseDeDupe(false), + fReverseMapPath(NULL), fLTOCodegenOnly(false), + fIgnoreAutoLink(false), fAllowDeadDups(false), fAllowWeakImports(true), fBitcodeKind(kBitcodeProcess), fPlatform(kPlatformUnknown), fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), - fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), + fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), fWatchOSVersionMin(ld::wOSVersionUnset), fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL), - fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1) + fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1), fTraceFileDescriptor(-1), fMaxDefaultCommonAlign(0) { this->checkForClassic(argc, argv); this->parsePreCommandLineEnvironmentSettings(); @@ -210,8 +212,11 @@ Options::Options(int argc, const char* argv[]) Options::~Options() { - if ( fDependencyFileDescriptor != -1 ) - ::close(fDependencyFileDescriptor); + if ( fDependencyFileDescriptor != -1 ) + ::close(fDependencyFileDescriptor); + + if ( fTraceFileDescriptor != -1 ) + ::close(fTraceFileDescriptor); } bool Options::errorBecauseOfWarnings() const @@ -318,12 +323,15 @@ uint32_t Options::initialSegProtection(const char* segName) const return it->init; } } - if ( strcmp(segName, "__PAGEZERO") == 0 ) { - return 0; + if ( strcmp(segName, "__TEXT") == 0 ) { + return ( fUseTextExecSegment ? VM_PROT_READ : (VM_PROT_READ | VM_PROT_EXECUTE) ); } - else if ( strcmp(segName, "__TEXT") == 0 ) { + else if ( strcmp(segName, "__TEXT_EXEC") == 0 ) { return VM_PROT_READ | VM_PROT_EXECUTE; } + else if ( strcmp(segName, "__PAGEZERO") == 0 ) { + return 0; + } else if ( strcmp(segName, "__LINKEDIT") == 0 ) { return VM_PROT_READ; } @@ -390,6 +398,9 @@ bool Options::hasCustomSectionAlignment(const char* segName, const char* sectNam if ( (strcmp(it->segmentName, segName) == 0) && (strcmp(it->sectionName, sectName) == 0) ) return true; } + if ( fEncryptable && (strcmp(sectName, "__oslogstring") == 0) && (strcmp(segName, "__TEXT") == 0) ) + return true; + return false; } @@ -399,6 +410,9 @@ uint8_t Options::customSectionAlignment(const char* segName, const char* sectNam if ( (strcmp(it->segmentName, segName) == 0) && (strcmp(it->sectionName, sectName) == 0) ) return it->alignment; } + if ( fEncryptable && (strcmp(sectName, "__oslogstring") == 0) && (strcmp(segName, "__TEXT") == 0) ) + return __builtin_ctz(fSegmentAlignment); + return 0; } @@ -603,7 +617,7 @@ void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype, Options::P switch ( type ) { case CPU_TYPE_I386: case CPU_TYPE_X86_64: - if ( (fPlatform == kPlatformOSX) && (fOutputKind != Options::kObjectFile) ) { + if ( (fPlatform == kPlatformOSX) && (fOutputKind != Options::kObjectFile) && (fMacVersionMin == ld::macVersionUnset) ) { #ifdef DEFAULT_MACOSX_MIN_VERSION warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION); setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); @@ -615,7 +629,7 @@ void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype, Options::P break; case CPU_TYPE_ARM: case CPU_TYPE_ARM64: - if ( (fPlatform == kPlatformiOS) && (fOutputKind != Options::kObjectFile) ) { + if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) { #if defined(DEFAULT_IPHONEOS_MIN_VERSION) warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION); setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); @@ -712,9 +726,8 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) co it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; - if ( checkForFile("%s/lib%s.tbd", dir, rootName, result) ) - return result; - if ( checkForFile("%s/lib%s.dylib", dir, rootName, result) ) + auto path = std::string(dir) + "/lib" + rootName + ".dylib"; + if ( findFile(path, {".tbd"}, result) ) return result; } for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); @@ -743,9 +756,8 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) co it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; - if ( lookForDylibs && checkForFile("%s/lib%s.tbd", dir, rootName, result) ) - return result; - if ( lookForDylibs && checkForFile("%s/lib%s.dylib", dir, rootName, result) ) + auto path = std::string(dir) + "/lib" + rootName + ".dylib"; + if ( lookForDylibs && findFile(path, {".tbd"}, result) ) return result; if ( lookForDylibs && checkForFile("%s/lib%s.so", dir, rootName, result) ) return result; @@ -785,15 +797,8 @@ Options::FileInfo Options::findFramework(const char* rootName, const char* suffi possiblePath = std::string(realPath).append(suffix); } FileInfo result; - bool found = result.checkFileExists(*this, (possiblePath + ".tbd").c_str()); - if ( !found ) - found = result.checkFileExists(*this, possiblePath.c_str()); - if ( fTraceDylibSearching ) - printf("[Logging for XBS]%sfound framework: '%s'\n", - (found ? " " : " not "), possiblePath.c_str()); - if ( found ) { + if ( findFile(possiblePath, {".tbd"}, result) ) return result; - } } // try without suffix if ( suffix != NULL ) @@ -802,75 +807,144 @@ Options::FileInfo Options::findFramework(const char* rootName, const char* suffi throwf("framework not found %s", rootName); } -Options::FileInfo Options::findFile(const std::string &path) const +static std::string replace_extension(const std::string &path, const std::string &ext) +{ + auto result = path; + auto lastSlashIdx = result.find_last_of('/'); + auto lastDotIdx = result.find_last_of('.'); + if (lastDotIdx != std::string::npos && lastDotIdx > lastSlashIdx) + result.erase(lastDotIdx, std::string::npos); + if ( ext.size() > 0 && ext[0] == '.' ) + result.append(ext); + else + result.append('.' + ext); + return result; +} + +bool Options::findFile(const std::string &path, const std::vector &tbdExtensions, FileInfo& result) const +{ + FileInfo tbdInfo; + for ( const auto &ext : tbdExtensions ) { + auto newPath = replace_extension(path, ext); + bool found = tbdInfo.checkFileExists(*this, newPath.c_str()); + if ( fTraceDylibSearching ) + printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), newPath.c_str()); + if ( found ) + break; + } + + FileInfo dylibInfo; + { + bool found = dylibInfo.checkFileExists(*this, path.c_str()); + if ( fTraceDylibSearching ) + printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), path.c_str()); + } + + // There is only a text-based stub file or a dynamic library file. + if ( tbdInfo.missing() != dylibInfo.missing() ) { + result = tbdInfo.missing() ? dylibInfo : tbdInfo; + } + // There are both - a text-based stub file and a dynamic library file. + else if ( !tbdInfo.missing() && !dylibInfo.missing() ) { + // Check if we should prefer the text-based stub file (installapi). + if (tapi::LinkerInterfaceFile::shouldPreferTextBasedStubFile(tbdInfo.path)) { + result = tbdInfo; + } + // If the files are still in sync we can use and should use the text-based stub file. + else if (tapi::LinkerInterfaceFile::areEquivalent(tbdInfo.path, dylibInfo.path)) { + result = tbdInfo; + } + // Otherwise issue a warning and fall-back to the dynamic library file. + else { + warning("text-based stub file %s and library file %s are out of sync. Falling back to library file for linking.", tbdInfo.path, dylibInfo.path); + result = dylibInfo; + } + } else { + return false; + } + + return true; +} + +static bool startsWith(const std::string& str, const std::string& prefix) +{ + return (str.compare(0, prefix.length(), prefix) == 0); +} + +static std::string getDirPath(const std::string& path) +{ + std::string::size_type lastSlashPos = path.find_last_of('/'); + if ( lastSlashPos == std::string::npos ) + return "./"; + else + return path.substr(0, lastSlashPos+1); +} + +Options::FileInfo Options::findFile(const std::string &path, const ld::dylib::File* fromDylib) const { FileInfo result; // if absolute path and not a .o file, then use SDK prefix if ( (path[0] == '/') && (strcmp(&path[path.size()-2], ".o") != 0) ) { - auto tbdFile = path; - auto lastSlashIdx = tbdFile.find_last_of('/'); - auto lastDotIdx = tbdFile.find_last_of('.'); - if (lastDotIdx != std::string::npos && lastDotIdx > lastSlashIdx) - tbdFile.erase(lastDotIdx, std::string::npos); - tbdFile.append(".tbd"); - for (const auto* sdkPathDir : fSDKPaths) { - auto possiblePath = std::string(sdkPathDir) + tbdFile; - if ( result.checkFileExists(*this, possiblePath.c_str()) ) - return result; - possiblePath = std::string(sdkPathDir) + path; - if ( result.checkFileExists(*this, possiblePath.c_str()) ) + auto possiblePath = std::string(sdkPathDir) + path; + if ( findFile(possiblePath, {".tbd"}, result) ) return result; } } - // try raw path - { - std::string file = path; - auto lastDotIdx = file.find_last_of('.'); - if (lastDotIdx != std::string::npos) - file.erase(lastDotIdx, std::string::npos); - if ( result.checkFileExists(*this, file.append(".tbd").c_str()) ) - return result; - } - if ( result.checkFileExists(*this, path.c_str()) ) { - return result; - } - - // try @executable_path substitution - if ( (path.find("@executable_path/") == 0) && (fExecutablePath != nullptr) ) { - char newPath[strlen(fExecutablePath) + path.size()]; - strcpy(newPath, fExecutablePath); - char* addPoint = strrchr(newPath,'/'); - if ( addPoint != nullptr ) - strcpy(&addPoint[1], &path[17]); - else - strcpy(newPath, &path[17]); - - std::string file = newPath; - auto lastDotIdx = file.find_last_of('.'); - if (lastDotIdx != std::string::npos) - file.erase(lastDotIdx, std::string::npos); - if ( result.checkFileExists(*this, file.append(".tbd").c_str()) ) { - 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; } - if ( result.checkFileExists(*this, newPath) ) { - return result; + else if ( startsWith(path, "@loader_path/") && (fromDylib != nullptr) ) { + char absPath[PATH_MAX]; + if ( realpath(fromDylib->path(), absPath) != NULL ) { + std::string loaderBasedPath = getDirPath(fromDylib->path()) + &path[13]; + if ( findFile(loaderBasedPath, {".tbd"}, result) ) + return result; + } + } + else if ( startsWith(path, "@rpath/") ) { + // first search any LC_RPATH supplied by dyld that re-exports dylib to be found + if ( fromDylib != nullptr ) { + for (const char* rp : fromDylib->rpaths() ) { + std::string rpath = rp; + // handle dylib that has LC_RPATH = @loader_path/blah + if ( startsWith(rpath, "@loader_path/") ) { + char absPath[PATH_MAX]; + if ( realpath(fromDylib->path(), absPath) != NULL ) + rpath = getDirPath(absPath) + &rpath[13]; + else + rpath = getDirPath(fromDylib->path()) + &rpath[13]; + } + std::string rpathBasedPath = rpath + "/" + &path[6]; + if ( findFile(rpathBasedPath, {".tbd"}, result) ) + return result; + } + } } } + // try raw path + if ( findFile(path, {".tbd"}, result) ) + return result; + // not found throwf("file not found: %s", path.c_str()); } -Options::FileInfo Options::findFileUsingPaths(const std::string &path) const +// search for indirect dylib first using -F and -L paths first +Options::FileInfo Options::findIndirectDylib(const std::string& installName, const ld::dylib::File* fromDylib) const { FileInfo result; - auto lastSlashPos = path.find_last_of('/'); + auto lastSlashPos = installName.find_last_of('/'); auto pos = ( lastSlashPos != std::string::npos ) ? lastSlashPos + 1 : 0; - auto leafName = path.substr(pos); + auto leafName = installName.substr(pos); // Is this in a framework? // /path/Foo.framework/Foo ==> true (Foo) @@ -879,7 +953,7 @@ Options::FileInfo Options::findFileUsingPaths(const std::string &path) const bool isFramework = false; if ( lastSlashPos != std::string::npos ) { auto frameworkDir = std::string("/").append(leafName).append(".framework/"); - if ( path.rfind(frameworkDir) != std::string::npos ) + if ( installName.rfind(frameworkDir) != std::string::npos ) isFramework = true; } @@ -888,14 +962,12 @@ Options::FileInfo Options::findFileUsingPaths(const std::string &path) const // don't need to try variations, just paths. We do need to add the additional bits // onto the framework path though. if ( isFramework ) { - auto endPos = path.rfind(".framework"); - auto beginPos = path.find_last_of('/', endPos); - auto leafPath = path.substr(beginPos); + 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 ( checkForFile("%s.%s", possiblePath.c_str(), "tbd", result) ) - return result; - if ( checkForFile("%s", possiblePath.c_str(), "", result) ) + if ( findFile(possiblePath, {".tbd"}, result) ) return result; } } else { @@ -903,20 +975,19 @@ Options::FileInfo Options::findFileUsingPaths(const std::string &path) const // 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) - && (path.find(".framework/") != 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); - if ( checkForFile("%s/%s", dir, std::string(leafName).append(".tbd").c_str(), result) ) - return result; - if ( checkForFile("%s/%s", dir, leafName.c_str(), result) ) + std::string possiblePath = dir + std::string("/") + leafName; + if ( findFile(possiblePath, {".tbd"}, result) ) return result; } } } // If we didn't find it fall back to findFile. - return findFile(path); + return findFile(installName, fromDylib); } @@ -1395,58 +1466,32 @@ Options::Treatment Options::parseTreatment(const char* treatment) void Options::setMacOSXVersionMin(const char* version) { - if ( version == NULL ) - throw "-macosx_version_min argument missing"; - - if ( (strncmp(version, "10.", 3) == 0) && isdigit(version[3]) ) { - unsigned int minorVersion = 0; - for (int i=3; isdigit(version[i]); ++i) { - minorVersion = minorVersion*10 + (version[i] - '0'); - } - if ( minorVersion > 255 ) { - warning("Mac OS X minor version > 255 in '%s'", version); - minorVersion = 255; - } - fMacVersionMin = (ld::MacVersionMin)(0x000A0000 | (minorVersion << 8)); - fPlatform = kPlatformOSX; - } - else { - warning("unknown option to -macosx_version_min, not 10.x"); + uint32_t value; + if ( !parsePackedVersion32(version, value) ) { + throwf("-macosx_version_min value malformed: '%s'", version); } + fMacVersionMin = (ld::MacVersionMin)value; + fPlatform = kPlatformOSX; } void Options::setIOSVersionMin(const char* version) { - if ( version == NULL ) - throw "-ios_version_min argument missing"; - if ( ! isdigit(version[0]) ) - throw "-ios_version_min argument is not a number"; - if ( version[1] != '.' ) - throw "-ios_version_min argument is missing period as second character"; - if ( ! isdigit(version[2]) ) - throw "-ios_version_min argument is not a number"; - - unsigned int majorVersion = version[0] - '0'; - unsigned int minorVersion = version[2] - '0'; - fIOSVersionMin = (ld::IOSVersionMin)((majorVersion << 16) | (minorVersion << 8)); + uint32_t value; + if ( !parsePackedVersion32(version, value) ) { + throwf("-ios_version_min value malformed: '%s'", version); + } + fIOSVersionMin = (ld::IOSVersionMin)value; fPlatform = kPlatformiOS; } void Options::setWatchOSVersionMin(const char* version) { - if ( version == NULL ) - throw "-watchos_version_min argument missing"; - if ( ! isdigit(version[0]) ) - throw "-watchos_version_min argument is not a number"; - if ( version[1] != '.' ) - throw "-watchos_version_min argument is missing period as second character"; - if ( ! isdigit(version[2]) ) - throw "-watchos_version_min argument is not a number"; - - unsigned int majorVersion = version[0] - '0'; - unsigned int minorVersion = version[2] - '0'; - fWatchOSVersionMin = (ld::WatchOSVersionMin)((majorVersion << 16) | (minorVersion << 8)); + uint32_t value; + if ( !parsePackedVersion32(version, value) ) { + throwf("-watchos_version_min value malformed: '%s'", version); + } + fWatchOSVersionMin = (ld::WatchOSVersionMin)value; fPlatform = kPlatformWatchOS; } @@ -1713,6 +1758,19 @@ void Options::parseOrderFile(const char* path, bool cstring) // order files override auto-ordering fAutoOrderInitializers = false; + // ld64 should prefer OrderFiles from the SDK over the ones in / + for (const char* sdkPath : fSDKPaths) { + char fullPath[PATH_MAX]; + strlcpy(fullPath, sdkPath, PATH_MAX); + strlcat(fullPath, "/", PATH_MAX); + strlcat(fullPath, path, PATH_MAX); + struct stat statBuffer; + if ( stat(fullPath, &statBuffer) == 0 ) { + path = strdup(fullPath); + break; + } + } + // read in whole file int fd = ::open(path, O_RDONLY, 0); if ( fd == -1 ) @@ -1779,6 +1837,12 @@ void Options::parseOrderFile(const char* path, bool cstring) else symbolStart = NULL; } + else if ( strncmp(symbolStart, "arm64:", 6) == 0 ) { + if ( fArchitecture == CPU_TYPE_ARM64 ) + symbolStart = &symbolStart[6]; + else + symbolStart = NULL; + } if ( symbolStart != NULL ) { char* objFileName = NULL; char* colon = strstr(symbolStart, ".o:"); @@ -2021,6 +2085,73 @@ std::string Options::getVersionString64(uint64_t ver) const return versionString.str(); } +// Convert X.Y[.Z] to 32-bit value xxxxyyzz +bool Options::parsePackedVersion32(const std::string& versionStr, uint32_t &result) +{ + result = 0; + + if ( versionStr.empty() ) + return false; + + size_t pos = versionStr.find('.'); + if ( pos == std::string::npos ) + return false; + + std::string majorStr = versionStr.substr(0, pos); + std::string rest = versionStr.substr(pos+1); + + try { + size_t majorEnd; + int majorValue = std::stoi(majorStr, &majorEnd); + if ( majorEnd != majorStr.size() ) + return false; + if ( majorValue < 0 ) + return false; + if ( majorValue > 65535 ) + return false; + + std::string minorStr; + std::string microStr; + pos = rest.find('.'); + if ( pos == std::string::npos ) { + minorStr = rest; + } + else { + minorStr = rest.substr(0, pos); + microStr = rest.substr(pos+1); + } + + size_t minorEnd; + int minorValue = std::stoi(minorStr, &minorEnd); + if ( minorEnd != minorStr.size() ) + return false; + if ( minorValue < 0 ) + return false; + if ( minorValue > 255 ) + return false; + + int microValue = 0; + if ( !microStr.empty() ) { + size_t microEnd; + microValue = std::stoi(microStr, µEnd); + if ( microEnd != microStr.size() ) + return false; + if ( microValue < 0 ) + return false; + if ( microValue > 255 ) + return false; + } + + result = (majorValue << 16) | (minorValue << 8) | microValue; + + return true; + } + catch (...) { + // std::stoi() throws exception on malformed input + return false; + } +} + std::string Options::getSDKVersionStr() const { return getVersionString32(fSDKVersion); @@ -2281,6 +2412,40 @@ void Options::parse(int argc, const char* argv[]) if ( fOverridePathlibLTO == NULL ) throw "missing argument to -lto_library"; } + else if ( strcmp(arg, "-cache_path_lto") == 0 ) { + fLtoCachePath = argv[++i]; + if ( fLtoCachePath == NULL ) + throw "missing argument to -cache_path_lto"; + } + else if ( strcmp(arg, "-prune_interval_lto") == 0 ) { + const char* value = argv[++i]; + if ( value == NULL ) + throw "missing argument to -prune_interval_lto"; + char* endptr; + fLtoPruneInterval = strtoul(value, &endptr, 10); + if ( *endptr != '\0') + throw "invalid argument for -prune_interval_lto"; + } + else if ( strcmp(arg, "-prune_after_lto") == 0 ) { + const char* value = argv[++i]; + if ( value == NULL ) + throw "missing argument to -prune_after_lto"; + char* endptr; + fLtoPruneAfter = strtoul(value, &endptr, 10); + if ( *endptr != '\0') + throw "invalid argument for -prune_after_lto"; + } + else if ( strcmp(arg, "-max_relative_cache_size_lto") == 0 ) { + const char* value = argv[++i]; + if ( value == NULL ) + throw "missing argument to -max_relative_cache_size_lto"; + char* endptr; + fLtoMaxCacheSize = strtoul(value, &endptr, 10); + if ( *endptr != '\0') + throw "invalid argument for -max_relative_cache_size_lto"; + if (fLtoMaxCacheSize > 100) + throw "Expect a value between 0 and 100 for -max_relative_cache_size_lto"; + } else if ( (arg[1] == 'l') && (strncmp(arg,"-lazy_",6) !=0) ) { snapshotArgCount = 0; FileInfo info = findLibrary(&arg[2]); @@ -2360,7 +2525,6 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-order_file") == 0 ) { snapshotFileArgIndex = 1; parseOrderFile(argv[++i], false); - cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-order_file_statistics") == 0 ) { fPrintOrderFileStatistics = true; @@ -2452,7 +2616,6 @@ void Options::parse(int argc, const char* argv[]) throw "can't use -unexported_symbols_list and -exported_symbols_list"; fExportMode = kDontExportSome; loadExportFile(argv[++i], "-unexported_symbols_list", fDontExportSymbols); - cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-exported_symbol") == 0 ) { if ( fExportMode == kDontExportSome ) @@ -2465,7 +2628,6 @@ void Options::parse(int argc, const char* argv[]) throw "can't use -unexported_symbol and -exported_symbol"; fExportMode = kDontExportSome; fDontExportSymbols.insert(argv[++i]); - cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-non_global_symbols_no_strip_list") == 0 ) { snapshotFileArgIndex = 1; @@ -2692,7 +2854,10 @@ void Options::parse(int argc, const char* argv[]) throw "-segprot missing segName max-prot init-prot"; seg.max = parseProtection(argv[++i]); seg.init = parseProtection(argv[++i]); - fCustomSegmentProtections.push_back(seg); + if ( strcmp(seg.name, "__LINKEDIT") == 0 ) + warning("-segprot cannot be used to modify __LINKEDIT protections"); + else + fCustomSegmentProtections.push_back(seg); cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-pagezero_size") == 0 ) { @@ -2718,9 +2883,6 @@ void Options::parse(int argc, const char* argv[]) if ( size == NULL ) throw "-stack_size missing
"; fStackSize = parseAddress(size); - uint64_t temp = fStackSize & (-4096); // page align - if ( (fStackSize != temp) ) - warning("-stack_size not page aligned, rounding down"); } else if ( strcmp(arg, "-allow_stack_execute") == 0 ) { fExecutableStack = true; @@ -2763,6 +2925,8 @@ void Options::parse(int argc, const char* argv[]) // Use this flag to set default behavior for deployement targets. else if ( strcmp(arg, "-macosx_version_min") == 0 ) { const char* macVers = argv[++i]; + if ( macVers == NULL ) + throw "-macosx_version_min missing version argument"; const char* envMacVers = getenv("MACOSX_DEPLOYMENT_TARGET"); const char* enviPhoneVers = getenv("IPHONEOS_DEPLOYMENT_TARGET"); if ( (envMacVers != NULL) && (enviPhoneVers != NULL) ) { @@ -2785,26 +2949,44 @@ void Options::parse(int argc, const char* argv[]) } } else if ( (strcmp(arg, "-ios_version_min") == 0) || (strcmp(arg, "-iphoneos_version_min") == 0) ) { - setIOSVersionMin(argv[++i]); + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-ios_version_min missing version argument"; + setIOSVersionMin(vers); } else if ( strcmp(arg, "-ios_simulator_version_min") == 0 ) { - setIOSVersionMin(argv[++i]); + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-ios_simulator_version_min missing version argument"; + setIOSVersionMin(vers); fTargetIOSSimulator = true; } else if ( strcmp(arg, "-watchos_version_min") == 0 ) { - setWatchOSVersionMin(argv[++i]); + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-watchos_version_min missing version argument"; + setWatchOSVersionMin(vers); } else if ( strcmp(arg, "-watchos_simulator_version_min") == 0 ) { - setWatchOSVersionMin(argv[++i]); + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-watchos_simulator_version_min missing version argument"; + setWatchOSVersionMin(vers); fTargetIOSSimulator = true; } #if SUPPORT_APPLE_TV else if ( strcmp(arg, "-tvos_version_min") == 0 ) { - setIOSVersionMin(argv[++i]); + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-tvos_version_min missing version argument"; + setIOSVersionMin(vers); fPlatform = kPlatform_tvOS; } else if ( strcmp(arg, "-tvos_simulator_version_min") == 0 ) { - setIOSVersionMin(argv[++i]); + const char* vers = argv[++i]; + if ( vers == NULL ) + throw "-tvos_simulator_version_min missing version argument"; + setIOSVersionMin(vers); fPlatform = kPlatform_tvOS; fTargetIOSSimulator = true; } @@ -3061,13 +3243,9 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-bitcode_hide_symbols") == 0 ) { fHideSymbols = true; - if ( !fBundleBitcode ) - warning("-bitcode_hide_symbols is ignored without -bitcode_bundle"); } else if ( strcmp(arg, "-bitcode_verify") == 0 ) { fVerifyBitcode = true; - if ( !fBundleBitcode ) - warning("-bitcode_verify is ignored without -bitcode_bundle"); } else if ( strcmp(arg, "-bitcode_symbol_map") == 0) { fReverseMapPath = argv[++i]; @@ -3628,6 +3806,45 @@ void Options::parse(int argc, const char* argv[]) fUseDataConstSegmentForceOff = true; cannotBeUsedWithBitcode(arg); } + else if ( strcmp(arg, "-text_exec") == 0 ) { + fUseTextExecSegment = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-add_split_seg_info") == 0) { + fSharedRegionEligible = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-no_deduplicate") == 0 ) { + fDeDupe = false; + } + else if ( strcmp(arg, "-verbose_deduplicate") == 0 ) { + fVerboseDeDupe = true; + } + else if ( strcmp(arg, "-max_default_common_align") == 0 ) { + const char* alignStr = argv[++i]; + if ( alignStr == NULL ) + throw "-max_default_common_align missing "; + // argument is a hexadecimal number + char* endptr; + unsigned long value = strtoul(alignStr, &endptr, 16); + if ( *endptr != '\0') + throw "argument for -max_default_common_align is not a hexadecimal number"; + if ( value > 0x8000 ) + throw "argument for -max_default_common_align must be less than or equal to 0x8000"; + if ( value == 0 ) { + warning("zero is not a valid -max_default_common_align"); + value = 1; + } + // alignment is power of 2 + uint8_t alignment = (uint8_t)__builtin_ctz(value); + if ( (unsigned long)(1 << alignment) != value ) { + warning("alignment for -max_default_common_align is not a power of two, using 0x%X", 1 << alignment); + } + fMaxDefaultCommonAlign = alignment; + } + else if ( strcmp(argv[i], "-no_weak_imports") == 0 ) { + fAllowWeakImports = false; + } // put this last so that it does not interfer with other options starting with 'i' else if ( strncmp(arg, "-i", 2) == 0 ) { const char* colon = strchr(arg, ':'); @@ -3702,16 +3919,7 @@ void Options::buildSearchPaths(int argc, const char* argv[]) } if ( libSearchDir[0] == '\0' ) throw "-L must be immediately followed by a directory path (no space)"; - struct stat statbuf; - if ( stat(libSearchDir, &statbuf) == 0 ) { - if ( statbuf.st_mode & S_IFDIR ) - libraryPaths.push_back(libSearchDir); - else - warning("path '%s' following -L not a directory", libSearchDir); - } - else { - warning("directory not found for option '-L%s'", libSearchDir); - } + libraryPaths.push_back(libSearchDir); } else if ( (argv[i][0] == '-') && (argv[i][1] == 'F') ) { const char* frameworkSearchDir = &argv[i][2]; @@ -3725,16 +3933,7 @@ void Options::buildSearchPaths(int argc, const char* argv[]) } if ( frameworkSearchDir[0] == '\0' ) throw "-F must be immediately followed by a directory path (no space)"; - struct stat statbuf; - if ( stat(frameworkSearchDir, &statbuf) == 0 ) { - if ( statbuf.st_mode & S_IFDIR ) - frameworkPaths.push_back(frameworkSearchDir); - else - warning("path '%s' following -F not a directory", frameworkSearchDir); - } - else { - warning("directory not found for option '-F%s'", frameworkSearchDir); - } + frameworkPaths.push_back(frameworkSearchDir); } else if ( strcmp(argv[i], "-Z") == 0 ) addStandardLibraryDirectories = false; @@ -3748,6 +3947,7 @@ void Options::buildSearchPaths(int argc, const char* argv[]) const char* ltoVers = lto::version(); if ( ltoVers != NULL ) fprintf(stderr, "LTO support using: %s\n", ltoVers); + fprintf(stderr, "TAPI support using: %s\n", tapi::Version::getFullVersionAsString().c_str()); exit(0); } } @@ -3821,8 +4021,13 @@ void Options::buildSearchPaths(int argc, const char* argv[]) strcat(newPath, libDir); struct stat statBuffer; if ( stat(newPath, &statBuffer) == 0 ) { - fLibrarySearchPaths.push_back(strdup(newPath)); - sdkOverride = true; + if ( (statBuffer.st_mode & S_IFDIR) == 0 ) { + warning("-syslibroot and -L combined path '%s' is not a directory", newPath); + } + else { + fLibrarySearchPaths.push_back(strdup(newPath)); + sdkOverride = true; + } } } } @@ -3832,11 +4037,21 @@ void Options::buildSearchPaths(int argc, const char* argv[]) // if one SDK is specified and a standard library path is not in the SDK, don't use it } else { - fLibrarySearchPaths.push_back(libDir); + struct stat statBuffer; + if ( stat(libDir, &statBuffer) == 0 ) { + if ( (statBuffer.st_mode & S_IFDIR) == 0 ) + warning("-L path '%s' is not a directory", libDir); + else + fLibrarySearchPaths.push_back(libDir); + } + else if ( !addStandardLibraryDirectories || (strcmp(libDir, "/usr/local/lib") != 0) ) { + warning("directory not found for option '-L%s'", libDir); + } } } } + // now merge sdk and framework paths to make real search paths fFrameworkSearchPaths.reserve(frameworkPaths.size()*(fSDKPaths.size()+1)); int frameIndex = 0; @@ -3860,8 +4075,13 @@ void Options::buildSearchPaths(int argc, const char* argv[]) strcat(newPath, frameworkDir); struct stat statBuffer; if ( stat(newPath, &statBuffer) == 0 ) { - fFrameworkSearchPaths.push_back(strdup(newPath)); - sdkOverride = true; + if ( (statBuffer.st_mode & S_IFDIR) == 0 ) { + warning("-syslibroot and -F combined path '%s' is not a directory", newPath); + } + else { + fFrameworkSearchPaths.push_back(strdup(newPath)); + sdkOverride = true; + } } } } @@ -3871,7 +4091,16 @@ void Options::buildSearchPaths(int argc, const char* argv[]) // if one SDK is specified and a standard library path is not in the SDK, don't use it } else { - fFrameworkSearchPaths.push_back(frameworkDir); + struct stat statBuffer; + if ( stat(frameworkDir, &statBuffer) == 0 ) { + if ( (statBuffer.st_mode & S_IFDIR) == 0 ) + warning("-F path '%s' is not a directory", frameworkDir); + else + fFrameworkSearchPaths.push_back(frameworkDir); + } + else if ( !addStandardLibraryDirectories || (strcmp(frameworkDir, "/Library/Frameworks/") != 0) ) { + warning("directory not found for option '-F%s'", frameworkDir); + } } } } @@ -3902,6 +4131,11 @@ void Options::parsePreCommandLineEnvironmentSettings() fTraceDylibs = true; fTraceIndirectDylibs = true; } + + if ((getenv("LD_TRACE_DEPENDENTS") != NULL)) { + + fTraceEmitJSON = true; + } if (getenv("RC_TRACE_DYLIB_SEARCHING") != NULL) { fTraceDylibSearching = true; @@ -3910,7 +4144,7 @@ void Options::parsePreCommandLineEnvironmentSettings() if (getenv("LD_PRINT_OPTIONS") != NULL) fPrintOptions = true; - if (fTraceDylibs || fTraceArchives) + if (fTraceDylibs || fTraceArchives || fTraceEmitJSON) fTraceOutputFile = getenv("LD_TRACE_FILE"); if (getenv("LD_PRINT_ORDER_FILE_STATISTICS") != NULL) @@ -3933,6 +4167,9 @@ void Options::parsePreCommandLineEnvironmentSettings() if (getenv("LD_ALLOW_CPU_SUBTYPE_MISMATCHES") != NULL) fAllowCpuSubtypeMismatches = true; + if (getenv("LD_DYLIB_CPU_SUBTYPES_MUST_MATCH") != NULL) + fEnforceDylibSubtypesMatch = true; + sWarningsSideFilePath = getenv("LD_WARN_FILE"); const char* customDyldPath = getenv("LD_DYLD_PATH"); @@ -4332,7 +4569,7 @@ void Options::reconfigureDefaults() // determine if info for shared region should be added if ( fOutputKind == Options::kDynamicLibrary ) { - if ( minOS(ld::mac10_5, ld::iOS_3_1) ) + if ( minOS(ld::mac10_5, ld::iOS_3_1) && !fTargetIOSSimulator ) if ( !fPrebind && !fSharedRegionEligibleForceOff ) if ( (strncmp(this->installPath(), "/usr/lib/", 9) == 0) || (strncmp(this->installPath(), "/System/Library/", 16) == 0) ) @@ -4354,6 +4591,12 @@ void Options::reconfigureDefaults() if ( fUseDataConstSegmentForceOn ) { fUseDataConstSegment = true; } + // A -kext for iOS 10 ==> -data_const, -text_exec, -add_split_seg_info + if ( (fOutputKind == Options::kKextBundle) && minOS(ld::mac10_Future, ld::iOS_10_0) && (fArchitecture == CPU_TYPE_ARM64) ) { + fUseDataConstSegment = true; + fUseTextExecSegment = true; + fSharedRegionEligible = true; + } if ( fUseDataConstSegment ) { addSectionRename("__DATA", "__got", "__DATA_CONST", "__got"); addSectionRename("__DATA", "__la_symbol_ptr", "__DATA_CONST", "__la_symbol_ptr"); @@ -4370,10 +4613,17 @@ void Options::reconfigureDefaults() addSectionRename("__DATA", "__objc_imageinfo", "__DATA_CONST", "__objc_imageinfo"); addSectionRename("__DATA", "__objc_const", "__DATA_CONST", "__objc_const"); } + if ( fUseTextExecSegment ) { + addSectionRename("__TEXT", "__text", "__TEXT_EXEC", "__text"); + addSectionRename("__TEXT", "__stubs", "__TEXT_EXEC", "__stubs"); + } // Use V2 shared cache info when targetting newer OSs - if ( fSharedRegionEligible && minOS(ld::mac10_Future, ld::iOS_9_0)) { + if ( fSharedRegionEligible && minOS(ld::mac10_12, ld::iOS_9_0)) { fSharedRegionEncodingV2 = true; + // only use v2 for Swift dylibs on Mac OS X + if ( (fPlatform == kPlatformOSX) && (strncmp(this->installPath(), "/System/Library/PrivateFrameworks/Swift/", 40) != 0) ) + fSharedRegionEncodingV2 = false; fIgnoreOptimizationHints = true; } @@ -4465,7 +4715,8 @@ void Options::reconfigureDefaults() fEncryptable = false; break; } - if ( (fArchitecture != CPU_TYPE_ARM) && (fArchitecture != CPU_TYPE_ARM64) ) + if ( (fArchitecture != CPU_TYPE_ARM) && (fArchitecture != CPU_TYPE_ARM64) + ) fEncryptable = false; if ( fEncryptableForceOn ) fEncryptable = true; @@ -4522,11 +4773,13 @@ void Options::reconfigureDefaults() // only ARM and x86_64 enforces that cpu-sub-types must match switch ( fArchitecture ) { case CPU_TYPE_ARM: + break; case CPU_TYPE_X86_64: + fEnforceDylibSubtypesMatch = false; break; case CPU_TYPE_I386: case CPU_TYPE_ARM64: - fAllowCpuSubtypeMismatches = true; + fEnforceDylibSubtypesMatch = false; break; } @@ -4565,7 +4818,7 @@ void Options::reconfigureDefaults() fCanUseUpwardDylib = true; // MacOSX 10.7 defaults to PIE - if ( ((fArchitecture == CPU_TYPE_X86_64) || (fArchitecture == CPU_TYPE_I386)) + if ( (fArchitecture == CPU_TYPE_I386) && (fOutputKind == kDynamicExecutable) && (fMacVersionMin >= ld::mac10_7) ) { fPositionIndependentExecutable = true; @@ -4579,6 +4832,10 @@ void Options::reconfigureDefaults() fPositionIndependentExecutable = true; } + // x86_64 defaults PIE (regardless of minOS version) + if ( (fArchitecture == CPU_TYPE_X86_64) && (fOutputKind == kDynamicExecutable) && (fMacVersionMin >= ld::mac10_6) ) + fPositionIndependentExecutable = true; + // Simulator defaults to PIE if ( fTargetIOSSimulator && (fOutputKind == kDynamicExecutable) ) fPositionIndependentExecutable = true; @@ -4588,8 +4845,12 @@ void Options::reconfigureDefaults() fPositionIndependentExecutable = false; // arm64 is always PIE - if ( (fArchitecture == CPU_TYPE_ARM64) && (fOutputKind == kDynamicExecutable) ) { + if ( ((fArchitecture == CPU_TYPE_ARM64) + ) + && (fOutputKind == kDynamicExecutable) ) { fPositionIndependentExecutable = true; + if ( fDisablePositionIndependentExecutable ) + warning("-no_pie ignored for arm64"); } // set fOutputSlidable @@ -4616,12 +4877,20 @@ void Options::reconfigureDefaults() if ( fMacVersionMin >= ld::mac10_7 ) { fTLVSupport = true; } - else if ( (fArchitecture == CPU_TYPE_ARM64) && min_iOS(ld::iOS_8_0) ) { + else if ( ((fArchitecture == CPU_TYPE_ARM64) + ) + && min_iOS(ld::iOS_8_0) ) { fTLVSupport = true; } else if ( (fArchitecture == CPU_TYPE_ARM) && min_iOS(ld::iOS_9_0) ) { fTLVSupport = true; } + else if ( fTargetIOSSimulator && (fArchitecture == CPU_TYPE_X86_64) && min_iOS(ld::iOS_8_0) ) { + fTLVSupport = true; + } + else if ( fTargetIOSSimulator && (fArchitecture == CPU_TYPE_I386) && min_iOS(ld::iOS_9_0) ) { + fTLVSupport = true; + } // default to adding version load command for dynamic code, static code must opt-in switch ( fOutputKind ) { @@ -4801,7 +5070,8 @@ void Options::reconfigureDefaults() case Options::kDynamicLibrary: case Options::kDynamicBundle: case Options::kDyld: - if ( (fArchitecture == CPU_TYPE_ARM64) + if ( ((fArchitecture == CPU_TYPE_ARM64) + ) || ((fArchitecture == CPU_TYPE_ARM) && min_iOS(ld::iOS_7_0)) ) { fSegmentAlignment = 4096*4; } @@ -4809,7 +5079,9 @@ void Options::reconfigureDefaults() case Options::kStaticExecutable: case Options::kKextBundle: // 16KB segments for arm64 kexts - if ( (fArchitecture == CPU_TYPE_ARM64) && min_iOS(ld::iOS_9_0) ) { + if ( ((fArchitecture == CPU_TYPE_ARM64) + ) + && min_iOS(ld::iOS_9_0) ) { fSegmentAlignment = 4096*4; } break; @@ -4870,6 +5142,13 @@ void Options::reconfigureDefaults() } } + // Reduce the default alignment of structures/arrays to save memory in embedded systems + if ( fMaxDefaultCommonAlign == 0 ) { + if ( fOutputKind == Options::kPreload ) + fMaxDefaultCommonAlign = 8; + else + fMaxDefaultCommonAlign = 15; + } } void Options::checkIllegalOptionCombinations() @@ -4877,9 +5156,24 @@ void Options::checkIllegalOptionCombinations() // check -undefined setting switch ( fUndefinedTreatment ) { case kUndefinedError: - case kUndefinedDynamicLookup: // always legal break; + case kUndefinedDynamicLookup: + switch (fPlatform) { + case kPlatformOSX: + break; + case kPlatformiOS: + case kPlatformWatchOS: + #if SUPPORT_APPLE_TV + case kPlatform_tvOS: + #endif + if ( fOutputKind != kKextBundle ) + warning("-undefined dynamic_lookup is deprecated on %s", platformName(fPlatform)); + break; + default: + break; + } + break; case kUndefinedWarning: case kUndefinedSuppress: // requires flat namespace @@ -4897,7 +5191,11 @@ void Options::checkIllegalOptionCombinations() const char* lastSlash = strrchr(info.path, '/'); if ( lastSlash == NULL ) lastSlash = info.path - 1; - if ( strcmp(&lastSlash[1], subUmbrella) == 0 ) { + std::string path(&lastSlash[1]); + auto idx = path.find(".tbd", path.size() - 4); + if (idx != std::string::npos) + path.erase(idx); + if ( path == subUmbrella ) { info.options.fReExport = true; found = true; fLinkSnapshot.recordSubUmbrella(info.path); @@ -4932,8 +5230,23 @@ void Options::checkIllegalOptionCombinations() } // sync reader options - if ( fNameSpace != kTwoLevelNameSpace ) + if ( fNameSpace != kTwoLevelNameSpace ) { fFlatNamespace = true; + switch (fPlatform) { + case kPlatformOSX: + break; + case kPlatformiOS: + case kPlatformWatchOS: + #if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + #endif + warning("-flat_namespace is deprecated on %s", platformName(fPlatform)); + break; + default: + break; + } + } + // check -stack_addr if ( fStackAddr != 0 ) { @@ -4957,37 +5270,54 @@ void Options::checkIllegalOptionCombinations() if ( fStackSize != 0 ) { switch (fArchitecture) { case CPU_TYPE_I386: - if ( fStackSize > 0xFFFFFFFF ) - throw "-stack_size must be < 4G for 32-bit processes"; - if ( fStackAddr == 0 ) { - fStackAddr = 0xC0000000; + if ( fPlatform == kPlatformOSX ) { + if ( fStackSize > 0xFFFFFFFF ) + throw "-stack_size must be < 4GB for 32-bit processes"; + if ( fStackAddr == 0 ) + fStackAddr = 0xC0000000; + if ( (fStackAddr > 0xB0000000) && ((fStackAddr-fStackSize) < 0xB0000000) ) + warning("custom stack placement overlaps and will disable shared region"); + } + else { + if ( fStackSize > 0x1F000000 ) + throw "-stack_size must be < 496MB"; + if ( fStackAddr == 0 ) + fStackAddr = 0xC0000000; } - if ( (fStackAddr > 0xB0000000) && ((fStackAddr-fStackSize) < 0xB0000000) ) - warning("custom stack placement overlaps and will disable shared region"); break; case CPU_TYPE_ARM: - if ( fStackSize > 0x2F000000 ) - throw "-stack_size must be < 752MB"; + if ( fStackSize > 0x1F000000 ) + throw "-stack_size must be < 496MB"; if ( fStackAddr == 0 ) - fStackAddr = 0x2F000000; - if ( fStackAddr > 0x30000000) - throw "-stack_addr must be < 0x30000000 for arm"; + fStackAddr = 0x1F000000; + if ( fStackAddr > 0x20000000) + throw "-stack_addr must be < 0x20000000 for arm"; break; case CPU_TYPE_X86_64: - if ( fStackAddr == 0 ) { - fStackAddr = 0x00007FFF5C000000LL; + if ( fPlatform == kPlatformOSX ) { + if ( fStackSize > 0x10000000000 ) + throw "-stack_size must be <= 1TB"; + if ( fStackAddr == 0 ) { + fStackAddr = 0x00007FFF5C000000LL; + } + } + else { + if ( fStackSize > 0x20000000 ) + throw "-stack_size must be <= 512MB"; + if ( fStackAddr == 0 ) { + fStackAddr = 0x120000000; } break; case CPU_TYPE_ARM64: if ( fStackSize > 0x20000000 ) - throw "-stack_size must be < 512MB"; - if ( fStackAddr == 0 ) { + throw "-stack_size must be <= 512MB"; + if ( fStackAddr == 0 ) fStackAddr = 0x120000000; } break; } - if ( (fStackSize & -4096) != fStackSize ) - throw "-stack_size must be multiples of 4K"; + if ( (fStackSize & (-fSegmentAlignment)) != fStackSize ) + throwf("-stack_size must be multiple of segment alignment (%lldKB)", fSegmentAlignment/1024); switch ( fOutputKind ) { case Options::kDynamicExecutable: case Options::kStaticExecutable: @@ -5187,7 +5517,7 @@ void Options::checkIllegalOptionCombinations() // zero page size not specified on command line, set default switch (fArchitecture) { case CPU_TYPE_I386: - case CPU_TYPE_ARM: + case CPU_TYPE_ARM: // first 4KB for 32-bit architectures fZeroPageSize = 0x1000; break; @@ -5310,13 +5640,19 @@ void Options::checkIllegalOptionCombinations() if ( (fOutputKind != Options::kDynamicExecutable) && (fDyldEnvironExtras.size() != 0) ) throw "-dyld_env can only used used when created main executables"; - // -segment_order can only be used with -preload - if ( !fSegmentOrder.empty() && (fOutputKind != Options::kPreload) ) + // -segment_order can only be used with -preload or -static + if ( !fSegmentOrder.empty() && ((fOutputKind != Options::kPreload) && (fOutputKind != kStaticExecutable)) ) throw "-segment_order can only used used with -preload output"; - if ( fBitcodeKind != kBitcodeProcess && - fOutputKind != Options::kObjectFile ) { - throw "-bitcode_process_mode can only be used together with -r"; + // warn about bitcode option combinations + if ( !fBundleBitcode ) { + if ( fVerifyBitcode ) + warning("-bitcode_verify is ignored without -bitcode_bundle"); + else if ( fHideSymbols ) + warning("-bitcode_hide_symbols is ignored without -bitcode_bundle"); + } + if ( fReverseMapPath != NULL && !fHideSymbols ) { + throw "-bitcode_symbol_map can only be used with -bitcode_hide_symbols"; } // auto fix up the process type for strip -S. // when there is only one input and output type is object file, downgrade kBitcodeProcess to kBitcodeAsData. @@ -5324,11 +5660,17 @@ void Options::checkIllegalOptionCombinations() fBitcodeKind = Options::kBitcodeAsData; // warn if building an embedded iOS dylib for pre-iOS 8 - // How can we suppress "ld: warning: embedded dylibs/frameworks only run on iOS 8 or laterÓ when building XCTest? + // How can we suppress "ld: warning: embedded dylibs/frameworks only run on iOS 8 or later" when building XCTest? if ( (fOutputKind == Options::kDynamicLibrary) && (fIOSVersionMin != ld::iOSVersionUnset) && (fDylibInstallName != NULL) ) { if ( !min_iOS(ld::iOS_8_0) && (fDylibInstallName[0] == '@') && !fEncryptableForceOff ) warning("embedded dylibs/frameworks only run on iOS 8 or later"); } + + + // produce nicer error when no input + if ( fInputFiles.empty() ) { + throw "no object files specified"; + } } @@ -5532,4 +5874,28 @@ void Options::dumpDependency(uint8_t opcode, const char* path) const } +void Options::writeToTraceFile(const char* buffer, size_t len) const +{ + // one time open() of custom LD_TRACE_FILE + if ( fTraceFileDescriptor == -1 ) { + if ( fTraceOutputFile != NULL ) { + fTraceFileDescriptor = open(fTraceOutputFile, O_WRONLY | O_APPEND | O_CREAT, 0666); + if ( fTraceFileDescriptor == -1 ) + throwf("Could not open or create trace file (errno=%d): %s", errno, fTraceOutputFile); + } + else { + fTraceFileDescriptor = fileno(stderr); + } + } + + while (len > 0) { + ssize_t amountWritten = write(fTraceFileDescriptor, buffer, len); + if ( amountWritten == -1 ) + /* Failure to write shouldn't fail the build. */ + return; + buffer += amountWritten; + len -= amountWritten; + } +} +