authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-07-16 16:58:47+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-07-19 17:22:46+02:00
log2672f7d9e8bcfc62b16a7073bddf330df4701762
tree022b50ede28e4941fc6c861a2b9f3841043c3853
parent376e1b4603e70f1407862fde0180cfe45ca56e73
signaturelock-open Commit is signed but in an unrecognized format.

wasm-linker: shared-memory fixes

Implements the `start` section which will execute a given function at startup of the program. After function execution, the _start function will be called by the runtime. In the case of shared-memory we set this section to the function `__wasm_init_memory` which will initialize all memory on startup. This also fixes the above mentioned function to ensure we correctly lower the i32 values. Lastly, this fixes a typo where we would retrieve a global, instead of setting its value.

1 files changed, 46 insertions(+), 30 deletions(-)

src/link/Wasm.zig+46-30
...@@ -124,6 +124,8 @@ exports: std.ArrayListUnmanaged(types.Export) = .{},...@@ -124,6 +124,8 @@ exports: std.ArrayListUnmanaged(types.Export) = .{},
124/// List of initialization functions. These must be called in order of priority124/// List of initialization functions. These must be called in order of priority
125/// by the (synthetic) __wasm_call_ctors function.125/// by the (synthetic) __wasm_call_ctors function.
126init_funcs: std.ArrayListUnmanaged(InitFuncLoc) = .{},126init_funcs: std.ArrayListUnmanaged(InitFuncLoc) = .{},
127/// Index to a function defining the entry of the wasm file
128entry: ?u32 = null,
127129
128/// Indirect function table, used to call function pointers130/// Indirect function table, used to call function pointers
129/// When this is non-zero, we must emit a table entry,131/// When this is non-zero, we must emit a table entry,
...@@ -477,7 +479,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -477,7 +479,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
477 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);479 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
478 }480 }
479 {481 {
480 const loc = try wasm_bin.createSyntheticSymbol("__wasm_tls_init", .function);482 const loc = try wasm_bin.createSyntheticSymbol("__wasm_init_tls", .function);
481 const symbol = loc.getSymbol(wasm_bin);483 const symbol = loc.getSymbol(wasm_bin);
482 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);484 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
483 }485 }
...@@ -843,6 +845,12 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {...@@ -843,6 +845,12 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
843 }845 }
844}846}
845847
848/// Writes an unsigned 32-bit integer as a LEB128-encoded 'i32.const' value.
849fn writeI32Const(writer: anytype, val: u32) !void {
850 try writer.writeByte(std.wasm.opcode(.i32_const));
851 try leb.writeILEB128(writer, @as(i32, @bitCast(val)));
852}
853
846fn setupInitMemoryFunction(wasm: *Wasm) !void {854fn setupInitMemoryFunction(wasm: *Wasm) !void {
847 // Passive segments are used to avoid memory being reinitialized on each855 // Passive segments are used to avoid memory being reinitialized on each
848 // thread's instantiation. These passive segments are initialized and856 // thread's instantiation. These passive segments are initialized and
...@@ -880,12 +888,9 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {...@@ -880,12 +888,9 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
880 try writer.writeByte(std.wasm.block_empty); // block type888 try writer.writeByte(std.wasm.block_empty); // block type
881889
882 // atomically check890 // atomically check
883 try writer.writeByte(std.wasm.opcode(.i32_const));891 try writeI32Const(writer, flag_address);
884 try leb.writeULEB128(writer, flag_address);892 try writeI32Const(writer, 0);
885 try writer.writeByte(std.wasm.opcode(.i32_const));893 try writeI32Const(writer, 1);
886 try leb.writeULEB128(writer, @as(u32, 0));
887 try writer.writeByte(std.wasm.opcode(.i32_const));
888 try leb.writeULEB128(writer, @as(u32, 1));
889 try writer.writeByte(std.wasm.opcode(.atomics_prefix));894 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
890 try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.i32_atomic_rmw_cmpxchg));895 try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.i32_atomic_rmw_cmpxchg));
891 try leb.writeULEB128(writer, @as(u32, 2)); // alignment896 try leb.writeULEB128(writer, @as(u32, 2)); // alignment
...@@ -909,24 +914,20 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {...@@ -909,24 +914,20 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
909 // For non-BSS segments we do a memory.init. Both these914 // For non-BSS segments we do a memory.init. Both these
910 // instructions take as their first argument the destination915 // instructions take as their first argument the destination
911 // address.916 // address.
912 try writer.writeByte(std.wasm.opcode(.i32_const));917 try writeI32Const(writer, segment.offset);
913 try leb.writeULEB128(writer, segment.offset);
914918
915 if (wasm.base.options.shared_memory and std.mem.eql(u8, entry.key_ptr.*, ".tdata")) {919 if (wasm.base.options.shared_memory and std.mem.eql(u8, entry.key_ptr.*, ".tdata")) {
916 // When we initialize the TLS segment we also set the `__tls_base`920 // When we initialize the TLS segment we also set the `__tls_base`
917 // global. This allows the runtime to use this static copy of the921 // global. This allows the runtime to use this static copy of the
918 // TLS data for the first/main thread.922 // TLS data for the first/main thread.
919 try writer.writeByte(std.wasm.opcode(.i32_const));923 try writeI32Const(writer, segment.offset);
920 try leb.writeULEB128(writer, segment.offset);
921 try writer.writeByte(std.wasm.opcode(.global_set));924 try writer.writeByte(std.wasm.opcode(.global_set));
922 const loc = wasm.findGlobalSymbol("__tls_base").?;925 const loc = wasm.findGlobalSymbol("__tls_base").?;
923 try leb.writeULEB128(writer, loc.getSymbol(wasm).index);926 try leb.writeULEB128(writer, loc.getSymbol(wasm).index);
924 }927 }
925928
926 try writer.writeByte(std.wasm.opcode(.i32_const));929 try writeI32Const(writer, 0);
927 try leb.writeULEB128(writer, @as(u32, 0));930 try writeI32Const(writer, segment.size);
928 try writer.writeByte(std.wasm.opcode(.i32_const));
929 try leb.writeULEB128(writer, segment.size);
930 try writer.writeByte(std.wasm.opcode(.misc_prefix));931 try writer.writeByte(std.wasm.opcode(.misc_prefix));
931 if (std.mem.eql(u8, entry.key_ptr.*, ".bss")) {932 if (std.mem.eql(u8, entry.key_ptr.*, ".bss")) {
932 // fill bss segment with zeroes933 // fill bss segment with zeroes
...@@ -942,18 +943,15 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {...@@ -942,18 +943,15 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
942943
943 if (wasm.base.options.shared_memory) {944 if (wasm.base.options.shared_memory) {
944 // we set the init memory flag to value '2'945 // we set the init memory flag to value '2'
945 try writer.writeByte(std.wasm.opcode(.i32_const));946 try writeI32Const(writer, flag_address);
946 try leb.writeULEB128(writer, flag_address);947 try writeI32Const(writer, 2);
947 try writer.writeByte(std.wasm.opcode(.i32_const));
948 try leb.writeULEB128(writer, @as(u32, 2));
949 try writer.writeByte(std.wasm.opcode(.atomics_prefix));948 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
950 try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.i32_atomic_store));949 try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.i32_atomic_store));
951 try leb.writeULEB128(writer, @as(u32, 2)); // alignment950 try leb.writeULEB128(writer, @as(u32, 2)); // alignment
952 try leb.writeULEB128(writer, @as(u32, 0)); // offset951 try leb.writeULEB128(writer, @as(u32, 0)); // offset
953952
954 // notify any waiters for segment initialization completion953 // notify any waiters for segment initialization completion
955 try writer.writeByte(std.wasm.opcode(.i32_const));954 try writeI32Const(writer, flag_address);
956 try leb.writeULEB128(writer, flag_address);
957 try writer.writeByte(std.wasm.opcode(.i32_const));955 try writer.writeByte(std.wasm.opcode(.i32_const));
958 try leb.writeILEB128(writer, @as(i32, -1)); // number of waiters956 try leb.writeILEB128(writer, @as(i32, -1)); // number of waiters
959 try writer.writeByte(std.wasm.opcode(.atomics_prefix));957 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
...@@ -968,12 +966,10 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {...@@ -968,12 +966,10 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void {
968966
969 // wait for thread to initialize memory segments967 // wait for thread to initialize memory segments
970 try writer.writeByte(std.wasm.opcode(.end)); // end $wait968 try writer.writeByte(std.wasm.opcode(.end)); // end $wait
971 try writer.writeByte(std.wasm.opcode(.i32_const));969 try writeI32Const(writer, flag_address);
972 try leb.writeULEB128(writer, flag_address);970 try writeI32Const(writer, 1); // expected flag value
973 try writer.writeByte(std.wasm.opcode(.i32_const));971 try writer.writeByte(std.wasm.opcode(.i64_const));
974 try leb.writeULEB128(writer, @as(u32, 1)); // expected flag value972 try leb.writeILEB128(writer, @as(i64, -1)); // timeout
975 try writer.writeByte(std.wasm.opcode(.i32_const));
976 try leb.writeILEB128(writer, @as(i32, -1)); // timeout
977 try writer.writeByte(std.wasm.opcode(.atomics_prefix));973 try writer.writeByte(std.wasm.opcode(.atomics_prefix));
978 try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.memory_atomic_wait32));974 try leb.writeULEB128(writer, std.wasm.atomicsOpcode(.memory_atomic_wait32));
979 try leb.writeULEB128(writer, @as(u32, 2)); // alignment975 try leb.writeULEB128(writer, @as(u32, 2)); // alignment
...@@ -2174,7 +2170,7 @@ fn sortDataSegments(wasm: *Wasm) !void {...@@ -2174,7 +2170,7 @@ fn sortDataSegments(wasm: *Wasm) !void {
21742170
2175 const SortContext = struct {2171 const SortContext = struct {
2176 fn sort(_: void, lhs: []const u8, rhs: []const u8) bool {2172 fn sort(_: void, lhs: []const u8, rhs: []const u8) bool {
2177 return order(lhs) <= order(rhs);2173 return order(lhs) < order(rhs);
2178 }2174 }
21792175
2180 fn order(name: []const u8) u8 {2176 fn order(name: []const u8) u8 {
...@@ -2405,6 +2401,13 @@ pub fn createFunction(...@@ -2405,6 +2401,13 @@ pub fn createFunction(
2405 return loc.index;2401 return loc.index;
2406}2402}
24072403
2404/// If required, sets the function index in the `start` section.
2405fn setupStartSection(wasm: *Wasm) !void {
2406 if (wasm.findGlobalSymbol("__wasm_init_memory")) |loc| {
2407 wasm.entry = loc.getSymbol(wasm).index;
2408 }
2409}
2410
2408fn initializeTLSFunction(wasm: *Wasm) !void {2411fn initializeTLSFunction(wasm: *Wasm) !void {
2409 if (!wasm.base.options.shared_memory) return;2412 if (!wasm.base.options.shared_memory) return;
24102413
...@@ -2426,7 +2429,7 @@ fn initializeTLSFunction(wasm: *Wasm) !void {...@@ -2426,7 +2429,7 @@ fn initializeTLSFunction(wasm: *Wasm) !void {
2426 try leb.writeULEB128(writer, param_local);2429 try leb.writeULEB128(writer, param_local);
24272430
2428 const tls_base_loc = wasm.findGlobalSymbol("__tls_base").?;2431 const tls_base_loc = wasm.findGlobalSymbol("__tls_base").?;
2429 try writer.writeByte(std.wasm.opcode(.global_get));2432 try writer.writeByte(std.wasm.opcode(.global_set));
2430 try leb.writeULEB128(writer, tls_base_loc.getSymbol(wasm).index);2433 try leb.writeULEB128(writer, tls_base_loc.getSymbol(wasm).index);
24312434
2432 // load stack values for the bulk-memory operation2435 // load stack values for the bulk-memory operation
...@@ -3329,6 +3332,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l...@@ -3329,6 +3332,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l
3329 try wasm.setupInitMemoryFunction();3332 try wasm.setupInitMemoryFunction();
3330 try wasm.setupTLSRelocationsFunction();3333 try wasm.setupTLSRelocationsFunction();
3331 try wasm.initializeTLSFunction();3334 try wasm.initializeTLSFunction();
3335 try wasm.setupStartSection();
3332 try wasm.setupExports();3336 try wasm.setupExports();
3333 try wasm.writeToFile(enabled_features, emit_features_count, arena);3337 try wasm.writeToFile(enabled_features, emit_features_count, arena);
33343338
...@@ -3466,6 +3470,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -3466,6 +3470,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
3466 try wasm.setupInitMemoryFunction();3470 try wasm.setupInitMemoryFunction();
3467 try wasm.setupTLSRelocationsFunction();3471 try wasm.setupTLSRelocationsFunction();
3468 try wasm.initializeTLSFunction();3472 try wasm.initializeTLSFunction();
3473 try wasm.setupStartSection();
3469 try wasm.setupExports();3474 try wasm.setupExports();
3470 try wasm.writeToFile(enabled_features, emit_features_count, arena);3475 try wasm.writeToFile(enabled_features, emit_features_count, arena);
3471}3476}
...@@ -3657,6 +3662,17 @@ fn writeToFile(...@@ -3657,6 +3662,17 @@ fn writeToFile(
3657 section_count += 1;3662 section_count += 1;
3658 }3663 }
36593664
3665 if (wasm.entry) |entry_index| {
3666 const header_offset = try reserveVecSectionHeader(&binary_bytes);
3667 try writeVecSectionHeader(
3668 binary_bytes.items,
3669 header_offset,
3670 .start,
3671 @intCast(binary_bytes.items.len - header_offset - header_size),
3672 entry_index,
3673 );
3674 }
3675
3660 // element section (function table)3676 // element section (function table)
3661 if (wasm.function_table.count() > 0) {3677 if (wasm.function_table.count() > 0) {
3662 const header_offset = try reserveVecSectionHeader(&binary_bytes);3678 const header_offset = try reserveVecSectionHeader(&binary_bytes);
...@@ -3690,7 +3706,7 @@ fn writeToFile(...@@ -3690,7 +3706,7 @@ fn writeToFile(
3690 }3706 }
36913707
3692 // When the shared-memory option is enabled, we *must* emit the 'data count' section.3708 // When the shared-memory option is enabled, we *must* emit the 'data count' section.
3693 const data_segments_count = wasm.data_segments.count() - @intFromBool(wasm.data_segments.contains(".bss") and import_memory);3709 const data_segments_count = wasm.data_segments.count() - @intFromBool(wasm.data_segments.contains(".bss") and !import_memory);
3694 if (data_segments_count != 0 and wasm.base.options.shared_memory) {3710 if (data_segments_count != 0 and wasm.base.options.shared_memory) {
3695 const header_offset = try reserveVecSectionHeader(&binary_bytes);3711 const header_offset = try reserveVecSectionHeader(&binary_bytes);
3696 try writeVecSectionHeader(3712 try writeVecSectionHeader(