authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-12-20 16:03:45-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:36-08:00
log694b129d8960a9c3548dccb2a8f0c82f44fbafa9
treec3008dc7568ab989d307c1265ed989c05cb080f3
parentee999d5a14c925b71d4b8c216bf1bd7c1b55e00f

wasm linker: fix data section in flush


2 files changed, 66 insertions(+), 36 deletions(-)

src/link/Wasm.zig+6-1
...@@ -327,6 +327,10 @@ pub const GlobalExport = extern struct {...@@ -327,6 +327,10 @@ pub const GlobalExport = extern struct {
327pub const OutputFunctionIndex = enum(u32) {327pub const OutputFunctionIndex = enum(u32) {
328 _,328 _,
329329
330 pub fn fromResolution(wasm: *const Wasm, resolution: FunctionImport.Resolution) ?OutputFunctionIndex {
331 return fromFunctionIndex(wasm, FunctionIndex.fromResolution(wasm, resolution) orelse return null);
332 }
333
330 pub fn fromFunctionIndex(wasm: *const Wasm, index: FunctionIndex) OutputFunctionIndex {334 pub fn fromFunctionIndex(wasm: *const Wasm, index: FunctionIndex) OutputFunctionIndex {
331 return @enumFromInt(wasm.function_imports.entries.len + @intFromEnum(index));335 return @enumFromInt(wasm.function_imports.entries.len + @intFromEnum(index));
332 }336 }
...@@ -1487,7 +1491,8 @@ pub const DataSegment = extern struct {...@@ -1487,7 +1491,8 @@ pub const DataSegment = extern struct {
1487 }1491 }
14881492
1489 pub fn isPassive(id: Id, wasm: *const Wasm) bool {1493 pub fn isPassive(id: Id, wasm: *const Wasm) bool {
1490 if (wasm.base.comp.config.import_memory and !id.isBss(wasm)) return true;1494 const comp = wasm.base.comp;
1495 if (comp.config.import_memory and !id.isBss(wasm)) return true;
1491 return switch (unpack(id, wasm)) {1496 return switch (unpack(id, wasm)) {
1492 .__zig_error_names, .__zig_error_name_table => false,1497 .__zig_error_names, .__zig_error_name_table => false,
1493 .object => |i| i.ptr(wasm).flags.is_passive,1498 .object => |i| i.ptr(wasm).flags.is_passive,
src/link/Wasm/Flush.zig+60-35
...@@ -21,10 +21,11 @@ const assert = std.debug.assert;...@@ -21,10 +21,11 @@ const assert = std.debug.assert;
2121
22/// Ordered list of data segments that will appear in the final binary.22/// Ordered list of data segments that will appear in the final binary.
23/// When sorted, to-be-merged segments will be made adjacent.23/// When sorted, to-be-merged segments will be made adjacent.
24/// Values are offset relative to segment start.24/// Values are virtual address.
25data_segments: std.AutoArrayHashMapUnmanaged(Wasm.DataSegment.Id, u32) = .empty,25data_segments: std.AutoArrayHashMapUnmanaged(Wasm.DataSegment.Id, u32) = .empty,
26/// Each time a `data_segment` offset equals zero it indicates a new group, and26/// Each time a `data_segment` offset equals zero it indicates a new group, and
27/// the next element in this array will contain the total merged segment size.27/// the next element in this array will contain the total merged segment size.
28/// Value is the virtual memory address of the end of the segment.
28data_segment_groups: std.ArrayListUnmanaged(u32) = .empty,29data_segment_groups: std.ArrayListUnmanaged(u32) = .empty,
2930
30binary_bytes: std.ArrayListUnmanaged(u8) = .empty,31binary_bytes: std.ArrayListUnmanaged(u8) = .empty,
...@@ -71,6 +72,10 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -71,6 +72,10 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
71 };72 };
72 const is_obj = comp.config.output_mode == .Obj;73 const is_obj = comp.config.output_mode == .Obj;
73 const allow_undefined = is_obj or wasm.import_symbols;74 const allow_undefined = is_obj or wasm.import_symbols;
75 //const undef_byte: u8 = switch (comp.root_mod.optimize_mode) {
76 // .Debug, .ReleaseSafe => 0xaa,
77 // .ReleaseFast, .ReleaseSmall => 0x00,
78 //};
7479
75 if (comp.zcu) |zcu| {80 if (comp.zcu) |zcu| {
76 const ip: *const InternPool = &zcu.intern_pool; // No mutations allowed!81 const ip: *const InternPool = &zcu.intern_pool; // No mutations allowed!
...@@ -304,45 +309,46 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -304,45 +309,46 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
304 }309 }
305310
306 const segment_ids = f.data_segments.keys();311 const segment_ids = f.data_segments.keys();
307 const segment_offsets = f.data_segments.values();312 const segment_vaddrs = f.data_segments.values();
308 assert(f.data_segment_groups.items.len == 0);313 assert(f.data_segment_groups.items.len == 0);
314 const data_vaddr: u32 = @intCast(memory_ptr);
309 {315 {
310 var seen_tls: enum { before, during, after } = .before;316 var seen_tls: enum { before, during, after } = .before;
311 var offset: u32 = 0;317 var category: Wasm.DataSegment.Category = undefined;
312 for (segment_ids, segment_offsets, 0..) |segment_id, *segment_offset, i| {318 for (segment_ids, segment_vaddrs, 0..) |segment_id, *segment_vaddr, i| {
313 const alignment = segment_id.alignment(wasm);319 const alignment = segment_id.alignment(wasm);
314 memory_ptr = alignment.forward(memory_ptr);320 category = segment_id.category(wasm);
321 const start_addr = alignment.forward(memory_ptr);
315322
316 const want_new_segment = b: {323 const want_new_segment = b: {
317 if (is_obj) break :b false;324 if (is_obj) break :b false;
318 switch (seen_tls) {325 switch (seen_tls) {
319 .before => if (segment_id.isTls(wasm)) {326 .before => if (category == .tls) {
320 virtual_addrs.tls_base = if (shared_memory) 0 else @intCast(memory_ptr);327 virtual_addrs.tls_base = if (shared_memory) 0 else @intCast(start_addr);
321 virtual_addrs.tls_align = alignment;328 virtual_addrs.tls_align = alignment;
322 seen_tls = .during;329 seen_tls = .during;
323 break :b true;330 break :b f.data_segment_groups.items.len > 0;
324 },331 },
325 .during => if (!segment_id.isTls(wasm)) {332 .during => if (category != .tls) {
326 virtual_addrs.tls_size = @intCast(memory_ptr - virtual_addrs.tls_base.?);333 virtual_addrs.tls_size = @intCast(start_addr - virtual_addrs.tls_base.?);
327 virtual_addrs.tls_align = virtual_addrs.tls_align.maxStrict(alignment);334 virtual_addrs.tls_align = virtual_addrs.tls_align.maxStrict(alignment);
328 seen_tls = .after;335 seen_tls = .after;
329 break :b true;336 break :b true;
330 },337 },
331 .after => {},338 .after => {},
332 }339 }
333 break :b i >= 1 and !wantSegmentMerge(wasm, segment_ids[i - 1], segment_id);340 break :b i >= 1 and !wantSegmentMerge(wasm, segment_ids[i - 1], segment_id, category);
334 };341 };
335 if (want_new_segment) {342 if (want_new_segment) {
336 if (offset > 0) try f.data_segment_groups.append(gpa, offset);343 log.debug("new segment at 0x{x} {} {s} {}", .{ start_addr, segment_id, segment_id.name(wasm), category });
337 offset = 0;344 try f.data_segment_groups.append(gpa, @intCast(memory_ptr));
338 }345 }
339346
340 const size = segment_id.size(wasm);347 const size = segment_id.size(wasm);
341 segment_offset.* = offset;348 segment_vaddr.* = @intCast(start_addr);
342 offset += size;349 memory_ptr = start_addr + size;
343 memory_ptr += size;
344 }350 }
345 if (offset > 0) try f.data_segment_groups.append(gpa, offset);351 if (category != .zero) try f.data_segment_groups.append(gpa, @intCast(memory_ptr));
346 }352 }
347353
348 if (shared_memory and any_passive_inits) {354 if (shared_memory and any_passive_inits) {
...@@ -588,7 +594,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -588,7 +594,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
588 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));594 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));
589 try binary_bytes.appendSlice(gpa, name);595 try binary_bytes.appendSlice(gpa, name);
590 try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.function));596 try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.function));
591 try leb.writeUleb128(binary_writer, @intFromEnum(exp.function_index));597 const func_index = Wasm.OutputFunctionIndex.fromFunctionIndex(wasm, exp.function_index);
598 try leb.writeUleb128(binary_writer, @intFromEnum(func_index));
592 }599 }
593 exports_len += wasm.function_exports.items.len;600 exports_len += wasm.function_exports.items.len;
594601
...@@ -620,9 +627,9 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -620,9 +627,9 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
620 }627 }
621 }628 }
622629
623 if (Wasm.FunctionIndex.fromResolution(wasm, wasm.entry_resolution)) |entry_index| {630 if (Wasm.OutputFunctionIndex.fromResolution(wasm, wasm.entry_resolution)) |func_index| {
624 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);631 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
625 replaceVecSectionHeader(binary_bytes, header_offset, .start, @intFromEnum(entry_index));632 replaceVecSectionHeader(binary_bytes, header_offset, .start, @intFromEnum(func_index));
626 }633 }
627634
628 // element section (function table)635 // element section (function table)
...@@ -720,28 +727,41 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -720,28 +727,41 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
720 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);727 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
721728
722 var group_index: u32 = 0;729 var group_index: u32 = 0;
723 var offset: u32 = undefined;730 var segment_offset: u32 = 0;
724 for (segment_ids, segment_offsets) |segment_id, segment_offset| {731 var group_start_addr: u32 = data_vaddr;
725 if (segment_id.isEmpty(wasm)) {732 var group_end_addr = f.data_segment_groups.items[group_index];
726 // It counted for virtual memory but it does not go into the binary.733 for (segment_ids, segment_vaddrs) |segment_id, segment_vaddr| {
727 continue;734 if (segment_vaddr >= group_end_addr) {
735 try binary_bytes.appendNTimes(gpa, 0, group_end_addr - group_start_addr - segment_offset);
736 group_index += 1;
737 if (group_index >= f.data_segment_groups.items.len) {
738 // All remaining segments are zero.
739 break;
740 }
741 group_start_addr = group_end_addr;
742 group_end_addr = f.data_segment_groups.items[group_index];
743 segment_offset = 0;
728 }744 }
729 if (segment_offset == 0) {745 if (segment_offset == 0) {
730 const group_size = f.data_segment_groups.items[group_index];746 const group_size = group_end_addr - group_start_addr;
731 group_index += 1;747 log.debug("emit data section group, {d} bytes", .{group_size});
732 offset = 0;
733
734 const flags: Object.DataSegmentFlags = if (segment_id.isPassive(wasm)) .passive else .active;748 const flags: Object.DataSegmentFlags = if (segment_id.isPassive(wasm)) .passive else .active;
735 try leb.writeUleb128(binary_writer, @intFromEnum(flags));749 try leb.writeUleb128(binary_writer, @intFromEnum(flags));
736 // when a segment is passive, it's initialized during runtime.750 // Passive segments are initialized at runtime.
737 if (flags != .passive) {751 if (flags != .passive) {
738 try emitInit(binary_writer, .{ .i32_const = @as(i32, @bitCast(segment_offset)) });752 try emitInit(binary_writer, .{ .i32_const = @as(i32, @bitCast(segment_offset)) });
739 }753 }
740 try leb.writeUleb128(binary_writer, group_size);754 try leb.writeUleb128(binary_writer, group_size);
741 }755 }
756 if (segment_id.isEmpty(wasm)) {
757 // It counted for virtual memory but it does not go into the binary.
758 continue;
759 }
742760
743 try binary_bytes.appendNTimes(gpa, 0, segment_offset - offset);761 // Padding for alignment.
744 offset = segment_offset;762 const needed_offset = segment_vaddr - group_start_addr;
763 try binary_bytes.appendNTimes(gpa, 0, needed_offset - segment_offset);
764 segment_offset = needed_offset;
745765
746 const code_start = binary_bytes.items.len;766 const code_start = binary_bytes.items.len;
747 append: {767 append: {
...@@ -768,7 +788,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -768,7 +788,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
768 };788 };
769 try binary_bytes.appendSlice(gpa, code.slice(wasm));789 try binary_bytes.appendSlice(gpa, code.slice(wasm));
770 }790 }
771 offset += @intCast(binary_bytes.items.len - code_start);791 segment_offset += @intCast(binary_bytes.items.len - code_start);
772 }792 }
773 assert(group_index == f.data_segment_groups.items.len);793 assert(group_index == f.data_segment_groups.items.len);
774794
...@@ -1111,12 +1131,17 @@ fn splitSegmentName(name: []const u8) struct { []const u8, []const u8 } {...@@ -1111,12 +1131,17 @@ fn splitSegmentName(name: []const u8) struct { []const u8, []const u8 } {
1111 return .{ name[0..pivot], name[pivot..] };1131 return .{ name[0..pivot], name[pivot..] };
1112}1132}
11131133
1114fn wantSegmentMerge(wasm: *const Wasm, a_id: Wasm.DataSegment.Id, b_id: Wasm.DataSegment.Id) bool {1134fn wantSegmentMerge(
1135 wasm: *const Wasm,
1136 a_id: Wasm.DataSegment.Id,
1137 b_id: Wasm.DataSegment.Id,
1138 b_category: Wasm.DataSegment.Category,
1139) bool {
1115 const a_category = a_id.category(wasm);1140 const a_category = a_id.category(wasm);
1116 const b_category = b_id.category(wasm);
1117 if (a_category != b_category) return false;1141 if (a_category != b_category) return false;
1118 if (a_category == .tls or b_category == .tls) return false;1142 if (a_category == .tls or b_category == .tls) return false;
1119 if (a_id.isPassive(wasm) != b_id.isPassive(wasm)) return false;1143 if (a_id.isPassive(wasm) != b_id.isPassive(wasm)) return false;
1144 if (b_category == .zero) return true;
1120 const a_name = a_id.name(wasm);1145 const a_name = a_id.name(wasm);
1121 const b_name = b_id.name(wasm);1146 const b_name = b_id.name(wasm);
1122 const a_prefix, _ = splitSegmentName(a_name);1147 const a_prefix, _ = splitSegmentName(a_name);