wasm linker: implement hidden visibility

This commit is contained in:
Andrew Kelley 2025-01-14 00:55:55 -08:00
parent f89ef2f7cd
commit c535422423
6 changed files with 45 additions and 25 deletions

View file

@ -3066,7 +3066,7 @@ pub fn saveState(comp: *Compilation) !void {
.wasm => { .wasm => {
const wasm = lf.cast(.wasm).?; const wasm = lf.cast(.wasm).?;
const is_obj = comp.config.output_mode == .Obj; const is_obj = comp.config.output_mode == .Obj;
try bufs.ensureUnusedCapacity(83); try bufs.ensureUnusedCapacity(85);
addBuf(&bufs, wasm.string_bytes.items); addBuf(&bufs, wasm.string_bytes.items);
// TODO make it well-defined memory layout // TODO make it well-defined memory layout
//addBuf(&bufs, mem.sliceAsBytes(wasm.objects.items)); //addBuf(&bufs, mem.sliceAsBytes(wasm.objects.items));
@ -3133,6 +3133,8 @@ pub fn saveState(comp: *Compilation) !void {
addBuf(&bufs, mem.sliceAsBytes(wasm.missing_exports.keys())); addBuf(&bufs, mem.sliceAsBytes(wasm.missing_exports.keys()));
addBuf(&bufs, mem.sliceAsBytes(wasm.function_exports.keys())); addBuf(&bufs, mem.sliceAsBytes(wasm.function_exports.keys()));
addBuf(&bufs, mem.sliceAsBytes(wasm.function_exports.values())); addBuf(&bufs, mem.sliceAsBytes(wasm.function_exports.values()));
addBuf(&bufs, mem.sliceAsBytes(wasm.hidden_function_exports.keys()));
addBuf(&bufs, mem.sliceAsBytes(wasm.hidden_function_exports.values()));
addBuf(&bufs, mem.sliceAsBytes(wasm.global_exports.items)); addBuf(&bufs, mem.sliceAsBytes(wasm.global_exports.items));
addBuf(&bufs, mem.sliceAsBytes(wasm.functions.keys())); addBuf(&bufs, mem.sliceAsBytes(wasm.functions.keys()));
addBuf(&bufs, mem.sliceAsBytes(wasm.function_imports.keys())); addBuf(&bufs, mem.sliceAsBytes(wasm.function_imports.keys()));

View file

@ -210,8 +210,7 @@ entry_resolution: FunctionImport.Resolution = .unresolved,
/// Empty when outputting an object. /// Empty when outputting an object.
function_exports: std.AutoArrayHashMapUnmanaged(String, FunctionIndex) = .empty, function_exports: std.AutoArrayHashMapUnmanaged(String, FunctionIndex) = .empty,
/// Tracks the value at the end of prelink. hidden_function_exports: std.AutoArrayHashMapUnmanaged(String, FunctionIndex) = .empty,
function_exports_len: u32 = 0,
global_exports: std.ArrayListUnmanaged(GlobalExport) = .empty, global_exports: std.ArrayListUnmanaged(GlobalExport) = .empty,
/// Tracks the value at the end of prelink. /// Tracks the value at the end of prelink.
global_exports_len: u32 = 0, global_exports_len: u32 = 0,
@ -360,9 +359,8 @@ pub const FunctionIndex = enum(u32) {
if (wasm.object_function_imports.getPtr(name)) |import| { if (wasm.object_function_imports.getPtr(name)) |import| {
return fromResolution(wasm, import.resolution); return fromResolution(wasm, import.resolution);
} }
if (wasm.function_exports.get(name)) |index| { if (wasm.function_exports.get(name)) |index| return index;
return index; if (wasm.hidden_function_exports.get(name)) |index| return index;
}
return null; return null;
} }
@ -1919,8 +1917,11 @@ pub const DataSegmentId = enum(u32) {
const zcu = wasm.base.comp.zcu.?; const zcu = wasm.base.comp.zcu.?;
const ip = &zcu.intern_pool; const ip = &zcu.intern_pool;
const nav = ip.getNav(i.key(wasm).*); const nav = ip.getNav(i.key(wasm).*);
return nav.getLinkSection().toSlice(ip) orelse return nav.getLinkSection().toSlice(ip) orelse switch (category(id, wasm)) {
if (nav.isThreadlocal(ip)) ".tdata" else ".data"; .tls => ".tdata",
.data => ".data",
.zero => ".bss",
};
}, },
}; };
} }
@ -3110,6 +3111,7 @@ pub fn deinit(wasm: *Wasm) void {
wasm.func_types.deinit(gpa); wasm.func_types.deinit(gpa);
wasm.function_exports.deinit(gpa); wasm.function_exports.deinit(gpa);
wasm.hidden_function_exports.deinit(gpa);
wasm.function_imports.deinit(gpa); wasm.function_imports.deinit(gpa);
wasm.functions.deinit(gpa); wasm.functions.deinit(gpa);
wasm.globals.deinit(gpa); wasm.globals.deinit(gpa);
@ -3417,7 +3419,6 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v
} }
} }
wasm.functions_end_prelink = @intCast(wasm.functions.entries.len); wasm.functions_end_prelink = @intCast(wasm.functions.entries.len);
wasm.function_exports_len = @intCast(wasm.function_exports.entries.len);
for (wasm.object_global_imports.keys(), wasm.object_global_imports.values(), 0..) |name, *import, i| { for (wasm.object_global_imports.keys(), wasm.object_global_imports.values(), 0..) |name, *import, i| {
if (import.flags.isIncluded(rdynamic)) { if (import.flags.isIncluded(rdynamic)) {
@ -3491,8 +3492,14 @@ fn markFunction(wasm: *Wasm, i: ObjectFunctionIndex) link.File.FlushError!void {
const function = i.ptr(wasm); const function = i.ptr(wasm);
markObject(wasm, function.object_index); markObject(wasm, function.object_index);
if (!is_obj and function.flags.isExported(rdynamic)) if (!is_obj and function.flags.isExported(rdynamic)) {
try wasm.function_exports.put(gpa, function.name.unwrap().?, @enumFromInt(gop.index)); const symbol_name = function.name.unwrap().?;
if (function.flags.visibility_hidden) {
try wasm.hidden_function_exports.put(gpa, symbol_name, @enumFromInt(gop.index));
} else {
try wasm.function_exports.put(gpa, symbol_name, @enumFromInt(gop.index));
}
}
try wasm.markRelocations(function.relocations(wasm)); try wasm.markRelocations(function.relocations(wasm));
} }
@ -3778,6 +3785,9 @@ pub fn flushModule(
const function_exports_end_zcu: u32 = @intCast(wasm.function_exports.entries.len); const function_exports_end_zcu: u32 = @intCast(wasm.function_exports.entries.len);
defer wasm.function_exports.shrinkRetainingCapacity(function_exports_end_zcu); defer wasm.function_exports.shrinkRetainingCapacity(function_exports_end_zcu);
const hidden_function_exports_end_zcu: u32 = @intCast(wasm.hidden_function_exports.entries.len);
defer wasm.hidden_function_exports.shrinkRetainingCapacity(hidden_function_exports_end_zcu);
wasm.flush_buffer.clear(); wasm.flush_buffer.clear();
try wasm.flush_buffer.missing_exports.reinit(gpa, wasm.missing_exports.keys(), &.{}); try wasm.flush_buffer.missing_exports.reinit(gpa, wasm.missing_exports.keys(), &.{});
try wasm.flush_buffer.function_imports.reinit(gpa, wasm.function_imports.keys(), wasm.function_imports.values()); try wasm.flush_buffer.function_imports.reinit(gpa, wasm.function_imports.keys(), wasm.function_imports.values());

View file

@ -164,10 +164,14 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
} }
} }
for (wasm.nav_exports.keys()) |*nav_export| { for (wasm.nav_exports.keys(), wasm.nav_exports.values()) |*nav_export, export_index| {
if (ip.isFunctionType(ip.getNav(nav_export.nav_index).typeOf(ip))) { if (ip.isFunctionType(ip.getNav(nav_export.nav_index).typeOf(ip))) {
log.debug("flush export '{s}' nav={d}", .{ nav_export.name.slice(wasm), nav_export.nav_index }); log.debug("flush export '{s}' nav={d}", .{ nav_export.name.slice(wasm), nav_export.nav_index });
try wasm.function_exports.put(gpa, nav_export.name, Wasm.FunctionIndex.fromIpNav(wasm, nav_export.nav_index).?); const function_index = Wasm.FunctionIndex.fromIpNav(wasm, nav_export.nav_index).?;
switch (export_index.ptr(zcu).opts.visibility) {
.default, .protected => try wasm.function_exports.put(gpa, nav_export.name, function_index),
.hidden => try wasm.hidden_function_exports.put(gpa, nav_export.name, function_index),
}
_ = f.missing_exports.swapRemove(nav_export.name); _ = f.missing_exports.swapRemove(nav_export.name);
_ = f.function_imports.swapRemove(nav_export.name); _ = f.function_imports.swapRemove(nav_export.name);

View file

@ -47,16 +47,14 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt
check_lib.checkInHeaders(); check_lib.checkInHeaders();
check_lib.checkExact("Section custom"); check_lib.checkExact("Section custom");
check_lib.checkExact("type data_segment"); check_lib.checkExact("type data_segment");
check_lib.checkExact("names 2"); check_lib.checkExact("names 1");
check_lib.checkExact("index 0");
check_lib.checkExact("name .rodata");
// for safe optimization modes `undefined` is stored in data instead of bss. // for safe optimization modes `undefined` is stored in data instead of bss.
if (is_safe) { if (is_safe) {
check_lib.checkExact("index 1"); check_lib.checkExact("index 0");
check_lib.checkExact("name .data"); check_lib.checkExact("name .data");
check_lib.checkNotPresent("name .bss"); check_lib.checkNotPresent("name .bss");
} else { } else {
check_lib.checkExact("index 1"); // bss section always last check_lib.checkExact("index 0"); // bss section always last
check_lib.checkExact("name .bss"); check_lib.checkExact("name .bss");
} }
test_step.dependOn(&check_lib.step); 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
check_lib.checkInHeaders(); check_lib.checkInHeaders();
check_lib.checkExact("Section custom"); check_lib.checkExact("Section custom");
check_lib.checkExact("type data_segment"); check_lib.checkExact("type data_segment");
check_lib.checkExact("names 2"); check_lib.checkExact("names 1");
check_lib.checkExact("index 0"); check_lib.checkExact("index 0");
check_lib.checkExact("name .rodata");
check_lib.checkExact("index 1");
check_lib.checkExact("name .bss"); check_lib.checkExact("name .bss");
test_step.dependOn(&check_lib.step); test_step.dependOn(&check_lib.step);

View file

@ -1,5 +1,9 @@
pub var bss: u32 = undefined; pub var bss: u32 = undefined;
export fn foo() void { fn foo() callconv(.c) u32 {
_ = bss; return bss;
}
comptime {
@export(&foo, .{ .name = "foo", .visibility = .hidden });
} }

View file

@ -1,5 +1,9 @@
pub var bss: u32 = 0; pub var bss: u32 = 0;
export fn foo() void { fn foo() callconv(.c) u32 {
_ = bss; return bss;
}
comptime {
@export(&foo, .{ .name = "foo", .visibility = .hidden });
} }