[lld] 3c9100f - [lld-macho] Support dynamic linking of thread-locals

Jez Ng via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 12 19:51:01 PDT 2020


Author: Jez Ng
Date: 2020-08-12T19:50:09-07:00
New Revision: 3c9100fb785c400c457ce83ba724a3b762b2348a

URL: https://github.com/llvm/llvm-project/commit/3c9100fb785c400c457ce83ba724a3b762b2348a
DIFF: https://github.com/llvm/llvm-project/commit/3c9100fb785c400c457ce83ba724a3b762b2348a.diff

LOG: [lld-macho] Support dynamic linking of thread-locals

References to symbols in dylibs work very similarly regardless of
whether the symbol is a TLV. The main difference is that we have a
separate `__thread_ptrs` section that acts as the GOT for these
thread-locals.

We can identify thread-locals in dylibs by a flag in their export trie
entries, and we cross-check it with the relocations that refer to them
to ensure that we are not using a GOT relocation to reference a
thread-local (or vice versa).

Reviewed By: #lld-macho, smeenai

Differential Revision: https://reviews.llvm.org/D85081

Added: 
    lld/test/MachO/invalid/bad-got-to-dylib-tlv-reference.s
    lld/test/MachO/invalid/bad-got-to-tlv-reference.s
    lld/test/MachO/invalid/bad-tlv-relocation.s
    lld/test/MachO/tlv-dylib.s

Modified: 
    lld/MachO/Arch/X86_64.cpp
    lld/MachO/ExportTrie.cpp
    lld/MachO/InputFiles.cpp
    lld/MachO/InputSection.cpp
    lld/MachO/InputSection.h
    lld/MachO/SymbolTable.cpp
    lld/MachO/SymbolTable.h
    lld/MachO/Symbols.h
    lld/MachO/SyntheticSections.cpp
    lld/MachO/SyntheticSections.h
    lld/MachO/Writer.cpp

Removed: 
    


################################################################################
diff  --git a/lld/MachO/Arch/X86_64.cpp b/lld/MachO/Arch/X86_64.cpp
index d168f10475c1..cdfb8b871803 100644
--- a/lld/MachO/Arch/X86_64.cpp
+++ b/lld/MachO/Arch/X86_64.cpp
@@ -224,6 +224,9 @@ void X86_64::prepareSymbolRelocation(lld::macho::Symbol &sym,
     // TODO: implement mov -> lea relaxation for non-dynamic symbols
   case X86_64_RELOC_GOT:
     in.got->addEntry(sym);
+    if (sym.isTlv())
+      error("found GOT relocation referencing thread-local variable in " +
+            toString(isec));
     break;
   case X86_64_RELOC_BRANCH: {
     // TODO: weak dysyms should go into the weak binding section instead
@@ -248,10 +251,20 @@ void X86_64::prepareSymbolRelocation(lld::macho::Symbol &sym,
   case X86_64_RELOC_SIGNED_4:
     break;
   case X86_64_RELOC_TLV:
-    if (isa<DylibSymbol>(&sym))
-      error("relocations to thread-local dylib symbols not yet implemented");
-    else
+    if (isa<DylibSymbol>(&sym)) {
+      in.tlvPointers->addEntry(sym);
+    } else {
       assert(isa<Defined>(&sym));
+      // TLV relocations on x86_64 are always used with a movq opcode, which
+      // can be converted to leaq opcodes if they reference a defined symbol.
+      // (This is in contrast to GOT relocations, which can be used with
+      // non-movq opcodes.) As such, there is no need to add an entry to
+      // tlvPointers here.
+    }
+    if (!sym.isTlv())
+      error(
+          "found X86_64_RELOC_TLV referencing a non-thread-local variable in " +
+          toString(isec));
     break;
   case X86_64_RELOC_SUBTRACTOR:
     fatal("TODO: handle relocation type " + std::to_string(r.type));
@@ -279,7 +292,7 @@ uint64_t X86_64::resolveSymbolVA(uint8_t *buf, const lld::macho::Symbol &sym,
     return sym.getVA();
   case X86_64_RELOC_TLV: {
     if (isa<DylibSymbol>(&sym))
-      error("relocations to thread-local dylib symbols not yet implemented");
+      return in.tlvPointers->addr + sym.gotIndex * WordSize;
 
     // Convert the movq to a leaq.
     assert(isa<Defined>(&sym));

diff  --git a/lld/MachO/ExportTrie.cpp b/lld/MachO/ExportTrie.cpp
index 993a55243532..f1c58d6e146b 100644
--- a/lld/MachO/ExportTrie.cpp
+++ b/lld/MachO/ExportTrie.cpp
@@ -59,11 +59,14 @@ struct Edge {
 
 struct ExportInfo {
   uint64_t address;
-  uint8_t flags;
-  explicit ExportInfo(const Symbol &sym)
-      : address(sym.getVA()),
-        flags(sym.isWeakDef() ? EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION : 0) {}
-  // TODO: Add proper support for re-exports & stub-and-resolver flags.
+  uint8_t flags = 0;
+  explicit ExportInfo(const Symbol &sym) : address(sym.getVA()) {
+    if (sym.isWeakDef())
+      flags |= EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
+    if (sym.isTlv())
+      flags |= EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL;
+    // TODO: Add proper support for re-exports & stub-and-resolver flags.
+  }
 };
 
 } // namespace

diff  --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp
index 28e138a84522..5df42b358203 100644
--- a/lld/MachO/InputFiles.cpp
+++ b/lld/MachO/InputFiles.cpp
@@ -363,8 +363,9 @@ DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella)
     parseTrie(buf + c->export_off, c->export_size,
               [&](const Twine &name, uint64_t flags) {
                 bool isWeakDef = flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
-                symbols.push_back(
-                    symtab->addDylib(saver.save(name), umbrella, isWeakDef));
+                bool isTlv = flags & EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL;
+                symbols.push_back(symtab->addDylib(saver.save(name), umbrella,
+                                                   isWeakDef, isTlv));
               });
   } else {
     error("LC_DYLD_INFO_ONLY not found in " + getName());
@@ -403,11 +404,12 @@ DylibFile::DylibFile(std::shared_ptr<llvm::MachO::InterfaceFile> interface,
 
   dylibName = saver.save(interface->getInstallName());
   // TODO(compnerd) filter out symbols based on the target platform
-  // TODO: handle weak defs
+  // TODO: handle weak defs, thread locals
   for (const auto symbol : interface->symbols())
     if (symbol->getArchitectures().has(config->arch))
       symbols.push_back(symtab->addDylib(saver.save(symbol->getName()),
-                                         umbrella, /*isWeakDef=*/false));
+                                         umbrella, /*isWeakDef=*/false,
+                                         /*isTlv=*/false));
   // TODO(compnerd) properly represent the hierarchy of the documents as it is
   // in theory possible to have re-exported dylibs from re-exported dylibs which
   // should be parent'ed to the child.

diff  --git a/lld/MachO/InputSection.cpp b/lld/MachO/InputSection.cpp
index a7fcf49334b2..66f30ad0a7f0 100644
--- a/lld/MachO/InputSection.cpp
+++ b/lld/MachO/InputSection.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "InputSection.h"
+#include "InputFiles.h"
 #include "OutputSegment.h"
 #include "Symbols.h"
 #include "Target.h"
@@ -55,3 +56,7 @@ void InputSection::writeTo(uint8_t *buf) {
     target->relocateOne(buf + r.offset, r, val);
   }
 }
+
+std::string lld::toString(const InputSection *isec) {
+  return (toString(isec->file) + ":(" + isec->name + ")").str();
+}

diff  --git a/lld/MachO/InputSection.h b/lld/MachO/InputSection.h
index 93517298f6c7..0f96ad35c309 100644
--- a/lld/MachO/InputSection.h
+++ b/lld/MachO/InputSection.h
@@ -74,6 +74,9 @@ class InputSection {
 extern std::vector<InputSection *> inputSections;
 
 } // namespace macho
+
+std::string toString(const macho::InputSection *);
+
 } // namespace lld
 
 #endif

diff  --git a/lld/MachO/SymbolTable.cpp b/lld/MachO/SymbolTable.cpp
index 1a8a1d5ac065..bd10a2c56989 100644
--- a/lld/MachO/SymbolTable.cpp
+++ b/lld/MachO/SymbolTable.cpp
@@ -69,14 +69,15 @@ Symbol *SymbolTable::addUndefined(StringRef name) {
   return s;
 }
 
-Symbol *SymbolTable::addDylib(StringRef name, DylibFile *file, bool isWeakDef) {
+Symbol *SymbolTable::addDylib(StringRef name, DylibFile *file, bool isWeakDef,
+                              bool isTlv) {
   Symbol *s;
   bool wasInserted;
   std::tie(s, wasInserted) = insert(name);
 
   if (wasInserted || isa<Undefined>(s) ||
       (isa<DylibSymbol>(s) && !isWeakDef && s->isWeakDef()))
-    replaceSymbol<DylibSymbol>(s, file, name, isWeakDef);
+    replaceSymbol<DylibSymbol>(s, file, name, isWeakDef, isTlv);
 
   return s;
 }

diff  --git a/lld/MachO/SymbolTable.h b/lld/MachO/SymbolTable.h
index 822eb5b35dac..178b26f48599 100644
--- a/lld/MachO/SymbolTable.h
+++ b/lld/MachO/SymbolTable.h
@@ -36,7 +36,7 @@ class SymbolTable {
 
   Symbol *addUndefined(StringRef name);
 
-  Symbol *addDylib(StringRef name, DylibFile *file, bool isWeakDef);
+  Symbol *addDylib(StringRef name, DylibFile *file, bool isWeakDef, bool isTlv);
 
   Symbol *addLazy(StringRef name, ArchiveFile *file,
                   const llvm::object::Archive::Symbol &sym);

diff  --git a/lld/MachO/Symbols.h b/lld/MachO/Symbols.h
index 1e0767a6a12b..4ccb87e18335 100644
--- a/lld/MachO/Symbols.h
+++ b/lld/MachO/Symbols.h
@@ -55,6 +55,11 @@ class Symbol {
 
   virtual bool isWeakDef() const { llvm_unreachable("cannot be weak"); }
 
+  virtual bool isTlv() const { llvm_unreachable("cannot be TLV"); }
+
+  // The index of this symbol in the GOT or the TLVPointer section, depending
+  // on whether it is a thread-local. A given symbol cannot be referenced by
+  // both these sections at once.
   uint32_t gotIndex = UINT32_MAX;
 
 protected:
@@ -72,6 +77,8 @@ class Defined : public Symbol {
 
   bool isWeakDef() const override { return weakDef; }
 
+  bool isTlv() const override { return isThreadLocalVariables(isec->flags); }
+
   static bool classof(const Symbol *s) { return s->kind() == DefinedKind; }
 
   uint64_t getVA() const override { return isec->getVA() + value; }
@@ -96,11 +103,13 @@ class Undefined : public Symbol {
 
 class DylibSymbol : public Symbol {
 public:
-  DylibSymbol(DylibFile *file, StringRefZ name, bool isWeakDef)
-      : Symbol(DylibKind, name), file(file), weakDef(isWeakDef) {}
+  DylibSymbol(DylibFile *file, StringRefZ name, bool isWeakDef, bool isTlv)
+      : Symbol(DylibKind, name), file(file), weakDef(isWeakDef), tlv(isTlv) {}
 
   bool isWeakDef() const override { return weakDef; }
 
+  bool isTlv() const override { return tlv; }
+
   static bool classof(const Symbol *s) { return s->kind() == DylibKind; }
 
   DylibFile *file;
@@ -109,6 +118,7 @@ class DylibSymbol : public Symbol {
 
 private:
   const bool weakDef;
+  const bool tlv;
 };
 
 class LazySymbol : public Symbol {

diff  --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp
index 5829319f5c2e..a4ab454ab2eb 100644
--- a/lld/MachO/SyntheticSections.cpp
+++ b/lld/MachO/SyntheticSections.cpp
@@ -82,22 +82,21 @@ void MachHeaderSection::writeTo(uint8_t *buf) const {
 PageZeroSection::PageZeroSection()
     : SyntheticSection(segment_names::pageZero, section_names::pageZero) {}
 
-GotSection::GotSection()
-    : SyntheticSection(segment_names::dataConst, section_names::got) {
+NonLazyPointerSectionBase::NonLazyPointerSectionBase(const char *segname,
+                                                     const char *name)
+    : SyntheticSection(segname, name) {
   align = 8;
   flags = MachO::S_NON_LAZY_SYMBOL_POINTERS;
-
-  // TODO: section_64::reserved1 should be an index into the indirect symbol
-  // table, which we do not currently emit
 }
 
-void GotSection::addEntry(Symbol &sym) {
+void NonLazyPointerSectionBase::addEntry(Symbol &sym) {
   if (entries.insert(&sym)) {
+    assert(sym.gotIndex == UINT32_MAX);
     sym.gotIndex = entries.size() - 1;
   }
 }
 
-void GotSection::writeTo(uint8_t *buf) const {
+void NonLazyPointerSectionBase::writeTo(uint8_t *buf) const {
   for (size_t i = 0, n = entries.size(); i < n; ++i)
     if (auto *defined = dyn_cast<Defined>(entries[i]))
       write64le(&buf[i * WordSize], defined->getVA());
@@ -107,7 +106,8 @@ BindingSection::BindingSection()
     : LinkEditSection(segment_names::linkEdit, section_names::binding) {}
 
 bool BindingSection::isNeeded() const {
-  return bindings.size() != 0 || in.got->isNeeded();
+  return bindings.size() != 0 || in.got->isNeeded() ||
+         in.tlvPointers->isNeeded();
 }
 
 namespace {
@@ -138,7 +138,6 @@ static void encodeBinding(const DylibSymbol &dysym, const OutputSection *osec,
     lastBinding.segment = seg;
     lastBinding.offset = offset;
   } else if (lastBinding.offset != offset) {
-    assert(lastBinding.offset <= offset);
     os << static_cast<uint8_t>(BIND_OPCODE_ADD_ADDR_ULEB);
     encodeULEB128(offset - lastBinding.offset, os);
     lastBinding.offset = offset;
@@ -169,6 +168,22 @@ static void encodeBinding(const DylibSymbol &dysym, const OutputSection *osec,
   lastBinding.offset += WordSize;
 }
 
+static bool encodeNonLazyPointerSection(NonLazyPointerSectionBase *osec,
+                                        Binding &lastBinding,
+                                        raw_svector_ostream &os) {
+  bool didEncode = false;
+  size_t idx = 0;
+  for (const Symbol *sym : osec->getEntries()) {
+    if (const auto *dysym = dyn_cast<DylibSymbol>(sym)) {
+      didEncode = true;
+      encodeBinding(*dysym, osec, idx * WordSize, /*addend=*/0, lastBinding,
+                    os);
+    }
+    ++idx;
+  }
+  return didEncode;
+}
+
 // Emit bind opcodes, which are a stream of byte-sized opcodes that dyld
 // interprets to update a record with the following fields:
 //  * segment index (of the segment to write the symbol addresses to, typically
@@ -185,15 +200,8 @@ static void encodeBinding(const DylibSymbol &dysym, const OutputSection *osec,
 void BindingSection::finalizeContents() {
   raw_svector_ostream os{contents};
   Binding lastBinding;
-  bool didEncode = false;
-  size_t gotIdx = 0;
-  for (const Symbol *sym : in.got->getEntries()) {
-    if (const auto *dysym = dyn_cast<DylibSymbol>(sym)) {
-      didEncode = true;
-      encodeBinding(*dysym, in.got, gotIdx * WordSize, 0, lastBinding, os);
-    }
-    ++gotIdx;
-  }
+  bool didEncode = encodeNonLazyPointerSection(in.got, lastBinding, os);
+  didEncode |= encodeNonLazyPointerSection(in.tlvPointers, lastBinding, os);
 
   // Sorting the relocations by segment and address allows us to encode them
   // more compactly.

diff  --git a/lld/MachO/SyntheticSections.h b/lld/MachO/SyntheticSections.h
index db2ffbe57d44..a900ef287331 100644
--- a/lld/MachO/SyntheticSections.h
+++ b/lld/MachO/SyntheticSections.h
@@ -13,6 +13,7 @@
 #include "ExportTrie.h"
 #include "InputSection.h"
 #include "OutputSection.h"
+#include "OutputSegment.h"
 #include "Target.h"
 
 #include "llvm/ADT/SetVector.h"
@@ -31,6 +32,7 @@ constexpr const char export_[] = "__export";
 constexpr const char symbolTable[] = "__symbol_table";
 constexpr const char stringTable[] = "__string_table";
 constexpr const char got[] = "__got";
+constexpr const char threadPtrs[] = "__thread_ptrs";
 
 } // namespace section_names
 
@@ -95,11 +97,13 @@ class PageZeroSection : public SyntheticSection {
   void writeTo(uint8_t *buf) const override {}
 };
 
-// This section will be populated by dyld with addresses to non-lazily-loaded
-// dylib symbols.
-class GotSection : public SyntheticSection {
+// This is the base class for the GOT and TLVPointer sections, which are nearly
+// functionally identical -- they will both be populated by dyld with addresses
+// to non-lazily-loaded dylib symbols. The main 
diff erence is that the
+// TLVPointerSection stores references to thread-local variables.
+class NonLazyPointerSectionBase : public SyntheticSection {
 public:
-  GotSection();
+  NonLazyPointerSectionBase(const char *segname, const char *name);
 
   const llvm::SetVector<const Symbol *> &getEntries() const { return entries; }
 
@@ -115,6 +119,23 @@ class GotSection : public SyntheticSection {
   llvm::SetVector<const Symbol *> entries;
 };
 
+class GotSection : public NonLazyPointerSectionBase {
+public:
+  GotSection()
+      : NonLazyPointerSectionBase(segment_names::dataConst,
+                                  section_names::got) {
+    // TODO: section_64::reserved1 should be an index into the indirect symbol
+    // table, which we do not currently emit
+  }
+};
+
+class TlvPointerSection : public NonLazyPointerSectionBase {
+public:
+  TlvPointerSection()
+      : NonLazyPointerSectionBase(segment_names::data,
+                                  section_names::threadPtrs) {}
+};
+
 struct BindingEntry {
   const DylibSymbol *dysym;
   const InputSection *isec;
@@ -297,6 +318,7 @@ struct InStruct {
   MachHeaderSection *header = nullptr;
   BindingSection *binding = nullptr;
   GotSection *got = nullptr;
+  TlvPointerSection *tlvPointers = nullptr;
   LazyPointerSection *lazyPointers = nullptr;
   StubsSection *stubs = nullptr;
   StubHelperSection *stubHelper = nullptr;

diff  --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp
index 886ca482136b..6243f6c3f8c9 100644
--- a/lld/MachO/Writer.cpp
+++ b/lld/MachO/Writer.cpp
@@ -540,6 +540,7 @@ void macho::createSyntheticSections() {
   in.header = make<MachHeaderSection>();
   in.binding = make<BindingSection>();
   in.got = make<GotSection>();
+  in.tlvPointers = make<TlvPointerSection>();
   in.lazyPointers = make<LazyPointerSection>();
   in.stubs = make<StubsSection>();
   in.stubHelper = make<StubHelperSection>();

diff  --git a/lld/test/MachO/invalid/bad-got-to-dylib-tlv-reference.s b/lld/test/MachO/invalid/bad-got-to-dylib-tlv-reference.s
new file mode 100644
index 000000000000..c1c817cb9019
--- /dev/null
+++ b/lld/test/MachO/invalid/bad-got-to-dylib-tlv-reference.s
@@ -0,0 +1,23 @@
+# REQUIRES: x86
+# RUN: split-file %s %t
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/libtlv.s -o %t/libtlv.o
+# RUN: lld -flavor darwinnew -dylib -install_name @executable_path/libtlv.dylib \
+# RUN:   -Z -L%S/../Inputs/MacOSX.sdk/usr/lib -lSystem -o %t/libtlv.dylib %t/libtlv.o
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/test.s -o %t/test.o
+# RUN: not lld -flavor darwinnew -Z -L%S/../Inputs/MacOSX.sdk/usr/lib -lSystem -L%t -ltlv -o /dev/null %t/test.o 2>&1 | FileCheck %s -DFILE=%t/test.o
+
+# CHECK: error: found GOT relocation referencing thread-local variable in [[FILE]]:(__text)
+
+#--- libtlv.s
+.section	__DATA,__thread_vars,thread_local_variables
+.globl _foo
+_foo:
+
+#--- test.s
+.text
+.globl _main
+_main:
+  movq _foo at GOTPCREL(%rip), %rax
+  ret

diff  --git a/lld/test/MachO/invalid/bad-got-to-tlv-reference.s b/lld/test/MachO/invalid/bad-got-to-tlv-reference.s
new file mode 100644
index 000000000000..916182f81197
--- /dev/null
+++ b/lld/test/MachO/invalid/bad-got-to-tlv-reference.s
@@ -0,0 +1,14 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o
+# RUN: not lld -flavor darwinnew -o /dev/null %t.o 2>&1 | FileCheck %s -DFILE=%t.o
+
+# CHECK: error: found GOT relocation referencing thread-local variable in [[FILE]]:(__text)
+
+.text
+.globl _main
+_main:
+  movq _foo at GOTPCREL(%rip), %rax
+  ret
+
+.section __DATA,__thread_vars,thread_local_variables
+_foo:

diff  --git a/lld/test/MachO/invalid/bad-tlv-relocation.s b/lld/test/MachO/invalid/bad-tlv-relocation.s
new file mode 100644
index 000000000000..586f1804b0aa
--- /dev/null
+++ b/lld/test/MachO/invalid/bad-tlv-relocation.s
@@ -0,0 +1,14 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o
+# RUN: not lld -flavor darwinnew -o /dev/null %t.o 2>&1 | FileCheck %s -DFILE=%t.o
+
+# CHECK: error: found X86_64_RELOC_TLV referencing a non-thread-local variable in [[FILE]]:(__text)
+
+.text
+.globl _main
+_main:
+  leaq _foo at TLVP(%rip), %rax
+  ret
+
+.data
+_foo:

diff  --git a/lld/test/MachO/tlv-dylib.s b/lld/test/MachO/tlv-dylib.s
new file mode 100644
index 000000000000..daa11be37b79
--- /dev/null
+++ b/lld/test/MachO/tlv-dylib.s
@@ -0,0 +1,40 @@
+# REQUIRES: x86
+# RUN: split-file %s %t
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/libtlv.s -o %t/libtlv.o
+# RUN: lld -flavor darwinnew -dylib -install_name @executable_path/libtlv.dylib \
+# RUN:   -Z -L%S/Inputs/MacOSX.sdk/usr/lib -lSystem -o %t/libtlv.dylib %t/libtlv.o
+# RUN: llvm-objdump --exports-trie -d --no-show-raw-insn %t/libtlv.dylib | FileCheck %s --check-prefix=DYLIB
+# DYLIB-DAG: _foo [per-thread]
+# DYLIB-DAG: _bar [per-thread]
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/test.s -o %t/test.o
+# RUN: lld -flavor darwinnew -Z -L%S/Inputs/MacOSX.sdk/usr/lib -lSystem -L%t -ltlv %t/test.o -o %t/test
+# RUN: llvm-objdump --bind -d --no-show-raw-insn %t/test | FileCheck %s
+
+# CHECK:      movq [[#]](%rip), %rax # [[#%x, FOO:]]
+# CHECK-NEXT: movq [[#]](%rip), %rax # [[#%x, BAR:]]
+# CHECK-NEXT: movq [[#]](%rip), %rax # [[#%x, BAZ:]]
+
+# CHECK-LABEL: Bind table:
+# CHECK-DAG: __DATA       __thread_ptrs  0x{{0*}}[[#%x, FOO]] pointer 0   libtlv   _foo
+# CHECK-DAG: __DATA       __thread_ptrs  0x{{0*}}[[#%x, BAR]] pointer 0   libtlv   _bar
+# CHECK-DAG: __DATA_CONST __got          0x{{0*}}[[#%x, BAZ]] pointer 0   libtlv   _baz
+
+#--- libtlv.s
+.section	__DATA,__thread_vars,thread_local_variables
+.globl _foo, _bar, _baz
+_foo:
+_bar:
+
+.text
+_baz:
+
+#--- test.s
+.globl _main
+_main:
+  mov _foo at TLVP(%rip), %rax
+  mov _bar at TLVP(%rip), %rax
+## Add a GOT entry to make sure we don't mix it up with TLVs
+  mov _baz at GOTPCREL(%rip), %rax
+  ret


        


More information about the llvm-commits mailing list