diff --git a/lld/test/wasm/relocation-bad-tls.s b/lld/test/wasm/relocation-bad-tls.s new file mode 100644 --- /dev/null +++ b/lld/test/wasm/relocation-bad-tls.s @@ -0,0 +1,24 @@ +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s +# RUN: not wasm-ld %t.o -o out.wasm 2>&1 | FileCheck %s + +.globl _start +_start: + .functype _start () -> () + i32.const foo@TLSREL + i32.const bar@TLSREL + end_function + +.section .data,"",@ +.globl foo +foo: + .int32 0 + .size foo, 4 + +.section .bss,"",@ +.globl bar +bar: + .int32 0 + .size bar, 4 + +# CHECK: relocation R_WASM_MEMORY_ADDR_TLS_SLEB cannot be used against `foo` in non-TLS section: .data +# CHECK: relocation R_WASM_MEMORY_ADDR_TLS_SLEB cannot be used against `bar` in non-TLS section: .bss diff --git a/lld/test/wasm/tls.s b/lld/test/wasm/tls.s --- a/lld/test/wasm/tls.s +++ b/lld/test/wasm/tls.s @@ -7,7 +7,7 @@ tls1_addr: .functype tls1_addr () -> (i32) global.get __tls_base - i32.const tls1 + i32.const tls1@TLSREL i32.add end_function @@ -15,7 +15,7 @@ tls2_addr: .functype tls2_addr () -> (i32) global.get __tls_base - i32.const tls2 + i32.const tls2@TLSREL i32.add end_function diff --git a/lld/wasm/InputChunks.cpp b/lld/wasm/InputChunks.cpp --- a/lld/wasm/InputChunks.cpp +++ b/lld/wasm/InputChunks.cpp @@ -79,6 +79,7 @@ case R_WASM_TABLE_INDEX_REL_SLEB: case R_WASM_MEMORY_ADDR_SLEB: case R_WASM_MEMORY_ADDR_REL_SLEB: + case R_WASM_MEMORY_ADDR_TLS_SLEB: existingValue = static_cast(decodeSLEB128(loc, &bytesRead)); break; case R_WASM_TABLE_INDEX_SLEB64: @@ -158,6 +159,7 @@ case R_WASM_TABLE_INDEX_REL_SLEB: case R_WASM_MEMORY_ADDR_SLEB: case R_WASM_MEMORY_ADDR_REL_SLEB: + case R_WASM_MEMORY_ADDR_TLS_SLEB: encodeSLEB128(static_cast(value), loc, 5); break; case R_WASM_TABLE_INDEX_SLEB64: diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -11,6 +11,7 @@ #include "InputChunks.h" #include "InputEvent.h" #include "InputGlobal.h" +#include "OutputSegment.h" #include "SymbolTable.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" @@ -154,7 +155,8 @@ case R_WASM_MEMORY_ADDR_REL_SLEB: case R_WASM_MEMORY_ADDR_REL_SLEB64: case R_WASM_MEMORY_ADDR_I32: - case R_WASM_MEMORY_ADDR_I64: { + case R_WASM_MEMORY_ADDR_I64: + case R_WASM_MEMORY_ADDR_TLS_SLEB: { const WasmSymbol &sym = wasmObj->syms()[reloc.Index]; if (sym.isUndefined()) return 0; @@ -227,10 +229,24 @@ case R_WASM_MEMORY_ADDR_REL_SLEB: case R_WASM_MEMORY_ADDR_REL_SLEB64: case R_WASM_MEMORY_ADDR_I32: - case R_WASM_MEMORY_ADDR_I64: + case R_WASM_MEMORY_ADDR_I64: { + if (isa(sym) || sym->isUndefWeak()) + return 0; + auto D = cast(sym); + // Treat non-TLS relocation against symbols that live in the TLS segment + // like TLS relocations. This beaviour exists to support older object + // files created before we introduced TLS relocations. + // TODO(sbc): Remove this legacy behaviour one day. This will break + // backward compat with old object files built with `-fPIC`. + if (D->segment && D->segment->outputSeg->name == ".tdata") + return D->getOutputSegmentOffset() + reloc.Addend; + return D->getVirtualAddress() + reloc.Addend; + } + case R_WASM_MEMORY_ADDR_TLS_SLEB: if (isa(sym) || sym->isUndefWeak()) return 0; - return cast(sym)->getVirtualAddress() + reloc.Addend; + // TLS relocations are relative to the start of the TLS output segment + return cast(sym)->getOutputSegmentOffset() + reloc.Addend; case R_WASM_TYPE_INDEX_LEB: return typeMap[reloc.Index]; case R_WASM_FUNCTION_INDEX_LEB: diff --git a/lld/wasm/OutputSections.cpp b/lld/wasm/OutputSections.cpp --- a/lld/wasm/OutputSections.cpp +++ b/lld/wasm/OutputSections.cpp @@ -133,13 +133,20 @@ std::count_if(segments.begin(), segments.end(), [](OutputSegment *segment) { return !segment->isBss; }); +#ifndef NDEBUG + unsigned activeCount = std::count_if( + segments.begin(), segments.end(), [](OutputSegment *segment) { + return (segment->initFlags & WASM_SEGMENT_IS_PASSIVE) == 0; + }); +#endif + + assert((!config->isPic || activeCount <= 1) && + "Currenly only a single data segment is supported in PIC mode"); + writeUleb128(os, segmentCount, "data segment count"); os.flush(); bodySize = dataSectionHeader.size(); - assert((!config->isPic || segments.size() <= 1) && - "Currenly only a single data segment is supported in PIC mode"); - for (OutputSegment *segment : segments) { if (segment->isBss) continue; diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp --- a/lld/wasm/Relocations.cpp +++ b/lld/wasm/Relocations.cpp @@ -9,6 +9,7 @@ #include "Relocations.h" #include "InputChunks.h" +#include "OutputSegment.h" #include "SyntheticSections.h" using namespace llvm; @@ -88,6 +89,16 @@ if (!isa(sym)) addGOTEntry(sym); break; + case R_WASM_MEMORY_ADDR_TLS_SLEB: + if (auto *D = dyn_cast(sym)) { + if (D->segment->outputSeg->name != ".tdata") { + error(toString(file) + ": relocation " + + relocTypeToString(reloc.Type) + " cannot be used against `" + + toString(*sym) + + "` in non-TLS section: " + D->segment->outputSeg->name); + } + } + break; } if (config->isPic) { diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -257,14 +257,8 @@ uint64_t DefinedData::getVirtualAddress() const { LLVM_DEBUG(dbgs() << "getVirtualAddress: " << getName() << "\n"); - if (segment) { - // For thread local data, the symbol location is relative to the start of - // the .tdata section, since they are used as offsets from __tls_base. - // Hence, we do not add in segment->outputSeg->startVA. - if (segment->outputSeg->name == ".tdata") - return segment->outputSegmentOffset + offset; + if (segment) return segment->outputSeg->startVA + segment->outputSegmentOffset + offset; - } return offset; } diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -749,15 +749,15 @@ } static StringRef getOutputDataSegmentName(StringRef name) { - // With PIC code we currently only support a single data segment since - // we only have a single __memory_base to use as our base address. - if (config->isPic) - return ".data"; // We only support one thread-local segment, so we must merge the segments // despite --no-merge-data-segments. // We also need to merge .tbss into .tdata so they share the same offsets. if (name.startswith(".tdata") || name.startswith(".tbss")) return ".tdata"; + // With PIC code we currently only support a single data segment since + // we only have a single __memory_base to use as our base address. + if (config->isPic) + return ".data"; if (!config->mergeDataSegments) return name; if (name.startswith(".text.")) @@ -1199,10 +1199,10 @@ if (!config->relocatable) { // Create linker synthesized functions - if (config->sharedMemory) - createInitMemoryFunction(); if (config->isPic) createApplyRelocationsFunction(); + else if (config->sharedMemory) + createInitMemoryFunction(); createCallCtorsFunction(); // Create export wrappers for commands if needed. diff --git a/llvm/include/llvm/BinaryFormat/WasmRelocs.def b/llvm/include/llvm/BinaryFormat/WasmRelocs.def --- a/llvm/include/llvm/BinaryFormat/WasmRelocs.def +++ b/llvm/include/llvm/BinaryFormat/WasmRelocs.def @@ -23,3 +23,4 @@ WASM_RELOC(R_WASM_TABLE_INDEX_SLEB64, 18) WASM_RELOC(R_WASM_TABLE_INDEX_I64, 19) WASM_RELOC(R_WASM_TABLE_NUMBER_LEB, 20) +WASM_RELOC(R_WASM_MEMORY_ADDR_TLS_SLEB, 21) diff --git a/llvm/include/llvm/MC/MCExpr.h b/llvm/include/llvm/MC/MCExpr.h --- a/llvm/include/llvm/MC/MCExpr.h +++ b/llvm/include/llvm/MC/MCExpr.h @@ -321,8 +321,9 @@ VK_Hexagon_IE_GOT, VK_WASM_TYPEINDEX, // Reference to a symbol's type (signature) - VK_WASM_MBREL, // Memory address relative to memory base - VK_WASM_TBREL, // Table index relative to table bare + VK_WASM_TLSREL, // Memory address relative to __tls_base + VK_WASM_MBREL, // Memory address relative to __memory_base + VK_WASM_TBREL, // Table index relative to __table_base VK_AMDGPU_GOTPCREL32_LO, // symbol@gotpcrel32@lo VK_AMDGPU_GOTPCREL32_HI, // symbol@gotpcrel32@hi diff --git a/llvm/lib/BinaryFormat/Wasm.cpp b/llvm/lib/BinaryFormat/Wasm.cpp --- a/llvm/lib/BinaryFormat/Wasm.cpp +++ b/llvm/lib/BinaryFormat/Wasm.cpp @@ -48,6 +48,7 @@ case R_WASM_MEMORY_ADDR_REL_SLEB64: case R_WASM_MEMORY_ADDR_I32: case R_WASM_MEMORY_ADDR_I64: + case R_WASM_MEMORY_ADDR_TLS_SLEB: case R_WASM_FUNCTION_OFFSET_I32: case R_WASM_SECTION_OFFSET_I32: return true; diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp --- a/llvm/lib/MC/MCExpr.cpp +++ b/llvm/lib/MC/MCExpr.cpp @@ -350,6 +350,7 @@ case VK_Hexagon_IE_GOT: return "IEGOT"; case VK_WASM_TYPEINDEX: return "TYPEINDEX"; case VK_WASM_MBREL: return "MBREL"; + case VK_WASM_TLSREL: return "TLSREL"; case VK_WASM_TBREL: return "TBREL"; case VK_AMDGPU_GOTPCREL32_LO: return "gotpcrel32@lo"; case VK_AMDGPU_GOTPCREL32_HI: return "gotpcrel32@hi"; @@ -490,6 +491,7 @@ .Case("typeindex", VK_WASM_TYPEINDEX) .Case("tbrel", VK_WASM_TBREL) .Case("mbrel", VK_WASM_MBREL) + .Case("tlsrel", VK_WASM_TLSREL) .Case("gotpcrel32@lo", VK_AMDGPU_GOTPCREL32_LO) .Case("gotpcrel32@hi", VK_AMDGPU_GOTPCREL32_HI) .Case("rel32@lo", VK_AMDGPU_REL32_LO) diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp --- a/llvm/lib/MC/WasmObjectWriter.cpp +++ b/llvm/lib/MC/WasmObjectWriter.cpp @@ -576,7 +576,8 @@ case wasm::R_WASM_MEMORY_ADDR_REL_SLEB: case wasm::R_WASM_MEMORY_ADDR_REL_SLEB64: case wasm::R_WASM_MEMORY_ADDR_I32: - case wasm::R_WASM_MEMORY_ADDR_I64: { + case wasm::R_WASM_MEMORY_ADDR_I64: + case wasm::R_WASM_MEMORY_ADDR_TLS_SLEB: { // Provisional value is address of the global plus the offset const MCSymbolWasm *Base = cast(Layout.getBaseSymbol(*RelEntry.Symbol)); @@ -685,6 +686,7 @@ case wasm::R_WASM_TABLE_INDEX_REL_SLEB: case wasm::R_WASM_MEMORY_ADDR_SLEB: case wasm::R_WASM_MEMORY_ADDR_REL_SLEB: + case wasm::R_WASM_MEMORY_ADDR_TLS_SLEB: writePatchableSLEB<5>(Stream, Value, Offset); break; case wasm::R_WASM_TABLE_INDEX_SLEB64: diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp --- a/llvm/lib/Object/WasmObjectFile.cpp +++ b/llvm/lib/Object/WasmObjectFile.cpp @@ -855,6 +855,7 @@ case wasm::R_WASM_MEMORY_ADDR_SLEB: case wasm::R_WASM_MEMORY_ADDR_I32: case wasm::R_WASM_MEMORY_ADDR_REL_SLEB: + case wasm::R_WASM_MEMORY_ADDR_TLS_SLEB: if (!isValidDataSymbol(Reloc.Index)) return make_error("Bad relocation data index", object_error::parse_failed); diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -100,6 +100,11 @@ // Only applicable to data symbols. MO_MEMORY_BASE_REL, + // On a symbol operand this indicates that the immediate is the symbol + // address relative the __tls_base wasm global. + // Only applicable to data symbols. + MO_TLS_BASE_REL, + // On a symbol operand this indicates that the immediate is the symbol // address relative the __table_base wasm global. // Only applicable to function symbols. diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp @@ -76,6 +76,8 @@ case MCSymbolRefExpr::VK_WASM_TBREL: assert(SymA.isFunction()); return wasm::R_WASM_TABLE_INDEX_REL_SLEB; + case MCSymbolRefExpr::VK_WASM_TLSREL: + return wasm::R_WASM_MEMORY_ADDR_TLS_SLEB; case MCSymbolRefExpr::VK_WASM_MBREL: assert(SymA.isData()); return is64Bit() ? wasm::R_WASM_MEMORY_ADDR_REL_SLEB64 diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp @@ -149,7 +149,8 @@ SDValue TLSBaseSym = CurDAG->getTargetExternalSymbol("__tls_base", PtrVT); SDValue TLSOffsetSym = CurDAG->getTargetGlobalAddress( - GA->getGlobal(), DL, PtrVT, GA->getOffset(), 0); + GA->getGlobal(), DL, PtrVT, GA->getOffset(), + WebAssemblyII::MO_TLS_BASE_REL); MachineSDNode *TLSBase = CurDAG->getMachineNode(GlobalGetIns, DL, PtrVT, TLSBaseSym); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h @@ -106,6 +106,7 @@ SDValue LowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const; SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const; SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const; SDValue LowerExternalSymbol(SDValue Op, SelectionDAG &DAG) const; SDValue LowerBR_JT(SDValue Op, SelectionDAG &DAG) const; SDValue LowerJumpTable(SDValue Op, SelectionDAG &DAG) const; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -139,6 +139,9 @@ case WebAssemblyII::MO_MEMORY_BASE_REL: Kind = MCSymbolRefExpr::VK_WASM_MBREL; break; + case WebAssemblyII::MO_TLS_BASE_REL: + Kind = MCSymbolRefExpr::VK_WASM_TLSREL; + break; case WebAssemblyII::MO_TABLE_BASE_REL: Kind = MCSymbolRefExpr::VK_WASM_TBREL; break; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -277,10 +277,9 @@ bool stripThreadLocals(Module &M) { bool Stripped = false; for (auto &GV : M.globals()) { - if (GV.getThreadLocalMode() != - GlobalValue::ThreadLocalMode::NotThreadLocal) { + if (GV.isThreadLocal()) { Stripped = true; - GV.setThreadLocalMode(GlobalValue::ThreadLocalMode::NotThreadLocal); + GV.setThreadLocal(false); } } return Stripped; diff --git a/llvm/test/CodeGen/WebAssembly/tls-general-dynamic.ll b/llvm/test/CodeGen/WebAssembly/tls-general-dynamic.ll --- a/llvm/test/CodeGen/WebAssembly/tls-general-dynamic.ll +++ b/llvm/test/CodeGen/WebAssembly/tls-general-dynamic.ll @@ -12,7 +12,7 @@ ; CHECK-NEXT: .functype address_of_tls () -> (i32) define i32 @address_of_tls() { ; TLS-DAG: global.get __tls_base - ; TLS-DAG: i32.const tls + ; TLS-DAG: i32.const tls@TLSREL ; TLS-NEXT: i32.add ; TLS-NEXT: return @@ -25,7 +25,7 @@ ; CHECK-NEXT: .functype ptr_to_tls () -> (i32) define i32* @ptr_to_tls() { ; TLS-DAG: global.get __tls_base - ; TLS-DAG: i32.const tls + ; TLS-DAG: i32.const tls@TLSREL ; TLS-NEXT: i32.add ; TLS-NEXT: return @@ -38,7 +38,7 @@ ; CHECK-NEXT: .functype tls_load () -> (i32) define i32 @tls_load() { ; TLS-DAG: global.get __tls_base - ; TLS-DAG: i32.const tls + ; TLS-DAG: i32.const tls@TLSREL ; TLS-NEXT: i32.add ; TLS-NEXT: i32.load 0 ; TLS-NEXT: return @@ -54,7 +54,7 @@ ; CHECK-NEXT: .functype tls_store (i32) -> () define void @tls_store(i32 %x) { ; TLS-DAG: global.get __tls_base - ; TLS-DAG: i32.const tls + ; TLS-DAG: i32.const tls@TLSREL ; TLS-NEXT: i32.add ; TLS-NEXT: i32.store 0 ; TLS-NEXT: return diff --git a/llvm/test/CodeGen/WebAssembly/tls-local-exec.ll b/llvm/test/CodeGen/WebAssembly/tls-local-exec.ll --- a/llvm/test/CodeGen/WebAssembly/tls-local-exec.ll +++ b/llvm/test/CodeGen/WebAssembly/tls-local-exec.ll @@ -8,7 +8,7 @@ ; CHECK-NEXT: .functype address_of_tls () -> (i32) define i32 @address_of_tls() { ; TLS-DAG: global.get __tls_base - ; TLS-DAG: i32.const tls + ; TLS-DAG: i32.const tls@TLSREL ; TLS-NEXT: i32.add ; TLS-NEXT: return @@ -21,7 +21,7 @@ ; CHECK-NEXT: .functype ptr_to_tls () -> (i32) define i32* @ptr_to_tls() { ; TLS-DAG: global.get __tls_base - ; TLS-DAG: i32.const tls + ; TLS-DAG: i32.const tls@TLSREL ; TLS-NEXT: i32.add ; TLS-NEXT: return @@ -34,7 +34,7 @@ ; CHECK-NEXT: .functype tls_load () -> (i32) define i32 @tls_load() { ; TLS-DAG: global.get __tls_base - ; TLS-DAG: i32.const tls + ; TLS-DAG: i32.const tls@TLSREL ; TLS-NEXT: i32.add ; TLS-NEXT: i32.load 0 ; TLS-NEXT: return @@ -50,7 +50,7 @@ ; CHECK-NEXT: .functype tls_store (i32) -> () define void @tls_store(i32 %x) { ; TLS-DAG: global.get __tls_base - ; TLS-DAG: i32.const tls + ; TLS-DAG: i32.const tls@TLSREL ; TLS-NEXT: i32.add ; TLS-NEXT: i32.store 0 ; TLS-NEXT: return diff --git a/llvm/test/MC/WebAssembly/tls.s b/llvm/test/MC/WebAssembly/tls.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/WebAssembly/tls.s @@ -0,0 +1,43 @@ +# RUN: llvm-mc -triple=wasm32-unknown-unknown < %s | FileCheck %s +# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj -o %t.o < %s +# RUN: obj2yaml %t.o | FileCheck %s --check-prefix=CHECK-OBJ + +tls_store: + .functype tls_store (i32) -> () + # CHECK: global.get __tls_base + # CHECK-NEXT: i32.const tls@TLSREL + # CHECK-NEXT: i32.add + # CHECK-NEXT: i32.store 0 + global.get __tls_base + i32.const tls@TLSREL + i32.add + i32.store 0 + end_function + + +# CHECK-OBJ: - Type: CODE +# CHECK-OBJ-NEXT: Relocations: +# CHECK-OBJ-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB +# CHECK-OBJ-NEXT: Index: 1 +# CHECK-OBJ-NEXT: Offset: 0x00000004 +# CHECK-OBJ-NEXT: - Type: R_WASM_MEMORY_ADDR_TLS_SLEB +# CHECK-OBJ-NEXT: Index: 2 +# CHECK-OBJ-NEXT: Offset: 0x0000000A + +# CHECK-OBJ: - Type: CUSTOM +# CHECK-OBJ-NEXT: Name: linking +# CHECK-OBJ-NEXT: Version: 2 +# CHECK-OBJ-NEXT: SymbolTable: +# CHECK-OBJ-NEXT: - Index: 0 +# CHECK-OBJ-NEXT: Kind: FUNCTION +# CHECK-OBJ-NEXT: Name: tls_store +# CHECK-OBJ-NEXT: Flags: [ BINDING_LOCAL ] +# CHECK-OBJ-NEXT: Function: 0 +# CHECK-OBJ-NEXT: - Index: 1 +# CHECK-OBJ-NEXT: Kind: DATA +# CHECK-OBJ-NEXT: Name: __tls_base +# CHECK-OBJ-NEXT: Flags: [ UNDEFINED ] +# CHECK-OBJ-NEXT: - Index: 2 +# CHECK-OBJ-NEXT: Kind: DATA +# CHECK-OBJ-NEXT: Name: tls +# CHECK-OBJ-NEXT: Flags: [ UNDEFINED ]