[WebAssembly] Add explict TLS symbol flag

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
This commit is contained in:
Sam Clegg 2021-09-08 04:53:13 -04:00
parent 3765d284c4
commit 44177e5fb2
14 changed files with 135 additions and 32 deletions

View File

@ -81,12 +81,7 @@ public:
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 @@ public:
// 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;

View File

@ -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);
@ -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)

View File

@ -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 " +
@ -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:

View File

@ -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;

View File

@ -74,6 +74,7 @@ public:
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;

View File

@ -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) + "`");

View File

@ -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,

View File

@ -67,6 +67,11 @@ public:
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; }

View File

@ -41,6 +41,9 @@ public:
/// @{
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 @@ private:
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 *);

View File

@ -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);
@ -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<MCRelaxableFragment>(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<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) {
@ -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;
}

View File

@ -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();

View File

@ -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
}

View File

@ -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:

View File

@ -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: