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 @@ -1049,10 +1049,31 @@ return std::min(startAddr, target->getImageBase() + elf::getHeaderSize()); } +static DenseMap> +getSymbolAssignmentValues(std::vector sectionCommands) { + DenseMap> ret; + for (BaseCommand *base : sectionCommands) { + if (auto *cmd = dyn_cast(base)) { + if (cmd->sym) + ret.try_emplace(cmd->sym, + std::make_pair(cmd->sym->section, cmd->sym->value)); + continue; + } + for (BaseCommand *base1 : cast(base)->sectionCommands) + if (auto *cmd = dyn_cast(base1)) + if (cmd->sym) + ret.try_emplace(cmd->sym, + std::make_pair(cmd->sym->section, cmd->sym->value)); + } + return ret; +} + // 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(); @@ -1060,6 +1081,9 @@ errorOnMissingSection = true; switchTo(aether); + DenseMap> oldValues = + getSymbolAssignmentValues(sectionCommands); + for (BaseCommand *base : sectionCommands) { if (auto *cmd = dyn_cast(base)) { cmd->addr = dot; @@ -1070,6 +1094,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 : oldValues) { + const Defined *sym = it.first; + if ((std::make_pair(sym->section, sym->value) != it.second) && + (!changed || sym->getName() < changed->getName())) + changed = sym; + } + return changed; } // Creates program headers as instructed by PHDRS linker script command. Index: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ ELF/Relocations.cpp @@ -1700,11 +1700,6 @@ if (pass == 0 && target->getThunkSectionSpacing()) createInitialThunkSections(outputSections); - // With Thunk Size much smaller than branch range we expect to - // converge quickly; if we get to 10 something has gone wrong. - if (pass == 10) - fatal("thunk creation not converged"); - // Create all the Thunks and insert them into synthetic ThunkSections. The // ThunkSections are later inserted back into InputSectionDescriptions. // We separate the creation of ThunkSections from the insertion of the 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,18 @@ template void Writer::finalizeAddressDependentContent() { ThunkCreator tc; AArch64Err843419Patcher a64p; + script->assignAddresses(); - // For some targets, like x86, this loop iterates only once. + int assignPasses = 0; for (;;) { - bool changed = false; + bool changed = target->needsThunks && tc.createThunks(outputSections); - script->assignAddresses(); - - if (target->needsThunks) - changed |= tc.createThunks(outputSections); + // With Thunk Size much smaller than branch range we expect to + // converge quickly; if we get to 10 something has gone wrong. + if (changed && tc.pass >= 10) { + error("thunk creation not converged"); + break; + } if (config->fixCortexA53Errata843419) { if (changed) @@ -1592,8 +1593,19 @@ changed |= part.relrDyn->updateAllocSize(); } - if (!changed) - return; + const Defined *changedSym = script->assignAddresses(); + if (!changed) { + // Some symbols may be dependent on section addresses. When we break the + // loop, the symbol values are finalized because a previous + // assignAddresses() finalized section addresses. + if (!changedSym) + break; + if (++assignPasses == 5) { + errorOrWarn("assignment to symbol " + toString(*changedSym) + + " does not converge"); + break; + } + } } } Index: test/ELF/linkerscript/symbol-assign-many-passes.test =================================================================== --- /dev/null +++ test/ELF/linkerscript/symbol-assign-many-passes.test @@ -0,0 +1,25 @@ +# REQUIRES: aarch64, 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 + +## AArch64 needs thunks and has different address finalization process, so test +## it as well. +# RUN: llvm-mc -filetype=obj -triple=aarch64 /dev/null -o %t.o +# RUN: ld.lld %t.o -T %s -o %t +# RUN: llvm-nm %t | FileCheck %s + +# CHECK: 0000000000001004 T a +# CHECK: 0000000000001003 T b +# CHECK: 0000000000001002 T c +# CHECK: 0000000000001001 T d +# CHECK: 0000000000001000 T e + +SECTIONS { + . = 0x1000; + a = b + 1; + b = c + 1; + c = d + 1; + d = e + 1; + e = .; +} Index: test/ELF/linkerscript/symbol-assign-many-passes2.test =================================================================== --- /dev/null +++ test/ELF/linkerscript/symbol-assign-many-passes2.test @@ -0,0 +1,28 @@ +# REQUIRES: arm +# RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=armv7a-linux-gnueabihf %S/../arm-thunk-many-passes.s -o %t.o +# RUN: ld.lld %t.o -T %s -o %t +# RUN: llvm-nm %t | FileCheck %s + +## arm-thunk-many-passes.s is worst case case of thunk generation that takes 9 +## passes to converge. It takes a few more passes to make symbol assignment +## converge. Test that +## 1. we don't error that "thunk creation not converged". +## 2. we check convergence of symbols defined in an output section descriptor. + +# CHECK: 01011050 T a +# CHECK: 0101104f T b +# CHECK: 0101104e T c +# CHECK: 0101104d T d +# CHECK: 0101104c T e + +SECTIONS { + . = SIZEOF_HEADERS; + .text 0x00011000 : { + a = b + 1; + b = c + 1; + c = d + 1; + d = e + 1; + *(.text); + } + e = .; +} Index: test/ELF/linkerscript/symbol-assign-not-converge.test =================================================================== --- /dev/null +++ test/ELF/linkerscript/symbol-assign-not-converge.test @@ -0,0 +1,20 @@ +# REQUIRES: aarch64, x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64 /dev/null -o %t.o +# RUN: not ld.lld %t.o -T %s -o /dev/null 2>&1 | FileCheck %s + +## AArch64 needs thunks and has different address finalization process, so test +## it as well. +# RUN: llvm-mc -filetype=obj -triple=aarch64 /dev/null -o %t.o +# RUN: not ld.lld %t.o -T %s -o /dev/null 2>&1 | FileCheck %s + +# CHECK: error: assignment to symbol a does not converge + +SECTIONS { + . = 0x1000; + a = b; + b = c; + c = d; + d = e; + e = f; + f = .; +}