Skip to content

Commit

Permalink
[nanoMIPS][LLD] Change in linker script parsing
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
AndrijaSyrmia committed Sep 13, 2024
1 parent b45082c commit 02081f8
Show file tree
Hide file tree
Showing 13 changed files with 320 additions and 14 deletions.
27 changes: 23 additions & 4 deletions lld/ELF/LinkerScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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;
}
Expand All @@ -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;

Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
8 changes: 7 additions & 1 deletion lld/ELF/OutputSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) +
Expand Down
59 changes: 52 additions & 7 deletions lld/ELF/ScriptParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -530,8 +530,15 @@ SmallVector<SectionCommand *, 0> 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<SectionCommand *, 0> v;
Expand All @@ -541,14 +548,38 @@ SmallVector<SectionCommand *, 0> 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<OutputDesc>(cmd)->osec.memoryRegionName = std::string(tok);
}

if (/* config->emachine == EM_NANOMIPS && */ consume("AT")) {
expect(">");
StringRef tok = next();
for (SectionCommand *cmd : v) {
OutputSection &osec = cast<OutputDesc>(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<OutputDesc>(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.
Expand Down Expand Up @@ -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));
}
Expand Down
23 changes: 21 additions & 2 deletions lld/ELF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2391,9 +2391,21 @@ SmallVector<PhdrEntry *, 0> Writer<ELFT>::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;
}
Expand Down Expand Up @@ -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 <class ELFT> void Writer<ELFT>::assignFileOffsetsBinary() {
Expand Down Expand Up @@ -2721,6 +2736,10 @@ static void checkOverlap(StringRef name, std::vector<SectionOffset> &sections,
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>>> " +
Expand Down
19 changes: 19 additions & 0 deletions lld/test/ELF/Inputs/nanomips-align-outsec.ld
Original file line number Diff line number Diff line change
@@ -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
}
16 changes: 16 additions & 0 deletions lld/test/ELF/Inputs/nanomips-dot-adjust.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
MEMORY {
SEG1 : ORIGIN = 0x1000, LENGTH = 0x1000
}

SECTIONS {

.text : {
*(.text)
} > SEG1 AT> SEG1

. = ALIGN(0x100);

.first . : {
*(.first)
} > SEG1 AT> SEG1
}
37 changes: 37 additions & 0 deletions lld/test/ELF/Inputs/nanomips-nobits-section.ld
Original file line number Diff line number Diff line change
@@ -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
}
3 changes: 3 additions & 0 deletions lld/test/ELF/input-section-flags.s
Original file line number Diff line number Diff line change
@@ -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
Expand Down
3 changes: 3 additions & 0 deletions lld/test/ELF/linkerscript/overlay-reject2.test
Original file line number Diff line number Diff line change
@@ -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

Expand Down
3 changes: 3 additions & 0 deletions lld/test/ELF/linkerscript/overlay.test
Original file line number Diff line number Diff line change
@@ -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
Expand Down
47 changes: 47 additions & 0 deletions lld/test/ELF/nanomips-align-outsec.s
Original file line number Diff line number Diff line change
@@ -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
31 changes: 31 additions & 0 deletions lld/test/ELF/nanomips-dot-adjust.s
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 02081f8

Please sign in to comment.