authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-07-18 19:55:20+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-07-19 17:22:46+02:00
log142dbc7b82c741692dd17f8c0455203826342bba
tree89899879d0bc08f077e3f0d2a4de1164a414eaa0
parent1a3304ed236b60cd31c790bae929a78c58c9d33e
signaturelock-open Commit is signed but in an unrecognized format.

wasm-linker: create TLS Wasm globals correctly

Previously, they were only created when we had any TLS segment. This meant that while the symbol existed, the global itself wouldn't. The result of this was a crash during symbol names writing as it would attempt to write the symbol name of a global that didn't exist. Now we always create them, and instead update its `init` value during `setupMemory`. In the future, the entire symbol (and global) will be removed by the garbage collector.

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

src/link/Wasm.zig+23-17
...@@ -410,7 +410,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -410,7 +410,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
410 },410 },
411 );411 );
412 } else {412 } else {
413 symbol.index = @as(u32, @intCast(wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len));413 symbol.index = @intCast(wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len);
414 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);414 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
415 const global = try wasm_bin.wasm_globals.addOne(allocator);415 const global = try wasm_bin.wasm_globals.addOne(allocator);
416 global.* = .{416 global.* = .{
...@@ -433,7 +433,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -433,7 +433,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
433 };433 };
434 if (options.output_mode == .Obj or options.import_table) {434 if (options.output_mode == .Obj or options.import_table) {
435 symbol.setUndefined(true);435 symbol.setUndefined(true);
436 symbol.index = @as(u32, @intCast(wasm_bin.imported_tables_count));436 symbol.index = @intCast(wasm_bin.imported_tables_count);
437 wasm_bin.imported_tables_count += 1;437 wasm_bin.imported_tables_count += 1;
438 try wasm_bin.imports.put(allocator, loc, .{438 try wasm_bin.imports.put(allocator, loc, .{
439 .module_name = try wasm_bin.string_table.put(allocator, wasm_bin.host_name),439 .module_name = try wasm_bin.string_table.put(allocator, wasm_bin.host_name),
...@@ -467,16 +467,31 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -467,16 +467,31 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
467 const loc = try wasm_bin.createSyntheticSymbol("__tls_base", .global);467 const loc = try wasm_bin.createSyntheticSymbol("__tls_base", .global);
468 const symbol = loc.getSymbol(wasm_bin);468 const symbol = loc.getSymbol(wasm_bin);
469 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);469 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
470 symbol.index = @intCast(wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len);
471 try wasm_bin.wasm_globals.append(wasm_bin.base.allocator, .{
472 .global_type = .{ .valtype = .i32, .mutable = true },
473 .init = .{ .i32_const = undefined },
474 });
470 }475 }
471 {476 {
472 const loc = try wasm_bin.createSyntheticSymbol("__tls_size", .global);477 const loc = try wasm_bin.createSyntheticSymbol("__tls_size", .global);
473 const symbol = loc.getSymbol(wasm_bin);478 const symbol = loc.getSymbol(wasm_bin);
474 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);479 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
480 symbol.index = @intCast(wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len);
481 try wasm_bin.wasm_globals.append(wasm_bin.base.allocator, .{
482 .global_type = .{ .valtype = .i32, .mutable = false },
483 .init = .{ .i32_const = undefined },
484 });
475 }485 }
476 {486 {
477 const loc = try wasm_bin.createSyntheticSymbol("__tls_align", .global);487 const loc = try wasm_bin.createSyntheticSymbol("__tls_align", .global);
478 const symbol = loc.getSymbol(wasm_bin);488 const symbol = loc.getSymbol(wasm_bin);
479 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);489 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
490 symbol.index = @intCast(wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len);
491 try wasm_bin.wasm_globals.append(wasm_bin.base.allocator, .{
492 .global_type = .{ .valtype = .i32, .mutable = false },
493 .init = .{ .i32_const = undefined },
494 });
480 }495 }
481 {496 {
482 const loc = try wasm_bin.createSyntheticSymbol("__wasm_init_tls", .function);497 const loc = try wasm_bin.createSyntheticSymbol("__wasm_init_tls", .function);
...@@ -2757,27 +2772,18 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2757,27 +2772,18 @@ fn setupMemory(wasm: *Wasm) !void {
2757 if (mem.eql(u8, entry.key_ptr.*, ".tdata")) {2772 if (mem.eql(u8, entry.key_ptr.*, ".tdata")) {
2758 if (wasm.findGlobalSymbol("__tls_size")) |loc| {2773 if (wasm.findGlobalSymbol("__tls_size")) |loc| {
2759 const sym = loc.getSymbol(wasm);2774 const sym = loc.getSymbol(wasm);
2760 sym.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count;2775 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.size);
2761 try wasm.wasm_globals.append(wasm.base.allocator, .{
2762 .global_type = .{ .valtype = .i32, .mutable = false },
2763 .init = .{ .i32_const = @as(i32, @intCast(segment.size)) },
2764 });
2765 }2776 }
2766 if (wasm.findGlobalSymbol("__tls_align")) |loc| {2777 if (wasm.findGlobalSymbol("__tls_align")) |loc| {
2767 const sym = loc.getSymbol(wasm);2778 const sym = loc.getSymbol(wasm);
2768 sym.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count;2779 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.alignment);
2769 try wasm.wasm_globals.append(wasm.base.allocator, .{
2770 .global_type = .{ .valtype = .i32, .mutable = false },
2771 .init = .{ .i32_const = @as(i32, @intCast(segment.alignment)) },
2772 });
2773 }2780 }
2774 if (wasm.findGlobalSymbol("__tls_base")) |loc| {2781 if (wasm.findGlobalSymbol("__tls_base")) |loc| {
2775 const sym = loc.getSymbol(wasm);2782 const sym = loc.getSymbol(wasm);
2776 sym.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count;2783 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = if (wasm.base.options.shared_memory)
2777 try wasm.wasm_globals.append(wasm.base.allocator, .{2784 @as(i32, 0)
2778 .global_type = .{ .valtype = .i32, .mutable = wasm.base.options.shared_memory },2785 else
2779 .init = .{ .i32_const = if (wasm.base.options.shared_memory) @as(u32, 0) else @as(i32, @intCast(memory_ptr)) },2786 @as(i32, @intCast(memory_ptr));
2780 });
2781 }2787 }
2782 }2788 }
27832789
test/link/wasm/shared-memory/build.zig+40-19
...@@ -5,11 +5,9 @@ pub fn build(b: *std.Build) void {...@@ -5,11 +5,9 @@ pub fn build(b: *std.Build) void {
5 b.default_step = test_step;5 b.default_step = test_step;
66
7 add(b, test_step, .Debug);7 add(b, test_step, .Debug);
88 add(b, test_step, .ReleaseFast);
9 // Enable the following build modes once garbage-collection is implemented properly.9 add(b, test_step, .ReleaseSmall);
10 // add(b, test_step, .ReleaseFast);10 add(b, test_step, .ReleaseSafe);
11 // add(b, test_step, .ReleaseSmall);
12 // add(b, test_step, .ReleaseSafe);
13}11}
1412
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode) void {13fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode) void {
...@@ -47,30 +45,53 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt...@@ -47,30 +45,53 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt
4745
48 // This section *must* be emit as the start function is set to the index46 // This section *must* be emit as the start function is set to the index
49 // of __wasm_init_memory47 // of __wasm_init_memory
50 check_lib.checkStart("Section start");48 // release modes will have the TLS segment optimized out in our test-case.
49 // This means we won't have __wasm_init_memory in such case, and therefore
50 // should also not have a section "start"
51 if (optimize_mode == .Debug) {
52 check_lib.checkStart("Section start");
53 }
5154
52 // This section is only and *must* be emit when shared-memory is enabled55 // This section is only and *must* be emit when shared-memory is enabled
53 check_lib.checkStart("Section data_count");56 // release modes will have the TLS segment optimized out in our test-case.
54 check_lib.checkNext("count 3");57 if (optimize_mode == .Debug) {
58 check_lib.checkStart("Section data_count");
59 check_lib.checkNext("count 3");
60 }
5561
56 check_lib.checkStart("Section custom");62 check_lib.checkStart("Section custom");
57 check_lib.checkNext("name name");63 check_lib.checkNext("name name");
58 check_lib.checkNext("type function");64 check_lib.checkNext("type function");
59 check_lib.checkNext("name __wasm_init_memory");65 if (optimize_mode == .Debug) {
66 check_lib.checkNext("name __wasm_init_memory");
67 }
60 check_lib.checkNext("name __wasm_init_tls");68 check_lib.checkNext("name __wasm_init_tls");
61 check_lib.checkNext("type global");69 check_lib.checkNext("type global");
62 check_lib.checkNext("name __tls_size");70
63 check_lib.checkNext("name __tls_align");71 // In debug mode the symbol __tls_base is resolved to an undefined symbol
64 check_lib.checkNext("name __tls_base");72 // from the object file, hence its placement differs than in release modes
73 // where the entire tls segment is optimized away, and tls_base will have
74 // its original position.
75 if (optimize_mode == .Debug) {
76 check_lib.checkNext("name __tls_size");
77 check_lib.checkNext("name __tls_align");
78 check_lib.checkNext("name __tls_base");
79 } else {
80 check_lib.checkNext("name __tls_base");
81 check_lib.checkNext("name __tls_size");
82 check_lib.checkNext("name __tls_align");
83 }
6584
66 check_lib.checkNext("type data_segment");85 check_lib.checkNext("type data_segment");
67 check_lib.checkNext("names 3");86 if (optimize_mode == .Debug) {
68 check_lib.checkNext("index 0");87 check_lib.checkNext("names 3");
69 check_lib.checkNext("name .rodata");88 check_lib.checkNext("index 0");
70 check_lib.checkNext("index 1");89 check_lib.checkNext("name .rodata");
71 check_lib.checkNext("name .bss");90 check_lib.checkNext("index 1");
72 check_lib.checkNext("index 2");91 check_lib.checkNext("name .bss");
73 check_lib.checkNext("name .tdata");92 check_lib.checkNext("index 2");
93 check_lib.checkNext("name .tdata");
94 }
7495
75 test_step.dependOn(&check_lib.step);96 test_step.dependOn(&check_lib.step);
76 }97 }