Skip to content

[ELF] -r: Synthesize R_RISCV_ALIGN at input section start #151639

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

Merged
Show file tree
Hide file tree
Changes from 2 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
121 changes: 121 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,6 +970,116 @@ 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do the R_RISCV_ALIGN need to be inserted at the "right" place for their offset? i.e., so that all R_RISCV_ALIGN come in address-ascending order. I know we cannot fully sort the relocations (iirc, we tried this before).

I guess we're not doing this, and we wouldn't expect the output of lld -r to be consumed by a different linker, so your tests are enough to show things are working.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't. LLD sorts relocations by offset, so later ALIGN relocations are fine. GNU ld seems fine as well.

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;
Expand Down
14 changes: 13 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 = ctx.arg.relocatable && ctx.arg.relax &&
(sec->flags & SHF_EXECINSTR) &&
ctx.arg.emachine == EM_RISCV;
// We visited SectionsCommands from processSectionCommands to
// layout sections. Now, we visit SectionsCommands again to fix
// section offsets.
Expand Down Expand Up @@ -1260,7 +1263,10 @@ bool LinkerScript::assignOffsets(OutputSection *sec) {
if (isa<PotentialSpillSection>(isec))
continue;
const uint64_t pos = dot;
dot = alignToPowerOf2(dot, isec->addralign);
// If synthesized ALIGN may be needed, call maybeSynthesizeAlign and
// disable the default handling if the return value is true.
if (!(synthesizeAlign && ctx.target->maybeSynthesizeAlign(dot, isec)))
dot = alignToPowerOf2(dot, isec->addralign);
isec->outSecOff = dot - sec->addr;
dot += isec->getSize();

Expand All @@ -1276,6 +1282,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
14 changes: 11 additions & 3 deletions lld/ELF/OutputSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -889,9 +889,17 @@ 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) {
// See RISCV::maybeSynthesizeAlign: Synthesized NOP bytes and ALIGN
// relocations might be needed between two input sections. Use a NOP for the
// filler.
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