429 lines
13 KiB
Zig
429 lines
13 KiB
Zig
//! Represents a defined symbol.
|
|
|
|
/// Allocated address value of this symbol.
|
|
value: u64 = 0,
|
|
|
|
/// Offset into the linker's intern table.
|
|
name: MachO.String = .{},
|
|
|
|
/// File where this symbol is defined.
|
|
file: File.Index = 0,
|
|
|
|
/// Reference to Atom containing this symbol if any.
|
|
/// Use `getAtom` to get the pointer to the atom.
|
|
atom_ref: MachO.Ref = .{ .index = 0, .file = 0 },
|
|
|
|
/// Assigned output section index for this symbol.
|
|
out_n_sect: u8 = 0,
|
|
|
|
/// Index of the source nlist this symbol references.
|
|
/// Use `getNlist` to pull the nlist from the relevant file.
|
|
nlist_idx: u32 = 0,
|
|
|
|
/// Misc flags for the symbol packaged as packed struct for compression.
|
|
flags: Flags = .{},
|
|
|
|
sect_flags: std.atomic.Value(u8) = std.atomic.Value(u8).init(0),
|
|
|
|
visibility: Visibility = .local,
|
|
|
|
extra: u32 = 0,
|
|
|
|
pub fn isLocal(symbol: Symbol) bool {
|
|
return !(symbol.flags.import or symbol.flags.@"export");
|
|
}
|
|
|
|
pub fn isSymbolStab(symbol: Symbol, macho_file: *MachO) bool {
|
|
const file = symbol.getFile(macho_file) orelse return false;
|
|
return switch (file) {
|
|
.object => symbol.getNlist(macho_file).stab(),
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
pub fn isTlvInit(symbol: Symbol, macho_file: *MachO) bool {
|
|
const name = symbol.getName(macho_file);
|
|
return std.mem.indexOf(u8, name, "$tlv$init") != null;
|
|
}
|
|
|
|
pub fn weakRef(symbol: Symbol, macho_file: *MachO) bool {
|
|
const file = symbol.getFile(macho_file).?;
|
|
const is_dylib_weak = switch (file) {
|
|
.dylib => |x| x.weak,
|
|
else => false,
|
|
};
|
|
return is_dylib_weak or symbol.flags.weak_ref;
|
|
}
|
|
|
|
pub fn getName(symbol: Symbol, macho_file: *MachO) [:0]const u8 {
|
|
return switch (symbol.getFile(macho_file).?) {
|
|
inline else => |x| x.getString(symbol.name),
|
|
};
|
|
}
|
|
|
|
pub fn getAtom(symbol: Symbol, macho_file: *MachO) ?*Atom {
|
|
return symbol.atom_ref.getAtom(macho_file);
|
|
}
|
|
|
|
pub fn getOutputSectionIndex(symbol: Symbol, macho_file: *MachO) u8 {
|
|
if (symbol.getAtom(macho_file)) |atom| return atom.out_n_sect;
|
|
return symbol.out_n_sect;
|
|
}
|
|
|
|
pub fn getSectionFlags(symbol: Symbol) SectionFlags {
|
|
return @bitCast(symbol.sect_flags.load(.seq_cst));
|
|
}
|
|
|
|
pub fn setSectionFlags(symbol: *Symbol, flags: SectionFlags) void {
|
|
_ = symbol.sect_flags.fetchOr(@bitCast(flags), .seq_cst);
|
|
}
|
|
|
|
pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File {
|
|
return macho_file.getFile(symbol.file);
|
|
}
|
|
|
|
/// Asserts file is an object.
|
|
pub fn getNlist(symbol: Symbol, macho_file: *MachO) macho.nlist_64 {
|
|
const file = symbol.getFile(macho_file).?;
|
|
return switch (file) {
|
|
.dylib => unreachable,
|
|
.zig_object => unreachable,
|
|
.object => |x| x.symtab.items(.nlist)[symbol.nlist_idx],
|
|
.internal => |x| x.symtab.items[symbol.nlist_idx],
|
|
};
|
|
}
|
|
|
|
pub fn getSize(symbol: Symbol, macho_file: *MachO) u64 {
|
|
const file = symbol.getFile(macho_file).?;
|
|
assert(file == .object);
|
|
return file.object.symtab.items(.size)[symbol.nlist_idx];
|
|
}
|
|
|
|
pub fn getDylibOrdinal(symbol: Symbol, macho_file: *MachO) ?u16 {
|
|
assert(symbol.flags.import);
|
|
const file = symbol.getFile(macho_file) orelse return null;
|
|
return switch (file) {
|
|
.dylib => |x| x.ordinal,
|
|
else => null,
|
|
};
|
|
}
|
|
|
|
pub fn getSymbolRank(symbol: Symbol, macho_file: *MachO) u32 {
|
|
const file = symbol.getFile(macho_file) orelse return std.math.maxInt(u32);
|
|
const in_archive = switch (file) {
|
|
.object => |x| !x.alive,
|
|
else => false,
|
|
};
|
|
return file.getSymbolRank(.{
|
|
.archive = in_archive,
|
|
.weak = symbol.flags.weak,
|
|
.tentative = symbol.flags.tentative,
|
|
});
|
|
}
|
|
|
|
pub fn getAddress(symbol: Symbol, opts: struct {
|
|
stubs: bool = true,
|
|
trampoline: bool = true,
|
|
}, macho_file: *MachO) u64 {
|
|
if (opts.stubs) {
|
|
if (symbol.getSectionFlags().stubs) {
|
|
return symbol.getStubsAddress(macho_file);
|
|
} else if (symbol.getSectionFlags().objc_stubs) {
|
|
return symbol.getObjcStubsAddress(macho_file);
|
|
}
|
|
}
|
|
if (symbol.flags.trampoline and opts.trampoline) {
|
|
return symbol.getTrampolineAddress(macho_file);
|
|
}
|
|
if (symbol.getAtom(macho_file)) |atom| return atom.getAddress(macho_file) + symbol.value;
|
|
return symbol.value;
|
|
}
|
|
|
|
pub fn getGotAddress(symbol: Symbol, macho_file: *MachO) u64 {
|
|
if (!symbol.getSectionFlags().has_got) return 0;
|
|
const extra = symbol.getExtra(macho_file);
|
|
return macho_file.got.getAddress(extra.got, macho_file);
|
|
}
|
|
|
|
pub fn getStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
|
|
if (!symbol.getSectionFlags().stubs) return 0;
|
|
const extra = symbol.getExtra(macho_file);
|
|
return macho_file.stubs.getAddress(extra.stubs, macho_file);
|
|
}
|
|
|
|
pub fn getObjcStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
|
|
if (!symbol.getSectionFlags().objc_stubs) return 0;
|
|
const extra = symbol.getExtra(macho_file);
|
|
return macho_file.objc_stubs.getAddress(extra.objc_stubs, macho_file);
|
|
}
|
|
|
|
pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 {
|
|
if (!symbol.getSectionFlags().objc_stubs) return 0;
|
|
const extra = symbol.getExtra(macho_file);
|
|
const file = symbol.getFile(macho_file).?;
|
|
return switch (file) {
|
|
.dylib, .zig_object => unreachable,
|
|
inline else => |x| x.symbols.items[extra.objc_selrefs].getAddress(.{}, macho_file),
|
|
};
|
|
}
|
|
|
|
pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 {
|
|
if (!symbol.getSectionFlags().tlv_ptr) return 0;
|
|
const extra = symbol.getExtra(macho_file);
|
|
return macho_file.tlv_ptr.getAddress(extra.tlv_ptr, macho_file);
|
|
}
|
|
|
|
pub fn getTrampolineAddress(symbol: Symbol, macho_file: *MachO) u64 {
|
|
if (!symbol.flags.trampoline) return 0;
|
|
const zo = macho_file.getZigObject().?;
|
|
const index = symbol.getExtra(macho_file).trampoline;
|
|
return zo.symbols.items[index].getAddress(.{}, macho_file);
|
|
}
|
|
|
|
pub fn getOutputSymtabIndex(symbol: Symbol, macho_file: *MachO) ?u32 {
|
|
if (!symbol.flags.output_symtab) return null;
|
|
assert(!symbol.isSymbolStab(macho_file));
|
|
const file = symbol.getFile(macho_file).?;
|
|
const symtab_ctx = switch (file) {
|
|
inline else => |x| x.output_symtab_ctx,
|
|
};
|
|
var idx = symbol.getExtra(macho_file).symtab;
|
|
if (symbol.isLocal()) {
|
|
idx += symtab_ctx.ilocal;
|
|
} else if (symbol.flags.@"export") {
|
|
idx += symtab_ctx.iexport;
|
|
} else {
|
|
assert(symbol.flags.import);
|
|
idx += symtab_ctx.iimport;
|
|
}
|
|
return idx;
|
|
}
|
|
|
|
const AddExtraOpts = struct {
|
|
got: ?u32 = null,
|
|
stubs: ?u32 = null,
|
|
objc_stubs: ?u32 = null,
|
|
objc_selrefs: ?u32 = null,
|
|
tlv_ptr: ?u32 = null,
|
|
symtab: ?u32 = null,
|
|
trampoline: ?u32 = null,
|
|
};
|
|
|
|
pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) void {
|
|
var extra = symbol.getExtra(macho_file);
|
|
inline for (@typeInfo(@TypeOf(opts)).@"struct".fields) |field| {
|
|
if (@field(opts, field.name)) |x| {
|
|
@field(extra, field.name) = x;
|
|
}
|
|
}
|
|
symbol.setExtra(extra, macho_file);
|
|
}
|
|
|
|
pub inline fn getExtra(symbol: Symbol, macho_file: *MachO) Extra {
|
|
return switch (symbol.getFile(macho_file).?) {
|
|
inline else => |x| x.getSymbolExtra(symbol.extra),
|
|
};
|
|
}
|
|
|
|
pub inline fn setExtra(symbol: Symbol, extra: Extra, macho_file: *MachO) void {
|
|
return switch (symbol.getFile(macho_file).?) {
|
|
inline else => |x| x.setSymbolExtra(symbol.extra, extra),
|
|
};
|
|
}
|
|
|
|
pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) void {
|
|
if (symbol.isLocal()) {
|
|
out.n_type = if (symbol.flags.abs) macho.N_ABS else macho.N_SECT;
|
|
out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.getOutputSectionIndex(macho_file) + 1);
|
|
out.n_desc = 0;
|
|
out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file);
|
|
|
|
switch (symbol.visibility) {
|
|
.hidden => out.n_type |= macho.N_PEXT,
|
|
else => {},
|
|
}
|
|
} else if (symbol.flags.@"export") {
|
|
assert(symbol.visibility == .global);
|
|
out.n_type = macho.N_EXT;
|
|
out.n_type |= if (symbol.flags.abs) macho.N_ABS else macho.N_SECT;
|
|
out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.getOutputSectionIndex(macho_file) + 1);
|
|
out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file);
|
|
out.n_desc = 0;
|
|
|
|
if (symbol.flags.weak) {
|
|
out.n_desc |= macho.N_WEAK_DEF;
|
|
}
|
|
if (symbol.flags.dyn_ref) {
|
|
out.n_desc |= macho.REFERENCED_DYNAMICALLY;
|
|
}
|
|
} else {
|
|
assert(symbol.visibility == .global);
|
|
out.n_type = macho.N_EXT;
|
|
out.n_sect = 0;
|
|
out.n_value = 0;
|
|
out.n_desc = 0;
|
|
|
|
// TODO:
|
|
// const ord: u16 = if (macho_file.options.namespace == .flat)
|
|
// @as(u8, @bitCast(macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP))
|
|
// else if (symbol.getDylibOrdinal(macho_file)) |ord|
|
|
// ord
|
|
// else
|
|
// macho.BIND_SPECIAL_DYLIB_SELF;
|
|
const ord: u16 = if (symbol.getDylibOrdinal(macho_file)) |ord|
|
|
ord
|
|
else
|
|
macho.BIND_SPECIAL_DYLIB_SELF;
|
|
out.n_desc = macho.N_SYMBOL_RESOLVER * ord;
|
|
|
|
if (symbol.flags.weak) {
|
|
out.n_desc |= macho.N_WEAK_DEF;
|
|
}
|
|
|
|
if (symbol.weakRef(macho_file)) {
|
|
out.n_desc |= macho.N_WEAK_REF;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn fmt(symbol: Symbol, macho_file: *MachO) std.fmt.Formatter(Format, Format.default) {
|
|
return .{ .data = .{
|
|
.symbol = symbol,
|
|
.macho_file = macho_file,
|
|
} };
|
|
}
|
|
|
|
const Format = struct {
|
|
symbol: Symbol,
|
|
macho_file: *MachO,
|
|
|
|
fn format2(f: Format, w: *Writer) Writer.Error!void {
|
|
const symbol = f.symbol;
|
|
try w.print("%{d} : {s} : @{x}", .{
|
|
symbol.nlist_idx,
|
|
symbol.getName(f.macho_file),
|
|
symbol.getAddress(.{}, f.macho_file),
|
|
});
|
|
if (symbol.getFile(f.macho_file)) |file| {
|
|
if (symbol.getOutputSectionIndex(f.macho_file) != 0) {
|
|
try w.print(" : sect({d})", .{symbol.getOutputSectionIndex(f.macho_file)});
|
|
}
|
|
if (symbol.getAtom(f.macho_file)) |atom| {
|
|
try w.print(" : atom({d})", .{atom.atom_index});
|
|
}
|
|
var buf: [3]u8 = .{'_'} ** 3;
|
|
if (symbol.flags.@"export") buf[0] = 'E';
|
|
if (symbol.flags.import) buf[1] = 'I';
|
|
switch (symbol.visibility) {
|
|
.local => buf[2] = 'L',
|
|
.hidden => buf[2] = 'H',
|
|
.global => buf[2] = 'G',
|
|
}
|
|
try w.print(" : {s}", .{&buf});
|
|
if (symbol.flags.weak) try w.writeAll(" : weak");
|
|
if (symbol.isSymbolStab(f.macho_file)) try w.writeAll(" : stab");
|
|
switch (file) {
|
|
.zig_object => |x| try w.print(" : zig_object({d})", .{x.index}),
|
|
.internal => |x| try w.print(" : internal({d})", .{x.index}),
|
|
.object => |x| try w.print(" : object({d})", .{x.index}),
|
|
.dylib => |x| try w.print(" : dylib({d})", .{x.index}),
|
|
}
|
|
} else try w.writeAll(" : unresolved");
|
|
}
|
|
};
|
|
|
|
pub const Flags = packed struct {
|
|
/// Whether the symbol is imported at runtime.
|
|
import: bool = false,
|
|
|
|
/// Whether the symbol is exported at runtime.
|
|
@"export": bool = false,
|
|
|
|
/// Whether this symbol is weak.
|
|
weak: bool = false,
|
|
|
|
/// Whether this symbol is weakly referenced.
|
|
weak_ref: bool = false,
|
|
|
|
/// Whether this symbol is dynamically referenced.
|
|
dyn_ref: bool = false,
|
|
|
|
/// Whether this symbol was marked as N_NO_DEAD_STRIP.
|
|
no_dead_strip: bool = false,
|
|
|
|
/// Whether this symbol can be interposed at runtime.
|
|
interposable: bool = false,
|
|
|
|
/// Whether this symbol is absolute.
|
|
abs: bool = false,
|
|
|
|
/// Whether this symbol is a tentative definition.
|
|
tentative: bool = false,
|
|
|
|
/// Whether this symbol is a thread-local variable.
|
|
tlv: bool = false,
|
|
|
|
/// Whether the symbol makes into the output symtab or not.
|
|
output_symtab: bool = false,
|
|
|
|
/// ZigObject specific flags
|
|
/// Whether the symbol has a trampoline
|
|
trampoline: bool = false,
|
|
};
|
|
|
|
pub const SectionFlags = packed struct(u8) {
|
|
/// Whether the symbol contains __got indirection.
|
|
needs_got: bool = false,
|
|
has_got: bool = false,
|
|
|
|
/// Whether the symbols contains __stubs indirection.
|
|
stubs: bool = false,
|
|
|
|
/// Whether the symbol has a TLV pointer.
|
|
tlv_ptr: bool = false,
|
|
|
|
/// Whether the symbol contains __objc_stubs indirection.
|
|
objc_stubs: bool = false,
|
|
|
|
_: u3 = 0,
|
|
};
|
|
|
|
pub const Visibility = enum {
|
|
global,
|
|
hidden,
|
|
local,
|
|
|
|
pub fn rank(vis: Visibility) u2 {
|
|
return switch (vis) {
|
|
.local => 2,
|
|
.hidden => 1,
|
|
.global => 0,
|
|
};
|
|
}
|
|
};
|
|
|
|
pub const Extra = struct {
|
|
got: u32 = 0,
|
|
stubs: u32 = 0,
|
|
objc_stubs: u32 = 0,
|
|
objc_selrefs: u32 = 0,
|
|
tlv_ptr: u32 = 0,
|
|
symtab: u32 = 0,
|
|
trampoline: u32 = 0,
|
|
};
|
|
|
|
pub const Index = u32;
|
|
|
|
const assert = std.debug.assert;
|
|
const macho = std.macho;
|
|
const std = @import("std");
|
|
const Writer = std.io.Writer;
|
|
|
|
const Atom = @import("Atom.zig");
|
|
const File = @import("file.zig").File;
|
|
const MachO = @import("../MachO.zig");
|
|
const Nlist = Object.Nlist;
|
|
const Object = @import("Object.zig");
|
|
const Symbol = @This();
|
|
const ZigGotSection = @import("synthetic.zig").ZigGotSection;
|