From 6ed9c0521055add99097fd43598d5a8e1e3665ff Mon Sep 17 00:00:00 2001 From: Pavel Verigo Date: Thu, 26 Mar 2026 11:19:33 +0000 Subject: [PATCH] link.Wasm: fix indirect function table handling The changes to the LLVM backend here changed the compiler_rt object which LLVM emits, and exposed some buggy behavior in the self-hosted WASM linker when parsing that object. --- src/link/Wasm/Object.zig | 66 +++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index c55d36f05a..8885fb5247 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -969,6 +969,41 @@ pub fn parse( func.type_index = func_type.ptr(ss).*; } + // Check for indirect function table in case of an MVP object file. + legacy_indirect_function_table: { + // If there is a symbol for each import table, this is not a legacy object file. + if (ss.table_imports.items.len == table_import_symbol_count) break :legacy_indirect_function_table; + if (table_import_symbol_count != 0) { + return diags.failParse(path, "expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{ + ss.table_imports.items.len, table_import_symbol_count, + }); + } + // MVP object files cannot have any table definitions, only imports + // (for the indirect function table). + const tables = wasm.object_tables.items[tables_start..]; + if (tables.len > 0) { + return diags.failParse(path, "table definition without representing table symbols", .{}); + } + if (ss.table_imports.items.len != 1) { + return diags.failParse(path, "found more than one table import, but no representing table symbols", .{}); + } + const table_import_name = ss.table_imports.items[0].name; + if (table_import_name != wasm.preloaded_strings.__indirect_function_table) { + return diags.failParse(path, "non-indirect function table import '{s}' is missing a corresponding symbol", .{ + table_import_name.slice(wasm), + }); + } + + try ss.symbol_table.append(gpa, .{ + .flags = .{ + .undefined = true, + .no_strip = true, + }, + .name = table_import_name.toOptional(), + .pointee = .{ .table_import = @enumFromInt(0) }, + }); + } + // Apply symbol table information. for (ss.symbol_table.items) |symbol| switch (symbol.pointee) { .function_import => |index| { @@ -1331,37 +1366,6 @@ pub fn parse( }; } - // Check for indirect function table in case of an MVP object file. - legacy_indirect_function_table: { - // If there is a symbol for each import table, this is not a legacy object file. - if (ss.table_imports.items.len == table_import_symbol_count) break :legacy_indirect_function_table; - if (table_import_symbol_count != 0) { - return diags.failParse(path, "expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{ - ss.table_imports.items.len, table_import_symbol_count, - }); - } - // MVP object files cannot have any table definitions, only imports - // (for the indirect function table). - const tables = wasm.object_tables.items[tables_start..]; - if (tables.len > 0) { - return diags.failParse(path, "table definition without representing table symbols", .{}); - } - if (ss.table_imports.items.len != 1) { - return diags.failParse(path, "found more than one table import, but no representing table symbols", .{}); - } - const table_import_name = ss.table_imports.items[0].name; - if (table_import_name != wasm.preloaded_strings.__indirect_function_table) { - return diags.failParse(path, "non-indirect function table import '{s}' is missing a corresponding symbol", .{ - table_import_name.slice(wasm), - }); - } - const ptr = wasm.object_table_imports.getPtr(table_import_name).?; - ptr.flags = .{ - .undefined = true, - .no_strip = true, - }; - } - for (wasm.object_init_funcs.items[init_funcs_start..]) |init_func| { const func = init_func.function_index.ptr(wasm); const params = func.type_index.ptr(wasm).params.slice(wasm);