master
  1//! Represents a defined symbol.
  2
  3/// Allocated address value of this symbol.
  4value: u64 = 0,
  5
  6/// Offset into the linker's intern table.
  7name: MachO.String = .{},
  8
  9/// File where this symbol is defined.
 10file: File.Index = 0,
 11
 12/// Reference to Atom containing this symbol if any.
 13/// Use `getAtom` to get the pointer to the atom.
 14atom_ref: MachO.Ref = .{ .index = 0, .file = 0 },
 15
 16/// Assigned output section index for this symbol.
 17out_n_sect: u8 = 0,
 18
 19/// Index of the source nlist this symbol references.
 20/// Use `getNlist` to pull the nlist from the relevant file.
 21nlist_idx: u32 = 0,
 22
 23/// Misc flags for the symbol packaged as packed struct for compression.
 24flags: Flags = .{},
 25
 26sect_flags: std.atomic.Value(u8) = std.atomic.Value(u8).init(0),
 27
 28visibility: Visibility = .local,
 29
 30extra: u32 = 0,
 31
 32pub fn isLocal(symbol: Symbol) bool {
 33    return !(symbol.flags.import or symbol.flags.@"export");
 34}
 35
 36pub fn isSymbolStab(symbol: Symbol, macho_file: *MachO) bool {
 37    const file = symbol.getFile(macho_file) orelse return false;
 38    return switch (file) {
 39        .object => symbol.getNlist(macho_file).n_type.bits.is_stab != 0,
 40        else => false,
 41    };
 42}
 43
 44pub fn isTlvInit(symbol: Symbol, macho_file: *MachO) bool {
 45    const name = symbol.getName(macho_file);
 46    return std.mem.indexOf(u8, name, "$tlv$init") != null;
 47}
 48
 49pub fn weakRef(symbol: Symbol, macho_file: *MachO) bool {
 50    const file = symbol.getFile(macho_file).?;
 51    const is_dylib_weak = switch (file) {
 52        .dylib => |x| x.weak,
 53        else => false,
 54    };
 55    return is_dylib_weak or symbol.flags.weak_ref;
 56}
 57
 58pub fn getName(symbol: Symbol, macho_file: *MachO) [:0]const u8 {
 59    return switch (symbol.getFile(macho_file).?) {
 60        inline else => |x| x.getString(symbol.name),
 61    };
 62}
 63
 64pub fn getAtom(symbol: Symbol, macho_file: *MachO) ?*Atom {
 65    return symbol.atom_ref.getAtom(macho_file);
 66}
 67
 68pub fn getOutputSectionIndex(symbol: Symbol, macho_file: *MachO) u8 {
 69    if (symbol.getAtom(macho_file)) |atom| return atom.out_n_sect;
 70    return symbol.out_n_sect;
 71}
 72
 73pub fn getSectionFlags(symbol: Symbol) SectionFlags {
 74    return @bitCast(symbol.sect_flags.load(.seq_cst));
 75}
 76
 77pub fn setSectionFlags(symbol: *Symbol, flags: SectionFlags) void {
 78    _ = symbol.sect_flags.fetchOr(@bitCast(flags), .seq_cst);
 79}
 80
 81pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File {
 82    return macho_file.getFile(symbol.file);
 83}
 84
 85/// Asserts file is an object.
 86pub fn getNlist(symbol: Symbol, macho_file: *MachO) macho.nlist_64 {
 87    const file = symbol.getFile(macho_file).?;
 88    return switch (file) {
 89        .dylib => unreachable,
 90        .zig_object => unreachable,
 91        .object => |x| x.symtab.items(.nlist)[symbol.nlist_idx],
 92        .internal => |x| x.symtab.items[symbol.nlist_idx],
 93    };
 94}
 95
 96pub fn getSize(symbol: Symbol, macho_file: *MachO) u64 {
 97    const file = symbol.getFile(macho_file).?;
 98    assert(file == .object);
 99    return file.object.symtab.items(.size)[symbol.nlist_idx];
100}
101
102pub fn getDylibOrdinal(symbol: Symbol, macho_file: *MachO) ?u16 {
103    assert(symbol.flags.import);
104    const file = symbol.getFile(macho_file) orelse return null;
105    return switch (file) {
106        .dylib => |x| x.ordinal,
107        else => null,
108    };
109}
110
111pub fn getSymbolRank(symbol: Symbol, macho_file: *MachO) u32 {
112    const file = symbol.getFile(macho_file) orelse return std.math.maxInt(u32);
113    const in_archive = switch (file) {
114        .object => |x| !x.alive,
115        else => false,
116    };
117    return file.getSymbolRank(.{
118        .archive = in_archive,
119        .weak = symbol.flags.weak,
120        .tentative = symbol.flags.tentative,
121    });
122}
123
124pub fn getAddress(symbol: Symbol, opts: struct {
125    stubs: bool = true,
126    trampoline: bool = true,
127}, macho_file: *MachO) u64 {
128    if (opts.stubs) {
129        if (symbol.getSectionFlags().stubs) {
130            return symbol.getStubsAddress(macho_file);
131        } else if (symbol.getSectionFlags().objc_stubs) {
132            return symbol.getObjcStubsAddress(macho_file);
133        }
134    }
135    if (symbol.flags.trampoline and opts.trampoline) {
136        return symbol.getTrampolineAddress(macho_file);
137    }
138    if (symbol.getAtom(macho_file)) |atom| return atom.getAddress(macho_file) + symbol.value;
139    return symbol.value;
140}
141
142pub fn getGotAddress(symbol: Symbol, macho_file: *MachO) u64 {
143    if (!symbol.getSectionFlags().has_got) return 0;
144    const extra = symbol.getExtra(macho_file);
145    return macho_file.got.getAddress(extra.got, macho_file);
146}
147
148pub fn getStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
149    if (!symbol.getSectionFlags().stubs) return 0;
150    const extra = symbol.getExtra(macho_file);
151    return macho_file.stubs.getAddress(extra.stubs, macho_file);
152}
153
154pub fn getObjcStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
155    if (!symbol.getSectionFlags().objc_stubs) return 0;
156    const extra = symbol.getExtra(macho_file);
157    return macho_file.objc_stubs.getAddress(extra.objc_stubs, macho_file);
158}
159
160pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 {
161    if (!symbol.getSectionFlags().objc_stubs) return 0;
162    const extra = symbol.getExtra(macho_file);
163    const file = symbol.getFile(macho_file).?;
164    return switch (file) {
165        .dylib, .zig_object => unreachable,
166        inline else => |x| x.symbols.items[extra.objc_selrefs].getAddress(.{}, macho_file),
167    };
168}
169
170pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 {
171    if (!symbol.getSectionFlags().tlv_ptr) return 0;
172    const extra = symbol.getExtra(macho_file);
173    return macho_file.tlv_ptr.getAddress(extra.tlv_ptr, macho_file);
174}
175
176pub fn getTrampolineAddress(symbol: Symbol, macho_file: *MachO) u64 {
177    if (!symbol.flags.trampoline) return 0;
178    const zo = macho_file.getZigObject().?;
179    const index = symbol.getExtra(macho_file).trampoline;
180    return zo.symbols.items[index].getAddress(.{}, macho_file);
181}
182
183pub fn getOutputSymtabIndex(symbol: Symbol, macho_file: *MachO) ?u32 {
184    if (!symbol.flags.output_symtab) return null;
185    assert(!symbol.isSymbolStab(macho_file));
186    const file = symbol.getFile(macho_file).?;
187    const symtab_ctx = switch (file) {
188        inline else => |x| x.output_symtab_ctx,
189    };
190    var idx = symbol.getExtra(macho_file).symtab;
191    if (symbol.isLocal()) {
192        idx += symtab_ctx.ilocal;
193    } else if (symbol.flags.@"export") {
194        idx += symtab_ctx.iexport;
195    } else {
196        assert(symbol.flags.import);
197        idx += symtab_ctx.iimport;
198    }
199    return idx;
200}
201
202const AddExtraOpts = struct {
203    got: ?u32 = null,
204    stubs: ?u32 = null,
205    objc_stubs: ?u32 = null,
206    objc_selrefs: ?u32 = null,
207    tlv_ptr: ?u32 = null,
208    symtab: ?u32 = null,
209    trampoline: ?u32 = null,
210};
211
212pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) void {
213    var extra = symbol.getExtra(macho_file);
214    inline for (@typeInfo(@TypeOf(opts)).@"struct".fields) |field| {
215        if (@field(opts, field.name)) |x| {
216            @field(extra, field.name) = x;
217        }
218    }
219    symbol.setExtra(extra, macho_file);
220}
221
222pub inline fn getExtra(symbol: Symbol, macho_file: *MachO) Extra {
223    return switch (symbol.getFile(macho_file).?) {
224        inline else => |x| x.getSymbolExtra(symbol.extra),
225    };
226}
227
228pub inline fn setExtra(symbol: Symbol, extra: Extra, macho_file: *MachO) void {
229    return switch (symbol.getFile(macho_file).?) {
230        inline else => |x| x.setSymbolExtra(symbol.extra, extra),
231    };
232}
233
234pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) void {
235    if (symbol.isLocal()) {
236        out.n_type = .{ .bits = .{
237            .ext = false,
238            .type = if (symbol.flags.abs) .abs else .sect,
239            .pext = false,
240            .is_stab = 0,
241        } };
242        out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.getOutputSectionIndex(macho_file) + 1);
243        out.n_desc = @bitCast(@as(u16, 0));
244        out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file);
245
246        switch (symbol.visibility) {
247            .hidden => out.n_type.bits.pext = true,
248            else => {},
249        }
250    } else if (symbol.flags.@"export") {
251        assert(symbol.visibility == .global);
252        out.n_type = .{ .bits = .{
253            .ext = true,
254            .type = if (symbol.flags.abs) .abs else .sect,
255            .pext = false,
256            .is_stab = 0,
257        } };
258        out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.getOutputSectionIndex(macho_file) + 1);
259        out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file);
260        out.n_desc = @bitCast(@as(u16, 0));
261
262        if (symbol.flags.weak) {
263            out.n_desc.weak_def_or_ref_to_weak = true;
264        }
265        if (symbol.flags.dyn_ref) {
266            out.n_desc.referenced_dynamically = true;
267        }
268    } else {
269        assert(symbol.visibility == .global);
270        out.n_type = .{ .bits = .{
271            .ext = true,
272            .type = .undf,
273            .pext = false,
274            .is_stab = 0,
275        } };
276        out.n_sect = 0;
277        out.n_value = 0;
278        out.n_desc = @bitCast(@as(u16, 0));
279
280        // TODO:
281        // const ord: u16 = if (macho_file.options.namespace == .flat)
282        //     @as(u8, @bitCast(macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP))
283        // else if (symbol.getDylibOrdinal(macho_file)) |ord|
284        //     ord
285        // else
286        //     macho.BIND_SPECIAL_DYLIB_SELF;
287        const ord: u16 = if (symbol.getDylibOrdinal(macho_file)) |ord|
288            ord
289        else
290            macho.BIND_SPECIAL_DYLIB_SELF;
291        out.n_desc = @bitCast(macho.N_SYMBOL_RESOLVER * ord);
292
293        if (symbol.flags.weak) {
294            out.n_desc.weak_def_or_ref_to_weak = true;
295        }
296
297        if (symbol.weakRef(macho_file)) {
298            out.n_desc.weak_ref = true;
299        }
300    }
301}
302
303pub fn fmt(symbol: Symbol, macho_file: *MachO) std.fmt.Alt(Format, Format.default) {
304    return .{ .data = .{
305        .symbol = symbol,
306        .macho_file = macho_file,
307    } };
308}
309
310const Format = struct {
311    symbol: Symbol,
312    macho_file: *MachO,
313
314    fn default(f: Format, w: *Writer) Writer.Error!void {
315        const symbol = f.symbol;
316        try w.print("%{d} : {s} : @{x}", .{
317            symbol.nlist_idx,
318            symbol.getName(f.macho_file),
319            symbol.getAddress(.{}, f.macho_file),
320        });
321        if (symbol.getFile(f.macho_file)) |file| {
322            if (symbol.getOutputSectionIndex(f.macho_file) != 0) {
323                try w.print(" : sect({d})", .{symbol.getOutputSectionIndex(f.macho_file)});
324            }
325            if (symbol.getAtom(f.macho_file)) |atom| {
326                try w.print(" : atom({d})", .{atom.atom_index});
327            }
328            var buf: [3]u8 = .{'_'} ** 3;
329            if (symbol.flags.@"export") buf[0] = 'E';
330            if (symbol.flags.import) buf[1] = 'I';
331            switch (symbol.visibility) {
332                .local => buf[2] = 'L',
333                .hidden => buf[2] = 'H',
334                .global => buf[2] = 'G',
335            }
336            try w.print(" : {s}", .{&buf});
337            if (symbol.flags.weak) try w.writeAll(" : weak");
338            if (symbol.isSymbolStab(f.macho_file)) try w.writeAll(" : stab");
339            switch (file) {
340                .zig_object => |x| try w.print(" : zig_object({d})", .{x.index}),
341                .internal => |x| try w.print(" : internal({d})", .{x.index}),
342                .object => |x| try w.print(" : object({d})", .{x.index}),
343                .dylib => |x| try w.print(" : dylib({d})", .{x.index}),
344            }
345        } else try w.writeAll(" : unresolved");
346    }
347};
348
349pub const Flags = packed struct {
350    /// Whether the symbol is imported at runtime.
351    import: bool = false,
352
353    /// Whether the symbol is exported at runtime.
354    @"export": bool = false,
355
356    /// Whether this symbol is weak.
357    weak: bool = false,
358
359    /// Whether this symbol is weakly referenced.
360    weak_ref: bool = false,
361
362    /// Whether this symbol is dynamically referenced.
363    dyn_ref: bool = false,
364
365    /// Whether this symbol was marked as N_NO_DEAD_STRIP.
366    no_dead_strip: bool = false,
367
368    /// Whether this symbol can be interposed at runtime.
369    interposable: bool = false,
370
371    /// Whether this symbol is absolute.
372    abs: bool = false,
373
374    /// Whether this symbol is a tentative definition.
375    tentative: bool = false,
376
377    /// Whether this symbol is a thread-local variable.
378    tlv: bool = false,
379
380    /// Whether the symbol makes into the output symtab or not.
381    output_symtab: bool = false,
382
383    /// ZigObject specific flags
384    /// Whether the symbol has a trampoline
385    trampoline: bool = false,
386};
387
388pub const SectionFlags = packed struct(u8) {
389    /// Whether the symbol contains __got indirection.
390    needs_got: bool = false,
391    has_got: bool = false,
392
393    /// Whether the symbols contains __stubs indirection.
394    stubs: bool = false,
395
396    /// Whether the symbol has a TLV pointer.
397    tlv_ptr: bool = false,
398
399    /// Whether the symbol contains __objc_stubs indirection.
400    objc_stubs: bool = false,
401
402    _: u3 = 0,
403};
404
405pub const Visibility = enum {
406    global,
407    hidden,
408    local,
409
410    pub fn rank(vis: Visibility) u2 {
411        return switch (vis) {
412            .local => 2,
413            .hidden => 1,
414            .global => 0,
415        };
416    }
417};
418
419pub const Extra = struct {
420    got: u32 = 0,
421    stubs: u32 = 0,
422    objc_stubs: u32 = 0,
423    objc_selrefs: u32 = 0,
424    tlv_ptr: u32 = 0,
425    symtab: u32 = 0,
426    trampoline: u32 = 0,
427};
428
429pub const Index = u32;
430
431const assert = std.debug.assert;
432const macho = std.macho;
433const std = @import("std");
434const Writer = std.Io.Writer;
435
436const Atom = @import("Atom.zig");
437const File = @import("file.zig").File;
438const MachO = @import("../MachO.zig");
439const Nlist = Object.Nlist;
440const Object = @import("Object.zig");
441const Symbol = @This();
442const ZigGotSection = @import("synthetic.zig").ZigGotSection;