macho: use TableSection for TLV pointer entries in zld driver

This commit is contained in:
Jakub Konka 2023-08-24 13:49:03 +02:00
parent 04e93dd265
commit 5750620715
2 changed files with 75 additions and 123 deletions

View File

@ -360,7 +360,6 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct {
pub fn getRelocTargetAtomIndex(zld: *Zld, target: SymbolWithLoc) ?Index {
if (zld.getStubsAtomIndexForSymbol(target)) |stubs_atom| return stubs_atom;
if (zld.getTlvPtrAtomIndexForSymbol(target)) |tlv_ptr_atom| return tlv_ptr_atom;
if (target.getFile() == null) {
const target_sym_name = zld.getSymbolName(target);
@ -413,7 +412,8 @@ fn scanAtomRelocsArm64(zld: *Zld, atom_index: Index, relocs: []align(1) const ma
.ARM64_RELOC_TLVP_LOAD_PAGE21,
.ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
=> {
try addTlvPtrEntry(zld, target);
const sym = zld.getSymbol(target);
if (sym.undf()) try zld.addTlvPtrEntry(target);
},
else => {},
}
@ -454,28 +454,14 @@ fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const mach
try zld.addGotEntry(target);
},
.X86_64_RELOC_TLV => {
try addTlvPtrEntry(zld, target);
const sym = zld.getSymbol(target);
if (sym.undf()) try zld.addTlvPtrEntry(target);
},
else => {},
}
}
}
fn addTlvPtrEntry(zld: *Zld, target: SymbolWithLoc) !void {
const target_sym = zld.getSymbol(target);
if (!target_sym.undf()) return;
if (zld.tlv_ptr_table.contains(target)) return;
const gpa = zld.gpa;
const atom_index = try zld.createTlvPtrAtom();
const tlv_ptr_index = @as(u32, @intCast(zld.tlv_ptr_entries.items.len));
try zld.tlv_ptr_entries.append(gpa, .{
.target = target,
.atom_index = atom_index,
});
try zld.tlv_ptr_table.putNoClobber(gpa, target, tlv_ptr_index);
}
pub fn addStub(zld: *Zld, target: SymbolWithLoc) !void {
const target_sym = zld.getSymbol(target);
if (!target_sym.undf()) return;
@ -641,10 +627,12 @@ fn resolveRelocsArm64(
const header = zld.sections.items(.header)[source_sym.n_sect - 1];
break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
};
const target_addr = if (is_via_got)
zld.getGotEntryAddress(target).?
else
try getRelocTargetAddress(zld, target, is_tlv);
const target_addr = blk: {
if (is_via_got) break :blk zld.getGotEntryAddress(target).?;
if (relocIsTlv(zld, rel) and zld.getSymbol(target).undf())
break :blk zld.getTlvPtrEntryAddress(target).?;
break :blk try getRelocTargetAddress(zld, target, is_tlv);
};
log.debug(" | source_addr = 0x{x}", .{source_addr});
@ -802,7 +790,7 @@ fn resolveRelocsArm64(
}
};
var inst = if (zld.tlv_ptr_table.contains(target)) aarch64.Instruction{
var inst = if (zld.tlv_ptr_table.lookup.contains(target)) aarch64.Instruction{
.load_store_register = .{
.rt = reg_info.rd,
.rn = reg_info.rn,
@ -938,14 +926,15 @@ fn resolveRelocsX86(
const header = zld.sections.items(.header)[source_sym.n_sect - 1];
break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
};
const target_addr = blk: {
if (is_via_got) break :blk zld.getGotEntryAddress(target).?;
if (relocIsTlv(zld, rel) and zld.getSymbol(target).undf())
break :blk zld.getTlvPtrEntryAddress(target).?;
break :blk try getRelocTargetAddress(zld, target, is_tlv);
};
log.debug(" | source_addr = 0x{x}", .{source_addr});
const target_addr = if (is_via_got)
zld.getGotEntryAddress(target).?
else
try getRelocTargetAddress(zld, target, is_tlv);
switch (rel_type) {
.X86_64_RELOC_BRANCH => {
const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
@ -971,7 +960,7 @@ fn resolveRelocsX86(
log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
if (zld.tlv_ptr_table.get(target) == null) {
if (zld.tlv_ptr_table.lookup.get(target) == null) {
// We need to rewrite the opcode from movq to leaq.
atom_code[rel_offset - 2] = 0x8d;
}
@ -1112,3 +1101,19 @@ pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool {
else => unreachable,
}
}
pub fn relocIsTlv(zld: *Zld, rel: macho.relocation_info) bool {
switch (zld.options.target.cpu.arch) {
.aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
.ARM64_RELOC_TLVP_LOAD_PAGE21,
.ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
=> return true,
else => return false,
},
.x86_64 => switch (@as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type))) {
.X86_64_RELOC_TLV => return true,
else => return false,
},
else => unreachable,
}
}

View File

@ -68,6 +68,7 @@ pub const Zld = struct {
sections: std.MultiArrayList(Section) = .{},
got_section_index: ?u8 = null,
tlv_ptr_section_index: ?u8 = null,
locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
globals: std.ArrayListUnmanaged(SymbolWithLoc) = .{},
@ -81,9 +82,7 @@ pub const Zld = struct {
strtab: StringTable(.strtab) = .{},
tlv_ptr_entries: std.ArrayListUnmanaged(IndirectPointer) = .{},
tlv_ptr_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
tlv_ptr_table: TableSection(SymbolWithLoc) = .{},
got_table: TableSection(SymbolWithLoc) = .{},
stubs: std.ArrayListUnmanaged(IndirectPointer) = .{},
@ -268,24 +267,6 @@ pub const Zld = struct {
return index;
}
pub fn createTlvPtrAtom(self: *Zld) !AtomIndex {
const sym_index = try self.allocateSymbol();
const atom_index = try self.createEmptyAtom(sym_index, @sizeOf(u64), 3);
const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
sym.n_type = macho.N_SECT;
const sect_id = (try self.getOutputSection(.{
.segname = makeStaticString("__DATA"),
.sectname = makeStaticString("__thread_ptrs"),
.flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
})).?;
sym.n_sect = sect_id + 1;
self.addAtomToSection(atom_index);
return atom_index;
}
fn createDyldStubBinderGotAtom(self: *Zld) !void {
const global_index = self.dyld_stub_binder_index orelse return;
const target = self.globals.items[global_index];
@ -841,7 +822,6 @@ pub const Zld = struct {
pub fn deinit(self: *Zld) void {
const gpa = self.gpa;
self.tlv_ptr_entries.deinit(gpa);
self.tlv_ptr_table.deinit(gpa);
self.got_table.deinit(gpa);
self.stubs.deinit(gpa);
@ -959,16 +939,26 @@ pub const Zld = struct {
return global_index;
}
pub fn addGotEntry(zld: *Zld, target: SymbolWithLoc) !void {
if (zld.got_table.lookup.contains(target)) return;
_ = try zld.got_table.allocateEntry(zld.gpa, target);
if (zld.got_section_index == null) {
zld.got_section_index = try zld.initSection("__DATA_CONST", "__got", .{
pub fn addGotEntry(self: *Zld, target: SymbolWithLoc) !void {
if (self.got_table.lookup.contains(target)) return;
_ = try self.got_table.allocateEntry(self.gpa, target);
if (self.got_section_index == null) {
self.got_section_index = try self.initSection("__DATA_CONST", "__got", .{
.flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
});
}
}
pub fn addTlvPtrEntry(self: *Zld, target: SymbolWithLoc) !void {
if (self.tlv_ptr_table.lookup.contains(target)) return;
_ = try self.tlv_ptr_table.allocateEntry(self.gpa, target);
if (self.tlv_ptr_section_index == null) {
self.tlv_ptr_section_index = try self.initSection("__DATA", "__thread_ptrs", .{
.flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
});
}
}
fn allocateSpecialSymbols(self: *Zld) !void {
for (&[_]?u32{
self.dso_handle_index,
@ -1033,12 +1023,10 @@ pub const Zld = struct {
} else if (atom.getFile() == null) outer: {
switch (header.type()) {
macho.S_NON_LAZY_SYMBOL_POINTERS => unreachable,
macho.S_THREAD_LOCAL_VARIABLE_POINTERS => unreachable,
macho.S_LAZY_SYMBOL_POINTERS => {
try self.writeLazyPointer(count, buffer.writer());
},
macho.S_THREAD_LOCAL_VARIABLE_POINTERS => {
buffer.appendSliceAssumeCapacity(&[_]u8{0} ** @sizeOf(u64));
},
else => {
if (self.stub_helper_preamble_sym_index) |sym_index| {
if (sym_index == atom.sym_index) {
@ -1087,12 +1075,11 @@ pub const Zld = struct {
}
}
fn writeGotEntries(self: *Zld) !void {
const sect_id = self.got_section_index orelse return;
fn writePointerEntries(self: *Zld, sect_id: u8, table: anytype) !void {
const header = self.sections.items(.header)[sect_id];
var buffer = try std.ArrayList(u8).initCapacity(self.gpa, header.size);
defer buffer.deinit();
for (self.got_table.entries.items) |entry| {
for (table.entries.items) |entry| {
const sym = self.getSymbol(entry);
buffer.writer().writeIntLittle(u64, sym.n_value) catch unreachable;
}
@ -1147,6 +1134,7 @@ pub const Zld = struct {
for (&[_]*?u8{
&self.got_section_index,
&self.tlv_ptr_section_index,
}) |maybe_index| {
if (maybe_index.*) |*index| {
index.* = backlinks[index.*];
@ -1236,6 +1224,12 @@ pub const Zld = struct {
header.size = self.got_table.count() * @sizeOf(u64);
header.@"align" = 3;
}
if (self.tlv_ptr_section_index) |sect_id| {
const header = &self.sections.items(.header)[sect_id];
header.size = self.tlv_ptr_table.count() * @sizeOf(u64);
header.@"align" = 3;
}
}
fn allocateSegments(self: *Zld) !void {
@ -1579,44 +1573,6 @@ pub const Zld = struct {
try rebase.finalize(self.gpa);
}
fn collectBindDataFromContainer(
self: *Zld,
sect_id: u8,
bind: *Bind,
container: anytype,
) !void {
const slice = self.sections.slice();
const segment_index = slice.items(.segment_index)[sect_id];
const seg = self.getSegment(sect_id);
try bind.entries.ensureUnusedCapacity(self.gpa, container.items.len);
for (container.items) |entry| {
const bind_sym_name = entry.getTargetSymbolName(self);
const bind_sym = entry.getTargetSymbol(self);
if (bind_sym.sect()) continue;
const sym = entry.getAtomSymbol(self);
const base_offset = sym.n_value - seg.vmaddr;
const dylib_ordinal = @divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER);
log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
base_offset,
bind_sym_name,
dylib_ordinal,
});
if (bind_sym.weakRef()) {
log.debug(" | marking as weak ref ", .{});
}
bind.entries.appendAssumeCapacity(.{
.target = entry.target,
.offset = base_offset,
.segment_id = segment_index,
.addend = 0,
});
}
}
fn collectBindData(
self: *Zld,
bind: *Bind,
@ -1629,8 +1585,8 @@ pub const Zld = struct {
}
// Next, unpack TLV pointers section
if (self.getSectionByName("__DATA", "__thread_ptrs")) |sect_id| {
try self.collectBindDataFromContainer(sect_id, bind, self.tlv_ptr_entries);
if (self.tlv_ptr_section_index) |sect_id| {
try MachO.collectBindDataFromTableSection(self.gpa, self, sect_id, bind, self.tlv_ptr_table);
}
// Finally, unpack the rest.
@ -2488,6 +2444,12 @@ pub const Zld = struct {
return header.addr + @sizeOf(u64) * index;
}
pub fn getTlvPtrEntryAddress(self: *Zld, sym_with_loc: SymbolWithLoc) ?u64 {
const index = self.tlv_ptr_table.lookup.get(sym_with_loc) orelse return null;
const header = self.sections.items(.header)[self.tlv_ptr_section_index.?];
return header.addr + @sizeOf(u64) * index;
}
/// Returns stubs atom that references `sym_with_loc` if one exists.
/// Returns null otherwise.
pub fn getStubsAtomIndexForSymbol(self: *Zld, sym_with_loc: SymbolWithLoc) ?AtomIndex {
@ -2496,14 +2458,6 @@ pub const Zld = struct {
return entry.atom_index;
}
/// Returns TLV pointer atom that references `sym_with_loc` if one exists.
/// Returns null otherwise.
pub fn getTlvPtrAtomIndexForSymbol(self: *Zld, sym_with_loc: SymbolWithLoc) ?AtomIndex {
const index = self.tlv_ptr_table.get(sym_with_loc) orelse return null;
const entry = self.tlv_ptr_entries.items[index];
return entry.atom_index;
}
/// Returns symbol location corresponding to the set entrypoint.
/// Asserts output mode is executable.
pub fn getEntryPoint(self: Zld) SymbolWithLoc {
@ -2835,18 +2789,8 @@ pub const Zld = struct {
scoped_log.debug("GOT entries:", .{});
scoped_log.debug("{}", .{self.got_table});
scoped_log.debug("__thread_ptrs entries:", .{});
for (self.tlv_ptr_entries.items, 0..) |entry, i| {
const atom_sym = entry.getAtomSymbol(self);
const target_sym = entry.getTargetSymbol(self);
const target_sym_name = entry.getTargetSymbolName(self);
assert(target_sym.undf());
scoped_log.debug(" {d}@{x} => import('{s}')", .{
i,
atom_sym.n_value,
target_sym_name,
});
}
scoped_log.debug("TLV pointers:", .{});
scoped_log.debug("{}", .{self.tlv_ptr_table});
scoped_log.debug("stubs entries:", .{});
for (self.stubs.items, 0..) |entry, i| {
@ -3435,7 +3379,10 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
}
try zld.writeAtoms();
try zld.writeGotEntries();
if (zld.got_section_index) |sect_id| try zld.writePointerEntries(sect_id, &zld.got_table);
if (zld.tlv_ptr_section_index) |sect_id| try zld.writePointerEntries(sect_id, &zld.tlv_ptr_table);
try eh_frame.write(&zld, &unwind_info);
try unwind_info.write(&zld);
try zld.writeLinkeditSegmentData();