authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-17 00:00:18+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-17 01:03:40+02:00
log407745a5e91685d52189548620d112a4b34c8127
treee1927fa0fb277ec135b8e1d76dc8c40647734778
parent54a403d4ff9e20daf1843725012ae44ec828a833

zld: simplify and move Relocations into TextBlock

It makes sense to have them as a dependent type since they only ever deal with TextBlocks. Simplify Relocations to rely on symbol indices and symbol resolver rather than pointers.

5 files changed, 937 insertions(+), 976 deletions(-)

CMakeLists.txt-1
...@@ -586,7 +586,6 @@ set(ZIG_STAGE2_SOURCES...@@ -586,7 +586,6 @@ set(ZIG_STAGE2_SOURCES
586 "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"586 "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"
587 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"587 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
588 "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig"588 "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig"
589 "${CMAKE_SOURCE_DIR}/src/link/MachO/reloc.zig"
590 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"589 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
591 "${CMAKE_SOURCE_DIR}/src/link/tapi.zig"590 "${CMAKE_SOURCE_DIR}/src/link/tapi.zig"
592 "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig"591 "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig"
src/link/MachO/Object.zig+14-103
...@@ -290,19 +290,6 @@ pub fn readLoadCommands(self: *Object, reader: anytype) !void {...@@ -290,19 +290,6 @@ pub fn readLoadCommands(self: *Object, reader: anytype) !void {
290 }290 }
291}291}
292292
293fn findFirst(comptime T: type, haystack: []T, start: usize, predicate: anytype) usize {
294 if (!@hasDecl(@TypeOf(predicate), "predicate"))
295 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
296
297 if (start == haystack.len) return start;
298
299 var i = start;
300 while (i < haystack.len) : (i += 1) {
301 if (predicate.predicate(haystack[i])) break;
302 }
303 return i;
304}
305
306const NlistWithIndex = struct {293const NlistWithIndex = struct {
307 nlist: macho.nlist_64,294 nlist: macho.nlist_64,
308 index: u32,295 index: u32,
...@@ -315,44 +302,29 @@ const NlistWithIndex = struct {...@@ -315,44 +302,29 @@ const NlistWithIndex = struct {
315 const Predicate = struct {302 const Predicate = struct {
316 addr: u64,303 addr: u64,
317304
318 fn predicate(self: @This(), symbol: NlistWithIndex) bool {305 pub fn predicate(self: @This(), symbol: NlistWithIndex) bool {
319 return symbol.nlist.n_value >= self.addr;306 return symbol.nlist.n_value >= self.addr;
320 }307 }
321 };308 };
322309
323 const start = findFirst(NlistWithIndex, symbols, 0, Predicate{ .addr = sect.addr });310 const start = Zld.findFirst(NlistWithIndex, symbols, 0, Predicate{ .addr = sect.addr });
324 const end = findFirst(NlistWithIndex, symbols, start, Predicate{ .addr = sect.addr + sect.size });311 const end = Zld.findFirst(NlistWithIndex, symbols, start, Predicate{ .addr = sect.addr + sect.size });
325312
326 return symbols[start..end];313 return symbols[start..end];
327 }314 }
328};315};
329316
330fn filterRelocs(relocs: []macho.relocation_info, start_addr: u64, end_addr: u64) []macho.relocation_info {
331 const Predicate = struct {
332 addr: u64,
333
334 fn predicate(self: @This(), rel: macho.relocation_info) bool {
335 return rel.r_address < self.addr;
336 }
337 };
338
339 const start = findFirst(macho.relocation_info, relocs, 0, Predicate{ .addr = end_addr });
340 const end = findFirst(macho.relocation_info, relocs, start, Predicate{ .addr = start_addr });
341
342 return relocs[start..end];
343}
344
345fn filterDice(dices: []macho.data_in_code_entry, start_addr: u64, end_addr: u64) []macho.data_in_code_entry {317fn filterDice(dices: []macho.data_in_code_entry, start_addr: u64, end_addr: u64) []macho.data_in_code_entry {
346 const Predicate = struct {318 const Predicate = struct {
347 addr: u64,319 addr: u64,
348320
349 fn predicate(self: @This(), dice: macho.data_in_code_entry) bool {321 pub fn predicate(self: @This(), dice: macho.data_in_code_entry) bool {
350 return dice.offset >= self.addr;322 return dice.offset >= self.addr;
351 }323 }
352 };324 };
353325
354 const start = findFirst(macho.data_in_code_entry, dices, 0, Predicate{ .addr = start_addr });326 const start = Zld.findFirst(macho.data_in_code_entry, dices, 0, Predicate{ .addr = start_addr });
355 const end = findFirst(macho.data_in_code_entry, dices, start, Predicate{ .addr = end_addr });327 const end = Zld.findFirst(macho.data_in_code_entry, dices, start, Predicate{ .addr = end_addr });
356328
357 return dices[start..end];329 return dices[start..end];
358}330}
...@@ -483,10 +455,10 @@ const TextBlockParser = struct {...@@ -483,10 +455,10 @@ const TextBlockParser = struct {
483 }455 }
484 }456 }
485457
486 const relocs = filterRelocs(self.relocs, start_addr, end_addr);458 try block.parseRelocsFromObject(relocs, object, .{
487 if (relocs.len > 0) {459 .base_addr = start_addr,
488 try self.object.parseRelocs(self.zld, relocs, block, start_addr);460 .zld = self.zld,
489 }461 });
490462
491 if (self.zld.has_dices) {463 if (self.zld.has_dices) {
492 const dices = filterDice(464 const dices = filterDice(
...@@ -745,8 +717,6 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {...@@ -745,8 +717,6 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
745 .n_desc = 0,717 .n_desc = 0,
746 .n_value = sect.addr,718 .n_value = sect.addr,
747 });719 });
748 const block_local = &zld.locals.items[block_local_sym_index];
749 block_local.n_sect = zld.sectionId(match);
750720
751 const block = try self.allocator.create(TextBlock);721 const block = try self.allocator.create(TextBlock);
752 errdefer self.allocator.destroy(block);722 errdefer self.allocator.destroy(block);
...@@ -757,69 +727,10 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {...@@ -757,69 +727,10 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
757 block.size = sect.size;727 block.size = sect.size;
758 block.alignment = sect.@"align";728 block.alignment = sect.@"align";
759729
760 try block.relocs.ensureTotalCapacity(relocs.len);730 try block.parseRelocsFromObject(relocs, self, .{
761 for (relocs) |rel| {731 .base_addr = 0,
762 const out_rel: TextBlock.Relocation = outer: {732 .zld = zld,
763 if (rel.r_extern == 0) {733 });
764 const rel_sect_id = @intCast(u16, rel.r_symbolnum - 1);
765 const sect_sym_index = self.sections_as_symbols.get(rel_sect_id) orelse blk: {
766 const sect_sym_index = @intCast(u32, zld.locals.items.len);
767 const sect_sym_name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{
768 self.name.?,
769 segmentName(sect),
770 sectionName(sect),
771 });
772 defer self.allocator.free(sect_sym_name);
773 try zld.locals.append(zld.allocator, .{
774 .n_strx = try zld.makeString(sect_sym_name),
775 .n_type = macho.N_SECT,
776 .n_sect = 0,
777 .n_desc = 0,
778 .n_value = 0,
779 });
780 try self.sections_as_symbols.putNoClobber(self.allocator, rel_sect_id, sect_sym_index);
781 break :blk sect_sym_index;
782 };
783 break :outer .{
784 .inner = rel,
785 .where = .local,
786 .where_index = sect_sym_index,
787 };
788 }
789
790 const rel_sym = self.symtab.items[rel.r_symbolnum];
791 const rel_sym_name = self.getString(rel_sym.n_strx);
792
793 if (Zld.symbolIsSect(rel_sym) and !Zld.symbolIsExt(rel_sym)) {
794 const where_index = self.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
795 break :outer .{
796 .inner = rel,
797 .where = .local,
798 .where_index = where_index,
799 };
800 }
801
802 const resolv = zld.symbol_resolver.get(rel_sym_name) orelse unreachable;
803 switch (resolv.where) {
804 .global => {
805 break :outer .{
806 .inner = rel,
807 .where = .local,
808 .where_index = resolv.local_sym_index,
809 };
810 },
811 .import => {
812 break :outer .{
813 .inner = rel,
814 .where = .import,
815 .where_index = resolv.where_index,
816 };
817 },
818 else => unreachable,
819 }
820 };
821 block.relocs.appendAssumeCapacity(out_rel);
822 }
823734
824 if (zld.has_dices) {735 if (zld.has_dices) {
825 const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + sect.size);736 const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + sect.size);
src/link/MachO/TextBlock.zig+887-18
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const TextBlock = @This();1const TextBlock = @This();
22
3const std = @import("std");3const std = @import("std");
4const assert = std.debug.assert;
4const commands = @import("commands.zig");5const commands = @import("commands.zig");
5const log = std.log.scoped(.text_block);6const log = std.log.scoped(.text_block);
6const macho = std.macho;7const macho = std.macho;
...@@ -8,6 +9,7 @@ const mem = std.mem;...@@ -8,6 +9,7 @@ const mem = std.mem;
89
9const Allocator = mem.Allocator;10const Allocator = mem.Allocator;
10const Arch = std.Target.Cpu.Arch;11const Arch = std.Target.Cpu.Arch;
12const Object = @import("Object.zig");
11const Zld = @import("Zld.zig");13const Zld = @import("Zld.zig");
1214
13allocator: *Allocator,15allocator: *Allocator,
...@@ -102,12 +104,396 @@ pub const Stab = union(enum) {...@@ -102,12 +104,396 @@ pub const Stab = union(enum) {
102};104};
103105
104pub const Relocation = struct {106pub const Relocation = struct {
105 inner: macho.relocation_info,107 /// Offset within the `block`s code buffer.
108 /// Note relocation size can be inferred by relocation's kind.
109 offset: u32,
110
106 where: enum {111 where: enum {
107 local,112 local,
108 import,113 import,
109 },114 },
115
110 where_index: u32,116 where_index: u32,
117
118 payload: union(enum) {
119 unsigned: Unsigned,
120 branch: Branch,
121 page: Page,
122 page_off: PageOff,
123 pointer_to_got: PointerToGot,
124 signed: Signed,
125 load: Load,
126 },
127
128 const ResolveArgs = struct {
129 block: *TextBlock,
130 offset: u32,
131 source_addr: u64,
132 target_addr: u64,
133 zld: *Zld,
134 };
135
136 pub const Unsigned = struct {
137 subtractor: ?u32,
138
139 /// Addend embedded directly in the relocation slot
140 addend: i64,
141
142 /// Extracted from r_length:
143 /// => 3 implies true
144 /// => 2 implies false
145 /// => * is unreachable
146 is_64bit: bool,
147
148 pub fn resolve(self: Unsigned, args: ResolveArgs) !void {
149 const result = if (self.subtractor) |subtractor|
150 @intCast(i64, args.target_addr) - @intCast(i64, subtractor.payload.regular.address) + self.addend
151 else
152 @intCast(i64, args.target_addr) + self.addend;
153
154 if (self.is_64bit) {
155 mem.writeIntLittle(u64, args.block.code[args.offset..][0..8], @bitCast(u64, result));
156 } else {
157 mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @truncate(u32, @bitCast(u64, result)));
158 }
159 }
160
161 pub fn format(self: Unsigned, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
162 _ = fmt;
163 _ = options;
164 try std.fmt.format(writer, "Unsigned {{ ", .{});
165 if (self.subtractor) |sub| {
166 try std.fmt.format(writer, ".subtractor = {}, ", .{sub});
167 }
168 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
169 const length: usize = if (self.is_64bit) 8 else 4;
170 try std.fmt.format(writer, ".length = {}, ", .{length});
171 try std.fmt.format(writer, "}}", .{});
172 }
173 };
174
175 pub const Branch = struct {
176 arch: Arch,
177
178 pub fn resolve(self: Branch, args: ResolveArgs) !void {
179 switch (self.arch) {
180 .aarch64 => {
181 const displacement = try math.cast(
182 i28,
183 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr),
184 );
185 const code = args.block.code[args.offset..][0..4];
186 var inst = aarch64.Instruction{
187 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
188 aarch64.Instruction,
189 aarch64.Instruction.unconditional_branch_immediate,
190 ), code),
191 };
192 inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));
193 mem.writeIntLittle(u32, code, inst.toU32());
194 },
195 .x86_64 => {
196 const displacement = try math.cast(
197 i32,
198 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4,
199 );
200 mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, displacement));
201 },
202 else => return error.UnsupportedCpuArchitecture,
203 }
204 }
205
206 pub fn format(self: Branch, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
207 _ = self;
208 _ = fmt;
209 _ = options;
210 try std.fmt.format(writer, "Branch {{}}", .{});
211 }
212 };
213
214 pub const Page = struct {
215 kind: enum {
216 page,
217 got,
218 tlvp,
219 },
220 addend: u32 = 0,
221
222 pub fn resolve(self: Page, args: ResolveArgs) !void {
223 const target_addr = args.target_addr + self.addend;
224 const source_page = @intCast(i32, args.source_addr >> 12);
225 const target_page = @intCast(i32, target_addr >> 12);
226 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
227
228 const code = args.block.code[args.offset..][0..4];
229 var inst = aarch64.Instruction{
230 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
231 aarch64.Instruction,
232 aarch64.Instruction.pc_relative_address,
233 ), code),
234 };
235 inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
236 inst.pc_relative_address.immlo = @truncate(u2, pages);
237
238 mem.writeIntLittle(u32, code, inst.toU32());
239 }
240
241 pub fn format(self: Page, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
242 _ = fmt;
243 _ = options;
244 try std.fmt.format(writer, "Page {{ ", .{});
245 switch (self.kind) {
246 .page => {},
247 .got => {
248 try std.fmt.format(writer, ".got, ", .{});
249 },
250 .tlvp => {
251 try std.fmt.format(writer, ".tlvp", .{});
252 },
253 }
254 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
255 try std.fmt.format(writer, "}}", .{});
256 }
257 };
258
259 pub const PageOff = struct {
260 kind: enum {
261 page,
262 got,
263 tlvp,
264 },
265 addend: u32 = 0,
266 op_kind: ?OpKind = null,
267
268 pub const OpKind = enum {
269 arithmetic,
270 load,
271 };
272
273 pub fn resolve(self: PageOff, args: ResolveArgs) !void {
274 const code = args.block.code[args.offset..][0..4];
275
276 switch (self.kind) {
277 .page => {
278 const target_addr = args.target_addr + self.addend;
279 const narrowed = @truncate(u12, target_addr);
280
281 const op_kind = self.op_kind orelse unreachable;
282 var inst: aarch64.Instruction = blk: {
283 switch (op_kind) {
284 .arithmetic => {
285 break :blk .{
286 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
287 aarch64.Instruction,
288 aarch64.Instruction.add_subtract_immediate,
289 ), code),
290 };
291 },
292 .load => {
293 break :blk .{
294 .load_store_register = mem.bytesToValue(meta.TagPayload(
295 aarch64.Instruction,
296 aarch64.Instruction.load_store_register,
297 ), code),
298 };
299 },
300 }
301 };
302
303 if (op_kind == .arithmetic) {
304 inst.add_subtract_immediate.imm12 = narrowed;
305 } else {
306 const offset: u12 = blk: {
307 if (inst.load_store_register.size == 0) {
308 if (inst.load_store_register.v == 1) {
309 // 128-bit SIMD is scaled by 16.
310 break :blk try math.divExact(u12, narrowed, 16);
311 }
312 // Otherwise, 8-bit SIMD or ldrb.
313 break :blk narrowed;
314 } else {
315 const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size);
316 break :blk try math.divExact(u12, narrowed, denom);
317 }
318 };
319 inst.load_store_register.offset = offset;
320 }
321
322 mem.writeIntLittle(u32, code, inst.toU32());
323 },
324 .got => {
325 const narrowed = @truncate(u12, args.target_addr);
326 var inst: aarch64.Instruction = .{
327 .load_store_register = mem.bytesToValue(meta.TagPayload(
328 aarch64.Instruction,
329 aarch64.Instruction.load_store_register,
330 ), code),
331 };
332 const offset = try math.divExact(u12, narrowed, 8);
333 inst.load_store_register.offset = offset;
334 mem.writeIntLittle(u32, code, inst.toU32());
335 },
336 .tlvp => {
337 const RegInfo = struct {
338 rd: u5,
339 rn: u5,
340 size: u1,
341 };
342 const reg_info: RegInfo = blk: {
343 if (isArithmeticOp(code)) {
344 const inst = mem.bytesToValue(meta.TagPayload(
345 aarch64.Instruction,
346 aarch64.Instruction.add_subtract_immediate,
347 ), code);
348 break :blk .{
349 .rd = inst.rd,
350 .rn = inst.rn,
351 .size = inst.sf,
352 };
353 } else {
354 const inst = mem.bytesToValue(meta.TagPayload(
355 aarch64.Instruction,
356 aarch64.Instruction.load_store_register,
357 ), code);
358 break :blk .{
359 .rd = inst.rt,
360 .rn = inst.rn,
361 .size = @truncate(u1, inst.size),
362 };
363 }
364 };
365 const narrowed = @truncate(u12, args.target_addr);
366 var inst = aarch64.Instruction{
367 .add_subtract_immediate = .{
368 .rd = reg_info.rd,
369 .rn = reg_info.rn,
370 .imm12 = narrowed,
371 .sh = 0,
372 .s = 0,
373 .op = 0,
374 .sf = reg_info.size,
375 },
376 };
377 mem.writeIntLittle(u32, code, inst.toU32());
378 },
379 }
380 }
381
382 pub fn format(self: PageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
383 _ = fmt;
384 _ = options;
385 try std.fmt.format(writer, "PageOff {{ ", .{});
386 switch (self.kind) {
387 .page => {},
388 .got => {
389 try std.fmt.format(writer, ".got, ", .{});
390 },
391 .tlvp => {
392 try std.fmt.format(writer, ".tlvp, ", .{});
393 },
394 }
395 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
396 try std.fmt.format(writer, ".op_kind = {s}, ", .{self.op_kind});
397 try std.fmt.format(writer, "}}", .{});
398 }
399 };
400
401 pub const PointerToGot = struct {
402 pub fn resolve(_: PointerToGot, args: ResolveArgs) !void {
403 const result = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr));
404 mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, result));
405 }
406
407 pub fn format(self: PointerToGot, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
408 _ = self;
409 _ = fmt;
410 _ = options;
411 try std.fmt.format(writer, "PointerToGot {{}}", .{});
412 }
413 };
414
415 pub const Signed = struct {
416 addend: i64,
417 correction: i4,
418
419 pub fn resolve(self: Signed, args: ResolveArgs) !void {
420 const target_addr = @intCast(i64, args.target_addr) + self.addend;
421 const displacement = try math.cast(
422 i32,
423 target_addr - @intCast(i64, args.source_addr) - self.correction - 4,
424 );
425 mem.writeIntLittle(u32, block.code[offset..][0..4], @bitCast(u32, displacement));
426 }
427
428 pub fn format(self: Signed, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
429 _ = fmt;
430 _ = options;
431 try std.fmt.format(writer, "Signed {{ ", .{});
432 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
433 try std.fmt.format(writer, ".correction = {}, ", .{self.correction});
434 try std.fmt.format(writer, "}}", .{});
435 }
436 };
437
438 pub const Load = struct {
439 kind: enum {
440 got,
441 tlvp,
442 },
443 addend: i32 = 0,
444
445 pub fn resolve(self: Load, block: *TextBlock, offset: u32, args: ResolveArgs) !void {
446 if (self.kind == .tlvp) {
447 // We need to rewrite the opcode from movq to leaq.
448 block.code[offset - 2] = 0x8d;
449 }
450 const displacement = try math.cast(
451 i32,
452 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4 + self.addend,
453 );
454 mem.writeIntLittle(u32, block.code[offset..][0..4], @bitCast(u32, displacement));
455 }
456
457 pub fn format(self: Load, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
458 _ = fmt;
459 _ = options;
460 try std.fmt.format(writer, "Load {{ ", .{});
461 try std.fmt.format(writer, "{s}, ", .{self.kind});
462 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
463 try std.fmt.format(writer, "}}", .{});
464 }
465 };
466
467 pub fn resolve(self: Relocation, block: *TextBlock, args: ResolveArgs) !void {
468 switch (self.payload) {
469 .unsigned => |unsigned| try unsigned.resolve(block, self.offset, args),
470 .branch => |branch| try branch.resolve(block, self.offset, args),
471 .page => |page| try page.resolve(block, self.offset, args),
472 .page_off => |page_off| try page_off.resolve(block, self.offset, args),
473 .pointer_to_got => |pointer_to_got| try pointer_to_got.resolve(block, self.offset, args),
474 .signed => |signed| try signed.resolve(block, self.offset, args),
475 .load => |load| try load.resolve(block, self.offset, args),
476 }
477 }
478
479 pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
480 try std.fmt.format(writer, "Relocation {{ ", .{});
481 try std.fmt.format(writer, ".offset = {}, ", .{self.offset});
482 try std.fmt.format(writer, ".where = {}, ", .{self.where});
483 try std.fmt.format(writer, ".where_index = {d}, ", .{self.where_index});
484
485 switch (self.payload) {
486 .unsigned => |unsigned| try unsigned.format(fmt, options, writer),
487 .branch => |branch| try branch.format(fmt, options, writer),
488 .page => |page| try page.format(fmt, options, writer),
489 .page_off => |page_off| try page_off.format(fmt, options, writer),
490 .pointer_to_got => |pointer_to_got| try pointer_to_got.format(fmt, options, writer),
491 .signed => |signed| try signed.format(fmt, options, writer),
492 .load => |load| try load.format(fmt, options, writer),
493 }
494
495 try std.fmt.format(writer, "}}", .{});
496 }
111};497};
112498
113pub fn init(allocator: *Allocator) TextBlock {499pub fn init(allocator: *Allocator) TextBlock {
...@@ -139,6 +525,462 @@ pub fn deinit(self: *TextBlock) void {...@@ -139,6 +525,462 @@ pub fn deinit(self: *TextBlock) void {
139 self.dices.deinit();525 self.dices.deinit();
140}526}
141527
528const RelocContext = struct {
529 base_addr: u64 = 0,
530 zld: *Zld,
531};
532
533fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocContext) !Relocation {
534 var parsed_rel = Relocation{
535 .offset = @intCast(u32, @intCast(u64, rel.r_address) - ctx.base_addr),
536 .where = undefined,
537 .where_index = undefined,
538 .payload = undefined,
539 };
540
541 if (rel.r_extern == 0) {
542 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
543
544 const local_sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {
545 const seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
546 const sect = seg.sections.items[sect_id];
547 const local_sym_index = @intCast(u32, ctx.zld.locals.items.len);
548 const sym_name = try std.fmt.allocPrint(ctx.zld.allocator, "l_{s}_{s}_{s}", .{
549 object.name.?,
550 commands.segmentName(sect),
551 commands.sectionName(sect),
552 });
553 defer ctx.zld.allocator.free(sym_name);
554
555 try ctx.zld.locals.append(ctx.zld.allocator, .{
556 .n_strx = try ctx.zld.makeString(sym_name),
557 .n_type = macho.N_SECT,
558 .n_sect = 0,
559 .n_desc = 0,
560 .n_value = 0,
561 });
562 try object.sections_as_symbols.putNoClobber(object.allocator, sect_id, local_sym_index);
563 break :blk local_sym_index;
564 };
565
566 parsed_rel.where = .local;
567 parsed_rel.where_index = local_sym_index;
568 } else {
569 const sym = object.symtab.items[rel.r_symbolnum];
570 const sym_name = object.getString(sym.n_strx);
571
572 if (Zld.symbolIsSect(sym) and !Zld.symbolIsExt(sym)) {
573 const where_index = object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
574 parsed_rel.where = .local;
575 parsed_rel.where_index = where_index;
576 } else {
577 const resolv = ctx.zld.symbol_resolver.get(sym_name) orelse unreachable;
578 switch (resolv.where) {
579 .global => {
580 parsed_rel.where = .local;
581 parsed_rel.where_index = resolv.local_sym_index;
582 },
583 .import => {
584 parsed_rel.where = .import;
585 parsed_rel.where_index = resolv.where_index;
586 },
587 else => unreachable,
588 }
589 }
590 }
591
592 return parsed_rel;
593}
594
595pub fn parseRelocsFromObject(
596 self: *TextBlock,
597 relocs: []macho.relocation_info,
598 object: *Object,
599 ctx: RelocContext,
600) !void {
601 const filtered_relocs = filterRelocs(relocs, ctx.base_addr, ctx.base_addr + self.size);
602 var it = RelocIterator{
603 .buffer = filtered_relocs,
604 };
605
606 var addend: u32 = 0;
607 var subtractor: ?u32 = null;
608
609 while (it.next()) |rel| {
610 if (isAddend(rel, object.arch.?)) {
611 // Addend is not a relocation with effect on the TextBlock, so
612 // parse it and carry on.
613 assert(addend == 0); // Oh no, addend was not reset!
614 addend = rel.r_symbolnum;
615
616 // Verify ADDEND is followed by a load.
617 const next = @intToEnum(macho.reloc_type_arm64, it.peek().r_type);
618 switch (next) {
619 .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {},
620 else => {
621 log.err("unexpected relocation type: expected PAGE21 or PAGEOFF12, found {s}", .{next});
622 return error.UnexpectedRelocationType;
623 },
624 }
625 continue;
626 }
627
628 if (isSubtractor(rel, object.arch.?)) {
629 // Subtractor is not a relocation with effect on the TextBlock, so
630 // parse it and carry on.
631 assert(subtractor == null); // Oh no, subtractor was not reset!
632 assert(rel.r_extern == 1);
633 const sym = object.symtab.items[rel.r_symbolnum];
634 const sym_name = object.getString(sym.n_strx);
635
636 if (Zld.symbolIsSect(sym) and !Zld.symbolIsExt(sym)) {
637 const where_index = object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
638 subtractor = where_index;
639 } else {
640 const resolv = ctx.zld.symbol_resolver.get(sym_name) orelse unreachable;
641 assert(resolv.where == .global);
642 subtractor = resolv.local_sym_index;
643 }
644
645 // Verify SUBTRACTOR is followed by UNSIGNED.
646 switch (object.arch.?) {
647 .aarch64 => {
648 const next = @intToEnum(macho.reloc_type_arm64, it.peek().r_type);
649 if (next != .ARM64_RELOC_UNSIGNED) {
650 log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next});
651 return error.UnexpectedRelocationType;
652 }
653 },
654 .x86_64 => {
655 const next = @intToEnum(macho.reloc_type_x86_64, it.peek().r_type);
656 if (next != .X86_64_RELOC_UNSIGNED) {
657 log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next});
658 return error.UnexpectedRelocationType;
659 }
660 },
661 else => unreachable,
662 }
663 continue;
664 }
665
666 var parsed_rel = try initRelocFromObject(rel, object, ctx);
667
668 switch (object.arch.?) {
669 .aarch64 => {
670 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
671 switch (rel_type) {
672 .ARM64_RELOC_ADDEND => unreachable,
673 .ARM64_RELOC_SUBTRACTOR => unreachable,
674 .ARM64_RELOC_BRANCH26 => {
675 self.parseBranch(rel, &parsed_rel, ctx);
676 },
677 .ARM64_RELOC_UNSIGNED => {
678 self.parseUnsigned(rel, &parsed_rel, subtractor, ctx);
679 subtractor = null;
680 },
681 .ARM64_RELOC_PAGE21,
682 .ARM64_RELOC_GOT_LOAD_PAGE21,
683 .ARM64_RELOC_TLVP_LOAD_PAGE21,
684 => {
685 self.parsePage(rel, &parsed_rel, addend);
686 if (rel_type == .ARM64_RELOC_PAGE21)
687 addend = 0;
688 },
689 .ARM64_RELOC_PAGEOFF12,
690 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
691 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
692 => {
693 self.parsePageOff(rel, &parsed_rel, addend, ctx);
694 if (rel_type == .ARM64_RELOC_PAGEOFF12)
695 addend = 0;
696 },
697 .ARM64_RELOC_POINTER_TO_GOT => {
698 self.parsePointerToGot(rel, &parsed_rel);
699 },
700 }
701 },
702 .x86_64 => {
703 switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
704 .X86_64_RELOC_SUBTRACTOR => unreachable,
705 .X86_64_RELOC_BRANCH => {
706 self.parseBranch(rel, &parsed_rel, ctx);
707 },
708 .X86_64_RELOC_UNSIGNED => {
709 self.parseUnsigned(rel, &parsed_rel, subtractor, ctx);
710 subtractor = null;
711 },
712 .X86_64_RELOC_SIGNED,
713 .X86_64_RELOC_SIGNED_1,
714 .X86_64_RELOC_SIGNED_2,
715 .X86_64_RELOC_SIGNED_4,
716 => {
717 self.parseSigned(rel, &parsed_rel, ctx);
718 },
719 .X86_64_RELOC_GOT_LOAD,
720 .X86_64_RELOC_GOT,
721 .X86_64_RELOC_TLV,
722 => {
723 self.parseLoad(rel, &parsed_rel);
724 },
725 }
726 },
727 else => unreachable,
728 }
729
730 try self.relocs.append(parsed_rel);
731
732 if (parsed_rel.where == .local) {
733 try self.references.put(parsed_rel.where_index, {});
734 }
735
736 const is_via_got = switch (parsed_rel.payload) {
737 .pointer_to_got => true,
738 .load => |load| load.kind == .got,
739 .page => |page| page.kind == .got,
740 .page_off => |page_off| page_off.kind == .got,
741 else => false,
742 };
743
744 if (is_via_got) blk: {
745 const key = Zld.GotIndirectionKey{
746 .where = switch (parsed_rel.where) {
747 .local => .local,
748 .import => .import,
749 },
750 .where_index = parsed_rel.where_index,
751 };
752 if (ctx.zld.got_entries.contains(key)) break :blk;
753
754 try ctx.zld.got_entries.putNoClobber(ctx.zld.allocator, key, {});
755 } else if (parsed_rel.payload == .unsigned) {
756 switch (parsed_rel.where) {
757 .import => {
758 log.warn("WAT {s}", .{ctx.zld.getString(ctx.zld.imports.items[parsed_rel.where_index].n_strx)});
759 try self.bindings.append(.{
760 .local_sym_index = parsed_rel.where_index,
761 .offset = parsed_rel.offset,
762 });
763 },
764 .local => {
765 const source_sym = ctx.zld.locals.items[self.local_sym_index];
766 const match = ctx.zld.unpackSectionId(source_sym.n_sect);
767 const seg = ctx.zld.load_commands.items[match.seg].Segment;
768 const sect = seg.sections.items[match.sect];
769 const sect_type = commands.sectionType(sect);
770
771 const should_rebase = rebase: {
772 if (!parsed_rel.payload.unsigned.is_64bit) break :rebase false;
773
774 // TODO actually, a check similar to what dyld is doing, that is, verifying
775 // that the segment is writable should be enough here.
776 const is_right_segment = blk: {
777 if (ctx.zld.data_segment_cmd_index) |idx| {
778 if (match.seg == idx) {
779 break :blk true;
780 }
781 }
782 if (ctx.zld.data_const_segment_cmd_index) |idx| {
783 if (match.seg == idx) {
784 break :blk true;
785 }
786 }
787 break :blk false;
788 };
789
790 if (!is_right_segment) break :rebase false;
791 if (sect_type != macho.S_LITERAL_POINTERS and
792 sect_type != macho.S_REGULAR and
793 sect_type != macho.S_MOD_INIT_FUNC_POINTERS and
794 sect_type != macho.S_MOD_TERM_FUNC_POINTERS)
795 {
796 break :rebase false;
797 }
798
799 break :rebase true;
800 };
801
802 if (should_rebase) {
803 try self.rebases.append(parsed_rel.offset);
804 }
805 },
806 }
807 } else if (parsed_rel.payload == .branch) blk: {
808 if (parsed_rel.where != .import) break :blk;
809 if (ctx.zld.stubs.contains(parsed_rel.where_index)) break :blk;
810
811 try ctx.zld.stubs.putNoClobber(ctx.zld.allocator, parsed_rel.where_index, {});
812 }
813 }
814}
815
816fn isAddend(rel: macho.relocation_info, arch: Arch) bool {
817 if (arch != .aarch64) return false;
818 return @intToEnum(macho.reloc_type_arm64, rel.r_type) == .ARM64_RELOC_ADDEND;
819}
820
821fn isSubtractor(rel: macho.relocation_info, arch: Arch) bool {
822 return switch (arch) {
823 .aarch64 => @intToEnum(macho.reloc_type_arm64, rel.r_type) == .ARM64_RELOC_SUBTRACTOR,
824 .x86_64 => @intToEnum(macho.reloc_type_x86_64, rel.r_type) == .X86_64_RELOC_SUBTRACTOR,
825 else => unreachable,
826 };
827}
828
829fn parseUnsigned(
830 self: TextBlock,
831 rel: macho.relocation_info,
832 out: *Relocation,
833 subtractor: ?u32,
834 ctx: RelocContext,
835) void {
836 assert(rel.r_pcrel == 0);
837
838 const is_64bit: bool = switch (rel.r_length) {
839 3 => true,
840 2 => false,
841 else => unreachable,
842 };
843
844 var addend: i64 = if (is_64bit)
845 mem.readIntLittle(i64, self.code[out.offset..][0..8])
846 else
847 mem.readIntLittle(i32, self.code[out.offset..][0..4]);
848
849 if (rel.r_extern == 0) {
850 assert(out.where == .local);
851 const target_sym = ctx.zld.locals.items[out.where_index];
852 addend -= @intCast(i64, target_sym.n_value);
853 }
854
855 out.payload = .{
856 .unsigned = .{
857 .subtractor = subtractor,
858 .is_64bit = is_64bit,
859 .addend = addend,
860 },
861 };
862}
863
864fn parseBranch(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ctx: RelocContext) void {
865 assert(rel.r_pcrel == 1);
866 assert(rel.r_length == 2);
867
868 out.payload = .{
869 .branch = .{
870 .arch = ctx.zld.target.?.cpu.arch,
871 },
872 };
873}
874
875fn parsePage(self: TextBlock, rel: macho.relocation_info, out: *Relocation, addend: u32) void {
876 _ = self;
877 assert(rel.r_pcrel == 1);
878 assert(rel.r_length == 2);
879
880 out.payload = .{
881 .page = .{
882 .kind = switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
883 .ARM64_RELOC_PAGE21 => .page,
884 .ARM64_RELOC_GOT_LOAD_PAGE21 => .got,
885 .ARM64_RELOC_TLVP_LOAD_PAGE21 => .tlvp,
886 else => unreachable,
887 },
888 .addend = addend,
889 },
890 };
891}
892
893fn parsePageOff(self: TextBlock, rel: macho.relocation_info, out: *Relocation, addend: u32, ctx: RelocContext) void {
894 assert(rel.r_pcrel == 0);
895 assert(rel.r_length == 2);
896
897 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
898 const op_kind: ?Relocation.PageOff.OpKind = blk: {
899 if (rel_type != .ARM64_RELOC_PAGEOFF12) break :blk null;
900 const op_kind: Relocation.PageOff.OpKind = if (isArithmeticOp(self.code[out.offset..][0..4]))
901 .arithmetic
902 else
903 .load;
904 break :blk op_kind;
905 };
906
907 out.payload = .{
908 .page_off = .{
909 .kind = switch (rel_type) {
910 .ARM64_RELOC_PAGEOFF12 => .page,
911 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => .got,
912 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => .tlvp,
913 else => unreachable,
914 },
915 .addend = addend,
916 .op_kind = op_kind,
917 },
918 };
919}
920
921fn parsePointerToGot(self: TextBlock, rel: macho.relocation_info, out: *Relocation) void {
922 _ = self;
923 assert(rel.r_pcrel == 1);
924 assert(rel.r_length == 2);
925
926 out.payload = .{
927 .pointer_to_got = .{},
928 };
929}
930
931fn parseSigned(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ctx: RelocContext) void {
932 assert(rel.r_pcrel == 1);
933 assert(rel.r_length == 2);
934
935 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
936 const correction: i4 = switch (rel_type) {
937 .X86_64_RELOC_SIGNED => 0,
938 .X86_64_RELOC_SIGNED_1 => 1,
939 .X86_64_RELOC_SIGNED_2 => 2,
940 .X86_64_RELOC_SIGNED_4 => 4,
941 else => unreachable,
942 };
943 var addend: i64 = mem.readIntLittle(i32, self.code[out.offset..][0..4]) + correction;
944
945 if (rel.r_extern == 0) {
946 const source_sym = ctx.zld.locals.items[self.local_sym_index];
947 const target_sym = switch (out.where) {
948 .local => ctx.zld.locals.items[out.where_index],
949 .import => ctx.zld.imports.items[out.where_index],
950 };
951 addend = @intCast(i64, source_sym.n_value + out.offset + 4) + addend - @intCast(i64, target_sym.n_value);
952 }
953
954 out.payload = .{
955 .signed = .{
956 .correction = correction,
957 .addend = addend,
958 },
959 };
960}
961
962fn parseLoad(self: TextBlock, rel: macho.relocation_info, out: *Relocation) void {
963 assert(rel.r_pcrel == 1);
964 assert(rel.r_length == 2);
965
966 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
967 const addend: i32 = if (rel_type == .X86_64_RELOC_GOT)
968 mem.readIntLittle(i32, self.code[out.offset..][0..4])
969 else
970 0;
971
972 out.payload = .{
973 .load = .{
974 .kind = switch (rel_type) {
975 .X86_64_RELOC_GOT_LOAD, .X86_64_RELOC_GOT => .got,
976 .X86_64_RELOC_TLV => .tlvp,
977 else => unreachable,
978 },
979 .addend = addend,
980 },
981 };
982}
983
142pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {984pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
143 for (self.relocs.items) |rel| {985 for (self.relocs.items) |rel| {
144 log.debug("relocating {}", .{rel});986 log.debug("relocating {}", .{rel});
...@@ -148,7 +990,15 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {...@@ -148,7 +990,15 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
148 break :blk sym.n_value + rel.offset;990 break :blk sym.n_value + rel.offset;
149 };991 };
150 const target_addr = blk: {992 const target_addr = blk: {
151 if (isGotIndirection(rel, zld.target.?.cpu.arch)) {993 const is_via_got = switch (rel.payload) {
994 .pointer_to_got => true,
995 .page => |page| page.kind == .got,
996 .page_off => |page_off| page_off.kind == .got,
997 .load => |load| load.kind == .got,
998 else => false,
999 };
1000
1001 if (is_via_got) {
152 const dc_seg = zld.load_commands.items[zld.data_const_segment_cmd_index.?].Segment;1002 const dc_seg = zld.load_commands.items[zld.data_const_segment_cmd_index.?].Segment;
153 const got = dc_seg.sections.items[zld.got_section_index.?];1003 const got = dc_seg.sections.items[zld.got_section_index.?];
154 const got_index = rel.target.got_index orelse {1004 const got_index = rel.target.got_index orelse {
...@@ -270,21 +1120,40 @@ pub fn print(self: *const TextBlock, zld: *Zld) void {...@@ -270,21 +1120,40 @@ pub fn print(self: *const TextBlock, zld: *Zld) void {
270 self.print_this(zld);1120 self.print_this(zld);
271}1121}
2721122
273fn isGotIndirection(rel: macho.relocation_info, arch: Arch) bool {1123const RelocIterator = struct {
274 return switch (arch) {1124 buffer: []const macho.relocation_info,
275 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {1125 index: i32 = -1,
276 .ARM64_RELOC_POINTER_TO_GOT,1126
277 .ARM64_RELOC_GOT_LOAD_PAGE21,1127 pub fn next(self: *RelocIterator) ?macho.relocation_info {
278 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,1128 self.index += 1;
279 => true,1129 if (self.index < self.buffer.len) {
280 else => false,1130 return self.buffer[@intCast(u32, self.index)];
281 },1131 }
282 .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {1132 return null;
283 .X86_64_RELOC_GOT,1133 }
284 .X86_64_RELOC_GOT_LOAD,1134
285 => true,1135 pub fn peek(self: RelocIterator) macho.relocation_info {
286 else => false,1136 assert(self.index + 1 < self.buffer.len);
287 },1137 return self.buffer[@intCast(u32, self.index + 1)];
288 else => unreachable,1138 }
1139};
1140
1141fn filterRelocs(relocs: []macho.relocation_info, start_addr: u64, end_addr: u64) []macho.relocation_info {
1142 const Predicate = struct {
1143 addr: u64,
1144
1145 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
1146 return rel.r_address < self.addr;
1147 }
289 };1148 };
1149
1150 const start = Zld.findFirst(macho.relocation_info, relocs, 0, Predicate{ .addr = end_addr });
1151 const end = Zld.findFirst(macho.relocation_info, relocs, start, Predicate{ .addr = start_addr });
1152
1153 return relocs[start..end];
1154}
1155
1156inline fn isArithmeticOp(inst: *const [4]u8) bool {
1157 const group_decode = @truncate(u5, inst[3]);
1158 return ((group_decode >> 2) == 4);
290}1159}
src/link/MachO/Zld.zig+36-14
...@@ -108,8 +108,8 @@ symbol_resolver: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},...@@ -108,8 +108,8 @@ symbol_resolver: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
108108
109strtab: std.ArrayListUnmanaged(u8) = .{},109strtab: std.ArrayListUnmanaged(u8) = .{},
110110
111// stubs: std.ArrayListUnmanaged(*Symbol) = .{},111stubs: std.AutoArrayHashMapUnmanaged(u32, void) = .{},
112got_entries: std.ArrayListUnmanaged(GotEntry) = .{},112got_entries: std.AutoArrayHashMapUnmanaged(GotIndirectionKey, void) = .{},
113113
114stub_helper_stubs_start_off: ?u64 = null,114stub_helper_stubs_start_off: ?u64 = null,
115115
...@@ -131,20 +131,12 @@ const SymbolWithLoc = struct {...@@ -131,20 +131,12 @@ const SymbolWithLoc = struct {
131 file: u16 = 0,131 file: u16 = 0,
132};132};
133133
134pub const GotEntry = struct {134pub const GotIndirectionKey = struct {
135 /// GOT entry can either be a local pointer or an extern (nonlazy) import.135 where: enum {
136 kind: enum {
137 local,136 local,
138 import,137 import,
139 },138 },
140139 where_index: u32,
141 /// Id to the macho.nlist_64 from the respective table: either locals or nonlazy imports.
142 /// TODO I'm more and more inclined to just manage a single, max two symbol tables
143 /// rather than 4 as we currently do, but I'll follow up in the future PR.
144 local_sym_index: u32,
145
146 /// Index of this entry in the GOT.
147 got_index: u32,
148};140};
149141
150pub const Output = struct {142pub const Output = struct {
...@@ -161,7 +153,7 @@ pub fn init(allocator: *Allocator) !Zld {...@@ -161,7 +153,7 @@ pub fn init(allocator: *Allocator) !Zld {
161}153}
162154
163pub fn deinit(self: *Zld) void {155pub fn deinit(self: *Zld) void {
164 // self.stubs.deinit(self.allocator);156 self.stubs.deinit(self.allocator);
165 self.got_entries.deinit(self.allocator);157 self.got_entries.deinit(self.allocator);
166158
167 for (self.load_commands.items) |*lc| {159 for (self.load_commands.items) |*lc| {
...@@ -3043,3 +3035,33 @@ pub fn sectionId(self: Zld, match: MatchingSection) u8 {...@@ -3043,3 +3035,33 @@ pub fn sectionId(self: Zld, match: MatchingSection) u8 {
3043 }3035 }
3044 return section;3036 return section;
3045}3037}
3038
3039pub fn unpackSectionId(self: Zld, section_id: u8) MatchingSection {
3040 var match: MatchingSection = undefined;
3041 var section: u8 = 0;
3042 outer: for (self.load_commands.items) |cmd, cmd_id| {
3043 assert(cmd == .Segment);
3044 for (cmd.Segment.sections.items) |_, sect_id| {
3045 section += 1;
3046 if (section_id == section) {
3047 match.seg = @intCast(u16, cmd_id);
3048 match.sect = @intCast(u16, sect_id);
3049 break :outer;
3050 }
3051 }
3052 }
3053 return match;
3054}
3055
3056pub fn findFirst(comptime T: type, haystack: []T, start: usize, predicate: anytype) usize {
3057 if (!@hasDecl(@TypeOf(predicate), "predicate"))
3058 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
3059
3060 if (start == haystack.len) return start;
3061
3062 var i = start;
3063 while (i < haystack.len) : (i += 1) {
3064 if (predicate.predicate(haystack[i])) break;
3065 }
3066 return i;
3067}
src/link/MachO/reloc.zig deleted-840
...@@ -1,840 +0,0 @@
1const std = @import("std");
2const aarch64 = @import("../../codegen/aarch64.zig");
3const assert = std.debug.assert;
4const commands = @import("commands.zig");
5const log = std.log.scoped(.reloc);
6const macho = std.macho;
7const math = std.math;
8const mem = std.mem;
9const meta = std.meta;
10
11const Allocator = mem.Allocator;
12const Arch = std.Target.Cpu.Arch;
13const Object = @import("Object.zig");
14const Symbol = @import("Symbol.zig");
15const TextBlock = @import("TextBlock.zig");
16const Zld = @import("Zld.zig");
17
18pub const Relocation = struct {
19 /// Offset within the `block`s code buffer.
20 /// Note relocation size can be inferred by relocation's kind.
21 offset: u32,
22
23 /// Target symbol: either a regular or a proxy.
24 target: *Symbol,
25
26 payload: union(enum) {
27 unsigned: Unsigned,
28 branch: Branch,
29 page: Page,
30 page_off: PageOff,
31 pointer_to_got: PointerToGot,
32 signed: Signed,
33 load: Load,
34 },
35
36 const ResolveArgs = struct {
37 block: *TextBlock,
38 offset: u32,
39 source_addr: u64,
40 target_addr: u64,
41 };
42
43 pub const Unsigned = struct {
44 subtractor: ?*Symbol = null,
45
46 /// Addend embedded directly in the relocation slot
47 addend: i64,
48
49 /// Extracted from r_length:
50 /// => 3 implies true
51 /// => 2 implies false
52 /// => * is unreachable
53 is_64bit: bool,
54
55 pub fn resolve(self: Unsigned, args: ResolveArgs) !void {
56 const result = if (self.subtractor) |subtractor|
57 @intCast(i64, args.target_addr) - @intCast(i64, subtractor.payload.regular.address) + self.addend
58 else
59 @intCast(i64, args.target_addr) + self.addend;
60
61 if (self.is_64bit) {
62 mem.writeIntLittle(u64, args.block.code[args.offset..][0..8], @bitCast(u64, result));
63 } else {
64 mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @truncate(u32, @bitCast(u64, result)));
65 }
66 }
67
68 pub fn format(self: Unsigned, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
69 _ = fmt;
70 _ = options;
71 try std.fmt.format(writer, "Unsigned {{ ", .{});
72 if (self.subtractor) |sub| {
73 try std.fmt.format(writer, ".subtractor = {}, ", .{sub});
74 }
75 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
76 const length: usize = if (self.is_64bit) 8 else 4;
77 try std.fmt.format(writer, ".length = {}, ", .{length});
78 try std.fmt.format(writer, "}}", .{});
79 }
80 };
81
82 pub const Branch = struct {
83 arch: Arch,
84
85 pub fn resolve(self: Branch, args: ResolveArgs) !void {
86 switch (self.arch) {
87 .aarch64 => {
88 const displacement = try math.cast(
89 i28,
90 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr),
91 );
92 const code = args.block.code[args.offset..][0..4];
93 var inst = aarch64.Instruction{
94 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
95 aarch64.Instruction,
96 aarch64.Instruction.unconditional_branch_immediate,
97 ), code),
98 };
99 inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));
100 mem.writeIntLittle(u32, code, inst.toU32());
101 },
102 .x86_64 => {
103 const displacement = try math.cast(
104 i32,
105 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4,
106 );
107 mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, displacement));
108 },
109 else => return error.UnsupportedCpuArchitecture,
110 }
111 }
112
113 pub fn format(self: Branch, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
114 _ = self;
115 _ = fmt;
116 _ = options;
117 try std.fmt.format(writer, "Branch {{}}", .{});
118 }
119 };
120
121 pub const Page = struct {
122 kind: enum {
123 page,
124 got,
125 tlvp,
126 },
127 addend: ?u32 = null,
128
129 pub fn resolve(self: Page, args: ResolveArgs) !void {
130 const target_addr = if (self.addend) |addend| args.target_addr + addend else args.target_addr;
131 const source_page = @intCast(i32, args.source_addr >> 12);
132 const target_page = @intCast(i32, target_addr >> 12);
133 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
134
135 const code = args.block.code[args.offset..][0..4];
136 var inst = aarch64.Instruction{
137 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
138 aarch64.Instruction,
139 aarch64.Instruction.pc_relative_address,
140 ), code),
141 };
142 inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
143 inst.pc_relative_address.immlo = @truncate(u2, pages);
144
145 mem.writeIntLittle(u32, code, inst.toU32());
146 }
147
148 pub fn format(self: Page, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
149 _ = fmt;
150 _ = options;
151 try std.fmt.format(writer, "Page {{ ", .{});
152 switch (self.kind) {
153 .page => {},
154 .got => {
155 try std.fmt.format(writer, ".got, ", .{});
156 },
157 .tlvp => {
158 try std.fmt.format(writer, ".tlvp", .{});
159 },
160 }
161 if (self.addend) |add| {
162 try std.fmt.format(writer, ".addend = {}, ", .{add});
163 }
164 try std.fmt.format(writer, "}}", .{});
165 }
166 };
167
168 pub const PageOff = struct {
169 kind: enum {
170 page,
171 got,
172 tlvp,
173 },
174 addend: ?u32 = null,
175 op_kind: ?OpKind = null,
176
177 pub const OpKind = enum {
178 arithmetic,
179 load,
180 };
181
182 pub fn resolve(self: PageOff, args: ResolveArgs) !void {
183 const code = args.block.code[args.offset..][0..4];
184
185 switch (self.kind) {
186 .page => {
187 const target_addr = if (self.addend) |addend| args.target_addr + addend else args.target_addr;
188 const narrowed = @truncate(u12, target_addr);
189
190 const op_kind = self.op_kind orelse unreachable;
191 var inst: aarch64.Instruction = blk: {
192 switch (op_kind) {
193 .arithmetic => {
194 break :blk .{
195 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
196 aarch64.Instruction,
197 aarch64.Instruction.add_subtract_immediate,
198 ), code),
199 };
200 },
201 .load => {
202 break :blk .{
203 .load_store_register = mem.bytesToValue(meta.TagPayload(
204 aarch64.Instruction,
205 aarch64.Instruction.load_store_register,
206 ), code),
207 };
208 },
209 }
210 };
211
212 if (op_kind == .arithmetic) {
213 inst.add_subtract_immediate.imm12 = narrowed;
214 } else {
215 const offset: u12 = blk: {
216 if (inst.load_store_register.size == 0) {
217 if (inst.load_store_register.v == 1) {
218 // 128-bit SIMD is scaled by 16.
219 break :blk try math.divExact(u12, narrowed, 16);
220 }
221 // Otherwise, 8-bit SIMD or ldrb.
222 break :blk narrowed;
223 } else {
224 const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size);
225 break :blk try math.divExact(u12, narrowed, denom);
226 }
227 };
228 inst.load_store_register.offset = offset;
229 }
230
231 mem.writeIntLittle(u32, code, inst.toU32());
232 },
233 .got => {
234 const narrowed = @truncate(u12, args.target_addr);
235 var inst: aarch64.Instruction = .{
236 .load_store_register = mem.bytesToValue(meta.TagPayload(
237 aarch64.Instruction,
238 aarch64.Instruction.load_store_register,
239 ), code),
240 };
241 const offset = try math.divExact(u12, narrowed, 8);
242 inst.load_store_register.offset = offset;
243 mem.writeIntLittle(u32, code, inst.toU32());
244 },
245 .tlvp => {
246 const RegInfo = struct {
247 rd: u5,
248 rn: u5,
249 size: u1,
250 };
251 const reg_info: RegInfo = blk: {
252 if (isArithmeticOp(code)) {
253 const inst = mem.bytesToValue(meta.TagPayload(
254 aarch64.Instruction,
255 aarch64.Instruction.add_subtract_immediate,
256 ), code);
257 break :blk .{
258 .rd = inst.rd,
259 .rn = inst.rn,
260 .size = inst.sf,
261 };
262 } else {
263 const inst = mem.bytesToValue(meta.TagPayload(
264 aarch64.Instruction,
265 aarch64.Instruction.load_store_register,
266 ), code);
267 break :blk .{
268 .rd = inst.rt,
269 .rn = inst.rn,
270 .size = @truncate(u1, inst.size),
271 };
272 }
273 };
274 const narrowed = @truncate(u12, args.target_addr);
275 var inst = aarch64.Instruction{
276 .add_subtract_immediate = .{
277 .rd = reg_info.rd,
278 .rn = reg_info.rn,
279 .imm12 = narrowed,
280 .sh = 0,
281 .s = 0,
282 .op = 0,
283 .sf = reg_info.size,
284 },
285 };
286 mem.writeIntLittle(u32, code, inst.toU32());
287 },
288 }
289 }
290
291 pub fn format(self: PageOff, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
292 _ = fmt;
293 _ = options;
294 try std.fmt.format(writer, "PageOff {{ ", .{});
295 switch (self.kind) {
296 .page => {},
297 .got => {
298 try std.fmt.format(writer, ".got, ", .{});
299 },
300 .tlvp => {
301 try std.fmt.format(writer, ".tlvp, ", .{});
302 },
303 }
304 if (self.addend) |add| {
305 try std.fmt.format(writer, ".addend = {}, ", .{add});
306 }
307 if (self.op_kind) |op| {
308 try std.fmt.format(writer, ".op_kind = {s}, ", .{op});
309 }
310 try std.fmt.format(writer, "}}", .{});
311 }
312 };
313
314 pub const PointerToGot = struct {
315 pub fn resolve(_: PointerToGot, args: ResolveArgs) !void {
316 const result = try math.cast(i32, @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr));
317 mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, result));
318 }
319
320 pub fn format(self: PointerToGot, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
321 _ = self;
322 _ = fmt;
323 _ = options;
324 try std.fmt.format(writer, "PointerToGot {{}}", .{});
325 }
326 };
327
328 pub const Signed = struct {
329 addend: i64,
330 correction: i4,
331
332 pub fn resolve(self: Signed, args: ResolveArgs) !void {
333 const target_addr = @intCast(i64, args.target_addr) + self.addend;
334 const displacement = try math.cast(
335 i32,
336 target_addr - @intCast(i64, args.source_addr) - self.correction - 4,
337 );
338 mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, displacement));
339 }
340
341 pub fn format(self: Signed, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
342 _ = fmt;
343 _ = options;
344 try std.fmt.format(writer, "Signed {{ ", .{});
345 try std.fmt.format(writer, ".addend = {}, ", .{self.addend});
346 try std.fmt.format(writer, ".correction = {}, ", .{self.correction});
347 try std.fmt.format(writer, "}}", .{});
348 }
349 };
350
351 pub const Load = struct {
352 kind: enum {
353 got,
354 tlvp,
355 },
356 addend: ?i32 = null,
357
358 pub fn resolve(self: Load, args: ResolveArgs) !void {
359 if (self.kind == .tlvp) {
360 // We need to rewrite the opcode from movq to leaq.
361 args.block.code[args.offset - 2] = 0x8d;
362 }
363 const addend = if (self.addend) |addend| addend else 0;
364 const displacement = try math.cast(
365 i32,
366 @intCast(i64, args.target_addr) - @intCast(i64, args.source_addr) - 4 + addend,
367 );
368 mem.writeIntLittle(u32, args.block.code[args.offset..][0..4], @bitCast(u32, displacement));
369 }
370
371 pub fn format(self: Load, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
372 _ = fmt;
373 _ = options;
374 try std.fmt.format(writer, "Load {{ ", .{});
375 try std.fmt.format(writer, "{s}, ", .{self.kind});
376 if (self.addend) |addend| {
377 try std.fmt.format(writer, ".addend = {}, ", .{addend});
378 }
379 try std.fmt.format(writer, "}}", .{});
380 }
381 };
382
383 pub fn resolve(self: Relocation, block: *TextBlock, source_addr: u64, target_addr: u64) !void {
384 const args = ResolveArgs{
385 .block = block,
386 .offset = self.offset,
387 .source_addr = source_addr,
388 .target_addr = target_addr,
389 };
390 switch (self.payload) {
391 .unsigned => |unsigned| try unsigned.resolve(args),
392 .branch => |branch| try branch.resolve(args),
393 .page => |page| try page.resolve(args),
394 .page_off => |page_off| try page_off.resolve(args),
395 .pointer_to_got => |pointer_to_got| try pointer_to_got.resolve(args),
396 .signed => |signed| try signed.resolve(args),
397 .load => |load| try load.resolve(args),
398 }
399 }
400
401 pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
402 try std.fmt.format(writer, "Relocation {{ ", .{});
403 try std.fmt.format(writer, ".offset = {}, ", .{self.offset});
404 try std.fmt.format(writer, ".target = {}, ", .{self.target});
405
406 switch (self.payload) {
407 .unsigned => |unsigned| try unsigned.format(fmt, options, writer),
408 .branch => |branch| try branch.format(fmt, options, writer),
409 .page => |page| try page.format(fmt, options, writer),
410 .page_off => |page_off| try page_off.format(fmt, options, writer),
411 .pointer_to_got => |pointer_to_got| try pointer_to_got.format(fmt, options, writer),
412 .signed => |signed| try signed.format(fmt, options, writer),
413 .load => |load| try load.format(fmt, options, writer),
414 }
415
416 try std.fmt.format(writer, "}}", .{});
417 }
418};
419
420pub const RelocIterator = struct {
421 buffer: []const macho.relocation_info,
422 index: i32 = -1,
423
424 pub fn next(self: *RelocIterator) ?macho.relocation_info {
425 self.index += 1;
426 if (self.index < self.buffer.len) {
427 return self.buffer[@intCast(u32, self.index)];
428 }
429 return null;
430 }
431
432 pub fn peek(self: RelocIterator) macho.relocation_info {
433 assert(self.index + 1 < self.buffer.len);
434 return self.buffer[@intCast(u32, self.index + 1)];
435 }
436};
437
438pub const Parser = struct {
439 object: *Object,
440 zld: *Zld,
441 it: *RelocIterator,
442 block: *TextBlock,
443
444 /// Base address of the parsed text block in the source section.
445 base_addr: u64,
446
447 /// Used only when targeting aarch64
448 addend: ?u32 = null,
449
450 /// Parsed subtractor symbol from _RELOC_SUBTRACTOR reloc type.
451 subtractor: ?*Symbol = null,
452
453 pub fn parse(self: *Parser) !void {
454 while (self.it.next()) |rel| {
455 const out_rel = blk: {
456 switch (self.object.arch.?) {
457 .aarch64 => {
458 const out_rel = switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
459 .ARM64_RELOC_BRANCH26 => try self.parseBranch(rel),
460 .ARM64_RELOC_SUBTRACTOR => {
461 // Subtractor is not a relocation with effect on the TextBlock, so
462 // parse it and carry on.
463 try self.parseSubtractor(rel);
464
465 // Verify SUBTRACTOR is followed by UNSIGNED.
466 const next = @intToEnum(macho.reloc_type_arm64, self.it.peek().r_type);
467 if (next != .ARM64_RELOC_UNSIGNED) {
468 log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next});
469 return error.UnexpectedRelocationType;
470 }
471 continue;
472 },
473 .ARM64_RELOC_UNSIGNED => try self.parseUnsigned(rel),
474 .ARM64_RELOC_ADDEND => {
475 // Addend is not a relocation with effect on the TextBlock, so
476 // parse it and carry on.
477 try self.parseAddend(rel);
478
479 // Verify ADDEND is followed by a load.
480 const next = @intToEnum(macho.reloc_type_arm64, self.it.peek().r_type);
481 switch (next) {
482 .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {},
483 else => {
484 log.err("unexpected relocation type: expected PAGE21 or PAGEOFF12, found {s}", .{next});
485 return error.UnexpectedRelocationType;
486 },
487 }
488 continue;
489 },
490 .ARM64_RELOC_PAGE21,
491 .ARM64_RELOC_GOT_LOAD_PAGE21,
492 .ARM64_RELOC_TLVP_LOAD_PAGE21,
493 => try self.parsePage(rel),
494 .ARM64_RELOC_PAGEOFF12,
495 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
496 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
497 => try self.parsePageOff(rel),
498 .ARM64_RELOC_POINTER_TO_GOT => try self.parsePointerToGot(rel),
499 };
500 break :blk out_rel;
501 },
502 .x86_64 => {
503 const out_rel = switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
504 .X86_64_RELOC_BRANCH => try self.parseBranch(rel),
505 .X86_64_RELOC_SUBTRACTOR => {
506 // Subtractor is not a relocation with effect on the TextBlock, so
507 // parse it and carry on.
508 try self.parseSubtractor(rel);
509
510 // Verify SUBTRACTOR is followed by UNSIGNED.
511 const next = @intToEnum(macho.reloc_type_x86_64, self.it.peek().r_type);
512 if (next != .X86_64_RELOC_UNSIGNED) {
513 log.err("unexpected relocation type: expected UNSIGNED, found {s}", .{next});
514 return error.UnexpectedRelocationType;
515 }
516 continue;
517 },
518 .X86_64_RELOC_UNSIGNED => try self.parseUnsigned(rel),
519 .X86_64_RELOC_SIGNED,
520 .X86_64_RELOC_SIGNED_1,
521 .X86_64_RELOC_SIGNED_2,
522 .X86_64_RELOC_SIGNED_4,
523 => try self.parseSigned(rel),
524 .X86_64_RELOC_GOT_LOAD,
525 .X86_64_RELOC_GOT,
526 .X86_64_RELOC_TLV,
527 => try self.parseLoad(rel),
528 };
529 break :blk out_rel;
530 },
531 else => unreachable,
532 }
533 };
534 try self.block.relocs.append(out_rel);
535
536 if (out_rel.target.payload == .regular) {
537 try self.block.references.put(out_rel.target.payload.regular.local_sym_index, {});
538 }
539
540 const is_via_got = switch (out_rel.payload) {
541 .pointer_to_got => true,
542 .load => |load| load.kind == .got,
543 .page => |page| page.kind == .got,
544 .page_off => |page_off| page_off.kind == .got,
545 else => false,
546 };
547
548 if (is_via_got and out_rel.target.got_index == null) {
549 const index = @intCast(u32, self.zld.got_entries.items.len);
550 out_rel.target.got_index = index;
551 try self.zld.got_entries.append(self.zld.allocator, out_rel.target);
552
553 log.debug("adding GOT entry for symbol {s} at index {}", .{
554 self.zld.getString(out_rel.target.strx),
555 index,
556 });
557 } else if (out_rel.payload == .unsigned) {
558 const sym = out_rel.target;
559 switch (sym.payload) {
560 .proxy => |proxy| {
561 try self.block.bindings.append(.{
562 .local_sym_index = proxy.local_sym_index,
563 .offset = out_rel.offset,
564 });
565 },
566 else => {
567 const source_sym = self.zld.locals.items[self.block.local_sym_index];
568 const source_reg = &source_sym.payload.regular;
569 const seg = self.zld.load_commands.items[source_reg.segment_id].Segment;
570 const sect = seg.sections.items[source_reg.section_id];
571 const sect_type = commands.sectionType(sect);
572
573 const should_rebase = rebase: {
574 if (!out_rel.payload.unsigned.is_64bit) break :rebase false;
575
576 // TODO actually, a check similar to what dyld is doing, that is, verifying
577 // that the segment is writable should be enough here.
578 const is_right_segment = blk: {
579 if (self.zld.data_segment_cmd_index) |idx| {
580 if (source_reg.segment_id == idx) {
581 break :blk true;
582 }
583 }
584 if (self.zld.data_const_segment_cmd_index) |idx| {
585 if (source_reg.segment_id == idx) {
586 break :blk true;
587 }
588 }
589 break :blk false;
590 };
591
592 if (!is_right_segment) break :rebase false;
593 if (sect_type != macho.S_LITERAL_POINTERS and
594 sect_type != macho.S_REGULAR and
595 sect_type != macho.S_MOD_INIT_FUNC_POINTERS and
596 sect_type != macho.S_MOD_TERM_FUNC_POINTERS)
597 {
598 break :rebase false;
599 }
600
601 break :rebase true;
602 };
603
604 if (should_rebase) {
605 try self.block.rebases.append(out_rel.offset);
606 }
607 },
608 }
609 } else if (out_rel.payload == .branch) blk: {
610 const sym = out_rel.target;
611
612 if (sym.stubs_index != null) break :blk;
613 if (sym.payload != .proxy) break :blk;
614
615 const index = @intCast(u32, self.zld.stubs.items.len);
616 sym.stubs_index = index;
617 try self.zld.stubs.append(self.zld.allocator, sym);
618
619 log.debug("adding stub entry for symbol {s} at index {}", .{ self.zld.getString(sym.strx), index });
620 }
621 }
622 }
623
624 fn parseBaseRelInfo(self: *Parser, rel: macho.relocation_info) !Relocation {
625 const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
626 const target = try self.object.symbolFromReloc(self.zld, rel);
627 return Relocation{
628 .offset = offset,
629 .target = target,
630 .payload = undefined,
631 };
632 }
633
634 fn parseUnsigned(self: *Parser, rel: macho.relocation_info) !Relocation {
635 defer {
636 // Reset parser's subtractor state
637 self.subtractor = null;
638 }
639
640 assert(rel.r_pcrel == 0);
641
642 var parsed = try self.parseBaseRelInfo(rel);
643 const is_64bit: bool = switch (rel.r_length) {
644 3 => true,
645 2 => false,
646 else => unreachable,
647 };
648
649 var addend: i64 = if (is_64bit)
650 mem.readIntLittle(i64, self.block.code[parsed.offset..][0..8])
651 else
652 mem.readIntLittle(i32, self.block.code[parsed.offset..][0..4]);
653
654 if (rel.r_extern == 0) {
655 addend -= @intCast(i64, parsed.target.payload.regular.address);
656 }
657
658 parsed.payload = .{
659 .unsigned = .{
660 .subtractor = self.subtractor,
661 .is_64bit = is_64bit,
662 .addend = addend,
663 },
664 };
665
666 return parsed;
667 }
668
669 fn parseBranch(self: *Parser, rel: macho.relocation_info) !Relocation {
670 assert(rel.r_pcrel == 1);
671 assert(rel.r_length == 2);
672
673 var parsed = try self.parseBaseRelInfo(rel);
674 parsed.payload = .{
675 .branch = .{
676 .arch = self.object.arch.?,
677 },
678 };
679 return parsed;
680 }
681
682 fn parsePage(self: *Parser, rel: macho.relocation_info) !Relocation {
683 assert(rel.r_pcrel == 1);
684 assert(rel.r_length == 2);
685
686 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
687
688 defer if (rel_type == .ARM64_RELOC_PAGE21) {
689 // Reset parser's addend state
690 self.addend = null;
691 };
692
693 const addend = if (rel_type == .ARM64_RELOC_PAGE21)
694 self.addend
695 else
696 null;
697
698 var parsed = try self.parseBaseRelInfo(rel);
699 parsed.payload = .{
700 .page = .{
701 .kind = switch (rel_type) {
702 .ARM64_RELOC_PAGE21 => .page,
703 .ARM64_RELOC_GOT_LOAD_PAGE21 => .got,
704 .ARM64_RELOC_TLVP_LOAD_PAGE21 => .tlvp,
705 else => unreachable,
706 },
707 .addend = addend,
708 },
709 };
710 return parsed;
711 }
712
713 fn parsePageOff(self: *Parser, rel: macho.relocation_info) !Relocation {
714 assert(rel.r_pcrel == 0);
715 assert(rel.r_length == 2);
716
717 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
718
719 defer if (rel_type == .ARM64_RELOC_PAGEOFF12) {
720 // Reset parser's addend state
721 self.addend = null;
722 };
723
724 const addend = if (rel_type == .ARM64_RELOC_PAGEOFF12)
725 self.addend
726 else
727 null;
728
729 var parsed = try self.parseBaseRelInfo(rel);
730 const op_kind: ?Relocation.PageOff.OpKind = blk: {
731 if (rel_type != .ARM64_RELOC_PAGEOFF12) break :blk null;
732 const op_kind: Relocation.PageOff.OpKind = if (isArithmeticOp(self.block.code[parsed.offset..][0..4]))
733 .arithmetic
734 else
735 .load;
736 break :blk op_kind;
737 };
738
739 parsed.payload = .{
740 .page_off = .{
741 .kind = switch (rel_type) {
742 .ARM64_RELOC_PAGEOFF12 => .page,
743 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => .got,
744 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => .tlvp,
745 else => unreachable,
746 },
747 .addend = addend,
748 .op_kind = op_kind,
749 },
750 };
751 return parsed;
752 }
753
754 fn parsePointerToGot(self: *Parser, rel: macho.relocation_info) !Relocation {
755 assert(rel.r_pcrel == 1);
756 assert(rel.r_length == 2);
757
758 var parsed = try self.parseBaseRelInfo(rel);
759 parsed.payload = .{
760 .pointer_to_got = .{},
761 };
762 return parsed;
763 }
764
765 fn parseAddend(self: *Parser, rel: macho.relocation_info) !void {
766 assert(rel.r_pcrel == 0);
767 assert(rel.r_extern == 0);
768 assert(self.addend == null);
769
770 self.addend = rel.r_symbolnum;
771 }
772
773 fn parseSigned(self: *Parser, rel: macho.relocation_info) !Relocation {
774 assert(rel.r_pcrel == 1);
775 assert(rel.r_length == 2);
776
777 var parsed = try self.parseBaseRelInfo(rel);
778 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
779 const correction: i4 = switch (rel_type) {
780 .X86_64_RELOC_SIGNED => 0,
781 .X86_64_RELOC_SIGNED_1 => 1,
782 .X86_64_RELOC_SIGNED_2 => 2,
783 .X86_64_RELOC_SIGNED_4 => 4,
784 else => unreachable,
785 };
786 var addend: i64 = mem.readIntLittle(i32, self.block.code[parsed.offset..][0..4]) + correction;
787
788 if (rel.r_extern == 0) {
789 const source_sym = self.zld.locals.items[self.block.local_sym_index].payload.regular;
790 const source_addr = source_sym.address + parsed.offset + 4;
791 const target_sym = parsed.target.payload.regular;
792 addend = @intCast(i64, source_addr) + addend - @intCast(i64, target_sym.address);
793 }
794
795 parsed.payload = .{
796 .signed = .{
797 .correction = correction,
798 .addend = addend,
799 },
800 };
801
802 return parsed;
803 }
804
805 fn parseSubtractor(self: *Parser, rel: macho.relocation_info) !void {
806 assert(rel.r_pcrel == 0);
807 assert(self.subtractor == null);
808
809 self.subtractor = try self.object.symbolFromReloc(self.zld, rel);
810 }
811
812 fn parseLoad(self: *Parser, rel: macho.relocation_info) !Relocation {
813 assert(rel.r_pcrel == 1);
814 assert(rel.r_length == 2);
815
816 var parsed = try self.parseBaseRelInfo(rel);
817 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
818 const addend = if (rel_type == .X86_64_RELOC_GOT)
819 mem.readIntLittle(i32, self.block.code[parsed.offset..][0..4])
820 else
821 null;
822
823 parsed.payload = .{
824 .load = .{
825 .kind = switch (rel_type) {
826 .X86_64_RELOC_GOT_LOAD, .X86_64_RELOC_GOT => .got,
827 .X86_64_RELOC_TLV => .tlvp,
828 else => unreachable,
829 },
830 .addend = addend,
831 },
832 };
833 return parsed;
834 }
835};
836
837inline fn isArithmeticOp(inst: *const [4]u8) bool {
838 const group_decode = @truncate(u5, inst[3]);
839 return ((group_decode >> 2) == 4);
840}