aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2021-07-06 19:09:49 +0200
committerJakub Konka <kubkon@jakubkonka.com>2021-07-15 18:49:47 +0200
commitdbd2eb7c7f9267e8ae508d0995c1d4c5a3b46309 (patch)
tree9f1749c1ed5cebc47e4813a6f3fceb3e0101ccb9 /src
parent15b85df3dd8a754bc26159ea2202781b748a613e (diff)
downloadzig-dbd2eb7c7f9267e8ae508d0995c1d4c5a3b46309.tar.gz
zig-dbd2eb7c7f9267e8ae508d0995c1d4c5a3b46309.zip
zld: simplify relocation parsing
Diffstat (limited to 'src')
-rw-r--r--src/link/MachO/Object.zig37
-rw-r--r--src/link/MachO/Zld.zig62
-rw-r--r--src/link/MachO/reloc.zig868
-rw-r--r--src/link/MachO/reloc/aarch64.zig618
-rw-r--r--src/link/MachO/reloc/x86_64.zig385
5 files changed, 761 insertions, 1209 deletions
diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig
index 06d5a260cd..6e8925b648 100644
--- a/src/link/MachO/Object.zig
+++ b/src/link/MachO/Object.zig
@@ -408,7 +408,6 @@ const TextBlockParser = struct {
const start_addr = senior_nlist.nlist.n_value - self.section.addr;
const end_addr = if (next_nlist) |n| n.nlist.n_value - self.section.addr else self.section.size;
- log.warn("{} - {}", .{ start_addr, end_addr });
const code = self.code[start_addr..end_addr];
const size = code.len;
@@ -430,7 +429,7 @@ const TextBlockParser = struct {
.aliases = alias_only_indices,
.references = std.AutoArrayHashMap(u32, void).init(self.allocator),
.code = try self.allocator.dupe(u8, code),
- .relocs = std.ArrayList(*Relocation).init(self.allocator),
+ .relocs = std.ArrayList(Relocation).init(self.allocator),
.size = size,
.alignment = self.section.@"align",
};
@@ -579,7 +578,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
.local_sym_index = local_sym_index,
.references = std.AutoArrayHashMap(u32, void).init(self.allocator),
.code = try self.allocator.dupe(u8, code),
- .relocs = std.ArrayList(*Relocation).init(self.allocator),
+ .relocs = std.ArrayList(Relocation).init(self.allocator),
.size = sect.size,
.alignment = sect.@"align",
};
@@ -607,30 +606,14 @@ fn parseRelocs(
var it = reloc.RelocIterator{
.buffer = relocs,
};
-
- switch (self.arch.?) {
- .aarch64 => {
- var parser = reloc.aarch64.Parser{
- .object = self,
- .zld = zld,
- .it = &it,
- .block = block,
- .base_addr = base_addr,
- };
- try parser.parse();
- },
- .x86_64 => {
- var parser = reloc.x86_64.Parser{
- .object = self,
- .zld = zld,
- .it = &it,
- .block = block,
- .base_addr = base_addr,
- };
- try parser.parse();
- },
- else => unreachable,
- }
+ var parser = reloc.Parser{
+ .object = self,
+ .zld = zld,
+ .it = &it,
+ .block = block,
+ .base_addr = base_addr,
+ };
+ try parser.parse();
}
pub fn symbolFromReloc(self: *Object, rel: macho.relocation_info) !*Symbol {
diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig
index 7f7997bc17..69f8821cb7 100644
--- a/src/link/MachO/Zld.zig
+++ b/src/link/MachO/Zld.zig
@@ -137,7 +137,7 @@ pub const TextBlock = struct {
aliases: ?[]u32 = null,
references: std.AutoArrayHashMap(u32, void),
code: []u8,
- relocs: std.ArrayList(*Relocation),
+ relocs: std.ArrayList(Relocation),
size: u64,
alignment: u32,
next: ?*TextBlock = null,
@@ -1604,7 +1604,7 @@ fn resolveSymbols(self: *Zld) !void {
.local_sym_index = local_sym_index,
.references = std.AutoArrayHashMap(u32, void).init(self.allocator),
.code = code,
- .relocs = std.ArrayList(*Relocation).init(self.allocator),
+ .relocs = std.ArrayList(Relocation).init(self.allocator),
.size = size,
.alignment = alignment,
};
@@ -1871,64 +1871,6 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
}
}
-fn relocTargetAddr(self: *Zld, object: *const Object, target: reloc.Relocation.Target) !u64 {
- const target_addr = blk: {
- switch (target) {
- .symbol => |sym_id| {
- const sym = object.symbols.items[sym_id];
- switch (sym.payload) {
- .regular => |reg| {
- log.debug(" | regular '{s}'", .{sym.name});
- break :blk reg.address;
- },
- .proxy => |proxy| {
- if (mem.eql(u8, sym.name, "__tlv_bootstrap")) {
- log.debug(" | symbol '__tlv_bootstrap'", .{});
- const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
- const tlv = segment.sections.items[self.tlv_section_index.?];
- break :blk tlv.addr;
- }
-
- log.debug(" | symbol stub '{s}'", .{sym.name});
- const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
- const stubs = segment.sections.items[self.stubs_section_index.?];
- const stubs_index = sym.stubs_index orelse {
- if (proxy.bind_info.items.len > 0) {
- break :blk 0; // Dynamically bound by dyld.
- }
- log.err(
- "expected stubs index or dynamic bind address when relocating symbol '{s}'",
- .{sym.name},
- );
- log.err("this is an internal linker error", .{});
- return error.FailedToResolveRelocationTarget;
- };
- break :blk stubs.addr + stubs_index * stubs.reserved2;
- },
- else => {
- log.err("failed to resolve symbol '{s}' as a relocation target", .{sym.name});
- log.err("this is an internal linker error", .{});
- return error.FailedToResolveRelocationTarget;
- },
- }
- },
- .section => |sect_id| {
- log.debug(" | section offset", .{});
- const source_sect = object.sections.items[sect_id];
- log.debug(" | section '{s},{s}'", .{
- segmentName(source_sect.inner),
- sectionName(source_sect.inner),
- });
- const target_map = source_sect.target_map orelse unreachable;
- const target_seg = self.load_commands.items[target_map.segment_id].Segment;
- const target_sect = target_seg.sections.items[target_map.section_id];
- break :blk target_sect.addr + target_map.offset;
- },
- }
- };
- return target_addr;
-}
-
fn populateMetadata(self: *Zld) !void {
if (self.pagezero_segment_cmd_index == null) {
self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
diff --git a/src/link/MachO/reloc.zig b/src/link/MachO/reloc.zig
index e11e850aa6..4693e89787 100644
--- a/src/link/MachO/reloc.zig
+++ b/src/link/MachO/reloc.zig
@@ -1,4 +1,5 @@
const std = @import("std");
+const aarch64 = @import("../../codegen/aarch64.zig");
const assert = std.debug.assert;
const log = std.log.scoped(.reloc);
const macho = std.macho;
@@ -6,141 +7,431 @@ const math = std.math;
const mem = std.mem;
const meta = std.meta;
-pub const aarch64 = @import("reloc/aarch64.zig");
-pub const x86_64 = @import("reloc/x86_64.zig");
-
const Allocator = mem.Allocator;
+const Arch = std.Target.Cpu.Arch;
+const Object = @import("Object.zig");
const Symbol = @import("Symbol.zig");
-const TextBlock = @import("Zld.zig").TextBlock;
+const TextBlock = Zld.TextBlock;
+const Zld = @import("Zld.zig");
pub const Relocation = struct {
- @"type": Type,
+ /// Offset within the `block`s code buffer.
+ /// Note relocation size can be inferred by relocation's kind.
offset: u32,
+
+ /// Parent block containing this relocation.
block: *TextBlock,
+
+ /// Target symbol: either a regular or a proxy.
target: *Symbol,
- pub fn cast(base: *Relocation, comptime T: type) ?*T {
- if (base.@"type" != T.base_type)
- return null;
+ payload: union(enum) {
+ unsigned: Unsigned,
+ branch: Branch,
+ page: Page,
+ page_off: PageOff,
+ pointer_to_got: PointerToGot,
+ signed: Signed,
+ load: Load,
+ },
- return @fieldParentPtr(T, "base", base);
- }
+ pub const Unsigned = struct {
+ subtractor: ?*Symbol = null,
+
+ /// Addend embedded directly in the relocation slot
+ addend: i64,
+
+ /// Extracted from r_length:
+ /// => 3 implies true
+ /// => 2 implies false
+ /// => * is unreachable
+ is_64bit: bool,
+
+ pub fn resolve(self: Unsigned, base: Relocation, source_addr: u64, target_addr: u64) !void {
+ // const addend = if (unsigned.base.target == .section)
+ // unsigned.addend - @intCast(i64, args.source_target_sect_addr.?)
+ // else
+ // unsigned.addend;
+
+ // const result = if (args.subtractor) |subtractor|
+ // @intCast(i64, args.target_addr) - @intCast(i64, subtractor) + addend
+ // else
+ // @intCast(i64, args.target_addr) + addend;
+
+ // log.debug(" | calculated addend 0x{x}", .{addend});
+ // log.debug(" | calculated unsigned value 0x{x}", .{result});
+
+ // if (unsigned.is_64bit) {
+ // mem.writeIntLittle(
+ // u64,
+ // unsigned.base.code[0..8],
+ // @bitCast(u64, result),
+ // );
+ // } else {
+ // mem.writeIntLittle(
+ // u32,
+ // unsigned.base.code[0..4],
+ // @truncate(u32, @bitCast(u64, result)),
+ // );
+ // }
+ }
- // pub fn resolve(base: *Relocation) !void {
- // return switch (base.@"type") {
- // .unsigned => @fieldParentPtr(Unsigned, "base", base).resolve(),
- // .branch_aarch64 => @fieldParentPtr(aarch64.Branch, "base", base).resolve(),
- // .page => @fieldParentPtr(aarch64.Page, "base", base).resolve(),
- // .page_off => @fieldParentPtr(aarch64.PageOff, "base", base).resolve(),
- // .got_page => @fieldParentPtr(aarch64.GotPage, "base", base).resolve(),
- // .got_page_off => @fieldParentPtr(aarch64.GotPageOff, "base", base).resolve(),
- // .pointer_to_got => @fieldParentPtr(aarch64.PointerToGot, "base", base).resolve(),
- // .tlvp_page => @fieldParentPtr(aarch64.TlvpPage, "base", base).resolve(),
- // .tlvp_page_off => @fieldParentPtr(aarch64.TlvpPageOff, "base", base).resolve(),
- // .branch_x86_64 => @fieldParentPtr(x86_64.Branch, "base", base).resolve(),
- // .signed => @fieldParentPtr(x86_64.Signed, "base", base).resolve(),
- // .got_load => @fieldParentPtr(x86_64.GotLoad, "base", base).resolve(),
- // .got => @fieldParentPtr(x86_64.Got, "base", base).resolve(),
- // .tlv => @fieldParentPtr(x86_64.Tlv, "base", base).resolve(),
- // };
- // }
-
- pub const Type = enum {
- branch_aarch64,
- unsigned,
- page,
- page_off,
- got_page,
- got_page_off,
- tlvp_page,
- pointer_to_got,
- tlvp_page_off,
- branch_x86_64,
- signed,
- got_load,
- got,
- tlv,
+ pub fn format(self: Unsigned, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
+ _ = fmt;
+ _ = options;
+ try std.fmt.format(writer, "Unsigned {{ ", .{});
+ if (self.subtractor) |sub| {
+ try std.fmt.format(writer, ".subtractor = {}, ", .{sub});
+ }
+ try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
+ const length: usize = if (self.is_64bit) 8 else 4;
+ try std.fmt.format(writer, ".length = {}, ", .{length});
+ try std.fmt.format(writer, "}}", .{});
+ }
};
- pub fn format(base: *const Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- try std.fmt.format(writer, "Relocation {{ ", .{});
- try std.fmt.format(writer, ".type = {s}, ", .{base.@"type"});
- try std.fmt.format(writer, ".offset = {}, ", .{base.offset});
- try std.fmt.format(writer, ".block = {}", .{base.block.local_sym_index});
- try std.fmt.format(writer, ".target = {}, ", .{base.target});
-
- try switch (base.@"type") {
- .unsigned => @fieldParentPtr(Unsigned, "base", base).format(fmt, options, writer),
- .branch_aarch64 => @fieldParentPtr(aarch64.Branch, "base", base).format(fmt, options, writer),
- .page => @fieldParentPtr(aarch64.Page, "base", base).format(fmt, options, writer),
- .page_off => @fieldParentPtr(aarch64.PageOff, "base", base).format(fmt, options, writer),
- .got_page => @fieldParentPtr(aarch64.GotPage, "base", base).format(fmt, options, writer),
- .got_page_off => @fieldParentPtr(aarch64.GotPageOff, "base", base).format(fmt, options, writer),
- .pointer_to_got => @fieldParentPtr(aarch64.PointerToGot, "base", base).format(fmt, options, writer),
- .tlvp_page => @fieldParentPtr(aarch64.TlvpPage, "base", base).format(fmt, options, writer),
- .tlvp_page_off => @fieldParentPtr(aarch64.TlvpPageOff, "base", base).format(fmt, options, writer),
- .branch_x86_64 => @fieldParentPtr(x86_64.Branch, "base", base).format(fmt, options, writer),
- .signed => @fieldParentPtr(x86_64.Signed, "base", base).format(fmt, options, writer),
- .got_load => @fieldParentPtr(x86_64.GotLoad, "base", base).format(fmt, options, writer),
- .got => @fieldParentPtr(x86_64.Got, "base", base).format(fmt, options, writer),
- .tlv => @fieldParentPtr(x86_64.Tlv, "base", base).format(fmt, options, writer),
+ pub const Branch = struct {
+ arch: Arch,
+
+ pub fn resolve(self: Branch, base: Relocation, source_addr: u64, target_addr: u64) !void {
+ switch (arch) {
+ .aarch64 => {
+ const displacement = try math.cast(i28, @intCast(i64, target_addr) - @intCast(i64, source_addr));
+ var inst = aarch64.Instruction{
+ .unconditional_branch_immediate = mem.bytesToValue(
+ meta.TagPayload(
+ aarch.Instruction,
+ aarch64.Instruction.unconditional_branch_immediate,
+ ),
+ base.block.code[base.offset..][0..4],
+ ),
+ };
+ inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));
+ mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], inst.toU32());
+ },
+ .x86_64 => {
+ const displacement = try math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4);
+ mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], @bitCast(u32, displacement));
+ },
+ else => return error.UnsupportedCpuArchitecture,
+ }
+ }
+
+ pub fn format(self: Branch, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
+ _ = fmt;
+ _ = options;
+ try std.fmt.format(writer, "Branch {{}}", .{});
+ }
+ };
+
+ pub const Page = struct {
+ kind: enum {
+ page,
+ got,
+ tlvp,
+ },
+ addend: ?u32 = null,
+
+ pub fn resolve(self: Page, base: Relocation, source_addr: u64, target_addr: u64) !void {
+ const actual_target_addr = if (self.addend) |addend| target_addr + addend else target_addr;
+ const source_page = @intCast(i32, source_addr >> 12);
+ const target_page = @intCast(i32, actual_target_addr >> 12);
+ const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
+
+ var inst = aarch64.Instruction{
+ .pc_relative_address = mem.bytesToValue(
+ meta.TagPayload(
+ aarch64.Instruction,
+ aarch64.Instruction.pc_relative_address,
+ ),
+ base.block.code[base.offset..][0..4],
+ ),
+ };
+ inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
+ inst.pc_relative_address.immlo = @truncate(u2, pages);
+
+ mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], inst.toU32());
+ }
+
+ pub fn format(self: Page, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
+ _ = fmt;
+ _ = options;
+ try std.fmt.format(writer, "Page {{ ", .{});
+ switch (self.kind) {
+ .page => {},
+ .got => {
+ try std.fmt.format(writer, ".got, ", .{});
+ },
+ .tlvp => {
+ try std.fmt.format(writer, ".tlvp", .{});
+ },
+ }
+ if (self.addend) |add| {
+ try std.fmt.format(writer, ".addend = {}, ", .{add});
+ }
+ try std.fmt.format(writer, "}}", .{});
+ }
+ };
+
+ pub const PageOff = struct {
+ kind: enum {
+ page,
+ got,
+ tlvp,
+ },
+ addend: ?u32 = null,
+ op_kind: ?OpKind = null,
+
+ pub const OpKind = enum {
+ arithmetic,
+ load,
};
- try std.fmt.format(writer, "}}", .{});
+ pub fn resolve(self: PageOff, base: Relocation, source_addr: u64, target_addr: u64) !void {
+ switch (self.kind) {
+ .page => {
+ // const target_addr = if (page_off.addend) |addend| args.target_addr + addend else args.target_addr;
+ // const narrowed = @truncate(u12, target_addr);
+
+ // log.debug(" | narrowed address within the page 0x{x}", .{narrowed});
+ // log.debug(" | {s} opcode", .{page_off.op_kind});
+
+ // var inst = page_off.inst;
+ // if (page_off.op_kind == .arithmetic) {
+ // inst.add_subtract_immediate.imm12 = narrowed;
+ // } else {
+ // const offset: u12 = blk: {
+ // if (inst.load_store_register.size == 0) {
+ // if (inst.load_store_register.v == 1) {
+ // // 128-bit SIMD is scaled by 16.
+ // break :blk try math.divExact(u12, narrowed, 16);
+ // }
+ // // Otherwise, 8-bit SIMD or ldrb.
+ // break :blk narrowed;
+ // } else {
+ // const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size);
+ // break :blk try math.divExact(u12, narrowed, denom);
+ // }
+ // };
+ // inst.load_store_register.offset = offset;
+ // }
+
+ // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32());
+
+ },
+ .got => {
+ // const narrowed = @truncate(u12, args.target_addr);
+
+ // log.debug(" | narrowed address within the page 0x{x}", .{narrowed});
+
+ // var inst = page_off.inst;
+ // const offset = try math.divExact(u12, narrowed, 8);
+ // inst.load_store_register.offset = offset;
+
+ // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32());
+ },
+ .tlvp => {
+
+ // const narrowed = @truncate(u12, args.target_addr);
+
+ // log.debug(" | narrowed address within the page 0x{x}", .{narrowed});
+
+ // var inst = page_off.inst;
+ // inst.add_subtract_immediate.imm12 = narrowed;
+
+ // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32());
+ },
+ }
+ }
+
+ pub fn format(self: PageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
+ _ = fmt;
+ _ = options;
+ try std.fmt.format(writer, "PageOff {{ ", .{});
+ switch (self.kind) {
+ .page => {},
+ .got => {
+ try std.fmt.format(writer, ".got, ", .{});
+ },
+ .tlvp => {
+ try std.fmt.format(writer, ".tlvp, ", .{});
+ },
+ }
+ if (self.addend) |add| {
+ try std.fmt.format(writer, ".addend = {}, ", .{add});
+ }
+ if (self.op_kind) |op| {
+ try std.fmt.format(writer, ".op_kind = {s}, ", .{op});
+ }
+ try std.fmt.format(writer, "}}", .{});
+ }
+ };
+
+ pub const PointerToGot = struct {
+ pub fn resolve(self: PointerToGot, base: Relocation, source_addr: u64, target_addr: u64) !void {
+ const result = try math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr));
+ mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], @bitCast(u32, result));
+ }
+
+ pub fn format(self: PointerToGot, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
+ _ = fmt;
+ _ = options;
+ try std.fmt.format(writer, "PointerToGot {{}}", .{});
+ }
+ };
+
+ pub const Signed = struct {
+ addend: i32,
+ correction: i4,
+
+ pub fn resolve(self: Signed, base: Relocation, source_addr: u64, target_addr: u64) !void {
+ // const target_addr = target_addr: {
+ // if (signed.base.target == .section) {
+ // const source_target = @intCast(i64, args.source_source_sect_addr.?) + @intCast(i64, signed.base.offset) + signed.addend + 4;
+ // const source_disp = source_target - @intCast(i64, args.source_target_sect_addr.?);
+ // break :target_addr @intCast(i64, args.target_addr) + source_disp;
+ // }
+ // break :target_addr @intCast(i64, args.target_addr) + signed.addend;
+ // };
+ // const displacement = try math.cast(
+ // i32,
+ // target_addr - @intCast(i64, args.source_addr) - signed.correction - 4,
+ // );
+
+ // log.debug(" | addend 0x{x}", .{signed.addend});
+ // log.debug(" | correction 0x{x}", .{signed.correction});
+ // log.debug(" | displacement 0x{x}", .{displacement});
+
+ // mem.writeIntLittle(u32, signed.base.code[0..4], @bitCast(u32, displacement));
+ }
+
+ pub fn format(self: Signed, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
+ _ = fmt;
+ _ = options;
+ try std.fmt.format(writer, "Signed {{ ", .{});
+ try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
+ try std.fmt.format(writer, ".correction = {}, ", .{self.correction});
+ try std.fmt.format(writer, "}}", .{});
+ }
+ };
+
+ pub const Load = struct {
+ kind: enum {
+ got,
+ tlvp,
+ },
+ addend: ?i32 = null,
+
+ pub fn resolve(self: Load, base: Relocation, source_addr: u64, target_addr: u64) !void {
+ if (self.kind == .tlvp) {
+ // We need to rewrite the opcode from movq to leaq.
+ base.block.code[base.offset - 2] = 0x8d;
+ }
+ const addend = if (self.addend) |addend| addend else 0;
+ const displacement = try math.cast(
+ i32,
+ @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + addend,
+ );
+ mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], @bitCast(u32, displacement));
+ }
+
+ pub fn format(self: Load, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
+ _ = fmt;
+ _ = options;
+ try std.fmt.format(writer, "Load {{ ", .{});
+ try std.fmt.format(writer, "{s}, ", .{self.kind});
+ if (self.addend) |addend| {
+ try std.fmt.format(writer, ".addend = {}, ", .{addend});
+ }
+ try std.fmt.format(writer, "}}", .{});
+ }
+ };
+
+ pub fn resolve(self: Relocation, zld: *Zld) !void {
+ const source_addr = blk: {
+ const sym = zld.locals.items[self.block.local_sym_index];
+ break :blk sym.payload.regular.address;
+ };
+ const target_addr = blk: {
+ const is_via_got = inner: {
+ switch (self.payload) {
+ .pointer_to_got => break :inner true,
+ .page => |page| page.kind == .got,
+ .page_off => |page_off| page_off == .got,
+ .load => {},
+ else => break :inner false,
+ }
+ };
+
+ if (is_via_got) {
+ const dc_seg = zld.load_commands.items[zld.data_const_segment_cmd_index.?].Segment;
+ const got = dc_seg.sections.items[zld.got_section_index.?];
+ const got_index = self.target.got_index orelse {
+ log.err("expected GOT entry for symbol '{s}'", .{self.target.name});
+ log.err(" this is an internal linker error", .{});
+ return error.FailedToResolveRelocationTarget;
+ };
+ break :blk got.addr + got_index * @sizeOf(u64);
+ }
+
+ switch (self.target.payload) {
+ .regular => |reg| break :blk reg.address,
+ .proxy => |proxy| {
+ if (mem.eql(u8, self.target.name, "__tlv_bootstrap")) {
+ const segment = zld.load_commands.items[zld.data_segment_cmd_index.?].Segment;
+ const tlv = segment.sections.items[zld.tlv_section_index.?];
+ break :blk tlv.addr;
+ }
+
+ const segment = zld.load_commands.items[zld.text_segment_cmd_index.?].Segment;
+ const stubs = segment.sections.items[zld.stubs_section_index.?];
+ const stubs_index = self.target.stubs_index orelse {
+ if (proxy.bind_info.items.len > 0) {
+ break :blk 0; // Dynamically bound by dyld.
+ }
+ log.err("expected stubs index or dynamic bind address for symbol '{s}'", .{
+ self.target.name,
+ });
+ log.err(" this is an internal linker error", .{});
+ return error.FailedToResolveRelocationTarget;
+ };
+ break :blk stubs.addr + stubs_index * stubs.reserved2;
+ },
+ else => {
+ log.err("failed to resolve symbol '{s}' as a relocation target", .{self.target.name});
+ log.err(" this is an internal linker error", .{});
+ return error.FailedToResolveRelocationTarget;
+ },
+ }
+ };
+ switch (self.payload) {
+ .unsigned => |unsigned| try unsigned.resolve(self, source_addr, target_addr),
+ .branch => |branch| try branch.resolve(self, source_addr, target_addr),
+ .page => |page| try page.resolve(self, source_addr, target_addr),
+ .page_off => |page_off| try page_off.resolve(self, source_addr, target_addr),
+ .pointer_to_got => |pointer_to_got| try pointer_to_got.resolve(self, source_addr, target_addr),
+ .signed => |signed| try signed.resolve(self, source_addr, target_addr),
+ .load => |load| try load.resolve(self, source_addr, target_addr),
+ }
}
-};
-pub const Unsigned = struct {
- base: Relocation,
- subtractor: ?*Symbol = null,
- /// Addend embedded directly in the relocation slot
- addend: i64,
- /// Extracted from r_length:
- /// => 3 implies true
- /// => 2 implies false
- /// => * is unreachable
- is_64bit: bool,
-
- pub const base_type: Relocation.Type = .unsigned;
-
- // pub fn resolve(unsigned: Unsigned) !void {
- // const addend = if (unsigned.base.target == .section)
- // unsigned.addend - @intCast(i64, args.source_target_sect_addr.?)
- // else
- // unsigned.addend;
-
- // const result = if (args.subtractor) |subtractor|
- // @intCast(i64, args.target_addr) - @intCast(i64, subtractor) + addend
- // else
- // @intCast(i64, args.target_addr) + addend;
-
- // log.debug(" | calculated addend 0x{x}", .{addend});
- // log.debug(" | calculated unsigned value 0x{x}", .{result});
-
- // if (unsigned.is_64bit) {
- // mem.writeIntLittle(
- // u64,
- // unsigned.base.code[0..8],
- // @bitCast(u64, result),
- // );
- // } else {
- // mem.writeIntLittle(
- // u32,
- // unsigned.base.code[0..4],
- // @truncate(u32, @bitCast(u64, result)),
- // );
- // }
- // }
-
- pub fn format(self: Unsigned, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- _ = fmt;
- _ = options;
- if (self.subtractor) |sub| {
- try std.fmt.format(writer, ".subtractor = {}, ", .{sub});
- }
- try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
- const length: usize = if (self.is_64bit) 8 else 4;
- try std.fmt.format(writer, ".length = {}, ", .{length});
+ pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
+ try std.fmt.format(writer, "Relocation {{ ", .{});
+ try std.fmt.format(writer, ".offset = {}, ", .{self.offset});
+ try std.fmt.format(writer, ".block = {}", .{self.block.local_sym_index});
+ try std.fmt.format(writer, ".target = {}, ", .{self.target});
+
+ switch (self.payload) {
+ .unsigned => |unsigned| try unsigned.format(fmt, options, writer),
+ .branch => |branch| try branch.format(fmt, options, writer),
+ .page => |page| try page.format(fmt, options, writer),
+ .page_off => |page_off| try page_off.format(fmt, options, writer),
+ .pointer_to_got => |pointer_to_got| try pointer_to_got.format(fmt, options, writer),
+ .signed => |signed| try signed.format(fmt, options, writer),
+ .load => |load| try load.format(fmt, options, writer),
+ }
+
+ try std.fmt.format(writer, "}}", .{});
}
};
@@ -161,3 +452,342 @@ pub const RelocIterator = struct {
return self.buffer[@intCast(u32, self.index + 1)];
}
};
+
+pub const Parser = struct {
+ object: *Object,
+ zld: *Zld,
+ it: *RelocIterator,
+ block: *TextBlock,
+
+ /// Base address of the parsed text block in the source section.
+ base_addr: u64,
+
+ /// Used only when targeting aarch64
+ addend: ?u32 = null,
+
+ /// Parsed subtractor symbol from _RELOC_SUBTRACTOR reloc type.
+ subtractor: ?*Symbol = null,
+
+ pub fn parse(self: *Parser) !void {
+ while (self.it.next()) |rel| {
+ const out_rel = blk: {
+ switch (self.object.arch.?) {
+ .aarch64 => {
+ const out_rel = switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
+ .ARM64_RELOC_BRANCH26 => try self.parseBranch(rel),
+ .ARM64_RELOC_SUBTRACTOR => {
+ // Subtractor is not a relocation with effect on the TextBlock, so
+ // parse it and carry on.
+ try self.parseSubtractor(rel);
+
+ // Verify SUBTRACTOR is followed by UNSIGNED.
+ const next = @intToEnum(macho.reloc_type_arm64, self.it.peek().r_type);
+ if (next != .ARM64_RELOC_UNSIGNED) {
+ log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next});
+ return error.UnexpectedRelocationType;
+ }
+ continue;
+ },
+ .ARM64_RELOC_UNSIGNED => try self.parseUnsigned(rel),
+ .ARM64_RELOC_ADDEND => {
+ // Addend is not a relocation with effect on the TextBlock, so
+ // parse it and carry on.
+ try self.parseAddend(rel);
+
+ // Verify ADDEND is followed by a load.
+ const next = @intToEnum(macho.reloc_type_arm64, self.it.peek().r_type);
+ switch (next) {
+ .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {},
+ else => {
+ log.err("unexpected relocation type: expected PAGE21 or PAGEOFF12, found {s}", .{next});
+ return error.UnexpectedRelocationType;
+ },
+ }
+ continue;
+ },
+ .ARM64_RELOC_PAGE21,
+ .ARM64_RELOC_GOT_LOAD_PAGE21,
+ .ARM64_RELOC_TLVP_LOAD_PAGE21,
+ => try self.parsePage(rel),
+ .ARM64_RELOC_PAGEOFF12,
+ .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
+ .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
+ => try self.parsePageOff(rel),
+ .ARM64_RELOC_POINTER_TO_GOT => try self.parsePointerToGot(rel),
+ };
+ break :blk out_rel;
+ },
+ .x86_64 => {
+ const out_rel = switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
+ .X86_64_RELOC_BRANCH => try self.parseBranch(rel),
+ .X86_64_RELOC_SUBTRACTOR => {
+ // Subtractor is not a relocation with effect on the TextBlock, so
+ // parse it and carry on.
+ try self.parseSubtractor(rel);
+
+ // Verify SUBTRACTOR is followed by UNSIGNED.
+ const next = @intToEnum(macho.reloc_type_x86_64, self.it.peek().r_type);
+ if (next != .X86_64_RELOC_UNSIGNED) {
+ log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next});
+ return error.UnexpectedRelocationType;
+ }
+ continue;
+ },
+ .X86_64_RELOC_UNSIGNED => try self.parseUnsigned(rel),
+ .X86_64_RELOC_SIGNED,
+ .X86_64_RELOC_SIGNED_1,
+ .X86_64_RELOC_SIGNED_2,
+ .X86_64_RELOC_SIGNED_4,
+ => try self.parseSigned(rel),
+ .X86_64_RELOC_GOT_LOAD,
+ .X86_64_RELOC_GOT,
+ .X86_64_RELOC_TLV,
+ => try self.parseLoad(rel),
+ };
+ break :blk out_rel;
+ },
+ else => unreachable,
+ }
+ };
+ try self.block.relocs.append(out_rel);
+
+ if (out_rel.target.payload == .regular) {
+ try self.block.references.put(out_rel.target.payload.regular.local_sym_index, {});
+ }
+
+ const is_via_got = switch (out_rel.payload) {
+ .pointer_to_got => true,
+ .load => |load| load.kind == .got,
+ .page => |page| page.kind == .got,
+ .page_off => |page_off| page_off.kind == .got,
+ else => false,
+ };
+
+ if (is_via_got and out_rel.target.got_index == null) {
+ const index = @intCast(u32, self.zld.got_entries.items.len);
+ out_rel.target.got_index = index;
+ try self.zld.got_entries.append(self.zld.allocator, out_rel.target);
+ log.debug("adding GOT entry for symbol {s} at index {}", .{ out_rel.target.name, index });
+ }
+
+ if (out_rel.payload == .branch) {
+ const sym = out_rel.target;
+
+ if (sym.stubs_index != null) continue;
+ if (sym.payload != .proxy) continue;
+
+ const index = @intCast(u32, self.zld.stubs.items.len);
+ sym.stubs_index = index;
+ try self.zld.stubs.append(self.zld.allocator, sym);
+
+ log.debug("adding stub entry for symbol {s} at index {}", .{ sym.name, index });
+ }
+ }
+ }
+
+ fn parseBaseRelInfo(self: *Parser, rel: macho.relocation_info) !Relocation {
+ const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
+ const target = try self.object.symbolFromReloc(rel);
+ return Relocation{
+ .offset = offset,
+ .target = target,
+ .block = self.block,
+ .payload = undefined,
+ };
+ }
+
+ fn parseUnsigned(self: *Parser, rel: macho.relocation_info) !Relocation {
+ defer {
+ // Reset parser's subtractor state
+ self.subtractor = null;
+ }
+
+ assert(rel.r_pcrel == 0);
+
+ var parsed = try self.parseBaseRelInfo(rel);
+ const is_64bit: bool = switch (rel.r_length) {
+ 3 => true,
+ 2 => false,
+ else => unreachable,
+ };
+ const addend: i64 = if (is_64bit)
+ mem.readIntLittle(i64, self.block.code[parsed.offset..][0..8])
+ else
+ mem.readIntLittle(i32, self.block.code[parsed.offset..][0..4]);
+
+ parsed.payload = .{
+ .unsigned = .{
+ .subtractor = self.subtractor,
+ .is_64bit = is_64bit,
+ .addend = addend,
+ },
+ };
+
+ return parsed;
+ }
+
+ fn parseBranch(self: *Parser, rel: macho.relocation_info) !Relocation {
+ assert(rel.r_pcrel == 1);
+ assert(rel.r_length == 2);
+
+ var parsed = try self.parseBaseRelInfo(rel);
+ parsed.payload = .{
+ .branch = .{
+ .arch = self.object.arch.?,
+ },
+ };
+ return parsed;
+ }
+
+ fn parsePage(self: *Parser, rel: macho.relocation_info) !Relocation {
+ assert(rel.r_pcrel == 1);
+ assert(rel.r_length == 2);
+
+ const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
+
+ defer if (rel_type == .ARM64_RELOC_PAGE21) {
+ // Reset parser's addend state
+ self.addend = null;
+ };
+
+ const addend = if (rel_type == .ARM64_RELOC_PAGE21)
+ self.addend
+ else
+ null;
+
+ var parsed = try self.parseBaseRelInfo(rel);
+ parsed.payload = .{
+ .page = .{
+ .kind = switch (rel_type) {
+ .ARM64_RELOC_PAGE21 => .page,
+ .ARM64_RELOC_GOT_LOAD_PAGE21 => .got,
+ .ARM64_RELOC_TLVP_LOAD_PAGE21 => .tlvp,
+ else => unreachable,
+ },
+ .addend = addend,
+ },
+ };
+ return parsed;
+ }
+
+ fn parsePageOff(self: *Parser, rel: macho.relocation_info) !Relocation {
+ assert(rel.r_pcrel == 0);
+ assert(rel.r_length == 2);
+
+ const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
+
+ defer if (rel_type == .ARM64_RELOC_PAGEOFF12) {
+ // Reset parser's addend state
+ self.addend = null;
+ };
+
+ const addend = if (rel_type == .ARM64_RELOC_PAGEOFF12)
+ self.addend
+ else
+ null;
+
+ var parsed = try self.parseBaseRelInfo(rel);
+ const op_kind: ?Relocation.PageOff.OpKind = blk: {
+ if (rel_type != .ARM64_RELOC_PAGEOFF12) break :blk null;
+ const op_kind: Relocation.PageOff.OpKind = if (isArithmeticOp(self.block.code[parsed.offset..][0..4]))
+ .arithmetic
+ else
+ .load;
+ break :blk op_kind;
+ };
+
+ parsed.payload = .{
+ .page_off = .{
+ .kind = switch (rel_type) {
+ .ARM64_RELOC_PAGEOFF12 => .page,
+ .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => .got,
+ .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => .tlvp,
+ else => unreachable,
+ },
+ .addend = addend,
+ .op_kind = op_kind,
+ },
+ };
+ return parsed;
+ }
+
+ fn parsePointerToGot(self: *Parser, rel: macho.relocation_info) !Relocation {
+ assert(rel.r_pcrel == 1);
+ assert(rel.r_length == 2);
+
+ var parsed = try self.parseBaseRelInfo(rel);
+ parsed.payload = .{
+ .pointer_to_got = .{},
+ };
+ return parsed;
+ }
+
+ fn parseAddend(self: *Parser, rel: macho.relocation_info) !void {
+ assert(rel.r_pcrel == 0);
+ assert(rel.r_extern == 0);
+ assert(self.addend == null);
+
+ self.addend = rel.r_symbolnum;
+ }
+
+ fn parseSigned(self: *Parser, rel: macho.relocation_info) !Relocation {
+ assert(rel.r_pcrel == 1);
+ assert(rel.r_length == 2);
+
+ var parsed = try self.parseBaseRelInfo(rel);
+ const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
+ const correction: i4 = switch (rel_type) {
+ .X86_64_RELOC_SIGNED => 0,
+ .X86_64_RELOC_SIGNED_1 => 1,
+ .X86_64_RELOC_SIGNED_2 => 2,
+ .X86_64_RELOC_SIGNED_4 => 4,
+ else => unreachable,
+ };
+ const addend = mem.readIntLittle(i32, self.block.code[parsed.offset..][0..4]) + correction;
+
+ parsed.payload = .{
+ .signed = .{
+ .correction = correction,
+ .addend = addend,
+ },
+ };
+
+ return parsed;
+ }
+
+ fn parseSubtractor(self: *Parser, rel: macho.relocation_info) !void {
+ assert(rel.r_pcrel == 0);
+ assert(self.subtractor == null);
+
+ self.subtractor = try self.object.symbolFromReloc(rel);
+ }
+
+ fn parseLoad(self: *Parser, rel: macho.relocation_info) !Relocation {
+ assert(rel.r_pcrel == 1);
+ assert(rel.r_length == 2);
+
+ var parsed = try self.parseBaseRelInfo(rel);
+ const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
+ const addend = if (rel_type == .X86_64_RELOC_GOT)
+ mem.readIntLittle(i32, self.block.code[parsed.offset..][0..4])
+ else
+ null;
+
+ parsed.payload = .{
+ .load = .{
+ .kind = switch (rel_type) {
+ .X86_64_RELOC_GOT_LOAD, .X86_64_RELOC_GOT => .got,
+ .X86_64_RELOC_TLV => .tlvp,
+ else => unreachable,
+ },
+ .addend = addend,
+ },
+ };
+ return parsed;
+ }
+};
+
+inline fn isArithmeticOp(inst: *const [4]u8) bool {
+ const group_decode = @truncate(u5, inst[3]);
+ return ((group_decode >> 2) == 4);
+}
diff --git a/src/link/MachO/reloc/aarch64.zig b/src/link/MachO/reloc/aarch64.zig
deleted file mode 100644
index 5105282e43..0000000000
--- a/src/link/MachO/reloc/aarch64.zig
+++ /dev/null
@@ -1,618 +0,0 @@
-const std = @import("std");
-const aarch64 = @import("../../../codegen/aarch64.zig");
-const assert = std.debug.assert;
-const log = std.log.scoped(.reloc);
-const macho = std.macho;
-const math = std.math;
-const mem = std.mem;
-const meta = std.meta;
-const reloc = @import("../reloc.zig");
-
-const Allocator = mem.Allocator;
-const Object = @import("../Object.zig");
-const Relocation = reloc.Relocation;
-const Symbol = @import("../Symbol.zig");
-const TextBlock = Zld.TextBlock;
-const Zld = @import("../Zld.zig");
-
-pub const Branch = struct {
- base: Relocation,
- /// Always .UnconditionalBranchImmediate
- // inst: aarch64.Instruction,
-
- pub const base_type: Relocation.Type = .branch_aarch64;
-
- // pub fn resolve(branch: Branch, args: Relocation.ResolveArgs) !void {
- // const displacement = try math.cast(i28, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr));
-
- // log.debug(" | displacement 0x{x}", .{displacement});
-
- // var inst = branch.inst;
- // inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));
- // mem.writeIntLittle(u32, branch.base.code[0..4], inst.toU32());
- // }
-
- pub fn format(self: Branch, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- _ = self;
- _ = fmt;
- _ = options;
- _ = writer;
- }
-};
-
-pub const Page = struct {
- base: Relocation,
- addend: ?u32 = null,
- /// Always .PCRelativeAddress
- // inst: aarch64.Instruction,
-
- pub const base_type: Relocation.Type = .page;
-
- // pub fn resolve(page: Page, args: Relocation.ResolveArgs) !void {
- // const target_addr = if (page.addend) |addend| args.target_addr + addend else args.target_addr;
- // const source_page = @intCast(i32, args.source_addr >> 12);
- // const target_page = @intCast(i32, target_addr >> 12);
- // const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
-
- // log.debug(" | calculated addend 0x{x}", .{page.addend});
- // log.debug(" | moving by {} pages", .{pages});
-
- // var inst = page.inst;
- // inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
- // inst.pc_relative_address.immlo = @truncate(u2, pages);
-
- // mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32());
- // }
-
- pub fn format(self: Page, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- _ = fmt;
- _ = options;
- if (self.addend) |addend| {
- try std.fmt.format(writer, ".addend = {}, ", .{addend});
- }
- }
-};
-
-pub const PageOff = struct {
- base: Relocation,
- addend: ?u32 = null,
- op_kind: OpKind,
- // inst: aarch64.Instruction,
-
- pub const base_type: Relocation.Type = .page_off;
-
- pub const OpKind = enum {
- arithmetic,
- load_store,
- };
-
- // pub fn resolve(page_off: PageOff, args: Relocation.ResolveArgs) !void {
- // const target_addr = if (page_off.addend) |addend| args.target_addr + addend else args.target_addr;
- // const narrowed = @truncate(u12, target_addr);
-
- // log.debug(" | narrowed address within the page 0x{x}", .{narrowed});
- // log.debug(" | {s} opcode", .{page_off.op_kind});
-
- // var inst = page_off.inst;
- // if (page_off.op_kind == .arithmetic) {
- // inst.add_subtract_immediate.imm12 = narrowed;
- // } else {
- // const offset: u12 = blk: {
- // if (inst.load_store_register.size == 0) {
- // if (inst.load_store_register.v == 1) {
- // // 128-bit SIMD is scaled by 16.
- // break :blk try math.divExact(u12, narrowed, 16);
- // }
- // // Otherwise, 8-bit SIMD or ldrb.
- // break :blk narrowed;
- // } else {
- // const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size);
- // break :blk try math.divExact(u12, narrowed, denom);
- // }
- // };
- // inst.load_store_register.offset = offset;
- // }
-
- // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32());
- // }
-
- pub fn format(self: PageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- _ = fmt;
- _ = options;
- if (self.addend) |addend| {
- try std.fmt.format(writer, ".addend = {}, ", .{addend});
- }
- try std.fmt.format(writer, ".op_kind = {s}, ", .{self.op_kind});
- }
-};
-
-pub const GotPage = struct {
- base: Relocation,
- /// Always .PCRelativeAddress
- // inst: aarch64.Instruction,
-
- pub const base_type: Relocation.Type = .got_page;
-
- // pub fn resolve(page: GotPage, args: Relocation.ResolveArgs) !void {
- // const source_page = @intCast(i32, args.source_addr >> 12);
- // const target_page = @intCast(i32, args.target_addr >> 12);
- // const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
-
- // log.debug(" | moving by {} pages", .{pages});
-
- // var inst = page.inst;
- // inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
- // inst.pc_relative_address.immlo = @truncate(u2, pages);
-
- // mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32());
- // }
-
- pub fn format(self: GotPage, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- _ = self;
- _ = fmt;
- _ = options;
- _ = writer;
- }
-};
-
-pub const GotPageOff = struct {
- base: Relocation,
- /// Always .LoadStoreRegister with size = 3 for GOT indirection
- // inst: aarch64.Instruction,
-
- pub const base_type: Relocation.Type = .got_page_off;
-
- // pub fn resolve(page_off: GotPageOff, args: Relocation.ResolveArgs) !void {
- // const narrowed = @truncate(u12, args.target_addr);
-
- // log.debug(" | narrowed address within the page 0x{x}", .{narrowed});
-
- // var inst = page_off.inst;
- // const offset = try math.divExact(u12, narrowed, 8);
- // inst.load_store_register.offset = offset;
-
- // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32());
- // }
-
- pub fn format(self: GotPageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- _ = self;
- _ = fmt;
- _ = options;
- _ = writer;
- }
-};
-
-pub const PointerToGot = struct {
- base: Relocation,
-
- pub const base_type: Relocation.Type = .pointer_to_got;
-
- // pub fn resolve(ptr_to_got: PointerToGot, args: Relocation.ResolveArgs) !void {
- // const result = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr));
-
- // log.debug(" | calculated value 0x{x}", .{result});
-
- // mem.writeIntLittle(u32, ptr_to_got.base.code[0..4], @bitCast(u32, result));
- // }
-
- pub fn format(self: PointerToGot, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- _ = self;
- _ = fmt;
- _ = options;
- _ = writer;
- }
-};
-
-pub const TlvpPage = struct {
- base: Relocation,
- /// Always .PCRelativeAddress
- // inst: aarch64.Instruction,
-
- pub const base_type: Relocation.Type = .tlvp_page;
-
- // pub fn resolve(page: TlvpPage, args: Relocation.ResolveArgs) !void {
- // const source_page = @intCast(i32, args.source_addr >> 12);
- // const target_page = @intCast(i32, args.target_addr >> 12);
- // const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
-
- // log.debug(" | moving by {} pages", .{pages});
-
- // var inst = page.inst;
- // inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
- // inst.pc_relative_address.immlo = @truncate(u2, pages);
-
- // mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32());
- // }
-
- pub fn format(self: TlvpPage, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- _ = self;
- _ = fmt;
- _ = options;
- _ = writer;
- }
-};
-
-pub const TlvpPageOff = struct {
- base: Relocation,
- /// Always .AddSubtractImmediate regardless of the source instruction.
- /// This means, we always rewrite the instruction to add even if the
- /// source instruction was an ldr.
- // inst: aarch64.Instruction,
-
- pub const base_type: Relocation.Type = .tlvp_page_off;
-
- // pub fn resolve(page_off: TlvpPageOff, args: Relocation.ResolveArgs) !void {
- // const narrowed = @truncate(u12, args.target_addr);
-
- // log.debug(" | narrowed address within the page 0x{x}", .{narrowed});
-
- // var inst = page_off.inst;
- // inst.add_subtract_immediate.imm12 = narrowed;
-
- // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32());
- // }
-
- pub fn format(self: TlvpPageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- _ = self;
- _ = fmt;
- _ = options;
- _ = writer;
- }
-};
-
-pub const Parser = struct {
- object: *Object,
- zld: *Zld,
- it: *reloc.RelocIterator,
- block: *TextBlock,
- base_addr: u64,
- addend: ?u32 = null,
- subtractor: ?*Symbol = null,
-
- pub fn parse(self: *Parser) !void {
- while (self.it.next()) |rel| {
- const out_rel = switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
- .ARM64_RELOC_BRANCH26 => try self.parseBranch(rel),
- .ARM64_RELOC_SUBTRACTOR => {
- // Subtractor is not a relocation with effect on the TextBlock, so
- // parse it and carry on.
- try self.parseSubtractor(rel);
- continue;
- },
- .ARM64_RELOC_UNSIGNED => try self.parseUnsigned(rel),
- .ARM64_RELOC_ADDEND => {
- // Addend is not a relocation with effect on the TextBlock, so
- // parse it and carry on.
- try self.parseAddend(rel);
- continue;
- },
- .ARM64_RELOC_PAGE21,
- .ARM64_RELOC_GOT_LOAD_PAGE21,
- .ARM64_RELOC_TLVP_LOAD_PAGE21,
- => try self.parsePage(rel),
- .ARM64_RELOC_PAGEOFF12 => try self.parsePageOff(rel),
- .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => try self.parseGotLoadPageOff(rel),
- .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => try self.parseTlvpLoadPageOff(rel),
- .ARM64_RELOC_POINTER_TO_GOT => try self.parsePointerToGot(rel),
- };
- try self.block.relocs.append(out_rel);
-
- if (out_rel.target.payload == .regular) {
- try self.block.references.put(out_rel.target.payload.regular.local_sym_index, {});
- }
-
- switch (out_rel.@"type") {
- .got_page, .got_page_off, .pointer_to_got => {
- const sym = out_rel.target;
-
- if (sym.got_index != null) continue;
-
- const index = @intCast(u32, self.zld.got_entries.items.len);
- sym.got_index = index;
- try self.zld.got_entries.append(self.zld.allocator, sym);
-
- log.debug("adding GOT entry for symbol {s} at index {}", .{ sym.name, index });
- },
- .branch_aarch64 => {
- const sym = out_rel.target;
-
- if (sym.stubs_index != null) continue;
- if (sym.payload != .proxy) continue;
-
- const index = @intCast(u32, self.zld.stubs.items.len);
- sym.stubs_index = index;
- try self.zld.stubs.append(self.zld.allocator, sym);
-
- log.debug("adding stub entry for symbol {s} at index {}", .{ sym.name, index });
- },
- else => {},
- }
- }
- }
-
- fn parseAddend(self: *Parser, rel: macho.relocation_info) !void {
- const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
- assert(rel_type == .ARM64_RELOC_ADDEND);
- assert(rel.r_pcrel == 0);
- assert(rel.r_extern == 0);
- assert(self.addend == null);
-
- self.addend = rel.r_symbolnum;
-
- // Verify ADDEND is followed by a load.
- const next = @intToEnum(macho.reloc_type_arm64, self.it.peek().r_type);
- switch (next) {
- .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {},
- else => {
- log.err("unexpected relocation type: expected PAGE21 or PAGEOFF12, found {s}", .{next});
- return error.UnexpectedRelocationType;
- },
- }
- }
-
- fn parseBranch(self: *Parser, rel: macho.relocation_info) !*Relocation {
- const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
- assert(rel_type == .ARM64_RELOC_BRANCH26);
- assert(rel.r_pcrel == 1);
- assert(rel.r_length == 2);
-
- const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
- const target = try self.object.symbolFromReloc(rel);
-
- var branch = try self.object.allocator.create(Branch);
- errdefer self.object.allocator.destroy(branch);
-
- branch.* = .{
- .base = .{
- .@"type" = .branch_aarch64,
- .offset = offset,
- .target = target,
- .block = self.block,
- },
- };
-
- return &branch.base;
- }
-
- fn parsePage(self: *Parser, rel: macho.relocation_info) !*Relocation {
- assert(rel.r_pcrel == 1);
- assert(rel.r_length == 2);
-
- const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
- const target = try self.object.symbolFromReloc(rel);
- const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
-
- const ptr: *Relocation = ptr: {
- switch (rel_type) {
- .ARM64_RELOC_PAGE21 => {
- defer {
- // Reset parser's addend state
- self.addend = null;
- }
- var page = try self.object.allocator.create(Page);
- errdefer self.object.allocator.destroy(page);
-
- page.* = .{
- .base = .{
- .@"type" = .page,
- .offset = offset,
- .target = target,
- .block = self.block,
- },
- .addend = self.addend,
- };
-
- break :ptr &page.base;
- },
- .ARM64_RELOC_GOT_LOAD_PAGE21 => {
- var page = try self.object.allocator.create(GotPage);
- errdefer self.object.allocator.destroy(page);
-
- page.* = .{
- .base = .{
- .@"type" = .got_page,
- .offset = offset,
- .target = target,
- .block = self.block,
- },
- };
-
- break :ptr &page.base;
- },
- .ARM64_RELOC_TLVP_LOAD_PAGE21 => {
- var page = try self.object.allocator.create(TlvpPage);
- errdefer self.object.allocator.destroy(page);
-
- page.* = .{
- .base = .{
- .@"type" = .tlvp_page,
- .offset = offset,
- .target = target,
- .block = self.block,
- },
- };
-
- break :ptr &page.base;
- },
- else => unreachable,
- }
- };
-
- return ptr;
- }
-
- fn parsePageOff(self: *Parser, rel: macho.relocation_info) !*Relocation {
- defer {
- // Reset parser's addend state
- self.addend = null;
- }
-
- const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
- assert(rel_type == .ARM64_RELOC_PAGEOFF12);
- assert(rel.r_pcrel == 0);
- assert(rel.r_length == 2);
-
- const target = try self.object.symbolFromReloc(rel);
- const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
- const op_kind: PageOff.OpKind = if (isArithmeticOp(self.block.code[offset..][0..4]))
- .arithmetic
- else
- .load_store;
-
- var page_off = try self.object.allocator.create(PageOff);
- errdefer self.object.allocator.destroy(page_off);
-
- page_off.* = .{
- .base = .{
- .@"type" = .page_off,
- .offset = offset,
- .target = target,
- .block = self.block,
- },
- .op_kind = op_kind,
- .addend = self.addend,
- };
-
- return &page_off.base;
- }
-
- fn parseGotLoadPageOff(self: *Parser, rel: macho.relocation_info) !*Relocation {
- const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
- assert(rel_type == .ARM64_RELOC_GOT_LOAD_PAGEOFF12);
- assert(rel.r_pcrel == 0);
- assert(rel.r_length == 2);
-
- const target = try self.object.symbolFromReloc(rel);
- const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
- assert(!isArithmeticOp(self.block.code[offset..][0..4]));
-
- var page_off = try self.object.allocator.create(GotPageOff);
- errdefer self.object.allocator.destroy(page_off);
-
- page_off.* = .{
- .base = .{
- .@"type" = .got_page_off,
- .offset = offset,
- .target = target,
- .block = self.block,
- },
- };
-
- return &page_off.base;
- }
-
- fn parseTlvpLoadPageOff(self: *Parser, rel: macho.relocation_info) !*Relocation {
- const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
- assert(rel_type == .ARM64_RELOC_TLVP_LOAD_PAGEOFF12);
- assert(rel.r_pcrel == 0);
- assert(rel.r_length == 2);
-
- const RegInfo = struct {
- rd: u5,
- rn: u5,
- size: u1,
- };
-
- const target = try self.object.symbolFromReloc(rel);
- const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
-
- var page_off = try self.object.allocator.create(TlvpPageOff);
- errdefer self.object.allocator.destroy(page_off);
-
- page_off.* = .{
- .base = .{
- .@"type" = .tlvp_page_off,
- .offset = offset,
- .target = target,
- .block = self.block,
- },
- };
-
- return &page_off.base;
- }
-
- fn parseSubtractor(self: *Parser, rel: macho.relocation_info) !void {
- const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
- assert(rel_type == .ARM64_RELOC_SUBTRACTOR);
- assert(rel.r_pcrel == 0);
- assert(self.subtractor == null);
-
- self.subtractor = try self.object.symbolFromReloc(rel);
-
- // Verify SUBTRACTOR is followed by UNSIGNED.
- const next = @intToEnum(macho.reloc_type_arm64, self.it.peek().r_type);
- if (next != .ARM64_RELOC_UNSIGNED) {
- log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next});
- return error.UnexpectedRelocationType;
- }
- }
-
- fn parseUnsigned(self: *Parser, rel: macho.relocation_info) !*Relocation {
- defer {
- // Reset parser's subtractor state
- self.subtractor = null;
- }
-
- const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
- assert(rel_type == .ARM64_RELOC_UNSIGNED);
- assert(rel.r_pcrel == 0);
-
- const target = try self.object.symbolFromReloc(rel);
- const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
- const is_64bit: bool = switch (rel.r_length) {
- 3 => true,
- 2 => false,
- else => unreachable,
- };
- const addend: i64 = if (is_64bit)
- mem.readIntLittle(i64, self.block.code[offset..][0..8])
- else
- mem.readIntLittle(i32, self.block.code[offset..][0..4]);
-
- var unsigned = try self.object.allocator.create(reloc.Unsigned);
- errdefer self.object.allocator.destroy(unsigned);
-
- unsigned.* = .{
- .base = .{
- .@"type" = .unsigned,
- .offset = offset,
- .target = target,
- .block = self.block,
- },
- .subtractor = self.subtractor,
- .is_64bit = is_64bit,
- .addend = addend,
- };
-
- return &unsigned.base;
- }
-
- fn parsePointerToGot(self: *Parser, rel: macho.relocation_info) !*Relocation {
- const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
- assert(rel_type == .ARM64_RELOC_POINTER_TO_GOT);
- assert(rel.r_pcrel == 1);
- assert(rel.r_length == 2);
-
- var ptr_to_got = try self.object.allocator.create(PointerToGot);
- errdefer self.object.allocator.destroy(ptr_to_got);
-
- const target = try self.object.symbolFromReloc(rel);
- const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
-
- ptr_to_got.* = .{
- .base = .{
- .@"type" = .pointer_to_got,
- .offset = offset,
- .target = target,
- .block = self.block,
- },
- };
-
- return &ptr_to_got.base;
- }
-};
-
-inline fn isArithmeticOp(inst: *const [4]u8) bool {
- const group_decode = @truncate(u5, inst[3]);
- return ((group_decode >> 2) == 4);
-}
diff --git a/src/link/MachO/reloc/x86_64.zig b/src/link/MachO/reloc/x86_64.zig
deleted file mode 100644
index 85c797dcd0..0000000000
--- a/src/link/MachO/reloc/x86_64.zig
+++ /dev/null
@@ -1,385 +0,0 @@
-const std = @import("std");
-const assert = std.debug.assert;
-const log = std.log.scoped(.reloc);
-const macho = std.macho;
-const math = std.math;
-const mem = std.mem;
-const meta = std.meta;
-const reloc = @import("../reloc.zig");
-
-const Allocator = mem.Allocator;
-const Object = @import("../Object.zig");
-const Relocation = reloc.Relocation;
-const Symbol = @import("../Symbol.zig");
-const TextBlock = Zld.TextBlock;
-const Zld = @import("../Zld.zig");
-
-pub const Branch = struct {
- base: Relocation,
-
- pub const base_type: Relocation.Type = .branch_x86_64;
-
- // pub fn resolve(branch: Branch, args: Relocation.ResolveArgs) !void {
- // const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4);
- // log.debug(" | displacement 0x{x}", .{displacement});
- // mem.writeIntLittle(u32, branch.base.code[0..4], @bitCast(u32, displacement));
- // }
-
- pub fn format(self: Branch, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- _ = self;
- _ = fmt;
- _ = options;
- _ = writer;
- }
-};
-
-pub const Signed = struct {
- base: Relocation,
- addend: i32,
- correction: i4,
-
- pub const base_type: Relocation.Type = .signed;
-
- // pub fn resolve(signed: Signed, args: Relocation.ResolveArgs) !void {
- // const target_addr = target_addr: {
- // if (signed.base.target == .section) {
- // const source_target = @intCast(i64, args.source_source_sect_addr.?) + @intCast(i64, signed.base.offset) + signed.addend + 4;
- // const source_disp = source_target - @intCast(i64, args.source_target_sect_addr.?);
- // break :target_addr @intCast(i64, args.target_addr) + source_disp;
- // }
- // break :target_addr @intCast(i64, args.target_addr) + signed.addend;
- // };
- // const displacement = try math.cast(
- // i32,
- // target_addr - @intCast(i64, args.source_addr) - signed.correction - 4,
- // );
-
- // log.debug(" | addend 0x{x}", .{signed.addend});
- // log.debug(" | correction 0x{x}", .{signed.correction});
- // log.debug(" | displacement 0x{x}", .{displacement});
-
- // mem.writeIntLittle(u32, signed.base.code[0..4], @bitCast(u32, displacement));
- // }
-
- pub fn format(self: Signed, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- _ = fmt;
- _ = options;
- try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
- try std.fmt.format(writer, ".correction = {}, ", .{self.correction});
- }
-};
-
-pub const GotLoad = struct {
- base: Relocation,
-
- pub const base_type: Relocation.Type = .got_load;
-
- // pub fn resolve(got_load: GotLoad, args: Relocation.ResolveArgs) !void {
- // const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4);
- // log.debug(" | displacement 0x{x}", .{displacement});
- // mem.writeIntLittle(u32, got_load.base.code[0..4], @bitCast(u32, displacement));
- // }
-
- pub fn format(self: GotLoad, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- _ = self;
- _ = fmt;
- _ = options;
- _ = writer;
- }
-};
-
-pub const Got = struct {
- base: Relocation,
- addend: i32,
-
- pub const base_type: Relocation.Type = .got;
-
- // pub fn resolve(got: Got, args: Relocation.ResolveArgs) !void {
- // const displacement = try math.cast(
- // i32,
- // @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4 + got.addend,
- // );
- // log.debug(" | displacement 0x{x}", .{displacement});
- // mem.writeIntLittle(u32, got.base.code[0..4], @bitCast(u32, displacement));
- // }
-
- pub fn format(self: Got, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- _ = fmt;
- _ = options;
- try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
- }
-};
-
-pub const Tlv = struct {
- base: Relocation,
-
- pub const base_type: Relocation.Type = .tlv;
-
- // pub fn resolve(tlv: Tlv, args: Relocation.ResolveArgs) !void {
- // // We need to rewrite the opcode from movq to leaq.
- // tlv.op.* = 0x8d;
- // log.debug(" | rewriting op to leaq", .{});
-
- // const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4);
- // log.debug(" | displacement 0x{x}", .{displacement});
-
- // mem.writeIntLittle(u32, tlv.base.code[0..4], @bitCast(u32, displacement));
- // }
- pub fn format(self: Tlv, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- _ = self;
- _ = fmt;
- _ = options;
- _ = writer;
- }
-};
-
-pub const Parser = struct {
- object: *Object,
- zld: *Zld,
- it: *reloc.RelocIterator,
- block: *TextBlock,
- base_addr: u64,
- subtractor: ?*Symbol = null,
-
- pub fn parse(self: *Parser) !void {
- while (self.it.next()) |rel| {
- const out_rel = switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
- .X86_64_RELOC_BRANCH => try self.parseBranch(rel),
- .X86_64_RELOC_SUBTRACTOR => {
- // Subtractor is not a relocation with effect on the TextBlock, so
- // parse it and carry on.
- try self.parseSubtractor(rel);
- continue;
- },
- .X86_64_RELOC_UNSIGNED => try self.parseUnsigned(rel),
- .X86_64_RELOC_SIGNED,
- .X86_64_RELOC_SIGNED_1,
- .X86_64_RELOC_SIGNED_2,
- .X86_64_RELOC_SIGNED_4,
- => try self.parseSigned(rel),
- .X86_64_RELOC_GOT_LOAD => try self.parseGotLoad(rel),
- .X86_64_RELOC_GOT => try self.parseGot(rel),
- .X86_64_RELOC_TLV => try self.parseTlv(rel),
- };
- try self.block.relocs.append(out_rel);
-
- if (out_rel.target.payload == .regular) {
- try self.block.references.put(out_rel.target.payload.regular.local_sym_index, {});
- }
-
- switch (out_rel.@"type") {
- .got_load, .got => {
- const sym = out_rel.target;
-
- if (sym.got_index != null) continue;
-
- const index = @intCast(u32, self.zld.got_entries.items.len);
- sym.got_index = index;
- try self.zld.got_entries.append(self.zld.allocator, sym);
-
- log.debug("adding GOT entry for symbol {s} at index {}", .{ sym.name, index });
- },
- .branch_x86_64 => {
- const sym = out_rel.target;
-
- if (sym.stubs_index != null) continue;
- if (sym.payload != .proxy) continue;
-
- const index = @intCast(u32, self.zld.stubs.items.len);
- sym.stubs_index = index;
- try self.zld.stubs.append(self.zld.allocator, sym);
-
- log.debug("adding stub entry for symbol {s} at index {}", .{ sym.name, index });
- },
- else => {},
- }
- }
- }
-
- fn parseBranch(self: *Parser, rel: macho.relocation_info) !*Relocation {
- const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
- assert(rel_type == .X86_64_RELOC_BRANCH);
- assert(rel.r_pcrel == 1);
- assert(rel.r_length == 2);
-
- const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
- const target = try self.object.symbolFromReloc(rel);
-
- var branch = try self.object.allocator.create(Branch);
- errdefer self.object.allocator.destroy(branch);
-
- branch.* = .{
- .base = .{
- .@"type" = .branch_x86_64,
- .offset = offset,
- .target = target,
- .block = self.block,
- },
- };
-
- return &branch.base;
- }
-
- fn parseSigned(self: *Parser, rel: macho.relocation_info) !*Relocation {
- assert(rel.r_pcrel == 1);
- assert(rel.r_length == 2);
-
- const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
- const target = try self.object.symbolFromReloc(rel);
- const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
- const correction: i4 = switch (rel_type) {
- .X86_64_RELOC_SIGNED => 0,
- .X86_64_RELOC_SIGNED_1 => 1,
- .X86_64_RELOC_SIGNED_2 => 2,
- .X86_64_RELOC_SIGNED_4 => 4,
- else => unreachable,
- };
- const addend = mem.readIntLittle(i32, self.block.code[offset..][0..4]) + correction;
-
- var signed = try self.object.allocator.create(Signed);
- errdefer self.object.allocator.destroy(signed);
-
- signed.* = .{
- .base = .{
- .@"type" = .signed,
- .offset = offset,
- .target = target,
- .block = self.block,
- },
- .addend = addend,
- .correction = correction,
- };
-
- return &signed.base;
- }
-
- fn parseGotLoad(self: *Parser, rel: macho.relocation_info) !*Relocation {
- const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
- assert(rel_type == .X86_64_RELOC_GOT_LOAD);
- assert(rel.r_pcrel == 1);
- assert(rel.r_length == 2);
-
- const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
- const target = try self.object.symbolFromReloc(rel);
-
- var got_load = try self.object.allocator.create(GotLoad);
- errdefer self.object.allocator.destroy(got_load);
-
- got_load.* = .{
- .base = .{
- .@"type" = .got_load,
- .offset = offset,
- .target = target,
- .block = self.block,
- },
- };
-
- return &got_load.base;
- }
-
- fn parseGot(self: *Parser, rel: macho.relocation_info) !*Relocation {
- const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
- assert(rel_type == .X86_64_RELOC_GOT);
- assert(rel.r_pcrel == 1);
- assert(rel.r_length == 2);
-
- const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
- const target = try self.object.symbolFromReloc(rel);
- const addend = mem.readIntLittle(i32, self.block.code[offset..][0..4]);
-
- var got = try self.object.allocator.create(Got);
- errdefer self.object.allocator.destroy(got);
-
- got.* = .{
- .base = .{
- .@"type" = .got,
- .offset = offset,
- .target = target,
- .block = self.block,
- },
- .addend = addend,
- };
-
- return &got.base;
- }
-
- fn parseTlv(self: *Parser, rel: macho.relocation_info) !*Relocation {
- const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
- assert(rel_type == .X86_64_RELOC_TLV);
- assert(rel.r_pcrel == 1);
- assert(rel.r_length == 2);
-
- const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
- const target = try self.object.symbolFromReloc(rel);
-
- var tlv = try self.object.allocator.create(Tlv);
- errdefer self.object.allocator.destroy(tlv);
-
- tlv.* = .{
- .base = .{
- .@"type" = .tlv,
- .offset = offset,
- .target = target,
- .block = self.block,
- },
- };
-
- return &tlv.base;
- }
-
- fn parseSubtractor(self: *Parser, rel: macho.relocation_info) !void {
- const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
- assert(rel_type == .X86_64_RELOC_SUBTRACTOR);
- assert(rel.r_pcrel == 0);
- assert(self.subtractor == null);
-
- self.subtractor = try self.object.symbolFromReloc(rel);
-
- // Verify SUBTRACTOR is followed by UNSIGNED.
- const next = @intToEnum(macho.reloc_type_x86_64, self.it.peek().r_type);
- if (next != .X86_64_RELOC_UNSIGNED) {
- log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next});
- return error.UnexpectedRelocationType;
- }
- }
-
- fn parseUnsigned(self: *Parser, rel: macho.relocation_info) !*Relocation {
- defer {
- // Reset parser's subtractor state
- self.subtractor = null;
- }
-
- const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
- assert(rel_type == .X86_64_RELOC_UNSIGNED);
- assert(rel.r_pcrel == 0);
-
- const target = try self.object.symbolFromReloc(rel);
- const is_64bit: bool = switch (rel.r_length) {
- 3 => true,
- 2 => false,
- else => unreachable,
- };
- const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
- const addend: i64 = if (is_64bit)
- mem.readIntLittle(i64, self.block.code[offset..][0..8])
- else
- mem.readIntLittle(i32, self.block.code[offset..][0..4]);
-
- var unsigned = try self.object.allocator.create(reloc.Unsigned);
- errdefer self.object.allocator.destroy(unsigned);
-
- unsigned.* = .{
- .base = .{
- .@"type" = .unsigned,
- .offset = offset,
- .target = target,
- .block = self.block,
- },
- .subtractor = self.subtractor,
- .is_64bit = is_64bit,
- .addend = addend,
- };
-
- return &unsigned.base;
- }
-};