authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-12-28 17:24:36-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:36-08:00
logb7a95911ab209b7b0657f9eb135c64d9c47ac561
tree7675612bb7c7f4da89aad73bcc6c21b3a62e7a3f
parent78987081ce84a49230073e8211dadc0dea7351c3

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

1 files changed, 87 insertions(+), 35 deletions(-)

src/link/Wasm/Object.zig+87-35
...@@ -82,6 +82,16 @@ pub const GlobalImport = struct {...@@ -82,6 +82,16 @@ pub const GlobalImport = struct {
82 mutable: bool,82 mutable: bool,
83};83};
8484
85pub const TableImport = struct {
86 module_name: Wasm.String,
87 name: Wasm.String,
88 limits_min: u32,
89 limits_max: u32,
90 limits_has_max: bool,
91 limits_is_shared: bool,
92 ref_type: std.wasm.RefType,
93};
94
85pub const DataSegmentFlags = enum(u32) { active, passive, active_memidx };95pub const DataSegmentFlags = enum(u32) { active, passive, active_memidx };
8696
87pub const SubsectionType = enum(u8) {97pub const SubsectionType = enum(u8) {
...@@ -115,7 +125,7 @@ pub const Symbol = struct {...@@ -115,7 +125,7 @@ pub const Symbol = struct {
115 global_import: ScratchSpace.GlobalImportIndex,125 global_import: ScratchSpace.GlobalImportIndex,
116 section: Wasm.ObjectSectionIndex,126 section: Wasm.ObjectSectionIndex,
117 table: Wasm.ObjectTableIndex,127 table: Wasm.ObjectTableIndex,
118 table_import: Wasm.TableImport.Index,128 table_import: ScratchSpace.TableImportIndex,
119 };129 };
120};130};
121131
...@@ -124,6 +134,7 @@ pub const ScratchSpace = struct {...@@ -124,6 +134,7 @@ pub const ScratchSpace = struct {
124 func_type_indexes: std.ArrayListUnmanaged(FuncTypeIndex) = .empty,134 func_type_indexes: std.ArrayListUnmanaged(FuncTypeIndex) = .empty,
125 func_imports: std.ArrayListUnmanaged(FunctionImport) = .empty,135 func_imports: std.ArrayListUnmanaged(FunctionImport) = .empty,
126 global_imports: std.ArrayListUnmanaged(GlobalImport) = .empty,136 global_imports: std.ArrayListUnmanaged(GlobalImport) = .empty,
137 table_imports: std.ArrayListUnmanaged(TableImport) = .empty,
127 symbol_table: std.ArrayListUnmanaged(Symbol) = .empty,138 symbol_table: std.ArrayListUnmanaged(Symbol) = .empty,
128 segment_info: std.ArrayListUnmanaged(SegmentInfo) = .empty,139 segment_info: std.ArrayListUnmanaged(SegmentInfo) = .empty,
129 exports: std.ArrayListUnmanaged(Export) = .empty,140 exports: std.ArrayListUnmanaged(Export) = .empty,
...@@ -158,6 +169,15 @@ pub const ScratchSpace = struct {...@@ -158,6 +169,15 @@ pub const ScratchSpace = struct {
158 }169 }
159 };170 };
160171
172 /// Index into `table_imports`.
173 const TableImportIndex = enum(u32) {
174 _,
175
176 fn ptr(index: TableImportIndex, ss: *const ScratchSpace) *TableImport {
177 return &ss.table_imports.items[@intFromEnum(index)];
178 }
179 };
180
161 /// Index into `func_types`.181 /// Index into `func_types`.
162 const FuncTypeIndex = enum(u32) {182 const FuncTypeIndex = enum(u32) {
163 _,183 _,
...@@ -173,6 +193,7 @@ pub const ScratchSpace = struct {...@@ -173,6 +193,7 @@ pub const ScratchSpace = struct {
173 ss.func_type_indexes.deinit(gpa);193 ss.func_type_indexes.deinit(gpa);
174 ss.func_imports.deinit(gpa);194 ss.func_imports.deinit(gpa);
175 ss.global_imports.deinit(gpa);195 ss.global_imports.deinit(gpa);
196 ss.table_imports.deinit(gpa);
176 ss.symbol_table.deinit(gpa);197 ss.symbol_table.deinit(gpa);
177 ss.segment_info.deinit(gpa);198 ss.segment_info.deinit(gpa);
178 ss.* = undefined;199 ss.* = undefined;
...@@ -184,6 +205,7 @@ pub const ScratchSpace = struct {...@@ -184,6 +205,7 @@ pub const ScratchSpace = struct {
184 ss.func_type_indexes.clearRetainingCapacity();205 ss.func_type_indexes.clearRetainingCapacity();
185 ss.func_imports.clearRetainingCapacity();206 ss.func_imports.clearRetainingCapacity();
186 ss.global_imports.clearRetainingCapacity();207 ss.global_imports.clearRetainingCapacity();
208 ss.table_imports.clearRetainingCapacity();
187 ss.symbol_table.clearRetainingCapacity();209 ss.symbol_table.clearRetainingCapacity();
188 ss.segment_info.clearRetainingCapacity();210 ss.segment_info.clearRetainingCapacity();
189 }211 }
...@@ -231,7 +253,7 @@ pub fn parse(...@@ -231,7 +253,7 @@ pub fn parse(
231 var opt_features: ?Wasm.Feature.Set = null;253 var opt_features: ?Wasm.Feature.Set = null;
232 var saw_linking_section = false;254 var saw_linking_section = false;
233 var has_tls = false;255 var has_tls = false;
234 var table_count: usize = 0;256 var table_import_symbol_count: usize = 0;
235 while (pos < bytes.len) : (wasm.object_total_sections += 1) {257 while (pos < bytes.len) : (wasm.object_total_sections += 1) {
236 const section_index: Wasm.ObjectSectionIndex = @enumFromInt(wasm.object_total_sections);258 const section_index: Wasm.ObjectSectionIndex = @enumFromInt(wasm.object_total_sections);
237259
...@@ -403,19 +425,19 @@ pub fn parse(...@@ -403,19 +425,19 @@ pub fn parse(
403 }425 }
404 },426 },
405 .table => {427 .table => {
406 table_count += 1;
407 const local_index, pos = readLeb(u32, bytes, pos);428 const local_index, pos = readLeb(u32, bytes, pos);
408 if (symbol.flags.undefined) {429 if (symbol.flags.undefined) {
409 const table_import: Wasm.TableImport.Index = @enumFromInt(table_imports_start + local_index);430 table_import_symbol_count += 1;
431 const table_import: ScratchSpace.TableImportIndex = @enumFromInt(local_index);
410 symbol.pointee = .{ .table_import = table_import };432 symbol.pointee = .{ .table_import = table_import };
411 if (symbol.flags.explicit_name) {433 if (symbol.flags.explicit_name) {
412 const name, pos = readBytes(bytes, pos);434 const name, pos = readBytes(bytes, pos);
413 symbol.name = (try wasm.internString(name)).toOptional();435 symbol.name = (try wasm.internString(name)).toOptional();
414 } else {436 } else {
415 symbol.name = table_import.key(wasm).toOptional();437 symbol.name = table_import.ptr(ss).name.toOptional();
416 }438 }
417 } else {439 } else {
418 symbol.pointee = .{ .table = @enumFromInt(tables_start + local_index) };440 symbol.pointee = .{ .table = @enumFromInt(tables_start + (local_index - ss.table_imports.items.len)) };
419 const name, pos = readBytes(bytes, pos);441 const name, pos = readBytes(bytes, pos);
420 symbol.name = (try wasm.internString(name)).toOptional();442 symbol.name = (try wasm.internString(name)).toOptional();
421 }443 }
...@@ -608,17 +630,14 @@ pub fn parse(...@@ -608,17 +630,14 @@ pub fn parse(
608 .table => {630 .table => {
609 const ref_type, pos = readEnum(std.wasm.RefType, bytes, pos);631 const ref_type, pos = readEnum(std.wasm.RefType, bytes, pos);
610 const limits, pos = readLimits(bytes, pos);632 const limits, pos = readLimits(bytes, pos);
611 try wasm.object_table_imports.put(gpa, interned_name, .{633 try ss.table_imports.append(gpa, .{
612 .flags = .{634 .name = interned_name,
613 .limits_has_max = limits.flags.has_max,
614 .limits_is_shared = limits.flags.is_shared,
615 .ref_type = .from(ref_type),
616 },
617 .module_name = interned_module_name,635 .module_name = interned_module_name,
618 .source_location = source_location,
619 .resolution = .unresolved,
620 .limits_min = limits.min,636 .limits_min = limits.min,
621 .limits_max = limits.max,637 .limits_max = limits.max,
638 .limits_has_max = limits.flags.has_max,
639 .limits_is_shared = limits.flags.is_shared,
640 .ref_type = ref_type,
622 });641 });
623 },642 },
624 }643 }
...@@ -697,7 +716,7 @@ pub fn parse(...@@ -697,7 +716,7 @@ pub fn parse(
697 .name = try wasm.internString(name),716 .name = try wasm.internString(name),
698 .pointee = switch (kind) {717 .pointee = switch (kind) {
699 .function => .{ .function = @enumFromInt(functions_start + (index - ss.func_imports.items.len)) },718 .function => .{ .function = @enumFromInt(functions_start + (index - ss.func_imports.items.len)) },
700 .table => .{ .table = @enumFromInt(tables_start + index) },719 .table => .{ .table = @enumFromInt(tables_start + (index - ss.table_imports.items.len)) },
701 .memory => .{ .memory = @enumFromInt(memories_start + index) },720 .memory => .{ .memory = @enumFromInt(memories_start + index) },
702 .global => .{ .global = @enumFromInt(globals_start + (index - ss.global_imports.items.len)) },721 .global => .{ .global = @enumFromInt(globals_start + (index - ss.global_imports.items.len)) },
703 },722 },
...@@ -884,7 +903,7 @@ pub fn parse(...@@ -884,7 +903,7 @@ pub fn parse(
884 }903 }
885 if (gop.value_ptr.module_name != ptr.module_name.toOptional()) {904 if (gop.value_ptr.module_name != ptr.module_name.toOptional()) {
886 var err = try diags.addErrorWithNotes(2);905 var err = try diags.addErrorWithNotes(2);
887 try err.addMsg("function symbol '{s}' mismatching module names", .{name.slice(wasm)});906 try err.addMsg("symbol '{s}' mismatching module names", .{name.slice(wasm)});
888 if (gop.value_ptr.module_name.slice(wasm)) |module_name| {907 if (gop.value_ptr.module_name.slice(wasm)) |module_name| {
889 gop.value_ptr.source_location.addNote(wasm, &err, "module '{s}' here", .{module_name});908 gop.value_ptr.source_location.addNote(wasm, &err, "module '{s}' here", .{module_name});
890 } else {909 } else {
...@@ -909,6 +928,49 @@ pub fn parse(...@@ -909,6 +928,49 @@ pub fn parse(
909 };928 };
910 }929 }
911 },930 },
931 .table_import => |index| {
932 const ptr = index.ptr(ss);
933 const name = symbol.name.unwrap().?;
934 if (symbol.flags.binding == .local) {
935 diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)});
936 continue;
937 }
938 const gop = try wasm.object_table_imports.getOrPut(gpa, name);
939 if (gop.found_existing) {
940 const existing_reftype = gop.value_ptr.flags.ref_type.to();
941 if (ptr.ref_type != existing_reftype) {
942 var err = try diags.addErrorWithNotes(2);
943 try err.addMsg("symbol '{s}' mismatching table reftypes", .{name.slice(wasm)});
944 gop.value_ptr.source_location.addNote(wasm, &err, "{s} here", .{@tagName(existing_reftype)});
945 err.addNote("{}: {s} here", .{ path, @tagName(ptr.ref_type) });
946 continue;
947 }
948 if (gop.value_ptr.module_name != ptr.module_name) {
949 var err = try diags.addErrorWithNotes(2);
950 try err.addMsg("symbol '{s}' mismatching module names", .{name.slice(wasm)});
951 gop.value_ptr.source_location.addNote(wasm, &err, "module '{s}' here", .{
952 gop.value_ptr.module_name.slice(wasm),
953 });
954 err.addNote("{}: module '{s}' here", .{ path, ptr.module_name.slice(wasm) });
955 continue;
956 }
957 if (symbol.flags.binding == .strong) gop.value_ptr.flags.binding = .strong;
958 if (!symbol.flags.visibility_hidden) gop.value_ptr.flags.visibility_hidden = false;
959 if (symbol.flags.no_strip) gop.value_ptr.flags.no_strip = true;
960 } else {
961 gop.value_ptr.* = .{
962 .flags = symbol.flags,
963 .module_name = ptr.module_name,
964 .source_location = source_location,
965 .resolution = .unresolved,
966 .limits_min = ptr.limits_min,
967 .limits_max = ptr.limits_max,
968 };
969 gop.value_ptr.flags.limits_has_max = ptr.limits_has_max;
970 gop.value_ptr.flags.limits_is_shared = ptr.limits_is_shared;
971 gop.value_ptr.flags.ref_type = .from(ptr.ref_type);
972 }
973 },
912 .function => |index| {974 .function => |index| {
913 assert(!symbol.flags.undefined);975 assert(!symbol.flags.undefined);
914 const ptr = index.ptr(wasm);976 const ptr = index.ptr(wasm);
...@@ -951,15 +1013,6 @@ pub fn parse(...@@ -951,15 +1013,6 @@ pub fn parse(
951 }1013 }
952 },1014 },
9531015
954 .table_import => |i| {
955 const ptr = i.value(wasm);
956 assert(i.key(wasm).toOptional() == symbol.name); // TODO
957 ptr.flags = symbol.flags;
958 if (symbol.flags.undefined and symbol.flags.binding == .local) {
959 const name = i.key(wasm).slice(wasm);
960 diags.addParseError(path, "local symbol '{s}' references import", .{name});
961 }
962 },
963 inline .global, .table => |i| {1016 inline .global, .table => |i| {
964 const ptr = i.ptr(wasm);1017 const ptr = i.ptr(wasm);
965 ptr.name = symbol.name;1018 ptr.name = symbol.name;
...@@ -1016,31 +1069,30 @@ pub fn parse(...@@ -1016,31 +1069,30 @@ pub fn parse(
10161069
1017 // Check for indirect function table in case of an MVP object file.1070 // Check for indirect function table in case of an MVP object file.
1018 legacy_indirect_function_table: {1071 legacy_indirect_function_table: {
1019 const table_import_names = wasm.object_table_imports.keys()[table_imports_start..];
1020 const table_import_values = wasm.object_table_imports.values()[table_imports_start..];
1021 // If there is a symbol for each import table, this is not a legacy object file.1072 // If there is a symbol for each import table, this is not a legacy object file.
1022 if (table_import_names.len == table_count) break :legacy_indirect_function_table;1073 if (ss.table_imports.items.len == table_import_symbol_count) break :legacy_indirect_function_table;
1023 if (table_count != 0) {1074 if (table_import_symbol_count != 0) {
1024 return diags.failParse(path, "expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{1075 return diags.failParse(path, "expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
1025 table_import_names.len, table_count,1076 ss.table_imports.items.len, table_import_symbol_count,
1026 });1077 });
1027 }1078 }
1028 // MVP object files cannot have any table definitions, only1079 // MVP object files cannot have any table definitions, only imports
1029 // imports (for the indirect function table).1080 // (for the indirect function table).
1030 const tables = wasm.object_tables.items[tables_start..];1081 const tables = wasm.object_tables.items[tables_start..];
1031 if (tables.len > 0) {1082 if (tables.len > 0) {
1032 return diags.failParse(path, "table definition without representing table symbols", .{});1083 return diags.failParse(path, "table definition without representing table symbols", .{});
1033 }1084 }
1034 if (table_import_names.len != 1) {1085 if (ss.table_imports.items.len != 1) {
1035 return diags.failParse(path, "found more than one table import, but no representing table symbols", .{});1086 return diags.failParse(path, "found more than one table import, but no representing table symbols", .{});
1036 }1087 }
1037 const table_import_name = table_import_names[0];1088 const table_import_name = ss.table_imports.items[0].name;
1038 if (table_import_name != wasm.preloaded_strings.__indirect_function_table) {1089 if (table_import_name != wasm.preloaded_strings.__indirect_function_table) {
1039 return diags.failParse(path, "non-indirect function table import '{s}' is missing a corresponding symbol", .{1090 return diags.failParse(path, "non-indirect function table import '{s}' is missing a corresponding symbol", .{
1040 table_import_name.slice(wasm),1091 table_import_name.slice(wasm),
1041 });1092 });
1042 }1093 }
1043 table_import_values[0].flags = .{1094 const ptr = wasm.object_table_imports.getPtr(table_import_name).?;
1095 ptr.flags = .{
1044 .undefined = true,1096 .undefined = true,
1045 .no_strip = true,1097 .no_strip = true,
1046 };1098 };