Commit 5750620715

Jakub Konka <kubkon@jakubkonka.com>
2023-08-24 13:49:03
macho: use TableSection for TLV pointer entries in zld driver
1 parent 04e93dd
Changed files (2)
src
link
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,
+    }
+}
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();