From 9a45484eda808f819b7c2f3ae94d6f09a60de634 Mon Sep 17 00:00:00 2001 From: Job Noorman Date: Sat, 9 Sep 2023 10:24:16 +0200 Subject: [PATCH] [ELF][RISCV] Implement --emit-relocs with relaxation Linker relaxation may change relocations (offsets and types). However, when --emit-relocs is used, relocations are simply copied from the input section causing a mismatch with the corresponding (relaxed) code section. This patch fixes this as follows: for non-relocatable RISC-V binaries, `InputSection::copyRelocations` reads relocations from the relocated section's `relocations` array (since this gets updated by the relaxation code). For all other cases, relocations are read from the input section directly as before. In order to reuse as much code as possible, and to keep the diff small, the original `InputSection::copyRelocations` is changed to accept the relocations as a range of `Relocation` objects. This means that, in the general case when reading from the input section, raw relocations need to be converted to `Relocation`s first, which introduces quite a bit of boiler plate. It also means there's a slight code size increase due to the extra instantiations of `copyRelocations` (for both range types). Reviewed By: MaskRay Differential Revision: https://reviews.llvm.org/D159082 --- lld/ELF/InputSection.cpp | 56 ++++++++++++++++----- lld/ELF/InputSection.h | 6 ++- lld/test/ELF/riscv-relax-emit-relocs.s | 69 ++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 14 deletions(-) create mode 100644 lld/test/ELF/riscv-relax-emit-relocs.s diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index b72679339281cf1..2be71a7c8b6a27a 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -349,29 +349,61 @@ InputSectionBase *InputSection::getRelocatedSection() const { return sections[info]; } +template +void InputSection::copyRelocations(uint8_t *buf) { + if (config->relax && !config->relocatable && config->emachine == EM_RISCV) { + // On RISC-V, relaxation might change relocations: copy from internal ones + // that are updated by relaxation. + InputSectionBase *sec = getRelocatedSection(); + copyRelocations(buf, llvm::make_range(sec->relocations.begin(), + sec->relocations.end())); + } else { + // Convert the raw relocations in the input section into Relocation objects + // suitable to be used by copyRelocations below. + struct MapRel { + const ObjFile &file; + Relocation operator()(const RelTy &rel) const { + // RelExpr is not used so set to a dummy value. + return Relocation{R_NONE, rel.getType(config->isMips64EL), rel.r_offset, + getAddend(rel), &file.getRelocTargetSym(rel)}; + } + }; + + using RawRels = ArrayRef; + using MapRelIter = + llvm::mapped_iterator; + auto mapRel = MapRel{*getFile()}; + RawRels rawRels = getDataAs(); + auto rels = llvm::make_range(MapRelIter(rawRels.begin(), mapRel), + MapRelIter(rawRels.end(), mapRel)); + copyRelocations(buf, rels); + } +} + // This is used for -r and --emit-relocs. We can't use memcpy to copy // relocations because we need to update symbol table offset and section index // for each relocation. So we copy relocations one by one. -template -void InputSection::copyRelocations(uint8_t *buf, ArrayRef rels) { +template +void InputSection::copyRelocations(uint8_t *buf, + llvm::iterator_range rels) { const TargetInfo &target = *elf::target; InputSectionBase *sec = getRelocatedSection(); (void)sec->contentMaybeDecompress(); // uncompress if needed - for (const RelTy &rel : rels) { - RelType type = rel.getType(config->isMips64EL); + for (const Relocation &rel : rels) { + RelType type = rel.type; const ObjFile *file = getFile(); - Symbol &sym = file->getRelocTargetSym(rel); + Symbol &sym = *rel.sym; auto *p = reinterpret_cast(buf); buf += sizeof(RelTy); if (RelTy::IsRela) - p->r_addend = getAddend(rel); + p->r_addend = rel.addend; // Output section VA is zero for -r, so r_offset is an offset within the // section, but for --emit-relocs it is a virtual address. - p->r_offset = sec->getVA(rel.r_offset); + p->r_offset = sec->getVA(rel.offset); p->setSymbolAndType(in.symTab->getSymbolIndex(&sym), type, config->isMips64EL); @@ -408,8 +440,8 @@ void InputSection::copyRelocations(uint8_t *buf, ArrayRef rels) { continue; } - int64_t addend = getAddend(rel); - const uint8_t *bufLoc = sec->content().begin() + rel.r_offset; + int64_t addend = rel.addend; + const uint8_t *bufLoc = sec->content().begin() + rel.offset; if (!RelTy::IsRela) addend = target.getImplicitAddend(bufLoc, type); @@ -432,7 +464,7 @@ void InputSection::copyRelocations(uint8_t *buf, ArrayRef rels) { if (RelTy::IsRela) p->r_addend = sym.getVA(addend) - section->getOutputSection()->addr; else if (config->relocatable && type != target.noneRel) - sec->addReloc({R_ABS, type, rel.r_offset, addend, &sym}); + sec->addReloc({R_ABS, type, rel.offset, addend, &sym}); } else if (config->emachine == EM_PPC && type == R_PPC_PLTREL24 && p->r_addend >= 0x8000 && sec->file->ppc32Got2) { // Similar to R_MIPS_GPREL{16,32}. If the addend of R_PPC_PLTREL24 @@ -1144,11 +1176,11 @@ template void InputSection::writeTo(uint8_t *buf) { // If -r or --emit-relocs is given, then an InputSection // may be a relocation section. if (LLVM_UNLIKELY(type == SHT_RELA)) { - copyRelocations(buf, getDataAs()); + copyRelocations(buf); return; } if (LLVM_UNLIKELY(type == SHT_REL)) { - copyRelocations(buf, getDataAs()); + copyRelocations(buf); return; } diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h index be64758e0b6899b..c33b33b16b8c6a0 100644 --- a/lld/ELF/InputSection.h +++ b/lld/ELF/InputSection.h @@ -397,8 +397,10 @@ class InputSection : public InputSectionBase { static InputSection discarded; private: - template - void copyRelocations(uint8_t *buf, llvm::ArrayRef rels); + template void copyRelocations(uint8_t *buf); + + template + void copyRelocations(uint8_t *buf, llvm::iterator_range rels); template void copyShtGroup(uint8_t *buf); }; diff --git a/lld/test/ELF/riscv-relax-emit-relocs.s b/lld/test/ELF/riscv-relax-emit-relocs.s new file mode 100644 index 000000000000000..ebd69b742d4f9dd --- /dev/null +++ b/lld/test/ELF/riscv-relax-emit-relocs.s @@ -0,0 +1,69 @@ +# REQUIRES: riscv +## Test that we can handle --emit-relocs while relaxing. + +# RUN: rm -rf %t && mkdir %t && cd %t + +# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax %s -o 32.o +# RUN: ld.lld -Ttext=0x10000 --emit-relocs 32.o -o 32 +# RUN: llvm-objdump -dr --no-show-raw-insn -M no-aliases 32 | FileCheck %s + +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax %s -o 64.o +# RUN: ld.lld -Ttext=0x10000 --emit-relocs 64.o -o 64 +# RUN: llvm-objdump -dr --no-show-raw-insn -M no-aliases 64 | FileCheck %s + +## -r should keep original relocations. +# RUN: ld.lld -r 64.o -o 64.r +# RUN: llvm-objdump -dr --no-show-raw-insn -M no-aliases 64.r | FileCheck %s --check-prefix=CHECKR + +## --no-relax should keep original relocations. +# RUN: ld.lld --emit-relocs --no-relax 64.o -o 64.norelax +# RUN: llvm-objdump -dr --no-show-raw-insn -M no-aliases 64.norelax | FileCheck %s --check-prefix=CHECKNORELAX + +# CHECK: <_start>: +# CHECK-NEXT: jal ra, 0x10008 +# CHECK-NEXT: R_RISCV_JAL f +# CHECK-NEXT: R_RISCV_RELAX *ABS* +# CHECK-NEXT: jal ra, 0x10008 +# CHECK-NEXT: R_RISCV_JAL f +# CHECK-NEXT: R_RISCV_RELAX *ABS* +# CHECK-EMPTY: +# CHECK-NEXT: : +# CHECK-NEXT: jalr zero, 0(ra) +# CHECK-NEXT: R_RISCV_ALIGN *ABS*+0x4 + +# CHECKR: <_start>: +# CHECKR-NEXT: auipc ra, 0 +# CHECKR-NEXT: R_RISCV_CALL_PLT f +# CHECKR-NEXT: R_RISCV_RELAX *ABS* +# CHECKR-NEXT: jalr ra, 0(ra) +# CHECKR-NEXT: auipc ra, 0 +# CHECKR-NEXT: R_RISCV_CALL_PLT f +# CHECKR-NEXT: R_RISCV_RELAX *ABS* +# CHECKR-NEXT: jalr ra, 0(ra) +# CHECKR-NEXT: addi zero, zero, 0 +# CHECKR-NEXT: R_RISCV_ALIGN *ABS*+0x4 +# CHECKR-EMPTY: +# CHECKR-NEXT: : +# CHECKR-NEXT: jalr zero, 0(ra) + +# CHECKNORELAX: <_start>: +# CHECKNORELAX-NEXT: auipc ra, 0 +# CHECKNORELAX-NEXT: R_RISCV_CALL_PLT f +# CHECKNORELAX-NEXT: R_RISCV_RELAX *ABS* +# CHECKNORELAX-NEXT: jalr ra, 16(ra) +# CHECKNORELAX-NEXT: auipc ra, 0 +# CHECKNORELAX-NEXT: R_RISCV_CALL_PLT f +# CHECKNORELAX-NEXT: R_RISCV_RELAX *ABS* +# CHECKNORELAX-NEXT: jalr ra, 8(ra) +# CHECKNORELAX-EMPTY: +# CHECKNORELAX-NEXT: : +# CHECKNORELAX-NEXT: jalr zero, 0(ra) +# CHECKNORELAX-NEXT: R_RISCV_ALIGN *ABS*+0x4 + +.global _start +_start: + call f + call f + .balign 8 +f: + ret