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.
This commit is contained in:
Luuk de Gram 2023-07-18 19:55:20 +02:00
parent 1a3304ed23
commit 142dbc7b82
No known key found for this signature in database
GPG Key ID: A8CFE58E4DC7D664
2 changed files with 63 additions and 36 deletions

View File

@ -410,7 +410,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
},
);
} else {
symbol.index = @as(u32, @intCast(wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len));
symbol.index = @intCast(wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len);
symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
const global = try wasm_bin.wasm_globals.addOne(allocator);
global.* = .{
@ -433,7 +433,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
};
if (options.output_mode == .Obj or options.import_table) {
symbol.setUndefined(true);
symbol.index = @as(u32, @intCast(wasm_bin.imported_tables_count));
symbol.index = @intCast(wasm_bin.imported_tables_count);
wasm_bin.imported_tables_count += 1;
try wasm_bin.imports.put(allocator, loc, .{
.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
const loc = try wasm_bin.createSyntheticSymbol("__tls_base", .global);
const symbol = loc.getSymbol(wasm_bin);
symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
symbol.index = @intCast(wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len);
try wasm_bin.wasm_globals.append(wasm_bin.base.allocator, .{
.global_type = .{ .valtype = .i32, .mutable = true },
.init = .{ .i32_const = undefined },
});
}
{
const loc = try wasm_bin.createSyntheticSymbol("__tls_size", .global);
const symbol = loc.getSymbol(wasm_bin);
symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
symbol.index = @intCast(wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len);
try wasm_bin.wasm_globals.append(wasm_bin.base.allocator, .{
.global_type = .{ .valtype = .i32, .mutable = false },
.init = .{ .i32_const = undefined },
});
}
{
const loc = try wasm_bin.createSyntheticSymbol("__tls_align", .global);
const symbol = loc.getSymbol(wasm_bin);
symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
symbol.index = @intCast(wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len);
try wasm_bin.wasm_globals.append(wasm_bin.base.allocator, .{
.global_type = .{ .valtype = .i32, .mutable = false },
.init = .{ .i32_const = undefined },
});
}
{
const loc = try wasm_bin.createSyntheticSymbol("__wasm_init_tls", .function);
@ -2757,27 +2772,18 @@ fn setupMemory(wasm: *Wasm) !void {
if (mem.eql(u8, entry.key_ptr.*, ".tdata")) {
if (wasm.findGlobalSymbol("__tls_size")) |loc| {
const sym = loc.getSymbol(wasm);
sym.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count;
try wasm.wasm_globals.append(wasm.base.allocator, .{
.global_type = .{ .valtype = .i32, .mutable = false },
.init = .{ .i32_const = @as(i32, @intCast(segment.size)) },
});
wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.size);
}
if (wasm.findGlobalSymbol("__tls_align")) |loc| {
const sym = loc.getSymbol(wasm);
sym.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count;
try wasm.wasm_globals.append(wasm.base.allocator, .{
.global_type = .{ .valtype = .i32, .mutable = false },
.init = .{ .i32_const = @as(i32, @intCast(segment.alignment)) },
});
wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = @intCast(segment.alignment);
}
if (wasm.findGlobalSymbol("__tls_base")) |loc| {
const sym = loc.getSymbol(wasm);
sym.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count;
try wasm.wasm_globals.append(wasm.base.allocator, .{
.global_type = .{ .valtype = .i32, .mutable = wasm.base.options.shared_memory },
.init = .{ .i32_const = if (wasm.base.options.shared_memory) @as(u32, 0) else @as(i32, @intCast(memory_ptr)) },
});
wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = if (wasm.base.options.shared_memory)
@as(i32, 0)
else
@as(i32, @intCast(memory_ptr));
}
}

View File

@ -5,11 +5,9 @@ pub fn build(b: *std.Build) void {
b.default_step = test_step;
add(b, test_step, .Debug);
// Enable the following build modes once garbage-collection is implemented properly.
// add(b, test_step, .ReleaseFast);
// add(b, test_step, .ReleaseSmall);
// add(b, test_step, .ReleaseSafe);
add(b, test_step, .ReleaseFast);
add(b, test_step, .ReleaseSmall);
add(b, test_step, .ReleaseSafe);
}
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
// This section *must* be emit as the start function is set to the index
// of __wasm_init_memory
check_lib.checkStart("Section start");
// release modes will have the TLS segment optimized out in our test-case.
// This means we won't have __wasm_init_memory in such case, and therefore
// should also not have a section "start"
if (optimize_mode == .Debug) {
check_lib.checkStart("Section start");
}
// This section is only and *must* be emit when shared-memory is enabled
check_lib.checkStart("Section data_count");
check_lib.checkNext("count 3");
// release modes will have the TLS segment optimized out in our test-case.
if (optimize_mode == .Debug) {
check_lib.checkStart("Section data_count");
check_lib.checkNext("count 3");
}
check_lib.checkStart("Section custom");
check_lib.checkNext("name name");
check_lib.checkNext("type function");
check_lib.checkNext("name __wasm_init_memory");
if (optimize_mode == .Debug) {
check_lib.checkNext("name __wasm_init_memory");
}
check_lib.checkNext("name __wasm_init_tls");
check_lib.checkNext("type global");
check_lib.checkNext("name __tls_size");
check_lib.checkNext("name __tls_align");
check_lib.checkNext("name __tls_base");
// In debug mode the symbol __tls_base is resolved to an undefined symbol
// from the object file, hence its placement differs than in release modes
// where the entire tls segment is optimized away, and tls_base will have
// its original position.
if (optimize_mode == .Debug) {
check_lib.checkNext("name __tls_size");
check_lib.checkNext("name __tls_align");
check_lib.checkNext("name __tls_base");
} else {
check_lib.checkNext("name __tls_base");
check_lib.checkNext("name __tls_size");
check_lib.checkNext("name __tls_align");
}
check_lib.checkNext("type data_segment");
check_lib.checkNext("names 3");
check_lib.checkNext("index 0");
check_lib.checkNext("name .rodata");
check_lib.checkNext("index 1");
check_lib.checkNext("name .bss");
check_lib.checkNext("index 2");
check_lib.checkNext("name .tdata");
if (optimize_mode == .Debug) {
check_lib.checkNext("names 3");
check_lib.checkNext("index 0");
check_lib.checkNext("name .rodata");
check_lib.checkNext("index 1");
check_lib.checkNext("name .bss");
check_lib.checkNext("index 2");
check_lib.checkNext("name .tdata");
}
test_step.dependOn(&check_lib.step);
}