zig/src/link/MachO/Relocation.zig
2025-07-07 22:43:52 -07:00

171 lines
5.5 KiB
Zig

tag: Tag,
offset: u32,
target: u32,
addend: i64,
type: Type,
meta: packed struct {
pcrel: bool,
has_subtractor: bool,
length: u2,
symbolnum: u24,
},
pub fn getTargetSymbolRef(rel: Relocation, atom: Atom, macho_file: *MachO) MachO.Ref {
assert(rel.tag == .@"extern");
return atom.getFile(macho_file).getSymbolRef(rel.target, macho_file);
}
pub fn getTargetSymbol(rel: Relocation, atom: Atom, macho_file: *MachO) *Symbol {
assert(rel.tag == .@"extern");
const ref = atom.getFile(macho_file).getSymbolRef(rel.target, macho_file);
return ref.getSymbol(macho_file).?;
}
pub fn getTargetAtom(rel: Relocation, atom: Atom, macho_file: *MachO) *Atom {
assert(rel.tag == .local);
return atom.getFile(macho_file).getAtom(rel.target).?;
}
pub fn getTargetAddress(rel: Relocation, atom: Atom, macho_file: *MachO) u64 {
return switch (rel.tag) {
.local => rel.getTargetAtom(atom, macho_file).getAddress(macho_file),
.@"extern" => rel.getTargetSymbol(atom, macho_file).getAddress(.{}, macho_file),
};
}
pub fn getGotTargetAddress(rel: Relocation, atom: Atom, macho_file: *MachO) u64 {
return switch (rel.tag) {
.local => 0,
.@"extern" => rel.getTargetSymbol(atom, macho_file).getGotAddress(macho_file),
};
}
pub fn getZigGotTargetAddress(rel: Relocation, macho_file: *MachO) u64 {
const zo = macho_file.getZigObject() orelse return 0;
return switch (rel.tag) {
.local => 0,
.@"extern" => {
const ref = zo.getSymbolRef(rel.target, macho_file);
return ref.getSymbol(macho_file).?.getZigGotAddress(macho_file);
},
};
}
pub fn getRelocAddend(rel: Relocation, cpu_arch: std.Target.Cpu.Arch) i64 {
const addend: i64 = switch (rel.type) {
.signed => 0,
.signed1 => -1,
.signed2 => -2,
.signed4 => -4,
else => 0,
};
return switch (cpu_arch) {
.x86_64 => if (rel.meta.pcrel) addend - 4 else addend,
else => addend,
};
}
pub fn lessThan(ctx: void, lhs: Relocation, rhs: Relocation) bool {
_ = ctx;
return lhs.offset < rhs.offset;
}
pub fn fmtPretty(rel: Relocation, cpu_arch: std.Target.Cpu.Arch) std.fmt.Formatter(Format, Format.pretty) {
return .{ .data = .{ .relocation = rel, .arch = cpu_arch } };
}
const Format = struct {
relocation: Relocation,
arch: std.Target.Cpu.Arch,
fn pretty(f: Format, w: *Writer) Writer.Error!void {
try w.writeAll(switch (f.relocation.type) {
.signed => "X86_64_RELOC_SIGNED",
.signed1 => "X86_64_RELOC_SIGNED_1",
.signed2 => "X86_64_RELOC_SIGNED_2",
.signed4 => "X86_64_RELOC_SIGNED_4",
.got_load => "X86_64_RELOC_GOT_LOAD",
.tlv => "X86_64_RELOC_TLV",
.page => "ARM64_RELOC_PAGE21",
.pageoff => "ARM64_RELOC_PAGEOFF12",
.got_load_page => "ARM64_RELOC_GOT_LOAD_PAGE21",
.got_load_pageoff => "ARM64_RELOC_GOT_LOAD_PAGEOFF12",
.tlvp_page => "ARM64_RELOC_TLVP_LOAD_PAGE21",
.tlvp_pageoff => "ARM64_RELOC_TLVP_LOAD_PAGEOFF12",
.branch => switch (f.arch) {
.x86_64 => "X86_64_RELOC_BRANCH",
.aarch64 => "ARM64_RELOC_BRANCH26",
else => unreachable,
},
.got => switch (f.arch) {
.x86_64 => "X86_64_RELOC_GOT",
.aarch64 => "ARM64_RELOC_POINTER_TO_GOT",
else => unreachable,
},
.subtractor => switch (f.arch) {
.x86_64 => "X86_64_RELOC_SUBTRACTOR",
.aarch64 => "ARM64_RELOC_SUBTRACTOR",
else => unreachable,
},
.unsigned => switch (f.arch) {
.x86_64 => "X86_64_RELOC_UNSIGNED",
.aarch64 => "ARM64_RELOC_UNSIGNED",
else => unreachable,
},
});
}
};
pub const Type = enum {
// x86_64
/// RIP-relative displacement (X86_64_RELOC_SIGNED)
signed,
/// RIP-relative displacement (X86_64_RELOC_SIGNED_1)
signed1,
/// RIP-relative displacement (X86_64_RELOC_SIGNED_2)
signed2,
/// RIP-relative displacement (X86_64_RELOC_SIGNED_4)
signed4,
/// RIP-relative GOT load (X86_64_RELOC_GOT_LOAD)
got_load,
/// RIP-relative TLV load (X86_64_RELOC_TLV)
tlv,
// arm64
/// PC-relative load (distance to page, ARM64_RELOC_PAGE21)
page,
/// Non-PC-relative offset to symbol (ARM64_RELOC_PAGEOFF12)
pageoff,
/// PC-relative GOT load (distance to page, ARM64_RELOC_GOT_LOAD_PAGE21)
got_load_page,
/// Non-PC-relative offset to GOT slot (ARM64_RELOC_GOT_LOAD_PAGEOFF12)
got_load_pageoff,
/// PC-relative TLV load (distance to page, ARM64_RELOC_TLVP_LOAD_PAGE21)
tlvp_page,
/// Non-PC-relative offset to TLV slot (ARM64_RELOC_TLVP_LOAD_PAGEOFF12)
tlvp_pageoff,
// common
/// PC-relative call/bl/b (X86_64_RELOC_BRANCH or ARM64_RELOC_BRANCH26)
branch,
/// PC-relative displacement to GOT pointer (X86_64_RELOC_GOT or ARM64_RELOC_POINTER_TO_GOT)
got,
/// Absolute subtractor value (X86_64_RELOC_SUBTRACTOR or ARM64_RELOC_SUBTRACTOR)
subtractor,
/// Absolute relocation (X86_64_RELOC_UNSIGNED or ARM64_RELOC_UNSIGNED)
unsigned,
};
const Tag = enum { local, @"extern" };
const std = @import("std");
const assert = std.debug.assert;
const macho = std.macho;
const math = std.math;
const Writer = std.io.Writer;
const Atom = @import("Atom.zig");
const MachO = @import("../MachO.zig");
const Relocation = @This();
const Symbol = @import("Symbol.zig");