authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-14 00:55:55-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:36-08:00
logc535422423abc0258b03693392bae86560a9cc79
treea8eee0bb0e957ca3aac8c491fbcfa0fb55c74dfb
parentf89ef2f7cd1dd4291156e94716df60795a7ee48c

wasm linker: implement hidden visibility


6 files changed, 45 insertions(+), 25 deletions(-)

src/Compilation.zig+3-1
...@@ -3066,7 +3066,7 @@ pub fn saveState(comp: *Compilation) !void {...@@ -3066,7 +3066,7 @@ pub fn saveState(comp: *Compilation) !void {
3066 .wasm => {3066 .wasm => {
3067 const wasm = lf.cast(.wasm).?;3067 const wasm = lf.cast(.wasm).?;
3068 const is_obj = comp.config.output_mode == .Obj;3068 const is_obj = comp.config.output_mode == .Obj;
3069 try bufs.ensureUnusedCapacity(83);3069 try bufs.ensureUnusedCapacity(85);
3070 addBuf(&bufs, wasm.string_bytes.items);3070 addBuf(&bufs, wasm.string_bytes.items);
3071 // TODO make it well-defined memory layout3071 // TODO make it well-defined memory layout
3072 //addBuf(&bufs, mem.sliceAsBytes(wasm.objects.items));3072 //addBuf(&bufs, mem.sliceAsBytes(wasm.objects.items));
...@@ -3133,6 +3133,8 @@ pub fn saveState(comp: *Compilation) !void {...@@ -3133,6 +3133,8 @@ pub fn saveState(comp: *Compilation) !void {
3133 addBuf(&bufs, mem.sliceAsBytes(wasm.missing_exports.keys()));3133 addBuf(&bufs, mem.sliceAsBytes(wasm.missing_exports.keys()));
3134 addBuf(&bufs, mem.sliceAsBytes(wasm.function_exports.keys()));3134 addBuf(&bufs, mem.sliceAsBytes(wasm.function_exports.keys()));
3135 addBuf(&bufs, mem.sliceAsBytes(wasm.function_exports.values()));3135 addBuf(&bufs, mem.sliceAsBytes(wasm.function_exports.values()));
3136 addBuf(&bufs, mem.sliceAsBytes(wasm.hidden_function_exports.keys()));
3137 addBuf(&bufs, mem.sliceAsBytes(wasm.hidden_function_exports.values()));
3136 addBuf(&bufs, mem.sliceAsBytes(wasm.global_exports.items));3138 addBuf(&bufs, mem.sliceAsBytes(wasm.global_exports.items));
3137 addBuf(&bufs, mem.sliceAsBytes(wasm.functions.keys()));3139 addBuf(&bufs, mem.sliceAsBytes(wasm.functions.keys()));
3138 addBuf(&bufs, mem.sliceAsBytes(wasm.function_imports.keys()));3140 addBuf(&bufs, mem.sliceAsBytes(wasm.function_imports.keys()));
src/link/Wasm.zig+20-10
...@@ -210,8 +210,7 @@ entry_resolution: FunctionImport.Resolution = .unresolved,...@@ -210,8 +210,7 @@ entry_resolution: FunctionImport.Resolution = .unresolved,
210210
211/// Empty when outputting an object.211/// Empty when outputting an object.
212function_exports: std.AutoArrayHashMapUnmanaged(String, FunctionIndex) = .empty,212function_exports: std.AutoArrayHashMapUnmanaged(String, FunctionIndex) = .empty,
213/// Tracks the value at the end of prelink.213hidden_function_exports: std.AutoArrayHashMapUnmanaged(String, FunctionIndex) = .empty,
214function_exports_len: u32 = 0,
215global_exports: std.ArrayListUnmanaged(GlobalExport) = .empty,214global_exports: std.ArrayListUnmanaged(GlobalExport) = .empty,
216/// Tracks the value at the end of prelink.215/// Tracks the value at the end of prelink.
217global_exports_len: u32 = 0,216global_exports_len: u32 = 0,
...@@ -360,9 +359,8 @@ pub const FunctionIndex = enum(u32) {...@@ -360,9 +359,8 @@ pub const FunctionIndex = enum(u32) {
360 if (wasm.object_function_imports.getPtr(name)) |import| {359 if (wasm.object_function_imports.getPtr(name)) |import| {
361 return fromResolution(wasm, import.resolution);360 return fromResolution(wasm, import.resolution);
362 }361 }
363 if (wasm.function_exports.get(name)) |index| {362 if (wasm.function_exports.get(name)) |index| return index;
364 return index;363 if (wasm.hidden_function_exports.get(name)) |index| return index;
365 }
366 return null;364 return null;
367 }365 }
368366
...@@ -1919,8 +1917,11 @@ pub const DataSegmentId = enum(u32) {...@@ -1919,8 +1917,11 @@ pub const DataSegmentId = enum(u32) {
1919 const zcu = wasm.base.comp.zcu.?;1917 const zcu = wasm.base.comp.zcu.?;
1920 const ip = &zcu.intern_pool;1918 const ip = &zcu.intern_pool;
1921 const nav = ip.getNav(i.key(wasm).*);1919 const nav = ip.getNav(i.key(wasm).*);
1922 return nav.getLinkSection().toSlice(ip) orelse1920 return nav.getLinkSection().toSlice(ip) orelse switch (category(id, wasm)) {
1923 if (nav.isThreadlocal(ip)) ".tdata" else ".data";1921 .tls => ".tdata",
1922 .data => ".data",
1923 .zero => ".bss",
1924 };
1924 },1925 },
1925 };1926 };
1926 }1927 }
...@@ -3110,6 +3111,7 @@ pub fn deinit(wasm: *Wasm) void {...@@ -3110,6 +3111,7 @@ pub fn deinit(wasm: *Wasm) void {
31103111
3111 wasm.func_types.deinit(gpa);3112 wasm.func_types.deinit(gpa);
3112 wasm.function_exports.deinit(gpa);3113 wasm.function_exports.deinit(gpa);
3114 wasm.hidden_function_exports.deinit(gpa);
3113 wasm.function_imports.deinit(gpa);3115 wasm.function_imports.deinit(gpa);
3114 wasm.functions.deinit(gpa);3116 wasm.functions.deinit(gpa);
3115 wasm.globals.deinit(gpa);3117 wasm.globals.deinit(gpa);
...@@ -3417,7 +3419,6 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v...@@ -3417,7 +3419,6 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v
3417 }3419 }
3418 }3420 }
3419 wasm.functions_end_prelink = @intCast(wasm.functions.entries.len);3421 wasm.functions_end_prelink = @intCast(wasm.functions.entries.len);
3420 wasm.function_exports_len = @intCast(wasm.function_exports.entries.len);
34213422
3422 for (wasm.object_global_imports.keys(), wasm.object_global_imports.values(), 0..) |name, *import, i| {3423 for (wasm.object_global_imports.keys(), wasm.object_global_imports.values(), 0..) |name, *import, i| {
3423 if (import.flags.isIncluded(rdynamic)) {3424 if (import.flags.isIncluded(rdynamic)) {
...@@ -3491,8 +3492,14 @@ fn markFunction(wasm: *Wasm, i: ObjectFunctionIndex) link.File.FlushError!void {...@@ -3491,8 +3492,14 @@ fn markFunction(wasm: *Wasm, i: ObjectFunctionIndex) link.File.FlushError!void {
3491 const function = i.ptr(wasm);3492 const function = i.ptr(wasm);
3492 markObject(wasm, function.object_index);3493 markObject(wasm, function.object_index);
34933494
3494 if (!is_obj and function.flags.isExported(rdynamic))3495 if (!is_obj and function.flags.isExported(rdynamic)) {
3495 try wasm.function_exports.put(gpa, function.name.unwrap().?, @enumFromInt(gop.index));3496 const symbol_name = function.name.unwrap().?;
3497 if (function.flags.visibility_hidden) {
3498 try wasm.hidden_function_exports.put(gpa, symbol_name, @enumFromInt(gop.index));
3499 } else {
3500 try wasm.function_exports.put(gpa, symbol_name, @enumFromInt(gop.index));
3501 }
3502 }
34963503
3497 try wasm.markRelocations(function.relocations(wasm));3504 try wasm.markRelocations(function.relocations(wasm));
3498}3505}
...@@ -3778,6 +3785,9 @@ pub fn flushModule(...@@ -3778,6 +3785,9 @@ pub fn flushModule(
3778 const function_exports_end_zcu: u32 = @intCast(wasm.function_exports.entries.len);3785 const function_exports_end_zcu: u32 = @intCast(wasm.function_exports.entries.len);
3779 defer wasm.function_exports.shrinkRetainingCapacity(function_exports_end_zcu);3786 defer wasm.function_exports.shrinkRetainingCapacity(function_exports_end_zcu);
37803787
3788 const hidden_function_exports_end_zcu: u32 = @intCast(wasm.hidden_function_exports.entries.len);
3789 defer wasm.hidden_function_exports.shrinkRetainingCapacity(hidden_function_exports_end_zcu);
3790
3781 wasm.flush_buffer.clear();3791 wasm.flush_buffer.clear();
3782 try wasm.flush_buffer.missing_exports.reinit(gpa, wasm.missing_exports.keys(), &.{});3792 try wasm.flush_buffer.missing_exports.reinit(gpa, wasm.missing_exports.keys(), &.{});
3783 try wasm.flush_buffer.function_imports.reinit(gpa, wasm.function_imports.keys(), wasm.function_imports.values());3793 try wasm.flush_buffer.function_imports.reinit(gpa, wasm.function_imports.keys(), wasm.function_imports.values());
src/link/Wasm/Flush.zig+6-2
...@@ -164,10 +164,14 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -164,10 +164,14 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
164 }164 }
165 }165 }
166166
167 for (wasm.nav_exports.keys()) |*nav_export| {167 for (wasm.nav_exports.keys(), wasm.nav_exports.values()) |*nav_export, export_index| {
168 if (ip.isFunctionType(ip.getNav(nav_export.nav_index).typeOf(ip))) {168 if (ip.isFunctionType(ip.getNav(nav_export.nav_index).typeOf(ip))) {
169 log.debug("flush export '{s}' nav={d}", .{ nav_export.name.slice(wasm), nav_export.nav_index });169 log.debug("flush export '{s}' nav={d}", .{ nav_export.name.slice(wasm), nav_export.nav_index });
170 try wasm.function_exports.put(gpa, nav_export.name, Wasm.FunctionIndex.fromIpNav(wasm, nav_export.nav_index).?);170 const function_index = Wasm.FunctionIndex.fromIpNav(wasm, nav_export.nav_index).?;
171 switch (export_index.ptr(zcu).opts.visibility) {
172 .default, .protected => try wasm.function_exports.put(gpa, nav_export.name, function_index),
173 .hidden => try wasm.hidden_function_exports.put(gpa, nav_export.name, function_index),
174 }
171 _ = f.missing_exports.swapRemove(nav_export.name);175 _ = f.missing_exports.swapRemove(nav_export.name);
172 _ = f.function_imports.swapRemove(nav_export.name);176 _ = f.function_imports.swapRemove(nav_export.name);
173177
test/link/wasm/bss/build.zig+4-8
...@@ -47,16 +47,14 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt...@@ -47,16 +47,14 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt
47 check_lib.checkInHeaders();47 check_lib.checkInHeaders();
48 check_lib.checkExact("Section custom");48 check_lib.checkExact("Section custom");
49 check_lib.checkExact("type data_segment");49 check_lib.checkExact("type data_segment");
50 check_lib.checkExact("names 2");50 check_lib.checkExact("names 1");
51 check_lib.checkExact("index 0");
52 check_lib.checkExact("name .rodata");
53 // for safe optimization modes `undefined` is stored in data instead of bss.51 // for safe optimization modes `undefined` is stored in data instead of bss.
54 if (is_safe) {52 if (is_safe) {
55 check_lib.checkExact("index 1");53 check_lib.checkExact("index 0");
56 check_lib.checkExact("name .data");54 check_lib.checkExact("name .data");
57 check_lib.checkNotPresent("name .bss");55 check_lib.checkNotPresent("name .bss");
58 } else {56 } else {
59 check_lib.checkExact("index 1"); // bss section always last57 check_lib.checkExact("index 0"); // bss section always last
60 check_lib.checkExact("name .bss");58 check_lib.checkExact("name .bss");
61 }59 }
62 test_step.dependOn(&check_lib.step);60 test_step.dependOn(&check_lib.step);
...@@ -84,10 +82,8 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt...@@ -84,10 +82,8 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt
84 check_lib.checkInHeaders();82 check_lib.checkInHeaders();
85 check_lib.checkExact("Section custom");83 check_lib.checkExact("Section custom");
86 check_lib.checkExact("type data_segment");84 check_lib.checkExact("type data_segment");
87 check_lib.checkExact("names 2");85 check_lib.checkExact("names 1");
88 check_lib.checkExact("index 0");86 check_lib.checkExact("index 0");
89 check_lib.checkExact("name .rodata");
90 check_lib.checkExact("index 1");
91 check_lib.checkExact("name .bss");87 check_lib.checkExact("name .bss");
9288
93 test_step.dependOn(&check_lib.step);89 test_step.dependOn(&check_lib.step);
test/link/wasm/bss/lib.zig+6-2
...@@ -1,5 +1,9 @@...@@ -1,5 +1,9 @@
1pub var bss: u32 = undefined;1pub var bss: u32 = undefined;
22
3export fn foo() void {3fn foo() callconv(.c) u32 {
4 _ = bss;4 return bss;
5}
6
7comptime {
8 @export(&foo, .{ .name = "foo", .visibility = .hidden });
5}9}
test/link/wasm/bss/lib2.zig+6-2
...@@ -1,5 +1,9 @@...@@ -1,5 +1,9 @@
1pub var bss: u32 = 0;1pub var bss: u32 = 0;
22
3export fn foo() void {3fn foo() callconv(.c) u32 {
4 _ = bss;4 return bss;
5}
6
7comptime {
8 @export(&foo, .{ .name = "foo", .visibility = .hidden });
5}9}