]> git.saurik.com Git - apple/ld64.git/blobdiff - src/ld/Resolver.cpp
ld64-241.9.tar.gz
[apple/ld64.git] / src / ld / Resolver.cpp
index ad4b22dbd287eaa7c81666574f7a683ad03354a2..a5405a9951057673fb0cdf1cd9c5a629acebdec2 100644 (file)
@@ -132,6 +132,7 @@ public:
 
        void                                                                    setFinalAliasOf() const {
                                                                                                (const_cast<AliasAtom*>(this))->setAttributesFromAtom(_aliasOf);
+                                                                                               (const_cast<AliasAtom*>(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<const char*>& 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<const ld::relocatable::File*>(&file);
        const ld::dylib::File* dylibFile = dynamic_cast<const ld::dylib::File*>(&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();
+               // <rdar://problem/17229513> embedded frameworks are only supported on iOS 8 and later
+               if ( (depInstallName != NULL) && (depInstallName[0] != '/') ) {
+                       if ( (_options.iOSVersionMin() != iOSVersionUnset) && (_options.iOSVersionMin() < iOS_8_0) ) {
+                               // <rdar://problem/17598404> 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<const char*> undefineNames;
+               _symbolTable.undefines(undefineNames);
+               if ( undefineNames.size() != 0 ) {
+                       for (std::vector<const char*>::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<const char*> undefineNames;
                _symbolTable.undefines(undefineNames);
                for(std::vector<const char*>::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) {
-                       // make proxy
-                       this->doAtom(*new UndefinedProxyAtom(*it));
+                       const char* undefName = *it;
+                       // <rdar://problem/14547001> "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 ) {
                // <rdar://problem/9777977> 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<const ld::Atom*>::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*>(atom))->setAttributesFromAtom(*target);
+                                               // alias has same attributes as target, except for scope
+                                               (const_cast<Atom*>(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;
 
+       // <rdar://problem/15314161> 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<const ld::Atom*>            newAtoms;
        std::vector<const char*>                        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<const ld::Atom*>::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();
        }
        
+       // <rdar://problem/14609792> 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<const char*>::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 ld::Atom*>::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();