diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp index 72d83159ad8ac..69853a616a5a2 100644 --- a/lld/ELF/Arch/RISCV.cpp +++ b/lld/ELF/Arch/RISCV.cpp @@ -45,7 +45,15 @@ 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 + bool synthesizeAlign(uint64_t &dot, InputSection *sec, Relocs rels); + template + bool synthesizeAlignEnd(uint64_t &dot, InputSection *sec, Relocs rels); + bool maybeSynthesizeAlign(uint64_t &dot, InputSection *sec) override; void finalizeRelax(int passes) const override; + + InputSection *baseSec = nullptr; + SmallVector, 0> synthesizedAligns; }; } // end anonymous namespace @@ -959,10 +967,102 @@ bool RISCV::relaxOnce(int pass) const { return changed; } +template +bool RISCV::synthesizeAlign(uint64_t &dot, InputSection *sec, + Relocs 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; + break; + } + if (!alignRel) { + synthesizedAligns.emplace_back(dot - baseSec->getVA(), + sec->addralign - 2); + dot += sec->addralign - 2; + return true; + } + } + return false; +} + +template +bool RISCV::synthesizeAlignEnd(uint64_t &dot, InputSection *sec, + Relocs rels) { + auto *f = cast>(baseSec->file); + auto shdr = f->template getELFShdrs()[baseSec->relSecIdx]; + sec = make(*f, shdr, baseSec->name); + auto *baseRelSec = cast(f->getSections()[baseSec->relSecIdx]); + baseSec = nullptr; + *sec = *baseRelSec; + + auto newSize = rels.size() + synthesizedAligns.size(); + auto *relas = makeThreadLocalN(newSize); + sec->size = newSize * sizeof(typename ELFT::Rela); + sec->content_ = reinterpret_cast(relas); + // Copy original relocations. + for (auto [i, r] : llvm::enumerate(rels)) { + relas[i].r_offset = r.r_offset; + relas[i].setRInfo(r.getRInfo(false), false); + relas[i].r_addend = r.r_addend; + } + // Append synthesized ALIGN relocations. + 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; + } + // In the output relocation section, replace the old relocation section with + // the new one. + for (SectionCommand *cmd : sec->getParent()->commands) { + auto *isd = dyn_cast(cmd); + if (!isd) + continue; + for (auto *&isec : isd->sections) + if (isec == baseRelSec) + isec = sec; + } + return false; +} + +bool RISCV::maybeSynthesizeAlign(uint64_t &dot, InputSection *sec) { + if (sec) { + if (ctx.arg.is64) { + const RelsOrRelas rs = sec->template relsOrRelas(); + return synthesizeAlign(dot, sec, rs.relas); + } else { + const RelsOrRelas rs = sec->template relsOrRelas(); + return synthesizeAlign(dot, sec, rs.relas); + } + } + if (!baseSec) + return false; + if (ctx.arg.is64) { + const RelsOrRelas rs = baseSec->template relsOrRelas(); + return synthesizeAlignEnd(dot, sec, rs.relas); + } else { + const RelsOrRelas rs = baseSec->template relsOrRelas(); + return synthesizeAlignEnd(dot, sec, rs.relas); + } +} + void RISCV::finalizeRelax(int passes) const { llvm::TimeTraceScope timeScope("Finalize RISC-V relaxation"); Log(ctx) << "relaxation passes: " << passes; SmallVector storage; + for (OutputSection *osec : ctx.outputSections) { if (!(osec->flags & SHF_EXECINSTR)) continue; diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp index a5d08f4979dab..95830aacf45ce 100644 --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -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. @@ -1260,7 +1263,8 @@ bool LinkerScript::assignOffsets(OutputSection *sec) { if (isa(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(); @@ -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)) { diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index 1020dd9f2569e..1ce4f2fd3b3f6 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -889,9 +889,14 @@ void OutputSection::sortInitFini() { std::array 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) { diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index f4a6d83dafad2..7d6d3cee338e5 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -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 {} diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index c3c057812a685..5990a2f6e5bb8 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1543,6 +1543,8 @@ template void Writer::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); diff --git a/lld/test/ELF/riscv-relocatable-align.s b/lld/test/ELF/riscv-relocatable-align.s new file mode 100644 index 0000000000000..006dd45138b37 --- /dev/null +++ b/lld/test/ELF/riscv-relocatable-align.s @@ -0,0 +1,111 @@ +# 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,+relax d.s -o dc.o +# 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 + +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax a.s -o acrel.o +# RUN: ld.lld -r bc.o bc.o ac.o bc.o b1c.o cc.o dc.o -o out.crel.ro + +# CHECK: : +# CHECK-NEXT: 0: 00158513 addi a0, a1, 0x1 +# CHECK-NEXT: 4: 0001 c.nop +# CHECK-NEXT: 6: 0001 c.nop +# CHECK-EMPTY: +# CHECK-NEXT: : +# 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: : +# 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: : +# CHECK-NEXT: 28: 00158513 addi a0, a1, 0x1 +# CHECK-EMPTY: +# CHECK-NEXT: : +# 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) +# CHECK-NEXT: 34: 0001 c.nop +# CHECK-NEXT: 0000000000000034: R_RISCV_ALIGN *ABS*+0x2 +# CHECK-EMPTY: +# CHECK-NEXT: : +# CHECK-NEXT: 36: 00258513 addi a0, a1, 0x2 + +# CHECK1: <_start>: +# CHECK1-NEXT: 010000ef jal ra, 0x10010 +# CHECK1-NEXT: 00000013 addi zero, zero, 0x0 +# CHECK1-EMPTY: +# CHECK1-NEXT: : +# CHECK1-NEXT: 00158513 addi a0, a1, 0x1 +# CHECK1-EMPTY: +# CHECK1-NEXT: : +# CHECK1-NEXT: 00258513 addi a0, a1, 0x2 + +#--- 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 +.option push +.option norelax +.balign 4 +d0: + addi a0, a1, 2 +.option pop