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:
Jakub Konka 2021-05-09 16:24:53 +02:00
parent 15d6efecfb
commit f67e756211
3 changed files with 26 additions and 24 deletions

View File

@ -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);
},

View File

@ -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;

View File

@ -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);