| ... | @@ -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(); |
| 656 | | 656 | |
| 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 | }; |
| 713 | | 723 | |
| 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 | } |
| 731 | | 741 | |
| 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 { |
| 764 | | 779 | |
| 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 | } |
| 809 | | 824 | |
| 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 | } |
| 813 | | 828 | |
| 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 | } |
| 820 | | 835 | |
| 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 | } |
| 845 | | 860 | |
| ... | @@ -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 | } |
| 861 | | 876 | |
| ... | @@ -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 | } |
| 873 | | 888 | |
| ... | @@ -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 remaining | 930 | // 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 | } |
| 1222 | | 1243 | |
| 1223 | if (!valid_feature_set) { | 1244 | if (!valid_feature_set) { |
| 1224 | return error.InvalidFeatureSet; | 1245 | return error.FlushFailure; |
| 1225 | } | 1246 | } |
| 1226 | | 1247 | |
| 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( |
| 1236 | | 1257 | |
| 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 used | 1278 | // 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 | } |
| 1265 | | 1287 | |
| ... | @@ -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 | } |
| 1280 | | 1303 | |
| 1281 | if (!valid_feature_set) { | 1304 | if (!valid_feature_set) { |
| 1282 | return error.InvalidFeatureSet; | 1305 | return error.FlushFailure; |
| 1283 | } | 1306 | } |
| 1284 | | 1307 | |
| 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 | else | 1373 | 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 | } |
| 1361 | | 1385 | |
| ... | @@ -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 { |
| 2108 | | 2131 | |
| 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 | } |
| 2119 | | 2142 | |
| 2120 | if (failed_exports) { | 2143 | if (failed_exports) { |
| 2121 | return error.MissingSymbol; | 2144 | return error.FlushFailure; |
| 2122 | } | 2145 | } |
| 2123 | } | 2146 | } |
| 2124 | | 2147 | |
| ... | @@ -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; |
| 2165 | | 2188 | |
| 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 | }; |
| 2170 | | 2193 | |
| 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 | } |
| 2176 | | 2199 | |
| 2177 | // Ensure the symbol is exported so host environment can access it | 2200 | // 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 { |
| 2274 | | 2297 | |
| 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 { |
| 2300 | | 2320 | |
| 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; |
| 2535 | | 2556 | |
| 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(); |
| 2542 | | 2564 | |
| 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(); |
| 2545 | | 2568 | |
| 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 | } |
| 2562 | | 2587 | |
| 2563 | /// Writes the WebAssembly in-memory module to the file | 2588 | /// 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 | } |
| 2960 | | 2985 | |
| 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 | |
| | 4072 | const 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 | |
| | 4105 | pub 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 | |
| | 4112 | pub 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 | |
| | 4117 | fn 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 | } |