authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-07-23 19:14:38+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-07-23 19:14:38+02:00
log255547d7a6a1acee9c9b65d251ec4935433b6878
treeed616d095ea8de11ab4ebfacd23183af158459ad
parentac2459327fd5adadadf20de7a1883b82a55fc7d6
parentf1af53f68ec629ca091452aeeeb2f7f7596e63b7
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #20731 from ziglang/parallel-macho-2

The tale of parallel MachO: part 2

35 files changed, 1325 insertions(+), 1189 deletions(-)

build.zig+1-1
......@@ -618,7 +618,7 @@ fn addCompilerStep(b: *std.Build, options: AddCompilerStepOptions) *std.Build.St
618618 .root_source_file = b.path("src/main.zig"),
619619 .target = options.target,
620620 .optimize = options.optimize,
621 .max_rss = 7_100_000_000,
621 .max_rss = 7_500_000_000,
622622 .strip = options.strip,
623623 .sanitize_thread = options.sanitize_thread,
624624 .single_threaded = options.single_threaded,
src/Compilation.zig+2-2
......@@ -105,8 +105,9 @@ win32_resource_table: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMa
105105 pub fn deinit(_: @This(), _: Allocator) void {}
106106} = .{},
107107
108link_error_flags: link.File.ErrorFlags = .{},
109108link_errors: std.ArrayListUnmanaged(link.File.ErrorMsg) = .{},
109link_errors_mutex: std.Thread.Mutex = .{},
110link_error_flags: link.File.ErrorFlags = .{},
110111lld_errors: std.ArrayListUnmanaged(LldError) = .{},
111112
112113work_queues: [
......@@ -3067,7 +3068,6 @@ pub fn totalErrorCount(comp: *Compilation) u32 {
30673068 total += @intFromBool(comp.link_error_flags.no_entry_point_found);
30683069 }
30693070 total += @intFromBool(comp.link_error_flags.missing_libc);
3070
30713071 total += comp.link_errors.items.len;
30723072
30733073 // Compile log errors only count if there are no other errors.
src/arch/x86_64/Emit.zig+3-3
......@@ -163,12 +163,12 @@ pub fn emitMir(emit: *Emit) Error!void {
163163 const zo = macho_file.getZigObject().?;
164164 const atom = zo.symbols.items[data.atom_index].getAtom(macho_file).?;
165165 const sym = &zo.symbols.items[data.sym_index];
166 if (sym.flags.needs_zig_got and !is_obj_or_static_lib) {
166 if (sym.getSectionFlags().needs_zig_got and !is_obj_or_static_lib) {
167167 _ = try sym.getOrCreateZigGotEntry(data.sym_index, macho_file);
168168 }
169 const @"type": link.File.MachO.Relocation.Type = if (sym.flags.needs_zig_got and !is_obj_or_static_lib)
169 const @"type": link.File.MachO.Relocation.Type = if (sym.getSectionFlags().needs_zig_got and !is_obj_or_static_lib)
170170 .zig_got_load
171 else if (sym.flags.needs_got)
171 else if (sym.getSectionFlags().needs_got)
172172 // TODO: it is possible to emit .got_load here that can potentially be relaxed
173173 // however this requires always to use a MOVQ mnemonic
174174 .got
src/arch/x86_64/Lower.zig+1-1
......@@ -451,7 +451,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
451451 break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) };
452452 },
453453 .mov => {
454 if (is_obj_or_static_lib and macho_sym.flags.needs_zig_got) emit_mnemonic = .lea;
454 if (is_obj_or_static_lib and macho_sym.getSectionFlags().needs_zig_got) emit_mnemonic = .lea;
455455 break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) };
456456 },
457457 else => unreachable,
src/codegen.zig+1-1
......@@ -924,7 +924,7 @@ fn genDeclRef(
924924 const name = decl.name.toSlice(ip);
925925 const lib_name = if (decl.getOwnedVariable(zcu)) |ov| ov.lib_name.toSlice(ip) else null;
926926 const sym_index = try macho_file.getGlobalSymbol(name, lib_name);
927 zo.symbols.items[sym_index].flags.needs_got = true;
927 zo.symbols.items[sym_index].setSectionFlags(.{ .needs_got = true });
928928 return GenResult.mcv(.{ .load_symbol = sym_index });
929929 }
930930 const sym_index = try zo.getOrCreateMetadataForDecl(macho_file, decl_index);
src/link.zig+67-1
......@@ -439,6 +439,58 @@ pub const File = struct {
439439 }
440440 }
441441
442 pub const ErrorWithNotes = struct {
443 base: *const File,
444
445 /// Allocated index in base.errors array.
446 index: usize,
447
448 /// Next available note slot.
449 note_slot: usize = 0,
450
451 pub fn addMsg(
452 err: ErrorWithNotes,
453 comptime format: []const u8,
454 args: anytype,
455 ) error{OutOfMemory}!void {
456 const gpa = err.base.comp.gpa;
457 const err_msg = &err.base.comp.link_errors.items[err.index];
458 err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
459 }
460
461 pub fn addNote(
462 err: *ErrorWithNotes,
463 comptime format: []const u8,
464 args: anytype,
465 ) error{OutOfMemory}!void {
466 const gpa = err.base.comp.gpa;
467 const err_msg = &err.base.comp.link_errors.items[err.index];
468 assert(err.note_slot < err_msg.notes.len);
469 err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };
470 err.note_slot += 1;
471 }
472 };
473
474 pub fn addErrorWithNotes(base: *const File, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
475 base.comp.link_errors_mutex.lock();
476 defer base.comp.link_errors_mutex.unlock();
477 const gpa = base.comp.gpa;
478 try base.comp.link_errors.ensureUnusedCapacity(gpa, 1);
479 return base.addErrorWithNotesAssumeCapacity(note_count);
480 }
481
482 pub fn addErrorWithNotesAssumeCapacity(base: *const File, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
483 const gpa = base.comp.gpa;
484 const index = base.comp.link_errors.items.len;
485 const err = base.comp.link_errors.addOneAssumeCapacity();
486 err.* = .{ .msg = undefined, .notes = try gpa.alloc(ErrorMsg, note_count) };
487 return .{ .base = base, .index = index };
488 }
489
490 pub fn hasErrors(base: *const File) bool {
491 return base.comp.link_errors.items.len > 0 or base.comp.link_error_flags.isSet();
492 }
493
442494 pub fn releaseLock(self: *File) void {
443495 if (self.lock) |*lock| {
444496 lock.release();
......@@ -874,9 +926,23 @@ pub const File = struct {
874926 }
875927 };
876928
877 pub const ErrorFlags = struct {
929 pub const ErrorFlags = packed struct {
878930 no_entry_point_found: bool = false,
879931 missing_libc: bool = false,
932
933 const Int = blk: {
934 const bits = @typeInfo(@This()).Struct.fields.len;
935 break :blk @Type(.{
936 .Int = .{
937 .signedness = .unsigned,
938 .bits = bits,
939 },
940 });
941 };
942
943 fn isSet(ef: ErrorFlags) bool {
944 return @as(Int, @bitCast(ef)) > 0;
945 }
880946 };
881947
882948 pub const ErrorMsg = struct {
src/link/Elf.zig+33-84
......@@ -995,12 +995,12 @@ pub fn growAllocSection(self: *Elf, shdr_index: u32, needed_size: u64) !void {
995995 if (maybe_phdr) |phdr| {
996996 const mem_capacity = self.allocatedVirtualSize(phdr.p_vaddr);
997997 if (needed_size > mem_capacity) {
998 var err = try self.addErrorWithNotes(2);
999 try err.addMsg(self, "fatal linker error: cannot expand load segment phdr({d}) in virtual memory", .{
998 var err = try self.base.addErrorWithNotes(2);
999 try err.addMsg("fatal linker error: cannot expand load segment phdr({d}) in virtual memory", .{
10001000 self.phdr_to_shdr_table.get(shdr_index).?,
10011001 });
1002 try err.addNote(self, "TODO: emit relocations to memory locations in self-hosted backends", .{});
1003 try err.addNote(self, "as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
1002 try err.addNote("TODO: emit relocations to memory locations in self-hosted backends", .{});
1003 try err.addNote("as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
10041004 }
10051005
10061006 phdr.p_memsz = needed_size;
......@@ -1276,7 +1276,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
12761276 };
12771277 }
12781278
1279 if (comp.link_errors.items.len > 0) return error.FlushFailure;
1279 if (self.base.hasErrors()) return error.FlushFailure;
12801280
12811281 // Dedup shared objects
12821282 {
......@@ -1423,7 +1423,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
14231423 try self.writeElfHeader();
14241424 }
14251425
1426 if (comp.link_errors.items.len > 0) return error.FlushFailure;
1426 if (self.base.hasErrors()) return error.FlushFailure;
14271427}
14281428
14291429/// --verbose-link output
......@@ -2852,9 +2852,9 @@ fn writePhdrTable(self: *Elf) !void {
28522852}
28532853
28542854pub fn writeElfHeader(self: *Elf) !void {
2855 const comp = self.base.comp;
2856 if (comp.link_errors.items.len > 0) return; // We had errors, so skip flushing to render the output unusable
2855 if (self.base.hasErrors()) return; // We had errors, so skip flushing to render the output unusable
28572856
2857 const comp = self.base.comp;
28582858 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
28592859
28602860 var index: usize = 0;
......@@ -4298,9 +4298,9 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {
42984298 // (revisit getMaxNumberOfPhdrs())
42994299 // 2. shift everything in file to free more space for EHDR + PHDR table
43004300 // TODO verify `getMaxNumberOfPhdrs()` is accurate and convert this into no-op
4301 var err = try self.addErrorWithNotes(1);
4302 try err.addMsg(self, "fatal linker error: not enough space reserved for EHDR and PHDR table", .{});
4303 try err.addNote(self, "required 0x{x}, available 0x{x}", .{ needed_size, available_space });
4301 var err = try self.base.addErrorWithNotes(1);
4302 try err.addMsg("fatal linker error: not enough space reserved for EHDR and PHDR table", .{});
4303 try err.addNote("required 0x{x}, available 0x{x}", .{ needed_size, available_space });
43044304 }
43054305
43064306 phdr_table_load.p_filesz = needed_size + ehsize;
......@@ -5863,56 +5863,6 @@ pub fn tlsAddress(self: *Elf) i64 {
58635863 return @intCast(phdr.p_vaddr);
58645864}
58655865
5866const ErrorWithNotes = struct {
5867 /// Allocated index in comp.link_errors array.
5868 index: usize,
5869
5870 /// Next available note slot.
5871 note_slot: usize = 0,
5872
5873 pub fn addMsg(
5874 err: ErrorWithNotes,
5875 elf_file: *Elf,
5876 comptime format: []const u8,
5877 args: anytype,
5878 ) error{OutOfMemory}!void {
5879 const comp = elf_file.base.comp;
5880 const gpa = comp.gpa;
5881 const err_msg = &comp.link_errors.items[err.index];
5882 err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
5883 }
5884
5885 pub fn addNote(
5886 err: *ErrorWithNotes,
5887 elf_file: *Elf,
5888 comptime format: []const u8,
5889 args: anytype,
5890 ) error{OutOfMemory}!void {
5891 const comp = elf_file.base.comp;
5892 const gpa = comp.gpa;
5893 const err_msg = &comp.link_errors.items[err.index];
5894 assert(err.note_slot < err_msg.notes.len);
5895 err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };
5896 err.note_slot += 1;
5897 }
5898};
5899
5900pub fn addErrorWithNotes(self: *Elf, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
5901 const comp = self.base.comp;
5902 const gpa = comp.gpa;
5903 try comp.link_errors.ensureUnusedCapacity(gpa, 1);
5904 return self.addErrorWithNotesAssumeCapacity(note_count);
5905}
5906
5907fn addErrorWithNotesAssumeCapacity(self: *Elf, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
5908 const comp = self.base.comp;
5909 const gpa = comp.gpa;
5910 const index = comp.link_errors.items.len;
5911 const err = comp.link_errors.addOneAssumeCapacity();
5912 err.* = .{ .msg = undefined, .notes = try gpa.alloc(link.File.ErrorMsg, note_count) };
5913 return .{ .index = index };
5914}
5915
59165866pub fn getShString(self: Elf, off: u32) [:0]const u8 {
59175867 assert(off < self.shstrtab.items.len);
59185868 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.shstrtab.items.ptr + off)), 0);
......@@ -5940,11 +5890,10 @@ pub fn insertDynString(self: *Elf, name: []const u8) error{OutOfMemory}!u32 {
59405890}
59415891
59425892fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
5943 const comp = self.base.comp;
5944 const gpa = comp.gpa;
5893 const gpa = self.base.comp.gpa;
59455894 const max_notes = 4;
59465895
5947 try comp.link_errors.ensureUnusedCapacity(gpa, undefs.count());
5896 try self.base.comp.link_errors.ensureUnusedCapacity(gpa, undefs.count());
59485897
59495898 var it = undefs.iterator();
59505899 while (it.next()) |entry| {
......@@ -5953,18 +5902,18 @@ fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
59535902 const natoms = @min(atoms.len, max_notes);
59545903 const nnotes = natoms + @intFromBool(atoms.len > max_notes);
59555904
5956 var err = try self.addErrorWithNotesAssumeCapacity(nnotes);
5957 try err.addMsg(self, "undefined symbol: {s}", .{self.symbol(undef_index).name(self)});
5905 var err = try self.base.addErrorWithNotesAssumeCapacity(nnotes);
5906 try err.addMsg("undefined symbol: {s}", .{self.symbol(undef_index).name(self)});
59585907
59595908 for (atoms[0..natoms]) |atom_index| {
59605909 const atom_ptr = self.atom(atom_index).?;
59615910 const file_ptr = self.file(atom_ptr.file_index).?;
5962 try err.addNote(self, "referenced by {s}:{s}", .{ file_ptr.fmtPath(), atom_ptr.name(self) });
5911 try err.addNote("referenced by {s}:{s}", .{ file_ptr.fmtPath(), atom_ptr.name(self) });
59635912 }
59645913
59655914 if (atoms.len > max_notes) {
59665915 const remaining = atoms.len - max_notes;
5967 try err.addNote(self, "referenced {d} more times", .{remaining});
5916 try err.addNote("referenced {d} more times", .{remaining});
59685917 }
59695918 }
59705919}
......@@ -5978,19 +5927,19 @@ fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemor
59785927 const notes = entry.value_ptr.*;
59795928 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
59805929
5981 var err = try self.addErrorWithNotes(nnotes + 1);
5982 try err.addMsg(self, "duplicate symbol definition: {s}", .{sym.name(self)});
5983 try err.addNote(self, "defined by {}", .{sym.file(self).?.fmtPath()});
5930 var err = try self.base.addErrorWithNotes(nnotes + 1);
5931 try err.addMsg("duplicate symbol definition: {s}", .{sym.name(self)});
5932 try err.addNote("defined by {}", .{sym.file(self).?.fmtPath()});
59845933
59855934 var inote: usize = 0;
59865935 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
59875936 const file_ptr = self.file(notes.items[inote]).?;
5988 try err.addNote(self, "defined by {}", .{file_ptr.fmtPath()});
5937 try err.addNote("defined by {}", .{file_ptr.fmtPath()});
59895938 }
59905939
59915940 if (notes.items.len > max_notes) {
59925941 const remaining = notes.items.len - max_notes;
5993 try err.addNote(self, "defined {d} more times", .{remaining});
5942 try err.addNote("defined {d} more times", .{remaining});
59945943 }
59955944
59965945 has_dupes = true;
......@@ -6005,16 +5954,16 @@ fn reportMissingLibraryError(
60055954 comptime format: []const u8,
60065955 args: anytype,
60075956) error{OutOfMemory}!void {
6008 var err = try self.addErrorWithNotes(checked_paths.len);
6009 try err.addMsg(self, format, args);
5957 var err = try self.base.addErrorWithNotes(checked_paths.len);
5958 try err.addMsg(format, args);
60105959 for (checked_paths) |path| {
6011 try err.addNote(self, "tried {s}", .{path});
5960 try err.addNote("tried {s}", .{path});
60125961 }
60135962}
60145963
60155964pub fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void {
6016 var err = try self.addErrorWithNotes(0);
6017 try err.addMsg(self, "fatal linker error: unsupported CPU architecture {s}", .{
5965 var err = try self.base.addErrorWithNotes(0);
5966 try err.addMsg("fatal linker error: unsupported CPU architecture {s}", .{
60185967 @tagName(self.getTarget().cpu.arch),
60195968 });
60205969}
......@@ -6025,9 +5974,9 @@ pub fn reportParseError(
60255974 comptime format: []const u8,
60265975 args: anytype,
60275976) error{OutOfMemory}!void {
6028 var err = try self.addErrorWithNotes(1);
6029 try err.addMsg(self, format, args);
6030 try err.addNote(self, "while parsing {s}", .{path});
5977 var err = try self.base.addErrorWithNotes(1);
5978 try err.addMsg(format, args);
5979 try err.addNote("while parsing {s}", .{path});
60315980}
60325981
60335982pub fn reportParseError2(
......@@ -6036,9 +5985,9 @@ pub fn reportParseError2(
60365985 comptime format: []const u8,
60375986 args: anytype,
60385987) error{OutOfMemory}!void {
6039 var err = try self.addErrorWithNotes(1);
6040 try err.addMsg(self, format, args);
6041 try err.addNote(self, "while parsing {}", .{self.file(file_index).?.fmtPath()});
5988 var err = try self.base.addErrorWithNotes(1);
5989 try err.addMsg(format, args);
5990 try err.addNote("while parsing {}", .{self.file(file_index).?.fmtPath()});
60425991}
60435992
60445993const FormatShdrCtx = struct {
src/link/Elf/Atom.zig+32-44
......@@ -631,15 +631,12 @@ fn dataType(symbol: *const Symbol, elf_file: *Elf) u2 {
631631}
632632
633633fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) RelocError!void {
634 var err = try elf_file.addErrorWithNotes(1);
635 try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {} at offset 0x{x}", .{
634 var err = try elf_file.base.addErrorWithNotes(1);
635 try err.addMsg("fatal linker error: unhandled relocation type {} at offset 0x{x}", .{
636636 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),
637637 rel.r_offset,
638638 });
639 try err.addNote(elf_file, "in {}:{s}", .{
640 self.file(elf_file).?.fmtPath(),
641 self.name(elf_file),
642 });
639 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
643640 return error.RelocFailure;
644641}
645642
......@@ -649,15 +646,12 @@ fn reportTextRelocError(
649646 rel: elf.Elf64_Rela,
650647 elf_file: *Elf,
651648) RelocError!void {
652 var err = try elf_file.addErrorWithNotes(1);
653 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
649 var err = try elf_file.base.addErrorWithNotes(1);
650 try err.addMsg("relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
654651 rel.r_offset,
655652 symbol.name(elf_file),
656653 });
657 try err.addNote(elf_file, "in {}:{s}", .{
658 self.file(elf_file).?.fmtPath(),
659 self.name(elf_file),
660 });
654 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
661655 return error.RelocFailure;
662656}
663657
......@@ -667,16 +661,13 @@ fn reportPicError(
667661 rel: elf.Elf64_Rela,
668662 elf_file: *Elf,
669663) RelocError!void {
670 var err = try elf_file.addErrorWithNotes(2);
671 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
664 var err = try elf_file.base.addErrorWithNotes(2);
665 try err.addMsg("relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
672666 rel.r_offset,
673667 symbol.name(elf_file),
674668 });
675 try err.addNote(elf_file, "in {}:{s}", .{
676 self.file(elf_file).?.fmtPath(),
677 self.name(elf_file),
678 });
679 try err.addNote(elf_file, "recompile with -fPIC", .{});
669 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
670 try err.addNote("recompile with -fPIC", .{});
680671 return error.RelocFailure;
681672}
682673
......@@ -686,16 +677,13 @@ fn reportNoPicError(
686677 rel: elf.Elf64_Rela,
687678 elf_file: *Elf,
688679) RelocError!void {
689 var err = try elf_file.addErrorWithNotes(2);
690 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
680 var err = try elf_file.base.addErrorWithNotes(2);
681 try err.addMsg("relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
691682 rel.r_offset,
692683 symbol.name(elf_file),
693684 });
694 try err.addNote(elf_file, "in {}:{s}", .{
695 self.file(elf_file).?.fmtPath(),
696 self.name(elf_file),
697 });
698 try err.addNote(elf_file, "recompile with -fno-PIC", .{});
685 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
686 try err.addNote("recompile with -fno-PIC", .{});
699687 return error.RelocFailure;
700688}
701689
......@@ -1332,9 +1320,9 @@ const x86_64 = struct {
13321320 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
13331321 } else {
13341322 x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]) catch {
1335 var err = try elf_file.addErrorWithNotes(1);
1336 try err.addMsg(elf_file, "could not relax {s}", .{@tagName(r_type)});
1337 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
1323 var err = try elf_file.base.addErrorWithNotes(1);
1324 try err.addMsg("could not relax {s}", .{@tagName(r_type)});
1325 try err.addNote("in {}:{s} at offset 0x{x}", .{
13381326 atom.file(elf_file).?.fmtPath(),
13391327 atom.name(elf_file),
13401328 rel.r_offset,
......@@ -1479,12 +1467,12 @@ const x86_64 = struct {
14791467 },
14801468
14811469 else => {
1482 var err = try elf_file.addErrorWithNotes(1);
1483 try err.addMsg(elf_file, "TODO: rewrite {} when followed by {}", .{
1470 var err = try elf_file.base.addErrorWithNotes(1);
1471 try err.addMsg("TODO: rewrite {} when followed by {}", .{
14841472 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
14851473 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
14861474 });
1487 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
1475 try err.addNote("in {}:{s} at offset 0x{x}", .{
14881476 self.file(elf_file).?.fmtPath(),
14891477 self.name(elf_file),
14901478 rels[0].r_offset,
......@@ -1534,12 +1522,12 @@ const x86_64 = struct {
15341522 },
15351523
15361524 else => {
1537 var err = try elf_file.addErrorWithNotes(1);
1538 try err.addMsg(elf_file, "TODO: rewrite {} when followed by {}", .{
1525 var err = try elf_file.base.addErrorWithNotes(1);
1526 try err.addMsg("TODO: rewrite {} when followed by {}", .{
15391527 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
15401528 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
15411529 });
1542 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
1530 try err.addNote("in {}:{s} at offset 0x{x}", .{
15431531 self.file(elf_file).?.fmtPath(),
15441532 self.name(elf_file),
15451533 rels[0].r_offset,
......@@ -1630,12 +1618,12 @@ const x86_64 = struct {
16301618 },
16311619
16321620 else => {
1633 var err = try elf_file.addErrorWithNotes(1);
1634 try err.addMsg(elf_file, "fatal linker error: rewrite {} when followed by {}", .{
1621 var err = try elf_file.base.addErrorWithNotes(1);
1622 try err.addMsg("fatal linker error: rewrite {} when followed by {}", .{
16351623 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
16361624 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
16371625 });
1638 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
1626 try err.addNote("in {}:{s} at offset 0x{x}", .{
16391627 self.file(elf_file).?.fmtPath(),
16401628 self.name(elf_file),
16411629 rels[0].r_offset,
......@@ -1824,9 +1812,9 @@ const aarch64 = struct {
18241812 aarch64_util.writeAdrpInst(pages, code);
18251813 } else {
18261814 // TODO: relax
1827 var err = try elf_file.addErrorWithNotes(1);
1828 try err.addMsg(elf_file, "TODO: relax ADR_GOT_PAGE", .{});
1829 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
1815 var err = try elf_file.base.addErrorWithNotes(1);
1816 try err.addMsg("TODO: relax ADR_GOT_PAGE", .{});
1817 try err.addNote("in {}:{s} at offset 0x{x}", .{
18301818 atom.file(elf_file).?.fmtPath(),
18311819 atom.name(elf_file),
18321820 r_offset,
......@@ -2118,9 +2106,9 @@ const riscv = struct {
21182106 if (S == atom_addr + @as(i64, @intCast(pair.r_offset))) break pair;
21192107 } else {
21202108 // TODO: implement searching forward
2121 var err = try elf_file.addErrorWithNotes(1);
2122 try err.addMsg(elf_file, "TODO: find HI20 paired reloc scanning forward", .{});
2123 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
2109 var err = try elf_file.base.addErrorWithNotes(1);
2110 try err.addMsg("TODO: find HI20 paired reloc scanning forward", .{});
2111 try err.addNote("in {}:{s} at offset 0x{x}", .{
21242112 atom.file(elf_file).?.fmtPath(),
21252113 atom.name(elf_file),
21262114 rel.r_offset,
src/link/Elf/Object.zig+13-13
......@@ -704,9 +704,9 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void {
704704 var end = start;
705705 while (end < data.len - sh_entsize and !isNull(data[end .. end + sh_entsize])) : (end += sh_entsize) {}
706706 if (!isNull(data[end .. end + sh_entsize])) {
707 var err = try elf_file.addErrorWithNotes(1);
708 try err.addMsg(elf_file, "string not null terminated", .{});
709 try err.addNote(elf_file, "in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
707 var err = try elf_file.base.addErrorWithNotes(1);
708 try err.addMsg("string not null terminated", .{});
709 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
710710 return error.MalformedObject;
711711 }
712712 end += sh_entsize;
......@@ -719,9 +719,9 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void {
719719 const sh_entsize: u32 = @intCast(shdr.sh_entsize);
720720 if (sh_entsize == 0) continue; // Malformed, don't split but don't error out
721721 if (shdr.sh_size % sh_entsize != 0) {
722 var err = try elf_file.addErrorWithNotes(1);
723 try err.addMsg(elf_file, "size not a multiple of sh_entsize", .{});
724 try err.addNote(elf_file, "in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
722 var err = try elf_file.base.addErrorWithNotes(1);
723 try err.addMsg("size not a multiple of sh_entsize", .{});
724 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
725725 return error.MalformedObject;
726726 }
727727
......@@ -779,10 +779,10 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
779779 const imsec = elf_file.inputMergeSection(imsec_index) orelse continue;
780780 if (imsec.offsets.items.len == 0) continue;
781781 const msub_index, const offset = imsec.findSubsection(@intCast(esym.st_value)) orelse {
782 var err = try elf_file.addErrorWithNotes(2);
783 try err.addMsg(elf_file, "invalid symbol value: {x}", .{esym.st_value});
784 try err.addNote(elf_file, "for symbol {s}", .{sym.name(elf_file)});
785 try err.addNote(elf_file, "in {}", .{self.fmtPath()});
782 var err = try elf_file.base.addErrorWithNotes(2);
783 try err.addMsg("invalid symbol value: {x}", .{esym.st_value});
784 try err.addNote("for symbol {s}", .{sym.name(elf_file)});
785 try err.addNote("in {}", .{self.fmtPath()});
786786 return error.MalformedObject;
787787 };
788788
......@@ -804,9 +804,9 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
804804 const imsec = elf_file.inputMergeSection(imsec_index) orelse continue;
805805 if (imsec.offsets.items.len == 0) continue;
806806 const msub_index, const offset = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse {
807 var err = try elf_file.addErrorWithNotes(1);
808 try err.addMsg(elf_file, "invalid relocation at offset 0x{x}", .{rel.r_offset});
809 try err.addNote(elf_file, "in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
807 var err = try elf_file.base.addErrorWithNotes(1);
808 try err.addMsg("invalid relocation at offset 0x{x}", .{rel.r_offset});
809 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
810810 return error.MalformedObject;
811811 };
812812 const msub = elf_file.mergeSubsection(msub_index);
src/link/Elf/eh_frame.zig+3-3
......@@ -591,12 +591,12 @@ const riscv = struct {
591591};
592592
593593fn reportInvalidReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela) !void {
594 var err = try elf_file.addErrorWithNotes(1);
595 try err.addMsg(elf_file, "invalid relocation type {} at offset 0x{x}", .{
594 var err = try elf_file.base.addErrorWithNotes(1);
595 try err.addMsg("invalid relocation type {} at offset 0x{x}", .{
596596 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),
597597 rel.r_offset,
598598 });
599 try err.addNote(elf_file, "in {}:.eh_frame", .{elf_file.file(rec.file_index).?.fmtPath()});
599 try err.addNote("in {}:.eh_frame", .{elf_file.file(rec.file_index).?.fmtPath()});
600600 return error.RelocFailure;
601601}
602602
src/link/Elf/relocatable.zig+4-4
......@@ -29,7 +29,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co
2929 };
3030 }
3131
32 if (comp.link_errors.items.len > 0) return error.FlushFailure;
32 if (elf_file.base.hasErrors()) return error.FlushFailure;
3333
3434 // First, we flush relocatable object file generated with our backends.
3535 if (elf_file.zigObjectPtr()) |zig_object| {
......@@ -146,7 +146,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co
146146 try elf_file.base.file.?.setEndPos(total_size);
147147 try elf_file.base.file.?.pwriteAll(buffer.items, 0);
148148
149 if (comp.link_errors.items.len > 0) return error.FlushFailure;
149 if (elf_file.base.hasErrors()) return error.FlushFailure;
150150}
151151
152152pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
......@@ -177,7 +177,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const
177177 };
178178 }
179179
180 if (comp.link_errors.items.len > 0) return error.FlushFailure;
180 if (elf_file.base.hasErrors()) return error.FlushFailure;
181181
182182 // Now, we are ready to resolve the symbols across all input files.
183183 // We will first resolve the files in the ZigObject, next in the parsed
......@@ -216,7 +216,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const
216216 try elf_file.writeShdrTable();
217217 try elf_file.writeElfHeader();
218218
219 if (comp.link_errors.items.len > 0) return error.FlushFailure;
219 if (elf_file.base.hasErrors()) return error.FlushFailure;
220220}
221221
222222fn parsePositional(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
src/link/MachO.zig+614-516
......@@ -25,8 +25,10 @@ sections: std.MultiArrayList(Section) = .{},
2525resolver: SymbolResolver = .{},
2626/// This table will be populated after `scanRelocs` has run.
2727/// Key is symbol index.
28undefs: std.AutoHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(Ref)) = .{},
29dupes: std.AutoHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(File.Index)) = .{},
28undefs: std.AutoArrayHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(Ref)) = .{},
29undefs_mutex: std.Thread.Mutex = .{},
30dupes: std.AutoArrayHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(File.Index)) = .{},
31dupes_mutex: std.Thread.Mutex = .{},
3032
3133dyld_info_cmd: macho.dyld_info_command = .{},
3234symtab_cmd: macho.symtab_command = .{},
......@@ -93,9 +95,10 @@ debug_str_sect_index: ?u8 = null,
9395debug_aranges_sect_index: ?u8 = null,
9496debug_line_sect_index: ?u8 = null,
9597
96has_tlv: bool = false,
97binds_to_weak: bool = false,
98weak_defines: bool = false,
98has_tlv: AtomicBool = AtomicBool.init(false),
99binds_to_weak: AtomicBool = AtomicBool.init(false),
100weak_defines: AtomicBool = AtomicBool.init(false),
101has_errors: AtomicBool = AtomicBool.init(false),
99102
100103/// Options
101104/// SDK layout
......@@ -305,20 +308,15 @@ pub fn deinit(self: *MachO) void {
305308 self.sections.deinit(gpa);
306309
307310 self.resolver.deinit(gpa);
308 {
309 var it = self.undefs.iterator();
310 while (it.next()) |entry| {
311 entry.value_ptr.deinit(gpa);
312 }
313 self.undefs.deinit(gpa);
311
312 for (self.undefs.values()) |*val| {
313 val.deinit(gpa);
314314 }
315 {
316 var it = self.dupes.iterator();
317 while (it.next()) |entry| {
318 entry.value_ptr.deinit(gpa);
319 }
320 self.dupes.deinit(gpa);
315 self.undefs.deinit(gpa);
316 for (self.dupes.values()) |*val| {
317 val.deinit(gpa);
321318 }
319 self.dupes.deinit(gpa);
322320
323321 self.symtab.deinit(gpa);
324322 self.strtab.deinit(gpa);
......@@ -395,17 +393,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
395393 }
396394
397395 for (positionals.items) |obj| {
398 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
399 error.MalformedObject,
400 error.MalformedArchive,
401 error.MalformedDylib,
402 error.InvalidCpuArch,
403 error.InvalidTarget,
404 => continue, // already reported
405 error.UnknownFileType => try self.reportParseError(obj.path, "unknown file type for an object file", .{}),
396 self.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
397 error.UnknownFileType => try self.reportParseError(obj.path, "unknown file type for an input file", .{}),
406398 else => |e| try self.reportParseError(
407399 obj.path,
408 "unexpected error: parsing input file failed with error {s}",
400 "unexpected error: reading input file failed with error {s}",
409401 .{@errorName(e)},
410402 ),
411403 };
......@@ -448,15 +440,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
448440 };
449441
450442 for (system_libs.items) |lib| {
451 self.parseLibrary(lib, false) catch |err| switch (err) {
452 error.MalformedArchive,
453 error.MalformedDylib,
454 error.InvalidCpuArch,
455 => continue, // already reported
456 error.UnknownFileType => try self.reportParseError(lib.path, "unknown file type for a library", .{}),
443 self.classifyInputFile(lib.path, lib, false) catch |err| switch (err) {
444 error.UnknownFileType => try self.reportParseError(lib.path, "unknown file type for an input file", .{}),
457445 else => |e| try self.reportParseError(
458446 lib.path,
459 "unexpected error: parsing library failed with error {s}",
447 "unexpected error: parsing input file failed with error {s}",
460448 .{@errorName(e)},
461449 ),
462450 };
......@@ -469,13 +457,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
469457 break :blk null;
470458 };
471459 if (compiler_rt_path) |path| {
472 self.parsePositional(path, false) catch |err| switch (err) {
473 error.MalformedObject,
474 error.MalformedArchive,
475 error.InvalidCpuArch,
476 error.InvalidTarget,
477 => {}, // already reported
478 error.UnknownFileType => try self.reportParseError(path, "unknown file type for a library", .{}),
460 self.classifyInputFile(path, .{ .path = path }, false) catch |err| switch (err) {
461 error.UnknownFileType => try self.reportParseError(path, "unknown file type for an input file", .{}),
479462 else => |e| try self.reportParseError(
480463 path,
481464 "unexpected error: parsing input file failed with error {s}",
......@@ -484,30 +467,18 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
484467 };
485468 }
486469
487 if (comp.link_errors.items.len > 0) return error.FlushFailure;
488
489 for (self.dylibs.items) |index| {
490 self.getFile(index).?.dylib.umbrella = index;
491 }
492
493 if (self.dylibs.items.len > 0) {
494 self.parseDependentDylibs() catch |err| {
495 switch (err) {
496 error.MissingLibraryDependencies => {},
497 else => |e| try self.reportUnexpectedError(
498 "unexpected error while parsing dependent libraries: {s}",
499 .{@errorName(e)},
500 ),
501 }
502 return error.FlushFailure;
503 };
504 }
470 try self.parseInputFiles();
471 self.parseDependentDylibs() catch |err| {
472 switch (err) {
473 error.MissingLibraryDependencies => {},
474 else => |e| try self.reportUnexpectedError(
475 "unexpected error while parsing dependent libraries: {s}",
476 .{@errorName(e)},
477 ),
478 }
479 };
505480
506 for (self.dylibs.items) |index| {
507 const dylib = self.getFile(index).?.dylib;
508 if (!dylib.explicit and !dylib.hoisted) continue;
509 try dylib.initSymbols(self);
510 }
481 if (self.base.hasErrors()) return error.FlushFailure;
511482
512483 {
513484 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
......@@ -579,12 +550,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
579550 else => |e| return e,
580551 };
581552 }
582 self.writeSectionsAndUpdateLinkeditSizes() catch |err| {
583 switch (err) {
584 error.ResolveFailed => return error.FlushFailure,
585 else => |e| return e,
586 }
587 };
553 try self.writeSectionsAndUpdateLinkeditSizes();
588554
589555 try self.writeSectionsToFile();
590556 try self.allocateLinkeditSegment();
......@@ -841,181 +807,186 @@ pub fn resolveLibSystem(
841807 });
842808}
843809
844pub const ParseError = error{
845 MalformedObject,
846 MalformedArchive,
847 MalformedDylib,
848 MalformedTbd,
849 NotLibStub,
850 InvalidCpuArch,
851 InvalidTarget,
852 InvalidTargetFatLibrary,
853 IncompatibleDylibVersion,
854 OutOfMemory,
855 Overflow,
856 InputOutput,
857 EndOfStream,
858 FileSystem,
859 NotSupported,
860 Unhandled,
861 UnknownFileType,
862} || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError || tapi.TapiError;
863
864pub fn parsePositional(self: *MachO, path: []const u8, must_link: bool) ParseError!void {
810pub fn classifyInputFile(self: *MachO, path: []const u8, lib: SystemLib, must_link: bool) !void {
865811 const tracy = trace(@src());
866812 defer tracy.end();
867 if (try Object.isObject(path)) {
868 try self.parseObject(path);
869 } else {
870 try self.parseLibrary(.{ .path = path }, must_link);
813
814 log.debug("classifying input file {s}", .{path});
815
816 const file = try std.fs.cwd().openFile(path, .{});
817 const fh = try self.addFileHandle(file);
818 var buffer: [Archive.SARMAG]u8 = undefined;
819
820 const fat_arch: ?fat.Arch = try self.parseFatFile(file, path);
821 const offset = if (fat_arch) |fa| fa.offset else 0;
822
823 if (readMachHeader(file, offset) catch null) |h| blk: {
824 if (h.magic != macho.MH_MAGIC_64) break :blk;
825 switch (h.filetype) {
826 macho.MH_OBJECT => try self.addObject(path, fh, offset),
827 macho.MH_DYLIB => _ = try self.addDylib(lib, true, fh, offset),
828 else => return error.UnknownFileType,
829 }
830 return;
871831 }
832 if (readArMagic(file, offset, &buffer) catch null) |ar_magic| blk: {
833 if (!mem.eql(u8, ar_magic, Archive.ARMAG)) break :blk;
834 try self.addArchive(lib, must_link, fh, fat_arch);
835 return;
836 }
837 _ = try self.addTbd(lib, true, fh);
872838}
873839
874fn parseLibrary(self: *MachO, lib: SystemLib, must_link: bool) ParseError!void {
875 const tracy = trace(@src());
876 defer tracy.end();
877 if (try fat.isFatLibrary(lib.path)) {
878 const fat_arch = try self.parseFatLibrary(lib.path);
879 if (try Archive.isArchive(lib.path, fat_arch)) {
880 try self.parseArchive(lib, must_link, fat_arch);
881 } else if (try Dylib.isDylib(lib.path, fat_arch)) {
882 _ = try self.parseDylib(lib, true, fat_arch);
883 } else return error.UnknownFileType;
884 } else if (try Archive.isArchive(lib.path, null)) {
885 try self.parseArchive(lib, must_link, null);
886 } else if (try Dylib.isDylib(lib.path, null)) {
887 _ = try self.parseDylib(lib, true, null);
888 } else {
889 _ = self.parseTbd(lib, true) catch |err| switch (err) {
890 error.MalformedTbd => return error.UnknownFileType,
891 else => |e| return e,
892 };
840fn parseFatFile(self: *MachO, file: std.fs.File, path: []const u8) !?fat.Arch {
841 const fat_h = fat.readFatHeader(file) catch return null;
842 if (fat_h.magic != macho.FAT_MAGIC and fat_h.magic != macho.FAT_MAGIC_64) return null;
843 var fat_archs_buffer: [2]fat.Arch = undefined;
844 const fat_archs = try fat.parseArchs(file, fat_h, &fat_archs_buffer);
845 const cpu_arch = self.getTarget().cpu.arch;
846 for (fat_archs) |arch| {
847 if (arch.tag == cpu_arch) return arch;
893848 }
849 try self.reportParseError(path, "missing arch in universal file: expected {s}", .{
850 @tagName(cpu_arch),
851 });
852 return error.MissingCpuArch;
853}
854
855pub fn readMachHeader(file: std.fs.File, offset: usize) !macho.mach_header_64 {
856 var buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined;
857 const nread = try file.preadAll(&buffer, offset);
858 if (nread != buffer.len) return error.InputOutput;
859 const hdr = @as(*align(1) const macho.mach_header_64, @ptrCast(&buffer)).*;
860 return hdr;
894861}
895862
896fn parseObject(self: *MachO, path: []const u8) ParseError!void {
863pub fn readArMagic(file: std.fs.File, offset: usize, buffer: *[Archive.SARMAG]u8) ![]const u8 {
864 const nread = try file.preadAll(buffer, offset);
865 if (nread != buffer.len) return error.InputOutput;
866 return buffer[0..Archive.SARMAG];
867}
868
869fn addObject(self: *MachO, path: []const u8, handle: File.HandleIndex, offset: u64) !void {
897870 const tracy = trace(@src());
898871 defer tracy.end();
899872
900873 const gpa = self.base.comp.gpa;
901 const file = try fs.cwd().openFile(path, .{});
902 const handle = try self.addFileHandle(file);
903874 const mtime: u64 = mtime: {
875 const file = self.getFileHandle(handle);
904876 const stat = file.stat() catch break :mtime 0;
905877 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
906878 };
907879 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
908 self.files.set(index, .{
909 .object = .{
910 .offset = 0, // TODO FAT objects
911 .path = try gpa.dupe(u8, path),
912 .file_handle = handle,
913 .mtime = mtime,
914 .index = index,
915 },
916 });
880 self.files.set(index, .{ .object = .{
881 .offset = offset,
882 .path = try gpa.dupe(u8, path),
883 .file_handle = handle,
884 .mtime = mtime,
885 .index = index,
886 } });
917887 try self.objects.append(gpa, index);
918
919 const object = self.getFile(index).?.object;
920 try object.parse(self);
921888}
922889
923pub fn parseFatLibrary(self: *MachO, path: []const u8) !fat.Arch {
924 var buffer: [2]fat.Arch = undefined;
925 const fat_archs = try fat.parseArchs(path, &buffer);
926 const cpu_arch = self.getTarget().cpu.arch;
927 for (fat_archs) |arch| {
928 if (arch.tag == cpu_arch) return arch;
890pub fn parseInputFiles(self: *MachO) !void {
891 const tracy = trace(@src());
892 defer tracy.end();
893
894 const tp = self.base.comp.thread_pool;
895 var wg: WaitGroup = .{};
896
897 {
898 wg.reset();
899 defer wg.wait();
900
901 for (self.objects.items) |index| {
902 tp.spawnWg(&wg, parseInputFileWorker, .{ self, self.getFile(index).? });
903 }
904 for (self.dylibs.items) |index| {
905 tp.spawnWg(&wg, parseInputFileWorker, .{ self, self.getFile(index).? });
906 }
929907 }
930 try self.reportParseError(path, "missing arch in universal file: expected {s}", .{@tagName(cpu_arch)});
931 return error.InvalidCpuArch;
908
909 if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
910}
911
912fn parseInputFileWorker(self: *MachO, file: File) void {
913 file.parse(self) catch |err| {
914 switch (err) {
915 error.MalformedObject,
916 error.MalformedDylib,
917 error.MalformedTbd,
918 error.InvalidCpuArch,
919 error.InvalidTarget,
920 => {}, // already reported
921 else => |e| self.reportParseError2(file.getIndex(), "unexpected error: parsing input file failed with error {s}", .{@errorName(e)}) catch {},
922 }
923 _ = self.has_errors.swap(true, .seq_cst);
924 };
932925}
933926
934fn parseArchive(self: *MachO, lib: SystemLib, must_link: bool, fat_arch: ?fat.Arch) ParseError!void {
927fn addArchive(self: *MachO, lib: SystemLib, must_link: bool, handle: File.HandleIndex, fat_arch: ?fat.Arch) !void {
935928 const tracy = trace(@src());
936929 defer tracy.end();
937930
938931 const gpa = self.base.comp.gpa;
939932
940 const file = try fs.cwd().openFile(lib.path, .{});
941 const handle = try self.addFileHandle(file);
942
943933 var archive = Archive{};
944934 defer archive.deinit(gpa);
945 try archive.parse(self, lib.path, handle, fat_arch);
935 try archive.unpack(self, lib.path, handle, fat_arch);
946936
947 var has_parse_error = false;
948 for (archive.objects.items) |extracted| {
949 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
950 self.files.set(index, .{ .object = extracted });
937 for (archive.objects.items) |unpacked| {
938 const index: File.Index = @intCast(try self.files.addOne(gpa));
939 self.files.set(index, .{ .object = unpacked });
951940 const object = &self.files.items(.data)[index].object;
952941 object.index = index;
953942 object.alive = must_link or lib.needed; // TODO: or self.options.all_load;
954943 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 };
962944 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());
967945 }
968 if (has_parse_error) return error.MalformedArchive;
969946}
970947
971fn parseDylib(self: *MachO, lib: SystemLib, explicit: bool, fat_arch: ?fat.Arch) ParseError!File.Index {
948fn addDylib(self: *MachO, lib: SystemLib, explicit: bool, handle: File.HandleIndex, offset: u64) !File.Index {
972949 const tracy = trace(@src());
973950 defer tracy.end();
974951
975952 const gpa = self.base.comp.gpa;
976953
977 const file = try fs.cwd().openFile(lib.path, .{});
978 defer file.close();
979
980 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
954 const index: File.Index = @intCast(try self.files.addOne(gpa));
981955 self.files.set(index, .{ .dylib = .{
956 .offset = offset,
957 .file_handle = handle,
958 .tag = .dylib,
982959 .path = try gpa.dupe(u8, lib.path),
983960 .index = index,
984961 .needed = lib.needed,
985962 .weak = lib.weak,
986963 .reexport = lib.reexport,
987964 .explicit = explicit,
965 .umbrella = index,
988966 } });
989 const dylib = &self.files.items(.data)[index].dylib;
990 try dylib.parse(self, file, fat_arch);
991
992967 try self.dylibs.append(gpa, index);
993968
994969 return index;
995970}
996971
997fn parseTbd(self: *MachO, lib: SystemLib, explicit: bool) ParseError!File.Index {
972fn addTbd(self: *MachO, lib: SystemLib, explicit: bool, handle: File.HandleIndex) !File.Index {
998973 const tracy = trace(@src());
999974 defer tracy.end();
1000975
1001976 const gpa = self.base.comp.gpa;
1002 const file = try fs.cwd().openFile(lib.path, .{});
1003 defer file.close();
1004
1005 var lib_stub = LibStub.loadFromFile(gpa, file) catch return error.MalformedTbd; // TODO actually handle different errors
1006 defer lib_stub.deinit();
1007
1008 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
977 const index: File.Index = @intCast(try self.files.addOne(gpa));
1009978 self.files.set(index, .{ .dylib = .{
979 .offset = 0,
980 .file_handle = handle,
981 .tag = .tbd,
1010982 .path = try gpa.dupe(u8, lib.path),
1011983 .index = index,
1012984 .needed = lib.needed,
1013985 .weak = lib.weak,
1014986 .reexport = lib.reexport,
1015987 .explicit = explicit,
988 .umbrella = index,
1016989 } });
1017 const dylib = &self.files.items(.data)[index].dylib;
1018 try dylib.parseTbd(self.getTarget().cpu.arch, self.platform, lib_stub, self);
1019990 try self.dylibs.append(gpa, index);
1020991
1021992 return index;
......@@ -1092,6 +1063,8 @@ fn parseDependentDylibs(self: *MachO) !void {
10921063 const tracy = trace(@src());
10931064 defer tracy.end();
10941065
1066 if (self.dylibs.items.len == 0) return;
1067
10951068 const gpa = self.base.comp.gpa;
10961069 const lib_dirs = self.lib_dirs;
10971070 const framework_dirs = self.framework_dirs;
......@@ -1108,7 +1081,7 @@ fn parseDependentDylibs(self: *MachO) !void {
11081081 while (index < self.dylibs.items.len) : (index += 1) {
11091082 const dylib_index = self.dylibs.items[index];
11101083
1111 var dependents = std.ArrayList(struct { id: Dylib.Id, file: File.Index }).init(gpa);
1084 var dependents = std.ArrayList(File.Index).init(gpa);
11121085 defer dependents.deinit();
11131086 try dependents.ensureTotalCapacityPrecise(self.getFile(dylib_index).?.dylib.dependents.items.len);
11141087
......@@ -1199,38 +1172,34 @@ fn parseDependentDylibs(self: *MachO) !void {
11991172 .path = full_path,
12001173 .weak = is_weak,
12011174 };
1175 const file = try std.fs.cwd().openFile(lib.path, .{});
1176 const fh = try self.addFileHandle(file);
1177 const fat_arch = try self.parseFatFile(file, lib.path);
1178 const offset = if (fat_arch) |fa| fa.offset else 0;
12021179 const file_index = file_index: {
1203 if (try fat.isFatLibrary(lib.path)) {
1204 const fat_arch = try self.parseFatLibrary(lib.path);
1205 if (try Dylib.isDylib(lib.path, fat_arch)) {
1206 break :file_index try self.parseDylib(lib, false, fat_arch);
1207 } else break :file_index @as(File.Index, 0);
1208 } else if (try Dylib.isDylib(lib.path, null)) {
1209 break :file_index try self.parseDylib(lib, false, null);
1210 } else {
1211 const file_index = self.parseTbd(lib, false) catch |err| switch (err) {
1212 error.MalformedTbd => @as(File.Index, 0),
1213 else => |e| return e,
1214 };
1215 break :file_index file_index;
1180 if (readMachHeader(file, offset) catch null) |h| blk: {
1181 if (h.magic != macho.MH_MAGIC_64) break :blk;
1182 switch (h.filetype) {
1183 macho.MH_DYLIB => break :file_index try self.addDylib(lib, false, fh, offset),
1184 else => break :file_index @as(File.Index, 0),
1185 }
12161186 }
1187 break :file_index try self.addTbd(lib, false, fh);
12171188 };
1218 dependents.appendAssumeCapacity(.{ .id = id, .file = file_index });
1189 dependents.appendAssumeCapacity(file_index);
12191190 }
12201191
12211192 const dylib = self.getFile(dylib_index).?.dylib;
1222 for (dependents.items) |entry| {
1223 const id = entry.id;
1224 const file_index = entry.file;
1193 for (dylib.dependents.items, dependents.items) |id, file_index| {
12251194 if (self.getFile(file_index)) |file| {
12261195 const dep_dylib = file.dylib;
1196 try dep_dylib.parse(self); // TODO in parallel
12271197 dep_dylib.hoisted = self.isHoisted(id.name);
1228 if (self.getFile(dep_dylib.umbrella) == null) {
1229 dep_dylib.umbrella = dylib.umbrella;
1230 }
1198 dep_dylib.umbrella = dylib.umbrella;
12311199 if (!dep_dylib.hoisted) {
12321200 const umbrella = dep_dylib.getUmbrella(self);
12331201 for (dep_dylib.exports.items(.name), dep_dylib.exports.items(.flags)) |off, flags| {
1202 // TODO rethink this entire algorithm
12341203 try umbrella.addExport(gpa, dep_dylib.getString(off), flags);
12351204 }
12361205 try umbrella.rpaths.ensureUnusedCapacity(gpa, dep_dylib.rpaths.keys().len);
......@@ -1238,15 +1207,13 @@ fn parseDependentDylibs(self: *MachO) !void {
12381207 umbrella.rpaths.putAssumeCapacity(try gpa.dupe(u8, rpath), {});
12391208 }
12401209 }
1241 } else {
1242 try self.reportDependencyError(
1243 dylib.getUmbrella(self).index,
1244 id.name,
1245 "unable to resolve dependency",
1246 .{},
1247 );
1248 has_errors = true;
1249 }
1210 } else try self.reportDependencyError(
1211 dylib.getUmbrella(self).index,
1212 id.name,
1213 "unable to resolve dependency",
1214 .{},
1215 );
1216 has_errors = true;
12501217 }
12511218 }
12521219
......@@ -1311,95 +1278,51 @@ fn markLive(self: *MachO) void {
13111278 if (self.getInternalObject()) |obj| obj.markLive(self);
13121279}
13131280
1314fn resolveSyntheticSymbols(self: *MachO) !void {
1315 const internal = self.getInternalObject() orelse return;
1316
1317 if (!self.base.isDynLib()) {
1318 self.mh_execute_header_index = try internal.addSymbol("__mh_execute_header", self);
1319 const sym = self.getSymbol(self.mh_execute_header_index.?);
1320 sym.flags.@"export" = true;
1321 sym.flags.dyn_ref = true;
1322 sym.visibility = .global;
1323 } else {
1324 self.mh_dylib_header_index = try internal.addSymbol("__mh_dylib_header", self);
1325 }
1326
1327 self.dso_handle_index = try internal.addSymbol("___dso_handle", self);
1328 self.dyld_private_index = try internal.addSymbol("dyld_private", self);
1329
1281fn convertTentativeDefsAndResolveSpecialSymbols(self: *MachO) !void {
1282 const tp = self.base.comp.thread_pool;
1283 var wg: WaitGroup = .{};
13301284 {
1331 const gpa = self.base.comp.gpa;
1332 var boundary_symbols = std.AutoHashMap(Symbol.Index, void).init(gpa);
1333 defer boundary_symbols.deinit();
1334
1285 wg.reset();
1286 defer wg.wait();
13351287 for (self.objects.items) |index| {
1336 const object = self.getFile(index).?.object;
1337 for (object.symbols.items, 0..) |sym_index, i| {
1338 const nlist = object.symtab.items(.nlist)[i];
1339 const name = self.getSymbol(sym_index).getName(self);
1340 if (!nlist.undf() or !nlist.ext()) continue;
1341 if (mem.startsWith(u8, name, "segment$start$") or
1342 mem.startsWith(u8, name, "segment$stop$") or
1343 mem.startsWith(u8, name, "section$start$") or
1344 mem.startsWith(u8, name, "section$stop$"))
1345 {
1346 _ = try boundary_symbols.put(sym_index, {});
1347 }
1348 }
1288 tp.spawnWg(&wg, convertTentativeDefinitionsWorker, .{ self, self.getFile(index).?.object });
13491289 }
1350
1351 try self.boundary_symbols.ensureTotalCapacityPrecise(gpa, boundary_symbols.count());
1352
1353 var it = boundary_symbols.iterator();
1354 while (it.next()) |entry| {
1355 _ = try internal.addSymbol(self.getSymbol(entry.key_ptr.*).getName(self), self);
1356 self.boundary_symbols.appendAssumeCapacity(entry.key_ptr.*);
1290 if (self.getInternalObject()) |obj| {
1291 tp.spawnWg(&wg, resolveSpecialSymbolsWorker, .{ self, obj });
13571292 }
13581293 }
1294 if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
13591295}
13601296
1361fn convertTentativeDefsAndResolveSpecialSymbols(self: *MachO) !void {
1362 for (self.objects.items) |index| {
1363 try self.getFile(index).?.object.convertTentativeDefinitions(self);
1364 }
1365 if (self.getInternalObject()) |obj| {
1366 try obj.resolveBoundarySymbols(self);
1367 try obj.resolveObjcMsgSendSymbols(self);
1368 }
1297fn convertTentativeDefinitionsWorker(self: *MachO, object: *Object) void {
1298 const tracy = trace(@src());
1299 defer tracy.end();
1300 object.convertTentativeDefinitions(self) catch |err| {
1301 self.reportParseError2(
1302 object.index,
1303 "unexpected error occurred while converting tentative symbols into defined symbols: {s}",
1304 .{@errorName(err)},
1305 ) catch {};
1306 _ = self.has_errors.swap(true, .seq_cst);
1307 };
13691308}
13701309
1371fn createObjcSections(self: *MachO) !void {
1372 const gpa = self.base.comp.gpa;
1373 var objc_msgsend_syms = std.AutoArrayHashMap(Symbol.Index, void).init(gpa);
1374 defer objc_msgsend_syms.deinit();
1375
1376 for (self.objects.items) |index| {
1377 const object = self.getFile(index).?.object;
1378
1379 for (object.symbols.items, 0..) |sym_index, i| {
1380 const nlist_idx = @as(Symbol.Index, @intCast(i));
1381 const nlist = object.symtab.items(.nlist)[nlist_idx];
1382 if (!nlist.ext()) continue;
1383 if (!nlist.undf()) continue;
1384
1385 const sym = self.getSymbol(sym_index);
1386 if (sym.getFile(self) != null) continue;
1387 if (mem.startsWith(u8, sym.getName(self), "_objc_msgSend$")) {
1388 _ = try objc_msgsend_syms.put(sym_index, {});
1389 }
1390 }
1391 }
1392
1393 for (objc_msgsend_syms.keys()) |sym_index| {
1394 const internal = self.getInternalObject().?;
1395 const sym = self.getSymbol(sym_index);
1396 _ = try internal.addSymbol(sym.getName(self), self);
1397 sym.visibility = .hidden;
1398 const name = eatPrefix(sym.getName(self), "_objc_msgSend$").?;
1399 const selrefs_index = try internal.addObjcMsgsendSections(name, self);
1400 try sym.addExtra(.{ .objc_selrefs = selrefs_index }, self);
1401 sym.flags.objc_stubs = true;
1402 }
1310fn resolveSpecialSymbolsWorker(self: *MachO, obj: *InternalObject) void {
1311 const tracy = trace(@src());
1312 defer tracy.end();
1313 obj.resolveBoundarySymbols(self) catch |err| {
1314 self.reportUnexpectedError("unexpected error occurred while resolving boundary symbols: {s}", .{
1315 @errorName(err),
1316 }) catch {};
1317 _ = self.has_errors.swap(true, .seq_cst);
1318 return;
1319 };
1320 obj.resolveObjcMsgSendSymbols(self) catch |err| {
1321 self.reportUnexpectedError("unexpected error occurred while resolving ObjC msgsend stubs: {s}", .{
1322 @errorName(err),
1323 }) catch {};
1324 _ = self.has_errors.swap(true, .seq_cst);
1325 };
14031326}
14041327
14051328pub fn dedupLiterals(self: *MachO) !void {
......@@ -1420,14 +1343,20 @@ pub fn dedupLiterals(self: *MachO) !void {
14201343 try object.resolveLiterals(&lp, self);
14211344 }
14221345
1423 if (self.getZigObject()) |zo| {
1424 zo.dedupLiterals(lp, self);
1425 }
1426 for (self.objects.items) |index| {
1427 self.getFile(index).?.object.dedupLiterals(lp, self);
1428 }
1429 if (self.getInternalObject()) |object| {
1430 object.dedupLiterals(lp, self);
1346 const tp = self.base.comp.thread_pool;
1347 var wg: WaitGroup = .{};
1348 {
1349 wg.reset();
1350 defer wg.wait();
1351 if (self.getZigObject()) |zo| {
1352 tp.spawnWg(&wg, File.dedupLiterals, .{ zo.asFile(), lp, self });
1353 }
1354 for (self.objects.items) |index| {
1355 tp.spawnWg(&wg, File.dedupLiterals, .{ self.getFile(index).?, lp, self });
1356 }
1357 if (self.getInternalObject()) |object| {
1358 tp.spawnWg(&wg, File.dedupLiterals, .{ object.asFile(), lp, self });
1359 }
14311360 }
14321361}
14331362
......@@ -1441,18 +1370,41 @@ fn claimUnresolved(self: *MachO) void {
14411370}
14421371
14431372fn checkDuplicates(self: *MachO) !void {
1444 if (self.getZigObject()) |zo| {
1445 try zo.asFile().checkDuplicates(self);
1446 }
1447 for (self.objects.items) |index| {
1448 try self.getFile(index).?.checkDuplicates(self);
1449 }
1450 if (self.getInternalObject()) |obj| {
1451 try obj.asFile().checkDuplicates(self);
1373 const tracy = trace(@src());
1374 defer tracy.end();
1375
1376 const tp = self.base.comp.thread_pool;
1377 var wg: WaitGroup = .{};
1378 {
1379 wg.reset();
1380 defer wg.wait();
1381 if (self.getZigObject()) |zo| {
1382 tp.spawnWg(&wg, checkDuplicatesWorker, .{ self, zo.asFile() });
1383 }
1384 for (self.objects.items) |index| {
1385 tp.spawnWg(&wg, checkDuplicatesWorker, .{ self, self.getFile(index).? });
1386 }
1387 if (self.getInternalObject()) |obj| {
1388 tp.spawnWg(&wg, checkDuplicatesWorker, .{ self, obj.asFile() });
1389 }
14521390 }
1391
1392 if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
1393
14531394 try self.reportDuplicates();
14541395}
14551396
1397fn checkDuplicatesWorker(self: *MachO, file: File) void {
1398 const tracy = trace(@src());
1399 defer tracy.end();
1400 file.checkDuplicates(self) catch |err| {
1401 self.reportParseError2(file.getIndex(), "failed to check for duplicate definitions: {s}", .{
1402 @errorName(err),
1403 }) catch {};
1404 _ = self.has_errors.swap(true, .seq_cst);
1405 };
1406}
1407
14561408fn markImportsAndExports(self: *MachO) void {
14571409 const tracy = trace(@src());
14581410 defer tracy.end();
......@@ -1491,16 +1443,26 @@ fn scanRelocs(self: *MachO) !void {
14911443 const tracy = trace(@src());
14921444 defer tracy.end();
14931445
1494 if (self.getZigObject()) |zo| {
1495 try zo.scanRelocs(self);
1496 }
1497 for (self.objects.items) |index| {
1498 try self.getFile(index).?.object.scanRelocs(self);
1499 }
1500 if (self.getInternalObject()) |obj| {
1501 obj.scanRelocs(self);
1446 const tp = self.base.comp.thread_pool;
1447 var wg: WaitGroup = .{};
1448
1449 {
1450 wg.reset();
1451 defer wg.wait();
1452
1453 if (self.getZigObject()) |zo| {
1454 tp.spawnWg(&wg, scanRelocsWorker, .{ self, zo.asFile() });
1455 }
1456 for (self.objects.items) |index| {
1457 tp.spawnWg(&wg, scanRelocsWorker, .{ self, self.getFile(index).? });
1458 }
1459 if (self.getInternalObject()) |obj| {
1460 tp.spawnWg(&wg, scanRelocsWorker, .{ self, obj.asFile() });
1461 }
15021462 }
15031463
1464 if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
1465
15041466 try self.reportUndefs();
15051467
15061468 if (self.getZigObject()) |zo| {
......@@ -1517,40 +1479,77 @@ fn scanRelocs(self: *MachO) !void {
15171479 }
15181480}
15191481
1482fn scanRelocsWorker(self: *MachO, file: File) void {
1483 file.scanRelocs(self) catch |err| {
1484 self.reportParseError2(file.getIndex(), "failed to scan relocations: {s}", .{
1485 @errorName(err),
1486 }) catch {};
1487 _ = self.has_errors.swap(true, .seq_cst);
1488 };
1489}
1490
1491fn sortGlobalSymbolsByName(self: *MachO, symbols: []SymbolResolver.Index) void {
1492 const lessThan = struct {
1493 fn lessThan(ctx: *MachO, lhs: SymbolResolver.Index, rhs: SymbolResolver.Index) bool {
1494 const lhs_name = ctx.resolver.keys.items[lhs - 1].getName(ctx);
1495 const rhs_name = ctx.resolver.keys.items[rhs - 1].getName(ctx);
1496 return mem.order(u8, lhs_name, rhs_name) == .lt;
1497 }
1498 }.lessThan;
1499 mem.sort(SymbolResolver.Index, symbols, self, lessThan);
1500}
1501
15201502fn reportUndefs(self: *MachO) !void {
15211503 const tracy = trace(@src());
15221504 defer tracy.end();
15231505
15241506 if (self.undefined_treatment == .suppress or
15251507 self.undefined_treatment == .dynamic_lookup) return;
1508 if (self.undefs.keys().len == 0) return; // Nothing to do
15261509
1510 const gpa = self.base.comp.gpa;
15271511 const max_notes = 4;
15281512
1529 var has_undefs = false;
1530 var it = self.undefs.iterator();
1531 while (it.next()) |entry| {
1532 const undef_sym = self.resolver.keys.items[entry.key_ptr.* - 1];
1533 const notes = entry.value_ptr.*;
1513 // We will sort by name, and then by file to ensure deterministic output.
1514 var keys = try std.ArrayList(SymbolResolver.Index).initCapacity(gpa, self.undefs.keys().len);
1515 defer keys.deinit();
1516 keys.appendSliceAssumeCapacity(self.undefs.keys());
1517 self.sortGlobalSymbolsByName(keys.items);
1518
1519 const refLessThan = struct {
1520 fn lessThan(ctx: void, lhs: Ref, rhs: Ref) bool {
1521 _ = ctx;
1522 return lhs.lessThan(rhs);
1523 }
1524 }.lessThan;
1525
1526 for (self.undefs.values()) |*refs| {
1527 mem.sort(Ref, refs.items, {}, refLessThan);
1528 }
1529
1530 for (keys.items) |key| {
1531 const undef_sym = self.resolver.keys.items[key - 1];
1532 const notes = self.undefs.get(key).?;
15341533 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
15351534
1536 var err = try self.addErrorWithNotes(nnotes);
1537 try err.addMsg(self, "undefined symbol: {s}", .{undef_sym.getName(self)});
1538 has_undefs = true;
1535 var err = try self.base.addErrorWithNotes(nnotes);
1536 try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)});
15391537
15401538 var inote: usize = 0;
15411539 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
15421540 const note = notes.items[inote];
15431541 const file = self.getFile(note.file).?;
15441542 const atom = note.getAtom(self).?;
1545 try err.addNote(self, "referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) });
1543 try err.addNote("referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) });
15461544 }
15471545
15481546 if (notes.items.len > max_notes) {
15491547 const remaining = notes.items.len - max_notes;
1550 try err.addNote(self, "referenced {d} more times", .{remaining});
1548 try err.addNote("referenced {d} more times", .{remaining});
15511549 }
15521550 }
1553 if (has_undefs) return error.HasUndefinedSymbols;
1551
1552 return error.HasUndefinedSymbols;
15541553}
15551554
15561555fn initOutputSections(self: *MachO) !void {
......@@ -1786,7 +1785,7 @@ pub fn sortSections(self: *MachO) !void {
17861785 if (self.getZigObject()) |zo| {
17871786 for (zo.getAtoms()) |atom_index| {
17881787 const atom = zo.getAtom(atom_index) orelse continue;
1789 if (!atom.flags.alive) continue;
1788 if (!atom.isAlive()) continue;
17901789 atom.out_n_sect = backlinks[atom.out_n_sect];
17911790 }
17921791 }
......@@ -1795,7 +1794,7 @@ pub fn sortSections(self: *MachO) !void {
17951794 const file = self.getFile(index).?;
17961795 for (file.getAtoms()) |atom_index| {
17971796 const atom = file.getAtom(atom_index) orelse continue;
1798 if (!atom.flags.alive) continue;
1797 if (!atom.isAlive()) continue;
17991798 atom.out_n_sect = backlinks[atom.out_n_sect];
18001799 }
18011800 }
......@@ -1803,7 +1802,7 @@ pub fn sortSections(self: *MachO) !void {
18031802 if (self.getInternalObject()) |object| {
18041803 for (object.getAtoms()) |atom_index| {
18051804 const atom = object.getAtom(atom_index) orelse continue;
1806 if (!atom.flags.alive) continue;
1805 if (!atom.isAlive()) continue;
18071806 atom.out_n_sect = backlinks[atom.out_n_sect];
18081807 }
18091808 }
......@@ -1844,7 +1843,7 @@ pub fn addAtomsToSections(self: *MachO) !void {
18441843 if (self.getZigObject()) |zo| {
18451844 for (zo.getAtoms()) |atom_index| {
18461845 const atom = zo.getAtom(atom_index) orelse continue;
1847 if (!atom.flags.alive) continue;
1846 if (!atom.isAlive()) continue;
18481847 if (self.isZigSection(atom.out_n_sect)) continue;
18491848 const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
18501849 try atoms.append(gpa, .{ .index = atom_index, .file = zo.index });
......@@ -1854,7 +1853,7 @@ pub fn addAtomsToSections(self: *MachO) !void {
18541853 const file = self.getFile(index).?;
18551854 for (file.getAtoms()) |atom_index| {
18561855 const atom = file.getAtom(atom_index) orelse continue;
1857 if (!atom.flags.alive) continue;
1856 if (!atom.isAlive()) continue;
18581857 const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
18591858 try atoms.append(gpa, .{ .index = atom_index, .file = index });
18601859 }
......@@ -1862,7 +1861,7 @@ pub fn addAtomsToSections(self: *MachO) !void {
18621861 if (self.getInternalObject()) |object| {
18631862 for (object.getAtoms()) |atom_index| {
18641863 const atom = object.getAtom(atom_index) orelse continue;
1865 if (!atom.flags.alive) continue;
1864 if (!atom.isAlive()) continue;
18661865 const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
18671866 try atoms.append(gpa, .{ .index = atom_index, .file = object.index });
18681867 }
......@@ -1881,46 +1880,43 @@ fn calcSectionSizes(self: *MachO) !void {
18811880 header.@"align" = 3;
18821881 }
18831882
1884 const slice = self.sections.slice();
1885 for (slice.items(.header), slice.items(.atoms)) |*header, atoms| {
1886 if (atoms.items.len == 0) continue;
1887 if (self.requiresThunks() and header.isCode()) continue;
1888
1889 for (atoms.items) |ref| {
1890 const atom = ref.getAtom(self).?;
1891 const atom_alignment = atom.alignment.toByteUnits() orelse 1;
1892 const offset = mem.alignForward(u64, header.size, atom_alignment);
1893 const padding = offset - header.size;
1894 atom.value = offset;
1895 header.size += padding + atom.size;
1896 header.@"align" = @max(header.@"align", atom.alignment.toLog2Units());
1897 }
1898 }
1899
1900 if (self.requiresThunks()) {
1883 const tp = self.base.comp.thread_pool;
1884 var wg: WaitGroup = .{};
1885 {
1886 wg.reset();
1887 defer wg.wait();
1888 const slice = self.sections.slice();
19011889 for (slice.items(.header), slice.items(.atoms), 0..) |header, atoms, i| {
1902 if (!header.isCode()) continue;
19031890 if (atoms.items.len == 0) continue;
1891 if (self.requiresThunks() and header.isCode()) continue;
1892 tp.spawnWg(&wg, calcSectionSizeWorker, .{ self, @as(u8, @intCast(i)) });
1893 }
19041894
1905 // Create jump/branch range extenders if needed.
1906 try thunks.createThunks(@intCast(i), self);
1895 if (self.requiresThunks()) {
1896 for (slice.items(.header), slice.items(.atoms), 0..) |header, atoms, i| {
1897 if (!header.isCode()) continue;
1898 if (atoms.items.len == 0) continue;
1899 tp.spawnWg(&wg, createThunksWorker, .{ self, @as(u8, @intCast(i)) });
1900 }
19071901 }
1908 }
19091902
1910 // At this point, we can also calculate symtab and data-in-code linkedit section sizes
1911 if (self.getZigObject()) |zo| {
1912 zo.asFile().calcSymtabSize(self);
1913 }
1914 for (self.objects.items) |index| {
1915 self.getFile(index).?.calcSymtabSize(self);
1916 }
1917 for (self.dylibs.items) |index| {
1918 self.getFile(index).?.calcSymtabSize(self);
1919 }
1920 if (self.getInternalObject()) |obj| {
1921 obj.asFile().calcSymtabSize(self);
1903 // At this point, we can also calculate symtab and data-in-code linkedit section sizes
1904 if (self.getZigObject()) |zo| {
1905 tp.spawnWg(&wg, File.calcSymtabSize, .{ zo.asFile(), self });
1906 }
1907 for (self.objects.items) |index| {
1908 tp.spawnWg(&wg, File.calcSymtabSize, .{ self.getFile(index).?, self });
1909 }
1910 for (self.dylibs.items) |index| {
1911 tp.spawnWg(&wg, File.calcSymtabSize, .{ self.getFile(index).?, self });
1912 }
1913 if (self.getInternalObject()) |obj| {
1914 tp.spawnWg(&wg, File.calcSymtabSize, .{ obj.asFile(), self });
1915 }
19221916 }
19231917
1918 if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
1919
19241920 try self.calcSymtabSize();
19251921
19261922 if (self.got_sect_index) |idx| {
......@@ -1968,6 +1964,49 @@ fn calcSectionSizes(self: *MachO) !void {
19681964 }
19691965}
19701966
1967fn calcSectionSizeWorker(self: *MachO, sect_id: u8) void {
1968 const tracy = trace(@src());
1969 defer tracy.end();
1970 const doWork = struct {
1971 fn doWork(macho_file: *MachO, header: *macho.section_64, atoms: []const Ref) !void {
1972 for (atoms) |ref| {
1973 const atom = ref.getAtom(macho_file).?;
1974 const atom_alignment = atom.alignment.toByteUnits() orelse 1;
1975 const offset = mem.alignForward(u64, header.size, atom_alignment);
1976 const padding = offset - header.size;
1977 atom.value = offset;
1978 header.size += padding + atom.size;
1979 header.@"align" = @max(header.@"align", atom.alignment.toLog2Units());
1980 }
1981 }
1982 }.doWork;
1983 const slice = self.sections.slice();
1984 const header = &slice.items(.header)[sect_id];
1985 const atoms = slice.items(.atoms)[sect_id].items;
1986 doWork(self, header, atoms) catch |err| {
1987 self.reportUnexpectedError("failed to calculate size of section '{s},{s}': {s}", .{
1988 header.segName(),
1989 header.sectName(),
1990 @errorName(err),
1991 }) catch {};
1992 _ = self.has_errors.swap(true, .seq_cst);
1993 };
1994}
1995
1996fn createThunksWorker(self: *MachO, sect_id: u8) void {
1997 const tracy = trace(@src());
1998 defer tracy.end();
1999 thunks.createThunks(sect_id, self) catch |err| {
2000 const header = self.sections.items(.header)[sect_id];
2001 self.reportUnexpectedError("failed to create thunks and calculate size of section '{s},{s}': {s}", .{
2002 header.segName(),
2003 header.sectName(),
2004 @errorName(err),
2005 }) catch {};
2006 _ = self.has_errors.swap(true, .seq_cst);
2007 };
2008}
2009
19712010fn generateUnwindInfo(self: *MachO) !void {
19722011 const tracy = trace(@src());
19732012 defer tracy.end();
......@@ -2349,6 +2388,9 @@ fn resizeSections(self: *MachO) !void {
23492388}
23502389
23512390fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void {
2391 const tracy = trace(@src());
2392 defer tracy.end();
2393
23522394 const gpa = self.base.comp.gpa;
23532395
23542396 const cmd = self.symtab_cmd;
......@@ -2356,64 +2398,98 @@ fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void {
23562398 try self.strtab.resize(gpa, cmd.strsize);
23572399 self.strtab.items[0] = 0;
23582400
2359 for (self.objects.items) |index| {
2360 try self.getFile(index).?.writeAtoms(self);
2361 }
2362 if (self.getZigObject()) |zo| {
2363 try zo.writeAtoms(self);
2364 }
2365 if (self.getInternalObject()) |obj| {
2366 try obj.asFile().writeAtoms(self);
2367 }
2368 for (self.thunks.items) |thunk| {
2369 const out = self.sections.items(.out)[thunk.out_n_sect].items;
2370 const off = math.cast(usize, thunk.value) orelse return error.Overflow;
2371 const size = thunk.size();
2372 var stream = std.io.fixedBufferStream(out[off..][0..size]);
2373 try thunk.write(self, stream.writer());
2374 }
2401 const tp = self.base.comp.thread_pool;
2402 var wg: WaitGroup = .{};
2403 {
2404 wg.reset();
2405 defer wg.wait();
23752406
2376 const slice = self.sections.slice();
2377 for (&[_]?u8{
2378 self.eh_frame_sect_index,
2379 self.unwind_info_sect_index,
2380 self.got_sect_index,
2381 self.stubs_sect_index,
2382 self.la_symbol_ptr_sect_index,
2383 self.tlv_ptr_sect_index,
2384 self.objc_stubs_sect_index,
2385 }) |maybe_sect_id| {
2386 if (maybe_sect_id) |sect_id| {
2387 const out = slice.items(.out)[sect_id].items;
2388 try self.writeSyntheticSection(sect_id, out);
2407 for (self.objects.items) |index| {
2408 tp.spawnWg(&wg, writeAtomsWorker, .{ self, self.getFile(index).? });
2409 }
2410 if (self.getZigObject()) |zo| {
2411 tp.spawnWg(&wg, writeAtomsWorker, .{ self, zo.asFile() });
2412 }
2413 if (self.getInternalObject()) |obj| {
2414 tp.spawnWg(&wg, writeAtomsWorker, .{ self, obj.asFile() });
2415 }
2416 for (self.thunks.items) |thunk| {
2417 tp.spawnWg(&wg, writeThunkWorker, .{ self, thunk });
23892418 }
2390 }
23912419
2392 if (self.la_symbol_ptr_sect_index) |_| {
2393 try self.updateLazyBindSize();
2394 }
2420 const slice = self.sections.slice();
2421 for (&[_]?u8{
2422 self.eh_frame_sect_index,
2423 self.unwind_info_sect_index,
2424 self.got_sect_index,
2425 self.stubs_sect_index,
2426 self.la_symbol_ptr_sect_index,
2427 self.tlv_ptr_sect_index,
2428 self.objc_stubs_sect_index,
2429 }) |maybe_sect_id| {
2430 if (maybe_sect_id) |sect_id| {
2431 const out = slice.items(.out)[sect_id].items;
2432 tp.spawnWg(&wg, writeSyntheticSectionWorker, .{ self, sect_id, out });
2433 }
2434 }
23952435
2396 try self.rebase.updateSize(self);
2397 try self.bind.updateSize(self);
2398 try self.weak_bind.updateSize(self);
2399 try self.export_trie.updateSize(self);
2400 try self.data_in_code.updateSize(self);
2436 if (self.la_symbol_ptr_sect_index) |_| {
2437 tp.spawnWg(&wg, updateLazyBindSizeWorker, .{self});
2438 }
24012439
2402 if (self.getZigObject()) |zo| {
2403 zo.asFile().writeSymtab(self, self);
2404 }
2405 for (self.objects.items) |index| {
2406 self.getFile(index).?.writeSymtab(self, self);
2407 }
2408 for (self.dylibs.items) |index| {
2409 self.getFile(index).?.writeSymtab(self, self);
2410 }
2411 if (self.getInternalObject()) |obj| {
2412 obj.asFile().writeSymtab(self, self);
2440 tp.spawnWg(&wg, updateLinkeditSizeWorker, .{ self, .rebase });
2441 tp.spawnWg(&wg, updateLinkeditSizeWorker, .{ self, .bind });
2442 tp.spawnWg(&wg, updateLinkeditSizeWorker, .{ self, .weak_bind });
2443 tp.spawnWg(&wg, updateLinkeditSizeWorker, .{ self, .export_trie });
2444 tp.spawnWg(&wg, updateLinkeditSizeWorker, .{ self, .data_in_code });
2445
2446 if (self.getZigObject()) |zo| {
2447 tp.spawnWg(&wg, File.writeSymtab, .{ zo.asFile(), self, self });
2448 }
2449 for (self.objects.items) |index| {
2450 tp.spawnWg(&wg, File.writeSymtab, .{ self.getFile(index).?, self, self });
2451 }
2452 for (self.dylibs.items) |index| {
2453 tp.spawnWg(&wg, File.writeSymtab, .{ self.getFile(index).?, self, self });
2454 }
2455 if (self.getInternalObject()) |obj| {
2456 tp.spawnWg(&wg, File.writeSymtab, .{ obj.asFile(), self, self });
2457 }
24132458 }
2459
2460 if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
2461}
2462
2463fn writeAtomsWorker(self: *MachO, file: File) void {
2464 const tracy = trace(@src());
2465 defer tracy.end();
2466 file.writeAtoms(self) catch |err| {
2467 self.reportParseError2(file.getIndex(), "failed to resolve relocations and write atoms: {s}", .{
2468 @errorName(err),
2469 }) catch {};
2470 _ = self.has_errors.swap(true, .seq_cst);
2471 };
2472}
2473
2474fn writeThunkWorker(self: *MachO, thunk: Thunk) void {
2475 const tracy = trace(@src());
2476 defer tracy.end();
2477 const doWork = struct {
2478 fn doWork(th: Thunk, buffer: []u8, macho_file: *MachO) !void {
2479 const off = math.cast(usize, th.value) orelse return error.Overflow;
2480 const size = th.size();
2481 var stream = std.io.fixedBufferStream(buffer[off..][0..size]);
2482 try th.write(macho_file, stream.writer());
2483 }
2484 }.doWork;
2485 const out = self.sections.items(.out)[thunk.out_n_sect].items;
2486 doWork(thunk, out, self) catch |err| {
2487 self.reportUnexpectedError("failed to write contents of thunk: {s}", .{@errorName(err)}) catch {};
2488 _ = self.has_errors.swap(true, .seq_cst);
2489 };
24142490}
24152491
2416fn writeSyntheticSection(self: *MachO, sect_id: u8, out: []u8) !void {
2492fn writeSyntheticSectionWorker(self: *MachO, sect_id: u8, out: []u8) void {
24172493 const tracy = trace(@src());
24182494 defer tracy.end();
24192495
......@@ -2427,6 +2503,22 @@ fn writeSyntheticSection(self: *MachO, sect_id: u8, out: []u8) !void {
24272503 objc_stubs,
24282504 };
24292505
2506 const doWork = struct {
2507 fn doWork(macho_file: *MachO, tag: Tag, buffer: []u8) !void {
2508 var stream = std.io.fixedBufferStream(buffer);
2509 switch (tag) {
2510 .eh_frame => eh_frame.write(macho_file, buffer),
2511 .unwind_info => try macho_file.unwind_info.write(macho_file, buffer),
2512 .got => try macho_file.got.write(macho_file, stream.writer()),
2513 .stubs => try macho_file.stubs.write(macho_file, stream.writer()),
2514 .la_symbol_ptr => try macho_file.la_symbol_ptr.write(macho_file, stream.writer()),
2515 .tlv_ptr => try macho_file.tlv_ptr.write(macho_file, stream.writer()),
2516 .objc_stubs => try macho_file.objc_stubs.write(macho_file, stream.writer()),
2517 }
2518 }
2519 }.doWork;
2520
2521 const header = self.sections.items(.header)[sect_id];
24302522 const tag: Tag = tag: {
24312523 if (self.eh_frame_sect_index != null and
24322524 self.eh_frame_sect_index.? == sect_id) break :tag .eh_frame;
......@@ -2444,26 +2536,57 @@ fn writeSyntheticSection(self: *MachO, sect_id: u8, out: []u8) !void {
24442536 self.objc_stubs_sect_index.? == sect_id) break :tag .objc_stubs;
24452537 unreachable;
24462538 };
2447 var stream = std.io.fixedBufferStream(out);
2448 switch (tag) {
2449 .eh_frame => eh_frame.write(self, out),
2450 .unwind_info => try self.unwind_info.write(self, out),
2451 .got => try self.got.write(self, stream.writer()),
2452 .stubs => try self.stubs.write(self, stream.writer()),
2453 .la_symbol_ptr => try self.la_symbol_ptr.write(self, stream.writer()),
2454 .tlv_ptr => try self.tlv_ptr.write(self, stream.writer()),
2455 .objc_stubs => try self.objc_stubs.write(self, stream.writer()),
2456 }
2539 doWork(self, tag, out) catch |err| {
2540 self.reportUnexpectedError("could not write section '{s},{s}': {s}", .{
2541 header.segName(),
2542 header.sectName(),
2543 @errorName(err),
2544 }) catch {};
2545 _ = self.has_errors.swap(true, .seq_cst);
2546 };
24572547}
24582548
2459fn updateLazyBindSize(self: *MachO) !void {
2549fn updateLazyBindSizeWorker(self: *MachO) void {
24602550 const tracy = trace(@src());
24612551 defer tracy.end();
2462 try self.lazy_bind.updateSize(self);
2463 const sect_id = self.stubs_helper_sect_index.?;
2464 const out = &self.sections.items(.out)[sect_id];
2465 var stream = std.io.fixedBufferStream(out.items);
2466 try self.stubs_helper.write(self, stream.writer());
2552 const doWork = struct {
2553 fn doWork(macho_file: *MachO) !void {
2554 try macho_file.lazy_bind.updateSize(macho_file);
2555 const sect_id = macho_file.stubs_helper_sect_index.?;
2556 const out = &macho_file.sections.items(.out)[sect_id];
2557 var stream = std.io.fixedBufferStream(out.items);
2558 try macho_file.stubs_helper.write(macho_file, stream.writer());
2559 }
2560 }.doWork;
2561 doWork(self) catch |err| {
2562 self.reportUnexpectedError("could not calculate size of lazy binding section: {s}", .{
2563 @errorName(err),
2564 }) catch {};
2565 _ = self.has_errors.swap(true, .seq_cst);
2566 };
2567}
2568
2569pub fn updateLinkeditSizeWorker(self: *MachO, tag: enum {
2570 rebase,
2571 bind,
2572 weak_bind,
2573 export_trie,
2574 data_in_code,
2575}) void {
2576 const res = switch (tag) {
2577 .rebase => self.rebase.updateSize(self),
2578 .bind => self.bind.updateSize(self),
2579 .weak_bind => self.weak_bind.updateSize(self),
2580 .export_trie => self.export_trie.updateSize(self),
2581 .data_in_code => self.data_in_code.updateSize(self),
2582 };
2583 res catch |err| {
2584 self.reportUnexpectedError("could not calculate size of {s} section: {s}", .{
2585 @tagName(tag),
2586 @errorName(err),
2587 }) catch {};
2588 _ = self.has_errors.swap(true, .seq_cst);
2589 };
24672590}
24682591
24692592fn writeSectionsToFile(self: *MachO) !void {
......@@ -2791,13 +2914,13 @@ fn writeHeader(self: *MachO, ncmds: usize, sizeofcmds: usize) !void {
27912914 header.flags |= macho.MH_NO_REEXPORTED_DYLIBS;
27922915 }
27932916
2794 if (self.has_tlv) {
2917 if (self.has_tlv.load(.seq_cst)) {
27952918 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
27962919 }
2797 if (self.binds_to_weak) {
2920 if (self.binds_to_weak.load(.seq_cst)) {
27982921 header.flags |= macho.MH_BINDS_TO_WEAK;
27992922 }
2800 if (self.weak_defines) {
2923 if (self.weak_defines.load(.seq_cst)) {
28012924 header.flags |= macho.MH_WEAK_DEFINES;
28022925 }
28032926
......@@ -3323,13 +3446,13 @@ fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !vo
33233446
33243447 const mem_capacity = self.allocatedSizeVirtual(seg.vmaddr);
33253448 if (needed_size > mem_capacity) {
3326 var err = try self.addErrorWithNotes(2);
3327 try err.addMsg(self, "fatal linker error: cannot expand segment seg({d})({s}) in virtual memory", .{
3449 var err = try self.base.addErrorWithNotes(2);
3450 try err.addMsg("fatal linker error: cannot expand segment seg({d})({s}) in virtual memory", .{
33283451 seg_id,
33293452 seg.segName(),
33303453 });
3331 try err.addNote(self, "TODO: emit relocations to memory locations in self-hosted backends", .{});
3332 try err.addNote(self, "as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
3454 try err.addNote("TODO: emit relocations to memory locations in self-hosted backends", .{});
3455 try err.addNote("as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
33333456 }
33343457
33353458 seg.vmsize = needed_size;
......@@ -3394,6 +3517,8 @@ pub fn getTarget(self: MachO) std.Target {
33943517/// the original file. This is super messy, but there doesn't seem any other
33953518/// way to please the XNU.
33963519pub fn invalidateKernelCache(dir: fs.Dir, sub_path: []const u8) !void {
3520 const tracy = trace(@src());
3521 defer tracy.end();
33973522 if (comptime builtin.target.isDarwin() and builtin.target.cpu.arch == .aarch64) {
33983523 try dir.copyFile(sub_path, dir, sub_path, .{});
33993524 }
......@@ -3618,65 +3743,15 @@ pub fn eatPrefix(path: []const u8, prefix: []const u8) ?[]const u8 {
36183743 return null;
36193744}
36203745
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
36713746pub fn reportParseError(
36723747 self: *MachO,
36733748 path: []const u8,
36743749 comptime format: []const u8,
36753750 args: anytype,
36763751) error{OutOfMemory}!void {
3677 var err = try self.addErrorWithNotes(1);
3678 try err.addMsg(self, format, args);
3679 try err.addNote(self, "while parsing {s}", .{path});
3752 var err = try self.base.addErrorWithNotes(1);
3753 try err.addMsg(format, args);
3754 try err.addNote("while parsing {s}", .{path});
36803755}
36813756
36823757pub fn reportParseError2(
......@@ -3685,9 +3760,9 @@ pub fn reportParseError2(
36853760 comptime format: []const u8,
36863761 args: anytype,
36873762) error{OutOfMemory}!void {
3688 var err = try self.addErrorWithNotes(1);
3689 try err.addMsg(self, format, args);
3690 try err.addNote(self, "while parsing {}", .{self.getFile(file_index).?.fmtPath()});
3763 var err = try self.base.addErrorWithNotes(1);
3764 try err.addMsg(format, args);
3765 try err.addNote("while parsing {}", .{self.getFile(file_index).?.fmtPath()});
36913766}
36923767
36933768fn reportMissingLibraryError(
......@@ -3696,10 +3771,10 @@ fn reportMissingLibraryError(
36963771 comptime format: []const u8,
36973772 args: anytype,
36983773) error{OutOfMemory}!void {
3699 var err = try self.addErrorWithNotes(checked_paths.len);
3700 try err.addMsg(self, format, args);
3774 var err = try self.base.addErrorWithNotes(checked_paths.len);
3775 try err.addMsg(format, args);
37013776 for (checked_paths) |path| {
3702 try err.addNote(self, "tried {s}", .{path});
3777 try err.addNote("tried {s}", .{path});
37033778 }
37043779}
37053780
......@@ -3711,12 +3786,12 @@ fn reportMissingDependencyError(
37113786 comptime format: []const u8,
37123787 args: anytype,
37133788) error{OutOfMemory}!void {
3714 var err = try self.addErrorWithNotes(2 + checked_paths.len);
3715 try err.addMsg(self, format, args);
3716 try err.addNote(self, "while resolving {s}", .{path});
3717 try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()});
3789 var err = try self.base.addErrorWithNotes(2 + checked_paths.len);
3790 try err.addMsg(format, args);
3791 try err.addNote("while resolving {s}", .{path});
3792 try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()});
37183793 for (checked_paths) |p| {
3719 try err.addNote(self, "tried {s}", .{p});
3794 try err.addNote("tried {s}", .{p});
37203795 }
37213796}
37223797
......@@ -3727,48 +3802,58 @@ fn reportDependencyError(
37273802 comptime format: []const u8,
37283803 args: anytype,
37293804) error{OutOfMemory}!void {
3730 var err = try self.addErrorWithNotes(2);
3731 try err.addMsg(self, format, args);
3732 try err.addNote(self, "while parsing {s}", .{path});
3733 try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()});
3805 var err = try self.base.addErrorWithNotes(2);
3806 try err.addMsg(format, args);
3807 try err.addNote("while parsing {s}", .{path});
3808 try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()});
37343809}
37353810
37363811pub fn reportUnexpectedError(self: *MachO, comptime format: []const u8, args: anytype) error{OutOfMemory}!void {
3737 var err = try self.addErrorWithNotes(1);
3738 try err.addMsg(self, format, args);
3739 try err.addNote(self, "please report this as a linker bug on https://github.com/ziglang/zig/issues/new/choose", .{});
3812 var err = try self.base.addErrorWithNotes(1);
3813 try err.addMsg(format, args);
3814 try err.addNote("please report this as a linker bug on https://github.com/ziglang/zig/issues/new/choose", .{});
37403815}
37413816
37423817fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
37433818 const tracy = trace(@src());
37443819 defer tracy.end();
37453820
3821 if (self.dupes.keys().len == 0) return; // Nothing to do
3822
3823 const gpa = self.base.comp.gpa;
37463824 const max_notes = 3;
37473825
3748 var has_dupes = false;
3749 var it = self.dupes.iterator();
3750 while (it.next()) |entry| {
3751 const sym = self.resolver.keys.items[entry.key_ptr.* - 1];
3752 const notes = entry.value_ptr.*;
3826 // We will sort by name, and then by file to ensure deterministic output.
3827 var keys = try std.ArrayList(SymbolResolver.Index).initCapacity(gpa, self.dupes.keys().len);
3828 defer keys.deinit();
3829 keys.appendSliceAssumeCapacity(self.dupes.keys());
3830 self.sortGlobalSymbolsByName(keys.items);
3831
3832 for (self.dupes.values()) |*refs| {
3833 mem.sort(File.Index, refs.items, {}, std.sort.asc(File.Index));
3834 }
3835
3836 for (keys.items) |key| {
3837 const sym = self.resolver.keys.items[key - 1];
3838 const notes = self.dupes.get(key).?;
37533839 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
37543840
3755 var err = try self.addErrorWithNotes(nnotes + 1);
3756 try err.addMsg(self, "duplicate symbol definition: {s}", .{sym.getName(self)});
3757 try err.addNote(self, "defined by {}", .{sym.getFile(self).?.fmtPath()});
3758 has_dupes = true;
3841 var err = try self.base.addErrorWithNotes(nnotes + 1);
3842 try err.addMsg("duplicate symbol definition: {s}", .{sym.getName(self)});
3843 try err.addNote("defined by {}", .{sym.getFile(self).?.fmtPath()});
37593844
37603845 var inote: usize = 0;
37613846 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
37623847 const file = self.getFile(notes.items[inote]).?;
3763 try err.addNote(self, "defined by {}", .{file.fmtPath()});
3848 try err.addNote("defined by {}", .{file.fmtPath()});
37643849 }
37653850
37663851 if (notes.items.len > max_notes) {
37673852 const remaining = notes.items.len - max_notes;
3768 try err.addNote(self, "defined {d} more times", .{remaining});
3853 try err.addNote("defined {d} more times", .{remaining});
37693854 }
37703855 }
3771 if (has_dupes) return error.HasDuplicates;
3856 return error.HasDuplicates;
37723857}
37733858
37743859pub fn getDebugSymbols(self: *MachO) ?*DebugSymbols {
......@@ -4367,6 +4452,13 @@ pub const Ref = struct {
43674452 return ref.index == other.index and ref.file == other.file;
43684453 }
43694454
4455 pub fn lessThan(ref: Ref, other: Ref) bool {
4456 if (ref.file == other.file) {
4457 return ref.index < other.index;
4458 }
4459 return ref.file < other.file;
4460 }
4461
43704462 pub fn getFile(ref: Ref, macho_file: *MachO) ?File {
43714463 return macho_file.getFile(ref.file);
43724464 }
......@@ -4487,6 +4579,11 @@ pub const SymbolResolver = struct {
44874579 pub const Index = u32;
44884580};
44894581
4582pub const String = struct {
4583 pos: u32 = 0,
4584 len: u32 = 0,
4585};
4586
44904587const MachO = @This();
44914588
44924589const std = @import("std");
......@@ -4523,6 +4620,7 @@ const Alignment = Atom.Alignment;
45234620const Allocator = mem.Allocator;
45244621const Archive = @import("MachO/Archive.zig");
45254622pub const Atom = @import("MachO/Atom.zig");
4623const AtomicBool = std.atomic.Value(bool);
45264624const Bind = bind.Bind;
45274625const Cache = std.Build.Cache;
45284626const CodeSignature = @import("MachO/CodeSignature.zig");
......@@ -4540,7 +4638,6 @@ const ObjcStubsSection = synthetic.ObjcStubsSection;
45404638const Object = @import("MachO/Object.zig");
45414639const LazyBind = bind.LazyBind;
45424640const LaSymbolPtrSection = synthetic.LaSymbolPtrSection;
4543const LibStub = tapi.LibStub;
45444641const Liveness = @import("../Liveness.zig");
45454642const LlvmObject = @import("../codegen/llvm.zig").Object;
45464643const Md5 = std.crypto.hash.Md5;
......@@ -4558,6 +4655,7 @@ const Thunk = thunks.Thunk;
45584655const TlvPtrSection = synthetic.TlvPtrSection;
45594656const Value = @import("../Value.zig");
45604657const UnwindInfo = @import("MachO/UnwindInfo.zig");
4658const WaitGroup = std.Thread.WaitGroup;
45614659const WeakBind = bind.WeakBind;
45624660const ZigGotSection = synthetic.ZigGotSection;
45634661const ZigObject = @import("MachO/ZigObject.zig");
src/link/MachO/Archive.zig+1-12
......@@ -1,21 +1,10 @@
11objects: std.ArrayListUnmanaged(Object) = .{},
22
3pub fn isArchive(path: []const u8, fat_arch: ?fat.Arch) !bool {
4 const file = try std.fs.cwd().openFile(path, .{});
5 defer file.close();
6 if (fat_arch) |arch| {
7 try file.seekTo(arch.offset);
8 }
9 const magic = file.reader().readBytesNoEof(SARMAG) catch return false;
10 if (!mem.eql(u8, &magic, ARMAG)) return false;
11 return true;
12}
13
143pub fn deinit(self: *Archive, allocator: Allocator) void {
154 self.objects.deinit(allocator);
165}
176
18pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index: File.HandleIndex, fat_arch: ?fat.Arch) !void {
7pub fn unpack(self: *Archive, macho_file: *MachO, path: []const u8, handle_index: File.HandleIndex, fat_arch: ?fat.Arch) !void {
198 const gpa = macho_file.base.comp.gpa;
209
2110 var arena = std.heap.ArenaAllocator.init(gpa);
src/link/MachO/Atom.zig+41-35
......@@ -2,7 +2,7 @@
22value: u64 = 0,
33
44/// Name of this Atom.
5name: u32 = 0,
5name: MachO.String = .{},
66
77/// Index into linker's input file table.
88file: File.Index = 0,
......@@ -26,7 +26,11 @@ off: u64 = 0,
2626/// Index of this atom in the linker's atoms table.
2727atom_index: Index = 0,
2828
29flags: Flags = .{},
29/// Specifies whether this atom is alive or has been garbage collected.
30alive: AtomicBool = AtomicBool.init(true),
31
32/// Specifies if this atom has been visited during garbage collection.
33visited: AtomicBool = AtomicBool.init(false),
3034
3135/// Points to the previous and next neighbors, based on the `text_offset`.
3236/// This can be used to find, for example, the capacity of this `TextBlock`.
......@@ -38,7 +42,6 @@ extra: u32 = 0,
3842pub fn getName(self: Atom, macho_file: *MachO) [:0]const u8 {
3943 return switch (self.getFile(macho_file)) {
4044 .dylib => unreachable,
41 .zig_object => |x| x.strtab.getAssumeExists(self.name),
4245 inline else => |x| x.getString(self.name),
4346 };
4447}
......@@ -98,6 +101,14 @@ pub fn markUnwindRecordsDead(self: Atom, macho_file: *MachO) void {
98101 }
99102}
100103
104pub fn isAlive(self: Atom) bool {
105 return self.alive.load(.seq_cst);
106}
107
108pub fn setAlive(self: *Atom, alive: bool) void {
109 _ = self.alive.swap(alive, .seq_cst);
110}
111
101112pub fn getThunk(self: Atom, macho_file: *MachO) *Thunk {
102113 const extra = self.getExtra(macho_file);
103114 return macho_file.getThunk(extra.thunk);
......@@ -350,7 +361,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {
350361 _ = free_list.swapRemove(i);
351362 }
352363
353 self.flags.alive = true;
364 self.setAlive(true);
354365}
355366
356367pub fn shrink(self: *Atom, macho_file: *MachO) void {
......@@ -444,7 +455,7 @@ pub fn freeRelocs(self: *Atom, macho_file: *MachO) void {
444455pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
445456 const tracy = trace(@src());
446457 defer tracy.end();
447 assert(self.flags.alive);
458 assert(self.isAlive());
448459
449460 const relocs = self.getRelocs(macho_file);
450461
......@@ -455,12 +466,12 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
455466 .branch => {
456467 const symbol = rel.getTargetSymbol(self, macho_file);
457468 if (symbol.flags.import or (symbol.flags.@"export" and symbol.flags.weak) or symbol.flags.interposable) {
458 symbol.flags.stubs = true;
469 symbol.setSectionFlags(.{ .stubs = true });
459470 if (symbol.flags.weak) {
460 macho_file.binds_to_weak = true;
471 macho_file.binds_to_weak.store(true, .seq_cst);
461472 }
462473 } else if (mem.startsWith(u8, symbol.getName(macho_file), "_objc_msgSend$")) {
463 symbol.flags.objc_stubs = true;
474 symbol.setSectionFlags(.{ .objc_stubs = true });
464475 }
465476 },
466477
......@@ -474,19 +485,19 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
474485 symbol.flags.interposable or
475486 macho_file.getTarget().cpu.arch == .aarch64) // TODO relax on arm64
476487 {
477 symbol.flags.needs_got = true;
488 symbol.setSectionFlags(.{ .needs_got = true });
478489 if (symbol.flags.weak) {
479 macho_file.binds_to_weak = true;
490 macho_file.binds_to_weak.store(true, .seq_cst);
480491 }
481492 }
482493 },
483494
484495 .zig_got_load => {
485 assert(rel.getTargetSymbol(self, macho_file).flags.has_zig_got);
496 assert(rel.getTargetSymbol(self, macho_file).getSectionFlags().has_zig_got);
486497 },
487498
488499 .got => {
489 rel.getTargetSymbol(self, macho_file).flags.needs_got = true;
500 rel.getTargetSymbol(self, macho_file).setSectionFlags(.{ .needs_got = true });
490501 },
491502
492503 .tlv,
......@@ -502,9 +513,9 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
502513 );
503514 }
504515 if (symbol.flags.import or (symbol.flags.@"export" and symbol.flags.weak) or symbol.flags.interposable) {
505 symbol.flags.tlv_ptr = true;
516 symbol.setSectionFlags(.{ .tlv_ptr = true });
506517 if (symbol.flags.weak) {
507 macho_file.binds_to_weak = true;
518 macho_file.binds_to_weak.store(true, .seq_cst);
508519 }
509520 }
510521 },
......@@ -514,17 +525,17 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
514525 if (rel.tag == .@"extern") {
515526 const symbol = rel.getTargetSymbol(self, macho_file);
516527 if (symbol.isTlvInit(macho_file)) {
517 macho_file.has_tlv = true;
528 macho_file.has_tlv.store(true, .seq_cst);
518529 continue;
519530 }
520531 if (symbol.flags.import) {
521532 if (symbol.flags.weak) {
522 macho_file.binds_to_weak = true;
533 macho_file.binds_to_weak.store(true, .seq_cst);
523534 }
524535 continue;
525536 }
526537 if (symbol.flags.@"export" and symbol.flags.weak) {
527 macho_file.binds_to_weak = true;
538 macho_file.binds_to_weak.store(true, .seq_cst);
528539 }
529540 }
530541 }
......@@ -548,6 +559,8 @@ fn reportUndefSymbol(self: Atom, rel: Relocation, macho_file: *MachO) !bool {
548559 const file = self.getFile(macho_file);
549560 const ref = file.getSymbolRef(rel.target, macho_file);
550561 if (ref.getFile(macho_file) == null) {
562 macho_file.undefs_mutex.lock();
563 defer macho_file.undefs_mutex.unlock();
551564 const gpa = macho_file.base.comp.gpa;
552565 const gop = try macho_file.undefs.getOrPut(gpa, file.getGlobals()[rel.target]);
553566 if (!gop.found_existing) {
......@@ -724,7 +737,7 @@ fn resolveRelocInner(
724737 assert(rel.tag == .@"extern");
725738 assert(rel.meta.length == 2);
726739 assert(rel.meta.pcrel);
727 if (rel.getTargetSymbol(self, macho_file).flags.has_got) {
740 if (rel.getTargetSymbol(self, macho_file).getSectionFlags().has_got) {
728741 try writer.writeInt(i32, @intCast(G + A - P), .little);
729742 } else {
730743 try x86_64.relaxGotLoad(self, code[rel_offset - 3 ..], rel, macho_file);
......@@ -748,7 +761,7 @@ fn resolveRelocInner(
748761 assert(rel.meta.length == 2);
749762 assert(rel.meta.pcrel);
750763 const sym = rel.getTargetSymbol(self, macho_file);
751 if (sym.flags.tlv_ptr) {
764 if (sym.getSectionFlags().tlv_ptr) {
752765 const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
753766 try writer.writeInt(i32, @intCast(S_ + A - P), .little);
754767 } else {
......@@ -776,7 +789,7 @@ fn resolveRelocInner(
776789 const target = switch (rel.type) {
777790 .page => S + A,
778791 .got_load_page => G + A,
779 .tlvp_page => if (sym.flags.tlv_ptr) blk: {
792 .tlvp_page => if (sym.getSectionFlags().tlv_ptr) blk: {
780793 const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
781794 break :blk S_ + A;
782795 } else S + A,
......@@ -831,7 +844,7 @@ fn resolveRelocInner(
831844
832845 const sym = rel.getTargetSymbol(self, macho_file);
833846 const target = target: {
834 const target = if (sym.flags.tlv_ptr) blk: {
847 const target = if (sym.getSectionFlags().tlv_ptr) blk: {
835848 const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
836849 break :blk S_ + A;
837850 } else S + A;
......@@ -869,7 +882,7 @@ fn resolveRelocInner(
869882 }
870883 };
871884
872 var inst = if (sym.flags.tlv_ptr) aarch64.Instruction{
885 var inst = if (sym.getSectionFlags().tlv_ptr) aarch64.Instruction{
873886 .load_store_register = .{
874887 .rt = reg_info.rd,
875888 .rn = reg_info.rn,
......@@ -906,15 +919,15 @@ const x86_64 = struct {
906919 encode(&.{inst}, code) catch return error.RelaxFail;
907920 },
908921 else => |x| {
909 var err = try macho_file.addErrorWithNotes(2);
910 try err.addMsg(macho_file, "{s}: 0x{x}: 0x{x}: failed to relax relocation of type {}", .{
922 var err = try macho_file.base.addErrorWithNotes(2);
923 try err.addMsg("{s}: 0x{x}: 0x{x}: failed to relax relocation of type {}", .{
911924 self.getName(macho_file),
912925 self.getAddress(macho_file),
913926 rel.offset,
914927 rel.fmtPretty(.x86_64),
915928 });
916 try err.addNote(macho_file, "expected .mov instruction but found .{s}", .{@tagName(x)});
917 try err.addNote(macho_file, "while parsing {}", .{self.getFile(macho_file).fmtPath()});
929 try err.addNote("expected .mov instruction but found .{s}", .{@tagName(x)});
930 try err.addNote("while parsing {}", .{self.getFile(macho_file).fmtPath()});
918931 return error.RelaxFailUnexpectedInstruction;
919932 },
920933 }
......@@ -1142,7 +1155,7 @@ fn format2(
11421155 atom.out_n_sect, atom.alignment, atom.size,
11431156 atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk,
11441157 });
1145 if (!atom.flags.alive) try writer.writeAll(" : [*]");
1158 if (!atom.isAlive()) try writer.writeAll(" : [*]");
11461159 if (atom.getUnwindRecords(macho_file).len > 0) {
11471160 try writer.writeAll(" : unwind{ ");
11481161 const extra = atom.getExtra(macho_file);
......@@ -1158,14 +1171,6 @@ fn format2(
11581171
11591172pub const Index = u32;
11601173
1161pub const Flags = packed struct {
1162 /// Specifies whether this atom is alive or has been garbage collected.
1163 alive: bool = true,
1164
1165 /// Specifies if this atom has been visited during garbage collection.
1166 visited: bool = false,
1167};
1168
11691174pub const Extra = struct {
11701175 /// Index of the range extension thunk of this atom.
11711176 thunk: u32 = 0,
......@@ -1209,6 +1214,7 @@ const trace = @import("../../tracy.zig").trace;
12091214
12101215const Allocator = mem.Allocator;
12111216const Atom = @This();
1217const AtomicBool = std.atomic.Value(bool);
12121218const File = @import("file.zig").File;
12131219const MachO = @import("../MachO.zig");
12141220const Object = @import("Object.zig");
src/link/MachO/CodeSignature.zig+4
......@@ -7,6 +7,7 @@ const log = std.log.scoped(.link);
77const macho = std.macho;
88const mem = std.mem;
99const testing = std.testing;
10const trace = @import("../../tracy.zig").trace;
1011const Allocator = mem.Allocator;
1112const Hasher = @import("hasher.zig").ParallelHasher;
1213const MachO = @import("../MachO.zig");
......@@ -264,6 +265,9 @@ pub fn writeAdhocSignature(
264265 opts: WriteOpts,
265266 writer: anytype,
266267) !void {
268 const tracy = trace(@src());
269 defer tracy.end();
270
267271 const allocator = macho_file.base.comp.gpa;
268272
269273 var header: macho.SuperBlob = .{
src/link/MachO/Dylib.zig+36-29
......@@ -1,5 +1,9 @@
1/// Non-zero for fat dylibs
2offset: u64,
13path: []const u8,
24index: File.Index,
5file_handle: File.HandleIndex,
6tag: enum { dylib, tbd },
37
48exports: std.MultiArrayList(Export) = .{},
59strtab: std.ArrayListUnmanaged(u8) = .{},
......@@ -11,7 +15,7 @@ symbols_extra: std.ArrayListUnmanaged(u32) = .{},
1115globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},
1216dependents: std.ArrayListUnmanaged(Id) = .{},
1317rpaths: std.StringArrayHashMapUnmanaged(void) = .{},
14umbrella: File.Index = 0,
18umbrella: File.Index,
1519platform: ?MachO.Platform = null,
1620
1721needed: bool,
......@@ -23,16 +27,6 @@ referenced: bool = false,
2327
2428output_symtab_ctx: MachO.SymtabCtx = .{},
2529
26pub fn isDylib(path: []const u8, fat_arch: ?fat.Arch) !bool {
27 const file = try std.fs.cwd().openFile(path, .{});
28 defer file.close();
29 if (fat_arch) |arch| {
30 try file.seekTo(arch.offset);
31 }
32 const header = file.reader().readStruct(macho.mach_header_64) catch return false;
33 return header.filetype == macho.MH_DYLIB;
34}
35
3630pub fn deinit(self: *Dylib, allocator: Allocator) void {
3731 allocator.free(self.path);
3832 self.exports.deinit(allocator);
......@@ -51,12 +45,21 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {
5145 self.rpaths.deinit(allocator);
5246}
5347
54pub fn parse(self: *Dylib, macho_file: *MachO, file: std.fs.File, fat_arch: ?fat.Arch) !void {
48pub fn parse(self: *Dylib, macho_file: *MachO) !void {
49 switch (self.tag) {
50 .tbd => try self.parseTbd(macho_file),
51 .dylib => try self.parseBinary(macho_file),
52 }
53 try self.initSymbols(macho_file);
54}
55
56fn parseBinary(self: *Dylib, macho_file: *MachO) !void {
5557 const tracy = trace(@src());
5658 defer tracy.end();
5759
5860 const gpa = macho_file.base.comp.gpa;
59 const offset = if (fat_arch) |ar| ar.offset else 0;
61 const file = macho_file.getFileHandle(self.file_handle);
62 const offset = self.offset;
6063
6164 log.debug("parsing dylib from binary: {s}", .{self.path});
6265
......@@ -258,13 +261,7 @@ fn parseTrie(self: *Dylib, data: []const u8, macho_file: *MachO) !void {
258261 try self.parseTrieNode(&it, gpa, arena.allocator(), "");
259262}
260263
261pub fn parseTbd(
262 self: *Dylib,
263 cpu_arch: std.Target.Cpu.Arch,
264 platform: MachO.Platform,
265 lib_stub: LibStub,
266 macho_file: *MachO,
267) !void {
264fn parseTbd(self: *Dylib, macho_file: *MachO) !void {
268265 const tracy = trace(@src());
269266 defer tracy.end();
270267
......@@ -272,6 +269,12 @@ pub fn parseTbd(
272269
273270 log.debug("parsing dylib from stub: {s}", .{self.path});
274271
272 const file = macho_file.getFileHandle(self.file_handle);
273 var lib_stub = LibStub.loadFromFile(gpa, file) catch |err| {
274 try macho_file.reportParseError2(self.index, "failed to parse TBD file: {s}", .{@errorName(err)});
275 return error.MalformedTbd;
276 };
277 defer lib_stub.deinit();
275278 const umbrella_lib = lib_stub.inner[0];
276279
277280 {
......@@ -290,7 +293,8 @@ pub fn parseTbd(
290293
291294 log.debug(" (install_name '{s}')", .{umbrella_lib.installName()});
292295
293 self.platform = platform;
296 const cpu_arch = macho_file.getTarget().cpu.arch;
297 self.platform = macho_file.platform;
294298
295299 var matcher = try TargetMatcher.init(gpa, cpu_arch, self.platform.?.toApplePlatform());
296300 defer matcher.deinit();
......@@ -495,7 +499,7 @@ fn addObjCExport(
495499 try self.addExport(allocator, full_name, .{});
496500}
497501
498pub fn initSymbols(self: *Dylib, macho_file: *MachO) !void {
502fn initSymbols(self: *Dylib, macho_file: *MachO) !void {
499503 const gpa = macho_file.base.comp.gpa;
500504
501505 const nsyms = self.exports.items(.name).len;
......@@ -609,15 +613,18 @@ pub inline fn getUmbrella(self: Dylib, macho_file: *MachO) *Dylib {
609613 return macho_file.getFile(self.umbrella).?.dylib;
610614}
611615
612fn addString(self: *Dylib, allocator: Allocator, name: []const u8) !u32 {
616fn addString(self: *Dylib, allocator: Allocator, name: []const u8) !MachO.String {
613617 const off = @as(u32, @intCast(self.strtab.items.len));
614 try self.strtab.writer(allocator).print("{s}\x00", .{name});
615 return off;
618 try self.strtab.ensureUnusedCapacity(allocator, name.len + 1);
619 self.strtab.appendSliceAssumeCapacity(name);
620 self.strtab.appendAssumeCapacity(0);
621 return .{ .pos = off, .len = @intCast(name.len + 1) };
616622}
617623
618pub fn getString(self: Dylib, off: u32) [:0]const u8 {
619 assert(off < self.strtab.items.len);
620 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
624pub fn getString(self: Dylib, string: MachO.String) [:0]const u8 {
625 assert(string.pos < self.strtab.items.len and string.pos + string.len <= self.strtab.items.len);
626 if (string.len == 0) return "";
627 return self.strtab.items[string.pos..][0 .. string.len - 1 :0];
621628}
622629
623630pub fn asFile(self: *Dylib) File {
......@@ -931,7 +938,7 @@ pub const Id = struct {
931938};
932939
933940const Export = struct {
934 name: u32,
941 name: MachO.String,
935942 flags: Flags,
936943
937944 const Flags = packed struct {
src/link/MachO/InternalObject.zig+25-24
......@@ -53,7 +53,7 @@ pub fn init(self: *InternalObject, allocator: Allocator) !void {
5353
5454pub fn initSymbols(self: *InternalObject, macho_file: *MachO) !void {
5555 const newSymbolAssumeCapacity = struct {
56 fn newSymbolAssumeCapacity(obj: *InternalObject, name: u32, args: struct {
56 fn newSymbolAssumeCapacity(obj: *InternalObject, name: MachO.String, args: struct {
5757 type: u8 = macho.N_UNDF | macho.N_EXT,
5858 desc: u16 = 0,
5959 }) Symbol.Index {
......@@ -69,7 +69,7 @@ pub fn initSymbols(self: *InternalObject, macho_file: *MachO) !void {
6969 const nlist_idx: u32 = @intCast(obj.symtab.items.len);
7070 const nlist = obj.symtab.addOneAssumeCapacity();
7171 nlist.* = .{
72 .n_strx = name,
72 .n_strx = name.pos,
7373 .n_type = args.type,
7474 .n_sect = 0,
7575 .n_desc = args.desc,
......@@ -197,16 +197,16 @@ pub fn resolveBoundarySymbols(self: *InternalObject, macho_file: *MachO) !void {
197197 try self.globals.ensureUnusedCapacity(gpa, nsyms);
198198
199199 for (boundary_symbols.keys(), boundary_symbols.values()) |name, ref| {
200 const name_off = try self.addString(gpa, name);
200 const name_str = try self.addString(gpa, name);
201201 const sym_index = self.addSymbolAssumeCapacity();
202202 self.boundary_symbols.appendAssumeCapacity(sym_index);
203203 const sym = &self.symbols.items[sym_index];
204 sym.name = name_off;
204 sym.name = name_str;
205205 sym.visibility = .local;
206206 const nlist_idx: u32 = @intCast(self.symtab.items.len);
207207 const nlist = self.symtab.addOneAssumeCapacity();
208208 nlist.* = .{
209 .n_strx = name_off,
209 .n_strx = name_str.pos,
210210 .n_type = macho.N_SECT,
211211 .n_sect = 0,
212212 .n_desc = 0,
......@@ -273,7 +273,7 @@ fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_fil
273273 const nlist_idx: u32 = @intCast(self.symtab.items.len);
274274 const nlist = try self.symtab.addOne(gpa);
275275 nlist.* = .{
276 .n_strx = name_str,
276 .n_strx = name_str.pos,
277277 .n_type = macho.N_SECT,
278278 .n_sect = @intCast(n_sect + 1),
279279 .n_desc = 0,
......@@ -373,15 +373,15 @@ pub fn resolveObjcMsgSendSymbols(self: *InternalObject, macho_file: *MachO) !voi
373373 const name = MachO.eatPrefix(sym_name, "_objc_msgSend$").?;
374374 const selrefs_index = try self.addObjcMsgsendSections(name, macho_file);
375375
376 const name_off = try self.addString(gpa, sym_name);
376 const name_str = try self.addString(gpa, sym_name);
377377 const sym_index = try self.addSymbol(gpa);
378378 const sym = &self.symbols.items[sym_index];
379 sym.name = name_off;
379 sym.name = name_str;
380380 sym.visibility = .hidden;
381381 const nlist_idx: u32 = @intCast(self.symtab.items.len);
382382 const nlist = try self.symtab.addOne(gpa);
383383 nlist.* = .{
384 .n_strx = name_off,
384 .n_strx = name_str.pos,
385385 .n_type = macho.N_SECT | macho.N_EXT | macho.N_PEXT,
386386 .n_sect = 0,
387387 .n_desc = 0,
......@@ -389,7 +389,7 @@ pub fn resolveObjcMsgSendSymbols(self: *InternalObject, macho_file: *MachO) !voi
389389 };
390390 sym.nlist_idx = nlist_idx;
391391 sym.extra = try self.addSymbolExtra(gpa, .{ .objc_selrefs = selrefs_index });
392 sym.flags.objc_stubs = true;
392 sym.setSectionFlags(.{ .objc_stubs = true });
393393
394394 const idx = ref.getFile(macho_file).?.object.globals.items[ref.index];
395395 try self.globals.append(gpa, idx);
......@@ -427,7 +427,7 @@ pub fn resolveLiterals(self: *InternalObject, lp: *MachO.LiteralPool, macho_file
427427 const lp_sym = lp.getSymbol(res.index, macho_file);
428428 const lp_atom = lp_sym.getAtom(macho_file).?;
429429 lp_atom.alignment = lp_atom.alignment.max(atom.alignment);
430 atom.flags.alive = false;
430 atom.setAlive(false);
431431 }
432432 atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
433433 }
......@@ -439,7 +439,7 @@ pub fn dedupLiterals(self: *InternalObject, lp: MachO.LiteralPool, macho_file: *
439439
440440 for (self.getAtoms()) |atom_index| {
441441 const atom = self.getAtom(atom_index) orelse continue;
442 if (!atom.flags.alive) continue;
442 if (!atom.isAlive()) continue;
443443
444444 const relocs = blk: {
445445 const extra = atom.getExtra(macho_file);
......@@ -464,7 +464,7 @@ pub fn dedupLiterals(self: *InternalObject, lp: MachO.LiteralPool, macho_file: *
464464 }
465465
466466 for (self.symbols.items) |*sym| {
467 if (!sym.flags.objc_stubs) continue;
467 if (!sym.getSectionFlags().objc_stubs) continue;
468468 const extra = sym.getExtra(macho_file);
469469 const file = sym.getFile(macho_file).?;
470470 if (file.getIndex() != self.index) continue;
......@@ -490,20 +490,20 @@ pub fn scanRelocs(self: *InternalObject, macho_file: *MachO) void {
490490 if (self.getEntryRef(macho_file)) |ref| {
491491 if (ref.getFile(macho_file) != null) {
492492 const sym = ref.getSymbol(macho_file).?;
493 if (sym.flags.import) sym.flags.stubs = true;
493 if (sym.flags.import) sym.setSectionFlags(.{ .stubs = true });
494494 }
495495 }
496496 if (self.getDyldStubBinderRef(macho_file)) |ref| {
497497 if (ref.getFile(macho_file) != null) {
498498 const sym = ref.getSymbol(macho_file).?;
499 sym.flags.needs_got = true;
499 sym.setSectionFlags(.{ .needs_got = true });
500500 }
501501 }
502502 if (self.getObjcMsgSendRef(macho_file)) |ref| {
503503 if (ref.getFile(macho_file) != null) {
504504 const sym = ref.getSymbol(macho_file).?;
505505 // TODO is it always needed, or only if we are synthesising fast stubs
506 sym.flags.needs_got = true;
506 sym.setSectionFlags(.{ .needs_got = true });
507507 }
508508 }
509509}
......@@ -570,7 +570,7 @@ pub fn writeAtoms(self: *InternalObject, macho_file: *MachO) !void {
570570
571571 for (self.getAtoms()) |atom_index| {
572572 const atom = self.getAtom(atom_index) orelse continue;
573 if (!atom.flags.alive) continue;
573 if (!atom.isAlive()) continue;
574574 const sect = atom.getInputSection(macho_file);
575575 if (sect.isZerofill()) continue;
576576 const off = std.math.cast(usize, atom.value) orelse return error.Overflow;
......@@ -624,17 +624,18 @@ fn getSectionData(self: *const InternalObject, index: u32) error{Overflow}![]con
624624 @panic("ref to non-existent section");
625625}
626626
627pub fn addString(self: *InternalObject, allocator: Allocator, name: []const u8) !u32 {
627pub fn addString(self: *InternalObject, allocator: Allocator, string: []const u8) !MachO.String {
628628 const off: u32 = @intCast(self.strtab.items.len);
629 try self.strtab.ensureUnusedCapacity(allocator, name.len + 1);
630 self.strtab.appendSliceAssumeCapacity(name);
629 try self.strtab.ensureUnusedCapacity(allocator, string.len + 1);
630 self.strtab.appendSliceAssumeCapacity(string);
631631 self.strtab.appendAssumeCapacity(0);
632 return off;
632 return .{ .pos = off, .len = @intCast(string.len + 1) };
633633}
634634
635pub fn getString(self: InternalObject, off: u32) [:0]const u8 {
636 assert(off < self.strtab.items.len);
637 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
635pub fn getString(self: InternalObject, string: MachO.String) [:0]const u8 {
636 assert(string.pos < self.strtab.items.len and string.pos + string.len <= self.strtab.items.len);
637 if (string.len == 0) return "";
638 return self.strtab.items[string.pos..][0 .. string.len - 1 :0];
638639}
639640
640641pub fn asFile(self: *InternalObject) File {
src/link/MachO/Object.zig+46-44
......@@ -38,13 +38,6 @@ compact_unwind_ctx: CompactUnwindCtx = .{},
3838output_symtab_ctx: MachO.SymtabCtx = .{},
3939output_ar_state: Archive.ArState = .{},
4040
41pub fn isObject(path: []const u8) !bool {
42 const file = try std.fs.cwd().openFile(path, .{});
43 defer file.close();
44 const header = file.reader().readStruct(macho.mach_header_64) catch return false;
45 return header.filetype == macho.MH_OBJECT;
46}
47
4841pub fn deinit(self: *Object, allocator: Allocator) void {
4942 if (self.in_archive) |*ar| allocator.free(ar.path);
5043 allocator.free(self.path);
......@@ -185,7 +178,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
185178
186179 fn rank(ctx: *const Object, nl: macho.nlist_64) u8 {
187180 if (!nl.ext()) {
188 const name = ctx.getString(nl.n_strx);
181 const name = ctx.getNStrx(nl.n_strx);
189182 if (name.len == 0) return 5;
190183 if (name[0] == 'l' or name[0] == 'L') return 4;
191184 return 3;
......@@ -270,9 +263,12 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
270263 mem.eql(u8, isec.sectName(), "__compact_unwind") or
271264 isec.attrs() & macho.S_ATTR_DEBUG != 0)
272265 {
273 atom.flags.alive = false;
266 atom.setAlive(false);
274267 }
275268 }
269
270 // Finally, we do a post-parse check for -ObjC to see if we need to force load this member anyhow.
271 self.alive = self.alive or (macho_file.force_load_objc and self.hasObjC());
276272}
277273
278274pub fn isCstringLiteral(sect: macho.section_64) bool {
......@@ -345,7 +341,7 @@ fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void {
345341 else
346342 sect.@"align";
347343 const atom_index = try self.addAtom(allocator, .{
348 .name = nlist.nlist.n_strx,
344 .name = .{ .pos = nlist.nlist.n_strx, .len = @intCast(self.getNStrx(nlist.nlist.n_strx).len + 1) },
349345 .n_sect = @intCast(n_sect),
350346 .off = nlist.nlist.n_value - sect.addr,
351347 .size = size,
......@@ -469,7 +465,7 @@ fn initCstringLiterals(self: *Object, allocator: Allocator, file: File.Handle, m
469465 const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
470466 self.symtab.set(nlist_index, .{
471467 .nlist = .{
472 .n_strx = name_str,
468 .n_strx = name_str.pos,
473469 .n_type = macho.N_SECT,
474470 .n_sect = @intCast(atom.n_sect + 1),
475471 .n_desc = 0,
......@@ -536,7 +532,7 @@ fn initFixedSizeLiterals(self: *Object, allocator: Allocator, macho_file: *MachO
536532 const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
537533 self.symtab.set(nlist_index, .{
538534 .nlist = .{
539 .n_strx = name_str,
535 .n_strx = name_str.pos,
540536 .n_type = macho.N_SECT,
541537 .n_sect = @intCast(atom.n_sect + 1),
542538 .n_desc = 0,
......@@ -594,7 +590,7 @@ fn initPointerLiterals(self: *Object, allocator: Allocator, macho_file: *MachO)
594590 const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
595591 self.symtab.set(nlist_index, .{
596592 .nlist = .{
597 .n_strx = name_str,
593 .n_strx = name_str.pos,
598594 .n_type = macho.N_SECT,
599595 .n_sect = @intCast(atom.n_sect + 1),
600596 .n_desc = 0,
......@@ -649,7 +645,7 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO
649645 const lp_sym = lp.getSymbol(res.index, macho_file);
650646 const lp_atom = lp_sym.getAtom(macho_file).?;
651647 lp_atom.alignment = lp_atom.alignment.max(atom.alignment);
652 atom.flags.alive = false;
648 atom.setAlive(false);
653649 }
654650 atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
655651 }
......@@ -687,7 +683,7 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO
687683 const lp_sym = lp.getSymbol(res.index, macho_file);
688684 const lp_atom = lp_sym.getAtom(macho_file).?;
689685 lp_atom.alignment = lp_atom.alignment.max(atom.alignment);
690 atom.flags.alive = false;
686 atom.setAlive(false);
691687 }
692688 atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
693689 }
......@@ -701,7 +697,7 @@ pub fn dedupLiterals(self: *Object, lp: MachO.LiteralPool, macho_file: *MachO) v
701697
702698 for (self.getAtoms()) |atom_index| {
703699 const atom = self.getAtom(atom_index) orelse continue;
704 if (!atom.flags.alive) continue;
700 if (!atom.isAlive()) continue;
705701
706702 const relocs = blk: {
707703 const extra = atom.getExtra(macho_file);
......@@ -800,7 +796,7 @@ fn linkNlistToAtom(self: *Object, macho_file: *MachO) !void {
800796 atom.* = atom_index;
801797 } else {
802798 try macho_file.reportParseError2(self.index, "symbol {s} not attached to any (sub)section", .{
803 self.getString(nlist.n_strx),
799 self.getNStrx(nlist.n_strx),
804800 });
805801 return error.MalformedObject;
806802 }
......@@ -825,7 +821,7 @@ fn initSymbols(self: *Object, allocator: Allocator, macho_file: *MachO) !void {
825821 const index = self.addSymbolAssumeCapacity();
826822 const symbol = &self.symbols.items[index];
827823 symbol.value = nlist.n_value;
828 symbol.name = nlist.n_strx;
824 symbol.name = .{ .pos = nlist.n_strx, .len = @intCast(self.getNStrx(nlist.n_strx).len + 1) };
829825 symbol.nlist_idx = @intCast(i);
830826 symbol.extra = self.addSymbolExtraAssumeCapacity(.{});
831827
......@@ -898,7 +894,7 @@ fn initSymbolStabs(self: *Object, allocator: Allocator, nlists: anytype, macho_f
898894 defer addr_lookup.deinit();
899895 for (syms) |sym| {
900896 if (sym.sect() and (sym.ext() or sym.pext())) {
901 try addr_lookup.putNoClobber(self.getString(sym.n_strx), sym.n_value);
897 try addr_lookup.putNoClobber(self.getNStrx(sym.n_strx), sym.n_value);
902898 }
903899 }
904900
......@@ -930,7 +926,7 @@ fn initSymbolStabs(self: *Object, allocator: Allocator, nlists: anytype, macho_f
930926 },
931927 macho.N_GSYM => {
932928 stab.is_func = false;
933 stab.index = sym_lookup.find(addr_lookup.get(self.getString(nlist.n_strx)).?);
929 stab.index = sym_lookup.find(addr_lookup.get(self.getNStrx(nlist.n_strx)).?);
934930 },
935931 macho.N_STSYM => {
936932 stab.is_func = false;
......@@ -994,7 +990,7 @@ fn initRelocs(self: *Object, file: File.Handle, cpu_arch: std.Target.Cpu.Arch, m
994990 var next_reloc: u32 = 0;
995991 for (subsections.items) |subsection| {
996992 const atom = self.getAtom(subsection.atom).?;
997 if (!atom.flags.alive) continue;
993 if (!atom.isAlive()) continue;
998994 if (next_reloc >= relocs.items.len) break;
999995 const end_addr = atom.off + atom.size;
1000996 const rel_index = next_reloc;
......@@ -1487,7 +1483,7 @@ pub fn resolveSymbols(self: *Object, macho_file: *MachO) !void {
14871483 if (!nlist.ext()) continue;
14881484 if (nlist.sect()) {
14891485 const atom = self.getAtom(atom_index).?;
1490 if (!atom.flags.alive) continue;
1486 if (!atom.isAlive()) continue;
14911487 }
14921488
14931489 const gop = try macho_file.resolver.getOrPut(gpa, .{
......@@ -1556,7 +1552,7 @@ pub fn scanRelocs(self: *Object, macho_file: *MachO) !void {
15561552
15571553 for (self.getAtoms()) |atom_index| {
15581554 const atom = self.getAtom(atom_index) orelse continue;
1559 if (!atom.flags.alive) continue;
1555 if (!atom.isAlive()) continue;
15601556 const sect = atom.getInputSection(macho_file);
15611557 if (sect.isZerofill()) continue;
15621558 try atom.scanRelocs(macho_file);
......@@ -1567,10 +1563,10 @@ pub fn scanRelocs(self: *Object, macho_file: *MachO) !void {
15671563 if (!rec.alive) continue;
15681564 if (rec.getFde(macho_file)) |fde| {
15691565 if (fde.getCie(macho_file).getPersonality(macho_file)) |sym| {
1570 sym.flags.needs_got = true;
1566 sym.setSectionFlags(.{ .needs_got = true });
15711567 }
15721568 } else if (rec.getPersonality(macho_file)) |sym| {
1573 sym.flags.needs_got = true;
1569 sym.setSectionFlags(.{ .needs_got = true });
15741570 }
15751571 }
15761572}
......@@ -1712,7 +1708,7 @@ pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, macho_file: *M
17121708 const gpa = macho_file.base.comp.gpa;
17131709 for (self.symtab.items(.nlist)) |nlist| {
17141710 if (!nlist.ext() or (nlist.undf() and !nlist.tentative())) continue;
1715 const off = try ar_symtab.strtab.insert(gpa, self.getString(nlist.n_strx));
1711 const off = try ar_symtab.strtab.insert(gpa, self.getNStrx(nlist.n_strx));
17161712 try ar_symtab.entries.append(gpa, .{ .off = off, .file = self.index });
17171713 }
17181714}
......@@ -1749,7 +1745,7 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void {
17491745 const ref = self.getSymbolRef(@intCast(i), macho_file);
17501746 const file = ref.getFile(macho_file) orelse continue;
17511747 if (file.getIndex() != self.index) continue;
1752 if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;
1748 if (sym.getAtom(macho_file)) |atom| if (!atom.isAlive()) continue;
17531749 if (sym.isSymbolStab(macho_file)) continue;
17541750 const name = sym.getName(macho_file);
17551751 if (name.len == 0) continue;
......@@ -1858,7 +1854,7 @@ pub fn writeAtoms(self: *Object, macho_file: *MachO) !void {
18581854 }
18591855 for (self.getAtoms()) |atom_index| {
18601856 const atom = self.getAtom(atom_index) orelse continue;
1861 if (!atom.flags.alive) continue;
1857 if (!atom.isAlive()) continue;
18621858 const sect = atom.getInputSection(macho_file);
18631859 if (sect.isZerofill()) continue;
18641860 const value = math.cast(usize, atom.value) orelse return error.Overflow;
......@@ -1897,7 +1893,7 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void {
18971893 }
18981894 for (self.getAtoms()) |atom_index| {
18991895 const atom = self.getAtom(atom_index) orelse continue;
1900 if (!atom.flags.alive) continue;
1896 if (!atom.isAlive()) continue;
19011897 const sect = atom.getInputSection(macho_file);
19021898 if (sect.isZerofill()) continue;
19031899 const value = math.cast(usize, atom.value) orelse return error.Overflow;
......@@ -2296,17 +2292,23 @@ pub fn getAtomRelocs(self: *const Object, atom: Atom, macho_file: *MachO) []cons
22962292 return relocs.items[extra.rel_index..][0..extra.rel_count];
22972293}
22982294
2299fn addString(self: *Object, allocator: Allocator, name: [:0]const u8) error{OutOfMemory}!u32 {
2295fn addString(self: *Object, allocator: Allocator, string: [:0]const u8) error{OutOfMemory}!MachO.String {
23002296 const off: u32 = @intCast(self.strtab.items.len);
2301 try self.strtab.ensureUnusedCapacity(allocator, name.len + 1);
2302 self.strtab.appendSliceAssumeCapacity(name);
2297 try self.strtab.ensureUnusedCapacity(allocator, string.len + 1);
2298 self.strtab.appendSliceAssumeCapacity(string);
23032299 self.strtab.appendAssumeCapacity(0);
2304 return off;
2300 return .{ .pos = off, .len = @intCast(string.len + 1) };
23052301}
23062302
2307pub fn getString(self: Object, off: u32) [:0]const u8 {
2308 assert(off < self.strtab.items.len);
2309 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
2303pub fn getString(self: Object, string: MachO.String) [:0]const u8 {
2304 assert(string.pos < self.strtab.items.len and string.pos + string.len <= self.strtab.items.len);
2305 if (string.len == 0) return "";
2306 return self.strtab.items[string.pos..][0 .. string.len - 1 :0];
2307}
2308
2309fn getNStrx(self: Object, n_strx: u32) [:0]const u8 {
2310 assert(n_strx < self.strtab.items.len);
2311 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + n_strx)), 0);
23102312}
23112313
23122314pub fn hasUnwindRecords(self: Object) bool {
......@@ -2325,9 +2327,9 @@ fn hasSymbolStabs(self: Object) bool {
23252327 return self.stab_files.items.len > 0;
23262328}
23272329
2328pub fn hasObjc(self: Object) bool {
2330fn hasObjC(self: Object) bool {
23292331 for (self.symtab.items(.nlist)) |nlist| {
2330 const name = self.getString(nlist.n_strx);
2332 const name = self.getNStrx(nlist.n_strx);
23312333 if (mem.startsWith(u8, name, "_OBJC_CLASS_$_")) return true;
23322334 }
23332335 for (self.sections.items(.header)) |sect| {
......@@ -2350,7 +2352,7 @@ pub fn asFile(self: *Object) File {
23502352}
23512353
23522354const AddAtomArgs = struct {
2353 name: u32,
2355 name: MachO.String,
23542356 n_sect: u8,
23552357 off: u64,
23562358 size: u64,
......@@ -2694,17 +2696,17 @@ const StabFile = struct {
26942696
26952697 fn getCompDir(sf: StabFile, object: Object) [:0]const u8 {
26962698 const nlist = object.symtab.items(.nlist)[sf.comp_dir];
2697 return object.getString(nlist.n_strx);
2699 return object.getNStrx(nlist.n_strx);
26982700 }
26992701
27002702 fn getTuName(sf: StabFile, object: Object) [:0]const u8 {
27012703 const nlist = object.symtab.items(.nlist)[sf.comp_dir + 1];
2702 return object.getString(nlist.n_strx);
2704 return object.getNStrx(nlist.n_strx);
27032705 }
27042706
27052707 fn getOsoPath(sf: StabFile, object: Object) [:0]const u8 {
27062708 const nlist = object.symtab.items(.nlist)[sf.comp_dir + 2];
2707 return object.getString(nlist.n_strx);
2709 return object.getNStrx(nlist.n_strx);
27082710 }
27092711
27102712 fn getOsoModTime(sf: StabFile, object: Object) u64 {
......@@ -2762,8 +2764,8 @@ const StabFile = struct {
27622764};
27632765
27642766const CompileUnit = struct {
2765 comp_dir: u32,
2766 tu_name: u32,
2767 comp_dir: MachO.String,
2768 tu_name: MachO.String,
27672769
27682770 fn getCompDir(cu: CompileUnit, object: Object) [:0]const u8 {
27692771 return object.getString(cu.comp_dir);
src/link/MachO/Symbol.zig+25-12
......@@ -4,7 +4,7 @@
44value: u64 = 0,
55
66/// Offset into the linker's intern table.
7name: u32 = 0,
7name: MachO.String = .{},
88
99/// File where this symbol is defined.
1010file: File.Index = 0,
......@@ -23,6 +23,8 @@ nlist_idx: u32 = 0,
2323/// Misc flags for the symbol packaged as packed struct for compression.
2424flags: Flags = .{},
2525
26sect_flags: std.atomic.Value(u8) = std.atomic.Value(u8).init(0),
27
2628visibility: Visibility = .local,
2729
2830extra: u32 = 0,
......@@ -55,7 +57,6 @@ pub fn weakRef(symbol: Symbol, macho_file: *MachO) bool {
5557
5658pub fn getName(symbol: Symbol, macho_file: *MachO) [:0]const u8 {
5759 return switch (symbol.getFile(macho_file).?) {
58 .zig_object => |x| x.strtab.getAssumeExists(symbol.name),
5960 inline else => |x| x.getString(symbol.name),
6061 };
6162}
......@@ -69,6 +70,14 @@ pub fn getOutputSectionIndex(symbol: Symbol, macho_file: *MachO) u8 {
6970 return symbol.out_n_sect;
7071}
7172
73pub fn getSectionFlags(symbol: Symbol) SectionFlags {
74 return @bitCast(symbol.sect_flags.load(.seq_cst));
75}
76
77pub fn setSectionFlags(symbol: *Symbol, flags: SectionFlags) void {
78 _ = symbol.sect_flags.fetchOr(@bitCast(flags), .seq_cst);
79}
80
7281pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File {
7382 return macho_file.getFile(symbol.file);
7483}
......@@ -116,9 +125,9 @@ pub fn getAddress(symbol: Symbol, opts: struct {
116125 stubs: bool = true,
117126}, macho_file: *MachO) u64 {
118127 if (opts.stubs) {
119 if (symbol.flags.stubs) {
128 if (symbol.getSectionFlags().stubs) {
120129 return symbol.getStubsAddress(macho_file);
121 } else if (symbol.flags.objc_stubs) {
130 } else if (symbol.getSectionFlags().objc_stubs) {
122131 return symbol.getObjcStubsAddress(macho_file);
123132 }
124133 }
......@@ -127,25 +136,25 @@ pub fn getAddress(symbol: Symbol, opts: struct {
127136}
128137
129138pub fn getGotAddress(symbol: Symbol, macho_file: *MachO) u64 {
130 if (!symbol.flags.has_got) return 0;
139 if (!symbol.getSectionFlags().has_got) return 0;
131140 const extra = symbol.getExtra(macho_file);
132141 return macho_file.got.getAddress(extra.got, macho_file);
133142}
134143
135144pub fn getStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
136 if (!symbol.flags.stubs) return 0;
145 if (!symbol.getSectionFlags().stubs) return 0;
137146 const extra = symbol.getExtra(macho_file);
138147 return macho_file.stubs.getAddress(extra.stubs, macho_file);
139148}
140149
141150pub fn getObjcStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
142 if (!symbol.flags.objc_stubs) return 0;
151 if (!symbol.getSectionFlags().objc_stubs) return 0;
143152 const extra = symbol.getExtra(macho_file);
144153 return macho_file.objc_stubs.getAddress(extra.objc_stubs, macho_file);
145154}
146155
147156pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 {
148 if (!symbol.flags.objc_stubs) return 0;
157 if (!symbol.getSectionFlags().objc_stubs) return 0;
149158 const extra = symbol.getExtra(macho_file);
150159 const file = symbol.getFile(macho_file).?;
151160 return switch (file) {
......@@ -155,7 +164,7 @@ pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 {
155164}
156165
157166pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 {
158 if (!symbol.flags.tlv_ptr) return 0;
167 if (!symbol.getSectionFlags().tlv_ptr) return 0;
159168 const extra = symbol.getExtra(macho_file);
160169 return macho_file.tlv_ptr.getAddress(extra.tlv_ptr, macho_file);
161170}
......@@ -167,14 +176,14 @@ const GetOrCreateZigGotEntryResult = struct {
167176
168177pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, macho_file: *MachO) !GetOrCreateZigGotEntryResult {
169178 assert(!macho_file.base.isRelocatable());
170 assert(symbol.flags.needs_zig_got);
171 if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).zig_got };
179 assert(symbol.getSectionFlags().needs_zig_got);
180 if (symbol.getSectionFlags().has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).zig_got };
172181 const index = try macho_file.zig_got.addSymbol(symbol_index, macho_file);
173182 return .{ .found_existing = false, .index = index };
174183}
175184
176185pub fn getZigGotAddress(symbol: Symbol, macho_file: *MachO) u64 {
177 if (!symbol.flags.has_zig_got) return 0;
186 if (!symbol.getSectionFlags().has_zig_got) return 0;
178187 const extras = symbol.getExtra(macho_file);
179188 return macho_file.zig_got.entryAddress(extras.zig_got, macho_file);
180189}
......@@ -384,7 +393,9 @@ pub const Flags = packed struct {
384393
385394 /// Whether the symbol makes into the output symtab or not.
386395 output_symtab: bool = false,
396};
387397
398pub const SectionFlags = packed struct(u8) {
388399 /// Whether the symbol contains __got indirection.
389400 needs_got: bool = false,
390401 has_got: bool = false,
......@@ -401,6 +412,8 @@ pub const Flags = packed struct {
401412
402413 /// Whether the symbol contains __objc_stubs indirection.
403414 objc_stubs: bool = false,
415
416 _: u1 = 0,
404417};
405418
406419pub const Visibility = enum {
src/link/MachO/UnwindInfo.zig+1-1
......@@ -53,7 +53,7 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
5353 for (macho_file.sections.items(.atoms)) |atoms| {
5454 for (atoms.items) |ref| {
5555 const atom = ref.getAtom(macho_file) orelse continue;
56 if (!atom.flags.alive) continue;
56 if (!atom.isAlive()) continue;
5757 const recs = atom.getUnwindRecords(macho_file);
5858 const file = atom.getFile(macho_file);
5959 try info.records.ensureUnusedCapacity(gpa, recs.len);
src/link/MachO/ZigObject.zig+51-41
......@@ -141,7 +141,7 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {
141141 }
142142}
143143
144fn newSymbol(self: *ZigObject, allocator: Allocator, name: u32, args: struct {
144fn newSymbol(self: *ZigObject, allocator: Allocator, name: MachO.String, args: struct {
145145 type: u8 = macho.N_UNDF | macho.N_EXT,
146146 desc: u16 = 0,
147147}) !Symbol.Index {
......@@ -158,7 +158,7 @@ fn newSymbol(self: *ZigObject, allocator: Allocator, name: u32, args: struct {
158158 const nlist_idx: u32 = @intCast(self.symtab.addOneAssumeCapacity());
159159 self.symtab.set(nlist_idx, .{
160160 .nlist = .{
161 .n_strx = name,
161 .n_strx = name.pos,
162162 .n_type = args.type,
163163 .n_sect = 0,
164164 .n_desc = args.desc,
......@@ -174,7 +174,7 @@ fn newSymbol(self: *ZigObject, allocator: Allocator, name: u32, args: struct {
174174 return index;
175175}
176176
177fn newAtom(self: *ZigObject, allocator: Allocator, name: u32, macho_file: *MachO) !Atom.Index {
177fn newAtom(self: *ZigObject, allocator: Allocator, name: MachO.String, macho_file: *MachO) !Atom.Index {
178178 try self.atoms.ensureUnusedCapacity(allocator, 1);
179179 try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra));
180180 try self.atoms_indexes.ensureUnusedCapacity(allocator, 1);
......@@ -192,7 +192,7 @@ fn newAtom(self: *ZigObject, allocator: Allocator, name: u32, macho_file: *MachO
192192 return index;
193193}
194194
195fn newSymbolWithAtom(self: *ZigObject, allocator: Allocator, name: u32, macho_file: *MachO) !Symbol.Index {
195fn newSymbolWithAtom(self: *ZigObject, allocator: Allocator, name: MachO.String, macho_file: *MachO) !Symbol.Index {
196196 const atom_index = try self.newAtom(allocator, name, macho_file);
197197 const sym_index = try self.newSymbol(allocator, name, .{ .type = macho.N_SECT });
198198 const sym = &self.symbols.items[sym_index];
......@@ -245,7 +245,7 @@ pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) !void {
245245 if (!nlist.ext()) continue;
246246 if (nlist.sect()) {
247247 const atom = self.getAtom(atom_index).?;
248 if (!atom.flags.alive) continue;
248 if (!atom.isAlive()) continue;
249249 }
250250
251251 const gop = try macho_file.resolver.getOrPut(gpa, .{
......@@ -391,7 +391,7 @@ pub fn claimUnresolved(self: *ZigObject, macho_file: *MachO) void {
391391pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void {
392392 for (self.getAtoms()) |atom_index| {
393393 const atom = self.getAtom(atom_index) orelse continue;
394 if (!atom.flags.alive) continue;
394 if (!atom.isAlive()) continue;
395395 const sect = atom.getInputSection(macho_file);
396396 if (sect.isZerofill()) continue;
397397 try atom.scanRelocs(macho_file);
......@@ -403,7 +403,7 @@ pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void {
403403 var has_error = false;
404404 for (self.getAtoms()) |atom_index| {
405405 const atom = self.getAtom(atom_index) orelse continue;
406 if (!atom.flags.alive) continue;
406 if (!atom.isAlive()) continue;
407407 const sect = &macho_file.sections.items(.header)[atom.out_n_sect];
408408 if (sect.isZerofill()) continue;
409409 if (!macho_file.isZigSection(atom.out_n_sect)) continue; // Non-Zig sections are handled separately
......@@ -450,7 +450,7 @@ pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void {
450450pub fn calcNumRelocs(self: *ZigObject, macho_file: *MachO) void {
451451 for (self.getAtoms()) |atom_index| {
452452 const atom = self.getAtom(atom_index) orelse continue;
453 if (!atom.flags.alive) continue;
453 if (!atom.isAlive()) continue;
454454 const header = &macho_file.sections.items(.header)[atom.out_n_sect];
455455 if (header.isZerofill()) continue;
456456 if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
......@@ -465,7 +465,7 @@ pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) !void {
465465
466466 for (self.getAtoms()) |atom_index| {
467467 const atom = self.getAtom(atom_index) orelse continue;
468 if (!atom.flags.alive) continue;
468 if (!atom.isAlive()) continue;
469469 const header = macho_file.sections.items(.header)[atom.out_n_sect];
470470 const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;
471471 if (header.isZerofill()) continue;
......@@ -505,7 +505,7 @@ pub fn writeAtomsRelocatable(self: *ZigObject, macho_file: *MachO) !void {
505505
506506 for (self.getAtoms()) |atom_index| {
507507 const atom = self.getAtom(atom_index) orelse continue;
508 if (!atom.flags.alive) continue;
508 if (!atom.isAlive()) continue;
509509 const sect = atom.getInputSection(macho_file);
510510 if (sect.isZerofill()) continue;
511511 if (macho_file.isZigSection(atom.out_n_sect)) continue;
......@@ -529,7 +529,7 @@ pub fn writeAtoms(self: *ZigObject, macho_file: *MachO) !void {
529529
530530 for (self.getAtoms()) |atom_index| {
531531 const atom = self.getAtom(atom_index) orelse continue;
532 if (!atom.flags.alive) continue;
532 if (!atom.isAlive()) continue;
533533 const sect = atom.getInputSection(macho_file);
534534 if (sect.isZerofill()) continue;
535535 if (macho_file.isZigSection(atom.out_n_sect)) continue;
......@@ -549,7 +549,7 @@ pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) void {
549549 const ref = self.getSymbolRef(@intCast(i), macho_file);
550550 const file = ref.getFile(macho_file) orelse continue;
551551 if (file.getIndex() != self.index) continue;
552 if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;
552 if (sym.getAtom(macho_file)) |atom| if (!atom.isAlive()) continue;
553553 sym.flags.output_symtab = true;
554554 if (sym.isLocal()) {
555555 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
......@@ -914,7 +914,7 @@ pub fn updateDecl(
914914 const lib_name = variable.lib_name.toSlice(&mod.intern_pool);
915915 const index = try self.getGlobalSymbol(macho_file, name, lib_name);
916916 const sym = &self.symbols.items[index];
917 sym.flags.needs_got = true;
917 sym.setSectionFlags(.{ .needs_got = true });
918918 return;
919919 }
920920
......@@ -992,10 +992,10 @@ fn updateDeclCode(
992992
993993 const sym_name = try std.fmt.allocPrintZ(gpa, "_{s}", .{decl.fqn.toSlice(ip)});
994994 defer gpa.free(sym_name);
995 sym.name = try self.strtab.insert(gpa, sym_name);
996 atom.flags.alive = true;
995 sym.name = try self.addString(gpa, sym_name);
996 atom.setAlive(true);
997997 atom.name = sym.name;
998 nlist.n_strx = sym.name;
998 nlist.n_strx = sym.name.pos;
999999 nlist.n_type = macho.N_SECT;
10001000 nlist.n_sect = sect_index + 1;
10011001 self.symtab.items(.size)[sym.nlist_idx] = code.len;
......@@ -1018,7 +1018,7 @@ fn updateDeclCode(
10181018
10191019 if (!macho_file.base.isRelocatable()) {
10201020 log.debug(" (updating offset table entry)", .{});
1021 assert(sym.flags.has_zig_got);
1021 assert(sym.getSectionFlags().has_zig_got);
10221022 const extra = sym.getExtra(macho_file);
10231023 try macho_file.zig_got.writeOne(macho_file, extra.zig_got);
10241024 }
......@@ -1034,7 +1034,7 @@ fn updateDeclCode(
10341034 errdefer self.freeDeclMetadata(macho_file, sym_index);
10351035
10361036 sym.value = 0;
1037 sym.flags.needs_zig_got = true;
1037 sym.setSectionFlags(.{ .needs_zig_got = true });
10381038 nlist.n_value = 0;
10391039
10401040 if (!macho_file.base.isRelocatable()) {
......@@ -1090,15 +1090,15 @@ fn createTlvInitializer(
10901090 const gpa = macho_file.base.comp.gpa;
10911091 const sym_name = try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{name});
10921092 defer gpa.free(sym_name);
1093 const off = try self.strtab.insert(gpa, sym_name);
1093 const string = try self.addString(gpa, sym_name);
10941094
1095 const sym_index = try self.newSymbolWithAtom(gpa, off, macho_file);
1095 const sym_index = try self.newSymbolWithAtom(gpa, string, macho_file);
10961096 const sym = &self.symbols.items[sym_index];
10971097 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
10981098 const atom = sym.getAtom(macho_file).?;
10991099 sym.out_n_sect = sect_index;
11001100 atom.out_n_sect = sect_index;
1101 atom.flags.alive = true;
1101 atom.setAlive(true);
11021102 atom.alignment = alignment;
11031103 atom.size = code.len;
11041104 nlist.n_sect = sect_index + 1;
......@@ -1142,10 +1142,10 @@ fn createTlvDescriptor(
11421142 atom.out_n_sect = sect_index;
11431143
11441144 sym.value = 0;
1145 sym.name = try self.strtab.insert(gpa, name);
1146 atom.flags.alive = true;
1145 sym.name = try self.addString(gpa, name);
1146 atom.setAlive(true);
11471147 atom.name = sym.name;
1148 nlist.n_strx = sym.name;
1148 nlist.n_strx = sym.name.pos;
11491149 nlist.n_sect = sect_index + 1;
11501150 nlist.n_type = macho.N_SECT;
11511151 nlist.n_value = 0;
......@@ -1296,8 +1296,8 @@ fn lowerConst(
12961296 var code_buffer = std.ArrayList(u8).init(gpa);
12971297 defer code_buffer.deinit();
12981298
1299 const name_str_index = try self.strtab.insert(gpa, name);
1300 const sym_index = try self.newSymbolWithAtom(gpa, name_str_index, macho_file);
1299 const name_str = try self.addString(gpa, name);
1300 const sym_index = try self.newSymbolWithAtom(gpa, name_str, macho_file);
13011301
13021302 const res = try codegen.generateSymbol(&macho_file.base, pt, src_loc, val, &code_buffer, .{
13031303 .none = {},
......@@ -1317,7 +1317,7 @@ fn lowerConst(
13171317 self.symtab.items(.size)[sym.nlist_idx] = code.len;
13181318
13191319 const atom = sym.getAtom(macho_file).?;
1320 atom.flags.alive = true;
1320 atom.setAlive(true);
13211321 atom.alignment = required_alignment;
13221322 atom.size = code.len;
13231323 atom.out_n_sect = output_section_index;
......@@ -1447,13 +1447,13 @@ fn updateLazySymbol(
14471447 var code_buffer = std.ArrayList(u8).init(gpa);
14481448 defer code_buffer.deinit();
14491449
1450 const name_str_index = blk: {
1450 const name_str = blk: {
14511451 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{
14521452 @tagName(lazy_sym.kind),
14531453 lazy_sym.ty.fmt(pt),
14541454 });
14551455 defer gpa.free(name);
1456 break :blk try self.strtab.insert(gpa, name);
1456 break :blk try self.addString(gpa, name);
14571457 };
14581458
14591459 const src = lazy_sym.ty.srcLocOrNull(mod) orelse Module.LazySrcLoc.unneeded;
......@@ -1480,18 +1480,18 @@ fn updateLazySymbol(
14801480 .const_data => macho_file.zig_const_sect_index.?,
14811481 };
14821482 const sym = &self.symbols.items[symbol_index];
1483 sym.name = name_str_index;
1483 sym.name = name_str;
14841484 sym.out_n_sect = output_section_index;
14851485
14861486 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
1487 nlist.n_strx = name_str_index;
1487 nlist.n_strx = name_str.pos;
14881488 nlist.n_type = macho.N_SECT;
14891489 nlist.n_sect = output_section_index + 1;
14901490 self.symtab.items(.size)[sym.nlist_idx] = code.len;
14911491
14921492 const atom = sym.getAtom(macho_file).?;
1493 atom.flags.alive = true;
1494 atom.name = name_str_index;
1493 atom.setAlive(true);
1494 atom.name = name_str;
14951495 atom.alignment = required_alignment;
14961496 atom.size = code.len;
14971497 atom.out_n_sect = output_section_index;
......@@ -1500,7 +1500,7 @@ fn updateLazySymbol(
15001500 errdefer self.freeDeclMetadata(macho_file, symbol_index);
15011501
15021502 sym.value = 0;
1503 sym.flags.needs_zig_got = true;
1503 sym.setSectionFlags(.{ .needs_zig_got = true });
15041504 nlist.n_value = 0;
15051505
15061506 if (!macho_file.base.isRelocatable()) {
......@@ -1553,10 +1553,10 @@ pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, l
15531553 const gpa = macho_file.base.comp.gpa;
15541554 const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name});
15551555 defer gpa.free(sym_name);
1556 const off = try self.strtab.insert(gpa, sym_name);
1557 const lookup_gop = try self.globals_lookup.getOrPut(gpa, off);
1556 const name_str = try self.addString(gpa, sym_name);
1557 const lookup_gop = try self.globals_lookup.getOrPut(gpa, name_str.pos);
15581558 if (!lookup_gop.found_existing) {
1559 const sym_index = try self.newSymbol(gpa, off, .{});
1559 const sym_index = try self.newSymbol(gpa, name_str, .{});
15601560 const sym = &self.symbols.items[sym_index];
15611561 lookup_gop.value_ptr.* = sym.nlist_idx;
15621562 }
......@@ -1571,12 +1571,12 @@ pub fn getOrCreateMetadataForDecl(
15711571 const gpa = macho_file.base.comp.gpa;
15721572 const gop = try self.decls.getOrPut(gpa, decl_index);
15731573 if (!gop.found_existing) {
1574 const sym_index = try self.newSymbolWithAtom(gpa, 0, macho_file);
1574 const sym_index = try self.newSymbolWithAtom(gpa, .{}, macho_file);
15751575 const sym = &self.symbols.items[sym_index];
15761576 if (isThreadlocal(macho_file, decl_index)) {
15771577 sym.flags.tlv = true;
15781578 } else {
1579 sym.flags.needs_zig_got = true;
1579 sym.setSectionFlags(.{ .needs_zig_got = true });
15801580 }
15811581 gop.value_ptr.* = .{ .symbol_index = sym_index };
15821582 }
......@@ -1609,9 +1609,9 @@ pub fn getOrCreateMetadataForLazySymbol(
16091609 };
16101610 switch (metadata.state.*) {
16111611 .unused => {
1612 const symbol_index = try self.newSymbolWithAtom(gpa, 0, macho_file);
1612 const symbol_index = try self.newSymbolWithAtom(gpa, .{}, macho_file);
16131613 const sym = &self.symbols.items[symbol_index];
1614 sym.flags.needs_zig_got = true;
1614 sym.setSectionFlags(.{ .needs_zig_got = true });
16151615 metadata.symbol_index.* = symbol_index;
16161616 },
16171617 .pending_flush => return metadata.symbol_index.*,
......@@ -1762,6 +1762,16 @@ pub fn setSymbolExtra(self: *ZigObject, index: u32, extra: Symbol.Extra) void {
17621762 }
17631763}
17641764
1765fn addString(self: *ZigObject, allocator: Allocator, string: []const u8) !MachO.String {
1766 const off = try self.strtab.insert(allocator, string);
1767 return .{ .pos = off, .len = @intCast(string.len + 1) };
1768}
1769
1770pub fn getString(self: ZigObject, string: MachO.String) [:0]const u8 {
1771 if (string.len == 0) return "";
1772 return self.strtab.buffer.items[string.pos..][0 .. string.len - 1 :0];
1773}
1774
17651775pub fn asFile(self: *ZigObject) File {
17661776 return .{ .zig_object = self };
17671777}
src/link/MachO/dead_strip.zig+10-10
......@@ -82,9 +82,8 @@ fn markSymbol(sym: *Symbol, roots: *std.ArrayList(*Atom), macho_file: *MachO) !v
8282}
8383
8484fn markAtom(atom: *Atom) bool {
85 const already_visited = atom.flags.visited;
86 atom.flags.visited = true;
87 return atom.flags.alive and !already_visited;
85 const already_visited = atom.visited.swap(true, .seq_cst);
86 return atom.isAlive() and !already_visited;
8887}
8988
9089fn mark(roots: []*Atom, objects: []const File.Index, macho_file: *MachO) void {
......@@ -105,7 +104,7 @@ fn mark(roots: []*Atom, objects: []const File.Index, macho_file: *MachO) void {
105104 !(mem.eql(u8, isec.sectName(), "__eh_frame") or
106105 mem.eql(u8, isec.sectName(), "__compact_unwind") or
107106 isec.attrs() & macho.S_ATTR_DEBUG != 0) and
108 !atom.flags.alive and refersLive(atom, macho_file))
107 !atom.isAlive() and refersLive(atom, macho_file))
109108 {
110109 markLive(atom, macho_file);
111110 loop = true;
......@@ -116,8 +115,8 @@ fn mark(roots: []*Atom, objects: []const File.Index, macho_file: *MachO) void {
116115}
117116
118117fn markLive(atom: *Atom, macho_file: *MachO) void {
119 assert(atom.flags.visited);
120 atom.flags.alive = true;
118 assert(atom.visited.load(.seq_cst));
119 atom.setAlive(true);
121120 track_live_log.debug("{}marking live atom({d},{s})", .{
122121 track_live_level,
123122 atom.atom_index,
......@@ -170,7 +169,7 @@ fn refersLive(atom: *Atom, macho_file: *MachO) bool {
170169 },
171170 };
172171 if (target_atom) |ta| {
173 if (ta.flags.alive) return true;
172 if (ta.isAlive()) return true;
174173 }
175174 }
176175 return false;
......@@ -181,9 +180,10 @@ fn prune(objects: []const File.Index, macho_file: *MachO) void {
181180 const file = macho_file.getFile(index).?;
182181 for (file.getAtoms()) |atom_index| {
183182 const atom = file.getAtom(atom_index) orelse continue;
184 if (atom.flags.alive and !atom.flags.visited) {
185 atom.flags.alive = false;
186 atom.markUnwindRecordsDead(macho_file);
183 if (!atom.visited.load(.seq_cst)) {
184 if (atom.alive.cmpxchgStrong(true, false, .seq_cst, .seq_cst) == null) {
185 atom.markUnwindRecordsDead(macho_file);
186 }
187187 }
188188 }
189189 }
src/link/MachO/dyld_info/Rebase.zig+1-1
......@@ -35,7 +35,7 @@ pub fn updateSize(rebase: *Rebase, macho_file: *MachO) !void {
3535 const file = macho_file.getFile(index).?;
3636 for (file.getAtoms()) |atom_index| {
3737 const atom = file.getAtom(atom_index) orelse continue;
38 if (!atom.flags.alive) continue;
38 if (!atom.isAlive()) continue;
3939 if (atom.getInputSection(macho_file).isZerofill()) continue;
4040 const atom_addr = atom.getAddress(macho_file);
4141 const seg_id = macho_file.sections.items(.segment_id)[atom.out_n_sect];
src/link/MachO/dyld_info/Trie.zig+3-3
......@@ -102,7 +102,7 @@ pub fn updateSize(self: *Trie, macho_file: *MachO) !void {
102102 if (ref.getFile(macho_file) == null) continue;
103103 const sym = ref.getSymbol(macho_file).?;
104104 if (!sym.flags.@"export") continue;
105 if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;
105 if (sym.getAtom(macho_file)) |atom| if (!atom.isAlive()) continue;
106106 var flags: u64 = if (sym.flags.abs)
107107 macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
108108 else if (sym.flags.tlv)
......@@ -111,8 +111,8 @@ pub fn updateSize(self: *Trie, macho_file: *MachO) !void {
111111 macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR;
112112 if (sym.flags.weak) {
113113 flags |= macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
114 macho_file.weak_defines = true;
115 macho_file.binds_to_weak = true;
114 macho_file.weak_defines.store(true, .seq_cst);
115 macho_file.binds_to_weak.store(true, .seq_cst);
116116 }
117117 try self.put(gpa, .{
118118 .name = sym.getName(macho_file),
src/link/MachO/dyld_info/bind.zig+3-6
......@@ -10,10 +10,7 @@ pub const Entry = struct {
1010 if (entry.target.eql(other.target)) {
1111 return entry.offset < other.offset;
1212 }
13 if (entry.target.file == other.target.file) {
14 return entry.target.index < other.target.index;
15 }
16 return entry.target.file < other.target.file;
13 return entry.target.lessThan(other.target);
1714 }
1815 return entry.segment_id < other.segment_id;
1916 }
......@@ -47,7 +44,7 @@ pub const Bind = struct {
4744 const file = macho_file.getFile(index).?;
4845 for (file.getAtoms()) |atom_index| {
4946 const atom = file.getAtom(atom_index) orelse continue;
50 if (!atom.flags.alive) continue;
47 if (!atom.isAlive()) continue;
5148 if (atom.getInputSection(macho_file).isZerofill()) continue;
5249 const atom_addr = atom.getAddress(macho_file);
5350 const relocs = atom.getRelocs(macho_file);
......@@ -299,7 +296,7 @@ pub const WeakBind = struct {
299296 const file = macho_file.getFile(index).?;
300297 for (file.getAtoms()) |atom_index| {
301298 const atom = file.getAtom(atom_index) orelse continue;
302 if (!atom.flags.alive) continue;
299 if (!atom.isAlive()) continue;
303300 if (atom.getInputSection(macho_file).isZerofill()) continue;
304301 const atom_addr = atom.getAddress(macho_file);
305302 const relocs = atom.getRelocs(macho_file);
src/link/MachO/fat.zig+17-16
......@@ -8,11 +8,17 @@ const native_endian = builtin.target.cpu.arch.endian();
88
99const MachO = @import("../MachO.zig");
1010
11pub fn isFatLibrary(path: []const u8) !bool {
12 const file = try std.fs.cwd().openFile(path, .{});
13 defer file.close();
14 const hdr = file.reader().readStructEndian(macho.fat_header, .big) catch return false;
15 return hdr.magic == macho.FAT_MAGIC;
11pub fn readFatHeader(file: std.fs.File) !macho.fat_header {
12 return readFatHeaderGeneric(macho.fat_header, file, 0);
13}
14
15fn readFatHeaderGeneric(comptime Hdr: type, file: std.fs.File, offset: usize) !Hdr {
16 var buffer: [@sizeOf(Hdr)]u8 = undefined;
17 const nread = try file.preadAll(&buffer, offset);
18 if (nread != buffer.len) return error.InputOutput;
19 var hdr = @as(*align(1) const Hdr, @ptrCast(&buffer)).*;
20 mem.byteSwapAllFields(Hdr, &hdr);
21 return hdr;
1622}
1723
1824pub const Arch = struct {
......@@ -21,17 +27,12 @@ pub const Arch = struct {
2127 size: u32,
2228};
2329
24pub fn parseArchs(path: []const u8, buffer: *[2]Arch) ![]const Arch {
25 const file = try std.fs.cwd().openFile(path, .{});
26 defer file.close();
27 const reader = file.reader();
28 const fat_header = try reader.readStructEndian(macho.fat_header, .big);
29 assert(fat_header.magic == macho.FAT_MAGIC);
30
30pub fn parseArchs(file: std.fs.File, fat_header: macho.fat_header, out: *[2]Arch) ![]const Arch {
3131 var count: usize = 0;
3232 var fat_arch_index: u32 = 0;
33 while (fat_arch_index < fat_header.nfat_arch) : (fat_arch_index += 1) {
34 const fat_arch = try reader.readStructEndian(macho.fat_arch, .big);
33 while (fat_arch_index < fat_header.nfat_arch and count < out.len) : (fat_arch_index += 1) {
34 const offset = @sizeOf(macho.fat_header) + @sizeOf(macho.fat_arch) * fat_arch_index;
35 const fat_arch = try readFatHeaderGeneric(macho.fat_arch, file, offset);
3536 // If we come across an architecture that we do not know how to handle, that's
3637 // fine because we can keep looking for one that might match.
3738 const arch: std.Target.Cpu.Arch = switch (fat_arch.cputype) {
......@@ -39,9 +40,9 @@ pub fn parseArchs(path: []const u8, buffer: *[2]Arch) ![]const Arch {
3940 macho.CPU_TYPE_X86_64 => if (fat_arch.cpusubtype == macho.CPU_SUBTYPE_X86_64_ALL) .x86_64 else continue,
4041 else => continue,
4142 };
42 buffer[count] = .{ .tag = arch, .offset = fat_arch.offset, .size = fat_arch.size };
43 out[count] = .{ .tag = arch, .offset = fat_arch.offset, .size = fat_arch.size };
4344 count += 1;
4445 }
4546
46 return buffer[0..count];
47 return out[0..count];
4748}
src/link/MachO/file.zig+33-9
......@@ -37,11 +37,10 @@ pub const File = union(enum) {
3737 }
3838
3939 pub fn scanRelocs(file: File, macho_file: *MachO) !void {
40 switch (file) {
40 return switch (file) {
4141 .dylib => unreachable,
42 .internal => |x| x.scanRelocs(macho_file),
4342 inline else => |x| x.scanRelocs(macho_file),
44 }
43 };
4544 }
4645
4746 /// Encodes symbol rank so that the following ordering applies:
......@@ -182,19 +181,19 @@ pub const File = union(enum) {
182181 if (ref.getFile(macho_file) == null) continue;
183182 if (ref.file != file.getIndex()) continue;
184183 const sym = ref.getSymbol(macho_file).?;
185 if (sym.flags.needs_got) {
184 if (sym.getSectionFlags().needs_got) {
186185 log.debug("'{s}' needs GOT", .{sym.getName(macho_file)});
187186 try macho_file.got.addSymbol(ref, macho_file);
188187 }
189 if (sym.flags.stubs) {
188 if (sym.getSectionFlags().stubs) {
190189 log.debug("'{s}' needs STUBS", .{sym.getName(macho_file)});
191190 try macho_file.stubs.addSymbol(ref, macho_file);
192191 }
193 if (sym.flags.tlv_ptr) {
192 if (sym.getSectionFlags().tlv_ptr) {
194193 log.debug("'{s}' needs TLV pointer", .{sym.getName(macho_file)});
195194 try macho_file.tlv_ptr.addSymbol(ref, macho_file);
196195 }
197 if (sym.flags.objc_stubs) {
196 if (sym.getSectionFlags().objc_stubs) {
198197 log.debug("'{s}' needs OBJC STUBS", .{sym.getName(macho_file)});
199198 try macho_file.objc_stubs.addSymbol(ref, macho_file);
200199 }
......@@ -268,6 +267,9 @@ pub const File = union(enum) {
268267 const ref_file = ref.getFile(macho_file) orelse continue;
269268 if (ref_file.getIndex() == file.getIndex()) continue;
270269
270 macho_file.dupes_mutex.lock();
271 defer macho_file.dupes_mutex.unlock();
272
271273 const gop = try macho_file.dupes.getOrPut(gpa, file.getGlobals()[i]);
272274 if (!gop.found_existing) {
273275 gop.value_ptr.* = .{};
......@@ -281,7 +283,7 @@ pub const File = union(enum) {
281283 defer tracy.end();
282284 for (file.getAtoms()) |atom_index| {
283285 const atom = file.getAtom(atom_index) orelse continue;
284 if (!atom.flags.alive) continue;
286 if (!atom.isAlive()) continue;
285287 atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);
286288 }
287289 }
......@@ -295,11 +297,18 @@ pub const File = union(enum) {
295297
296298 pub fn writeAtoms(file: File, macho_file: *MachO) !void {
297299 return switch (file) {
298 .dylib, .zig_object => unreachable,
300 .dylib => unreachable,
299301 inline else => |x| x.writeAtoms(macho_file),
300302 };
301303 }
302304
305 pub fn writeAtomsRelocatable(file: File, macho_file: *MachO) !void {
306 return switch (file) {
307 .dylib, .internal => unreachable,
308 inline else => |x| x.writeAtomsRelocatable(macho_file),
309 };
310 }
311
303312 pub fn calcSymtabSize(file: File, macho_file: *MachO) void {
304313 return switch (file) {
305314 inline else => |x| x.calcSymtabSize(macho_file),
......@@ -335,6 +344,21 @@ pub const File = union(enum) {
335344 };
336345 }
337346
347 pub fn parse(file: File, macho_file: *MachO) !void {
348 return switch (file) {
349 .internal, .zig_object => unreachable,
350 .object => |x| x.parse(macho_file),
351 .dylib => |x| x.parse(macho_file),
352 };
353 }
354
355 pub fn parseAr(file: File, macho_file: *MachO) !void {
356 return switch (file) {
357 .internal, .zig_object, .dylib => unreachable,
358 .object => |x| x.parseAr(macho_file),
359 };
360 }
361
338362 pub const Index = u32;
339363
340364 pub const Entry = union(enum) {
src/link/MachO/hasher.zig+2
......@@ -55,6 +55,8 @@ pub fn ParallelHasher(comptime Hasher: type) type {
5555 out: *[hash_size]u8,
5656 err: *fs.File.PReadError!usize,
5757 ) void {
58 const tracy = trace(@src());
59 defer tracy.end();
5860 err.* = file.preadAll(buffer, fstart);
5961 Hasher.hash(buffer, out, .{});
6062 }
src/link/MachO/relocatable.zig+129-121
......@@ -27,22 +27,21 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c
2727 }
2828
2929 for (positionals.items) |obj| {
30 macho_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
31 error.MalformedObject,
32 error.MalformedArchive,
33 error.InvalidCpuArch,
34 error.InvalidTarget,
35 => continue, // already reported
36 error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an object file", .{}),
30 macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
31 error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an input file", .{}),
3732 else => |e| try macho_file.reportParseError(
3833 obj.path,
39 "unexpected error: parsing input file failed with error {s}",
34 "unexpected error: reading input file failed with error {s}",
4035 .{@errorName(e)},
4136 ),
4237 };
4338 }
4439
45 if (comp.link_errors.items.len > 0) return error.FlushFailure;
40 if (macho_file.base.hasErrors()) return error.FlushFailure;
41
42 try macho_file.parseInputFiles();
43
44 if (macho_file.base.hasErrors()) return error.FlushFailure;
4645
4746 try macho_file.resolveSymbols();
4847 try macho_file.dedupLiterals();
......@@ -93,22 +92,21 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
9392 }
9493
9594 for (positionals.items) |obj| {
96 parsePositional(macho_file, obj.path) catch |err| switch (err) {
97 error.MalformedObject,
98 error.MalformedArchive,
99 error.InvalidCpuArch,
100 error.InvalidTarget,
101 => continue, // already reported
102 error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an object file", .{}),
95 macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
96 error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an input file", .{}),
10397 else => |e| try macho_file.reportParseError(
10498 obj.path,
105 "unexpected error: parsing input file failed with error {s}",
99 "unexpected error: reading input file failed with error {s}",
106100 .{@errorName(e)},
107101 ),
108102 };
109103 }
110104
111 if (comp.link_errors.items.len > 0) return error.FlushFailure;
105 if (macho_file.base.hasErrors()) return error.FlushFailure;
106
107 try parseInputFilesAr(macho_file);
108
109 if (macho_file.base.hasErrors()) return error.FlushFailure;
112110
113111 // First, we flush relocatable object file generated with our backends.
114112 if (macho_file.getZigObject()) |zo| {
......@@ -225,79 +223,19 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
225223 try macho_file.base.file.?.setEndPos(total_size);
226224 try macho_file.base.file.?.pwriteAll(buffer.items, 0);
227225
228 if (comp.link_errors.items.len > 0) return error.FlushFailure;
229}
230
231fn parsePositional(macho_file: *MachO, path: []const u8) MachO.ParseError!void {
232 const tracy = trace(@src());
233 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;
226 if (macho_file.base.hasErrors()) return error.FlushFailure;
244227}
245228
246fn parseObject(macho_file: *MachO, path: []const u8) MachO.ParseError!void {
229fn parseInputFilesAr(macho_file: *MachO) !void {
247230 const tracy = trace(@src());
248231 defer tracy.end();
249232
250 const gpa = macho_file.base.comp.gpa;
251 const file = try std.fs.cwd().openFile(path, .{});
252 errdefer file.close();
253 const handle = try macho_file.addFileHandle(file);
254 const mtime: u64 = mtime: {
255 const stat = file.stat() catch break :mtime 0;
256 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
257 };
258 const index = @as(File.Index, @intCast(try macho_file.files.addOne(gpa)));
259 macho_file.files.set(index, .{
260 .object = .{
261 .offset = 0, // TODO FAT objects
262 .path = try gpa.dupe(u8, path),
263 .file_handle = handle,
264 .mtime = mtime,
265 .index = index,
266 },
267 });
268 try macho_file.objects.append(gpa, index);
269
270 const object = macho_file.getFile(index).?.object;
271 try object.parseAr(macho_file);
272}
273
274fn parseArchive(macho_file: *MachO, path: []const u8, fat_arch: ?fat.Arch) MachO.ParseError!void {
275 const tracy = trace(@src());
276 defer tracy.end();
277
278 const gpa = macho_file.base.comp.gpa;
279
280 const file = try std.fs.cwd().openFile(path, .{});
281 errdefer file.close();
282 const handle = try macho_file.addFileHandle(file);
283
284 var archive = Archive{};
285 defer archive.deinit(gpa);
286 try archive.parse(macho_file, path, handle, fat_arch);
287
288 var has_parse_error = false;
289 for (archive.objects.items) |extracted| {
290 const index = @as(File.Index, @intCast(try macho_file.files.addOne(gpa)));
291 macho_file.files.set(index, .{ .object = extracted });
292 const object = &macho_file.files.items(.data)[index].object;
293 object.index = index;
294 object.parseAr(macho_file) catch |err| switch (err) {
295 error.InvalidCpuArch => has_parse_error = true,
296 else => |e| return e,
233 for (macho_file.objects.items) |index| {
234 macho_file.getFile(index).?.parseAr(macho_file) catch |err| switch (err) {
235 error.InvalidCpuArch => {}, // already reported
236 else => |e| try macho_file.reportParseError2(index, "unexpected error: parsing input file failed with error {s}", .{@errorName(e)}),
297237 };
298 try macho_file.objects.append(gpa, index);
299238 }
300 if (has_parse_error) return error.MalformedArchive;
301239}
302240
303241fn markExports(macho_file: *MachO) void {
......@@ -323,7 +261,7 @@ fn initOutputSections(macho_file: *MachO) !void {
323261 const file = macho_file.getFile(index).?;
324262 for (file.getAtoms()) |atom_index| {
325263 const atom = file.getAtom(atom_index) orelse continue;
326 if (!atom.flags.alive) continue;
264 if (!atom.isAlive()) continue;
327265 atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);
328266 }
329267 }
......@@ -350,37 +288,54 @@ fn calcSectionSizes(macho_file: *MachO) !void {
350288 const tracy = trace(@src());
351289 defer tracy.end();
352290
353 for (macho_file.sections.items(.atoms), 0..) |atoms, i| {
354 if (atoms.items.len == 0) continue;
355 calcSectionSize(macho_file, @intCast(i));
356 }
357
358291 if (macho_file.getZigObject()) |zo| {
359 // TODO this will create a race
292 // TODO this will create a race as we need to track merging of debug sections which we currently don't
360293 zo.calcNumRelocs(macho_file);
361 zo.calcSymtabSize(macho_file);
362294 }
363295
364 if (macho_file.eh_frame_sect_index) |_| {
365 try calcEhFrameSize(macho_file);
366 }
296 const tp = macho_file.base.comp.thread_pool;
297 var wg: WaitGroup = .{};
298 {
299 wg.reset();
300 defer wg.wait();
301
302 for (macho_file.sections.items(.atoms), 0..) |atoms, i| {
303 if (atoms.items.len == 0) continue;
304 tp.spawnWg(&wg, calcSectionSizeWorker, .{ macho_file, @as(u8, @intCast(i)) });
305 }
306
307 if (macho_file.eh_frame_sect_index) |_| {
308 tp.spawnWg(&wg, calcEhFrameSizeWorker, .{macho_file});
309 }
367310
368 for (macho_file.objects.items) |index| {
369311 if (macho_file.unwind_info_sect_index) |_| {
370 macho_file.getFile(index).?.object.calcCompactUnwindSizeRelocatable(macho_file);
312 for (macho_file.objects.items) |index| {
313 tp.spawnWg(&wg, Object.calcCompactUnwindSizeRelocatable, .{
314 macho_file.getFile(index).?.object,
315 macho_file,
316 });
317 }
371318 }
372 macho_file.getFile(index).?.calcSymtabSize(macho_file);
373 }
374319
375 try macho_file.data_in_code.updateSize(macho_file);
320 for (macho_file.objects.items) |index| {
321 tp.spawnWg(&wg, File.calcSymtabSize, .{ macho_file.getFile(index).?, macho_file });
322 }
323 if (macho_file.getZigObject()) |zo| {
324 tp.spawnWg(&wg, File.calcSymtabSize, .{ zo.asFile(), macho_file });
325 }
326
327 tp.spawnWg(&wg, MachO.updateLinkeditSizeWorker, .{ macho_file, .data_in_code });
328 }
376329
377330 if (macho_file.unwind_info_sect_index) |_| {
378331 calcCompactUnwindSize(macho_file);
379332 }
380333 try calcSymtabSize(macho_file);
334
335 if (macho_file.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
381336}
382337
383fn calcSectionSize(macho_file: *MachO, sect_id: u8) void {
338fn calcSectionSizeWorker(macho_file: *MachO, sect_id: u8) void {
384339 const tracy = trace(@src());
385340 defer tracy.end();
386341
......@@ -401,14 +356,25 @@ fn calcSectionSize(macho_file: *MachO, sect_id: u8) void {
401356 }
402357}
403358
404fn calcEhFrameSize(macho_file: *MachO) !void {
359fn calcEhFrameSizeWorker(macho_file: *MachO) void {
405360 const tracy = trace(@src());
406361 defer tracy.end();
407362
363 const doWork = struct {
364 fn doWork(mfile: *MachO, header: *macho.section_64) !void {
365 header.size = try eh_frame.calcSize(mfile);
366 header.@"align" = 3;
367 header.nreloc = eh_frame.calcNumRelocs(mfile);
368 }
369 }.doWork;
370
408371 const header = &macho_file.sections.items(.header)[macho_file.eh_frame_sect_index.?];
409 header.size = try eh_frame.calcSize(macho_file);
410 header.@"align" = 3;
411 header.nreloc = eh_frame.calcNumRelocs(macho_file);
372 doWork(macho_file, header) catch |err| {
373 macho_file.reportUnexpectedError("failed to calculate size of section '__TEXT,__eh_frame': {s}", .{
374 @errorName(err),
375 }) catch {};
376 _ = macho_file.has_errors.swap(true, .seq_cst);
377 };
412378}
413379
414380fn calcCompactUnwindSize(macho_file: *MachO) void {
......@@ -639,33 +605,74 @@ fn writeSections(macho_file: *MachO) !void {
639605 try macho_file.strtab.resize(gpa, cmd.strsize);
640606 macho_file.strtab.items[0] = 0;
641607
642 for (macho_file.objects.items) |index| {
643 try macho_file.getFile(index).?.object.writeAtomsRelocatable(macho_file);
644 macho_file.getFile(index).?.writeSymtab(macho_file, macho_file);
608 const tp = macho_file.base.comp.thread_pool;
609 var wg: WaitGroup = .{};
610 {
611 wg.reset();
612 defer wg.wait();
613
614 for (macho_file.objects.items) |index| {
615 tp.spawnWg(&wg, writeAtomsWorker, .{ macho_file, macho_file.getFile(index).? });
616 tp.spawnWg(&wg, File.writeSymtab, .{ macho_file.getFile(index).?, macho_file, macho_file });
617 }
618
619 if (macho_file.getZigObject()) |zo| {
620 tp.spawnWg(&wg, writeAtomsWorker, .{ macho_file, zo.asFile() });
621 tp.spawnWg(&wg, File.writeSymtab, .{ zo.asFile(), macho_file, macho_file });
622 }
623
624 if (macho_file.eh_frame_sect_index) |_| {
625 tp.spawnWg(&wg, writeEhFrameWorker, .{macho_file});
626 }
627
628 if (macho_file.unwind_info_sect_index) |_| {
629 for (macho_file.objects.items) |index| {
630 tp.spawnWg(&wg, writeCompactUnwindWorker, .{ macho_file, macho_file.getFile(index).?.object });
631 }
632 }
645633 }
646634
635 if (macho_file.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
636
647637 if (macho_file.getZigObject()) |zo| {
648638 try zo.writeRelocs(macho_file);
649 try zo.writeAtomsRelocatable(macho_file);
650 zo.writeSymtab(macho_file, macho_file);
651 }
652
653 if (macho_file.eh_frame_sect_index) |_| {
654 try writeEhFrame(macho_file);
655639 }
640}
656641
657 if (macho_file.unwind_info_sect_index) |_| {
658 for (macho_file.objects.items) |index| {
659 try macho_file.getFile(index).?.object.writeCompactUnwindRelocatable(macho_file);
660 }
661 }
642fn writeAtomsWorker(macho_file: *MachO, file: File) void {
643 const tracy = trace(@src());
644 defer tracy.end();
645 file.writeAtomsRelocatable(macho_file) catch |err| {
646 macho_file.reportParseError2(file.getIndex(), "failed to write atoms: {s}", .{
647 @errorName(err),
648 }) catch {};
649 _ = macho_file.has_errors.swap(true, .seq_cst);
650 };
662651}
663652
664fn writeEhFrame(macho_file: *MachO) !void {
653fn writeEhFrameWorker(macho_file: *MachO) void {
654 const tracy = trace(@src());
655 defer tracy.end();
665656 const sect_index = macho_file.eh_frame_sect_index.?;
666657 const buffer = macho_file.sections.items(.out)[sect_index];
667658 const relocs = macho_file.sections.items(.relocs)[sect_index];
668 try eh_frame.writeRelocs(macho_file, buffer.items, relocs.items);
659 eh_frame.writeRelocs(macho_file, buffer.items, relocs.items) catch |err| {
660 macho_file.reportUnexpectedError("failed to write '__LD,__eh_frame' section: {s}", .{
661 @errorName(err),
662 }) catch {};
663 _ = macho_file.has_errors.swap(true, .seq_cst);
664 };
665}
666
667fn writeCompactUnwindWorker(macho_file: *MachO, object: *Object) void {
668 const tracy = trace(@src());
669 defer tracy.end();
670 object.writeCompactUnwindRelocatable(macho_file) catch |err| {
671 macho_file.reportUnexpectedError("failed to write '__LD,__eh_frame' section: {s}", .{
672 @errorName(err),
673 }) catch {};
674 _ = macho_file.has_errors.swap(true, .seq_cst);
675 };
669676}
670677
671678fn writeSectionsToFile(macho_file: *MachO) !void {
......@@ -778,3 +785,4 @@ const File = @import("file.zig").File;
778785const MachO = @import("../MachO.zig");
779786const Object = @import("Object.zig");
780787const Symbol = @import("Symbol.zig");
788const WaitGroup = std.Thread.WaitGroup;
src/link/MachO/synthetic.zig+4-4
......@@ -24,8 +24,8 @@ pub const ZigGotSection = struct {
2424 const entry = &zig_got.entries.items[index];
2525 entry.* = sym_index;
2626 const symbol = &zo.symbols.items[sym_index];
27 assert(symbol.flags.needs_zig_got);
28 symbol.flags.has_zig_got = true;
27 assert(symbol.getSectionFlags().needs_zig_got);
28 symbol.setSectionFlags(.{ .has_zig_got = true });
2929 symbol.addExtra(.{ .zig_got = index }, macho_file);
3030 return index;
3131 }
......@@ -121,7 +121,7 @@ pub const GotSection = struct {
121121 const entry = try got.symbols.addOne(gpa);
122122 entry.* = ref;
123123 const symbol = ref.getSymbol(macho_file).?;
124 symbol.flags.has_got = true;
124 symbol.setSectionFlags(.{ .has_got = true });
125125 symbol.addExtra(.{ .got = index }, macho_file);
126126 }
127127
......@@ -689,7 +689,7 @@ pub const DataInCode = struct {
689689 dices[next_dice].offset < end_off) : (next_dice += 1)
690690 {}
691691
692 if (atom.flags.alive) for (dices[start_dice..next_dice]) |d| {
692 if (atom.isAlive()) for (dices[start_dice..next_dice]) |d| {
693693 dice.entries.appendAssumeCapacity(.{
694694 .atom_ref = .{ .index = atom_index, .file = index },
695695 .offset = @intCast(d.offset - start_off),
src/link/MachO/thunks.zig+3-3
......@@ -17,7 +17,7 @@ pub fn createThunks(sect_id: u8, macho_file: *MachO) !void {
1717 while (i < atoms.len) {
1818 const start = i;
1919 const start_atom = atoms[start].getAtom(macho_file).?;
20 assert(start_atom.flags.alive);
20 assert(start_atom.isAlive());
2121 start_atom.value = advance(header, start_atom.size, start_atom.alignment);
2222 i += 1;
2323
......@@ -25,7 +25,7 @@ pub fn createThunks(sect_id: u8, macho_file: *MachO) !void {
2525 header.size - start_atom.value < max_allowed_distance) : (i += 1)
2626 {
2727 const atom = atoms[i].getAtom(macho_file).?;
28 assert(atom.flags.alive);
28 assert(atom.isAlive());
2929 atom.value = advance(header, atom.size, atom.alignment);
3030 }
3131
......@@ -71,7 +71,7 @@ fn scanRelocs(thunk_index: Thunk.Index, gpa: Allocator, atoms: []const MachO.Ref
7171
7272fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {
7373 const target = rel.getTargetSymbol(atom.*, macho_file);
74 if (target.flags.stubs or target.flags.objc_stubs) return false;
74 if (target.getSectionFlags().stubs or target.getSectionFlags().objc_stubs) return false;
7575 if (atom.out_n_sect != target.getOutputSectionIndex(macho_file)) return false;
7676 const target_atom = target.getAtom(macho_file).?;
7777 if (target_atom.value == @as(u64, @bitCast(@as(i64, -1)))) return false;
src/link/Wasm.zig+89-127
......@@ -658,9 +658,9 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {
658658 var object = Object.create(wasm, obj_file, path, null) catch |err| switch (err) {
659659 error.InvalidMagicByte, error.NotObjectFile => return false,
660660 else => |e| {
661 var err_note = try wasm.addErrorWithNotes(1);
662 try err_note.addMsg(wasm, "Failed parsing object file: {s}", .{@errorName(e)});
663 try err_note.addNote(wasm, "while parsing '{s}'", .{path});
661 var err_note = try wasm.base.addErrorWithNotes(1);
662 try err_note.addMsg("Failed parsing object file: {s}", .{@errorName(e)});
663 try err_note.addNote("while parsing '{s}'", .{path});
664664 return error.FlushFailure;
665665 },
666666 };
......@@ -714,9 +714,9 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
714714 return false;
715715 },
716716 else => |e| {
717 var err_note = try wasm.addErrorWithNotes(1);
718 try err_note.addMsg(wasm, "Failed parsing archive: {s}", .{@errorName(e)});
719 try err_note.addNote(wasm, "while parsing archive {s}", .{path});
717 var err_note = try wasm.base.addErrorWithNotes(1);
718 try err_note.addMsg("Failed parsing archive: {s}", .{@errorName(e)});
719 try err_note.addNote("while parsing archive {s}", .{path});
720720 return error.FlushFailure;
721721 },
722722 };
......@@ -741,9 +741,9 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
741741
742742 for (offsets.keys()) |file_offset| {
743743 var object = archive.parseObject(wasm, file_offset) catch |e| {
744 var err_note = try wasm.addErrorWithNotes(1);
745 try err_note.addMsg(wasm, "Failed parsing object: {s}", .{@errorName(e)});
746 try err_note.addNote(wasm, "while parsing object in archive {s}", .{path});
744 var err_note = try wasm.base.addErrorWithNotes(1);
745 try err_note.addMsg("Failed parsing object: {s}", .{@errorName(e)});
746 try err_note.addNote("while parsing object in archive {s}", .{path});
747747 return error.FlushFailure;
748748 };
749749 object.index = @enumFromInt(wasm.files.len);
......@@ -779,9 +779,9 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
779779
780780 if (symbol.isLocal()) {
781781 if (symbol.isUndefined()) {
782 var err = try wasm.addErrorWithNotes(1);
783 try err.addMsg(wasm, "Local symbols are not allowed to reference imports", .{});
784 try err.addNote(wasm, "symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() });
782 var err = try wasm.base.addErrorWithNotes(1);
783 try err.addMsg("Local symbols are not allowed to reference imports", .{});
784 try err.addNote("symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() });
785785 }
786786 try wasm.resolved_symbols.putNoClobber(gpa, location, {});
787787 continue;
......@@ -816,10 +816,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
816816 break :outer; // existing is weak, while new one isn't. Replace it.
817817 }
818818 // both are defined and weak, we have a symbol collision.
819 var err = try wasm.addErrorWithNotes(2);
820 try err.addMsg(wasm, "symbol '{s}' defined multiple times", .{sym_name});
821 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
822 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
819 var err = try wasm.base.addErrorWithNotes(2);
820 try err.addMsg("symbol '{s}' defined multiple times", .{sym_name});
821 try err.addNote("first definition in '{s}'", .{existing_file_path});
822 try err.addNote("next definition in '{s}'", .{obj_file.path()});
823823 }
824824
825825 try wasm.discarded.put(gpa, location, existing_loc);
......@@ -827,10 +827,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
827827 }
828828
829829 if (symbol.tag != existing_sym.tag) {
830 var err = try wasm.addErrorWithNotes(2);
831 try err.addMsg(wasm, "symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) });
832 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
833 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
830 var err = try wasm.base.addErrorWithNotes(2);
831 try err.addMsg("symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) });
832 try err.addNote("first definition in '{s}'", .{existing_file_path});
833 try err.addNote("next definition in '{s}'", .{obj_file.path()});
834834 }
835835
836836 if (existing_sym.isUndefined() and symbol.isUndefined()) {
......@@ -847,14 +847,14 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
847847 const imp = obj_file.import(sym_index);
848848 const module_name = obj_file.string(imp.module_name);
849849 if (!mem.eql(u8, existing_name, module_name)) {
850 var err = try wasm.addErrorWithNotes(2);
851 try err.addMsg(wasm, "symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{
850 var err = try wasm.base.addErrorWithNotes(2);
851 try err.addMsg("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{
852852 sym_name,
853853 existing_name,
854854 module_name,
855855 });
856 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
857 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
856 try err.addNote("first definition in '{s}'", .{existing_file_path});
857 try err.addNote("next definition in '{s}'", .{obj_file.path()});
858858 }
859859 }
860860
......@@ -867,10 +867,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
867867 const existing_ty = wasm.getGlobalType(existing_loc);
868868 const new_ty = wasm.getGlobalType(location);
869869 if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) {
870 var err = try wasm.addErrorWithNotes(2);
871 try err.addMsg(wasm, "symbol '{s}' mismatching global types", .{sym_name});
872 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
873 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
870 var err = try wasm.base.addErrorWithNotes(2);
871 try err.addMsg("symbol '{s}' mismatching global types", .{sym_name});
872 try err.addNote("first definition in '{s}'", .{existing_file_path});
873 try err.addNote("next definition in '{s}'", .{obj_file.path()});
874874 }
875875 }
876876
......@@ -878,11 +878,11 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
878878 const existing_ty = wasm.getFunctionSignature(existing_loc);
879879 const new_ty = wasm.getFunctionSignature(location);
880880 if (!existing_ty.eql(new_ty)) {
881 var err = try wasm.addErrorWithNotes(3);
882 try err.addMsg(wasm, "symbol '{s}' mismatching function signatures.", .{sym_name});
883 try err.addNote(wasm, "expected signature {}, but found signature {}", .{ existing_ty, new_ty });
884 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
885 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
881 var err = try wasm.base.addErrorWithNotes(3);
882 try err.addMsg("symbol '{s}' mismatching function signatures.", .{sym_name});
883 try err.addNote("expected signature {}, but found signature {}", .{ existing_ty, new_ty });
884 try err.addNote("first definition in '{s}'", .{existing_file_path});
885 try err.addNote("next definition in '{s}'", .{obj_file.path()});
886886 }
887887 }
888888
......@@ -930,9 +930,9 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
930930 // Parse object and and resolve symbols again before we check remaining
931931 // undefined symbols.
932932 var object = archive.parseObject(wasm, offset.items[0]) catch |e| {
933 var err_note = try wasm.addErrorWithNotes(1);
934 try err_note.addMsg(wasm, "Failed parsing object: {s}", .{@errorName(e)});
935 try err_note.addNote(wasm, "while parsing object in archive {s}", .{archive.name});
933 var err_note = try wasm.base.addErrorWithNotes(1);
934 try err_note.addMsg("Failed parsing object: {s}", .{@errorName(e)});
935 try err_note.addNote("while parsing object in archive {s}", .{archive.name});
936936 return error.FlushFailure;
937937 };
938938 object.index = @enumFromInt(wasm.files.len);
......@@ -1237,9 +1237,9 @@ fn validateFeatures(
12371237 allowed[used_index] = is_enabled;
12381238 emit_features_count.* += @intFromBool(is_enabled);
12391239 } else if (is_enabled and !allowed[used_index]) {
1240 var err = try wasm.addErrorWithNotes(1);
1241 try err.addMsg(wasm, "feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))});
1242 try err.addNote(wasm, "defined in '{s}'", .{wasm.files.items(.data)[used_set >> 1].object.path});
1240 var err = try wasm.base.addErrorWithNotes(1);
1241 try err.addMsg("feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))});
1242 try err.addNote("defined in '{s}'", .{wasm.files.items(.data)[used_set >> 1].object.path});
12431243 valid_feature_set = false;
12441244 }
12451245 }
......@@ -1251,7 +1251,8 @@ fn validateFeatures(
12511251 if (shared_memory) {
12521252 const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)];
12531253 if (@as(u1, @truncate(disallowed_feature)) != 0) {
1254 try wasm.addErrorWithoutNotes(
1254 var err = try wasm.base.addErrorWithNotes(0);
1255 try err.addMsg(
12551256 "shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled",
12561257 .{wasm.files.items(.data)[disallowed_feature >> 1].object.path},
12571258 );
......@@ -1260,7 +1261,8 @@ fn validateFeatures(
12601261
12611262 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
12621263 if (!allowed[@intFromEnum(feature)]) {
1263 try wasm.addErrorWithoutNotes("feature '{}' is not used but is required for shared-memory", .{feature});
1264 var err = try wasm.base.addErrorWithNotes(0);
1265 try err.addMsg("feature '{}' is not used but is required for shared-memory", .{feature});
12641266 }
12651267 }
12661268 }
......@@ -1268,7 +1270,8 @@ fn validateFeatures(
12681270 if (has_tls) {
12691271 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
12701272 if (!allowed[@intFromEnum(feature)]) {
1271 try wasm.addErrorWithoutNotes("feature '{}' is not used but is required for thread-local storage", .{feature});
1273 var err = try wasm.base.addErrorWithNotes(0);
1274 try err.addMsg("feature '{}' is not used but is required for thread-local storage", .{feature});
12721275 }
12731276 }
12741277 }
......@@ -1281,10 +1284,10 @@ fn validateFeatures(
12811284 // from here a feature is always used
12821285 const disallowed_feature = disallowed[@intFromEnum(feature.tag)];
12831286 if (@as(u1, @truncate(disallowed_feature)) != 0) {
1284 var err = try wasm.addErrorWithNotes(2);
1285 try err.addMsg(wasm, "feature '{}' is disallowed, but used by linked object", .{feature.tag});
1286 try err.addNote(wasm, "disallowed by '{s}'", .{wasm.files.items(.data)[disallowed_feature >> 1].object.path});
1287 try err.addNote(wasm, "used in '{s}'", .{object.path});
1287 var err = try wasm.base.addErrorWithNotes(2);
1288 try err.addMsg("feature '{}' is disallowed, but used by linked object", .{feature.tag});
1289 try err.addNote("disallowed by '{s}'", .{wasm.files.items(.data)[disallowed_feature >> 1].object.path});
1290 try err.addNote("used in '{s}'", .{object.path});
12881291 valid_feature_set = false;
12891292 }
12901293
......@@ -1295,10 +1298,10 @@ fn validateFeatures(
12951298 for (required, 0..) |required_feature, feature_index| {
12961299 const is_required = @as(u1, @truncate(required_feature)) != 0;
12971300 if (is_required and !object_used_features[feature_index]) {
1298 var err = try wasm.addErrorWithNotes(2);
1299 try err.addMsg(wasm, "feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))});
1300 try err.addNote(wasm, "required by '{s}'", .{wasm.files.items(.data)[required_feature >> 1].object.path});
1301 try err.addNote(wasm, "missing in '{s}'", .{object.path});
1301 var err = try wasm.base.addErrorWithNotes(2);
1302 try err.addMsg("feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))});
1303 try err.addNote("required by '{s}'", .{wasm.files.items(.data)[required_feature >> 1].object.path});
1304 try err.addNote("missing in '{s}'", .{object.path});
13021305 valid_feature_set = false;
13031306 }
13041307 }
......@@ -1376,9 +1379,9 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void {
13761379 else
13771380 wasm.name;
13781381 const symbol_name = undef.getName(wasm);
1379 var err = try wasm.addErrorWithNotes(1);
1380 try err.addMsg(wasm, "could not resolve undefined symbol '{s}'", .{symbol_name});
1381 try err.addNote(wasm, "defined in '{s}'", .{file_name});
1382 var err = try wasm.base.addErrorWithNotes(1);
1383 try err.addMsg("could not resolve undefined symbol '{s}'", .{symbol_name});
1384 try err.addNote("defined in '{s}'", .{file_name});
13821385 }
13831386 }
13841387 if (found_undefined_symbols) {
......@@ -1757,7 +1760,8 @@ fn setupInitFunctions(wasm: *Wasm) !void {
17571760 break :ty object.func_types[func.type_index];
17581761 };
17591762 if (ty.params.len != 0) {
1760 try wasm.addErrorWithoutNotes("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)});
1763 var err = try wasm.base.addErrorWithNotes(0);
1764 try err.addMsg("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)});
17611765 }
17621766 log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)});
17631767 wasm.init_funcs.appendAssumeCapacity(.{
......@@ -2140,7 +2144,8 @@ fn checkExportNames(wasm: *Wasm) !void {
21402144
21412145 for (force_exp_names) |exp_name| {
21422146 const loc = wasm.findGlobalSymbol(exp_name) orelse {
2143 try wasm.addErrorWithoutNotes("could not export '{s}', symbol not found", .{exp_name});
2147 var err = try wasm.base.addErrorWithNotes(0);
2148 try err.addMsg("could not export '{s}', symbol not found", .{exp_name});
21442149 failed_exports = true;
21452150 continue;
21462151 };
......@@ -2203,13 +2208,15 @@ fn setupStart(wasm: *Wasm) !void {
22032208 const entry_name = wasm.entry_name orelse return;
22042209
22052210 const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {
2206 try wasm.addErrorWithoutNotes("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});
2211 var err = try wasm.base.addErrorWithNotes(0);
2212 try err.addMsg("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});
22072213 return error.FlushFailure;
22082214 };
22092215
22102216 const symbol = symbol_loc.getSymbol(wasm);
22112217 if (symbol.tag != .function) {
2212 try wasm.addErrorWithoutNotes("Entry symbol '{s}' is not a function", .{entry_name});
2218 var err = try wasm.base.addErrorWithNotes(0);
2219 try err.addMsg("Entry symbol '{s}' is not a function", .{entry_name});
22132220 return error.FlushFailure;
22142221 }
22152222
......@@ -2314,13 +2321,16 @@ fn setupMemory(wasm: *Wasm) !void {
23142321
23152322 if (wasm.initial_memory) |initial_memory| {
23162323 if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) {
2317 try wasm.addErrorWithoutNotes("Initial memory must be {d}-byte aligned", .{page_size});
2324 var err = try wasm.base.addErrorWithNotes(0);
2325 try err.addMsg("Initial memory must be {d}-byte aligned", .{page_size});
23182326 }
23192327 if (memory_ptr > initial_memory) {
2320 try wasm.addErrorWithoutNotes("Initial memory too small, must be at least {d} bytes", .{memory_ptr});
2328 var err = try wasm.base.addErrorWithNotes(0);
2329 try err.addMsg("Initial memory too small, must be at least {d} bytes", .{memory_ptr});
23212330 }
23222331 if (initial_memory > max_memory_allowed) {
2323 try wasm.addErrorWithoutNotes("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});
2332 var err = try wasm.base.addErrorWithNotes(0);
2333 try err.addMsg("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});
23242334 }
23252335 memory_ptr = initial_memory;
23262336 }
......@@ -2337,13 +2347,16 @@ fn setupMemory(wasm: *Wasm) !void {
23372347
23382348 if (wasm.max_memory) |max_memory| {
23392349 if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) {
2340 try wasm.addErrorWithoutNotes("Maximum memory must be {d}-byte aligned", .{page_size});
2350 var err = try wasm.base.addErrorWithNotes(0);
2351 try err.addMsg("Maximum memory must be {d}-byte aligned", .{page_size});
23412352 }
23422353 if (memory_ptr > max_memory) {
2343 try wasm.addErrorWithoutNotes("Maximum memory too small, must be at least {d} bytes", .{memory_ptr});
2354 var err = try wasm.base.addErrorWithNotes(0);
2355 try err.addMsg("Maximum memory too small, must be at least {d} bytes", .{memory_ptr});
23442356 }
23452357 if (max_memory > max_memory_allowed) {
2346 try wasm.addErrorWithoutNotes("Maximum memory exceeds maximum amount {d}", .{max_memory_allowed});
2358 var err = try wasm.base.addErrorWithNotes(0);
2359 try err.addMsg("Maximum memory exceeds maximum amount {d}", .{max_memory_allowed});
23472360 }
23482361 wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size));
23492362 wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX);
......@@ -2446,9 +2459,9 @@ pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: Sym
24462459 break :blk index;
24472460 };
24482461 } else {
2449 var err = try wasm.addErrorWithNotes(1);
2450 try err.addMsg(wasm, "found unknown section '{s}'", .{section_name});
2451 try err.addNote(wasm, "defined in '{s}'", .{obj_file.path()});
2462 var err = try wasm.base.addErrorWithNotes(1);
2463 try err.addMsg("found unknown section '{s}'", .{section_name});
2464 try err.addNote("defined in '{s}'", .{obj_file.path()});
24522465 return error.UnexpectedValue;
24532466 }
24542467 },
......@@ -2564,23 +2577,23 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
25642577 if (wasm.zig_object_index != .null) {
25652578 try wasm.resolveSymbolsInObject(wasm.zig_object_index);
25662579 }
2567 if (comp.link_errors.items.len > 0) return error.FlushFailure;
2580 if (wasm.base.hasErrors()) return error.FlushFailure;
25682581 for (wasm.objects.items) |object_index| {
25692582 try wasm.resolveSymbolsInObject(object_index);
25702583 }
2571 if (comp.link_errors.items.len > 0) return error.FlushFailure;
2584 if (wasm.base.hasErrors()) return error.FlushFailure;
25722585
25732586 var emit_features_count: u32 = 0;
25742587 var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;
25752588 try wasm.validateFeatures(&enabled_features, &emit_features_count);
25762589 try wasm.resolveSymbolsInArchives();
2577 if (comp.link_errors.items.len > 0) return error.FlushFailure;
2590 if (wasm.base.hasErrors()) return error.FlushFailure;
25782591 try wasm.resolveLazySymbols();
25792592 try wasm.checkUndefinedSymbols();
25802593 try wasm.checkExportNames();
25812594
25822595 try wasm.setupInitFunctions();
2583 if (comp.link_errors.items.len > 0) return error.FlushFailure;
2596 if (wasm.base.hasErrors()) return error.FlushFailure;
25842597 try wasm.setupStart();
25852598
25862599 try wasm.markReferences();
......@@ -2589,7 +2602,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
25892602 try wasm.mergeTypes();
25902603 try wasm.allocateAtoms();
25912604 try wasm.setupMemory();
2592 if (comp.link_errors.items.len > 0) return error.FlushFailure;
2605 if (wasm.base.hasErrors()) return error.FlushFailure;
25932606 wasm.allocateVirtualAddresses();
25942607 wasm.mapFunctionTable();
25952608 try wasm.initializeCallCtorsFunction();
......@@ -2599,7 +2612,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
25992612 try wasm.setupStartSection();
26002613 try wasm.setupExports();
26012614 try wasm.writeToFile(enabled_features, emit_features_count, arena);
2602 if (comp.link_errors.items.len > 0) return error.FlushFailure;
2615 if (wasm.base.hasErrors()) return error.FlushFailure;
26032616}
26042617
26052618/// Writes the WebAssembly in-memory module to the file
......@@ -2997,7 +3010,10 @@ fn writeToFile(
29973010 }) catch unreachable;
29983011 try emitBuildIdSection(&binary_bytes, str);
29993012 },
3000 else => |mode| try wasm.addErrorWithoutNotes("build-id '{s}' is not supported for WebAssembly", .{@tagName(mode)}),
3013 else => |mode| {
3014 var err = try wasm.base.addErrorWithNotes(0);
3015 try err.addMsg("build-id '{s}' is not supported for WebAssembly", .{@tagName(mode)});
3016 },
30013017 }
30023018
30033019 var debug_bytes = std.ArrayList(u8).init(gpa);
......@@ -4086,57 +4102,3 @@ fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8
40864102 .command => "_start",
40874103 };
40884104}
4089
4090const ErrorWithNotes = struct {
4091 /// Allocated index in comp.link_errors array.
4092 index: usize,
4093
4094 /// Next available note slot.
4095 note_slot: usize = 0,
4096
4097 pub fn addMsg(
4098 err: ErrorWithNotes,
4099 wasm_file: *const Wasm,
4100 comptime format: []const u8,
4101 args: anytype,
4102 ) error{OutOfMemory}!void {
4103 const comp = wasm_file.base.comp;
4104 const gpa = comp.gpa;
4105 const err_msg = &comp.link_errors.items[err.index];
4106 err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
4107 }
4108
4109 pub fn addNote(
4110 err: *ErrorWithNotes,
4111 wasm_file: *const Wasm,
4112 comptime format: []const u8,
4113 args: anytype,
4114 ) error{OutOfMemory}!void {
4115 const comp = wasm_file.base.comp;
4116 const gpa = comp.gpa;
4117 const err_msg = &comp.link_errors.items[err.index];
4118 err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };
4119 err.note_slot += 1;
4120 }
4121};
4122
4123pub fn addErrorWithNotes(wasm: *const Wasm, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
4124 const comp = wasm.base.comp;
4125 const gpa = comp.gpa;
4126 try comp.link_errors.ensureUnusedCapacity(gpa, 1);
4127 return wasm.addErrorWithNotesAssumeCapacity(note_count);
4128}
4129
4130pub fn addErrorWithoutNotes(wasm: *const Wasm, comptime fmt: []const u8, args: anytype) !void {
4131 const err = try wasm.addErrorWithNotes(0);
4132 try err.addMsg(wasm, fmt, args);
4133}
4134
4135fn addErrorWithNotesAssumeCapacity(wasm: *const Wasm, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
4136 const comp = wasm.base.comp;
4137 const gpa = comp.gpa;
4138 const index = comp.link_errors.items.len;
4139 const err = comp.link_errors.addOneAssumeCapacity();
4140 err.* = .{ .msg = undefined, .notes = try gpa.alloc(link.File.ErrorMsg, note_count) };
4141 return .{ .index = index };
4142}
src/link/Wasm/Object.zig+15-15
......@@ -235,27 +235,27 @@ fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?S
235235 if (object.imported_tables_count == table_count) return null;
236236
237237 if (table_count != 0) {
238 var err = try wasm_file.addErrorWithNotes(1);
239 try err.addMsg(wasm_file, "Expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
238 var err = try wasm_file.base.addErrorWithNotes(1);
239 try err.addMsg("Expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
240240 object.imported_tables_count,
241241 table_count,
242242 });
243 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
243 try err.addNote("defined in '{s}'", .{object.path});
244244 return error.MissingTableSymbols;
245245 }
246246
247247 // MVP object files cannot have any table definitions, only imports (for the indirect function table).
248248 if (object.tables.len > 0) {
249 var err = try wasm_file.addErrorWithNotes(1);
250 try err.addMsg(wasm_file, "Unexpected table definition without representing table symbols.", .{});
251 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
249 var err = try wasm_file.base.addErrorWithNotes(1);
250 try err.addMsg("Unexpected table definition without representing table symbols.", .{});
251 try err.addNote("defined in '{s}'", .{object.path});
252252 return error.UnexpectedTable;
253253 }
254254
255255 if (object.imported_tables_count != 1) {
256 var err = try wasm_file.addErrorWithNotes(1);
257 try err.addMsg(wasm_file, "Found more than one table import, but no representing table symbols", .{});
258 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
256 var err = try wasm_file.base.addErrorWithNotes(1);
257 try err.addMsg("Found more than one table import, but no representing table symbols", .{});
258 try err.addNote("defined in '{s}'", .{object.path});
259259 return error.MissingTableSymbols;
260260 }
261261
......@@ -266,9 +266,9 @@ fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?S
266266 } else unreachable;
267267
268268 if (!std.mem.eql(u8, object.string_table.get(table_import.name), "__indirect_function_table")) {
269 var err = try wasm_file.addErrorWithNotes(1);
270 try err.addMsg(wasm_file, "Non-indirect function table import '{s}' is missing a corresponding symbol", .{object.string_table.get(table_import.name)});
271 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
269 var err = try wasm_file.base.addErrorWithNotes(1);
270 try err.addMsg("Non-indirect function table import '{s}' is missing a corresponding symbol", .{object.string_table.get(table_import.name)});
271 try err.addNote("defined in '{s}'", .{object.path});
272272 return error.MissingTableSymbols;
273273 }
274274
......@@ -596,9 +596,9 @@ fn Parser(comptime ReaderType: type) type {
596596 try reader.readNoEof(name);
597597
598598 const tag = types.known_features.get(name) orelse {
599 var err = try parser.wasm_file.addErrorWithNotes(1);
600 try err.addMsg(parser.wasm_file, "Object file contains unknown feature: {s}", .{name});
601 try err.addNote(parser.wasm_file, "defined in '{s}'", .{parser.object.path});
599 var err = try parser.wasm_file.base.addErrorWithNotes(1);
600 try err.addMsg("Object file contains unknown feature: {s}", .{name});
601 try err.addNote("defined in '{s}'", .{parser.object.path});
602602 return error.UnknownFeature;
603603 };
604604 feature.* = .{
src/link/tapi.zig+9-3
......@@ -129,8 +129,8 @@ pub const Tbd = union(enum) {
129129
130130pub const TapiError = error{
131131 NotLibStub,
132 FileTooBig,
133} || yaml.YamlError || std.fs.File.ReadError;
132 InputOutput,
133} || yaml.YamlError || std.fs.File.PReadError;
134134
135135pub const LibStub = struct {
136136 /// Underlying memory for stub's contents.
......@@ -140,8 +140,14 @@ pub const LibStub = struct {
140140 inner: []Tbd,
141141
142142 pub fn loadFromFile(allocator: Allocator, file: fs.File) TapiError!LibStub {
143 const source = try file.readToEndAlloc(allocator, std.math.maxInt(u32));
143 const filesize = blk: {
144 const stat = file.stat() catch break :blk std.math.maxInt(u32);
145 break :blk @min(stat.size, std.math.maxInt(u32));
146 };
147 const source = try allocator.alloc(u8, filesize);
144148 defer allocator.free(source);
149 const amt = try file.preadAll(source, 0);
150 if (amt != filesize) return error.InputOutput;
145151
146152 var lib_stub = LibStub{
147153 .yaml = try Yaml.load(allocator, source),
src/main.zig+3
......@@ -211,6 +211,9 @@ fn verifyLibcxxCorrectlyLinked() void {
211211}
212212
213213fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
214 const tr = tracy.trace(@src());
215 defer tr.end();
216
214217 if (args.len <= 1) {
215218 std.log.info("{s}", .{usage});
216219 fatal("expected command argument", .{});