From 3652c191443fc977c7e109d7d23f90e3d18704db Mon Sep 17 00:00:00 2001 From: Job Noorman Date: Fri, 22 Sep 2023 12:15:36 +0200 Subject: [PATCH] [BOLT][RISCV] Implement TLS le/ie relocations Handle the following relocations related to TLS local-exec and initial-exec: - R_RISCV_TLS_GOT_HI20 - R_RISCV_TPREL_HI20 - R_RISCV_TPREL_ADD - R_RISCV_TPREL_LO12_I - R_RISCV_TPREL_LO12_S In addition, GNU ld has a quirk where after TLS le relaxation, two unofficial relocation types may be emitted: - R_RISCV_TPREL_I - R_RISCV_TPREL_S Since they are unofficial (defined in the reserved range of relocation types), LLVM does not define them. Hence, I've defined them locally in BOLT in a private namespace. --- bolt/lib/Core/Relocation.cpp | 26 ++++++ bolt/lib/Rewrite/RewriteInstance.cpp | 3 +- bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp | 2 + bolt/test/RISCV/Inputs/tls-le-gnu-ld.yaml | 92 ++++++++++++++++++++ bolt/test/RISCV/reloc-tls.s | 44 ++++++++++ bolt/test/RISCV/tls-le-gnu-ld.test | 11 +++ 6 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 bolt/test/RISCV/Inputs/tls-le-gnu-ld.yaml create mode 100644 bolt/test/RISCV/reloc-tls.s create mode 100644 bolt/test/RISCV/tls-le-gnu-ld.test diff --git a/bolt/lib/Core/Relocation.cpp b/bolt/lib/Core/Relocation.cpp index 6a4e7089bf248..f89b7ffa5876c 100644 --- a/bolt/lib/Core/Relocation.cpp +++ b/bolt/lib/Core/Relocation.cpp @@ -20,6 +20,13 @@ using namespace llvm; using namespace bolt; +namespace ELFReserved { +enum { + R_RISCV_TPREL_I = 49, + R_RISCV_TPREL_S = 50, +}; +} // namespace ELFReserved + Triple::ArchType Relocation::Arch; static bool isSupportedX86(uint64_t Type) { @@ -111,6 +118,13 @@ static bool isSupportedRISCV(uint64_t Type) { case ELF::R_RISCV_LO12_I: case ELF::R_RISCV_LO12_S: case ELF::R_RISCV_64: + case ELF::R_RISCV_TLS_GOT_HI20: + case ELF::R_RISCV_TPREL_HI20: + case ELF::R_RISCV_TPREL_ADD: + case ELF::R_RISCV_TPREL_LO12_I: + case ELF::R_RISCV_TPREL_LO12_S: + case ELFReserved::R_RISCV_TPREL_I: + case ELFReserved::R_RISCV_TPREL_S: return true; } } @@ -214,6 +228,7 @@ static size_t getSizeForTypeRISCV(uint64_t Type) { return 4; case ELF::R_RISCV_64: case ELF::R_RISCV_GOT_HI20: + case ELF::R_RISCV_TLS_GOT_HI20: // See extractValueRISCV for why this is necessary. return 8; } @@ -532,6 +547,7 @@ static uint64_t extractValueRISCV(uint64_t Type, uint64_t Contents, case ELF::R_RISCV_BRANCH: return extractBImmRISCV(Contents); case ELF::R_RISCV_GOT_HI20: + case ELF::R_RISCV_TLS_GOT_HI20: // We need to know the exact address of the GOT entry so we extract the // value from both the AUIPC and L[D|W]. We cannot rely on the symbol in the // relocation for this since it simply refers to the object that is stored @@ -600,6 +616,7 @@ static bool isGOTRISCV(uint64_t Type) { default: return false; case ELF::R_RISCV_GOT_HI20: + case ELF::R_RISCV_TLS_GOT_HI20: return true; } } @@ -636,6 +653,14 @@ static bool isTLSRISCV(uint64_t Type) { switch (Type) { default: return false; + case ELF::R_RISCV_TLS_GOT_HI20: + case ELF::R_RISCV_TPREL_HI20: + case ELF::R_RISCV_TPREL_ADD: + case ELF::R_RISCV_TPREL_LO12_I: + case ELF::R_RISCV_TPREL_LO12_S: + case ELFReserved::R_RISCV_TPREL_I: + case ELFReserved::R_RISCV_TPREL_S: + return true; } } @@ -733,6 +758,7 @@ static bool isPCRelativeRISCV(uint64_t Type) { case ELF::R_RISCV_RVC_JUMP: case ELF::R_RISCV_RVC_BRANCH: case ELF::R_RISCV_32_PCREL: + case ELF::R_RISCV_TLS_GOT_HI20: return true; } } diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp index 5cbbd1a5a8aca..414fd6df475a5 100644 --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -2332,7 +2332,8 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection, if (BC->isX86()) return; - // The non-got related TLS relocations on AArch64 also could be skipped. + // The non-got related TLS relocations on AArch64 and RISC-V also could be + // skipped. if (!Relocation::isGOT(RType)) return; } diff --git a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp index c2e64039a2500..087f5b4e16ded 100644 --- a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp +++ b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp @@ -46,6 +46,7 @@ class RISCVMCPlusBuilder : public MCPlusBuilder { case ELF::R_RISCV_HI20: case ELF::R_RISCV_LO12_I: case ELF::R_RISCV_LO12_S: + case ELF::R_RISCV_TLS_GOT_HI20: return true; default: llvm_unreachable("Unexpected RISCV relocation type in code"); @@ -396,6 +397,7 @@ class RISCVMCPlusBuilder : public MCPlusBuilder { default: return Expr; case ELF::R_RISCV_GOT_HI20: + case ELF::R_RISCV_TLS_GOT_HI20: // The GOT is reused so no need to create GOT relocations case ELF::R_RISCV_PCREL_HI20: return RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_PCREL_HI, Ctx); diff --git a/bolt/test/RISCV/Inputs/tls-le-gnu-ld.yaml b/bolt/test/RISCV/Inputs/tls-le-gnu-ld.yaml new file mode 100644 index 0000000000000..feec407ffdfda --- /dev/null +++ b/bolt/test/RISCV/Inputs/tls-le-gnu-ld.yaml @@ -0,0 +1,92 @@ +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_RISCV + Entry: 0x100B0 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + FirstSec: .text + LastSec: .text + VAddr: 0x10000 + Align: 0x1000 + Offset: 0x0 + - Type: PT_TLS + Flags: [ PF_R ] + FirstSec: .tbss + LastSec: .tbss + VAddr: 0x110C0 + Align: 0x8 + Offset: 0xc0 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x100B0 + AddressAlign: 0x4 + Content: '13000000832202002320520067800000' + - Name: .tbss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + Address: 0x110C0 + AddressAlign: 0x8 + Size: 0x8 + - Name: .rela.text + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Link: .symtab + AddressAlign: 0x8 + Info: .text + Relocations: + - Offset: 0x100B4 + Type: R_RISCV_NONE + - Offset: 0x100B4 + Type: R_RISCV_RELAX + - Offset: 0x100B4 + Type: R_RISCV_NONE + - Offset: 0x100B4 + Type: R_RISCV_RELAX + - Offset: 0x100B4 + Symbol: i + Type: 0x31 + - Offset: 0x100B4 + Type: R_RISCV_RELAX + - Offset: 0x100B8 + Symbol: i + Type: 0x32 + - Offset: 0x100B8 + Type: R_RISCV_RELAX + - Type: SectionHeaderTable + Sections: + - Name: .text + - Name: .rela.text + - Name: .tbss + - Name: .symtab + - Name: .strtab + - Name: .shstrtab +Symbols: + - Name: .text + Type: STT_SECTION + Section: .text + Value: 0x100B0 + - Name: .tbss + Type: STT_SECTION + Section: .tbss + Value: 0x110C0 + - Name: '__global_pointer$' + Index: SHN_ABS + Binding: STB_GLOBAL + Value: 0x118C0 + - Name: i + Type: STT_TLS + Section: .tbss + Binding: STB_GLOBAL + Size: 0x8 + - Name: _start + Section: .text + Binding: STB_GLOBAL + Value: 0x100B0 + Size: 0x10 +... diff --git a/bolt/test/RISCV/reloc-tls.s b/bolt/test/RISCV/reloc-tls.s new file mode 100644 index 0000000000000..6ced25b8e630a --- /dev/null +++ b/bolt/test/RISCV/reloc-tls.s @@ -0,0 +1,44 @@ +// RUN: llvm-mc -triple riscv64 -filetype obj -o %t.o %s +// RUN: ld.lld --emit-relocs -o %t %t.o +// RUN: llvm-bolt --print-cfg --print-only=tls_le,tls_ie -o /dev/null %t \ +// RUN: | FileCheck %s + +// CHECK-LABEL: Binary Function "tls_le{{.*}}" after building cfg { +// CHECK: lui a5, 0 +// CHECK-NEXT: add a5, a5, tp +// CHECK-NEXT: lw t0, 0(a5) +// CHECK-NEXT: sw t0, 0(a5) + +// CHECK-LABEL: Binary Function "tls_ie" after building cfg { +// CHECK-LABEL: .Ltmp0 +// CHECK: auipc a0, %pcrel_hi(__BOLT_got_zero+{{[0-9]+}}) +// CHECK-NEXT: ld a0, %pcrel_lo(.Ltmp0)(a0) + .text + .globl tls_le, _start + .p2align 2 +tls_le: +_start: + nop + lui a5, %tprel_hi(i) + add a5, a5, tp, %tprel_add(i) + lw t0, %tprel_lo(i)(a5) + sw t0, %tprel_lo(i)(a5) + ret + .size _start, .-_start + + .globl tls_ie + .p2align 2 +tls_ie: + nop + la.tls.ie a0, i + ret + .size tls_ie, .-tls_ie + + .section .tbss,"awT",@nobits + .type i,@object + .globl i + .p2align 3 +i: + .quad 0 + .size i, .-i + diff --git a/bolt/test/RISCV/tls-le-gnu-ld.test b/bolt/test/RISCV/tls-le-gnu-ld.test new file mode 100644 index 0000000000000..c3ff08b30ee60 --- /dev/null +++ b/bolt/test/RISCV/tls-le-gnu-ld.test @@ -0,0 +1,11 @@ +// This test checks that the binaries produces with GNU ld TLS le relaxation are +// properly processed by BOLT. GNU ld currently emits two non-standard +// relocations (R_RISCV_TPREL_I and R_RISCV_TPREL_S) in this case. + +// RUN: yaml2obj %p/Inputs/tls-le-gnu-ld.yaml &> %t.exe +// RUN: llvm-bolt %t.exe -o %t.bolt.exe --print-cfg --print-only=_start \ +// RUN: | FileCheck %s + +// CHECK: Binary Function "_start" after building cfg { +// CHECK: lw t0, 0(tp) +// CHECK-NEXT: sw t0, 0(tp)