authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-07-11 22:35:52+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-07-18 09:13:08+02:00
logb62281a9c87caab22ff22fb783751a1cd784bcaa
tree79a558be8ca1a37314d222c4b304d229eedefff2
parent90c54f1eb6d34fb703b6c4e093580a0f2f70d559

macho: re-enable relocatable mode


5 files changed, 340 insertions(+), 363 deletions(-)

src/link/MachO.zig+10-3
...@@ -290,10 +290,16 @@ pub fn deinit(self: *MachO) void {...@@ -290,10 +290,16 @@ pub fn deinit(self: *MachO) void {
290 self.dylibs.deinit(gpa);290 self.dylibs.deinit(gpa);
291291
292 self.segments.deinit(gpa);292 self.segments.deinit(gpa);
293 for (self.sections.items(.atoms), self.sections.items(.out), self.sections.items(.thunks)) |*atoms, *out, *thnks| {293 for (
294 self.sections.items(.atoms),
295 self.sections.items(.out),
296 self.sections.items(.thunks),
297 self.sections.items(.relocs),
298 ) |*atoms, *out, *thnks, *relocs| {
294 atoms.deinit(gpa);299 atoms.deinit(gpa);
295 out.deinit(gpa);300 out.deinit(gpa);
296 thnks.deinit(gpa);301 thnks.deinit(gpa);
302 relocs.deinit(gpa);
297 }303 }
298 self.sections.deinit(gpa);304 self.sections.deinit(gpa);
299305
...@@ -1457,10 +1463,10 @@ pub fn dedupLiterals(self: *MachO) !void {...@@ -1457,10 +1463,10 @@ pub fn dedupLiterals(self: *MachO) !void {
14571463
1458fn claimUnresolved(self: *MachO) void {1464fn claimUnresolved(self: *MachO) void {
1459 if (self.getZigObject()) |zo| {1465 if (self.getZigObject()) |zo| {
1460 zo.claimUnresolved(self);1466 zo.asFile().claimUnresolved(self);
1461 }1467 }
1462 for (self.objects.items) |index| {1468 for (self.objects.items) |index| {
1463 self.getFile(index).?.object.claimUnresolved(self);1469 self.getFile(index).?.claimUnresolved(self);
1464 }1470 }
1465}1471}
14661472
...@@ -3987,6 +3993,7 @@ const Section = struct {...@@ -3987,6 +3993,7 @@ const Section = struct {
3987 last_atom_index: Atom.Index = 0,3993 last_atom_index: Atom.Index = 0,
3988 thunks: std.ArrayListUnmanaged(Thunk.Index) = .{},3994 thunks: std.ArrayListUnmanaged(Thunk.Index) = .{},
3989 out: std.ArrayListUnmanaged(u8) = .{},3995 out: std.ArrayListUnmanaged(u8) = .{},
3996 relocs: std.ArrayListUnmanaged(macho.relocation_info) = .{},
3990};3997};
39913998
3992pub const LiteralPool = struct {3999pub const LiteralPool = struct {
src/link/MachO/Atom.zig+1
...@@ -1008,6 +1008,7 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r...@@ -1008,6 +1008,7 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.r
1008 .r_extern = 0,1008 .r_extern = 0,
1009 .r_type = @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_ADDEND),1009 .r_type = @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_ADDEND),
1010 };1010 };
1011 i += 1;
1011 }1012 }
10121013
1013 const r_type: macho.reloc_type_arm64 = switch (rel.type) {1014 const r_type: macho.reloc_type_arm64 = switch (rel.type) {
src/link/MachO/Object.zig+8-56
...@@ -1636,56 +1636,6 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {...@@ -1636,56 +1636,6 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {
1636 }1636 }
1637}1637}
16381638
1639pub fn claimUnresolved(self: *Object, macho_file: *MachO) void {
1640 const tracy = trace(@src());
1641 defer tracy.end();
1642
1643 for (self.symbols.items, 0..) |*sym, i| {
1644 const nlist = self.symtab.items(.nlist)[i];
1645 if (!nlist.ext()) continue;
1646 if (!nlist.undf()) continue;
1647
1648 if (self.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
1649
1650 const is_import = switch (macho_file.undefined_treatment) {
1651 .@"error" => false,
1652 .warn, .suppress => nlist.weakRef(),
1653 .dynamic_lookup => true,
1654 };
1655 if (is_import) {
1656 sym.value = 0;
1657 sym.atom_ref = .{ .index = 0, .file = 0 };
1658 sym.flags.weak = false;
1659 sym.flags.weak_ref = nlist.weakRef();
1660 sym.flags.import = is_import;
1661 sym.visibility = .global;
1662
1663 const idx = self.globals.items[i];
1664 macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = self.index };
1665 }
1666 }
1667}
1668
1669pub fn claimUnresolvedRelocatable(self: *Object, macho_file: *MachO) void {
1670 const tracy = trace(@src());
1671 defer tracy.end();
1672
1673 for (self.symbols.items, self.symtab.items(.nlist), 0..) |*sym, nlist, i| {
1674 if (!nlist.ext()) continue;
1675 if (!nlist.undf()) continue;
1676 if (self.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
1677
1678 sym.value = 0;
1679 sym.atom_ref = .{ .index = 0, .file = 0 };
1680 sym.flags.weak_ref = nlist.weakRef();
1681 sym.flags.import = true;
1682 sym.visibility = .global;
1683
1684 const idx = self.globals.items[i];
1685 macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = self.index };
1686 }
1687}
1688
1689fn addSection(self: *Object, allocator: Allocator, segname: []const u8, sectname: []const u8) !u8 {1639fn addSection(self: *Object, allocator: Allocator, segname: []const u8, sectname: []const u8) !u8 {
1690 const n_sect = @as(u8, @intCast(try self.sections.addOne(allocator)));1640 const n_sect = @as(u8, @intCast(try self.sections.addOne(allocator)));
1691 self.sections.set(n_sect, .{1641 self.sections.set(n_sect, .{
...@@ -1936,7 +1886,7 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void {...@@ -1936,7 +1886,7 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void {
1936 const tracy = trace(@src());1886 const tracy = trace(@src());
1937 defer tracy.end();1887 defer tracy.end();
19381888
1939 const gpa = macho_file.base.allocator;1889 const gpa = macho_file.base.comp.gpa;
1940 const headers = self.sections.items(.header);1890 const headers = self.sections.items(.header);
1941 const sections_data = try gpa.alloc([]const u8, headers.len);1891 const sections_data = try gpa.alloc([]const u8, headers.len);
1942 defer {1892 defer {
...@@ -1995,15 +1945,17 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {...@@ -1995,15 +1945,17 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
1995 const tracy = trace(@src());1945 const tracy = trace(@src());
1996 defer tracy.end();1946 defer tracy.end();
19971947
1948 const cpu_arch = macho_file.getTarget().cpu.arch;
1949
1998 const addReloc = struct {1950 const addReloc = struct {
1999 fn addReloc(offset: u32, cpu_arch: std.Target.Cpu.Arch) !macho.relocation_info {1951 fn addReloc(offset: u32, arch: std.Target.Cpu.Arch) !macho.relocation_info {
2000 return .{1952 return .{
2001 .r_address = math.cast(i32, offset) orelse return error.Overflow,1953 .r_address = math.cast(i32, offset) orelse return error.Overflow,
2002 .r_symbolnum = 0,1954 .r_symbolnum = 0,
2003 .r_pcrel = 0,1955 .r_pcrel = 0,
2004 .r_length = 3,1956 .r_length = 3,
2005 .r_extern = 0,1957 .r_extern = 0,
2006 .r_type = switch (cpu_arch) {1958 .r_type = switch (arch) {
2007 .aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),1959 .aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
2008 .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),1960 .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
2009 else => unreachable,1961 else => unreachable,
...@@ -2039,7 +1991,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {...@@ -2039,7 +1991,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
2039 const atom = rec.getAtom(macho_file);1991 const atom = rec.getAtom(macho_file);
2040 const addr = rec.getAtomAddress(macho_file);1992 const addr = rec.getAtomAddress(macho_file);
2041 out.rangeStart = addr;1993 out.rangeStart = addr;
2042 var reloc = try addReloc(offset, macho_file.options.cpu_arch.?);1994 var reloc = try addReloc(offset, cpu_arch);
2043 reloc.r_symbolnum = atom.out_n_sect + 1;1995 reloc.r_symbolnum = atom.out_n_sect + 1;
2044 relocs[reloc_index] = reloc;1996 relocs[reloc_index] = reloc;
2045 reloc_index += 1;1997 reloc_index += 1;
...@@ -2048,7 +2000,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {...@@ -2048,7 +2000,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
2048 // Personality function2000 // Personality function
2049 if (rec.getPersonality(macho_file)) |sym| {2001 if (rec.getPersonality(macho_file)) |sym| {
2050 const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow;2002 const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow;
2051 var reloc = try addReloc(offset + 16, macho_file.options.cpu_arch.?);2003 var reloc = try addReloc(offset + 16, cpu_arch);
2052 reloc.r_symbolnum = r_symbolnum;2004 reloc.r_symbolnum = r_symbolnum;
2053 reloc.r_extern = 1;2005 reloc.r_extern = 1;
2054 relocs[reloc_index] = reloc;2006 relocs[reloc_index] = reloc;
...@@ -2059,7 +2011,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {...@@ -2059,7 +2011,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
2059 if (rec.getLsdaAtom(macho_file)) |atom| {2011 if (rec.getLsdaAtom(macho_file)) |atom| {
2060 const addr = rec.getLsdaAddress(macho_file);2012 const addr = rec.getLsdaAddress(macho_file);
2061 out.lsda = addr;2013 out.lsda = addr;
2062 var reloc = try addReloc(offset + 24, macho_file.options.cpu_arch.?);2014 var reloc = try addReloc(offset + 24, cpu_arch);
2063 reloc.r_symbolnum = atom.out_n_sect + 1;2015 reloc.r_symbolnum = atom.out_n_sect + 1;
2064 relocs[reloc_index] = reloc;2016 relocs[reloc_index] = reloc;
2065 reloc_index += 1;2017 reloc_index += 1;
src/link/MachO/file.zig+88
...@@ -118,7 +118,24 @@ pub const File = union(enum) {...@@ -118,7 +118,24 @@ pub const File = union(enum) {
118 };118 };
119 }119 }
120120
121 pub fn getNlists(file: File) []macho.nlist_64 {
122 return switch (file) {
123 .dylib => unreachable,
124 .internal => |x| x.symtab.items,
125 inline else => |x| x.symtab.items(.nlist),
126 };
127 }
128
129 pub fn getGlobals(file: File) []MachO.SymbolResolver.Index {
130 return switch (file) {
131 inline else => |x| x.globals.items,
132 };
133 }
134
121 pub fn markImportsExports(file: File, macho_file: *MachO) void {135 pub fn markImportsExports(file: File, macho_file: *MachO) void {
136 const tracy = trace(@src());
137 defer tracy.end();
138
122 const nsyms = switch (file) {139 const nsyms = switch (file) {
123 .dylib => unreachable,140 .dylib => unreachable,
124 inline else => |x| x.symbols.items.len,141 inline else => |x| x.symbols.items.len,
...@@ -138,7 +155,25 @@ pub const File = union(enum) {...@@ -138,7 +155,25 @@ pub const File = union(enum) {
138 }155 }
139 }156 }
140157
158 pub fn markExportsRelocatable(file: File, macho_file: *MachO) void {
159 const tracy = trace(@src());
160 defer tracy.end();
161
162 assert(file == .object or file == .zig_object);
163
164 for (file.getSymbols(), 0..) |*sym, i| {
165 const ref = file.getSymbolRef(@intCast(i), macho_file);
166 const other_file = ref.getFile(macho_file) orelse continue;
167 if (other_file.getIndex() != file.getIndex()) continue;
168 if (sym.visibility != .global) continue;
169 sym.flags.@"export" = true;
170 }
171 }
172
141 pub fn createSymbolIndirection(file: File, macho_file: *MachO) !void {173 pub fn createSymbolIndirection(file: File, macho_file: *MachO) !void {
174 const tracy = trace(@src());
175 defer tracy.end();
176
142 const nsyms = switch (file) {177 const nsyms = switch (file) {
143 inline else => |x| x.symbols.items.len,178 inline else => |x| x.symbols.items.len,
144 };179 };
...@@ -166,6 +201,59 @@ pub const File = union(enum) {...@@ -166,6 +201,59 @@ pub const File = union(enum) {
166 }201 }
167 }202 }
168203
204 pub fn claimUnresolved(file: File, macho_file: *MachO) void {
205 const tracy = trace(@src());
206 defer tracy.end();
207
208 assert(file == .object or file == .zig_object);
209
210 for (file.getSymbols(), file.getNlists(), 0..) |*sym, nlist, i| {
211 if (!nlist.ext()) continue;
212 if (!nlist.undf()) continue;
213
214 if (file.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
215
216 const is_import = switch (macho_file.undefined_treatment) {
217 .@"error" => false,
218 .warn, .suppress => nlist.weakRef(),
219 .dynamic_lookup => true,
220 };
221 if (is_import) {
222 sym.value = 0;
223 sym.atom_ref = .{ .index = 0, .file = 0 };
224 sym.flags.weak = false;
225 sym.flags.weak_ref = nlist.weakRef();
226 sym.flags.import = is_import;
227 sym.visibility = .global;
228
229 const idx = file.getGlobals()[i];
230 macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = file.getIndex() };
231 }
232 }
233 }
234
235 pub fn claimUnresolvedRelocatable(file: File, macho_file: *MachO) void {
236 const tracy = trace(@src());
237 defer tracy.end();
238
239 assert(file == .object or file == .zig_object);
240
241 for (file.getSymbols(), file.getNlists(), 0..) |*sym, nlist, i| {
242 if (!nlist.ext()) continue;
243 if (!nlist.undf()) continue;
244 if (file.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
245
246 sym.value = 0;
247 sym.atom_ref = .{ .index = 0, .file = 0 };
248 sym.flags.weak_ref = nlist.weakRef();
249 sym.flags.import = true;
250 sym.visibility = .global;
251
252 const idx = file.getGlobals()[i];
253 macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = file.getIndex() };
254 }
255 }
256
169 pub fn initOutputSections(file: File, macho_file: *MachO) !void {257 pub fn initOutputSections(file: File, macho_file: *MachO) !void {
170 const tracy = trace(@src());258 const tracy = trace(@src());
171 defer tracy.end();259 defer tracy.end();
src/link/MachO/relocatable.zig+233-304
...@@ -26,70 +26,51 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c...@@ -26,70 +26,51 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c
26 return;26 return;
27 }27 }
2828
29 @panic("TODO -r mode");29 for (positionals.items) |obj| {
3030 macho_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
31 // for (positionals.items) |obj| {31 error.MalformedObject,
32 // macho_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {32 error.MalformedArchive,
33 // error.MalformedObject,33 error.InvalidCpuArch,
34 // error.MalformedArchive,34 error.InvalidTarget,
35 // error.InvalidCpuArch,35 => continue, // already reported
36 // error.InvalidTarget,36 error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an object file", .{}),
37 // => continue, // already reported37 else => |e| try macho_file.reportParseError(
38 // error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an object file", .{}),38 obj.path,
39 // else => |e| try macho_file.reportParseError(39 "unexpected error: parsing input file failed with error {s}",
40 // obj.path,40 .{@errorName(e)},
41 // "unexpected error: parsing input file failed with error {s}",41 ),
42 // .{@errorName(e)},42 };
43 // ),43 }
44 // };
45 // }
4644
47 // if (comp.link_errors.items.len > 0) return error.FlushFailure;45 if (comp.link_errors.items.len > 0) return error.FlushFailure;
48
49 // try macho_file.addUndefinedGlobals();
50 // try macho_file.resolveSymbols();
51 // try macho_file.parseDebugInfo();
52 // try macho_file.dedupLiterals();
53 // markExports(macho_file);
54 // claimUnresolved(macho_file);
55 // try initOutputSections(macho_file);
56 // try macho_file.sortSections();
57 // try macho_file.addAtomsToSections();
58 // try calcSectionSizes(macho_file);
59
60 // try createSegment(macho_file);
61 // try allocateSections(macho_file);
62 // allocateSegment(macho_file);
63
64 // var off = off: {
65 // const seg = macho_file.segments.items[0];
66 // const off = math.cast(u32, seg.fileoff + seg.filesize) orelse return error.Overflow;
67 // break :off mem.alignForward(u32, off, @alignOf(macho.relocation_info));
68 // };
69 // off = allocateSectionsRelocs(macho_file, off);
70
71 // if (build_options.enable_logging) {
72 // state_log.debug("{}", .{macho_file.dumpState()});
73 // }
7446
75 // try macho_file.calcSymtabSize();47 try macho_file.resolveSymbols();
76 // try writeAtoms(macho_file);48 try macho_file.dedupLiterals();
77 // try writeCompactUnwind(macho_file);49 markExports(macho_file);
78 // try writeEhFrame(macho_file);50 claimUnresolved(macho_file);
51 try initOutputSections(macho_file);
52 try macho_file.sortSections();
53 try macho_file.addAtomsToSections();
54 try calcSectionSizes(macho_file);
7955
80 // off = mem.alignForward(u32, off, @alignOf(u64));56 try createSegment(macho_file);
81 // off = try macho_file.writeDataInCode(0, off);57 try allocateSections(macho_file);
82 // off = mem.alignForward(u32, off, @alignOf(u64));58 allocateSegment(macho_file);
83 // off = try macho_file.writeSymtab(off);
84 // off = mem.alignForward(u32, off, @alignOf(u64));
85 // off = try macho_file.writeStrtab(off);
8659
87 // // In order to please Apple ld (and possibly other MachO linkers in the wild),60 if (build_options.enable_logging) {
88 // // we will now sanitize segment names of Zig-specific segments.61 state_log.debug("{}", .{macho_file.dumpState()});
89 // sanitizeZigSections(macho_file);62 }
9063
91 // const ncmds, const sizeofcmds = try writeLoadCommands(macho_file);64 try writeSections(macho_file);
92 // try writeHeader(macho_file, ncmds, sizeofcmds);65 sortRelocs(macho_file);
66 try writeSectionsToFile(macho_file);
67
68 // In order to please Apple ld (and possibly other MachO linkers in the wild),
69 // we will now sanitize segment names of Zig-specific segments.
70 sanitizeZigSections(macho_file);
71
72 const ncmds, const sizeofcmds = try writeLoadCommands(macho_file);
73 try writeHeader(macho_file, ncmds, sizeofcmds);
93}74}
9475
95pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {76pub 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 {...@@ -353,9 +334,9 @@ pub fn claimUnresolved(macho_file: *MachO) void {
353334
354fn initOutputSections(macho_file: *MachO) !void {335fn initOutputSections(macho_file: *MachO) !void {
355 for (macho_file.objects.items) |index| {336 for (macho_file.objects.items) |index| {
356 const object = macho_file.getFile(index).?.object;337 const file = macho_file.getFile(index).?;
357 for (object.atoms.items) |atom_index| {338 for (file.getAtoms()) |atom_index| {
358 const atom = macho_file.getAtom(atom_index) orelse continue;339 const atom = file.getAtom(atom_index) orelse continue;
359 if (!atom.flags.alive) continue;340 if (!atom.flags.alive) continue;
360 atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);341 atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);
361 }342 }
...@@ -383,69 +364,141 @@ fn calcSectionSizes(macho_file: *MachO) !void {...@@ -383,69 +364,141 @@ fn calcSectionSizes(macho_file: *MachO) !void {
383 const tracy = trace(@src());364 const tracy = trace(@src());
384 defer tracy.end();365 defer tracy.end();
385366
386 const slice = macho_file.sections.slice();367 for (macho_file.sections.items(.atoms), 0..) |atoms, i| {
387 for (slice.items(.header), slice.items(.atoms)) |*header, atoms| {
388 if (atoms.items.len == 0) continue;368 if (atoms.items.len == 0) continue;
389 for (atoms.items) |atom_index| {369 calcSectionSize(macho_file, @intCast(i));
390 const atom = macho_file.getAtom(atom_index).?;
391 const atom_alignment = atom.alignment.toByteUnits() orelse 1;
392 const offset = mem.alignForward(u64, header.size, atom_alignment);
393 const padding = offset - header.size;
394 atom.value = offset;
395 header.size += padding + atom.size;
396 header.@"align" = @max(header.@"align", atom.alignment.toLog2Units());
397 header.nreloc += atom.calcNumRelocs(macho_file);
398 }
399 }370 }
400371
401 if (macho_file.unwind_info_sect_index) |index| {372 if (macho_file.eh_frame_sect_index) |_| {
402 calcCompactUnwindSize(macho_file, index);373 try calcEhFrameSize(macho_file);
403 }374 }
404375
405 if (macho_file.eh_frame_sect_index) |index| {376 for (macho_file.objects.items) |index| {
406 const sect = &macho_file.sections.items(.header)[index];377 if (macho_file.unwind_info_sect_index) |_| {
407 sect.size = try eh_frame.calcSize(macho_file);378 macho_file.getFile(index).?.object.calcCompactUnwindSizeRelocatable(macho_file);
408 sect.@"align" = 3;379 }
409 sect.nreloc = eh_frame.calcNumRelocs(macho_file);380 macho_file.getFile(index).?.calcSymtabSize(macho_file);
410 }381 }
411382
412 if (macho_file.getZigObject()) |zo| {383 try macho_file.data_in_code.updateSize(macho_file);
413 for (zo.atoms.items) |atom_index| {384
414 const atom = macho_file.getAtom(atom_index) orelse continue;385 calcCompactUnwindSize(macho_file);
415 if (!atom.flags.alive) continue;386 calcSymtabSize(macho_file);
416 const header = &macho_file.sections.items(.header)[atom.out_n_sect];387
417 if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;388 // TODO
418 header.nreloc += atom.calcNumRelocs(macho_file);389 // if (macho_file.getZigObject()) |zo| {
419 }390 // for (zo.atoms.items) |atom_index| {
391 // const atom = macho_file.getAtom(atom_index) orelse continue;
392 // if (!atom.flags.alive) continue;
393 // const header = &macho_file.sections.items(.header)[atom.out_n_sect];
394 // if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
395 // header.nreloc += atom.calcNumRelocs(macho_file);
396 // }
397 // }
398}
399
400fn calcSectionSize(macho_file: *MachO, sect_id: u8) void {
401 const tracy = trace(@src());
402 defer tracy.end();
403
404 const slice = macho_file.sections.slice();
405 const header = &slice.items(.header)[sect_id];
406 const atoms = slice.items(.atoms)[sect_id].items;
407 for (atoms) |ref| {
408 const atom = ref.getAtom(macho_file).?;
409 const atom_alignment = atom.alignment.toByteUnits() orelse 1;
410 const offset = mem.alignForward(u64, header.size, atom_alignment);
411 const padding = offset - header.size;
412 atom.value = offset;
413 header.size += padding + atom.size;
414 header.@"align" = @max(header.@"align", atom.alignment.toLog2Units());
415 const nreloc = atom.calcNumRelocs(macho_file);
416 atom.addExtra(.{ .rel_out_index = header.nreloc, .rel_out_count = nreloc }, macho_file);
417 header.nreloc += nreloc;
420 }418 }
421}419}
422420
423fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void {421fn calcEhFrameSize(macho_file: *MachO) !void {
424 var size: u32 = 0;422 const tracy = trace(@src());
423 defer tracy.end();
424
425 const header = &macho_file.sections.items(.header)[macho_file.eh_frame_sect_index.?];
426 header.size = try eh_frame.calcSize(macho_file);
427 header.@"align" = 3;
428 header.nreloc = eh_frame.calcNumRelocs(macho_file);
429}
430
431fn calcCompactUnwindSize(macho_file: *MachO) void {
432 const tracy = trace(@src());
433 defer tracy.end();
434
435 var nrec: u32 = 0;
425 var nreloc: u32 = 0;436 var nreloc: u32 = 0;
426437
427 for (macho_file.objects.items) |index| {438 for (macho_file.objects.items) |index| {
428 const object = macho_file.getFile(index).?.object;439 const ctx = &macho_file.getFile(index).?.object.compact_unwind_ctx;
429 for (object.unwind_records_indexes.items) |irec| {440 ctx.rec_index = nrec;
430 const rec = object.getUnwindRecord(irec);441 ctx.reloc_index = nreloc;
431 if (!rec.alive) continue;442 nrec += ctx.rec_count;
432 size += @sizeOf(macho.compact_unwind_entry);443 nreloc += ctx.reloc_count;
433 nreloc += 1;
434 if (rec.getPersonality(macho_file)) |_| {
435 nreloc += 1;
436 }
437 if (rec.getLsdaAtom(macho_file)) |_| {
438 nreloc += 1;
439 }
440 }
441 }444 }
442445
443 const sect = &macho_file.sections.items(.header)[sect_index];446 const sect = &macho_file.sections.items(.header)[macho_file.unwind_info_sect_index.?];
444 sect.size = size;447 sect.size = nrec * @sizeOf(macho.compact_unwind_entry);
445 sect.nreloc = nreloc;448 sect.nreloc = nreloc;
446 sect.@"align" = 3;449 sect.@"align" = 3;
447}450}
448451
452fn calcSymtabSize(macho_file: *MachO) void {
453 const tracy = trace(@src());
454 defer tracy.end();
455
456 var nlocals: u32 = 0;
457 var nstabs: u32 = 0;
458 var nexports: u32 = 0;
459 var nimports: u32 = 0;
460 var strsize: u32 = 1;
461
462 for (macho_file.objects.items) |index| {
463 const object = macho_file.getFile(index).?.object;
464 const ctx = &object.output_symtab_ctx;
465 ctx.ilocal = nlocals;
466 ctx.istab = nstabs;
467 ctx.iexport = nexports;
468 ctx.iimport = nimports;
469 ctx.stroff = strsize;
470 nlocals += ctx.nlocals;
471 nstabs += ctx.nstabs;
472 nexports += ctx.nexports;
473 nimports += ctx.nimports;
474 strsize += ctx.strsize;
475 }
476
477 for (macho_file.objects.items) |index| {
478 const object = macho_file.getFile(index).?.object;
479 const ctx = &object.output_symtab_ctx;
480 ctx.istab += nlocals;
481 ctx.iexport += nlocals + nstabs;
482 ctx.iimport += nlocals + nstabs + nexports;
483 }
484
485 {
486 const cmd = &macho_file.symtab_cmd;
487 cmd.nsyms = nlocals + nstabs + nexports + nimports;
488 cmd.strsize = strsize;
489 }
490
491 {
492 const cmd = &macho_file.dysymtab_cmd;
493 cmd.ilocalsym = 0;
494 cmd.nlocalsym = nlocals + nstabs;
495 cmd.iextdefsym = nlocals + nstabs;
496 cmd.nextdefsym = nexports;
497 cmd.iundefsym = nlocals + nstabs + nexports;
498 cmd.nundefsym = nimports;
499 }
500}
501
449fn allocateSections(macho_file: *MachO) !void {502fn allocateSections(macho_file: *MachO) !void {
450 const slice = macho_file.sections.slice();503 const slice = macho_file.sections.slice();
451 for (slice.items(.header)) |*header| {504 for (slice.items(.header)) |*header| {
...@@ -463,6 +516,37 @@ fn allocateSections(macho_file: *MachO) !void {...@@ -463,6 +516,37 @@ fn allocateSections(macho_file: *MachO) !void {
463 }516 }
464 header.size = needed_size;517 header.size = needed_size;
465 }518 }
519
520 var fileoff: u32 = 0;
521 for (slice.items(.header)) |header| {
522 fileoff = @max(fileoff, header.offset + @as(u32, @intCast(header.size)));
523 }
524
525 for (slice.items(.header)) |*header| {
526 if (header.nreloc == 0) continue;
527 header.reloff = mem.alignForward(u32, fileoff, @alignOf(macho.relocation_info));
528 fileoff = header.reloff + header.nreloc * @sizeOf(macho.relocation_info);
529 }
530
531 // In -r mode, there is no LINKEDIT segment and so we allocate required LINKEDIT commands
532 // as if they were detached or part of the single segment.
533
534 // DATA_IN_CODE
535 {
536 const cmd = &macho_file.data_in_code_cmd;
537 cmd.dataoff = fileoff;
538 fileoff += cmd.datasize;
539 fileoff = mem.alignForward(u32, fileoff, @alignOf(u64));
540 }
541
542 // SYMTAB
543 {
544 const cmd = &macho_file.symtab_cmd;
545 cmd.symoff = fileoff;
546 fileoff += cmd.nsyms * @sizeOf(macho.nlist_64);
547 fileoff = mem.alignForward(u32, fileoff, @alignOf(u32));
548 cmd.stroff = fileoff;
549 }
466}550}
467551
468/// Renames segment names in Zig sections to standard MachO segment names such as552/// Renames segment names in Zig sections to standard MachO segment names such as
...@@ -525,232 +609,77 @@ fn allocateSegment(macho_file: *MachO) void {...@@ -525,232 +609,77 @@ fn allocateSegment(macho_file: *MachO) void {
525 seg.filesize = fileoff - seg.fileoff;609 seg.filesize = fileoff - seg.fileoff;
526}610}
527611
528fn allocateSectionsRelocs(macho_file: *MachO, off: u32) u32 {
529 var fileoff = off;
530 const slice = macho_file.sections.slice();
531 for (slice.items(.header)) |*header| {
532 if (header.nreloc == 0) continue;
533 header.reloff = mem.alignForward(u32, fileoff, @alignOf(macho.relocation_info));
534 fileoff = header.reloff + header.nreloc * @sizeOf(macho.relocation_info);
535 }
536 return fileoff;
537}
538
539// We need to sort relocations in descending order to be compatible with Apple's linker.612// We need to sort relocations in descending order to be compatible with Apple's linker.
540fn sortReloc(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation_info) bool {613fn sortReloc(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation_info) bool {
541 _ = ctx;614 _ = ctx;
542 return lhs.r_address > rhs.r_address;615 return lhs.r_address > rhs.r_address;
543}616}
544617
545fn writeAtoms(macho_file: *MachO) !void {618fn sortRelocs(macho_file: *MachO) void {
619 const tracy = trace(@src());
620 defer tracy.end();
621
622 for (macho_file.sections.items(.relocs)) |*relocs| {
623 mem.sort(macho.relocation_info, relocs.items, {}, sortReloc);
624 }
625}
626
627fn writeSections(macho_file: *MachO) !void {
546 const tracy = trace(@src());628 const tracy = trace(@src());
547 defer tracy.end();629 defer tracy.end();
548630
549 const gpa = macho_file.base.comp.gpa;631 const gpa = macho_file.base.comp.gpa;
550 const cpu_arch = macho_file.getTarget().cpu.arch;632 const cpu_arch = macho_file.getTarget().cpu.arch;
551 const slice = macho_file.sections.slice();633 const slice = macho_file.sections.slice();
552634 for (slice.items(.header), slice.items(.out), slice.items(.relocs)) |header, *out, *relocs| {
553 var relocs = std.ArrayList(macho.relocation_info).init(gpa);
554 defer relocs.deinit();
555
556 for (slice.items(.header), slice.items(.atoms), 0..) |header, atoms, i| {
557 if (atoms.items.len == 0) continue;
558 if (header.isZerofill()) continue;635 if (header.isZerofill()) continue;
559 if (macho_file.isZigSection(@intCast(i)) or macho_file.isDebugSection(@intCast(i))) continue;636 try out.resize(gpa, header.size);
560
561 const size = math.cast(usize, header.size) orelse return error.Overflow;
562 const code = try gpa.alloc(u8, size);
563 defer gpa.free(code);
564 const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;637 const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;
565 @memset(code, padding_byte);638 @memset(out.items, padding_byte);
566639 try relocs.resize(gpa, header.nreloc);
567 try relocs.ensureTotalCapacity(header.nreloc);
568
569 for (atoms.items) |atom_index| {
570 const atom = macho_file.getAtom(atom_index).?;
571 assert(atom.flags.alive);
572 const off = math.cast(usize, atom.value) orelse return error.Overflow;
573 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
574 try atom.getData(macho_file, code[off..][0..atom_size]);
575 try atom.writeRelocs(macho_file, code[off..][0..atom_size], &relocs);
576 }
577
578 assert(relocs.items.len == header.nreloc);
579
580 mem.sort(macho.relocation_info, relocs.items, {}, sortReloc);
581
582 // TODO scattered writes?
583 try macho_file.base.file.?.pwriteAll(code, header.offset);
584 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
585
586 relocs.clearRetainingCapacity();
587 }640 }
588641
589 if (macho_file.getZigObject()) |zo| {642 const cmd = macho_file.symtab_cmd;
590 // TODO: this is ugly; perhaps we should aggregrate before?643 try macho_file.symtab.resize(gpa, cmd.nsyms);
591 var zo_relocs = std.AutoArrayHashMap(u8, std.ArrayList(macho.relocation_info)).init(gpa);644 try macho_file.strtab.resize(gpa, cmd.strsize);
592 defer {645 macho_file.strtab.items[0] = 0;
593 for (zo_relocs.values()) |*list| {
594 list.deinit();
595 }
596 zo_relocs.deinit();
597 }
598
599 for (macho_file.sections.items(.header), 0..) |header, n_sect| {
600 if (header.isZerofill()) continue;
601 if (!macho_file.isZigSection(@intCast(n_sect)) and !macho_file.isDebugSection(@intCast(n_sect))) continue;
602 const gop = try zo_relocs.getOrPut(@intCast(n_sect));
603 if (gop.found_existing) continue;
604 gop.value_ptr.* = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc);
605 }
606646
607 for (zo.atoms.items) |atom_index| {
608 const atom = macho_file.getAtom(atom_index) orelse continue;
609 if (!atom.flags.alive) continue;
610 const header = macho_file.sections.items(.header)[atom.out_n_sect];
611 if (header.isZerofill()) continue;
612 if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
613 if (atom.getRelocs(macho_file).len == 0) continue;
614 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
615 const code = try gpa.alloc(u8, atom_size);
616 defer gpa.free(code);
617 atom.getData(macho_file, code) catch |err| switch (err) {
618 error.InputOutput => {
619 try macho_file.reportUnexpectedError("fetching code for '{s}' failed", .{
620 atom.getName(macho_file),
621 });
622 return error.FlushFailure;
623 },
624 else => |e| {
625 try macho_file.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{
626 atom.getName(macho_file),
627 @errorName(e),
628 });
629 return error.FlushFailure;
630 },
631 };
632 const file_offset = header.offset + atom.value;
633 const rels = zo_relocs.getPtr(atom.out_n_sect).?;
634 try atom.writeRelocs(macho_file, code, rels);
635 try macho_file.base.file.?.pwriteAll(code, file_offset);
636 }
637
638 for (zo_relocs.keys(), zo_relocs.values()) |sect_id, rels| {
639 const header = macho_file.sections.items(.header)[sect_id];
640 assert(rels.items.len == header.nreloc);
641 mem.sort(macho.relocation_info, rels.items, {}, sortReloc);
642 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(rels.items), header.reloff);
643 }
644 }
645}
646
647fn writeCompactUnwind(macho_file: *MachO) !void {
648 const sect_index = macho_file.unwind_info_sect_index orelse return;
649 const gpa = macho_file.base.comp.gpa;
650 const header = macho_file.sections.items(.header)[sect_index];
651
652 const nrecs = math.cast(usize, @divExact(header.size, @sizeOf(macho.compact_unwind_entry))) orelse return error.Overflow;
653 var entries = try std.ArrayList(macho.compact_unwind_entry).initCapacity(gpa, nrecs);
654 defer entries.deinit();
655
656 var relocs = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc);
657 defer relocs.deinit();
658
659 const addReloc = struct {
660 fn addReloc(offset: i32, cpu_arch: std.Target.Cpu.Arch) macho.relocation_info {
661 return .{
662 .r_address = offset,
663 .r_symbolnum = 0,
664 .r_pcrel = 0,
665 .r_length = 3,
666 .r_extern = 0,
667 .r_type = switch (cpu_arch) {
668 .aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
669 .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
670 else => unreachable,
671 },
672 };
673 }
674 }.addReloc;
675
676 var offset: i32 = 0;
677 for (macho_file.objects.items) |index| {647 for (macho_file.objects.items) |index| {
678 const object = macho_file.getFile(index).?.object;648 try macho_file.getFile(index).?.object.writeAtomsRelocatable(macho_file);
679 for (object.unwind_records_indexes.items) |irec| {649 macho_file.getFile(index).?.writeSymtab(macho_file, macho_file);
680 const rec = object.getUnwindRecord(irec);650 }
681 if (!rec.alive) continue;
682
683 var out: macho.compact_unwind_entry = .{
684 .rangeStart = 0,
685 .rangeLength = rec.length,
686 .compactUnwindEncoding = rec.enc.enc,
687 .personalityFunction = 0,
688 .lsda = 0,
689 };
690
691 {
692 // Function address
693 const atom = rec.getAtom(macho_file);
694 const addr = rec.getAtomAddress(macho_file);
695 out.rangeStart = addr;
696 var reloc = addReloc(offset, macho_file.getTarget().cpu.arch);
697 reloc.r_symbolnum = atom.out_n_sect + 1;
698 relocs.appendAssumeCapacity(reloc);
699 }
700
701 // Personality function
702 if (rec.getPersonality(macho_file)) |sym| {
703 const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow;
704 var reloc = addReloc(offset + 16, macho_file.getTarget().cpu.arch);
705 reloc.r_symbolnum = r_symbolnum;
706 reloc.r_extern = 1;
707 relocs.appendAssumeCapacity(reloc);
708 }
709651
710 // LSDA address652 if (macho_file.eh_frame_sect_index) |_| {
711 if (rec.getLsdaAtom(macho_file)) |atom| {653 try writeEhFrame(macho_file);
712 const addr = rec.getLsdaAddress(macho_file);654 }
713 out.lsda = addr;
714 var reloc = addReloc(offset + 24, macho_file.getTarget().cpu.arch);
715 reloc.r_symbolnum = atom.out_n_sect + 1;
716 relocs.appendAssumeCapacity(reloc);
717 }
718655
719 entries.appendAssumeCapacity(out);656 if (macho_file.unwind_info_sect_index) |_| {
720 offset += @sizeOf(macho.compact_unwind_entry);657 for (macho_file.objects.items) |index| {
658 try macho_file.getFile(index).?.object.writeCompactUnwindRelocatable(macho_file);
721 }659 }
722 }660 }
723
724 assert(entries.items.len == nrecs);
725 assert(relocs.items.len == header.nreloc);
726
727 mem.sort(macho.relocation_info, relocs.items, {}, sortReloc);
728
729 // TODO scattered writes?
730 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(entries.items), header.offset);
731 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
732}661}
733662
734fn writeEhFrame(macho_file: *MachO) !void {663fn writeEhFrame(macho_file: *MachO) !void {
735 const sect_index = macho_file.eh_frame_sect_index orelse return;664 const sect_index = macho_file.eh_frame_sect_index.?;
736 const gpa = macho_file.base.comp.gpa;665 const buffer = macho_file.sections.items(.out)[sect_index];
737 const header = macho_file.sections.items(.header)[sect_index];666 const relocs = macho_file.sections.items(.relocs)[sect_index];
738 const size = math.cast(usize, header.size) orelse return error.Overflow;667 try eh_frame.writeRelocs(macho_file, buffer.items, relocs.items);
739668}
740 const code = try gpa.alloc(u8, size);
741 defer gpa.free(code);
742
743 var relocs = try std.ArrayList(macho.relocation_info).initCapacity(gpa, header.nreloc);
744 defer relocs.deinit();
745669
746 try eh_frame.writeRelocs(macho_file, code, &relocs);670fn writeSectionsToFile(macho_file: *MachO) !void {
747 assert(relocs.items.len == header.nreloc);671 const tracy = trace(@src());
672 defer tracy.end();
748673
749 mem.sort(macho.relocation_info, relocs.items, {}, sortReloc);674 const slice = macho_file.sections.slice();
675 for (slice.items(.header), slice.items(.out), slice.items(.relocs)) |header, out, relocs| {
676 try macho_file.base.file.?.pwriteAll(out.items, header.offset);
677 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
678 }
750679
751 // TODO scattered writes?680 try macho_file.writeDataInCode();
752 try macho_file.base.file.?.pwriteAll(code, header.offset);681 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(macho_file.symtab.items), macho_file.symtab_cmd.symoff);
753 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);682 try macho_file.base.file.?.pwriteAll(macho_file.strtab.items, macho_file.symtab_cmd.stroff);
754}683}
755684
756fn writeLoadCommands(macho_file: *MachO) !struct { usize, usize } {685fn writeLoadCommands(macho_file: *MachO) !struct { usize, usize } {