authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 18:17:37-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 18:17:37-08:00
log9dd6efb7e422ddc54ca34dc76b0d9efab746f8bf
tree0b83a8901a3bc0efff2f92084cb4519842e58980
parent10db1b9eda849420b92cc21e2cbc93e0c74740bb

wasm linker: fix TLS data segments

fix calculation of alignment and size include __tls_align and __tls_size globals along with __tls_base include them only if the TLS segment is emitted add missing reloc logic for memory_addr_tls_sleb fix name of data segments to include only the prefix

2 files changed, 39 insertions(+), 25 deletions(-)

src/link/Wasm/Flush.zig+38-25
...@@ -330,16 +330,6 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -330,16 +330,6 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
330 try wasm.addFunction(.__wasm_init_memory, &.{}, &.{});330 try wasm.addFunction(.__wasm_init_memory, &.{}, &.{});
331 }331 }
332332
333 // When we have TLS GOT entries and shared memory is enabled,
334 // we must perform runtime relocations or else we don't create the function.
335 if (shared_memory) {
336 // This logic that checks `any_tls_relocs` is missing the part where it
337 // also notices threadlocal globals from Zcu code.
338 if (wasm.any_tls_relocs) try wasm.addFunction(.__wasm_apply_global_tls_relocs, &.{}, &.{});
339 try wasm.addFunction(.__wasm_init_tls, &.{.i32}, &.{});
340 try wasm.globals.put(gpa, .__tls_base, {});
341 }
342
343 try wasm.tables.ensureUnusedCapacity(gpa, 1);333 try wasm.tables.ensureUnusedCapacity(gpa, 1);
344334
345 if (f.indirect_function_table.entries.len > 0) {335 if (f.indirect_function_table.entries.len > 0) {
...@@ -446,17 +436,25 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -446,17 +436,25 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
446 const want_new_segment = b: {436 const want_new_segment = b: {
447 if (is_obj) break :b false;437 if (is_obj) break :b false;
448 switch (seen_tls) {438 switch (seen_tls) {
449 .before => if (category == .tls) {439 .before => switch (category) {
450 virtual_addrs.tls_base = if (shared_memory) 0 else @intCast(start_addr);440 .tls => {
451 virtual_addrs.tls_align = alignment;441 virtual_addrs.tls_base = if (shared_memory) 0 else @intCast(start_addr);
452 seen_tls = .during;442 virtual_addrs.tls_align = alignment;
453 break :b f.data_segment_groups.items.len > 0;443 seen_tls = .during;
444 break :b f.data_segment_groups.items.len > 0;
445 },
446 else => {},
454 },447 },
455 .during => if (category != .tls) {448 .during => switch (category) {
456 virtual_addrs.tls_size = @intCast(start_addr - virtual_addrs.tls_base.?);449 .tls => {
457 virtual_addrs.tls_align = virtual_addrs.tls_align.maxStrict(alignment);450 virtual_addrs.tls_align = virtual_addrs.tls_align.maxStrict(alignment);
458 seen_tls = .after;451 virtual_addrs.tls_size = @intCast(memory_ptr - virtual_addrs.tls_base.?);
459 break :b true;452 break :b false;
453 },
454 else => {
455 seen_tls = .after;
456 break :b true;
457 },
460 },458 },
461 .after => {},459 .after => {},
462 }460 }
...@@ -480,6 +478,9 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -480,6 +478,9 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
480 .first_segment = first_segment,478 .first_segment = first_segment,
481 .end_addr = @intCast(memory_ptr),479 .end_addr = @intCast(memory_ptr),
482 });480 });
481 if (category == .tls and seen_tls == .during) {
482 virtual_addrs.tls_size = @intCast(memory_ptr - virtual_addrs.tls_base.?);
483 }
483 }484 }
484485
485 if (shared_memory and wasm.any_passive_inits) {486 if (shared_memory and wasm.any_passive_inits) {
...@@ -537,6 +538,19 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -537,6 +538,19 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
537 }538 }
538 f.memory_layout_finished = true;539 f.memory_layout_finished = true;
539540
541 // When we have TLS GOT entries and shared memory is enabled, we must
542 // perform runtime relocations or else we don't create the function.
543 if (shared_memory and virtual_addrs.tls_base != null) {
544 // This logic that checks `any_tls_relocs` is missing the part where it
545 // also notices threadlocal globals from Zcu code.
546 if (wasm.any_tls_relocs) try wasm.addFunction(.__wasm_apply_global_tls_relocs, &.{}, &.{});
547 try wasm.addFunction(.__wasm_init_tls, &.{.i32}, &.{});
548 try wasm.globals.ensureUnusedCapacity(gpa, 3);
549 wasm.globals.putAssumeCapacity(.__tls_base, {});
550 wasm.globals.putAssumeCapacity(.__tls_size, {});
551 wasm.globals.putAssumeCapacity(.__tls_align, {});
552 }
553
540 var section_index: u32 = 0;554 var section_index: u32 = 0;
541 // Index of the code section. Used to tell relocation table where the section lives.555 // Index of the code section. Used to tell relocation table where the section lives.
542 var code_section_index: ?u32 = null;556 var code_section_index: ?u32 = null;
...@@ -986,9 +1000,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -986,9 +1000,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
986 }1000 }
987 segment_offset += @intCast(binary_bytes.items.len - code_start);1001 segment_offset += @intCast(binary_bytes.items.len - code_start);
988 }1002 }
989 assert(group_index == f.data_segment_groups.items.len);
9901003
991 replaceVecSectionHeader(binary_bytes, header_offset, .data, group_index);1004 replaceVecSectionHeader(binary_bytes, header_offset, .data, @intCast(f.data_segment_groups.items.len));
992 data_section_index = section_index;1005 data_section_index = section_index;
993 section_index += 1;1006 section_index += 1;
994 }1007 }
...@@ -1128,7 +1141,7 @@ fn emitNameSection(...@@ -1128,7 +1141,7 @@ fn emitNameSection(
1128 try leb.writeUleb128(binary_bytes.writer(gpa), total_data_segments);1141 try leb.writeUleb128(binary_bytes.writer(gpa), total_data_segments);
11291142
1130 for (data_segment_groups, 0..) |group, i| {1143 for (data_segment_groups, 0..) |group, i| {
1131 const name = group.first_segment.name(wasm);1144 const name, _ = splitSegmentName(group.first_segment.name(wasm));
1132 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(i)));1145 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(i)));
1133 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));1146 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));
1134 try binary_bytes.appendSlice(gpa, name);1147 try binary_bytes.appendSlice(gpa, name);
...@@ -1680,8 +1693,8 @@ fn applyRelocs(code: []u8, code_offset: u32, relocs: Wasm.ObjectRelocation.Itera...@@ -1680,8 +1693,8 @@ fn applyRelocs(code: []u8, code_offset: u32, relocs: Wasm.ObjectRelocation.Itera
1680 .memory_addr_rel_sleb64 => @panic("TODO implement relocation memory_addr_rel_sleb64"),1693 .memory_addr_rel_sleb64 => @panic("TODO implement relocation memory_addr_rel_sleb64"),
1681 .memory_addr_sleb => reloc_sleb_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)),1694 .memory_addr_sleb => reloc_sleb_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)),
1682 .memory_addr_sleb64 => reloc_sleb64_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)),1695 .memory_addr_sleb64 => reloc_sleb64_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)),
1683 .memory_addr_tls_sleb => @panic("TODO implement relocation memory_addr_tls_sleb"),1696 .memory_addr_tls_sleb => reloc_sleb_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)),
1684 .memory_addr_tls_sleb64 => @panic("TODO implement relocation memory_addr_tls_sleb64"),1697 .memory_addr_tls_sleb64 => reloc_sleb64_addr(sliced_code, .fromObjectData(wasm, pointee.data, addend.*)),
16851698
1686 .memory_addr_import_i32 => reloc_u32_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)),1699 .memory_addr_import_i32 => reloc_u32_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)),
1687 .memory_addr_import_i64 => reloc_u64_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)),1700 .memory_addr_import_i64 => reloc_u64_addr(sliced_code, .fromSymbolName(wasm, pointee.symbol_name, addend.*)),
test/link/wasm/shared-memory/build.zig+1
...@@ -43,6 +43,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt...@@ -43,6 +43,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt
43 check_exe.checkInHeaders();43 check_exe.checkInHeaders();
44 check_exe.checkExact("Section export");44 check_exe.checkExact("Section export");
45 check_exe.checkExact("entries 2");45 check_exe.checkExact("entries 2");
46 check_exe.checkExact("name foo");
46 check_exe.checkExact("name memory"); // ensure we also export memory again47 check_exe.checkExact("name memory"); // ensure we also export memory again
4748
48 // This section *must* be emit as the start function is set to the index49 // This section *must* be emit as the start function is set to the index