diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -339,6 +339,9 @@ // Describe memory region usage. void printMemoryUsage(raw_ostream &os); + // Verify memory/lma overflows. + void checkMemoryRegions() const; + // SECTIONS command list. SmallVector sectionCommands; diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -157,12 +157,6 @@ static void expandMemoryRegion(MemoryRegion *memRegion, uint64_t size, StringRef secName) { memRegion->curPos += size; - uint64_t newSize = memRegion->curPos - memRegion->getOrigin(); - uint64_t length = memRegion->getLength(); - if (newSize > length) - error("section '" + secName + "' will not fit in region '" + - memRegion->name + "': overflowed by " + Twine(newSize - length) + - " bytes"); } void LinkerScript::expandMemoryRegions(uint64_t size) { @@ -1461,3 +1455,23 @@ os << '\n'; } } + +static void checkMemoryRegion(const MemoryRegion *region, + const OutputSection *osec, uint64_t addr) { + uint64_t osecEnd = addr + osec->size; + uint64_t regionEnd = region->getOrigin() + region->getLength(); + if (osecEnd > regionEnd) { + error("section '" + osec->name + "' will not fit in region '" + + region->name + "': overflowed by " + Twine(osecEnd - regionEnd) + + " bytes"); + } +} + +void LinkerScript::checkMemoryRegions() const { + for (const OutputSection *sec : outputSections) { + if (const MemoryRegion *memoryRegion = sec->memRegion) + checkMemoryRegion(memoryRegion, sec, sec->addr); + if (const MemoryRegion *lmaRegion = sec->lmaRegion) + checkMemoryRegion(lmaRegion, sec, sec->getLMA()); + } +} diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -2147,6 +2147,8 @@ // of finalizing other sections. for (OutputSection *sec : outputSections) sec->finalize(); + + script->checkMemoryRegions(); } // Ensure data sections are not mixed with executable sections when diff --git a/lld/test/ELF/linkerscript/end-overflow-check.test b/lld/test/ELF/linkerscript/end-overflow-check.test new file mode 100644 --- /dev/null +++ b/lld/test/ELF/linkerscript/end-overflow-check.test @@ -0,0 +1,74 @@ +REQUIRES: x86 + +# RUN: rm -rf %t && split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/a.s -o %t/a.o + +## The error should be triggered only for the second test where the overflow really exists. + +RUN: ld.lld %t/a.o -T %t/b.lds -o /dev/null 2>&1 +RUN: not ld.lld %t/a.o -T %t/c.lds -o /dev/null 2>&1 | FileCheck --check-prefix=ERROR %s + +# ERROR: error: section '_abss' will not fit in region 'SRAM0': overflowed by 1024 bytes +# ERROR: error: section '.c.bss' will not fit in region 'SRAM0': overflowed by 1024 bytes +# ERROR: error: section '.text' will not fit in region 'SRAM0': overflowed by 1025 bytes + +#--- a.s +.section .a.bss, "aw", %nobits +.globl abss +abss: +.zero 0xDF0 +.size abss, 0xDF0 + +.section .c.bss, "aw", %nobits +.globl cbss + +.text +.globl _start +_start: +nop + +#--- b.lds +MEMORY +{ + SRAM0 (rw) : ORIGIN = 0x20000400, LENGTH = 10K +} + +SECTIONS +{ + _abss ALIGN(REGION1__PRE_ALIGNMENT) : + { + REGION1__BEGIN = .; REGION1__ALIGNED_BEGIN = .; REGION1_ALIGNED_BEGIN = .; + *(.a.bss) + REGION1__END = .; . = ALIGN(REGION1__POST_ALIGNMENT); REGION1_ALIGNED_END = .; + } > SRAM0 +} + +REGION1__PRE_ALIGNMENT = 0x00000800; +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 = 4K +} + +SECTIONS +{ + _abss ALIGN(REGION1__PRE_ALIGNMENT) : + { + REGION1__BEGIN = .; REGION1__ALIGNED_BEGIN = .; REGION1_ALIGNED_BEGIN = .; + *(.a.bss) + REGION1__END = .; . = ALIGN(REGION1__POST_ALIGNMENT); REGION1_ALIGNED_END = .; + } > SRAM0 +} + +REGION1__PRE_ALIGNMENT = 0x00000800; +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; diff --git a/lld/test/ELF/linkerscript/memory-err.s b/lld/test/ELF/linkerscript/memory-err.s --- a/lld/test/ELF/linkerscript/memory-err.s +++ b/lld/test/ELF/linkerscript/memory-err.s @@ -62,14 +62,14 @@ ## ORIGIN/LENGTH can be simple symbolic expressions. If the expression ## requires interaction with memory regions, it may fail. -# RUN: echo 'MEMORY { ram : ORIGIN = symbol, LENGTH = 4097 } \ +# RUN: echo 'MEMORY { ram : ORIGIN = symbol, LENGTH = 4094 } \ # RUN: SECTIONS { \ # RUN: .text : { *(.text) } > ram \ # RUN: symbol = .; \ # RUN: .data : { *(.data) } > ram \ # RUN: }' > %t.script # RUN: not ld.lld -T %t.script %t.o -o /dev/null 2>&1 | FileCheck --check-prefix=ERR_OVERFLOW %s -# ERR_OVERFLOW: error: section '.text' will not fit in region 'ram': overflowed by 18446744073709547518 bytes +# ERR_OVERFLOW: error: section '.data' will not fit in region 'ram': overflowed by 2 bytes nop