diff --git a/lld/wasm/InputChunks.h b/lld/wasm/InputChunks.h index f1174d937ad3b9..ed86662e75692e 100644 --- a/lld/wasm/InputChunks.h +++ b/lld/wasm/InputChunks.h @@ -81,12 +81,7 @@ class InputChunk { void writeRelocations(llvm::raw_ostream &os) const; void generateRelocationCode(raw_ostream &os) const; - bool isTLS() const { - // Older object files don't include WASM_SEG_FLAG_TLS and instead - // relied on the naming convention. - return flags & llvm::wasm::WASM_SEG_FLAG_TLS || name.startswith(".tdata") || - name.startswith(".tbss"); - } + bool isTLS() const { return flags & llvm::wasm::WASM_SEG_FLAG_TLS; } ObjFile *file; OutputSection *outputSec = nullptr; @@ -113,11 +108,15 @@ class InputChunk { // Signals the chunk was discarded by COMDAT handling. unsigned discarded : 1; + // Signals that the chuck was implicitly marked as TLS based on its name + // alone. This is a compatibility mechanism to support older object files. + unsigned implicitTLS : 1; + protected: InputChunk(ObjFile *f, Kind k, StringRef name, uint32_t alignment = 0, uint32_t flags = 0) : name(name), file(f), alignment(alignment), flags(flags), sectionKind(k), - live(!config->gcSections), discarded(false) {} + live(!config->gcSections), discarded(false), implicitTLS(false) {} ArrayRef data() const { return rawData; } uint64_t getTombstone() const; diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp index 35d8dd1ed7be70..3f87df07a95195 100644 --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -491,7 +491,14 @@ void ObjFile::parse(bool ignoreComdats) { } else seg = make(s, this); seg->discarded = isExcludedByComdat(seg); - + // Older object files did not include WASM_SEG_FLAG_TLS and instead + // relied on the naming convention. To maintain compat with such objects + // we still imply the TLS flag based on the name of the segment. + if (!seg->isTLS() && + (seg->name.startswith(".tdata") || seg->name.startswith(".tbss"))) { + seg->flags |= WASM_SEG_FLAG_TLS; + seg->implicitTLS = true; + } segments.emplace_back(seg); } setRelocs(segments, dataSection); @@ -592,6 +599,9 @@ Symbol *ObjFile::createDefined(const WasmSymbol &sym) { InputChunk *seg = segments[sym.Info.DataRef.Segment]; auto offset = sym.Info.DataRef.Offset; auto size = sym.Info.DataRef.Size; + if (seg->implicitTLS) { + flags |= WASM_SYMBOL_TLS; + } if (sym.isBindingLocal()) return make(name, flags, this, seg, offset, size); if (seg->discarded) diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp index 1dca6d621b2624..06d7f5d6752b52 100644 --- a/lld/wasm/Relocations.cpp +++ b/lld/wasm/Relocations.cpp @@ -120,6 +120,12 @@ void scanRelocations(InputChunk *chunk) { // merged with normal data and allowing TLS relocation in non-TLS // segments. if (config->sharedMemory) { + if (!sym->isTLS()) { + error(toString(file) + ": relocation " + + relocTypeToString(reloc.Type) + + " cannot be used against non-TLS symbol `" + toString(*sym) + + "`"); + } if (auto *D = dyn_cast(sym)) { if (!D->segment->outputSeg->isTLS()) { error(toString(file) + ": relocation " + @@ -133,6 +139,12 @@ void scanRelocations(InputChunk *chunk) { } if (config->isPic) { + if (sym->isTLS() && sym->isUndefined()) { + error(toString(file) + + ": TLS symbol is undefined, but TLS symbols cannot yet be " + "imported: `" + + toString(*sym) + "`"); + } switch (reloc.Type) { case R_WASM_TABLE_INDEX_SLEB: case R_WASM_TABLE_INDEX_SLEB64: @@ -146,15 +158,6 @@ void scanRelocations(InputChunk *chunk) { " cannot be used against symbol " + toString(*sym) + "; recompile with -fPIC"); break; - case R_WASM_MEMORY_ADDR_TLS_SLEB: - case R_WASM_MEMORY_ADDR_TLS_SLEB64: - if (!sym->isDefined()) { - error(toString(file) + - ": TLS symbol is undefined, but TLS symbols cannot yet be " - "imported: `" + - toString(*sym) + "`"); - } - break; case R_WASM_TABLE_INDEX_I32: case R_WASM_TABLE_INDEX_I64: case R_WASM_MEMORY_ADDR_I32: diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp index 09b5badb9c07c3..fd13b70f8c6dac 100644 --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -205,6 +205,8 @@ bool Symbol::isHidden() const { return (flags & WASM_SYMBOL_VISIBILITY_MASK) == WASM_SYMBOL_VISIBILITY_HIDDEN; } +bool Symbol::isTLS() const { return flags & WASM_SYMBOL_TLS; } + void Symbol::setHidden(bool isHidden) { LLVM_DEBUG(dbgs() << "setHidden: " << name << " -> " << isHidden << "\n"); flags &= ~WASM_SYMBOL_VISIBILITY_MASK; diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h index ef2ae7f457af90..415881e0c19823 100644 --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -74,6 +74,7 @@ class Symbol { bool isLocal() const; bool isWeak() const; bool isHidden() const; + bool isTLS() const; // Returns true if this symbol exists in a discarded (due to COMDAT) section bool isDiscarded() const; diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp index 82a2499081fb82..eff5bf5b4213f9 100644 --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -639,7 +639,7 @@ void Writer::calculateExports() { } else if (auto *t = dyn_cast(sym)) { export_ = {name, WASM_EXTERNAL_TAG, t->getTagIndex()}; } else if (auto *d = dyn_cast(sym)) { - if (d->segment && d->segment->isTLS()) { + if (sym->isTLS()) { // We can't currenly export TLS data symbols. if (sym->isExportedExplicit()) error("TLS symbols cannot yet be exported: `" + toString(*sym) + "`"); diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h index c38e64928521f3..1d0eb7009c7cc9 100644 --- a/llvm/include/llvm/BinaryFormat/Wasm.h +++ b/llvm/include/llvm/BinaryFormat/Wasm.h @@ -379,6 +379,7 @@ const unsigned WASM_SYMBOL_UNDEFINED = 0x10; const unsigned WASM_SYMBOL_EXPORTED = 0x20; const unsigned WASM_SYMBOL_EXPLICIT_NAME = 0x40; const unsigned WASM_SYMBOL_NO_STRIP = 0x80; +const unsigned WASM_SYMBOL_TLS = 0x100; #define WASM_RELOC(name, value) name = value, diff --git a/llvm/include/llvm/MC/MCSymbolWasm.h b/llvm/include/llvm/MC/MCSymbolWasm.h index 852ab678e61652..7feffb4a6929fc 100644 --- a/llvm/include/llvm/MC/MCSymbolWasm.h +++ b/llvm/include/llvm/MC/MCSymbolWasm.h @@ -67,6 +67,11 @@ class MCSymbolWasm : public MCSymbol { modifyFlags(wasm::WASM_SYMBOL_NO_STRIP, wasm::WASM_SYMBOL_NO_STRIP); } + bool isTLS() const { return getFlags() & wasm::WASM_SYMBOL_TLS; } + void setTLS() const { + modifyFlags(wasm::WASM_SYMBOL_TLS, wasm::WASM_SYMBOL_TLS); + } + bool isWeak() const { return IsWeak; } void setWeak(bool isWeak) { IsWeak = isWeak; } diff --git a/llvm/include/llvm/MC/MCWasmStreamer.h b/llvm/include/llvm/MC/MCWasmStreamer.h index 6651f071f799dc..818f59e5ab3e57 100644 --- a/llvm/include/llvm/MC/MCWasmStreamer.h +++ b/llvm/include/llvm/MC/MCWasmStreamer.h @@ -41,6 +41,9 @@ class MCWasmStreamer : public MCObjectStreamer { /// @{ void changeSection(MCSection *Section, const MCExpr *Subsection) override; + void emitLabel(MCSymbol *Symbol, SMLoc Loc = SMLoc()) override; + void emitLabelAtPos(MCSymbol *Symbol, SMLoc Loc, MCFragment *F, + uint64_t Offset) override; void emitAssemblerFlag(MCAssemblerFlag Flag) override; void emitThumbFunc(MCSymbol *Func) override; void emitWeakReference(MCSymbol *Alias, const MCSymbol *Symbol) override; @@ -68,6 +71,8 @@ class MCWasmStreamer : public MCObjectStreamer { void emitInstToFragment(const MCInst &Inst, const MCSubtargetInfo &) override; void emitInstToData(const MCInst &Inst, const MCSubtargetInfo &) override; + void fixSymbolsInTLSFixups(const MCExpr *expr); + /// Merge the content of the fragment \p EF into the fragment \p DF. void mergeFragment(MCDataFragment *, MCDataFragment *); diff --git a/llvm/lib/MC/MCWasmStreamer.cpp b/llvm/lib/MC/MCWasmStreamer.cpp index e3d2439cef8171..47951bd5e8726f 100644 --- a/llvm/lib/MC/MCWasmStreamer.cpp +++ b/llvm/lib/MC/MCWasmStreamer.cpp @@ -49,6 +49,27 @@ void MCWasmStreamer::mergeFragment(MCDataFragment *DF, MCDataFragment *EF) { DF->getContents().append(EF->getContents().begin(), EF->getContents().end()); } +void MCWasmStreamer::emitLabel(MCSymbol *S, SMLoc Loc) { + auto *Symbol = cast(S); + MCObjectStreamer::emitLabel(Symbol, Loc); + + const MCSectionWasm &Section = + static_cast(*getCurrentSectionOnly()); + if (Section.getSegmentFlags() & wasm::WASM_SEG_FLAG_TLS) + Symbol->setTLS(); +} + +void MCWasmStreamer::emitLabelAtPos(MCSymbol *S, SMLoc Loc, MCFragment *F, + uint64_t Offset) { + auto *Symbol = cast(S); + MCObjectStreamer::emitLabelAtPos(Symbol, Loc, F, Offset); + + const MCSectionWasm &Section = + static_cast(*getCurrentSectionOnly()); + if (Section.getSegmentFlags() & wasm::WASM_SEG_FLAG_TLS) + Symbol->setTLS(); +} + void MCWasmStreamer::emitAssemblerFlag(MCAssemblerFlag Flag) { // Let the target do whatever target specific stuff it needs to do. getAssembler().getBackend().handleAssemblerFlag(Flag); @@ -117,6 +138,10 @@ bool MCWasmStreamer::emitSymbolAttribute(MCSymbol *S, MCSymbolAttr Attribute) { Symbol->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); break; + case MCSA_ELF_TypeTLS: + Symbol->setTLS(); + break; + case MCSA_ELF_TypeObject: case MCSA_Cold: break; @@ -156,6 +181,10 @@ void MCWasmStreamer::emitIdent(StringRef IdentString) { void MCWasmStreamer::emitInstToFragment(const MCInst &Inst, const MCSubtargetInfo &STI) { this->MCObjectStreamer::emitInstToFragment(Inst, STI); + MCRelaxableFragment &F = *cast(getCurrentFragment()); + + for (auto &Fixup : F.getFixups()) + fixSymbolsInTLSFixups(Fixup.getValue()); } void MCWasmStreamer::emitInstToData(const MCInst &Inst, @@ -166,6 +195,9 @@ void MCWasmStreamer::emitInstToData(const MCInst &Inst, raw_svector_ostream VecOS(Code); Assembler.getEmitter().encodeInstruction(Inst, VecOS, Fixups, STI); + for (auto &Fixup : Fixups) + fixSymbolsInTLSFixups(Fixup.getValue()); + // Append the encoded instruction to the current data fragment (or create a // new such fragment if the current fragment is not a data fragment). MCDataFragment *DF = getOrCreateDataFragment(); @@ -185,16 +217,32 @@ void MCWasmStreamer::finishImpl() { this->MCObjectStreamer::finishImpl(); } -MCStreamer *llvm::createWasmStreamer(MCContext &Context, - std::unique_ptr &&MAB, - std::unique_ptr &&OW, - std::unique_ptr &&CE, - bool RelaxAll) { - MCWasmStreamer *S = - new MCWasmStreamer(Context, std::move(MAB), std::move(OW), std::move(CE)); - if (RelaxAll) - S->getAssembler().setRelaxAll(true); - return S; +void MCWasmStreamer::fixSymbolsInTLSFixups(const MCExpr *expr) { + switch (expr->getKind()) { + case MCExpr::Target: + case MCExpr::Constant: + break; + + case MCExpr::Binary: { + const MCBinaryExpr *be = cast(expr); + fixSymbolsInTLSFixups(be->getLHS()); + fixSymbolsInTLSFixups(be->getRHS()); + break; + } + + case MCExpr::SymbolRef: { + const MCSymbolRefExpr &symRef = *cast(expr); + if (symRef.getKind() == MCSymbolRefExpr::VK_WASM_TLSREL) { + getAssembler().registerSymbol(symRef.getSymbol()); + cast(symRef.getSymbol()).setTLS(); + } + break; + } + + case MCExpr::Unary: + fixSymbolsInTLSFixups(cast(expr)->getSubExpr()); + break; + } } void MCWasmStreamer::emitThumbFunc(MCSymbol *Func) { @@ -215,3 +263,15 @@ void MCWasmStreamer::emitTBSSSymbol(MCSection *Section, MCSymbol *Symbol, uint64_t Size, unsigned ByteAlignment) { llvm_unreachable("Wasm doesn't support this directive"); } + +MCStreamer *llvm::createWasmStreamer(MCContext &Context, + std::unique_ptr &&MAB, + std::unique_ptr &&OW, + std::unique_ptr &&CE, + bool RelaxAll) { + MCWasmStreamer *S = + new MCWasmStreamer(Context, std::move(MAB), std::move(OW), std::move(CE)); + if (RelaxAll) + S->getAssembler().setRelaxAll(true); + return S; +} diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp index 7da5e1534bf153..2d16c3747180fb 100644 --- a/llvm/lib/MC/WasmObjectWriter.cpp +++ b/llvm/lib/MC/WasmObjectWriter.cpp @@ -1748,6 +1748,8 @@ uint64_t WasmObjectWriter::writeOneObject(MCAssembler &Asm, Flags |= wasm::WASM_SYMBOL_EXPLICIT_NAME; if (WS.hasExportName()) Flags |= wasm::WASM_SYMBOL_EXPORTED; + if (WS.isTLS()) + Flags |= wasm::WASM_SYMBOL_TLS; wasm::WasmSymbolInfo Info; Info.Name = WS.getName(); diff --git a/llvm/lib/ObjectYAML/WasmYAML.cpp b/llvm/lib/ObjectYAML/WasmYAML.cpp index 752654ddbbaf14..365f63cce9bc3c 100644 --- a/llvm/lib/ObjectYAML/WasmYAML.cpp +++ b/llvm/lib/ObjectYAML/WasmYAML.cpp @@ -561,6 +561,7 @@ void ScalarBitSetTraits::bitset( BCaseMask(EXPORTED, EXPORTED); BCaseMask(EXPLICIT_NAME, EXPLICIT_NAME); BCaseMask(NO_STRIP, NO_STRIP); + BCaseMask(TLS, TLS); #undef BCaseMask } diff --git a/llvm/test/MC/WebAssembly/tls.s b/llvm/test/MC/WebAssembly/tls.s index 542273c339c30f..fc7c7c1b28b4db 100644 --- a/llvm/test/MC/WebAssembly/tls.s +++ b/llvm/test/MC/WebAssembly/tls.s @@ -18,6 +18,11 @@ tls_store: i32.store 0 end_function +tls_get_undefined: + .functype tls_get_undefined (i32) -> (i32) + i32.const tls_undefined@TLSREL + end_function + .section .tls.foo,"T",@ # CHECK: .tls.foo,"T",@ tls1: @@ -56,13 +61,22 @@ tls2: # CHECK-OBJ-NEXT: - Index: 2 # CHECK-OBJ-NEXT: Kind: DATA # CHECK-OBJ-NEXT: Name: tls1 -# CHECK-OBJ-NEXT: Flags: [ BINDING_LOCAL ] +# CHECK-OBJ-NEXT: Flags: [ BINDING_LOCAL, TLS ] # CHECK-OBJ-NEXT: Segment: 0 # CHECK-OBJ-NEXT: Size: 4 # CHECK-OBJ-NEXT: - Index: 3 +# CHECK-OBJ-NEXT: Kind: FUNCTION +# CHECK-OBJ-NEXT: Name: tls_get_undefined +# CHECK-OBJ-NEXT: Flags: [ BINDING_LOCAL ] +# CHECK-OBJ-NEXT: Function: 1 +# CHECK-OBJ-NEXT: - Index: 4 +# CHECK-OBJ-NEXT: Kind: DATA +# CHECK-OBJ-NEXT: Name: tls_undefined +# CHECK-OBJ-NEXT: Flags: [ UNDEFINED, TLS ] +# CHECK-OBJ-NEXT: - Index: 5 # CHECK-OBJ-NEXT: Kind: DATA # CHECK-OBJ-NEXT: Name: tls2 -# CHECK-OBJ-NEXT: Flags: [ BINDING_LOCAL ] +# CHECK-OBJ-NEXT: Flags: [ BINDING_LOCAL, TLS ] # CHECK-OBJ-NEXT: Segment: 1 # CHECK-OBJ-NEXT: Size: 4 # CHECK-OBJ-NEXT: SegmentInfo: diff --git a/llvm/test/MC/WebAssembly/tls64.s b/llvm/test/MC/WebAssembly/tls64.s index f066fd29424e27..9d6dd5a0f729a6 100644 --- a/llvm/test/MC/WebAssembly/tls64.s +++ b/llvm/test/MC/WebAssembly/tls64.s @@ -56,13 +56,13 @@ tls2: # CHECK-OBJ-NEXT: - Index: 2 # CHECK-OBJ-NEXT: Kind: DATA # CHECK-OBJ-NEXT: Name: tls1 -# CHECK-OBJ-NEXT: Flags: [ BINDING_LOCAL ] +# CHECK-OBJ-NEXT: Flags: [ BINDING_LOCAL, TLS ] # CHECK-OBJ-NEXT: Segment: 0 # CHECK-OBJ-NEXT: Size: 4 # CHECK-OBJ-NEXT: - Index: 3 # CHECK-OBJ-NEXT: Kind: DATA # CHECK-OBJ-NEXT: Name: tls2 -# CHECK-OBJ-NEXT: Flags: [ BINDING_LOCAL ] +# CHECK-OBJ-NEXT: Flags: [ BINDING_LOCAL, TLS ] # CHECK-OBJ-NEXT: Segment: 1 # CHECK-OBJ-NEXT: Size: 4 # CHECK-OBJ-NEXT: SegmentInfo: