Skip to content

Commit

Permalink
[LLD][PowerPC] Add check in LLD to produce an error for missing TLSGD…
Browse files Browse the repository at this point in the history
…/TLSLD

The function `__tls_get_addr` is used to get the address of an object that is Thread Local Storage.
It needs to have two relocations on it.
One relocation is for the function call itself and it is either R_PPC64_REL24 or R_PPC64_REL24_NOTOC.
The other is R_PPC64_TLSGD or R_PPC64_TLSLD for the symbol that is having its address computed.

In the early days of the transition from the ELFv1 ABI that is used for big endian PowerPC Linux distributions to the ELFv2 ABI that is used for little endian PowerPC Linux distributions, there was some ambiguity in the specification of the relocations for TLS. The GNU linker has implemented support for correct handling of calls to __tls_get_addr with a missing relocation. Unfortunately, we didn't notice that the IBM XL compiler did not handle TLS according to the updated ABI until we tried linking XL compiled libraries with LLD. As a result, there is a lot of code out there in various libraries compiled with XL that have this problem.

This patch adds a new error check in LLD that makes sure calls to `__tls_get_addr` are not missing the TLSGD/TLSLD relocation.

Reviewed By: MaskRay

Differential Revision: https://reviews.llvm.org/D85994
  • Loading branch information
stefanp-ibm committed Aug 21, 2020
1 parent 36ee9a3 commit 02e02f5
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 2 deletions.
26 changes: 24 additions & 2 deletions lld/ELF/Relocations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1263,7 +1263,7 @@ static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type,

template <class ELFT, class RelTy>
static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
RelTy *end) {
RelTy *start, RelTy *end) {
const RelTy &rel = *i;
uint32_t symIndex = rel.getSymbol(config->isMips64EL);
Symbol &sym = sec.getFile<ELFT>()->getSymbol(symIndex);
Expand Down Expand Up @@ -1308,6 +1308,28 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
int64_t addend = computeAddend<ELFT>(rel, end, sec, expr, sym.isLocal());

if (config->emachine == EM_PPC64) {
// For a call to __tls_get_addr, the instruction 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].
if ((type == R_PPC64_REL24 || type == R_PPC64_REL24_NOTOC) &&
sym.getName() == "__tls_get_addr") {
bool err = i - start < 2;
if (!err) {
// Subtract 2 to get the previous iterator because we have already done
// ++i above. This is now safe because we know that i-1 is not the
// start.
const RelTy &prevRel = *(i - 2);
RelType prevType = prevRel.getType(config->isMips64EL);
err = prevRel.r_offset != rel.r_offset ||
(prevType != R_PPC64_TLSGD && prevType != R_PPC64_TLSLD);
}

if (err)
errorOrWarn("call to __tls_get_addr is missing a "
"R_PPC64_TLSGD/R_PPC64_TLSLD relocation" +
getLocation(sec, sym, offset));
}

// 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.
Expand Down Expand Up @@ -1502,7 +1524,7 @@ static void scanRelocs(InputSectionBase &sec, ArrayRef<RelTy> rels) {
sec.relocations.reserve(rels.size());

for (auto i = rels.begin(), end = rels.end(); i != end;)
scanReloc<ELFT>(sec, getOffset, i, end);
scanReloc<ELFT>(sec, getOffset, i, rels.begin(), end);

// Sort relocations by offset for more efficient searching for
// R_RISCV_PCREL_HI20 and R_PPC64_ADDR64.
Expand Down
53 changes: 53 additions & 0 deletions lld/test/ELF/ppc64-tls-missing-gdld.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# 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: 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

# 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)

# 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+0x40)

GeneralDynamic:
addis 3, 2, x@got@tlsgd@ha
addi 3, 3, x@got@tlsgd@l
bl __tls_get_addr
blr

GeneralDynamic_NOTOC:
addis 3, 2, x@got@tlsgd@ha
addi 3, 3, x@got@tlsgd@l
bl __tls_get_addr@notoc
blr

LocalDynamic:
addis 3, 2, x@got@tlsld@ha
addi 3, 3, x@got@tlsld@l
bl __tls_get_addr
blr

LocalDynamic_NOTOC:
addis 3, 2, x@got@tlsld@ha
addi 3, 3, x@got@tlsld@l
bl __tls_get_addr@notoc
blr

CallOnly:
bl __tls_get_addr
blr

0 comments on commit 02e02f5

Please sign in to comment.