diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -208,6 +208,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 @@ -318,9 +318,10 @@ if (config->tocOptimize && config->emachine != EM_PPC64) error("--toc-optimize is only supported on PowerPC64 targets"); - if (config->pcRelOptimize && config->emachine != EM_PPC64) error("--pcrel-optimize is only supported on PowerPC64 targets"); + if (config->fixPPC64TLSReloc && config->emachine != EM_PPC64) + error("--fix-ppc64-tls-reloc is only supported on PowerPC64 targets"); if (config->pie && config->shared) error("-shared and -pie may not be used together"); @@ -1340,6 +1341,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 @@ -130,6 +130,10 @@ // [.got, .got + 0xFFFC]. bool ppc64SmallCodeModelTocRelocs = false; + // True if the file has TLSGD/TLSLD GOT relocations without R_PPC64_TLSGD or + // R_PPC64_TLSLD. Disable TLS relaxation to avoid bad code generation. + 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/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -414,6 +414,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 @@ -208,9 +208,13 @@ return 1; } + // ARM, Hexagon and RISC-V do not support GD/LD to IE/LE relaxation. For + // PPC64, if the file has missing R_PPC64_TLSGD/R_PPC64_TLSLD, disable + // relaxation as well. bool toExecRelax = !config->shared && config->emachine != EM_ARM && config->emachine != EM_HEXAGON && - config->emachine != EM_RISCV; + config->emachine != EM_RISCV && + !c.file->ppc64DisableTLSRelax; // If we are producing an executable and the symbol is non-preemptable, it // must be defined and the code sequence can be relaxed to use Local-Exec. @@ -1534,6 +1538,42 @@ // Not all relocations end up in Sec.Relocations, but a lot do. sec.relocations.reserve(rels.size()); + if (config->fixPPC64TLSReloc) { + bool hasMarker = false, hasGDLD = false; + for (const RelTy &rel : rels) { + uint32_t symIndex = rel.getSymbol(false); + Symbol &sym = sec.getFile()->getSymbol(symIndex); + RelType type = rel.getType(false); + switch (type) { + case R_PPC64_TLSGD: + case R_PPC64_TLSLD: + hasMarker = true; + break; + case R_PPC64_GOT_TLSGD16: + case R_PPC64_GOT_TLSGD16_HA: + case R_PPC64_GOT_TLSGD16_HI: + case R_PPC64_GOT_TLSGD16_LO: + case R_PPC64_GOT_TLSLD16: + case R_PPC64_GOT_TLSLD16_HA: + case R_PPC64_GOT_TLSLD16_HI: + case R_PPC64_GOT_TLSLD16_LO: + hasGDLD = true; + break; + } + const uint8_t *loc = sec.data().begin() + rel.r_offset; + RelExpr expr = target->getRelExpr(type, sym, loc); + if (expr == R_TLSGD_GOT && expr != R_TLSLD_GOT) + continue; + } + // If the option is set we should check all calls to __tls_get_addr for + // missing relocations. + if (hasGDLD && !hasMarker) { + sec.file->ppc64DisableTLSRelax = true; + warn(toString(&sec) + ": disable TLS relaxation due to missing " + "R_PPC64_TLSGD/R_PPC64_TLSLD"); + } + } + for (auto i = rels.begin(), end = rels.end(); i != end;) scanReloc(sec, getOffset, i, rels.begin(), end); diff --git a/lld/test/ELF/ppc64-local-dynamic.s b/lld/test/ELF/ppc64-local-dynamic.s --- a/lld/test/ELF/ppc64-local-dynamic.s +++ b/lld/test/ELF/ppc64-local-dynamic.s @@ -7,6 +7,9 @@ // RUN: llvm-objdump --section-headers %t.so | FileCheck --check-prefix=CheckGot %s // RUN: llvm-objdump -d %t.so | FileCheck --check-prefix=Dis %s +/// R_PPC64_TLSLD exists. Test --fix-ppc64-tls-reloc does not warn. +// RUN: ld.lld -shared --fix-ppc64-tls-reloc --fatal-warnings -e test %t.o -z separate-code -o %tf.so && cmp %t.so %tf.so + // RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-linux %s -o %t.o // RUN: ld.lld -shared %t.o -z separate-code -o %t.so // RUN: llvm-readelf -r %t.o | FileCheck --check-prefix=InputRelocs %s diff --git a/lld/test/ELF/ppc64-options.s b/lld/test/ELF/ppc64-options.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/ppc64-options.s @@ -0,0 +1,18 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: not ld.lld %t --toc-optimize -o /dev/null 2>&1 | FileCheck %s + +# CHECK: error: --toc-optimize is only supported on PowerPC64 + +# RUN: not ld.lld %t --toc-optimize -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK2 + +# CHECK2: error: --fix-ppc64-tls-reloc is only supported on PowerPC64 + + .global __start + .type __start,@function + + .text + .quad 0 + __start: + diff --git a/lld/test/ELF/ppc64-tls-gd.s b/lld/test/ELF/ppc64-tls-gd.s --- a/lld/test/ELF/ppc64-tls-gd.s +++ b/lld/test/ELF/ppc64-tls-gd.s @@ -7,7 +7,11 @@ # RUN: llvm-readobj -r %t.so | FileCheck --check-prefix=GD-REL %s # RUN: llvm-objdump -d --no-show-raw-insn %t.so | FileCheck --check-prefix=GD %s +## R_PPC64_TLSGD exists. Test --fix-ppc64-tls-reloc does not warn. +# RUN: ld.lld -shared --fix-ppc64-tls-reloc --fatal-warnings %t.o %t1.o -o %tf.so && cmp %t.so %tf.so + # RUN: ld.lld %t.o %t1.o -o %t +# RUN: ld.lld --fix-ppc64-tls-reloc --fatal-warnings %t.o %t1.o -o %tf && cmp %t %tf # RUN: llvm-readelf -r %t | FileCheck --check-prefix=NOREL %s # RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefix=LE %s @@ -16,29 +20,29 @@ # RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefix=IE %s # GD-REL: .rela.dyn { -# GD-REL-NEXT: 0x20548 R_PPC64_DTPMOD64 a 0x0 -# GD-REL-NEXT: 0x20550 R_PPC64_DTPREL64 a 0x0 -# GD-REL-NEXT: 0x20558 R_PPC64_DTPMOD64 b 0x0 -# GD-REL-NEXT: 0x20560 R_PPC64_DTPREL64 b 0x0 -# GD-REL-NEXT: 0x20568 R_PPC64_DTPMOD64 c 0x0 -# GD-REL-NEXT: 0x20570 R_PPC64_DTPREL64 c 0x0 +# GD-REL-NEXT: 0x20578 R_PPC64_DTPMOD64 a 0x0 +# GD-REL-NEXT: 0x20580 R_PPC64_DTPREL64 a 0x0 +# GD-REL-NEXT: 0x20588 R_PPC64_DTPMOD64 b 0x0 +# GD-REL-NEXT: 0x20590 R_PPC64_DTPREL64 b 0x0 +# GD-REL-NEXT: 0x20598 R_PPC64_DTPMOD64 c 0x0 +# GD-REL-NEXT: 0x205A0 R_PPC64_DTPREL64 c 0x0 # GD-REL-NEXT: } ## &DTPMOD(a) - .TOC. = &.got[0] - (.got+0x8000) = -32768 # GD: addis 3, 2, 0 # GD-NEXT: addi 3, 3, -32768 -# GD-NEXT: bl 0x10400 +# GD-NEXT: bl [[#%x,TGA:]] # GD-NEXT: ld 2, 24(1) ## &DTPMOD(b) - .TOC. = &.got[2] - (.got+0x8000) = -32752 # GD-NEXT: addis 3, 2, 0 # GD-NEXT: addi 3, 3, -32752 -# GD-NEXT: bl 0x10400 +# GD-NEXT: bl [[#TGA]] # GD-NEXT: ld 2, 24(1) ## &DTPMOD(b) - .TOC. = &.got[4] - (.got+0x8000) = -32736 # GD-NEXT: li 3, -32736 -# GD-NEXT: bl 0x10400 +# GD-NEXT: bl [[#TGA]] # GD-NEXT: ld 2, 24(1) # NOREL: no relocations @@ -81,6 +85,9 @@ # IE-NEXT: nop # IE-NEXT: add 3, 3, 13 +.globl _start +_start: + addis 3, 2, a@got@tlsgd@ha addi 3, 3, a@got@tlsgd@l bl __tls_get_addr(a@tlsgd) 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,117 @@ # 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: split-file %s %t +# RUN: llvm-mc --triple=ppc64le %t/a.s --filetype=obj -o %t/a.o +# RUN: llvm-mc --triple=ppc64le %t/b.s --filetype=obj -o %t/b.o +# RUN: llvm-mc --triple=ppc64le %t/tga.s --filetype=obj -o %t/tga.o -## 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. +## By default, there is no diagnostic for missing R_PPC64_TLSGD/R_PPC64_TLSLD. +# RUN: ld.lld --shared --fatal-warnings %t/a.o %t/b.o -o %t.so +# RUN: llvm-objdump -d --no-leading-addr %t.so | FileCheck %s --check-prefix=DIS +## If -no-pie or -pie, without --fix-ppc64-tls-reloc, the output is incorrect +## but there is no diagnostic. +# RUN: ld.lld --fatal-warnings %t/a.o %t/tga.o -o /dev/null +# RUN: ld.lld --fatal-warnings --fix-ppc64-tls-reloc --no-fix-ppc64-tls-reloc %t/a.o %t/tga.o -o %t1 +# RUN: llvm-objdump -d --no-leading-addr %t1 | FileCheck %s --check-prefix=BAD + +# BAD-LABEL: : +# BAD-NEXT: nop +# BAD-NEXT: addis 3, 13, 0 +# BAD-NEXT: bl {{.*}} +# BAD-NEXT: nop + +## --fix-ppc64-tls-reloc warns and disables TLS relaxation. +# RUN: ld.lld --fix-ppc64-tls-reloc %t/a.o %t/tga.o -o %t2 2>&1 | FileCheck %s --check-prefix=WARN +# RUN: llvm-readelf -x .got %t2 | FileCheck %s --check-prefix=HEX +# RUN: llvm-objdump -d --no-leading-addr %t2 | FileCheck %s --check-prefix=DIS + +# WARN: warning: {{.*}}.o:(.text): disable TLS relaxation due to missing R_PPC64_TLSGD/R_PPC64_TLSLD + +## .got+0: x is local - relaxed to LE - its DTPMOD/DTPREL slots are link-time constants. +## DTPMOD is 1. DTPREL is st_value-0x8000 = -0x8000. +## .got+16: DTPMOD/DTPREL for _TLS_MODULE_BASE_ is 1 and 0, respectively. +## .got+32: TPOFFSET for x = st_value-0x7000 +# HEX: section '.got': +# HEX-NEXT: [[#%x,IGNORE:]] 01000000 00000000 0080ffff ffffffff +# HEX-NEXT: [[#%x,IGNORE:]] 01000000 00000000 00000000 00000000 +# HEX-NEXT: [[#%x,IGNORE:]] 0090ffff ffffffff + +## .TOC.-32768 = (.got+0x8000)-32768 = .got +# DIS-LABEL: : +# DIS-NEXT: addis 3, 2, 0 +# DIS-NEXT: addi 3, 3, -32768 +# DIS-NEXT: bl [[#%x,TGA:]] +# DIS-LABEL: : +# DIS-NEXT: addis 3, 2, 0 +# DIS-NEXT: addi 3, 3, -32768 +# DIS-NEXT: bl [[#TGA]] + +## LocalDynamic references _TLS_MODULE_BASE_. +## .TOC.-32752 = (.got+0x8000)-32752 = .got+16 +# DIS-LABEL: : +# DIS-NEXT: addis 3, 2, 0 +# DIS-NEXT: addi 3, 3, -32752 +# DIS-NEXT: bl [[#TGA]] +# DIS-LABEL: : +# DIS-NEXT: addis 3, 2, 0 +# DIS-NEXT: addi 3, 3, -32752 +# DIS-NEXT: bl [[#TGA]] + +## Technically we don't have to disable IE to LE relaxation, +## but disabling it for implementation simplicity does not hurt. +# DIS-LABEL: : +# DIS-NEXT: addis 3, 2, 0 +# DIS-NEXT: ld 3, -32736(3) +# DIS-NEXT: add 3, 3, 13 + +#--- a.s GeneralDynamic: addis 3, 2, x@got@tlsgd@ha addi 3, 3, x@got@tlsgd@l bl __tls_get_addr - blr + nop GeneralDynamic_NOTOC: addis 3, 2, x@got@tlsgd@ha addi 3, 3, x@got@tlsgd@l bl __tls_get_addr@notoc - blr + nop LocalDynamic: addis 3, 2, x@got@tlsld@ha addi 3, 3, x@got@tlsld@l bl __tls_get_addr - blr + nop LocalDynamic_NOTOC: addis 3, 2, x@got@tlsld@ha addi 3, 3, x@got@tlsld@l bl __tls_get_addr@notoc - blr + nop +InitialExec: + addis 3, 2, x@got@tprel@ha + ld 3, x@got@tprel@l(3) + add 3, 3, x@tls + +.globl _start +_start: + +.section .tbss,"awT",@nobits +.globl x +x: + .quad 0 + +#--- b.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. CallOnly: bl __tls_get_addr + nop + blr + +#--- tga.s +.globl __tls_get_addr +__tls_get_addr: blr