diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2023-04-07 07:37:35 +0200 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2023-04-13 11:47:51 +0200 |
| commit | 094ff60252cf57b2ae7bb69abb3e8e696873a3be (patch) | |
| tree | 34b87d0d8962450c64de422fc2438236f3d70f8e /src | |
| parent | 09b6bd86dea690aeb1fe1a832bb56b44b414120b (diff) | |
| download | zig-094ff60252cf57b2ae7bb69abb3e8e696873a3be.tar.gz zig-094ff60252cf57b2ae7bb69abb3e8e696873a3be.zip | |
macho: emit TLV pointers and variables
Diffstat (limited to 'src')
| -rw-r--r-- | src/link.zig | 4 | ||||
| -rw-r--r-- | src/link/MachO.zig | 150 | ||||
| -rw-r--r-- | src/link/MachO/Relocation.zig | 17 |
3 files changed, 135 insertions, 36 deletions
diff --git a/src/link.zig b/src/link.zig index 18002f64a6..c8999cee47 100644 --- a/src/link.zig +++ b/src/link.zig @@ -540,7 +540,7 @@ pub const File = struct { /// May be called before or after updateDeclExports for any given Decl. pub fn updateDecl(base: *File, module: *Module, decl_index: Module.Decl.Index) UpdateDeclError!void { const decl = module.declPtr(decl_index); - log.debug("updateDecl {*} ({s}), type={}", .{ decl, decl.name, decl.ty.fmtDebug() }); + log.debug("updateDecl {*} ({s}), type={}", .{ decl, decl.name, decl.ty.fmt(module) }); assert(decl.has_tv); if (build_options.only_c) { assert(base.tag == .c); @@ -564,7 +564,7 @@ pub const File = struct { pub fn updateFunc(base: *File, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) UpdateDeclError!void { const owner_decl = module.declPtr(func.owner_decl); log.debug("updateFunc {*} ({s}), type={}", .{ - owner_decl, owner_decl.name, owner_decl.ty.fmtDebug(), + owner_decl, owner_decl.name, owner_decl.ty.fmt(module), }); if (build_options.only_c) { assert(base.tag == .c); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 694d115cdd..b958e64e58 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1338,13 +1338,12 @@ pub fn createAtom(self: *MachO) !Atom.Index { pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !Atom.Index { const atom_index = try self.createAtom(); - const atom = self.getAtomPtr(atom_index); - atom.size = @sizeOf(u64); + self.getAtomPtr(atom_index).size = @sizeOf(u64); - const sym = atom.getSymbolPtr(self); + const sym = self.getAtom(atom_index).getSymbolPtr(self); sym.n_type = macho.N_SECT; sym.n_sect = self.got_section_index.? + 1; - sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64)); + sym.n_value = try self.allocateAtom(atom_index, @sizeOf(u64), @alignOf(u64)); log.debug("allocated GOT atom at 0x{x}", .{sym.n_value}); @@ -1553,7 +1552,7 @@ fn createStubHelperAtom(self: *MachO) !Atom.Index { try Atom.addRelocation(self, atom_index, .{ .type = .branch, - .target = .{ .sym_index = stub_helper_preamble_atom_sym_index, .file = null }, + .target = .{ .sym_index = stub_helper_preamble_atom_sym_index }, .offset = 6, .addend = 0, .pcrel = true, @@ -1576,7 +1575,7 @@ fn createStubHelperAtom(self: *MachO) !Atom.Index { try Atom.addRelocation(self, atom_index, .{ .type = .branch, - .target = .{ .sym_index = stub_helper_preamble_atom_sym_index, .file = null }, + .target = .{ .sym_index = stub_helper_preamble_atom_sym_index }, .offset = 4, .addend = 0, .pcrel = true, @@ -1604,7 +1603,7 @@ fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLo try Atom.addRelocation(self, atom_index, .{ .type = .unsigned, - .target = .{ .sym_index = stub_sym_index, .file = null }, + .target = .{ .sym_index = stub_sym_index }, .offset = 0, .addend = 0, .pcrel = false, @@ -1658,7 +1657,7 @@ fn createStubAtom(self: *MachO, laptr_sym_index: u32) !Atom.Index { try Atom.addRelocation(self, atom_index, .{ .type = .branch, - .target = .{ .sym_index = laptr_sym_index, .file = null }, + .target = .{ .sym_index = laptr_sym_index }, .offset = 2, .addend = 0, .pcrel = true, @@ -1680,7 +1679,7 @@ fn createStubAtom(self: *MachO, laptr_sym_index: u32) !Atom.Index { try Atom.addRelocations(self, atom_index, &[_]Relocation{ .{ .type = .page, - .target = .{ .sym_index = laptr_sym_index, .file = null }, + .target = .{ .sym_index = laptr_sym_index }, .offset = 0, .addend = 0, .pcrel = true, @@ -1688,7 +1687,7 @@ fn createStubAtom(self: *MachO, laptr_sym_index: u32) !Atom.Index { }, .{ .type = .pageoff, - .target = .{ .sym_index = laptr_sym_index, .file = null }, + .target = .{ .sym_index = laptr_sym_index }, .offset = 4, .addend = 0, .pcrel = false, @@ -1706,6 +1705,67 @@ fn createStubAtom(self: *MachO, laptr_sym_index: u32) !Atom.Index { return atom_index; } +fn createThreadLocalDescriptorAtom(self: *MachO, target: SymbolWithLoc) !Atom.Index { + const gpa = self.base.allocator; + const size = 3 * @sizeOf(u64); + const required_alignment: u32 = 1; + const atom_index = try self.createAtom(); + self.getAtomPtr(atom_index).size = size; + + const target_sym_name = self.getSymbolName(target); + const name_delimiter = mem.indexOf(u8, target_sym_name, "$").?; + const sym_name = try gpa.dupe(u8, target_sym_name[0..name_delimiter]); + defer gpa.free(sym_name); + + const sym = self.getAtom(atom_index).getSymbolPtr(self); + sym.n_type = macho.N_SECT; + sym.n_sect = self.thread_vars_section_index.? + 1; + sym.n_strx = try self.strtab.insert(gpa, sym_name); + sym.n_value = try self.allocateAtom(atom_index, size, required_alignment); + + log.debug("allocated threadlocal descriptor atom '{s}' at 0x{x}", .{ sym_name, sym.n_value }); + + try Atom.addRelocation(self, atom_index, .{ + .type = .tlv_initializer, + .target = target, + .offset = 0x10, + .addend = 0, + .pcrel = false, + .length = 3, + }); + + var code: [size]u8 = undefined; + mem.set(u8, &code, 0); + try self.writeAtom(atom_index, &code); + + return atom_index; +} + +fn createThreadLocalPointerAtom(self: *MachO, tlv_desc_sym_index: u32) !Atom.Index { + const atom_index = try self.createAtom(); + self.getAtomPtr(atom_index).size = @sizeOf(u64); + + const sym = self.getAtom(atom_index).getSymbolPtr(self); + sym.n_type = macho.N_SECT; + sym.n_sect = self.thread_ptr_section_index.? + 1; + sym.n_value = try self.allocateAtom(atom_index, @sizeOf(u64), @alignOf(u64)); + + log.debug("allocated threadlocal pointer atom at 0x{x}", .{sym.n_value}); + + try Atom.addRelocation(self, atom_index, .{ + .type = .unsigned, + .target = .{ .sym_index = tlv_desc_sym_index }, + .offset = 0, + .addend = 0, + .pcrel = false, + .length = 3, + }); + try Atom.addRebase(self, atom_index, 0); + try self.writePtrWidthAtom(atom_index); + + return atom_index; +} + fn createMhExecuteHeaderSymbol(self: *MachO) !void { if (self.base.options.output_mode != .Exe) return; if (self.getGlobal("__mh_execute_header")) |global| { @@ -2091,6 +2151,13 @@ fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void { fn addTlvEntry(self: *MachO, target: SymbolWithLoc) !void { if (self.tlvp_table.lookup.contains(target)) return; + const tlvp_index = try self.tlvp_table.allocateEntry(self.base.allocator, target); + const tlv_desc_atom_index = try self.createThreadLocalDescriptorAtom(target); + const tlv_desc_atom = self.getAtom(tlv_desc_atom_index); + const tlv_ptr_atom_index = try self.createThreadLocalPointerAtom(tlv_desc_atom.getSymbolIndex().?); + const tlv_ptr_atom = self.getAtom(tlv_ptr_atom_index); + self.tlvp_table.entries.items[tlvp_index].sym_index = tlv_ptr_atom.getSymbolIndex().?; + self.markRelocsDirtyByTarget(target); } pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { @@ -2444,16 +2511,28 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64 const required_alignment = decl.getAlignment(self.base.options.target); - const sym_name = try decl.getFullyQualifiedName(mod); - defer self.base.allocator.free(sym_name); + const decl_name = try decl.getFullyQualifiedName(mod); + defer gpa.free(decl_name); const decl_metadata = self.decls.get(decl_index).?; const atom_index = decl_metadata.atom; const atom = self.getAtom(atom_index); const sym_index = atom.getSymbolIndex().?; const sect_id = decl_metadata.section; + const header = &self.sections.items(.header)[sect_id]; + const segment = self.getSegment(sect_id); + const is_threadlocal = if (!self.base.options.single_threaded) + header.flags == macho.S_THREAD_LOCAL_REGULAR or header.flags == macho.S_THREAD_LOCAL_ZEROFILL + else + false; const code_len = code.len; + const sym_name = if (is_threadlocal) + try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{decl_name}) + else + decl_name; + defer if (is_threadlocal) gpa.free(sym_name); + if (atom.size != 0) { const sym = atom.getSymbolPtr(self); sym.n_strx = try self.strtab.insert(gpa, sym_name); @@ -2471,25 +2550,29 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64 if (vaddr != sym.n_value) { sym.n_value = vaddr; - log.debug(" (updating GOT entry)", .{}); - const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null }; - const got_atom_index = self.got_table.getAtomIndex(self, got_target).?; - self.markRelocsDirtyByTarget(got_target); - try self.writePtrWidthAtom(got_atom_index); + const target = SymbolWithLoc{ .sym_index = sym_index }; + self.markRelocsDirtyByTarget(target); + if (is_threadlocal) { + @panic("TODO update the threadlocal variable's name also"); + // log.debug(" (updating threadlocal pointer entry)", .{}); + // const tlvp_atom_index = self.tlvp_table.getAtomIndex(self, target).?; + // try self.writePtrWidthAtom(tlvp_atom_index); + } else { + log.debug(" (updating GOT entry)", .{}); + const got_atom_index = self.got_table.getAtomIndex(self, target).?; + try self.writePtrWidthAtom(got_atom_index); + } } } else if (code_len < atom.size) { self.shrinkAtom(atom_index, code_len); } else if (atom.next_index == null) { - const header = &self.sections.items(.header)[sect_id]; - const segment = self.getSegment(sect_id); const needed_size = (sym.n_value + code_len) - segment.vmaddr; header.size = needed_size; } self.getAtomPtr(atom_index).size = code_len; } else { - const name_str_index = try self.strtab.insert(gpa, sym_name); const sym = atom.getSymbolPtr(self); - sym.n_strx = name_str_index; + sym.n_strx = try self.strtab.insert(gpa, sym_name); sym.n_type = macho.N_SECT; sym.n_sect = sect_id + 1; sym.n_desc = 0; @@ -2503,7 +2586,12 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64 self.getAtomPtr(atom_index).size = code_len; sym.n_value = vaddr; - try self.addGotEntry(.{ .sym_index = sym_index }); + const target: SymbolWithLoc = .{ .sym_index = sym_index }; + if (is_threadlocal) { + try self.addTlvEntry(target); + } else { + try self.addGotEntry(target); + } } try self.writeAtom(atom_index, code); @@ -2851,7 +2939,7 @@ fn populateMissingMetadata(self: *MachO) !void { if (self.thread_vars_section_index == null) { self.thread_vars_section_index = try self.allocateSection("__DATA3", "__thread_vars", .{ .size = @sizeOf(u64) * 3, - .alignment = @alignOf(u64), + .alignment = @sizeOf(u64), .flags = macho.S_THREAD_LOCAL_VARIABLES, .prot = macho.PROT.READ | macho.PROT.WRITE, }); @@ -3650,6 +3738,10 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void { var header: macho.mach_header_64 = .{}; header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL; + if (!self.base.options.single_threaded) { + header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; + } + switch (self.base.options.target.cpu.arch) { .aarch64 => { header.cputype = macho.CPU_TYPE_ARM64; @@ -3674,12 +3766,6 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void { else => unreachable, } - if (self.getSectionByName("__DATA", "__thread_vars")) |sect_id| { - if (self.sections.items(.header)[sect_id].size > 0) { - header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; - } - } - header.ncmds = ncmds; header.sizeofcmds = sizeofcmds; @@ -3859,8 +3945,7 @@ pub fn getSymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) macho.nlist_64 /// Returns name of the symbol described by `sym_with_loc` descriptor. pub fn getSymbolName(self: *const MachO, sym_with_loc: SymbolWithLoc) []const u8 { - assert(sym_with_loc.file == null); - const sym = self.locals.items[sym_with_loc.sym_index]; + const sym = self.getSymbol(sym_with_loc); return self.strtab.get(sym.n_strx).?; } @@ -4274,6 +4359,9 @@ pub fn logSymtab(self: *MachO) void { log.debug("stubs entries:", .{}); log.debug("{}", .{self.stubs_table.fmtDebug(self)}); + + log.debug("threadlocal entries:", .{}); + log.debug("{}", .{self.tlvp_table.fmtDebug(self)}); } pub fn logAtoms(self: *MachO) void { diff --git a/src/link/MachO/Relocation.zig b/src/link/MachO/Relocation.zig index d77acd76f0..06a255ad59 100644 --- a/src/link/MachO/Relocation.zig +++ b/src/link/MachO/Relocation.zig @@ -37,6 +37,8 @@ pub const Type = enum { branch, /// Absolute pointer value unsigned, + /// Relative offset to TLV initializer + tlv_initializer, }; /// Returns true if and only if the reloc is dirty AND the target address is available. @@ -65,7 +67,16 @@ pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, cod const target_atom_index = self.getTargetAtomIndex(macho_file).?; // Oops, you didn't check if the relocation can be resolved with isResolvable(). const target_atom = macho_file.getAtom(target_atom_index); - const target_addr = @intCast(i64, target_atom.getSymbol(macho_file).n_value) + self.addend; + + const target_addr: i64 = switch (self.type) { + .tlv_initializer => blk: { + assert(self.addend == 0); // Addend here makes no sense. + const header = macho_file.sections.items(.header)[macho_file.thread_data_section_index.?]; + const target_sym = target_atom.getSymbol(macho_file); + break :blk @intCast(i64, target_sym.n_value - header.addr); + }, + else => @intCast(i64, target_atom.getSymbol(macho_file).n_value) + self.addend, + }; log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{ source_addr, @@ -190,7 +201,7 @@ fn resolveAarch64(self: Relocation, source_addr: u64, target_addr: i64, code: [] }; mem.writeIntLittle(u32, buffer[0..4], inst.toU32()); }, - .unsigned => switch (self.length) { + .tlv_initializer, .unsigned => switch (self.length) { 2 => mem.writeIntLittle(u32, buffer[0..4], @truncate(u32, @bitCast(u64, target_addr))), 3 => mem.writeIntLittle(u64, buffer[0..8], @bitCast(u64, target_addr)), else => unreachable, @@ -205,7 +216,7 @@ fn resolveX8664(self: Relocation, source_addr: u64, target_addr: i64, code: []u8 const displacement = @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4); mem.writeIntLittle(u32, code[self.offset..][0..4], @bitCast(u32, displacement)); }, - .unsigned => { + .tlv_initializer, .unsigned => { switch (self.length) { 2 => { mem.writeIntLittle(u32, code[self.offset..][0..4], @truncate(u32, @bitCast(u64, target_addr))); |
