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...@@ -1587,6 +1587,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
1587 .pdb_source_path = options.pdb_source_path,1587 .pdb_source_path = options.pdb_source_path,
1588 .pdb_out_path = options.pdb_out_path,1588 .pdb_out_path = options.pdb_out_path,
1589 .entry_addr = null, // CLI does not expose this option (yet?)1589 .entry_addr = null, // CLI does not expose this option (yet?)
1590 .object_host_name = null, // TODO expose in the CLI
1590 };1591 };
15911592
1592 switch (options.cache_mode) {1593 switch (options.cache_mode) {
src/link.zig+1
...@@ -400,6 +400,7 @@ pub const File = struct {...@@ -400,6 +400,7 @@ pub const File = struct {
400 export_table: bool,400 export_table: bool,
401 initial_memory: ?u64,401 initial_memory: ?u64,
402 max_memory: ?u64,402 max_memory: ?u64,
403 object_host_name: ?[]const u8,
403 export_symbol_names: []const []const u8,404 export_symbol_names: []const []const u8,
404 global_base: ?u64,405 global_base: ?u64,
405 build_id: std.zig.BuildId,406 build_id: std.zig.BuildId,
src/link/Wasm.zig+39-20
...@@ -150,10 +150,10 @@ nav_fixups: std.ArrayListUnmanaged(NavFixup) = .empty,...@@ -150,10 +150,10 @@ nav_fixups: std.ArrayListUnmanaged(NavFixup) = .empty,
150symbol_table: std.AutoArrayHashMapUnmanaged(String, void) = .empty,150symbol_table: std.AutoArrayHashMapUnmanaged(String, void) = .empty,
151151
152/// When importing objects from the host environment, a name must be supplied.152/// 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 Zig153/// LLVM uses "env" by default when none is given.
154/// to support existing code.154/// This value is passed to object files since wasm tooling conventions provides
155/// TODO: Allow setting this through a flag?155/// no way to specify the module name in the symbol table.
156host_name: String,156object_host_name: OptionalString,
157157
158/// Memory section158/// Memory section
159memories: std.wasm.Memory = .{ .limits = .{159memories: std.wasm.Memory = .{ .limits = .{
...@@ -737,7 +737,7 @@ const DebugSection = struct {};...@@ -737,7 +737,7 @@ const DebugSection = struct {};
737737
738pub const FunctionImport = extern struct {738pub const FunctionImport = extern struct {
739 flags: SymbolFlags,739 flags: SymbolFlags,
740 module_name: String,740 module_name: OptionalString,
741 source_location: SourceLocation,741 source_location: SourceLocation,
742 resolution: Resolution,742 resolution: Resolution,
743 type: FunctionType.Index,743 type: FunctionType.Index,
...@@ -862,7 +862,7 @@ pub const FunctionImport = extern struct {...@@ -862,7 +862,7 @@ pub const FunctionImport = extern struct {
862 return index.key(wasm).*;862 return index.key(wasm).*;
863 }863 }
864864
865 pub fn moduleName(index: Index, wasm: *const Wasm) String {865 pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString {
866 return index.value(wasm).module_name;866 return index.value(wasm).module_name;
867 }867 }
868868
...@@ -888,7 +888,7 @@ pub const Function = extern struct {...@@ -888,7 +888,7 @@ pub const Function = extern struct {
888888
889pub const GlobalImport = extern struct {889pub const GlobalImport = extern struct {
890 flags: SymbolFlags,890 flags: SymbolFlags,
891 module_name: String,891 module_name: OptionalString,
892 source_location: SourceLocation,892 source_location: SourceLocation,
893 resolution: Resolution,893 resolution: Resolution,
894894
...@@ -1009,7 +1009,7 @@ pub const GlobalImport = extern struct {...@@ -1009,7 +1009,7 @@ pub const GlobalImport = extern struct {
1009 return index.key(wasm).*;1009 return index.key(wasm).*;
1010 }1010 }
10111011
1012 pub fn moduleName(index: Index, wasm: *const Wasm) String {1012 pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString {
1013 return index.value(wasm).module_name;1013 return index.value(wasm).module_name;
1014 }1014 }
10151015
...@@ -1114,7 +1114,7 @@ pub const TableImport = extern struct {...@@ -1114,7 +1114,7 @@ pub const TableImport = extern struct {
1114 return index.key(wasm).*;1114 return index.key(wasm).*;
1115 }1115 }
11161116
1117 pub fn moduleName(index: Index, wasm: *const Wasm) String {1117 pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString {
1118 return index.value(wasm).module_name;1118 return index.value(wasm).module_name;
1119 }1119 }
1120 };1120 };
...@@ -1604,7 +1604,7 @@ pub const ZcuImportIndex = enum(u32) {...@@ -1604,7 +1604,7 @@ pub const ZcuImportIndex = enum(u32) {
1604 return wasm.getExistingString(name_slice).?;1604 return wasm.getExistingString(name_slice).?;
1605 }1605 }
16061606
1607 pub fn moduleName(index: ZcuImportIndex, wasm: *const Wasm) String {1607 pub fn moduleName(index: ZcuImportIndex, wasm: *const Wasm) OptionalString {
1608 const zcu = wasm.base.comp.zcu.?;1608 const zcu = wasm.base.comp.zcu.?;
1609 const ip = &zcu.intern_pool;1609 const ip = &zcu.intern_pool;
1610 const nav_index = index.ptr(wasm).*;1610 const nav_index = index.ptr(wasm).*;
...@@ -1613,8 +1613,8 @@ pub const ZcuImportIndex = enum(u32) {...@@ -1613,8 +1613,8 @@ pub const ZcuImportIndex = enum(u32) {
1613 .@"extern" => |*ext| ext,1613 .@"extern" => |*ext| ext,
1614 else => unreachable,1614 else => unreachable,
1615 };1615 };
1616 const lib_name = ext.lib_name.toSlice(ip) orelse return wasm.host_name;1616 const lib_name = ext.lib_name.toSlice(ip) orelse return .none;
1617 return wasm.getExistingString(lib_name).?;1617 return wasm.getExistingString(lib_name).?.toOptional();
1618 }1618 }
16191619
1620 pub fn functionType(index: ZcuImportIndex, wasm: *Wasm) FunctionType.Index {1620 pub fn functionType(index: ZcuImportIndex, wasm: *Wasm) FunctionType.Index {
...@@ -1639,8 +1639,8 @@ pub const ZcuImportIndex = enum(u32) {...@@ -1639,8 +1639,8 @@ pub const ZcuImportIndex = enum(u32) {
1639 }1639 }
1640};1640};
16411641
1642/// 0. Index into `object_function_imports`.1642/// 0. Index into `Wasm.object_function_imports`.
1643/// 1. Index into `imports`.1643/// 1. Index into `Wasm.imports`.
1644pub const FunctionImportId = enum(u32) {1644pub const FunctionImportId = enum(u32) {
1645 _,1645 _,
16461646
...@@ -1695,7 +1695,7 @@ pub const FunctionImportId = enum(u32) {...@@ -1695,7 +1695,7 @@ pub const FunctionImportId = enum(u32) {
1695 };1695 };
1696 }1696 }
16971697
1698 pub fn moduleName(id: FunctionImportId, wasm: *const Wasm) String {1698 pub fn moduleName(id: FunctionImportId, wasm: *const Wasm) OptionalString {
1699 return switch (unpack(id, wasm)) {1699 return switch (unpack(id, wasm)) {
1700 inline .object_function_import, .zcu_import => |i| i.moduleName(wasm),1700 inline .object_function_import, .zcu_import => |i| i.moduleName(wasm),
1701 };1701 };
...@@ -1706,6 +1706,24 @@ pub const FunctionImportId = enum(u32) {...@@ -1706,6 +1706,24 @@ pub const FunctionImportId = enum(u32) {
1706 inline .object_function_import, .zcu_import => |i| i.functionType(wasm),1706 inline .object_function_import, .zcu_import => |i| i.functionType(wasm),
1707 };1707 };
1708 }1708 }
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 }
1709};1727};
17101728
1711/// 0. Index into `object_global_imports`.1729/// 0. Index into `object_global_imports`.
...@@ -1760,7 +1778,7 @@ pub const GlobalImportId = enum(u32) {...@@ -1760,7 +1778,7 @@ pub const GlobalImportId = enum(u32) {
1760 };1778 };
1761 }1779 }
17621780
1763 pub fn moduleName(id: GlobalImportId, wasm: *const Wasm) String {1781 pub fn moduleName(id: GlobalImportId, wasm: *const Wasm) OptionalString {
1764 return switch (unpack(id, wasm)) {1782 return switch (unpack(id, wasm)) {
1765 inline .object_global_import, .zcu_import => |i| i.moduleName(wasm),1783 inline .object_global_import, .zcu_import => |i| i.moduleName(wasm),
1766 };1784 };
...@@ -2082,7 +2100,7 @@ pub fn createEmpty(...@@ -2082,7 +2100,7 @@ pub fn createEmpty(
20822100
2083 .entry_name = undefined,2101 .entry_name = undefined,
2084 .dump_argv_list = .empty,2102 .dump_argv_list = .empty,
2085 .host_name = undefined,2103 .object_host_name = .none,
2086 .preloaded_strings = undefined,2104 .preloaded_strings = undefined,
2087 };2105 };
2088 if (use_llvm and comp.config.have_zcu) {2106 if (use_llvm and comp.config.have_zcu) {
...@@ -2090,7 +2108,7 @@ pub fn createEmpty(...@@ -2090,7 +2108,7 @@ pub fn createEmpty(
2090 }2108 }
2091 errdefer wasm.base.destroy();2109 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
2095 inline for (@typeInfo(PreloadedStrings).@"struct".fields) |field| {2113 inline for (@typeInfo(PreloadedStrings).@"struct".fields) |field| {
2096 @field(wasm.preloaded_strings, field.name) = try wasm.internString(field.name);2114 @field(wasm.preloaded_strings, field.name) = try wasm.internString(field.name);
...@@ -2162,7 +2180,7 @@ fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void {...@@ -2162,7 +2180,7 @@ fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void {
2162 var ss: Object.ScratchSpace = .{};2180 var ss: Object.ScratchSpace = .{};
2163 defer ss.deinit(gpa);2181 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);
2166 wasm.objects.appendAssumeCapacity(object);2184 wasm.objects.appendAssumeCapacity(object);
2167}2185}
21682186
...@@ -2201,7 +2219,7 @@ fn parseArchive(wasm: *Wasm, obj: link.Input.Object) !void {...@@ -2201,7 +2219,7 @@ fn parseArchive(wasm: *Wasm, obj: link.Input.Object) !void {
2201 try wasm.objects.ensureUnusedCapacity(gpa, offsets.count());2219 try wasm.objects.ensureUnusedCapacity(gpa, offsets.count());
2202 for (offsets.keys()) |file_offset| {2220 for (offsets.keys()) |file_offset| {
2203 const contents = file_contents[file_offset..];2221 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);
2205 wasm.objects.appendAssumeCapacity(object);2223 wasm.objects.appendAssumeCapacity(object);
2206 }2224 }
2207}2225}
...@@ -2313,6 +2331,7 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index...@@ -2313,6 +2331,7 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index
2313 assert(!wasm.navs_exe.contains(nav_index));2331 assert(!wasm.navs_exe.contains(nav_index));
2314 }2332 }
2315 const name = try wasm.internString(ext.name.toSlice(ip));2333 const name = try wasm.internString(ext.name.toSlice(ip));
2334 if (ext.lib_name.toSlice(ip)) |ext_name| _ = try wasm.internString(ext_name);
2316 try wasm.imports.ensureUnusedCapacity(gpa, 1);2335 try wasm.imports.ensureUnusedCapacity(gpa, 1);
2317 if (ip.isFunctionType(nav.typeOf(ip))) {2336 if (ip.isFunctionType(nav.typeOf(ip))) {
2318 try wasm.function_imports.ensureUnusedCapacity(gpa, 1);2337 try wasm.function_imports.ensureUnusedCapacity(gpa, 1);
src/link/Wasm/Archive.zig+1-1
...@@ -147,7 +147,7 @@ pub fn parseObject(...@@ -147,7 +147,7 @@ pub fn parseObject(
147 wasm: *Wasm,147 wasm: *Wasm,
148 file_contents: []const u8,148 file_contents: []const u8,
149 path: Path,149 path: Path,
150 host_name: Wasm.String,150 host_name: Wasm.OptionalString,
151 scratch_space: *Object.ScratchSpace,151 scratch_space: *Object.ScratchSpace,
152 must_link: bool,152 must_link: bool,
153 gc_sections: bool,153 gc_sections: bool,
src/link/Wasm/Flush.zig+5-3
...@@ -105,6 +105,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -105,6 +105,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
105105
106 if (!allow_undefined) {106 if (!allow_undefined) {
107 for (f.function_imports.keys(), f.function_imports.values()) |name, function_import_id| {107 for (f.function_imports.keys(), f.function_imports.values()) |name, function_import_id| {
108 if (function_import_id.undefinedAllowed(wasm)) continue;
108 const src_loc = function_import_id.sourceLocation(wasm);109 const src_loc = function_import_id.sourceLocation(wasm);
109 src_loc.addError(wasm, "undefined function: {s}", .{name.slice(wasm)});110 src_loc.addError(wasm, "undefined function: {s}", .{name.slice(wasm)});
110 }111 }
...@@ -403,7 +404,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -403,7 +404,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
403 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);404 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
404405
405 for (f.function_imports.values()) |id| {406 for (f.function_imports.values()) |id| {
406 const module_name = id.moduleName(wasm).slice(wasm);407 const module_name = id.moduleName(wasm).slice(wasm).?;
407 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));408 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
408 try binary_writer.writeAll(module_name);409 try binary_writer.writeAll(module_name);
409410
...@@ -437,7 +438,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -437,7 +438,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
437 total_imports += 1;438 total_imports += 1;
438 } else if (import_memory) {439 } else if (import_memory) {
439 try emitMemoryImport(wasm, binary_bytes, &.{440 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().?,
441 .name = if (is_obj) wasm.preloaded_strings.__linear_memory else wasm.preloaded_strings.memory,443 .name = if (is_obj) wasm.preloaded_strings.__linear_memory else wasm.preloaded_strings.memory,
442 .limits_min = wasm.memories.limits.min,444 .limits_min = wasm.memories.limits.min,
443 .limits_max = wasm.memories.limits.max,445 .limits_max = wasm.memories.limits.max,
...@@ -448,7 +450,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -448,7 +450,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
448 }450 }
449451
450 for (f.global_imports.values()) |id| {452 for (f.global_imports.values()) |id| {
451 const module_name = id.moduleName(wasm).slice(wasm);453 const module_name = id.moduleName(wasm).slice(wasm).?;
452 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));454 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
453 try binary_writer.writeAll(module_name);455 try binary_writer.writeAll(module_name);
454456
src/link/Wasm/Object.zig+2-2
...@@ -179,7 +179,7 @@ pub fn parse(...@@ -179,7 +179,7 @@ pub fn parse(
179 bytes: []const u8,179 bytes: []const u8,
180 path: Path,180 path: Path,
181 archive_member_name: ?[]const u8,181 archive_member_name: ?[]const u8,
182 host_name: Wasm.String,182 host_name: Wasm.OptionalString,
183 ss: *ScratchSpace,183 ss: *ScratchSpace,
184 must_link: bool,184 must_link: bool,
185 gc_sections: bool,185 gc_sections: bool,
...@@ -560,7 +560,7 @@ pub fn parse(...@@ -560,7 +560,7 @@ pub fn parse(
560 .mutable = mutable,560 .mutable = mutable,
561 },561 },
562 },562 },
563 .module_name = interned_module_name,563 .module_name = interned_module_name.toOptional(),
564 .source_location = source_location,564 .source_location = source_location,
565 .resolution = .unresolved,565 .resolution = .unresolved,
566 });566 });