diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2024-12-28 17:24:36 -0800 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2025-01-15 15:11:36 -0800 |
| commit | b7a95911ab209b7b0657f9eb135c64d9c47ac561 (patch) | |
| tree | 7675612bb7c7f4da89aad73bcc6c21b3a62e7a3f /src | |
| parent | 78987081ce84a49230073e8211dadc0dea7351c3 (diff) | |
| download | zig-b7a95911ab209b7b0657f9eb135c64d9c47ac561.tar.gz zig-b7a95911ab209b7b0657f9eb135c64d9c47ac561.zip | |
wasm linker: fix table imports in objects
they need to reference a local index until the object parsing is
complete and also need to check reftype matching
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/Wasm/Object.zig | 122 |
1 files changed, 87 insertions, 35 deletions
diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index d289897aaa..69a467b0de 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -82,6 +82,16 @@ pub const GlobalImport = struct { mutable: bool, }; +pub const TableImport = struct { + module_name: Wasm.String, + name: Wasm.String, + limits_min: u32, + limits_max: u32, + limits_has_max: bool, + limits_is_shared: bool, + ref_type: std.wasm.RefType, +}; + pub const DataSegmentFlags = enum(u32) { active, passive, active_memidx }; pub const SubsectionType = enum(u8) { @@ -115,7 +125,7 @@ pub const Symbol = struct { global_import: ScratchSpace.GlobalImportIndex, section: Wasm.ObjectSectionIndex, table: Wasm.ObjectTableIndex, - table_import: Wasm.TableImport.Index, + table_import: ScratchSpace.TableImportIndex, }; }; @@ -124,6 +134,7 @@ pub const ScratchSpace = struct { func_type_indexes: std.ArrayListUnmanaged(FuncTypeIndex) = .empty, func_imports: std.ArrayListUnmanaged(FunctionImport) = .empty, global_imports: std.ArrayListUnmanaged(GlobalImport) = .empty, + table_imports: std.ArrayListUnmanaged(TableImport) = .empty, symbol_table: std.ArrayListUnmanaged(Symbol) = .empty, segment_info: std.ArrayListUnmanaged(SegmentInfo) = .empty, exports: std.ArrayListUnmanaged(Export) = .empty, @@ -158,6 +169,15 @@ pub const ScratchSpace = struct { } }; + /// Index into `table_imports`. + const TableImportIndex = enum(u32) { + _, + + fn ptr(index: TableImportIndex, ss: *const ScratchSpace) *TableImport { + return &ss.table_imports.items[@intFromEnum(index)]; + } + }; + /// Index into `func_types`. const FuncTypeIndex = enum(u32) { _, @@ -173,6 +193,7 @@ pub const ScratchSpace = struct { ss.func_type_indexes.deinit(gpa); ss.func_imports.deinit(gpa); ss.global_imports.deinit(gpa); + ss.table_imports.deinit(gpa); ss.symbol_table.deinit(gpa); ss.segment_info.deinit(gpa); ss.* = undefined; @@ -184,6 +205,7 @@ pub const ScratchSpace = struct { ss.func_type_indexes.clearRetainingCapacity(); ss.func_imports.clearRetainingCapacity(); ss.global_imports.clearRetainingCapacity(); + ss.table_imports.clearRetainingCapacity(); ss.symbol_table.clearRetainingCapacity(); ss.segment_info.clearRetainingCapacity(); } @@ -231,7 +253,7 @@ pub fn parse( var opt_features: ?Wasm.Feature.Set = null; var saw_linking_section = false; var has_tls = false; - var table_count: usize = 0; + var table_import_symbol_count: usize = 0; while (pos < bytes.len) : (wasm.object_total_sections += 1) { const section_index: Wasm.ObjectSectionIndex = @enumFromInt(wasm.object_total_sections); @@ -403,19 +425,19 @@ pub fn parse( } }, .table => { - table_count += 1; const local_index, pos = readLeb(u32, bytes, pos); if (symbol.flags.undefined) { - const table_import: Wasm.TableImport.Index = @enumFromInt(table_imports_start + local_index); + table_import_symbol_count += 1; + const table_import: ScratchSpace.TableImportIndex = @enumFromInt(local_index); symbol.pointee = .{ .table_import = table_import }; if (symbol.flags.explicit_name) { const name, pos = readBytes(bytes, pos); symbol.name = (try wasm.internString(name)).toOptional(); } else { - symbol.name = table_import.key(wasm).toOptional(); + symbol.name = table_import.ptr(ss).name.toOptional(); } } else { - symbol.pointee = .{ .table = @enumFromInt(tables_start + local_index) }; + symbol.pointee = .{ .table = @enumFromInt(tables_start + (local_index - ss.table_imports.items.len)) }; const name, pos = readBytes(bytes, pos); symbol.name = (try wasm.internString(name)).toOptional(); } @@ -608,17 +630,14 @@ pub fn parse( .table => { const ref_type, pos = readEnum(std.wasm.RefType, bytes, pos); const limits, pos = readLimits(bytes, pos); - try wasm.object_table_imports.put(gpa, interned_name, .{ - .flags = .{ - .limits_has_max = limits.flags.has_max, - .limits_is_shared = limits.flags.is_shared, - .ref_type = .from(ref_type), - }, + try ss.table_imports.append(gpa, .{ + .name = interned_name, .module_name = interned_module_name, - .source_location = source_location, - .resolution = .unresolved, .limits_min = limits.min, .limits_max = limits.max, + .limits_has_max = limits.flags.has_max, + .limits_is_shared = limits.flags.is_shared, + .ref_type = ref_type, }); }, } @@ -697,7 +716,7 @@ pub fn parse( .name = try wasm.internString(name), .pointee = switch (kind) { .function => .{ .function = @enumFromInt(functions_start + (index - ss.func_imports.items.len)) }, - .table => .{ .table = @enumFromInt(tables_start + index) }, + .table => .{ .table = @enumFromInt(tables_start + (index - ss.table_imports.items.len)) }, .memory => .{ .memory = @enumFromInt(memories_start + index) }, .global => .{ .global = @enumFromInt(globals_start + (index - ss.global_imports.items.len)) }, }, @@ -884,7 +903,7 @@ pub fn parse( } if (gop.value_ptr.module_name != ptr.module_name.toOptional()) { var err = try diags.addErrorWithNotes(2); - try err.addMsg("function symbol '{s}' mismatching module names", .{name.slice(wasm)}); + try err.addMsg("symbol '{s}' mismatching module names", .{name.slice(wasm)}); if (gop.value_ptr.module_name.slice(wasm)) |module_name| { gop.value_ptr.source_location.addNote(wasm, &err, "module '{s}' here", .{module_name}); } else { @@ -909,6 +928,49 @@ pub fn parse( }; } }, + .table_import => |index| { + const ptr = index.ptr(ss); + const name = symbol.name.unwrap().?; + if (symbol.flags.binding == .local) { + diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)}); + continue; + } + const gop = try wasm.object_table_imports.getOrPut(gpa, name); + if (gop.found_existing) { + const existing_reftype = gop.value_ptr.flags.ref_type.to(); + if (ptr.ref_type != existing_reftype) { + var err = try diags.addErrorWithNotes(2); + try err.addMsg("symbol '{s}' mismatching table reftypes", .{name.slice(wasm)}); + gop.value_ptr.source_location.addNote(wasm, &err, "{s} here", .{@tagName(existing_reftype)}); + err.addNote("{}: {s} here", .{ path, @tagName(ptr.ref_type) }); + continue; + } + if (gop.value_ptr.module_name != ptr.module_name) { + var err = try diags.addErrorWithNotes(2); + try err.addMsg("symbol '{s}' mismatching module names", .{name.slice(wasm)}); + gop.value_ptr.source_location.addNote(wasm, &err, "module '{s}' here", .{ + gop.value_ptr.module_name.slice(wasm), + }); + err.addNote("{}: module '{s}' here", .{ path, ptr.module_name.slice(wasm) }); + continue; + } + if (symbol.flags.binding == .strong) gop.value_ptr.flags.binding = .strong; + if (!symbol.flags.visibility_hidden) gop.value_ptr.flags.visibility_hidden = false; + if (symbol.flags.no_strip) gop.value_ptr.flags.no_strip = true; + } else { + gop.value_ptr.* = .{ + .flags = symbol.flags, + .module_name = ptr.module_name, + .source_location = source_location, + .resolution = .unresolved, + .limits_min = ptr.limits_min, + .limits_max = ptr.limits_max, + }; + gop.value_ptr.flags.limits_has_max = ptr.limits_has_max; + gop.value_ptr.flags.limits_is_shared = ptr.limits_is_shared; + gop.value_ptr.flags.ref_type = .from(ptr.ref_type); + } + }, .function => |index| { assert(!symbol.flags.undefined); const ptr = index.ptr(wasm); @@ -951,15 +1013,6 @@ pub fn parse( } }, - .table_import => |i| { - const ptr = i.value(wasm); - assert(i.key(wasm).toOptional() == symbol.name); // TODO - ptr.flags = symbol.flags; - if (symbol.flags.undefined and symbol.flags.binding == .local) { - const name = i.key(wasm).slice(wasm); - diags.addParseError(path, "local symbol '{s}' references import", .{name}); - } - }, inline .global, .table => |i| { const ptr = i.ptr(wasm); ptr.name = symbol.name; @@ -1016,31 +1069,30 @@ pub fn parse( // Check for indirect function table in case of an MVP object file. legacy_indirect_function_table: { - const table_import_names = wasm.object_table_imports.keys()[table_imports_start..]; - const table_import_values = wasm.object_table_imports.values()[table_imports_start..]; // If there is a symbol for each import table, this is not a legacy object file. - if (table_import_names.len == table_count) break :legacy_indirect_function_table; - if (table_count != 0) { + 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.", .{ - table_import_names.len, table_count, + ss.table_imports.items.len, table_import_symbol_count, }); } - // MVP object files cannot have any table definitions, only - // imports (for the indirect function table). + // 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 (table_import_names.len != 1) { + 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 = table_import_names[0]; + 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), }); } - table_import_values[0].flags = .{ + const ptr = wasm.object_table_imports.getPtr(table_import_name).?; + ptr.flags = .{ .undefined = true, .no_strip = true, }; |
