Commit 2339b25fd4

Luuk de Gram <luuk@degram.dev>
2023-01-09 19:22:55
wasm-linker: discard symbol when both undefined
During symbol resolution when both symbols are undefined, we must discard the new symbol with a reference to the existing symbol. This ensures the original symbol remains undefined. This fixes symbol resolution when linking with WASI-libC.
1 parent cbbf8c8
Changed files (1)
src
src/link/Wasm.zig
@@ -600,27 +600,34 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
         }
 
         if (existing_sym.isUndefined() and symbol.isUndefined()) {
-            const existing_name = if (existing_loc.file) |file_index| blk: {
-                const obj = wasm.objects.items[file_index];
-                const name_index = obj.findImport(symbol.tag.externalType(), existing_sym.index).module_name;
-                break :blk obj.string_table.get(name_index);
-            } else blk: {
-                const name_index = wasm.imports.get(existing_loc).?.module_name;
-                break :blk wasm.string_table.get(name_index);
-            };
+            // only verify module/import name for function symbols
+            if (symbol.tag == .function) {
+                const existing_name = if (existing_loc.file) |file_index| blk: {
+                    const obj = wasm.objects.items[file_index];
+                    const name_index = obj.findImport(symbol.tag.externalType(), existing_sym.index).module_name;
+                    break :blk obj.string_table.get(name_index);
+                } else blk: {
+                    const name_index = wasm.imports.get(existing_loc).?.module_name;
+                    break :blk wasm.string_table.get(name_index);
+                };
 
-            const module_index = object.findImport(symbol.tag.externalType(), symbol.index).module_name;
-            const module_name = object.string_table.get(module_index);
-            if (!mem.eql(u8, existing_name, module_name)) {
-                log.err("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{
-                    sym_name,
-                    existing_name,
-                    module_name,
-                });
-                log.err("  first definition in '{s}'", .{existing_file_path});
-                log.err("  next definition in '{s}'", .{object.name});
-                return error.ModuleNameMismatch;
+                const module_index = object.findImport(symbol.tag.externalType(), symbol.index).module_name;
+                const module_name = object.string_table.get(module_index);
+                if (!mem.eql(u8, existing_name, module_name)) {
+                    log.err("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{
+                        sym_name,
+                        existing_name,
+                        module_name,
+                    });
+                    log.err("  first definition in '{s}'", .{existing_file_path});
+                    log.err("  next definition in '{s}'", .{object.name});
+                    return error.ModuleNameMismatch;
+                }
             }
+
+            // both undefined so skip overwriting existing symbol and discard the new symbol
+            try wasm.discarded.put(wasm.base.allocator, location, existing_loc);
+            continue;
         }
 
         if (existing_sym.tag == .global) {
@@ -646,8 +653,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void {
             }
         }
 
-        // when both symbols are weak, we skip overwriting
-        if (existing_sym.isWeak() and symbol.isWeak()) {
+        // when both symbols are weak, we skip overwriting unless the existing
+        // symbol is weak and the new one isn't, in which case we *do* overwrite it.
+        if (existing_sym.isWeak() and symbol.isWeak()) blk: {
+            if (existing_sym.isUndefined() and !symbol.isUndefined()) break :blk;
             try wasm.discarded.put(wasm.base.allocator, location, existing_loc);
             continue;
         }
@@ -1935,7 +1944,9 @@ fn setupStart(wasm: *Wasm) !void {
         return error.MissingSymbol;
     };
 
-    const symbol_loc = wasm.globals.get(symbol_name_offset).?;
+    const symbol_loc = wasm.globals.get(symbol_name_offset) orelse {
+        log.err("Entry symbol '{s}' not found", .{entry_name});
+    };
     const symbol = symbol_loc.getSymbol(wasm);
     if (symbol.tag != .function) {
         log.err("Entry symbol '{s}' is not a function", .{entry_name});