Skip to content

Commit 649cac3

Browse files
committed
[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
1 parent 0aa459f commit 649cac3

File tree

3 files changed

+117
-14
lines changed

3 files changed

+117
-14
lines changed

lld/ELF/InputSection.cpp

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -349,29 +349,61 @@ InputSectionBase *InputSection::getRelocatedSection() const {
349349
return sections[info];
350350
}
351351

352+
template <class ELFT, class RelTy>
353+
void InputSection::copyRelocations(uint8_t *buf) {
354+
if (config->relax && !config->relocatable && config->emachine == EM_RISCV) {
355+
// On RISC-V, relaxation might change relocations: copy from internal ones
356+
// that are updated by relaxation.
357+
InputSectionBase *sec = getRelocatedSection();
358+
copyRelocations<ELFT, RelTy>(buf, llvm::make_range(sec->relocations.begin(),
359+
sec->relocations.end()));
360+
} else {
361+
// Convert the raw relocations in the input section into Relocation objects
362+
// suitable to be used by copyRelocations below.
363+
struct MapRel {
364+
const ObjFile<ELFT> &file;
365+
Relocation operator()(const RelTy &rel) const {
366+
// RelExpr is not used so set to a dummy value.
367+
return Relocation{R_NONE, rel.getType(config->isMips64EL), rel.r_offset,
368+
getAddend<ELFT>(rel), &file.getRelocTargetSym(rel)};
369+
}
370+
};
371+
372+
using RawRels = ArrayRef<RelTy>;
373+
using MapRelIter =
374+
llvm::mapped_iterator<typename RawRels::iterator, MapRel>;
375+
auto mapRel = MapRel{*getFile<ELFT>()};
376+
RawRels rawRels = getDataAs<RelTy>();
377+
auto rels = llvm::make_range(MapRelIter(rawRels.begin(), mapRel),
378+
MapRelIter(rawRels.end(), mapRel));
379+
copyRelocations<ELFT, RelTy>(buf, rels);
380+
}
381+
}
382+
352383
// This is used for -r and --emit-relocs. We can't use memcpy to copy
353384
// relocations because we need to update symbol table offset and section index
354385
// for each relocation. So we copy relocations one by one.
355-
template <class ELFT, class RelTy>
356-
void InputSection::copyRelocations(uint8_t *buf, ArrayRef<RelTy> rels) {
386+
template <class ELFT, class RelTy, class RelIt>
387+
void InputSection::copyRelocations(uint8_t *buf,
388+
llvm::iterator_range<RelIt> rels) {
357389
const TargetInfo &target = *elf::target;
358390
InputSectionBase *sec = getRelocatedSection();
359391
(void)sec->contentMaybeDecompress(); // uncompress if needed
360392

361-
for (const RelTy &rel : rels) {
362-
RelType type = rel.getType(config->isMips64EL);
393+
for (const Relocation &rel : rels) {
394+
RelType type = rel.type;
363395
const ObjFile<ELFT> *file = getFile<ELFT>();
364-
Symbol &sym = file->getRelocTargetSym(rel);
396+
Symbol &sym = *rel.sym;
365397

366398
auto *p = reinterpret_cast<typename ELFT::Rela *>(buf);
367399
buf += sizeof(RelTy);
368400

369401
if (RelTy::IsRela)
370-
p->r_addend = getAddend<ELFT>(rel);
402+
p->r_addend = rel.addend;
371403

372404
// Output section VA is zero for -r, so r_offset is an offset within the
373405
// section, but for --emit-relocs it is a virtual address.
374-
p->r_offset = sec->getVA(rel.r_offset);
406+
p->r_offset = sec->getVA(rel.offset);
375407
p->setSymbolAndType(in.symTab->getSymbolIndex(&sym), type,
376408
config->isMips64EL);
377409

@@ -408,8 +440,8 @@ void InputSection::copyRelocations(uint8_t *buf, ArrayRef<RelTy> rels) {
408440
continue;
409441
}
410442

411-
int64_t addend = getAddend<ELFT>(rel);
412-
const uint8_t *bufLoc = sec->content().begin() + rel.r_offset;
443+
int64_t addend = rel.addend;
444+
const uint8_t *bufLoc = sec->content().begin() + rel.offset;
413445
if (!RelTy::IsRela)
414446
addend = target.getImplicitAddend(bufLoc, type);
415447

@@ -432,7 +464,7 @@ void InputSection::copyRelocations(uint8_t *buf, ArrayRef<RelTy> rels) {
432464
if (RelTy::IsRela)
433465
p->r_addend = sym.getVA(addend) - section->getOutputSection()->addr;
434466
else if (config->relocatable && type != target.noneRel)
435-
sec->addReloc({R_ABS, type, rel.r_offset, addend, &sym});
467+
sec->addReloc({R_ABS, type, rel.offset, addend, &sym});
436468
} else if (config->emachine == EM_PPC && type == R_PPC_PLTREL24 &&
437469
p->r_addend >= 0x8000 && sec->file->ppc32Got2) {
438470
// Similar to R_MIPS_GPREL{16,32}. If the addend of R_PPC_PLTREL24
@@ -1106,11 +1138,11 @@ template <class ELFT> void InputSection::writeTo(uint8_t *buf) {
11061138
// If -r or --emit-relocs is given, then an InputSection
11071139
// may be a relocation section.
11081140
if (LLVM_UNLIKELY(type == SHT_RELA)) {
1109-
copyRelocations<ELFT>(buf, getDataAs<typename ELFT::Rela>());
1141+
copyRelocations<ELFT, typename ELFT::Rela>(buf);
11101142
return;
11111143
}
11121144
if (LLVM_UNLIKELY(type == SHT_REL)) {
1113-
copyRelocations<ELFT>(buf, getDataAs<typename ELFT::Rel>());
1145+
copyRelocations<ELFT, typename ELFT::Rel>(buf);
11141146
return;
11151147
}
11161148

lld/ELF/InputSection.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -396,8 +396,10 @@ class InputSection : public InputSectionBase {
396396
static InputSection discarded;
397397

398398
private:
399-
template <class ELFT, class RelTy>
400-
void copyRelocations(uint8_t *buf, llvm::ArrayRef<RelTy> rels);
399+
template <class ELFT, class RelTy> void copyRelocations(uint8_t *buf);
400+
401+
template <class ELFT, class RelTy, class RelIt>
402+
void copyRelocations(uint8_t *buf, llvm::iterator_range<RelIt> rels);
401403

402404
template <class ELFT> void copyShtGroup(uint8_t *buf);
403405
};
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# REQUIRES: riscv
2+
## Test that we can handle --emit-relocs while relaxing.
3+
4+
# RUN: rm -rf %t && mkdir %t && cd %t
5+
6+
# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax %s -o 32.o
7+
# RUN: ld.lld -Ttext=0x10000 --emit-relocs 32.o -o 32
8+
# RUN: llvm-objdump -dr --no-show-raw-insn -M no-aliases 32 | FileCheck %s
9+
10+
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax %s -o 64.o
11+
# RUN: ld.lld -Ttext=0x10000 --emit-relocs 64.o -o 64
12+
# RUN: llvm-objdump -dr --no-show-raw-insn -M no-aliases 64 | FileCheck %s
13+
14+
## -r should keep original relocations.
15+
# RUN: ld.lld -r 64.o -o 64.r
16+
# RUN: llvm-objdump -dr --no-show-raw-insn -M no-aliases 64.r | FileCheck %s --check-prefix=CHECKR
17+
18+
## --no-relax should keep original relocations.
19+
# RUN: ld.lld --emit-relocs --no-relax 64.o -o 64.norelax
20+
# RUN: llvm-objdump -dr --no-show-raw-insn -M no-aliases 64.norelax | FileCheck %s --check-prefix=CHECKNORELAX
21+
22+
# CHECK: <_start>:
23+
# CHECK-NEXT: jal ra, 0x10008 <f>
24+
# CHECK-NEXT: R_RISCV_JAL f
25+
# CHECK-NEXT: R_RISCV_RELAX *ABS*
26+
# CHECK-NEXT: jal ra, 0x10008 <f>
27+
# CHECK-NEXT: R_RISCV_JAL f
28+
# CHECK-NEXT: R_RISCV_RELAX *ABS*
29+
# CHECK-EMPTY:
30+
# CHECK-NEXT: <f>:
31+
# CHECK-NEXT: jalr zero, 0(ra)
32+
# CHECK-NEXT: R_RISCV_ALIGN *ABS*+0x4
33+
34+
# CHECKR: <_start>:
35+
# CHECKR-NEXT: auipc ra, 0
36+
# CHECKR-NEXT: R_RISCV_CALL_PLT f
37+
# CHECKR-NEXT: R_RISCV_RELAX *ABS*
38+
# CHECKR-NEXT: jalr ra, 0(ra)
39+
# CHECKR-NEXT: auipc ra, 0
40+
# CHECKR-NEXT: R_RISCV_CALL_PLT f
41+
# CHECKR-NEXT: R_RISCV_RELAX *ABS*
42+
# CHECKR-NEXT: jalr ra, 0(ra)
43+
# CHECKR-NEXT: addi zero, zero, 0
44+
# CHECKR-NEXT: R_RISCV_ALIGN *ABS*+0x4
45+
# CHECKR-EMPTY:
46+
# CHECKR-NEXT: <f>:
47+
# CHECKR-NEXT: jalr zero, 0(ra)
48+
49+
# CHECKNORELAX: <_start>:
50+
# CHECKNORELAX-NEXT: auipc ra, 0
51+
# CHECKNORELAX-NEXT: R_RISCV_CALL_PLT f
52+
# CHECKNORELAX-NEXT: R_RISCV_RELAX *ABS*
53+
# CHECKNORELAX-NEXT: jalr ra, 16(ra)
54+
# CHECKNORELAX-NEXT: auipc ra, 0
55+
# CHECKNORELAX-NEXT: R_RISCV_CALL_PLT f
56+
# CHECKNORELAX-NEXT: R_RISCV_RELAX *ABS*
57+
# CHECKNORELAX-NEXT: jalr ra, 8(ra)
58+
# CHECKNORELAX-EMPTY:
59+
# CHECKNORELAX-NEXT: <f>:
60+
# CHECKNORELAX-NEXT: jalr zero, 0(ra)
61+
# CHECKNORELAX-NEXT: R_RISCV_ALIGN *ABS*+0x4
62+
63+
.global _start
64+
_start:
65+
call f
66+
call f
67+
.balign 8
68+
f:
69+
ret

0 commit comments

Comments
 (0)