diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -207,6 +207,7 @@ bool timeTraceEnabled; bool tocOptimize; bool pcRelOptimize; + bool addMissingTLSReloc; bool undefinedVersion; bool unique; bool useAndroidRelrTags = false; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -320,6 +320,9 @@ if (config->pcRelOptimize && config->emachine != EM_PPC64) error("--pcrel--optimize is only supported on the PowerPC64 target"); + if (config->addMissingTLSReloc && config->emachine != EM_PPC64) + error("--add-missing-tls-reloc is only supported on the PowerPC64 target"); + if (config->pie && config->shared) error("-shared and -pie may not be used together"); @@ -1307,6 +1310,8 @@ args.hasFlag(OPT_toc_optimize, OPT_no_toc_optimize, m == EM_PPC64); config->pcRelOptimize = args.hasFlag(OPT_pcrel_optimize, OPT_no_pcrel_optimize, m == EM_PPC64); + config->addMissingTLSReloc = args.hasFlag( + OPT_add_missing_tls_reloc, OPT_no_add_missing_tls_reloc, false); } // Returns a value of "-format" option. diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -411,6 +411,10 @@ "(PowerPC64) Enable PC-relative optimizations (default)", "(PowerPC64) Disable PC-relative optimizations">; +defm add_missing_tls_reloc : BB<"add-missing-tls-reloc", + "(PowerPC64) Add TLSGD / TLSLD relocation if missing", + "(PowerPC64) Do not add TLSGD / TLSLD relocation if missing (default)">; + def trace: F<"trace">, HelpText<"Print the names of the input files">; defm trace_symbol: Eq<"trace-symbol", "Trace references to symbols">; diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -1293,14 +1293,62 @@ if (offset == uint64_t(-1)) return; + const uint8_t *relocatedAddr = sec.data().begin() + rel.r_offset; + RelExpr expr = target->getRelExpr(type, sym, relocatedAddr); + int64_t addend = computeAddend(rel, end, sec, expr, sym.isLocal()); + + // The function __tls_get_addr is special and should only be used for Thread + // Local Storage accesses. We need to check it before we check if it is + // undefined as it will be relaxed away in statically linked binaries on + // Power PC. + // For a call to __tls_get_addr one of two scenarios is possible: + // 1) The instruction is part of a TLS sequence and needs to be relocated by + // two relocations, R_PPC64_TLSGD/R_PPC64_TLSLD and R_PPC64_REL24[_NOTOC]. + // R_PPC64_TLSGD/R_PPC64_TLSLD should precede R_PPC64_REL24[_NOTOC]. + // 2) The call is a simple function call and is not part of a TLS sequence + // and is relocated only by R_PPC64_REL24[_NOTOC]. + if (config->emachine == EM_PPC64 && + (type == R_PPC64_REL24 || type == R_PPC64_REL24_NOTOC) && + sym.getName() == "__tls_get_addr" && i - start >= 2) { + // Obtain the previous relocation. + const RelTy &prevRel = *(i - 2); + const RelType prevType = prevRel.getType(config->isMips64EL); + // Check if this call to __tls_get_addr is part of a TLS sequence. + if (prevType == R_PPC64_GOT_TLSGD16_HA || + prevType == R_PPC64_GOT_TLSGD16_LO || + prevType == R_PPC64_GOT_TLSLD16_HA || + prevType == R_PPC64_GOT_TLSLD16_LO || + prevType == R_PPC64_GOT_TLSGD_PCREL34 || + prevType == R_PPC64_GOT_TLSLD_PCREL34) { + // Based on the type of the previous relocation it looks like this call + // to __tls_get_addr is meant to be part of a TLS sequence. If the + // option to try to fix this is set then add the missing relocation. + // Otherwise, report the issue. + if (config->addMissingTLSReloc) { + uint32_t symIndexBack = prevRel.getSymbol(config->isMips64EL); + Symbol &symBack = sec.getFile()->getSymbol(symIndexBack); + if (prevType == R_PPC64_GOT_TLSGD16_HA || + prevType == R_PPC64_GOT_TLSGD16_LO || + prevType == R_PPC64_GOT_TLSGD_PCREL34) + handleTlsRelocation(R_PPC64_TLSGD, symBack, sec, offset, addend, + R_TLSDESC_CALL); + else + handleTlsRelocation(R_PPC64_TLSLD, symBack, sec, offset, addend, + R_TLSLD_HINT); + return; + } else { + errorOrWarn("call to __tls_get_addr is missing a " + "R_PPC64_TLSGD/R_PPC64_TLSLD relocation" + + getLocation(sec, sym, offset)); + } + } + } + // Error if the target symbol is undefined. Symbol index 0 may be used by // marker relocations, e.g. R_*_NONE and R_ARM_V4BX. Don't error on them. if (symIndex != 0 && maybeReportUndefined(sym, sec, rel.r_offset)) return; - const uint8_t *relocatedAddr = sec.data().begin() + rel.r_offset; - RelExpr expr = target->getRelExpr(type, sym, relocatedAddr); - // Ignore R_*_NONE and other marker relocations. if (expr == R_NONE) return; @@ -1315,9 +1363,6 @@ getLocation(sec, sym, offset)); } - // Read an addend. - int64_t addend = computeAddend(rel, end, sec, expr, sym.isLocal()); - if (config->emachine == EM_PPC64) { // We can separate the small code model relocations into 2 categories: // 1) Those that access the compiler generated .toc sections. diff --git a/lld/test/ELF/ppc64-call-tls-get-addr-exec.s b/lld/test/ELF/ppc64-call-tls-get-addr-exec.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/ppc64-call-tls-get-addr-exec.s @@ -0,0 +1,22 @@ +# REQUIRES: ppc +# RUN: llvm-mc --triple=powerpc64le %s --filetype=obj -o %t1.o +# RUN: llvm-mc --triple=powerpc64 %s --filetype=obj -o %t2.o +# RUN: ld.lld %t1.o -o %t1 +# RUN: ld.lld %t2.o -o %t2 +# RUN: llvm-objdump -dr --no-show-raw-insn %t1 | FileCheck %s +# RUN: llvm-objdump -dr --no-show-raw-insn %t2 | FileCheck %s + +## Since this is not a shared object we need to include a definition. +__tls_get_addr: + blr + +## Without all of the TLS relocations that come before the __tls_get_addr call +## this call should be treated as we treat all other calls. +# CHECK-LABEL: : +# CHECK: bl +# CHECK-NEXT: nop +# CHECK-NEXT: blr +CallOnly: + bl __tls_get_addr + nop + blr diff --git a/lld/test/ELF/ppc64-call-tls-get-addr-missing.s b/lld/test/ELF/ppc64-call-tls-get-addr-missing.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/ppc64-call-tls-get-addr-missing.s @@ -0,0 +1,11 @@ +# REQUIRES: ppc +# RUN: llvm-mc --triple=powerpc64le %s --filetype=obj -o %t1.o +# RUN: not ld.lld %t1.o -o %t1 2>&1 | FileCheck %s + +## Since this is being compiled without --shared we should see the linker +## complain about the callee being missing. +# CHECK: error: undefined symbol: __tls_get_addr +CallOnly: + bl __tls_get_addr + nop + blr diff --git a/lld/test/ELF/ppc64-call-tls-get-addr.s b/lld/test/ELF/ppc64-call-tls-get-addr.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/ppc64-call-tls-get-addr.s @@ -0,0 +1,19 @@ +# REQUIRES: ppc +# RUN: llvm-mc --triple=powerpc64le %s --filetype=obj -o %t1.o +# RUN: llvm-mc --triple=powerpc64 %s --filetype=obj -o %t2.o +# RUN: ld.lld --shared %t1.o -o %t1.so +# RUN: ld.lld --shared %t2.o -o %t2.so +# RUN: llvm-objdump -dr --no-show-raw-insn %t1.so | FileCheck %s +# RUN: llvm-objdump -dr --no-show-raw-insn %t2.so | FileCheck %s + +## Without all of the TLS relocations that come before the __tls_get_addr call +## this call should be treated as we treat all other calls. +## Also note that the nop is replaced with a TOC restore here. +# CHECK-LABEL: : +# CHECK: bl +# CHECK-NEXT: ld 2, 24(1) +# CHECK-NEXT: blr +CallOnly: + bl __tls_get_addr + nop + blr diff --git a/lld/test/ELF/ppc64-tls-add-missing-gdld.s b/lld/test/ELF/ppc64-tls-add-missing-gdld.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/ppc64-tls-add-missing-gdld.s @@ -0,0 +1,59 @@ +# REQUIRES: ppc +# RUN: split-file %s %t + +# RUN: llvm-mc --triple=powerpc64le %t/data --filetype=obj -o %t_data.o +# RUN: llvm-mc --triple=powerpc64le %t/text --filetype=obj -o %t_text.o +# RUN: ld.lld --add-missing-tls-reloc %t_text.o %t_data.o -o %t_exec +# RUN: llvm-objdump -dr %t_exec --no-show-raw-insn --mcpu=pwr10 | FileCheck %s + +#--- data +.section .tbss,"awT",@nobits +.globl x +x: + .long 0 +.globl y +y: + .long 0 + +#--- text +.section .text, "ax", %progbits +# CHECK-LABEL: : +# CHECK: nop +# CHECK-NEXT: addis 3, 13, 0 +# CHECK-NEXT: nop +# CHECK-NEXT: addi 3, 3, -28672 +# CHECK-NEXT: nop +# CHECK-NEXT: addis 3, 13, 0 +# CHECK-NEXT: nop +# CHECK-NEXT: addi 3, 3, -28668 +# CHECK-NEXT: blr +GeneralDynamic: + addis 3, 2, x@got@tlsgd@ha + addi 3, 3, x@got@tlsgd@l + bl __tls_get_addr + nop + addis 3, 2, y@got@tlsgd@ha + addi 3, 3, y@got@tlsgd@l + bl __tls_get_addr + nop + blr + +# CHECK-LABEL: : +# CHECK: addis 3, 13, 0 +# CHECK-NEXT: nop +# CHECK-NEXT: addi 3, 3, 4096 +# CHECK-NEXT: nop +# CHECK-NEXT: addis 3, 13, 0 +# CHECK-NEXT: nop +# CHECK-NEXT: addi 3, 3, 4096 +# CHECK-NEXT: blr +LocalDynamic: + addis 3, 2, x@got@tlsld@ha + addi 3, 3, x@got@tlsld@l + bl __tls_get_addr + nop + addis 3, 2, y@got@tlsld@ha + addi 3, 3, y@got@tlsld@l + bl __tls_get_addr + nop + blr diff --git a/lld/test/ELF/ppc64-tls-missing-gdld.s b/lld/test/ELF/ppc64-tls-missing-gdld.s --- a/lld/test/ELF/ppc64-tls-missing-gdld.s +++ b/lld/test/ELF/ppc64-tls-missing-gdld.s @@ -1,12 +1,24 @@ # REQUIRES: ppc # RUN: llvm-mc --triple=powerpc64le %s --filetype=obj -o %t1.o # RUN: llvm-mc --triple=powerpc64 %s --filetype=obj -o %t2.o -# RUN: ld.lld --shared --fatal-warnings %t1.o -o /dev/null -# RUN: ld.lld --shared --fatal-warnings %t2.o -o /dev/null +# RUN: not ld.lld --shared %t1.o -o /dev/null 2>&1 | FileCheck %s +# RUN: not ld.lld --shared %t2.o -o /dev/null 2>&1 | FileCheck %s -## User code can call __tls_get_addr by specifying the tls_index parameter. -## We need to allow R_PPC64_REL24/R_PPC64_REL24_NOTOC referencing __tls_get_addr -## without a pairing R_PPC64_TLSGD/R_PPC64_TLSLD. +# CHECK: ld.lld: error: call to __tls_get_addr is missing a R_PPC64_TLSGD/R_PPC64_TLSLD relocation +# CHECK-NEXT: defined in {{.*}}.o +# CHECK-NEXT: referenced by {{.*}}.o:(.text+0x8) + +# CHECK: ld.lld: error: call to __tls_get_addr is missing a R_PPC64_TLSGD/R_PPC64_TLSLD relocation +# CHECK-NEXT: defined in {{.*}}.o +# CHECK-NEXT: referenced by {{.*}}.o:(.text+0x18) + +# CHECK: ld.lld: error: call to __tls_get_addr is missing a R_PPC64_TLSGD/R_PPC64_TLSLD relocation +# CHECK-NEXT: defined in {{.*}}.o +# CHECK-NEXT: referenced by {{.*}}.o:(.text+0x28) + +# CHECK: ld.lld: error: call to __tls_get_addr is missing a R_PPC64_TLSGD/R_PPC64_TLSLD relocation +# CHECK-NEXT: defined in {{.*}}.o +# CHECK-NEXT: referenced by {{.*}}.o:(.text+0x38) GeneralDynamic: addis 3, 2, x@got@tlsgd@ha @@ -31,7 +43,3 @@ addi 3, 3, x@got@tlsld@l bl __tls_get_addr@notoc blr - -CallOnly: - bl __tls_get_addr - blr