Skip to content

ELF: Synthesize R_RISCV_ALIGN at input section start #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions lld/ELF/Arch/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,18 @@ class RISCV final : public TargetInfo {
uint64_t val) const override;
void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override;
bool relaxOnce(int pass) const override;
template <class ELFT, class RelTy>
bool synthesizeAlignOne(uint64_t &dot, InputSection *sec, Relocs<RelTy> rels);
template <class ELFT, class RelTy>
void synthesizeAlignEnd(uint64_t &dot, InputSection *sec, Relocs<RelTy> rels);
template <class ELFT>
bool synthesizeAlignAux(uint64_t &dot, InputSection *sec);
bool maybeSynthesizeAlign(uint64_t &dot, InputSection *sec) override;
void finalizeRelax(int passes) const override;

// Used by synthesized ALIGN relocations.
InputSection *baseSec = nullptr;
SmallVector<std::pair<uint64_t, uint64_t>, 0> synthesizedAligns;
};

} // end anonymous namespace
Expand Down Expand Up @@ -959,10 +970,121 @@ bool RISCV::relaxOnce(int pass) const {
return changed;
}

// If the section alignment is >= 4, advance `dot` to insert NOPs and synthesize
// an ALIGN relocation. Otherwise, return false to use default handling.
template <class ELFT, class RelTy>
bool RISCV::synthesizeAlignOne(uint64_t &dot, InputSection *sec,
Relocs<RelTy> rels) {
if (!baseSec) {
// Record the first section with RELAX relocations.
for (auto rel : rels) {
if (rel.getType(false) == R_RISCV_RELAX) {
baseSec = sec;
break;
}
}
} else if (sec->addralign >= 4) {
// If the alignment is >= 4 and the section does not start with an ALIGN
// relocation, synthesize one.
bool alignRel = false;
for (auto rel : rels)
if (rel.r_offset == 0 && rel.getType(false) == R_RISCV_ALIGN)
alignRel = true;
if (!alignRel) {
synthesizedAligns.emplace_back(dot - baseSec->getVA(),
sec->addralign - 2);
dot += sec->addralign - 2;
return true;
}
}
return false;
}

// Finalize the relocation section by appending synthesized ALIGN relocations
// after processing all input sections.
template <class ELFT, class RelTy>
void RISCV::synthesizeAlignEnd(uint64_t &dot, InputSection *sec,
Relocs<RelTy> rels) {
auto *f = cast<ObjFile<ELFT>>(baseSec->file);
auto shdr = f->template getELFShdrs<ELFT>()[baseSec->relSecIdx];
// Create a copy of InputSection.
sec = make<InputSection>(*f, shdr, baseSec->name);
auto *baseRelSec = cast<InputSection>(f->getSections()[baseSec->relSecIdx]);
*sec = *baseRelSec;
baseSec = nullptr;

// Allocate buffer for original and synthesized relocations in RELA format.
// If CREL is used, OutputSection::finalizeNonAllocCrel will convert RELA to
// CREL.
auto newSize = rels.size() + synthesizedAligns.size();
auto *relas = makeThreadLocalN<typename ELFT::Rela>(newSize);
sec->size = newSize * sizeof(typename ELFT::Rela);
sec->content_ = reinterpret_cast<uint8_t *>(relas);
sec->type = SHT_RELA;
// Copy original relocations to the new buffer, potentially converting CREL to
// RELA.
for (auto [i, r] : llvm::enumerate(rels)) {
relas[i].r_offset = r.r_offset;
relas[i].setSymbolAndType(r.getSymbol(0), r.getType(0), false);
if constexpr (RelTy::HasAddend)
relas[i].r_addend = r.r_addend;
}
// Append synthesized ALIGN relocations to the buffer.
for (auto [i, r] : llvm::enumerate(synthesizedAligns)) {
auto &rela = relas[rels.size() + i];
rela.r_offset = r.first;
rela.setSymbolAndType(0, R_RISCV_ALIGN, false);
rela.r_addend = r.second;
}
// Replace the old relocation section with the new one in the output section.
// addOrphanSections ensures that the output relocation section is processed
// after osec.
for (SectionCommand *cmd : sec->getParent()->commands) {
auto *isd = dyn_cast<InputSectionDescription>(cmd);
if (!isd)
continue;
for (auto *&isec : isd->sections)
if (isec == baseRelSec)
isec = sec;
}
}

template <class ELFT>
bool RISCV::synthesizeAlignAux(uint64_t &dot, InputSection *sec) {
bool ret = false;
if (sec) {
invokeOnRelocs(*sec, ret = synthesizeAlignOne<ELFT>, dot, sec);
} else if (baseSec) {
invokeOnRelocs(*baseSec, synthesizeAlignEnd<ELFT>, dot, sec);
}
return ret;
}

// Without linker relaxation enabled for a particular relocatable file or
// section, the assembler will not generate R_RISCV_ALIGN relocations for
// alignment directives. This becomes problematic in a two-stage linking
// process: ld -r a.o b.o -o ab.o; ld ab.o -o ab. This function synthesizes an
// R_RISCV_ALIGN relocation at section start when needed.
//
// When called with an input section (`sec` is not null): If the section
// alignment is >= 4, advance `dot` to insert NOPs and synthesize an ALIGN
// relocation.
//
// When called after all input sections are processed (`sec` is null): The
// output relocation section is updated with all the newly synthesized ALIGN
// relocations.
bool RISCV::maybeSynthesizeAlign(uint64_t &dot, InputSection *sec) {
assert(ctx.arg.relocatable);
if (ctx.arg.is64)
return synthesizeAlignAux<ELF64LE>(dot, sec);
return synthesizeAlignAux<ELF32LE>(dot, sec);
}

void RISCV::finalizeRelax(int passes) const {
llvm::TimeTraceScope timeScope("Finalize RISC-V relaxation");
Log(ctx) << "relaxation passes: " << passes;
SmallVector<InputSection *, 0> storage;

for (OutputSection *osec : ctx.outputSections) {
if (!(osec->flags & SHF_EXECINSTR))
continue;
Expand Down
12 changes: 11 additions & 1 deletion lld/ELF/LinkerScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1230,6 +1230,9 @@ bool LinkerScript::assignOffsets(OutputSection *sec) {
if (sec->firstInOverlay)
state->overlaySize = 0;

bool synthesizeAlign =
(sec->flags & SHF_EXECINSTR) && ctx.arg.relocatable && ctx.arg.relax &&
is_contained({EM_RISCV, EM_LOONGARCH}, ctx.arg.emachine);
// We visited SectionsCommands from processSectionCommands to
// layout sections. Now, we visit SectionsCommands again to fix
// section offsets.
Expand Down Expand Up @@ -1260,7 +1263,8 @@ bool LinkerScript::assignOffsets(OutputSection *sec) {
if (isa<PotentialSpillSection>(isec))
continue;
const uint64_t pos = dot;
dot = alignToPowerOf2(dot, isec->addralign);
if (!(synthesizeAlign && ctx.target->maybeSynthesizeAlign(dot, isec)))
dot = alignToPowerOf2(dot, isec->addralign);
isec->outSecOff = dot - sec->addr;
dot += isec->getSize();

Expand All @@ -1276,6 +1280,12 @@ bool LinkerScript::assignOffsets(OutputSection *sec) {
if (ctx.in.relroPadding && sec == ctx.in.relroPadding->getParent())
expandOutputSection(alignToPowerOf2(dot, ctx.arg.commonPageSize) - dot);

if (synthesizeAlign) {
const uint64_t pos = dot;
ctx.target->maybeSynthesizeAlign(dot, nullptr);
expandOutputSection(dot - pos);
}

// Non-SHF_ALLOC sections do not affect the addresses of other OutputSections
// as they are not part of the process image.
if (!(sec->flags & SHF_ALLOC)) {
Expand Down
11 changes: 8 additions & 3 deletions lld/ELF/OutputSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -889,9 +889,14 @@ void OutputSection::sortInitFini() {
std::array<uint8_t, 4> OutputSection::getFiller(Ctx &ctx) {
if (filler)
return *filler;
if (flags & SHF_EXECINSTR)
return ctx.target->trapInstr;
return {0, 0, 0, 0};
if (!(flags & SHF_EXECINSTR))
return {0, 0, 0, 0};
if (ctx.arg.relocatable && ctx.arg.emachine == EM_RISCV) {
if (ctx.arg.eflags & EF_RISCV_RVC)
return {1, 0, 1, 0};
return {0x13, 0, 0, 0};
}
return ctx.target->trapInstr;
}

void OutputSection::checkDynRelAddends(Ctx &ctx) {
Expand Down
3 changes: 3 additions & 0 deletions lld/ELF/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ class TargetInfo {

// Do a linker relaxation pass and return true if we changed something.
virtual bool relaxOnce(int pass) const { return false; }
virtual bool maybeSynthesizeAlign(uint64_t &dot, InputSection *sec) {
return false;
}
// Do finalize relaxation after collecting relaxation infos.
virtual void finalizeRelax(int passes) const {}

Expand Down
2 changes: 2 additions & 0 deletions lld/ELF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1543,6 +1543,8 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {

uint32_t pass = 0, assignPasses = 0;
for (;;) {
if (ctx.arg.relocatable)
break;
bool changed = ctx.target->needsThunks
? tc.createThunks(pass, ctx.outputSections)
: ctx.target->relaxOnce(pass);
Expand Down
130 changes: 130 additions & 0 deletions lld/test/ELF/riscv-relocatable-align.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# RUN: rm -rf %t && split-file %s %t && cd %t
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax a.s -o ac.o
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax b.s -o bc.o
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax b1.s -o b1c.o
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax c.s -o cc.o
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c d.s -o dc.o

## No RELAX. Don't synthesize ALIGN.
# RUN: ld.lld -r bc.o dc.o -o bd.ro
# RUN: llvm-readelf -r bd.ro | FileCheck %s --check-prefix=NOREL

# NOREL: no relocations

# RUN: ld.lld -r bc.o bc.o ac.o bc.o b1c.o cc.o dc.o -o out.ro
# RUN: llvm-objdump -dr -M no-aliases out.ro | FileCheck %s

# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax a.s -o a.o
# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax b.s -o b.o
# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax d.s -o d.o
# RUN: ld.lld -r a.o b.o d.o -o out0.ro
# RUN: ld.lld -Ttext=0x10000 out0.ro -o out0
# RUN: llvm-objdump -dr -M no-aliases out0 | FileCheck %s --check-prefix=CHECK1

# CHECK: <b0>:
# CHECK-NEXT: 0: 00158513 addi a0, a1, 0x1
# CHECK-NEXT: 4: 0001 c.nop
# CHECK-NEXT: 6: 0001 c.nop
# CHECK-EMPTY:
# CHECK-NEXT: <b0>:
# CHECK-NEXT: 8: 00158513 addi a0, a1, 0x1
# CHECK-EMPTY:
# CHECK-NEXT: <_start>:
# CHECK-NEXT: c: 00000097 auipc ra, 0x0
# CHECK-NEXT: 000000000000000c: R_RISCV_CALL_PLT foo
# CHECK-NEXT: 000000000000000c: R_RISCV_RELAX *ABS*
# CHECK-NEXT: 10: 000080e7 jalr ra, 0x0(ra) <_start>
# CHECK-NEXT: 14: 0001 c.nop
# CHECK-NEXT: 0000000000000014: R_RISCV_ALIGN *ABS*+0x6
# CHECK-NEXT: 16: 0001 c.nop
# CHECK-NEXT: 18: 0001 c.nop
# CHECK-EMPTY:
# CHECK-NEXT: <b0>:
# CHECK-NEXT: 1a: 00158513 addi a0, a1, 0x1
# CHECK-NEXT: 1e: 0001 c.nop
# CHECK-NEXT: 20: 0001 c.nop
# CHECK-NEXT: 0000000000000020: R_RISCV_ALIGN *ABS*+0x6
# CHECK-NEXT: 22: 0001 c.nop
# CHECK-NEXT: 24: 00000013 addi zero, zero, 0x0
# CHECK-EMPTY:
# CHECK-NEXT: <b0>:
# CHECK-NEXT: 28: 00158513 addi a0, a1, 0x1
# CHECK-EMPTY:
# CHECK-NEXT: <c0>:
# CHECK-NEXT: 2c: 00000097 auipc ra, 0x0
# CHECK-NEXT: 000000000000002c: R_RISCV_CALL_PLT foo
# CHECK-NEXT: 000000000000002c: R_RISCV_RELAX *ABS*
# CHECK-NEXT: 30: 000080e7 jalr ra, 0x0(ra) <c0>
# CHECK-NEXT: 34: 0001 c.nop
# CHECK-NEXT: 0000000000000034: R_RISCV_ALIGN *ABS*+0x2
# CHECK-EMPTY:
# CHECK-NEXT: <d0>:
# CHECK-NEXT: 36: 00258513 addi a0, a1, 0x2

# CHECK1: <_start>:
# CHECK1-NEXT: 010000ef jal ra, 0x10010 <foo>
# CHECK1-NEXT: 00000013 addi zero, zero, 0x0
# CHECK1-EMPTY:
# CHECK1-NEXT: <b0>:
# CHECK1-NEXT: 00158513 addi a0, a1, 0x1
# CHECK1-EMPTY:
# CHECK1-NEXT: <d0>:
# CHECK1-NEXT: 00258513 addi a0, a1, 0x2

## Test CREL.
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax --crel a.s -o acrel.o
# RUN: ld.lld -r acrel.o bc.o -o out1.ro
# RUN: llvm-objdump -dr -M no-aliases out1.ro | FileCheck %s --check-prefix=CHECK2

# CHECK2: <_start>:
# CHECK2-NEXT: 0: 00000097 auipc ra, 0x0
# CHECK2-NEXT: 0000000000000000: R_RISCV_CALL_PLT foo
# CHECK2-NEXT: 0000000000000000: R_RISCV_RELAX *ABS*
# CHECK2-NEXT: 4: 000080e7 jalr ra, 0x0(ra) <_start>
# CHECK2-NEXT: 8: 0001 c.nop
# CHECK2-NEXT: 0000000000000008: R_RISCV_ALIGN *ABS*+0x6
# CHECK2-NEXT: a: 0001 c.nop
# CHECK2-NEXT: c: 0001 c.nop
# CHECK2-EMPTY:
# CHECK2-NEXT: <b0>:
# CHECK2-NEXT: e: 00158513 addi a0, a1, 0x1

#--- a.s
.globl _start
_start:
call foo

.section .text1,"ax"
.globl foo
foo:

#--- b.s
## Needs synthesized ALIGN
.option push
.option norelax
.balign 8
b0:
addi a0, a1, 1
.option pop

#--- b1.s
.option push
.option norelax
.reloc ., R_RISCV_ALIGN, 6
addi x0, x0, 0
c.nop
.balign 8
b0:
addi a0, a1, 1
.option pop

#--- c.s
.balign 2
c0:
call foo

#--- d.s
## Needs synthesized ALIGN
.balign 4
d0:
addi a0, a1, 2