Commit a4895f3c42

Andrew Kelley <andrew@ziglang.org>
2024-12-30 23:15:48
wasm object parsing: fix handling of weak functions and globals
1 parent 4fccb5a
Changed files (3)
src/link/Wasm/Object.zig
@@ -982,9 +982,6 @@ pub fn parse(
                     source_location.addNote(&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,
@@ -1004,7 +1001,7 @@ pub fn parse(
             }
             const gop = try wasm.object_global_imports.getOrPut(gpa, name);
             if (gop.found_existing) {
-                const existing_ty = gop.value_ptr.flags.global_type.to();
+                const existing_ty = gop.value_ptr.type();
                 if (ptr.valtype != existing_ty.valtype) {
                     var err = try diags.addErrorWithNotes(2);
                     try err.addMsg("symbol '{s}' mismatching global types", .{name.slice(wasm)});
@@ -1034,9 +1031,6 @@ pub fn parse(
                     source_location.addNote(&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,
@@ -1117,6 +1111,11 @@ pub fn parse(
                     gop.value_ptr.source_location = source_location;
                     gop.value_ptr.module_name = host_name;
                     gop.value_ptr.resolution = .fromObjectFunction(wasm, index);
+                    gop.value_ptr.flags = symbol.flags;
+                    continue;
+                }
+                if (ptr.flags.binding == .weak) {
+                    // Keep the existing one.
                     continue;
                 }
                 var err = try diags.addErrorWithNotes(2);
@@ -1134,8 +1133,65 @@ pub fn parse(
                 };
             }
         },
-
-        inline .global, .table => |i| {
+        .global => |index| {
+            const ptr = index.ptr(wasm);
+            ptr.name = symbol.name;
+            ptr.flags = symbol.flags;
+            if (symbol.flags.binding == .local) continue; // No participation in symbol resolution.
+            const new_ty = ptr.type();
+            const name = symbol.name.unwrap().?;
+            const gop = try wasm.object_global_imports.getOrPut(gpa, name);
+            if (gop.found_existing) {
+                const existing_ty = gop.value_ptr.type();
+                if (new_ty.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(&err, "type {s} here", .{@tagName(existing_ty.valtype)});
+                    source_location.addNote(&err, "type {s} here", .{@tagName(new_ty.valtype)});
+                    continue;
+                }
+                if (new_ty.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(&err, "{s} here", .{
+                        if (existing_ty.mutable) "mutable" else "not mutable",
+                    });
+                    source_location.addNote(&err, "{s} here", .{
+                        if (new_ty.mutable) "mutable" else "not mutable",
+                    });
+                    continue;
+                }
+                if (gop.value_ptr.resolution == .unresolved or gop.value_ptr.flags.binding == .weak) {
+                    // Intentional: if they're both weak, take the last one.
+                    gop.value_ptr.source_location = source_location;
+                    gop.value_ptr.module_name = host_name;
+                    gop.value_ptr.resolution = .fromObjectGlobal(wasm, index);
+                    gop.value_ptr.flags = symbol.flags;
+                    continue;
+                }
+                if (ptr.flags.binding == .weak) {
+                    // Keep the existing one.
+                    continue;
+                }
+                var err = try diags.addErrorWithNotes(2);
+                try err.addMsg("symbol collision: {s}", .{name.slice(wasm)});
+                gop.value_ptr.source_location.addNote(&err, "exported as {s} here", .{@tagName(existing_ty.valtype)});
+                source_location.addNote(&err, "exported as {s} here", .{@tagName(new_ty.valtype)});
+                continue;
+            } else {
+                gop.value_ptr.* = .{
+                    .flags = symbol.flags,
+                    .module_name = .none,
+                    .source_location = source_location,
+                    .resolution = .unresolved,
+                };
+                gop.value_ptr.flags.global_type = .{
+                    .valtype = .from(new_ty.valtype),
+                    .mutable = new_ty.mutable,
+                };
+            }
+        },
+        .table => |i| {
             const ptr = i.ptr(wasm);
             ptr.name = symbol.name;
             ptr.flags = symbol.flags;
src/link/Wasm.zig
@@ -1113,7 +1113,7 @@ pub const GlobalImport = extern struct {
             });
         }
 
-        fn fromObjectGlobal(wasm: *const Wasm, object_global: ObjectGlobalIndex) Resolution {
+        pub fn fromObjectGlobal(wasm: *const Wasm, object_global: ObjectGlobalIndex) Resolution {
             return pack(wasm, .{ .object_global = object_global });
         }
 
@@ -1154,9 +1154,13 @@ pub const GlobalImport = extern struct {
         }
 
         pub fn globalType(index: Index, wasm: *const Wasm) ObjectGlobal.Type {
-            return value(index, wasm).flags.global_type.to();
+            return value(index, wasm).type();
         }
     };
+
+    pub fn @"type"(gi: *const GlobalImport) ObjectGlobal.Type {
+        return gi.flags.global_type.to();
+    }
 };
 
 pub const ObjectGlobal = extern struct {
@@ -1169,6 +1173,10 @@ pub const ObjectGlobal = extern struct {
     offset: u32,
     size: u32,
 
+    pub fn @"type"(og: *const ObjectGlobal) Type {
+        return og.flags.global_type.to();
+    }
+
     pub const Type = struct {
         valtype: std.wasm.Valtype,
         mutable: bool,
src/Compilation.zig
@@ -1596,7 +1596,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
             .pdb_source_path = options.pdb_source_path,
             .pdb_out_path = options.pdb_out_path,
             .entry_addr = null, // CLI does not expose this option (yet?)
-            .object_host_name = null, // TODO expose in the CLI
+            .object_host_name = "env",
         };
 
         switch (options.cache_mode) {