From f67e756211b6e69cc8fadbb6b1ec5af1bd5c7049 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 9 May 2021 16:24:53 +0200 Subject: [PATCH] zld: adjust signed displacement source target addr Previously, I mistakenly assumed that offset of the relocation is enough when calculating relative offset of the target from the source target section base address in case of section-based relocs on x86_64. While this is true for `__TEXT,__text` section which always starts at 0x0 in object files, this is absolutely not true for `__TEXT,__StaticInit` section which will have nonzero base address hence resulting in incorrect displacement calculations for SIGNED relocs. --- src/link/MachO/Zld.zig | 6 ++++-- src/link/MachO/reloc.zig | 11 +++++++---- src/link/MachO/reloc/x86_64.zig | 33 +++++++++++++++------------------ 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 4d19da1e97..3904192995 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -1533,7 +1533,8 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { } if (rel.target == .section) { const source_sect = object.sections.items[rel.target.section]; - args.source_sect_addr = source_sect.inner.addr; + args.source_source_sect_addr = sect.inner.addr; + args.source_target_sect_addr = source_sect.inner.addr; } rebases: { @@ -1588,7 +1589,8 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { else => |tt| { if (tt == .signed and rel.target == .section) { const source_sect = object.sections.items[rel.target.section]; - args.source_sect_addr = source_sect.inner.addr; + args.source_source_sect_addr = sect.inner.addr; + args.source_target_sect_addr = source_sect.inner.addr; } args.target_addr = try self.relocTargetAddr(@intCast(u16, object_id), rel.target); }, diff --git a/src/link/MachO/reloc.zig b/src/link/MachO/reloc.zig index 1ce9fa2c2d..89b7aa4228 100644 --- a/src/link/MachO/reloc.zig +++ b/src/link/MachO/reloc.zig @@ -29,7 +29,8 @@ pub const Relocation = struct { source_addr: u64, target_addr: u64, subtractor: ?u64 = null, - source_sect_addr: ?u64 = null, + source_source_sect_addr: ?u64 = null, + source_target_sect_addr: ?u64 = null, }; pub fn resolve(base: *Relocation, args: ResolveArgs) !void { @@ -39,8 +40,10 @@ pub const Relocation = struct { log.debug(" | target address 0x{x}", .{args.target_addr}); if (args.subtractor) |sub| log.debug(" | subtractor address 0x{x}", .{sub}); - if (args.source_sect_addr) |addr| - log.debug(" | source section address 0x{x}", .{addr}); + if (args.source_source_sect_addr) |addr| + log.debug(" | source source section address 0x{x}", .{addr}); + if (args.source_target_sect_addr) |addr| + log.debug(" | source target section address 0x{x}", .{addr}); return switch (base.@"type") { .unsigned => @fieldParentPtr(Unsigned, "base", base).resolve(args), @@ -104,7 +107,7 @@ pub const Unsigned = struct { pub fn resolve(unsigned: Unsigned, args: Relocation.ResolveArgs) !void { const addend = if (unsigned.base.target == .section) - unsigned.addend - @intCast(i64, args.source_sect_addr.?) + unsigned.addend - @intCast(i64, args.source_target_sect_addr.?) else unsigned.addend; diff --git a/src/link/MachO/reloc/x86_64.zig b/src/link/MachO/reloc/x86_64.zig index 32f83924e8..a5e3ff2825 100644 --- a/src/link/MachO/reloc/x86_64.zig +++ b/src/link/MachO/reloc/x86_64.zig @@ -33,16 +33,19 @@ pub const Signed = struct { pub fn resolve(signed: Signed, args: Relocation.ResolveArgs) !void { const target_addr = target_addr: { if (signed.base.target == .section) { - const source_target = @intCast(i64, signed.base.offset) + signed.addend + 4 + signed.correction; - const source_disp = source_target - @intCast(i64, args.source_sect_addr.?); + const source_target = @intCast(i64, args.source_source_sect_addr.?) + @intCast(i64, signed.base.offset) + signed.addend + 4; + const source_disp = source_target - @intCast(i64, args.source_target_sect_addr.?); break :target_addr @intCast(i64, args.target_addr) + source_disp; } break :target_addr @intCast(i64, args.target_addr) + signed.addend; }; - const displacement = try math.cast(i32, target_addr - @intCast(i64, args.source_addr) - signed.correction - 4); + const displacement = try math.cast( + i32, + target_addr - @intCast(i64, args.source_addr) - signed.correction - 4, + ); - log.debug(" | calculated addend 0x{x}", .{signed.addend}); - log.debug(" | calculated correction 0x{x}", .{signed.correction}); + log.debug(" | addend 0x{x}", .{signed.addend}); + log.debug(" | correction 0x{x}", .{signed.correction}); log.debug(" | displacement 0x{x}", .{displacement}); mem.writeIntLittle(u32, signed.base.code[0..4], @bitCast(u32, displacement)); @@ -172,20 +175,14 @@ pub const Parser = struct { const offset = @intCast(u32, rel.r_address); const inst = parser.code[offset..][0..4]; - const addend = mem.readIntLittle(i32, inst); - - const correction: i4 = correction: { - if (is_extern) break :correction 0; - - const corr: i4 = switch (rel_type) { - .X86_64_RELOC_SIGNED => 0, - .X86_64_RELOC_SIGNED_1 => 1, - .X86_64_RELOC_SIGNED_2 => 2, - .X86_64_RELOC_SIGNED_4 => 4, - else => unreachable, - }; - break :correction corr; + const correction: i4 = switch (rel_type) { + .X86_64_RELOC_SIGNED => 0, + .X86_64_RELOC_SIGNED_1 => 1, + .X86_64_RELOC_SIGNED_2 => 2, + .X86_64_RELOC_SIGNED_4 => 4, + else => unreachable, }; + const addend = mem.readIntLittle(i32, inst) + correction; var signed = try parser.allocator.create(Signed); errdefer parser.allocator.destroy(signed);