Commit 5ba5a2c133
Changed files (4)
src
src/link/Wasm/Archive.zig
@@ -1,14 +1,3 @@
-const Archive = @This();
-
-const std = @import("std");
-const assert = std.debug.assert;
-const fs = std.fs;
-const log = std.log.scoped(.archive);
-const mem = std.mem;
-
-const Allocator = mem.Allocator;
-const Object = @import("Object.zig");
-
file: fs.File,
name: []const u8,
@@ -151,10 +140,7 @@ fn parseTableOfContents(archive: *Archive, allocator: Allocator, reader: anytype
const sym_tab = try allocator.alloc(u8, sym_tab_size - 4 - (4 * num_symbols));
defer allocator.free(sym_tab);
- reader.readNoEof(sym_tab) catch {
- log.err("incomplete symbol table: expected symbol table of length 0x{x}", .{sym_tab.len});
- return error.MalformedArchive;
- };
+ reader.readNoEof(sym_tab) catch return error.IncompleteSymbolTable;
var i: usize = 0;
var pos: usize = 0;
@@ -178,12 +164,10 @@ fn parseTableOfContents(archive: *Archive, allocator: Allocator, reader: anytype
fn parseNameTable(archive: *Archive, allocator: Allocator, reader: anytype) !void {
const header: ar_hdr = try reader.readStruct(ar_hdr);
if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) {
- log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, header.ar_fmag });
- return error.MalformedArchive;
+ return error.InvalidHeaderDelimiter;
}
if (!mem.eql(u8, header.ar_name[0..2], "//")) {
- log.err("invalid archive. Long name table missing", .{});
- return error.MalformedArchive;
+ return error.MissingTableName;
}
const table_size = try header.size();
const long_file_names = try allocator.alloc(u8, table_size);
@@ -194,7 +178,8 @@ fn parseNameTable(archive: *Archive, allocator: Allocator, reader: anytype) !voi
/// From a given file offset, starts reading for a file header.
/// When found, parses the object file into an `Object` and returns it.
-pub fn parseObject(archive: Archive, allocator: Allocator, file_offset: u32) !Object {
+pub fn parseObject(archive: Archive, wasm_file: *const Wasm, file_offset: u32) !Object {
+ const gpa = wasm_file.base.comp.gpa;
try archive.file.seekTo(file_offset);
const reader = archive.file.reader();
const header = try reader.readStruct(ar_hdr);
@@ -202,22 +187,33 @@ pub fn parseObject(archive: Archive, allocator: Allocator, file_offset: u32) !Ob
try archive.file.seekTo(0);
if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) {
- log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, header.ar_fmag });
- return error.MalformedArchive;
+ return error.InvalidHeaderDelimiter;
}
const object_name = try archive.parseName(header);
const name = name: {
var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
const path = try std.os.realpath(archive.name, &buffer);
- break :name try std.fmt.allocPrint(allocator, "{s}({s})", .{ path, object_name });
+ break :name try std.fmt.allocPrint(gpa, "{s}({s})", .{ path, object_name });
};
- defer allocator.free(name);
+ defer gpa.free(name);
const object_file = try std.fs.cwd().openFile(archive.name, .{});
errdefer object_file.close();
const object_file_size = try header.size();
try object_file.seekTo(current_offset);
- return Object.create(allocator, object_file, name, object_file_size);
+ return Object.create(wasm_file, object_file, name, object_file_size);
}
+
+const std = @import("std");
+const assert = std.debug.assert;
+const fs = std.fs;
+const log = std.log.scoped(.archive);
+const mem = std.mem;
+
+const Allocator = mem.Allocator;
+const Object = @import("Object.zig");
+const Wasm = @import("../Wasm.zig");
+
+const Archive = @This();
src/link/Wasm/Object.zig
@@ -127,7 +127,8 @@ pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadErro
/// This also parses and verifies the object file.
/// When a max size is given, will only parse up to the given size,
/// else will read until the end of the file.
-pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8, maybe_max_size: ?usize) InitError!Object {
+pub fn create(wasm_file: *const Wasm, file: std.fs.File, name: []const u8, maybe_max_size: ?usize) InitError!Object {
+ const gpa = wasm_file.base.comp.gpa;
var object: Object = .{
.file = file,
.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
}
var fbs = std.io.fixedBufferStream(file_contents);
- try object.parse(gpa, fbs.reader(), &is_object_file);
+ try object.parse(gpa, wasm_file, fbs.reader(), &is_object_file);
errdefer object.deinit(gpa);
if (!is_object_file) return error.NotObjectFile;
@@ -224,7 +225,7 @@ pub fn findImport(object: *const Object, sym: Symbol) types.Import {
/// we initialize a new table symbol that corresponds to that import and return that symbol.
///
/// When the object file is *NOT* MVP, we return `null`.
-fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {
+fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?Symbol {
var table_count: usize = 0;
for (object.symtable) |sym| {
if (sym.tag == .table) table_count += 1;
@@ -234,21 +235,27 @@ fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {
if (object.imported_tables_count == table_count) return null;
if (table_count != 0) {
- log.err("Expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
+ var err = try wasm_file.addErrorWithNotes(1);
+ try err.addMsg(wasm_file, "Expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
object.imported_tables_count,
table_count,
});
+ try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
return error.MissingTableSymbols;
}
// MVP object files cannot have any table definitions, only imports (for the indirect function table).
if (object.tables.len > 0) {
- log.err("Unexpected table definition without representing table symbols.", .{});
+ var err = try wasm_file.addErrorWithNotes(1);
+ try err.addMsg(wasm_file, "Unexpected table definition without representing table symbols.", .{});
+ try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
return error.UnexpectedTable;
}
if (object.imported_tables_count != 1) {
- log.err("Found more than one table import, but no representing table symbols", .{});
+ var err = try wasm_file.addErrorWithNotes(1);
+ try err.addMsg(wasm_file, "Found more than one table import, but no representing table symbols", .{});
+ try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
return error.MissingTableSymbols;
}
@@ -259,7 +266,9 @@ fn checkLegacyIndirectFunctionTable(object: *Object) !?Symbol {
} else unreachable;
if (!std.mem.eql(u8, object.string_table.get(table_import.name), "__indirect_function_table")) {
- log.err("Non-indirect function table import '{s}' is missing a corresponding symbol", .{object.string_table.get(table_import.name)});
+ var err = try wasm_file.addErrorWithNotes(1);
+ try err.addMsg(wasm_file, "Non-indirect function table import '{s}' is missing a corresponding symbol", .{object.string_table.get(table_import.name)});
+ try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
return error.MissingTableSymbols;
}
@@ -312,8 +321,8 @@ pub const ParseError = error{
UnknownFeature,
};
-fn parse(object: *Object, gpa: Allocator, reader: anytype, is_object_file: *bool) Parser(@TypeOf(reader)).Error!void {
- var parser = Parser(@TypeOf(reader)).init(object, reader);
+fn parse(object: *Object, gpa: Allocator, wasm_file: *const Wasm, reader: anytype, is_object_file: *bool) Parser(@TypeOf(reader)).Error!void {
+ var parser = Parser(@TypeOf(reader)).init(object, wasm_file, reader);
return parser.parseObject(gpa, is_object_file);
}
@@ -325,9 +334,11 @@ fn Parser(comptime ReaderType: type) type {
reader: std.io.CountingReader(ReaderType),
/// Object file we're building
object: *Object,
+ /// Read-only reference to the WebAssembly linker
+ wasm_file: *const Wasm,
- fn init(object: *Object, reader: ReaderType) ObjectParser {
- return .{ .object = object, .reader = std.io.countingReader(reader) };
+ fn init(object: *Object, wasm_file: *const Wasm, reader: ReaderType) ObjectParser {
+ return .{ .object = object, .wasm_file = wasm_file, .reader = std.io.countingReader(reader) };
}
/// Verifies that the first 4 bytes contains \0Asm
@@ -585,7 +596,9 @@ fn Parser(comptime ReaderType: type) type {
try reader.readNoEof(name);
const tag = types.known_features.get(name) orelse {
- log.err("Object file contains unknown feature: {s}", .{name});
+ var err = try parser.wasm_file.addErrorWithNotes(1);
+ try err.addMsg(parser.wasm_file, "Object file contains unknown feature: {s}", .{name});
+ try err.addNote(parser.wasm_file, "defined in '{s}'", .{parser.object.path});
return error.UnknownFeature;
};
feature.* = .{
@@ -754,7 +767,7 @@ fn Parser(comptime ReaderType: type) type {
// we found all symbols, check for indirect function table
// in case of an MVP object file
- if (try parser.object.checkLegacyIndirectFunctionTable()) |symbol| {
+ if (try parser.object.checkLegacyIndirectFunctionTable(parser.wasm_file)) |symbol| {
try symbols.append(symbol);
log.debug("Found legacy indirect function table. Created symbol", .{});
}
src/link/Wasm/ZigObject.zig
@@ -1213,6 +1213,6 @@ const StringTable = @import("../StringTable.zig");
const Symbol = @import("Symbol.zig");
const Type = @import("../../type.zig").Type;
const TypedValue = @import("../../TypedValue.zig");
-const Value = @import("../../value.zig").Value;
+const Value = @import("../../Value.zig");
const Wasm = @import("../Wasm.zig");
const ZigObject = @This();
src/link/Wasm.zig
@@ -655,9 +655,14 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {
errdefer obj_file.close();
const gpa = wasm.base.comp.gpa;
- var object = Object.create(gpa, obj_file, path, null) catch |err| switch (err) {
+ var object = Object.create(wasm, obj_file, path, null) catch |err| switch (err) {
error.InvalidMagicByte, error.NotObjectFile => return false,
- else => |e| return e,
+ else => |e| {
+ var err_note = try wasm.addErrorWithNotes(1);
+ try err_note.addMsg(wasm, "Failed parsing object file: {s}", .{@errorName(e)});
+ try err_note.addNote(wasm, "while parsing '{s}'", .{path});
+ return error.FlushFailure;
+ },
};
errdefer object.deinit(gpa);
object.index = @enumFromInt(wasm.files.len);
@@ -708,7 +713,12 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
archive.deinit(gpa);
return false;
},
- else => |e| return e,
+ else => |e| {
+ var err_note = try wasm.addErrorWithNotes(1);
+ try err_note.addMsg(wasm, "Failed parsing archive: {s}", .{@errorName(e)});
+ try err_note.addNote(wasm, "while parsing archive {s}", .{path});
+ return error.FlushFailure;
+ },
};
if (!force_load) {
@@ -730,7 +740,12 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
}
for (offsets.keys()) |file_offset| {
- var object = try archive.parseObject(gpa, file_offset);
+ var object = archive.parseObject(wasm, file_offset) catch |e| {
+ var err_note = try wasm.addErrorWithNotes(1);
+ try err_note.addMsg(wasm, "Failed parsing object: {s}", .{@errorName(e)});
+ try err_note.addNote(wasm, "while parsing object in archive {s}", .{path});
+ return error.FlushFailure;
+ };
object.index = @enumFromInt(wasm.files.len);
try wasm.files.append(gpa, .{ .object = object });
try wasm.objects.append(gpa, object.index);
@@ -764,9 +779,9 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
if (symbol.isLocal()) {
if (symbol.isUndefined()) {
- log.err("Local symbols are not allowed to reference imports", .{});
- log.err(" symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() });
- return error.UndefinedLocal;
+ var err = try wasm.addErrorWithNotes(1);
+ try err.addMsg(wasm, "Local symbols are not allowed to reference imports", .{});
+ try err.addNote(wasm, "symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() });
}
try wasm.resolved_symbols.putNoClobber(gpa, location, {});
continue;
@@ -801,10 +816,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
break :outer; // existing is weak, while new one isn't. Replace it.
}
// both are defined and weak, we have a symbol collision.
- log.err("symbol '{s}' defined multiple times", .{sym_name});
- log.err(" first definition in '{s}'", .{existing_file_path});
- log.err(" next definition in '{s}'", .{obj_file.path()});
- return error.SymbolCollision;
+ var err = try wasm.addErrorWithNotes(2);
+ try err.addMsg(wasm, "symbol '{s}' defined multiple times", .{sym_name});
+ try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
+ try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
}
try wasm.discarded.put(gpa, location, existing_loc);
@@ -812,10 +827,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
}
if (symbol.tag != existing_sym.tag) {
- log.err("symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) });
- log.err(" first definition in '{s}'", .{existing_file_path});
- log.err(" next definition in '{s}'", .{obj_file.path()});
- return error.SymbolMismatchingType;
+ var err = try wasm.addErrorWithNotes(2);
+ try err.addMsg(wasm, "symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) });
+ try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
+ try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
}
if (existing_sym.isUndefined() and symbol.isUndefined()) {
@@ -832,14 +847,14 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
const imp = obj_file.import(sym_index);
const module_name = obj_file.string(imp.module_name);
if (!mem.eql(u8, existing_name, module_name)) {
- log.err("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{
+ var err = try wasm.addErrorWithNotes(2);
+ try err.addMsg(wasm, "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}'", .{obj_file.path()});
- return error.ModuleNameMismatch;
+ try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
+ try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
}
}
@@ -852,10 +867,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
const existing_ty = wasm.getGlobalType(existing_loc);
const new_ty = wasm.getGlobalType(location);
if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) {
- log.err("symbol '{s}' mismatching global types", .{sym_name});
- log.err(" first definition in '{s}'", .{existing_file_path});
- log.err(" next definition in '{s}'", .{obj_file.path()});
- return error.GlobalTypeMismatch;
+ var err = try wasm.addErrorWithNotes(2);
+ try err.addMsg(wasm, "symbol '{s}' mismatching global types", .{sym_name});
+ try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
+ try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
}
}
@@ -863,11 +878,11 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
const existing_ty = wasm.getFunctionSignature(existing_loc);
const new_ty = wasm.getFunctionSignature(location);
if (!existing_ty.eql(new_ty)) {
- log.err("symbol '{s}' mismatching function signatures.", .{sym_name});
- log.err(" expected signature {}, but found signature {}", .{ existing_ty, new_ty });
- log.err(" first definition in '{s}'", .{existing_file_path});
- log.err(" next definition in '{s}'", .{obj_file.path()});
- return error.FunctionSignatureMismatch;
+ var err = try wasm.addErrorWithNotes(3);
+ try err.addMsg(wasm, "symbol '{s}' mismatching function signatures.", .{sym_name});
+ try err.addNote(wasm, "expected signature {}, but found signature {}", .{ existing_ty, new_ty });
+ try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
+ try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
}
}
@@ -914,7 +929,12 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
// Symbol is found in unparsed object file within current archive.
// Parse object and and resolve symbols again before we check remaining
// undefined symbols.
- var object = try archive.parseObject(gpa, offset.items[0]);
+ var object = archive.parseObject(wasm, offset.items[0]) catch |e| {
+ var err_note = try wasm.addErrorWithNotes(1);
+ try err_note.addMsg(wasm, "Failed parsing object: {s}", .{@errorName(e)});
+ try err_note.addNote(wasm, "while parsing object in archive {s}", .{archive.name});
+ return error.FlushFailure;
+ };
object.index = @enumFromInt(wasm.files.len);
try wasm.files.append(gpa, .{ .object = object });
try wasm.objects.append(gpa, object.index);
@@ -1214,20 +1234,21 @@ fn validateFeatures(
allowed[used_index] = is_enabled;
emit_features_count.* += @intFromBool(is_enabled);
} else if (is_enabled and !allowed[used_index]) {
- log.err("feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))});
- log.err(" defined in '{s}'", .{wasm.files.items(.data)[used_set >> 1].object.path});
+ var err = try wasm.addErrorWithNotes(1);
+ try err.addMsg(wasm, "feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))});
+ try err.addNote(wasm, "defined in '{s}'", .{wasm.files.items(.data)[used_set >> 1].object.path});
valid_feature_set = false;
}
}
if (!valid_feature_set) {
- return error.InvalidFeatureSet;
+ return error.FlushFailure;
}
if (shared_memory) {
const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)];
if (@as(u1, @truncate(disallowed_feature)) != 0) {
- log.err(
+ try wasm.addErrorWithoutNotes(
"shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled",
.{wasm.files.items(.data)[disallowed_feature >> 1].object.path},
);
@@ -1236,7 +1257,7 @@ fn validateFeatures(
for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
if (!allowed[@intFromEnum(feature)]) {
- log.err("feature '{}' is not used but is required for shared-memory", .{feature});
+ try wasm.addErrorWithoutNotes("feature '{}' is not used but is required for shared-memory", .{feature});
}
}
}
@@ -1244,7 +1265,7 @@ fn validateFeatures(
if (has_tls) {
for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
if (!allowed[@intFromEnum(feature)]) {
- log.err("feature '{}' is not used but is required for thread-local storage", .{feature});
+ try wasm.addErrorWithoutNotes("feature '{}' is not used but is required for thread-local storage", .{feature});
}
}
}
@@ -1257,9 +1278,10 @@ fn validateFeatures(
// from here a feature is always used
const disallowed_feature = disallowed[@intFromEnum(feature.tag)];
if (@as(u1, @truncate(disallowed_feature)) != 0) {
- log.err("feature '{}' is disallowed, but used by linked object", .{feature.tag});
- log.err(" disallowed by '{s}'", .{wasm.files.items(.data)[disallowed_feature >> 1].object.path});
- log.err(" used in '{s}'", .{object.path});
+ var err = try wasm.addErrorWithNotes(2);
+ try err.addMsg(wasm, "feature '{}' is disallowed, but used by linked object", .{feature.tag});
+ try err.addNote(wasm, "disallowed by '{s}'", .{wasm.files.items(.data)[disallowed_feature >> 1].object.path});
+ try err.addNote(wasm, "used in '{s}'", .{object.path});
valid_feature_set = false;
}
@@ -1270,16 +1292,17 @@ fn validateFeatures(
for (required, 0..) |required_feature, feature_index| {
const is_required = @as(u1, @truncate(required_feature)) != 0;
if (is_required and !object_used_features[feature_index]) {
- log.err("feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))});
- log.err(" required by '{s}'", .{wasm.files.items(.data)[required_feature >> 1].object.path});
- log.err(" missing in '{s}'", .{object.path});
+ var err = try wasm.addErrorWithNotes(2);
+ try err.addMsg(wasm, "feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))});
+ try err.addNote(wasm, "required by '{s}'", .{wasm.files.items(.data)[required_feature >> 1].object.path});
+ try err.addNote(wasm, "missing in '{s}'", .{object.path});
valid_feature_set = false;
}
}
}
if (!valid_feature_set) {
- return error.InvalidFeatureSet;
+ return error.FlushFailure;
}
to_emit.* = allowed;
@@ -1350,12 +1373,13 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void {
else
wasm.name;
const symbol_name = undef.getName(wasm);
- log.err("could not resolve undefined symbol '{s}'", .{symbol_name});
- log.err(" defined in '{s}'", .{file_name});
+ var err = try wasm.addErrorWithNotes(1);
+ try err.addMsg(wasm, "could not resolve undefined symbol '{s}'", .{symbol_name});
+ try err.addNote(wasm, "defined in '{s}'", .{file_name});
}
}
if (found_undefined_symbols) {
- return error.UndefinedSymbol;
+ return error.FlushFailure;
}
}
@@ -1728,8 +1752,7 @@ fn setupInitFunctions(wasm: *Wasm) !void {
break :ty object.func_types[func.type_index];
};
if (ty.params.len != 0) {
- log.err("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)});
- return error.InvalidInitFunc;
+ try wasm.addErrorWithoutNotes("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)});
}
log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)});
wasm.init_funcs.appendAssumeCapacity(.{
@@ -2108,7 +2131,7 @@ fn setupExports(wasm: *Wasm) !void {
for (force_exp_names) |exp_name| {
const loc = wasm.findGlobalSymbol(exp_name) orelse {
- log.err("could not export '{s}', symbol not found", .{exp_name});
+ try wasm.addErrorWithoutNotes("could not export '{s}', symbol not found", .{exp_name});
failed_exports = true;
continue;
};
@@ -2118,7 +2141,7 @@ fn setupExports(wasm: *Wasm) !void {
}
if (failed_exports) {
- return error.MissingSymbol;
+ return error.FlushFailure;
}
}
@@ -2164,14 +2187,14 @@ fn setupStart(wasm: *Wasm) !void {
const entry_name = wasm.entry_name orelse return;
const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {
- log.err("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});
- return error.MissingSymbol;
+ try wasm.addErrorWithoutNotes("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});
+ return error.FlushFailure;
};
const symbol = symbol_loc.getSymbol(wasm);
if (symbol.tag != .function) {
- log.err("Entry symbol '{s}' is not a function", .{entry_name});
- return error.InvalidEntryKind;
+ try wasm.addErrorWithoutNotes("Entry symbol '{s}' is not a function", .{entry_name});
+ return error.FlushFailure;
}
// Ensure the symbol is exported so host environment can access it
@@ -2274,16 +2297,13 @@ fn setupMemory(wasm: *Wasm) !void {
if (wasm.initial_memory) |initial_memory| {
if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) {
- log.err("Initial memory must be {d}-byte aligned", .{page_size});
- return error.MissAlignment;
+ try wasm.addErrorWithoutNotes("Initial memory must be {d}-byte aligned", .{page_size});
}
if (memory_ptr > initial_memory) {
- log.err("Initial memory too small, must be at least {d} bytes", .{memory_ptr});
- return error.MemoryTooSmall;
+ try wasm.addErrorWithoutNotes("Initial memory too small, must be at least {d} bytes", .{memory_ptr});
}
if (initial_memory > max_memory_allowed) {
- log.err("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});
- return error.MemoryTooBig;
+ try wasm.addErrorWithoutNotes("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});
}
memory_ptr = initial_memory;
}
@@ -2300,16 +2320,13 @@ fn setupMemory(wasm: *Wasm) !void {
if (wasm.max_memory) |max_memory| {
if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) {
- log.err("Maximum memory must be {d}-byte aligned", .{page_size});
- return error.MissAlignment;
+ try wasm.addErrorWithoutNotes("Maximum memory must be {d}-byte aligned", .{page_size});
}
if (memory_ptr > max_memory) {
- log.err("Maxmimum memory too small, must be at least {d} bytes", .{memory_ptr});
- return error.MemoryTooSmall;
+ try wasm.addErrorWithoutNotes("Maxmimum memory too small, must be at least {d} bytes", .{memory_ptr});
}
if (max_memory > max_memory_allowed) {
- log.err("Maximum memory exceeds maxmium amount {d}", .{max_memory_allowed});
- return error.MemoryTooBig;
+ try wasm.addErrorWithoutNotes("Maximum memory exceeds maxmium amount {d}", .{max_memory_allowed});
}
wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size));
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
break :blk index;
};
} else {
- log.err("found unknown section '{s}'", .{section_name});
+ var err = try wasm.addErrorWithNotes(1);
+ try err.addMsg(wasm, "found unknown section '{s}'", .{section_name});
+ try err.addNote(wasm, "defined in '{s}'", .{obj_file.path()});
return error.UnexpectedValue;
}
},
@@ -2529,18 +2548,22 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node)
if (wasm.zig_object_index != .null) {
try wasm.resolveSymbolsInObject(wasm.zig_object_index);
}
+ if (comp.link_errors.items.len > 0) return error.FlushFailure;
for (wasm.objects.items) |object_index| {
try wasm.resolveSymbolsInObject(object_index);
}
+ if (comp.link_errors.items.len > 0) return error.FlushFailure;
var emit_features_count: u32 = 0;
var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;
try wasm.validateFeatures(&enabled_features, &emit_features_count);
try wasm.resolveSymbolsInArchives();
+ if (comp.link_errors.items.len > 0) return error.FlushFailure;
try wasm.resolveLazySymbols();
try wasm.checkUndefinedSymbols();
try wasm.setupInitFunctions();
+ if (comp.link_errors.items.len > 0) return error.FlushFailure;
try wasm.setupStart();
try wasm.markReferences();
@@ -2549,6 +2572,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node)
try wasm.mergeTypes();
try wasm.allocateAtoms();
try wasm.setupMemory();
+ if (comp.link_errors.items.len > 0) return error.FlushFailure;
wasm.allocateVirtualAddresses();
wasm.mapFunctionTable();
try wasm.initializeCallCtorsFunction();
@@ -2558,6 +2582,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node)
try wasm.setupStartSection();
try wasm.setupExports();
try wasm.writeToFile(enabled_features, emit_features_count, arena);
+ if (comp.link_errors.items.len > 0) return error.FlushFailure;
}
/// Writes the WebAssembly in-memory module to the file
@@ -2955,7 +2980,7 @@ fn writeToFile(
}) catch unreachable;
try emitBuildIdSection(&binary_bytes, str);
},
- else => |mode| log.err("build-id '{s}' is not supported for WASM", .{@tagName(mode)}),
+ else => |mode| try wasm.addErrorWithoutNotes("build-id '{s}' is not supported for WebAssembly", .{@tagName(mode)}),
}
var debug_bytes = std.ArrayList(u8).init(gpa);
@@ -4043,3 +4068,57 @@ fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8
.command => "_start",
};
}
+
+const ErrorWithNotes = struct {
+ /// Allocated index in comp.link_errors array.
+ index: usize,
+
+ /// Next available note slot.
+ note_slot: usize = 0,
+
+ pub fn addMsg(
+ err: ErrorWithNotes,
+ wasm_file: *const Wasm,
+ comptime format: []const u8,
+ args: anytype,
+ ) error{OutOfMemory}!void {
+ const comp = wasm_file.base.comp;
+ const gpa = comp.gpa;
+ const err_msg = &comp.link_errors.items[err.index];
+ err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
+ }
+
+ pub fn addNote(
+ err: *ErrorWithNotes,
+ wasm_file: *const Wasm,
+ comptime format: []const u8,
+ args: anytype,
+ ) error{OutOfMemory}!void {
+ const comp = wasm_file.base.comp;
+ const gpa = comp.gpa;
+ const err_msg = &comp.link_errors.items[err.index];
+ err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };
+ err.note_slot += 1;
+ }
+};
+
+pub fn addErrorWithNotes(wasm: *const Wasm, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
+ const comp = wasm.base.comp;
+ const gpa = comp.gpa;
+ try comp.link_errors.ensureUnusedCapacity(gpa, 1);
+ return wasm.addErrorWithNotesAssumeCapacity(note_count);
+}
+
+pub fn addErrorWithoutNotes(wasm: *const Wasm, comptime fmt: []const u8, args: anytype) !void {
+ const err = try wasm.addErrorWithNotes(0);
+ try err.addMsg(wasm, fmt, args);
+}
+
+fn addErrorWithNotesAssumeCapacity(wasm: *const Wasm, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
+ const comp = wasm.base.comp;
+ const gpa = comp.gpa;
+ const index = comp.link_errors.items.len;
+ const err = comp.link_errors.addOneAssumeCapacity();
+ err.* = .{ .msg = undefined, .notes = try gpa.alloc(link.File.ErrorMsg, note_count) };
+ return .{ .index = index };
+}