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...@@ -618,7 +618,7 @@ fn addCompilerStep(b: *std.Build, options: AddCompilerStepOptions) *std.Build.St
618 .root_source_file = b.path("src/main.zig"),618 .root_source_file = b.path("src/main.zig"),
619 .target = options.target,619 .target = options.target,
620 .optimize = options.optimize,620 .optimize = options.optimize,
621 .max_rss = 7_100_000_000,621 .max_rss = 7_500_000_000,
622 .strip = options.strip,622 .strip = options.strip,
623 .sanitize_thread = options.sanitize_thread,623 .sanitize_thread = options.sanitize_thread,
624 .single_threaded = options.single_threaded,624 .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...@@ -105,8 +105,9 @@ win32_resource_table: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMa
105 pub fn deinit(_: @This(), _: Allocator) void {}105 pub fn deinit(_: @This(), _: Allocator) void {}
106} = .{},106} = .{},
107107
108link_error_flags: link.File.ErrorFlags = .{},
109link_errors: std.ArrayListUnmanaged(link.File.ErrorMsg) = .{},108link_errors: std.ArrayListUnmanaged(link.File.ErrorMsg) = .{},
109link_errors_mutex: std.Thread.Mutex = .{},
110link_error_flags: link.File.ErrorFlags = .{},
110lld_errors: std.ArrayListUnmanaged(LldError) = .{},111lld_errors: std.ArrayListUnmanaged(LldError) = .{},
111112
112work_queues: [113work_queues: [
...@@ -3067,7 +3068,6 @@ pub fn totalErrorCount(comp: *Compilation) u32 {...@@ -3067,7 +3068,6 @@ pub fn totalErrorCount(comp: *Compilation) u32 {
3067 total += @intFromBool(comp.link_error_flags.no_entry_point_found);3068 total += @intFromBool(comp.link_error_flags.no_entry_point_found);
3068 }3069 }
3069 total += @intFromBool(comp.link_error_flags.missing_libc);3070 total += @intFromBool(comp.link_error_flags.missing_libc);
3070
3071 total += comp.link_errors.items.len;3071 total += comp.link_errors.items.len;
30723072
3073 // Compile log errors only count if there are no other errors.3073 // Compile log errors only count if there are no other errors.
src/arch/x86_64/Emit.zig+3-3
...@@ -163,12 +163,12 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -163,12 +163,12 @@ pub fn emitMir(emit: *Emit) Error!void {
163 const zo = macho_file.getZigObject().?;163 const zo = macho_file.getZigObject().?;
164 const atom = zo.symbols.items[data.atom_index].getAtom(macho_file).?;164 const atom = zo.symbols.items[data.atom_index].getAtom(macho_file).?;
165 const sym = &zo.symbols.items[data.sym_index];165 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) {
167 _ = try sym.getOrCreateZigGotEntry(data.sym_index, macho_file);167 _ = try sym.getOrCreateZigGotEntry(data.sym_index, macho_file);
168 }168 }
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)
170 .zig_got_load170 .zig_got_load
171 else if (sym.flags.needs_got)171 else if (sym.getSectionFlags().needs_got)
172 // TODO: it is possible to emit .got_load here that can potentially be relaxed172 // TODO: it is possible to emit .got_load here that can potentially be relaxed
173 // however this requires always to use a MOVQ mnemonic173 // however this requires always to use a MOVQ mnemonic
174 .got174 .got
src/arch/x86_64/Lower.zig+1-1
...@@ -451,7 +451,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)...@@ -451,7 +451,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
451 break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) };451 break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) };
452 },452 },
453 .mov => {453 .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;
455 break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) };455 break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) };
456 },456 },
457 else => unreachable,457 else => unreachable,
src/codegen.zig+1-1
...@@ -924,7 +924,7 @@ fn genDeclRef(...@@ -924,7 +924,7 @@ fn genDeclRef(
924 const name = decl.name.toSlice(ip);924 const name = decl.name.toSlice(ip);
925 const lib_name = if (decl.getOwnedVariable(zcu)) |ov| ov.lib_name.toSlice(ip) else null;925 const lib_name = if (decl.getOwnedVariable(zcu)) |ov| ov.lib_name.toSlice(ip) else null;
926 const sym_index = try macho_file.getGlobalSymbol(name, lib_name);926 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 });
928 return GenResult.mcv(.{ .load_symbol = sym_index });928 return GenResult.mcv(.{ .load_symbol = sym_index });
929 }929 }
930 const sym_index = try zo.getOrCreateMetadataForDecl(macho_file, decl_index);930 const sym_index = try zo.getOrCreateMetadataForDecl(macho_file, decl_index);
src/link.zig+67-1
...@@ -439,6 +439,58 @@ pub const File = struct {...@@ -439,6 +439,58 @@ pub const File = struct {
439 }439 }
440 }440 }
441441
442 pub const ErrorWithNotes = struct {
443 base: *const File,
444
445 /// Allocated index in base.errors array.
446 index: usize,
447
448 /// Next available note slot.
449 note_slot: usize = 0,
450
451 pub fn addMsg(
452 err: ErrorWithNotes,
453 comptime format: []const u8,
454 args: anytype,
455 ) error{OutOfMemory}!void {
456 const gpa = err.base.comp.gpa;
457 const err_msg = &err.base.comp.link_errors.items[err.index];
458 err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
459 }
460
461 pub fn addNote(
462 err: *ErrorWithNotes,
463 comptime format: []const u8,
464 args: anytype,
465 ) error{OutOfMemory}!void {
466 const gpa = err.base.comp.gpa;
467 const err_msg = &err.base.comp.link_errors.items[err.index];
468 assert(err.note_slot < err_msg.notes.len);
469 err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };
470 err.note_slot += 1;
471 }
472 };
473
474 pub fn addErrorWithNotes(base: *const File, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
475 base.comp.link_errors_mutex.lock();
476 defer base.comp.link_errors_mutex.unlock();
477 const gpa = base.comp.gpa;
478 try base.comp.link_errors.ensureUnusedCapacity(gpa, 1);
479 return base.addErrorWithNotesAssumeCapacity(note_count);
480 }
481
482 pub fn addErrorWithNotesAssumeCapacity(base: *const File, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
483 const gpa = base.comp.gpa;
484 const index = base.comp.link_errors.items.len;
485 const err = base.comp.link_errors.addOneAssumeCapacity();
486 err.* = .{ .msg = undefined, .notes = try gpa.alloc(ErrorMsg, note_count) };
487 return .{ .base = base, .index = index };
488 }
489
490 pub fn hasErrors(base: *const File) bool {
491 return base.comp.link_errors.items.len > 0 or base.comp.link_error_flags.isSet();
492 }
493
442 pub fn releaseLock(self: *File) void {494 pub fn releaseLock(self: *File) void {
443 if (self.lock) |*lock| {495 if (self.lock) |*lock| {
444 lock.release();496 lock.release();
...@@ -874,9 +926,23 @@ pub const File = struct {...@@ -874,9 +926,23 @@ pub const File = struct {
874 }926 }
875 };927 };
876928
877 pub const ErrorFlags = struct {929 pub const ErrorFlags = packed struct {
878 no_entry_point_found: bool = false,930 no_entry_point_found: bool = false,
879 missing_libc: bool = false,931 missing_libc: bool = false,
932
933 const Int = blk: {
934 const bits = @typeInfo(@This()).Struct.fields.len;
935 break :blk @Type(.{
936 .Int = .{
937 .signedness = .unsigned,
938 .bits = bits,
939 },
940 });
941 };
942
943 fn isSet(ef: ErrorFlags) bool {
944 return @as(Int, @bitCast(ef)) > 0;
945 }
880 };946 };
881947
882 pub const ErrorMsg = struct {948 pub const ErrorMsg = struct {
src/link/Elf.zig+33-84
...@@ -995,12 +995,12 @@ pub fn growAllocSection(self: *Elf, shdr_index: u32, needed_size: u64) !void {...@@ -995,12 +995,12 @@ pub fn growAllocSection(self: *Elf, shdr_index: u32, needed_size: u64) !void {
995 if (maybe_phdr) |phdr| {995 if (maybe_phdr) |phdr| {
996 const mem_capacity = self.allocatedVirtualSize(phdr.p_vaddr);996 const mem_capacity = self.allocatedVirtualSize(phdr.p_vaddr);
997 if (needed_size > mem_capacity) {997 if (needed_size > mem_capacity) {
998 var err = try self.addErrorWithNotes(2);998 var err = try self.base.addErrorWithNotes(2);
999 try err.addMsg(self, "fatal linker error: cannot expand load segment phdr({d}) in virtual memory", .{999 try err.addMsg("fatal linker error: cannot expand load segment phdr({d}) in virtual memory", .{
1000 self.phdr_to_shdr_table.get(shdr_index).?,1000 self.phdr_to_shdr_table.get(shdr_index).?,
1001 });1001 });
1002 try err.addNote(self, "TODO: emit relocations to memory locations in self-hosted backends", .{});1002 try err.addNote("TODO: emit relocations to memory locations in self-hosted backends", .{});
1003 try err.addNote(self, "as a workaround, try increasing pre-allocated virtual memory of each segment", .{});1003 try err.addNote("as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
1004 }1004 }
10051005
1006 phdr.p_memsz = needed_size;1006 phdr.p_memsz = needed_size;
...@@ -1276,7 +1276,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -1276,7 +1276,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
1276 };1276 };
1277 }1277 }
12781278
1279 if (comp.link_errors.items.len > 0) return error.FlushFailure;1279 if (self.base.hasErrors()) return error.FlushFailure;
12801280
1281 // Dedup shared objects1281 // Dedup shared objects
1282 {1282 {
...@@ -1423,7 +1423,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod...@@ -1423,7 +1423,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
1423 try self.writeElfHeader();1423 try self.writeElfHeader();
1424 }1424 }
14251425
1426 if (comp.link_errors.items.len > 0) return error.FlushFailure;1426 if (self.base.hasErrors()) return error.FlushFailure;
1427}1427}
14281428
1429/// --verbose-link output1429/// --verbose-link output
...@@ -2852,9 +2852,9 @@ fn writePhdrTable(self: *Elf) !void {...@@ -2852,9 +2852,9 @@ fn writePhdrTable(self: *Elf) !void {
2852}2852}
28532853
2854pub fn writeElfHeader(self: *Elf) !void {2854pub fn writeElfHeader(self: *Elf) !void {
2855 const comp = self.base.comp;2855 if (self.base.hasErrors()) return; // We had errors, so skip flushing to render the output unusable
2856 if (comp.link_errors.items.len > 0) return; // We had errors, so skip flushing to render the output unusable
28572856
2857 const comp = self.base.comp;
2858 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;2858 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
28592859
2860 var index: usize = 0;2860 var index: usize = 0;
...@@ -4298,9 +4298,9 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {...@@ -4298,9 +4298,9 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {
4298 // (revisit getMaxNumberOfPhdrs())4298 // (revisit getMaxNumberOfPhdrs())
4299 // 2. shift everything in file to free more space for EHDR + PHDR table4299 // 2. shift everything in file to free more space for EHDR + PHDR table
4300 // TODO verify `getMaxNumberOfPhdrs()` is accurate and convert this into no-op4300 // TODO verify `getMaxNumberOfPhdrs()` is accurate and convert this into no-op
4301 var err = try self.addErrorWithNotes(1);4301 var err = try self.base.addErrorWithNotes(1);
4302 try err.addMsg(self, "fatal linker error: not enough space reserved for EHDR and PHDR table", .{});4302 try err.addMsg("fatal linker error: not enough space reserved for EHDR and PHDR table", .{});
4303 try err.addNote(self, "required 0x{x}, available 0x{x}", .{ needed_size, available_space });4303 try err.addNote("required 0x{x}, available 0x{x}", .{ needed_size, available_space });
4304 }4304 }
43054305
4306 phdr_table_load.p_filesz = needed_size + ehsize;4306 phdr_table_load.p_filesz = needed_size + ehsize;
...@@ -5863,56 +5863,6 @@ pub fn tlsAddress(self: *Elf) i64 {...@@ -5863,56 +5863,6 @@ pub fn tlsAddress(self: *Elf) i64 {
5863 return @intCast(phdr.p_vaddr);5863 return @intCast(phdr.p_vaddr);
5864}5864}
58655865
5866const ErrorWithNotes = struct {
5867 /// Allocated index in comp.link_errors array.
5868 index: usize,
5869
5870 /// Next available note slot.
5871 note_slot: usize = 0,
5872
5873 pub fn addMsg(
5874 err: ErrorWithNotes,
5875 elf_file: *Elf,
5876 comptime format: []const u8,
5877 args: anytype,
5878 ) error{OutOfMemory}!void {
5879 const comp = elf_file.base.comp;
5880 const gpa = comp.gpa;
5881 const err_msg = &comp.link_errors.items[err.index];
5882 err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
5883 }
5884
5885 pub fn addNote(
5886 err: *ErrorWithNotes,
5887 elf_file: *Elf,
5888 comptime format: []const u8,
5889 args: anytype,
5890 ) error{OutOfMemory}!void {
5891 const comp = elf_file.base.comp;
5892 const gpa = comp.gpa;
5893 const err_msg = &comp.link_errors.items[err.index];
5894 assert(err.note_slot < err_msg.notes.len);
5895 err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };
5896 err.note_slot += 1;
5897 }
5898};
5899
5900pub fn addErrorWithNotes(self: *Elf, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
5901 const comp = self.base.comp;
5902 const gpa = comp.gpa;
5903 try comp.link_errors.ensureUnusedCapacity(gpa, 1);
5904 return self.addErrorWithNotesAssumeCapacity(note_count);
5905}
5906
5907fn addErrorWithNotesAssumeCapacity(self: *Elf, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
5908 const comp = self.base.comp;
5909 const gpa = comp.gpa;
5910 const index = comp.link_errors.items.len;
5911 const err = comp.link_errors.addOneAssumeCapacity();
5912 err.* = .{ .msg = undefined, .notes = try gpa.alloc(link.File.ErrorMsg, note_count) };
5913 return .{ .index = index };
5914}
5915
5916pub fn getShString(self: Elf, off: u32) [:0]const u8 {5866pub fn getShString(self: Elf, off: u32) [:0]const u8 {
5917 assert(off < self.shstrtab.items.len);5867 assert(off < self.shstrtab.items.len);
5918 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.shstrtab.items.ptr + off)), 0);5868 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.shstrtab.items.ptr + off)), 0);
...@@ -5940,11 +5890,10 @@ pub fn insertDynString(self: *Elf, name: []const u8) error{OutOfMemory}!u32 {...@@ -5940,11 +5890,10 @@ pub fn insertDynString(self: *Elf, name: []const u8) error{OutOfMemory}!u32 {
5940}5890}
59415891
5942fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {5892fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
5943 const comp = self.base.comp;5893 const gpa = self.base.comp.gpa;
5944 const gpa = comp.gpa;
5945 const max_notes = 4;5894 const max_notes = 4;
59465895
5947 try comp.link_errors.ensureUnusedCapacity(gpa, undefs.count());5896 try self.base.comp.link_errors.ensureUnusedCapacity(gpa, undefs.count());
59485897
5949 var it = undefs.iterator();5898 var it = undefs.iterator();
5950 while (it.next()) |entry| {5899 while (it.next()) |entry| {
...@@ -5953,18 +5902,18 @@ fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {...@@ -5953,18 +5902,18 @@ fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
5953 const natoms = @min(atoms.len, max_notes);5902 const natoms = @min(atoms.len, max_notes);
5954 const nnotes = natoms + @intFromBool(atoms.len > max_notes);5903 const nnotes = natoms + @intFromBool(atoms.len > max_notes);
59555904
5956 var err = try self.addErrorWithNotesAssumeCapacity(nnotes);5905 var err = try self.base.addErrorWithNotesAssumeCapacity(nnotes);
5957 try err.addMsg(self, "undefined symbol: {s}", .{self.symbol(undef_index).name(self)});5906 try err.addMsg("undefined symbol: {s}", .{self.symbol(undef_index).name(self)});
59585907
5959 for (atoms[0..natoms]) |atom_index| {5908 for (atoms[0..natoms]) |atom_index| {
5960 const atom_ptr = self.atom(atom_index).?;5909 const atom_ptr = self.atom(atom_index).?;
5961 const file_ptr = self.file(atom_ptr.file_index).?;5910 const file_ptr = self.file(atom_ptr.file_index).?;
5962 try err.addNote(self, "referenced by {s}:{s}", .{ file_ptr.fmtPath(), atom_ptr.name(self) });5911 try err.addNote("referenced by {s}:{s}", .{ file_ptr.fmtPath(), atom_ptr.name(self) });
5963 }5912 }
59645913
5965 if (atoms.len > max_notes) {5914 if (atoms.len > max_notes) {
5966 const remaining = atoms.len - max_notes;5915 const remaining = atoms.len - max_notes;
5967 try err.addNote(self, "referenced {d} more times", .{remaining});5916 try err.addNote("referenced {d} more times", .{remaining});
5968 }5917 }
5969 }5918 }
5970}5919}
...@@ -5978,19 +5927,19 @@ fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemor...@@ -5978,19 +5927,19 @@ fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemor
5978 const notes = entry.value_ptr.*;5927 const notes = entry.value_ptr.*;
5979 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);5928 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
59805929
5981 var err = try self.addErrorWithNotes(nnotes + 1);5930 var err = try self.base.addErrorWithNotes(nnotes + 1);
5982 try err.addMsg(self, "duplicate symbol definition: {s}", .{sym.name(self)});5931 try err.addMsg("duplicate symbol definition: {s}", .{sym.name(self)});
5983 try err.addNote(self, "defined by {}", .{sym.file(self).?.fmtPath()});5932 try err.addNote("defined by {}", .{sym.file(self).?.fmtPath()});
59845933
5985 var inote: usize = 0;5934 var inote: usize = 0;
5986 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {5935 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
5987 const file_ptr = self.file(notes.items[inote]).?;5936 const file_ptr = self.file(notes.items[inote]).?;
5988 try err.addNote(self, "defined by {}", .{file_ptr.fmtPath()});5937 try err.addNote("defined by {}", .{file_ptr.fmtPath()});
5989 }5938 }
59905939
5991 if (notes.items.len > max_notes) {5940 if (notes.items.len > max_notes) {
5992 const remaining = notes.items.len - max_notes;5941 const remaining = notes.items.len - max_notes;
5993 try err.addNote(self, "defined {d} more times", .{remaining});5942 try err.addNote("defined {d} more times", .{remaining});
5994 }5943 }
59955944
5996 has_dupes = true;5945 has_dupes = true;
...@@ -6005,16 +5954,16 @@ fn reportMissingLibraryError(...@@ -6005,16 +5954,16 @@ fn reportMissingLibraryError(
6005 comptime format: []const u8,5954 comptime format: []const u8,
6006 args: anytype,5955 args: anytype,
6007) error{OutOfMemory}!void {5956) error{OutOfMemory}!void {
6008 var err = try self.addErrorWithNotes(checked_paths.len);5957 var err = try self.base.addErrorWithNotes(checked_paths.len);
6009 try err.addMsg(self, format, args);5958 try err.addMsg(format, args);
6010 for (checked_paths) |path| {5959 for (checked_paths) |path| {
6011 try err.addNote(self, "tried {s}", .{path});5960 try err.addNote("tried {s}", .{path});
6012 }5961 }
6013}5962}
60145963
6015pub fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void {5964pub fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void {
6016 var err = try self.addErrorWithNotes(0);5965 var err = try self.base.addErrorWithNotes(0);
6017 try err.addMsg(self, "fatal linker error: unsupported CPU architecture {s}", .{5966 try err.addMsg("fatal linker error: unsupported CPU architecture {s}", .{
6018 @tagName(self.getTarget().cpu.arch),5967 @tagName(self.getTarget().cpu.arch),
6019 });5968 });
6020}5969}
...@@ -6025,9 +5974,9 @@ pub fn reportParseError(...@@ -6025,9 +5974,9 @@ pub fn reportParseError(
6025 comptime format: []const u8,5974 comptime format: []const u8,
6026 args: anytype,5975 args: anytype,
6027) error{OutOfMemory}!void {5976) error{OutOfMemory}!void {
6028 var err = try self.addErrorWithNotes(1);5977 var err = try self.base.addErrorWithNotes(1);
6029 try err.addMsg(self, format, args);5978 try err.addMsg(format, args);
6030 try err.addNote(self, "while parsing {s}", .{path});5979 try err.addNote("while parsing {s}", .{path});
6031}5980}
60325981
6033pub fn reportParseError2(5982pub fn reportParseError2(
...@@ -6036,9 +5985,9 @@ pub fn reportParseError2(...@@ -6036,9 +5985,9 @@ pub fn reportParseError2(
6036 comptime format: []const u8,5985 comptime format: []const u8,
6037 args: anytype,5986 args: anytype,
6038) error{OutOfMemory}!void {5987) error{OutOfMemory}!void {
6039 var err = try self.addErrorWithNotes(1);5988 var err = try self.base.addErrorWithNotes(1);
6040 try err.addMsg(self, format, args);5989 try err.addMsg(format, args);
6041 try err.addNote(self, "while parsing {}", .{self.file(file_index).?.fmtPath()});5990 try err.addNote("while parsing {}", .{self.file(file_index).?.fmtPath()});
6042}5991}
60435992
6044const FormatShdrCtx = struct {5993const FormatShdrCtx = struct {
src/link/Elf/Atom.zig+32-44
...@@ -631,15 +631,12 @@ fn dataType(symbol: *const Symbol, elf_file: *Elf) u2 {...@@ -631,15 +631,12 @@ fn dataType(symbol: *const Symbol, elf_file: *Elf) u2 {
631}631}
632632
633fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) RelocError!void {633fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) RelocError!void {
634 var err = try elf_file.addErrorWithNotes(1);634 var err = try elf_file.base.addErrorWithNotes(1);
635 try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {} at offset 0x{x}", .{635 try err.addMsg("fatal linker error: unhandled relocation type {} at offset 0x{x}", .{
636 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),636 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),
637 rel.r_offset,637 rel.r_offset,
638 });638 });
639 try err.addNote(elf_file, "in {}:{s}", .{639 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
640 self.file(elf_file).?.fmtPath(),
641 self.name(elf_file),
642 });
643 return error.RelocFailure;640 return error.RelocFailure;
644}641}
645642
...@@ -649,15 +646,12 @@ fn reportTextRelocError(...@@ -649,15 +646,12 @@ fn reportTextRelocError(
649 rel: elf.Elf64_Rela,646 rel: elf.Elf64_Rela,
650 elf_file: *Elf,647 elf_file: *Elf,
651) RelocError!void {648) RelocError!void {
652 var err = try elf_file.addErrorWithNotes(1);649 var err = try elf_file.base.addErrorWithNotes(1);
653 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{650 try err.addMsg("relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
654 rel.r_offset,651 rel.r_offset,
655 symbol.name(elf_file),652 symbol.name(elf_file),
656 });653 });
657 try err.addNote(elf_file, "in {}:{s}", .{654 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
658 self.file(elf_file).?.fmtPath(),
659 self.name(elf_file),
660 });
661 return error.RelocFailure;655 return error.RelocFailure;
662}656}
663657
...@@ -667,16 +661,13 @@ fn reportPicError(...@@ -667,16 +661,13 @@ fn reportPicError(
667 rel: elf.Elf64_Rela,661 rel: elf.Elf64_Rela,
668 elf_file: *Elf,662 elf_file: *Elf,
669) RelocError!void {663) RelocError!void {
670 var err = try elf_file.addErrorWithNotes(2);664 var err = try elf_file.base.addErrorWithNotes(2);
671 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{665 try err.addMsg("relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
672 rel.r_offset,666 rel.r_offset,
673 symbol.name(elf_file),667 symbol.name(elf_file),
674 });668 });
675 try err.addNote(elf_file, "in {}:{s}", .{669 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
676 self.file(elf_file).?.fmtPath(),670 try err.addNote("recompile with -fPIC", .{});
677 self.name(elf_file),
678 });
679 try err.addNote(elf_file, "recompile with -fPIC", .{});
680 return error.RelocFailure;671 return error.RelocFailure;
681}672}
682673
...@@ -686,16 +677,13 @@ fn reportNoPicError(...@@ -686,16 +677,13 @@ fn reportNoPicError(
686 rel: elf.Elf64_Rela,677 rel: elf.Elf64_Rela,
687 elf_file: *Elf,678 elf_file: *Elf,
688) RelocError!void {679) RelocError!void {
689 var err = try elf_file.addErrorWithNotes(2);680 var err = try elf_file.base.addErrorWithNotes(2);
690 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{681 try err.addMsg("relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
691 rel.r_offset,682 rel.r_offset,
692 symbol.name(elf_file),683 symbol.name(elf_file),
693 });684 });
694 try err.addNote(elf_file, "in {}:{s}", .{685 try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
695 self.file(elf_file).?.fmtPath(),686 try err.addNote("recompile with -fno-PIC", .{});
696 self.name(elf_file),
697 });
698 try err.addNote(elf_file, "recompile with -fno-PIC", .{});
699 return error.RelocFailure;687 return error.RelocFailure;
700}688}
701689
...@@ -1332,9 +1320,9 @@ const x86_64 = struct {...@@ -1332,9 +1320,9 @@ const x86_64 = struct {
1332 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);1320 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
1333 } else {1321 } else {
1334 x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]) catch {1322 x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]) catch {
1335 var err = try elf_file.addErrorWithNotes(1);1323 var err = try elf_file.base.addErrorWithNotes(1);
1336 try err.addMsg(elf_file, "could not relax {s}", .{@tagName(r_type)});1324 try err.addMsg("could not relax {s}", .{@tagName(r_type)});
1337 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{1325 try err.addNote("in {}:{s} at offset 0x{x}", .{
1338 atom.file(elf_file).?.fmtPath(),1326 atom.file(elf_file).?.fmtPath(),
1339 atom.name(elf_file),1327 atom.name(elf_file),
1340 rel.r_offset,1328 rel.r_offset,
...@@ -1479,12 +1467,12 @@ const x86_64 = struct {...@@ -1479,12 +1467,12 @@ const x86_64 = struct {
1479 },1467 },
14801468
1481 else => {1469 else => {
1482 var err = try elf_file.addErrorWithNotes(1);1470 var err = try elf_file.base.addErrorWithNotes(1);
1483 try err.addMsg(elf_file, "TODO: rewrite {} when followed by {}", .{1471 try err.addMsg("TODO: rewrite {} when followed by {}", .{
1484 relocation.fmtRelocType(rels[0].r_type(), .x86_64),1472 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
1485 relocation.fmtRelocType(rels[1].r_type(), .x86_64),1473 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
1486 });1474 });
1487 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{1475 try err.addNote("in {}:{s} at offset 0x{x}", .{
1488 self.file(elf_file).?.fmtPath(),1476 self.file(elf_file).?.fmtPath(),
1489 self.name(elf_file),1477 self.name(elf_file),
1490 rels[0].r_offset,1478 rels[0].r_offset,
...@@ -1534,12 +1522,12 @@ const x86_64 = struct {...@@ -1534,12 +1522,12 @@ const x86_64 = struct {
1534 },1522 },
15351523
1536 else => {1524 else => {
1537 var err = try elf_file.addErrorWithNotes(1);1525 var err = try elf_file.base.addErrorWithNotes(1);
1538 try err.addMsg(elf_file, "TODO: rewrite {} when followed by {}", .{1526 try err.addMsg("TODO: rewrite {} when followed by {}", .{
1539 relocation.fmtRelocType(rels[0].r_type(), .x86_64),1527 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
1540 relocation.fmtRelocType(rels[1].r_type(), .x86_64),1528 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
1541 });1529 });
1542 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{1530 try err.addNote("in {}:{s} at offset 0x{x}", .{
1543 self.file(elf_file).?.fmtPath(),1531 self.file(elf_file).?.fmtPath(),
1544 self.name(elf_file),1532 self.name(elf_file),
1545 rels[0].r_offset,1533 rels[0].r_offset,
...@@ -1630,12 +1618,12 @@ const x86_64 = struct {...@@ -1630,12 +1618,12 @@ const x86_64 = struct {
1630 },1618 },
16311619
1632 else => {1620 else => {
1633 var err = try elf_file.addErrorWithNotes(1);1621 var err = try elf_file.base.addErrorWithNotes(1);
1634 try err.addMsg(elf_file, "fatal linker error: rewrite {} when followed by {}", .{1622 try err.addMsg("fatal linker error: rewrite {} when followed by {}", .{
1635 relocation.fmtRelocType(rels[0].r_type(), .x86_64),1623 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
1636 relocation.fmtRelocType(rels[1].r_type(), .x86_64),1624 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
1637 });1625 });
1638 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{1626 try err.addNote("in {}:{s} at offset 0x{x}", .{
1639 self.file(elf_file).?.fmtPath(),1627 self.file(elf_file).?.fmtPath(),
1640 self.name(elf_file),1628 self.name(elf_file),
1641 rels[0].r_offset,1629 rels[0].r_offset,
...@@ -1824,9 +1812,9 @@ const aarch64 = struct {...@@ -1824,9 +1812,9 @@ const aarch64 = struct {
1824 aarch64_util.writeAdrpInst(pages, code);1812 aarch64_util.writeAdrpInst(pages, code);
1825 } else {1813 } else {
1826 // TODO: relax1814 // TODO: relax
1827 var err = try elf_file.addErrorWithNotes(1);1815 var err = try elf_file.base.addErrorWithNotes(1);
1828 try err.addMsg(elf_file, "TODO: relax ADR_GOT_PAGE", .{});1816 try err.addMsg("TODO: relax ADR_GOT_PAGE", .{});
1829 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{1817 try err.addNote("in {}:{s} at offset 0x{x}", .{
1830 atom.file(elf_file).?.fmtPath(),1818 atom.file(elf_file).?.fmtPath(),
1831 atom.name(elf_file),1819 atom.name(elf_file),
1832 r_offset,1820 r_offset,
...@@ -2118,9 +2106,9 @@ const riscv = struct {...@@ -2118,9 +2106,9 @@ const riscv = struct {
2118 if (S == atom_addr + @as(i64, @intCast(pair.r_offset))) break pair;2106 if (S == atom_addr + @as(i64, @intCast(pair.r_offset))) break pair;
2119 } else {2107 } else {
2120 // TODO: implement searching forward2108 // TODO: implement searching forward
2121 var err = try elf_file.addErrorWithNotes(1);2109 var err = try elf_file.base.addErrorWithNotes(1);
2122 try err.addMsg(elf_file, "TODO: find HI20 paired reloc scanning forward", .{});2110 try err.addMsg("TODO: find HI20 paired reloc scanning forward", .{});
2123 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{2111 try err.addNote("in {}:{s} at offset 0x{x}", .{
2124 atom.file(elf_file).?.fmtPath(),2112 atom.file(elf_file).?.fmtPath(),
2125 atom.name(elf_file),2113 atom.name(elf_file),
2126 rel.r_offset,2114 rel.r_offset,
src/link/Elf/Object.zig+13-13
...@@ -704,9 +704,9 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void {...@@ -704,9 +704,9 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void {
704 var end = start;704 var end = start;
705 while (end < data.len - sh_entsize and !isNull(data[end .. end + sh_entsize])) : (end += sh_entsize) {}705 while (end < data.len - sh_entsize and !isNull(data[end .. end + sh_entsize])) : (end += sh_entsize) {}
706 if (!isNull(data[end .. end + sh_entsize])) {706 if (!isNull(data[end .. end + sh_entsize])) {
707 var err = try elf_file.addErrorWithNotes(1);707 var err = try elf_file.base.addErrorWithNotes(1);
708 try err.addMsg(elf_file, "string not null terminated", .{});708 try err.addMsg("string not null terminated", .{});
709 try err.addNote(elf_file, "in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });709 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
710 return error.MalformedObject;710 return error.MalformedObject;
711 }711 }
712 end += sh_entsize;712 end += sh_entsize;
...@@ -719,9 +719,9 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void {...@@ -719,9 +719,9 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void {
719 const sh_entsize: u32 = @intCast(shdr.sh_entsize);719 const sh_entsize: u32 = @intCast(shdr.sh_entsize);
720 if (sh_entsize == 0) continue; // Malformed, don't split but don't error out720 if (sh_entsize == 0) continue; // Malformed, don't split but don't error out
721 if (shdr.sh_size % sh_entsize != 0) {721 if (shdr.sh_size % sh_entsize != 0) {
722 var err = try elf_file.addErrorWithNotes(1);722 var err = try elf_file.base.addErrorWithNotes(1);
723 try err.addMsg(elf_file, "size not a multiple of sh_entsize", .{});723 try err.addMsg("size not a multiple of sh_entsize", .{});
724 try err.addNote(elf_file, "in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });724 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
725 return error.MalformedObject;725 return error.MalformedObject;
726 }726 }
727727
...@@ -779,10 +779,10 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {...@@ -779,10 +779,10 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
779 const imsec = elf_file.inputMergeSection(imsec_index) orelse continue;779 const imsec = elf_file.inputMergeSection(imsec_index) orelse continue;
780 if (imsec.offsets.items.len == 0) continue;780 if (imsec.offsets.items.len == 0) continue;
781 const msub_index, const offset = imsec.findSubsection(@intCast(esym.st_value)) orelse {781 const msub_index, const offset = imsec.findSubsection(@intCast(esym.st_value)) orelse {
782 var err = try elf_file.addErrorWithNotes(2);782 var err = try elf_file.base.addErrorWithNotes(2);
783 try err.addMsg(elf_file, "invalid symbol value: {x}", .{esym.st_value});783 try err.addMsg("invalid symbol value: {x}", .{esym.st_value});
784 try err.addNote(elf_file, "for symbol {s}", .{sym.name(elf_file)});784 try err.addNote("for symbol {s}", .{sym.name(elf_file)});
785 try err.addNote(elf_file, "in {}", .{self.fmtPath()});785 try err.addNote("in {}", .{self.fmtPath()});
786 return error.MalformedObject;786 return error.MalformedObject;
787 };787 };
788788
...@@ -804,9 +804,9 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {...@@ -804,9 +804,9 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
804 const imsec = elf_file.inputMergeSection(imsec_index) orelse continue;804 const imsec = elf_file.inputMergeSection(imsec_index) orelse continue;
805 if (imsec.offsets.items.len == 0) continue;805 if (imsec.offsets.items.len == 0) continue;
806 const msub_index, const offset = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse {806 const msub_index, const offset = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse {
807 var err = try elf_file.addErrorWithNotes(1);807 var err = try elf_file.base.addErrorWithNotes(1);
808 try err.addMsg(elf_file, "invalid relocation at offset 0x{x}", .{rel.r_offset});808 try err.addMsg("invalid relocation at offset 0x{x}", .{rel.r_offset});
809 try err.addNote(elf_file, "in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });809 try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
810 return error.MalformedObject;810 return error.MalformedObject;
811 };811 };
812 const msub = elf_file.mergeSubsection(msub_index);812 const msub = elf_file.mergeSubsection(msub_index);
src/link/Elf/eh_frame.zig+3-3
...@@ -591,12 +591,12 @@ const riscv = struct {...@@ -591,12 +591,12 @@ const riscv = struct {
591};591};
592592
593fn reportInvalidReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela) !void {593fn reportInvalidReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela) !void {
594 var err = try elf_file.addErrorWithNotes(1);594 var err = try elf_file.base.addErrorWithNotes(1);
595 try err.addMsg(elf_file, "invalid relocation type {} at offset 0x{x}", .{595 try err.addMsg("invalid relocation type {} at offset 0x{x}", .{
596 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),596 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),
597 rel.r_offset,597 rel.r_offset,
598 });598 });
599 try err.addNote(elf_file, "in {}:.eh_frame", .{elf_file.file(rec.file_index).?.fmtPath()});599 try err.addNote("in {}:.eh_frame", .{elf_file.file(rec.file_index).?.fmtPath()});
600 return error.RelocFailure;600 return error.RelocFailure;
601}601}
602602
src/link/Elf/relocatable.zig+4-4
...@@ -29,7 +29,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co...@@ -29,7 +29,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co
29 };29 };
30 }30 }
3131
32 if (comp.link_errors.items.len > 0) return error.FlushFailure;32 if (elf_file.base.hasErrors()) return error.FlushFailure;
3333
34 // First, we flush relocatable object file generated with our backends.34 // First, we flush relocatable object file generated with our backends.
35 if (elf_file.zigObjectPtr()) |zig_object| {35 if (elf_file.zigObjectPtr()) |zig_object| {
...@@ -146,7 +146,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co...@@ -146,7 +146,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co
146 try elf_file.base.file.?.setEndPos(total_size);146 try elf_file.base.file.?.setEndPos(total_size);
147 try elf_file.base.file.?.pwriteAll(buffer.items, 0);147 try elf_file.base.file.?.pwriteAll(buffer.items, 0);
148148
149 if (comp.link_errors.items.len > 0) return error.FlushFailure;149 if (elf_file.base.hasErrors()) return error.FlushFailure;
150}150}
151151
152pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {152pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
...@@ -177,7 +177,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const...@@ -177,7 +177,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const
177 };177 };
178 }178 }
179179
180 if (comp.link_errors.items.len > 0) return error.FlushFailure;180 if (elf_file.base.hasErrors()) return error.FlushFailure;
181181
182 // Now, we are ready to resolve the symbols across all input files.182 // Now, we are ready to resolve the symbols across all input files.
183 // We will first resolve the files in the ZigObject, next in the parsed183 // We will first resolve the files in the ZigObject, next in the parsed
...@@ -216,7 +216,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const...@@ -216,7 +216,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const
216 try elf_file.writeShdrTable();216 try elf_file.writeShdrTable();
217 try elf_file.writeElfHeader();217 try elf_file.writeElfHeader();
218218
219 if (comp.link_errors.items.len > 0) return error.FlushFailure;219 if (elf_file.base.hasErrors()) return error.FlushFailure;
220}220}
221221
222fn parsePositional(elf_file: *Elf, path: []const u8) Elf.ParseError!void {222fn parsePositional(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
src/link/MachO.zig+614-516
...@@ -25,8 +25,10 @@ sections: std.MultiArrayList(Section) = .{},...@@ -25,8 +25,10 @@ sections: std.MultiArrayList(Section) = .{},
25resolver: SymbolResolver = .{},25resolver: SymbolResolver = .{},
26/// This table will be populated after `scanRelocs` has run.26/// This table will be populated after `scanRelocs` has run.
27/// Key is symbol index.27/// Key is symbol index.
28undefs: std.AutoHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(Ref)) = .{},28undefs: std.AutoArrayHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(Ref)) = .{},
29dupes: std.AutoHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(File.Index)) = .{},29undefs_mutex: std.Thread.Mutex = .{},
30dupes: std.AutoArrayHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(File.Index)) = .{},
31dupes_mutex: std.Thread.Mutex = .{},
3032
31dyld_info_cmd: macho.dyld_info_command = .{},33dyld_info_cmd: macho.dyld_info_command = .{},
32symtab_cmd: macho.symtab_command = .{},34symtab_cmd: macho.symtab_command = .{},
...@@ -93,9 +95,10 @@ debug_str_sect_index: ?u8 = null,...@@ -93,9 +95,10 @@ debug_str_sect_index: ?u8 = null,
93debug_aranges_sect_index: ?u8 = null,95debug_aranges_sect_index: ?u8 = null,
94debug_line_sect_index: ?u8 = null,96debug_line_sect_index: ?u8 = null,
9597
96has_tlv: bool = false,98has_tlv: AtomicBool = AtomicBool.init(false),
97binds_to_weak: bool = false,99binds_to_weak: AtomicBool = AtomicBool.init(false),
98weak_defines: bool = false,100weak_defines: AtomicBool = AtomicBool.init(false),
101has_errors: AtomicBool = AtomicBool.init(false),
99102
100/// Options103/// Options
101/// SDK layout104/// SDK layout
...@@ -305,20 +308,15 @@ pub fn deinit(self: *MachO) void {...@@ -305,20 +308,15 @@ pub fn deinit(self: *MachO) void {
305 self.sections.deinit(gpa);308 self.sections.deinit(gpa);
306309
307 self.resolver.deinit(gpa);310 self.resolver.deinit(gpa);
308 {311
309 var it = self.undefs.iterator();312 for (self.undefs.values()) |*val| {
310 while (it.next()) |entry| {313 val.deinit(gpa);
311 entry.value_ptr.deinit(gpa);
312 }
313 self.undefs.deinit(gpa);
314 }314 }
315 {315 self.undefs.deinit(gpa);
316 var it = self.dupes.iterator();316 for (self.dupes.values()) |*val| {
317 while (it.next()) |entry| {317 val.deinit(gpa);
318 entry.value_ptr.deinit(gpa);
319 }
320 self.dupes.deinit(gpa);
321 }318 }
319 self.dupes.deinit(gpa);
322320
323 self.symtab.deinit(gpa);321 self.symtab.deinit(gpa);
324 self.strtab.deinit(gpa);322 self.strtab.deinit(gpa);
...@@ -395,17 +393,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -395,17 +393,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
395 }393 }
396394
397 for (positionals.items) |obj| {395 for (positionals.items) |obj| {
398 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {396 self.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
399 error.MalformedObject,397 error.UnknownFileType => try self.reportParseError(obj.path, "unknown file type for an input file", .{}),
400 error.MalformedArchive,
401 error.MalformedDylib,
402 error.InvalidCpuArch,
403 error.InvalidTarget,
404 => continue, // already reported
405 error.UnknownFileType => try self.reportParseError(obj.path, "unknown file type for an object file", .{}),
406 else => |e| try self.reportParseError(398 else => |e| try self.reportParseError(
407 obj.path,399 obj.path,
408 "unexpected error: parsing input file failed with error {s}",400 "unexpected error: reading input file failed with error {s}",
409 .{@errorName(e)},401 .{@errorName(e)},
410 ),402 ),
411 };403 };
...@@ -448,15 +440,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -448,15 +440,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
448 };440 };
449441
450 for (system_libs.items) |lib| {442 for (system_libs.items) |lib| {
451 self.parseLibrary(lib, false) catch |err| switch (err) {443 self.classifyInputFile(lib.path, lib, false) catch |err| switch (err) {
452 error.MalformedArchive,444 error.UnknownFileType => try self.reportParseError(lib.path, "unknown file type for an input file", .{}),
453 error.MalformedDylib,
454 error.InvalidCpuArch,
455 => continue, // already reported
456 error.UnknownFileType => try self.reportParseError(lib.path, "unknown file type for a library", .{}),
457 else => |e| try self.reportParseError(445 else => |e| try self.reportParseError(
458 lib.path,446 lib.path,
459 "unexpected error: parsing library failed with error {s}",447 "unexpected error: parsing input file failed with error {s}",
460 .{@errorName(e)},448 .{@errorName(e)},
461 ),449 ),
462 };450 };
...@@ -469,13 +457,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -469,13 +457,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
469 break :blk null;457 break :blk null;
470 };458 };
471 if (compiler_rt_path) |path| {459 if (compiler_rt_path) |path| {
472 self.parsePositional(path, false) catch |err| switch (err) {460 self.classifyInputFile(path, .{ .path = path }, false) catch |err| switch (err) {
473 error.MalformedObject,461 error.UnknownFileType => try self.reportParseError(path, "unknown file type for an input file", .{}),
474 error.MalformedArchive,
475 error.InvalidCpuArch,
476 error.InvalidTarget,
477 => {}, // already reported
478 error.UnknownFileType => try self.reportParseError(path, "unknown file type for a library", .{}),
479 else => |e| try self.reportParseError(462 else => |e| try self.reportParseError(
480 path,463 path,
481 "unexpected error: parsing input file failed with error {s}",464 "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...@@ -484,30 +467,18 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
484 };467 };
485 }468 }
486469
487 if (comp.link_errors.items.len > 0) return error.FlushFailure;470 try self.parseInputFiles();
488471 self.parseDependentDylibs() catch |err| {
489 for (self.dylibs.items) |index| {472 switch (err) {
490 self.getFile(index).?.dylib.umbrella = index;473 error.MissingLibraryDependencies => {},
491 }474 else => |e| try self.reportUnexpectedError(
492475 "unexpected error while parsing dependent libraries: {s}",
493 if (self.dylibs.items.len > 0) {476 .{@errorName(e)},
494 self.parseDependentDylibs() catch |err| {477 ),
495 switch (err) {478 }
496 error.MissingLibraryDependencies => {},479 };
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 }
505480
506 for (self.dylibs.items) |index| {481 if (self.base.hasErrors()) return error.FlushFailure;
507 const dylib = self.getFile(index).?.dylib;
508 if (!dylib.explicit and !dylib.hoisted) continue;
509 try dylib.initSymbols(self);
510 }
511482
512 {483 {
513 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));484 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...@@ -579,12 +550,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
579 else => |e| return e,550 else => |e| return e,
580 };551 };
581 }552 }
582 self.writeSectionsAndUpdateLinkeditSizes() catch |err| {553 try self.writeSectionsAndUpdateLinkeditSizes();
583 switch (err) {
584 error.ResolveFailed => return error.FlushFailure,
585 else => |e| return e,
586 }
587 };
588554
589 try self.writeSectionsToFile();555 try self.writeSectionsToFile();
590 try self.allocateLinkeditSegment();556 try self.allocateLinkeditSegment();
...@@ -841,181 +807,186 @@ pub fn resolveLibSystem(...@@ -841,181 +807,186 @@ pub fn resolveLibSystem(
841 });807 });
842}808}
843809
844pub const ParseError = error{810pub fn classifyInputFile(self: *MachO, path: []const u8, lib: SystemLib, must_link: bool) !void {
845 MalformedObject,
846 MalformedArchive,
847 MalformedDylib,
848 MalformedTbd,
849 NotLibStub,
850 InvalidCpuArch,
851 InvalidTarget,
852 InvalidTargetFatLibrary,
853 IncompatibleDylibVersion,
854 OutOfMemory,
855 Overflow,
856 InputOutput,
857 EndOfStream,
858 FileSystem,
859 NotSupported,
860 Unhandled,
861 UnknownFileType,
862} || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError || tapi.TapiError;
863
864pub fn parsePositional(self: *MachO, path: []const u8, must_link: bool) ParseError!void {
865 const tracy = trace(@src());811 const tracy = trace(@src());
866 defer tracy.end();812 defer tracy.end();
867 if (try Object.isObject(path)) {813
868 try self.parseObject(path);814 log.debug("classifying input file {s}", .{path});
869 } else {815
870 try self.parseLibrary(.{ .path = path }, must_link);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;
871 }831 }
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);
872}838}
873839
874fn parseLibrary(self: *MachO, lib: SystemLib, must_link: bool) ParseError!void {840fn parseFatFile(self: *MachO, file: std.fs.File, path: []const u8) !?fat.Arch {
875 const tracy = trace(@src());841 const fat_h = fat.readFatHeader(file) catch return null;
876 defer tracy.end();842 if (fat_h.magic != macho.FAT_MAGIC and fat_h.magic != macho.FAT_MAGIC_64) return null;
877 if (try fat.isFatLibrary(lib.path)) {843 var fat_archs_buffer: [2]fat.Arch = undefined;
878 const fat_arch = try self.parseFatLibrary(lib.path);844 const fat_archs = try fat.parseArchs(file, fat_h, &fat_archs_buffer);
879 if (try Archive.isArchive(lib.path, fat_arch)) {845 const cpu_arch = self.getTarget().cpu.arch;
880 try self.parseArchive(lib, must_link, fat_arch);846 for (fat_archs) |arch| {
881 } else if (try Dylib.isDylib(lib.path, fat_arch)) {847 if (arch.tag == cpu_arch) return arch;
882 _ = try self.parseDylib(lib, true, fat_arch);
883 } else return error.UnknownFileType;
884 } else if (try Archive.isArchive(lib.path, null)) {
885 try self.parseArchive(lib, must_link, null);
886 } else if (try Dylib.isDylib(lib.path, null)) {
887 _ = try self.parseDylib(lib, true, null);
888 } else {
889 _ = self.parseTbd(lib, true) catch |err| switch (err) {
890 error.MalformedTbd => return error.UnknownFileType,
891 else => |e| return e,
892 };
893 }848 }
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;
894}861}
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 {
897 const tracy = trace(@src());870 const tracy = trace(@src());
898 defer tracy.end();871 defer tracy.end();
899872
900 const gpa = self.base.comp.gpa;873 const gpa = self.base.comp.gpa;
901 const file = try fs.cwd().openFile(path, .{});
902 const handle = try self.addFileHandle(file);
903 const mtime: u64 = mtime: {874 const mtime: u64 = mtime: {
875 const file = self.getFileHandle(handle);
904 const stat = file.stat() catch break :mtime 0;876 const stat = file.stat() catch break :mtime 0;
905 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));877 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
906 };878 };
907 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));879 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
908 self.files.set(index, .{880 self.files.set(index, .{ .object = .{
909 .object = .{881 .offset = offset,
910 .offset = 0, // TODO FAT objects882 .path = try gpa.dupe(u8, path),
911 .path = try gpa.dupe(u8, path),883 .file_handle = handle,
912 .file_handle = handle,884 .mtime = mtime,
913 .mtime = mtime,885 .index = index,
914 .index = index,886 } });
915 },
916 });
917 try self.objects.append(gpa, index);887 try self.objects.append(gpa, index);
918
919 const object = self.getFile(index).?.object;
920 try object.parse(self);
921}888}
922889
923pub fn parseFatLibrary(self: *MachO, path: []const u8) !fat.Arch {890pub fn parseInputFiles(self: *MachO) !void {
924 var buffer: [2]fat.Arch = undefined;891 const tracy = trace(@src());
925 const fat_archs = try fat.parseArchs(path, &buffer);892 defer tracy.end();
926 const cpu_arch = self.getTarget().cpu.arch;893
927 for (fat_archs) |arch| {894 const tp = self.base.comp.thread_pool;
928 if (arch.tag == cpu_arch) return arch;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 }
929 }907 }
930 try self.reportParseError(path, "missing arch in universal file: expected {s}", .{@tagName(cpu_arch)});908
931 return error.InvalidCpuArch;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 };
932}925}
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 {
935 const tracy = trace(@src());928 const tracy = trace(@src());
936 defer tracy.end();929 defer tracy.end();
937930
938 const gpa = self.base.comp.gpa;931 const gpa = self.base.comp.gpa;
939932
940 const file = try fs.cwd().openFile(lib.path, .{});
941 const handle = try self.addFileHandle(file);
942
943 var archive = Archive{};933 var archive = Archive{};
944 defer archive.deinit(gpa);934 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;937 for (archive.objects.items) |unpacked| {
948 for (archive.objects.items) |extracted| {938 const index: File.Index = @intCast(try self.files.addOne(gpa));
949 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));939 self.files.set(index, .{ .object = unpacked });
950 self.files.set(index, .{ .object = extracted });
951 const object = &self.files.items(.data)[index].object;940 const object = &self.files.items(.data)[index].object;
952 object.index = index;941 object.index = index;
953 object.alive = must_link or lib.needed; // TODO: or self.options.all_load;942 object.alive = must_link or lib.needed; // TODO: or self.options.all_load;
954 object.hidden = lib.hidden;943 object.hidden = lib.hidden;
955 object.parse(self) catch |err| switch (err) {
956 error.MalformedObject,
957 error.InvalidCpuArch,
958 error.InvalidTarget,
959 => has_parse_error = true,
960 else => |e| return e,
961 };
962 try self.objects.append(gpa, index);944 try self.objects.append(gpa, index);
963
964 // Finally, we do a post-parse check for -ObjC to see if we need to force load this member
965 // anyhow.
966 object.alive = object.alive or (self.force_load_objc and object.hasObjc());
967 }945 }
968 if (has_parse_error) return error.MalformedArchive;
969}946}
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 {
972 const tracy = trace(@src());949 const tracy = trace(@src());
973 defer tracy.end();950 defer tracy.end();
974951
975 const gpa = self.base.comp.gpa;952 const gpa = self.base.comp.gpa;
976953
977 const file = try fs.cwd().openFile(lib.path, .{});954 const index: File.Index = @intCast(try self.files.addOne(gpa));
978 defer file.close();
979
980 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
981 self.files.set(index, .{ .dylib = .{955 self.files.set(index, .{ .dylib = .{
956 .offset = offset,
957 .file_handle = handle,
958 .tag = .dylib,
982 .path = try gpa.dupe(u8, lib.path),959 .path = try gpa.dupe(u8, lib.path),
983 .index = index,960 .index = index,
984 .needed = lib.needed,961 .needed = lib.needed,
985 .weak = lib.weak,962 .weak = lib.weak,
986 .reexport = lib.reexport,963 .reexport = lib.reexport,
987 .explicit = explicit,964 .explicit = explicit,
965 .umbrella = index,
988 } });966 } });
989 const dylib = &self.files.items(.data)[index].dylib;
990 try dylib.parse(self, file, fat_arch);
991
992 try self.dylibs.append(gpa, index);967 try self.dylibs.append(gpa, index);
993968
994 return index;969 return index;
995}970}
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 {
998 const tracy = trace(@src());973 const tracy = trace(@src());
999 defer tracy.end();974 defer tracy.end();
1000975
1001 const gpa = self.base.comp.gpa;976 const gpa = self.base.comp.gpa;
1002 const file = try fs.cwd().openFile(lib.path, .{});977 const index: File.Index = @intCast(try self.files.addOne(gpa));
1003 defer file.close();
1004
1005 var lib_stub = LibStub.loadFromFile(gpa, file) catch return error.MalformedTbd; // TODO actually handle different errors
1006 defer lib_stub.deinit();
1007
1008 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
1009 self.files.set(index, .{ .dylib = .{978 self.files.set(index, .{ .dylib = .{
979 .offset = 0,
980 .file_handle = handle,
981 .tag = .tbd,
1010 .path = try gpa.dupe(u8, lib.path),982 .path = try gpa.dupe(u8, lib.path),
1011 .index = index,983 .index = index,
1012 .needed = lib.needed,984 .needed = lib.needed,
1013 .weak = lib.weak,985 .weak = lib.weak,
1014 .reexport = lib.reexport,986 .reexport = lib.reexport,
1015 .explicit = explicit,987 .explicit = explicit,
988 .umbrella = index,
1016 } });989 } });
1017 const dylib = &self.files.items(.data)[index].dylib;
1018 try dylib.parseTbd(self.getTarget().cpu.arch, self.platform, lib_stub, self);
1019 try self.dylibs.append(gpa, index);990 try self.dylibs.append(gpa, index);
1020991
1021 return index;992 return index;
...@@ -1092,6 +1063,8 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1092,6 +1063,8 @@ fn parseDependentDylibs(self: *MachO) !void {
1092 const tracy = trace(@src());1063 const tracy = trace(@src());
1093 defer tracy.end();1064 defer tracy.end();
10941065
1066 if (self.dylibs.items.len == 0) return;
1067
1095 const gpa = self.base.comp.gpa;1068 const gpa = self.base.comp.gpa;
1096 const lib_dirs = self.lib_dirs;1069 const lib_dirs = self.lib_dirs;
1097 const framework_dirs = self.framework_dirs;1070 const framework_dirs = self.framework_dirs;
...@@ -1108,7 +1081,7 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1108,7 +1081,7 @@ fn parseDependentDylibs(self: *MachO) !void {
1108 while (index < self.dylibs.items.len) : (index += 1) {1081 while (index < self.dylibs.items.len) : (index += 1) {
1109 const dylib_index = self.dylibs.items[index];1082 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);
1112 defer dependents.deinit();1085 defer dependents.deinit();
1113 try dependents.ensureTotalCapacityPrecise(self.getFile(dylib_index).?.dylib.dependents.items.len);1086 try dependents.ensureTotalCapacityPrecise(self.getFile(dylib_index).?.dylib.dependents.items.len);
11141087
...@@ -1199,38 +1172,34 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1199,38 +1172,34 @@ fn parseDependentDylibs(self: *MachO) !void {
1199 .path = full_path,1172 .path = full_path,
1200 .weak = is_weak,1173 .weak = is_weak,
1201 };1174 };
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;
1202 const file_index = file_index: {1179 const file_index = file_index: {
1203 if (try fat.isFatLibrary(lib.path)) {1180 if (readMachHeader(file, offset) catch null) |h| blk: {
1204 const fat_arch = try self.parseFatLibrary(lib.path);1181 if (h.magic != macho.MH_MAGIC_64) break :blk;
1205 if (try Dylib.isDylib(lib.path, fat_arch)) {1182 switch (h.filetype) {
1206 break :file_index try self.parseDylib(lib, false, fat_arch);1183 macho.MH_DYLIB => break :file_index try self.addDylib(lib, false, fh, offset),
1207 } else break :file_index @as(File.Index, 0);1184 else => break :file_index @as(File.Index, 0),
1208 } else if (try Dylib.isDylib(lib.path, null)) {1185 }
1209 break :file_index try self.parseDylib(lib, false, null);
1210 } else {
1211 const file_index = self.parseTbd(lib, false) catch |err| switch (err) {
1212 error.MalformedTbd => @as(File.Index, 0),
1213 else => |e| return e,
1214 };
1215 break :file_index file_index;
1216 }1186 }
1187 break :file_index try self.addTbd(lib, false, fh);
1217 };1188 };
1218 dependents.appendAssumeCapacity(.{ .id = id, .file = file_index });1189 dependents.appendAssumeCapacity(file_index);
1219 }1190 }
12201191
1221 const dylib = self.getFile(dylib_index).?.dylib;1192 const dylib = self.getFile(dylib_index).?.dylib;
1222 for (dependents.items) |entry| {1193 for (dylib.dependents.items, dependents.items) |id, file_index| {
1223 const id = entry.id;
1224 const file_index = entry.file;
1225 if (self.getFile(file_index)) |file| {1194 if (self.getFile(file_index)) |file| {
1226 const dep_dylib = file.dylib;1195 const dep_dylib = file.dylib;
1196 try dep_dylib.parse(self); // TODO in parallel
1227 dep_dylib.hoisted = self.isHoisted(id.name);1197 dep_dylib.hoisted = self.isHoisted(id.name);
1228 if (self.getFile(dep_dylib.umbrella) == null) {1198 dep_dylib.umbrella = dylib.umbrella;
1229 dep_dylib.umbrella = dylib.umbrella;
1230 }
1231 if (!dep_dylib.hoisted) {1199 if (!dep_dylib.hoisted) {
1232 const umbrella = dep_dylib.getUmbrella(self);1200 const umbrella = dep_dylib.getUmbrella(self);
1233 for (dep_dylib.exports.items(.name), dep_dylib.exports.items(.flags)) |off, flags| {1201 for (dep_dylib.exports.items(.name), dep_dylib.exports.items(.flags)) |off, flags| {
1202 // TODO rethink this entire algorithm
1234 try umbrella.addExport(gpa, dep_dylib.getString(off), flags);1203 try umbrella.addExport(gpa, dep_dylib.getString(off), flags);
1235 }1204 }
1236 try umbrella.rpaths.ensureUnusedCapacity(gpa, dep_dylib.rpaths.keys().len);1205 try umbrella.rpaths.ensureUnusedCapacity(gpa, dep_dylib.rpaths.keys().len);
...@@ -1238,15 +1207,13 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1238,15 +1207,13 @@ fn parseDependentDylibs(self: *MachO) !void {
1238 umbrella.rpaths.putAssumeCapacity(try gpa.dupe(u8, rpath), {});1207 umbrella.rpaths.putAssumeCapacity(try gpa.dupe(u8, rpath), {});
1239 }1208 }
1240 }1209 }
1241 } else {1210 } else try self.reportDependencyError(
1242 try self.reportDependencyError(1211 dylib.getUmbrella(self).index,
1243 dylib.getUmbrella(self).index,1212 id.name,
1244 id.name,1213 "unable to resolve dependency",
1245 "unable to resolve dependency",1214 .{},
1246 .{},1215 );
1247 );1216 has_errors = true;
1248 has_errors = true;
1249 }
1250 }1217 }
1251 }1218 }
12521219
...@@ -1311,95 +1278,51 @@ fn markLive(self: *MachO) void {...@@ -1311,95 +1278,51 @@ fn markLive(self: *MachO) void {
1311 if (self.getInternalObject()) |obj| obj.markLive(self);1278 if (self.getInternalObject()) |obj| obj.markLive(self);
1312}1279}
13131280
1314fn resolveSyntheticSymbols(self: *MachO) !void {1281fn convertTentativeDefsAndResolveSpecialSymbols(self: *MachO) !void {
1315 const internal = self.getInternalObject() orelse return;1282 const tp = self.base.comp.thread_pool;
13161283 var wg: WaitGroup = .{};
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
1330 {1284 {
1331 const gpa = self.base.comp.gpa;1285 wg.reset();
1332 var boundary_symbols = std.AutoHashMap(Symbol.Index, void).init(gpa);1286 defer wg.wait();
1333 defer boundary_symbols.deinit();
1334
1335 for (self.objects.items) |index| {1287 for (self.objects.items) |index| {
1336 const object = self.getFile(index).?.object;1288 tp.spawnWg(&wg, convertTentativeDefinitionsWorker, .{ self, 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 }
1349 }1289 }
13501290 if (self.getInternalObject()) |obj| {
1351 try self.boundary_symbols.ensureTotalCapacityPrecise(gpa, boundary_symbols.count());1291 tp.spawnWg(&wg, resolveSpecialSymbolsWorker, .{ self, obj });
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.*);
1357 }1292 }
1358 }1293 }
1294 if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
1359}1295}
13601296
1361fn convertTentativeDefsAndResolveSpecialSymbols(self: *MachO) !void {1297fn convertTentativeDefinitionsWorker(self: *MachO, object: *Object) void {
1362 for (self.objects.items) |index| {1298 const tracy = trace(@src());
1363 try self.getFile(index).?.object.convertTentativeDefinitions(self);1299 defer tracy.end();
1364 }1300 object.convertTentativeDefinitions(self) catch |err| {
1365 if (self.getInternalObject()) |obj| {1301 self.reportParseError2(
1366 try obj.resolveBoundarySymbols(self);1302 object.index,
1367 try obj.resolveObjcMsgSendSymbols(self);1303 "unexpected error occurred while converting tentative symbols into defined symbols: {s}",
1368 }1304 .{@errorName(err)},
1305 ) catch {};
1306 _ = self.has_errors.swap(true, .seq_cst);
1307 };
1369}1308}
13701309
1371fn createObjcSections(self: *MachO) !void {1310fn resolveSpecialSymbolsWorker(self: *MachO, obj: *InternalObject) void {
1372 const gpa = self.base.comp.gpa;1311 const tracy = trace(@src());
1373 var objc_msgsend_syms = std.AutoArrayHashMap(Symbol.Index, void).init(gpa);1312 defer tracy.end();
1374 defer objc_msgsend_syms.deinit();1313 obj.resolveBoundarySymbols(self) catch |err| {
13751314 self.reportUnexpectedError("unexpected error occurred while resolving boundary symbols: {s}", .{
1376 for (self.objects.items) |index| {1315 @errorName(err),
1377 const object = self.getFile(index).?.object;1316 }) catch {};
13781317 _ = self.has_errors.swap(true, .seq_cst);
1379 for (object.symbols.items, 0..) |sym_index, i| {1318 return;
1380 const nlist_idx = @as(Symbol.Index, @intCast(i));1319 };
1381 const nlist = object.symtab.items(.nlist)[nlist_idx];1320 obj.resolveObjcMsgSendSymbols(self) catch |err| {
1382 if (!nlist.ext()) continue;1321 self.reportUnexpectedError("unexpected error occurred while resolving ObjC msgsend stubs: {s}", .{
1383 if (!nlist.undf()) continue;1322 @errorName(err),
13841323 }) catch {};
1385 const sym = self.getSymbol(sym_index);1324 _ = self.has_errors.swap(true, .seq_cst);
1386 if (sym.getFile(self) != null) continue;1325 };
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 }
1403}1326}
14041327
1405pub fn dedupLiterals(self: *MachO) !void {1328pub fn dedupLiterals(self: *MachO) !void {
...@@ -1420,14 +1343,20 @@ pub fn dedupLiterals(self: *MachO) !void {...@@ -1420,14 +1343,20 @@ pub fn dedupLiterals(self: *MachO) !void {
1420 try object.resolveLiterals(&lp, self);1343 try object.resolveLiterals(&lp, self);
1421 }1344 }
14221345
1423 if (self.getZigObject()) |zo| {1346 const tp = self.base.comp.thread_pool;
1424 zo.dedupLiterals(lp, self);1347 var wg: WaitGroup = .{};
1425 }1348 {
1426 for (self.objects.items) |index| {1349 wg.reset();
1427 self.getFile(index).?.object.dedupLiterals(lp, self);1350 defer wg.wait();
1428 }1351 if (self.getZigObject()) |zo| {
1429 if (self.getInternalObject()) |object| {1352 tp.spawnWg(&wg, File.dedupLiterals, .{ zo.asFile(), lp, self });
1430 object.dedupLiterals(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 }
1431 }1360 }
1432}1361}
14331362
...@@ -1441,18 +1370,41 @@ fn claimUnresolved(self: *MachO) void {...@@ -1441,18 +1370,41 @@ fn claimUnresolved(self: *MachO) void {
1441}1370}
14421371
1443fn checkDuplicates(self: *MachO) !void {1372fn checkDuplicates(self: *MachO) !void {
1444 if (self.getZigObject()) |zo| {1373 const tracy = trace(@src());
1445 try zo.asFile().checkDuplicates(self);1374 defer tracy.end();
1446 }1375
1447 for (self.objects.items) |index| {1376 const tp = self.base.comp.thread_pool;
1448 try self.getFile(index).?.checkDuplicates(self);1377 var wg: WaitGroup = .{};
1449 }1378 {
1450 if (self.getInternalObject()) |obj| {1379 wg.reset();
1451 try obj.asFile().checkDuplicates(self);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 }
1452 }1390 }
1391
1392 if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
1393
1453 try self.reportDuplicates();1394 try self.reportDuplicates();
1454}1395}
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
1456fn markImportsAndExports(self: *MachO) void {1408fn markImportsAndExports(self: *MachO) void {
1457 const tracy = trace(@src());1409 const tracy = trace(@src());
1458 defer tracy.end();1410 defer tracy.end();
...@@ -1491,16 +1443,26 @@ fn scanRelocs(self: *MachO) !void {...@@ -1491,16 +1443,26 @@ fn scanRelocs(self: *MachO) !void {
1491 const tracy = trace(@src());1443 const tracy = trace(@src());
1492 defer tracy.end();1444 defer tracy.end();
14931445
1494 if (self.getZigObject()) |zo| {1446 const tp = self.base.comp.thread_pool;
1495 try zo.scanRelocs(self);1447 var wg: WaitGroup = .{};
1496 }1448
1497 for (self.objects.items) |index| {1449 {
1498 try self.getFile(index).?.object.scanRelocs(self);1450 wg.reset();
1499 }1451 defer wg.wait();
1500 if (self.getInternalObject()) |obj| {1452
1501 obj.scanRelocs(self);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 }
1502 }1462 }
15031463
1464 if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
1465
1504 try self.reportUndefs();1466 try self.reportUndefs();
15051467
1506 if (self.getZigObject()) |zo| {1468 if (self.getZigObject()) |zo| {
...@@ -1517,40 +1479,77 @@ fn scanRelocs(self: *MachO) !void {...@@ -1517,40 +1479,77 @@ fn scanRelocs(self: *MachO) !void {
1517 }1479 }
1518}1480}
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
1520fn reportUndefs(self: *MachO) !void {1502fn reportUndefs(self: *MachO) !void {
1521 const tracy = trace(@src());1503 const tracy = trace(@src());
1522 defer tracy.end();1504 defer tracy.end();
15231505
1524 if (self.undefined_treatment == .suppress or1506 if (self.undefined_treatment == .suppress or
1525 self.undefined_treatment == .dynamic_lookup) return;1507 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;
1527 const max_notes = 4;1511 const max_notes = 4;
15281512
1529 var has_undefs = false;1513 // We will sort by name, and then by file to ensure deterministic output.
1530 var it = self.undefs.iterator();1514 var keys = try std.ArrayList(SymbolResolver.Index).initCapacity(gpa, self.undefs.keys().len);
1531 while (it.next()) |entry| {1515 defer keys.deinit();
1532 const undef_sym = self.resolver.keys.items[entry.key_ptr.* - 1];1516 keys.appendSliceAssumeCapacity(self.undefs.keys());
1533 const notes = entry.value_ptr.*;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).?;
1534 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);1533 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
15351534
1536 var err = try self.addErrorWithNotes(nnotes);1535 var err = try self.base.addErrorWithNotes(nnotes);
1537 try err.addMsg(self, "undefined symbol: {s}", .{undef_sym.getName(self)});1536 try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)});
1538 has_undefs = true;
15391537
1540 var inote: usize = 0;1538 var inote: usize = 0;
1541 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {1539 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
1542 const note = notes.items[inote];1540 const note = notes.items[inote];
1543 const file = self.getFile(note.file).?;1541 const file = self.getFile(note.file).?;
1544 const atom = note.getAtom(self).?;1542 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) });
1546 }1544 }
15471545
1548 if (notes.items.len > max_notes) {1546 if (notes.items.len > max_notes) {
1549 const remaining = notes.items.len - max_notes;1547 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});
1551 }1549 }
1552 }1550 }
1553 if (has_undefs) return error.HasUndefinedSymbols;1551
1552 return error.HasUndefinedSymbols;
1554}1553}
15551554
1556fn initOutputSections(self: *MachO) !void {1555fn initOutputSections(self: *MachO) !void {
...@@ -1786,7 +1785,7 @@ pub fn sortSections(self: *MachO) !void {...@@ -1786,7 +1785,7 @@ pub fn sortSections(self: *MachO) !void {
1786 if (self.getZigObject()) |zo| {1785 if (self.getZigObject()) |zo| {
1787 for (zo.getAtoms()) |atom_index| {1786 for (zo.getAtoms()) |atom_index| {
1788 const atom = zo.getAtom(atom_index) orelse continue;1787 const atom = zo.getAtom(atom_index) orelse continue;
1789 if (!atom.flags.alive) continue;1788 if (!atom.isAlive()) continue;
1790 atom.out_n_sect = backlinks[atom.out_n_sect];1789 atom.out_n_sect = backlinks[atom.out_n_sect];
1791 }1790 }
1792 }1791 }
...@@ -1795,7 +1794,7 @@ pub fn sortSections(self: *MachO) !void {...@@ -1795,7 +1794,7 @@ pub fn sortSections(self: *MachO) !void {
1795 const file = self.getFile(index).?;1794 const file = self.getFile(index).?;
1796 for (file.getAtoms()) |atom_index| {1795 for (file.getAtoms()) |atom_index| {
1797 const atom = file.getAtom(atom_index) orelse continue;1796 const atom = file.getAtom(atom_index) orelse continue;
1798 if (!atom.flags.alive) continue;1797 if (!atom.isAlive()) continue;
1799 atom.out_n_sect = backlinks[atom.out_n_sect];1798 atom.out_n_sect = backlinks[atom.out_n_sect];
1800 }1799 }
1801 }1800 }
...@@ -1803,7 +1802,7 @@ pub fn sortSections(self: *MachO) !void {...@@ -1803,7 +1802,7 @@ pub fn sortSections(self: *MachO) !void {
1803 if (self.getInternalObject()) |object| {1802 if (self.getInternalObject()) |object| {
1804 for (object.getAtoms()) |atom_index| {1803 for (object.getAtoms()) |atom_index| {
1805 const atom = object.getAtom(atom_index) orelse continue;1804 const atom = object.getAtom(atom_index) orelse continue;
1806 if (!atom.flags.alive) continue;1805 if (!atom.isAlive()) continue;
1807 atom.out_n_sect = backlinks[atom.out_n_sect];1806 atom.out_n_sect = backlinks[atom.out_n_sect];
1808 }1807 }
1809 }1808 }
...@@ -1844,7 +1843,7 @@ pub fn addAtomsToSections(self: *MachO) !void {...@@ -1844,7 +1843,7 @@ pub fn addAtomsToSections(self: *MachO) !void {
1844 if (self.getZigObject()) |zo| {1843 if (self.getZigObject()) |zo| {
1845 for (zo.getAtoms()) |atom_index| {1844 for (zo.getAtoms()) |atom_index| {
1846 const atom = zo.getAtom(atom_index) orelse continue;1845 const atom = zo.getAtom(atom_index) orelse continue;
1847 if (!atom.flags.alive) continue;1846 if (!atom.isAlive()) continue;
1848 if (self.isZigSection(atom.out_n_sect)) continue;1847 if (self.isZigSection(atom.out_n_sect)) continue;
1849 const atoms = &self.sections.items(.atoms)[atom.out_n_sect];1848 const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
1850 try atoms.append(gpa, .{ .index = atom_index, .file = zo.index });1849 try atoms.append(gpa, .{ .index = atom_index, .file = zo.index });
...@@ -1854,7 +1853,7 @@ pub fn addAtomsToSections(self: *MachO) !void {...@@ -1854,7 +1853,7 @@ pub fn addAtomsToSections(self: *MachO) !void {
1854 const file = self.getFile(index).?;1853 const file = self.getFile(index).?;
1855 for (file.getAtoms()) |atom_index| {1854 for (file.getAtoms()) |atom_index| {
1856 const atom = file.getAtom(atom_index) orelse continue;1855 const atom = file.getAtom(atom_index) orelse continue;
1857 if (!atom.flags.alive) continue;1856 if (!atom.isAlive()) continue;
1858 const atoms = &self.sections.items(.atoms)[atom.out_n_sect];1857 const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
1859 try atoms.append(gpa, .{ .index = atom_index, .file = index });1858 try atoms.append(gpa, .{ .index = atom_index, .file = index });
1860 }1859 }
...@@ -1862,7 +1861,7 @@ pub fn addAtomsToSections(self: *MachO) !void {...@@ -1862,7 +1861,7 @@ pub fn addAtomsToSections(self: *MachO) !void {
1862 if (self.getInternalObject()) |object| {1861 if (self.getInternalObject()) |object| {
1863 for (object.getAtoms()) |atom_index| {1862 for (object.getAtoms()) |atom_index| {
1864 const atom = object.getAtom(atom_index) orelse continue;1863 const atom = object.getAtom(atom_index) orelse continue;
1865 if (!atom.flags.alive) continue;1864 if (!atom.isAlive()) continue;
1866 const atoms = &self.sections.items(.atoms)[atom.out_n_sect];1865 const atoms = &self.sections.items(.atoms)[atom.out_n_sect];
1867 try atoms.append(gpa, .{ .index = atom_index, .file = object.index });1866 try atoms.append(gpa, .{ .index = atom_index, .file = object.index });
1868 }1867 }
...@@ -1881,46 +1880,43 @@ fn calcSectionSizes(self: *MachO) !void {...@@ -1881,46 +1880,43 @@ fn calcSectionSizes(self: *MachO) !void {
1881 header.@"align" = 3;1880 header.@"align" = 3;
1882 }1881 }
18831882
1884 const slice = self.sections.slice();1883 const tp = self.base.comp.thread_pool;
1885 for (slice.items(.header), slice.items(.atoms)) |*header, atoms| {1884 var wg: WaitGroup = .{};
1886 if (atoms.items.len == 0) continue;1885 {
1887 if (self.requiresThunks() and header.isCode()) continue;1886 wg.reset();
18881887 defer wg.wait();
1889 for (atoms.items) |ref| {1888 const slice = self.sections.slice();
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()) {
1901 for (slice.items(.header), slice.items(.atoms), 0..) |header, atoms, i| {1889 for (slice.items(.header), slice.items(.atoms), 0..) |header, atoms, i| {
1902 if (!header.isCode()) continue;
1903 if (atoms.items.len == 0) continue;1890 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.1895 if (self.requiresThunks()) {
1906 try thunks.createThunks(@intCast(i), self);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 }
1907 }1901 }
1908 }
19091902
1910 // At this point, we can also calculate symtab and data-in-code linkedit section sizes1903 // At this point, we can also calculate symtab and data-in-code linkedit section sizes
1911 if (self.getZigObject()) |zo| {1904 if (self.getZigObject()) |zo| {
1912 zo.asFile().calcSymtabSize(self);1905 tp.spawnWg(&wg, File.calcSymtabSize, .{ zo.asFile(), self });
1913 }1906 }
1914 for (self.objects.items) |index| {1907 for (self.objects.items) |index| {
1915 self.getFile(index).?.calcSymtabSize(self);1908 tp.spawnWg(&wg, File.calcSymtabSize, .{ self.getFile(index).?, self });
1916 }1909 }
1917 for (self.dylibs.items) |index| {1910 for (self.dylibs.items) |index| {
1918 self.getFile(index).?.calcSymtabSize(self);1911 tp.spawnWg(&wg, File.calcSymtabSize, .{ self.getFile(index).?, self });
1919 }1912 }
1920 if (self.getInternalObject()) |obj| {1913 if (self.getInternalObject()) |obj| {
1921 obj.asFile().calcSymtabSize(self);1914 tp.spawnWg(&wg, File.calcSymtabSize, .{ obj.asFile(), self });
1915 }
1922 }1916 }
19231917
1918 if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
1919
1924 try self.calcSymtabSize();1920 try self.calcSymtabSize();
19251921
1926 if (self.got_sect_index) |idx| {1922 if (self.got_sect_index) |idx| {
...@@ -1968,6 +1964,49 @@ fn calcSectionSizes(self: *MachO) !void {...@@ -1968,6 +1964,49 @@ fn calcSectionSizes(self: *MachO) !void {
1968 }1964 }
1969}1965}
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
1971fn generateUnwindInfo(self: *MachO) !void {2010fn generateUnwindInfo(self: *MachO) !void {
1972 const tracy = trace(@src());2011 const tracy = trace(@src());
1973 defer tracy.end();2012 defer tracy.end();
...@@ -2349,6 +2388,9 @@ fn resizeSections(self: *MachO) !void {...@@ -2349,6 +2388,9 @@ fn resizeSections(self: *MachO) !void {
2349}2388}
23502389
2351fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void {2390fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void {
2391 const tracy = trace(@src());
2392 defer tracy.end();
2393
2352 const gpa = self.base.comp.gpa;2394 const gpa = self.base.comp.gpa;
23532395
2354 const cmd = self.symtab_cmd;2396 const cmd = self.symtab_cmd;
...@@ -2356,64 +2398,98 @@ fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void {...@@ -2356,64 +2398,98 @@ fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void {
2356 try self.strtab.resize(gpa, cmd.strsize);2398 try self.strtab.resize(gpa, cmd.strsize);
2357 self.strtab.items[0] = 0;2399 self.strtab.items[0] = 0;
23582400
2359 for (self.objects.items) |index| {2401 const tp = self.base.comp.thread_pool;
2360 try self.getFile(index).?.writeAtoms(self);2402 var wg: WaitGroup = .{};
2361 }2403 {
2362 if (self.getZigObject()) |zo| {2404 wg.reset();
2363 try zo.writeAtoms(self);2405 defer wg.wait();
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 }
23752406
2376 const slice = self.sections.slice();2407 for (self.objects.items) |index| {
2377 for (&[_]?u8{2408 tp.spawnWg(&wg, writeAtomsWorker, .{ self, self.getFile(index).? });
2378 self.eh_frame_sect_index,2409 }
2379 self.unwind_info_sect_index,2410 if (self.getZigObject()) |zo| {
2380 self.got_sect_index,2411 tp.spawnWg(&wg, writeAtomsWorker, .{ self, zo.asFile() });
2381 self.stubs_sect_index,2412 }
2382 self.la_symbol_ptr_sect_index,2413 if (self.getInternalObject()) |obj| {
2383 self.tlv_ptr_sect_index,2414 tp.spawnWg(&wg, writeAtomsWorker, .{ self, obj.asFile() });
2384 self.objc_stubs_sect_index,2415 }
2385 }) |maybe_sect_id| {2416 for (self.thunks.items) |thunk| {
2386 if (maybe_sect_id) |sect_id| {2417 tp.spawnWg(&wg, writeThunkWorker, .{ self, thunk });
2387 const out = slice.items(.out)[sect_id].items;
2388 try self.writeSyntheticSection(sect_id, out);
2389 }2418 }
2390 }
23912419
2392 if (self.la_symbol_ptr_sect_index) |_| {2420 const slice = self.sections.slice();
2393 try self.updateLazyBindSize();2421 for (&[_]?u8{
2394 }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);2436 if (self.la_symbol_ptr_sect_index) |_| {
2397 try self.bind.updateSize(self);2437 tp.spawnWg(&wg, updateLazyBindSizeWorker, .{self});
2398 try self.weak_bind.updateSize(self);2438 }
2399 try self.export_trie.updateSize(self);
2400 try self.data_in_code.updateSize(self);
24012439
2402 if (self.getZigObject()) |zo| {2440 tp.spawnWg(&wg, updateLinkeditSizeWorker, .{ self, .rebase });
2403 zo.asFile().writeSymtab(self, self);2441 tp.spawnWg(&wg, updateLinkeditSizeWorker, .{ self, .bind });
2404 }2442 tp.spawnWg(&wg, updateLinkeditSizeWorker, .{ self, .weak_bind });
2405 for (self.objects.items) |index| {2443 tp.spawnWg(&wg, updateLinkeditSizeWorker, .{ self, .export_trie });
2406 self.getFile(index).?.writeSymtab(self, self);2444 tp.spawnWg(&wg, updateLinkeditSizeWorker, .{ self, .data_in_code });
2407 }2445
2408 for (self.dylibs.items) |index| {2446 if (self.getZigObject()) |zo| {
2409 self.getFile(index).?.writeSymtab(self, self);2447 tp.spawnWg(&wg, File.writeSymtab, .{ zo.asFile(), self, self });
2410 }2448 }
2411 if (self.getInternalObject()) |obj| {2449 for (self.objects.items) |index| {
2412 obj.asFile().writeSymtab(self, self);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 }
2413 }2458 }
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 };
2414}2490}
24152491
2416fn writeSyntheticSection(self: *MachO, sect_id: u8, out: []u8) !void {2492fn writeSyntheticSectionWorker(self: *MachO, sect_id: u8, out: []u8) void {
2417 const tracy = trace(@src());2493 const tracy = trace(@src());
2418 defer tracy.end();2494 defer tracy.end();
24192495
...@@ -2427,6 +2503,22 @@ fn writeSyntheticSection(self: *MachO, sect_id: u8, out: []u8) !void {...@@ -2427,6 +2503,22 @@ fn writeSyntheticSection(self: *MachO, sect_id: u8, out: []u8) !void {
2427 objc_stubs,2503 objc_stubs,
2428 };2504 };
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];
2430 const tag: Tag = tag: {2522 const tag: Tag = tag: {
2431 if (self.eh_frame_sect_index != null and2523 if (self.eh_frame_sect_index != null and
2432 self.eh_frame_sect_index.? == sect_id) break :tag .eh_frame;2524 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 {...@@ -2444,26 +2536,57 @@ fn writeSyntheticSection(self: *MachO, sect_id: u8, out: []u8) !void {
2444 self.objc_stubs_sect_index.? == sect_id) break :tag .objc_stubs;2536 self.objc_stubs_sect_index.? == sect_id) break :tag .objc_stubs;
2445 unreachable;2537 unreachable;
2446 };2538 };
2447 var stream = std.io.fixedBufferStream(out);2539 doWork(self, tag, out) catch |err| {
2448 switch (tag) {2540 self.reportUnexpectedError("could not write section '{s},{s}': {s}", .{
2449 .eh_frame => eh_frame.write(self, out),2541 header.segName(),
2450 .unwind_info => try self.unwind_info.write(self, out),2542 header.sectName(),
2451 .got => try self.got.write(self, stream.writer()),2543 @errorName(err),
2452 .stubs => try self.stubs.write(self, stream.writer()),2544 }) catch {};
2453 .la_symbol_ptr => try self.la_symbol_ptr.write(self, stream.writer()),2545 _ = self.has_errors.swap(true, .seq_cst);
2454 .tlv_ptr => try self.tlv_ptr.write(self, stream.writer()),2546 };
2455 .objc_stubs => try self.objc_stubs.write(self, stream.writer()),
2456 }
2457}2547}
24582548
2459fn updateLazyBindSize(self: *MachO) !void {2549fn updateLazyBindSizeWorker(self: *MachO) void {
2460 const tracy = trace(@src());2550 const tracy = trace(@src());
2461 defer tracy.end();2551 defer tracy.end();
2462 try self.lazy_bind.updateSize(self);2552 const doWork = struct {
2463 const sect_id = self.stubs_helper_sect_index.?;2553 fn doWork(macho_file: *MachO) !void {
2464 const out = &self.sections.items(.out)[sect_id];2554 try macho_file.lazy_bind.updateSize(macho_file);
2465 var stream = std.io.fixedBufferStream(out.items);2555 const sect_id = macho_file.stubs_helper_sect_index.?;
2466 try self.stubs_helper.write(self, stream.writer());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 };
2467}2590}
24682591
2469fn writeSectionsToFile(self: *MachO) !void {2592fn writeSectionsToFile(self: *MachO) !void {
...@@ -2791,13 +2914,13 @@ fn writeHeader(self: *MachO, ncmds: usize, sizeofcmds: usize) !void {...@@ -2791,13 +2914,13 @@ fn writeHeader(self: *MachO, ncmds: usize, sizeofcmds: usize) !void {
2791 header.flags |= macho.MH_NO_REEXPORTED_DYLIBS;2914 header.flags |= macho.MH_NO_REEXPORTED_DYLIBS;
2792 }2915 }
27932916
2794 if (self.has_tlv) {2917 if (self.has_tlv.load(.seq_cst)) {
2795 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;2918 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
2796 }2919 }
2797 if (self.binds_to_weak) {2920 if (self.binds_to_weak.load(.seq_cst)) {
2798 header.flags |= macho.MH_BINDS_TO_WEAK;2921 header.flags |= macho.MH_BINDS_TO_WEAK;
2799 }2922 }
2800 if (self.weak_defines) {2923 if (self.weak_defines.load(.seq_cst)) {
2801 header.flags |= macho.MH_WEAK_DEFINES;2924 header.flags |= macho.MH_WEAK_DEFINES;
2802 }2925 }
28032926
...@@ -3323,13 +3446,13 @@ fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !vo...@@ -3323,13 +3446,13 @@ fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !vo
33233446
3324 const mem_capacity = self.allocatedSizeVirtual(seg.vmaddr);3447 const mem_capacity = self.allocatedSizeVirtual(seg.vmaddr);
3325 if (needed_size > mem_capacity) {3448 if (needed_size > mem_capacity) {
3326 var err = try self.addErrorWithNotes(2);3449 var err = try self.base.addErrorWithNotes(2);
3327 try err.addMsg(self, "fatal linker error: cannot expand segment seg({d})({s}) in virtual memory", .{3450 try err.addMsg("fatal linker error: cannot expand segment seg({d})({s}) in virtual memory", .{
3328 seg_id,3451 seg_id,
3329 seg.segName(),3452 seg.segName(),
3330 });3453 });
3331 try err.addNote(self, "TODO: emit relocations to memory locations in self-hosted backends", .{});3454 try err.addNote("TODO: emit relocations to memory locations in self-hosted backends", .{});
3332 try err.addNote(self, "as a workaround, try increasing pre-allocated virtual memory of each segment", .{});3455 try err.addNote("as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
3333 }3456 }
33343457
3335 seg.vmsize = needed_size;3458 seg.vmsize = needed_size;
...@@ -3394,6 +3517,8 @@ pub fn getTarget(self: MachO) std.Target {...@@ -3394,6 +3517,8 @@ pub fn getTarget(self: MachO) std.Target {
3394/// the original file. This is super messy, but there doesn't seem any other3517/// the original file. This is super messy, but there doesn't seem any other
3395/// way to please the XNU.3518/// way to please the XNU.
3396pub fn invalidateKernelCache(dir: fs.Dir, sub_path: []const u8) !void {3519pub fn invalidateKernelCache(dir: fs.Dir, sub_path: []const u8) !void {
3520 const tracy = trace(@src());
3521 defer tracy.end();
3397 if (comptime builtin.target.isDarwin() and builtin.target.cpu.arch == .aarch64) {3522 if (comptime builtin.target.isDarwin() and builtin.target.cpu.arch == .aarch64) {
3398 try dir.copyFile(sub_path, dir, sub_path, .{});3523 try dir.copyFile(sub_path, dir, sub_path, .{});
3399 }3524 }
...@@ -3618,65 +3743,15 @@ pub fn eatPrefix(path: []const u8, prefix: []const u8) ?[]const u8 {...@@ -3618,65 +3743,15 @@ pub fn eatPrefix(path: []const u8, prefix: []const u8) ?[]const u8 {
3618 return null;3743 return null;
3619}3744}
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
3671pub fn reportParseError(3746pub fn reportParseError(
3672 self: *MachO,3747 self: *MachO,
3673 path: []const u8,3748 path: []const u8,
3674 comptime format: []const u8,3749 comptime format: []const u8,
3675 args: anytype,3750 args: anytype,
3676) error{OutOfMemory}!void {3751) error{OutOfMemory}!void {
3677 var err = try self.addErrorWithNotes(1);3752 var err = try self.base.addErrorWithNotes(1);
3678 try err.addMsg(self, format, args);3753 try err.addMsg(format, args);
3679 try err.addNote(self, "while parsing {s}", .{path});3754 try err.addNote("while parsing {s}", .{path});
3680}3755}
36813756
3682pub fn reportParseError2(3757pub fn reportParseError2(
...@@ -3685,9 +3760,9 @@ pub fn reportParseError2(...@@ -3685,9 +3760,9 @@ pub fn reportParseError2(
3685 comptime format: []const u8,3760 comptime format: []const u8,
3686 args: anytype,3761 args: anytype,
3687) error{OutOfMemory}!void {3762) error{OutOfMemory}!void {
3688 var err = try self.addErrorWithNotes(1);3763 var err = try self.base.addErrorWithNotes(1);
3689 try err.addMsg(self, format, args);3764 try err.addMsg(format, args);
3690 try err.addNote(self, "while parsing {}", .{self.getFile(file_index).?.fmtPath()});3765 try err.addNote("while parsing {}", .{self.getFile(file_index).?.fmtPath()});
3691}3766}
36923767
3693fn reportMissingLibraryError(3768fn reportMissingLibraryError(
...@@ -3696,10 +3771,10 @@ fn reportMissingLibraryError(...@@ -3696,10 +3771,10 @@ fn reportMissingLibraryError(
3696 comptime format: []const u8,3771 comptime format: []const u8,
3697 args: anytype,3772 args: anytype,
3698) error{OutOfMemory}!void {3773) error{OutOfMemory}!void {
3699 var err = try self.addErrorWithNotes(checked_paths.len);3774 var err = try self.base.addErrorWithNotes(checked_paths.len);
3700 try err.addMsg(self, format, args);3775 try err.addMsg(format, args);
3701 for (checked_paths) |path| {3776 for (checked_paths) |path| {
3702 try err.addNote(self, "tried {s}", .{path});3777 try err.addNote("tried {s}", .{path});
3703 }3778 }
3704}3779}
37053780
...@@ -3711,12 +3786,12 @@ fn reportMissingDependencyError(...@@ -3711,12 +3786,12 @@ fn reportMissingDependencyError(
3711 comptime format: []const u8,3786 comptime format: []const u8,
3712 args: anytype,3787 args: anytype,
3713) error{OutOfMemory}!void {3788) error{OutOfMemory}!void {
3714 var err = try self.addErrorWithNotes(2 + checked_paths.len);3789 var err = try self.base.addErrorWithNotes(2 + checked_paths.len);
3715 try err.addMsg(self, format, args);3790 try err.addMsg(format, args);
3716 try err.addNote(self, "while resolving {s}", .{path});3791 try err.addNote("while resolving {s}", .{path});
3717 try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()});3792 try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()});
3718 for (checked_paths) |p| {3793 for (checked_paths) |p| {
3719 try err.addNote(self, "tried {s}", .{p});3794 try err.addNote("tried {s}", .{p});
3720 }3795 }
3721}3796}
37223797
...@@ -3727,48 +3802,58 @@ fn reportDependencyError(...@@ -3727,48 +3802,58 @@ fn reportDependencyError(
3727 comptime format: []const u8,3802 comptime format: []const u8,
3728 args: anytype,3803 args: anytype,
3729) error{OutOfMemory}!void {3804) error{OutOfMemory}!void {
3730 var err = try self.addErrorWithNotes(2);3805 var err = try self.base.addErrorWithNotes(2);
3731 try err.addMsg(self, format, args);3806 try err.addMsg(format, args);
3732 try err.addNote(self, "while parsing {s}", .{path});3807 try err.addNote("while parsing {s}", .{path});
3733 try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()});3808 try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()});
3734}3809}
37353810
3736pub fn reportUnexpectedError(self: *MachO, comptime format: []const u8, args: anytype) error{OutOfMemory}!void {3811pub fn reportUnexpectedError(self: *MachO, comptime format: []const u8, args: anytype) error{OutOfMemory}!void {
3737 var err = try self.addErrorWithNotes(1);3812 var err = try self.base.addErrorWithNotes(1);
3738 try err.addMsg(self, format, args);3813 try err.addMsg(format, args);
3739 try err.addNote(self, "please report this as a linker bug on https://github.com/ziglang/zig/issues/new/choose", .{});3814 try err.addNote("please report this as a linker bug on https://github.com/ziglang/zig/issues/new/choose", .{});
3740}3815}
37413816
3742fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {3817fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
3743 const tracy = trace(@src());3818 const tracy = trace(@src());
3744 defer tracy.end();3819 defer tracy.end();
37453820
3821 if (self.dupes.keys().len == 0) return; // Nothing to do
3822
3823 const gpa = self.base.comp.gpa;
3746 const max_notes = 3;3824 const max_notes = 3;
37473825
3748 var has_dupes = false;3826 // We will sort by name, and then by file to ensure deterministic output.
3749 var it = self.dupes.iterator();3827 var keys = try std.ArrayList(SymbolResolver.Index).initCapacity(gpa, self.dupes.keys().len);
3750 while (it.next()) |entry| {3828 defer keys.deinit();
3751 const sym = self.resolver.keys.items[entry.key_ptr.* - 1];3829 keys.appendSliceAssumeCapacity(self.dupes.keys());
3752 const notes = entry.value_ptr.*;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).?;
3753 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);3839 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
37543840
3755 var err = try self.addErrorWithNotes(nnotes + 1);3841 var err = try self.base.addErrorWithNotes(nnotes + 1);
3756 try err.addMsg(self, "duplicate symbol definition: {s}", .{sym.getName(self)});3842 try err.addMsg("duplicate symbol definition: {s}", .{sym.getName(self)});
3757 try err.addNote(self, "defined by {}", .{sym.getFile(self).?.fmtPath()});3843 try err.addNote("defined by {}", .{sym.getFile(self).?.fmtPath()});
3758 has_dupes = true;
37593844
3760 var inote: usize = 0;3845 var inote: usize = 0;
3761 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {3846 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
3762 const file = self.getFile(notes.items[inote]).?;3847 const file = self.getFile(notes.items[inote]).?;
3763 try err.addNote(self, "defined by {}", .{file.fmtPath()});3848 try err.addNote("defined by {}", .{file.fmtPath()});
3764 }3849 }
37653850
3766 if (notes.items.len > max_notes) {3851 if (notes.items.len > max_notes) {
3767 const remaining = notes.items.len - max_notes;3852 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});
3769 }3854 }
3770 }3855 }
3771 if (has_dupes) return error.HasDuplicates;3856 return error.HasDuplicates;
3772}3857}
37733858
3774pub fn getDebugSymbols(self: *MachO) ?*DebugSymbols {3859pub fn getDebugSymbols(self: *MachO) ?*DebugSymbols {
...@@ -4367,6 +4452,13 @@ pub const Ref = struct {...@@ -4367,6 +4452,13 @@ pub const Ref = struct {
4367 return ref.index == other.index and ref.file == other.file;4452 return ref.index == other.index and ref.file == other.file;
4368 }4453 }
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
4370 pub fn getFile(ref: Ref, macho_file: *MachO) ?File {4462 pub fn getFile(ref: Ref, macho_file: *MachO) ?File {
4371 return macho_file.getFile(ref.file);4463 return macho_file.getFile(ref.file);
4372 }4464 }
...@@ -4487,6 +4579,11 @@ pub const SymbolResolver = struct {...@@ -4487,6 +4579,11 @@ pub const SymbolResolver = struct {
4487 pub const Index = u32;4579 pub const Index = u32;
4488};4580};
44894581
4582pub const String = struct {
4583 pos: u32 = 0,
4584 len: u32 = 0,
4585};
4586
4490const MachO = @This();4587const MachO = @This();
44914588
4492const std = @import("std");4589const std = @import("std");
...@@ -4523,6 +4620,7 @@ const Alignment = Atom.Alignment;...@@ -4523,6 +4620,7 @@ const Alignment = Atom.Alignment;
4523const Allocator = mem.Allocator;4620const Allocator = mem.Allocator;
4524const Archive = @import("MachO/Archive.zig");4621const Archive = @import("MachO/Archive.zig");
4525pub const Atom = @import("MachO/Atom.zig");4622pub const Atom = @import("MachO/Atom.zig");
4623const AtomicBool = std.atomic.Value(bool);
4526const Bind = bind.Bind;4624const Bind = bind.Bind;
4527const Cache = std.Build.Cache;4625const Cache = std.Build.Cache;
4528const CodeSignature = @import("MachO/CodeSignature.zig");4626const CodeSignature = @import("MachO/CodeSignature.zig");
...@@ -4540,7 +4638,6 @@ const ObjcStubsSection = synthetic.ObjcStubsSection;...@@ -4540,7 +4638,6 @@ const ObjcStubsSection = synthetic.ObjcStubsSection;
4540const Object = @import("MachO/Object.zig");4638const Object = @import("MachO/Object.zig");
4541const LazyBind = bind.LazyBind;4639const LazyBind = bind.LazyBind;
4542const LaSymbolPtrSection = synthetic.LaSymbolPtrSection;4640const LaSymbolPtrSection = synthetic.LaSymbolPtrSection;
4543const LibStub = tapi.LibStub;
4544const Liveness = @import("../Liveness.zig");4641const Liveness = @import("../Liveness.zig");
4545const LlvmObject = @import("../codegen/llvm.zig").Object;4642const LlvmObject = @import("../codegen/llvm.zig").Object;
4546const Md5 = std.crypto.hash.Md5;4643const Md5 = std.crypto.hash.Md5;
...@@ -4558,6 +4655,7 @@ const Thunk = thunks.Thunk;...@@ -4558,6 +4655,7 @@ const Thunk = thunks.Thunk;
4558const TlvPtrSection = synthetic.TlvPtrSection;4655const TlvPtrSection = synthetic.TlvPtrSection;
4559const Value = @import("../Value.zig");4656const Value = @import("../Value.zig");
4560const UnwindInfo = @import("MachO/UnwindInfo.zig");4657const UnwindInfo = @import("MachO/UnwindInfo.zig");
4658const WaitGroup = std.Thread.WaitGroup;
4561const WeakBind = bind.WeakBind;4659const WeakBind = bind.WeakBind;
4562const ZigGotSection = synthetic.ZigGotSection;4660const ZigGotSection = synthetic.ZigGotSection;
4563const ZigObject = @import("MachO/ZigObject.zig");4661const ZigObject = @import("MachO/ZigObject.zig");
src/link/MachO/Archive.zig+1-12
...@@ -1,21 +1,10 @@...@@ -1,21 +1,10 @@
1objects: std.ArrayListUnmanaged(Object) = .{},1objects: std.ArrayListUnmanaged(Object) = .{},
22
3pub fn isArchive(path: []const u8, fat_arch: ?fat.Arch) !bool {
4 const file = try std.fs.cwd().openFile(path, .{});
5 defer file.close();
6 if (fat_arch) |arch| {
7 try file.seekTo(arch.offset);
8 }
9 const magic = file.reader().readBytesNoEof(SARMAG) catch return false;
10 if (!mem.eql(u8, &magic, ARMAG)) return false;
11 return true;
12}
13
14pub fn deinit(self: *Archive, allocator: Allocator) void {3pub fn deinit(self: *Archive, allocator: Allocator) void {
15 self.objects.deinit(allocator);4 self.objects.deinit(allocator);
16}5}
176
18pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index: File.HandleIndex, fat_arch: ?fat.Arch) !void {7pub fn unpack(self: *Archive, macho_file: *MachO, path: []const u8, handle_index: File.HandleIndex, fat_arch: ?fat.Arch) !void {
19 const gpa = macho_file.base.comp.gpa;8 const gpa = macho_file.base.comp.gpa;
209
21 var arena = std.heap.ArenaAllocator.init(gpa);10 var arena = std.heap.ArenaAllocator.init(gpa);
src/link/MachO/Atom.zig+41-35
...@@ -2,7 +2,7 @@...@@ -2,7 +2,7 @@
2value: u64 = 0,2value: u64 = 0,
33
4/// Name of this Atom.4/// Name of this Atom.
5name: u32 = 0,5name: MachO.String = .{},
66
7/// Index into linker's input file table.7/// Index into linker's input file table.
8file: File.Index = 0,8file: File.Index = 0,
...@@ -26,7 +26,11 @@ off: u64 = 0,...@@ -26,7 +26,11 @@ off: u64 = 0,
26/// Index of this atom in the linker's atoms table.26/// Index of this atom in the linker's atoms table.
27atom_index: Index = 0,27atom_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
31/// Points to the previous and next neighbors, based on the `text_offset`.35/// Points to the previous and next neighbors, based on the `text_offset`.
32/// This can be used to find, for example, the capacity of this `TextBlock`.36/// This can be used to find, for example, the capacity of this `TextBlock`.
...@@ -38,7 +42,6 @@ extra: u32 = 0,...@@ -38,7 +42,6 @@ extra: u32 = 0,
38pub fn getName(self: Atom, macho_file: *MachO) [:0]const u8 {42pub fn getName(self: Atom, macho_file: *MachO) [:0]const u8 {
39 return switch (self.getFile(macho_file)) {43 return switch (self.getFile(macho_file)) {
40 .dylib => unreachable,44 .dylib => unreachable,
41 .zig_object => |x| x.strtab.getAssumeExists(self.name),
42 inline else => |x| x.getString(self.name),45 inline else => |x| x.getString(self.name),
43 };46 };
44}47}
...@@ -98,6 +101,14 @@ pub fn markUnwindRecordsDead(self: Atom, macho_file: *MachO) void {...@@ -98,6 +101,14 @@ pub fn markUnwindRecordsDead(self: Atom, macho_file: *MachO) void {
98 }101 }
99}102}
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
101pub fn getThunk(self: Atom, macho_file: *MachO) *Thunk {112pub fn getThunk(self: Atom, macho_file: *MachO) *Thunk {
102 const extra = self.getExtra(macho_file);113 const extra = self.getExtra(macho_file);
103 return macho_file.getThunk(extra.thunk);114 return macho_file.getThunk(extra.thunk);
...@@ -350,7 +361,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {...@@ -350,7 +361,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void {
350 _ = free_list.swapRemove(i);361 _ = free_list.swapRemove(i);
351 }362 }
352363
353 self.flags.alive = true;364 self.setAlive(true);
354}365}
355366
356pub fn shrink(self: *Atom, macho_file: *MachO) void {367pub fn shrink(self: *Atom, macho_file: *MachO) void {
...@@ -444,7 +455,7 @@ pub fn freeRelocs(self: *Atom, macho_file: *MachO) void {...@@ -444,7 +455,7 @@ pub fn freeRelocs(self: *Atom, macho_file: *MachO) void {
444pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {455pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
445 const tracy = trace(@src());456 const tracy = trace(@src());
446 defer tracy.end();457 defer tracy.end();
447 assert(self.flags.alive);458 assert(self.isAlive());
448459
449 const relocs = self.getRelocs(macho_file);460 const relocs = self.getRelocs(macho_file);
450461
...@@ -455,12 +466,12 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {...@@ -455,12 +466,12 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
455 .branch => {466 .branch => {
456 const symbol = rel.getTargetSymbol(self, macho_file);467 const symbol = rel.getTargetSymbol(self, macho_file);
457 if (symbol.flags.import or (symbol.flags.@"export" and symbol.flags.weak) or symbol.flags.interposable) {468 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 });
459 if (symbol.flags.weak) {470 if (symbol.flags.weak) {
460 macho_file.binds_to_weak = true;471 macho_file.binds_to_weak.store(true, .seq_cst);
461 }472 }
462 } else if (mem.startsWith(u8, symbol.getName(macho_file), "_objc_msgSend$")) {473 } else if (mem.startsWith(u8, symbol.getName(macho_file), "_objc_msgSend$")) {
463 symbol.flags.objc_stubs = true;474 symbol.setSectionFlags(.{ .objc_stubs = true });
464 }475 }
465 },476 },
466477
...@@ -474,19 +485,19 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {...@@ -474,19 +485,19 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
474 symbol.flags.interposable or485 symbol.flags.interposable or
475 macho_file.getTarget().cpu.arch == .aarch64) // TODO relax on arm64486 macho_file.getTarget().cpu.arch == .aarch64) // TODO relax on arm64
476 {487 {
477 symbol.flags.needs_got = true;488 symbol.setSectionFlags(.{ .needs_got = true });
478 if (symbol.flags.weak) {489 if (symbol.flags.weak) {
479 macho_file.binds_to_weak = true;490 macho_file.binds_to_weak.store(true, .seq_cst);
480 }491 }
481 }492 }
482 },493 },
483494
484 .zig_got_load => {495 .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);
486 },497 },
487498
488 .got => {499 .got => {
489 rel.getTargetSymbol(self, macho_file).flags.needs_got = true;500 rel.getTargetSymbol(self, macho_file).setSectionFlags(.{ .needs_got = true });
490 },501 },
491502
492 .tlv,503 .tlv,
...@@ -502,9 +513,9 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {...@@ -502,9 +513,9 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
502 );513 );
503 }514 }
504 if (symbol.flags.import or (symbol.flags.@"export" and symbol.flags.weak) or symbol.flags.interposable) {515 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 });
506 if (symbol.flags.weak) {517 if (symbol.flags.weak) {
507 macho_file.binds_to_weak = true;518 macho_file.binds_to_weak.store(true, .seq_cst);
508 }519 }
509 }520 }
510 },521 },
...@@ -514,17 +525,17 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {...@@ -514,17 +525,17 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
514 if (rel.tag == .@"extern") {525 if (rel.tag == .@"extern") {
515 const symbol = rel.getTargetSymbol(self, macho_file);526 const symbol = rel.getTargetSymbol(self, macho_file);
516 if (symbol.isTlvInit(macho_file)) {527 if (symbol.isTlvInit(macho_file)) {
517 macho_file.has_tlv = true;528 macho_file.has_tlv.store(true, .seq_cst);
518 continue;529 continue;
519 }530 }
520 if (symbol.flags.import) {531 if (symbol.flags.import) {
521 if (symbol.flags.weak) {532 if (symbol.flags.weak) {
522 macho_file.binds_to_weak = true;533 macho_file.binds_to_weak.store(true, .seq_cst);
523 }534 }
524 continue;535 continue;
525 }536 }
526 if (symbol.flags.@"export" and symbol.flags.weak) {537 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);
528 }539 }
529 }540 }
530 }541 }
...@@ -548,6 +559,8 @@ fn reportUndefSymbol(self: Atom, rel: Relocation, macho_file: *MachO) !bool {...@@ -548,6 +559,8 @@ fn reportUndefSymbol(self: Atom, rel: Relocation, macho_file: *MachO) !bool {
548 const file = self.getFile(macho_file);559 const file = self.getFile(macho_file);
549 const ref = file.getSymbolRef(rel.target, macho_file);560 const ref = file.getSymbolRef(rel.target, macho_file);
550 if (ref.getFile(macho_file) == null) {561 if (ref.getFile(macho_file) == null) {
562 macho_file.undefs_mutex.lock();
563 defer macho_file.undefs_mutex.unlock();
551 const gpa = macho_file.base.comp.gpa;564 const gpa = macho_file.base.comp.gpa;
552 const gop = try macho_file.undefs.getOrPut(gpa, file.getGlobals()[rel.target]);565 const gop = try macho_file.undefs.getOrPut(gpa, file.getGlobals()[rel.target]);
553 if (!gop.found_existing) {566 if (!gop.found_existing) {
...@@ -724,7 +737,7 @@ fn resolveRelocInner(...@@ -724,7 +737,7 @@ fn resolveRelocInner(
724 assert(rel.tag == .@"extern");737 assert(rel.tag == .@"extern");
725 assert(rel.meta.length == 2);738 assert(rel.meta.length == 2);
726 assert(rel.meta.pcrel);739 assert(rel.meta.pcrel);
727 if (rel.getTargetSymbol(self, macho_file).flags.has_got) {740 if (rel.getTargetSymbol(self, macho_file).getSectionFlags().has_got) {
728 try writer.writeInt(i32, @intCast(G + A - P), .little);741 try writer.writeInt(i32, @intCast(G + A - P), .little);
729 } else {742 } else {
730 try x86_64.relaxGotLoad(self, code[rel_offset - 3 ..], rel, macho_file);743 try x86_64.relaxGotLoad(self, code[rel_offset - 3 ..], rel, macho_file);
...@@ -748,7 +761,7 @@ fn resolveRelocInner(...@@ -748,7 +761,7 @@ fn resolveRelocInner(
748 assert(rel.meta.length == 2);761 assert(rel.meta.length == 2);
749 assert(rel.meta.pcrel);762 assert(rel.meta.pcrel);
750 const sym = rel.getTargetSymbol(self, macho_file);763 const sym = rel.getTargetSymbol(self, macho_file);
751 if (sym.flags.tlv_ptr) {764 if (sym.getSectionFlags().tlv_ptr) {
752 const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));765 const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
753 try writer.writeInt(i32, @intCast(S_ + A - P), .little);766 try writer.writeInt(i32, @intCast(S_ + A - P), .little);
754 } else {767 } else {
...@@ -776,7 +789,7 @@ fn resolveRelocInner(...@@ -776,7 +789,7 @@ fn resolveRelocInner(
776 const target = switch (rel.type) {789 const target = switch (rel.type) {
777 .page => S + A,790 .page => S + A,
778 .got_load_page => G + A,791 .got_load_page => G + A,
779 .tlvp_page => if (sym.flags.tlv_ptr) blk: {792 .tlvp_page => if (sym.getSectionFlags().tlv_ptr) blk: {
780 const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));793 const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
781 break :blk S_ + A;794 break :blk S_ + A;
782 } else S + A,795 } else S + A,
...@@ -831,7 +844,7 @@ fn resolveRelocInner(...@@ -831,7 +844,7 @@ fn resolveRelocInner(
831844
832 const sym = rel.getTargetSymbol(self, macho_file);845 const sym = rel.getTargetSymbol(self, macho_file);
833 const target = target: {846 const target = target: {
834 const target = if (sym.flags.tlv_ptr) blk: {847 const target = if (sym.getSectionFlags().tlv_ptr) blk: {
835 const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));848 const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
836 break :blk S_ + A;849 break :blk S_ + A;
837 } else S + A;850 } else S + A;
...@@ -869,7 +882,7 @@ fn resolveRelocInner(...@@ -869,7 +882,7 @@ fn resolveRelocInner(
869 }882 }
870 };883 };
871884
872 var inst = if (sym.flags.tlv_ptr) aarch64.Instruction{885 var inst = if (sym.getSectionFlags().tlv_ptr) aarch64.Instruction{
873 .load_store_register = .{886 .load_store_register = .{
874 .rt = reg_info.rd,887 .rt = reg_info.rd,
875 .rn = reg_info.rn,888 .rn = reg_info.rn,
...@@ -906,15 +919,15 @@ const x86_64 = struct {...@@ -906,15 +919,15 @@ const x86_64 = struct {
906 encode(&.{inst}, code) catch return error.RelaxFail;919 encode(&.{inst}, code) catch return error.RelaxFail;
907 },920 },
908 else => |x| {921 else => |x| {
909 var err = try macho_file.addErrorWithNotes(2);922 var err = try macho_file.base.addErrorWithNotes(2);
910 try err.addMsg(macho_file, "{s}: 0x{x}: 0x{x}: failed to relax relocation of type {}", .{923 try err.addMsg("{s}: 0x{x}: 0x{x}: failed to relax relocation of type {}", .{
911 self.getName(macho_file),924 self.getName(macho_file),
912 self.getAddress(macho_file),925 self.getAddress(macho_file),
913 rel.offset,926 rel.offset,
914 rel.fmtPretty(.x86_64),927 rel.fmtPretty(.x86_64),
915 });928 });
916 try err.addNote(macho_file, "expected .mov instruction but found .{s}", .{@tagName(x)});929 try err.addNote("expected .mov instruction but found .{s}", .{@tagName(x)});
917 try err.addNote(macho_file, "while parsing {}", .{self.getFile(macho_file).fmtPath()});930 try err.addNote("while parsing {}", .{self.getFile(macho_file).fmtPath()});
918 return error.RelaxFailUnexpectedInstruction;931 return error.RelaxFailUnexpectedInstruction;
919 },932 },
920 }933 }
...@@ -1142,7 +1155,7 @@ fn format2(...@@ -1142,7 +1155,7 @@ fn format2(
1142 atom.out_n_sect, atom.alignment, atom.size,1155 atom.out_n_sect, atom.alignment, atom.size,
1143 atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk,1156 atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk,
1144 });1157 });
1145 if (!atom.flags.alive) try writer.writeAll(" : [*]");1158 if (!atom.isAlive()) try writer.writeAll(" : [*]");
1146 if (atom.getUnwindRecords(macho_file).len > 0) {1159 if (atom.getUnwindRecords(macho_file).len > 0) {
1147 try writer.writeAll(" : unwind{ ");1160 try writer.writeAll(" : unwind{ ");
1148 const extra = atom.getExtra(macho_file);1161 const extra = atom.getExtra(macho_file);
...@@ -1158,14 +1171,6 @@ fn format2(...@@ -1158,14 +1171,6 @@ fn format2(
11581171
1159pub const Index = u32;1172pub 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
1169pub const Extra = struct {1174pub const Extra = struct {
1170 /// Index of the range extension thunk of this atom.1175 /// Index of the range extension thunk of this atom.
1171 thunk: u32 = 0,1176 thunk: u32 = 0,
...@@ -1209,6 +1214,7 @@ const trace = @import("../../tracy.zig").trace;...@@ -1209,6 +1214,7 @@ const trace = @import("../../tracy.zig").trace;
12091214
1210const Allocator = mem.Allocator;1215const Allocator = mem.Allocator;
1211const Atom = @This();1216const Atom = @This();
1217const AtomicBool = std.atomic.Value(bool);
1212const File = @import("file.zig").File;1218const File = @import("file.zig").File;
1213const MachO = @import("../MachO.zig");1219const MachO = @import("../MachO.zig");
1214const Object = @import("Object.zig");1220const Object = @import("Object.zig");
src/link/MachO/CodeSignature.zig+4
...@@ -7,6 +7,7 @@ const log = std.log.scoped(.link);...@@ -7,6 +7,7 @@ const log = std.log.scoped(.link);
7const macho = std.macho;7const macho = std.macho;
8const mem = std.mem;8const mem = std.mem;
9const testing = std.testing;9const testing = std.testing;
10const trace = @import("../../tracy.zig").trace;
10const Allocator = mem.Allocator;11const Allocator = mem.Allocator;
11const Hasher = @import("hasher.zig").ParallelHasher;12const Hasher = @import("hasher.zig").ParallelHasher;
12const MachO = @import("../MachO.zig");13const MachO = @import("../MachO.zig");
...@@ -264,6 +265,9 @@ pub fn writeAdhocSignature(...@@ -264,6 +265,9 @@ pub fn writeAdhocSignature(
264 opts: WriteOpts,265 opts: WriteOpts,
265 writer: anytype,266 writer: anytype,
266) !void {267) !void {
268 const tracy = trace(@src());
269 defer tracy.end();
270
267 const allocator = macho_file.base.comp.gpa;271 const allocator = macho_file.base.comp.gpa;
268272
269 var header: macho.SuperBlob = .{273 var header: macho.SuperBlob = .{
src/link/MachO/Dylib.zig+36-29
...@@ -1,5 +1,9 @@...@@ -1,5 +1,9 @@
1/// Non-zero for fat dylibs
2offset: u64,
1path: []const u8,3path: []const u8,
2index: File.Index,4index: File.Index,
5file_handle: File.HandleIndex,
6tag: enum { dylib, tbd },
37
4exports: std.MultiArrayList(Export) = .{},8exports: std.MultiArrayList(Export) = .{},
5strtab: std.ArrayListUnmanaged(u8) = .{},9strtab: std.ArrayListUnmanaged(u8) = .{},
...@@ -11,7 +15,7 @@ symbols_extra: std.ArrayListUnmanaged(u32) = .{},...@@ -11,7 +15,7 @@ symbols_extra: std.ArrayListUnmanaged(u32) = .{},
11globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},15globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},
12dependents: std.ArrayListUnmanaged(Id) = .{},16dependents: std.ArrayListUnmanaged(Id) = .{},
13rpaths: std.StringArrayHashMapUnmanaged(void) = .{},17rpaths: std.StringArrayHashMapUnmanaged(void) = .{},
14umbrella: File.Index = 0,18umbrella: File.Index,
15platform: ?MachO.Platform = null,19platform: ?MachO.Platform = null,
1620
17needed: bool,21needed: bool,
...@@ -23,16 +27,6 @@ referenced: bool = false,...@@ -23,16 +27,6 @@ referenced: bool = false,
2327
24output_symtab_ctx: MachO.SymtabCtx = .{},28output_symtab_ctx: MachO.SymtabCtx = .{},
2529
26pub fn isDylib(path: []const u8, fat_arch: ?fat.Arch) !bool {
27 const file = try std.fs.cwd().openFile(path, .{});
28 defer file.close();
29 if (fat_arch) |arch| {
30 try file.seekTo(arch.offset);
31 }
32 const header = file.reader().readStruct(macho.mach_header_64) catch return false;
33 return header.filetype == macho.MH_DYLIB;
34}
35
36pub fn deinit(self: *Dylib, allocator: Allocator) void {30pub fn deinit(self: *Dylib, allocator: Allocator) void {
37 allocator.free(self.path);31 allocator.free(self.path);
38 self.exports.deinit(allocator);32 self.exports.deinit(allocator);
...@@ -51,12 +45,21 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {...@@ -51,12 +45,21 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {
51 self.rpaths.deinit(allocator);45 self.rpaths.deinit(allocator);
52}46}
5347
54pub fn parse(self: *Dylib, macho_file: *MachO, file: std.fs.File, fat_arch: ?fat.Arch) !void {48pub fn parse(self: *Dylib, macho_file: *MachO) !void {
49 switch (self.tag) {
50 .tbd => try self.parseTbd(macho_file),
51 .dylib => try self.parseBinary(macho_file),
52 }
53 try self.initSymbols(macho_file);
54}
55
56fn parseBinary(self: *Dylib, macho_file: *MachO) !void {
55 const tracy = trace(@src());57 const tracy = trace(@src());
56 defer tracy.end();58 defer tracy.end();
5759
58 const gpa = macho_file.base.comp.gpa;60 const gpa = macho_file.base.comp.gpa;
59 const offset = if (fat_arch) |ar| ar.offset else 0;61 const file = macho_file.getFileHandle(self.file_handle);
62 const offset = self.offset;
6063
61 log.debug("parsing dylib from binary: {s}", .{self.path});64 log.debug("parsing dylib from binary: {s}", .{self.path});
6265
...@@ -258,13 +261,7 @@ fn parseTrie(self: *Dylib, data: []const u8, macho_file: *MachO) !void {...@@ -258,13 +261,7 @@ fn parseTrie(self: *Dylib, data: []const u8, macho_file: *MachO) !void {
258 try self.parseTrieNode(&it, gpa, arena.allocator(), "");261 try self.parseTrieNode(&it, gpa, arena.allocator(), "");
259}262}
260263
261pub fn parseTbd(264fn parseTbd(self: *Dylib, macho_file: *MachO) !void {
262 self: *Dylib,
263 cpu_arch: std.Target.Cpu.Arch,
264 platform: MachO.Platform,
265 lib_stub: LibStub,
266 macho_file: *MachO,
267) !void {
268 const tracy = trace(@src());265 const tracy = trace(@src());
269 defer tracy.end();266 defer tracy.end();
270267
...@@ -272,6 +269,12 @@ pub fn parseTbd(...@@ -272,6 +269,12 @@ pub fn parseTbd(
272269
273 log.debug("parsing dylib from stub: {s}", .{self.path});270 log.debug("parsing dylib from stub: {s}", .{self.path});
274271
272 const file = macho_file.getFileHandle(self.file_handle);
273 var lib_stub = LibStub.loadFromFile(gpa, file) catch |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();
275 const umbrella_lib = lib_stub.inner[0];278 const umbrella_lib = lib_stub.inner[0];
276279
277 {280 {
...@@ -290,7 +293,8 @@ pub fn parseTbd(...@@ -290,7 +293,8 @@ pub fn parseTbd(
290293
291 log.debug(" (install_name '{s}')", .{umbrella_lib.installName()});294 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
295 var matcher = try TargetMatcher.init(gpa, cpu_arch, self.platform.?.toApplePlatform());299 var matcher = try TargetMatcher.init(gpa, cpu_arch, self.platform.?.toApplePlatform());
296 defer matcher.deinit();300 defer matcher.deinit();
...@@ -495,7 +499,7 @@ fn addObjCExport(...@@ -495,7 +499,7 @@ fn addObjCExport(
495 try self.addExport(allocator, full_name, .{});499 try self.addExport(allocator, full_name, .{});
496}500}
497501
498pub fn initSymbols(self: *Dylib, macho_file: *MachO) !void {502fn initSymbols(self: *Dylib, macho_file: *MachO) !void {
499 const gpa = macho_file.base.comp.gpa;503 const gpa = macho_file.base.comp.gpa;
500504
501 const nsyms = self.exports.items(.name).len;505 const nsyms = self.exports.items(.name).len;
...@@ -609,15 +613,18 @@ pub inline fn getUmbrella(self: Dylib, macho_file: *MachO) *Dylib {...@@ -609,15 +613,18 @@ pub inline fn getUmbrella(self: Dylib, macho_file: *MachO) *Dylib {
609 return macho_file.getFile(self.umbrella).?.dylib;613 return macho_file.getFile(self.umbrella).?.dylib;
610}614}
611615
612fn addString(self: *Dylib, allocator: Allocator, name: []const u8) !u32 {616fn addString(self: *Dylib, allocator: Allocator, name: []const u8) !MachO.String {
613 const off = @as(u32, @intCast(self.strtab.items.len));617 const off = @as(u32, @intCast(self.strtab.items.len));
614 try self.strtab.writer(allocator).print("{s}\x00", .{name});618 try self.strtab.ensureUnusedCapacity(allocator, name.len + 1);
615 return off;619 self.strtab.appendSliceAssumeCapacity(name);
620 self.strtab.appendAssumeCapacity(0);
621 return .{ .pos = off, .len = @intCast(name.len + 1) };
616}622}
617623
618pub fn getString(self: Dylib, off: u32) [:0]const u8 {624pub fn getString(self: Dylib, string: MachO.String) [:0]const u8 {
619 assert(off < self.strtab.items.len);625 assert(string.pos < self.strtab.items.len and string.pos + string.len <= self.strtab.items.len);
620 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);626 if (string.len == 0) return "";
627 return self.strtab.items[string.pos..][0 .. string.len - 1 :0];
621}628}
622629
623pub fn asFile(self: *Dylib) File {630pub fn asFile(self: *Dylib) File {
...@@ -931,7 +938,7 @@ pub const Id = struct {...@@ -931,7 +938,7 @@ pub const Id = struct {
931};938};
932939
933const Export = struct {940const Export = struct {
934 name: u32,941 name: MachO.String,
935 flags: Flags,942 flags: Flags,
936943
937 const Flags = packed struct {944 const Flags = packed struct {
src/link/MachO/InternalObject.zig+25-24
...@@ -53,7 +53,7 @@ pub fn init(self: *InternalObject, allocator: Allocator) !void {...@@ -53,7 +53,7 @@ pub fn init(self: *InternalObject, allocator: Allocator) !void {
5353
54pub fn initSymbols(self: *InternalObject, macho_file: *MachO) !void {54pub fn initSymbols(self: *InternalObject, macho_file: *MachO) !void {
55 const newSymbolAssumeCapacity = struct {55 const newSymbolAssumeCapacity = struct {
56 fn newSymbolAssumeCapacity(obj: *InternalObject, name: u32, args: struct {56 fn newSymbolAssumeCapacity(obj: *InternalObject, name: MachO.String, args: struct {
57 type: u8 = macho.N_UNDF | macho.N_EXT,57 type: u8 = macho.N_UNDF | macho.N_EXT,
58 desc: u16 = 0,58 desc: u16 = 0,
59 }) Symbol.Index {59 }) Symbol.Index {
...@@ -69,7 +69,7 @@ pub fn initSymbols(self: *InternalObject, macho_file: *MachO) !void {...@@ -69,7 +69,7 @@ pub fn initSymbols(self: *InternalObject, macho_file: *MachO) !void {
69 const nlist_idx: u32 = @intCast(obj.symtab.items.len);69 const nlist_idx: u32 = @intCast(obj.symtab.items.len);
70 const nlist = obj.symtab.addOneAssumeCapacity();70 const nlist = obj.symtab.addOneAssumeCapacity();
71 nlist.* = .{71 nlist.* = .{
72 .n_strx = name,72 .n_strx = name.pos,
73 .n_type = args.type,73 .n_type = args.type,
74 .n_sect = 0,74 .n_sect = 0,
75 .n_desc = args.desc,75 .n_desc = args.desc,
...@@ -197,16 +197,16 @@ pub fn resolveBoundarySymbols(self: *InternalObject, macho_file: *MachO) !void {...@@ -197,16 +197,16 @@ pub fn resolveBoundarySymbols(self: *InternalObject, macho_file: *MachO) !void {
197 try self.globals.ensureUnusedCapacity(gpa, nsyms);197 try self.globals.ensureUnusedCapacity(gpa, nsyms);
198198
199 for (boundary_symbols.keys(), boundary_symbols.values()) |name, ref| {199 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);
201 const sym_index = self.addSymbolAssumeCapacity();201 const sym_index = self.addSymbolAssumeCapacity();
202 self.boundary_symbols.appendAssumeCapacity(sym_index);202 self.boundary_symbols.appendAssumeCapacity(sym_index);
203 const sym = &self.symbols.items[sym_index];203 const sym = &self.symbols.items[sym_index];
204 sym.name = name_off;204 sym.name = name_str;
205 sym.visibility = .local;205 sym.visibility = .local;
206 const nlist_idx: u32 = @intCast(self.symtab.items.len);206 const nlist_idx: u32 = @intCast(self.symtab.items.len);
207 const nlist = self.symtab.addOneAssumeCapacity();207 const nlist = self.symtab.addOneAssumeCapacity();
208 nlist.* = .{208 nlist.* = .{
209 .n_strx = name_off,209 .n_strx = name_str.pos,
210 .n_type = macho.N_SECT,210 .n_type = macho.N_SECT,
211 .n_sect = 0,211 .n_sect = 0,
212 .n_desc = 0,212 .n_desc = 0,
...@@ -273,7 +273,7 @@ fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_fil...@@ -273,7 +273,7 @@ fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_fil
273 const nlist_idx: u32 = @intCast(self.symtab.items.len);273 const nlist_idx: u32 = @intCast(self.symtab.items.len);
274 const nlist = try self.symtab.addOne(gpa);274 const nlist = try self.symtab.addOne(gpa);
275 nlist.* = .{275 nlist.* = .{
276 .n_strx = name_str,276 .n_strx = name_str.pos,
277 .n_type = macho.N_SECT,277 .n_type = macho.N_SECT,
278 .n_sect = @intCast(n_sect + 1),278 .n_sect = @intCast(n_sect + 1),
279 .n_desc = 0,279 .n_desc = 0,
...@@ -373,15 +373,15 @@ pub fn resolveObjcMsgSendSymbols(self: *InternalObject, macho_file: *MachO) !voi...@@ -373,15 +373,15 @@ pub fn resolveObjcMsgSendSymbols(self: *InternalObject, macho_file: *MachO) !voi
373 const name = MachO.eatPrefix(sym_name, "_objc_msgSend$").?;373 const name = MachO.eatPrefix(sym_name, "_objc_msgSend$").?;
374 const selrefs_index = try self.addObjcMsgsendSections(name, macho_file);374 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);
377 const sym_index = try self.addSymbol(gpa);377 const sym_index = try self.addSymbol(gpa);
378 const sym = &self.symbols.items[sym_index];378 const sym = &self.symbols.items[sym_index];
379 sym.name = name_off;379 sym.name = name_str;
380 sym.visibility = .hidden;380 sym.visibility = .hidden;
381 const nlist_idx: u32 = @intCast(self.symtab.items.len);381 const nlist_idx: u32 = @intCast(self.symtab.items.len);
382 const nlist = try self.symtab.addOne(gpa);382 const nlist = try self.symtab.addOne(gpa);
383 nlist.* = .{383 nlist.* = .{
384 .n_strx = name_off,384 .n_strx = name_str.pos,
385 .n_type = macho.N_SECT | macho.N_EXT | macho.N_PEXT,385 .n_type = macho.N_SECT | macho.N_EXT | macho.N_PEXT,
386 .n_sect = 0,386 .n_sect = 0,
387 .n_desc = 0,387 .n_desc = 0,
...@@ -389,7 +389,7 @@ pub fn resolveObjcMsgSendSymbols(self: *InternalObject, macho_file: *MachO) !voi...@@ -389,7 +389,7 @@ pub fn resolveObjcMsgSendSymbols(self: *InternalObject, macho_file: *MachO) !voi
389 };389 };
390 sym.nlist_idx = nlist_idx;390 sym.nlist_idx = nlist_idx;
391 sym.extra = try self.addSymbolExtra(gpa, .{ .objc_selrefs = selrefs_index });391 sym.extra = try self.addSymbolExtra(gpa, .{ .objc_selrefs = selrefs_index });
392 sym.flags.objc_stubs = true;392 sym.setSectionFlags(.{ .objc_stubs = true });
393393
394 const idx = ref.getFile(macho_file).?.object.globals.items[ref.index];394 const idx = ref.getFile(macho_file).?.object.globals.items[ref.index];
395 try self.globals.append(gpa, idx);395 try self.globals.append(gpa, idx);
...@@ -427,7 +427,7 @@ pub fn resolveLiterals(self: *InternalObject, lp: *MachO.LiteralPool, macho_file...@@ -427,7 +427,7 @@ pub fn resolveLiterals(self: *InternalObject, lp: *MachO.LiteralPool, macho_file
427 const lp_sym = lp.getSymbol(res.index, macho_file);427 const lp_sym = lp.getSymbol(res.index, macho_file);
428 const lp_atom = lp_sym.getAtom(macho_file).?;428 const lp_atom = lp_sym.getAtom(macho_file).?;
429 lp_atom.alignment = lp_atom.alignment.max(atom.alignment);429 lp_atom.alignment = lp_atom.alignment.max(atom.alignment);
430 atom.flags.alive = false;430 atom.setAlive(false);
431 }431 }
432 atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);432 atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
433 }433 }
...@@ -439,7 +439,7 @@ pub fn dedupLiterals(self: *InternalObject, lp: MachO.LiteralPool, macho_file: *...@@ -439,7 +439,7 @@ pub fn dedupLiterals(self: *InternalObject, lp: MachO.LiteralPool, macho_file: *
439439
440 for (self.getAtoms()) |atom_index| {440 for (self.getAtoms()) |atom_index| {
441 const atom = self.getAtom(atom_index) orelse continue;441 const atom = self.getAtom(atom_index) orelse continue;
442 if (!atom.flags.alive) continue;442 if (!atom.isAlive()) continue;
443443
444 const relocs = blk: {444 const relocs = blk: {
445 const extra = atom.getExtra(macho_file);445 const extra = atom.getExtra(macho_file);
...@@ -464,7 +464,7 @@ pub fn dedupLiterals(self: *InternalObject, lp: MachO.LiteralPool, macho_file: *...@@ -464,7 +464,7 @@ pub fn dedupLiterals(self: *InternalObject, lp: MachO.LiteralPool, macho_file: *
464 }464 }
465465
466 for (self.symbols.items) |*sym| {466 for (self.symbols.items) |*sym| {
467 if (!sym.flags.objc_stubs) continue;467 if (!sym.getSectionFlags().objc_stubs) continue;
468 const extra = sym.getExtra(macho_file);468 const extra = sym.getExtra(macho_file);
469 const file = sym.getFile(macho_file).?;469 const file = sym.getFile(macho_file).?;
470 if (file.getIndex() != self.index) continue;470 if (file.getIndex() != self.index) continue;
...@@ -490,20 +490,20 @@ pub fn scanRelocs(self: *InternalObject, macho_file: *MachO) void {...@@ -490,20 +490,20 @@ pub fn scanRelocs(self: *InternalObject, macho_file: *MachO) void {
490 if (self.getEntryRef(macho_file)) |ref| {490 if (self.getEntryRef(macho_file)) |ref| {
491 if (ref.getFile(macho_file) != null) {491 if (ref.getFile(macho_file) != null) {
492 const sym = ref.getSymbol(macho_file).?;492 const sym = ref.getSymbol(macho_file).?;
493 if (sym.flags.import) sym.flags.stubs = true;493 if (sym.flags.import) sym.setSectionFlags(.{ .stubs = true });
494 }494 }
495 }495 }
496 if (self.getDyldStubBinderRef(macho_file)) |ref| {496 if (self.getDyldStubBinderRef(macho_file)) |ref| {
497 if (ref.getFile(macho_file) != null) {497 if (ref.getFile(macho_file) != null) {
498 const sym = ref.getSymbol(macho_file).?;498 const sym = ref.getSymbol(macho_file).?;
499 sym.flags.needs_got = true;499 sym.setSectionFlags(.{ .needs_got = true });
500 }500 }
501 }501 }
502 if (self.getObjcMsgSendRef(macho_file)) |ref| {502 if (self.getObjcMsgSendRef(macho_file)) |ref| {
503 if (ref.getFile(macho_file) != null) {503 if (ref.getFile(macho_file) != null) {
504 const sym = ref.getSymbol(macho_file).?;504 const sym = ref.getSymbol(macho_file).?;
505 // TODO is it always needed, or only if we are synthesising fast stubs505 // 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 });
507 }507 }
508 }508 }
509}509}
...@@ -570,7 +570,7 @@ pub fn writeAtoms(self: *InternalObject, macho_file: *MachO) !void {...@@ -570,7 +570,7 @@ pub fn writeAtoms(self: *InternalObject, macho_file: *MachO) !void {
570570
571 for (self.getAtoms()) |atom_index| {571 for (self.getAtoms()) |atom_index| {
572 const atom = self.getAtom(atom_index) orelse continue;572 const atom = self.getAtom(atom_index) orelse continue;
573 if (!atom.flags.alive) continue;573 if (!atom.isAlive()) continue;
574 const sect = atom.getInputSection(macho_file);574 const sect = atom.getInputSection(macho_file);
575 if (sect.isZerofill()) continue;575 if (sect.isZerofill()) continue;
576 const off = std.math.cast(usize, atom.value) orelse return error.Overflow;576 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...@@ -624,17 +624,18 @@ fn getSectionData(self: *const InternalObject, index: u32) error{Overflow}![]con
624 @panic("ref to non-existent section");624 @panic("ref to non-existent section");
625}625}
626626
627pub fn addString(self: *InternalObject, allocator: Allocator, name: []const u8) !u32 {627pub fn addString(self: *InternalObject, allocator: Allocator, string: []const u8) !MachO.String {
628 const off: u32 = @intCast(self.strtab.items.len);628 const off: u32 = @intCast(self.strtab.items.len);
629 try self.strtab.ensureUnusedCapacity(allocator, name.len + 1);629 try self.strtab.ensureUnusedCapacity(allocator, string.len + 1);
630 self.strtab.appendSliceAssumeCapacity(name);630 self.strtab.appendSliceAssumeCapacity(string);
631 self.strtab.appendAssumeCapacity(0);631 self.strtab.appendAssumeCapacity(0);
632 return off;632 return .{ .pos = off, .len = @intCast(string.len + 1) };
633}633}
634634
635pub fn getString(self: InternalObject, off: u32) [:0]const u8 {635pub fn getString(self: InternalObject, string: MachO.String) [:0]const u8 {
636 assert(off < self.strtab.items.len);636 assert(string.pos < self.strtab.items.len and string.pos + string.len <= self.strtab.items.len);
637 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);637 if (string.len == 0) return "";
638 return self.strtab.items[string.pos..][0 .. string.len - 1 :0];
638}639}
639640
640pub fn asFile(self: *InternalObject) File {641pub fn asFile(self: *InternalObject) File {
src/link/MachO/Object.zig+46-44
...@@ -38,13 +38,6 @@ compact_unwind_ctx: CompactUnwindCtx = .{},...@@ -38,13 +38,6 @@ compact_unwind_ctx: CompactUnwindCtx = .{},
38output_symtab_ctx: MachO.SymtabCtx = .{},38output_symtab_ctx: MachO.SymtabCtx = .{},
39output_ar_state: Archive.ArState = .{},39output_ar_state: Archive.ArState = .{},
4040
41pub fn isObject(path: []const u8) !bool {
42 const file = try std.fs.cwd().openFile(path, .{});
43 defer file.close();
44 const header = file.reader().readStruct(macho.mach_header_64) catch return false;
45 return header.filetype == macho.MH_OBJECT;
46}
47
48pub fn deinit(self: *Object, allocator: Allocator) void {41pub fn deinit(self: *Object, allocator: Allocator) void {
49 if (self.in_archive) |*ar| allocator.free(ar.path);42 if (self.in_archive) |*ar| allocator.free(ar.path);
50 allocator.free(self.path);43 allocator.free(self.path);
...@@ -185,7 +178,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {...@@ -185,7 +178,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
185178
186 fn rank(ctx: *const Object, nl: macho.nlist_64) u8 {179 fn rank(ctx: *const Object, nl: macho.nlist_64) u8 {
187 if (!nl.ext()) {180 if (!nl.ext()) {
188 const name = ctx.getString(nl.n_strx);181 const name = ctx.getNStrx(nl.n_strx);
189 if (name.len == 0) return 5;182 if (name.len == 0) return 5;
190 if (name[0] == 'l' or name[0] == 'L') return 4;183 if (name[0] == 'l' or name[0] == 'L') return 4;
191 return 3;184 return 3;
...@@ -270,9 +263,12 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {...@@ -270,9 +263,12 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
270 mem.eql(u8, isec.sectName(), "__compact_unwind") or263 mem.eql(u8, isec.sectName(), "__compact_unwind") or
271 isec.attrs() & macho.S_ATTR_DEBUG != 0)264 isec.attrs() & macho.S_ATTR_DEBUG != 0)
272 {265 {
273 atom.flags.alive = false;266 atom.setAlive(false);
274 }267 }
275 }268 }
269
270 // Finally, we do a post-parse check for -ObjC to see if we need to force load this member anyhow.
271 self.alive = self.alive or (macho_file.force_load_objc and self.hasObjC());
276}272}
277273
278pub fn isCstringLiteral(sect: macho.section_64) bool {274pub fn isCstringLiteral(sect: macho.section_64) bool {
...@@ -345,7 +341,7 @@ fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void {...@@ -345,7 +341,7 @@ fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void {
345 else341 else
346 sect.@"align";342 sect.@"align";
347 const atom_index = try self.addAtom(allocator, .{343 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) },
349 .n_sect = @intCast(n_sect),345 .n_sect = @intCast(n_sect),
350 .off = nlist.nlist.n_value - sect.addr,346 .off = nlist.nlist.n_value - sect.addr,
351 .size = size,347 .size = size,
...@@ -469,7 +465,7 @@ fn initCstringLiterals(self: *Object, allocator: Allocator, file: File.Handle, m...@@ -469,7 +465,7 @@ fn initCstringLiterals(self: *Object, allocator: Allocator, file: File.Handle, m
469 const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));465 const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
470 self.symtab.set(nlist_index, .{466 self.symtab.set(nlist_index, .{
471 .nlist = .{467 .nlist = .{
472 .n_strx = name_str,468 .n_strx = name_str.pos,
473 .n_type = macho.N_SECT,469 .n_type = macho.N_SECT,
474 .n_sect = @intCast(atom.n_sect + 1),470 .n_sect = @intCast(atom.n_sect + 1),
475 .n_desc = 0,471 .n_desc = 0,
...@@ -536,7 +532,7 @@ fn initFixedSizeLiterals(self: *Object, allocator: Allocator, macho_file: *MachO...@@ -536,7 +532,7 @@ fn initFixedSizeLiterals(self: *Object, allocator: Allocator, macho_file: *MachO
536 const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));532 const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
537 self.symtab.set(nlist_index, .{533 self.symtab.set(nlist_index, .{
538 .nlist = .{534 .nlist = .{
539 .n_strx = name_str,535 .n_strx = name_str.pos,
540 .n_type = macho.N_SECT,536 .n_type = macho.N_SECT,
541 .n_sect = @intCast(atom.n_sect + 1),537 .n_sect = @intCast(atom.n_sect + 1),
542 .n_desc = 0,538 .n_desc = 0,
...@@ -594,7 +590,7 @@ fn initPointerLiterals(self: *Object, allocator: Allocator, macho_file: *MachO)...@@ -594,7 +590,7 @@ fn initPointerLiterals(self: *Object, allocator: Allocator, macho_file: *MachO)
594 const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));590 const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
595 self.symtab.set(nlist_index, .{591 self.symtab.set(nlist_index, .{
596 .nlist = .{592 .nlist = .{
597 .n_strx = name_str,593 .n_strx = name_str.pos,
598 .n_type = macho.N_SECT,594 .n_type = macho.N_SECT,
599 .n_sect = @intCast(atom.n_sect + 1),595 .n_sect = @intCast(atom.n_sect + 1),
600 .n_desc = 0,596 .n_desc = 0,
...@@ -649,7 +645,7 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO...@@ -649,7 +645,7 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO
649 const lp_sym = lp.getSymbol(res.index, macho_file);645 const lp_sym = lp.getSymbol(res.index, macho_file);
650 const lp_atom = lp_sym.getAtom(macho_file).?;646 const lp_atom = lp_sym.getAtom(macho_file).?;
651 lp_atom.alignment = lp_atom.alignment.max(atom.alignment);647 lp_atom.alignment = lp_atom.alignment.max(atom.alignment);
652 atom.flags.alive = false;648 atom.setAlive(false);
653 }649 }
654 atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);650 atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
655 }651 }
...@@ -687,7 +683,7 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO...@@ -687,7 +683,7 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO
687 const lp_sym = lp.getSymbol(res.index, macho_file);683 const lp_sym = lp.getSymbol(res.index, macho_file);
688 const lp_atom = lp_sym.getAtom(macho_file).?;684 const lp_atom = lp_sym.getAtom(macho_file).?;
689 lp_atom.alignment = lp_atom.alignment.max(atom.alignment);685 lp_atom.alignment = lp_atom.alignment.max(atom.alignment);
690 atom.flags.alive = false;686 atom.setAlive(false);
691 }687 }
692 atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);688 atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
693 }689 }
...@@ -701,7 +697,7 @@ pub fn dedupLiterals(self: *Object, lp: MachO.LiteralPool, macho_file: *MachO) v...@@ -701,7 +697,7 @@ pub fn dedupLiterals(self: *Object, lp: MachO.LiteralPool, macho_file: *MachO) v
701697
702 for (self.getAtoms()) |atom_index| {698 for (self.getAtoms()) |atom_index| {
703 const atom = self.getAtom(atom_index) orelse continue;699 const atom = self.getAtom(atom_index) orelse continue;
704 if (!atom.flags.alive) continue;700 if (!atom.isAlive()) continue;
705701
706 const relocs = blk: {702 const relocs = blk: {
707 const extra = atom.getExtra(macho_file);703 const extra = atom.getExtra(macho_file);
...@@ -800,7 +796,7 @@ fn linkNlistToAtom(self: *Object, macho_file: *MachO) !void {...@@ -800,7 +796,7 @@ fn linkNlistToAtom(self: *Object, macho_file: *MachO) !void {
800 atom.* = atom_index;796 atom.* = atom_index;
801 } else {797 } else {
802 try macho_file.reportParseError2(self.index, "symbol {s} not attached to any (sub)section", .{798 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),
804 });800 });
805 return error.MalformedObject;801 return error.MalformedObject;
806 }802 }
...@@ -825,7 +821,7 @@ fn initSymbols(self: *Object, allocator: Allocator, macho_file: *MachO) !void {...@@ -825,7 +821,7 @@ fn initSymbols(self: *Object, allocator: Allocator, macho_file: *MachO) !void {
825 const index = self.addSymbolAssumeCapacity();821 const index = self.addSymbolAssumeCapacity();
826 const symbol = &self.symbols.items[index];822 const symbol = &self.symbols.items[index];
827 symbol.value = nlist.n_value;823 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) };
829 symbol.nlist_idx = @intCast(i);825 symbol.nlist_idx = @intCast(i);
830 symbol.extra = self.addSymbolExtraAssumeCapacity(.{});826 symbol.extra = self.addSymbolExtraAssumeCapacity(.{});
831827
...@@ -898,7 +894,7 @@ fn initSymbolStabs(self: *Object, allocator: Allocator, nlists: anytype, macho_f...@@ -898,7 +894,7 @@ fn initSymbolStabs(self: *Object, allocator: Allocator, nlists: anytype, macho_f
898 defer addr_lookup.deinit();894 defer addr_lookup.deinit();
899 for (syms) |sym| {895 for (syms) |sym| {
900 if (sym.sect() and (sym.ext() or sym.pext())) {896 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);
902 }898 }
903 }899 }
904900
...@@ -930,7 +926,7 @@ fn initSymbolStabs(self: *Object, allocator: Allocator, nlists: anytype, macho_f...@@ -930,7 +926,7 @@ fn initSymbolStabs(self: *Object, allocator: Allocator, nlists: anytype, macho_f
930 },926 },
931 macho.N_GSYM => {927 macho.N_GSYM => {
932 stab.is_func = false;928 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)).?);
934 },930 },
935 macho.N_STSYM => {931 macho.N_STSYM => {
936 stab.is_func = false;932 stab.is_func = false;
...@@ -994,7 +990,7 @@ fn initRelocs(self: *Object, file: File.Handle, cpu_arch: std.Target.Cpu.Arch, m...@@ -994,7 +990,7 @@ fn initRelocs(self: *Object, file: File.Handle, cpu_arch: std.Target.Cpu.Arch, m
994 var next_reloc: u32 = 0;990 var next_reloc: u32 = 0;
995 for (subsections.items) |subsection| {991 for (subsections.items) |subsection| {
996 const atom = self.getAtom(subsection.atom).?;992 const atom = self.getAtom(subsection.atom).?;
997 if (!atom.flags.alive) continue;993 if (!atom.isAlive()) continue;
998 if (next_reloc >= relocs.items.len) break;994 if (next_reloc >= relocs.items.len) break;
999 const end_addr = atom.off + atom.size;995 const end_addr = atom.off + atom.size;
1000 const rel_index = next_reloc;996 const rel_index = next_reloc;
...@@ -1487,7 +1483,7 @@ pub fn resolveSymbols(self: *Object, macho_file: *MachO) !void {...@@ -1487,7 +1483,7 @@ pub fn resolveSymbols(self: *Object, macho_file: *MachO) !void {
1487 if (!nlist.ext()) continue;1483 if (!nlist.ext()) continue;
1488 if (nlist.sect()) {1484 if (nlist.sect()) {
1489 const atom = self.getAtom(atom_index).?;1485 const atom = self.getAtom(atom_index).?;
1490 if (!atom.flags.alive) continue;1486 if (!atom.isAlive()) continue;
1491 }1487 }
14921488
1493 const gop = try macho_file.resolver.getOrPut(gpa, .{1489 const gop = try macho_file.resolver.getOrPut(gpa, .{
...@@ -1556,7 +1552,7 @@ pub fn scanRelocs(self: *Object, macho_file: *MachO) !void {...@@ -1556,7 +1552,7 @@ pub fn scanRelocs(self: *Object, macho_file: *MachO) !void {
15561552
1557 for (self.getAtoms()) |atom_index| {1553 for (self.getAtoms()) |atom_index| {
1558 const atom = self.getAtom(atom_index) orelse continue;1554 const atom = self.getAtom(atom_index) orelse continue;
1559 if (!atom.flags.alive) continue;1555 if (!atom.isAlive()) continue;
1560 const sect = atom.getInputSection(macho_file);1556 const sect = atom.getInputSection(macho_file);
1561 if (sect.isZerofill()) continue;1557 if (sect.isZerofill()) continue;
1562 try atom.scanRelocs(macho_file);1558 try atom.scanRelocs(macho_file);
...@@ -1567,10 +1563,10 @@ pub fn scanRelocs(self: *Object, macho_file: *MachO) !void {...@@ -1567,10 +1563,10 @@ pub fn scanRelocs(self: *Object, macho_file: *MachO) !void {
1567 if (!rec.alive) continue;1563 if (!rec.alive) continue;
1568 if (rec.getFde(macho_file)) |fde| {1564 if (rec.getFde(macho_file)) |fde| {
1569 if (fde.getCie(macho_file).getPersonality(macho_file)) |sym| {1565 if (fde.getCie(macho_file).getPersonality(macho_file)) |sym| {
1570 sym.flags.needs_got = true;1566 sym.setSectionFlags(.{ .needs_got = true });
1571 }1567 }
1572 } else if (rec.getPersonality(macho_file)) |sym| {1568 } else if (rec.getPersonality(macho_file)) |sym| {
1573 sym.flags.needs_got = true;1569 sym.setSectionFlags(.{ .needs_got = true });
1574 }1570 }
1575 }1571 }
1576}1572}
...@@ -1712,7 +1708,7 @@ pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, macho_file: *M...@@ -1712,7 +1708,7 @@ pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, macho_file: *M
1712 const gpa = macho_file.base.comp.gpa;1708 const gpa = macho_file.base.comp.gpa;
1713 for (self.symtab.items(.nlist)) |nlist| {1709 for (self.symtab.items(.nlist)) |nlist| {
1714 if (!nlist.ext() or (nlist.undf() and !nlist.tentative())) continue;1710 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));
1716 try ar_symtab.entries.append(gpa, .{ .off = off, .file = self.index });1712 try ar_symtab.entries.append(gpa, .{ .off = off, .file = self.index });
1717 }1713 }
1718}1714}
...@@ -1749,7 +1745,7 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void {...@@ -1749,7 +1745,7 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void {
1749 const ref = self.getSymbolRef(@intCast(i), macho_file);1745 const ref = self.getSymbolRef(@intCast(i), macho_file);
1750 const file = ref.getFile(macho_file) orelse continue;1746 const file = ref.getFile(macho_file) orelse continue;
1751 if (file.getIndex() != self.index) continue;1747 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;
1753 if (sym.isSymbolStab(macho_file)) continue;1749 if (sym.isSymbolStab(macho_file)) continue;
1754 const name = sym.getName(macho_file);1750 const name = sym.getName(macho_file);
1755 if (name.len == 0) continue;1751 if (name.len == 0) continue;
...@@ -1858,7 +1854,7 @@ pub fn writeAtoms(self: *Object, macho_file: *MachO) !void {...@@ -1858,7 +1854,7 @@ pub fn writeAtoms(self: *Object, macho_file: *MachO) !void {
1858 }1854 }
1859 for (self.getAtoms()) |atom_index| {1855 for (self.getAtoms()) |atom_index| {
1860 const atom = self.getAtom(atom_index) orelse continue;1856 const atom = self.getAtom(atom_index) orelse continue;
1861 if (!atom.flags.alive) continue;1857 if (!atom.isAlive()) continue;
1862 const sect = atom.getInputSection(macho_file);1858 const sect = atom.getInputSection(macho_file);
1863 if (sect.isZerofill()) continue;1859 if (sect.isZerofill()) continue;
1864 const value = math.cast(usize, atom.value) orelse return error.Overflow;1860 const value = math.cast(usize, atom.value) orelse return error.Overflow;
...@@ -1897,7 +1893,7 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void {...@@ -1897,7 +1893,7 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void {
1897 }1893 }
1898 for (self.getAtoms()) |atom_index| {1894 for (self.getAtoms()) |atom_index| {
1899 const atom = self.getAtom(atom_index) orelse continue;1895 const atom = self.getAtom(atom_index) orelse continue;
1900 if (!atom.flags.alive) continue;1896 if (!atom.isAlive()) continue;
1901 const sect = atom.getInputSection(macho_file);1897 const sect = atom.getInputSection(macho_file);
1902 if (sect.isZerofill()) continue;1898 if (sect.isZerofill()) continue;
1903 const value = math.cast(usize, atom.value) orelse return error.Overflow;1899 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...@@ -2296,17 +2292,23 @@ pub fn getAtomRelocs(self: *const Object, atom: Atom, macho_file: *MachO) []cons
2296 return relocs.items[extra.rel_index..][0..extra.rel_count];2292 return relocs.items[extra.rel_index..][0..extra.rel_count];
2297}2293}
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 {
2300 const off: u32 = @intCast(self.strtab.items.len);2296 const off: u32 = @intCast(self.strtab.items.len);
2301 try self.strtab.ensureUnusedCapacity(allocator, name.len + 1);2297 try self.strtab.ensureUnusedCapacity(allocator, string.len + 1);
2302 self.strtab.appendSliceAssumeCapacity(name);2298 self.strtab.appendSliceAssumeCapacity(string);
2303 self.strtab.appendAssumeCapacity(0);2299 self.strtab.appendAssumeCapacity(0);
2304 return off;2300 return .{ .pos = off, .len = @intCast(string.len + 1) };
2305}2301}
23062302
2307pub fn getString(self: Object, off: u32) [:0]const u8 {2303pub fn getString(self: Object, string: MachO.String) [:0]const u8 {
2308 assert(off < self.strtab.items.len);2304 assert(string.pos < self.strtab.items.len and string.pos + string.len <= self.strtab.items.len);
2309 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);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);
2310}2312}
23112313
2312pub fn hasUnwindRecords(self: Object) bool {2314pub fn hasUnwindRecords(self: Object) bool {
...@@ -2325,9 +2327,9 @@ fn hasSymbolStabs(self: Object) bool {...@@ -2325,9 +2327,9 @@ fn hasSymbolStabs(self: Object) bool {
2325 return self.stab_files.items.len > 0;2327 return self.stab_files.items.len > 0;
2326}2328}
23272329
2328pub fn hasObjc(self: Object) bool {2330fn hasObjC(self: Object) bool {
2329 for (self.symtab.items(.nlist)) |nlist| {2331 for (self.symtab.items(.nlist)) |nlist| {
2330 const name = self.getString(nlist.n_strx);2332 const name = self.getNStrx(nlist.n_strx);
2331 if (mem.startsWith(u8, name, "_OBJC_CLASS_$_")) return true;2333 if (mem.startsWith(u8, name, "_OBJC_CLASS_$_")) return true;
2332 }2334 }
2333 for (self.sections.items(.header)) |sect| {2335 for (self.sections.items(.header)) |sect| {
...@@ -2350,7 +2352,7 @@ pub fn asFile(self: *Object) File {...@@ -2350,7 +2352,7 @@ pub fn asFile(self: *Object) File {
2350}2352}
23512353
2352const AddAtomArgs = struct {2354const AddAtomArgs = struct {
2353 name: u32,2355 name: MachO.String,
2354 n_sect: u8,2356 n_sect: u8,
2355 off: u64,2357 off: u64,
2356 size: u64,2358 size: u64,
...@@ -2694,17 +2696,17 @@ const StabFile = struct {...@@ -2694,17 +2696,17 @@ const StabFile = struct {
26942696
2695 fn getCompDir(sf: StabFile, object: Object) [:0]const u8 {2697 fn getCompDir(sf: StabFile, object: Object) [:0]const u8 {
2696 const nlist = object.symtab.items(.nlist)[sf.comp_dir];2698 const nlist = object.symtab.items(.nlist)[sf.comp_dir];
2697 return object.getString(nlist.n_strx);2699 return object.getNStrx(nlist.n_strx);
2698 }2700 }
26992701
2700 fn getTuName(sf: StabFile, object: Object) [:0]const u8 {2702 fn getTuName(sf: StabFile, object: Object) [:0]const u8 {
2701 const nlist = object.symtab.items(.nlist)[sf.comp_dir + 1];2703 const nlist = object.symtab.items(.nlist)[sf.comp_dir + 1];
2702 return object.getString(nlist.n_strx);2704 return object.getNStrx(nlist.n_strx);
2703 }2705 }
27042706
2705 fn getOsoPath(sf: StabFile, object: Object) [:0]const u8 {2707 fn getOsoPath(sf: StabFile, object: Object) [:0]const u8 {
2706 const nlist = object.symtab.items(.nlist)[sf.comp_dir + 2];2708 const nlist = object.symtab.items(.nlist)[sf.comp_dir + 2];
2707 return object.getString(nlist.n_strx);2709 return object.getNStrx(nlist.n_strx);
2708 }2710 }
27092711
2710 fn getOsoModTime(sf: StabFile, object: Object) u64 {2712 fn getOsoModTime(sf: StabFile, object: Object) u64 {
...@@ -2762,8 +2764,8 @@ const StabFile = struct {...@@ -2762,8 +2764,8 @@ const StabFile = struct {
2762};2764};
27632765
2764const CompileUnit = struct {2766const CompileUnit = struct {
2765 comp_dir: u32,2767 comp_dir: MachO.String,
2766 tu_name: u32,2768 tu_name: MachO.String,
27672769
2768 fn getCompDir(cu: CompileUnit, object: Object) [:0]const u8 {2770 fn getCompDir(cu: CompileUnit, object: Object) [:0]const u8 {
2769 return object.getString(cu.comp_dir);2771 return object.getString(cu.comp_dir);
src/link/MachO/Symbol.zig+25-12
...@@ -4,7 +4,7 @@...@@ -4,7 +4,7 @@
4value: u64 = 0,4value: u64 = 0,
55
6/// Offset into the linker's intern table.6/// Offset into the linker's intern table.
7name: u32 = 0,7name: MachO.String = .{},
88
9/// File where this symbol is defined.9/// File where this symbol is defined.
10file: File.Index = 0,10file: File.Index = 0,
...@@ -23,6 +23,8 @@ nlist_idx: u32 = 0,...@@ -23,6 +23,8 @@ nlist_idx: u32 = 0,
23/// Misc flags for the symbol packaged as packed struct for compression.23/// Misc flags for the symbol packaged as packed struct for compression.
24flags: Flags = .{},24flags: Flags = .{},
2525
26sect_flags: std.atomic.Value(u8) = std.atomic.Value(u8).init(0),
27
26visibility: Visibility = .local,28visibility: Visibility = .local,
2729
28extra: u32 = 0,30extra: u32 = 0,
...@@ -55,7 +57,6 @@ pub fn weakRef(symbol: Symbol, macho_file: *MachO) bool {...@@ -55,7 +57,6 @@ pub fn weakRef(symbol: Symbol, macho_file: *MachO) bool {
5557
56pub fn getName(symbol: Symbol, macho_file: *MachO) [:0]const u8 {58pub fn getName(symbol: Symbol, macho_file: *MachO) [:0]const u8 {
57 return switch (symbol.getFile(macho_file).?) {59 return switch (symbol.getFile(macho_file).?) {
58 .zig_object => |x| x.strtab.getAssumeExists(symbol.name),
59 inline else => |x| x.getString(symbol.name),60 inline else => |x| x.getString(symbol.name),
60 };61 };
61}62}
...@@ -69,6 +70,14 @@ pub fn getOutputSectionIndex(symbol: Symbol, macho_file: *MachO) u8 {...@@ -69,6 +70,14 @@ pub fn getOutputSectionIndex(symbol: Symbol, macho_file: *MachO) u8 {
69 return symbol.out_n_sect;70 return symbol.out_n_sect;
70}71}
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
72pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File {81pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File {
73 return macho_file.getFile(symbol.file);82 return macho_file.getFile(symbol.file);
74}83}
...@@ -116,9 +125,9 @@ pub fn getAddress(symbol: Symbol, opts: struct {...@@ -116,9 +125,9 @@ pub fn getAddress(symbol: Symbol, opts: struct {
116 stubs: bool = true,125 stubs: bool = true,
117}, macho_file: *MachO) u64 {126}, macho_file: *MachO) u64 {
118 if (opts.stubs) {127 if (opts.stubs) {
119 if (symbol.flags.stubs) {128 if (symbol.getSectionFlags().stubs) {
120 return symbol.getStubsAddress(macho_file);129 return symbol.getStubsAddress(macho_file);
121 } else if (symbol.flags.objc_stubs) {130 } else if (symbol.getSectionFlags().objc_stubs) {
122 return symbol.getObjcStubsAddress(macho_file);131 return symbol.getObjcStubsAddress(macho_file);
123 }132 }
124 }133 }
...@@ -127,25 +136,25 @@ pub fn getAddress(symbol: Symbol, opts: struct {...@@ -127,25 +136,25 @@ pub fn getAddress(symbol: Symbol, opts: struct {
127}136}
128137
129pub fn getGotAddress(symbol: Symbol, macho_file: *MachO) u64 {138pub fn getGotAddress(symbol: Symbol, macho_file: *MachO) u64 {
130 if (!symbol.flags.has_got) return 0;139 if (!symbol.getSectionFlags().has_got) return 0;
131 const extra = symbol.getExtra(macho_file);140 const extra = symbol.getExtra(macho_file);
132 return macho_file.got.getAddress(extra.got, macho_file);141 return macho_file.got.getAddress(extra.got, macho_file);
133}142}
134143
135pub fn getStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {144pub fn getStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
136 if (!symbol.flags.stubs) return 0;145 if (!symbol.getSectionFlags().stubs) return 0;
137 const extra = symbol.getExtra(macho_file);146 const extra = symbol.getExtra(macho_file);
138 return macho_file.stubs.getAddress(extra.stubs, macho_file);147 return macho_file.stubs.getAddress(extra.stubs, macho_file);
139}148}
140149
141pub fn getObjcStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {150pub fn getObjcStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
142 if (!symbol.flags.objc_stubs) return 0;151 if (!symbol.getSectionFlags().objc_stubs) return 0;
143 const extra = symbol.getExtra(macho_file);152 const extra = symbol.getExtra(macho_file);
144 return macho_file.objc_stubs.getAddress(extra.objc_stubs, macho_file);153 return macho_file.objc_stubs.getAddress(extra.objc_stubs, macho_file);
145}154}
146155
147pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 {156pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 {
148 if (!symbol.flags.objc_stubs) return 0;157 if (!symbol.getSectionFlags().objc_stubs) return 0;
149 const extra = symbol.getExtra(macho_file);158 const extra = symbol.getExtra(macho_file);
150 const file = symbol.getFile(macho_file).?;159 const file = symbol.getFile(macho_file).?;
151 return switch (file) {160 return switch (file) {
...@@ -155,7 +164,7 @@ pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 {...@@ -155,7 +164,7 @@ pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 {
155}164}
156165
157pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 {166pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 {
158 if (!symbol.flags.tlv_ptr) return 0;167 if (!symbol.getSectionFlags().tlv_ptr) return 0;
159 const extra = symbol.getExtra(macho_file);168 const extra = symbol.getExtra(macho_file);
160 return macho_file.tlv_ptr.getAddress(extra.tlv_ptr, macho_file);169 return macho_file.tlv_ptr.getAddress(extra.tlv_ptr, macho_file);
161}170}
...@@ -167,14 +176,14 @@ const GetOrCreateZigGotEntryResult = struct {...@@ -167,14 +176,14 @@ const GetOrCreateZigGotEntryResult = struct {
167176
168pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, macho_file: *MachO) !GetOrCreateZigGotEntryResult {177pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, macho_file: *MachO) !GetOrCreateZigGotEntryResult {
169 assert(!macho_file.base.isRelocatable());178 assert(!macho_file.base.isRelocatable());
170 assert(symbol.flags.needs_zig_got);179 assert(symbol.getSectionFlags().needs_zig_got);
171 if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).zig_got };180 if (symbol.getSectionFlags().has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).zig_got };
172 const index = try macho_file.zig_got.addSymbol(symbol_index, macho_file);181 const index = try macho_file.zig_got.addSymbol(symbol_index, macho_file);
173 return .{ .found_existing = false, .index = index };182 return .{ .found_existing = false, .index = index };
174}183}
175184
176pub fn getZigGotAddress(symbol: Symbol, macho_file: *MachO) u64 {185pub 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;
178 const extras = symbol.getExtra(macho_file);187 const extras = symbol.getExtra(macho_file);
179 return macho_file.zig_got.entryAddress(extras.zig_got, macho_file);188 return macho_file.zig_got.entryAddress(extras.zig_got, macho_file);
180}189}
...@@ -384,7 +393,9 @@ pub const Flags = packed struct {...@@ -384,7 +393,9 @@ pub const Flags = packed struct {
384393
385 /// Whether the symbol makes into the output symtab or not.394 /// Whether the symbol makes into the output symtab or not.
386 output_symtab: bool = false,395 output_symtab: bool = false,
396};
387397
398pub const SectionFlags = packed struct(u8) {
388 /// Whether the symbol contains __got indirection.399 /// Whether the symbol contains __got indirection.
389 needs_got: bool = false,400 needs_got: bool = false,
390 has_got: bool = false,401 has_got: bool = false,
...@@ -401,6 +412,8 @@ pub const Flags = packed struct {...@@ -401,6 +412,8 @@ pub const Flags = packed struct {
401412
402 /// Whether the symbol contains __objc_stubs indirection.413 /// Whether the symbol contains __objc_stubs indirection.
403 objc_stubs: bool = false,414 objc_stubs: bool = false,
415
416 _: u1 = 0,
404};417};
405418
406pub const Visibility = enum {419pub const Visibility = enum {
src/link/MachO/UnwindInfo.zig+1-1
...@@ -53,7 +53,7 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {...@@ -53,7 +53,7 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
53 for (macho_file.sections.items(.atoms)) |atoms| {53 for (macho_file.sections.items(.atoms)) |atoms| {
54 for (atoms.items) |ref| {54 for (atoms.items) |ref| {
55 const atom = ref.getAtom(macho_file) orelse continue;55 const atom = ref.getAtom(macho_file) orelse continue;
56 if (!atom.flags.alive) continue;56 if (!atom.isAlive()) continue;
57 const recs = atom.getUnwindRecords(macho_file);57 const recs = atom.getUnwindRecords(macho_file);
58 const file = atom.getFile(macho_file);58 const file = atom.getFile(macho_file);
59 try info.records.ensureUnusedCapacity(gpa, recs.len);59 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 {...@@ -141,7 +141,7 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {
141 }141 }
142}142}
143143
144fn newSymbol(self: *ZigObject, allocator: Allocator, name: u32, args: struct {144fn newSymbol(self: *ZigObject, allocator: Allocator, name: MachO.String, args: struct {
145 type: u8 = macho.N_UNDF | macho.N_EXT,145 type: u8 = macho.N_UNDF | macho.N_EXT,
146 desc: u16 = 0,146 desc: u16 = 0,
147}) !Symbol.Index {147}) !Symbol.Index {
...@@ -158,7 +158,7 @@ fn newSymbol(self: *ZigObject, allocator: Allocator, name: u32, args: struct {...@@ -158,7 +158,7 @@ fn newSymbol(self: *ZigObject, allocator: Allocator, name: u32, args: struct {
158 const nlist_idx: u32 = @intCast(self.symtab.addOneAssumeCapacity());158 const nlist_idx: u32 = @intCast(self.symtab.addOneAssumeCapacity());
159 self.symtab.set(nlist_idx, .{159 self.symtab.set(nlist_idx, .{
160 .nlist = .{160 .nlist = .{
161 .n_strx = name,161 .n_strx = name.pos,
162 .n_type = args.type,162 .n_type = args.type,
163 .n_sect = 0,163 .n_sect = 0,
164 .n_desc = args.desc,164 .n_desc = args.desc,
...@@ -174,7 +174,7 @@ fn newSymbol(self: *ZigObject, allocator: Allocator, name: u32, args: struct {...@@ -174,7 +174,7 @@ fn newSymbol(self: *ZigObject, allocator: Allocator, name: u32, args: struct {
174 return index;174 return index;
175}175}
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 {
178 try self.atoms.ensureUnusedCapacity(allocator, 1);178 try self.atoms.ensureUnusedCapacity(allocator, 1);
179 try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra));179 try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra));
180 try self.atoms_indexes.ensureUnusedCapacity(allocator, 1);180 try self.atoms_indexes.ensureUnusedCapacity(allocator, 1);
...@@ -192,7 +192,7 @@ fn newAtom(self: *ZigObject, allocator: Allocator, name: u32, macho_file: *MachO...@@ -192,7 +192,7 @@ fn newAtom(self: *ZigObject, allocator: Allocator, name: u32, macho_file: *MachO
192 return index;192 return index;
193}193}
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 {
196 const atom_index = try self.newAtom(allocator, name, macho_file);196 const atom_index = try self.newAtom(allocator, name, macho_file);
197 const sym_index = try self.newSymbol(allocator, name, .{ .type = macho.N_SECT });197 const sym_index = try self.newSymbol(allocator, name, .{ .type = macho.N_SECT });
198 const sym = &self.symbols.items[sym_index];198 const sym = &self.symbols.items[sym_index];
...@@ -245,7 +245,7 @@ pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) !void {...@@ -245,7 +245,7 @@ pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) !void {
245 if (!nlist.ext()) continue;245 if (!nlist.ext()) continue;
246 if (nlist.sect()) {246 if (nlist.sect()) {
247 const atom = self.getAtom(atom_index).?;247 const atom = self.getAtom(atom_index).?;
248 if (!atom.flags.alive) continue;248 if (!atom.isAlive()) continue;
249 }249 }
250250
251 const gop = try macho_file.resolver.getOrPut(gpa, .{251 const gop = try macho_file.resolver.getOrPut(gpa, .{
...@@ -391,7 +391,7 @@ pub fn claimUnresolved(self: *ZigObject, macho_file: *MachO) void {...@@ -391,7 +391,7 @@ pub fn claimUnresolved(self: *ZigObject, macho_file: *MachO) void {
391pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void {391pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void {
392 for (self.getAtoms()) |atom_index| {392 for (self.getAtoms()) |atom_index| {
393 const atom = self.getAtom(atom_index) orelse continue;393 const atom = self.getAtom(atom_index) orelse continue;
394 if (!atom.flags.alive) continue;394 if (!atom.isAlive()) continue;
395 const sect = atom.getInputSection(macho_file);395 const sect = atom.getInputSection(macho_file);
396 if (sect.isZerofill()) continue;396 if (sect.isZerofill()) continue;
397 try atom.scanRelocs(macho_file);397 try atom.scanRelocs(macho_file);
...@@ -403,7 +403,7 @@ pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void {...@@ -403,7 +403,7 @@ pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void {
403 var has_error = false;403 var has_error = false;
404 for (self.getAtoms()) |atom_index| {404 for (self.getAtoms()) |atom_index| {
405 const atom = self.getAtom(atom_index) orelse continue;405 const atom = self.getAtom(atom_index) orelse continue;
406 if (!atom.flags.alive) continue;406 if (!atom.isAlive()) continue;
407 const sect = &macho_file.sections.items(.header)[atom.out_n_sect];407 const sect = &macho_file.sections.items(.header)[atom.out_n_sect];
408 if (sect.isZerofill()) continue;408 if (sect.isZerofill()) continue;
409 if (!macho_file.isZigSection(atom.out_n_sect)) continue; // Non-Zig sections are handled separately409 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 {...@@ -450,7 +450,7 @@ pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void {
450pub fn calcNumRelocs(self: *ZigObject, macho_file: *MachO) void {450pub fn calcNumRelocs(self: *ZigObject, macho_file: *MachO) void {
451 for (self.getAtoms()) |atom_index| {451 for (self.getAtoms()) |atom_index| {
452 const atom = self.getAtom(atom_index) orelse continue;452 const atom = self.getAtom(atom_index) orelse continue;
453 if (!atom.flags.alive) continue;453 if (!atom.isAlive()) continue;
454 const header = &macho_file.sections.items(.header)[atom.out_n_sect];454 const header = &macho_file.sections.items(.header)[atom.out_n_sect];
455 if (header.isZerofill()) continue;455 if (header.isZerofill()) continue;
456 if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;456 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 {...@@ -465,7 +465,7 @@ pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) !void {
465465
466 for (self.getAtoms()) |atom_index| {466 for (self.getAtoms()) |atom_index| {
467 const atom = self.getAtom(atom_index) orelse continue;467 const atom = self.getAtom(atom_index) orelse continue;
468 if (!atom.flags.alive) continue;468 if (!atom.isAlive()) continue;
469 const header = macho_file.sections.items(.header)[atom.out_n_sect];469 const header = macho_file.sections.items(.header)[atom.out_n_sect];
470 const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;470 const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;
471 if (header.isZerofill()) continue;471 if (header.isZerofill()) continue;
...@@ -505,7 +505,7 @@ pub fn writeAtomsRelocatable(self: *ZigObject, macho_file: *MachO) !void {...@@ -505,7 +505,7 @@ pub fn writeAtomsRelocatable(self: *ZigObject, macho_file: *MachO) !void {
505505
506 for (self.getAtoms()) |atom_index| {506 for (self.getAtoms()) |atom_index| {
507 const atom = self.getAtom(atom_index) orelse continue;507 const atom = self.getAtom(atom_index) orelse continue;
508 if (!atom.flags.alive) continue;508 if (!atom.isAlive()) continue;
509 const sect = atom.getInputSection(macho_file);509 const sect = atom.getInputSection(macho_file);
510 if (sect.isZerofill()) continue;510 if (sect.isZerofill()) continue;
511 if (macho_file.isZigSection(atom.out_n_sect)) continue;511 if (macho_file.isZigSection(atom.out_n_sect)) continue;
...@@ -529,7 +529,7 @@ pub fn writeAtoms(self: *ZigObject, macho_file: *MachO) !void {...@@ -529,7 +529,7 @@ pub fn writeAtoms(self: *ZigObject, macho_file: *MachO) !void {
529529
530 for (self.getAtoms()) |atom_index| {530 for (self.getAtoms()) |atom_index| {
531 const atom = self.getAtom(atom_index) orelse continue;531 const atom = self.getAtom(atom_index) orelse continue;
532 if (!atom.flags.alive) continue;532 if (!atom.isAlive()) continue;
533 const sect = atom.getInputSection(macho_file);533 const sect = atom.getInputSection(macho_file);
534 if (sect.isZerofill()) continue;534 if (sect.isZerofill()) continue;
535 if (macho_file.isZigSection(atom.out_n_sect)) continue;535 if (macho_file.isZigSection(atom.out_n_sect)) continue;
...@@ -549,7 +549,7 @@ pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) void {...@@ -549,7 +549,7 @@ pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) void {
549 const ref = self.getSymbolRef(@intCast(i), macho_file);549 const ref = self.getSymbolRef(@intCast(i), macho_file);
550 const file = ref.getFile(macho_file) orelse continue;550 const file = ref.getFile(macho_file) orelse continue;
551 if (file.getIndex() != self.index) continue;551 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;
553 sym.flags.output_symtab = true;553 sym.flags.output_symtab = true;
554 if (sym.isLocal()) {554 if (sym.isLocal()) {
555 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);555 sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
...@@ -914,7 +914,7 @@ pub fn updateDecl(...@@ -914,7 +914,7 @@ pub fn updateDecl(
914 const lib_name = variable.lib_name.toSlice(&mod.intern_pool);914 const lib_name = variable.lib_name.toSlice(&mod.intern_pool);
915 const index = try self.getGlobalSymbol(macho_file, name, lib_name);915 const index = try self.getGlobalSymbol(macho_file, name, lib_name);
916 const sym = &self.symbols.items[index];916 const sym = &self.symbols.items[index];
917 sym.flags.needs_got = true;917 sym.setSectionFlags(.{ .needs_got = true });
918 return;918 return;
919 }919 }
920920
...@@ -992,10 +992,10 @@ fn updateDeclCode(...@@ -992,10 +992,10 @@ fn updateDeclCode(
992992
993 const sym_name = try std.fmt.allocPrintZ(gpa, "_{s}", .{decl.fqn.toSlice(ip)});993 const sym_name = try std.fmt.allocPrintZ(gpa, "_{s}", .{decl.fqn.toSlice(ip)});
994 defer gpa.free(sym_name);994 defer gpa.free(sym_name);
995 sym.name = try self.strtab.insert(gpa, sym_name);995 sym.name = try self.addString(gpa, sym_name);
996 atom.flags.alive = true;996 atom.setAlive(true);
997 atom.name = sym.name;997 atom.name = sym.name;
998 nlist.n_strx = sym.name;998 nlist.n_strx = sym.name.pos;
999 nlist.n_type = macho.N_SECT;999 nlist.n_type = macho.N_SECT;
1000 nlist.n_sect = sect_index + 1;1000 nlist.n_sect = sect_index + 1;
1001 self.symtab.items(.size)[sym.nlist_idx] = code.len;1001 self.symtab.items(.size)[sym.nlist_idx] = code.len;
...@@ -1018,7 +1018,7 @@ fn updateDeclCode(...@@ -1018,7 +1018,7 @@ fn updateDeclCode(
10181018
1019 if (!macho_file.base.isRelocatable()) {1019 if (!macho_file.base.isRelocatable()) {
1020 log.debug(" (updating offset table entry)", .{});1020 log.debug(" (updating offset table entry)", .{});
1021 assert(sym.flags.has_zig_got);1021 assert(sym.getSectionFlags().has_zig_got);
1022 const extra = sym.getExtra(macho_file);1022 const extra = sym.getExtra(macho_file);
1023 try macho_file.zig_got.writeOne(macho_file, extra.zig_got);1023 try macho_file.zig_got.writeOne(macho_file, extra.zig_got);
1024 }1024 }
...@@ -1034,7 +1034,7 @@ fn updateDeclCode(...@@ -1034,7 +1034,7 @@ fn updateDeclCode(
1034 errdefer self.freeDeclMetadata(macho_file, sym_index);1034 errdefer self.freeDeclMetadata(macho_file, sym_index);
10351035
1036 sym.value = 0;1036 sym.value = 0;
1037 sym.flags.needs_zig_got = true;1037 sym.setSectionFlags(.{ .needs_zig_got = true });
1038 nlist.n_value = 0;1038 nlist.n_value = 0;
10391039
1040 if (!macho_file.base.isRelocatable()) {1040 if (!macho_file.base.isRelocatable()) {
...@@ -1090,15 +1090,15 @@ fn createTlvInitializer(...@@ -1090,15 +1090,15 @@ fn createTlvInitializer(
1090 const gpa = macho_file.base.comp.gpa;1090 const gpa = macho_file.base.comp.gpa;
1091 const sym_name = try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{name});1091 const sym_name = try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{name});
1092 defer gpa.free(sym_name);1092 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);
1096 const sym = &self.symbols.items[sym_index];1096 const sym = &self.symbols.items[sym_index];
1097 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];1097 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
1098 const atom = sym.getAtom(macho_file).?;1098 const atom = sym.getAtom(macho_file).?;
1099 sym.out_n_sect = sect_index;1099 sym.out_n_sect = sect_index;
1100 atom.out_n_sect = sect_index;1100 atom.out_n_sect = sect_index;
1101 atom.flags.alive = true;1101 atom.setAlive(true);
1102 atom.alignment = alignment;1102 atom.alignment = alignment;
1103 atom.size = code.len;1103 atom.size = code.len;
1104 nlist.n_sect = sect_index + 1;1104 nlist.n_sect = sect_index + 1;
...@@ -1142,10 +1142,10 @@ fn createTlvDescriptor(...@@ -1142,10 +1142,10 @@ fn createTlvDescriptor(
1142 atom.out_n_sect = sect_index;1142 atom.out_n_sect = sect_index;
11431143
1144 sym.value = 0;1144 sym.value = 0;
1145 sym.name = try self.strtab.insert(gpa, name);1145 sym.name = try self.addString(gpa, name);
1146 atom.flags.alive = true;1146 atom.setAlive(true);
1147 atom.name = sym.name;1147 atom.name = sym.name;
1148 nlist.n_strx = sym.name;1148 nlist.n_strx = sym.name.pos;
1149 nlist.n_sect = sect_index + 1;1149 nlist.n_sect = sect_index + 1;
1150 nlist.n_type = macho.N_SECT;1150 nlist.n_type = macho.N_SECT;
1151 nlist.n_value = 0;1151 nlist.n_value = 0;
...@@ -1296,8 +1296,8 @@ fn lowerConst(...@@ -1296,8 +1296,8 @@ fn lowerConst(
1296 var code_buffer = std.ArrayList(u8).init(gpa);1296 var code_buffer = std.ArrayList(u8).init(gpa);
1297 defer code_buffer.deinit();1297 defer code_buffer.deinit();
12981298
1299 const name_str_index = try self.strtab.insert(gpa, name);1299 const name_str = try self.addString(gpa, name);
1300 const sym_index = try self.newSymbolWithAtom(gpa, name_str_index, macho_file);1300 const sym_index = try self.newSymbolWithAtom(gpa, name_str, macho_file);
13011301
1302 const res = try codegen.generateSymbol(&macho_file.base, pt, src_loc, val, &code_buffer, .{1302 const res = try codegen.generateSymbol(&macho_file.base, pt, src_loc, val, &code_buffer, .{
1303 .none = {},1303 .none = {},
...@@ -1317,7 +1317,7 @@ fn lowerConst(...@@ -1317,7 +1317,7 @@ fn lowerConst(
1317 self.symtab.items(.size)[sym.nlist_idx] = code.len;1317 self.symtab.items(.size)[sym.nlist_idx] = code.len;
13181318
1319 const atom = sym.getAtom(macho_file).?;1319 const atom = sym.getAtom(macho_file).?;
1320 atom.flags.alive = true;1320 atom.setAlive(true);
1321 atom.alignment = required_alignment;1321 atom.alignment = required_alignment;
1322 atom.size = code.len;1322 atom.size = code.len;
1323 atom.out_n_sect = output_section_index;1323 atom.out_n_sect = output_section_index;
...@@ -1447,13 +1447,13 @@ fn updateLazySymbol(...@@ -1447,13 +1447,13 @@ fn updateLazySymbol(
1447 var code_buffer = std.ArrayList(u8).init(gpa);1447 var code_buffer = std.ArrayList(u8).init(gpa);
1448 defer code_buffer.deinit();1448 defer code_buffer.deinit();
14491449
1450 const name_str_index = blk: {1450 const name_str = blk: {
1451 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{1451 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{}", .{
1452 @tagName(lazy_sym.kind),1452 @tagName(lazy_sym.kind),
1453 lazy_sym.ty.fmt(pt),1453 lazy_sym.ty.fmt(pt),
1454 });1454 });
1455 defer gpa.free(name);1455 defer gpa.free(name);
1456 break :blk try self.strtab.insert(gpa, name);1456 break :blk try self.addString(gpa, name);
1457 };1457 };
14581458
1459 const src = lazy_sym.ty.srcLocOrNull(mod) orelse Module.LazySrcLoc.unneeded;1459 const src = lazy_sym.ty.srcLocOrNull(mod) orelse Module.LazySrcLoc.unneeded;
...@@ -1480,18 +1480,18 @@ fn updateLazySymbol(...@@ -1480,18 +1480,18 @@ fn updateLazySymbol(
1480 .const_data => macho_file.zig_const_sect_index.?,1480 .const_data => macho_file.zig_const_sect_index.?,
1481 };1481 };
1482 const sym = &self.symbols.items[symbol_index];1482 const sym = &self.symbols.items[symbol_index];
1483 sym.name = name_str_index;1483 sym.name = name_str;
1484 sym.out_n_sect = output_section_index;1484 sym.out_n_sect = output_section_index;
14851485
1486 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];1486 const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
1487 nlist.n_strx = name_str_index;1487 nlist.n_strx = name_str.pos;
1488 nlist.n_type = macho.N_SECT;1488 nlist.n_type = macho.N_SECT;
1489 nlist.n_sect = output_section_index + 1;1489 nlist.n_sect = output_section_index + 1;
1490 self.symtab.items(.size)[sym.nlist_idx] = code.len;1490 self.symtab.items(.size)[sym.nlist_idx] = code.len;
14911491
1492 const atom = sym.getAtom(macho_file).?;1492 const atom = sym.getAtom(macho_file).?;
1493 atom.flags.alive = true;1493 atom.setAlive(true);
1494 atom.name = name_str_index;1494 atom.name = name_str;
1495 atom.alignment = required_alignment;1495 atom.alignment = required_alignment;
1496 atom.size = code.len;1496 atom.size = code.len;
1497 atom.out_n_sect = output_section_index;1497 atom.out_n_sect = output_section_index;
...@@ -1500,7 +1500,7 @@ fn updateLazySymbol(...@@ -1500,7 +1500,7 @@ fn updateLazySymbol(
1500 errdefer self.freeDeclMetadata(macho_file, symbol_index);1500 errdefer self.freeDeclMetadata(macho_file, symbol_index);
15011501
1502 sym.value = 0;1502 sym.value = 0;
1503 sym.flags.needs_zig_got = true;1503 sym.setSectionFlags(.{ .needs_zig_got = true });
1504 nlist.n_value = 0;1504 nlist.n_value = 0;
15051505
1506 if (!macho_file.base.isRelocatable()) {1506 if (!macho_file.base.isRelocatable()) {
...@@ -1553,10 +1553,10 @@ pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, l...@@ -1553,10 +1553,10 @@ pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, l
1553 const gpa = macho_file.base.comp.gpa;1553 const gpa = macho_file.base.comp.gpa;
1554 const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name});1554 const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name});
1555 defer gpa.free(sym_name);1555 defer gpa.free(sym_name);
1556 const off = try self.strtab.insert(gpa, sym_name);1556 const name_str = try self.addString(gpa, sym_name);
1557 const lookup_gop = try self.globals_lookup.getOrPut(gpa, off);1557 const lookup_gop = try self.globals_lookup.getOrPut(gpa, name_str.pos);
1558 if (!lookup_gop.found_existing) {1558 if (!lookup_gop.found_existing) {
1559 const sym_index = try self.newSymbol(gpa, off, .{});1559 const sym_index = try self.newSymbol(gpa, name_str, .{});
1560 const sym = &self.symbols.items[sym_index];1560 const sym = &self.symbols.items[sym_index];
1561 lookup_gop.value_ptr.* = sym.nlist_idx;1561 lookup_gop.value_ptr.* = sym.nlist_idx;
1562 }1562 }
...@@ -1571,12 +1571,12 @@ pub fn getOrCreateMetadataForDecl(...@@ -1571,12 +1571,12 @@ pub fn getOrCreateMetadataForDecl(
1571 const gpa = macho_file.base.comp.gpa;1571 const gpa = macho_file.base.comp.gpa;
1572 const gop = try self.decls.getOrPut(gpa, decl_index);1572 const gop = try self.decls.getOrPut(gpa, decl_index);
1573 if (!gop.found_existing) {1573 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);
1575 const sym = &self.symbols.items[sym_index];1575 const sym = &self.symbols.items[sym_index];
1576 if (isThreadlocal(macho_file, decl_index)) {1576 if (isThreadlocal(macho_file, decl_index)) {
1577 sym.flags.tlv = true;1577 sym.flags.tlv = true;
1578 } else {1578 } else {
1579 sym.flags.needs_zig_got = true;1579 sym.setSectionFlags(.{ .needs_zig_got = true });
1580 }1580 }
1581 gop.value_ptr.* = .{ .symbol_index = sym_index };1581 gop.value_ptr.* = .{ .symbol_index = sym_index };
1582 }1582 }
...@@ -1609,9 +1609,9 @@ pub fn getOrCreateMetadataForLazySymbol(...@@ -1609,9 +1609,9 @@ pub fn getOrCreateMetadataForLazySymbol(
1609 };1609 };
1610 switch (metadata.state.*) {1610 switch (metadata.state.*) {
1611 .unused => {1611 .unused => {
1612 const symbol_index = try self.newSymbolWithAtom(gpa, 0, macho_file);1612 const symbol_index = try self.newSymbolWithAtom(gpa, .{}, macho_file);
1613 const sym = &self.symbols.items[symbol_index];1613 const sym = &self.symbols.items[symbol_index];
1614 sym.flags.needs_zig_got = true;1614 sym.setSectionFlags(.{ .needs_zig_got = true });
1615 metadata.symbol_index.* = symbol_index;1615 metadata.symbol_index.* = symbol_index;
1616 },1616 },
1617 .pending_flush => return metadata.symbol_index.*,1617 .pending_flush => return metadata.symbol_index.*,
...@@ -1762,6 +1762,16 @@ pub fn setSymbolExtra(self: *ZigObject, index: u32, extra: Symbol.Extra) void {...@@ -1762,6 +1762,16 @@ pub fn setSymbolExtra(self: *ZigObject, index: u32, extra: Symbol.Extra) void {
1762 }1762 }
1763}1763}
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
1765pub fn asFile(self: *ZigObject) File {1775pub fn asFile(self: *ZigObject) File {
1766 return .{ .zig_object = self };1776 return .{ .zig_object = self };
1767}1777}
src/link/MachO/dead_strip.zig+10-10
...@@ -82,9 +82,8 @@ fn markSymbol(sym: *Symbol, roots: *std.ArrayList(*Atom), macho_file: *MachO) !v...@@ -82,9 +82,8 @@ fn markSymbol(sym: *Symbol, roots: *std.ArrayList(*Atom), macho_file: *MachO) !v
82}82}
8383
84fn markAtom(atom: *Atom) bool {84fn markAtom(atom: *Atom) bool {
85 const already_visited = atom.flags.visited;85 const already_visited = atom.visited.swap(true, .seq_cst);
86 atom.flags.visited = true;86 return atom.isAlive() and !already_visited;
87 return atom.flags.alive and !already_visited;
88}87}
8988
90fn mark(roots: []*Atom, objects: []const File.Index, macho_file: *MachO) void {89fn 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 {...@@ -105,7 +104,7 @@ fn mark(roots: []*Atom, objects: []const File.Index, macho_file: *MachO) void {
105 !(mem.eql(u8, isec.sectName(), "__eh_frame") or104 !(mem.eql(u8, isec.sectName(), "__eh_frame") or
106 mem.eql(u8, isec.sectName(), "__compact_unwind") or105 mem.eql(u8, isec.sectName(), "__compact_unwind") or
107 isec.attrs() & macho.S_ATTR_DEBUG != 0) and106 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))
109 {108 {
110 markLive(atom, macho_file);109 markLive(atom, macho_file);
111 loop = true;110 loop = true;
...@@ -116,8 +115,8 @@ fn mark(roots: []*Atom, objects: []const File.Index, macho_file: *MachO) void {...@@ -116,8 +115,8 @@ fn mark(roots: []*Atom, objects: []const File.Index, macho_file: *MachO) void {
116}115}
117116
118fn markLive(atom: *Atom, macho_file: *MachO) void {117fn markLive(atom: *Atom, macho_file: *MachO) void {
119 assert(atom.flags.visited);118 assert(atom.visited.load(.seq_cst));
120 atom.flags.alive = true;119 atom.setAlive(true);
121 track_live_log.debug("{}marking live atom({d},{s})", .{120 track_live_log.debug("{}marking live atom({d},{s})", .{
122 track_live_level,121 track_live_level,
123 atom.atom_index,122 atom.atom_index,
...@@ -170,7 +169,7 @@ fn refersLive(atom: *Atom, macho_file: *MachO) bool {...@@ -170,7 +169,7 @@ fn refersLive(atom: *Atom, macho_file: *MachO) bool {
170 },169 },
171 };170 };
172 if (target_atom) |ta| {171 if (target_atom) |ta| {
173 if (ta.flags.alive) return true;172 if (ta.isAlive()) return true;
174 }173 }
175 }174 }
176 return false;175 return false;
...@@ -181,9 +180,10 @@ fn prune(objects: []const File.Index, macho_file: *MachO) void {...@@ -181,9 +180,10 @@ fn prune(objects: []const File.Index, macho_file: *MachO) void {
181 const file = macho_file.getFile(index).?;180 const file = macho_file.getFile(index).?;
182 for (file.getAtoms()) |atom_index| {181 for (file.getAtoms()) |atom_index| {
183 const atom = file.getAtom(atom_index) orelse continue;182 const atom = file.getAtom(atom_index) orelse continue;
184 if (atom.flags.alive and !atom.flags.visited) {183 if (!atom.visited.load(.seq_cst)) {
185 atom.flags.alive = false;184 if (atom.alive.cmpxchgStrong(true, false, .seq_cst, .seq_cst) == null) {
186 atom.markUnwindRecordsDead(macho_file);185 atom.markUnwindRecordsDead(macho_file);
186 }
187 }187 }
188 }188 }
189 }189 }
src/link/MachO/dyld_info/Rebase.zig+1-1
...@@ -35,7 +35,7 @@ pub fn updateSize(rebase: *Rebase, macho_file: *MachO) !void {...@@ -35,7 +35,7 @@ pub fn updateSize(rebase: *Rebase, macho_file: *MachO) !void {
35 const file = macho_file.getFile(index).?;35 const file = macho_file.getFile(index).?;
36 for (file.getAtoms()) |atom_index| {36 for (file.getAtoms()) |atom_index| {
37 const atom = file.getAtom(atom_index) orelse continue;37 const atom = file.getAtom(atom_index) orelse continue;
38 if (!atom.flags.alive) continue;38 if (!atom.isAlive()) continue;
39 if (atom.getInputSection(macho_file).isZerofill()) continue;39 if (atom.getInputSection(macho_file).isZerofill()) continue;
40 const atom_addr = atom.getAddress(macho_file);40 const atom_addr = atom.getAddress(macho_file);
41 const seg_id = macho_file.sections.items(.segment_id)[atom.out_n_sect];41 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 {...@@ -102,7 +102,7 @@ pub fn updateSize(self: *Trie, macho_file: *MachO) !void {
102 if (ref.getFile(macho_file) == null) continue;102 if (ref.getFile(macho_file) == null) continue;
103 const sym = ref.getSymbol(macho_file).?;103 const sym = ref.getSymbol(macho_file).?;
104 if (!sym.flags.@"export") continue;104 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;
106 var flags: u64 = if (sym.flags.abs)106 var flags: u64 = if (sym.flags.abs)
107 macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE107 macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
108 else if (sym.flags.tlv)108 else if (sym.flags.tlv)
...@@ -111,8 +111,8 @@ pub fn updateSize(self: *Trie, macho_file: *MachO) !void {...@@ -111,8 +111,8 @@ pub fn updateSize(self: *Trie, macho_file: *MachO) !void {
111 macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR;111 macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR;
112 if (sym.flags.weak) {112 if (sym.flags.weak) {
113 flags |= macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;113 flags |= macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
114 macho_file.weak_defines = true;114 macho_file.weak_defines.store(true, .seq_cst);
115 macho_file.binds_to_weak = true;115 macho_file.binds_to_weak.store(true, .seq_cst);
116 }116 }
117 try self.put(gpa, .{117 try self.put(gpa, .{
118 .name = sym.getName(macho_file),118 .name = sym.getName(macho_file),
src/link/MachO/dyld_info/bind.zig+3-6
...@@ -10,10 +10,7 @@ pub const Entry = struct {...@@ -10,10 +10,7 @@ pub const Entry = struct {
10 if (entry.target.eql(other.target)) {10 if (entry.target.eql(other.target)) {
11 return entry.offset < other.offset;11 return entry.offset < other.offset;
12 }12 }
13 if (entry.target.file == other.target.file) {13 return entry.target.lessThan(other.target);
14 return entry.target.index < other.target.index;
15 }
16 return entry.target.file < other.target.file;
17 }14 }
18 return entry.segment_id < other.segment_id;15 return entry.segment_id < other.segment_id;
19 }16 }
...@@ -47,7 +44,7 @@ pub const Bind = struct {...@@ -47,7 +44,7 @@ pub const Bind = struct {
47 const file = macho_file.getFile(index).?;44 const file = macho_file.getFile(index).?;
48 for (file.getAtoms()) |atom_index| {45 for (file.getAtoms()) |atom_index| {
49 const atom = file.getAtom(atom_index) orelse continue;46 const atom = file.getAtom(atom_index) orelse continue;
50 if (!atom.flags.alive) continue;47 if (!atom.isAlive()) continue;
51 if (atom.getInputSection(macho_file).isZerofill()) continue;48 if (atom.getInputSection(macho_file).isZerofill()) continue;
52 const atom_addr = atom.getAddress(macho_file);49 const atom_addr = atom.getAddress(macho_file);
53 const relocs = atom.getRelocs(macho_file);50 const relocs = atom.getRelocs(macho_file);
...@@ -299,7 +296,7 @@ pub const WeakBind = struct {...@@ -299,7 +296,7 @@ pub const WeakBind = struct {
299 const file = macho_file.getFile(index).?;296 const file = macho_file.getFile(index).?;
300 for (file.getAtoms()) |atom_index| {297 for (file.getAtoms()) |atom_index| {
301 const atom = file.getAtom(atom_index) orelse continue;298 const atom = file.getAtom(atom_index) orelse continue;
302 if (!atom.flags.alive) continue;299 if (!atom.isAlive()) continue;
303 if (atom.getInputSection(macho_file).isZerofill()) continue;300 if (atom.getInputSection(macho_file).isZerofill()) continue;
304 const atom_addr = atom.getAddress(macho_file);301 const atom_addr = atom.getAddress(macho_file);
305 const relocs = atom.getRelocs(macho_file);302 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();...@@ -8,11 +8,17 @@ const native_endian = builtin.target.cpu.arch.endian();
88
9const MachO = @import("../MachO.zig");9const MachO = @import("../MachO.zig");
1010
11pub fn isFatLibrary(path: []const u8) !bool {11pub fn readFatHeader(file: std.fs.File) !macho.fat_header {
12 const file = try std.fs.cwd().openFile(path, .{});12 return readFatHeaderGeneric(macho.fat_header, file, 0);
13 defer file.close();13}
14 const hdr = file.reader().readStructEndian(macho.fat_header, .big) catch return false;14
15 return hdr.magic == macho.FAT_MAGIC;15fn readFatHeaderGeneric(comptime Hdr: type, file: std.fs.File, offset: usize) !Hdr {
16 var buffer: [@sizeOf(Hdr)]u8 = undefined;
17 const nread = try file.preadAll(&buffer, offset);
18 if (nread != buffer.len) return error.InputOutput;
19 var hdr = @as(*align(1) const Hdr, @ptrCast(&buffer)).*;
20 mem.byteSwapAllFields(Hdr, &hdr);
21 return hdr;
16}22}
1723
18pub const Arch = struct {24pub const Arch = struct {
...@@ -21,17 +27,12 @@ pub const Arch = struct {...@@ -21,17 +27,12 @@ pub const Arch = struct {
21 size: u32,27 size: u32,
22};28};
2329
24pub fn parseArchs(path: []const u8, buffer: *[2]Arch) ![]const Arch {30pub fn parseArchs(file: std.fs.File, fat_header: macho.fat_header, out: *[2]Arch) ![]const Arch {
25 const file = try std.fs.cwd().openFile(path, .{});
26 defer file.close();
27 const reader = file.reader();
28 const fat_header = try reader.readStructEndian(macho.fat_header, .big);
29 assert(fat_header.magic == macho.FAT_MAGIC);
30
31 var count: usize = 0;31 var count: usize = 0;
32 var fat_arch_index: u32 = 0;32 var fat_arch_index: u32 = 0;
33 while (fat_arch_index < fat_header.nfat_arch) : (fat_arch_index += 1) {33 while (fat_arch_index < fat_header.nfat_arch and count < out.len) : (fat_arch_index += 1) {
34 const fat_arch = try reader.readStructEndian(macho.fat_arch, .big);34 const offset = @sizeOf(macho.fat_header) + @sizeOf(macho.fat_arch) * fat_arch_index;
35 const fat_arch = try readFatHeaderGeneric(macho.fat_arch, file, offset);
35 // If we come across an architecture that we do not know how to handle, that's36 // If we come across an architecture that we do not know how to handle, that's
36 // fine because we can keep looking for one that might match.37 // fine because we can keep looking for one that might match.
37 const arch: std.Target.Cpu.Arch = switch (fat_arch.cputype) {38 const arch: std.Target.Cpu.Arch = switch (fat_arch.cputype) {
...@@ -39,9 +40,9 @@ pub fn parseArchs(path: []const u8, buffer: *[2]Arch) ![]const Arch {...@@ -39,9 +40,9 @@ pub fn parseArchs(path: []const u8, buffer: *[2]Arch) ![]const Arch {
39 macho.CPU_TYPE_X86_64 => if (fat_arch.cpusubtype == macho.CPU_SUBTYPE_X86_64_ALL) .x86_64 else continue,40 macho.CPU_TYPE_X86_64 => if (fat_arch.cpusubtype == macho.CPU_SUBTYPE_X86_64_ALL) .x86_64 else continue,
40 else => continue,41 else => continue,
41 };42 };
42 buffer[count] = .{ .tag = arch, .offset = fat_arch.offset, .size = fat_arch.size };43 out[count] = .{ .tag = arch, .offset = fat_arch.offset, .size = fat_arch.size };
43 count += 1;44 count += 1;
44 }45 }
4546
46 return buffer[0..count];47 return out[0..count];
47}48}
src/link/MachO/file.zig+33-9
...@@ -37,11 +37,10 @@ pub const File = union(enum) {...@@ -37,11 +37,10 @@ pub const File = union(enum) {
37 }37 }
3838
39 pub fn scanRelocs(file: File, macho_file: *MachO) !void {39 pub fn scanRelocs(file: File, macho_file: *MachO) !void {
40 switch (file) {40 return switch (file) {
41 .dylib => unreachable,41 .dylib => unreachable,
42 .internal => |x| x.scanRelocs(macho_file),
43 inline else => |x| x.scanRelocs(macho_file),42 inline else => |x| x.scanRelocs(macho_file),
44 }43 };
45 }44 }
4645
47 /// Encodes symbol rank so that the following ordering applies:46 /// Encodes symbol rank so that the following ordering applies:
...@@ -182,19 +181,19 @@ pub const File = union(enum) {...@@ -182,19 +181,19 @@ pub const File = union(enum) {
182 if (ref.getFile(macho_file) == null) continue;181 if (ref.getFile(macho_file) == null) continue;
183 if (ref.file != file.getIndex()) continue;182 if (ref.file != file.getIndex()) continue;
184 const sym = ref.getSymbol(macho_file).?;183 const sym = ref.getSymbol(macho_file).?;
185 if (sym.flags.needs_got) {184 if (sym.getSectionFlags().needs_got) {
186 log.debug("'{s}' needs GOT", .{sym.getName(macho_file)});185 log.debug("'{s}' needs GOT", .{sym.getName(macho_file)});
187 try macho_file.got.addSymbol(ref, macho_file);186 try macho_file.got.addSymbol(ref, macho_file);
188 }187 }
189 if (sym.flags.stubs) {188 if (sym.getSectionFlags().stubs) {
190 log.debug("'{s}' needs STUBS", .{sym.getName(macho_file)});189 log.debug("'{s}' needs STUBS", .{sym.getName(macho_file)});
191 try macho_file.stubs.addSymbol(ref, macho_file);190 try macho_file.stubs.addSymbol(ref, macho_file);
192 }191 }
193 if (sym.flags.tlv_ptr) {192 if (sym.getSectionFlags().tlv_ptr) {
194 log.debug("'{s}' needs TLV pointer", .{sym.getName(macho_file)});193 log.debug("'{s}' needs TLV pointer", .{sym.getName(macho_file)});
195 try macho_file.tlv_ptr.addSymbol(ref, macho_file);194 try macho_file.tlv_ptr.addSymbol(ref, macho_file);
196 }195 }
197 if (sym.flags.objc_stubs) {196 if (sym.getSectionFlags().objc_stubs) {
198 log.debug("'{s}' needs OBJC STUBS", .{sym.getName(macho_file)});197 log.debug("'{s}' needs OBJC STUBS", .{sym.getName(macho_file)});
199 try macho_file.objc_stubs.addSymbol(ref, macho_file);198 try macho_file.objc_stubs.addSymbol(ref, macho_file);
200 }199 }
...@@ -268,6 +267,9 @@ pub const File = union(enum) {...@@ -268,6 +267,9 @@ pub const File = union(enum) {
268 const ref_file = ref.getFile(macho_file) orelse continue;267 const ref_file = ref.getFile(macho_file) orelse continue;
269 if (ref_file.getIndex() == file.getIndex()) continue;268 if (ref_file.getIndex() == file.getIndex()) continue;
270269
270 macho_file.dupes_mutex.lock();
271 defer macho_file.dupes_mutex.unlock();
272
271 const gop = try macho_file.dupes.getOrPut(gpa, file.getGlobals()[i]);273 const gop = try macho_file.dupes.getOrPut(gpa, file.getGlobals()[i]);
272 if (!gop.found_existing) {274 if (!gop.found_existing) {
273 gop.value_ptr.* = .{};275 gop.value_ptr.* = .{};
...@@ -281,7 +283,7 @@ pub const File = union(enum) {...@@ -281,7 +283,7 @@ pub const File = union(enum) {
281 defer tracy.end();283 defer tracy.end();
282 for (file.getAtoms()) |atom_index| {284 for (file.getAtoms()) |atom_index| {
283 const atom = file.getAtom(atom_index) orelse continue;285 const atom = file.getAtom(atom_index) orelse continue;
284 if (!atom.flags.alive) continue;286 if (!atom.isAlive()) continue;
285 atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);287 atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);
286 }288 }
287 }289 }
...@@ -295,11 +297,18 @@ pub const File = union(enum) {...@@ -295,11 +297,18 @@ pub const File = union(enum) {
295297
296 pub fn writeAtoms(file: File, macho_file: *MachO) !void {298 pub fn writeAtoms(file: File, macho_file: *MachO) !void {
297 return switch (file) {299 return switch (file) {
298 .dylib, .zig_object => unreachable,300 .dylib => unreachable,
299 inline else => |x| x.writeAtoms(macho_file),301 inline else => |x| x.writeAtoms(macho_file),
300 };302 };
301 }303 }
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
303 pub fn calcSymtabSize(file: File, macho_file: *MachO) void {312 pub fn calcSymtabSize(file: File, macho_file: *MachO) void {
304 return switch (file) {313 return switch (file) {
305 inline else => |x| x.calcSymtabSize(macho_file),314 inline else => |x| x.calcSymtabSize(macho_file),
...@@ -335,6 +344,21 @@ pub const File = union(enum) {...@@ -335,6 +344,21 @@ pub const File = union(enum) {
335 };344 };
336 }345 }
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
338 pub const Index = u32;362 pub const Index = u32;
339363
340 pub const Entry = union(enum) {364 pub const Entry = union(enum) {
src/link/MachO/hasher.zig+2
...@@ -55,6 +55,8 @@ pub fn ParallelHasher(comptime Hasher: type) type {...@@ -55,6 +55,8 @@ pub fn ParallelHasher(comptime Hasher: type) type {
55 out: *[hash_size]u8,55 out: *[hash_size]u8,
56 err: *fs.File.PReadError!usize,56 err: *fs.File.PReadError!usize,
57 ) void {57 ) void {
58 const tracy = trace(@src());
59 defer tracy.end();
58 err.* = file.preadAll(buffer, fstart);60 err.* = file.preadAll(buffer, fstart);
59 Hasher.hash(buffer, out, .{});61 Hasher.hash(buffer, out, .{});
60 }62 }
src/link/MachO/relocatable.zig+129-121
...@@ -27,22 +27,21 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c...@@ -27,22 +27,21 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c
27 }27 }
2828
29 for (positionals.items) |obj| {29 for (positionals.items) |obj| {
30 macho_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {30 macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
31 error.MalformedObject,31 error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an input file", .{}),
32 error.MalformedArchive,
33 error.InvalidCpuArch,
34 error.InvalidTarget,
35 => continue, // already reported
36 error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an object file", .{}),
37 else => |e| try macho_file.reportParseError(32 else => |e| try macho_file.reportParseError(
38 obj.path,33 obj.path,
39 "unexpected error: parsing input file failed with error {s}",34 "unexpected error: reading input file failed with error {s}",
40 .{@errorName(e)},35 .{@errorName(e)},
41 ),36 ),
42 };37 };
43 }38 }
4439
45 if (comp.link_errors.items.len > 0) return error.FlushFailure;40 if (macho_file.base.hasErrors()) return error.FlushFailure;
41
42 try macho_file.parseInputFiles();
43
44 if (macho_file.base.hasErrors()) return error.FlushFailure;
4645
47 try macho_file.resolveSymbols();46 try macho_file.resolveSymbols();
48 try macho_file.dedupLiterals();47 try macho_file.dedupLiterals();
...@@ -93,22 +92,21 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -93,22 +92,21 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
93 }92 }
9493
95 for (positionals.items) |obj| {94 for (positionals.items) |obj| {
96 parsePositional(macho_file, obj.path) catch |err| switch (err) {95 macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
97 error.MalformedObject,96 error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an input file", .{}),
98 error.MalformedArchive,
99 error.InvalidCpuArch,
100 error.InvalidTarget,
101 => continue, // already reported
102 error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an object file", .{}),
103 else => |e| try macho_file.reportParseError(97 else => |e| try macho_file.reportParseError(
104 obj.path,98 obj.path,
105 "unexpected error: parsing input file failed with error {s}",99 "unexpected error: reading input file failed with error {s}",
106 .{@errorName(e)},100 .{@errorName(e)},
107 ),101 ),
108 };102 };
109 }103 }
110104
111 if (comp.link_errors.items.len > 0) return error.FlushFailure;105 if (macho_file.base.hasErrors()) return error.FlushFailure;
106
107 try parseInputFilesAr(macho_file);
108
109 if (macho_file.base.hasErrors()) return error.FlushFailure;
112110
113 // First, we flush relocatable object file generated with our backends.111 // First, we flush relocatable object file generated with our backends.
114 if (macho_file.getZigObject()) |zo| {112 if (macho_file.getZigObject()) |zo| {
...@@ -225,79 +223,19 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?...@@ -225,79 +223,19 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
225 try macho_file.base.file.?.setEndPos(total_size);223 try macho_file.base.file.?.setEndPos(total_size);
226 try macho_file.base.file.?.pwriteAll(buffer.items, 0);224 try macho_file.base.file.?.pwriteAll(buffer.items, 0);
227225
228 if (comp.link_errors.items.len > 0) return error.FlushFailure;226 if (macho_file.base.hasErrors()) return error.FlushFailure;
229}
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;
244}227}
245228
246fn parseObject(macho_file: *MachO, path: []const u8) MachO.ParseError!void {229fn parseInputFilesAr(macho_file: *MachO) !void {
247 const tracy = trace(@src());230 const tracy = trace(@src());
248 defer tracy.end();231 defer tracy.end();
249232
250 const gpa = macho_file.base.comp.gpa;233 for (macho_file.objects.items) |index| {
251 const file = try std.fs.cwd().openFile(path, .{});234 macho_file.getFile(index).?.parseAr(macho_file) catch |err| switch (err) {
252 errdefer file.close();235 error.InvalidCpuArch => {}, // already reported
253 const handle = try macho_file.addFileHandle(file);236 else => |e| try macho_file.reportParseError2(index, "unexpected error: parsing input file failed with error {s}", .{@errorName(e)}),
254 const mtime: u64 = mtime: {
255 const stat = file.stat() catch break :mtime 0;
256 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
257 };
258 const index = @as(File.Index, @intCast(try macho_file.files.addOne(gpa)));
259 macho_file.files.set(index, .{
260 .object = .{
261 .offset = 0, // TODO FAT objects
262 .path = try gpa.dupe(u8, path),
263 .file_handle = handle,
264 .mtime = mtime,
265 .index = index,
266 },
267 });
268 try macho_file.objects.append(gpa, index);
269
270 const object = macho_file.getFile(index).?.object;
271 try object.parseAr(macho_file);
272}
273
274fn parseArchive(macho_file: *MachO, path: []const u8, fat_arch: ?fat.Arch) MachO.ParseError!void {
275 const tracy = trace(@src());
276 defer tracy.end();
277
278 const gpa = macho_file.base.comp.gpa;
279
280 const file = try std.fs.cwd().openFile(path, .{});
281 errdefer file.close();
282 const handle = try macho_file.addFileHandle(file);
283
284 var archive = Archive{};
285 defer archive.deinit(gpa);
286 try archive.parse(macho_file, path, handle, fat_arch);
287
288 var has_parse_error = false;
289 for (archive.objects.items) |extracted| {
290 const index = @as(File.Index, @intCast(try macho_file.files.addOne(gpa)));
291 macho_file.files.set(index, .{ .object = extracted });
292 const object = &macho_file.files.items(.data)[index].object;
293 object.index = index;
294 object.parseAr(macho_file) catch |err| switch (err) {
295 error.InvalidCpuArch => has_parse_error = true,
296 else => |e| return e,
297 };237 };
298 try macho_file.objects.append(gpa, index);
299 }238 }
300 if (has_parse_error) return error.MalformedArchive;
301}239}
302240
303fn markExports(macho_file: *MachO) void {241fn markExports(macho_file: *MachO) void {
...@@ -323,7 +261,7 @@ fn initOutputSections(macho_file: *MachO) !void {...@@ -323,7 +261,7 @@ fn initOutputSections(macho_file: *MachO) !void {
323 const file = macho_file.getFile(index).?;261 const file = macho_file.getFile(index).?;
324 for (file.getAtoms()) |atom_index| {262 for (file.getAtoms()) |atom_index| {
325 const atom = file.getAtom(atom_index) orelse continue;263 const atom = file.getAtom(atom_index) orelse continue;
326 if (!atom.flags.alive) continue;264 if (!atom.isAlive()) continue;
327 atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);265 atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);
328 }266 }
329 }267 }
...@@ -350,37 +288,54 @@ fn calcSectionSizes(macho_file: *MachO) !void {...@@ -350,37 +288,54 @@ fn calcSectionSizes(macho_file: *MachO) !void {
350 const tracy = trace(@src());288 const tracy = trace(@src());
351 defer tracy.end();289 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
358 if (macho_file.getZigObject()) |zo| {291 if (macho_file.getZigObject()) |zo| {
359 // TODO this will create a race292 // TODO this will create a race as we need to track merging of debug sections which we currently don't
360 zo.calcNumRelocs(macho_file);293 zo.calcNumRelocs(macho_file);
361 zo.calcSymtabSize(macho_file);
362 }294 }
363295
364 if (macho_file.eh_frame_sect_index) |_| {296 const tp = macho_file.base.comp.thread_pool;
365 try calcEhFrameSize(macho_file);297 var wg: WaitGroup = .{};
366 }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| {
369 if (macho_file.unwind_info_sect_index) |_| {311 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 }
371 }318 }
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
377 if (macho_file.unwind_info_sect_index) |_| {330 if (macho_file.unwind_info_sect_index) |_| {
378 calcCompactUnwindSize(macho_file);331 calcCompactUnwindSize(macho_file);
379 }332 }
380 try calcSymtabSize(macho_file);333 try calcSymtabSize(macho_file);
334
335 if (macho_file.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
381}336}
382337
383fn calcSectionSize(macho_file: *MachO, sect_id: u8) void {338fn calcSectionSizeWorker(macho_file: *MachO, sect_id: u8) void {
384 const tracy = trace(@src());339 const tracy = trace(@src());
385 defer tracy.end();340 defer tracy.end();
386341
...@@ -401,14 +356,25 @@ fn calcSectionSize(macho_file: *MachO, sect_id: u8) void {...@@ -401,14 +356,25 @@ fn calcSectionSize(macho_file: *MachO, sect_id: u8) void {
401 }356 }
402}357}
403358
404fn calcEhFrameSize(macho_file: *MachO) !void {359fn calcEhFrameSizeWorker(macho_file: *MachO) void {
405 const tracy = trace(@src());360 const tracy = trace(@src());
406 defer tracy.end();361 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
408 const header = &macho_file.sections.items(.header)[macho_file.eh_frame_sect_index.?];371 const header = &macho_file.sections.items(.header)[macho_file.eh_frame_sect_index.?];
409 header.size = try eh_frame.calcSize(macho_file);372 doWork(macho_file, header) catch |err| {
410 header.@"align" = 3;373 macho_file.reportUnexpectedError("failed to calculate size of section '__TEXT,__eh_frame': {s}", .{
411 header.nreloc = eh_frame.calcNumRelocs(macho_file);374 @errorName(err),
375 }) catch {};
376 _ = macho_file.has_errors.swap(true, .seq_cst);
377 };
412}378}
413379
414fn calcCompactUnwindSize(macho_file: *MachO) void {380fn calcCompactUnwindSize(macho_file: *MachO) void {
...@@ -639,33 +605,74 @@ fn writeSections(macho_file: *MachO) !void {...@@ -639,33 +605,74 @@ fn writeSections(macho_file: *MachO) !void {
639 try macho_file.strtab.resize(gpa, cmd.strsize);605 try macho_file.strtab.resize(gpa, cmd.strsize);
640 macho_file.strtab.items[0] = 0;606 macho_file.strtab.items[0] = 0;
641607
642 for (macho_file.objects.items) |index| {608 const tp = macho_file.base.comp.thread_pool;
643 try macho_file.getFile(index).?.object.writeAtomsRelocatable(macho_file);609 var wg: WaitGroup = .{};
644 macho_file.getFile(index).?.writeSymtab(macho_file, macho_file);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 }
645 }633 }
646634
635 if (macho_file.has_errors.swap(false, .seq_cst)) return error.FlushFailure;
636
647 if (macho_file.getZigObject()) |zo| {637 if (macho_file.getZigObject()) |zo| {
648 try zo.writeRelocs(macho_file);638 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);
655 }639 }
640}
656641
657 if (macho_file.unwind_info_sect_index) |_| {642fn writeAtomsWorker(macho_file: *MachO, file: File) void {
658 for (macho_file.objects.items) |index| {643 const tracy = trace(@src());
659 try macho_file.getFile(index).?.object.writeCompactUnwindRelocatable(macho_file);644 defer tracy.end();
660 }645 file.writeAtomsRelocatable(macho_file) catch |err| {
661 }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 };
662}651}
663652
664fn writeEhFrame(macho_file: *MachO) !void {653fn writeEhFrameWorker(macho_file: *MachO) void {
654 const tracy = trace(@src());
655 defer tracy.end();
665 const sect_index = macho_file.eh_frame_sect_index.?;656 const sect_index = macho_file.eh_frame_sect_index.?;
666 const buffer = macho_file.sections.items(.out)[sect_index];657 const buffer = macho_file.sections.items(.out)[sect_index];
667 const relocs = macho_file.sections.items(.relocs)[sect_index];658 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 };
669}676}
670677
671fn writeSectionsToFile(macho_file: *MachO) !void {678fn writeSectionsToFile(macho_file: *MachO) !void {
...@@ -778,3 +785,4 @@ const File = @import("file.zig").File;...@@ -778,3 +785,4 @@ const File = @import("file.zig").File;
778const MachO = @import("../MachO.zig");785const MachO = @import("../MachO.zig");
779const Object = @import("Object.zig");786const Object = @import("Object.zig");
780const Symbol = @import("Symbol.zig");787const Symbol = @import("Symbol.zig");
788const WaitGroup = std.Thread.WaitGroup;
src/link/MachO/synthetic.zig+4-4
...@@ -24,8 +24,8 @@ pub const ZigGotSection = struct {...@@ -24,8 +24,8 @@ pub const ZigGotSection = struct {
24 const entry = &zig_got.entries.items[index];24 const entry = &zig_got.entries.items[index];
25 entry.* = sym_index;25 entry.* = sym_index;
26 const symbol = &zo.symbols.items[sym_index];26 const symbol = &zo.symbols.items[sym_index];
27 assert(symbol.flags.needs_zig_got);27 assert(symbol.getSectionFlags().needs_zig_got);
28 symbol.flags.has_zig_got = true;28 symbol.setSectionFlags(.{ .has_zig_got = true });
29 symbol.addExtra(.{ .zig_got = index }, macho_file);29 symbol.addExtra(.{ .zig_got = index }, macho_file);
30 return index;30 return index;
31 }31 }
...@@ -121,7 +121,7 @@ pub const GotSection = struct {...@@ -121,7 +121,7 @@ pub const GotSection = struct {
121 const entry = try got.symbols.addOne(gpa);121 const entry = try got.symbols.addOne(gpa);
122 entry.* = ref;122 entry.* = ref;
123 const symbol = ref.getSymbol(macho_file).?;123 const symbol = ref.getSymbol(macho_file).?;
124 symbol.flags.has_got = true;124 symbol.setSectionFlags(.{ .has_got = true });
125 symbol.addExtra(.{ .got = index }, macho_file);125 symbol.addExtra(.{ .got = index }, macho_file);
126 }126 }
127127
...@@ -689,7 +689,7 @@ pub const DataInCode = struct {...@@ -689,7 +689,7 @@ pub const DataInCode = struct {
689 dices[next_dice].offset < end_off) : (next_dice += 1)689 dices[next_dice].offset < end_off) : (next_dice += 1)
690 {}690 {}
691691
692 if (atom.flags.alive) for (dices[start_dice..next_dice]) |d| {692 if (atom.isAlive()) for (dices[start_dice..next_dice]) |d| {
693 dice.entries.appendAssumeCapacity(.{693 dice.entries.appendAssumeCapacity(.{
694 .atom_ref = .{ .index = atom_index, .file = index },694 .atom_ref = .{ .index = atom_index, .file = index },
695 .offset = @intCast(d.offset - start_off),695 .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 {...@@ -17,7 +17,7 @@ pub fn createThunks(sect_id: u8, macho_file: *MachO) !void {
17 while (i < atoms.len) {17 while (i < atoms.len) {
18 const start = i;18 const start = i;
19 const start_atom = atoms[start].getAtom(macho_file).?;19 const start_atom = atoms[start].getAtom(macho_file).?;
20 assert(start_atom.flags.alive);20 assert(start_atom.isAlive());
21 start_atom.value = advance(header, start_atom.size, start_atom.alignment);21 start_atom.value = advance(header, start_atom.size, start_atom.alignment);
22 i += 1;22 i += 1;
2323
...@@ -25,7 +25,7 @@ pub fn createThunks(sect_id: u8, macho_file: *MachO) !void {...@@ -25,7 +25,7 @@ pub fn createThunks(sect_id: u8, macho_file: *MachO) !void {
25 header.size - start_atom.value < max_allowed_distance) : (i += 1)25 header.size - start_atom.value < max_allowed_distance) : (i += 1)
26 {26 {
27 const atom = atoms[i].getAtom(macho_file).?;27 const atom = atoms[i].getAtom(macho_file).?;
28 assert(atom.flags.alive);28 assert(atom.isAlive());
29 atom.value = advance(header, atom.size, atom.alignment);29 atom.value = advance(header, atom.size, atom.alignment);
30 }30 }
3131
...@@ -71,7 +71,7 @@ fn scanRelocs(thunk_index: Thunk.Index, gpa: Allocator, atoms: []const MachO.Ref...@@ -71,7 +71,7 @@ fn scanRelocs(thunk_index: Thunk.Index, gpa: Allocator, atoms: []const MachO.Ref
7171
72fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {72fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {
73 const target = rel.getTargetSymbol(atom.*, macho_file);73 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;
75 if (atom.out_n_sect != target.getOutputSectionIndex(macho_file)) return false;75 if (atom.out_n_sect != target.getOutputSectionIndex(macho_file)) return false;
76 const target_atom = target.getAtom(macho_file).?;76 const target_atom = target.getAtom(macho_file).?;
77 if (target_atom.value == @as(u64, @bitCast(@as(i64, -1)))) return false;77 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 {...@@ -658,9 +658,9 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {
658 var object = Object.create(wasm, obj_file, path, null) catch |err| switch (err) {658 var object = Object.create(wasm, obj_file, path, null) catch |err| switch (err) {
659 error.InvalidMagicByte, error.NotObjectFile => return false,659 error.InvalidMagicByte, error.NotObjectFile => return false,
660 else => |e| {660 else => |e| {
661 var err_note = try wasm.addErrorWithNotes(1);661 var err_note = try wasm.base.addErrorWithNotes(1);
662 try err_note.addMsg(wasm, "Failed parsing object file: {s}", .{@errorName(e)});662 try err_note.addMsg("Failed parsing object file: {s}", .{@errorName(e)});
663 try err_note.addNote(wasm, "while parsing '{s}'", .{path});663 try err_note.addNote("while parsing '{s}'", .{path});
664 return error.FlushFailure;664 return error.FlushFailure;
665 },665 },
666 };666 };
...@@ -714,9 +714,9 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {...@@ -714,9 +714,9 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
714 return false;714 return false;
715 },715 },
716 else => |e| {716 else => |e| {
717 var err_note = try wasm.addErrorWithNotes(1);717 var err_note = try wasm.base.addErrorWithNotes(1);
718 try err_note.addMsg(wasm, "Failed parsing archive: {s}", .{@errorName(e)});718 try err_note.addMsg("Failed parsing archive: {s}", .{@errorName(e)});
719 try err_note.addNote(wasm, "while parsing archive {s}", .{path});719 try err_note.addNote("while parsing archive {s}", .{path});
720 return error.FlushFailure;720 return error.FlushFailure;
721 },721 },
722 };722 };
...@@ -741,9 +741,9 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {...@@ -741,9 +741,9 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
741741
742 for (offsets.keys()) |file_offset| {742 for (offsets.keys()) |file_offset| {
743 var object = archive.parseObject(wasm, file_offset) catch |e| {743 var object = archive.parseObject(wasm, file_offset) catch |e| {
744 var err_note = try wasm.addErrorWithNotes(1);744 var err_note = try wasm.base.addErrorWithNotes(1);
745 try err_note.addMsg(wasm, "Failed parsing object: {s}", .{@errorName(e)});745 try err_note.addMsg("Failed parsing object: {s}", .{@errorName(e)});
746 try err_note.addNote(wasm, "while parsing object in archive {s}", .{path});746 try err_note.addNote("while parsing object in archive {s}", .{path});
747 return error.FlushFailure;747 return error.FlushFailure;
748 };748 };
749 object.index = @enumFromInt(wasm.files.len);749 object.index = @enumFromInt(wasm.files.len);
...@@ -779,9 +779,9 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {...@@ -779,9 +779,9 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
779779
780 if (symbol.isLocal()) {780 if (symbol.isLocal()) {
781 if (symbol.isUndefined()) {781 if (symbol.isUndefined()) {
782 var err = try wasm.addErrorWithNotes(1);782 var err = try wasm.base.addErrorWithNotes(1);
783 try err.addMsg(wasm, "Local symbols are not allowed to reference imports", .{});783 try err.addMsg("Local symbols are not allowed to reference imports", .{});
784 try err.addNote(wasm, "symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() });784 try err.addNote("symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() });
785 }785 }
786 try wasm.resolved_symbols.putNoClobber(gpa, location, {});786 try wasm.resolved_symbols.putNoClobber(gpa, location, {});
787 continue;787 continue;
...@@ -816,10 +816,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {...@@ -816,10 +816,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
816 break :outer; // existing is weak, while new one isn't. Replace it.816 break :outer; // existing is weak, while new one isn't. Replace it.
817 }817 }
818 // both are defined and weak, we have a symbol collision.818 // both are defined and weak, we have a symbol collision.
819 var err = try wasm.addErrorWithNotes(2);819 var err = try wasm.base.addErrorWithNotes(2);
820 try err.addMsg(wasm, "symbol '{s}' defined multiple times", .{sym_name});820 try err.addMsg("symbol '{s}' defined multiple times", .{sym_name});
821 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});821 try err.addNote("first definition in '{s}'", .{existing_file_path});
822 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});822 try err.addNote("next definition in '{s}'", .{obj_file.path()});
823 }823 }
824824
825 try wasm.discarded.put(gpa, location, existing_loc);825 try wasm.discarded.put(gpa, location, existing_loc);
...@@ -827,10 +827,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {...@@ -827,10 +827,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
827 }827 }
828828
829 if (symbol.tag != existing_sym.tag) {829 if (symbol.tag != existing_sym.tag) {
830 var err = try wasm.addErrorWithNotes(2);830 var err = try wasm.base.addErrorWithNotes(2);
831 try err.addMsg(wasm, "symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) });831 try err.addMsg("symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) });
832 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});832 try err.addNote("first definition in '{s}'", .{existing_file_path});
833 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});833 try err.addNote("next definition in '{s}'", .{obj_file.path()});
834 }834 }
835835
836 if (existing_sym.isUndefined() and symbol.isUndefined()) {836 if (existing_sym.isUndefined() and symbol.isUndefined()) {
...@@ -847,14 +847,14 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {...@@ -847,14 +847,14 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
847 const imp = obj_file.import(sym_index);847 const imp = obj_file.import(sym_index);
848 const module_name = obj_file.string(imp.module_name);848 const module_name = obj_file.string(imp.module_name);
849 if (!mem.eql(u8, existing_name, module_name)) {849 if (!mem.eql(u8, existing_name, module_name)) {
850 var err = try wasm.addErrorWithNotes(2);850 var err = try wasm.base.addErrorWithNotes(2);
851 try err.addMsg(wasm, "symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{851 try err.addMsg("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{
852 sym_name,852 sym_name,
853 existing_name,853 existing_name,
854 module_name,854 module_name,
855 });855 });
856 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});856 try err.addNote("first definition in '{s}'", .{existing_file_path});
857 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});857 try err.addNote("next definition in '{s}'", .{obj_file.path()});
858 }858 }
859 }859 }
860860
...@@ -867,10 +867,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {...@@ -867,10 +867,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
867 const existing_ty = wasm.getGlobalType(existing_loc);867 const existing_ty = wasm.getGlobalType(existing_loc);
868 const new_ty = wasm.getGlobalType(location);868 const new_ty = wasm.getGlobalType(location);
869 if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) {869 if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) {
870 var err = try wasm.addErrorWithNotes(2);870 var err = try wasm.base.addErrorWithNotes(2);
871 try err.addMsg(wasm, "symbol '{s}' mismatching global types", .{sym_name});871 try err.addMsg("symbol '{s}' mismatching global types", .{sym_name});
872 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});872 try err.addNote("first definition in '{s}'", .{existing_file_path});
873 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});873 try err.addNote("next definition in '{s}'", .{obj_file.path()});
874 }874 }
875 }875 }
876876
...@@ -878,11 +878,11 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {...@@ -878,11 +878,11 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
878 const existing_ty = wasm.getFunctionSignature(existing_loc);878 const existing_ty = wasm.getFunctionSignature(existing_loc);
879 const new_ty = wasm.getFunctionSignature(location);879 const new_ty = wasm.getFunctionSignature(location);
880 if (!existing_ty.eql(new_ty)) {880 if (!existing_ty.eql(new_ty)) {
881 var err = try wasm.addErrorWithNotes(3);881 var err = try wasm.base.addErrorWithNotes(3);
882 try err.addMsg(wasm, "symbol '{s}' mismatching function signatures.", .{sym_name});882 try err.addMsg("symbol '{s}' mismatching function signatures.", .{sym_name});
883 try err.addNote(wasm, "expected signature {}, but found signature {}", .{ existing_ty, new_ty });883 try err.addNote("expected signature {}, but found signature {}", .{ existing_ty, new_ty });
884 try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});884 try err.addNote("first definition in '{s}'", .{existing_file_path});
885 try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});885 try err.addNote("next definition in '{s}'", .{obj_file.path()});
886 }886 }
887 }887 }
888888
...@@ -930,9 +930,9 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {...@@ -930,9 +930,9 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
930 // Parse object and and resolve symbols again before we check remaining930 // Parse object and and resolve symbols again before we check remaining
931 // undefined symbols.931 // undefined symbols.
932 var object = archive.parseObject(wasm, offset.items[0]) catch |e| {932 var object = archive.parseObject(wasm, offset.items[0]) catch |e| {
933 var err_note = try wasm.addErrorWithNotes(1);933 var err_note = try wasm.base.addErrorWithNotes(1);
934 try err_note.addMsg(wasm, "Failed parsing object: {s}", .{@errorName(e)});934 try err_note.addMsg("Failed parsing object: {s}", .{@errorName(e)});
935 try err_note.addNote(wasm, "while parsing object in archive {s}", .{archive.name});935 try err_note.addNote("while parsing object in archive {s}", .{archive.name});
936 return error.FlushFailure;936 return error.FlushFailure;
937 };937 };
938 object.index = @enumFromInt(wasm.files.len);938 object.index = @enumFromInt(wasm.files.len);
...@@ -1237,9 +1237,9 @@ fn validateFeatures(...@@ -1237,9 +1237,9 @@ fn validateFeatures(
1237 allowed[used_index] = is_enabled;1237 allowed[used_index] = is_enabled;
1238 emit_features_count.* += @intFromBool(is_enabled);1238 emit_features_count.* += @intFromBool(is_enabled);
1239 } else if (is_enabled and !allowed[used_index]) {1239 } else if (is_enabled and !allowed[used_index]) {
1240 var err = try wasm.addErrorWithNotes(1);1240 var err = try wasm.base.addErrorWithNotes(1);
1241 try err.addMsg(wasm, "feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))});1241 try err.addMsg("feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))});
1242 try err.addNote(wasm, "defined in '{s}'", .{wasm.files.items(.data)[used_set >> 1].object.path});1242 try err.addNote("defined in '{s}'", .{wasm.files.items(.data)[used_set >> 1].object.path});
1243 valid_feature_set = false;1243 valid_feature_set = false;
1244 }1244 }
1245 }1245 }
...@@ -1251,7 +1251,8 @@ fn validateFeatures(...@@ -1251,7 +1251,8 @@ fn validateFeatures(
1251 if (shared_memory) {1251 if (shared_memory) {
1252 const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)];1252 const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)];
1253 if (@as(u1, @truncate(disallowed_feature)) != 0) {1253 if (@as(u1, @truncate(disallowed_feature)) != 0) {
1254 try wasm.addErrorWithoutNotes(1254 var err = try wasm.base.addErrorWithNotes(0);
1255 try err.addMsg(
1255 "shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled",1256 "shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled",
1256 .{wasm.files.items(.data)[disallowed_feature >> 1].object.path},1257 .{wasm.files.items(.data)[disallowed_feature >> 1].object.path},
1257 );1258 );
...@@ -1260,7 +1261,8 @@ fn validateFeatures(...@@ -1260,7 +1261,8 @@ fn validateFeatures(
12601261
1261 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {1262 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
1262 if (!allowed[@intFromEnum(feature)]) {1263 if (!allowed[@intFromEnum(feature)]) {
1263 try wasm.addErrorWithoutNotes("feature '{}' is not used but is required for shared-memory", .{feature});1264 var err = try wasm.base.addErrorWithNotes(0);
1265 try err.addMsg("feature '{}' is not used but is required for shared-memory", .{feature});
1264 }1266 }
1265 }1267 }
1266 }1268 }
...@@ -1268,7 +1270,8 @@ fn validateFeatures(...@@ -1268,7 +1270,8 @@ fn validateFeatures(
1268 if (has_tls) {1270 if (has_tls) {
1269 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {1271 for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
1270 if (!allowed[@intFromEnum(feature)]) {1272 if (!allowed[@intFromEnum(feature)]) {
1271 try wasm.addErrorWithoutNotes("feature '{}' is not used but is required for thread-local storage", .{feature});1273 var err = try wasm.base.addErrorWithNotes(0);
1274 try err.addMsg("feature '{}' is not used but is required for thread-local storage", .{feature});
1272 }1275 }
1273 }1276 }
1274 }1277 }
...@@ -1281,10 +1284,10 @@ fn validateFeatures(...@@ -1281,10 +1284,10 @@ fn validateFeatures(
1281 // from here a feature is always used1284 // from here a feature is always used
1282 const disallowed_feature = disallowed[@intFromEnum(feature.tag)];1285 const disallowed_feature = disallowed[@intFromEnum(feature.tag)];
1283 if (@as(u1, @truncate(disallowed_feature)) != 0) {1286 if (@as(u1, @truncate(disallowed_feature)) != 0) {
1284 var err = try wasm.addErrorWithNotes(2);1287 var err = try wasm.base.addErrorWithNotes(2);
1285 try err.addMsg(wasm, "feature '{}' is disallowed, but used by linked object", .{feature.tag});1288 try err.addMsg("feature '{}' is disallowed, but used by linked object", .{feature.tag});
1286 try err.addNote(wasm, "disallowed by '{s}'", .{wasm.files.items(.data)[disallowed_feature >> 1].object.path});1289 try err.addNote("disallowed by '{s}'", .{wasm.files.items(.data)[disallowed_feature >> 1].object.path});
1287 try err.addNote(wasm, "used in '{s}'", .{object.path});1290 try err.addNote("used in '{s}'", .{object.path});
1288 valid_feature_set = false;1291 valid_feature_set = false;
1289 }1292 }
12901293
...@@ -1295,10 +1298,10 @@ fn validateFeatures(...@@ -1295,10 +1298,10 @@ fn validateFeatures(
1295 for (required, 0..) |required_feature, feature_index| {1298 for (required, 0..) |required_feature, feature_index| {
1296 const is_required = @as(u1, @truncate(required_feature)) != 0;1299 const is_required = @as(u1, @truncate(required_feature)) != 0;
1297 if (is_required and !object_used_features[feature_index]) {1300 if (is_required and !object_used_features[feature_index]) {
1298 var err = try wasm.addErrorWithNotes(2);1301 var err = try wasm.base.addErrorWithNotes(2);
1299 try err.addMsg(wasm, "feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))});1302 try err.addMsg("feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))});
1300 try err.addNote(wasm, "required by '{s}'", .{wasm.files.items(.data)[required_feature >> 1].object.path});1303 try err.addNote("required by '{s}'", .{wasm.files.items(.data)[required_feature >> 1].object.path});
1301 try err.addNote(wasm, "missing in '{s}'", .{object.path});1304 try err.addNote("missing in '{s}'", .{object.path});
1302 valid_feature_set = false;1305 valid_feature_set = false;
1303 }1306 }
1304 }1307 }
...@@ -1376,9 +1379,9 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void {...@@ -1376,9 +1379,9 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void {
1376 else1379 else
1377 wasm.name;1380 wasm.name;
1378 const symbol_name = undef.getName(wasm);1381 const symbol_name = undef.getName(wasm);
1379 var err = try wasm.addErrorWithNotes(1);1382 var err = try wasm.base.addErrorWithNotes(1);
1380 try err.addMsg(wasm, "could not resolve undefined symbol '{s}'", .{symbol_name});1383 try err.addMsg("could not resolve undefined symbol '{s}'", .{symbol_name});
1381 try err.addNote(wasm, "defined in '{s}'", .{file_name});1384 try err.addNote("defined in '{s}'", .{file_name});
1382 }1385 }
1383 }1386 }
1384 if (found_undefined_symbols) {1387 if (found_undefined_symbols) {
...@@ -1757,7 +1760,8 @@ fn setupInitFunctions(wasm: *Wasm) !void {...@@ -1757,7 +1760,8 @@ fn setupInitFunctions(wasm: *Wasm) !void {
1757 break :ty object.func_types[func.type_index];1760 break :ty object.func_types[func.type_index];
1758 };1761 };
1759 if (ty.params.len != 0) {1762 if (ty.params.len != 0) {
1760 try wasm.addErrorWithoutNotes("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)});1763 var err = try wasm.base.addErrorWithNotes(0);
1764 try err.addMsg("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)});
1761 }1765 }
1762 log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)});1766 log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)});
1763 wasm.init_funcs.appendAssumeCapacity(.{1767 wasm.init_funcs.appendAssumeCapacity(.{
...@@ -2140,7 +2144,8 @@ fn checkExportNames(wasm: *Wasm) !void {...@@ -2140,7 +2144,8 @@ fn checkExportNames(wasm: *Wasm) !void {
21402144
2141 for (force_exp_names) |exp_name| {2145 for (force_exp_names) |exp_name| {
2142 const loc = wasm.findGlobalSymbol(exp_name) orelse {2146 const loc = wasm.findGlobalSymbol(exp_name) orelse {
2143 try wasm.addErrorWithoutNotes("could not export '{s}', symbol not found", .{exp_name});2147 var err = try wasm.base.addErrorWithNotes(0);
2148 try err.addMsg("could not export '{s}', symbol not found", .{exp_name});
2144 failed_exports = true;2149 failed_exports = true;
2145 continue;2150 continue;
2146 };2151 };
...@@ -2203,13 +2208,15 @@ fn setupStart(wasm: *Wasm) !void {...@@ -2203,13 +2208,15 @@ fn setupStart(wasm: *Wasm) !void {
2203 const entry_name = wasm.entry_name orelse return;2208 const entry_name = wasm.entry_name orelse return;
22042209
2205 const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {2210 const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {
2206 try wasm.addErrorWithoutNotes("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});2211 var err = try wasm.base.addErrorWithNotes(0);
2212 try err.addMsg("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});
2207 return error.FlushFailure;2213 return error.FlushFailure;
2208 };2214 };
22092215
2210 const symbol = symbol_loc.getSymbol(wasm);2216 const symbol = symbol_loc.getSymbol(wasm);
2211 if (symbol.tag != .function) {2217 if (symbol.tag != .function) {
2212 try wasm.addErrorWithoutNotes("Entry symbol '{s}' is not a function", .{entry_name});2218 var err = try wasm.base.addErrorWithNotes(0);
2219 try err.addMsg("Entry symbol '{s}' is not a function", .{entry_name});
2213 return error.FlushFailure;2220 return error.FlushFailure;
2214 }2221 }
22152222
...@@ -2314,13 +2321,16 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2314,13 +2321,16 @@ fn setupMemory(wasm: *Wasm) !void {
23142321
2315 if (wasm.initial_memory) |initial_memory| {2322 if (wasm.initial_memory) |initial_memory| {
2316 if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) {2323 if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) {
2317 try wasm.addErrorWithoutNotes("Initial memory must be {d}-byte aligned", .{page_size});2324 var err = try wasm.base.addErrorWithNotes(0);
2325 try err.addMsg("Initial memory must be {d}-byte aligned", .{page_size});
2318 }2326 }
2319 if (memory_ptr > initial_memory) {2327 if (memory_ptr > initial_memory) {
2320 try wasm.addErrorWithoutNotes("Initial memory too small, must be at least {d} bytes", .{memory_ptr});2328 var err = try wasm.base.addErrorWithNotes(0);
2329 try err.addMsg("Initial memory too small, must be at least {d} bytes", .{memory_ptr});
2321 }2330 }
2322 if (initial_memory > max_memory_allowed) {2331 if (initial_memory > max_memory_allowed) {
2323 try wasm.addErrorWithoutNotes("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});2332 var err = try wasm.base.addErrorWithNotes(0);
2333 try err.addMsg("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});
2324 }2334 }
2325 memory_ptr = initial_memory;2335 memory_ptr = initial_memory;
2326 }2336 }
...@@ -2337,13 +2347,16 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2337,13 +2347,16 @@ fn setupMemory(wasm: *Wasm) !void {
23372347
2338 if (wasm.max_memory) |max_memory| {2348 if (wasm.max_memory) |max_memory| {
2339 if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) {2349 if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) {
2340 try wasm.addErrorWithoutNotes("Maximum memory must be {d}-byte aligned", .{page_size});2350 var err = try wasm.base.addErrorWithNotes(0);
2351 try err.addMsg("Maximum memory must be {d}-byte aligned", .{page_size});
2341 }2352 }
2342 if (memory_ptr > max_memory) {2353 if (memory_ptr > max_memory) {
2343 try wasm.addErrorWithoutNotes("Maximum memory too small, must be at least {d} bytes", .{memory_ptr});2354 var err = try wasm.base.addErrorWithNotes(0);
2355 try err.addMsg("Maximum memory too small, must be at least {d} bytes", .{memory_ptr});
2344 }2356 }
2345 if (max_memory > max_memory_allowed) {2357 if (max_memory > max_memory_allowed) {
2346 try wasm.addErrorWithoutNotes("Maximum memory exceeds maximum amount {d}", .{max_memory_allowed});2358 var err = try wasm.base.addErrorWithNotes(0);
2359 try err.addMsg("Maximum memory exceeds maximum amount {d}", .{max_memory_allowed});
2347 }2360 }
2348 wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size));2361 wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size));
2349 wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX);2362 wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX);
...@@ -2446,9 +2459,9 @@ pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: Sym...@@ -2446,9 +2459,9 @@ pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: Sym
2446 break :blk index;2459 break :blk index;
2447 };2460 };
2448 } else {2461 } else {
2449 var err = try wasm.addErrorWithNotes(1);2462 var err = try wasm.base.addErrorWithNotes(1);
2450 try err.addMsg(wasm, "found unknown section '{s}'", .{section_name});2463 try err.addMsg("found unknown section '{s}'", .{section_name});
2451 try err.addNote(wasm, "defined in '{s}'", .{obj_file.path()});2464 try err.addNote("defined in '{s}'", .{obj_file.path()});
2452 return error.UnexpectedValue;2465 return error.UnexpectedValue;
2453 }2466 }
2454 },2467 },
...@@ -2564,23 +2577,23 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -2564,23 +2577,23 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
2564 if (wasm.zig_object_index != .null) {2577 if (wasm.zig_object_index != .null) {
2565 try wasm.resolveSymbolsInObject(wasm.zig_object_index);2578 try wasm.resolveSymbolsInObject(wasm.zig_object_index);
2566 }2579 }
2567 if (comp.link_errors.items.len > 0) return error.FlushFailure;2580 if (wasm.base.hasErrors()) return error.FlushFailure;
2568 for (wasm.objects.items) |object_index| {2581 for (wasm.objects.items) |object_index| {
2569 try wasm.resolveSymbolsInObject(object_index);2582 try wasm.resolveSymbolsInObject(object_index);
2570 }2583 }
2571 if (comp.link_errors.items.len > 0) return error.FlushFailure;2584 if (wasm.base.hasErrors()) return error.FlushFailure;
25722585
2573 var emit_features_count: u32 = 0;2586 var emit_features_count: u32 = 0;
2574 var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;2587 var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;
2575 try wasm.validateFeatures(&enabled_features, &emit_features_count);2588 try wasm.validateFeatures(&enabled_features, &emit_features_count);
2576 try wasm.resolveSymbolsInArchives();2589 try wasm.resolveSymbolsInArchives();
2577 if (comp.link_errors.items.len > 0) return error.FlushFailure;2590 if (wasm.base.hasErrors()) return error.FlushFailure;
2578 try wasm.resolveLazySymbols();2591 try wasm.resolveLazySymbols();
2579 try wasm.checkUndefinedSymbols();2592 try wasm.checkUndefinedSymbols();
2580 try wasm.checkExportNames();2593 try wasm.checkExportNames();
25812594
2582 try wasm.setupInitFunctions();2595 try wasm.setupInitFunctions();
2583 if (comp.link_errors.items.len > 0) return error.FlushFailure;2596 if (wasm.base.hasErrors()) return error.FlushFailure;
2584 try wasm.setupStart();2597 try wasm.setupStart();
25852598
2586 try wasm.markReferences();2599 try wasm.markReferences();
...@@ -2589,7 +2602,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -2589,7 +2602,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
2589 try wasm.mergeTypes();2602 try wasm.mergeTypes();
2590 try wasm.allocateAtoms();2603 try wasm.allocateAtoms();
2591 try wasm.setupMemory();2604 try wasm.setupMemory();
2592 if (comp.link_errors.items.len > 0) return error.FlushFailure;2605 if (wasm.base.hasErrors()) return error.FlushFailure;
2593 wasm.allocateVirtualAddresses();2606 wasm.allocateVirtualAddresses();
2594 wasm.mapFunctionTable();2607 wasm.mapFunctionTable();
2595 try wasm.initializeCallCtorsFunction();2608 try wasm.initializeCallCtorsFunction();
...@@ -2599,7 +2612,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no...@@ -2599,7 +2612,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
2599 try wasm.setupStartSection();2612 try wasm.setupStartSection();
2600 try wasm.setupExports();2613 try wasm.setupExports();
2601 try wasm.writeToFile(enabled_features, emit_features_count, arena);2614 try wasm.writeToFile(enabled_features, emit_features_count, arena);
2602 if (comp.link_errors.items.len > 0) return error.FlushFailure;2615 if (wasm.base.hasErrors()) return error.FlushFailure;
2603}2616}
26042617
2605/// Writes the WebAssembly in-memory module to the file2618/// Writes the WebAssembly in-memory module to the file
...@@ -2997,7 +3010,10 @@ fn writeToFile(...@@ -2997,7 +3010,10 @@ fn writeToFile(
2997 }) catch unreachable;3010 }) catch unreachable;
2998 try emitBuildIdSection(&binary_bytes, str);3011 try emitBuildIdSection(&binary_bytes, str);
2999 },3012 },
3000 else => |mode| try wasm.addErrorWithoutNotes("build-id '{s}' is not supported for WebAssembly", .{@tagName(mode)}),3013 else => |mode| {
3014 var err = try wasm.base.addErrorWithNotes(0);
3015 try err.addMsg("build-id '{s}' is not supported for WebAssembly", .{@tagName(mode)});
3016 },
3001 }3017 }
30023018
3003 var debug_bytes = std.ArrayList(u8).init(gpa);3019 var debug_bytes = std.ArrayList(u8).init(gpa);
...@@ -4086,57 +4102,3 @@ fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8...@@ -4086,57 +4102,3 @@ fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8
4086 .command => "_start",4102 .command => "_start",
4087 };4103 };
4088}4104}
4089
4090const ErrorWithNotes = struct {
4091 /// Allocated index in comp.link_errors array.
4092 index: usize,
4093
4094 /// Next available note slot.
4095 note_slot: usize = 0,
4096
4097 pub fn addMsg(
4098 err: ErrorWithNotes,
4099 wasm_file: *const Wasm,
4100 comptime format: []const u8,
4101 args: anytype,
4102 ) error{OutOfMemory}!void {
4103 const comp = wasm_file.base.comp;
4104 const gpa = comp.gpa;
4105 const err_msg = &comp.link_errors.items[err.index];
4106 err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
4107 }
4108
4109 pub fn addNote(
4110 err: *ErrorWithNotes,
4111 wasm_file: *const Wasm,
4112 comptime format: []const u8,
4113 args: anytype,
4114 ) error{OutOfMemory}!void {
4115 const comp = wasm_file.base.comp;
4116 const gpa = comp.gpa;
4117 const err_msg = &comp.link_errors.items[err.index];
4118 err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };
4119 err.note_slot += 1;
4120 }
4121};
4122
4123pub fn addErrorWithNotes(wasm: *const Wasm, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
4124 const comp = wasm.base.comp;
4125 const gpa = comp.gpa;
4126 try comp.link_errors.ensureUnusedCapacity(gpa, 1);
4127 return wasm.addErrorWithNotesAssumeCapacity(note_count);
4128}
4129
4130pub fn addErrorWithoutNotes(wasm: *const Wasm, comptime fmt: []const u8, args: anytype) !void {
4131 const err = try wasm.addErrorWithNotes(0);
4132 try err.addMsg(wasm, fmt, args);
4133}
4134
4135fn addErrorWithNotesAssumeCapacity(wasm: *const Wasm, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
4136 const comp = wasm.base.comp;
4137 const gpa = comp.gpa;
4138 const index = comp.link_errors.items.len;
4139 const err = comp.link_errors.addOneAssumeCapacity();
4140 err.* = .{ .msg = undefined, .notes = try gpa.alloc(link.File.ErrorMsg, note_count) };
4141 return .{ .index = index };
4142}
src/link/Wasm/Object.zig+15-15
...@@ -235,27 +235,27 @@ fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?S...@@ -235,27 +235,27 @@ fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?S
235 if (object.imported_tables_count == table_count) return null;235 if (object.imported_tables_count == table_count) return null;
236236
237 if (table_count != 0) {237 if (table_count != 0) {
238 var err = try wasm_file.addErrorWithNotes(1);238 var err = try wasm_file.base.addErrorWithNotes(1);
239 try err.addMsg(wasm_file, "Expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{239 try err.addMsg("Expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
240 object.imported_tables_count,240 object.imported_tables_count,
241 table_count,241 table_count,
242 });242 });
243 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});243 try err.addNote("defined in '{s}'", .{object.path});
244 return error.MissingTableSymbols;244 return error.MissingTableSymbols;
245 }245 }
246246
247 // MVP object files cannot have any table definitions, only imports (for the indirect function table).247 // MVP object files cannot have any table definitions, only imports (for the indirect function table).
248 if (object.tables.len > 0) {248 if (object.tables.len > 0) {
249 var err = try wasm_file.addErrorWithNotes(1);249 var err = try wasm_file.base.addErrorWithNotes(1);
250 try err.addMsg(wasm_file, "Unexpected table definition without representing table symbols.", .{});250 try err.addMsg("Unexpected table definition without representing table symbols.", .{});
251 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});251 try err.addNote("defined in '{s}'", .{object.path});
252 return error.UnexpectedTable;252 return error.UnexpectedTable;
253 }253 }
254254
255 if (object.imported_tables_count != 1) {255 if (object.imported_tables_count != 1) {
256 var err = try wasm_file.addErrorWithNotes(1);256 var err = try wasm_file.base.addErrorWithNotes(1);
257 try err.addMsg(wasm_file, "Found more than one table import, but no representing table symbols", .{});257 try err.addMsg("Found more than one table import, but no representing table symbols", .{});
258 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});258 try err.addNote("defined in '{s}'", .{object.path});
259 return error.MissingTableSymbols;259 return error.MissingTableSymbols;
260 }260 }
261261
...@@ -266,9 +266,9 @@ fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?S...@@ -266,9 +266,9 @@ fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?S
266 } else unreachable;266 } else unreachable;
267267
268 if (!std.mem.eql(u8, object.string_table.get(table_import.name), "__indirect_function_table")) {268 if (!std.mem.eql(u8, object.string_table.get(table_import.name), "__indirect_function_table")) {
269 var err = try wasm_file.addErrorWithNotes(1);269 var err = try wasm_file.base.addErrorWithNotes(1);
270 try err.addMsg(wasm_file, "Non-indirect function table import '{s}' is missing a corresponding symbol", .{object.string_table.get(table_import.name)});270 try err.addMsg("Non-indirect function table import '{s}' is missing a corresponding symbol", .{object.string_table.get(table_import.name)});
271 try err.addNote(wasm_file, "defined in '{s}'", .{object.path});271 try err.addNote("defined in '{s}'", .{object.path});
272 return error.MissingTableSymbols;272 return error.MissingTableSymbols;
273 }273 }
274274
...@@ -596,9 +596,9 @@ fn Parser(comptime ReaderType: type) type {...@@ -596,9 +596,9 @@ fn Parser(comptime ReaderType: type) type {
596 try reader.readNoEof(name);596 try reader.readNoEof(name);
597597
598 const tag = types.known_features.get(name) orelse {598 const tag = types.known_features.get(name) orelse {
599 var err = try parser.wasm_file.addErrorWithNotes(1);599 var err = try parser.wasm_file.base.addErrorWithNotes(1);
600 try err.addMsg(parser.wasm_file, "Object file contains unknown feature: {s}", .{name});600 try err.addMsg("Object file contains unknown feature: {s}", .{name});
601 try err.addNote(parser.wasm_file, "defined in '{s}'", .{parser.object.path});601 try err.addNote("defined in '{s}'", .{parser.object.path});
602 return error.UnknownFeature;602 return error.UnknownFeature;
603 };603 };
604 feature.* = .{604 feature.* = .{
src/link/tapi.zig+9-3
...@@ -129,8 +129,8 @@ pub const Tbd = union(enum) {...@@ -129,8 +129,8 @@ pub const Tbd = union(enum) {
129129
130pub const TapiError = error{130pub const TapiError = error{
131 NotLibStub,131 NotLibStub,
132 FileTooBig,132 InputOutput,
133} || yaml.YamlError || std.fs.File.ReadError;133} || yaml.YamlError || std.fs.File.PReadError;
134134
135pub const LibStub = struct {135pub const LibStub = struct {
136 /// Underlying memory for stub's contents.136 /// Underlying memory for stub's contents.
...@@ -140,8 +140,14 @@ pub const LibStub = struct {...@@ -140,8 +140,14 @@ pub const LibStub = struct {
140 inner: []Tbd,140 inner: []Tbd,
141141
142 pub fn loadFromFile(allocator: Allocator, file: fs.File) TapiError!LibStub {142 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);
144 defer allocator.free(source);148 defer allocator.free(source);
149 const amt = try file.preadAll(source, 0);
150 if (amt != filesize) return error.InputOutput;
145151
146 var lib_stub = LibStub{152 var lib_stub = LibStub{
147 .yaml = try Yaml.load(allocator, source),153 .yaml = try Yaml.load(allocator, source),
src/main.zig+3
...@@ -211,6 +211,9 @@ fn verifyLibcxxCorrectlyLinked() void {...@@ -211,6 +211,9 @@ fn verifyLibcxxCorrectlyLinked() void {
211}211}
212212
213fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {213fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
214 const tr = tracy.trace(@src());
215 defer tr.end();
216
214 if (args.len <= 1) {217 if (args.len <= 1) {
215 std.log.info("{s}", .{usage});218 std.log.info("{s}", .{usage});
216 fatal("expected command argument", .{});219 fatal("expected command argument", .{});