Skip to content

Commit

Permalink
[WebAssembly] Add explict TLS symbol flag
Browse files Browse the repository at this point in the history
As before we maintain backwards compat with older object files
by also infering the TLS flag based on the name of the segment.

This change is was split out from https://reviews.llvm.org/D108877.

Differential Revision: https://reviews.llvm.org/D109426
  • Loading branch information
sbc100 committed Sep 9, 2021
1 parent 3765d28 commit 44177e5
Show file tree
Hide file tree
Showing 14 changed files with 135 additions and 32 deletions.
13 changes: 6 additions & 7 deletions lld/wasm/InputChunks.h
Expand Up @@ -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;
Expand All @@ -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<uint8_t> data() const { return rawData; }
uint64_t getTombstone() const;

Expand Down
12 changes: 11 additions & 1 deletion lld/wasm/InputFiles.cpp
Expand Up @@ -491,7 +491,14 @@ void ObjFile::parse(bool ignoreComdats) {
} else
seg = make<InputSegment>(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);
Expand Down Expand Up @@ -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<DefinedData>(name, flags, this, seg, offset, size);
if (seg->discarded)
Expand Down
21 changes: 12 additions & 9 deletions lld/wasm/Relocations.cpp
Expand Up @@ -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<DefinedData>(sym)) {
if (!D->segment->outputSeg->isTLS()) {
error(toString(file) + ": relocation " +
Expand All @@ -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:
Expand All @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions lld/wasm/Symbols.cpp
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions lld/wasm/Symbols.h
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion lld/wasm/Writer.cpp
Expand Up @@ -639,7 +639,7 @@ void Writer::calculateExports() {
} else if (auto *t = dyn_cast<DefinedTag>(sym)) {
export_ = {name, WASM_EXTERNAL_TAG, t->getTagIndex()};
} else if (auto *d = dyn_cast<DefinedData>(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) + "`");
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/BinaryFormat/Wasm.h
Expand Up @@ -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,

Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/MC/MCSymbolWasm.h
Expand Up @@ -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; }

Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/MC/MCWasmStreamer.h
Expand Up @@ -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;
Expand Down Expand Up @@ -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 *);

Expand Down
80 changes: 70 additions & 10 deletions llvm/lib/MC/MCWasmStreamer.cpp
Expand Up @@ -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<MCSymbolWasm>(S);
MCObjectStreamer::emitLabel(Symbol, Loc);

const MCSectionWasm &Section =
static_cast<const MCSectionWasm &>(*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<MCSymbolWasm>(S);
MCObjectStreamer::emitLabelAtPos(Symbol, Loc, F, Offset);

const MCSectionWasm &Section =
static_cast<const MCSectionWasm &>(*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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<MCRelaxableFragment>(getCurrentFragment());

for (auto &Fixup : F.getFixups())
fixSymbolsInTLSFixups(Fixup.getValue());
}

void MCWasmStreamer::emitInstToData(const MCInst &Inst,
Expand All @@ -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();
Expand All @@ -185,16 +217,32 @@ void MCWasmStreamer::finishImpl() {
this->MCObjectStreamer::finishImpl();
}

MCStreamer *llvm::createWasmStreamer(MCContext &Context,
std::unique_ptr<MCAsmBackend> &&MAB,
std::unique_ptr<MCObjectWriter> &&OW,
std::unique_ptr<MCCodeEmitter> &&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<MCBinaryExpr>(expr);
fixSymbolsInTLSFixups(be->getLHS());
fixSymbolsInTLSFixups(be->getRHS());
break;
}

case MCExpr::SymbolRef: {
const MCSymbolRefExpr &symRef = *cast<MCSymbolRefExpr>(expr);
if (symRef.getKind() == MCSymbolRefExpr::VK_WASM_TLSREL) {
getAssembler().registerSymbol(symRef.getSymbol());
cast<MCSymbolWasm>(symRef.getSymbol()).setTLS();
}
break;
}

case MCExpr::Unary:
fixSymbolsInTLSFixups(cast<MCUnaryExpr>(expr)->getSubExpr());
break;
}
}

void MCWasmStreamer::emitThumbFunc(MCSymbol *Func) {
Expand All @@ -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<MCAsmBackend> &&MAB,
std::unique_ptr<MCObjectWriter> &&OW,
std::unique_ptr<MCCodeEmitter> &&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;
}
2 changes: 2 additions & 0 deletions llvm/lib/MC/WasmObjectWriter.cpp
Expand Up @@ -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();
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/ObjectYAML/WasmYAML.cpp
Expand Up @@ -561,6 +561,7 @@ void ScalarBitSetTraits<WasmYAML::SymbolFlags>::bitset(
BCaseMask(EXPORTED, EXPORTED);
BCaseMask(EXPLICIT_NAME, EXPLICIT_NAME);
BCaseMask(NO_STRIP, NO_STRIP);
BCaseMask(TLS, TLS);
#undef BCaseMask
}

Expand Down
18 changes: 16 additions & 2 deletions llvm/test/MC/WebAssembly/tls.s
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/MC/WebAssembly/tls64.s
Expand Up @@ -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:
Expand Down

0 comments on commit 44177e5

Please sign in to comment.