Skip to content

Commit

Permalink
[ELF] Support R_RISCV_SET_ULEB128/R_RISCV_SUB_ULEB128 in SHF_ALLOC se…
Browse files Browse the repository at this point in the history
…ctions (llvm#77261)

Complement llvm#72610 (non-SHF_ALLOC sections). GCC-generated
.gcc_exception_table has the SHF_ALLOC flag and may contain
R_RISCV_SET_ULEB128/R_RISCV_SUB_ULEB128 relocations.
  • Loading branch information
MaskRay authored and justinfargnoli committed Jan 28, 2024
1 parent dccb542 commit 8415a09
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 23 deletions.
42 changes: 42 additions & 0 deletions lld/ELF/Arch/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class RISCV final : public TargetInfo {
const uint8_t *loc) const override;
void relocate(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override;
bool relaxOnce(int pass) const override;
};

Expand Down Expand Up @@ -307,6 +308,7 @@ RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s,
case R_RISCV_RELAX:
return config->relax ? R_RELAX_HINT : R_NONE;
case R_RISCV_SET_ULEB128:
case R_RISCV_SUB_ULEB128:
return R_RISCV_LEB128;
default:
error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
Expand Down Expand Up @@ -515,6 +517,46 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
}
}

void RISCV::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
uint64_t secAddr = sec.getOutputSection()->addr;
if (auto *s = dyn_cast<InputSection>(&sec))
secAddr += s->outSecOff;
else if (auto *ehIn = dyn_cast<EhInputSection>(&sec))
secAddr += ehIn->getParent()->outSecOff;
for (size_t i = 0, size = sec.relocs().size(); i != size; ++i) {
const Relocation &rel = sec.relocs()[i];
uint8_t *loc = buf + rel.offset;
const uint64_t val =
sec.getRelocTargetVA(sec.file, rel.type, rel.addend,
secAddr + rel.offset, *rel.sym, rel.expr);

switch (rel.expr) {
case R_RELAX_HINT:
break;
case R_RISCV_LEB128:
if (i + 1 < size) {
const Relocation &rel1 = sec.relocs()[i + 1];
if (rel.type == R_RISCV_SET_ULEB128 &&
rel1.type == R_RISCV_SUB_ULEB128 && rel.offset == rel1.offset) {
auto val = rel.sym->getVA(rel.addend) - rel1.sym->getVA(rel1.addend);
if (overwriteULEB128(loc, val) >= 0x80)
errorOrWarn(sec.getLocation(rel.offset) + ": ULEB128 value " +
Twine(val) + " exceeds available space; references '" +
lld::toString(*rel.sym) + "'");
++i;
continue;
}
}
errorOrWarn(sec.getLocation(rel.offset) +
": R_RISCV_SET_ULEB128 not paired with R_RISCV_SUB_SET128");
return;
default:
relocate(loc, rel, val);
break;
}
}
}

namespace {
struct SymbolAnchor {
uint64_t offset;
Expand Down
11 changes: 1 addition & 10 deletions lld/ELF/InputSection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,7 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
case R_RELAX_TLS_LD_TO_LE_ABS:
case R_RELAX_GOT_PC_NOPIC:
case R_RISCV_ADD:
case R_RISCV_LEB128:
return sym.getVA(a);
case R_ADDEND:
return a;
Expand Down Expand Up @@ -875,16 +876,6 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
}
}

// Overwrite a ULEB128 value and keep the original length.
static uint64_t overwriteULEB128(uint8_t *bufLoc, uint64_t val) {
while (*bufLoc & 0x80) {
*bufLoc++ = 0x80 | (val & 0x7f);
val >>= 7;
}
*bufLoc = val;
return val;
}

// This function applies relocations to sections without SHF_ALLOC bit.
// Such sections are never mapped to memory at runtime. Debug sections are
// an example. Relocations in non-alloc sections are much easier to
Expand Down
4 changes: 2 additions & 2 deletions lld/ELF/Relocations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -988,8 +988,8 @@ bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type,
if (!config->isPic)
return true;

// The size of a non preemptible symbol is a constant.
if (e == R_SIZE)
// Constant when referencing a non-preemptible symbol.
if (e == R_SIZE || e == R_RISCV_LEB128)
return true;

// For the target and the relocation, we want to know if they are
Expand Down
10 changes: 10 additions & 0 deletions lld/ELF/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,16 @@ inline void write32(void *p, uint32_t v) {
inline void write64(void *p, uint64_t v) {
llvm::support::endian::write64(p, v, config->endianness);
}

// Overwrite a ULEB128 value and keep the original length.
inline uint64_t overwriteULEB128(uint8_t *bufLoc, uint64_t val) {
while (*bufLoc & 0x80) {
*bufLoc++ = 0x80 | (val & 0x7f);
val >>= 7;
}
*bufLoc = val;
return val;
}
} // namespace elf
} // namespace lld

Expand Down
80 changes: 69 additions & 11 deletions lld/test/ELF/riscv-reloc-leb128.s
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# REQUIRES: riscv
# RUN: rm -rf %t && split-file %s %t && cd %t
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax a.s -o a.o
# RUN: llvm-readobj -r -x .debug_rnglists -x .debug_loclists a.o | FileCheck %s --check-prefix=REL
# RUN: ld.lld -shared --gc-sections a.o -o a.so
# RUN: llvm-readelf -x .debug_rnglists -x .debug_loclists a.so | FileCheck %s
# RUN: llvm-readobj -r -x .gcc_except_table -x .debug_rnglists -x .debug_loclists a.o | FileCheck %s --check-prefix=REL
# RUN: ld.lld -shared --gc-sections --noinhibit-exec a.o -o a.so
# RUN: llvm-readelf -x .gcc_except_table -x .debug_rnglists -x .debug_loclists a.so | FileCheck %s

# REL: .rela.debug_rnglists {
# REL-NEXT: 0x0 R_RISCV_SET_ULEB128 w1 0x83
# REL-NEXT: 0x0 R_RISCV_SUB_ULEB128 w2 0x0
# REL-NEXT: 0x0 R_RISCV_SET_ULEB128 w1 0x82
# REL-NEXT: 0x0 R_RISCV_SUB_ULEB128 w2 0xFFFFFFFFFFFFFFFF
# REL-NEXT: 0x1 R_RISCV_SET_ULEB128 w2 0x78
# REL-NEXT: 0x1 R_RISCV_SUB_ULEB128 w1 0x0
# REL-NEXT: 0x3 R_RISCV_SET_ULEB128 w1 0x89
Expand All @@ -28,12 +28,18 @@
# REL-NEXT: 0x1 R_RISCV_SUB_ULEB128 x1 0x0
# REL-NEXT: }

# REL: Hex dump of section '.gcc_except_table':
# REL-NEXT: 0x00000000 7b800181 01808001 81800180 80800181 {
# REL-NEXT: 0x00000010 808001 .
# REL: Hex dump of section '.debug_rnglists':
# REL-NEXT: 0x00000000 7b800181 01808001 81800180 80800181 {
# REL-NEXT: 0x00000010 808001 .
# REL: Hex dump of section '.debug_loclists':
# REL-NEXT: 0x00000000 0008 .

# CHECK: Hex dump of section '.gcc_except_table':
# CHECK-NEXT: 0x[[#%x,]] 7ffc0085 01fcff00 858001fc ffff0085 .
# CHECK-NEXT: 0x[[#%x,]] 808001 .
# CHECK: Hex dump of section '.debug_rnglists':
# CHECK-NEXT: 0x00000000 7ffc0085 01fcff00 858001fc ffff0085 .
# CHECK-NEXT: 0x00000010 808001 .
Expand All @@ -50,21 +56,32 @@

# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax sub.s -o sub.o
# RUN: not ld.lld -shared sub.o 2>&1 | FileCheck %s --check-prefix=SUB
# SUB: error: sub.o:(.debug_rnglists+0x8): unknown relocation (61) against symbol w2
# SUB: error: sub.o:(.debug_rnglists+0x8): has non-ABS relocation R_RISCV_SUB_ULEB128 against symbol 'w2'

# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax unpaired1.s -o unpaired1.o
# RUN: not ld.lld -shared unpaired1.o 2>&1 | FileCheck %s --check-prefix=UNPAIRED
# RUN: not ld.lld -shared --threads=1 unpaired1.o 2>&1 | FileCheck %s --check-prefix=UNPAIRED
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax unpaired2.s -o unpaired2.o
# RUN: not ld.lld -shared unpaired2.o 2>&1 | FileCheck %s --check-prefix=UNPAIRED
# RUN: not ld.lld -shared --threads=1 unpaired2.o 2>&1 | FileCheck %s --check-prefix=UNPAIRED
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax unpaired3.s -o unpaired3.o
# RUN: not ld.lld -shared unpaired3.o 2>&1 | FileCheck %s --check-prefix=UNPAIRED
# RUN: not ld.lld -shared --threads=1 unpaired3.o 2>&1 | FileCheck %s --check-prefix=UNPAIRED
# UNPAIRED: error: {{.*}}.o:(.alloc+0x8): R_RISCV_SET_ULEB128 not paired with R_RISCV_SUB_SET128
# UNPAIRED: error: {{.*}}.o:(.debug_rnglists+0x8): R_RISCV_SET_ULEB128 not paired with R_RISCV_SUB_SET128

# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax overflow.s -o overflow.o
# RUN: not ld.lld -shared overflow.o 2>&1 | FileCheck %s --check-prefix=OVERFLOW
# RUN: not ld.lld -shared --threads=1 overflow.o 2>&1 | FileCheck %s --check-prefix=OVERFLOW
# OVERFLOW: error: overflow.o:(.alloc+0x8): ULEB128 value 128 exceeds available space; references 'w2'
# OVERFLOW: error: overflow.o:(.debug_rnglists+0x8): ULEB128 value 128 exceeds available space; references 'w2'

# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax preemptable.s -o preemptable.o
# RUN: not ld.lld -shared --threads=1 preemptable.o 2>&1 | FileCheck %s --check-prefix=PREEMPTABLE --implicit-check-not=error:
# PREEMPTABLE: error: relocation R_RISCV_SET_ULEB128 cannot be used against symbol 'w2'; recompile with -fPIC
# PREEMPTABLE: error: relocation R_RISCV_SUB_ULEB128 cannot be used against symbol 'w1'; recompile with -fPIC

#--- a.s
.cfi_startproc
.cfi_lsda 0x1b,.LLSDA0
.cfi_endproc

.section .text.w,"axR"
w1:
call foo # 4 bytes after relaxation
Expand All @@ -75,8 +92,22 @@ x1:
call foo # 4 bytes after relaxation
x2:

.section .gcc_except_table,"a"
.LLSDA0:
.reloc ., R_RISCV_SET_ULEB128, w1+130
.reloc ., R_RISCV_SUB_ULEB128, w2-1 # non-zero addend for SUB
.byte 0x7b
.uleb128 w2-w1+120 # initial value: 0x0180
.uleb128 w1-w2+137 # initial value: 0x0181
.uleb128 w2-w1+16376 # initial value: 0x018080
.uleb128 w1-w2+16393 # initial value: 0x018081
.uleb128 w2-w1+2097144 # initial value: 0x01808080
.uleb128 w1-w2+2097161 # initial value: 0x01808081

.section .debug_rnglists
.uleb128 w1-w2+131 # initial value: 0x7b
.reloc ., R_RISCV_SET_ULEB128, w1+130
.reloc ., R_RISCV_SUB_ULEB128, w2-1 # non-zero addend for SUB
.byte 0x7b
.uleb128 w2-w1+120 # initial value: 0x0180
.uleb128 w1-w2+137 # initial value: 0x0181
.uleb128 w2-w1+16376 # initial value: 0x018080
Expand All @@ -99,13 +130,22 @@ w1: call foo; w2:

#--- unpaired1.s
w1: call foo; w2:
.section .alloc,"a"
.quad 0
.reloc ., R_RISCV_SET_ULEB128, w2+120
.byte 0x7f
.section .debug_rnglists
.quad 0;
.reloc ., R_RISCV_SET_ULEB128, w2+120
.byte 0x7f

#--- unpaired2.s
w1: call foo; w2:
.section .alloc,"a"
.quad 0
.reloc ., R_RISCV_SET_ULEB128, w2+120
.reloc .+1, R_RISCV_SUB_ULEB128, w1
.byte 0x7f
.section .debug_rnglists
.quad 0
.reloc ., R_RISCV_SET_ULEB128, w2+120
Expand All @@ -114,6 +154,11 @@ w1: call foo; w2:

#--- unpaired3.s
w1: call foo; w2:
.section .alloc,"a"
.quad 0
.reloc ., R_RISCV_SET_ULEB128, w2+120
.reloc ., R_RISCV_SUB64, w1
.byte 0x7f
.section .debug_rnglists
.quad 0
.reloc ., R_RISCV_SET_ULEB128, w2+120
Expand All @@ -122,8 +167,21 @@ w1: call foo; w2:

#--- overflow.s
w1: call foo; w2:
.section .alloc,"a"
.quad 0
.reloc ., R_RISCV_SET_ULEB128, w2+124
.reloc ., R_RISCV_SUB_ULEB128, w1
.byte 0x7f
.section .debug_rnglists
.quad 0
.reloc ., R_RISCV_SET_ULEB128, w2+124
.reloc ., R_RISCV_SUB_ULEB128, w1
.byte 0x7f

#--- preemptable.s
.globl w1, w2
w1: call foo; w2:
.section .alloc,"a"
.uleb128 w2-w1
.section .debug_rnglists
.uleb128 w2-w1

0 comments on commit 8415a09

Please sign in to comment.