1pub const File = union(enum) {
2 zig_object: *ZigObject,
3 internal: *InternalObject,
4 object: *Object,
5 dylib: *Dylib,
6
7 pub fn getIndex(file: File) Index {
8 return switch (file) {
9 inline else => |x| x.index,
10 };
11 }
12
13 pub fn fmtPath(file: File) std.fmt.Alt(File, formatPath) {
14 return .{ .data = file };
15 }
16
17 fn formatPath(file: File, w: *Writer) Writer.Error!void {
18 switch (file) {
19 .zig_object => |zo| try w.writeAll(zo.basename),
20 .internal => try w.writeAll("internal"),
21 .object => |x| try w.print("{f}", .{x.fmtPath()}),
22 .dylib => |dl| try w.print("{f}", .{@as(Path, dl.path)}),
23 }
24 }
25
26 pub fn resolveSymbols(file: File, macho_file: *MachO) !void {
27 return switch (file) {
28 inline else => |x| x.resolveSymbols(macho_file),
29 };
30 }
31
32 pub fn scanRelocs(file: File, macho_file: *MachO) !void {
33 return switch (file) {
34 .dylib => unreachable,
35 inline else => |x| x.scanRelocs(macho_file),
36 };
37 }
38
39 /// Encodes symbol rank so that the following ordering applies:
40 /// * strong in object
41 /// * strong in archive/dylib
42 /// * weak in object
43 /// * weak in archive/dylib
44 /// * tentative in object
45 /// * tentative in archive
46 /// * unclaimed
47 /// Ties are broken by file priority.
48 pub fn getSymbolRank(file: File, args: struct {
49 archive: bool = false,
50 weak: bool = false,
51 tentative: bool = false,
52 }) u32 {
53 const archive_or_dylib = @as(u32, @intFromBool(file == .dylib or args.archive)) << 29;
54 const strength: u32 = if (args.tentative) 0b10 << 30 else if (args.weak) 0b01 << 30 else 0b00 << 30;
55 return strength | archive_or_dylib | file.getIndex();
56 }
57
58 pub fn getAtom(file: File, atom_index: Atom.Index) ?*Atom {
59 return switch (file) {
60 .dylib => unreachable,
61 inline else => |x| x.getAtom(atom_index),
62 };
63 }
64
65 pub fn getAtoms(file: File) []const Atom.Index {
66 return switch (file) {
67 .dylib => unreachable,
68 inline else => |x| x.getAtoms(),
69 };
70 }
71
72 pub fn addAtomExtra(file: File, allocator: Allocator, extra: Atom.Extra) !u32 {
73 return switch (file) {
74 .dylib => unreachable,
75 inline else => |x| x.addAtomExtra(allocator, extra),
76 };
77 }
78
79 pub fn getAtomExtra(file: File, index: u32) Atom.Extra {
80 return switch (file) {
81 .dylib => unreachable,
82 inline else => |x| x.getAtomExtra(index),
83 };
84 }
85
86 pub fn setAtomExtra(file: File, index: u32, extra: Atom.Extra) void {
87 return switch (file) {
88 .dylib => unreachable,
89 inline else => |x| x.setAtomExtra(index, extra),
90 };
91 }
92
93 pub fn getSymbols(file: File) []Symbol {
94 return switch (file) {
95 inline else => |x| x.symbols.items,
96 };
97 }
98
99 pub fn getSymbolRef(file: File, sym_index: Symbol.Index, macho_file: *MachO) MachO.Ref {
100 return switch (file) {
101 inline else => |x| x.getSymbolRef(sym_index, macho_file),
102 };
103 }
104
105 pub fn getNlists(file: File) []macho.nlist_64 {
106 return switch (file) {
107 .dylib => unreachable,
108 .internal => |x| x.symtab.items,
109 inline else => |x| x.symtab.items(.nlist),
110 };
111 }
112
113 pub fn getGlobals(file: File) []MachO.SymbolResolver.Index {
114 return switch (file) {
115 inline else => |x| x.globals.items,
116 };
117 }
118
119 pub fn markImportsExports(file: File, macho_file: *MachO) void {
120 const tracy = trace(@src());
121 defer tracy.end();
122
123 const nsyms = switch (file) {
124 .dylib => unreachable,
125 inline else => |x| x.symbols.items.len,
126 };
127 for (0..nsyms) |i| {
128 const ref = file.getSymbolRef(@intCast(i), macho_file);
129 if (ref.getFile(macho_file) == null) continue;
130 const sym = ref.getSymbol(macho_file).?;
131 if (sym.visibility != .global) continue;
132 if (sym.getFile(macho_file).? == .dylib and !sym.flags.abs) {
133 sym.flags.import = true;
134 continue;
135 }
136 if (file.getIndex() == ref.file) {
137 sym.flags.@"export" = true;
138 }
139 }
140 }
141
142 pub fn markExportsRelocatable(file: File, macho_file: *MachO) void {
143 const tracy = trace(@src());
144 defer tracy.end();
145
146 assert(file == .object or file == .zig_object);
147
148 for (file.getSymbols(), 0..) |*sym, i| {
149 const ref = file.getSymbolRef(@intCast(i), macho_file);
150 const other_file = ref.getFile(macho_file) orelse continue;
151 if (other_file.getIndex() != file.getIndex()) continue;
152 if (sym.visibility != .global) continue;
153 sym.flags.@"export" = true;
154 }
155 }
156
157 pub fn createSymbolIndirection(file: File, macho_file: *MachO) !void {
158 const tracy = trace(@src());
159 defer tracy.end();
160
161 const nsyms = switch (file) {
162 inline else => |x| x.symbols.items.len,
163 };
164 for (0..nsyms) |i| {
165 const ref = file.getSymbolRef(@intCast(i), macho_file);
166 if (ref.getFile(macho_file) == null) continue;
167 if (ref.file != file.getIndex()) continue;
168 const sym = ref.getSymbol(macho_file).?;
169 if (sym.getSectionFlags().needs_got) {
170 log.debug("'{s}' needs GOT", .{sym.getName(macho_file)});
171 try macho_file.got.addSymbol(ref, macho_file);
172 }
173 if (sym.getSectionFlags().stubs) {
174 log.debug("'{s}' needs STUBS", .{sym.getName(macho_file)});
175 try macho_file.stubs.addSymbol(ref, macho_file);
176 }
177 if (sym.getSectionFlags().tlv_ptr) {
178 log.debug("'{s}' needs TLV pointer", .{sym.getName(macho_file)});
179 try macho_file.tlv_ptr.addSymbol(ref, macho_file);
180 }
181 if (sym.getSectionFlags().objc_stubs) {
182 log.debug("'{s}' needs OBJC STUBS", .{sym.getName(macho_file)});
183 try macho_file.objc_stubs.addSymbol(ref, macho_file);
184 }
185 }
186 }
187
188 pub fn claimUnresolved(file: File, macho_file: *MachO) void {
189 const tracy = trace(@src());
190 defer tracy.end();
191
192 assert(file == .object or file == .zig_object);
193
194 for (file.getSymbols(), file.getNlists(), 0..) |*sym, nlist, i| {
195 if (!nlist.n_type.bits.ext) continue;
196 if (nlist.n_type.bits.type != .undf) continue;
197
198 if (file.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
199
200 const is_import = switch (macho_file.undefined_treatment) {
201 .@"error" => false,
202 .warn, .suppress => nlist.n_desc.weak_ref,
203 .dynamic_lookup => true,
204 };
205 if (is_import) {
206 sym.value = 0;
207 sym.atom_ref = .{ .index = 0, .file = 0 };
208 sym.flags.weak = false;
209 sym.flags.weak_ref = nlist.n_desc.weak_ref;
210 sym.flags.import = is_import;
211 sym.visibility = .global;
212
213 const idx = file.getGlobals()[i];
214 macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = file.getIndex() };
215 }
216 }
217 }
218
219 pub fn claimUnresolvedRelocatable(file: File, macho_file: *MachO) void {
220 const tracy = trace(@src());
221 defer tracy.end();
222
223 assert(file == .object or file == .zig_object);
224
225 for (file.getSymbols(), file.getNlists(), 0..) |*sym, nlist, i| {
226 if (!nlist.n_type.bits.ext) continue;
227 if (nlist.n_type.bits.type != .undf) continue;
228 if (file.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
229
230 sym.value = 0;
231 sym.atom_ref = .{ .index = 0, .file = 0 };
232 sym.flags.weak_ref = nlist.n_desc.weak_ref;
233 sym.flags.import = true;
234 sym.visibility = .global;
235
236 const idx = file.getGlobals()[i];
237 macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = file.getIndex() };
238 }
239 }
240
241 pub fn checkDuplicates(file: File, macho_file: *MachO) !void {
242 const tracy = trace(@src());
243 defer tracy.end();
244
245 const io = macho_file.base.comp.io;
246 const gpa = macho_file.base.comp.gpa;
247
248 for (file.getSymbols(), file.getNlists(), 0..) |sym, nlist, i| {
249 if (sym.visibility != .global) continue;
250 if (sym.flags.weak) continue;
251 if (nlist.n_type.bits.type == .undf) continue;
252 const ref = file.getSymbolRef(@intCast(i), macho_file);
253 const ref_file = ref.getFile(macho_file) orelse continue;
254 if (ref_file.getIndex() == file.getIndex()) continue;
255
256 macho_file.dupes_mutex.lockUncancelable(io);
257 defer macho_file.dupes_mutex.unlock(io);
258
259 const gop = try macho_file.dupes.getOrPut(gpa, file.getGlobals()[i]);
260 if (!gop.found_existing) {
261 gop.value_ptr.* = .empty;
262 }
263 try gop.value_ptr.append(gpa, file.getIndex());
264 }
265 }
266
267 pub fn initOutputSections(file: File, macho_file: *MachO) !void {
268 const tracy = trace(@src());
269 defer tracy.end();
270 for (file.getAtoms()) |atom_index| {
271 const atom = file.getAtom(atom_index) orelse continue;
272 if (!atom.isAlive()) continue;
273 atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);
274 }
275 }
276
277 pub fn dedupLiterals(file: File, lp: MachO.LiteralPool, macho_file: *MachO) void {
278 return switch (file) {
279 .dylib => unreachable,
280 inline else => |x| x.dedupLiterals(lp, macho_file),
281 };
282 }
283
284 pub fn writeAtoms(file: File, macho_file: *MachO) !void {
285 return switch (file) {
286 .dylib => unreachable,
287 inline else => |x| x.writeAtoms(macho_file),
288 };
289 }
290
291 pub fn writeAtomsRelocatable(file: File, macho_file: *MachO) !void {
292 return switch (file) {
293 .dylib, .internal => unreachable,
294 inline else => |x| x.writeAtomsRelocatable(macho_file),
295 };
296 }
297
298 pub fn calcSymtabSize(file: File, macho_file: *MachO) void {
299 return switch (file) {
300 inline else => |x| x.calcSymtabSize(macho_file),
301 };
302 }
303
304 pub fn writeSymtab(file: File, macho_file: *MachO, ctx: anytype) void {
305 return switch (file) {
306 inline else => |x| x.writeSymtab(macho_file, ctx),
307 };
308 }
309
310 pub fn updateArSymtab(file: File, ar_symtab: *Archive.ArSymtab, macho_file: *MachO) error{OutOfMemory}!void {
311 return switch (file) {
312 .dylib, .internal => unreachable,
313 inline else => |x| x.updateArSymtab(ar_symtab, macho_file),
314 };
315 }
316
317 pub fn updateArSize(file: File, macho_file: *MachO) !void {
318 return switch (file) {
319 .dylib, .internal => unreachable,
320 .zig_object => |x| x.updateArSize(),
321 .object => |x| x.updateArSize(macho_file),
322 };
323 }
324
325 pub fn writeAr(file: File, macho_file: *MachO, writer: anytype) !void {
326 return switch (file) {
327 .dylib, .internal => unreachable,
328 .zig_object => |x| x.writeAr(writer),
329 .object => |x| x.writeAr(macho_file, writer),
330 };
331 }
332
333 pub fn parse(file: File, macho_file: *MachO) !void {
334 return switch (file) {
335 .internal, .zig_object => unreachable,
336 .object => |x| x.parse(macho_file),
337 .dylib => |x| x.parse(macho_file),
338 };
339 }
340
341 pub fn parseAr(file: File, macho_file: *MachO) !void {
342 return switch (file) {
343 .internal, .zig_object, .dylib => unreachable,
344 .object => |x| x.parseAr(macho_file),
345 };
346 }
347
348 pub const Index = u32;
349
350 pub const Entry = union(enum) {
351 null: void,
352 zig_object: ZigObject,
353 internal: InternalObject,
354 object: Object,
355 dylib: Dylib,
356 };
357
358 pub const Handle = Io.File;
359 pub const HandleIndex = Index;
360};
361
362const std = @import("std");
363const Io = std.Io;
364const assert = std.debug.assert;
365const log = std.log.scoped(.link);
366const macho = std.macho;
367const Allocator = std.mem.Allocator;
368const Path = std.Build.Cache.Path;
369const Writer = std.Io.Writer;
370
371const trace = @import("../../tracy.zig").trace;
372const Archive = @import("Archive.zig");
373const Atom = @import("Atom.zig");
374const InternalObject = @import("InternalObject.zig");
375const MachO = @import("../MachO.zig");
376const Object = @import("Object.zig");
377const Dylib = @import("Dylib.zig");
378const Symbol = @import("Symbol.zig");
379const ZigObject = @import("ZigObject.zig");