authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-14 23:38:36-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:37-08:00
logae1641412124a4b09c8274b18792c162bc3cf65b
tree3cd6eb62635f7a053381b9e53b2c2e87cc38a667
parenta7bd1a631b9dd5b1221fdc0f3e4b299bda18138c

wasm linker: ability to get data and functions from objects


2 files changed, 106 insertions(+), 42 deletions(-)

src/link/Wasm.zig+43-6
...@@ -180,8 +180,10 @@ dump_argv_list: std.ArrayListUnmanaged([]const u8),...@@ -180,8 +180,10 @@ dump_argv_list: std.ArrayListUnmanaged([]const u8),
180preloaded_strings: PreloadedStrings,180preloaded_strings: PreloadedStrings,
181181
182/// This field is used when emitting an object; `navs_exe` used otherwise.182/// This field is used when emitting an object; `navs_exe` used otherwise.
183/// Does not include externs since that data lives elsewhere.
183navs_obj: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ZcuDataObj) = .empty,184navs_obj: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ZcuDataObj) = .empty,
184/// This field is unused when emitting an object; `navs_obj` used otherwise.185/// This field is unused when emitting an object; `navs_obj` used otherwise.
186/// Does not include externs since that data lives elsewhere.
185navs_exe: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ZcuDataExe) = .empty,187navs_exe: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ZcuDataExe) = .empty,
186/// Tracks all InternPool values referenced by codegen. Needed for outputting188/// Tracks all InternPool values referenced by codegen. Needed for outputting
187/// the data segment. This one does not track ref count because object files189/// the data segment. This one does not track ref count because object files
...@@ -221,6 +223,9 @@ functions: std.AutoArrayHashMapUnmanaged(FunctionImport.Resolution, void) = .emp...@@ -221,6 +223,9 @@ functions: std.AutoArrayHashMapUnmanaged(FunctionImport.Resolution, void) = .emp
221/// Tracks the value at the end of prelink, at which point `functions`223/// Tracks the value at the end of prelink, at which point `functions`
222/// contains only object file functions, and nothing from the Zcu yet.224/// contains only object file functions, and nothing from the Zcu yet.
223functions_end_prelink: u32 = 0,225functions_end_prelink: u32 = 0,
226
227function_imports_len_prelink: u32 = 0,
228data_imports_len_prelink: u32 = 0,
224/// At the end of prelink, this is populated with needed functions from229/// At the end of prelink, this is populated with needed functions from
225/// objects.230/// objects.
226///231///
...@@ -3447,6 +3452,9 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v...@@ -3447,6 +3452,9 @@ pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!v
3447 wasm.memories.limits.max = @max(wasm.memories.limits.max, memory_import.limits_max);3452 wasm.memories.limits.max = @max(wasm.memories.limits.max, memory_import.limits_max);
3448 wasm.memories.limits.flags.has_max = wasm.memories.limits.flags.has_max or memory_import.limits_has_max;3453 wasm.memories.limits.flags.has_max = wasm.memories.limits.flags.has_max or memory_import.limits_has_max;
3449 }3454 }
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);
3450}3458}
34513459
3452pub fn markFunctionImport(3460pub fn markFunctionImport(
...@@ -3608,7 +3616,7 @@ fn markDataSegment(wasm: *Wasm, segment_index: ObjectDataSegment.Index) link.Fil...@@ -3608,7 +3616,7 @@ fn markDataSegment(wasm: *Wasm, segment_index: ObjectDataSegment.Index) link.Fil
3608 try wasm.markRelocations(segment.relocations(wasm));3616 try wasm.markRelocations(segment.relocations(wasm));
3609}3617}
36103618
3611fn markDataImport(3619pub fn markDataImport(
3612 wasm: *Wasm,3620 wasm: *Wasm,
3613 name: String,3621 name: String,
3614 import: *ObjectDataImport,3622 import: *ObjectDataImport,
...@@ -4499,11 +4507,40 @@ pub fn navAddr(wasm: *Wasm, nav_index: InternPool.Nav.Index) u32 {...@@ -4499,11 +4507,40 @@ pub fn navAddr(wasm: *Wasm, nav_index: InternPool.Nav.Index) u32 {
4499 assert(wasm.flush_buffer.memory_layout_finished);4507 assert(wasm.flush_buffer.memory_layout_finished);
4500 const comp = wasm.base.comp;4508 const comp = wasm.base.comp;
4501 assert(comp.config.output_mode != .Obj);4509 assert(comp.config.output_mode != .Obj);
4502 // If there is no entry it means the value is zero bits so any address will do.4510 if (wasm.navs_exe.getIndex(nav_index)) |i| {
4503 const navs_exe_index: NavsExeIndex = @enumFromInt(wasm.navs_exe.getIndex(nav_index) orelse return 0);4511 const navs_exe_index: NavsExeIndex = @enumFromInt(i);
4504 log.debug("navAddr {s} {}", .{ navs_exe_index.name(wasm), nav_index });4512 log.debug("navAddr {s} {}", .{ navs_exe_index.name(wasm), nav_index });
4505 const ds_id: DataSegmentId = .pack(wasm, .{ .nav_exe = navs_exe_index });4513 const ds_id: DataSegmentId = .pack(wasm, .{ .nav_exe = navs_exe_index });
4506 return wasm.flush_buffer.data_segments.get(ds_id).?;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;
4507}4544}
45084545
4509/// Asserts it is called after `Flush.data_segments` is fully populated and sorted.4546/// Asserts it is called after `Flush.data_segments` is fully populated and sorted.
src/link/Wasm/Flush.zig+63-36
...@@ -122,45 +122,71 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -122,45 +122,71 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
122122
123 const entry_name = if (wasm.entry_resolution.isNavOrUnresolved(wasm)) wasm.entry_name else .none;123 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.125 if (comp.zcu) |zcu| {
126 // Likewise detect `@tagName` calls so those functions can be included in the output and synthesized.126 const ip: *const InternPool = &zcu.intern_pool; // No mutations allowed!
127 for (wasm.mir_instructions.items(.tag), wasm.mir_instructions.items(.data)) |tag, *data| switch (tag) {127
128 .call_intrinsic => {128 // Detect any intrinsics that were called; they need to have dependencies on the symbols marked.
129 const symbol_name = try wasm.internString(@tagName(data.intrinsic));129 // Likewise detect `@tagName` calls so those functions can be included in the output and synthesized.
130 const i: Wasm.FunctionImport.Index = @enumFromInt(wasm.object_function_imports.getIndex(symbol_name) orelse {130 for (wasm.mir_instructions.items(.tag), wasm.mir_instructions.items(.data)) |tag, *data| switch (tag) {
131 return diags.fail("missing compiler runtime intrinsic '{s}' (undefined linker symbol)", .{131 .call_intrinsic => {
132 @tagName(data.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 });
133 });137 });
134 });138 try wasm.markFunctionImport(symbol_name, i.value(wasm), i);
135 try wasm.markFunctionImport(symbol_name, i.value(wasm), i);139 },
136 },140 .call_tag_name => {
137 .call_tag_name => {141 assert(ip.indexToKey(data.ip_index) == .enum_type);
138 const zcu = comp.zcu.?;142 const gop = try wasm.zcu_funcs.getOrPut(gpa, data.ip_index);
139 const ip = &zcu.intern_pool;143 if (!gop.found_existing) {
140 assert(ip.indexToKey(data.ip_index) == .enum_type);144 wasm.tag_name_table_ref_count += 1;
141 const gop = try wasm.zcu_funcs.getOrPut(gpa, data.ip_index);145 const int_tag_ty = Zcu.Type.fromInterned(data.ip_index).intTagType(zcu);
142 if (!gop.found_existing) {146 gop.value_ptr.* = .{ .tag_name = .{
143 wasm.tag_name_table_ref_count += 1;147 .symbol_name = try wasm.internStringFmt("__zig_tag_name_{d}", .{@intFromEnum(data.ip_index)}),
144 const int_tag_ty = Zcu.Type.fromInterned(data.ip_index).intTagType(zcu);148 .type_index = try wasm.internFunctionType(.Unspecified, &.{int_tag_ty.ip_index}, .slice_const_u8_sentinel_0, target),
145 gop.value_ptr.* = .{ .tag_name = .{149 .table_index = @intCast(wasm.tag_name_offs.items.len),
146 .symbol_name = try wasm.internStringFmt("__zig_tag_name_{d}", .{@intFromEnum(data.ip_index)}),150 } };
147 .type_index = try wasm.internFunctionType(.Unspecified, &.{int_tag_ty.ip_index}, .slice_const_u8_sentinel_0, target),151 try wasm.functions.put(gpa, .fromZcuFunc(wasm, @enumFromInt(gop.index)), {});
148 .table_index = @intCast(wasm.tag_name_offs.items.len),152 const tag_names = ip.loadEnumType(data.ip_index).names;
149 } };153 for (tag_names.get(ip)) |tag_name| {
150 try wasm.functions.put(gpa, .fromZcuFunc(wasm, @enumFromInt(gop.index)), {});154 const slice = tag_name.toSlice(ip);
151 const tag_names = ip.loadEnumType(data.ip_index).names;155 try wasm.tag_name_offs.append(gpa, @intCast(wasm.tag_name_bytes.items.len));
152 for (tag_names.get(ip)) |tag_name| {156 try wasm.tag_name_bytes.appendSlice(gpa, slice[0 .. slice.len + 1]);
153 const slice = tag_name.toSlice(ip);157 }
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]);
156 }158 }
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;
157 }174 }
158 },175 }
159 else => continue,
160 };
161176
162 if (comp.zcu) |zcu| {177 {
163 const ip: *const InternPool = &zcu.intern_pool; // No mutations allowed!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
165 if (wasm.error_name_table_ref_count > 0) {191 if (wasm.error_name_table_ref_count > 0) {
166 // Ensure Zcu error name structures are populated.192 // Ensure Zcu error name structures are populated.
...@@ -437,7 +463,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -437,7 +463,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
437 break :b i >= 1 and !wantSegmentMerge(wasm, segment_ids[i - 1], segment_id, category);463 break :b i >= 1 and !wantSegmentMerge(wasm, segment_ids[i - 1], segment_id, category);
438 };464 };
439 if (want_new_segment) {465 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 });
441 try f.data_segment_groups.append(gpa, .{467 try f.data_segment_groups.append(gpa, .{
442 .end_addr = @intCast(memory_ptr),468 .end_addr = @intCast(memory_ptr),
443 .first_segment = first_segment,469 .first_segment = first_segment,
...@@ -447,6 +473,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -447,6 +473,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
447473
448 const size = segment_id.size(wasm);474 const size = segment_id.size(wasm);
449 segment_vaddr.* = @intCast(start_addr);475 segment_vaddr.* = @intCast(start_addr);
476 log.debug("0x{x} {d} {s}", .{ start_addr, @intFromEnum(segment_id), segment_id.name(wasm) });
450 memory_ptr = start_addr + size;477 memory_ptr = start_addr + size;
451 }478 }
452 if (category != .zero) try f.data_segment_groups.append(gpa, .{479 if (category != .zero) try f.data_segment_groups.append(gpa, .{