mirror of
https://github.com/ziglang/zig.git
synced 2026-01-11 01:45:12 +00:00
macho: handle TLS imported from dylib
This is a missing feature which requires `__thread_ptrs` section to be synthesised for any extern reference to a global TLS variable.
This commit is contained in:
parent
2e7a48d6bf
commit
3ff05b79b9
@ -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) });
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user