Index: lld/ELF/LinkerScript.h =================================================================== --- lld/ELF/LinkerScript.h +++ lld/ELF/LinkerScript.h @@ -274,10 +274,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, bool verify); + void setDot(Expr e, const Twine &loc, bool inSec, bool verify); + void expandOutputSection(uint64_t size, bool verify); + void expandMemoryRegions(uint64_t size, bool verify); SmallVector computeInputSections(const InputSectionDescription *, @@ -292,7 +292,7 @@ std::pair findMemoryRegion(OutputSection *sec, MemoryRegion *hint); - void assignOffsets(OutputSection *sec); + void assignOffsets(OutputSection *sec, bool verify); // This captures the local AddressState and makes it accessible // deliberately. This is needed as there are some cases where we cannot just @@ -325,7 +325,7 @@ bool needsInterpSection(); bool shouldKeep(InputSectionBase *s); - const Defined *assignAddresses(); + const Defined *assignAddresses(bool verify); void allocateHeaders(SmallVector &phdrs); void processSectionCommands(); void processSymbolAssignments(); Index: lld/ELF/LinkerScript.cpp =================================================================== --- lld/ELF/LinkerScript.cpp +++ lld/ELF/LinkerScript.cpp @@ -155,30 +155,30 @@ // Expands the memory region by the specified size. static void expandMemoryRegion(MemoryRegion *memRegion, uint64_t size, - StringRef secName) { + StringRef secName, bool verify) { memRegion->curPos += size; uint64_t newSize = memRegion->curPos - memRegion->getOrigin(); uint64_t length = memRegion->getLength(); - if (newSize > length) + if (verify && (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, bool verify) { if (state->memRegion) - expandMemoryRegion(state->memRegion, size, state->outSec->name); + expandMemoryRegion(state->memRegion, size, state->outSec->name, verify); // 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, verify); } -void LinkerScript::expandOutputSection(uint64_t size) { +void LinkerScript::expandOutputSection(uint64_t size, bool verify) { state->outSec->size += size; - expandMemoryRegions(size); + expandMemoryRegions(size, verify); } -void LinkerScript::setDot(Expr e, const Twine &loc, bool inSec) { +void LinkerScript::setDot(Expr e, const Twine &loc, bool inSec, bool verify) { uint64_t val = e().getValue(); if (val < dot && inSec) error(loc + ": unable to move location counter backward for: " + @@ -186,7 +186,7 @@ // Update to location counter means update to section size. if (inSec) - expandOutputSection(val - dot); + expandOutputSection(val - dot, verify); dot = val; } @@ -364,9 +364,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, + bool verify) { if (cmd->name == ".") { - setDot(cmd->expression, cmd->location, inSec); + setDot(cmd->expression, cmd->location, inSec, verify); return; } @@ -957,7 +958,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, bool verify) { 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 +980,7 @@ if (state->memRegion) dot = state->memRegion->curPos; if (sec->addrExpr) - setDot(sec->addrExpr, sec->location, false); + setDot(sec->addrExpr, sec->location, false, verify); // 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 +988,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, verify); } state->outSec = sec; @@ -1000,7 +1001,7 @@ const uint64_t pos = dot; dot = alignToPowerOf2(dot, sec->addralign); sec->addr = dot; - expandMemoryRegions(dot - pos); + expandMemoryRegions(dot - pos, verify); } // state->lmaOffset is LMA minus VMA. If LMA is explicitly specified via AT() @@ -1014,7 +1015,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, verify); state->lmaOffset = lmaStart - dot; } else if (!sameMemRegion || !prevLMARegionIsDefault) { state->lmaOffset = 0; @@ -1036,7 +1037,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, verify); assign->size = dot - assign->addr; continue; } @@ -1045,7 +1046,7 @@ if (auto *data = dyn_cast(cmd)) { data->offset = dot - sec->addr; dot += data->size; - expandOutputSection(data->size); + expandOutputSection(data->size, verify); continue; } @@ -1062,7 +1063,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, verify); } } @@ -1300,7 +1301,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(bool verify) { if (script->hasSectionsCommand) { // With a linker script, assignment of addresses to headers is covered by // allocateHeaders(). @@ -1322,11 +1323,11 @@ for (SectionCommand *cmd : sectionCommands) { if (auto *assign = dyn_cast(cmd)) { assign->addr = dot; - assignSymbol(assign, false); + assignSymbol(assign, false, verify); assign->size = dot - assign->addr; continue; } - assignOffsets(&cast(cmd)->osec); + assignOffsets(&cast(cmd)->osec, verify); } state = nullptr; @@ -1430,7 +1431,7 @@ return ret; } -void LinkerScript::printMemoryUsage(raw_ostream& os) { +void LinkerScript::printMemoryUsage(raw_ostream &os) { auto printSize = [&](uint64_t size) { if ((size & 0x3fffffff) == 0) os << format_decimal(size >> 30, 10) << " GB"; Index: lld/ELF/Writer.cpp =================================================================== --- lld/ELF/Writer.cpp +++ lld/ELF/Writer.cpp @@ -1599,7 +1599,8 @@ ThunkCreator tc; AArch64Err843419Patcher a64p; ARMErr657417Patcher a32p; - script->assignAddresses(); + // First run with verification disabled to avoid false positives. + script->assignAddresses(false); // .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 @@ -1628,12 +1629,12 @@ if (config->fixCortexA53Errata843419) { if (changed) - script->assignAddresses(); + script->assignAddresses(true); changed |= a64p.createFixes(); } if (config->fixCortexA8) { if (changed) - script->assignAddresses(); + script->assignAddresses(true); changed |= a32p.createFixes(); } @@ -1646,7 +1647,7 @@ changed |= part.relrDyn->updateAllocSize(); } - const Defined *changedSym = script->assignAddresses(); + const Defined *changedSym = script->assignAddresses(true); if (!changed) { // Some symbols may be dependent on section addresses. When we break the // loop, the symbol values are finalized because a previous @@ -1730,7 +1731,7 @@ assert(config->optimizeBBJumps); SmallVector storage; - script->assignAddresses(); + script->assignAddresses(false); // For every output section that has executable input sections, this // does the following: // 1. Deletes all direct jump instructions in input sections that @@ -1751,7 +1752,7 @@ numDeleted += target->deleteFallThruJmpInsn(sec, sec.file, next); } if (numDeleted > 0) { - script->assignAddresses(); + script->assignAddresses(false); LLVM_DEBUG(llvm::dbgs() << "Removing " << numDeleted << " fall through jumps\n"); } Index: lld/test/ELF/linkerscript/end-overflow-check.test =================================================================== --- /dev/null +++ lld/test/ELF/linkerscript/end-overflow-check.test @@ -0,0 +1,120 @@ +REQUIRES: x86 + +# RUN: split-file %s %ts +# RUN: llvm-mc -filetype=obj -triple=x86_64 %ts/a.s -o %ts/a.o + +# The error should be triggered only for the second test where the overflow really exists. + +RUN: ld.lld %ts/a.o -T %ts/b.lds -o /dev/null 2>&1 +RUN: not ld.lld %ts/a.o -T %ts/c.lds -o /dev/null 2>&1 | FileCheck --check-prefix=ERROR %s + +# ERROR: ld.lld: error: section '_abss' will not fit in region 'SRAM0': overflowed by 496 bytes +# ERROR: ld.lld: error: section '_abss' will not fit in region 'SRAM0': overflowed by 512 bytes +# ERROR: ld.lld: error: section '.c.bss' will not fit in region 'SRAM0': overflowed by 512 bytes +# ERROR: ld.lld: error: section '.c.bss' will not fit in region 'SRAM0': overflowed by 512 bytes +# ERROR: ld.lld: error: section '.text' will not fit in region 'SRAM0': overflowed by 512 bytes +# ERROR: ld.lld: error: section '.text' will not fit in region 'SRAM0': overflowed by 513 bytes + +#--- a.s + .section .a.bss, "aw", %nobits + .globl abss +abss: + .zero 0xDF0 + .size abss, 0xDF0 + + .section .b.bss, "aw", %nobits + .globl abss +bbss: + .zero 16 + .size bbss, 16 + + .section .c.bss, "aw", %nobits + .globl cbss + + .text + .globl _start +_start: + nop + +#--- b.lds + +MEMORY +{ + SRAM0 (rw) : ORIGIN = 0x20000400, LENGTH = 20K +} + +SECTIONS +{ + _bbss 0x20001D60 : + { + REGION2_BEGIN = .; + *(.b.bss) + REGION2_END = .; + } > SRAM0 + _abss : + { + REGION1__BEGIN = .; . = ALIGN(REGION1__PRE_ALIGNMENT); REGION1__ALIGNED_BEGIN = .; REGION1_ALIGNED_BEGIN = .; + *(.a.bss) + REGION1__END = .; . = ALIGN(REGION1__POST_ALIGNMENT); REGION1_ALIGNED_END = .; + } > SRAM0 +} + + +REGION1__SIZE_RAW = ABSOLUTE(REGION1__END - REGION1__ALIGNED_BEGIN); +REGION1__SIZE_RAW_LOG2CEIL = LOG2CEIL(REGION1__SIZE_RAW); +REGION1__SIZE_NPOW2 = MAX(1 << (REGION1__SIZE_RAW_LOG2CEIL), 32); +REGION1__MIN_SR_SIZE = MAX(1 << (REGION1__SIZE_RAW_LOG2CEIL - 3), 32); +REGION1__MIN_SR_SIZE_MASKED = REGION1__BEGIN & ~(REGION1__MIN_SR_SIZE - 1); +REGION1__MIN_SR_ALIGNED_BEGIN = REGION1__MIN_SR_SIZE_MASKED + ((REGION1__BEGIN != REGION1__MIN_SR_SIZE_MASKED) ? REGION1__MIN_SR_SIZE : 0); +REGION1__MIN_SR_ALIGNED_END = REGION1__MIN_SR_ALIGNED_BEGIN + REGION1__SIZE_RAW; REGION1__XOR = ((ABSOLUTE(REGION1__MIN_SR_ALIGNED_BEGIN) | (ABSOLUTE(REGION1__MIN_SR_ALIGNED_END) - 1)) & ~(ABSOLUTE(REGION1__MIN_SR_ALIGNED_BEGIN) & (ABSOLUTE(REGION1__MIN_SR_ALIGNED_END) - 1))); +REGION1__REGION_SHIFT_WITH_SR = LOG2CEIL(REGION1__XOR); +REGION1__SR_SHIFT = REGION1__REGION_SHIFT_WITH_SR - 3; +REGION1__SR_SIZE = MAX(1 << REGION1__SR_SHIFT, 32); +REGION1__PRE_ALIGNMENT = MIN(REGION1__SR_SIZE, REGION1__SIZE_NPOW2); +REGION1__PADDED_XOR = ((ABSOLUTE(REGION1__ALIGNED_BEGIN) | (ABSOLUTE(REGION1__END) - 1)) & ~(ABSOLUTE(REGION1__ALIGNED_BEGIN) & (ABSOLUTE(REGION1__END) - 1))); +REGION1__PADDED_REGION_SHIFT = LOG2CEIL(REGION1__PADDED_XOR); +REGION1__PADDED_SR_SHIFT = REGION1__PADDED_REGION_SHIFT - 3; +REGION1__PADDED_SR_SIZE = MAX(1 << REGION1__PADDED_SR_SHIFT, 32); +REGION1__POST_ALIGNMENT = REGION1__PADDED_SR_SIZE; + +#--- c.lds + +MEMORY +{ + SRAM0 (rw) : ORIGIN = 0x20000400, LENGTH = 10K +} + +SECTIONS +{ + _bbss 0x20001D60 : + { + REGION2_BEGIN = .; + *(.b.bss) + REGION2_END = .; + } > SRAM0 + _abss : + { + REGION1__BEGIN = .; . = ALIGN(REGION1__PRE_ALIGNMENT); REGION1__ALIGNED_BEGIN = .; REGION1_ALIGNED_BEGIN = .; + *(.a.bss) + REGION1__END = .; . = ALIGN(REGION1__POST_ALIGNMENT); REGION1_ALIGNED_END = .; + } > SRAM0 +} + + +REGION1__SIZE_RAW = ABSOLUTE(REGION1__END - REGION1__ALIGNED_BEGIN); +REGION1__SIZE_RAW_LOG2CEIL = LOG2CEIL(REGION1__SIZE_RAW); +REGION1__SIZE_NPOW2 = MAX(1 << (REGION1__SIZE_RAW_LOG2CEIL), 32); +REGION1__MIN_SR_SIZE = MAX(1 << (REGION1__SIZE_RAW_LOG2CEIL - 3), 32); +REGION1__MIN_SR_SIZE_MASKED = REGION1__BEGIN & ~(REGION1__MIN_SR_SIZE - 1); +REGION1__MIN_SR_ALIGNED_BEGIN = REGION1__MIN_SR_SIZE_MASKED + ((REGION1__BEGIN != REGION1__MIN_SR_SIZE_MASKED) ? REGION1__MIN_SR_SIZE : 0); +REGION1__MIN_SR_ALIGNED_END = REGION1__MIN_SR_ALIGNED_BEGIN + REGION1__SIZE_RAW; REGION1__XOR = ((ABSOLUTE(REGION1__MIN_SR_ALIGNED_BEGIN) | (ABSOLUTE(REGION1__MIN_SR_ALIGNED_END) - 1)) & ~(ABSOLUTE(REGION1__MIN_SR_ALIGNED_BEGIN) & (ABSOLUTE(REGION1__MIN_SR_ALIGNED_END) - 1))); +REGION1__REGION_SHIFT_WITH_SR = LOG2CEIL(REGION1__XOR); +REGION1__SR_SHIFT = REGION1__REGION_SHIFT_WITH_SR - 3; +REGION1__SR_SIZE = MAX(1 << REGION1__SR_SHIFT, 32); +REGION1__PRE_ALIGNMENT = MIN(REGION1__SR_SIZE, REGION1__SIZE_NPOW2); +REGION1__PADDED_XOR = ((ABSOLUTE(REGION1__ALIGNED_BEGIN) | (ABSOLUTE(REGION1__END) - 1)) & ~(ABSOLUTE(REGION1__ALIGNED_BEGIN) & (ABSOLUTE(REGION1__END) - 1))); +REGION1__PADDED_REGION_SHIFT = LOG2CEIL(REGION1__PADDED_XOR); +REGION1__PADDED_SR_SHIFT = REGION1__PADDED_REGION_SHIFT - 3; +REGION1__PADDED_SR_SIZE = MAX(1 << REGION1__PADDED_SR_SHIFT, 32); +REGION1__POST_ALIGNMENT = REGION1__PADDED_SR_SIZE; +