| author | |
| committer | |
| log | 142dbc7b82c741692dd17f8c0455203826342bba |
| tree | 89899879d0bc08f077e3f0d2a4de1164a414eaa0 |
| parent | 1a3304ed236b60cd31c790bae929a78c58c9d33e |
| signature |
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 | 410 | }, |
| 411 | 411 | ); |
| 412 | 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 | 414 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 415 | 415 | const global = try wasm_bin.wasm_globals.addOne(allocator); |
| 416 | 416 | global.* = .{ |
| ... | ... | @@ -433,7 +433,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 433 | 433 | }; |
| 434 | 434 | if (options.output_mode == .Obj or options.import_table) { |
| 435 | 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 | 437 | wasm_bin.imported_tables_count += 1; |
| 438 | 438 | try wasm_bin.imports.put(allocator, loc, .{ |
| 439 | 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 | 467 | const loc = try wasm_bin.createSyntheticSymbol("__tls_base", .global); |
| 468 | 468 | const symbol = loc.getSymbol(wasm_bin); |
| 469 | 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 | 477 | const loc = try wasm_bin.createSyntheticSymbol("__tls_size", .global); |
| 473 | 478 | const symbol = loc.getSymbol(wasm_bin); |
| 474 | 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 | 487 | const loc = try wasm_bin.createSyntheticSymbol("__tls_align", .global); |
| 478 | 488 | const symbol = loc.getSymbol(wasm_bin); |
| 479 | 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 | 497 | const loc = try wasm_bin.createSyntheticSymbol("__wasm_init_tls", .function); |
| ... | ... | @@ -2757,27 +2772,18 @@ fn setupMemory(wasm: *Wasm) !void { |
| 2757 | 2772 | if (mem.eql(u8, entry.key_ptr.*, ".tdata")) { |
| 2758 | 2773 | if (wasm.findGlobalSymbol("__tls_size")) |loc| { |
| 2759 | 2774 | 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); | |
| 2765 | 2776 | } |
| 2766 | 2777 | if (wasm.findGlobalSymbol("__tls_align")) |loc| { |
| 2767 | 2778 | 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); | |
| 2773 | 2780 | } |
| 2774 | 2781 | if (wasm.findGlobalSymbol("__tls_base")) |loc| { |
| 2775 | 2782 | 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)); | |
| 2781 | 2787 | } |
| 2782 | 2788 | } |
| 2783 | 2789 |
test/link/wasm/shared-memory/build.zig+40-19| ... | ... | @@ -5,11 +5,9 @@ pub fn build(b: *std.Build) void { |
| 5 | 5 | b.default_step = test_step; |
| 6 | 6 | |
| 7 | 7 | 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); | |
| 13 | 11 | } |
| 14 | 12 | |
| 15 | 13 | fn 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 | 45 | |
| 48 | 46 | // This section *must* be emit as the start function is set to the index |
| 49 | 47 | // 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 | } | |
| 51 | 54 | |
| 52 | 55 | // 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 | } | |
| 55 | 61 | |
| 56 | 62 | check_lib.checkStart("Section custom"); |
| 57 | 63 | check_lib.checkNext("name name"); |
| 58 | 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 | 68 | check_lib.checkNext("name __wasm_init_tls"); |
| 61 | 69 | 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 | } | |
| 65 | 84 | |
| 66 | 85 | 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 | } | |
| 74 | 95 | |
| 75 | 96 | test_step.dependOn(&check_lib.step); |
| 76 | 97 | } |