authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-07-19 08:52:43+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-07-22 12:05:56+02:00
logcba3389d906ff36f7913c7497d55ce1bf3164022
tree8da5db548c4b965080235a38432976c7e022abc0
parent1fc42ed3e7ca0b74b54aaa827276d995d6c7c6cd

macho: redo input file parsing in prep for multithreading


17 files changed, 522 insertions(+), 704 deletions(-)

src/Compilation.zig+2-2
......@@ -105,8 +105,9 @@ win32_resource_table: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMa
105105 pub fn deinit(_: @This(), _: Allocator) void {}
106106} = .{},
107107
108link_error_flags: link.File.ErrorFlags = .{},
109108link_errors: std.ArrayListUnmanaged(link.File.ErrorMsg) = .{},
109link_errors_mutex: std.Thread.Mutex = .{},
110link_error_flags: link.File.ErrorFlags = .{},
110111lld_errors: std.ArrayListUnmanaged(LldError) = .{},
111112
112113work_queues: [
......@@ -3067,7 +3068,6 @@ pub fn totalErrorCount(comp: *Compilation) u32 {
30673068 total += @intFromBool(comp.link_error_flags.no_entry_point_found);
30683069 }
30693070 total += @intFromBool(comp.link_error_flags.missing_libc);
3070
30713071 total += comp.link_errors.items.len;
30723072
30733073 // Compile log errors only count if there are no other errors.
src/link.zig+67-1
......@@ -439,6 +439,58 @@ pub const File = struct {
439439 }
440440 }
441441
442 pub const ErrorWithNotes = struct {
443 base: *const File,
444
445 /// Allocated index in base.errors array.
446 index: usize,
447
448 /// Next available note slot.
449 note_slot: usize = 0,
450
451 pub fn addMsg(
452 err: ErrorWithNotes,
453 comptime format: []const u8,
454 args: anytype,
455 ) error{OutOfMemory}!void {
456 const gpa = err.base.comp.gpa;
457 const err_msg = &err.base.comp.link_errors.items[err.index];
458 err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
459 }
460
461 pub fn addNote(
462 err: *ErrorWithNotes,
463 comptime format: []const u8,
464 args: anytype,
465 ) error{OutOfMemory}!void {
466 const gpa = err.base.comp.gpa;
467 const err_msg = &err.base.comp.link_errors.items[err.index];
468 assert(err.note_slot < err_msg.notes.len);
469 err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };
470 err.note_slot += 1;
471 }
472 };
473
474 pub fn addErrorWithNotes(base: *const File, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
475 base.comp.link_errors_mutex.lock();
476 defer base.comp.link_errors_mutex.unlock();
477 const gpa = base.comp.gpa;
478 try base.comp.link_errors.ensureUnusedCapacity(gpa, 1);
479 return base.addErrorWithNotesAssumeCapacity(note_count);
480 }
481
482 pub fn addErrorWithNotesAssumeCapacity(base: *const File, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
483 const gpa = base.comp.gpa;
484 const index = base.comp.link_errors.items.len;
485 const err = base.comp.link_errors.addOneAssumeCapacity();
486 err.* = .{ .msg = undefined, .notes = try gpa.alloc(ErrorMsg, note_count) };
487 return .{ .base = base, .index = index };
488 }
489
490 pub fn hasErrors(base: *const File) bool {
491 return base.comp.link_errors.items.len > 0 or base.comp.link_error_flags.isSet();
492 }
493
442494 pub fn releaseLock(self: *File) void {
443495 if (self.lock) |*lock| {
444496 lock.release();
......@@ -874,9 +926,23 @@ pub const File = struct {
874926 }
875927 };
876928
877 pub const ErrorFlags = struct {
929 pub const ErrorFlags = packed struct {
878930 no_entry_point_found: bool = false,
879931 missing_libc: bool = false,
932
933 const Int = blk: {
934 const bits = @typeInfo(@This()).Struct.fields.len;
935 break :blk @Type(.{
936 .Int = .{
937 .signedness = .unsigned,
938 .bits = bits,
939 },
940 });
941 };
942
943 fn isSet(ef: ErrorFlags) bool {
944 return @as(Int, @bitCast(ef)) > 0;
945 }
880946 };
881947
882948 pub const ErrorMsg = struct {
src/link/Elf.zig+33-84
......@@ -995,12 +995,12 @@ pub fn growAllocSection(self: *Elf, shdr_index: u32, needed_size: u64) !void {
995995 if (maybe_phdr) |phdr| {
996996 const mem_capacity = self.allocatedVirtualSize(phdr.p_vaddr);
997997 if (needed_size > mem_capacity) {
998 var err = try self.addErrorWithNotes(2);
999 try err.addMsg(self, "fatal linker error: cannot expand load segment phdr({d}) in virtual memory", .{
998 var err = try self.base.addErrorWithNotes(2);
999 try err.addMsg("fatal linker error: cannot expand load segment phdr({d}) in virtual memory", .{
10001000 self.phdr_to_shdr_table.get(shdr_index).?,
10011001 });
1002 try err.addNote(self, "TODO: emit relocations to memory locations in self-hosted backends", .{});
1003 try err.addNote(self, "as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
1002 try err.addNote("TODO: emit relocations to memory locations in self-hosted backends", .{});
1003 try err.addNote("as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
10041004 }
10051005
10061006 phdr.p_memsz = needed_size;
......@@ -1276,7 +1276,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
12761276 };
12771277 }
12781278
1279 if (comp.link_errors.items.len > 0) return error.FlushFailure;
1279 if (self.base.hasErrors()) return error.FlushFailure;
12801280
12811281 // Dedup shared objects
12821282 {
......@@ -1423,7 +1423,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
14231423 try self.writeElfHeader();
14241424 }
14251425
1426 if (comp.link_errors.items.len > 0) return error.FlushFailure;
1426 if (self.base.hasErrors()) return error.FlushFailure;
14271427}
14281428
14291429/// --verbose-link output
......@@ -2852,9 +2852,9 @@ fn writePhdrTable(self: *Elf) !void {
28522852}
28532853
28542854pub fn writeElfHeader(self: *Elf) !void {
2855 const comp = self.base.comp;
2856 if (comp.link_errors.items.len > 0) return; // We had errors, so skip flushing to render the output unusable
2855 if (self.base.hasErrors()) return; // We had errors, so skip flushing to render the output unusable
28572856
2857 const comp = self.base.comp;
28582858 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
28592859
28602860 var index: usize = 0;
......@@ -4298,9 +4298,9 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {
42984298 // (revisit getMaxNumberOfPhdrs())
42994299 // 2. shift everything in file to free more space for EHDR + PHDR table
43004300 // TODO verify `getMaxNumberOfPhdrs()` is accurate and convert this into no-op
4301 var err = try self.addErrorWithNotes(1);
4302 try err.addMsg(self, "fatal linker error: not enough space reserved for EHDR and PHDR table", .{});
4303 try err.addNote(self, "required 0x{x}, available 0x{x}", .{ needed_size, available_space });
4301 var err = try self.base.addErrorWithNotes(1);
4302 try err.addMsg("fatal linker error: not enough space reserved for EHDR and PHDR table", .{});
4303 try err.addNote("required 0x{x}, available 0x{x}", .{ needed_size, available_space });
43044304 }
43054305
43064306 phdr_table_load.p_filesz = needed_size + ehsize;
......@@ -5863,56 +5863,6 @@ pub fn tlsAddress(self: *Elf) i64 {
58635863 return @intCast(phdr.p_vaddr);
58645864}
58655865
5866const ErrorWithNotes = struct {
5867 /// Allocated index in comp.link_errors array.
5868 index: usize,
5869
5870 /// Next available note slot.
5871 note_slot: usize = 0,
5872
5873 pub fn addMsg(
5874 err: ErrorWithNotes,
5875 elf_file: *Elf,
5876 comptime format: []const u8,
5877 args: anytype,
5878 ) error{OutOfMemory}!void {
5879 const comp = elf_file.base.comp;
5880 const gpa = comp.gpa;
5881 const err_msg = &comp.link_errors.items[err.index];
5882 err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
5883 }
5884
5885 pub fn addNote(
5886 err: *ErrorWithNotes,
5887 elf_file: *Elf,
5888 comptime format: []const u8,
5889 args: anytype,
5890 ) error{OutOfMemory}!void {
5891 const comp = elf_file.base.comp;
5892 const gpa = comp.gpa;
5893 const err_msg = &comp.link_errors.items[err.index];
5894 assert(err.note_slot < err_msg.notes.len);
5895 err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };
5896 err.note_slot += 1;
5897 }
5898};
5899
5900pub fn addErrorWithNotes(self: *Elf, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
5901 const comp = self.base.comp;
5902 const gpa = comp.gpa;
5903 try comp.link_errors.ensureUnusedCapacity(gpa, 1);
5904 return self.addErrorWithNotesAssumeCapacity(note_count);
5905}
5906
5907fn addErrorWithNotesAssumeCapacity(self: *Elf, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
5908 const comp = self.base.comp;
5909 const gpa = comp.gpa;
5910 const index = comp.link_errors.items.len;
5911 const err = comp.link_errors.addOneAssumeCapacity();
5912 err.* = .{ .msg = undefined, .notes = try gpa.alloc(link.File.ErrorMsg, note_count) };
5913 return .{ .index = index };
5914}
5915
59165866pub fn getShString(self: Elf, off: u32) [:0]const u8 {
59175867 assert(off < self.shstrtab.items.len);
59185868 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.shstrtab.items.ptr + off)), 0);
......@@ -5940,11 +5890,10 @@ pub fn insertDynString(self: *Elf, name: []const u8) error{OutOfMemory}!u32 {
59405890}
59415891
59425892fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
5943 const comp = self.base.comp;
5944 const gpa = comp.gpa;
5893 const gpa = self.base.comp.gpa;
59455894 const max_notes = 4;
59465895
5947 try comp.link_errors.ensureUnusedCapacity(gpa, undefs.count());
5896 try self.base.comp.link_errors.ensureUnusedCapacity(gpa, undefs.count());
59485897
59495898 var it = undefs.iterator();
59505899 while (it.next()) |entry| {
......@@ -5953,18 +5902,18 @@ fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
59535902 const natoms = @min(atoms.len, max_notes);
59545903 const nnotes = natoms + @intFromBool(atoms.len > max_notes);
59555904
5956 var err = try self.addErrorWithNotesAssumeCapacity(nnotes);
5957 try err.addMsg(self, "undefined symbol: {s}", .{self.symbol(undef_index).name(self)});
5905 var err = try self.base.addErrorWithNotesAssumeCapacity(nnotes);
5906 try err.addMsg("undefined symbol: {s}", .{self.symbol(undef_index).name(self)});
59585907
59595908 for (atoms[0..natoms]) |atom_index| {
59605909 const atom_ptr = self.atom(atom_index).?;
59615910 const file_ptr = self.file(atom_ptr.file_index).?;
5962 try err.addNote(self, "referenced by {s}:{s}", .{ file_ptr.fmtPath(), atom_ptr.name(self) });
5911 try err.addNote("referenced by {s}:{s}", .{ file_ptr.fmtPath(), atom_ptr.name(self) });
59635912 }
59645913
59655914 if (atoms.len > max_notes) {
59665915 const remaining = atoms.len - max_notes;
5967 try err.addNote(self, "referenced {d} more times", .{remaining});
5916 try err.addNote("referenced {d} more times", .{remaining});
59685917 }
59695918 }
59705919}
......@@ -5978,19 +5927,19 @@ fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemor
59785927 const notes = entry.value_ptr.*;
59795928 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
59805929
5981 var err = try self.addErrorWithNotes(nnotes + 1);
5982 try err.addMsg(self, "duplicate symbol definition: {s}", .{sym.name(self)});
5983 try err.addNote(self, "defined by {}", .{sym.file(self).?.fmtPath()});
5930 var err = try self.base.addErrorWithNotes(nnotes + 1);
5931 try err.addMsg("duplicate symbol definition: {s}", .{sym.name(self)});
5932 try err.addNote("defined by {}", .{sym.file(self).?.fmtPath()});
59845933
59855934 var inote: usize = 0;
59865935 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
59875936 const file_ptr = self.file(notes.items[inote]).?;
5988 try err.addNote(self, "defined by {}", .{file_ptr.fmtPath()});
5937 try err.addNote("defined by {}", .{file_ptr.fmtPath()});
59895938 }
59905939
59915940 if (notes.items.len > max_notes) {
59925941 const remaining = notes.items.len - max_notes;
5993 try err.addNote(self, "defined {d} more times", .{remaining});
5942 try err.addNote("defined {d} more times", .{remaining});
59945943 }
59955944
59965945 has_dupes = true;
......@@ -6005,16 +5954,16 @@ fn reportMissingLibraryError(
60055954 comptime format: []const u8,
60065955 args: anytype,
60075956) error{OutOfMemory}!void {
6008 var err = try self.addErrorWithNotes(checked_paths.len);
6009 try err.addMsg(self, format, args);
5957 var err = try self.base.addErrorWithNotes(checked_paths.len);
5958 try err.addMsg(format, args);
60105959 for (checked_paths) |path| {
6011 try err.addNote(self, "tried {s}", .{path});
5960 try err.addNote("tried {s}", .{path});
60125961 }
60135962}
60145963
60155964pub fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void {
6016 var err = try self.addErrorWithNotes(0);
6017 try err.addMsg(self, "fatal linker error: unsupported CPU architecture {s}", .{
5965 var err = try self.base.addErrorWithNotes(0);
5966 try err.addMsg("fatal linker error: unsupported CPU architecture {s}", .{
60185967 @tagName(self.getTarget().cpu.arch),
60195968 });
60205969}
......@@ -6025,9 +5974,9 @@ pub fn reportParseError(
60255974 comptime format: []const u8,
60265975 args: anytype,
60275976) error{OutOfMemory}!void {
6028 var err = try self.addErrorWithNotes(1);
6029 try err.addMsg(self, format, args);
6030 try err.addNote(self, "while parsing {s}", .{path});
5977 var err = try self.base.addErrorWithNotes(1);
5978 try err.addMsg(format, args);
5979 try err.addNote("while parsing {s}", .{path});
60315980}
60325981
60335982pub fn reportParseError2(
......@@ -6036,9 +5985,9 @@ pub fn reportParseError2(
60365985 comptime format: []const u8,
60375986 args: anytype,
60385987) error{OutOfMemory}!void {
6039 var err = try self.addErrorWithNotes(1);
6040 try err.addMsg(self, format, args);
6041 try err.addNote(self, "while parsing {}", .{self.file(file_index).?.fmtPath()});
5988 var err = try self.base.addErrorWithNotes(1);
5989 try err.addMsg(format, args);
5990 try err.addNote("while parsing {}", .{self.file(file_index).?.fmtPath()});
60425991}
60435992
60445993const FormatShdrCtx = struct {
src/link/Elf/Atom.zig+32-44
......@@ -631,15 +631,12 @@ fn dataType(symbol: *const Symbol, elf_file: *Elf) u2 {
631631}
632632
633633fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) RelocError!void {
634 var err = try elf_file.addErrorWithNotes(1);
635 try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {} at offset 0x{x}", .{
634 var err = try elf_file.base.addErrorWithNotes(1);
635 try err.addMsg("fatal linker error: unhandled relocation type {} at offset 0x{x}", .{
636636 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),
637637 rel.r_offset,
638638 });
639 try err.addNote(elf_file, "in {}:{s}", .{
640 self.file(elf_file).?.fmtPath(),
641 self.name(elf_file),
642 });
639 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
643640 return error.RelocFailure;
644641}
645642
......@@ -649,15 +646,12 @@ fn reportTextRelocError(
649646 rel: elf.Elf64_Rela,
650647 elf_file: *Elf,
651648) RelocError!void {
652 var err = try elf_file.addErrorWithNotes(1);
653 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
649 var err = try elf_file.base.addErrorWithNotes(1);
650 try err.addMsg("relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
654651 rel.r_offset,
655652 symbol.name(elf_file),
656653 });
657 try err.addNote(elf_file, "in {}:{s}", .{
658 self.file(elf_file).?.fmtPath(),
659 self.name(elf_file),
660 });
654 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
661655 return error.RelocFailure;
662656}
663657
......@@ -667,16 +661,13 @@ fn reportPicError(
667661 rel: elf.Elf64_Rela,
668662 elf_file: *Elf,
669663) RelocError!void {
670 var err = try elf_file.addErrorWithNotes(2);
671 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
664 var err = try elf_file.base.addErrorWithNotes(2);
665 try err.addMsg("relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
672666 rel.r_offset,
673667 symbol.name(elf_file),
674668 });
675 try err.addNote(elf_file, "in {}:{s}", .{
676 self.file(elf_file).?.fmtPath(),
677 self.name(elf_file),
678 });
679 try err.addNote(elf_file, "recompile with -fPIC", .{});
669 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
670 try err.addNote("recompile with -fPIC", .{});
680671 return error.RelocFailure;
681672}
682673
......@@ -686,16 +677,13 @@ fn reportNoPicError(
686677 rel: elf.Elf64_Rela,
687678 elf_file: *Elf,
688679) RelocError!void {
689 var err = try elf_file.addErrorWithNotes(2);
690 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
680 var err = try elf_file.base.addErrorWithNotes(2);
681 try err.addMsg("relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
691682 rel.r_offset,
692683 symbol.name(elf_file),
693684 });
694 try err.addNote(elf_file, "in {}:{s}", .{
695 self.file(elf_file).?.fmtPath(),
696 self.name(elf_file),
697 });
698 try err.addNote(elf_file, "recompile with -fno-PIC", .{});
685 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
686 try err.addNote("recompile with -fno-PIC", .{});
699687 return error.RelocFailure;
700688}
701689
......@@ -1332,9 +1320,9 @@ const x86_64 = struct {
13321320 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
13331321 } else {
13341322 x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]) catch {
1335 var err = try elf_file.addErrorWithNotes(1);
1336 try err.addMsg(elf_file, "could not relax {s}", .{@tagName(r_type)});
1337 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
1323 var err = try elf_file.base.addErrorWithNotes(1);
1324 try err.addMsg("could not relax {s}", .{@tagName(r_type)});
1325 try err.addNote("in {}:{s} at offset 0x{x}", .{
13381326 atom.file(elf_file).?.fmtPath(),
13391327 atom.name(elf_file),
13401328 rel.r_offset,
......@@ -1479,12 +1467,12 @@ const x86_64 = struct {
14791467 },
14801468
14811469 else => {
1482 var err = try elf_file.addErrorWithNotes(1);
1483 try err.addMsg(elf_file, "TODO: rewrite {} when followed by {}", .{
1470 var err = try elf_file.base.addErrorWithNotes(1);
1471 try err.addMsg("TODO: rewrite {} when followed by {}", .{
14841472 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
14851473 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
14861474 });
1487 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
1475 try err.addNote("in {}:{s} at offset 0x{x}", .{
14881476 self.file(elf_file).?.fmtPath(),
14891477 self.name(elf_file),
14901478 rels[0].r_offset,
......@@ -1534,12 +1522,12 @@ const x86_64 = struct {
15341522 },
15351523
15361524 else => {
1537 var err = try elf_file.addErrorWithNotes(1);
1538 try err.addMsg(elf_file, "TODO: rewrite {} when followed by {}", .{
1525 var err = try elf_file.base.addErrorWithNotes(1);
1526 try err.addMsg("TODO: rewrite {} when followed by {}", .{
15391527 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
15401528 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
15411529 });
1542 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
1530 try err.addNote("in {}:{s} at offset 0x{x}", .{
15431531 self.file(elf_file).?.fmtPath(),
15441532 self.name(elf_file),
15451533 rels[0].r_offset,
......@@ -1630,12 +1618,12 @@ const x86_64 = struct {
16301618 },
16311619
16321620 else => {
1633 var err = try elf_file.addErrorWithNotes(1);
1634 try err.addMsg(elf_file, "fatal linker error: rewrite {} when followed by {}", .{
1621 var err = try elf_file.base.addErrorWithNotes(1);
1622 try err.addMsg("fatal linker error: rewrite {} when followed by {}", .{
16351623 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
16361624 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
16371625 });
1638 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
1626 try err.addNote("in {}:{s} at offset 0x{x}", .{
16391627 self.file(elf_file).?.fmtPath(),
16401628 self.name(elf_file),
16411629 rels[0].r_offset,
......@@ -1824,9 +1812,9 @@ const aarch64 = struct {
18241812 aarch64_util.writeAdrpInst(pages, code);
18251813 } else {
18261814 // TODO: relax
1827 var err = try elf_file.addErrorWithNotes(1);
1828 try err.addMsg(elf_file, "TODO: relax ADR_GOT_PAGE", .{});
1829 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
1815 var err = try elf_file.base.addErrorWithNotes(1);
1816 try err.addMsg("TODO: relax ADR_GOT_PAGE", .{});
1817 try err.addNote("in {}:{s} at offset 0x{x}", .{
18301818 atom.file(elf_file).?.fmtPath(),
18311819 atom.name(elf_file),
18321820 r_offset,
......@@ -2118,9 +2106,9 @@ const riscv = struct {
21182106 if (S == atom_addr + @as(i64, @intCast(pair.r_offset))) break pair;
21192107 } else {
21202108 // TODO: implement searching forward
2121 var err = try elf_file.addErrorWithNotes(1);
2122 try err.addMsg(elf_file, "TODO: find HI20 paired reloc scanning forward", .{});
2123 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
2109 var err = try elf_file.base.addErrorWithNotes(1);
2110 try err.addMsg("TODO: find HI20 paired reloc scanning forward", .{});
2111 try err.addNote("in {}:{s} at offset 0x{x}", .{
21242112 atom.file(elf_file).?.fmtPath(),
21252113 atom.name(elf_file),
21262114 rel.r_offset,
src/link/Elf/Object.zig+13-13
......@@ -704,9 +704,9 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void {
704704 var end = start;
705705 while (end < data.len - sh_entsize and !isNull(data[end .. end + sh_entsize])) : (end += sh_entsize) {}
706706 if (!isNull(data[end .. end + sh_entsize])) {
707 var err = try elf_file.addErrorWithNotes(1);
708 try err.addMsg(elf_file, "string not null terminated", .{});
709 try err.addNote(elf_file, "in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
707 var err = try elf_file.base.addErrorWithNotes(1);
708 try err.addMsg("string not null terminated", .{});
709 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
710710 return error.MalformedObject;
711711 }
712712 end += sh_entsize;
......@@ -719,9 +719,9 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void {
719719 const sh_entsize: u32 = @intCast(shdr.sh_entsize);
720720 if (sh_entsize == 0) continue; // Malformed, don't split but don't error out
721721 if (shdr.sh_size % sh_entsize != 0) {
722 var err = try elf_file.addErrorWithNotes(1);
723 try err.addMsg(elf_file, "size not a multiple of sh_entsize", .{});
724 try err.addNote(elf_file, "in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
722 var err = try elf_file.base.addErrorWithNotes(1);
723 try err.addMsg("size not a multiple of sh_entsize", .{});
724 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
725725 return error.MalformedObject;
726726 }
727727
......@@ -779,10 +779,10 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
779779 const imsec = elf_file.inputMergeSection(imsec_index) orelse continue;
780780 if (imsec.offsets.items.len == 0) continue;
781781 const msub_index, const offset = imsec.findSubsection(@intCast(esym.st_value)) orelse {
782 var err = try elf_file.addErrorWithNotes(2);
783 try err.addMsg(elf_file, "invalid symbol value: {x}", .{esym.st_value});
784 try err.addNote(elf_file, "for symbol {s}", .{sym.name(elf_file)});
785 try err.addNote(elf_file, "in {}", .{self.fmtPath()});
782 var err = try elf_file.base.addErrorWithNotes(2);
783 try err.addMsg("invalid symbol value: {x}", .{esym.st_value});
784 try err.addNote("for symbol {s}", .{sym.name(elf_file)});
785 try err.addNote("in {}", .{self.fmtPath()});
786786 return error.MalformedObject;
787787 };
788788
......@@ -804,9 +804,9 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
804804 const imsec = elf_file.inputMergeSection(imsec_index) orelse continue;
805805 if (imsec.offsets.items.len == 0) continue;
806806 const msub_index, const offset = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse {
807 var err = try elf_file.addErrorWithNotes(1);
808 try err.addMsg(elf_file, "invalid relocation at offset 0x{x}", .{rel.r_offset});
809 try err.addNote(elf_file, "in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
807 var err = try elf_file.base.addErrorWithNotes(1);
808 try err.addMsg("invalid relocation at offset 0x{x}", .{rel.r_offset});
809 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
810810 return error.MalformedObject;
811811 };
812812 const msub = elf_file.mergeSubsection(msub_index);
src/link/Elf/eh_frame.zig+3-3
......@@ -591,12 +591,12 @@ const riscv = struct {
591591};
592592
593593fn reportInvalidReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela) !void {
594 var err = try elf_file.addErrorWithNotes(1);
595 try err.addMsg(elf_file, "invalid relocation type {} at offset 0x{x}", .{
594 var err = try elf_file.base.addErrorWithNotes(1);
595 try err.addMsg("invalid relocation type {} at offset 0x{x}", .{
596596 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),
597597 rel.r_offset,
598598 });
599 try err.addNote(elf_file, "in {}:.eh_frame", .{elf_file.file(rec.file_index).?.fmtPath()});
599 try err.addNote("in {}:.eh_frame", .{elf_file.file(rec.file_index).?.fmtPath()});
600600 return error.RelocFailure;
601601}
602602
src/link/Elf/relocatable.zig+4-4
......@@ -29,7 +29,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co
2929 };
3030 }
3131
32 if (comp.link_errors.items.len > 0) return error.FlushFailure;
32 if (elf_file.base.hasErrors()) return error.FlushFailure;
3333
3434 // First, we flush relocatable object file generated with our backends.
3535 if (elf_file.zigObjectPtr()) |zig_object| {
......@@ -146,7 +146,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co
146146 try elf_file.base.file.?.setEndPos(total_size);
147147 try elf_file.base.file.?.pwriteAll(buffer.items, 0);
148148
149 if (comp.link_errors.items.len > 0) return error.FlushFailure;
149 if (elf_file.base.hasErrors()) return error.FlushFailure;
150150}
151151
152152pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
......@@ -177,7 +177,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const
177177 };
178178 }
179179
180 if (comp.link_errors.items.len > 0) return error.FlushFailure;
180 if (elf_file.base.hasErrors()) return error.FlushFailure;
181181
182182 // Now, we are ready to resolve the symbols across all input files.
183183 // We will first resolve the files in the ZigObject, next in the parsed
......@@ -216,7 +216,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const
216216 try elf_file.writeShdrTable();
217217 try elf_file.writeElfHeader();
218218
219 if (comp.link_errors.items.len > 0) return error.FlushFailure;
219 if (elf_file.base.hasErrors()) return error.FlushFailure;
220220}
221221
222222fn parsePositional(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
src/link/MachO.zig+178-265
......@@ -395,17 +395,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
395395 }
396396
397397 for (positionals.items) |obj| {
398 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
399 error.MalformedObject,
400 error.MalformedArchive,
401 error.MalformedDylib,
402 error.InvalidCpuArch,
403 error.InvalidTarget,
404 => continue, // already reported
405 error.UnknownFileType => try self.reportParseError(obj.path, "unknown file type for an object file", .{}),
398 self.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
399 error.UnknownFileType => try self.reportParseError(obj.path, "unknown file type for an input file", .{}),
406400 else => |e| try self.reportParseError(
407401 obj.path,
408 "unexpected error: parsing input file failed with error {s}",
402 "unexpected error: reading input file failed with error {s}",
409403 .{@errorName(e)},
410404 ),
411405 };
......@@ -448,15 +442,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
448442 };
449443
450444 for (system_libs.items) |lib| {
451 self.parseLibrary(lib, false) catch |err| switch (err) {
452 error.MalformedArchive,
453 error.MalformedDylib,
454 error.InvalidCpuArch,
455 => continue, // already reported
456 error.UnknownFileType => try self.reportParseError(lib.path, "unknown file type for a library", .{}),
445 self.classifyInputFile(lib.path, lib, false) catch |err| switch (err) {
446 error.UnknownFileType => try self.reportParseError(lib.path, "unknown file type for an input file", .{}),
457447 else => |e| try self.reportParseError(
458448 lib.path,
459 "unexpected error: parsing library failed with error {s}",
449 "unexpected error: parsing input file failed with error {s}",
460450 .{@errorName(e)},
461451 ),
462452 };
......@@ -469,13 +459,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
469459 break :blk null;
470460 };
471461 if (compiler_rt_path) |path| {
472 self.parsePositional(path, false) catch |err| switch (err) {
473 error.MalformedObject,
474 error.MalformedArchive,
475 error.InvalidCpuArch,
476 error.InvalidTarget,
477 => {}, // already reported
478 error.UnknownFileType => try self.reportParseError(path, "unknown file type for a library", .{}),
462 self.classifyInputFile(path, .{ .path = path }, false) catch |err| switch (err) {
463 error.UnknownFileType => try self.reportParseError(path, "unknown file type for an input file", .{}),
479464 else => |e| try self.reportParseError(
480465 path,
481466 "unexpected error: parsing input file failed with error {s}",
......@@ -484,30 +469,20 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
484469 };
485470 }
486471
487 if (comp.link_errors.items.len > 0) return error.FlushFailure;
472 if (self.base.hasErrors()) return error.FlushFailure;
488473
489 for (self.dylibs.items) |index| {
490 self.getFile(index).?.dylib.umbrella = index;
491 }
492
493 if (self.dylibs.items.len > 0) {
494 self.parseDependentDylibs() catch |err| {
495 switch (err) {
496 error.MissingLibraryDependencies => {},
497 else => |e| try self.reportUnexpectedError(
498 "unexpected error while parsing dependent libraries: {s}",
499 .{@errorName(e)},
500 ),
501 }
502 return error.FlushFailure;
503 };
504 }
474 try self.parseInputFiles();
475 self.parseDependentDylibs() catch |err| {
476 switch (err) {
477 error.MissingLibraryDependencies => {},
478 else => |e| try self.reportUnexpectedError(
479 "unexpected error while parsing dependent libraries: {s}",
480 .{@errorName(e)},
481 ),
482 }
483 };
505484
506 for (self.dylibs.items) |index| {
507 const dylib = self.getFile(index).?.dylib;
508 if (!dylib.explicit and !dylib.hoisted) continue;
509 try dylib.initSymbols(self);
510 }
485 if (self.base.hasErrors()) return error.FlushFailure;
511486
512487 {
513488 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
......@@ -841,181 +816,173 @@ pub fn resolveLibSystem(
841816 });
842817}
843818
844pub const ParseError = error{
845 MalformedObject,
846 MalformedArchive,
847 MalformedDylib,
848 MalformedTbd,
849 NotLibStub,
850 InvalidCpuArch,
851 InvalidTarget,
852 InvalidTargetFatLibrary,
853 IncompatibleDylibVersion,
854 OutOfMemory,
855 Overflow,
856 InputOutput,
857 EndOfStream,
858 FileSystem,
859 NotSupported,
860 Unhandled,
861 UnknownFileType,
862} || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError || tapi.TapiError;
863
864pub fn parsePositional(self: *MachO, path: []const u8, must_link: bool) ParseError!void {
819pub fn classifyInputFile(self: *MachO, path: []const u8, lib: SystemLib, must_link: bool) !void {
865820 const tracy = trace(@src());
866821 defer tracy.end();
867 if (try Object.isObject(path)) {
868 try self.parseObject(path);
869 } else {
870 try self.parseLibrary(.{ .path = path }, must_link);
822
823 log.debug("classifying input file {s}", .{path});
824
825 const file = try std.fs.cwd().openFile(path, .{});
826 const fh = try self.addFileHandle(file);
827 var buffer: [Archive.SARMAG]u8 = undefined;
828
829 const fat_arch: ?fat.Arch = try self.parseFatFile(file, path);
830 const offset = if (fat_arch) |fa| fa.offset else 0;
831
832 if (readMachHeader(file, offset) catch null) |h| blk: {
833 if (h.magic != macho.MH_MAGIC_64) break :blk;
834 switch (h.filetype) {
835 macho.MH_OBJECT => try self.addObject(path, fh, offset),
836 macho.MH_DYLIB => _ = try self.addDylib(lib, true, fh, offset),
837 else => return error.UnknownFileType,
838 }
839 return;
840 }
841 if (readArMagic(file, offset, &buffer) catch null) |ar_magic| blk: {
842 if (!mem.eql(u8, ar_magic, Archive.ARMAG)) break :blk;
843 try self.addArchive(lib, must_link, fh, fat_arch);
844 return;
871845 }
846 _ = try self.addTbd(lib, true, fh);
872847}
873848
874fn parseLibrary(self: *MachO, lib: SystemLib, must_link: bool) ParseError!void {
875 const tracy = trace(@src());
876 defer tracy.end();
877 if (try fat.isFatLibrary(lib.path)) {
878 const fat_arch = try self.parseFatLibrary(lib.path);
879 if (try Archive.isArchive(lib.path, fat_arch)) {
880 try self.parseArchive(lib, must_link, fat_arch);
881 } else if (try Dylib.isDylib(lib.path, fat_arch)) {
882 _ = try self.parseDylib(lib, true, fat_arch);
883 } else return error.UnknownFileType;
884 } else if (try Archive.isArchive(lib.path, null)) {
885 try self.parseArchive(lib, must_link, null);
886 } else if (try Dylib.isDylib(lib.path, null)) {
887 _ = try self.parseDylib(lib, true, null);
888 } else {
889 _ = self.parseTbd(lib, true) catch |err| switch (err) {
890 error.MalformedTbd => return error.UnknownFileType,
891 else => |e| return e,
892 };
849fn parseFatFile(self: *MachO, file: std.fs.File, path: []const u8) !?fat.Arch {
850 const fat_h = fat.readFatHeader(file) catch return null;
851 if (fat_h.magic != macho.FAT_MAGIC and fat_h.magic != macho.FAT_MAGIC_64) return null;
852 var fat_archs_buffer: [2]fat.Arch = undefined;
853 const fat_archs = try fat.parseArchs(file, fat_h, &fat_archs_buffer);
854 const cpu_arch = self.getTarget().cpu.arch;
855 for (fat_archs) |arch| {
856 if (arch.tag == cpu_arch) return arch;
893857 }
858 try self.reportParseError(path, "missing arch in universal file: expected {s}", .{
859 @tagName(cpu_arch),
860 });
861 return error.MissingCpuArch;
862}
863
864pub fn readMachHeader(file: std.fs.File, offset: usize) !macho.mach_header_64 {
865 var buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined;
866 const nread = try file.preadAll(&buffer, offset);
867 if (nread != buffer.len) return error.InputOutput;
868 const hdr = @as(*align(1) const macho.mach_header_64, @ptrCast(&buffer)).*;
869 return hdr;
894870}
895871
896fn parseObject(self: *MachO, path: []const u8) ParseError!void {
872pub fn readArMagic(file: std.fs.File, offset: usize, buffer: *[Archive.SARMAG]u8) ![]const u8 {
873 const nread = try file.preadAll(buffer, offset);
874 if (nread != buffer.len) return error.InputOutput;
875 return buffer[0..Archive.SARMAG];
876}
877
878fn addObject(self: *MachO, path: []const u8, handle: File.HandleIndex, offset: u64) !void {
897879 const tracy = trace(@src());
898880 defer tracy.end();
899881
900882 const gpa = self.base.comp.gpa;
901 const file = try fs.cwd().openFile(path, .{});
902 const handle = try self.addFileHandle(file);
903883 const mtime: u64 = mtime: {
884 const file = self.getFileHandle(handle);
904885 const stat = file.stat() catch break :mtime 0;
905886 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
906887 };
907888 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
908 self.files.set(index, .{
909 .object = .{
910 .offset = 0, // TODO FAT objects
911 .path = try gpa.dupe(u8, path),
912 .file_handle = handle,
913 .mtime = mtime,
914 .index = index,
915 },
916 });
889 self.files.set(index, .{ .object = .{
890 .offset = offset,
891 .path = try gpa.dupe(u8, path),
892 .file_handle = handle,
893 .mtime = mtime,
894 .index = index,
895 } });
917896 try self.objects.append(gpa, index);
918
919 const object = self.getFile(index).?.object;
920 try object.parse(self);
921897}
922898
923pub fn parseFatLibrary(self: *MachO, path: []const u8) !fat.Arch {
924 var buffer: [2]fat.Arch = undefined;
925 const fat_archs = try fat.parseArchs(path, &buffer);
926 const cpu_arch = self.getTarget().cpu.arch;
927 for (fat_archs) |arch| {
928 if (arch.tag == cpu_arch) return arch;
899pub fn parseInputFiles(self: *MachO) !void {
900 const tracy = trace(@src());
901 defer tracy.end();
902
903 for (self.objects.items) |index| {
904 self.getFile(index).?.parse(self) catch |err| switch (err) {
905 error.MalformedObject,
906 error.InvalidCpuArch,
907 error.InvalidTarget,
908 => {}, // already reported
909 else => |e| try self.reportParseError2(index, "unexpected error: parsing input file failed with error {s}", .{@errorName(e)}),
910 };
911 }
912 for (self.dylibs.items) |index| {
913 self.getFile(index).?.parse(self) catch |err| switch (err) {
914 error.MalformedDylib,
915 error.InvalidCpuArch,
916 error.InvalidTarget,
917 => {}, // already reported
918 else => |e| try self.reportParseError2(index, "unexpected error: parsing input file failed with error {s}", .{@errorName(e)}),
919 };
929920 }
930 try self.reportParseError(path, "missing arch in universal file: expected {s}", .{@tagName(cpu_arch)});
931 return error.InvalidCpuArch;
932921}
933922
934fn parseArchive(self: *MachO, lib: SystemLib, must_link: bool, fat_arch: ?fat.Arch) ParseError!void {
923fn addArchive(self: *MachO, lib: SystemLib, must_link: bool, handle: File.HandleIndex, fat_arch: ?fat.Arch) !void {
935924 const tracy = trace(@src());
936925 defer tracy.end();
937926
938927 const gpa = self.base.comp.gpa;
939928
940 const file = try fs.cwd().openFile(lib.path, .{});
941 const handle = try self.addFileHandle(file);
942
943929 var archive = Archive{};
944930 defer archive.deinit(gpa);
945 try archive.parse(self, lib.path, handle, fat_arch);
931 try archive.unpack(self, lib.path, handle, fat_arch);
946932
947 var has_parse_error = false;
948 for (archive.objects.items) |extracted| {
949 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
950 self.files.set(index, .{ .object = extracted });
933 for (archive.objects.items) |unpacked| {
934 const index: File.Index = @intCast(try self.files.addOne(gpa));
935 self.files.set(index, .{ .object = unpacked });
951936 const object = &self.files.items(.data)[index].object;
952937 object.index = index;
953938 object.alive = must_link or lib.needed; // TODO: or self.options.all_load;
954939 object.hidden = lib.hidden;
955 object.parse(self) catch |err| switch (err) {
956 error.MalformedObject,
957 error.InvalidCpuArch,
958 error.InvalidTarget,
959 => has_parse_error = true,
960 else => |e| return e,
961 };
962940 try self.objects.append(gpa, index);
963
964 // Finally, we do a post-parse check for -ObjC to see if we need to force load this member
965 // anyhow.
966 object.alive = object.alive or (self.force_load_objc and object.hasObjc());
967941 }
968 if (has_parse_error) return error.MalformedArchive;
969942}
970943
971fn parseDylib(self: *MachO, lib: SystemLib, explicit: bool, fat_arch: ?fat.Arch) ParseError!File.Index {
944fn addDylib(self: *MachO, lib: SystemLib, explicit: bool, handle: File.HandleIndex, offset: u64) !File.Index {
972945 const tracy = trace(@src());
973946 defer tracy.end();
974947
975948 const gpa = self.base.comp.gpa;
976949
977 const file = try fs.cwd().openFile(lib.path, .{});
978 defer file.close();
979
980 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
950 const index: File.Index = @intCast(try self.files.addOne(gpa));
981951 self.files.set(index, .{ .dylib = .{
952 .offset = offset,
953 .file_handle = handle,
954 .tag = .dylib,
982955 .path = try gpa.dupe(u8, lib.path),
983956 .index = index,
984957 .needed = lib.needed,
985958 .weak = lib.weak,
986959 .reexport = lib.reexport,
987960 .explicit = explicit,
961 .umbrella = index,
988962 } });
989 const dylib = &self.files.items(.data)[index].dylib;
990 try dylib.parse(self, file, fat_arch);
991
992963 try self.dylibs.append(gpa, index);
993964
994965 return index;
995966}
996967
997fn parseTbd(self: *MachO, lib: SystemLib, explicit: bool) ParseError!File.Index {
968fn addTbd(self: *MachO, lib: SystemLib, explicit: bool, handle: File.HandleIndex) !File.Index {
998969 const tracy = trace(@src());
999970 defer tracy.end();
1000971
1001972 const gpa = self.base.comp.gpa;
1002 const file = try fs.cwd().openFile(lib.path, .{});
1003 defer file.close();
1004
1005 var lib_stub = LibStub.loadFromFile(gpa, file) catch return error.MalformedTbd; // TODO actually handle different errors
1006 defer lib_stub.deinit();
1007
1008 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
973 const index: File.Index = @intCast(try self.files.addOne(gpa));
1009974 self.files.set(index, .{ .dylib = .{
975 .offset = 0,
976 .file_handle = handle,
977 .tag = .tbd,
1010978 .path = try gpa.dupe(u8, lib.path),
1011979 .index = index,
1012980 .needed = lib.needed,
1013981 .weak = lib.weak,
1014982 .reexport = lib.reexport,
1015983 .explicit = explicit,
984 .umbrella = index,
1016985 } });
1017 const dylib = &self.files.items(.data)[index].dylib;
1018 try dylib.parseTbd(self.getTarget().cpu.arch, self.platform, lib_stub, self);
1019986 try self.dylibs.append(gpa, index);
1020987
1021988 return index;
......@@ -1092,6 +1059,8 @@ fn parseDependentDylibs(self: *MachO) !void {
10921059 const tracy = trace(@src());
10931060 defer tracy.end();
10941061
1062 if (self.dylibs.items.len == 0) return;
1063
10951064 const gpa = self.base.comp.gpa;
10961065 const lib_dirs = self.lib_dirs;
10971066 const framework_dirs = self.framework_dirs;
......@@ -1108,7 +1077,7 @@ fn parseDependentDylibs(self: *MachO) !void {
11081077 while (index < self.dylibs.items.len) : (index += 1) {
11091078 const dylib_index = self.dylibs.items[index];
11101079
1111 var dependents = std.ArrayList(struct { id: Dylib.Id, file: File.Index }).init(gpa);
1080 var dependents = std.ArrayList(File.Index).init(gpa);
11121081 defer dependents.deinit();
11131082 try dependents.ensureTotalCapacityPrecise(self.getFile(dylib_index).?.dylib.dependents.items.len);
11141083
......@@ -1199,38 +1168,34 @@ fn parseDependentDylibs(self: *MachO) !void {
11991168 .path = full_path,
12001169 .weak = is_weak,
12011170 };
1171 const file = try std.fs.cwd().openFile(lib.path, .{});
1172 const fh = try self.addFileHandle(file);
1173 const fat_arch = try self.parseFatFile(file, lib.path);
1174 const offset = if (fat_arch) |fa| fa.offset else 0;
12021175 const file_index = file_index: {
1203 if (try fat.isFatLibrary(lib.path)) {
1204 const fat_arch = try self.parseFatLibrary(lib.path);
1205 if (try Dylib.isDylib(lib.path, fat_arch)) {
1206 break :file_index try self.parseDylib(lib, false, fat_arch);
1207 } else break :file_index @as(File.Index, 0);
1208 } else if (try Dylib.isDylib(lib.path, null)) {
1209 break :file_index try self.parseDylib(lib, false, null);
1210 } else {
1211 const file_index = self.parseTbd(lib, false) catch |err| switch (err) {
1212 error.MalformedTbd => @as(File.Index, 0),
1213 else => |e| return e,
1214 };
1215 break :file_index file_index;
1176 if (readMachHeader(file, offset) catch null) |h| blk: {
1177 if (h.magic != macho.MH_MAGIC_64) break :blk;
1178 switch (h.filetype) {
1179 macho.MH_DYLIB => break :file_index try self.addDylib(lib, false, fh, offset),
1180 else => break :file_index @as(File.Index, 0),
1181 }
12161182 }
1183 break :file_index try self.addTbd(lib, false, fh);
12171184 };
1218 dependents.appendAssumeCapacity(.{ .id = id, .file = file_index });
1185 dependents.appendAssumeCapacity(file_index);
12191186 }
12201187
12211188 const dylib = self.getFile(dylib_index).?.dylib;
1222 for (dependents.items) |entry| {
1223 const id = entry.id;
1224 const file_index = entry.file;
1189 for (dylib.dependents.items, dependents.items) |id, file_index| {
12251190 if (self.getFile(file_index)) |file| {
12261191 const dep_dylib = file.dylib;
1192 try dep_dylib.parse(self); // TODO in parallel
12271193 dep_dylib.hoisted = self.isHoisted(id.name);
1228 if (self.getFile(dep_dylib.umbrella) == null) {
1229 dep_dylib.umbrella = dylib.umbrella;
1230 }
1194 dep_dylib.umbrella = dylib.umbrella;
12311195 if (!dep_dylib.hoisted) {
12321196 const umbrella = dep_dylib.getUmbrella(self);
12331197 for (dep_dylib.exports.items(.name), dep_dylib.exports.items(.flags)) |off, flags| {
1198 // TODO rethink this entire algorithm
12341199 try umbrella.addExport(gpa, dep_dylib.getString(off), flags);
12351200 }
12361201 try umbrella.rpaths.ensureUnusedCapacity(gpa, dep_dylib.rpaths.keys().len);
......@@ -1238,15 +1203,13 @@ fn parseDependentDylibs(self: *MachO) !void {
12381203 umbrella.rpaths.putAssumeCapacity(try gpa.dupe(u8, rpath), {});
12391204 }
12401205 }
1241 } else {
1242 try self.reportDependencyError(
1243 dylib.getUmbrella(self).index,
1244 id.name,
1245 "unable to resolve dependency",
1246 .{},
1247 );
1248 has_errors = true;
1249 }
1206 } else try self.reportDependencyError(
1207 dylib.getUmbrella(self).index,
1208 id.name,
1209 "unable to resolve dependency",
1210 .{},
1211 );
1212 has_errors = true;
12501213 }
12511214 }
12521215
......@@ -1533,8 +1496,8 @@ fn reportUndefs(self: *MachO) !void {
15331496 const notes = entry.value_ptr.*;
15341497 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
15351498
1536 var err = try self.addErrorWithNotes(nnotes);
1537 try err.addMsg(self, "undefined symbol: {s}", .{undef_sym.getName(self)});
1499 var err = try self.base.addErrorWithNotes(nnotes);
1500 try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)});
15381501 has_undefs = true;
15391502
15401503 var inote: usize = 0;
......@@ -1542,12 +1505,12 @@ fn reportUndefs(self: *MachO) !void {
15421505 const note = notes.items[inote];
15431506 const file = self.getFile(note.file).?;
15441507 const atom = note.getAtom(self).?;
1545 try err.addNote(self, "referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) });
1508 try err.addNote("referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) });
15461509 }
15471510
15481511 if (notes.items.len > max_notes) {
15491512 const remaining = notes.items.len - max_notes;
1550 try err.addNote(self, "referenced {d} more times", .{remaining});
1513 try err.addNote("referenced {d} more times", .{remaining});
15511514 }
15521515 }
15531516 if (has_undefs) return error.HasUndefinedSymbols;
......@@ -3323,13 +3286,13 @@ fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !vo
33233286
33243287 const mem_capacity = self.allocatedSizeVirtual(seg.vmaddr);
33253288 if (needed_size > mem_capacity) {
3326 var err = try self.addErrorWithNotes(2);
3327 try err.addMsg(self, "fatal linker error: cannot expand segment seg({d})({s}) in virtual memory", .{
3289 var err = try self.base.addErrorWithNotes(2);
3290 try err.addMsg("fatal linker error: cannot expand segment seg({d})({s}) in virtual memory", .{
33283291 seg_id,
33293292 seg.segName(),
33303293 });
3331 try err.addNote(self, "TODO: emit relocations to memory locations in self-hosted backends", .{});
3332 try err.addNote(self, "as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
3294 try err.addNote("TODO: emit relocations to memory locations in self-hosted backends", .{});
3295 try err.addNote("as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
33333296 }
33343297
33353298 seg.vmsize = needed_size;
......@@ -3618,65 +3581,15 @@ pub fn eatPrefix(path: []const u8, prefix: []const u8) ?[]const u8 {
36183581 return null;
36193582}
36203583
3621const ErrorWithNotes = struct {
3622 /// Allocated index in comp.link_errors array.
3623 index: usize,
3624
3625 /// Next available note slot.
3626 note_slot: usize = 0,
3627
3628 pub fn addMsg(
3629 err: ErrorWithNotes,
3630 macho_file: *MachO,
3631 comptime format: []const u8,
3632 args: anytype,
3633 ) error{OutOfMemory}!void {
3634 const comp = macho_file.base.comp;
3635 const gpa = comp.gpa;
3636 const err_msg = &comp.link_errors.items[err.index];
3637 err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
3638 }
3639
3640 pub fn addNote(
3641 err: *ErrorWithNotes,
3642 macho_file: *MachO,
3643 comptime format: []const u8,
3644 args: anytype,
3645 ) error{OutOfMemory}!void {
3646 const comp = macho_file.base.comp;
3647 const gpa = comp.gpa;
3648 const err_msg = &comp.link_errors.items[err.index];
3649 assert(err.note_slot < err_msg.notes.len);
3650 err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };
3651 err.note_slot += 1;
3652 }
3653};
3654
3655pub fn addErrorWithNotes(self: *MachO, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
3656 const comp = self.base.comp;
3657 const gpa = comp.gpa;
3658 try comp.link_errors.ensureUnusedCapacity(gpa, 1);
3659 return self.addErrorWithNotesAssumeCapacity(note_count);
3660}
3661
3662fn addErrorWithNotesAssumeCapacity(self: *MachO, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
3663 const comp = self.base.comp;
3664 const gpa = comp.gpa;
3665 const index = comp.link_errors.items.len;
3666 const err = comp.link_errors.addOneAssumeCapacity();
3667 err.* = .{ .msg = undefined, .notes = try gpa.alloc(link.File.ErrorMsg, note_count) };
3668 return .{ .index = index };
3669}
3670
36713584pub fn reportParseError(
36723585 self: *MachO,
36733586 path: []const u8,
36743587 comptime format: []const u8,
36753588 args: anytype,
36763589) error{OutOfMemory}!void {
3677 var err = try self.addErrorWithNotes(1);
3678 try err.addMsg(self, format, args);
3679 try err.addNote(self, "while parsing {s}", .{path});
3590 var err = try self.base.addErrorWithNotes(1);
3591 try err.addMsg(format, args);
3592 try err.addNote("while parsing {s}", .{path});
36803593}
36813594
36823595pub fn reportParseError2(
......@@ -3685,9 +3598,9 @@ pub fn reportParseError2(
36853598 comptime format: []const u8,
36863599 args: anytype,
36873600) error{OutOfMemory}!void {
3688 var err = try self.addErrorWithNotes(1);
3689 try err.addMsg(self, format, args);
3690 try err.addNote(self, "while parsing {}", .{self.getFile(file_index).?.fmtPath()});
3601 var err = try self.base.addErrorWithNotes(1);
3602 try err.addMsg(format, args);
3603 try err.addNote("while parsing {}", .{self.getFile(file_index).?.fmtPath()});
36913604}
36923605
36933606fn reportMissingLibraryError(
......@@ -3696,10 +3609,10 @@ fn reportMissingLibraryError(
36963609 comptime format: []const u8,
36973610 args: anytype,
36983611) error{OutOfMemory}!void {
3699 var err = try self.addErrorWithNotes(checked_paths.len);
3700 try err.addMsg(self, format, args);
3612 var err = try self.base.addErrorWithNotes(checked_paths.len);
3613 try err.addMsg(format, args);
37013614 for (checked_paths) |path| {
3702 try err.addNote(self, "tried {s}", .{path});
3615 try err.addNote("tried {s}", .{path});
37033616 }
37043617}
37053618
......@@ -3711,12 +3624,12 @@ fn reportMissingDependencyError(
37113624 comptime format: []const u8,
37123625 args: anytype,
37133626) error{OutOfMemory}!void {
3714 var err = try self.addErrorWithNotes(2 + checked_paths.len);
3715 try err.addMsg(self, format, args);
3716 try err.addNote(self, "while resolving {s}", .{path});
3717 try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()});
3627 var err = try self.base.addErrorWithNotes(2 + checked_paths.len);
3628 try err.addMsg(format, args);
3629 try err.addNote("while resolving {s}", .{path});
3630 try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()});
37183631 for (checked_paths) |p| {
3719 try err.addNote(self, "tried {s}", .{p});
3632 try err.addNote("tried {s}", .{p});
37203633 }
37213634}
37223635
......@@ -3727,16 +3640,16 @@ fn reportDependencyError(
37273640 comptime format: []const u8,
37283641 args: anytype,
37293642) error{OutOfMemory}!void {
3730 var err = try self.addErrorWithNotes(2);
3731 try err.addMsg(self, format, args);
3732 try err.addNote(self, "while parsing {s}", .{path});
3733 try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()});
3643 var err = try self.base.addErrorWithNotes(2);
3644 try err.addMsg(format, args);
3645 try err.addNote("while parsing {s}", .{path});
3646 try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()});
37343647}
37353648
37363649pub fn reportUnexpectedError(self: *MachO, comptime format: []const u8, args: anytype) error{OutOfMemory}!void {
3737 var err = try self.addErrorWithNotes(1);
3738 try err.addMsg(self, format, args);
3739 try err.addNote(self, "please report this as a linker bug on https://github.com/ziglang/zig/issues/new/choose", .{});
3650 var err = try self.base.addErrorWithNotes(1);
3651 try err.addMsg(format, args);
3652 try err.addNote("please report this as a linker bug on https://github.com/ziglang/zig/issues/new/choose", .{});
37403653}
37413654
37423655fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
......@@ -3752,20 +3665,20 @@ fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
37523665 const notes = entry.value_ptr.*;
37533666 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
37543667
3755 var err = try self.addErrorWithNotes(nnotes + 1);
3756 try err.addMsg(self, "duplicate symbol definition: {s}", .{sym.getName(self)});
3757 try err.addNote(self, "defined by {}", .{sym.getFile(self).?.fmtPath()});
3668 var err = try self.base.addErrorWithNotes(nnotes + 1);
3669 try err.addMsg("duplicate symbol definition: {s}", .{sym.getName(self)});
3670 try err.addNote("defined by {}", .{sym.getFile(self).?.fmtPath()});
37583671 has_dupes = true;
37593672
37603673 var inote: usize = 0;
37613674 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
37623675 const file = self.getFile(notes.items[inote]).?;
3763 try err.addNote(self, "defined by {}", .{file.fmtPath()});
3676 try err.addNote("defined by {}", .{file.fmtPath()});
37643677 }
37653678
37663679 if (notes.items.len > max_notes) {
37673680 const remaining = notes.items.len - max_notes;
3768 try err.addNote(self, "defined {d} more times", .{remaining});
3681 try err.addNote("defined {d} more times", .{remaining});
37693682 }
37703683 }
37713684 if (has_dupes) return error.HasDuplicates;
src/link/MachO/Archive.zig+1-12
......@@ -1,21 +1,10 @@
11objects: std.ArrayListUnmanaged(Object) = .{},
22
3pub fn isArchive(path: []const u8, fat_arch: ?fat.Arch) !bool {
4 const file = try std.fs.cwd().openFile(path, .{});
5 defer file.close();
6 if (fat_arch) |arch| {
7 try file.seekTo(arch.offset);
8 }
9 const magic = file.reader().readBytesNoEof(SARMAG) catch return false;
10 if (!mem.eql(u8, &magic, ARMAG)) return false;
11 return true;
12}
13
143pub fn deinit(self: *Archive, allocator: Allocator) void {
154 self.objects.deinit(allocator);
165}
176
18pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index: File.HandleIndex, fat_arch: ?fat.Arch) !void {
7pub fn unpack(self: *Archive, macho_file: *MachO, path: []const u8, handle_index: File.HandleIndex, fat_arch: ?fat.Arch) !void {
198 const gpa = macho_file.base.comp.gpa;
209
2110 var arena = std.heap.ArenaAllocator.init(gpa);
src/link/MachO/Atom.zig+4-4
......@@ -906,15 +906,15 @@ const x86_64 = struct {
906906 encode(&.{inst}, code) catch return error.RelaxFail;
907907 },
908908 else => |x| {
909 var err = try macho_file.addErrorWithNotes(2);
910 try err.addMsg(macho_file, "{s}: 0x{x}: 0x{x}: failed to relax relocation of type {}", .{
909 var err = try macho_file.base.addErrorWithNotes(2);
910 try err.addMsg("{s}: 0x{x}: 0x{x}: failed to relax relocation of type {}", .{
911911 self.getName(macho_file),
912912 self.getAddress(macho_file),
913913 rel.offset,
914914 rel.fmtPretty(.x86_64),
915915 });
916 try err.addNote(macho_file, "expected .mov instruction but found .{s}", .{@tagName(x)});
917 try err.addNote(macho_file, "while parsing {}", .{self.getFile(macho_file).fmtPath()});
916 try err.addNote("expected .mov instruction but found .{s}", .{@tagName(x)});
917 try err.addNote("while parsing {}", .{self.getFile(macho_file).fmtPath()});
918918 return error.RelaxFailUnexpectedInstruction;
919919 },
920920 }
src/link/MachO/Dylib.zig+23-22
......@@ -1,5 +1,9 @@
1/// Non-zero for fat dylibs
2offset: u64,
13path: []const u8,
24index: File.Index,
5file_handle: File.HandleIndex,
6tag: enum { dylib, tbd },
37
48exports: std.MultiArrayList(Export) = .{},
59strtab: std.ArrayListUnmanaged(u8) = .{},
......@@ -11,7 +15,7 @@ symbols_extra: std.ArrayListUnmanaged(u32) = .{},
1115globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},
1216dependents: std.ArrayListUnmanaged(Id) = .{},
1317rpaths: std.StringArrayHashMapUnmanaged(void) = .{},
14umbrella: File.Index = 0,
18umbrella: File.Index,
1519platform: ?MachO.Platform = null,
1620
1721needed: bool,
......@@ -23,16 +27,6 @@ referenced: bool = false,
2327
2428output_symtab_ctx: MachO.SymtabCtx = .{},
2529
26pub fn isDylib(path: []const u8, fat_arch: ?fat.Arch) !bool {
27 const file = try std.fs.cwd().openFile(path, .{});
28 defer file.close();
29 if (fat_arch) |arch| {
30 try file.seekTo(arch.offset);
31 }
32 const header = file.reader().readStruct(macho.mach_header_64) catch return false;
33 return header.filetype == macho.MH_DYLIB;
34}
35
3630pub fn deinit(self: *Dylib, allocator: Allocator) void {
3731 allocator.free(self.path);
3832 self.exports.deinit(allocator);
......@@ -51,12 +45,21 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {
5145 self.rpaths.deinit(allocator);
5246}
5347
54pub fn parse(self: *Dylib, macho_file: *MachO, file: std.fs.File, fat_arch: ?fat.Arch) !void {
48pub fn parse(self: *Dylib, macho_file: *MachO) !void {
49 switch (self.tag) {
50 .tbd => try self.parseTbd(macho_file),
51 .dylib => try self.parseBinary(macho_file),
52 }
53 try self.initSymbols(macho_file);
54}
55
56fn parseBinary(self: *Dylib, macho_file: *MachO) !void {
5557 const tracy = trace(@src());
5658 defer tracy.end();
5759
5860 const gpa = macho_file.base.comp.gpa;
59 const offset = if (fat_arch) |ar| ar.offset else 0;
61 const file = macho_file.getFileHandle(self.file_handle);
62 const offset = self.offset;
6063
6164 log.debug("parsing dylib from binary: {s}", .{self.path});
6265
......@@ -258,13 +261,7 @@ fn parseTrie(self: *Dylib, data: []const u8, macho_file: *MachO) !void {
258261 try self.parseTrieNode(&it, gpa, arena.allocator(), "");
259262}
260263
261pub fn parseTbd(
262 self: *Dylib,
263 cpu_arch: std.Target.Cpu.Arch,
264 platform: MachO.Platform,
265 lib_stub: LibStub,
266 macho_file: *MachO,
267) !void {
264fn parseTbd(self: *Dylib, macho_file: *MachO) !void {
268265 const tracy = trace(@src());
269266 defer tracy.end();
270267
......@@ -272,6 +269,9 @@ pub fn parseTbd(
272269
273270 log.debug("parsing dylib from stub: {s}", .{self.path});
274271
272 const file = macho_file.getFileHandle(self.file_handle);
273 var lib_stub = LibStub.loadFromFile(gpa, file) catch return error.NotLibStub;
274 defer lib_stub.deinit();
275275 const umbrella_lib = lib_stub.inner[0];
276276
277277 {
......@@ -290,7 +290,8 @@ pub fn parseTbd(
290290
291291 log.debug(" (install_name '{s}')", .{umbrella_lib.installName()});
292292
293 self.platform = platform;
293 const cpu_arch = macho_file.getTarget().cpu.arch;
294 self.platform = macho_file.platform;
294295
295296 var matcher = try TargetMatcher.init(gpa, cpu_arch, self.platform.?.toApplePlatform());
296297 defer matcher.deinit();
......@@ -495,7 +496,7 @@ fn addObjCExport(
495496 try self.addExport(allocator, full_name, .{});
496497}
497498
498pub fn initSymbols(self: *Dylib, macho_file: *MachO) !void {
499fn initSymbols(self: *Dylib, macho_file: *MachO) !void {
499500 const gpa = macho_file.base.comp.gpa;
500501
501502 const nsyms = self.exports.items(.name).len;
src/link/MachO/Object.zig+4-8
......@@ -38,13 +38,6 @@ compact_unwind_ctx: CompactUnwindCtx = .{},
3838output_symtab_ctx: MachO.SymtabCtx = .{},
3939output_ar_state: Archive.ArState = .{},
4040
41pub fn isObject(path: []const u8) !bool {
42 const file = try std.fs.cwd().openFile(path, .{});
43 defer file.close();
44 const header = file.reader().readStruct(macho.mach_header_64) catch return false;
45 return header.filetype == macho.MH_OBJECT;
46}
47
4841pub fn deinit(self: *Object, allocator: Allocator) void {
4942 if (self.in_archive) |*ar| allocator.free(ar.path);
5043 allocator.free(self.path);
......@@ -273,6 +266,9 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
273266 atom.flags.alive = false;
274267 }
275268 }
269
270 // Finally, we do a post-parse check for -ObjC to see if we need to force load this member anyhow.
271 self.alive = self.alive or (macho_file.force_load_objc and self.hasObjC());
276272}
277273
278274pub fn isCstringLiteral(sect: macho.section_64) bool {
......@@ -2325,7 +2321,7 @@ fn hasSymbolStabs(self: Object) bool {
23252321 return self.stab_files.items.len > 0;
23262322}
23272323
2328pub fn hasObjc(self: Object) bool {
2324fn hasObjC(self: Object) bool {
23292325 for (self.symtab.items(.nlist)) |nlist| {
23302326 const name = self.getString(nlist.n_strx);
23312327 if (mem.startsWith(u8, name, "_OBJC_CLASS_$_")) return true;
src/link/MachO/fat.zig+17-16
......@@ -8,11 +8,17 @@ const native_endian = builtin.target.cpu.arch.endian();
88
99const MachO = @import("../MachO.zig");
1010
11pub fn isFatLibrary(path: []const u8) !bool {
12 const file = try std.fs.cwd().openFile(path, .{});
13 defer file.close();
14 const hdr = file.reader().readStructEndian(macho.fat_header, .big) catch return false;
15 return hdr.magic == macho.FAT_MAGIC;
11pub fn readFatHeader(file: std.fs.File) !macho.fat_header {
12 return readFatHeaderGeneric(macho.fat_header, file, 0);
13}
14
15fn readFatHeaderGeneric(comptime Hdr: type, file: std.fs.File, offset: usize) !Hdr {
16 var buffer: [@sizeOf(Hdr)]u8 = undefined;
17 const nread = try file.preadAll(&buffer, offset);
18 if (nread != buffer.len) return error.InputOutput;
19 var hdr = @as(*align(1) const Hdr, @ptrCast(&buffer)).*;
20 mem.byteSwapAllFields(Hdr, &hdr);
21 return hdr;
1622}
1723
1824pub const Arch = struct {
......@@ -21,17 +27,12 @@ pub const Arch = struct {
2127 size: u32,
2228};
2329
24pub fn parseArchs(path: []const u8, buffer: *[2]Arch) ![]const Arch {
25 const file = try std.fs.cwd().openFile(path, .{});
26 defer file.close();
27 const reader = file.reader();
28 const fat_header = try reader.readStructEndian(macho.fat_header, .big);
29 assert(fat_header.magic == macho.FAT_MAGIC);
30
30pub fn parseArchs(file: std.fs.File, fat_header: macho.fat_header, out: *[2]Arch) ![]const Arch {
3131 var count: usize = 0;
3232 var fat_arch_index: u32 = 0;
33 while (fat_arch_index < fat_header.nfat_arch) : (fat_arch_index += 1) {
34 const fat_arch = try reader.readStructEndian(macho.fat_arch, .big);
33 while (fat_arch_index < fat_header.nfat_arch and count < out.len) : (fat_arch_index += 1) {
34 const offset = @sizeOf(macho.fat_header) + @sizeOf(macho.fat_arch) * fat_arch_index;
35 const fat_arch = try readFatHeaderGeneric(macho.fat_arch, file, offset);
3536 // If we come across an architecture that we do not know how to handle, that's
3637 // fine because we can keep looking for one that might match.
3738 const arch: std.Target.Cpu.Arch = switch (fat_arch.cputype) {
......@@ -39,9 +40,9 @@ pub fn parseArchs(path: []const u8, buffer: *[2]Arch) ![]const Arch {
3940 macho.CPU_TYPE_X86_64 => if (fat_arch.cpusubtype == macho.CPU_SUBTYPE_X86_64_ALL) .x86_64 else continue,
4041 else => continue,
4142 };
42 buffer[count] = .{ .tag = arch, .offset = fat_arch.offset, .size = fat_arch.size };
43 out[count] = .{ .tag = arch, .offset = fat_arch.offset, .size = fat_arch.size };
4344 count += 1;
4445 }
4546
46 return buffer[0..count];
47 return out[0..count];
4748}
src/link/MachO/file.zig+15
......@@ -335,6 +335,21 @@ pub const File = union(enum) {
335335 };
336336 }
337337
338 pub fn parse(file: File, macho_file: *MachO) !void {
339 return switch (file) {
340 .internal, .zig_object => unreachable,
341 .object => |x| x.parse(macho_file),
342 .dylib => |x| x.parse(macho_file),
343 };
344 }
345
346 pub fn parseAr(file: File, macho_file: *MachO) !void {
347 return switch (file) {
348 .internal, .zig_object, .dylib => unreachable,
349 .object => |x| x.parseAr(macho_file),
350 };
351 }
352
338353 pub const Index = u32;
339354
340355 pub const Entry = union(enum) {
src/link/MachO/relocatable.zig+22-84
......@@ -27,22 +27,21 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c
2727 }
2828
2929 for (positionals.items) |obj| {
30 macho_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
31 error.MalformedObject,
32 error.MalformedArchive,
33 error.InvalidCpuArch,
34 error.InvalidTarget,
35 => continue, // already reported
36 error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an object file", .{}),
30 macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
31 error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an input file", .{}),
3732 else => |e| try macho_file.reportParseError(
3833 obj.path,
39 "unexpected error: parsing input file failed with error {s}",
34 "unexpected error: reading input file failed with error {s}",
4035 .{@errorName(e)},
4136 ),
4237 };
4338 }
4439
45 if (comp.link_errors.items.len > 0) return error.FlushFailure;
40 if (macho_file.base.hasErrors()) return error.FlushFailure;
41
42 try macho_file.parseInputFiles();
43
44 if (macho_file.base.hasErrors()) return error.FlushFailure;
4645
4746 try macho_file.resolveSymbols();
4847 try macho_file.dedupLiterals();
......@@ -93,22 +92,21 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
9392 }
9493
9594 for (positionals.items) |obj| {
96 parsePositional(macho_file, obj.path) catch |err| switch (err) {
97 error.MalformedObject,
98 error.MalformedArchive,
99 error.InvalidCpuArch,
100 error.InvalidTarget,
101 => continue, // already reported
102 error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an object file", .{}),
95 macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
96 error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an input file", .{}),
10397 else => |e| try macho_file.reportParseError(
10498 obj.path,
105 "unexpected error: parsing input file failed with error {s}",
99 "unexpected error: reading input file failed with error {s}",
106100 .{@errorName(e)},
107101 ),
108102 };
109103 }
110104
111 if (comp.link_errors.items.len > 0) return error.FlushFailure;
105 if (macho_file.base.hasErrors()) return error.FlushFailure;
106
107 try parseInputFilesAr(macho_file);
108
109 if (macho_file.base.hasErrors()) return error.FlushFailure;
112110
113111 // First, we flush relocatable object file generated with our backends.
114112 if (macho_file.getZigObject()) |zo| {
......@@ -225,79 +223,19 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
225223 try macho_file.base.file.?.setEndPos(total_size);
226224 try macho_file.base.file.?.pwriteAll(buffer.items, 0);
227225
228 if (comp.link_errors.items.len > 0) return error.FlushFailure;
226 if (macho_file.base.hasErrors()) return error.FlushFailure;
229227}
230228
231fn parsePositional(macho_file: *MachO, path: []const u8) MachO.ParseError!void {
229fn parseInputFilesAr(macho_file: *MachO) !void {
232230 const tracy = trace(@src());
233231 defer tracy.end();
234 if (try Object.isObject(path)) {
235 try parseObject(macho_file, path);
236 } else if (try fat.isFatLibrary(path)) {
237 const fat_arch = try macho_file.parseFatLibrary(path);
238 if (try Archive.isArchive(path, fat_arch)) {
239 try parseArchive(macho_file, path, fat_arch);
240 } else return error.UnknownFileType;
241 } else if (try Archive.isArchive(path, null)) {
242 try parseArchive(macho_file, path, null);
243 } else return error.UnknownFileType;
244}
245232
246fn parseObject(macho_file: *MachO, path: []const u8) MachO.ParseError!void {
247 const tracy = trace(@src());
248 defer tracy.end();
249
250 const gpa = macho_file.base.comp.gpa;
251 const file = try std.fs.cwd().openFile(path, .{});
252 errdefer file.close();
253 const handle = try macho_file.addFileHandle(file);
254 const mtime: u64 = mtime: {
255 const stat = file.stat() catch break :mtime 0;
256 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
257 };
258 const index = @as(File.Index, @intCast(try macho_file.files.addOne(gpa)));
259 macho_file.files.set(index, .{
260 .object = .{
261 .offset = 0, // TODO FAT objects
262 .path = try gpa.dupe(u8, path),
263 .file_handle = handle,
264 .mtime = mtime,
265 .index = index,
266 },
267 });
268 try macho_file.objects.append(gpa, index);
269
270 const object = macho_file.getFile(index).?.object;
271 try object.parseAr(macho_file);
272}
273
274fn parseArchive(macho_file: *MachO, path: []const u8, fat_arch: ?fat.Arch) MachO.ParseError!void {
275 const tracy = trace(@src());
276 defer tracy.end();
277
278 const gpa = macho_file.base.comp.gpa;
279
280 const file = try std.fs.cwd().openFile(path, .{});
281 errdefer file.close();
282 const handle = try macho_file.addFileHandle(file);
283
284 var archive = Archive{};
285 defer archive.deinit(gpa);
286 try archive.parse(macho_file, path, handle, fat_arch);
287
288 var has_parse_error = false;
289 for (archive.objects.items) |extracted| {
290 const index = @as(File.Index, @intCast(try macho_file.files.addOne(gpa)));
291 macho_file.files.set(index, .{ .object = extracted });
292 const object = &macho_file.files.items(.data)[index].object;
293 object.index = index;
294 object.parseAr(macho_file) catch |err| switch (err) {
295 error.InvalidCpuArch => has_parse_error = true,
296 else => |e| return e,
233 for (macho_file.objects.items) |index| {
234 macho_file.getFile(index).?.parseAr(macho_file) catch |err| switch (err) {
235 error.InvalidCpuArch => {}, // already reported
236 else => |e| try macho_file.reportParseError2(index, "unexpected error: parsing input file failed with error {s}", .{@errorName(e)}),
297237 };
298 try macho_file.objects.append(gpa, index);
299238 }
300 if (has_parse_error) return error.MalformedArchive;
301239}
302240
303241fn markExports(macho_file: *MachO) void {
src/link/Wasm.zig+89-127
......@@ -658,9 +658,9 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {
658658 var object = Object.create(wasm, obj_file, path, null) catch |err| switch (err) {
659659 error.InvalidMagicByte, error.NotObjectFile => return false,
660660 else => |e| {
661 var err_note = try wasm.addErrorWithNotes(1);
662 try err_note.addMsg(wasm, "Failed parsing object file: {s}", .{@errorName(e)});
663 try err_note.addNote(wasm, "while parsing '{s}'", .{path});
661 var err_note = try wasm.base.addErrorWithNotes(1);
662 try err_note.addMsg("Failed parsing object file: {s}", .{@errorName(e)});
663 try err_note.addNote("while parsing '{s}'", .{path});
664664 return error.FlushFailure;
665665 },
666666 };
......@@ -714,9 +714,9 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
714714 return false;
715715 },
716716 else => |e| {
717 var err_note = try wasm.addErrorWithNotes(1);
718 try err_note.addMsg(wasm, "Failed parsing archive: {s}", .{@errorName(e)});
719 try err_note.addNote(wasm, "while parsing archive {s}", .{path});
717 var err_note = try wasm.base.addErrorWithNotes(1);
718 try err_note.addMsg("Failed parsing archive: {s}", .{@errorName(e)});
719 try err_note.addNote("while parsing archive {s}", .{path});
720720 return error.FlushFailure;
721721 },
722722 };
......@@ -741,9 +741,9 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
741741
742742 for (offsets.keys()) |file_offset| {
743743 var object = archive.parseObject(wasm, file_offset) catch |e| {
744 var err_note = try wasm.addErrorWithNotes(1);
745 try err_note.addMsg(wasm, "Failed parsing object: {s}", .{@errorName(e)});
746 try err_note.addNote(wasm, "while parsing object in archive {s}", .{path});
744 var err_note = try wasm.base.addErrorWithNotes(1);
745 try err_note.addMsg("Failed parsing object: {s}", .{@errorName(e)});
746 try err_note.addNote("while parsing object in archive {s}", .{path});
747747 return error.FlushFailure;
748748 };
749749 object.index = @enumFromInt(wasm.files.len);
......@@ -779,9 +779,9 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
779779
780780 if (symbol.isLocal()) {
781781 if (symbol.isUndefined()) {
782 var err = try wasm.addErrorWithNotes(1);
783 try err.addMsg(wasm, "Local symbols are not allowed to reference imports", .{});
784 try err.addNote(wasm, "symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() });
782 var err = try wasm.base.addErrorWithNotes(1);
783 try err.addMsg("Local symbols are not allowed to reference imports", .{});
784 try err.addNote("symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() });
785785 }
786786 try wasm.resolved_symbols.putNoClobber(gpa, location, {});
787787 continue;
......@@ -816,10 +816,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
816816 break :outer; // existing is weak, while new one isn't. Replace it.
817817 }
818818 // both are defined and weak, we have a symbol collision.
819 var err = try wasm.addErrorWithNotes(2);
820 try err.addMsg(wasm, "symbol '{s}' defined multiple times", .{sym_name});
821 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
822 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
819 var err = try wasm.base.addErrorWithNotes(2);
820 try err.addMsg("symbol '{s}' defined multiple times", .{sym_name});
821 try err.addNote("first definition in '{s}'", .{existing_file_path});
822 try err.addNote("next definition in '{s}'", .{obj_file.path()});
823823 }
824824
825825 try wasm.discarded.put(gpa, location, existing_loc);
......@@ -827,10 +827,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
827827 }
828828
829829 if (symbol.tag != existing_sym.tag) {
830 var err = try wasm.addErrorWithNotes(2);
831 try err.addMsg(wasm, "symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) });
832 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
833 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
830 var err = try wasm.base.addErrorWithNotes(2);
831 try err.addMsg("symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) });
832 try err.addNote("first definition in '{s}'", .{existing_file_path});
833 try err.addNote("next definition in '{s}'", .{obj_file.path()});
834834 }
835835
836836 if (existing_sym.isUndefined() and symbol.isUndefined()) {
......@@ -847,14 +847,14 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
847847 const imp = obj_file.import(sym_index);
848848 const module_name = obj_file.string(imp.module_name);
849849 if (!mem.eql(u8, existing_name, module_name)) {
850 var err = try wasm.addErrorWithNotes(2);
851 try err.addMsg(wasm, "symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{
850 var err = try wasm.base.addErrorWithNotes(2);
851 try err.addMsg("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{
852852 sym_name,
853853 existing_name,
854854 module_name,
855855 });
856 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
857 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
856 try err.addNote("first definition in '{s}'", .{existing_file_path});
857 try err.addNote("next definition in '{s}'", .{obj_file.path()});
858858 }
859859 }
860860
......@@ -867,10 +867,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
867867 const existing_ty = wasm.getGlobalType(existing_loc);
868868 const new_ty = wasm.getGlobalType(location);
869869 if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) {
870 var err = try wasm.addErrorWithNotes(2);
871 try err.addMsg(wasm, "symbol '{s}' mismatching global types", .{sym_name});
872 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
873 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
870 var err = try wasm.base.addErrorWithNotes(2);
871 try err.addMsg("symbol '{s}' mismatching global types", .{sym_name});
872 try err.addNote("first definition in '{s}'", .{existing_file_path});
873 try err.addNote("next definition in '{s}'", .{obj_file.path()});
874874 }
875875 }
876876
......@@ -878,11 +878,11 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
878878 const existing_ty = wasm.getFunctionSignature(existing_loc);
879879 const new_ty = wasm.getFunctionSignature(location);
880880 if (!existing_ty.eql(new_ty)) {
881 var err = try wasm.addErrorWithNotes(3);
882 try err.addMsg(wasm, "symbol '{s}' mismatching function signatures.", .{sym_name});
883 try err.addNote(wasm, "expected signature {}, but found signature {}", .{ existing_ty, new_ty });
884 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
885 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
881 var err = try wasm.base.addErrorWithNotes(3);
882 try err.addMsg("symbol '{s}' mismatching function signatures.", .{sym_name});
883 try err.addNote("expected signature {}, but found signature {}", .{ existing_ty, new_ty });
884 try err.addNote("first definition in '{s}'", .{existing_file_path});
885 try err.addNote("next definition in '{s}'", .{obj_file.path()});
886886 }
887887 }
888888
......@@ -930,9 +930,9 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
930930 // Parse object and and resolve symbols again before we check remaining
931931 // undefined symbols.
932932 var object = archive.parseObject(wasm, offset.items[0]) catch |e| {
933 var err_note = try wasm.addErrorWithNotes(1);
934 try err_note.addMsg(wasm, "Failed parsing object: {s}", .{@errorName(e)});
935 try err_note.addNote(wasm, "while parsing object in archive {s}", .{archive.name});
933 var err_note = try wasm.base.addErrorWithNotes(1);
934 try err_note.addMsg("Failed parsing object: {s}", .{@errorName(e)});
935 try err_note.addNote("while parsing object in archive {s}", .{archive.name});
936936 return error.FlushFailure;
937937 };
938938 object.index = @enumFromInt(wasm.files.len);
......@@ -1237,9 +1237,9 @@ fn validateFeatures(
12371237 allowed[used_index] = is_enabled;
12381238 emit_features_count.* += @intFromBool(is_enabled);
12391239 } else if (is_enabled and !allowed[used_index]) {
1240 var err = try wasm.addErrorWithNotes(1);
1241 try err.addMsg(wasm, "feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))});
1242 try err.addNote(wasm, "defined in '{s}'", .{wasm.files.items(.data)[used_set >> 1].object.path});
1240 var err = try wasm.base.addErrorWithNotes(1);
1241 try err.addMsg("feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))});
1242 try err.addNote("defined in '{s}'", .{wasm.files.items(.data)[used_set >> 1].object.path});
12431243 valid_feature_set = false;
12441244 }
12451245 }
......@@ -1251,7 +1251,8 @@ fn validateFeatures(
12511251 if (shared_memory) {
12521252 const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)];
12531253 if (@as(u1, @truncate(disallowed_feature)) != 0) {
1254 try wasm.addErrorWithoutNotes(
1254 var err = try wasm.base.addErrorWithNotes(0);
1255 try err.addMsg(
12551256 "shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled",
12561257 .{wasm.files.items(.data)[disallowed_feature >> 1].object.path},
12571258 );
......@@ -1260,7 +1261,8 @@ fn validateFeatures(
12601261
12611262 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
12621263 if (!allowed[@intFromEnum(feature)]) {
1263 try wasm.addErrorWithoutNotes("feature '{}' is not used but is required for shared-memory", .{feature});
1264 var err = try wasm.base.addErrorWithNotes(0);
1265 try err.addMsg("feature '{}' is not used but is required for shared-memory", .{feature});
12641266 }
12651267 }
12661268 }
......@@ -1268,7 +1270,8 @@ fn validateFeatures(
12681270 if (has_tls) {
12691271 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
12701272 if (!allowed[@intFromEnum(feature)]) {
1271 try wasm.addErrorWithoutNotes("feature '{}' is not used but is required for thread-local storage", .{feature});
1273 var err = try wasm.base.addErrorWithNotes(0);
1274 try err.addMsg("feature '{}' is not used but is required for thread-local storage", .{feature});
12721275 }
12731276 }
12741277 }
......@@ -1281,10 +1284,10 @@ fn validateFeatures(
12811284 // from here a feature is always used
12821285 const disallowed_feature = disallowed[@intFromEnum(feature.tag)];
12831286 if (@as(u1, @truncate(disallowed_feature)) != 0) {
1284 var err = try wasm.addErrorWithNotes(2);
1285 try err.addMsg(wasm, "feature '{}' is disallowed, but used by linked object", .{feature.tag});
1286 try err.addNote(wasm, "disallowed by '{s}'", .{wasm.files.items(.data)[disallowed_feature >> 1].object.path});
1287 try err.addNote(wasm, "used in '{s}'", .{object.path});
1287 var err = try wasm.base.addErrorWithNotes(2);
1288 try err.addMsg("feature '{}' is disallowed, but used by linked object", .{feature.tag});
1289 try err.addNote("disallowed by '{s}'", .{wasm.files.items(.data)[disallowed_feature >> 1].object.path});
1290 try err.addNote("used in '{s}'", .{object.path});
12881291 valid_feature_set = false;
12891292 }
12901293
......@@ -1295,10 +1298,10 @@ fn validateFeatures(
12951298 for (required, 0..) |required_feature, feature_index| {
12961299 const is_required = @as(u1, @truncate(required_feature)) != 0;
12971300 if (is_required and !object_used_features[feature_index]) {
1298 var err = try wasm.addErrorWithNotes(2);
1299 try err.addMsg(wasm, "feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))});
1300 try err.addNote(wasm, "required by '{s}'", .{wasm.files.items(.data)[required_feature >> 1].object.path});
1301 try err.addNote(wasm, "missing in '{s}'", .{object.path});
1301 var err = try wasm.base.addErrorWithNotes(2);
1302 try err.addMsg("feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))});
1303 try err.addNote("required by '{s}'", .{wasm.files.items(.data)[required_feature >> 1].object.path});
1304 try err.addNote("missing in '{s}'", .{object.path});
13021305 valid_feature_set = false;
13031306 }
13041307 }
......@@ -1376,9 +1379,9 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void {
13761379 else
13771380 wasm.name;
13781381 const symbol_name = undef.getName(wasm);
1379 var err = try wasm.addErrorWithNotes(1);
1380 try err.addMsg(wasm, "could not resolve undefined symbol '{s}'", .{symbol_name});
1381 try err.addNote(wasm, "defined in '{s}'", .{file_name});
1382 var err = try wasm.base.addErrorWithNotes(1);
1383 try err.addMsg("could not resolve undefined symbol '{s}'", .{symbol_name});
1384 try err.addNote("defined in '{s}'", .{file_name});
13821385 }
13831386 }
13841387 if (found_undefined_symbols) {
......@@ -1757,7 +1760,8 @@ fn setupInitFunctions(wasm: *Wasm) !void {
17571760 break :ty object.func_types[func.type_index];
17581761 };
17591762 if (ty.params.len != 0) {
1760 try wasm.addErrorWithoutNotes("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)});
1763 var err = try wasm.base.addErrorWithNotes(0);
1764 try err.addMsg("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)});
17611765 }
17621766 log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)});
17631767 wasm.init_funcs.appendAssumeCapacity(.{
......@@ -2140,7 +2144,8 @@ fn checkExportNames(wasm: *Wasm) !void {
21402144
21412145 for (force_exp_names) |exp_name| {
21422146 const loc = wasm.findGlobalSymbol(exp_name) orelse {
2143 try wasm.addErrorWithoutNotes("could not export '{s}', symbol not found", .{exp_name});
2147 var err = try wasm.base.addErrorWithNotes(0);
2148 try err.addMsg("could not export '{s}', symbol not found", .{exp_name});
21442149 failed_exports = true;
21452150 continue;
21462151 };
......@@ -2203,13 +2208,15 @@ fn setupStart(wasm: *Wasm) !void {
22032208 const entry_name = wasm.entry_name orelse return;
22042209
22052210 const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {
2206 try wasm.addErrorWithoutNotes("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});
2211 var err = try wasm.base.addErrorWithNotes(0);
2212 try err.addMsg("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});
22072213 return error.FlushFailure;
22082214 };
22092215
22102216 const symbol = symbol_loc.getSymbol(wasm);
22112217 if (symbol.tag != .function) {
2212 try wasm.addErrorWithoutNotes("Entry symbol '{s}' is not a function", .{entry_name});
2218 var err = try wasm.base.addErrorWithNotes(0);
2219 try err.addMsg("Entry symbol '{s}' is not a function", .{entry_name});
22132220 return error.FlushFailure;
22142221 }
22152222
......@@ -2314,13 +2321,16 @@ fn setupMemory(wasm: *Wasm) !void {
23142321
23152322 if (wasm.initial_memory) |initial_memory| {
23162323 if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) {
2317 try wasm.addErrorWithoutNotes("Initial memory must be {d}-byte aligned", .{page_size});
2324 var err = try wasm.base.addErrorWithNotes(0);
2325 try err.addMsg("Initial memory must be {d}-byte aligned", .{page_size});
23182326 }
23192327 if (memory_ptr > initial_memory) {
2320 try wasm.addErrorWithoutNotes("Initial memory too small, must be at least {d} bytes", .{memory_ptr});
2328 var err = try wasm.base.addErrorWithNotes(0);
2329 try err.addMsg("Initial memory too small, must be at least {d} bytes", .{memory_ptr});
23212330 }
23222331 if (initial_memory > max_memory_allowed) {
2323 try wasm.addErrorWithoutNotes("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});
2332 var err = try wasm.base.addErrorWithNotes(0);
2333 try err.addMsg("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});
23242334 }
23252335 memory_ptr = initial_memory;
23262336 }
......@@ -2337,13 +2347,16 @@ fn setupMemory(wasm: *Wasm) !void {
23372347
23382348 if (wasm.max_memory) |max_memory| {
23392349 if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) {
2340 try wasm.addErrorWithoutNotes("Maximum memory must be {d}-byte aligned", .{page_size});
2350 var err = try wasm.base.addErrorWithNotes(0);
2351 try err.addMsg("Maximum memory must be {d}-byte aligned", .{page_size});
23412352 }
23422353 if (memory_ptr > max_memory) {
2343 try wasm.addErrorWithoutNotes("Maximum memory too small, must be at least {d} bytes", .{memory_ptr});
2354 var err = try wasm.base.addErrorWithNotes(0);
2355 try err.addMsg("Maximum memory too small, must be at least {d} bytes", .{memory_ptr});
23442356 }
23452357 if (max_memory > max_memory_allowed) {
2346 try wasm.addErrorWithoutNotes("Maximum memory exceeds maximum amount {d}", .{max_memory_allowed});
2358 var err = try wasm.base.addErrorWithNotes(0);
2359 try err.addMsg("Maximum memory exceeds maximum amount {d}", .{max_memory_allowed});
23472360 }
23482361 wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size));
23492362 wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX);
......@@ -2446,9 +2459,9 @@ pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: Sym
24462459 break :blk index;
24472460 };
24482461 } else {
2449 var err = try wasm.addErrorWithNotes(1);
2450 try err.addMsg(wasm, "found unknown section '{s}'", .{section_name});
2451 try err.addNote(wasm, "defined in '{s}'", .{obj_file.path()});
2462 var err = try wasm.base.addErrorWithNotes(1);
2463 try err.addMsg("found unknown section '{s}'", .{section_name});
2464 try err.addNote("defined in '{s}'", .{obj_file.path()});
24522465 return error.UnexpectedValue;
24532466 }
24542467 },
......@@ -2564,23 +2577,23 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
25642577 if (wasm.zig_object_index != .null) {
25652578 try wasm.resolveSymbolsInObject(wasm.zig_object_index);
25662579 }
2567 if (comp.link_errors.items.len > 0) return error.FlushFailure;
2580 if (wasm.base.hasErrors()) return error.FlushFailure;
25682581 for (wasm.objects.items) |object_index| {
25692582 try wasm.resolveSymbolsInObject(object_index);
25702583 }
2571 if (comp.link_errors.items.len > 0) return error.FlushFailure;
2584 if (wasm.base.hasErrors()) return error.FlushFailure;
25722585
25732586 var emit_features_count: u32 = 0;
25742587 var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;
25752588 try wasm.validateFeatures(&enabled_features, &emit_features_count);
25762589 try wasm.resolveSymbolsInArchives();
2577 if (comp.link_errors.items.len > 0) return error.FlushFailure;
2590 if (wasm.base.hasErrors()) return error.FlushFailure;
25782591 try wasm.resolveLazySymbols();
25792592 try wasm.checkUndefinedSymbols();
25802593 try wasm.checkExportNames();
25812594
25822595 try wasm.setupInitFunctions();
2583 if (comp.link_errors.items.len > 0) return error.FlushFailure;
2596 if (wasm.base.hasErrors()) return error.FlushFailure;
25842597 try wasm.setupStart();
25852598
25862599 try wasm.markReferences();
......@@ -2589,7 +2602,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
25892602 try wasm.mergeTypes();
25902603 try wasm.allocateAtoms();
25912604 try wasm.setupMemory();
2592 if (comp.link_errors.items.len > 0) return error.FlushFailure;
2605 if (wasm.base.hasErrors()) return error.FlushFailure;
25932606 wasm.allocateVirtualAddresses();
25942607 wasm.mapFunctionTable();
25952608 try wasm.initializeCallCtorsFunction();
......@@ -2599,7 +2612,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
25992612 try wasm.setupStartSection();
26002613 try wasm.setupExports();
26012614 try wasm.writeToFile(enabled_features, emit_features_count, arena);
2602 if (comp.link_errors.items.len > 0) return error.FlushFailure;
2615 if (wasm.base.hasErrors()) return error.FlushFailure;
26032616}
26042617
26052618/// Writes the WebAssembly in-memory module to the file
......@@ -2997,7 +3010,10 @@ fn writeToFile(
29973010 }) catch unreachable;
29983011 try emitBuildIdSection(&binary_bytes, str);
29993012 },
3000 else => |mode| try wasm.addErrorWithoutNotes("build-id '{s}' is not supported for WebAssembly", .{@tagName(mode)}),
3013 else => |mode| {
3014 var err = try wasm.base.addErrorWithNotes(0);
3015 try err.addMsg("build-id '{s}' is not supported for WebAssembly", .{@tagName(mode)});
3016 },
30013017 }
30023018
30033019 var debug_bytes = std.ArrayList(u8).init(gpa);
......@@ -4086,57 +4102,3 @@ fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8
40864102 .command => "_start",
40874103 };
40884104}
4089
4090const ErrorWithNotes = struct {
4091 /// Allocated index in comp.link_errors array.
4092 index: usize,
4093
4094 /// Next available note slot.
4095 note_slot: usize = 0,
4096
4097 pub fn addMsg(
4098 err: ErrorWithNotes,
4099 wasm_file: *const Wasm,
4100 comptime format: []const u8,
4101 args: anytype,
4102 ) error{OutOfMemory}!void {
4103 const comp = wasm_file.base.comp;
4104 const gpa = comp.gpa;
4105 const err_msg = &comp.link_errors.items[err.index];
4106 err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
4107 }
4108
4109 pub fn addNote(
4110 err: *ErrorWithNotes,
4111 wasm_file: *const Wasm,
4112 comptime format: []const u8,
4113 args: anytype,
4114 ) error{OutOfMemory}!void {
4115 const comp = wasm_file.base.comp;
4116 const gpa = comp.gpa;
4117 const err_msg = &comp.link_errors.items[err.index];
4118 err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };
4119 err.note_slot += 1;
4120 }
4121};
4122
4123pub fn addErrorWithNotes(wasm: *const Wasm, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
4124 const comp = wasm.base.comp;
4125 const gpa = comp.gpa;
4126 try comp.link_errors.ensureUnusedCapacity(gpa, 1);
4127 return wasm.addErrorWithNotesAssumeCapacity(note_count);
4128}
4129
4130pub fn addErrorWithoutNotes(wasm: *const Wasm, comptime fmt: []const u8, args: anytype) !void {
4131 const err = try wasm.addErrorWithNotes(0);
4132 try err.addMsg(wasm, fmt, args);
4133}
4134
4135fn addErrorWithNotesAssumeCapacity(wasm: *const Wasm, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
4136 const comp = wasm.base.comp;
4137 const gpa = comp.gpa;
4138 const index = comp.link_errors.items.len;
4139 const err = comp.link_errors.addOneAssumeCapacity();
4140 err.* = .{ .msg = undefined, .notes = try gpa.alloc(link.File.ErrorMsg, note_count) };
4141 return .{ .index = index };
4142}
src/link/Wasm/Object.zig+15-15
......@@ -235,27 +235,27 @@ fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?S
235235 if (object.imported_tables_count == table_count) return null;
236236
237237 if (table_count != 0) {
238 var err = try wasm_file.addErrorWithNotes(1);
239 try err.addMsg(wasm_file, "Expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
238 var err = try wasm_file.base.addErrorWithNotes(1);
239 try err.addMsg("Expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
240240 object.imported_tables_count,
241241 table_count,
242242 });
243 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
243 try err.addNote("defined in '{s}'", .{object.path});
244244 return error.MissingTableSymbols;
245245 }
246246
247247 // MVP object files cannot have any table definitions, only imports (for the indirect function table).
248248 if (object.tables.len > 0) {
249 var err = try wasm_file.addErrorWithNotes(1);
250 try err.addMsg(wasm_file, "Unexpected table definition without representing table symbols.", .{});
251 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
249 var err = try wasm_file.base.addErrorWithNotes(1);
250 try err.addMsg("Unexpected table definition without representing table symbols.", .{});
251 try err.addNote("defined in '{s}'", .{object.path});
252252 return error.UnexpectedTable;
253253 }
254254
255255 if (object.imported_tables_count != 1) {
256 var err = try wasm_file.addErrorWithNotes(1);
257 try err.addMsg(wasm_file, "Found more than one table import, but no representing table symbols", .{});
258 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
256 var err = try wasm_file.base.addErrorWithNotes(1);
257 try err.addMsg("Found more than one table import, but no representing table symbols", .{});
258 try err.addNote("defined in '{s}'", .{object.path});
259259 return error.MissingTableSymbols;
260260 }
261261
......@@ -266,9 +266,9 @@ fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?S
266266 } else unreachable;
267267
268268 if (!std.mem.eql(u8, object.string_table.get(table_import.name), "__indirect_function_table")) {
269 var err = try wasm_file.addErrorWithNotes(1);
270 try err.addMsg(wasm_file, "Non-indirect function table import '{s}' is missing a corresponding symbol", .{object.string_table.get(table_import.name)});
271 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
269 var err = try wasm_file.base.addErrorWithNotes(1);
270 try err.addMsg("Non-indirect function table import '{s}' is missing a corresponding symbol", .{object.string_table.get(table_import.name)});
271 try err.addNote("defined in '{s}'", .{object.path});
272272 return error.MissingTableSymbols;
273273 }
274274
......@@ -596,9 +596,9 @@ fn Parser(comptime ReaderType: type) type {
596596 try reader.readNoEof(name);
597597
598598 const tag = types.known_features.get(name) orelse {
599 var err = try parser.wasm_file.addErrorWithNotes(1);
600 try err.addMsg(parser.wasm_file, "Object file contains unknown feature: {s}", .{name});
601 try err.addNote(parser.wasm_file, "defined in '{s}'", .{parser.object.path});
599 var err = try parser.wasm_file.base.addErrorWithNotes(1);
600 try err.addMsg("Object file contains unknown feature: {s}", .{name});
601 try err.addNote("defined in '{s}'", .{parser.object.path});
602602 return error.UnknownFeature;
603603 };
604604 feature.* = .{