summaryrefslogtreecommitdiff
path: root/lld
diff options
context:
space:
mode:
Diffstat (limited to 'lld')
-rw-r--r--lld/ELF/Arch/RISCV.cpp54
-rw-r--r--lld/ELF/Config.h1
-rw-r--r--lld/ELF/Driver.cpp4
-rw-r--r--lld/ELF/Options.td4
-rw-r--r--lld/ELF/Symbols.cpp1
-rw-r--r--lld/ELF/Symbols.h3
-rw-r--r--lld/ELF/Writer.cpp18
-rw-r--r--lld/docs/ld.lld.12
-rw-r--r--lld/test/ELF/riscv-relax-hi20-lo12-pie.s33
-rw-r--r--lld/test/ELF/riscv-relax-hi20-lo12.s61
10 files changed, 177 insertions, 4 deletions
diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 8e556b905b1c..26977015dab3 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -48,6 +48,11 @@ public:
} // 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 a6e87511bcfb..c446508dfa2f 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 5099f1cbe96a..5ba5eab23355 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 87d84246ac45..5b3d9f6c40de 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 c2375e07f248..62a8a3c30664 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 5e3d52d4e553..e0b74faafeca 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 534794bd835a..f6b62b778b36 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1872,10 +1872,20 @@ template <class ELFT> void Writer<ELFT>::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<Defined>(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 09b82995ead4..d97cd26a6378 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 000000000000..87e4f8f000e5
--- /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 000000000000..feef08767c19
--- /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: <a>:
+# 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 : { }
+}