diff --git a/lld/test/wasm/tls-export.s b/lld/test/wasm/tls-export.s new file mode 100644 index 00000000000000..b8a36aa55aa1bd --- /dev/null +++ b/lld/test/wasm/tls-export.s @@ -0,0 +1,26 @@ +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s +# RUN: wasm-ld -no-gc-sections --shared-memory --no-entry -o %t.wasm %t.o +# RUN: not wasm-ld --shared-memory --no-entry --export=tls1 -o %t.wasm %t.o 2>&1 | FileCheck %s +# With --export-all we ignore TLS symbols so we don't expect an error here +# RUN: wasm-ld --shared-memory --no-entry --export-all -o %t.wasm %t.o + +# CHECK: error: TLS symbols cannot yet be exported: `tls1` + +.section .tdata.tls1,"",@ +.globl tls1 +.p2align 2 +tls1: + .int32 1 + .size tls1, 4 + +.section .custom_section.target_features,"",@ + .int8 3 + .int8 43 + .int8 7 + .ascii "atomics" + .int8 43 + .int8 11 + .ascii "bulk-memory" + .int8 43 + .int8 15 + .ascii "mutable-globals" diff --git a/lld/wasm/InputChunks.h b/lld/wasm/InputChunks.h index fe30fea62d1cc2..f17751b312931b 100644 --- a/lld/wasm/InputChunks.h +++ b/lld/wasm/InputChunks.h @@ -117,6 +117,9 @@ class InputSegment : public InputChunk { const OutputSegment *outputSeg = nullptr; uint32_t outputSegmentOffset = 0; uint32_t alignment = 0; + bool isTLS() { + return getName().startswith(".tdata") || getName().startswith(".tbss"); + } protected: ArrayRef data() const override { return segment.Data.Content; } diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp index 889280a71c1ad9..3b1dccbe466709 100644 --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -247,7 +247,7 @@ uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc, uint64_t tombstone, // 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") + if (D->segment && D->segment->outputSeg->isTLS()) return D->getOutputSegmentOffset() + reloc.Addend; uint64_t value = D->getVA(reloc.Addend); diff --git a/lld/wasm/OutputSegment.h b/lld/wasm/OutputSegment.h index 30553b9883e228..c9d58cf231b800 100644 --- a/lld/wasm/OutputSegment.h +++ b/lld/wasm/OutputSegment.h @@ -32,6 +32,8 @@ class OutputSegment { size += inSeg->getSize(); } + bool isTLS() const { return name == ".tdata"; } + StringRef name; bool isBss = false; uint32_t index = 0; diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp index 18eb522ebe0b79..f909c377b9a54f 100644 --- a/lld/wasm/Relocations.cpp +++ b/lld/wasm/Relocations.cpp @@ -115,7 +115,7 @@ void scanRelocations(InputChunk *chunk) { break; case R_WASM_MEMORY_ADDR_TLS_SLEB: if (auto *D = dyn_cast(sym)) { - if (D->segment->outputSeg->name != ".tdata") { + if (!D->segment->outputSeg->isTLS()) { error(toString(file) + ": relocation " + relocTypeToString(reloc.Type) + " cannot be used against `" + toString(*sym) + diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp index 2bb89ae8ea5fed..66f276ba10fc79 100644 --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -207,13 +207,14 @@ bool Symbol::isExported() const { if (!isDefined() || isLocal()) return false; - if (forceExport || config->exportAll) + if (config->exportAll || (config->exportDynamic && !isHidden())) return true; - if (config->exportDynamic && !isHidden()) - return true; + return isExportedExplicit(); +} - return flags & WASM_SYMBOL_EXPORTED; +bool Symbol::isExportedExplicit() const { + return forceExport || flags & WASM_SYMBOL_EXPORTED; } bool Symbol::isNoStrip() const { diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h index e2a594ff0bef33..e6968d45261a5c 100644 --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -110,6 +110,7 @@ class Symbol { WasmSymbolType getWasmType() const; bool isExported() const; + bool isExportedExplicit() const; // Indicates that the symbol is used in an __attribute__((used)) directive // or similar. diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp index dbe947daf781c3..7598d6f360d4ac 100644 --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -270,7 +270,7 @@ void Writer::layoutMemory() { log(formatv("mem: {0,-15} offset={1,-8} size={2,-8} align={3}", seg->name, memoryPtr, seg->size, seg->alignment)); - if (!config->relocatable && seg->name == ".tdata") { + if (!config->relocatable && seg->isTLS()) { if (config->sharedMemory) { auto *tlsSize = cast(WasmSym::tlsSize); setGlobalPtr(tlsSize, seg->size); @@ -631,6 +631,12 @@ void Writer::calculateExports() { } else if (auto *e = dyn_cast(sym)) { export_ = {name, WASM_EXTERNAL_EVENT, e->getEventIndex()}; } else if (auto *d = dyn_cast(sym)) { + if (d->segment && d->segment->isTLS()) { + // We can't currenly export TLS data symbols. + if (sym->isExportedExplicit()) + error("TLS symbols cannot yet be exported: `" + toString(*sym) + "`"); + continue; + } out.globalSec->dataAddressGlobals.push_back(d); export_ = {name, WASM_EXTERNAL_GLOBAL, globalIndex++}; } else { @@ -900,7 +906,7 @@ void Writer::combineOutputSegments() { OutputSegment *combined = nullptr; std::vector new_segments; for (OutputSegment *s : segments) { - if (s->name == ".tdata") { + if (s->isTLS()) { new_segments.push_back(s); } else { if (!combined) { @@ -946,7 +952,7 @@ static void createFunction(DefinedFunction *func, StringRef bodyContent) { bool Writer::needsPassiveInitialization(const OutputSegment *segment) { return segment->initFlags & WASM_DATA_SEGMENT_IS_PASSIVE && - segment->name != ".tdata" && !segment->isBss; + !segment->isTLS() && !segment->isBss; } bool Writer::hasPassiveInitializedSegments() {