[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