authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-12-18 20:05:01-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:36-08:00
log070b973c4a3c25e688e9b0b59ff449a294226a05
tree4dd237792b09c20dad444c864da67e7e0309bfc0
parent23d0882b54f1d7b8907eac47c445cfe4e093249d

wasm linker: allow undefined imports when lib name is provided

and expose object_host_name as an option for setting the lib name for object files, since the wasm linking standards don't specify a way to do it.

6 files changed, 49 insertions(+), 26 deletions(-)

src/Compilation.zig+1
......@@ -1587,6 +1587,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
15871587 .pdb_source_path = options.pdb_source_path,
15881588 .pdb_out_path = options.pdb_out_path,
15891589 .entry_addr = null, // CLI does not expose this option (yet?)
1590 .object_host_name = null, // TODO expose in the CLI
15901591 };
15911592
15921593 switch (options.cache_mode) {
src/link.zig+1
......@@ -400,6 +400,7 @@ pub const File = struct {
400400 export_table: bool,
401401 initial_memory: ?u64,
402402 max_memory: ?u64,
403 object_host_name: ?[]const u8,
403404 export_symbol_names: []const []const u8,
404405 global_base: ?u64,
405406 build_id: std.zig.BuildId,
src/link/Wasm.zig+39-20
......@@ -150,10 +150,10 @@ nav_fixups: std.ArrayListUnmanaged(NavFixup) = .empty,
150150symbol_table: std.AutoArrayHashMapUnmanaged(String, void) = .empty,
151151
152152/// When importing objects from the host environment, a name must be supplied.
153/// LLVM uses "env" by default when none is given. This would be a good default for Zig
154/// to support existing code.
155/// TODO: Allow setting this through a flag?
156host_name: String,
153/// LLVM uses "env" by default when none is given.
154/// This value is passed to object files since wasm tooling conventions provides
155/// no way to specify the module name in the symbol table.
156object_host_name: OptionalString,
157157
158158/// Memory section
159159memories: std.wasm.Memory = .{ .limits = .{
......@@ -737,7 +737,7 @@ const DebugSection = struct {};
737737
738738pub const FunctionImport = extern struct {
739739 flags: SymbolFlags,
740 module_name: String,
740 module_name: OptionalString,
741741 source_location: SourceLocation,
742742 resolution: Resolution,
743743 type: FunctionType.Index,
......@@ -862,7 +862,7 @@ pub const FunctionImport = extern struct {
862862 return index.key(wasm).*;
863863 }
864864
865 pub fn moduleName(index: Index, wasm: *const Wasm) String {
865 pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString {
866866 return index.value(wasm).module_name;
867867 }
868868
......@@ -888,7 +888,7 @@ pub const Function = extern struct {
888888
889889pub const GlobalImport = extern struct {
890890 flags: SymbolFlags,
891 module_name: String,
891 module_name: OptionalString,
892892 source_location: SourceLocation,
893893 resolution: Resolution,
894894
......@@ -1009,7 +1009,7 @@ pub const GlobalImport = extern struct {
10091009 return index.key(wasm).*;
10101010 }
10111011
1012 pub fn moduleName(index: Index, wasm: *const Wasm) String {
1012 pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString {
10131013 return index.value(wasm).module_name;
10141014 }
10151015
......@@ -1114,7 +1114,7 @@ pub const TableImport = extern struct {
11141114 return index.key(wasm).*;
11151115 }
11161116
1117 pub fn moduleName(index: Index, wasm: *const Wasm) String {
1117 pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString {
11181118 return index.value(wasm).module_name;
11191119 }
11201120 };
......@@ -1604,7 +1604,7 @@ pub const ZcuImportIndex = enum(u32) {
16041604 return wasm.getExistingString(name_slice).?;
16051605 }
16061606
1607 pub fn moduleName(index: ZcuImportIndex, wasm: *const Wasm) String {
1607 pub fn moduleName(index: ZcuImportIndex, wasm: *const Wasm) OptionalString {
16081608 const zcu = wasm.base.comp.zcu.?;
16091609 const ip = &zcu.intern_pool;
16101610 const nav_index = index.ptr(wasm).*;
......@@ -1613,8 +1613,8 @@ pub const ZcuImportIndex = enum(u32) {
16131613 .@"extern" => |*ext| ext,
16141614 else => unreachable,
16151615 };
1616 const lib_name = ext.lib_name.toSlice(ip) orelse return wasm.host_name;
1617 return wasm.getExistingString(lib_name).?;
1616 const lib_name = ext.lib_name.toSlice(ip) orelse return .none;
1617 return wasm.getExistingString(lib_name).?.toOptional();
16181618 }
16191619
16201620 pub fn functionType(index: ZcuImportIndex, wasm: *Wasm) FunctionType.Index {
......@@ -1639,8 +1639,8 @@ pub const ZcuImportIndex = enum(u32) {
16391639 }
16401640};
16411641
1642/// 0. Index into `object_function_imports`.
1643/// 1. Index into `imports`.
1642/// 0. Index into `Wasm.object_function_imports`.
1643/// 1. Index into `Wasm.imports`.
16441644pub const FunctionImportId = enum(u32) {
16451645 _,
16461646
......@@ -1695,7 +1695,7 @@ pub const FunctionImportId = enum(u32) {
16951695 };
16961696 }
16971697
1698 pub fn moduleName(id: FunctionImportId, wasm: *const Wasm) String {
1698 pub fn moduleName(id: FunctionImportId, wasm: *const Wasm) OptionalString {
16991699 return switch (unpack(id, wasm)) {
17001700 inline .object_function_import, .zcu_import => |i| i.moduleName(wasm),
17011701 };
......@@ -1706,6 +1706,24 @@ pub const FunctionImportId = enum(u32) {
17061706 inline .object_function_import, .zcu_import => |i| i.functionType(wasm),
17071707 };
17081708 }
1709
1710 /// Asserts not emitting an object, and `Wasm.import_symbols` is false.
1711 pub fn undefinedAllowed(id: FunctionImportId, wasm: *const Wasm) bool {
1712 assert(!wasm.import_symbols);
1713 assert(wasm.base.comp.config.output_mode != .Obj);
1714 return switch (unpack(id, wasm)) {
1715 .object_function_import => |i| {
1716 const import = i.value(wasm);
1717 return import.flags.binding == .strong and import.module_name != .none;
1718 },
1719 .zcu_import => |i| {
1720 const zcu = wasm.base.comp.zcu.?;
1721 const ip = &zcu.intern_pool;
1722 const ext = ip.getNav(i.ptr(wasm).*).toExtern(ip).?;
1723 return !ext.is_weak_linkage and ext.lib_name != .none;
1724 },
1725 };
1726 }
17091727};
17101728
17111729/// 0. Index into `object_global_imports`.
......@@ -1760,7 +1778,7 @@ pub const GlobalImportId = enum(u32) {
17601778 };
17611779 }
17621780
1763 pub fn moduleName(id: GlobalImportId, wasm: *const Wasm) String {
1781 pub fn moduleName(id: GlobalImportId, wasm: *const Wasm) OptionalString {
17641782 return switch (unpack(id, wasm)) {
17651783 inline .object_global_import, .zcu_import => |i| i.moduleName(wasm),
17661784 };
......@@ -2082,7 +2100,7 @@ pub fn createEmpty(
20822100
20832101 .entry_name = undefined,
20842102 .dump_argv_list = .empty,
2085 .host_name = undefined,
2103 .object_host_name = .none,
20862104 .preloaded_strings = undefined,
20872105 };
20882106 if (use_llvm and comp.config.have_zcu) {
......@@ -2090,7 +2108,7 @@ pub fn createEmpty(
20902108 }
20912109 errdefer wasm.base.destroy();
20922110
2093 wasm.host_name = try wasm.internString("env");
2111 if (options.object_host_name) |name| wasm.object_host_name = (try wasm.internString(name)).toOptional();
20942112
20952113 inline for (@typeInfo(PreloadedStrings).@"struct".fields) |field| {
20962114 @field(wasm.preloaded_strings, field.name) = try wasm.internString(field.name);
......@@ -2162,7 +2180,7 @@ fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void {
21622180 var ss: Object.ScratchSpace = .{};
21632181 defer ss.deinit(gpa);
21642182
2165 const object = try Object.parse(wasm, file_contents, obj.path, null, wasm.host_name, &ss, obj.must_link, gc_sections);
2183 const object = try Object.parse(wasm, file_contents, obj.path, null, wasm.object_host_name, &ss, obj.must_link, gc_sections);
21662184 wasm.objects.appendAssumeCapacity(object);
21672185}
21682186
......@@ -2201,7 +2219,7 @@ fn parseArchive(wasm: *Wasm, obj: link.Input.Object) !void {
22012219 try wasm.objects.ensureUnusedCapacity(gpa, offsets.count());
22022220 for (offsets.keys()) |file_offset| {
22032221 const contents = file_contents[file_offset..];
2204 const object = try archive.parseObject(wasm, contents, obj.path, wasm.host_name, &ss, obj.must_link, gc_sections);
2222 const object = try archive.parseObject(wasm, contents, obj.path, wasm.object_host_name, &ss, obj.must_link, gc_sections);
22052223 wasm.objects.appendAssumeCapacity(object);
22062224 }
22072225}
......@@ -2313,6 +2331,7 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index
23132331 assert(!wasm.navs_exe.contains(nav_index));
23142332 }
23152333 const name = try wasm.internString(ext.name.toSlice(ip));
2334 if (ext.lib_name.toSlice(ip)) |ext_name| _ = try wasm.internString(ext_name);
23162335 try wasm.imports.ensureUnusedCapacity(gpa, 1);
23172336 if (ip.isFunctionType(nav.typeOf(ip))) {
23182337 try wasm.function_imports.ensureUnusedCapacity(gpa, 1);
src/link/Wasm/Archive.zig+1-1
......@@ -147,7 +147,7 @@ pub fn parseObject(
147147 wasm: *Wasm,
148148 file_contents: []const u8,
149149 path: Path,
150 host_name: Wasm.String,
150 host_name: Wasm.OptionalString,
151151 scratch_space: *Object.ScratchSpace,
152152 must_link: bool,
153153 gc_sections: bool,
src/link/Wasm/Flush.zig+5-3
......@@ -105,6 +105,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
105105
106106 if (!allow_undefined) {
107107 for (f.function_imports.keys(), f.function_imports.values()) |name, function_import_id| {
108 if (function_import_id.undefinedAllowed(wasm)) continue;
108109 const src_loc = function_import_id.sourceLocation(wasm);
109110 src_loc.addError(wasm, "undefined function: {s}", .{name.slice(wasm)});
110111 }
......@@ -403,7 +404,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
403404 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
404405
405406 for (f.function_imports.values()) |id| {
406 const module_name = id.moduleName(wasm).slice(wasm);
407 const module_name = id.moduleName(wasm).slice(wasm).?;
407408 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
408409 try binary_writer.writeAll(module_name);
409410
......@@ -437,7 +438,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
437438 total_imports += 1;
438439 } else if (import_memory) {
439440 try emitMemoryImport(wasm, binary_bytes, &.{
440 .module_name = wasm.host_name,
441 // TODO the import_memory option needs to specify from which module
442 .module_name = wasm.object_host_name.unwrap().?,
441443 .name = if (is_obj) wasm.preloaded_strings.__linear_memory else wasm.preloaded_strings.memory,
442444 .limits_min = wasm.memories.limits.min,
443445 .limits_max = wasm.memories.limits.max,
......@@ -448,7 +450,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
448450 }
449451
450452 for (f.global_imports.values()) |id| {
451 const module_name = id.moduleName(wasm).slice(wasm);
453 const module_name = id.moduleName(wasm).slice(wasm).?;
452454 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
453455 try binary_writer.writeAll(module_name);
454456
src/link/Wasm/Object.zig+2-2
......@@ -179,7 +179,7 @@ pub fn parse(
179179 bytes: []const u8,
180180 path: Path,
181181 archive_member_name: ?[]const u8,
182 host_name: Wasm.String,
182 host_name: Wasm.OptionalString,
183183 ss: *ScratchSpace,
184184 must_link: bool,
185185 gc_sections: bool,
......@@ -560,7 +560,7 @@ pub fn parse(
560560 .mutable = mutable,
561561 },
562562 },
563 .module_name = interned_module_name,
563 .module_name = interned_module_name.toOptional(),
564564 .source_location = source_location,
565565 .resolution = .unresolved,
566566 });