From 02081f8a3a5633a168feaeea3905a2aa48288289 Mon Sep 17 00:00:00 2001 From: Andrija Date: Fri, 6 Sep 2024 08:48:05 +0200 Subject: [PATCH] [nanoMIPS][LLD] Change in linker script parsing Changed parsing of the linker script, as some nanoMIPS linker scripts require different set of rules for parsing: - Dot operator can be used as section address expression, and its value should be determined by the latest value of dot, not the current position in the memory region - Alignment for output section is not ignored for nanoMIPS if output section has got address expression - SHT_NOBITS sections don't occupy load addresses - OVERLAY sections can have a load region and assignment or byte commands in them. --- lld/ELF/LinkerScript.cpp | 27 +++++++-- lld/ELF/OutputSections.cpp | 8 ++- lld/ELF/ScriptParser.cpp | 59 ++++++++++++++++--- lld/ELF/Writer.cpp | 23 +++++++- lld/test/ELF/Inputs/nanomips-align-outsec.ld | 19 ++++++ lld/test/ELF/Inputs/nanomips-dot-adjust.ld | 16 +++++ .../ELF/Inputs/nanomips-nobits-section.ld | 37 ++++++++++++ lld/test/ELF/input-section-flags.s | 3 + .../ELF/linkerscript/overlay-reject2.test | 3 + lld/test/ELF/linkerscript/overlay.test | 3 + lld/test/ELF/nanomips-align-outsec.s | 47 +++++++++++++++ lld/test/ELF/nanomips-dot-adjust.s | 31 ++++++++++ lld/test/ELF/nanomips-nobits-section.s | 58 ++++++++++++++++++ 13 files changed, 320 insertions(+), 14 deletions(-) create mode 100644 lld/test/ELF/Inputs/nanomips-align-outsec.ld create mode 100644 lld/test/ELF/Inputs/nanomips-dot-adjust.ld create mode 100644 lld/test/ELF/Inputs/nanomips-nobits-section.ld create mode 100644 lld/test/ELF/nanomips-align-outsec.s create mode 100644 lld/test/ELF/nanomips-dot-adjust.s create mode 100644 lld/test/ELF/nanomips-nobits-section.s diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp index 668a79b5edd7f3a..61ff207cf5552da 100644 --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -169,7 +169,8 @@ void LinkerScript::expandMemoryRegions(uint64_t size) { if (state->memRegion) expandMemoryRegion(state->memRegion, size, state->outSec->name); // Only expand the LMARegion if it is different from memRegion. - if (state->lmaRegion && state->memRegion != state->lmaRegion) + if (state->lmaRegion && state->memRegion != state->lmaRegion && + (config->emachine != EM_NANOMIPS || state->outSec->type != SHT_NOBITS)) expandMemoryRegion(state->lmaRegion, size, state->outSec->name); } @@ -948,7 +949,8 @@ LinkerScript::findMemoryRegion(OutputSection *sec, MemoryRegion *hint) { static OutputSection *findFirstSection(PhdrEntry *load) { for (OutputSection *sec : outputSections) - if (sec->ptLoad == load) + if (sec->ptLoad == load && + (config->emachine != EM_NANOMIPS || sec->type != SHT_NOBITS)) return sec; return nullptr; } @@ -960,6 +962,7 @@ void LinkerScript::assignOffsets(OutputSection *sec) { const bool sameMemRegion = state->memRegion == sec->memRegion; const bool prevLMARegionIsDefault = state->lmaRegion == nullptr; const uint64_t savedDot = dot; + uint64_t savedOverlayCurPos = 0; state->memRegion = sec->memRegion; state->lmaRegion = sec->lmaRegion; @@ -974,10 +977,20 @@ void LinkerScript::assignOffsets(OutputSection *sec) { else dot = state->tbssAddr; } else { - if (state->memRegion) + if (state->memRegion) { dot = state->memRegion->curPos; - if (sec->addrExpr) + savedOverlayCurPos = state->memRegion->curPos; + } + if (sec->addrExpr) { + // FIXME: nanoMIPS workaround for interpreting the dot symbol + if (config->emachine == EM_NANOMIPS) + dot = savedDot; setDot(sec->addrExpr, sec->location, false); + } + + // FIXME: Seting the alignment is not ignored for nanoMIPS + if (config->emachine == EM_NANOMIPS) + dot = alignToPowerOf2(dot, sec->addralign); // 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 @@ -1073,6 +1086,12 @@ void LinkerScript::assignOffsets(OutputSection *sec) { state->tbssAddr = dot; dot = savedDot; } + + if (config->emachine == EM_NANOMIPS && (sec->inOverlay)) { + dot = savedDot; + if (sec->memRegion) + sec->memRegion->curPos = savedOverlayCurPos; + } } static bool isDiscardable(const OutputSection &sec) { diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index dc40f07ca6a1075..e102fe09647b161 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -120,7 +120,13 @@ void OutputSection::commitSection(InputSection *isec) { // (e.g. https://github.com/ClangBuiltLinux/linux/issues/1597) // traditionally rely on the behavior. Issue a warning to not break // them. Other types get an error. - auto diagnose = type == SHT_NOBITS ? warn : errorOrWarn; + // FIXME: A .rel.dyn section may be mapped to a SHT_PROGBITS section in + // a nanoMIPS linker script, so this is a workaround to let it pass + auto diagnose = + ((type == SHT_NOBITS) || + (isec->name == ".rel.dyn" && config->emachine == EM_NANOMIPS)) + ? warn + : errorOrWarn; diagnose("section type mismatch for " + isec->name + "\n>>> " + toString(isec) + ": " + getELFSectionTypeName(config->emachine, isec->type) + diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp index bb09bde5d22ef9e..8b2ee01a1587b7a 100644 --- a/lld/ELF/ScriptParser.cpp +++ b/lld/ELF/ScriptParser.cpp @@ -530,8 +530,15 @@ SmallVector ScriptParser::readOverlay() { // for first of all: to allow sections with overlapping VAs at different LMAs. Expr addrExpr = readExpr(); expect(":"); - expect("AT"); - Expr lmaExpr = readParenExpr(); + Expr lmaExpr; + // if (config->emachine == EM_NANOMIPS) { + if (consume("AT")) + lmaExpr = readParenExpr(); + // } + // else { + // expect("AT"); + // lmaExpr = readParenExpr(); + // } expect("{"); SmallVector v; @@ -541,14 +548,38 @@ SmallVector ScriptParser::readOverlay() { // starting from the base load address specified. OutputDesc *osd = readOverlaySectionDescription(); osd->osec.addrExpr = addrExpr; - if (prev) - osd->osec.lmaExpr = [=] { return prev->getLMA() + prev->size; }; - else - osd->osec.lmaExpr = lmaExpr; + if (lmaExpr) { + if (prev) + osd->osec.lmaExpr = [=] { return prev->getLMA() + prev->size; }; + else + osd->osec.lmaExpr = lmaExpr; + } v.push_back(osd); prev = &osd->osec; } + if (/* config->emachine == EM_NANOMIPS && */ consume(">")) { + StringRef tok = next(); + for (SectionCommand *cmd : v) + cast(cmd)->osec.memoryRegionName = std::string(tok); + } + + if (/* config->emachine == EM_NANOMIPS && */ consume("AT")) { + expect(">"); + StringRef tok = next(); + for (SectionCommand *cmd : v) { + OutputSection &osec = cast(cmd)->osec; + osec.lmaRegionName = std::string(tok); + if (osec.lmaExpr) + error("section can't have both LMA and a load region"); + } + } + + for (SectionCommand *cmd : v) { + OutputSection &osec = cast(cmd)->osec; + if (!osec.lmaExpr && osec.lmaRegionName.empty()) + error("overlay section must have LMA or a load region"); + } // According to the specification, at the end of the overlay, the location // counter should be equal to the overlay base address plus size of the // largest section seen in the overlay. @@ -891,8 +922,22 @@ OutputDesc *ScriptParser::readOverlaySectionDescription() { while (!errorCount() && !consume("}")) { uint64_t withFlags = 0; uint64_t withoutFlags = 0; - if (consume("INPUT_SECTION_FLAGS")) + StringRef tok = next(); + // nanoMIPS change begin + if (tok == ";") { + continue; + } else if (SymbolAssignment *assign = readAssignment(tok)) { + osd->osec.commands.push_back(assign); + continue; + } else if (ByteCommand *data = readByteCommand(tok)) { + osd->osec.commands.push_back(data); + continue; + } + if (tok == "INPUT_SECTION_FLAGS") { std::tie(withFlags, withoutFlags) = readInputSectionFlags(); + tok = next(); + } + osd->osec.commands.push_back( readInputSectionRules(next(), withFlags, withoutFlags)); } diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 71b6d8a5f2c1edb..e92c6216d4ad11a 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -2391,9 +2391,21 @@ SmallVector Writer::createPhdrs(Partition &part) { uint64_t newFlags = computeFlags(sec->getPhdrFlags()); bool sameLMARegion = load && !sec->lmaExpr && sec->lmaRegion == load->firstSec->lmaRegion; + // FIXME: Added specifics for EM_NANOMIPS, as parsing OVERLAY in linker + // script is different for this architecture. New load program header is + // needed so OVERLAY sections can have same virtual addresses but different + // load addresses + // FIXME: Also nanoMIPS, added a case where when output section alignment is + // not the same as the first section's in the load program header, then a + // new load program header is created, as this can result in lmas + // overlapping if (!(load && newFlags == flags && sec != relroEnd && sec->memRegion == load->firstSec->memRegion && - (sameLMARegion || load->lastSec == Out::programHeaders))) { + (sameLMARegion || load->lastSec == Out::programHeaders)) || + (config->emachine == EM_NANOMIPS && + (sec->inOverlay || sec->type == SHT_NOBITS || + load->firstSec->type == SHT_NOBITS || + sec->addralign != load->firstSec->addralign))) { load = addHdr(PT_LOAD, newFlags); flags = newFlags; } @@ -2571,7 +2583,10 @@ static uint64_t computeFileOffset(OutputSection *os, uint64_t off) { // If two sections share the same PT_LOAD the file offset is calculated // using this formula: Off2 = Off1 + (VA2 - VA1). OutputSection *first = os->ptLoad->firstSec; - return first->offset + os->addr - first->addr; + uint64_t result = first->offset + os->addr - first->addr; + if (config->emachine == EM_NANOMIPS && os->inOverlay) + result = first->offset + os->getLMA() - first->getLMA(); + return result; } template void Writer::assignFileOffsetsBinary() { @@ -2721,6 +2736,10 @@ static void checkOverlap(StringRef name, std::vector §ions, if (isVirtualAddr && a.sec->inOverlay && b.sec->inOverlay) continue; + if (config->emachine == EM_NANOMIPS && + (a.sec->type == SHT_NOBITS || b.sec->type == SHT_NOBITS)) + continue; + errorOrWarn("section " + a.sec->name + " " + name + " range overlaps with " + b.sec->name + "\n>>> " + a.sec->name + " range is " + rangeToString(a.offset, a.sec->size) + "\n>>> " + diff --git a/lld/test/ELF/Inputs/nanomips-align-outsec.ld b/lld/test/ELF/Inputs/nanomips-align-outsec.ld new file mode 100644 index 000000000000000..0462a2d1e797e82 --- /dev/null +++ b/lld/test/ELF/Inputs/nanomips-align-outsec.ld @@ -0,0 +1,19 @@ +MEMORY { + SEG1 : ORIGIN = 0x1000, LENGTH = 0x1000 + SEG2 : ORIGIN = 0x2000, LENGTH = 0x1000 +} + +SECTIONS { + + .text : { + *(.text) + } > SEG1 AT> SEG1 + + .first ORIGIN(SEG2) + SIZEOF(.text) : ALIGN(0x20) { + *(.first) + } > SEG2 AT> SEG1 + + .second : ALIGN(0x40) { + *(.second) + } > SEG2 AT> SEG1 +} \ No newline at end of file diff --git a/lld/test/ELF/Inputs/nanomips-dot-adjust.ld b/lld/test/ELF/Inputs/nanomips-dot-adjust.ld new file mode 100644 index 000000000000000..11004160707afe4 --- /dev/null +++ b/lld/test/ELF/Inputs/nanomips-dot-adjust.ld @@ -0,0 +1,16 @@ +MEMORY { + SEG1 : ORIGIN = 0x1000, LENGTH = 0x1000 +} + +SECTIONS { + + .text : { + *(.text) + } > SEG1 AT> SEG1 + + . = ALIGN(0x100); + + .first . : { + *(.first) + } > SEG1 AT> SEG1 +} \ No newline at end of file diff --git a/lld/test/ELF/Inputs/nanomips-nobits-section.ld b/lld/test/ELF/Inputs/nanomips-nobits-section.ld new file mode 100644 index 000000000000000..152197b1b60fcf1 --- /dev/null +++ b/lld/test/ELF/Inputs/nanomips-nobits-section.ld @@ -0,0 +1,37 @@ +MEMORY { + SEG1 : ORIGIN = 0x1000, LENGTH = 0x1000 + SEG2 : ORIGIN = 0x2000, LENGTH = 0x1000 +} + +SECTIONS { + + .text : { + *(.text) + } > SEG1 AT> SEG1 + + .first (NOLOAD) : { + *(.first) + } > SEG2 AT> SEG1 + + .second : { + *(.second) + } > SEG2 AT> SEG1 + + .third (NOLOAD) : { + *(.third) + } > SEG2 AT> SEG1 + + .nanoMIPS.abiflags : { + *(.nanoMIPS.abiflags) + } > SEG2 AT> SEG1 + + + + .bss : { + *(.bss) + } > SEG2 AT> SEG1 + + .data : { + *(.data) + } > SEG2 AT> SEG1 +} \ No newline at end of file diff --git a/lld/test/ELF/input-section-flags.s b/lld/test/ELF/input-section-flags.s index 0c8e31c77b0dc61..f878faf1f96be96 100644 --- a/lld/test/ELF/input-section-flags.s +++ b/lld/test/ELF/input-section-flags.s @@ -1,4 +1,7 @@ # REQUIRES: x86 +# Note: Due to different parsing of the linker script +# for nanomips this test is unsupported, fixup needed +# UNSUPPORTED: nanomips # RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o ## Test the INPUT_SECTION_FLAGS feature. It prefixes an input section list and diff --git a/lld/test/ELF/linkerscript/overlay-reject2.test b/lld/test/ELF/linkerscript/overlay-reject2.test index be886d7c1dac9a9..bf9483a5b9aa930 100644 --- a/lld/test/ELF/linkerscript/overlay-reject2.test +++ b/lld/test/ELF/linkerscript/overlay-reject2.test @@ -1,4 +1,7 @@ # REQUIRES: x86 +# Note: Due to different parsing of the linker script +# for nanomips this test is unsupported, fixup needed +# UNSUPPORTED: nanomips # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux /dev/null -o %t.o # RUN: not ld.lld %t.o --script %s -o /dev/null 2>&1 | FileCheck %s diff --git a/lld/test/ELF/linkerscript/overlay.test b/lld/test/ELF/linkerscript/overlay.test index 2d3c88759c63087..9ced0e9c1744af6 100644 --- a/lld/test/ELF/linkerscript/overlay.test +++ b/lld/test/ELF/linkerscript/overlay.test @@ -1,4 +1,7 @@ # REQUIRES: x86 +# Note: Due to different parsing of the linker script +# for nanomips this test is unsupported, fixup needed +# UNSUPPORTED: nanomips # RUN: echo 'nop; .section .small, "a"; .long 0; .section .big, "a"; .quad 1;' \ # RUN: | llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t.o # RUN: ld.lld %t.o --script %s -o %t diff --git a/lld/test/ELF/nanomips-align-outsec.s b/lld/test/ELF/nanomips-align-outsec.s new file mode 100644 index 000000000000000..43cdce5795c52f2 --- /dev/null +++ b/lld/test/ELF/nanomips-align-outsec.s @@ -0,0 +1,47 @@ +# REQUIRES: nanomips + + +# RUN: llvm-mc -filetype=obj -triple nanomips-elf -mcpu=i7200 %s -o %t.o +# RUN: ld.lld -T %S/Inputs/nanomips-align-outsec.ld %t.o -o %t +# RUN: llvm-objdump -h --triple=nanomips-elf --mcpu=i7200 %t | FileCheck %s +# RUN: llvm-objdump -p --triple=nanomips-elf --mcpu=i7200 %t | FileCheck %s --check-prefix=CHECK-PHDR + +# CHECK: .first 00000004 00002020 00001020 +# CHECK-NEXT: .second 00000004 00002040 00001040 + + +# CHECK-PHDR: 0x00002020 vaddr 0x00002020 paddr 0x00001020 +# CHECK-PHDR: 0x00002040 vaddr 0x00002040 paddr 0x00001040 + + .section .text, "ax", @progbits + .align 1 + .globl _start + .ent _start + +_start: + addiu $a1, $a2, 1 + + .end _start + .size _start, .-_start + + .section .first, "ax", @progbits + .align 1 + .globl first_fun + .ent first_fun + +first_fun: + addiu $a1, $a2, 2 + + .end first_fun + .size first_fun, .-first_fun + + .section .second, "ax", @progbits + .align 1 + .globl second_fun + .ent second_fun + +second_fun: + beqic $a1, 2, second_fun + + .end second_fun + .size second_fun, .-second_fun \ No newline at end of file diff --git a/lld/test/ELF/nanomips-dot-adjust.s b/lld/test/ELF/nanomips-dot-adjust.s new file mode 100644 index 000000000000000..dc789e8e592bdad --- /dev/null +++ b/lld/test/ELF/nanomips-dot-adjust.s @@ -0,0 +1,31 @@ +# REQUIRES: nanomips + + +# RUN: llvm-mc -filetype=obj -triple nanomips-elf -mcpu=i7200 %s -o %t.o +# RUN: ld.lld -T %S/Inputs/nanomips-dot-adjust.ld %t.o -o %t +# RUN: llvm-objdump -h --triple=nanomips-elf --mcpu=i7200 %t | FileCheck %s + +# CHECK: .text {{[0-9a-fA-F]*}} 00001000 +# CHECK-NEXT: .first {{[0-9a-fA-F]*}} 00001100 + + .section .text, "ax", @progbits + .align 1 + .globl _start + .ent _start + +_start: + addiu $a1, $a2, 1 + + .end _start + .size _start, .-_start + + .section .first, "ax", @progbits + .align 1 + .globl first_fun + .ent first_fun + +first_fun: + addiu $a1, $a2, 2 + + .end first_fun + .size first_fun, .-first_fun diff --git a/lld/test/ELF/nanomips-nobits-section.s b/lld/test/ELF/nanomips-nobits-section.s new file mode 100644 index 000000000000000..9b37f48575a73f5 --- /dev/null +++ b/lld/test/ELF/nanomips-nobits-section.s @@ -0,0 +1,58 @@ +# REQUIRES: nanomips + +# Note that sections first and third are intentionally put as "ax",@progbits +# and lld will issue a warning about this. This happens in some nanoMIPS +# linker scripts. + +# RUN: llvm-mc -filetype=obj -triple nanomips-elf -mcpu=i7200 %s -o %t.o +# RUN: ld.lld -T %S/Inputs/nanomips-nobits-section.ld %t.o -o %t +# RUN: llvm-objdump -h --triple=nanomips-elf --mcpu=i7200 %t | FileCheck %s +# RUN: llvm-objdump -p --triple=nanomips-elf --mcpu=i7200 %t | FileCheck %s --check-prefix=CHECK-PHDR + +# CHECK: .text 00000004 00001000 00001000 +# CHECK-NEXT: .first 00000004 00002000 00002000 +# CHECK-NEXT: .second 00000004 00002004 00001004 +# CHECK-NEXT: .third 00000004 00002008 00002008 + +# CHECK-PHDR: vaddr 0x00002000 +# CHECK-PHDR-NEXT: filesz 0x00000000 memsz 0x00000004 +# CHECK-PHDR: vaddr 0x00002008 +# CHECK-PHDR-NEXT: filesz 0x00000000 memsz 0x00000004 + + + .section .text, "ax", @progbits + .align 1 + .globl _start + .ent _start + +_start: + beqic $a1, 1, _start + + .end _start + .size _start, .-_start + + .section .first, "ax", @progbits + .align 1 + .globl a + +a: + .4byte 0 + .size a, 4 + + .section .second, "ax", @progbits + .align 1 + .globl second_fun + .ent second_fun + +second_fun: + beqic $a1, 2, second_fun + + .end second_fun + .size second_fun, .-second_fun + + .section .third, "ax", @progbits + .align 1 + .globl b +b: + .4byte 0 + .size b, 4 \ No newline at end of file