diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp --- a/lld/ELF/Arch/RISCV.cpp +++ b/lld/ELF/Arch/RISCV.cpp @@ -7,6 +7,8 @@ //===----------------------------------------------------------------------===// #include "InputFiles.h" +#include "Symbols.h" +#include "SyntheticSections.h" #include "Target.h" using namespace llvm; @@ -25,11 +27,33 @@ RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const override; void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + + void writeGotPltHeader(uint8_t *Buf) const override; + void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; + + void writePltHeader(uint8_t *Buf) const override; + + void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, + int32_t Index, unsigned RelOff) const override; + + bool usesOnlyLowPageBits(RelType Type) const override; }; } // end anonymous namespace -RISCV::RISCV() { NoneRel = R_RISCV_NONE; } +RISCV::RISCV() { + NoneRel = R_RISCV_NONE; + CopyRel = R_RISCV_COPY; + RelativeRel = R_RISCV_RELATIVE; + GotRel = Config->Is64 ? R_RISCV_64 : R_RISCV_32; + PltRel = R_RISCV_JUMP_SLOT; + GotEntrySize = Config->Wordsize; + GotPltEntrySize = Config->Wordsize; + PltEntrySize = 16; + PltHeaderSize = 32; + GotPltHeaderEntriesNum = 2; + GotBaseSymInGotPlt = false; +} static uint32_t getEFlags(InputFile *F) { if (Config->Is64) @@ -59,6 +83,93 @@ return Target; } +bool RISCV::usesOnlyLowPageBits(RelType Type) const { + return Type == R_RISCV_LO12_I || Type == R_RISCV_PCREL_LO12_I || + Type == R_RISCV_LO12_S || Type == R_RISCV_PCREL_LO12_S || + // These are used in a pair to calculate relative address in debug + // sections, so they aren't really absolute. We list those here as a + // hack so the linker doesn't try to create dynamic relocations. + Type == R_RISCV_ADD8 || Type == R_RISCV_ADD16 || + Type == R_RISCV_ADD32 || Type == R_RISCV_ADD64 || + Type == R_RISCV_SUB8 || Type == R_RISCV_SUB16 || + Type == R_RISCV_SUB32 || Type == R_RISCV_SUB64 || + Type == R_RISCV_SUB6 || + Type == R_RISCV_SET6 || Type == R_RISCV_SET8 || + Type == R_RISCV_SET16 || Type == R_RISCV_SET32; +} + +static void writeUint(uint8_t *Buf, uint64_t Val) { + if (Config->Is64) + write64(Buf, Val); + else + write32(Buf, Val); +} + +void RISCV::writeGotPltHeader(uint8_t *Buf) const { + // These entries are used by the plt header and will be filled by the + // dynamic linker. Initalize both to zero at link time. + writeUint(Buf, 0); // _dl_runtime_resolve + writeUint(Buf + GotPltEntrySize, 0); // link_map +} + +void RISCV::writeGotPlt(uint8_t *Buf, const Symbol &S) const { + // .got.plt is initialized with the address of the plt header, so the first + // time a plt function is called it will jump to the header, which computes + // the plt index by the difference between the return address and the start + // address of plt. + writeUint(Buf, In.Plt->getVA()); +} + +void RISCV::writePltHeader(uint8_t *Buf) const { + if (Config->EFlags & EF_RISCV_RVE) { + error("PLT is not supported with RVE ABI"); + return; + } + + uint64_t PcRelGotPlt = In.GotPlt->getVA() - In.Plt->getVA(); + + write32le(Buf + 0, 0x00000397); // 1: auipc t2, %pcrel_hi(.got.plt) + relocateOne(Buf + 0, R_RISCV_PCREL_HI20, PcRelGotPlt); + write32le(Buf + 4, 0x41c30333); // sub t1, t1, t3 + if (Config->Is64) { + write32le(Buf + 8, 0x0003be03); // ld t3, %pcrel_lo(1b)(t2) + } else { + write32le(Buf + 8, 0x0003ae03); // lw t3, %pcrel_lo(1b)(t2) + } + relocateOne(Buf + 8, R_RISCV_PCREL_LO12_I, PcRelGotPlt); + write32le(Buf + 12, 0xfd430313); // addi t1, t1, -44 + write32le(Buf + 16, 0x00038293); // addi t0, t2, %pcrel_lo(1b) + relocateOne(Buf + 16, R_RISCV_PCREL_LO12_I, PcRelGotPlt); + if (Config->Is64) { + write32le(Buf + 20, 0x00135313); // srli t1, t1, 1 + write32le(Buf + 24, 0x0082b283); // ld t0, 8(t0) + } else { + write32le(Buf + 20, 0x00235313); // srli t1, t1, 2 + write32le(Buf + 24, 0x0042a283); // lw t0, 4(t0) + } + write32le(Buf + 28, 0x000e0067); // jr t3 +} + +void RISCV::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, + int32_t Index, unsigned RelOff) const { + if (Config->EFlags & EF_RISCV_RVE) { + error("PLT is not supported with RVE ABI"); + return; + } + + write32le(Buf + 0, 0x00000e17); // auipc t3, %pcrel_hi(f@.got.plt) + if (Config->Is64) { + write32le(Buf + 4, 0x000e3e03); // ld t3, %pcrel_lo(-4)(t3) + } else { + write32le(Buf + 4, 0x000e2e03); // lw t3, %pcrel_lo(-4)(t3) + } + write32le(Buf + 8, 0x000e0367); // jalr t1, t3 + write32le(Buf + 12, 0x00000013); // nop + + relocateOne(Buf + 0, R_RISCV_PCREL_HI20, GotEntryAddr - PltEntryAddr); + relocateOne(Buf + 4, R_RISCV_PCREL_LO12_I, GotEntryAddr - PltEntryAddr); +} + RelExpr RISCV::getRelExpr(const RelType Type, const Symbol &S, const uint8_t *Loc) const { switch (Type) { @@ -70,11 +181,15 @@ case R_RISCV_RVC_JUMP: case R_RISCV_32_PCREL: return R_PC; + case R_RISCV_CALL_PLT: + return R_PLT_PC; case R_RISCV_PCREL_LO12_I: case R_RISCV_PCREL_LO12_S: return R_RISCV_PC_INDIRECT; - case R_RISCV_RELAX: + case R_RISCV_GOT_HI20: + return R_GOT_PC; case R_RISCV_ALIGN: + case R_RISCV_RELAX: return R_HINT; default: return R_ABS; @@ -175,6 +290,7 @@ } // auipc + jalr pair + case R_RISCV_CALL_PLT: case R_RISCV_CALL: { int64_t Hi = SignExtend64(Val + 0x800, Bits) >> 12; checkInt(Loc, Hi, 20, Type); @@ -186,6 +302,7 @@ } case R_RISCV_PCREL_HI20: + case R_RISCV_GOT_HI20: case R_RISCV_HI20: { uint64_t Hi = Val + 0x800; checkInt(Loc, SignExtend64(Hi, Bits) >> 12, 20, Type); diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -572,7 +572,7 @@ }); for (auto It = Range.first; It != Range.second; ++It) - if (It->Expr == R_PC) + if (It->Expr == R_PC || It->Expr == R_PLT_PC || It->Expr == R_GOT_PC) return &*It; error("R_RISCV_PCREL_LO12 relocation points to " + IS->getObjMsg(D->Value) + diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -383,7 +383,8 @@ // file (PC, or GOT for example). static bool isRelExpr(RelExpr Expr) { return oneof(Expr); + R_PPC_CALL_PLT, R_AARCH64_PAGE_PC, R_RELAX_GOT_PC, + R_RISCV_PC_INDIRECT>(Expr); } // Returns true if a given relocation can be computed at link-time. diff --git a/lld/test/ELF/Inputs/riscv-shared.s b/lld/test/ELF/Inputs/riscv-shared.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/riscv-shared.s @@ -0,0 +1,10 @@ +.global f +.type f, @function +f: + +.data +.global x +.type x, @object +.size x, 4 +x: + .word 42 diff --git a/lld/test/ELF/riscv-copy.s b/lld/test/ELF/riscv-copy.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/riscv-copy.s @@ -0,0 +1,21 @@ +# REQUIRES: riscv + +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=-relax %s -o %t.rv32.o +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=-relax -position-independent %p/Inputs/riscv-shared.s -o %t.shared.rv32.o +# RUN: ld.lld -shared %t.shared.rv32.o -o %t.shared.rv32.so +# RUN: ld.lld %t.rv32.o %t.shared.rv32.so -o %t.rv32 + +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=-relax %s -o %t.rv64.o +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=-relax -position-independent %p/Inputs/riscv-shared.s -o %t.shared.rv64.o +# RUN: ld.lld -shared %t.shared.rv64.o -o %t.shared.rv64.so +# RUN: ld.lld %t.rv64.o %t.shared.rv64.so -o %t.rv64 + +# RUN: llvm-readobj -dyn-relocations %t.rv32 | FileCheck %s +# RUN: llvm-readobj -dyn-relocations %t.rv64 | FileCheck %s +# CHECK: Dynamic Relocations { +# CHECK-NEXT: 0x{{[0-9a-f]+}} R_RISCV_COPY x 0x0 +# CHECK-NEXT: } + +.global _start +_start: + la a0, x diff --git a/lld/test/ELF/riscv-fpic-got.s b/lld/test/ELF/riscv-fpic-got.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/riscv-fpic-got.s @@ -0,0 +1,39 @@ +# REQUIRES: riscv + +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=-relax -position-independent %s -o %t.rv32.o +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=-relax -position-independent %p/Inputs/riscv-shared.s -o %t.shared.rv32.o +# RUN: ld.lld -shared %t.shared.rv32.o -o %t.shared.rv32.so +# RUN: ld.lld %t.rv32.o %t.shared.rv32.so -o %t.rv32 + +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=-relax -position-independent %s -o %t.rv64.o +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=-relax -position-independent %p/Inputs/riscv-shared.s -o %t.shared.rv64.o +# RUN: ld.lld -shared %t.shared.rv64.o -o %t.shared.rv64.so +# RUN: ld.lld %t.rv64.o %t.shared.rv64.so -o %t.rv64 + +# RUN: llvm-readobj -sections -dyn-relocations -dyn-symbols %t.rv32 | FileCheck -DXLEN=32 %s +# RUN: llvm-readobj -sections -dyn-relocations -dyn-symbols %t.rv64 | FileCheck -DXLEN=64 %s +# CHECK: Section { +# CHECK: Name: .got +# CHECK: Address: [[GOT:0x[0-9A-F]+]] +# CHECK: } + +# CHECK: Dynamic Relocations { +# CHECK-NEXT: [[GOT]] R_RISCV_[[XLEN]] x 0x0 +# CHECK-NEXT: } + +# CHECK: DynamicSymbols [ +# CHECK: Name: x + +# RUN: llvm-objdump -d %t.rv32 | FileCheck --check-prefix=DISASM32 %s +# _start = 0x11000, .got = 0x12060 +# DISASM32: auipc a0, 1 +# DISASM32-NEXT: lw a0, 96(a0) + +# RUN: llvm-objdump -d %t.rv64 | FileCheck --check-prefix=DISASM64 %s +# _start = 0x11000, .got = 0x120c0 +# DISASM64: auipc a0, 1 +# DISASM64-NEXT: ld a0, 192(a0) + +.global _start +_start: + la a0, x diff --git a/lld/test/ELF/riscv-fpic-pie.s b/lld/test/ELF/riscv-fpic-pie.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/riscv-fpic-pie.s @@ -0,0 +1,32 @@ +# REQUIRES: riscv + +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=-relax -position-independent %s -o %t.rv32.o +# RUN: ld.lld -pie -Ttext=0x1000 %t.rv32.o -o %t.rv32 + +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=-relax -position-independent %s -o %t.rv64.o +# RUN: ld.lld -pie -Ttext=0x1000 %t.rv64.o -o %t.rv64 + +# RUN: llvm-readobj -sections -dyn-relocations %t.rv32 | FileCheck %s +# RUN: llvm-readobj -sections -dyn-relocations %t.rv64 | FileCheck %s +# CHECK: Section { +# CHECK: Name: .got +# CHECK: Address: [[GOT:0x[0-9A-F]+]] +# CHECK: } + +# CHECK: Dynamic Relocations { +# CHECK-NEXT: [[GOT]] R_RISCV_RELATIVE - 0x1000 +# CHECK-NEXT: } + +# RUN: llvm-objdump -d %t.rv32 | FileCheck --check-prefix=DISASM32 %s +# _start = 0x1000, .got = 0x3060 +# DISASM32: auipc a0, 2 +# DISASM32-NEXT: lw a0, 96(a0) + +# RUN: llvm-objdump -d %t.rv64 | FileCheck --check-prefix=DISASM64 %s +# _start = 0x1000, .got = 0x30c0 +# DISASM64: auipc a0, 2 +# DISASM64-NEXT: ld a0, 192(a0) + +.global _start +_start: + la a0, _start diff --git a/lld/test/ELF/riscv-fpic-plt.s b/lld/test/ELF/riscv-fpic-plt.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/riscv-fpic-plt.s @@ -0,0 +1,82 @@ +# REQUIRES: riscv + +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=-relax -position-independent %s -o %t.rv32.o +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=-relax -position-independent %p/Inputs/riscv-shared.s -o %t.shared.rv32.o +# RUN: ld.lld -shared %t.shared.rv32.o -o %t.shared.rv32.so +# RUN: ld.lld %t.rv32.o %t.shared.rv32.so -o %t.rv32 + +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=-relax -position-independent %s -o %t.rv64.o +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=-relax -position-independent %p/Inputs/riscv-shared.s -o %t.shared.rv64.o +# RUN: ld.lld -shared %t.shared.rv64.o -o %t.shared.rv64.so +# RUN: ld.lld %t.rv64.o %t.shared.rv64.so -o %t.rv64 + +# RUN: llvm-readobj -dyn-relocations -dyn-symbols %t.rv32 | FileCheck %s +# RUN: llvm-readobj -dyn-relocations -dyn-symbols %t.rv64 | FileCheck %s +# CHECK: Dynamic Relocations { +# CHECK-NEXT: 0x{{[0-9A-F]+}} R_RISCV_JUMP_SLOT f 0x0 +# CHECK-NEXT: } + +# CHECK: DynamicSymbols [ +# CHECK: Name: f + +# RUN: llvm-objdump -d %t.rv32 | FileCheck --check-prefix=DIS %s +# RUN: llvm-objdump -d %t.rv64 | FileCheck --check-prefix=DIS %s +# _start = 0x11000, f@plt = 0x11030 +# DIS: _start: +# DIS-NEXT: auipc ra, 0 +# DIS-NEXT: jalr ra, ra, 48 + +# RUN: llvm-objdump -d -j.plt %t.rv32 | FileCheck --check-prefix=PLT32 %s +# .plt = 0x11010, .got.plt = 0x13000 +# PLT32: 11010 .plt: +# PLT32-NEXT: auipc t2, 2 +# PLT32-NEXT: sub t1, t1, t3 +# PLT32-NEXT: lw t3, -16(t2) +# PLT32-NEXT: addi t1, t1, -44 +# PLT32-NEXT: addi t0, t2, -16 +# PLT32-NEXT: srli t1, t1, 2 +# PLT32-NEXT: lw t0, 4(t0) +# PLT32-NEXT: jr t3 +# f@plt = 0x11030, f = 0x13008 +# PLT32-NEXT: auipc t3, 2 +# PLT32-NEXT: lw t3, -40(t3) +# PLT32-NEXT: jalr t1, t3, 0 +# PLT32-NEXT: nop + +# RUN: llvm-objdump -d -j.plt %t.rv64 | FileCheck --check-prefix=PLT64 %s +# .plt = 0x11010, .got.plt = 0x13000 +# PLT64: 11010 .plt: +# PLT64-NEXT: auipc t2, 2 +# PLT64-NEXT: sub t1, t1, t3 +# PLT64-NEXT: ld t3, -16(t2) +# PLT64-NEXT: addi t1, t1, -44 +# PLT64-NEXT: addi t0, t2, -16 +# PLT64-NEXT: srli t1, t1, 1 +# PLT64-NEXT: ld t0, 8(t0) +# PLT64-NEXT: jr t3 +# f@plt = 0x11030, f = 0x13010 +# PLT64-NEXT: auipc t3, 2 +# PLT64-NEXT: ld t3, -32(t3) +# PLT64-NEXT: jalr t1, t3, 0 +# PLT64-NEXT: nop + +# RUN: llvm-objdump -s -j.got.plt %t.rv32 | FileCheck --check-prefix=GOTPLT32 %s +# .plt = 0x00011010 +# GOTPLT32: Contents of section .got.plt: +# GOTPLT32-NEXT: 13000 00000000 00000000 10100100 + +# RUN: llvm-objdump -s -j.got.plt %t.rv64 | FileCheck --check-prefix=GOTPLT64 %s +# .plt = 0x0000000000011010 +# GOTPLT64: Contents of section .got.plt: +# GOTPLT64-NEXT: 13000 00000000 00000000 00000000 00000000 +# GOTPLT64-NEXT: 13010 10100100 00000000 + +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+e,-relax -position-independent %s -o %t.rv32e.o +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+e,-relax -position-independent %p/Inputs/riscv-shared.s -o %t.shared.rv32e.o +# RUN: ld.lld -shared %t.shared.rv32e.o -o %t.shared.rv32e.so +# RUN: not ld.lld %t.rv32e.o %t.shared.rv32e.so -o %t.rv32e 2>&1 | FileCheck --check-prefix=RV32E %s +# RV32E: error: PLT is not supported with RVE ABI + +.global _start +_start: + call f@plt