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...@@ -105,8 +105,9 @@ win32_resource_table: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMa
105 pub fn deinit(_: @This(), _: Allocator) void {}105 pub fn deinit(_: @This(), _: Allocator) void {}
106} = .{},106} = .{},
107107
108link_error_flags: link.File.ErrorFlags = .{},
109link_errors: std.ArrayListUnmanaged(link.File.ErrorMsg) = .{},108link_errors: std.ArrayListUnmanaged(link.File.ErrorMsg) = .{},
109link_errors_mutex: std.Thread.Mutex = .{},
110link_error_flags: link.File.ErrorFlags = .{},
110lld_errors: std.ArrayListUnmanaged(LldError) = .{},111lld_errors: std.ArrayListUnmanaged(LldError) = .{},
111112
112work_queues: [113work_queues: [
...@@ -3067,7 +3068,6 @@ pub fn totalErrorCount(comp: *Compilation) u32 {...@@ -3067,7 +3068,6 @@ pub fn totalErrorCount(comp: *Compilation) u32 {
3067 total += @intFromBool(comp.link_error_flags.no_entry_point_found);3068 total += @intFromBool(comp.link_error_flags.no_entry_point_found);
3068 }3069 }
3069 total += @intFromBool(comp.link_error_flags.missing_libc);3070 total += @intFromBool(comp.link_error_flags.missing_libc);
3070
3071 total += comp.link_errors.items.len;3071 total += comp.link_errors.items.len;
30723072
3073 // Compile log errors only count if there are no other errors.3073 // Compile log errors only count if there are no other errors.
src/link.zig+67-1
...@@ -439,6 +439,58 @@ pub const File = struct {...@@ -439,6 +439,58 @@ pub const File = struct {
439 }439 }
440 }440 }
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
442 pub fn releaseLock(self: *File) void {494 pub fn releaseLock(self: *File) void {
443 if (self.lock) |*lock| {495 if (self.lock) |*lock| {
444 lock.release();496 lock.release();
...@@ -874,9 +926,23 @@ pub const File = struct {...@@ -874,9 +926,23 @@ pub const File = struct {
874 }926 }
875 };927 };
876928
877 pub const ErrorFlags = struct {929 pub const ErrorFlags = packed struct {
878 no_entry_point_found: bool = false,930 no_entry_point_found: bool = false,
879 missing_libc: bool = false,931 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 }
880 };946 };
881947
882 pub const ErrorMsg = struct {948 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 {...@@ -995,12 +995,12 @@ pub fn growAllocSection(self: *Elf, shdr_index: u32, needed_size: u64) !void {
995 if (maybe_phdr) |phdr| {995 if (maybe_phdr) |phdr| {
996 const mem_capacity = self.allocatedVirtualSize(phdr.p_vaddr);996 const mem_capacity = self.allocatedVirtualSize(phdr.p_vaddr);
997 if (needed_size > mem_capacity) {997 if (needed_size > mem_capacity) {
998 var err = try self.addErrorWithNotes(2);998 var err = try self.base.addErrorWithNotes(2);
999 try err.addMsg(self, "fatal linker error: cannot expand load segment phdr({d}) in virtual memory", .{999 try err.addMsg("fatal linker error: cannot expand load segment phdr({d}) in virtual memory", .{
1000 self.phdr_to_shdr_table.get(shdr_index).?,1000 self.phdr_to_shdr_table.get(shdr_index).?,
1001 });1001 });
1002 try err.addNote(self, "TODO: emit relocations to memory locations in self-hosted backends", .{});1002 try err.addNote("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", .{});1003 try err.addNote("as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
1004 }1004 }
10051005
1006 phdr.p_memsz = needed_size;1006 phdr.p_memsz = needed_size;
...@@ -1276,7 +1276,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -1276,7 +1276,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
1276 };1276 };
1277 }1277 }
12781278
1279 if (comp.link_errors.items.len > 0) return error.FlushFailure;1279 if (self.base.hasErrors()) return error.FlushFailure;
12801280
1281 // Dedup shared objects1281 // Dedup shared objects
1282 {1282 {
...@@ -1423,7 +1423,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -1423,7 +1423,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
1423 try self.writeElfHeader();1423 try self.writeElfHeader();
1424 }1424 }
14251425
1426 if (comp.link_errors.items.len > 0) return error.FlushFailure;1426 if (self.base.hasErrors()) return error.FlushFailure;
1427}1427}
14281428
1429/// --verbose-link output1429/// --verbose-link output
...@@ -2852,9 +2852,9 @@ fn writePhdrTable(self: *Elf) !void {...@@ -2852,9 +2852,9 @@ fn writePhdrTable(self: *Elf) !void {
2852}2852}
28532853
2854pub fn writeElfHeader(self: *Elf) !void {2854pub fn writeElfHeader(self: *Elf) !void {
2855 const comp = self.base.comp;2855 if (self.base.hasErrors()) return; // We had errors, so skip flushing to render the output unusable
2856 if (comp.link_errors.items.len > 0) return; // We had errors, so skip flushing to render the output unusable
28572856
2857 const comp = self.base.comp;
2858 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;2858 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
28592859
2860 var index: usize = 0;2860 var index: usize = 0;
...@@ -4298,9 +4298,9 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {...@@ -4298,9 +4298,9 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {
4298 // (revisit getMaxNumberOfPhdrs())4298 // (revisit getMaxNumberOfPhdrs())
4299 // 2. shift everything in file to free more space for EHDR + PHDR table4299 // 2. shift everything in file to free more space for EHDR + PHDR table
4300 // TODO verify `getMaxNumberOfPhdrs()` is accurate and convert this into no-op4300 // TODO verify `getMaxNumberOfPhdrs()` is accurate and convert this into no-op
4301 var err = try self.addErrorWithNotes(1);4301 var err = try self.base.addErrorWithNotes(1);
4302 try err.addMsg(self, "fatal linker error: not enough space reserved for EHDR and PHDR table", .{});4302 try err.addMsg("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 });4303 try err.addNote("required 0x{x}, available 0x{x}", .{ needed_size, available_space });
4304 }4304 }
43054305
4306 phdr_table_load.p_filesz = needed_size + ehsize;4306 phdr_table_load.p_filesz = needed_size + ehsize;
...@@ -5863,56 +5863,6 @@ pub fn tlsAddress(self: *Elf) i64 {...@@ -5863,56 +5863,6 @@ pub fn tlsAddress(self: *Elf) i64 {
5863 return @intCast(phdr.p_vaddr);5863 return @intCast(phdr.p_vaddr);
5864}5864}
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
5916pub fn getShString(self: Elf, off: u32) [:0]const u8 {5866pub fn getShString(self: Elf, off: u32) [:0]const u8 {
5917 assert(off < self.shstrtab.items.len);5867 assert(off < self.shstrtab.items.len);
5918 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.shstrtab.items.ptr + off)), 0);5868 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 {...@@ -5940,11 +5890,10 @@ pub fn insertDynString(self: *Elf, name: []const u8) error{OutOfMemory}!u32 {
5940}5890}
59415891
5942fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {5892fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
5943 const comp = self.base.comp;5893 const gpa = self.base.comp.gpa;
5944 const gpa = comp.gpa;
5945 const max_notes = 4;5894 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
5949 var it = undefs.iterator();5898 var it = undefs.iterator();
5950 while (it.next()) |entry| {5899 while (it.next()) |entry| {
...@@ -5953,18 +5902,18 @@ fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {...@@ -5953,18 +5902,18 @@ fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
5953 const natoms = @min(atoms.len, max_notes);5902 const natoms = @min(atoms.len, max_notes);
5954 const nnotes = natoms + @intFromBool(atoms.len > max_notes);5903 const nnotes = natoms + @intFromBool(atoms.len > max_notes);
59555904
5956 var err = try self.addErrorWithNotesAssumeCapacity(nnotes);5905 var err = try self.base.addErrorWithNotesAssumeCapacity(nnotes);
5957 try err.addMsg(self, "undefined symbol: {s}", .{self.symbol(undef_index).name(self)});5906 try err.addMsg("undefined symbol: {s}", .{self.symbol(undef_index).name(self)});
59585907
5959 for (atoms[0..natoms]) |atom_index| {5908 for (atoms[0..natoms]) |atom_index| {
5960 const atom_ptr = self.atom(atom_index).?;5909 const atom_ptr = self.atom(atom_index).?;
5961 const file_ptr = self.file(atom_ptr.file_index).?;5910 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) });
5963 }5912 }
59645913
5965 if (atoms.len > max_notes) {5914 if (atoms.len > max_notes) {
5966 const remaining = atoms.len - max_notes;5915 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});
5968 }5917 }
5969 }5918 }
5970}5919}
...@@ -5978,19 +5927,19 @@ fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemor...@@ -5978,19 +5927,19 @@ fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemor
5978 const notes = entry.value_ptr.*;5927 const notes = entry.value_ptr.*;
5979 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);5928 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
59805929
5981 var err = try self.addErrorWithNotes(nnotes + 1);5930 var err = try self.base.addErrorWithNotes(nnotes + 1);
5982 try err.addMsg(self, "duplicate symbol definition: {s}", .{sym.name(self)});5931 try err.addMsg("duplicate symbol definition: {s}", .{sym.name(self)});
5983 try err.addNote(self, "defined by {}", .{sym.file(self).?.fmtPath()});5932 try err.addNote("defined by {}", .{sym.file(self).?.fmtPath()});
59845933
5985 var inote: usize = 0;5934 var inote: usize = 0;
5986 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {5935 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
5987 const file_ptr = self.file(notes.items[inote]).?;5936 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()});
5989 }5938 }
59905939
5991 if (notes.items.len > max_notes) {5940 if (notes.items.len > max_notes) {
5992 const remaining = notes.items.len - max_notes;5941 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});
5994 }5943 }
59955944
5996 has_dupes = true;5945 has_dupes = true;
...@@ -6005,16 +5954,16 @@ fn reportMissingLibraryError(...@@ -6005,16 +5954,16 @@ fn reportMissingLibraryError(
6005 comptime format: []const u8,5954 comptime format: []const u8,
6006 args: anytype,5955 args: anytype,
6007) error{OutOfMemory}!void {5956) error{OutOfMemory}!void {
6008 var err = try self.addErrorWithNotes(checked_paths.len);5957 var err = try self.base.addErrorWithNotes(checked_paths.len);
6009 try err.addMsg(self, format, args);5958 try err.addMsg(format, args);
6010 for (checked_paths) |path| {5959 for (checked_paths) |path| {
6011 try err.addNote(self, "tried {s}", .{path});5960 try err.addNote("tried {s}", .{path});
6012 }5961 }
6013}5962}
60145963
6015pub fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void {5964pub fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void {
6016 var err = try self.addErrorWithNotes(0);5965 var err = try self.base.addErrorWithNotes(0);
6017 try err.addMsg(self, "fatal linker error: unsupported CPU architecture {s}", .{5966 try err.addMsg("fatal linker error: unsupported CPU architecture {s}", .{
6018 @tagName(self.getTarget().cpu.arch),5967 @tagName(self.getTarget().cpu.arch),
6019 });5968 });
6020}5969}
...@@ -6025,9 +5974,9 @@ pub fn reportParseError(...@@ -6025,9 +5974,9 @@ pub fn reportParseError(
6025 comptime format: []const u8,5974 comptime format: []const u8,
6026 args: anytype,5975 args: anytype,
6027) error{OutOfMemory}!void {5976) error{OutOfMemory}!void {
6028 var err = try self.addErrorWithNotes(1);5977 var err = try self.base.addErrorWithNotes(1);
6029 try err.addMsg(self, format, args);5978 try err.addMsg(format, args);
6030 try err.addNote(self, "while parsing {s}", .{path});5979 try err.addNote("while parsing {s}", .{path});
6031}5980}
60325981
6033pub fn reportParseError2(5982pub fn reportParseError2(
...@@ -6036,9 +5985,9 @@ pub fn reportParseError2(...@@ -6036,9 +5985,9 @@ pub fn reportParseError2(
6036 comptime format: []const u8,5985 comptime format: []const u8,
6037 args: anytype,5986 args: anytype,
6038) error{OutOfMemory}!void {5987) error{OutOfMemory}!void {
6039 var err = try self.addErrorWithNotes(1);5988 var err = try self.base.addErrorWithNotes(1);
6040 try err.addMsg(self, format, args);5989 try err.addMsg(format, args);
6041 try err.addNote(self, "while parsing {}", .{self.file(file_index).?.fmtPath()});5990 try err.addNote("while parsing {}", .{self.file(file_index).?.fmtPath()});
6042}5991}
60435992
6044const FormatShdrCtx = struct {5993const FormatShdrCtx = struct {
src/link/Elf/Atom.zig+32-44
...@@ -631,15 +631,12 @@ fn dataType(symbol: *const Symbol, elf_file: *Elf) u2 {...@@ -631,15 +631,12 @@ fn dataType(symbol: *const Symbol, elf_file: *Elf) u2 {
631}631}
632632
633fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) RelocError!void {633fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) RelocError!void {
634 var err = try elf_file.addErrorWithNotes(1);634 var err = try elf_file.base.addErrorWithNotes(1);
635 try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {} at offset 0x{x}", .{635 try err.addMsg("fatal linker error: unhandled relocation type {} at offset 0x{x}", .{
636 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),636 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),
637 rel.r_offset,637 rel.r_offset,
638 });638 });
639 try err.addNote(elf_file, "in {}:{s}", .{639 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
640 self.file(elf_file).?.fmtPath(),
641 self.name(elf_file),
642 });
643 return error.RelocFailure;640 return error.RelocFailure;
644}641}
645642
...@@ -649,15 +646,12 @@ fn reportTextRelocError(...@@ -649,15 +646,12 @@ fn reportTextRelocError(
649 rel: elf.Elf64_Rela,646 rel: elf.Elf64_Rela,
650 elf_file: *Elf,647 elf_file: *Elf,
651) RelocError!void {648) RelocError!void {
652 var err = try elf_file.addErrorWithNotes(1);649 var err = try elf_file.base.addErrorWithNotes(1);
653 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{650 try err.addMsg("relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
654 rel.r_offset,651 rel.r_offset,
655 symbol.name(elf_file),652 symbol.name(elf_file),
656 });653 });
657 try err.addNote(elf_file, "in {}:{s}", .{654 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
658 self.file(elf_file).?.fmtPath(),
659 self.name(elf_file),
660 });
661 return error.RelocFailure;655 return error.RelocFailure;
662}656}
663657
...@@ -667,16 +661,13 @@ fn reportPicError(...@@ -667,16 +661,13 @@ fn reportPicError(
667 rel: elf.Elf64_Rela,661 rel: elf.Elf64_Rela,
668 elf_file: *Elf,662 elf_file: *Elf,
669) RelocError!void {663) RelocError!void {
670 var err = try elf_file.addErrorWithNotes(2);664 var err = try elf_file.base.addErrorWithNotes(2);
671 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{665 try err.addMsg("relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
672 rel.r_offset,666 rel.r_offset,
673 symbol.name(elf_file),667 symbol.name(elf_file),
674 });668 });
675 try err.addNote(elf_file, "in {}:{s}", .{669 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
676 self.file(elf_file).?.fmtPath(),670 try err.addNote("recompile with -fPIC", .{});
677 self.name(elf_file),
678 });
679 try err.addNote(elf_file, "recompile with -fPIC", .{});
680 return error.RelocFailure;671 return error.RelocFailure;
681}672}
682673
...@@ -686,16 +677,13 @@ fn reportNoPicError(...@@ -686,16 +677,13 @@ fn reportNoPicError(
686 rel: elf.Elf64_Rela,677 rel: elf.Elf64_Rela,
687 elf_file: *Elf,678 elf_file: *Elf,
688) RelocError!void {679) RelocError!void {
689 var err = try elf_file.addErrorWithNotes(2);680 var err = try elf_file.base.addErrorWithNotes(2);
690 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{681 try err.addMsg("relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
691 rel.r_offset,682 rel.r_offset,
692 symbol.name(elf_file),683 symbol.name(elf_file),
693 });684 });
694 try err.addNote(elf_file, "in {}:{s}", .{685 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
695 self.file(elf_file).?.fmtPath(),686 try err.addNote("recompile with -fno-PIC", .{});
696 self.name(elf_file),
697 });
698 try err.addNote(elf_file, "recompile with -fno-PIC", .{});
699 return error.RelocFailure;687 return error.RelocFailure;
700}688}
701689
...@@ -1332,9 +1320,9 @@ const x86_64 = struct {...@@ -1332,9 +1320,9 @@ const x86_64 = struct {
1332 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);1320 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
1333 } else {1321 } else {
1334 x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]) catch {1322 x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]) catch {
1335 var err = try elf_file.addErrorWithNotes(1);1323 var err = try elf_file.base.addErrorWithNotes(1);
1336 try err.addMsg(elf_file, "could not relax {s}", .{@tagName(r_type)});1324 try err.addMsg("could not relax {s}", .{@tagName(r_type)});
1337 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{1325 try err.addNote("in {}:{s} at offset 0x{x}", .{
1338 atom.file(elf_file).?.fmtPath(),1326 atom.file(elf_file).?.fmtPath(),
1339 atom.name(elf_file),1327 atom.name(elf_file),
1340 rel.r_offset,1328 rel.r_offset,
...@@ -1479,12 +1467,12 @@ const x86_64 = struct {...@@ -1479,12 +1467,12 @@ const x86_64 = struct {
1479 },1467 },
14801468
1481 else => {1469 else => {
1482 var err = try elf_file.addErrorWithNotes(1);1470 var err = try elf_file.base.addErrorWithNotes(1);
1483 try err.addMsg(elf_file, "TODO: rewrite {} when followed by {}", .{1471 try err.addMsg("TODO: rewrite {} when followed by {}", .{
1484 relocation.fmtRelocType(rels[0].r_type(), .x86_64),1472 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
1485 relocation.fmtRelocType(rels[1].r_type(), .x86_64),1473 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
1486 });1474 });
1487 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{1475 try err.addNote("in {}:{s} at offset 0x{x}", .{
1488 self.file(elf_file).?.fmtPath(),1476 self.file(elf_file).?.fmtPath(),
1489 self.name(elf_file),1477 self.name(elf_file),
1490 rels[0].r_offset,1478 rels[0].r_offset,
...@@ -1534,12 +1522,12 @@ const x86_64 = struct {...@@ -1534,12 +1522,12 @@ const x86_64 = struct {
1534 },1522 },
15351523
1536 else => {1524 else => {
1537 var err = try elf_file.addErrorWithNotes(1);1525 var err = try elf_file.base.addErrorWithNotes(1);
1538 try err.addMsg(elf_file, "TODO: rewrite {} when followed by {}", .{1526 try err.addMsg("TODO: rewrite {} when followed by {}", .{
1539 relocation.fmtRelocType(rels[0].r_type(), .x86_64),1527 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
1540 relocation.fmtRelocType(rels[1].r_type(), .x86_64),1528 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
1541 });1529 });
1542 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{1530 try err.addNote("in {}:{s} at offset 0x{x}", .{
1543 self.file(elf_file).?.fmtPath(),1531 self.file(elf_file).?.fmtPath(),
1544 self.name(elf_file),1532 self.name(elf_file),
1545 rels[0].r_offset,1533 rels[0].r_offset,
...@@ -1630,12 +1618,12 @@ const x86_64 = struct {...@@ -1630,12 +1618,12 @@ const x86_64 = struct {
1630 },1618 },
16311619
1632 else => {1620 else => {
1633 var err = try elf_file.addErrorWithNotes(1);1621 var err = try elf_file.base.addErrorWithNotes(1);
1634 try err.addMsg(elf_file, "fatal linker error: rewrite {} when followed by {}", .{1622 try err.addMsg("fatal linker error: rewrite {} when followed by {}", .{
1635 relocation.fmtRelocType(rels[0].r_type(), .x86_64),1623 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
1636 relocation.fmtRelocType(rels[1].r_type(), .x86_64),1624 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
1637 });1625 });
1638 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{1626 try err.addNote("in {}:{s} at offset 0x{x}", .{
1639 self.file(elf_file).?.fmtPath(),1627 self.file(elf_file).?.fmtPath(),
1640 self.name(elf_file),1628 self.name(elf_file),
1641 rels[0].r_offset,1629 rels[0].r_offset,
...@@ -1824,9 +1812,9 @@ const aarch64 = struct {...@@ -1824,9 +1812,9 @@ const aarch64 = struct {
1824 aarch64_util.writeAdrpInst(pages, code);1812 aarch64_util.writeAdrpInst(pages, code);
1825 } else {1813 } else {
1826 // TODO: relax1814 // TODO: relax
1827 var err = try elf_file.addErrorWithNotes(1);1815 var err = try elf_file.base.addErrorWithNotes(1);
1828 try err.addMsg(elf_file, "TODO: relax ADR_GOT_PAGE", .{});1816 try err.addMsg("TODO: relax ADR_GOT_PAGE", .{});
1829 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{1817 try err.addNote("in {}:{s} at offset 0x{x}", .{
1830 atom.file(elf_file).?.fmtPath(),1818 atom.file(elf_file).?.fmtPath(),
1831 atom.name(elf_file),1819 atom.name(elf_file),
1832 r_offset,1820 r_offset,
...@@ -2118,9 +2106,9 @@ const riscv = struct {...@@ -2118,9 +2106,9 @@ const riscv = struct {
2118 if (S == atom_addr + @as(i64, @intCast(pair.r_offset))) break pair;2106 if (S == atom_addr + @as(i64, @intCast(pair.r_offset))) break pair;
2119 } else {2107 } else {
2120 // TODO: implement searching forward2108 // TODO: implement searching forward
2121 var err = try elf_file.addErrorWithNotes(1);2109 var err = try elf_file.base.addErrorWithNotes(1);
2122 try err.addMsg(elf_file, "TODO: find HI20 paired reloc scanning forward", .{});2110 try err.addMsg("TODO: find HI20 paired reloc scanning forward", .{});
2123 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{2111 try err.addNote("in {}:{s} at offset 0x{x}", .{
2124 atom.file(elf_file).?.fmtPath(),2112 atom.file(elf_file).?.fmtPath(),
2125 atom.name(elf_file),2113 atom.name(elf_file),
2126 rel.r_offset,2114 rel.r_offset,
src/link/Elf/Object.zig+13-13
...@@ -704,9 +704,9 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void {...@@ -704,9 +704,9 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void {
704 var end = start;704 var end = start;
705 while (end < data.len - sh_entsize and !isNull(data[end .. end + sh_entsize])) : (end += sh_entsize) {}705 while (end < data.len - sh_entsize and !isNull(data[end .. end + sh_entsize])) : (end += sh_entsize) {}
706 if (!isNull(data[end .. end + sh_entsize])) {706 if (!isNull(data[end .. end + sh_entsize])) {
707 var err = try elf_file.addErrorWithNotes(1);707 var err = try elf_file.base.addErrorWithNotes(1);
708 try err.addMsg(elf_file, "string not null terminated", .{});708 try err.addMsg("string not null terminated", .{});
709 try err.addNote(elf_file, "in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });709 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
710 return error.MalformedObject;710 return error.MalformedObject;
711 }711 }
712 end += sh_entsize;712 end += sh_entsize;
...@@ -719,9 +719,9 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void {...@@ -719,9 +719,9 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void {
719 const sh_entsize: u32 = @intCast(shdr.sh_entsize);719 const sh_entsize: u32 = @intCast(shdr.sh_entsize);
720 if (sh_entsize == 0) continue; // Malformed, don't split but don't error out720 if (sh_entsize == 0) continue; // Malformed, don't split but don't error out
721 if (shdr.sh_size % sh_entsize != 0) {721 if (shdr.sh_size % sh_entsize != 0) {
722 var err = try elf_file.addErrorWithNotes(1);722 var err = try elf_file.base.addErrorWithNotes(1);
723 try err.addMsg(elf_file, "size not a multiple of sh_entsize", .{});723 try err.addMsg("size not a multiple of sh_entsize", .{});
724 try err.addNote(elf_file, "in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });724 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
725 return error.MalformedObject;725 return error.MalformedObject;
726 }726 }
727727
...@@ -779,10 +779,10 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {...@@ -779,10 +779,10 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
779 const imsec = elf_file.inputMergeSection(imsec_index) orelse continue;779 const imsec = elf_file.inputMergeSection(imsec_index) orelse continue;
780 if (imsec.offsets.items.len == 0) continue;780 if (imsec.offsets.items.len == 0) continue;
781 const msub_index, const offset = imsec.findSubsection(@intCast(esym.st_value)) orelse {781 const msub_index, const offset = imsec.findSubsection(@intCast(esym.st_value)) orelse {
782 var err = try elf_file.addErrorWithNotes(2);782 var err = try elf_file.base.addErrorWithNotes(2);
783 try err.addMsg(elf_file, "invalid symbol value: {x}", .{esym.st_value});783 try err.addMsg("invalid symbol value: {x}", .{esym.st_value});
784 try err.addNote(elf_file, "for symbol {s}", .{sym.name(elf_file)});784 try err.addNote("for symbol {s}", .{sym.name(elf_file)});
785 try err.addNote(elf_file, "in {}", .{self.fmtPath()});785 try err.addNote("in {}", .{self.fmtPath()});
786 return error.MalformedObject;786 return error.MalformedObject;
787 };787 };
788788
...@@ -804,9 +804,9 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {...@@ -804,9 +804,9 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
804 const imsec = elf_file.inputMergeSection(imsec_index) orelse continue;804 const imsec = elf_file.inputMergeSection(imsec_index) orelse continue;
805 if (imsec.offsets.items.len == 0) continue;805 if (imsec.offsets.items.len == 0) continue;
806 const msub_index, const offset = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse {806 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);807 var err = try elf_file.base.addErrorWithNotes(1);
808 try err.addMsg(elf_file, "invalid relocation at offset 0x{x}", .{rel.r_offset});808 try err.addMsg("invalid relocation at offset 0x{x}", .{rel.r_offset});
809 try err.addNote(elf_file, "in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });809 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
810 return error.MalformedObject;810 return error.MalformedObject;
811 };811 };
812 const msub = elf_file.mergeSubsection(msub_index);812 const msub = elf_file.mergeSubsection(msub_index);
src/link/Elf/eh_frame.zig+3-3
...@@ -591,12 +591,12 @@ const riscv = struct {...@@ -591,12 +591,12 @@ const riscv = struct {
591};591};
592592
593fn reportInvalidReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela) !void {593fn reportInvalidReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela) !void {
594 var err = try elf_file.addErrorWithNotes(1);594 var err = try elf_file.base.addErrorWithNotes(1);
595 try err.addMsg(elf_file, "invalid relocation type {} at offset 0x{x}", .{595 try err.addMsg("invalid relocation type {} at offset 0x{x}", .{
596 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),596 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),
597 rel.r_offset,597 rel.r_offset,
598 });598 });
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()});
600 return error.RelocFailure;600 return error.RelocFailure;
601}601}
602602
src/link/Elf/relocatable.zig+4-4
...@@ -29,7 +29,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co...@@ -29,7 +29,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co
29 };29 };
30 }30 }
3131
32 if (comp.link_errors.items.len > 0) return error.FlushFailure;32 if (elf_file.base.hasErrors()) return error.FlushFailure;
3333
34 // First, we flush relocatable object file generated with our backends.34 // First, we flush relocatable object file generated with our backends.
35 if (elf_file.zigObjectPtr()) |zig_object| {35 if (elf_file.zigObjectPtr()) |zig_object| {
...@@ -146,7 +146,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co...@@ -146,7 +146,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co
146 try elf_file.base.file.?.setEndPos(total_size);146 try elf_file.base.file.?.setEndPos(total_size);
147 try elf_file.base.file.?.pwriteAll(buffer.items, 0);147 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;
150}150}
151151
152pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {152pub 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...@@ -177,7 +177,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const
177 };177 };
178 }178 }
179179
180 if (comp.link_errors.items.len > 0) return error.FlushFailure;180 if (elf_file.base.hasErrors()) return error.FlushFailure;
181181
182 // Now, we are ready to resolve the symbols across all input files.182 // Now, we are ready to resolve the symbols across all input files.
183 // We will first resolve the files in the ZigObject, next in the parsed183 // 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...@@ -216,7 +216,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const
216 try elf_file.writeShdrTable();216 try elf_file.writeShdrTable();
217 try elf_file.writeElfHeader();217 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;
220}220}
221221
222fn parsePositional(elf_file: *Elf, path: []const u8) Elf.ParseError!void {222fn 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...@@ -395,17 +395,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
395 }395 }
396396
397 for (positionals.items) |obj| {397 for (positionals.items) |obj| {
398 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {398 self.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
399 error.MalformedObject,399 error.UnknownFileType => try self.reportParseError(obj.path, "unknown file type for an input file", .{}),
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", .{}),
406 else => |e| try self.reportParseError(400 else => |e| try self.reportParseError(
407 obj.path,401 obj.path,
408 "unexpected error: parsing input file failed with error {s}",402 "unexpected error: reading input file failed with error {s}",
409 .{@errorName(e)},403 .{@errorName(e)},
410 ),404 ),
411 };405 };
...@@ -448,15 +442,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -448,15 +442,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
448 };442 };
449443
450 for (system_libs.items) |lib| {444 for (system_libs.items) |lib| {
451 self.parseLibrary(lib, false) catch |err| switch (err) {445 self.classifyInputFile(lib.path, lib, false) catch |err| switch (err) {
452 error.MalformedArchive,446 error.UnknownFileType => try self.reportParseError(lib.path, "unknown file type for an input file", .{}),
453 error.MalformedDylib,
454 error.InvalidCpuArch,
455 => continue, // already reported
456 error.UnknownFileType => try self.reportParseError(lib.path, "unknown file type for a library", .{}),
457 else => |e| try self.reportParseError(447 else => |e| try self.reportParseError(
458 lib.path,448 lib.path,
459 "unexpected error: parsing library failed with error {s}",449 "unexpected error: parsing input file failed with error {s}",
460 .{@errorName(e)},450 .{@errorName(e)},
461 ),451 ),
462 };452 };
...@@ -469,13 +459,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -469,13 +459,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
469 break :blk null;459 break :blk null;
470 };460 };
471 if (compiler_rt_path) |path| {461 if (compiler_rt_path) |path| {
472 self.parsePositional(path, false) catch |err| switch (err) {462 self.classifyInputFile(path, .{ .path = path }, false) catch |err| switch (err) {
473 error.MalformedObject,463 error.UnknownFileType => try self.reportParseError(path, "unknown file type for an input file", .{}),
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", .{}),
479 else => |e| try self.reportParseError(464 else => |e| try self.reportParseError(
480 path,465 path,
481 "unexpected error: parsing input file failed with error {s}",466 "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...@@ -484,30 +469,20 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
484 };469 };
485 }470 }
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| {474 try self.parseInputFiles();
490 self.getFile(index).?.dylib.umbrella = index;475 self.parseDependentDylibs() catch |err| {
491 }476 switch (err) {
492477 error.MissingLibraryDependencies => {},
493 if (self.dylibs.items.len > 0) {478 else => |e| try self.reportUnexpectedError(
494 self.parseDependentDylibs() catch |err| {479 "unexpected error while parsing dependent libraries: {s}",
495 switch (err) {480 .{@errorName(e)},
496 error.MissingLibraryDependencies => {},481 ),
497 else => |e| try self.reportUnexpectedError(482 }
498 "unexpected error while parsing dependent libraries: {s}",483 };
499 .{@errorName(e)},
500 ),
501 }
502 return error.FlushFailure;
503 };
504 }
505484
506 for (self.dylibs.items) |index| {485 if (self.base.hasErrors()) return error.FlushFailure;
507 const dylib = self.getFile(index).?.dylib;
508 if (!dylib.explicit and !dylib.hoisted) continue;
509 try dylib.initSymbols(self);
510 }
511486
512 {487 {
513 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));488 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
...@@ -841,181 +816,173 @@ pub fn resolveLibSystem(...@@ -841,181 +816,173 @@ pub fn resolveLibSystem(
841 });816 });
842}817}
843818
844pub const ParseError = error{819pub fn classifyInputFile(self: *MachO, path: []const u8, lib: SystemLib, must_link: bool) !void {
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 {
865 const tracy = trace(@src());820 const tracy = trace(@src());
866 defer tracy.end();821 defer tracy.end();
867 if (try Object.isObject(path)) {822
868 try self.parseObject(path);823 log.debug("classifying input file {s}", .{path});
869 } else {824
870 try self.parseLibrary(.{ .path = path }, must_link);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;
871 }845 }
846 _ = try self.addTbd(lib, true, fh);
872}847}
873848
874fn parseLibrary(self: *MachO, lib: SystemLib, must_link: bool) ParseError!void {849fn parseFatFile(self: *MachO, file: std.fs.File, path: []const u8) !?fat.Arch {
875 const tracy = trace(@src());850 const fat_h = fat.readFatHeader(file) catch return null;
876 defer tracy.end();851 if (fat_h.magic != macho.FAT_MAGIC and fat_h.magic != macho.FAT_MAGIC_64) return null;
877 if (try fat.isFatLibrary(lib.path)) {852 var fat_archs_buffer: [2]fat.Arch = undefined;
878 const fat_arch = try self.parseFatLibrary(lib.path);853 const fat_archs = try fat.parseArchs(file, fat_h, &fat_archs_buffer);
879 if (try Archive.isArchive(lib.path, fat_arch)) {854 const cpu_arch = self.getTarget().cpu.arch;
880 try self.parseArchive(lib, must_link, fat_arch);855 for (fat_archs) |arch| {
881 } else if (try Dylib.isDylib(lib.path, fat_arch)) {856 if (arch.tag == cpu_arch) return 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 };
893 }857 }
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;
894}870}
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 {
897 const tracy = trace(@src());879 const tracy = trace(@src());
898 defer tracy.end();880 defer tracy.end();
899881
900 const gpa = self.base.comp.gpa;882 const gpa = self.base.comp.gpa;
901 const file = try fs.cwd().openFile(path, .{});
902 const handle = try self.addFileHandle(file);
903 const mtime: u64 = mtime: {883 const mtime: u64 = mtime: {
884 const file = self.getFileHandle(handle);
904 const stat = file.stat() catch break :mtime 0;885 const stat = file.stat() catch break :mtime 0;
905 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));886 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
906 };887 };
907 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));888 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
908 self.files.set(index, .{889 self.files.set(index, .{ .object = .{
909 .object = .{890 .offset = offset,
910 .offset = 0, // TODO FAT objects891 .path = try gpa.dupe(u8, path),
911 .path = try gpa.dupe(u8, path),892 .file_handle = handle,
912 .file_handle = handle,893 .mtime = mtime,
913 .mtime = mtime,894 .index = index,
914 .index = index,895 } });
915 },
916 });
917 try self.objects.append(gpa, index);896 try self.objects.append(gpa, index);
918
919 const object = self.getFile(index).?.object;
920 try object.parse(self);
921}897}
922898
923pub fn parseFatLibrary(self: *MachO, path: []const u8) !fat.Arch {899pub fn parseInputFiles(self: *MachO) !void {
924 var buffer: [2]fat.Arch = undefined;900 const tracy = trace(@src());
925 const fat_archs = try fat.parseArchs(path, &buffer);901 defer tracy.end();
926 const cpu_arch = self.getTarget().cpu.arch;902
927 for (fat_archs) |arch| {903 for (self.objects.items) |index| {
928 if (arch.tag == cpu_arch) return arch;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 };
929 }920 }
930 try self.reportParseError(path, "missing arch in universal file: expected {s}", .{@tagName(cpu_arch)});
931 return error.InvalidCpuArch;
932}921}
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 {
935 const tracy = trace(@src());924 const tracy = trace(@src());
936 defer tracy.end();925 defer tracy.end();
937926
938 const gpa = self.base.comp.gpa;927 const gpa = self.base.comp.gpa;
939928
940 const file = try fs.cwd().openFile(lib.path, .{});
941 const handle = try self.addFileHandle(file);
942
943 var archive = Archive{};929 var archive = Archive{};
944 defer archive.deinit(gpa);930 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;933 for (archive.objects.items) |unpacked| {
948 for (archive.objects.items) |extracted| {934 const index: File.Index = @intCast(try self.files.addOne(gpa));
949 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));935 self.files.set(index, .{ .object = unpacked });
950 self.files.set(index, .{ .object = extracted });
951 const object = &self.files.items(.data)[index].object;936 const object = &self.files.items(.data)[index].object;
952 object.index = index;937 object.index = index;
953 object.alive = must_link or lib.needed; // TODO: or self.options.all_load;938 object.alive = must_link or lib.needed; // TODO: or self.options.all_load;
954 object.hidden = lib.hidden;939 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 };
962 try self.objects.append(gpa, index);940 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());
967 }941 }
968 if (has_parse_error) return error.MalformedArchive;
969}942}
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 {
972 const tracy = trace(@src());945 const tracy = trace(@src());
973 defer tracy.end();946 defer tracy.end();
974947
975 const gpa = self.base.comp.gpa;948 const gpa = self.base.comp.gpa;
976949
977 const file = try fs.cwd().openFile(lib.path, .{});950 const index: File.Index = @intCast(try self.files.addOne(gpa));
978 defer file.close();
979
980 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
981 self.files.set(index, .{ .dylib = .{951 self.files.set(index, .{ .dylib = .{
952 .offset = offset,
953 .file_handle = handle,
954 .tag = .dylib,
982 .path = try gpa.dupe(u8, lib.path),955 .path = try gpa.dupe(u8, lib.path),
983 .index = index,956 .index = index,
984 .needed = lib.needed,957 .needed = lib.needed,
985 .weak = lib.weak,958 .weak = lib.weak,
986 .reexport = lib.reexport,959 .reexport = lib.reexport,
987 .explicit = explicit,960 .explicit = explicit,
961 .umbrella = index,
988 } });962 } });
989 const dylib = &self.files.items(.data)[index].dylib;
990 try dylib.parse(self, file, fat_arch);
991
992 try self.dylibs.append(gpa, index);963 try self.dylibs.append(gpa, index);
993964
994 return index;965 return index;
995}966}
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 {
998 const tracy = trace(@src());969 const tracy = trace(@src());
999 defer tracy.end();970 defer tracy.end();
1000971
1001 const gpa = self.base.comp.gpa;972 const gpa = self.base.comp.gpa;
1002 const file = try fs.cwd().openFile(lib.path, .{});973 const index: File.Index = @intCast(try self.files.addOne(gpa));
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)));
1009 self.files.set(index, .{ .dylib = .{974 self.files.set(index, .{ .dylib = .{
975 .offset = 0,
976 .file_handle = handle,
977 .tag = .tbd,
1010 .path = try gpa.dupe(u8, lib.path),978 .path = try gpa.dupe(u8, lib.path),
1011 .index = index,979 .index = index,
1012 .needed = lib.needed,980 .needed = lib.needed,
1013 .weak = lib.weak,981 .weak = lib.weak,
1014 .reexport = lib.reexport,982 .reexport = lib.reexport,
1015 .explicit = explicit,983 .explicit = explicit,
984 .umbrella = index,
1016 } });985 } });
1017 const dylib = &self.files.items(.data)[index].dylib;
1018 try dylib.parseTbd(self.getTarget().cpu.arch, self.platform, lib_stub, self);
1019 try self.dylibs.append(gpa, index);986 try self.dylibs.append(gpa, index);
1020987
1021 return index;988 return index;
...@@ -1092,6 +1059,8 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1092,6 +1059,8 @@ fn parseDependentDylibs(self: *MachO) !void {
1092 const tracy = trace(@src());1059 const tracy = trace(@src());
1093 defer tracy.end();1060 defer tracy.end();
10941061
1062 if (self.dylibs.items.len == 0) return;
1063
1095 const gpa = self.base.comp.gpa;1064 const gpa = self.base.comp.gpa;
1096 const lib_dirs = self.lib_dirs;1065 const lib_dirs = self.lib_dirs;
1097 const framework_dirs = self.framework_dirs;1066 const framework_dirs = self.framework_dirs;
...@@ -1108,7 +1077,7 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1108,7 +1077,7 @@ fn parseDependentDylibs(self: *MachO) !void {
1108 while (index < self.dylibs.items.len) : (index += 1) {1077 while (index < self.dylibs.items.len) : (index += 1) {
1109 const dylib_index = self.dylibs.items[index];1078 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);
1112 defer dependents.deinit();1081 defer dependents.deinit();
1113 try dependents.ensureTotalCapacityPrecise(self.getFile(dylib_index).?.dylib.dependents.items.len);1082 try dependents.ensureTotalCapacityPrecise(self.getFile(dylib_index).?.dylib.dependents.items.len);
11141083
...@@ -1199,38 +1168,34 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1199,38 +1168,34 @@ fn parseDependentDylibs(self: *MachO) !void {
1199 .path = full_path,1168 .path = full_path,
1200 .weak = is_weak,1169 .weak = is_weak,
1201 };1170 };
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;
1202 const file_index = file_index: {1175 const file_index = file_index: {
1203 if (try fat.isFatLibrary(lib.path)) {1176 if (readMachHeader(file, offset) catch null) |h| blk: {
1204 const fat_arch = try self.parseFatLibrary(lib.path);1177 if (h.magic != macho.MH_MAGIC_64) break :blk;
1205 if (try Dylib.isDylib(lib.path, fat_arch)) {1178 switch (h.filetype) {
1206 break :file_index try self.parseDylib(lib, false, fat_arch);1179 macho.MH_DYLIB => break :file_index try self.addDylib(lib, false, fh, offset),
1207 } else break :file_index @as(File.Index, 0);1180 else => break :file_index @as(File.Index, 0),
1208 } else if (try Dylib.isDylib(lib.path, null)) {1181 }
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;
1216 }1182 }
1183 break :file_index try self.addTbd(lib, false, fh);
1217 };1184 };
1218 dependents.appendAssumeCapacity(.{ .id = id, .file = file_index });1185 dependents.appendAssumeCapacity(file_index);
1219 }1186 }
12201187
1221 const dylib = self.getFile(dylib_index).?.dylib;1188 const dylib = self.getFile(dylib_index).?.dylib;
1222 for (dependents.items) |entry| {1189 for (dylib.dependents.items, dependents.items) |id, file_index| {
1223 const id = entry.id;
1224 const file_index = entry.file;
1225 if (self.getFile(file_index)) |file| {1190 if (self.getFile(file_index)) |file| {
1226 const dep_dylib = file.dylib;1191 const dep_dylib = file.dylib;
1192 try dep_dylib.parse(self); // TODO in parallel
1227 dep_dylib.hoisted = self.isHoisted(id.name);1193 dep_dylib.hoisted = self.isHoisted(id.name);
1228 if (self.getFile(dep_dylib.umbrella) == null) {1194 dep_dylib.umbrella = dylib.umbrella;
1229 dep_dylib.umbrella = dylib.umbrella;
1230 }
1231 if (!dep_dylib.hoisted) {1195 if (!dep_dylib.hoisted) {
1232 const umbrella = dep_dylib.getUmbrella(self);1196 const umbrella = dep_dylib.getUmbrella(self);
1233 for (dep_dylib.exports.items(.name), dep_dylib.exports.items(.flags)) |off, flags| {1197 for (dep_dylib.exports.items(.name), dep_dylib.exports.items(.flags)) |off, flags| {
1198 // TODO rethink this entire algorithm
1234 try umbrella.addExport(gpa, dep_dylib.getString(off), flags);1199 try umbrella.addExport(gpa, dep_dylib.getString(off), flags);
1235 }1200 }
1236 try umbrella.rpaths.ensureUnusedCapacity(gpa, dep_dylib.rpaths.keys().len);1201 try umbrella.rpaths.ensureUnusedCapacity(gpa, dep_dylib.rpaths.keys().len);
...@@ -1238,15 +1203,13 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1238,15 +1203,13 @@ fn parseDependentDylibs(self: *MachO) !void {
1238 umbrella.rpaths.putAssumeCapacity(try gpa.dupe(u8, rpath), {});1203 umbrella.rpaths.putAssumeCapacity(try gpa.dupe(u8, rpath), {});
1239 }1204 }
1240 }1205 }
1241 } else {1206 } else try self.reportDependencyError(
1242 try self.reportDependencyError(1207 dylib.getUmbrella(self).index,
1243 dylib.getUmbrella(self).index,1208 id.name,
1244 id.name,1209 "unable to resolve dependency",
1245 "unable to resolve dependency",1210 .{},
1246 .{},1211 );
1247 );1212 has_errors = true;
1248 has_errors = true;
1249 }
1250 }1213 }
1251 }1214 }
12521215
...@@ -1533,8 +1496,8 @@ fn reportUndefs(self: *MachO) !void {...@@ -1533,8 +1496,8 @@ fn reportUndefs(self: *MachO) !void {
1533 const notes = entry.value_ptr.*;1496 const notes = entry.value_ptr.*;
1534 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);1497 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
15351498
1536 var err = try self.addErrorWithNotes(nnotes);1499 var err = try self.base.addErrorWithNotes(nnotes);
1537 try err.addMsg(self, "undefined symbol: {s}", .{undef_sym.getName(self)});1500 try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)});
1538 has_undefs = true;1501 has_undefs = true;
15391502
1540 var inote: usize = 0;1503 var inote: usize = 0;
...@@ -1542,12 +1505,12 @@ fn reportUndefs(self: *MachO) !void {...@@ -1542,12 +1505,12 @@ fn reportUndefs(self: *MachO) !void {
1542 const note = notes.items[inote];1505 const note = notes.items[inote];
1543 const file = self.getFile(note.file).?;1506 const file = self.getFile(note.file).?;
1544 const atom = note.getAtom(self).?;1507 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) });
1546 }1509 }
15471510
1548 if (notes.items.len > max_notes) {1511 if (notes.items.len > max_notes) {
1549 const remaining = notes.items.len - max_notes;1512 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});
1551 }1514 }
1552 }1515 }
1553 if (has_undefs) return error.HasUndefinedSymbols;1516 if (has_undefs) return error.HasUndefinedSymbols;
...@@ -3323,13 +3286,13 @@ fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !vo...@@ -3323,13 +3286,13 @@ fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !vo
33233286
3324 const mem_capacity = self.allocatedSizeVirtual(seg.vmaddr);3287 const mem_capacity = self.allocatedSizeVirtual(seg.vmaddr);
3325 if (needed_size > mem_capacity) {3288 if (needed_size > mem_capacity) {
3326 var err = try self.addErrorWithNotes(2);3289 var err = try self.base.addErrorWithNotes(2);
3327 try err.addMsg(self, "fatal linker error: cannot expand segment seg({d})({s}) in virtual memory", .{3290 try err.addMsg("fatal linker error: cannot expand segment seg({d})({s}) in virtual memory", .{
3328 seg_id,3291 seg_id,
3329 seg.segName(),3292 seg.segName(),
3330 });3293 });
3331 try err.addNote(self, "TODO: emit relocations to memory locations in self-hosted backends", .{});3294 try err.addNote("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", .{});3295 try err.addNote("as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
3333 }3296 }
33343297
3335 seg.vmsize = needed_size;3298 seg.vmsize = needed_size;
...@@ -3618,65 +3581,15 @@ pub fn eatPrefix(path: []const u8, prefix: []const u8) ?[]const u8 {...@@ -3618,65 +3581,15 @@ pub fn eatPrefix(path: []const u8, prefix: []const u8) ?[]const u8 {
3618 return null;3581 return null;
3619}3582}
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
3671pub fn reportParseError(3584pub fn reportParseError(
3672 self: *MachO,3585 self: *MachO,
3673 path: []const u8,3586 path: []const u8,
3674 comptime format: []const u8,3587 comptime format: []const u8,
3675 args: anytype,3588 args: anytype,
3676) error{OutOfMemory}!void {3589) error{OutOfMemory}!void {
3677 var err = try self.addErrorWithNotes(1);3590 var err = try self.base.addErrorWithNotes(1);
3678 try err.addMsg(self, format, args);3591 try err.addMsg(format, args);
3679 try err.addNote(self, "while parsing {s}", .{path});3592 try err.addNote("while parsing {s}", .{path});
3680}3593}
36813594
3682pub fn reportParseError2(3595pub fn reportParseError2(
...@@ -3685,9 +3598,9 @@ pub fn reportParseError2(...@@ -3685,9 +3598,9 @@ pub fn reportParseError2(
3685 comptime format: []const u8,3598 comptime format: []const u8,
3686 args: anytype,3599 args: anytype,
3687) error{OutOfMemory}!void {3600) error{OutOfMemory}!void {
3688 var err = try self.addErrorWithNotes(1);3601 var err = try self.base.addErrorWithNotes(1);
3689 try err.addMsg(self, format, args);3602 try err.addMsg(format, args);
3690 try err.addNote(self, "while parsing {}", .{self.getFile(file_index).?.fmtPath()});3603 try err.addNote("while parsing {}", .{self.getFile(file_index).?.fmtPath()});
3691}3604}
36923605
3693fn reportMissingLibraryError(3606fn reportMissingLibraryError(
...@@ -3696,10 +3609,10 @@ fn reportMissingLibraryError(...@@ -3696,10 +3609,10 @@ fn reportMissingLibraryError(
3696 comptime format: []const u8,3609 comptime format: []const u8,
3697 args: anytype,3610 args: anytype,
3698) error{OutOfMemory}!void {3611) error{OutOfMemory}!void {
3699 var err = try self.addErrorWithNotes(checked_paths.len);3612 var err = try self.base.addErrorWithNotes(checked_paths.len);
3700 try err.addMsg(self, format, args);3613 try err.addMsg(format, args);
3701 for (checked_paths) |path| {3614 for (checked_paths) |path| {
3702 try err.addNote(self, "tried {s}", .{path});3615 try err.addNote("tried {s}", .{path});
3703 }3616 }
3704}3617}
37053618
...@@ -3711,12 +3624,12 @@ fn reportMissingDependencyError(...@@ -3711,12 +3624,12 @@ fn reportMissingDependencyError(
3711 comptime format: []const u8,3624 comptime format: []const u8,
3712 args: anytype,3625 args: anytype,
3713) error{OutOfMemory}!void {3626) error{OutOfMemory}!void {
3714 var err = try self.addErrorWithNotes(2 + checked_paths.len);3627 var err = try self.base.addErrorWithNotes(2 + checked_paths.len);
3715 try err.addMsg(self, format, args);3628 try err.addMsg(format, args);
3716 try err.addNote(self, "while resolving {s}", .{path});3629 try err.addNote("while resolving {s}", .{path});
3717 try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()});3630 try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()});
3718 for (checked_paths) |p| {3631 for (checked_paths) |p| {
3719 try err.addNote(self, "tried {s}", .{p});3632 try err.addNote("tried {s}", .{p});
3720 }3633 }
3721}3634}
37223635
...@@ -3727,16 +3640,16 @@ fn reportDependencyError(...@@ -3727,16 +3640,16 @@ fn reportDependencyError(
3727 comptime format: []const u8,3640 comptime format: []const u8,
3728 args: anytype,3641 args: anytype,
3729) error{OutOfMemory}!void {3642) error{OutOfMemory}!void {
3730 var err = try self.addErrorWithNotes(2);3643 var err = try self.base.addErrorWithNotes(2);
3731 try err.addMsg(self, format, args);3644 try err.addMsg(format, args);
3732 try err.addNote(self, "while parsing {s}", .{path});3645 try err.addNote("while parsing {s}", .{path});
3733 try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()});3646 try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()});
3734}3647}
37353648
3736pub fn reportUnexpectedError(self: *MachO, comptime format: []const u8, args: anytype) error{OutOfMemory}!void {3649pub fn reportUnexpectedError(self: *MachO, comptime format: []const u8, args: anytype) error{OutOfMemory}!void {
3737 var err = try self.addErrorWithNotes(1);3650 var err = try self.base.addErrorWithNotes(1);
3738 try err.addMsg(self, format, args);3651 try err.addMsg(format, args);
3739 try err.addNote(self, "please report this as a linker bug on https://github.com/ziglang/zig/issues/new/choose", .{});3652 try err.addNote("please report this as a linker bug on https://github.com/ziglang/zig/issues/new/choose", .{});
3740}3653}
37413654
3742fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {3655fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
...@@ -3752,20 +3665,20 @@ fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {...@@ -3752,20 +3665,20 @@ fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
3752 const notes = entry.value_ptr.*;3665 const notes = entry.value_ptr.*;
3753 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);3666 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
37543667
3755 var err = try self.addErrorWithNotes(nnotes + 1);3668 var err = try self.base.addErrorWithNotes(nnotes + 1);
3756 try err.addMsg(self, "duplicate symbol definition: {s}", .{sym.getName(self)});3669 try err.addMsg("duplicate symbol definition: {s}", .{sym.getName(self)});
3757 try err.addNote(self, "defined by {}", .{sym.getFile(self).?.fmtPath()});3670 try err.addNote("defined by {}", .{sym.getFile(self).?.fmtPath()});
3758 has_dupes = true;3671 has_dupes = true;
37593672
3760 var inote: usize = 0;3673 var inote: usize = 0;
3761 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {3674 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
3762 const file = self.getFile(notes.items[inote]).?;3675 const file = self.getFile(notes.items[inote]).?;
3763 try err.addNote(self, "defined by {}", .{file.fmtPath()});3676 try err.addNote("defined by {}", .{file.fmtPath()});
3764 }3677 }
37653678
3766 if (notes.items.len > max_notes) {3679 if (notes.items.len > max_notes) {
3767 const remaining = notes.items.len - max_notes;3680 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});
3769 }3682 }
3770 }3683 }
3771 if (has_dupes) return error.HasDuplicates;3684 if (has_dupes) return error.HasDuplicates;
src/link/MachO/Archive.zig+1-12
...@@ -1,21 +1,10 @@...@@ -1,21 +1,10 @@
1objects: std.ArrayListUnmanaged(Object) = .{},1objects: 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
14pub fn deinit(self: *Archive, allocator: Allocator) void {3pub fn deinit(self: *Archive, allocator: Allocator) void {
15 self.objects.deinit(allocator);4 self.objects.deinit(allocator);
16}5}
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 {
19 const gpa = macho_file.base.comp.gpa;8 const gpa = macho_file.base.comp.gpa;
209
21 var arena = std.heap.ArenaAllocator.init(gpa);10 var arena = std.heap.ArenaAllocator.init(gpa);
src/link/MachO/Atom.zig+4-4
...@@ -906,15 +906,15 @@ const x86_64 = struct {...@@ -906,15 +906,15 @@ const x86_64 = struct {
906 encode(&.{inst}, code) catch return error.RelaxFail;906 encode(&.{inst}, code) catch return error.RelaxFail;
907 },907 },
908 else => |x| {908 else => |x| {
909 var err = try macho_file.addErrorWithNotes(2);909 var err = try macho_file.base.addErrorWithNotes(2);
910 try err.addMsg(macho_file, "{s}: 0x{x}: 0x{x}: failed to relax relocation of type {}", .{910 try err.addMsg("{s}: 0x{x}: 0x{x}: failed to relax relocation of type {}", .{
911 self.getName(macho_file),911 self.getName(macho_file),
912 self.getAddress(macho_file),912 self.getAddress(macho_file),
913 rel.offset,913 rel.offset,
914 rel.fmtPretty(.x86_64),914 rel.fmtPretty(.x86_64),
915 });915 });
916 try err.addNote(macho_file, "expected .mov instruction but found .{s}", .{@tagName(x)});916 try err.addNote("expected .mov instruction but found .{s}", .{@tagName(x)});
917 try err.addNote(macho_file, "while parsing {}", .{self.getFile(macho_file).fmtPath()});917 try err.addNote("while parsing {}", .{self.getFile(macho_file).fmtPath()});
918 return error.RelaxFailUnexpectedInstruction;918 return error.RelaxFailUnexpectedInstruction;
919 },919 },
920 }920 }
src/link/MachO/Dylib.zig+23-22
...@@ -1,5 +1,9 @@...@@ -1,5 +1,9 @@
1/// Non-zero for fat dylibs
2offset: u64,
1path: []const u8,3path: []const u8,
2index: File.Index,4index: File.Index,
5file_handle: File.HandleIndex,
6tag: enum { dylib, tbd },
37
4exports: std.MultiArrayList(Export) = .{},8exports: std.MultiArrayList(Export) = .{},
5strtab: std.ArrayListUnmanaged(u8) = .{},9strtab: std.ArrayListUnmanaged(u8) = .{},
...@@ -11,7 +15,7 @@ symbols_extra: std.ArrayListUnmanaged(u32) = .{},...@@ -11,7 +15,7 @@ symbols_extra: std.ArrayListUnmanaged(u32) = .{},
11globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},15globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},
12dependents: std.ArrayListUnmanaged(Id) = .{},16dependents: std.ArrayListUnmanaged(Id) = .{},
13rpaths: std.StringArrayHashMapUnmanaged(void) = .{},17rpaths: std.StringArrayHashMapUnmanaged(void) = .{},
14umbrella: File.Index = 0,18umbrella: File.Index,
15platform: ?MachO.Platform = null,19platform: ?MachO.Platform = null,
1620
17needed: bool,21needed: bool,
...@@ -23,16 +27,6 @@ referenced: bool = false,...@@ -23,16 +27,6 @@ referenced: bool = false,
2327
24output_symtab_ctx: MachO.SymtabCtx = .{},28output_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
36pub fn deinit(self: *Dylib, allocator: Allocator) void {30pub fn deinit(self: *Dylib, allocator: Allocator) void {
37 allocator.free(self.path);31 allocator.free(self.path);
38 self.exports.deinit(allocator);32 self.exports.deinit(allocator);
...@@ -51,12 +45,21 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {...@@ -51,12 +45,21 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {
51 self.rpaths.deinit(allocator);45 self.rpaths.deinit(allocator);
52}46}
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 {
55 const tracy = trace(@src());57 const tracy = trace(@src());
56 defer tracy.end();58 defer tracy.end();
5759
58 const gpa = macho_file.base.comp.gpa;60 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
61 log.debug("parsing dylib from binary: {s}", .{self.path});64 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 {...@@ -258,13 +261,7 @@ fn parseTrie(self: *Dylib, data: []const u8, macho_file: *MachO) !void {
258 try self.parseTrieNode(&it, gpa, arena.allocator(), "");261 try self.parseTrieNode(&it, gpa, arena.allocator(), "");
259}262}
260263
261pub fn parseTbd(264fn parseTbd(self: *Dylib, macho_file: *MachO) !void {
262 self: *Dylib,
263 cpu_arch: std.Target.Cpu.Arch,
264 platform: MachO.Platform,
265 lib_stub: LibStub,
266 macho_file: *MachO,
267) !void {
268 const tracy = trace(@src());265 const tracy = trace(@src());
269 defer tracy.end();266 defer tracy.end();
270267
...@@ -272,6 +269,9 @@ pub fn parseTbd(...@@ -272,6 +269,9 @@ pub fn parseTbd(
272269
273 log.debug("parsing dylib from stub: {s}", .{self.path});270 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();
275 const umbrella_lib = lib_stub.inner[0];275 const umbrella_lib = lib_stub.inner[0];
276276
277 {277 {
...@@ -290,7 +290,8 @@ pub fn parseTbd(...@@ -290,7 +290,8 @@ pub fn parseTbd(
290290
291 log.debug(" (install_name '{s}')", .{umbrella_lib.installName()});291 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
295 var matcher = try TargetMatcher.init(gpa, cpu_arch, self.platform.?.toApplePlatform());296 var matcher = try TargetMatcher.init(gpa, cpu_arch, self.platform.?.toApplePlatform());
296 defer matcher.deinit();297 defer matcher.deinit();
...@@ -495,7 +496,7 @@ fn addObjCExport(...@@ -495,7 +496,7 @@ fn addObjCExport(
495 try self.addExport(allocator, full_name, .{});496 try self.addExport(allocator, full_name, .{});
496}497}
497498
498pub fn initSymbols(self: *Dylib, macho_file: *MachO) !void {499fn initSymbols(self: *Dylib, macho_file: *MachO) !void {
499 const gpa = macho_file.base.comp.gpa;500 const gpa = macho_file.base.comp.gpa;
500501
501 const nsyms = self.exports.items(.name).len;502 const nsyms = self.exports.items(.name).len;
src/link/MachO/Object.zig+4-8
...@@ -38,13 +38,6 @@ compact_unwind_ctx: CompactUnwindCtx = .{},...@@ -38,13 +38,6 @@ compact_unwind_ctx: CompactUnwindCtx = .{},
38output_symtab_ctx: MachO.SymtabCtx = .{},38output_symtab_ctx: MachO.SymtabCtx = .{},
39output_ar_state: Archive.ArState = .{},39output_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
48pub fn deinit(self: *Object, allocator: Allocator) void {41pub fn deinit(self: *Object, allocator: Allocator) void {
49 if (self.in_archive) |*ar| allocator.free(ar.path);42 if (self.in_archive) |*ar| allocator.free(ar.path);
50 allocator.free(self.path);43 allocator.free(self.path);
...@@ -273,6 +266,9 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {...@@ -273,6 +266,9 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
273 atom.flags.alive = false;266 atom.flags.alive = false;
274 }267 }
275 }268 }
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());
276}272}
277273
278pub fn isCstringLiteral(sect: macho.section_64) bool {274pub fn isCstringLiteral(sect: macho.section_64) bool {
...@@ -2325,7 +2321,7 @@ fn hasSymbolStabs(self: Object) bool {...@@ -2325,7 +2321,7 @@ fn hasSymbolStabs(self: Object) bool {
2325 return self.stab_files.items.len > 0;2321 return self.stab_files.items.len > 0;
2326}2322}
23272323
2328pub fn hasObjc(self: Object) bool {2324fn hasObjC(self: Object) bool {
2329 for (self.symtab.items(.nlist)) |nlist| {2325 for (self.symtab.items(.nlist)) |nlist| {
2330 const name = self.getString(nlist.n_strx);2326 const name = self.getString(nlist.n_strx);
2331 if (mem.startsWith(u8, name, "_OBJC_CLASS_$_")) return true;2327 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();...@@ -8,11 +8,17 @@ const native_endian = builtin.target.cpu.arch.endian();
88
9const MachO = @import("../MachO.zig");9const MachO = @import("../MachO.zig");
1010
11pub fn isFatLibrary(path: []const u8) !bool {11pub fn readFatHeader(file: std.fs.File) !macho.fat_header {
12 const file = try std.fs.cwd().openFile(path, .{});12 return readFatHeaderGeneric(macho.fat_header, file, 0);
13 defer file.close();13}
14 const hdr = file.reader().readStructEndian(macho.fat_header, .big) catch return false;14
15 return hdr.magic == macho.FAT_MAGIC;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;
16}22}
1723
18pub const Arch = struct {24pub const Arch = struct {
...@@ -21,17 +27,12 @@ pub const Arch = struct {...@@ -21,17 +27,12 @@ pub const Arch = struct {
21 size: u32,27 size: u32,
22};28};
2329
24pub fn parseArchs(path: []const u8, buffer: *[2]Arch) ![]const Arch {30pub fn parseArchs(file: std.fs.File, fat_header: macho.fat_header, out: *[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
31 var count: usize = 0;31 var count: usize = 0;
32 var fat_arch_index: u32 = 0;32 var fat_arch_index: u32 = 0;
33 while (fat_arch_index < fat_header.nfat_arch) : (fat_arch_index += 1) {33 while (fat_arch_index < fat_header.nfat_arch and count < out.len) : (fat_arch_index += 1) {
34 const fat_arch = try reader.readStructEndian(macho.fat_arch, .big);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);
35 // If we come across an architecture that we do not know how to handle, that's36 // If we come across an architecture that we do not know how to handle, that's
36 // fine because we can keep looking for one that might match.37 // fine because we can keep looking for one that might match.
37 const arch: std.Target.Cpu.Arch = switch (fat_arch.cputype) {38 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 {...@@ -39,9 +40,9 @@ pub fn parseArchs(path: []const u8, buffer: *[2]Arch) ![]const Arch {
39 macho.CPU_TYPE_X86_64 => if (fat_arch.cpusubtype == macho.CPU_SUBTYPE_X86_64_ALL) .x86_64 else continue,40 macho.CPU_TYPE_X86_64 => if (fat_arch.cpusubtype == macho.CPU_SUBTYPE_X86_64_ALL) .x86_64 else continue,
40 else => continue,41 else => continue,
41 };42 };
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 };
43 count += 1;44 count += 1;
44 }45 }
4546
46 return buffer[0..count];47 return out[0..count];
47}48}
src/link/MachO/file.zig+15
...@@ -335,6 +335,21 @@ pub const File = union(enum) {...@@ -335,6 +335,21 @@ pub const File = union(enum) {
335 };335 };
336 }336 }
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
338 pub const Index = u32;353 pub const Index = u32;
339354
340 pub const Entry = union(enum) {355 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...@@ -27,22 +27,21 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c
27 }27 }
2828
29 for (positionals.items) |obj| {29 for (positionals.items) |obj| {
30 macho_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {30 macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
31 error.MalformedObject,31 error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an input file", .{}),
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", .{}),
37 else => |e| try macho_file.reportParseError(32 else => |e| try macho_file.reportParseError(
38 obj.path,33 obj.path,
39 "unexpected error: parsing input file failed with error {s}",34 "unexpected error: reading input file failed with error {s}",
40 .{@errorName(e)},35 .{@errorName(e)},
41 ),36 ),
42 };37 };
43 }38 }
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
47 try macho_file.resolveSymbols();46 try macho_file.resolveSymbols();
48 try macho_file.dedupLiterals();47 try macho_file.dedupLiterals();
...@@ -93,22 +92,21 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -93,22 +92,21 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
93 }92 }
9493
95 for (positionals.items) |obj| {94 for (positionals.items) |obj| {
96 parsePositional(macho_file, obj.path) catch |err| switch (err) {95 macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
97 error.MalformedObject,96 error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an input file", .{}),
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", .{}),
103 else => |e| try macho_file.reportParseError(97 else => |e| try macho_file.reportParseError(
104 obj.path,98 obj.path,
105 "unexpected error: parsing input file failed with error {s}",99 "unexpected error: reading input file failed with error {s}",
106 .{@errorName(e)},100 .{@errorName(e)},
107 ),101 ),
108 };102 };
109 }103 }
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
113 // First, we flush relocatable object file generated with our backends.111 // First, we flush relocatable object file generated with our backends.
114 if (macho_file.getZigObject()) |zo| {112 if (macho_file.getZigObject()) |zo| {
...@@ -225,79 +223,19 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -225,79 +223,19 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
225 try macho_file.base.file.?.setEndPos(total_size);223 try macho_file.base.file.?.setEndPos(total_size);
226 try macho_file.base.file.?.pwriteAll(buffer.items, 0);224 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;
229}227}
230228
231fn parsePositional(macho_file: *MachO, path: []const u8) MachO.ParseError!void {229fn parseInputFilesAr(macho_file: *MachO) !void {
232 const tracy = trace(@src());230 const tracy = trace(@src());
233 defer tracy.end();231 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 {233 for (macho_file.objects.items) |index| {
247 const tracy = trace(@src());234 macho_file.getFile(index).?.parseAr(macho_file) catch |err| switch (err) {
248 defer tracy.end();235 error.InvalidCpuArch => {}, // already reported
249236 else => |e| try macho_file.reportParseError2(index, "unexpected error: parsing input file failed with error {s}", .{@errorName(e)}),
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,
297 };237 };
298 try macho_file.objects.append(gpa, index);
299 }238 }
300 if (has_parse_error) return error.MalformedArchive;
301}239}
302240
303fn markExports(macho_file: *MachO) void {241fn markExports(macho_file: *MachO) void {
src/link/Wasm.zig+89-127
...@@ -658,9 +658,9 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {...@@ -658,9 +658,9 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {
658 var object = Object.create(wasm, obj_file, path, null) catch |err| switch (err) {658 var object = Object.create(wasm, obj_file, path, null) catch |err| switch (err) {
659 error.InvalidMagicByte, error.NotObjectFile => return false,659 error.InvalidMagicByte, error.NotObjectFile => return false,
660 else => |e| {660 else => |e| {
661 var err_note = try wasm.addErrorWithNotes(1);661 var err_note = try wasm.base.addErrorWithNotes(1);
662 try err_note.addMsg(wasm, "Failed parsing object file: {s}", .{@errorName(e)});662 try err_note.addMsg("Failed parsing object file: {s}", .{@errorName(e)});
663 try err_note.addNote(wasm, "while parsing '{s}'", .{path});663 try err_note.addNote("while parsing '{s}'", .{path});
664 return error.FlushFailure;664 return error.FlushFailure;
665 },665 },
666 };666 };
...@@ -714,9 +714,9 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {...@@ -714,9 +714,9 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
714 return false;714 return false;
715 },715 },
716 else => |e| {716 else => |e| {
717 var err_note = try wasm.addErrorWithNotes(1);717 var err_note = try wasm.base.addErrorWithNotes(1);
718 try err_note.addMsg(wasm, "Failed parsing archive: {s}", .{@errorName(e)});718 try err_note.addMsg("Failed parsing archive: {s}", .{@errorName(e)});
719 try err_note.addNote(wasm, "while parsing archive {s}", .{path});719 try err_note.addNote("while parsing archive {s}", .{path});
720 return error.FlushFailure;720 return error.FlushFailure;
721 },721 },
722 };722 };
...@@ -741,9 +741,9 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {...@@ -741,9 +741,9 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
741741
742 for (offsets.keys()) |file_offset| {742 for (offsets.keys()) |file_offset| {
743 var object = archive.parseObject(wasm, file_offset) catch |e| {743 var object = archive.parseObject(wasm, file_offset) catch |e| {
744 var err_note = try wasm.addErrorWithNotes(1);744 var err_note = try wasm.base.addErrorWithNotes(1);
745 try err_note.addMsg(wasm, "Failed parsing object: {s}", .{@errorName(e)});745 try err_note.addMsg("Failed parsing object: {s}", .{@errorName(e)});
746 try err_note.addNote(wasm, "while parsing object in archive {s}", .{path});746 try err_note.addNote("while parsing object in archive {s}", .{path});
747 return error.FlushFailure;747 return error.FlushFailure;
748 };748 };
749 object.index = @enumFromInt(wasm.files.len);749 object.index = @enumFromInt(wasm.files.len);
...@@ -779,9 +779,9 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {...@@ -779,9 +779,9 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
779779
780 if (symbol.isLocal()) {780 if (symbol.isLocal()) {
781 if (symbol.isUndefined()) {781 if (symbol.isUndefined()) {
782 var err = try wasm.addErrorWithNotes(1);782 var err = try wasm.base.addErrorWithNotes(1);
783 try err.addMsg(wasm, "Local symbols are not allowed to reference imports", .{});783 try err.addMsg("Local symbols are not allowed to reference imports", .{});
784 try err.addNote(wasm, "symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() });784 try err.addNote("symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() });
785 }785 }
786 try wasm.resolved_symbols.putNoClobber(gpa, location, {});786 try wasm.resolved_symbols.putNoClobber(gpa, location, {});
787 continue;787 continue;
...@@ -816,10 +816,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {...@@ -816,10 +816,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
816 break :outer; // existing is weak, while new one isn't. Replace it.816 break :outer; // existing is weak, while new one isn't. Replace it.
817 }817 }
818 // both are defined and weak, we have a symbol collision.818 // both are defined and weak, we have a symbol collision.
819 var err = try wasm.addErrorWithNotes(2);819 var err = try wasm.base.addErrorWithNotes(2);
820 try err.addMsg(wasm, "symbol '{s}' defined multiple times", .{sym_name});820 try err.addMsg("symbol '{s}' defined multiple times", .{sym_name});
821 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});821 try err.addNote("first definition in '{s}'", .{existing_file_path});
822 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});822 try err.addNote("next definition in '{s}'", .{obj_file.path()});
823 }823 }
824824
825 try wasm.discarded.put(gpa, location, existing_loc);825 try wasm.discarded.put(gpa, location, existing_loc);
...@@ -827,10 +827,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {...@@ -827,10 +827,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
827 }827 }
828828
829 if (symbol.tag != existing_sym.tag) {829 if (symbol.tag != existing_sym.tag) {
830 var err = try wasm.addErrorWithNotes(2);830 var err = try wasm.base.addErrorWithNotes(2);
831 try err.addMsg(wasm, "symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) });831 try err.addMsg("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});832 try err.addNote("first definition in '{s}'", .{existing_file_path});
833 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});833 try err.addNote("next definition in '{s}'", .{obj_file.path()});
834 }834 }
835835
836 if (existing_sym.isUndefined() and symbol.isUndefined()) {836 if (existing_sym.isUndefined() and symbol.isUndefined()) {
...@@ -847,14 +847,14 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {...@@ -847,14 +847,14 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
847 const imp = obj_file.import(sym_index);847 const imp = obj_file.import(sym_index);
848 const module_name = obj_file.string(imp.module_name);848 const module_name = obj_file.string(imp.module_name);
849 if (!mem.eql(u8, existing_name, module_name)) {849 if (!mem.eql(u8, existing_name, module_name)) {
850 var err = try wasm.addErrorWithNotes(2);850 var err = try wasm.base.addErrorWithNotes(2);
851 try err.addMsg(wasm, "symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{851 try err.addMsg("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{
852 sym_name,852 sym_name,
853 existing_name,853 existing_name,
854 module_name,854 module_name,
855 });855 });
856 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});856 try err.addNote("first definition in '{s}'", .{existing_file_path});
857 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});857 try err.addNote("next definition in '{s}'", .{obj_file.path()});
858 }858 }
859 }859 }
860860
...@@ -867,10 +867,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {...@@ -867,10 +867,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
867 const existing_ty = wasm.getGlobalType(existing_loc);867 const existing_ty = wasm.getGlobalType(existing_loc);
868 const new_ty = wasm.getGlobalType(location);868 const new_ty = wasm.getGlobalType(location);
869 if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) {869 if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) {
870 var err = try wasm.addErrorWithNotes(2);870 var err = try wasm.base.addErrorWithNotes(2);
871 try err.addMsg(wasm, "symbol '{s}' mismatching global types", .{sym_name});871 try err.addMsg("symbol '{s}' mismatching global types", .{sym_name});
872 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});872 try err.addNote("first definition in '{s}'", .{existing_file_path});
873 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});873 try err.addNote("next definition in '{s}'", .{obj_file.path()});
874 }874 }
875 }875 }
876876
...@@ -878,11 +878,11 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {...@@ -878,11 +878,11 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
878 const existing_ty = wasm.getFunctionSignature(existing_loc);878 const existing_ty = wasm.getFunctionSignature(existing_loc);
879 const new_ty = wasm.getFunctionSignature(location);879 const new_ty = wasm.getFunctionSignature(location);
880 if (!existing_ty.eql(new_ty)) {880 if (!existing_ty.eql(new_ty)) {
881 var err = try wasm.addErrorWithNotes(3);881 var err = try wasm.base.addErrorWithNotes(3);
882 try err.addMsg(wasm, "symbol '{s}' mismatching function signatures.", .{sym_name});882 try err.addMsg("symbol '{s}' mismatching function signatures.", .{sym_name});
883 try err.addNote(wasm, "expected signature {}, but found signature {}", .{ existing_ty, new_ty });883 try err.addNote("expected signature {}, but found signature {}", .{ existing_ty, new_ty });
884 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});884 try err.addNote("first definition in '{s}'", .{existing_file_path});
885 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});885 try err.addNote("next definition in '{s}'", .{obj_file.path()});
886 }886 }
887 }887 }
888888
...@@ -930,9 +930,9 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {...@@ -930,9 +930,9 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
930 // Parse object and and resolve symbols again before we check remaining930 // Parse object and and resolve symbols again before we check remaining
931 // undefined symbols.931 // undefined symbols.
932 var object = archive.parseObject(wasm, offset.items[0]) catch |e| {932 var object = archive.parseObject(wasm, offset.items[0]) catch |e| {
933 var err_note = try wasm.addErrorWithNotes(1);933 var err_note = try wasm.base.addErrorWithNotes(1);
934 try err_note.addMsg(wasm, "Failed parsing object: {s}", .{@errorName(e)});934 try err_note.addMsg("Failed parsing object: {s}", .{@errorName(e)});
935 try err_note.addNote(wasm, "while parsing object in archive {s}", .{archive.name});935 try err_note.addNote("while parsing object in archive {s}", .{archive.name});
936 return error.FlushFailure;936 return error.FlushFailure;
937 };937 };
938 object.index = @enumFromInt(wasm.files.len);938 object.index = @enumFromInt(wasm.files.len);
...@@ -1237,9 +1237,9 @@ fn validateFeatures(...@@ -1237,9 +1237,9 @@ fn validateFeatures(
1237 allowed[used_index] = is_enabled;1237 allowed[used_index] = is_enabled;
1238 emit_features_count.* += @intFromBool(is_enabled);1238 emit_features_count.* += @intFromBool(is_enabled);
1239 } else if (is_enabled and !allowed[used_index]) {1239 } else if (is_enabled and !allowed[used_index]) {
1240 var err = try wasm.addErrorWithNotes(1);1240 var err = try wasm.base.addErrorWithNotes(1);
1241 try err.addMsg(wasm, "feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))});1241 try err.addMsg("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});1242 try err.addNote("defined in '{s}'", .{wasm.files.items(.data)[used_set >> 1].object.path});
1243 valid_feature_set = false;1243 valid_feature_set = false;
1244 }1244 }
1245 }1245 }
...@@ -1251,7 +1251,8 @@ fn validateFeatures(...@@ -1251,7 +1251,8 @@ fn validateFeatures(
1251 if (shared_memory) {1251 if (shared_memory) {
1252 const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)];1252 const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)];
1253 if (@as(u1, @truncate(disallowed_feature)) != 0) {1253 if (@as(u1, @truncate(disallowed_feature)) != 0) {
1254 try wasm.addErrorWithoutNotes(1254 var err = try wasm.base.addErrorWithNotes(0);
1255 try err.addMsg(
1255 "shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled",1256 "shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled",
1256 .{wasm.files.items(.data)[disallowed_feature >> 1].object.path},1257 .{wasm.files.items(.data)[disallowed_feature >> 1].object.path},
1257 );1258 );
...@@ -1260,7 +1261,8 @@ fn validateFeatures(...@@ -1260,7 +1261,8 @@ fn validateFeatures(
12601261
1261 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {1262 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
1262 if (!allowed[@intFromEnum(feature)]) {1263 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});
1264 }1266 }
1265 }1267 }
1266 }1268 }
...@@ -1268,7 +1270,8 @@ fn validateFeatures(...@@ -1268,7 +1270,8 @@ fn validateFeatures(
1268 if (has_tls) {1270 if (has_tls) {
1269 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {1271 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
1270 if (!allowed[@intFromEnum(feature)]) {1272 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});
1272 }1275 }
1273 }1276 }
1274 }1277 }
...@@ -1281,10 +1284,10 @@ fn validateFeatures(...@@ -1281,10 +1284,10 @@ fn validateFeatures(
1281 // from here a feature is always used1284 // from here a feature is always used
1282 const disallowed_feature = disallowed[@intFromEnum(feature.tag)];1285 const disallowed_feature = disallowed[@intFromEnum(feature.tag)];
1283 if (@as(u1, @truncate(disallowed_feature)) != 0) {1286 if (@as(u1, @truncate(disallowed_feature)) != 0) {
1284 var err = try wasm.addErrorWithNotes(2);1287 var err = try wasm.base.addErrorWithNotes(2);
1285 try err.addMsg(wasm, "feature '{}' is disallowed, but used by linked object", .{feature.tag});1288 try err.addMsg("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});1289 try err.addNote("disallowed by '{s}'", .{wasm.files.items(.data)[disallowed_feature >> 1].object.path});
1287 try err.addNote(wasm, "used in '{s}'", .{object.path});1290 try err.addNote("used in '{s}'", .{object.path});
1288 valid_feature_set = false;1291 valid_feature_set = false;
1289 }1292 }
12901293
...@@ -1295,10 +1298,10 @@ fn validateFeatures(...@@ -1295,10 +1298,10 @@ fn validateFeatures(
1295 for (required, 0..) |required_feature, feature_index| {1298 for (required, 0..) |required_feature, feature_index| {
1296 const is_required = @as(u1, @truncate(required_feature)) != 0;1299 const is_required = @as(u1, @truncate(required_feature)) != 0;
1297 if (is_required and !object_used_features[feature_index]) {1300 if (is_required and !object_used_features[feature_index]) {
1298 var err = try wasm.addErrorWithNotes(2);1301 var err = try wasm.base.addErrorWithNotes(2);
1299 try err.addMsg(wasm, "feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))});1302 try err.addMsg("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});1303 try err.addNote("required by '{s}'", .{wasm.files.items(.data)[required_feature >> 1].object.path});
1301 try err.addNote(wasm, "missing in '{s}'", .{object.path});1304 try err.addNote("missing in '{s}'", .{object.path});
1302 valid_feature_set = false;1305 valid_feature_set = false;
1303 }1306 }
1304 }1307 }
...@@ -1376,9 +1379,9 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void {...@@ -1376,9 +1379,9 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void {
1376 else1379 else
1377 wasm.name;1380 wasm.name;
1378 const symbol_name = undef.getName(wasm);1381 const symbol_name = undef.getName(wasm);
1379 var err = try wasm.addErrorWithNotes(1);1382 var err = try wasm.base.addErrorWithNotes(1);
1380 try err.addMsg(wasm, "could not resolve undefined symbol '{s}'", .{symbol_name});1383 try err.addMsg("could not resolve undefined symbol '{s}'", .{symbol_name});
1381 try err.addNote(wasm, "defined in '{s}'", .{file_name});1384 try err.addNote("defined in '{s}'", .{file_name});
1382 }1385 }
1383 }1386 }
1384 if (found_undefined_symbols) {1387 if (found_undefined_symbols) {
...@@ -1757,7 +1760,8 @@ fn setupInitFunctions(wasm: *Wasm) !void {...@@ -1757,7 +1760,8 @@ fn setupInitFunctions(wasm: *Wasm) !void {
1757 break :ty object.func_types[func.type_index];1760 break :ty object.func_types[func.type_index];
1758 };1761 };
1759 if (ty.params.len != 0) {1762 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)});
1761 }1765 }
1762 log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)});1766 log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)});
1763 wasm.init_funcs.appendAssumeCapacity(.{1767 wasm.init_funcs.appendAssumeCapacity(.{
...@@ -2140,7 +2144,8 @@ fn checkExportNames(wasm: *Wasm) !void {...@@ -2140,7 +2144,8 @@ fn checkExportNames(wasm: *Wasm) !void {
21402144
2141 for (force_exp_names) |exp_name| {2145 for (force_exp_names) |exp_name| {
2142 const loc = wasm.findGlobalSymbol(exp_name) orelse {2146 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});
2144 failed_exports = true;2149 failed_exports = true;
2145 continue;2150 continue;
2146 };2151 };
...@@ -2203,13 +2208,15 @@ fn setupStart(wasm: *Wasm) !void {...@@ -2203,13 +2208,15 @@ fn setupStart(wasm: *Wasm) !void {
2203 const entry_name = wasm.entry_name orelse return;2208 const entry_name = wasm.entry_name orelse return;
22042209
2205 const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {2210 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});
2207 return error.FlushFailure;2213 return error.FlushFailure;
2208 };2214 };
22092215
2210 const symbol = symbol_loc.getSymbol(wasm);2216 const symbol = symbol_loc.getSymbol(wasm);
2211 if (symbol.tag != .function) {2217 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});
2213 return error.FlushFailure;2220 return error.FlushFailure;
2214 }2221 }
22152222
...@@ -2314,13 +2321,16 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2314,13 +2321,16 @@ fn setupMemory(wasm: *Wasm) !void {
23142321
2315 if (wasm.initial_memory) |initial_memory| {2322 if (wasm.initial_memory) |initial_memory| {
2316 if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) {2323 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});
2318 }2326 }
2319 if (memory_ptr > initial_memory) {2327 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});
2321 }2330 }
2322 if (initial_memory > max_memory_allowed) {2331 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});
2324 }2334 }
2325 memory_ptr = initial_memory;2335 memory_ptr = initial_memory;
2326 }2336 }
...@@ -2337,13 +2347,16 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2337,13 +2347,16 @@ fn setupMemory(wasm: *Wasm) !void {
23372347
2338 if (wasm.max_memory) |max_memory| {2348 if (wasm.max_memory) |max_memory| {
2339 if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) {2349 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});
2341 }2352 }
2342 if (memory_ptr > max_memory) {2353 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});
2344 }2356 }
2345 if (max_memory > max_memory_allowed) {2357 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});
2347 }2360 }
2348 wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size));2361 wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size));
2349 wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX);2362 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...@@ -2446,9 +2459,9 @@ pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: Sym
2446 break :blk index;2459 break :blk index;
2447 };2460 };
2448 } else {2461 } else {
2449 var err = try wasm.addErrorWithNotes(1);2462 var err = try wasm.base.addErrorWithNotes(1);
2450 try err.addMsg(wasm, "found unknown section '{s}'", .{section_name});2463 try err.addMsg("found unknown section '{s}'", .{section_name});
2451 try err.addNote(wasm, "defined in '{s}'", .{obj_file.path()});2464 try err.addNote("defined in '{s}'", .{obj_file.path()});
2452 return error.UnexpectedValue;2465 return error.UnexpectedValue;
2453 }2466 }
2454 },2467 },
...@@ -2564,23 +2577,23 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -2564,23 +2577,23 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
2564 if (wasm.zig_object_index != .null) {2577 if (wasm.zig_object_index != .null) {
2565 try wasm.resolveSymbolsInObject(wasm.zig_object_index);2578 try wasm.resolveSymbolsInObject(wasm.zig_object_index);
2566 }2579 }
2567 if (comp.link_errors.items.len > 0) return error.FlushFailure;2580 if (wasm.base.hasErrors()) return error.FlushFailure;
2568 for (wasm.objects.items) |object_index| {2581 for (wasm.objects.items) |object_index| {
2569 try wasm.resolveSymbolsInObject(object_index);2582 try wasm.resolveSymbolsInObject(object_index);
2570 }2583 }
2571 if (comp.link_errors.items.len > 0) return error.FlushFailure;2584 if (wasm.base.hasErrors()) return error.FlushFailure;
25722585
2573 var emit_features_count: u32 = 0;2586 var emit_features_count: u32 = 0;
2574 var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;2587 var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;
2575 try wasm.validateFeatures(&enabled_features, &emit_features_count);2588 try wasm.validateFeatures(&enabled_features, &emit_features_count);
2576 try wasm.resolveSymbolsInArchives();2589 try wasm.resolveSymbolsInArchives();
2577 if (comp.link_errors.items.len > 0) return error.FlushFailure;2590 if (wasm.base.hasErrors()) return error.FlushFailure;
2578 try wasm.resolveLazySymbols();2591 try wasm.resolveLazySymbols();
2579 try wasm.checkUndefinedSymbols();2592 try wasm.checkUndefinedSymbols();
2580 try wasm.checkExportNames();2593 try wasm.checkExportNames();
25812594
2582 try wasm.setupInitFunctions();2595 try wasm.setupInitFunctions();
2583 if (comp.link_errors.items.len > 0) return error.FlushFailure;2596 if (wasm.base.hasErrors()) return error.FlushFailure;
2584 try wasm.setupStart();2597 try wasm.setupStart();
25852598
2586 try wasm.markReferences();2599 try wasm.markReferences();
...@@ -2589,7 +2602,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -2589,7 +2602,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
2589 try wasm.mergeTypes();2602 try wasm.mergeTypes();
2590 try wasm.allocateAtoms();2603 try wasm.allocateAtoms();
2591 try wasm.setupMemory();2604 try wasm.setupMemory();
2592 if (comp.link_errors.items.len > 0) return error.FlushFailure;2605 if (wasm.base.hasErrors()) return error.FlushFailure;
2593 wasm.allocateVirtualAddresses();2606 wasm.allocateVirtualAddresses();
2594 wasm.mapFunctionTable();2607 wasm.mapFunctionTable();
2595 try wasm.initializeCallCtorsFunction();2608 try wasm.initializeCallCtorsFunction();
...@@ -2599,7 +2612,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -2599,7 +2612,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
2599 try wasm.setupStartSection();2612 try wasm.setupStartSection();
2600 try wasm.setupExports();2613 try wasm.setupExports();
2601 try wasm.writeToFile(enabled_features, emit_features_count, arena);2614 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;
2603}2616}
26042617
2605/// Writes the WebAssembly in-memory module to the file2618/// Writes the WebAssembly in-memory module to the file
...@@ -2997,7 +3010,10 @@ fn writeToFile(...@@ -2997,7 +3010,10 @@ fn writeToFile(
2997 }) catch unreachable;3010 }) catch unreachable;
2998 try emitBuildIdSection(&binary_bytes, str);3011 try emitBuildIdSection(&binary_bytes, str);
2999 },3012 },
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 },
3001 }3017 }
30023018
3003 var debug_bytes = std.ArrayList(u8).init(gpa);3019 var debug_bytes = std.ArrayList(u8).init(gpa);
...@@ -4086,57 +4102,3 @@ fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8...@@ -4086,57 +4102,3 @@ fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8
4086 .command => "_start",4102 .command => "_start",
4087 };4103 };
4088}4104}
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...@@ -235,27 +235,27 @@ fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?S
235 if (object.imported_tables_count == table_count) return null;235 if (object.imported_tables_count == table_count) return null;
236236
237 if (table_count != 0) {237 if (table_count != 0) {
238 var err = try wasm_file.addErrorWithNotes(1);238 var err = try wasm_file.base.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.", .{239 try err.addMsg("Expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
240 object.imported_tables_count,240 object.imported_tables_count,
241 table_count,241 table_count,
242 });242 });
243 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});243 try err.addNote("defined in '{s}'", .{object.path});
244 return error.MissingTableSymbols;244 return error.MissingTableSymbols;
245 }245 }
246246
247 // MVP object files cannot have any table definitions, only imports (for the indirect function table).247 // MVP object files cannot have any table definitions, only imports (for the indirect function table).
248 if (object.tables.len > 0) {248 if (object.tables.len > 0) {
249 var err = try wasm_file.addErrorWithNotes(1);249 var err = try wasm_file.base.addErrorWithNotes(1);
250 try err.addMsg(wasm_file, "Unexpected table definition without representing table symbols.", .{});250 try err.addMsg("Unexpected table definition without representing table symbols.", .{});
251 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});251 try err.addNote("defined in '{s}'", .{object.path});
252 return error.UnexpectedTable;252 return error.UnexpectedTable;
253 }253 }
254254
255 if (object.imported_tables_count != 1) {255 if (object.imported_tables_count != 1) {
256 var err = try wasm_file.addErrorWithNotes(1);256 var err = try wasm_file.base.addErrorWithNotes(1);
257 try err.addMsg(wasm_file, "Found more than one table import, but no representing table symbols", .{});257 try err.addMsg("Found more than one table import, but no representing table symbols", .{});
258 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});258 try err.addNote("defined in '{s}'", .{object.path});
259 return error.MissingTableSymbols;259 return error.MissingTableSymbols;
260 }260 }
261261
...@@ -266,9 +266,9 @@ fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?S...@@ -266,9 +266,9 @@ fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?S
266 } else unreachable;266 } else unreachable;
267267
268 if (!std.mem.eql(u8, object.string_table.get(table_import.name), "__indirect_function_table")) {268 if (!std.mem.eql(u8, object.string_table.get(table_import.name), "__indirect_function_table")) {
269 var err = try wasm_file.addErrorWithNotes(1);269 var err = try wasm_file.base.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)});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(wasm_file, "defined in '{s}'", .{object.path});271 try err.addNote("defined in '{s}'", .{object.path});
272 return error.MissingTableSymbols;272 return error.MissingTableSymbols;
273 }273 }
274274
...@@ -596,9 +596,9 @@ fn Parser(comptime ReaderType: type) type {...@@ -596,9 +596,9 @@ fn Parser(comptime ReaderType: type) type {
596 try reader.readNoEof(name);596 try reader.readNoEof(name);
597597
598 const tag = types.known_features.get(name) orelse {598 const tag = types.known_features.get(name) orelse {
599 var err = try parser.wasm_file.addErrorWithNotes(1);599 var err = try parser.wasm_file.base.addErrorWithNotes(1);
600 try err.addMsg(parser.wasm_file, "Object file contains unknown feature: {s}", .{name});600 try err.addMsg("Object file contains unknown feature: {s}", .{name});
601 try err.addNote(parser.wasm_file, "defined in '{s}'", .{parser.object.path});601 try err.addNote("defined in '{s}'", .{parser.object.path});
602 return error.UnknownFeature;602 return error.UnknownFeature;
603 };603 };
604 feature.* = .{604 feature.* = .{