Commit a72e7384aa

Andrew Kelley <andrew@ziglang.org>
2024-12-29 00:44:35
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 parent eb94389
Changed files (1)
src
link
src/link/Wasm/Object.zig
@@ -75,6 +75,13 @@ pub const FunctionImport = struct {
     function_index: ScratchSpace.FuncTypeIndex,
 };
 
+pub const GlobalImport = struct {
+    module_name: Wasm.String,
+    name: Wasm.String,
+    valtype: std.wasm.Valtype,
+    mutable: bool,
+};
+
 pub const DataSegmentFlags = enum(u32) { active, passive, active_memidx };
 
 pub const SubsectionType = enum(u8) {
@@ -105,7 +112,7 @@ pub const Symbol = struct {
         data: Wasm.ObjectData.Index,
         data_import: void,
         global: Wasm.ObjectGlobalIndex,
-        global_import: Wasm.GlobalImport.Index,
+        global_import: ScratchSpace.GlobalImportIndex,
         section: Wasm.ObjectSectionIndex,
         table: Wasm.ObjectTableIndex,
         table_import: Wasm.TableImport.Index,
@@ -116,6 +123,7 @@ pub const ScratchSpace = struct {
     func_types: std.ArrayListUnmanaged(Wasm.FunctionType.Index) = .empty,
     func_type_indexes: std.ArrayListUnmanaged(FuncTypeIndex) = .empty,
     func_imports: std.ArrayListUnmanaged(FunctionImport) = .empty,
+    global_imports: std.ArrayListUnmanaged(GlobalImport) = .empty,
     symbol_table: std.ArrayListUnmanaged(Symbol) = .empty,
     segment_info: std.ArrayListUnmanaged(SegmentInfo) = .empty,
     exports: std.ArrayListUnmanaged(Export) = .empty,
@@ -141,6 +149,15 @@ pub const ScratchSpace = struct {
         }
     };
 
+    /// Index into `global_imports`.
+    const GlobalImportIndex = enum(u32) {
+        _,
+
+        fn ptr(index: GlobalImportIndex, ss: *const ScratchSpace) *GlobalImport {
+            return &ss.global_imports.items[@intFromEnum(index)];
+        }
+    };
+
     /// Index into `func_types`.
     const FuncTypeIndex = enum(u32) {
         _,
@@ -155,6 +172,7 @@ pub const ScratchSpace = struct {
         ss.func_types.deinit(gpa);
         ss.func_type_indexes.deinit(gpa);
         ss.func_imports.deinit(gpa);
+        ss.global_imports.deinit(gpa);
         ss.symbol_table.deinit(gpa);
         ss.segment_info.deinit(gpa);
         ss.* = undefined;
@@ -165,6 +183,7 @@ pub const ScratchSpace = struct {
         ss.func_types.clearRetainingCapacity();
         ss.func_type_indexes.clearRetainingCapacity();
         ss.func_imports.clearRetainingCapacity();
+        ss.global_imports.clearRetainingCapacity();
         ss.symbol_table.clearRetainingCapacity();
         ss.segment_info.clearRetainingCapacity();
     }
@@ -361,7 +380,7 @@ pub fn parse(
                                                     symbol.name = function_import.ptr(ss).name.toOptional();
                                                 }
                                             } else {
-                                                symbol.pointee = .{ .function = @enumFromInt(functions_start + local_index) };
+                                                symbol.pointee = .{ .function = @enumFromInt(functions_start + (local_index - ss.func_imports.items.len)) };
                                                 const name, pos = readBytes(bytes, pos);
                                                 symbol.name = (try wasm.internString(name)).toOptional();
                                             }
@@ -369,16 +388,16 @@ pub fn parse(
                                         .global => {
                                             const local_index, pos = readLeb(u32, bytes, pos);
                                             if (symbol.flags.undefined) {
-                                                const global_import: Wasm.GlobalImport.Index = @enumFromInt(global_imports_start + local_index);
+                                                const global_import: ScratchSpace.GlobalImportIndex = @enumFromInt(local_index);
                                                 symbol.pointee = .{ .global_import = global_import };
                                                 if (symbol.flags.explicit_name) {
                                                     const name, pos = readBytes(bytes, pos);
                                                     symbol.name = (try wasm.internString(name)).toOptional();
                                                 } else {
-                                                    symbol.name = global_import.key(wasm).toOptional();
+                                                    symbol.name = global_import.ptr(ss).name.toOptional();
                                                 }
                                             } else {
-                                                symbol.pointee = .{ .global = @enumFromInt(globals_start + local_index) };
+                                                symbol.pointee = .{ .global = @enumFromInt(globals_start + (local_index - ss.global_imports.items.len)) };
                                                 const name, pos = readBytes(bytes, pos);
                                                 symbol.name = (try wasm.internString(name)).toOptional();
                                             }
@@ -579,16 +598,11 @@ pub fn parse(
                             const valtype, pos = readEnum(std.wasm.Valtype, bytes, pos);
                             const mutable = bytes[pos] == 0x01;
                             pos += 1;
-                            try wasm.object_global_imports.put(gpa, interned_name, .{
-                                .flags = .{
-                                    .global_type = .{
-                                        .valtype = .from(valtype),
-                                        .mutable = mutable,
-                                    },
-                                },
-                                .module_name = interned_module_name.toOptional(),
-                                .source_location = source_location,
-                                .resolution = .unresolved,
+                            try ss.global_imports.append(gpa, .{
+                                .name = interned_name,
+                                .valtype = valtype,
+                                .mutable = mutable,
+                                .module_name = interned_module_name,
                             });
                         },
                         .table => {
@@ -840,6 +854,61 @@ pub fn parse(
                 };
             }
         },
+        .global_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_global_imports.getOrPut(gpa, name);
+            if (gop.found_existing) {
+                const existing_ty = gop.value_ptr.flags.global_type.to();
+                if (ptr.valtype != existing_ty.valtype) {
+                    var err = try diags.addErrorWithNotes(2);
+                    try err.addMsg("symbol '{s}' mismatching global types", .{name.slice(wasm)});
+                    gop.value_ptr.source_location.addNote(wasm, &err, "type {s} here", .{@tagName(existing_ty.valtype)});
+                    source_location.addNote(wasm, &err, "type {s} here", .{@tagName(ptr.valtype)});
+                    continue;
+                }
+                if (ptr.mutable != existing_ty.mutable) {
+                    var err = try diags.addErrorWithNotes(2);
+                    try err.addMsg("symbol '{s}' mismatching global mutability", .{name.slice(wasm)});
+                    gop.value_ptr.source_location.addNote(wasm, &err, "{s} here", .{
+                        if (existing_ty.mutable) "mutable" else "not mutable",
+                    });
+                    source_location.addNote(wasm, &err, "{s} here", .{
+                        if (ptr.mutable) "mutable" else "not mutable",
+                    });
+                    continue;
+                }
+                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)});
+                    if (gop.value_ptr.module_name.slice(wasm)) |module_name| {
+                        gop.value_ptr.source_location.addNote(wasm, &err, "module '{s}' here", .{module_name});
+                    } else {
+                        gop.value_ptr.source_location.addNote(wasm, &err, "no module here", .{});
+                    }
+                    source_location.addNote(wasm, &err, "module '{s}' here", .{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.toOptional(),
+                    .source_location = source_location,
+                    .resolution = .unresolved,
+                };
+                gop.value_ptr.flags.global_type = .{
+                    .valtype = .from(ptr.valtype),
+                    .mutable = ptr.mutable,
+                };
+            }
+        },
         .function => |index| {
             assert(!symbol.flags.undefined);
             const ptr = index.ptr(wasm);
@@ -882,7 +951,7 @@ pub fn parse(
             }
         },
 
-        inline .global_import, .table_import => |i| {
+        .table_import => |i| {
             const ptr = i.value(wasm);
             assert(i.key(wasm).toOptional() == symbol.name); // TODO
             ptr.flags = symbol.flags;