Skip to content

Commit 9fdc38c

Browse files
authored
[WebAssembly][Object] Support more elem segment flags (#123427)
Some tools (e.g. Rust tooling) produce element segment descriptors with neither elemkind or element type descriptors, but with init exprs instead of func indices (this is with the flags value of 4 in https://webassembly.github.io/spec/core/binary/modules.html#element-section). LLVM doesn't fully model reference types or the various ways to initialize element segments, but we do want to correctly parse and skip over all type sections, so this change updates the object parser to handle that case, and refactors for more clarity. The test file is updated to include one additional elem segment with a flags value of 4, an initializer value of (32.const 0) and an empty vector. Also support parsing files that export imported (undefined) functions.
1 parent 87e4b68 commit 9fdc38c

File tree

7 files changed

+36
-19
lines changed

7 files changed

+36
-19
lines changed

lld/wasm/SyntheticSections.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ void ElemSection::writeBody() {
594594
}
595595
writeInitExpr(os, initExpr);
596596

597-
if (flags & WASM_ELEM_SEGMENT_MASK_HAS_ELEM_KIND) {
597+
if (flags & WASM_ELEM_SEGMENT_MASK_HAS_ELEM_DESC) {
598598
// We only write active function table initializers, for which the elem kind
599599
// is specified to be written as 0x00 and interpreted to mean "funcref".
600600
const uint8_t elemKind = 0;

llvm/include/llvm/BinaryFormat/Wasm.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ enum : unsigned {
170170
WASM_ELEM_SEGMENT_HAS_TABLE_NUMBER = 0x02, // if passive == 0
171171
WASM_ELEM_SEGMENT_HAS_INIT_EXPRS = 0x04,
172172
};
173-
const unsigned WASM_ELEM_SEGMENT_MASK_HAS_ELEM_KIND = 0x3;
173+
const unsigned WASM_ELEM_SEGMENT_MASK_HAS_ELEM_DESC = 0x3;
174174

175175
// Feature policy prefixes used in the custom "target_features" section
176176
enum : uint8_t {
@@ -415,6 +415,10 @@ struct WasmDataSegment {
415415
uint32_t Comdat; // from the "comdat info" section
416416
};
417417

418+
// 3 different element segment modes are encodable. This class is currently
419+
// only used during decoding (see WasmElemSegment below).
420+
enum class ElemSegmentMode { Active, Passive, Declarative };
421+
418422
// Represents a Wasm element segment, with some limitations compared the spec:
419423
// 1) Does not model passive or declarative segments (Segment will end up with
420424
// an Offset field of i32.const 0)

llvm/lib/MC/WasmObjectWriter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1019,7 +1019,7 @@ void WasmObjectWriter::writeElemSection(
10191019
encodeSLEB128(InitialTableOffset, W->OS);
10201020
W->OS << char(wasm::WASM_OPCODE_END);
10211021

1022-
if (Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_KIND) {
1022+
if (Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_DESC) {
10231023
// We only write active function table initializers, for which the elem kind
10241024
// is specified to be written as 0x00 and interpreted to mean "funcref".
10251025
const uint8_t ElemKind = 0;

llvm/lib/Object/WasmObjectFile.cpp

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,15 +1440,20 @@ Error WasmObjectFile::parseExportSection(ReadContext &Ctx) {
14401440
Info.Flags = 0;
14411441
switch (Ex.Kind) {
14421442
case wasm::WASM_EXTERNAL_FUNCTION: {
1443-
if (!isDefinedFunctionIndex(Ex.Index))
1443+
if (!isValidFunctionIndex(Ex.Index))
14441444
return make_error<GenericBinaryError>("invalid function export",
14451445
object_error::parse_failed);
1446-
getDefinedFunction(Ex.Index).ExportName = Ex.Name;
14471446
Info.Kind = wasm::WASM_SYMBOL_TYPE_FUNCTION;
14481447
Info.ElementIndex = Ex.Index;
1449-
unsigned FuncIndex = Info.ElementIndex - NumImportedFunctions;
1450-
wasm::WasmFunction &Function = Functions[FuncIndex];
1451-
Signature = &Signatures[Function.SigIndex];
1448+
if (isDefinedFunctionIndex(Ex.Index)) {
1449+
getDefinedFunction(Ex.Index).ExportName = Ex.Name;
1450+
unsigned FuncIndex = Info.ElementIndex - NumImportedFunctions;
1451+
wasm::WasmFunction &Function = Functions[FuncIndex];
1452+
Signature = &Signatures[Function.SigIndex];
1453+
}
1454+
// Else the function is imported. LLVM object files don't use this
1455+
// pattern and we still treat this as an undefined symbol, but we want to
1456+
// parse it without crashing.
14521457
break;
14531458
}
14541459
case wasm::WASM_EXTERNAL_GLOBAL: {
@@ -1645,17 +1650,25 @@ Error WasmObjectFile::parseElemSection(ReadContext &Ctx) {
16451650
return make_error<GenericBinaryError>(
16461651
"Unsupported flags for element segment", object_error::parse_failed);
16471652

1648-
bool IsPassive = (Segment.Flags & wasm::WASM_ELEM_SEGMENT_IS_PASSIVE) != 0;
1649-
bool IsDeclarative =
1650-
IsPassive && (Segment.Flags & wasm::WASM_ELEM_SEGMENT_IS_DECLARATIVE);
1653+
wasm::ElemSegmentMode Mode;
1654+
if ((Segment.Flags & wasm::WASM_ELEM_SEGMENT_IS_PASSIVE) == 0) {
1655+
Mode = wasm::ElemSegmentMode::Active;
1656+
} else if (Segment.Flags & wasm::WASM_ELEM_SEGMENT_IS_DECLARATIVE) {
1657+
Mode = wasm::ElemSegmentMode::Declarative;
1658+
} else {
1659+
Mode = wasm::ElemSegmentMode::Passive;
1660+
}
16511661
bool HasTableNumber =
1652-
!IsPassive &&
1662+
Mode == wasm::ElemSegmentMode::Active &&
16531663
(Segment.Flags & wasm::WASM_ELEM_SEGMENT_HAS_TABLE_NUMBER);
1664+
bool HasElemKind =
1665+
(Segment.Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_DESC) &&
1666+
!(Segment.Flags & wasm::WASM_ELEM_SEGMENT_HAS_INIT_EXPRS);
1667+
bool HasElemType =
1668+
(Segment.Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_DESC) &&
1669+
(Segment.Flags & wasm::WASM_ELEM_SEGMENT_HAS_INIT_EXPRS);
16541670
bool HasInitExprs =
16551671
(Segment.Flags & wasm::WASM_ELEM_SEGMENT_HAS_INIT_EXPRS);
1656-
bool HasElemKind =
1657-
(Segment.Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_KIND) &&
1658-
!HasInitExprs;
16591672

16601673
if (HasTableNumber)
16611674
Segment.TableNumber = readVaruint32(Ctx);
@@ -1666,7 +1679,7 @@ Error WasmObjectFile::parseElemSection(ReadContext &Ctx) {
16661679
return make_error<GenericBinaryError>("invalid TableNumber",
16671680
object_error::parse_failed);
16681681

1669-
if (IsPassive || IsDeclarative) {
1682+
if (Mode != wasm::ElemSegmentMode::Active) {
16701683
Segment.Offset.Extended = false;
16711684
Segment.Offset.Inst.Opcode = wasm::WASM_OPCODE_I32_CONST;
16721685
Segment.Offset.Inst.Value.Int32 = 0;
@@ -1692,7 +1705,7 @@ Error WasmObjectFile::parseElemSection(ReadContext &Ctx) {
16921705
object_error::parse_failed);
16931706
Segment.ElemKind = wasm::ValType::FUNCREF;
16941707
}
1695-
} else if (HasInitExprs) {
1708+
} else if (HasElemType) {
16961709
auto ElemType = parseValType(Ctx, readVaruint32(Ctx));
16971710
Segment.ElemKind = ElemType;
16981711
} else {

llvm/lib/ObjectYAML/WasmEmitter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ void WasmWriter::writeSectionContent(raw_ostream &OS,
497497

498498
writeInitExpr(OS, Segment.Offset);
499499

500-
if (Segment.Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_KIND) {
500+
if (Segment.Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_DESC) {
501501
// We only support active function table initializers, for which the elem
502502
// kind is specified to be written as 0x00 and interpreted to mean
503503
// "funcref".

llvm/lib/ObjectYAML/WasmYAML.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ void MappingTraits<WasmYAML::ElemSegment>::mapping(
381381
Segment.Flags & wasm::WASM_ELEM_SEGMENT_HAS_TABLE_NUMBER)
382382
IO.mapOptional("TableNumber", Segment.TableNumber);
383383
if (!IO.outputting() ||
384-
Segment.Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_KIND)
384+
Segment.Flags & wasm::WASM_ELEM_SEGMENT_MASK_HAS_ELEM_DESC)
385385
IO.mapOptional("ElemKind", Segment.ElemKind);
386386
// TODO: Omit "offset" for passive segments? It's neither meaningful nor
387387
// encoded.
5 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)