Skip to content

[ELF] Support R_RISCV_SET_ULEB128/R_RISCV_SUB_ULEB128 in non-SHF_ALLOC sections #72610

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
merged 1 commit into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions lld/ELF/Arch/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,8 @@ RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s,
case R_RISCV_TPREL_ADD:
case R_RISCV_RELAX:
return config->relax ? R_RELAX_HINT : R_NONE;
case R_RISCV_SET_ULEB128:
return R_RISCV_LEB128;
default:
error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
") against symbol " + toString(s));
Expand Down
41 changes: 38 additions & 3 deletions lld/ELF/InputSection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,16 @@ 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 All @@ -885,6 +895,7 @@ template <class ELFT, class RelTy>
void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef<RelTy> rels) {
const unsigned bits = sizeof(typename ELFT::uint) * 8;
const TargetInfo &target = *elf::target;
const auto emachine = config->emachine;
const bool isDebug = isDebugSection(*this);
const bool isDebugLocOrRanges =
isDebug && (name == ".debug_loc" || name == ".debug_ranges");
Expand All @@ -896,14 +907,15 @@ void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef<RelTy> rels) {
break;
}

for (const RelTy &rel : rels) {
for (size_t i = 0, relsSize = rels.size(); i != relsSize; ++i) {
const RelTy &rel = rels[i];
RelType type = rel.getType(config->isMips64EL);

// GCC 8.0 or earlier have a bug that they emit R_386_GOTPC relocations
// against _GLOBAL_OFFSET_TABLE_ for .debug_info. The bug has been fixed
// in 2017 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82630), but we
// need to keep this bug-compatible code for a while.
if (config->emachine == EM_386 && type == R_386_GOTPC)
if (emachine == EM_386 && type == R_386_GOTPC)
continue;

uint64_t offset = rel.r_offset;
Expand All @@ -916,6 +928,30 @@ void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef<RelTy> rels) {
RelExpr expr = target.getRelExpr(type, sym, bufLoc);
if (expr == R_NONE)
continue;
auto *ds = dyn_cast<Defined>(&sym);

if (emachine == EM_RISCV && type == R_RISCV_SET_ULEB128) {
if (++i < relsSize &&
rels[i].getType(/*isMips64EL=*/false) == R_RISCV_SUB_ULEB128 &&
rels[i].r_offset == offset) {
uint64_t val;
if (!ds && tombstone) {
val = *tombstone;
} else {
val = sym.getVA(addend) -
(getFile<ELFT>()->getRelocTargetSym(rels[i]).getVA(0) +
getAddend<ELFT>(rels[i]));
}
if (overwriteULEB128(bufLoc, val) >= 0x80)
errorOrWarn(getLocation(offset) + ": ULEB128 value " + Twine(val) +
" exceeds available space; references '" +
lld::toString(sym) + "'");
continue;
}
errorOrWarn(getLocation(offset) +
": R_RISCV_SET_ULEB128 not paired with R_RISCV_SUB_SET128");
return;
}

if (tombstone ||
(isDebug && (type == target.symbolicRel || expr == R_DTPREL))) {
Expand Down Expand Up @@ -947,7 +983,6 @@ void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef<RelTy> rels) {
//
// TODO To reduce disruption, we use 0 instead of -1 as the tombstone
// value. Enable -1 in a future release.
auto *ds = dyn_cast<Defined>(&sym);
if (!sym.getOutputSection() || (ds && ds->folded && !isDebugLine)) {
// If -z dead-reloc-in-nonalloc= is specified, respect it.
const uint64_t value = tombstone ? SignExtend64<bits>(*tombstone)
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/Relocations.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ enum RelExpr {
R_PPC64_TOCBASE,
R_PPC64_RELAX_GOT_PC,
R_RISCV_ADD,
R_RISCV_LEB128,
R_RISCV_PC_INDIRECT,
// Same as R_PC but with page-aligned semantics.
R_LOONGARCH_PAGE_PC,
Expand Down
129 changes: 129 additions & 0 deletions lld/test/ELF/riscv-reloc-leb128.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# 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

# REL: .rela.debug_rnglists {
# REL-NEXT: 0x0 R_RISCV_SET_ULEB128 w1 0x83
# REL-NEXT: 0x0 R_RISCV_SUB_ULEB128 w2 0x0
# 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
# REL-NEXT: 0x3 R_RISCV_SUB_ULEB128 w2 0x0
# REL-NEXT: 0x5 R_RISCV_SET_ULEB128 w2 0x3FF8
# REL-NEXT: 0x5 R_RISCV_SUB_ULEB128 w1 0x0
# REL-NEXT: 0x8 R_RISCV_SET_ULEB128 w1 0x4009
# REL-NEXT: 0x8 R_RISCV_SUB_ULEB128 w2 0x0
# REL-NEXT: 0xB R_RISCV_SET_ULEB128 w2 0x1FFFF8
# REL-NEXT: 0xB R_RISCV_SUB_ULEB128 w1 0x0
# REL-NEXT: 0xF R_RISCV_SET_ULEB128 w1 0x200009
# REL-NEXT: 0xF R_RISCV_SUB_ULEB128 w2 0x0
# REL-NEXT: }
# REL: .rela.debug_loclists {
# REL-NEXT: 0x0 R_RISCV_SET_ULEB128 w2 0x3
# REL-NEXT: 0x0 R_RISCV_SUB_ULEB128 w1 0x4
# REL-NEXT: 0x1 R_RISCV_SET_ULEB128 x2 0x0
# REL-NEXT: 0x1 R_RISCV_SUB_ULEB128 x1 0x0
# REL-NEXT: }

# 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 '.debug_rnglists':
# CHECK-NEXT: 0x00000000 7ffc0085 01fcff00 858001fc ffff0085 .
# CHECK-NEXT: 0x00000010 808001 .
# CHECK: Hex dump of section '.debug_loclists':
# CHECK-NEXT: 0x00000000 0300 .

# RUN: ld.lld -shared --gc-sections -z dead-reloc-in-nonalloc=.debug_loclists=0x7f a.o -o a127.so
# RUN: llvm-readelf -x .debug_loclists a127.so | FileCheck %s --check-prefix=CHECK127
# CHECK127: Hex dump of section '.debug_loclists':
# CHECK127-NEXT: 0x00000000 037f .

# RUN: not ld.lld -shared --gc-sections -z dead-reloc-in-nonalloc=.debug_loclists=0x80 a.o 2>&1 | FileCheck %s --check-prefix=CHECK128
# CHECK128: error: a.o:(.debug_loclists+0x1): ULEB128 value 128 exceeds available space; references 'x2'

# 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
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we could have some better error message for orphan R_RISCV_SUB_ULEB128?

Copy link
Member Author

Choose a reason for hiding this comment

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

We could, but I feel that the utility of the dedicated diagnostic will be very low. So it is probably not useful to have more code on it...

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I agree that's not useful and not bring too much value...general that happened means some thing really broken or corrupted.


# 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: 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: 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
# 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
# OVERFLOW: error: overflow.o:(.debug_rnglists+0x8): ULEB128 value 128 exceeds available space; references 'w2'

#--- a.s
.section .text.w,"axR"
w1:
call foo # 4 bytes after relaxation
w2:

.section .text.x,"ax"
x1:
call foo # 4 bytes after relaxation
x2:

.section .debug_rnglists
.uleb128 w1-w2+131 # initial value: 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_loclists
.reloc ., R_RISCV_SET_ULEB128, w2+3
.reloc ., R_RISCV_SUB_ULEB128, w1+4 # SUB with a non-zero addend
.byte 0
.uleb128 x2-x1 # references discarded symbols

#--- sub.s
w1: call foo; w2:
.section .debug_rnglists
.quad 0;
.reloc ., R_RISCV_SUB_ULEB128, w2+120
.byte 0x7f

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

#--- unpaired2.s
w1: call foo; w2:
.section .debug_rnglists
.quad 0
.reloc ., R_RISCV_SET_ULEB128, w2+120
.reloc .+1, R_RISCV_SUB_ULEB128, w1
.byte 0x7f

#--- unpaired3.s
w1: call foo; w2:
.section .debug_rnglists
.quad 0
.reloc ., R_RISCV_SET_ULEB128, w2+120
.reloc ., R_RISCV_SUB64, w1
.byte 0x7f

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