authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-02-08 07:22:34+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-02-08 07:22:34+01:00
log9ca6cc1e2f6af1b5197f92e4b56865ab822f4040
tree579ce8050c21bc9baf5fc5c9268866d20b95b70c
parentba8375328cd6ddf5025a400fc1d1817b88be6d53
parent272fc2df2e0ba79866ff56bacacece2f60dec10b
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #18853 from ziglang/macho-static-lib

macho: implement our own archiver

6 files changed, 761 insertions(+), 189 deletions(-)

src/link/MachO.zig+19-82
......@@ -379,10 +379,6 @@ pub fn deinit(self: *MachO) void {
379379}
380380
381381pub fn flush(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {
382 // TODO: I think this is just a temp and can be removed once we can emit static archives
383 if (self.base.isStaticLib() and build_options.have_llvm) {
384 return self.base.linkAsArchive(arena, prog_node);
385 }
386382 try self.flushModule(arena, prog_node);
387383}
388384
......@@ -395,8 +391,6 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
395391
396392 if (self.llvm_object) |llvm_object| {
397393 try self.base.emitLlvmObject(arena, llvm_object, prog_node);
398 // TODO: I think this is just a temp and can be removed once we can emit static archives
399 if (self.base.isStaticLib() and build_options.have_llvm) return;
400394 }
401395
402396 var sub_prog_node = prog_node.start("MachO Flush", 0);
......@@ -417,8 +411,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
417411 if (comp.verbose_link) try self.dumpArgv(comp);
418412
419413 if (self.getZigObject()) |zo| try zo.flushModule(self);
420 if (self.base.isStaticLib()) return self.flushStaticLib(comp, module_obj_path);
421 if (self.base.isObject()) return relocatable.flush(self, comp, module_obj_path);
414 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);
415 if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);
422416
423417 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
424418 defer positionals.deinit();
......@@ -577,7 +571,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
577571 },
578572 };
579573
580 try self.markImportsAndExports();
574 self.markImportsAndExports();
581575 self.deadStripDylibs();
582576
583577 for (self.dylibs.items, 1..) |index, ord| {
......@@ -606,7 +600,9 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
606600 self.allocateSyntheticSymbols();
607601 try self.allocateLinkeditSegment();
608602
609 state_log.debug("{}", .{self.dumpState()});
603 if (build_options.enable_logging) {
604 state_log.debug("{}", .{self.dumpState()});
605 }
610606
611607 try self.initDyldInfoSections();
612608
......@@ -892,16 +888,6 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
892888 Compilation.dump_argv(argv.items);
893889}
894890
895fn flushStaticLib(self: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
896 _ = comp;
897 _ = module_obj_path;
898
899 var err = try self.addErrorWithNotes(0);
900 try err.addMsg(self, "TODO implement flushStaticLib", .{});
901
902 return error.FlushFailure;
903}
904
905891pub fn resolveLibSystem(
906892 self: *MachO,
907893 arena: Allocator,
......@@ -934,7 +920,7 @@ pub fn resolveLibSystem(
934920 });
935921}
936922
937const ParseError = error{
923pub const ParseError = error{
938924 MalformedObject,
939925 MalformedArchive,
940926 MalformedDylib,
......@@ -1011,7 +997,7 @@ fn parseObject(self: *MachO, path: []const u8) ParseError!void {
1011997 try object.parse(self);
1012998}
1013999
1014fn parseFatLibrary(self: *MachO, path: []const u8) !fat.Arch {
1000pub fn parseFatLibrary(self: *MachO, path: []const u8) !fat.Arch {
10151001 var buffer: [2]fat.Arch = undefined;
10161002 const fat_archs = try fat.parseArchs(path, &buffer);
10171003 const cpu_arch = self.getTarget().cpu.arch;
......@@ -1525,46 +1511,11 @@ fn createObjcSections(self: *MachO) !void {
15251511}
15261512
15271513fn claimUnresolved(self: *MachO) error{OutOfMemory}!void {
1528 const gpa = self.base.comp.gpa;
1529
1530 var objects = try std.ArrayList(File.Index).initCapacity(gpa, self.objects.items.len + 1);
1531 defer objects.deinit();
1532 if (self.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index);
1533 objects.appendSliceAssumeCapacity(self.objects.items);
1534
1535 for (objects.items) |index| {
1536 const file = self.getFile(index).?;
1537
1538 for (file.getSymbols(), 0..) |sym_index, i| {
1539 const nlist_idx = @as(Symbol.Index, @intCast(i));
1540 const nlist = switch (file) {
1541 .object => |x| x.symtab.items(.nlist)[nlist_idx],
1542 .zig_object => |x| x.symtab.items(.nlist)[nlist_idx],
1543 else => unreachable,
1544 };
1545 if (!nlist.ext()) continue;
1546 if (!nlist.undf()) continue;
1547
1548 const sym = self.getSymbol(sym_index);
1549 if (sym.getFile(self) != null) continue;
1550
1551 const is_import = switch (self.undefined_treatment) {
1552 .@"error" => false,
1553 .warn, .suppress => nlist.weakRef(),
1554 .dynamic_lookup => true,
1555 };
1556 if (is_import) {
1557 sym.value = 0;
1558 sym.atom = 0;
1559 sym.nlist_idx = 0;
1560 sym.file = self.internal_object.?;
1561 sym.flags.weak = false;
1562 sym.flags.weak_ref = nlist.weakRef();
1563 sym.flags.import = is_import;
1564 sym.visibility = .global;
1565 try self.getInternalObject().?.symbols.append(self.base.comp.gpa, sym_index);
1566 }
1567 }
1514 if (self.getZigObject()) |zo| {
1515 try zo.asFile().claimUnresolved(self);
1516 }
1517 for (self.objects.items) |index| {
1518 try self.getFile(index).?.claimUnresolved(self);
15681519 }
15691520}
15701521
......@@ -1590,26 +1541,12 @@ fn checkDuplicates(self: *MachO) !void {
15901541 try self.reportDuplicates(dupes);
15911542}
15921543
1593fn markImportsAndExports(self: *MachO) error{OutOfMemory}!void {
1594 const gpa = self.base.comp.gpa;
1595 var objects = try std.ArrayList(File.Index).initCapacity(gpa, self.objects.items.len + 1);
1596 defer objects.deinit();
1597 if (self.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index);
1598 objects.appendSliceAssumeCapacity(self.objects.items);
1599
1600 for (objects.items) |index| {
1601 for (self.getFile(index).?.getSymbols()) |sym_index| {
1602 const sym = self.getSymbol(sym_index);
1603 const file = sym.getFile(self) orelse continue;
1604 if (sym.visibility != .global) continue;
1605 if (file == .dylib and !sym.flags.abs) {
1606 sym.flags.import = true;
1607 continue;
1608 }
1609 if (file.getIndex() == index) {
1610 sym.flags.@"export" = true;
1611 }
1612 }
1544fn markImportsAndExports(self: *MachO) void {
1545 if (self.getZigObject()) |zo| {
1546 zo.asFile().markImportsExports(self);
1547 }
1548 for (self.objects.items) |index| {
1549 self.getFile(index).?.markImportsExports(self);
16131550 }
16141551
16151552 for (self.undefined_symbols.items) |index| {
src/link/MachO/Archive.zig+229-61
......@@ -1,63 +1,5 @@
11objects: std.ArrayListUnmanaged(Object) = .{},
22
3// Archive files start with the ARMAG identifying string. Then follows a
4// `struct ar_hdr', and as many bytes of member file data as its `ar_size'
5// member indicates, for each member file.
6/// String that begins an archive file.
7pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n";
8/// Size of that string.
9pub const SARMAG: u4 = 8;
10
11/// String in ar_fmag at the end of each header.
12const ARFMAG: *const [2:0]u8 = "`\n";
13
14const ar_hdr = extern struct {
15 /// Member file name, sometimes / terminated.
16 ar_name: [16]u8,
17
18 /// File date, decimal seconds since Epoch.
19 ar_date: [12]u8,
20
21 /// User ID, in ASCII format.
22 ar_uid: [6]u8,
23
24 /// Group ID, in ASCII format.
25 ar_gid: [6]u8,
26
27 /// File mode, in ASCII octal.
28 ar_mode: [8]u8,
29
30 /// File size, in ASCII decimal.
31 ar_size: [10]u8,
32
33 /// Always contains ARFMAG.
34 ar_fmag: [2]u8,
35
36 fn date(self: ar_hdr) !u64 {
37 const value = mem.trimRight(u8, &self.ar_date, &[_]u8{@as(u8, 0x20)});
38 return std.fmt.parseInt(u64, value, 10);
39 }
40
41 fn size(self: ar_hdr) !u32 {
42 const value = mem.trimRight(u8, &self.ar_size, &[_]u8{@as(u8, 0x20)});
43 return std.fmt.parseInt(u32, value, 10);
44 }
45
46 fn name(self: *const ar_hdr) ?[]const u8 {
47 const value = &self.ar_name;
48 if (mem.startsWith(u8, value, "#1/")) return null;
49 const sentinel = mem.indexOfScalar(u8, value, '/') orelse value.len;
50 return value[0..sentinel];
51 }
52
53 fn nameLength(self: ar_hdr) !?u32 {
54 const value = &self.ar_name;
55 if (!mem.startsWith(u8, value, "#1/")) return null;
56 const trimmed = mem.trimRight(u8, self.ar_name["#1/".len..], &[_]u8{0x20});
57 return try std.fmt.parseInt(u32, trimmed, 10);
58 }
59};
60
613pub fn isArchive(path: []const u8, fat_arch: ?fat.Arch) !bool {
624 const file = try std.fs.cwd().openFile(path, .{});
635 defer file.close();
......@@ -85,9 +27,9 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:
8527 try handle.seekTo(offset);
8628
8729 const reader = handle.reader();
88 _ = try reader.readBytesNoEof(Archive.SARMAG);
30 _ = try reader.readBytesNoEof(SARMAG);
8931
90 var pos: usize = Archive.SARMAG;
32 var pos: usize = SARMAG;
9133 while (true) {
9234 if (pos >= size) break;
9335 if (!mem.isAligned(pos, 2)) {
......@@ -123,7 +65,10 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:
12365 pos += hdr_size;
12466 }
12567
126 if (mem.eql(u8, name, "__.SYMDEF") or mem.eql(u8, name, "__.SYMDEF SORTED")) continue;
68 if (mem.eql(u8, name, SYMDEF) or
69 mem.eql(u8, name, SYMDEF64) or
70 mem.eql(u8, name, SYMDEF_SORTED) or
71 mem.eql(u8, name, SYMDEF64_SORTED)) continue;
12772
12873 const object = Object{
12974 .archive = .{
......@@ -143,7 +88,229 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:
14388 }
14489}
14590
91pub fn writeHeader(
92 object_name: []const u8,
93 object_size: usize,
94 format: Format,
95 writer: anytype,
96) !void {
97 var hdr: ar_hdr = .{
98 .ar_name = undefined,
99 .ar_date = undefined,
100 .ar_uid = undefined,
101 .ar_gid = undefined,
102 .ar_mode = undefined,
103 .ar_size = undefined,
104 .ar_fmag = undefined,
105 };
106 @memset(mem.asBytes(&hdr), 0x20);
107 inline for (@typeInfo(ar_hdr).Struct.fields) |field| {
108 var stream = std.io.fixedBufferStream(&@field(hdr, field.name));
109 stream.writer().print("0", .{}) catch unreachable;
110 }
111 @memcpy(&hdr.ar_fmag, ARFMAG);
112
113 const object_name_len = mem.alignForward(usize, object_name.len + 1, ptrWidth(format));
114 const total_object_size = object_size + object_name_len;
115
116 {
117 var stream = std.io.fixedBufferStream(&hdr.ar_name);
118 stream.writer().print("#1/{d}", .{object_name_len}) catch unreachable;
119 }
120 {
121 var stream = std.io.fixedBufferStream(&hdr.ar_size);
122 stream.writer().print("{d}", .{total_object_size}) catch unreachable;
123 }
124
125 try writer.writeAll(mem.asBytes(&hdr));
126 try writer.print("{s}\x00", .{object_name});
127
128 const padding = object_name_len - object_name.len - 1;
129 if (padding > 0) {
130 try writer.writeByteNTimes(0, padding);
131 }
132}
133
134// Archive files start with the ARMAG identifying string. Then follows a
135// `struct ar_hdr', and as many bytes of member file data as its `ar_size'
136// member indicates, for each member file.
137/// String that begins an archive file.
138pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n";
139/// Size of that string.
140pub const SARMAG: u4 = 8;
141
142/// String in ar_fmag at the end of each header.
143const ARFMAG: *const [2:0]u8 = "`\n";
144
145pub const SYMDEF = "__.SYMDEF";
146pub const SYMDEF64 = "__.SYMDEF_64";
147pub const SYMDEF_SORTED = "__.SYMDEF SORTED";
148pub const SYMDEF64_SORTED = "__.SYMDEF_64 SORTED";
149
150pub const ar_hdr = extern struct {
151 /// Member file name, sometimes / terminated.
152 ar_name: [16]u8,
153
154 /// File date, decimal seconds since Epoch.
155 ar_date: [12]u8,
156
157 /// User ID, in ASCII format.
158 ar_uid: [6]u8,
159
160 /// Group ID, in ASCII format.
161 ar_gid: [6]u8,
162
163 /// File mode, in ASCII octal.
164 ar_mode: [8]u8,
165
166 /// File size, in ASCII decimal.
167 ar_size: [10]u8,
168
169 /// Always contains ARFMAG.
170 ar_fmag: [2]u8,
171
172 fn date(self: ar_hdr) !u64 {
173 const value = mem.trimRight(u8, &self.ar_date, &[_]u8{@as(u8, 0x20)});
174 return std.fmt.parseInt(u64, value, 10);
175 }
176
177 fn size(self: ar_hdr) !u32 {
178 const value = mem.trimRight(u8, &self.ar_size, &[_]u8{@as(u8, 0x20)});
179 return std.fmt.parseInt(u32, value, 10);
180 }
181
182 fn name(self: *const ar_hdr) ?[]const u8 {
183 const value = &self.ar_name;
184 if (mem.startsWith(u8, value, "#1/")) return null;
185 const sentinel = mem.indexOfScalar(u8, value, '/') orelse value.len;
186 return value[0..sentinel];
187 }
188
189 fn nameLength(self: ar_hdr) !?u32 {
190 const value = &self.ar_name;
191 if (!mem.startsWith(u8, value, "#1/")) return null;
192 const trimmed = mem.trimRight(u8, self.ar_name["#1/".len..], &[_]u8{0x20});
193 return try std.fmt.parseInt(u32, trimmed, 10);
194 }
195};
196
197pub const ArSymtab = struct {
198 entries: std.ArrayListUnmanaged(Entry) = .{},
199 strtab: StringTable = .{},
200
201 pub fn deinit(ar: *ArSymtab, allocator: Allocator) void {
202 ar.entries.deinit(allocator);
203 ar.strtab.deinit(allocator);
204 }
205
206 pub fn sort(ar: *ArSymtab) void {
207 mem.sort(Entry, ar.entries.items, {}, Entry.lessThan);
208 }
209
210 pub fn size(ar: ArSymtab, format: Format) usize {
211 const ptr_width = ptrWidth(format);
212 return ptr_width + ar.entries.items.len * 2 * ptr_width + ptr_width + mem.alignForward(usize, ar.strtab.buffer.items.len, ptr_width);
213 }
214
215 pub fn write(ar: ArSymtab, format: Format, macho_file: *MachO, writer: anytype) !void {
216 const ptr_width = ptrWidth(format);
217 // Header
218 try writeHeader(SYMDEF, ar.size(format), format, writer);
219 // Symtab size
220 try writeInt(format, ar.entries.items.len * 2 * ptr_width, writer);
221 // Symtab entries
222 for (ar.entries.items) |entry| {
223 const file_off = switch (macho_file.getFile(entry.file).?) {
224 .zig_object => |x| x.output_ar_state.file_off,
225 .object => |x| x.output_ar_state.file_off,
226 else => unreachable,
227 };
228 // Name offset
229 try writeInt(format, entry.off, writer);
230 // File offset
231 try writeInt(format, file_off, writer);
232 }
233 // Strtab size
234 const strtab_size = mem.alignForward(usize, ar.strtab.buffer.items.len, ptr_width);
235 const padding = strtab_size - ar.strtab.buffer.items.len;
236 try writeInt(format, strtab_size, writer);
237 // Strtab
238 try writer.writeAll(ar.strtab.buffer.items);
239 if (padding > 0) {
240 try writer.writeByteNTimes(0, padding);
241 }
242 }
243
244 const FormatContext = struct {
245 ar: ArSymtab,
246 macho_file: *MachO,
247 };
248
249 pub fn fmt(ar: ArSymtab, macho_file: *MachO) std.fmt.Formatter(format2) {
250 return .{ .data = .{ .ar = ar, .macho_file = macho_file } };
251 }
252
253 fn format2(
254 ctx: FormatContext,
255 comptime unused_fmt_string: []const u8,
256 options: std.fmt.FormatOptions,
257 writer: anytype,
258 ) !void {
259 _ = unused_fmt_string;
260 _ = options;
261 const ar = ctx.ar;
262 const macho_file = ctx.macho_file;
263 for (ar.entries.items, 0..) |entry, i| {
264 const name = ar.strtab.getAssumeExists(entry.off);
265 const file = macho_file.getFile(entry.file).?;
266 try writer.print(" {d}: {s} in file({d})({})\n", .{ i, name, entry.file, file.fmtPath() });
267 }
268 }
269
270 const Entry = struct {
271 /// Symbol name offset
272 off: u32,
273 /// Exporting file
274 file: File.Index,
275
276 pub fn lessThan(ctx: void, lhs: Entry, rhs: Entry) bool {
277 _ = ctx;
278 if (lhs.off == rhs.off) return lhs.file < rhs.file;
279 return lhs.off < rhs.off;
280 }
281 };
282};
283
284pub const Format = enum {
285 p32,
286 p64,
287};
288
289pub fn ptrWidth(format: Format) usize {
290 return switch (format) {
291 .p32 => @as(usize, 4),
292 .p64 => 8,
293 };
294}
295
296pub fn writeInt(format: Format, value: u64, writer: anytype) !void {
297 switch (format) {
298 .p32 => try writer.writeInt(u32, std.math.cast(u32, value) orelse return error.Overflow, .little),
299 .p64 => try writer.writeInt(u64, value, .little),
300 }
301}
302
303pub const ArState = struct {
304 /// File offset of the ar_hdr describing the contributing
305 /// object in the archive.
306 file_off: u64 = 0,
307
308 /// Total size of the contributing object (excludes ar_hdr and long name with padding).
309 size: u64 = 0,
310};
311
146312const fat = @import("fat.zig");
313const link = @import("../../link.zig");
147314const log = std.log.scoped(.link);
148315const macho = std.macho;
149316const mem = std.mem;
......@@ -154,3 +321,4 @@ const Archive = @This();
154321const File = @import("file.zig").File;
155322const MachO = @import("../MachO.zig");
156323const Object = @import("Object.zig");
324const StringTable = @import("../StringTable.zig");
src/link/MachO/Object.zig+110-2
......@@ -1,4 +1,4 @@
1archive: ?Archive = null,
1archive: ?InArchive = null,
22path: []const u8,
33file_handle: File.HandleIndex,
44mtime: u64,
......@@ -29,8 +29,9 @@ hidden: bool = false,
2929
3030dynamic_relocs: MachO.DynamicRelocs = .{},
3131output_symtab_ctx: MachO.SymtabCtx = .{},
32output_ar_state: Archive.ArState = .{},
3233
33const Archive = struct {
34const InArchive = struct {
3435 path: []const u8,
3536 offset: u64,
3637};
......@@ -1232,6 +1233,112 @@ fn addSection(self: *Object, allocator: Allocator, segname: []const u8, sectname
12321233 return n_sect;
12331234}
12341235
1236pub fn parseAr(self: *Object, macho_file: *MachO) !void {
1237 const tracy = trace(@src());
1238 defer tracy.end();
1239
1240 const gpa = macho_file.base.comp.gpa;
1241 const offset = if (self.archive) |ar| ar.offset else 0;
1242 const handle = macho_file.getFileHandle(self.file_handle);
1243
1244 var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined;
1245 {
1246 const amt = try handle.preadAll(&header_buffer, offset);
1247 if (amt != @sizeOf(macho.mach_header_64)) return error.InputOutput;
1248 }
1249 self.header = @as(*align(1) const macho.mach_header_64, @ptrCast(&header_buffer)).*;
1250
1251 const this_cpu_arch: std.Target.Cpu.Arch = switch (self.header.?.cputype) {
1252 macho.CPU_TYPE_ARM64 => .aarch64,
1253 macho.CPU_TYPE_X86_64 => .x86_64,
1254 else => |x| {
1255 try macho_file.reportParseError2(self.index, "unknown cpu architecture: {d}", .{x});
1256 return error.InvalidCpuArch;
1257 },
1258 };
1259 if (macho_file.getTarget().cpu.arch != this_cpu_arch) {
1260 try macho_file.reportParseError2(self.index, "invalid cpu architecture: {s}", .{@tagName(this_cpu_arch)});
1261 return error.InvalidCpuArch;
1262 }
1263
1264 const lc_buffer = try gpa.alloc(u8, self.header.?.sizeofcmds);
1265 defer gpa.free(lc_buffer);
1266 {
1267 const amt = try handle.preadAll(lc_buffer, offset + @sizeOf(macho.mach_header_64));
1268 if (amt != self.header.?.sizeofcmds) return error.InputOutput;
1269 }
1270
1271 var it = LoadCommandIterator{
1272 .ncmds = self.header.?.ncmds,
1273 .buffer = lc_buffer,
1274 };
1275 while (it.next()) |lc| switch (lc.cmd()) {
1276 .SYMTAB => {
1277 const cmd = lc.cast(macho.symtab_command).?;
1278 try self.strtab.resize(gpa, cmd.strsize);
1279 {
1280 const amt = try handle.preadAll(self.strtab.items, cmd.stroff + offset);
1281 if (amt != self.strtab.items.len) return error.InputOutput;
1282 }
1283
1284 const symtab_buffer = try gpa.alloc(u8, cmd.nsyms * @sizeOf(macho.nlist_64));
1285 defer gpa.free(symtab_buffer);
1286 {
1287 const amt = try handle.preadAll(symtab_buffer, cmd.symoff + offset);
1288 if (amt != symtab_buffer.len) return error.InputOutput;
1289 }
1290 const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(symtab_buffer.ptr))[0..cmd.nsyms];
1291 try self.symtab.ensureUnusedCapacity(gpa, symtab.len);
1292 for (symtab) |nlist| {
1293 self.symtab.appendAssumeCapacity(.{
1294 .nlist = nlist,
1295 .atom = 0,
1296 .size = 0,
1297 });
1298 }
1299 },
1300 .BUILD_VERSION,
1301 .VERSION_MIN_MACOSX,
1302 .VERSION_MIN_IPHONEOS,
1303 .VERSION_MIN_TVOS,
1304 .VERSION_MIN_WATCHOS,
1305 => if (self.platform == null) {
1306 self.platform = MachO.Platform.fromLoadCommand(lc);
1307 },
1308 else => {},
1309 };
1310}
1311
1312pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, macho_file: *MachO) error{OutOfMemory}!void {
1313 const gpa = macho_file.base.comp.gpa;
1314 for (self.symtab.items(.nlist)) |nlist| {
1315 if (!nlist.ext() or (nlist.undf() and !nlist.tentative())) continue;
1316 const off = try ar_symtab.strtab.insert(gpa, self.getString(nlist.n_strx));
1317 try ar_symtab.entries.append(gpa, .{ .off = off, .file = self.index });
1318 }
1319}
1320
1321pub fn updateArSize(self: *Object, macho_file: *MachO) !void {
1322 const file = macho_file.getFileHandle(self.file_handle);
1323 const size = (try file.stat()).size;
1324 self.output_ar_state.size = size;
1325}
1326
1327pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void {
1328 // Header
1329 const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;
1330 try Archive.writeHeader(self.path, size, ar_format, writer);
1331 // Data
1332 const file = macho_file.getFileHandle(self.file_handle);
1333 // TODO try using copyRangeAll
1334 const gpa = macho_file.base.comp.gpa;
1335 const data = try gpa.alloc(u8, size);
1336 defer gpa.free(data);
1337 const amt = try file.preadAll(data, 0);
1338 if (amt != size) return error.InputOutput;
1339 try writer.writeAll(data);
1340}
1341
12351342pub fn calcSymtabSize(self: *Object, macho_file: *MachO) !void {
12361343 const tracy = trace(@src());
12371344 defer tracy.end();
......@@ -2241,6 +2348,7 @@ const trace = @import("../../tracy.zig").trace;
22412348const std = @import("std");
22422349
22432350const Allocator = mem.Allocator;
2351const Archive = @import("Archive.zig");
22442352const Atom = @import("Atom.zig");
22452353const Cie = eh_frame.Cie;
22462354const DwarfInfo = @import("DwarfInfo.zig");
src/link/MachO/ZigObject.zig+37
......@@ -1,3 +1,4 @@
1data: std.ArrayListUnmanaged(u8) = .{},
12/// Externally owned memory.
23path: []const u8,
34index: File.Index,
......@@ -47,6 +48,7 @@ relocs: RelocationTable = .{},
4748
4849dynamic_relocs: MachO.DynamicRelocs = .{},
4950output_symtab_ctx: MachO.SymtabCtx = .{},
51output_ar_state: Archive.ArState = .{},
5052
5153pub fn init(self: *ZigObject, macho_file: *MachO) !void {
5254 const comp = macho_file.base.comp;
......@@ -57,6 +59,7 @@ pub fn init(self: *ZigObject, macho_file: *MachO) !void {
5759}
5860
5961pub fn deinit(self: *ZigObject, allocator: Allocator) void {
62 self.data.deinit(allocator);
6063 self.symtab.deinit(allocator);
6164 self.strtab.deinit(allocator);
6265 self.symbols.deinit(allocator);
......@@ -279,6 +282,40 @@ pub fn checkDuplicates(self: *ZigObject, dupes: anytype, macho_file: *MachO) !vo
279282 }
280283}
281284
285/// This is just a temporary helper function that allows us to re-read what we wrote to file into a buffer.
286/// We need this so that we can write to an archive.
287/// TODO implement writing ZigObject data directly to a buffer instead.
288pub fn readFileContents(self: *ZigObject, size: usize, macho_file: *MachO) !void {
289 const gpa = macho_file.base.comp.gpa;
290 try self.data.resize(gpa, size);
291 const amt = try macho_file.base.file.?.preadAll(self.data.items, 0);
292 if (amt != size) return error.InputOutput;
293}
294
295pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, macho_file: *MachO) error{OutOfMemory}!void {
296 const gpa = macho_file.base.comp.gpa;
297 for (self.symbols.items) |sym_index| {
298 const sym = macho_file.getSymbol(sym_index);
299 const file = sym.getFile(macho_file).?;
300 assert(file.getIndex() == self.index);
301 if (!sym.flags.@"export") continue;
302 const off = try ar_symtab.strtab.insert(gpa, sym.getName(macho_file));
303 try ar_symtab.entries.append(gpa, .{ .off = off, .file = self.index });
304 }
305}
306
307pub fn updateArSize(self: *ZigObject) void {
308 self.output_ar_state.size = self.data.items.len;
309}
310
311pub fn writeAr(self: ZigObject, ar_format: Archive.Format, writer: anytype) !void {
312 // Header
313 const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;
314 try Archive.writeHeader(self.path, size, ar_format, writer);
315 // Data
316 try writer.writeAll(self.data.items);
317}
318
282319pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void {
283320 for (self.atoms.items) |atom_index| {
284321 const atom = macho_file.getAtom(atom_index) orelse continue;
src/link/MachO/file.zig+116
......@@ -44,6 +44,97 @@ pub const File = union(enum) {
4444 }
4545 }
4646
47 pub fn claimUnresolved(file: File, macho_file: *MachO) error{OutOfMemory}!void {
48 assert(file == .object or file == .zig_object);
49
50 for (file.getSymbols(), 0..) |sym_index, i| {
51 const nlist_idx = @as(Symbol.Index, @intCast(i));
52 const nlist = switch (file) {
53 .object => |x| x.symtab.items(.nlist)[nlist_idx],
54 .zig_object => |x| x.symtab.items(.nlist)[nlist_idx],
55 else => unreachable,
56 };
57 if (!nlist.ext()) continue;
58 if (!nlist.undf()) continue;
59
60 const sym = macho_file.getSymbol(sym_index);
61 if (sym.getFile(macho_file) != null) continue;
62
63 const is_import = switch (macho_file.undefined_treatment) {
64 .@"error" => false,
65 .warn, .suppress => nlist.weakRef(),
66 .dynamic_lookup => true,
67 };
68 if (is_import) {
69 sym.value = 0;
70 sym.atom = 0;
71 sym.nlist_idx = 0;
72 sym.file = macho_file.internal_object.?;
73 sym.flags.weak = false;
74 sym.flags.weak_ref = nlist.weakRef();
75 sym.flags.import = is_import;
76 sym.visibility = .global;
77 try macho_file.getInternalObject().?.symbols.append(macho_file.base.comp.gpa, sym_index);
78 }
79 }
80 }
81
82 pub fn claimUnresolvedRelocatable(file: File, macho_file: *MachO) void {
83 assert(file == .object or file == .zig_object);
84
85 for (file.getSymbols(), 0..) |sym_index, i| {
86 const nlist_idx = @as(Symbol.Index, @intCast(i));
87 const nlist = switch (file) {
88 .object => |x| x.symtab.items(.nlist)[nlist_idx],
89 .zig_object => |x| x.symtab.items(.nlist)[nlist_idx],
90 else => unreachable,
91 };
92 if (!nlist.ext()) continue;
93 if (!nlist.undf()) continue;
94
95 const sym = macho_file.getSymbol(sym_index);
96 if (sym.getFile(macho_file) != null) continue;
97
98 sym.value = 0;
99 sym.atom = 0;
100 sym.nlist_idx = nlist_idx;
101 sym.file = file.getIndex();
102 sym.flags.weak_ref = nlist.weakRef();
103 sym.flags.import = true;
104 sym.visibility = .global;
105 }
106 }
107
108 pub fn markImportsExports(file: File, macho_file: *MachO) void {
109 assert(file == .object or file == .zig_object);
110
111 for (file.getSymbols()) |sym_index| {
112 const sym = macho_file.getSymbol(sym_index);
113 const other_file = sym.getFile(macho_file) orelse continue;
114 if (sym.visibility != .global) continue;
115 if (other_file == .dylib and !sym.flags.abs) {
116 sym.flags.import = true;
117 continue;
118 }
119 if (other_file.getIndex() == file.getIndex()) {
120 sym.flags.@"export" = true;
121 }
122 }
123 }
124
125 pub fn markExportsRelocatable(file: File, macho_file: *MachO) void {
126 assert(file == .object or file == .zig_object);
127
128 for (file.getSymbols()) |sym_index| {
129 const sym = macho_file.getSymbol(sym_index);
130 const other_file = sym.getFile(macho_file) orelse continue;
131 if (sym.visibility != .global) continue;
132 if (other_file.getIndex() == file.getIndex()) {
133 sym.flags.@"export" = true;
134 }
135 }
136 }
137
47138 /// Encodes symbol rank so that the following ordering applies:
48139 /// * strong in object
49140 /// * weak in object
......@@ -84,6 +175,29 @@ pub const File = union(enum) {
84175 };
85176 }
86177
178 pub fn updateArSymtab(file: File, ar_symtab: *Archive.ArSymtab, macho_file: *MachO) error{OutOfMemory}!void {
179 return switch (file) {
180 .dylib, .internal => unreachable,
181 inline else => |x| x.updateArSymtab(ar_symtab, macho_file),
182 };
183 }
184
185 pub fn updateArSize(file: File, macho_file: *MachO) !void {
186 return switch (file) {
187 .dylib, .internal => unreachable,
188 .zig_object => |x| x.updateArSize(),
189 .object => |x| x.updateArSize(macho_file),
190 };
191 }
192
193 pub fn writeAr(file: File, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void {
194 return switch (file) {
195 .dylib, .internal => unreachable,
196 .zig_object => |x| x.writeAr(ar_format, writer),
197 .object => |x| x.writeAr(ar_format, macho_file, writer),
198 };
199 }
200
87201 pub fn calcSymtabSize(file: File, macho_file: *MachO) !void {
88202 return switch (file) {
89203 inline else => |x| x.calcSymtabSize(macho_file),
......@@ -110,10 +224,12 @@ pub const File = union(enum) {
110224 pub const HandleIndex = Index;
111225};
112226
227const assert = std.debug.assert;
113228const macho = std.macho;
114229const std = @import("std");
115230
116231const Allocator = std.mem.Allocator;
232const Archive = @import("Archive.zig");
117233const Atom = @import("Atom.zig");
118234const InternalObject = @import("InternalObject.zig");
119235const MachO = @import("../MachO.zig");
src/link/MachO/relocatable.zig+250-44
......@@ -1,4 +1,4 @@
1pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
1pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
22 const gpa = macho_file.base.comp.gpa;
33
44 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
......@@ -46,8 +46,8 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u
4646
4747 try macho_file.addUndefinedGlobals();
4848 try macho_file.resolveSymbols();
49 try markExports(macho_file);
50 try claimUnresolved(macho_file);
49 markExports(macho_file);
50 claimUnresolved(macho_file);
5151 try initOutputSections(macho_file);
5252 try macho_file.sortSections();
5353 try macho_file.addAtomsToSections();
......@@ -64,7 +64,9 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u
6464 };
6565 off = allocateSectionsRelocs(macho_file, off);
6666
67 state_log.debug("{}", .{macho_file.dumpState()});
67 if (build_options.enable_logging) {
68 state_log.debug("{}", .{macho_file.dumpState()});
69 }
6870
6971 try macho_file.calcSymtabSize();
7072 try writeAtoms(macho_file);
......@@ -86,54 +88,254 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u
8688 try writeHeader(macho_file, ncmds, sizeofcmds);
8789}
8890
89fn markExports(macho_file: *MachO) error{OutOfMemory}!void {
90 var objects = try std.ArrayList(File.Index).initCapacity(macho_file.base.comp.gpa, macho_file.objects.items.len + 1);
91 defer objects.deinit();
92 if (macho_file.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index);
93 objects.appendSliceAssumeCapacity(macho_file.objects.items);
94
95 for (objects.items) |index| {
96 for (macho_file.getFile(index).?.getSymbols()) |sym_index| {
97 const sym = macho_file.getSymbol(sym_index);
98 const file = sym.getFile(macho_file) orelse continue;
99 if (sym.visibility != .global) continue;
100 if (file.getIndex() == index) {
101 sym.flags.@"export" = true;
102 }
91pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
92 const gpa = comp.gpa;
93
94 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
95 defer positionals.deinit();
96
97 try positionals.ensureUnusedCapacity(comp.objects.len);
98 positionals.appendSliceAssumeCapacity(comp.objects);
99
100 for (comp.c_object_table.keys()) |key| {
101 try positionals.append(.{ .path = key.status.success.object_path });
102 }
103
104 if (module_obj_path) |path| try positionals.append(.{ .path = path });
105
106 for (positionals.items) |obj| {
107 parsePositional(macho_file, obj.path) catch |err| switch (err) {
108 error.MalformedObject,
109 error.MalformedArchive,
110 error.InvalidCpuArch,
111 error.InvalidTarget,
112 => continue, // already reported
113 error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an object file", .{}),
114 else => |e| try macho_file.reportParseError(
115 obj.path,
116 "unexpected error: parsing input file failed with error {s}",
117 .{@errorName(e)},
118 ),
119 };
120 }
121
122 if (comp.link_errors.items.len > 0) return error.FlushFailure;
123
124 // First, we flush relocatable object file generated with our backends.
125 if (macho_file.getZigObject()) |zo| {
126 zo.resolveSymbols(macho_file);
127 zo.asFile().markExportsRelocatable(macho_file);
128 zo.asFile().claimUnresolvedRelocatable(macho_file);
129 try macho_file.sortSections();
130 try macho_file.addAtomsToSections();
131 try calcSectionSizes(macho_file);
132 try createSegment(macho_file);
133 try allocateSections(macho_file);
134 allocateSegment(macho_file);
135
136 var off = off: {
137 const seg = macho_file.segments.items[0];
138 const off = math.cast(u32, seg.fileoff + seg.filesize) orelse return error.Overflow;
139 break :off mem.alignForward(u32, off, @alignOf(macho.relocation_info));
140 };
141 off = allocateSectionsRelocs(macho_file, off);
142
143 if (build_options.enable_logging) {
144 state_log.debug("{}", .{macho_file.dumpState()});
103145 }
146
147 try macho_file.calcSymtabSize();
148 try writeAtoms(macho_file);
149
150 off = mem.alignForward(u32, off, @alignOf(u64));
151 off = try macho_file.writeDataInCode(0, off);
152 off = mem.alignForward(u32, off, @alignOf(u64));
153 off = try macho_file.writeSymtab(off);
154 off = mem.alignForward(u32, off, @alignOf(u64));
155 off = try macho_file.writeStrtab(off);
156
157 // In order to please Apple ld (and possibly other MachO linkers in the wild),
158 // we will now sanitize segment names of Zig-specific segments.
159 sanitizeZigSections(macho_file);
160
161 const ncmds, const sizeofcmds = try writeLoadCommands(macho_file);
162 try writeHeader(macho_file, ncmds, sizeofcmds);
163
164 // TODO we can avoid reading in the file contents we just wrote if we give the linker
165 // ability to write directly to a buffer.
166 try zo.readFileContents(off, macho_file);
104167 }
105}
106168
107fn claimUnresolved(macho_file: *MachO) error{OutOfMemory}!void {
108 var objects = try std.ArrayList(File.Index).initCapacity(macho_file.base.comp.gpa, macho_file.objects.items.len + 1);
109 defer objects.deinit();
110 if (macho_file.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index);
111 objects.appendSliceAssumeCapacity(macho_file.objects.items);
169 var files = std.ArrayList(File.Index).init(gpa);
170 defer files.deinit();
171 try files.ensureTotalCapacityPrecise(macho_file.objects.items.len + 1);
172 if (macho_file.getZigObject()) |zo| files.appendAssumeCapacity(zo.index);
173 for (macho_file.objects.items) |index| files.appendAssumeCapacity(index);
174
175 const format: Archive.Format = .p32;
176 const ptr_width = Archive.ptrWidth(format);
112177
113 for (objects.items) |index| {
114 const file = macho_file.getFile(index).?;
178 // Update ar symtab from parsed objects
179 var ar_symtab: Archive.ArSymtab = .{};
180 defer ar_symtab.deinit(gpa);
181
182 for (files.items) |index| {
183 try macho_file.getFile(index).?.updateArSymtab(&ar_symtab, macho_file);
184 }
185
186 ar_symtab.sort();
187
188 // Update sizes of contributing objects
189 for (files.items) |index| {
190 try macho_file.getFile(index).?.updateArSize(macho_file);
191 }
115192
116 for (file.getSymbols(), 0..) |sym_index, i| {
117 const nlist_idx = @as(Symbol.Index, @intCast(i));
118 const nlist = switch (file) {
119 .object => |x| x.symtab.items(.nlist)[nlist_idx],
120 .zig_object => |x| x.symtab.items(.nlist)[nlist_idx],
193 // Update file offsets of contributing objects
194 const total_size: usize = blk: {
195 var pos: usize = Archive.SARMAG;
196 pos += @sizeOf(Archive.ar_hdr) + Archive.SYMDEF.len + 1;
197 pos = mem.alignForward(usize, pos, ptr_width);
198 pos += ar_symtab.size(format);
199
200 for (files.items) |index| {
201 const file = macho_file.getFile(index).?;
202 const state = switch (file) {
203 .zig_object => |x| &x.output_ar_state,
204 .object => |x| &x.output_ar_state,
121205 else => unreachable,
122206 };
123 if (!nlist.ext()) continue;
124 if (!nlist.undf()) continue;
125
126 const sym = macho_file.getSymbol(sym_index);
127 if (sym.getFile(macho_file) != null) continue;
128
129 sym.value = 0;
130 sym.atom = 0;
131 sym.nlist_idx = nlist_idx;
132 sym.file = index;
133 sym.flags.weak_ref = nlist.weakRef();
134 sym.flags.import = true;
135 sym.visibility = .global;
207 const path = switch (file) {
208 .zig_object => |x| x.path,
209 .object => |x| x.path,
210 else => unreachable,
211 };
212 pos = mem.alignForward(usize, pos, ptr_width);
213 state.file_off = pos;
214 pos += @sizeOf(Archive.ar_hdr) + path.len + 1;
215 pos = mem.alignForward(usize, pos, ptr_width);
216 pos += math.cast(usize, state.size) orelse return error.Overflow;
217 }
218
219 break :blk pos;
220 };
221
222 if (build_options.enable_logging) {
223 state_log.debug("ar_symtab\n{}\n", .{ar_symtab.fmt(macho_file)});
224 }
225
226 var buffer = std.ArrayList(u8).init(gpa);
227 defer buffer.deinit();
228 try buffer.ensureTotalCapacityPrecise(total_size);
229 const writer = buffer.writer();
230
231 // Write magic
232 try writer.writeAll(Archive.ARMAG);
233
234 // Write symtab
235 try ar_symtab.write(format, macho_file, writer);
236
237 // Write object files
238 for (files.items) |index| {
239 const aligned = mem.alignForward(usize, buffer.items.len, ptr_width);
240 const padding = aligned - buffer.items.len;
241 if (padding > 0) {
242 try writer.writeByteNTimes(0, padding);
136243 }
244 try macho_file.getFile(index).?.writeAr(format, macho_file, writer);
245 }
246
247 assert(buffer.items.len == total_size);
248
249 try macho_file.base.file.?.setEndPos(total_size);
250 try macho_file.base.file.?.pwriteAll(buffer.items, 0);
251
252 if (comp.link_errors.items.len > 0) return error.FlushFailure;
253}
254
255fn parsePositional(macho_file: *MachO, path: []const u8) MachO.ParseError!void {
256 const tracy = trace(@src());
257 defer tracy.end();
258 if (try Object.isObject(path)) {
259 try parseObject(macho_file, path);
260 } else if (try fat.isFatLibrary(path)) {
261 const fat_arch = try macho_file.parseFatLibrary(path);
262 if (try Archive.isArchive(path, fat_arch)) {
263 try parseArchive(macho_file, path, fat_arch);
264 } else return error.UnknownFileType;
265 } else if (try Archive.isArchive(path, null)) {
266 try parseArchive(macho_file, path, null);
267 } else return error.UnknownFileType;
268}
269
270fn parseObject(macho_file: *MachO, path: []const u8) MachO.ParseError!void {
271 const tracy = trace(@src());
272 defer tracy.end();
273
274 const gpa = macho_file.base.comp.gpa;
275 const file = try std.fs.cwd().openFile(path, .{});
276 errdefer file.close();
277 const handle = try macho_file.addFileHandle(file);
278 const mtime: u64 = mtime: {
279 const stat = file.stat() catch break :mtime 0;
280 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
281 };
282 const index = @as(File.Index, @intCast(try macho_file.files.addOne(gpa)));
283 macho_file.files.set(index, .{ .object = .{
284 .path = try gpa.dupe(u8, path),
285 .file_handle = handle,
286 .mtime = mtime,
287 .index = index,
288 } });
289 try macho_file.objects.append(gpa, index);
290
291 const object = macho_file.getFile(index).?.object;
292 try object.parseAr(macho_file);
293}
294
295fn parseArchive(macho_file: *MachO, path: []const u8, fat_arch: ?fat.Arch) MachO.ParseError!void {
296 const tracy = trace(@src());
297 defer tracy.end();
298
299 const gpa = macho_file.base.comp.gpa;
300
301 const file = try std.fs.cwd().openFile(path, .{});
302 errdefer file.close();
303 const handle = try macho_file.addFileHandle(file);
304
305 var archive = Archive{};
306 defer archive.deinit(gpa);
307 try archive.parse(macho_file, path, handle, fat_arch);
308
309 var has_parse_error = false;
310 for (archive.objects.items) |extracted| {
311 const index = @as(File.Index, @intCast(try macho_file.files.addOne(gpa)));
312 macho_file.files.set(index, .{ .object = extracted });
313 const object = &macho_file.files.items(.data)[index].object;
314 object.index = index;
315 object.parseAr(macho_file) catch |err| switch (err) {
316 error.InvalidCpuArch => has_parse_error = true,
317 else => |e| return e,
318 };
319 try macho_file.objects.append(gpa, index);
320 }
321 if (has_parse_error) return error.MalformedArchive;
322}
323
324fn markExports(macho_file: *MachO) void {
325 if (macho_file.getZigObject()) |zo| {
326 zo.asFile().markExportsRelocatable(macho_file);
327 }
328 for (macho_file.objects.items) |index| {
329 macho_file.getFile(index).?.markExportsRelocatable(macho_file);
330 }
331}
332
333pub fn claimUnresolved(macho_file: *MachO) void {
334 if (macho_file.getZigObject()) |zo| {
335 zo.asFile().claimUnresolvedRelocatable(macho_file);
336 }
337 for (macho_file.objects.items) |index| {
338 macho_file.getFile(index).?.claimUnresolvedRelocatable(macho_file);
137339 }
138340}
139341
......@@ -609,7 +811,9 @@ fn writeHeader(macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void {
609811}
610812
611813const assert = std.debug.assert;
814const build_options = @import("build_options");
612815const eh_frame = @import("eh_frame.zig");
816const fat = @import("fat.zig");
613817const link = @import("../../link.zig");
614818const load_commands = @import("load_commands.zig");
615819const log = std.log.scoped(.link);
......@@ -620,8 +824,10 @@ const state_log = std.log.scoped(.link_state);
620824const std = @import("std");
621825const trace = @import("../../tracy.zig").trace;
622826
827const Archive = @import("Archive.zig");
623828const Atom = @import("Atom.zig");
624829const Compilation = @import("../../Compilation.zig");
625830const File = @import("file.zig").File;
626831const MachO = @import("../MachO.zig");
832const Object = @import("Object.zig");
627833const Symbol = @import("Symbol.zig");