Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -271,7 +271,7 @@ bool needsInterpSection(); bool shouldKeep(InputSectionBase *s); - void assignAddresses(); + const Defined *assignAddresses(); void allocateHeaders(std::vector &phdrs); void processSectionCommands(); void declareSymbols(); Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -1054,7 +1054,9 @@ // Here we assign addresses as instructed by linker script SECTIONS // sub-commands. Doing that allows us to use final VA values, so here // we also handle rest commands like symbol assignments and ASSERTs. -void LinkerScript::assignAddresses() { +// Returns a symbol that has changed its section or value, or nullptr if no +// symbol has changed. +const Defined *LinkerScript::assignAddresses() { dot = getInitialDot(); auto deleter = std::make_unique(); @@ -1062,6 +1064,13 @@ errorOnMissingSection = true; switchTo(aether); + DenseMap> old; + for (BaseCommand *base : sectionCommands) + if (auto *cmd = dyn_cast(base)) + if (cmd->sym) + old.try_emplace(cmd->sym, + std::make_pair(cmd->sym->section, cmd->sym->value)); + for (BaseCommand *base : sectionCommands) { if (auto *cmd = dyn_cast(base)) { cmd->addr = dot; @@ -1072,6 +1081,17 @@ assignOffsets(cast(base)); } ctx = nullptr; + + // Return the lexicographical smallest (for determinism) Defined whose + // section/value has changed. + const Defined *changed = nullptr; + for (auto &it : old) { + const Defined *sym = it.first; + if ((sym->section != it.second.first || sym->value != it.second.second) && + (!changed || sym->getName() < changed->getName())) + return sym; + } + return nullptr; } // Creates program headers as instructed by PHDRS linker script command. Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -580,8 +580,6 @@ if (errorCount()) return; - script->assignAddresses(); - // If -compressed-debug-sections is specified, we need to compress // .debug_* sections. Do it right now because it changes the size of // output sections. @@ -1567,15 +1565,12 @@ template void Writer::finalizeAddressDependentContent() { ThunkCreator tc; AArch64Err843419Patcher a64p; + script->assignAddresses(); // For some targets, like x86, this loop iterates only once. + int tries = 5; for (;;) { - bool changed = false; - - script->assignAddresses(); - - if (target->needsThunks) - changed |= tc.createThunks(outputSections); + bool changed = target->needsThunks && tc.createThunks(outputSections); if (config->fixCortexA53Errata843419) { if (changed) @@ -1592,8 +1587,16 @@ changed |= part.relrDyn->updateAllocSize(); } - if (!changed) - return; + const Defined *changedSym = script->assignAddresses(); + if (!changed) { + if (!changedSym) + break; + if (--tries == 0) { + errorOrWarn("assignment to symbol " + toString(*changedSym) + + " does not converge"); + break; + } + } } } Index: test/ELF/linkerscript/expr-converge.test =================================================================== --- /dev/null +++ test/ELF/linkerscript/expr-converge.test @@ -0,0 +1,13 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64 /dev/null -o %t.o +# RUN: ld.lld %t.o -T %s -o %t +# RUN: llvm-nm %t | FileCheck %s + +# CHECK: 000000000000ffff T a +# CHECK: 000000000000ffff T b + +a = b; +SECTIONS { + . = 0xffff + (b >> 8) - 0xff; + b = .; +}