authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-04-02 19:53:16+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-04-13 10:56:03+02:00
log6a866f1a96d232a681a71b899657df1ae70b8f2e
tree0e2ffbb81ef73db48a840a52ae59ebbe935e3a87
parent4e676ecbb5c107ca5c9dbc497920d0150e4e1d8e

zld: preprocess relocs on arm64


3 files changed, 764 insertions(+), 50 deletions(-)

CMakeLists.txt+1
...@@ -574,6 +574,7 @@ set(ZIG_STAGE2_SOURCES...@@ -574,6 +574,7 @@ set(ZIG_STAGE2_SOURCES
574 "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"574 "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"
575 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"575 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
576 "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig"576 "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig"
577 "${CMAKE_SOURCE_DIR}/src/link/MachO/reloc.zig"
577 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"578 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
578 "${CMAKE_SOURCE_DIR}/src/link/C/zig.h"579 "${CMAKE_SOURCE_DIR}/src/link/C/zig.h"
579 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"580 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"
src/link/MachO/Object.zig+55-50
...@@ -7,8 +7,10 @@ const io = std.io;...@@ -7,8 +7,10 @@ const io = std.io;
7const log = std.log.scoped(.object);7const log = std.log.scoped(.object);
8const macho = std.macho;8const macho = std.macho;
9const mem = std.mem;9const mem = std.mem;
10const reloc = @import("reloc.zig");
1011
11const Allocator = mem.Allocator;12const Allocator = mem.Allocator;
13const Relocation = reloc.Relocation;
12const Symbol = @import("Symbol.zig");14const Symbol = @import("Symbol.zig");
13const parseName = @import("Zld.zig").parseName;15const parseName = @import("Zld.zig").parseName;
1416
...@@ -22,6 +24,7 @@ file_offset: ?u32 = null,...@@ -22,6 +24,7 @@ file_offset: ?u32 = null,
22name: ?[]u8 = null,24name: ?[]u8 = null,
2325
24load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},26load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
27sections: std.ArrayListUnmanaged(Section) = .{},
2528
26segment_cmd_index: ?u16 = null,29segment_cmd_index: ?u16 = null,
27symtab_cmd_index: ?u16 = null,30symtab_cmd_index: ?u16 = null,
...@@ -42,6 +45,24 @@ strtab: std.ArrayListUnmanaged(u8) = .{},...@@ -42,6 +45,24 @@ strtab: std.ArrayListUnmanaged(u8) = .{},
4245
43data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},46data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
4447
48const Section = struct {
49 inner: macho.section_64,
50 code: []u8,
51 relocs: ?[]*Relocation,
52 // TODO store object-to-exe-section mapping here
53
54 pub fn deinit(self: *Section, allocator: *Allocator) void {
55 allocator.free(self.code);
56
57 if (self.relocs) |relocs| {
58 for (relocs) |rel| {
59 allocator.destroy(rel);
60 }
61 allocator.free(relocs);
62 }
63 }
64};
65
45pub fn init(allocator: *Allocator) Object {66pub fn init(allocator: *Allocator) Object {
46 return .{67 return .{
47 .allocator = allocator,68 .allocator = allocator,
...@@ -53,6 +74,12 @@ pub fn deinit(self: *Object) void {...@@ -53,6 +74,12 @@ pub fn deinit(self: *Object) void {
53 lc.deinit(self.allocator);74 lc.deinit(self.allocator);
54 }75 }
55 self.load_commands.deinit(self.allocator);76 self.load_commands.deinit(self.allocator);
77
78 for (self.sections.items) |*sect| {
79 sect.deinit(self.allocator);
80 }
81 self.sections.deinit(self.allocator);
82
56 self.symtab.deinit(self.allocator);83 self.symtab.deinit(self.allocator);
57 self.strtab.deinit(self.allocator);84 self.strtab.deinit(self.allocator);
58 self.data_in_code_entries.deinit(self.allocator);85 self.data_in_code_entries.deinit(self.allocator);
...@@ -95,15 +122,9 @@ pub fn parse(self: *Object) !void {...@@ -95,15 +122,9 @@ pub fn parse(self: *Object) !void {
95 }122 }
96123
97 try self.readLoadCommands(reader);124 try self.readLoadCommands(reader);
125 try self.parseSections();
98 if (self.symtab_cmd_index != null) try self.parseSymtab();126 if (self.symtab_cmd_index != null) try self.parseSymtab();
99 if (self.data_in_code_cmd_index != null) try self.readDataInCode();127 if (self.data_in_code_cmd_index != null) try self.readDataInCode();
100
101 {
102 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
103 for (seg.sections.items) |_, sect_id| {
104 try self.parseRelocs(@intCast(u16, sect_id));
105 }
106 }
107}128}
108129
109pub fn readLoadCommands(self: *Object, reader: anytype) !void {130pub fn readLoadCommands(self: *Object, reader: anytype) !void {
...@@ -170,53 +191,37 @@ pub fn readLoadCommands(self: *Object, reader: anytype) !void {...@@ -170,53 +191,37 @@ pub fn readLoadCommands(self: *Object, reader: anytype) !void {
170 }191 }
171}192}
172193
173pub fn parseRelocs(self: *Object, sect_id: u16) !void {194pub fn parseSections(self: *Object) !void {
174 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;195 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
175 const sect = seg.sections.items[sect_id];196
176197 try self.sections.ensureCapacity(self.allocator, seg.sections.items.len);
177 if (sect.nreloc == 0) return;198
178199 for (seg.sections.items) |sect| {
179 var raw_relocs = try self.allocator.alloc(u8, @sizeOf(macho.relocation_info) * sect.nreloc);200 // Read sections' code
180 defer self.allocator.free(raw_relocs);201 var code = try self.allocator.alloc(u8, sect.size);
181 _ = try self.file.?.preadAll(raw_relocs, sect.reloff);202 _ = try self.file.?.preadAll(code, sect.offset);
182 const relocs = mem.bytesAsSlice(macho.relocation_info, raw_relocs);203
183204 var section = Section{
184 for (relocs) |reloc| {205 .inner = sect,
185 const is_addend = is_addend: {206 .code = code,
186 switch (self.arch.?) {207 .relocs = undefined,
187 .x86_64 => {
188 const rel_type = @intToEnum(macho.reloc_type_x86_64, reloc.r_type);
189 log.warn("{s}", .{rel_type});
190
191 break :is_addend false;
192 },
193 .aarch64 => {
194 const rel_type = @intToEnum(macho.reloc_type_arm64, reloc.r_type);
195 log.warn("{s}", .{rel_type});
196
197 break :is_addend rel_type == .ARM64_RELOC_ADDEND;
198 },
199 else => unreachable,
200 }
201 };208 };
202209
203 if (!is_addend) {210 // Parse relocations
204 if (reloc.r_extern == 1) {211 var relocs: ?[]*Relocation = if (sect.nreloc > 0) relocs: {
205 const sym = self.symtab.items[reloc.r_symbolnum];212 var raw_relocs = try self.allocator.alloc(u8, @sizeOf(macho.relocation_info) * sect.nreloc);
206 const sym_name = self.getString(sym.inner.n_strx);213 defer self.allocator.free(raw_relocs);
207 log.warn(" | symbol = {s}", .{sym_name});214
208 } else {215 _ = try self.file.?.preadAll(raw_relocs, sect.reloff);
209 const target_sect = seg.sections.items[reloc.r_symbolnum - 1];216
210 log.warn(" | section = {s},{s}", .{217 break :relocs try reloc.parse(
211 parseName(&target_sect.segname),218 self.allocator,
212 parseName(&target_sect.sectname),219 &section.code,
213 });220 mem.bytesAsSlice(macho.relocation_info, raw_relocs),
214 }221 );
215 }222 } else null;
216223
217 log.warn(" | offset = 0x{x}", .{reloc.r_address});224 self.sections.appendAssumeCapacity(section);
218 log.warn(" | PC = {}", .{reloc.r_pcrel == 1});
219 log.warn(" | length = {}", .{reloc.r_length});
220 }225 }
221}226}
222227
src/link/MachO/reloc.zig created+708
...@@ -0,0 +1,708 @@
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 mem = std.mem;
7const meta = std.meta;
8
9const Allocator = mem.Allocator;
10
11pub const Relocation = struct {
12 @"type": Type,
13 code: *[]u8,
14 offset: u32,
15 target: Target,
16
17 pub fn cast(base: *Relocation, comptime T: type) ?*T {
18 if (base.@"type" != T.base_type)
19 return null;
20
21 return @fieldParentPtr(T, "base", base);
22 }
23
24 pub const Type = enum {
25 branch,
26 unsigned,
27 page,
28 page_off,
29 got_page,
30 got_page_off,
31 tlvp_page,
32 tlvp_page_off,
33 };
34
35 pub const Target = union(enum) {
36 symbol: u32,
37 section: u16,
38
39 pub fn from_reloc(reloc: macho.relocation_info) Target {
40 return if (reloc.r_extern == 1) .{
41 .symbol = reloc.r_symbolnum,
42 } else .{
43 .section = @intCast(u16, reloc.r_symbolnum - 1),
44 };
45 }
46 };
47
48 pub const Branch = struct {
49 base: Relocation,
50 /// Always .UnconditionalBranchImmediate
51 inst: aarch64.Instruction,
52
53 pub const base_type: Relocation.Type = .branch;
54
55 pub fn resolve(branch: Branch, source_addr: u64, target_addr: u64) !void {
56 const displacement = try math.cast(i28, @intCast(i64, target_addr) - @intCast(i64, source_addr));
57 var inst = branch.inst;
58 inst.AddSubtractImmediate.imm26 = @truncate(u26, @bitCast(u28, displacement) >> 2);
59 mem.writeIntLittle(u32, branch.base.code.*[branch.base.offset..], inst.toU32());
60 }
61 };
62
63 pub const Unsigned = struct {
64 base: Relocation,
65 subtractor: ?Target = null,
66 /// Addend embedded directly in the relocation slot
67 addend: i64,
68 /// Extracted from r_length:
69 /// => 3 implies true
70 /// => 2 implies false
71 /// => * is unreachable
72 is_64bit: bool,
73
74 pub const base_type: Relocation.Type = .unsigned;
75
76 pub fn resolve(unsigned: Unsigned, target_addr: u64, subtractor: i64) !void {
77 const result = @intCast(i64, target_addr) - subtractor + unsigned.addend;
78
79 log.debug(" | calculated addend 0x{x}", .{unsigned.addend});
80 log.debug(" | calculated unsigned value 0x{x}", .{result});
81
82 if (unsigned.is_64bit) {
83 mem.writeIntLittle(
84 u64,
85 unsigned.base.code.*[unsigned.base.offset..],
86 @bitCast(u64, result),
87 );
88 } else {
89 mem.writeIntLittle(
90 u32,
91 unsigned.base.code.*[unsigned.base.offset..],
92 @truncate(u32, @bitCast(u64, result)),
93 );
94 }
95 }
96 };
97
98 pub const Page = struct {
99 base: Relocation,
100 addend: ?u32 = null,
101 /// Always .PCRelativeAddress
102 inst: aarch64.Instruction,
103
104 pub const base_type: Relocation.Type = .page;
105
106 pub fn resolve(page: Page, source_addr: u64, target_addr: u64) !void {
107 const ta = if (page.addend) |a| target_addr + a else target_addr;
108 const source_page = @intCast(i32, source_addr >> 12);
109 const target_page = @intCast(i32, ta >> 12);
110 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
111
112 log.debug(" | moving by {} pages", .{pages});
113
114 var inst = page.inst;
115 inst.PCRelativeAddress.immhi = @truncate(u19, pages >> 2);
116 inst.PCRelativeAddress.immlo = @truncate(u2, pages);
117
118 mem.writeIntLittle(u32, page.base.code.*[page.base.offset..], inst.toU32());
119 }
120 };
121
122 pub const PageOff = struct {
123 base: Relocation,
124 addend: ?u32 = null,
125 op_kind: OpKind,
126 inst: aarch64.Instruction,
127
128 pub const base_type: Relocation.Type = .page_off;
129
130 pub const OpKind = enum {
131 arithmetic,
132 load_store,
133 };
134
135 pub fn resolve(page_off: PageOff, target_addr: u64) !void {
136 const ta = if (page_off.addend) |a| target_addr + a else target_addr;
137 const narrowed = @truncate(u12, ta);
138
139 log.debug(" | narrowed address within the page 0x{x}", .{narrowed});
140 log.debug(" | {s} opcode", .{page_off.op_kind});
141
142 var inst = page_off.inst;
143 if (page_off.op_kind == .arithmetic) {
144 inst.AddSubtractImmediate.imm12 = narrowed;
145 } else {
146 const offset: u12 = blk: {
147 if (inst.LoadStoreRegister.size == 0) {
148 if (inst.LoadStoreRegister.v == 1) {
149 // 128-bit SIMD is scaled by 16.
150 break :blk try math.divExact(u12, narrowed, 16);
151 }
152 // Otherwise, 8-bit SIMD or ldrb.
153 break :blk narrowed;
154 } else {
155 const denom: u4 = try math.powi(u4, 2, inst.LoadStoreRegister.size);
156 break :blk try math.divExact(u12, narrowed, denom);
157 }
158 };
159 inst.LoadStoreRegister.offset = offset;
160 }
161
162 mem.writeIntLittle(u32, page_off.base.code.*[page_off.base.offset..], inst.toU32());
163 }
164 };
165
166 pub const GotPage = struct {
167 base: Relocation,
168 /// Always .PCRelativeAddress
169 inst: aarch64.Instruction,
170
171 pub const base_type: Relocation.Type = .got_page;
172
173 pub fn resolve(page: GotPage, source_addr: u64, target_addr: u64) !void {
174 const source_page = @intCast(i32, source_addr >> 12);
175 const target_page = @intCast(i32, target_addr >> 12);
176 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
177
178 log.debug(" | moving by {} pages", .{pages});
179
180 var inst = page.inst;
181 inst.PCRelativeAddress.immhi = @truncate(u19, pages >> 2);
182 inst.PCRelativeAddress.immlo = @truncate(u2, pages);
183
184 mem.writeIntLittle(u32, page.base.code.*[page.base.offset..], inst.toU32());
185 }
186 };
187
188 pub const GotPageOff = struct {
189 base: Relocation,
190 /// Always .LoadStoreRegister with size = 3 for GOT indirection
191 inst: aarch64.Instruction,
192
193 pub const base_type: Relocation.Type = .got_page_off;
194
195 pub fn resolve(page_off: GotPageOff, target_addr: u64) !void {
196 const narrowed = @truncate(u12, target_addr);
197
198 log.debug(" | narrowed address within the page 0x{x}", .{narrowed});
199
200 var inst = page_off.inst;
201 const offset = try math.divExact(u12, narrowed, 8);
202 inst.LoadStoreRegister.offset = offset;
203
204 mem.writeIntLittle(u32, page_off.base.code.*[page_off.base.offset..], inst.toU32());
205 }
206 };
207
208 pub const TlvpPage = struct {
209 base: Relocation,
210 /// Always .PCRelativeAddress
211 inst: aarch64.Instruction,
212
213 pub const base_type: Relocation.Type = .tlvp_page;
214
215 pub fn resolve(page: TlvpPage, source_addr: u64, target_addr: u64) !void {
216 const source_page = @intCast(i32, source_addr >> 12);
217 const target_page = @intCast(i32, target_addr >> 12);
218 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
219
220 log.debug(" | moving by {} pages", .{pages});
221
222 var inst = page.inst;
223 inst.PCRelativeAddress.immhi = @truncate(u19, pages >> 2);
224 inst.PCRelativeAddress.immlo = @truncate(u2, pages);
225
226 mem.writeIntLittle(u32, page.base.code.*[page.base.offset..], inst.toU32());
227 }
228 };
229
230 pub const TlvpPageOff = struct {
231 base: Relocation,
232 /// Always .AddSubtractImmediate regardless of the source instruction.
233 /// This means, we always rewrite the instruction to add even if the
234 /// source instruction was an ldr.
235 inst: aarch64.Instruction,
236
237 pub const base_type: Relocation.Type = .tlvp_page_off;
238
239 pub fn resolve(page_off: TlvpPageOff, target_addr: u64) !void {
240 const narrowed = @truncate(u12, target_addr);
241
242 log.debug(" | narrowed address within the page 0x{x}", .{narrowed});
243
244 var inst = page_off.inst;
245 inst.AddSubtractImmediate.imm12 = narrowed;
246
247 mem.writeIntLittle(u32, page_off.base.code.*[page_off.base.offset..], inst.toU32());
248 }
249 };
250};
251
252pub fn parse(allocator: *Allocator, code: *[]u8, relocs: []const macho.relocation_info) ![]*Relocation {
253 var it = RelocIterator{
254 .buffer = relocs,
255 };
256
257 var parser = Parser{
258 .allocator = allocator,
259 .it = &it,
260 .code = code,
261 .parsed = std.ArrayList(*Relocation).init(allocator),
262 };
263 defer parser.deinit();
264
265 return parser.parse();
266}
267
268const RelocIterator = struct {
269 buffer: []const macho.relocation_info,
270 index: i64 = -1,
271
272 pub fn next(self: *RelocIterator) ?macho.relocation_info {
273 self.index += 1;
274 if (self.index < self.buffer.len) {
275 const reloc = self.buffer[@intCast(u64, self.index)];
276 log.warn("{s}", .{@intToEnum(macho.reloc_type_arm64, reloc.r_type)});
277 log.warn(" | offset = {}", .{reloc.r_address});
278 log.warn(" | PC = {}", .{reloc.r_pcrel == 1});
279 log.warn(" | length = {}", .{reloc.r_length});
280 log.warn(" | symbolnum = {}", .{reloc.r_symbolnum});
281 log.warn(" | extern = {}", .{reloc.r_extern == 1});
282 return reloc;
283 }
284 return null;
285 }
286
287 pub fn peek(self: *RelocIterator) ?macho.reloc_type_arm64 {
288 if (self.index + 1 < self.buffer.len) {
289 const reloc = self.buffer[@intCast(u64, self.index + 1)];
290 const tt = @intToEnum(macho.reloc_type_arm64, reloc.r_type);
291 return tt;
292 }
293 return null;
294 }
295};
296
297const Parser = struct {
298 allocator: *Allocator,
299 it: *RelocIterator,
300 code: *[]u8,
301 parsed: std.ArrayList(*Relocation),
302 addend: ?u32 = null,
303 subtractor: ?Relocation.Target = null,
304
305 fn deinit(parser: *Parser) void {
306 parser.parsed.deinit();
307 }
308
309 fn parse(parser: *Parser) ![]*Relocation {
310 while (parser.it.next()) |reloc| {
311 switch (@intToEnum(macho.reloc_type_arm64, reloc.r_type)) {
312 .ARM64_RELOC_BRANCH26 => {
313 try parser.parseBranch(reloc);
314 },
315 .ARM64_RELOC_SUBTRACTOR => {
316 try parser.parseSubtractor(reloc);
317 },
318 .ARM64_RELOC_UNSIGNED => {
319 try parser.parseUnsigned(reloc);
320 },
321 .ARM64_RELOC_ADDEND => {
322 try parser.parseAddend(reloc);
323 },
324 .ARM64_RELOC_PAGE21,
325 .ARM64_RELOC_GOT_LOAD_PAGE21,
326 .ARM64_RELOC_TLVP_LOAD_PAGE21,
327 => {
328 try parser.parsePage(reloc);
329 },
330 .ARM64_RELOC_PAGEOFF12 => {
331 try parser.parsePageOff(reloc);
332 },
333 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {
334 try parser.parseGotLoadPageOff(reloc);
335 },
336 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
337 try parser.parseTlvpLoadPageOff(reloc);
338 },
339 .ARM64_RELOC_POINTER_TO_GOT => {
340 return error.ToDoRelocPointerToGot;
341 },
342 }
343 }
344
345 return parser.parsed.toOwnedSlice();
346 }
347
348 fn parseAddend(parser: *Parser, reloc: macho.relocation_info) !void {
349 const reloc_type = @intToEnum(macho.reloc_type_arm64, reloc.r_type);
350 assert(reloc_type == .ARM64_RELOC_ADDEND);
351 assert(reloc.r_pcrel == 0);
352 assert(reloc.r_extern == 0);
353 assert(parser.addend == null);
354
355 parser.addend = reloc.r_symbolnum;
356
357 // Verify ADDEND is followed by a load.
358 if (parser.it.peek()) |tt| {
359 switch (tt) {
360 .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {},
361 else => |other| {
362 log.err("unexpected relocation type: expected PAGE21 or PAGEOFF12, found {s}", .{other});
363 return error.UnexpectedRelocationType;
364 },
365 }
366 } else {
367 log.err("unexpected end of stream", .{});
368 return error.UnexpectedEndOfStream;
369 }
370 }
371
372 fn parseBranch(parser: *Parser, reloc: macho.relocation_info) !void {
373 const reloc_type = @intToEnum(macho.reloc_type_arm64, reloc.r_type);
374 assert(reloc_type == .ARM64_RELOC_BRANCH26);
375 assert(reloc.r_pcrel == 1);
376 assert(reloc.r_length == 2);
377
378 const offset = @intCast(u32, reloc.r_address);
379 const inst = parser.code.*[offset..][0..4];
380 const parsed_inst = aarch64.Instruction{ .UnconditionalBranchImmediate = mem.bytesToValue(
381 meta.TagPayload(
382 aarch64.Instruction,
383 aarch64.Instruction.UnconditionalBranchImmediate,
384 ),
385 inst,
386 ) };
387
388 var branch = try parser.allocator.create(Relocation.Branch);
389 errdefer parser.allocator.destroy(branch);
390
391 const target = Relocation.Target.from_reloc(reloc);
392
393 branch.* = .{
394 .base = .{
395 .@"type" = .branch,
396 .code = parser.code,
397 .offset = @intCast(u32, reloc.r_address),
398 .target = target,
399 },
400 .inst = parsed_inst,
401 };
402
403 log.warn(" | emitting {}", .{branch});
404 try parser.parsed.append(&branch.base);
405 }
406
407 fn parsePage(parser: *Parser, reloc: macho.relocation_info) !void {
408 assert(reloc.r_pcrel == 1);
409 assert(reloc.r_length == 2);
410
411 const reloc_type = @intToEnum(macho.reloc_type_arm64, reloc.r_type);
412 const target = Relocation.Target.from_reloc(reloc);
413
414 const offset = @intCast(u32, reloc.r_address);
415 const inst = parser.code.*[offset..][0..4];
416 const parsed_inst = aarch64.Instruction{ .PCRelativeAddress = mem.bytesToValue(meta.TagPayload(
417 aarch64.Instruction,
418 aarch64.Instruction.PCRelativeAddress,
419 ), inst) };
420
421 const ptr: *Relocation = ptr: {
422 switch (reloc_type) {
423 .ARM64_RELOC_PAGE21 => {
424 defer {
425 // Reset parser's addend state
426 parser.addend = null;
427 }
428 var page = try parser.allocator.create(Relocation.Page);
429 errdefer parser.allocator.destroy(page);
430
431 page.* = .{
432 .base = .{
433 .@"type" = .page,
434 .code = parser.code,
435 .offset = offset,
436 .target = target,
437 },
438 .addend = parser.addend,
439 .inst = parsed_inst,
440 };
441
442 log.warn(" | emitting {}", .{page});
443
444 break :ptr &page.base;
445 },
446 .ARM64_RELOC_GOT_LOAD_PAGE21 => {
447 var page = try parser.allocator.create(Relocation.GotPage);
448 errdefer parser.allocator.destroy(page);
449
450 page.* = .{
451 .base = .{
452 .@"type" = .got_page,
453 .code = parser.code,
454 .offset = offset,
455 .target = target,
456 },
457 .inst = parsed_inst,
458 };
459
460 log.warn(" | emitting {}", .{page});
461
462 break :ptr &page.base;
463 },
464 .ARM64_RELOC_TLVP_LOAD_PAGE21 => {
465 var page = try parser.allocator.create(Relocation.TlvpPage);
466 errdefer parser.allocator.destroy(page);
467
468 page.* = .{
469 .base = .{
470 .@"type" = .tlvp_page,
471 .code = parser.code,
472 .offset = offset,
473 .target = target,
474 },
475 .inst = parsed_inst,
476 };
477
478 log.warn(" | emitting {}", .{page});
479
480 break :ptr &page.base;
481 },
482 else => unreachable,
483 }
484 };
485
486 try parser.parsed.append(ptr);
487 }
488
489 fn parsePageOff(parser: *Parser, reloc: macho.relocation_info) !void {
490 defer {
491 // Reset parser's addend state
492 parser.addend = null;
493 }
494
495 const reloc_type = @intToEnum(macho.reloc_type_arm64, reloc.r_type);
496 assert(reloc_type == .ARM64_RELOC_PAGEOFF12);
497 assert(reloc.r_pcrel == 0);
498 assert(reloc.r_length == 2);
499
500 const offset = @intCast(u32, reloc.r_address);
501 const inst = parser.code.*[offset..][0..4];
502
503 var op_kind: Relocation.PageOff.OpKind = undefined;
504 var parsed_inst: aarch64.Instruction = undefined;
505 if (isArithmeticOp(inst)) {
506 op_kind = .arithmetic;
507 parsed_inst = .{ .AddSubtractImmediate = mem.bytesToValue(meta.TagPayload(
508 aarch64.Instruction,
509 aarch64.Instruction.AddSubtractImmediate,
510 ), inst) };
511 } else {
512 op_kind = .load_store;
513 parsed_inst = .{ .LoadStoreRegister = mem.bytesToValue(meta.TagPayload(
514 aarch64.Instruction,
515 aarch64.Instruction.LoadStoreRegister,
516 ), inst) };
517 }
518 const target = Relocation.Target.from_reloc(reloc);
519
520 var page_off = try parser.allocator.create(Relocation.PageOff);
521 errdefer parser.allocator.destroy(page_off);
522
523 page_off.* = .{
524 .base = .{
525 .@"type" = .page_off,
526 .code = parser.code,
527 .offset = offset,
528 .target = target,
529 },
530 .op_kind = op_kind,
531 .inst = parsed_inst,
532 .addend = parser.addend,
533 };
534
535 log.warn(" | emitting {}", .{page_off});
536 try parser.parsed.append(&page_off.base);
537 }
538
539 fn parseGotLoadPageOff(parser: *Parser, reloc: macho.relocation_info) !void {
540 const reloc_type = @intToEnum(macho.reloc_type_arm64, reloc.r_type);
541 assert(reloc_type == .ARM64_RELOC_GOT_LOAD_PAGEOFF12);
542 assert(reloc.r_pcrel == 0);
543 assert(reloc.r_length == 2);
544
545 const offset = @intCast(u32, reloc.r_address);
546 const inst = parser.code.*[offset..][0..4];
547 assert(!isArithmeticOp(inst));
548
549 const parsed_inst = mem.bytesToValue(meta.TagPayload(
550 aarch64.Instruction,
551 aarch64.Instruction.LoadStoreRegister,
552 ), inst);
553 assert(parsed_inst.size == 3);
554
555 const target = Relocation.Target.from_reloc(reloc);
556
557 var page_off = try parser.allocator.create(Relocation.GotPageOff);
558 errdefer parser.allocator.destroy(page_off);
559
560 page_off.* = .{
561 .base = .{
562 .@"type" = .got_page_off,
563 .code = parser.code,
564 .offset = offset,
565 .target = target,
566 },
567 .inst = .{
568 .LoadStoreRegister = parsed_inst,
569 },
570 };
571
572 log.warn(" | emitting {}", .{page_off});
573 try parser.parsed.append(&page_off.base);
574 }
575
576 fn parseTlvpLoadPageOff(parser: *Parser, reloc: macho.relocation_info) !void {
577 const reloc_type = @intToEnum(macho.reloc_type_arm64, reloc.r_type);
578 assert(reloc_type == .ARM64_RELOC_TLVP_LOAD_PAGEOFF12);
579 assert(reloc.r_pcrel == 0);
580 assert(reloc.r_length == 2);
581
582 const RegInfo = struct {
583 rd: u5,
584 rn: u5,
585 size: u1,
586 };
587
588 const offset = @intCast(u32, reloc.r_address);
589 const inst = parser.code.*[offset..][0..4];
590 const parsed: RegInfo = parsed: {
591 if (isArithmeticOp(inst)) {
592 const parsed_inst = mem.bytesAsValue(meta.TagPayload(
593 aarch64.Instruction,
594 aarch64.Instruction.AddSubtractImmediate,
595 ), inst);
596 break :parsed .{
597 .rd = parsed_inst.rd,
598 .rn = parsed_inst.rn,
599 .size = parsed_inst.sf,
600 };
601 } else {
602 const parsed_inst = mem.bytesAsValue(meta.TagPayload(
603 aarch64.Instruction,
604 aarch64.Instruction.LoadStoreRegister,
605 ), inst);
606 break :parsed .{
607 .rd = parsed_inst.rt,
608 .rn = parsed_inst.rn,
609 .size = @truncate(u1, parsed_inst.size),
610 };
611 }
612 };
613
614 const target = Relocation.Target.from_reloc(reloc);
615
616 var page_off = try parser.allocator.create(Relocation.TlvpPageOff);
617 errdefer parser.allocator.destroy(page_off);
618
619 page_off.* = .{
620 .base = .{
621 .@"type" = .tlvp_page_off,
622 .code = parser.code,
623 .offset = @intCast(u32, reloc.r_address),
624 .target = target,
625 },
626 .inst = .{
627 .AddSubtractImmediate = .{
628 .rd = parsed.rd,
629 .rn = parsed.rn,
630 .imm12 = 0, // This will be filled when target addresses are known.
631 .sh = 0,
632 .s = 0,
633 .op = 0,
634 .sf = parsed.size,
635 },
636 },
637 };
638
639 log.warn(" | emitting {}", .{page_off});
640 try parser.parsed.append(&page_off.base);
641 }
642
643 fn parseSubtractor(parser: *Parser, reloc: macho.relocation_info) !void {
644 const reloc_type = @intToEnum(macho.reloc_type_arm64, reloc.r_type);
645 assert(reloc_type == .ARM64_RELOC_SUBTRACTOR);
646 assert(reloc.r_pcrel == 0);
647 assert(parser.subtractor == null);
648
649 parser.subtractor = Relocation.Target.from_reloc(reloc);
650
651 // Verify SUBTRACTOR is followed by UNSIGNED.
652 if (parser.it.peek()) |tt| {
653 if (tt != .ARM64_RELOC_UNSIGNED) {
654 log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{tt});
655 return error.UnexpectedRelocationType;
656 }
657 } else {
658 log.err("unexpected end of stream", .{});
659 return error.UnexpectedEndOfStream;
660 }
661 }
662
663 fn parseUnsigned(parser: *Parser, reloc: macho.relocation_info) !void {
664 defer {
665 // Reset parser's subtractor state
666 parser.subtractor = null;
667 }
668
669 const reloc_type = @intToEnum(macho.reloc_type_arm64, reloc.r_type);
670 assert(reloc_type == .ARM64_RELOC_UNSIGNED);
671 assert(reloc.r_pcrel == 0);
672
673 var unsigned = try parser.allocator.create(Relocation.Unsigned);
674 errdefer parser.allocator.destroy(unsigned);
675
676 const target = Relocation.Target.from_reloc(reloc);
677 const is_64bit: bool = switch (reloc.r_length) {
678 3 => true,
679 2 => false,
680 else => unreachable,
681 };
682 const offset = @intCast(u32, reloc.r_address);
683 const addend: i64 = if (is_64bit)
684 mem.readIntLittle(i64, parser.code.*[offset..][0..8])
685 else
686 mem.readIntLittle(i32, parser.code.*[offset..][0..4]);
687
688 unsigned.* = .{
689 .base = .{
690 .@"type" = .unsigned,
691 .code = parser.code,
692 .offset = offset,
693 .target = target,
694 },
695 .subtractor = parser.subtractor,
696 .is_64bit = is_64bit,
697 .addend = addend,
698 };
699
700 log.warn(" | emitting {}", .{unsigned});
701 try parser.parsed.append(&unsigned.base);
702 }
703};
704
705fn isArithmeticOp(inst: *const [4]u8) callconv(.Inline) bool {
706 const group_decode = @truncate(u5, inst[3]);
707 return ((group_decode >> 2) == 4);
708}