[lld] [llvm] [WebAssembly] Implement support for Common symbols (PR #199303)
Derek Schuff via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 20 16:56:02 PDT 2026
https://github.com/dschuff updated https://github.com/llvm/llvm-project/pull/199303
>From a58e6245b69d2c0b0e50bd6f93a692c90162b605 Mon Sep 17 00:00:00 2001
From: Derek Schuff <dschuff at chromium.org>
Date: Thu, 14 May 2026 22:18:59 +0000
Subject: [PATCH 1/5] Initial pass for common symbols
---
lld/test/wasm/common.s | 111 ++++++++++++++++++
lld/wasm/Config.h | 2 +
lld/wasm/InputChunks.cpp | 10 ++
lld/wasm/InputChunks.h | 19 +++
lld/wasm/InputFiles.cpp | 6 +
lld/wasm/SymbolTable.cpp | 50 ++++++++
lld/wasm/SymbolTable.h | 2 +
lld/wasm/Symbols.cpp | 2 +
lld/wasm/Symbols.h | 26 +++-
lld/wasm/Writer.cpp | 60 ++++++++++
llvm/include/llvm/BinaryFormat/Wasm.h | 8 ++
llvm/include/llvm/ObjectYAML/WasmYAML.h | 1 +
llvm/lib/MC/MCWasmStreamer.cpp | 16 ++-
llvm/lib/MC/WasmObjectWriter.cpp | 20 +++-
llvm/lib/Object/WasmObjectFile.cpp | 38 +++---
llvm/lib/ObjectYAML/WasmYAML.cpp | 14 ++-
.../WebAssembly/WebAssemblyAsmPrinter.cpp | 7 --
llvm/test/CodeGen/WebAssembly/common-error.ll | 6 -
llvm/test/CodeGen/WebAssembly/common.ll | 9 ++
llvm/test/MC/WebAssembly/common-error.s | 6 -
llvm/test/MC/WebAssembly/common.s | 31 +++++
llvm/tools/llvm-readobj/WasmDumper.cpp | 12 +-
llvm/tools/obj2yaml/wasm2yaml.cpp | 6 +-
23 files changed, 410 insertions(+), 52 deletions(-)
create mode 100644 lld/test/wasm/common.s
delete mode 100644 llvm/test/CodeGen/WebAssembly/common-error.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/common.ll
delete mode 100644 llvm/test/MC/WebAssembly/common-error.s
create mode 100644 llvm/test/MC/WebAssembly/common.s
diff --git a/lld/test/wasm/common.s b/lld/test/wasm/common.s
new file mode 100644
index 0000000000000..09a1c5d60e14a
--- /dev/null
+++ b/lld/test/wasm/common.s
@@ -0,0 +1,111 @@
+# RUN: split-file %s %t
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t/common1.o %t/common1.s
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t/common2.o %t/common2.s
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t/main.o %t/main.s
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t/strong.o %t/strong.s
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t/weak.o %t/weak.s
+
+# Test 1: Basic common symbol merging (max size and max alignment)
+# RUN: wasm-ld --no-stack-first --global-base=1024 --export=foo --export=bar %t/common1.o %t/common2.o %t/main.o -o %t.wasm
+# RUN: obj2yaml %t.wasm | FileCheck %s
+
+# Test 2: Strong defined symbol takes precedence over common symbol
+# RUN: wasm-ld --no-stack-first --global-base=1024 --export=foo %t/common1.o %t/strong.o %t/main.o -o %t_strong.wasm
+# RUN: obj2yaml %t_strong.wasm | FileCheck %s --check-prefix=STRONG
+
+# Test 3: Common symbol takes precedence over weak defined symbol
+# RUN: wasm-ld --no-stack-first --global-base=1024 --export=foo %t/common1.o %t/weak.o %t/main.o -o %t_weak.wasm
+# RUN: obj2yaml %t_weak.wasm | FileCheck %s --check-prefix=WEAK
+
+#--- common1.s
+.comm foo, 4, 2
+.comm bar, 8, 3
+
+#--- common2.s
+.comm foo, 8, 3
+.comm bar, 4, 2
+
+#--- strong.s
+.globl foo
+.section .data.foo,"",@
+foo:
+ .int32 42
+.size foo, 4
+
+#--- weak.s
+.weak foo
+.section .data.foo,"",@
+foo:
+ .int32 99
+.size foo, 4
+
+#--- main.s
+.globl _start
+_start:
+ .functype _start () -> ()
+ i32.const foo
+ drop
+ i32.const bar
+ drop
+ end_function
+
+# CHECK: - Type: GLOBAL
+# CHECK-NEXT: Globals:
+# CHECK-NEXT: - Index: 0
+# CHECK-NEXT: Type: I32
+# CHECK-NEXT: Mutable: true
+# CHECK-NEXT: InitExpr:
+# CHECK-NEXT: Opcode: I32_CONST
+# CHECK-NEXT: Value: 66576
+# CHECK-NEXT: - Index: 1
+# CHECK-NEXT: Type: I32
+# CHECK-NEXT: Mutable: false
+# CHECK-NEXT: InitExpr:
+# CHECK-NEXT: Opcode: I32_CONST
+# CHECK-NEXT: Value: 1024
+# CHECK-NEXT: - Index: 2
+# CHECK-NEXT: Type: I32
+# CHECK-NEXT: Mutable: false
+# CHECK-NEXT: InitExpr:
+# CHECK-NEXT: Opcode: I32_CONST
+# CHECK-NEXT: Value: 1032
+
+# STRONG: - Type: GLOBAL
+# STRONG-NEXT: Globals:
+# STRONG-NEXT: - Index: 0
+# STRONG-NEXT: Type: I32
+# STRONG-NEXT: Mutable: true
+# STRONG-NEXT: InitExpr:
+# STRONG-NEXT: Opcode: I32_CONST
+# STRONG-NEXT: Value: 66576
+# STRONG-NEXT: - Index: 1
+# STRONG-NEXT: Type: I32
+# STRONG-NEXT: Mutable: false
+# STRONG-NEXT: InitExpr:
+# STRONG-NEXT: Opcode: I32_CONST
+# STRONG-NEXT: Value: 1024
+# STRONG: - Type: DATA
+# STRONG-NEXT: Segments:
+# STRONG-NEXT: - SectionOffset: {{[0-9]+}}
+# STRONG-NEXT: InitFlags: 0
+# STRONG-NEXT: Offset:
+# STRONG-NEXT: Opcode: I32_CONST
+# STRONG-NEXT: Value: 1024
+# STRONG-NEXT: Content: 2A000000
+
+# WEAK-NOT: - SectionOffset: 3
+# WEAK-NOT: Content: '63000000'
+# WEAK: - Type: GLOBAL
+# WEAK-NEXT: Globals:
+# WEAK-NEXT: - Index: 0
+# WEAK-NEXT: Type: I32
+# WEAK-NEXT: Mutable: true
+# WEAK-NEXT: InitExpr:
+# WEAK-NEXT: Opcode: I32_CONST
+# WEAK-NEXT: Value: 66576
+# WEAK-NEXT: - Index: 1
+# WEAK-NEXT: Type: I32
+# WEAK-NEXT: Mutable: false
+# WEAK-NEXT: InitExpr:
+# WEAK-NEXT: Opcode: I32_CONST
+# WEAK-NEXT: Value: 1024
diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h
index 491bf9233b0cf..b2a5febeb2807 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -38,6 +38,7 @@ class DefinedFunction;
class DefinedGlobal;
class UndefinedGlobal;
class TableSymbol;
+class InputChunk;
// For --unresolved-symbols.
enum class UnresolvedPolicy { ReportError, Warn, Ignore, ImportDynamic };
@@ -146,6 +147,7 @@ struct Ctx {
llvm::SmallVector<InputFunction *, 0> syntheticFunctions;
llvm::SmallVector<InputGlobal *, 0> syntheticGlobals;
llvm::SmallVector<InputTable *, 0> syntheticTables;
+ llvm::SmallVector<InputChunk *, 0> syntheticInputSegments;
// linker-generated symbols
struct WasmSym {
diff --git a/lld/wasm/InputChunks.cpp b/lld/wasm/InputChunks.cpp
index 79bac5f444405..e5662822f41b1 100644
--- a/lld/wasm/InputChunks.cpp
+++ b/lld/wasm/InputChunks.cpp
@@ -73,6 +73,9 @@ uint32_t InputChunk::getSize() const {
}
}
+ if (const auto *sis = dyn_cast<SyntheticInputSegment>(this))
+ return sis->getSize();
+
return data().size();
}
@@ -92,6 +95,9 @@ void InputChunk::writeTo(uint8_t *buf) const {
// Apply relocations
ms->relocate(buf + outSecOff);
return;
+ } else if (const auto *sis = dyn_cast<SyntheticInputSegment>(this)) {
+ sis->writeTo(buf);
+ return;
}
// Copy contents
@@ -386,6 +392,10 @@ void InputFunction::writeCompressed(uint8_t *buf) const {
LLVM_DEBUG(dbgs() << " total: " << (buf + chunkSize - orig) << "\n");
}
+void SyntheticInputSegment::writeTo(uint8_t *buf) const {
+ memset(buf + outSecOff, 0, size);
+}
+
uint64_t InputChunk::getChunkOffset(uint64_t offset) const {
if (const auto *ms = dyn_cast<MergeInputChunk>(this)) {
LLVM_DEBUG(dbgs() << "getChunkOffset(merged): " << name << "\n");
diff --git a/lld/wasm/InputChunks.h b/lld/wasm/InputChunks.h
index e772894d95456..314f21eff370b 100644
--- a/lld/wasm/InputChunks.h
+++ b/lld/wasm/InputChunks.h
@@ -45,6 +45,7 @@ class InputChunk {
Function,
SyntheticFunction,
Section,
+ SyntheticDataSegment,
};
StringRef name;
@@ -341,6 +342,24 @@ class SyntheticFunction : public InputFunction {
void setBody(ArrayRef<uint8_t> body) { rawData = body; }
};
+class SyntheticInputSegment : public InputChunk {
+public:
+ SyntheticInputSegment(StringRef name, uint32_t alignment, uint32_t flags)
+ : InputChunk(nullptr, InputChunk::SyntheticDataSegment, name, alignment, flags) {}
+
+ static bool classof(const InputChunk *c) {
+ return c->kind() == SyntheticDataSegment;
+ }
+
+ void setSize(uint64_t s) { size = s; }
+ uint32_t getSize() const { return size; }
+
+ void writeTo(uint8_t *buf) const;
+
+private:
+ uint64_t size = 0;
+};
+
// Represents a single Wasm Section within an input file.
class InputSection : public InputChunk {
public:
diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp
index ad7f207259a5b..694ba5e3506fa 100644
--- a/lld/wasm/InputFiles.cpp
+++ b/lld/wasm/InputFiles.cpp
@@ -675,6 +675,12 @@ Symbol *ObjFile::createDefined(const WasmSymbol &sym) {
return symtab->addDefinedFunction(name, flags, this, func);
}
case WASM_SYMBOL_TYPE_DATA: {
+ if ((flags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_COMMON) {
+ assert(!sym.isBindingLocal());
+ auto size = sym.Info.CommonRef.Size;
+ auto alignment = sym.Info.CommonRef.Alignment;
+ return symtab->addCommon(name, flags, this, size, alignment);
+ }
InputChunk *seg = segments[sym.Info.DataRef.Segment];
auto offset = sym.Info.DataRef.Offset;
auto size = sym.Info.DataRef.Size;
diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index 88ac54302c286..3353b5a76c523 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -335,6 +335,16 @@ static bool shouldReplace(const Symbol *existing, InputFile *newFile,
return true;
}
+ // If existing symbol is common, it can be overridden by a strong definition.
+ if (existing->isCommon()) {
+ if ((newFlags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK) {
+ LLVM_DEBUG(dbgs() << "existing common symbol takes precedence over new weak\n");
+ return false;
+ }
+ LLVM_DEBUG(dbgs() << "replacing existing common symbol with strong definition\n");
+ return true;
+ }
+
// Now we have two defined symbols. If the new one is weak, we can ignore it.
if ((newFlags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK) {
LLVM_DEBUG(dbgs() << "existing symbol takes precedence\n");
@@ -496,6 +506,46 @@ Symbol *SymbolTable::addSharedData(StringRef name, uint32_t flags,
return s;
}
+Symbol *SymbolTable::addCommon(StringRef name, uint32_t flags, InputFile *file,
+ uint64_t size, uint32_t alignment) {
+ LLVM_DEBUG(dbgs() << "addCommon: " << name << " size:" << size
+ << " align:" << alignment << "\n");
+ auto [s, wasInserted] = insert(name, file);
+
+ auto replaceSym = [&]() {
+ replaceSymbol<CommonSymbol>(s, name, flags, file, size, alignment);
+ };
+
+ if (wasInserted || s->isLazy()) {
+ replaceSym();
+ return s;
+ }
+
+ checkDataType(s, file);
+
+ if (auto *existingCommon = dyn_cast<CommonSymbol>(s)) {
+ uint64_t newSize = std::max(existingCommon->getSize(), size);
+ uint32_t newAlign = std::max(existingCommon->getAlignment(), alignment);
+ existingCommon->setCommon(newSize, newAlign);
+ existingCommon->flags |= flags & WASM_SYMBOL_NO_STRIP;
+ return s;
+ }
+
+ if (s->isDefined()) {
+ if (s->isWeak()) {
+ LLVM_DEBUG(dbgs() << "replacing existing weak defined symbol with common\n");
+ replaceSym();
+ } else {
+ LLVM_DEBUG(dbgs() << "existing strong defined symbol takes precedence over common\n");
+ }
+ return s;
+ }
+
+ LLVM_DEBUG(dbgs() << "resolving existing undefined symbol with common\n");
+ replaceSym();
+ return s;
+}
+
Symbol *SymbolTable::addDefinedFunction(StringRef name, uint32_t flags,
InputFile *file,
InputFunction *function) {
diff --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h
index 0667cedecdb4f..007aabdca0f35 100644
--- a/lld/wasm/SymbolTable.h
+++ b/lld/wasm/SymbolTable.h
@@ -55,6 +55,8 @@ class SymbolTable {
Symbol *addSharedData(StringRef name, uint32_t flags, InputFile *file);
Symbol *addSharedTag(StringRef name, uint32_t flags, InputFile *file,
const WasmSignature *sig);
+ Symbol *addCommon(StringRef name, uint32_t flags, InputFile *file,
+ uint64_t size, uint32_t alignment);
Symbol *addDefinedFunction(StringRef name, uint32_t flags, InputFile *file,
InputFunction *function);
Symbol *addDefinedData(StringRef name, uint32_t flags, InputFile *file,
diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp
index 9ce2a9f9f5091..d82e13b8fb2e0 100644
--- a/lld/wasm/Symbols.cpp
+++ b/lld/wasm/Symbols.cpp
@@ -62,6 +62,8 @@ std::string toString(wasm::Symbol::Kind kind) {
return "UndefinedTable";
case wasm::Symbol::UndefinedTagKind:
return "UndefinedTag";
+ case wasm::Symbol::CommonKind:
+ return "CommonSymbol";
case wasm::Symbol::LazyKind:
return "LazyKind";
case wasm::Symbol::SectionKind:
diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h
index 47f1b3ad54ea2..d0df380394fa2 100644
--- a/lld/wasm/Symbols.h
+++ b/lld/wasm/Symbols.h
@@ -59,6 +59,7 @@ class Symbol {
UndefinedGlobalKind,
UndefinedTableKind,
UndefinedTagKind,
+ CommonKind,
LazyKind,
SharedFunctionKind,
SharedDataKind,
@@ -77,6 +78,7 @@ class Symbol {
}
bool isLazy() const { return symbolKind == LazyKind; }
+ bool isCommon() const { return symbolKind == CommonKind; }
bool isShared() const {
return symbolKind == SharedFunctionKind || symbolKind == SharedDataKind ||
symbolKind == SharedTagKind;
@@ -294,7 +296,7 @@ class DataSymbol : public Symbol {
public:
static bool classof(const Symbol *s) {
return s->kind() == DefinedDataKind || s->kind() == UndefinedDataKind ||
- s->kind() == SharedDataKind;
+ s->kind() == SharedDataKind || s->kind() == CommonKind;
}
protected:
@@ -349,6 +351,27 @@ class UndefinedData : public DataSymbol {
}
};
+class CommonSymbol : public DataSymbol {
+public:
+ CommonSymbol(StringRef name, uint32_t flags, InputFile *file,
+ uint64_t size, uint32_t alignment)
+ : DataSymbol(name, CommonKind, flags, file), size(size), alignment(alignment) {}
+
+ static bool classof(const Symbol *s) { return s->kind() == CommonKind; }
+
+ uint64_t getSize() const { return size; }
+ uint32_t getAlignment() const { return alignment; }
+
+ void setCommon(uint64_t s, uint32_t a) {
+ size = s;
+ alignment = a;
+ }
+
+private:
+ uint64_t size;
+ uint32_t alignment;
+};
+
class GlobalSymbol : public Symbol {
public:
static bool classof(const Symbol *s) {
@@ -566,6 +589,7 @@ union SymbolUnion {
alignas(SectionSymbol) char k[sizeof(SectionSymbol)];
alignas(SharedFunctionSymbol) char l[sizeof(SharedFunctionSymbol)];
alignas(SharedTagSymbol) char m[sizeof(SharedTagSymbol)];
+ alignas(CommonSymbol) char n[sizeof(CommonSymbol)];
};
// It is important to keep the size of SymbolUnion small for performance and
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 038a8b3f5417d..191f47590afc0 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -84,6 +84,7 @@ class Writer {
void calculateCustomSections();
void calculateTypes();
void createOutputSegments();
+ void allocateCommonSymbols();
OutputSegment *createOutputSegment(StringRef name);
void combineOutputSegments();
void layoutMemory();
@@ -1046,6 +1047,50 @@ OutputSegment *Writer::createOutputSegment(StringRef name) {
return s;
}
+void Writer::allocateCommonSymbols() {
+ if (ctx.arg.relocatable)
+ return;
+
+ std::vector<CommonSymbol *> commons;
+ for (Symbol *sym : symtab->symbols()) {
+ if (auto *c = dyn_cast<CommonSymbol>(sym)) {
+ if (c->isLive()) {
+ commons.push_back(c);
+ }
+ }
+ }
+
+ if (commons.empty())
+ return;
+
+ log("-- allocateCommonSymbols");
+
+ uint64_t size = 0;
+ uint32_t alignLog2 = 0;
+
+ for (CommonSymbol *c : commons) {
+ alignLog2 = std::max(alignLog2, c->getAlignment());
+ size = alignTo(size, 1ULL << c->getAlignment());
+ size += c->getSize();
+ }
+
+ auto *commonSeg = make<SyntheticInputSegment>(".bss.common", alignLog2, 0);
+ commonSeg->setSize(size);
+ commonSeg->live = true;
+ ctx.syntheticInputSegments.push_back(commonSeg);
+
+ uint64_t offset = 0;
+ for (CommonSymbol *c : commons) {
+ uint64_t size = c->getSize();
+ uint32_t alignLog2 = c->getAlignment();
+ offset = alignTo(offset, 1ULL << alignLog2);
+ log(formatv("allocateCommonSymbol: {0} size={1} align={2} offset={3}",
+ c->getName(), size, alignLog2, offset));
+ replaceSymbol<DefinedData>(c, c->getName(), c->flags, c->getFile(), commonSeg, offset, size);
+ offset += size;
+ }
+}
+
void Writer::createOutputSegments() {
for (ObjFile *file : ctx.objectFiles) {
for (InputChunk *segment : file->segments) {
@@ -1067,6 +1112,18 @@ void Writer::createOutputSegments() {
}
}
+ // Process synthetic segments
+ for (InputChunk *segment : ctx.syntheticInputSegments) {
+ if (!segment->live)
+ continue;
+ StringRef name = getOutputDataSegmentName(*segment);
+ OutputSegment *s = nullptr;
+ if (!segmentMap.contains(name))
+ segmentMap[name] = createOutputSegment(name);
+ s = segmentMap[name];
+ s->addInputSegment(segment);
+ }
+
// Sort segments by type, placing .bss last
llvm::stable_sort(segments,
[](const OutputSegment *a, const OutputSegment *b) {
@@ -1734,6 +1791,9 @@ void Writer::run() {
if (!ctx.isPic && ctx.sym.tableBase)
setGlobalPtr(cast<DefinedGlobal>(ctx.sym.tableBase), ctx.arg.tableBase);
+ log("-- allocateCommonSymbols");
+ allocateCommonSymbols();
+
log("-- createOutputSegments");
createOutputSegments();
log("-- createSyntheticSections");
diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h
index fa71b9b8480f1..1ab9b351a8342 100644
--- a/llvm/include/llvm/BinaryFormat/Wasm.h
+++ b/llvm/include/llvm/BinaryFormat/Wasm.h
@@ -251,6 +251,7 @@ const unsigned WASM_SYMBOL_VISIBILITY_MASK = 0xc;
const unsigned WASM_SYMBOL_BINDING_GLOBAL = 0x0;
const unsigned WASM_SYMBOL_BINDING_WEAK = 0x1;
const unsigned WASM_SYMBOL_BINDING_LOCAL = 0x2;
+const unsigned WASM_SYMBOL_BINDING_COMMON = 0x3;
const unsigned WASM_SYMBOL_VISIBILITY_DEFAULT = 0x0;
const unsigned WASM_SYMBOL_VISIBILITY_HIDDEN = 0x4;
const unsigned WASM_SYMBOL_UNDEFINED = 0x10;
@@ -456,6 +457,11 @@ struct WasmDataReference {
uint64_t Size;
};
+struct WasmCommonReference {
+ uint64_t Size;
+ uint32_t Alignment;
+};
+
struct WasmRelocation {
uint8_t Type; // The type of the relocation.
uint32_t Index; // Index into either symbol or type index space.
@@ -486,6 +492,8 @@ struct WasmSymbolInfo {
uint32_t ElementIndex;
// For a data symbols, the address of the data relative to segment.
WasmDataReference DataRef;
+ // For common symbols, the size and alignment.
+ WasmCommonReference CommonRef;
};
};
diff --git a/llvm/include/llvm/ObjectYAML/WasmYAML.h b/llvm/include/llvm/ObjectYAML/WasmYAML.h
index b0ab04d1d8ac3..f9c678a6a9d07 100644
--- a/llvm/include/llvm/ObjectYAML/WasmYAML.h
+++ b/llvm/include/llvm/ObjectYAML/WasmYAML.h
@@ -166,6 +166,7 @@ struct SymbolInfo {
union {
uint32_t ElementIndex;
wasm::WasmDataReference DataRef;
+ wasm::WasmCommonReference CommonRef;
};
};
diff --git a/llvm/lib/MC/MCWasmStreamer.cpp b/llvm/lib/MC/MCWasmStreamer.cpp
index 8b4ae4cee3e79..73a0369b3993c 100644
--- a/llvm/lib/MC/MCWasmStreamer.cpp
+++ b/llvm/lib/MC/MCWasmStreamer.cpp
@@ -130,9 +130,19 @@ bool MCWasmStreamer::emitSymbolAttribute(MCSymbol *S, MCSymbolAttr Attribute) {
void MCWasmStreamer::emitCommonSymbol(MCSymbol *S, uint64_t Size,
Align ByteAlignment) {
- getContext().reportError(getStartTokLoc(),
- "common symbols are not yet implemented for Wasm: " +
- S->getName());
+ auto *Symbol = static_cast<MCSymbolWasm *>(S);
+ getAssembler().registerSymbol(*Symbol);
+
+ if (!Symbol->isExternal())
+ Symbol->setExternal(true);
+
+ Symbol->setType(wasm::WASM_SYMBOL_TYPE_DATA);
+
+ if (Symbol->declareCommon(Size, ByteAlignment))
+ report_fatal_error(Twine("Symbol: ") + Symbol->getName() +
+ " redeclared as different type");
+
+ Symbol->setSize(MCConstantExpr::create(Size, getContext()));
}
void MCWasmStreamer::emitELFSize(MCSymbol *Symbol, const MCExpr *Value) {
diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp
index 44ca69f0ff302..40be313c218c4 100644
--- a/llvm/lib/MC/WasmObjectWriter.cpp
+++ b/llvm/lib/MC/WasmObjectWriter.cpp
@@ -1161,9 +1161,14 @@ void WasmObjectWriter::writeLinkingMetaDataSection(
case wasm::WASM_SYMBOL_TYPE_DATA:
writeString(Sym.Name);
if ((Sym.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0) {
- encodeULEB128(Sym.DataRef.Segment, W->OS);
- encodeULEB128(Sym.DataRef.Offset, W->OS);
- encodeULEB128(Sym.DataRef.Size, W->OS);
+ if ((Sym.Flags & wasm::WASM_SYMBOL_BINDING_MASK) == wasm::WASM_SYMBOL_BINDING_COMMON) {
+ encodeULEB128(Sym.CommonRef.Size, W->OS);
+ W->OS << char(Sym.CommonRef.Alignment);
+ } else {
+ encodeULEB128(Sym.DataRef.Segment, W->OS);
+ encodeULEB128(Sym.DataRef.Offset, W->OS);
+ encodeULEB128(Sym.DataRef.Size, W->OS);
+ }
}
break;
case wasm::WASM_SYMBOL_TYPE_SECTION: {
@@ -1789,9 +1794,11 @@ uint64_t WasmObjectWriter::writeOneObject(MCAssembler &Asm,
Flags |= wasm::WASM_SYMBOL_BINDING_WEAK;
if (WS.isHidden())
Flags |= wasm::WASM_SYMBOL_VISIBILITY_HIDDEN;
- if (!WS.isExternal() && WS.isDefined())
+ if (WS.isCommon())
+ Flags |= wasm::WASM_SYMBOL_BINDING_COMMON;
+ else if (!WS.isExternal() && WS.isDefined())
Flags |= wasm::WASM_SYMBOL_BINDING_LOCAL;
- if (WS.isUndefined())
+ if (WS.isUndefined() && !WS.isCommon())
Flags |= wasm::WASM_SYMBOL_UNDEFINED;
if (WS.isNoStrip()) {
Flags |= wasm::WASM_SYMBOL_NO_STRIP;
@@ -1813,6 +1820,9 @@ uint64_t WasmObjectWriter::writeOneObject(MCAssembler &Asm,
if (!WS.isData()) {
assert(WasmIndices.contains(&WS));
Info.ElementIndex = WasmIndices.find(&WS)->second;
+ } else if (WS.isCommon()) {
+ Info.CommonRef.Size = WS.getCommonSize();
+ Info.CommonRef.Alignment = Log2(WS.getCommonAlignment().valueOrOne());
} else if (WS.isDefined()) {
assert(DataLocations.contains(&WS));
Info.DataRef = DataLocations.find(&WS)->second;
diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index 98f60bd710c7e..795ac8a7e351f 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -829,23 +829,29 @@ Error WasmObjectFile::parseLinkingSectionSymtab(ReadContext &Ctx) {
case wasm::WASM_SYMBOL_TYPE_DATA:
Info.Name = readString(Ctx);
if (IsDefined) {
- auto Index = readVaruint32(Ctx);
- auto Offset = readVaruint64(Ctx);
- auto Size = readVaruint64(Ctx);
- if (!(Info.Flags & wasm::WASM_SYMBOL_ABSOLUTE)) {
- if (Index >= DataSegments.size())
- return make_error<GenericBinaryError>(
- "invalid data segment index: " + Twine(Index),
- object_error::parse_failed);
- size_t SegmentSize = DataSegments[Index].Data.Content.size();
- if (Offset > SegmentSize)
- return make_error<GenericBinaryError>(
- "invalid data symbol offset: `" + Info.Name +
- "` (offset: " + Twine(Offset) +
- " segment size: " + Twine(SegmentSize) + ")",
- object_error::parse_failed);
+ if ((Info.Flags & wasm::WASM_SYMBOL_BINDING_MASK) == wasm::WASM_SYMBOL_BINDING_COMMON) {
+ auto Size = readVaruint64(Ctx);
+ auto Alignment = readUint8(Ctx);
+ Info.CommonRef = wasm::WasmCommonReference{Size, Alignment};
+ } else {
+ auto Index = readVaruint32(Ctx);
+ auto Offset = readVaruint64(Ctx);
+ auto Size = readVaruint64(Ctx);
+ if (!(Info.Flags & wasm::WASM_SYMBOL_ABSOLUTE)) {
+ if (Index >= DataSegments.size())
+ return make_error<GenericBinaryError>(
+ "invalid data segment index: " + Twine(Index),
+ object_error::parse_failed);
+ size_t SegmentSize = DataSegments[Index].Data.Content.size();
+ if (Offset > SegmentSize)
+ return make_error<GenericBinaryError>(
+ "invalid data symbol offset: `" + Info.Name +
+ "` (offset: " + Twine(Offset) +
+ " segment size: " + Twine(SegmentSize) + ")",
+ object_error::parse_failed);
+ }
+ Info.DataRef = wasm::WasmDataReference{Index, Offset, Size};
}
- Info.DataRef = wasm::WasmDataReference{Index, Offset, Size};
}
break;
diff --git a/llvm/lib/ObjectYAML/WasmYAML.cpp b/llvm/lib/ObjectYAML/WasmYAML.cpp
index edbcb3c6ead11..2e3476b87d4f2 100644
--- a/llvm/lib/ObjectYAML/WasmYAML.cpp
+++ b/llvm/lib/ObjectYAML/WasmYAML.cpp
@@ -523,11 +523,16 @@ void MappingTraits<WasmYAML::SymbolInfo>::mapping(IO &IO,
IO.mapRequired("Tag", Info.ElementIndex);
} else if (Info.Kind == wasm::WASM_SYMBOL_TYPE_DATA) {
if ((Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0) {
- if ((Info.Flags & wasm::WASM_SYMBOL_ABSOLUTE) == 0) {
- IO.mapRequired("Segment", Info.DataRef.Segment);
+ if ((Info.Flags & wasm::WASM_SYMBOL_BINDING_MASK) == wasm::WASM_SYMBOL_BINDING_COMMON) {
+ IO.mapRequired("Size", Info.CommonRef.Size);
+ IO.mapRequired("Align", Info.CommonRef.Alignment);
+ } else {
+ if ((Info.Flags & wasm::WASM_SYMBOL_ABSOLUTE) == 0) {
+ IO.mapRequired("Segment", Info.DataRef.Segment);
+ }
+ IO.mapOptional("Offset", Info.DataRef.Offset, 0u);
+ IO.mapRequired("Size", Info.DataRef.Size);
}
- IO.mapOptional("Offset", Info.DataRef.Offset, 0u);
- IO.mapRequired("Size", Info.DataRef.Size);
}
} else if (Info.Kind == wasm::WASM_SYMBOL_TYPE_SECTION) {
IO.mapRequired("Section", Info.ElementIndex);
@@ -575,6 +580,7 @@ void ScalarBitSetTraits<WasmYAML::SymbolFlags>::bitset(
// BCaseMask(BINDING_MASK, BINDING_GLOBAL);
BCaseMask(BINDING_MASK, BINDING_WEAK);
BCaseMask(BINDING_MASK, BINDING_LOCAL);
+ BCaseMask(BINDING_MASK, BINDING_COMMON);
// BCaseMask(VISIBILITY_MASK, VISIBILITY_DEFAULT);
BCaseMask(VISIBILITY_MASK, VISIBILITY_HIDDEN);
BCaseMask(UNDEFINED, UNDEFINED);
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
index edea99e629407..b32507b7c1a10 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
@@ -180,13 +180,6 @@ MCSymbolWasm *WebAssemblyAsmPrinter::getMCSymbolForFunction(
}
void WebAssemblyAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) {
- if (GV->hasCommonLinkage()) {
- OutContext.reportError(SMLoc(),
- "common symbols are not yet implemented for Wasm: " +
- getSymbol(GV)->getName());
- return;
- }
-
if (!WebAssembly::isWasmVarAddressSpace(GV->getAddressSpace())) {
AsmPrinter::emitGlobalVariable(GV);
return;
diff --git a/llvm/test/CodeGen/WebAssembly/common-error.ll b/llvm/test/CodeGen/WebAssembly/common-error.ll
deleted file mode 100644
index 8fce516fbf88e..0000000000000
--- a/llvm/test/CodeGen/WebAssembly/common-error.ll
+++ /dev/null
@@ -1,6 +0,0 @@
-; RUN: not llc -mtriple=wasm32-unknown-unknown -filetype=asm %s -o - 2>&1 | FileCheck %s
-
-; CHECK: common symbols are not yet implemented for Wasm: x
-; CHECK: common symbols are not yet implemented for Wasm: y
- at x = common global i32 0, align 4
- at y = common global i32 0, align 4
diff --git a/llvm/test/CodeGen/WebAssembly/common.ll b/llvm/test/CodeGen/WebAssembly/common.ll
new file mode 100644
index 0000000000000..ea372465f926b
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/common.ll
@@ -0,0 +1,9 @@
+; RUN: llc -mtriple=wasm32-unknown-unknown -filetype=asm %s -o - | FileCheck %s
+
+; CHECK: .type x, at object
+; CHECK-NEXT: .comm x,4,2
+; CHECK: .type y, at object
+; CHECK-NEXT: .comm y,8,3
+
+ at x = common global i32 0, align 4
+ at y = common global i64 0, align 8
diff --git a/llvm/test/MC/WebAssembly/common-error.s b/llvm/test/MC/WebAssembly/common-error.s
deleted file mode 100644
index 38c8fbca352b2..0000000000000
--- a/llvm/test/MC/WebAssembly/common-error.s
+++ /dev/null
@@ -1,6 +0,0 @@
-# RUN: not llvm-mc -triple=wasm32-unknown-unknown -filetype=obj %s 2>&1 | FileCheck %s
-
-# CHECK:common-error.s:5:9: error: common symbols are not yet implemented for Wasm: x
-# CHECK:common-error.s:6:9: error: common symbols are not yet implemented for Wasm: y
- .comm x,4,4
- .comm y,4,4
diff --git a/llvm/test/MC/WebAssembly/common.s b/llvm/test/MC/WebAssembly/common.s
new file mode 100644
index 0000000000000..9af65dbcc4a84
--- /dev/null
+++ b/llvm/test/MC/WebAssembly/common.s
@@ -0,0 +1,31 @@
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %s -o %t.o
+# RUN: obj2yaml %t.o | FileCheck %s
+
+ .comm x,4,4
+ .comm y,8,8
+
+ .hidden z
+ .comm z,16,16
+
+# CHECK: - Type: CUSTOM
+# CHECK-NEXT: Name: linking
+# CHECK-NEXT: Version: 2
+# CHECK-NEXT: SymbolTable:
+# CHECK-NEXT: - Index: 0
+# CHECK-NEXT: Kind: DATA
+# CHECK-NEXT: Name: x
+# CHECK-NEXT: Flags: [ BINDING_COMMON ]
+# CHECK-NEXT: Size: 4
+# CHECK-NEXT: Align: 4
+# CHECK-NEXT: - Index: 1
+# CHECK-NEXT: Kind: DATA
+# CHECK-NEXT: Name: y
+# CHECK-NEXT: Flags: [ BINDING_COMMON ]
+# CHECK-NEXT: Size: 8
+# CHECK-NEXT: Align: 8
+# CHECK-NEXT: - Index: 2
+# CHECK-NEXT: Kind: DATA
+# CHECK-NEXT: Name: z
+# CHECK-NEXT: Flags: [ BINDING_COMMON, VISIBILITY_HIDDEN ]
+# CHECK-NEXT: Size: 16
+# CHECK-NEXT: Align: 16
diff --git a/llvm/tools/llvm-readobj/WasmDumper.cpp b/llvm/tools/llvm-readobj/WasmDumper.cpp
index 5d7639b0b70fd..f5d9010dc3704 100644
--- a/llvm/tools/llvm-readobj/WasmDumper.cpp
+++ b/llvm/tools/llvm-readobj/WasmDumper.cpp
@@ -45,6 +45,7 @@ const EnumEntry<unsigned> WasmSymbolFlags[] = {
ENUM_ENTRY(BINDING_GLOBAL),
ENUM_ENTRY(BINDING_WEAK),
ENUM_ENTRY(BINDING_LOCAL),
+ ENUM_ENTRY(BINDING_COMMON),
ENUM_ENTRY(VISIBILITY_DEFAULT),
ENUM_ENTRY(VISIBILITY_HIDDEN),
ENUM_ENTRY(UNDEFINED),
@@ -235,9 +236,14 @@ void WasmDumper::printSymbol(const SymbolRef &Sym) {
if (Symbol.Info.Kind != wasm::WASM_SYMBOL_TYPE_DATA) {
W.printHex("ElementIndex", Symbol.Info.ElementIndex);
} else if (!(Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED)) {
- W.printHex("Offset", Symbol.Info.DataRef.Offset);
- W.printHex("Segment", Symbol.Info.DataRef.Segment);
- W.printHex("Size", Symbol.Info.DataRef.Size);
+ if ((Symbol.Info.Flags & wasm::WASM_SYMBOL_BINDING_MASK) == wasm::WASM_SYMBOL_BINDING_COMMON) {
+ W.printHex("Size", Symbol.Info.CommonRef.Size);
+ W.printHex("Alignment", Symbol.Info.CommonRef.Alignment);
+ } else {
+ W.printHex("Offset", Symbol.Info.DataRef.Offset);
+ W.printHex("Segment", Symbol.Info.DataRef.Segment);
+ W.printHex("Size", Symbol.Info.DataRef.Size);
+ }
}
}
diff --git a/llvm/tools/obj2yaml/wasm2yaml.cpp b/llvm/tools/obj2yaml/wasm2yaml.cpp
index 7b504faa5ae4e..f13ba564b79a9 100644
--- a/llvm/tools/obj2yaml/wasm2yaml.cpp
+++ b/llvm/tools/obj2yaml/wasm2yaml.cpp
@@ -135,7 +135,11 @@ WasmDumper::dumpCustomSection(const WasmSection &WasmSec) {
Info.Flags = Symbol.Flags;
switch (Symbol.Kind) {
case wasm::WASM_SYMBOL_TYPE_DATA:
- Info.DataRef = Symbol.DataRef;
+ if ((Symbol.Flags & wasm::WASM_SYMBOL_BINDING_MASK) == wasm::WASM_SYMBOL_BINDING_COMMON) {
+ Info.CommonRef = Symbol.CommonRef;
+ } else {
+ Info.DataRef = Symbol.DataRef;
+ }
break;
case wasm::WASM_SYMBOL_TYPE_FUNCTION:
case wasm::WASM_SYMBOL_TYPE_GLOBAL:
>From 41df2214b19ab6ed62453f7fc95402470db9fdf1 Mon Sep 17 00:00:00 2001
From: Derek Schuff <dschuff at chromium.org>
Date: Fri, 22 May 2026 21:41:51 +0000
Subject: [PATCH 2/5] don't allow common | absolute
---
llvm/lib/Object/WasmObjectFile.cpp | 4 ++++
llvm/test/Object/Wasm/common-absolute.yaml | 21 +++++++++++++++++++++
2 files changed, 25 insertions(+)
create mode 100644 llvm/test/Object/Wasm/common-absolute.yaml
diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index 795ac8a7e351f..02afd5be72373 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -830,6 +830,10 @@ Error WasmObjectFile::parseLinkingSectionSymtab(ReadContext &Ctx) {
Info.Name = readString(Ctx);
if (IsDefined) {
if ((Info.Flags & wasm::WASM_SYMBOL_BINDING_MASK) == wasm::WASM_SYMBOL_BINDING_COMMON) {
+ if (Info.Flags & wasm::WASM_SYMBOL_ABSOLUTE)
+ return make_error<GenericBinaryError>(
+ "common symbols cannot be absolute: " + Info.Name,
+ object_error::parse_failed);
auto Size = readVaruint64(Ctx);
auto Alignment = readUint8(Ctx);
Info.CommonRef = wasm::WasmCommonReference{Size, Alignment};
diff --git a/llvm/test/Object/Wasm/common-absolute.yaml b/llvm/test/Object/Wasm/common-absolute.yaml
new file mode 100644
index 0000000000000..930584aa5dc00
--- /dev/null
+++ b/llvm/test/Object/Wasm/common-absolute.yaml
@@ -0,0 +1,21 @@
+# RUN: yaml2obj %s | not llvm-objdump -s - 2>&1 | FileCheck %s
+
+# Check that a common data symbol cannot be marked as absolute.
+
+# CHECK: common symbols cannot be absolute: foo
+
+--- !WASM
+FileHeader:
+ Version: 0x00000001
+Sections:
+ - Type: CUSTOM
+ Name: linking
+ Version: 2
+ SymbolTable:
+ - Index: 0
+ Kind: DATA
+ Name: foo
+ Flags: [ BINDING_COMMON, ABSOLUTE ]
+ Size: 4
+ Align: 4
+...
>From f99cd8af2712bead048f1e13466ede3b386d77c9 Mon Sep 17 00:00:00 2001
From: Derek Schuff <dschuff at chromium.org>
Date: Sat, 23 May 2026 00:18:09 +0000
Subject: [PATCH 3/5] avoid C++20-ism
---
lld/wasm/SymbolTable.cpp | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index 3353b5a76c523..73a98965bea82 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -510,7 +510,9 @@ Symbol *SymbolTable::addCommon(StringRef name, uint32_t flags, InputFile *file,
uint64_t size, uint32_t alignment) {
LLVM_DEBUG(dbgs() << "addCommon: " << name << " size:" << size
<< " align:" << alignment << "\n");
- auto [s, wasInserted] = insert(name, file);
+ auto val = insert(name, file);
+ Symbol *s = val.first;
+ bool wasInserted = val.second;
auto replaceSym = [&]() {
replaceSymbol<CommonSymbol>(s, name, flags, file, size, alignment);
>From 25acd6fbd7b0984ef7c98fad5c62193b53a43e2c Mon Sep 17 00:00:00 2001
From: Derek Schuff <dschuff at chromium.org>
Date: Sat, 23 May 2026 00:20:59 +0000
Subject: [PATCH 4/5] clang-format
---
lld/wasm/InputChunks.h | 3 ++-
lld/wasm/SymbolTable.cpp | 15 ++++++++++-----
lld/wasm/Symbols.h | 7 ++++---
lld/wasm/Writer.cpp | 3 ++-
llvm/lib/MC/WasmObjectWriter.cpp | 3 ++-
llvm/lib/Object/WasmObjectFile.cpp | 3 ++-
llvm/lib/ObjectYAML/WasmYAML.cpp | 3 ++-
llvm/tools/llvm-readobj/WasmDumper.cpp | 18 +++++++-----------
llvm/tools/obj2yaml/wasm2yaml.cpp | 3 ++-
9 files changed, 33 insertions(+), 25 deletions(-)
diff --git a/lld/wasm/InputChunks.h b/lld/wasm/InputChunks.h
index 314f21eff370b..4a7739bef4539 100644
--- a/lld/wasm/InputChunks.h
+++ b/lld/wasm/InputChunks.h
@@ -345,7 +345,8 @@ class SyntheticFunction : public InputFunction {
class SyntheticInputSegment : public InputChunk {
public:
SyntheticInputSegment(StringRef name, uint32_t alignment, uint32_t flags)
- : InputChunk(nullptr, InputChunk::SyntheticDataSegment, name, alignment, flags) {}
+ : InputChunk(nullptr, InputChunk::SyntheticDataSegment, name, alignment,
+ flags) {}
static bool classof(const InputChunk *c) {
return c->kind() == SyntheticDataSegment;
diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index 73a98965bea82..a178951890d26 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -338,10 +338,12 @@ static bool shouldReplace(const Symbol *existing, InputFile *newFile,
// If existing symbol is common, it can be overridden by a strong definition.
if (existing->isCommon()) {
if ((newFlags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK) {
- LLVM_DEBUG(dbgs() << "existing common symbol takes precedence over new weak\n");
+ LLVM_DEBUG(
+ dbgs() << "existing common symbol takes precedence over new weak\n");
return false;
}
- LLVM_DEBUG(dbgs() << "replacing existing common symbol with strong definition\n");
+ LLVM_DEBUG(
+ dbgs() << "replacing existing common symbol with strong definition\n");
return true;
}
@@ -535,10 +537,13 @@ Symbol *SymbolTable::addCommon(StringRef name, uint32_t flags, InputFile *file,
if (s->isDefined()) {
if (s->isWeak()) {
- LLVM_DEBUG(dbgs() << "replacing existing weak defined symbol with common\n");
- replaceSym();
+ LLVM_DEBUG(
+ dbgs() << "replacing existing weak defined symbol with common\n");
+ replaceSym();
} else {
- LLVM_DEBUG(dbgs() << "existing strong defined symbol takes precedence over common\n");
+ LLVM_DEBUG(
+ dbgs()
+ << "existing strong defined symbol takes precedence over common\n");
}
return s;
}
diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h
index d0df380394fa2..1e5c85bd94954 100644
--- a/lld/wasm/Symbols.h
+++ b/lld/wasm/Symbols.h
@@ -353,9 +353,10 @@ class UndefinedData : public DataSymbol {
class CommonSymbol : public DataSymbol {
public:
- CommonSymbol(StringRef name, uint32_t flags, InputFile *file,
- uint64_t size, uint32_t alignment)
- : DataSymbol(name, CommonKind, flags, file), size(size), alignment(alignment) {}
+ CommonSymbol(StringRef name, uint32_t flags, InputFile *file, uint64_t size,
+ uint32_t alignment)
+ : DataSymbol(name, CommonKind, flags, file), size(size),
+ alignment(alignment) {}
static bool classof(const Symbol *s) { return s->kind() == CommonKind; }
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 191f47590afc0..71b3a902e368c 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -1086,7 +1086,8 @@ void Writer::allocateCommonSymbols() {
offset = alignTo(offset, 1ULL << alignLog2);
log(formatv("allocateCommonSymbol: {0} size={1} align={2} offset={3}",
c->getName(), size, alignLog2, offset));
- replaceSymbol<DefinedData>(c, c->getName(), c->flags, c->getFile(), commonSeg, offset, size);
+ replaceSymbol<DefinedData>(c, c->getName(), c->flags, c->getFile(),
+ commonSeg, offset, size);
offset += size;
}
}
diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp
index 40be313c218c4..2c780576142a5 100644
--- a/llvm/lib/MC/WasmObjectWriter.cpp
+++ b/llvm/lib/MC/WasmObjectWriter.cpp
@@ -1161,7 +1161,8 @@ void WasmObjectWriter::writeLinkingMetaDataSection(
case wasm::WASM_SYMBOL_TYPE_DATA:
writeString(Sym.Name);
if ((Sym.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0) {
- if ((Sym.Flags & wasm::WASM_SYMBOL_BINDING_MASK) == wasm::WASM_SYMBOL_BINDING_COMMON) {
+ if ((Sym.Flags & wasm::WASM_SYMBOL_BINDING_MASK) ==
+ wasm::WASM_SYMBOL_BINDING_COMMON) {
encodeULEB128(Sym.CommonRef.Size, W->OS);
W->OS << char(Sym.CommonRef.Alignment);
} else {
diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp
index 02afd5be72373..6ad9eca63a358 100644
--- a/llvm/lib/Object/WasmObjectFile.cpp
+++ b/llvm/lib/Object/WasmObjectFile.cpp
@@ -829,7 +829,8 @@ Error WasmObjectFile::parseLinkingSectionSymtab(ReadContext &Ctx) {
case wasm::WASM_SYMBOL_TYPE_DATA:
Info.Name = readString(Ctx);
if (IsDefined) {
- if ((Info.Flags & wasm::WASM_SYMBOL_BINDING_MASK) == wasm::WASM_SYMBOL_BINDING_COMMON) {
+ if ((Info.Flags & wasm::WASM_SYMBOL_BINDING_MASK) ==
+ wasm::WASM_SYMBOL_BINDING_COMMON) {
if (Info.Flags & wasm::WASM_SYMBOL_ABSOLUTE)
return make_error<GenericBinaryError>(
"common symbols cannot be absolute: " + Info.Name,
diff --git a/llvm/lib/ObjectYAML/WasmYAML.cpp b/llvm/lib/ObjectYAML/WasmYAML.cpp
index 2e3476b87d4f2..8c93d3a0763f7 100644
--- a/llvm/lib/ObjectYAML/WasmYAML.cpp
+++ b/llvm/lib/ObjectYAML/WasmYAML.cpp
@@ -523,7 +523,8 @@ void MappingTraits<WasmYAML::SymbolInfo>::mapping(IO &IO,
IO.mapRequired("Tag", Info.ElementIndex);
} else if (Info.Kind == wasm::WASM_SYMBOL_TYPE_DATA) {
if ((Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0) {
- if ((Info.Flags & wasm::WASM_SYMBOL_BINDING_MASK) == wasm::WASM_SYMBOL_BINDING_COMMON) {
+ if ((Info.Flags & wasm::WASM_SYMBOL_BINDING_MASK) ==
+ wasm::WASM_SYMBOL_BINDING_COMMON) {
IO.mapRequired("Size", Info.CommonRef.Size);
IO.mapRequired("Align", Info.CommonRef.Alignment);
} else {
diff --git a/llvm/tools/llvm-readobj/WasmDumper.cpp b/llvm/tools/llvm-readobj/WasmDumper.cpp
index f5d9010dc3704..b8f37c7f5f2d1 100644
--- a/llvm/tools/llvm-readobj/WasmDumper.cpp
+++ b/llvm/tools/llvm-readobj/WasmDumper.cpp
@@ -42,16 +42,11 @@ const EnumEntry<uint32_t> WasmSectionTypes[] = {
const EnumEntry<unsigned> WasmSymbolFlags[] = {
#define ENUM_ENTRY(X) \
{ #X, wasm::WASM_SYMBOL_##X }
- ENUM_ENTRY(BINDING_GLOBAL),
- ENUM_ENTRY(BINDING_WEAK),
- ENUM_ENTRY(BINDING_LOCAL),
- ENUM_ENTRY(BINDING_COMMON),
- ENUM_ENTRY(VISIBILITY_DEFAULT),
- ENUM_ENTRY(VISIBILITY_HIDDEN),
- ENUM_ENTRY(UNDEFINED),
- ENUM_ENTRY(EXPORTED),
- ENUM_ENTRY(EXPLICIT_NAME),
- ENUM_ENTRY(NO_STRIP),
+ ENUM_ENTRY(BINDING_GLOBAL), ENUM_ENTRY(BINDING_WEAK),
+ ENUM_ENTRY(BINDING_LOCAL), ENUM_ENTRY(BINDING_COMMON),
+ ENUM_ENTRY(VISIBILITY_DEFAULT), ENUM_ENTRY(VISIBILITY_HIDDEN),
+ ENUM_ENTRY(UNDEFINED), ENUM_ENTRY(EXPORTED),
+ ENUM_ENTRY(EXPLICIT_NAME), ENUM_ENTRY(NO_STRIP),
#undef ENUM_ENTRY
};
@@ -236,7 +231,8 @@ void WasmDumper::printSymbol(const SymbolRef &Sym) {
if (Symbol.Info.Kind != wasm::WASM_SYMBOL_TYPE_DATA) {
W.printHex("ElementIndex", Symbol.Info.ElementIndex);
} else if (!(Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED)) {
- if ((Symbol.Info.Flags & wasm::WASM_SYMBOL_BINDING_MASK) == wasm::WASM_SYMBOL_BINDING_COMMON) {
+ if ((Symbol.Info.Flags & wasm::WASM_SYMBOL_BINDING_MASK) ==
+ wasm::WASM_SYMBOL_BINDING_COMMON) {
W.printHex("Size", Symbol.Info.CommonRef.Size);
W.printHex("Alignment", Symbol.Info.CommonRef.Alignment);
} else {
diff --git a/llvm/tools/obj2yaml/wasm2yaml.cpp b/llvm/tools/obj2yaml/wasm2yaml.cpp
index f13ba564b79a9..2db5387b1ec6c 100644
--- a/llvm/tools/obj2yaml/wasm2yaml.cpp
+++ b/llvm/tools/obj2yaml/wasm2yaml.cpp
@@ -135,7 +135,8 @@ WasmDumper::dumpCustomSection(const WasmSection &WasmSec) {
Info.Flags = Symbol.Flags;
switch (Symbol.Kind) {
case wasm::WASM_SYMBOL_TYPE_DATA:
- if ((Symbol.Flags & wasm::WASM_SYMBOL_BINDING_MASK) == wasm::WASM_SYMBOL_BINDING_COMMON) {
+ if ((Symbol.Flags & wasm::WASM_SYMBOL_BINDING_MASK) ==
+ wasm::WASM_SYMBOL_BINDING_COMMON) {
Info.CommonRef = Symbol.CommonRef;
} else {
Info.DataRef = Symbol.DataRef;
>From 71bb22cc8978a709e2373982fdc0e358ab0c8754 Mon Sep 17 00:00:00 2001
From: Derek Schuff <dschuff at chromium.org>
Date: Fri, 19 Jun 2026 00:36:49 +0000
Subject: [PATCH 5/5] Fix generating relocatable output with common symbols.
also fix after symbol GC change upstream
---
lld/test/wasm/common.s | 21 +++++++++++++++++++++
lld/wasm/SyntheticSections.cpp | 3 +++
2 files changed, 24 insertions(+)
diff --git a/lld/test/wasm/common.s b/lld/test/wasm/common.s
index 09a1c5d60e14a..3e4c5009a8fcc 100644
--- a/lld/test/wasm/common.s
+++ b/lld/test/wasm/common.s
@@ -17,6 +17,10 @@
# RUN: wasm-ld --no-stack-first --global-base=1024 --export=foo %t/common1.o %t/weak.o %t/main.o -o %t_weak.wasm
# RUN: obj2yaml %t_weak.wasm | FileCheck %s --check-prefix=WEAK
+# Test 4: Relocatable link preserves common symbols (merged)
+# RUN: wasm-ld -r %t/common1.o %t/common2.o -o %t/common_reloc.o
+# RUN: obj2yaml %t/common_reloc.o | FileCheck %s --check-prefix=RELOC
+
#--- common1.s
.comm foo, 4, 2
.comm bar, 8, 3
@@ -40,9 +44,12 @@ foo:
.size foo, 4
#--- main.s
+.globaltype __stack_pointer, i32
.globl _start
_start:
.functype _start () -> ()
+ global.get __stack_pointer
+ drop
i32.const foo
drop
i32.const bar
@@ -109,3 +116,17 @@ _start:
# WEAK-NEXT: InitExpr:
# WEAK-NEXT: Opcode: I32_CONST
# WEAK-NEXT: Value: 1024
+
+# RELOC: SymbolTable:
+# RELOC-NEXT: - Index: 0
+# RELOC-NEXT: Kind: DATA
+# RELOC-NEXT: Name: foo
+# RELOC-NEXT: Flags: [ BINDING_COMMON ]
+# RELOC-NEXT: Size: 8
+# RELOC-NEXT: Align: 3
+# RELOC-NEXT: - Index: 1
+# RELOC-NEXT: Kind: DATA
+# RELOC-NEXT: Name: bar
+# RELOC-NEXT: Flags: [ BINDING_COMMON ]
+# RELOC-NEXT: Size: 8
+# RELOC-NEXT: Align: 3
diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp
index d1a01c7ec3f9d..30caae73a75a9 100644
--- a/lld/wasm/SyntheticSections.cpp
+++ b/lld/wasm/SyntheticSections.cpp
@@ -732,6 +732,9 @@ void LinkingSection::writeBody() {
writeUleb128(sub.os, dataSym->getVA(), "data offset");
}
writeUleb128(sub.os, dataSym->getSize(), "data size");
+ } else if (auto *commonSym = dyn_cast<CommonSymbol>(sym)) {
+ writeUleb128(sub.os, commonSym->getSize(), "common size");
+ writeU8(sub.os, commonSym->getAlignment(), "common alignment");
}
} else {
auto *s = cast<OutputSectionSymbol>(sym);
More information about the llvm-commits
mailing list