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/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. @@ -1527,6 +1531,42 @@ processRelocAux(sec, expr, type, offset, sym, rel, addend); } +// R_PPC64_TLSGD/R_PPC64_TLSLD is required to mark `bl __tls_get_addr` for +// General Dynamic/Local Dynamic code sequences. If a GD/LD GOT relocation is +// found but no R_PPC64_TLSGD/R_PPC64_TLSLD is seen, we assume that the +// instructions are generated by very old IBM XL compilers. Work around the +// issue by disabling GD/LD to IE/LE relaxation. +template +static void checkPPC64TLSRelax(InputSectionBase &sec, ArrayRef rels) { + // Skip if sec is synthetic (sec.file is null) or if sec has been marked. + if (!sec.file || sec.file->ppc64DisableTLSRelax) + return; + bool hasGDLD = false; + for (const RelTy &rel : rels) { + RelType type = rel.getType(false); + switch (type) { + case R_PPC64_TLSGD: + case R_PPC64_TLSLD: + return; // Found a marker + 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; + } + } + if (hasGDLD) { + sec.file->ppc64DisableTLSRelax = true; + warn(toString(sec.file) + ": disable TLS relaxation due to missing " + "R_PPC64_TLSGD/R_PPC64_TLSLD relocation"); + } +} + template static void scanRelocs(InputSectionBase &sec, ArrayRef rels) { OffsetGetter getOffset(sec); @@ -1534,6 +1574,9 @@ // Not all relocations end up in Sec.Relocations, but a lot do. sec.relocations.reserve(rels.size()); + if (config->emachine == EM_PPC64) + checkPPC64TLSRelax(sec, rels); + for (auto i = rels.begin(), end = rels.end(); i != end;) scanReloc(sec, getOffset, i, rels.begin(), end); 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,106 @@ # 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. +# RUN: ld.lld --shared --fatal-warnings %t/b.o -o /dev/null +## Warn missing R_PPC64_TLSGD/R_PPC64_TLSLD. +# RUN: ld.lld --shared %t/a.o -o %t.so 2>&1 | FileCheck %s --check-prefix=WARN +# RUN: llvm-objdump -d --no-leading-addr %t.so | FileCheck %s --check-prefix=DIS + +# RUN: ld.lld %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: disable TLS relaxation due to missing R_PPC64_TLSGD/R_PPC64_TLSLD relocation + +## .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 CallOnly: bl __tls_get_addr + nop + blr + +#--- tga.s +.globl __tls_get_addr +__tls_get_addr: blr