diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -34,6 +34,13 @@ class ThunkSection; struct OutputDesc; +enum AssignFlags { + AF_None = 0, + // This is the address-assignment final pass; region overflow errors should + // only be enforced when this is set. + AF_FinalPass = (1 << 0), +}; + // This represents an r-value in the linker script. struct ExprValue { ExprValue(SectionBase *sec, bool forceAbsolute, uint64_t val, @@ -271,10 +278,10 @@ llvm::DenseMap nameToOutputSection; void addSymbol(SymbolAssignment *cmd); - void assignSymbol(SymbolAssignment *cmd, bool inSec); - void setDot(Expr e, const Twine &loc, bool inSec); - void expandOutputSection(uint64_t size); - void expandMemoryRegions(uint64_t size); + void assignSymbol(SymbolAssignment *cmd, bool inSec, AssignFlags flags); + void setDot(Expr e, const Twine &loc, bool inSec, AssignFlags flags); + void expandOutputSection(uint64_t size, AssignFlags flags); + void expandMemoryRegions(uint64_t size, AssignFlags flags); SmallVector computeInputSections(const InputSectionDescription *, @@ -289,7 +296,7 @@ std::pair findMemoryRegion(OutputSection *sec, MemoryRegion *hint); - void assignOffsets(OutputSection *sec); + void assignOffsets(OutputSection *sec, AssignFlags flags); // This captures the local AddressState and makes it accessible // deliberately. This is needed as there are some cases where we cannot just @@ -322,7 +329,7 @@ bool needsInterpSection(); bool shouldKeep(InputSectionBase *s); - const Defined *assignAddresses(); + const Defined *assignAddresses(AssignFlags flags); void allocateHeaders(SmallVector &phdrs); void processSectionCommands(); void processSymbolAssignments(); diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -155,30 +155,31 @@ // Expands the memory region by the specified size. static void expandMemoryRegion(MemoryRegion *memRegion, uint64_t size, - StringRef secName) { + StringRef secName, AssignFlags flags) { memRegion->curPos += size; uint64_t newSize = memRegion->curPos - (memRegion->origin)().getValue(); uint64_t length = (memRegion->length)().getValue(); - if (newSize > length) + if ((flags & AF_FinalPass) && newSize > length) error("section '" + secName + "' will not fit in region '" + memRegion->name + "': overflowed by " + Twine(newSize - length) + " bytes"); } -void LinkerScript::expandMemoryRegions(uint64_t size) { +void LinkerScript::expandMemoryRegions(uint64_t size, AssignFlags flags) { if (state->memRegion) - expandMemoryRegion(state->memRegion, size, state->outSec->name); + expandMemoryRegion(state->memRegion, size, state->outSec->name, flags); // Only expand the LMARegion if it is different from memRegion. if (state->lmaRegion && state->memRegion != state->lmaRegion) - expandMemoryRegion(state->lmaRegion, size, state->outSec->name); + expandMemoryRegion(state->lmaRegion, size, state->outSec->name, flags); } -void LinkerScript::expandOutputSection(uint64_t size) { +void LinkerScript::expandOutputSection(uint64_t size, AssignFlags flags) { state->outSec->size += size; - expandMemoryRegions(size); + expandMemoryRegions(size, flags); } -void LinkerScript::setDot(Expr e, const Twine &loc, bool inSec) { +void LinkerScript::setDot(Expr e, const Twine &loc, bool inSec, + AssignFlags flags) { uint64_t val = e().getValue(); if (val < dot && inSec) error(loc + ": unable to move location counter backward for: " + @@ -186,7 +187,7 @@ // Update to location counter means update to section size. if (inSec) - expandOutputSection(val - dot); + expandOutputSection(val - dot, flags); dot = val; } @@ -364,9 +365,10 @@ // This function is called from assignAddresses, while we are // fixing the output section addresses. This function is supposed // to set the final value for a given symbol assignment. -void LinkerScript::assignSymbol(SymbolAssignment *cmd, bool inSec) { +void LinkerScript::assignSymbol(SymbolAssignment *cmd, bool inSec, + AssignFlags flags) { if (cmd->name == ".") { - setDot(cmd->expression, cmd->location, inSec); + setDot(cmd->expression, cmd->location, inSec, flags); return; } @@ -957,7 +959,7 @@ // This function assigns offsets to input sections and an output section // for a single sections command (e.g. ".text { *(.text); }"). -void LinkerScript::assignOffsets(OutputSection *sec) { +void LinkerScript::assignOffsets(OutputSection *sec, AssignFlags flags) { const bool isTbss = (sec->flags & SHF_TLS) && sec->type == SHT_NOBITS; const bool sameMemRegion = state->memRegion == sec->memRegion; const bool prevLMARegionIsDefault = state->lmaRegion == nullptr; @@ -979,7 +981,7 @@ if (state->memRegion) dot = state->memRegion->curPos; if (sec->addrExpr) - setDot(sec->addrExpr, sec->location, false); + setDot(sec->addrExpr, sec->location, false, flags); // If the address of the section has been moved forward by an explicit // expression so that it now starts past the current curPos of the enclosing @@ -987,7 +989,7 @@ // between the previous section, if any, and the start of this section. if (state->memRegion && state->memRegion->curPos < dot) expandMemoryRegion(state->memRegion, dot - state->memRegion->curPos, - sec->name); + sec->name, flags); } state->outSec = sec; @@ -1000,7 +1002,7 @@ const uint64_t pos = dot; dot = alignToPowerOf2(dot, sec->addralign); sec->addr = dot; - expandMemoryRegions(dot - pos); + expandMemoryRegions(dot - pos, flags); } // state->lmaOffset is LMA minus VMA. If LMA is explicitly specified via AT() @@ -1014,7 +1016,7 @@ } else if (MemoryRegion *mr = sec->lmaRegion) { uint64_t lmaStart = alignToPowerOf2(mr->curPos, sec->addralign); if (mr->curPos < lmaStart) - expandMemoryRegion(mr, lmaStart - mr->curPos, sec->name); + expandMemoryRegion(mr, lmaStart - mr->curPos, sec->name, flags); state->lmaOffset = lmaStart - dot; } else if (!sameMemRegion || !prevLMARegionIsDefault) { state->lmaOffset = 0; @@ -1036,7 +1038,7 @@ // This handles the assignments to symbol or to the dot. if (auto *assign = dyn_cast(cmd)) { assign->addr = dot; - assignSymbol(assign, true); + assignSymbol(assign, true, flags); assign->size = dot - assign->addr; continue; } @@ -1045,7 +1047,7 @@ if (auto *data = dyn_cast(cmd)) { data->offset = dot - sec->addr; dot += data->size; - expandOutputSection(data->size); + expandOutputSection(data->size, flags); continue; } @@ -1062,7 +1064,7 @@ // Update output section size after adding each section. This is so that // SIZEOF works correctly in the case below: // .foo { *(.aaa) a = SIZEOF(.foo); *(.bbb) } - expandOutputSection(dot - pos); + expandOutputSection(dot - pos, flags); } } @@ -1300,7 +1302,7 @@ // we also handle rest commands like symbol assignments and ASSERTs. // Returns a symbol that has changed its section or value, or nullptr if no // symbol has changed. -const Defined *LinkerScript::assignAddresses() { +const Defined *LinkerScript::assignAddresses(AssignFlags flags) { if (script->hasSectionsCommand) { // With a linker script, assignment of addresses to headers is covered by // allocateHeaders(). @@ -1322,11 +1324,11 @@ for (SectionCommand *cmd : sectionCommands) { if (auto *assign = dyn_cast(cmd)) { assign->addr = dot; - assignSymbol(assign, false); + assignSymbol(assign, false, flags); assign->size = dot - assign->addr; continue; } - assignOffsets(&cast(cmd)->osec); + assignOffsets(&cast(cmd)->osec, flags); } state = nullptr; diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1627,7 +1627,7 @@ ThunkCreator tc; AArch64Err843419Patcher a64p; ARMErr657417Patcher a32p; - script->assignAddresses(); + script->assignAddresses(AF_None); // .ARM.exidx and SHF_LINK_ORDER do not require precise addresses, but they // do require the relative addresses of OutputSections because linker scripts // can assign Virtual Addresses to OutputSections that are not monotonically @@ -1656,12 +1656,12 @@ if (config->fixCortexA53Errata843419) { if (changed) - script->assignAddresses(); + script->assignAddresses(AF_None); changed |= a64p.createFixes(); } if (config->fixCortexA8) { if (changed) - script->assignAddresses(); + script->assignAddresses(AF_None); changed |= a32p.createFixes(); } @@ -1674,7 +1674,7 @@ changed |= part.relrDyn->updateAllocSize(); } - const Defined *changedSym = script->assignAddresses(); + const Defined *changedSym = script->assignAddresses(AF_None); if (!changed) { // Some symbols may be dependent on section addresses. When we break the // loop, the symbol values are finalized because a previous @@ -1688,6 +1688,9 @@ } } } + + script->assignAddresses(AF_FinalPass); + if (!config->relocatable && config->emachine == EM_RISCV) riscvFinalizeRelax(pass); @@ -1758,7 +1761,7 @@ assert(config->optimizeBBJumps); SmallVector storage; - script->assignAddresses(); + script->assignAddresses(AF_None); // For every output section that has executable input sections, this // does the following: // 1. Deletes all direct jump instructions in input sections that @@ -1779,7 +1782,7 @@ numDeleted += target->deleteFallThruJmpInsn(sec, sec.file, next); } if (numDeleted > 0) { - script->assignAddresses(); + script->assignAddresses(AF_None); LLVM_DEBUG(llvm::dbgs() << "Removing " << numDeleted << " fall through jumps\n"); } diff --git a/lld/test/ELF/riscv-relax-call-region-overflow.s b/lld/test/ELF/riscv-relax-call-region-overflow.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/riscv-relax-call-region-overflow.s @@ -0,0 +1,39 @@ +# REQUIRES: riscv + +# RUN: rm -rf %t && split-file %s %t && cd %t + +## Check that relaxation prevents region overflow + +# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+c,+relax a.s -o a.32c.o +# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+c,+relax b.s -o b.32c.o +# RUN: ld.lld -T lds a.32c.o b.32c.o -o 32c +# RUN: llvm-objdump --section-headers 32c | FileCheck %s --check-prefixes=RELAX_SECTIONS +# RELAX_SECTIONS: 1 .text 0000000a 00000000 TEXT + +## Check that we still overflow with relaxation disabled + +# RUN: not ld.lld -T lds a.32c.o b.32c.o --no-relax -o /dev/null 2>&1 | FileCheck --check-prefix=ERR0 %s +# ERR0: ld.lld: error: section '.text' will not fit in region 'ROM' + +#--- a.s +.global _start +_start: + # These calls can be relaxed to be much smaller, enough to fit within the + # tiny ROM region + call bar + call bar + call bar + call bar + +#--- b.s +.global bar +bar: + ret + +#--- lds +MEMORY { + ROM (rx) : ORIGIN = 0, LENGTH = 12 +} +SECTIONS { + .text 0x00000 : { *(.text) } > ROM +}