authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-11 01:15:30-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-10-11 10:36:19-07:00
log13fb68c064900cee67583e603d8c2acf41770663
tree537e524b28610d80f2553d1b124f67b4656fb80a
parent5e53203e82bdeb0273160db156050164c5c69267

link: consolidate diagnostics

By organizing linker diagnostics into this struct, it becomes possible to share more code between linker backends, and more importantly it becomes possible to pass only the Diag struct to some functions, rather than passing the entire linker state object in. This makes data dependencies more obvious, making it easier to rearrange code and to multithread. Also fix MachO code abusing an atomic variable. Not only was it using the wrong atomic operation, it is unnecessary additional state since the state is already being protected by a mutex.

15 files changed, 513 insertions(+), 440 deletions(-)

src/Compilation.zig+11-81
......@@ -106,10 +106,7 @@ win32_resource_table: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMa
106106 pub fn deinit(_: @This(), _: Allocator) void {}
107107} = .{},
108108
109link_errors: std.ArrayListUnmanaged(link.File.ErrorMsg) = .empty,
110link_errors_mutex: std.Thread.Mutex = .{},
111link_error_flags: link.File.ErrorFlags = .{},
112lld_errors: std.ArrayListUnmanaged(LldError) = .empty,
109link_diags: link.Diags,
113110
114111work_queues: [
115112 len: {
......@@ -842,21 +839,6 @@ pub const MiscError = struct {
842839 }
843840};
844841
845pub const LldError = struct {
846 /// Allocated with gpa.
847 msg: []const u8,
848 context_lines: []const []const u8 = &.{},
849
850 pub fn deinit(self: *LldError, gpa: Allocator) void {
851 for (self.context_lines) |line| {
852 gpa.free(line);
853 }
854
855 gpa.free(self.context_lines);
856 gpa.free(self.msg);
857 }
858};
859
860842pub const EmitLoc = struct {
861843 /// If this is `null` it means the file will be output to the cache directory.
862844 /// When provided, both the open file handle and the path name must outlive the `Compilation`.
......@@ -1558,6 +1540,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
15581540 .global_cc_argv = options.global_cc_argv,
15591541 .file_system_inputs = options.file_system_inputs,
15601542 .parent_whole_cache = options.parent_whole_cache,
1543 .link_diags = .init(gpa),
15611544 };
15621545
15631546 // Prevent some footguns by making the "any" fields of config reflect
......@@ -1999,13 +1982,7 @@ pub fn destroy(comp: *Compilation) void {
19991982 }
20001983 comp.failed_win32_resources.deinit(gpa);
20011984
2002 for (comp.link_errors.items) |*item| item.deinit(gpa);
2003 comp.link_errors.deinit(gpa);
2004
2005 for (comp.lld_errors.items) |*lld_error| {
2006 lld_error.deinit(gpa);
2007 }
2008 comp.lld_errors.deinit(gpa);
1985 comp.link_diags.deinit();
20091986
20101987 comp.clearMiscFailures();
20111988
......@@ -2304,7 +2281,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
23042281
23052282 if (anyErrors(comp)) {
23062283 // Skip flushing and keep source files loaded for error reporting.
2307 comp.link_error_flags = .{};
2284 comp.link_diags.flags = .{};
23082285 return;
23092286 }
23102287
......@@ -2451,7 +2428,7 @@ fn flush(
24512428 if (comp.bin_file) |lf| {
24522429 // This is needed before reading the error flags.
24532430 lf.flush(arena, tid, prog_node) catch |err| switch (err) {
2454 error.FlushFailure, error.LinkFailure => {}, // error reported through link_error_flags
2431 error.FlushFailure, error.LinkFailure => {}, // error reported through link_diags.flags
24552432 error.LLDReportedFailure => {}, // error reported via lockAndParseLldStderr
24562433 else => |e| return e,
24572434 };
......@@ -3070,7 +3047,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
30703047 try bundle.addBundleAsRoots(error_bundle);
30713048 }
30723049
3073 for (comp.lld_errors.items) |lld_error| {
3050 for (comp.link_diags.lld.items) |lld_error| {
30743051 const notes_len = @as(u32, @intCast(lld_error.context_lines.len));
30753052
30763053 try bundle.addRootErrorMessage(.{
......@@ -3091,7 +3068,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
30913068 });
30923069 if (value.children) |b| try bundle.addBundleAsNotes(b);
30933070 }
3094 if (comp.alloc_failure_occurred) {
3071 if (comp.alloc_failure_occurred or comp.link_diags.flags.alloc_failure_occurred) {
30953072 try bundle.addRootErrorMessage(.{
30963073 .msg = try bundle.addString("memory allocation failure"),
30973074 });
......@@ -3220,14 +3197,14 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
32203197 }
32213198
32223199 if (bundle.root_list.items.len == 0) {
3223 if (comp.link_error_flags.no_entry_point_found) {
3200 if (comp.link_diags.flags.no_entry_point_found) {
32243201 try bundle.addRootErrorMessage(.{
32253202 .msg = try bundle.addString("no entry point found"),
32263203 });
32273204 }
32283205 }
32293206
3230 if (comp.link_error_flags.missing_libc) {
3207 if (comp.link_diags.flags.missing_libc) {
32313208 try bundle.addRootErrorMessage(.{
32323209 .msg = try bundle.addString("libc not available"),
32333210 .notes_len = 2,
......@@ -3241,7 +3218,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
32413218 }));
32423219 }
32433220
3244 for (comp.link_errors.items) |link_err| {
3221 for (comp.link_diags.msgs.items) |link_err| {
32453222 try bundle.addRootErrorMessage(.{
32463223 .msg = try bundle.addString(link_err.msg),
32473224 .notes_len = @intCast(link_err.notes.len),
......@@ -6161,6 +6138,7 @@ fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {
61616138}
61626139
61636140fn setAllocFailure(comp: *Compilation) void {
6141 @branchHint(.cold);
61646142 log.debug("memory allocation failure", .{});
61656143 comp.alloc_failure_occurred = true;
61666144}
......@@ -6195,54 +6173,6 @@ pub fn lockAndSetMiscFailure(
61956173 return setMiscFailure(comp, tag, format, args);
61966174}
61976175
6198fn parseLldStderr(comp: *Compilation, prefix: []const u8, stderr: []const u8) Allocator.Error!void {
6199 var context_lines = std.ArrayList([]const u8).init(comp.gpa);
6200 defer context_lines.deinit();
6201
6202 var current_err: ?*LldError = null;
6203 var lines = mem.splitSequence(u8, stderr, if (builtin.os.tag == .windows) "\r\n" else "\n");
6204 while (lines.next()) |line| {
6205 if (line.len > prefix.len + ":".len and
6206 mem.eql(u8, line[0..prefix.len], prefix) and line[prefix.len] == ':')
6207 {
6208 if (current_err) |err| {
6209 err.context_lines = try context_lines.toOwnedSlice();
6210 }
6211
6212 var split = mem.splitSequence(u8, line, "error: ");
6213 _ = split.first();
6214
6215 const duped_msg = try std.fmt.allocPrint(comp.gpa, "{s}: {s}", .{ prefix, split.rest() });
6216 errdefer comp.gpa.free(duped_msg);
6217
6218 current_err = try comp.lld_errors.addOne(comp.gpa);
6219 current_err.?.* = .{ .msg = duped_msg };
6220 } else if (current_err != null) {
6221 const context_prefix = ">>> ";
6222 var trimmed = mem.trimRight(u8, line, &std.ascii.whitespace);
6223 if (mem.startsWith(u8, trimmed, context_prefix)) {
6224 trimmed = trimmed[context_prefix.len..];
6225 }
6226
6227 if (trimmed.len > 0) {
6228 const duped_line = try comp.gpa.dupe(u8, trimmed);
6229 try context_lines.append(duped_line);
6230 }
6231 }
6232 }
6233
6234 if (current_err) |err| {
6235 err.context_lines = try context_lines.toOwnedSlice();
6236 }
6237}
6238
6239pub fn lockAndParseLldStderr(comp: *Compilation, prefix: []const u8, stderr: []const u8) void {
6240 comp.mutex.lock();
6241 defer comp.mutex.unlock();
6242
6243 comp.parseLldStderr(prefix, stderr) catch comp.setAllocFailure();
6244}
6245
62466176pub fn dump_argv(argv: []const []const u8) void {
62476177 std.debug.lockStdErr();
62486178 defer std.debug.unlockStdErr();
src/link.zig+249-84
......@@ -37,6 +37,252 @@ pub const SystemLib = struct {
3737 path: ?Path,
3838};
3939
40pub const Diags = struct {
41 /// Stored here so that function definitions can distinguish between
42 /// needing an allocator for things besides error reporting.
43 gpa: Allocator,
44 mutex: std.Thread.Mutex,
45 msgs: std.ArrayListUnmanaged(Msg),
46 flags: Flags,
47 lld: std.ArrayListUnmanaged(Lld),
48
49 pub const Flags = packed struct {
50 no_entry_point_found: bool = false,
51 missing_libc: bool = false,
52 alloc_failure_occurred: bool = false,
53
54 const Int = blk: {
55 const bits = @typeInfo(@This()).@"struct".fields.len;
56 break :blk @Type(.{ .int = .{
57 .signedness = .unsigned,
58 .bits = bits,
59 } });
60 };
61
62 pub fn anySet(ef: Flags) bool {
63 return @as(Int, @bitCast(ef)) > 0;
64 }
65 };
66
67 pub const Lld = struct {
68 /// Allocated with gpa.
69 msg: []const u8,
70 context_lines: []const []const u8 = &.{},
71
72 pub fn deinit(self: *Lld, gpa: Allocator) void {
73 for (self.context_lines) |line| gpa.free(line);
74 gpa.free(self.context_lines);
75 gpa.free(self.msg);
76 self.* = undefined;
77 }
78 };
79
80 pub const Msg = struct {
81 msg: []const u8,
82 notes: []Msg = &.{},
83
84 pub fn deinit(self: *Msg, gpa: Allocator) void {
85 for (self.notes) |*note| note.deinit(gpa);
86 gpa.free(self.notes);
87 gpa.free(self.msg);
88 }
89 };
90
91 pub const ErrorWithNotes = struct {
92 diags: *Diags,
93 /// Allocated index in diags.msgs array.
94 index: usize,
95 /// Next available note slot.
96 note_slot: usize = 0,
97
98 pub fn addMsg(
99 err: ErrorWithNotes,
100 comptime format: []const u8,
101 args: anytype,
102 ) error{OutOfMemory}!void {
103 const gpa = err.diags.gpa;
104 const err_msg = &err.diags.msgs.items[err.index];
105 err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
106 }
107
108 pub fn addNote(
109 err: *ErrorWithNotes,
110 comptime format: []const u8,
111 args: anytype,
112 ) error{OutOfMemory}!void {
113 const gpa = err.diags.gpa;
114 const err_msg = &err.diags.msgs.items[err.index];
115 assert(err.note_slot < err_msg.notes.len);
116 err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };
117 err.note_slot += 1;
118 }
119 };
120
121 pub fn init(gpa: Allocator) Diags {
122 return .{
123 .gpa = gpa,
124 .mutex = .{},
125 .msgs = .empty,
126 .flags = .{},
127 .lld = .empty,
128 };
129 }
130
131 pub fn deinit(diags: *Diags) void {
132 const gpa = diags.gpa;
133
134 for (diags.msgs.items) |*item| item.deinit(gpa);
135 diags.msgs.deinit(gpa);
136
137 for (diags.lld.items) |*item| item.deinit(gpa);
138 diags.lld.deinit(gpa);
139
140 diags.* = undefined;
141 }
142
143 pub fn hasErrors(diags: *Diags) bool {
144 return diags.msgs.items.len > 0 or diags.flags.anySet();
145 }
146
147 pub fn lockAndParseLldStderr(diags: *Diags, prefix: []const u8, stderr: []const u8) void {
148 diags.mutex.lock();
149 defer diags.mutex.unlock();
150
151 diags.parseLldStderr(prefix, stderr) catch diags.setAllocFailure();
152 }
153
154 fn parseLldStderr(
155 diags: *Diags,
156 prefix: []const u8,
157 stderr: []const u8,
158 ) Allocator.Error!void {
159 const gpa = diags.gpa;
160
161 var context_lines = std.ArrayList([]const u8).init(gpa);
162 defer context_lines.deinit();
163
164 var current_err: ?*Lld = null;
165 var lines = mem.splitSequence(u8, stderr, if (builtin.os.tag == .windows) "\r\n" else "\n");
166 while (lines.next()) |line| {
167 if (line.len > prefix.len + ":".len and
168 mem.eql(u8, line[0..prefix.len], prefix) and line[prefix.len] == ':')
169 {
170 if (current_err) |err| {
171 err.context_lines = try context_lines.toOwnedSlice();
172 }
173
174 var split = mem.splitSequence(u8, line, "error: ");
175 _ = split.first();
176
177 const duped_msg = try std.fmt.allocPrint(gpa, "{s}: {s}", .{ prefix, split.rest() });
178 errdefer gpa.free(duped_msg);
179
180 current_err = try diags.lld.addOne(gpa);
181 current_err.?.* = .{ .msg = duped_msg };
182 } else if (current_err != null) {
183 const context_prefix = ">>> ";
184 var trimmed = mem.trimRight(u8, line, &std.ascii.whitespace);
185 if (mem.startsWith(u8, trimmed, context_prefix)) {
186 trimmed = trimmed[context_prefix.len..];
187 }
188
189 if (trimmed.len > 0) {
190 const duped_line = try gpa.dupe(u8, trimmed);
191 try context_lines.append(duped_line);
192 }
193 }
194 }
195
196 if (current_err) |err| {
197 err.context_lines = try context_lines.toOwnedSlice();
198 }
199 }
200
201 pub fn fail(diags: *Diags, comptime format: []const u8, args: anytype) error{LinkFailure} {
202 @branchHint(.cold);
203 addError(diags, format, args);
204 return error.LinkFailure;
205 }
206
207 pub fn addError(diags: *Diags, comptime format: []const u8, args: anytype) void {
208 @branchHint(.cold);
209 const gpa = diags.gpa;
210 diags.mutex.lock();
211 defer diags.mutex.unlock();
212 diags.msgs.ensureUnusedCapacity(gpa, 1) catch |err| switch (err) {
213 error.OutOfMemory => {
214 diags.flags.alloc_failure_occurred = true;
215 return;
216 },
217 };
218 const err_msg: Msg = .{
219 .msg = std.fmt.allocPrint(gpa, format, args) catch |err| switch (err) {
220 error.OutOfMemory => {
221 diags.flags.alloc_failure_occurred = true;
222 return;
223 },
224 },
225 };
226 diags.msgs.appendAssumeCapacity(err_msg);
227 }
228
229 pub fn addErrorWithNotes(diags: *Diags, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
230 @branchHint(.cold);
231 const gpa = diags.gpa;
232 diags.mutex.lock();
233 defer diags.mutex.unlock();
234 try diags.msgs.ensureUnusedCapacity(gpa, 1);
235 return addErrorWithNotesAssumeCapacity(diags, note_count);
236 }
237
238 pub fn addErrorWithNotesAssumeCapacity(diags: *Diags, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
239 @branchHint(.cold);
240 const gpa = diags.gpa;
241 const index = diags.msgs.items.len;
242 const err = diags.msgs.addOneAssumeCapacity();
243 err.* = .{
244 .msg = undefined,
245 .notes = try gpa.alloc(Diags.Msg, note_count),
246 };
247 return .{
248 .diags = diags,
249 .index = index,
250 };
251 }
252
253 pub fn reportMissingLibraryError(
254 diags: *Diags,
255 checked_paths: []const []const u8,
256 comptime format: []const u8,
257 args: anytype,
258 ) error{OutOfMemory}!void {
259 @branchHint(.cold);
260 var err = try diags.addErrorWithNotes(checked_paths.len);
261 try err.addMsg(format, args);
262 for (checked_paths) |path| {
263 try err.addNote("tried {s}", .{path});
264 }
265 }
266
267 pub fn reportParseError(
268 diags: *Diags,
269 path: Path,
270 comptime format: []const u8,
271 args: anytype,
272 ) error{OutOfMemory}!void {
273 @branchHint(.cold);
274 var err = try diags.addErrorWithNotes(1);
275 try err.addMsg(format, args);
276 try err.addNote("while parsing {}", .{path});
277 }
278
279 pub fn setAllocFailure(diags: *Diags) void {
280 @branchHint(.cold);
281 log.debug("memory allocation failure", .{});
282 diags.flags.alloc_failure_occurred = true;
283 }
284};
285
40286pub fn hashAddSystemLibs(
41287 man: *Cache.Manifest,
42288 hm: std.StringArrayHashMapUnmanaged(SystemLib),
......@@ -446,58 +692,6 @@ pub const File = struct {
446692 }
447693 }
448694
449 pub const ErrorWithNotes = struct {
450 base: *const File,
451
452 /// Allocated index in base.errors array.
453 index: usize,
454
455 /// Next available note slot.
456 note_slot: usize = 0,
457
458 pub fn addMsg(
459 err: ErrorWithNotes,
460 comptime format: []const u8,
461 args: anytype,
462 ) error{OutOfMemory}!void {
463 const gpa = err.base.comp.gpa;
464 const err_msg = &err.base.comp.link_errors.items[err.index];
465 err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
466 }
467
468 pub fn addNote(
469 err: *ErrorWithNotes,
470 comptime format: []const u8,
471 args: anytype,
472 ) error{OutOfMemory}!void {
473 const gpa = err.base.comp.gpa;
474 const err_msg = &err.base.comp.link_errors.items[err.index];
475 assert(err.note_slot < err_msg.notes.len);
476 err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };
477 err.note_slot += 1;
478 }
479 };
480
481 pub fn addErrorWithNotes(base: *const File, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
482 base.comp.link_errors_mutex.lock();
483 defer base.comp.link_errors_mutex.unlock();
484 const gpa = base.comp.gpa;
485 try base.comp.link_errors.ensureUnusedCapacity(gpa, 1);
486 return base.addErrorWithNotesAssumeCapacity(note_count);
487 }
488
489 pub fn addErrorWithNotesAssumeCapacity(base: *const File, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
490 const gpa = base.comp.gpa;
491 const index = base.comp.link_errors.items.len;
492 const err = base.comp.link_errors.addOneAssumeCapacity();
493 err.* = .{ .msg = undefined, .notes = try gpa.alloc(ErrorMsg, note_count) };
494 return .{ .base = base, .index = index };
495 }
496
497 pub fn hasErrors(base: *const File) bool {
498 return base.comp.link_errors.items.len > 0 or base.comp.link_error_flags.isSet();
499 }
500
501695 pub fn releaseLock(self: *File) void {
502696 if (self.lock) |*lock| {
503697 lock.release();
......@@ -523,7 +717,7 @@ pub const File = struct {
523717 }
524718
525719 /// TODO audit this error set. most of these should be collapsed into one error,
526 /// and ErrorFlags should be updated to convey the meaning to the user.
720 /// and Diags.Flags should be updated to convey the meaning to the user.
527721 pub const FlushError = error{
528722 CacheUnavailable,
529723 CurrentWorkingDirectoryUnlinked,
......@@ -939,36 +1133,6 @@ pub const File = struct {
9391133 }
9401134 };
9411135
942 pub const ErrorFlags = packed struct {
943 no_entry_point_found: bool = false,
944 missing_libc: bool = false,
945
946 const Int = blk: {
947 const bits = @typeInfo(@This()).@"struct".fields.len;
948 break :blk @Type(.{ .int = .{
949 .signedness = .unsigned,
950 .bits = bits,
951 } });
952 };
953
954 fn isSet(ef: ErrorFlags) bool {
955 return @as(Int, @bitCast(ef)) > 0;
956 }
957 };
958
959 pub const ErrorMsg = struct {
960 msg: []const u8,
961 notes: []ErrorMsg = &.{},
962
963 pub fn deinit(self: *ErrorMsg, gpa: Allocator) void {
964 for (self.notes) |*note| {
965 note.deinit(gpa);
966 }
967 gpa.free(self.notes);
968 gpa.free(self.msg);
969 }
970 };
971
9721136 pub const LazySymbol = struct {
9731137 pub const Kind = enum { code, const_data };
9741138
......@@ -1154,7 +1318,8 @@ pub fn spawnLld(
11541318 switch (term) {
11551319 .Exited => |code| if (code != 0) {
11561320 if (comp.clang_passthrough_mode) std.process.exit(code);
1157 comp.lockAndParseLldStderr(argv[1], stderr);
1321 const diags = &comp.link_diags;
1322 diags.lockAndParseLldStderr(argv[1], stderr);
11581323 return error.LLDReportedFailure;
11591324 },
11601325 else => {
src/link/Coff.zig+3-2
......@@ -1679,6 +1679,7 @@ pub fn flushModule(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
16791679
16801680 const comp = self.base.comp;
16811681 const gpa = comp.gpa;
1682 const diags = &comp.link_diags;
16821683
16831684 if (self.llvm_object) |llvm_object| {
16841685 try self.base.emitLlvmObject(arena, llvm_object, prog_node);
......@@ -1796,10 +1797,10 @@ pub fn flushModule(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
17961797
17971798 if (self.entry_addr == null and comp.config.output_mode == .Exe) {
17981799 log.debug("flushing. no_entry_point_found = true\n", .{});
1799 comp.link_error_flags.no_entry_point_found = true;
1800 diags.flags.no_entry_point_found = true;
18001801 } else {
18011802 log.debug("flushing. no_entry_point_found = false\n", .{});
1802 comp.link_error_flags.no_entry_point_found = false;
1803 diags.flags.no_entry_point_found = false;
18031804 try self.writeHeader();
18041805 }
18051806
src/link/Elf.zig+32-33
......@@ -769,6 +769,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
769769
770770 const comp = self.base.comp;
771771 const gpa = comp.gpa;
772 const diags = &comp.link_diags;
772773
773774 if (self.llvm_object) |llvm_object| {
774775 try self.base.emitLlvmObject(arena, llvm_object, prog_node);
......@@ -848,7 +849,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
848849 }
849850
850851 // libc dep
851 comp.link_error_flags.missing_libc = false;
852 diags.flags.missing_libc = false;
852853 if (comp.config.link_libc) {
853854 if (comp.libc_installation) |lc| {
854855 const flags = target_util.libcFullLinkFlags(target);
......@@ -868,7 +869,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
868869 if (try self.accessLibPath(arena, &test_path, &checked_paths, lc.crt_dir.?, lib_name, .static))
869870 break :success;
870871
871 try self.reportMissingLibraryError(
872 try diags.reportMissingLibraryError(
872873 checked_paths.items,
873874 "missing system library: '{s}' was not found",
874875 .{lib_name},
......@@ -901,7 +902,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
901902 });
902903 try self.parseLibraryReportingFailure(.{ .path = path }, false);
903904 } else {
904 comp.link_error_flags.missing_libc = true;
905 diags.flags.missing_libc = true;
905906 }
906907 }
907908
......@@ -920,7 +921,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
920921 if (csu.crtend) |path| try parseObjectReportingFailure(self, path);
921922 if (csu.crtn) |path| try parseObjectReportingFailure(self, path);
922923
923 if (self.base.hasErrors()) return error.FlushFailure;
924 if (diags.hasErrors()) return error.FlushFailure;
924925
925926 // Dedup shared objects
926927 {
......@@ -1078,14 +1079,14 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
10781079
10791080 if (self.base.isExe() and self.linkerDefinedPtr().?.entry_index == null) {
10801081 log.debug("flushing. no_entry_point_found = true", .{});
1081 comp.link_error_flags.no_entry_point_found = true;
1082 diags.flags.no_entry_point_found = true;
10821083 } else {
10831084 log.debug("flushing. no_entry_point_found = false", .{});
1084 comp.link_error_flags.no_entry_point_found = false;
1085 diags.flags.no_entry_point_found = false;
10851086 try self.writeElfHeader();
10861087 }
10871088
1088 if (self.base.hasErrors()) return error.FlushFailure;
1089 if (diags.hasErrors()) return error.FlushFailure;
10891090}
10901091
10911092/// --verbose-link output
......@@ -1358,7 +1359,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
13581359}
13591360
13601361pub const ParseError = error{
1361 /// Indicates the error is already reported on `Compilation.link_errors`.
1362 /// Indicates the error is already reported on `Compilation.link_diags`.
13621363 LinkFailure,
13631364
13641365 OutOfMemory,
......@@ -1484,7 +1485,10 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
14841485 const tracy = trace(@src());
14851486 defer tracy.end();
14861487
1487 const gpa = self.base.comp.gpa;
1488 const comp = self.base.comp;
1489 const gpa = comp.gpa;
1490 const diags = &comp.link_diags;
1491
14881492 const in_file = try lib.path.root_dir.handle.openFile(lib.path.sub_path, .{});
14891493 defer in_file.close();
14901494 const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
......@@ -1533,7 +1537,7 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
15331537 }
15341538 }
15351539
1536 try self.reportMissingLibraryError(
1540 try diags.reportMissingLibraryError(
15371541 checked_paths.items,
15381542 "missing library dependency: GNU ld script '{}' requires '{s}', but file not found",
15391543 .{ @as(Path, lib.path), script_arg.path },
......@@ -1856,6 +1860,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
18561860
18571861 const comp = self.base.comp;
18581862 const gpa = comp.gpa;
1863 const diags = &comp.link_diags;
18591864
18601865 const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type.
18611866 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
......@@ -2376,7 +2381,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
23762381 }
23772382
23782383 // libc dep
2379 comp.link_error_flags.missing_libc = false;
2384 diags.flags.missing_libc = false;
23802385 if (comp.config.link_libc) {
23812386 if (comp.libc_installation != null) {
23822387 const needs_grouping = link_mode == .static;
......@@ -2401,7 +2406,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
24012406 .dynamic => "libc.so",
24022407 }));
24032408 } else {
2404 comp.link_error_flags.missing_libc = true;
2409 diags.flags.missing_libc = true;
24052410 }
24062411 }
24072412 }
......@@ -2546,7 +2551,8 @@ fn writePhdrTable(self: *Elf) !void {
25462551}
25472552
25482553pub fn writeElfHeader(self: *Elf) !void {
2549 if (self.base.hasErrors()) return; // We had errors, so skip flushing to render the output unusable
2554 const diags = &self.base.comp.link_diags;
2555 if (diags.hasErrors()) return; // We had errors, so skip flushing to render the output unusable
25502556
25512557 const comp = self.base.comp;
25522558 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
......@@ -3700,6 +3706,7 @@ fn addLoadPhdrs(self: *Elf) error{OutOfMemory}!void {
37003706
37013707/// Allocates PHDR table in virtual memory and in file.
37023708fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {
3709 const diags = &self.base.comp.link_diags;
37033710 const phdr_table = &self.phdrs.items[self.phdr_indexes.table.int().?];
37043711 const phdr_table_load = &self.phdrs.items[self.phdr_indexes.table_load.int().?];
37053712
......@@ -3720,7 +3727,7 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {
37203727 // (revisit getMaxNumberOfPhdrs())
37213728 // 2. shift everything in file to free more space for EHDR + PHDR table
37223729 // TODO verify `getMaxNumberOfPhdrs()` is accurate and convert this into no-op
3723 var err = try self.base.addErrorWithNotes(1);
3730 var err = try diags.addErrorWithNotes(1);
37243731 try err.addMsg("fatal linker error: not enough space reserved for EHDR and PHDR table", .{});
37253732 try err.addNote("required 0x{x}, available 0x{x}", .{ needed_size, available_space });
37263733 }
......@@ -4855,16 +4862,17 @@ pub fn insertDynString(self: *Elf, name: []const u8) error{OutOfMemory}!u32 {
48554862
48564863fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
48574864 const gpa = self.base.comp.gpa;
4865 const diags = &self.base.comp.link_diags;
48584866 const max_notes = 4;
48594867
4860 try self.base.comp.link_errors.ensureUnusedCapacity(gpa, undefs.count());
4868 try diags.msgs.ensureUnusedCapacity(gpa, undefs.count());
48614869
48624870 for (undefs.keys(), undefs.values()) |key, refs| {
48634871 const undef_sym = self.resolver.keys.items[key - 1];
48644872 const nrefs = @min(refs.items.len, max_notes);
48654873 const nnotes = nrefs + @intFromBool(refs.items.len > max_notes);
48664874
4867 var err = try self.base.addErrorWithNotesAssumeCapacity(nnotes);
4875 var err = try diags.addErrorWithNotesAssumeCapacity(nnotes);
48684876 try err.addMsg("undefined symbol: {s}", .{undef_sym.name(self)});
48694877
48704878 for (refs.items[0..nrefs]) |ref| {
......@@ -4882,6 +4890,7 @@ fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
48824890
48834891fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemory }!void {
48844892 if (dupes.keys().len == 0) return; // Nothing to do
4893 const diags = &self.base.comp.link_diags;
48854894
48864895 const max_notes = 3;
48874896
......@@ -4889,7 +4898,7 @@ fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemor
48894898 const sym = self.resolver.keys.items[key - 1];
48904899 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
48914900
4892 var err = try self.base.addErrorWithNotes(nnotes + 1);
4901 var err = try diags.addErrorWithNotes(nnotes + 1);
48934902 try err.addMsg("duplicate symbol definition: {s}", .{sym.name(self)});
48944903 try err.addNote("defined by {}", .{sym.file(self).?.fmtPath()});
48954904
......@@ -4908,21 +4917,9 @@ fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemor
49084917 return error.HasDuplicates;
49094918}
49104919
4911fn reportMissingLibraryError(
4912 self: *Elf,
4913 checked_paths: []const []const u8,
4914 comptime format: []const u8,
4915 args: anytype,
4916) error{OutOfMemory}!void {
4917 var err = try self.base.addErrorWithNotes(checked_paths.len);
4918 try err.addMsg(format, args);
4919 for (checked_paths) |path| {
4920 try err.addNote("tried {s}", .{path});
4921 }
4922}
4923
49244920fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void {
4925 var err = try self.base.addErrorWithNotes(0);
4921 const diags = &self.base.comp.link_diags;
4922 var err = try diags.addErrorWithNotes(0);
49264923 try err.addMsg("fatal linker error: unsupported CPU architecture {s}", .{
49274924 @tagName(self.getTarget().cpu.arch),
49284925 });
......@@ -4934,7 +4931,8 @@ pub fn addParseError(
49344931 comptime format: []const u8,
49354932 args: anytype,
49364933) error{OutOfMemory}!void {
4937 var err = try self.base.addErrorWithNotes(1);
4934 const diags = &self.base.comp.link_diags;
4935 var err = try diags.addErrorWithNotes(1);
49384936 try err.addMsg(format, args);
49394937 try err.addNote("while parsing {}", .{path});
49404938}
......@@ -4945,7 +4943,8 @@ pub fn addFileError(
49454943 comptime format: []const u8,
49464944 args: anytype,
49474945) error{OutOfMemory}!void {
4948 var err = try self.base.addErrorWithNotes(1);
4946 const diags = &self.base.comp.link_diags;
4947 var err = try diags.addErrorWithNotes(1);
49494948 try err.addMsg(format, args);
49504949 try err.addNote("while parsing {}", .{self.file(file_index).?.fmtPath()});
49514950}
src/link/Elf/Atom.zig+20-10
......@@ -519,7 +519,8 @@ fn dataType(symbol: *const Symbol, elf_file: *Elf) u2 {
519519}
520520
521521fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) RelocError!void {
522 var err = try elf_file.base.addErrorWithNotes(1);
522 const diags = &elf_file.base.comp.link_diags;
523 var err = try diags.addErrorWithNotes(1);
523524 try err.addMsg("fatal linker error: unhandled relocation type {} at offset 0x{x}", .{
524525 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),
525526 rel.r_offset,
......@@ -534,7 +535,8 @@ fn reportTextRelocError(
534535 rel: elf.Elf64_Rela,
535536 elf_file: *Elf,
536537) RelocError!void {
537 var err = try elf_file.base.addErrorWithNotes(1);
538 const diags = &elf_file.base.comp.link_diags;
539 var err = try diags.addErrorWithNotes(1);
538540 try err.addMsg("relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
539541 rel.r_offset,
540542 symbol.name(elf_file),
......@@ -549,7 +551,8 @@ fn reportPicError(
549551 rel: elf.Elf64_Rela,
550552 elf_file: *Elf,
551553) RelocError!void {
552 var err = try elf_file.base.addErrorWithNotes(2);
554 const diags = &elf_file.base.comp.link_diags;
555 var err = try diags.addErrorWithNotes(2);
553556 try err.addMsg("relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
554557 rel.r_offset,
555558 symbol.name(elf_file),
......@@ -565,7 +568,8 @@ fn reportNoPicError(
565568 rel: elf.Elf64_Rela,
566569 elf_file: *Elf,
567570) RelocError!void {
568 var err = try elf_file.base.addErrorWithNotes(2);
571 const diags = &elf_file.base.comp.link_diags;
572 var err = try diags.addErrorWithNotes(2);
569573 try err.addMsg("relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
570574 rel.r_offset,
571575 symbol.name(elf_file),
......@@ -1082,6 +1086,7 @@ const x86_64 = struct {
10821086 stream: anytype,
10831087 ) (error{ InvalidInstruction, CannotEncode } || RelocError)!void {
10841088 dev.check(.x86_64_backend);
1089 const diags = &elf_file.base.comp.link_diags;
10851090 const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type());
10861091 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
10871092
......@@ -1176,7 +1181,7 @@ const x86_64 = struct {
11761181 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
11771182 } else {
11781183 x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]) catch {
1179 var err = try elf_file.base.addErrorWithNotes(1);
1184 var err = try diags.addErrorWithNotes(1);
11801185 try err.addMsg("could not relax {s}", .{@tagName(r_type)});
11811186 try err.addNote("in {}:{s} at offset 0x{x}", .{
11821187 atom.file(elf_file).?.fmtPath(),
......@@ -1301,6 +1306,7 @@ const x86_64 = struct {
13011306 ) !void {
13021307 dev.check(.x86_64_backend);
13031308 assert(rels.len == 2);
1309 const diags = &elf_file.base.comp.link_diags;
13041310 const writer = stream.writer();
13051311 const rel: elf.R_X86_64 = @enumFromInt(rels[1].r_type());
13061312 switch (rel) {
......@@ -1317,7 +1323,7 @@ const x86_64 = struct {
13171323 },
13181324
13191325 else => {
1320 var err = try elf_file.base.addErrorWithNotes(1);
1326 var err = try diags.addErrorWithNotes(1);
13211327 try err.addMsg("TODO: rewrite {} when followed by {}", .{
13221328 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
13231329 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
......@@ -1341,6 +1347,7 @@ const x86_64 = struct {
13411347 ) !void {
13421348 dev.check(.x86_64_backend);
13431349 assert(rels.len == 2);
1350 const diags = &elf_file.base.comp.link_diags;
13441351 const writer = stream.writer();
13451352 const rel: elf.R_X86_64 = @enumFromInt(rels[1].r_type());
13461353 switch (rel) {
......@@ -1372,7 +1379,7 @@ const x86_64 = struct {
13721379 },
13731380
13741381 else => {
1375 var err = try elf_file.base.addErrorWithNotes(1);
1382 var err = try diags.addErrorWithNotes(1);
13761383 try err.addMsg("TODO: rewrite {} when followed by {}", .{
13771384 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
13781385 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
......@@ -1446,6 +1453,7 @@ const x86_64 = struct {
14461453 ) !void {
14471454 dev.check(.x86_64_backend);
14481455 assert(rels.len == 2);
1456 const diags = &elf_file.base.comp.link_diags;
14491457 const writer = stream.writer();
14501458 const rel: elf.R_X86_64 = @enumFromInt(rels[1].r_type());
14511459 switch (rel) {
......@@ -1468,7 +1476,7 @@ const x86_64 = struct {
14681476 },
14691477
14701478 else => {
1471 var err = try elf_file.base.addErrorWithNotes(1);
1479 var err = try diags.addErrorWithNotes(1);
14721480 try err.addMsg("fatal linker error: rewrite {} when followed by {}", .{
14731481 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
14741482 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
......@@ -1603,6 +1611,7 @@ const aarch64 = struct {
16031611 ) (error{ UnexpectedRemainder, DivisionByZero } || RelocError)!void {
16041612 _ = it;
16051613
1614 const diags = &elf_file.base.comp.link_diags;
16061615 const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type());
16071616 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
16081617 const cwriter = stream.writer();
......@@ -1657,7 +1666,7 @@ const aarch64 = struct {
16571666 aarch64_util.writeAdrpInst(pages, code);
16581667 } else {
16591668 // TODO: relax
1660 var err = try elf_file.base.addErrorWithNotes(1);
1669 var err = try diags.addErrorWithNotes(1);
16611670 try err.addMsg("TODO: relax ADR_GOT_PAGE", .{});
16621671 try err.addNote("in {}:{s} at offset 0x{x}", .{
16631672 atom.file(elf_file).?.fmtPath(),
......@@ -1882,6 +1891,7 @@ const riscv = struct {
18821891 code: []u8,
18831892 stream: anytype,
18841893 ) !void {
1894 const diags = &elf_file.base.comp.link_diags;
18851895 const r_type: elf.R_RISCV = @enumFromInt(rel.r_type());
18861896 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
18871897 const cwriter = stream.writer();
......@@ -1943,7 +1953,7 @@ const riscv = struct {
19431953 if (S == atom_addr + @as(i64, @intCast(pair.r_offset))) break pair;
19441954 } else {
19451955 // TODO: implement searching forward
1946 var err = try elf_file.base.addErrorWithNotes(1);
1956 var err = try diags.addErrorWithNotes(1);
19471957 try err.addMsg("TODO: find HI20 paired reloc scanning forward", .{});
19481958 try err.addNote("in {}:{s} at offset 0x{x}", .{
19491959 atom.file(elf_file).?.fmtPath(),
src/link/Elf/Object.zig+6-4
......@@ -644,6 +644,7 @@ pub fn checkDuplicates(self: *Object, dupes: anytype, elf_file: *Elf) error{OutO
644644
645645pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void {
646646 const gpa = elf_file.base.comp.gpa;
647 const diags = &elf_file.base.comp.link_diags;
647648
648649 try self.input_merge_sections.ensureUnusedCapacity(gpa, self.shdrs.items.len);
649650 try self.input_merge_sections_indexes.resize(gpa, self.shdrs.items.len);
......@@ -685,7 +686,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void {
685686 var end = start;
686687 while (end < data.len - sh_entsize and !isNull(data[end .. end + sh_entsize])) : (end += sh_entsize) {}
687688 if (!isNull(data[end .. end + sh_entsize])) {
688 var err = try elf_file.base.addErrorWithNotes(1);
689 var err = try diags.addErrorWithNotes(1);
689690 try err.addMsg("string not null terminated", .{});
690691 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
691692 return error.LinkFailure;
......@@ -700,7 +701,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void {
700701 const sh_entsize: u32 = @intCast(shdr.sh_entsize);
701702 if (sh_entsize == 0) continue; // Malformed, don't split but don't error out
702703 if (shdr.sh_size % sh_entsize != 0) {
703 var err = try elf_file.base.addErrorWithNotes(1);
704 var err = try diags.addErrorWithNotes(1);
704705 try err.addMsg("size not a multiple of sh_entsize", .{});
705706 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
706707 return error.LinkFailure;
......@@ -738,6 +739,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{
738739 Overflow,
739740}!void {
740741 const gpa = elf_file.base.comp.gpa;
742 const diags = &elf_file.base.comp.link_diags;
741743
742744 for (self.input_merge_sections_indexes.items) |index| {
743745 const imsec = self.inputMergeSection(index) orelse continue;
......@@ -776,7 +778,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{
776778 const imsec = self.inputMergeSection(imsec_index) orelse continue;
777779 if (imsec.offsets.items.len == 0) continue;
778780 const res = imsec.findSubsection(@intCast(esym.st_value)) orelse {
779 var err = try elf_file.base.addErrorWithNotes(2);
781 var err = try diags.addErrorWithNotes(2);
780782 try err.addMsg("invalid symbol value: {x}", .{esym.st_value});
781783 try err.addNote("for symbol {s}", .{sym.name(elf_file)});
782784 try err.addNote("in {}", .{self.fmtPath()});
......@@ -802,7 +804,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{
802804 if (imsec.offsets.items.len == 0) continue;
803805 const msec = elf_file.mergeSection(imsec.merge_section_index);
804806 const res = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse {
805 var err = try elf_file.base.addErrorWithNotes(1);
807 var err = try diags.addErrorWithNotes(1);
806808 try err.addMsg("invalid relocation at offset 0x{x}", .{rel.r_offset});
807809 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
808810 return error.LinkFailure;
src/link/Elf/eh_frame.zig+2-1
......@@ -611,7 +611,8 @@ const riscv = struct {
611611};
612612
613613fn reportInvalidReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela) !void {
614 var err = try elf_file.base.addErrorWithNotes(1);
614 const diags = &elf_file.base.comp.link_diags;
615 var err = try diags.addErrorWithNotes(1);
615616 try err.addMsg("invalid relocation type {} at offset 0x{x}", .{
616617 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),
617618 rel.r_offset,
src/link/Elf/relocatable.zig+7-4
......@@ -1,5 +1,6 @@
11pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void {
22 const gpa = comp.gpa;
3 const diags = &comp.link_diags;
34
45 for (comp.objects) |obj| {
56 switch (Compilation.classifyFileExt(obj.path.sub_path)) {
......@@ -21,7 +22,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path
2122 try parseObjectStaticLibReportingFailure(elf_file, comp.compiler_rt_obj.?.full_object_path);
2223 }
2324
24 if (elf_file.base.hasErrors()) return error.FlushFailure;
25 if (diags.hasErrors()) return error.FlushFailure;
2526
2627 // First, we flush relocatable object file generated with our backends.
2728 if (elf_file.zigObjectPtr()) |zig_object| {
......@@ -146,10 +147,12 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path
146147 try elf_file.base.file.?.setEndPos(total_size);
147148 try elf_file.base.file.?.pwriteAll(buffer.items, 0);
148149
149 if (elf_file.base.hasErrors()) return error.FlushFailure;
150 if (diags.hasErrors()) return error.FlushFailure;
150151}
151152
152153pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void {
154 const diags = &comp.link_diags;
155
153156 for (comp.objects) |obj| {
154157 if (obj.isObject()) {
155158 try elf_file.parseObjectReportingFailure(obj.path);
......@@ -167,7 +170,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) l
167170
168171 if (module_obj_path) |path| try elf_file.parseObjectReportingFailure(path);
169172
170 if (elf_file.base.hasErrors()) return error.FlushFailure;
173 if (diags.hasErrors()) return error.FlushFailure;
171174
172175 // Now, we are ready to resolve the symbols across all input files.
173176 // We will first resolve the files in the ZigObject, next in the parsed
......@@ -213,7 +216,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) l
213216 try elf_file.writeShdrTable();
214217 try elf_file.writeElfHeader();
215218
216 if (elf_file.base.hasErrors()) return error.FlushFailure;
219 if (diags.hasErrors()) return error.FlushFailure;
217220}
218221
219222fn parseObjectStaticLibReportingFailure(elf_file: *Elf, path: Path) error{OutOfMemory}!void {
src/link/MachO.zig+80-119
......@@ -100,7 +100,6 @@ debug_rnglists_sect_index: ?u8 = null,
100100has_tlv: AtomicBool = AtomicBool.init(false),
101101binds_to_weak: AtomicBool = AtomicBool.init(false),
102102weak_defines: AtomicBool = AtomicBool.init(false),
103has_errors: AtomicBool = AtomicBool.init(false),
104103
105104/// Options
106105/// SDK layout
......@@ -347,6 +346,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
347346
348347 const comp = self.base.comp;
349348 const gpa = comp.gpa;
349 const diags = &self.base.comp.link_diags;
350350
351351 if (self.llvm_object) |llvm_object| {
352352 try self.base.emitLlvmObject(arena, llvm_object, prog_node);
......@@ -397,8 +397,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
397397
398398 for (positionals.items) |obj| {
399399 self.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
400 error.UnknownFileType => try self.reportParseError(obj.path, "unknown file type for an input file", .{}),
401 else => |e| try self.reportParseError(
400 error.UnknownFileType => try diags.reportParseError(obj.path, "unknown file type for an input file", .{}),
401 else => |e| try diags.reportParseError(
402402 obj.path,
403403 "unexpected error: reading input file failed with error {s}",
404404 .{@errorName(e)},
......@@ -444,8 +444,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
444444
445445 for (system_libs.items) |lib| {
446446 self.classifyInputFile(lib.path, lib, false) catch |err| switch (err) {
447 error.UnknownFileType => try self.reportParseError(lib.path, "unknown file type for an input file", .{}),
448 else => |e| try self.reportParseError(
447 error.UnknownFileType => try diags.reportParseError(lib.path, "unknown file type for an input file", .{}),
448 else => |e| try diags.reportParseError(
449449 lib.path,
450450 "unexpected error: parsing input file failed with error {s}",
451451 .{@errorName(e)},
......@@ -461,8 +461,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
461461 };
462462 if (compiler_rt_path) |path| {
463463 self.classifyInputFile(path, .{ .path = path }, false) catch |err| switch (err) {
464 error.UnknownFileType => try self.reportParseError(path, "unknown file type for an input file", .{}),
465 else => |e| try self.reportParseError(
464 error.UnknownFileType => try diags.reportParseError(path, "unknown file type for an input file", .{}),
465 else => |e| try diags.reportParseError(
466466 path,
467467 "unexpected error: parsing input file failed with error {s}",
468468 .{@errorName(e)},
......@@ -474,14 +474,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
474474 self.parseDependentDylibs() catch |err| {
475475 switch (err) {
476476 error.MissingLibraryDependencies => {},
477 else => |e| try self.reportUnexpectedError(
478 "unexpected error while parsing dependent libraries: {s}",
479 .{@errorName(e)},
480 ),
477 else => |e| return diags.fail("failed to parse dependent libraries: {s}", .{@errorName(e)}),
481478 }
482479 };
483480
484 if (self.base.hasErrors()) return error.FlushFailure;
481 if (diags.hasErrors()) return error.FlushFailure;
485482
486483 {
487484 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
......@@ -502,10 +499,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
502499
503500 self.checkDuplicates() catch |err| switch (err) {
504501 error.HasDuplicates => return error.FlushFailure,
505 else => |e| {
506 try self.reportUnexpectedError("unexpected error while checking for duplicate symbol definitions", .{});
507 return e;
508 },
502 else => |e| return diags.fail("failed to check for duplicate symbol definitions: {s}", .{@errorName(e)}),
509503 };
510504
511505 self.markImportsAndExports();
......@@ -520,10 +514,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
520514
521515 self.scanRelocs() catch |err| switch (err) {
522516 error.HasUndefinedSymbols => return error.FlushFailure,
523 else => |e| {
524 try self.reportUnexpectedError("unexpected error while scanning relocations", .{});
525 return e;
526 },
517 else => |e| return diags.fail("failed to scan relocations: {s}", .{@errorName(e)}),
527518 };
528519
529520 try self.initOutputSections();
......@@ -784,6 +775,8 @@ pub fn resolveLibSystem(
784775 comp: *Compilation,
785776 out_libs: anytype,
786777) !void {
778 const diags = &self.base.comp.link_diags;
779
787780 var test_path = std.ArrayList(u8).init(arena);
788781 var checked_paths = std.ArrayList([]const u8).init(arena);
789782
......@@ -803,7 +796,7 @@ pub fn resolveLibSystem(
803796 if (try accessLibPath(arena, &test_path, &checked_paths, dir, "System")) break :success;
804797 }
805798
806 try self.reportMissingLibraryError(checked_paths.items, "unable to find libSystem system library", .{});
799 try diags.reportMissingLibraryError(checked_paths.items, "unable to find libSystem system library", .{});
807800 return error.MissingLibSystem;
808801 }
809802
......@@ -845,6 +838,7 @@ pub fn classifyInputFile(self: *MachO, path: Path, lib: SystemLib, must_link: bo
845838}
846839
847840fn parseFatFile(self: *MachO, file: std.fs.File, path: Path) !?fat.Arch {
841 const diags = &self.base.comp.link_diags;
848842 const fat_h = fat.readFatHeader(file) catch return null;
849843 if (fat_h.magic != macho.FAT_MAGIC and fat_h.magic != macho.FAT_MAGIC_64) return null;
850844 var fat_archs_buffer: [2]fat.Arch = undefined;
......@@ -853,7 +847,7 @@ fn parseFatFile(self: *MachO, file: std.fs.File, path: Path) !?fat.Arch {
853847 for (fat_archs) |arch| {
854848 if (arch.tag == cpu_arch) return arch;
855849 }
856 try self.reportParseError(path, "missing arch in universal file: expected {s}", .{
850 try diags.reportParseError(path, "missing arch in universal file: expected {s}", .{
857851 @tagName(cpu_arch),
858852 });
859853 return error.MissingCpuArch;
......@@ -901,6 +895,7 @@ pub fn parseInputFiles(self: *MachO) !void {
901895 const tracy = trace(@src());
902896 defer tracy.end();
903897
898 const diags = &self.base.comp.link_diags;
904899 const tp = self.base.comp.thread_pool;
905900 var wg: WaitGroup = .{};
906901
......@@ -916,7 +911,7 @@ pub fn parseInputFiles(self: *MachO) !void {
916911 }
917912 }
918913
919 if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
914 if (diags.hasErrors()) return error.LinkFailure;
920915}
921916
922917fn parseInputFileWorker(self: *MachO, file: File) void {
......@@ -928,9 +923,9 @@ fn parseInputFileWorker(self: *MachO, file: File) void {
928923 error.InvalidMachineType,
929924 error.InvalidTarget,
930925 => {}, // already reported
926
931927 else => |e| self.reportParseError2(file.getIndex(), "unexpected error: parsing input file failed with error {s}", .{@errorName(e)}) catch {},
932928 }
933 _ = self.has_errors.swap(true, .seq_cst);
934929 };
935930}
936931
......@@ -1296,6 +1291,7 @@ fn markLive(self: *MachO) void {
12961291
12971292fn convertTentativeDefsAndResolveSpecialSymbols(self: *MachO) !void {
12981293 const tp = self.base.comp.thread_pool;
1294 const diags = &self.base.comp.link_diags;
12991295 var wg: WaitGroup = .{};
13001296 {
13011297 wg.reset();
......@@ -1307,7 +1303,7 @@ fn convertTentativeDefsAndResolveSpecialSymbols(self: *MachO) !void {
13071303 tp.spawnWg(&wg, resolveSpecialSymbolsWorker, .{ self, obj });
13081304 }
13091305 }
1310 if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
1306 if (diags.hasErrors()) return error.LinkFailure;
13111307}
13121308
13131309fn convertTentativeDefinitionsWorker(self: *MachO, object: *Object) void {
......@@ -1319,26 +1315,19 @@ fn convertTentativeDefinitionsWorker(self: *MachO, object: *Object) void {
13191315 "unexpected error occurred while converting tentative symbols into defined symbols: {s}",
13201316 .{@errorName(err)},
13211317 ) catch {};
1322 _ = self.has_errors.swap(true, .seq_cst);
13231318 };
13241319}
13251320
13261321fn resolveSpecialSymbolsWorker(self: *MachO, obj: *InternalObject) void {
13271322 const tracy = trace(@src());
13281323 defer tracy.end();
1329 obj.resolveBoundarySymbols(self) catch |err| {
1330 self.reportUnexpectedError("unexpected error occurred while resolving boundary symbols: {s}", .{
1331 @errorName(err),
1332 }) catch {};
1333 _ = self.has_errors.swap(true, .seq_cst);
1334 return;
1335 };
1336 obj.resolveObjcMsgSendSymbols(self) catch |err| {
1337 self.reportUnexpectedError("unexpected error occurred while resolving ObjC msgsend stubs: {s}", .{
1338 @errorName(err),
1339 }) catch {};
1340 _ = self.has_errors.swap(true, .seq_cst);
1341 };
1324
1325 const diags = &self.base.comp.link_diags;
1326
1327 obj.resolveBoundarySymbols(self) catch |err|
1328 return diags.addError("failed to resolve boundary symbols: {s}", .{@errorName(err)});
1329 obj.resolveObjcMsgSendSymbols(self) catch |err|
1330 return diags.addError("failed to resolve ObjC msgsend stubs: {s}", .{@errorName(err)});
13421331}
13431332
13441333pub fn dedupLiterals(self: *MachO) !void {
......@@ -1390,6 +1379,8 @@ fn checkDuplicates(self: *MachO) !void {
13901379 defer tracy.end();
13911380
13921381 const tp = self.base.comp.thread_pool;
1382 const diags = &self.base.comp.link_diags;
1383
13931384 var wg: WaitGroup = .{};
13941385 {
13951386 wg.reset();
......@@ -1405,7 +1396,7 @@ fn checkDuplicates(self: *MachO) !void {
14051396 }
14061397 }
14071398
1408 if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
1399 if (diags.hasErrors()) return error.LinkFailure;
14091400
14101401 try self.reportDuplicates();
14111402}
......@@ -1417,7 +1408,6 @@ fn checkDuplicatesWorker(self: *MachO, file: File) void {
14171408 self.reportParseError2(file.getIndex(), "failed to check for duplicate definitions: {s}", .{
14181409 @errorName(err),
14191410 }) catch {};
1420 _ = self.has_errors.swap(true, .seq_cst);
14211411 };
14221412}
14231413
......@@ -1460,6 +1450,8 @@ fn scanRelocs(self: *MachO) !void {
14601450 defer tracy.end();
14611451
14621452 const tp = self.base.comp.thread_pool;
1453 const diags = &self.base.comp.link_diags;
1454
14631455 var wg: WaitGroup = .{};
14641456
14651457 {
......@@ -1477,7 +1469,7 @@ fn scanRelocs(self: *MachO) !void {
14771469 }
14781470 }
14791471
1480 if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
1472 if (diags.hasErrors()) return error.LinkFailure;
14811473
14821474 if (self.getInternalObject()) |obj| {
14831475 try obj.checkUndefs(self);
......@@ -1503,7 +1495,6 @@ fn scanRelocsWorker(self: *MachO, file: File) void {
15031495 self.reportParseError2(file.getIndex(), "failed to scan relocations: {s}", .{
15041496 @errorName(err),
15051497 }) catch {};
1506 _ = self.has_errors.swap(true, .seq_cst);
15071498 };
15081499}
15091500
......@@ -1527,6 +1518,7 @@ fn reportUndefs(self: *MachO) !void {
15271518 if (self.undefs.keys().len == 0) return; // Nothing to do
15281519
15291520 const gpa = self.base.comp.gpa;
1521 const diags = &self.base.comp.link_diags;
15301522 const max_notes = 4;
15311523
15321524 // We will sort by name, and then by file to ensure deterministic output.
......@@ -1558,7 +1550,7 @@ fn reportUndefs(self: *MachO) !void {
15581550 break :nnotes @min(nnotes, max_notes) + @intFromBool(nnotes > max_notes);
15591551 };
15601552
1561 var err = try self.base.addErrorWithNotes(nnotes);
1553 var err = try diags.addErrorWithNotes(nnotes);
15621554 try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)});
15631555
15641556 switch (notes) {
......@@ -1908,6 +1900,7 @@ fn calcSectionSizes(self: *MachO) !void {
19081900 const tracy = trace(@src());
19091901 defer tracy.end();
19101902
1903 const diags = &self.base.comp.link_diags;
19111904 const cpu_arch = self.getTarget().cpu.arch;
19121905
19131906 if (self.data_sect_index) |idx| {
......@@ -1951,7 +1944,7 @@ fn calcSectionSizes(self: *MachO) !void {
19511944 }
19521945 }
19531946
1954 if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
1947 if (diags.hasErrors()) return error.LinkFailure;
19551948
19561949 try self.calcSymtabSize();
19571950
......@@ -2003,6 +1996,9 @@ fn calcSectionSizes(self: *MachO) !void {
20031996fn calcSectionSizeWorker(self: *MachO, sect_id: u8) void {
20041997 const tracy = trace(@src());
20051998 defer tracy.end();
1999
2000 const diags = &self.base.comp.link_diags;
2001
20062002 const doWork = struct {
20072003 fn doWork(macho_file: *MachO, header: *macho.section_64, atoms: []const Ref) !void {
20082004 for (atoms) |ref| {
......@@ -2020,26 +2016,21 @@ fn calcSectionSizeWorker(self: *MachO, sect_id: u8) void {
20202016 const header = &slice.items(.header)[sect_id];
20212017 const atoms = slice.items(.atoms)[sect_id].items;
20222018 doWork(self, header, atoms) catch |err| {
2023 self.reportUnexpectedError("failed to calculate size of section '{s},{s}': {s}", .{
2024 header.segName(),
2025 header.sectName(),
2026 @errorName(err),
2027 }) catch {};
2028 _ = self.has_errors.swap(true, .seq_cst);
2019 try diags.addError("failed to calculate size of section '{s},{s}': {s}", .{
2020 header.segName(), header.sectName(), @errorName(err),
2021 });
20292022 };
20302023}
20312024
20322025fn createThunksWorker(self: *MachO, sect_id: u8) void {
20332026 const tracy = trace(@src());
20342027 defer tracy.end();
2028 const diags = &self.base.comp.link_diags;
20352029 self.createThunks(sect_id) catch |err| {
20362030 const header = self.sections.items(.header)[sect_id];
2037 self.reportUnexpectedError("failed to create thunks and calculate size of section '{s},{s}': {s}", .{
2038 header.segName(),
2039 header.sectName(),
2040 @errorName(err),
2041 }) catch {};
2042 _ = self.has_errors.swap(true, .seq_cst);
2031 diags.addError("failed to create thunks and calculate size of section '{s},{s}': {s}", .{
2032 header.segName(), header.sectName(), @errorName(err),
2033 });
20432034 };
20442035}
20452036
......@@ -2047,6 +2038,8 @@ fn generateUnwindInfo(self: *MachO) !void {
20472038 const tracy = trace(@src());
20482039 defer tracy.end();
20492040
2041 const diags = &self.base.comp.link_diags;
2042
20502043 if (self.eh_frame_sect_index) |index| {
20512044 const sect = &self.sections.items(.header)[index];
20522045 sect.size = try eh_frame.calcSize(self);
......@@ -2055,10 +2048,7 @@ fn generateUnwindInfo(self: *MachO) !void {
20552048 if (self.unwind_info_sect_index) |index| {
20562049 const sect = &self.sections.items(.header)[index];
20572050 self.unwind_info.generate(self) catch |err| switch (err) {
2058 error.TooManyPersonalities => return self.reportUnexpectedError(
2059 "too many personalities in unwind info",
2060 .{},
2061 ),
2051 error.TooManyPersonalities => return diags.fail("too many personalities in unwind info", .{}),
20622052 else => |e| return e,
20632053 };
20642054 sect.size = self.unwind_info.calcSize();
......@@ -2427,6 +2417,7 @@ fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void {
24272417 defer tracy.end();
24282418
24292419 const gpa = self.base.comp.gpa;
2420 const diags = &self.base.comp.link_diags;
24302421
24312422 const cmd = self.symtab_cmd;
24322423 try self.symtab.resize(gpa, cmd.nsyms);
......@@ -2495,7 +2486,7 @@ fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void {
24952486 };
24962487 }
24972488
2498 if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
2489 if (diags.hasErrors()) return error.LinkFailure;
24992490}
25002491
25012492fn writeAtomsWorker(self: *MachO, file: File) void {
......@@ -2505,13 +2496,15 @@ fn writeAtomsWorker(self: *MachO, file: File) void {
25052496 self.reportParseError2(file.getIndex(), "failed to resolve relocations and write atoms: {s}", .{
25062497 @errorName(err),
25072498 }) catch {};
2508 _ = self.has_errors.swap(true, .seq_cst);
25092499 };
25102500}
25112501
25122502fn writeThunkWorker(self: *MachO, thunk: Thunk) void {
25132503 const tracy = trace(@src());
25142504 defer tracy.end();
2505
2506 const diags = &self.base.comp.link_diags;
2507
25152508 const doWork = struct {
25162509 fn doWork(th: Thunk, buffer: []u8, macho_file: *MachO) !void {
25172510 const off = math.cast(usize, th.value) orelse return error.Overflow;
......@@ -2522,8 +2515,7 @@ fn writeThunkWorker(self: *MachO, thunk: Thunk) void {
25222515 }.doWork;
25232516 const out = self.sections.items(.out)[thunk.out_n_sect].items;
25242517 doWork(thunk, out, self) catch |err| {
2525 self.reportUnexpectedError("failed to write contents of thunk: {s}", .{@errorName(err)}) catch {};
2526 _ = self.has_errors.swap(true, .seq_cst);
2518 diags.addError("failed to write contents of thunk: {s}", .{@errorName(err)});
25272519 };
25282520}
25292521
......@@ -2531,6 +2523,8 @@ fn writeSyntheticSectionWorker(self: *MachO, sect_id: u8, out: []u8) void {
25312523 const tracy = trace(@src());
25322524 defer tracy.end();
25332525
2526 const diags = &self.base.comp.link_diags;
2527
25342528 const Tag = enum {
25352529 eh_frame,
25362530 unwind_info,
......@@ -2575,18 +2569,18 @@ fn writeSyntheticSectionWorker(self: *MachO, sect_id: u8, out: []u8) void {
25752569 unreachable;
25762570 };
25772571 doWork(self, tag, out) catch |err| {
2578 self.reportUnexpectedError("could not write section '{s},{s}': {s}", .{
2579 header.segName(),
2580 header.sectName(),
2581 @errorName(err),
2582 }) catch {};
2583 _ = self.has_errors.swap(true, .seq_cst);
2572 diags.addError("could not write section '{s},{s}': {s}", .{
2573 header.segName(), header.sectName(), @errorName(err),
2574 });
25842575 };
25852576}
25862577
25872578fn updateLazyBindSizeWorker(self: *MachO) void {
25882579 const tracy = trace(@src());
25892580 defer tracy.end();
2581
2582 const diags = &self.base.comp.link_diags;
2583
25902584 const doWork = struct {
25912585 fn doWork(macho_file: *MachO) !void {
25922586 try macho_file.lazy_bind_section.updateSize(macho_file);
......@@ -2596,12 +2590,8 @@ fn updateLazyBindSizeWorker(self: *MachO) void {
25962590 try macho_file.stubs_helper.write(macho_file, stream.writer());
25972591 }
25982592 }.doWork;
2599 doWork(self) catch |err| {
2600 self.reportUnexpectedError("could not calculate size of lazy binding section: {s}", .{
2601 @errorName(err),
2602 }) catch {};
2603 _ = self.has_errors.swap(true, .seq_cst);
2604 };
2593 doWork(self) catch |err|
2594 diags.addError("could not calculate size of lazy binding section: {s}", .{@errorName(err)});
26052595}
26062596
26072597pub fn updateLinkeditSizeWorker(self: *MachO, tag: enum {
......@@ -2611,6 +2601,7 @@ pub fn updateLinkeditSizeWorker(self: *MachO, tag: enum {
26112601 export_trie,
26122602 data_in_code,
26132603}) void {
2604 const diags = &self.base.comp.link_diags;
26142605 const res = switch (tag) {
26152606 .rebase => self.rebase_section.updateSize(self),
26162607 .bind => self.bind_section.updateSize(self),
......@@ -2618,13 +2609,8 @@ pub fn updateLinkeditSizeWorker(self: *MachO, tag: enum {
26182609 .export_trie => self.export_trie.updateSize(self),
26192610 .data_in_code => self.data_in_code.updateSize(self),
26202611 };
2621 res catch |err| {
2622 self.reportUnexpectedError("could not calculate size of {s} section: {s}", .{
2623 @tagName(tag),
2624 @errorName(err),
2625 }) catch {};
2626 _ = self.has_errors.swap(true, .seq_cst);
2627 };
2612 res catch |err|
2613 diags.addError("could not calculate size of {s} section: {s}", .{ @tagName(tag), @errorName(err) });
26282614}
26292615
26302616fn writeSectionsToFile(self: *MachO) !void {
......@@ -3432,6 +3418,7 @@ pub fn growSection(self: *MachO, sect_index: u8, needed_size: u64) !void {
34323418}
34333419
34343420fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !void {
3421 const diags = &self.base.comp.link_diags;
34353422 const sect = &self.sections.items(.header)[sect_index];
34363423
34373424 const seg_id = self.sections.items(.segment_id)[sect_index];
......@@ -3467,7 +3454,7 @@ fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !vo
34673454
34683455 const mem_capacity = self.allocatedSizeVirtual(seg.vmaddr);
34693456 if (needed_size > mem_capacity) {
3470 var err = try self.base.addErrorWithNotes(2);
3457 var err = try diags.addErrorWithNotes(2);
34713458 try err.addMsg("fatal linker error: cannot expand segment seg({d})({s}) in virtual memory", .{
34723459 seg_id,
34733460 seg.segName(),
......@@ -3766,41 +3753,18 @@ pub fn eatPrefix(path: []const u8, prefix: []const u8) ?[]const u8 {
37663753 return null;
37673754}
37683755
3769pub fn reportParseError(
3770 self: *MachO,
3771 path: Path,
3772 comptime format: []const u8,
3773 args: anytype,
3774) error{OutOfMemory}!void {
3775 var err = try self.base.addErrorWithNotes(1);
3776 try err.addMsg(format, args);
3777 try err.addNote("while parsing {}", .{path});
3778}
3779
37803756pub fn reportParseError2(
37813757 self: *MachO,
37823758 file_index: File.Index,
37833759 comptime format: []const u8,
37843760 args: anytype,
37853761) error{OutOfMemory}!void {
3786 var err = try self.base.addErrorWithNotes(1);
3762 const diags = &self.base.comp.link_diags;
3763 var err = try diags.addErrorWithNotes(1);
37873764 try err.addMsg(format, args);
37883765 try err.addNote("while parsing {}", .{self.getFile(file_index).?.fmtPath()});
37893766}
37903767
3791fn reportMissingLibraryError(
3792 self: *MachO,
3793 checked_paths: []const []const u8,
3794 comptime format: []const u8,
3795 args: anytype,
3796) error{OutOfMemory}!void {
3797 var err = try self.base.addErrorWithNotes(checked_paths.len);
3798 try err.addMsg(format, args);
3799 for (checked_paths) |path| {
3800 try err.addNote("tried {s}", .{path});
3801 }
3802}
3803
38043768fn reportMissingDependencyError(
38053769 self: *MachO,
38063770 parent: File.Index,
......@@ -3809,7 +3773,8 @@ fn reportMissingDependencyError(
38093773 comptime format: []const u8,
38103774 args: anytype,
38113775) error{OutOfMemory}!void {
3812 var err = try self.base.addErrorWithNotes(2 + checked_paths.len);
3776 const diags = &self.base.comp.link_diags;
3777 var err = try diags.addErrorWithNotes(2 + checked_paths.len);
38133778 try err.addMsg(format, args);
38143779 try err.addNote("while resolving {s}", .{path});
38153780 try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()});
......@@ -3825,18 +3790,13 @@ fn reportDependencyError(
38253790 comptime format: []const u8,
38263791 args: anytype,
38273792) error{OutOfMemory}!void {
3828 var err = try self.base.addErrorWithNotes(2);
3793 const diags = &self.base.comp.link_diags;
3794 var err = try diags.addErrorWithNotes(2);
38293795 try err.addMsg(format, args);
38303796 try err.addNote("while parsing {s}", .{path});
38313797 try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()});
38323798}
38333799
3834pub fn reportUnexpectedError(self: *MachO, comptime format: []const u8, args: anytype) error{OutOfMemory}!void {
3835 var err = try self.base.addErrorWithNotes(1);
3836 try err.addMsg(format, args);
3837 try err.addNote("please report this as a linker bug on https://github.com/ziglang/zig/issues/new/choose", .{});
3838}
3839
38403800fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
38413801 const tracy = trace(@src());
38423802 defer tracy.end();
......@@ -3844,6 +3804,7 @@ fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
38443804 if (self.dupes.keys().len == 0) return; // Nothing to do
38453805
38463806 const gpa = self.base.comp.gpa;
3807 const diags = &self.base.comp.link_diags;
38473808 const max_notes = 3;
38483809
38493810 // We will sort by name, and then by file to ensure deterministic output.
......@@ -3861,7 +3822,7 @@ fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
38613822 const notes = self.dupes.get(key).?;
38623823 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
38633824
3864 var err = try self.base.addErrorWithNotes(nnotes + 1);
3825 var err = try diags.addErrorWithNotes(nnotes + 1);
38653826 try err.addMsg("duplicate symbol definition: {s}", .{sym.getName(self)});
38663827 try err.addNote("defined by {}", .{sym.getFile(self).?.fmtPath()});
38673828
src/link/MachO/Archive.zig+2-1
......@@ -6,6 +6,7 @@ pub fn deinit(self: *Archive, allocator: Allocator) void {
66
77pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File.HandleIndex, fat_arch: ?fat.Arch) !void {
88 const gpa = macho_file.base.comp.gpa;
9 const diags = &macho_file.base.comp.link_diags;
910
1011 var arena = std.heap.ArenaAllocator.init(gpa);
1112 defer arena.deinit();
......@@ -28,7 +29,7 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File
2829 pos += @sizeOf(ar_hdr);
2930
3031 if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) {
31 try macho_file.reportParseError(path, "invalid header delimiter: expected '{s}', found '{s}'", .{
32 try diags.reportParseError(path, "invalid header delimiter: expected '{s}', found '{s}'", .{
3233 std.fmt.fmtSliceEscapeLower(ARFMAG), std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag),
3334 });
3435 return error.MalformedArchive;
src/link/MachO/Atom.zig+2-1
......@@ -893,6 +893,7 @@ fn resolveRelocInner(
893893const x86_64 = struct {
894894 fn relaxGotLoad(self: Atom, code: []u8, rel: Relocation, macho_file: *MachO) ResolveError!void {
895895 dev.check(.x86_64_backend);
896 const diags = &macho_file.base.comp.link_diags;
896897 const old_inst = disassemble(code) orelse return error.RelaxFail;
897898 switch (old_inst.encoding.mnemonic) {
898899 .mov => {
......@@ -901,7 +902,7 @@ const x86_64 = struct {
901902 encode(&.{inst}, code) catch return error.RelaxFail;
902903 },
903904 else => |x| {
904 var err = try macho_file.base.addErrorWithNotes(2);
905 var err = try diags.addErrorWithNotes(2);
905906 try err.addMsg("{s}: 0x{x}: 0x{x}: failed to relax relocation of type {}", .{
906907 self.getName(macho_file),
907908 self.getAddress(macho_file),
src/link/MachO/ZigObject.zig+12-29
......@@ -364,6 +364,8 @@ pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void {
364364
365365pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void {
366366 const gpa = macho_file.base.comp.gpa;
367 const diags = &macho_file.base.comp.link_diags;
368
367369 var has_error = false;
368370 for (self.getAtoms()) |atom_index| {
369371 const atom = self.getAtom(atom_index) orelse continue;
......@@ -379,17 +381,12 @@ pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void {
379381 defer gpa.free(code);
380382 self.getAtomData(macho_file, atom.*, code) catch |err| {
381383 switch (err) {
382 error.InputOutput => {
383 try macho_file.reportUnexpectedError("fetching code for '{s}' failed", .{
384 atom.getName(macho_file),
385 });
386 },
387 else => |e| {
388 try macho_file.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{
389 atom.getName(macho_file),
390 @errorName(e),
391 });
392 },
384 error.InputOutput => return diags.fail("fetching code for '{s}' failed", .{
385 atom.getName(macho_file),
386 }),
387 else => |e| return diags.fail("failed to fetch code for '{s}': {s}", .{
388 atom.getName(macho_file), @errorName(e),
389 }),
393390 }
394391 has_error = true;
395392 continue;
......@@ -398,9 +395,7 @@ pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void {
398395 atom.resolveRelocs(macho_file, code) catch |err| {
399396 switch (err) {
400397 error.ResolveFailed => {},
401 else => |e| {
402 try macho_file.reportUnexpectedError("unexpected error while resolving relocations: {s}", .{@errorName(e)});
403 },
398 else => |e| return diags.fail("failed to resolve relocations: {s}", .{@errorName(e)}),
404399 }
405400 has_error = true;
406401 continue;
......@@ -426,6 +421,7 @@ pub fn calcNumRelocs(self: *ZigObject, macho_file: *MachO) void {
426421
427422pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) !void {
428423 const gpa = macho_file.base.comp.gpa;
424 const diags = &macho_file.base.comp.link_diags;
429425
430426 for (self.getAtoms()) |atom_index| {
431427 const atom = self.getAtom(atom_index) orelse continue;
......@@ -439,21 +435,8 @@ pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) !void {
439435 const atom_size = std.math.cast(usize, atom.size) orelse return error.Overflow;
440436 const code = try gpa.alloc(u8, atom_size);
441437 defer gpa.free(code);
442 self.getAtomData(macho_file, atom.*, code) catch |err| switch (err) {
443 error.InputOutput => {
444 try macho_file.reportUnexpectedError("fetching code for '{s}' failed", .{
445 atom.getName(macho_file),
446 });
447 return error.FlushFailure;
448 },
449 else => |e| {
450 try macho_file.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{
451 atom.getName(macho_file),
452 @errorName(e),
453 });
454 return error.FlushFailure;
455 },
456 };
438 self.getAtomData(macho_file, atom.*, code) catch |err|
439 return diags.fail("failed to fetch code for '{s}': {s}", .{ atom.getName(macho_file), @errorName(err) });
457440 const file_offset = header.offset + atom.value;
458441 try atom.writeRelocs(macho_file, code, relocs[extra.rel_out_index..][0..extra.rel_out_count]);
459442 try macho_file.base.file.?.pwriteAll(code, file_offset);
src/link/MachO/relocatable.zig+28-30
......@@ -1,5 +1,6 @@
11pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void {
22 const gpa = macho_file.base.comp.gpa;
3 const diags = &macho_file.base.comp.link_diags;
34
45 // TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list.
56 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
......@@ -29,8 +30,8 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
2930
3031 for (positionals.items) |obj| {
3132 macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
32 error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an input file", .{}),
33 else => |e| try macho_file.reportParseError(
33 error.UnknownFileType => try diags.reportParseError(obj.path, "unknown file type for an input file", .{}),
34 else => |e| try diags.reportParseError(
3435 obj.path,
3536 "unexpected error: reading input file failed with error {s}",
3637 .{@errorName(e)},
......@@ -38,11 +39,11 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
3839 };
3940 }
4041
41 if (macho_file.base.hasErrors()) return error.FlushFailure;
42 if (diags.hasErrors()) return error.FlushFailure;
4243
4344 try macho_file.parseInputFiles();
4445
45 if (macho_file.base.hasErrors()) return error.FlushFailure;
46 if (diags.hasErrors()) return error.FlushFailure;
4647
4748 try macho_file.resolveSymbols();
4849 try macho_file.dedupLiterals();
......@@ -75,6 +76,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat
7576
7677pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void {
7778 const gpa = comp.gpa;
79 const diags = &macho_file.base.comp.link_diags;
7880
7981 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
8082 defer positionals.deinit();
......@@ -94,8 +96,8 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
9496
9597 for (positionals.items) |obj| {
9698 macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
97 error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an input file", .{}),
98 else => |e| try macho_file.reportParseError(
99 error.UnknownFileType => try diags.reportParseError(obj.path, "unknown file type for an input file", .{}),
100 else => |e| try diags.reportParseError(
99101 obj.path,
100102 "unexpected error: reading input file failed with error {s}",
101103 .{@errorName(e)},
......@@ -103,11 +105,11 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
103105 };
104106 }
105107
106 if (macho_file.base.hasErrors()) return error.FlushFailure;
108 if (diags.hasErrors()) return error.FlushFailure;
107109
108110 try parseInputFilesAr(macho_file);
109111
110 if (macho_file.base.hasErrors()) return error.FlushFailure;
112 if (diags.hasErrors()) return error.FlushFailure;
111113
112114 // First, we flush relocatable object file generated with our backends.
113115 if (macho_file.getZigObject()) |zo| {
......@@ -228,7 +230,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
228230 try macho_file.base.file.?.setEndPos(total_size);
229231 try macho_file.base.file.?.pwriteAll(buffer.items, 0);
230232
231 if (macho_file.base.hasErrors()) return error.FlushFailure;
233 if (diags.hasErrors()) return error.FlushFailure;
232234}
233235
234236fn parseInputFilesAr(macho_file: *MachO) !void {
......@@ -293,6 +295,8 @@ fn calcSectionSizes(macho_file: *MachO) !void {
293295 const tracy = trace(@src());
294296 defer tracy.end();
295297
298 const diags = &macho_file.base.comp.link_diags;
299
296300 if (macho_file.getZigObject()) |zo| {
297301 // TODO this will create a race as we need to track merging of debug sections which we currently don't
298302 zo.calcNumRelocs(macho_file);
......@@ -337,7 +341,7 @@ fn calcSectionSizes(macho_file: *MachO) !void {
337341 }
338342 try calcSymtabSize(macho_file);
339343
340 if (macho_file.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
344 if (diags.hasErrors()) return error.LinkFailure;
341345}
342346
343347fn calcSectionSizeWorker(macho_file: *MachO, sect_id: u8) void {
......@@ -365,6 +369,8 @@ fn calcEhFrameSizeWorker(macho_file: *MachO) void {
365369 const tracy = trace(@src());
366370 defer tracy.end();
367371
372 const diags = &macho_file.base.comp.link_diags;
373
368374 const doWork = struct {
369375 fn doWork(mfile: *MachO, header: *macho.section_64) !void {
370376 header.size = try eh_frame.calcSize(mfile);
......@@ -374,12 +380,8 @@ fn calcEhFrameSizeWorker(macho_file: *MachO) void {
374380 }.doWork;
375381
376382 const header = &macho_file.sections.items(.header)[macho_file.eh_frame_sect_index.?];
377 doWork(macho_file, header) catch |err| {
378 macho_file.reportUnexpectedError("failed to calculate size of section '__TEXT,__eh_frame': {s}", .{
379 @errorName(err),
380 }) catch {};
381 _ = macho_file.has_errors.swap(true, .seq_cst);
382 };
383 doWork(macho_file, header) catch |err|
384 diags.addError("failed to calculate size of section '__TEXT,__eh_frame': {s}", .{@errorName(err)});
383385}
384386
385387fn calcCompactUnwindSize(macho_file: *MachO) void {
......@@ -592,6 +594,7 @@ fn writeSections(macho_file: *MachO) !void {
592594 defer tracy.end();
593595
594596 const gpa = macho_file.base.comp.gpa;
597 const diags = &macho_file.base.comp.link_diags;
595598 const cpu_arch = macho_file.getTarget().cpu.arch;
596599 const slice = macho_file.sections.slice();
597600 for (slice.items(.header), slice.items(.out), slice.items(.relocs), 0..) |header, *out, *relocs, n_sect| {
......@@ -637,7 +640,7 @@ fn writeSections(macho_file: *MachO) !void {
637640 }
638641 }
639642
640 if (macho_file.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
643 if (diags.hasErrors()) return error.LinkFailure;
641644
642645 if (macho_file.getZigObject()) |zo| {
643646 try zo.writeRelocs(macho_file);
......@@ -651,33 +654,28 @@ fn writeAtomsWorker(macho_file: *MachO, file: File) void {
651654 macho_file.reportParseError2(file.getIndex(), "failed to write atoms: {s}", .{
652655 @errorName(err),
653656 }) catch {};
654 _ = macho_file.has_errors.swap(true, .seq_cst);
655657 };
656658}
657659
658660fn writeEhFrameWorker(macho_file: *MachO) void {
659661 const tracy = trace(@src());
660662 defer tracy.end();
663
664 const diags = &macho_file.base.comp.link_diags;
661665 const sect_index = macho_file.eh_frame_sect_index.?;
662666 const buffer = macho_file.sections.items(.out)[sect_index];
663667 const relocs = macho_file.sections.items(.relocs)[sect_index];
664 eh_frame.writeRelocs(macho_file, buffer.items, relocs.items) catch |err| {
665 macho_file.reportUnexpectedError("failed to write '__LD,__eh_frame' section: {s}", .{
666 @errorName(err),
667 }) catch {};
668 _ = macho_file.has_errors.swap(true, .seq_cst);
669 };
668 eh_frame.writeRelocs(macho_file, buffer.items, relocs.items) catch |err|
669 diags.addError("failed to write '__LD,__eh_frame' section: {s}", .{@errorName(err)});
670670}
671671
672672fn writeCompactUnwindWorker(macho_file: *MachO, object: *Object) void {
673673 const tracy = trace(@src());
674674 defer tracy.end();
675 object.writeCompactUnwindRelocatable(macho_file) catch |err| {
676 macho_file.reportUnexpectedError("failed to write '__LD,__eh_frame' section: {s}", .{
677 @errorName(err),
678 }) catch {};
679 _ = macho_file.has_errors.swap(true, .seq_cst);
680 };
675
676 const diags = &macho_file.base.comp.link_diags;
677 object.writeCompactUnwindRelocatable(macho_file) catch |err|
678 diags.addError("failed to write '__LD,__eh_frame' section: {s}", .{@errorName(err)});
681679}
682680
683681fn writeSectionsToFile(macho_file: *MachO) !void {
src/link/Wasm.zig+51-36
......@@ -649,6 +649,8 @@ fn parseInputFiles(wasm: *Wasm, files: []const []const u8) !void {
649649/// file and parsed successfully. Returns false when file is not an object file.
650650/// May return an error instead when parsing failed.
651651fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {
652 const diags = &wasm.base.comp.link_diags;
653
652654 const obj_file = try fs.cwd().openFile(path, .{});
653655 errdefer obj_file.close();
654656
......@@ -656,7 +658,7 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {
656658 var object = Object.create(wasm, obj_file, path, null) catch |err| switch (err) {
657659 error.InvalidMagicByte, error.NotObjectFile => return false,
658660 else => |e| {
659 var err_note = try wasm.base.addErrorWithNotes(1);
661 var err_note = try diags.addErrorWithNotes(1);
660662 try err_note.addMsg("Failed parsing object file: {s}", .{@errorName(e)});
661663 try err_note.addNote("while parsing '{s}'", .{path});
662664 return error.FlushFailure;
......@@ -698,6 +700,7 @@ pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom {
698700/// are referenced by other object files or Zig code.
699701fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
700702 const gpa = wasm.base.comp.gpa;
703 const diags = &wasm.base.comp.link_diags;
701704
702705 const archive_file = try fs.cwd().openFile(path, .{});
703706 errdefer archive_file.close();
......@@ -712,7 +715,7 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
712715 return false;
713716 },
714717 else => |e| {
715 var err_note = try wasm.base.addErrorWithNotes(1);
718 var err_note = try diags.addErrorWithNotes(1);
716719 try err_note.addMsg("Failed parsing archive: {s}", .{@errorName(e)});
717720 try err_note.addNote("while parsing archive {s}", .{path});
718721 return error.FlushFailure;
......@@ -739,7 +742,7 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
739742
740743 for (offsets.keys()) |file_offset| {
741744 var object = archive.parseObject(wasm, file_offset) catch |e| {
742 var err_note = try wasm.base.addErrorWithNotes(1);
745 var err_note = try diags.addErrorWithNotes(1);
743746 try err_note.addMsg("Failed parsing object: {s}", .{@errorName(e)});
744747 try err_note.addNote("while parsing object in archive {s}", .{path});
745748 return error.FlushFailure;
......@@ -763,6 +766,7 @@ fn requiresTLSReloc(wasm: *const Wasm) bool {
763766
764767fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
765768 const gpa = wasm.base.comp.gpa;
769 const diags = &wasm.base.comp.link_diags;
766770 const obj_file = wasm.file(file_index).?;
767771 log.debug("Resolving symbols in object: '{s}'", .{obj_file.path()});
768772
......@@ -777,7 +781,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
777781
778782 if (symbol.isLocal()) {
779783 if (symbol.isUndefined()) {
780 var err = try wasm.base.addErrorWithNotes(1);
784 var err = try diags.addErrorWithNotes(1);
781785 try err.addMsg("Local symbols are not allowed to reference imports", .{});
782786 try err.addNote("symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() });
783787 }
......@@ -814,7 +818,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
814818 break :outer; // existing is weak, while new one isn't. Replace it.
815819 }
816820 // both are defined and weak, we have a symbol collision.
817 var err = try wasm.base.addErrorWithNotes(2);
821 var err = try diags.addErrorWithNotes(2);
818822 try err.addMsg("symbol '{s}' defined multiple times", .{sym_name});
819823 try err.addNote("first definition in '{s}'", .{existing_file_path});
820824 try err.addNote("next definition in '{s}'", .{obj_file.path()});
......@@ -825,7 +829,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
825829 }
826830
827831 if (symbol.tag != existing_sym.tag) {
828 var err = try wasm.base.addErrorWithNotes(2);
832 var err = try diags.addErrorWithNotes(2);
829833 try err.addMsg("symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) });
830834 try err.addNote("first definition in '{s}'", .{existing_file_path});
831835 try err.addNote("next definition in '{s}'", .{obj_file.path()});
......@@ -845,7 +849,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
845849 const imp = obj_file.import(sym_index);
846850 const module_name = obj_file.string(imp.module_name);
847851 if (!mem.eql(u8, existing_name, module_name)) {
848 var err = try wasm.base.addErrorWithNotes(2);
852 var err = try diags.addErrorWithNotes(2);
849853 try err.addMsg("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{
850854 sym_name,
851855 existing_name,
......@@ -865,7 +869,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
865869 const existing_ty = wasm.getGlobalType(existing_loc);
866870 const new_ty = wasm.getGlobalType(location);
867871 if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) {
868 var err = try wasm.base.addErrorWithNotes(2);
872 var err = try diags.addErrorWithNotes(2);
869873 try err.addMsg("symbol '{s}' mismatching global types", .{sym_name});
870874 try err.addNote("first definition in '{s}'", .{existing_file_path});
871875 try err.addNote("next definition in '{s}'", .{obj_file.path()});
......@@ -876,7 +880,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
876880 const existing_ty = wasm.getFunctionSignature(existing_loc);
877881 const new_ty = wasm.getFunctionSignature(location);
878882 if (!existing_ty.eql(new_ty)) {
879 var err = try wasm.base.addErrorWithNotes(3);
883 var err = try diags.addErrorWithNotes(3);
880884 try err.addMsg("symbol '{s}' mismatching function signatures.", .{sym_name});
881885 try err.addNote("expected signature {}, but found signature {}", .{ existing_ty, new_ty });
882886 try err.addNote("first definition in '{s}'", .{existing_file_path});
......@@ -909,6 +913,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
909913
910914fn resolveSymbolsInArchives(wasm: *Wasm) !void {
911915 const gpa = wasm.base.comp.gpa;
916 const diags = &wasm.base.comp.link_diags;
912917 if (wasm.archives.items.len == 0) return;
913918
914919 log.debug("Resolving symbols in archives", .{});
......@@ -928,7 +933,7 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
928933 // Parse object and and resolve symbols again before we check remaining
929934 // undefined symbols.
930935 var object = archive.parseObject(wasm, offset.items[0]) catch |e| {
931 var err_note = try wasm.base.addErrorWithNotes(1);
936 var err_note = try diags.addErrorWithNotes(1);
932937 try err_note.addMsg("Failed parsing object: {s}", .{@errorName(e)});
933938 try err_note.addNote("while parsing object in archive {s}", .{archive.name});
934939 return error.FlushFailure;
......@@ -1172,6 +1177,7 @@ fn validateFeatures(
11721177 emit_features_count: *u32,
11731178) !void {
11741179 const comp = wasm.base.comp;
1180 const diags = &wasm.base.comp.link_diags;
11751181 const target = comp.root_mod.resolved_target.result;
11761182 const shared_memory = comp.config.shared_memory;
11771183 const cpu_features = target.cpu.features;
......@@ -1235,7 +1241,7 @@ fn validateFeatures(
12351241 allowed[used_index] = is_enabled;
12361242 emit_features_count.* += @intFromBool(is_enabled);
12371243 } else if (is_enabled and !allowed[used_index]) {
1238 var err = try wasm.base.addErrorWithNotes(1);
1244 var err = try diags.addErrorWithNotes(1);
12391245 try err.addMsg("feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))});
12401246 try err.addNote("defined in '{s}'", .{wasm.files.items(.data)[used_set >> 1].object.path});
12411247 valid_feature_set = false;
......@@ -1249,7 +1255,7 @@ fn validateFeatures(
12491255 if (shared_memory) {
12501256 const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)];
12511257 if (@as(u1, @truncate(disallowed_feature)) != 0) {
1252 var err = try wasm.base.addErrorWithNotes(0);
1258 var err = try diags.addErrorWithNotes(0);
12531259 try err.addMsg(
12541260 "shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled",
12551261 .{wasm.files.items(.data)[disallowed_feature >> 1].object.path},
......@@ -1259,7 +1265,7 @@ fn validateFeatures(
12591265
12601266 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
12611267 if (!allowed[@intFromEnum(feature)]) {
1262 var err = try wasm.base.addErrorWithNotes(0);
1268 var err = try diags.addErrorWithNotes(0);
12631269 try err.addMsg("feature '{}' is not used but is required for shared-memory", .{feature});
12641270 }
12651271 }
......@@ -1268,7 +1274,7 @@ fn validateFeatures(
12681274 if (has_tls) {
12691275 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
12701276 if (!allowed[@intFromEnum(feature)]) {
1271 var err = try wasm.base.addErrorWithNotes(0);
1277 var err = try diags.addErrorWithNotes(0);
12721278 try err.addMsg("feature '{}' is not used but is required for thread-local storage", .{feature});
12731279 }
12741280 }
......@@ -1282,7 +1288,7 @@ fn validateFeatures(
12821288 // from here a feature is always used
12831289 const disallowed_feature = disallowed[@intFromEnum(feature.tag)];
12841290 if (@as(u1, @truncate(disallowed_feature)) != 0) {
1285 var err = try wasm.base.addErrorWithNotes(2);
1291 var err = try diags.addErrorWithNotes(2);
12861292 try err.addMsg("feature '{}' is disallowed, but used by linked object", .{feature.tag});
12871293 try err.addNote("disallowed by '{s}'", .{wasm.files.items(.data)[disallowed_feature >> 1].object.path});
12881294 try err.addNote("used in '{s}'", .{object.path});
......@@ -1296,7 +1302,7 @@ fn validateFeatures(
12961302 for (required, 0..) |required_feature, feature_index| {
12971303 const is_required = @as(u1, @truncate(required_feature)) != 0;
12981304 if (is_required and !object_used_features[feature_index]) {
1299 var err = try wasm.base.addErrorWithNotes(2);
1305 var err = try diags.addErrorWithNotes(2);
13001306 try err.addMsg("feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))});
13011307 try err.addNote("required by '{s}'", .{wasm.files.items(.data)[required_feature >> 1].object.path});
13021308 try err.addNote("missing in '{s}'", .{object.path});
......@@ -1364,6 +1370,7 @@ pub fn findGlobalSymbol(wasm: *Wasm, name: []const u8) ?SymbolLoc {
13641370
13651371fn checkUndefinedSymbols(wasm: *const Wasm) !void {
13661372 const comp = wasm.base.comp;
1373 const diags = &wasm.base.comp.link_diags;
13671374 if (comp.config.output_mode == .Obj) return;
13681375 if (wasm.import_symbols) return;
13691376
......@@ -1377,7 +1384,7 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void {
13771384 else
13781385 wasm.name;
13791386 const symbol_name = undef.getName(wasm);
1380 var err = try wasm.base.addErrorWithNotes(1);
1387 var err = try diags.addErrorWithNotes(1);
13811388 try err.addMsg("could not resolve undefined symbol '{s}'", .{symbol_name});
13821389 try err.addNote("defined in '{s}'", .{file_name});
13831390 }
......@@ -1736,6 +1743,7 @@ fn sortDataSegments(wasm: *Wasm) !void {
17361743/// contain any parameters.
17371744fn setupInitFunctions(wasm: *Wasm) !void {
17381745 const gpa = wasm.base.comp.gpa;
1746 const diags = &wasm.base.comp.link_diags;
17391747 // There's no constructors for Zig so we can simply search through linked object files only.
17401748 for (wasm.objects.items) |file_index| {
17411749 const object: Object = wasm.files.items(.data)[@intFromEnum(file_index)].object;
......@@ -1751,7 +1759,7 @@ fn setupInitFunctions(wasm: *Wasm) !void {
17511759 break :ty object.func_types[func.type_index];
17521760 };
17531761 if (ty.params.len != 0) {
1754 var err = try wasm.base.addErrorWithNotes(0);
1762 var err = try diags.addErrorWithNotes(0);
17551763 try err.addMsg("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)});
17561764 }
17571765 log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)});
......@@ -2130,12 +2138,13 @@ fn mergeTypes(wasm: *Wasm) !void {
21302138
21312139fn checkExportNames(wasm: *Wasm) !void {
21322140 const force_exp_names = wasm.export_symbol_names;
2141 const diags = &wasm.base.comp.link_diags;
21332142 if (force_exp_names.len > 0) {
21342143 var failed_exports = false;
21352144
21362145 for (force_exp_names) |exp_name| {
21372146 const loc = wasm.findGlobalSymbol(exp_name) orelse {
2138 var err = try wasm.base.addErrorWithNotes(0);
2147 var err = try diags.addErrorWithNotes(0);
21392148 try err.addMsg("could not export '{s}', symbol not found", .{exp_name});
21402149 failed_exports = true;
21412150 continue;
......@@ -2195,18 +2204,19 @@ fn setupExports(wasm: *Wasm) !void {
21952204
21962205fn setupStart(wasm: *Wasm) !void {
21972206 const comp = wasm.base.comp;
2207 const diags = &wasm.base.comp.link_diags;
21982208 // do not export entry point if user set none or no default was set.
21992209 const entry_name = wasm.entry_name orelse return;
22002210
22012211 const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {
2202 var err = try wasm.base.addErrorWithNotes(0);
2212 var err = try diags.addErrorWithNotes(0);
22032213 try err.addMsg("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});
22042214 return error.FlushFailure;
22052215 };
22062216
22072217 const symbol = symbol_loc.getSymbol(wasm);
22082218 if (symbol.tag != .function) {
2209 var err = try wasm.base.addErrorWithNotes(0);
2219 var err = try diags.addErrorWithNotes(0);
22102220 try err.addMsg("Entry symbol '{s}' is not a function", .{entry_name});
22112221 return error.FlushFailure;
22122222 }
......@@ -2220,6 +2230,7 @@ fn setupStart(wasm: *Wasm) !void {
22202230/// Sets up the memory section of the wasm module, as well as the stack.
22212231fn setupMemory(wasm: *Wasm) !void {
22222232 const comp = wasm.base.comp;
2233 const diags = &wasm.base.comp.link_diags;
22232234 const shared_memory = comp.config.shared_memory;
22242235 log.debug("Setting up memory layout", .{});
22252236 const page_size = std.wasm.page_size; // 64kb
......@@ -2312,15 +2323,15 @@ fn setupMemory(wasm: *Wasm) !void {
23122323
23132324 if (wasm.initial_memory) |initial_memory| {
23142325 if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) {
2315 var err = try wasm.base.addErrorWithNotes(0);
2326 var err = try diags.addErrorWithNotes(0);
23162327 try err.addMsg("Initial memory must be {d}-byte aligned", .{page_size});
23172328 }
23182329 if (memory_ptr > initial_memory) {
2319 var err = try wasm.base.addErrorWithNotes(0);
2330 var err = try diags.addErrorWithNotes(0);
23202331 try err.addMsg("Initial memory too small, must be at least {d} bytes", .{memory_ptr});
23212332 }
23222333 if (initial_memory > max_memory_allowed) {
2323 var err = try wasm.base.addErrorWithNotes(0);
2334 var err = try diags.addErrorWithNotes(0);
23242335 try err.addMsg("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});
23252336 }
23262337 memory_ptr = initial_memory;
......@@ -2338,15 +2349,15 @@ fn setupMemory(wasm: *Wasm) !void {
23382349
23392350 if (wasm.max_memory) |max_memory| {
23402351 if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) {
2341 var err = try wasm.base.addErrorWithNotes(0);
2352 var err = try diags.addErrorWithNotes(0);
23422353 try err.addMsg("Maximum memory must be {d}-byte aligned", .{page_size});
23432354 }
23442355 if (memory_ptr > max_memory) {
2345 var err = try wasm.base.addErrorWithNotes(0);
2356 var err = try diags.addErrorWithNotes(0);
23462357 try err.addMsg("Maximum memory too small, must be at least {d} bytes", .{memory_ptr});
23472358 }
23482359 if (max_memory > max_memory_allowed) {
2349 var err = try wasm.base.addErrorWithNotes(0);
2360 var err = try diags.addErrorWithNotes(0);
23502361 try err.addMsg("Maximum memory exceeds maximum amount {d}", .{max_memory_allowed});
23512362 }
23522363 wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size));
......@@ -2364,6 +2375,7 @@ fn setupMemory(wasm: *Wasm) !void {
23642375pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: Symbol.Index) !u32 {
23652376 const comp = wasm.base.comp;
23662377 const gpa = comp.gpa;
2378 const diags = &wasm.base.comp.link_diags;
23672379 const obj_file = wasm.file(file_index).?;
23682380 const symbol = obj_file.symbols()[@intFromEnum(symbol_index)];
23692381 const index: u32 = @intCast(wasm.segments.items.len);
......@@ -2450,7 +2462,7 @@ pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: Sym
24502462 break :blk index;
24512463 };
24522464 } else {
2453 var err = try wasm.base.addErrorWithNotes(1);
2465 var err = try diags.addErrorWithNotes(1);
24542466 try err.addMsg("found unknown section '{s}'", .{section_name});
24552467 try err.addNote("defined in '{s}'", .{obj_file.path()});
24562468 return error.UnexpectedValue;
......@@ -2487,6 +2499,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
24872499 defer tracy.end();
24882500
24892501 const comp = wasm.base.comp;
2502 const diags = &comp.link_diags;
24902503 if (wasm.llvm_object) |llvm_object| {
24912504 try wasm.base.emitLlvmObject(arena, llvm_object, prog_node);
24922505 const use_lld = build_options.have_llvm and comp.config.use_lld;
......@@ -2569,23 +2582,23 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
25692582 if (wasm.zig_object_index != .null) {
25702583 try wasm.resolveSymbolsInObject(wasm.zig_object_index);
25712584 }
2572 if (wasm.base.hasErrors()) return error.FlushFailure;
2585 if (diags.hasErrors()) return error.FlushFailure;
25732586 for (wasm.objects.items) |object_index| {
25742587 try wasm.resolveSymbolsInObject(object_index);
25752588 }
2576 if (wasm.base.hasErrors()) return error.FlushFailure;
2589 if (diags.hasErrors()) return error.FlushFailure;
25772590
25782591 var emit_features_count: u32 = 0;
25792592 var enabled_features: [@typeInfo(types.Feature.Tag).@"enum".fields.len]bool = undefined;
25802593 try wasm.validateFeatures(&enabled_features, &emit_features_count);
25812594 try wasm.resolveSymbolsInArchives();
2582 if (wasm.base.hasErrors()) return error.FlushFailure;
2595 if (diags.hasErrors()) return error.FlushFailure;
25832596 try wasm.resolveLazySymbols();
25842597 try wasm.checkUndefinedSymbols();
25852598 try wasm.checkExportNames();
25862599
25872600 try wasm.setupInitFunctions();
2588 if (wasm.base.hasErrors()) return error.FlushFailure;
2601 if (diags.hasErrors()) return error.FlushFailure;
25892602 try wasm.setupStart();
25902603
25912604 try wasm.markReferences();
......@@ -2594,7 +2607,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
25942607 try wasm.mergeTypes();
25952608 try wasm.allocateAtoms();
25962609 try wasm.setupMemory();
2597 if (wasm.base.hasErrors()) return error.FlushFailure;
2610 if (diags.hasErrors()) return error.FlushFailure;
25982611 wasm.allocateVirtualAddresses();
25992612 wasm.mapFunctionTable();
26002613 try wasm.initializeCallCtorsFunction();
......@@ -2604,7 +2617,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
26042617 try wasm.setupStartSection();
26052618 try wasm.setupExports();
26062619 try wasm.writeToFile(enabled_features, emit_features_count, arena);
2607 if (wasm.base.hasErrors()) return error.FlushFailure;
2620 if (diags.hasErrors()) return error.FlushFailure;
26082621}
26092622
26102623/// Writes the WebAssembly in-memory module to the file
......@@ -2615,6 +2628,7 @@ fn writeToFile(
26152628 arena: Allocator,
26162629) !void {
26172630 const comp = wasm.base.comp;
2631 const diags = &comp.link_diags;
26182632 const gpa = comp.gpa;
26192633 const use_llvm = comp.config.use_llvm;
26202634 const use_lld = build_options.have_llvm and comp.config.use_lld;
......@@ -3003,7 +3017,7 @@ fn writeToFile(
30033017 try emitBuildIdSection(&binary_bytes, str);
30043018 },
30053019 else => |mode| {
3006 var err = try wasm.base.addErrorWithNotes(0);
3020 var err = try diags.addErrorWithNotes(0);
30073021 try err.addMsg("build-id '{s}' is not supported for WebAssembly", .{@tagName(mode)});
30083022 },
30093023 }
......@@ -3684,7 +3698,8 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
36843698 switch (term) {
36853699 .Exited => |code| {
36863700 if (code != 0) {
3687 comp.lockAndParseLldStderr(linker_command, stderr);
3701 const diags = &comp.link_diags;
3702 diags.lockAndParseLldStderr(linker_command, stderr);
36883703 return error.LLDReportedFailure;
36893704 }
36903705 },
src/link/Wasm/Object.zig+8-5
......@@ -226,6 +226,8 @@ pub fn findImport(object: *const Object, sym: Symbol) types.Import {
226226///
227227/// When the object file is *NOT* MVP, we return `null`.
228228fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?Symbol {
229 const diags = &wasm_file.base.comp.link_diags;
230
229231 var table_count: usize = 0;
230232 for (object.symtable) |sym| {
231233 if (sym.tag == .table) table_count += 1;
......@@ -235,7 +237,7 @@ fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?S
235237 if (object.imported_tables_count == table_count) return null;
236238
237239 if (table_count != 0) {
238 var err = try wasm_file.base.addErrorWithNotes(1);
240 var err = try diags.addErrorWithNotes(1);
239241 try err.addMsg("Expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
240242 object.imported_tables_count,
241243 table_count,
......@@ -246,14 +248,14 @@ fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?S
246248
247249 // MVP object files cannot have any table definitions, only imports (for the indirect function table).
248250 if (object.tables.len > 0) {
249 var err = try wasm_file.base.addErrorWithNotes(1);
251 var err = try diags.addErrorWithNotes(1);
250252 try err.addMsg("Unexpected table definition without representing table symbols.", .{});
251253 try err.addNote("defined in '{s}'", .{object.path});
252254 return error.UnexpectedTable;
253255 }
254256
255257 if (object.imported_tables_count != 1) {
256 var err = try wasm_file.base.addErrorWithNotes(1);
258 var err = try diags.addErrorWithNotes(1);
257259 try err.addMsg("Found more than one table import, but no representing table symbols", .{});
258260 try err.addNote("defined in '{s}'", .{object.path});
259261 return error.MissingTableSymbols;
......@@ -266,7 +268,7 @@ fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?S
266268 } else unreachable;
267269
268270 if (!std.mem.eql(u8, object.string_table.get(table_import.name), "__indirect_function_table")) {
269 var err = try wasm_file.base.addErrorWithNotes(1);
271 var err = try diags.addErrorWithNotes(1);
270272 try err.addMsg("Non-indirect function table import '{s}' is missing a corresponding symbol", .{object.string_table.get(table_import.name)});
271273 try err.addNote("defined in '{s}'", .{object.path});
272274 return error.MissingTableSymbols;
......@@ -587,6 +589,7 @@ fn Parser(comptime ReaderType: type) type {
587589 /// to be able to link.
588590 /// Logs an info message when an undefined feature is detected.
589591 fn parseFeatures(parser: *ObjectParser, gpa: Allocator) !void {
592 const diags = &parser.wasm_file.base.comp.link_diags;
590593 const reader = parser.reader.reader();
591594 for (try readVec(&parser.object.features, reader, gpa)) |*feature| {
592595 const prefix = try readEnum(types.Feature.Prefix, reader);
......@@ -596,7 +599,7 @@ fn Parser(comptime ReaderType: type) type {
596599 try reader.readNoEof(name);
597600
598601 const tag = types.known_features.get(name) orelse {
599 var err = try parser.wasm_file.base.addErrorWithNotes(1);
602 var err = try diags.addErrorWithNotes(1);
600603 try err.addMsg("Object file contains unknown feature: {s}", .{name});
601604 try err.addNote("defined in '{s}'", .{parser.object.path});
602605 return error.UnknownFeature;