Commit f67e756211

Jakub Konka <kubkon@jakubkonka.com>
2021-05-09 16:24:53
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.
1 parent 15d6efe
Changed files (3)
src
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);
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;
 
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);
                         },