diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp index 8e556b905b1c2c..26977015dab3ac 100644 --- a/lld/ELF/Arch/RISCV.cpp +++ b/lld/ELF/Arch/RISCV.cpp @@ -48,6 +48,11 @@ class RISCV final : public TargetInfo { } // end anonymous namespace +// These are internal relocation numbers for GP relaxation. They aren't part +// of the psABI spec. +#define INTERNAL_R_RISCV_GPREL_I 256 +#define INTERNAL_R_RISCV_GPREL_S 257 + const uint64_t dtpOffset = 0x800; enum Op { @@ -62,6 +67,7 @@ enum Op { enum Reg { X_RA = 1, + X_GP = 3, X_TP = 4, X_T0 = 5, X_T1 = 6, @@ -436,6 +442,20 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { return; } + case INTERNAL_R_RISCV_GPREL_I: + case INTERNAL_R_RISCV_GPREL_S: { + Defined *gp = ElfSym::riscvGlobalPointer; + int64_t displace = SignExtend64(val - gp->getVA(), bits); + checkInt(loc, displace, 12, rel); + uint32_t insn = (read32le(loc) & ~(31 << 15)) | (X_GP << 15); + if (rel.type == INTERNAL_R_RISCV_GPREL_I) + insn = setLO12_I(insn, displace); + else + insn = setLO12_S(insn, displace); + write32le(loc, insn); + return; + } + case R_RISCV_ADD8: *loc += val; return; @@ -614,6 +634,30 @@ static void relaxTlsLe(const InputSection &sec, size_t i, uint64_t loc, } } +static void relaxHi20Lo12(const InputSection &sec, size_t i, uint64_t loc, + Relocation &r, uint32_t &remove) { + const Defined *gp = ElfSym::riscvGlobalPointer; + if (!gp) + return; + + if (!isInt<12>(r.sym->getVA(r.addend) - gp->getVA())) + return; + + switch (r.type) { + case R_RISCV_HI20: + // Remove lui rd, %hi20(x). + sec.relaxAux->relocTypes[i] = R_RISCV_RELAX; + remove = 4; + break; + case R_RISCV_LO12_I: + sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_GPREL_I; + break; + case R_RISCV_LO12_S: + sec.relaxAux->relocTypes[i] = INTERNAL_R_RISCV_GPREL_S; + break; + } +} + static bool relax(InputSection &sec) { const uint64_t secAddr = sec.getVA(); auto &aux = *sec.relaxAux; @@ -665,6 +709,13 @@ static bool relax(InputSection &sec) { sec.relocs()[i + 1].type == R_RISCV_RELAX) relaxTlsLe(sec, i, loc, r, remove); break; + case R_RISCV_HI20: + case R_RISCV_LO12_I: + case R_RISCV_LO12_S: + if (i + 1 != sec.relocs().size() && + sec.relocs()[i + 1].type == R_RISCV_RELAX) + relaxHi20Lo12(sec, i, loc, r, remove); + break; } // For all anchors whose offsets are <= r.offset, they are preceded by @@ -778,6 +829,9 @@ void elf::riscvFinalizeRelax(int passes) { } } else if (RelType newType = aux.relocTypes[i]) { switch (newType) { + case INTERNAL_R_RISCV_GPREL_I: + case INTERNAL_R_RISCV_GPREL_S: + break; case R_RISCV_RELAX: // Used by relaxTlsLe to indicate the relocation is ignored. break; diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index a6e87511bcfb62..c446508dfa2f0f 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -253,6 +253,7 @@ struct Config { bool printGcSections; bool printIcfSections; bool relax; + bool relaxGP; bool relocatable; bool relrGlibc = false; bool relrPackDynRelocs = false; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 5099f1cbe96a6a..5ba5eab2335501 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -357,6 +357,9 @@ static void checkOptions() { if (config->pcRelOptimize && config->emachine != EM_PPC64) error("--pcrel-optimize is only supported on PowerPC64 targets"); + if (config->relaxGP && config->emachine != EM_RISCV) + error("--relax-gp is only supported on RISC-V targets"); + if (config->pie && config->shared) error("-shared and -pie may not be used together"); @@ -1217,6 +1220,7 @@ static void readConfigs(opt::InputArgList &args) { config->printSymbolOrder = args.getLastArgValue(OPT_print_symbol_order); config->relax = args.hasFlag(OPT_relax, OPT_no_relax, true); + config->relaxGP = args.hasFlag(OPT_relax_gp, OPT_no_relax_gp, false); config->rpath = getRpath(args); config->relocatable = args.hasArg(OPT_relocatable); diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index 87d84246ac45d7..5b3d9f6c40deea 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -355,6 +355,10 @@ defm relax: BB<"relax", "Enable target-specific relaxations if supported (default)", "Disable target-specific relaxations">; +defm relax_gp: BB<"relax-gp", + "Enable global pointer relaxation", + "Disable global pointer relaxation (default)">; + defm reproduce: EEq<"reproduce", "Write tar file containing inputs and command to reproduce link">; diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp index c2375e07f248c3..62a8a3c30664e3 100644 --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -71,6 +71,7 @@ Defined *ElfSym::globalOffsetTable; Defined *ElfSym::mipsGp; Defined *ElfSym::mipsGpDisp; Defined *ElfSym::mipsLocalGp; +Defined *ElfSym::riscvGlobalPointer; Defined *ElfSym::relaIpltStart; Defined *ElfSym::relaIpltEnd; Defined *ElfSym::tlsModuleBase; diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index 5e3d52d4e55302..e0b74faafeca62 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -512,6 +512,9 @@ struct ElfSym { static Defined *mipsGpDisp; static Defined *mipsLocalGp; + // __global_pointer$ for RISC-V. + static Defined *riscvGlobalPointer; + // __rel{,a}_iplt_{start,end} symbols. static Defined *relaIpltStart; static Defined *relaIpltEnd; diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 534794bd835a39..f6b62b778b3626 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1872,10 +1872,20 @@ template void Writer::finalizeSections() { // should only be defined in an executable. If .sdata does not exist, its // value/section does not matter but it has to be relative, so set its // st_shndx arbitrarily to 1 (Out::elfHeader). - if (config->emachine == EM_RISCV && !config->shared) { - OutputSection *sec = findSection(".sdata"); - addOptionalRegular("__global_pointer$", sec ? sec : Out::elfHeader, 0x800, - STV_DEFAULT); + if (config->emachine == EM_RISCV) { + ElfSym::riscvGlobalPointer = nullptr; + if (!config->shared) { + OutputSection *sec = findSection(".sdata"); + addOptionalRegular( + "__global_pointer$", sec ? sec : Out::elfHeader, 0x800, STV_DEFAULT); + // Set riscvGlobalPointer to be used by the optional global pointer + // relaxation. + if (config->relaxGP) { + Symbol *s = symtab.find("__global_pointer$"); + if (s && s->isDefined()) + ElfSym::riscvGlobalPointer = cast(s); + } + } } if (config->emachine == EM_386 || config->emachine == EM_X86_64) { diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1 index 09b82995ead41d..d97cd26a6378b3 100644 --- a/lld/docs/ld.lld.1 +++ b/lld/docs/ld.lld.1 @@ -493,6 +493,8 @@ and .It Fl -pop-state Restore the states saved by .Fl -push-state. +.It Fl --relax-gp +Enable global pointer relaxation for RISC-V. .It Fl -relocatable , Fl r Create relocatable object file. .It Fl -reproduce Ns = Ns Ar path diff --git a/lld/test/ELF/riscv-relax-hi20-lo12-pie.s b/lld/test/ELF/riscv-relax-hi20-lo12-pie.s new file mode 100644 index 00000000000000..87e4f8f000e599 --- /dev/null +++ b/lld/test/ELF/riscv-relax-hi20-lo12-pie.s @@ -0,0 +1,33 @@ +# REQUIRES: riscv +# RUN: rm -rf %t && split-file %s %t && cd %t + +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax a.s -o rv32.o +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax a.s -o rv64.o +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax a.s -o rv64-pie.o + +# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32.o lds -pie -o rv32 +# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64.o lds -shared -o rv64 +# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv32 | FileCheck %s +# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv64 | FileCheck %s + +# CHECK: lui a0, 512 +# CHECK-NEXT: addi a0, a0, 1 +# CHECK-NEXT: lw a0, 1(a0) +# CHECK-NEXT: sw a0, 1(a0) + +#--- a.s +.globl abs +abs = 0x200001 + +.global _start +_start: + lui a0, %hi(abs) + addi a0, a0, %lo(abs) + lw a0, %lo(abs)(a0) + sw a0, %lo(abs)(a0) + +#--- lds +SECTIONS { + .text : {*(.text) } + .sdata 0x200000 : {} +} diff --git a/lld/test/ELF/riscv-relax-hi20-lo12.s b/lld/test/ELF/riscv-relax-hi20-lo12.s new file mode 100644 index 00000000000000..feef08767c19be --- /dev/null +++ b/lld/test/ELF/riscv-relax-hi20-lo12.s @@ -0,0 +1,61 @@ +# REQUIRES: riscv +# RUN: rm -rf %t && split-file %s %t && cd %t + +# RUN: llvm-mc -filetype=obj -triple=riscv32-unknown-elf -mattr=+relax a.s -o rv32.o +# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=+relax a.s -o rv64.o + +# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv32.o lds -o rv32 +# RUN: ld.lld --relax-gp --undefined=__global_pointer$ rv64.o lds -o rv64 +# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv32 | FileCheck %s +# RUN: llvm-objdump -td -M no-aliases --no-show-raw-insn rv64 | FileCheck %s + +# CHECK: 00000028 l .text {{0*}}0 a + +# CHECK-NOT: lui +# CHECK: addi a0, gp, -2048 +# CHECK-NEXT: lw a0, -2048(gp) +# CHECK-NEXT: sw a0, -2048(gp) +# CHECK-NOT: lui +# CHECK-NEXT: addi a0, gp, 2047 +# CHECK-NEXT: lb a0, 2047(gp) +# CHECK-NEXT: sb a0, 2047(gp) +# CHECK-NEXT: lui a0, 513 +# CHECK-NEXT: addi a0, a0, 0 +# CHECK-NEXT: lw a0, 0(a0) +# CHECK-NEXT: sw a0, 0(a0) +# CHECK-EMPTY: +# CHECK-NEXT: : +# CHECK-NEXT: addi a0, a0, 1 + +#--- a.s +.global _start +_start: + lui a0, %hi(foo) + addi a0, a0, %lo(foo) + lw a0, %lo(foo)(a0) + sw a0, %lo(foo)(a0) + lui a0, %hi(bar) + addi a0, a0, %lo(bar) + lb a0, %lo(bar)(a0) + sb a0, %lo(bar)(a0) + lui a0, %hi(norelax) + addi a0, a0, %lo(norelax) + lw a0, %lo(norelax)(a0) + sw a0, %lo(norelax)(a0) +a: + addi a0, a0, 1 + +.section .sdata,"aw" +foo: + .word 0 + .space 4091 +bar: + .byte 0 +norelax: + .word 0 + +#--- lds +SECTIONS { + .text : {*(.text) } + .sdata 0x200000 : { } +}