X-Git-Url: https://git.saurik.com/apple/ld64.git/blobdiff_plain/d425e3882ca60fabae080ddb890789ef2e73a66b..599556ff3dd31aab68bb9685f1ed7fc4867803e7:/src/ld/Resolver.cpp diff --git a/src/ld/Resolver.cpp b/src/ld/Resolver.cpp index ad4b22d..a5405a9 100644 --- a/src/ld/Resolver.cpp +++ b/src/ld/Resolver.cpp @@ -132,6 +132,7 @@ public: void setFinalAliasOf() const { (const_cast(this))->setAttributesFromAtom(_aliasOf); + (const_cast(this))->setScope(ld::Atom::scopeGlobal); } private: @@ -281,13 +282,21 @@ void Resolver::initializeState() _internal.objcObjectConstraint = ld::File::objcConstraintGC; _internal.cpuSubType = _options.subArchitecture(); + + // 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; @@ -295,12 +304,60 @@ void Resolver::buildAtomList() } +void Resolver::doLinkerOption(const std::vector& linkerOption, const char* fileName) +{ + if ( linkerOption.size() == 1 ) { + const char* lo1 = linkerOption.front(); + if ( strncmp(lo1, "-l", 2) == 0 ) { + _internal.linkerOptionLibraries.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 ) { + _internal.linkerOptionFrameworks.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; + default: + sprintf(versionString, "unknown ABI version 0x%02X", value); + } +} + void Resolver::doFile(const ld::File& file) { const ld::relocatable::File* objFile = dynamic_cast(&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 ) { + for (relocatable::File::LinkerOptionsList::const_iterator it=lo->begin(); it != lo->end(); ++it) { + this->doLinkerOption(*it, file.path()); + } + } + // update which form of ObjC is being used switch ( file.objCConstraint() ) { case ld::File::objcConstraintNone: @@ -312,19 +369,55 @@ 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; @@ -360,7 +453,16 @@ void Resolver::doFile(const ld::File& file) 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; } } @@ -376,25 +478,62 @@ 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; } + if ( _options.checkDylibsAreAppExtensionSafe() && !dylibFile->appExtensionSafe() ) { + warning("linking against dylib 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->iOSMinVersion() >= 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, @loaderpath, etc.", depInstallName, dylibFile->path()); + 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) { - //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); @@ -486,7 +625,11 @@ void Resolver::doAtom(const ld::Atom& atom) // remember if any atoms are proxies that require LTO if ( atom.contentType() == ld::Atom::typeLTOtemporary ) _haveLLVMObjs = true; - + + // 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() ) @@ -510,6 +653,8 @@ 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::kindDtraceExtra: @@ -527,6 +672,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 ) ) { @@ -638,14 +785,34 @@ void Resolver::resolveUndefines() } } } - + + // 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)); } } @@ -697,6 +864,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: @@ -713,6 +881,12 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoadNowLEA: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64Branch26: + case ld::Fixup::kindStoreTargetAddressARM64Page21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: +#endif if ( fit->binding == ld::Fixup::bindingByContentBound ) { // normally this was done in convertReferencesToIndirect() // but a archive loaded .o file may have a forward reference @@ -857,6 +1031,7 @@ void Resolver::deadStripOptimize(bool force) if ( _haveLLVMObjs && !force ) { // 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 { _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLive()), _atoms.end()); @@ -1302,7 +1477,38 @@ 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() { @@ -1325,6 +1531,9 @@ void Resolver::linkTimeOptimize() 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(); @@ -1338,19 +1547,24 @@ void Resolver::linkTimeOptimize() 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.arch = _options.architecture(); + optOpt.mcpu = _options.mcpuLTO(); optOpt.llvmOptions = &_options.llvmOptions(); + optOpt.initialUndefines = &_options.initialUndefines(); std::vector newAtoms; std::vector 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(); @@ -1365,6 +1579,10 @@ void Resolver::linkTimeOptimize() 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) for(std::vector::iterator uit = additionalUndefines.begin(); uit != additionalUndefines.end(); ++uit) { const char *targetName = *uit; @@ -1402,6 +1620,7 @@ void Resolver::linkTimeOptimize() } else { // last chance to check for undefines + this->resolveUndefines(); this->checkUndefines(true); // check new code does not override some dylib @@ -1442,6 +1661,14 @@ void Resolver::tweakWeakness() } } +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() { @@ -1453,6 +1680,7 @@ void Resolver::resolve() this->deadStripOptimize(); this->checkUndefines(); this->checkDylibSymbolCollisions(); + this->syncAliases(); this->removeCoalescedAwayAtoms(); this->fillInEntryPoint(); this->linkTimeOptimize();