Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ lld/ELF/Config.h @@ -70,6 +70,9 @@ // For -z *stack enum class GnuStackKind { None, Exec, NoExec }; +// For tlsVariant and --force-tls-variant2 +enum class TlsVariant { None, One, OneArm, OneAdjusted, Two }; + struct SymbolVersion { llvm::StringRef name; bool isExternCpp; @@ -253,6 +256,7 @@ SeparateSegmentKind zSeparate; ELFKind ekind = ELFNoneKind; uint16_t emachine = llvm::ELF::EM_NONE; + TlsVariant tlsVariant = TlsVariant::None; llvm::Optional imageBase; uint64_t commonPageSize; uint64_t maxPageSize; Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -1274,6 +1274,31 @@ config->picThunk = args.hasArg(OPT_pic_veneer, config->isPic); config->wordsize = config->is64 ? 8 : 4; + if (args.hasArg(OPT_force_tls_variant2)) { + config->tlsVariant = TlsVariant::Two; + } else { + switch (m) { + case EM_ARM: + case EM_AARCH64: + config->tlsVariant = TlsVariant::OneArm; + break; + case EM_MIPS: + case EM_PPC: + case EM_PPC64: + config->tlsVariant = TlsVariant::OneAdjusted; + break; + case EM_RISCV: + config->tlsVariant = TlsVariant::One; + break; + case EM_HEXAGON: + case EM_SPARCV9: + case EM_386: + case EM_X86_64: + config->tlsVariant = TlsVariant::Two; + break; + } + } + // ELF defines two different ways to store relocation addends as shown below: // // Rel: Addends are stored to the location where relocations are applied. It Index: lld/ELF/InputSection.cpp =================================================================== --- lld/ELF/InputSection.cpp +++ lld/ELF/InputSection.cpp @@ -641,31 +641,22 @@ // before TP. The alignment padding is added so that (TP - padding - // p_memsz) is congruent to p_vaddr modulo p_align. PhdrEntry *tls = Out::tlsPhdr; - switch (config->emachine) { - // Variant 1. - case EM_ARM: - case EM_AARCH64: + switch (config->tlsVariant) { + case TlsVariant::One: + return s.getVA(0) + (tls->p_vaddr & (tls->p_align - 1)); + case TlsVariant::OneArm: return s.getVA(0) + config->wordsize * 2 + ((tls->p_vaddr - config->wordsize * 2) & (tls->p_align - 1)); - case EM_MIPS: - case EM_PPC: - case EM_PPC64: + case TlsVariant::OneAdjusted: // Adjusted Variant 1. TP is placed with a displacement of 0x7000, which is // to allow a signed 16-bit offset to reach 0x1000 of TCB/thread-library // data and 0xf000 of the program's TLS segment. return s.getVA(0) + (tls->p_vaddr & (tls->p_align - 1)) - 0x7000; - case EM_RISCV: - return s.getVA(0) + (tls->p_vaddr & (tls->p_align - 1)); - - // Variant 2. - case EM_HEXAGON: - case EM_SPARCV9: - case EM_386: - case EM_X86_64: + case TlsVariant::Two: return s.getVA(0) - tls->p_memsz - ((-tls->p_vaddr - tls->p_memsz) & (tls->p_align - 1)); default: - llvm_unreachable("unhandled Config->EMachine"); + llvm_unreachable("unhandled config->tlsVariant"); } } Index: lld/ELF/Options.td =================================================================== --- lld/ELF/Options.td +++ lld/ELF/Options.td @@ -210,6 +210,9 @@ def fix_cortex_a8: F<"fix-cortex-a8">, HelpText<"Apply fixes for ARM Cortex-A8 erratum 657417">; +def force_tls_variant2: FF<"force-tls-variant2">, + HelpText<"Perform TLS offset calculations using variant 2">; + defm format: Eq<"format", "Change the input format of the inputs following this option">, MetaVarName<"[default,elf,binary]">; Index: lld/test/ELF/force-tls-variant2.s =================================================================== --- /dev/null +++ lld/test/ELF/force-tls-variant2.s @@ -0,0 +1,56 @@ +# REQUIRES: arm +# RUN: llvm-mc %s -o %t.o -filetype=obj -triple=armv7a +# RUN: ld.lld %t.o -o %t +# RUN: llvm-objdump -d --triple=armv7a %t | FileCheck --check-prefixes=CHECK,VAR1 %s +# RUN: ld.lld --force-tls-variant2 %t.o -o %t2 +# RUN: llvm-objdump -d --triple=armv7a %t2 | FileCheck --check-prefixes=CHECK,VAR2 %s + +## Test forcing of TLS variant 2 for a target that defaults to a form of TLS +## variant 1 using the --force-tls-variant2 option. + + .syntax unified + .globl _start,x,y,z + + .text + .p2align 2 + .type _start,%function +_start: + .p2align 2 +## Generate R_ARM_TLS_LE32 relocations. These resolve statically to the offset +## of the variable from the thread pointer. + .word x(TPOFF) + .word y(TPOFF) + .word z(TPOFF) + + .section .tdata,"awT",%progbits + .p2align 2 + .type x,%object +x: + .word 10 + + .section .tbss,"awT",%nobits + .p2align 2 + .type z,%object +z: + .space 4 + .type y,%object +y: + .space 4 + +# CHECK: Disassembly of section .text: +# CHECK-EMPTY: +# CHECK-NEXT: <_start>: + +## Offset of x from thread pointer = (TcbSize + 0x0 = 0x8) +# VAR1-NEXT: 20114: 08 00 00 00 +## Offset of y from thread pointer = (TcbSize + 0x8 = 0x10) +# VAR1-NEXT: 20118: 10 00 00 00 +## Offset of z from thread pointer = (TcbSize + 0x4 = 0xc) +# VAR1-NEXT: 2011c: 0c 00 00 00 + +## Offset of x from thread pointer = -12 +# VAR2-NEXT: 20114: f4 ff ff ff +## Offset of y from thread pointer = -4 +# VAR2-NEXT: 20118: fc ff ff ff +## Offset of z from thread pointer = -8 +# VAR2-NEXT: 2011c: f8 ff ff ff