Skip to content

Commit 4f2143b

Browse files
committed
link/Wasm: improve symbol resolution
This adds additional checks during symbol resolution: - Ensures function signatures match when a symbol will be replaced. - Ensures global types match when the symbol is being replaced. - When both symbols are undefined, ensures they have a matching module name. Those changes ensure the result will pass the validator when the runtime compiles the Wasm module. Additionally, this also slightly changes the behavior when both the existing symbol and new symbol are both defined. Rather than always resulting in a collision, it only results in a collision when both are also weak. Else, the non-weak symbol will be picked.
1 parent 63c25cc commit 4f2143b

File tree

1 file changed

+96
-4
lines changed

1 file changed

+96
-4
lines changed

src/link/Wasm.zig

Lines changed: 96 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -463,8 +463,6 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
463463
continue;
464464
}
465465

466-
// TODO: Store undefined symbols so we can verify at the end if they've all been found
467-
// if not, emit an error (unless --allow-undefined is enabled).
468466
const maybe_existing = try self.globals.getOrPut(self.base.allocator, sym_name_index);
469467
if (!maybe_existing.found_existing) {
470468
maybe_existing.value_ptr.* = location;
@@ -483,8 +481,15 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
483481
break :blk self.objects.items[file].name;
484482
} else self.name;
485483

486-
if (!existing_sym.isUndefined()) {
487-
if (!symbol.isUndefined()) {
484+
if (!existing_sym.isUndefined()) outer: {
485+
if (!symbol.isUndefined()) inner: {
486+
if (symbol.isWeak()) {
487+
break :inner; // ignore the new symbol (discard it)
488+
}
489+
if (existing_sym.isWeak()) {
490+
break :outer; // existing is weak, while new one isn't. Replace it.
491+
}
492+
// both are defined and weak, we have a symbol collision.
488493
log.err("symbol '{s}' defined multiple times", .{sym_name});
489494
log.err(" first definition in '{s}'", .{existing_file_path});
490495
log.err(" next definition in '{s}'", .{object.name});
@@ -502,6 +507,53 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void {
502507
return error.SymbolMismatchingType;
503508
}
504509

510+
if (existing_sym.isUndefined() and symbol.isUndefined()) {
511+
const existing_name = if (existing_loc.file) |file_index| blk: {
512+
const obj = self.objects.items[file_index];
513+
const name_index = obj.findImport(symbol.tag.externalType(), existing_sym.index).module_name;
514+
break :blk obj.string_table.get(name_index);
515+
} else blk: {
516+
const name_index = self.imports.get(existing_loc).?.module_name;
517+
break :blk self.string_table.get(name_index);
518+
};
519+
520+
const module_index = object.findImport(symbol.tag.externalType(), symbol.index).module_name;
521+
const module_name = object.string_table.get(module_index);
522+
if (!mem.eql(u8, existing_name, module_name)) {
523+
log.err("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{
524+
sym_name,
525+
existing_name,
526+
module_name,
527+
});
528+
log.err(" first definition in '{s}'", .{existing_file_path});
529+
log.err(" next definition in '{s}'", .{object.name});
530+
return error.ModuleNameMismatch;
531+
}
532+
}
533+
534+
if (existing_sym.tag == .global) {
535+
const existing_ty = self.getGlobalType(existing_loc);
536+
const new_ty = self.getGlobalType(location);
537+
if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) {
538+
log.err("symbol '{s}' mismatching global types", .{sym_name});
539+
log.err(" first definition in '{s}'", .{existing_file_path});
540+
log.err(" next definition in '{s}'", .{object.name});
541+
return error.GlobalTypeMismatch;
542+
}
543+
}
544+
545+
if (existing_sym.tag == .function) {
546+
const existing_ty = self.getFunctionSignature(existing_loc);
547+
const new_ty = self.getFunctionSignature(location);
548+
if (!existing_ty.eql(new_ty)) {
549+
log.err("symbol '{s}' mismatching function signatures.", .{sym_name});
550+
log.err(" expected signature {}, but found signature {}", .{ existing_ty, new_ty });
551+
log.err(" first definition in '{s}'", .{existing_file_path});
552+
log.err(" next definition in '{s}'", .{object.name});
553+
return error.FunctionSignatureMismatch;
554+
}
555+
}
556+
505557
// when both symbols are weak, we skip overwriting
506558
if (existing_sym.isWeak() and symbol.isWeak()) {
507559
try self.discarded.put(self.base.allocator, location, existing_loc);
@@ -797,6 +849,46 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void {
797849
try self.resolved_symbols.put(self.base.allocator, atom.symbolLoc(), {});
798850
}
799851

852+
/// From a given symbol location, returns its `wasm.GlobalType`.
853+
/// Asserts the Symbol represents a global.
854+
fn getGlobalType(self: *const Wasm, loc: SymbolLoc) wasm.GlobalType {
855+
const symbol = loc.getSymbol(self);
856+
assert(symbol.tag == .global);
857+
const is_undefined = symbol.isUndefined();
858+
if (loc.file) |file_index| {
859+
const obj: Object = self.objects.items[file_index];
860+
if (is_undefined) {
861+
return obj.findImport(.global, symbol.index).kind.global;
862+
}
863+
return obj.globals[symbol.index].global_type;
864+
}
865+
if (is_undefined) {
866+
return self.imports.get(loc).?.kind.global;
867+
}
868+
return self.wasm_globals.items[symbol.index].global_type;
869+
}
870+
871+
/// From a given symbol location, returns its `wasm.Type`.
872+
/// Asserts the Symbol represents a function.
873+
fn getFunctionSignature(self: *const Wasm, loc: SymbolLoc) wasm.Type {
874+
const symbol = loc.getSymbol(self);
875+
assert(symbol.tag == .function);
876+
const is_undefined = symbol.isUndefined();
877+
if (loc.file) |file_index| {
878+
const obj: Object = self.objects.items[file_index];
879+
if (is_undefined) {
880+
const ty_index = obj.findImport(.function, symbol.index).kind.function;
881+
return obj.func_types[ty_index];
882+
}
883+
return obj.func_types[obj.functions[symbol.index].type_index];
884+
}
885+
if (is_undefined) {
886+
const ty_index = self.imports.get(loc).?.kind.function;
887+
return self.func_types.items[ty_index];
888+
}
889+
return self.func_types.items[self.functions.get(.{ .file = loc.file, .index = loc.index }).?.type_index];
890+
}
891+
800892
/// Lowers a constant typed value to a local symbol and atom.
801893
/// Returns the symbol index of the local
802894
/// The given `decl` is the parent decl whom owns the constant.

0 commit comments

Comments
 (0)