From 101299e85625faf29b4afce07ad1e3522ea75421 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 4 Jul 2024 17:49:35 +0200 Subject: [PATCH 01/45] macho: move unwind info records ownership to Objects --- src/link/MachO.zig | 29 ++---- src/link/MachO/Archive.zig | 4 +- src/link/MachO/Atom.zig | 11 ++- src/link/MachO/Object.zig | 168 ++++++++++++++++++--------------- src/link/MachO/UnwindInfo.zig | 75 ++++++++------- src/link/MachO/dead_strip.zig | 8 +- src/link/MachO/relocatable.zig | 23 +++-- 7 files changed, 173 insertions(+), 145 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index ff083d367c..ed1a78c2ae 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -67,7 +67,6 @@ entry_index: ?Symbol.Index = null, atoms: std.ArrayListUnmanaged(Atom) = .{}, atoms_extra: std.ArrayListUnmanaged(u32) = .{}, thunks: std.ArrayListUnmanaged(Thunk) = .{}, -unwind_records: std.ArrayListUnmanaged(UnwindInfo.Record) = .{}, /// String interning table strings: StringTable = .{}, @@ -357,7 +356,6 @@ pub fn deinit(self: *MachO) void { thunk.deinit(gpa); } self.thunks.deinit(gpa); - self.unwind_records.deinit(gpa); } pub fn flush(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { @@ -982,12 +980,15 @@ fn parseObject(self: *MachO, path: []const u8) ParseError!void { break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000))); }; const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); - self.files.set(index, .{ .object = .{ - .path = try gpa.dupe(u8, path), - .file_handle = handle, - .mtime = mtime, - .index = index, - } }); + self.files.set(index, .{ + .object = .{ + .offset = 0, // TODO FAT objects + .path = try gpa.dupe(u8, path), + .file_handle = handle, + .mtime = mtime, + .index = index, + }, + }); try self.objects.append(gpa, index); const object = self.getFile(index).?.object; @@ -4058,18 +4059,6 @@ pub fn getGlobalByName(self: *MachO, name: []const u8) ?Symbol.Index { return self.globals.get(off); } -pub fn addUnwindRecord(self: *MachO) !UnwindInfo.Record.Index { - const index = @as(UnwindInfo.Record.Index, @intCast(self.unwind_records.items.len)); - const rec = try self.unwind_records.addOne(self.base.comp.gpa); - rec.* = .{}; - return index; -} - -pub fn getUnwindRecord(self: *MachO, index: UnwindInfo.Record.Index) *UnwindInfo.Record { - assert(index < self.unwind_records.items.len); - return &self.unwind_records.items[index]; -} - pub fn addThunk(self: *MachO) !Thunk.Index { const index = @as(Thunk.Index, @intCast(self.thunks.items.len)); const thunk = try self.thunks.addOne(self.base.comp.gpa); diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index c1ce582036..c478a9bef7 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -67,9 +67,9 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index: mem.eql(u8, name, SYMDEF64_SORTED)) continue; const object = Object{ - .archive = .{ + .offset = pos, + .in_archive = .{ .path = try gpa.dupe(u8, path), - .offset = pos, .size = hdr_size, }, .path = try gpa.dupe(u8, name), diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index 1a315330e3..c0a53da7e4 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -91,14 +91,16 @@ pub fn getUnwindRecords(self: Atom, macho_file: *MachO) []const UnwindInfo.Recor if (!self.flags.unwind) return &[0]UnwindInfo.Record.Index{}; const extra = self.getExtra(macho_file).?; return switch (self.getFile(macho_file)) { - .dylib, .zig_object, .internal => unreachable, - .object => |x| x.unwind_records.items[extra.unwind_index..][0..extra.unwind_count], + .dylib => unreachable, + .zig_object, .internal => &[0]UnwindInfo.Record.Index{}, + .object => |x| x.unwind_records_indexes.items[extra.unwind_index..][0..extra.unwind_count], }; } pub fn markUnwindRecordsDead(self: Atom, macho_file: *MachO) void { + const object = self.getFile(macho_file).object; for (self.getUnwindRecords(macho_file)) |cu_index| { - const cu = macho_file.getUnwindRecord(cu_index); + const cu = object.getUnwindRecord(cu_index); cu.alive = false; if (cu.getFdePtr(macho_file)) |fde| { @@ -1170,6 +1172,7 @@ fn format2( _ = unused_fmt_string; const atom = ctx.atom; const macho_file = ctx.macho_file; + const file = atom.getFile(macho_file); try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d})", .{ atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file), atom.out_n_sect, atom.alignment, atom.size, @@ -1181,7 +1184,7 @@ fn format2( try writer.writeAll(" : unwind{ "); const extra = atom.getExtra(macho_file).?; for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| { - const rec = macho_file.getUnwindRecord(index); + const rec = file.object.getUnwindRecord(index); try writer.print("{d}", .{index}); if (!rec.alive) try writer.writeAll("([*])"); if (i < extra.unwind_index + extra.unwind_count - 1) try writer.writeAll(", "); diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index c856c65d4e..8ecd88b413 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -1,8 +1,10 @@ -archive: ?InArchive = null, +/// Non-zero for fat object files or archives +offset: u64, path: []const u8, file_handle: File.HandleIndex, mtime: u64, index: File.Index, +in_archive: ?InArchive = null, header: ?macho.mach_header_64 = null, sections: std.MultiArrayList(Section) = .{}, @@ -21,7 +23,8 @@ compact_unwind_sect_index: ?u8 = null, cies: std.ArrayListUnmanaged(Cie) = .{}, fdes: std.ArrayListUnmanaged(Fde) = .{}, eh_frame_data: std.ArrayListUnmanaged(u8) = .{}, -unwind_records: std.ArrayListUnmanaged(UnwindInfo.Record.Index) = .{}, +unwind_records: std.ArrayListUnmanaged(UnwindInfo.Record) = .{}, +unwind_records_indexes: std.ArrayListUnmanaged(UnwindInfo.Record.Index) = .{}, data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, alive: bool = true, @@ -39,7 +42,7 @@ pub fn isObject(path: []const u8) !bool { } pub fn deinit(self: *Object, allocator: Allocator) void { - if (self.archive) |*ar| allocator.free(ar.path); + if (self.in_archive) |*ar| allocator.free(ar.path); allocator.free(self.path); for (self.sections.items(.relocs), self.sections.items(.subsections)) |*relocs, *sub| { relocs.deinit(allocator); @@ -54,6 +57,7 @@ pub fn deinit(self: *Object, allocator: Allocator) void { self.fdes.deinit(allocator); self.eh_frame_data.deinit(allocator); self.unwind_records.deinit(allocator); + self.unwind_records_indexes.deinit(allocator); for (self.stab_files.items) |*sf| { sf.stabs.deinit(allocator); } @@ -66,12 +70,11 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { defer tracy.end(); const gpa = macho_file.base.comp.gpa; - const offset = if (self.archive) |ar| ar.offset else 0; const handle = macho_file.getFileHandle(self.file_handle); var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined; { - const amt = try handle.preadAll(&header_buffer, offset); + const amt = try handle.preadAll(&header_buffer, self.offset); if (amt != @sizeOf(macho.mach_header_64)) return error.InputOutput; } self.header = @as(*align(1) const macho.mach_header_64, @ptrCast(&header_buffer)).*; @@ -92,7 +95,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { const lc_buffer = try gpa.alloc(u8, self.header.?.sizeofcmds); defer gpa.free(lc_buffer); { - const amt = try handle.preadAll(lc_buffer, offset + @sizeOf(macho.mach_header_64)); + const amt = try handle.preadAll(lc_buffer, self.offset + @sizeOf(macho.mach_header_64)); if (amt != self.header.?.sizeofcmds) return error.InputOutput; } @@ -119,14 +122,14 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { const cmd = lc.cast(macho.symtab_command).?; try self.strtab.resize(gpa, cmd.strsize); { - const amt = try handle.preadAll(self.strtab.items, cmd.stroff + offset); + const amt = try handle.preadAll(self.strtab.items, cmd.stroff + self.offset); if (amt != self.strtab.items.len) return error.InputOutput; } const symtab_buffer = try gpa.alloc(u8, cmd.nsyms * @sizeOf(macho.nlist_64)); defer gpa.free(symtab_buffer); { - const amt = try handle.preadAll(symtab_buffer, cmd.symoff + offset); + const amt = try handle.preadAll(symtab_buffer, cmd.symoff + self.offset); if (amt != symtab_buffer.len) return error.InputOutput; } const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(symtab_buffer.ptr))[0..cmd.nsyms]; @@ -144,7 +147,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { const buffer = try gpa.alloc(u8, cmd.datasize); defer gpa.free(buffer); { - const amt = try handle.preadAll(buffer, offset + cmd.dataoff); + const amt = try handle.preadAll(buffer, self.offset + cmd.dataoff); if (amt != buffer.len) return error.InputOutput; } const ndice = @divExact(cmd.datasize, @sizeOf(macho.data_in_code_entry)); @@ -218,11 +221,11 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { // Parse Apple's __LD,__compact_unwind section if (self.compact_unwind_sect_index) |index| { - try self.initUnwindRecords(index, macho_file); + try self.initUnwindRecords(gpa, index, handle, macho_file); } if (self.hasUnwindRecords() or self.hasEhFrameRecords()) { - try self.parseUnwindRecords(macho_file); + try self.parseUnwindRecords(gpa, macho_file.getTarget().cpu.arch, macho_file); } if (self.platform) |platform| { @@ -987,7 +990,7 @@ fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void { } } -fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void { +fn initUnwindRecords(self: *Object, allocator: Allocator, sect_id: u8, file: File.Handle, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); @@ -1003,19 +1006,22 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void { } }; - const gpa = macho_file.base.comp.gpa; - const data = try self.getSectionData(sect_id, macho_file); - defer gpa.free(data); + const header = self.sections.items(.header)[sect_id]; + const data = try allocator.alloc(u8, header.size); + defer allocator.free(data); + const amt = try file.preadAll(data, header.offset + self.offset); + if (amt != data.len) return error.InputOutput; + const nrecs = @divExact(data.len, @sizeOf(macho.compact_unwind_entry)); const recs = @as([*]align(1) const macho.compact_unwind_entry, @ptrCast(data.ptr))[0..nrecs]; const sym_lookup = SymbolLookup{ .ctx = self }; - try self.unwind_records.resize(gpa, nrecs); + try self.unwind_records.ensureTotalCapacityPrecise(allocator, nrecs); + try self.unwind_records_indexes.ensureTotalCapacityPrecise(allocator, nrecs); - const header = self.sections.items(.header)[sect_id]; const relocs = self.sections.items(.relocs)[sect_id].items; var reloc_idx: usize = 0; - for (recs, self.unwind_records.items, 0..) |rec, *out_index, rec_idx| { + for (recs, 0..) |rec, rec_idx| { const rec_start = rec_idx * @sizeOf(macho.compact_unwind_entry); const rec_end = rec_start + @sizeOf(macho.compact_unwind_entry); const reloc_start = reloc_idx; @@ -1023,11 +1029,11 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void { relocs[reloc_idx].offset < rec_end) : (reloc_idx += 1) {} - out_index.* = try macho_file.addUnwindRecord(); - const out = macho_file.getUnwindRecord(out_index.*); + const out_index = self.addUnwindRecordAssumeCapacity(); + self.unwind_records_indexes.appendAssumeCapacity(out_index); + const out = self.getUnwindRecord(out_index); out.length = rec.rangeLength; out.enc = .{ .enc = rec.compactUnwindEncoding }; - out.file = self.index; for (relocs[reloc_start..reloc_idx]) |rel| { if (rel.type != .unsigned or rel.meta.length != 3) { @@ -1090,7 +1096,7 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void { } } -fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void { +fn parseUnwindRecords(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch, macho_file: *MachO) !void { // Synthesise missing unwind records. // The logic here is as follows: // 1. if an atom has unwind info record that is not DWARF, FDE is marked dead @@ -1100,8 +1106,7 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void { const Superposition = struct { atom: Atom.Index, size: u64, cu: ?UnwindInfo.Record.Index = null, fde: ?Fde.Index = null }; - const gpa = macho_file.base.comp.gpa; - var superposition = std.AutoArrayHashMap(u64, Superposition).init(gpa); + var superposition = std.AutoArrayHashMap(u64, Superposition).init(allocator); defer superposition.deinit(); const slice = self.symtab.slice(); @@ -1119,8 +1124,8 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void { } } - for (self.unwind_records.items) |rec_index| { - const rec = macho_file.getUnwindRecord(rec_index); + for (self.unwind_records_indexes.items) |rec_index| { + const rec = self.getUnwindRecord(rec_index); const atom = rec.getAtom(macho_file); const addr = atom.getInputAddress(macho_file) + rec.atom_offset; superposition.getPtr(addr).?.cu = rec_index; @@ -1137,7 +1142,7 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void { const fde = &self.fdes.items[fde_index]; if (meta.cu) |rec_index| { - const rec = macho_file.getUnwindRecord(rec_index); + const rec = self.getUnwindRecord(rec_index); if (!rec.enc.isDwarf(macho_file)) { // Mark FDE dead fde.alive = false; @@ -1147,15 +1152,14 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void { } } else { // Synthesise new unwind info record - const rec_index = try macho_file.addUnwindRecord(); - const rec = macho_file.getUnwindRecord(rec_index); - try self.unwind_records.append(gpa, rec_index); + const rec_index = try self.addUnwindRecord(allocator); + const rec = self.getUnwindRecord(rec_index); + try self.unwind_records_indexes.append(allocator, rec_index); rec.length = @intCast(meta.size); rec.atom = fde.atom; rec.atom_offset = fde.atom_offset; rec.fde = fde_index; - rec.file = fde.file; - switch (macho_file.getTarget().cpu.arch) { + switch (cpu_arch) { .x86_64 => rec.enc.setMode(macho.UNWIND_X86_64_MODE.DWARF), .aarch64 => rec.enc.setMode(macho.UNWIND_ARM64_MODE.DWARF), else => unreachable, @@ -1163,10 +1167,10 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void { } } else if (meta.cu == null and meta.fde == null) { // Create a null record - const rec_index = try macho_file.addUnwindRecord(); - const rec = macho_file.getUnwindRecord(rec_index); + const rec_index = try self.addUnwindRecord(allocator); + const rec = self.getUnwindRecord(rec_index); const atom = macho_file.getAtom(meta.atom).?; - try self.unwind_records.append(gpa, rec_index); + try self.unwind_records_indexes.append(allocator, rec_index); rec.length = @intCast(meta.size); rec.atom = meta.atom; rec.atom_offset = @intCast(addr - atom.getInputAddress(macho_file)); @@ -1174,25 +1178,31 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void { } } - const sortFn = struct { - fn sortFn(ctx: *MachO, lhs_index: UnwindInfo.Record.Index, rhs_index: UnwindInfo.Record.Index) bool { - const lhs = ctx.getUnwindRecord(lhs_index); - const rhs = ctx.getUnwindRecord(rhs_index); - const lhsa = lhs.getAtom(ctx); - const rhsa = rhs.getAtom(ctx); - return lhsa.getInputAddress(ctx) + lhs.atom_offset < rhsa.getInputAddress(ctx) + rhs.atom_offset; + const SortCtx = struct { + object: *Object, + mfile: *MachO, + + fn sort(ctx: @This(), lhs_index: UnwindInfo.Record.Index, rhs_index: UnwindInfo.Record.Index) bool { + const lhs = ctx.object.getUnwindRecord(lhs_index); + const rhs = ctx.object.getUnwindRecord(rhs_index); + const lhsa = lhs.getAtom(ctx.mfile); + const rhsa = rhs.getAtom(ctx.mfile); + return lhsa.getInputAddress(ctx.mfile) + lhs.atom_offset < rhsa.getInputAddress(ctx.mfile) + rhs.atom_offset; } - }.sortFn; - mem.sort(UnwindInfo.Record.Index, self.unwind_records.items, macho_file, sortFn); + }; + mem.sort(UnwindInfo.Record.Index, self.unwind_records_indexes.items, SortCtx{ + .object = self, + .mfile = macho_file, + }, SortCtx.sort); // Associate unwind records to atoms var next_cu: u32 = 0; - while (next_cu < self.unwind_records.items.len) { + while (next_cu < self.unwind_records_indexes.items.len) { const start = next_cu; - const rec_index = self.unwind_records.items[start]; - const rec = macho_file.getUnwindRecord(rec_index); - while (next_cu < self.unwind_records.items.len and - macho_file.getUnwindRecord(self.unwind_records.items[next_cu]).atom == rec.atom) : (next_cu += 1) + const rec_index = self.unwind_records_indexes.items[start]; + const rec = self.getUnwindRecord(rec_index); + while (next_cu < self.unwind_records_indexes.items.len and + self.getUnwindRecord(self.unwind_records_indexes.items[next_cu]).atom == rec.atom) : (next_cu += 1) {} const atom = rec.getAtom(macho_file); @@ -1441,7 +1451,7 @@ pub fn checkDuplicates(self: *Object, dupes: anytype, macho_file: *MachO) error{ } } -pub fn scanRelocs(self: Object, macho_file: *MachO) !void { +pub fn scanRelocs(self: *Object, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); @@ -1453,8 +1463,8 @@ pub fn scanRelocs(self: Object, macho_file: *MachO) !void { try atom.scanRelocs(macho_file); } - for (self.unwind_records.items) |rec_index| { - const rec = macho_file.getUnwindRecord(rec_index); + for (self.unwind_records_indexes.items) |rec_index| { + const rec = self.getUnwindRecord(rec_index); if (!rec.alive) continue; if (rec.getFde(macho_file)) |fde| { if (fde.getCie(macho_file).getPersonality(macho_file)) |sym| { @@ -1532,12 +1542,11 @@ pub fn parseAr(self: *Object, macho_file: *MachO) !void { defer tracy.end(); const gpa = macho_file.base.comp.gpa; - const offset = if (self.archive) |ar| ar.offset else 0; const handle = macho_file.getFileHandle(self.file_handle); var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined; { - const amt = try handle.preadAll(&header_buffer, offset); + const amt = try handle.preadAll(&header_buffer, self.offset); if (amt != @sizeOf(macho.mach_header_64)) return error.InputOutput; } self.header = @as(*align(1) const macho.mach_header_64, @ptrCast(&header_buffer)).*; @@ -1558,7 +1567,7 @@ pub fn parseAr(self: *Object, macho_file: *MachO) !void { const lc_buffer = try gpa.alloc(u8, self.header.?.sizeofcmds); defer gpa.free(lc_buffer); { - const amt = try handle.preadAll(lc_buffer, offset + @sizeOf(macho.mach_header_64)); + const amt = try handle.preadAll(lc_buffer, self.offset + @sizeOf(macho.mach_header_64)); if (amt != self.header.?.sizeofcmds) return error.InputOutput; } @@ -1571,14 +1580,14 @@ pub fn parseAr(self: *Object, macho_file: *MachO) !void { const cmd = lc.cast(macho.symtab_command).?; try self.strtab.resize(gpa, cmd.strsize); { - const amt = try handle.preadAll(self.strtab.items, cmd.stroff + offset); + const amt = try handle.preadAll(self.strtab.items, cmd.stroff + self.offset); if (amt != self.strtab.items.len) return error.InputOutput; } const symtab_buffer = try gpa.alloc(u8, cmd.nsyms * @sizeOf(macho.nlist_64)); defer gpa.free(symtab_buffer); { - const amt = try handle.preadAll(symtab_buffer, cmd.symoff + offset); + const amt = try handle.preadAll(symtab_buffer, cmd.symoff + self.offset); if (amt != symtab_buffer.len) return error.InputOutput; } const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(symtab_buffer.ptr))[0..cmd.nsyms]; @@ -1613,7 +1622,7 @@ pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, macho_file: *M } pub fn updateArSize(self: *Object, macho_file: *MachO) !void { - self.output_ar_state.size = if (self.archive) |ar| ar.size else size: { + self.output_ar_state.size = if (self.in_archive) |ar| ar.size else size: { const file = macho_file.getFileHandle(self.file_handle); break :size (try file.stat()).size; }; @@ -1622,7 +1631,6 @@ pub fn updateArSize(self: *Object, macho_file: *MachO) !void { pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void { // Header const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow; - const offset: u64 = if (self.archive) |ar| ar.offset else 0; try Archive.writeHeader(self.path, size, ar_format, writer); // Data const file = macho_file.getFileHandle(self.file_handle); @@ -1630,7 +1638,7 @@ pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writ const gpa = macho_file.base.comp.gpa; const data = try gpa.alloc(u8, size); defer gpa.free(data); - const amt = try file.preadAll(data, offset); + const amt = try file.preadAll(data, self.offset); if (amt != size) return error.InputOutput; try writer.writeAll(data); } @@ -1680,7 +1688,7 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void { self.output_symtab_ctx.strsize += @as(u32, @intCast(comp_dir.len + 1)); // comp_dir self.output_symtab_ctx.strsize += @as(u32, @intCast(tu_name.len + 1)); // tu_name - if (self.archive) |ar| { + if (self.in_archive) |ar| { self.output_symtab_ctx.strsize += @as(u32, @intCast(ar.path.len + 1 + self.path.len + 1 + 1)); } else { self.output_symtab_ctx.strsize += @as(u32, @intCast(self.path.len + 1)); @@ -1820,7 +1828,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O index += 1; // N_OSO path n_strx = @as(u32, @intCast(ctx.strtab.items.len)); - if (self.archive) |ar| { + if (self.in_archive) |ar| { ctx.strtab.appendSliceAssumeCapacity(ar.path); ctx.strtab.appendAssumeCapacity('('); ctx.strtab.appendSliceAssumeCapacity(self.path); @@ -1989,11 +1997,10 @@ fn getSectionData(self: *const Object, index: u32, macho_file: *MachO) ![]u8 { assert(index < slice.items(.header).len); const sect = slice.items(.header)[index]; const handle = macho_file.getFileHandle(self.file_handle); - const offset = if (self.archive) |ar| ar.offset else 0; const size = math.cast(usize, sect.size) orelse return error.Overflow; const buffer = try gpa.alloc(u8, size); errdefer gpa.free(buffer); - const amt = try handle.preadAll(buffer, sect.offset + offset); + const amt = try handle.preadAll(buffer, sect.offset + self.offset); if (amt != buffer.len) return error.InputOutput; return buffer; } @@ -2002,9 +2009,8 @@ pub fn getAtomData(self: *const Object, macho_file: *MachO, atom: Atom, buffer: assert(buffer.len == atom.size); const slice = self.sections.slice(); const handle = macho_file.getFileHandle(self.file_handle); - const offset = if (self.archive) |ar| ar.offset else 0; const sect = slice.items(.header)[atom.n_sect]; - const amt = try handle.preadAll(buffer, sect.offset + offset + atom.off); + const amt = try handle.preadAll(buffer, sect.offset + self.offset + atom.off); if (amt != buffer.len) return error.InputOutput; } @@ -2068,6 +2074,23 @@ pub fn asFile(self: *Object) File { return .{ .object = self }; } +fn addUnwindRecord(self: *Object, allocator: Allocator) !UnwindInfo.Record.Index { + try self.unwind_records.ensureUnusedCapacity(allocator, 1); + return self.addUnwindRecordAssumeCapacity(); +} + +fn addUnwindRecordAssumeCapacity(self: *Object) UnwindInfo.Record.Index { + const index = @as(UnwindInfo.Record.Index, @intCast(self.unwind_records.items.len)); + const rec = self.unwind_records.addOneAssumeCapacity(); + rec.* = .{ .file = self.index }; + return index; +} + +pub fn getUnwindRecord(self: *Object, index: UnwindInfo.Record.Index) *UnwindInfo.Record { + assert(index < self.unwind_records.items.len); + return &self.unwind_records.items[index]; +} + pub fn format( self: *Object, comptime unused_fmt_string: []const u8, @@ -2171,8 +2194,8 @@ fn formatUnwindRecords( const object = ctx.object; const macho_file = ctx.macho_file; try writer.writeAll(" unwind records\n"); - for (object.unwind_records.items) |rec| { - try writer.print(" rec({d}) : {}\n", .{ rec, macho_file.getUnwindRecord(rec).fmt(macho_file) }); + for (object.unwind_records_indexes.items) |rec| { + try writer.print(" rec({d}) : {}\n", .{ rec, object.getUnwindRecord(rec).fmt(macho_file) }); } } @@ -2211,7 +2234,7 @@ fn formatPath( ) !void { _ = unused_fmt_string; _ = options; - if (object.archive) |ar| { + if (object.in_archive) |ar| { try writer.writeAll(ar.path); try writer.writeByte('('); try writer.writeAll(object.path); @@ -2285,7 +2308,6 @@ const CompileUnit = struct { const InArchive = struct { path: []const u8, - offset: u64, size: u32, }; @@ -2300,11 +2322,10 @@ const x86_64 = struct { const gpa = macho_file.base.comp.gpa; const handle = macho_file.getFileHandle(self.file_handle); - const offset = if (self.archive) |ar| ar.offset else 0; const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info)); defer gpa.free(relocs_buffer); { - const amt = try handle.preadAll(relocs_buffer, sect.reloff + offset); + const amt = try handle.preadAll(relocs_buffer, sect.reloff + self.offset); if (amt != relocs_buffer.len) return error.InputOutput; } const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc]; @@ -2463,11 +2484,10 @@ const aarch64 = struct { const gpa = macho_file.base.comp.gpa; const handle = macho_file.getFileHandle(self.file_handle); - const offset = if (self.archive) |ar| ar.offset else 0; const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info)); defer gpa.free(relocs_buffer); { - const amt = try handle.preadAll(relocs_buffer, sect.reloff + offset); + const amt = try handle.preadAll(relocs_buffer, sect.reloff + self.offset); if (amt != relocs_buffer.len) return error.InputOutput; } const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc]; diff --git a/src/link/MachO/UnwindInfo.zig b/src/link/MachO/UnwindInfo.zig index 44bb9bfab1..252333d5bb 100644 --- a/src/link/MachO/UnwindInfo.zig +++ b/src/link/MachO/UnwindInfo.zig @@ -1,6 +1,6 @@ /// List of all unwind records gathered from all objects and sorted /// by allocated relative function address within the section. -records: std.ArrayListUnmanaged(Record.Index) = .{}, +records: std.ArrayListUnmanaged(Record.Ref) = .{}, /// List of all personalities referenced by either unwind info entries /// or __eh_frame entries. @@ -25,10 +25,10 @@ pub fn deinit(info: *UnwindInfo, allocator: Allocator) void { info.lsdas_lookup.deinit(allocator); } -fn canFold(macho_file: *MachO, lhs_index: Record.Index, rhs_index: Record.Index) bool { +fn canFold(macho_file: *MachO, lhs_ref: Record.Ref, rhs_ref: Record.Ref) bool { const cpu_arch = macho_file.getTarget().cpu.arch; - const lhs = macho_file.getUnwindRecord(lhs_index); - const rhs = macho_file.getUnwindRecord(rhs_index); + const lhs = lhs_ref.getUnwindRecord(macho_file); + const rhs = rhs_ref.getUnwindRecord(macho_file); if (cpu_arch == .x86_64) { if (lhs.enc.getMode() == @intFromEnum(macho.UNWIND_X86_64_MODE.STACK_IND) or rhs.enc.getMode() == @intFromEnum(macho.UNWIND_X86_64_MODE.STACK_IND)) return false; @@ -52,17 +52,18 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void { const atom = macho_file.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; const recs = atom.getUnwindRecords(macho_file); + const file = atom.getFile(macho_file); try info.records.ensureUnusedCapacity(gpa, recs.len); for (recs) |rec| { - if (!macho_file.getUnwindRecord(rec).alive) continue; - info.records.appendAssumeCapacity(rec); + if (!file.object.getUnwindRecord(rec).alive) continue; + info.records.appendAssumeCapacity(.{ .record = rec, .file = file.getIndex() }); } } } // Encode records - for (info.records.items) |index| { - const rec = macho_file.getUnwindRecord(index); + for (info.records.items) |ref| { + const rec = ref.getUnwindRecord(macho_file); if (rec.getFde(macho_file)) |fde| { rec.enc.setDwarfSectionOffset(@intCast(fde.out_offset)); if (fde.getLsdaAtom(macho_file)) |lsda| { @@ -83,16 +84,16 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void { // Sort by assigned relative address within each output section const sortFn = struct { - fn sortFn(ctx: *MachO, lhs_index: Record.Index, rhs_index: Record.Index) bool { - const lhs = ctx.getUnwindRecord(lhs_index); - const rhs = ctx.getUnwindRecord(rhs_index); + fn sortFn(ctx: *MachO, lhs_ref: Record.Ref, rhs_ref: Record.Ref) bool { + const lhs = lhs_ref.getUnwindRecord(ctx); + const rhs = rhs_ref.getUnwindRecord(ctx); const lhsa = lhs.getAtom(ctx); const rhsa = rhs.getAtom(ctx); if (lhsa.out_n_sect == rhsa.out_n_sect) return lhs.getAtomAddress(ctx) < rhs.getAtomAddress(ctx); return lhsa.out_n_sect < rhsa.out_n_sect; } }.sortFn; - mem.sort(Record.Index, info.records.items, macho_file, sortFn); + mem.sort(Record.Ref, info.records.items, macho_file, sortFn); // Fold the records // Any adjacent two records that share encoding can be folded into one. @@ -101,8 +102,8 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void { var j: usize = 1; while (j < info.records.items.len) : (j += 1) { if (canFold(macho_file, info.records.items[i], info.records.items[j])) { - const rec = macho_file.getUnwindRecord(info.records.items[i]); - rec.length += macho_file.getUnwindRecord(info.records.items[j]).length + 1; + const rec = info.records.items[i].getUnwindRecord(macho_file); + rec.length += info.records.items[j].getUnwindRecord(macho_file).length + 1; } else { i += 1; info.records.items[i] = info.records.items[j]; @@ -111,14 +112,15 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void { info.records.shrinkAndFree(gpa, i + 1); } - for (info.records.items) |rec_index| { - const rec = macho_file.getUnwindRecord(rec_index); + for (info.records.items) |ref| { + const rec = ref.getUnwindRecord(macho_file); const atom = rec.getAtom(macho_file); - log.debug("@{x}-{x} : {s} : rec({d}) : {}", .{ + log.debug("@{x}-{x} : {s} : rec({d}) : object({d}) : {}", .{ rec.getAtomAddress(macho_file), rec.getAtomAddress(macho_file) + rec.length, atom.getName(macho_file), - rec_index, + ref.record, + ref.file, rec.enc, }); } @@ -161,8 +163,8 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void { ).init(gpa); defer common_encodings_counts.deinit(); - for (info.records.items) |rec_index| { - const rec = macho_file.getUnwindRecord(rec_index); + for (info.records.items) |ref| { + const rec = ref.getUnwindRecord(macho_file); if (rec.enc.isDwarf(macho_file)) continue; const gop = try common_encodings_counts.getOrPut(rec.enc); if (!gop.found_existing) { @@ -190,7 +192,7 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void { { var i: u32 = 0; while (i < info.records.items.len) { - const rec = macho_file.getUnwindRecord(info.records.items[i]); + const rec = info.records.items[i].getUnwindRecord(macho_file); const range_start_max: u64 = rec.getAtomAddress(macho_file) + compressed_entry_func_offset_mask; var encoding_count: u9 = info.common_encodings_count; var space_left: u32 = second_level_page_words - @@ -202,7 +204,7 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void { }; while (space_left >= 1 and i < info.records.items.len) { - const next = macho_file.getUnwindRecord(info.records.items[i]); + const next = info.records.items[i].getUnwindRecord(macho_file); const is_dwarf = next.enc.isDwarf(macho_file); if (next.getAtomAddress(macho_file) >= range_start_max) { @@ -244,8 +246,8 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void { // Save records having an LSDA pointer log.debug("LSDA pointers:", .{}); try info.lsdas_lookup.ensureTotalCapacityPrecise(gpa, info.records.items.len); - for (info.records.items, 0..) |index, i| { - const rec = macho_file.getUnwindRecord(index); + for (info.records.items, 0..) |ref, i| { + const rec = ref.getUnwindRecord(macho_file); info.lsdas_lookup.appendAssumeCapacity(@intCast(info.lsdas.items.len)); if (rec.getLsdaAtom(macho_file)) |lsda| { log.debug(" @{x} => lsda({d})", .{ rec.getAtomAddress(macho_file), lsda.atom_index }); @@ -301,7 +303,7 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void { (info.lsdas.items.len * @sizeOf(macho.unwind_info_section_header_lsda_index_entry)))); for (info.pages.items, 0..) |page, i| { assert(page.count > 0); - const rec = macho_file.getUnwindRecord(info.records.items[page.start]); + const rec = info.records.items[page.start].getUnwindRecord(macho_file); try writer.writeStruct(macho.unwind_info_section_header_index_entry{ .functionOffset = @as(u32, @intCast(rec.getAtomAddress(macho_file) - seg.vmaddr)), .secondLevelPagesSectionOffset = @as(u32, @intCast(pages_base_offset + i * second_level_page_bytes)), @@ -310,7 +312,7 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void { }); } - const last_rec = macho_file.getUnwindRecord(info.records.items[info.records.items.len - 1]); + const last_rec = info.records.items[info.records.items.len - 1].getUnwindRecord(macho_file); const sentinel_address = @as(u32, @intCast(last_rec.getAtomAddress(macho_file) + last_rec.length - seg.vmaddr)); try writer.writeStruct(macho.unwind_info_section_header_index_entry{ .functionOffset = sentinel_address, @@ -320,7 +322,7 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void { }); for (info.lsdas.items) |index| { - const rec = macho_file.getUnwindRecord(info.records.items[index]); + const rec = info.records.items[index].getUnwindRecord(macho_file); try writer.writeStruct(macho.unwind_info_section_header_lsda_index_entry{ .functionOffset = @as(u32, @intCast(rec.getAtomAddress(macho_file) - seg.vmaddr)), .lsdaOffset = @as(u32, @intCast(rec.getLsdaAddress(macho_file) - seg.vmaddr)), @@ -537,6 +539,15 @@ pub const Record = struct { } pub const Index = u32; + + const Ref = struct { + record: Index, + file: File.Index, + + pub fn getUnwindRecord(ref: Ref, macho_file: *MachO) *Record { + return macho_file.getFile(ref.file).?.object.getUnwindRecord(ref.record); + } + }; }; const max_personalities = 3; @@ -635,8 +646,8 @@ const Page = struct { .entryCount = page.count, }); - for (info.records.items[page.start..][0..page.count]) |index| { - const rec = macho_file.getUnwindRecord(index); + for (info.records.items[page.start..][0..page.count]) |ref| { + const rec = ref.getUnwindRecord(macho_file); try writer.writeStruct(macho.unwind_info_regular_second_level_entry{ .functionOffset = @as(u32, @intCast(rec.getAtomAddress(macho_file) - seg.vmaddr)), .encoding = rec.enc.enc, @@ -658,9 +669,9 @@ const Page = struct { } assert(page.count > 0); - const first_rec = macho_file.getUnwindRecord(info.records.items[page.start]); - for (info.records.items[page.start..][0..page.count]) |index| { - const rec = macho_file.getUnwindRecord(index); + const first_rec = info.records.items[page.start].getUnwindRecord(macho_file); + for (info.records.items[page.start..][0..page.count]) |ref| { + const rec = ref.getUnwindRecord(macho_file); const enc_index = blk: { if (info.getCommonEncoding(rec.enc)) |id| break :blk id; const ncommon = info.common_encodings_count; diff --git a/src/link/MachO/dead_strip.zig b/src/link/MachO/dead_strip.zig index e91682ca58..84e56c9249 100644 --- a/src/link/MachO/dead_strip.zig +++ b/src/link/MachO/dead_strip.zig @@ -41,8 +41,9 @@ fn collectRoots(roots: *std.ArrayList(*Atom), objects: []const File.Index, macho } for (macho_file.objects.items) |index| { - for (macho_file.getFile(index).?.object.unwind_records.items) |cu_index| { - const cu = macho_file.getUnwindRecord(cu_index); + const object = macho_file.getFile(index).?.object; + for (object.unwind_records_indexes.items) |cu_index| { + const cu = object.getUnwindRecord(cu_index); if (!cu.alive) continue; if (cu.getFde(macho_file)) |fde| { if (fde.getCie(macho_file).getPersonality(macho_file)) |sym| try markSymbol(sym, roots, macho_file); @@ -127,8 +128,9 @@ fn markLive(atom: *Atom, macho_file: *MachO) void { } } + const file = atom.getFile(macho_file); for (atom.getUnwindRecords(macho_file)) |cu_index| { - const cu = macho_file.getUnwindRecord(cu_index); + const cu = file.object.getUnwindRecord(cu_index); const cu_atom = cu.getAtom(macho_file); if (markAtom(cu_atom)) markLive(cu_atom, macho_file); diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index 3fc37ef9ac..a2979eafe0 100644 --- a/src/link/MachO/relocatable.zig +++ b/src/link/MachO/relocatable.zig @@ -286,12 +286,15 @@ fn parseObject(macho_file: *MachO, path: []const u8) MachO.ParseError!void { break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000))); }; const index = @as(File.Index, @intCast(try macho_file.files.addOne(gpa))); - macho_file.files.set(index, .{ .object = .{ - .path = try gpa.dupe(u8, path), - .file_handle = handle, - .mtime = mtime, - .index = index, - } }); + macho_file.files.set(index, .{ + .object = .{ + .offset = 0, // TODO FAT objects + .path = try gpa.dupe(u8, path), + .file_handle = handle, + .mtime = mtime, + .index = index, + }, + }); try macho_file.objects.append(gpa, index); const object = macho_file.getFile(index).?.object; @@ -420,8 +423,8 @@ fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void { for (macho_file.objects.items) |index| { const object = macho_file.getFile(index).?.object; - for (object.unwind_records.items) |irec| { - const rec = macho_file.getUnwindRecord(irec); + for (object.unwind_records_indexes.items) |irec| { + const rec = object.getUnwindRecord(irec); if (!rec.alive) continue; size += @sizeOf(macho.compact_unwind_entry); nreloc += 1; @@ -670,8 +673,8 @@ fn writeCompactUnwind(macho_file: *MachO) !void { var offset: i32 = 0; for (macho_file.objects.items) |index| { const object = macho_file.getFile(index).?.object; - for (object.unwind_records.items) |irec| { - const rec = macho_file.getUnwindRecord(irec); + for (object.unwind_records_indexes.items) |irec| { + const rec = object.getUnwindRecord(irec); if (!rec.alive) continue; var out: macho.compact_unwind_entry = .{ From e2bfd6fc691a92f9dc36597a8febb03293b0f5ad Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 5 Jul 2024 08:16:23 +0200 Subject: [PATCH 02/45] macho: revamp how we compute dyld relocs --- src/link/MachO.zig | 130 +---- src/link/MachO/Atom.zig | 37 +- src/link/MachO/Object.zig | 1 - src/link/MachO/ZigObject.zig | 1 - src/link/MachO/dyld_info/Rebase.zig | 115 ++++- src/link/MachO/dyld_info/Trie.zig | 775 +++++++++++----------------- src/link/MachO/dyld_info/bind.zig | 265 +++++++++- src/link/MachO/synthetic.zig | 138 ----- 8 files changed, 655 insertions(+), 807 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index ed1a78c2ae..b557eb350e 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -82,11 +82,11 @@ stubs_helper: StubsHelperSection = .{}, objc_stubs: ObjcStubsSection = .{}, la_symbol_ptr: LaSymbolPtrSection = .{}, tlv_ptr: TlvPtrSection = .{}, -rebase: RebaseSection = .{}, -bind: BindSection = .{}, -weak_bind: WeakBindSection = .{}, -lazy_bind: LazyBindSection = .{}, -export_trie: ExportTrieSection = .{}, +rebase: Rebase = .{}, +bind: Bind = .{}, +weak_bind: WeakBind = .{}, +lazy_bind: LazyBind = .{}, +export_trie: ExportTrie = .{}, unwind_info: UnwindInfo = .{}, /// Tracked loadable segments during incremental linking. @@ -590,8 +590,6 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n state_log.debug("{}", .{self.dumpState()}); } - try self.initDyldInfoSections(); - // Beyond this point, everything has been allocated a virtual address and we can resolve // the relocations, and commit objects to file. if (self.getZigObject()) |zo| { @@ -2500,87 +2498,6 @@ fn allocateLinkeditSegment(self: *MachO) !void { seg.fileoff = mem.alignForward(u64, fileoff, page_size); } -fn initDyldInfoSections(self: *MachO) !void { - const tracy = trace(@src()); - defer tracy.end(); - - const gpa = self.base.comp.gpa; - - if (self.zig_got_sect_index != null) try self.zig_got.addDyldRelocs(self); - if (self.got_sect_index != null) try self.got.addDyldRelocs(self); - if (self.tlv_ptr_sect_index != null) try self.tlv_ptr.addDyldRelocs(self); - if (self.la_symbol_ptr_sect_index != null) try self.la_symbol_ptr.addDyldRelocs(self); - try self.initExportTrie(); - - var objects = try std.ArrayList(File.Index).initCapacity(gpa, self.objects.items.len + 1); - defer objects.deinit(); - if (self.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index); - objects.appendSliceAssumeCapacity(self.objects.items); - - var nrebases: usize = 0; - var nbinds: usize = 0; - var nweak_binds: usize = 0; - for (objects.items) |index| { - const ctx = switch (self.getFile(index).?) { - .zig_object => |x| x.dynamic_relocs, - .object => |x| x.dynamic_relocs, - else => unreachable, - }; - nrebases += ctx.rebase_relocs; - nbinds += ctx.bind_relocs; - nweak_binds += ctx.weak_bind_relocs; - } - if (self.getInternalObject()) |int| { - nrebases += int.num_rebase_relocs; - } - try self.rebase.entries.ensureUnusedCapacity(gpa, nrebases); - try self.bind.entries.ensureUnusedCapacity(gpa, nbinds); - try self.weak_bind.entries.ensureUnusedCapacity(gpa, nweak_binds); -} - -fn initExportTrie(self: *MachO) !void { - const tracy = trace(@src()); - defer tracy.end(); - - const gpa = self.base.comp.gpa; - try self.export_trie.init(gpa); - - const seg = self.getTextSegment(); - for (self.objects.items) |index| { - for (self.getFile(index).?.getSymbols()) |sym_index| { - const sym = self.getSymbol(sym_index); - if (!sym.flags.@"export") continue; - if (sym.getAtom(self)) |atom| if (!atom.flags.alive) continue; - if (sym.getFile(self).?.getIndex() != index) continue; - var flags: u64 = if (sym.flags.abs) - macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE - else if (sym.flags.tlv) - macho.EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL - else - macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR; - if (sym.flags.weak) { - flags |= macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; - self.weak_defines = true; - self.binds_to_weak = true; - } - try self.export_trie.put(gpa, .{ - .name = sym.getName(self), - .vmaddr_offset = sym.getAddress(.{ .stubs = false }, self) - seg.vmaddr, - .export_flags = flags, - }); - } - } - - if (self.mh_execute_header_index) |index| { - const sym = self.getSymbol(index); - try self.export_trie.put(gpa, .{ - .name = sym.getName(self), - .vmaddr_offset = sym.getAddress(.{}, self) - seg.vmaddr, - .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR, - }); - } -} - fn writeAtoms(self: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); @@ -2659,13 +2576,13 @@ fn writeUnwindInfo(self: *MachO) !void { fn finalizeDyldInfoSections(self: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - const gpa = self.base.comp.gpa; - - try self.rebase.finalize(gpa); - try self.bind.finalize(gpa, self); - try self.weak_bind.finalize(gpa, self); - try self.lazy_bind.finalize(gpa, self); - try self.export_trie.finalize(gpa); + try self.rebase.updateSize(self); + try self.bind.updateSize(self); + try self.weak_bind.updateSize(self); + if (self.la_symbol_ptr_sect_index) |_| { + try self.lazy_bind.updateSize(self); + } + try self.export_trie.updateSize(self); } fn writeSyntheticSections(self: *MachO) !void { @@ -2742,25 +2659,14 @@ fn writeDyldInfoSections(self: *MachO, off: u32) !u32 { const gpa = self.base.comp.gpa; const cmd = &self.dyld_info_cmd; var needed_size: u32 = 0; - - cmd.rebase_off = needed_size; - cmd.rebase_size = mem.alignForward(u32, @intCast(self.rebase.size()), @alignOf(u64)); needed_size += cmd.rebase_size; - cmd.bind_off = needed_size; - cmd.bind_size = mem.alignForward(u32, @intCast(self.bind.size()), @alignOf(u64)); needed_size += cmd.bind_size; - cmd.weak_bind_off = needed_size; - cmd.weak_bind_size = mem.alignForward(u32, @intCast(self.weak_bind.size()), @alignOf(u64)); needed_size += cmd.weak_bind_size; - cmd.lazy_bind_off = needed_size; - cmd.lazy_bind_size = mem.alignForward(u32, @intCast(self.lazy_bind.size()), @alignOf(u64)); needed_size += cmd.lazy_bind_size; - cmd.export_off = needed_size; - cmd.export_size = mem.alignForward(u32, @intCast(self.export_trie.size), @alignOf(u64)); needed_size += cmd.export_size; const buffer = try gpa.alloc(u8, needed_size); @@ -2785,7 +2691,6 @@ fn writeDyldInfoSections(self: *MachO, off: u32) !u32 { cmd.weak_bind_off += off; cmd.lazy_bind_off += off; cmd.export_off += off; - try self.base.file.?.pwriteAll(buffer, off); return off + needed_size; @@ -4831,6 +4736,7 @@ const mem = std.mem; const meta = std.meta; const aarch64 = @import("../arch/aarch64/bits.zig"); +const bind = @import("MachO/dyld_info/bind.zig"); const calcUuid = @import("MachO/uuid.zig").calcUuid; const codegen = @import("../codegen.zig"); const dead_strip = @import("MachO/dead_strip.zig"); @@ -4851,13 +4757,13 @@ const Alignment = Atom.Alignment; const Allocator = mem.Allocator; const Archive = @import("MachO/Archive.zig"); pub const Atom = @import("MachO/Atom.zig"); -const BindSection = synthetic.BindSection; +const Bind = bind.Bind; const Cache = std.Build.Cache; const CodeSignature = @import("MachO/CodeSignature.zig"); const Compilation = @import("../Compilation.zig"); pub const DebugSymbols = @import("MachO/DebugSymbols.zig"); const Dylib = @import("MachO/Dylib.zig"); -const ExportTrieSection = synthetic.ExportTrieSection; +const ExportTrie = @import("MachO/dyld_info/Trie.zig"); const File = @import("MachO/file.zig").File; const GotSection = synthetic.GotSection; const Hash = std.hash.Wyhash; @@ -4865,7 +4771,7 @@ const Indsymtab = synthetic.Indsymtab; const InternalObject = @import("MachO/InternalObject.zig"); const ObjcStubsSection = synthetic.ObjcStubsSection; const Object = @import("MachO/Object.zig"); -const LazyBindSection = synthetic.LazyBindSection; +const LazyBind = bind.LazyBind; const LaSymbolPtrSection = synthetic.LaSymbolPtrSection; const LibStub = tapi.LibStub; const Liveness = @import("../Liveness.zig"); @@ -4875,7 +4781,7 @@ const Zcu = @import("../Zcu.zig"); /// Deprecated. const Module = Zcu; const InternPool = @import("../InternPool.zig"); -const RebaseSection = synthetic.RebaseSection; +const Rebase = @import("MachO/dyld_info/Rebase.zig"); pub const Relocation = @import("MachO/Relocation.zig"); const StringTable = @import("StringTable.zig"); const StubsSection = synthetic.StubsSection; @@ -4885,6 +4791,6 @@ const Thunk = thunks.Thunk; const TlvPtrSection = synthetic.TlvPtrSection; const Value = @import("../Value.zig"); const UnwindInfo = @import("MachO/UnwindInfo.zig"); -const WeakBindSection = synthetic.WeakBindSection; +const WeakBind = bind.WeakBind; const ZigGotSection = synthetic.ZigGotSection; const ZigObject = @import("MachO/ZigObject.zig"); diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index c0a53da7e4..c7a65843bb 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -460,11 +460,6 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void { defer tracy.end(); assert(self.flags.alive); - const dynrel_ctx = switch (self.getFile(macho_file)) { - .zig_object => |x| &x.dynamic_relocs, - .object => |x| &x.dynamic_relocs, - else => unreachable, - }; const relocs = self.getRelocs(macho_file); for (relocs) |rel| { @@ -537,21 +532,15 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void { continue; } if (symbol.flags.import) { - dynrel_ctx.bind_relocs += 1; if (symbol.flags.weak) { - dynrel_ctx.weak_bind_relocs += 1; macho_file.binds_to_weak = true; } continue; } if (symbol.flags.@"export" and symbol.flags.weak) { - dynrel_ctx.weak_bind_relocs += 1; macho_file.binds_to_weak = true; - } else if (symbol.flags.interposable) { - dynrel_ctx.bind_relocs += 1; } } - dynrel_ctx.rebase_relocs += 1; } }, @@ -651,8 +640,6 @@ fn resolveRelocInner( ) ResolveError!void { const cpu_arch = macho_file.getTarget().cpu.arch; const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow; - const seg_id = macho_file.sections.items(.segment_id)[self.out_n_sect]; - const seg = macho_file.segments.items[seg_id]; const P = @as(i64, @intCast(self.getAddress(macho_file))) + @as(i64, @intCast(rel_offset)); const A = rel.addend + rel.getRelocAddend(cpu_arch); const S: i64 = @intCast(rel.getTargetAddress(macho_file)); @@ -706,29 +693,8 @@ fn resolveRelocInner( try writer.writeInt(u64, @intCast(S - TLS), .little); return; } - const entry = bind.Entry{ - .target = rel.target, - .offset = @as(u64, @intCast(P)) - seg.vmaddr, - .segment_id = seg_id, - .addend = A, - }; - if (sym.flags.import) { - macho_file.bind.entries.appendAssumeCapacity(entry); - if (sym.flags.weak) { - macho_file.weak_bind.entries.appendAssumeCapacity(entry); - } - return; - } - if (sym.flags.@"export" and sym.flags.weak) { - macho_file.weak_bind.entries.appendAssumeCapacity(entry); - } else if (sym.flags.interposable) { - macho_file.bind.entries.appendAssumeCapacity(entry); - } + if (sym.flags.import) return; } - macho_file.rebase.entries.appendAssumeCapacity(.{ - .offset = @as(u64, @intCast(P)) - seg.vmaddr, - .segment_id = seg_id, - }); try writer.writeInt(u64, @bitCast(S + A - SUB), .little); } else if (rel.meta.length == 2) { try writer.writeInt(u32, @bitCast(@as(i32, @truncate(S + A - SUB))), .little); @@ -1239,7 +1205,6 @@ pub const Alignment = @import("../../InternPool.zig").Alignment; const aarch64 = @import("../aarch64.zig"); const assert = std.debug.assert; -const bind = @import("dyld_info/bind.zig"); const macho = std.macho; const math = std.math; const mem = std.mem; diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 8ecd88b413..30bdce7d9e 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -30,7 +30,6 @@ data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, alive: bool = true, hidden: bool = false, -dynamic_relocs: MachO.DynamicRelocs = .{}, output_symtab_ctx: MachO.SymtabCtx = .{}, output_ar_state: Archive.ArState = .{}, diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index b59b6a6720..f731f36b7e 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -48,7 +48,6 @@ relocs: RelocationTable = .{}, dwarf: ?Dwarf = null, -dynamic_relocs: MachO.DynamicRelocs = .{}, output_symtab_ctx: MachO.SymtabCtx = .{}, output_ar_state: Archive.ArState = .{}, diff --git a/src/link/MachO/dyld_info/Rebase.zig b/src/link/MachO/dyld_info/Rebase.zig index f0121cf3dd..8348aa01f8 100644 --- a/src/link/MachO/dyld_info/Rebase.zig +++ b/src/link/MachO/dyld_info/Rebase.zig @@ -1,14 +1,3 @@ -const Rebase = @This(); - -const std = @import("std"); -const assert = std.debug.assert; -const leb = std.leb; -const log = std.log.scoped(.link_dyld_info); -const macho = std.macho; -const testing = std.testing; - -const Allocator = std.mem.Allocator; - entries: std.ArrayListUnmanaged(Entry) = .{}, buffer: std.ArrayListUnmanaged(u8) = .{}, @@ -30,11 +19,94 @@ pub fn deinit(rebase: *Rebase, gpa: Allocator) void { rebase.buffer.deinit(gpa); } -pub fn size(rebase: Rebase) u64 { - return @as(u64, @intCast(rebase.buffer.items.len)); +pub fn updateSize(rebase: *Rebase, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const gpa = macho_file.base.comp.gpa; + + var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 1); + defer objects.deinit(); + objects.appendSliceAssumeCapacity(macho_file.objects.items); + if (macho_file.getInternalObject()) |obj| objects.appendAssumeCapacity(obj.index); + + for (objects.items) |index| { + const file = macho_file.getFile(index).?; + for (file.getAtoms()) |atom_index| { + const atom = macho_file.getAtom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + if (atom.getInputSection(macho_file).isZerofill()) continue; + const atom_addr = atom.getAddress(macho_file); + const seg_id = macho_file.sections.items(.segment_id)[atom.out_n_sect]; + const seg = macho_file.segments.items[seg_id]; + for (atom.getRelocs(macho_file)) |rel| { + if (rel.type != .unsigned or rel.meta.length != 3) continue; + if (rel.tag == .@"extern") { + const sym = rel.getTargetSymbol(macho_file); + if (sym.isTlvInit(macho_file)) continue; + if (sym.flags.import) continue; + } + const rel_offset = rel.offset - atom.off; + try rebase.entries.append(gpa, .{ + .offset = atom_addr + rel_offset - seg.vmaddr, + .segment_id = seg_id, + }); + } + } + } + + if (macho_file.got_sect_index) |sid| { + const seg_id = macho_file.sections.items(.segment_id)[sid]; + const seg = macho_file.segments.items[seg_id]; + for (macho_file.got.symbols.items, 0..) |ref, idx| { + const sym = macho_file.getSymbol(ref); + const addr = macho_file.got.getAddress(@intCast(idx), macho_file); + if (!sym.flags.import) { + try rebase.entries.append(gpa, .{ + .offset = addr - seg.vmaddr, + .segment_id = seg_id, + }); + } + } + } + + if (macho_file.la_symbol_ptr_sect_index) |sid| { + const sect = macho_file.sections.items(.header)[sid]; + const seg_id = macho_file.sections.items(.segment_id)[sid]; + const seg = macho_file.segments.items[seg_id]; + for (macho_file.stubs.symbols.items, 0..) |ref, idx| { + const sym = macho_file.getSymbol(ref); + const addr = sect.addr + idx * @sizeOf(u64); + const rebase_entry = Rebase.Entry{ + .offset = addr - seg.vmaddr, + .segment_id = seg_id, + }; + if ((sym.flags.import and !sym.flags.weak) or !sym.flags.import) { + try rebase.entries.append(gpa, rebase_entry); + } + } + } + + if (macho_file.tlv_ptr_sect_index) |sid| { + const seg_id = macho_file.sections.items(.segment_id)[sid]; + const seg = macho_file.segments.items[seg_id]; + for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| { + const sym = macho_file.getSymbol(ref); + const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file); + if (!sym.flags.import) { + try rebase.entries.append(gpa, .{ + .offset = addr - seg.vmaddr, + .segment_id = seg_id, + }); + } + } + } + + try rebase.finalize(gpa); + macho_file.dyld_info_cmd.rebase_size = mem.alignForward(u32, @intCast(rebase.buffer.items.len), @alignOf(u64)); } -pub fn finalize(rebase: *Rebase, gpa: Allocator) !void { +fn finalize(rebase: *Rebase, gpa: Allocator) !void { if (rebase.entries.items.len == 0) return; const writer = rebase.buffer.writer(gpa); @@ -198,7 +270,6 @@ fn done(writer: anytype) !void { } pub fn write(rebase: Rebase, writer: anytype) !void { - if (rebase.size() == 0) return; try writer.writeAll(rebase.buffer.items); } @@ -574,3 +645,17 @@ test "rebase - composite" { macho.REBASE_OPCODE_DONE, }, rebase.buffer.items); } + +const std = @import("std"); +const assert = std.debug.assert; +const leb = std.leb; +const log = std.log.scoped(.link_dyld_info); +const macho = std.macho; +const mem = std.mem; +const testing = std.testing; +const trace = @import("../../../tracy.zig").trace; + +const Allocator = mem.Allocator; +const File = @import("../file.zig").File; +const MachO = @import("../../MachO.zig"); +const Rebase = @This(); diff --git a/src/link/MachO/dyld_info/Trie.zig b/src/link/MachO/dyld_info/Trie.zig index a6f717a043..aead1372f0 100644 --- a/src/link/MachO/dyld_info/Trie.zig +++ b/src/link/MachO/dyld_info/Trie.zig @@ -28,269 +28,270 @@ //! After the optional exported symbol information is a byte of how many edges (0-255) that //! this node has leaving it, followed by each edge. Each edge is a zero terminated UTF8 of //! the addition chars in the symbol, followed by a uleb128 offset for the node that edge points to. -const Trie = @This(); - -const std = @import("std"); -const mem = std.mem; -const leb = std.leb; -const log = std.log.scoped(.macho); -const macho = std.macho; -const testing = std.testing; -const assert = std.debug.assert; -const Allocator = mem.Allocator; - -pub const Node = struct { - base: *Trie, - - /// Terminal info associated with this node. - /// If this node is not a terminal node, info is null. - terminal_info: ?struct { - /// Export flags associated with this exported symbol. - export_flags: u64, - /// VM address offset wrt to the section this symbol is defined against. - vmaddr_offset: u64, - } = null, - - /// Offset of this node in the trie output byte stream. - trie_offset: ?u64 = null, - - /// List of all edges originating from this node. - edges: std.ArrayListUnmanaged(Edge) = .{}, - - node_dirty: bool = true, - - /// Edge connecting to nodes in the trie. - pub const Edge = struct { - from: *Node, - to: *Node, - label: []u8, - - fn deinit(self: *Edge, allocator: Allocator) void { - self.to.deinit(allocator); - allocator.destroy(self.to); - allocator.free(self.label); - self.from = undefined; - self.to = undefined; - self.label = undefined; - } - }; - - fn deinit(self: *Node, allocator: Allocator) void { - for (self.edges.items) |*edge| { - edge.deinit(allocator); - } - self.edges.deinit(allocator); - } - - /// Inserts a new node starting from `self`. - fn put(self: *Node, allocator: Allocator, label: []const u8) !*Node { - // Check for match with edges from this node. - for (self.edges.items) |*edge| { - const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.to; - if (match == 0) continue; - if (match == edge.label.len) return edge.to.put(allocator, label[match..]); - - // Found a match, need to splice up nodes. - // From: A -> B - // To: A -> C -> B - const mid = try allocator.create(Node); - mid.* = .{ .base = self.base }; - const to_label = try allocator.dupe(u8, edge.label[match..]); - allocator.free(edge.label); - const to_node = edge.to; - edge.to = mid; - edge.label = try allocator.dupe(u8, label[0..match]); - self.base.node_count += 1; - - try mid.edges.append(allocator, .{ - .from = mid, - .to = to_node, - .label = to_label, - }); - - return if (match == label.len) mid else mid.put(allocator, label[match..]); - } - - // Add a new node. - const node = try allocator.create(Node); - node.* = .{ .base = self.base }; - self.base.node_count += 1; - - try self.edges.append(allocator, .{ - .from = self, - .to = node, - .label = try allocator.dupe(u8, label), - }); - - return node; - } - - /// Recursively parses the node from the input byte stream. - fn read(self: *Node, allocator: Allocator, reader: anytype) Trie.ReadError!usize { - self.node_dirty = true; - const trie_offset = try reader.context.getPos(); - self.trie_offset = trie_offset; - - var nread: usize = 0; - - const node_size = try leb.readUleb128(u64, reader); - if (node_size > 0) { - const export_flags = try leb.readUleb128(u64, reader); - // TODO Parse special flags. - assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and - export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0); - - const vmaddr_offset = try leb.readUleb128(u64, reader); - - self.terminal_info = .{ - .export_flags = export_flags, - .vmaddr_offset = vmaddr_offset, - }; - } - - const nedges = try reader.readByte(); - self.base.node_count += nedges; - - nread += (try reader.context.getPos()) - trie_offset; - - var i: usize = 0; - while (i < nedges) : (i += 1) { - const edge_start_pos = try reader.context.getPos(); - - const label = blk: { - var label_buf = std.ArrayList(u8).init(allocator); - while (true) { - const next = try reader.readByte(); - if (next == @as(u8, 0)) - break; - try label_buf.append(next); - } - break :blk try label_buf.toOwnedSlice(); - }; - - const seek_to = try leb.readUleb128(u64, reader); - const return_pos = try reader.context.getPos(); - - nread += return_pos - edge_start_pos; - try reader.context.seekTo(seek_to); - - const node = try allocator.create(Node); - node.* = .{ .base = self.base }; - - nread += try node.read(allocator, reader); - try self.edges.append(allocator, .{ - .from = self, - .to = node, - .label = label, - }); - try reader.context.seekTo(return_pos); - } - - return nread; - } - - /// Writes this node to a byte stream. - /// The children of this node *are* not written to the byte stream - /// recursively. To write all nodes to a byte stream in sequence, - /// iterate over `Trie.ordered_nodes` and call this method on each node. - /// This is one of the requirements of the MachO. - /// Panics if `finalize` was not called before calling this method. - fn write(self: Node, writer: anytype) !void { - assert(!self.node_dirty); - if (self.terminal_info) |info| { - // Terminal node info: encode export flags and vmaddr offset of this symbol. - var info_buf: [@sizeOf(u64) * 2]u8 = undefined; - var info_stream = std.io.fixedBufferStream(&info_buf); - // TODO Implement for special flags. - assert(info.export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and - info.export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0); - try leb.writeUleb128(info_stream.writer(), info.export_flags); - try leb.writeUleb128(info_stream.writer(), info.vmaddr_offset); - - // Encode the size of the terminal node info. - var size_buf: [@sizeOf(u64)]u8 = undefined; - var size_stream = std.io.fixedBufferStream(&size_buf); - try leb.writeUleb128(size_stream.writer(), info_stream.pos); - - // Now, write them to the output stream. - try writer.writeAll(size_buf[0..size_stream.pos]); - try writer.writeAll(info_buf[0..info_stream.pos]); - } else { - // Non-terminal node is delimited by 0 byte. - try writer.writeByte(0); - } - // Write number of edges (max legal number of edges is 256). - try writer.writeByte(@as(u8, @intCast(self.edges.items.len))); - - for (self.edges.items) |edge| { - // Write edge label and offset to next node in trie. - try writer.writeAll(edge.label); - try writer.writeByte(0); - try leb.writeUleb128(writer, edge.to.trie_offset.?); - } - } - - const FinalizeResult = struct { - /// Current size of this node in bytes. - node_size: u64, - - /// True if the trie offset of this node in the output byte stream - /// would need updating; false otherwise. - updated: bool, - }; - - /// Updates offset of this node in the output byte stream. - fn finalize(self: *Node, offset_in_trie: u64) !FinalizeResult { - var stream = std.io.countingWriter(std.io.null_writer); - const writer = stream.writer(); - - var node_size: u64 = 0; - if (self.terminal_info) |info| { - try leb.writeUleb128(writer, info.export_flags); - try leb.writeUleb128(writer, info.vmaddr_offset); - try leb.writeUleb128(writer, stream.bytes_written); - } else { - node_size += 1; // 0x0 for non-terminal nodes - } - node_size += 1; // 1 byte for edge count - - for (self.edges.items) |edge| { - const next_node_offset = edge.to.trie_offset orelse 0; - node_size += edge.label.len + 1; - try leb.writeUleb128(writer, next_node_offset); - } - - const trie_offset = self.trie_offset orelse 0; - const updated = offset_in_trie != trie_offset; - self.trie_offset = offset_in_trie; - self.node_dirty = false; - node_size += stream.bytes_written; - - return FinalizeResult{ .node_size = node_size, .updated = updated }; - } -}; /// The root node of the trie. -root: ?*Node = null, +root: ?Node.Index = null, +buffer: std.ArrayListUnmanaged(u8) = .{}, +nodes: std.MultiArrayList(Node) = .{}, +edges: std.ArrayListUnmanaged(Edge) = .{}, -/// If you want to access nodes ordered in DFS fashion, -/// you should call `finalize` first since the nodes -/// in this container are not guaranteed to not be stale -/// if more insertions took place after the last `finalize` -/// call. -ordered_nodes: std.ArrayListUnmanaged(*Node) = .{}, +/// Insert a symbol into the trie, updating the prefixes in the process. +/// This operation may change the layout of the trie by splicing edges in +/// certain circumstances. +fn put(self: *Trie, allocator: Allocator, symbol: ExportSymbol) !void { + // const tracy = trace(@src()); + // defer tracy.end(); -/// The size of the trie in bytes. -/// This value may be outdated if there were additional -/// insertions performed after `finalize` was called. -/// Call `finalize` before accessing this value to ensure -/// it is up-to-date. -size: u64 = 0, + const node_index = try self.putNode(self.root.?, allocator, symbol.name); + const slice = self.nodes.slice(); + slice.items(.is_terminal)[node_index] = true; + slice.items(.vmaddr_offset)[node_index] = symbol.vmaddr_offset; + slice.items(.export_flags)[node_index] = symbol.export_flags; +} -/// Number of nodes currently in the trie. -node_count: usize = 0, +/// Inserts a new node starting at `node_index`. +fn putNode(self: *Trie, node_index: Node.Index, allocator: Allocator, label: []const u8) !Node.Index { + // Check for match with edges from this node. + for (self.nodes.items(.edges)[node_index].items) |edge_index| { + const edge = &self.edges.items[edge_index]; + const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.node; + if (match == 0) continue; + if (match == edge.label.len) return self.putNode(edge.node, allocator, label[match..]); -trie_dirty: bool = true, + // Found a match, need to splice up nodes. + // From: A -> B + // To: A -> C -> B + const mid_index = try self.addNode(allocator); + const to_label = edge.label[match..]; + const to_node = edge.node; + edge.node = mid_index; + edge.label = label[0..match]; + + const new_edge_index = try self.addEdge(allocator); + const new_edge = &self.edges.items[new_edge_index]; + new_edge.node = to_node; + new_edge.label = to_label; + try self.nodes.items(.edges)[mid_index].append(allocator, new_edge_index); + + return if (match == label.len) mid_index else self.putNode(mid_index, allocator, label[match..]); + } + + // Add a new node. + const new_node_index = try self.addNode(allocator); + const new_edge_index = try self.addEdge(allocator); + const new_edge = &self.edges.items[new_edge_index]; + new_edge.node = new_node_index; + new_edge.label = label; + try self.nodes.items(.edges)[node_index].append(allocator, new_edge_index); + + return new_node_index; +} + +pub fn updateSize(self: *Trie, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const gpa = macho_file.base.comp.gpa; + + try self.init(gpa); + // TODO + // try self.nodes.ensureUnusedCapacity(gpa, macho_file.resolver.values.items.len * 2); + // try self.edges.ensureUnusedCapacity(gpa, macho_file.resolver.values.items.len * 2); + + const seg = macho_file.getTextSegment(); + for (macho_file.objects.items) |index| { + for (macho_file.getFile(index).?.getSymbols()) |ref| { + const sym = macho_file.getSymbol(ref); + if (!sym.flags.@"export") continue; + if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue; + var flags: u64 = if (sym.flags.abs) + macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE + else if (sym.flags.tlv) + macho.EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL + else + macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR; + if (sym.flags.weak) { + flags |= macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; + macho_file.weak_defines = true; + macho_file.binds_to_weak = true; + } + try self.put(gpa, .{ + .name = sym.getName(macho_file), + .vmaddr_offset = sym.getAddress(.{ .stubs = false }, macho_file) - seg.vmaddr, + .export_flags = flags, + }); + } + } + + try self.finalize(gpa); + + macho_file.dyld_info_cmd.export_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64)); +} + +/// Finalizes this trie for writing to a byte stream. +/// This step performs multiple passes through the trie ensuring +/// there are no gaps after every `Node` is ULEB128 encoded. +/// Call this method before trying to `write` the trie to a byte stream. +fn finalize(self: *Trie, allocator: Allocator) !void { + const tracy = trace(@src()); + defer tracy.end(); + + var ordered_nodes = std.ArrayList(Node.Index).init(allocator); + defer ordered_nodes.deinit(); + try ordered_nodes.ensureTotalCapacityPrecise(self.nodes.items(.is_terminal).len); + + var fifo = std.fifo.LinearFifo(Node.Index, .Dynamic).init(allocator); + defer fifo.deinit(); + + try fifo.writeItem(self.root.?); + + while (fifo.readItem()) |next_index| { + const edges = &self.nodes.items(.edges)[next_index]; + for (edges.items) |edge_index| { + const edge = self.edges.items[edge_index]; + try fifo.writeItem(edge.node); + } + ordered_nodes.appendAssumeCapacity(next_index); + } + + var more: bool = true; + var size: u32 = 0; + while (more) { + size = 0; + more = false; + for (ordered_nodes.items) |node_index| { + const res = try self.finalizeNode(node_index, size); + size += res.node_size; + if (res.updated) more = true; + } + } + + try self.buffer.ensureTotalCapacityPrecise(allocator, size); + for (ordered_nodes.items) |node_index| { + try self.writeNode(node_index, self.buffer.writer(allocator)); + } +} + +const FinalizeNodeResult = struct { + /// Current size of this node in bytes. + node_size: u32, + + /// True if the trie offset of this node in the output byte stream + /// would need updating; false otherwise. + updated: bool, +}; + +/// Updates offset of this node in the output byte stream. +fn finalizeNode(self: *Trie, node_index: Node.Index, offset_in_trie: u32) !FinalizeNodeResult { + var stream = std.io.countingWriter(std.io.null_writer); + const writer = stream.writer(); + const slice = self.nodes.slice(); + + var node_size: u32 = 0; + if (slice.items(.is_terminal)[node_index]) { + const export_flags = slice.items(.export_flags)[node_index]; + const vmaddr_offset = slice.items(.vmaddr_offset)[node_index]; + try leb.writeULEB128(writer, export_flags); + try leb.writeULEB128(writer, vmaddr_offset); + try leb.writeULEB128(writer, stream.bytes_written); + } else { + node_size += 1; // 0x0 for non-terminal nodes + } + node_size += 1; // 1 byte for edge count + + for (slice.items(.edges)[node_index].items) |edge_index| { + const edge = &self.edges.items[edge_index]; + const next_node_offset = slice.items(.trie_offset)[edge.node]; + node_size += @intCast(edge.label.len + 1); + try leb.writeULEB128(writer, next_node_offset); + } + + const trie_offset = slice.items(.trie_offset)[node_index]; + const updated = offset_in_trie != trie_offset; + slice.items(.trie_offset)[node_index] = offset_in_trie; + node_size += @intCast(stream.bytes_written); + + return .{ .node_size = node_size, .updated = updated }; +} + +fn init(self: *Trie, allocator: Allocator) !void { + assert(self.root == null); + self.root = try self.addNode(allocator); +} + +pub fn deinit(self: *Trie, allocator: Allocator) void { + for (self.nodes.items(.edges)) |*edges| { + edges.deinit(allocator); + } + self.nodes.deinit(allocator); + self.edges.deinit(allocator); + self.buffer.deinit(allocator); +} + +pub fn write(self: Trie, writer: anytype) !void { + if (self.buffer.items.len == 0) return; + try writer.writeAll(self.buffer.items); +} + +/// Writes this node to a byte stream. +/// The children of this node *are* not written to the byte stream +/// recursively. To write all nodes to a byte stream in sequence, +/// iterate over `Trie.ordered_nodes` and call this method on each node. +/// This is one of the requirements of the MachO. +/// Panics if `finalize` was not called before calling this method. +fn writeNode(self: *Trie, node_index: Node.Index, writer: anytype) !void { + const slice = self.nodes.slice(); + const edges = slice.items(.edges)[node_index]; + const is_terminal = slice.items(.is_terminal)[node_index]; + const export_flags = slice.items(.export_flags)[node_index]; + const vmaddr_offset = slice.items(.vmaddr_offset)[node_index]; + + if (is_terminal) { + // Terminal node info: encode export flags and vmaddr offset of this symbol. + var info_buf: [@sizeOf(u64) * 2]u8 = undefined; + var info_stream = std.io.fixedBufferStream(&info_buf); + // TODO Implement for special flags. + assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and + export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0); + try leb.writeULEB128(info_stream.writer(), export_flags); + try leb.writeULEB128(info_stream.writer(), vmaddr_offset); + + // Encode the size of the terminal node info. + var size_buf: [@sizeOf(u64)]u8 = undefined; + var size_stream = std.io.fixedBufferStream(&size_buf); + try leb.writeULEB128(size_stream.writer(), info_stream.pos); + + // Now, write them to the output stream. + try writer.writeAll(size_buf[0..size_stream.pos]); + try writer.writeAll(info_buf[0..info_stream.pos]); + } else { + // Non-terminal node is delimited by 0 byte. + try writer.writeByte(0); + } + // Write number of edges (max legal number of edges is 256). + try writer.writeByte(@as(u8, @intCast(edges.items.len))); + + for (edges.items) |edge_index| { + const edge = self.edges.items[edge_index]; + // Write edge label and offset to next node in trie. + try writer.writeAll(edge.label); + try writer.writeByte(0); + try leb.writeULEB128(writer, slice.items(.trie_offset)[edge.node]); + } +} + +fn addNode(self: *Trie, allocator: Allocator) !Node.Index { + const index: Node.Index = @intCast(try self.nodes.addOne(allocator)); + self.nodes.set(index, .{}); + return index; +} + +fn addEdge(self: *Trie, allocator: Allocator) !Edge.Index { + const index: Edge.Index = @intCast(self.edges.items.len); + const edge = try self.edges.addOne(allocator); + edge.* = .{}; + return index; +} /// Export symbol that is to be placed in the trie. pub const ExportSymbol = struct { @@ -305,186 +306,34 @@ pub const ExportSymbol = struct { export_flags: u64, }; -/// Insert a symbol into the trie, updating the prefixes in the process. -/// This operation may change the layout of the trie by splicing edges in -/// certain circumstances. -pub fn put(self: *Trie, allocator: Allocator, symbol: ExportSymbol) !void { - const node = try self.root.?.put(allocator, symbol.name); - node.terminal_info = .{ - .vmaddr_offset = symbol.vmaddr_offset, - .export_flags = symbol.export_flags, - }; - self.trie_dirty = true; -} +const Node = struct { + is_terminal: bool = false, -/// Finalizes this trie for writing to a byte stream. -/// This step performs multiple passes through the trie ensuring -/// there are no gaps after every `Node` is ULEB128 encoded. -/// Call this method before trying to `write` the trie to a byte stream. -pub fn finalize(self: *Trie, allocator: Allocator) !void { - if (!self.trie_dirty) return; + /// Export flags associated with this exported symbol. + export_flags: u64 = 0, - self.ordered_nodes.shrinkRetainingCapacity(0); - try self.ordered_nodes.ensureTotalCapacity(allocator, self.node_count); + /// VM address offset wrt to the section this symbol is defined against. + vmaddr_offset: u64 = 0, - var fifo = std.fifo.LinearFifo(*Node, .Dynamic).init(allocator); - defer fifo.deinit(); + /// Offset of this node in the trie output byte stream. + trie_offset: u32 = 0, - try fifo.writeItem(self.root.?); + /// List of all edges originating from this node. + edges: std.ArrayListUnmanaged(Edge.Index) = .{}, - while (fifo.readItem()) |next| { - for (next.edges.items) |*edge| { - try fifo.writeItem(edge.to); - } - self.ordered_nodes.appendAssumeCapacity(next); - } - - var more: bool = true; - while (more) { - self.size = 0; - more = false; - for (self.ordered_nodes.items) |node| { - const res = try node.finalize(self.size); - self.size += res.node_size; - if (res.updated) more = true; - } - } - - self.trie_dirty = false; -} - -const ReadError = error{ - OutOfMemory, - EndOfStream, - Overflow, + const Index = u32; }; -/// Parse the trie from a byte stream. -pub fn read(self: *Trie, allocator: Allocator, reader: anytype) ReadError!usize { - return self.root.?.read(allocator, reader); -} +/// Edge connecting nodes in the trie. +const Edge = struct { + /// Target node in the trie. + node: Node.Index = 0, -/// Write the trie to a byte stream. -/// Panics if the trie was not finalized using `finalize` before calling this method. -pub fn write(self: Trie, writer: anytype) !void { - assert(!self.trie_dirty); - for (self.ordered_nodes.items) |node| { - try node.write(writer); - } -} + /// Matching prefix. + label: []const u8 = "", -pub fn init(self: *Trie, allocator: Allocator) !void { - assert(self.root == null); - const root = try allocator.create(Node); - root.* = .{ .base = self }; - self.root = root; - self.node_count += 1; -} - -pub fn deinit(self: *Trie, allocator: Allocator) void { - if (self.root) |root| { - root.deinit(allocator); - allocator.destroy(root); - } - self.ordered_nodes.deinit(allocator); -} - -test "Trie node count" { - const gpa = testing.allocator; - var trie: Trie = .{}; - defer trie.deinit(gpa); - try trie.init(gpa); - - try testing.expectEqual(@as(usize, 1), trie.node_count); - try testing.expect(trie.root != null); - - try trie.put(gpa, .{ - .name = "_main", - .vmaddr_offset = 0, - .export_flags = 0, - }); - try testing.expectEqual(@as(usize, 2), trie.node_count); - - // Inserting the same node shouldn't update the trie. - try trie.put(gpa, .{ - .name = "_main", - .vmaddr_offset = 0, - .export_flags = 0, - }); - try testing.expectEqual(@as(usize, 2), trie.node_count); - - try trie.put(gpa, .{ - .name = "__mh_execute_header", - .vmaddr_offset = 0x1000, - .export_flags = 0, - }); - try testing.expectEqual(@as(usize, 4), trie.node_count); - - // Inserting the same node shouldn't update the trie. - try trie.put(gpa, .{ - .name = "__mh_execute_header", - .vmaddr_offset = 0x1000, - .export_flags = 0, - }); - try testing.expectEqual(@as(usize, 4), trie.node_count); - try trie.put(gpa, .{ - .name = "_main", - .vmaddr_offset = 0, - .export_flags = 0, - }); - try testing.expectEqual(@as(usize, 4), trie.node_count); -} - -test "Trie basic" { - const gpa = testing.allocator; - var trie: Trie = .{}; - defer trie.deinit(gpa); - try trie.init(gpa); - - // root --- _st ---> node - try trie.put(gpa, .{ - .name = "_st", - .vmaddr_offset = 0, - .export_flags = 0, - }); - try testing.expect(trie.root.?.edges.items.len == 1); - try testing.expect(mem.eql(u8, trie.root.?.edges.items[0].label, "_st")); - - { - // root --- _st ---> node --- art ---> node - try trie.put(gpa, .{ - .name = "_start", - .vmaddr_offset = 0, - .export_flags = 0, - }); - try testing.expect(trie.root.?.edges.items.len == 1); - - const nextEdge = &trie.root.?.edges.items[0]; - try testing.expect(mem.eql(u8, nextEdge.label, "_st")); - try testing.expect(nextEdge.to.edges.items.len == 1); - try testing.expect(mem.eql(u8, nextEdge.to.edges.items[0].label, "art")); - } - { - // root --- _ ---> node --- st ---> node --- art ---> node - // | - // | --- main ---> node - try trie.put(gpa, .{ - .name = "_main", - .vmaddr_offset = 0, - .export_flags = 0, - }); - try testing.expect(trie.root.?.edges.items.len == 1); - - const nextEdge = &trie.root.?.edges.items[0]; - try testing.expect(mem.eql(u8, nextEdge.label, "_")); - try testing.expect(nextEdge.to.edges.items.len == 2); - try testing.expect(mem.eql(u8, nextEdge.to.edges.items[0].label, "st")); - try testing.expect(mem.eql(u8, nextEdge.to.edges.items[1].label, "main")); - - const nextNextEdge = &nextEdge.to.edges.items[0]; - try testing.expect(mem.eql(u8, nextNextEdge.to.edges.items[0].label, "art")); - } -} + const Index = u32; +}; fn expectEqualHexStrings(expected: []const u8, given: []const u8) !void { assert(expected.len > 0); @@ -502,7 +351,7 @@ fn expectEqualHexStrings(expected: []const u8, given: []const u8) !void { } test "write Trie to a byte stream" { - var gpa = testing.allocator; + const gpa = testing.allocator; var trie: Trie = .{}; defer trie.deinit(gpa); try trie.init(gpa); @@ -519,7 +368,6 @@ test "write Trie to a byte stream" { }); try trie.finalize(gpa); - try trie.finalize(gpa); // Finalizing mulitple times is a nop subsequently unless we add new nodes. const exp_buffer = [_]u8{ 0x0, 0x1, // node root @@ -531,51 +379,7 @@ test "write Trie to a byte stream" { 0x2, 0x0, 0x0, 0x0, // terminal node 0x3, 0x0, 0x80, 0x20, 0x0, // terminal node }; - - const buffer = try gpa.alloc(u8, trie.size); - defer gpa.free(buffer); - var stream = std.io.fixedBufferStream(buffer); - { - _ = try trie.write(stream.writer()); - try expectEqualHexStrings(&exp_buffer, buffer); - } - { - // Writing finalized trie again should yield the same result. - try stream.seekTo(0); - _ = try trie.write(stream.writer()); - try expectEqualHexStrings(&exp_buffer, buffer); - } -} - -test "parse Trie from byte stream" { - const gpa = testing.allocator; - - const in_buffer = [_]u8{ - 0x0, 0x1, // node root - 0x5f, 0x0, 0x5, // edge '_' - 0x0, 0x2, // non-terminal node - 0x5f, 0x6d, 0x68, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, // edge '_mh_execute_header' - 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x0, 0x21, // edge '_mh_execute_header' - 0x6d, 0x61, 0x69, 0x6e, 0x0, 0x25, // edge 'main' - 0x2, 0x0, 0x0, 0x0, // terminal node - 0x3, 0x0, 0x80, 0x20, 0x0, // terminal node - }; - - var in_stream = std.io.fixedBufferStream(&in_buffer); - var trie: Trie = .{}; - defer trie.deinit(gpa); - try trie.init(gpa); - const nread = try trie.read(gpa, in_stream.reader()); - - try testing.expect(nread == in_buffer.len); - - try trie.finalize(gpa); - - const out_buffer = try gpa.alloc(u8, trie.size); - defer gpa.free(out_buffer); - var out_stream = std.io.fixedBufferStream(out_buffer); - _ = try trie.write(out_stream.writer()); - try expectEqualHexStrings(&in_buffer, out_buffer); + try expectEqualHexStrings(&exp_buffer, trie.buffer.items); } test "ordering bug" { @@ -602,11 +406,18 @@ test "ordering bug" { 0x88, 0x80, 0x02, 0x01, 0x73, 0x53, 0x74, 0x72, 0x00, 0x12, 0x03, 0x00, 0xD8, 0x0A, 0x00, }; - - const buffer = try gpa.alloc(u8, trie.size); - defer gpa.free(buffer); - var stream = std.io.fixedBufferStream(buffer); - // Writing finalized trie again should yield the same result. - _ = try trie.write(stream.writer()); - try expectEqualHexStrings(&exp_buffer, buffer); + try expectEqualHexStrings(&exp_buffer, trie.buffer.items); } + +const assert = std.debug.assert; +const leb = std.leb; +const log = std.log.scoped(.macho); +const macho = std.macho; +const mem = std.mem; +const std = @import("std"); +const testing = std.testing; +const trace = @import("../../../tracy.zig").trace; + +const Allocator = mem.Allocator; +const MachO = @import("../../MachO.zig"); +const Trie = @This(); diff --git a/src/link/MachO/dyld_info/bind.zig b/src/link/MachO/dyld_info/bind.zig index 15d3df76e5..28e8b1d7d2 100644 --- a/src/link/MachO/dyld_info/bind.zig +++ b/src/link/MachO/dyld_info/bind.zig @@ -1,14 +1,3 @@ -const std = @import("std"); -const assert = std.debug.assert; -const leb = std.leb; -const log = std.log.scoped(.link_dyld_info); -const macho = std.macho; -const testing = std.testing; - -const Allocator = std.mem.Allocator; -const MachO = @import("../../MachO.zig"); -const Symbol = @import("../Symbol.zig"); - pub const Entry = struct { target: Symbol.Index, offset: u64, @@ -39,11 +28,108 @@ pub const Bind = struct { self.buffer.deinit(gpa); } - pub fn size(self: Self) u64 { - return @intCast(self.buffer.items.len); + pub fn updateSize(self: *Self, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const gpa = macho_file.base.comp.gpa; + const cpu_arch = macho_file.getTarget().cpu.arch; + + var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 1); + defer objects.deinit(); + objects.appendSliceAssumeCapacity(macho_file.objects.items); + if (macho_file.getInternalObject()) |obj| objects.appendAssumeCapacity(obj.index); + + for (objects.items) |index| { + const file = macho_file.getFile(index).?; + for (file.getAtoms()) |atom_index| { + const atom = macho_file.getAtom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + if (atom.getInputSection(macho_file).isZerofill()) continue; + const atom_addr = atom.getAddress(macho_file); + const relocs = atom.getRelocs(macho_file); + const seg_id = macho_file.sections.items(.segment_id)[atom.out_n_sect]; + const seg = macho_file.segments.items[seg_id]; + for (relocs) |rel| { + if (rel.type != .unsigned or rel.meta.length != 3 or rel.tag != .@"extern") continue; + const rel_offset = rel.offset - atom.off; + const addend = rel.addend + rel.getRelocAddend(cpu_arch); + const sym = rel.getTargetSymbol(macho_file); + if (sym.isTlvInit(macho_file)) continue; + const entry = Entry{ + .target = rel.target, + .offset = atom_addr + rel_offset - seg.vmaddr, + .segment_id = seg_id, + .addend = addend, + }; + if (sym.flags.import or (!(sym.flags.@"export" and sym.flags.weak) and sym.flags.interposable)) { + try self.entries.append(gpa, entry); + } + } + } + } + + if (macho_file.got_sect_index) |sid| { + const seg_id = macho_file.sections.items(.segment_id)[sid]; + const seg = macho_file.segments.items[seg_id]; + for (macho_file.got.symbols.items, 0..) |ref, idx| { + const sym = macho_file.getSymbol(ref); + const addr = macho_file.got.getAddress(@intCast(idx), macho_file); + const entry = Entry{ + .target = ref, + .offset = addr - seg.vmaddr, + .segment_id = seg_id, + .addend = 0, + }; + if (sym.flags.import or (sym.flags.@"export" and sym.flags.interposable and !sym.flags.weak)) { + try self.entries.append(gpa, entry); + } + } + } + + if (macho_file.la_symbol_ptr_sect_index) |sid| { + const sect = macho_file.sections.items(.header)[sid]; + const seg_id = macho_file.sections.items(.segment_id)[sid]; + const seg = macho_file.segments.items[seg_id]; + for (macho_file.stubs.symbols.items, 0..) |ref, idx| { + const sym = macho_file.getSymbol(ref); + const addr = sect.addr + idx * @sizeOf(u64); + const bind_entry = Entry{ + .target = ref, + .offset = addr - seg.vmaddr, + .segment_id = seg_id, + .addend = 0, + }; + if (sym.flags.import and sym.flags.weak) { + try self.entries.append(gpa, bind_entry); + } + } + } + + if (macho_file.tlv_ptr_sect_index) |sid| { + const seg_id = macho_file.sections.items(.segment_id)[sid]; + const seg = macho_file.segments.items[seg_id]; + + for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| { + const sym = macho_file.getSymbol(ref); + const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file); + const entry = Entry{ + .target = ref, + .offset = addr - seg.vmaddr, + .segment_id = seg_id, + .addend = 0, + }; + if (sym.flags.import or (sym.flags.@"export" and sym.flags.interposable and !sym.flags.weak)) { + try self.entries.append(gpa, entry); + } + } + } + + try self.finalize(gpa, macho_file); + macho_file.dyld_info_cmd.bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64)); } - pub fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void { + fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void { if (self.entries.items.len == 0) return; const writer = self.buffer.writer(gpa); @@ -178,7 +264,6 @@ pub const Bind = struct { } pub fn write(self: Self, writer: anytype) !void { - if (self.size() == 0) return; try writer.writeAll(self.buffer.items); } }; @@ -194,11 +279,109 @@ pub const WeakBind = struct { self.buffer.deinit(gpa); } - pub fn size(self: Self) u64 { - return @intCast(self.buffer.items.len); + pub fn updateSize(self: *Self, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const gpa = macho_file.base.comp.gpa; + const cpu_arch = macho_file.getTarget().cpu.arch; + + var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 1); + defer objects.deinit(); + objects.appendSliceAssumeCapacity(macho_file.objects.items); + if (macho_file.getInternalObject()) |obj| objects.appendAssumeCapacity(obj.index); + + for (objects.items) |index| { + const file = macho_file.getFile(index).?; + for (file.getAtoms()) |atom_index| { + const atom = macho_file.getAtom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + if (atom.getInputSection(macho_file).isZerofill()) continue; + const atom_addr = atom.getAddress(macho_file); + const relocs = atom.getRelocs(macho_file); + const seg_id = macho_file.sections.items(.segment_id)[atom.out_n_sect]; + const seg = macho_file.segments.items[seg_id]; + for (relocs) |rel| { + if (rel.type != .unsigned or rel.meta.length != 3 or rel.tag != .@"extern") continue; + const rel_offset = rel.offset - atom.off; + const addend = rel.addend + rel.getRelocAddend(cpu_arch); + const sym = rel.getTargetSymbol(macho_file); + if (sym.isTlvInit(macho_file)) continue; + const entry = Entry{ + .target = rel.target, + .offset = atom_addr + rel_offset - seg.vmaddr, + .segment_id = seg_id, + .addend = addend, + }; + if (!sym.isLocal() and sym.flags.weak) { + try self.entries.append(gpa, entry); + } + } + } + } + + if (macho_file.got_sect_index) |sid| { + const seg_id = macho_file.sections.items(.segment_id)[sid]; + const seg = macho_file.segments.items[seg_id]; + for (macho_file.got.symbols.items, 0..) |ref, idx| { + const sym = macho_file.getSymbol(ref); + const addr = macho_file.got.getAddress(@intCast(idx), macho_file); + const entry = Entry{ + .target = ref, + .offset = addr - seg.vmaddr, + .segment_id = seg_id, + .addend = 0, + }; + if (sym.flags.weak) { + try self.entries.append(gpa, entry); + } + } + } + + if (macho_file.la_symbol_ptr_sect_index) |sid| { + const sect = macho_file.sections.items(.header)[sid]; + const seg_id = macho_file.sections.items(.segment_id)[sid]; + const seg = macho_file.segments.items[seg_id]; + + for (macho_file.stubs.symbols.items, 0..) |ref, idx| { + const sym = macho_file.getSymbol(ref); + const addr = sect.addr + idx * @sizeOf(u64); + const bind_entry = Entry{ + .target = ref, + .offset = addr - seg.vmaddr, + .segment_id = seg_id, + .addend = 0, + }; + if (sym.flags.weak) { + try self.entries.append(gpa, bind_entry); + } + } + } + + if (macho_file.tlv_ptr_sect_index) |sid| { + const seg_id = macho_file.sections.items(.segment_id)[sid]; + const seg = macho_file.segments.items[seg_id]; + + for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| { + const sym = macho_file.getSymbol(ref); + const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file); + const entry = Entry{ + .target = ref, + .offset = addr - seg.vmaddr, + .segment_id = seg_id, + .addend = 0, + }; + if (sym.flags.weak) { + try self.entries.append(gpa, entry); + } + } + } + + try self.finalize(gpa, macho_file); + macho_file.dyld_info_cmd.weak_bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64)); } - pub fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void { + fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void { if (self.entries.items.len == 0) return; const writer = self.buffer.writer(gpa); @@ -322,7 +505,6 @@ pub const WeakBind = struct { } pub fn write(self: Self, writer: anytype) !void { - if (self.size() == 0) return; try writer.writeAll(self.buffer.items); } }; @@ -340,11 +522,36 @@ pub const LazyBind = struct { self.offsets.deinit(gpa); } - pub fn size(self: Self) u64 { - return @intCast(self.buffer.items.len); + pub fn updateSize(self: *Self, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const gpa = macho_file.base.comp.gpa; + + const sid = macho_file.la_symbol_ptr_sect_index.?; + const sect = macho_file.sections.items(.header)[sid]; + const seg_id = macho_file.sections.items(.segment_id)[sid]; + const seg = macho_file.segments.items[seg_id]; + + for (macho_file.stubs.symbols.items, 0..) |ref, idx| { + const sym = macho_file.getSymbol(ref); + const addr = sect.addr + idx * @sizeOf(u64); + const bind_entry = Entry{ + .target = ref, + .offset = addr - seg.vmaddr, + .segment_id = seg_id, + .addend = 0, + }; + if ((sym.flags.import and !sym.flags.weak) or (sym.flags.interposable and !sym.flags.weak)) { + try self.entries.append(gpa, bind_entry); + } + } + + try self.finalize(gpa, macho_file); + macho_file.dyld_info_cmd.lazy_bind_size = mem.alignForward(u32, @intCast(self.buffer.items.len), @alignOf(u64)); } - pub fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void { + fn finalize(self: *Self, gpa: Allocator, ctx: *MachO) !void { try self.offsets.ensureTotalCapacityPrecise(gpa, self.entries.items.len); const writer = self.buffer.writer(gpa); @@ -474,3 +681,17 @@ fn done(writer: anytype) !void { log.debug(">>> done", .{}); try writer.writeByte(macho.BIND_OPCODE_DONE); } + +const assert = std.debug.assert; +const leb = std.leb; +const log = std.log.scoped(.link_dyld_info); +const macho = std.macho; +const mem = std.mem; +const testing = std.testing; +const trace = @import("../../../tracy.zig").trace; +const std = @import("std"); + +const Allocator = mem.Allocator; +const File = @import("../file.zig").File; +const MachO = @import("../../MachO.zig"); +const Symbol = @import("../Symbol.zig"); diff --git a/src/link/MachO/synthetic.zig b/src/link/MachO/synthetic.zig index a7729b9137..4cac061d93 100644 --- a/src/link/MachO/synthetic.zig +++ b/src/link/MachO/synthetic.zig @@ -70,22 +70,6 @@ pub const ZigGotSection = struct { } } - pub fn addDyldRelocs(zig_got: ZigGotSection, macho_file: *MachO) !void { - const tracy = trace(@src()); - defer tracy.end(); - const gpa = macho_file.base.comp.gpa; - const seg_id = macho_file.sections.items(.segment_id)[macho_file.zig_got_sect_index.?]; - const seg = macho_file.segments.items[seg_id]; - - for (0..zig_got.entries.items.len) |idx| { - const addr = zig_got.entryAddress(@intCast(idx), macho_file); - try macho_file.rebase.entries.append(gpa, .{ - .offset = addr - seg.vmaddr, - .segment_id = seg_id, - }); - } - } - const FormatCtx = struct { zig_got: ZigGotSection, macho_file: *MachO, @@ -146,41 +130,6 @@ pub const GotSection = struct { return got.symbols.items.len * @sizeOf(u64); } - pub fn addDyldRelocs(got: GotSection, macho_file: *MachO) !void { - const tracy = trace(@src()); - defer tracy.end(); - const gpa = macho_file.base.comp.gpa; - const seg_id = macho_file.sections.items(.segment_id)[macho_file.got_sect_index.?]; - const seg = macho_file.segments.items[seg_id]; - - for (got.symbols.items, 0..) |sym_index, idx| { - const sym = macho_file.getSymbol(sym_index); - const addr = got.getAddress(@intCast(idx), macho_file); - const entry = bind.Entry{ - .target = sym_index, - .offset = addr - seg.vmaddr, - .segment_id = seg_id, - .addend = 0, - }; - if (sym.flags.import) { - try macho_file.bind.entries.append(gpa, entry); - if (sym.flags.weak) { - try macho_file.weak_bind.entries.append(gpa, entry); - } - } else { - try macho_file.rebase.entries.append(gpa, .{ - .offset = addr - seg.vmaddr, - .segment_id = seg_id, - }); - if (sym.flags.weak) { - try macho_file.weak_bind.entries.append(gpa, entry); - } else if (sym.flags.interposable) { - try macho_file.bind.entries.append(gpa, entry); - } - } - } - } - pub fn write(got: GotSection, macho_file: *MachO, writer: anytype) !void { const tracy = trace(@src()); defer tracy.end(); @@ -446,49 +395,6 @@ pub const LaSymbolPtrSection = struct { return macho_file.stubs.symbols.items.len * @sizeOf(u64); } - pub fn addDyldRelocs(laptr: LaSymbolPtrSection, macho_file: *MachO) !void { - const tracy = trace(@src()); - defer tracy.end(); - _ = laptr; - const gpa = macho_file.base.comp.gpa; - - const sect = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_sect_index.?]; - const seg_id = macho_file.sections.items(.segment_id)[macho_file.la_symbol_ptr_sect_index.?]; - const seg = macho_file.segments.items[seg_id]; - - for (macho_file.stubs.symbols.items, 0..) |sym_index, idx| { - const sym = macho_file.getSymbol(sym_index); - const addr = sect.addr + idx * @sizeOf(u64); - const rebase_entry = Rebase.Entry{ - .offset = addr - seg.vmaddr, - .segment_id = seg_id, - }; - const bind_entry = bind.Entry{ - .target = sym_index, - .offset = addr - seg.vmaddr, - .segment_id = seg_id, - .addend = 0, - }; - if (sym.flags.import) { - if (sym.flags.weak) { - try macho_file.bind.entries.append(gpa, bind_entry); - try macho_file.weak_bind.entries.append(gpa, bind_entry); - } else { - try macho_file.lazy_bind.entries.append(gpa, bind_entry); - try macho_file.rebase.entries.append(gpa, rebase_entry); - } - } else { - if (sym.flags.weak) { - try macho_file.rebase.entries.append(gpa, rebase_entry); - try macho_file.weak_bind.entries.append(gpa, bind_entry); - } else if (sym.flags.interposable) { - try macho_file.lazy_bind.entries.append(gpa, bind_entry); - try macho_file.rebase.entries.append(gpa, rebase_entry); - } - } - } - } - pub fn write(laptr: LaSymbolPtrSection, macho_file: *MachO, writer: anytype) !void { const tracy = trace(@src()); defer tracy.end(); @@ -539,41 +445,6 @@ pub const TlvPtrSection = struct { return tlv.symbols.items.len * @sizeOf(u64); } - pub fn addDyldRelocs(tlv: TlvPtrSection, macho_file: *MachO) !void { - const tracy = trace(@src()); - defer tracy.end(); - const gpa = macho_file.base.comp.gpa; - const seg_id = macho_file.sections.items(.segment_id)[macho_file.tlv_ptr_sect_index.?]; - const seg = macho_file.segments.items[seg_id]; - - for (tlv.symbols.items, 0..) |sym_index, idx| { - const sym = macho_file.getSymbol(sym_index); - const addr = tlv.getAddress(@intCast(idx), macho_file); - const entry = bind.Entry{ - .target = sym_index, - .offset = addr - seg.vmaddr, - .segment_id = seg_id, - .addend = 0, - }; - if (sym.flags.import) { - try macho_file.bind.entries.append(gpa, entry); - if (sym.flags.weak) { - try macho_file.weak_bind.entries.append(gpa, entry); - } - } else { - try macho_file.rebase.entries.append(gpa, .{ - .offset = addr - seg.vmaddr, - .segment_id = seg_id, - }); - if (sym.flags.weak) { - try macho_file.weak_bind.entries.append(gpa, entry); - } else if (sym.flags.interposable) { - try macho_file.bind.entries.append(gpa, entry); - } - } - } - } - pub fn write(tlv: TlvPtrSection, macho_file: *MachO, writer: anytype) !void { const tracy = trace(@src()); defer tracy.end(); @@ -772,21 +643,12 @@ pub const Indsymtab = struct { } }; -pub const RebaseSection = Rebase; -pub const BindSection = bind.Bind; -pub const WeakBindSection = bind.WeakBind; -pub const LazyBindSection = bind.LazyBind; -pub const ExportTrieSection = Trie; - const aarch64 = @import("../aarch64.zig"); const assert = std.debug.assert; -const bind = @import("dyld_info/bind.zig"); const math = std.math; const std = @import("std"); const trace = @import("../../tracy.zig").trace; const Allocator = std.mem.Allocator; const MachO = @import("../MachO.zig"); -const Rebase = @import("dyld_info/Rebase.zig"); const Symbol = @import("Symbol.zig"); -const Trie = @import("dyld_info/Trie.zig"); From d136d06a77baeef8680b6a8f334922a8f70da2bf Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 5 Jul 2024 20:02:11 +0200 Subject: [PATCH 03/45] macho: handle ZigObject when calculating dyld relocs --- src/link/MachO/dyld_info/Rebase.zig | 15 ++++++++++++++- src/link/MachO/dyld_info/bind.zig | 6 ++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/link/MachO/dyld_info/Rebase.zig b/src/link/MachO/dyld_info/Rebase.zig index 8348aa01f8..9233a25e07 100644 --- a/src/link/MachO/dyld_info/Rebase.zig +++ b/src/link/MachO/dyld_info/Rebase.zig @@ -25,9 +25,10 @@ pub fn updateSize(rebase: *Rebase, macho_file: *MachO) !void { const gpa = macho_file.base.comp.gpa; - var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 1); + var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 2); defer objects.deinit(); objects.appendSliceAssumeCapacity(macho_file.objects.items); + if (macho_file.getZigObject()) |obj| objects.appendAssumeCapacity(obj.index); if (macho_file.getInternalObject()) |obj| objects.appendAssumeCapacity(obj.index); for (objects.items) |index| { @@ -55,6 +56,18 @@ pub fn updateSize(rebase: *Rebase, macho_file: *MachO) !void { } } + if (macho_file.zig_got_sect_index) |sid| { + const seg_id = macho_file.sections.items(.segment_id)[sid]; + const seg = macho_file.segments.items[seg_id]; + for (0..macho_file.zig_got.entries.items.len) |idx| { + const addr = macho_file.zig_got.entryAddress(@intCast(idx), macho_file); + try rebase.entries.append(gpa, .{ + .offset = addr - seg.vmaddr, + .segment_id = seg_id, + }); + } + } + if (macho_file.got_sect_index) |sid| { const seg_id = macho_file.sections.items(.segment_id)[sid]; const seg = macho_file.segments.items[seg_id]; diff --git a/src/link/MachO/dyld_info/bind.zig b/src/link/MachO/dyld_info/bind.zig index 28e8b1d7d2..8684c66bac 100644 --- a/src/link/MachO/dyld_info/bind.zig +++ b/src/link/MachO/dyld_info/bind.zig @@ -35,9 +35,10 @@ pub const Bind = struct { const gpa = macho_file.base.comp.gpa; const cpu_arch = macho_file.getTarget().cpu.arch; - var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 1); + var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 2); defer objects.deinit(); objects.appendSliceAssumeCapacity(macho_file.objects.items); + if (macho_file.getZigObject()) |obj| objects.appendAssumeCapacity(obj.index); if (macho_file.getInternalObject()) |obj| objects.appendAssumeCapacity(obj.index); for (objects.items) |index| { @@ -286,9 +287,10 @@ pub const WeakBind = struct { const gpa = macho_file.base.comp.gpa; const cpu_arch = macho_file.getTarget().cpu.arch; - var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 1); + var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 2); defer objects.deinit(); objects.appendSliceAssumeCapacity(macho_file.objects.items); + if (macho_file.getZigObject()) |obj| objects.appendAssumeCapacity(obj.index); if (macho_file.getInternalObject()) |obj| objects.appendAssumeCapacity(obj.index); for (objects.items) |index| { From f8cea21514ad18a2b47260783183d047f5a966c0 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 6 Jul 2024 06:46:04 +0200 Subject: [PATCH 04/45] macho: remove obsolete field from InternalObject --- src/link/MachO/InternalObject.zig | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/link/MachO/InternalObject.zig b/src/link/MachO/InternalObject.zig index cbc7f3025c..83bbb8ee54 100644 --- a/src/link/MachO/InternalObject.zig +++ b/src/link/MachO/InternalObject.zig @@ -7,7 +7,6 @@ symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, objc_methnames: std.ArrayListUnmanaged(u8) = .{}, objc_selrefs: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64), -num_rebase_relocs: u32 = 0, output_symtab_ctx: MachO.SymtabCtx = .{}, pub fn deinit(self: *InternalObject, allocator: Allocator) void { @@ -104,7 +103,6 @@ fn addObjcSelrefsSection(self: *InternalObject, methname_atom_index: Atom.Index, }); try atom.addExtra(.{ .rel_index = 0, .rel_count = 1 }, macho_file); atom.flags.relocs = true; - self.num_rebase_relocs += 1; return atom_index; } From 9d5a900f4bf23fc3cf6e2848b9e9c370bba6c938 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 8 Jul 2024 08:01:31 +0200 Subject: [PATCH 05/45] macho: migrate Object to self-ownership of atoms and symbols --- src/link/MachO.zig | 18 +- src/link/MachO/Object.zig | 1274 ++++++++++++++++++++++++++----------- 2 files changed, 905 insertions(+), 387 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index b557eb350e..99424b6f40 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -63,9 +63,6 @@ dso_handle_index: ?Symbol.Index = null, objc_msg_send_index: ?Symbol.Index = null, entry_index: ?Symbol.Index = null, -/// List of atoms that are either synthetic or map directly to the Zig source program. -atoms: std.ArrayListUnmanaged(Atom) = .{}, -atoms_extra: std.ArrayListUnmanaged(u32) = .{}, thunks: std.ArrayListUnmanaged(Thunk) = .{}, /// String interning table @@ -549,13 +546,14 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n try dead_strip.gcAtoms(self); } - self.checkDuplicates() catch |err| switch (err) { - error.HasDuplicates => return error.FlushFailure, - else => |e| { - try self.reportUnexpectedError("unexpected error while checking for duplicate symbol definitions", .{}); - return e; - }, - }; + // TODO + // self.checkDuplicates() catch |err| switch (err) { + // error.HasDuplicates => return error.FlushFailure, + // else => |e| { + // try self.reportUnexpectedError("unexpected error while checking for duplicate symbol definitions", .{}); + // return e; + // }, + // }; self.markImportsAndExports(); self.deadStripDylibs(); diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 30bdce7d9e..fcbb31f4ed 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -12,7 +12,11 @@ symtab: std.MultiArrayList(Nlist) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, +symbols_extra: std.ArrayListUnmanaged(u32) = .{}, +globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{}, atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, +atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{}, +atoms_extra: std.ArrayListUnmanaged(u32) = .{}, platform: ?MachO.Platform = null, compile_unit: ?CompileUnit = null, @@ -30,6 +34,7 @@ data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, alive: bool = true, hidden: bool = false, +compact_unwind_ctx: CompactUnwindCtx = .{}, output_symtab_ctx: MachO.SymtabCtx = .{}, output_ar_state: Archive.ArState = .{}, @@ -51,7 +56,11 @@ pub fn deinit(self: *Object, allocator: Allocator) void { self.symtab.deinit(allocator); self.strtab.deinit(allocator); self.symbols.deinit(allocator); + self.symbols_extra.deinit(allocator); + self.globals.deinit(allocator); self.atoms.deinit(allocator); + self.atoms_indexes.deinit(allocator); + self.atoms_extra.deinit(allocator); self.cies.deinit(allocator); self.fdes.deinit(allocator); self.eh_frame_data.deinit(allocator); @@ -70,6 +79,10 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { const gpa = macho_file.base.comp.gpa; const handle = macho_file.getFileHandle(self.file_handle); + const cpu_arch = macho_file.getTarget().cpu.arch; + + // Atom at index 0 is reserved as null atom + try self.atoms.append(gpa, .{ .extra = try self.addAtomExtra(gpa, .{}) }); var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined; { @@ -86,7 +99,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { return error.InvalidCpuArch; }, }; - if (macho_file.getTarget().cpu.arch != this_cpu_arch) { + if (cpu_arch != this_cpu_arch) { try macho_file.reportParseError2(self.index, "invalid cpu architecture: {s}", .{@tagName(this_cpu_arch)}); return error.InvalidCpuArch; } @@ -198,20 +211,20 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { mem.sort(NlistIdx, nlists.items, self, NlistIdx.lessThan); if (self.hasSubsections()) { - try self.initSubsections(nlists.items, macho_file); + try self.initSubsections(gpa, nlists.items); } else { - try self.initSections(nlists.items, macho_file); + try self.initSections(gpa, nlists.items); } - try self.initCstringLiterals(macho_file); - try self.initFixedSizeLiterals(macho_file); - try self.initPointerLiterals(macho_file); + try self.initCstringLiterals(gpa, handle, macho_file); + try self.initFixedSizeLiterals(gpa, macho_file); + try self.initPointerLiterals(gpa, macho_file); try self.linkNlistToAtom(macho_file); try self.sortAtoms(macho_file); - try self.initSymbols(macho_file); - try self.initSymbolStabs(nlists.items, macho_file); - try self.initRelocs(macho_file); + try self.initSymbols(gpa, macho_file); + try self.initSymbolStabs(gpa, nlists.items, macho_file); + try self.initRelocs(handle, cpu_arch, macho_file); // Parse DWARF __TEXT,__eh_frame section if (self.eh_frame_sect_index) |index| { @@ -224,13 +237,13 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { } if (self.hasUnwindRecords() or self.hasEhFrameRecords()) { - try self.parseUnwindRecords(gpa, macho_file.getTarget().cpu.arch, macho_file); + try self.parseUnwindRecords(gpa, cpu_arch, macho_file); } if (self.platform) |platform| { if (!macho_file.platform.eqlTarget(platform)) { try macho_file.reportParseError2(self.index, "invalid platform: {}", .{ - platform.fmtTarget(macho_file.getTarget().cpu.arch), + platform.fmtTarget(cpu_arch), }); return error.InvalidTarget; } @@ -247,7 +260,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { } for (self.atoms.items) |atom_index| { - const atom = macho_file.getAtom(atom_index).?; + const atom = self.getAtom(atom_index) orelse continue; const isec = atom.getInputSection(macho_file); if (mem.eql(u8, isec.sectName(), "__eh_frame") or mem.eql(u8, isec.sectName(), "__compact_unwind") or @@ -276,10 +289,9 @@ pub fn isPtrLiteral(sect: macho.section_64) bool { return sect.type() == macho.S_LITERAL_POINTERS; } -fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void { +fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void { const tracy = trace(@src()); defer tracy.end(); - const gpa = macho_file.base.comp.gpa; const slice = self.sections.slice(); for (slice.items(.header), slice.items(.subsections), 0..) |sect, *subsections, n_sect| { if (isCstringLiteral(sect)) continue; @@ -294,17 +306,18 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void { } else nlists.len; if (nlist_start == nlist_end or nlists[nlist_start].nlist.n_value > sect.addr) { - const name = try std.fmt.allocPrintZ(gpa, "{s}${s}", .{ sect.segName(), sect.sectName() }); - defer gpa.free(name); + const name = try std.fmt.allocPrintZ(allocator, "{s}${s}", .{ sect.segName(), sect.sectName() }); + defer allocator.free(name); const size = if (nlist_start == nlist_end) sect.size else nlists[nlist_start].nlist.n_value - sect.addr; - const atom_index = try self.addAtom(.{ - .name = try self.addString(gpa, name), + const atom_index = try self.addAtom(allocator, .{ + .name = try self.addString(allocator, name), .n_sect = @intCast(n_sect), .off = 0, .size = size, .alignment = sect.@"align", - }, macho_file); - try subsections.append(gpa, .{ + }); + try self.atoms_indexes.append(allocator, atom_index); + try subsections.append(allocator, .{ .atom = atom_index, .off = 0, }); @@ -327,14 +340,15 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void { @min(@ctz(nlist.nlist.n_value), sect.@"align") else sect.@"align"; - const atom_index = try self.addAtom(.{ + const atom_index = try self.addAtom(allocator, .{ .name = nlist.nlist.n_strx, .n_sect = @intCast(n_sect), .off = nlist.nlist.n_value - sect.addr, .size = size, .alignment = alignment, - }, macho_file); - try subsections.append(gpa, .{ + }); + try self.atoms_indexes.append(allocator, atom_index); + try subsections.append(allocator, .{ .atom = atom_index, .off = nlist.nlist.n_value - sect.addr, }); @@ -346,30 +360,31 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void { } } -fn initSections(self: *Object, nlists: anytype, macho_file: *MachO) !void { +fn initSections(self: *Object, allocator: Allocator, nlists: anytype) !void { const tracy = trace(@src()); defer tracy.end(); - const gpa = macho_file.base.comp.gpa; const slice = self.sections.slice(); - try self.atoms.ensureUnusedCapacity(gpa, self.sections.items(.header).len); + try self.atoms.ensureUnusedCapacity(allocator, self.sections.items(.header).len); + try self.atoms_indexes.ensureUnusedCapacity(allocator, self.sections.items(.header).len); for (slice.items(.header), 0..) |sect, n_sect| { if (isCstringLiteral(sect)) continue; if (isFixedSizeLiteral(sect)) continue; if (isPtrLiteral(sect)) continue; - const name = try std.fmt.allocPrintZ(gpa, "{s}${s}", .{ sect.segName(), sect.sectName() }); - defer gpa.free(name); + const name = try std.fmt.allocPrintZ(allocator, "{s}${s}", .{ sect.segName(), sect.sectName() }); + defer allocator.free(name); - const atom_index = try self.addAtom(.{ - .name = try self.addString(gpa, name), + const atom_index = try self.addAtom(allocator, .{ + .name = try self.addString(allocator, name), .n_sect = @intCast(n_sect), .off = 0, .size = sect.size, .alignment = sect.@"align", - }, macho_file); - try slice.items(.subsections)[n_sect].append(gpa, .{ .atom = atom_index, .off = 0 }); + }); + try self.atoms_indexes.append(allocator, atom_index); + try slice.items(.subsections)[n_sect].append(allocator, .{ .atom = atom_index, .off = 0 }); const nlist_start = for (nlists, 0..) |nlist, i| { if (nlist.nlist.n_sect - 1 == n_sect) break i; @@ -398,21 +413,25 @@ fn initSections(self: *Object, nlists: anytype, macho_file: *MachO) !void { } } -fn initCstringLiterals(self: *Object, macho_file: *MachO) !void { +fn initCstringLiterals(self: *Object, allocator: Allocator, file: File.Handle, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - const gpa = macho_file.base.comp.gpa; const slice = self.sections.slice(); for (slice.items(.header), 0..) |sect, n_sect| { if (!isCstringLiteral(sect)) continue; - const data = try self.getSectionData(@intCast(n_sect), macho_file); - defer gpa.free(data); + const sect_size = math.cast(usize, sect.size) orelse return error.Overflow; + const data = try allocator.alloc(u8, sect_size); + defer allocator.free(data); + const amt = try file.preadAll(data, sect.offset + self.offset); + if (amt != data.len) return error.InputOutput; + var count: u32 = 0; var start: u32 = 0; while (start < data.len) { + defer count += 1; var end = start; while (end < data.len - 1 and data[end] != 0) : (end += 1) {} if (data[end] != 0) { @@ -425,32 +444,52 @@ fn initCstringLiterals(self: *Object, macho_file: *MachO) !void { } end += 1; - const atom_index = try self.addAtom(.{ - .name = 0, + const name = try std.fmt.allocPrintZ(allocator, "l._str{d}", .{count}); + defer allocator.free(name); + const name_str = try self.addString(allocator, name); + + const atom_index = try self.addAtom(allocator, .{ + .name = name_str, .n_sect = @intCast(n_sect), .off = start, .size = end - start, .alignment = sect.@"align", - }, macho_file); - try slice.items(.subsections)[n_sect].append(gpa, .{ + }); + try self.atoms_indexes.append(allocator, atom_index); + try slice.items(.subsections)[n_sect].append(allocator, .{ .atom = atom_index, .off = start, }); + const atom = self.getAtom(atom_index).?; + const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator)); + self.symtab.set(nlist_index, .{ + .nlist = .{ + .n_strx = name_str, + .n_type = macho.N_SECT, + .n_sect = @intCast(atom.n_sect + 1), + .n_desc = 0, + .n_value = atom.getInputAddress(macho_file), + }, + .size = atom.size, + .atom = atom_index, + }); + atom.addExtra(.{ .literal_symbol_index = nlist_index }, macho_file); + start = end; } } } -fn initFixedSizeLiterals(self: *Object, macho_file: *MachO) !void { +fn initFixedSizeLiterals(self: *Object, allocator: Allocator, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - const gpa = macho_file.base.comp.gpa; const slice = self.sections.slice(); for (slice.items(.header), 0..) |sect, n_sect| { if (!isFixedSizeLiteral(sect)) continue; + const rec_size: u8 = switch (sect.type()) { macho.S_4BYTE_LITERALS => 4, macho.S_8BYTE_LITERALS => 8, @@ -465,28 +504,52 @@ fn initFixedSizeLiterals(self: *Object, macho_file: *MachO) !void { ); return error.MalformedObject; } + var pos: u32 = 0; - while (pos < sect.size) : (pos += rec_size) { - const atom_index = try self.addAtom(.{ - .name = 0, + var count: u32 = 0; + while (pos < sect.size) : ({ + pos += rec_size; + count += 1; + }) { + const name = try std.fmt.allocPrintZ(allocator, "l._literal{d}", .{count}); + defer allocator.free(name); + const name_str = try self.addString(allocator, name); + + const atom_index = try self.addAtom(allocator, .{ + .name = name_str, .n_sect = @intCast(n_sect), .off = pos, .size = rec_size, .alignment = sect.@"align", - }, macho_file); - try slice.items(.subsections)[n_sect].append(gpa, .{ + }); + try self.atoms_indexes.append(allocator, atom_index); + try slice.items(.subsections)[n_sect].append(allocator, .{ .atom = atom_index, .off = pos, }); + + const atom = self.getAtom(atom_index).?; + const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator)); + self.symtab.set(nlist_index, .{ + .nlist = .{ + .n_strx = name_str, + .n_type = macho.N_SECT, + .n_sect = @intCast(atom.n_sect + 1), + .n_desc = 0, + .n_value = atom.getInputAddress(macho_file), + }, + .size = atom.size, + .atom = atom_index, + }); + atom.addExtra(.{ .literal_symbol_index = nlist_index }, macho_file); } } } -fn initPointerLiterals(self: *Object, macho_file: *MachO) !void { +fn initPointerLiterals(self: *Object, allocator: Allocator, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - const gpa = macho_file.base.comp.gpa; const slice = self.sections.slice(); for (slice.items(.header), 0..) |sect, n_sect| { @@ -505,134 +568,171 @@ fn initPointerLiterals(self: *Object, macho_file: *MachO) !void { for (0..num_ptrs) |i| { const pos: u32 = @as(u32, @intCast(i)) * rec_size; - const atom_index = try self.addAtom(.{ - .name = 0, + + const name = try std.fmt.allocPrintZ(allocator, "l._ptr{d}", .{i}); + defer allocator.free(name); + const name_str = try self.addString(allocator, name); + + const atom_index = try self.addAtom(allocator, .{ + .name = name_str, .n_sect = @intCast(n_sect), .off = pos, .size = rec_size, .alignment = sect.@"align", - }, macho_file); - try slice.items(.subsections)[n_sect].append(gpa, .{ + }); + try self.atoms_indexes.append(allocator, atom_index); + try slice.items(.subsections)[n_sect].append(allocator, .{ .atom = atom_index, .off = pos, }); + + const atom = self.getAtom(atom_index).?; + const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator)); + self.symtab.set(nlist_index, .{ + .nlist = .{ + .n_strx = name_str, + .n_type = macho.N_SECT, + .n_sect = @intCast(atom.n_sect + 1), + .n_desc = 0, + .n_value = atom.getInputAddress(macho_file), + }, + .size = atom.size, + .atom = atom_index, + }); + atom.addExtra(.{ .literal_symbol_index = nlist_index }, macho_file); } } } pub fn resolveLiterals(self: Object, lp: *MachO.LiteralPool, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + const gpa = macho_file.base.comp.gpa; + const file = macho_file.getFileHandle(self.file_handle); var buffer = std.ArrayList(u8).init(gpa); defer buffer.deinit(); + var sections_data = std.AutoHashMap(u32, []const u8).init(gpa); + try sections_data.ensureTotalCapacity(@intCast(self.sections.items(.header).len)); + defer { + var it = sections_data.iterator(); + while (it.next()) |entry| { + gpa.free(entry.value_ptr.*); + } + sections_data.deinit(); + } + const slice = self.sections.slice(); - for (slice.items(.header), slice.items(.subsections), 0..) |header, subs, n_sect| { + for (slice.items(.header), slice.items(.subsections)) |header, subs| { if (isCstringLiteral(header) or isFixedSizeLiteral(header)) { - const data = try self.getSectionData(@intCast(n_sect), macho_file); + const sect_size = math.cast(usize, header.size) orelse return error.Overflow; + const data = try gpa.alloc(u8, sect_size); defer gpa.free(data); + const amt = try file.preadAll(data, header.offset + self.offset); + if (amt != data.len) return error.InputOutput; for (subs.items) |sub| { - const atom = macho_file.getAtom(sub.atom).?; + const atom = self.getAtom(sub.atom).?; const atom_off = math.cast(usize, atom.off) orelse return error.Overflow; const atom_size = math.cast(usize, atom.size) orelse return error.Overflow; const atom_data = data[atom_off..][0..atom_size]; const res = try lp.insert(gpa, header.type(), atom_data); if (!res.found_existing) { - res.atom.* = sub.atom; + res.ref.* = .{ .index = atom.getExtra(macho_file).literal_symbol_index, .file = self.index }; + } else { + const lp_sym = lp.getSymbol(res.index, macho_file); + const lp_atom = lp_sym.getAtom(macho_file).?; + lp_atom.alignment = @max(lp_atom.alignment, atom.alignment); + atom.flags.alive = false; } - atom.flags.literal_pool = true; - try atom.addExtra(.{ .literal_index = res.index }, macho_file); + atom.addExtra(.{ .literal_pool_index = res.index }, macho_file); } } else if (isPtrLiteral(header)) { for (subs.items) |sub| { - const atom = macho_file.getAtom(sub.atom).?; + const atom = self.getAtom(sub.atom).?; const relocs = atom.getRelocs(macho_file); assert(relocs.len == 1); const rel = relocs[0]; const target = switch (rel.tag) { - .local => rel.target, - .@"extern" => rel.getTargetSymbol(macho_file).atom, + .local => rel.getTargetAtom(atom.*, macho_file), + .@"extern" => rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?, }; const addend = math.cast(u32, rel.addend) orelse return error.Overflow; - const target_atom = macho_file.getAtom(target).?; - const target_atom_size = math.cast(usize, target_atom.size) orelse return error.Overflow; - try buffer.ensureUnusedCapacity(target_atom_size); - buffer.resize(target_atom_size) catch unreachable; - try target_atom.getData(macho_file, buffer.items); + const target_size = math.cast(usize, target.size) orelse return error.Overflow; + try buffer.ensureUnusedCapacity(target_size); + buffer.resize(target_size) catch unreachable; + const gop = try sections_data.getOrPut(target.n_sect); + if (!gop.found_existing) { + const target_sect = slice.items(.header)[target.n_sect]; + const target_sect_size = math.cast(usize, target_sect.size) orelse return error.Overflow; + const data = try gpa.alloc(u8, target_sect_size); + const amt = try file.preadAll(data, target_sect.offset + self.offset); + if (amt != data.len) return error.InputOutput; + gop.value_ptr.* = data; + } + const data = gop.value_ptr.*; + const target_off = math.cast(usize, target.off) orelse return error.Overflow; + @memcpy(buffer.items, data[target_off..][0..target_size]); const res = try lp.insert(gpa, header.type(), buffer.items[addend..]); buffer.clearRetainingCapacity(); if (!res.found_existing) { - res.atom.* = sub.atom; + res.ref.* = .{ .index = atom.getExtra(macho_file).literal_symbol_index, .file = self.index }; + } else { + const lp_sym = lp.getSymbol(res.index, macho_file); + const lp_atom = lp_sym.getAtom(macho_file).?; + lp_atom.alignment = @max(lp_atom.alignment, atom.alignment); + atom.flags.alive = false; } - atom.flags.literal_pool = true; - try atom.addExtra(.{ .literal_index = res.index }, macho_file); + atom.addExtra(.{ .literal_pool_index = res.index }, macho_file); } } } } -pub fn dedupLiterals(self: Object, lp: MachO.LiteralPool, macho_file: *MachO) void { - for (self.atoms.items) |atom_index| { - const atom = macho_file.getAtom(atom_index) orelse continue; +pub fn dedupLiterals(self: *Object, lp: MachO.LiteralPool, macho_file: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); + + for (self.getAtoms()) |atom_index| { + const atom = self.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; - if (!atom.flags.relocs) continue; const relocs = blk: { - const extra = atom.getExtra(macho_file).?; + const extra = atom.getExtra(macho_file); const relocs = self.sections.items(.relocs)[atom.n_sect].items; break :blk relocs[extra.rel_index..][0..extra.rel_count]; }; - for (relocs) |*rel| switch (rel.tag) { - .local => { - const target = macho_file.getAtom(rel.target).?; - if (target.getLiteralPoolIndex(macho_file)) |lp_index| { - const lp_atom = lp.getAtom(lp_index, macho_file); - if (target.atom_index != lp_atom.atom_index) { - lp_atom.alignment = lp_atom.alignment.max(target.alignment); - target.flags.alive = false; - rel.target = lp_atom.atom_index; - } - } - }, - .@"extern" => { - const target_sym = rel.getTargetSymbol(macho_file); - if (target_sym.getAtom(macho_file)) |target_atom| { - if (target_atom.getLiteralPoolIndex(macho_file)) |lp_index| { - const lp_atom = lp.getAtom(lp_index, macho_file); - if (target_atom.atom_index != lp_atom.atom_index) { - lp_atom.alignment = lp_atom.alignment.max(target_atom.alignment); - target_atom.flags.alive = false; - target_sym.atom = lp_atom.atom_index; - } - } - } - }, - }; + for (relocs) |*rel| { + if (rel.tag != .@"extern") continue; + const target_sym_ref = rel.getTargetSymbolRef(atom.*, macho_file); + const file = target_sym_ref.getFile(macho_file) orelse continue; + if (file.getIndex() != self.index) continue; + const target_sym = target_sym_ref.getSymbol(macho_file).?; + const target_atom = target_sym.getAtom(macho_file) orelse continue; + const isec = target_atom.getInputSection(macho_file); + if (!Object.isCstringLiteral(isec) and !Object.isFixedSizeLiteral(isec) and !Object.isPtrLiteral(isec)) continue; + const lp_index = target_atom.getExtra(macho_file).literal_pool_index; + const lp_sym = lp.getSymbol(lp_index, macho_file); + const lp_atom_ref = lp_sym.atom_ref; + if (target_atom.atom_index != lp_atom_ref.index or target_atom.file != lp_atom_ref.file) { + target_sym.atom_ref = lp_atom_ref; + } + } } -} -const AddAtomArgs = struct { - name: u32, - n_sect: u8, - off: u64, - size: u64, - alignment: u32, -}; - -fn addAtom(self: *Object, args: AddAtomArgs, macho_file: *MachO) !Atom.Index { - const gpa = macho_file.base.comp.gpa; - const atom_index = try macho_file.addAtom(); - const atom = macho_file.getAtom(atom_index).?; - atom.file = self.index; - atom.atom_index = atom_index; - atom.name = args.name; - atom.n_sect = args.n_sect; - atom.size = args.size; - atom.alignment = Atom.Alignment.fromLog2Units(args.alignment); - atom.off = args.off; - try self.atoms.append(gpa, atom_index); - return atom_index; + for (self.symbols.items) |*sym| { + const atom = sym.getAtom(macho_file) orelse continue; + const isec = atom.getInputSection(macho_file); + if (!Object.isCstringLiteral(isec) and !Object.isFixedSizeLiteral(isec) and !Object.isPtrLiteral(isec)) continue; + const lp_index = atom.getExtra(macho_file).literal_pool_index; + const lp_sym = lp.getSymbol(lp_index, macho_file); + const lp_atom_ref = lp_sym.atom_ref; + if (atom.atom_index != lp_atom_ref.index or self.index != lp_atom_ref.file) { + sym.atom_ref = lp_atom_ref; + } + } } pub fn findAtom(self: Object, addr: u64) ?Atom.Index { @@ -704,55 +804,59 @@ fn linkNlistToAtom(self: *Object, macho_file: *MachO) !void { } } -fn initSymbols(self: *Object, macho_file: *MachO) !void { +fn initSymbols(self: *Object, allocator: Allocator, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - const gpa = macho_file.base.comp.gpa; - const slice = self.symtab.slice(); - try self.symbols.ensureUnusedCapacity(gpa, slice.items(.nlist).len); + const slice = self.symtab.slice(); + const nsyms = slice.items(.nlist).len; + + try self.symbols.ensureTotalCapacityPrecise(allocator, nsyms); + try self.symbols_extra.ensureTotalCapacityPrecise(allocator, nsyms * @sizeOf(Symbol.Extra)); + try self.globals.ensureTotalCapacityPrecise(allocator, nsyms); + self.globals.resize(allocator, nsyms) catch unreachable; + @memset(self.globals.items, 0); for (slice.items(.nlist), slice.items(.atom), 0..) |nlist, atom_index, i| { - if (nlist.ext()) { - const name = self.getString(nlist.n_strx); - const off = try macho_file.strings.insert(gpa, name); - const gop = try macho_file.getOrCreateGlobal(off); - self.symbols.addOneAssumeCapacity().* = gop.index; - if (nlist.undf() and nlist.weakRef()) { - macho_file.getSymbol(gop.index).flags.weak_ref = true; - } - continue; - } + const index = self.addSymbolAssumeCapacity(); + const symbol = &self.symbols.items[index]; + symbol.value = nlist.n_value; + symbol.name = .{ .pos = nlist.n_strx, .len = @intCast(self.getNStrx(nlist.n_strx).len + 1) }; + symbol.nlist_idx = @intCast(i); + symbol.extra = self.addSymbolExtraAssumeCapacity(.{}); - const index = try macho_file.addSymbol(); - self.symbols.appendAssumeCapacity(index); - const symbol = macho_file.getSymbol(index); - symbol.* = .{ - .value = nlist.n_value, - .name = nlist.n_strx, - .nlist_idx = @intCast(i), - .atom = 0, - .file = self.index, - }; - - if (macho_file.getAtom(atom_index)) |atom| { + if (self.getAtom(atom_index)) |atom| { assert(!nlist.abs()); symbol.value -= atom.getInputAddress(macho_file); - symbol.atom = atom_index; + symbol.atom_ref = .{ .index = atom_index, .file = self.index }; } + symbol.flags.weak = nlist.weakDef(); symbol.flags.abs = nlist.abs(); + symbol.flags.tentative = nlist.tentative(); symbol.flags.no_dead_strip = symbol.flags.no_dead_strip or nlist.noDeadStrip(); + symbol.flags.dyn_ref = nlist.n_desc & macho.REFERENCED_DYNAMICALLY != 0; + symbol.flags.interposable = nlist.ext() and (nlist.sect() or nlist.abs()) and macho_file.options.dylib and macho_file.options.namespace == .flat and !nlist.pext(); if (nlist.sect() and self.sections.items(.header)[nlist.n_sect - 1].type() == macho.S_THREAD_LOCAL_VARIABLES) { symbol.flags.tlv = true; } + + if (nlist.ext()) { + if (nlist.undf()) { + symbol.flags.weak_ref = nlist.weakRef(); + } else if (nlist.pext() or (nlist.weakDef() and nlist.weakRef()) or self.hidden) { + symbol.visibility = .hidden; + } else { + symbol.visibility = .global; + } + } } } -fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void { +fn initSymbolStabs(self: *Object, allocator: Allocator, nlists: anytype, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); @@ -778,14 +882,13 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void { if (start == end) return; - const gpa = macho_file.base.comp.gpa; const syms = self.symtab.items(.nlist); const sym_lookup = SymbolLookup{ .ctx = self, .entries = nlists }; // We need to cache nlists by name so that we can properly resolve local N_GSYM stabs. // What happens is `ld -r` will emit an N_GSYM stab for a symbol that may be either an // external or private external. - var addr_lookup = std.StringHashMap(u64).init(gpa); + var addr_lookup = std.StringHashMap(u64).init(allocator); defer addr_lookup.deinit(); for (syms) |sym| { if (sym.sect() and (sym.ext() or sym.pext())) { @@ -834,29 +937,35 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void { return error.MalformedObject; }, } - try sf.stabs.append(gpa, stab); + try sf.stabs.append(allocator, stab); } - try self.stab_files.append(gpa, sf); + try self.stab_files.append(allocator, sf); } } fn sortAtoms(self: *Object, macho_file: *MachO) !void { - const lessThanAtom = struct { - fn lessThanAtom(ctx: *MachO, lhs: Atom.Index, rhs: Atom.Index) bool { - return ctx.getAtom(lhs).?.getInputAddress(ctx) < ctx.getAtom(rhs).?.getInputAddress(ctx); + const Ctx = struct { + object: *Object, + mfile: *MachO, + + fn lessThanAtom(ctx: @This(), lhs: Atom.Index, rhs: Atom.Index) bool { + return ctx.object.getAtom(lhs).?.getInputAddress(ctx.mfile) < + ctx.object.getAtom(rhs).?.getInputAddress(ctx.mfile); } - }.lessThanAtom; - mem.sort(Atom.Index, self.atoms.items, macho_file, lessThanAtom); + }; + mem.sort(Atom.Index, self.atoms_indexes.items, Ctx{ + .object = self, + .mfile = macho_file, + }, Ctx.lessThanAtom); } -fn initRelocs(self: *Object, macho_file: *MachO) !void { +fn initRelocs(self: *Object, file: File.Handle, cpu_arch: std.Target.Cpu.Arch, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - const cpu_arch = macho_file.getTarget().cpu.arch; const slice = self.sections.slice(); - for (slice.items(.header), slice.items(.relocs), 0..) |sect, *out, n_sect| { + for (slice.items(.header), slice.items(.relocs)) |sect, *out| { if (sect.nreloc == 0) continue; // We skip relocs for __DWARF since even in -r mode, the linker is expected to emit // debug symbol stabs in the relocatable. This made me curious why that is. For now, @@ -865,8 +974,8 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void { !mem.eql(u8, sect.sectName(), "__compact_unwind")) continue; switch (cpu_arch) { - .x86_64 => try x86_64.parseRelocs(self, @intCast(n_sect), sect, out, macho_file), - .aarch64 => try aarch64.parseRelocs(self, @intCast(n_sect), sect, out, macho_file), + .x86_64 => try x86_64.parseRelocs(self, sect, out, file, macho_file), + .aarch64 => try aarch64.parseRelocs(self, sect, out, file, macho_file), else => unreachable, } @@ -878,7 +987,7 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void { var next_reloc: u32 = 0; for (subsections.items) |subsection| { - const atom = macho_file.getAtom(subsection.atom).?; + const atom = self.getAtom(subsection.atom).?; if (!atom.flags.alive) continue; if (next_reloc >= relocs.items.len) break; const end_addr = atom.off + atom.size; @@ -887,27 +996,22 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void { while (next_reloc < relocs.items.len and relocs.items[next_reloc].offset < end_addr) : (next_reloc += 1) {} const rel_count = next_reloc - rel_index; - try atom.addExtra(.{ .rel_index = rel_index, .rel_count = rel_count }, macho_file); - atom.flags.relocs = true; + atom.addExtra(.{ .rel_index = @intCast(rel_index), .rel_count = @intCast(rel_count) }, macho_file); } } } -fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void { +fn initEhFrameRecords(self: *Object, allocator: Allocator, sect_id: u8, file: File.Handle, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - const gpa = macho_file.base.comp.gpa; const nlists = self.symtab.items(.nlist); const slice = self.sections.slice(); const sect = slice.items(.header)[sect_id]; const relocs = slice.items(.relocs)[sect_id]; - // TODO: read into buffer directly - const data = try self.getSectionData(sect_id, macho_file); - defer gpa.free(data); - - try self.eh_frame_data.ensureTotalCapacityPrecise(gpa, data.len); - self.eh_frame_data.appendSliceAssumeCapacity(data); + try self.eh_frame_data.resize(allocator, sect.size); + const amt = try file.preadAll(self.eh_frame_data.items, sect.offset + self.offset); + if (amt != self.eh_frame_data.items.len) return error.InputOutput; // Check for non-personality relocs in FDEs and apply them for (relocs.items, 0..) |rel, i| { @@ -939,12 +1043,12 @@ fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void { var it = eh_frame.Iterator{ .data = self.eh_frame_data.items }; while (try it.next()) |rec| { switch (rec.tag) { - .cie => try self.cies.append(gpa, .{ + .cie => try self.cies.append(allocator, .{ .offset = rec.offset, .size = rec.size, .file = self.index, }), - .fde => try self.fdes.append(gpa, .{ + .fde => try self.fdes.append(allocator, .{ .offset = rec.offset, .size = rec.size, .cie = undefined, @@ -1205,8 +1309,7 @@ fn parseUnwindRecords(self: *Object, allocator: Allocator, cpu_arch: std.Target. {} const atom = rec.getAtom(macho_file); - try atom.addExtra(.{ .unwind_index = start, .unwind_count = next_cu - start }, macho_file); - atom.flags.unwind = true; + atom.addExtra(.{ .unwind_index = start, .unwind_count = next_cu - start }, macho_file); } } @@ -1233,11 +1336,31 @@ pub fn parseDebugInfo(self: *Object, macho_file: *MachO) !void { if (debug_info_index == null or debug_abbrev_index == null) return; - const debug_info = try self.getSectionData(@intCast(debug_info_index.?), macho_file); + const slice = self.sections.slice(); + const file = macho_file.getFileHandle(self.file_handle); + const debug_info = blk: { + const sect = slice.items(.header)[debug_info_index.?]; + const data = try gpa.alloc(u8, sect.size); + const amt = try file.preadAll(data, sect.offset + self.offset); + if (amt != data.len) return error.InputOutput; + break :blk data; + }; defer gpa.free(debug_info); - const debug_abbrev = try self.getSectionData(@intCast(debug_abbrev_index.?), macho_file); + const debug_abbrev = blk: { + const sect = slice.items(.header)[debug_abbrev_index.?]; + const data = try gpa.alloc(u8, sect.size); + const amt = try file.preadAll(data, sect.offset + self.offset); + if (amt != data.len) return error.InputOutput; + break :blk data; + }; defer gpa.free(debug_abbrev); - const debug_str = if (debug_str_index) |index| try self.getSectionData(@intCast(index), macho_file) else &[0]u8{}; + const debug_str = if (debug_str_index) |sid| blk: { + const sect = slice.items(.header)[sid]; + const data = try gpa.alloc(u8, sect.size); + const amt = try file.preadAll(data, sect.offset + self.offset); + if (amt != data.len) return error.InputOutput; + break :blk data; + } else &[0]u8{}; defer gpa.free(debug_str); self.compile_unit = self.findCompileUnit(.{ @@ -1347,70 +1470,37 @@ pub fn resolveSymbols(self: *Object, macho_file: *MachO) void { const tracy = trace(@src()); defer tracy.end(); - for (self.symbols.items, 0..) |index, i| { - const nlist_idx = @as(Symbol.Index, @intCast(i)); - const nlist = self.symtab.items(.nlist)[nlist_idx]; - const atom_index = self.symtab.items(.atom)[nlist_idx]; + const gpa = macho_file.base.allocator; + for (self.symtab.items(.nlist), self.symtab.items(.atom), self.globals.items, 0..) |nlist, atom_index, *global, i| { if (!nlist.ext()) continue; - if (nlist.undf() and !nlist.tentative()) continue; if (nlist.sect()) { - const atom = macho_file.getAtom(atom_index).?; - if (!atom.flags.alive) continue; + const atom = self.getAtom(atom_index).?; + if (!atom.alive.load(.seq_cst)) continue; + } + + const gop = try macho_file.resolver.getOrPut(gpa, .{ + .index = @intCast(i), + .file = self.index, + }, macho_file); + if (!gop.found_existing) { + gop.ref.* = .{ .index = 0, .file = 0 }; + } + global.* = gop.index; + + if (nlist.undf() and !nlist.tentative()) continue; + if (gop.ref.getFile(macho_file) == null) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; + continue; } - const symbol = macho_file.getSymbol(index); if (self.asFile().getSymbolRank(.{ .archive = !self.alive, .weak = nlist.weakDef(), .tentative = nlist.tentative(), - }) < symbol.getSymbolRank(macho_file)) { - const value = if (nlist.sect()) blk: { - const atom = macho_file.getAtom(atom_index).?; - break :blk nlist.n_value - atom.getInputAddress(macho_file); - } else nlist.n_value; - symbol.value = value; - symbol.atom = atom_index; - symbol.nlist_idx = nlist_idx; - symbol.file = self.index; - symbol.flags.weak = nlist.weakDef(); - symbol.flags.abs = nlist.abs(); - symbol.flags.tentative = nlist.tentative(); - symbol.flags.weak_ref = false; - symbol.flags.dyn_ref = nlist.n_desc & macho.REFERENCED_DYNAMICALLY != 0; - symbol.flags.no_dead_strip = symbol.flags.no_dead_strip or nlist.noDeadStrip(); - // TODO: symbol.flags.interposable = macho_file.base.isDynLib() and macho_file.options.namespace == .flat and !nlist.pext(); - symbol.flags.interposable = false; - - if (nlist.sect() and - self.sections.items(.header)[nlist.n_sect - 1].type() == macho.S_THREAD_LOCAL_VARIABLES) - { - symbol.flags.tlv = true; - } + }) < gop.ref.getSymbol(macho_file).?.getSymbolRank(macho_file)) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; } - - // Regardless of who the winner is, we still merge symbol visibility here. - if (nlist.pext() or (nlist.weakDef() and nlist.weakRef()) or self.hidden) { - if (symbol.visibility != .global) { - symbol.visibility = .hidden; - } - } else { - symbol.visibility = .global; - } - } -} - -pub fn resetGlobals(self: *Object, macho_file: *MachO) void { - for (self.symbols.items, 0..) |sym_index, nlist_idx| { - if (!self.symtab.items(.nlist)[nlist_idx].ext()) continue; - const sym = macho_file.getSymbol(sym_index); - const name = sym.name; - const global = sym.flags.global; - const weak_ref = sym.flags.weak_ref; - sym.* = .{}; - sym.name = name; - sym.flags.global = global; - sym.flags.weak_ref = weak_ref; } } @@ -1418,12 +1508,13 @@ pub fn markLive(self: *Object, macho_file: *MachO) void { const tracy = trace(@src()); defer tracy.end(); - for (self.symbols.items, 0..) |index, nlist_idx| { - const nlist = self.symtab.items(.nlist)[nlist_idx]; + for (0..self.symbols.items.len) |i| { + const nlist = self.symtab.items(.nlist)[i]; if (!nlist.ext()) continue; - const sym = macho_file.getSymbol(index); - const file = sym.getFile(macho_file) orelse continue; + const ref = self.getSymbolRef(@intCast(i), macho_file); + const file = ref.getFile(macho_file) orelse continue; + const sym = ref.getSymbol(macho_file).?; const should_keep = nlist.undf() or (nlist.tentative() and !sym.flags.tentative); if (should_keep and file == .object and !file.object.alive) { file.object.alive = true; @@ -1432,30 +1523,47 @@ pub fn markLive(self: *Object, macho_file: *MachO) void { } } -pub fn checkDuplicates(self: *Object, dupes: anytype, macho_file: *MachO) error{OutOfMemory}!void { - for (self.symbols.items, 0..) |index, nlist_idx| { - const sym = macho_file.getSymbol(index); - if (sym.visibility != .global) continue; - const file = sym.getFile(macho_file) orelse continue; - if (file.getIndex() == self.index) continue; +pub fn mergeSymbolVisibility(self: *Object, macho_file: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); - const nlist = self.symtab.items(.nlist)[nlist_idx]; - if (!nlist.undf() and !nlist.tentative() and !(nlist.weakDef() or nlist.pext())) { - const gop = try dupes.getOrPut(index); - if (!gop.found_existing) { - gop.value_ptr.* = .{}; - } - try gop.value_ptr.append(macho_file.base.comp.gpa, self.index); + for (self.symbols.items, 0..) |sym, i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + const global = ref.getSymbol(macho_file) orelse continue; + if (global.visibility != .global) { + global.visibility = sym.visibility; + } + if (sym.flags.weak_ref) { + global.flags.weak_ref = true; } } } +// TODO +// pub fn checkDuplicates(self: *Object, dupes: anytype, macho_file: *MachO) error{OutOfMemory}!void { +// for (self.symbols.items, 0..) |index, nlist_idx| { +// const sym = macho_file.getSymbol(index); +// if (sym.visibility != .global) continue; +// const file = sym.getFile(macho_file) orelse continue; +// if (file.getIndex() == self.index) continue; + +// const nlist = self.symtab.items(.nlist)[nlist_idx]; +// if (!nlist.undf() and !nlist.tentative() and !(nlist.weakDef() or nlist.pext())) { +// const gop = try dupes.getOrPut(index); +// if (!gop.found_existing) { +// gop.value_ptr.* = .{}; +// } +// try gop.value_ptr.append(macho_file.base.comp.gpa, self.index); +// } +// } +// } + pub fn scanRelocs(self: *Object, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - for (self.atoms.items) |atom_index| { - const atom = macho_file.getAtom(atom_index).?; + for (self.getAtoms()) |atom_index| { + const atom = self.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; const sect = atom.getInputSection(macho_file); if (sect.isZerofill()) continue; @@ -1480,38 +1588,35 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void { defer tracy.end(); const gpa = macho_file.base.comp.gpa; - for (self.symbols.items, 0..) |index, i| { - const sym = macho_file.getSymbol(index); + for (self.symbols.items, self.globals.items, 0..) |*sym, off, i| { if (!sym.flags.tentative) continue; - const sym_file = sym.getFile(macho_file).?; - if (sym_file.getIndex() != self.index) continue; + if (macho_file.resolver.get(off).?.file != self.index) continue; const nlist_idx = @as(Symbol.Index, @intCast(i)); const nlist = &self.symtab.items(.nlist)[nlist_idx]; const nlist_atom = &self.symtab.items(.atom)[nlist_idx]; - const atom_index = try macho_file.addAtom(); - try self.atoms.append(gpa, atom_index); - const name = try std.fmt.allocPrintZ(gpa, "__DATA$__common${s}", .{sym.getName(macho_file)}); defer gpa.free(name); - const atom = macho_file.getAtom(atom_index).?; - atom.atom_index = atom_index; - atom.name = try self.addString(gpa, name); - atom.file = self.index; - atom.size = nlist.n_value; - atom.alignment = Atom.Alignment.fromLog2Units((nlist.n_desc >> 8) & 0x0f); + const alignment = (nlist.n_desc >> 8) & 0x0f; const n_sect = try self.addSection(gpa, "__DATA", "__common"); + const atom_index = try self.addAtom(gpa, .{ + .name = try self.addString(gpa, name), + .n_sect = n_sect, + .off = 0, + .size = nlist.n_value, + .alignment = alignment, + }); + try self.atoms_indexes.append(gpa, atom_index); + const sect = &self.sections.items(.header)[n_sect]; sect.flags = macho.S_ZEROFILL; - sect.size = atom.size; - sect.@"align" = atom.alignment.toLog2Units(); - atom.n_sect = n_sect; + sect.size = nlist.n_value; + sect.@"align" = alignment; sym.value = 0; - sym.atom = atom_index; - sym.flags.global = true; + sym.atom_ref = .{ .index = atom_index, .file = self.index }; sym.flags.weak = false; sym.flags.weak_ref = false; sym.flags.tentative = false; @@ -1525,6 +1630,56 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void { } } +pub fn claimUnresolved(self: *Object, macho_file: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); + + for (self.symbols.items, 0..) |*sym, i| { + const nlist = self.symtab.items(.nlist)[i]; + if (!nlist.ext()) continue; + if (!nlist.undf()) continue; + + if (self.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue; + + const is_import = switch (macho_file.options.undefined_treatment) { + .@"error" => false, + .warn, .suppress => nlist.weakRef(), + .dynamic_lookup => true, + }; + if (is_import) { + sym.value = 0; + sym.atom_ref = .{ .index = 0, .file = 0 }; + sym.flags.weak = false; + sym.flags.weak_ref = nlist.weakRef(); + sym.flags.import = is_import; + sym.visibility = .global; + + const idx = self.globals.items[i]; + macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = self.index }; + } + } +} + +pub fn claimUnresolvedRelocatable(self: *Object, macho_file: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); + + for (self.symbols.items, self.symtab.items(.nlist), 0..) |*sym, nlist, i| { + if (!nlist.ext()) continue; + if (!nlist.undf()) continue; + if (self.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue; + + sym.value = 0; + sym.atom_ref = .{ .index = 0, .file = 0 }; + sym.flags.weak_ref = nlist.weakRef(); + sym.flags.import = true; + sym.visibility = .global; + + const idx = self.globals.items[i]; + macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = self.index }; + } +} + fn addSection(self: *Object, allocator: Allocator, segname: []const u8, sectname: []const u8) !u32 { const n_sect = @as(u32, @intCast(try self.sections.addOne(allocator))); self.sections.set(n_sect, .{ @@ -1646,19 +1801,22 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - for (self.symbols.items) |sym_index| { - const sym = macho_file.getSymbol(sym_index); - const file = sym.getFile(macho_file) orelse continue; + const is_obj = macho_file.base.isObject(); + + for (self.symbols.items, 0..) |*sym, i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + const file = ref.getFile(macho_file) orelse continue; if (file.getIndex() != self.index) continue; if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue; if (sym.isSymbolStab(macho_file)) continue; const name = sym.getName(macho_file); + if (name.len == 0) continue; // TODO in -r mode, we actually want to merge symbol names and emit only one // work it out when emitting relocs - if (name.len > 0 and - (name[0] == 'L' or name[0] == 'l' or + if ((name[0] == 'L' or name[0] == 'l' or mem.startsWith(u8, name, "_OBJC_SELECTOR_REFERENCES_")) and - !macho_file.base.isObject()) continue; + !is_obj) + continue; sym.flags.output_symtab = true; if (sym.isLocal()) { try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file); @@ -1693,9 +1851,9 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void { self.output_symtab_ctx.strsize += @as(u32, @intCast(self.path.len + 1)); } - for (self.symbols.items) |sym_index| { - const sym = macho_file.getSymbol(sym_index); - const file = sym.getFile(macho_file) orelse continue; + for (self.symbols.items, 0..) |sym, i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + const file = ref.getFile(macho_file) orelse continue; if (file.getIndex() != self.index) continue; if (!sym.flags.output_symtab) continue; if (macho_file.base.isObject()) { @@ -1732,28 +1890,204 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void { } } -pub fn writeSymtab(self: Object, macho_file: *MachO, ctx: anytype) error{Overflow}!void { +pub fn writeAtoms(self: *Object, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - for (self.symbols.items) |sym_index| { - const sym = macho_file.getSymbol(sym_index); - const file = sym.getFile(macho_file) orelse continue; + const gpa = macho_file.base.comp.gpa; + const headers = self.sections.items(.header); + const sections_data = try gpa.alloc([]const u8, headers.len); + defer { + for (sections_data) |data| { + gpa.free(data); + } + gpa.free(sections_data); + } + @memset(sections_data, &[0]u8{}); + const file = macho_file.getFileHandle(self.file_handle); + + for (headers, 0..) |header, n_sect| { + if (header.isZerofill()) continue; + const data = try gpa.alloc(u8, header.size); + const amt = try file.preadAll(data, header.offset + self.offset); + if (amt != data.len) return error.InputOutput; + sections_data[n_sect] = data; + } + for (self.getAtoms()) |atom_index| { + const atom = self.getAtom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + const sect = atom.getInputSection(macho_file); + if (sect.isZerofill()) continue; + const off = atom.value; + const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; + const data = sections_data[atom.n_sect]; + @memcpy(buffer[off..][0..atom.size], data[atom.off..][0..atom.size]); + try atom.resolveRelocs(macho_file, buffer[off..][0..atom.size]); + } +} + +pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const gpa = macho_file.base.allocator; + const headers = self.sections.items(.header); + const sections_data = try gpa.alloc([]const u8, headers.len); + defer { + for (sections_data) |data| { + gpa.free(data); + } + gpa.free(sections_data); + } + @memset(sections_data, &[0]u8{}); + const file = macho_file.getFileHandle(self.file_handle); + + for (headers, 0..) |header, n_sect| { + if (header.isZerofill()) continue; + const data = try gpa.alloc(u8, header.size); + const amt = try file.preadAll(data, header.offset + self.offset); + if (amt != data.len) return error.InputOutput; + sections_data[n_sect] = data; + } + for (self.getAtoms()) |atom_index| { + const atom = self.getAtom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + const sect = atom.getInputSection(macho_file); + if (sect.isZerofill()) continue; + const off = atom.value; + const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; + const data = sections_data[atom.n_sect]; + @memcpy(buffer[off..][0..atom.size], data[atom.off..][0..atom.size]); + const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items; + const extra = atom.getExtra(macho_file); + try atom.writeRelocs(macho_file, buffer[off..][0..atom.size], relocs[extra.rel_out_index..][0..extra.rel_out_count]); + } +} + +pub fn calcCompactUnwindSizeRelocatable(self: *Object, macho_file: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); + + const ctx = &self.compact_unwind_ctx; + + for (self.unwind_records_indexes.items) |irec| { + const rec = self.getUnwindRecord(irec); + if (!rec.alive) continue; + + ctx.rec_count += 1; + ctx.reloc_count += 1; + if (rec.getPersonality(macho_file)) |_| { + ctx.reloc_count += 1; + } + if (rec.getLsdaAtom(macho_file)) |_| { + ctx.reloc_count += 1; + } + } +} + +pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const addReloc = struct { + fn addReloc(offset: u32, cpu_arch: std.Target.Cpu.Arch) !macho.relocation_info { + return .{ + .r_address = math.cast(i32, offset) orelse return error.Overflow, + .r_symbolnum = 0, + .r_pcrel = 0, + .r_length = 3, + .r_extern = 0, + .r_type = switch (cpu_arch) { + .aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED), + .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED), + else => unreachable, + }, + }; + } + }.addReloc; + + const nsect = macho_file.unwind_info_sect_index.?; + const buffer = macho_file.sections.items(.out)[nsect].items; + const relocs = macho_file.sections.items(.relocs)[nsect].items; + + var rec_index: u32 = self.compact_unwind_ctx.rec_index; + var reloc_index: u32 = self.compact_unwind_ctx.reloc_index; + + for (self.unwind_records_indexes.items) |irec| { + const rec = self.getUnwindRecord(irec); + if (!rec.alive) continue; + + var out: macho.compact_unwind_entry = .{ + .rangeStart = 0, + .rangeLength = rec.length, + .compactUnwindEncoding = rec.enc.enc, + .personalityFunction = 0, + .lsda = 0, + }; + defer rec_index += 1; + + const offset = rec_index * @sizeOf(macho.compact_unwind_entry); + + { + // Function address + const atom = rec.getAtom(macho_file); + const addr = rec.getAtomAddress(macho_file); + out.rangeStart = addr; + var reloc = try addReloc(offset, macho_file.options.cpu_arch.?); + reloc.r_symbolnum = atom.out_n_sect + 1; + relocs[reloc_index] = reloc; + reloc_index += 1; + } + + // Personality function + if (rec.getPersonality(macho_file)) |sym| { + const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow; + var reloc = try addReloc(offset + 16, macho_file.options.cpu_arch.?); + reloc.r_symbolnum = r_symbolnum; + reloc.r_extern = 1; + relocs[reloc_index] = reloc; + reloc_index += 1; + } + + // LSDA address + if (rec.getLsdaAtom(macho_file)) |atom| { + const addr = rec.getLsdaAddress(macho_file); + out.lsda = addr; + var reloc = try addReloc(offset + 24, macho_file.options.cpu_arch.?); + reloc.r_symbolnum = atom.out_n_sect + 1; + relocs[reloc_index] = reloc; + reloc_index += 1; + } + + @memcpy(buffer[offset..][0..@sizeOf(macho.compact_unwind_entry)], mem.asBytes(&out)); + } +} + +pub fn writeSymtab(self: Object, macho_file: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); + + var n_strx = self.output_symtab_ctx.stroff; + for (self.symbols.items, 0..) |sym, i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + const file = ref.getFile(macho_file) orelse continue; if (file.getIndex() != self.index) continue; const idx = sym.getOutputSymtabIndex(macho_file) orelse continue; - const n_strx = @as(u32, @intCast(ctx.strtab.items.len)); - ctx.strtab.appendSliceAssumeCapacity(sym.getName(macho_file)); - ctx.strtab.appendAssumeCapacity(0); - const out_sym = &ctx.symtab.items[idx]; + const out_sym = &macho_file.symtab.items[idx]; out_sym.n_strx = n_strx; sym.setOutputSym(macho_file, out_sym); + const name = sym.getName(macho_file); + @memcpy(macho_file.strtab.items[n_strx..][0..name.len], name); + n_strx += @intCast(name.len); + macho_file.strtab.items[n_strx] = 0; + n_strx += 1; } if (macho_file.base.comp.config.debug_format != .strip and self.hasDebugInfo()) - try self.writeStabs(macho_file, ctx); + try self.writeStabs(n_strx, macho_file); } -pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{Overflow}!void { +pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { const writeFuncStab = struct { inline fn writeFuncStab( n_strx: u32, @@ -1761,7 +2095,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O n_value: u64, size: u64, index: u32, - context: anytype, + context: *MachO, ) void { context.symtab.items[index] = .{ .n_strx = 0, @@ -1795,6 +2129,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O }.writeFuncStab; var index = self.output_symtab_ctx.istab; + var n_strx = stroff; if (self.compile_unit) |cu| { const comp_dir = cu.getCompDir(self); @@ -1802,10 +2137,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O // Open scope // N_SO comp_dir - var n_strx = @as(u32, @intCast(ctx.strtab.items.len)); - ctx.strtab.appendSliceAssumeCapacity(comp_dir); - ctx.strtab.appendAssumeCapacity(0); - ctx.symtab.items[index] = .{ + macho_file.symtab.items[index] = .{ .n_strx = n_strx, .n_type = macho.N_SO, .n_sect = 0, @@ -1813,11 +2145,12 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O .n_value = 0, }; index += 1; + @memcpy(macho_file.strtab.items[n_strx..][0..comp_dir.len], comp_dir); + n_strx += @intCast(comp_dir.len); + macho_file.strtab.items[n_strx] = 0; + n_strx += 1; // N_SO tu_name - n_strx = @as(u32, @intCast(ctx.strtab.items.len)); - ctx.strtab.appendSliceAssumeCapacity(tu_name); - ctx.strtab.appendAssumeCapacity(0); - ctx.symtab.items[index] = .{ + macho_file.symtab.items[index] = .{ .n_strx = n_strx, .n_type = macho.N_SO, .n_sect = 0, @@ -1825,19 +2158,12 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O .n_value = 0, }; index += 1; + @memcpy(macho_file.strtab.items[n_strx..][0..tu_name.len], tu_name); + n_strx += @intCast(tu_name.len); + macho_file.strtab.items[n_strx] = 0; + n_strx += 1; // N_OSO path - n_strx = @as(u32, @intCast(ctx.strtab.items.len)); - if (self.in_archive) |ar| { - ctx.strtab.appendSliceAssumeCapacity(ar.path); - ctx.strtab.appendAssumeCapacity('('); - ctx.strtab.appendSliceAssumeCapacity(self.path); - ctx.strtab.appendAssumeCapacity(')'); - ctx.strtab.appendAssumeCapacity(0); - } else { - ctx.strtab.appendSliceAssumeCapacity(self.path); - ctx.strtab.appendAssumeCapacity(0); - } - ctx.symtab.items[index] = .{ + macho_file.symtab.items[index] = .{ .n_strx = n_strx, .n_type = macho.N_OSO, .n_sect = 0, @@ -1845,10 +2171,27 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O .n_value = self.mtime, }; index += 1; + if (self.in_archive) |ar| { + @memcpy(macho_file.strtab.items[n_strx..][0..ar.path.len], ar.path); + n_strx += @intCast(ar.path.len); + macho_file.strtab.items[n_strx] = '('; + n_strx += 1; + @memcpy(macho_file.strtab.items[n_strx..][0..self.path.len], self.path); + n_strx += @intCast(self.path.len); + macho_file.strtab.items[n_strx] = ')'; + n_strx += 1; + macho_file.strtab.items[n_strx] = 0; + n_strx += 1; + } else { + @memcpy(macho_file.strtab.items[n_strx..][0..self.path.len], self.path); + n_strx += @intCast(self.path.len); + macho_file.strtab.items[n_strx] = 0; + n_strx += 1; + } - for (self.symbols.items) |sym_index| { - const sym = macho_file.getSymbol(sym_index); - const file = sym.getFile(macho_file) orelse continue; + for (self.symbols.items, 0..) |sym, i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + const file = ref.getFile(macho_file) orelse continue; if (file.getIndex() != self.index) continue; if (!sym.flags.output_symtab) continue; if (macho_file.base.isObject()) { @@ -1858,17 +2201,17 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O const sect = macho_file.sections.items(.header)[sym.out_n_sect]; const sym_n_strx = n_strx: { const symtab_index = sym.getOutputSymtabIndex(macho_file).?; - const osym = ctx.symtab.items[symtab_index]; + const osym = macho_file.symtab.items[symtab_index]; break :n_strx osym.n_strx; }; const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.out_n_sect + 1) else 0; const sym_n_value = sym.getAddress(.{}, macho_file); const sym_size = sym.getSize(macho_file); if (sect.isCode()) { - writeFuncStab(sym_n_strx, sym_n_sect, sym_n_value, sym_size, index, ctx); + writeFuncStab(sym_n_strx, sym_n_sect, sym_n_value, sym_size, index, macho_file); index += 4; } else if (sym.visibility == .global) { - ctx.symtab.items[index] = .{ + macho_file.symtab.items[index] = .{ .n_strx = sym_n_strx, .n_type = macho.N_GSYM, .n_sect = sym_n_sect, @@ -1877,7 +2220,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O }; index += 1; } else { - ctx.symtab.items[index] = .{ + macho_file.symtab.items[index] = .{ .n_strx = sym_n_strx, .n_type = macho.N_STSYM, .n_sect = sym_n_sect, @@ -1890,7 +2233,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O // Close scope // N_SO - ctx.symtab.items[index] = .{ + macho_file.symtab.items[index] = .{ .n_strx = 0, .n_type = macho.N_SO, .n_sect = 0, @@ -1901,12 +2244,13 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O assert(self.hasSymbolStabs()); for (self.stab_files.items) |sf| { + const comp_dir = sf.getCombDir(self); + const tu_name = sf.getTuName(self); + const oso_path = sf.getOsoPath(self); + // Open scope // N_SO comp_dir - var n_strx = @as(u32, @intCast(ctx.strtab.items.len)); - ctx.strtab.appendSliceAssumeCapacity(sf.getCompDir(self)); - ctx.strtab.appendAssumeCapacity(0); - ctx.symtab.items[index] = .{ + macho_file.symtab.items[index] = .{ .n_strx = n_strx, .n_type = macho.N_SO, .n_sect = 0, @@ -1914,11 +2258,12 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O .n_value = 0, }; index += 1; + @memcpy(macho_file.strtab.items[n_strx..][0..comp_dir.len], comp_dir); + n_strx += @intCast(comp_dir.len); + macho_file.strtab.items[n_strx] = 0; + n_strx += 1; // N_SO tu_name - n_strx = @as(u32, @intCast(ctx.strtab.items.len)); - ctx.strtab.appendSliceAssumeCapacity(sf.getTuName(self)); - ctx.strtab.appendAssumeCapacity(0); - ctx.symtab.items[index] = .{ + macho_file.symtab.items[index] = .{ .n_strx = n_strx, .n_type = macho.N_SO, .n_sect = 0, @@ -1926,11 +2271,12 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O .n_value = 0, }; index += 1; + @memcpy(macho_file.strtab.items[n_strx..][0..tu_name.len], tu_name); + n_strx += @intCast(tu_name.len); + macho_file.strtab.items[n_strx] = 0; + n_strx += 1; // N_OSO path - n_strx = @as(u32, @intCast(ctx.strtab.items.len)); - ctx.strtab.appendSliceAssumeCapacity(sf.getOsoPath(self)); - ctx.strtab.appendAssumeCapacity(0); - ctx.symtab.items[index] = .{ + macho_file.symtab.items[index] = .{ .n_strx = n_strx, .n_type = macho.N_OSO, .n_sect = 0, @@ -1938,6 +2284,10 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O .n_value = sf.getOsoModTime(self), }; index += 1; + @memcpy(macho_file.strtab.items[n_strx..][0..oso_path.len], oso_path); + n_strx += @intCast(oso_path.len); + macho_file.strtab.items[n_strx] = 0; + n_strx += 1; for (sf.stabs.items) |stab| { const sym = stab.getSymbol(macho_file) orelse continue; @@ -1946,17 +2296,17 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O if (!sym.flags.output_symtab) continue; const sym_n_strx = n_strx: { const symtab_index = sym.getOutputSymtabIndex(macho_file).?; - const osym = ctx.symtab.items[symtab_index]; + const osym = macho_file.symtab.items[symtab_index]; break :n_strx osym.n_strx; }; const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.out_n_sect + 1) else 0; const sym_n_value = sym.getAddress(.{}, macho_file); const sym_size = sym.getSize(macho_file); if (stab.is_func) { - writeFuncStab(sym_n_strx, sym_n_sect, sym_n_value, sym_size, index, ctx); + writeFuncStab(sym_n_strx, sym_n_sect, sym_n_value, sym_size, index, macho_file); index += 4; } else if (sym.visibility == .global) { - ctx.symtab.items[index] = .{ + macho_file.symtab.items[index] = .{ .n_strx = sym_n_strx, .n_type = macho.N_GSYM, .n_sect = sym_n_sect, @@ -1965,7 +2315,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O }; index += 1; } else { - ctx.symtab.items[index] = .{ + macho_file.symtab.items[index] = .{ .n_strx = sym_n_strx, .n_type = macho.N_STSYM, .n_sect = sym_n_sect, @@ -1978,7 +2328,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O // Close scope // N_SO - ctx.symtab.items[index] = .{ + macho_file.symtab.items[index] = .{ .n_strx = 0, .n_type = macho.N_SO, .n_sect = 0, @@ -1990,36 +2340,6 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O } } -fn getSectionData(self: *const Object, index: u32, macho_file: *MachO) ![]u8 { - const gpa = macho_file.base.comp.gpa; - const slice = self.sections.slice(); - assert(index < slice.items(.header).len); - const sect = slice.items(.header)[index]; - const handle = macho_file.getFileHandle(self.file_handle); - const size = math.cast(usize, sect.size) orelse return error.Overflow; - const buffer = try gpa.alloc(u8, size); - errdefer gpa.free(buffer); - const amt = try handle.preadAll(buffer, sect.offset + self.offset); - if (amt != buffer.len) return error.InputOutput; - return buffer; -} - -pub fn getAtomData(self: *const Object, macho_file: *MachO, atom: Atom, buffer: []u8) !void { - assert(buffer.len == atom.size); - const slice = self.sections.slice(); - const handle = macho_file.getFileHandle(self.file_handle); - const sect = slice.items(.header)[atom.n_sect]; - const amt = try handle.preadAll(buffer, sect.offset + self.offset + atom.off); - if (amt != buffer.len) return error.InputOutput; -} - -pub fn getAtomRelocs(self: *const Object, atom: Atom, macho_file: *MachO) []const Relocation { - if (!atom.flags.relocs) return &[0]Relocation{}; - const extra = atom.getExtra(macho_file).?; - const relocs = self.sections.items(.relocs)[atom.n_sect]; - return relocs.items[extra.rel_index..][0..extra.rel_count]; -} - fn addString(self: *Object, allocator: Allocator, name: [:0]const u8) error{OutOfMemory}!u32 { const off: u32 = @intCast(self.strtab.items.len); try self.strtab.ensureUnusedCapacity(allocator, name.len + 1); @@ -2073,6 +2393,143 @@ pub fn asFile(self: *Object) File { return .{ .object = self }; } +const AddAtomArgs = struct { + name: MachO.String, + n_sect: u8, + off: u64, + size: u64, + alignment: u32, +}; + +fn addAtom(self: *Object, allocator: Allocator, args: AddAtomArgs) !Atom.Index { + const atom_index: Atom.Index = @intCast(self.atoms.items.len); + const atom = try self.atoms.addOne(allocator); + atom.* = .{ + .file = self.index, + .atom_index = atom_index, + .name = args.name, + .n_sect = args.n_sect, + .size = args.size, + .off = args.off, + .extra = try self.addAtomExtra(allocator, .{}), + .alignment = args.alignment, + }; + return atom_index; +} + +pub fn getAtom(self: *Object, atom_index: Atom.Index) ?*Atom { + if (atom_index == 0) return null; + assert(atom_index < self.atoms.items.len); + return &self.atoms.items[atom_index]; +} + +pub fn getAtoms(self: *Object) []const Atom.Index { + return self.atoms_indexes.items; +} + +fn addAtomExtra(self: *Object, allocator: Allocator, extra: Atom.Extra) !u32 { + const fields = @typeInfo(Atom.Extra).Struct.fields; + try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len); + return self.addAtomExtraAssumeCapacity(extra); +} + +fn addAtomExtraAssumeCapacity(self: *Object, extra: Atom.Extra) u32 { + const index = @as(u32, @intCast(self.atoms_extra.items.len)); + const fields = @typeInfo(Atom.Extra).Struct.fields; + inline for (fields) |field| { + self.atoms_extra.appendAssumeCapacity(switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }); + } + return index; +} + +pub fn getAtomExtra(self: Object, index: u32) Atom.Extra { + const fields = @typeInfo(Atom.Extra).Struct.fields; + var i: usize = index; + var result: Atom.Extra = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.type) { + u32 => self.atoms_extra.items[i], + else => @compileError("bad field type"), + }; + i += 1; + } + return result; +} + +pub fn setAtomExtra(self: *Object, index: u32, extra: Atom.Extra) void { + assert(index > 0); + const fields = @typeInfo(Atom.Extra).Struct.fields; + inline for (fields, 0..) |field, i| { + self.atoms_extra.items[index + i] = switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }; + } +} + +fn addSymbol(self: *Object, allocator: Allocator) !Symbol.Index { + try self.symbols.ensureUnusedCapacity(allocator, 1); + return self.addSymbolAssumeCapacity(); +} + +fn addSymbolAssumeCapacity(self: *Object) Symbol.Index { + const index: Symbol.Index = @intCast(self.symbols.items.len); + const symbol = self.symbols.addOneAssumeCapacity(); + symbol.* = .{ .file = self.index }; + return index; +} + +pub fn getSymbolRef(self: Object, index: Symbol.Index, macho_file: *MachO) MachO.Ref { + const global_index = self.globals.items[index]; + if (macho_file.resolver.get(global_index)) |ref| return ref; + return .{ .index = index, .file = self.index }; +} + +pub fn addSymbolExtra(self: *Object, allocator: Allocator, extra: Symbol.Extra) !u32 { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len); + return self.addSymbolExtraAssumeCapacity(extra); +} + +fn addSymbolExtraAssumeCapacity(self: *Object, extra: Symbol.Extra) u32 { + const index = @as(u32, @intCast(self.symbols_extra.items.len)); + const fields = @typeInfo(Symbol.Extra).Struct.fields; + inline for (fields) |field| { + self.symbols_extra.appendAssumeCapacity(switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }); + } + return index; +} + +pub fn getSymbolExtra(self: Object, index: u32) Symbol.Extra { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + var i: usize = index; + var result: Symbol.Extra = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.type) { + u32 => self.symbols_extra.items[i], + else => @compileError("bad field type"), + }; + i += 1; + } + return result; +} + +pub fn setSymbolExtra(self: *Object, index: u32, extra: Symbol.Extra) void { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + inline for (fields, 0..) |field, i| { + self.symbols_extra.items[index + i] = switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }; + } +} + fn addUnwindRecord(self: *Object, allocator: Allocator) !UnwindInfo.Record.Index { try self.unwind_records.ensureUnusedCapacity(allocator, 1); return self.addUnwindRecordAssumeCapacity(); @@ -2124,10 +2581,11 @@ fn formatAtoms( _ = unused_fmt_string; _ = options; const object = ctx.object; + const macho_file = ctx.macho_file; try writer.writeAll(" atoms\n"); - for (object.atoms.items) |atom_index| { - const atom = ctx.macho_file.getAtom(atom_index).?; - try writer.print(" {}\n", .{atom.fmt(ctx.macho_file)}); + for (object.getAtoms()) |atom_index| { + const atom = object.getAtom(atom_index) orelse continue; + try writer.print(" {}\n", .{atom.fmt(macho_file)}); } } @@ -2214,10 +2672,26 @@ fn formatSymtab( _ = unused_fmt_string; _ = options; const object = ctx.object; + const macho_file = ctx.macho_file; try writer.writeAll(" symbols\n"); - for (object.symbols.items) |index| { - const sym = ctx.macho_file.getSymbol(index); - try writer.print(" {}\n", .{sym.fmt(ctx.macho_file)}); + for (object.symbols.items, 0..) |sym, i| { + const ref = object.getSymbolRef(@intCast(i), macho_file); + if (ref.getFile(macho_file) == null) { + // TODO any better way of handling this? + try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); + } else { + try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); + } + } + for (object.stab_files.items) |sf| { + try writer.print(" stabs({s},{s},{s})\n", .{ + sf.getCompDir(object), + sf.getTuName(object), + sf.getOsoPath(object), + }); + for (sf.stabs.items) |stab| { + try writer.print(" {}", .{stab.fmt(object)}); + } } } @@ -2286,8 +2760,47 @@ const StabFile = struct { is_func: bool = true, symbol: ?Symbol.Index = null, - fn getSymbol(stab: Stab, macho_file: *MachO) ?*Symbol { - return if (stab.symbol) |s| macho_file.getSymbol(s) else null; + fn getSymbol(stab: Stab, object: *const Object) ?*Symbol { + const index = stab.index orelse return null; + return &object.symbols.items[index]; + } + + pub fn format( + stab: Stab, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = stab; + _ = unused_fmt_string; + _ = options; + _ = writer; + @compileError("do not format stabs directly"); + } + + const StabFormatContext = struct { Stab, *const Object }; + + pub fn fmt(stab: Stab, object: *const Object) std.fmt.Formatter(format2) { + return .{ .data = .{ stab, object } }; + } + + fn format2( + ctx: StabFormatContext, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = unused_fmt_string; + _ = options; + const stab, const object = ctx; + const sym = stab.getSymbol(object).?; + if (stab.is_func) { + try writer.print("func({d})", .{stab.index.?}); + } else if (sym.visibility == .global) { + try writer.print("gsym({d})", .{stab.index.?}); + } else { + try writer.print("stsym({d})", .{stab.index.?}); + } } }; }; @@ -2310,6 +2823,13 @@ const InArchive = struct { size: u32, }; +const CompactUnwindCtx = struct { + rec_index: u32 = 0, + rec_count: u32 = 0, + reloc_index: u32 = 0, + reloc_count: u32 = 0, +}; + const x86_64 = struct { fn parseRelocs( self: *const Object, From c59583e43de35e91ac194860cd1eb63e61c272aa Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 9 Jul 2024 06:43:26 +0200 Subject: [PATCH 06/45] macho: migrate InternalObject and Dylib --- src/link/MachO.zig | 139 ------ src/link/MachO/Dylib.zig | 182 ++++--- src/link/MachO/InternalObject.zig | 801 ++++++++++++++++++++++++------ 3 files changed, 778 insertions(+), 344 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 99424b6f40..9f7a306a74 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -3823,145 +3823,6 @@ pub fn getFileHandle(self: MachO, index: File.HandleIndex) File.Handle { return self.file_handles.items[index]; } -pub fn addAtom(self: *MachO) error{OutOfMemory}!Atom.Index { - const index = @as(Atom.Index, @intCast(self.atoms.items.len)); - const atom = try self.atoms.addOne(self.base.comp.gpa); - atom.* = .{}; - return index; -} - -pub fn getAtom(self: *MachO, index: Atom.Index) ?*Atom { - if (index == 0) return null; - assert(index < self.atoms.items.len); - return &self.atoms.items[index]; -} - -pub fn addAtomExtra(self: *MachO, extra: Atom.Extra) !u32 { - const fields = @typeInfo(Atom.Extra).Struct.fields; - try self.atoms_extra.ensureUnusedCapacity(self.base.comp.gpa, fields.len); - return self.addAtomExtraAssumeCapacity(extra); -} - -pub fn addAtomExtraAssumeCapacity(self: *MachO, extra: Atom.Extra) u32 { - const index = @as(u32, @intCast(self.atoms_extra.items.len)); - const fields = @typeInfo(Atom.Extra).Struct.fields; - inline for (fields) |field| { - self.atoms_extra.appendAssumeCapacity(switch (field.type) { - u32 => @field(extra, field.name), - else => @compileError("bad field type"), - }); - } - return index; -} - -pub fn getAtomExtra(self: *MachO, index: u32) ?Atom.Extra { - if (index == 0) return null; - const fields = @typeInfo(Atom.Extra).Struct.fields; - var i: usize = index; - var result: Atom.Extra = undefined; - inline for (fields) |field| { - @field(result, field.name) = switch (field.type) { - u32 => self.atoms_extra.items[i], - else => @compileError("bad field type"), - }; - i += 1; - } - return result; -} - -pub fn setAtomExtra(self: *MachO, index: u32, extra: Atom.Extra) void { - assert(index > 0); - const fields = @typeInfo(Atom.Extra).Struct.fields; - inline for (fields, 0..) |field, i| { - self.atoms_extra.items[index + i] = switch (field.type) { - u32 => @field(extra, field.name), - else => @compileError("bad field type"), - }; - } -} - -pub fn addSymbol(self: *MachO) !Symbol.Index { - const index = @as(Symbol.Index, @intCast(self.symbols.items.len)); - const symbol = try self.symbols.addOne(self.base.comp.gpa); - symbol.* = .{}; - return index; -} - -pub fn getSymbol(self: *MachO, index: Symbol.Index) *Symbol { - assert(index < self.symbols.items.len); - return &self.symbols.items[index]; -} - -pub fn addSymbolExtra(self: *MachO, extra: Symbol.Extra) !u32 { - const fields = @typeInfo(Symbol.Extra).Struct.fields; - try self.symbols_extra.ensureUnusedCapacity(self.base.comp.gpa, fields.len); - return self.addSymbolExtraAssumeCapacity(extra); -} - -pub fn addSymbolExtraAssumeCapacity(self: *MachO, extra: Symbol.Extra) u32 { - const index = @as(u32, @intCast(self.symbols_extra.items.len)); - const fields = @typeInfo(Symbol.Extra).Struct.fields; - inline for (fields) |field| { - self.symbols_extra.appendAssumeCapacity(switch (field.type) { - u32 => @field(extra, field.name), - else => @compileError("bad field type"), - }); - } - return index; -} - -pub fn getSymbolExtra(self: MachO, index: u32) ?Symbol.Extra { - if (index == 0) return null; - const fields = @typeInfo(Symbol.Extra).Struct.fields; - var i: usize = index; - var result: Symbol.Extra = undefined; - inline for (fields) |field| { - @field(result, field.name) = switch (field.type) { - u32 => self.symbols_extra.items[i], - else => @compileError("bad field type"), - }; - i += 1; - } - return result; -} - -pub fn setSymbolExtra(self: *MachO, index: u32, extra: Symbol.Extra) void { - assert(index > 0); - const fields = @typeInfo(Symbol.Extra).Struct.fields; - inline for (fields, 0..) |field, i| { - self.symbols_extra.items[index + i] = switch (field.type) { - u32 => @field(extra, field.name), - else => @compileError("bad field type"), - }; - } -} - -const GetOrCreateGlobalResult = struct { - found_existing: bool, - index: Symbol.Index, -}; - -pub fn getOrCreateGlobal(self: *MachO, off: u32) !GetOrCreateGlobalResult { - const gpa = self.base.comp.gpa; - const gop = try self.globals.getOrPut(gpa, off); - if (!gop.found_existing) { - const index = try self.addSymbol(); - const global = self.getSymbol(index); - global.name = off; - global.flags.global = true; - gop.value_ptr.* = index; - } - return .{ - .found_existing = gop.found_existing, - .index = gop.value_ptr.*, - }; -} - -pub fn getGlobalByName(self: *MachO, name: []const u8) ?Symbol.Index { - const off = self.strings.getOffset(name) orelse return null; - return self.globals.get(off); -} - pub fn addThunk(self: *MachO) !Thunk.Index { const index = @as(Thunk.Index, @intCast(self.thunks.items.len)); const thunk = try self.thunks.addOne(self.base.comp.gpa); diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 458b66d433..6bb3056a61 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -7,6 +7,8 @@ id: ?Id = null, ordinal: u16 = 0, symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, +symbols_extra: std.ArrayListUnmanaged(u32) = .{}, +globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{}, dependents: std.ArrayListUnmanaged(Id) = .{}, rpaths: std.StringArrayHashMapUnmanaged(void) = .{}, umbrella: File.Index = 0, @@ -37,6 +39,8 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void { self.strtab.deinit(allocator); if (self.id) |*id| id.deinit(allocator); self.symbols.deinit(allocator); + self.symbols_extra.deinit(allocator); + self.globals.deinit(allocator); for (self.dependents.items) |*id| { id.deinit(allocator); } @@ -494,13 +498,21 @@ fn addObjCExport( pub fn initSymbols(self: *Dylib, macho_file: *MachO) !void { const gpa = macho_file.base.comp.gpa; - try self.symbols.ensureTotalCapacityPrecise(gpa, self.exports.items(.name).len); + const nsyms = self.exports.items(.name).len; + try self.symbols.ensureTotalCapacityPrecise(gpa, nsyms); + try self.symbols_extra.ensureTotalCapacityPrecise(gpa, nsyms * @sizeOf(Symbol.Extra)); + try self.globals.ensureTotalCapacityPrecise(gpa, nsyms); + self.globals.resize(gpa, nsyms) catch unreachable; + @memset(self.globals.items, 0); - for (self.exports.items(.name)) |noff| { - const name = self.getString(noff); - const off = try macho_file.strings.insert(gpa, name); - const gop = try macho_file.getOrCreateGlobal(off); - self.symbols.addOneAssumeCapacity().* = gop.index; + for (self.exports.items(.name), self.exports.items(.flags)) |noff, flags| { + const index = self.addSymbolAssumeCapacity(); + const symbol = &self.symbols.items[index]; + symbol.name = noff; + symbol.extra = self.addSymbolExtraAssumeCapacity(.{}); + symbol.flags.weak = flags.weak; + symbol.flags.tlv = flags.tlv; + symbol.visibility = .global; } } @@ -510,37 +522,31 @@ pub fn resolveSymbols(self: *Dylib, macho_file: *MachO) void { if (!self.explicit and !self.hoisted) return; - for (self.symbols.items, self.exports.items(.flags)) |index, flags| { - const global = macho_file.getSymbol(index); + const gpa = macho_file.base.comp.gpa; + + for (self.exports.items(.flags), self.globals.items, 0..) |flags, *global, i| { + const gop = try macho_file.resolver.getOrPut(gpa, .{ + .index = @intCast(i), + .file = self.index, + }, macho_file); + if (!gop.found_existing) { + gop.ref.* = .{ .index = 0, .file = 0 }; + } + global.* = gop.index; + + if (gop.ref.getFile(macho_file) == null) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; + continue; + } + if (self.asFile().getSymbolRank(.{ .weak = flags.weak, - }) < global.getSymbolRank(macho_file)) { - global.value = 0; - global.atom = 0; - global.nlist_idx = 0; - global.file = self.index; - global.flags.weak = flags.weak; - global.flags.tlv = flags.tlv; - global.flags.dyn_ref = false; - global.flags.tentative = false; - global.visibility = .global; + }) < gop.ref.getSymbol(macho_file).?.getSymbolRank(macho_file)) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; } } } -pub fn resetGlobals(self: *Dylib, macho_file: *MachO) void { - for (self.symbols.items) |sym_index| { - const sym = macho_file.getSymbol(sym_index); - const name = sym.name; - const global = sym.flags.global; - const weak_ref = sym.flags.weak_ref; - sym.* = .{}; - sym.name = name; - sym.flags.global = global; - sym.flags.weak_ref = weak_ref; - } -} - pub fn isAlive(self: Dylib, macho_file: *MachO) bool { if (!macho_file.dead_strip_dylibs) return self.explicit or self.referenced or self.needed; return self.referenced or self.needed; @@ -550,48 +556,52 @@ pub fn markReferenced(self: *Dylib, macho_file: *MachO) void { const tracy = trace(@src()); defer tracy.end(); - for (self.symbols.items) |global_index| { - const global = macho_file.getSymbol(global_index); - const file_ptr = global.getFile(macho_file) orelse continue; - if (file_ptr.getIndex() != self.index) continue; + for (0..self.symbols.items.len) |i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + const file = ref.getFile(macho_file) orelse continue; + if (file.getIndex() != self.index) continue; + const global = ref.getSymbol(macho_file).?; if (global.isLocal()) continue; self.referenced = true; break; } } -pub fn calcSymtabSize(self: *Dylib, macho_file: *MachO) !void { +pub fn calcSymtabSize(self: *Dylib, macho_file: *MachO) void { const tracy = trace(@src()); defer tracy.end(); - for (self.symbols.items) |global_index| { - const global = macho_file.getSymbol(global_index); - const file_ptr = global.getFile(macho_file) orelse continue; - if (file_ptr.getIndex() != self.index) continue; - if (global.isLocal()) continue; - assert(global.flags.import); - global.flags.output_symtab = true; - try global.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file); + for (self.symbols.items, 0..) |*sym, i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + const file = ref.getFile(macho_file) orelse continue; + if (file.getIndex() != self.index) continue; + if (sym.isLocal()) continue; + assert(sym.flags.import); + sym.flags.output_symtab = true; + sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file); self.output_symtab_ctx.nimports += 1; - self.output_symtab_ctx.strsize += @as(u32, @intCast(global.getName(macho_file).len + 1)); + self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1)); } } -pub fn writeSymtab(self: Dylib, macho_file: *MachO, ctx: anytype) void { +pub fn writeSymtab(self: Dylib, macho_file: *MachO) void { const tracy = trace(@src()); defer tracy.end(); - for (self.symbols.items) |global_index| { - const global = macho_file.getSymbol(global_index); - const file = global.getFile(macho_file) orelse continue; + var n_strx = self.output_symtab_ctx.stroff; + for (self.symbols.items, 0..) |sym, i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + const file = ref.getFile(macho_file) orelse continue; if (file.getIndex() != self.index) continue; - const idx = global.getOutputSymtabIndex(macho_file) orelse continue; - const n_strx = @as(u32, @intCast(ctx.strtab.items.len)); - ctx.strtab.appendSliceAssumeCapacity(global.getName(macho_file)); - ctx.strtab.appendAssumeCapacity(0); - const out_sym = &ctx.symtab.items[idx]; + const idx = sym.getOutputSymtabIndex(macho_file) orelse continue; + const out_sym = &macho_file.symtab.items[idx]; out_sym.n_strx = n_strx; - global.setOutputSym(macho_file, out_sym); + sym.setOutputSym(macho_file, out_sym); + const name = sym.getName(macho_file); + @memcpy(macho_file.strtab.items[n_strx..][0..name.len], name); + n_strx += @intCast(name.len); + macho_file.strtab.items[n_strx] = 0; + n_strx += 1; } } @@ -605,7 +615,7 @@ fn addString(self: *Dylib, allocator: Allocator, name: []const u8) !u32 { return off; } -pub inline fn getString(self: Dylib, off: u32) [:0]const u8 { +pub fn getString(self: Dylib, off: u32) [:0]const u8 { assert(off < self.strtab.items.len); return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0); } @@ -614,6 +624,66 @@ pub fn asFile(self: *Dylib) File { return .{ .dylib = self }; } +fn addSymbol(self: *Dylib, allocator: Allocator) !Symbol.Index { + try self.symbols.ensureUnusedCapacity(allocator, 1); + return self.addSymbolAssumeCapacity(); +} + +fn addSymbolAssumeCapacity(self: *Dylib) Symbol.Index { + const index: Symbol.Index = @intCast(self.symbols.items.len); + const symbol = self.symbols.addOneAssumeCapacity(); + symbol.* = .{ .file = self.index }; + return index; +} + +pub fn getSymbolRef(self: Dylib, index: Symbol.Index, macho_file: *MachO) MachO.Ref { + const global_index = self.globals.items[index]; + if (macho_file.resolver.get(global_index)) |ref| return ref; + return .{ .index = index, .file = self.index }; +} + +pub fn addSymbolExtra(self: *Dylib, allocator: Allocator, extra: Symbol.Extra) !u32 { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len); + return self.addSymbolExtraAssumeCapacity(extra); +} + +fn addSymbolExtraAssumeCapacity(self: *Dylib, extra: Symbol.Extra) u32 { + const index = @as(u32, @intCast(self.symbols_extra.items.len)); + const fields = @typeInfo(Symbol.Extra).Struct.fields; + inline for (fields) |field| { + self.symbols_extra.appendAssumeCapacity(switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }); + } + return index; +} + +pub fn getSymbolExtra(self: Dylib, index: u32) Symbol.Extra { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + var i: usize = index; + var result: Symbol.Extra = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.type) { + u32 => self.symbols_extra.items[i], + else => @compileError("bad field type"), + }; + i += 1; + } + return result; +} + +pub fn setSymbolExtra(self: *Dylib, index: u32, extra: Symbol.Extra) void { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + inline for (fields, 0..) |field, i| { + self.symbols_extra.items[index + i] = switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }; + } +} + pub fn format( self: *Dylib, comptime unused_fmt_string: []const u8, diff --git a/src/link/MachO/InternalObject.zig b/src/link/MachO/InternalObject.zig index 83bbb8ee54..2499f01a03 100644 --- a/src/link/MachO/InternalObject.zig +++ b/src/link/MachO/InternalObject.zig @@ -1,12 +1,28 @@ index: File.Index, sections: std.MultiArrayList(Section) = .{}, -atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, -symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, +atoms: std.ArrayListUnmanaged(Atom) = .{}, +atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{}, +atoms_extra: std.ArrayListUnmanaged(u32) = .{}, +symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{}, +strtab: std.ArrayListUnmanaged(u8) = .{}, +symbols: std.ArrayListUnmanaged(Symbol) = .{}, +symbols_extra: std.ArrayListUnmanaged(u32) = .{}, +globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{}, objc_methnames: std.ArrayListUnmanaged(u8) = .{}, objc_selrefs: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64), +force_undefined: std.ArrayListUnmanaged(Symbol.Index) = .{}, +entry_index: ?Symbol.Index = null, +dyld_stub_binder_index: ?Symbol.Index = null, +dyld_private: ?Symbol.Index = null, +objc_msg_send_index: ?Symbol.Index = null, +mh_execute_header_index: ?Symbol.Index = null, +mh_dylib_header_index: ?Symbol.Index = null, +dso_handle_index: ?Symbol.Index = null, +boundary_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, + output_symtab_ctx: MachO.SymtabCtx = .{}, pub fn deinit(self: *InternalObject, allocator: Allocator) void { @@ -15,39 +31,224 @@ pub fn deinit(self: *InternalObject, allocator: Allocator) void { } self.sections.deinit(allocator); self.atoms.deinit(allocator); + self.atoms_indexes.deinit(allocator); + self.atoms_extra.deinit(allocator); + self.symtab.deinit(allocator); + self.strtab.deinit(allocator); self.symbols.deinit(allocator); + self.symbols_extra.deinit(allocator); + self.globals.deinit(allocator); self.objc_methnames.deinit(allocator); + self.force_undefined.deinit(allocator); + self.boundary_symbols.deinit(allocator); } -pub fn addSymbol(self: *InternalObject, name: [:0]const u8, macho_file: *MachO) !Symbol.Index { +pub fn init(self: *InternalObject, allocator: Allocator) !void { + // Atom at index 0 is reserved as null atom. + try self.atoms.append(allocator, .{}); + try self.atoms_extra.append(allocator, 0); + // Null byte in strtab + try self.strtab.append(allocator, 0); +} + +pub fn initSymbols(self: *InternalObject, macho_file: *MachO) !void { + const createSymbol = struct { + fn createSymbol(obj: *InternalObject, name: u32, args: struct { + type: u8 = macho.N_UNDF | macho.N_EXT, + desc: u16 = 0, + }) Symbol.Index { + const index = obj.addSymbolAssumeCapacity(); + const symbol = &obj.symbols.items[index]; + symbol.name = name; + symbol.extra = obj.addSymbolExtraAssumeCapacity(.{}); + symbol.flags.dyn_ref = args.desc & macho.REFERENCED_DYNAMICALLY != 0; + symbol.visibility = if (args.type & macho.N_EXT != 0) blk: { + break :blk if (args.type & macho.N_PEXT != 0) .hidden else .global; + } else .local; + + const nlist_idx: u32 = @intCast(obj.symtab.items.len); + const nlist = obj.symtab.addOneAssumeCapacity(); + nlist.* = .{ + .n_strx = name, + .n_type = args.type, + .n_sect = 0, + .n_desc = args.desc, + .n_value = 0, + }; + symbol.nlist_idx = nlist_idx; + return index; + } + }.createSymbol; + const gpa = macho_file.base.comp.gpa; - try self.symbols.ensureUnusedCapacity(gpa, 1); - const off = try macho_file.strings.insert(gpa, name); - const gop = try macho_file.getOrCreateGlobal(off); - self.symbols.addOneAssumeCapacity().* = gop.index; - const sym = macho_file.getSymbol(gop.index); - sym.file = self.index; - sym.value = 0; - sym.atom = 0; - sym.nlist_idx = 0; - sym.flags = .{ .global = true }; - return gop.index; + var nsyms = macho_file.base.comp.force_undefined_symbols.keys().len; + nsyms += 1; // dyld_stub_binder + nsyms += 1; // _objc_msgSend + if (!macho_file.base.isDynLib()) { + nsyms += 1; // entry + nsyms += 1; // __mh_execute_header + } else { + nsyms += 1; // __mh_dylib_header + } + nsyms += 1; // ___dso_handle + nsyms += 1; // dyld_private + + try self.symbols.ensureTotalCapacityPrecise(gpa, nsyms); + try self.symbols_extra.ensureTotalCapacityPrecise(gpa, nsyms * @sizeOf(Symbol.Extra)); + try self.symtab.ensureTotalCapacityPrecise(gpa, nsyms); + try self.globals.ensureTotalCapacityPrecise(gpa, nsyms); + self.globals.resize(gpa, nsyms) catch unreachable; + @memset(self.globals.items, 0); + + try self.force_undefined.ensureTotalCapacityPrecise(gpa, macho_file.base.comp.force_undefined_symbols.keys().len); + for (macho_file.base.comp.force_undefined_symbols.keys()) |name| { + self.force_undefined.addOneAssumeCapacity().* = createSymbol(self, try self.addString(gpa, name), .{}); + } + + self.dyld_stub_binder_index = createSymbol(self, try self.addString(gpa, "dyld_stub_binder"), .{}); + self.objc_msg_send_index = createSymbol(self, try self.addString(gpa, "_objc_msgSend"), .{}); + + if (!macho_file.base.isDynLib()) { + self.entry_index = createSymbol(self, try self.addString(gpa, macho_file.entry_name orelse "_main"), .{}); + self.mh_execute_header_index = createSymbol(self, try self.addString(gpa, "__mh_execute_header"), .{ + .type = macho.N_SECT | macho.N_EXT, + .desc = macho.REFERENCED_DYNAMICALLY, + }); + } else { + self.mh_dylib_header_index = createSymbol(self, try self.addString(gpa, "__mh_dylib_header"), .{ + .type = macho.N_SECT | macho.N_EXT, + }); + } + + self.dso_handle_index = createSymbol(self, try self.addString(gpa, "___dso_handle"), .{ + .type = macho.N_SECT | macho.N_EXT, + }); + self.dyld_private_index = createSymbol(self, try self.addString(gpa, "dyld_private"), .{ + .type = macho.N_SECT, + }); +} + +pub fn resolveSymbols(self: *InternalObject, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const gpa = macho_file.base.comp.gpa; + + for (self.symtab.items, self.globals.items, 0..) |nlist, *global, i| { + const gop = try macho_file.resolver.getOrPut(gpa, .{ + .index = @intCast(i), + .file = self.index, + }, macho_file); + if (!gop.found_existing) { + gop.ref.* = .{ .index = 0, .file = 0 }; + } + global.* = gop.index; + + if (nlist.undf()) continue; + if (gop.ref.getFile(macho_file) == null) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; + continue; + } + + if (self.asFile().getSymbolRank(.{ + .archive = false, + .weak = false, + .tentative = false, + }) < gop.ref.getSymbol(macho_file).?.getSymbolRank(macho_file)) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; + } + } +} + +pub fn resolveBoundarySymbols(self: *InternalObject, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const gpa = macho_file.base.comp.gpa; + var boundary_symbols = std.StringArrayHashMap(MachO.Ref).init(gpa); + defer boundary_symbols.deinit(); + + for (macho_file.objects.items) |index| { + const object = macho_file.getFile(index).?.object; + for (object.symbols.items, 0..) |sym, i| { + const nlist = object.symtab.items(.nlist)[i]; + if (!nlist.undf() or !nlist.ext()) continue; + const ref = object.getSymbolRef(@intCast(i), macho_file); + if (ref.getFile(macho_file) != null) continue; + const name = sym.getName(macho_file); + if (mem.startsWith(u8, name, "segment$start$") or + mem.startsWith(u8, name, "segment$stop$") or + mem.startsWith(u8, name, "section$start$") or + mem.startsWith(u8, name, "section$stop$")) + { + const gop = try boundary_symbols.getOrPut(name); + if (!gop.found_existing) { + gop.value_ptr.* = .{ .index = @intCast(i), .file = index }; + } + } + } + } + + const nsyms = boundary_symbols.values().len; + try self.boundary_symbols.ensureTotalCapacityPrecise(gpa, nsyms); + try self.symbols.ensureUnusedCapacity(gpa, nsyms); + try self.symtab.ensureUnusedCapacity(gpa, nsyms); + try self.symbols_extra.ensureUnusedCapacity(gpa, nsyms * @sizeOf(Symbol.Extra)); + try self.globals.ensureUnusedCapacity(gpa, nsyms); + + for (boundary_symbols.keys(), boundary_symbols.values()) |name, ref| { + const name_off = try self.addString(gpa, name); + const sym_index = self.addSymbolAssumeCapacity(); + self.boundary_symbols.appendAssumeCapacity(sym_index); + const sym = &self.symbols.items[sym_index]; + sym.name = name_off; + sym.visibility = .local; + const nlist_idx: u32 = @intCast(self.symtab.items.len); + const nlist = self.symtab.addOneAssumeCapacity(); + nlist.* = .{ + .n_strx = name_off.pos, + .n_type = macho.N_SECT, + .n_sect = 0, + .n_desc = 0, + .n_value = 0, + }; + sym.nlist_idx = nlist_idx; + sym.extra = self.addSymbolExtraAssumeCapacity(.{}); + + const idx = ref.getFile(macho_file).?.object.globals.items[ref.index]; + self.globals.addOneAssumeCapacity().* = idx; + macho_file.resolver.values.items[idx - 1] = .{ .index = sym_index, .file = self.index }; + } +} + +pub fn markLive(self: *InternalObject, macho_file: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); + + for (0..self.symbols.items.len) |i| { + const nlist = self.symtab.items[i]; + if (!nlist.ext()) continue; + + const ref = self.getSymbolRef(@intCast(i), macho_file); + const file = ref.getFile(macho_file) orelse continue; + if (file == .object and !file.object.alive) { + file.object.alive = true; + file.object.markLive(macho_file); + } + } } /// Creates a fake input sections __TEXT,__objc_methname and __DATA,__objc_selrefs. -pub fn addObjcMsgsendSections(self: *InternalObject, sym_name: []const u8, macho_file: *MachO) !Atom.Index { - const methname_atom_index = try self.addObjcMethnameSection(sym_name, macho_file); - return try self.addObjcSelrefsSection(methname_atom_index, macho_file); +pub fn addObjcMsgsendSections(self: *InternalObject, sym_name: []const u8, macho_file: *MachO) !Symbol.Index { + const methname_sym_index = try self.addObjcMethnameSection(sym_name, macho_file); + return try self.addObjcSelrefsSection(methname_sym_index, macho_file); } -fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_file: *MachO) !Atom.Index { +fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_file: *MachO) !Symbol.Index { const gpa = macho_file.base.comp.gpa; - const atom_index = try macho_file.addAtom(); - try self.atoms.append(gpa, atom_index); - - const atom = macho_file.getAtom(atom_index).?; - atom.atom_index = atom_index; - atom.file = self.index; + const atom_index = try self.addAtom(gpa); + try self.atoms_indexes.append(gpa, atom_index); + const atom = self.getAtom(atom_index).?; atom.size = methname.len + 1; atom.alignment = .@"1"; @@ -63,19 +264,34 @@ fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_fil try self.objc_methnames.ensureUnusedCapacity(gpa, methname.len + 1); self.objc_methnames.writer(gpa).print("{s}\x00", .{methname}) catch unreachable; + const name_str = try self.addString(gpa, "ltmp"); + const sym_index = try self.addSymbol(gpa); + const sym = &self.symbols.items[sym_index]; + sym.name = name_str; + sym.atom_ref = .{ .index = atom_index, .file = self.index }; + sym.extra = try self.addSymbolExtra(gpa, .{}); + const nlist_idx: u32 = @intCast(self.symtab.items.len); + const nlist = try self.symtab.addOne(gpa); + nlist.* = .{ + .n_strx = name_str.pos, + .n_type = macho.N_SECT, + .n_sect = @intCast(n_sect + 1), + .n_desc = 0, + .n_value = 0, + }; + sym.nlist_idx = nlist_idx; + try self.globals.append(gpa, 0); + return atom_index; } -fn addObjcSelrefsSection(self: *InternalObject, methname_atom_index: Atom.Index, macho_file: *MachO) !Atom.Index { - const gpa = macho_file.base.comp.gpa; - const atom_index = try macho_file.addAtom(); - try self.atoms.append(gpa, atom_index); - - const atom = macho_file.getAtom(atom_index).?; - atom.atom_index = atom_index; - atom.file = self.index; +fn addObjcSelrefsSection(self: *InternalObject, methname_sym_index: Symbol.Index, macho_file: *MachO) !Symbol.Index { + const gpa = macho_file.base.allocator; + const atom_index = try self.addAtom(gpa); + try self.atoms_indexes.append(gpa, atom_index); + const atom = self.getAtom(atom_index).?; atom.size = @sizeOf(u64); - atom.alignment = .@"8"; + atom.alignment = 3; const n_sect = try self.addSection(gpa, "__DATA", "__objc_selrefs"); const sect = &self.sections.items(.header)[n_sect]; @@ -89,9 +305,9 @@ fn addObjcSelrefsSection(self: *InternalObject, methname_atom_index: Atom.Index, const relocs = &self.sections.items(.relocs)[n_sect]; try relocs.ensureUnusedCapacity(gpa, 1); relocs.appendAssumeCapacity(.{ - .tag = .local, + .tag = .@"extern", .offset = 0, - .target = methname_atom_index, + .target = methname_sym_index, .addend = 0, .type = .unsigned, .meta = .{ @@ -101,139 +317,283 @@ fn addObjcSelrefsSection(self: *InternalObject, methname_atom_index: Atom.Index, .has_subtractor = false, }, }); - try atom.addExtra(.{ .rel_index = 0, .rel_count = 1 }, macho_file); - atom.flags.relocs = true; + atom.addExtra(.{ .rel_index = 0, .rel_count = 1 }, macho_file); - return atom_index; + const sym_index = try self.addSymbol(gpa); + const sym = &self.symbols.items[sym_index]; + sym.atom_ref = .{ .index = atom_index, .file = self.index }; + sym.extra = try self.addSymbolExtra(gpa, .{}); + const nlist_idx: u32 = @intCast(self.symtab.items.len); + const nlist = try self.symtab.addOne(gpa); + nlist.* = .{ + .n_strx = 0, + .n_type = macho.N_SECT, + .n_sect = @intCast(n_sect + 1), + .n_desc = 0, + .n_value = 0, + }; + sym.nlist_idx = nlist_idx; + try self.globals.append(gpa, 0); + atom.addExtra(.{ .literal_symbol_index = sym_index }, macho_file); + + return sym_index; } -pub fn resolveLiterals(self: InternalObject, lp: *MachO.LiteralPool, macho_file: *MachO) !void { +pub fn resolveObjcMsgSendSymbols(self: *InternalObject, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const gpa = macho_file.base.comp.gpa; + + var objc_msgsend_syms = std.StringArrayHashMap(MachO.Ref).init(gpa); + defer objc_msgsend_syms.deinit(); + + for (macho_file.objects.items) |index| { + const object = macho_file.getFile(index).?.object; + + for (object.symbols.items, 0..) |sym, i| { + const nlist = object.symtab.items(.nlist)[i]; + if (!nlist.ext()) continue; + if (!nlist.undf()) continue; + + const ref = object.getSymbolRef(@intCast(i), macho_file); + if (ref.getFile(macho_file) != null) continue; + + const name = sym.getName(macho_file); + if (mem.startsWith(u8, name, "_objc_msgSend$")) { + const gop = try objc_msgsend_syms.getOrPut(name); + if (!gop.found_existing) { + gop.value_ptr.* = .{ .index = @intCast(i), .file = index }; + } + } + } + } + + for (objc_msgsend_syms.keys(), objc_msgsend_syms.values()) |sym_name, ref| { + const name = MachO.eatPrefix(sym_name, "_objc_msgSend$").?; + const selrefs_index = try self.addObjcMsgsendSections(name, macho_file); + + const name_off = try self.addString(gpa, sym_name); + const sym_index = try self.addSymbol(gpa); + const sym = &self.symbols.items[sym_index]; + sym.name = name_off; + sym.visibility = .hidden; + const nlist_idx: u32 = @intCast(self.symtab.items.len); + const nlist = try self.symtab.addOne(gpa); + nlist.* = .{ + .n_strx = name_off, + .n_type = macho.N_SECT | macho.N_EXT | macho.N_PEXT, + .n_sect = 0, + .n_desc = 0, + .n_value = 0, + }; + sym.nlist_idx = nlist_idx; + sym.extra = try self.addSymbolExtra(gpa, .{ .objc_selrefs = selrefs_index }); + sym.setSectionFlags(.{ .objc_stubs = true }); + + const idx = ref.getFile(macho_file).?.object.globals.items[ref.index]; + try self.globals.append(gpa, idx); + macho_file.resolver.values.items[idx - 1] = .{ .index = sym_index, .file = self.index }; + } +} + +pub fn resolveLiterals(self: *InternalObject, lp: *MachO.LiteralPool, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + const gpa = macho_file.base.comp.gpa; var buffer = std.ArrayList(u8).init(gpa); defer buffer.deinit(); const slice = self.sections.slice(); - for (slice.items(.header), self.atoms.items, 0..) |header, atom_index, n_sect| { - if (Object.isCstringLiteral(header) or Object.isFixedSizeLiteral(header)) { - const data = try self.getSectionData(@intCast(n_sect)); - const atom = macho_file.getAtom(atom_index).?; - const res = try lp.insert(gpa, header.type(), data); - if (!res.found_existing) { - res.atom.* = atom_index; - } - atom.flags.literal_pool = true; - try atom.addExtra(.{ .literal_index = res.index }, macho_file); - } else if (Object.isPtrLiteral(header)) { - const atom = macho_file.getAtom(atom_index).?; - const relocs = atom.getRelocs(macho_file); - assert(relocs.len == 1); - const rel = relocs[0]; - assert(rel.tag == .local); - const target = macho_file.getAtom(rel.target).?; - const addend = std.math.cast(u32, rel.addend) orelse return error.Overflow; - const target_size = std.math.cast(usize, target.size) orelse return error.Overflow; - try buffer.ensureUnusedCapacity(target_size); - buffer.resize(target_size) catch unreachable; - try target.getData(macho_file, buffer.items); - const res = try lp.insert(gpa, header.type(), buffer.items[addend..]); - buffer.clearRetainingCapacity(); - if (!res.found_existing) { - res.atom.* = atom_index; - } - atom.flags.literal_pool = true; - try atom.addExtra(.{ .literal_index = res.index }, macho_file); + for (slice.items(.header), self.getAtoms()) |header, atom_index| { + if (!Object.isPtrLiteral(header)) continue; + const atom = self.getAtom(atom_index).?; + const relocs = atom.getRelocs(macho_file); + assert(relocs.len == 1); + const rel = relocs[0]; + assert(rel.tag == .@"extern"); + const target = rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?; + try buffer.ensureUnusedCapacity(target.size); + buffer.resize(target.size) catch unreachable; + @memcpy(buffer.items, self.getSectionData(target.n_sect)); + const res = try lp.insert(gpa, header.type(), buffer.items); + buffer.clearRetainingCapacity(); + if (!res.found_existing) { + res.ref.* = .{ .index = atom.getExtra(macho_file).literal_symbol_index, .file = self.index }; + } else { + const lp_sym = lp.getSymbol(res.index, macho_file); + const lp_atom = lp_sym.getAtom(macho_file).?; + lp_atom.alignment = @max(lp_atom.alignment, atom.alignment); + atom.flags.alive = false; } + atom.addExtra(.{ .literal_pool_index = res.index }, macho_file); } } -pub fn dedupLiterals(self: InternalObject, lp: MachO.LiteralPool, macho_file: *MachO) void { - for (self.atoms.items) |atom_index| { - const atom = macho_file.getAtom(atom_index) orelse continue; - if (!atom.flags.alive) continue; - if (!atom.flags.relocs) continue; +pub fn dedupLiterals(self: *InternalObject, lp: MachO.LiteralPool, macho_file: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); + + for (self.getAtoms()) |atom_index| { + const atom = self.getAtom(atom_index) orelse continue; + if (!atom.alive.load(.seq_cst)) continue; const relocs = blk: { - const extra = atom.getExtra(macho_file).?; + const extra = atom.getExtra(macho_file); const relocs = self.sections.items(.relocs)[atom.n_sect].items; break :blk relocs[extra.rel_index..][0..extra.rel_count]; }; - for (relocs) |*rel| switch (rel.tag) { - .local => { - const target = macho_file.getAtom(rel.target).?; - if (target.getLiteralPoolIndex(macho_file)) |lp_index| { - const lp_atom = lp.getAtom(lp_index, macho_file); - if (target.atom_index != lp_atom.atom_index) { - lp_atom.alignment = lp_atom.alignment.max(target.alignment); - target.flags.alive = false; - rel.target = lp_atom.atom_index; - } - } - }, - .@"extern" => { - const target_sym = rel.getTargetSymbol(macho_file); - if (target_sym.getAtom(macho_file)) |target_atom| { - if (target_atom.getLiteralPoolIndex(macho_file)) |lp_index| { - const lp_atom = lp.getAtom(lp_index, macho_file); - if (target_atom.atom_index != lp_atom.atom_index) { - lp_atom.alignment = lp_atom.alignment.max(target_atom.alignment); - target_atom.flags.alive = false; - target_sym.atom = lp_atom.atom_index; - } - } - } - }, - }; + for (relocs) |*rel| { + if (rel.tag != .@"extern") continue; + const target_sym_ref = rel.getTargetSymbolRef(atom.*, macho_file); + const file = target_sym_ref.getFile(macho_file) orelse continue; + if (file.getIndex() != self.index) continue; + const target_sym = target_sym_ref.getSymbol(macho_file).?; + const target_atom = target_sym.getAtom(macho_file) orelse continue; + if (!Object.isPtrLiteral(target_atom.getInputSection(macho_file))) continue; + const lp_index = target_atom.getExtra(macho_file).literal_pool_index; + const lp_sym = lp.getSymbol(lp_index, macho_file); + const lp_atom_ref = lp_sym.atom_ref; + if (target_atom.atom_index != lp_atom_ref.index or target_atom.file != lp_atom_ref.file) { + target_sym.atom_ref = lp_atom_ref; + } + } } - for (self.symbols.items) |sym_index| { - const sym = macho_file.getSymbol(sym_index); - if (!sym.flags.objc_stubs) continue; - var extra = sym.getExtra(macho_file).?; - const atom = macho_file.getAtom(extra.objc_selrefs).?; - if (atom.getLiteralPoolIndex(macho_file)) |lp_index| { - const lp_atom = lp.getAtom(lp_index, macho_file); - if (atom.atom_index != lp_atom.atom_index) { - lp_atom.alignment = lp_atom.alignment.max(atom.alignment); - atom.flags.alive = false; - extra.objc_selrefs = lp_atom.atom_index; - sym.setExtra(extra, macho_file); + for (self.symbols.items) |*sym| { + if (!sym.getSectionFlags().objc_stubs) continue; + const extra = sym.getExtra(macho_file); + const file = sym.getFile(macho_file).?; + if (file.getIndex() != self.index) continue; + const tsym = switch (file) { + .dylib => unreachable, + inline else => |x| &x.symbols.items[extra.objc_selrefs], + }; + const atom = tsym.getAtom(macho_file) orelse continue; + if (!Object.isPtrLiteral(atom.getInputSection(macho_file))) continue; + const lp_index = atom.getExtra(macho_file).literal_pool_index; + const lp_sym = lp.getSymbol(lp_index, macho_file); + const lp_atom_ref = lp_sym.atom_ref; + if (atom.atom_index != lp_atom_ref.index or atom.file != lp_atom_ref.file) { + tsym.atom_ref = lp_atom_ref; + } + } +} + +pub fn scanRelocs(self: *InternalObject, macho_file: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); + + if (self.getEntryRef(macho_file)) |ref| { + if (ref.getFile(macho_file) != null) { + const sym = ref.getSymbol(macho_file).?; + if (sym.flags.import) sym.flags.stubs = true; + } + } + if (self.getDyldStubBinderRef(macho_file)) |ref| { + if (ref.getFile(macho_file) != null) { + const sym = ref.getSymbol(macho_file).?; + sym.flags.got = true; + } + } + if (self.getObjcMsgSendRef(macho_file)) |ref| { + if (ref.getFile(macho_file) != null) { + const sym = ref.getSymbol(macho_file).?; + // TODO is it always needed, or only if we are synthesising fast stubs + sym.flags.got = true; + } + } +} + +pub fn allocateSyntheticSymbols(self: *InternalObject, macho_file: *MachO) void { + const text_seg = macho_file.getTextSegment(); + + if (self.mh_execute_header_index) |index| { + const ref = self.getSymbolRef(index, macho_file); + if (ref.getFile(macho_file)) |file| { + if (file.getIndex() == self.index) { + const sym = &self.symbols.items[index]; + sym.value = text_seg.vmaddr; + } + } + } + + if (macho_file.data_sect_index) |idx| { + const sect = macho_file.sections.items(.header)[idx]; + for (&[_]?Symbol.Index{ + self.dso_handle_index, + self.mh_dylib_header_index, + self.dyld_private_index, + }) |maybe_index| { + if (maybe_index) |index| { + const ref = self.getSymbolRef(index, macho_file); + if (ref.getFile(macho_file)) |file| { + if (file.getIndex() == self.index) { + const sym = &self.symbols.items[index]; + sym.value = sect.addr; + sym.out_n_sect = idx; + } + } } } } } -pub fn calcSymtabSize(self: *InternalObject, macho_file: *MachO) !void { - for (self.symbols.items) |sym_index| { - const sym = macho_file.getSymbol(sym_index); - if (sym.getFile(macho_file)) |file| if (file.getIndex() != self.index) continue; +pub fn calcSymtabSize(self: *InternalObject, macho_file: *MachO) void { + for (self.symbols.items, 0..) |*sym, i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + const file = ref.getFile(macho_file) orelse continue; + if (file.getIndex() != self.index) continue; + if (sym.getName(macho_file).len == 0) continue; sym.flags.output_symtab = true; if (sym.isLocal()) { - try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file); + sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file); self.output_symtab_ctx.nlocals += 1; } else if (sym.flags.@"export") { - try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file); + sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file); self.output_symtab_ctx.nexports += 1; } else { assert(sym.flags.import); - try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file); + sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file); self.output_symtab_ctx.nimports += 1; } self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1)); } } -pub fn writeSymtab(self: InternalObject, macho_file: *MachO, ctx: anytype) void { - for (self.symbols.items) |sym_index| { - const sym = macho_file.getSymbol(sym_index); - if (sym.getFile(macho_file)) |file| if (file.getIndex() != self.index) continue; +pub fn writeAtoms(self: *InternalObject, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + + for (self.getAtoms()) |atom_index| { + const atom = self.getAtom(atom_index) orelse continue; + if (!atom.alive.load(.seq_cst)) continue; + const sect = atom.getInputSection(macho_file); + if (sect.isZerofill()) continue; + const off = atom.value; + const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items[off..][0..atom.size]; + @memcpy(buffer, self.getSectionData(atom.n_sect)); + try atom.resolveRelocs(macho_file, buffer); + } +} + +pub fn writeSymtab(self: InternalObject, macho_file: *MachO) void { + var n_strx = self.output_symtab_ctx.stroff; + for (self.symbols.items, 0..) |sym, i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + const file = ref.getFile(macho_file) orelse continue; + if (file.getIndex() != self.index) continue; const idx = sym.getOutputSymtabIndex(macho_file) orelse continue; - const n_strx = @as(u32, @intCast(ctx.strtab.items.len)); - ctx.strtab.appendSliceAssumeCapacity(sym.getName(macho_file)); - ctx.strtab.appendAssumeCapacity(0); - const out_sym = &ctx.symtab.items[idx]; + const out_sym = &macho_file.symtab.items[idx]; out_sym.n_strx = n_strx; sym.setOutputSym(macho_file, out_sym); + const name = sym.getName(macho_file); + @memcpy(macho_file.strtab.items[n_strx..][0..name.len], name); + n_strx += @intCast(name.len); + macho_file.strtab.items[n_strx] = 0; + n_strx += 1; } } @@ -262,32 +622,167 @@ fn getSectionData(self: *const InternalObject, index: u32) error{Overflow}![]con @panic("ref to non-existent section"); } -pub fn getAtomData(self: *const InternalObject, atom: Atom, buffer: []u8) error{Overflow}!void { - assert(buffer.len == atom.size); - const data = try self.getSectionData(atom.n_sect); - const off = std.math.cast(usize, atom.off) orelse return error.Overflow; - const size = std.math.cast(usize, atom.size) orelse return error.Overflow; - @memcpy(buffer, data[off..][0..size]); -} - -pub fn getAtomRelocs(self: *const InternalObject, atom: Atom, macho_file: *MachO) []const Relocation { - if (!atom.flags.relocs) return &[0]Relocation{}; - const extra = atom.getExtra(macho_file).?; - const relocs = self.sections.items(.relocs)[atom.n_sect]; - return relocs.items[extra.rel_index..][0..extra.rel_count]; +pub fn addString(self: *InternalObject, allocator: Allocator, name: []const u8) !u32 { + const off: u32 = @intCast(self.strtab.items.len); + try self.strtab.ensureUnusedCapacity(allocator, name.len + 1); + self.strtab.appendSliceAssumeCapacity(name); + self.strtab.appendAssumeCapacity(0); + return off; } pub fn getString(self: InternalObject, off: u32) [:0]const u8 { - _ = self; - _ = off; - // We don't have any local strings for synthetic atoms. - return ""; + assert(off < self.strtab.items.len); + return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0); } pub fn asFile(self: *InternalObject) File { return .{ .internal = self }; } +fn addAtom(self: *InternalObject, allocator: Allocator) !Atom.Index { + const atom_index: Atom.Index = @intCast(self.atoms.items.len); + const atom = try self.atoms.addOne(allocator); + atom.* = .{ + .file = self.index, + .atom_index = atom_index, + .extra = try self.addAtomExtra(allocator, .{}), + }; + return atom_index; +} + +pub fn getAtom(self: *InternalObject, atom_index: Atom.Index) ?*Atom { + if (atom_index == 0) return null; + assert(atom_index < self.atoms.items.len); + return &self.atoms.items[atom_index]; +} + +pub fn getAtoms(self: InternalObject) []const Atom.Index { + return self.atoms_indexes.items; +} + +fn addAtomExtra(self: *InternalObject, allocator: Allocator, extra: Atom.Extra) !u32 { + const fields = @typeInfo(Atom.Extra).Struct.fields; + try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len); + return self.addAtomExtraAssumeCapacity(extra); +} + +fn addAtomExtraAssumeCapacity(self: *InternalObject, extra: Atom.Extra) u32 { + const index = @as(u32, @intCast(self.atoms_extra.items.len)); + const fields = @typeInfo(Atom.Extra).Struct.fields; + inline for (fields) |field| { + self.atoms_extra.appendAssumeCapacity(switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }); + } + return index; +} + +pub fn getAtomExtra(self: InternalObject, index: u32) Atom.Extra { + const fields = @typeInfo(Atom.Extra).Struct.fields; + var i: usize = index; + var result: Atom.Extra = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.type) { + u32 => self.atoms_extra.items[i], + else => @compileError("bad field type"), + }; + i += 1; + } + return result; +} + +pub fn setAtomExtra(self: *InternalObject, index: u32, extra: Atom.Extra) void { + assert(index > 0); + const fields = @typeInfo(Atom.Extra).Struct.fields; + inline for (fields, 0..) |field, i| { + self.atoms_extra.items[index + i] = switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }; + } +} + +pub fn getEntryRef(self: InternalObject, macho_file: *MachO) ?MachO.Ref { + const index = self.entry_index orelse return null; + return self.getSymbolRef(index, macho_file); +} + +pub fn getDyldStubBinderRef(self: InternalObject, macho_file: *MachO) ?MachO.Ref { + const index = self.dyld_stub_binder_index orelse return null; + return self.getSymbolRef(index, macho_file); +} + +pub fn getDyldPrivateRef(self: InternalObject, macho_file: *MachO) ?MachO.Ref { + const index = self.dyld_private_index orelse return null; + return self.getSymbolRef(index, macho_file); +} + +pub fn getObjcMsgSendRef(self: InternalObject, macho_file: *MachO) ?MachO.Ref { + const index = self.objc_msg_send_index orelse return null; + return self.getSymbolRef(index, macho_file); +} + +pub fn addSymbol(self: *InternalObject, allocator: Allocator) !Symbol.Index { + try self.symbols.ensureUnusedCapacity(allocator, 1); + return self.addSymbolAssumeCapacity(); +} + +pub fn addSymbolAssumeCapacity(self: *InternalObject) Symbol.Index { + const index: Symbol.Index = @intCast(self.symbols.items.len); + const symbol = self.symbols.addOneAssumeCapacity(); + symbol.* = .{ .file = self.index }; + return index; +} + +pub fn getSymbolRef(self: InternalObject, index: Symbol.Index, macho_file: *MachO) MachO.Ref { + const global_index = self.globals.items[index]; + if (macho_file.resolver.get(global_index)) |ref| return ref; + return .{ .index = index, .file = self.index }; +} + +pub fn addSymbolExtra(self: *InternalObject, allocator: Allocator, extra: Symbol.Extra) !u32 { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len); + return self.addSymbolExtraAssumeCapacity(extra); +} + +fn addSymbolExtraAssumeCapacity(self: *InternalObject, extra: Symbol.Extra) u32 { + const index = @as(u32, @intCast(self.symbols_extra.items.len)); + const fields = @typeInfo(Symbol.Extra).Struct.fields; + inline for (fields) |field| { + self.symbols_extra.appendAssumeCapacity(switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }); + } + return index; +} + +pub fn getSymbolExtra(self: InternalObject, index: u32) Symbol.Extra { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + var i: usize = index; + var result: Symbol.Extra = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.type) { + u32 => self.symbols_extra.items[i], + else => @compileError("bad field type"), + }; + i += 1; + } + return result; +} + +pub fn setSymbolExtra(self: *InternalObject, index: u32, extra: Symbol.Extra) void { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + inline for (fields, 0..) |field, i| { + self.symbols_extra.items[index + i] = switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }; + } +} + const FormatContext = struct { self: *InternalObject, macho_file: *MachO, @@ -309,8 +804,8 @@ fn formatAtoms( _ = unused_fmt_string; _ = options; try writer.writeAll(" atoms\n"); - for (ctx.self.atoms.items) |atom_index| { - const atom = ctx.macho_file.getAtom(atom_index).?; + for (ctx.self.getAtoms()) |atom_index| { + const atom = ctx.self.getAtom(atom_index) orelse continue; try writer.print(" {}\n", .{atom.fmt(ctx.macho_file)}); } } @@ -330,10 +825,17 @@ fn formatSymtab( ) !void { _ = unused_fmt_string; _ = options; + const macho_file = ctx.macho_file; + const self = ctx.self; try writer.writeAll(" symbols\n"); - for (ctx.self.symbols.items) |index| { - const global = ctx.macho_file.getSymbol(index); - try writer.print(" {}\n", .{global.fmt(ctx.macho_file)}); + for (self.symbols.items, 0..) |sym, i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + if (ref.getFile(macho_file) == null) { + // TODO any better way of handling this? + try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); + } else { + try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); + } } } @@ -352,6 +854,7 @@ const assert = std.debug.assert; const macho = std.macho; const mem = std.mem; const std = @import("std"); +const trace = @import("../../tracy.zig").trace; const Allocator = std.mem.Allocator; const Atom = @import("Atom.zig"); From b9bac32a2562985ca7a67877169343975fd8f851 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 9 Jul 2024 11:22:24 +0200 Subject: [PATCH 07/45] macho: migrate Atom and Symbol --- src/link/MachO.zig | 171 ++++++++++++++++++++++++------ src/link/MachO/Atom.zig | 166 +++++++++++++---------------- src/link/MachO/InternalObject.zig | 6 ++ src/link/MachO/Object.zig | 14 ++- src/link/MachO/Symbol.zig | 72 +++++++------ 5 files changed, 266 insertions(+), 163 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 9f7a306a74..06f512e584 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -22,16 +22,10 @@ dylibs: std.ArrayListUnmanaged(File.Index) = .{}, segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{}, sections: std.MultiArrayList(Section) = .{}, -symbols: std.ArrayListUnmanaged(Symbol) = .{}, -symbols_extra: std.ArrayListUnmanaged(u32) = .{}, -symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{}, -globals: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{}, +resolver: SymbolResolver = .{}, /// This table will be populated after `scanRelocs` has run. /// Key is symbol index. -undefs: std.AutoHashMapUnmanaged(Symbol.Index, std.ArrayListUnmanaged(Atom.Index)) = .{}, -/// Global symbols we need to resolve for the link to succeed. -undefined_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, -boundary_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, +undefs: std.AutoHashMapUnmanaged(Ref, std.ArrayListUnmanaged(Ref)) = .{}, dyld_info_cmd: macho.dyld_info_command = .{}, symtab_cmd: macho.symtab_command = .{}, @@ -55,19 +49,8 @@ eh_frame_sect_index: ?u8 = null, unwind_info_sect_index: ?u8 = null, objc_stubs_sect_index: ?u8 = null, -mh_execute_header_index: ?Symbol.Index = null, -mh_dylib_header_index: ?Symbol.Index = null, -dyld_private_index: ?Symbol.Index = null, -dyld_stub_binder_index: ?Symbol.Index = null, -dso_handle_index: ?Symbol.Index = null, -objc_msg_send_index: ?Symbol.Index = null, -entry_index: ?Symbol.Index = null, - thunks: std.ArrayListUnmanaged(Thunk) = .{}, -/// String interning table -strings: StringTable = .{}, - /// Output synthetic sections symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, @@ -4196,7 +4179,7 @@ const Section = struct { pub const LiteralPool = struct { table: std.AutoArrayHashMapUnmanaged(void, void) = .{}, keys: std.ArrayListUnmanaged(Key) = .{}, - values: std.ArrayListUnmanaged(Atom.Index) = .{}, + values: std.ArrayListUnmanaged(MachO.Ref) = .{}, data: std.ArrayListUnmanaged(u8) = .{}, pub fn deinit(lp: *LiteralPool, allocator: Allocator) void { @@ -4206,17 +4189,21 @@ pub const LiteralPool = struct { lp.data.deinit(allocator); } - pub fn getAtom(lp: LiteralPool, index: Index, macho_file: *MachO) *Atom { - assert(index < lp.values.items.len); - return macho_file.getAtom(lp.values.items[index]).?; - } - const InsertResult = struct { found_existing: bool, index: Index, - atom: *Atom.Index, + ref: *MachO.Ref, }; + pub fn getSymbolRef(lp: LiteralPool, index: Index) MachO.Ref { + assert(index < lp.values.items.len); + return lp.values.items[index]; + } + + pub fn getSymbol(lp: LiteralPool, index: Index, macho_file: *MachO) *Symbol { + return lp.getSymbolRef(index).getSymbol(macho_file).?; + } + pub fn insert(lp: *LiteralPool, allocator: Allocator, @"type": u8, string: []const u8) !InsertResult { const size: u32 = @intCast(string.len); try lp.data.ensureUnusedCapacity(allocator, size); @@ -4278,12 +4265,6 @@ const HotUpdateState = struct { mach_task: ?std.c.MachTask = null, }; -pub const DynamicRelocs = struct { - rebase_relocs: u32 = 0, - bind_relocs: u32 = 0, - weak_bind_relocs: u32 = 0, -}; - pub const SymtabCtx = struct { ilocal: u32 = 0, istab: u32 = 0, @@ -4293,6 +4274,7 @@ pub const SymtabCtx = struct { nstabs: u32 = 0, nexports: u32 = 0, nimports: u32 = 0, + stroff: u32 = 0, strsize: u32 = 0, }; @@ -4579,6 +4561,131 @@ const UndefinedTreatment = enum { dynamic_lookup, }; +/// A reference to atom or symbol in an input file. +/// If file == 0, symbol is an undefined global. +pub const Ref = struct { + index: u32, + file: File.Index, + + pub fn eql(ref: Ref, other: Ref) bool { + return ref.index == other.index and ref.file == other.file; + } + + pub fn getFile(ref: Ref, macho_file: *MachO) ?File { + return macho_file.getFile(ref.file); + } + + pub fn getAtom(ref: Ref, macho_file: *MachO) ?*Atom { + const file = ref.getFile(macho_file) orelse return null; + return file.getAtom(ref.index); + } + + pub fn getSymbol(ref: Ref, macho_file: *MachO) ?*Symbol { + const file = ref.getFile(macho_file) orelse return null; + return switch (file) { + inline else => |x| &x.symbols.items[ref.index], + }; + } + + pub fn format( + ref: Ref, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = unused_fmt_string; + _ = options; + try writer.print("%{d} in file({d})", .{ ref.index, ref.file }); + } +}; + +pub const SymbolResolver = struct { + keys: std.ArrayListUnmanaged(Key) = .{}, + values: std.ArrayListUnmanaged(Ref) = .{}, + table: std.AutoArrayHashMapUnmanaged(void, void) = .{}, + + const Result = struct { + found_existing: bool, + index: Index, + ref: *Ref, + }; + + pub fn deinit(resolver: *SymbolResolver, allocator: Allocator) void { + resolver.keys.deinit(allocator); + resolver.values.deinit(allocator); + resolver.table.deinit(allocator); + } + + pub fn getOrPut( + resolver: *SymbolResolver, + allocator: Allocator, + ref: Ref, + macho_file: *MachO, + ) !Result { + const adapter = Adapter{ .keys = resolver.keys.items, .macho_file = macho_file }; + const key = Key{ .index = ref.index, .file = ref.file }; + const gop = try resolver.table.getOrPutAdapted(allocator, key, adapter); + if (!gop.found_existing) { + try resolver.keys.append(allocator, key); + _ = try resolver.values.addOne(allocator); + } + return .{ + .found_existing = gop.found_existing, + .index = @intCast(gop.index + 1), + .ref = &resolver.values.items[gop.index], + }; + } + + pub fn get(resolver: SymbolResolver, index: Index) ?Ref { + if (index == 0) return null; + return resolver.values.items[index - 1]; + } + + pub fn reset(resolver: *SymbolResolver) void { + resolver.keys.clearRetainingCapacity(); + resolver.values.clearRetainingCapacity(); + resolver.table.clearRetainingCapacity(); + } + + const Key = struct { + index: Symbol.Index, + file: File.Index, + + fn getName(key: Key, macho_file: *MachO) [:0]const u8 { + const ref = Ref{ .index = key.index, .file = key.file }; + return ref.getSymbol(macho_file).?.getName(macho_file); + } + + fn eql(key: Key, other: Key, macho_file: *MachO) bool { + const key_name = key.getName(macho_file); + const other_name = other.getName(macho_file); + return mem.eql(u8, key_name, other_name); + } + + fn hash(key: Key, macho_file: *MachO) u32 { + const name = key.getName(macho_file); + return @truncate(Hash.hash(0, name)); + } + }; + + const Adapter = struct { + keys: []const Key, + macho_file: *MachO, + + pub fn eql(ctx: @This(), key: Key, b_void: void, b_map_index: usize) bool { + _ = b_void; + const other = ctx.keys[b_map_index]; + return key.eql(other, ctx.macho_file); + } + + pub fn hash(ctx: @This(), key: Key) u32 { + return key.hash(ctx.macho_file); + } + }; + + pub const Index = u32; +}; + const MachO = @This(); const std = @import("std"); diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index c7a65843bb..2ed86ef66f 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -47,16 +47,6 @@ pub fn getFile(self: Atom, macho_file: *MachO) File { return macho_file.getFile(self.file).?; } -pub fn getData(self: Atom, macho_file: *MachO, buffer: []u8) !void { - assert(buffer.len == self.size); - switch (self.getFile(macho_file)) { - .internal => |x| try x.getAtomData(self, buffer), - .object => |x| try x.getAtomData(macho_file, self, buffer), - .zig_object => |x| try x.getAtomData(macho_file, self, buffer), - else => unreachable, - } -} - pub fn getRelocs(self: Atom, macho_file: *MachO) []const Relocation { return switch (self.getFile(macho_file)) { .dylib => unreachable, @@ -88,8 +78,7 @@ pub fn getPriority(self: Atom, macho_file: *MachO) u64 { } pub fn getUnwindRecords(self: Atom, macho_file: *MachO) []const UnwindInfo.Record.Index { - if (!self.flags.unwind) return &[0]UnwindInfo.Record.Index{}; - const extra = self.getExtra(macho_file).?; + const extra = self.getExtra(macho_file); return switch (self.getFile(macho_file)) { .dylib => unreachable, .zig_object, .internal => &[0]UnwindInfo.Record.Index{}, @@ -110,44 +99,39 @@ pub fn markUnwindRecordsDead(self: Atom, macho_file: *MachO) void { } pub fn getThunk(self: Atom, macho_file: *MachO) *Thunk { - assert(self.flags.thunk); - const extra = self.getExtra(macho_file).?; + const extra = self.getExtra(macho_file); return macho_file.getThunk(extra.thunk); } -pub fn getLiteralPoolIndex(self: Atom, macho_file: *MachO) ?MachO.LiteralPool.Index { - if (!self.flags.literal_pool) return null; - return self.getExtra(macho_file).?.literal_index; -} - const AddExtraOpts = struct { thunk: ?u32 = null, rel_index: ?u32 = null, rel_count: ?u32 = null, + rel_out_index: ?u32 = null, + rel_out_count: ?u32 = null, unwind_index: ?u32 = null, unwind_count: ?u32 = null, - literal_index: ?u32 = null, + literal_pool_index: ?u32 = null, + literal_symbol_index: ?u32 = null, }; -pub fn addExtra(atom: *Atom, opts: AddExtraOpts, macho_file: *MachO) !void { - if (atom.getExtra(macho_file) == null) { - atom.extra = try macho_file.addAtomExtra(.{}); - } - var extra = atom.getExtra(macho_file).?; +pub fn addExtra(atom: *Atom, opts: AddExtraOpts, macho_file: *MachO) void { + const file = atom.getFile(macho_file); + var extra = file.getAtomExtra(atom.extra); inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| { if (@field(opts, field.name)) |x| { @field(extra, field.name) = x; } } - atom.setExtra(extra, macho_file); + file.setAtomExtra(atom.extra, extra); } -pub inline fn getExtra(atom: Atom, macho_file: *MachO) ?Extra { - return macho_file.getAtomExtra(atom.extra); +pub inline fn getExtra(atom: Atom, macho_file: *MachO) Extra { + return atom.getFile(macho_file).getAtomExtra(atom.extra); } pub inline fn setExtra(atom: Atom, extra: Extra, macho_file: *MachO) void { - macho_file.setAtomExtra(atom.extra, extra); + atom.getFile(macho_file).setAtomExtra(atom.extra, extra); } pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 { @@ -467,7 +451,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void { switch (rel.type) { .branch => { - const symbol = rel.getTargetSymbol(macho_file); + const symbol = rel.getTargetSymbol(self, macho_file); if (symbol.flags.import or (symbol.flags.@"export" and symbol.flags.weak) or symbol.flags.interposable) { symbol.flags.stubs = true; if (symbol.flags.weak) { @@ -482,7 +466,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void { .got_load_page, .got_load_pageoff, => { - const symbol = rel.getTargetSymbol(macho_file); + const symbol = rel.getTargetSymbol(self, macho_file); if (symbol.flags.import or (symbol.flags.@"export" and symbol.flags.weak) or symbol.flags.interposable or @@ -496,18 +480,18 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void { }, .zig_got_load => { - assert(rel.getTargetSymbol(macho_file).flags.has_zig_got); + assert(rel.getTargetSymbol(self, macho_file).flags.has_zig_got); }, .got => { - rel.getTargetSymbol(macho_file).flags.needs_got = true; + rel.getTargetSymbol(self, macho_file).flags.needs_got = true; }, .tlv, .tlvp_page, .tlvp_pageoff, => { - const symbol = rel.getTargetSymbol(macho_file); + const symbol = rel.getTargetSymbol(self, macho_file); if (!symbol.flags.tlv) { try macho_file.reportParseError2( self.getFile(macho_file).getIndex(), @@ -526,7 +510,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void { .unsigned => { if (rel.meta.length == 3) { // TODO this really should check if this is pointer width if (rel.tag == .@"extern") { - const symbol = rel.getTargetSymbol(macho_file); + const symbol = rel.getTargetSymbol(self, macho_file); if (symbol.isTlvInit(macho_file)) { macho_file.has_tlv = true; continue; @@ -559,14 +543,15 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void { fn reportUndefSymbol(self: Atom, rel: Relocation, macho_file: *MachO) !bool { if (rel.tag == .local) return false; - const sym = rel.getTargetSymbol(macho_file); - if (sym.getFile(macho_file) == null) { + const file = self.getFile(macho_file); + const ref = file.getSymbolRef(rel.target, macho_file); + if (ref.getFile(macho_file) == null) { const gpa = macho_file.base.comp.gpa; - const gop = try macho_file.undefs.getOrPut(gpa, rel.target); + const gop = try macho_file.undefs.getOrPut(gpa, .{ .index = rel.target, .file = self.file }); if (!gop.found_existing) { gop.value_ptr.* = .{}; } - try gop.value_ptr.append(gpa, self.atom_index); + try gop.value_ptr.append(gpa, .{ .index = self.atom_index, .file = self.file }); return true; } @@ -582,7 +567,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void { const name = self.getName(macho_file); const relocs = self.getRelocs(macho_file); - relocs_log.debug("{x}: {s}", .{ self.getAddress(macho_file), name }); + relocs_log.debug("{x}: {s}", .{ self.value, name }); var has_error = false; var stream = std.io.fixedBufferStream(buffer); @@ -593,7 +578,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void { const subtractor = if (rel.meta.has_subtractor) relocs[i - 1] else null; if (rel.tag == .@"extern") { - if (rel.getTargetSymbol(macho_file).getFile(macho_file) == null) continue; + if (rel.getTargetSymbol(self, macho_file).getFile(macho_file) == null) continue; } try stream.seekTo(rel_offset); @@ -601,8 +586,8 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void { switch (err) { error.RelaxFail => { const target = switch (rel.tag) { - .@"extern" => rel.getTargetSymbol(macho_file).getName(macho_file), - .local => rel.getTargetAtom(macho_file).getName(macho_file), + .@"extern" => rel.getTargetSymbol(self, macho_file).getName(macho_file), + .local => rel.getTargetAtom(self, macho_file).getName(macho_file), }; try macho_file.reportParseError2( file.getIndex(), @@ -642,12 +627,12 @@ fn resolveRelocInner( const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow; const P = @as(i64, @intCast(self.getAddress(macho_file))) + @as(i64, @intCast(rel_offset)); const A = rel.addend + rel.getRelocAddend(cpu_arch); - const S: i64 = @intCast(rel.getTargetAddress(macho_file)); - const G: i64 = @intCast(rel.getGotTargetAddress(macho_file)); + const S: i64 = @intCast(rel.getTargetAddress(self, macho_file)); + const G: i64 = @intCast(rel.getGotTargetAddress(self, macho_file)); const TLS = @as(i64, @intCast(macho_file.getTlsAddress())); - const SUB = if (subtractor) |sub| @as(i64, @intCast(sub.getTargetAddress(macho_file))) else 0; + const SUB = if (subtractor) |sub| @as(i64, @intCast(sub.getTargetAddress(self, macho_file))) else 0; // Address of the __got_zig table entry if any. - const ZIG_GOT = @as(i64, @intCast(rel.getZigGotTargetAddress(macho_file))); + const ZIG_GOT = @as(i64, @intCast(rel.getZigGotTargetAddress(self, macho_file))); const divExact = struct { fn divExact(atom: Atom, r: Relocation, num: u12, den: u12, ctx: *MachO) !u12 { @@ -668,7 +653,7 @@ fn resolveRelocInner( rel_offset, @tagName(rel.type), S + A - SUB, - rel.getTargetAtom(macho_file).atom_index, + rel.getTargetAtom(self, macho_file).atom_index, }), .@"extern" => relocs_log.debug(" {x}<+{d}>: {s}: [=> {x}] G({x}) ZG({x}) ({s})", .{ P, @@ -677,7 +662,7 @@ fn resolveRelocInner( S + A - SUB, G + A, ZIG_GOT + A, - rel.getTargetSymbol(macho_file).getName(macho_file), + rel.getTargetSymbol(self, macho_file).getName(macho_file), }), } @@ -688,7 +673,7 @@ fn resolveRelocInner( assert(!rel.meta.pcrel); if (rel.meta.length == 3) { if (rel.tag == .@"extern") { - const sym = rel.getTargetSymbol(macho_file); + const sym = rel.getTargetSymbol(self, macho_file); if (sym.isTlvInit(macho_file)) { try writer.writeInt(u64, @intCast(S - TLS), .little); return; @@ -718,7 +703,7 @@ fn resolveRelocInner( .aarch64 => { const disp: i28 = math.cast(i28, S + A - P) orelse blk: { const thunk = self.getThunk(macho_file); - const S_: i64 = @intCast(thunk.getTargetAddress(rel.target, macho_file)); + const S_: i64 = @intCast(thunk.getTargetAddress(rel.getTargetSymbolRef(self, macho_file), macho_file)); break :blk math.cast(i28, S_ + A - P) orelse return error.Overflow; }; aarch64.writeBranchImm(disp, code[rel_offset..][0..4]); @@ -731,7 +716,7 @@ fn resolveRelocInner( assert(rel.tag == .@"extern"); assert(rel.meta.length == 2); assert(rel.meta.pcrel); - if (rel.getTargetSymbol(macho_file).flags.has_got) { + if (rel.getTargetSymbol(self, macho_file).flags.has_got) { try writer.writeInt(i32, @intCast(G + A - P), .little); } else { try x86_64.relaxGotLoad(self, code[rel_offset - 3 ..], rel, macho_file); @@ -754,7 +739,7 @@ fn resolveRelocInner( assert(rel.tag == .@"extern"); assert(rel.meta.length == 2); assert(rel.meta.pcrel); - const sym = rel.getTargetSymbol(macho_file); + const sym = rel.getTargetSymbol(self, macho_file); if (sym.flags.tlv_ptr) { const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file)); try writer.writeInt(i32, @intCast(S_ + A - P), .little); @@ -777,7 +762,7 @@ fn resolveRelocInner( assert(rel.tag == .@"extern"); assert(rel.meta.length == 2); assert(rel.meta.pcrel); - const sym = rel.getTargetSymbol(macho_file); + const sym = rel.getTargetSymbol(self, macho_file); const source = math.cast(u64, P) orelse return error.Overflow; const target = target: { const target = switch (rel.type) { @@ -836,7 +821,7 @@ fn resolveRelocInner( assert(rel.meta.length == 2); assert(!rel.meta.pcrel); - const sym = rel.getTargetSymbol(macho_file); + const sym = rel.getTargetSymbol(self, macho_file); const target = target: { const target = if (sym.flags.tlv_ptr) blk: { const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file)); @@ -980,48 +965,47 @@ pub fn calcNumRelocs(self: Atom, macho_file: *MachO) u32 { } } -pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.ArrayList(macho.relocation_info)) !void { +pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.relocation_info) !void { const tracy = trace(@src()); defer tracy.end(); const cpu_arch = macho_file.getTarget().cpu.arch; const relocs = self.getRelocs(macho_file); - var stream = std.io.fixedBufferStream(code); + var i: usize = 0; for (relocs) |rel| { + defer i += 1; const rel_offset = rel.offset - self.off; const r_address: i32 = math.cast(i32, self.value + rel_offset) orelse return error.Overflow; const r_symbolnum = r_symbolnum: { const r_symbolnum: u32 = switch (rel.tag) { - .local => rel.getTargetAtom(macho_file).out_n_sect + 1, - .@"extern" => rel.getTargetSymbol(macho_file).getOutputSymtabIndex(macho_file).?, + .local => rel.getTargetAtom(self, macho_file).out_n_sect + 1, + .@"extern" => rel.getTargetSymbol(self, macho_file).getOutputSymtabIndex(macho_file).?, }; break :r_symbolnum math.cast(u24, r_symbolnum) orelse return error.Overflow; }; const r_extern = rel.tag == .@"extern"; var addend = rel.addend + rel.getRelocAddend(cpu_arch); if (rel.tag == .local) { - const target: i64 = @intCast(rel.getTargetAddress(macho_file)); + const target: i64 = @intCast(rel.getTargetAddress(self, macho_file)); addend += target; } - try stream.seekTo(rel_offset); - switch (cpu_arch) { .aarch64 => { if (rel.type == .unsigned) switch (rel.meta.length) { 0, 1 => unreachable, - 2 => try stream.writer().writeInt(i32, @truncate(addend), .little), - 3 => try stream.writer().writeInt(i64, addend, .little), + 2 => mem.writeInt(i32, code[rel_offset..][0..4], @truncate(addend), .little), + 3 => mem.writeInt(i64, code[rel_offset..][0..8], addend, .little), } else if (addend > 0) { - buffer.appendAssumeCapacity(.{ + buffer[i] = .{ .r_address = r_address, .r_symbolnum = @bitCast(math.cast(i24, addend) orelse return error.Overflow), .r_pcrel = 0, .r_length = 2, .r_extern = 0, .r_type = @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_ADDEND), - }); + }; } const r_type: macho.reloc_type_arm64 = switch (rel.type) { @@ -1045,14 +1029,14 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra .tlv, => unreachable, }; - buffer.appendAssumeCapacity(.{ + buffer[i] = .{ .r_address = r_address, .r_symbolnum = r_symbolnum, .r_pcrel = @intFromBool(rel.meta.pcrel), .r_extern = @intFromBool(r_extern), .r_length = rel.meta.length, .r_type = @intFromEnum(r_type), - }); + }; }, .x86_64 => { if (rel.meta.pcrel) { @@ -1064,8 +1048,8 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra } switch (rel.meta.length) { 0, 1 => unreachable, - 2 => try stream.writer().writeInt(i32, @truncate(addend), .little), - 3 => try stream.writer().writeInt(i64, addend, .little), + 2 => mem.writeInt(i32, code[rel_offset..][0..4], @truncate(addend), .little), + 3 => mem.writeInt(i64, code[rel_offset..][0..8], addend, .little), } const r_type: macho.reloc_type_x86_64 = switch (rel.type) { @@ -1089,18 +1073,20 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra .tlvp_pageoff, => unreachable, }; - buffer.appendAssumeCapacity(.{ + buffer[i] = .{ .r_address = r_address, .r_symbolnum = r_symbolnum, .r_pcrel = @intFromBool(rel.meta.pcrel), .r_extern = @intFromBool(r_extern), .r_length = rel.meta.length, .r_type = @intFromEnum(r_type), - }); + }; }, else => unreachable, } } + + assert(i == buffer.len); } pub fn format( @@ -1139,16 +1125,15 @@ fn format2( const atom = ctx.atom; const macho_file = ctx.macho_file; const file = atom.getFile(macho_file); - try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d})", .{ - atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file), - atom.out_n_sect, atom.alignment, atom.size, - atom.getRelocs(macho_file).len, + try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d}) : thunk({d})", .{ + atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file), + atom.out_n_sect, atom.alignment, atom.size, + atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk, }); - if (atom.flags.thunk) try writer.print(" : thunk({d})", .{atom.getExtra(macho_file).?.thunk}); if (!atom.flags.alive) try writer.writeAll(" : [*]"); - if (atom.flags.unwind) { + if (atom.getUnwindRecords(macho_file).len > 0) { try writer.writeAll(" : unwind{ "); - const extra = atom.getExtra(macho_file).?; + const extra = atom.getExtra(macho_file); for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| { const rec = file.object.getUnwindRecord(index); try writer.print("{d}", .{index}); @@ -1167,18 +1152,6 @@ pub const Flags = packed struct { /// Specifies if this atom has been visited during garbage collection. visited: bool = false, - - /// Whether this atom has a range extension thunk. - thunk: bool = false, - - /// Whether this atom has any relocations. - relocs: bool = false, - - /// Whether this atom has any unwind records. - unwind: bool = false, - - /// Whether this atom has LiteralPool entry. - literal_pool: bool = false, }; pub const Extra = struct { @@ -1191,6 +1164,12 @@ pub const Extra = struct { /// Count of relocations belonging to this atom. rel_count: u32 = 0, + /// Start index of relocations being written out to file for this atom. + rel_out_index: u32 = 0, + + /// Count of relocations written out to file for this atom. + rel_out_count: u32 = 0, + /// Start index of relocations belonging to this atom. unwind_index: u32 = 0, @@ -1198,7 +1177,10 @@ pub const Extra = struct { unwind_count: u32 = 0, /// Index into LiteralPool entry for this atom. - literal_index: u32 = 0, + literal_pool_index: u32 = 0, + + /// Index into the File's symbol table for local symbol representing this literal atom. + literal_symbol_index: u32 = 0, }; pub const Alignment = @import("../../InternPool.zig").Alignment; diff --git a/src/link/MachO/InternalObject.zig b/src/link/MachO/InternalObject.zig index 2499f01a03..32e89116b1 100644 --- a/src/link/MachO/InternalObject.zig +++ b/src/link/MachO/InternalObject.zig @@ -639,6 +639,12 @@ pub fn asFile(self: *InternalObject) File { return .{ .internal = self }; } +pub fn getAtomRelocs(self: *const InternalObject, atom: Atom, macho_file: *MachO) []const Relocation { + const extra = atom.getExtra(macho_file).?; + const relocs = self.sections.items(.relocs)[atom.n_sect]; + return relocs.items[extra.rel_index..][0..extra.rel_count]; +} + fn addAtom(self: *InternalObject, allocator: Allocator) !Atom.Index { const atom_index: Atom.Index = @intCast(self.atoms.items.len); const atom = try self.atoms.addOne(allocator); diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index fcbb31f4ed..17dd198c6b 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -1860,7 +1860,7 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void { const name = sym.getName(macho_file); if (name.len > 0 and (name[0] == 'L' or name[0] == 'l')) continue; } - const sect = macho_file.sections.items(.header)[sym.out_n_sect]; + const sect = macho_file.sections.items(.header)[sym.getOutputSectionIndex(macho_file)]; if (sect.isCode()) { self.output_symtab_ctx.nstabs += 4; // N_BNSYM, N_FUN, N_FUN, N_ENSYM } else if (sym.visibility == .global) { @@ -2198,13 +2198,13 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { const name = sym.getName(macho_file); if (name.len > 0 and (name[0] == 'L' or name[0] == 'l')) continue; } - const sect = macho_file.sections.items(.header)[sym.out_n_sect]; + const sect = macho_file.sections.items(.header)[sym.getOutputSectionIndex(macho_file)]; const sym_n_strx = n_strx: { const symtab_index = sym.getOutputSymtabIndex(macho_file).?; const osym = macho_file.symtab.items[symtab_index]; break :n_strx osym.n_strx; }; - const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.out_n_sect + 1) else 0; + const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.getOutputSectionIndex(macho_file) + 1) else 0; const sym_n_value = sym.getAddress(.{}, macho_file); const sym_size = sym.getSize(macho_file); if (sect.isCode()) { @@ -2299,7 +2299,7 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { const osym = macho_file.symtab.items[symtab_index]; break :n_strx osym.n_strx; }; - const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.out_n_sect + 1) else 0; + const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.getOutputSectionIndex(macho_file) + 1) else 0; const sym_n_value = sym.getAddress(.{}, macho_file); const sym_size = sym.getSize(macho_file); if (stab.is_func) { @@ -2340,6 +2340,12 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { } } +pub fn getAtomRelocs(self: *const Object, atom: Atom, macho_file: *MachO) []const Relocation { + const extra = atom.getExtra(macho_file).?; + const relocs = self.sections.items(.relocs)[atom.n_sect]; + return relocs.items[extra.rel_index..][0..extra.rel_count]; +} + fn addString(self: *Object, allocator: Allocator, name: [:0]const u8) error{OutOfMemory}!u32 { const off: u32 = @intCast(self.strtab.items.len); try self.strtab.ensureUnusedCapacity(allocator, name.len + 1); diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index e413fe818d..003996fc95 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -9,17 +9,16 @@ name: u32 = 0, /// File where this symbol is defined. file: File.Index = 0, -/// Atom containing this symbol if any. -/// Index of 0 means there is no associated atom with this symbol. +/// Reference to Atom containing this symbol if any. /// Use `getAtom` to get the pointer to the atom. -atom: Atom.Index = 0, +atom_ref: MachO.Ref = .{ .index = 0, .file = 0 }, /// Assigned output section index for this symbol. out_n_sect: u8 = 0, /// Index of the source nlist this symbol references. /// Use `getNlist` to pull the nlist from the relevant file. -nlist_idx: Index = 0, +nlist_idx: u32 = 0, /// Misc flags for the symbol packaged as packed struct for compression. flags: Flags = .{}, @@ -55,16 +54,19 @@ pub fn weakRef(symbol: Symbol, macho_file: *MachO) bool { } pub fn getName(symbol: Symbol, macho_file: *MachO) [:0]const u8 { - if (symbol.flags.global) return macho_file.strings.getAssumeExists(symbol.name); return switch (symbol.getFile(macho_file).?) { - .dylib => unreachable, // There are no local symbols for dylibs .zig_object => |x| x.strtab.getAssumeExists(symbol.name), inline else => |x| x.getString(symbol.name), }; } pub fn getAtom(symbol: Symbol, macho_file: *MachO) ?*Atom { - return macho_file.getAtom(symbol.atom); + return symbol.atom_ref.getAtom(macho_file); +} + +pub fn getOutputSectionIndex(symbol: Symbol, macho_file: *MachO) u8 { + if (symbol.getAtom(macho_file)) |atom| return atom.out_n_sect; + return symbol.out_n_sect; } pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File { @@ -75,8 +77,10 @@ pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File { pub fn getNlist(symbol: Symbol, macho_file: *MachO) macho.nlist_64 { const file = symbol.getFile(macho_file).?; return switch (file) { + .dylib => unreachable, + .zig_object => unreachable, .object => |x| x.symtab.items(.nlist)[symbol.nlist_idx], - else => unreachable, + .internal => |x| x.symtab.items[symbol.nlist_idx], }; } @@ -124,33 +128,35 @@ pub fn getAddress(symbol: Symbol, opts: struct { pub fn getGotAddress(symbol: Symbol, macho_file: *MachO) u64 { if (!symbol.flags.has_got) return 0; - const extra = symbol.getExtra(macho_file).?; + const extra = symbol.getExtra(macho_file); return macho_file.got.getAddress(extra.got, macho_file); } pub fn getStubsAddress(symbol: Symbol, macho_file: *MachO) u64 { if (!symbol.flags.stubs) return 0; - const extra = symbol.getExtra(macho_file).?; + const extra = symbol.getExtra(macho_file); return macho_file.stubs.getAddress(extra.stubs, macho_file); } pub fn getObjcStubsAddress(symbol: Symbol, macho_file: *MachO) u64 { if (!symbol.flags.objc_stubs) return 0; - const extra = symbol.getExtra(macho_file).?; + const extra = symbol.getExtra(macho_file); return macho_file.objc_stubs.getAddress(extra.objc_stubs, macho_file); } pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 { if (!symbol.flags.objc_stubs) return 0; - const extra = symbol.getExtra(macho_file).?; - const atom = macho_file.getAtom(extra.objc_selrefs).?; - assert(atom.flags.alive); - return atom.getAddress(macho_file); + const extra = symbol.getExtra(macho_file); + const file = symbol.getFile(macho_file).?; + return switch (file) { + .dylib, .zig_object => unreachable, + .object, .internal => |x| x.symbols.items[extra.objc_selrefs].getAddress(.{}, macho_file), + }; } pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 { if (!symbol.flags.tlv_ptr) return 0; - const extra = symbol.getExtra(macho_file).?; + const extra = symbol.getExtra(macho_file); return macho_file.tlv_ptr.getAddress(extra.tlv_ptr, macho_file); } @@ -162,14 +168,14 @@ const GetOrCreateZigGotEntryResult = struct { pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, macho_file: *MachO) !GetOrCreateZigGotEntryResult { assert(!macho_file.base.isRelocatable()); assert(symbol.flags.needs_zig_got); - if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).?.zig_got }; + if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).zig_got }; const index = try macho_file.zig_got.addSymbol(symbol_index, macho_file); return .{ .found_existing = false, .index = index }; } pub fn getZigGotAddress(symbol: Symbol, macho_file: *MachO) u64 { if (!symbol.flags.has_zig_got) return 0; - const extras = symbol.getExtra(macho_file).?; + const extras = symbol.getExtra(macho_file); return macho_file.zig_got.entryAddress(extras.zig_got, macho_file); } @@ -202,11 +208,8 @@ const AddExtraOpts = struct { symtab: ?u32 = null, }; -pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) !void { - if (symbol.getExtra(macho_file) == null) { - symbol.extra = try macho_file.addSymbolExtra(.{}); - } - var extra = symbol.getExtra(macho_file).?; +pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) void { + var extra = symbol.getExtra(macho_file); inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| { if (@field(opts, field.name)) |x| { @field(extra, field.name) = x; @@ -215,18 +218,22 @@ pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) !void { symbol.setExtra(extra, macho_file); } -pub inline fn getExtra(symbol: Symbol, macho_file: *MachO) ?Extra { - return macho_file.getSymbolExtra(symbol.extra); +pub inline fn getExtra(symbol: Symbol, macho_file: *MachO) Extra { + return switch (symbol.getFile(macho_file).?) { + inline else => |x| x.getSymbolExtra(symbol.extra), + }; } pub inline fn setExtra(symbol: Symbol, extra: Extra, macho_file: *MachO) void { - macho_file.setSymbolExtra(symbol.extra, extra); + return switch (symbol.getFile(macho_file).?) { + inline else => |x| x.setSymbolExtra(symbol.extra, extra), + }; } pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) void { if (symbol.isLocal()) { out.n_type = if (symbol.flags.abs) macho.N_ABS else macho.N_SECT; - out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.out_n_sect + 1); + out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.getOutputSectionIndex(macho_file) + 1); out.n_desc = 0; out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file); @@ -238,7 +245,7 @@ pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) vo assert(symbol.visibility == .global); out.n_type = macho.N_EXT; out.n_type |= if (symbol.flags.abs) macho.N_ABS else macho.N_SECT; - out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.out_n_sect + 1); + out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.getOutputSectionIndex(macho_file) + 1); out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file); out.n_desc = 0; @@ -318,8 +325,8 @@ fn format2( symbol.getAddress(.{}, ctx.macho_file), }); if (symbol.getFile(ctx.macho_file)) |file| { - if (symbol.out_n_sect != 0) { - try writer.print(" : sect({d})", .{symbol.out_n_sect}); + if (symbol.getOutputSectionIndex(ctx.macho_file) != 0) { + try writer.print(" : sect({d})", .{symbol.getOutputSectionIndex(ctx.macho_file)}); } if (symbol.getAtom(ctx.macho_file)) |atom| { try writer.print(" : atom({d})", .{atom.atom_index}); @@ -346,11 +353,6 @@ pub const Flags = packed struct { /// Whether the symbol is exported at runtime. @"export": bool = false, - /// Whether the symbol is effectively an extern and takes part in global - /// symbol resolution. Then, its name will be saved in global string interning - /// table. - global: bool = false, - /// Whether this symbol is weak. weak: bool = false, From 355992cbdff96db66dd79e8bbbee04cb081f683f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 9 Jul 2024 11:34:38 +0200 Subject: [PATCH 08/45] macho: migrate some of MachO driver --- src/link/MachO.zig | 111 ++++++++++---------------------------- src/link/MachO/Object.zig | 4 +- 2 files changed, 32 insertions(+), 83 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 06f512e584..408bd8cbe1 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -224,15 +224,8 @@ pub fn createEmpty( // Append null file try self.files.append(gpa, .null); - // Atom at index 0 is reserved as null atom - try self.atoms.append(gpa, .{}); - try self.atoms_extra.append(gpa, 0); // Append empty string to string tables - try self.strings.buffer.append(gpa, 0); try self.strtab.append(gpa, 0); - // Append null symbols - try self.symbols.append(gpa, .{}); - try self.symbols_extra.append(gpa, 0); if (opt_zcu) |zcu| { if (!use_llvm) { @@ -301,10 +294,7 @@ pub fn deinit(self: *MachO) void { } self.sections.deinit(gpa); - self.symbols.deinit(gpa); - self.symbols_extra.deinit(gpa); - self.symbols_free_list.deinit(gpa); - self.globals.deinit(gpa); + self.resolver.deinit(gpa); { var it = self.undefs.iterator(); while (it.next()) |entry| { @@ -312,10 +302,7 @@ pub fn deinit(self: *MachO) void { } self.undefs.deinit(gpa); } - self.undefined_symbols.deinit(gpa); - self.boundary_symbols.deinit(gpa); - self.strings.deinit(gpa); self.symtab.deinit(gpa); self.strtab.deinit(gpa); self.got.deinit(gpa); @@ -330,11 +317,6 @@ pub fn deinit(self: *MachO) void { self.export_trie.deinit(gpa); self.unwind_info.deinit(gpa); - self.atoms.deinit(gpa); - self.atoms_extra.deinit(gpa); - for (self.thunks.items) |*thunk| { - thunk.deinit(gpa); - } self.thunks.deinit(gpa); } @@ -513,17 +495,14 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); self.files.set(index, .{ .internal = .{ .index = index } }); self.internal_object = index; + const object = self.getInternalObject().?; + try object.init(gpa); + try object.initSymbols(self); } - try self.addUndefinedGlobals(); try self.resolveSymbols(); - try self.parseDebugInfo(); - try self.resolveSyntheticSymbols(); - - try self.convertTentativeDefinitions(); - try self.createObjcSections(); + try self.convertTentativeDefsAndResolveSpecialSymbols(); try self.dedupLiterals(); - try self.claimUnresolved(); if (self.base.gc_sections) { try dead_strip.gcAtoms(self); @@ -546,6 +525,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n dylib.ordinal = @intCast(ord); } + try self.claimUnresolved(); + self.scanRelocs() catch |err| switch (err) { error.HasUndefinedSymbols => return error.FlushFailure, else => |e| { @@ -1307,35 +1288,6 @@ fn parseDependentDylibs(self: *MachO) !void { if (has_errors) return error.MissingLibraryDependencies; } -pub fn addUndefinedGlobals(self: *MachO) !void { - const gpa = self.base.comp.gpa; - - try self.undefined_symbols.ensureUnusedCapacity(gpa, self.base.comp.force_undefined_symbols.keys().len); - for (self.base.comp.force_undefined_symbols.keys()) |name| { - const off = try self.strings.insert(gpa, name); - const gop = try self.getOrCreateGlobal(off); - self.undefined_symbols.appendAssumeCapacity(gop.index); - } - - if (!self.base.isDynLib() and self.entry_name != null) { - const off = try self.strings.insert(gpa, self.entry_name.?); - const gop = try self.getOrCreateGlobal(off); - self.entry_index = gop.index; - } - - { - const off = try self.strings.insert(gpa, "dyld_stub_binder"); - const gop = try self.getOrCreateGlobal(off); - self.dyld_stub_binder_index = gop.index; - } - - { - const off = try self.strings.insert(gpa, "_objc_msgSend"); - const gop = try self.getOrCreateGlobal(off); - self.objc_msg_send_index = gop.index; - } -} - /// When resolving symbols, we approach the problem similarly to `mold`. /// 1. Resolve symbols across all objects (including those preemptively extracted archives). /// 2. Resolve symbols across all shared objects. @@ -1348,18 +1300,17 @@ pub fn resolveSymbols(self: *MachO) !void { defer tracy.end(); // Resolve symbols in the ZigObject. For now, we assume that it's always live. - if (self.getZigObject()) |zo| zo.asFile().resolveSymbols(self); + if (self.getZigObject()) |zo| try zo.asFile().resolveSymbols(self); // Resolve symbols on the set of all objects and shared objects (even if some are unneeded). - for (self.objects.items) |index| self.getFile(index).?.resolveSymbols(self); - for (self.dylibs.items) |index| self.getFile(index).?.resolveSymbols(self); + for (self.objects.items) |index| try self.getFile(index).?.resolveSymbols(self); + for (self.dylibs.items) |index| try self.getFile(index).?.resolveSymbols(self); + if (self.getInternalObject()) |obj| try obj.resolveSymbols(self); // Mark live objects. self.markLive(); // Reset state of all globals after marking live objects. - if (self.getZigObject()) |zo| zo.asFile().resetGlobals(self); - for (self.objects.items) |index| self.getFile(index).?.resetGlobals(self); - for (self.dylibs.items) |index| self.getFile(index).?.resetGlobals(self); + self.resolver.reset(); // Prune dead objects. var i: usize = 0; @@ -1373,37 +1324,26 @@ pub fn resolveSymbols(self: *MachO) !void { } // Re-resolve the symbols. - if (self.getZigObject()) |zo| zo.resolveSymbols(self); - for (self.objects.items) |index| self.getFile(index).?.resolveSymbols(self); - for (self.dylibs.items) |index| self.getFile(index).?.resolveSymbols(self); + if (self.getZigObject()) |zo| try zo.resolveSymbols(self); + for (self.objects.items) |index| try self.getFile(index).?.resolveSymbols(self); + for (self.dylibs.items) |index| try self.getFile(index).?.resolveSymbols(self); + if (self.getInternalObject()) |obj| try obj.resolveSymbols(self); + + // Merge symbol visibility + if (self.getZigObject()) |zo| zo.mergeSymbolVisibility(self); + for (self.objects.items) |index| self.getFile(index).?.object.mergeSymbolVisibility(self); } fn markLive(self: *MachO) void { const tracy = trace(@src()); defer tracy.end(); - for (self.undefined_symbols.items) |index| { - if (self.getSymbol(index).getFile(self)) |file| { - if (file == .object) file.object.alive = true; - } - } - if (self.entry_index) |index| { - const sym = self.getSymbol(index); - if (sym.getFile(self)) |file| { - if (file == .object) file.object.alive = true; - } - } if (self.getZigObject()) |zo| zo.markLive(self); for (self.objects.items) |index| { const object = self.getFile(index).?.object; if (object.alive) object.markLive(self); } -} - -pub fn parseDebugInfo(self: *MachO) !void { - for (self.objects.items) |index| { - try self.getFile(index).?.object.parseDebugInfo(self); - } + if (self.getInternalObject()) |obj| obj.markLive(self); } fn resolveSyntheticSymbols(self: *MachO) !void { @@ -1453,10 +1393,14 @@ fn resolveSyntheticSymbols(self: *MachO) !void { } } -fn convertTentativeDefinitions(self: *MachO) !void { +fn convertTentativeDefsAndResolveSpecialSymbols(self: *MachO) !void { for (self.objects.items) |index| { try self.getFile(index).?.object.convertTentativeDefinitions(self); } + if (self.getInternalObject()) |obj| { + try obj.resolveBoundarySymbols(self); + try obj.resolveObjcMsgSendSymbols(self); + } } fn createObjcSections(self: *MachO) !void { @@ -1494,6 +1438,9 @@ fn createObjcSections(self: *MachO) !void { } pub fn dedupLiterals(self: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + const gpa = self.base.comp.gpa; var lp: LiteralPool = .{}; defer lp.deinit(gpa); diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 17dd198c6b..a668b932d9 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -259,6 +259,8 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { // } } + try self.parseDebugInfo(macho_file); + for (self.atoms.items) |atom_index| { const atom = self.getAtom(atom_index) orelse continue; const isec = atom.getInputSection(macho_file); @@ -1317,7 +1319,7 @@ fn parseUnwindRecords(self: *Object, allocator: Allocator, cpu_arch: std.Target. /// and record that so that we can emit symbol stabs. /// TODO in the future, we want parse debug info and debug line sections so that /// we can provide nice error locations to the user. -pub fn parseDebugInfo(self: *Object, macho_file: *MachO) !void { +fn parseDebugInfo(self: *Object, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); From 2e87883be85956b2a5be682423256e9ba700c13f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 9 Jul 2024 18:17:23 +0200 Subject: [PATCH 09/45] macho: migrate the MachO driver --- src/link/MachO.zig | 1008 +++++++++++++++++++------------------------- 1 file changed, 429 insertions(+), 579 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 408bd8cbe1..78ea58c509 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -540,13 +540,13 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n try self.sortSections(); try self.addAtomsToSections(); try self.calcSectionSizes(); - try self.generateUnwindInfo(); - try self.initSegments(); + try self.generateUnwindInfo(); + + try self.initSegments(); try self.allocateSections(); self.allocateSegments(); self.allocateSyntheticSymbols(); - try self.allocateLinkeditSegment(); if (build_options.enable_logging) { state_log.debug("{}", .{self.dumpState()}); @@ -554,6 +554,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n // Beyond this point, everything has been allocated a virtual address and we can resolve // the relocations, and commit objects to file. + try self.resizeSections(); + if (self.getZigObject()) |zo| { var has_resolve_error = false; @@ -597,33 +599,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n if (has_resolve_error) return error.FlushFailure; } + try self.writeSectionsAndUpdateLinkeditSizes(); - self.writeAtoms() catch |err| switch (err) { - error.ResolveFailed => return error.FlushFailure, - else => |e| { - try self.reportUnexpectedError("unexpected error while resolving relocations", .{}); - return e; - }, - }; - try self.writeUnwindInfo(); - try self.finalizeDyldInfoSections(); - try self.writeSyntheticSections(); - - var off = math.cast(u32, self.getLinkeditSegment().fileoff) orelse return error.Overflow; - off = try self.writeDyldInfoSections(off); - off = mem.alignForward(u32, off, @alignOf(u64)); - off = try self.writeFunctionStarts(off); - off = mem.alignForward(u32, off, @alignOf(u64)); - off = try self.writeDataInCode(self.getTextSegment().vmaddr, off); - try self.calcSymtabSize(); - off = mem.alignForward(u32, off, @alignOf(u64)); - off = try self.writeSymtab(off); - off = mem.alignForward(u32, off, @alignOf(u32)); - off = try self.writeIndsymtab(off); - off = mem.alignForward(u32, off, @alignOf(u64)); - off = try self.writeStrtab(off); - - self.getLinkeditSegment().filesize = off - self.getLinkeditSegment().fileoff; + try self.writeSectionsToFile(); + try self.allocateLinkeditSegment(); + try self.writeLinkeditSectionsToFile(); var codesig: ?CodeSignature = if (self.requiresCodeSig()) blk: { // Preallocate space for the code signature. @@ -1498,48 +1478,23 @@ fn checkDuplicates(self: *MachO) !void { } fn markImportsAndExports(self: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); + if (self.getZigObject()) |zo| { zo.asFile().markImportsExports(self); } for (self.objects.items) |index| { self.getFile(index).?.markImportsExports(self); } - - for (self.undefined_symbols.items) |index| { - const sym = self.getSymbol(index); - if (sym.getFile(self)) |file| { - if (sym.visibility != .global) continue; - if (file == .dylib and !sym.flags.abs) sym.flags.import = true; - } - } - - for (&[_]?Symbol.Index{ - self.entry_index, - self.dyld_stub_binder_index, - self.objc_msg_send_index, - }) |index| { - if (index) |idx| { - const sym = self.getSymbol(idx); - if (sym.getFile(self)) |file| { - if (file == .dylib) sym.flags.import = true; - } - } + if (self.getInternalObject()) |obj| { + obj.asFile().markImportsExports(self); } } fn deadStripDylibs(self: *MachO) void { - for (&[_]?Symbol.Index{ - self.entry_index, - self.dyld_stub_binder_index, - self.objc_msg_send_index, - }) |index| { - if (index) |idx| { - const sym = self.getSymbol(idx); - if (sym.getFile(self)) |file| { - if (file == .dylib) file.dylib.referenced = true; - } - } - } + const tracy = trace(@src()); + defer tracy.end(); for (self.dylibs.items) |index| { self.getFile(index).?.dylib.markReferenced(self); @@ -1560,50 +1515,26 @@ fn scanRelocs(self: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - if (self.getZigObject()) |zo| try zo.scanRelocs(self); - + if (self.getZigObject()) |zo| { + try zo.scanRelocs(self); + } for (self.objects.items) |index| { try self.getFile(index).?.object.scanRelocs(self); } + if (self.getInternalObject()) |obj| { + try obj.scanRelocs(self); + } try self.reportUndefs(); - if (self.entry_index) |index| { - const sym = self.getSymbol(index); - if (sym.getFile(self) != null) { - if (sym.flags.import) sym.flags.stubs = true; - } + if (self.getZigObject()) |zo| { + try zo.asFile().createSymbolIndirection(self); } - - if (self.dyld_stub_binder_index) |index| { - const sym = self.getSymbol(index); - if (sym.getFile(self) != null) sym.flags.needs_got = true; + for (self.objects.items) |index| { + try self.getFile(index).?.createSymbolIndirection(self); } - - if (self.objc_msg_send_index) |index| { - const sym = self.getSymbol(index); - if (sym.getFile(self) != null) - sym.flags.needs_got = true; // TODO is it always needed, or only if we are synthesising fast stubs? - } - - for (self.symbols.items, 0..) |*symbol, i| { - const index = @as(Symbol.Index, @intCast(i)); - if (symbol.flags.needs_got) { - log.debug("'{s}' needs GOT", .{symbol.getName(self)}); - try self.got.addSymbol(index, self); - } - if (symbol.flags.stubs) { - log.debug("'{s}' needs STUBS", .{symbol.getName(self)}); - try self.stubs.addSymbol(index, self); - } - if (symbol.flags.tlv_ptr) { - log.debug("'{s}' needs TLV pointer", .{symbol.getName(self)}); - try self.tlv_ptr.addSymbol(index, self); - } - if (symbol.flags.objc_stubs) { - log.debug("'{s}' needs OBJC STUBS", .{symbol.getName(self)}); - try self.objc_stubs.addSymbol(index, self); - } + if (self.getInternalObject()) |obj| { + try obj.asFile().createSymbolIndirection(self); } } @@ -1611,17 +1542,15 @@ fn reportUndefs(self: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - switch (self.undefined_treatment) { - .dynamic_lookup, .suppress => return, - .@"error", .warn => {}, - } + if (self.undefined_treatment == .suppress or + self.undefined_treatment == .dynamic_lookup) return; const max_notes = 4; var has_undefs = false; var it = self.undefs.iterator(); while (it.next()) |entry| { - const undef_sym = self.getSymbol(entry.key_ptr.*); + const undef_sym = entry.key_ptr.getSymbol(self).?; const notes = entry.value_ptr.*; const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes); @@ -1631,8 +1560,9 @@ fn reportUndefs(self: *MachO) !void { var inote: usize = 0; while (inote < @min(notes.items.len, max_notes)) : (inote += 1) { - const atom = self.getAtom(notes.items[inote]).?; - const file = atom.getFile(self); + const note = notes.items[inote]; + const file = self.getFile(note.file).?; + const atom = note.getAtom(self).?; try err.addNote(self, "referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) }); } @@ -1641,64 +1571,18 @@ fn reportUndefs(self: *MachO) !void { try err.addNote(self, "referenced {d} more times", .{remaining}); } } - - for (self.undefined_symbols.items) |index| { - const sym = self.getSymbol(index); - if (sym.getFile(self) != null) continue; // If undefined in an object file, will be reported above - has_undefs = true; - var err = try self.addErrorWithNotes(1); - try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)}); - try err.addNote(self, "-u command line option", .{}); - } - - if (self.entry_index) |index| { - const sym = self.getSymbol(index); - if (sym.getFile(self) == null) { - has_undefs = true; - var err = try self.addErrorWithNotes(1); - try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)}); - try err.addNote(self, "implicit entry/start for main executable", .{}); - } - } - - if (self.dyld_stub_binder_index) |index| { - const sym = self.getSymbol(index); - if (sym.getFile(self) == null and self.stubs_sect_index != null) { - has_undefs = true; - var err = try self.addErrorWithNotes(1); - try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)}); - try err.addNote(self, "implicit -u command line option", .{}); - } - } - - if (self.objc_msg_send_index) |index| { - const sym = self.getSymbol(index); - if (sym.getFile(self) == null and self.objc_stubs_sect_index != null) { - has_undefs = true; - var err = try self.addErrorWithNotes(1); - try err.addMsg(self, "undefined symbol: {s}", .{sym.getName(self)}); - try err.addNote(self, "implicit -u command line option", .{}); - } - } - if (has_undefs) return error.HasUndefinedSymbols; } fn initOutputSections(self: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + for (self.objects.items) |index| { - const object = self.getFile(index).?.object; - for (object.atoms.items) |atom_index| { - const atom = self.getAtom(atom_index) orelse continue; - if (!atom.flags.alive) continue; - atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(self), self); - } + try self.getFile(index).?.initOutputSections(self); } - if (self.getInternalObject()) |object| { - for (object.atoms.items) |atom_index| { - const atom = self.getAtom(atom_index) orelse continue; - if (!atom.flags.alive) continue; - atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(self), self); - } + if (self.getInternalObject()) |obj| { + try obj.asFile().initOutputSections(self); } self.text_sect_index = self.getSectionByName("__TEXT", "__text") orelse try self.addSection("__TEXT", "__text", .{ @@ -1771,46 +1655,50 @@ fn initSyntheticSections(self: *MachO) !void { self.eh_frame_sect_index = try self.addSection("__TEXT", "__eh_frame", .{}); } - for (self.boundary_symbols.items) |sym_index| { + if (self.getInternalObject()) |obj| { const gpa = self.base.comp.gpa; - const sym = self.getSymbol(sym_index); - const name = sym.getName(self); - if (eatPrefix(name, "segment$start$")) |segname| { - if (self.getSegmentByName(segname) == null) { // TODO check segname is valid - const prot = getSegmentProt(segname); - _ = try self.segments.append(gpa, .{ - .cmdsize = @sizeOf(macho.segment_command_64), - .segname = makeStaticString(segname), - .initprot = prot, - .maxprot = prot, - }); - } - } else if (eatPrefix(name, "segment$stop$")) |segname| { - if (self.getSegmentByName(segname) == null) { // TODO check segname is valid - const prot = getSegmentProt(segname); - _ = try self.segments.append(gpa, .{ - .cmdsize = @sizeOf(macho.segment_command_64), - .segname = makeStaticString(segname), - .initprot = prot, - .maxprot = prot, - }); - } - } else if (eatPrefix(name, "section$start$")) |actual_name| { - const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic - const segname = actual_name[0..sep]; // TODO check segname is valid - const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid - if (self.getSectionByName(segname, sectname) == null) { - _ = try self.addSection(segname, sectname, .{}); - } - } else if (eatPrefix(name, "section$stop$")) |actual_name| { - const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic - const segname = actual_name[0..sep]; // TODO check segname is valid - const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid - if (self.getSectionByName(segname, sectname) == null) { - _ = try self.addSection(segname, sectname, .{}); - } - } else unreachable; + for (obj.boundary_symbols.items) |sym_index| { + const ref = obj.getSymbolRef(sym_index, self); + const sym = ref.getSymbol(self).?; + const name = sym.getName(self); + + if (eatPrefix(name, "segment$start$")) |segname| { + if (self.getSegmentByName(segname) == null) { // TODO check segname is valid + const prot = getSegmentProt(segname); + _ = try self.segments.append(gpa, .{ + .cmdsize = @sizeOf(macho.segment_command_64), + .segname = makeStaticString(segname), + .initprot = prot, + .maxprot = prot, + }); + } + } else if (eatPrefix(name, "segment$stop$")) |segname| { + if (self.getSegmentByName(segname) == null) { // TODO check segname is valid + const prot = getSegmentProt(segname); + _ = try self.segments.append(gpa, .{ + .cmdsize = @sizeOf(macho.segment_command_64), + .segname = makeStaticString(segname), + .initprot = prot, + .maxprot = prot, + }); + } + } else if (eatPrefix(name, "section$start$")) |actual_name| { + const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic + const segname = actual_name[0..sep]; // TODO check segname is valid + const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid + if (self.getSectionByName(segname, sectname) == null) { + _ = try self.addSection(segname, sectname, .{}); + } + } else if (eatPrefix(name, "section$stop$")) |actual_name| { + const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic + const segname = actual_name[0..sep]; // TODO check segname is valid + const sectname = actual_name[sep + 1 ..]; // TODO check sectname is valid + if (self.getSectionByName(segname, sectname) == null) { + _ = try self.addSection(segname, sectname, .{}); + } + } else unreachable; + } } } @@ -1917,38 +1805,25 @@ pub fn sortSections(self: *MachO) !void { } if (self.getZigObject()) |zo| { - for (zo.atoms.items) |atom_index| { - const atom = self.getAtom(atom_index) orelse continue; + for (zo.getAtoms()) |atom_index| { + const atom = zo.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; atom.out_n_sect = backlinks[atom.out_n_sect]; } - - for (zo.symtab.items(.nlist)) |*sym| { - if (sym.sect()) { - sym.n_sect = backlinks[sym.n_sect - 1] + 1; - } - } - - for (zo.symbols.items) |sym_index| { - const sym = self.getSymbol(sym_index); - const atom = sym.getAtom(self) orelse continue; - if (!atom.flags.alive) continue; - if (sym.getFile(self).?.getIndex() != zo.index) continue; - sym.out_n_sect = backlinks[sym.out_n_sect]; - } } for (self.objects.items) |index| { - for (self.getFile(index).?.object.atoms.items) |atom_index| { - const atom = self.getAtom(atom_index) orelse continue; + const file = self.getFile(index).?; + for (file.getAtoms()) |atom_index| { + const atom = file.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; atom.out_n_sect = backlinks[atom.out_n_sect]; } } if (self.getInternalObject()) |object| { - for (object.atoms.items) |atom_index| { - const atom = self.getAtom(atom_index) orelse continue; + for (object.getAtoms()) |atom_index| { + const atom = object.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; atom.out_n_sect = backlinks[atom.out_n_sect]; } @@ -1985,35 +1860,23 @@ pub fn addAtomsToSections(self: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); + const gpa = self.base.comp.gpa; + for (self.objects.items) |index| { - const object = self.getFile(index).?.object; - for (object.atoms.items) |atom_index| { - const atom = self.getAtom(atom_index) orelse continue; + const file = self.getFile(index).?; + for (file.getAtoms()) |atom_index| { + const atom = file.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; const atoms = &self.sections.items(.atoms)[atom.out_n_sect]; - try atoms.append(self.base.comp.gpa, atom_index); - } - for (object.symbols.items) |sym_index| { - const sym = self.getSymbol(sym_index); - const atom = sym.getAtom(self) orelse continue; - if (!atom.flags.alive) continue; - if (sym.getFile(self).?.getIndex() != index) continue; - sym.out_n_sect = atom.out_n_sect; + try atoms.append(gpa, .{ .index = atom_index, .file = index }); } } if (self.getInternalObject()) |object| { - for (object.atoms.items) |atom_index| { - const atom = self.getAtom(atom_index) orelse continue; + for (object.getAtoms()) |atom_index| { + const atom = object.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; const atoms = &self.sections.items(.atoms)[atom.out_n_sect]; - try atoms.append(self.base.comp.gpa, atom_index); - } - for (object.symbols.items) |sym_index| { - const sym = self.getSymbol(sym_index); - const atom = sym.getAtom(self) orelse continue; - if (!atom.flags.alive) continue; - if (sym.getFile(self).?.getIndex() != object.index) continue; - sym.out_n_sect = atom.out_n_sect; + try atoms.append(gpa, .{ .index = atom_index, .file = object.index }); } } } @@ -2035,8 +1898,8 @@ fn calcSectionSizes(self: *MachO) !void { if (atoms.items.len == 0) continue; if (self.requiresThunks() and header.isCode()) continue; - for (atoms.items) |atom_index| { - const atom = self.getAtom(atom_index).?; + for (atoms.items) |ref| { + const atom = ref.getAtom(self).?; const atom_alignment = atom.alignment.toByteUnits() orelse 1; const offset = mem.alignForward(u64, header.size, atom_alignment); const padding = offset - header.size; @@ -2056,6 +1919,22 @@ fn calcSectionSizes(self: *MachO) !void { } } + // At this point, we can also calculate symtab and data-in-code linkedit section sizes + if (self.getZigObject()) |zo| { + zo.asFile().calcSymtabSize(self); + } + for (self.objects.items) |index| { + self.getFile(index).?.calcSymtabSize(self); + } + for (self.dylibs.items) |index| { + self.getFile(index).?.calcSymtabSize(self); + } + if (self.getInternalObject()) |obj| { + obj.asFile().calcSymtabSize(self); + } + + try self.calcSymtabSize(); + if (self.got_sect_index) |idx| { const header = &self.sections.items(.header)[idx]; header.size = self.got.size(); @@ -2336,77 +2215,61 @@ fn allocateSegments(self: *MachO) void { } fn allocateSyntheticSymbols(self: *MachO) void { - const text_seg = self.getTextSegment(); + if (self.getInternalObject()) |obj| { + obj.allocateSyntheticSymbols(self); - if (self.mh_execute_header_index) |index| { - const global = self.getSymbol(index); - global.value = text_seg.vmaddr; - } + const text_seg = self.getTextSegment(); - if (self.data_sect_index) |idx| { - const sect = self.sections.items(.header)[idx]; - for (&[_]?Symbol.Index{ - self.dso_handle_index, - self.mh_dylib_header_index, - self.dyld_private_index, - }) |maybe_index| { - if (maybe_index) |index| { - const global = self.getSymbol(index); - global.value = sect.addr; - global.out_n_sect = idx; - } + for (obj.boundary_symbols.items) |sym_index| { + const ref = obj.getSymbolRef(sym_index, self); + const sym = ref.getSymbol(self).?; + const name = sym.getName(self); + + sym.value = text_seg.vmaddr; + + if (mem.startsWith(u8, name, "segment$start$")) { + const segname = name["segment$start$".len..]; + if (self.getSegmentByName(segname)) |seg_id| { + const seg = self.segments.items[seg_id]; + sym.value = seg.vmaddr; + } + } else if (mem.startsWith(u8, name, "segment$stop$")) { + const segname = name["segment$stop$".len..]; + if (self.getSegmentByName(segname)) |seg_id| { + const seg = self.segments.items[seg_id]; + sym.value = seg.vmaddr + seg.vmsize; + } + } else if (mem.startsWith(u8, name, "section$start$")) { + const actual_name = name["section$start$".len..]; + const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic + const segname = actual_name[0..sep]; + const sectname = actual_name[sep + 1 ..]; + if (self.getSectionByName(segname, sectname)) |sect_id| { + const sect = self.sections.items(.header)[sect_id]; + sym.value = sect.addr; + sym.out_n_sect = sect_id; + } + } else if (mem.startsWith(u8, name, "section$stop$")) { + const actual_name = name["section$stop$".len..]; + const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic + const segname = actual_name[0..sep]; + const sectname = actual_name[sep + 1 ..]; + if (self.getSectionByName(segname, sectname)) |sect_id| { + const sect = self.sections.items(.header)[sect_id]; + sym.value = sect.addr + sect.size; + sym.out_n_sect = sect_id; + } + } else unreachable; } - } - for (self.boundary_symbols.items) |sym_index| { - const sym = self.getSymbol(sym_index); - const name = sym.getName(self); + if (self.objc_stubs.symbols.items.len > 0) { + const addr = self.sections.items(.header)[self.objc_stubs_sect_index.?].addr; - sym.flags.@"export" = false; - sym.value = text_seg.vmaddr; - - if (mem.startsWith(u8, name, "segment$start$")) { - const segname = name["segment$start$".len..]; - if (self.getSegmentByName(segname)) |seg_id| { - const seg = self.segments.items[seg_id]; - sym.value = seg.vmaddr; + for (self.objc_stubs.symbols.items, 0..) |ref, idx| { + const sym = ref.getSymbol(self).?; + sym.value = addr + idx * ObjcStubsSection.entrySize(self.getTarget().cpu.arch); + sym.out_n_sect = self.objc_stubs_sect_index.?; } - } else if (mem.startsWith(u8, name, "segment$stop$")) { - const segname = name["segment$stop$".len..]; - if (self.getSegmentByName(segname)) |seg_id| { - const seg = self.segments.items[seg_id]; - sym.value = seg.vmaddr + seg.vmsize; - } - } else if (mem.startsWith(u8, name, "section$start$")) { - const actual_name = name["section$start$".len..]; - const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic - const segname = actual_name[0..sep]; - const sectname = actual_name[sep + 1 ..]; - if (self.getSectionByName(segname, sectname)) |sect_id| { - const sect = self.sections.items(.header)[sect_id]; - sym.value = sect.addr; - sym.out_n_sect = sect_id; - } - } else if (mem.startsWith(u8, name, "section$stop$")) { - const actual_name = name["section$stop$".len..]; - const sep = mem.indexOfScalar(u8, actual_name, '$').?; // TODO error rather than a panic - const segname = actual_name[0..sep]; - const sectname = actual_name[sep + 1 ..]; - if (self.getSectionByName(segname, sectname)) |sect_id| { - const sect = self.sections.items(.header)[sect_id]; - sym.value = sect.addr + sect.size; - sym.out_n_sect = sect_id; - } - } else unreachable; - } - - if (self.objc_stubs.symbols.items.len > 0) { - const addr = self.sections.items(.header)[self.objc_stubs_sect_index.?].addr; - - for (self.objc_stubs.symbols.items, 0..) |sym_index, idx| { - const sym = self.getSymbol(sym_index); - sym.value = addr + idx * ObjcStubsSection.entrySize(self.getTarget().cpu.arch); - sym.out_n_sect = self.objc_stubs_sect_index.?; } } } @@ -2424,56 +2287,270 @@ fn allocateLinkeditSegment(self: *MachO) !void { const seg = self.getLinkeditSegment(); seg.vmaddr = mem.alignForward(u64, vmaddr, page_size); seg.fileoff = mem.alignForward(u64, fileoff, page_size); + + var off = math.cast(u32, seg.fileoff) orelse return error.Overflow; + // DYLD_INFO_ONLY + { + const cmd = &self.dyld_info_cmd; + cmd.rebase_off = off; + off += cmd.rebase_size; + cmd.bind_off = off; + off += cmd.bind_size; + cmd.weak_bind_off = off; + off += cmd.weak_bind_size; + cmd.lazy_bind_off = off; + off += cmd.lazy_bind_size; + cmd.export_off = off; + off += cmd.export_size; + off = mem.alignForward(u32, off, @alignOf(u64)); + } + + // FUNCTION_STARTS + { + const cmd = &self.function_starts_cmd; + cmd.dataoff = off; + off += cmd.datasize; + off = mem.alignForward(u32, off, @alignOf(u64)); + } + + // DATA_IN_CODE + { + const cmd = &self.data_in_code_cmd; + cmd.dataoff = off; + off += cmd.datasize; + off = mem.alignForward(u32, off, @alignOf(u64)); + } + + // SYMTAB (symtab) + { + const cmd = &self.symtab_cmd; + cmd.symoff = off; + off += cmd.nsyms * @sizeOf(macho.nlist_64); + off = mem.alignForward(u32, off, @alignOf(u32)); + } + + // DYSYMTAB + { + const cmd = &self.dysymtab_cmd; + cmd.indirectsymoff = off; + off += cmd.nindirectsyms * @sizeOf(u32); + off = mem.alignForward(u32, off, @alignOf(u64)); + } + + // SYMTAB (strtab) + { + const cmd = &self.symtab_cmd; + cmd.stroff = off; + off += cmd.strsize; + } + + seg.filesize = off - seg.fileoff; } -fn writeAtoms(self: *MachO) !void { +fn resizeSections(self: *MachO) !void { + const slice = self.sections.slice(); + for (slice.items(.header), slice.items(.atoms), slice.items(.out)) |header, atoms, *out| { + if (atoms.items.len == 0) continue; + if (header.isZerofill()) continue; + const cpu_arch = self.getTarget().cpu.arch; + try out.resize(self.base.comp.gpa, header.size); + const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0; + @memset(out.items, padding_byte); + } +} + +fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void { + const gpa = self.base.comp.gpa; + + const cmd = self.symtab_cmd; + try self.symtab.resize(gpa, cmd.nsyms); + try self.strtab.resize(gpa, cmd.strsize); + self.strtab.items[0] = 0; + + for (self.objects.items) |index| { + try self.getFile(index).?.writeAtoms(self); + } + if (self.getInternalObject()) |obj| { + try obj.asFile().writeAtoms(self); + } + for (self.thunks.items) |thunk| { + const out = self.sections.items(.out)[thunk.out_n_sect].items; + const off = thunk.value; + const size = thunk.size(); + var stream = std.io.fixedBufferStream(out[off..][0..size]); + try thunk.write(self, stream.writer()); + } + + const slice = self.sections.slice(); + for (&[_]?u8{ + self.eh_frame_sect_index, + self.unwind_info_sect_index, + self.got_sect_index, + self.stubs_sect_index, + self.la_symbol_ptr_sect_index, + self.tlv_ptr_sect_index, + self.objc_stubs_sect_index, + }) |maybe_sect_id| { + if (maybe_sect_id) |sect_id| { + const out = &slice.items(.out)[sect_id]; + try self.writeSyntheticSection(sect_id, out); + } + } + + if (self.la_symbol_ptr_sect_index) |_| { + try self.updatelazyBindSize(); + } + + try self.rebase.updateSize(self); + try self.bind.updateSize(self); + try self.weak_bind.updateSize(self); + try self.export_trie.updateSize(self); + try self.data_in_code.updateSize(self); + + if (self.getZigObject()) |zo| { + zo.asFile().writeSymtab(self); + } + for (self.objects.items) |index| { + self.getFile(index).?.writeSymtab(self); + } + for (self.dylibs.items) |index| { + self.getFile(index).?.writeSymtab(self); + } + if (self.getInternalObject()) |obj| { + obj.asFile().writeSymtab(self); + } +} + +fn writeSyntheticSection(self: *MachO, sect_id: u8, out: []u8) !void { const tracy = trace(@src()); defer tracy.end(); - const gpa = self.base.comp.gpa; - var arena = std.heap.ArenaAllocator.init(gpa); - defer arena.deinit(); + const Tag = enum { + eh_frame, + unwind_info, + got, + stubs, + la_symbol_ptr, + tlv_ptr, + objc_stubs, + }; + + const tag: Tag = tag: { + if (self.eh_frame_sect_index != null and + self.eh_frame_sect_index.? == sect_id) break :tag .eh_frame; + if (self.unwind_info_sect_index != null and + self.unwind_info_sect_index.? == sect_id) break :tag .unwind_info; + if (self.got_sect_index != null and + self.got_sect_index.? == sect_id) break :tag .got; + if (self.stubs_sect_index != null and + self.stubs_sect_index.? == sect_id) break :tag .stubs; + if (self.la_symbol_ptr_sect_index != null and + self.la_symbol_ptr_sect_index.? == sect_id) break :tag .la_symbol_ptr; + if (self.tlv_ptr_sect_index != null and + self.tlv_ptr_sect_index.? == sect_id) break :tag .tlv_ptr; + if (self.objc_stubs_sect_index != null and + self.objc_stubs_sect_index.? == sect_id) break :tag .objc_stubs; + unreachable; + }; + var stream = std.io.fixedBufferStream(out); + switch (tag) { + .eh_frame => eh_frame.write(self, out), + .unwind_info => try self.unwind_info.write(self, out), + .got => try self.got.write(self, stream.writer()), + .stubs => try self.stubs.write(self, stream.writer()), + .la_symbol_ptr => try self.la_symbol_ptr.write(self, stream.writer()), + .tlv_ptr => try self.tlv_ptr.write(self, stream.writer()), + .objc_stubs => try self.objc_stubs.write(self, stream.writer()), + } +} + +fn updateLazyBindSize(self: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + try self.lazy_bind.updateSize(self); + const sect_id = self.stubs_helper_sect_index.?; + const out = &self.sections.items(.out)[sect_id]; + var stream = std.io.fixedBufferStream(out.items); + try self.stubs_helper.write(self, stream.writer()); +} + +fn writeSectionsToFile(self: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); - const cpu_arch = self.getTarget().cpu.arch; const slice = self.sections.slice(); - - var has_resolve_error = false; - for (slice.items(.header), slice.items(.atoms)) |header, atoms| { - if (atoms.items.len == 0) continue; - if (header.isZerofill()) continue; - - const size = math.cast(usize, header.size) orelse return error.Overflow; - const buffer = try gpa.alloc(u8, size); - defer gpa.free(buffer); - const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0; - @memset(buffer, padding_byte); - - for (atoms.items) |atom_index| { - const atom = self.getAtom(atom_index).?; - assert(atom.flags.alive); - const off = math.cast(usize, atom.value) orelse return error.Overflow; - const atom_size = math.cast(usize, atom.size) orelse return error.Overflow; - try atom.getData(self, buffer[off..][0..atom_size]); - atom.resolveRelocs(self, buffer[off..][0..atom_size]) catch |err| switch (err) { - error.ResolveFailed => has_resolve_error = true, - else => |e| return e, - }; - } - - try self.base.file.?.pwriteAll(buffer, header.offset); + for (slice.items(.header), slice.items(.out)) |header, out| { + try self.base.file.pwriteAll(out.items, header.offset); } +} - for (self.thunks.items) |thunk| { - const header = slice.items(.header)[thunk.out_n_sect]; - const offset = thunk.value + header.offset; - const buffer = try gpa.alloc(u8, thunk.size()); - defer gpa.free(buffer); - var stream = std.io.fixedBufferStream(buffer); - try thunk.write(self, stream.writer()); - try self.base.file.?.pwriteAll(buffer, offset); - } +fn writeLinkeditSectionsToFile(self: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + try self.writeDyldInfo(); + try self.writeDataInCode(); + try self.writeSymtabToFile(); + try self.writeIndsymtab(); +} - if (has_resolve_error) return error.ResolveFailed; +fn writeDyldInfo(self: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const gpa = self.base.allocator; + const base_off = self.getLinkeditSegment().fileoff; + const cmd = self.dyld_info_cmd; + var needed_size: u32 = 0; + needed_size += cmd.rebase_size; + needed_size += cmd.bind_size; + needed_size += cmd.weak_bind_size; + needed_size += cmd.lazy_bind_size; + needed_size += cmd.export_size; + + const buffer = try gpa.alloc(u8, needed_size); + defer gpa.free(buffer); + @memset(buffer, 0); + + var stream = std.io.fixedBufferStream(buffer); + const writer = stream.writer(); + + try self.rebase.write(writer); + try stream.seekTo(cmd.bind_off - base_off); + try self.bind.write(writer); + try stream.seekTo(cmd.weak_bind_off - base_off); + try self.weak_bind.write(writer); + try stream.seekTo(cmd.lazy_bind_off - base_off); + try self.lazy_bind.write(writer); + try stream.seekTo(cmd.export_off - base_off); + try self.export_trie.write(writer); + try self.base.file.pwriteAll(buffer, cmd.rebase_off); +} + +pub fn writeDataInCode(self: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + const cmd = self.data_in_code_cmd; + try self.base.file.pwriteAll(mem.sliceAsBytes(self.data_in_code.entries.items), cmd.dataoff); +} + +fn writeIndsymtab(self: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + const gpa = self.base.comp.gpa; + const cmd = self.dysymtab_cmd; + const needed_size = cmd.nindirectsyms * @sizeOf(u32); + var buffer = try std.ArrayList(u8).initCapacity(gpa, needed_size); + defer buffer.deinit(); + try self.indsymtab.write(self, buffer.writer()); + try self.base.file.pwriteAll(buffer.items, cmd.indirectsymoff); +} + +pub fn writeSymtabToFile(self: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + const cmd = self.symtab_cmd; + try self.base.file.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff); + try self.base.file.pwriteAll(self.strtab.items, cmd.stroff); } fn writeUnwindInfo(self: *MachO) !void { @@ -2501,193 +2578,12 @@ fn writeUnwindInfo(self: *MachO) !void { } } -fn finalizeDyldInfoSections(self: *MachO) !void { - const tracy = trace(@src()); - defer tracy.end(); - try self.rebase.updateSize(self); - try self.bind.updateSize(self); - try self.weak_bind.updateSize(self); - if (self.la_symbol_ptr_sect_index) |_| { - try self.lazy_bind.updateSize(self); - } - try self.export_trie.updateSize(self); -} - -fn writeSyntheticSections(self: *MachO) !void { +fn calcSymtabSize(self: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); const gpa = self.base.comp.gpa; - if (self.got_sect_index) |sect_id| { - const header = self.sections.items(.header)[sect_id]; - const size = math.cast(usize, header.size) orelse return error.Overflow; - var buffer = try std.ArrayList(u8).initCapacity(gpa, size); - defer buffer.deinit(); - try self.got.write(self, buffer.writer()); - assert(buffer.items.len == header.size); - try self.base.file.?.pwriteAll(buffer.items, header.offset); - } - - if (self.stubs_sect_index) |sect_id| { - const header = self.sections.items(.header)[sect_id]; - const size = math.cast(usize, header.size) orelse return error.Overflow; - var buffer = try std.ArrayList(u8).initCapacity(gpa, size); - defer buffer.deinit(); - try self.stubs.write(self, buffer.writer()); - assert(buffer.items.len == header.size); - try self.base.file.?.pwriteAll(buffer.items, header.offset); - } - - if (self.stubs_helper_sect_index) |sect_id| { - const header = self.sections.items(.header)[sect_id]; - const size = math.cast(usize, header.size) orelse return error.Overflow; - var buffer = try std.ArrayList(u8).initCapacity(gpa, size); - defer buffer.deinit(); - try self.stubs_helper.write(self, buffer.writer()); - assert(buffer.items.len == header.size); - try self.base.file.?.pwriteAll(buffer.items, header.offset); - } - - if (self.la_symbol_ptr_sect_index) |sect_id| { - const header = self.sections.items(.header)[sect_id]; - const size = math.cast(usize, header.size) orelse return error.Overflow; - var buffer = try std.ArrayList(u8).initCapacity(gpa, size); - defer buffer.deinit(); - try self.la_symbol_ptr.write(self, buffer.writer()); - assert(buffer.items.len == header.size); - try self.base.file.?.pwriteAll(buffer.items, header.offset); - } - - if (self.tlv_ptr_sect_index) |sect_id| { - const header = self.sections.items(.header)[sect_id]; - const size = math.cast(usize, header.size) orelse return error.Overflow; - var buffer = try std.ArrayList(u8).initCapacity(gpa, size); - defer buffer.deinit(); - try self.tlv_ptr.write(self, buffer.writer()); - assert(buffer.items.len == header.size); - try self.base.file.?.pwriteAll(buffer.items, header.offset); - } - - if (self.objc_stubs_sect_index) |sect_id| { - const header = self.sections.items(.header)[sect_id]; - const size = math.cast(usize, header.size) orelse return error.Overflow; - var buffer = try std.ArrayList(u8).initCapacity(gpa, size); - defer buffer.deinit(); - try self.objc_stubs.write(self, buffer.writer()); - assert(buffer.items.len == header.size); - try self.base.file.?.pwriteAll(buffer.items, header.offset); - } -} - -fn writeDyldInfoSections(self: *MachO, off: u32) !u32 { - const tracy = trace(@src()); - defer tracy.end(); - - const gpa = self.base.comp.gpa; - const cmd = &self.dyld_info_cmd; - var needed_size: u32 = 0; - needed_size += cmd.rebase_size; - cmd.bind_off = needed_size; - needed_size += cmd.bind_size; - cmd.weak_bind_off = needed_size; - needed_size += cmd.weak_bind_size; - cmd.lazy_bind_off = needed_size; - needed_size += cmd.lazy_bind_size; - cmd.export_off = needed_size; - needed_size += cmd.export_size; - - const buffer = try gpa.alloc(u8, needed_size); - defer gpa.free(buffer); - @memset(buffer, 0); - - var stream = std.io.fixedBufferStream(buffer); - const writer = stream.writer(); - - try self.rebase.write(writer); - try stream.seekTo(cmd.bind_off); - try self.bind.write(writer); - try stream.seekTo(cmd.weak_bind_off); - try self.weak_bind.write(writer); - try stream.seekTo(cmd.lazy_bind_off); - try self.lazy_bind.write(writer); - try stream.seekTo(cmd.export_off); - try self.export_trie.write(writer); - - cmd.rebase_off += off; - cmd.bind_off += off; - cmd.weak_bind_off += off; - cmd.lazy_bind_off += off; - cmd.export_off += off; - try self.base.file.?.pwriteAll(buffer, off); - - return off + needed_size; -} - -fn writeFunctionStarts(self: *MachO, off: u32) !u32 { - // TODO actually write it out - const cmd = &self.function_starts_cmd; - cmd.dataoff = off; - return off; -} - -pub fn writeDataInCode(self: *MachO, base_address: u64, off: u32) !u32 { - const cmd = &self.data_in_code_cmd; - cmd.dataoff = off; - - const gpa = self.base.comp.gpa; - var dices = std.ArrayList(macho.data_in_code_entry).init(gpa); - defer dices.deinit(); - - for (self.objects.items) |index| { - const object = self.getFile(index).?.object; - const in_dices = object.getDataInCode(); - - try dices.ensureUnusedCapacity(in_dices.len); - - var next_dice: usize = 0; - for (object.atoms.items) |atom_index| { - if (next_dice >= in_dices.len) break; - const atom = self.getAtom(atom_index) orelse continue; - const start_off = atom.getInputAddress(self); - const end_off = start_off + atom.size; - const start_dice = next_dice; - - if (end_off < in_dices[next_dice].offset) continue; - - while (next_dice < in_dices.len and - in_dices[next_dice].offset < end_off) : (next_dice += 1) - {} - - if (atom.flags.alive) for (in_dices[start_dice..next_dice]) |dice| { - dices.appendAssumeCapacity(.{ - .offset = @intCast(atom.getAddress(self) + dice.offset - start_off - base_address), - .length = dice.length, - .kind = dice.kind, - }); - }; - } - } - - const needed_size = math.cast(u32, dices.items.len * @sizeOf(macho.data_in_code_entry)) orelse return error.Overflow; - cmd.datasize = needed_size; - - try self.base.file.?.pwriteAll(mem.sliceAsBytes(dices.items), cmd.dataoff); - - return off + needed_size; -} - -pub fn calcSymtabSize(self: *MachO) !void { - const tracy = trace(@src()); - defer tracy.end(); - const gpa = self.base.comp.gpa; - - var nlocals: u32 = 0; - var nstabs: u32 = 0; - var nexports: u32 = 0; - var nimports: u32 = 0; - var strsize: u32 = 0; - var files = std.ArrayList(File.Index).init(gpa); defer files.deinit(); try files.ensureTotalCapacityPrecise(self.objects.items.len + self.dylibs.items.len + 2); @@ -2696,6 +2592,12 @@ pub fn calcSymtabSize(self: *MachO) !void { for (self.dylibs.items) |index| files.appendAssumeCapacity(index); if (self.internal_object) |index| files.appendAssumeCapacity(index); + var nlocals: u32 = 0; + var nstabs: u32 = 0; + var nexports: u32 = 0; + var nimports: u32 = 0; + var strsize: u32 = 0; + for (files.items) |index| { const file = self.getFile(index).?; const ctx = switch (file) { @@ -2705,7 +2607,7 @@ pub fn calcSymtabSize(self: *MachO) !void { ctx.istab = nstabs; ctx.iexport = nexports; ctx.iimport = nimports; - try file.calcSymtabSize(self); + ctx.stroff = strsize; nlocals += ctx.nlocals; nstabs += ctx.nstabs; nexports += ctx.nexports; @@ -2723,6 +2625,8 @@ pub fn calcSymtabSize(self: *MachO) !void { ctx.iimport += nlocals + nstabs + nexports; } + try self.indsymtab.updateSize(self); + { const cmd = &self.symtab_cmd; cmd.nsyms = nlocals + nstabs + nexports + nimports; @@ -2740,60 +2644,6 @@ pub fn calcSymtabSize(self: *MachO) !void { } } -pub fn writeSymtab(self: *MachO, off: u32) !u32 { - const tracy = trace(@src()); - defer tracy.end(); - const gpa = self.base.comp.gpa; - const cmd = &self.symtab_cmd; - cmd.symoff = off; - - try self.symtab.resize(gpa, cmd.nsyms); - try self.strtab.ensureUnusedCapacity(gpa, cmd.strsize - 1); - - if (self.getZigObject()) |zo| { - zo.writeSymtab(self, self); - } - for (self.objects.items) |index| { - try self.getFile(index).?.writeSymtab(self, self); - } - for (self.dylibs.items) |index| { - try self.getFile(index).?.writeSymtab(self, self); - } - if (self.getInternalObject()) |internal| { - internal.writeSymtab(self, self); - } - - assert(self.strtab.items.len == cmd.strsize); - - try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff); - - return off + cmd.nsyms * @sizeOf(macho.nlist_64); -} - -fn writeIndsymtab(self: *MachO, off: u32) !u32 { - const gpa = self.base.comp.gpa; - const cmd = &self.dysymtab_cmd; - cmd.indirectsymoff = off; - cmd.nindirectsyms = self.indsymtab.nsyms(self); - - const needed_size = cmd.nindirectsyms * @sizeOf(u32); - var buffer = try std.ArrayList(u8).initCapacity(gpa, needed_size); - defer buffer.deinit(); - try self.indsymtab.write(self, buffer.writer()); - - try self.base.file.?.pwriteAll(buffer.items, cmd.indirectsymoff); - assert(buffer.items.len == needed_size); - - return off + needed_size; -} - -pub fn writeStrtab(self: *MachO, off: u32) !u32 { - const cmd = &self.symtab_cmd; - cmd.stroff = off; - try self.base.file.?.pwriteAll(self.strtab.items, cmd.stroff); - return off + cmd.strsize; -} - fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } { const comp = self.base.comp; const gpa = comp.gpa; From f86a38564f5d8eb903b940ddf9b9f5685a3ca932 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 9 Jul 2024 19:30:58 +0200 Subject: [PATCH 10/45] macho: migrate synthetic sections --- src/link/MachO.zig | 3 + src/link/MachO/synthetic.zig | 200 +++++++++++++++++++++++------------ 2 files changed, 136 insertions(+), 67 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 78ea58c509..5d95838f30 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -68,6 +68,7 @@ weak_bind: WeakBind = .{}, lazy_bind: LazyBind = .{}, export_trie: ExportTrie = .{}, unwind_info: UnwindInfo = .{}, +data_in_code: DataInCode = .{}, /// Tracked loadable segments during incremental linking. zig_text_seg_index: ?u8 = null, @@ -316,6 +317,7 @@ pub fn deinit(self: *MachO) void { self.lazy_bind.deinit(gpa); self.export_trie.deinit(gpa); self.unwind_info.deinit(gpa); + self.data_in_code.deinit(gpa); self.thunks.deinit(gpa); } @@ -4524,6 +4526,7 @@ const Bind = bind.Bind; const Cache = std.Build.Cache; const CodeSignature = @import("MachO/CodeSignature.zig"); const Compilation = @import("../Compilation.zig"); +const DataInCode = synthetic.DataInCode; pub const DebugSymbols = @import("MachO/DebugSymbols.zig"); const Dylib = @import("MachO/Dylib.zig"); const ExportTrie = @import("MachO/dyld_info/Trie.zig"); diff --git a/src/link/MachO/synthetic.zig b/src/link/MachO/synthetic.zig index 4cac061d93..a366fea2b9 100644 --- a/src/link/MachO/synthetic.zig +++ b/src/link/MachO/synthetic.zig @@ -18,15 +18,15 @@ pub const ZigGotSection = struct { } pub fn addSymbol(zig_got: *ZigGotSection, sym_index: Symbol.Index, macho_file: *MachO) !Index { - const comp = macho_file.base.comp; - const gpa = comp.gpa; + const gpa = macho_file.base.comp.gpa; + const zo = macho_file.getZigObject().?; const index = try zig_got.allocateEntry(gpa); const entry = &zig_got.entries.items[index]; entry.* = sym_index; - const symbol = macho_file.getSymbol(sym_index); + const symbol = zo.getSymbol(sym_index); assert(symbol.flags.needs_zig_got); symbol.flags.has_zig_got = true; - try symbol.addExtra(.{ .zig_got = index }, macho_file); + symbol.addExtra(.{ .zig_got = index }, macho_file); return index; } @@ -53,9 +53,10 @@ pub const ZigGotSection = struct { try macho_file.growSection(macho_file.zig_got_sect_index.?, needed_size); zig_got.dirty = false; } + const zo = macho_file.getZigObject().?; const off = zig_got.entryOffset(index, macho_file); const entry = zig_got.entries.items[index]; - const value = macho_file.getSymbol(entry).getAddress(.{ .stubs = false }, macho_file); + const value = zo.getSymbol(entry).getAddress(.{ .stubs = false }, macho_file); var buf: [8]u8 = undefined; std.mem.writeInt(u64, &buf, value, .little); @@ -63,8 +64,9 @@ pub const ZigGotSection = struct { } pub fn writeAll(zig_got: ZigGotSection, macho_file: *MachO, writer: anytype) !void { + const zo = macho_file.getZigObject().?; for (zig_got.entries.items) |entry| { - const symbol = macho_file.getSymbol(entry); + const symbol = zo.getSymbol(entry); const value = symbol.address(.{ .stubs = false }, macho_file); try writer.writeInt(u64, value, .little); } @@ -87,22 +89,25 @@ pub const ZigGotSection = struct { ) !void { _ = options; _ = unused_fmt_string; + const zig_got = ctx.zig_got; + const macho_file = ctx.macho_file; + const zo = macho_file.getZigObject().?; try writer.writeAll("__zig_got\n"); - for (ctx.zig_got.entries.items, 0..) |entry, index| { - const symbol = ctx.macho_file.getSymbol(entry); + for (zig_got.entries.items, 0..) |entry, index| { + const symbol = zo.getSymbol(entry); try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{ index, - ctx.zig_got.entryAddress(@intCast(index), ctx.macho_file), + zig_got.entryAddress(@intCast(index), macho_file), entry, - symbol.getAddress(.{}, ctx.macho_file), - symbol.getName(ctx.macho_file), + symbol.getAddress(.{}, macho_file), + symbol.getName(macho_file), }); } } }; pub const GotSection = struct { - symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, + symbols: std.ArrayListUnmanaged(MachO.Ref) = .{}, pub const Index = u32; @@ -110,14 +115,14 @@ pub const GotSection = struct { got.symbols.deinit(allocator); } - pub fn addSymbol(got: *GotSection, sym_index: Symbol.Index, macho_file: *MachO) !void { + pub fn addSymbol(got: *GotSection, ref: MachO.Ref, macho_file: *MachO) !void { const gpa = macho_file.base.comp.gpa; const index = @as(Index, @intCast(got.symbols.items.len)); const entry = try got.symbols.addOne(gpa); - entry.* = sym_index; - const symbol = macho_file.getSymbol(sym_index); + entry.* = ref; + const symbol = ref.getSymbol(macho_file).?; symbol.flags.has_got = true; - try symbol.addExtra(.{ .got = index }, macho_file); + symbol.addExtra(.{ .got = index }, macho_file); } pub fn getAddress(got: GotSection, index: Index, macho_file: *MachO) u64 { @@ -133,8 +138,8 @@ pub const GotSection = struct { pub fn write(got: GotSection, macho_file: *MachO, writer: anytype) !void { const tracy = trace(@src()); defer tracy.end(); - for (got.symbols.items) |sym_index| { - const sym = macho_file.getSymbol(sym_index); + for (got.symbols.items) |ref| { + const sym = ref.getSymbol(macho_file).?; const value = if (sym.flags.import) @as(u64, 0) else sym.getAddress(.{}, macho_file); try writer.writeInt(u64, value, .little); } @@ -157,12 +162,12 @@ pub const GotSection = struct { ) !void { _ = options; _ = unused_fmt_string; - for (ctx.got.symbols.items, 0..) |entry, i| { - const symbol = ctx.macho_file.getSymbol(entry); + for (ctx.got.symbols.items, 0..) |ref, i| { + const symbol = ref.getSymbol(ctx.macho_file).?; try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{ i, symbol.getGotAddress(ctx.macho_file), - entry, + ref, symbol.getAddress(.{}, ctx.macho_file), symbol.getName(ctx.macho_file), }); @@ -171,7 +176,7 @@ pub const GotSection = struct { }; pub const StubsSection = struct { - symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, + symbols: std.ArrayListUnmanaged(MachO.Ref) = .{}, pub const Index = u32; @@ -179,13 +184,13 @@ pub const StubsSection = struct { stubs.symbols.deinit(allocator); } - pub fn addSymbol(stubs: *StubsSection, sym_index: Symbol.Index, macho_file: *MachO) !void { + pub fn addSymbol(stubs: *StubsSection, ref: MachO.Ref, macho_file: *MachO) !void { const gpa = macho_file.base.comp.gpa; const index = @as(Index, @intCast(stubs.symbols.items.len)); const entry = try stubs.symbols.addOne(gpa); - entry.* = sym_index; - const symbol = macho_file.getSymbol(sym_index); - try symbol.addExtra(.{ .stubs = index }, macho_file); + entry.* = ref; + const symbol = ref.getSymbol(macho_file).?; + symbol.addExtra(.{ .stubs = index }, macho_file); } pub fn getAddress(stubs: StubsSection, index: Index, macho_file: *MachO) u64 { @@ -205,8 +210,8 @@ pub const StubsSection = struct { const cpu_arch = macho_file.getTarget().cpu.arch; const laptr_sect = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_sect_index.?]; - for (stubs.symbols.items, 0..) |sym_index, idx| { - const sym = macho_file.getSymbol(sym_index); + for (stubs.symbols.items, 0..) |ref, idx| { + const sym = ref.getSymbol(macho_file).?; const source = sym.getAddress(.{ .stubs = true }, macho_file); const target = laptr_sect.addr + idx * @sizeOf(u64); switch (cpu_arch) { @@ -248,12 +253,12 @@ pub const StubsSection = struct { ) !void { _ = options; _ = unused_fmt_string; - for (ctx.stubs.symbols.items, 0..) |entry, i| { - const symbol = ctx.macho_file.getSymbol(entry); + for (ctx.stubs.symbols.items, 0..) |ref, i| { + const symbol = ref.getSymbol(ctx.macho_file).?; try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{ i, symbol.getStubsAddress(ctx.macho_file), - entry, + ref, symbol.getAddress(.{}, ctx.macho_file), symbol.getName(ctx.macho_file), }); @@ -284,8 +289,8 @@ pub const StubsHelperSection = struct { _ = stubs_helper; const cpu_arch = macho_file.getTarget().cpu.arch; var s: usize = preambleSize(cpu_arch); - for (macho_file.stubs.symbols.items) |sym_index| { - const sym = macho_file.getSymbol(sym_index); + for (macho_file.stubs.symbols.items) |ref| { + const sym = ref.getSymbol(macho_file).?; if (sym.flags.weak) continue; s += entrySize(cpu_arch); } @@ -304,8 +309,8 @@ pub const StubsHelperSection = struct { const entry_size = entrySize(cpu_arch); var idx: usize = 0; - for (macho_file.stubs.symbols.items) |sym_index| { - const sym = macho_file.getSymbol(sym_index); + for (macho_file.stubs.symbols.items) |ref| { + const sym = ref.getSymbol(macho_file).?; if (sym.flags.weak) continue; const offset = macho_file.lazy_bind.offsets.items[idx]; const source: i64 = @intCast(sect.addr + preamble_size + entry_size * idx); @@ -339,14 +344,15 @@ pub const StubsHelperSection = struct { fn writePreamble(stubs_helper: StubsHelperSection, macho_file: *MachO, writer: anytype) !void { _ = stubs_helper; + const obj = macho_file.getInternalObject().?; const cpu_arch = macho_file.getTarget().cpu.arch; const sect = macho_file.sections.items(.header)[macho_file.stubs_helper_sect_index.?]; const dyld_private_addr = target: { - const sym = macho_file.getSymbol(macho_file.dyld_private_index.?); + const sym = obj.getDyldPrivateRef(macho_file).?.getSymbol(macho_file).?; break :target sym.getAddress(.{}, macho_file); }; const dyld_stub_binder_addr = target: { - const sym = macho_file.getSymbol(macho_file.dyld_stub_binder_index.?); + const sym = obj.getDyldStubBinderRef(macho_file).?.getSymbol(macho_file).?; break :target sym.getGotAddress(macho_file); }; switch (cpu_arch) { @@ -402,8 +408,8 @@ pub const LaSymbolPtrSection = struct { const cpu_arch = macho_file.getTarget().cpu.arch; const sect = macho_file.sections.items(.header)[macho_file.stubs_helper_sect_index.?]; var stub_helper_idx: u32 = 0; - for (macho_file.stubs.symbols.items) |sym_index| { - const sym = macho_file.getSymbol(sym_index); + for (macho_file.stubs.symbols.items) |ref| { + const sym = ref.getSymbol(macho_file).?; if (sym.flags.weak) { const value = sym.getAddress(.{ .stubs = false }, macho_file); try writer.writeInt(u64, @intCast(value), .little); @@ -418,7 +424,7 @@ pub const LaSymbolPtrSection = struct { }; pub const TlvPtrSection = struct { - symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, + symbols: std.ArrayListUnmanaged(MachO.Ref) = .{}, pub const Index = u32; @@ -426,13 +432,13 @@ pub const TlvPtrSection = struct { tlv.symbols.deinit(allocator); } - pub fn addSymbol(tlv: *TlvPtrSection, sym_index: Symbol.Index, macho_file: *MachO) !void { + pub fn addSymbol(tlv: *TlvPtrSection, ref: MachO.Ref, macho_file: *MachO) !void { const gpa = macho_file.base.comp.gpa; const index = @as(Index, @intCast(tlv.symbols.items.len)); const entry = try tlv.symbols.addOne(gpa); - entry.* = sym_index; - const symbol = macho_file.getSymbol(sym_index); - try symbol.addExtra(.{ .tlv_ptr = index }, macho_file); + entry.* = ref; + const symbol = ref.getSymbol(macho_file).?; + symbol.addExtra(.{ .tlv_ptr = index }, macho_file); } pub fn getAddress(tlv: TlvPtrSection, index: Index, macho_file: *MachO) u64 { @@ -449,8 +455,8 @@ pub const TlvPtrSection = struct { const tracy = trace(@src()); defer tracy.end(); - for (tlv.symbols.items) |sym_index| { - const sym = macho_file.getSymbol(sym_index); + for (tlv.symbols.items) |ref| { + const sym = ref.getSymbol(macho_file).?; if (sym.flags.import) { try writer.writeInt(u64, 0, .little); } else { @@ -476,12 +482,12 @@ pub const TlvPtrSection = struct { ) !void { _ = options; _ = unused_fmt_string; - for (ctx.tlv.symbols.items, 0..) |entry, i| { - const symbol = ctx.macho_file.getSymbol(entry); + for (ctx.tlv.symbols.items, 0..) |ref, i| { + const symbol = ref.getSymbol(ctx.macho_file).?; try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{ i, symbol.getTlvPtrAddress(ctx.macho_file), - entry, + ref, symbol.getAddress(.{}, ctx.macho_file), symbol.getName(ctx.macho_file), }); @@ -490,7 +496,7 @@ pub const TlvPtrSection = struct { }; pub const ObjcStubsSection = struct { - symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, + symbols: std.ArrayListUnmanaged(MachO.Ref) = .{}, pub fn deinit(objc: *ObjcStubsSection, allocator: Allocator) void { objc.symbols.deinit(allocator); @@ -504,13 +510,13 @@ pub const ObjcStubsSection = struct { }; } - pub fn addSymbol(objc: *ObjcStubsSection, sym_index: Symbol.Index, macho_file: *MachO) !void { + pub fn addSymbol(objc: *ObjcStubsSection, ref: MachO.Ref, macho_file: *MachO) !void { const gpa = macho_file.base.comp.gpa; const index = @as(Index, @intCast(objc.symbols.items.len)); const entry = try objc.symbols.addOne(gpa); - entry.* = sym_index; - const symbol = macho_file.getSymbol(sym_index); - try symbol.addExtra(.{ .objc_stubs = index }, macho_file); + entry.* = ref; + const symbol = ref.getSymbol(macho_file).?; + symbol.addExtra(.{ .objc_stubs = index }, macho_file); } pub fn getAddress(objc: ObjcStubsSection, index: Index, macho_file: *MachO) u64 { @@ -527,8 +533,10 @@ pub const ObjcStubsSection = struct { const tracy = trace(@src()); defer tracy.end(); - for (objc.symbols.items, 0..) |sym_index, idx| { - const sym = macho_file.getSymbol(sym_index); + const obj = macho_file.getInternalObject().?; + + for (objc.symbols.items, 0..) |ref, idx| { + const sym = ref.getSymbol(macho_file).?; const addr = objc.getAddress(@intCast(idx), macho_file); switch (macho_file.getTarget().cpu.arch) { .x86_64 => { @@ -540,7 +548,7 @@ pub const ObjcStubsSection = struct { } try writer.writeAll(&.{ 0xff, 0x25 }); { - const target_sym = macho_file.getSymbol(macho_file.objc_msg_send_index.?); + const target_sym = obj.getObjcMsgSendRef(macho_file).?.getSymbol(macho_file).?; const target = target_sym.getGotAddress(macho_file); const source = addr + 7; try writer.writeInt(i32, @intCast(target - source - 2 - 4), .little); @@ -560,7 +568,7 @@ pub const ObjcStubsSection = struct { ); } { - const target_sym = macho_file.getSymbol(macho_file.objc_msg_send_index.?); + const target_sym = obj.getObjcMsgSendRef(macho_file).?.getSymbol(macho_file).?; const target = target_sym.getGotAddress(macho_file); const source = addr + 2 * @sizeOf(u32); const pages = try aarch64.calcNumberOfPages(@intCast(source), @intCast(target)); @@ -599,12 +607,12 @@ pub const ObjcStubsSection = struct { ) !void { _ = options; _ = unused_fmt_string; - for (ctx.objc.symbols.items, 0..) |entry, i| { - const symbol = ctx.macho_file.getSymbol(entry); + for (ctx.objc.symbols.items, 0..) |ref, i| { + const symbol = ref.getSymbol(ctx.macho_file).?; try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{ i, symbol.getObjcStubsAddress(ctx.macho_file), - entry, + ref, symbol.getAddress(.{}, ctx.macho_file), symbol.getName(ctx.macho_file), }); @@ -620,31 +628,89 @@ pub const Indsymtab = struct { return @intCast(macho_file.stubs.symbols.items.len * 2 + macho_file.got.symbols.items.len); } + pub fn updateSize(ind: *Indsymtab, macho_file: *MachO) !void { + macho_file.dysymtab_cmd.nindirectsyms = ind.nsyms(macho_file); + } + pub fn write(ind: Indsymtab, macho_file: *MachO, writer: anytype) !void { const tracy = trace(@src()); defer tracy.end(); _ = ind; - for (macho_file.stubs.symbols.items) |sym_index| { - const sym = macho_file.getSymbol(sym_index); + for (macho_file.stubs.symbols.items) |ref| { + const sym = ref.getSymbol(macho_file).?; try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little); } - for (macho_file.got.symbols.items) |sym_index| { - const sym = macho_file.getSymbol(sym_index); + for (macho_file.got.symbols.items) |ref| { + const sym = ref.getSymbol(macho_file).?; try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little); } - for (macho_file.stubs.symbols.items) |sym_index| { - const sym = macho_file.getSymbol(sym_index); + for (macho_file.stubs.symbols.items) |ref| { + const sym = ref.getSymbol(macho_file).?; try writer.writeInt(u32, sym.getOutputSymtabIndex(macho_file).?, .little); } } }; +pub const DataInCode = struct { + entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, + + pub fn deinit(dice: *DataInCode, allocator: Allocator) void { + dice.entries.deinit(allocator); + } + + pub fn size(dice: DataInCode) usize { + return dice.entries.items.len * @sizeOf(macho.data_in_code_entry); + } + + pub fn updateSize(dice: *DataInCode, macho_file: *MachO) !void { + const gpa = macho_file.base.comp.gpa; + const base_address = if (!macho_file.base.isRelocatable()) + macho_file.getTextSegment().vmaddr + else + 0; + + for (macho_file.objects.items) |index| { + const object = macho_file.getFile(index).?.object; + const dices = object.getDataInCode(); + + try dice.entries.ensureUnusedCapacity(gpa, dices.len); + + var next_dice: usize = 0; + for (object.getAtoms()) |atom_index| { + if (next_dice >= dices.len) break; + const atom = object.getAtom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + const start_off = atom.getInputAddress(macho_file); + const end_off = start_off + atom.size; + const start_dice = next_dice; + + if (end_off < dices[next_dice].offset) continue; + + while (next_dice < dices.len and + dices[next_dice].offset < end_off) : (next_dice += 1) + {} + + if (atom.alive.load(.seq_cst)) for (dices[start_dice..next_dice]) |d| { + dice.entries.appendAssumeCapacity(.{ + .offset = @intCast(atom.getAddress(macho_file) + d.offset - start_off - base_address), + .length = d.length, + .kind = d.kind, + }); + }; + } + } + + macho_file.data_in_code_cmd.datasize = math.cast(u32, dice.size()) orelse return error.Overflow; + } +}; + const aarch64 = @import("../aarch64.zig"); const assert = std.debug.assert; +const macho = std.macho; const math = std.math; const std = @import("std"); const trace = @import("../../tracy.zig").trace; From 58997363d3c94aaa66960f0e87b40d5f98f0471b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 9 Jul 2024 19:42:20 +0200 Subject: [PATCH 11/45] macho: migrate MachO.File abstraction --- src/link/MachO/file.zig | 237 +++++++++++++++++++++------------------- 1 file changed, 127 insertions(+), 110 deletions(-) diff --git a/src/link/MachO/file.zig b/src/link/MachO/file.zig index 60c7a70f26..c01416efd5 100644 --- a/src/link/MachO/file.zig +++ b/src/link/MachO/file.zig @@ -32,106 +32,15 @@ pub const File = union(enum) { pub fn resolveSymbols(file: File, macho_file: *MachO) void { switch (file) { - .internal => unreachable, inline else => |x| x.resolveSymbols(macho_file), } } - pub fn resetGlobals(file: File, macho_file: *MachO) void { + pub fn scanRelocs(file: File, macho_file: *MachO) !void { switch (file) { - .internal => unreachable, - inline else => |x| x.resetGlobals(macho_file), - } - } - - pub fn claimUnresolved(file: File, macho_file: *MachO) error{OutOfMemory}!void { - assert(file == .object or file == .zig_object); - - for (file.getSymbols(), 0..) |sym_index, i| { - const nlist_idx = @as(Symbol.Index, @intCast(i)); - const nlist = switch (file) { - .object => |x| x.symtab.items(.nlist)[nlist_idx], - .zig_object => |x| x.symtab.items(.nlist)[nlist_idx], - else => unreachable, - }; - if (!nlist.ext()) continue; - if (!nlist.undf()) continue; - - const sym = macho_file.getSymbol(sym_index); - if (sym.getFile(macho_file) != null) continue; - - const is_import = switch (macho_file.undefined_treatment) { - .@"error" => false, - .warn, .suppress => nlist.weakRef(), - .dynamic_lookup => true, - }; - if (is_import) { - sym.value = 0; - sym.atom = 0; - sym.nlist_idx = 0; - sym.file = macho_file.internal_object.?; - sym.flags.weak = false; - sym.flags.weak_ref = nlist.weakRef(); - sym.flags.import = is_import; - sym.visibility = .global; - try macho_file.getInternalObject().?.symbols.append(macho_file.base.comp.gpa, sym_index); - } - } - } - - pub fn claimUnresolvedRelocatable(file: File, macho_file: *MachO) void { - assert(file == .object or file == .zig_object); - - for (file.getSymbols(), 0..) |sym_index, i| { - const nlist_idx = @as(Symbol.Index, @intCast(i)); - const nlist = switch (file) { - .object => |x| x.symtab.items(.nlist)[nlist_idx], - .zig_object => |x| x.symtab.items(.nlist)[nlist_idx], - else => unreachable, - }; - if (!nlist.ext()) continue; - if (!nlist.undf()) continue; - - const sym = macho_file.getSymbol(sym_index); - if (sym.getFile(macho_file) != null) continue; - - sym.value = 0; - sym.atom = 0; - sym.nlist_idx = nlist_idx; - sym.file = file.getIndex(); - sym.flags.weak_ref = nlist.weakRef(); - sym.flags.import = true; - sym.visibility = .global; - } - } - - pub fn markImportsExports(file: File, macho_file: *MachO) void { - assert(file == .object or file == .zig_object); - - for (file.getSymbols()) |sym_index| { - const sym = macho_file.getSymbol(sym_index); - const other_file = sym.getFile(macho_file) orelse continue; - if (sym.visibility != .global) continue; - if (other_file == .dylib and !sym.flags.abs) { - sym.flags.import = true; - continue; - } - if (other_file.getIndex() == file.getIndex()) { - sym.flags.@"export" = true; - } - } - } - - pub fn markExportsRelocatable(file: File, macho_file: *MachO) void { - assert(file == .object or file == .zig_object); - - for (file.getSymbols()) |sym_index| { - const sym = macho_file.getSymbol(sym_index); - const other_file = sym.getFile(macho_file) orelse continue; - if (sym.visibility != .global) continue; - if (other_file.getIndex() == file.getIndex()) { - sym.flags.@"export" = true; - } + .dylib => unreachable, + .internal => |x| x.scanRelocs(macho_file), + inline else => |x| x.scanRelocs(macho_file), } } @@ -162,16 +71,134 @@ pub const File = union(enum) { return base + (file.getIndex() << 24); } - pub fn getSymbols(file: File) []const Symbol.Index { + pub fn getAtom(file: File, atom_index: Atom.Index) ?*Atom { return switch (file) { - inline else => |x| x.symbols.items, + .dylib => unreachable, + inline else => |x| x.getAtom(atom_index), }; } pub fn getAtoms(file: File) []const Atom.Index { return switch (file) { .dylib => unreachable, - inline else => |x| x.atoms.items, + inline else => |x| x.getAtoms(), + }; + } + + pub fn addAtomExtra(file: File, allocator: Allocator, extra: Atom.Extra) !u32 { + return switch (file) { + .dylib => unreachable, + inline else => |x| x.addAtomExtra(allocator, extra), + }; + } + + pub fn getAtomExtra(file: File, index: u32) Atom.Extra { + return switch (file) { + .dylib => unreachable, + inline else => |x| x.getAtomExtra(index), + }; + } + + pub fn setAtomExtra(file: File, index: u32, extra: Atom.Extra) void { + return switch (file) { + .dylib => unreachable, + inline else => |x| x.setAtomExtra(index, extra), + }; + } + + pub fn getSymbols(file: File) []Symbol { + return switch (file) { + inline else => |x| x.symbols.items, + }; + } + + pub fn getSymbolRef(file: File, sym_index: Symbol.Index, macho_file: *MachO) MachO.Ref { + return switch (file) { + inline else => |x| x.getSymbolRef(sym_index, macho_file), + }; + } + + pub fn markImportsExports(file: File, macho_file: *MachO) void { + const nsyms = switch (file) { + .dylib => unreachable, + inline else => |x| x.symbols.items.len, + }; + for (0..nsyms) |i| { + const ref = file.getSymbolRef(@intCast(i), macho_file); + if (ref.getFile(macho_file) == null) continue; + const sym = ref.getSymbol(macho_file).?; + if (sym.visibility != .global) continue; + if (sym.getFile(macho_file).? == .dylib and !sym.flags.abs) { + sym.flags.import = true; + continue; + } + if (file.getIndex() == ref.file) { + sym.flags.@"export" = true; + } + } + } + + pub fn createSymbolIndirection(file: File, macho_file: *MachO) !void { + const nsyms = switch (file) { + inline else => |x| x.symbols.items.len, + }; + for (0..nsyms) |i| { + const ref = file.getSymbolRef(@intCast(i), macho_file); + if (ref.getFile(macho_file) == null) continue; + if (ref.file != file.getIndex()) continue; + const sym = ref.getSymbol(macho_file).?; + if (sym.getSectionFlags().got) { + log.debug("'{s}' needs GOT", .{sym.getName(macho_file)}); + try macho_file.got.addSymbol(ref, macho_file); + } + if (sym.getSectionFlags().stubs) { + log.debug("'{s}' needs STUBS", .{sym.getName(macho_file)}); + try macho_file.stubs.addSymbol(ref, macho_file); + } + if (sym.getSectionFlags().tlv_ptr) { + log.debug("'{s}' needs TLV pointer", .{sym.getName(macho_file)}); + try macho_file.tlv_ptr.addSymbol(ref, macho_file); + } + if (sym.getSectionFlags().objc_stubs) { + log.debug("'{s}' needs OBJC STUBS", .{sym.getName(macho_file)}); + try macho_file.objc_stubs.addSymbol(ref, macho_file); + } + } + } + + pub fn initOutputSections(file: File, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + for (file.getAtoms()) |atom_index| { + const atom = file.getAtom(atom_index) orelse continue; + if (!atom.alive.load(.seq_cst)) continue; + atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file); + } + } + + pub fn dedupLiterals(file: File, lp: MachO.LiteralPool, macho_file: *MachO) void { + return switch (file) { + .dylib => unreachable, + inline else => |x| x.dedupLiterals(lp, macho_file), + }; + } + + pub fn writeAtoms(file: File, macho_file: *MachO) !void { + return switch (file) { + .dylib => unreachable, + inline else => |x| x.writeAtoms(macho_file), + }; + } + + pub fn calcSymtabSize(file: File, macho_file: *MachO) !void { + return switch (file) { + inline else => |x| x.calcSymtabSize(macho_file), + }; + } + + pub fn writeSymtab(file: File, macho_file: *MachO, ctx: anytype) !void { + return switch (file) { + inline else => |x| x.writeSymtab(macho_file, ctx), }; } @@ -198,18 +225,6 @@ pub const File = union(enum) { }; } - pub fn calcSymtabSize(file: File, macho_file: *MachO) !void { - return switch (file) { - inline else => |x| x.calcSymtabSize(macho_file), - }; - } - - pub fn writeSymtab(file: File, macho_file: *MachO, ctx: anytype) !void { - return switch (file) { - inline else => |x| x.writeSymtab(macho_file, ctx), - }; - } - pub const Index = u32; pub const Entry = union(enum) { @@ -225,8 +240,10 @@ pub const File = union(enum) { }; const assert = std.debug.assert; +const log = std.log.scoped(.link); const macho = std.macho; const std = @import("std"); +const trace = @import("../../tracy.zig").trace; const Allocator = std.mem.Allocator; const Archive = @import("Archive.zig"); From b96339f6f6ed368d04639c1d6c67eb85c5eb3c7b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 9 Jul 2024 19:52:44 +0200 Subject: [PATCH 12/45] macho: migrate Relocation struct --- src/link/MachO/Relocation.zig | 34 ++++++++++++------ src/link/MachO/thunks.zig | 66 ++++++++++++++++++++--------------- 2 files changed, 60 insertions(+), 40 deletions(-) diff --git a/src/link/MachO/Relocation.zig b/src/link/MachO/Relocation.zig index 425171a463..390e543ea9 100644 --- a/src/link/MachO/Relocation.zig +++ b/src/link/MachO/Relocation.zig @@ -1,4 +1,4 @@ -tag: enum { @"extern", local }, +tag: Tag, offset: u32, target: u32, addend: i64, @@ -10,34 +10,44 @@ meta: packed struct { symbolnum: u24, }, -pub fn getTargetSymbol(rel: Relocation, macho_file: *MachO) *Symbol { +pub fn getTargetSymbolRef(rel: Relocation, atom: Atom, macho_file: *MachO) MachO.Ref { assert(rel.tag == .@"extern"); - return macho_file.getSymbol(rel.target); + return atom.getFile(macho_file).getSymbolRef(rel.target, macho_file); } -pub fn getTargetAtom(rel: Relocation, macho_file: *MachO) *Atom { +pub fn getTargetSymbol(rel: Relocation, atom: Atom, macho_file: *MachO) *Symbol { + assert(rel.tag == .@"extern"); + const ref = atom.getFile(macho_file).getSymbolRef(rel.target, macho_file); + return ref.getSymbol(macho_file).?; +} + +pub fn getTargetAtom(rel: Relocation, atom: Atom, macho_file: *MachO) *Atom { assert(rel.tag == .local); - return macho_file.getAtom(rel.target).?; + return atom.getFile(macho_file).getAtom(rel.target).?; } -pub fn getTargetAddress(rel: Relocation, macho_file: *MachO) u64 { +pub fn getTargetAddress(rel: Relocation, atom: Atom, macho_file: *MachO) u64 { return switch (rel.tag) { - .local => rel.getTargetAtom(macho_file).getAddress(macho_file), - .@"extern" => rel.getTargetSymbol(macho_file).getAddress(.{}, macho_file), + .local => rel.getTargetAtom(atom, macho_file).getAddress(macho_file), + .@"extern" => rel.getTargetSymbol(atom, macho_file).getAddress(.{}, macho_file), }; } -pub fn getGotTargetAddress(rel: Relocation, macho_file: *MachO) u64 { +pub fn getGotTargetAddress(rel: Relocation, atom: Atom, macho_file: *MachO) u64 { return switch (rel.tag) { .local => 0, - .@"extern" => rel.getTargetSymbol(macho_file).getGotAddress(macho_file), + .@"extern" => rel.getTargetSymbol(atom, macho_file).getGotAddress(macho_file), }; } pub fn getZigGotTargetAddress(rel: Relocation, macho_file: *MachO) u64 { + const zo = macho_file.getZigObject().?; return switch (rel.tag) { .local => 0, - .@"extern" => rel.getTargetSymbol(macho_file).getZigGotAddress(macho_file), + .@"extern" => { + const ref = zo.getSymbolRef(rel.target, macho_file); + return ref.getSymbol(macho_file).?.getZigGotAddress(macho_file); + }, }; } @@ -155,6 +165,8 @@ pub const Type = enum { unsigned, }; +const Tag = enum { local, @"extern" }; + const assert = std.debug.assert; const macho = std.macho; const math = std.math; diff --git a/src/link/MachO/thunks.zig b/src/link/MachO/thunks.zig index 710dac6d42..d832e6fcf6 100644 --- a/src/link/MachO/thunks.zig +++ b/src/link/MachO/thunks.zig @@ -5,55 +5,45 @@ pub fn createThunks(sect_id: u8, macho_file: *MachO) !void { const gpa = macho_file.base.comp.gpa; const slice = macho_file.sections.slice(); const header = &slice.items(.header)[sect_id]; + const thnks = &slice.items(.thunks)[sect_id]; const atoms = slice.items(.atoms)[sect_id].items; assert(atoms.len > 0); - for (atoms) |atom_index| { - macho_file.getAtom(atom_index).?.value = @bitCast(@as(i64, -1)); + for (atoms) |ref| { + ref.getAtom(macho_file).?.value = @bitCast(@as(i64, -1)); } var i: usize = 0; while (i < atoms.len) { const start = i; - const start_atom = macho_file.getAtom(atoms[start]).?; + const start_atom = atoms[start].getAtom(macho_file).?; assert(start_atom.flags.alive); - start_atom.value = try advance(header, start_atom.size, start_atom.alignment); + start_atom.value = advance(header, start_atom.size, start_atom.alignment); i += 1; while (i < atoms.len and header.size - start_atom.value < max_allowed_distance) : (i += 1) { - const atom_index = atoms[i]; - const atom = macho_file.getAtom(atom_index).?; + const atom = atoms[i].getAtom(macho_file).?; assert(atom.flags.alive); - atom.value = try advance(header, atom.size, atom.alignment); + atom.value = advance(header, atom.size, atom.alignment); } // Insert a thunk at the group end const thunk_index = try macho_file.addThunk(); const thunk = macho_file.getThunk(thunk_index); thunk.out_n_sect = sect_id; + try thnks.append(gpa, thunk_index); // Scan relocs in the group and create trampolines for any unreachable callsite - for (atoms[start..i]) |atom_index| { - const atom = macho_file.getAtom(atom_index).?; - log.debug("atom({d}) {s}", .{ atom_index, atom.getName(macho_file) }); - for (atom.getRelocs(macho_file)) |rel| { - if (rel.type != .branch) continue; - if (isReachable(atom, rel, macho_file)) continue; - try thunk.symbols.put(gpa, rel.target, {}); - } - try atom.addExtra(.{ .thunk = thunk_index }, macho_file); - atom.flags.thunk = true; - } - + try scanRelocs(thunk_index, gpa, atoms[start..i], macho_file); thunk.value = try advance(header, thunk.size(), .@"4"); log.debug("thunk({d}) : {}", .{ thunk_index, thunk.fmt(macho_file) }); } } -fn advance(sect: *macho.section_64, size: u64, alignment: Atom.Alignment) !u64 { +fn advance(sect: *macho.section_64, size: u64, alignment: Atom.Alignment) u64 { const offset = alignment.forward(sect.size); const padding = offset - sect.size; sect.size += padding + size; @@ -61,14 +51,32 @@ fn advance(sect: *macho.section_64, size: u64, alignment: Atom.Alignment) !u64 { return offset; } +fn scanRelocs(thunk_index: Thunk.Index, gpa: Allocator, atoms: []const MachO.Ref, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const thunk = macho_file.getThunk(thunk_index); + + for (atoms) |ref| { + const atom = ref.getAtom(macho_file).?; + log.debug("atom({d}) {s}", .{ atom.atom_index, atom.getName(macho_file) }); + for (atom.getRelocs(macho_file)) |rel| { + if (rel.type != .branch) continue; + if (isReachable(atom, rel, macho_file)) continue; + try thunk.symbols.put(gpa, rel.getTargetSymbolRef(atom.*, macho_file), {}); + } + atom.addExtra(.{ .thunk = thunk_index }, macho_file); + } +} + fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool { - const target = rel.getTargetSymbol(macho_file); + const target = rel.getTargetSymbol(atom.*, macho_file); if (target.flags.stubs or target.flags.objc_stubs) return false; - if (atom.out_n_sect != target.out_n_sect) return false; + if (atom.out_n_sect != target.getOutputSectionIndex(macho_file)) return false; const target_atom = target.getAtom(macho_file).?; if (target_atom.value == @as(u64, @bitCast(@as(i64, -1)))) return false; const saddr = @as(i64, @intCast(atom.getAddress(macho_file))) + @as(i64, @intCast(rel.offset - atom.off)); - const taddr: i64 = @intCast(rel.getTargetAddress(macho_file)); + const taddr: i64 = @intCast(rel.getTargetAddress(atom.*, macho_file)); _ = math.cast(i28, taddr + rel.addend - saddr) orelse return false; return true; } @@ -76,7 +84,7 @@ fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool { pub const Thunk = struct { value: u64 = 0, out_n_sect: u8 = 0, - symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{}, + symbols: std.AutoArrayHashMapUnmanaged(MachO.Ref, void) = .{}, pub fn deinit(thunk: *Thunk, allocator: Allocator) void { thunk.symbols.deinit(allocator); @@ -96,8 +104,8 @@ pub const Thunk = struct { } pub fn write(thunk: Thunk, macho_file: *MachO, writer: anytype) !void { - for (thunk.symbols.keys(), 0..) |sym_index, i| { - const sym = macho_file.getSymbol(sym_index); + for (thunk.symbols.keys(), 0..) |ref, i| { + const sym = ref.getSymbol(macho_file).?; const saddr = thunk.getAddress(macho_file) + i * trampoline_size; const taddr = sym.getAddress(.{}, macho_file); const pages = try aarch64.calcNumberOfPages(@intCast(saddr), @intCast(taddr)); @@ -144,9 +152,9 @@ pub const Thunk = struct { const thunk = ctx.thunk; const macho_file = ctx.macho_file; try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() }); - for (thunk.symbols.keys()) |index| { - const sym = macho_file.getSymbol(index); - try writer.print(" %{d} : {s} : @{x}\n", .{ index, sym.getName(macho_file), sym.value }); + for (thunk.symbols.keys()) |ref| { + const sym = ref.getSymbol(macho_file).?; + try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value }); } } From 882ff3ae31a39edf23db00b558db60ee574d9859 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 9 Jul 2024 19:59:43 +0200 Subject: [PATCH 13/45] macho: migrate eh_frame module --- src/link/MachO/eh_frame.zig | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/link/MachO/eh_frame.zig b/src/link/MachO/eh_frame.zig index b05da1b828..46d6aef3e0 100644 --- a/src/link/MachO/eh_frame.zig +++ b/src/link/MachO/eh_frame.zig @@ -68,7 +68,8 @@ pub const Cie = struct { pub fn getPersonality(cie: Cie, macho_file: *MachO) ?*Symbol { const personality = cie.personality orelse return null; - return macho_file.getSymbol(personality.index); + const object = cie.getObject(macho_file); + return object.getSymbolRef(personality.index, macho_file).getSymbol(macho_file); } pub fn eql(cie: Cie, other: Cie, macho_file: *MachO) bool { @@ -223,11 +224,11 @@ pub const Fde = struct { } pub fn getAtom(fde: Fde, macho_file: *MachO) *Atom { - return macho_file.getAtom(fde.atom).?; + return fde.getObject(macho_file).getAtom(fde.atom).?; } pub fn getLsdaAtom(fde: Fde, macho_file: *MachO) ?*Atom { - return macho_file.getAtom(fde.lsda); + return fde.getObject(macho_file).getAtom(fde.lsda); } pub fn format( @@ -448,7 +449,7 @@ pub fn write(macho_file: *MachO, buffer: []u8) void { } } -pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho.relocation_info)) error{Overflow}!void { +pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: []macho.relocation_info) error{Overflow}!void { const tracy = trace(@src()); defer tracy.end(); @@ -459,6 +460,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho. else => 0, }; + var i: usize = 0; for (macho_file.objects.items) |index| { const object = macho_file.getFile(index).?.object; for (object.cies.items) |cie| { @@ -469,7 +471,7 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho. if (cie.getPersonality(macho_file)) |sym| { const r_address = math.cast(i32, cie.out_offset + cie.personality.?.offset) orelse return error.Overflow; const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow; - relocs.appendAssumeCapacity(.{ + relocs[i] = .{ .r_address = r_address, .r_symbolnum = r_symbolnum, .r_length = 2, @@ -480,7 +482,8 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho. .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_GOT), else => unreachable, }, - }); + }; + i += 1; } } } @@ -531,6 +534,8 @@ pub fn writeRelocs(macho_file: *MachO, code: []u8, relocs: *std.ArrayList(macho. } } } + + assert(relocs.len == i); } pub const EH_PE = struct { From f8b5466aef4131821b259aa5f0d6b9654a5595ce Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 9 Jul 2024 20:26:34 +0200 Subject: [PATCH 14/45] macho: migrate UnwindInfo --- src/link/MachO.zig | 4 +-- src/link/MachO/UnwindInfo.zig | 33 ++++++++++++------ src/link/MachO/dead_strip.zig | 64 ++++++++++++++++++++++------------- 3 files changed, 64 insertions(+), 37 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 5d95838f30..9703890985 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -354,8 +354,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n if (comp.verbose_link) try self.dumpArgv(comp); if (self.getZigObject()) |zo| try zo.flushModule(self, tid); - if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path); - if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path); + // if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path); + // if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path); var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); defer positionals.deinit(); diff --git a/src/link/MachO/UnwindInfo.zig b/src/link/MachO/UnwindInfo.zig index 252333d5bb..f06db2f842 100644 --- a/src/link/MachO/UnwindInfo.zig +++ b/src/link/MachO/UnwindInfo.zig @@ -4,7 +4,7 @@ records: std.ArrayListUnmanaged(Record.Ref) = .{}, /// List of all personalities referenced by either unwind info entries /// or __eh_frame entries. -personalities: [max_personalities]Symbol.Index = undefined, +personalities: [max_personalities]MachO.Ref = undefined, personalities_count: u2 = 0, /// List of common encodings sorted in descending order with the most common first. @@ -42,14 +42,17 @@ fn canFold(macho_file: *MachO, lhs_ref: Record.Ref, rhs_ref: Record.Ref) bool { } pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + const gpa = macho_file.base.comp.gpa; log.debug("generating unwind info", .{}); // Collect all unwind records for (macho_file.sections.items(.atoms)) |atoms| { - for (atoms.items) |atom_index| { - const atom = macho_file.getAtom(atom_index) orelse continue; + for (atoms.items) |ref| { + const atom = ref.getAtom(macho_file) orelse continue; if (!atom.flags.alive) continue; const recs = atom.getUnwindRecords(macho_file); const file = atom.getFile(macho_file); @@ -73,11 +76,15 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void { } const cie = fde.getCie(macho_file); if (cie.getPersonality(macho_file)) |_| { - const personality_index = try info.getOrPutPersonalityFunction(cie.personality.?.index); // TODO handle error + const object = cie.getObject(macho_file); + const sym_ref = object.getSymbolRef(cie.personality.?.index, macho_file); + const personality_index = try info.getOrPutPersonalityFunction(sym_ref); // TODO handle error rec.enc.setPersonalityIndex(personality_index + 1); } } else if (rec.getPersonality(macho_file)) |_| { - const personality_index = try info.getOrPutPersonalityFunction(rec.personality.?); // TODO handle error + const object = rec.getObject(macho_file); + const sym_ref = object.getSymbolRef(rec.personality.?, macho_file); + const personality_index = try info.getOrPutPersonalityFunction(sym_ref); // TODO handle error rec.enc.setPersonalityIndex(personality_index + 1); } } @@ -257,6 +264,9 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void { } pub fn calcSize(info: UnwindInfo) usize { + const tracy = trace(@src()); + defer tracy.end(); + var total_size: usize = 0; total_size += @sizeOf(macho.unwind_info_section_header); total_size += @@ -293,8 +303,8 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void { try writer.writeAll(mem.sliceAsBytes(info.common_encodings[0..info.common_encodings_count])); - for (info.personalities[0..info.personalities_count]) |sym_index| { - const sym = macho_file.getSymbol(sym_index); + for (info.personalities[0..info.personalities_count]) |ref| { + const sym = ref.getSymbol(macho_file).?; try writer.writeInt(u32, @intCast(sym.getGotAddress(macho_file) - seg.vmaddr), .little); } @@ -342,13 +352,13 @@ pub fn write(info: UnwindInfo, macho_file: *MachO, buffer: []u8) !void { @memset(buffer[stream.pos..], 0); } -fn getOrPutPersonalityFunction(info: *UnwindInfo, sym_index: Symbol.Index) error{TooManyPersonalities}!u2 { +fn getOrPutPersonalityFunction(info: *UnwindInfo, ref: MachO.Ref) error{TooManyPersonalities}!u2 { comptime var index: u2 = 0; inline while (index < max_personalities) : (index += 1) { - if (info.personalities[index] == sym_index) { + if (info.personalities[index].eql(ref)) { return index; } else if (index == info.personalities_count) { - info.personalities[index] = sym_index; + info.personalities[index] = ref; info.personalities_count += 1; return index; } @@ -472,7 +482,8 @@ pub const Record = struct { pub fn getPersonality(rec: Record, macho_file: *MachO) ?*Symbol { const personality = rec.personality orelse return null; - return macho_file.getSymbol(personality); + const object = rec.getObject(macho_file); + return object.getSymbolRef(personality, macho_file).getSymbol(macho_file); } pub fn getFde(rec: Record, macho_file: *MachO) ?Fde { diff --git a/src/link/MachO/dead_strip.zig b/src/link/MachO/dead_strip.zig index 84e56c9249..eb27109a82 100644 --- a/src/link/MachO/dead_strip.zig +++ b/src/link/MachO/dead_strip.zig @@ -17,16 +17,16 @@ pub fn gcAtoms(macho_file: *MachO) !void { fn collectRoots(roots: *std.ArrayList(*Atom), objects: []const File.Index, macho_file: *MachO) !void { for (objects) |index| { const object = macho_file.getFile(index).?; - for (object.getSymbols()) |sym_index| { - const sym = macho_file.getSymbol(sym_index); - const file = sym.getFile(macho_file) orelse continue; + for (object.getSymbols(), 0..) |*sym, i| { + const ref = object.getSymbolRef(@intCast(i), macho_file); + const file = ref.getFile(macho_file) orelse continue; if (file.getIndex() != index) continue; if (sym.flags.no_dead_strip or (macho_file.base.isDynLib() and sym.visibility == .global)) try markSymbol(sym, roots, macho_file); } for (object.getAtoms()) |atom_index| { - const atom = macho_file.getAtom(atom_index).?; + const atom = object.getAtom(atom_index) orelse continue; const isec = atom.getInputSection(macho_file); switch (isec.type()) { macho.S_MOD_INIT_FUNC_POINTERS, @@ -51,19 +51,27 @@ fn collectRoots(roots: *std.ArrayList(*Atom), objects: []const File.Index, macho } } - for (macho_file.undefined_symbols.items) |sym_index| { - const sym = macho_file.getSymbol(sym_index); - try markSymbol(sym, roots, macho_file); - } + if (macho_file.getInternalObject()) |obj| { + for (obj.force_undefined.items) |sym_index| { + const ref = obj.getSymbolRef(sym_index, macho_file); + if (ref.getFile(macho_file) != null) { + const sym = ref.getSymbol(macho_file).?; + try markSymbol(sym, roots, macho_file); + } + } - for (&[_]?Symbol.Index{ - macho_file.entry_index, - macho_file.dyld_stub_binder_index, - macho_file.objc_msg_send_index, - }) |index| { - if (index) |idx| { - const sym = macho_file.getSymbol(idx); - try markSymbol(sym, roots, macho_file); + for (&[_]?Symbol.Index{ + obj.entry_index, + obj.dyld_stub_binder_index, + obj.objc_msg_send_index, + }) |index| { + if (index) |idx| { + const ref = obj.getSymbolRef(idx, macho_file); + if (ref.getFile(macho_file) != null) { + const sym = ref.getSymbol(macho_file).?; + try markSymbol(sym, roots, macho_file); + } + } } } } @@ -89,8 +97,9 @@ fn mark(roots: []*Atom, objects: []const File.Index, macho_file: *MachO) void { loop = false; for (objects) |index| { - for (macho_file.getFile(index).?.getAtoms()) |atom_index| { - const atom = macho_file.getAtom(atom_index).?; + const file = macho_file.getFile(index).?; + for (file.getAtoms()) |atom_index| { + const atom = file.getAtom(atom_index) orelse continue; const isec = atom.getInputSection(macho_file); if (isec.isDontDeadStripIfReferencesLive() and !(mem.eql(u8, isec.sectName(), "__eh_frame") or @@ -120,8 +129,11 @@ fn markLive(atom: *Atom, macho_file: *MachO) void { for (atom.getRelocs(macho_file)) |rel| { const target_atom = switch (rel.tag) { - .local => rel.getTargetAtom(macho_file), - .@"extern" => rel.getTargetSymbol(macho_file).getAtom(macho_file), + .local => rel.getTargetAtom(atom.*, macho_file), + .@"extern" => blk: { + const ref = rel.getTargetSymbolRef(atom.*, macho_file); + break :blk if (ref.getSymbol(macho_file)) |sym| sym.getAtom(macho_file) else null; + }, }; if (target_atom) |ta| { if (markAtom(ta)) markLive(ta, macho_file); @@ -151,8 +163,11 @@ fn markLive(atom: *Atom, macho_file: *MachO) void { fn refersLive(atom: *Atom, macho_file: *MachO) bool { for (atom.getRelocs(macho_file)) |rel| { const target_atom = switch (rel.tag) { - .local => rel.getTargetAtom(macho_file), - .@"extern" => rel.getTargetSymbol(macho_file).getAtom(macho_file), + .local => rel.getTargetAtom(atom.*, macho_file), + .@"extern" => blk: { + const ref = rel.getTargetSymbolRef(atom.*, macho_file); + break :blk if (ref.getSymbol(macho_file)) |sym| sym.getAtom(macho_file) else null; + }, }; if (target_atom) |ta| { if (ta.flags.alive) return true; @@ -163,8 +178,9 @@ fn refersLive(atom: *Atom, macho_file: *MachO) bool { fn prune(objects: []const File.Index, macho_file: *MachO) void { for (objects) |index| { - for (macho_file.getFile(index).?.getAtoms()) |atom_index| { - const atom = macho_file.getAtom(atom_index).?; + const file = macho_file.getFile(index).?; + for (file.getAtoms()) |atom_index| { + const atom = file.getAtom(atom_index) orelse continue; if (atom.flags.alive and !atom.flags.visited) { atom.flags.alive = false; atom.markUnwindRecordsDead(macho_file); From 5b4c0cc1f9c4cb047064cffb70bc649b83681814 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 10 Jul 2024 16:28:00 +0200 Subject: [PATCH 15/45] macho: update ZigObject to use new ownership model --- src/arch/x86_64/Emit.zig | 16 +- src/arch/x86_64/Lower.zig | 4 +- src/codegen.zig | 9 +- src/link/MachO.zig | 22 +- src/link/MachO/Atom.zig | 2 - src/link/MachO/ZigObject.zig | 467 ++++++++++++++++++++++------------- 6 files changed, 320 insertions(+), 200 deletions(-) diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index ae54904aaf..2bfb4d6704 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -51,12 +51,12 @@ pub fn emitMir(emit: *Emit) Error!void { }); } else if (emit.lower.bin_file.cast(link.File.MachO)) |macho_file| { // Add relocation to the decl. - const atom = macho_file.getSymbol(symbol.atom_index).getAtom(macho_file).?; - const sym_index = macho_file.getZigObject().?.symbols.items[symbol.sym_index]; + const zo = macho_file.getZigObject().?; + const atom = zo.symbols.items[symbol.atom_index].getAtom(macho_file).?; try atom.addReloc(macho_file, .{ .tag = .@"extern", .offset = end_offset - 4, - .target = sym_index, + .target = symbol.sym_index, .addend = 0, .type = .branch, .meta = .{ @@ -160,11 +160,11 @@ pub fn emitMir(emit: *Emit) Error!void { .Obj => true, .Lib => emit.lower.link_mode == .static, }; - const atom = macho_file.getSymbol(data.atom_index).getAtom(macho_file).?; - const sym_index = macho_file.getZigObject().?.symbols.items[data.sym_index]; - const sym = macho_file.getSymbol(sym_index); + const zo = macho_file.getZigObject().?; + const atom = zo.symbols.items[data.atom_index].getAtom(macho_file).?; + const sym = zo.symbols.items[data.sym_index]; if (sym.flags.needs_zig_got and !is_obj_or_static_lib) { - _ = try sym.getOrCreateZigGotEntry(sym_index, macho_file); + _ = try sym.getOrCreateZigGotEntry(data.sym_index, macho_file); } const @"type": link.File.MachO.Relocation.Type = if (sym.flags.needs_zig_got and !is_obj_or_static_lib) .zig_got_load @@ -179,7 +179,7 @@ pub fn emitMir(emit: *Emit) Error!void { try atom.addReloc(macho_file, .{ .tag = .@"extern", .offset = @intCast(end_offset - 4), - .target = sym_index, + .target = data.sym_index, .addend = 0, .type = @"type", .meta = .{ diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig index bf074ff98f..ed77f71488 100644 --- a/src/arch/x86_64/Lower.zig +++ b/src/arch/x86_64/Lower.zig @@ -425,8 +425,8 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) else => unreachable, }; } else if (lower.bin_file.cast(link.File.MachO)) |macho_file| { - const sym_index = macho_file.getZigObject().?.symbols.items[sym.sym_index]; - const macho_sym = macho_file.getSymbol(sym_index); + const zo = macho_file.getZigObject().?; + const macho_sym = zo.symbols.items[sym.sym_index]; if (macho_sym.flags.tlv) { _ = lower.reloc(.{ .linker_reloc = sym }); diff --git a/src/codegen.zig b/src/codegen.zig index 33a0b556f7..3dc3d415be 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -901,15 +901,16 @@ fn genDeclRef( } return GenResult.mcv(.{ .load_symbol = sym.esym_index }); } else if (lf.cast(link.File.MachO)) |macho_file| { + const zo = macho_file.getZigObject().?; if (is_extern) { const name = decl.name.toSlice(ip); const lib_name = if (decl.getOwnedVariable(zcu)) |ov| ov.lib_name.toSlice(ip) else null; const sym_index = try macho_file.getGlobalSymbol(name, lib_name); - macho_file.getSymbol(macho_file.getZigObject().?.symbols.items[sym_index]).flags.needs_got = true; + zo.symbols.items[sym_index].flags.needs_got = true; return GenResult.mcv(.{ .load_symbol = sym_index }); } - const sym_index = try macho_file.getZigObject().?.getOrCreateMetadataForDecl(macho_file, decl_index); - const sym = macho_file.getSymbol(sym_index); + const sym_index = try zo.getOrCreateMetadataForDecl(macho_file, decl_index); + const sym = zo.symbols.items[sym_index]; if (is_threadlocal) { return GenResult.mcv(.{ .load_tlv = sym.nlist_idx }); } @@ -956,7 +957,7 @@ fn genUnnamedConst( }, .macho => { const macho_file = lf.cast(link.File.MachO).?; - const local = macho_file.getSymbol(local_sym_index); + const local = macho_file.getZigObject().?.symbols.items[local_sym_index]; return GenResult.mcv(.{ .load_symbol = local.nlist_idx }); }, .coff => { diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 9703890985..47b8eb2ce1 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -290,8 +290,10 @@ pub fn deinit(self: *MachO) void { self.dylibs.deinit(gpa); self.segments.deinit(gpa); - for (self.sections.items(.atoms)) |*list| { - list.deinit(gpa); + for (self.sections.items(.atoms), self.sections.items(.out), self.sections.items(.thunks)) |*atoms, *out, *thnks| { + atoms.deinit(gpa); + out.deinit(gpa); + thnks.deinit(gpa); } self.sections.deinit(gpa); @@ -561,8 +563,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n if (self.getZigObject()) |zo| { var has_resolve_error = false; - for (zo.atoms.items) |atom_index| { - const atom = self.getAtom(atom_index) orelse continue; + for (zo.getAtoms()) |atom_index| { + const atom = zo.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; const sect = &self.sections.items(.header)[atom.out_n_sect]; if (sect.isZerofill()) continue; @@ -573,7 +575,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n const atom_size = math.cast(usize, atom.size) orelse return error.Overflow; const code = try gpa.alloc(u8, atom_size); defer gpa.free(code); - atom.getData(self, code) catch |err| switch (err) { + zo.getAtomData(self, atom.*, code) catch |err| switch (err) { error.InputOutput => { try self.reportUnexpectedError("fetching code for '{s}' failed", .{ atom.getName(self), @@ -1524,7 +1526,7 @@ fn scanRelocs(self: *MachO) !void { try self.getFile(index).?.object.scanRelocs(self); } if (self.getInternalObject()) |obj| { - try obj.scanRelocs(self); + obj.scanRelocs(self); } try self.reportUndefs(); @@ -2394,7 +2396,7 @@ fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void { self.objc_stubs_sect_index, }) |maybe_sect_id| { if (maybe_sect_id) |sect_id| { - const out = &slice.items(.out)[sect_id]; + const out = slice.items(.out)[sect_id].items; try self.writeSyntheticSection(sect_id, out); } } @@ -3970,9 +3972,11 @@ pub const base_tag: link.File.Tag = link.File.Tag.macho; const Section = struct { header: macho.section_64, segment_id: u8, - atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, + atoms: std.ArrayListUnmanaged(Ref) = .{}, free_list: std.ArrayListUnmanaged(Atom.Index) = .{}, last_atom_index: Atom.Index = 0, + thunks: std.ArrayListUnmanaged(Thunk.Index) = .{}, + out: std.ArrayListUnmanaged(u8) = .{}, }; pub const LiteralPool = struct { @@ -4018,7 +4022,7 @@ pub const LiteralPool = struct { return .{ .found_existing = gop.found_existing, .index = @intCast(gop.index), - .atom = &lp.values.items[gop.index], + .ref = &lp.values.items[gop.index], }; } diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index 2ed86ef66f..b1736f7339 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -423,7 +423,6 @@ pub fn addReloc(self: *Atom, macho_file: *MachO, reloc: Relocation) !void { const gpa = macho_file.base.comp.gpa; const file = self.getFile(macho_file); assert(file == .zig_object); - assert(self.flags.relocs); var extra = self.getExtra(macho_file).?; const rels = &file.zig_object.relocs.items[extra.rel_index]; try rels.append(gpa, reloc); @@ -432,7 +431,6 @@ pub fn addReloc(self: *Atom, macho_file: *MachO, reloc: Relocation) !void { } pub fn freeRelocs(self: *Atom, macho_file: *MachO) void { - if (!self.flags.relocs) return; self.getFile(macho_file).zig_object.freeAtomRelocs(self.*, macho_file); var extra = self.getExtra(macho_file).?; extra.rel_count = 0; diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index f731f36b7e..dba3e4c1b1 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -6,9 +6,15 @@ index: File.Index, symtab: std.MultiArrayList(Nlist) = .{}, strtab: StringTable = .{}, -symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, -atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, -globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{}, +symbols: std.ArrayListUnmanaged(Symbol) = .{}, +symbols_extra: std.ArrayListUnmanaged(u32) = .{}, +globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{}, +/// Maps string index (so name) into nlist index for the global symbol defined within this +/// module. +globals_lookup: std.AutoHashMapUnmanaged(u32, u32) = .{}, +atoms: std.ArrayListUnmanaged(Atom) = .{}, +atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{}, +atoms_extra: std.ArrayListUnmanaged(u32) = .{}, /// Table of tracked LazySymbols. lazy_syms: LazySymbolTable = .{}, @@ -58,10 +64,13 @@ debug_info_header_dirty: bool = false, debug_line_header_dirty: bool = false, pub fn init(self: *ZigObject, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + const comp = macho_file.base.comp; const gpa = comp.gpa; - try self.atoms.append(gpa, 0); // null input section + try self.atoms.append(gpa, .{ .extra = try self.addAtomExtra(gpa, .{}) }); // null input section try self.strtab.buffer.append(gpa, 0); switch (comp.config.debug_format) { @@ -84,8 +93,12 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void { self.symtab.deinit(allocator); self.strtab.deinit(allocator); self.symbols.deinit(allocator); - self.atoms.deinit(allocator); + self.symbols_extra.deinit(allocator); + self.globals.deinit(allocator); self.globals_lookup.deinit(allocator); + self.atoms.deinit(allocator); + self.atoms_indexes.deinit(allocator); + self.atoms_extra.deinit(allocator); { var it = self.decls.iterator(); @@ -139,32 +152,21 @@ fn addNlist(self: *ZigObject, allocator: Allocator) !Symbol.Index { return index; } -pub fn addAtom(self: *ZigObject, macho_file: *MachO) !Symbol.Index { - const gpa = macho_file.base.comp.gpa; - const atom_index = try macho_file.addAtom(); - const symbol_index = try macho_file.addSymbol(); - const nlist_index = try self.addNlist(gpa); - - try self.atoms.append(gpa, atom_index); - try self.symbols.append(gpa, symbol_index); - - const atom = macho_file.getAtom(atom_index).?; - atom.file = self.index; - atom.atom_index = atom_index; - - const symbol = macho_file.getSymbol(symbol_index); - symbol.file = self.index; - symbol.atom = atom_index; - +pub fn createAtomForDecl(self: *ZigObject, allocator: Allocator, macho_file: *MachO) !Symbol.Index { + const atom_index = try self.addAtom(allocator); + const symbol_index = try self.addSymbol(allocator); + const nlist_index = try self.addNlist(allocator); self.symtab.items(.atom)[nlist_index] = atom_index; + try self.atoms_indexes.append(allocator, atom_index); + const symbol = &self.symbols.items[symbol_index]; + symbol.atom_ref = .{ .index = atom_index, .file = self.index }; symbol.nlist_idx = nlist_index; - + symbol.extra = try self.addSymbolExtra(allocator, .{}); const relocs_index = @as(u32, @intCast(self.relocs.items.len)); - const relocs = try self.relocs.addOne(gpa); + const relocs = try self.relocs.addOne(allocator); relocs.* = .{}; - try atom.addExtra(.{ .rel_index = relocs_index, .rel_count = 0 }, macho_file); - atom.flags.relocs = true; - + const atom = self.getAtom(atom_index).?; + atom.addExtra(.{ .rel_index = relocs_index, .rel_count = 0 }, macho_file); return symbol_index; } @@ -191,89 +193,51 @@ pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8 } pub fn getAtomRelocs(self: *ZigObject, atom: Atom, macho_file: *MachO) []const Relocation { - if (!atom.flags.relocs) return &[0]Relocation{}; - const extra = atom.getExtra(macho_file).?; + const extra = atom.getExtra(macho_file); const relocs = self.relocs.items[extra.rel_index]; return relocs.items[0..extra.rel_count]; } pub fn freeAtomRelocs(self: *ZigObject, atom: Atom, macho_file: *MachO) void { - if (atom.flags.relocs) { - const extra = atom.getExtra(macho_file).?; - self.relocs.items[extra.rel_index].clearRetainingCapacity(); - } + const extra = atom.getExtra(macho_file); + self.relocs.items[extra.rel_index].clearRetainingCapacity(); } -pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) void { +pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - for (self.symbols.items, 0..) |index, i| { - const nlist_idx = @as(Symbol.Index, @intCast(i)); - const nlist = self.symtab.items(.nlist)[nlist_idx]; - const atom_index = self.symtab.items(.atom)[nlist_idx]; + const gpa = macho_file.base.comp.gpa; + for (self.symtab.items(.nlist), self.symtab.items(.atom), self.globals.items, 0..) |nlist, atom_index, *global, i| { if (!nlist.ext()) continue; - if (nlist.undf() and !nlist.tentative()) continue; if (nlist.sect()) { - const atom = macho_file.getAtom(atom_index).?; + const atom = self.getAtom(atom_index).?; if (!atom.flags.alive) continue; } - const symbol = macho_file.getSymbol(index); + const gop = try macho_file.resolver.getOrPut(gpa, .{ + .index = @intCast(i), + .file = self.index, + }, macho_file); + if (!gop.found_existing) { + gop.ref.* = .{ .index = 0, .file = 0 }; + } + global.* = gop.index; + + if (nlist.undf() and !nlist.tentative()) continue; + if (gop.ref.getFile(macho_file) == null) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; + continue; + } + if (self.asFile().getSymbolRank(.{ .archive = false, .weak = nlist.weakDef(), .tentative = nlist.tentative(), - }) < symbol.getSymbolRank(macho_file)) { - const value = if (nlist.sect()) blk: { - const atom = macho_file.getAtom(atom_index).?; - break :blk nlist.n_value - atom.getInputAddress(macho_file); - } else nlist.n_value; - const out_n_sect = if (nlist.sect()) macho_file.getAtom(atom_index).?.out_n_sect else 0; - symbol.value = value; - symbol.atom = atom_index; - symbol.out_n_sect = out_n_sect; - symbol.nlist_idx = nlist_idx; - symbol.file = self.index; - symbol.flags.weak = nlist.weakDef(); - symbol.flags.abs = nlist.abs(); - symbol.flags.tentative = nlist.tentative(); - symbol.flags.weak_ref = false; - symbol.flags.dyn_ref = nlist.n_desc & macho.REFERENCED_DYNAMICALLY != 0; - symbol.flags.no_dead_strip = symbol.flags.no_dead_strip or nlist.noDeadStrip(); - // TODO: symbol.flags.interposable = macho_file.base.isDynLib() and macho_file.options.namespace == .flat and !nlist.pext(); - symbol.flags.interposable = false; - - if (nlist.sect() and - macho_file.sections.items(.header)[nlist.n_sect - 1].type() == macho.S_THREAD_LOCAL_VARIABLES) - { - symbol.flags.tlv = true; - } + }) < gop.ref.getSymbol(macho_file).?.getSymbolRank(macho_file)) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; } - - // Regardless of who the winner is, we still merge symbol visibility here. - if (nlist.pext() or (nlist.weakDef() and nlist.weakRef())) { - if (symbol.visibility != .global) { - symbol.visibility = .hidden; - } - } else { - symbol.visibility = .global; - } - } -} - -pub fn resetGlobals(self: *ZigObject, macho_file: *MachO) void { - for (self.symbols.items, 0..) |sym_index, nlist_idx| { - if (!self.symtab.items(.nlist)[nlist_idx].ext()) continue; - const sym = macho_file.getSymbol(sym_index); - const name = sym.name; - const global = sym.flags.global; - const weak_ref = sym.flags.weak_ref; - sym.* = .{}; - sym.name = name; - sym.flags.global = global; - sym.flags.weak_ref = weak_ref; } } @@ -281,12 +245,13 @@ pub fn markLive(self: *ZigObject, macho_file: *MachO) void { const tracy = trace(@src()); defer tracy.end(); - for (self.symbols.items, 0..) |index, nlist_idx| { - const nlist = self.symtab.items(.nlist)[nlist_idx]; + for (0..self.symbols.items.len) |i| { + const nlist = self.symtab.items(.nlist)[i]; if (!nlist.ext()) continue; - const sym = macho_file.getSymbol(index); - const file = sym.getFile(macho_file) orelse continue; + const ref = self.getSymbolRef(@intCast(i), macho_file); + const file = ref.getFile(macho_file) orelse continue; + const sym = ref.getSymbol(macho_file).?; const should_keep = nlist.undf() or (nlist.tentative() and !sym.flags.tentative); if (should_keep and file == .object and !file.object.alive) { file.object.alive = true; @@ -295,24 +260,41 @@ pub fn markLive(self: *ZigObject, macho_file: *MachO) void { } } -pub fn checkDuplicates(self: *ZigObject, dupes: anytype, macho_file: *MachO) !void { - for (self.symbols.items, 0..) |index, nlist_idx| { - const sym = macho_file.getSymbol(index); - if (sym.visibility != .global) continue; - const file = sym.getFile(macho_file) orelse continue; - if (file.getIndex() == self.index) continue; +pub fn mergeSymbolVisibility(self: *ZigObject, macho_file: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); - const nlist = self.symtab.items(.nlist)[nlist_idx]; - if (!nlist.undf() and !nlist.tentative() and !(nlist.weakDef() or nlist.pext())) { - const gop = try dupes.getOrPut(index); - if (!gop.found_existing) { - gop.value_ptr.* = .{}; - } - try gop.value_ptr.append(macho_file.base.comp.gpa, self.index); + for (self.symbols.items, 0..) |sym, i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + const global = ref.getSymbol(macho_file) orelse continue; + if (global.visibility != .global) { + global.visibility = sym.visibility; + } + if (sym.flags.weak_ref) { + global.flags.weak_ref = true; } } } +// TODO +// pub fn checkDuplicates(self: *ZigObject, dupes: anytype, macho_file: *MachO) !void { +// for (self.symbols.items, 0..) |index, nlist_idx| { +// const sym = macho_file.getSymbol(index); +// if (sym.visibility != .global) continue; +// const file = sym.getFile(macho_file) orelse continue; +// if (file.getIndex() == self.index) continue; + +// const nlist = self.symtab.items(.nlist)[nlist_idx]; +// if (!nlist.undf() and !nlist.tentative() and !(nlist.weakDef() or nlist.pext())) { +// const gop = try dupes.getOrPut(index); +// if (!gop.found_existing) { +// gop.value_ptr.* = .{}; +// } +// try gop.value_ptr.append(macho_file.base.comp.gpa, self.index); +// } +// } +// } + pub fn resolveLiterals(self: *ZigObject, lp: *MachO.LiteralPool, macho_file: *MachO) !void { _ = self; _ = lp; @@ -362,8 +344,8 @@ pub fn writeAr(self: ZigObject, ar_format: Archive.Format, writer: anytype) !voi } pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void { - for (self.atoms.items) |atom_index| { - const atom = macho_file.getAtom(atom_index) orelse continue; + for (self.getAtoms()) |atom_index| { + const atom = self.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; const sect = atom.getInputSection(macho_file); if (sect.isZerofill()) continue; @@ -371,46 +353,49 @@ pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void { } } -pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) !void { +pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) void { const tracy = trace(@src()); defer tracy.end(); - for (self.symbols.items) |sym_index| { - const sym = macho_file.getSymbol(sym_index); - const file = sym.getFile(macho_file) orelse continue; + for (self.symbols.items, 0..) |*sym, i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + const file = ref.getFile(macho_file) orelse continue; if (file.getIndex() != self.index) continue; if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue; sym.flags.output_symtab = true; if (sym.isLocal()) { - try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file); + sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file); self.output_symtab_ctx.nlocals += 1; } else if (sym.flags.@"export") { - try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file); + sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file); self.output_symtab_ctx.nexports += 1; } else { assert(sym.flags.import); - try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file); + sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file); self.output_symtab_ctx.nimports += 1; } self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1)); } } -pub fn writeSymtab(self: ZigObject, macho_file: *MachO, ctx: anytype) void { +pub fn writeSymtab(self: ZigObject, macho_file: *MachO) void { const tracy = trace(@src()); defer tracy.end(); - for (self.symbols.items) |sym_index| { - const sym = macho_file.getSymbol(sym_index); - const file = sym.getFile(macho_file) orelse continue; + var n_strx = self.output_symtab_ctx.stroff; + for (self.symbols.items, 0..) |sym, i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + const file = ref.getFile(macho_file) orelse continue; if (file.getIndex() != self.index) continue; const idx = sym.getOutputSymtabIndex(macho_file) orelse continue; - const n_strx = @as(u32, @intCast(ctx.strtab.items.len)); - ctx.strtab.appendSliceAssumeCapacity(sym.getName(macho_file)); - ctx.strtab.appendAssumeCapacity(0); - const out_sym = &ctx.symtab.items[idx]; + const out_sym = &macho_file.symtab.items[idx]; out_sym.n_strx = n_strx; sym.setOutputSym(macho_file, out_sym); + const name = sym.getName(macho_file); + @memcpy(macho_file.strtab.items[n_strx..][0..name.len], name); + n_strx += @intCast(name.len); + macho_file.strtab.items[n_strx] = 0; + n_strx += 1; } } @@ -523,9 +508,9 @@ pub fn getDeclVAddr( reloc_info: link.File.RelocInfo, ) !u64 { const sym_index = try self.getOrCreateMetadataForDecl(macho_file, decl_index); - const sym = macho_file.getSymbol(sym_index); + const sym = self.symbols.items[sym_index]; const vaddr = sym.getAddress(.{}, macho_file); - const parent_atom = macho_file.getSymbol(reloc_info.parent_atom_index).getAtom(macho_file).?; + const parent_atom = self.symbols.items[reloc_info.parent_atom_index].getAtom(macho_file).?; try parent_atom.addReloc(macho_file, .{ .tag = .@"extern", .offset = @intCast(reloc_info.offset), @@ -549,9 +534,9 @@ pub fn getAnonDeclVAddr( reloc_info: link.File.RelocInfo, ) !u64 { const sym_index = self.anon_decls.get(decl_val).?.symbol_index; - const sym = macho_file.getSymbol(sym_index); + const sym = self.symbols.items[sym_index]; const vaddr = sym.getAddress(.{}, macho_file); - const parent_atom = macho_file.getSymbol(reloc_info.parent_atom_index).getAtom(macho_file).?; + const parent_atom = self.symbols.items[reloc_info.parent_atom_index].getAtom(macho_file).?; try parent_atom.addReloc(macho_file, .{ .tag = .@"extern", .offset = @intCast(reloc_info.offset), @@ -584,7 +569,7 @@ pub fn lowerAnonDecl( else => explicit_alignment, }; if (self.anon_decls.get(decl_val)) |metadata| { - const existing_alignment = macho_file.getSymbol(metadata.symbol_index).getAtom(macho_file).?.alignment; + const existing_alignment = self.symbols.items[metadata.symbol_index].getAtom(macho_file).?.alignment; if (decl_alignment.order(existing_alignment).compare(.lte)) return .ok; } @@ -628,13 +613,10 @@ fn freeUnnamedConsts(self: *ZigObject, macho_file: *MachO, decl_index: InternPoo } fn freeDeclMetadata(self: *ZigObject, macho_file: *MachO, sym_index: Symbol.Index) void { - _ = self; - const gpa = macho_file.base.comp.gpa; - const sym = macho_file.getSymbol(sym_index); + const sym = self.symbols.items[sym_index]; sym.getAtom(macho_file).?.free(macho_file); log.debug("adding %{d} to local symbols free list", .{sym_index}); - macho_file.symbols_free_list.append(gpa, sym_index) catch {}; - macho_file.symbols.items[sym_index] = .{}; + // TODO redo this // TODO free GOT entry here } @@ -675,7 +657,7 @@ pub fn updateFunc( const sym_index = try self.getOrCreateMetadataForDecl(macho_file, decl_index); self.freeUnnamedConsts(macho_file, decl_index); - macho_file.getSymbol(sym_index).getAtom(macho_file).?.freeRelocs(macho_file); + self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file); var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); @@ -708,7 +690,7 @@ pub fn updateFunc( try self.updateDeclCode(macho_file, pt, decl_index, sym_index, sect_index, code); if (decl_state) |*ds| { - const sym = macho_file.getSymbol(sym_index); + const sym = self.symbols.items[sym_index]; try self.dwarf.?.commitDeclState( pt, decl_index, @@ -743,13 +725,13 @@ pub fn updateDecl( const name = decl.name.toSlice(&mod.intern_pool); const lib_name = variable.lib_name.toSlice(&mod.intern_pool); const index = try self.getGlobalSymbol(macho_file, name, lib_name); - const actual_index = self.symbols.items[index]; - macho_file.getSymbol(actual_index).flags.needs_got = true; + const sym = &self.symbols.items[index]; + sym.flags.needs_got = true; return; } const sym_index = try self.getOrCreateMetadataForDecl(macho_file, decl_index); - macho_file.getSymbol(sym_index).getAtom(macho_file).?.freeRelocs(macho_file); + self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file); const gpa = macho_file.base.comp.gpa; var code_buffer = std.ArrayList(u8).init(gpa); @@ -784,7 +766,7 @@ pub fn updateDecl( } if (decl_state) |*ds| { - const sym = macho_file.getSymbol(sym_index); + const sym = self.symbols.items[sym_index]; try self.dwarf.?.commitDeclState( pt, decl_index, @@ -816,7 +798,7 @@ fn updateDeclCode( const required_alignment = decl.getAlignment(pt); const sect = &macho_file.sections.items(.header)[sect_index]; - const sym = macho_file.getSymbol(sym_index); + const sym = &self.symbols.items[sym_index]; const nlist = &self.symtab.items(.nlist)[sym.nlist_idx]; const atom = sym.getAtom(macho_file).?; @@ -850,13 +832,13 @@ fn updateDeclCode( if (!macho_file.base.isRelocatable()) { log.debug(" (updating offset table entry)", .{}); assert(sym.flags.has_zig_got); - const extra = sym.getExtra(macho_file).?; + const extra = sym.getExtra(macho_file); try macho_file.zig_got.writeOne(macho_file, extra.zig_got); } } } else if (code.len < old_size) { atom.shrink(macho_file); - } else if (macho_file.getAtom(atom.next_index) == null) { + } else if (self.getAtom(atom.next_index) == null) { const needed_size = atom.value + code.len; sect.size = needed_size; } @@ -922,8 +904,8 @@ fn createTlvInitializer( const sym_name = try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{name}); defer gpa.free(sym_name); - const sym_index = try self.addAtom(macho_file); - const sym = macho_file.getSymbol(sym_index); + const sym_index = try self.createAtomForDecl(gpa, macho_file); + const sym = &self.symbols.items[sym_index]; const nlist = &self.symtab.items(.nlist)[sym.nlist_idx]; const atom = sym.getAtom(macho_file).?; @@ -945,7 +927,6 @@ fn createTlvInitializer( const slice = macho_file.sections.slice(); const header = slice.items(.header)[sect_index]; - const atoms = &slice.items(.atoms)[sect_index]; const gop = try self.tlv_initializers.getOrPut(gpa, atom.atom_index); assert(!gop.found_existing); // TODO incremental updates @@ -956,8 +937,6 @@ fn createTlvInitializer( gop.value_ptr.data = try gpa.dupe(u8, code); } - try atoms.append(gpa, atom.atom_index); - return sym_index; } @@ -970,7 +949,7 @@ fn createTlvDescriptor( ) !void { const gpa = macho_file.base.comp.gpa; - const sym = macho_file.getSymbol(sym_index); + const sym = &self.symbols.items[sym_index]; const nlist = &self.symtab.items(.nlist)[sym.nlist_idx]; const atom = sym.getAtom(macho_file).?; const alignment = Atom.Alignment.fromNonzeroByteUnits(@alignOf(u64)); @@ -1000,7 +979,7 @@ fn createTlvDescriptor( try atom.addReloc(macho_file, .{ .tag = .@"extern", .offset = 0, - .target = self.symbols.items[tlv_bootstrap_index], + .target = tlv_bootstrap_index, .addend = 0, .type = .unsigned, .meta = .{ @@ -1020,11 +999,14 @@ fn createTlvDescriptor( .pcrel = false, .has_subtractor = false, .length = 3, - .symbolnum = @intCast(macho_file.getSymbol(init_sym_index).nlist_idx), + .symbolnum = @intCast(init_sym_index), }, }); - try macho_file.sections.items(.atoms)[sect_index].append(gpa, atom.atom_index); + try macho_file.sections.items(.atoms)[sect_index].append(gpa, .{ + .index = atom.atom_index, + .file = self.index, + }); } fn getDeclOutputSection( @@ -1115,7 +1097,7 @@ pub fn lowerUnnamedConst( return error.CodegenFail; }, }; - const sym = macho_file.getSymbol(sym_index); + const sym = self.symbols.items[sym_index]; try unnamed_consts.append(gpa, sym.atom); return sym_index; } @@ -1140,7 +1122,7 @@ fn lowerConst( var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); - const sym_index = try self.addAtom(macho_file); + const sym_index = try self.createAtomForDecl(gpa, macho_file); const res = try codegen.generateSymbol(&macho_file.base, pt, src_loc, val, &code_buffer, .{ .none = {}, @@ -1152,7 +1134,7 @@ fn lowerConst( .fail => |em| return .{ .fail = em }, }; - const sym = macho_file.getSymbol(sym_index); + const sym = &self.symbols.items[sym_index]; const name_str_index = try self.strtab.insert(gpa, name); sym.name = name_str_index; sym.out_n_sect = output_section_index; @@ -1218,7 +1200,7 @@ pub fn updateExports( }, }; const sym_index = metadata.symbol_index; - const nlist_idx = macho_file.getSymbol(sym_index).nlist_idx; + const nlist_idx = self.symbols.items[sym_index].nlist_idx; const nlist = self.symtab.items(.nlist)[nlist_idx]; for (export_indices) |export_idx| { @@ -1322,7 +1304,7 @@ fn updateLazySymbol( .code => macho_file.zig_text_sect_index.?, .const_data => macho_file.zig_const_sect_index.?, }; - const sym = macho_file.getSymbol(symbol_index); + const sym = &self.symbols.items[symbol_index]; sym.name = name_str_index; sym.out_n_sect = output_section_index; @@ -1382,12 +1364,12 @@ pub fn deleteExport( const nlist = &self.symtab.items(.nlist)[nlist_index.*]; self.symtab.items(.size)[nlist_index.*] = 0; _ = self.globals_lookup.remove(nlist.n_strx); - const sym_index = macho_file.globals.get(nlist.n_strx).?; - const sym = macho_file.getSymbol(sym_index); - if (sym.file == self.index) { - _ = macho_file.globals.swapRemove(nlist.n_strx); - sym.* = .{}; - } + // TODO actually remove the export + // const sym_index = macho_file.globals.get(nlist.n_strx).?; + // const sym = &self.symbols.items[sym_index]; + // if (sym.file == self.index) { + // sym.* = .{}; + // } nlist.* = MachO.null_sym; } @@ -1404,9 +1386,11 @@ pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, l nlist.n_strx = off; nlist.n_type = macho.N_EXT; lookup_gop.value_ptr.* = nlist_index; - const global_name_off = try macho_file.strings.insert(gpa, sym_name); - const gop = try macho_file.getOrCreateGlobal(global_name_off); - try self.symbols.append(gpa, gop.index); + _ = try macho_file.resolver.getOrPut(gpa, .{ + .index = nlist_index, + .file = self.index, + }, macho_file); + try self.globals.append(gpa, nlist_index); } return lookup_gop.value_ptr.*; } @@ -1420,10 +1404,10 @@ pub fn getOrCreateMetadataForDecl( const gop = try self.decls.getOrPut(gpa, decl_index); if (!gop.found_existing) { const any_non_single_threaded = macho_file.base.comp.config.any_non_single_threaded; - const sym_index = try self.addAtom(macho_file); + const sym_index = try self.createAtomForDecl(gpa, macho_file); const mod = macho_file.base.comp.module.?; const decl = mod.declPtr(decl_index); - const sym = macho_file.getSymbol(sym_index); + const sym = &self.symbols.items[sym_index]; if (decl.getOwnedVariable(mod)) |variable| { if (variable.is_threadlocal and any_non_single_threaded) { sym.flags.tlv = true; @@ -1463,8 +1447,8 @@ pub fn getOrCreateMetadataForLazySymbol( }; switch (metadata.state.*) { .unused => { - const symbol_index = try self.addAtom(macho_file); - const sym = macho_file.getSymbol(symbol_index); + const symbol_index = try self.createAtomForDecl(gpa, macho_file); + const sym = &self.symbols.items[symbol_index]; sym.flags.needs_zig_got = true; metadata.symbol_index.* = symbol_index; }, @@ -1478,6 +1462,130 @@ pub fn getOrCreateMetadataForLazySymbol( return symbol_index; } +fn addAtom(self: *ZigObject, allocator: Allocator) !Atom.Index { + const atom_index: Atom.Index = @intCast(self.atoms.items.len); + const atom = try self.atoms.addOne(allocator); + atom.* = .{ + .file = self.index, + .atom_index = atom_index, + .extra = try self.addAtomExtra(allocator, .{}), + }; + return atom_index; +} + +pub fn getAtom(self: *ZigObject, atom_index: Atom.Index) ?*Atom { + if (atom_index == 0) return null; + assert(atom_index < self.atoms.items.len); + return &self.atoms.items[atom_index]; +} + +pub fn getAtoms(self: *ZigObject) []const Atom.Index { + return self.atoms_indexes.items; +} + +fn addAtomExtra(self: *ZigObject, allocator: Allocator, extra: Atom.Extra) !u32 { + const fields = @typeInfo(Atom.Extra).Struct.fields; + try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len); + return self.addAtomExtraAssumeCapacity(extra); +} + +fn addAtomExtraAssumeCapacity(self: *ZigObject, extra: Atom.Extra) u32 { + const index = @as(u32, @intCast(self.atoms_extra.items.len)); + const fields = @typeInfo(Atom.Extra).Struct.fields; + inline for (fields) |field| { + self.atoms_extra.appendAssumeCapacity(switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }); + } + return index; +} + +pub fn getAtomExtra(self: ZigObject, index: u32) Atom.Extra { + const fields = @typeInfo(Atom.Extra).Struct.fields; + var i: usize = index; + var result: Atom.Extra = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.type) { + u32 => self.atoms_extra.items[i], + else => @compileError("bad field type"), + }; + i += 1; + } + return result; +} + +pub fn setAtomExtra(self: *ZigObject, index: u32, extra: Atom.Extra) void { + assert(index > 0); + const fields = @typeInfo(Atom.Extra).Struct.fields; + inline for (fields, 0..) |field, i| { + self.atoms_extra.items[index + i] = switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }; + } +} + +fn addSymbol(self: *ZigObject, allocator: Allocator) !Symbol.Index { + try self.symbols.ensureUnusedCapacity(allocator, 1); + return self.addSymbolAssumeCapacity(); +} + +fn addSymbolAssumeCapacity(self: *ZigObject) Symbol.Index { + const index: Symbol.Index = @intCast(self.symbols.items.len); + const symbol = self.symbols.addOneAssumeCapacity(); + symbol.* = .{ .file = self.index }; + return index; +} + +pub fn getSymbolRef(self: ZigObject, index: Symbol.Index, macho_file: *MachO) MachO.Ref { + const global_index = self.globals.items[index]; + if (macho_file.resolver.get(global_index)) |ref| return ref; + return .{ .index = index, .file = self.index }; +} + +pub fn addSymbolExtra(self: *ZigObject, allocator: Allocator, extra: Symbol.Extra) !u32 { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len); + return self.addSymbolExtraAssumeCapacity(extra); +} + +fn addSymbolExtraAssumeCapacity(self: *ZigObject, extra: Symbol.Extra) u32 { + const index = @as(u32, @intCast(self.symbols_extra.items.len)); + const fields = @typeInfo(Symbol.Extra).Struct.fields; + inline for (fields) |field| { + self.symbols_extra.appendAssumeCapacity(switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }); + } + return index; +} + +pub fn getSymbolExtra(self: ZigObject, index: u32) Symbol.Extra { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + var i: usize = index; + var result: Symbol.Extra = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.type) { + u32 => self.symbols_extra.items[i], + else => @compileError("bad field type"), + }; + i += 1; + } + return result; +} + +pub fn setSymbolExtra(self: *ZigObject, index: u32, extra: Symbol.Extra) void { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + inline for (fields, 0..) |field, i| { + self.symbols_extra.items[index + i] = switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }; + } +} + pub fn asFile(self: *ZigObject) File { return .{ .zig_object = self }; } @@ -1503,9 +1611,16 @@ fn formatSymtab( _ = unused_fmt_string; _ = options; try writer.writeAll(" symbols\n"); - for (ctx.self.symbols.items) |index| { - const sym = ctx.macho_file.getSymbol(index); - try writer.print(" {}\n", .{sym.fmt(ctx.macho_file)}); + const self = ctx.self; + const macho_file = ctx.macho_file; + for (self.symbols.items, 0) |sym, i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + if (ref.getFile(macho_file) == null) { + // TODO any better way of handling this? + try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); + } else { + try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); + } } } @@ -1524,10 +1639,12 @@ fn formatAtoms( ) !void { _ = unused_fmt_string; _ = options; + const self = ctx.self; + const macho_file = ctx.macho_file; try writer.writeAll(" atoms\n"); - for (ctx.self.atoms.items) |atom_index| { - const atom = ctx.macho_file.getAtom(atom_index) orelse continue; - try writer.print(" {}\n", .{atom.fmt(ctx.macho_file)}); + for (self.getAtoms()) |atom_index| { + const atom = self.getAtom(atom_index) orelse continue; + try writer.print(" {}\n", .{atom.fmt(macho_file)}); } } From 174de37cefde3b22b02fee5982e7bacead4d200f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 10 Jul 2024 16:36:32 +0200 Subject: [PATCH 16/45] macho: fix compile errors --- src/arch/x86_64/CodeGen.zig | 10 +- src/arch/x86_64/Emit.zig | 2 +- src/link/MachO.zig | 65 +++---- src/link/MachO/Atom.zig | 34 ++-- src/link/MachO/DebugSymbols.zig | 12 +- src/link/MachO/Dylib.zig | 24 ++- src/link/MachO/InternalObject.zig | 38 ++--- src/link/MachO/Object.zig | 254 +++++++++++++++------------- src/link/MachO/Symbol.zig | 4 +- src/link/MachO/UnwindInfo.zig | 4 +- src/link/MachO/ZigObject.zig | 40 ++++- src/link/MachO/dyld_info/Rebase.zig | 10 +- src/link/MachO/dyld_info/Trie.zig | 46 +++-- src/link/MachO/dyld_info/bind.zig | 52 +++--- src/link/MachO/file.zig | 22 +-- src/link/MachO/synthetic.zig | 10 +- src/link/MachO/thunks.zig | 6 +- 17 files changed, 352 insertions(+), 281 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 1b8d84aa23..8343dec3bd 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -12362,8 +12362,9 @@ fn genCall(self: *Self, info: union(enum) { try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }, .{}); try self.asmRegister(.{ ._, .call }, .rax); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const sym_index = try macho_file.getZigObject().?.getOrCreateMetadataForDecl(macho_file, func.owner_decl); - const sym = macho_file.getSymbol(sym_index); + const zo = macho_file.getZigObject().?; + const sym_index = try zo.getOrCreateMetadataForDecl(macho_file, func.owner_decl); + const sym = zo.symbols.items[sym_index]; try self.genSetReg( .rax, Type.usize, @@ -15396,9 +15397,10 @@ fn genLazySymbolRef( else => unreachable, } } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const sym_index = macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, pt, lazy_sym) catch |err| + const zo = macho_file.getZigObject().?; + const sym_index = zo.getOrCreateMetadataForLazySymbol(macho_file, pt, lazy_sym) catch |err| return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const sym = macho_file.getSymbol(sym_index); + const sym = zo.symbols.items[sym_index]; switch (tag) { .lea, .call => try self.genSetReg( reg, diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 2bfb4d6704..9641268a7d 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -162,7 +162,7 @@ pub fn emitMir(emit: *Emit) Error!void { }; const zo = macho_file.getZigObject().?; const atom = zo.symbols.items[data.atom_index].getAtom(macho_file).?; - const sym = zo.symbols.items[data.sym_index]; + const sym = &zo.symbols.items[data.sym_index]; if (sym.flags.needs_zig_got and !is_obj_or_static_lib) { _ = try sym.getOrCreateZigGotEntry(data.sym_index, macho_file); } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 47b8eb2ce1..a00c156b35 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -529,7 +529,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n dylib.ordinal = @intCast(ord); } - try self.claimUnresolved(); + self.claimUnresolved(); self.scanRelocs() catch |err| switch (err) { error.HasUndefinedSymbols => return error.FlushFailure, @@ -603,7 +603,12 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n if (has_resolve_error) return error.FlushFailure; } - try self.writeSectionsAndUpdateLinkeditSizes(); + self.writeSectionsAndUpdateLinkeditSizes() catch |err| { + switch (err) { + error.ResolveFailed => return error.FlushFailure, + else => |e| return e, + } + }; try self.writeSectionsToFile(); try self.allocateLinkeditSegment(); @@ -1450,12 +1455,12 @@ pub fn dedupLiterals(self: *MachO) !void { } } -fn claimUnresolved(self: *MachO) error{OutOfMemory}!void { +fn claimUnresolved(self: *MachO) void { if (self.getZigObject()) |zo| { - try zo.asFile().claimUnresolved(self); + zo.claimUnresolved(self); } for (self.objects.items) |index| { - try self.getFile(index).?.claimUnresolved(self); + self.getFile(index).?.object.claimUnresolved(self); } } @@ -2402,7 +2407,7 @@ fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void { } if (self.la_symbol_ptr_sect_index) |_| { - try self.updatelazyBindSize(); + try self.updateLazyBindSize(); } try self.rebase.updateSize(self); @@ -2412,16 +2417,16 @@ fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void { try self.data_in_code.updateSize(self); if (self.getZigObject()) |zo| { - zo.asFile().writeSymtab(self); + zo.asFile().writeSymtab(self, self); } for (self.objects.items) |index| { - self.getFile(index).?.writeSymtab(self); + self.getFile(index).?.writeSymtab(self, self); } for (self.dylibs.items) |index| { - self.getFile(index).?.writeSymtab(self); + self.getFile(index).?.writeSymtab(self, self); } if (self.getInternalObject()) |obj| { - obj.asFile().writeSymtab(self); + obj.asFile().writeSymtab(self, self); } } @@ -2484,7 +2489,7 @@ fn writeSectionsToFile(self: *MachO) !void { const slice = self.sections.slice(); for (slice.items(.header), slice.items(.out)) |header, out| { - try self.base.file.pwriteAll(out.items, header.offset); + try self.base.file.?.pwriteAll(out.items, header.offset); } } @@ -2501,7 +2506,7 @@ fn writeDyldInfo(self: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const base_off = self.getLinkeditSegment().fileoff; const cmd = self.dyld_info_cmd; var needed_size: u32 = 0; @@ -2527,14 +2532,14 @@ fn writeDyldInfo(self: *MachO) !void { try self.lazy_bind.write(writer); try stream.seekTo(cmd.export_off - base_off); try self.export_trie.write(writer); - try self.base.file.pwriteAll(buffer, cmd.rebase_off); + try self.base.file.?.pwriteAll(buffer, cmd.rebase_off); } pub fn writeDataInCode(self: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); const cmd = self.data_in_code_cmd; - try self.base.file.pwriteAll(mem.sliceAsBytes(self.data_in_code.entries.items), cmd.dataoff); + try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.data_in_code.entries.items), cmd.dataoff); } fn writeIndsymtab(self: *MachO) !void { @@ -2546,15 +2551,15 @@ fn writeIndsymtab(self: *MachO) !void { var buffer = try std.ArrayList(u8).initCapacity(gpa, needed_size); defer buffer.deinit(); try self.indsymtab.write(self, buffer.writer()); - try self.base.file.pwriteAll(buffer.items, cmd.indirectsymoff); + try self.base.file.?.pwriteAll(buffer.items, cmd.indirectsymoff); } pub fn writeSymtabToFile(self: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); const cmd = self.symtab_cmd; - try self.base.file.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff); - try self.base.file.pwriteAll(self.strtab.items, cmd.stroff); + try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff); + try self.base.file.?.pwriteAll(self.strtab.items, cmd.stroff); } fn writeUnwindInfo(self: *MachO) !void { @@ -2687,18 +2692,20 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } { try load_commands.writeDylinkerLC(writer); ncmds += 1; - if (self.entry_index) |global_index| { - const sym = self.getSymbol(global_index); - const seg = self.getTextSegment(); - const entryoff: u32 = if (sym.getFile(self) == null) - 0 - else - @as(u32, @intCast(sym.getAddress(.{ .stubs = true }, self) - seg.vmaddr)); - try writer.writeStruct(macho.entry_point_command{ - .entryoff = entryoff, - .stacksize = self.base.stack_size, - }); - ncmds += 1; + if (self.getInternalObject()) |obj| { + if (obj.getEntryRef(self)) |ref| { + const sym = ref.getSymbol(self).?; + const seg = self.getTextSegment(); + const entryoff: u32 = if (sym.getFile(self) == null) + 0 + else + @as(u32, @intCast(sym.getAddress(.{ .stubs = true }, self) - seg.vmaddr)); + try writer.writeStruct(macho.entry_point_command{ + .entryoff = entryoff, + .stacksize = self.base.stack_size, + }); + ncmds += 1; + } } if (self.base.isDynLib()) { diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index b1736f7339..d03727aa6a 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -213,7 +213,8 @@ pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 { /// File offset relocation happens transparently, so it is not included in /// this calculation. pub fn capacity(self: Atom, macho_file: *MachO) u64 { - const next_addr = if (macho_file.getAtom(self.next_index)) |next| + const zo = macho_file.getZigObject().?; + const next_addr = if (zo.getAtom(self.next_index)) |next| next.getAddress(macho_file) else std.math.maxInt(u32); @@ -222,7 +223,8 @@ pub fn capacity(self: Atom, macho_file: *MachO) u64 { pub fn freeListEligible(self: Atom, macho_file: *MachO) bool { // No need to keep a free list node for the last block. - const next = macho_file.getAtom(self.next_index) orelse return false; + const zo = macho_file.getZigObject().?; + const next = zo.getAtom(self.next_index) orelse return false; const cap = next.getAddress(macho_file) - self.getAddress(macho_file); const ideal_cap = MachO.padToIdeal(self.size); if (cap <= ideal_cap) return false; @@ -231,6 +233,7 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool { } pub fn allocate(self: *Atom, macho_file: *MachO) !void { + const zo = macho_file.getZigObject().?; const sect = &macho_file.sections.items(.header)[self.out_n_sect]; const free_list = &macho_file.sections.items(.free_list)[self.out_n_sect]; const last_atom_index = &macho_file.sections.items(.last_atom_index)[self.out_n_sect]; @@ -250,7 +253,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void { var i: usize = free_list.items.len; while (i < free_list.items.len) { const big_atom_index = free_list.items[i]; - const big_atom = macho_file.getAtom(big_atom_index).?; + const big_atom = zo.getAtom(big_atom_index).?; // We now have a pointer to a live atom that has too much capacity. // Is it enough that we could fit this new atom? const cap = big_atom.capacity(macho_file); @@ -282,7 +285,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void { free_list_removal = i; } break :blk new_start_vaddr; - } else if (macho_file.getAtom(last_atom_index.*)) |last| { + } else if (zo.getAtom(last_atom_index.*)) |last| { const ideal_capacity = MachO.padToIdeal(last.size); const ideal_capacity_end_vaddr = last.value + ideal_capacity; const new_start_vaddr = self.alignment.forward(ideal_capacity_end_vaddr); @@ -302,7 +305,7 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void { }); const expand_section = if (atom_placement) |placement_index| - macho_file.getAtom(placement_index).?.next_index == 0 + zo.getAtom(placement_index).?.next_index == 0 else true; if (expand_section) { @@ -327,15 +330,15 @@ pub fn allocate(self: *Atom, macho_file: *MachO) !void { // This function can also reallocate an atom. // In this case we need to "unplug" it from its previous location before // plugging it in to its new location. - if (macho_file.getAtom(self.prev_index)) |prev| { + if (zo.getAtom(self.prev_index)) |prev| { prev.next_index = self.next_index; } - if (macho_file.getAtom(self.next_index)) |next| { + if (zo.getAtom(self.next_index)) |next| { next.prev_index = self.prev_index; } if (atom_placement) |big_atom_index| { - const big_atom = macho_file.getAtom(big_atom_index).?; + const big_atom = zo.getAtom(big_atom_index).?; self.prev_index = big_atom_index; self.next_index = big_atom.next_index; big_atom.next_index = self.atom_index; @@ -365,6 +368,7 @@ pub fn free(self: *Atom, macho_file: *MachO) void { const comp = macho_file.base.comp; const gpa = comp.gpa; + const zo = macho_file.getZigObject().?; const free_list = &macho_file.sections.items(.free_list)[self.out_n_sect]; const last_atom_index = &macho_file.sections.items(.last_atom_index)[self.out_n_sect]; var already_have_free_list_node = false; @@ -383,9 +387,9 @@ pub fn free(self: *Atom, macho_file: *MachO) void { } } - if (macho_file.getAtom(last_atom_index.*)) |last_atom| { + if (zo.getAtom(last_atom_index.*)) |last_atom| { if (last_atom.atom_index == self.atom_index) { - if (macho_file.getAtom(self.prev_index)) |_| { + if (zo.getAtom(self.prev_index)) |_| { // TODO shrink the section size here last_atom_index.* = self.prev_index; } else { @@ -394,7 +398,7 @@ pub fn free(self: *Atom, macho_file: *MachO) void { } } - if (macho_file.getAtom(self.prev_index)) |prev| { + if (zo.getAtom(self.prev_index)) |prev| { prev.next_index = self.next_index; if (!already_have_free_list_node and prev.*.freeListEligible(macho_file)) { // The free list is heuristics, it doesn't have to be perfect, so we can @@ -405,7 +409,7 @@ pub fn free(self: *Atom, macho_file: *MachO) void { self.prev_index = 0; } - if (macho_file.getAtom(self.next_index)) |next| { + if (zo.getAtom(self.next_index)) |next| { next.prev_index = self.prev_index; } else { self.next_index = 0; @@ -423,7 +427,7 @@ pub fn addReloc(self: *Atom, macho_file: *MachO, reloc: Relocation) !void { const gpa = macho_file.base.comp.gpa; const file = self.getFile(macho_file); assert(file == .zig_object); - var extra = self.getExtra(macho_file).?; + var extra = self.getExtra(macho_file); const rels = &file.zig_object.relocs.items[extra.rel_index]; try rels.append(gpa, reloc); extra.rel_count += 1; @@ -432,7 +436,7 @@ pub fn addReloc(self: *Atom, macho_file: *MachO, reloc: Relocation) !void { pub fn freeRelocs(self: *Atom, macho_file: *MachO) void { self.getFile(macho_file).zig_object.freeAtomRelocs(self.*, macho_file); - var extra = self.getExtra(macho_file).?; + var extra = self.getExtra(macho_file); extra.rel_count = 0; self.setExtra(extra, macho_file); } @@ -630,7 +634,7 @@ fn resolveRelocInner( const TLS = @as(i64, @intCast(macho_file.getTlsAddress())); const SUB = if (subtractor) |sub| @as(i64, @intCast(sub.getTargetAddress(self, macho_file))) else 0; // Address of the __got_zig table entry if any. - const ZIG_GOT = @as(i64, @intCast(rel.getZigGotTargetAddress(self, macho_file))); + const ZIG_GOT = @as(i64, @intCast(rel.getZigGotTargetAddress(macho_file))); const divExact = struct { fn divExact(atom: Atom, r: Relocation, num: u12, den: u12, ctx: *MachO) !u12 { diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index c022a30664..52061f325b 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -175,8 +175,9 @@ fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64 } pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void { + const zo = macho_file.getZigObject().?; for (self.relocs.items) |*reloc| { - const sym = macho_file.getSymbol(reloc.target); + const sym = zo.symbols.items[reloc.target]; const sym_name = sym.getName(macho_file); const addr = switch (reloc.type) { .direct_load => sym.getAddress(.{}, macho_file), @@ -382,23 +383,22 @@ pub fn writeSymtab(self: *DebugSymbols, off: u32, macho_file: *MachO) !u32 { cmd.symoff = off; try self.symtab.resize(gpa, cmd.nsyms); - try self.strtab.ensureUnusedCapacity(gpa, cmd.strsize - 1); + try self.strtab.resize(gpa, cmd.strsize); + self.strtab.items[0] = 0; if (macho_file.getZigObject()) |zo| { zo.writeSymtab(macho_file, self); } for (macho_file.objects.items) |index| { - try macho_file.getFile(index).?.writeSymtab(macho_file, self); + macho_file.getFile(index).?.writeSymtab(macho_file, self); } for (macho_file.dylibs.items) |index| { - try macho_file.getFile(index).?.writeSymtab(macho_file, self); + macho_file.getFile(index).?.writeSymtab(macho_file, self); } if (macho_file.getInternalObject()) |internal| { internal.writeSymtab(macho_file, self); } - assert(self.strtab.items.len == cmd.strsize); - try self.file.pwriteAll(mem.sliceAsBytes(self.symtab.items), cmd.symoff); return off + cmd.nsyms * @sizeOf(macho.nlist_64); diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 6bb3056a61..9909279190 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -6,7 +6,7 @@ strtab: std.ArrayListUnmanaged(u8) = .{}, id: ?Id = null, ordinal: u16 = 0, -symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, +symbols: std.ArrayListUnmanaged(Symbol) = .{}, symbols_extra: std.ArrayListUnmanaged(u32) = .{}, globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{}, dependents: std.ArrayListUnmanaged(Id) = .{}, @@ -516,7 +516,7 @@ pub fn initSymbols(self: *Dylib, macho_file: *MachO) !void { } } -pub fn resolveSymbols(self: *Dylib, macho_file: *MachO) void { +pub fn resolveSymbols(self: *Dylib, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); @@ -584,7 +584,7 @@ pub fn calcSymtabSize(self: *Dylib, macho_file: *MachO) void { } } -pub fn writeSymtab(self: Dylib, macho_file: *MachO) void { +pub fn writeSymtab(self: Dylib, macho_file: *MachO, ctx: anytype) void { const tracy = trace(@src()); defer tracy.end(); @@ -594,13 +594,13 @@ pub fn writeSymtab(self: Dylib, macho_file: *MachO) void { const file = ref.getFile(macho_file) orelse continue; if (file.getIndex() != self.index) continue; const idx = sym.getOutputSymtabIndex(macho_file) orelse continue; - const out_sym = &macho_file.symtab.items[idx]; + const out_sym = &ctx.symtab.items[idx]; out_sym.n_strx = n_strx; sym.setOutputSym(macho_file, out_sym); const name = sym.getName(macho_file); - @memcpy(macho_file.strtab.items[n_strx..][0..name.len], name); + @memcpy(ctx.strtab.items[n_strx..][0..name.len], name); n_strx += @intCast(name.len); - macho_file.strtab.items[n_strx] = 0; + ctx.strtab.items[n_strx] = 0; n_strx += 1; } } @@ -718,10 +718,16 @@ fn formatSymtab( _ = unused_fmt_string; _ = options; const dylib = ctx.dylib; + const macho_file = ctx.macho_file; try writer.writeAll(" globals\n"); - for (dylib.symbols.items) |index| { - const global = ctx.macho_file.getSymbol(index); - try writer.print(" {}\n", .{global.fmt(ctx.macho_file)}); + for (dylib.symbols.items, 0..) |sym, i| { + const ref = dylib.getSymbolRef(@intCast(i), macho_file); + if (ref.getFile(macho_file) == null) { + // TODO any better way of handling this? + try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)}); + } else { + try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)}); + } } } diff --git a/src/link/MachO/InternalObject.zig b/src/link/MachO/InternalObject.zig index 32e89116b1..9578bc84ec 100644 --- a/src/link/MachO/InternalObject.zig +++ b/src/link/MachO/InternalObject.zig @@ -16,7 +16,7 @@ objc_selrefs: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64), force_undefined: std.ArrayListUnmanaged(Symbol.Index) = .{}, entry_index: ?Symbol.Index = null, dyld_stub_binder_index: ?Symbol.Index = null, -dyld_private: ?Symbol.Index = null, +dyld_private_index: ?Symbol.Index = null, objc_msg_send_index: ?Symbol.Index = null, mh_execute_header_index: ?Symbol.Index = null, mh_dylib_header_index: ?Symbol.Index = null, @@ -206,7 +206,7 @@ pub fn resolveBoundarySymbols(self: *InternalObject, macho_file: *MachO) !void { const nlist_idx: u32 = @intCast(self.symtab.items.len); const nlist = self.symtab.addOneAssumeCapacity(); nlist.* = .{ - .n_strx = name_off.pos, + .n_strx = name_off, .n_type = macho.N_SECT, .n_sect = 0, .n_desc = 0, @@ -273,7 +273,7 @@ fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_fil const nlist_idx: u32 = @intCast(self.symtab.items.len); const nlist = try self.symtab.addOne(gpa); nlist.* = .{ - .n_strx = name_str.pos, + .n_strx = name_str, .n_type = macho.N_SECT, .n_sect = @intCast(n_sect + 1), .n_desc = 0, @@ -286,12 +286,12 @@ fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_fil } fn addObjcSelrefsSection(self: *InternalObject, methname_sym_index: Symbol.Index, macho_file: *MachO) !Symbol.Index { - const gpa = macho_file.base.allocator; + const gpa = macho_file.base.comp.gpa; const atom_index = try self.addAtom(gpa); try self.atoms_indexes.append(gpa, atom_index); const atom = self.getAtom(atom_index).?; atom.size = @sizeOf(u64); - atom.alignment = 3; + atom.alignment = .@"8"; const n_sect = try self.addSection(gpa, "__DATA", "__objc_selrefs"); const sect = &self.sections.items(.header)[n_sect]; @@ -389,7 +389,7 @@ pub fn resolveObjcMsgSendSymbols(self: *InternalObject, macho_file: *MachO) !voi }; sym.nlist_idx = nlist_idx; sym.extra = try self.addSymbolExtra(gpa, .{ .objc_selrefs = selrefs_index }); - sym.setSectionFlags(.{ .objc_stubs = true }); + sym.flags.objc_stubs = true; const idx = ref.getFile(macho_file).?.object.globals.items[ref.index]; try self.globals.append(gpa, idx); @@ -417,7 +417,7 @@ pub fn resolveLiterals(self: *InternalObject, lp: *MachO.LiteralPool, macho_file const target = rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?; try buffer.ensureUnusedCapacity(target.size); buffer.resize(target.size) catch unreachable; - @memcpy(buffer.items, self.getSectionData(target.n_sect)); + @memcpy(buffer.items, try self.getSectionData(target.n_sect)); const res = try lp.insert(gpa, header.type(), buffer.items); buffer.clearRetainingCapacity(); if (!res.found_existing) { @@ -425,7 +425,7 @@ pub fn resolveLiterals(self: *InternalObject, lp: *MachO.LiteralPool, macho_file } else { const lp_sym = lp.getSymbol(res.index, macho_file); const lp_atom = lp_sym.getAtom(macho_file).?; - lp_atom.alignment = @max(lp_atom.alignment, atom.alignment); + lp_atom.alignment = lp_atom.alignment.max(atom.alignment); atom.flags.alive = false; } atom.addExtra(.{ .literal_pool_index = res.index }, macho_file); @@ -438,7 +438,7 @@ pub fn dedupLiterals(self: *InternalObject, lp: MachO.LiteralPool, macho_file: * for (self.getAtoms()) |atom_index| { const atom = self.getAtom(atom_index) orelse continue; - if (!atom.alive.load(.seq_cst)) continue; + if (!atom.flags.alive) continue; const relocs = blk: { const extra = atom.getExtra(macho_file); @@ -463,7 +463,7 @@ pub fn dedupLiterals(self: *InternalObject, lp: MachO.LiteralPool, macho_file: * } for (self.symbols.items) |*sym| { - if (!sym.getSectionFlags().objc_stubs) continue; + if (!sym.flags.objc_stubs) continue; const extra = sym.getExtra(macho_file); const file = sym.getFile(macho_file).?; if (file.getIndex() != self.index) continue; @@ -495,14 +495,14 @@ pub fn scanRelocs(self: *InternalObject, macho_file: *MachO) void { if (self.getDyldStubBinderRef(macho_file)) |ref| { if (ref.getFile(macho_file) != null) { const sym = ref.getSymbol(macho_file).?; - sym.flags.got = true; + sym.flags.needs_got = true; } } if (self.getObjcMsgSendRef(macho_file)) |ref| { if (ref.getFile(macho_file) != null) { const sym = ref.getSymbol(macho_file).?; // TODO is it always needed, or only if we are synthesising fast stubs - sym.flags.got = true; + sym.flags.needs_got = true; } } } @@ -569,30 +569,30 @@ pub fn writeAtoms(self: *InternalObject, macho_file: *MachO) !void { for (self.getAtoms()) |atom_index| { const atom = self.getAtom(atom_index) orelse continue; - if (!atom.alive.load(.seq_cst)) continue; + if (!atom.flags.alive) continue; const sect = atom.getInputSection(macho_file); if (sect.isZerofill()) continue; const off = atom.value; const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items[off..][0..atom.size]; - @memcpy(buffer, self.getSectionData(atom.n_sect)); + @memcpy(buffer, try self.getSectionData(atom.n_sect)); try atom.resolveRelocs(macho_file, buffer); } } -pub fn writeSymtab(self: InternalObject, macho_file: *MachO) void { +pub fn writeSymtab(self: InternalObject, macho_file: *MachO, ctx: anytype) void { var n_strx = self.output_symtab_ctx.stroff; for (self.symbols.items, 0..) |sym, i| { const ref = self.getSymbolRef(@intCast(i), macho_file); const file = ref.getFile(macho_file) orelse continue; if (file.getIndex() != self.index) continue; const idx = sym.getOutputSymtabIndex(macho_file) orelse continue; - const out_sym = &macho_file.symtab.items[idx]; + const out_sym = &ctx.symtab.items[idx]; out_sym.n_strx = n_strx; sym.setOutputSym(macho_file, out_sym); const name = sym.getName(macho_file); - @memcpy(macho_file.strtab.items[n_strx..][0..name.len], name); + @memcpy(ctx.strtab.items[n_strx..][0..name.len], name); n_strx += @intCast(name.len); - macho_file.strtab.items[n_strx] = 0; + ctx.strtab.items[n_strx] = 0; n_strx += 1; } } @@ -640,7 +640,7 @@ pub fn asFile(self: *InternalObject) File { } pub fn getAtomRelocs(self: *const InternalObject, atom: Atom, macho_file: *MachO) []const Relocation { - const extra = atom.getExtra(macho_file).?; + const extra = atom.getExtra(macho_file); const relocs = self.sections.items(.relocs)[atom.n_sect]; return relocs.items[extra.rel_index..][0..extra.rel_count]; } diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index a668b932d9..fe99df22bd 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -11,10 +11,10 @@ sections: std.MultiArrayList(Section) = .{}, symtab: std.MultiArrayList(Nlist) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, -symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, +symbols: std.ArrayListUnmanaged(Symbol) = .{}, symbols_extra: std.ArrayListUnmanaged(u32) = .{}, globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{}, -atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, +atoms: std.ArrayListUnmanaged(Atom) = .{}, atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{}, atoms_extra: std.ArrayListUnmanaged(u32) = .{}, @@ -228,7 +228,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { // Parse DWARF __TEXT,__eh_frame section if (self.eh_frame_sect_index) |index| { - try self.initEhFrameRecords(index, macho_file); + try self.initEhFrameRecords(gpa, index, handle, macho_file); } // Parse Apple's __LD,__compact_unwind section @@ -261,7 +261,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { try self.parseDebugInfo(macho_file); - for (self.atoms.items) |atom_index| { + for (self.getAtoms()) |atom_index| { const atom = self.getAtom(atom_index) orelse continue; const isec = atom.getInputSection(macho_file); if (mem.eql(u8, isec.sectName(), "__eh_frame") or @@ -606,7 +606,7 @@ fn initPointerLiterals(self: *Object, allocator: Allocator, macho_file: *MachO) } } -pub fn resolveLiterals(self: Object, lp: *MachO.LiteralPool, macho_file: *MachO) !void { +pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); @@ -646,7 +646,7 @@ pub fn resolveLiterals(self: Object, lp: *MachO.LiteralPool, macho_file: *MachO) } else { const lp_sym = lp.getSymbol(res.index, macho_file); const lp_atom = lp_sym.getAtom(macho_file).?; - lp_atom.alignment = @max(lp_atom.alignment, atom.alignment); + lp_atom.alignment = lp_atom.alignment.max(atom.alignment); atom.flags.alive = false; } atom.addExtra(.{ .literal_pool_index = res.index }, macho_file); @@ -684,7 +684,7 @@ pub fn resolveLiterals(self: Object, lp: *MachO.LiteralPool, macho_file: *MachO) } else { const lp_sym = lp.getSymbol(res.index, macho_file); const lp_atom = lp_sym.getAtom(macho_file).?; - lp_atom.alignment = @max(lp_atom.alignment, atom.alignment); + lp_atom.alignment = lp_atom.alignment.max(atom.alignment); atom.flags.alive = false; } atom.addExtra(.{ .literal_pool_index = res.index }, macho_file); @@ -823,7 +823,7 @@ fn initSymbols(self: *Object, allocator: Allocator, macho_file: *MachO) !void { const index = self.addSymbolAssumeCapacity(); const symbol = &self.symbols.items[index]; symbol.value = nlist.n_value; - symbol.name = .{ .pos = nlist.n_strx, .len = @intCast(self.getNStrx(nlist.n_strx).len + 1) }; + symbol.name = nlist.n_strx; symbol.nlist_idx = @intCast(i); symbol.extra = self.addSymbolExtraAssumeCapacity(.{}); @@ -838,7 +838,9 @@ fn initSymbols(self: *Object, allocator: Allocator, macho_file: *MachO) !void { symbol.flags.tentative = nlist.tentative(); symbol.flags.no_dead_strip = symbol.flags.no_dead_strip or nlist.noDeadStrip(); symbol.flags.dyn_ref = nlist.n_desc & macho.REFERENCED_DYNAMICALLY != 0; - symbol.flags.interposable = nlist.ext() and (nlist.sect() or nlist.abs()) and macho_file.options.dylib and macho_file.options.namespace == .flat and !nlist.pext(); + symbol.flags.interposable = nlist.ext() and (nlist.sect() or nlist.abs()) and macho_file.base.isDynLib() and !nlist.pext(); + // TODO + // symbol.flags.interposable = nlist.ext() and (nlist.sect() or nlist.abs()) and macho_file.base.isDynLib() and macho_file.options.namespace == .flat and !nlist.pext(); if (nlist.sect() and self.sections.items(.header)[nlist.n_sect - 1].type() == macho.S_THREAD_LOCAL_VARIABLES) @@ -869,7 +871,7 @@ fn initSymbolStabs(self: *Object, allocator: Allocator, nlists: anytype, macho_f fn find(fs: @This(), addr: u64) ?Symbol.Index { // TODO binary search since we have the list sorted for (fs.entries) |nlist| { - if (nlist.nlist.n_value == addr) return fs.ctx.symbols.items[nlist.idx]; + if (nlist.nlist.n_value == addr) return @intCast(nlist.idx); } return null; } @@ -920,17 +922,17 @@ fn initSymbolStabs(self: *Object, allocator: Allocator, nlists: anytype, macho_f switch (nlist.n_type) { macho.N_BNSYM => { stab.is_func = true; - stab.symbol = sym_lookup.find(nlist.n_value); + stab.index = sym_lookup.find(nlist.n_value); // TODO validate i += 3; }, macho.N_GSYM => { stab.is_func = false; - stab.symbol = sym_lookup.find(addr_lookup.get(self.getString(nlist.n_strx)).?); + stab.index = sym_lookup.find(addr_lookup.get(self.getString(nlist.n_strx)).?); }, macho.N_STSYM => { stab.is_func = false; - stab.symbol = sym_lookup.find(nlist.n_value); + stab.index = sym_lookup.find(nlist.n_value); }, else => { try macho_file.reportParseError2(self.index, "unhandled symbol stab type 0x{x}", .{ @@ -1103,9 +1105,9 @@ fn initUnwindRecords(self: *Object, allocator: Allocator, sect_id: u8, file: Fil ctx: *const Object, fn find(fs: @This(), addr: u64) ?Symbol.Index { - for (fs.ctx.symbols.items, 0..) |sym_index, i| { + for (0..fs.ctx.symbols.items.len) |i| { const nlist = fs.ctx.symtab.items(.nlist)[i]; - if (nlist.ext() and nlist.n_value == addr) return sym_index; + if (nlist.ext() and nlist.n_value == addr) return @intCast(i); } return null; } @@ -1274,7 +1276,7 @@ fn parseUnwindRecords(self: *Object, allocator: Allocator, cpu_arch: std.Target. // Create a null record const rec_index = try self.addUnwindRecord(allocator); const rec = self.getUnwindRecord(rec_index); - const atom = macho_file.getAtom(meta.atom).?; + const atom = self.getAtom(meta.atom).?; try self.unwind_records_indexes.append(allocator, rec_index); rec.length = @intCast(meta.size); rec.atom = meta.atom; @@ -1468,17 +1470,17 @@ fn findCompileUnit(self: *Object, args: struct { }; } -pub fn resolveSymbols(self: *Object, macho_file: *MachO) void { +pub fn resolveSymbols(self: *Object, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - const gpa = macho_file.base.allocator; + const gpa = macho_file.base.comp.gpa; for (self.symtab.items(.nlist), self.symtab.items(.atom), self.globals.items, 0..) |nlist, atom_index, *global, i| { if (!nlist.ext()) continue; if (nlist.sect()) { const atom = self.getAtom(atom_index).?; - if (!atom.alive.load(.seq_cst)) continue; + if (!atom.flags.alive) continue; } const gop = try macho_file.resolver.getOrPut(gpa, .{ @@ -1643,7 +1645,7 @@ pub fn claimUnresolved(self: *Object, macho_file: *MachO) void { if (self.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue; - const is_import = switch (macho_file.options.undefined_treatment) { + const is_import = switch (macho_file.undefined_treatment) { .@"error" => false, .warn, .suppress => nlist.weakRef(), .dynamic_lookup => true, @@ -1682,8 +1684,8 @@ pub fn claimUnresolvedRelocatable(self: *Object, macho_file: *MachO) void { } } -fn addSection(self: *Object, allocator: Allocator, segname: []const u8, sectname: []const u8) !u32 { - const n_sect = @as(u32, @intCast(try self.sections.addOne(allocator))); +fn addSection(self: *Object, allocator: Allocator, segname: []const u8, sectname: []const u8) !u8 { + const n_sect = @as(u8, @intCast(try self.sections.addOne(allocator))); self.sections.set(n_sect, .{ .header = .{ .sectname = MachO.makeStaticString(sectname), @@ -1799,7 +1801,7 @@ pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writ try writer.writeAll(data); } -pub fn calcSymtabSize(self: *Object, macho_file: *MachO) !void { +pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void { const tracy = trace(@src()); defer tracy.end(); @@ -1821,27 +1823,27 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) !void { continue; sym.flags.output_symtab = true; if (sym.isLocal()) { - try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file); + sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file); self.output_symtab_ctx.nlocals += 1; } else if (sym.flags.@"export") { - try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file); + sym.addExtra(.{ .symtab = self.output_symtab_ctx.nexports }, macho_file); self.output_symtab_ctx.nexports += 1; } else { assert(sym.flags.import); - try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file); + sym.addExtra(.{ .symtab = self.output_symtab_ctx.nimports }, macho_file); self.output_symtab_ctx.nimports += 1; } self.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + 1)); } if (macho_file.base.comp.config.debug_format != .strip and self.hasDebugInfo()) - try self.calcStabsSize(macho_file); + self.calcStabsSize(macho_file); } -pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void { +pub fn calcStabsSize(self: *Object, macho_file: *MachO) void { if (self.compile_unit) |cu| { - const comp_dir = cu.getCompDir(self); - const tu_name = cu.getTuName(self); + const comp_dir = cu.getCompDir(self.*); + const tu_name = cu.getTuName(self.*); self.output_symtab_ctx.nstabs += 4; // N_SO, N_SO, N_OSO, N_SO self.output_symtab_ctx.strsize += @as(u32, @intCast(comp_dir.len + 1)); // comp_dir @@ -1876,12 +1878,12 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void { for (self.stab_files.items) |sf| { self.output_symtab_ctx.nstabs += 4; // N_SO, N_SO, N_OSO, N_SO - self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getCompDir(self).len + 1)); // comp_dir - self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getTuName(self).len + 1)); // tu_name - self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getOsoPath(self).len + 1)); // path + self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getCompDir(self.*).len + 1)); // comp_dir + self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getTuName(self.*).len + 1)); // tu_name + self.output_symtab_ctx.strsize += @as(u32, @intCast(sf.getOsoPath(self.*).len + 1)); // path for (sf.stabs.items) |stab| { - const sym = stab.getSymbol(macho_file) orelse continue; + const sym = stab.getSymbol(self.*) orelse continue; const file = sym.getFile(macho_file).?; if (file.getIndex() != self.index) continue; if (!sym.flags.output_symtab) continue; @@ -2065,7 +2067,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void { } } -pub fn writeSymtab(self: Object, macho_file: *MachO) void { +pub fn writeSymtab(self: Object, macho_file: *MachO, ctx: anytype) void { const tracy = trace(@src()); defer tracy.end(); @@ -2075,21 +2077,21 @@ pub fn writeSymtab(self: Object, macho_file: *MachO) void { const file = ref.getFile(macho_file) orelse continue; if (file.getIndex() != self.index) continue; const idx = sym.getOutputSymtabIndex(macho_file) orelse continue; - const out_sym = &macho_file.symtab.items[idx]; + const out_sym = &ctx.symtab.items[idx]; out_sym.n_strx = n_strx; sym.setOutputSym(macho_file, out_sym); const name = sym.getName(macho_file); - @memcpy(macho_file.strtab.items[n_strx..][0..name.len], name); + @memcpy(ctx.strtab.items[n_strx..][0..name.len], name); n_strx += @intCast(name.len); - macho_file.strtab.items[n_strx] = 0; + ctx.strtab.items[n_strx] = 0; n_strx += 1; } if (macho_file.base.comp.config.debug_format != .strip and self.hasDebugInfo()) - try self.writeStabs(n_strx, macho_file); + self.writeStabs(n_strx, macho_file, ctx); } -pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { +pub fn writeStabs(self: Object, stroff: u32, macho_file: *MachO, ctx: anytype) void { const writeFuncStab = struct { inline fn writeFuncStab( n_strx: u32, @@ -2097,7 +2099,7 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { n_value: u64, size: u64, index: u32, - context: *MachO, + context: anytype, ) void { context.symtab.items[index] = .{ .n_strx = 0, @@ -2139,7 +2141,7 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { // Open scope // N_SO comp_dir - macho_file.symtab.items[index] = .{ + ctx.symtab.items[index] = .{ .n_strx = n_strx, .n_type = macho.N_SO, .n_sect = 0, @@ -2147,9 +2149,9 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { .n_value = 0, }; index += 1; - @memcpy(macho_file.strtab.items[n_strx..][0..comp_dir.len], comp_dir); + @memcpy(ctx.strtab.items[n_strx..][0..comp_dir.len], comp_dir); n_strx += @intCast(comp_dir.len); - macho_file.strtab.items[n_strx] = 0; + ctx.strtab.items[n_strx] = 0; n_strx += 1; // N_SO tu_name macho_file.symtab.items[index] = .{ @@ -2160,12 +2162,12 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { .n_value = 0, }; index += 1; - @memcpy(macho_file.strtab.items[n_strx..][0..tu_name.len], tu_name); + @memcpy(ctx.strtab.items[n_strx..][0..tu_name.len], tu_name); n_strx += @intCast(tu_name.len); - macho_file.strtab.items[n_strx] = 0; + ctx.strtab.items[n_strx] = 0; n_strx += 1; // N_OSO path - macho_file.symtab.items[index] = .{ + ctx.symtab.items[index] = .{ .n_strx = n_strx, .n_type = macho.N_OSO, .n_sect = 0, @@ -2174,20 +2176,20 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { }; index += 1; if (self.in_archive) |ar| { - @memcpy(macho_file.strtab.items[n_strx..][0..ar.path.len], ar.path); + @memcpy(ctx.strtab.items[n_strx..][0..ar.path.len], ar.path); n_strx += @intCast(ar.path.len); - macho_file.strtab.items[n_strx] = '('; + ctx.strtab.items[n_strx] = '('; n_strx += 1; - @memcpy(macho_file.strtab.items[n_strx..][0..self.path.len], self.path); + @memcpy(ctx.strtab.items[n_strx..][0..self.path.len], self.path); n_strx += @intCast(self.path.len); - macho_file.strtab.items[n_strx] = ')'; + ctx.strtab.items[n_strx] = ')'; n_strx += 1; - macho_file.strtab.items[n_strx] = 0; + ctx.strtab.items[n_strx] = 0; n_strx += 1; } else { - @memcpy(macho_file.strtab.items[n_strx..][0..self.path.len], self.path); + @memcpy(ctx.strtab.items[n_strx..][0..self.path.len], self.path); n_strx += @intCast(self.path.len); - macho_file.strtab.items[n_strx] = 0; + ctx.strtab.items[n_strx] = 0; n_strx += 1; } @@ -2203,17 +2205,17 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { const sect = macho_file.sections.items(.header)[sym.getOutputSectionIndex(macho_file)]; const sym_n_strx = n_strx: { const symtab_index = sym.getOutputSymtabIndex(macho_file).?; - const osym = macho_file.symtab.items[symtab_index]; + const osym = ctx.symtab.items[symtab_index]; break :n_strx osym.n_strx; }; const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.getOutputSectionIndex(macho_file) + 1) else 0; const sym_n_value = sym.getAddress(.{}, macho_file); const sym_size = sym.getSize(macho_file); if (sect.isCode()) { - writeFuncStab(sym_n_strx, sym_n_sect, sym_n_value, sym_size, index, macho_file); + writeFuncStab(sym_n_strx, sym_n_sect, sym_n_value, sym_size, index, ctx); index += 4; } else if (sym.visibility == .global) { - macho_file.symtab.items[index] = .{ + ctx.symtab.items[index] = .{ .n_strx = sym_n_strx, .n_type = macho.N_GSYM, .n_sect = sym_n_sect, @@ -2222,7 +2224,7 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { }; index += 1; } else { - macho_file.symtab.items[index] = .{ + ctx.symtab.items[index] = .{ .n_strx = sym_n_strx, .n_type = macho.N_STSYM, .n_sect = sym_n_sect, @@ -2235,7 +2237,7 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { // Close scope // N_SO - macho_file.symtab.items[index] = .{ + ctx.symtab.items[index] = .{ .n_strx = 0, .n_type = macho.N_SO, .n_sect = 0, @@ -2246,13 +2248,13 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { assert(self.hasSymbolStabs()); for (self.stab_files.items) |sf| { - const comp_dir = sf.getCombDir(self); + const comp_dir = sf.getCompDir(self); const tu_name = sf.getTuName(self); const oso_path = sf.getOsoPath(self); // Open scope // N_SO comp_dir - macho_file.symtab.items[index] = .{ + ctx.symtab.items[index] = .{ .n_strx = n_strx, .n_type = macho.N_SO, .n_sect = 0, @@ -2260,12 +2262,12 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { .n_value = 0, }; index += 1; - @memcpy(macho_file.strtab.items[n_strx..][0..comp_dir.len], comp_dir); + @memcpy(ctx.strtab.items[n_strx..][0..comp_dir.len], comp_dir); n_strx += @intCast(comp_dir.len); - macho_file.strtab.items[n_strx] = 0; + ctx.strtab.items[n_strx] = 0; n_strx += 1; // N_SO tu_name - macho_file.symtab.items[index] = .{ + ctx.symtab.items[index] = .{ .n_strx = n_strx, .n_type = macho.N_SO, .n_sect = 0, @@ -2273,12 +2275,12 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { .n_value = 0, }; index += 1; - @memcpy(macho_file.strtab.items[n_strx..][0..tu_name.len], tu_name); + @memcpy(ctx.strtab.items[n_strx..][0..tu_name.len], tu_name); n_strx += @intCast(tu_name.len); - macho_file.strtab.items[n_strx] = 0; + ctx.strtab.items[n_strx] = 0; n_strx += 1; // N_OSO path - macho_file.symtab.items[index] = .{ + ctx.symtab.items[index] = .{ .n_strx = n_strx, .n_type = macho.N_OSO, .n_sect = 0, @@ -2286,29 +2288,29 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { .n_value = sf.getOsoModTime(self), }; index += 1; - @memcpy(macho_file.strtab.items[n_strx..][0..oso_path.len], oso_path); + @memcpy(ctx.strtab.items[n_strx..][0..oso_path.len], oso_path); n_strx += @intCast(oso_path.len); - macho_file.strtab.items[n_strx] = 0; + ctx.strtab.items[n_strx] = 0; n_strx += 1; for (sf.stabs.items) |stab| { - const sym = stab.getSymbol(macho_file) orelse continue; + const sym = stab.getSymbol(self) orelse continue; const file = sym.getFile(macho_file).?; if (file.getIndex() != self.index) continue; if (!sym.flags.output_symtab) continue; const sym_n_strx = n_strx: { const symtab_index = sym.getOutputSymtabIndex(macho_file).?; - const osym = macho_file.symtab.items[symtab_index]; + const osym = ctx.symtab.items[symtab_index]; break :n_strx osym.n_strx; }; const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.getOutputSectionIndex(macho_file) + 1) else 0; const sym_n_value = sym.getAddress(.{}, macho_file); const sym_size = sym.getSize(macho_file); if (stab.is_func) { - writeFuncStab(sym_n_strx, sym_n_sect, sym_n_value, sym_size, index, macho_file); + writeFuncStab(sym_n_strx, sym_n_sect, sym_n_value, sym_size, index, ctx); index += 4; } else if (sym.visibility == .global) { - macho_file.symtab.items[index] = .{ + ctx.symtab.items[index] = .{ .n_strx = sym_n_strx, .n_type = macho.N_GSYM, .n_sect = sym_n_sect, @@ -2317,7 +2319,7 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { }; index += 1; } else { - macho_file.symtab.items[index] = .{ + ctx.symtab.items[index] = .{ .n_strx = sym_n_strx, .n_type = macho.N_STSYM, .n_sect = sym_n_sect, @@ -2330,7 +2332,7 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { // Close scope // N_SO - macho_file.symtab.items[index] = .{ + ctx.symtab.items[index] = .{ .n_strx = 0, .n_type = macho.N_SO, .n_sect = 0, @@ -2343,7 +2345,7 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void { } pub fn getAtomRelocs(self: *const Object, atom: Atom, macho_file: *MachO) []const Relocation { - const extra = atom.getExtra(macho_file).?; + const extra = atom.getExtra(macho_file); const relocs = self.sections.items(.relocs)[atom.n_sect]; return relocs.items[extra.rel_index..][0..extra.rel_count]; } @@ -2402,7 +2404,7 @@ pub fn asFile(self: *Object) File { } const AddAtomArgs = struct { - name: MachO.String, + name: u32, n_sect: u8, off: u64, size: u64, @@ -2420,7 +2422,7 @@ fn addAtom(self: *Object, allocator: Allocator, args: AddAtomArgs) !Atom.Index { .size = args.size, .off = args.off, .extra = try self.addAtomExtra(allocator, .{}), - .alignment = args.alignment, + .alignment = Atom.Alignment.fromLog2Units(args.alignment), }; return atom_index; } @@ -2693,12 +2695,12 @@ fn formatSymtab( } for (object.stab_files.items) |sf| { try writer.print(" stabs({s},{s},{s})\n", .{ - sf.getCompDir(object), - sf.getTuName(object), - sf.getOsoPath(object), + sf.getCompDir(object.*), + sf.getTuName(object.*), + sf.getOsoPath(object.*), }); for (sf.stabs.items) |stab| { - try writer.print(" {}", .{stab.fmt(object)}); + try writer.print(" {}", .{stab.fmt(object.*)}); } } } @@ -2744,33 +2746,33 @@ const StabFile = struct { comp_dir: u32, stabs: std.ArrayListUnmanaged(Stab) = .{}, - fn getCompDir(sf: StabFile, object: *const Object) [:0]const u8 { + fn getCompDir(sf: StabFile, object: Object) [:0]const u8 { const nlist = object.symtab.items(.nlist)[sf.comp_dir]; return object.getString(nlist.n_strx); } - fn getTuName(sf: StabFile, object: *const Object) [:0]const u8 { + fn getTuName(sf: StabFile, object: Object) [:0]const u8 { const nlist = object.symtab.items(.nlist)[sf.comp_dir + 1]; return object.getString(nlist.n_strx); } - fn getOsoPath(sf: StabFile, object: *const Object) [:0]const u8 { + fn getOsoPath(sf: StabFile, object: Object) [:0]const u8 { const nlist = object.symtab.items(.nlist)[sf.comp_dir + 2]; return object.getString(nlist.n_strx); } - fn getOsoModTime(sf: StabFile, object: *const Object) u64 { + fn getOsoModTime(sf: StabFile, object: Object) u64 { const nlist = object.symtab.items(.nlist)[sf.comp_dir + 2]; return nlist.n_value; } const Stab = struct { is_func: bool = true, - symbol: ?Symbol.Index = null, + index: ?Symbol.Index = null, - fn getSymbol(stab: Stab, object: *const Object) ?*Symbol { + fn getSymbol(stab: Stab, object: Object) ?Symbol { const index = stab.index orelse return null; - return &object.symbols.items[index]; + return object.symbols.items[index]; } pub fn format( @@ -2786,9 +2788,9 @@ const StabFile = struct { @compileError("do not format stabs directly"); } - const StabFormatContext = struct { Stab, *const Object }; + const StabFormatContext = struct { Stab, Object }; - pub fn fmt(stab: Stab, object: *const Object) std.fmt.Formatter(format2) { + pub fn fmt(stab: Stab, object: Object) std.fmt.Formatter(format2) { return .{ .data = .{ stab, object } }; } @@ -2817,11 +2819,11 @@ const CompileUnit = struct { comp_dir: u32, tu_name: u32, - fn getCompDir(cu: CompileUnit, object: *const Object) [:0]const u8 { + fn getCompDir(cu: CompileUnit, object: Object) [:0]const u8 { return object.getString(cu.comp_dir); } - fn getTuName(cu: CompileUnit, object: *const Object) [:0]const u8 { + fn getTuName(cu: CompileUnit, object: Object) [:0]const u8 { return object.getString(cu.tu_name); } }; @@ -2840,15 +2842,14 @@ const CompactUnwindCtx = struct { const x86_64 = struct { fn parseRelocs( - self: *const Object, - n_sect: u8, + self: *Object, sect: macho.section_64, out: *std.ArrayListUnmanaged(Relocation), + handle: File.Handle, macho_file: *MachO, ) !void { const gpa = macho_file.base.comp.gpa; - const handle = macho_file.getFileHandle(self.file_handle); const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info)); defer gpa.free(relocs_buffer); { @@ -2857,8 +2858,12 @@ const x86_64 = struct { } const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc]; - const code = try self.getSectionData(@intCast(n_sect), macho_file); + const code = try gpa.alloc(u8, sect.size); defer gpa.free(code); + { + const amt = try handle.preadAll(code, sect.offset + self.offset); + if (amt != code.len) return error.InputOutput; + } try out.ensureTotalCapacityPrecise(gpa, relocs.len); @@ -2880,8 +2885,9 @@ const x86_64 = struct { .X86_64_RELOC_SIGNED_4 => 4, else => 0, }; + var is_extern = rel.r_extern == 1; - const target = if (rel.r_extern == 0) blk: { + const target = if (!is_extern) blk: { const nsect = rel.r_symbolnum - 1; const taddr: i64 = if (rel.r_pcrel == 1) @as(i64, @intCast(sect.addr)) + rel.r_address + addend + 4 @@ -2893,9 +2899,15 @@ const x86_64 = struct { }); return error.MalformedObject; }; - addend = taddr - @as(i64, @intCast(macho_file.getAtom(target).?.getInputAddress(macho_file))); + const target_atom = self.getAtom(target).?; + addend = taddr - @as(i64, @intCast(target_atom.getInputAddress(macho_file))); + const isec = target_atom.getInputSection(macho_file); + if (isCstringLiteral(isec) or isFixedSizeLiteral(isec) or isPtrLiteral(isec)) { + is_extern = true; + break :blk target_atom.getExtra(macho_file).literal_symbol_index; + } break :blk target; - } else self.symbols.items[rel.r_symbolnum]; + } else rel.r_symbolnum; const has_subtractor = if (i > 0 and @as(macho.reloc_type_x86_64, @enumFromInt(relocs[i - 1].r_type)) == .X86_64_RELOC_SUBTRACTOR) @@ -2909,7 +2921,7 @@ const x86_64 = struct { break :blk true; } else false; - const @"type": Relocation.Type = validateRelocType(rel, rel_type) catch |err| { + const @"type": Relocation.Type = validateRelocType(rel, rel_type, is_extern) catch |err| { switch (err) { error.Pcrel => try macho_file.reportParseError2( self.index, @@ -2936,7 +2948,7 @@ const x86_64 = struct { }; out.appendAssumeCapacity(.{ - .tag = if (rel.r_extern == 1) .@"extern" else .local, + .tag = if (is_extern) .@"extern" else .local, .offset = @as(u32, @intCast(rel.r_address)), .target = target, .addend = addend, @@ -2951,7 +2963,7 @@ const x86_64 = struct { } } - fn validateRelocType(rel: macho.relocation_info, rel_type: macho.reloc_type_x86_64) !Relocation.Type { + fn validateRelocType(rel: macho.relocation_info, rel_type: macho.reloc_type_x86_64, is_extern: bool) !Relocation.Type { switch (rel_type) { .X86_64_RELOC_UNSIGNED => { if (rel.r_pcrel == 1) return error.Pcrel; @@ -2971,7 +2983,7 @@ const x86_64 = struct { => { if (rel.r_pcrel == 0) return error.NonPcrel; if (rel.r_length != 2) return error.InvalidLength; - if (rel.r_extern == 0) return error.NonExtern; + if (!is_extern) return error.NonExtern; return switch (rel_type) { .X86_64_RELOC_BRANCH => .branch, .X86_64_RELOC_GOT_LOAD => .got_load, @@ -3002,15 +3014,14 @@ const x86_64 = struct { const aarch64 = struct { fn parseRelocs( - self: *const Object, - n_sect: u8, + self: *Object, sect: macho.section_64, out: *std.ArrayListUnmanaged(Relocation), + handle: File.Handle, macho_file: *MachO, ) !void { const gpa = macho_file.base.comp.gpa; - const handle = macho_file.getFileHandle(self.file_handle); const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info)); defer gpa.free(relocs_buffer); { @@ -3019,8 +3030,12 @@ const aarch64 = struct { } const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc]; - const code = try self.getSectionData(@intCast(n_sect), macho_file); + const code = try gpa.alloc(u8, sect.size); defer gpa.free(code); + { + const amt = try handle.preadAll(code, sect.offset + self.offset); + if (amt != code.len) return error.InputOutput; + } try out.ensureTotalCapacityPrecise(gpa, relocs.len); @@ -3066,8 +3081,9 @@ const aarch64 = struct { } const rel_type: macho.reloc_type_arm64 = @enumFromInt(rel.r_type); + var is_extern = rel.r_extern == 1; - const target = if (rel.r_extern == 0) blk: { + const target = if (!is_extern) blk: { const nsect = rel.r_symbolnum - 1; const taddr: i64 = if (rel.r_pcrel == 1) @as(i64, @intCast(sect.addr)) + rel.r_address + addend @@ -3079,9 +3095,15 @@ const aarch64 = struct { }); return error.MalformedObject; }; - addend = taddr - @as(i64, @intCast(macho_file.getAtom(target).?.getInputAddress(macho_file))); + const target_atom = self.getAtom(target).?; + addend = taddr - @as(i64, @intCast(target_atom.getInputAddress(macho_file))); + const isec = target_atom.getInputSection(macho_file); + if (isCstringLiteral(isec) or isFixedSizeLiteral(isec) or isPtrLiteral(isec)) { + is_extern = true; + break :blk target_atom.getExtra(macho_file).literal_symbol_index; + } break :blk target; - } else self.symbols.items[rel.r_symbolnum]; + } else rel.r_symbolnum; const has_subtractor = if (i > 0 and @as(macho.reloc_type_arm64, @enumFromInt(relocs[i - 1].r_type)) == .ARM64_RELOC_SUBTRACTOR) @@ -3095,7 +3117,7 @@ const aarch64 = struct { break :blk true; } else false; - const @"type": Relocation.Type = validateRelocType(rel, rel_type) catch |err| { + const @"type": Relocation.Type = validateRelocType(rel, rel_type, is_extern) catch |err| { switch (err) { error.Pcrel => try macho_file.reportParseError2( self.index, @@ -3122,7 +3144,7 @@ const aarch64 = struct { }; out.appendAssumeCapacity(.{ - .tag = if (rel.r_extern == 1) .@"extern" else .local, + .tag = if (is_extern) .@"extern" else .local, .offset = @as(u32, @intCast(rel.r_address)), .target = target, .addend = addend, @@ -3137,7 +3159,7 @@ const aarch64 = struct { } } - fn validateRelocType(rel: macho.relocation_info, rel_type: macho.reloc_type_arm64) !Relocation.Type { + fn validateRelocType(rel: macho.relocation_info, rel_type: macho.reloc_type_arm64, is_extern: bool) !Relocation.Type { switch (rel_type) { .ARM64_RELOC_UNSIGNED => { if (rel.r_pcrel == 1) return error.Pcrel; @@ -3158,7 +3180,7 @@ const aarch64 = struct { => { if (rel.r_pcrel == 0) return error.NonPcrel; if (rel.r_length != 2) return error.InvalidLength; - if (rel.r_extern == 0) return error.NonExtern; + if (!is_extern) return error.NonExtern; return switch (rel_type) { .ARM64_RELOC_BRANCH26 => .branch, .ARM64_RELOC_PAGE21 => .page, @@ -3175,7 +3197,7 @@ const aarch64 = struct { => { if (rel.r_pcrel == 1) return error.Pcrel; if (rel.r_length != 2) return error.InvalidLength; - if (rel.r_extern == 0) return error.NonExtern; + if (!is_extern) return error.NonExtern; return switch (rel_type) { .ARM64_RELOC_PAGEOFF12 => .pageoff, .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => .got_load_pageoff, diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index 003996fc95..8cb198e7d7 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -150,7 +150,7 @@ pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 { const file = symbol.getFile(macho_file).?; return switch (file) { .dylib, .zig_object => unreachable, - .object, .internal => |x| x.symbols.items[extra.objc_selrefs].getAddress(.{}, macho_file), + inline else => |x| x.symbols.items[extra.objc_selrefs].getAddress(.{}, macho_file), }; } @@ -186,7 +186,7 @@ pub fn getOutputSymtabIndex(symbol: Symbol, macho_file: *MachO) ?u32 { const symtab_ctx = switch (file) { inline else => |x| x.output_symtab_ctx, }; - var idx = symbol.getExtra(macho_file).?.symtab; + var idx = symbol.getExtra(macho_file).symtab; if (symbol.isLocal()) { idx += symtab_ctx.ilocal; } else if (symbol.flags.@"export") { diff --git a/src/link/MachO/UnwindInfo.zig b/src/link/MachO/UnwindInfo.zig index f06db2f842..8fb4b2ce63 100644 --- a/src/link/MachO/UnwindInfo.zig +++ b/src/link/MachO/UnwindInfo.zig @@ -473,11 +473,11 @@ pub const Record = struct { } pub fn getAtom(rec: Record, macho_file: *MachO) *Atom { - return macho_file.getAtom(rec.atom).?; + return rec.getObject(macho_file).getAtom(rec.atom).?; } pub fn getLsdaAtom(rec: Record, macho_file: *MachO) ?*Atom { - return macho_file.getAtom(rec.lsda); + return rec.getObject(macho_file).getAtom(rec.lsda); } pub fn getPersonality(rec: Record, macho_file: *MachO) ?*Symbol { diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index dba3e4c1b1..544d778f39 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -343,6 +343,36 @@ pub fn writeAr(self: ZigObject, ar_format: Archive.Format, writer: anytype) !voi try writer.writeAll(self.data.items); } +pub fn claimUnresolved(self: *ZigObject, macho_file: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); + + for (self.symbols.items, 0..) |*sym, i| { + const nlist = self.symtab.items(.nlist)[i]; + if (!nlist.ext()) continue; + if (!nlist.undf()) continue; + + if (self.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue; + + const is_import = switch (macho_file.undefined_treatment) { + .@"error" => false, + .warn, .suppress => nlist.weakRef(), + .dynamic_lookup => true, + }; + if (is_import) { + sym.value = 0; + sym.atom_ref = .{ .index = 0, .file = 0 }; + sym.flags.weak = false; + sym.flags.weak_ref = nlist.weakRef(); + sym.flags.import = is_import; + sym.visibility = .global; + + const idx = self.globals.items[i]; + macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = self.index }; + } + } +} + pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void { for (self.getAtoms()) |atom_index| { const atom = self.getAtom(atom_index) orelse continue; @@ -378,7 +408,7 @@ pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) void { } } -pub fn writeSymtab(self: ZigObject, macho_file: *MachO) void { +pub fn writeSymtab(self: ZigObject, macho_file: *MachO, ctx: anytype) void { const tracy = trace(@src()); defer tracy.end(); @@ -388,13 +418,13 @@ pub fn writeSymtab(self: ZigObject, macho_file: *MachO) void { const file = ref.getFile(macho_file) orelse continue; if (file.getIndex() != self.index) continue; const idx = sym.getOutputSymtabIndex(macho_file) orelse continue; - const out_sym = &macho_file.symtab.items[idx]; + const out_sym = &ctx.symtab.items[idx]; out_sym.n_strx = n_strx; sym.setOutputSym(macho_file, out_sym); const name = sym.getName(macho_file); - @memcpy(macho_file.strtab.items[n_strx..][0..name.len], name); + @memcpy(ctx.strtab.items[n_strx..][0..name.len], name); n_strx += @intCast(name.len); - macho_file.strtab.items[n_strx] = 0; + ctx.strtab.items[n_strx] = 0; n_strx += 1; } } @@ -1098,7 +1128,7 @@ pub fn lowerUnnamedConst( }, }; const sym = self.symbols.items[sym_index]; - try unnamed_consts.append(gpa, sym.atom); + try unnamed_consts.append(gpa, sym.atom_ref.index); return sym_index; } diff --git a/src/link/MachO/dyld_info/Rebase.zig b/src/link/MachO/dyld_info/Rebase.zig index 9233a25e07..c0bcb42ed1 100644 --- a/src/link/MachO/dyld_info/Rebase.zig +++ b/src/link/MachO/dyld_info/Rebase.zig @@ -34,7 +34,7 @@ pub fn updateSize(rebase: *Rebase, macho_file: *MachO) !void { for (objects.items) |index| { const file = macho_file.getFile(index).?; for (file.getAtoms()) |atom_index| { - const atom = macho_file.getAtom(atom_index) orelse continue; + const atom = file.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; if (atom.getInputSection(macho_file).isZerofill()) continue; const atom_addr = atom.getAddress(macho_file); @@ -43,7 +43,7 @@ pub fn updateSize(rebase: *Rebase, macho_file: *MachO) !void { for (atom.getRelocs(macho_file)) |rel| { if (rel.type != .unsigned or rel.meta.length != 3) continue; if (rel.tag == .@"extern") { - const sym = rel.getTargetSymbol(macho_file); + const sym = rel.getTargetSymbol(atom.*, macho_file); if (sym.isTlvInit(macho_file)) continue; if (sym.flags.import) continue; } @@ -72,7 +72,7 @@ pub fn updateSize(rebase: *Rebase, macho_file: *MachO) !void { const seg_id = macho_file.sections.items(.segment_id)[sid]; const seg = macho_file.segments.items[seg_id]; for (macho_file.got.symbols.items, 0..) |ref, idx| { - const sym = macho_file.getSymbol(ref); + const sym = ref.getSymbol(macho_file).?; const addr = macho_file.got.getAddress(@intCast(idx), macho_file); if (!sym.flags.import) { try rebase.entries.append(gpa, .{ @@ -88,7 +88,7 @@ pub fn updateSize(rebase: *Rebase, macho_file: *MachO) !void { const seg_id = macho_file.sections.items(.segment_id)[sid]; const seg = macho_file.segments.items[seg_id]; for (macho_file.stubs.symbols.items, 0..) |ref, idx| { - const sym = macho_file.getSymbol(ref); + const sym = ref.getSymbol(macho_file).?; const addr = sect.addr + idx * @sizeOf(u64); const rebase_entry = Rebase.Entry{ .offset = addr - seg.vmaddr, @@ -104,7 +104,7 @@ pub fn updateSize(rebase: *Rebase, macho_file: *MachO) !void { const seg_id = macho_file.sections.items(.segment_id)[sid]; const seg = macho_file.segments.items[seg_id]; for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| { - const sym = macho_file.getSymbol(ref); + const sym = ref.getSymbol(macho_file).?; const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file); if (!sym.flags.import) { try rebase.entries.append(gpa, .{ diff --git a/src/link/MachO/dyld_info/Trie.zig b/src/link/MachO/dyld_info/Trie.zig index aead1372f0..44e6cd4234 100644 --- a/src/link/MachO/dyld_info/Trie.zig +++ b/src/link/MachO/dyld_info/Trie.zig @@ -94,33 +94,31 @@ pub fn updateSize(self: *Trie, macho_file: *MachO) !void { const gpa = macho_file.base.comp.gpa; try self.init(gpa); - // TODO - // try self.nodes.ensureUnusedCapacity(gpa, macho_file.resolver.values.items.len * 2); - // try self.edges.ensureUnusedCapacity(gpa, macho_file.resolver.values.items.len * 2); + try self.nodes.ensureUnusedCapacity(gpa, macho_file.resolver.values.items.len * 2); + try self.edges.ensureUnusedCapacity(gpa, macho_file.resolver.values.items.len * 2); const seg = macho_file.getTextSegment(); - for (macho_file.objects.items) |index| { - for (macho_file.getFile(index).?.getSymbols()) |ref| { - const sym = macho_file.getSymbol(ref); - if (!sym.flags.@"export") continue; - if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue; - var flags: u64 = if (sym.flags.abs) - macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE - else if (sym.flags.tlv) - macho.EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL - else - macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR; - if (sym.flags.weak) { - flags |= macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; - macho_file.weak_defines = true; - macho_file.binds_to_weak = true; - } - try self.put(gpa, .{ - .name = sym.getName(macho_file), - .vmaddr_offset = sym.getAddress(.{ .stubs = false }, macho_file) - seg.vmaddr, - .export_flags = flags, - }); + for (macho_file.resolver.values.items) |ref| { + if (ref.getFile(macho_file) == null) continue; + const sym = ref.getSymbol(macho_file).?; + if (!sym.flags.@"export") continue; + if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue; + var flags: u64 = if (sym.flags.abs) + macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE + else if (sym.flags.tlv) + macho.EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL + else + macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR; + if (sym.flags.weak) { + flags |= macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; + macho_file.weak_defines = true; + macho_file.binds_to_weak = true; } + try self.put(gpa, .{ + .name = sym.getName(macho_file), + .vmaddr_offset = sym.getAddress(.{ .stubs = false }, macho_file) - seg.vmaddr, + .export_flags = flags, + }); } try self.finalize(gpa); diff --git a/src/link/MachO/dyld_info/bind.zig b/src/link/MachO/dyld_info/bind.zig index 8684c66bac..94e7c7ef29 100644 --- a/src/link/MachO/dyld_info/bind.zig +++ b/src/link/MachO/dyld_info/bind.zig @@ -1,17 +1,19 @@ pub const Entry = struct { - target: Symbol.Index, + target: MachO.Ref, offset: u64, segment_id: u8, addend: i64, pub fn lessThan(ctx: *MachO, entry: Entry, other: Entry) bool { + _ = ctx; if (entry.segment_id == other.segment_id) { - if (entry.target == other.target) { + if (entry.target.eql(other.target)) { return entry.offset < other.offset; } - const entry_name = ctx.getSymbol(entry.target).getName(ctx); - const other_name = ctx.getSymbol(other.target).getName(ctx); - return std.mem.lessThan(u8, entry_name, other_name); + if (entry.target.file == other.target.file) { + return entry.target.index < other.target.index; + } + return entry.target.file < other.target.file; } return entry.segment_id < other.segment_id; } @@ -44,7 +46,7 @@ pub const Bind = struct { for (objects.items) |index| { const file = macho_file.getFile(index).?; for (file.getAtoms()) |atom_index| { - const atom = macho_file.getAtom(atom_index) orelse continue; + const atom = file.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; if (atom.getInputSection(macho_file).isZerofill()) continue; const atom_addr = atom.getAddress(macho_file); @@ -55,10 +57,10 @@ pub const Bind = struct { if (rel.type != .unsigned or rel.meta.length != 3 or rel.tag != .@"extern") continue; const rel_offset = rel.offset - atom.off; const addend = rel.addend + rel.getRelocAddend(cpu_arch); - const sym = rel.getTargetSymbol(macho_file); + const sym = rel.getTargetSymbol(atom.*, macho_file); if (sym.isTlvInit(macho_file)) continue; const entry = Entry{ - .target = rel.target, + .target = rel.getTargetSymbolRef(atom.*, macho_file), .offset = atom_addr + rel_offset - seg.vmaddr, .segment_id = seg_id, .addend = addend, @@ -74,7 +76,7 @@ pub const Bind = struct { const seg_id = macho_file.sections.items(.segment_id)[sid]; const seg = macho_file.segments.items[seg_id]; for (macho_file.got.symbols.items, 0..) |ref, idx| { - const sym = macho_file.getSymbol(ref); + const sym = ref.getSymbol(macho_file).?; const addr = macho_file.got.getAddress(@intCast(idx), macho_file); const entry = Entry{ .target = ref, @@ -93,7 +95,7 @@ pub const Bind = struct { const seg_id = macho_file.sections.items(.segment_id)[sid]; const seg = macho_file.segments.items[seg_id]; for (macho_file.stubs.symbols.items, 0..) |ref, idx| { - const sym = macho_file.getSymbol(ref); + const sym = ref.getSymbol(macho_file).?; const addr = sect.addr + idx * @sizeOf(u64); const bind_entry = Entry{ .target = ref, @@ -112,7 +114,7 @@ pub const Bind = struct { const seg = macho_file.segments.items[seg_id]; for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| { - const sym = macho_file.getSymbol(ref); + const sym = ref.getSymbol(macho_file).?; const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file); const entry = Entry{ .target = ref, @@ -162,7 +164,7 @@ pub const Bind = struct { var addend: i64 = 0; var count: usize = 0; var skip: u64 = 0; - var target: ?Symbol.Index = null; + var target: ?MachO.Ref = null; var state: enum { start, @@ -173,7 +175,7 @@ pub const Bind = struct { var i: usize = 0; while (i < entries.len) : (i += 1) { const current = entries[i]; - if (target == null or target.? != current.target) { + if (target == null or !target.?.eql(current.target)) { switch (state) { .start => {}, .bind_single => try doBind(writer), @@ -182,7 +184,7 @@ pub const Bind = struct { state = .start; target = current.target; - const sym = ctx.getSymbol(current.target); + const sym = current.target.getSymbol(ctx).?; const name = sym.getName(ctx); const flags: u8 = if (sym.weakRef(ctx)) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0; const ordinal: i16 = ord: { @@ -296,7 +298,7 @@ pub const WeakBind = struct { for (objects.items) |index| { const file = macho_file.getFile(index).?; for (file.getAtoms()) |atom_index| { - const atom = macho_file.getAtom(atom_index) orelse continue; + const atom = file.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; if (atom.getInputSection(macho_file).isZerofill()) continue; const atom_addr = atom.getAddress(macho_file); @@ -307,10 +309,10 @@ pub const WeakBind = struct { if (rel.type != .unsigned or rel.meta.length != 3 or rel.tag != .@"extern") continue; const rel_offset = rel.offset - atom.off; const addend = rel.addend + rel.getRelocAddend(cpu_arch); - const sym = rel.getTargetSymbol(macho_file); + const sym = rel.getTargetSymbol(atom.*, macho_file); if (sym.isTlvInit(macho_file)) continue; const entry = Entry{ - .target = rel.target, + .target = rel.getTargetSymbolRef(atom.*, macho_file), .offset = atom_addr + rel_offset - seg.vmaddr, .segment_id = seg_id, .addend = addend, @@ -326,7 +328,7 @@ pub const WeakBind = struct { const seg_id = macho_file.sections.items(.segment_id)[sid]; const seg = macho_file.segments.items[seg_id]; for (macho_file.got.symbols.items, 0..) |ref, idx| { - const sym = macho_file.getSymbol(ref); + const sym = ref.getSymbol(macho_file).?; const addr = macho_file.got.getAddress(@intCast(idx), macho_file); const entry = Entry{ .target = ref, @@ -346,7 +348,7 @@ pub const WeakBind = struct { const seg = macho_file.segments.items[seg_id]; for (macho_file.stubs.symbols.items, 0..) |ref, idx| { - const sym = macho_file.getSymbol(ref); + const sym = ref.getSymbol(macho_file).?; const addr = sect.addr + idx * @sizeOf(u64); const bind_entry = Entry{ .target = ref, @@ -365,7 +367,7 @@ pub const WeakBind = struct { const seg = macho_file.segments.items[seg_id]; for (macho_file.tlv_ptr.symbols.items, 0..) |ref, idx| { - const sym = macho_file.getSymbol(ref); + const sym = ref.getSymbol(macho_file).?; const addr = macho_file.tlv_ptr.getAddress(@intCast(idx), macho_file); const entry = Entry{ .target = ref, @@ -415,7 +417,7 @@ pub const WeakBind = struct { var addend: i64 = 0; var count: usize = 0; var skip: u64 = 0; - var target: ?Symbol.Index = null; + var target: ?MachO.Ref = null; var state: enum { start, @@ -426,7 +428,7 @@ pub const WeakBind = struct { var i: usize = 0; while (i < entries.len) : (i += 1) { const current = entries[i]; - if (target == null or target.? != current.target) { + if (target == null or !target.?.eql(current.target)) { switch (state) { .start => {}, .bind_single => try doBind(writer), @@ -435,7 +437,7 @@ pub const WeakBind = struct { state = .start; target = current.target; - const sym = ctx.getSymbol(current.target); + const sym = current.target.getSymbol(ctx).?; const name = sym.getName(ctx); const flags: u8 = 0; // TODO NON_WEAK_DEFINITION @@ -536,7 +538,7 @@ pub const LazyBind = struct { const seg = macho_file.segments.items[seg_id]; for (macho_file.stubs.symbols.items, 0..) |ref, idx| { - const sym = macho_file.getSymbol(ref); + const sym = ref.getSymbol(macho_file).?; const addr = sect.addr + idx * @sizeOf(u64); const bind_entry = Entry{ .target = ref, @@ -565,7 +567,7 @@ pub const LazyBind = struct { for (self.entries.items) |entry| { self.offsets.appendAssumeCapacity(@intCast(self.buffer.items.len)); - const sym = ctx.getSymbol(entry.target); + const sym = entry.target.getSymbol(ctx).?; const name = sym.getName(ctx); const flags: u8 = if (sym.weakRef(ctx)) macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT else 0; const ordinal: i16 = ord: { diff --git a/src/link/MachO/file.zig b/src/link/MachO/file.zig index c01416efd5..c1558292f5 100644 --- a/src/link/MachO/file.zig +++ b/src/link/MachO/file.zig @@ -30,10 +30,10 @@ pub const File = union(enum) { } } - pub fn resolveSymbols(file: File, macho_file: *MachO) void { - switch (file) { + pub fn resolveSymbols(file: File, macho_file: *MachO) !void { + return switch (file) { inline else => |x| x.resolveSymbols(macho_file), - } + }; } pub fn scanRelocs(file: File, macho_file: *MachO) !void { @@ -147,19 +147,19 @@ pub const File = union(enum) { if (ref.getFile(macho_file) == null) continue; if (ref.file != file.getIndex()) continue; const sym = ref.getSymbol(macho_file).?; - if (sym.getSectionFlags().got) { + if (sym.flags.needs_got) { log.debug("'{s}' needs GOT", .{sym.getName(macho_file)}); try macho_file.got.addSymbol(ref, macho_file); } - if (sym.getSectionFlags().stubs) { + if (sym.flags.stubs) { log.debug("'{s}' needs STUBS", .{sym.getName(macho_file)}); try macho_file.stubs.addSymbol(ref, macho_file); } - if (sym.getSectionFlags().tlv_ptr) { + if (sym.flags.tlv_ptr) { log.debug("'{s}' needs TLV pointer", .{sym.getName(macho_file)}); try macho_file.tlv_ptr.addSymbol(ref, macho_file); } - if (sym.getSectionFlags().objc_stubs) { + if (sym.flags.objc_stubs) { log.debug("'{s}' needs OBJC STUBS", .{sym.getName(macho_file)}); try macho_file.objc_stubs.addSymbol(ref, macho_file); } @@ -171,7 +171,7 @@ pub const File = union(enum) { defer tracy.end(); for (file.getAtoms()) |atom_index| { const atom = file.getAtom(atom_index) orelse continue; - if (!atom.alive.load(.seq_cst)) continue; + if (!atom.flags.alive) continue; atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file); } } @@ -185,18 +185,18 @@ pub const File = union(enum) { pub fn writeAtoms(file: File, macho_file: *MachO) !void { return switch (file) { - .dylib => unreachable, + .dylib, .zig_object => unreachable, inline else => |x| x.writeAtoms(macho_file), }; } - pub fn calcSymtabSize(file: File, macho_file: *MachO) !void { + pub fn calcSymtabSize(file: File, macho_file: *MachO) void { return switch (file) { inline else => |x| x.calcSymtabSize(macho_file), }; } - pub fn writeSymtab(file: File, macho_file: *MachO, ctx: anytype) !void { + pub fn writeSymtab(file: File, macho_file: *MachO, ctx: anytype) void { return switch (file) { inline else => |x| x.writeSymtab(macho_file, ctx), }; diff --git a/src/link/MachO/synthetic.zig b/src/link/MachO/synthetic.zig index a366fea2b9..14bc97c229 100644 --- a/src/link/MachO/synthetic.zig +++ b/src/link/MachO/synthetic.zig @@ -23,7 +23,7 @@ pub const ZigGotSection = struct { const index = try zig_got.allocateEntry(gpa); const entry = &zig_got.entries.items[index]; entry.* = sym_index; - const symbol = zo.getSymbol(sym_index); + const symbol = &zo.symbols.items[sym_index]; assert(symbol.flags.needs_zig_got); symbol.flags.has_zig_got = true; symbol.addExtra(.{ .zig_got = index }, macho_file); @@ -56,7 +56,7 @@ pub const ZigGotSection = struct { const zo = macho_file.getZigObject().?; const off = zig_got.entryOffset(index, macho_file); const entry = zig_got.entries.items[index]; - const value = zo.getSymbol(entry).getAddress(.{ .stubs = false }, macho_file); + const value = zo.symbols.items[entry].getAddress(.{ .stubs = false }, macho_file); var buf: [8]u8 = undefined; std.mem.writeInt(u64, &buf, value, .little); @@ -66,7 +66,7 @@ pub const ZigGotSection = struct { pub fn writeAll(zig_got: ZigGotSection, macho_file: *MachO, writer: anytype) !void { const zo = macho_file.getZigObject().?; for (zig_got.entries.items) |entry| { - const symbol = zo.getSymbol(entry); + const symbol = zo.symbols.items[entry]; const value = symbol.address(.{ .stubs = false }, macho_file); try writer.writeInt(u64, value, .little); } @@ -94,7 +94,7 @@ pub const ZigGotSection = struct { const zo = macho_file.getZigObject().?; try writer.writeAll("__zig_got\n"); for (zig_got.entries.items, 0..) |entry, index| { - const symbol = zo.getSymbol(entry); + const symbol = zo.symbols.items[entry]; try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{ index, zig_got.entryAddress(@intCast(index), macho_file), @@ -694,7 +694,7 @@ pub const DataInCode = struct { dices[next_dice].offset < end_off) : (next_dice += 1) {} - if (atom.alive.load(.seq_cst)) for (dices[start_dice..next_dice]) |d| { + if (atom.flags.alive) for (dices[start_dice..next_dice]) |d| { dice.entries.appendAssumeCapacity(.{ .offset = @intCast(atom.getAddress(macho_file) + d.offset - start_off - base_address), .length = d.length, diff --git a/src/link/MachO/thunks.zig b/src/link/MachO/thunks.zig index d832e6fcf6..2f2bd177e4 100644 --- a/src/link/MachO/thunks.zig +++ b/src/link/MachO/thunks.zig @@ -37,7 +37,7 @@ pub fn createThunks(sect_id: u8, macho_file: *MachO) !void { // Scan relocs in the group and create trampolines for any unreachable callsite try scanRelocs(thunk_index, gpa, atoms[start..i], macho_file); - thunk.value = try advance(header, thunk.size(), .@"4"); + thunk.value = advance(header, thunk.size(), .@"4"); log.debug("thunk({d}) : {}", .{ thunk_index, thunk.fmt(macho_file) }); } @@ -99,8 +99,8 @@ pub const Thunk = struct { return header.addr + thunk.value; } - pub fn getTargetAddress(thunk: Thunk, sym_index: Symbol.Index, macho_file: *MachO) u64 { - return thunk.getAddress(macho_file) + thunk.symbols.getIndex(sym_index).? * trampoline_size; + pub fn getTargetAddress(thunk: Thunk, ref: MachO.Ref, macho_file: *MachO) u64 { + return thunk.getAddress(macho_file) + thunk.symbols.getIndex(ref).? * trampoline_size; } pub fn write(thunk: Thunk, macho_file: *MachO, writer: anytype) !void { From 18778e2a0aab02c88694bfed5623ef7c205e12c7 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 10 Jul 2024 22:48:03 +0200 Subject: [PATCH 17/45] macho: bring back parts of ar --- src/link/MachO.zig | 2 +- src/link/MachO/Object.zig | 2 + src/link/MachO/Relocation.zig | 2 +- src/link/MachO/file.zig | 10 ++++- src/link/MachO/relocatable.zig | 77 +++++++++++++++++----------------- 5 files changed, 51 insertions(+), 42 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index a00c156b35..ee0eb9533c 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -356,7 +356,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n if (comp.verbose_link) try self.dumpArgv(comp); if (self.getZigObject()) |zo| try zo.flushModule(self, tid); - // if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path); + if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path); // if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path); var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index fe99df22bd..d6f4d903ea 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -77,6 +77,8 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); + log.debug("parsing {}", .{self.fmtPath()}); + const gpa = macho_file.base.comp.gpa; const handle = macho_file.getFileHandle(self.file_handle); const cpu_arch = macho_file.getTarget().cpu.arch; diff --git a/src/link/MachO/Relocation.zig b/src/link/MachO/Relocation.zig index 390e543ea9..bfb551f224 100644 --- a/src/link/MachO/Relocation.zig +++ b/src/link/MachO/Relocation.zig @@ -41,7 +41,7 @@ pub fn getGotTargetAddress(rel: Relocation, atom: Atom, macho_file: *MachO) u64 } pub fn getZigGotTargetAddress(rel: Relocation, macho_file: *MachO) u64 { - const zo = macho_file.getZigObject().?; + const zo = macho_file.getZigObject() orelse return 0; return switch (rel.tag) { .local => 0, .@"extern" => { diff --git a/src/link/MachO/file.zig b/src/link/MachO/file.zig index c1558292f5..5f52d6eb49 100644 --- a/src/link/MachO/file.zig +++ b/src/link/MachO/file.zig @@ -205,6 +205,8 @@ pub const File = union(enum) { pub fn updateArSymtab(file: File, ar_symtab: *Archive.ArSymtab, macho_file: *MachO) error{OutOfMemory}!void { return switch (file) { .dylib, .internal => unreachable, + // TODO + .zig_object => unreachable, inline else => |x| x.updateArSymtab(ar_symtab, macho_file), }; } @@ -212,7 +214,9 @@ pub const File = union(enum) { pub fn updateArSize(file: File, macho_file: *MachO) !void { return switch (file) { .dylib, .internal => unreachable, - .zig_object => |x| x.updateArSize(), + // TODO + .zig_object => unreachable, + // .zig_object => |x| x.updateArSize(), .object => |x| x.updateArSize(macho_file), }; } @@ -220,7 +224,9 @@ pub const File = union(enum) { pub fn writeAr(file: File, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void { return switch (file) { .dylib, .internal => unreachable, - .zig_object => |x| x.writeAr(ar_format, writer), + // TODO + .zig_object => unreachable, + // .zig_object => |x| x.writeAr(ar_format, writer), .object => |x| x.writeAr(ar_format, macho_file, writer), }; } diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index a2979eafe0..516b9a1c46 100644 --- a/src/link/MachO/relocatable.zig +++ b/src/link/MachO/relocatable.zig @@ -127,55 +127,56 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? if (comp.link_errors.items.len > 0) return error.FlushFailure; - // First, we flush relocatable object file generated with our backends. - if (macho_file.getZigObject()) |zo| { - zo.resolveSymbols(macho_file); - zo.asFile().markExportsRelocatable(macho_file); - zo.asFile().claimUnresolvedRelocatable(macho_file); - try macho_file.sortSections(); - try macho_file.addAtomsToSections(); - try calcSectionSizes(macho_file); - try createSegment(macho_file); - try allocateSections(macho_file); - allocateSegment(macho_file); + // TODO re-enable + // // First, we flush relocatable object file generated with our backends. + // if (macho_file.getZigObject()) |zo| { + // zo.resolveSymbols(macho_file); + // zo.asFile().markExportsRelocatable(macho_file); + // zo.asFile().claimUnresolvedRelocatable(macho_file); + // try macho_file.sortSections(); + // try macho_file.addAtomsToSections(); + // try calcSectionSizes(macho_file); + // try createSegment(macho_file); + // try allocateSections(macho_file); + // allocateSegment(macho_file); - var off = off: { - const seg = macho_file.segments.items[0]; - const off = math.cast(u32, seg.fileoff + seg.filesize) orelse return error.Overflow; - break :off mem.alignForward(u32, off, @alignOf(macho.relocation_info)); - }; - off = allocateSectionsRelocs(macho_file, off); + // var off = off: { + // const seg = macho_file.segments.items[0]; + // const off = math.cast(u32, seg.fileoff + seg.filesize) orelse return error.Overflow; + // break :off mem.alignForward(u32, off, @alignOf(macho.relocation_info)); + // }; + // off = allocateSectionsRelocs(macho_file, off); - if (build_options.enable_logging) { - state_log.debug("{}", .{macho_file.dumpState()}); - } + // if (build_options.enable_logging) { + // state_log.debug("{}", .{macho_file.dumpState()}); + // } - try macho_file.calcSymtabSize(); - try writeAtoms(macho_file); + // try macho_file.calcSymtabSize(); + // try writeAtoms(macho_file); - off = mem.alignForward(u32, off, @alignOf(u64)); - off = try macho_file.writeDataInCode(0, off); - off = mem.alignForward(u32, off, @alignOf(u64)); - off = try macho_file.writeSymtab(off); - off = mem.alignForward(u32, off, @alignOf(u64)); - off = try macho_file.writeStrtab(off); + // off = mem.alignForward(u32, off, @alignOf(u64)); + // off = try macho_file.writeDataInCode(0, off); + // off = mem.alignForward(u32, off, @alignOf(u64)); + // off = try macho_file.writeSymtab(off); + // off = mem.alignForward(u32, off, @alignOf(u64)); + // off = try macho_file.writeStrtab(off); - // In order to please Apple ld (and possibly other MachO linkers in the wild), - // we will now sanitize segment names of Zig-specific segments. - sanitizeZigSections(macho_file); + // // In order to please Apple ld (and possibly other MachO linkers in the wild), + // // we will now sanitize segment names of Zig-specific segments. + // sanitizeZigSections(macho_file); - const ncmds, const sizeofcmds = try writeLoadCommands(macho_file); - try writeHeader(macho_file, ncmds, sizeofcmds); + // const ncmds, const sizeofcmds = try writeLoadCommands(macho_file); + // try writeHeader(macho_file, ncmds, sizeofcmds); - // TODO we can avoid reading in the file contents we just wrote if we give the linker - // ability to write directly to a buffer. - try zo.readFileContents(off, macho_file); - } + // // TODO we can avoid reading in the file contents we just wrote if we give the linker + // // ability to write directly to a buffer. + // try zo.readFileContents(off, macho_file); + // } var files = std.ArrayList(File.Index).init(gpa); defer files.deinit(); try files.ensureTotalCapacityPrecise(macho_file.objects.items.len + 1); - if (macho_file.getZigObject()) |zo| files.appendAssumeCapacity(zo.index); + // if (macho_file.getZigObject()) |zo| files.appendAssumeCapacity(zo.index); for (macho_file.objects.items) |index| files.appendAssumeCapacity(index); const format: Archive.Format = .p32; From 05a790d784e4f1258d550e31178cf1e163750b6b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 11 Jul 2024 06:40:53 +0200 Subject: [PATCH 18/45] macho: link hello world in zig compiled with llvm --- src/link/MachO.zig | 7 +++++-- src/link/MachO/synthetic.zig | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index ee0eb9533c..ebe280ca76 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1542,6 +1542,9 @@ fn scanRelocs(self: *MachO) !void { for (self.objects.items) |index| { try self.getFile(index).?.createSymbolIndirection(self); } + for (self.dylibs.items) |index| { + try self.getFile(index).?.createSymbolIndirection(self); + } if (self.getInternalObject()) |obj| { try obj.asFile().createSymbolIndirection(self); } @@ -2358,9 +2361,9 @@ fn allocateLinkeditSegment(self: *MachO) !void { fn resizeSections(self: *MachO) !void { const slice = self.sections.slice(); - for (slice.items(.header), slice.items(.atoms), slice.items(.out)) |header, atoms, *out| { - if (atoms.items.len == 0) continue; + for (slice.items(.header), slice.items(.out), 0..) |header, *out, n_sect| { if (header.isZerofill()) continue; + if (self.isZigSection(@intCast(n_sect))) continue; // TODO this is horrible const cpu_arch = self.getTarget().cpu.arch; try out.resize(self.base.comp.gpa, header.size); const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0; diff --git a/src/link/MachO/synthetic.zig b/src/link/MachO/synthetic.zig index 14bc97c229..8b5a87c26e 100644 --- a/src/link/MachO/synthetic.zig +++ b/src/link/MachO/synthetic.zig @@ -91,9 +91,9 @@ pub const ZigGotSection = struct { _ = unused_fmt_string; const zig_got = ctx.zig_got; const macho_file = ctx.macho_file; - const zo = macho_file.getZigObject().?; try writer.writeAll("__zig_got\n"); for (zig_got.entries.items, 0..) |entry, index| { + const zo = macho_file.getZigObject().?; const symbol = zo.symbols.items[entry]; try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{ index, From b4e6b3c53c0fd54b43f648d9a207e8ff592dd0ff Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 11 Jul 2024 13:46:54 +0200 Subject: [PATCH 19/45] macho: bring back parts of r mode --- src/link/MachO.zig | 2 +- src/link/MachO/relocatable.zig | 108 +++++++++++++++++---------------- 2 files changed, 56 insertions(+), 54 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index ebe280ca76..98c1eb2339 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -357,7 +357,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n if (self.getZigObject()) |zo| try zo.flushModule(self, tid); if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path); - // if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path); + if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path); var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); defer positionals.deinit(); diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index 516b9a1c46..9dcd7a64ff 100644 --- a/src/link/MachO/relocatable.zig +++ b/src/link/MachO/relocatable.zig @@ -26,68 +26,70 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c return; } - for (positionals.items) |obj| { - macho_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) { - error.MalformedObject, - error.MalformedArchive, - error.InvalidCpuArch, - error.InvalidTarget, - => continue, // already reported - error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an object file", .{}), - else => |e| try macho_file.reportParseError( - obj.path, - "unexpected error: parsing input file failed with error {s}", - .{@errorName(e)}, - ), - }; - } + @panic("TODO -r mode"); - if (comp.link_errors.items.len > 0) return error.FlushFailure; + // for (positionals.items) |obj| { + // macho_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) { + // error.MalformedObject, + // error.MalformedArchive, + // error.InvalidCpuArch, + // error.InvalidTarget, + // => continue, // already reported + // error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an object file", .{}), + // else => |e| try macho_file.reportParseError( + // obj.path, + // "unexpected error: parsing input file failed with error {s}", + // .{@errorName(e)}, + // ), + // }; + // } - try macho_file.addUndefinedGlobals(); - try macho_file.resolveSymbols(); - try macho_file.parseDebugInfo(); - try macho_file.dedupLiterals(); - markExports(macho_file); - claimUnresolved(macho_file); - try initOutputSections(macho_file); - try macho_file.sortSections(); - try macho_file.addAtomsToSections(); - try calcSectionSizes(macho_file); + // if (comp.link_errors.items.len > 0) return error.FlushFailure; - try createSegment(macho_file); - try allocateSections(macho_file); - allocateSegment(macho_file); + // try macho_file.addUndefinedGlobals(); + // try macho_file.resolveSymbols(); + // try macho_file.parseDebugInfo(); + // try macho_file.dedupLiterals(); + // markExports(macho_file); + // claimUnresolved(macho_file); + // try initOutputSections(macho_file); + // try macho_file.sortSections(); + // try macho_file.addAtomsToSections(); + // try calcSectionSizes(macho_file); - var off = off: { - const seg = macho_file.segments.items[0]; - const off = math.cast(u32, seg.fileoff + seg.filesize) orelse return error.Overflow; - break :off mem.alignForward(u32, off, @alignOf(macho.relocation_info)); - }; - off = allocateSectionsRelocs(macho_file, off); + // try createSegment(macho_file); + // try allocateSections(macho_file); + // allocateSegment(macho_file); - if (build_options.enable_logging) { - state_log.debug("{}", .{macho_file.dumpState()}); - } + // var off = off: { + // const seg = macho_file.segments.items[0]; + // const off = math.cast(u32, seg.fileoff + seg.filesize) orelse return error.Overflow; + // break :off mem.alignForward(u32, off, @alignOf(macho.relocation_info)); + // }; + // off = allocateSectionsRelocs(macho_file, off); - try macho_file.calcSymtabSize(); - try writeAtoms(macho_file); - try writeCompactUnwind(macho_file); - try writeEhFrame(macho_file); + // if (build_options.enable_logging) { + // state_log.debug("{}", .{macho_file.dumpState()}); + // } - off = mem.alignForward(u32, off, @alignOf(u64)); - off = try macho_file.writeDataInCode(0, off); - off = mem.alignForward(u32, off, @alignOf(u64)); - off = try macho_file.writeSymtab(off); - off = mem.alignForward(u32, off, @alignOf(u64)); - off = try macho_file.writeStrtab(off); + // try macho_file.calcSymtabSize(); + // try writeAtoms(macho_file); + // try writeCompactUnwind(macho_file); + // try writeEhFrame(macho_file); - // In order to please Apple ld (and possibly other MachO linkers in the wild), - // we will now sanitize segment names of Zig-specific segments. - sanitizeZigSections(macho_file); + // off = mem.alignForward(u32, off, @alignOf(u64)); + // off = try macho_file.writeDataInCode(0, off); + // off = mem.alignForward(u32, off, @alignOf(u64)); + // off = try macho_file.writeSymtab(off); + // off = mem.alignForward(u32, off, @alignOf(u64)); + // off = try macho_file.writeStrtab(off); - const ncmds, const sizeofcmds = try writeLoadCommands(macho_file); - try writeHeader(macho_file, ncmds, sizeofcmds); + // // In order to please Apple ld (and possibly other MachO linkers in the wild), + // // we will now sanitize segment names of Zig-specific segments. + // sanitizeZigSections(macho_file); + + // const ncmds, const sizeofcmds = try writeLoadCommands(macho_file); + // try writeHeader(macho_file, ncmds, sizeofcmds); } pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { From 3d58faed12d018d70a4cd28aff89b733621a0259 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 11 Jul 2024 14:19:42 +0200 Subject: [PATCH 20/45] macho: we do not yet support interposable symbols --- src/link/MachO/Object.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index d6f4d903ea..dc62bc6500 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -840,7 +840,7 @@ fn initSymbols(self: *Object, allocator: Allocator, macho_file: *MachO) !void { symbol.flags.tentative = nlist.tentative(); symbol.flags.no_dead_strip = symbol.flags.no_dead_strip or nlist.noDeadStrip(); symbol.flags.dyn_ref = nlist.n_desc & macho.REFERENCED_DYNAMICALLY != 0; - symbol.flags.interposable = nlist.ext() and (nlist.sect() or nlist.abs()) and macho_file.base.isDynLib() and !nlist.pext(); + symbol.flags.interposable = false; // TODO // symbol.flags.interposable = nlist.ext() and (nlist.sect() or nlist.abs()) and macho_file.base.isDynLib() and macho_file.options.namespace == .flat and !nlist.pext(); From 90c54f1eb6d34fb703b6c4e093580a0f2f70d559 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 11 Jul 2024 14:49:16 +0200 Subject: [PATCH 21/45] macho: fix symbol visibility merging logic --- src/link/MachO/Object.zig | 2 +- src/link/MachO/Symbol.zig | 15 ++++++++++++++- src/link/MachO/ZigObject.zig | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index dc62bc6500..d283b35a88 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -1536,7 +1536,7 @@ pub fn mergeSymbolVisibility(self: *Object, macho_file: *MachO) void { for (self.symbols.items, 0..) |sym, i| { const ref = self.getSymbolRef(@intCast(i), macho_file); const global = ref.getSymbol(macho_file) orelse continue; - if (global.visibility != .global) { + if (sym.visibility.rank() < global.visibility.rank()) { global.visibility = sym.visibility; } if (sym.flags.weak_ref) { diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index 8cb198e7d7..72cd55bf5a 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -331,9 +331,14 @@ fn format2( if (symbol.getAtom(ctx.macho_file)) |atom| { try writer.print(" : atom({d})", .{atom.atom_index}); } - var buf: [2]u8 = .{'_'} ** 2; + var buf: [3]u8 = .{'_'} ** 3; if (symbol.flags.@"export") buf[0] = 'E'; if (symbol.flags.import) buf[1] = 'I'; + switch (symbol.visibility) { + .local => buf[2] = 'L', + .hidden => buf[2] = 'H', + .global => buf[2] = 'G', + } try writer.print(" : {s}", .{&buf}); if (symbol.flags.weak) try writer.writeAll(" : weak"); if (symbol.isSymbolStab(ctx.macho_file)) try writer.writeAll(" : stab"); @@ -402,6 +407,14 @@ pub const Visibility = enum { global, hidden, local, + + pub fn rank(vis: Visibility) u2 { + return switch (vis) { + .local => 2, + .hidden => 1, + .global => 0, + }; + } }; pub const Extra = struct { diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 544d778f39..661a88904e 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -267,7 +267,7 @@ pub fn mergeSymbolVisibility(self: *ZigObject, macho_file: *MachO) void { for (self.symbols.items, 0..) |sym, i| { const ref = self.getSymbolRef(@intCast(i), macho_file); const global = ref.getSymbol(macho_file) orelse continue; - if (global.visibility != .global) { + if (sym.visibility.rank() < global.visibility.rank()) { global.visibility = sym.visibility; } if (sym.flags.weak_ref) { From b62281a9c87caab22ff22fb783751a1cd784bcaa Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 11 Jul 2024 22:35:52 +0200 Subject: [PATCH 22/45] macho: re-enable relocatable mode --- src/link/MachO.zig | 13 +- src/link/MachO/Atom.zig | 1 + src/link/MachO/Object.zig | 64 +--- src/link/MachO/file.zig | 88 ++++++ src/link/MachO/relocatable.zig | 537 ++++++++++++++------------------- 5 files changed, 340 insertions(+), 363 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 98c1eb2339..2b4005c1fe 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -290,10 +290,16 @@ pub fn deinit(self: *MachO) void { self.dylibs.deinit(gpa); self.segments.deinit(gpa); - for (self.sections.items(.atoms), self.sections.items(.out), self.sections.items(.thunks)) |*atoms, *out, *thnks| { + for ( + self.sections.items(.atoms), + self.sections.items(.out), + self.sections.items(.thunks), + self.sections.items(.relocs), + ) |*atoms, *out, *thnks, *relocs| { atoms.deinit(gpa); out.deinit(gpa); thnks.deinit(gpa); + relocs.deinit(gpa); } self.sections.deinit(gpa); @@ -1457,10 +1463,10 @@ pub fn dedupLiterals(self: *MachO) !void { fn claimUnresolved(self: *MachO) void { if (self.getZigObject()) |zo| { - zo.claimUnresolved(self); + zo.asFile().claimUnresolved(self); } for (self.objects.items) |index| { - self.getFile(index).?.object.claimUnresolved(self); + self.getFile(index).?.claimUnresolved(self); } } @@ -3987,6 +3993,7 @@ const Section = struct { last_atom_index: Atom.Index = 0, thunks: std.ArrayListUnmanaged(Thunk.Index) = .{}, out: std.ArrayListUnmanaged(u8) = .{}, + relocs: std.ArrayListUnmanaged(macho.relocation_info) = .{}, }; pub const LiteralPool = struct { diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index d03727aa6a..b928542600 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -1008,6 +1008,7 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r .r_extern = 0, .r_type = @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_ADDEND), }; + i += 1; } const r_type: macho.reloc_type_arm64 = switch (rel.type) { diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index d283b35a88..7ab128aaeb 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -1636,56 +1636,6 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void { } } -pub fn claimUnresolved(self: *Object, macho_file: *MachO) void { - const tracy = trace(@src()); - defer tracy.end(); - - for (self.symbols.items, 0..) |*sym, i| { - const nlist = self.symtab.items(.nlist)[i]; - if (!nlist.ext()) continue; - if (!nlist.undf()) continue; - - if (self.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue; - - const is_import = switch (macho_file.undefined_treatment) { - .@"error" => false, - .warn, .suppress => nlist.weakRef(), - .dynamic_lookup => true, - }; - if (is_import) { - sym.value = 0; - sym.atom_ref = .{ .index = 0, .file = 0 }; - sym.flags.weak = false; - sym.flags.weak_ref = nlist.weakRef(); - sym.flags.import = is_import; - sym.visibility = .global; - - const idx = self.globals.items[i]; - macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = self.index }; - } - } -} - -pub fn claimUnresolvedRelocatable(self: *Object, macho_file: *MachO) void { - const tracy = trace(@src()); - defer tracy.end(); - - for (self.symbols.items, self.symtab.items(.nlist), 0..) |*sym, nlist, i| { - if (!nlist.ext()) continue; - if (!nlist.undf()) continue; - if (self.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue; - - sym.value = 0; - sym.atom_ref = .{ .index = 0, .file = 0 }; - sym.flags.weak_ref = nlist.weakRef(); - sym.flags.import = true; - sym.visibility = .global; - - const idx = self.globals.items[i]; - macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = self.index }; - } -} - fn addSection(self: *Object, allocator: Allocator, segname: []const u8, sectname: []const u8) !u8 { const n_sect = @as(u8, @intCast(try self.sections.addOne(allocator))); self.sections.set(n_sect, .{ @@ -1936,7 +1886,7 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - const gpa = macho_file.base.allocator; + const gpa = macho_file.base.comp.gpa; const headers = self.sections.items(.header); const sections_data = try gpa.alloc([]const u8, headers.len); defer { @@ -1995,15 +1945,17 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); + const cpu_arch = macho_file.getTarget().cpu.arch; + const addReloc = struct { - fn addReloc(offset: u32, cpu_arch: std.Target.Cpu.Arch) !macho.relocation_info { + fn addReloc(offset: u32, arch: std.Target.Cpu.Arch) !macho.relocation_info { return .{ .r_address = math.cast(i32, offset) orelse return error.Overflow, .r_symbolnum = 0, .r_pcrel = 0, .r_length = 3, .r_extern = 0, - .r_type = switch (cpu_arch) { + .r_type = switch (arch) { .aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED), .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED), else => unreachable, @@ -2039,7 +1991,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void { const atom = rec.getAtom(macho_file); const addr = rec.getAtomAddress(macho_file); out.rangeStart = addr; - var reloc = try addReloc(offset, macho_file.options.cpu_arch.?); + var reloc = try addReloc(offset, cpu_arch); reloc.r_symbolnum = atom.out_n_sect + 1; relocs[reloc_index] = reloc; reloc_index += 1; @@ -2048,7 +2000,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void { // Personality function if (rec.getPersonality(macho_file)) |sym| { const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow; - var reloc = try addReloc(offset + 16, macho_file.options.cpu_arch.?); + var reloc = try addReloc(offset + 16, cpu_arch); reloc.r_symbolnum = r_symbolnum; reloc.r_extern = 1; relocs[reloc_index] = reloc; @@ -2059,7 +2011,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void { if (rec.getLsdaAtom(macho_file)) |atom| { const addr = rec.getLsdaAddress(macho_file); out.lsda = addr; - var reloc = try addReloc(offset + 24, macho_file.options.cpu_arch.?); + var reloc = try addReloc(offset + 24, cpu_arch); reloc.r_symbolnum = atom.out_n_sect + 1; relocs[reloc_index] = reloc; reloc_index += 1; diff --git a/src/link/MachO/file.zig b/src/link/MachO/file.zig index 5f52d6eb49..4957ed71c2 100644 --- a/src/link/MachO/file.zig +++ b/src/link/MachO/file.zig @@ -118,7 +118,24 @@ pub const File = union(enum) { }; } + pub fn getNlists(file: File) []macho.nlist_64 { + return switch (file) { + .dylib => unreachable, + .internal => |x| x.symtab.items, + inline else => |x| x.symtab.items(.nlist), + }; + } + + pub fn getGlobals(file: File) []MachO.SymbolResolver.Index { + return switch (file) { + inline else => |x| x.globals.items, + }; + } + pub fn markImportsExports(file: File, macho_file: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); + const nsyms = switch (file) { .dylib => unreachable, inline else => |x| x.symbols.items.len, @@ -138,7 +155,25 @@ pub const File = union(enum) { } } + pub fn markExportsRelocatable(file: File, macho_file: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); + + assert(file == .object or file == .zig_object); + + for (file.getSymbols(), 0..) |*sym, i| { + const ref = file.getSymbolRef(@intCast(i), macho_file); + const other_file = ref.getFile(macho_file) orelse continue; + if (other_file.getIndex() != file.getIndex()) continue; + if (sym.visibility != .global) continue; + sym.flags.@"export" = true; + } + } + pub fn createSymbolIndirection(file: File, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + const nsyms = switch (file) { inline else => |x| x.symbols.items.len, }; @@ -166,6 +201,59 @@ pub const File = union(enum) { } } + pub fn claimUnresolved(file: File, macho_file: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); + + assert(file == .object or file == .zig_object); + + for (file.getSymbols(), file.getNlists(), 0..) |*sym, nlist, i| { + if (!nlist.ext()) continue; + if (!nlist.undf()) continue; + + if (file.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue; + + const is_import = switch (macho_file.undefined_treatment) { + .@"error" => false, + .warn, .suppress => nlist.weakRef(), + .dynamic_lookup => true, + }; + if (is_import) { + sym.value = 0; + sym.atom_ref = .{ .index = 0, .file = 0 }; + sym.flags.weak = false; + sym.flags.weak_ref = nlist.weakRef(); + sym.flags.import = is_import; + sym.visibility = .global; + + const idx = file.getGlobals()[i]; + macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = file.getIndex() }; + } + } + } + + pub fn claimUnresolvedRelocatable(file: File, macho_file: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); + + assert(file == .object or file == .zig_object); + + for (file.getSymbols(), file.getNlists(), 0..) |*sym, nlist, i| { + if (!nlist.ext()) continue; + if (!nlist.undf()) continue; + if (file.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue; + + sym.value = 0; + sym.atom_ref = .{ .index = 0, .file = 0 }; + sym.flags.weak_ref = nlist.weakRef(); + sym.flags.import = true; + sym.visibility = .global; + + const idx = file.getGlobals()[i]; + macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = file.getIndex() }; + } + } + pub fn initOutputSections(file: File, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index 9dcd7a64ff..0095118c86 100644 --- a/src/link/MachO/relocatable.zig +++ b/src/link/MachO/relocatable.zig @@ -26,70 +26,51 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c return; } - @panic("TODO -r mode"); + for (positionals.items) |obj| { + macho_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) { + error.MalformedObject, + error.MalformedArchive, + error.InvalidCpuArch, + error.InvalidTarget, + => continue, // already reported + error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an object file", .{}), + else => |e| try macho_file.reportParseError( + obj.path, + "unexpected error: parsing input file failed with error {s}", + .{@errorName(e)}, + ), + }; + } - // for (positionals.items) |obj| { - // macho_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) { - // error.MalformedObject, - // error.MalformedArchive, - // error.InvalidCpuArch, - // error.InvalidTarget, - // => continue, // already reported - // error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an object file", .{}), - // else => |e| try macho_file.reportParseError( - // obj.path, - // "unexpected error: parsing input file failed with error {s}", - // .{@errorName(e)}, - // ), - // }; - // } + if (comp.link_errors.items.len > 0) return error.FlushFailure; - // if (comp.link_errors.items.len > 0) return error.FlushFailure; + try macho_file.resolveSymbols(); + try macho_file.dedupLiterals(); + markExports(macho_file); + claimUnresolved(macho_file); + try initOutputSections(macho_file); + try macho_file.sortSections(); + try macho_file.addAtomsToSections(); + try calcSectionSizes(macho_file); - // try macho_file.addUndefinedGlobals(); - // try macho_file.resolveSymbols(); - // try macho_file.parseDebugInfo(); - // try macho_file.dedupLiterals(); - // markExports(macho_file); - // claimUnresolved(macho_file); - // try initOutputSections(macho_file); - // try macho_file.sortSections(); - // try macho_file.addAtomsToSections(); - // try calcSectionSizes(macho_file); + try createSegment(macho_file); + try allocateSections(macho_file); + allocateSegment(macho_file); - // try createSegment(macho_file); - // try allocateSections(macho_file); - // allocateSegment(macho_file); + if (build_options.enable_logging) { + state_log.debug("{}", .{macho_file.dumpState()}); + } - // var off = off: { - // const seg = macho_file.segments.items[0]; - // const off = math.cast(u32, seg.fileoff + seg.filesize) orelse return error.Overflow; - // break :off mem.alignForward(u32, off, @alignOf(macho.relocation_info)); - // }; - // off = allocateSectionsRelocs(macho_file, off); + try writeSections(macho_file); + sortRelocs(macho_file); + try writeSectionsToFile(macho_file); - // if (build_options.enable_logging) { - // state_log.debug("{}", .{macho_file.dumpState()}); - // } + // In order to please Apple ld (and possibly other MachO linkers in the wild), + // we will now sanitize segment names of Zig-specific segments. + sanitizeZigSections(macho_file); - // try macho_file.calcSymtabSize(); - // try writeAtoms(macho_file); - // try writeCompactUnwind(macho_file); - // try writeEhFrame(macho_file); - - // off = mem.alignForward(u32, off, @alignOf(u64)); - // off = try macho_file.writeDataInCode(0, off); - // off = mem.alignForward(u32, off, @alignOf(u64)); - // off = try macho_file.writeSymtab(off); - // off = mem.alignForward(u32, off, @alignOf(u64)); - // off = try macho_file.writeStrtab(off); - - // // In order to please Apple ld (and possibly other MachO linkers in the wild), - // // we will now sanitize segment names of Zig-specific segments. - // sanitizeZigSections(macho_file); - - // const ncmds, const sizeofcmds = try writeLoadCommands(macho_file); - // try writeHeader(macho_file, ncmds, sizeofcmds); + const ncmds, const sizeofcmds = try writeLoadCommands(macho_file); + try writeHeader(macho_file, ncmds, sizeofcmds); } pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { @@ -353,9 +334,9 @@ pub fn claimUnresolved(macho_file: *MachO) void { fn initOutputSections(macho_file: *MachO) !void { for (macho_file.objects.items) |index| { - const object = macho_file.getFile(index).?.object; - for (object.atoms.items) |atom_index| { - const atom = macho_file.getAtom(atom_index) orelse continue; + const file = macho_file.getFile(index).?; + for (file.getAtoms()) |atom_index| { + const atom = file.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file); } @@ -383,69 +364,141 @@ fn calcSectionSizes(macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - const slice = macho_file.sections.slice(); - for (slice.items(.header), slice.items(.atoms)) |*header, atoms| { + for (macho_file.sections.items(.atoms), 0..) |atoms, i| { if (atoms.items.len == 0) continue; - for (atoms.items) |atom_index| { - const atom = macho_file.getAtom(atom_index).?; - const atom_alignment = atom.alignment.toByteUnits() orelse 1; - const offset = mem.alignForward(u64, header.size, atom_alignment); - const padding = offset - header.size; - atom.value = offset; - header.size += padding + atom.size; - header.@"align" = @max(header.@"align", atom.alignment.toLog2Units()); - header.nreloc += atom.calcNumRelocs(macho_file); + calcSectionSize(macho_file, @intCast(i)); + } + + if (macho_file.eh_frame_sect_index) |_| { + try calcEhFrameSize(macho_file); + } + + for (macho_file.objects.items) |index| { + if (macho_file.unwind_info_sect_index) |_| { + macho_file.getFile(index).?.object.calcCompactUnwindSizeRelocatable(macho_file); } + macho_file.getFile(index).?.calcSymtabSize(macho_file); } - if (macho_file.unwind_info_sect_index) |index| { - calcCompactUnwindSize(macho_file, index); - } + try macho_file.data_in_code.updateSize(macho_file); - if (macho_file.eh_frame_sect_index) |index| { - const sect = &macho_file.sections.items(.header)[index]; - sect.size = try eh_frame.calcSize(macho_file); - sect.@"align" = 3; - sect.nreloc = eh_frame.calcNumRelocs(macho_file); - } + calcCompactUnwindSize(macho_file); + calcSymtabSize(macho_file); - if (macho_file.getZigObject()) |zo| { - for (zo.atoms.items) |atom_index| { - const atom = macho_file.getAtom(atom_index) orelse continue; - if (!atom.flags.alive) continue; - const header = &macho_file.sections.items(.header)[atom.out_n_sect]; - if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue; - header.nreloc += atom.calcNumRelocs(macho_file); - } + // TODO + // if (macho_file.getZigObject()) |zo| { + // for (zo.atoms.items) |atom_index| { + // const atom = macho_file.getAtom(atom_index) orelse continue; + // if (!atom.flags.alive) continue; + // const header = &macho_file.sections.items(.header)[atom.out_n_sect]; + // if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue; + // header.nreloc += atom.calcNumRelocs(macho_file); + // } + // } +} + +fn calcSectionSize(macho_file: *MachO, sect_id: u8) void { + const tracy = trace(@src()); + defer tracy.end(); + + const slice = macho_file.sections.slice(); + const header = &slice.items(.header)[sect_id]; + const atoms = slice.items(.atoms)[sect_id].items; + for (atoms) |ref| { + const atom = ref.getAtom(macho_file).?; + const atom_alignment = atom.alignment.toByteUnits() orelse 1; + const offset = mem.alignForward(u64, header.size, atom_alignment); + const padding = offset - header.size; + atom.value = offset; + header.size += padding + atom.size; + header.@"align" = @max(header.@"align", atom.alignment.toLog2Units()); + const nreloc = atom.calcNumRelocs(macho_file); + atom.addExtra(.{ .rel_out_index = header.nreloc, .rel_out_count = nreloc }, macho_file); + header.nreloc += nreloc; } } -fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void { - var size: u32 = 0; +fn calcEhFrameSize(macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const header = &macho_file.sections.items(.header)[macho_file.eh_frame_sect_index.?]; + header.size = try eh_frame.calcSize(macho_file); + header.@"align" = 3; + header.nreloc = eh_frame.calcNumRelocs(macho_file); +} + +fn calcCompactUnwindSize(macho_file: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); + + var nrec: u32 = 0; var nreloc: u32 = 0; for (macho_file.objects.items) |index| { - const object = macho_file.getFile(index).?.object; - for (object.unwind_records_indexes.items) |irec| { - const rec = object.getUnwindRecord(irec); - if (!rec.alive) continue; - size += @sizeOf(macho.compact_unwind_entry); - nreloc += 1; - if (rec.getPersonality(macho_file)) |_| { - nreloc += 1; - } - if (rec.getLsdaAtom(macho_file)) |_| { - nreloc += 1; - } - } + const ctx = &macho_file.getFile(index).?.object.compact_unwind_ctx; + ctx.rec_index = nrec; + ctx.reloc_index = nreloc; + nrec += ctx.rec_count; + nreloc += ctx.reloc_count; } - const sect = &macho_file.sections.items(.header)[sect_index]; - sect.size = size; + const sect = &macho_file.sections.items(.header)[macho_file.unwind_info_sect_index.?]; + sect.size = nrec * @sizeOf(macho.compact_unwind_entry); sect.nreloc = nreloc; sect.@"align" = 3; } +fn calcSymtabSize(macho_file: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); + + var nlocals: u32 = 0; + var nstabs: u32 = 0; + var nexports: u32 = 0; + var nimports: u32 = 0; + var strsize: u32 = 1; + + for (macho_file.objects.items) |index| { + const object = macho_file.getFile(index).?.object; + const ctx = &object.output_symtab_ctx; + ctx.ilocal = nlocals; + ctx.istab = nstabs; + ctx.iexport = nexports; + ctx.iimport = nimports; + ctx.stroff = strsize; + nlocals += ctx.nlocals; + nstabs += ctx.nstabs; + nexports += ctx.nexports; + nimports += ctx.nimports; + strsize += ctx.strsize; + } + + for (macho_file.objects.items) |index| { + const object = macho_file.getFile(index).?.object; + const ctx = &object.output_symtab_ctx; + ctx.istab += nlocals; + ctx.iexport += nlocals + nstabs; + ctx.iimport += nlocals + nstabs + nexports; + } + + { + const cmd = &macho_file.symtab_cmd; + cmd.nsyms = nlocals + nstabs + nexports + nimports; + cmd.strsize = strsize; + } + + { + const cmd = &macho_file.dysymtab_cmd; + cmd.ilocalsym = 0; + cmd.nlocalsym = nlocals + nstabs; + cmd.iextdefsym = nlocals + nstabs; + cmd.nextdefsym = nexports; + cmd.iundefsym = nlocals + nstabs + nexports; + cmd.nundefsym = nimports; + } +} + fn allocateSections(macho_file: *MachO) !void { const slice = macho_file.sections.slice(); for (slice.items(.header)) |*header| { @@ -463,6 +516,37 @@ fn allocateSections(macho_file: *MachO) !void { } header.size = needed_size; } + + var fileoff: u32 = 0; + for (slice.items(.header)) |header| { + fileoff = @max(fileoff, header.offset + @as(u32, @intCast(header.size))); + } + + for (slice.items(.header)) |*header| { + if (header.nreloc == 0) continue; + header.reloff = mem.alignForward(u32, fileoff, @alignOf(macho.relocation_info)); + fileoff = header.reloff + header.nreloc * @sizeOf(macho.relocation_info); + } + + // In -r mode, there is no LINKEDIT segment and so we allocate required LINKEDIT commands + // as if they were detached or part of the single segment. + + // DATA_IN_CODE + { + const cmd = &macho_file.data_in_code_cmd; + cmd.dataoff = fileoff; + fileoff += cmd.datasize; + fileoff = mem.alignForward(u32, fileoff, @alignOf(u64)); + } + + // SYMTAB + { + const cmd = &macho_file.symtab_cmd; + cmd.symoff = fileoff; + fileoff += cmd.nsyms * @sizeOf(macho.nlist_64); + fileoff = mem.alignForward(u32, fileoff, @alignOf(u32)); + cmd.stroff = fileoff; + } } /// Renames segment names in Zig sections to standard MachO segment names such as @@ -525,232 +609,77 @@ fn allocateSegment(macho_file: *MachO) void { seg.filesize = fileoff - seg.fileoff; } -fn allocateSectionsRelocs(macho_file: *MachO, off: u32) u32 { - var fileoff = off; - const slice = macho_file.sections.slice(); - for (slice.items(.header)) |*header| { - if (header.nreloc == 0) continue; - header.reloff = mem.alignForward(u32, fileoff, @alignOf(macho.relocation_info)); - fileoff = header.reloff + header.nreloc * @sizeOf(macho.relocation_info); - } - return fileoff; -} - // We need to sort relocations in descending order to be compatible with Apple's linker. fn sortReloc(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation_info) bool { _ = ctx; return lhs.r_address > rhs.r_address; } -fn writeAtoms(macho_file: *MachO) !void { +fn sortRelocs(macho_file: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); + + for (macho_file.sections.items(.relocs)) |*relocs| { + mem.sort(macho.relocation_info, relocs.items, {}, sortReloc); + } +} + +fn writeSections(macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); const gpa = macho_file.base.comp.gpa; const cpu_arch = macho_file.getTarget().cpu.arch; const slice = macho_file.sections.slice(); - - var relocs = std.ArrayList(macho.relocation_info).init(gpa); - defer relocs.deinit(); - - for (slice.items(.header), slice.items(.atoms), 0..) |header, atoms, i| { - if (atoms.items.len == 0) continue; + for (slice.items(.header), slice.items(.out), slice.items(.relocs)) |header, *out, *relocs| { if (header.isZerofill()) continue; - if (macho_file.isZigSection(@intCast(i)) or macho_file.isDebugSection(@intCast(i))) continue; - - const size = math.cast(usize, header.size) orelse return error.Overflow; - const code = try gpa.alloc(u8, size); - defer gpa.free(code); + try out.resize(gpa, header.size); const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0; - @memset(code, padding_byte); - - try relocs.ensureTotalCapacity(header.nreloc); - - for (atoms.items) |atom_index| { - const atom = macho_file.getAtom(atom_index).?; - assert(atom.flags.alive); - const off = math.cast(usize, atom.value) orelse return error.Overflow; - const atom_size = math.cast(usize, atom.size) orelse return error.Overflow; - try atom.getData(macho_file, code[off..][0..atom_size]); - try atom.writeRelocs(macho_file, code[off..][0..atom_size], &relocs); - } - - assert(relocs.items.len == header.nreloc); - - mem.sort(macho.relocation_info, relocs.items, {}, sortReloc); - - // TODO scattered writes? - try macho_file.base.file.?.pwriteAll(code, header.offset); - try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff); - - relocs.clearRetainingCapacity(); + @memset(out.items, padding_byte); + try relocs.resize(gpa, header.nreloc); } - if (macho_file.getZigObject()) |zo| { - // TODO: this is ugly; perhaps we should aggregrate before? - var zo_relocs = std.AutoArrayHashMap(u8, std.ArrayList(macho.relocation_info)).init(gpa); - defer { - for (zo_relocs.values()) |*list| { - list.deinit(); - } - zo_relocs.deinit(); - } + const cmd = macho_file.symtab_cmd; + try macho_file.symtab.resize(gpa, cmd.nsyms); + try macho_file.strtab.resize(gpa, cmd.strsize); + macho_file.strtab.items[0] = 0; - for (macho_file.sections.items(.header), 0..) |header, n_sect| { - if (header.isZerofill()) continue; - if (!macho_file.isZigSection(@intCast(n_sect)) and !macho_file.isDebugSection(@intCast(n_sect))) continue; - const gop = try zo_relocs.getOrPut(@intCast(n_sect)); - if (gop.found_existing) continue; - gop.value_ptr.* = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc); - } - - for (zo.atoms.items) |atom_index| { - const atom = macho_file.getAtom(atom_index) orelse continue; - if (!atom.flags.alive) continue; - const header = macho_file.sections.items(.header)[atom.out_n_sect]; - if (header.isZerofill()) continue; - if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue; - if (atom.getRelocs(macho_file).len == 0) continue; - const atom_size = math.cast(usize, atom.size) orelse return error.Overflow; - const code = try gpa.alloc(u8, atom_size); - defer gpa.free(code); - atom.getData(macho_file, code) catch |err| switch (err) { - error.InputOutput => { - try macho_file.reportUnexpectedError("fetching code for '{s}' failed", .{ - atom.getName(macho_file), - }); - return error.FlushFailure; - }, - else => |e| { - try macho_file.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{ - atom.getName(macho_file), - @errorName(e), - }); - return error.FlushFailure; - }, - }; - const file_offset = header.offset + atom.value; - const rels = zo_relocs.getPtr(atom.out_n_sect).?; - try atom.writeRelocs(macho_file, code, rels); - try macho_file.base.file.?.pwriteAll(code, file_offset); - } - - for (zo_relocs.keys(), zo_relocs.values()) |sect_id, rels| { - const header = macho_file.sections.items(.header)[sect_id]; - assert(rels.items.len == header.nreloc); - mem.sort(macho.relocation_info, rels.items, {}, sortReloc); - try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(rels.items), header.reloff); - } - } -} - -fn writeCompactUnwind(macho_file: *MachO) !void { - const sect_index = macho_file.unwind_info_sect_index orelse return; - const gpa = macho_file.base.comp.gpa; - const header = macho_file.sections.items(.header)[sect_index]; - - const nrecs = math.cast(usize, @divExact(header.size, @sizeOf(macho.compact_unwind_entry))) orelse return error.Overflow; - var entries = try std.ArrayList(macho.compact_unwind_entry).initCapacity(gpa, nrecs); - defer entries.deinit(); - - var relocs = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc); - defer relocs.deinit(); - - const addReloc = struct { - fn addReloc(offset: i32, cpu_arch: std.Target.Cpu.Arch) macho.relocation_info { - return .{ - .r_address = offset, - .r_symbolnum = 0, - .r_pcrel = 0, - .r_length = 3, - .r_extern = 0, - .r_type = switch (cpu_arch) { - .aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED), - .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED), - else => unreachable, - }, - }; - } - }.addReloc; - - var offset: i32 = 0; for (macho_file.objects.items) |index| { - const object = macho_file.getFile(index).?.object; - for (object.unwind_records_indexes.items) |irec| { - const rec = object.getUnwindRecord(irec); - if (!rec.alive) continue; - - var out: macho.compact_unwind_entry = .{ - .rangeStart = 0, - .rangeLength = rec.length, - .compactUnwindEncoding = rec.enc.enc, - .personalityFunction = 0, - .lsda = 0, - }; - - { - // Function address - const atom = rec.getAtom(macho_file); - const addr = rec.getAtomAddress(macho_file); - out.rangeStart = addr; - var reloc = addReloc(offset, macho_file.getTarget().cpu.arch); - reloc.r_symbolnum = atom.out_n_sect + 1; - relocs.appendAssumeCapacity(reloc); - } - - // Personality function - if (rec.getPersonality(macho_file)) |sym| { - const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow; - var reloc = addReloc(offset + 16, macho_file.getTarget().cpu.arch); - reloc.r_symbolnum = r_symbolnum; - reloc.r_extern = 1; - relocs.appendAssumeCapacity(reloc); - } - - // LSDA address - if (rec.getLsdaAtom(macho_file)) |atom| { - const addr = rec.getLsdaAddress(macho_file); - out.lsda = addr; - var reloc = addReloc(offset + 24, macho_file.getTarget().cpu.arch); - reloc.r_symbolnum = atom.out_n_sect + 1; - relocs.appendAssumeCapacity(reloc); - } - - entries.appendAssumeCapacity(out); - offset += @sizeOf(macho.compact_unwind_entry); - } + try macho_file.getFile(index).?.object.writeAtomsRelocatable(macho_file); + macho_file.getFile(index).?.writeSymtab(macho_file, macho_file); } - assert(entries.items.len == nrecs); - assert(relocs.items.len == header.nreloc); + if (macho_file.eh_frame_sect_index) |_| { + try writeEhFrame(macho_file); + } - mem.sort(macho.relocation_info, relocs.items, {}, sortReloc); - - // TODO scattered writes? - try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(entries.items), header.offset); - try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff); + if (macho_file.unwind_info_sect_index) |_| { + for (macho_file.objects.items) |index| { + try macho_file.getFile(index).?.object.writeCompactUnwindRelocatable(macho_file); + } + } } fn writeEhFrame(macho_file: *MachO) !void { - const sect_index = macho_file.eh_frame_sect_index orelse return; - const gpa = macho_file.base.comp.gpa; - const header = macho_file.sections.items(.header)[sect_index]; - const size = math.cast(usize, header.size) orelse return error.Overflow; + const sect_index = macho_file.eh_frame_sect_index.?; + const buffer = macho_file.sections.items(.out)[sect_index]; + const relocs = macho_file.sections.items(.relocs)[sect_index]; + try eh_frame.writeRelocs(macho_file, buffer.items, relocs.items); +} - const code = try gpa.alloc(u8, size); - defer gpa.free(code); +fn writeSectionsToFile(macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); - var relocs = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc); - defer relocs.deinit(); + const slice = macho_file.sections.slice(); + for (slice.items(.header), slice.items(.out), slice.items(.relocs)) |header, out, relocs| { + try macho_file.base.file.?.pwriteAll(out.items, header.offset); + try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff); + } - try eh_frame.writeRelocs(macho_file, code, &relocs); - assert(relocs.items.len == header.nreloc); - - mem.sort(macho.relocation_info, relocs.items, {}, sortReloc); - - // TODO scattered writes? - try macho_file.base.file.?.pwriteAll(code, header.offset); - try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff); + try macho_file.writeDataInCode(); + try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(macho_file.symtab.items), macho_file.symtab_cmd.symoff); + try macho_file.base.file.?.pwriteAll(macho_file.strtab.items, macho_file.symtab_cmd.stroff); } fn writeLoadCommands(macho_file: *MachO) !struct { usize, usize } { From 4aff0ec394cb6ef42db982df86caf8976b558d43 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 12 Jul 2024 06:50:57 +0200 Subject: [PATCH 23/45] macho: move relocs re-resolution logic to ZigObject --- src/link/MachO.zig | 45 +++------------------------------ src/link/MachO/ZigObject.zig | 49 ++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 41 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 2b4005c1fe..e99fd6bf58 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -567,47 +567,10 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n try self.resizeSections(); if (self.getZigObject()) |zo| { - var has_resolve_error = false; - - for (zo.getAtoms()) |atom_index| { - const atom = zo.getAtom(atom_index) orelse continue; - if (!atom.flags.alive) continue; - const sect = &self.sections.items(.header)[atom.out_n_sect]; - if (sect.isZerofill()) continue; - if (!self.isZigSection(atom.out_n_sect)) continue; // Non-Zig sections are handled separately - if (atom.getRelocs(self).len == 0) continue; - // TODO: we will resolve and write ZigObject's TLS data twice: - // once here, and once in writeAtoms - const atom_size = math.cast(usize, atom.size) orelse return error.Overflow; - const code = try gpa.alloc(u8, atom_size); - defer gpa.free(code); - zo.getAtomData(self, atom.*, code) catch |err| switch (err) { - error.InputOutput => { - try self.reportUnexpectedError("fetching code for '{s}' failed", .{ - atom.getName(self), - }); - return error.FlushFailure; - }, - else => |e| { - try self.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{ - atom.getName(self), - @errorName(e), - }); - return error.FlushFailure; - }, - }; - const file_offset = sect.offset + atom.value; - atom.resolveRelocs(self, code) catch |err| switch (err) { - error.ResolveFailed => has_resolve_error = true, - else => |e| { - try self.reportUnexpectedError("unexpected error while resolving relocations", .{}); - return e; - }, - }; - try self.base.file.?.pwriteAll(code, file_offset); - } - - if (has_resolve_error) return error.FlushFailure; + zo.resolveRelocs(self) catch |err| switch (err) { + error.ResolveFailed => return error.FlushFailure, + else => |e| return e, + }; } self.writeSectionsAndUpdateLinkeditSizes() catch |err| { switch (err) { diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 661a88904e..79e563ac1a 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -383,6 +383,55 @@ pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void { } } +pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void { + const gpa = macho_file.base.comp.gpa; + var has_error = false; + for (self.getAtoms()) |atom_index| { + const atom = self.getAtom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + const sect = &macho_file.sections.items(.header)[atom.out_n_sect]; + if (sect.isZerofill()) continue; + if (!macho_file.isZigSection(atom.out_n_sect)) continue; // Non-Zig sections are handled separately + if (atom.getRelocs(macho_file).len == 0) continue; + // TODO: we will resolve and write ZigObject's TLS data twice: + // once here, and once in writeAtoms + const atom_size = std.math.cast(usize, atom.size) orelse return error.Overflow; + const code = try gpa.alloc(u8, atom_size); + defer gpa.free(code); + self.getAtomData(macho_file, atom.*, code) catch |err| { + switch (err) { + error.InputOutput => { + try macho_file.reportUnexpectedError("fetching code for '{s}' failed", .{ + atom.getName(macho_file), + }); + }, + else => |e| { + try macho_file.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{ + atom.getName(macho_file), + @errorName(e), + }); + }, + } + has_error = true; + continue; + }; + const file_offset = sect.offset + atom.value; + atom.resolveRelocs(macho_file, code) catch |err| { + switch (err) { + error.ResolveFailed => {}, + else => |e| { + try macho_file.reportUnexpectedError("unexpected error while resolving relocations: {s}", .{@errorName(e)}); + }, + } + has_error = true; + continue; + }; + try macho_file.base.file.?.pwriteAll(code, file_offset); + } + + if (has_error) return error.ResolveFailed; +} + pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) void { const tracy = trace(@src()); defer tracy.end(); From 2579c55d4903b016e0fb6d1f52fbbfe4a2a71c26 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 12 Jul 2024 13:18:52 +0200 Subject: [PATCH 24/45] macho: adjust global creation in ZigObject to new model --- src/link/MachO/ZigObject.zig | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 79e563ac1a..8e65feb264 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -167,6 +167,7 @@ pub fn createAtomForDecl(self: *ZigObject, allocator: Allocator, macho_file: *Ma relocs.* = .{}; const atom = self.getAtom(atom_index).?; atom.addExtra(.{ .rel_index = relocs_index, .rel_count = 0 }, macho_file); + try self.globals.append(allocator, 0); return symbol_index; } @@ -1315,22 +1316,30 @@ pub fn updateExports( break :blk global_nlist_index; }; const global_nlist = &self.symtab.items(.nlist)[global_nlist_index]; + const atom_index = self.symtab.items(.atom)[nlist_idx]; + const global_sym = &self.symbols.items[global_nlist_index]; global_nlist.n_value = nlist.n_value; global_nlist.n_sect = nlist.n_sect; global_nlist.n_type = macho.N_EXT | macho.N_SECT; self.symtab.items(.size)[global_nlist_index] = self.symtab.items(.size)[nlist_idx]; - self.symtab.items(.atom)[global_nlist_index] = self.symtab.items(.atom)[nlist_idx]; + self.symtab.items(.atom)[global_nlist_index] = atom_index; + global_sym.atom_ref = .{ .index = atom_index, .file = self.index }; switch (exp.opts.linkage) { .internal => { // Symbol should be hidden, or in MachO lingo, private extern. global_nlist.n_type |= macho.N_PEXT; + global_sym.visibility = .hidden; + }, + .strong => { + global_sym.visibility = .global; }, - .strong => {}, .weak => { // Weak linkage is specified as part of n_desc field. // Symbol's n_type is like for a symbol with strong linkage. global_nlist.n_desc |= macho.N_WEAK_DEF; + global_sym.visibility = .global; + global_sym.flags.weak = true; }, else => unreachable, } @@ -1460,16 +1469,17 @@ pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, l const off = try self.strtab.insert(gpa, sym_name); const lookup_gop = try self.globals_lookup.getOrPut(gpa, off); if (!lookup_gop.found_existing) { + const sym_index = try self.addSymbol(gpa); + const sym = &self.symbols.items[sym_index]; const nlist_index = try self.addNlist(gpa); const nlist = &self.symtab.items(.nlist)[nlist_index]; nlist.n_strx = off; nlist.n_type = macho.N_EXT; + sym.name = off; + sym.nlist_idx = nlist_index; + sym.extra = try self.addSymbolExtra(gpa, .{}); lookup_gop.value_ptr.* = nlist_index; - _ = try macho_file.resolver.getOrPut(gpa, .{ - .index = nlist_index, - .file = self.index, - }, macho_file); - try self.globals.append(gpa, nlist_index); + try self.globals.append(gpa, 0); } return lookup_gop.value_ptr.*; } @@ -1692,7 +1702,7 @@ fn formatSymtab( try writer.writeAll(" symbols\n"); const self = ctx.self; const macho_file = ctx.macho_file; - for (self.symbols.items, 0) |sym, i| { + for (self.symbols.items, 0..) |sym, i| { const ref = self.getSymbolRef(@intCast(i), macho_file); if (ref.getFile(macho_file) == null) { // TODO any better way of handling this? From cba04ff24403ea5d134524eee318424599f068a6 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 12 Jul 2024 15:35:01 +0200 Subject: [PATCH 25/45] macho: re-enable calculating num of relocs for ZigObject --- src/link/MachO/ZigObject.zig | 10 ++++++++++ src/link/MachO/relocatable.zig | 20 ++++++++------------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 8e65feb264..bddd5379f4 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -433,6 +433,16 @@ pub fn resolveRelocs(self: *ZigObject, macho_file: *MachO) !void { if (has_error) return error.ResolveFailed; } +pub fn calcNumRelocs(self: *ZigObject, macho_file: *MachO) void { + for (self.getAtoms()) |atom_index| { + const atom = self.getAtom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue; + const header = &macho_file.sections.items(.header)[atom.out_n_sect]; + header.nreloc += atom.calcNumRelocs(macho_file); + } +} + pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) void { const tracy = trace(@src()); defer tracy.end(); diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index 0095118c86..c3d15c2241 100644 --- a/src/link/MachO/relocatable.zig +++ b/src/link/MachO/relocatable.zig @@ -369,6 +369,11 @@ fn calcSectionSizes(macho_file: *MachO) !void { calcSectionSize(macho_file, @intCast(i)); } + if (macho_file.getZigObject()) |zo| { + // TODO this will create a race + zo.calcNumRelocs(macho_file); + } + if (macho_file.eh_frame_sect_index) |_| { try calcEhFrameSize(macho_file); } @@ -382,19 +387,10 @@ fn calcSectionSizes(macho_file: *MachO) !void { try macho_file.data_in_code.updateSize(macho_file); - calcCompactUnwindSize(macho_file); + if (macho_file.unwind_info_sect_index) |_| { + calcCompactUnwindSize(macho_file); + } calcSymtabSize(macho_file); - - // TODO - // if (macho_file.getZigObject()) |zo| { - // for (zo.atoms.items) |atom_index| { - // const atom = macho_file.getAtom(atom_index) orelse continue; - // if (!atom.flags.alive) continue; - // const header = &macho_file.sections.items(.header)[atom.out_n_sect]; - // if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue; - // header.nreloc += atom.calcNumRelocs(macho_file); - // } - // } } fn calcSectionSize(macho_file: *MachO, sect_id: u8) void { From 853ca403c4164a67f32773a069b8f5f5579c4542 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 12 Jul 2024 15:56:26 +0200 Subject: [PATCH 26/45] macho: bring back relocatable mode for ZigObject --- src/link/MachO/ZigObject.zig | 43 ++++++++++++++++++++++++++++++++-- src/link/MachO/relocatable.zig | 31 +++++++++++++++++------- 2 files changed, 64 insertions(+), 10 deletions(-) diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index bddd5379f4..b48a7d7dde 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -437,9 +437,48 @@ pub fn calcNumRelocs(self: *ZigObject, macho_file: *MachO) void { for (self.getAtoms()) |atom_index| { const atom = self.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; - if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue; const header = &macho_file.sections.items(.header)[atom.out_n_sect]; - header.nreloc += atom.calcNumRelocs(macho_file); + if (header.isZerofill()) continue; + if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue; + const nreloc = atom.calcNumRelocs(macho_file); + atom.addExtra(.{ .rel_out_index = header.nreloc, .rel_out_count = nreloc }, macho_file); + header.nreloc += nreloc; + } +} + +pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) !void { + const gpa = macho_file.base.comp.gpa; + + for (self.getAtoms()) |atom_index| { + const atom = self.getAtom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + const header = macho_file.sections.items(.header)[atom.out_n_sect]; + const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items; + if (header.isZerofill()) continue; + if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue; + if (atom.getRelocs(macho_file).len == 0) continue; + const extra = atom.getExtra(macho_file); + const atom_size = std.math.cast(usize, atom.size) orelse return error.Overflow; + const code = try gpa.alloc(u8, atom_size); + defer gpa.free(code); + self.getAtomData(macho_file, atom.*, code) catch |err| switch (err) { + error.InputOutput => { + try macho_file.reportUnexpectedError("fetching code for '{s}' failed", .{ + atom.getName(macho_file), + }); + return error.FlushFailure; + }, + else => |e| { + try macho_file.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{ + atom.getName(macho_file), + @errorName(e), + }); + return error.FlushFailure; + }, + }; + const file_offset = header.offset + atom.value; + try atom.writeRelocs(macho_file, code, relocs[extra.rel_out_index..][0..extra.rel_out_count]); + try macho_file.base.file.?.pwriteAll(code, file_offset); } } diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index c3d15c2241..2d4a3411db 100644 --- a/src/link/MachO/relocatable.zig +++ b/src/link/MachO/relocatable.zig @@ -372,6 +372,7 @@ fn calcSectionSizes(macho_file: *MachO) !void { if (macho_file.getZigObject()) |zo| { // TODO this will create a race zo.calcNumRelocs(macho_file); + zo.calcSymtabSize(macho_file); } if (macho_file.eh_frame_sect_index) |_| { @@ -390,7 +391,7 @@ fn calcSectionSizes(macho_file: *MachO) !void { if (macho_file.unwind_info_sect_index) |_| { calcCompactUnwindSize(macho_file); } - calcSymtabSize(macho_file); + try calcSymtabSize(macho_file); } fn calcSectionSize(macho_file: *MachO, sect_id: u8) void { @@ -445,19 +446,27 @@ fn calcCompactUnwindSize(macho_file: *MachO) void { sect.@"align" = 3; } -fn calcSymtabSize(macho_file: *MachO) void { +fn calcSymtabSize(macho_file: *MachO) error{OutOfMemory}!void { const tracy = trace(@src()); defer tracy.end(); + const gpa = macho_file.base.comp.gpa; + var nlocals: u32 = 0; var nstabs: u32 = 0; var nexports: u32 = 0; var nimports: u32 = 0; var strsize: u32 = 1; - for (macho_file.objects.items) |index| { - const object = macho_file.getFile(index).?.object; - const ctx = &object.output_symtab_ctx; + var objects = try std.ArrayList(File.Index).initCapacity(gpa, macho_file.objects.items.len + 1); + defer objects.deinit(); + if (macho_file.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index); + objects.appendSliceAssumeCapacity(macho_file.objects.items); + + for (objects.items) |index| { + const ctx = switch (macho_file.getFile(index).?) { + inline else => |x| &x.output_symtab_ctx, + }; ctx.ilocal = nlocals; ctx.istab = nstabs; ctx.iexport = nexports; @@ -470,9 +479,10 @@ fn calcSymtabSize(macho_file: *MachO) void { strsize += ctx.strsize; } - for (macho_file.objects.items) |index| { - const object = macho_file.getFile(index).?.object; - const ctx = &object.output_symtab_ctx; + for (objects.items) |index| { + const ctx = switch (macho_file.getFile(index).?) { + inline else => |x| &x.output_symtab_ctx, + }; ctx.istab += nlocals; ctx.iexport += nlocals + nstabs; ctx.iimport += nlocals + nstabs + nexports; @@ -645,6 +655,11 @@ fn writeSections(macho_file: *MachO) !void { macho_file.getFile(index).?.writeSymtab(macho_file, macho_file); } + if (macho_file.getZigObject()) |zo| { + try zo.writeRelocs(macho_file); + zo.writeSymtab(macho_file, macho_file); + } + if (macho_file.eh_frame_sect_index) |_| { try writeEhFrame(macho_file); } From 01fc33c949cb8609324cfbcdf7f0524b93ff2561 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 13 Jul 2024 07:29:42 +0200 Subject: [PATCH 27/45] macho: emit relocs for non-zig-sections in ZigObject --- src/link/MachO/Atom.zig | 1 + src/link/MachO/ZigObject.zig | 31 +++++++++++++++++++++++++++++++ src/link/MachO/relocatable.zig | 1 + 3 files changed, 33 insertions(+) diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index b928542600..df2327f6bc 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -979,6 +979,7 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r defer i += 1; const rel_offset = rel.offset - self.off; const r_address: i32 = math.cast(i32, self.value + rel_offset) orelse return error.Overflow; + assert(r_address >= 0); const r_symbolnum = r_symbolnum: { const r_symbolnum: u32 = switch (rel.tag) { .local => rel.getTargetAtom(self, macho_file).out_n_sect + 1, diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index b48a7d7dde..a84a95da7e 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -482,6 +482,37 @@ pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) !void { } } +// TODO we need this because not everything gets written out incrementally. +// For example, TLS data gets written out via traditional route. +// Is there any better way of handling this? +pub fn writeAtomsRelocatable(self: *ZigObject, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + + for (self.getAtoms()) |atom_index| { + const atom = self.getAtom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + const sect = atom.getInputSection(macho_file); + if (sect.isZerofill()) continue; + if (macho_file.isZigSection(atom.out_n_sect)) continue; + const off = atom.value; + const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; + try self.getAtomData(macho_file, atom.*, buffer[off..][0..atom.size]); + const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items; + const extra = atom.getExtra(macho_file); + try atom.writeRelocs(macho_file, buffer[off..][0..atom.size], relocs[extra.rel_out_index..][0..extra.rel_out_count]); + } +} + +// TODO we need this because not everything gets written out incrementally. +// For example, TLS data gets written out via traditional route. +// Is there any better way of handling this? +pub fn writeAtoms(self: *ZigObject, macho_file: *MachO) !void { + const gpa = macho_file.base.comp.gpa; + _ = gpa; + _ = self; +} + pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) void { const tracy = trace(@src()); defer tracy.end(); diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index 2d4a3411db..1039b6557c 100644 --- a/src/link/MachO/relocatable.zig +++ b/src/link/MachO/relocatable.zig @@ -657,6 +657,7 @@ fn writeSections(macho_file: *MachO) !void { if (macho_file.getZigObject()) |zo| { try zo.writeRelocs(macho_file); + try zo.writeAtomsRelocatable(macho_file); zo.writeSymtab(macho_file, macho_file); } From e5a66184eda96d8571ea5abaf7d5623d092bfac2 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 13 Jul 2024 07:56:14 +0200 Subject: [PATCH 28/45] macho: pretty print relocation types in logs and errors --- src/link/MachO/Atom.zig | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index df2327f6bc..e858b65a40 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -593,8 +593,14 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void { }; try macho_file.reportParseError2( file.getIndex(), - "{s}: 0x{x}: 0x{x}: failed to relax relocation: type {s}, target {s}", - .{ name, self.getAddress(macho_file), rel.offset, @tagName(rel.type), target }, + "{s}: 0x{x}: 0x{x}: failed to relax relocation: type {}, target {s}", + .{ + name, + self.getAddress(macho_file), + rel.offset, + rel.fmtPretty(macho_file.getTarget().cpu.arch), + target, + }, ); has_error = true; }, @@ -650,17 +656,17 @@ fn resolveRelocInner( }.divExact; switch (rel.tag) { - .local => relocs_log.debug(" {x}<+{d}>: {s}: [=> {x}] atom({d})", .{ + .local => relocs_log.debug(" {x}<+{d}>: {}: [=> {x}] atom({d})", .{ P, rel_offset, - @tagName(rel.type), + rel.fmtPretty(cpu_arch), S + A - SUB, rel.getTargetAtom(self, macho_file).atom_index, }), - .@"extern" => relocs_log.debug(" {x}<+{d}>: {s}: [=> {x}] G({x}) ZG({x}) ({s})", .{ + .@"extern" => relocs_log.debug(" {x}<+{d}>: {}: [=> {x}] G({x}) ZG({x}) ({s})", .{ P, rel_offset, - @tagName(rel.type), + rel.fmtPretty(cpu_arch), S + A - SUB, G + A, ZIG_GOT + A, @@ -900,11 +906,11 @@ const x86_64 = struct { }, else => |x| { var err = try macho_file.addErrorWithNotes(2); - try err.addMsg(macho_file, "{s}: 0x{x}: 0x{x}: failed to relax relocation of type {s}", .{ + try err.addMsg(macho_file, "{s}: 0x{x}: 0x{x}: failed to relax relocation of type {}", .{ self.getName(macho_file), self.getAddress(macho_file), rel.offset, - @tagName(rel.type), + rel.fmtPretty(.x86_64), }); try err.addNote(macho_file, "expected .mov instruction but found .{s}", .{@tagName(x)}); try err.addNote(macho_file, "while parsing {}", .{self.getFile(macho_file).fmtPath()}); From 129fe8668c8b4835c11c50360ee4ed36972ae773 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 13 Jul 2024 07:56:28 +0200 Subject: [PATCH 29/45] macho: write non-incremental atoms in ZigObject --- src/link/MachO.zig | 3 +++ src/link/MachO/ZigObject.zig | 17 ++++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index e99fd6bf58..3c02900589 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -2351,6 +2351,9 @@ fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void { for (self.objects.items) |index| { try self.getFile(index).?.writeAtoms(self); } + if (self.getZigObject()) |zo| { + try zo.writeAtoms(self); + } if (self.getInternalObject()) |obj| { try obj.asFile().writeAtoms(self); } diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index a84a95da7e..160e67d3d5 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -508,9 +508,20 @@ pub fn writeAtomsRelocatable(self: *ZigObject, macho_file: *MachO) !void { // For example, TLS data gets written out via traditional route. // Is there any better way of handling this? pub fn writeAtoms(self: *ZigObject, macho_file: *MachO) !void { - const gpa = macho_file.base.comp.gpa; - _ = gpa; - _ = self; + const tracy = trace(@src()); + defer tracy.end(); + + for (self.getAtoms()) |atom_index| { + const atom = self.getAtom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + const sect = atom.getInputSection(macho_file); + if (sect.isZerofill()) continue; + if (macho_file.isZigSection(atom.out_n_sect)) continue; + const off = atom.value; + const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; + try self.getAtomData(macho_file, atom.*, buffer[off..][0..atom.size]); + try atom.resolveRelocs(macho_file, buffer[off..][0..atom.size]); + } } pub fn calcSymtabSize(self: *ZigObject, macho_file: *MachO) void { From 84189f9d56f207e92d0a9d1f5145079cc46f8db0 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 13 Jul 2024 10:00:38 +0200 Subject: [PATCH 30/45] macho: skip resizing incremental Zig sections in r mode --- src/link/MachO/relocatable.zig | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index 1039b6557c..6b2594c493 100644 --- a/src/link/MachO/relocatable.zig +++ b/src/link/MachO/relocatable.zig @@ -637,11 +637,13 @@ fn writeSections(macho_file: *MachO) !void { const gpa = macho_file.base.comp.gpa; const cpu_arch = macho_file.getTarget().cpu.arch; const slice = macho_file.sections.slice(); - for (slice.items(.header), slice.items(.out), slice.items(.relocs)) |header, *out, *relocs| { + for (slice.items(.header), slice.items(.out), slice.items(.relocs), 0..) |header, *out, *relocs, n_sect| { if (header.isZerofill()) continue; - try out.resize(gpa, header.size); - const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0; - @memset(out.items, padding_byte); + if (!macho_file.isZigSection(@intCast(n_sect))) { // TODO this is wrong; what about debug sections? + try out.resize(gpa, header.size); + const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0; + @memset(out.items, padding_byte); + } try relocs.resize(gpa, header.nreloc); } From 387a71fa5bed4782d6f6789b8ca1ce637a31076f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 13 Jul 2024 10:22:02 +0200 Subject: [PATCH 31/45] macho: re-enable writing out static archive with ZigObject --- src/link/MachO/ZigObject.zig | 10 +++-- src/link/MachO/file.zig | 10 +---- src/link/MachO/relocatable.zig | 68 ++++++++++++++-------------------- 3 files changed, 35 insertions(+), 53 deletions(-) diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 160e67d3d5..1cf95e404d 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -313,7 +313,9 @@ pub fn dedupLiterals(self: *ZigObject, lp: MachO.LiteralPool, macho_file: *MachO /// This is just a temporary helper function that allows us to re-read what we wrote to file into a buffer. /// We need this so that we can write to an archive. /// TODO implement writing ZigObject data directly to a buffer instead. -pub fn readFileContents(self: *ZigObject, size: usize, macho_file: *MachO) !void { +pub fn readFileContents(self: *ZigObject, macho_file: *MachO) !void { + // Size of the output object file is always the offset + size of the strtab + const size = macho_file.symtab_cmd.stroff + macho_file.symtab_cmd.strsize; const gpa = macho_file.base.comp.gpa; try self.data.resize(gpa, size); const amt = try macho_file.base.file.?.preadAll(self.data.items, 0); @@ -322,9 +324,9 @@ pub fn readFileContents(self: *ZigObject, size: usize, macho_file: *MachO) !void pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, macho_file: *MachO) error{OutOfMemory}!void { const gpa = macho_file.base.comp.gpa; - for (self.symbols.items) |sym_index| { - const sym = macho_file.getSymbol(sym_index); - const file = sym.getFile(macho_file).?; + for (self.symbols.items, 0..) |sym, i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + const file = ref.getFile(macho_file).?; assert(file.getIndex() == self.index); if (!sym.flags.@"export") continue; const off = try ar_symtab.strtab.insert(gpa, sym.getName(macho_file)); diff --git a/src/link/MachO/file.zig b/src/link/MachO/file.zig index 4957ed71c2..a035c79ffb 100644 --- a/src/link/MachO/file.zig +++ b/src/link/MachO/file.zig @@ -293,8 +293,6 @@ pub const File = union(enum) { pub fn updateArSymtab(file: File, ar_symtab: *Archive.ArSymtab, macho_file: *MachO) error{OutOfMemory}!void { return switch (file) { .dylib, .internal => unreachable, - // TODO - .zig_object => unreachable, inline else => |x| x.updateArSymtab(ar_symtab, macho_file), }; } @@ -302,9 +300,7 @@ pub const File = union(enum) { pub fn updateArSize(file: File, macho_file: *MachO) !void { return switch (file) { .dylib, .internal => unreachable, - // TODO - .zig_object => unreachable, - // .zig_object => |x| x.updateArSize(), + .zig_object => |x| x.updateArSize(), .object => |x| x.updateArSize(macho_file), }; } @@ -312,9 +308,7 @@ pub const File = union(enum) { pub fn writeAr(file: File, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void { return switch (file) { .dylib, .internal => unreachable, - // TODO - .zig_object => unreachable, - // .zig_object => |x| x.writeAr(ar_format, writer), + .zig_object => |x| x.writeAr(ar_format, writer), .object => |x| x.writeAr(ar_format, macho_file, writer), }; } diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index 6b2594c493..5ea6ae18f3 100644 --- a/src/link/MachO/relocatable.zig +++ b/src/link/MachO/relocatable.zig @@ -110,56 +110,42 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? if (comp.link_errors.items.len > 0) return error.FlushFailure; - // TODO re-enable - // // First, we flush relocatable object file generated with our backends. - // if (macho_file.getZigObject()) |zo| { - // zo.resolveSymbols(macho_file); - // zo.asFile().markExportsRelocatable(macho_file); - // zo.asFile().claimUnresolvedRelocatable(macho_file); - // try macho_file.sortSections(); - // try macho_file.addAtomsToSections(); - // try calcSectionSizes(macho_file); - // try createSegment(macho_file); - // try allocateSections(macho_file); - // allocateSegment(macho_file); + // First, we flush relocatable object file generated with our backends. + if (macho_file.getZigObject()) |zo| { + try zo.resolveSymbols(macho_file); + zo.asFile().markExportsRelocatable(macho_file); + zo.asFile().claimUnresolvedRelocatable(macho_file); + try macho_file.sortSections(); + try macho_file.addAtomsToSections(); + try calcSectionSizes(macho_file); + try createSegment(macho_file); + try allocateSections(macho_file); + allocateSegment(macho_file); - // var off = off: { - // const seg = macho_file.segments.items[0]; - // const off = math.cast(u32, seg.fileoff + seg.filesize) orelse return error.Overflow; - // break :off mem.alignForward(u32, off, @alignOf(macho.relocation_info)); - // }; - // off = allocateSectionsRelocs(macho_file, off); + if (build_options.enable_logging) { + state_log.debug("{}", .{macho_file.dumpState()}); + } - // if (build_options.enable_logging) { - // state_log.debug("{}", .{macho_file.dumpState()}); - // } + try writeSections(macho_file); + sortRelocs(macho_file); + try writeSectionsToFile(macho_file); - // try macho_file.calcSymtabSize(); - // try writeAtoms(macho_file); + // In order to please Apple ld (and possibly other MachO linkers in the wild), + // we will now sanitize segment names of Zig-specific segments. + sanitizeZigSections(macho_file); - // off = mem.alignForward(u32, off, @alignOf(u64)); - // off = try macho_file.writeDataInCode(0, off); - // off = mem.alignForward(u32, off, @alignOf(u64)); - // off = try macho_file.writeSymtab(off); - // off = mem.alignForward(u32, off, @alignOf(u64)); - // off = try macho_file.writeStrtab(off); + const ncmds, const sizeofcmds = try writeLoadCommands(macho_file); + try writeHeader(macho_file, ncmds, sizeofcmds); - // // In order to please Apple ld (and possibly other MachO linkers in the wild), - // // we will now sanitize segment names of Zig-specific segments. - // sanitizeZigSections(macho_file); - - // const ncmds, const sizeofcmds = try writeLoadCommands(macho_file); - // try writeHeader(macho_file, ncmds, sizeofcmds); - - // // TODO we can avoid reading in the file contents we just wrote if we give the linker - // // ability to write directly to a buffer. - // try zo.readFileContents(off, macho_file); - // } + // TODO we can avoid reading in the file contents we just wrote if we give the linker + // ability to write directly to a buffer. + try zo.readFileContents(macho_file); + } var files = std.ArrayList(File.Index).init(gpa); defer files.deinit(); try files.ensureTotalCapacityPrecise(macho_file.objects.items.len + 1); - // if (macho_file.getZigObject()) |zo| files.appendAssumeCapacity(zo.index); + if (macho_file.getZigObject()) |zo| files.appendAssumeCapacity(zo.index); for (macho_file.objects.items) |index| files.appendAssumeCapacity(index); const format: Archive.Format = .p32; From 234aa86802ececa8450d84e4cb16da7ce8a5cf79 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 14 Jul 2024 07:11:12 +0200 Subject: [PATCH 32/45] macho: update non-incremental section sizes for ZigObject sections --- src/link/MachO.zig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 3c02900589..bb1a9a064e 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1843,6 +1843,15 @@ pub fn addAtomsToSections(self: *MachO) !void { const gpa = self.base.comp.gpa; + if (self.getZigObject()) |zo| { + for (zo.getAtoms()) |atom_index| { + const atom = zo.getAtom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + if (self.isZigSection(atom.out_n_sect)) continue; + const atoms = &self.sections.items(.atoms)[atom.out_n_sect]; + try atoms.append(gpa, .{ .index = atom_index, .file = zo.index }); + } + } for (self.objects.items) |index| { const file = self.getFile(index).?; for (file.getAtoms()) |atom_index| { From f9fbd6302f0f4d1ef5ed1c1d2fc63eabb313978d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 15 Jul 2024 07:38:29 +0200 Subject: [PATCH 33/45] macho: test TLS in Zig with x86_64 backend --- test/link/macho.zig | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/link/macho.zig b/test/link/macho.zig index b77bd279cb..3bc6b0a289 100644 --- a/test/link/macho.zig +++ b/test/link/macho.zig @@ -25,6 +25,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { macho_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = x86_64_target })); macho_step.dependOn(testReexportsZig(b, .{ .use_llvm = false, .target = x86_64_target })); macho_step.dependOn(testRelocatableZig(b, .{ .use_llvm = false, .target = x86_64_target })); + macho_step.dependOn(testTlsZig(b, .{ .use_llvm = false, .target = x86_64_target })); // Exercise linker with LLVM backend macho_step.dependOn(testDeadStrip(b, .{ .target = default_target })); @@ -56,6 +57,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { macho_step.dependOn(testTentative(b, .{ .target = default_target })); macho_step.dependOn(testThunks(b, .{ .target = aarch64_target })); macho_step.dependOn(testTlsLargeTbss(b, .{ .target = default_target })); + macho_step.dependOn(testTlsZig(b, .{ .target = default_target })); macho_step.dependOn(testUndefinedFlag(b, .{ .target = default_target })); macho_step.dependOn(testUnwindInfo(b, .{ .target = default_target })); macho_step.dependOn(testUnwindInfoNoSubsectionsX64(b, .{ .target = x86_64_target })); @@ -2274,6 +2276,32 @@ fn testTlsLargeTbss(b: *Build, opts: Options) *Step { return test_step; } +fn testTlsZig(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "tls-zig", opts); + + const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes = + \\const std = @import("std"); + \\threadlocal var x: i32 = 0; + \\threadlocal var y: i32 = -1; + \\pub fn main() void { + \\ std.io.getStdOut().writer().print("{d} {d}\n", .{x, y}) catch unreachable; + \\ x -= 1; + \\ y += 1; + \\ std.io.getStdOut().writer().print("{d} {d}\n", .{x, y}) catch unreachable; + \\} + }); + + const run = addRunArtifact(exe); + run.expectStdOutEqual( + \\0 -1 + \\-1 0 + \\ + ); + test_step.dependOn(&run.step); + + return test_step; +} + fn testTwoLevelNamespace(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "two-level-namespace", opts); From 521933e1c05977105ee4eee70096a13064068f8b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 15 Jul 2024 08:05:07 +0200 Subject: [PATCH 34/45] macho: do not randomly append non-incr atoms in ZigObject --- src/link/MachO/ZigObject.zig | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 1cf95e404d..680f19a245 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -497,6 +497,7 @@ pub fn writeAtomsRelocatable(self: *ZigObject, macho_file: *MachO) !void { const sect = atom.getInputSection(macho_file); if (sect.isZerofill()) continue; if (macho_file.isZigSection(atom.out_n_sect)) continue; + if (atom.getRelocs(macho_file).len == 0) continue; const off = atom.value; const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; try self.getAtomData(macho_file, atom.*, buffer[off..][0..atom.size]); @@ -1175,11 +1176,6 @@ fn createTlvDescriptor( .symbolnum = @intCast(init_sym_index), }, }); - - try macho_file.sections.items(.atoms)[sect_index].append(gpa, .{ - .index = atom.atom_index, - .file = self.index, - }); } fn getDeclOutputSection( From 91de8dc8abd9db03ba4ac4c4a5fcbe86e8bc7ee4 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 15 Jul 2024 08:43:19 +0200 Subject: [PATCH 35/45] macho: fix unresolved symbols error reporting --- src/link/MachO.zig | 4 ++-- src/link/MachO/Atom.zig | 2 +- test/link/macho.zig | 29 +++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index bb1a9a064e..7b695304bb 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -25,7 +25,7 @@ sections: std.MultiArrayList(Section) = .{}, resolver: SymbolResolver = .{}, /// This table will be populated after `scanRelocs` has run. /// Key is symbol index. -undefs: std.AutoHashMapUnmanaged(Ref, std.ArrayListUnmanaged(Ref)) = .{}, +undefs: std.AutoHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(Ref)) = .{}, dyld_info_cmd: macho.dyld_info_command = .{}, symtab_cmd: macho.symtab_command = .{}, @@ -1531,7 +1531,7 @@ fn reportUndefs(self: *MachO) !void { var has_undefs = false; var it = self.undefs.iterator(); while (it.next()) |entry| { - const undef_sym = entry.key_ptr.getSymbol(self).?; + const undef_sym = self.resolver.keys.items[entry.key_ptr.* - 1]; const notes = entry.value_ptr.*; const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes); diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index e858b65a40..0a2ebb931c 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -549,7 +549,7 @@ fn reportUndefSymbol(self: Atom, rel: Relocation, macho_file: *MachO) !bool { const ref = file.getSymbolRef(rel.target, macho_file); if (ref.getFile(macho_file) == null) { const gpa = macho_file.base.comp.gpa; - const gop = try macho_file.undefs.getOrPut(gpa, .{ .index = rel.target, .file = self.file }); + const gop = try macho_file.undefs.getOrPut(gpa, file.getGlobals()[rel.target]); if (!gop.found_existing) { gop.value_ptr.* = .{}; } diff --git a/test/link/macho.zig b/test/link/macho.zig index 3bc6b0a289..7dd27c0467 100644 --- a/test/link/macho.zig +++ b/test/link/macho.zig @@ -26,6 +26,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { macho_step.dependOn(testReexportsZig(b, .{ .use_llvm = false, .target = x86_64_target })); macho_step.dependOn(testRelocatableZig(b, .{ .use_llvm = false, .target = x86_64_target })); macho_step.dependOn(testTlsZig(b, .{ .use_llvm = false, .target = x86_64_target })); + macho_step.dependOn(testUnresolvedError(b, .{ .use_llvm = false, .target = x86_64_target })); // Exercise linker with LLVM backend macho_step.dependOn(testDeadStrip(b, .{ .target = default_target })); @@ -59,6 +60,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { macho_step.dependOn(testTlsLargeTbss(b, .{ .target = default_target })); macho_step.dependOn(testTlsZig(b, .{ .target = default_target })); macho_step.dependOn(testUndefinedFlag(b, .{ .target = default_target })); + macho_step.dependOn(testUnresolvedError(b, .{ .target = default_target })); macho_step.dependOn(testUnwindInfo(b, .{ .target = default_target })); macho_step.dependOn(testUnwindInfoNoSubsectionsX64(b, .{ .target = x86_64_target })); macho_step.dependOn(testUnwindInfoNoSubsectionsArm64(b, .{ .target = aarch64_target })); @@ -2499,6 +2501,33 @@ fn testUndefinedFlag(b: *Build, opts: Options) *Step { return test_step; } +fn testUnresolvedError(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "unresolved-error", opts); + + const obj = addObject(b, opts, .{ .name = "a", .zig_source_bytes = + \\extern fn foo() i32; + \\export fn bar() i32 { return foo() + 1; } + }); + + const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes = + \\const std = @import("std"); + \\extern fn foo() i32; + \\extern fn bar() i32; + \\pub fn main() void { + \\ std.debug.print("foo() + bar() = {d}", .{foo() + bar()}); + \\} + }); + exe.addObject(obj); + + expectLinkErrors(exe, test_step, .{ .exact = &.{ + "error: undefined symbol: _foo", + "note: referenced by /?/a.o:_bar", + "note: referenced by /?/main.o:_a.main", + } }); + + return test_step; +} + fn testUnwindInfo(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "unwind-info", opts); From e117e05768be08078a8484eaf982cfa5347a674e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 15 Jul 2024 14:51:17 +0200 Subject: [PATCH 36/45] macho: ensure we always name decls like LLVM to avoid confusion --- src/link/MachO/ZigObject.zig | 4 +++- test/link/link.zig | 3 ++- test/link/macho.zig | 21 +++++++++++++++------ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 680f19a245..b363ab91ed 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -979,7 +979,9 @@ fn updateDeclCode( sym.out_n_sect = sect_index; atom.out_n_sect = sect_index; - sym.name = try self.strtab.insert(gpa, decl.fqn.toSlice(ip)); + const sym_name = try std.fmt.allocPrintZ(gpa, "_{s}", .{decl.fqn.toSlice(ip)}); + defer gpa.free(sym_name); + sym.name = try self.strtab.insert(gpa, sym_name); atom.flags.alive = true; atom.name = sym.name; nlist.n_strx = sym.name; diff --git a/test/link/link.zig b/test/link/link.zig index a098da757d..aee3bbe01b 100644 --- a/test/link/link.zig +++ b/test/link/link.zig @@ -74,8 +74,9 @@ fn addCompileStep( .target = base.target, .optimize = base.optimize, .root_source_file = rsf: { + const name = b.fmt("{s}.zig", .{overlay.name}); const bytes = overlay.zig_source_bytes orelse break :rsf null; - break :rsf b.addWriteFiles().add("a.zig", bytes); + break :rsf b.addWriteFiles().add(name, bytes); }, .pic = overlay.pic, .strip = if (base.strip) |s| s else overlay.strip, diff --git a/test/link/macho.zig b/test/link/macho.zig index 7dd27c0467..6c047ee322 100644 --- a/test/link/macho.zig +++ b/test/link/macho.zig @@ -916,7 +916,7 @@ fn testLinksection(b: *Build, opts: Options) *Step { if (opts.optimize == .Debug) { check.checkInSymtab(); - check.checkContains("(__TEXT,__TestGenFnA) _a.testGenericFn__anon_"); + check.checkContains("(__TEXT,__TestGenFnA) _main.testGenericFn__anon_"); } test_step.dependOn(&check.step); @@ -2519,11 +2519,20 @@ fn testUnresolvedError(b: *Build, opts: Options) *Step { }); exe.addObject(obj); - expectLinkErrors(exe, test_step, .{ .exact = &.{ - "error: undefined symbol: _foo", - "note: referenced by /?/a.o:_bar", - "note: referenced by /?/main.o:_a.main", - } }); + // TODO order should match across backends if possible + if (opts.use_llvm) { + expectLinkErrors(exe, test_step, .{ .exact = &.{ + "error: undefined symbol: _foo", + "note: referenced by /?/a.o:_bar", + "note: referenced by /?/main.o:_main.main", + } }); + } else { + expectLinkErrors(exe, test_step, .{ .exact = &.{ + "error: undefined symbol: _foo", + "note: referenced by /?/main.o:_main.main", + "note: referenced by /?/a.o:__TEXT$__text_zig", + } }); + } return test_step; } From 103c16c87955facd373bd20435100fa7eb322349 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 15 Jul 2024 18:26:05 +0200 Subject: [PATCH 37/45] macho: clean up atom+symbol creation logic in ZigObject --- src/link/MachO/InternalObject.zig | 22 +++--- src/link/MachO/ZigObject.zig | 123 +++++++++++++++++------------- 2 files changed, 79 insertions(+), 66 deletions(-) diff --git a/src/link/MachO/InternalObject.zig b/src/link/MachO/InternalObject.zig index 9578bc84ec..55d7be00fb 100644 --- a/src/link/MachO/InternalObject.zig +++ b/src/link/MachO/InternalObject.zig @@ -52,8 +52,8 @@ pub fn init(self: *InternalObject, allocator: Allocator) !void { } pub fn initSymbols(self: *InternalObject, macho_file: *MachO) !void { - const createSymbol = struct { - fn createSymbol(obj: *InternalObject, name: u32, args: struct { + const newSymbolAssumeCapacity = struct { + fn newSymbolAssumeCapacity(obj: *InternalObject, name: u32, args: struct { type: u8 = macho.N_UNDF | macho.N_EXT, desc: u16 = 0, }) Symbol.Index { @@ -78,7 +78,7 @@ pub fn initSymbols(self: *InternalObject, macho_file: *MachO) !void { symbol.nlist_idx = nlist_idx; return index; } - }.createSymbol; + }.newSymbolAssumeCapacity; const gpa = macho_file.base.comp.gpa; var nsyms = macho_file.base.comp.force_undefined_symbols.keys().len; @@ -102,28 +102,28 @@ pub fn initSymbols(self: *InternalObject, macho_file: *MachO) !void { try self.force_undefined.ensureTotalCapacityPrecise(gpa, macho_file.base.comp.force_undefined_symbols.keys().len); for (macho_file.base.comp.force_undefined_symbols.keys()) |name| { - self.force_undefined.addOneAssumeCapacity().* = createSymbol(self, try self.addString(gpa, name), .{}); + self.force_undefined.addOneAssumeCapacity().* = newSymbolAssumeCapacity(self, try self.addString(gpa, name), .{}); } - self.dyld_stub_binder_index = createSymbol(self, try self.addString(gpa, "dyld_stub_binder"), .{}); - self.objc_msg_send_index = createSymbol(self, try self.addString(gpa, "_objc_msgSend"), .{}); + self.dyld_stub_binder_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "dyld_stub_binder"), .{}); + self.objc_msg_send_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "_objc_msgSend"), .{}); if (!macho_file.base.isDynLib()) { - self.entry_index = createSymbol(self, try self.addString(gpa, macho_file.entry_name orelse "_main"), .{}); - self.mh_execute_header_index = createSymbol(self, try self.addString(gpa, "__mh_execute_header"), .{ + self.entry_index = newSymbolAssumeCapacity(self, try self.addString(gpa, macho_file.entry_name orelse "_main"), .{}); + self.mh_execute_header_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__mh_execute_header"), .{ .type = macho.N_SECT | macho.N_EXT, .desc = macho.REFERENCED_DYNAMICALLY, }); } else { - self.mh_dylib_header_index = createSymbol(self, try self.addString(gpa, "__mh_dylib_header"), .{ + self.mh_dylib_header_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__mh_dylib_header"), .{ .type = macho.N_SECT | macho.N_EXT, }); } - self.dso_handle_index = createSymbol(self, try self.addString(gpa, "___dso_handle"), .{ + self.dso_handle_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "___dso_handle"), .{ .type = macho.N_SECT | macho.N_EXT, }); - self.dyld_private_index = createSymbol(self, try self.addString(gpa, "dyld_private"), .{ + self.dyld_private_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "dyld_private"), .{ .type = macho.N_SECT, }); } diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index b363ab91ed..fdeec186e5 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -141,34 +141,64 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void { } } -fn addNlist(self: *ZigObject, allocator: Allocator) !Symbol.Index { +fn newSymbol(self: *ZigObject, allocator: Allocator, name: u32, args: struct { + type: u8 = macho.N_UNDF | macho.N_EXT, + desc: u16 = 0, +}) !Symbol.Index { try self.symtab.ensureUnusedCapacity(allocator, 1); - const index = @as(Symbol.Index, @intCast(self.symtab.addOneAssumeCapacity())); - self.symtab.set(index, .{ - .nlist = MachO.null_sym, + try self.symbols.ensureUnusedCapacity(allocator, 1); + try self.symbols_extra.ensureUnusedCapacity(allocator, @sizeOf(Symbol.Extra)); + try self.globals.ensureUnusedCapacity(allocator, 1); + + const index = self.addSymbolAssumeCapacity(); + const symbol = &self.symbols.items[index]; + symbol.name = name; + symbol.extra = self.addSymbolExtraAssumeCapacity(.{}); + + const nlist_idx: u32 = @intCast(self.symtab.addOneAssumeCapacity()); + self.symtab.set(nlist_idx, .{ + .nlist = .{ + .n_strx = name, + .n_type = args.type, + .n_sect = 0, + .n_desc = args.desc, + .n_value = 0, + }, .size = 0, .atom = 0, }); + symbol.nlist_idx = nlist_idx; + + self.globals.appendAssumeCapacity(0); + return index; } -pub fn createAtomForDecl(self: *ZigObject, allocator: Allocator, macho_file: *MachO) !Symbol.Index { - const atom_index = try self.addAtom(allocator); - const symbol_index = try self.addSymbol(allocator); - const nlist_index = try self.addNlist(allocator); - self.symtab.items(.atom)[nlist_index] = atom_index; - try self.atoms_indexes.append(allocator, atom_index); - const symbol = &self.symbols.items[symbol_index]; - symbol.atom_ref = .{ .index = atom_index, .file = self.index }; - symbol.nlist_idx = nlist_index; - symbol.extra = try self.addSymbolExtra(allocator, .{}); +fn newAtom(self: *ZigObject, allocator: Allocator, name: u32, macho_file: *MachO) !Atom.Index { + try self.atoms.ensureUnusedCapacity(allocator, 1); + try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra)); + try self.atoms_indexes.ensureUnusedCapacity(allocator, 1); + try self.relocs.ensureUnusedCapacity(allocator, 1); + + const index = self.addAtomAssumeCapacity(); + self.atoms_indexes.appendAssumeCapacity(index); + const atom = self.getAtom(index).?; + atom.name = name; + const relocs_index = @as(u32, @intCast(self.relocs.items.len)); - const relocs = try self.relocs.addOne(allocator); - relocs.* = .{}; - const atom = self.getAtom(atom_index).?; + self.relocs.addOneAssumeCapacity().* = .{}; atom.addExtra(.{ .rel_index = relocs_index, .rel_count = 0 }, macho_file); - try self.globals.append(allocator, 0); - return symbol_index; + + return index; +} + +fn newSymbolWithAtom(self: *ZigObject, allocator: Allocator, name: u32, macho_file: *MachO) !Symbol.Index { + const atom_index = try self.newAtom(allocator, name, macho_file); + const sym_index = try self.newSymbol(allocator, name, .{ .type = macho.N_SECT }); + const sym = &self.symbols.items[sym_index]; + sym.atom_ref = .{ .index = atom_index, .file = self.index }; + self.symtab.items(.atom)[sym.nlist_idx] = atom_index; + return sym_index; } pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8) !void { @@ -1079,27 +1109,19 @@ fn createTlvInitializer( const gpa = macho_file.base.comp.gpa; const sym_name = try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{name}); defer gpa.free(sym_name); + const off = try self.strtab.insert(gpa, sym_name); - const sym_index = try self.createAtomForDecl(gpa, macho_file); + const sym_index = try self.newSymbolWithAtom(gpa, off, macho_file); const sym = &self.symbols.items[sym_index]; const nlist = &self.symtab.items(.nlist)[sym.nlist_idx]; const atom = sym.getAtom(macho_file).?; - sym.out_n_sect = sect_index; atom.out_n_sect = sect_index; - - sym.value = 0; - sym.name = try self.strtab.insert(gpa, sym_name); atom.flags.alive = true; - atom.name = sym.name; - nlist.n_strx = sym.name; - nlist.n_sect = sect_index + 1; - nlist.n_type = macho.N_SECT; - nlist.n_value = 0; - self.symtab.items(.size)[sym.nlist_idx] = code.len; - atom.alignment = alignment; atom.size = code.len; + nlist.n_sect = sect_index + 1; + self.symtab.items(.size)[sym.nlist_idx] = code.len; const slice = macho_file.sections.slice(); const header = slice.items(.header)[sect_index]; @@ -1293,7 +1315,8 @@ fn lowerConst( var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); - const sym_index = try self.createAtomForDecl(gpa, macho_file); + const name_str_index = try self.strtab.insert(gpa, name); + const sym_index = try self.newSymbolWithAtom(gpa, name_str_index, macho_file); const res = try codegen.generateSymbol(&macho_file.base, pt, src_loc, val, &code_buffer, .{ .none = {}, @@ -1306,19 +1329,14 @@ fn lowerConst( }; const sym = &self.symbols.items[sym_index]; - const name_str_index = try self.strtab.insert(gpa, name); - sym.name = name_str_index; sym.out_n_sect = output_section_index; const nlist = &self.symtab.items(.nlist)[sym.nlist_idx]; - nlist.n_strx = name_str_index; - nlist.n_type = macho.N_SECT; nlist.n_sect = output_section_index + 1; self.symtab.items(.size)[sym.nlist_idx] = code.len; const atom = sym.getAtom(macho_file).?; atom.flags.alive = true; - atom.name = name_str_index; atom.alignment = required_alignment; atom.size = code.len; atom.out_n_sect = output_section_index; @@ -1327,9 +1345,6 @@ fn lowerConst( // TODO rename and re-audit this method errdefer self.freeDeclMetadata(macho_file, sym_index); - sym.value = 0; - nlist.n_value = 0; - const sect = macho_file.sections.items(.header)[output_section_index]; const file_offset = sect.offset + atom.value; try macho_file.base.file.?.pwriteAll(code, file_offset); @@ -1560,17 +1575,9 @@ pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, l const off = try self.strtab.insert(gpa, sym_name); const lookup_gop = try self.globals_lookup.getOrPut(gpa, off); if (!lookup_gop.found_existing) { - const sym_index = try self.addSymbol(gpa); + const sym_index = try self.newSymbol(gpa, off, .{}); const sym = &self.symbols.items[sym_index]; - const nlist_index = try self.addNlist(gpa); - const nlist = &self.symtab.items(.nlist)[nlist_index]; - nlist.n_strx = off; - nlist.n_type = macho.N_EXT; - sym.name = off; - sym.nlist_idx = nlist_index; - sym.extra = try self.addSymbolExtra(gpa, .{}); - lookup_gop.value_ptr.* = nlist_index; - try self.globals.append(gpa, 0); + lookup_gop.value_ptr.* = sym.nlist_idx; } return lookup_gop.value_ptr.*; } @@ -1584,10 +1591,10 @@ pub fn getOrCreateMetadataForDecl( const gop = try self.decls.getOrPut(gpa, decl_index); if (!gop.found_existing) { const any_non_single_threaded = macho_file.base.comp.config.any_non_single_threaded; - const sym_index = try self.createAtomForDecl(gpa, macho_file); + const sym_index = try self.newSymbolWithAtom(gpa, 0, macho_file); + const sym = &self.symbols.items[sym_index]; const mod = macho_file.base.comp.module.?; const decl = mod.declPtr(decl_index); - const sym = &self.symbols.items[sym_index]; if (decl.getOwnedVariable(mod)) |variable| { if (variable.is_threadlocal and any_non_single_threaded) { sym.flags.tlv = true; @@ -1627,7 +1634,7 @@ pub fn getOrCreateMetadataForLazySymbol( }; switch (metadata.state.*) { .unused => { - const symbol_index = try self.createAtomForDecl(gpa, macho_file); + const symbol_index = try self.newSymbolWithAtom(gpa, 0, macho_file); const sym = &self.symbols.items[symbol_index]; sym.flags.needs_zig_got = true; metadata.symbol_index.* = symbol_index; @@ -1643,12 +1650,18 @@ pub fn getOrCreateMetadataForLazySymbol( } fn addAtom(self: *ZigObject, allocator: Allocator) !Atom.Index { + try self.atoms.ensureUnusedCapacity(allocator, 1); + try self.atoms_extra.ensureUnusedCapacity(allocator, 1); + return self.addAtomAssumeCapacity(); +} + +fn addAtomAssumeCapacity(self: *ZigObject) Atom.Index { const atom_index: Atom.Index = @intCast(self.atoms.items.len); - const atom = try self.atoms.addOne(allocator); + const atom = self.atoms.addOneAssumeCapacity(); atom.* = .{ .file = self.index, .atom_index = atom_index, - .extra = try self.addAtomExtra(allocator, .{}), + .extra = self.addAtomExtraAssumeCapacity(.{}), }; return atom_index; } From a9e3088d9ce9edd61765e610e5d1a5ea8a5f6733 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 15 Jul 2024 22:33:09 +0200 Subject: [PATCH 38/45] macho: extract testing logic for TLS into a helper --- src/link/MachO/ZigObject.zig | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index fdeec186e5..e75e8c9d19 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -958,14 +958,11 @@ pub fn updateDecl( return; }, }; - const sect_index = try self.getDeclOutputSection(macho_file, decl, code); - const is_threadlocal = switch (macho_file.sections.items(.header)[sect_index].type()) { - macho.S_THREAD_LOCAL_ZEROFILL, macho.S_THREAD_LOCAL_REGULAR => true, - else => false, - }; - if (is_threadlocal) { + if (isThreadlocal(macho_file, decl_index)) { + const sect_index = try self.getDeclOutputSection(macho_file, decl, code); try self.updateTlv(macho_file, pt, decl_index, sym_index, sect_index, code); } else { + const sect_index = try self.getDeclOutputSection(macho_file, decl, code); try self.updateDeclCode(macho_file, pt, decl_index, sym_index, sect_index, code); } @@ -1590,17 +1587,11 @@ pub fn getOrCreateMetadataForDecl( const gpa = macho_file.base.comp.gpa; const gop = try self.decls.getOrPut(gpa, decl_index); if (!gop.found_existing) { - const any_non_single_threaded = macho_file.base.comp.config.any_non_single_threaded; const sym_index = try self.newSymbolWithAtom(gpa, 0, macho_file); const sym = &self.symbols.items[sym_index]; - const mod = macho_file.base.comp.module.?; - const decl = mod.declPtr(decl_index); - if (decl.getOwnedVariable(mod)) |variable| { - if (variable.is_threadlocal and any_non_single_threaded) { - sym.flags.tlv = true; - } - } - if (!sym.flags.tlv) { + if (isThreadlocal(macho_file, decl_index)) { + sym.flags.tlv = true; + } else { sym.flags.needs_zig_got = true; } gop.value_ptr.* = .{ .symbol_index = sym_index }; @@ -1649,6 +1640,14 @@ pub fn getOrCreateMetadataForLazySymbol( return symbol_index; } +fn isThreadlocal(macho_file: *MachO, decl_index: InternPool.DeclIndex) bool { + const any_non_single_threaded = macho_file.base.comp.config.any_non_single_threaded; + const zcu = macho_file.base.comp.module.?; + const decl = zcu.declPtr(decl_index); + const variable = decl.getOwnedVariable(zcu) orelse return false; + return variable.is_threadlocal and any_non_single_threaded; +} + fn addAtom(self: *ZigObject, allocator: Allocator) !Atom.Index { try self.atoms.ensureUnusedCapacity(allocator, 1); try self.atoms_extra.ensureUnusedCapacity(allocator, 1); From 33388130775672df611091910e0cb1482a7eeb02 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 15 Jul 2024 22:53:52 +0200 Subject: [PATCH 39/45] macho: use isec for working out getAtomData in ZigObject --- src/link/MachO/ZigObject.zig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index e75e8c9d19..39eadc11c7 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -204,10 +204,10 @@ fn newSymbolWithAtom(self: *ZigObject, allocator: Allocator, name: u32, macho_fi pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8) !void { assert(atom.file == self.index); assert(atom.size == buffer.len); - const sect = macho_file.sections.items(.header)[atom.out_n_sect]; - assert(!sect.isZerofill()); + const isec = atom.getInputSection(macho_file); + assert(!isec.isZerofill()); - switch (sect.type()) { + switch (isec.type()) { macho.S_THREAD_LOCAL_REGULAR => { const tlv = self.tlv_initializers.get(atom.atom_index).?; @memcpy(buffer, tlv.data); @@ -216,6 +216,7 @@ pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8 @memset(buffer, 0); }, else => { + const sect = macho_file.sections.items(.header)[atom.out_n_sect]; const file_offset = sect.offset + atom.value; const amt = try macho_file.base.file.?.preadAll(buffer, file_offset); if (amt != buffer.len) return error.InputOutput; From e9328e7da81300f1b3d3ebf0c91c6cc650e2747b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 16 Jul 2024 15:38:35 +0200 Subject: [PATCH 40/45] macho: fix 32bit compilation issues --- src/link/MachO.zig | 5 ++-- src/link/MachO/Atom.zig | 2 +- src/link/MachO/InternalObject.zig | 10 ++++--- src/link/MachO/Object.zig | 43 ++++++++++++++++++++----------- src/link/MachO/ZigObject.zig | 14 +++++----- src/link/MachO/relocatable.zig | 3 ++- 6 files changed, 48 insertions(+), 29 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 7b695304bb..d86e2aca17 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -2343,7 +2343,8 @@ fn resizeSections(self: *MachO) !void { if (header.isZerofill()) continue; if (self.isZigSection(@intCast(n_sect))) continue; // TODO this is horrible const cpu_arch = self.getTarget().cpu.arch; - try out.resize(self.base.comp.gpa, header.size); + const size = math.cast(usize, header.size) orelse return error.Overflow; + try out.resize(self.base.comp.gpa, size); const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0; @memset(out.items, padding_byte); } @@ -2368,7 +2369,7 @@ fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void { } for (self.thunks.items) |thunk| { const out = self.sections.items(.out)[thunk.out_n_sect].items; - const off = thunk.value; + const off = math.cast(usize, thunk.value) orelse return error.Overflow; const size = thunk.size(); var stream = std.io.fixedBufferStream(out[off..][0..size]); try thunk.write(self, stream.writer()); diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index 0a2ebb931c..64f5723371 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -983,7 +983,7 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r var i: usize = 0; for (relocs) |rel| { defer i += 1; - const rel_offset = rel.offset - self.off; + const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow; const r_address: i32 = math.cast(i32, self.value + rel_offset) orelse return error.Overflow; assert(r_address >= 0); const r_symbolnum = r_symbolnum: { diff --git a/src/link/MachO/InternalObject.zig b/src/link/MachO/InternalObject.zig index 55d7be00fb..ca6a53e983 100644 --- a/src/link/MachO/InternalObject.zig +++ b/src/link/MachO/InternalObject.zig @@ -415,8 +415,9 @@ pub fn resolveLiterals(self: *InternalObject, lp: *MachO.LiteralPool, macho_file const rel = relocs[0]; assert(rel.tag == .@"extern"); const target = rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?; - try buffer.ensureUnusedCapacity(target.size); - buffer.resize(target.size) catch unreachable; + const target_size = std.math.cast(usize, target.size) orelse return error.Overflow; + try buffer.ensureUnusedCapacity(target_size); + buffer.resize(target_size) catch unreachable; @memcpy(buffer.items, try self.getSectionData(target.n_sect)); const res = try lp.insert(gpa, header.type(), buffer.items); buffer.clearRetainingCapacity(); @@ -572,8 +573,9 @@ pub fn writeAtoms(self: *InternalObject, macho_file: *MachO) !void { if (!atom.flags.alive) continue; const sect = atom.getInputSection(macho_file); if (sect.isZerofill()) continue; - const off = atom.value; - const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items[off..][0..atom.size]; + const off = std.math.cast(usize, atom.value) orelse return error.Overflow; + const size = std.math.cast(usize, atom.size) orelse return error.Overflow; + const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items[off..][0..size]; @memcpy(buffer, try self.getSectionData(atom.n_sect)); try atom.resolveRelocs(macho_file, buffer); } diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 7ab128aaeb..eda7ac91f1 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -1015,7 +1015,8 @@ fn initEhFrameRecords(self: *Object, allocator: Allocator, sect_id: u8, file: Fi const sect = slice.items(.header)[sect_id]; const relocs = slice.items(.relocs)[sect_id]; - try self.eh_frame_data.resize(allocator, sect.size); + const size = math.cast(usize, sect.size) orelse return error.Overflow; + try self.eh_frame_data.resize(allocator, size); const amt = try file.preadAll(self.eh_frame_data.items, sect.offset + self.offset); if (amt != self.eh_frame_data.items.len) return error.InputOutput; @@ -1116,7 +1117,8 @@ fn initUnwindRecords(self: *Object, allocator: Allocator, sect_id: u8, file: Fil }; const header = self.sections.items(.header)[sect_id]; - const data = try allocator.alloc(u8, header.size); + const size = math.cast(usize, header.size) orelse return error.Overflow; + const data = try allocator.alloc(u8, size); defer allocator.free(data); const amt = try file.preadAll(data, header.offset + self.offset); if (amt != data.len) return error.InputOutput; @@ -1346,7 +1348,8 @@ fn parseDebugInfo(self: *Object, macho_file: *MachO) !void { const file = macho_file.getFileHandle(self.file_handle); const debug_info = blk: { const sect = slice.items(.header)[debug_info_index.?]; - const data = try gpa.alloc(u8, sect.size); + const size = math.cast(usize, sect.size) orelse return error.Overflow; + const data = try gpa.alloc(u8, size); const amt = try file.preadAll(data, sect.offset + self.offset); if (amt != data.len) return error.InputOutput; break :blk data; @@ -1354,7 +1357,8 @@ fn parseDebugInfo(self: *Object, macho_file: *MachO) !void { defer gpa.free(debug_info); const debug_abbrev = blk: { const sect = slice.items(.header)[debug_abbrev_index.?]; - const data = try gpa.alloc(u8, sect.size); + const size = math.cast(usize, sect.size) orelse return error.Overflow; + const data = try gpa.alloc(u8, size); const amt = try file.preadAll(data, sect.offset + self.offset); if (amt != data.len) return error.InputOutput; break :blk data; @@ -1362,7 +1366,8 @@ fn parseDebugInfo(self: *Object, macho_file: *MachO) !void { defer gpa.free(debug_abbrev); const debug_str = if (debug_str_index) |sid| blk: { const sect = slice.items(.header)[sid]; - const data = try gpa.alloc(u8, sect.size); + const size = math.cast(usize, sect.size) orelse return error.Overflow; + const data = try gpa.alloc(u8, size); const amt = try file.preadAll(data, sect.offset + self.offset); if (amt != data.len) return error.InputOutput; break :blk data; @@ -1864,7 +1869,8 @@ pub fn writeAtoms(self: *Object, macho_file: *MachO) !void { for (headers, 0..) |header, n_sect| { if (header.isZerofill()) continue; - const data = try gpa.alloc(u8, header.size); + const size = math.cast(usize, header.size) orelse return error.Overflow; + const data = try gpa.alloc(u8, size); const amt = try file.preadAll(data, header.offset + self.offset); if (amt != data.len) return error.InputOutput; sections_data[n_sect] = data; @@ -1874,11 +1880,13 @@ pub fn writeAtoms(self: *Object, macho_file: *MachO) !void { if (!atom.flags.alive) continue; const sect = atom.getInputSection(macho_file); if (sect.isZerofill()) continue; - const off = atom.value; + const value = math.cast(usize, atom.value) orelse return error.Overflow; + const off = math.cast(usize, atom.off) orelse return error.Overflow; + const size = math.cast(usize, atom.size) orelse return error.Overflow; const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; const data = sections_data[atom.n_sect]; - @memcpy(buffer[off..][0..atom.size], data[atom.off..][0..atom.size]); - try atom.resolveRelocs(macho_file, buffer[off..][0..atom.size]); + @memcpy(buffer[value..][0..size], data[off..][0..size]); + try atom.resolveRelocs(macho_file, buffer[value..][0..size]); } } @@ -1900,7 +1908,8 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void { for (headers, 0..) |header, n_sect| { if (header.isZerofill()) continue; - const data = try gpa.alloc(u8, header.size); + const size = math.cast(usize, header.size) orelse return error.Overflow; + const data = try gpa.alloc(u8, size); const amt = try file.preadAll(data, header.offset + self.offset); if (amt != data.len) return error.InputOutput; sections_data[n_sect] = data; @@ -1910,13 +1919,15 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void { if (!atom.flags.alive) continue; const sect = atom.getInputSection(macho_file); if (sect.isZerofill()) continue; - const off = atom.value; + const value = math.cast(usize, atom.value) orelse return error.Overflow; + const off = math.cast(usize, atom.off) orelse return error.Overflow; + const size = math.cast(usize, atom.size) orelse return error.Overflow; const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; const data = sections_data[atom.n_sect]; - @memcpy(buffer[off..][0..atom.size], data[atom.off..][0..atom.size]); + @memcpy(buffer[value..][0..size], data[off..][0..size]); const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items; const extra = atom.getExtra(macho_file); - try atom.writeRelocs(macho_file, buffer[off..][0..atom.size], relocs[extra.rel_out_index..][0..extra.rel_out_count]); + try atom.writeRelocs(macho_file, buffer[value..][0..size], relocs[extra.rel_out_index..][0..extra.rel_out_count]); } } @@ -2812,7 +2823,8 @@ const x86_64 = struct { } const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc]; - const code = try gpa.alloc(u8, sect.size); + const sect_size = math.cast(usize, sect.size) orelse return error.Overflow; + const code = try gpa.alloc(u8, sect_size); defer gpa.free(code); { const amt = try handle.preadAll(code, sect.offset + self.offset); @@ -2984,7 +2996,8 @@ const aarch64 = struct { } const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc]; - const code = try gpa.alloc(u8, sect.size); + const sect_size = math.cast(usize, sect.size) orelse return error.Overflow; + const code = try gpa.alloc(u8, sect_size); defer gpa.free(code); { const amt = try handle.preadAll(code, sect.offset + self.offset); diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 39eadc11c7..d65cef5c10 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -529,12 +529,13 @@ pub fn writeAtomsRelocatable(self: *ZigObject, macho_file: *MachO) !void { if (sect.isZerofill()) continue; if (macho_file.isZigSection(atom.out_n_sect)) continue; if (atom.getRelocs(macho_file).len == 0) continue; - const off = atom.value; + const off = std.math.cast(usize, atom.value) orelse return error.Overflow; + const size = std.math.cast(usize, atom.size) orelse return error.Overflow; const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; - try self.getAtomData(macho_file, atom.*, buffer[off..][0..atom.size]); + try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]); const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items; const extra = atom.getExtra(macho_file); - try atom.writeRelocs(macho_file, buffer[off..][0..atom.size], relocs[extra.rel_out_index..][0..extra.rel_out_count]); + try atom.writeRelocs(macho_file, buffer[off..][0..size], relocs[extra.rel_out_index..][0..extra.rel_out_count]); } } @@ -551,10 +552,11 @@ pub fn writeAtoms(self: *ZigObject, macho_file: *MachO) !void { const sect = atom.getInputSection(macho_file); if (sect.isZerofill()) continue; if (macho_file.isZigSection(atom.out_n_sect)) continue; - const off = atom.value; + const off = std.math.cast(usize, atom.value) orelse return error.Overflow; + const size = std.math.cast(usize, atom.size) orelse return error.Overflow; const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items; - try self.getAtomData(macho_file, atom.*, buffer[off..][0..atom.size]); - try atom.resolveRelocs(macho_file, buffer[off..][0..atom.size]); + try self.getAtomData(macho_file, atom.*, buffer[off..][0..size]); + try atom.resolveRelocs(macho_file, buffer[off..][0..size]); } } diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index 5ea6ae18f3..51689ea226 100644 --- a/src/link/MachO/relocatable.zig +++ b/src/link/MachO/relocatable.zig @@ -626,7 +626,8 @@ fn writeSections(macho_file: *MachO) !void { for (slice.items(.header), slice.items(.out), slice.items(.relocs), 0..) |header, *out, *relocs, n_sect| { if (header.isZerofill()) continue; if (!macho_file.isZigSection(@intCast(n_sect))) { // TODO this is wrong; what about debug sections? - try out.resize(gpa, header.size); + const size = math.cast(usize, header.size) orelse return error.Overflow; + try out.resize(gpa, size); const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0; @memset(out.items, padding_byte); } From b339a306806bbc19a4e64746b5e0330b989bfead Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 16 Jul 2024 15:52:02 +0200 Subject: [PATCH 41/45] macho: fix off-by-one when populating strtab --- src/link/MachO.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index d86e2aca17..6061de7d14 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -2590,7 +2590,7 @@ fn calcSymtabSize(self: *MachO) !void { var nstabs: u32 = 0; var nexports: u32 = 0; var nimports: u32 = 0; - var strsize: u32 = 0; + var strsize: u32 = 1; for (files.items) |index| { const file = self.getFile(index).?; @@ -2624,7 +2624,7 @@ fn calcSymtabSize(self: *MachO) !void { { const cmd = &self.symtab_cmd; cmd.nsyms = nlocals + nstabs + nexports + nimports; - cmd.strsize = strsize + 1; + cmd.strsize = strsize; } { From d19aab2e872df58c56a17ca7b9ee1ea9aab82b99 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 16 Jul 2024 18:33:32 +0200 Subject: [PATCH 42/45] macho: bump max rss by a slight margin --- build.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.zig b/build.zig index bd811952fa..148e374a5f 100644 --- a/build.zig +++ b/build.zig @@ -620,7 +620,7 @@ fn addCompilerStep(b: *std.Build, options: AddCompilerStepOptions) *std.Build.St .root_source_file = b.path("src/main.zig"), .target = options.target, .optimize = options.optimize, - .max_rss = 7_000_000_000, + .max_rss = 7_100_000_000, .strip = options.strip, .sanitize_thread = options.sanitize_thread, .single_threaded = options.single_threaded, From 34f34dbe3246547d4586d284cf44510e3f8feaa2 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 16 Jul 2024 21:13:57 +0200 Subject: [PATCH 43/45] macho: reinstate duplicate definition checking --- src/link/MachO.zig | 59 ++++++++++++++++++------------------ src/link/MachO/Object.zig | 19 ------------ src/link/MachO/ZigObject.zig | 19 ------------ src/link/MachO/file.zig | 26 ++++++++++++++-- test/link/macho.zig | 32 +++++++++++++++++++ 5 files changed, 86 insertions(+), 69 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 6061de7d14..df11df30a1 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -26,6 +26,7 @@ resolver: SymbolResolver = .{}, /// This table will be populated after `scanRelocs` has run. /// Key is symbol index. undefs: std.AutoHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(Ref)) = .{}, +dupes: std.AutoHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(File.Index)) = .{}, dyld_info_cmd: macho.dyld_info_command = .{}, symtab_cmd: macho.symtab_command = .{}, @@ -311,6 +312,13 @@ pub fn deinit(self: *MachO) void { } self.undefs.deinit(gpa); } + { + var it = self.dupes.iterator(); + while (it.next()) |entry| { + entry.value_ptr.deinit(gpa); + } + self.dupes.deinit(gpa); + } self.symtab.deinit(gpa); self.strtab.deinit(gpa); @@ -518,14 +526,13 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n try dead_strip.gcAtoms(self); } - // TODO - // self.checkDuplicates() catch |err| switch (err) { - // error.HasDuplicates => return error.FlushFailure, - // else => |e| { - // try self.reportUnexpectedError("unexpected error while checking for duplicate symbol definitions", .{}); - // return e; - // }, - // }; + self.checkDuplicates() catch |err| switch (err) { + error.HasDuplicates => return error.FlushFailure, + else => |e| { + try self.reportUnexpectedError("unexpected error while checking for duplicate symbol definitions", .{}); + return e; + }, + }; self.markImportsAndExports(); self.deadStripDylibs(); @@ -1434,25 +1441,16 @@ fn claimUnresolved(self: *MachO) void { } fn checkDuplicates(self: *MachO) !void { - const gpa = self.base.comp.gpa; - - var dupes = std.AutoArrayHashMap(Symbol.Index, std.ArrayListUnmanaged(File.Index)).init(gpa); - defer { - for (dupes.values()) |*list| { - list.deinit(gpa); - } - dupes.deinit(); - } - if (self.getZigObject()) |zo| { - try zo.checkDuplicates(&dupes, self); + try zo.asFile().checkDuplicates(self); } - for (self.objects.items) |index| { - try self.getFile(index).?.object.checkDuplicates(&dupes, self); + try self.getFile(index).?.checkDuplicates(self); } - - try self.reportDuplicates(dupes); + if (self.getInternalObject()) |obj| { + try obj.asFile().checkDuplicates(self); + } + try self.reportDuplicates(); } fn markImportsAndExports(self: *MachO) void { @@ -3737,22 +3735,23 @@ pub fn reportUnexpectedError(self: *MachO, comptime format: []const u8, args: an try err.addNote(self, "please report this as a linker bug on https://github.com/ziglang/zig/issues/new/choose", .{}); } -fn reportDuplicates(self: *MachO, dupes: anytype) error{ HasDuplicates, OutOfMemory }!void { +fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void { const tracy = trace(@src()); defer tracy.end(); const max_notes = 3; var has_dupes = false; - var it = dupes.iterator(); + var it = self.dupes.iterator(); while (it.next()) |entry| { - const sym = self.getSymbol(entry.key_ptr.*); + const sym = self.resolver.keys.items[entry.key_ptr.* - 1]; const notes = entry.value_ptr.*; const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes); var err = try self.addErrorWithNotes(nnotes + 1); try err.addMsg(self, "duplicate symbol definition: {s}", .{sym.getName(self)}); try err.addNote(self, "defined by {}", .{sym.getFile(self).?.fmtPath()}); + has_dupes = true; var inote: usize = 0; while (inote < @min(notes.items.len, max_notes)) : (inote += 1) { @@ -3764,10 +3763,7 @@ fn reportDuplicates(self: *MachO, dupes: anytype) error{ HasDuplicates, OutOfMem const remaining = notes.items.len - max_notes; try err.addNote(self, "defined {d} more times", .{remaining}); } - - has_dupes = true; } - if (has_dupes) return error.HasDuplicates; } @@ -4452,6 +4448,11 @@ pub const SymbolResolver = struct { return ref.getSymbol(macho_file).?.getName(macho_file); } + pub fn getFile(key: Key, macho_file: *MachO) ?File { + const ref = Ref{ .index = key.index, .file = key.file }; + return ref.getFile(macho_file); + } + fn eql(key: Key, other: Key, macho_file: *MachO) bool { const key_name = key.getName(macho_file); const other_name = other.getName(macho_file); diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index eda7ac91f1..10d987b044 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -1550,25 +1550,6 @@ pub fn mergeSymbolVisibility(self: *Object, macho_file: *MachO) void { } } -// TODO -// pub fn checkDuplicates(self: *Object, dupes: anytype, macho_file: *MachO) error{OutOfMemory}!void { -// for (self.symbols.items, 0..) |index, nlist_idx| { -// const sym = macho_file.getSymbol(index); -// if (sym.visibility != .global) continue; -// const file = sym.getFile(macho_file) orelse continue; -// if (file.getIndex() == self.index) continue; - -// const nlist = self.symtab.items(.nlist)[nlist_idx]; -// if (!nlist.undf() and !nlist.tentative() and !(nlist.weakDef() or nlist.pext())) { -// const gop = try dupes.getOrPut(index); -// if (!gop.found_existing) { -// gop.value_ptr.* = .{}; -// } -// try gop.value_ptr.append(macho_file.base.comp.gpa, self.index); -// } -// } -// } - pub fn scanRelocs(self: *Object, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index d65cef5c10..f8ec5171f4 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -308,25 +308,6 @@ pub fn mergeSymbolVisibility(self: *ZigObject, macho_file: *MachO) void { } } -// TODO -// pub fn checkDuplicates(self: *ZigObject, dupes: anytype, macho_file: *MachO) !void { -// for (self.symbols.items, 0..) |index, nlist_idx| { -// const sym = macho_file.getSymbol(index); -// if (sym.visibility != .global) continue; -// const file = sym.getFile(macho_file) orelse continue; -// if (file.getIndex() == self.index) continue; - -// const nlist = self.symtab.items(.nlist)[nlist_idx]; -// if (!nlist.undf() and !nlist.tentative() and !(nlist.weakDef() or nlist.pext())) { -// const gop = try dupes.getOrPut(index); -// if (!gop.found_existing) { -// gop.value_ptr.* = .{}; -// } -// try gop.value_ptr.append(macho_file.base.comp.gpa, self.index); -// } -// } -// } - pub fn resolveLiterals(self: *ZigObject, lp: *MachO.LiteralPool, macho_file: *MachO) !void { _ = self; _ = lp; diff --git a/src/link/MachO/file.zig b/src/link/MachO/file.zig index a035c79ffb..b9d9d407c9 100644 --- a/src/link/MachO/file.zig +++ b/src/link/MachO/file.zig @@ -24,7 +24,7 @@ pub const File = union(enum) { _ = options; switch (file) { .zig_object => |x| try writer.writeAll(x.path), - .internal => try writer.writeAll(""), + .internal => try writer.writeAll("internal"), .object => |x| try writer.print("{}", .{x.fmtPath()}), .dylib => |x| try writer.writeAll(x.path), } @@ -57,7 +57,7 @@ pub const File = union(enum) { weak: bool = false, tentative: bool = false, }) u32 { - if (file == .object and !args.archive) { + if (file != .dylib and !args.archive) { const base: u32 = blk: { if (args.tentative) break :blk 3; break :blk if (args.weak) 2 else 1; @@ -254,6 +254,28 @@ pub const File = union(enum) { } } + pub fn checkDuplicates(file: File, macho_file: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const gpa = macho_file.base.comp.gpa; + + for (file.getSymbols(), file.getNlists(), 0..) |sym, nlist, i| { + if (sym.visibility != .global) continue; + if (sym.flags.weak) continue; + if (nlist.undf()) continue; + const ref = file.getSymbolRef(@intCast(i), macho_file); + const ref_file = ref.getFile(macho_file) orelse continue; + if (ref_file.getIndex() == file.getIndex()) continue; + + const gop = try macho_file.dupes.getOrPut(gpa, file.getGlobals()[i]); + if (!gop.found_existing) { + gop.value_ptr.* = .{}; + } + try gop.value_ptr.append(gpa, file.getIndex()); + } + } + pub fn initOutputSections(file: File, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); diff --git a/test/link/macho.zig b/test/link/macho.zig index 6c047ee322..12f4d9b492 100644 --- a/test/link/macho.zig +++ b/test/link/macho.zig @@ -30,6 +30,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { // Exercise linker with LLVM backend macho_step.dependOn(testDeadStrip(b, .{ .target = default_target })); + macho_step.dependOn(testDuplicateDefinitions(b, .{ .target = default_target })); macho_step.dependOn(testEmptyObject(b, .{ .target = default_target })); macho_step.dependOn(testEmptyZig(b, .{ .target = default_target })); macho_step.dependOn(testEntryPoint(b, .{ .target = default_target })); @@ -182,6 +183,37 @@ fn testDeadStrip(b: *Build, opts: Options) *Step { return test_step; } +fn testDuplicateDefinitions(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "duplicate-definitions", opts); + + const obj = addObject(b, opts, .{ .name = "a", .zig_source_bytes = + \\var x: usize = 1; + \\export fn strong() void { x += 1; } + \\export fn weak() void { x += 1; } + }); + + const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes = + \\var x: usize = 1; + \\export fn strong() void { x += 1; } + \\comptime { @export(weakImpl, .{ .name = "weak", .linkage = .weak }); } + \\fn weakImpl() callconv(.C) void { x += 1; } + \\extern fn weak() void; + \\pub fn main() void { + \\ weak(); + \\ strong(); + \\} + }); + exe.addObject(obj); + + expectLinkErrors(exe, test_step, .{ .exact = &.{ + "error: duplicate symbol definition: _strong", + "note: defined by /?/a.o", + "note: defined by /?/main.o", + } }); + + return test_step; +} + fn testDeadStripDylibs(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "dead-strip-dylibs", opts); From 3bdbf81a3f574c0cf538b443825558e45bceecee Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 17 Jul 2024 00:14:52 +0200 Subject: [PATCH 44/45] macho: fix emitting data-in-code entries --- src/link/MachO.zig | 6 +++++- src/link/MachO/synthetic.zig | 33 ++++++++++++++++++++++++++------- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index df11df30a1..08cbc9cb67 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -2521,8 +2521,12 @@ fn writeDyldInfo(self: *MachO) !void { pub fn writeDataInCode(self: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); + const gpa = self.base.comp.gpa; const cmd = self.data_in_code_cmd; - try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.data_in_code.entries.items), cmd.dataoff); + var buffer = try std.ArrayList(u8).initCapacity(gpa, self.data_in_code.size()); + defer buffer.deinit(); + try self.data_in_code.write(self, buffer.writer()); + try self.base.file.?.pwriteAll(buffer.items, cmd.dataoff); } fn writeIndsymtab(self: *MachO) !void { diff --git a/src/link/MachO/synthetic.zig b/src/link/MachO/synthetic.zig index 8b5a87c26e..d7316d63b7 100644 --- a/src/link/MachO/synthetic.zig +++ b/src/link/MachO/synthetic.zig @@ -656,7 +656,7 @@ pub const Indsymtab = struct { }; pub const DataInCode = struct { - entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, + entries: std.ArrayListUnmanaged(Entry) = .{}, pub fn deinit(dice: *DataInCode, allocator: Allocator) void { dice.entries.deinit(allocator); @@ -668,10 +668,6 @@ pub const DataInCode = struct { pub fn updateSize(dice: *DataInCode, macho_file: *MachO) !void { const gpa = macho_file.base.comp.gpa; - const base_address = if (!macho_file.base.isRelocatable()) - macho_file.getTextSegment().vmaddr - else - 0; for (macho_file.objects.items) |index| { const object = macho_file.getFile(index).?.object; @@ -683,7 +679,6 @@ pub const DataInCode = struct { for (object.getAtoms()) |atom_index| { if (next_dice >= dices.len) break; const atom = object.getAtom(atom_index) orelse continue; - if (!atom.flags.alive) continue; const start_off = atom.getInputAddress(macho_file); const end_off = start_off + atom.size; const start_dice = next_dice; @@ -696,7 +691,8 @@ pub const DataInCode = struct { if (atom.flags.alive) for (dices[start_dice..next_dice]) |d| { dice.entries.appendAssumeCapacity(.{ - .offset = @intCast(atom.getAddress(macho_file) + d.offset - start_off - base_address), + .atom_ref = .{ .index = atom_index, .file = index }, + .offset = @intCast(d.offset - start_off), .length = d.length, .kind = d.kind, }); @@ -706,6 +702,29 @@ pub const DataInCode = struct { macho_file.data_in_code_cmd.datasize = math.cast(u32, dice.size()) orelse return error.Overflow; } + + pub fn write(dice: DataInCode, macho_file: *MachO, writer: anytype) !void { + const base_address = if (!macho_file.base.isRelocatable()) + macho_file.getTextSegment().vmaddr + else + 0; + for (dice.entries.items) |entry| { + const atom_address = entry.atom_ref.getAtom(macho_file).?.getAddress(macho_file); + const offset = atom_address + entry.offset - base_address; + try writer.writeStruct(macho.data_in_code_entry{ + .offset = @intCast(offset), + .length = entry.length, + .kind = entry.kind, + }); + } + } + + const Entry = struct { + atom_ref: MachO.Ref, + offset: u32, + length: u16, + kind: u16, + }; }; const aarch64 = @import("../aarch64.zig"); From f5a941b3d6238222f43da568c96cfa5b3e56ce5d Mon Sep 17 00:00:00 2001 From: mlugg Date: Thu, 18 Jul 2024 18:45:07 +0100 Subject: [PATCH 45/45] Sema: return module-relative path for `@src()` This is one possible approach to fixing an issue with reproducible builds where the compiler's cwd changes the paths returned by `@src()`. --- src/Sema.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 2b43b75132..b2603a98a8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -17787,8 +17787,7 @@ fn zirBuiltinSrc( }; const file_name_val = v: { - // The compiler must not call realpath anywhere. - const file_name = try fn_owner_decl.getFileScope(mod).fullPath(sema.arena); + const file_name = fn_owner_decl.getFileScope(mod).sub_file_path; const array_ty = try pt.intern(.{ .array_type = .{ .len = file_name.len, .sentinel = .zero_u8,