X-Git-Url: https://git.saurik.com/apple/ld64.git/blobdiff_plain/ba348e2165668ae0f4af8b349fc4a6d0910950ed..HEAD:/src/ld/Resolver.cpp diff --git a/src/ld/Resolver.cpp b/src/ld/Resolver.cpp index 8a72b67..17e00cb 100644 --- a/src/ld/Resolver.cpp +++ b/src/ld/Resolver.cpp @@ -51,13 +51,17 @@ #include #include "Options.h" - #include "ld.hpp" +#include "Bitcode.hpp" #include "InputFiles.h" #include "SymbolTable.h" #include "Resolver.h" #include "parsers/lto_file.h" +#include "configure.h" + +#define VAL(x) #x +#define STRINGIFY(x) VAL(x) namespace ld { namespace tool { @@ -175,7 +179,7 @@ private: SectionBoundaryAtom* SectionBoundaryAtom::makeSectionBoundaryAtom(const char* name, bool start, const char* segSectName) { - + const char* segSectDividor = strrchr(segSectName, '$'); if ( segSectDividor == NULL ) throwf("malformed section$ symbol name: %s", name); @@ -185,8 +189,12 @@ SectionBoundaryAtom* SectionBoundaryAtom::makeSectionBoundaryAtom(const char* na throwf("malformed section$ symbol name: %s", name); char segName[18]; strlcpy(segName, segSectName, segNameLen+1); - - const ld::Section* section = new ld::Section(strdup(segName), sectionName, ld::Section::typeUnclassified); + + ld::Section::Type sectType = ld::Section::typeUnclassified; + if (!strcmp(segName, "__TEXT") && !strcmp(sectionName, "__thread_starts")) + sectType = ld::Section::typeThreadStarts; + + const ld::Section* section = new ld::Section(strdup(segName), sectionName, sectType); return new SectionBoundaryAtom(name, *section, (start ? ld::Atom::typeSectionStart : typeSectionEnd)); } @@ -275,12 +283,6 @@ SegmentBoundaryAtom* SegmentBoundaryAtom::makeOldSegmentBoundaryAtom(const char* void Resolver::initializeState() { - // set initial objc constraint based on command line options - if ( _options.objcGc() ) - _internal.objcObjectConstraint = ld::File::objcConstraintRetainReleaseOrGC; - else if ( _options.objcGcOnly() ) - _internal.objcObjectConstraint = ld::File::objcConstraintGC; - _internal.cpuSubType = _options.subArchitecture(); // In -r mode, look for -linker_option additions @@ -290,6 +292,11 @@ void Resolver::initializeState() doLinkerOption(*it, "command line"); } } +#ifdef LD64_VERSION_NUM + uint32_t packedNum = Options::parseVersionNumber32(STRINGIFY(LD64_VERSION_NUM)); + uint64_t combined = (uint64_t)TOOL_LD << 32 | packedNum; + _internal.toolsVersions.insert(combined); +#endif } void Resolver::buildAtomList() @@ -308,8 +315,10 @@ void Resolver::doLinkerOption(const std::vector& linkerOption, cons { if ( linkerOption.size() == 1 ) { const char* lo1 = linkerOption.front(); - if ( strncmp(lo1, "-l", 2) == 0 ) { - _internal.linkerOptionLibraries.insert(&lo1[2]); + if ( strncmp(lo1, "-l", 2) == 0) { + if (_internal.linkerOptionLibraries.count(&lo1[2]) == 0) { + _internal.unprocessedLinkerOptionLibraries.insert(&lo1[2]); + } } else { warning("unknown linker option from object file ignored: '%s' in %s", lo1, fileName); @@ -318,8 +327,10 @@ void Resolver::doLinkerOption(const std::vector& linkerOption, cons else if ( linkerOption.size() == 2 ) { const char* lo2a = linkerOption[0]; const char* lo2b = linkerOption[1]; - if ( strcmp(lo2a, "-framework") == 0 ) { - _internal.linkerOptionFrameworks.insert(lo2b); + if ( strcmp(lo2a, "-framework") == 0) { + if (_internal.linkerOptionFrameworks.count(lo2b) == 0) { + _internal.unprocessedLinkerOptionFrameworks.insert(lo2b); + } } else { warning("unknown linker option from object file ignored: '%s' '%s' from %s", lo2a, lo2b, fileName); @@ -330,16 +341,6 @@ void Resolver::doLinkerOption(const std::vector& linkerOption, cons } } -static void userReadableSwiftVersion(uint8_t value, char versionString[32]) -{ - switch (value) { - case 1: - strcpy(versionString, "1.0"); - break; - default: - sprintf(versionString, "0x%02X", value); - } -} void Resolver::doFile(const ld::File& file) { @@ -349,68 +350,100 @@ void Resolver::doFile(const ld::File& file) if ( objFile != NULL ) { // if file has linker options, process them ld::relocatable::File::LinkerOptionsList* lo = objFile->linkerOptions(); - if ( lo != NULL ) { + if ( lo != NULL && !_options.ignoreAutoLink() ) { for (relocatable::File::LinkerOptionsList::const_iterator it=lo->begin(); it != lo->end(); ++it) { this->doLinkerOption(*it, file.path()); } + // process any additional linker-options introduced by this new archive member being loaded + if ( _completedInitialObjectFiles ) { + _inputFiles.addLinkerOptionLibraries(_internal, *this); + _inputFiles.createIndirectDylibs(); + } } - // update which form of ObjC is being used - switch ( file.objCConstraint() ) { - case ld::File::objcConstraintNone: - break; - case ld::File::objcConstraintRetainRelease: - if ( _internal.objcObjectConstraint == ld::File::objcConstraintGC ) - throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", file.path()); - if ( _options.objcGcOnly() ) - throwf("command line specified -objc_gc_only, but file is retain/release based: %s", file.path()); - if ( _options.objcGc() ) - throwf("command line specified -objc_gc, but file is retain/release based: %s", file.path()); - if ( !_options.targetIOSSimulator() && (_internal.objcObjectConstraint != ld::File::objcConstraintRetainReleaseForSimulator) ) - _internal.objcObjectConstraint = ld::File::objcConstraintRetainRelease; - break; - case ld::File::objcConstraintRetainReleaseOrGC: - if ( _internal.objcObjectConstraint == ld::File::objcConstraintNone ) - _internal.objcObjectConstraint = ld::File::objcConstraintRetainReleaseOrGC; - if ( _options.targetIOSSimulator() ) - warning("linking ObjC for iOS Simulator, but object file (%s) was compiled for MacOSX", file.path()); - break; - case ld::File::objcConstraintGC: - if ( _internal.objcObjectConstraint == ld::File::objcConstraintRetainRelease ) - throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", file.path()); - _internal.objcObjectConstraint = ld::File::objcConstraintGC; - if ( _options.targetIOSSimulator() ) - warning("linking ObjC for iOS Simulator, but object file (%s) was compiled for MacOSX", file.path()); - break; - case ld::File::objcConstraintRetainReleaseForSimulator: - if ( _internal.objcObjectConstraint == ld::File::objcConstraintNone ) { - if ( !_options.targetIOSSimulator() && (_options.outputKind() != Options::kObjectFile) ) - warning("ObjC object file (%s) was compiled for iOS Simulator, but linking for MacOSX", file.path()); - _internal.objcObjectConstraint = ld::File::objcConstraintRetainReleaseForSimulator; - } - else if ( _internal.objcObjectConstraint != ld::File::objcConstraintRetainReleaseForSimulator ) { - _internal.objcObjectConstraint = ld::File::objcConstraintRetainReleaseForSimulator; + if ( objFile->hasObjC() ) + _internal.hasObjC = true; + + // Resolve bitcode section in the object file + if ( _options.bundleBitcode() ) { + if ( objFile->getBitcode() == NULL ) { + // Handle the special case for compiler_rt objects. Add the file to the list to be process. + if ( objFile->sourceKind() == ld::relocatable::File::kSourceCompilerArchive ) { + _internal.filesFromCompilerRT.push_back(objFile); + } else if (objFile->sourceKind() != ld::relocatable::File::kSourceLTO ) { + // No bitcode section, figure out if the object file comes from LTO/compiler static library + _options.platforms().forEach(^(ld::Platform platform, uint32_t version, bool &stop) { + switch ( platform ) { + case ld::kPlatform_macOS: + case ld::kPlatform_bridgeOS: + case ld::kPlatform_iOSMac: + case ld::kPlatform_unknown: + warning("all bitcode will be dropped because '%s' was built without bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. ", file.path()); + _internal.filesWithBitcode.clear(); + _internal.dropAllBitcode = true; + break; + case ld::kPlatform_iOS: + case ld::kPlatform_iOSSimulator: + throwf("'%s' does not contain bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.", file.path()); + break; + case ld::kPlatform_watchOS: + case ld::kPlatform_watchOSSimulator: + case ld::kPlatform_tvOS: + case ld::kPlatform_tvOSSimulator: + throwf("'%s' does not contain bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE) or obtain an updated library from the vendor", file.path()); + break; + } + }); } - break; + } else { + // contains bitcode, check if it is just a marker + if ( objFile->getBitcode()->isMarker() ) { + // if -bitcode_verify_bundle is used, check if all the object files participate in the linking have full bitcode embedded. + // error on any marker encountered. + if ( _options.verifyBitcode() ) + throwf("bitcode bundle could not be generated because '%s' was built without full bitcode. " + "All object files and libraries for bitcode must be generated from Xcode Archive or Install build", + objFile->path()); + // update the internal state that marker is encountered. + _internal.embedMarkerOnly = true; + _internal.filesWithBitcode.clear(); + _internal.dropAllBitcode = true; + } else if ( !_internal.dropAllBitcode ) + _internal.filesWithBitcode.push_back(objFile); + } } - + // verify all files use same version of Swift language if ( file.swiftVersion() != 0 ) { + _internal.someObjectFileHasSwift = true; if ( _internal.swiftVersion == 0 ) { _internal.swiftVersion = file.swiftVersion(); } else if ( file.swiftVersion() != _internal.swiftVersion ) { - char fileVersion[32]; - char otherVersion[32]; - userReadableSwiftVersion(file.swiftVersion(), fileVersion); - userReadableSwiftVersion(_internal.swiftVersion, otherVersion); + char fileVersion[64]; + char otherVersion[64]; + Options::userReadableSwiftVersion(file.swiftVersion(), fileVersion); + Options::userReadableSwiftVersion(_internal.swiftVersion, otherVersion); if ( file.swiftVersion() > _internal.swiftVersion ) { - throwf("%s compiled with newer version of Swift language (%s) than previous files (%s)", - file.path(), fileVersion, otherVersion); + if ( _options.warnOnSwiftABIVersionMismatches() ) { + warning("%s compiled with newer version of Swift language (%s) than previous files (%s)", + file.path(), fileVersion, otherVersion); + } else { + throwf("%s compiled with newer version of Swift language (%s) than previous files (%s)", + file.path(), fileVersion, otherVersion); + } } else { - throwf("%s compiled with older version of Swift language (%s) than previous files (%s)", - file.path(), fileVersion, otherVersion); + if ( _options.warnOnSwiftABIVersionMismatches() ) { + warning("%s compiled with older version of Swift language (%s) than previous files (%s)", + file.path(), fileVersion, otherVersion); + } else { + throwf("%s compiled with older version of Swift language (%s) than previous files (%s)", + file.path(), fileVersion, otherVersion); + } } } } @@ -422,7 +455,29 @@ void Resolver::doFile(const ld::File& file) // remember if any .o file did not have MH_SUBSECTIONS_VIA_SYMBOLS bit set if ( ! objFile->canScatterAtoms() ) _internal.allObjectFilesScatterable = false; - + + // remember if building for profiling (so we don't warn about initializers) + if ( objFile->hasllvmProfiling() ) + _havellvmProfiling = true; + +#if MULTI + // update minOSVersion off all .o files + uint32_t objMinOS = objFile->minOSVersion(); + if ( !objMinOS ) + _internal.objectFileFoundWithNoVersion = true; + if ( (_options.outputKind() == Options::kObjectFile) && (objMinOS > _internal.minOSVersion) ) + _internal.minOSVersion = objMinOS; +#endif + + auto objPlatforms = objFile->platforms(); + if ( (!objPlatforms.empty()) && (_options.outputKind() == Options::kObjectFile) && (_internal.derivedPlatforms.empty()) ) + _internal.derivedPlatforms = objPlatforms; + // update set of known tools used + for (const std::pair& entry : objFile->toolVersions()) { + uint64_t combined = (uint64_t)entry.first << 32 | entry.second; + _internal.toolsVersions.insert(combined); + } + // update cpu-sub-type cpu_subtype_t nextObjectSubType = file.cpuSubType(); switch ( _options.architecture() ) { @@ -445,6 +500,19 @@ void Resolver::doFile(const ld::File& file) } break; + case CPU_TYPE_ARM64: + if ( _options.subArchitecture() != nextObjectSubType ) { + if ( _options.allowSubArchitectureMismatches() ) { + warning("object file %s was built for different arm64 sub-type (%d) than link command line (%d)", + file.path(), nextObjectSubType, _options.subArchitecture()); + } + else { + throwf("object file %s was built for different arm64 sub-type (%d) than link command line (%d)", + file.path(), nextObjectSubType, _options.subArchitecture()); + } + } + break; + case CPU_TYPE_I386: _internal.cpuSubType = CPU_SUBTYPE_I386_ALL; break; @@ -464,66 +532,133 @@ void Resolver::doFile(const ld::File& file) } } if ( dylibFile != NULL ) { - // update which form of ObjC dylibs are being linked - switch ( dylibFile->objCConstraint() ) { - case ld::File::objcConstraintNone: - break; - case ld::File::objcConstraintRetainRelease: - if ( _internal.objcDylibConstraint == ld::File::objcConstraintGC ) - throwf("%s built with incompatible Garbage Collection settings to link with previous dylibs", file.path()); - if ( _options.objcGcOnly() ) - throwf("command line specified -objc_gc_only, but dylib is retain/release based: %s", file.path()); - if ( _options.objcGc() ) - throwf("command line specified -objc_gc, but dylib is retain/release based: %s", file.path()); - if ( _options.targetIOSSimulator() ) - warning("linking ObjC for iOS Simulator, but dylib (%s) was compiled for MacOSX", file.path()); - _internal.objcDylibConstraint = ld::File::objcConstraintRetainRelease; - break; - case ld::File::objcConstraintRetainReleaseOrGC: - if ( _internal.objcDylibConstraint == ld::File::objcConstraintNone ) - _internal.objcDylibConstraint = ld::File::objcConstraintRetainReleaseOrGC; - if ( _options.targetIOSSimulator() ) - warning("linking ObjC for iOS Simulator, but dylib (%s) was compiled for MacOSX", file.path()); - break; - case ld::File::objcConstraintGC: - if ( _internal.objcDylibConstraint == ld::File::objcConstraintRetainRelease ) - throwf("%s built with incompatible Garbage Collection settings to link with previous dylibs", file.path()); - if ( _options.targetIOSSimulator() ) - warning("linking ObjC for iOS Simulator, but dylib (%s) was compiled for MacOSX", file.path()); - _internal.objcDylibConstraint = ld::File::objcConstraintGC; - break; - case ld::File::objcConstraintRetainReleaseForSimulator: - if ( _internal.objcDylibConstraint == ld::File::objcConstraintNone ) - _internal.objcDylibConstraint = ld::File::objcConstraintRetainReleaseForSimulator; - else if ( _internal.objcDylibConstraint != ld::File::objcConstraintRetainReleaseForSimulator ) { - warning("ObjC dylib (%s) was compiled for iOS Simulator, but dylibs others were compiled for MacOSX", file.path()); - _internal.objcDylibConstraint = ld::File::objcConstraintRetainReleaseForSimulator; + // Check dylib for bitcode, if the library install path is relative path or @rpath, it has to contain bitcode + if ( _options.bundleBitcode() ) { + bool isSystemFramework = ( dylibFile->installPath() != NULL ) && ( dylibFile->installPath()[0] == '/' ); + if ( dylibFile->getBitcode() == NULL && !isSystemFramework ) { + // Check if the dylib is from toolchain by checking the path + char tcLibPath[PATH_MAX]; + char ldPath[PATH_MAX]; + char tempPath[PATH_MAX]; + uint32_t bufSize = PATH_MAX; + // toolchain library path should pointed to *.xctoolchain/usr/lib + if ( _NSGetExecutablePath(ldPath, &bufSize) != -1 ) { + if ( realpath(ldPath, tempPath) != NULL ) { + char* lastSlash = strrchr(tempPath, '/'); + if ( lastSlash != NULL ) + strcpy(lastSlash, "/../lib"); + } } - break; + // Compare toolchain library path to the dylib path + if ( realpath(tempPath, tcLibPath) == NULL || + realpath(dylibFile->path(), tempPath) == NULL || + strncmp(tcLibPath, tempPath, strlen(tcLibPath)) != 0 ) { + _options.platforms().forEach(^(ld::Platform platform, uint32_t version, bool &stop) { + switch ( platform ) { + case ld::kPlatform_macOS: + case ld::kPlatform_bridgeOS: + case ld::kPlatform_iOSMac: + case ld::kPlatform_unknown: + warning("all bitcode will be dropped because '%s' was built without bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.", file.path()); + _internal.filesWithBitcode.clear(); + _internal.dropAllBitcode = true; + break; + case ld::kPlatform_iOS: + case ld::kPlatform_iOSSimulator: + throwf("'%s' does not contain bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.", file.path()); + break; + case ld::kPlatform_watchOS: + case ld::kPlatform_watchOSSimulator: + case ld::kPlatform_tvOS: + case ld::kPlatform_tvOSSimulator: + throwf("'%s' does not contain bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE) or obtain an updated library from the vendor", file.path()); + break; + } + }); + } + } + // Error on bitcode marker in non-system frameworks if -bitcode_verify is used + if ( _options.verifyBitcode() && !isSystemFramework && + dylibFile->getBitcode() != NULL && dylibFile->getBitcode()->isMarker() ) + throwf("bitcode bundle could not be generated because '%s' was built without full bitcode. " + "All frameworks and dylibs for bitcode must be generated from Xcode Archive or Install build", + dylibFile->path()); } + + // Don't allow swift frameworks to link other swift frameworks. + if ( !_internal.firstSwiftDylibFile && _options.outputKind() == Options::kDynamicLibrary + && file.swiftVersion() != 0 && getenv("LD_DISALLOW_SWIFT_LINKING_SWIFT")) { + // Check that we aren't a whitelisted path. + bool inWhiteList = false; + const char *whitelistedPaths[] = { "/System/Library/PrivateFrameworks/Swift" }; + for (auto whitelistedPath : whitelistedPaths) { + if (!strncmp(whitelistedPath, dylibFile->installPath(), strlen(whitelistedPath))) { + inWhiteList = true; + break; + } + } + if (!inWhiteList) { + _internal.firstSwiftDylibFile = dylibFile; + } + } + + // verify dylibs use same version of Swift language + if ( file.swiftVersion() != 0 ) { + if ( _internal.swiftVersion == 0 ) { + _internal.swiftVersion = file.swiftVersion(); + } + else if ( file.swiftVersion() != _internal.swiftVersion ) { + char fileVersion[64]; + char otherVersion[64]; + Options::userReadableSwiftVersion(file.swiftVersion(), fileVersion); + Options::userReadableSwiftVersion(_internal.swiftVersion, otherVersion); + if ( file.swiftVersion() > _internal.swiftVersion ) { + if ( _options.warnOnSwiftABIVersionMismatches() ) { + warning("%s compiled with newer version of Swift language (%s) than previous files (%s)", + file.path(), fileVersion, otherVersion); + } else { + throwf("%s compiled with newer version of Swift language (%s) than previous files (%s)", + file.path(), fileVersion, otherVersion); + } + } + else { + if ( _options.warnOnSwiftABIVersionMismatches() ) { + warning("%s compiled with older version of Swift language (%s) than previous files (%s)", + file.path(), fileVersion, otherVersion); + } else { + throwf("%s compiled with older version of Swift language (%s) than previous files (%s)", + file.path(), fileVersion, otherVersion); + } + } + } + } + if ( _options.checkDylibsAreAppExtensionSafe() && !dylibFile->appExtensionSafe() ) { - warning("linking against dylib not safe for use in application extensions: %s", file.path()); + warning("linking against a dylib which is not safe for use in application extensions: %s", file.path()); } const char* depInstallName = dylibFile->installPath(); // embedded frameworks are only supported on iOS 8 and later if ( (depInstallName != NULL) && (depInstallName[0] != '/') ) { - if ( (_options.iOSVersionMin() != iOSVersionUnset) && (_options.iOSVersionMin() < iOS_8_0) ) { + if ( _options.platforms().contains(ld::kPlatform_iOS) && !_options.platforms().minOS(iOS_8_0) ) { // only warn about linking against embedded dylib if it is built for iOS 8 or later - if ( dylibFile->iOSMinVersion() >= iOS_8_0 ) - warning("embedded dylibs/frameworks are only supported on iOS 8.0 and later (%s)", depInstallName); + if ( dylibFile->platforms().minOS(ld::iOS_8_0) ) + throwf("embedded dylibs/frameworks are only supported on iOS 8.0 and later (%s)", depInstallName); } } if ( _options.sharedRegionEligible() ) { assert(depInstallName != NULL); - if ( depInstallName[0] == '@' ) + if ( depInstallName[0] == '@' ) { warning("invalid -install_name (%s) in dependent dylib (%s). Dylibs/frameworks which might go in dyld shared cache " - "cannot link with dylib that uses @rpath, @loaderpath, etc.", depInstallName, dylibFile->path()); - if ( (strncmp(depInstallName, "/usr/lib/", 9) != 0) && (strncmp(depInstallName, "/System/Library/", 16) != 0) ) + "cannot link with dylib that uses @rpath, @loader_path, etc.", depInstallName, dylibFile->path()); + } else if ( (strncmp(depInstallName, "/usr/lib/", 9) != 0) && (strncmp(depInstallName, "/System/Library/", 16) != 0) ) { warning("invalid -install_name (%s) in dependent dylib (%s). Dylibs/frameworks which might go in dyld shared cache " "cannot link with dylibs that won't be in the shared cache", depInstallName, dylibFile->path()); + } } } - } void Resolver::doAtom(const ld::Atom& atom) @@ -556,14 +691,14 @@ void Resolver::doAtom(const ld::Atom& atom) } else if ( _options.outputKind() == Options::kDynamicLibrary ) { if ( atom.file() != NULL ) - warning("target OS does not support re-exporting symbol %s from %s\n", _options.demangleSymbol(name), atom.file()->path()); + warning("target OS does not support re-exporting symbol %s from %s\n", _options.demangleSymbol(name), atom.safeFilePath()); else warning("target OS does not support re-exporting symbol %s\n", _options.demangleSymbol(name)); } } else { if ( atom.file() != NULL ) - warning("cannot export hidden symbol %s from %s", _options.demangleSymbol(name), atom.file()->path()); + warning("cannot export hidden symbol %s from %s", _options.demangleSymbol(name), atom.safeFilePath()); else warning("cannot export hidden symbol %s", _options.demangleSymbol(name)); } @@ -575,7 +710,7 @@ void Resolver::doAtom(const ld::Atom& atom) (const_cast(&atom))->setScope(ld::Atom::scopeGlobal); } else { - throwf("requested re-export symbol %s is not from a dylib, but from %s\n", _options.demangleSymbol(name), atom.file()->path()); + throwf("requested re-export symbol %s is not from a dylib, but from %s\n", _options.demangleSymbol(name), atom.safeFilePath()); } } break; @@ -586,7 +721,7 @@ void Resolver::doAtom(const ld::Atom& atom) //fprintf(stderr, "demote %s to hidden\n", name); } if ( _options.canReExportSymbols() && _options.shouldReExport(name) ) { - throwf("requested re-export symbol %s is not from a dylib, but from %s\n", _options.demangleSymbol(name), atom.file()->path()); + throwf("requested re-export symbol %s is not from a dylib, but from %s\n", _options.demangleSymbol(name), atom.safeFilePath()); } break; } @@ -601,16 +736,21 @@ void Resolver::doAtom(const ld::Atom& atom) // tell symbol table about non-static atoms if ( atom.scope() != ld::Atom::scopeTranslationUnit ) { - _symbolTable.add(atom, _options.deadCodeStrip() && _completedInitialObjectFiles); + _symbolTable.add(atom, _options.deadCodeStrip() && (_completedInitialObjectFiles || _options.allowDeadDuplicates())); // add symbol aliases defined on the command line if ( _options.haveCmdLineAliases() ) { const std::vector& aliases = _options.cmdLineAliases(); for (std::vector::const_iterator it=aliases.begin(); it != aliases.end(); ++it) { if ( strcmp(it->realName, atom.name()) == 0 ) { - const AliasAtom* alias = new AliasAtom(atom, it->alias); - _aliasesFromCmdLine.push_back(alias); - this->doAtom(*alias); + if ( strcmp(it->realName, it->alias) == 0 ) { + warning("ignoring alias of itself '%s'", it->realName); + } + else { + const AliasAtom* alias = new AliasAtom(atom, it->alias); + _aliasesFromCmdLine.push_back(alias); + this->doAtom(*alias); + } } } } @@ -627,10 +767,25 @@ void Resolver::doAtom(const ld::Atom& atom) if ( atom.section().type() == ld::Section::typeTempAlias ) _haveAliases = true; + // error or warn about initializers + if ( (atom.section().type() == ld::Section::typeInitializerPointers) && !_havellvmProfiling ) { + switch ( _options.initializersTreatment() ) { + case Options::kError: + throwf("static initializer found in '%s'",atom.safeFilePath()); + case Options::kWarning: + warning("static initializer found in '%s'. Use -no_inits to make this an error. Use -no_warn_inits to suppress warning",atom.safeFilePath()); + break; + default: + break; + } + } + if ( _options.deadCodeStrip() ) { // add to set of dead-strip-roots, all symbols that the compiler marks as don't strip if ( atom.dontDeadStrip() ) _deadStripRoots.insert(&atom); + else if ( atom.dontDeadStripIfReferencesLive() ) + _dontDeadStripIfReferencesLive.push_back(&atom); if ( atom.scope() == ld::Atom::scopeGlobal ) { // -exported_symbols_list that has wildcards and -dead_strip @@ -825,6 +980,10 @@ void Resolver::resolveUndefines() } } + // After resolving all the undefs within the linkageUnit, record all the remaining undefs and all the proxies. + if (_options.bundleBitcode() && _options.hideSymbols()) + _symbolTable.mustPreserveForBitcode(_internal.allUndefProxies); + } @@ -833,12 +992,12 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) //fprintf(stderr, "markLive(%p) %s\n", &atom, atom.name()); // if -why_live cares about this symbol, then dump chain if ( (previous->referer != NULL) && _options.printWhyLive(atom.name()) ) { - fprintf(stderr, "%s from %s\n", atom.name(), atom.file()->path()); + fprintf(stderr, "%s from %s\n", atom.name(), atom.safeFilePath()); int depth = 1; for(WhyLiveBackChain* p = previous; p != NULL; p = p->previous, ++depth) { for(int i=depth; i > 0; --i) fprintf(stderr, " "); - fprintf(stderr, "%s from %s\n", p->referer->name(), p->referer->file()->path()); + fprintf(stderr, "%s from %s\n", p->referer->name(), p->referer->safeFilePath()); } } @@ -866,6 +1025,9 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) case ld::Fixup::kindSubtractTargetAddress: case ld::Fixup::kindStoreTargetAddressLittleEndian32: case ld::Fixup::kindStoreTargetAddressLittleEndian64: +#if SUPPORT_ARCH_arm64e + case ld::Fixup::kindStoreTargetAddressLittleEndianAuth64: +#endif case ld::Fixup::kindStoreTargetAddressBigEndian32: case ld::Fixup::kindStoreTargetAddressBigEndian64: case ld::Fixup::kindStoreTargetAddressX86PCRel32: @@ -883,6 +1045,8 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) case ld::Fixup::kindStoreTargetAddressARM64Page21: case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21: #endif if ( fit->binding == ld::Fixup::bindingByContentBound ) { // normally this was done in convertReferencesToIndirect() @@ -1015,6 +1179,38 @@ void Resolver::deadStripOptimize(bool force) this->markLive(**it, &rootChain); } + // special case atoms that need to be live if they reference something live + if ( ! _dontDeadStripIfReferencesLive.empty() ) { + for (std::vector::iterator it=_dontDeadStripIfReferencesLive.begin(); it != _dontDeadStripIfReferencesLive.end(); ++it) { + const Atom* liveIfRefLiveAtom = *it; + //fprintf(stderr, "live-if-live atom: %s\n", liveIfRefLiveAtom->name()); + if ( liveIfRefLiveAtom->live() ) + continue; + bool hasLiveRef = false; + for (ld::Fixup::iterator fit=liveIfRefLiveAtom->fixupsBegin(); fit != liveIfRefLiveAtom->fixupsEnd(); ++fit) { + const Atom* target = NULL; + switch ( fit->binding ) { + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + target = _internal.indirectBindingTable[fit->u.bindingIndex]; + break; + default: + break; + } + if ( (target != NULL) && target->live() ) + hasLiveRef = true; + } + if ( hasLiveRef ) { + WhyLiveBackChain rootChain; + rootChain.previous = NULL; + rootChain.referer = liveIfRefLiveAtom; + this->markLive(*liveIfRefLiveAtom, &rootChain); + } + } + } + // now remove all non-live atoms from _atoms const bool log = false; if ( log ) { @@ -1026,11 +1222,13 @@ void Resolver::deadStripOptimize(bool force) } if ( _haveLLVMObjs && !force ) { + std::copy_if(_atoms.begin(), _atoms.end(), std::back_inserter(_internal.deadAtoms), NotLiveLTO() ); // don't remove combinable atoms, they may come back in lto output _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLiveLTO()), _atoms.end()); _symbolTable.removeDeadAtoms(); } else { + std::copy_if(_atoms.begin(), _atoms.end(), std::back_inserter(_internal.deadAtoms), NotLive() ); _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLive()), _atoms.end()); } @@ -1197,11 +1395,11 @@ bool Resolver::printReferencedBy(const char* name, SymbolTable::IndirectBindingS ++foundReferenceCount; } else if ( atom->contentType() == ld::Atom::typeCFI ) { - fprintf(stderr, " Dwarf Exception Unwind Info (__eh_frame) in %s\n", pathLeafName(atom->file()->path())); + fprintf(stderr, " Dwarf Exception Unwind Info (__eh_frame) in %s\n", pathLeafName(atom->safeFilePath())); ++foundReferenceCount; } else { - fprintf(stderr, " %s in %s\n", _options.demangleSymbol(atom->name()), pathLeafName(atom->file()->path())); + fprintf(stderr, " %s in %s\n", _options.demangleSymbol(atom->name()), pathLeafName(atom->safeFilePath())); ++foundReferenceCount; break; // if undefined used twice in a function, only show first } @@ -1261,6 +1459,10 @@ void Resolver::checkUndefines(bool force) int unresolvableExportsCount = 0; if ( unresolvableCount != 0 ) { if ( doPrint ) { + for (const auto& lib : _internal.missingLinkerOptionLibraries) + warning("Could not find auto-linked library '%s'", lib); + for (const auto& frm : _internal.missingLinkerOptionFrameworks) + warning("Could not find auto-linked framework '%s'", frm); if ( _options.printArchPrefix() ) fprintf(stderr, "Undefined symbols for architecture %s:\n", _options.architectureName()); else @@ -1417,6 +1619,7 @@ void Resolver::fillInHelpersInInternalState() } _internal.classicBindingHelper = NULL; + // FIXME: What about fMakeThreadedStartsSection? if ( needsStubHelper && !_options.makeCompressedDyldInfo() ) { // "dyld_stub_binding_helper" comes from .o file, so should already exist in symbol table if ( _symbolTable.hasName("dyld_stub_binding_helper") ) { @@ -1437,6 +1640,7 @@ void Resolver::fillInHelpersInInternalState() } _internal.compressedFastBinderProxy = NULL; + // FIXME: What about fMakeThreadedStartsSection? if ( needsStubHelper && _options.makeCompressedDyldInfo() ) { // "dyld_stub_binder" comes from libSystem.dylib so will need to manually resolve if ( !_symbolTable.hasName("dyld_stub_binder") ) { @@ -1467,6 +1671,11 @@ void Resolver::fillInInternalState() // make sure there is a __text section so that codesigning works if ( (_options.outputKind() == Options::kDynamicLibrary) || (_options.outputKind() == Options::kDynamicBundle) ) _internal.getFinalSection(*new ld::Section("__TEXT", "__text", ld::Section::typeCode)); + + // Don't allow swift frameworks to link other swift frameworks. + if ( _internal.someObjectFileHasSwift && _internal.firstSwiftDylibFile != nullptr ) + throwf("linking swift frameworks against other swift frameworks (%s) is not permitted", + _internal.firstSwiftDylibFile->path()); } void Resolver::fillInEntryPoint() @@ -1535,12 +1744,19 @@ void Resolver::linkTimeOptimize() lto::OptimizeOptions optOpt; optOpt.outputFilePath = _options.outputFilePath(); optOpt.tmpObjectFilePath = _options.tempLtoObjectPath(); + optOpt.ltoCachePath = _options.ltoCachePath(); + optOpt.ltoPruneIntervalOverwrite = _options.ltoPruneIntervalOverwrite(); + optOpt.ltoPruneInterval = _options.ltoPruneInterval(); + optOpt.ltoPruneAfter = _options.ltoPruneAfter(); + optOpt.ltoMaxCacheSize = _options.ltoMaxCacheSize(); optOpt.preserveAllGlobals = _options.allGlobalsAreDeadStripRoots() || _options.hasExportRestrictList(); optOpt.verbose = _options.verbose(); optOpt.saveTemps = _options.saveTempFiles(); + optOpt.ltoCodegenOnly = _options.ltoCodegenOnly(); optOpt.pie = _options.positionIndependentExecutable(); optOpt.mainExecutable = _options.linkingMainExecutable();; optOpt.staticExecutable = (_options.outputKind() == Options::kStaticExecutable); + optOpt.preload = (_options.outputKind() == Options::kPreload); optOpt.relocatable = (_options.outputKind() == Options::kObjectFile); optOpt.allowTextRelocs = _options.allowTextRelocs(); optOpt.linkerDeadStripping = _options.deadCodeStrip(); @@ -1548,8 +1764,16 @@ void Resolver::linkTimeOptimize() optOpt.keepDwarfUnwind = _options.keepDwarfUnwind(); optOpt.verboseOptimizationHints = _options.verboseOptimizationHints(); optOpt.armUsesZeroCostExceptions = _options.armUsesZeroCostExceptions(); + optOpt.simulator = _options.targetIOSSimulator(); + optOpt.ignoreMismatchPlatform = ((_options.outputKind() == Options::kPreload) || (_options.outputKind() == Options::kStaticExecutable)); +#if SUPPORT_ARCH_arm64e + optOpt.supportsAuthenticatedPointers = _options.supportsAuthenticatedPointers(); +#endif + optOpt.bitcodeBundle = (_options.bundleBitcode() && (_options.bitcodeKind() != Options::kBitcodeMarker)); + optOpt.maxDefaultCommonAlignment = _options.maxDefaultCommonAlign(); optOpt.arch = _options.architecture(); optOpt.mcpu = _options.mcpuLTO(); + optOpt.platforms = _options.platforms(); optOpt.llvmOptions = &_options.llvmOptions(); optOpt.initialUndefines = &_options.initialUndefines(); @@ -1592,14 +1816,21 @@ void Resolver::linkTimeOptimize() // if -dead_strip on command line if ( _options.deadCodeStrip() ) { - // clear liveness bit + // run through all atoms again and make live_section LTO atoms are preserved from dead_stripping if needed + _dontDeadStripIfReferencesLive.clear(); for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + const ld::Atom* atom = *it; + if ( atom->dontDeadStripIfReferencesLive() ) { + _dontDeadStripIfReferencesLive.push_back(atom); + } + + // clear liveness bit (const_cast(*it))->setLive((*it)->dontDeadStrip()); } // and re-compute dead code this->deadStripOptimize(true); } - + // if -exported_symbols_list on command line, re-force scope if ( _options.hasExportMaskList() ) { for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { @@ -1623,6 +1854,23 @@ void Resolver::linkTimeOptimize() // check new code does not override some dylib this->checkDylibSymbolCollisions(); + + // remove undefs from LTO objects that gets optimized away + std::unordered_set mustPreserve; + if ( _internal.classicBindingHelper != NULL ) + mustPreserve.insert(_internal.classicBindingHelper); + if ( _internal.compressedFastBinderProxy != NULL ) + mustPreserve.insert(_internal.compressedFastBinderProxy); + if ( _internal.lazyBindingHelper != NULL ) + mustPreserve.insert(_internal.lazyBindingHelper); + if ( const ld::Atom* entry = this->entryPoint(true) ) + mustPreserve.insert(entry); + for (Options::UndefinesIterator uit=_options.initialUndefinesBegin(); uit != _options.initialUndefinesEnd(); ++uit) { + SymbolTable::IndirectBindingSlot slot = _symbolTable.findSlotForName(*uit); + if ( _internal.indirectBindingTable[slot] != NULL ) + mustPreserve.insert(_internal.indirectBindingTable[slot]); + } + _symbolTable.removeDeadUndefs(_atoms, mustPreserve); } } @@ -1659,6 +1907,12 @@ void Resolver::tweakWeakness() } } +void Resolver::buildArchivesList() +{ + // Determine which archives were linked and update the internal state. + _inputFiles.archives(_internal); +} + void Resolver::dumpAtoms() { fprintf(stderr, "Resolver all atoms:\n"); @@ -1685,6 +1939,7 @@ void Resolver::resolve() this->fillInInternalState(); this->tweakWeakness(); _symbolTable.checkDuplicateSymbols(); + this->buildArchivesList(); }