| ... | @@ -38,8 +38,8 @@ unnamed_consts: UnnamedConstTable = .{}, | ... | @@ -38,8 +38,8 @@ unnamed_consts: UnnamedConstTable = .{}, |
| 38 | /// Table of tracked AnonDecls. | 38 | /// Table of tracked AnonDecls. |
| 39 | anon_decls: AnonDeclTable = .{}, | 39 | anon_decls: AnonDeclTable = .{}, |
| 40 | | 40 | |
| 41 | /// TLS variables indexed by Atom.Index. | 41 | /// TLV initializers indexed by Atom.Index. |
| 42 | tls_variables: TlsTable = .{}, | 42 | tlv_initializers: TlvInitializerTable = .{}, |
| 43 | | 43 | |
| 44 | /// A table of relocations. | 44 | /// A table of relocations. |
| 45 | relocs: RelocationTable = .{}, | 45 | relocs: RelocationTable = .{}, |
| ... | @@ -91,10 +91,10 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void { | ... | @@ -91,10 +91,10 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void { |
| 91 | } | 91 | } |
| 92 | self.relocs.deinit(allocator); | 92 | self.relocs.deinit(allocator); |
| 93 | | 93 | |
| 94 | for (self.tls_variables.values()) |*tlv| { | 94 | for (self.tlv_initializers.values()) |*tlv_init| { |
| 95 | tlv.deinit(allocator); | 95 | tlv_init.deinit(allocator); |
| 96 | } | 96 | } |
| 97 | self.tls_variables.deinit(allocator); | 97 | self.tlv_initializers.deinit(allocator); |
| 98 | } | 98 | } |
| 99 | | 99 | |
| 100 | fn addNlist(self: *ZigObject, allocator: Allocator) !Symbol.Index { | 100 | fn addNlist(self: *ZigObject, allocator: Allocator) !Symbol.Index { |
| ... | @@ -137,25 +137,35 @@ pub fn addAtom(self: *ZigObject, macho_file: *MachO) !Symbol.Index { | ... | @@ -137,25 +137,35 @@ pub fn addAtom(self: *ZigObject, macho_file: *MachO) !Symbol.Index { |
| 137 | } | 137 | } |
| 138 | | 138 | |
| 139 | /// Caller owns the memory. | 139 | /// Caller owns the memory. |
| 140 | pub fn getAtomDataAlloc(self: ZigObject, macho_file: *MachO, atom: Atom) ![]u8 { | 140 | pub fn getAtomDataAlloc( |
| 141 | const gpa = macho_file.base.comp.gpa; | 141 | self: ZigObject, |
| | 142 | macho_file: *MachO, |
| | 143 | allocator: Allocator, |
| | 144 | atom: Atom, |
| | 145 | ) ![]u8 { |
| 142 | assert(atom.file == self.index); | 146 | assert(atom.file == self.index); |
| 143 | const sect = macho_file.sections.items(.header)[atom.out_n_sect]; | 147 | const sect = macho_file.sections.items(.header)[atom.out_n_sect]; |
| | 148 | assert(!sect.isZerofill()); |
| 144 | | 149 | |
| 145 | switch (sect.type()) { | 150 | switch (sect.type()) { |
| 146 | macho.S_THREAD_LOCAL_REGULAR => { | 151 | macho.S_THREAD_LOCAL_REGULAR => { |
| 147 | const tlv = self.tls_variables.get(atom.atom_index).?; | 152 | const tlv = self.tlv_initializers.get(atom.atom_index).?; |
| 148 | const code = try gpa.dupe(u8, tlv.code); | 153 | const data = try allocator.dupe(u8, tlv.data); |
| 149 | return code; | 154 | return data; |
| | 155 | }, |
| | 156 | macho.S_THREAD_LOCAL_VARIABLES => { |
| | 157 | const data = try allocator.alloc(u8, atom.size); |
| | 158 | @memset(data, 0); |
| | 159 | return data; |
| 150 | }, | 160 | }, |
| 151 | else => { | 161 | else => { |
| 152 | const file_offset = sect.offset + atom.value - sect.addr; | 162 | const file_offset = sect.offset + atom.value - sect.addr; |
| 153 | const size = std.math.cast(usize, atom.size) orelse return error.Overflow; | 163 | const size = std.math.cast(usize, atom.size) orelse return error.Overflow; |
| 154 | const code = try gpa.alloc(u8, size); | 164 | const data = try allocator.alloc(u8, size); |
| 155 | errdefer gpa.free(code); | 165 | errdefer allocator.free(data); |
| 156 | const amt = try macho_file.base.file.?.preadAll(code, file_offset); | 166 | const amt = try macho_file.base.file.?.preadAll(data, file_offset); |
| 157 | if (amt != code.len) return error.InputOutput; | 167 | if (amt != data.len) return error.InputOutput; |
| 158 | return code; | 168 | return data; |
| 159 | }, | 169 | }, |
| 160 | } | 170 | } |
| 161 | } | 171 | } |
| ... | @@ -421,7 +431,7 @@ pub fn lowerAnonDecl( | ... | @@ -421,7 +431,7 @@ pub fn lowerAnonDecl( |
| 421 | self: *ZigObject, | 431 | self: *ZigObject, |
| 422 | macho_file: *MachO, | 432 | macho_file: *MachO, |
| 423 | decl_val: InternPool.Index, | 433 | decl_val: InternPool.Index, |
| 424 | explicit_alignment: InternPool.Alignment, | 434 | explicit_alignment: Atom.Alignment, |
| 425 | src_loc: Module.SrcLoc, | 435 | src_loc: Module.SrcLoc, |
| 426 | ) !codegen.Result { | 436 | ) !codegen.Result { |
| 427 | const gpa = macho_file.base.comp.gpa; | 437 | const gpa = macho_file.base.comp.gpa; |
| ... | @@ -732,6 +742,9 @@ fn updateDeclCode( | ... | @@ -732,6 +742,9 @@ fn updateDeclCode( |
| 732 | } | 742 | } |
| 733 | } | 743 | } |
| 734 | | 744 | |
| | 745 | /// Lowering a TLV on macOS involves two stages: |
| | 746 | /// 1. first we lower the initializer into appopriate section (__thread_data or __thread_bss) |
| | 747 | /// 2. next, we create a corresponding threadlocal variable descriptor in __thread_vars |
| 735 | fn updateTlv( | 748 | fn updateTlv( |
| 736 | self: *ZigObject, | 749 | self: *ZigObject, |
| 737 | macho_file: *MachO, | 750 | macho_file: *MachO, |
| ... | @@ -740,9 +753,7 @@ fn updateTlv( | ... | @@ -740,9 +753,7 @@ fn updateTlv( |
| 740 | sect_index: u8, | 753 | sect_index: u8, |
| 741 | code: []const u8, | 754 | code: []const u8, |
| 742 | ) !void { | 755 | ) !void { |
| 743 | const comp = macho_file.base.comp; | 756 | const mod = macho_file.base.comp.module.?; |
| 744 | const gpa = comp.gpa; | | |
| 745 | const mod = comp.module.?; | | |
| 746 | const decl = mod.declPtr(decl_index); | 757 | const decl = mod.declPtr(decl_index); |
| 747 | const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)); | 758 | const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)); |
| 748 | | 759 | |
| ... | @@ -750,6 +761,32 @@ fn updateTlv( | ... | @@ -750,6 +761,32 @@ fn updateTlv( |
| 750 | | 761 | |
| 751 | const required_alignment = decl.getAlignment(mod); | 762 | const required_alignment = decl.getAlignment(mod); |
| 752 | | 763 | |
| | 764 | // 1. Lower TLV initializer |
| | 765 | const init_sym_index = try self.createTlvInitializer( |
| | 766 | macho_file, |
| | 767 | decl_name, |
| | 768 | required_alignment, |
| | 769 | sect_index, |
| | 770 | code, |
| | 771 | ); |
| | 772 | |
| | 773 | // 2. Create TLV descriptor |
| | 774 | try self.createTlvDescriptor(macho_file, sym_index, init_sym_index, decl_name); |
| | 775 | } |
| | 776 | |
| | 777 | fn createTlvInitializer( |
| | 778 | self: *ZigObject, |
| | 779 | macho_file: *MachO, |
| | 780 | name: []const u8, |
| | 781 | alignment: Atom.Alignment, |
| | 782 | sect_index: u8, |
| | 783 | code: []const u8, |
| | 784 | ) !Symbol.Index { |
| | 785 | const gpa = macho_file.base.comp.gpa; |
| | 786 | const sym_name = try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{name}); |
| | 787 | defer gpa.free(sym_name); |
| | 788 | |
| | 789 | const sym_index = try self.addAtom(macho_file); |
| 753 | const sym = macho_file.getSymbol(sym_index); | 790 | const sym = macho_file.getSymbol(sym_index); |
| 754 | const nlist = &self.symtab.items(.nlist)[sym.nlist_idx]; | 791 | const nlist = &self.symtab.items(.nlist)[sym.nlist_idx]; |
| 755 | const atom = sym.getAtom(macho_file).?; | 792 | const atom = sym.getAtom(macho_file).?; |
| ... | @@ -758,7 +795,7 @@ fn updateTlv( | ... | @@ -758,7 +795,7 @@ fn updateTlv( |
| 758 | atom.out_n_sect = sect_index; | 795 | atom.out_n_sect = sect_index; |
| 759 | | 796 | |
| 760 | sym.value = 0; | 797 | sym.value = 0; |
| 761 | sym.name = try macho_file.strings.insert(gpa, decl_name); | 798 | sym.name = try macho_file.strings.insert(gpa, sym_name); |
| 762 | atom.flags.alive = true; | 799 | atom.flags.alive = true; |
| 763 | atom.name = sym.name; | 800 | atom.name = sym.name; |
| 764 | nlist.n_strx = sym.name; | 801 | nlist.n_strx = sym.name; |
| ... | @@ -767,23 +804,94 @@ fn updateTlv( | ... | @@ -767,23 +804,94 @@ fn updateTlv( |
| 767 | nlist.n_value = 0; | 804 | nlist.n_value = 0; |
| 768 | self.symtab.items(.size)[sym.nlist_idx] = code.len; | 805 | self.symtab.items(.size)[sym.nlist_idx] = code.len; |
| 769 | | 806 | |
| 770 | atom.alignment = required_alignment; | 807 | atom.alignment = alignment; |
| 771 | atom.size = code.len; | 808 | atom.size = code.len; |
| 772 | | 809 | |
| 773 | const slice = macho_file.sections.slice(); | 810 | const slice = macho_file.sections.slice(); |
| 774 | const header = slice.items(.header)[sect_index]; | 811 | const header = slice.items(.header)[sect_index]; |
| 775 | const atoms = &slice.items(.atoms)[sect_index]; | 812 | const atoms = &slice.items(.atoms)[sect_index]; |
| 776 | | 813 | |
| 777 | const gop = try self.tls_variables.getOrPut(gpa, atom.atom_index); | 814 | const gop = try self.tlv_initializers.getOrPut(gpa, atom.atom_index); |
| 778 | assert(!gop.found_existing); // TODO incremental updates | 815 | assert(!gop.found_existing); // TODO incremental updates |
| 779 | gop.value_ptr.* = .{ .symbol_index = sym_index }; | 816 | gop.value_ptr.* = .{ .symbol_index = sym_index }; |
| 780 | | 817 | |
| 781 | // We only store the data for the TLV if it's non-zerofill. | 818 | // We only store the data for the TLV if it's non-zerofill. |
| 782 | if (!header.isZerofill()) { | 819 | if (!header.isZerofill()) { |
| 783 | gop.value_ptr.code = try gpa.dupe(u8, code); | 820 | gop.value_ptr.data = try gpa.dupe(u8, code); |
| 784 | } | 821 | } |
| 785 | | 822 | |
| 786 | try atoms.append(gpa, atom.atom_index); | 823 | try atoms.append(gpa, atom.atom_index); |
| | 824 | |
| | 825 | return sym_index; |
| | 826 | } |
| | 827 | |
| | 828 | fn createTlvDescriptor( |
| | 829 | self: *ZigObject, |
| | 830 | macho_file: *MachO, |
| | 831 | sym_index: Symbol.Index, |
| | 832 | init_sym_index: Symbol.Index, |
| | 833 | name: []const u8, |
| | 834 | ) !void { |
| | 835 | const gpa = macho_file.base.comp.gpa; |
| | 836 | |
| | 837 | const sym = macho_file.getSymbol(sym_index); |
| | 838 | const nlist = &self.symtab.items(.nlist)[sym.nlist_idx]; |
| | 839 | const atom = sym.getAtom(macho_file).?; |
| | 840 | const alignment = Atom.Alignment.fromNonzeroByteUnits(@alignOf(u64)); |
| | 841 | const size: u64 = @sizeOf(u64) * 3; |
| | 842 | |
| | 843 | const sect_index = macho_file.getSectionByName("__DATA", "__thread_vars") orelse |
| | 844 | try macho_file.addSection("__DATA", "__thread_vars", .{ |
| | 845 | .flags = macho.S_THREAD_LOCAL_VARIABLES, |
| | 846 | }); |
| | 847 | sym.out_n_sect = sect_index; |
| | 848 | atom.out_n_sect = sect_index; |
| | 849 | |
| | 850 | sym.value = 0; |
| | 851 | sym.name = try macho_file.strings.insert(gpa, name); |
| | 852 | atom.flags.alive = true; |
| | 853 | atom.name = sym.name; |
| | 854 | nlist.n_strx = sym.name; |
| | 855 | nlist.n_sect = sect_index + 1; |
| | 856 | nlist.n_type = macho.N_SECT; |
| | 857 | nlist.n_value = 0; |
| | 858 | self.symtab.items(.size)[sym.nlist_idx] = size; |
| | 859 | |
| | 860 | atom.alignment = alignment; |
| | 861 | atom.size = size; |
| | 862 | |
| | 863 | const tlv_bootstrap_index = blk: { |
| | 864 | const index = try self.getGlobalSymbol(macho_file, "_tlv_bootstrap", null); |
| | 865 | break :blk self.symbols.items[index]; |
| | 866 | }; |
| | 867 | try atom.addReloc(macho_file, .{ |
| | 868 | .tag = .@"extern", |
| | 869 | .offset = 0, |
| | 870 | .target = tlv_bootstrap_index, |
| | 871 | .addend = 0, |
| | 872 | .type = .unsigned, |
| | 873 | .meta = .{ |
| | 874 | .pcrel = false, |
| | 875 | .has_subtractor = false, |
| | 876 | .length = 3, |
| | 877 | .symbolnum = 0, |
| | 878 | }, |
| | 879 | }); |
| | 880 | try atom.addReloc(macho_file, .{ |
| | 881 | .tag = .@"extern", |
| | 882 | .offset = 16, |
| | 883 | .target = init_sym_index, |
| | 884 | .addend = 0, |
| | 885 | .type = .unsigned, |
| | 886 | .meta = .{ |
| | 887 | .pcrel = false, |
| | 888 | .has_subtractor = false, |
| | 889 | .length = 3, |
| | 890 | .symbolnum = 0, |
| | 891 | }, |
| | 892 | }); |
| | 893 | |
| | 894 | try macho_file.sections.items(.atoms)[sect_index].append(gpa, atom.atom_index); |
| 787 | } | 895 | } |
| 788 | | 896 | |
| 789 | fn getDeclOutputSection( | 897 | fn getDeclOutputSection( |
| ... | @@ -888,7 +996,7 @@ fn lowerConst( | ... | @@ -888,7 +996,7 @@ fn lowerConst( |
| 888 | macho_file: *MachO, | 996 | macho_file: *MachO, |
| 889 | name: []const u8, | 997 | name: []const u8, |
| 890 | tv: TypedValue, | 998 | tv: TypedValue, |
| 891 | required_alignment: InternPool.Alignment, | 999 | required_alignment: Atom.Alignment, |
| 892 | output_section_index: u8, | 1000 | output_section_index: u8, |
| 893 | src_loc: Module.SrcLoc, | 1001 | src_loc: Module.SrcLoc, |
| 894 | ) !LowerConstResult { | 1002 | ) !LowerConstResult { |
| ... | @@ -1040,7 +1148,7 @@ fn updateLazySymbol( | ... | @@ -1040,7 +1148,7 @@ fn updateLazySymbol( |
| 1040 | const gpa = macho_file.base.comp.gpa; | 1148 | const gpa = macho_file.base.comp.gpa; |
| 1041 | const mod = macho_file.base.comp.module.?; | 1149 | const mod = macho_file.base.comp.module.?; |
| 1042 | | 1150 | |
| 1043 | var required_alignment: InternPool.Alignment = .none; | 1151 | var required_alignment: Atom.Alignment = .none; |
| 1044 | var code_buffer = std.ArrayList(u8).init(gpa); | 1152 | var code_buffer = std.ArrayList(u8).init(gpa); |
| 1045 | defer code_buffer.deinit(); | 1153 | defer code_buffer.deinit(); |
| 1046 | | 1154 | |
| ... | @@ -1315,12 +1423,12 @@ const LazySymbolMetadata = struct { | ... | @@ -1315,12 +1423,12 @@ const LazySymbolMetadata = struct { |
| 1315 | const_state: State = .unused, | 1423 | const_state: State = .unused, |
| 1316 | }; | 1424 | }; |
| 1317 | | 1425 | |
| 1318 | const TlsVariable = struct { | 1426 | const TlvInitializer = struct { |
| 1319 | symbol_index: Symbol.Index, | 1427 | symbol_index: Symbol.Index, |
| 1320 | code: []const u8 = &[0]u8{}, | 1428 | data: []const u8 = &[0]u8{}, |
| 1321 | | 1429 | |
| 1322 | fn deinit(tlv: *TlsVariable, allocator: Allocator) void { | 1430 | fn deinit(tlv_init: *TlvInitializer, allocator: Allocator) void { |
| 1323 | allocator.free(tlv.code); | 1431 | allocator.free(tlv_init.data); |
| 1324 | } | 1432 | } |
| 1325 | }; | 1433 | }; |
| 1326 | | 1434 | |
| ... | @@ -1329,7 +1437,7 @@ const UnnamedConstTable = std.AutoHashMapUnmanaged(InternPool.DeclIndex, std.Arr | ... | @@ -1329,7 +1437,7 @@ const UnnamedConstTable = std.AutoHashMapUnmanaged(InternPool.DeclIndex, std.Arr |
| 1329 | const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, DeclMetadata); | 1437 | const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, DeclMetadata); |
| 1330 | const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.OptionalDeclIndex, LazySymbolMetadata); | 1438 | const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.OptionalDeclIndex, LazySymbolMetadata); |
| 1331 | const RelocationTable = std.ArrayListUnmanaged(std.ArrayListUnmanaged(Relocation)); | 1439 | const RelocationTable = std.ArrayListUnmanaged(std.ArrayListUnmanaged(Relocation)); |
| 1332 | const TlsTable = std.AutoArrayHashMapUnmanaged(Atom.Index, TlsVariable); | 1440 | const TlvInitializerTable = std.AutoArrayHashMapUnmanaged(Atom.Index, TlvInitializer); |
| 1333 | | 1441 | |
| 1334 | const assert = std.debug.assert; | 1442 | const assert = std.debug.assert; |
| 1335 | const builtin = @import("builtin"); | 1443 | const builtin = @import("builtin"); |