diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2024-01-21 10:32:06 +0100 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2024-01-24 12:34:42 +0100 |
| commit | 3a6410959ca6df6f020547c58845730753dc9e97 (patch) | |
| tree | a8e3f8b35f617fcd903d97be7c478527edb65632 /src/link | |
| parent | 411c7f6669ed2eb758f371dfde59e03abf05aa0a (diff) | |
| download | zig-3a6410959ca6df6f020547c58845730753dc9e97.tar.gz zig-3a6410959ca6df6f020547c58845730753dc9e97.zip | |
macho: actually lower TLS variables
Diffstat (limited to 'src/link')
| -rw-r--r-- | src/link/MachO.zig | 17 | ||||
| -rw-r--r-- | src/link/MachO/ZigObject.zig | 168 | ||||
| -rw-r--r-- | src/link/MachO/dyld_info/Rebase.zig | 4 | ||||
| -rw-r--r-- | src/link/MachO/dyld_info/bind.zig | 8 |
4 files changed, 162 insertions, 35 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig index cba79e1262..9a4bdfcc86 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -606,7 +606,10 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node if (!atom.flags.alive) continue; const sect = &self.sections.items(.header)[atom.out_n_sect]; if (sect.isZerofill()) continue; - const code = zo.getAtomDataAlloc(self, atom.*) catch |err| switch (err) { + if (mem.indexOf(u8, sect.segName(), "ZIG") == null) continue; // Non-Zig sections are handled separately + // TODO: we will resolve and write ZigObject's TLS data twice: + // once here, and once in writeAtoms + const code = zo.getAtomDataAlloc(self, gpa, atom.*) catch |err| switch (err) { error.InputOutput => { try self.reportUnexpectedError("fetching code for '{s}' failed", .{ atom.getName(self), @@ -1806,7 +1809,7 @@ fn initOutputSections(self: *MachO) !void { .aarch64 => 2, else => unreachable, }, - .flags = macho.S_SYMBOL_STUBS | + .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, }); } @@ -2545,6 +2548,9 @@ fn writeAtoms(self: *MachO) !void { defer tracy.end(); const gpa = self.base.comp.gpa; + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + const cpu_arch = self.getTarget().cpu.arch; const slice = self.sections.slice(); @@ -2562,7 +2568,12 @@ fn writeAtoms(self: *MachO) !void { const atom = self.getAtom(atom_index).?; assert(atom.flags.alive); const off = atom.value - header.addr; - @memcpy(buffer[off..][0..atom.size], atom.getFile(self).object.getAtomData(atom.*)); + const data = switch (atom.getFile(self)) { + .object => |x| x.getAtomData(atom.*), + .zig_object => |x| try x.getAtomDataAlloc(self, arena.allocator(), atom.*), + else => unreachable, + }; + @memcpy(buffer[off..][0..atom.size], data); atom.resolveRelocs(self, buffer[off..][0..atom.size]) catch |err| switch (err) { error.ResolveFailed => has_resolve_error = true, else => |e| return e, diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 4b08131f76..5c1fc9971f 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -38,8 +38,8 @@ unnamed_consts: UnnamedConstTable = .{}, /// Table of tracked AnonDecls. anon_decls: AnonDeclTable = .{}, -/// TLS variables indexed by Atom.Index. -tls_variables: TlsTable = .{}, +/// TLV initializers indexed by Atom.Index. +tlv_initializers: TlvInitializerTable = .{}, /// A table of relocations. relocs: RelocationTable = .{}, @@ -91,10 +91,10 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void { } self.relocs.deinit(allocator); - for (self.tls_variables.values()) |*tlv| { - tlv.deinit(allocator); + for (self.tlv_initializers.values()) |*tlv_init| { + tlv_init.deinit(allocator); } - self.tls_variables.deinit(allocator); + self.tlv_initializers.deinit(allocator); } fn addNlist(self: *ZigObject, allocator: Allocator) !Symbol.Index { @@ -137,25 +137,35 @@ pub fn addAtom(self: *ZigObject, macho_file: *MachO) !Symbol.Index { } /// Caller owns the memory. -pub fn getAtomDataAlloc(self: ZigObject, macho_file: *MachO, atom: Atom) ![]u8 { - const gpa = macho_file.base.comp.gpa; +pub fn getAtomDataAlloc( + self: ZigObject, + macho_file: *MachO, + allocator: Allocator, + atom: Atom, +) ![]u8 { assert(atom.file == self.index); const sect = macho_file.sections.items(.header)[atom.out_n_sect]; + assert(!sect.isZerofill()); switch (sect.type()) { macho.S_THREAD_LOCAL_REGULAR => { - const tlv = self.tls_variables.get(atom.atom_index).?; - const code = try gpa.dupe(u8, tlv.code); - return code; + const tlv = self.tlv_initializers.get(atom.atom_index).?; + const data = try allocator.dupe(u8, tlv.data); + return data; + }, + macho.S_THREAD_LOCAL_VARIABLES => { + const data = try allocator.alloc(u8, atom.size); + @memset(data, 0); + return data; }, else => { const file_offset = sect.offset + atom.value - sect.addr; const size = std.math.cast(usize, atom.size) orelse return error.Overflow; - const code = try gpa.alloc(u8, size); - errdefer gpa.free(code); - const amt = try macho_file.base.file.?.preadAll(code, file_offset); - if (amt != code.len) return error.InputOutput; - return code; + const data = try allocator.alloc(u8, size); + errdefer allocator.free(data); + const amt = try macho_file.base.file.?.preadAll(data, file_offset); + if (amt != data.len) return error.InputOutput; + return data; }, } } @@ -421,7 +431,7 @@ pub fn lowerAnonDecl( self: *ZigObject, macho_file: *MachO, decl_val: InternPool.Index, - explicit_alignment: InternPool.Alignment, + explicit_alignment: Atom.Alignment, src_loc: Module.SrcLoc, ) !codegen.Result { const gpa = macho_file.base.comp.gpa; @@ -732,6 +742,9 @@ fn updateDeclCode( } } +/// Lowering a TLV on macOS involves two stages: +/// 1. first we lower the initializer into appopriate section (__thread_data or __thread_bss) +/// 2. next, we create a corresponding threadlocal variable descriptor in __thread_vars fn updateTlv( self: *ZigObject, macho_file: *MachO, @@ -740,9 +753,7 @@ fn updateTlv( sect_index: u8, code: []const u8, ) !void { - const comp = macho_file.base.comp; - const gpa = comp.gpa; - const mod = comp.module.?; + const mod = macho_file.base.comp.module.?; const decl = mod.declPtr(decl_index); const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)); @@ -750,6 +761,32 @@ fn updateTlv( const required_alignment = decl.getAlignment(mod); + // 1. Lower TLV initializer + const init_sym_index = try self.createTlvInitializer( + macho_file, + decl_name, + required_alignment, + sect_index, + code, + ); + + // 2. Create TLV descriptor + try self.createTlvDescriptor(macho_file, sym_index, init_sym_index, decl_name); +} + +fn createTlvInitializer( + self: *ZigObject, + macho_file: *MachO, + name: []const u8, + alignment: Atom.Alignment, + sect_index: u8, + code: []const u8, +) !Symbol.Index { + const gpa = macho_file.base.comp.gpa; + const sym_name = try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{name}); + defer gpa.free(sym_name); + + const sym_index = try self.addAtom(macho_file); const sym = macho_file.getSymbol(sym_index); const nlist = &self.symtab.items(.nlist)[sym.nlist_idx]; const atom = sym.getAtom(macho_file).?; @@ -758,7 +795,7 @@ fn updateTlv( atom.out_n_sect = sect_index; sym.value = 0; - sym.name = try macho_file.strings.insert(gpa, decl_name); + sym.name = try macho_file.strings.insert(gpa, sym_name); atom.flags.alive = true; atom.name = sym.name; nlist.n_strx = sym.name; @@ -767,23 +804,94 @@ fn updateTlv( nlist.n_value = 0; self.symtab.items(.size)[sym.nlist_idx] = code.len; - atom.alignment = required_alignment; + atom.alignment = alignment; atom.size = code.len; const slice = macho_file.sections.slice(); const header = slice.items(.header)[sect_index]; const atoms = &slice.items(.atoms)[sect_index]; - const gop = try self.tls_variables.getOrPut(gpa, atom.atom_index); + const gop = try self.tlv_initializers.getOrPut(gpa, atom.atom_index); assert(!gop.found_existing); // TODO incremental updates gop.value_ptr.* = .{ .symbol_index = sym_index }; // We only store the data for the TLV if it's non-zerofill. if (!header.isZerofill()) { - gop.value_ptr.code = try gpa.dupe(u8, code); + gop.value_ptr.data = try gpa.dupe(u8, code); } try atoms.append(gpa, atom.atom_index); + + return sym_index; +} + +fn createTlvDescriptor( + self: *ZigObject, + macho_file: *MachO, + sym_index: Symbol.Index, + init_sym_index: Symbol.Index, + name: []const u8, +) !void { + const gpa = macho_file.base.comp.gpa; + + const sym = macho_file.getSymbol(sym_index); + const nlist = &self.symtab.items(.nlist)[sym.nlist_idx]; + const atom = sym.getAtom(macho_file).?; + const alignment = Atom.Alignment.fromNonzeroByteUnits(@alignOf(u64)); + const size: u64 = @sizeOf(u64) * 3; + + const sect_index = macho_file.getSectionByName("__DATA", "__thread_vars") orelse + try macho_file.addSection("__DATA", "__thread_vars", .{ + .flags = macho.S_THREAD_LOCAL_VARIABLES, + }); + sym.out_n_sect = sect_index; + atom.out_n_sect = sect_index; + + sym.value = 0; + sym.name = try macho_file.strings.insert(gpa, name); + atom.flags.alive = true; + atom.name = sym.name; + nlist.n_strx = sym.name; + nlist.n_sect = sect_index + 1; + nlist.n_type = macho.N_SECT; + nlist.n_value = 0; + self.symtab.items(.size)[sym.nlist_idx] = size; + + atom.alignment = alignment; + atom.size = size; + + const tlv_bootstrap_index = blk: { + const index = try self.getGlobalSymbol(macho_file, "_tlv_bootstrap", null); + break :blk self.symbols.items[index]; + }; + try atom.addReloc(macho_file, .{ + .tag = .@"extern", + .offset = 0, + .target = tlv_bootstrap_index, + .addend = 0, + .type = .unsigned, + .meta = .{ + .pcrel = false, + .has_subtractor = false, + .length = 3, + .symbolnum = 0, + }, + }); + try atom.addReloc(macho_file, .{ + .tag = .@"extern", + .offset = 16, + .target = init_sym_index, + .addend = 0, + .type = .unsigned, + .meta = .{ + .pcrel = false, + .has_subtractor = false, + .length = 3, + .symbolnum = 0, + }, + }); + + try macho_file.sections.items(.atoms)[sect_index].append(gpa, atom.atom_index); } fn getDeclOutputSection( @@ -888,7 +996,7 @@ fn lowerConst( macho_file: *MachO, name: []const u8, tv: TypedValue, - required_alignment: InternPool.Alignment, + required_alignment: Atom.Alignment, output_section_index: u8, src_loc: Module.SrcLoc, ) !LowerConstResult { @@ -1040,7 +1148,7 @@ fn updateLazySymbol( const gpa = macho_file.base.comp.gpa; const mod = macho_file.base.comp.module.?; - var required_alignment: InternPool.Alignment = .none; + var required_alignment: Atom.Alignment = .none; var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); @@ -1315,12 +1423,12 @@ const LazySymbolMetadata = struct { const_state: State = .unused, }; -const TlsVariable = struct { +const TlvInitializer = struct { symbol_index: Symbol.Index, - code: []const u8 = &[0]u8{}, + data: []const u8 = &[0]u8{}, - fn deinit(tlv: *TlsVariable, allocator: Allocator) void { - allocator.free(tlv.code); + fn deinit(tlv_init: *TlvInitializer, allocator: Allocator) void { + allocator.free(tlv_init.data); } }; @@ -1329,7 +1437,7 @@ const UnnamedConstTable = std.AutoHashMapUnmanaged(InternPool.DeclIndex, std.Arr const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, DeclMetadata); const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.OptionalDeclIndex, LazySymbolMetadata); const RelocationTable = std.ArrayListUnmanaged(std.ArrayListUnmanaged(Relocation)); -const TlsTable = std.AutoArrayHashMapUnmanaged(Atom.Index, TlsVariable); +const TlvInitializerTable = std.AutoArrayHashMapUnmanaged(Atom.Index, TlvInitializer); const assert = std.debug.assert; const builtin = @import("builtin"); diff --git a/src/link/MachO/dyld_info/Rebase.zig b/src/link/MachO/dyld_info/Rebase.zig index ffad0362f9..5209dcd9f4 100644 --- a/src/link/MachO/dyld_info/Rebase.zig +++ b/src/link/MachO/dyld_info/Rebase.zig @@ -3,7 +3,7 @@ const Rebase = @This(); const std = @import("std"); const assert = std.debug.assert; const leb = std.leb; -const log = std.log.scoped(.dyld_info); +const log = std.log.scoped(.link_dyld_info); const macho = std.macho; const testing = std.testing; @@ -39,6 +39,8 @@ pub fn finalize(rebase: *Rebase, gpa: Allocator) !void { const writer = rebase.buffer.writer(gpa); + log.debug("rebase opcodes", .{}); + std.mem.sort(Entry, rebase.entries.items, {}, Entry.lessThan); try setTypePointer(writer); diff --git a/src/link/MachO/dyld_info/bind.zig b/src/link/MachO/dyld_info/bind.zig index cee57e1edf..bb746b05e3 100644 --- a/src/link/MachO/dyld_info/bind.zig +++ b/src/link/MachO/dyld_info/bind.zig @@ -1,7 +1,7 @@ const std = @import("std"); const assert = std.debug.assert; const leb = std.leb; -const log = std.log.scoped(.dyld_info); +const log = std.log.scoped(.link_dyld_info); const macho = std.macho; const testing = std.testing; @@ -48,6 +48,8 @@ pub const Bind = struct { const writer = self.buffer.writer(gpa); + log.debug("bind opcodes", .{}); + std.mem.sort(Entry, self.entries.items, ctx, Entry.lessThan); var start: usize = 0; @@ -201,6 +203,8 @@ pub const WeakBind = struct { const writer = self.buffer.writer(gpa); + log.debug("weak bind opcodes", .{}); + std.mem.sort(Entry, self.entries.items, ctx, Entry.lessThan); var start: usize = 0; @@ -348,6 +352,8 @@ pub const LazyBind = struct { var cwriter = std.io.countingWriter(self.buffer.writer(gpa)); const writer = cwriter.writer(); + log.debug("lazy bind opcodes", .{}); + var addend: i64 = 0; for (self.entries.items) |entry| { |
