diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -979,6 +979,52 @@ } target->relocateOne(bufLoc, type, targetVA); break; + case R_PC: + if (config->emachine == EM_RISCV && rel.sym->isUndefWeak() && + !config->isPic) { + // Patch addresses of undefined weak symbols to be NULL. + if (rel.addend != 0) + error(getErrorLocation(bufLoc) + + "undefined weak symbol relocation with non-zero addend"); + uint32_t insn = read32le(bufLoc); + switch (type) { + case R_RISCV_CALL: + // Clear immediate in following jalr. + write32le(bufLoc + 4, read32le(bufLoc + 4) & 0x000fffff); + LLVM_FALLTHROUGH; + case R_RISCV_PCREL_HI20: + // Rewrite auipc rd, %pcrel_hi(sym) to lui rd, 0. + write32le(bufLoc, (insn & 0x00000f80) | 0x00000037); + break; + case R_RISCV_JAL: + write32le(bufLoc, insn & 0x00000fff); + break; + case R_RISCV_BRANCH: + error(getErrorLocation(bufLoc) + + "conditional branch to undefined weak symbol '" + + rel.sym->getName() + "'"); + break; + default: + error(getErrorLocation(bufLoc) + + "unknown RISC-V relocation " + toString(type) + + " for undefined weak symbol"); + break; + } + break; + } + target->relocateOne(bufLoc, type, targetVA); + break; + case R_RISCV_PC_INDIRECT: + if (!config->isPic) { + const Relocation *hiRel = getRISCVPCRelHi20(rel.sym, rel.addend); + if (hiRel && hiRel->type == R_RISCV_PCREL_HI20 + && hiRel->sym->isUndefWeak() && hiRel->addend == 0) { + // Patch addresses of undefined weak symbols to be NULL. + targetVA = 0; + } + } + target->relocateOne(bufLoc, type, targetVA); + break; default: target->relocateOne(bufLoc, type, targetVA); break; diff --git a/lld/test/ELF/riscv-undefined-weak-branch.s b/lld/test/ELF/riscv-undefined-weak-branch.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/riscv-undefined-weak-branch.s @@ -0,0 +1,15 @@ +// REQUIRES: riscv +// RUN: llvm-mc -filetype=obj -triple=riscv64-none-linux %s -o %t.o +// RUN: not ld.lld %t.o -o /dev/null 2>&1 | FileCheck %s + +// Check that we don't permit conditional branches to undefined weak +// symbols in non-PIC code. + + .weak target + + .text + .global _start +_start: + beq a0, a1, target + +// CHECK: error: {{.*}}: conditional branch to undefined weak symbol 'target' diff --git a/lld/test/ELF/riscv-undefined-weak.s b/lld/test/ELF/riscv-undefined-weak.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/riscv-undefined-weak.s @@ -0,0 +1,42 @@ +// REQUIRES: riscv +// RUN: llvm-mc -filetype=obj -triple=riscv64-none-linux %s -o %t.o +// RUN: ld.lld %t.o -o %t +// RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s + +// Check that undefined weak symbols are resolved to NULL in non-PIC +// RISC-V code. + + .weak target + + .text + .global _start +_start: +// R_RISCV_PCREL_HI20 + R_RISCV_LO12_I + la a0, target +// R_RISCV_PCREL_HI20 + R_RISCV_LO12_I + ld a1, target +// R_RISCV_PCREL_HI20 + R_RISCV_LO12_S + sd a2, target, t0 +// R_RISCV_JAL + j target +// R_RISCV_JAL + jal target +// R_RISCV_CALL + call target +// R_RISCV_CALL + tail target + +// CHECK: Disassembly of section .text: +// CHECK-EMPTY: +// CHECK: 11120: lui a0, 0 +// CHECK-NEXT: 11124: mv a0, a0 +// CHECK: 11128: lui a1, 0 +// CHECK-NEXT: 1112c: ld a1, 0(a1) +// CHECK: 11130: lui t0, 0 +// CHECK-NEXT: 11134: sd a2, 0(t0) +// CHECK-NEXT: 11138: j 0 +// CHECK-NEXT: 1113c: jal 0 +// CHECK-NEXT: 11140: lui ra, 0 +// CHECK-NEXT: 11144: jalr ra +// CHECK-NEXT: 11148: lui t1, 0 +// CHECK-NEXT: 1114c: jr t1