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 @@ -57,6 +57,7 @@ enum Reg { X_RA = 1, + X_TP = 4, X_T0 = 5, X_T1 = 6, X_T2 = 7, @@ -270,10 +271,9 @@ case R_RISCV_TPREL_LO12_I: case R_RISCV_TPREL_LO12_S: return R_TPREL; - case R_RISCV_TPREL_ADD: - return R_NONE; case R_RISCV_ALIGN: return R_RELAX_HINT; + case R_RISCV_TPREL_ADD: case R_RISCV_RELAX: return config->relax ? R_RELAX_HINT : R_NONE; default: @@ -401,6 +401,7 @@ case R_RISCV_PCREL_LO12_I: case R_RISCV_TPREL_LO12_I: + case R_RISCV_TPREL_I: case R_RISCV_LO12_I: { uint64_t hi = (val + 0x800) >> 12; uint64_t lo = val - (hi << 12); @@ -410,6 +411,7 @@ case R_RISCV_PCREL_LO12_S: case R_RISCV_TPREL_LO12_S: + case R_RISCV_TPREL_S: case R_RISCV_LO12_S: { uint64_t hi = (val + 0x800) >> 12; uint64_t lo = val - (hi << 12); @@ -567,6 +569,33 @@ } } +// Relax local-exec TLS when hi20 is zero. +static void relaxTlsLe(const InputSection &sec, size_t i, uint64_t loc, + Relocation &r, uint32_t &remove) { + const Symbol &sym = *r.sym; + if (hi20(sym.getVA(r.addend)) != 0) + return; + const uint32_t insn = read32le(sec.rawData.data() + r.offset); + switch (r.type) { + case R_RISCV_TPREL_HI20: + case R_RISCV_TPREL_ADD: + // Remove lui rd, %tprel_hi(x) and add rd, rd, tp, %tprel_add(x). + sec.relaxAux->relocTypes[i] = R_RISCV_RELAX; + remove = 4; + break; + case R_RISCV_TPREL_LO12_I: + // addi rd, rd, %tprel_lo(x) => addi rd, tp, st_value(x) + sec.relaxAux->relocTypes[i] = R_RISCV_TPREL_I; + sec.relaxAux->writes.push_back((insn & ~(31 << 15)) | (X_TP << 15)); + break; + case R_RISCV_TPREL_LO12_S: + // sw rs, %tprel_lo(x)(rd) => sw rs, st_value(x)(rd) + sec.relaxAux->relocTypes[i] = R_RISCV_TPREL_S; + sec.relaxAux->writes.push_back((insn & ~(31 << 15)) | (X_TP << 15)); + break; + } +} + static bool relax(InputSection &sec) { const uint64_t secAddr = sec.getVA(); auto &aux = *sec.relaxAux; @@ -610,6 +639,14 @@ sec.relocations[i + 1].type == R_RISCV_RELAX) relaxCall(sec, i, loc, r, remove); break; + case R_RISCV_TPREL_HI20: + case R_RISCV_TPREL_ADD: + case R_RISCV_TPREL_LO12_I: + case R_RISCV_TPREL_LO12_S: + if (i + 1 != sec.relocations.size() && + sec.relocations[i + 1].type == R_RISCV_RELAX) + relaxTlsLe(sec, i, loc, r, remove); + break; } // For all anchors whose offsets are <= r.offset, they are preceded by @@ -697,7 +734,7 @@ for (size_t i = 0, e = rels.size(); i != e; ++i) { uint32_t remove = aux.relocDeltas[i] - delta; delta = aux.relocDeltas[i]; - if (remove == 0) + if (remove == 0 && aux.relocTypes[i] == R_RISCV_NONE) continue; // Copy from last location to the current relocated location. @@ -723,15 +760,19 @@ } } } else if (RelType newType = aux.relocTypes[i]) { - const uint32_t insn = aux.writes[writesIdx++]; switch (newType) { + case R_RISCV_RELAX: + // Used by relaxTlsLe to indicate the relocation is ignored. + break; case R_RISCV_RVC_JUMP: skip = 2; - write16le(p, insn); + write16le(p, aux.writes[writesIdx++]); break; case R_RISCV_JAL: + case R_RISCV_TPREL_I: + case R_RISCV_TPREL_S: skip = 4; - write32le(p, insn); + write32le(p, aux.writes[writesIdx++]); break; default: llvm_unreachable("unsupported type"); diff --git a/lld/test/ELF/riscv-tls-le.s b/lld/test/ELF/riscv-tls-le.s --- a/lld/test/ELF/riscv-tls-le.s +++ b/lld/test/ELF/riscv-tls-le.s @@ -1,17 +1,21 @@ # REQUIRES: riscv +## Additionally test that (a) -no-pie/-pie have the same behavior +## (b) --no-relax/--relax have the same behavior when R_RISCV_RELAX is suppressed. # RUN: llvm-mc -filetype=obj -triple=riscv32 %s -o %t.32.o -# RUN: ld.lld %t.32.o -o %t.32 +# RUN: ld.lld --relax %t.32.o -o %t.32 # RUN: llvm-nm -p %t.32 | FileCheck --check-prefixes=NM %s # RUN: llvm-objdump -d --no-show-raw-insn %t.32 | FileCheck --check-prefixes=LE %s -# RUN: ld.lld -pie %t.32.o -o %t.32 +# RUN: ld.lld -pie --no-relax %t.32.o -o %t.32 # RUN: llvm-objdump -d --no-show-raw-insn %t.32 | FileCheck --check-prefixes=LE %s -# RUN: llvm-mc -filetype=obj -triple=riscv64 %s -o %t.64.o -# RUN: ld.lld %t.64.o -o %t.64 +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax %s -o %t.64.o +# RUN: ld.lld --no-relax %t.64.o -o %t.64 # RUN: llvm-objdump -d --no-show-raw-insn %t.64 | FileCheck --check-prefixes=LE %s -# RUN: ld.lld -pie %t.64.o -o %t.64 +# RUN: ld.lld -pie --no-relax %t.64.o -o %t.64 # RUN: llvm-objdump -d --no-show-raw-insn %t.64 | FileCheck --check-prefixes=LE %s +# RUN: ld.lld %t.64.o -o %t.64.relax +# RUN: llvm-objdump -d --no-show-raw-insn %t.64.relax | FileCheck --check-prefixes=LE-RELAX %s # RUN: not ld.lld -shared %t.32.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR --implicit-check-not=error: @@ -19,23 +23,43 @@ # ERR: error: relocation R_RISCV_TPREL_LO12_I against .LANCHOR0 cannot be used with -shared # ERR: error: relocation R_RISCV_TPREL_HI20 against a cannot be used with -shared # ERR: error: relocation R_RISCV_TPREL_LO12_S against a cannot be used with -shared +# ERR: error: relocation R_RISCV_TPREL_HI20 against a cannot be used with -shared +# ERR: error: relocation R_RISCV_TPREL_LO12_S against a cannot be used with -shared # NM: {{0*}}00000008 b .LANCHOR0 -# NM: {{0*}}0000000c B a +# NM: {{0*}}00000800 B a ## .LANCHOR0@tprel = 8 ## a@tprel = 12 -# LE: lui a5, 0 +# LE: lui a3, 0 +# LE-NEXT: add a3, a3, tp +# LE-NEXT: addi a3, a3, 8 +# LE-NEXT: lui a4, 0 +# LE-NEXT: add a4, a4, tp +# LE-NEXT: sw a0, 2044(a4) +# LE-NEXT: lui a5, 1 # LE-NEXT: add a5, a5, tp -# LE-NEXT: addi a5, a5, 8 -# LE-NEXT: lui a5, 0 -# LE-NEXT: add a5, a5, tp -# LE-NEXT: sw a0, 12(a5) +# LE-NEXT: sw a0, -2048(a5) +# LE-EMPTY: + +# LE-RELAX: <.text>: +# LE-RELAX-NEXT: addi a3, tp, 8 +# LE-RELAX-NEXT: sw a0, 2044(tp) +# LE-RELAX-NEXT: lui a5, 1 +# LE-RELAX-NEXT: add a5, a5, tp +# LE-RELAX-NEXT: sw a0, -2048(a5) +# LE-RELAX-EMPTY: + +lui a3, %tprel_hi(.LANCHOR0) +add a3, a3, tp, %tprel_add(.LANCHOR0) +addi a3, a3, %tprel_lo(.LANCHOR0) -lui a5, %tprel_hi(.LANCHOR0) -add a5, a5, tp, %tprel_add(.LANCHOR0) -addi a5, a5, %tprel_lo(.LANCHOR0) +## hi20(a-4) = hi20(0x7fc) = 0. relaxable +lui a4, %tprel_hi(a-4) +add a4, a4, tp, %tprel_add(a-4) +sw a0, %tprel_lo(a-4)(a4) +## hi20(a) = hi20(0x800) = 1. not relaxable lui a5, %tprel_hi(a) add a5, a5, tp, %tprel_add(a) sw a0, %tprel_lo(a)(a5) @@ -43,6 +67,7 @@ .section .tbss .space 8 .LANCHOR0: -.zero 4 +.space 0x800-8 .globl a a: +.zero 4 diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def --- a/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def +++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def @@ -46,6 +46,8 @@ ELF_RELOC(R_RISCV_RVC_BRANCH, 44) ELF_RELOC(R_RISCV_RVC_JUMP, 45) ELF_RELOC(R_RISCV_RVC_LUI, 46) +ELF_RELOC(R_RISCV_TPREL_I, 49) +ELF_RELOC(R_RISCV_TPREL_S, 50) ELF_RELOC(R_RISCV_RELAX, 51) ELF_RELOC(R_RISCV_SUB6, 52) ELF_RELOC(R_RISCV_SET6, 53)