From 5750620715bde214778546d2adc6db5fdfad5588 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 24 Aug 2023 13:49:03 +0200 Subject: [PATCH] macho: use TableSection for TLV pointer entries in zld driver --- src/link/MachO/Atom.zig | 63 ++++++++++--------- src/link/MachO/zld.zig | 135 ++++++++++++---------------------------- 2 files changed, 75 insertions(+), 123 deletions(-) diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index 8b6c7638c3..d62b23d6c7 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -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, + } +} diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig index 4146d0cd07..c15f81c7e3 100644 --- a/src/link/MachO/zld.zig +++ b/src/link/MachO/zld.zig @@ -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();