Commit a327d238f1

Andrew Kelley <andrew@ziglang.org>
2025-01-10 09:42:04
wasm linker: handle function data references properly
1 parent d999a8e
Changed files (3)
src/link/Wasm/Flush.zig
@@ -54,6 +54,11 @@ const IndirectFunctionTableIndex = enum(u32) {
     fn fromOutputFunctionIndex(f: *const Flush, i: Wasm.OutputFunctionIndex) IndirectFunctionTableIndex {
         return @enumFromInt(f.indirect_function_table.getIndex(i).?);
     }
+
+    fn fromZcuIndirectFunctionSetIndex(i: Wasm.ZcuIndirectFunctionSetIndex) IndirectFunctionTableIndex {
+        // These are the same since those are added to the table first.
+        return @enumFromInt(@intFromEnum(i));
+    }
 };
 
 const DataSegmentGroup = struct {
@@ -755,6 +760,14 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
                 mem.writeInt(u64, wasm.string_bytes.items[nav_fixup.offset..][0..8], vaddr, .little);
             }
         }
+        for (wasm.func_table_fixups.items) |fixup| {
+            const table_index: IndirectFunctionTableIndex = .fromZcuIndirectFunctionSetIndex(fixup.table_index);
+            if (!is64) {
+                mem.writeInt(u32, wasm.string_bytes.items[fixup.offset..][0..4], @intFromEnum(table_index), .little);
+            } else {
+                mem.writeInt(u64, wasm.string_bytes.items[fixup.offset..][0..8], @intFromEnum(table_index), .little);
+            }
+        }
     }
 
     // Data section.
src/link/Wasm.zig
@@ -151,7 +151,11 @@ uav_fixups: std.ArrayListUnmanaged(UavFixup) = .empty,
 /// List of locations within `string_bytes` that must be patched with the virtual
 /// memory address of a Nav during `flush`.
 /// When emitting an object file, `out_relocs` is used instead.
+/// No functions here only global variables.
 nav_fixups: std.ArrayListUnmanaged(NavFixup) = .empty,
+/// When a nav reference is a function pointer, this tracks the required function
+/// table entry index that needs to overwrite the code in the final output.
+func_table_fixups: std.ArrayListUnmanaged(FuncTableFixup) = .empty,
 /// Symbols to be emitted into an object file. Remains empty when not emitting
 /// an object file.
 symbol_table: std.AutoArrayHashMapUnmanaged(String, void) = .empty,
@@ -307,6 +311,12 @@ pub const NavFixup = extern struct {
     offset: u32,
 };
 
+pub const FuncTableFixup = extern struct {
+    table_index: ZcuIndirectFunctionSetIndex,
+    /// Index into `string_bytes`.
+    offset: u32,
+};
+
 /// Index into `objects`.
 pub const ObjectIndex = enum(u32) {
     _,
@@ -2208,7 +2218,7 @@ pub const FunctionImportId = enum(u32) {
     pub fn pack(unpacked: Unpacked, wasm: *const Wasm) FunctionImportId {
         return switch (unpacked) {
             .object_function_import => |i| @enumFromInt(@intFromEnum(i)),
-            .zcu_import => |i| @enumFromInt(@intFromEnum(i) - wasm.object_function_imports.entries.len),
+            .zcu_import => |i| @enumFromInt(@intFromEnum(i) + wasm.object_function_imports.entries.len),
         };
     }
 
@@ -2295,7 +2305,7 @@ pub const GlobalImportId = enum(u32) {
     pub fn pack(unpacked: Unpacked, wasm: *const Wasm) GlobalImportId {
         return switch (unpacked) {
             .object_global_import => |i| @enumFromInt(@intFromEnum(i)),
-            .zcu_import => |i| @enumFromInt(@intFromEnum(i) - wasm.object_global_imports.entries.len),
+            .zcu_import => |i| @enumFromInt(@intFromEnum(i) + wasm.object_global_imports.entries.len),
         };
     }
 
@@ -2360,7 +2370,7 @@ pub const DataImportId = enum(u32) {
     pub fn pack(unpacked: Unpacked, wasm: *const Wasm) DataImportId {
         return switch (unpacked) {
             .object_data_import => |i| @enumFromInt(@intFromEnum(i)),
-            .zcu_import => |i| @enumFromInt(@intFromEnum(i) - wasm.object_data_imports.entries.len),
+            .zcu_import => |i| @enumFromInt(@intFromEnum(i) + wasm.object_data_imports.entries.len),
         };
     }
 
@@ -3027,6 +3037,7 @@ pub fn deinit(wasm: *Wasm) void {
     wasm.out_relocs.deinit(gpa);
     wasm.uav_fixups.deinit(gpa);
     wasm.nav_fixups.deinit(gpa);
+    wasm.func_table_fixups.deinit(gpa);
 
     wasm.zcu_indirect_function_set.deinit(gpa);
     wasm.object_indirect_function_import_set.deinit(gpa);
src/codegen.zig
@@ -738,19 +738,32 @@ fn lowerNavRef(
             dev.check(link.File.Tag.wasm.devFeature());
             const wasm = lf.cast(.wasm).?;
             assert(reloc_parent == .none);
-            if (is_obj) {
-                try wasm.out_relocs.append(gpa, .{
-                    .offset = @intCast(code.items.len),
-                    .pointee = .{ .symbol_index = try wasm.navSymbolIndex(nav_index) },
-                    .tag = if (ptr_width_bytes == 4) .memory_addr_i32 else .memory_addr_i64,
-                    .addend = @intCast(offset),
-                });
+            if (is_fn_body) {
+                const gop = try wasm.zcu_indirect_function_set.getOrPut(gpa, nav_index);
+                if (!gop.found_existing) gop.value_ptr.* = {};
+                if (is_obj) {
+                    @panic("TODO add out_reloc for this");
+                } else {
+                    try wasm.func_table_fixups.append(gpa, .{
+                        .table_index = @enumFromInt(gop.index),
+                        .offset = @intCast(code.items.len),
+                    });
+                }
             } else {
-                try wasm.nav_fixups.ensureUnusedCapacity(gpa, 1);
-                wasm.nav_fixups.appendAssumeCapacity(.{
-                    .navs_exe_index = try wasm.refNavExe(nav_index),
-                    .offset = @intCast(code.items.len),
-                });
+                if (is_obj) {
+                    try wasm.out_relocs.append(gpa, .{
+                        .offset = @intCast(code.items.len),
+                        .pointee = .{ .symbol_index = try wasm.navSymbolIndex(nav_index) },
+                        .tag = if (ptr_width_bytes == 4) .memory_addr_i32 else .memory_addr_i64,
+                        .addend = @intCast(offset),
+                    });
+                } else {
+                    try wasm.nav_fixups.ensureUnusedCapacity(gpa, 1);
+                    wasm.nav_fixups.appendAssumeCapacity(.{
+                        .navs_exe_index = try wasm.refNavExe(nav_index),
+                        .offset = @intCast(code.items.len),
+                    });
+                }
             }
             code.appendNTimesAssumeCapacity(0, ptr_width_bytes);
             return;