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 fixPPC64TLSReloc; 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->fixPPC64TLSReloc && config->emachine != EM_PPC64) + error("--fix-ppc64-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->fixPPC64TLSReloc = + args.hasFlag(OPT_fix_ppc64_tls_reloc, OPT_no_fix_ppc64_tls_reloc, false); } // Returns a value of "-format" option. diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -131,6 +131,10 @@ // [.got, .got + 0xFFFC]. bool ppc64SmallCodeModelTocRelocs = false; + // On PPC64 we may need to turn off TLS relaxation to avoid bad code + // generation when calls to __tls_get_adds are missing a relocation. + bool ppc64DisableTLSRelax = false; + // groupId is used for --warn-backrefs which is an optional error // checking feature. All files within the same --{start,end}-group or // --{start,end}-lib get the same group ID. Otherwise, each file gets a new diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -1052,22 +1052,26 @@ target->relocate(bufLoc, rel, targetVA); break; case R_RELAX_TLS_IE_TO_LE: - target->relaxTlsIeToLe(bufLoc, rel, targetVA); + if (!file->ppc64DisableTLSRelax) + target->relaxTlsIeToLe(bufLoc, rel, targetVA); break; case R_RELAX_TLS_LD_TO_LE: case R_RELAX_TLS_LD_TO_LE_ABS: - target->relaxTlsLdToLe(bufLoc, rel, targetVA); + if (!file->ppc64DisableTLSRelax) + target->relaxTlsLdToLe(bufLoc, rel, targetVA); break; case R_RELAX_TLS_GD_TO_LE: case R_RELAX_TLS_GD_TO_LE_NEG: - target->relaxTlsGdToLe(bufLoc, rel, targetVA); + if (!file->ppc64DisableTLSRelax) + target->relaxTlsGdToLe(bufLoc, rel, targetVA); break; case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC: case R_RELAX_TLS_GD_TO_IE: case R_RELAX_TLS_GD_TO_IE_ABS: case R_RELAX_TLS_GD_TO_IE_GOT_OFF: case R_RELAX_TLS_GD_TO_IE_GOTPLT: - target->relaxTlsGdToIe(bufLoc, rel, targetVA); + if (!file->ppc64DisableTLSRelax) + target->relaxTlsGdToIe(bufLoc, rel, targetVA); break; case R_PPC64_CALL: // If this is a call to __tls_get_addr, it may be part of a TLS 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 fix_ppc64_tls_reloc : BB<"fix-ppc64-tls-reloc", + "(PowerPC64) Check for missing TLSGD / TLSLD relocation and disable TLS relaxation if missing", + "(PowerPC64) Do not check for missing TLSGD / TLSLD relocation (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 @@ -1319,6 +1319,27 @@ int64_t addend = computeAddend(rel, end, sec, expr, sym.isLocal()); if (config->emachine == EM_PPC64) { + // If the option is set we should check all calls to __tls_get_addr for + // missing relocations. + if (config->fixPPC64TLSReloc) { + if ((type == R_PPC64_REL24 || type == R_PPC64_REL24_NOTOC) && + sym.getName() == "__tls_get_addr") { + + bool disableTLSRelax = (i - start < 2); + if (!disableTLSRelax) { + const RelTy &prevRel = *(i - 2); + const RelType prevType = prevRel.getType(config->isMips64EL); + disableTLSRelax = + (prevType != R_PPC64_TLSGD && prevType != R_PPC64_TLSLD); + } + if (disableTLSRelax) { + warn("TLS relaxations are disabled because a call to __tls_get_addr " + "is missing the R_PPC64_TLSGD/R_PPC64_TLSLD relocation." + + getLocation(sec, sym, offset)); + sec.file->ppc64DisableTLSRelax = true; + } + } + } // We can separate the small code model relocations into 2 categories: // 1) Those that access the compiler generated .toc sections. // 2) Those that access the linker allocated got entries. 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.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-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,37 +1,68 @@ # 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 - -## 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. +# RUN: ld.lld --shared %t1.o --soname=t1 -o %t1.so +# RUN: ld.lld --shared %t2.o --soname=t2 -o %t2.so +# RUN: ld.lld --shared %t1.o --fix-ppc64-tls-reloc --soname=t1-fix -o %t1-fix.so +# RUN: ld.lld --shared %t2.o --fix-ppc64-tls-reloc --soname=t2-fix -o %t2-fix.so +# RUN: llvm-objdump --no-show-raw-insn --mcpu=pwr10 -dr %t1.so | FileCheck %s +# RUN: llvm-objdump --no-show-raw-insn --mcpu=pwr10 -dr %t2.so | FileCheck %s +# RUN: llvm-objdump --no-show-raw-insn --mcpu=pwr10 -dr %t1-fix.so | FileCheck %s +# RUN: llvm-objdump --no-show-raw-insn --mcpu=pwr10 -dr %t2-fix.so | FileCheck %s +#CHECK-LABEL: : +#CHECK: addis 3, 2, 0 +#CHECK-NEXT: addi 3, 3, -32768 +#CHECK-NEXT: bl +#CHECK-NEXT: ld 2, 24(1) +#CHECK-NEXT: blr GeneralDynamic: addis 3, 2, x@got@tlsgd@ha addi 3, 3, x@got@tlsgd@l bl __tls_get_addr + nop blr +#CHECK-LABEL: : +#CHECK: addis 3, 2, 0 +#CHECK-NEXT: addi 3, 3, -32768 +#CHECK-NEXT: bl +#CHECK-NEXT: blr GeneralDynamic_NOTOC: addis 3, 2, x@got@tlsgd@ha addi 3, 3, x@got@tlsgd@l bl __tls_get_addr@notoc blr +#CHECK-LABEL: : +#CHECK: addis 3, 2, 0 +#CHECK-NEXT: addi 3, 3, -32752 +#CHECK-NEXT: bl +#CHECK-NEXT: ld 2, 24(1) +#CHECK-NEXT: blr LocalDynamic: addis 3, 2, x@got@tlsld@ha addi 3, 3, x@got@tlsld@l bl __tls_get_addr + nop blr +#CHECK-LABEL: : +#CHECK: addis 3, 2, 0 +#CHECK-NEXT: addi 3, 3, -32752 +#CHECK-NEXT: bl +#CHECK-NEXT: blr LocalDynamic_NOTOC: addis 3, 2, x@got@tlsld@ha addi 3, 3, x@got@tlsld@l bl __tls_get_addr@notoc blr +#CHECK-LABEL: : +#CHECK: bl +#CHECK-NEXT: ld 2, 24(1) +#CHECK-NEXT: blr CallOnly: bl __tls_get_addr + nop blr