Commit 3ff05b79b9
src/link/MachO/Atom.zig
@@ -403,6 +403,13 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
}
try self.addPtrBindingOrRebase(rel, target, context);
},
+ .ARM64_RELOC_TLVP_LOAD_PAGE21,
+ .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
+ => {
+ if (target == .global) {
+ try addTlvPtrEntry(target, context);
+ }
+ },
else => {},
}
},
@@ -452,6 +459,11 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
@intCast(i64, target_sect_base_addr);
}
},
+ .X86_64_RELOC_TLV => {
+ if (target == .global) {
+ try addTlvPtrEntry(target, context);
+ }
+ },
else => {},
}
},
@@ -531,6 +543,47 @@ fn addPtrBindingOrRebase(
}
}
+fn addTlvPtrEntry(target: Relocation.Target, context: RelocContext) !void {
+ if (context.macho_file.tlv_ptr_entries_map.contains(target)) return;
+
+ const value_ptr = blk: {
+ if (context.macho_file.tlv_ptr_entries_map_free_list.popOrNull()) |i| {
+ log.debug("reusing __thread_ptrs entry index {d} for {}", .{ i, target });
+ context.macho_file.tlv_ptr_entries_map.keys()[i] = target;
+ const value_ptr = context.macho_file.tlv_ptr_entries_map.getPtr(target).?;
+ break :blk value_ptr;
+ } else {
+ const res = try context.macho_file.tlv_ptr_entries_map.getOrPut(
+ context.macho_file.base.allocator,
+ target,
+ );
+ log.debug("creating new __thread_ptrs entry at index {d} for {}", .{
+ context.macho_file.tlv_ptr_entries_map.getIndex(target).?,
+ target,
+ });
+ break :blk res.value_ptr;
+ }
+ };
+ const atom = try context.macho_file.createTlvPtrAtom(target);
+ value_ptr.* = atom;
+
+ const match = (try context.macho_file.getMatchingSection(.{
+ .segname = MachO.makeStaticString("__DATA"),
+ .sectname = MachO.makeStaticString("__thread_ptrs"),
+ .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
+ })).?;
+ if (!context.object.start_atoms.contains(match)) {
+ try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
+ }
+ if (context.object.end_atoms.getPtr(match)) |last| {
+ last.*.next = atom;
+ atom.prev = last.*;
+ last.* = atom;
+ } else {
+ try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
+ }
+}
+
fn addGotEntry(target: Relocation.Target, context: RelocContext) !void {
if (context.macho_file.got_entries_map.contains(target)) return;
@@ -667,6 +720,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
const sym = macho_file.locals.items[self.local_sym_index];
break :blk sym.n_value + rel.offset;
};
+ var is_via_thread_ptrs: bool = false;
const target_addr = blk: {
const is_via_got = got: {
switch (arch) {
@@ -742,8 +796,13 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
.undef => {
break :blk if (macho_file.stubs_map.get(n_strx)) |atom|
macho_file.locals.items[atom.local_sym_index].n_value
- else
- 0;
+ else inner: {
+ if (macho_file.tlv_ptr_entries_map.get(rel.target)) |atom| {
+ is_via_thread_ptrs = true;
+ break :inner macho_file.locals.items[atom.local_sym_index].n_value;
+ }
+ break :inner 0;
+ };
},
}
},
@@ -854,10 +913,12 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
},
.ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
const code = self.code.items[rel.offset..][0..4];
+ const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
+
const RegInfo = struct {
rd: u5,
rn: u5,
- size: u1,
+ size: u2,
};
const reg_info: RegInfo = blk: {
if (isArithmeticOp(code)) {
@@ -878,13 +939,25 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
break :blk .{
.rd = inst.rt,
.rn = inst.rn,
- .size = @truncate(u1, inst.size),
+ .size = inst.size,
};
}
};
- const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));
- var inst = aarch64.Instruction{
+ var inst = if (is_via_thread_ptrs) blk: {
+ const offset = try math.divExact(u12, narrowed, 8);
+ break :blk aarch64.Instruction{
+ .load_store_register = .{
+ .rt = reg_info.rd,
+ .rn = reg_info.rn,
+ .offset = offset,
+ .opc = 0b01,
+ .op1 = 0b01,
+ .v = 0,
+ .size = reg_info.size,
+ },
+ };
+ } else aarch64.Instruction{
.add_subtract_immediate = .{
.rd = reg_info.rd,
.rn = reg_info.rn,
@@ -892,7 +965,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
.sh = 0,
.s = 0,
.op = 0,
- .sf = reg_info.size,
+ .sf = @truncate(u1, reg_info.size),
},
};
mem.writeIntLittle(u32, code, inst.toU32());
@@ -942,8 +1015,10 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));
},
.X86_64_RELOC_TLV => {
- // We need to rewrite the opcode from movq to leaq.
- self.code.items[rel.offset - 2] = 0x8d;
+ if (!is_via_thread_ptrs) {
+ // We need to rewrite the opcode from movq to leaq.
+ self.code.items[rel.offset - 2] = 0x8d;
+ }
const displacement = try math.cast(
i32,
@intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend,
src/link/MachO.zig
@@ -130,6 +130,7 @@ objc_imageinfo_section_index: ?u16 = null,
tlv_section_index: ?u16 = null,
tlv_data_section_index: ?u16 = null,
tlv_bss_section_index: ?u16 = null,
+tlv_ptrs_section_index: ?u16 = null,
la_symbol_ptr_section_index: ?u16 = null,
data_section_index: ?u16 = null,
bss_section_index: ?u16 = null,
@@ -164,6 +165,9 @@ stub_helper_preamble_atom: ?*Atom = null,
strtab: std.ArrayListUnmanaged(u8) = .{},
strtab_dir: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
+tlv_ptr_entries_map: std.AutoArrayHashMapUnmanaged(Atom.Relocation.Target, *Atom) = .{},
+tlv_ptr_entries_map_free_list: std.ArrayListUnmanaged(u32) = .{},
+
got_entries_map: std.AutoArrayHashMapUnmanaged(Atom.Relocation.Target, *Atom) = .{},
got_entries_map_free_list: std.ArrayListUnmanaged(u32) = .{},
@@ -1525,6 +1529,24 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
.sect = self.tlv_section_index.?,
};
},
+ macho.S_THREAD_LOCAL_VARIABLE_POINTERS => {
+ if (self.tlv_ptrs_section_index == null) {
+ self.tlv_ptrs_section_index = try self.initSection(
+ self.data_segment_cmd_index.?,
+ "__thread_ptrs",
+ sect.size,
+ sect.@"align",
+ .{
+ .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
+ },
+ );
+ }
+
+ break :blk .{
+ .seg = self.data_segment_cmd_index.?,
+ .sect = self.tlv_ptrs_section_index.?,
+ };
+ },
macho.S_THREAD_LOCAL_REGULAR => {
if (self.tlv_data_section_index == null) {
self.tlv_data_section_index = try self.initSection(
@@ -2142,6 +2164,24 @@ pub fn createGotAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {
return atom;
}
+pub fn createTlvPtrAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {
+ const local_sym_index = @intCast(u32, self.locals.items.len);
+ try self.locals.append(self.base.allocator, .{
+ .n_strx = 0,
+ .n_type = macho.N_SECT,
+ .n_sect = 0,
+ .n_desc = 0,
+ .n_value = 0,
+ });
+ const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);
+ assert(target == .global);
+ try atom.bindings.append(self.base.allocator, .{
+ .n_strx = target.global,
+ .offset = 0,
+ });
+ return atom;
+}
+
fn createDyldPrivateAtom(self: *MachO) !void {
if (self.dyld_private_atom != null) return;
const local_sym_index = @intCast(u32, self.locals.items.len);
@@ -3214,6 +3254,8 @@ pub fn deinit(self: *MachO) void {
}
self.section_ordinals.deinit(self.base.allocator);
+ self.tlv_ptr_entries_map.deinit(self.base.allocator);
+ self.tlv_ptr_entries_map_free_list.deinit(self.base.allocator);
self.got_entries_map.deinit(self.base.allocator);
self.got_entries_map_free_list.deinit(self.base.allocator);
self.stubs_map.deinit(self.base.allocator);
@@ -4989,6 +5031,7 @@ fn sortSections(self: *MachO) !void {
&self.objc_data_section_index,
&self.data_section_index,
&self.tlv_section_index,
+ &self.tlv_ptrs_section_index,
&self.tlv_data_section_index,
&self.tlv_bss_section_index,
&self.bss_section_index,
@@ -6216,6 +6259,14 @@ fn logSymtab(self: MachO) void {
}
}
+ log.debug("__thread_ptrs entries:", .{});
+ for (self.tlv_ptr_entries_map.keys()) |key| {
+ switch (key) {
+ .local => unreachable,
+ .global => |n_strx| log.debug(" {} => {s}", .{ key, self.getString(n_strx) }),
+ }
+ }
+
log.debug("stubs:", .{});
for (self.stubs_map.keys()) |key| {
log.debug(" {} => {s}", .{ key, self.getString(key) });