authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-03-14 19:31:45+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-03-18 20:13:29+01:00
log00af3a79aede098c60eeff38ad72fa399b9d3ccf
tree17badac16ab8b35d7f1c6217a4aaf7cadd672ba5
parentfb9d3cd50e9a6d112277d7de158ef857162c01d9
signaturelock-open Commit is signed but in an unrecognized format.

wasm-linker: emit 'data count' & segment flags

When linking with shared-memory enabled, we must ensure to emit the "data count" section as well as emit the correct segment flags to tell the runtime/loader that each segment is passive. This is required as we don't emit the offsets for such segments but instead initialize each segment (for each thread) during runtime.

1 files changed, 49 insertions(+), 5 deletions(-)

src/link/Wasm.zig+49-5
...@@ -180,6 +180,16 @@ pub const Segment = struct {...@@ -180,6 +180,16 @@ pub const Segment = struct {
180 alignment: u32,180 alignment: u32,
181 size: u32,181 size: u32,
182 offset: u32,182 offset: u32,
183 flags: u32,
184
185 pub const Flag = enum(u32) {
186 WASM_DATA_SEGMENT_IS_PASSIVE = 0x01,
187 WASM_DATA_SEGMENT_HAS_MEMINDEX = 0x02,
188 };
189
190 pub fn isPassive(segment: Segment) bool {
191 return segment.flags & @enumToInt(Flag.WASM_DATA_SEGMENT_IS_PASSIVE) != 0;
192 }
183};193};
184194
185pub const Export = struct {195pub const Export = struct {
...@@ -1673,6 +1683,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {...@@ -1673,6 +1683,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
1673 .alignment = atom.alignment,1683 .alignment = atom.alignment,
1674 .size = atom.size,1684 .size = atom.size,
1675 .offset = 0,1685 .offset = 0,
1686 .flags = 0,
1676 });1687 });
1677 }1688 }
16781689
...@@ -1711,10 +1722,15 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {...@@ -1711,10 +1722,15 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
1711 break :result index;1722 break :result index;
1712 } else {1723 } else {
1713 const index = @intCast(u32, wasm.segments.items.len);1724 const index = @intCast(u32, wasm.segments.items.len);
1725 var flags: u32 = 0;
1726 if (wasm.base.options.shared_memory) {
1727 flags |= @enumToInt(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE);
1728 }
1714 try wasm.segments.append(wasm.base.allocator, .{1729 try wasm.segments.append(wasm.base.allocator, .{
1715 .alignment = atom.alignment,1730 .alignment = atom.alignment,
1716 .size = 0,1731 .size = 0,
1717 .offset = 0,1732 .offset = 0,
1733 .flags = flags,
1718 });1734 });
1719 gop.value_ptr.* = index;1735 gop.value_ptr.* = index;
17201736
...@@ -2365,7 +2381,16 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, relocatable_index: u32...@@ -2365,7 +2381,16 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, relocatable_index: u32
2365 const result = try wasm.data_segments.getOrPut(wasm.base.allocator, segment_info.outputName(merge_segment));2381 const result = try wasm.data_segments.getOrPut(wasm.base.allocator, segment_info.outputName(merge_segment));
2366 if (!result.found_existing) {2382 if (!result.found_existing) {
2367 result.value_ptr.* = index;2383 result.value_ptr.* = index;
2368 try wasm.appendDummySegment();2384 var flags: u32 = 0;
2385 if (wasm.base.options.shared_memory) {
2386 flags |= @enumToInt(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE);
2387 }
2388 try wasm.segments.append(wasm.base.allocator, .{
2389 .alignment = 1,
2390 .size = 0,
2391 .offset = 0,
2392 .flags = flags,
2393 });
2369 return index;2394 return index;
2370 } else return result.value_ptr.*;2395 } else return result.value_ptr.*;
2371 },2396 },
...@@ -2439,6 +2464,7 @@ fn appendDummySegment(wasm: *Wasm) !void {...@@ -2439,6 +2464,7 @@ fn appendDummySegment(wasm: *Wasm) !void {
2439 .alignment = 1,2464 .alignment = 1,
2440 .size = 0,2465 .size = 0,
2441 .offset = 0,2466 .offset = 0,
2467 .flags = 0,
2442 });2468 });
2443}2469}
24442470
...@@ -3147,6 +3173,19 @@ fn writeToFile(...@@ -3147,6 +3173,19 @@ fn writeToFile(
3147 section_count += 1;3173 section_count += 1;
3148 }3174 }
31493175
3176 // When the shared-memory option is enabled, we *must* emit the 'data count' section.
3177 const data_segments_count = wasm.data_segments.count() - @boolToInt(wasm.data_segments.contains(".bss") and import_memory);
3178 if (data_segments_count != 0 and wasm.base.options.shared_memory) {
3179 const header_offset = try reserveVecSectionHeader(&binary_bytes);
3180 try writeVecSectionHeader(
3181 binary_bytes.items,
3182 header_offset,
3183 .data_count,
3184 @intCast(u32, binary_bytes.items.len - header_offset - header_size),
3185 @intCast(u32, data_segments_count),
3186 );
3187 }
3188
3150 // Code section3189 // Code section
3151 var code_section_size: u32 = 0;3190 var code_section_size: u32 = 0;
3152 if (wasm.code_section_index) |code_index| {3191 if (wasm.code_section_index) |code_index| {
...@@ -3197,7 +3236,7 @@ fn writeToFile(...@@ -3197,7 +3236,7 @@ fn writeToFile(
3197 }3236 }
31983237
3199 // Data section3238 // Data section
3200 if (wasm.data_segments.count() != 0) {3239 if (data_segments_count != 0) {
3201 const header_offset = try reserveVecSectionHeader(&binary_bytes);3240 const header_offset = try reserveVecSectionHeader(&binary_bytes);
32023241
3203 var it = wasm.data_segments.iterator();3242 var it = wasm.data_segments.iterator();
...@@ -3212,10 +3251,15 @@ fn writeToFile(...@@ -3212,10 +3251,15 @@ fn writeToFile(
3212 segment_count += 1;3251 segment_count += 1;
3213 var atom_index = wasm.atoms.get(segment_index).?;3252 var atom_index = wasm.atoms.get(segment_index).?;
32143253
3215 // flag and index to memory section (currently, there can only be 1 memory section in wasm)3254 try leb.writeULEB128(binary_writer, segment.flags);
3216 try leb.writeULEB128(binary_writer, @as(u32, 0));3255 if (segment.flags & @enumToInt(Wasm.Segment.Flag.WASM_DATA_SEGMENT_HAS_MEMINDEX) != 0) {
3256 try leb.writeULEB128(binary_writer, @as(u32, 0)); // memory is always index 0 as we only have 1 memory entry
3257 }
3258 // when a segment is passive, it's initialized during runtime.
3259 if (!segment.isPassive()) {
3260 try emitInit(binary_writer, .{ .i32_const = @bitCast(i32, segment.offset) });
3261 }
3217 // offset into data section3262 // offset into data section
3218 try emitInit(binary_writer, .{ .i32_const = @bitCast(i32, segment.offset) });
3219 try leb.writeULEB128(binary_writer, segment.size);3263 try leb.writeULEB128(binary_writer, segment.size);
32203264
3221 // fill in the offset table and the data segments3265 // fill in the offset table and the data segments