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 {...@@ -379,10 +379,6 @@ pub fn deinit(self: *MachO) void {
379}379}
380380
381pub fn flush(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void {381pub 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 }
386 try self.flushModule(arena, prog_node);382 try self.flushModule(arena, prog_node);
387}383}
388384
...@@ -395,8 +391,6 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node...@@ -395,8 +391,6 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
395391
396 if (self.llvm_object) |llvm_object| {392 if (self.llvm_object) |llvm_object| {
397 try self.base.emitLlvmObject(arena, llvm_object, prog_node);393 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;
400 }394 }
401395
402 var sub_prog_node = prog_node.start("MachO Flush", 0);396 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...@@ -417,8 +411,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
417 if (comp.verbose_link) try self.dumpArgv(comp);411 if (comp.verbose_link) try self.dumpArgv(comp);
418412
419 if (self.getZigObject()) |zo| try zo.flushModule(self);413 if (self.getZigObject()) |zo| try zo.flushModule(self);
420 if (self.base.isStaticLib()) return self.flushStaticLib(comp, module_obj_path);414 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);
421 if (self.base.isObject()) return relocatable.flush(self, comp, module_obj_path);415 if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);
422416
423 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);417 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
424 defer positionals.deinit();418 defer positionals.deinit();
...@@ -577,7 +571,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node...@@ -577,7 +571,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
577 },571 },
578 };572 };
579573
580 try self.markImportsAndExports();574 self.markImportsAndExports();
581 self.deadStripDylibs();575 self.deadStripDylibs();
582576
583 for (self.dylibs.items, 1..) |index, ord| {577 for (self.dylibs.items, 1..) |index, ord| {
...@@ -606,7 +600,9 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node...@@ -606,7 +600,9 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
606 self.allocateSyntheticSymbols();600 self.allocateSyntheticSymbols();
607 try self.allocateLinkeditSegment();601 try self.allocateLinkeditSegment();
608602
609 state_log.debug("{}", .{self.dumpState()});603 if (build_options.enable_logging) {
604 state_log.debug("{}", .{self.dumpState()});
605 }
610606
611 try self.initDyldInfoSections();607 try self.initDyldInfoSections();
612608
...@@ -892,16 +888,6 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -892,16 +888,6 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
892 Compilation.dump_argv(argv.items);888 Compilation.dump_argv(argv.items);
893}889}
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
905pub fn resolveLibSystem(891pub fn resolveLibSystem(
906 self: *MachO,892 self: *MachO,
907 arena: Allocator,893 arena: Allocator,
...@@ -934,7 +920,7 @@ pub fn resolveLibSystem(...@@ -934,7 +920,7 @@ pub fn resolveLibSystem(
934 });920 });
935}921}
936922
937const ParseError = error{923pub const ParseError = error{
938 MalformedObject,924 MalformedObject,
939 MalformedArchive,925 MalformedArchive,
940 MalformedDylib,926 MalformedDylib,
...@@ -1011,7 +997,7 @@ fn parseObject(self: *MachO, path: []const u8) ParseError!void {...@@ -1011,7 +997,7 @@ fn parseObject(self: *MachO, path: []const u8) ParseError!void {
1011 try object.parse(self);997 try object.parse(self);
1012}998}
1013999
1014fn parseFatLibrary(self: *MachO, path: []const u8) !fat.Arch {1000pub fn parseFatLibrary(self: *MachO, path: []const u8) !fat.Arch {
1015 var buffer: [2]fat.Arch = undefined;1001 var buffer: [2]fat.Arch = undefined;
1016 const fat_archs = try fat.parseArchs(path, &buffer);1002 const fat_archs = try fat.parseArchs(path, &buffer);
1017 const cpu_arch = self.getTarget().cpu.arch;1003 const cpu_arch = self.getTarget().cpu.arch;
...@@ -1525,46 +1511,11 @@ fn createObjcSections(self: *MachO) !void {...@@ -1525,46 +1511,11 @@ fn createObjcSections(self: *MachO) !void {
1525}1511}
15261512
1527fn claimUnresolved(self: *MachO) error{OutOfMemory}!void {1513fn claimUnresolved(self: *MachO) error{OutOfMemory}!void {
1528 const gpa = self.base.comp.gpa;1514 if (self.getZigObject()) |zo| {
15291515 try zo.asFile().claimUnresolved(self);
1530 var objects = try std.ArrayList(File.Index).initCapacity(gpa, self.objects.items.len + 1);1516 }
1531 defer objects.deinit();1517 for (self.objects.items) |index| {
1532 if (self.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index);1518 try self.getFile(index).?.claimUnresolved(self);
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 }
1568 }1519 }
1569}1520}
15701521
...@@ -1590,26 +1541,12 @@ fn checkDuplicates(self: *MachO) !void {...@@ -1590,26 +1541,12 @@ fn checkDuplicates(self: *MachO) !void {
1590 try self.reportDuplicates(dupes);1541 try self.reportDuplicates(dupes);
1591}1542}
15921543
1593fn markImportsAndExports(self: *MachO) error{OutOfMemory}!void {1544fn markImportsAndExports(self: *MachO) void {
1594 const gpa = self.base.comp.gpa;1545 if (self.getZigObject()) |zo| {
1595 var objects = try std.ArrayList(File.Index).initCapacity(gpa, self.objects.items.len + 1);1546 zo.asFile().markImportsExports(self);
1596 defer objects.deinit();1547 }
1597 if (self.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index);1548 for (self.objects.items) |index| {
1598 objects.appendSliceAssumeCapacity(self.objects.items);1549 self.getFile(index).?.markImportsExports(self);
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 }
1613 }1550 }
16141551
1615 for (self.undefined_symbols.items) |index| {1552 for (self.undefined_symbols.items) |index| {
src/link/MachO/Archive.zig+229-61
...@@ -1,63 +1,5 @@...@@ -1,63 +1,5 @@
1objects: std.ArrayListUnmanaged(Object) = .{},1objects: 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
61pub fn isArchive(path: []const u8, fat_arch: ?fat.Arch) !bool {3pub fn isArchive(path: []const u8, fat_arch: ?fat.Arch) !bool {
62 const file = try std.fs.cwd().openFile(path, .{});4 const file = try std.fs.cwd().openFile(path, .{});
63 defer file.close();5 defer file.close();
...@@ -85,9 +27,9 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:...@@ -85,9 +27,9 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:
85 try handle.seekTo(offset);27 try handle.seekTo(offset);
8628
87 const reader = handle.reader();29 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;
91 while (true) {33 while (true) {
92 if (pos >= size) break;34 if (pos >= size) break;
93 if (!mem.isAligned(pos, 2)) {35 if (!mem.isAligned(pos, 2)) {
...@@ -123,7 +65,10 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:...@@ -123,7 +65,10 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:
123 pos += hdr_size;65 pos += hdr_size;
124 }66 }
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
128 const object = Object{73 const object = Object{
129 .archive = .{74 .archive = .{
...@@ -143,7 +88,229 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:...@@ -143,7 +88,229 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index:
143 }88 }
144}89}
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
146const fat = @import("fat.zig");312const fat = @import("fat.zig");
313const link = @import("../../link.zig");
147const log = std.log.scoped(.link);314const log = std.log.scoped(.link);
148const macho = std.macho;315const macho = std.macho;
149const mem = std.mem;316const mem = std.mem;
...@@ -154,3 +321,4 @@ const Archive = @This();...@@ -154,3 +321,4 @@ const Archive = @This();
154const File = @import("file.zig").File;321const File = @import("file.zig").File;
155const MachO = @import("../MachO.zig");322const MachO = @import("../MachO.zig");
156const Object = @import("Object.zig");323const Object = @import("Object.zig");
324const StringTable = @import("../StringTable.zig");
src/link/MachO/Object.zig+110-2
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1archive: ?Archive = null,1archive: ?InArchive = null,
2path: []const u8,2path: []const u8,
3file_handle: File.HandleIndex,3file_handle: File.HandleIndex,
4mtime: u64,4mtime: u64,
...@@ -29,8 +29,9 @@ hidden: bool = false,...@@ -29,8 +29,9 @@ hidden: bool = false,
2929
30dynamic_relocs: MachO.DynamicRelocs = .{},30dynamic_relocs: MachO.DynamicRelocs = .{},
31output_symtab_ctx: MachO.SymtabCtx = .{},31output_symtab_ctx: MachO.SymtabCtx = .{},
32output_ar_state: Archive.ArState = .{},
3233
33const Archive = struct {34const InArchive = struct {
34 path: []const u8,35 path: []const u8,
35 offset: u64,36 offset: u64,
36};37};
...@@ -1232,6 +1233,112 @@ fn addSection(self: *Object, allocator: Allocator, segname: []const u8, sectname...@@ -1232,6 +1233,112 @@ fn addSection(self: *Object, allocator: Allocator, segname: []const u8, sectname
1232 return n_sect;1233 return n_sect;
1233}1234}
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
1235pub fn calcSymtabSize(self: *Object, macho_file: *MachO) !void {1342pub fn calcSymtabSize(self: *Object, macho_file: *MachO) !void {
1236 const tracy = trace(@src());1343 const tracy = trace(@src());
1237 defer tracy.end();1344 defer tracy.end();
...@@ -2241,6 +2348,7 @@ const trace = @import("../../tracy.zig").trace;...@@ -2241,6 +2348,7 @@ const trace = @import("../../tracy.zig").trace;
2241const std = @import("std");2348const std = @import("std");
22422349
2243const Allocator = mem.Allocator;2350const Allocator = mem.Allocator;
2351const Archive = @import("Archive.zig");
2244const Atom = @import("Atom.zig");2352const Atom = @import("Atom.zig");
2245const Cie = eh_frame.Cie;2353const Cie = eh_frame.Cie;
2246const DwarfInfo = @import("DwarfInfo.zig");2354const DwarfInfo = @import("DwarfInfo.zig");
src/link/MachO/ZigObject.zig+37
...@@ -1,3 +1,4 @@...@@ -1,3 +1,4 @@
1data: std.ArrayListUnmanaged(u8) = .{},
1/// Externally owned memory.2/// Externally owned memory.
2path: []const u8,3path: []const u8,
3index: File.Index,4index: File.Index,
...@@ -47,6 +48,7 @@ relocs: RelocationTable = .{},...@@ -47,6 +48,7 @@ relocs: RelocationTable = .{},
4748
48dynamic_relocs: MachO.DynamicRelocs = .{},49dynamic_relocs: MachO.DynamicRelocs = .{},
49output_symtab_ctx: MachO.SymtabCtx = .{},50output_symtab_ctx: MachO.SymtabCtx = .{},
51output_ar_state: Archive.ArState = .{},
5052
51pub fn init(self: *ZigObject, macho_file: *MachO) !void {53pub fn init(self: *ZigObject, macho_file: *MachO) !void {
52 const comp = macho_file.base.comp;54 const comp = macho_file.base.comp;
...@@ -57,6 +59,7 @@ pub fn init(self: *ZigObject, macho_file: *MachO) !void {...@@ -57,6 +59,7 @@ pub fn init(self: *ZigObject, macho_file: *MachO) !void {
57}59}
5860
59pub fn deinit(self: *ZigObject, allocator: Allocator) void {61pub fn deinit(self: *ZigObject, allocator: Allocator) void {
62 self.data.deinit(allocator);
60 self.symtab.deinit(allocator);63 self.symtab.deinit(allocator);
61 self.strtab.deinit(allocator);64 self.strtab.deinit(allocator);
62 self.symbols.deinit(allocator);65 self.symbols.deinit(allocator);
...@@ -279,6 +282,40 @@ pub fn checkDuplicates(self: *ZigObject, dupes: anytype, macho_file: *MachO) !vo...@@ -279,6 +282,40 @@ pub fn checkDuplicates(self: *ZigObject, dupes: anytype, macho_file: *MachO) !vo
279 }282 }
280}283}
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
282pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void {319pub fn scanRelocs(self: *ZigObject, macho_file: *MachO) !void {
283 for (self.atoms.items) |atom_index| {320 for (self.atoms.items) |atom_index| {
284 const atom = macho_file.getAtom(atom_index) orelse continue;321 const atom = macho_file.getAtom(atom_index) orelse continue;
src/link/MachO/file.zig+116
...@@ -44,6 +44,97 @@ pub const File = union(enum) {...@@ -44,6 +44,97 @@ pub const File = union(enum) {
44 }44 }
45 }45 }
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
47 /// Encodes symbol rank so that the following ordering applies:138 /// Encodes symbol rank so that the following ordering applies:
48 /// * strong in object139 /// * strong in object
49 /// * weak in object140 /// * weak in object
...@@ -84,6 +175,29 @@ pub const File = union(enum) {...@@ -84,6 +175,29 @@ pub const File = union(enum) {
84 };175 };
85 }176 }
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
87 pub fn calcSymtabSize(file: File, macho_file: *MachO) !void {201 pub fn calcSymtabSize(file: File, macho_file: *MachO) !void {
88 return switch (file) {202 return switch (file) {
89 inline else => |x| x.calcSymtabSize(macho_file),203 inline else => |x| x.calcSymtabSize(macho_file),
...@@ -110,10 +224,12 @@ pub const File = union(enum) {...@@ -110,10 +224,12 @@ pub const File = union(enum) {
110 pub const HandleIndex = Index;224 pub const HandleIndex = Index;
111};225};
112226
227const assert = std.debug.assert;
113const macho = std.macho;228const macho = std.macho;
114const std = @import("std");229const std = @import("std");
115230
116const Allocator = std.mem.Allocator;231const Allocator = std.mem.Allocator;
232const Archive = @import("Archive.zig");
117const Atom = @import("Atom.zig");233const Atom = @import("Atom.zig");
118const InternalObject = @import("InternalObject.zig");234const InternalObject = @import("InternalObject.zig");
119const MachO = @import("../MachO.zig");235const MachO = @import("../MachO.zig");
src/link/MachO/relocatable.zig+250-44
...@@ -1,4 +1,4 @@...@@ -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 {
2 const gpa = macho_file.base.comp.gpa;2 const gpa = macho_file.base.comp.gpa;
33
4 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);4 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...@@ -46,8 +46,8 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u
4646
47 try macho_file.addUndefinedGlobals();47 try macho_file.addUndefinedGlobals();
48 try macho_file.resolveSymbols();48 try macho_file.resolveSymbols();
49 try markExports(macho_file);49 markExports(macho_file);
50 try claimUnresolved(macho_file);50 claimUnresolved(macho_file);
51 try initOutputSections(macho_file);51 try initOutputSections(macho_file);
52 try macho_file.sortSections();52 try macho_file.sortSections();
53 try macho_file.addAtomsToSections();53 try macho_file.addAtomsToSections();
...@@ -64,7 +64,9 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u...@@ -64,7 +64,9 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u
64 };64 };
65 off = allocateSectionsRelocs(macho_file, off);65 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
69 try macho_file.calcSymtabSize();71 try macho_file.calcSymtabSize();
70 try writeAtoms(macho_file);72 try writeAtoms(macho_file);
...@@ -86,54 +88,254 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u...@@ -86,54 +88,254 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u
86 try writeHeader(macho_file, ncmds, sizeofcmds);88 try writeHeader(macho_file, ncmds, sizeofcmds);
87}89}
8890
89fn markExports(macho_file: *MachO) error{OutOfMemory}!void {91pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
90 var objects = try std.ArrayList(File.Index).initCapacity(macho_file.base.comp.gpa, macho_file.objects.items.len + 1);92 const gpa = comp.gpa;
91 defer objects.deinit();93
92 if (macho_file.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index);94 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
93 objects.appendSliceAssumeCapacity(macho_file.objects.items);95 defer positionals.deinit();
9496
95 for (objects.items) |index| {97 try positionals.ensureUnusedCapacity(comp.objects.len);
96 for (macho_file.getFile(index).?.getSymbols()) |sym_index| {98 positionals.appendSliceAssumeCapacity(comp.objects);
97 const sym = macho_file.getSymbol(sym_index);99
98 const file = sym.getFile(macho_file) orelse continue;100 for (comp.c_object_table.keys()) |key| {
99 if (sym.visibility != .global) continue;101 try positionals.append(.{ .path = key.status.success.object_path });
100 if (file.getIndex() == index) {102 }
101 sym.flags.@"export" = true;103
102 }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()});
103 }145 }
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);
104 }167 }
105}
106168
107fn claimUnresolved(macho_file: *MachO) error{OutOfMemory}!void {169 var files = std.ArrayList(File.Index).init(gpa);
108 var objects = try std.ArrayList(File.Index).initCapacity(macho_file.base.comp.gpa, macho_file.objects.items.len + 1);170 defer files.deinit();
109 defer objects.deinit();171 try files.ensureTotalCapacityPrecise(macho_file.objects.items.len + 1);
110 if (macho_file.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index);172 if (macho_file.getZigObject()) |zo| files.appendAssumeCapacity(zo.index);
111 objects.appendSliceAssumeCapacity(macho_file.objects.items);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| {178 // Update ar symtab from parsed objects
114 const file = macho_file.getFile(index).?;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| {193 // Update file offsets of contributing objects
117 const nlist_idx = @as(Symbol.Index, @intCast(i));194 const total_size: usize = blk: {
118 const nlist = switch (file) {195 var pos: usize = Archive.SARMAG;
119 .object => |x| x.symtab.items(.nlist)[nlist_idx],196 pos += @sizeOf(Archive.ar_hdr) + Archive.SYMDEF.len + 1;
120 .zig_object => |x| x.symtab.items(.nlist)[nlist_idx],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,
121 else => unreachable,205 else => unreachable,
122 };206 };
123 if (!nlist.ext()) continue;207 const path = switch (file) {
124 if (!nlist.undf()) continue;208 .zig_object => |x| x.path,
125209 .object => |x| x.path,
126 const sym = macho_file.getSymbol(sym_index);210 else => unreachable,
127 if (sym.getFile(macho_file) != null) continue;211 };
128212 pos = mem.alignForward(usize, pos, ptr_width);
129 sym.value = 0;213 state.file_off = pos;
130 sym.atom = 0;214 pos += @sizeOf(Archive.ar_hdr) + path.len + 1;
131 sym.nlist_idx = nlist_idx;215 pos = mem.alignForward(usize, pos, ptr_width);
132 sym.file = index;216 pos += math.cast(usize, state.size) orelse return error.Overflow;
133 sym.flags.weak_ref = nlist.weakRef();217 }
134 sym.flags.import = true;218
135 sym.visibility = .global;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);
136 }243 }
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);
137 }339 }
138}340}
139341
...@@ -609,7 +811,9 @@ fn writeHeader(macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void {...@@ -609,7 +811,9 @@ fn writeHeader(macho_file: *MachO, ncmds: usize, sizeofcmds: usize) !void {
609}811}
610812
611const assert = std.debug.assert;813const assert = std.debug.assert;
814const build_options = @import("build_options");
612const eh_frame = @import("eh_frame.zig");815const eh_frame = @import("eh_frame.zig");
816const fat = @import("fat.zig");
613const link = @import("../../link.zig");817const link = @import("../../link.zig");
614const load_commands = @import("load_commands.zig");818const load_commands = @import("load_commands.zig");
615const log = std.log.scoped(.link);819const log = std.log.scoped(.link);
...@@ -620,8 +824,10 @@ const state_log = std.log.scoped(.link_state);...@@ -620,8 +824,10 @@ const state_log = std.log.scoped(.link_state);
620const std = @import("std");824const std = @import("std");
621const trace = @import("../../tracy.zig").trace;825const trace = @import("../../tracy.zig").trace;
622826
827const Archive = @import("Archive.zig");
623const Atom = @import("Atom.zig");828const Atom = @import("Atom.zig");
624const Compilation = @import("../../Compilation.zig");829const Compilation = @import("../../Compilation.zig");
625const File = @import("file.zig").File;830const File = @import("file.zig").File;
626const MachO = @import("../MachO.zig");831const MachO = @import("../MachO.zig");
832const Object = @import("Object.zig");
627const Symbol = @import("Symbol.zig");833const Symbol = @import("Symbol.zig");