authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-12-28 15:44:35-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:36-08:00
loga72e7384aae81d012f017c837df8c16048bdea4e
treece3daf169efab9f124fa86c2870be5eda5b6b50b
parenteb943890d975a50618db8ca7593563fcf8b107da

wasm linker: fix global imports in objects

they need to reference a local index until the object parsing is complete and also need to check mutability and type matching

1 files changed, 85 insertions(+), 16 deletions(-)

src/link/Wasm/Object.zig+85-16
...@@ -75,6 +75,13 @@ pub const FunctionImport = struct {...@@ -75,6 +75,13 @@ pub const FunctionImport = struct {
75 function_index: ScratchSpace.FuncTypeIndex,75 function_index: ScratchSpace.FuncTypeIndex,
76};76};
7777
78pub const GlobalImport = struct {
79 module_name: Wasm.String,
80 name: Wasm.String,
81 valtype: std.wasm.Valtype,
82 mutable: bool,
83};
84
78pub const DataSegmentFlags = enum(u32) { active, passive, active_memidx };85pub const DataSegmentFlags = enum(u32) { active, passive, active_memidx };
7986
80pub const SubsectionType = enum(u8) {87pub const SubsectionType = enum(u8) {
...@@ -105,7 +112,7 @@ pub const Symbol = struct {...@@ -105,7 +112,7 @@ pub const Symbol = struct {
105 data: Wasm.ObjectData.Index,112 data: Wasm.ObjectData.Index,
106 data_import: void,113 data_import: void,
107 global: Wasm.ObjectGlobalIndex,114 global: Wasm.ObjectGlobalIndex,
108 global_import: Wasm.GlobalImport.Index,115 global_import: ScratchSpace.GlobalImportIndex,
109 section: Wasm.ObjectSectionIndex,116 section: Wasm.ObjectSectionIndex,
110 table: Wasm.ObjectTableIndex,117 table: Wasm.ObjectTableIndex,
111 table_import: Wasm.TableImport.Index,118 table_import: Wasm.TableImport.Index,
...@@ -116,6 +123,7 @@ pub const ScratchSpace = struct {...@@ -116,6 +123,7 @@ pub const ScratchSpace = struct {
116 func_types: std.ArrayListUnmanaged(Wasm.FunctionType.Index) = .empty,123 func_types: std.ArrayListUnmanaged(Wasm.FunctionType.Index) = .empty,
117 func_type_indexes: std.ArrayListUnmanaged(FuncTypeIndex) = .empty,124 func_type_indexes: std.ArrayListUnmanaged(FuncTypeIndex) = .empty,
118 func_imports: std.ArrayListUnmanaged(FunctionImport) = .empty,125 func_imports: std.ArrayListUnmanaged(FunctionImport) = .empty,
126 global_imports: std.ArrayListUnmanaged(GlobalImport) = .empty,
119 symbol_table: std.ArrayListUnmanaged(Symbol) = .empty,127 symbol_table: std.ArrayListUnmanaged(Symbol) = .empty,
120 segment_info: std.ArrayListUnmanaged(SegmentInfo) = .empty,128 segment_info: std.ArrayListUnmanaged(SegmentInfo) = .empty,
121 exports: std.ArrayListUnmanaged(Export) = .empty,129 exports: std.ArrayListUnmanaged(Export) = .empty,
...@@ -141,6 +149,15 @@ pub const ScratchSpace = struct {...@@ -141,6 +149,15 @@ pub const ScratchSpace = struct {
141 }149 }
142 };150 };
143151
152 /// Index into `global_imports`.
153 const GlobalImportIndex = enum(u32) {
154 _,
155
156 fn ptr(index: GlobalImportIndex, ss: *const ScratchSpace) *GlobalImport {
157 return &ss.global_imports.items[@intFromEnum(index)];
158 }
159 };
160
144 /// Index into `func_types`.161 /// Index into `func_types`.
145 const FuncTypeIndex = enum(u32) {162 const FuncTypeIndex = enum(u32) {
146 _,163 _,
...@@ -155,6 +172,7 @@ pub const ScratchSpace = struct {...@@ -155,6 +172,7 @@ pub const ScratchSpace = struct {
155 ss.func_types.deinit(gpa);172 ss.func_types.deinit(gpa);
156 ss.func_type_indexes.deinit(gpa);173 ss.func_type_indexes.deinit(gpa);
157 ss.func_imports.deinit(gpa);174 ss.func_imports.deinit(gpa);
175 ss.global_imports.deinit(gpa);
158 ss.symbol_table.deinit(gpa);176 ss.symbol_table.deinit(gpa);
159 ss.segment_info.deinit(gpa);177 ss.segment_info.deinit(gpa);
160 ss.* = undefined;178 ss.* = undefined;
...@@ -165,6 +183,7 @@ pub const ScratchSpace = struct {...@@ -165,6 +183,7 @@ pub const ScratchSpace = struct {
165 ss.func_types.clearRetainingCapacity();183 ss.func_types.clearRetainingCapacity();
166 ss.func_type_indexes.clearRetainingCapacity();184 ss.func_type_indexes.clearRetainingCapacity();
167 ss.func_imports.clearRetainingCapacity();185 ss.func_imports.clearRetainingCapacity();
186 ss.global_imports.clearRetainingCapacity();
168 ss.symbol_table.clearRetainingCapacity();187 ss.symbol_table.clearRetainingCapacity();
169 ss.segment_info.clearRetainingCapacity();188 ss.segment_info.clearRetainingCapacity();
170 }189 }
...@@ -361,7 +380,7 @@ pub fn parse(...@@ -361,7 +380,7 @@ pub fn parse(
361 symbol.name = function_import.ptr(ss).name.toOptional();380 symbol.name = function_import.ptr(ss).name.toOptional();
362 }381 }
363 } else {382 } else {
364 symbol.pointee = .{ .function = @enumFromInt(functions_start + local_index) };383 symbol.pointee = .{ .function = @enumFromInt(functions_start + (local_index - ss.func_imports.items.len)) };
365 const name, pos = readBytes(bytes, pos);384 const name, pos = readBytes(bytes, pos);
366 symbol.name = (try wasm.internString(name)).toOptional();385 symbol.name = (try wasm.internString(name)).toOptional();
367 }386 }
...@@ -369,16 +388,16 @@ pub fn parse(...@@ -369,16 +388,16 @@ pub fn parse(
369 .global => {388 .global => {
370 const local_index, pos = readLeb(u32, bytes, pos);389 const local_index, pos = readLeb(u32, bytes, pos);
371 if (symbol.flags.undefined) {390 if (symbol.flags.undefined) {
372 const global_import: Wasm.GlobalImport.Index = @enumFromInt(global_imports_start + local_index);391 const global_import: ScratchSpace.GlobalImportIndex = @enumFromInt(local_index);
373 symbol.pointee = .{ .global_import = global_import };392 symbol.pointee = .{ .global_import = global_import };
374 if (symbol.flags.explicit_name) {393 if (symbol.flags.explicit_name) {
375 const name, pos = readBytes(bytes, pos);394 const name, pos = readBytes(bytes, pos);
376 symbol.name = (try wasm.internString(name)).toOptional();395 symbol.name = (try wasm.internString(name)).toOptional();
377 } else {396 } else {
378 symbol.name = global_import.key(wasm).toOptional();397 symbol.name = global_import.ptr(ss).name.toOptional();
379 }398 }
380 } else {399 } else {
381 symbol.pointee = .{ .global = @enumFromInt(globals_start + local_index) };400 symbol.pointee = .{ .global = @enumFromInt(globals_start + (local_index - ss.global_imports.items.len)) };
382 const name, pos = readBytes(bytes, pos);401 const name, pos = readBytes(bytes, pos);
383 symbol.name = (try wasm.internString(name)).toOptional();402 symbol.name = (try wasm.internString(name)).toOptional();
384 }403 }
...@@ -579,16 +598,11 @@ pub fn parse(...@@ -579,16 +598,11 @@ pub fn parse(
579 const valtype, pos = readEnum(std.wasm.Valtype, bytes, pos);598 const valtype, pos = readEnum(std.wasm.Valtype, bytes, pos);
580 const mutable = bytes[pos] == 0x01;599 const mutable = bytes[pos] == 0x01;
581 pos += 1;600 pos += 1;
582 try wasm.object_global_imports.put(gpa, interned_name, .{601 try ss.global_imports.append(gpa, .{
583 .flags = .{602 .name = interned_name,
584 .global_type = .{603 .valtype = valtype,
585 .valtype = .from(valtype),604 .mutable = mutable,
586 .mutable = mutable,605 .module_name = interned_module_name,
587 },
588 },
589 .module_name = interned_module_name.toOptional(),
590 .source_location = source_location,
591 .resolution = .unresolved,
592 });606 });
593 },607 },
594 .table => {608 .table => {
...@@ -840,6 +854,61 @@ pub fn parse(...@@ -840,6 +854,61 @@ pub fn parse(
840 };854 };
841 }855 }
842 },856 },
857 .global_import => |index| {
858 const ptr = index.ptr(ss);
859 const name = symbol.name.unwrap().?;
860 if (symbol.flags.binding == .local) {
861 diags.addParseError(path, "local symbol '{s}' references import", .{name.slice(wasm)});
862 continue;
863 }
864 const gop = try wasm.object_global_imports.getOrPut(gpa, name);
865 if (gop.found_existing) {
866 const existing_ty = gop.value_ptr.flags.global_type.to();
867 if (ptr.valtype != existing_ty.valtype) {
868 var err = try diags.addErrorWithNotes(2);
869 try err.addMsg("symbol '{s}' mismatching global types", .{name.slice(wasm)});
870 gop.value_ptr.source_location.addNote(wasm, &err, "type {s} here", .{@tagName(existing_ty.valtype)});
871 source_location.addNote(wasm, &err, "type {s} here", .{@tagName(ptr.valtype)});
872 continue;
873 }
874 if (ptr.mutable != existing_ty.mutable) {
875 var err = try diags.addErrorWithNotes(2);
876 try err.addMsg("symbol '{s}' mismatching global mutability", .{name.slice(wasm)});
877 gop.value_ptr.source_location.addNote(wasm, &err, "{s} here", .{
878 if (existing_ty.mutable) "mutable" else "not mutable",
879 });
880 source_location.addNote(wasm, &err, "{s} here", .{
881 if (ptr.mutable) "mutable" else "not mutable",
882 });
883 continue;
884 }
885 if (gop.value_ptr.module_name != ptr.module_name.toOptional()) {
886 var err = try diags.addErrorWithNotes(2);
887 try err.addMsg("function symbol '{s}' mismatching module names", .{name.slice(wasm)});
888 if (gop.value_ptr.module_name.slice(wasm)) |module_name| {
889 gop.value_ptr.source_location.addNote(wasm, &err, "module '{s}' here", .{module_name});
890 } else {
891 gop.value_ptr.source_location.addNote(wasm, &err, "no module here", .{});
892 }
893 source_location.addNote(wasm, &err, "module '{s}' here", .{ptr.module_name.slice(wasm)});
894 continue;
895 }
896 if (symbol.flags.binding == .strong) gop.value_ptr.flags.binding = .strong;
897 if (!symbol.flags.visibility_hidden) gop.value_ptr.flags.visibility_hidden = false;
898 if (symbol.flags.no_strip) gop.value_ptr.flags.no_strip = true;
899 } else {
900 gop.value_ptr.* = .{
901 .flags = symbol.flags,
902 .module_name = ptr.module_name.toOptional(),
903 .source_location = source_location,
904 .resolution = .unresolved,
905 };
906 gop.value_ptr.flags.global_type = .{
907 .valtype = .from(ptr.valtype),
908 .mutable = ptr.mutable,
909 };
910 }
911 },
843 .function => |index| {912 .function => |index| {
844 assert(!symbol.flags.undefined);913 assert(!symbol.flags.undefined);
845 const ptr = index.ptr(wasm);914 const ptr = index.ptr(wasm);
...@@ -882,7 +951,7 @@ pub fn parse(...@@ -882,7 +951,7 @@ pub fn parse(
882 }951 }
883 },952 },
884953
885 inline .global_import, .table_import => |i| {954 .table_import => |i| {
886 const ptr = i.value(wasm);955 const ptr = i.value(wasm);
887 assert(i.key(wasm).toOptional() == symbol.name); // TODO956 assert(i.key(wasm).toOptional() == symbol.name); // TODO
888 ptr.flags = symbol.flags;957 ptr.flags = symbol.flags;