diff --git a/lld/ELF/Arch/Hexagon.cpp b/lld/ELF/Arch/Hexagon.cpp --- a/lld/ELF/Arch/Hexagon.cpp +++ b/lld/ELF/Arch/Hexagon.cpp @@ -119,6 +119,7 @@ case R_HEX_PLT_B22_PCREL: case R_HEX_B22_PCREL_X: case R_HEX_B32_PCREL_X: + case R_HEX_GD_PLT_B22_PCREL: return R_PLT_PC; case R_HEX_IE_32_6_X: case R_HEX_IE_16_X: @@ -303,6 +304,7 @@ case R_HEX_B15_PCREL_X: or32le(loc, applyMask(0x00df20fe, val & 0x3f)); break; + case R_HEX_GD_PLT_B22_PCREL: case R_HEX_B22_PCREL: case R_HEX_PLT_B22_PCREL: checkInt(loc, val, 22, rel); diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h --- a/lld/ELF/Relocations.h +++ b/lld/ELF/Relocations.h @@ -114,6 +114,9 @@ template void reportUndefinedSymbols(); +void hexagonTLSSymbolUpdate(ArrayRef outputSections); +bool hexagonNeedsTLSSymbol(ArrayRef outputSections); + class ThunkSection; class Thunk; struct InputSectionDescription; diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -1975,6 +1975,46 @@ return addressesChanged; } +// The following aid in the conversion of call x@GDPLT to call __tls_get_addr +// hexagonNeedsTLSSymbol scans for relocations would require a call to +// __tls_get_addr. +// hexagonTLSSymbolUpdate rebinds the relocation to __tls_get_addr. +bool hexagonNeedsTLSSymbol(ArrayRef outputSections) { + bool needTLSSymbol = false; + forEachInputSectionDescription( + outputSections, [&](OutputSection *os, InputSectionDescription *isd) { + for (InputSection *isec : isd->sections) + for (Relocation &rel : isec->relocations) + if (rel.sym->type == llvm::ELF::STT_TLS && + rel.expr == R_PLT_PC) { + needTLSSymbol = true; + return; + } + }); + return needTLSSymbol; +} + +void hexagonTLSSymbolUpdate(ArrayRef outputSections) { + bool needEntry = true; + forEachInputSectionDescription( + outputSections, [&](OutputSection *os, InputSectionDescription *isd) { + for (InputSection *isec : isd->sections) + for (Relocation &rel : isec->relocations) + if (rel.sym->type == llvm::ELF::STT_TLS && + rel.expr == R_PLT_PC) { + Symbol *sym = symtab->find("__tls_get_addr"); + if (!sym) + error("unable to find __tls_get_addr"); + if (needEntry) { + addPltEntry(in.plt, in.gotPlt, in.relaPlt, target->pltRel, + *sym); + needEntry = false; + } + rel.sym = sym; + } + }); +} + template void scanRelocations(InputSectionBase &); template void scanRelocations(InputSectionBase &); template void scanRelocations(InputSectionBase &); diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1567,6 +1567,10 @@ ARMErr657417Patcher a32p; script->assignAddresses(); + // Converts call x@GDPLT to call __tls_get_addr + if (config->emachine == EM_HEXAGON) + hexagonTLSSymbolUpdate(outputSections); + int assignPasses = 0; for (;;) { bool changed = target->needsThunks && tc.createThunks(outputSections); @@ -1811,6 +1815,15 @@ sec->addrExpr = [=] { return i->second; }; } + // With the outputSections available check for GDPLT relocations + // and add __tls_get_addr symbol if needed. + if (config->emachine == EM_HEXAGON && !symtab->find("__tls_get_addr") && + hexagonNeedsTLSSymbol(outputSections)) { + Symbol *sym = symtab->addSymbol(Undefined{ + nullptr, "__tls_get_addr", STB_GLOBAL, STV_DEFAULT, STT_NOTYPE}); + partitions[0].dynSymTab->addSymbol(sym); + } + // This is a bit of a hack. A value of 0 means undef, so we set it // to 1 to make __ehdr_start defined. The section number is not // particularly relevant. diff --git a/lld/test/ELF/hexagon-tls-gd-xform.s b/lld/test/ELF/hexagon-tls-gd-xform.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/hexagon-tls-gd-xform.s @@ -0,0 +1,48 @@ +# REQUIRES: hexagon +# RUN: llvm-mc -filetype=obj -defsym GDPLT=1 -triple=hexagon-unknown-elf \ +# RUN: %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=hexagon-unknown-elf %s -o %t1.o +# RUN: ld.lld -shared %t.o -o %t.so +# RUN: ld.lld -shared %t1.o -o %t1.so +# RUN: llvm-objdump -d --no-show-raw-insn --print-imm-hex %t.so | \ +# RUN: FileCheck --check-prefix=CHECK_GDPLT %s +# RUN: llvm-objdump -d --no-show-raw-insn --print-imm-hex %t1.so | FileCheck %s +# RUN: llvm-readobj -r %t.so | FileCheck -check-prefix=RELA_GDPLT %s + +## Make sure __tls_get_addr is not present unless there is a GDPLT relocation. +# RUN: llvm-readobj -r %t1.so | FileCheck -check-prefix=RELA \ +# RUN: --implicit-check-not="__tls_get_addr" %s + +.globl _start +.type _start, @function + +_start: +.ifdef GDPLT + call x@gdplt +# CHECK_GDPLT: 101ec: { call 0x10220 } +.else + call x +# CHECK: 101b8: { call 0x101e0 } +.endif + +# CHECK_GDPLT: 10220: { immext(#0x20040) +# CHECK_GDPLT-NEXT: 10224: r14 = add(pc,##0x2007c) } +# CHECK_GDPLT-NEXT: 10228: { r28 = memw(r14+#0x0) } +# CHECK_GDPLT-NEXT: 1022c: { jumpr r28 } + + +## Looking at the above check, 0x10220+0x2007c must equal the entry for +## __tls_get_addr, 0x3029C + +# RELA_GDPLT: Relocations [ +# RELA_GDPLT-NEXT Section (5) .rela.plt { +# RELA_GDPLT-NEXT 0x30298 R_HEX_JMP_SLOT x 0x0 +# RELA_GDPLT-NEXT 0x3029C R_HEX_JMP_SLOT __tls_get_addr 0x0 +# RELA_GDPLT-NEXT } +# RELA_GDPLT-NEXT] + +# RELA: Relocations [ +# RELA-NEXT Section (5) .rela.plt { +# RELA-NEXT 0x30298 R_HEX_JMP_SLOT x 0x0 +# RELA-NEXT } +# RELA-NEXT]