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
410410 },
411411 );
412412 } 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);
414414 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
415415 const global = try wasm_bin.wasm_globals.addOne(allocator);
416416 global.* = .{
......@@ -433,7 +433,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
433433 };
434434 if (options.output_mode == .Obj or options.import_table) {
435435 symbol.setUndefined(true);
436 symbol.index = @as(u32, @intCast(wasm_bin.imported_tables_count));
436 symbol.index = @intCast(wasm_bin.imported_tables_count);
437437 wasm_bin.imported_tables_count += 1;
438438 try wasm_bin.imports.put(allocator, loc, .{
439439 .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
467467 const loc = try wasm_bin.createSyntheticSymbol("__tls_base", .global);
468468 const symbol = loc.getSymbol(wasm_bin);
469469 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 });
470475 }
471476 {
472477 const loc = try wasm_bin.createSyntheticSymbol("__tls_size", .global);
473478 const symbol = loc.getSymbol(wasm_bin);
474479 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 });
475485 }
476486 {
477487 const loc = try wasm_bin.createSyntheticSymbol("__tls_align", .global);
478488 const symbol = loc.getSymbol(wasm_bin);
479489 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 });
480495 }
481496 {
482497 const loc = try wasm_bin.createSyntheticSymbol("__wasm_init_tls", .function);
......@@ -2757,27 +2772,18 @@ fn setupMemory(wasm: *Wasm) !void {
27572772 if (mem.eql(u8, entry.key_ptr.*, ".tdata")) {
27582773 if (wasm.findGlobalSymbol("__tls_size")) |loc| {
27592774 const sym = loc.getSymbol(wasm);
2760 sym.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count;
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 });
2775 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.size);
27652776 }
27662777 if (wasm.findGlobalSymbol("__tls_align")) |loc| {
27672778 const sym = loc.getSymbol(wasm);
2768 sym.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count;
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 });
2779 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.alignment);
27732780 }
27742781 if (wasm.findGlobalSymbol("__tls_base")) |loc| {
27752782 const sym = loc.getSymbol(wasm);
2776 sym.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count;
2777 try wasm.wasm_globals.append(wasm.base.allocator, .{
2778 .global_type = .{ .valtype = .i32, .mutable = wasm.base.options.shared_memory },
2779 .init = .{ .i32_const = if (wasm.base.options.shared_memory) @as(u32, 0) else @as(i32, @intCast(memory_ptr)) },
2780 });
2783 wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = if (wasm.base.options.shared_memory)
2784 @as(i32, 0)
2785 else
2786 @as(i32, @intCast(memory_ptr));
27812787 }
27822788 }
27832789
test/link/wasm/shared-memory/build.zig+40-19
......@@ -5,11 +5,9 @@ pub fn build(b: *std.Build) void {
55 b.default_step = test_step;
66
77 add(b, test_step, .Debug);
8
9 // Enable the following build modes once garbage-collection is implemented properly.
10 // add(b, test_step, .ReleaseFast);
11 // add(b, test_step, .ReleaseSmall);
12 // add(b, test_step, .ReleaseSafe);
8 add(b, test_step, .ReleaseFast);
9 add(b, test_step, .ReleaseSmall);
10 add(b, test_step, .ReleaseSafe);
1311}
1412
1513fn 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
4745
4846 // This section *must* be emit as the start function is set to the index
4947 // 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
5255 // This section is only and *must* be emit when shared-memory is enabled
53 check_lib.checkStart("Section data_count");
54 check_lib.checkNext("count 3");
56 // release modes will have the TLS segment optimized out in our test-case.
57 if (optimize_mode == .Debug) {
58 check_lib.checkStart("Section data_count");
59 check_lib.checkNext("count 3");
60 }
5561
5662 check_lib.checkStart("Section custom");
5763 check_lib.checkNext("name name");
5864 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 }
6068 check_lib.checkNext("name __wasm_init_tls");
6169 check_lib.checkNext("type global");
62 check_lib.checkNext("name __tls_size");
63 check_lib.checkNext("name __tls_align");
64 check_lib.checkNext("name __tls_base");
70
71 // In debug mode the symbol __tls_base is resolved to an undefined symbol
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
6685 check_lib.checkNext("type data_segment");
67 check_lib.checkNext("names 3");
68 check_lib.checkNext("index 0");
69 check_lib.checkNext("name .rodata");
70 check_lib.checkNext("index 1");
71 check_lib.checkNext("name .bss");
72 check_lib.checkNext("index 2");
73 check_lib.checkNext("name .tdata");
86 if (optimize_mode == .Debug) {
87 check_lib.checkNext("names 3");
88 check_lib.checkNext("index 0");
89 check_lib.checkNext("name .rodata");
90 check_lib.checkNext("index 1");
91 check_lib.checkNext("name .bss");
92 check_lib.checkNext("index 2");
93 check_lib.checkNext("name .tdata");
94 }
7495
7596 test_step.dependOn(&check_lib.step);
7697 }