authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2024-02-12 17:12:53+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2024-02-29 15:24:07+01:00
log5ba5a2c133be5e06083f088aa875ff18658fbf8c
treefb79714cf6bbc5a519bbd7480a7ca32d6f2f4dc5
parent5ef832133895fd69fc8378463b86759eaab6913a
signaturelock-open Commit is signed but in an unrecognized format.

wasm: integrate linker errors with `Compilation`

Rather than using the logger, we now emit proper 'compiler'-errors just like the ELF and MachO linkers with notes. We now also support emitting multiple errors before quiting the linking process in certain phases, such as symbol resolution. This means we will print all symbols which were resolved incorrectly, rather than the first one we encounter.

4 files changed, 194 insertions(+), 106 deletions(-)

src/link/Wasm.zig+146-67
...@@ -655,9 +655,14 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {...@@ -655,9 +655,14 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {
655 errdefer obj_file.close();655 errdefer obj_file.close();
656656
657 const gpa = wasm.base.comp.gpa;657 const gpa = wasm.base.comp.gpa;
658 var object = Object.create(gpa, obj_file, path, null) catch |err| switch (err) {658 var object = Object.create(wasm, obj_file, path, null) catch |err| switch (err) {
659 error.InvalidMagicByte, error.NotObjectFile => return false,659 error.InvalidMagicByte, error.NotObjectFile => return false,
660 else => |e| return e,660 else => |e| {
661 var err_note = try wasm.addErrorWithNotes(1);
662 try err_note.addMsg(wasm, "Failed parsing object file: {s}", .{@errorName(e)});
663 try err_note.addNote(wasm, "while parsing '{s}'", .{path});
664 return error.FlushFailure;
665 },
661 };666 };
662 errdefer object.deinit(gpa);667 errdefer object.deinit(gpa);
663 object.index = @enumFromInt(wasm.files.len);668 object.index = @enumFromInt(wasm.files.len);
...@@ -708,7 +713,12 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {...@@ -708,7 +713,12 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
708 archive.deinit(gpa);713 archive.deinit(gpa);
709 return false;714 return false;
710 },715 },
711 else => |e| return e,716 else => |e| {
717 var err_note = try wasm.addErrorWithNotes(1);
718 try err_note.addMsg(wasm, "Failed parsing archive: {s}", .{@errorName(e)});
719 try err_note.addNote(wasm, "while parsing archive {s}", .{path});
720 return error.FlushFailure;
721 },
712 };722 };
713723
714 if (!force_load) {724 if (!force_load) {
...@@ -730,7 +740,12 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {...@@ -730,7 +740,12 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
730 }740 }
731741
732 for (offsets.keys()) |file_offset| {742 for (offsets.keys()) |file_offset| {
733 var object = try archive.parseObject(gpa, file_offset);743 var object = archive.parseObject(wasm, file_offset) catch |e| {
744 var err_note = try wasm.addErrorWithNotes(1);
745 try err_note.addMsg(wasm, "Failed parsing object: {s}", .{@errorName(e)});
746 try err_note.addNote(wasm, "while parsing object in archive {s}", .{path});
747 return error.FlushFailure;
748 };
734 object.index = @enumFromInt(wasm.files.len);749 object.index = @enumFromInt(wasm.files.len);
735 try wasm.files.append(gpa, .{ .object = object });750 try wasm.files.append(gpa, .{ .object = object });
736 try wasm.objects.append(gpa, object.index);751 try wasm.objects.append(gpa, object.index);
...@@ -764,9 +779,9 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {...@@ -764,9 +779,9 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
764779
765 if (symbol.isLocal()) {780 if (symbol.isLocal()) {
766 if (symbol.isUndefined()) {781 if (symbol.isUndefined()) {
767 log.err("Local symbols are not allowed to reference imports", .{});782 var err = try wasm.addErrorWithNotes(1);
768 log.err(" symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() });783 try err.addMsg(wasm, "Local symbols are not allowed to reference imports", .{});
769 return error.UndefinedLocal;784 try err.addNote(wasm, "symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() });
770 }785 }
771 try wasm.resolved_symbols.putNoClobber(gpa, location, {});786 try wasm.resolved_symbols.putNoClobber(gpa, location, {});
772 continue;787 continue;
...@@ -801,10 +816,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {...@@ -801,10 +816,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
801 break :outer; // existing is weak, while new one isn't. Replace it.816 break :outer; // existing is weak, while new one isn't. Replace it.
802 }817 }
803 // both are defined and weak, we have a symbol collision.818 // both are defined and weak, we have a symbol collision.
804 log.err("symbol '{s}' defined multiple times", .{sym_name});819 var err = try wasm.addErrorWithNotes(2);
805 log.err(" first definition in '{s}'", .{existing_file_path});820 try err.addMsg(wasm, "symbol '{s}' defined multiple times", .{sym_name});
806 log.err(" next definition in '{s}'", .{obj_file.path()});821 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
807 return error.SymbolCollision;822 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
808 }823 }
809824
810 try wasm.discarded.put(gpa, location, existing_loc);825 try wasm.discarded.put(gpa, location, existing_loc);
...@@ -812,10 +827,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {...@@ -812,10 +827,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
812 }827 }
813828
814 if (symbol.tag != existing_sym.tag) {829 if (symbol.tag != existing_sym.tag) {
815 log.err("symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) });830 var err = try wasm.addErrorWithNotes(2);
816 log.err(" first definition in '{s}'", .{existing_file_path});831 try err.addMsg(wasm, "symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) });
817 log.err(" next definition in '{s}'", .{obj_file.path()});832 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
818 return error.SymbolMismatchingType;833 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
819 }834 }
820835
821 if (existing_sym.isUndefined() and symbol.isUndefined()) {836 if (existing_sym.isUndefined() and symbol.isUndefined()) {
...@@ -832,14 +847,14 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {...@@ -832,14 +847,14 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
832 const imp = obj_file.import(sym_index);847 const imp = obj_file.import(sym_index);
833 const module_name = obj_file.string(imp.module_name);848 const module_name = obj_file.string(imp.module_name);
834 if (!mem.eql(u8, existing_name, module_name)) {849 if (!mem.eql(u8, existing_name, module_name)) {
835 log.err("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{850 var err = try wasm.addErrorWithNotes(2);
851 try err.addMsg(wasm, "symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{
836 sym_name,852 sym_name,
837 existing_name,853 existing_name,
838 module_name,854 module_name,
839 });855 });
840 log.err(" first definition in '{s}'", .{existing_file_path});856 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
841 log.err(" next definition in '{s}'", .{obj_file.path()});857 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
842 return error.ModuleNameMismatch;
843 }858 }
844 }859 }
845860
...@@ -852,10 +867,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {...@@ -852,10 +867,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
852 const existing_ty = wasm.getGlobalType(existing_loc);867 const existing_ty = wasm.getGlobalType(existing_loc);
853 const new_ty = wasm.getGlobalType(location);868 const new_ty = wasm.getGlobalType(location);
854 if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) {869 if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) {
855 log.err("symbol '{s}' mismatching global types", .{sym_name});870 var err = try wasm.addErrorWithNotes(2);
856 log.err(" first definition in '{s}'", .{existing_file_path});871 try err.addMsg(wasm, "symbol '{s}' mismatching global types", .{sym_name});
857 log.err(" next definition in '{s}'", .{obj_file.path()});872 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
858 return error.GlobalTypeMismatch;873 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
859 }874 }
860 }875 }
861876
...@@ -863,11 +878,11 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {...@@ -863,11 +878,11 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
863 const existing_ty = wasm.getFunctionSignature(existing_loc);878 const existing_ty = wasm.getFunctionSignature(existing_loc);
864 const new_ty = wasm.getFunctionSignature(location);879 const new_ty = wasm.getFunctionSignature(location);
865 if (!existing_ty.eql(new_ty)) {880 if (!existing_ty.eql(new_ty)) {
866 log.err("symbol '{s}' mismatching function signatures.", .{sym_name});881 var err = try wasm.addErrorWithNotes(3);
867 log.err(" expected signature {}, but found signature {}", .{ existing_ty, new_ty });882 try err.addMsg(wasm, "symbol '{s}' mismatching function signatures.", .{sym_name});
868 log.err(" first definition in '{s}'", .{existing_file_path});883 try err.addNote(wasm, "expected signature {}, but found signature {}", .{ existing_ty, new_ty });
869 log.err(" next definition in '{s}'", .{obj_file.path()});884 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
870 return error.FunctionSignatureMismatch;885 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
871 }886 }
872 }887 }
873888
...@@ -914,7 +929,12 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {...@@ -914,7 +929,12 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
914 // Symbol is found in unparsed object file within current archive.929 // Symbol is found in unparsed object file within current archive.
915 // Parse object and and resolve symbols again before we check remaining930 // Parse object and and resolve symbols again before we check remaining
916 // undefined symbols.931 // undefined symbols.
917 var object = try archive.parseObject(gpa, offset.items[0]);932 var object = archive.parseObject(wasm, offset.items[0]) catch |e| {
933 var err_note = try wasm.addErrorWithNotes(1);
934 try err_note.addMsg(wasm, "Failed parsing object: {s}", .{@errorName(e)});
935 try err_note.addNote(wasm, "while parsing object in archive {s}", .{archive.name});
936 return error.FlushFailure;
937 };
918 object.index = @enumFromInt(wasm.files.len);938 object.index = @enumFromInt(wasm.files.len);
919 try wasm.files.append(gpa, .{ .object = object });939 try wasm.files.append(gpa, .{ .object = object });
920 try wasm.objects.append(gpa, object.index);940 try wasm.objects.append(gpa, object.index);
...@@ -1214,20 +1234,21 @@ fn validateFeatures(...@@ -1214,20 +1234,21 @@ fn validateFeatures(
1214 allowed[used_index] = is_enabled;1234 allowed[used_index] = is_enabled;
1215 emit_features_count.* += @intFromBool(is_enabled);1235 emit_features_count.* += @intFromBool(is_enabled);
1216 } else if (is_enabled and !allowed[used_index]) {1236 } else if (is_enabled and !allowed[used_index]) {
1217 log.err("feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))});1237 var err = try wasm.addErrorWithNotes(1);
1218 log.err(" defined in '{s}'", .{wasm.files.items(.data)[used_set >> 1].object.path});1238 try err.addMsg(wasm, "feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))});
1239 try err.addNote(wasm, "defined in '{s}'", .{wasm.files.items(.data)[used_set >> 1].object.path});
1219 valid_feature_set = false;1240 valid_feature_set = false;
1220 }1241 }
1221 }1242 }
12221243
1223 if (!valid_feature_set) {1244 if (!valid_feature_set) {
1224 return error.InvalidFeatureSet;1245 return error.FlushFailure;
1225 }1246 }
12261247
1227 if (shared_memory) {1248 if (shared_memory) {
1228 const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)];1249 const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)];
1229 if (@as(u1, @truncate(disallowed_feature)) != 0) {1250 if (@as(u1, @truncate(disallowed_feature)) != 0) {
1230 log.err(1251 try wasm.addErrorWithoutNotes(
1231 "shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled",1252 "shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled",
1232 .{wasm.files.items(.data)[disallowed_feature >> 1].object.path},1253 .{wasm.files.items(.data)[disallowed_feature >> 1].object.path},
1233 );1254 );
...@@ -1236,7 +1257,7 @@ fn validateFeatures(...@@ -1236,7 +1257,7 @@ fn validateFeatures(
12361257
1237 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {1258 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
1238 if (!allowed[@intFromEnum(feature)]) {1259 if (!allowed[@intFromEnum(feature)]) {
1239 log.err("feature '{}' is not used but is required for shared-memory", .{feature});1260 try wasm.addErrorWithoutNotes("feature '{}' is not used but is required for shared-memory", .{feature});
1240 }1261 }
1241 }1262 }
1242 }1263 }
...@@ -1244,7 +1265,7 @@ fn validateFeatures(...@@ -1244,7 +1265,7 @@ fn validateFeatures(
1244 if (has_tls) {1265 if (has_tls) {
1245 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {1266 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
1246 if (!allowed[@intFromEnum(feature)]) {1267 if (!allowed[@intFromEnum(feature)]) {
1247 log.err("feature '{}' is not used but is required for thread-local storage", .{feature});1268 try wasm.addErrorWithoutNotes("feature '{}' is not used but is required for thread-local storage", .{feature});
1248 }1269 }
1249 }1270 }
1250 }1271 }
...@@ -1257,9 +1278,10 @@ fn validateFeatures(...@@ -1257,9 +1278,10 @@ fn validateFeatures(
1257 // from here a feature is always used1278 // from here a feature is always used
1258 const disallowed_feature = disallowed[@intFromEnum(feature.tag)];1279 const disallowed_feature = disallowed[@intFromEnum(feature.tag)];
1259 if (@as(u1, @truncate(disallowed_feature)) != 0) {1280 if (@as(u1, @truncate(disallowed_feature)) != 0) {
1260 log.err("feature '{}' is disallowed, but used by linked object", .{feature.tag});1281 var err = try wasm.addErrorWithNotes(2);
1261 log.err(" disallowed by '{s}'", .{wasm.files.items(.data)[disallowed_feature >> 1].object.path});1282 try err.addMsg(wasm, "feature '{}' is disallowed, but used by linked object", .{feature.tag});
1262 log.err(" used in '{s}'", .{object.path});1283 try err.addNote(wasm, "disallowed by '{s}'", .{wasm.files.items(.data)[disallowed_feature >> 1].object.path});
1284 try err.addNote(wasm, "used in '{s}'", .{object.path});
1263 valid_feature_set = false;1285 valid_feature_set = false;
1264 }1286 }
12651287
...@@ -1270,16 +1292,17 @@ fn validateFeatures(...@@ -1270,16 +1292,17 @@ fn validateFeatures(
1270 for (required, 0..) |required_feature, feature_index| {1292 for (required, 0..) |required_feature, feature_index| {
1271 const is_required = @as(u1, @truncate(required_feature)) != 0;1293 const is_required = @as(u1, @truncate(required_feature)) != 0;
1272 if (is_required and !object_used_features[feature_index]) {1294 if (is_required and !object_used_features[feature_index]) {
1273 log.err("feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))});1295 var err = try wasm.addErrorWithNotes(2);
1274 log.err(" required by '{s}'", .{wasm.files.items(.data)[required_feature >> 1].object.path});1296 try err.addMsg(wasm, "feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))});
1275 log.err(" missing in '{s}'", .{object.path});1297 try err.addNote(wasm, "required by '{s}'", .{wasm.files.items(.data)[required_feature >> 1].object.path});
1298 try err.addNote(wasm, "missing in '{s}'", .{object.path});
1276 valid_feature_set = false;1299 valid_feature_set = false;
1277 }1300 }
1278 }1301 }
1279 }1302 }
12801303
1281 if (!valid_feature_set) {1304 if (!valid_feature_set) {
1282 return error.InvalidFeatureSet;1305 return error.FlushFailure;
1283 }1306 }
12841307
1285 to_emit.* = allowed;1308 to_emit.* = allowed;
...@@ -1350,12 +1373,13 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void {...@@ -1350,12 +1373,13 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void {
1350 else1373 else
1351 wasm.name;1374 wasm.name;
1352 const symbol_name = undef.getName(wasm);1375 const symbol_name = undef.getName(wasm);
1353 log.err("could not resolve undefined symbol '{s}'", .{symbol_name});1376 var err = try wasm.addErrorWithNotes(1);
1354 log.err(" defined in '{s}'", .{file_name});1377 try err.addMsg(wasm, "could not resolve undefined symbol '{s}'", .{symbol_name});
1378 try err.addNote(wasm, "defined in '{s}'", .{file_name});
1355 }1379 }
1356 }1380 }
1357 if (found_undefined_symbols) {1381 if (found_undefined_symbols) {
1358 return error.UndefinedSymbol;1382 return error.FlushFailure;
1359 }1383 }
1360}1384}
13611385
...@@ -1728,8 +1752,7 @@ fn setupInitFunctions(wasm: *Wasm) !void {...@@ -1728,8 +1752,7 @@ fn setupInitFunctions(wasm: *Wasm) !void {
1728 break :ty object.func_types[func.type_index];1752 break :ty object.func_types[func.type_index];
1729 };1753 };
1730 if (ty.params.len != 0) {1754 if (ty.params.len != 0) {
1731 log.err("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)});1755 try wasm.addErrorWithoutNotes("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)});
1732 return error.InvalidInitFunc;
1733 }1756 }
1734 log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)});1757 log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)});
1735 wasm.init_funcs.appendAssumeCapacity(.{1758 wasm.init_funcs.appendAssumeCapacity(.{
...@@ -2108,7 +2131,7 @@ fn setupExports(wasm: *Wasm) !void {...@@ -2108,7 +2131,7 @@ fn setupExports(wasm: *Wasm) !void {
21082131
2109 for (force_exp_names) |exp_name| {2132 for (force_exp_names) |exp_name| {
2110 const loc = wasm.findGlobalSymbol(exp_name) orelse {2133 const loc = wasm.findGlobalSymbol(exp_name) orelse {
2111 log.err("could not export '{s}', symbol not found", .{exp_name});2134 try wasm.addErrorWithoutNotes("could not export '{s}', symbol not found", .{exp_name});
2112 failed_exports = true;2135 failed_exports = true;
2113 continue;2136 continue;
2114 };2137 };
...@@ -2118,7 +2141,7 @@ fn setupExports(wasm: *Wasm) !void {...@@ -2118,7 +2141,7 @@ fn setupExports(wasm: *Wasm) !void {
2118 }2141 }
21192142
2120 if (failed_exports) {2143 if (failed_exports) {
2121 return error.MissingSymbol;2144 return error.FlushFailure;
2122 }2145 }
2123 }2146 }
21242147
...@@ -2164,14 +2187,14 @@ fn setupStart(wasm: *Wasm) !void {...@@ -2164,14 +2187,14 @@ fn setupStart(wasm: *Wasm) !void {
2164 const entry_name = wasm.entry_name orelse return;2187 const entry_name = wasm.entry_name orelse return;
21652188
2166 const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {2189 const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {
2167 log.err("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});2190 try wasm.addErrorWithoutNotes("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});
2168 return error.MissingSymbol;2191 return error.FlushFailure;
2169 };2192 };
21702193
2171 const symbol = symbol_loc.getSymbol(wasm);2194 const symbol = symbol_loc.getSymbol(wasm);
2172 if (symbol.tag != .function) {2195 if (symbol.tag != .function) {
2173 log.err("Entry symbol '{s}' is not a function", .{entry_name});2196 try wasm.addErrorWithoutNotes("Entry symbol '{s}' is not a function", .{entry_name});
2174 return error.InvalidEntryKind;2197 return error.FlushFailure;
2175 }2198 }
21762199
2177 // Ensure the symbol is exported so host environment can access it2200 // Ensure the symbol is exported so host environment can access it
...@@ -2274,16 +2297,13 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2274,16 +2297,13 @@ fn setupMemory(wasm: *Wasm) !void {
22742297
2275 if (wasm.initial_memory) |initial_memory| {2298 if (wasm.initial_memory) |initial_memory| {
2276 if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) {2299 if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) {
2277 log.err("Initial memory must be {d}-byte aligned", .{page_size});2300 try wasm.addErrorWithoutNotes("Initial memory must be {d}-byte aligned", .{page_size});
2278 return error.MissAlignment;
2279 }2301 }
2280 if (memory_ptr > initial_memory) {2302 if (memory_ptr > initial_memory) {
2281 log.err("Initial memory too small, must be at least {d} bytes", .{memory_ptr});2303 try wasm.addErrorWithoutNotes("Initial memory too small, must be at least {d} bytes", .{memory_ptr});
2282 return error.MemoryTooSmall;
2283 }2304 }
2284 if (initial_memory > max_memory_allowed) {2305 if (initial_memory > max_memory_allowed) {
2285 log.err("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});2306 try wasm.addErrorWithoutNotes("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});
2286 return error.MemoryTooBig;
2287 }2307 }
2288 memory_ptr = initial_memory;2308 memory_ptr = initial_memory;
2289 }2309 }
...@@ -2300,16 +2320,13 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2300,16 +2320,13 @@ fn setupMemory(wasm: *Wasm) !void {
23002320
2301 if (wasm.max_memory) |max_memory| {2321 if (wasm.max_memory) |max_memory| {
2302 if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) {2322 if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) {
2303 log.err("Maximum memory must be {d}-byte aligned", .{page_size});2323 try wasm.addErrorWithoutNotes("Maximum memory must be {d}-byte aligned", .{page_size});
2304 return error.MissAlignment;
2305 }2324 }
2306 if (memory_ptr > max_memory) {2325 if (memory_ptr > max_memory) {
2307 log.err("Maxmimum memory too small, must be at least {d} bytes", .{memory_ptr});2326 try wasm.addErrorWithoutNotes("Maxmimum memory too small, must be at least {d} bytes", .{memory_ptr});
2308 return error.MemoryTooSmall;
2309 }2327 }
2310 if (max_memory > max_memory_allowed) {2328 if (max_memory > max_memory_allowed) {
2311 log.err("Maximum memory exceeds maxmium amount {d}", .{max_memory_allowed});2329 try wasm.addErrorWithoutNotes("Maximum memory exceeds maxmium amount {d}", .{max_memory_allowed});
2312 return error.MemoryTooBig;
2313 }2330 }
2314 wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size));2331 wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size));
2315 wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX);2332 wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX);
...@@ -2412,7 +2429,9 @@ pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: Sym...@@ -2412,7 +2429,9 @@ pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: Sym
2412 break :blk index;2429 break :blk index;
2413 };2430 };
2414 } else {2431 } else {
2415 log.err("found unknown section '{s}'", .{section_name});2432 var err = try wasm.addErrorWithNotes(1);
2433 try err.addMsg(wasm, "found unknown section '{s}'", .{section_name});
2434 try err.addNote(wasm, "defined in '{s}'", .{obj_file.path()});
2416 return error.UnexpectedValue;2435 return error.UnexpectedValue;
2417 }2436 }
2418 },2437 },
...@@ -2529,18 +2548,22 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node)...@@ -2529,18 +2548,22 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node)
2529 if (wasm.zig_object_index != .null) {2548 if (wasm.zig_object_index != .null) {
2530 try wasm.resolveSymbolsInObject(wasm.zig_object_index);2549 try wasm.resolveSymbolsInObject(wasm.zig_object_index);
2531 }2550 }
2551 if (comp.link_errors.items.len > 0) return error.FlushFailure;
2532 for (wasm.objects.items) |object_index| {2552 for (wasm.objects.items) |object_index| {
2533 try wasm.resolveSymbolsInObject(object_index);2553 try wasm.resolveSymbolsInObject(object_index);
2534 }2554 }
2555 if (comp.link_errors.items.len > 0) return error.FlushFailure;
25352556
2536 var emit_features_count: u32 = 0;2557 var emit_features_count: u32 = 0;
2537 var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;2558 var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;
2538 try wasm.validateFeatures(&enabled_features, &emit_features_count);2559 try wasm.validateFeatures(&enabled_features, &emit_features_count);
2539 try wasm.resolveSymbolsInArchives();2560 try wasm.resolveSymbolsInArchives();
2561 if (comp.link_errors.items.len > 0) return error.FlushFailure;
2540 try wasm.resolveLazySymbols();2562 try wasm.resolveLazySymbols();
2541 try wasm.checkUndefinedSymbols();2563 try wasm.checkUndefinedSymbols();
25422564
2543 try wasm.setupInitFunctions();2565 try wasm.setupInitFunctions();
2566 if (comp.link_errors.items.len > 0) return error.FlushFailure;
2544 try wasm.setupStart();2567 try wasm.setupStart();
25452568
2546 try wasm.markReferences();2569 try wasm.markReferences();
...@@ -2549,6 +2572,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node)...@@ -2549,6 +2572,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node)
2549 try wasm.mergeTypes();2572 try wasm.mergeTypes();
2550 try wasm.allocateAtoms();2573 try wasm.allocateAtoms();
2551 try wasm.setupMemory();2574 try wasm.setupMemory();
2575 if (comp.link_errors.items.len > 0) return error.FlushFailure;
2552 wasm.allocateVirtualAddresses();2576 wasm.allocateVirtualAddresses();
2553 wasm.mapFunctionTable();2577 wasm.mapFunctionTable();
2554 try wasm.initializeCallCtorsFunction();2578 try wasm.initializeCallCtorsFunction();
...@@ -2558,6 +2582,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node)...@@ -2558,6 +2582,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node)
2558 try wasm.setupStartSection();2582 try wasm.setupStartSection();
2559 try wasm.setupExports();2583 try wasm.setupExports();
2560 try wasm.writeToFile(enabled_features, emit_features_count, arena);2584 try wasm.writeToFile(enabled_features, emit_features_count, arena);
2585 if (comp.link_errors.items.len > 0) return error.FlushFailure;
2561}2586}
25622587
2563/// Writes the WebAssembly in-memory module to the file2588/// Writes the WebAssembly in-memory module to the file
...@@ -2955,7 +2980,7 @@ fn writeToFile(...@@ -2955,7 +2980,7 @@ fn writeToFile(
2955 }) catch unreachable;2980 }) catch unreachable;
2956 try emitBuildIdSection(&binary_bytes, str);2981 try emitBuildIdSection(&binary_bytes, str);
2957 },2982 },
2958 else => |mode| log.err("build-id '{s}' is not supported for WASM", .{@tagName(mode)}),2983 else => |mode| try wasm.addErrorWithoutNotes("build-id '{s}' is not supported for WebAssembly", .{@tagName(mode)}),
2959 }2984 }
29602985
2961 var debug_bytes = std.ArrayList(u8).init(gpa);2986 var debug_bytes = std.ArrayList(u8).init(gpa);
...@@ -4043,3 +4068,57 @@ fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8...@@ -4043,3 +4068,57 @@ fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8
4043 .command => "_start",4068 .command => "_start",
4044 };4069 };
4045}4070}
4071
4072const ErrorWithNotes = struct {
4073 /// Allocated index in comp.link_errors array.
4074 index: usize,
4075
4076 /// Next available note slot.
4077 note_slot: usize = 0,
4078
4079 pub fn addMsg(
4080 err: ErrorWithNotes,
4081 wasm_file: *const Wasm,
4082 comptime format: []const u8,
4083 args: anytype,
4084 ) error{OutOfMemory}!void {
4085 const comp = wasm_file.base.comp;
4086 const gpa = comp.gpa;
4087 const err_msg = &comp.link_errors.items[err.index];
4088 err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
4089 }
4090
4091 pub fn addNote(
4092 err: *ErrorWithNotes,
4093 wasm_file: *const Wasm,
4094 comptime format: []const u8,
4095 args: anytype,
4096 ) error{OutOfMemory}!void {
4097 const comp = wasm_file.base.comp;
4098 const gpa = comp.gpa;
4099 const err_msg = &comp.link_errors.items[err.index];
4100 err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };
4101 err.note_slot += 1;
4102 }
4103};
4104
4105pub fn addErrorWithNotes(wasm: *const Wasm, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
4106 const comp = wasm.base.comp;
4107 const gpa = comp.gpa;
4108 try comp.link_errors.ensureUnusedCapacity(gpa, 1);
4109 return wasm.addErrorWithNotesAssumeCapacity(note_count);
4110}
4111
4112pub fn addErrorWithoutNotes(wasm: *const Wasm, comptime fmt: []const u8, args: anytype) !void {
4113 const err = try wasm.addErrorWithNotes(0);
4114 try err.addMsg(wasm, fmt, args);
4115}
4116
4117fn addErrorWithNotesAssumeCapacity(wasm: *const Wasm, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
4118 const comp = wasm.base.comp;
4119 const gpa = comp.gpa;
4120 const index = comp.link_errors.items.len;
4121 const err = comp.link_errors.addOneAssumeCapacity();
4122 err.* = .{ .msg = undefined, .notes = try gpa.alloc(link.File.ErrorMsg, note_count) };
4123 return .{ .index = index };
4124}
src/link/Wasm/Archive.zig+21-25
...@@ -1,14 +1,3 @@...@@ -1,14 +1,3 @@
1const Archive = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const fs = std.fs;
6const log = std.log.scoped(.archive);
7const mem = std.mem;
8
9const Allocator = mem.Allocator;
10const Object = @import("Object.zig");
11
12file: fs.File,1file: fs.File,
13name: []const u8,2name: []const u8,
143
...@@ -151,10 +140,7 @@ fn parseTableOfContents(archive: *Archive, allocator: Allocator, reader: anytype...@@ -151,10 +140,7 @@ fn parseTableOfContents(archive: *Archive, allocator: Allocator, reader: anytype
151 const sym_tab = try allocator.alloc(u8, sym_tab_size - 4 - (4 * num_symbols));140 const sym_tab = try allocator.alloc(u8, sym_tab_size - 4 - (4 * num_symbols));
152 defer allocator.free(sym_tab);141 defer allocator.free(sym_tab);
153142
154 reader.readNoEof(sym_tab) catch {143 reader.readNoEof(sym_tab) catch return error.IncompleteSymbolTable;
155 log.err("incomplete symbol table: expected symbol table of length 0x{x}", .{sym_tab.len});
156 return error.MalformedArchive;
157 };
158144
159 var i: usize = 0;145 var i: usize = 0;
160 var pos: usize = 0;146 var pos: usize = 0;
...@@ -178,12 +164,10 @@ fn parseTableOfContents(archive: *Archive, allocator: Allocator, reader: anytype...@@ -178,12 +164,10 @@ fn parseTableOfContents(archive: *Archive, allocator: Allocator, reader: anytype
178fn parseNameTable(archive: *Archive, allocator: Allocator, reader: anytype) !void {164fn parseNameTable(archive: *Archive, allocator: Allocator, reader: anytype) !void {
179 const header: ar_hdr = try reader.readStruct(ar_hdr);165 const header: ar_hdr = try reader.readStruct(ar_hdr);
180 if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) {166 if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) {
181 log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, header.ar_fmag });167 return error.InvalidHeaderDelimiter;
182 return error.MalformedArchive;
183 }168 }
184 if (!mem.eql(u8, header.ar_name[0..2], "//")) {169 if (!mem.eql(u8, header.ar_name[0..2], "//")) {
185 log.err("invalid archive. Long name table missing", .{});170 return error.MissingTableName;
186 return error.MalformedArchive;
187 }171 }
188 const table_size = try header.size();172 const table_size = try header.size();
189 const long_file_names = try allocator.alloc(u8, table_size);173 const long_file_names = try allocator.alloc(u8, table_size);
...@@ -194,7 +178,8 @@ fn parseNameTable(archive: *Archive, allocator: Allocator, reader: anytype) !voi...@@ -194,7 +178,8 @@ fn parseNameTable(archive: *Archive, allocator: Allocator, reader: anytype) !voi
194178
195/// From a given file offset, starts reading for a file header.179/// From a given file offset, starts reading for a file header.
196/// When found, parses the object file into an `Object` and returns it.180/// When found, parses the object file into an `Object` and returns it.
197pub fn parseObject(archive: Archive, allocator: Allocator, file_offset: u32) !Object {181pub fn parseObject(archive: Archive, wasm_file: *const Wasm, file_offset: u32) !Object {
182 const gpa = wasm_file.base.comp.gpa;
198 try archive.file.seekTo(file_offset);183 try archive.file.seekTo(file_offset);
199 const reader = archive.file.reader();184 const reader = archive.file.reader();
200 const header = try reader.readStruct(ar_hdr);185 const header = try reader.readStruct(ar_hdr);
...@@ -202,22 +187,33 @@ pub fn parseObject(archive: Archive, allocator: Allocator, file_offset: u32) !Ob...@@ -202,22 +187,33 @@ pub fn parseObject(archive: Archive, allocator: Allocator, file_offset: u32) !Ob
202 try archive.file.seekTo(0);187 try archive.file.seekTo(0);
203188
204 if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) {189 if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) {
205 log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, header.ar_fmag });190 return error.InvalidHeaderDelimiter;
206 return error.MalformedArchive;
207 }191 }
208192
209 const object_name = try archive.parseName(header);193 const object_name = try archive.parseName(header);
210 const name = name: {194 const name = name: {
211 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;195 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
212 const path = try std.os.realpath(archive.name, &buffer);196 const path = try std.os.realpath(archive.name, &buffer);
213 break :name try std.fmt.allocPrint(allocator, "{s}({s})", .{ path, object_name });197 break :name try std.fmt.allocPrint(gpa, "{s}({s})", .{ path, object_name });
214 };198 };
215 defer allocator.free(name);199 defer gpa.free(name);
216200
217 const object_file = try std.fs.cwd().openFile(archive.name, .{});201 const object_file = try std.fs.cwd().openFile(archive.name, .{});
218 errdefer object_file.close();202 errdefer object_file.close();
219203
220 const object_file_size = try header.size();204 const object_file_size = try header.size();
221 try object_file.seekTo(current_offset);205 try object_file.seekTo(current_offset);
222 return Object.create(allocator, object_file, name, object_file_size);206 return Object.create(wasm_file, object_file, name, object_file_size);
223}207}
208
209const std = @import("std");
210const assert = std.debug.assert;
211const fs = std.fs;
212const log = std.log.scoped(.archive);
213const mem = std.mem;
214
215const Allocator = mem.Allocator;
216const Object = @import("Object.zig");
217const Wasm = @import("../Wasm.zig");
218
219const Archive = @This();
src/link/Wasm/Object.zig+26-13
...@@ -127,7 +127,8 @@ pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadErro...@@ -127,7 +127,8 @@ pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadErro
127/// This also parses and verifies the object file.127/// This also parses and verifies the object file.
128/// When a max size is given, will only parse up to the given size,128/// When a max size is given, will only parse up to the given size,
129/// else will read until the end of the file.129/// else will read until the end of the file.
130pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_size: ?usize) InitError!Object {130pub fn create(wasm_file: *const Wasm, file: std.fs.File, name: []const u8, maybe_max_size: ?usize) InitError!Object {
131 const gpa = wasm_file.base.comp.gpa;
131 var object: Object = .{132 var object: Object = .{
132 .file = file,133 .file = file,
133 .path = try gpa.dupe(u8, name),134 .path = try gpa.dupe(u8, name),
...@@ -151,7 +152,7 @@ pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_siz...@@ -151,7 +152,7 @@ pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_siz
151 }152 }
152 var fbs = std.io.fixedBufferStream(file_contents);153 var fbs = std.io.fixedBufferStream(file_contents);
153154
154 try object.parse(gpa, fbs.reader(), &is_object_file);155 try object.parse(gpa, wasm_file, fbs.reader(), &is_object_file);
155 errdefer object.deinit(gpa);156 errdefer object.deinit(gpa);
156 if (!is_object_file) return error.NotObjectFile;157 if (!is_object_file) return error.NotObjectFile;
157158
...@@ -224,7 +225,7 @@ pub fn findImport(object: *const Object, sym: Symbol) types.Import {...@@ -224,7 +225,7 @@ pub fn findImport(object: *const Object, sym: Symbol) types.Import {
224/// we initialize a new table symbol that corresponds to that import and return that symbol.225/// we initialize a new table symbol that corresponds to that import and return that symbol.
225///226///
226/// When the object file is *NOT* MVP, we return `null`.227/// When the object file is *NOT* MVP, we return `null`.
227fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {228fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?Symbol {
228 var table_count: usize = 0;229 var table_count: usize = 0;
229 for (object.symtable) |sym| {230 for (object.symtable) |sym| {
230 if (sym.tag == .table) table_count += 1;231 if (sym.tag == .table) table_count += 1;
...@@ -234,21 +235,27 @@ fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {...@@ -234,21 +235,27 @@ fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {
234 if (object.imported_tables_count == table_count) return null;235 if (object.imported_tables_count == table_count) return null;
235236
236 if (table_count != 0) {237 if (table_count != 0) {
237 log.err("Expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{238 var err = try wasm_file.addErrorWithNotes(1);
239 try err.addMsg(wasm_file, "Expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
238 object.imported_tables_count,240 object.imported_tables_count,
239 table_count,241 table_count,
240 });242 });
243 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
241 return error.MissingTableSymbols;244 return error.MissingTableSymbols;
242 }245 }
243246
244 // MVP object files cannot have any table definitions, only imports (for the indirect function table).247 // MVP object files cannot have any table definitions, only imports (for the indirect function table).
245 if (object.tables.len > 0) {248 if (object.tables.len > 0) {
246 log.err("Unexpected table definition without representing table symbols.", .{});249 var err = try wasm_file.addErrorWithNotes(1);
250 try err.addMsg(wasm_file, "Unexpected table definition without representing table symbols.", .{});
251 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
247 return error.UnexpectedTable;252 return error.UnexpectedTable;
248 }253 }
249254
250 if (object.imported_tables_count != 1) {255 if (object.imported_tables_count != 1) {
251 log.err("Found more than one table import, but no representing table symbols", .{});256 var err = try wasm_file.addErrorWithNotes(1);
257 try err.addMsg(wasm_file, "Found more than one table import, but no representing table symbols", .{});
258 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
252 return error.MissingTableSymbols;259 return error.MissingTableSymbols;
253 }260 }
254261
...@@ -259,7 +266,9 @@ fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {...@@ -259,7 +266,9 @@ fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {
259 } else unreachable;266 } else unreachable;
260267
261 if (!std.mem.eql(u8, object.string_table.get(table_import.name), "__indirect_function_table")) {268 if (!std.mem.eql(u8, object.string_table.get(table_import.name), "__indirect_function_table")) {
262 log.err("Non-indirect function table import '{s}' is missing a corresponding symbol", .{object.string_table.get(table_import.name)});269 var err = try wasm_file.addErrorWithNotes(1);
270 try err.addMsg(wasm_file, "Non-indirect function table import '{s}' is missing a corresponding symbol", .{object.string_table.get(table_import.name)});
271 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
263 return error.MissingTableSymbols;272 return error.MissingTableSymbols;
264 }273 }
265274
...@@ -312,8 +321,8 @@ pub const ParseError = error{...@@ -312,8 +321,8 @@ pub const ParseError = error{
312 UnknownFeature,321 UnknownFeature,
313};322};
314323
315fn parse(object: *Object, gpa: Allocator, reader: anytype, is_object_file: *bool) Parser(@TypeOf(reader)).Error!void {324fn parse(object: *Object, gpa: Allocator, wasm_file: *const Wasm, reader: anytype, is_object_file: *bool) Parser(@TypeOf(reader)).Error!void {
316 var parser = Parser(@TypeOf(reader)).init(object, reader);325 var parser = Parser(@TypeOf(reader)).init(object, wasm_file, reader);
317 return parser.parseObject(gpa, is_object_file);326 return parser.parseObject(gpa, is_object_file);
318}327}
319328
...@@ -325,9 +334,11 @@ fn Parser(comptime ReaderType: type) type {...@@ -325,9 +334,11 @@ fn Parser(comptime ReaderType: type) type {
325 reader: std.io.CountingReader(ReaderType),334 reader: std.io.CountingReader(ReaderType),
326 /// Object file we're building335 /// Object file we're building
327 object: *Object,336 object: *Object,
337 /// Read-only reference to the WebAssembly linker
338 wasm_file: *const Wasm,
328339
329 fn init(object: *Object, reader: ReaderType) ObjectParser {340 fn init(object: *Object, wasm_file: *const Wasm, reader: ReaderType) ObjectParser {
330 return .{ .object = object, .reader = std.io.countingReader(reader) };341 return .{ .object = object, .wasm_file = wasm_file, .reader = std.io.countingReader(reader) };
331 }342 }
332343
333 /// Verifies that the first 4 bytes contains \0Asm344 /// Verifies that the first 4 bytes contains \0Asm
...@@ -585,7 +596,9 @@ fn Parser(comptime ReaderType: type) type {...@@ -585,7 +596,9 @@ fn Parser(comptime ReaderType: type) type {
585 try reader.readNoEof(name);596 try reader.readNoEof(name);
586597
587 const tag = types.known_features.get(name) orelse {598 const tag = types.known_features.get(name) orelse {
588 log.err("Object file contains unknown feature: {s}", .{name});599 var err = try parser.wasm_file.addErrorWithNotes(1);
600 try err.addMsg(parser.wasm_file, "Object file contains unknown feature: {s}", .{name});
601 try err.addNote(parser.wasm_file, "defined in '{s}'", .{parser.object.path});
589 return error.UnknownFeature;602 return error.UnknownFeature;
590 };603 };
591 feature.* = .{604 feature.* = .{
...@@ -754,7 +767,7 @@ fn Parser(comptime ReaderType: type) type {...@@ -754,7 +767,7 @@ fn Parser(comptime ReaderType: type) type {
754767
755 // we found all symbols, check for indirect function table768 // we found all symbols, check for indirect function table
756 // in case of an MVP object file769 // in case of an MVP object file
757 if (try parser.object.checkLegacyIndirectFunctionTable()) |symbol| {770 if (try parser.object.checkLegacyIndirectFunctionTable(parser.wasm_file)) |symbol| {
758 try symbols.append(symbol);771 try symbols.append(symbol);
759 log.debug("Found legacy indirect function table. Created symbol", .{});772 log.debug("Found legacy indirect function table. Created symbol", .{});
760 }773 }
src/link/Wasm/ZigObject.zig+1-1
...@@ -1213,6 +1213,6 @@ const StringTable = @import("../StringTable.zig");...@@ -1213,6 +1213,6 @@ const StringTable = @import("../StringTable.zig");
1213const Symbol = @import("Symbol.zig");1213const Symbol = @import("Symbol.zig");
1214const Type = @import("../../type.zig").Type;1214const Type = @import("../../type.zig").Type;
1215const TypedValue = @import("../../TypedValue.zig");1215const TypedValue = @import("../../TypedValue.zig");
1216const Value = @import("../../value.zig").Value;1216const Value = @import("../../Value.zig");
1217const Wasm = @import("../Wasm.zig");1217const Wasm = @import("../Wasm.zig");
1218const ZigObject = @This();1218const ZigObject = @This();