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");