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.
This commit is contained in:
@@ -1587,6 +1587,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
|
||||
.pdb_source_path = options.pdb_source_path,
|
||||
.pdb_out_path = options.pdb_out_path,
|
||||
.entry_addr = null, // CLI does not expose this option (yet?)
|
||||
.object_host_name = null, // TODO expose in the CLI
|
||||
};
|
||||
|
||||
switch (options.cache_mode) {
|
||||
|
||||
@@ -400,6 +400,7 @@ pub const File = struct {
|
||||
export_table: bool,
|
||||
initial_memory: ?u64,
|
||||
max_memory: ?u64,
|
||||
object_host_name: ?[]const u8,
|
||||
export_symbol_names: []const []const u8,
|
||||
global_base: ?u64,
|
||||
build_id: std.zig.BuildId,
|
||||
|
||||
@@ -150,10 +150,10 @@ nav_fixups: std.ArrayListUnmanaged(NavFixup) = .empty,
|
||||
symbol_table: std.AutoArrayHashMapUnmanaged(String, void) = .empty,
|
||||
|
||||
/// When importing objects from the host environment, a name must be supplied.
|
||||
/// LLVM uses "env" by default when none is given. This would be a good default for Zig
|
||||
/// to support existing code.
|
||||
/// TODO: Allow setting this through a flag?
|
||||
host_name: String,
|
||||
/// LLVM uses "env" by default when none is given.
|
||||
/// This value is passed to object files since wasm tooling conventions provides
|
||||
/// no way to specify the module name in the symbol table.
|
||||
object_host_name: OptionalString,
|
||||
|
||||
/// Memory section
|
||||
memories: std.wasm.Memory = .{ .limits = .{
|
||||
@@ -737,7 +737,7 @@ const DebugSection = struct {};
|
||||
|
||||
pub const FunctionImport = extern struct {
|
||||
flags: SymbolFlags,
|
||||
module_name: String,
|
||||
module_name: OptionalString,
|
||||
source_location: SourceLocation,
|
||||
resolution: Resolution,
|
||||
type: FunctionType.Index,
|
||||
@@ -862,7 +862,7 @@ pub const FunctionImport = extern struct {
|
||||
return index.key(wasm).*;
|
||||
}
|
||||
|
||||
pub fn moduleName(index: Index, wasm: *const Wasm) String {
|
||||
pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString {
|
||||
return index.value(wasm).module_name;
|
||||
}
|
||||
|
||||
@@ -888,7 +888,7 @@ pub const Function = extern struct {
|
||||
|
||||
pub const GlobalImport = extern struct {
|
||||
flags: SymbolFlags,
|
||||
module_name: String,
|
||||
module_name: OptionalString,
|
||||
source_location: SourceLocation,
|
||||
resolution: Resolution,
|
||||
|
||||
@@ -1009,7 +1009,7 @@ pub const GlobalImport = extern struct {
|
||||
return index.key(wasm).*;
|
||||
}
|
||||
|
||||
pub fn moduleName(index: Index, wasm: *const Wasm) String {
|
||||
pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString {
|
||||
return index.value(wasm).module_name;
|
||||
}
|
||||
|
||||
@@ -1114,7 +1114,7 @@ pub const TableImport = extern struct {
|
||||
return index.key(wasm).*;
|
||||
}
|
||||
|
||||
pub fn moduleName(index: Index, wasm: *const Wasm) String {
|
||||
pub fn moduleName(index: Index, wasm: *const Wasm) OptionalString {
|
||||
return index.value(wasm).module_name;
|
||||
}
|
||||
};
|
||||
@@ -1604,7 +1604,7 @@ pub const ZcuImportIndex = enum(u32) {
|
||||
return wasm.getExistingString(name_slice).?;
|
||||
}
|
||||
|
||||
pub fn moduleName(index: ZcuImportIndex, wasm: *const Wasm) String {
|
||||
pub fn moduleName(index: ZcuImportIndex, wasm: *const Wasm) OptionalString {
|
||||
const zcu = wasm.base.comp.zcu.?;
|
||||
const ip = &zcu.intern_pool;
|
||||
const nav_index = index.ptr(wasm).*;
|
||||
@@ -1613,8 +1613,8 @@ pub const ZcuImportIndex = enum(u32) {
|
||||
.@"extern" => |*ext| ext,
|
||||
else => unreachable,
|
||||
};
|
||||
const lib_name = ext.lib_name.toSlice(ip) orelse return wasm.host_name;
|
||||
return wasm.getExistingString(lib_name).?;
|
||||
const lib_name = ext.lib_name.toSlice(ip) orelse return .none;
|
||||
return wasm.getExistingString(lib_name).?.toOptional();
|
||||
}
|
||||
|
||||
pub fn functionType(index: ZcuImportIndex, wasm: *Wasm) FunctionType.Index {
|
||||
@@ -1639,8 +1639,8 @@ pub const ZcuImportIndex = enum(u32) {
|
||||
}
|
||||
};
|
||||
|
||||
/// 0. Index into `object_function_imports`.
|
||||
/// 1. Index into `imports`.
|
||||
/// 0. Index into `Wasm.object_function_imports`.
|
||||
/// 1. Index into `Wasm.imports`.
|
||||
pub const FunctionImportId = enum(u32) {
|
||||
_,
|
||||
|
||||
@@ -1695,7 +1695,7 @@ pub const FunctionImportId = enum(u32) {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn moduleName(id: FunctionImportId, wasm: *const Wasm) String {
|
||||
pub fn moduleName(id: FunctionImportId, wasm: *const Wasm) OptionalString {
|
||||
return switch (unpack(id, wasm)) {
|
||||
inline .object_function_import, .zcu_import => |i| i.moduleName(wasm),
|
||||
};
|
||||
@@ -1706,6 +1706,24 @@ pub const FunctionImportId = enum(u32) {
|
||||
inline .object_function_import, .zcu_import => |i| i.functionType(wasm),
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts not emitting an object, and `Wasm.import_symbols` is false.
|
||||
pub fn undefinedAllowed(id: FunctionImportId, wasm: *const Wasm) bool {
|
||||
assert(!wasm.import_symbols);
|
||||
assert(wasm.base.comp.config.output_mode != .Obj);
|
||||
return switch (unpack(id, wasm)) {
|
||||
.object_function_import => |i| {
|
||||
const import = i.value(wasm);
|
||||
return import.flags.binding == .strong and import.module_name != .none;
|
||||
},
|
||||
.zcu_import => |i| {
|
||||
const zcu = wasm.base.comp.zcu.?;
|
||||
const ip = &zcu.intern_pool;
|
||||
const ext = ip.getNav(i.ptr(wasm).*).toExtern(ip).?;
|
||||
return !ext.is_weak_linkage and ext.lib_name != .none;
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// 0. Index into `object_global_imports`.
|
||||
@@ -1760,7 +1778,7 @@ pub const GlobalImportId = enum(u32) {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn moduleName(id: GlobalImportId, wasm: *const Wasm) String {
|
||||
pub fn moduleName(id: GlobalImportId, wasm: *const Wasm) OptionalString {
|
||||
return switch (unpack(id, wasm)) {
|
||||
inline .object_global_import, .zcu_import => |i| i.moduleName(wasm),
|
||||
};
|
||||
@@ -2082,7 +2100,7 @@ pub fn createEmpty(
|
||||
|
||||
.entry_name = undefined,
|
||||
.dump_argv_list = .empty,
|
||||
.host_name = undefined,
|
||||
.object_host_name = .none,
|
||||
.preloaded_strings = undefined,
|
||||
};
|
||||
if (use_llvm and comp.config.have_zcu) {
|
||||
@@ -2090,7 +2108,7 @@ pub fn createEmpty(
|
||||
}
|
||||
errdefer wasm.base.destroy();
|
||||
|
||||
wasm.host_name = try wasm.internString("env");
|
||||
if (options.object_host_name) |name| wasm.object_host_name = (try wasm.internString(name)).toOptional();
|
||||
|
||||
inline for (@typeInfo(PreloadedStrings).@"struct".fields) |field| {
|
||||
@field(wasm.preloaded_strings, field.name) = try wasm.internString(field.name);
|
||||
@@ -2162,7 +2180,7 @@ fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void {
|
||||
var ss: Object.ScratchSpace = .{};
|
||||
defer ss.deinit(gpa);
|
||||
|
||||
const object = try Object.parse(wasm, file_contents, obj.path, null, wasm.host_name, &ss, obj.must_link, gc_sections);
|
||||
const object = try Object.parse(wasm, file_contents, obj.path, null, wasm.object_host_name, &ss, obj.must_link, gc_sections);
|
||||
wasm.objects.appendAssumeCapacity(object);
|
||||
}
|
||||
|
||||
@@ -2201,7 +2219,7 @@ fn parseArchive(wasm: *Wasm, obj: link.Input.Object) !void {
|
||||
try wasm.objects.ensureUnusedCapacity(gpa, offsets.count());
|
||||
for (offsets.keys()) |file_offset| {
|
||||
const contents = file_contents[file_offset..];
|
||||
const object = try archive.parseObject(wasm, contents, obj.path, wasm.host_name, &ss, obj.must_link, gc_sections);
|
||||
const object = try archive.parseObject(wasm, contents, obj.path, wasm.object_host_name, &ss, obj.must_link, gc_sections);
|
||||
wasm.objects.appendAssumeCapacity(object);
|
||||
}
|
||||
}
|
||||
@@ -2313,6 +2331,7 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index
|
||||
assert(!wasm.navs_exe.contains(nav_index));
|
||||
}
|
||||
const name = try wasm.internString(ext.name.toSlice(ip));
|
||||
if (ext.lib_name.toSlice(ip)) |ext_name| _ = try wasm.internString(ext_name);
|
||||
try wasm.imports.ensureUnusedCapacity(gpa, 1);
|
||||
if (ip.isFunctionType(nav.typeOf(ip))) {
|
||||
try wasm.function_imports.ensureUnusedCapacity(gpa, 1);
|
||||
|
||||
@@ -147,7 +147,7 @@ pub fn parseObject(
|
||||
wasm: *Wasm,
|
||||
file_contents: []const u8,
|
||||
path: Path,
|
||||
host_name: Wasm.String,
|
||||
host_name: Wasm.OptionalString,
|
||||
scratch_space: *Object.ScratchSpace,
|
||||
must_link: bool,
|
||||
gc_sections: bool,
|
||||
|
||||
@@ -105,6 +105,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
|
||||
|
||||
if (!allow_undefined) {
|
||||
for (f.function_imports.keys(), f.function_imports.values()) |name, function_import_id| {
|
||||
if (function_import_id.undefinedAllowed(wasm)) continue;
|
||||
const src_loc = function_import_id.sourceLocation(wasm);
|
||||
src_loc.addError(wasm, "undefined function: {s}", .{name.slice(wasm)});
|
||||
}
|
||||
@@ -403,7 +404,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
|
||||
const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
|
||||
|
||||
for (f.function_imports.values()) |id| {
|
||||
const module_name = id.moduleName(wasm).slice(wasm);
|
||||
const module_name = id.moduleName(wasm).slice(wasm).?;
|
||||
try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
|
||||
try binary_writer.writeAll(module_name);
|
||||
|
||||
@@ -437,7 +438,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
|
||||
total_imports += 1;
|
||||
} else if (import_memory) {
|
||||
try emitMemoryImport(wasm, binary_bytes, &.{
|
||||
.module_name = wasm.host_name,
|
||||
// TODO the import_memory option needs to specify from which module
|
||||
.module_name = wasm.object_host_name.unwrap().?,
|
||||
.name = if (is_obj) wasm.preloaded_strings.__linear_memory else wasm.preloaded_strings.memory,
|
||||
.limits_min = wasm.memories.limits.min,
|
||||
.limits_max = wasm.memories.limits.max,
|
||||
@@ -448,7 +450,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
|
||||
}
|
||||
|
||||
for (f.global_imports.values()) |id| {
|
||||
const module_name = id.moduleName(wasm).slice(wasm);
|
||||
const module_name = id.moduleName(wasm).slice(wasm).?;
|
||||
try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));
|
||||
try binary_writer.writeAll(module_name);
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ pub fn parse(
|
||||
bytes: []const u8,
|
||||
path: Path,
|
||||
archive_member_name: ?[]const u8,
|
||||
host_name: Wasm.String,
|
||||
host_name: Wasm.OptionalString,
|
||||
ss: *ScratchSpace,
|
||||
must_link: bool,
|
||||
gc_sections: bool,
|
||||
@@ -560,7 +560,7 @@ pub fn parse(
|
||||
.mutable = mutable,
|
||||
},
|
||||
},
|
||||
.module_name = interned_module_name,
|
||||
.module_name = interned_module_name.toOptional(),
|
||||
.source_location = source_location,
|
||||
.resolution = .unresolved,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user