master
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 gpa = macho_file.base.comp.gpa;
246
247 for (file.getSymbols(), file.getNlists(), 0..) |sym, nlist, i| {
248 if (sym.visibility != .global) continue;
249 if (sym.flags.weak) continue;
250 if (nlist.n_type.bits.type == .undf) continue;
251 const ref = file.getSymbolRef(@intCast(i), macho_file);
252 const ref_file = ref.getFile(macho_file) orelse continue;
253 if (ref_file.getIndex() == file.getIndex()) continue;
254
255 macho_file.dupes_mutex.lock();
256 defer macho_file.dupes_mutex.unlock();
257
258 const gop = try macho_file.dupes.getOrPut(gpa, file.getGlobals()[i]);
259 if (!gop.found_existing) {
260 gop.value_ptr.* = .{};
261 }
262 try gop.value_ptr.append(gpa, file.getIndex());
263 }
264 }
265
266 pub fn initOutputSections(file: File, macho_file: *MachO) !void {
267 const tracy = trace(@src());
268 defer tracy.end();
269 for (file.getAtoms()) |atom_index| {
270 const atom = file.getAtom(atom_index) orelse continue;
271 if (!atom.isAlive()) continue;
272 atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(macho_file), macho_file);
273 }
274 }
275
276 pub fn dedupLiterals(file: File, lp: MachO.LiteralPool, macho_file: *MachO) void {
277 return switch (file) {
278 .dylib => unreachable,
279 inline else => |x| x.dedupLiterals(lp, macho_file),
280 };
281 }
282
283 pub fn writeAtoms(file: File, macho_file: *MachO) !void {
284 return switch (file) {
285 .dylib => unreachable,
286 inline else => |x| x.writeAtoms(macho_file),
287 };
288 }
289
290 pub fn writeAtomsRelocatable(file: File, macho_file: *MachO) !void {
291 return switch (file) {
292 .dylib, .internal => unreachable,
293 inline else => |x| x.writeAtomsRelocatable(macho_file),
294 };
295 }
296
297 pub fn calcSymtabSize(file: File, macho_file: *MachO) void {
298 return switch (file) {
299 inline else => |x| x.calcSymtabSize(macho_file),
300 };
301 }
302
303 pub fn writeSymtab(file: File, macho_file: *MachO, ctx: anytype) void {
304 return switch (file) {
305 inline else => |x| x.writeSymtab(macho_file, ctx),
306 };
307 }
308
309 pub fn updateArSymtab(file: File, ar_symtab: *Archive.ArSymtab, macho_file: *MachO) error{OutOfMemory}!void {
310 return switch (file) {
311 .dylib, .internal => unreachable,
312 inline else => |x| x.updateArSymtab(ar_symtab, macho_file),
313 };
314 }
315
316 pub fn updateArSize(file: File, macho_file: *MachO) !void {
317 return switch (file) {
318 .dylib, .internal => unreachable,
319 .zig_object => |x| x.updateArSize(),
320 .object => |x| x.updateArSize(macho_file),
321 };
322 }
323
324 pub fn writeAr(file: File, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void {
325 return switch (file) {
326 .dylib, .internal => unreachable,
327 .zig_object => |x| x.writeAr(ar_format, writer),
328 .object => |x| x.writeAr(ar_format, macho_file, writer),
329 };
330 }
331
332 pub fn parse(file: File, macho_file: *MachO) !void {
333 return switch (file) {
334 .internal, .zig_object => unreachable,
335 .object => |x| x.parse(macho_file),
336 .dylib => |x| x.parse(macho_file),
337 };
338 }
339
340 pub fn parseAr(file: File, macho_file: *MachO) !void {
341 return switch (file) {
342 .internal, .zig_object, .dylib => unreachable,
343 .object => |x| x.parseAr(macho_file),
344 };
345 }
346
347 pub const Index = u32;
348
349 pub const Entry = union(enum) {
350 null: void,
351 zig_object: ZigObject,
352 internal: InternalObject,
353 object: Object,
354 dylib: Dylib,
355 };
356
357 pub const Handle = std.fs.File;
358 pub const HandleIndex = Index;
359};
360
361const std = @import("std");
362const assert = std.debug.assert;
363const log = std.log.scoped(.link);
364const macho = std.macho;
365const Allocator = std.mem.Allocator;
366const Path = std.Build.Cache.Path;
367const Writer = std.Io.Writer;
368
369const trace = @import("../../tracy.zig").trace;
370const Archive = @import("Archive.zig");
371const Atom = @import("Atom.zig");
372const InternalObject = @import("InternalObject.zig");
373const MachO = @import("../MachO.zig");
374const Object = @import("Object.zig");
375const Dylib = @import("Dylib.zig");
376const Symbol = @import("Symbol.zig");
377const ZigObject = @import("ZigObject.zig");