authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-06 19:09:49+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-15 18:49:47+02:00
logdbd2eb7c7f9267e8ae508d0995c1d4c5a3b46309
tree9f1749c1ed5cebc47e4813a6f3fceb3e0101ccb9
parent15b85df3dd8a754bc26159ea2202781b748a613e

zld: simplify relocation parsing


6 files changed, 761 insertions(+), 1211 deletions(-)

CMakeLists.txt-2
......@@ -588,8 +588,6 @@ set(ZIG_STAGE2_SOURCES
588588 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
589589 "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig"
590590 "${CMAKE_SOURCE_DIR}/src/link/MachO/reloc.zig"
591 "${CMAKE_SOURCE_DIR}/src/link/MachO/reloc/aarch64.zig"
592 "${CMAKE_SOURCE_DIR}/src/link/MachO/reloc/x86_64.zig"
593591 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
594592 "${CMAKE_SOURCE_DIR}/src/link/tapi.zig"
595593 "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig"
src/link/MachO/Object.zig+10-27
......@@ -408,7 +408,6 @@ const TextBlockParser = struct {
408408
409409 const start_addr = senior_nlist.nlist.n_value - self.section.addr;
410410 const end_addr = if (next_nlist) |n| n.nlist.n_value - self.section.addr else self.section.size;
411 log.warn("{} - {}", .{ start_addr, end_addr });
412411
413412 const code = self.code[start_addr..end_addr];
414413 const size = code.len;
......@@ -430,7 +429,7 @@ const TextBlockParser = struct {
430429 .aliases = alias_only_indices,
431430 .references = std.AutoArrayHashMap(u32, void).init(self.allocator),
432431 .code = try self.allocator.dupe(u8, code),
433 .relocs = std.ArrayList(*Relocation).init(self.allocator),
432 .relocs = std.ArrayList(Relocation).init(self.allocator),
434433 .size = size,
435434 .alignment = self.section.@"align",
436435 };
......@@ -579,7 +578,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
579578 .local_sym_index = local_sym_index,
580579 .references = std.AutoArrayHashMap(u32, void).init(self.allocator),
581580 .code = try self.allocator.dupe(u8, code),
582 .relocs = std.ArrayList(*Relocation).init(self.allocator),
581 .relocs = std.ArrayList(Relocation).init(self.allocator),
583582 .size = sect.size,
584583 .alignment = sect.@"align",
585584 };
......@@ -607,30 +606,14 @@ fn parseRelocs(
607606 var it = reloc.RelocIterator{
608607 .buffer = relocs,
609608 };
610
611 switch (self.arch.?) {
612 .aarch64 => {
613 var parser = reloc.aarch64.Parser{
614 .object = self,
615 .zld = zld,
616 .it = &it,
617 .block = block,
618 .base_addr = base_addr,
619 };
620 try parser.parse();
621 },
622 .x86_64 => {
623 var parser = reloc.x86_64.Parser{
624 .object = self,
625 .zld = zld,
626 .it = &it,
627 .block = block,
628 .base_addr = base_addr,
629 };
630 try parser.parse();
631 },
632 else => unreachable,
633 }
609 var parser = reloc.Parser{
610 .object = self,
611 .zld = zld,
612 .it = &it,
613 .block = block,
614 .base_addr = base_addr,
615 };
616 try parser.parse();
634617}
635618
636619pub fn symbolFromReloc(self: *Object, rel: macho.relocation_info) !*Symbol {
src/link/MachO/Zld.zig+2-60
......@@ -137,7 +137,7 @@ pub const TextBlock = struct {
137137 aliases: ?[]u32 = null,
138138 references: std.AutoArrayHashMap(u32, void),
139139 code: []u8,
140 relocs: std.ArrayList(*Relocation),
140 relocs: std.ArrayList(Relocation),
141141 size: u64,
142142 alignment: u32,
143143 next: ?*TextBlock = null,
......@@ -1604,7 +1604,7 @@ fn resolveSymbols(self: *Zld) !void {
16041604 .local_sym_index = local_sym_index,
16051605 .references = std.AutoArrayHashMap(u32, void).init(self.allocator),
16061606 .code = code,
1607 .relocs = std.ArrayList(*Relocation).init(self.allocator),
1607 .relocs = std.ArrayList(Relocation).init(self.allocator),
16081608 .size = size,
16091609 .alignment = alignment,
16101610 };
......@@ -1871,64 +1871,6 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
18711871 }
18721872}
18731873
1874fn relocTargetAddr(self: *Zld, object: *const Object, target: reloc.Relocation.Target) !u64 {
1875 const target_addr = blk: {
1876 switch (target) {
1877 .symbol => |sym_id| {
1878 const sym = object.symbols.items[sym_id];
1879 switch (sym.payload) {
1880 .regular => |reg| {
1881 log.debug(" | regular '{s}'", .{sym.name});
1882 break :blk reg.address;
1883 },
1884 .proxy => |proxy| {
1885 if (mem.eql(u8, sym.name, "__tlv_bootstrap")) {
1886 log.debug(" | symbol '__tlv_bootstrap'", .{});
1887 const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1888 const tlv = segment.sections.items[self.tlv_section_index.?];
1889 break :blk tlv.addr;
1890 }
1891
1892 log.debug(" | symbol stub '{s}'", .{sym.name});
1893 const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1894 const stubs = segment.sections.items[self.stubs_section_index.?];
1895 const stubs_index = sym.stubs_index orelse {
1896 if (proxy.bind_info.items.len > 0) {
1897 break :blk 0; // Dynamically bound by dyld.
1898 }
1899 log.err(
1900 "expected stubs index or dynamic bind address when relocating symbol '{s}'",
1901 .{sym.name},
1902 );
1903 log.err("this is an internal linker error", .{});
1904 return error.FailedToResolveRelocationTarget;
1905 };
1906 break :blk stubs.addr + stubs_index * stubs.reserved2;
1907 },
1908 else => {
1909 log.err("failed to resolve symbol '{s}' as a relocation target", .{sym.name});
1910 log.err("this is an internal linker error", .{});
1911 return error.FailedToResolveRelocationTarget;
1912 },
1913 }
1914 },
1915 .section => |sect_id| {
1916 log.debug(" | section offset", .{});
1917 const source_sect = object.sections.items[sect_id];
1918 log.debug(" | section '{s},{s}'", .{
1919 segmentName(source_sect.inner),
1920 sectionName(source_sect.inner),
1921 });
1922 const target_map = source_sect.target_map orelse unreachable;
1923 const target_seg = self.load_commands.items[target_map.segment_id].Segment;
1924 const target_sect = target_seg.sections.items[target_map.section_id];
1925 break :blk target_sect.addr + target_map.offset;
1926 },
1927 }
1928 };
1929 return target_addr;
1930}
1931
19321874fn populateMetadata(self: *Zld) !void {
19331875 if (self.pagezero_segment_cmd_index == null) {
19341876 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
src/link/MachO/reloc.zig+749-119
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const aarch64 = @import("../../codegen/aarch64.zig");
23const assert = std.debug.assert;
34const log = std.log.scoped(.reloc);
45const macho = std.macho;
......@@ -6,141 +7,431 @@ const math = std.math;
67const mem = std.mem;
78const meta = std.meta;
89
9pub const aarch64 = @import("reloc/aarch64.zig");
10pub const x86_64 = @import("reloc/x86_64.zig");
11
1210const Allocator = mem.Allocator;
11const Arch = std.Target.Cpu.Arch;
12const Object = @import("Object.zig");
1313const Symbol = @import("Symbol.zig");
14const TextBlock = @import("Zld.zig").TextBlock;
14const TextBlock = Zld.TextBlock;
15const Zld = @import("Zld.zig");
1516
1617pub const Relocation = struct {
17 @"type": Type,
18 /// Offset within the `block`s code buffer.
19 /// Note relocation size can be inferred by relocation's kind.
1820 offset: u32,
21
22 /// Parent block containing this relocation.
1923 block: *TextBlock,
24
25 /// Target symbol: either a regular or a proxy.
2026 target: *Symbol,
2127
22 pub fn cast(base: *Relocation, comptime T: type) ?*T {
23 if (base.@"type" != T.base_type)
24 return null;
28 payload: union(enum) {
29 unsigned: Unsigned,
30 branch: Branch,
31 page: Page,
32 page_off: PageOff,
33 pointer_to_got: PointerToGot,
34 signed: Signed,
35 load: Load,
36 },
2537
26 return @fieldParentPtr(T, "base", base);
27 }
38 pub const Unsigned = struct {
39 subtractor: ?*Symbol = null,
40
41 /// Addend embedded directly in the relocation slot
42 addend: i64,
43
44 /// Extracted from r_length:
45 /// => 3 implies true
46 /// => 2 implies false
47 /// => * is unreachable
48 is_64bit: bool,
49
50 pub fn resolve(self: Unsigned, base: Relocation, source_addr: u64, target_addr: u64) !void {
51 // const addend = if (unsigned.base.target == .section)
52 // unsigned.addend - @intCast(i64, args.source_target_sect_addr.?)
53 // else
54 // unsigned.addend;
55
56 // const result = if (args.subtractor) |subtractor|
57 // @intCast(i64, args.target_addr) - @intCast(i64, subtractor) + addend
58 // else
59 // @intCast(i64, args.target_addr) + addend;
60
61 // log.debug(" | calculated addend 0x{x}", .{addend});
62 // log.debug(" | calculated unsigned value 0x{x}", .{result});
63
64 // if (unsigned.is_64bit) {
65 // mem.writeIntLittle(
66 // u64,
67 // unsigned.base.code[0..8],
68 // @bitCast(u64, result),
69 // );
70 // } else {
71 // mem.writeIntLittle(
72 // u32,
73 // unsigned.base.code[0..4],
74 // @truncate(u32, @bitCast(u64, result)),
75 // );
76 // }
77 }
2878
29 // pub fn resolve(base: *Relocation) !void {
30 // return switch (base.@"type") {
31 // .unsigned => @fieldParentPtr(Unsigned, "base", base).resolve(),
32 // .branch_aarch64 => @fieldParentPtr(aarch64.Branch, "base", base).resolve(),
33 // .page => @fieldParentPtr(aarch64.Page, "base", base).resolve(),
34 // .page_off => @fieldParentPtr(aarch64.PageOff, "base", base).resolve(),
35 // .got_page => @fieldParentPtr(aarch64.GotPage, "base", base).resolve(),
36 // .got_page_off => @fieldParentPtr(aarch64.GotPageOff, "base", base).resolve(),
37 // .pointer_to_got => @fieldParentPtr(aarch64.PointerToGot, "base", base).resolve(),
38 // .tlvp_page => @fieldParentPtr(aarch64.TlvpPage, "base", base).resolve(),
39 // .tlvp_page_off => @fieldParentPtr(aarch64.TlvpPageOff, "base", base).resolve(),
40 // .branch_x86_64 => @fieldParentPtr(x86_64.Branch, "base", base).resolve(),
41 // .signed => @fieldParentPtr(x86_64.Signed, "base", base).resolve(),
42 // .got_load => @fieldParentPtr(x86_64.GotLoad, "base", base).resolve(),
43 // .got => @fieldParentPtr(x86_64.Got, "base", base).resolve(),
44 // .tlv => @fieldParentPtr(x86_64.Tlv, "base", base).resolve(),
45 // };
46 // }
47
48 pub const Type = enum {
49 branch_aarch64,
50 unsigned,
51 page,
52 page_off,
53 got_page,
54 got_page_off,
55 tlvp_page,
56 pointer_to_got,
57 tlvp_page_off,
58 branch_x86_64,
59 signed,
60 got_load,
61 got,
62 tlv,
79 pub fn format(self: Unsigned, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
80 _ = fmt;
81 _ = options;
82 try std.fmt.format(writer, "Unsigned {{ ", .{});
83 if (self.subtractor) |sub| {
84 try std.fmt.format(writer, ".subtractor = {}, ", .{sub});
85 }
86 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
87 const length: usize = if (self.is_64bit) 8 else 4;
88 try std.fmt.format(writer, ".length = {}, ", .{length});
89 try std.fmt.format(writer, "}}", .{});
90 }
6391 };
6492
65 pub fn format(base: *const Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
66 try std.fmt.format(writer, "Relocation {{ ", .{});
67 try std.fmt.format(writer, ".type = {s}, ", .{base.@"type"});
68 try std.fmt.format(writer, ".offset = {}, ", .{base.offset});
69 try std.fmt.format(writer, ".block = {}", .{base.block.local_sym_index});
70 try std.fmt.format(writer, ".target = {}, ", .{base.target});
71
72 try switch (base.@"type") {
73 .unsigned => @fieldParentPtr(Unsigned, "base", base).format(fmt, options, writer),
74 .branch_aarch64 => @fieldParentPtr(aarch64.Branch, "base", base).format(fmt, options, writer),
75 .page => @fieldParentPtr(aarch64.Page, "base", base).format(fmt, options, writer),
76 .page_off => @fieldParentPtr(aarch64.PageOff, "base", base).format(fmt, options, writer),
77 .got_page => @fieldParentPtr(aarch64.GotPage, "base", base).format(fmt, options, writer),
78 .got_page_off => @fieldParentPtr(aarch64.GotPageOff, "base", base).format(fmt, options, writer),
79 .pointer_to_got => @fieldParentPtr(aarch64.PointerToGot, "base", base).format(fmt, options, writer),
80 .tlvp_page => @fieldParentPtr(aarch64.TlvpPage, "base", base).format(fmt, options, writer),
81 .tlvp_page_off => @fieldParentPtr(aarch64.TlvpPageOff, "base", base).format(fmt, options, writer),
82 .branch_x86_64 => @fieldParentPtr(x86_64.Branch, "base", base).format(fmt, options, writer),
83 .signed => @fieldParentPtr(x86_64.Signed, "base", base).format(fmt, options, writer),
84 .got_load => @fieldParentPtr(x86_64.GotLoad, "base", base).format(fmt, options, writer),
85 .got => @fieldParentPtr(x86_64.Got, "base", base).format(fmt, options, writer),
86 .tlv => @fieldParentPtr(x86_64.Tlv, "base", base).format(fmt, options, writer),
93 pub const Branch = struct {
94 arch: Arch,
95
96 pub fn resolve(self: Branch, base: Relocation, source_addr: u64, target_addr: u64) !void {
97 switch (arch) {
98 .aarch64 => {
99 const displacement = try math.cast(i28, @intCast(i64, target_addr) - @intCast(i64, source_addr));
100 var inst = aarch64.Instruction{
101 .unconditional_branch_immediate = mem.bytesToValue(
102 meta.TagPayload(
103 aarch.Instruction,
104 aarch64.Instruction.unconditional_branch_immediate,
105 ),
106 base.block.code[base.offset..][0..4],
107 ),
108 };
109 inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));
110 mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], inst.toU32());
111 },
112 .x86_64 => {
113 const displacement = try math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4);
114 mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], @bitCast(u32, displacement));
115 },
116 else => return error.UnsupportedCpuArchitecture,
117 }
118 }
119
120 pub fn format(self: Branch, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
121 _ = fmt;
122 _ = options;
123 try std.fmt.format(writer, "Branch {{}}", .{});
124 }
125 };
126
127 pub const Page = struct {
128 kind: enum {
129 page,
130 got,
131 tlvp,
132 },
133 addend: ?u32 = null,
134
135 pub fn resolve(self: Page, base: Relocation, source_addr: u64, target_addr: u64) !void {
136 const actual_target_addr = if (self.addend) |addend| target_addr + addend else target_addr;
137 const source_page = @intCast(i32, source_addr >> 12);
138 const target_page = @intCast(i32, actual_target_addr >> 12);
139 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
140
141 var inst = aarch64.Instruction{
142 .pc_relative_address = mem.bytesToValue(
143 meta.TagPayload(
144 aarch64.Instruction,
145 aarch64.Instruction.pc_relative_address,
146 ),
147 base.block.code[base.offset..][0..4],
148 ),
149 };
150 inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
151 inst.pc_relative_address.immlo = @truncate(u2, pages);
152
153 mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], inst.toU32());
154 }
155
156 pub fn format(self: Page, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
157 _ = fmt;
158 _ = options;
159 try std.fmt.format(writer, "Page {{ ", .{});
160 switch (self.kind) {
161 .page => {},
162 .got => {
163 try std.fmt.format(writer, ".got, ", .{});
164 },
165 .tlvp => {
166 try std.fmt.format(writer, ".tlvp", .{});
167 },
168 }
169 if (self.addend) |add| {
170 try std.fmt.format(writer, ".addend = {}, ", .{add});
171 }
172 try std.fmt.format(writer, "}}", .{});
173 }
174 };
175
176 pub const PageOff = struct {
177 kind: enum {
178 page,
179 got,
180 tlvp,
181 },
182 addend: ?u32 = null,
183 op_kind: ?OpKind = null,
184
185 pub const OpKind = enum {
186 arithmetic,
187 load,
87188 };
88189
89 try std.fmt.format(writer, "}}", .{});
190 pub fn resolve(self: PageOff, base: Relocation, source_addr: u64, target_addr: u64) !void {
191 switch (self.kind) {
192 .page => {
193 // const target_addr = if (page_off.addend) |addend| args.target_addr + addend else args.target_addr;
194 // const narrowed = @truncate(u12, target_addr);
195
196 // log.debug(" | narrowed address within the page 0x{x}", .{narrowed});
197 // log.debug(" | {s} opcode", .{page_off.op_kind});
198
199 // var inst = page_off.inst;
200 // if (page_off.op_kind == .arithmetic) {
201 // inst.add_subtract_immediate.imm12 = narrowed;
202 // } else {
203 // const offset: u12 = blk: {
204 // if (inst.load_store_register.size == 0) {
205 // if (inst.load_store_register.v == 1) {
206 // // 128-bit SIMD is scaled by 16.
207 // break :blk try math.divExact(u12, narrowed, 16);
208 // }
209 // // Otherwise, 8-bit SIMD or ldrb.
210 // break :blk narrowed;
211 // } else {
212 // const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size);
213 // break :blk try math.divExact(u12, narrowed, denom);
214 // }
215 // };
216 // inst.load_store_register.offset = offset;
217 // }
218
219 // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32());
220
221 },
222 .got => {
223 // const narrowed = @truncate(u12, args.target_addr);
224
225 // log.debug(" | narrowed address within the page 0x{x}", .{narrowed});
226
227 // var inst = page_off.inst;
228 // const offset = try math.divExact(u12, narrowed, 8);
229 // inst.load_store_register.offset = offset;
230
231 // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32());
232 },
233 .tlvp => {
234
235 // const narrowed = @truncate(u12, args.target_addr);
236
237 // log.debug(" | narrowed address within the page 0x{x}", .{narrowed});
238
239 // var inst = page_off.inst;
240 // inst.add_subtract_immediate.imm12 = narrowed;
241
242 // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32());
243 },
244 }
245 }
246
247 pub fn format(self: PageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
248 _ = fmt;
249 _ = options;
250 try std.fmt.format(writer, "PageOff {{ ", .{});
251 switch (self.kind) {
252 .page => {},
253 .got => {
254 try std.fmt.format(writer, ".got, ", .{});
255 },
256 .tlvp => {
257 try std.fmt.format(writer, ".tlvp, ", .{});
258 },
259 }
260 if (self.addend) |add| {
261 try std.fmt.format(writer, ".addend = {}, ", .{add});
262 }
263 if (self.op_kind) |op| {
264 try std.fmt.format(writer, ".op_kind = {s}, ", .{op});
265 }
266 try std.fmt.format(writer, "}}", .{});
267 }
268 };
269
270 pub const PointerToGot = struct {
271 pub fn resolve(self: PointerToGot, base: Relocation, source_addr: u64, target_addr: u64) !void {
272 const result = try math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr));
273 mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], @bitCast(u32, result));
274 }
275
276 pub fn format(self: PointerToGot, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
277 _ = fmt;
278 _ = options;
279 try std.fmt.format(writer, "PointerToGot {{}}", .{});
280 }
281 };
282
283 pub const Signed = struct {
284 addend: i32,
285 correction: i4,
286
287 pub fn resolve(self: Signed, base: Relocation, source_addr: u64, target_addr: u64) !void {
288 // const target_addr = target_addr: {
289 // if (signed.base.target == .section) {
290 // const source_target = @intCast(i64, args.source_source_sect_addr.?) + @intCast(i64, signed.base.offset) + signed.addend + 4;
291 // const source_disp = source_target - @intCast(i64, args.source_target_sect_addr.?);
292 // break :target_addr @intCast(i64, args.target_addr) + source_disp;
293 // }
294 // break :target_addr @intCast(i64, args.target_addr) + signed.addend;
295 // };
296 // const displacement = try math.cast(
297 // i32,
298 // target_addr - @intCast(i64, args.source_addr) - signed.correction - 4,
299 // );
300
301 // log.debug(" | addend 0x{x}", .{signed.addend});
302 // log.debug(" | correction 0x{x}", .{signed.correction});
303 // log.debug(" | displacement 0x{x}", .{displacement});
304
305 // mem.writeIntLittle(u32, signed.base.code[0..4], @bitCast(u32, displacement));
306 }
307
308 pub fn format(self: Signed, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
309 _ = fmt;
310 _ = options;
311 try std.fmt.format(writer, "Signed {{ ", .{});
312 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
313 try std.fmt.format(writer, ".correction = {}, ", .{self.correction});
314 try std.fmt.format(writer, "}}", .{});
315 }
316 };
317
318 pub const Load = struct {
319 kind: enum {
320 got,
321 tlvp,
322 },
323 addend: ?i32 = null,
324
325 pub fn resolve(self: Load, base: Relocation, source_addr: u64, target_addr: u64) !void {
326 if (self.kind == .tlvp) {
327 // We need to rewrite the opcode from movq to leaq.
328 base.block.code[base.offset - 2] = 0x8d;
329 }
330 const addend = if (self.addend) |addend| addend else 0;
331 const displacement = try math.cast(
332 i32,
333 @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + addend,
334 );
335 mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], @bitCast(u32, displacement));
336 }
337
338 pub fn format(self: Load, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
339 _ = fmt;
340 _ = options;
341 try std.fmt.format(writer, "Load {{ ", .{});
342 try std.fmt.format(writer, "{s}, ", .{self.kind});
343 if (self.addend) |addend| {
344 try std.fmt.format(writer, ".addend = {}, ", .{addend});
345 }
346 try std.fmt.format(writer, "}}", .{});
347 }
348 };
349
350 pub fn resolve(self: Relocation, zld: *Zld) !void {
351 const source_addr = blk: {
352 const sym = zld.locals.items[self.block.local_sym_index];
353 break :blk sym.payload.regular.address;
354 };
355 const target_addr = blk: {
356 const is_via_got = inner: {
357 switch (self.payload) {
358 .pointer_to_got => break :inner true,
359 .page => |page| page.kind == .got,
360 .page_off => |page_off| page_off == .got,
361 .load => {},
362 else => break :inner false,
363 }
364 };
365
366 if (is_via_got) {
367 const dc_seg = zld.load_commands.items[zld.data_const_segment_cmd_index.?].Segment;
368 const got = dc_seg.sections.items[zld.got_section_index.?];
369 const got_index = self.target.got_index orelse {
370 log.err("expected GOT entry for symbol '{s}'", .{self.target.name});
371 log.err(" this is an internal linker error", .{});
372 return error.FailedToResolveRelocationTarget;
373 };
374 break :blk got.addr + got_index * @sizeOf(u64);
375 }
376
377 switch (self.target.payload) {
378 .regular => |reg| break :blk reg.address,
379 .proxy => |proxy| {
380 if (mem.eql(u8, self.target.name, "__tlv_bootstrap")) {
381 const segment = zld.load_commands.items[zld.data_segment_cmd_index.?].Segment;
382 const tlv = segment.sections.items[zld.tlv_section_index.?];
383 break :blk tlv.addr;
384 }
385
386 const segment = zld.load_commands.items[zld.text_segment_cmd_index.?].Segment;
387 const stubs = segment.sections.items[zld.stubs_section_index.?];
388 const stubs_index = self.target.stubs_index orelse {
389 if (proxy.bind_info.items.len > 0) {
390 break :blk 0; // Dynamically bound by dyld.
391 }
392 log.err("expected stubs index or dynamic bind address for symbol '{s}'", .{
393 self.target.name,
394 });
395 log.err(" this is an internal linker error", .{});
396 return error.FailedToResolveRelocationTarget;
397 };
398 break :blk stubs.addr + stubs_index * stubs.reserved2;
399 },
400 else => {
401 log.err("failed to resolve symbol '{s}' as a relocation target", .{self.target.name});
402 log.err(" this is an internal linker error", .{});
403 return error.FailedToResolveRelocationTarget;
404 },
405 }
406 };
407 switch (self.payload) {
408 .unsigned => |unsigned| try unsigned.resolve(self, source_addr, target_addr),
409 .branch => |branch| try branch.resolve(self, source_addr, target_addr),
410 .page => |page| try page.resolve(self, source_addr, target_addr),
411 .page_off => |page_off| try page_off.resolve(self, source_addr, target_addr),
412 .pointer_to_got => |pointer_to_got| try pointer_to_got.resolve(self, source_addr, target_addr),
413 .signed => |signed| try signed.resolve(self, source_addr, target_addr),
414 .load => |load| try load.resolve(self, source_addr, target_addr),
415 }
90416 }
91};
92417
93pub const Unsigned = struct {
94 base: Relocation,
95 subtractor: ?*Symbol = null,
96 /// Addend embedded directly in the relocation slot
97 addend: i64,
98 /// Extracted from r_length:
99 /// => 3 implies true
100 /// => 2 implies false
101 /// => * is unreachable
102 is_64bit: bool,
103
104 pub const base_type: Relocation.Type = .unsigned;
105
106 // pub fn resolve(unsigned: Unsigned) !void {
107 // const addend = if (unsigned.base.target == .section)
108 // unsigned.addend - @intCast(i64, args.source_target_sect_addr.?)
109 // else
110 // unsigned.addend;
111
112 // const result = if (args.subtractor) |subtractor|
113 // @intCast(i64, args.target_addr) - @intCast(i64, subtractor) + addend
114 // else
115 // @intCast(i64, args.target_addr) + addend;
116
117 // log.debug(" | calculated addend 0x{x}", .{addend});
118 // log.debug(" | calculated unsigned value 0x{x}", .{result});
119
120 // if (unsigned.is_64bit) {
121 // mem.writeIntLittle(
122 // u64,
123 // unsigned.base.code[0..8],
124 // @bitCast(u64, result),
125 // );
126 // } else {
127 // mem.writeIntLittle(
128 // u32,
129 // unsigned.base.code[0..4],
130 // @truncate(u32, @bitCast(u64, result)),
131 // );
132 // }
133 // }
134
135 pub fn format(self: Unsigned, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
136 _ = fmt;
137 _ = options;
138 if (self.subtractor) |sub| {
139 try std.fmt.format(writer, ".subtractor = {}, ", .{sub});
140 }
141 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
142 const length: usize = if (self.is_64bit) 8 else 4;
143 try std.fmt.format(writer, ".length = {}, ", .{length});
418 pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
419 try std.fmt.format(writer, "Relocation {{ ", .{});
420 try std.fmt.format(writer, ".offset = {}, ", .{self.offset});
421 try std.fmt.format(writer, ".block = {}", .{self.block.local_sym_index});
422 try std.fmt.format(writer, ".target = {}, ", .{self.target});
423
424 switch (self.payload) {
425 .unsigned => |unsigned| try unsigned.format(fmt, options, writer),
426 .branch => |branch| try branch.format(fmt, options, writer),
427 .page => |page| try page.format(fmt, options, writer),
428 .page_off => |page_off| try page_off.format(fmt, options, writer),
429 .pointer_to_got => |pointer_to_got| try pointer_to_got.format(fmt, options, writer),
430 .signed => |signed| try signed.format(fmt, options, writer),
431 .load => |load| try load.format(fmt, options, writer),
432 }
433
434 try std.fmt.format(writer, "}}", .{});
144435 }
145436};
146437
......@@ -161,3 +452,342 @@ pub const RelocIterator = struct {
161452 return self.buffer[@intCast(u32, self.index + 1)];
162453 }
163454};
455
456pub const Parser = struct {
457 object: *Object,
458 zld: *Zld,
459 it: *RelocIterator,
460 block: *TextBlock,
461
462 /// Base address of the parsed text block in the source section.
463 base_addr: u64,
464
465 /// Used only when targeting aarch64
466 addend: ?u32 = null,
467
468 /// Parsed subtractor symbol from _RELOC_SUBTRACTOR reloc type.
469 subtractor: ?*Symbol = null,
470
471 pub fn parse(self: *Parser) !void {
472 while (self.it.next()) |rel| {
473 const out_rel = blk: {
474 switch (self.object.arch.?) {
475 .aarch64 => {
476 const out_rel = switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
477 .ARM64_RELOC_BRANCH26 => try self.parseBranch(rel),
478 .ARM64_RELOC_SUBTRACTOR => {
479 // Subtractor is not a relocation with effect on the TextBlock, so
480 // parse it and carry on.
481 try self.parseSubtractor(rel);
482
483 // Verify SUBTRACTOR is followed by UNSIGNED.
484 const next = @intToEnum(macho.reloc_type_arm64, self.it.peek().r_type);
485 if (next != .ARM64_RELOC_UNSIGNED) {
486 log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next});
487 return error.UnexpectedRelocationType;
488 }
489 continue;
490 },
491 .ARM64_RELOC_UNSIGNED => try self.parseUnsigned(rel),
492 .ARM64_RELOC_ADDEND => {
493 // Addend is not a relocation with effect on the TextBlock, so
494 // parse it and carry on.
495 try self.parseAddend(rel);
496
497 // Verify ADDEND is followed by a load.
498 const next = @intToEnum(macho.reloc_type_arm64, self.it.peek().r_type);
499 switch (next) {
500 .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {},
501 else => {
502 log.err("unexpected relocation type: expected PAGE21 or PAGEOFF12, found {s}", .{next});
503 return error.UnexpectedRelocationType;
504 },
505 }
506 continue;
507 },
508 .ARM64_RELOC_PAGE21,
509 .ARM64_RELOC_GOT_LOAD_PAGE21,
510 .ARM64_RELOC_TLVP_LOAD_PAGE21,
511 => try self.parsePage(rel),
512 .ARM64_RELOC_PAGEOFF12,
513 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
514 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
515 => try self.parsePageOff(rel),
516 .ARM64_RELOC_POINTER_TO_GOT => try self.parsePointerToGot(rel),
517 };
518 break :blk out_rel;
519 },
520 .x86_64 => {
521 const out_rel = switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
522 .X86_64_RELOC_BRANCH => try self.parseBranch(rel),
523 .X86_64_RELOC_SUBTRACTOR => {
524 // Subtractor is not a relocation with effect on the TextBlock, so
525 // parse it and carry on.
526 try self.parseSubtractor(rel);
527
528 // Verify SUBTRACTOR is followed by UNSIGNED.
529 const next = @intToEnum(macho.reloc_type_x86_64, self.it.peek().r_type);
530 if (next != .X86_64_RELOC_UNSIGNED) {
531 log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next});
532 return error.UnexpectedRelocationType;
533 }
534 continue;
535 },
536 .X86_64_RELOC_UNSIGNED => try self.parseUnsigned(rel),
537 .X86_64_RELOC_SIGNED,
538 .X86_64_RELOC_SIGNED_1,
539 .X86_64_RELOC_SIGNED_2,
540 .X86_64_RELOC_SIGNED_4,
541 => try self.parseSigned(rel),
542 .X86_64_RELOC_GOT_LOAD,
543 .X86_64_RELOC_GOT,
544 .X86_64_RELOC_TLV,
545 => try self.parseLoad(rel),
546 };
547 break :blk out_rel;
548 },
549 else => unreachable,
550 }
551 };
552 try self.block.relocs.append(out_rel);
553
554 if (out_rel.target.payload == .regular) {
555 try self.block.references.put(out_rel.target.payload.regular.local_sym_index, {});
556 }
557
558 const is_via_got = switch (out_rel.payload) {
559 .pointer_to_got => true,
560 .load => |load| load.kind == .got,
561 .page => |page| page.kind == .got,
562 .page_off => |page_off| page_off.kind == .got,
563 else => false,
564 };
565
566 if (is_via_got and out_rel.target.got_index == null) {
567 const index = @intCast(u32, self.zld.got_entries.items.len);
568 out_rel.target.got_index = index;
569 try self.zld.got_entries.append(self.zld.allocator, out_rel.target);
570 log.debug("adding GOT entry for symbol {s} at index {}", .{ out_rel.target.name, index });
571 }
572
573 if (out_rel.payload == .branch) {
574 const sym = out_rel.target;
575
576 if (sym.stubs_index != null) continue;
577 if (sym.payload != .proxy) continue;
578
579 const index = @intCast(u32, self.zld.stubs.items.len);
580 sym.stubs_index = index;
581 try self.zld.stubs.append(self.zld.allocator, sym);
582
583 log.debug("adding stub entry for symbol {s} at index {}", .{ sym.name, index });
584 }
585 }
586 }
587
588 fn parseBaseRelInfo(self: *Parser, rel: macho.relocation_info) !Relocation {
589 const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
590 const target = try self.object.symbolFromReloc(rel);
591 return Relocation{
592 .offset = offset,
593 .target = target,
594 .block = self.block,
595 .payload = undefined,
596 };
597 }
598
599 fn parseUnsigned(self: *Parser, rel: macho.relocation_info) !Relocation {
600 defer {
601 // Reset parser's subtractor state
602 self.subtractor = null;
603 }
604
605 assert(rel.r_pcrel == 0);
606
607 var parsed = try self.parseBaseRelInfo(rel);
608 const is_64bit: bool = switch (rel.r_length) {
609 3 => true,
610 2 => false,
611 else => unreachable,
612 };
613 const addend: i64 = if (is_64bit)
614 mem.readIntLittle(i64, self.block.code[parsed.offset..][0..8])
615 else
616 mem.readIntLittle(i32, self.block.code[parsed.offset..][0..4]);
617
618 parsed.payload = .{
619 .unsigned = .{
620 .subtractor = self.subtractor,
621 .is_64bit = is_64bit,
622 .addend = addend,
623 },
624 };
625
626 return parsed;
627 }
628
629 fn parseBranch(self: *Parser, rel: macho.relocation_info) !Relocation {
630 assert(rel.r_pcrel == 1);
631 assert(rel.r_length == 2);
632
633 var parsed = try self.parseBaseRelInfo(rel);
634 parsed.payload = .{
635 .branch = .{
636 .arch = self.object.arch.?,
637 },
638 };
639 return parsed;
640 }
641
642 fn parsePage(self: *Parser, rel: macho.relocation_info) !Relocation {
643 assert(rel.r_pcrel == 1);
644 assert(rel.r_length == 2);
645
646 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
647
648 defer if (rel_type == .ARM64_RELOC_PAGE21) {
649 // Reset parser's addend state
650 self.addend = null;
651 };
652
653 const addend = if (rel_type == .ARM64_RELOC_PAGE21)
654 self.addend
655 else
656 null;
657
658 var parsed = try self.parseBaseRelInfo(rel);
659 parsed.payload = .{
660 .page = .{
661 .kind = switch (rel_type) {
662 .ARM64_RELOC_PAGE21 => .page,
663 .ARM64_RELOC_GOT_LOAD_PAGE21 => .got,
664 .ARM64_RELOC_TLVP_LOAD_PAGE21 => .tlvp,
665 else => unreachable,
666 },
667 .addend = addend,
668 },
669 };
670 return parsed;
671 }
672
673 fn parsePageOff(self: *Parser, rel: macho.relocation_info) !Relocation {
674 assert(rel.r_pcrel == 0);
675 assert(rel.r_length == 2);
676
677 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
678
679 defer if (rel_type == .ARM64_RELOC_PAGEOFF12) {
680 // Reset parser's addend state
681 self.addend = null;
682 };
683
684 const addend = if (rel_type == .ARM64_RELOC_PAGEOFF12)
685 self.addend
686 else
687 null;
688
689 var parsed = try self.parseBaseRelInfo(rel);
690 const op_kind: ?Relocation.PageOff.OpKind = blk: {
691 if (rel_type != .ARM64_RELOC_PAGEOFF12) break :blk null;
692 const op_kind: Relocation.PageOff.OpKind = if (isArithmeticOp(self.block.code[parsed.offset..][0..4]))
693 .arithmetic
694 else
695 .load;
696 break :blk op_kind;
697 };
698
699 parsed.payload = .{
700 .page_off = .{
701 .kind = switch (rel_type) {
702 .ARM64_RELOC_PAGEOFF12 => .page,
703 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => .got,
704 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => .tlvp,
705 else => unreachable,
706 },
707 .addend = addend,
708 .op_kind = op_kind,
709 },
710 };
711 return parsed;
712 }
713
714 fn parsePointerToGot(self: *Parser, rel: macho.relocation_info) !Relocation {
715 assert(rel.r_pcrel == 1);
716 assert(rel.r_length == 2);
717
718 var parsed = try self.parseBaseRelInfo(rel);
719 parsed.payload = .{
720 .pointer_to_got = .{},
721 };
722 return parsed;
723 }
724
725 fn parseAddend(self: *Parser, rel: macho.relocation_info) !void {
726 assert(rel.r_pcrel == 0);
727 assert(rel.r_extern == 0);
728 assert(self.addend == null);
729
730 self.addend = rel.r_symbolnum;
731 }
732
733 fn parseSigned(self: *Parser, rel: macho.relocation_info) !Relocation {
734 assert(rel.r_pcrel == 1);
735 assert(rel.r_length == 2);
736
737 var parsed = try self.parseBaseRelInfo(rel);
738 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
739 const correction: i4 = switch (rel_type) {
740 .X86_64_RELOC_SIGNED => 0,
741 .X86_64_RELOC_SIGNED_1 => 1,
742 .X86_64_RELOC_SIGNED_2 => 2,
743 .X86_64_RELOC_SIGNED_4 => 4,
744 else => unreachable,
745 };
746 const addend = mem.readIntLittle(i32, self.block.code[parsed.offset..][0..4]) + correction;
747
748 parsed.payload = .{
749 .signed = .{
750 .correction = correction,
751 .addend = addend,
752 },
753 };
754
755 return parsed;
756 }
757
758 fn parseSubtractor(self: *Parser, rel: macho.relocation_info) !void {
759 assert(rel.r_pcrel == 0);
760 assert(self.subtractor == null);
761
762 self.subtractor = try self.object.symbolFromReloc(rel);
763 }
764
765 fn parseLoad(self: *Parser, rel: macho.relocation_info) !Relocation {
766 assert(rel.r_pcrel == 1);
767 assert(rel.r_length == 2);
768
769 var parsed = try self.parseBaseRelInfo(rel);
770 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
771 const addend = if (rel_type == .X86_64_RELOC_GOT)
772 mem.readIntLittle(i32, self.block.code[parsed.offset..][0..4])
773 else
774 null;
775
776 parsed.payload = .{
777 .load = .{
778 .kind = switch (rel_type) {
779 .X86_64_RELOC_GOT_LOAD, .X86_64_RELOC_GOT => .got,
780 .X86_64_RELOC_TLV => .tlvp,
781 else => unreachable,
782 },
783 .addend = addend,
784 },
785 };
786 return parsed;
787 }
788};
789
790inline fn isArithmeticOp(inst: *const [4]u8) bool {
791 const group_decode = @truncate(u5, inst[3]);
792 return ((group_decode >> 2) == 4);
793}
src/link/MachO/reloc/aarch64.zig deleted-618
......@@ -1,618 +0,0 @@
1const std = @import("std");
2const aarch64 = @import("../../../codegen/aarch64.zig");
3const assert = std.debug.assert;
4const log = std.log.scoped(.reloc);
5const macho = std.macho;
6const math = std.math;
7const mem = std.mem;
8const meta = std.meta;
9const reloc = @import("../reloc.zig");
10
11const Allocator = mem.Allocator;
12const Object = @import("../Object.zig");
13const Relocation = reloc.Relocation;
14const Symbol = @import("../Symbol.zig");
15const TextBlock = Zld.TextBlock;
16const Zld = @import("../Zld.zig");
17
18pub const Branch = struct {
19 base: Relocation,
20 /// Always .UnconditionalBranchImmediate
21 // inst: aarch64.Instruction,
22
23 pub const base_type: Relocation.Type = .branch_aarch64;
24
25 // pub fn resolve(branch: Branch, args: Relocation.ResolveArgs) !void {
26 // const displacement = try math.cast(i28, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr));
27
28 // log.debug(" | displacement 0x{x}", .{displacement});
29
30 // var inst = branch.inst;
31 // inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));
32 // mem.writeIntLittle(u32, branch.base.code[0..4], inst.toU32());
33 // }
34
35 pub fn format(self: Branch, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
36 _ = self;
37 _ = fmt;
38 _ = options;
39 _ = writer;
40 }
41};
42
43pub const Page = struct {
44 base: Relocation,
45 addend: ?u32 = null,
46 /// Always .PCRelativeAddress
47 // inst: aarch64.Instruction,
48
49 pub const base_type: Relocation.Type = .page;
50
51 // pub fn resolve(page: Page, args: Relocation.ResolveArgs) !void {
52 // const target_addr = if (page.addend) |addend| args.target_addr + addend else args.target_addr;
53 // const source_page = @intCast(i32, args.source_addr >> 12);
54 // const target_page = @intCast(i32, target_addr >> 12);
55 // const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
56
57 // log.debug(" | calculated addend 0x{x}", .{page.addend});
58 // log.debug(" | moving by {} pages", .{pages});
59
60 // var inst = page.inst;
61 // inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
62 // inst.pc_relative_address.immlo = @truncate(u2, pages);
63
64 // mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32());
65 // }
66
67 pub fn format(self: Page, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
68 _ = fmt;
69 _ = options;
70 if (self.addend) |addend| {
71 try std.fmt.format(writer, ".addend = {}, ", .{addend});
72 }
73 }
74};
75
76pub const PageOff = struct {
77 base: Relocation,
78 addend: ?u32 = null,
79 op_kind: OpKind,
80 // inst: aarch64.Instruction,
81
82 pub const base_type: Relocation.Type = .page_off;
83
84 pub const OpKind = enum {
85 arithmetic,
86 load_store,
87 };
88
89 // pub fn resolve(page_off: PageOff, args: Relocation.ResolveArgs) !void {
90 // const target_addr = if (page_off.addend) |addend| args.target_addr + addend else args.target_addr;
91 // const narrowed = @truncate(u12, target_addr);
92
93 // log.debug(" | narrowed address within the page 0x{x}", .{narrowed});
94 // log.debug(" | {s} opcode", .{page_off.op_kind});
95
96 // var inst = page_off.inst;
97 // if (page_off.op_kind == .arithmetic) {
98 // inst.add_subtract_immediate.imm12 = narrowed;
99 // } else {
100 // const offset: u12 = blk: {
101 // if (inst.load_store_register.size == 0) {
102 // if (inst.load_store_register.v == 1) {
103 // // 128-bit SIMD is scaled by 16.
104 // break :blk try math.divExact(u12, narrowed, 16);
105 // }
106 // // Otherwise, 8-bit SIMD or ldrb.
107 // break :blk narrowed;
108 // } else {
109 // const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size);
110 // break :blk try math.divExact(u12, narrowed, denom);
111 // }
112 // };
113 // inst.load_store_register.offset = offset;
114 // }
115
116 // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32());
117 // }
118
119 pub fn format(self: PageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
120 _ = fmt;
121 _ = options;
122 if (self.addend) |addend| {
123 try std.fmt.format(writer, ".addend = {}, ", .{addend});
124 }
125 try std.fmt.format(writer, ".op_kind = {s}, ", .{self.op_kind});
126 }
127};
128
129pub const GotPage = struct {
130 base: Relocation,
131 /// Always .PCRelativeAddress
132 // inst: aarch64.Instruction,
133
134 pub const base_type: Relocation.Type = .got_page;
135
136 // pub fn resolve(page: GotPage, args: Relocation.ResolveArgs) !void {
137 // const source_page = @intCast(i32, args.source_addr >> 12);
138 // const target_page = @intCast(i32, args.target_addr >> 12);
139 // const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
140
141 // log.debug(" | moving by {} pages", .{pages});
142
143 // var inst = page.inst;
144 // inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
145 // inst.pc_relative_address.immlo = @truncate(u2, pages);
146
147 // mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32());
148 // }
149
150 pub fn format(self: GotPage, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
151 _ = self;
152 _ = fmt;
153 _ = options;
154 _ = writer;
155 }
156};
157
158pub const GotPageOff = struct {
159 base: Relocation,
160 /// Always .LoadStoreRegister with size = 3 for GOT indirection
161 // inst: aarch64.Instruction,
162
163 pub const base_type: Relocation.Type = .got_page_off;
164
165 // pub fn resolve(page_off: GotPageOff, args: Relocation.ResolveArgs) !void {
166 // const narrowed = @truncate(u12, args.target_addr);
167
168 // log.debug(" | narrowed address within the page 0x{x}", .{narrowed});
169
170 // var inst = page_off.inst;
171 // const offset = try math.divExact(u12, narrowed, 8);
172 // inst.load_store_register.offset = offset;
173
174 // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32());
175 // }
176
177 pub fn format(self: GotPageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
178 _ = self;
179 _ = fmt;
180 _ = options;
181 _ = writer;
182 }
183};
184
185pub const PointerToGot = struct {
186 base: Relocation,
187
188 pub const base_type: Relocation.Type = .pointer_to_got;
189
190 // pub fn resolve(ptr_to_got: PointerToGot, args: Relocation.ResolveArgs) !void {
191 // const result = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr));
192
193 // log.debug(" | calculated value 0x{x}", .{result});
194
195 // mem.writeIntLittle(u32, ptr_to_got.base.code[0..4], @bitCast(u32, result));
196 // }
197
198 pub fn format(self: PointerToGot, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
199 _ = self;
200 _ = fmt;
201 _ = options;
202 _ = writer;
203 }
204};
205
206pub const TlvpPage = struct {
207 base: Relocation,
208 /// Always .PCRelativeAddress
209 // inst: aarch64.Instruction,
210
211 pub const base_type: Relocation.Type = .tlvp_page;
212
213 // pub fn resolve(page: TlvpPage, args: Relocation.ResolveArgs) !void {
214 // const source_page = @intCast(i32, args.source_addr >> 12);
215 // const target_page = @intCast(i32, args.target_addr >> 12);
216 // const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
217
218 // log.debug(" | moving by {} pages", .{pages});
219
220 // var inst = page.inst;
221 // inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
222 // inst.pc_relative_address.immlo = @truncate(u2, pages);
223
224 // mem.writeIntLittle(u32, page.base.code[0..4], inst.toU32());
225 // }
226
227 pub fn format(self: TlvpPage, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
228 _ = self;
229 _ = fmt;
230 _ = options;
231 _ = writer;
232 }
233};
234
235pub const TlvpPageOff = struct {
236 base: Relocation,
237 /// Always .AddSubtractImmediate regardless of the source instruction.
238 /// This means, we always rewrite the instruction to add even if the
239 /// source instruction was an ldr.
240 // inst: aarch64.Instruction,
241
242 pub const base_type: Relocation.Type = .tlvp_page_off;
243
244 // pub fn resolve(page_off: TlvpPageOff, args: Relocation.ResolveArgs) !void {
245 // const narrowed = @truncate(u12, args.target_addr);
246
247 // log.debug(" | narrowed address within the page 0x{x}", .{narrowed});
248
249 // var inst = page_off.inst;
250 // inst.add_subtract_immediate.imm12 = narrowed;
251
252 // mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32());
253 // }
254
255 pub fn format(self: TlvpPageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
256 _ = self;
257 _ = fmt;
258 _ = options;
259 _ = writer;
260 }
261};
262
263pub const Parser = struct {
264 object: *Object,
265 zld: *Zld,
266 it: *reloc.RelocIterator,
267 block: *TextBlock,
268 base_addr: u64,
269 addend: ?u32 = null,
270 subtractor: ?*Symbol = null,
271
272 pub fn parse(self: *Parser) !void {
273 while (self.it.next()) |rel| {
274 const out_rel = switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
275 .ARM64_RELOC_BRANCH26 => try self.parseBranch(rel),
276 .ARM64_RELOC_SUBTRACTOR => {
277 // Subtractor is not a relocation with effect on the TextBlock, so
278 // parse it and carry on.
279 try self.parseSubtractor(rel);
280 continue;
281 },
282 .ARM64_RELOC_UNSIGNED => try self.parseUnsigned(rel),
283 .ARM64_RELOC_ADDEND => {
284 // Addend is not a relocation with effect on the TextBlock, so
285 // parse it and carry on.
286 try self.parseAddend(rel);
287 continue;
288 },
289 .ARM64_RELOC_PAGE21,
290 .ARM64_RELOC_GOT_LOAD_PAGE21,
291 .ARM64_RELOC_TLVP_LOAD_PAGE21,
292 => try self.parsePage(rel),
293 .ARM64_RELOC_PAGEOFF12 => try self.parsePageOff(rel),
294 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => try self.parseGotLoadPageOff(rel),
295 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => try self.parseTlvpLoadPageOff(rel),
296 .ARM64_RELOC_POINTER_TO_GOT => try self.parsePointerToGot(rel),
297 };
298 try self.block.relocs.append(out_rel);
299
300 if (out_rel.target.payload == .regular) {
301 try self.block.references.put(out_rel.target.payload.regular.local_sym_index, {});
302 }
303
304 switch (out_rel.@"type") {
305 .got_page, .got_page_off, .pointer_to_got => {
306 const sym = out_rel.target;
307
308 if (sym.got_index != null) continue;
309
310 const index = @intCast(u32, self.zld.got_entries.items.len);
311 sym.got_index = index;
312 try self.zld.got_entries.append(self.zld.allocator, sym);
313
314 log.debug("adding GOT entry for symbol {s} at index {}", .{ sym.name, index });
315 },
316 .branch_aarch64 => {
317 const sym = out_rel.target;
318
319 if (sym.stubs_index != null) continue;
320 if (sym.payload != .proxy) continue;
321
322 const index = @intCast(u32, self.zld.stubs.items.len);
323 sym.stubs_index = index;
324 try self.zld.stubs.append(self.zld.allocator, sym);
325
326 log.debug("adding stub entry for symbol {s} at index {}", .{ sym.name, index });
327 },
328 else => {},
329 }
330 }
331 }
332
333 fn parseAddend(self: *Parser, rel: macho.relocation_info) !void {
334 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
335 assert(rel_type == .ARM64_RELOC_ADDEND);
336 assert(rel.r_pcrel == 0);
337 assert(rel.r_extern == 0);
338 assert(self.addend == null);
339
340 self.addend = rel.r_symbolnum;
341
342 // Verify ADDEND is followed by a load.
343 const next = @intToEnum(macho.reloc_type_arm64, self.it.peek().r_type);
344 switch (next) {
345 .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {},
346 else => {
347 log.err("unexpected relocation type: expected PAGE21 or PAGEOFF12, found {s}", .{next});
348 return error.UnexpectedRelocationType;
349 },
350 }
351 }
352
353 fn parseBranch(self: *Parser, rel: macho.relocation_info) !*Relocation {
354 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
355 assert(rel_type == .ARM64_RELOC_BRANCH26);
356 assert(rel.r_pcrel == 1);
357 assert(rel.r_length == 2);
358
359 const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
360 const target = try self.object.symbolFromReloc(rel);
361
362 var branch = try self.object.allocator.create(Branch);
363 errdefer self.object.allocator.destroy(branch);
364
365 branch.* = .{
366 .base = .{
367 .@"type" = .branch_aarch64,
368 .offset = offset,
369 .target = target,
370 .block = self.block,
371 },
372 };
373
374 return &branch.base;
375 }
376
377 fn parsePage(self: *Parser, rel: macho.relocation_info) !*Relocation {
378 assert(rel.r_pcrel == 1);
379 assert(rel.r_length == 2);
380
381 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
382 const target = try self.object.symbolFromReloc(rel);
383 const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
384
385 const ptr: *Relocation = ptr: {
386 switch (rel_type) {
387 .ARM64_RELOC_PAGE21 => {
388 defer {
389 // Reset parser's addend state
390 self.addend = null;
391 }
392 var page = try self.object.allocator.create(Page);
393 errdefer self.object.allocator.destroy(page);
394
395 page.* = .{
396 .base = .{
397 .@"type" = .page,
398 .offset = offset,
399 .target = target,
400 .block = self.block,
401 },
402 .addend = self.addend,
403 };
404
405 break :ptr &page.base;
406 },
407 .ARM64_RELOC_GOT_LOAD_PAGE21 => {
408 var page = try self.object.allocator.create(GotPage);
409 errdefer self.object.allocator.destroy(page);
410
411 page.* = .{
412 .base = .{
413 .@"type" = .got_page,
414 .offset = offset,
415 .target = target,
416 .block = self.block,
417 },
418 };
419
420 break :ptr &page.base;
421 },
422 .ARM64_RELOC_TLVP_LOAD_PAGE21 => {
423 var page = try self.object.allocator.create(TlvpPage);
424 errdefer self.object.allocator.destroy(page);
425
426 page.* = .{
427 .base = .{
428 .@"type" = .tlvp_page,
429 .offset = offset,
430 .target = target,
431 .block = self.block,
432 },
433 };
434
435 break :ptr &page.base;
436 },
437 else => unreachable,
438 }
439 };
440
441 return ptr;
442 }
443
444 fn parsePageOff(self: *Parser, rel: macho.relocation_info) !*Relocation {
445 defer {
446 // Reset parser's addend state
447 self.addend = null;
448 }
449
450 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
451 assert(rel_type == .ARM64_RELOC_PAGEOFF12);
452 assert(rel.r_pcrel == 0);
453 assert(rel.r_length == 2);
454
455 const target = try self.object.symbolFromReloc(rel);
456 const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
457 const op_kind: PageOff.OpKind = if (isArithmeticOp(self.block.code[offset..][0..4]))
458 .arithmetic
459 else
460 .load_store;
461
462 var page_off = try self.object.allocator.create(PageOff);
463 errdefer self.object.allocator.destroy(page_off);
464
465 page_off.* = .{
466 .base = .{
467 .@"type" = .page_off,
468 .offset = offset,
469 .target = target,
470 .block = self.block,
471 },
472 .op_kind = op_kind,
473 .addend = self.addend,
474 };
475
476 return &page_off.base;
477 }
478
479 fn parseGotLoadPageOff(self: *Parser, rel: macho.relocation_info) !*Relocation {
480 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
481 assert(rel_type == .ARM64_RELOC_GOT_LOAD_PAGEOFF12);
482 assert(rel.r_pcrel == 0);
483 assert(rel.r_length == 2);
484
485 const target = try self.object.symbolFromReloc(rel);
486 const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
487 assert(!isArithmeticOp(self.block.code[offset..][0..4]));
488
489 var page_off = try self.object.allocator.create(GotPageOff);
490 errdefer self.object.allocator.destroy(page_off);
491
492 page_off.* = .{
493 .base = .{
494 .@"type" = .got_page_off,
495 .offset = offset,
496 .target = target,
497 .block = self.block,
498 },
499 };
500
501 return &page_off.base;
502 }
503
504 fn parseTlvpLoadPageOff(self: *Parser, rel: macho.relocation_info) !*Relocation {
505 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
506 assert(rel_type == .ARM64_RELOC_TLVP_LOAD_PAGEOFF12);
507 assert(rel.r_pcrel == 0);
508 assert(rel.r_length == 2);
509
510 const RegInfo = struct {
511 rd: u5,
512 rn: u5,
513 size: u1,
514 };
515
516 const target = try self.object.symbolFromReloc(rel);
517 const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
518
519 var page_off = try self.object.allocator.create(TlvpPageOff);
520 errdefer self.object.allocator.destroy(page_off);
521
522 page_off.* = .{
523 .base = .{
524 .@"type" = .tlvp_page_off,
525 .offset = offset,
526 .target = target,
527 .block = self.block,
528 },
529 };
530
531 return &page_off.base;
532 }
533
534 fn parseSubtractor(self: *Parser, rel: macho.relocation_info) !void {
535 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
536 assert(rel_type == .ARM64_RELOC_SUBTRACTOR);
537 assert(rel.r_pcrel == 0);
538 assert(self.subtractor == null);
539
540 self.subtractor = try self.object.symbolFromReloc(rel);
541
542 // Verify SUBTRACTOR is followed by UNSIGNED.
543 const next = @intToEnum(macho.reloc_type_arm64, self.it.peek().r_type);
544 if (next != .ARM64_RELOC_UNSIGNED) {
545 log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next});
546 return error.UnexpectedRelocationType;
547 }
548 }
549
550 fn parseUnsigned(self: *Parser, rel: macho.relocation_info) !*Relocation {
551 defer {
552 // Reset parser's subtractor state
553 self.subtractor = null;
554 }
555
556 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
557 assert(rel_type == .ARM64_RELOC_UNSIGNED);
558 assert(rel.r_pcrel == 0);
559
560 const target = try self.object.symbolFromReloc(rel);
561 const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
562 const is_64bit: bool = switch (rel.r_length) {
563 3 => true,
564 2 => false,
565 else => unreachable,
566 };
567 const addend: i64 = if (is_64bit)
568 mem.readIntLittle(i64, self.block.code[offset..][0..8])
569 else
570 mem.readIntLittle(i32, self.block.code[offset..][0..4]);
571
572 var unsigned = try self.object.allocator.create(reloc.Unsigned);
573 errdefer self.object.allocator.destroy(unsigned);
574
575 unsigned.* = .{
576 .base = .{
577 .@"type" = .unsigned,
578 .offset = offset,
579 .target = target,
580 .block = self.block,
581 },
582 .subtractor = self.subtractor,
583 .is_64bit = is_64bit,
584 .addend = addend,
585 };
586
587 return &unsigned.base;
588 }
589
590 fn parsePointerToGot(self: *Parser, rel: macho.relocation_info) !*Relocation {
591 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
592 assert(rel_type == .ARM64_RELOC_POINTER_TO_GOT);
593 assert(rel.r_pcrel == 1);
594 assert(rel.r_length == 2);
595
596 var ptr_to_got = try self.object.allocator.create(PointerToGot);
597 errdefer self.object.allocator.destroy(ptr_to_got);
598
599 const target = try self.object.symbolFromReloc(rel);
600 const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
601
602 ptr_to_got.* = .{
603 .base = .{
604 .@"type" = .pointer_to_got,
605 .offset = offset,
606 .target = target,
607 .block = self.block,
608 },
609 };
610
611 return &ptr_to_got.base;
612 }
613};
614
615inline fn isArithmeticOp(inst: *const [4]u8) bool {
616 const group_decode = @truncate(u5, inst[3]);
617 return ((group_decode >> 2) == 4);
618}
src/link/MachO/reloc/x86_64.zig deleted-385
......@@ -1,385 +0,0 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const log = std.log.scoped(.reloc);
4const macho = std.macho;
5const math = std.math;
6const mem = std.mem;
7const meta = std.meta;
8const reloc = @import("../reloc.zig");
9
10const Allocator = mem.Allocator;
11const Object = @import("../Object.zig");
12const Relocation = reloc.Relocation;
13const Symbol = @import("../Symbol.zig");
14const TextBlock = Zld.TextBlock;
15const Zld = @import("../Zld.zig");
16
17pub const Branch = struct {
18 base: Relocation,
19
20 pub const base_type: Relocation.Type = .branch_x86_64;
21
22 // pub fn resolve(branch: Branch, args: Relocation.ResolveArgs) !void {
23 // const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4);
24 // log.debug(" | displacement 0x{x}", .{displacement});
25 // mem.writeIntLittle(u32, branch.base.code[0..4], @bitCast(u32, displacement));
26 // }
27
28 pub fn format(self: Branch, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
29 _ = self;
30 _ = fmt;
31 _ = options;
32 _ = writer;
33 }
34};
35
36pub const Signed = struct {
37 base: Relocation,
38 addend: i32,
39 correction: i4,
40
41 pub const base_type: Relocation.Type = .signed;
42
43 // pub fn resolve(signed: Signed, args: Relocation.ResolveArgs) !void {
44 // const target_addr = target_addr: {
45 // if (signed.base.target == .section) {
46 // const source_target = @intCast(i64, args.source_source_sect_addr.?) + @intCast(i64, signed.base.offset) + signed.addend + 4;
47 // const source_disp = source_target - @intCast(i64, args.source_target_sect_addr.?);
48 // break :target_addr @intCast(i64, args.target_addr) + source_disp;
49 // }
50 // break :target_addr @intCast(i64, args.target_addr) + signed.addend;
51 // };
52 // const displacement = try math.cast(
53 // i32,
54 // target_addr - @intCast(i64, args.source_addr) - signed.correction - 4,
55 // );
56
57 // log.debug(" | addend 0x{x}", .{signed.addend});
58 // log.debug(" | correction 0x{x}", .{signed.correction});
59 // log.debug(" | displacement 0x{x}", .{displacement});
60
61 // mem.writeIntLittle(u32, signed.base.code[0..4], @bitCast(u32, displacement));
62 // }
63
64 pub fn format(self: Signed, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
65 _ = fmt;
66 _ = options;
67 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
68 try std.fmt.format(writer, ".correction = {}, ", .{self.correction});
69 }
70};
71
72pub const GotLoad = struct {
73 base: Relocation,
74
75 pub const base_type: Relocation.Type = .got_load;
76
77 // pub fn resolve(got_load: GotLoad, args: Relocation.ResolveArgs) !void {
78 // const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4);
79 // log.debug(" | displacement 0x{x}", .{displacement});
80 // mem.writeIntLittle(u32, got_load.base.code[0..4], @bitCast(u32, displacement));
81 // }
82
83 pub fn format(self: GotLoad, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
84 _ = self;
85 _ = fmt;
86 _ = options;
87 _ = writer;
88 }
89};
90
91pub const Got = struct {
92 base: Relocation,
93 addend: i32,
94
95 pub const base_type: Relocation.Type = .got;
96
97 // pub fn resolve(got: Got, args: Relocation.ResolveArgs) !void {
98 // const displacement = try math.cast(
99 // i32,
100 // @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4 + got.addend,
101 // );
102 // log.debug(" | displacement 0x{x}", .{displacement});
103 // mem.writeIntLittle(u32, got.base.code[0..4], @bitCast(u32, displacement));
104 // }
105
106 pub fn format(self: Got, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
107 _ = fmt;
108 _ = options;
109 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
110 }
111};
112
113pub const Tlv = struct {
114 base: Relocation,
115
116 pub const base_type: Relocation.Type = .tlv;
117
118 // pub fn resolve(tlv: Tlv, args: Relocation.ResolveArgs) !void {
119 // // We need to rewrite the opcode from movq to leaq.
120 // tlv.op.* = 0x8d;
121 // log.debug(" | rewriting op to leaq", .{});
122
123 // const displacement = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4);
124 // log.debug(" | displacement 0x{x}", .{displacement});
125
126 // mem.writeIntLittle(u32, tlv.base.code[0..4], @bitCast(u32, displacement));
127 // }
128 pub fn format(self: Tlv, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
129 _ = self;
130 _ = fmt;
131 _ = options;
132 _ = writer;
133 }
134};
135
136pub const Parser = struct {
137 object: *Object,
138 zld: *Zld,
139 it: *reloc.RelocIterator,
140 block: *TextBlock,
141 base_addr: u64,
142 subtractor: ?*Symbol = null,
143
144 pub fn parse(self: *Parser) !void {
145 while (self.it.next()) |rel| {
146 const out_rel = switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
147 .X86_64_RELOC_BRANCH => try self.parseBranch(rel),
148 .X86_64_RELOC_SUBTRACTOR => {
149 // Subtractor is not a relocation with effect on the TextBlock, so
150 // parse it and carry on.
151 try self.parseSubtractor(rel);
152 continue;
153 },
154 .X86_64_RELOC_UNSIGNED => try self.parseUnsigned(rel),
155 .X86_64_RELOC_SIGNED,
156 .X86_64_RELOC_SIGNED_1,
157 .X86_64_RELOC_SIGNED_2,
158 .X86_64_RELOC_SIGNED_4,
159 => try self.parseSigned(rel),
160 .X86_64_RELOC_GOT_LOAD => try self.parseGotLoad(rel),
161 .X86_64_RELOC_GOT => try self.parseGot(rel),
162 .X86_64_RELOC_TLV => try self.parseTlv(rel),
163 };
164 try self.block.relocs.append(out_rel);
165
166 if (out_rel.target.payload == .regular) {
167 try self.block.references.put(out_rel.target.payload.regular.local_sym_index, {});
168 }
169
170 switch (out_rel.@"type") {
171 .got_load, .got => {
172 const sym = out_rel.target;
173
174 if (sym.got_index != null) continue;
175
176 const index = @intCast(u32, self.zld.got_entries.items.len);
177 sym.got_index = index;
178 try self.zld.got_entries.append(self.zld.allocator, sym);
179
180 log.debug("adding GOT entry for symbol {s} at index {}", .{ sym.name, index });
181 },
182 .branch_x86_64 => {
183 const sym = out_rel.target;
184
185 if (sym.stubs_index != null) continue;
186 if (sym.payload != .proxy) continue;
187
188 const index = @intCast(u32, self.zld.stubs.items.len);
189 sym.stubs_index = index;
190 try self.zld.stubs.append(self.zld.allocator, sym);
191
192 log.debug("adding stub entry for symbol {s} at index {}", .{ sym.name, index });
193 },
194 else => {},
195 }
196 }
197 }
198
199 fn parseBranch(self: *Parser, rel: macho.relocation_info) !*Relocation {
200 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
201 assert(rel_type == .X86_64_RELOC_BRANCH);
202 assert(rel.r_pcrel == 1);
203 assert(rel.r_length == 2);
204
205 const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
206 const target = try self.object.symbolFromReloc(rel);
207
208 var branch = try self.object.allocator.create(Branch);
209 errdefer self.object.allocator.destroy(branch);
210
211 branch.* = .{
212 .base = .{
213 .@"type" = .branch_x86_64,
214 .offset = offset,
215 .target = target,
216 .block = self.block,
217 },
218 };
219
220 return &branch.base;
221 }
222
223 fn parseSigned(self: *Parser, rel: macho.relocation_info) !*Relocation {
224 assert(rel.r_pcrel == 1);
225 assert(rel.r_length == 2);
226
227 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
228 const target = try self.object.symbolFromReloc(rel);
229 const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
230 const correction: i4 = switch (rel_type) {
231 .X86_64_RELOC_SIGNED => 0,
232 .X86_64_RELOC_SIGNED_1 => 1,
233 .X86_64_RELOC_SIGNED_2 => 2,
234 .X86_64_RELOC_SIGNED_4 => 4,
235 else => unreachable,
236 };
237 const addend = mem.readIntLittle(i32, self.block.code[offset..][0..4]) + correction;
238
239 var signed = try self.object.allocator.create(Signed);
240 errdefer self.object.allocator.destroy(signed);
241
242 signed.* = .{
243 .base = .{
244 .@"type" = .signed,
245 .offset = offset,
246 .target = target,
247 .block = self.block,
248 },
249 .addend = addend,
250 .correction = correction,
251 };
252
253 return &signed.base;
254 }
255
256 fn parseGotLoad(self: *Parser, rel: macho.relocation_info) !*Relocation {
257 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
258 assert(rel_type == .X86_64_RELOC_GOT_LOAD);
259 assert(rel.r_pcrel == 1);
260 assert(rel.r_length == 2);
261
262 const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
263 const target = try self.object.symbolFromReloc(rel);
264
265 var got_load = try self.object.allocator.create(GotLoad);
266 errdefer self.object.allocator.destroy(got_load);
267
268 got_load.* = .{
269 .base = .{
270 .@"type" = .got_load,
271 .offset = offset,
272 .target = target,
273 .block = self.block,
274 },
275 };
276
277 return &got_load.base;
278 }
279
280 fn parseGot(self: *Parser, rel: macho.relocation_info) !*Relocation {
281 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
282 assert(rel_type == .X86_64_RELOC_GOT);
283 assert(rel.r_pcrel == 1);
284 assert(rel.r_length == 2);
285
286 const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
287 const target = try self.object.symbolFromReloc(rel);
288 const addend = mem.readIntLittle(i32, self.block.code[offset..][0..4]);
289
290 var got = try self.object.allocator.create(Got);
291 errdefer self.object.allocator.destroy(got);
292
293 got.* = .{
294 .base = .{
295 .@"type" = .got,
296 .offset = offset,
297 .target = target,
298 .block = self.block,
299 },
300 .addend = addend,
301 };
302
303 return &got.base;
304 }
305
306 fn parseTlv(self: *Parser, rel: macho.relocation_info) !*Relocation {
307 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
308 assert(rel_type == .X86_64_RELOC_TLV);
309 assert(rel.r_pcrel == 1);
310 assert(rel.r_length == 2);
311
312 const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
313 const target = try self.object.symbolFromReloc(rel);
314
315 var tlv = try self.object.allocator.create(Tlv);
316 errdefer self.object.allocator.destroy(tlv);
317
318 tlv.* = .{
319 .base = .{
320 .@"type" = .tlv,
321 .offset = offset,
322 .target = target,
323 .block = self.block,
324 },
325 };
326
327 return &tlv.base;
328 }
329
330 fn parseSubtractor(self: *Parser, rel: macho.relocation_info) !void {
331 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
332 assert(rel_type == .X86_64_RELOC_SUBTRACTOR);
333 assert(rel.r_pcrel == 0);
334 assert(self.subtractor == null);
335
336 self.subtractor = try self.object.symbolFromReloc(rel);
337
338 // Verify SUBTRACTOR is followed by UNSIGNED.
339 const next = @intToEnum(macho.reloc_type_x86_64, self.it.peek().r_type);
340 if (next != .X86_64_RELOC_UNSIGNED) {
341 log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next});
342 return error.UnexpectedRelocationType;
343 }
344 }
345
346 fn parseUnsigned(self: *Parser, rel: macho.relocation_info) !*Relocation {
347 defer {
348 // Reset parser's subtractor state
349 self.subtractor = null;
350 }
351
352 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
353 assert(rel_type == .X86_64_RELOC_UNSIGNED);
354 assert(rel.r_pcrel == 0);
355
356 const target = try self.object.symbolFromReloc(rel);
357 const is_64bit: bool = switch (rel.r_length) {
358 3 => true,
359 2 => false,
360 else => unreachable,
361 };
362 const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
363 const addend: i64 = if (is_64bit)
364 mem.readIntLittle(i64, self.block.code[offset..][0..8])
365 else
366 mem.readIntLittle(i32, self.block.code[offset..][0..4]);
367
368 var unsigned = try self.object.allocator.create(reloc.Unsigned);
369 errdefer self.object.allocator.destroy(unsigned);
370
371 unsigned.* = .{
372 .base = .{
373 .@"type" = .unsigned,
374 .offset = offset,
375 .target = target,
376 .block = self.block,
377 },
378 .subtractor = self.subtractor,
379 .is_64bit = is_64bit,
380 .addend = addend,
381 };
382
383 return &unsigned.base;
384 }
385};