void setFinalAliasOf() const {
(const_cast<AliasAtom*>(this))->setAttributesFromAtom(_aliasOf);
+ (const_cast<AliasAtom*>(this))->setScope(ld::Atom::scopeGlobal);
}
private:
_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;
}
+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:
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;
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;
}
}
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);
// 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() )
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:
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 ) ) {
}
}
}
-
+
+ // 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));
}
}
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:
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
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());
_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()
{
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();
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();
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;
}
else {
// last chance to check for undefines
+ this->resolveUndefines();
this->checkUndefines(true);
// check new code does not override some dylib
}
}
+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()
{
this->deadStripOptimize();
this->checkUndefines();
this->checkDylibSymbolCollisions();
+ this->syncAliases();
this->removeCoalescedAwayAtoms();
this->fillInEntryPoint();
this->linkTimeOptimize();