zld: fix resolving TLV offset relocations

This commit is contained in:
Jakub Konka
2021-07-10 14:07:56 +02:00
parent 322be2698d
commit dd5c7588d1
2 changed files with 37 additions and 63 deletions

View File

@@ -131,7 +131,6 @@ pub const TextBlock = struct {
size: u64,
alignment: u32,
rebases: std.ArrayList(u64),
tlv_offsets: std.ArrayList(TlvOffset),
next: ?*TextBlock = null,
prev: ?*TextBlock = null,
@@ -140,11 +139,6 @@ pub const TextBlock = struct {
offset: u64,
};
pub const TlvOffset = struct {
local_sym_index: u32,
offset: u64,
};
pub fn init(allocator: *Allocator) TextBlock {
return .{
.allocator = allocator,
@@ -155,7 +149,6 @@ pub const TextBlock = struct {
.size = undefined,
.alignment = undefined,
.rebases = std.ArrayList(u64).init(allocator),
.tlv_offsets = std.ArrayList(TextBlock.TlvOffset).init(allocator),
};
}
@@ -170,7 +163,6 @@ pub const TextBlock = struct {
self.allocator.free(self.code);
self.relocs.deinit();
self.rebases.deinit();
self.tlv_offsets.deinit();
}
pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
@@ -210,9 +202,6 @@ pub const TextBlock = struct {
if (self.rebases.items.len > 0) {
log.warn(" | rebases: {any}", .{self.rebases.items});
}
if (self.tlv_offsets.items.len > 0) {
log.warn(" | TLV offsets: {any}", .{self.tlv_offsets.items});
}
log.warn(" | size = {}", .{self.size});
log.warn(" | align = {}", .{self.alignment});
}
@@ -1120,10 +1109,7 @@ fn writeTextBlocks(self: *Zld) !void {
var code = try self.allocator.alloc(u8, sect.size);
defer self.allocator.free(code);
if (sect_type == macho.S_ZEROFILL or
sect_type == macho.S_THREAD_LOCAL_ZEROFILL or
sect_type == macho.S_THREAD_LOCAL_VARIABLES)
{
if (sect_type == macho.S_ZEROFILL or sect_type == macho.S_THREAD_LOCAL_ZEROFILL) {
mem.set(u8, code, 0);
} else {
var base_off: u64 = sect.size;
@@ -2051,41 +2037,6 @@ fn flush(self: *Zld) !void {
sect.offset = 0;
}
if (self.tlv_section_index) |index| {
// TODO this should be part of relocation resolution routine.
const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
const sect = &seg.sections.items[index];
const base_addr = if (self.tlv_data_section_index) |i|
seg.sections.items[i].addr
else
seg.sections.items[self.tlv_bss_section_index.?].addr;
var block: *TextBlock = self.blocks.get(.{
.seg = self.data_segment_cmd_index.?,
.sect = index,
}) orelse unreachable;
var buffer = try self.allocator.alloc(u8, @intCast(usize, sect.size));
defer self.allocator.free(buffer);
_ = try self.file.?.preadAll(buffer, sect.offset);
while (true) {
for (block.tlv_offsets.items) |tlv_offset| {
const sym = self.locals.items[tlv_offset.local_sym_index];
assert(sym.payload == .regular);
const offset = sym.payload.regular.address - base_addr;
mem.writeIntLittle(u64, buffer[tlv_offset.offset..][0..@sizeOf(u64)], offset);
}
if (block.prev) |prev| {
block = prev;
} else break;
}
try self.file.?.pwriteAll(buffer, sect.offset);
}
try self.writeGotEntries();
try self.setEntryPoint();
try self.writeRebaseInfoTable();

View File

@@ -50,7 +50,7 @@ pub const Relocation = struct {
source_sect_addr: ?u64 = null,
pub fn resolve(self: Unsigned, base: Relocation, source_addr: u64, target_addr: u64) !void {
pub fn resolve(self: Unsigned, base: Relocation, _: u64, target_addr: u64) !void {
const addend = if (self.source_sect_addr) |addr|
self.addend - @intCast(i64, addr)
else
@@ -430,12 +430,43 @@ pub const Relocation = struct {
}
switch (self.target.payload) {
.regular => |reg| break :blk reg.address,
.regular => |reg| {
const is_tlv = is_tlv: {
const sym = zld.locals.items[self.block.local_sym_index];
const seg = zld.load_commands.items[sym.payload.regular.segment_id].Segment;
const sect = seg.sections.items[sym.payload.regular.section_id];
break :is_tlv commands.sectionType(sect) == macho.S_THREAD_LOCAL_VARIABLES;
};
if (is_tlv) {
// For TLV relocations, the value specified as a relocation is the displacement from the
// TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
// defined TLV template init section in the following order:
// * wrt to __thread_data if defined, then
// * wrt to __thread_bss
const seg = zld.load_commands.items[zld.data_segment_cmd_index.?].Segment;
const base_address = inner: {
if (zld.tlv_data_section_index) |i| {
break :inner seg.sections.items[i].addr;
} else if (zld.tlv_bss_section_index) |i| {
break :inner seg.sections.items[i].addr;
} else {
log.err("threadlocal variables present but no initializer sections found", .{});
log.err(" __thread_data not found", .{});
log.err(" __thread_bss not found", .{});
return error.FailedToResolveRelocationTarget;
}
};
break :blk reg.address - base_address;
}
break :blk reg.address;
},
.proxy => |proxy| {
if (mem.eql(u8, self.target.name, "__tlv_bootstrap")) {
const segment = zld.load_commands.items[zld.data_segment_cmd_index.?].Segment;
const tlv = segment.sections.items[zld.tlv_section_index.?];
break :blk tlv.addr;
break :blk 0; // Dynamically bound by dyld.
// const segment = zld.load_commands.items[zld.data_segment_cmd_index.?].Segment;
// const tlv = segment.sections.items[zld.tlv_section_index.?];
// break :blk tlv.addr;
}
const segment = zld.load_commands.items[zld.text_segment_cmd_index.?].Segment;
@@ -677,14 +708,6 @@ pub const Parser = struct {
if (should_rebase) {
try self.block.rebases.append(out_rel.offset);
}
// TLV is handled via a separate offset mechanism.
if (sect_type == macho.S_THREAD_LOCAL_VARIABLES) {
try self.block.tlv_offsets.append(.{
.local_sym_index = out_rel.target.payload.regular.local_sym_index,
.offset = out_rel.offset,
});
}
},
}
} else if (out_rel.payload == .branch) blk: {