X-Git-Url: https://git.saurik.com/apple/ld64.git/blobdiff_plain/a645023da60d22e86be13f7b4d97adeff8bc6665..7f09b9353af9897bf18933788d6a59c152c29edd:/src/ld/Resolver.cpp diff --git a/src/ld/Resolver.cpp b/src/ld/Resolver.cpp index e17e632..1c8f16e 100644 --- a/src/ld/Resolver.cpp +++ b/src/ld/Resolver.cpp @@ -47,14 +47,13 @@ #include #include #include -#include -#include #include #include #include "Options.h" #include "ld.hpp" +#include "Bitcode.hpp" #include "InputFiles.h" #include "SymbolTable.h" #include "Resolver.h" @@ -80,8 +79,6 @@ public: _name(nm) {} // overrides of ld::Atom virtual const ld::File* file() const { return NULL; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return _name; } virtual uint64_t size() const { return 0; } virtual uint64_t objectAddress() const { return 0; } @@ -116,8 +113,8 @@ public: // overrides of ld::Atom virtual const ld::File* file() const { return _aliasOf.file(); } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return _aliasOf.translationUnitSource(dir, nm); } + virtual const char* translationUnitSource() const + { return _aliasOf.translationUnitSource(); } virtual const char* name() const { return _name; } virtual uint64_t size() const { return 0; } virtual uint64_t objectAddress() const { return _aliasOf.objectAddress(); } @@ -133,6 +130,11 @@ public: virtual ld::Atom::UnwindInfo::iterator endUnwind() const { return NULL; } virtual ld::Atom::LineInfo::iterator beginLineInfo() const { return NULL; } virtual ld::Atom::LineInfo::iterator endLineInfo() const { return NULL; } + + void setFinalAliasOf() const { + (const_cast(this))->setAttributesFromAtom(_aliasOf); + (const_cast(this))->setScope(ld::Atom::scopeGlobal); + } private: const char* _name; @@ -149,8 +151,6 @@ public: static SectionBoundaryAtom* makeOldSectionBoundaryAtom(const char* name, bool start); // overrides of ld::Atom - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return _name; } virtual uint64_t size() const { return 0; } @@ -215,8 +215,6 @@ public: static SegmentBoundaryAtom* makeOldSegmentBoundaryAtom(const char* name, bool start); // overrides of ld::Atom - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return _name; } virtual uint64_t size() const { return 0; } @@ -285,36 +283,77 @@ void Resolver::initializeState() _internal.objcObjectConstraint = ld::File::objcConstraintGC; _internal.cpuSubType = _options.subArchitecture(); + _internal.minOSVersion = _options.minOSversion(); + _internal.derivedPlatformLoadCommand = 0; + + // In -r mode, look for -linker_option additions + if ( _options.outputKind() == Options::kObjectFile ) { + ld::relocatable::File::LinkerOptionsList lo = _options.linkerOptions(); + for (relocatable::File::LinkerOptionsList::const_iterator it=lo.begin(); it != lo.end(); ++it) { + doLinkerOption(*it, "command line"); + } + } } void Resolver::buildAtomList() { // each input files contributes initial atoms _atoms.reserve(1024); - _inputFiles.forEachInitialAtom(*this); + _inputFiles.forEachInitialAtom(*this, _internal); + _completedInitialObjectFiles = true; //_symbolTable.printStatistics(); } -unsigned int Resolver::ppcSubTypeIndex(uint32_t subtype) + +void Resolver::doLinkerOption(const std::vector& linkerOption, const char* fileName) { - switch ( subtype ) { - case CPU_SUBTYPE_POWERPC_ALL: - return 0; - case CPU_SUBTYPE_POWERPC_750: - // G3 - return 1; - case CPU_SUBTYPE_POWERPC_7400: - case CPU_SUBTYPE_POWERPC_7450: - // G4 - return 2; - case CPU_SUBTYPE_POWERPC_970: - // G5 can run everything - return 3; - default: - throw "Unhandled PPC cpu subtype!"; + if ( linkerOption.size() == 1 ) { + const char* lo1 = linkerOption.front(); + 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); + } + } + else if ( linkerOption.size() == 2 ) { + const char* lo2a = linkerOption[0]; + const char* lo2b = linkerOption[1]; + 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); + } + } + else { + warning("unknown linker option from object file ignored, starting with: '%s' from %s", linkerOption.front(), fileName); + } +} + +static void userReadableSwiftVersion(uint8_t value, char versionString[64]) +{ + switch (value) { + case 1: + strcpy(versionString, "1.0"); break; + case 2: + strcpy(versionString, "1.1"); + break; + case 3: + strcpy(versionString, "2.0"); + break; + case 4: + strcpy(versionString, "3.0"); + break; + default: + sprintf(versionString, "unknown ABI version 0x%02X", value); } } @@ -324,6 +363,63 @@ void Resolver::doFile(const ld::File& file) const ld::dylib::File* dylibFile = dynamic_cast(&file); if ( objFile != NULL ) { + // if file has linker options, process them + ld::relocatable::File::LinkerOptionsList* lo = objFile->linkerOptions(); + 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(); + } + } + // Resolve bitcode section in the object file + if ( _options.bundleBitcode() ) { + if ( objFile->getBitcode() == NULL ) { + // No bitcode section, figure out if the object file comes from LTO/compiler static library + if (objFile->sourceKind() != ld::relocatable::File::kSourceLTO && + objFile->sourceKind() != ld::relocatable::File::kSourceCompilerArchive ) { + switch ( _options.platform() ) { + case Options::kPlatformOSX: + case Options::kPlatformUnknown: + 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 Options::kPlatformiOS: + 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 Options::kPlatformWatchOS: +#if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: +#endif + 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; + } + } + } 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); + } + } + // update which form of ObjC is being used switch ( file.objCConstraint() ) { case ld::File::objcConstraintNone: @@ -335,57 +431,78 @@ void Resolver::doFile(const ld::File& file) 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()); - _internal.objcObjectConstraint = ld::File::objcConstraintRetainRelease; + 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; + } break; } + // verify all files 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]; + userReadableSwiftVersion(file.swiftVersion(), fileVersion); + 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); + } + else { + throwf("%s compiled with older version of Swift language (%s) than previous files (%s)", + file.path(), fileVersion, otherVersion); + } + } + } + // in -r mode, if any .o files have dwarf then add UUID to output .o file if ( objFile->debugInfo() == ld::relocatable::File::kDebugInfoDwarf ) _internal.someObjectFileHasDwarf = true; - // remember if any objc classes built for fix-and-continue - if ( objFile->objcReplacementClasses() ) - _internal.hasObjcReplacementClasses = true; - // remember if any .o file did not have MH_SUBSECTIONS_VIA_SYMBOLS bit set if ( ! objFile->canScatterAtoms() ) _internal.allObjectFilesScatterable = false; + // update minOSVersion off all .o files + uint32_t objMinOS = objFile->minOSVersion(); + if ( !objMinOS ) + _internal.objectFileFoundWithNoVersion = true; + + uint32_t objPlatformLC = objFile->platformLoadCommand(); + if ( (objPlatformLC != 0) && (_internal.derivedPlatformLoadCommand == 0) && (_options.outputKind() == Options::kObjectFile) ) + _internal.derivedPlatformLoadCommand = objPlatformLC; + + if ( (_options.outputKind() == Options::kObjectFile) && (objMinOS > _internal.minOSVersion) ) + _internal.minOSVersion = objMinOS; + // update cpu-sub-type cpu_subtype_t nextObjectSubType = file.cpuSubType(); switch ( _options.architecture() ) { - case CPU_TYPE_POWERPC: - // no checking when -force_cpusubtype_ALL is used - if ( _options.forceCpuSubtypeAll() ) - return; - if ( _options.preferSubArchitecture() ) { - // warn if some .o file is not compatible with desired output sub-type - if ( _options.subArchitecture() != nextObjectSubType ) { - if ( ppcSubTypeIndex(nextObjectSubType) > ppcSubTypeIndex(_options.subArchitecture()) ) { - if ( !_inputFiles.inferredArch() ) - warning("cpu-sub-type of %s is not compatible with command line cpu-sub-type", file.path()); - _internal.cpuSubType = nextObjectSubType; - } - } - } - else { - // command line to linker just had -arch ppc - // figure out final sub-type based on sub-type of all .o files - if ( ppcSubTypeIndex(nextObjectSubType) > ppcSubTypeIndex(_internal.cpuSubType) ) { - _internal.cpuSubType = nextObjectSubType; - } - } - break; - case CPU_TYPE_ARM: if ( _options.subArchitecture() != nextObjectSubType ) { if ( (_options.subArchitecture() == CPU_SUBTYPE_ARM_ALL) && _options.forceCpuSubtypeAll() ) { @@ -394,6 +511,10 @@ void Resolver::doFile(const ld::File& file) else if ( nextObjectSubType == CPU_SUBTYPE_ARM_ALL ) { warning("CPU_SUBTYPE_ARM_ALL subtype is deprecated: %s", file.path()); } + else if ( _options.allowSubArchitectureMismatches() ) { + //warning("object file %s was built for different arm sub-type (%d) than link command line (%d)", + // file.path(), nextObjectSubType, _options.subArchitecture()); + } else { throwf("object file %s was built for different arm sub-type (%d) than link command line (%d)", file.path(), nextObjectSubType, _options.subArchitecture()); @@ -401,19 +522,76 @@ void Resolver::doFile(const ld::File& file) } break; - case CPU_TYPE_POWERPC64: - break; - case CPU_TYPE_I386: _internal.cpuSubType = CPU_SUBTYPE_I386_ALL; break; case CPU_TYPE_X86_64: - _internal.cpuSubType = CPU_SUBTYPE_X86_64_ALL; + if ( _options.subArchitecture() != nextObjectSubType ) { + if ( _options.allowSubArchitectureMismatches() ) { + warning("object file %s was built for different x86_64 sub-type (%d) than link command line (%d)", + file.path(), nextObjectSubType, _options.subArchitecture()); + } + else { + throwf("object file %s was built for different x86_64 sub-type (%d) than link command line (%d)", + file.path(), nextObjectSubType, _options.subArchitecture()); + } + } break; } } if ( dylibFile != NULL ) { + // 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"); + } + } + // Compare toolchain library path to the dylib path + if ( realpath(tempPath, tcLibPath) == NULL || + realpath(dylibFile->path(), tempPath) == NULL || + strncmp(tcLibPath, tempPath, strlen(tcLibPath)) != 0 ) { + switch ( _options.platform() ) { + case Options::kPlatformOSX: + case Options::kPlatformUnknown: + 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 Options::kPlatformiOS: + 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 Options::kPlatformWatchOS: +#if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: +#endif + 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()); + } + // update which form of ObjC dylibs are being linked switch ( dylibFile->objCConstraint() ) { case ld::File::objcConstraintNone: @@ -425,17 +603,75 @@ void Resolver::doFile(const ld::File& file) 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()); - _internal.objcDylibConstraint = ld::File::objcConstraintGC; + 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; + } + break; + } + + // 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]; + userReadableSwiftVersion(file.swiftVersion(), fileVersion); + 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); + } + 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 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) ) { + // only warn about linking against embedded dylib if it is built for iOS 8 or later + if ( dylibFile->minOSVersion() >= 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] == '@' ) { + 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, @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()); + } } } @@ -443,7 +679,9 @@ void Resolver::doFile(const ld::File& file) void Resolver::doAtom(const ld::Atom& atom) { - //fprintf(stderr, "Resolver::doAtom(%p), name=%s, sect=%s\n", &atom, atom.name(), atom.section().sectionName()); + //fprintf(stderr, "Resolver::doAtom(%p), name=%s, sect=%s, scope=%d\n", &atom, atom.name(), atom.section().sectionName(), atom.scope()); + if ( _ltoCodeGenFinished && (atom.contentType() == ld::Atom::typeLTOtemporary) && (atom.scope() != ld::Atom::scopeTranslationUnit) ) + warning("'%s' is implemented in bitcode, but it was loaded too late", atom.name()); // add to list of known atoms _atoms.push_back(&atom); @@ -467,18 +705,18 @@ void Resolver::doAtom(const ld::Atom& atom) // marking proxy atom as global triggers the re-export (const_cast(&atom))->setScope(ld::Atom::scopeGlobal); } - else { + else if ( _options.outputKind() == Options::kDynamicLibrary ) { if ( atom.file() != NULL ) - warning("cannot re-export symbol %s from %s\n", SymbolTable::demangle(name), atom.file()->path()); + warning("target OS does not support re-exporting symbol %s from %s\n", _options.demangleSymbol(name), atom.file()->path()); else - warning("cannot re-export symbol %s\n", SymbolTable::demangle(name)); + 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", SymbolTable::demangle(name), atom.file()->path()); + warning("cannot export hidden symbol %s from %s", _options.demangleSymbol(name), atom.file()->path()); else - warning("cannot export hidden symbol %s", SymbolTable::demangle(name)); + warning("cannot export hidden symbol %s", _options.demangleSymbol(name)); } } } @@ -488,7 +726,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", SymbolTable::demangle(name), atom.file()->path()); + throwf("requested re-export symbol %s is not from a dylib, but from %s\n", _options.demangleSymbol(name), atom.file()->path()); } } break; @@ -499,7 +737,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", SymbolTable::demangle(name), atom.file()->path()); + throwf("requested re-export symbol %s is not from a dylib, but from %s\n", _options.demangleSymbol(name), atom.file()->path()); } break; } @@ -507,21 +745,28 @@ void Resolver::doAtom(const ld::Atom& atom) // work around for kernel that uses 'l' labels in assembly code if ( (atom.symbolTableInclusion() == ld::Atom::symbolTableNotInFinalLinkedImages) - && (atom.name()[0] == 'l') && (_options.outputKind() == Options::kStaticExecutable) ) + && (atom.name()[0] == 'l') && (_options.outputKind() == Options::kStaticExecutable) + && (strncmp(atom.name(), "ltmp", 4) != 0) ) (const_cast(&atom))->setSymbolTableInclusion(ld::Atom::symbolTableIn); // 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 ld::Atom* alias = new AliasAtom(atom, it->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); + } } } } @@ -534,14 +779,16 @@ void Resolver::doAtom(const ld::Atom& atom) if ( atom.contentType() == ld::Atom::typeLTOtemporary ) _haveLLVMObjs = true; - // if we've already partitioned into final sections, and lto needs a symbol very late, add it - if ( _addToFinalSection ) - _internal.addAtom(atom); + // remember if any atoms are aliases + if ( atom.section().type() == ld::Section::typeTempAlias ) + _haveAliases = true; 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 @@ -561,10 +808,10 @@ bool Resolver::isDtraceProbe(ld::Fixup::Kind kind) case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: case ld::Fixup::kindStoreARMDtraceCallSiteNop: case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: + case ld::Fixup::kindStoreARM64DtraceCallSiteNop: + case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: case ld::Fixup::kindStoreThumbDtraceCallSiteNop: case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: - case ld::Fixup::kindStorePPCDtraceCallSiteNop: - case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: case ld::Fixup::kindDtraceExtra: return true; default: @@ -580,6 +827,8 @@ void Resolver::convertReferencesToIndirect(const ld::Atom& atom) const ld::Atom* dummy; ld::Fixup::iterator end = atom.fixupsEnd(); for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != end; ++fit) { + if ( fit->kind == ld::Fixup::kindLinkerOptimizationHint ) + _internal.someObjectHasOptimizationHints = true; switch ( fit->binding ) { case ld::Fixup::bindingByNameUnbound: if ( isDtraceProbe(fit->kind) && (_options.outputKind() != Options::kObjectFile ) ) { @@ -639,7 +888,7 @@ void Resolver::resolveUndefines() const char* undef = *it; // load for previous undefine may also have loaded this undefine, so check again if ( ! _symbolTable.hasName(undef) ) { - _inputFiles.searchLibraries(undef, true, true, *this); + _inputFiles.searchLibraries(undef, true, true, false, *this); if ( !_symbolTable.hasName(undef) && (_options.outputKind() != Options::kObjectFile) ) { if ( strncmp(undef, "section$", 8) == 0 ) { if ( strncmp(undef, "section$start$", 14) == 0 ) { @@ -686,19 +935,39 @@ void Resolver::resolveUndefines() const ld::Atom* curAtom = _symbolTable.atomForSlot(_symbolTable.findSlotForName(*it)); assert(curAtom != NULL); if ( curAtom->definition() == ld::Atom::definitionTentative ) { - _inputFiles.searchLibraries(*it, searchDylibs, true, *this); + _inputFiles.searchLibraries(*it, searchDylibs, true, true, *this); } } } } - + + // Use linker options to resolve any remaining undefined symbols + if ( !_internal.linkerOptionLibraries.empty() || !_internal.linkerOptionFrameworks.empty() ) { + std::vector undefineNames; + _symbolTable.undefines(undefineNames); + if ( undefineNames.size() != 0 ) { + for (std::vector::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) { + const char* undef = *it; + if ( ! _symbolTable.hasName(undef) ) { + _inputFiles.searchLibraries(undef, true, true, false, *this); + } + } + } + } + // create proxies as needed for undefined symbols if ( (_options.undefinedTreatment() != Options::kUndefinedError) || (_options.outputKind() == Options::kObjectFile) ) { std::vector undefineNames; _symbolTable.undefines(undefineNames); for(std::vector::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) { - // make proxy - this->doAtom(*new UndefinedProxyAtom(*it)); + const char* undefName = *it; + // "ld -r -exported_symbol _foo" has wrong error message if _foo is undefined + bool makeProxy = true; + if ( (_options.outputKind() == Options::kObjectFile) && _options.hasExportMaskList() && _options.shouldExport(undefName) ) + makeProxy = false; + + if ( makeProxy ) + this->doAtom(*new UndefinedProxyAtom(undefName)); } } @@ -714,6 +983,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); + } @@ -750,6 +1023,7 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) case ld::Fixup::kindNoneGroupSubordinate: case ld::Fixup::kindNoneGroupSubordinateFDE: case ld::Fixup::kindNoneGroupSubordinateLSDA: + case ld::Fixup::kindNoneGroupSubordinatePersonality: case ld::Fixup::kindSetTargetAddress: case ld::Fixup::kindSubtractTargetAddress: case ld::Fixup::kindStoreTargetAddressLittleEndian32: @@ -760,9 +1034,20 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad: + case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoadNowLEA: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: - case ld::Fixup::kindStoreTargetAddressPPCBranch24: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64Branch26: + 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() // but a archive loaded .o file may have a forward reference @@ -798,14 +1083,14 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) target = _internal.indirectBindingTable[fit->u.bindingIndex]; if ( target == NULL ) { const char* targetName = _symbolTable.indirectName(fit->u.bindingIndex); - _inputFiles.searchLibraries(targetName, true, true, *this); + _inputFiles.searchLibraries(targetName, true, true, false, *this); target = _internal.indirectBindingTable[fit->u.bindingIndex]; } if ( target != NULL ) { if ( target->definition() == ld::Atom::definitionTentative ) { // need to search archives for overrides of common symbols bool searchDylibs = (_options.commonsMode() == Options::kCommonsOverriddenByDylibs); - _inputFiles.searchLibraries(target->name(), searchDylibs, true, *this); + _inputFiles.searchLibraries(target->name(), searchDylibs, true, true, *this); // recompute target since it may have been overridden by searchLibraries() target = _internal.indirectBindingTable[fit->u.bindingIndex]; } @@ -826,8 +1111,23 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) } +class NotLiveLTO { +public: + bool operator()(const ld::Atom* atom) const { + if (atom->live() || atom->dontDeadStrip() ) + return false; + // don't kill combinable atoms in first pass + switch ( atom->combine() ) { + case ld::Atom::combineByNameAndContent: + case ld::Atom::combineByNameAndReferences: + return false; + default: + return true; + } + } +}; -void Resolver::deadStripOptimize() +void Resolver::deadStripOptimize(bool force) { // only do this optimization with -dead_strip if ( ! _options.deadCodeStrip() ) @@ -842,7 +1142,7 @@ void Resolver::deadStripOptimize() for (Options::UndefinesIterator uit=_options.initialUndefinesBegin(); uit != _options.initialUndefinesEnd(); ++uit) { SymbolTable::IndirectBindingSlot slot = _symbolTable.findSlotForName(*uit); if ( _internal.indirectBindingTable[slot] == NULL ) { - _inputFiles.searchLibraries(*uit, false, true, *this); + _inputFiles.searchLibraries(*uit, false, true, false, *this); } if ( _internal.indirectBindingTable[slot] != NULL ) _deadStripRoots.insert(_internal.indirectBindingTable[slot]); @@ -879,25 +1179,116 @@ void Resolver::deadStripOptimize() 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 ) { - fprintf(stderr, "deadStripOptimize() all atoms with liveness:\n"); + fprintf(stderr, "deadStripOptimize() all %ld atoms with liveness:\n", _atoms.size()); + for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + const ld::File* file = (*it)->file(); + fprintf(stderr, " live=%d atom=%p name=%s from=%s\n", (*it)->live(), *it, (*it)->name(), (file ? file->path() : "")); + } + } + + 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()); + } + + if ( log ) { + fprintf(stderr, "deadStripOptimize() %ld remaining atoms\n", _atoms.size()); for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { - fprintf(stderr, " live=%d name=%s\n", (*it)->live(), (*it)->name()); + fprintf(stderr, " live=%d atom=%p name=%s\n", (*it)->live(), *it, (*it)->name()); } } - _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLive()), _atoms.end()); } +// This is called when LTO is used but -dead_strip is not used. +// Some undefines were eliminated by LTO, but others were not. +void Resolver::remainingUndefines(std::vector& undefs) +{ + StringSet undefSet; + // search all atoms for references that are unbound + for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + const ld::Atom* atom = *it; + for (ld::Fixup::iterator fit=atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + switch ( (ld::Fixup::TargetBinding)fit->binding ) { + case ld::Fixup::bindingByNameUnbound: + assert(0 && "should not be by-name this late"); + undefSet.insert(fit->u.name); + break; + case ld::Fixup::bindingsIndirectlyBound: + if ( _internal.indirectBindingTable[fit->u.bindingIndex] == NULL ) { + undefSet.insert(_symbolTable.indirectName(fit->u.bindingIndex)); + } + break; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingNone: + case ld::Fixup::bindingDirectlyBound: + break; + } + } + } + // look for any initial undefines that are still undefined + for (Options::UndefinesIterator uit=_options.initialUndefinesBegin(); uit != _options.initialUndefinesEnd(); ++uit) { + if ( ! _symbolTable.hasName(*uit) ) { + undefSet.insert(*uit); + } + } + + // copy set to vector + for (StringSet::const_iterator it=undefSet.begin(); it != undefSet.end(); ++it) { + fprintf(stderr, "undef: %s\n", *it); + undefs.push_back(*it); + } +} + void Resolver::liveUndefines(std::vector& undefs) { StringSet undefSet; // search all live atoms for references that are unbound for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { const ld::Atom* atom = *it; - assert(atom->live()); + if ( ! atom->live() ) + continue; for (ld::Fixup::iterator fit=atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { switch ( (ld::Fixup::TargetBinding)fit->binding ) { case ld::Fixup::bindingByNameUnbound: @@ -1008,7 +1399,7 @@ bool Resolver::printReferencedBy(const char* name, SymbolTable::IndirectBindingS ++foundReferenceCount; } else { - fprintf(stderr, " %s in %s\n", SymbolTable::demangle(atom->name()), pathLeafName(atom->file()->path())); + fprintf(stderr, " %s in %s\n", _options.demangleSymbol(atom->name()), pathLeafName(atom->file()->path())); ++foundReferenceCount; break; // if undefined used twice in a function, only show first } @@ -1047,8 +1438,10 @@ void Resolver::checkUndefines(bool force) break; } std::vector unresolvableUndefines; - if ( _options.deadCodeStrip() ) + if ( _options.deadCodeStrip() ) this->liveUndefines(unresolvableUndefines); + else if( _haveLLVMObjs ) + this->remainingUndefines(unresolvableUndefines); // LTO may have eliminated need for some undefines else _symbolTable.undefines(unresolvableUndefines); @@ -1073,7 +1466,7 @@ void Resolver::checkUndefines(bool force) for (int i=0; i < unresolvableCount; ++i) { const char* name = unresolvableUndefines[i]; unsigned int slot = _symbolTable.findSlotForName(name); - fprintf(stderr, " \"%s\", referenced from:\n", SymbolTable::demangle(name)); + fprintf(stderr, " \"%s\", referenced from:\n", _options.demangleSymbol(name)); // scan all atoms for references bool foundAtomReference = printReferencedBy(name, slot); // scan command line options @@ -1090,6 +1483,10 @@ void Resolver::checkUndefines(bool force) else if ( _options.hasReExportList() && _options.shouldReExport(name) ) { fprintf(stderr, " -reexported_symbols_list command line option\n"); } + else if ( (_options.outputKind() == Options::kDynamicExecutable) + && (_options.entryName() != NULL) && (strcmp(name, _options.entryName()) == 0) ) { + fprintf(stderr, " implicit entry/start for main executable\n"); + } else { bool isInitialUndefine = false; for (Options::UndefinesIterator uit=_options.initialUndefinesBegin(); uit != _options.initialUndefinesEnd(); ++uit) { @@ -1107,7 +1504,7 @@ void Resolver::checkUndefines(bool force) bool printedStart = false; for (SymbolTable::byNameIterator sit=_symbolTable.begin(); sit != _symbolTable.end(); sit++) { const ld::Atom* atom = *sit; - if ( (atom != NULL) && (strstr(atom->name(), name) != NULL) ) { + if ( (atom != NULL) && (atom->symbolTableInclusion() == ld::Atom::symbolTableIn) && (strstr(atom->name(), name) != NULL) ) { if ( ! printedStart ) { fprintf(stderr, " (maybe you meant: %s", atom->name()); printedStart = true; @@ -1119,6 +1516,10 @@ void Resolver::checkUndefines(bool force) } if ( printedStart ) fprintf(stderr, ")\n"); + // Add comment to error message when __ZTV symbols are undefined + if ( strncmp(name, "__ZTV", 5) == 0 ) { + fprintf(stderr, " NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.\n"); + } } } if ( doError ) @@ -1139,7 +1540,7 @@ void Resolver::checkDylibSymbolCollisions() // No warning about tentative definition conflicting with dylib definition // for each tentative definition in symbol table look for dylib that exports same symbol name if ( atom->definition() == ld::Atom::definitionTentative ) { - _inputFiles.searchLibraries(atom->name(), true, false, *this); + _inputFiles.searchLibraries(atom->name(), true, false, false, *this); } // record any overrides of weak symbols in any linked dylib if ( (atom->definition() == ld::Atom::definitionRegular) && (atom->symbolTableInclusion() == ld::Atom::symbolTableIn) ) { @@ -1154,6 +1555,7 @@ void Resolver::checkDylibSymbolCollisions() const ld::Atom* Resolver::entryPoint(bool searchArchives) { const char* symbolName = NULL; + bool makingDylib = false; switch ( _options.outputKind() ) { case Options::kDynamicExecutable: case Options::kStaticExecutable: @@ -1163,6 +1565,7 @@ const ld::Atom* Resolver::entryPoint(bool searchArchives) break; case Options::kDynamicLibrary: symbolName = _options.initFunctionName(); + makingDylib = true; break; case Options::kObjectFile: case Options::kDynamicBundle: @@ -1174,7 +1577,7 @@ const ld::Atom* Resolver::entryPoint(bool searchArchives) SymbolTable::IndirectBindingSlot slot = _symbolTable.findSlotForName(symbolName); if ( (_internal.indirectBindingTable[slot] == NULL) && searchArchives ) { // ld64 can not find a -e entry point from an archive - _inputFiles.searchLibraries(symbolName, false, true, *this); + _inputFiles.searchLibraries(symbolName, false, true, false, *this); } if ( _internal.indirectBindingTable[slot] == NULL ) { if ( strcmp(symbolName, "start") == 0 ) @@ -1182,6 +1585,10 @@ const ld::Atom* Resolver::entryPoint(bool searchArchives) else throwf("entry point (%s) undefined.", symbolName); } + else if ( _internal.indirectBindingTable[slot]->definition() == ld::Atom::definitionProxy ) { + if ( makingDylib ) + throwf("-init function (%s) found in linked dylib, must be in dylib being linked", symbolName); + } return _internal.indirectBindingTable[slot]; } return NULL; @@ -1231,7 +1638,7 @@ void Resolver::fillInHelpersInInternalState() if ( needsStubHelper && _options.makeCompressedDyldInfo() ) { // "dyld_stub_binder" comes from libSystem.dylib so will need to manually resolve if ( !_symbolTable.hasName("dyld_stub_binder") ) { - _inputFiles.searchLibraries("dyld_stub_binder", true, false, *this); + _inputFiles.searchLibraries("dyld_stub_binder", true, false, false, *this); } if ( _symbolTable.hasName("dyld_stub_binder") ) { SymbolTable::IndirectBindingSlot slot = _symbolTable.findSlotForName("dyld_stub_binder"); @@ -1243,9 +1650,6 @@ void Resolver::fillInHelpersInInternalState() _internal.compressedFastBinderProxy = new UndefinedProxyAtom("dyld_stub_binder"); this->doAtom(*_internal.compressedFastBinderProxy); } - else { - warning("symbol dyld_stub_binder not found, normally in libSystem.dylib"); - } } } } @@ -1260,17 +1664,60 @@ 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(ld::Section("__TEXT", "__text", ld::Section::typeCode)); + _internal.getFinalSection(*new ld::Section("__TEXT", "__text", ld::Section::typeCode)); +} - // add entry point +void Resolver::fillInEntryPoint() +{ _internal.entryPoint = this->entryPoint(true); } - +void Resolver::syncAliases() +{ + if ( !_haveAliases || (_options.outputKind() == Options::kObjectFile) ) + return; + + // Set attributes of alias to match its found target + for (std::vector::iterator it = _atoms.begin(); it != _atoms.end(); ++it) { + const ld::Atom* atom = *it; + if ( atom->section().type() == ld::Section::typeTempAlias ) { + assert(atom->fixupsBegin() != atom->fixupsEnd()); + for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + const ld::Atom* target; + ld::Atom::Scope scope; + assert(fit->kind == ld::Fixup::kindNoneFollowOn); + switch ( fit->binding ) { + case ld::Fixup::bindingByNameUnbound: + break; + case ld::Fixup::bindingsIndirectlyBound: + target = _internal.indirectBindingTable[fit->u.bindingIndex]; + assert(target != NULL); + scope = atom->scope(); + (const_cast(atom))->setAttributesFromAtom(*target); + // alias has same attributes as target, except for scope + (const_cast(atom))->setScope(scope); + break; + default: + assert(0 && "internal error: unexpected alias binding"); + } + } + } + } +} void Resolver::removeCoalescedAwayAtoms() { + const bool log = false; + if ( log ) { + fprintf(stderr, "removeCoalescedAwayAtoms() starts with %lu atoms\n", _atoms.size()); + } _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), AtomCoalescedAway()), _atoms.end()); + if ( log ) { + fprintf(stderr, "removeCoalescedAwayAtoms() after removing coalesced atoms, %lu remain\n", _atoms.size()); + for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + fprintf(stderr, " atom=%p %s\n", *it, (*it)->name()); + } + } } void Resolver::linkTimeOptimize() @@ -1278,73 +1725,79 @@ void Resolver::linkTimeOptimize() // only do work here if some llvm obj files where loaded if ( ! _haveLLVMObjs ) return; - + + // LTO: Symbol multiply defined error should specify exactly where the symbol is found + _symbolTable.checkDuplicateSymbols(); + // run LLVM lto code-gen lto::OptimizeOptions optOpt; optOpt.outputFilePath = _options.outputFilePath(); optOpt.tmpObjectFilePath = _options.tempLtoObjectPath(); - optOpt.allGlobalsAReDeadStripRoots = _options.allGlobalsAreDeadStripRoots(); + optOpt.ltoCachePath = _options.ltoCachePath(); + 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.relocatable = (_options.outputKind() == Options::kObjectFile); optOpt.allowTextRelocs = _options.allowTextRelocs(); optOpt.linkerDeadStripping = _options.deadCodeStrip(); + optOpt.needsUnwindInfoSection = _options.needsUnwindInfoSection(); + 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)); + optOpt.bitcodeBundle = _options.bundleBitcode(); + optOpt.maxDefaultCommonAlignment = _options.maxDefaultCommonAlign(); optOpt.arch = _options.architecture(); + optOpt.mcpu = _options.mcpuLTO(); + optOpt.platform = _options.platform(); + optOpt.minOSVersion = _options.minOSversion(); optOpt.llvmOptions = &_options.llvmOptions(); + optOpt.initialUndefines = &_options.initialUndefines(); std::vector newAtoms; std::vector additionalUndefines; - if ( ! lto::optimize(_atoms, _internal, _inputFiles.nextInputOrdinal(), optOpt, *this, newAtoms, additionalUndefines) ) + if ( ! lto::optimize(_atoms, _internal, optOpt, *this, newAtoms, additionalUndefines) ) return; // if nothing done - + _ltoCodeGenFinished = true; // add all newly created atoms to _atoms and update symbol table for(std::vector::iterator it = newAtoms.begin(); it != newAtoms.end(); ++it) this->doAtom(**it); - + // some atoms might have been optimized way (marked coalesced), remove them this->removeCoalescedAwayAtoms(); - - // add new atoms into their final section - for (std::vector::iterator it = newAtoms.begin(); it != newAtoms.end(); ++it) { - _internal.addAtom(**it); - } - // remove temp lto section and move all of its atoms to their final section - ld::Internal::FinalSection* tempLTOsection = NULL; - for (std::vector::iterator sit=_internal.sections.begin(); sit != _internal.sections.end(); ++sit) { - ld::Internal::FinalSection* sect = *sit; - if ( sect->type() == ld::Section::typeTempLTO ) { - tempLTOsection = sect; - // remove temp lto section from final image - _internal.sections.erase(sit); - break; - } - } - // lto atoms now have proper section info, so add to final section - if ( tempLTOsection != NULL ) { - for (std::vector::iterator ait=tempLTOsection->atoms.begin(); ait != tempLTOsection->atoms.end(); ++ait) { - const ld::Atom* atom = *ait; - if ( ! atom->coalescedAway() ) { - this->convertReferencesToIndirect(*atom); - _internal.addAtom(*atom); - } - } + // run through all atoms again and make sure newly codegened atoms have references bound + for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) + this->convertReferencesToIndirect(**it); + + // adjust section of any new + for (std::vector::const_iterator it=_aliasesFromCmdLine.begin(); it != _aliasesFromCmdLine.end(); ++it) { + const AliasAtom* aliasAtom = *it; + // update fields in AliasAtom to match newly constructed mach-o atom + aliasAtom->setFinalAliasOf(); } + // add any auto-link libraries requested by LTO output to dylibs to search + _inputFiles.addLinkerOptionLibraries(_internal, *this); + _inputFiles.createIndirectDylibs(); + // resolve new undefines (e.g calls to _malloc and _memcpy that llvm compiler conjures up) - _addToFinalSection = true; for(std::vector::iterator uit = additionalUndefines.begin(); uit != additionalUndefines.end(); ++uit) { const char *targetName = *uit; // these symbols may or may not already be in linker's symbol table if ( ! _symbolTable.hasName(targetName) ) { - _inputFiles.searchLibraries(targetName, true, true, *this); + _inputFiles.searchLibraries(targetName, true, true, false, *this); } } - _addToFinalSection = false; // if -dead_strip on command line if ( _options.deadCodeStrip() ) { @@ -1353,23 +1806,28 @@ void Resolver::linkTimeOptimize() (const_cast(*it))->setLive((*it)->dontDeadStrip()); } // and re-compute dead code - this->deadStripOptimize(); - - // remove newly dead atoms from each section - for (std::vector::iterator sit=_internal.sections.begin(); sit != _internal.sections.end(); ++sit) { - ld::Internal::FinalSection* sect = *sit; - sect->atoms.erase(std::remove_if(sect->atoms.begin(), sect->atoms.end(), NotLive()), sect->atoms.end()); + 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) { + const ld::Atom* atom = *it; + if ( atom->scope() == ld::Atom::scopeGlobal ) { + if ( !_options.shouldExport(atom->name()) ) { + (const_cast(atom))->setScope(ld::Atom::scopeLinkageUnit); + } + } } } if ( _options.outputKind() == Options::kObjectFile ) { // if -r mode, add proxies for new undefines (e.g. ___stack_chk_fail) - _addToFinalSection = true; this->resolveUndefines(); - _addToFinalSection = false; } else { // last chance to check for undefines + this->resolveUndefines(); this->checkUndefines(true); // check new code does not override some dylib @@ -1378,6 +1836,53 @@ void Resolver::linkTimeOptimize() } +void Resolver::tweakWeakness() +{ + // Add command line options to control symbol weak-def bit on exported symbols + if ( _options.hasWeakBitTweaks() ) { + for (std::vector::iterator sit = _internal.sections.begin(); sit != _internal.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( atom->definition() != ld::Atom::definitionRegular ) + continue; + const char* name = atom->name(); + if ( atom->scope() == ld::Atom::scopeGlobal ) { + if ( atom->combine() == ld::Atom::combineNever ) { + if ( _options.forceWeak(name) ) + (const_cast(atom))->setCombine(ld::Atom::combineByName); + } + else if ( atom->combine() == ld::Atom::combineByName ) { + if ( _options.forceNotWeak(name) ) + (const_cast(atom))->setCombine(ld::Atom::combineNever); + } + } + else { + if ( _options.forceWeakNonWildCard(name) ) + warning("cannot force to be weak, non-external symbol %s", name); + else if ( _options.forceNotWeakNonWildcard(name) ) + warning("cannot force to be not-weak, non-external symbol %s", name); + } + } + } + } +} + +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"); + for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + const ld::Atom* atom = *it; + fprintf(stderr, " %p name=%s, def=%d\n", atom, atom->name(), atom->definition()); + } +} + void Resolver::resolve() { this->initializeState(); @@ -1388,9 +1893,14 @@ void Resolver::resolve() this->deadStripOptimize(); this->checkUndefines(); this->checkDylibSymbolCollisions(); + this->syncAliases(); this->removeCoalescedAwayAtoms(); - this->fillInInternalState(); + this->fillInEntryPoint(); this->linkTimeOptimize(); + this->fillInInternalState(); + this->tweakWeakness(); + _symbolTable.checkDuplicateSymbols(); + this->buildArchivesList(); }