Skip to content

Commit df7b83b

Browse files
committed
wasm linker: ability to get data and functions from objects
1 parent e812b5a commit df7b83b

File tree

2 files changed

+106
-42
lines changed

2 files changed

+106
-42
lines changed

src/link/Wasm.zig

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,10 @@ dump_argv_list: std.ArrayListUnmanaged([]const u8),
180180
preloaded_strings: PreloadedStrings,
181181

182182
/// This field is used when emitting an object; `navs_exe` used otherwise.
183+
/// Does not include externs since that data lives elsewhere.
183184
navs_obj: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ZcuDataObj) = .empty,
184185
/// This field is unused when emitting an object; `navs_obj` used otherwise.
186+
/// Does not include externs since that data lives elsewhere.
185187
navs_exe: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ZcuDataExe) = .empty,
186188
/// Tracks all InternPool values referenced by codegen. Needed for outputting
187189
/// the data segment. This one does not track ref count because object files
@@ -221,6 +223,9 @@ functions: std.AutoArrayHashMapUnmanaged(FunctionImport.Resolution, void) = .emp
221223
/// Tracks the value at the end of prelink, at which point `functions`
222224
/// contains only object file functions, and nothing from the Zcu yet.
223225
functions_end_prelink: u32 = 0,
226+
227+
function_imports_len_prelink: u32 = 0,
228+
data_imports_len_prelink: u32 = 0,
224229
/// At the end of prelink, this is populated with needed functions from
225230
/// objects.
226231
///
@@ -3447,6 +3452,9 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v
34473452
wasm.memories.limits.max = @max(wasm.memories.limits.max, memory_import.limits_max);
34483453
wasm.memories.limits.flags.has_max = wasm.memories.limits.flags.has_max or memory_import.limits_has_max;
34493454
}
3455+
3456+
wasm.function_imports_len_prelink = @intCast(wasm.function_imports.entries.len);
3457+
wasm.data_imports_len_prelink = @intCast(wasm.data_imports.entries.len);
34503458
}
34513459

34523460
pub fn markFunctionImport(
@@ -3608,7 +3616,7 @@ fn markDataSegment(wasm: *Wasm, segment_index: ObjectDataSegment.Index) link.Fil
36083616
try wasm.markRelocations(segment.relocations(wasm));
36093617
}
36103618

3611-
fn markDataImport(
3619+
pub fn markDataImport(
36123620
wasm: *Wasm,
36133621
name: String,
36143622
import: *ObjectDataImport,
@@ -4499,11 +4507,40 @@ pub fn navAddr(wasm: *Wasm, nav_index: InternPool.Nav.Index) u32 {
44994507
assert(wasm.flush_buffer.memory_layout_finished);
45004508
const comp = wasm.base.comp;
45014509
assert(comp.config.output_mode != .Obj);
4502-
// If there is no entry it means the value is zero bits so any address will do.
4503-
const navs_exe_index: NavsExeIndex = @enumFromInt(wasm.navs_exe.getIndex(nav_index) orelse return 0);
4504-
log.debug("navAddr {s} {}", .{ navs_exe_index.name(wasm), nav_index });
4505-
const ds_id: DataSegmentId = .pack(wasm, .{ .nav_exe = navs_exe_index });
4506-
return wasm.flush_buffer.data_segments.get(ds_id).?;
4510+
if (wasm.navs_exe.getIndex(nav_index)) |i| {
4511+
const navs_exe_index: NavsExeIndex = @enumFromInt(i);
4512+
log.debug("navAddr {s} {}", .{ navs_exe_index.name(wasm), nav_index });
4513+
const ds_id: DataSegmentId = .pack(wasm, .{ .nav_exe = navs_exe_index });
4514+
return wasm.flush_buffer.data_segments.get(ds_id).?;
4515+
}
4516+
const zcu = comp.zcu.?;
4517+
const ip = &zcu.intern_pool;
4518+
const nav = ip.getNav(nav_index);
4519+
if (nav.getResolvedExtern(ip)) |ext| {
4520+
if (wasm.getExistingString(ext.name.toSlice(ip))) |symbol_name| {
4521+
if (wasm.object_data_imports.getPtr(symbol_name)) |import| {
4522+
switch (import.resolution.unpack(wasm)) {
4523+
.unresolved => unreachable,
4524+
.object => |object_data_index| {
4525+
const object_data = object_data_index.ptr(wasm);
4526+
const ds_id: DataSegmentId = .fromObjectDataSegment(wasm, object_data.segment);
4527+
const segment_base_addr = wasm.flush_buffer.data_segments.get(ds_id).?;
4528+
return segment_base_addr + object_data.offset;
4529+
},
4530+
.__zig_error_names => @panic("TODO"),
4531+
.__zig_error_name_table => @panic("TODO"),
4532+
.__heap_base => @panic("TODO"),
4533+
.__heap_end => @panic("TODO"),
4534+
.uav_exe => @panic("TODO"),
4535+
.uav_obj => @panic("TODO"),
4536+
.nav_exe => @panic("TODO"),
4537+
.nav_obj => @panic("TODO"),
4538+
}
4539+
}
4540+
}
4541+
}
4542+
// Otherwise it's a zero bit type; any address will do.
4543+
return 0;
45074544
}
45084545

45094546
/// Asserts it is called after `Flush.data_segments` is fully populated and sorted.

src/link/Wasm/Flush.zig

Lines changed: 63 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -122,45 +122,71 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
122122

123123
const entry_name = if (wasm.entry_resolution.isNavOrUnresolved(wasm)) wasm.entry_name else .none;
124124

125-
// Detect any intrinsics that were called; they need to have dependencies on the symbols marked.
126-
// Likewise detect `@tagName` calls so those functions can be included in the output and synthesized.
127-
for (wasm.mir_instructions.items(.tag), wasm.mir_instructions.items(.data)) |tag, *data| switch (tag) {
128-
.call_intrinsic => {
129-
const symbol_name = try wasm.internString(@tagName(data.intrinsic));
130-
const i: Wasm.FunctionImport.Index = @enumFromInt(wasm.object_function_imports.getIndex(symbol_name) orelse {
131-
return diags.fail("missing compiler runtime intrinsic '{s}' (undefined linker symbol)", .{
132-
@tagName(data.intrinsic),
125+
if (comp.zcu) |zcu| {
126+
const ip: *const InternPool = &zcu.intern_pool; // No mutations allowed!
127+
128+
// Detect any intrinsics that were called; they need to have dependencies on the symbols marked.
129+
// Likewise detect `@tagName` calls so those functions can be included in the output and synthesized.
130+
for (wasm.mir_instructions.items(.tag), wasm.mir_instructions.items(.data)) |tag, *data| switch (tag) {
131+
.call_intrinsic => {
132+
const symbol_name = try wasm.internString(@tagName(data.intrinsic));
133+
const i: Wasm.FunctionImport.Index = @enumFromInt(wasm.object_function_imports.getIndex(symbol_name) orelse {
134+
return diags.fail("missing compiler runtime intrinsic '{s}' (undefined linker symbol)", .{
135+
@tagName(data.intrinsic),
136+
});
133137
});
134-
});
135-
try wasm.markFunctionImport(symbol_name, i.value(wasm), i);
136-
},
137-
.call_tag_name => {
138-
const zcu = comp.zcu.?;
139-
const ip = &zcu.intern_pool;
140-
assert(ip.indexToKey(data.ip_index) == .enum_type);
141-
const gop = try wasm.zcu_funcs.getOrPut(gpa, data.ip_index);
142-
if (!gop.found_existing) {
143-
wasm.tag_name_table_ref_count += 1;
144-
const int_tag_ty = Zcu.Type.fromInterned(data.ip_index).intTagType(zcu);
145-
gop.value_ptr.* = .{ .tag_name = .{
146-
.symbol_name = try wasm.internStringFmt("__zig_tag_name_{d}", .{@intFromEnum(data.ip_index)}),
147-
.type_index = try wasm.internFunctionType(.Unspecified, &.{int_tag_ty.ip_index}, .slice_const_u8_sentinel_0, target),
148-
.table_index = @intCast(wasm.tag_name_offs.items.len),
149-
} };
150-
try wasm.functions.put(gpa, .fromZcuFunc(wasm, @enumFromInt(gop.index)), {});
151-
const tag_names = ip.loadEnumType(data.ip_index).names;
152-
for (tag_names.get(ip)) |tag_name| {
153-
const slice = tag_name.toSlice(ip);
154-
try wasm.tag_name_offs.append(gpa, @intCast(wasm.tag_name_bytes.items.len));
155-
try wasm.tag_name_bytes.appendSlice(gpa, slice[0 .. slice.len + 1]);
138+
try wasm.markFunctionImport(symbol_name, i.value(wasm), i);
139+
},
140+
.call_tag_name => {
141+
assert(ip.indexToKey(data.ip_index) == .enum_type);
142+
const gop = try wasm.zcu_funcs.getOrPut(gpa, data.ip_index);
143+
if (!gop.found_existing) {
144+
wasm.tag_name_table_ref_count += 1;
145+
const int_tag_ty = Zcu.Type.fromInterned(data.ip_index).intTagType(zcu);
146+
gop.value_ptr.* = .{ .tag_name = .{
147+
.symbol_name = try wasm.internStringFmt("__zig_tag_name_{d}", .{@intFromEnum(data.ip_index)}),
148+
.type_index = try wasm.internFunctionType(.Unspecified, &.{int_tag_ty.ip_index}, .slice_const_u8_sentinel_0, target),
149+
.table_index = @intCast(wasm.tag_name_offs.items.len),
150+
} };
151+
try wasm.functions.put(gpa, .fromZcuFunc(wasm, @enumFromInt(gop.index)), {});
152+
const tag_names = ip.loadEnumType(data.ip_index).names;
153+
for (tag_names.get(ip)) |tag_name| {
154+
const slice = tag_name.toSlice(ip);
155+
try wasm.tag_name_offs.append(gpa, @intCast(wasm.tag_name_bytes.items.len));
156+
try wasm.tag_name_bytes.appendSlice(gpa, slice[0 .. slice.len + 1]);
157+
}
156158
}
159+
},
160+
else => continue,
161+
};
162+
163+
{
164+
var i = wasm.function_imports_len_prelink;
165+
while (i < f.function_imports.entries.len) {
166+
const symbol_name = f.function_imports.keys()[i];
167+
if (wasm.object_function_imports.getIndex(symbol_name)) |import_index_usize| {
168+
const import_index: Wasm.FunctionImport.Index = @enumFromInt(import_index_usize);
169+
try wasm.markFunctionImport(symbol_name, import_index.value(wasm), import_index);
170+
f.function_imports.swapRemoveAt(i);
171+
continue;
172+
}
173+
i += 1;
157174
}
158-
},
159-
else => continue,
160-
};
175+
}
161176

162-
if (comp.zcu) |zcu| {
163-
const ip: *const InternPool = &zcu.intern_pool; // No mutations allowed!
177+
{
178+
var i = wasm.data_imports_len_prelink;
179+
while (i < f.data_imports.entries.len) {
180+
const symbol_name = f.data_imports.keys()[i];
181+
if (wasm.object_data_imports.getIndex(symbol_name)) |import_index_usize| {
182+
const import_index: Wasm.ObjectDataImport.Index = @enumFromInt(import_index_usize);
183+
try wasm.markDataImport(symbol_name, import_index.value(wasm), import_index);
184+
f.data_imports.swapRemoveAt(i);
185+
continue;
186+
}
187+
i += 1;
188+
}
189+
}
164190

165191
if (wasm.error_name_table_ref_count > 0) {
166192
// Ensure Zcu error name structures are populated.
@@ -437,7 +463,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
437463
break :b i >= 1 and !wantSegmentMerge(wasm, segment_ids[i - 1], segment_id, category);
438464
};
439465
if (want_new_segment) {
440-
log.debug("new segment at 0x{x} {} {s} {}", .{ start_addr, segment_id, segment_id.name(wasm), category });
466+
log.debug("new segment group at 0x{x} {} {s} {}", .{ start_addr, segment_id, segment_id.name(wasm), category });
441467
try f.data_segment_groups.append(gpa, .{
442468
.end_addr = @intCast(memory_ptr),
443469
.first_segment = first_segment,
@@ -447,6 +473,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
447473

448474
const size = segment_id.size(wasm);
449475
segment_vaddr.* = @intCast(start_addr);
476+
log.debug("0x{x} {d} {s}", .{ start_addr, @intFromEnum(segment_id), segment_id.name(wasm) });
450477
memory_ptr = start_addr + size;
451478
}
452479
if (category != .zero) try f.data_segment_groups.append(gpa, .{

0 commit comments

Comments
 (0)