authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-13 23:18:27+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-15 08:59:20+01:00
log3ff05b79b94f46c0c3f75c7b54a2b0a66840cdf8
treeb1aeb47e46c4f148e29673196a2034b3766012d6
parent2e7a48d6bf6ca3ee4c1fdac04733fd2adb096d06

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.

2 files changed, 135 insertions(+), 9 deletions(-)

src/link/MachO.zig+51
...@@ -130,6 +130,7 @@ objc_imageinfo_section_index: ?u16 = null,...@@ -130,6 +130,7 @@ objc_imageinfo_section_index: ?u16 = null,
130tlv_section_index: ?u16 = null,130tlv_section_index: ?u16 = null,
131tlv_data_section_index: ?u16 = null,131tlv_data_section_index: ?u16 = null,
132tlv_bss_section_index: ?u16 = null,132tlv_bss_section_index: ?u16 = null,
133tlv_ptrs_section_index: ?u16 = null,
133la_symbol_ptr_section_index: ?u16 = null,134la_symbol_ptr_section_index: ?u16 = null,
134data_section_index: ?u16 = null,135data_section_index: ?u16 = null,
135bss_section_index: ?u16 = null,136bss_section_index: ?u16 = null,
...@@ -164,6 +165,9 @@ stub_helper_preamble_atom: ?*Atom = null,...@@ -164,6 +165,9 @@ stub_helper_preamble_atom: ?*Atom = null,
164strtab: std.ArrayListUnmanaged(u8) = .{},165strtab: std.ArrayListUnmanaged(u8) = .{},
165strtab_dir: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},166strtab_dir: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
166167
168tlv_ptr_entries_map: std.AutoArrayHashMapUnmanaged(Atom.Relocation.Target, *Atom) = .{},
169tlv_ptr_entries_map_free_list: std.ArrayListUnmanaged(u32) = .{},
170
167got_entries_map: std.AutoArrayHashMapUnmanaged(Atom.Relocation.Target, *Atom) = .{},171got_entries_map: std.AutoArrayHashMapUnmanaged(Atom.Relocation.Target, *Atom) = .{},
168got_entries_map_free_list: std.ArrayListUnmanaged(u32) = .{},172got_entries_map_free_list: std.ArrayListUnmanaged(u32) = .{},
169173
...@@ -1525,6 +1529,24 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1525,6 +1529,24 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
1525 .sect = self.tlv_section_index.?,1529 .sect = self.tlv_section_index.?,
1526 };1530 };
1527 },1531 },
1532 macho.S_THREAD_LOCAL_VARIABLE_POINTERS => {
1533 if (self.tlv_ptrs_section_index == null) {
1534 self.tlv_ptrs_section_index = try self.initSection(
1535 self.data_segment_cmd_index.?,
1536 "__thread_ptrs",
1537 sect.size,
1538 sect.@"align",
1539 .{
1540 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
1541 },
1542 );
1543 }
1544
1545 break :blk .{
1546 .seg = self.data_segment_cmd_index.?,
1547 .sect = self.tlv_ptrs_section_index.?,
1548 };
1549 },
1528 macho.S_THREAD_LOCAL_REGULAR => {1550 macho.S_THREAD_LOCAL_REGULAR => {
1529 if (self.tlv_data_section_index == null) {1551 if (self.tlv_data_section_index == null) {
1530 self.tlv_data_section_index = try self.initSection(1552 self.tlv_data_section_index = try self.initSection(
...@@ -2142,6 +2164,24 @@ pub fn createGotAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {...@@ -2142,6 +2164,24 @@ pub fn createGotAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {
2142 return atom;2164 return atom;
2143}2165}
21442166
2167pub fn createTlvPtrAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {
2168 const local_sym_index = @intCast(u32, self.locals.items.len);
2169 try self.locals.append(self.base.allocator, .{
2170 .n_strx = 0,
2171 .n_type = macho.N_SECT,
2172 .n_sect = 0,
2173 .n_desc = 0,
2174 .n_value = 0,
2175 });
2176 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);
2177 assert(target == .global);
2178 try atom.bindings.append(self.base.allocator, .{
2179 .n_strx = target.global,
2180 .offset = 0,
2181 });
2182 return atom;
2183}
2184
2145fn createDyldPrivateAtom(self: *MachO) !void {2185fn createDyldPrivateAtom(self: *MachO) !void {
2146 if (self.dyld_private_atom != null) return;2186 if (self.dyld_private_atom != null) return;
2147 const local_sym_index = @intCast(u32, self.locals.items.len);2187 const local_sym_index = @intCast(u32, self.locals.items.len);
...@@ -3214,6 +3254,8 @@ pub fn deinit(self: *MachO) void {...@@ -3214,6 +3254,8 @@ pub fn deinit(self: *MachO) void {
3214 }3254 }
32153255
3216 self.section_ordinals.deinit(self.base.allocator);3256 self.section_ordinals.deinit(self.base.allocator);
3257 self.tlv_ptr_entries_map.deinit(self.base.allocator);
3258 self.tlv_ptr_entries_map_free_list.deinit(self.base.allocator);
3217 self.got_entries_map.deinit(self.base.allocator);3259 self.got_entries_map.deinit(self.base.allocator);
3218 self.got_entries_map_free_list.deinit(self.base.allocator);3260 self.got_entries_map_free_list.deinit(self.base.allocator);
3219 self.stubs_map.deinit(self.base.allocator);3261 self.stubs_map.deinit(self.base.allocator);
...@@ -4989,6 +5031,7 @@ fn sortSections(self: *MachO) !void {...@@ -4989,6 +5031,7 @@ fn sortSections(self: *MachO) !void {
4989 &self.objc_data_section_index,5031 &self.objc_data_section_index,
4990 &self.data_section_index,5032 &self.data_section_index,
4991 &self.tlv_section_index,5033 &self.tlv_section_index,
5034 &self.tlv_ptrs_section_index,
4992 &self.tlv_data_section_index,5035 &self.tlv_data_section_index,
4993 &self.tlv_bss_section_index,5036 &self.tlv_bss_section_index,
4994 &self.bss_section_index,5037 &self.bss_section_index,
...@@ -6216,6 +6259,14 @@ fn logSymtab(self: MachO) void {...@@ -6216,6 +6259,14 @@ fn logSymtab(self: MachO) void {
6216 }6259 }
6217 }6260 }
62186261
6262 log.debug("__thread_ptrs entries:", .{});
6263 for (self.tlv_ptr_entries_map.keys()) |key| {
6264 switch (key) {
6265 .local => unreachable,
6266 .global => |n_strx| log.debug(" {} => {s}", .{ key, self.getString(n_strx) }),
6267 }
6268 }
6269
6219 log.debug("stubs:", .{});6270 log.debug("stubs:", .{});
6220 for (self.stubs_map.keys()) |key| {6271 for (self.stubs_map.keys()) |key| {
6221 log.debug(" {} => {s}", .{ key, self.getString(key) });6272 log.debug(" {} => {s}", .{ key, self.getString(key) });
src/link/MachO/Atom.zig+84-9
...@@ -403,6 +403,13 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC...@@ -403,6 +403,13 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
403 }403 }
404 try self.addPtrBindingOrRebase(rel, target, context);404 try self.addPtrBindingOrRebase(rel, target, context);
405 },405 },
406 .ARM64_RELOC_TLVP_LOAD_PAGE21,
407 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
408 => {
409 if (target == .global) {
410 try addTlvPtrEntry(target, context);
411 }
412 },
406 else => {},413 else => {},
407 }414 }
408 },415 },
...@@ -452,6 +459,11 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC...@@ -452,6 +459,11 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
452 @intCast(i64, target_sect_base_addr);459 @intCast(i64, target_sect_base_addr);
453 }460 }
454 },461 },
462 .X86_64_RELOC_TLV => {
463 if (target == .global) {
464 try addTlvPtrEntry(target, context);
465 }
466 },
455 else => {},467 else => {},
456 }468 }
457 },469 },
...@@ -531,6 +543,47 @@ fn addPtrBindingOrRebase(...@@ -531,6 +543,47 @@ fn addPtrBindingOrRebase(
531 }543 }
532}544}
533545
546fn addTlvPtrEntry(target: Relocation.Target, context: RelocContext) !void {
547 if (context.macho_file.tlv_ptr_entries_map.contains(target)) return;
548
549 const value_ptr = blk: {
550 if (context.macho_file.tlv_ptr_entries_map_free_list.popOrNull()) |i| {
551 log.debug("reusing __thread_ptrs entry index {d} for {}", .{ i, target });
552 context.macho_file.tlv_ptr_entries_map.keys()[i] = target;
553 const value_ptr = context.macho_file.tlv_ptr_entries_map.getPtr(target).?;
554 break :blk value_ptr;
555 } else {
556 const res = try context.macho_file.tlv_ptr_entries_map.getOrPut(
557 context.macho_file.base.allocator,
558 target,
559 );
560 log.debug("creating new __thread_ptrs entry at index {d} for {}", .{
561 context.macho_file.tlv_ptr_entries_map.getIndex(target).?,
562 target,
563 });
564 break :blk res.value_ptr;
565 }
566 };
567 const atom = try context.macho_file.createTlvPtrAtom(target);
568 value_ptr.* = atom;
569
570 const match = (try context.macho_file.getMatchingSection(.{
571 .segname = MachO.makeStaticString("__DATA"),
572 .sectname = MachO.makeStaticString("__thread_ptrs"),
573 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
574 })).?;
575 if (!context.object.start_atoms.contains(match)) {
576 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
577 }
578 if (context.object.end_atoms.getPtr(match)) |last| {
579 last.*.next = atom;
580 atom.prev = last.*;
581 last.* = atom;
582 } else {
583 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
584 }
585}
586
534fn addGotEntry(target: Relocation.Target, context: RelocContext) !void {587fn addGotEntry(target: Relocation.Target, context: RelocContext) !void {
535 if (context.macho_file.got_entries_map.contains(target)) return;588 if (context.macho_file.got_entries_map.contains(target)) return;
536589
...@@ -667,6 +720,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -667,6 +720,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
667 const sym = macho_file.locals.items[self.local_sym_index];720 const sym = macho_file.locals.items[self.local_sym_index];
668 break :blk sym.n_value + rel.offset;721 break :blk sym.n_value + rel.offset;
669 };722 };
723 var is_via_thread_ptrs: bool = false;
670 const target_addr = blk: {724 const target_addr = blk: {
671 const is_via_got = got: {725 const is_via_got = got: {
672 switch (arch) {726 switch (arch) {
...@@ -742,8 +796,13 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -742,8 +796,13 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
742 .undef => {796 .undef => {
743 break :blk if (macho_file.stubs_map.get(n_strx)) |atom|797 break :blk if (macho_file.stubs_map.get(n_strx)) |atom|
744 macho_file.locals.items[atom.local_sym_index].n_value798 macho_file.locals.items[atom.local_sym_index].n_value
745 else799 else inner: {
746 0;800 if (macho_file.tlv_ptr_entries_map.get(rel.target)) |atom| {
801 is_via_thread_ptrs = true;
802 break :inner macho_file.locals.items[atom.local_sym_index].n_value;
803 }
804 break :inner 0;
805 };
747 },806 },
748 }807 }
749 },808 },
...@@ -854,10 +913,12 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -854,10 +913,12 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
854 },913 },
855 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {914 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
856 const code = self.code.items[rel.offset..][0..4];915 const code = self.code.items[rel.offset..][0..4];
916 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
917
857 const RegInfo = struct {918 const RegInfo = struct {
858 rd: u5,919 rd: u5,
859 rn: u5,920 rn: u5,
860 size: u1,921 size: u2,
861 };922 };
862 const reg_info: RegInfo = blk: {923 const reg_info: RegInfo = blk: {
863 if (isArithmeticOp(code)) {924 if (isArithmeticOp(code)) {
...@@ -878,13 +939,25 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -878,13 +939,25 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
878 break :blk .{939 break :blk .{
879 .rd = inst.rt,940 .rd = inst.rt,
880 .rn = inst.rn,941 .rn = inst.rn,
881 .size = @truncate(u1, inst.size),942 .size = inst.size,
882 };943 };
883 }944 }
884 };945 };
885 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
886 const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));946 const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));
887 var inst = aarch64.Instruction{947 var inst = if (is_via_thread_ptrs) blk: {
948 const offset = try math.divExact(u12, narrowed, 8);
949 break :blk aarch64.Instruction{
950 .load_store_register = .{
951 .rt = reg_info.rd,
952 .rn = reg_info.rn,
953 .offset = offset,
954 .opc = 0b01,
955 .op1 = 0b01,
956 .v = 0,
957 .size = reg_info.size,
958 },
959 };
960 } else aarch64.Instruction{
888 .add_subtract_immediate = .{961 .add_subtract_immediate = .{
889 .rd = reg_info.rd,962 .rd = reg_info.rd,
890 .rn = reg_info.rn,963 .rn = reg_info.rn,
...@@ -892,7 +965,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -892,7 +965,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
892 .sh = 0,965 .sh = 0,
893 .s = 0,966 .s = 0,
894 .op = 0,967 .op = 0,
895 .sf = reg_info.size,968 .sf = @truncate(u1, reg_info.size),
896 },969 },
897 };970 };
898 mem.writeIntLittle(u32, code, inst.toU32());971 mem.writeIntLittle(u32, code, inst.toU32());
...@@ -942,8 +1015,10 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -942,8 +1015,10 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
942 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));1015 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));
943 },1016 },
944 .X86_64_RELOC_TLV => {1017 .X86_64_RELOC_TLV => {
945 // We need to rewrite the opcode from movq to leaq.1018 if (!is_via_thread_ptrs) {
946 self.code.items[rel.offset - 2] = 0x8d;1019 // We need to rewrite the opcode from movq to leaq.
1020 self.code.items[rel.offset - 2] = 0x8d;
1021 }
947 const displacement = try math.cast(1022 const displacement = try math.cast(
948 i32,1023 i32,
949 @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend,1024 @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend,