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.
This commit is contained in:
@@ -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);
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user