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 {
290290 self.dylibs.deinit(gpa);
291291
292292 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| {
294299 atoms.deinit(gpa);
295300 out.deinit(gpa);
296301 thnks.deinit(gpa);
302 relocs.deinit(gpa);
297303 }
298304 self.sections.deinit(gpa);
299305
......@@ -1457,10 +1463,10 @@ pub fn dedupLiterals(self: *MachO) !void {
14571463
14581464fn claimUnresolved(self: *MachO) void {
14591465 if (self.getZigObject()) |zo| {
1460 zo.claimUnresolved(self);
1466 zo.asFile().claimUnresolved(self);
14611467 }
14621468 for (self.objects.items) |index| {
1463 self.getFile(index).?.object.claimUnresolved(self);
1469 self.getFile(index).?.claimUnresolved(self);
14641470 }
14651471}
14661472
......@@ -3987,6 +3993,7 @@ const Section = struct {
39873993 last_atom_index: Atom.Index = 0,
39883994 thunks: std.ArrayListUnmanaged(Thunk.Index) = .{},
39893995 out: std.ArrayListUnmanaged(u8) = .{},
3996 relocs: std.ArrayListUnmanaged(macho.relocation_info) = .{},
39903997};
39913998
39923999pub 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
10081008 .r_extern = 0,
10091009 .r_type = @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_ADDEND),
10101010 };
1011 i += 1;
10111012 }
10121013
10131014 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 {
16361636 }
16371637}
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
16891639fn addSection(self: *Object, allocator: Allocator, segname: []const u8, sectname: []const u8) !u8 {
16901640 const n_sect = @as(u8, @intCast(try self.sections.addOne(allocator)));
16911641 self.sections.set(n_sect, .{
......@@ -1936,7 +1886,7 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void {
19361886 const tracy = trace(@src());
19371887 defer tracy.end();
19381888
1939 const gpa = macho_file.base.allocator;
1889 const gpa = macho_file.base.comp.gpa;
19401890 const headers = self.sections.items(.header);
19411891 const sections_data = try gpa.alloc([]const u8, headers.len);
19421892 defer {
......@@ -1995,15 +1945,17 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
19951945 const tracy = trace(@src());
19961946 defer tracy.end();
19971947
1948 const cpu_arch = macho_file.getTarget().cpu.arch;
1949
19981950 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 {
20001952 return .{
20011953 .r_address = math.cast(i32, offset) orelse return error.Overflow,
20021954 .r_symbolnum = 0,
20031955 .r_pcrel = 0,
20041956 .r_length = 3,
20051957 .r_extern = 0,
2006 .r_type = switch (cpu_arch) {
1958 .r_type = switch (arch) {
20071959 .aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
20081960 .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
20091961 else => unreachable,
......@@ -2039,7 +1991,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
20391991 const atom = rec.getAtom(macho_file);
20401992 const addr = rec.getAtomAddress(macho_file);
20411993 out.rangeStart = addr;
2042 var reloc = try addReloc(offset, macho_file.options.cpu_arch.?);
1994 var reloc = try addReloc(offset, cpu_arch);
20431995 reloc.r_symbolnum = atom.out_n_sect + 1;
20441996 relocs[reloc_index] = reloc;
20451997 reloc_index += 1;
......@@ -2048,7 +2000,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
20482000 // Personality function
20492001 if (rec.getPersonality(macho_file)) |sym| {
20502002 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);
20522004 reloc.r_symbolnum = r_symbolnum;
20532005 reloc.r_extern = 1;
20542006 relocs[reloc_index] = reloc;
......@@ -2059,7 +2011,7 @@ pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
20592011 if (rec.getLsdaAtom(macho_file)) |atom| {
20602012 const addr = rec.getLsdaAddress(macho_file);
20612013 out.lsda = addr;
2062 var reloc = try addReloc(offset + 24, macho_file.options.cpu_arch.?);
2014 var reloc = try addReloc(offset + 24, cpu_arch);
20632015 reloc.r_symbolnum = atom.out_n_sect + 1;
20642016 relocs[reloc_index] = reloc;
20652017 reloc_index += 1;
src/link/MachO/file.zig+88
......@@ -118,7 +118,24 @@ pub const File = union(enum) {
118118 };
119119 }
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
121135 pub fn markImportsExports(file: File, macho_file: *MachO) void {
136 const tracy = trace(@src());
137 defer tracy.end();
138
122139 const nsyms = switch (file) {
123140 .dylib => unreachable,
124141 inline else => |x| x.symbols.items.len,
......@@ -138,7 +155,25 @@ pub const File = union(enum) {
138155 }
139156 }
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
141173 pub fn createSymbolIndirection(file: File, macho_file: *MachO) !void {
174 const tracy = trace(@src());
175 defer tracy.end();
176
142177 const nsyms = switch (file) {
143178 inline else => |x| x.symbols.items.len,
144179 };
......@@ -166,6 +201,59 @@ pub const File = union(enum) {
166201 }
167202 }
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
169257 pub fn initOutputSections(file: File, macho_file: *MachO) !void {
170258 const tracy = trace(@src());
171259 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
2626 return;
2727 }
2828
29 @panic("TODO -r mode");
30
31 // for (positionals.items) |obj| {
32 // macho_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
33 // error.MalformedObject,
34 // error.MalformedArchive,
35 // error.InvalidCpuArch,
36 // error.InvalidTarget,
37 // => continue, // already reported
38 // error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an object file", .{}),
39 // else => |e| try macho_file.reportParseError(
40 // obj.path,
41 // "unexpected error: parsing input file failed with error {s}",
42 // .{@errorName(e)},
43 // ),
44 // };
45 // }
29 for (positionals.items) |obj| {
30 macho_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
31 error.MalformedObject,
32 error.MalformedArchive,
33 error.InvalidCpuArch,
34 error.InvalidTarget,
35 => continue, // already reported
36 error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an object file", .{}),
37 else => |e| try macho_file.reportParseError(
38 obj.path,
39 "unexpected error: parsing input file failed with error {s}",
40 .{@errorName(e)},
41 ),
42 };
43 }
4644
47 // 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 // }
45 if (comp.link_errors.items.len > 0) return error.FlushFailure;
7446
75 // try macho_file.calcSymtabSize();
76 // try writeAtoms(macho_file);
77 // try writeCompactUnwind(macho_file);
78 // try writeEhFrame(macho_file);
47 try macho_file.resolveSymbols();
48 try macho_file.dedupLiterals();
49 markExports(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));
81 // off = try macho_file.writeDataInCode(0, off);
82 // off = mem.alignForward(u32, off, @alignOf(u64));
83 // off = try macho_file.writeSymtab(off);
84 // off = mem.alignForward(u32, off, @alignOf(u64));
85 // off = try macho_file.writeStrtab(off);
56 try createSegment(macho_file);
57 try allocateSections(macho_file);
58 allocateSegment(macho_file);
8659
87 // // In order to please Apple ld (and possibly other MachO linkers in the wild),
88 // // we will now sanitize segment names of Zig-specific segments.
89 // sanitizeZigSections(macho_file);
60 if (build_options.enable_logging) {
61 state_log.debug("{}", .{macho_file.dumpState()});
62 }
9063
91 // const ncmds, const sizeofcmds = try writeLoadCommands(macho_file);
92 // try writeHeader(macho_file, ncmds, sizeofcmds);
64 try writeSections(macho_file);
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);
9374}
9475
9576pub 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 {
353334
354335fn initOutputSections(macho_file: *MachO) !void {
355336 for (macho_file.objects.items) |index| {
356 const object = macho_file.getFile(index).?.object;
357 for (object.atoms.items) |atom_index| {
358 const atom = macho_file.getAtom(atom_index) orelse continue;
337 const file = macho_file.getFile(index).?;
338 for (file.getAtoms()) |atom_index| {
339 const atom = file.getAtom(atom_index) orelse continue;
359340 if (!atom.flags.alive) continue;
360341 atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);
361342 }
......@@ -383,69 +364,141 @@ fn calcSectionSizes(macho_file: *MachO) !void {
383364 const tracy = trace(@src());
384365 defer tracy.end();
385366
386 const slice = macho_file.sections.slice();
387 for (slice.items(.header), slice.items(.atoms)) |*header, atoms| {
367 for (macho_file.sections.items(.atoms), 0..) |atoms, i| {
388368 if (atoms.items.len == 0) continue;
389 for (atoms.items) |atom_index| {
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 }
369 calcSectionSize(macho_file, @intCast(i));
399370 }
400371
401 if (macho_file.unwind_info_sect_index) |index| {
402 calcCompactUnwindSize(macho_file, index);
372 if (macho_file.eh_frame_sect_index) |_| {
373 try calcEhFrameSize(macho_file);
403374 }
404375
405 if (macho_file.eh_frame_sect_index) |index| {
406 const sect = &macho_file.sections.items(.header)[index];
407 sect.size = try eh_frame.calcSize(macho_file);
408 sect.@"align" = 3;
409 sect.nreloc = eh_frame.calcNumRelocs(macho_file);
376 for (macho_file.objects.items) |index| {
377 if (macho_file.unwind_info_sect_index) |_| {
378 macho_file.getFile(index).?.object.calcCompactUnwindSizeRelocatable(macho_file);
379 }
380 macho_file.getFile(index).?.calcSymtabSize(macho_file);
410381 }
411382
412 if (macho_file.getZigObject()) |zo| {
413 for (zo.atoms.items) |atom_index| {
414 const atom = macho_file.getAtom(atom_index) orelse continue;
415 if (!atom.flags.alive) continue;
416 const header = &macho_file.sections.items(.header)[atom.out_n_sect];
417 if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
418 header.nreloc += atom.calcNumRelocs(macho_file);
419 }
383 try macho_file.data_in_code.updateSize(macho_file);
384
385 calcCompactUnwindSize(macho_file);
386 calcSymtabSize(macho_file);
387
388 // TODO
389 // if (macho_file.getZigObject()) |zo| {
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;
420418 }
421419}
422420
423fn calcCompactUnwindSize(macho_file: *MachO, sect_index: u8) void {
424 var size: u32 = 0;
421fn calcEhFrameSize(macho_file: *MachO) !void {
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;
425436 var nreloc: u32 = 0;
426437
427438 for (macho_file.objects.items) |index| {
428 const object = macho_file.getFile(index).?.object;
429 for (object.unwind_records_indexes.items) |irec| {
430 const rec = object.getUnwindRecord(irec);
431 if (!rec.alive) continue;
432 size += @sizeOf(macho.compact_unwind_entry);
433 nreloc += 1;
434 if (rec.getPersonality(macho_file)) |_| {
435 nreloc += 1;
436 }
437 if (rec.getLsdaAtom(macho_file)) |_| {
438 nreloc += 1;
439 }
440 }
439 const ctx = &macho_file.getFile(index).?.object.compact_unwind_ctx;
440 ctx.rec_index = nrec;
441 ctx.reloc_index = nreloc;
442 nrec += ctx.rec_count;
443 nreloc += ctx.reloc_count;
441444 }
442445
443 const sect = &macho_file.sections.items(.header)[sect_index];
444 sect.size = size;
446 const sect = &macho_file.sections.items(.header)[macho_file.unwind_info_sect_index.?];
447 sect.size = nrec * @sizeOf(macho.compact_unwind_entry);
445448 sect.nreloc = nreloc;
446449 sect.@"align" = 3;
447450}
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
449502fn allocateSections(macho_file: *MachO) !void {
450503 const slice = macho_file.sections.slice();
451504 for (slice.items(.header)) |*header| {
......@@ -463,6 +516,37 @@ fn allocateSections(macho_file: *MachO) !void {
463516 }
464517 header.size = needed_size;
465518 }
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 }
466550}
467551
468552/// Renames segment names in Zig sections to standard MachO segment names such as
......@@ -525,232 +609,77 @@ fn allocateSegment(macho_file: *MachO) void {
525609 seg.filesize = fileoff - seg.fileoff;
526610}
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
539612// We need to sort relocations in descending order to be compatible with Apple's linker.
540613fn sortReloc(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation_info) bool {
541614 _ = ctx;
542615 return lhs.r_address > rhs.r_address;
543616}
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 {
546628 const tracy = trace(@src());
547629 defer tracy.end();
548630
549631 const gpa = macho_file.base.comp.gpa;
550632 const cpu_arch = macho_file.getTarget().cpu.arch;
551633 const slice = macho_file.sections.slice();
552
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;
634 for (slice.items(.header), slice.items(.out), slice.items(.relocs)) |header, *out, *relocs| {
558635 if (header.isZerofill()) continue;
559 if (macho_file.isZigSection(@intCast(i)) or macho_file.isDebugSection(@intCast(i))) continue;
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);
636 try out.resize(gpa, header.size);
564637 const padding_byte: u8 = if (header.isCode() and cpu_arch == .x86_64) 0xcc else 0;
565 @memset(code, padding_byte);
566
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();
638 @memset(out.items, padding_byte);
639 try relocs.resize(gpa, header.nreloc);
587640 }
588641
589 if (macho_file.getZigObject()) |zo| {
590 // TODO: this is ugly; perhaps we should aggregrate before?
591 var zo_relocs = std.AutoArrayHashMap(u8, std.ArrayList(macho.relocation_info)).init(gpa);
592 defer {
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 }
642 const cmd = macho_file.symtab_cmd;
643 try macho_file.symtab.resize(gpa, cmd.nsyms);
644 try macho_file.strtab.resize(gpa, cmd.strsize);
645 macho_file.strtab.items[0] = 0;
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;
677647 for (macho_file.objects.items) |index| {
678 const object = macho_file.getFile(index).?.object;
679 for (object.unwind_records_indexes.items) |irec| {
680 const rec = object.getUnwindRecord(irec);
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 }
648 try macho_file.getFile(index).?.object.writeAtomsRelocatable(macho_file);
649 macho_file.getFile(index).?.writeSymtab(macho_file, macho_file);
650 }
709651
710 // LSDA address
711 if (rec.getLsdaAtom(macho_file)) |atom| {
712 const addr = rec.getLsdaAddress(macho_file);
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 }
652 if (macho_file.eh_frame_sect_index) |_| {
653 try writeEhFrame(macho_file);
654 }
718655
719 entries.appendAssumeCapacity(out);
720 offset += @sizeOf(macho.compact_unwind_entry);
656 if (macho_file.unwind_info_sect_index) |_| {
657 for (macho_file.objects.items) |index| {
658 try macho_file.getFile(index).?.object.writeCompactUnwindRelocatable(macho_file);
721659 }
722660 }
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);
732661}
733662
734663fn writeEhFrame(macho_file: *MachO) !void {
735 const sect_index = macho_file.eh_frame_sect_index orelse return;
736 const gpa = macho_file.base.comp.gpa;
737 const header = macho_file.sections.items(.header)[sect_index];
738 const size = math.cast(usize, header.size) orelse return error.Overflow;
739
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();
664 const sect_index = macho_file.eh_frame_sect_index.?;
665 const buffer = macho_file.sections.items(.out)[sect_index];
666 const relocs = macho_file.sections.items(.relocs)[sect_index];
667 try eh_frame.writeRelocs(macho_file, buffer.items, relocs.items);
668}
745669
746 try eh_frame.writeRelocs(macho_file, code, &relocs);
747 assert(relocs.items.len == header.nreloc);
670fn writeSectionsToFile(macho_file: *MachO) !void {
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?
752 try macho_file.base.file.?.pwriteAll(code, header.offset);
753 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
680 try macho_file.writeDataInCode();
681 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(macho_file.symtab.items), macho_file.symtab_cmd.symoff);
682 try macho_file.base.file.?.pwriteAll(macho_file.strtab.items, macho_file.symtab_cmd.stroff);
754683}
755684
756685fn writeLoadCommands(macho_file: *MachO) !struct { usize, usize } {