authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-04-22 13:39:07+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-04-22 13:39:07+02:00
logc4a63389e4eefb78c1ee2028047447094bb864dc
tree95edea0e36efa81c2a6ea8d0574feeb5ba90f25e
parent68e4a5784791f733774e161b72a283b69a75b0de
parent14dfbbc21365131c7ac85f08f543058f43fca0c2
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #15371 from ziglang/better-elf

link: make GOT (and other synthetic sections) handling common across linkers

20 files changed, 985 insertions(+), 1046 deletions(-)

src/arch/aarch64/CodeGen.zig+1
...@@ -4290,6 +4290,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4290,6 +4290,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4290 if (self.bin_file.cast(link.File.Elf)) |elf_file| {4290 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4291 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);4291 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
4292 const atom = elf_file.getAtom(atom_index);4292 const atom = elf_file.getAtom(atom_index);
4293 _ = try atom.getOrCreateOffsetTableEntry(elf_file);
4293 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));4294 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
4294 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr });4295 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr });
4295 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {4296 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
src/arch/arm/CodeGen.zig+1
...@@ -4270,6 +4270,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4270,6 +4270,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4270 if (self.bin_file.cast(link.File.Elf)) |elf_file| {4270 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4271 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);4271 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
4272 const atom = elf_file.getAtom(atom_index);4272 const atom = elf_file.getAtom(atom_index);
4273 _ = try atom.getOrCreateOffsetTableEntry(elf_file);
4273 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));4274 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
4274 try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr });4275 try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr });
4275 } else if (self.bin_file.cast(link.File.MachO)) |_| {4276 } else if (self.bin_file.cast(link.File.MachO)) |_| {
src/arch/riscv64/CodeGen.zig+1
...@@ -1734,6 +1734,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -1734,6 +1734,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
1734 const func = func_payload.data;1734 const func = func_payload.data;
1735 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);1735 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
1736 const atom = elf_file.getAtom(atom_index);1736 const atom = elf_file.getAtom(atom_index);
1737 _ = try atom.getOrCreateOffsetTableEntry(elf_file);
1737 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));1738 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
1738 try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });1739 try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });
1739 _ = try self.addInst(.{1740 _ = try self.addInst(.{
src/arch/sparc64/CodeGen.zig+1
...@@ -1254,6 +1254,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -1254,6 +1254,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
1254 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {1254 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
1255 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);1255 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
1256 const atom = elf_file.getAtom(atom_index);1256 const atom = elf_file.getAtom(atom_index);
1257 _ = try atom.getOrCreateOffsetTableEntry(elf_file);
1257 break :blk @intCast(u32, atom.getOffsetTableAddress(elf_file));1258 break :blk @intCast(u32, atom.getOffsetTableAddress(elf_file));
1258 } else unreachable;1259 } else unreachable;
12591260
src/arch/x86_64/CodeGen.zig+10-4
...@@ -5624,7 +5624,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -5624,7 +5624,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
56245624
5625 if (self.bin_file.cast(link.File.Elf)) |elf_file| {5625 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
5626 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);5626 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
5627 const got_addr = elf_file.getAtom(atom_index).getOffsetTableAddress(elf_file);5627 const atom = elf_file.getAtom(atom_index);
5628 _ = try atom.getOrCreateOffsetTableEntry(elf_file);
5629 const got_addr = atom.getOffsetTableAddress(elf_file);
5628 try self.asmMemory(.call, Memory.sib(.qword, .{5630 try self.asmMemory(.call, Memory.sib(.qword, .{
5629 .base = .ds,5631 .base = .ds,
5630 .disp = @intCast(i32, got_addr),5632 .disp = @intCast(i32, got_addr),
...@@ -5853,7 +5855,9 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {...@@ -5853,7 +5855,9 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {
5853 .{ .kind = .const_data, .ty = Type.anyerror },5855 .{ .kind = .const_data, .ty = Type.anyerror },
5854 4, // dword alignment5856 4, // dword alignment
5855 );5857 );
5856 const got_addr = elf_file.getAtom(atom_index).getOffsetTableAddress(elf_file);5858 const atom = elf_file.getAtom(atom_index);
5859 _ = try atom.getOrCreateOffsetTableEntry(elf_file);
5860 const got_addr = atom.getOffsetTableAddress(elf_file);
5857 try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{5861 try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{
5858 .base = .ds,5862 .base = .ds,
5859 .disp = @intCast(i32, got_addr),5863 .disp = @intCast(i32, got_addr),
...@@ -7574,7 +7578,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -7574,7 +7578,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
7574 const atom_index = try self.getSymbolIndexForDecl(self.mod_fn.owner_decl);7578 const atom_index = try self.getSymbolIndexForDecl(self.mod_fn.owner_decl);
7575 if (self.bin_file.cast(link.File.MachO)) |_| {7579 if (self.bin_file.cast(link.File.MachO)) |_| {
7576 _ = try self.addInst(.{7580 _ = try self.addInst(.{
7577 .tag = .mov_linker,7581 .tag = .lea_linker,
7578 .ops = .tlv_reloc,7582 .ops = .tlv_reloc,
7579 .data = .{ .payload = try self.addExtra(Mir.LeaRegisterReloc{7583 .data = .{ .payload = try self.addExtra(Mir.LeaRegisterReloc{
7580 .reg = @enumToInt(Register.rdi),7584 .reg = @enumToInt(Register.rdi),
...@@ -8230,7 +8234,9 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {...@@ -8230,7 +8234,9 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void {
8230 .{ .kind = .const_data, .ty = Type.anyerror },8234 .{ .kind = .const_data, .ty = Type.anyerror },
8231 4, // dword alignment8235 4, // dword alignment
8232 );8236 );
8233 const got_addr = elf_file.getAtom(atom_index).getOffsetTableAddress(elf_file);8237 const atom = elf_file.getAtom(atom_index);
8238 _ = try atom.getOrCreateOffsetTableEntry(elf_file);
8239 const got_addr = atom.getOffsetTableAddress(elf_file);
8234 try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{8240 try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{
8235 .base = .ds,8241 .base = .ds,
8236 .disp = @intCast(i32, got_addr),8242 .disp = @intCast(i32, got_addr),
src/codegen.zig+1
...@@ -1006,6 +1006,7 @@ fn genDeclRef(...@@ -1006,6 +1006,7 @@ fn genDeclRef(
1006 if (bin_file.cast(link.File.Elf)) |elf_file| {1006 if (bin_file.cast(link.File.Elf)) |elf_file| {
1007 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);1007 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
1008 const atom = elf_file.getAtom(atom_index);1008 const atom = elf_file.getAtom(atom_index);
1009 _ = try atom.getOrCreateOffsetTableEntry(elf_file);
1009 return GenResult.mcv(.{ .memory = atom.getOffsetTableAddress(elf_file) });1010 return GenResult.mcv(.{ .memory = atom.getOffsetTableAddress(elf_file) });
1010 } else if (bin_file.cast(link.File.MachO)) |macho_file| {1011 } else if (bin_file.cast(link.File.MachO)) |macho_file| {
1011 const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);1012 const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);
src/link/Coff.zig+167-155
...@@ -37,13 +37,14 @@ strtab_offset: ?u32 = null,...@@ -37,13 +37,14 @@ strtab_offset: ?u32 = null,
3737
38temp_strtab: StringTable(.temp_strtab) = .{},38temp_strtab: StringTable(.temp_strtab) = .{},
3939
40got_entries: std.ArrayListUnmanaged(Entry) = .{},40got_table: TableSection(SymbolWithLoc) = .{},
41got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
42got_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
4341
44/// A table of ImportTables partitioned by the library name.42/// A table of ImportTables partitioned by the library name.
45/// Key is an offset into the interning string table `temp_strtab`.43/// Key is an offset into the interning string table `temp_strtab`.
46import_tables: std.AutoArrayHashMapUnmanaged(u32, ImportTable) = .{},44import_tables: std.AutoArrayHashMapUnmanaged(u32, ImportTable) = .{},
45
46got_table_count_dirty: bool = true,
47got_table_contents_dirty: bool = true,
47imports_count_dirty: bool = true,48imports_count_dirty: bool = true,
4849
49/// Virtual address of the entry point procedure relative to image base.50/// Virtual address of the entry point procedure relative to image base.
...@@ -106,12 +107,6 @@ const HotUpdateState = struct {...@@ -106,12 +107,6 @@ const HotUpdateState = struct {
106 loaded_base_address: ?std.os.windows.HMODULE = null,107 loaded_base_address: ?std.os.windows.HMODULE = null,
107};108};
108109
109const Entry = struct {
110 target: SymbolWithLoc,
111 // Index into the synthetic symbol table (i.e., file == null).
112 sym_index: u32,
113};
114
115const RelocTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation));110const RelocTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation));
116const BaseRelocationTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32));111const BaseRelocationTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32));
117const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index));112const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index));
...@@ -188,7 +183,8 @@ pub const PtrWidth = enum {...@@ -188,7 +183,8 @@ pub const PtrWidth = enum {
188 p32,183 p32,
189 p64,184 p64,
190185
191 fn abiSize(pw: PtrWidth) u4 {186 /// Size in bytes.
187 pub fn size(pw: PtrWidth) u4 {
192 return switch (pw) {188 return switch (pw) {
193 .p32 => 4,189 .p32 => 4,
194 .p64 => 8,190 .p64 => 8,
...@@ -310,9 +306,7 @@ pub fn deinit(self: *Coff) void {...@@ -310,9 +306,7 @@ pub fn deinit(self: *Coff) void {
310 self.globals_free_list.deinit(gpa);306 self.globals_free_list.deinit(gpa);
311 self.strtab.deinit(gpa);307 self.strtab.deinit(gpa);
312 self.temp_strtab.deinit(gpa);308 self.temp_strtab.deinit(gpa);
313 self.got_entries.deinit(gpa);309 self.got_table.deinit(gpa);
314 self.got_entries_free_list.deinit(gpa);
315 self.got_entries_table.deinit(gpa);
316310
317 for (self.import_tables.values()) |*itab| {311 for (self.import_tables.values()) |*itab| {
318 itab.deinit(gpa);312 itab.deinit(gpa);
...@@ -371,7 +365,7 @@ fn populateMissingMetadata(self: *Coff) !void {...@@ -371,7 +365,7 @@ fn populateMissingMetadata(self: *Coff) !void {
371 }365 }
372366
373 if (self.got_section_index == null) {367 if (self.got_section_index == null) {
374 const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize();368 const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.size();
375 self.got_section_index = try self.allocateSection(".got", file_size, .{369 self.got_section_index = try self.allocateSection(".got", file_size, .{
376 .CNT_INITIALIZED_DATA = 1,370 .CNT_INITIALIZED_DATA = 1,
377 .MEM_READ = 1,371 .MEM_READ = 1,
...@@ -396,7 +390,7 @@ fn populateMissingMetadata(self: *Coff) !void {...@@ -396,7 +390,7 @@ fn populateMissingMetadata(self: *Coff) !void {
396 }390 }
397391
398 if (self.idata_section_index == null) {392 if (self.idata_section_index == null) {
399 const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize();393 const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.size();
400 self.idata_section_index = try self.allocateSection(".idata", file_size, .{394 self.idata_section_index = try self.allocateSection(".idata", file_size, .{
401 .CNT_INITIALIZED_DATA = 1,395 .CNT_INITIALIZED_DATA = 1,
402 .MEM_READ = 1,396 .MEM_READ = 1,
...@@ -498,8 +492,8 @@ fn growSection(self: *Coff, sect_id: u32, needed_size: u32) !void {...@@ -498,8 +492,8 @@ fn growSection(self: *Coff, sect_id: u32, needed_size: u32) !void {
498492
499 const sect_vm_capacity = self.allocatedVirtualSize(header.virtual_address);493 const sect_vm_capacity = self.allocatedVirtualSize(header.virtual_address);
500 if (needed_size > sect_vm_capacity) {494 if (needed_size > sect_vm_capacity) {
501 try self.growSectionVirtualMemory(sect_id, needed_size);
502 self.markRelocsDirtyByAddress(header.virtual_address + needed_size);495 self.markRelocsDirtyByAddress(header.virtual_address + needed_size);
496 try self.growSectionVirtualMemory(sect_id, needed_size);
503 }497 }
504498
505 header.virtual_size = @max(header.virtual_size, needed_size);499 header.virtual_size = @max(header.virtual_size, needed_size);
...@@ -698,26 +692,12 @@ fn allocateGlobal(self: *Coff) !u32 {...@@ -698,26 +692,12 @@ fn allocateGlobal(self: *Coff) !u32 {
698 return index;692 return index;
699}693}
700694
701pub fn allocateGotEntry(self: *Coff, target: SymbolWithLoc) !u32 {695fn addGotEntry(self: *Coff, target: SymbolWithLoc) !void {
702 const gpa = self.base.allocator;696 if (self.got_table.lookup.contains(target)) return;
703 try self.got_entries.ensureUnusedCapacity(gpa, 1);697 const got_index = try self.got_table.allocateEntry(self.base.allocator, target);
704698 try self.writeOffsetTableEntry(got_index);
705 const index: u32 = blk: {699 self.got_table_count_dirty = true;
706 if (self.got_entries_free_list.popOrNull()) |index| {700 self.markRelocsDirtyByTarget(target);
707 log.debug(" (reusing GOT entry index {d})", .{index});
708 break :blk index;
709 } else {
710 log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.items.len});
711 const index = @intCast(u32, self.got_entries.items.len);
712 _ = self.got_entries.addOneAssumeCapacity();
713 break :blk index;
714 }
715 };
716
717 self.got_entries.items[index] = .{ .target = target, .sym_index = 0 };
718 try self.got_entries_table.putNoClobber(gpa, target, index);
719
720 return index;
721}701}
722702
723pub fn createAtom(self: *Coff) !Atom.Index {703pub fn createAtom(self: *Coff) !Atom.Index {
...@@ -737,37 +717,6 @@ pub fn createAtom(self: *Coff) !Atom.Index {...@@ -737,37 +717,6 @@ pub fn createAtom(self: *Coff) !Atom.Index {
737 return atom_index;717 return atom_index;
738}718}
739719
740fn createGotAtom(self: *Coff, target: SymbolWithLoc) !Atom.Index {
741 const atom_index = try self.createAtom();
742 const atom = self.getAtomPtr(atom_index);
743 atom.size = @sizeOf(u64);
744
745 const sym = atom.getSymbolPtr(self);
746 sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1);
747 sym.value = try self.allocateAtom(atom_index, atom.size, @sizeOf(u64));
748
749 log.debug("allocated GOT atom at 0x{x}", .{sym.value});
750
751 try Atom.addRelocation(self, atom_index, .{
752 .type = .direct,
753 .target = target,
754 .offset = 0,
755 .addend = 0,
756 .pcrel = false,
757 .length = 3,
758 });
759
760 const target_sym = self.getSymbol(target);
761 switch (target_sym.section_number) {
762 .UNDEFINED => @panic("TODO generate a binding for undefined GOT target"),
763 .ABSOLUTE => {},
764 .DEBUG => unreachable, // not possible
765 else => try Atom.addBaseRelocation(self, atom_index, 0),
766 }
767
768 return atom_index;
769}
770
771fn growAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignment: u32) !u32 {720fn growAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignment: u32) !u32 {
772 const atom = self.getAtom(atom_index);721 const atom = self.getAtom(atom_index);
773 const sym = atom.getSymbol(self);722 const sym = atom.getSymbol(self);
...@@ -873,17 +822,75 @@ fn writeMem(handle: std.ChildProcess.Id, pvaddr: std.os.windows.LPVOID, code: []...@@ -873,17 +822,75 @@ fn writeMem(handle: std.ChildProcess.Id, pvaddr: std.os.windows.LPVOID, code: []
873 if (amt != code.len) return error.InputOutput;822 if (amt != code.len) return error.InputOutput;
874}823}
875824
876fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void {825fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
826 const sect_id = self.got_section_index.?;
827
828 if (self.got_table_count_dirty) {
829 const needed_size = @intCast(u32, self.got_table.entries.items.len * self.ptr_width.size());
830 try self.growSection(sect_id, needed_size);
831 self.got_table_count_dirty = false;
832 }
833
834 const header = &self.sections.items(.header)[sect_id];
835 const entry = self.got_table.entries.items[index];
836 const entry_value = self.getSymbol(entry).value;
837 const entry_offset = index * self.ptr_width.size();
838 const file_offset = header.pointer_to_raw_data + entry_offset;
839 const vmaddr = header.virtual_address + entry_offset;
840
841 log.debug("writing GOT entry {d}: @{x} => {x}", .{ index, vmaddr, entry_value + self.getImageBase() });
842
877 switch (self.ptr_width) {843 switch (self.ptr_width) {
878 .p32 => {844 .p32 => {
879 var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32);845 var buf: [4]u8 = undefined;
880 try self.writeAtom(atom_index, &buffer);846 mem.writeIntLittle(u32, &buf, @intCast(u32, entry_value + self.getImageBase()));
847 try self.base.file.?.pwriteAll(&buf, file_offset);
881 },848 },
882 .p64 => {849 .p64 => {
883 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);850 var buf: [8]u8 = undefined;
884 try self.writeAtom(atom_index, &buffer);851 mem.writeIntLittle(u64, &buf, entry_value + self.getImageBase());
852 try self.base.file.?.pwriteAll(&buf, file_offset);
885 },853 },
886 }854 }
855
856 if (is_hot_update_compatible) {
857 if (self.base.child_pid) |handle| {
858 const gpa = self.base.allocator;
859 const slide = @ptrToInt(self.hot_state.loaded_base_address.?);
860 const actual_vmaddr = vmaddr + slide;
861 const pvaddr = @intToPtr(*anyopaque, actual_vmaddr);
862 log.debug("writing GOT entry to memory at address {x}", .{actual_vmaddr});
863 if (build_options.enable_logging) {
864 switch (self.ptr_width) {
865 .p32 => {
866 var buf: [4]u8 = undefined;
867 try debugMem(gpa, handle, pvaddr, &buf);
868 },
869 .p64 => {
870 var buf: [8]u8 = undefined;
871 try debugMem(gpa, handle, pvaddr, &buf);
872 },
873 }
874 }
875
876 switch (self.ptr_width) {
877 .p32 => {
878 var buf: [4]u8 = undefined;
879 mem.writeIntLittle(u32, &buf, @intCast(u32, entry_value + slide));
880 writeMem(handle, pvaddr, &buf) catch |err| {
881 log.warn("writing to protected memory failed with error: {s}", .{@errorName(err)});
882 };
883 },
884 .p64 => {
885 var buf: [8]u8 = undefined;
886 mem.writeIntLittle(u64, &buf, entry_value + slide);
887 writeMem(handle, pvaddr, &buf) catch |err| {
888 log.warn("writing to protected memory failed with error: {s}", .{@errorName(err)});
889 };
890 },
891 }
892 }
893 }
887}894}
888895
889fn markRelocsDirtyByTarget(self: *Coff, target: SymbolWithLoc) void {896fn markRelocsDirtyByTarget(self: *Coff, target: SymbolWithLoc) void {
...@@ -904,6 +911,15 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {...@@ -904,6 +911,15 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {
904 reloc.dirty = true;911 reloc.dirty = true;
905 }912 }
906 }913 }
914
915 // TODO: dirty only really affected GOT cells
916 for (self.got_table.entries.items) |entry| {
917 const target_addr = self.getSymbol(entry).value;
918 if (target_addr >= addr) {
919 self.got_table_contents_dirty = true;
920 break;
921 }
922 }
907}923}
908924
909fn resolveRelocs(self: *Coff, atom_index: Atom.Index, relocs: []*const Relocation, code: []u8, image_base: u64) void {925fn resolveRelocs(self: *Coff, atom_index: Atom.Index, relocs: []*const Relocation, code: []u8, image_base: u64) void {
...@@ -994,17 +1010,7 @@ fn freeAtom(self: *Coff, atom_index: Atom.Index) void {...@@ -994,17 +1010,7 @@ fn freeAtom(self: *Coff, atom_index: Atom.Index) void {
994 self.locals_free_list.append(gpa, sym_index) catch {};1010 self.locals_free_list.append(gpa, sym_index) catch {};
9951011
996 // Try freeing GOT atom if this decl had one1012 // Try freeing GOT atom if this decl had one
997 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };1013 self.got_table.freeEntry(gpa, .{ .sym_index = sym_index });
998 if (self.got_entries_table.get(got_target)) |got_index| {
999 self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {};
1000 self.got_entries.items[got_index] = .{
1001 .target = .{ .sym_index = 0, .file = null },
1002 .sym_index = 0,
1003 };
1004 _ = self.got_entries_table.remove(got_target);
1005
1006 log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
1007 }
10081014
1009 self.locals.items[sym_index].section_number = .UNDEFINED;1015 self.locals.items[sym_index].section_number = .UNDEFINED;
1010 _ = self.atom_by_index_table.remove(sym_index);1016 _ = self.atom_by_index_table.remove(sym_index);
...@@ -1243,14 +1249,7 @@ fn updateLazySymbolAtom(...@@ -1243,14 +1249,7 @@ fn updateLazySymbolAtom(
1243 atom.size = code_len;1249 atom.size = code_len;
1244 symbol.value = vaddr;1250 symbol.value = vaddr;
12451251
1246 const got_target = SymbolWithLoc{ .sym_index = local_sym_index, .file = null };1252 try self.addGotEntry(.{ .sym_index = local_sym_index });
1247 const got_index = try self.allocateGotEntry(got_target);
1248 const got_atom_index = try self.createGotAtom(got_target);
1249 const got_atom = self.getAtom(got_atom_index);
1250 self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
1251 try self.writePtrWidthAtom(got_atom_index);
1252
1253 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());
1254 try self.writeAtom(atom_index, code);1253 try self.writeAtom(atom_index, code);
1255}1254}
12561255
...@@ -1321,6 +1320,7 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple...@@ -1321,6 +1320,7 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple
1321 const decl_metadata = self.decls.get(decl_index).?;1320 const decl_metadata = self.decls.get(decl_index).?;
1322 const atom_index = decl_metadata.atom;1321 const atom_index = decl_metadata.atom;
1323 const atom = self.getAtom(atom_index);1322 const atom = self.getAtom(atom_index);
1323 const sym_index = atom.getSymbolIndex().?;
1324 const sect_index = decl_metadata.section;1324 const sect_index = decl_metadata.section;
1325 const code_len = @intCast(u32, code.len);1325 const code_len = @intCast(u32, code.len);
13261326
...@@ -1340,10 +1340,9 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple...@@ -1340,10 +1340,9 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple
1340 if (vaddr != sym.value) {1340 if (vaddr != sym.value) {
1341 sym.value = vaddr;1341 sym.value = vaddr;
1342 log.debug(" (updating GOT entry)", .{});1342 log.debug(" (updating GOT entry)", .{});
1343 const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null };1343 const got_entry_index = self.got_table.lookup.get(.{ .sym_index = sym_index }).?;
1344 const got_atom_index = self.getGotAtomIndexForSymbol(got_target).?;1344 try self.writeOffsetTableEntry(got_entry_index);
1345 self.markRelocsDirtyByTarget(got_target);1345 self.markRelocsDirtyByTarget(.{ .sym_index = sym_index });
1346 try self.writePtrWidthAtom(got_atom_index);
1347 }1346 }
1348 } else if (code_len < atom.size) {1347 } else if (code_len < atom.size) {
1349 self.shrinkAtom(atom_index, code_len);1348 self.shrinkAtom(atom_index, code_len);
...@@ -1361,15 +1360,9 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple...@@ -1361,15 +1360,9 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple
1361 self.getAtomPtr(atom_index).size = code_len;1360 self.getAtomPtr(atom_index).size = code_len;
1362 sym.value = vaddr;1361 sym.value = vaddr;
13631362
1364 const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null };1363 try self.addGotEntry(.{ .sym_index = sym_index });
1365 const got_index = try self.allocateGotEntry(got_target);
1366 const got_atom_index = try self.createGotAtom(got_target);
1367 const got_atom = self.getAtom(got_atom_index);
1368 self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
1369 try self.writePtrWidthAtom(got_atom_index);
1370 }1364 }
13711365
1372 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());
1373 try self.writeAtom(atom_index, code);1366 try self.writeAtom(atom_index, code);
1374}1367}
13751368
...@@ -1651,6 +1644,16 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -1651,6 +1644,16 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
1651 try self.writeAtom(atom_index, code.items);1644 try self.writeAtom(atom_index, code.items);
1652 }1645 }
16531646
1647 // Update GOT if it got moved in memory.
1648 if (self.got_table_contents_dirty) {
1649 for (self.got_table.entries.items, 0..) |entry, i| {
1650 if (!self.got_table.lookup.contains(entry)) continue;
1651 // TODO: write all in one go rather than incrementally.
1652 try self.writeOffsetTableEntry(i);
1653 }
1654 self.got_table_contents_dirty = false;
1655 }
1656
1654 try self.writeBaseRelocations();1657 try self.writeBaseRelocations();
16551658
1656 if (self.getEntryPoint()) |entry_sym_loc| {1659 if (self.getEntryPoint()) |entry_sym_loc| {
...@@ -1739,48 +1742,82 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl_index: Module.Dec...@@ -1739,48 +1742,82 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl_index: Module.Dec
1739fn writeBaseRelocations(self: *Coff) !void {1742fn writeBaseRelocations(self: *Coff) !void {
1740 const gpa = self.base.allocator;1743 const gpa = self.base.allocator;
17411744
1742 var pages = std.AutoHashMap(u32, std.ArrayList(coff.BaseRelocation)).init(gpa);1745 var page_table = std.AutoHashMap(u32, std.ArrayList(coff.BaseRelocation)).init(gpa);
1743 defer {1746 defer {
1744 var it = pages.valueIterator();1747 var it = page_table.valueIterator();
1745 while (it.next()) |inner| {1748 while (it.next()) |inner| {
1746 inner.deinit();1749 inner.deinit();
1747 }1750 }
1748 pages.deinit();1751 page_table.deinit();
1749 }1752 }
17501753
1751 var it = self.base_relocs.iterator();1754 {
1752 while (it.next()) |entry| {1755 var it = self.base_relocs.iterator();
1753 const atom_index = entry.key_ptr.*;1756 while (it.next()) |entry| {
1754 const atom = self.getAtom(atom_index);1757 const atom_index = entry.key_ptr.*;
1755 const offsets = entry.value_ptr.*;1758 const atom = self.getAtom(atom_index);
1756
1757 for (offsets.items) |offset| {
1758 const sym = atom.getSymbol(self);1759 const sym = atom.getSymbol(self);
1759 const rva = sym.value + offset;1760 const offsets = entry.value_ptr.*;
1760 const page = mem.alignBackwardGeneric(u32, rva, self.page_size);1761
1761 const gop = try pages.getOrPut(page);1762 for (offsets.items) |offset| {
1762 if (!gop.found_existing) {1763 const rva = sym.value + offset;
1763 gop.value_ptr.* = std.ArrayList(coff.BaseRelocation).init(gpa);1764 const page = mem.alignBackwardGeneric(u32, rva, self.page_size);
1765 const gop = try page_table.getOrPut(page);
1766 if (!gop.found_existing) {
1767 gop.value_ptr.* = std.ArrayList(coff.BaseRelocation).init(gpa);
1768 }
1769 try gop.value_ptr.append(.{
1770 .offset = @intCast(u12, rva - page),
1771 .type = .DIR64,
1772 });
1773 }
1774 }
1775
1776 {
1777 const header = &self.sections.items(.header)[self.got_section_index.?];
1778 for (self.got_table.entries.items, 0..) |entry, index| {
1779 if (!self.got_table.lookup.contains(entry)) continue;
1780
1781 const sym = self.getSymbol(entry);
1782 if (sym.section_number == .UNDEFINED) continue;
1783
1784 const rva = @intCast(u32, header.virtual_address + index * self.ptr_width.size());
1785 const page = mem.alignBackwardGeneric(u32, rva, self.page_size);
1786 const gop = try page_table.getOrPut(page);
1787 if (!gop.found_existing) {
1788 gop.value_ptr.* = std.ArrayList(coff.BaseRelocation).init(gpa);
1789 }
1790 try gop.value_ptr.append(.{
1791 .offset = @intCast(u12, rva - page),
1792 .type = .DIR64,
1793 });
1764 }1794 }
1765 try gop.value_ptr.append(.{
1766 .offset = @intCast(u12, rva - page),
1767 .type = .DIR64,
1768 });
1769 }1795 }
1770 }1796 }
17711797
1798 // Sort pages by address.
1799 var pages = try std.ArrayList(u32).initCapacity(gpa, page_table.count());
1800 defer pages.deinit();
1801 {
1802 var it = page_table.keyIterator();
1803 while (it.next()) |page| {
1804 pages.appendAssumeCapacity(page.*);
1805 }
1806 }
1807 std.sort.sort(u32, pages.items, {}, std.sort.asc(u32));
1808
1772 var buffer = std.ArrayList(u8).init(gpa);1809 var buffer = std.ArrayList(u8).init(gpa);
1773 defer buffer.deinit();1810 defer buffer.deinit();
17741811
1775 var pages_it = pages.iterator();1812 for (pages.items) |page| {
1776 while (pages_it.next()) |entry| {1813 const entries = page_table.getPtr(page).?;
1777 // Pad to required 4byte alignment1814 // Pad to required 4byte alignment
1778 if (!mem.isAlignedGeneric(1815 if (!mem.isAlignedGeneric(
1779 usize,1816 usize,
1780 entry.value_ptr.items.len * @sizeOf(coff.BaseRelocation),1817 entries.items.len * @sizeOf(coff.BaseRelocation),
1781 @sizeOf(u32),1818 @sizeOf(u32),
1782 )) {1819 )) {
1783 try entry.value_ptr.append(.{1820 try entries.append(.{
1784 .offset = 0,1821 .offset = 0,
1785 .type = .ABSOLUTE,1822 .type = .ABSOLUTE,
1786 });1823 });
...@@ -1788,14 +1825,14 @@ fn writeBaseRelocations(self: *Coff) !void {...@@ -1788,14 +1825,14 @@ fn writeBaseRelocations(self: *Coff) !void {
17881825
1789 const block_size = @intCast(1826 const block_size = @intCast(
1790 u32,1827 u32,
1791 entry.value_ptr.items.len * @sizeOf(coff.BaseRelocation) + @sizeOf(coff.BaseRelocationDirectoryEntry),1828 entries.items.len * @sizeOf(coff.BaseRelocation) + @sizeOf(coff.BaseRelocationDirectoryEntry),
1792 );1829 );
1793 try buffer.ensureUnusedCapacity(block_size);1830 try buffer.ensureUnusedCapacity(block_size);
1794 buffer.appendSliceAssumeCapacity(mem.asBytes(&coff.BaseRelocationDirectoryEntry{1831 buffer.appendSliceAssumeCapacity(mem.asBytes(&coff.BaseRelocationDirectoryEntry{
1795 .page_rva = entry.key_ptr.*,1832 .page_rva = page,
1796 .block_size = block_size,1833 .block_size = block_size,
1797 }));1834 }));
1798 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(entry.value_ptr.items));1835 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(entries.items));
1799 }1836 }
18001837
1801 const header = &self.sections.items(.header)[self.reloc_section_index.?];1838 const header = &self.sections.items(.header)[self.reloc_section_index.?];
...@@ -2315,14 +2352,6 @@ pub fn getAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.In...@@ -2315,14 +2352,6 @@ pub fn getAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.In
2315 return self.atom_by_index_table.get(sym_loc.sym_index);2352 return self.atom_by_index_table.get(sym_loc.sym_index);
2316}2353}
23172354
2318/// Returns GOT atom that references `sym_loc` if one exists.
2319/// Returns null otherwise.
2320pub fn getGotAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index {
2321 const got_index = self.got_entries_table.get(sym_loc) orelse return null;
2322 const got_entry = self.got_entries.items[got_index];
2323 return self.getAtomIndexForSymbol(.{ .sym_index = got_entry.sym_index, .file = null });
2324}
2325
2326fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void {2355fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void {
2327 if (name.len <= 8) {2356 if (name.len <= 8) {
2328 mem.copy(u8, &header.name, name);2357 mem.copy(u8, &header.name, name);
...@@ -2410,25 +2439,7 @@ fn logSymtab(self: *Coff) void {...@@ -2410,25 +2439,7 @@ fn logSymtab(self: *Coff) void {
2410 }2439 }
24112440
2412 log.debug("GOT entries:", .{});2441 log.debug("GOT entries:", .{});
2413 for (self.got_entries.items, 0..) |entry, i| {2442 log.debug("{}", .{self.got_table});
2414 const got_sym = self.getSymbol(.{ .sym_index = entry.sym_index, .file = null });
2415 const target_sym = self.getSymbol(entry.target);
2416 if (target_sym.section_number == .UNDEFINED) {
2417 log.debug(" {d}@{x} => import('{s}')", .{
2418 i,
2419 got_sym.value,
2420 self.getSymbolName(entry.target),
2421 });
2422 } else {
2423 log.debug(" {d}@{x} => local(%{d}) in object({?d}) {s}", .{
2424 i,
2425 got_sym.value,
2426 entry.target.sym_index,
2427 entry.target.file,
2428 logSymAttributes(target_sym, &buf),
2429 });
2430 }
2431 }
2432}2443}
24332444
2434fn logSections(self: *Coff) void {2445fn logSections(self: *Coff) void {
...@@ -2484,6 +2495,7 @@ const LlvmObject = @import("../codegen/llvm.zig").Object;...@@ -2484,6 +2495,7 @@ const LlvmObject = @import("../codegen/llvm.zig").Object;
2484const Module = @import("../Module.zig");2495const Module = @import("../Module.zig");
2485const Object = @import("Coff/Object.zig");2496const Object = @import("Coff/Object.zig");
2486const Relocation = @import("Coff/Relocation.zig");2497const Relocation = @import("Coff/Relocation.zig");
2498const TableSection = @import("table_section.zig").TableSection;
2487const StringTable = @import("strtab.zig").StringTable;2499const StringTable = @import("strtab.zig").StringTable;
2488const Type = @import("../type.zig").Type;2500const Type = @import("../type.zig").Type;
2489const TypedValue = @import("../TypedValue.zig");2501const TypedValue = @import("../TypedValue.zig");
src/link/Coff/Relocation.zig+9-9
...@@ -48,17 +48,16 @@ dirty: bool = true,...@@ -48,17 +48,16 @@ dirty: bool = true,
48/// Returns address of the target if any.48/// Returns address of the target if any.
49pub fn getTargetAddress(self: Relocation, coff_file: *const Coff) ?u32 {49pub fn getTargetAddress(self: Relocation, coff_file: *const Coff) ?u32 {
50 switch (self.type) {50 switch (self.type) {
51 .got, .got_page, .got_pageoff, .direct, .page, .pageoff => {51 .got, .got_page, .got_pageoff => {
52 const maybe_target_atom_index = switch (self.type) {52 const got_index = coff_file.got_table.lookup.get(self.target) orelse return null;
53 .got, .got_page, .got_pageoff => coff_file.getGotAtomIndexForSymbol(self.target),53 const header = coff_file.sections.items(.header)[coff_file.got_section_index.?];
54 .direct, .page, .pageoff => coff_file.getAtomIndexForSymbol(self.target),54 return header.virtual_address + got_index * coff_file.ptr_width.size();
55 else => unreachable,55 },
56 };56 .direct, .page, .pageoff => {
57 const target_atom_index = maybe_target_atom_index orelse return null;57 const target_atom_index = coff_file.getAtomIndexForSymbol(self.target) orelse return null;
58 const target_atom = coff_file.getAtom(target_atom_index);58 const target_atom = coff_file.getAtom(target_atom_index);
59 return target_atom.getSymbol(coff_file).value;59 return target_atom.getSymbol(coff_file).value;
60 },60 },
61
62 .import, .import_page, .import_pageoff => {61 .import, .import_page, .import_pageoff => {
63 const sym = coff_file.getSymbol(self.target);62 const sym = coff_file.getSymbol(self.target);
64 const index = coff_file.import_tables.getIndex(sym.value) orelse return null;63 const index = coff_file.import_tables.getIndex(sym.value) orelse return null;
...@@ -74,7 +73,8 @@ pub fn getTargetAddress(self: Relocation, coff_file: *const Coff) ?u32 {...@@ -74,7 +73,8 @@ pub fn getTargetAddress(self: Relocation, coff_file: *const Coff) ?u32 {
7473
75/// Returns true if and only if the reloc is dirty AND the target address is available.74/// Returns true if and only if the reloc is dirty AND the target address is available.
76pub fn isResolvable(self: Relocation, coff_file: *Coff) bool {75pub fn isResolvable(self: Relocation, coff_file: *Coff) bool {
77 _ = self.getTargetAddress(coff_file) orelse return false;76 const addr = self.getTargetAddress(coff_file) orelse return false;
77 if (addr == 0) return false;
78 return self.dirty;78 return self.dirty;
79}79}
8080
src/link/Elf.zig+25-46
...@@ -30,6 +30,7 @@ const LlvmObject = @import("../codegen/llvm.zig").Object;...@@ -30,6 +30,7 @@ const LlvmObject = @import("../codegen/llvm.zig").Object;
30const Module = @import("../Module.zig");30const Module = @import("../Module.zig");
31const Package = @import("../Package.zig");31const Package = @import("../Package.zig");
32const StringTable = @import("strtab.zig").StringTable;32const StringTable = @import("strtab.zig").StringTable;
33const TableSection = @import("table_section.zig").TableSection;
33const Type = @import("../type.zig").Type;34const Type = @import("../type.zig").Type;
34const TypedValue = @import("../TypedValue.zig");35const TypedValue = @import("../TypedValue.zig");
35const Value = @import("../value.zig").Value;36const Value = @import("../value.zig").Value;
...@@ -148,17 +149,13 @@ global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},...@@ -148,17 +149,13 @@ global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
148149
149local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},150local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
150global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},151global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
151offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
152152
153/// Same order as in the file. The value is the absolute vaddr value.153got_table: TableSection(u32) = .{},
154/// If the vaddr of the executable program header changes, the entire
155/// offset table needs to be rewritten.
156offset_table: std.ArrayListUnmanaged(u64) = .{},
157154
158phdr_table_dirty: bool = false,155phdr_table_dirty: bool = false,
159shdr_table_dirty: bool = false,156shdr_table_dirty: bool = false,
160shstrtab_dirty: bool = false,157shstrtab_dirty: bool = false,
161offset_table_count_dirty: bool = false,158got_table_count_dirty: bool = false,
162159
163debug_strtab_dirty: bool = false,160debug_strtab_dirty: bool = false,
164debug_abbrev_section_dirty: bool = false,161debug_abbrev_section_dirty: bool = false,
...@@ -329,8 +326,7 @@ pub fn deinit(self: *Elf) void {...@@ -329,8 +326,7 @@ pub fn deinit(self: *Elf) void {
329 self.global_symbols.deinit(gpa);326 self.global_symbols.deinit(gpa);
330 self.global_symbol_free_list.deinit(gpa);327 self.global_symbol_free_list.deinit(gpa);
331 self.local_symbol_free_list.deinit(gpa);328 self.local_symbol_free_list.deinit(gpa);
332 self.offset_table_free_list.deinit(gpa);329 self.got_table.deinit(gpa);
333 self.offset_table.deinit(gpa);
334330
335 {331 {
336 var it = self.decls.iterator();332 var it = self.decls.iterator();
...@@ -1289,6 +1285,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1289,6 +1285,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1289 assert(!self.shdr_table_dirty);1285 assert(!self.shdr_table_dirty);
1290 assert(!self.shstrtab_dirty);1286 assert(!self.shstrtab_dirty);
1291 assert(!self.debug_strtab_dirty);1287 assert(!self.debug_strtab_dirty);
1288 assert(!self.got_table_count_dirty);
1292}1289}
12931290
1294fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void {1291fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void {
...@@ -2168,7 +2165,7 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void {...@@ -2168,7 +2165,7 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void {
2168 _ = self.atom_by_index_table.remove(local_sym_index);2165 _ = self.atom_by_index_table.remove(local_sym_index);
2169 self.getAtomPtr(atom_index).local_sym_index = 0;2166 self.getAtomPtr(atom_index).local_sym_index = 0;
21702167
2171 self.offset_table_free_list.append(self.base.allocator, atom.offset_table_index) catch {};2168 self.got_table.freeEntry(gpa, local_sym_index);
2172}2169}
21732170
2174fn shrinkAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64) void {2171fn shrinkAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64) void {
...@@ -2191,11 +2188,9 @@ pub fn createAtom(self: *Elf) !Atom.Index {...@@ -2191,11 +2188,9 @@ pub fn createAtom(self: *Elf) !Atom.Index {
2191 const atom_index = @intCast(Atom.Index, self.atoms.items.len);2188 const atom_index = @intCast(Atom.Index, self.atoms.items.len);
2192 const atom = try self.atoms.addOne(gpa);2189 const atom = try self.atoms.addOne(gpa);
2193 const local_sym_index = try self.allocateLocalSymbol();2190 const local_sym_index = try self.allocateLocalSymbol();
2194 const offset_table_index = try self.allocateGotOffset();
2195 try self.atom_by_index_table.putNoClobber(gpa, local_sym_index, atom_index);2191 try self.atom_by_index_table.putNoClobber(gpa, local_sym_index, atom_index);
2196 atom.* = .{2192 atom.* = .{
2197 .local_sym_index = local_sym_index,2193 .local_sym_index = local_sym_index,
2198 .offset_table_index = offset_table_index,
2199 .prev_index = null,2194 .prev_index = null,
2200 .next_index = null,2195 .next_index = null,
2201 };2196 };
...@@ -2352,26 +2347,6 @@ pub fn allocateLocalSymbol(self: *Elf) !u32 {...@@ -2352,26 +2347,6 @@ pub fn allocateLocalSymbol(self: *Elf) !u32 {
2352 return index;2347 return index;
2353}2348}
23542349
2355pub fn allocateGotOffset(self: *Elf) !u32 {
2356 try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1);
2357
2358 const index = blk: {
2359 if (self.offset_table_free_list.popOrNull()) |index| {
2360 log.debug(" (reusing GOT offset at index {d})", .{index});
2361 break :blk index;
2362 } else {
2363 log.debug(" (allocating GOT offset at index {d})", .{self.offset_table.items.len});
2364 const index = @intCast(u32, self.offset_table.items.len);
2365 _ = self.offset_table.addOneAssumeCapacity();
2366 self.offset_table_count_dirty = true;
2367 break :blk index;
2368 }
2369 };
2370
2371 self.offset_table.items[index] = 0;
2372 return index;
2373}
2374
2375fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void {2350fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void {
2376 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;2351 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
2377 for (unnamed_consts.items) |atom| {2352 for (unnamed_consts.items) |atom| {
...@@ -2465,6 +2440,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s...@@ -2465,6 +2440,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
2465 const decl_metadata = self.decls.get(decl_index).?;2440 const decl_metadata = self.decls.get(decl_index).?;
2466 const atom_index = decl_metadata.atom;2441 const atom_index = decl_metadata.atom;
2467 const atom = self.getAtom(atom_index);2442 const atom = self.getAtom(atom_index);
2443 const local_sym_index = atom.getSymbolIndex().?;
24682444
2469 const shdr_index = decl_metadata.shdr;2445 const shdr_index = decl_metadata.shdr;
2470 if (atom.getSymbol(self).st_size != 0 and self.base.child_pid == null) {2446 if (atom.getSymbol(self).st_size != 0 and self.base.child_pid == null) {
...@@ -2485,8 +2461,9 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s...@@ -2485,8 +2461,9 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
2485 local_sym.st_value = vaddr;2461 local_sym.st_value = vaddr;
24862462
2487 log.debug(" (writing new offset table entry)", .{});2463 log.debug(" (writing new offset table entry)", .{});
2488 self.offset_table.items[atom.offset_table_index] = vaddr;2464 const got_entry_index = self.got_table.lookup.get(local_sym_index).?;
2489 try self.writeOffsetTableEntry(atom.offset_table_index);2465 self.got_table.entries.items[got_entry_index] = local_sym_index;
2466 try self.writeOffsetTableEntry(got_entry_index);
2490 }2467 }
2491 } else if (code.len < local_sym.st_size) {2468 } else if (code.len < local_sym.st_size) {
2492 self.shrinkAtom(atom_index, code.len);2469 self.shrinkAtom(atom_index, code.len);
...@@ -2494,7 +2471,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s...@@ -2494,7 +2471,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
2494 local_sym.st_size = code.len;2471 local_sym.st_size = code.len;
24952472
2496 // TODO this write could be avoided if no fields of the symbol were changed.2473 // TODO this write could be avoided if no fields of the symbol were changed.
2497 try self.writeSymbol(atom.getSymbolIndex().?);2474 try self.writeSymbol(local_sym_index);
2498 } else {2475 } else {
2499 const local_sym = atom.getSymbolPtr(self);2476 const local_sym = atom.getSymbolPtr(self);
2500 local_sym.* = .{2477 local_sym.* = .{
...@@ -2509,12 +2486,12 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s...@@ -2509,12 +2486,12 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
2509 errdefer self.freeAtom(atom_index);2486 errdefer self.freeAtom(atom_index);
2510 log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr });2487 log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr });
25112488
2512 self.offset_table.items[atom.offset_table_index] = vaddr;
2513 local_sym.st_value = vaddr;2489 local_sym.st_value = vaddr;
2514 local_sym.st_size = code.len;2490 local_sym.st_size = code.len;
25152491
2516 try self.writeSymbol(atom.getSymbolIndex().?);2492 try self.writeSymbol(local_sym_index);
2517 try self.writeOffsetTableEntry(atom.offset_table_index);2493 const got_entry_index = try atom.getOrCreateOffsetTableEntry(self);
2494 try self.writeOffsetTableEntry(got_entry_index);
2518 }2495 }
25192496
2520 const local_sym = atom.getSymbolPtr(self);2497 const local_sym = atom.getSymbolPtr(self);
...@@ -2755,12 +2732,12 @@ fn updateLazySymbolAtom(...@@ -2755,12 +2732,12 @@ fn updateLazySymbolAtom(
2755 errdefer self.freeAtom(atom_index);2732 errdefer self.freeAtom(atom_index);
2756 log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr });2733 log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr });
27572734
2758 self.offset_table.items[atom.offset_table_index] = vaddr;
2759 local_sym.st_value = vaddr;2735 local_sym.st_value = vaddr;
2760 local_sym.st_size = code.len;2736 local_sym.st_size = code.len;
27612737
2762 try self.writeSymbol(local_sym_index);2738 try self.writeSymbol(local_sym_index);
2763 try self.writeOffsetTableEntry(atom.offset_table_index);2739 const got_entry_index = try atom.getOrCreateOffsetTableEntry(self);
2740 try self.writeOffsetTableEntry(got_entry_index);
27642741
2765 const section_offset = vaddr - self.program_headers.items[phdr_index].p_vaddr;2742 const section_offset = vaddr - self.program_headers.items[phdr_index].p_vaddr;
2766 const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset;2743 const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset;
...@@ -2989,32 +2966,34 @@ fn writeSectHeader(self: *Elf, index: usize) !void {...@@ -2989,32 +2966,34 @@ fn writeSectHeader(self: *Elf, index: usize) !void {
2989 }2966 }
2990}2967}
29912968
2992fn writeOffsetTableEntry(self: *Elf, index: usize) !void {2969fn writeOffsetTableEntry(self: *Elf, index: @TypeOf(self.got_table).Index) !void {
2993 const entry_size: u16 = self.archPtrWidthBytes();2970 const entry_size: u16 = self.archPtrWidthBytes();
2994 if (self.offset_table_count_dirty) {2971 if (self.got_table_count_dirty) {
2995 const needed_size = self.offset_table.items.len * entry_size;2972 const needed_size = self.got_table.entries.items.len * entry_size;
2996 try self.growAllocSection(self.got_section_index.?, needed_size);2973 try self.growAllocSection(self.got_section_index.?, needed_size);
2997 self.offset_table_count_dirty = false;2974 self.got_table_count_dirty = false;
2998 }2975 }
2999 const endian = self.base.options.target.cpu.arch.endian();2976 const endian = self.base.options.target.cpu.arch.endian();
3000 const shdr = &self.sections.items(.shdr)[self.got_section_index.?];2977 const shdr = &self.sections.items(.shdr)[self.got_section_index.?];
3001 const off = shdr.sh_offset + @as(u64, entry_size) * index;2978 const off = shdr.sh_offset + @as(u64, entry_size) * index;
3002 const phdr = &self.program_headers.items[self.phdr_got_index.?];2979 const phdr = &self.program_headers.items[self.phdr_got_index.?];
3003 const vaddr = phdr.p_vaddr + @as(u64, entry_size) * index;2980 const vaddr = phdr.p_vaddr + @as(u64, entry_size) * index;
2981 const got_entry = self.got_table.entries.items[index];
2982 const got_value = self.getSymbol(got_entry).st_value;
3004 switch (entry_size) {2983 switch (entry_size) {
3005 2 => {2984 2 => {
3006 var buf: [2]u8 = undefined;2985 var buf: [2]u8 = undefined;
3007 mem.writeInt(u16, &buf, @intCast(u16, self.offset_table.items[index]), endian);2986 mem.writeInt(u16, &buf, @intCast(u16, got_value), endian);
3008 try self.base.file.?.pwriteAll(&buf, off);2987 try self.base.file.?.pwriteAll(&buf, off);
3009 },2988 },
3010 4 => {2989 4 => {
3011 var buf: [4]u8 = undefined;2990 var buf: [4]u8 = undefined;
3012 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);2991 mem.writeInt(u32, &buf, @intCast(u32, got_value), endian);
3013 try self.base.file.?.pwriteAll(&buf, off);2992 try self.base.file.?.pwriteAll(&buf, off);
3014 },2993 },
3015 8 => {2994 8 => {
3016 var buf: [8]u8 = undefined;2995 var buf: [8]u8 = undefined;
3017 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);2996 mem.writeInt(u64, &buf, got_value, endian);
3018 try self.base.file.?.pwriteAll(&buf, off);2997 try self.base.file.?.pwriteAll(&buf, off);
30192998
3020 if (self.base.child_pid) |pid| {2999 if (self.base.child_pid) |pid| {
src/link/Elf/Atom.zig+13-5
...@@ -14,9 +14,6 @@ const Elf = @import("../Elf.zig");...@@ -14,9 +14,6 @@ const Elf = @import("../Elf.zig");
14/// offset table entry.14/// offset table entry.
15local_sym_index: u32,15local_sym_index: u32,
1616
17/// This field is undefined for symbols with size = 0.
18offset_table_index: u32,
19
20/// Points to the previous and next neighbors, based on the `text_offset`.17/// Points to the previous and next neighbors, based on the `text_offset`.
21/// This can be used to find, for example, the capacity of this `TextBlock`.18/// This can be used to find, for example, the capacity of this `TextBlock`.
22prev_index: ?Index,19prev_index: ?Index,
...@@ -48,13 +45,24 @@ pub fn getName(self: Atom, elf_file: *const Elf) []const u8 {...@@ -48,13 +45,24 @@ pub fn getName(self: Atom, elf_file: *const Elf) []const u8 {
48 return elf_file.getSymbolName(self.getSymbolIndex().?);45 return elf_file.getSymbolName(self.getSymbolIndex().?);
49}46}
5047
48/// If entry already exists, returns index to it.
49/// Otherwise, creates a new entry in the Global Offset Table for this Atom.
50pub fn getOrCreateOffsetTableEntry(self: Atom, elf_file: *Elf) !u32 {
51 const sym_index = self.getSymbolIndex().?;
52 if (elf_file.got_table.lookup.get(sym_index)) |index| return index;
53 const index = try elf_file.got_table.allocateEntry(elf_file.base.allocator, sym_index);
54 elf_file.got_table_count_dirty = true;
55 return index;
56}
57
51pub fn getOffsetTableAddress(self: Atom, elf_file: *Elf) u64 {58pub fn getOffsetTableAddress(self: Atom, elf_file: *Elf) u64 {
52 assert(self.getSymbolIndex() != null);59 const sym_index = self.getSymbolIndex().?;
60 const got_entry_index = elf_file.got_table.lookup.get(sym_index).?;
53 const target = elf_file.base.options.target;61 const target = elf_file.base.options.target;
54 const ptr_bits = target.cpu.arch.ptrBitWidth();62 const ptr_bits = target.cpu.arch.ptrBitWidth();
55 const ptr_bytes: u64 = @divExact(ptr_bits, 8);63 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
56 const got = elf_file.program_headers.items[elf_file.phdr_got_index.?];64 const got = elf_file.program_headers.items[elf_file.phdr_got_index.?];
57 return got.p_vaddr + self.offset_table_index * ptr_bytes;65 return got.p_vaddr + got_entry_index * ptr_bytes;
58}66}
5967
60/// Returns how much room there is to grow in virtual address space.68/// Returns how much room there is to grow in virtual address space.
src/link/MachO.zig+419-614
...@@ -19,6 +19,7 @@ const fat = @import("MachO/fat.zig");...@@ -19,6 +19,7 @@ const fat = @import("MachO/fat.zig");
19const link = @import("../link.zig");19const link = @import("../link.zig");
20const llvm_backend = @import("../codegen/llvm.zig");20const llvm_backend = @import("../codegen/llvm.zig");
21const load_commands = @import("MachO/load_commands.zig");21const load_commands = @import("MachO/load_commands.zig");
22const stubs = @import("MachO/stubs.zig");
22const target_util = @import("../target.zig");23const target_util = @import("../target.zig");
23const trace = @import("../tracy.zig").trace;24const trace = @import("../tracy.zig").trace;
24const zld = @import("MachO/zld.zig");25const zld = @import("MachO/zld.zig");
...@@ -41,6 +42,7 @@ const Md5 = std.crypto.hash.Md5;...@@ -41,6 +42,7 @@ const Md5 = std.crypto.hash.Md5;
41const Module = @import("../Module.zig");42const Module = @import("../Module.zig");
42const Relocation = @import("MachO/Relocation.zig");43const Relocation = @import("MachO/Relocation.zig");
43const StringTable = @import("strtab.zig").StringTable;44const StringTable = @import("strtab.zig").StringTable;
45const TableSection = @import("table_section.zig").TableSection;
44const Trie = @import("MachO/Trie.zig");46const Trie = @import("MachO/Trie.zig");
45const Type = @import("../type.zig").Type;47const Type = @import("../type.zig").Type;
46const TypedValue = @import("../TypedValue.zig");48const TypedValue = @import("../TypedValue.zig");
...@@ -150,17 +152,20 @@ globals_free_list: std.ArrayListUnmanaged(u32) = .{},...@@ -150,17 +152,20 @@ globals_free_list: std.ArrayListUnmanaged(u32) = .{},
150152
151dyld_stub_binder_index: ?u32 = null,153dyld_stub_binder_index: ?u32 = null,
152dyld_private_atom_index: ?Atom.Index = null,154dyld_private_atom_index: ?Atom.Index = null,
153stub_helper_preamble_atom_index: ?Atom.Index = null,
154155
155strtab: StringTable(.strtab) = .{},156strtab: StringTable(.strtab) = .{},
156157
157got_table: SectionTable = .{},158got_table: TableSection(SymbolWithLoc) = .{},
158stubs_table: SectionTable = .{},159stub_table: TableSection(SymbolWithLoc) = .{},
159tlv_table: SectionTable = .{},
160160
161error_flags: File.ErrorFlags = File.ErrorFlags{},161error_flags: File.ErrorFlags = File.ErrorFlags{},
162162
163segment_table_dirty: bool = false,163segment_table_dirty: bool = false,
164got_table_count_dirty: bool = false,
165got_table_contents_dirty: bool = false,
166stub_table_count_dirty: bool = false,
167stub_table_contents_dirty: bool = false,
168stub_helper_preamble_allocated: bool = false,
164169
165/// A helper var to indicate if we are at the start of the incremental updates, or170/// A helper var to indicate if we are at the start of the incremental updates, or
166/// already somewhere further along the update-and-run chain.171/// already somewhere further along the update-and-run chain.
...@@ -210,17 +215,16 @@ rebases: RebaseTable = .{},...@@ -210,17 +215,16 @@ rebases: RebaseTable = .{},
210/// this will be a table indexed by index into the list of Atoms.215/// this will be a table indexed by index into the list of Atoms.
211bindings: BindingTable = .{},216bindings: BindingTable = .{},
212217
213/// A table of lazy bindings indexed by the owning them `Atom`.
214/// Note that once we refactor `Atom`'s lifetime and ownership rules,
215/// this will be a table indexed by index into the list of Atoms.
216lazy_bindings: BindingTable = .{},
217
218/// Table of tracked LazySymbols.218/// Table of tracked LazySymbols.
219lazy_syms: LazySymbolTable = .{},219lazy_syms: LazySymbolTable = .{},
220220
221/// Table of tracked Decls.221/// Table of tracked Decls.
222decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},222decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},
223223
224/// Table of threadlocal variables descriptors.
225/// They are emitted in the `__thread_vars` section.
226tlv_table: TlvSymbolTable = .{},
227
224/// Hot-code swapping state.228/// Hot-code swapping state.
225hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{},229hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{},
226230
...@@ -237,6 +241,8 @@ const LazySymbolMetadata = struct {...@@ -237,6 +241,8 @@ const LazySymbolMetadata = struct {
237 alignment: u32,241 alignment: u32,
238};242};
239243
244const TlvSymbolTable = std.AutoArrayHashMapUnmanaged(SymbolWithLoc, Atom.Index);
245
240const DeclMetadata = struct {246const DeclMetadata = struct {
241 atom: Atom.Index,247 atom: Atom.Index,
242 section: u8,248 section: u8,
...@@ -265,122 +271,6 @@ const DeclMetadata = struct {...@@ -265,122 +271,6 @@ const DeclMetadata = struct {
265 }271 }
266};272};
267273
268const SectionTable = struct {
269 entries: std.ArrayListUnmanaged(Entry) = .{},
270 free_list: std.ArrayListUnmanaged(u32) = .{},
271 lookup: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
272
273 pub fn deinit(st: *ST, allocator: Allocator) void {
274 st.entries.deinit(allocator);
275 st.free_list.deinit(allocator);
276 st.lookup.deinit(allocator);
277 }
278
279 pub fn allocateEntry(st: *ST, allocator: Allocator, target: SymbolWithLoc) !u32 {
280 try st.entries.ensureUnusedCapacity(allocator, 1);
281 const index = blk: {
282 if (st.free_list.popOrNull()) |index| {
283 log.debug(" (reusing entry index {d})", .{index});
284 break :blk index;
285 } else {
286 log.debug(" (allocating entry at index {d})", .{st.entries.items.len});
287 const index = @intCast(u32, st.entries.items.len);
288 _ = st.entries.addOneAssumeCapacity();
289 break :blk index;
290 }
291 };
292 st.entries.items[index] = .{ .target = target, .sym_index = 0 };
293 try st.lookup.putNoClobber(allocator, target, index);
294 return index;
295 }
296
297 pub fn freeEntry(st: *ST, allocator: Allocator, target: SymbolWithLoc) void {
298 const index = st.lookup.get(target) orelse return;
299 st.free_list.append(allocator, index) catch {};
300 st.entries.items[index] = .{
301 .target = .{ .sym_index = 0 },
302 .sym_index = 0,
303 };
304 _ = st.lookup.remove(target);
305 }
306
307 pub fn getAtomIndex(st: *const ST, macho_file: *MachO, target: SymbolWithLoc) ?Atom.Index {
308 const index = st.lookup.get(target) orelse return null;
309 return st.entries.items[index].getAtomIndex(macho_file);
310 }
311
312 const FormatContext = struct {
313 macho_file: *MachO,
314 st: *const ST,
315 };
316
317 fn fmt(
318 ctx: FormatContext,
319 comptime unused_format_string: []const u8,
320 options: std.fmt.FormatOptions,
321 writer: anytype,
322 ) @TypeOf(writer).Error!void {
323 _ = options;
324 comptime assert(unused_format_string.len == 0);
325 try writer.writeAll("SectionTable:\n");
326 for (ctx.st.entries.items, 0..) |entry, i| {
327 const atom_sym = entry.getSymbol(ctx.macho_file);
328 const target_sym = ctx.macho_file.getSymbol(entry.target);
329 try writer.print(" {d}@{x} => ", .{ i, atom_sym.n_value });
330 if (target_sym.undf()) {
331 try writer.print("import('{s}')", .{
332 ctx.macho_file.getSymbolName(entry.target),
333 });
334 } else {
335 try writer.print("local(%{d}) in object({?d})", .{
336 entry.target.sym_index,
337 entry.target.file,
338 });
339 }
340 try writer.writeByte('\n');
341 }
342 }
343
344 fn format(st: *const ST, comptime unused_format_string: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
345 _ = st;
346 _ = unused_format_string;
347 _ = options;
348 _ = writer;
349 @compileError("do not format SectionTable directly; use st.fmtDebug()");
350 }
351
352 pub fn fmtDebug(st: *const ST, macho_file: *MachO) std.fmt.Formatter(fmt) {
353 return .{ .data = .{
354 .macho_file = macho_file,
355 .st = st,
356 } };
357 }
358
359 const ST = @This();
360
361 const Entry = struct {
362 target: SymbolWithLoc,
363 // Index into the synthetic symbol table (i.e., file == null).
364 sym_index: u32,
365
366 pub fn getSymbol(entry: Entry, macho_file: *MachO) macho.nlist_64 {
367 return macho_file.getSymbol(.{ .sym_index = entry.sym_index });
368 }
369
370 pub fn getSymbolPtr(entry: Entry, macho_file: *MachO) *macho.nlist_64 {
371 return macho_file.getSymbolPtr(.{ .sym_index = entry.sym_index });
372 }
373
374 pub fn getAtomIndex(entry: Entry, macho_file: *MachO) ?Atom.Index {
375 return macho_file.getAtomIndexForSymbol(.{ .sym_index = entry.sym_index });
376 }
377
378 pub fn getName(entry: Entry, macho_file: *MachO) []const u8 {
379 return macho_file.getSymbolName(.{ .sym_index = entry.sym_index });
380 }
381 };
382};
383
384const BindingTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Binding));274const BindingTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Binding));
385const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index));275const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index));
386const RebaseTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32));276const RebaseTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32));
...@@ -722,15 +612,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -722,15 +612,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
722 return error.UndefinedSymbolReference;612 return error.UndefinedSymbolReference;
723 }613 }
724614
725 try self.createDyldPrivateAtom();
726 try self.createStubHelperPreambleAtom();
727
728 for (actions.items) |action| switch (action.kind) {615 for (actions.items) |action| switch (action.kind) {
729 .none => {},616 .none => {},
730 .add_got => try self.addGotEntry(action.target),617 .add_got => try self.addGotEntry(action.target),
731 .add_stub => try self.addStubEntry(action.target),618 .add_stub => try self.addStubEntry(action.target),
732 };619 };
733620
621 try self.createDyldPrivateAtom();
622 try self.writeStubHelperPreamble();
623
734 try self.allocateSpecialSymbols();624 try self.allocateSpecialSymbols();
735625
736 for (self.relocs.keys()) |atom_index| {626 for (self.relocs.keys()) |atom_index| {
...@@ -756,6 +646,27 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -756,6 +646,27 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
756 try self.writeAtom(atom_index, code.items);646 try self.writeAtom(atom_index, code.items);
757 }647 }
758648
649 // Update GOT if it got moved in memory.
650 if (self.got_table_contents_dirty) {
651 for (self.got_table.entries.items, 0..) |entry, i| {
652 if (!self.got_table.lookup.contains(entry)) continue;
653 // TODO: write all in one go rather than incrementally.
654 try self.writeOffsetTableEntry(i);
655 }
656 self.got_table_contents_dirty = false;
657 }
658
659 // Update stubs if we moved any section in memory.
660 // TODO: we probably don't need to update all sections if only one got moved.
661 if (self.stub_table_contents_dirty) {
662 for (self.stub_table.entries.items, 0..) |entry, i| {
663 if (!self.stub_table.lookup.contains(entry)) continue;
664 // TODO: write all in one go rather than incrementally.
665 try self.writeStubTableEntry(i);
666 }
667 self.stub_table_contents_dirty = false;
668 }
669
759 if (build_options.enable_logging) {670 if (build_options.enable_logging) {
760 self.logSymtab();671 self.logSymtab();
761 self.logSections();672 self.logSections();
...@@ -1239,19 +1150,16 @@ pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void {...@@ -1239,19 +1150,16 @@ pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void {
1239 }1150 }
1240 }1151 }
12411152
1153 Atom.resolveRelocations(self, atom_index, relocs.items, code);
1154
1242 if (is_hot_update_compatible) {1155 if (is_hot_update_compatible) {
1243 if (self.base.child_pid) |pid| blk: {1156 if (self.hot_state.mach_task) |task| {
1244 const task = self.hot_state.mach_task orelse {1157 self.writeToMemory(task, section.segment_index, sym.n_value, code) catch |err| {
1245 log.warn("cannot hot swap: no Mach task acquired for child process with pid {d}", .{pid});
1246 break :blk;
1247 };
1248 self.updateAtomInMemory(task, section.segment_index, sym.n_value, code) catch |err| {
1249 log.warn("cannot hot swap: writing to memory failed: {s}", .{@errorName(err)});1158 log.warn("cannot hot swap: writing to memory failed: {s}", .{@errorName(err)});
1250 };1159 };
1251 }1160 }
1252 }1161 }
12531162
1254 Atom.resolveRelocations(self, atom_index, relocs.items, code);
1255 try self.base.file.?.pwriteAll(code, file_offset);1163 try self.base.file.?.pwriteAll(code, file_offset);
12561164
1257 // Now we can mark the relocs as resolved.1165 // Now we can mark the relocs as resolved.
...@@ -1260,7 +1168,7 @@ pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void {...@@ -1260,7 +1168,7 @@ pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void {
1260 }1168 }
1261}1169}
12621170
1263fn updateAtomInMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index: u8, addr: u64, code: []const u8) !void {1171fn writeToMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index: u8, addr: u64, code: []const u8) !void {
1264 const segment = self.segments.items[segment_index];1172 const segment = self.segments.items[segment_index];
1265 const cpu_arch = self.base.options.target.cpu.arch;1173 const cpu_arch = self.base.options.target.cpu.arch;
1266 const nwritten = if (!segment.isWriteable())1174 const nwritten = if (!segment.isWriteable())
...@@ -1270,9 +1178,145 @@ fn updateAtomInMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index:...@@ -1270,9 +1178,145 @@ fn updateAtomInMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index:
1270 if (nwritten != code.len) return error.InputOutput;1178 if (nwritten != code.len) return error.InputOutput;
1271}1179}
12721180
1273fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void {1181fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
1274 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);1182 const sect_id = self.got_section_index.?;
1275 try self.writeAtom(atom_index, &buffer);1183
1184 if (self.got_table_count_dirty) {
1185 const needed_size = self.got_table.entries.items.len * @sizeOf(u64);
1186 try self.growSection(sect_id, needed_size);
1187 self.got_table_count_dirty = false;
1188 }
1189
1190 const header = &self.sections.items(.header)[sect_id];
1191 const segment_index = self.sections.items(.segment_index)[sect_id];
1192 const entry = self.got_table.entries.items[index];
1193 const entry_value = self.getSymbol(entry).n_value;
1194 const entry_offset = index * @sizeOf(u64);
1195 const file_offset = header.offset + entry_offset;
1196 const vmaddr = header.addr + entry_offset;
1197
1198 log.debug("writing GOT entry {d}: @{x} => {x}", .{ index, vmaddr, entry_value });
1199
1200 var buf: [@sizeOf(u64)]u8 = undefined;
1201 mem.writeIntLittle(u64, &buf, entry_value);
1202 try self.base.file.?.pwriteAll(&buf, file_offset);
1203
1204 if (is_hot_update_compatible) {
1205 if (self.hot_state.mach_task) |task| {
1206 self.writeToMemory(task, segment_index, vmaddr, &buf) catch |err| {
1207 log.warn("cannot hot swap: writing to memory failed: {s}", .{@errorName(err)});
1208 };
1209 }
1210 }
1211}
1212
1213fn writeStubHelperPreamble(self: *MachO) !void {
1214 if (self.stub_helper_preamble_allocated) return;
1215
1216 const gpa = self.base.allocator;
1217 const cpu_arch = self.base.options.target.cpu.arch;
1218 const size = stubs.calcStubHelperPreambleSize(cpu_arch);
1219
1220 var buf = try std.ArrayList(u8).initCapacity(gpa, size);
1221 defer buf.deinit();
1222
1223 const dyld_private_addr = self.getAtom(self.dyld_private_atom_index.?).getSymbol(self).n_value;
1224 const dyld_stub_binder_got_addr = blk: {
1225 const index = self.got_table.lookup.get(self.getGlobalByIndex(self.dyld_stub_binder_index.?)).?;
1226 const header = self.sections.items(.header)[self.got_section_index.?];
1227 break :blk header.addr + @sizeOf(u64) * index;
1228 };
1229 const header = self.sections.items(.header)[self.stub_helper_section_index.?];
1230
1231 try stubs.writeStubHelperPreambleCode(.{
1232 .cpu_arch = cpu_arch,
1233 .source_addr = header.addr,
1234 .dyld_private_addr = dyld_private_addr,
1235 .dyld_stub_binder_got_addr = dyld_stub_binder_got_addr,
1236 }, buf.writer());
1237 try self.base.file.?.pwriteAll(buf.items, header.offset);
1238
1239 self.stub_helper_preamble_allocated = true;
1240}
1241
1242fn writeStubTableEntry(self: *MachO, index: usize) !void {
1243 const stubs_sect_id = self.stubs_section_index.?;
1244 const stub_helper_sect_id = self.stub_helper_section_index.?;
1245 const laptr_sect_id = self.la_symbol_ptr_section_index.?;
1246
1247 const cpu_arch = self.base.options.target.cpu.arch;
1248 const stub_entry_size = stubs.calcStubEntrySize(cpu_arch);
1249 const stub_helper_entry_size = stubs.calcStubHelperEntrySize(cpu_arch);
1250 const stub_helper_preamble_size = stubs.calcStubHelperPreambleSize(cpu_arch);
1251
1252 if (self.stub_table_count_dirty) {
1253 // We grow all 3 sections one by one.
1254 {
1255 const needed_size = stub_entry_size * self.stub_table.entries.items.len;
1256 try self.growSection(stubs_sect_id, needed_size);
1257 }
1258 {
1259 const needed_size = stub_helper_preamble_size + stub_helper_entry_size * self.stub_table.entries.items.len;
1260 try self.growSection(stub_helper_sect_id, needed_size);
1261 }
1262 {
1263 const needed_size = @sizeOf(u64) * self.stub_table.entries.items.len;
1264 try self.growSection(laptr_sect_id, needed_size);
1265 }
1266 self.stub_table_count_dirty = false;
1267 }
1268
1269 const gpa = self.base.allocator;
1270
1271 const stubs_header = self.sections.items(.header)[stubs_sect_id];
1272 const stub_helper_header = self.sections.items(.header)[stub_helper_sect_id];
1273 const laptr_header = self.sections.items(.header)[laptr_sect_id];
1274
1275 const entry = self.stub_table.entries.items[index];
1276 const stub_addr: u64 = stubs_header.addr + stub_entry_size * index;
1277 const stub_helper_addr: u64 = stub_helper_header.addr + stub_helper_preamble_size + stub_helper_entry_size * index;
1278 const laptr_addr: u64 = laptr_header.addr + @sizeOf(u64) * index;
1279
1280 log.debug("writing stub entry {d}: @{x} => '{s}'", .{ index, stub_addr, self.getSymbolName(entry) });
1281
1282 {
1283 var buf = try std.ArrayList(u8).initCapacity(gpa, stub_entry_size);
1284 defer buf.deinit();
1285 try stubs.writeStubCode(.{
1286 .cpu_arch = cpu_arch,
1287 .source_addr = stub_addr,
1288 .target_addr = laptr_addr,
1289 }, buf.writer());
1290 const off = stubs_header.offset + stub_entry_size * index;
1291 try self.base.file.?.pwriteAll(buf.items, off);
1292 }
1293
1294 {
1295 var buf = try std.ArrayList(u8).initCapacity(gpa, stub_helper_entry_size);
1296 defer buf.deinit();
1297 try stubs.writeStubHelperCode(.{
1298 .cpu_arch = cpu_arch,
1299 .source_addr = stub_helper_addr,
1300 .target_addr = stub_helper_header.addr,
1301 }, buf.writer());
1302 const off = stub_helper_header.offset + stub_helper_preamble_size + stub_helper_entry_size * index;
1303 try self.base.file.?.pwriteAll(buf.items, off);
1304 }
1305
1306 {
1307 var buf: [@sizeOf(u64)]u8 = undefined;
1308 mem.writeIntLittle(u64, &buf, stub_helper_addr);
1309 const off = laptr_header.offset + @sizeOf(u64) * index;
1310 try self.base.file.?.pwriteAll(&buf, off);
1311 }
1312
1313 // TODO: generating new stub entry will require pulling the address of the symbol from the
1314 // target dylib when updating directly in memory.
1315 if (is_hot_update_compatible) {
1316 if (self.hot_state.mach_task) |_| {
1317 @panic("TODO: update a stub entry in memory");
1318 }
1319 }
1276}1320}
12771321
1278fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {1322fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {
...@@ -1290,13 +1334,28 @@ fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void {...@@ -1290,13 +1334,28 @@ fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void {
1290 log.debug("marking relocs dirty by address: {x}", .{addr});1334 log.debug("marking relocs dirty by address: {x}", .{addr});
1291 for (self.relocs.values()) |*relocs| {1335 for (self.relocs.values()) |*relocs| {
1292 for (relocs.items) |*reloc| {1336 for (relocs.items) |*reloc| {
1293 const target_atom_index = reloc.getTargetAtomIndex(self) orelse continue;1337 const target_addr = reloc.getTargetBaseAddress(self) orelse continue;
1294 const target_atom = self.getAtom(target_atom_index);1338 if (target_addr < addr) continue;
1295 const target_sym = target_atom.getSymbol(self);
1296 if (target_sym.n_value < addr) continue;
1297 reloc.dirty = true;1339 reloc.dirty = true;
1298 }1340 }
1299 }1341 }
1342
1343 // TODO: dirty only really affected GOT cells
1344 for (self.got_table.entries.items) |entry| {
1345 const target_addr = self.getSymbol(entry).n_value;
1346 if (target_addr >= addr) {
1347 self.got_table_contents_dirty = true;
1348 break;
1349 }
1350 }
1351
1352 {
1353 const stubs_addr = self.getSegment(self.stubs_section_index.?).vmaddr;
1354 const stub_helper_addr = self.getSegment(self.stub_helper_section_index.?).vmaddr;
1355 const laptr_addr = self.getSegment(self.la_symbol_ptr_section_index.?).vmaddr;
1356 if (stubs_addr >= addr or stub_helper_addr >= addr or laptr_addr >= addr)
1357 self.stub_table_contents_dirty = true;
1358 }
1300}1359}
13011360
1302pub fn allocateSpecialSymbols(self: *MachO) !void {1361pub fn allocateSpecialSymbols(self: *MachO) !void {
...@@ -1335,40 +1394,6 @@ pub fn createAtom(self: *MachO) !Atom.Index {...@@ -1335,40 +1394,6 @@ pub fn createAtom(self: *MachO) !Atom.Index {
1335 return atom_index;1394 return atom_index;
1336}1395}
13371396
1338pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !Atom.Index {
1339 const atom_index = try self.createAtom();
1340 self.getAtomPtr(atom_index).size = @sizeOf(u64);
1341
1342 const sym = self.getAtom(atom_index).getSymbolPtr(self);
1343 sym.n_type = macho.N_SECT;
1344 sym.n_sect = self.got_section_index.? + 1;
1345 sym.n_value = try self.allocateAtom(atom_index, @sizeOf(u64), @alignOf(u64));
1346
1347 log.debug("allocated GOT atom at 0x{x}", .{sym.n_value});
1348
1349 try Atom.addRelocation(self, atom_index, .{
1350 .type = .unsigned,
1351 .target = target,
1352 .offset = 0,
1353 .addend = 0,
1354 .pcrel = false,
1355 .length = 3,
1356 });
1357
1358 const target_sym = self.getSymbol(target);
1359 if (target_sym.undf()) {
1360 try Atom.addBinding(self, atom_index, .{
1361 .target = self.getGlobal(self.getSymbolName(target)).?,
1362 .offset = 0,
1363 });
1364 } else {
1365 try Atom.addRebase(self, atom_index, 0);
1366 }
1367 try self.writePtrWidthAtom(atom_index);
1368
1369 return atom_index;
1370}
1371
1372fn createDyldPrivateAtom(self: *MachO) !void {1397fn createDyldPrivateAtom(self: *MachO) !void {
1373 if (self.dyld_private_atom_index != null) return;1398 if (self.dyld_private_atom_index != null) return;
13741399
...@@ -1383,339 +1408,17 @@ fn createDyldPrivateAtom(self: *MachO) !void {...@@ -1383,339 +1408,17 @@ fn createDyldPrivateAtom(self: *MachO) !void {
13831408
1384 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));1409 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
1385 log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});1410 log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});
1386 try self.writePtrWidthAtom(atom_index);1411 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
1387}1412 try self.writeAtom(atom_index, &buffer);
1388
1389fn createStubHelperPreambleAtom(self: *MachO) !void {
1390 if (self.stub_helper_preamble_atom_index != null) return;
1391
1392 const gpa = self.base.allocator;
1393 const arch = self.base.options.target.cpu.arch;
1394 const size: u5 = switch (arch) {
1395 .x86_64 => 15,
1396 .aarch64 => 6 * @sizeOf(u32),
1397 else => unreachable,
1398 };
1399 const atom_index = try self.createAtom();
1400 const atom = self.getAtomPtr(atom_index);
1401 atom.size = size;
1402
1403 const required_alignment: u32 = switch (arch) {
1404 .x86_64 => 1,
1405 .aarch64 => @alignOf(u32),
1406 else => unreachable,
1407 };
1408
1409 const sym = atom.getSymbolPtr(self);
1410 sym.n_type = macho.N_SECT;
1411 sym.n_sect = self.stub_helper_section_index.? + 1;
1412
1413 const dyld_private = self.getAtom(self.dyld_private_atom_index.?).getSymbolWithLoc();
1414 const dyld_stub_binder = self.globals.items[self.dyld_stub_binder_index.?];
1415
1416 const code = try gpa.alloc(u8, size);
1417 defer gpa.free(code);
1418 mem.set(u8, code, 0);
1419
1420 switch (arch) {
1421 .x86_64 => {
1422 // lea %r11, [rip + disp]
1423 code[0] = 0x4c;
1424 code[1] = 0x8d;
1425 code[2] = 0x1d;
1426 // push %r11
1427 code[7] = 0x41;
1428 code[8] = 0x53;
1429 // jmp [rip + disp]
1430 code[9] = 0xff;
1431 code[10] = 0x25;
1432
1433 try Atom.addRelocations(self, atom_index, &[_]Relocation{ .{
1434 .type = .signed,
1435 .target = dyld_private,
1436 .offset = 3,
1437 .addend = 0,
1438 .pcrel = true,
1439 .length = 2,
1440 }, .{
1441 .type = .got,
1442 .target = dyld_stub_binder,
1443 .offset = 11,
1444 .addend = 0,
1445 .pcrel = true,
1446 .length = 2,
1447 } });
1448 },
1449
1450 .aarch64 => {
1451 // adrp x17, 0
1452 mem.writeIntLittle(u32, code[0..][0..4], aarch64.Instruction.adrp(.x17, 0).toU32());
1453 // add x17, x17, 0
1454 mem.writeIntLittle(u32, code[4..][0..4], aarch64.Instruction.add(.x17, .x17, 0, false).toU32());
1455 // stp x16, x17, [sp, #-16]!
1456 mem.writeIntLittle(u32, code[8..][0..4], aarch64.Instruction.stp(
1457 .x16,
1458 .x17,
1459 aarch64.Register.sp,
1460 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
1461 ).toU32());
1462 // adrp x16, 0
1463 mem.writeIntLittle(u32, code[12..][0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
1464 // ldr x16, [x16, 0]
1465 mem.writeIntLittle(u32, code[16..][0..4], aarch64.Instruction.ldr(
1466 .x16,
1467 .x16,
1468 aarch64.Instruction.LoadStoreOffset.imm(0),
1469 ).toU32());
1470 // br x16
1471 mem.writeIntLittle(u32, code[20..][0..4], aarch64.Instruction.br(.x16).toU32());
1472
1473 try Atom.addRelocations(self, atom_index, &[_]Relocation{ .{
1474 .type = .page,
1475 .target = dyld_private,
1476 .offset = 0,
1477 .addend = 0,
1478 .pcrel = true,
1479 .length = 2,
1480 }, .{
1481 .type = .pageoff,
1482 .target = dyld_private,
1483 .offset = 4,
1484 .addend = 0,
1485 .pcrel = false,
1486 .length = 2,
1487 }, .{
1488 .type = .got_page,
1489 .target = dyld_stub_binder,
1490 .offset = 12,
1491 .addend = 0,
1492 .pcrel = true,
1493 .length = 2,
1494 }, .{
1495 .type = .got_pageoff,
1496 .target = dyld_stub_binder,
1497 .offset = 16,
1498 .addend = 0,
1499 .pcrel = false,
1500 .length = 2,
1501 } });
1502 },
1503
1504 else => unreachable,
1505 }
1506 self.stub_helper_preamble_atom_index = atom_index;
1507
1508 sym.n_value = try self.allocateAtom(atom_index, size, required_alignment);
1509 log.debug("allocated stub preamble atom at 0x{x}", .{sym.n_value});
1510 try self.writeAtom(atom_index, code);
1511}
1512
1513fn createStubHelperAtom(self: *MachO) !Atom.Index {
1514 const gpa = self.base.allocator;
1515 const arch = self.base.options.target.cpu.arch;
1516 const size: u4 = switch (arch) {
1517 .x86_64 => 10,
1518 .aarch64 => 3 * @sizeOf(u32),
1519 else => unreachable,
1520 };
1521 const atom_index = try self.createAtom();
1522 const atom = self.getAtomPtr(atom_index);
1523 atom.size = size;
1524
1525 const required_alignment: u32 = switch (arch) {
1526 .x86_64 => 1,
1527 .aarch64 => @alignOf(u32),
1528 else => unreachable,
1529 };
1530
1531 const sym = atom.getSymbolPtr(self);
1532 sym.n_type = macho.N_SECT;
1533 sym.n_sect = self.stub_helper_section_index.? + 1;
1534
1535 const code = try gpa.alloc(u8, size);
1536 defer gpa.free(code);
1537 mem.set(u8, code, 0);
1538
1539 const stub_helper_preamble_atom_sym_index = if (self.stub_helper_preamble_atom_index) |stub_index|
1540 self.getAtom(stub_index).getSymbolIndex().?
1541 else
1542 unreachable;
1543
1544 switch (arch) {
1545 .x86_64 => {
1546 // pushq
1547 code[0] = 0x68;
1548 // Next 4 bytes 1..4 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
1549 // jmpq
1550 code[5] = 0xe9;
1551
1552 try Atom.addRelocation(self, atom_index, .{
1553 .type = .branch,
1554 .target = .{ .sym_index = stub_helper_preamble_atom_sym_index },
1555 .offset = 6,
1556 .addend = 0,
1557 .pcrel = true,
1558 .length = 2,
1559 });
1560 },
1561 .aarch64 => {
1562 const literal = blk: {
1563 const div_res = try math.divExact(u64, size - @sizeOf(u32), 4);
1564 break :blk math.cast(u18, div_res) orelse return error.Overflow;
1565 };
1566 // ldr w16, literal
1567 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldrLiteral(
1568 .w16,
1569 literal,
1570 ).toU32());
1571 // b disp
1572 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(0).toU32());
1573 // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
1574
1575 try Atom.addRelocation(self, atom_index, .{
1576 .type = .branch,
1577 .target = .{ .sym_index = stub_helper_preamble_atom_sym_index },
1578 .offset = 4,
1579 .addend = 0,
1580 .pcrel = true,
1581 .length = 2,
1582 });
1583 },
1584 else => unreachable,
1585 }
1586
1587 sym.n_value = try self.allocateAtom(atom_index, size, required_alignment);
1588 log.debug("allocated stub helper atom at 0x{x}", .{sym.n_value});
1589 try self.writeAtom(atom_index, code);
1590
1591 return atom_index;
1592}
1593
1594fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !Atom.Index {
1595 const atom_index = try self.createAtom();
1596 const atom = self.getAtomPtr(atom_index);
1597 atom.size = @sizeOf(u64);
1598
1599 const sym = atom.getSymbolPtr(self);
1600 sym.n_type = macho.N_SECT;
1601 sym.n_sect = self.la_symbol_ptr_section_index.? + 1;
1602
1603 try Atom.addRelocation(self, atom_index, .{
1604 .type = .unsigned,
1605 .target = .{ .sym_index = stub_sym_index },
1606 .offset = 0,
1607 .addend = 0,
1608 .pcrel = false,
1609 .length = 3,
1610 });
1611 try Atom.addRebase(self, atom_index, 0);
1612 try Atom.addLazyBinding(self, atom_index, .{
1613 .target = self.getGlobal(self.getSymbolName(target)).?,
1614 .offset = 0,
1615 });
1616
1617 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
1618 log.debug("allocated lazy pointer atom at 0x{x} ({s})", .{ sym.n_value, self.getSymbolName(target) });
1619 try self.writePtrWidthAtom(atom_index);
1620
1621 return atom_index;
1622}
1623
1624fn createStubAtom(self: *MachO, laptr_sym_index: u32) !Atom.Index {
1625 const gpa = self.base.allocator;
1626 const arch = self.base.options.target.cpu.arch;
1627 const size: u4 = switch (arch) {
1628 .x86_64 => 6,
1629 .aarch64 => 3 * @sizeOf(u32),
1630 else => unreachable, // unhandled architecture type
1631 };
1632 const atom_index = try self.createAtom();
1633 const atom = self.getAtomPtr(atom_index);
1634 atom.size = size;
1635
1636 const required_alignment: u32 = switch (arch) {
1637 .x86_64 => 1,
1638 .aarch64 => @alignOf(u32),
1639 else => unreachable, // unhandled architecture type
1640
1641 };
1642
1643 const sym = atom.getSymbolPtr(self);
1644 sym.n_type = macho.N_SECT;
1645 sym.n_sect = self.stubs_section_index.? + 1;
1646
1647 const code = try gpa.alloc(u8, size);
1648 defer gpa.free(code);
1649 mem.set(u8, code, 0);
1650
1651 switch (arch) {
1652 .x86_64 => {
1653 // jmp
1654 code[0] = 0xff;
1655 code[1] = 0x25;
1656
1657 try Atom.addRelocation(self, atom_index, .{
1658 .type = .branch,
1659 .target = .{ .sym_index = laptr_sym_index },
1660 .offset = 2,
1661 .addend = 0,
1662 .pcrel = true,
1663 .length = 2,
1664 });
1665 },
1666 .aarch64 => {
1667 // adrp x16, pages
1668 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
1669 // ldr x16, x16, offset
1670 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(
1671 .x16,
1672 .x16,
1673 aarch64.Instruction.LoadStoreOffset.imm(0),
1674 ).toU32());
1675 // br x16
1676 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32());
1677
1678 try Atom.addRelocations(self, atom_index, &[_]Relocation{
1679 .{
1680 .type = .page,
1681 .target = .{ .sym_index = laptr_sym_index },
1682 .offset = 0,
1683 .addend = 0,
1684 .pcrel = true,
1685 .length = 2,
1686 },
1687 .{
1688 .type = .pageoff,
1689 .target = .{ .sym_index = laptr_sym_index },
1690 .offset = 4,
1691 .addend = 0,
1692 .pcrel = false,
1693 .length = 2,
1694 },
1695 });
1696 },
1697 else => unreachable,
1698 }
1699
1700 sym.n_value = try self.allocateAtom(atom_index, size, required_alignment);
1701 log.debug("allocated stub atom at 0x{x}", .{sym.n_value});
1702 try self.writeAtom(atom_index, code);
1703
1704 return atom_index;
1705}1413}
17061414
1707fn createThreadLocalDescriptorAtom(self: *MachO, target: SymbolWithLoc) !Atom.Index {1415fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: SymbolWithLoc) !Atom.Index {
1708 const gpa = self.base.allocator;1416 const gpa = self.base.allocator;
1709 const size = 3 * @sizeOf(u64);1417 const size = 3 * @sizeOf(u64);
1710 const required_alignment: u32 = 1;1418 const required_alignment: u32 = 1;
1711 const atom_index = try self.createAtom();1419 const atom_index = try self.createAtom();
1712 self.getAtomPtr(atom_index).size = size;1420 self.getAtomPtr(atom_index).size = size;
17131421
1714 const target_sym_name = self.getSymbolName(target);
1715 const name_delimiter = mem.indexOf(u8, target_sym_name, "$").?;
1716 const sym_name = try gpa.dupe(u8, target_sym_name[0..name_delimiter]);
1717 defer gpa.free(sym_name);
1718
1719 const sym = self.getAtom(atom_index).getSymbolPtr(self);1422 const sym = self.getAtom(atom_index).getSymbolPtr(self);
1720 sym.n_type = macho.N_SECT;1423 sym.n_type = macho.N_SECT;
1721 sym.n_sect = self.thread_vars_section_index.? + 1;1424 sym.n_sect = self.thread_vars_section_index.? + 1;
...@@ -1889,8 +1592,7 @@ pub fn deinit(self: *MachO) void {...@@ -1889,8 +1592,7 @@ pub fn deinit(self: *MachO) void {
1889 }1592 }
18901593
1891 self.got_table.deinit(gpa);1594 self.got_table.deinit(gpa);
1892 self.stubs_table.deinit(gpa);1595 self.stub_table.deinit(gpa);
1893 self.tlv_table.deinit(gpa);
1894 self.strtab.deinit(gpa);1596 self.strtab.deinit(gpa);
18951597
1896 self.locals.deinit(gpa);1598 self.locals.deinit(gpa);
...@@ -1923,14 +1625,12 @@ pub fn deinit(self: *MachO) void {...@@ -1923,14 +1625,12 @@ pub fn deinit(self: *MachO) void {
19231625
1924 self.atoms.deinit(gpa);1626 self.atoms.deinit(gpa);
19251627
1926 if (self.base.options.module) |_| {1628 for (self.decls.values()) |*m| {
1927 for (self.decls.values()) |*m| {1629 m.exports.deinit(gpa);
1928 m.exports.deinit(gpa);
1929 }
1930 self.decls.deinit(gpa);
1931 } else {
1932 assert(self.decls.count() == 0);
1933 }1630 }
1631 self.decls.deinit(gpa);
1632 self.lazy_syms.deinit(gpa);
1633 self.tlv_table.deinit(gpa);
19341634
1935 for (self.unnamed_const_atoms.values()) |*atoms| {1635 for (self.unnamed_const_atoms.values()) |*atoms| {
1936 atoms.deinit(gpa);1636 atoms.deinit(gpa);
...@@ -1953,11 +1653,6 @@ pub fn deinit(self: *MachO) void {...@@ -1953,11 +1653,6 @@ pub fn deinit(self: *MachO) void {
1953 bindings.deinit(gpa);1653 bindings.deinit(gpa);
1954 }1654 }
1955 self.bindings.deinit(gpa);1655 self.bindings.deinit(gpa);
1956
1957 for (self.lazy_bindings.values()) |*bindings| {
1958 bindings.deinit(gpa);
1959 }
1960 self.lazy_bindings.deinit(gpa);
1961}1656}
19621657
1963fn freeAtom(self: *MachO, atom_index: Atom.Index) void {1658fn freeAtom(self: *MachO, atom_index: Atom.Index) void {
...@@ -2104,31 +1799,16 @@ fn allocateGlobal(self: *MachO) !u32 {...@@ -2104,31 +1799,16 @@ fn allocateGlobal(self: *MachO) !u32 {
2104fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void {1799fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void {
2105 if (self.got_table.lookup.contains(target)) return;1800 if (self.got_table.lookup.contains(target)) return;
2106 const got_index = try self.got_table.allocateEntry(self.base.allocator, target);1801 const got_index = try self.got_table.allocateEntry(self.base.allocator, target);
2107 const got_atom_index = try self.createGotAtom(target);1802 try self.writeOffsetTableEntry(got_index);
2108 const got_atom = self.getAtom(got_atom_index);1803 self.got_table_count_dirty = true;
2109 self.got_table.entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
2110 self.markRelocsDirtyByTarget(target);1804 self.markRelocsDirtyByTarget(target);
2111}1805}
21121806
2113fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void {1807fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void {
2114 if (self.stubs_table.lookup.contains(target)) return;1808 if (self.stub_table.lookup.contains(target)) return;
2115 const stub_index = try self.stubs_table.allocateEntry(self.base.allocator, target);1809 const stub_index = try self.stub_table.allocateEntry(self.base.allocator, target);
2116 const stub_helper_atom_index = try self.createStubHelperAtom();1810 try self.writeStubTableEntry(stub_index);
2117 const stub_helper_atom = self.getAtom(stub_helper_atom_index);1811 self.stub_table_count_dirty = true;
2118 const laptr_atom_index = try self.createLazyPointerAtom(stub_helper_atom.getSymbolIndex().?, target);
2119 const laptr_atom = self.getAtom(laptr_atom_index);
2120 const stub_atom_index = try self.createStubAtom(laptr_atom.getSymbolIndex().?);
2121 const stub_atom = self.getAtom(stub_atom_index);
2122 self.stubs_table.entries.items[stub_index].sym_index = stub_atom.getSymbolIndex().?;
2123 self.markRelocsDirtyByTarget(target);
2124}
2125
2126fn addTlvEntry(self: *MachO, target: SymbolWithLoc) !void {
2127 if (self.tlv_table.lookup.contains(target)) return;
2128 const tlv_index = try self.tlv_table.allocateEntry(self.base.allocator, target);
2129 const tlv_atom_index = try self.createThreadLocalDescriptorAtom(target);
2130 const tlv_atom = self.getAtom(tlv_atom_index);
2131 self.tlv_table.entries.items[tlv_index].sym_index = tlv_atom.getSymbolIndex().?;
2132 self.markRelocsDirtyByTarget(target);1812 self.markRelocsDirtyByTarget(target);
2133}1813}
21341814
...@@ -2278,6 +1958,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)...@@ -2278,6 +1958,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
2278 }1958 }
2279 }1959 }
22801960
1961 const is_threadlocal = if (decl.val.castTag(.variable)) |payload|
1962 payload.data.is_threadlocal and !self.base.options.single_threaded
1963 else
1964 false;
1965 if (is_threadlocal) return self.updateThreadlocalVariable(module, decl_index);
1966
2281 const atom_index = try self.getOrCreateAtomForDecl(decl_index);1967 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2282 const sym_index = self.getAtom(atom_index).getSymbolIndex().?;1968 const sym_index = self.getAtom(atom_index).getSymbolIndex().?;
2283 Atom.freeRelocations(self, atom_index);1969 Atom.freeRelocations(self, atom_index);
...@@ -2426,6 +2112,101 @@ pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol, alignmen...@@ -2426,6 +2112,101 @@ pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol, alignmen
2426 return atom.*.?;2112 return atom.*.?;
2427}2113}
24282114
2115fn updateThreadlocalVariable(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void {
2116 // Lowering a TLV on macOS involves two stages:
2117 // 1. first we lower the initializer into appopriate section (__thread_data or __thread_bss)
2118 // 2. next, we create a corresponding threadlocal variable descriptor in __thread_vars
2119
2120 // 1. Lower the initializer value.
2121 const init_atom_index = try self.getOrCreateAtomForDecl(decl_index);
2122 const init_atom = self.getAtomPtr(init_atom_index);
2123 const init_sym_index = init_atom.getSymbolIndex().?;
2124 Atom.freeRelocations(self, init_atom_index);
2125
2126 const gpa = self.base.allocator;
2127
2128 var code_buffer = std.ArrayList(u8).init(gpa);
2129 defer code_buffer.deinit();
2130
2131 var decl_state: ?Dwarf.DeclState = if (self.d_sym) |*d_sym|
2132 try d_sym.dwarf.initDeclState(module, decl_index)
2133 else
2134 null;
2135 defer if (decl_state) |*ds| ds.deinit();
2136
2137 const decl = module.declPtr(decl_index);
2138 const decl_metadata = self.decls.get(decl_index).?;
2139 const decl_val = decl.val.castTag(.variable).?.data.init;
2140 const res = if (decl_state) |*ds|
2141 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
2142 .ty = decl.ty,
2143 .val = decl_val,
2144 }, &code_buffer, .{
2145 .dwarf = ds,
2146 }, .{
2147 .parent_atom_index = init_sym_index,
2148 })
2149 else
2150 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
2151 .ty = decl.ty,
2152 .val = decl_val,
2153 }, &code_buffer, .none, .{
2154 .parent_atom_index = init_sym_index,
2155 });
2156
2157 var code = switch (res) {
2158 .ok => code_buffer.items,
2159 .fail => |em| {
2160 decl.analysis = .codegen_failure;
2161 try module.failed_decls.put(module.gpa, decl_index, em);
2162 return;
2163 },
2164 };
2165
2166 const required_alignment = decl.getAlignment(self.base.options.target);
2167
2168 const decl_name = try decl.getFullyQualifiedName(module);
2169 defer gpa.free(decl_name);
2170
2171 const init_sym_name = try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{decl_name});
2172 defer gpa.free(init_sym_name);
2173
2174 const sect_id = decl_metadata.section;
2175 const init_sym = init_atom.getSymbolPtr(self);
2176 init_sym.n_strx = try self.strtab.insert(gpa, init_sym_name);
2177 init_sym.n_type = macho.N_SECT;
2178 init_sym.n_sect = sect_id + 1;
2179 init_sym.n_desc = 0;
2180 init_atom.size = code.len;
2181
2182 init_sym.n_value = try self.allocateAtom(init_atom_index, code.len, required_alignment);
2183 errdefer self.freeAtom(init_atom_index);
2184
2185 log.debug("allocated atom for {s} at 0x{x}", .{ init_sym_name, init_sym.n_value });
2186 log.debug(" (required alignment 0x{x})", .{required_alignment});
2187
2188 try self.writeAtom(init_atom_index, code);
2189
2190 if (decl_state) |*ds| {
2191 try self.d_sym.?.dwarf.commitDeclState(
2192 module,
2193 decl_index,
2194 init_sym.n_value,
2195 self.getAtom(init_atom_index).size,
2196 ds,
2197 );
2198 }
2199
2200 try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
2201
2202 // 2. Create a TLV descriptor.
2203 const init_atom_sym_loc = init_atom.getSymbolWithLoc();
2204 const gop = try self.tlv_table.getOrPut(gpa, init_atom_sym_loc);
2205 assert(!gop.found_existing);
2206 gop.value_ptr.* = try self.createThreadLocalDescriptorAtom(decl_name, init_atom_sym_loc);
2207 self.markRelocsDirtyByTarget(init_atom_sym_loc);
2208}
2209
2429pub fn getOrCreateAtomForDecl(self: *MachO, decl_index: Module.Decl.Index) !Atom.Index {2210pub fn getOrCreateAtomForDecl(self: *MachO, decl_index: Module.Decl.Index) !Atom.Index {
2430 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);2211 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
2431 if (!gop.found_existing) {2212 if (!gop.found_existing) {
...@@ -2493,21 +2274,11 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64...@@ -2493,21 +2274,11 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64
2493 const sect_id = decl_metadata.section;2274 const sect_id = decl_metadata.section;
2494 const header = &self.sections.items(.header)[sect_id];2275 const header = &self.sections.items(.header)[sect_id];
2495 const segment = self.getSegment(sect_id);2276 const segment = self.getSegment(sect_id);
2496 const is_threadlocal = if (!self.base.options.single_threaded)
2497 header.flags == macho.S_THREAD_LOCAL_REGULAR or header.flags == macho.S_THREAD_LOCAL_ZEROFILL
2498 else
2499 false;
2500 const code_len = code.len;2277 const code_len = code.len;
25012278
2502 const sym_name = if (is_threadlocal)
2503 try std.fmt.allocPrint(gpa, "{s}$tlv$init", .{decl_name})
2504 else
2505 decl_name;
2506 defer if (is_threadlocal) gpa.free(sym_name);
2507
2508 if (atom.size != 0) {2279 if (atom.size != 0) {
2509 const sym = atom.getSymbolPtr(self);2280 const sym = atom.getSymbolPtr(self);
2510 sym.n_strx = try self.strtab.insert(gpa, sym_name);2281 sym.n_strx = try self.strtab.insert(gpa, decl_name);
2511 sym.n_type = macho.N_SECT;2282 sym.n_type = macho.N_SECT;
2512 sym.n_sect = sect_id + 1;2283 sym.n_sect = sect_id + 1;
2513 sym.n_desc = 0;2284 sym.n_desc = 0;
...@@ -2517,23 +2288,15 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64...@@ -2517,23 +2288,15 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64
25172288
2518 if (need_realloc) {2289 if (need_realloc) {
2519 const vaddr = try self.growAtom(atom_index, code_len, required_alignment);2290 const vaddr = try self.growAtom(atom_index, code_len, required_alignment);
2520 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, sym.n_value, vaddr });2291 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl_name, sym.n_value, vaddr });
2521 log.debug(" (required alignment 0x{x})", .{required_alignment});2292 log.debug(" (required alignment 0x{x})", .{required_alignment});
25222293
2523 if (vaddr != sym.n_value) {2294 if (vaddr != sym.n_value) {
2524 sym.n_value = vaddr;2295 sym.n_value = vaddr;
2525 // TODO: I think we should update the offset to the initializer here too.
2526 const target: SymbolWithLoc = if (is_threadlocal) blk: {
2527 const tlv_atom_index = self.tlv_table.getAtomIndex(self, .{
2528 .sym_index = sym_index,
2529 }).?;
2530 const tlv_atom = self.getAtom(tlv_atom_index);
2531 break :blk tlv_atom.getSymbolWithLoc();
2532 } else .{ .sym_index = sym_index };
2533 self.markRelocsDirtyByTarget(target);
2534 log.debug(" (updating GOT entry)", .{});2296 log.debug(" (updating GOT entry)", .{});
2535 const got_atom_index = self.got_table.getAtomIndex(self, target).?;2297 const got_atom_index = self.got_table.lookup.get(.{ .sym_index = sym_index }).?;
2536 try self.writePtrWidthAtom(got_atom_index);2298 try self.writeOffsetTableEntry(got_atom_index);
2299 self.markRelocsDirtyByTarget(.{ .sym_index = sym_index });
2537 }2300 }
2538 } else if (code_len < atom.size) {2301 } else if (code_len < atom.size) {
2539 self.shrinkAtom(atom_index, code_len);2302 self.shrinkAtom(atom_index, code_len);
...@@ -2544,7 +2307,7 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64...@@ -2544,7 +2307,7 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64
2544 self.getAtomPtr(atom_index).size = code_len;2307 self.getAtomPtr(atom_index).size = code_len;
2545 } else {2308 } else {
2546 const sym = atom.getSymbolPtr(self);2309 const sym = atom.getSymbolPtr(self);
2547 sym.n_strx = try self.strtab.insert(gpa, sym_name);2310 sym.n_strx = try self.strtab.insert(gpa, decl_name);
2548 sym.n_type = macho.N_SECT;2311 sym.n_type = macho.N_SECT;
2549 sym.n_sect = sect_id + 1;2312 sym.n_sect = sect_id + 1;
2550 sym.n_desc = 0;2313 sym.n_desc = 0;
...@@ -2552,21 +2315,13 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64...@@ -2552,21 +2315,13 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64
2552 const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment);2315 const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment);
2553 errdefer self.freeAtom(atom_index);2316 errdefer self.freeAtom(atom_index);
25542317
2555 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, vaddr });2318 log.debug("allocated atom for {s} at 0x{x}", .{ decl_name, vaddr });
2556 log.debug(" (required alignment 0x{x})", .{required_alignment});2319 log.debug(" (required alignment 0x{x})", .{required_alignment});
25572320
2558 self.getAtomPtr(atom_index).size = code_len;2321 self.getAtomPtr(atom_index).size = code_len;
2559 sym.n_value = vaddr;2322 sym.n_value = vaddr;
25602323
2561 if (is_threadlocal) {2324 try self.addGotEntry(.{ .sym_index = sym_index });
2562 try self.addTlvEntry(.{ .sym_index = sym_index });
2563 }
2564 const target: SymbolWithLoc = if (is_threadlocal) blk: {
2565 const tlv_atom_index = self.tlv_table.getAtomIndex(self, .{ .sym_index = sym_index }).?;
2566 const tlv_atom = self.getAtom(tlv_atom_index);
2567 break :blk tlv_atom.getSymbolWithLoc();
2568 } else .{ .sym_index = sym_index };
2569 try self.addGotEntry(target);
2570 }2325 }
25712326
2572 try self.writeAtom(atom_index, code);2327 try self.writeAtom(atom_index, code);
...@@ -2828,11 +2583,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -2828,11 +2583,7 @@ fn populateMissingMetadata(self: *MachO) !void {
2828 }2583 }
28292584
2830 if (self.stubs_section_index == null) {2585 if (self.stubs_section_index == null) {
2831 const stub_size: u32 = switch (cpu_arch) {2586 const stub_size = stubs.calcStubEntrySize(cpu_arch);
2832 .x86_64 => 6,
2833 .aarch64 => 3 * @sizeOf(u32),
2834 else => unreachable, // unhandled architecture type
2835 };
2836 self.stubs_section_index = try self.allocateSection("__TEXT2", "__stubs", .{2587 self.stubs_section_index = try self.allocateSection("__TEXT2", "__stubs", .{
2837 .size = stub_size,2588 .size = stub_size,
2838 .alignment = switch (cpu_arch) {2589 .alignment = switch (cpu_arch) {
...@@ -3021,7 +2772,7 @@ fn growSection(self: *MachO, sect_id: u8, needed_size: u64) !void {...@@ -3021,7 +2772,7 @@ fn growSection(self: *MachO, sect_id: u8, needed_size: u64) !void {
3021 const last_atom = self.getAtom(last_atom_index);2772 const last_atom = self.getAtom(last_atom_index);
3022 const sym = last_atom.getSymbol(self);2773 const sym = last_atom.getSymbol(self);
3023 break :blk (sym.n_value + last_atom.size) - segment.vmaddr;2774 break :blk (sym.n_value + last_atom.size) - segment.vmaddr;
3024 } else 0;2775 } else header.size;
30252776
3026 log.debug("moving {s},{s} from 0x{x} to 0x{x}", .{2777 log.debug("moving {s},{s} from 0x{x} to 0x{x}", .{
3027 header.segName(),2778 header.segName(),
...@@ -3249,6 +3000,28 @@ fn writeLinkeditSegmentData(self: *MachO) !void {...@@ -3249,6 +3000,28 @@ fn writeLinkeditSegmentData(self: *MachO) !void {
3249 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);3000 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
3250}3001}
32513002
3003fn collectRebaseDataFromTableSection(self: *MachO, sect_id: u8, rebase: *Rebase, table: anytype) !void {
3004 const header = self.sections.items(.header)[sect_id];
3005 const segment_index = self.sections.items(.segment_index)[sect_id];
3006 const segment = self.segments.items[segment_index];
3007 const base_offset = header.addr - segment.vmaddr;
3008 const is_got = if (self.got_section_index) |index| index == sect_id else false;
3009
3010 try rebase.entries.ensureUnusedCapacity(self.base.allocator, table.entries.items.len);
3011
3012 for (table.entries.items, 0..) |entry, i| {
3013 if (!table.lookup.contains(entry)) continue;
3014 const sym = self.getSymbol(entry);
3015 if (is_got and sym.undf()) continue;
3016 const offset = i * @sizeOf(u64);
3017 log.debug(" | rebase at {x}", .{base_offset + offset});
3018 rebase.entries.appendAssumeCapacity(.{
3019 .offset = base_offset + offset,
3020 .segment_id = segment_index,
3021 });
3022 }
3023}
3024
3252fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {3025fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
3253 const gpa = self.base.allocator;3026 const gpa = self.base.allocator;
3254 const slice = self.sections.slice();3027 const slice = self.sections.slice();
...@@ -3276,9 +3049,42 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {...@@ -3276,9 +3049,42 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
3276 }3049 }
3277 }3050 }
32783051
3052 try self.collectRebaseDataFromTableSection(self.got_section_index.?, rebase, self.got_table);
3053 try self.collectRebaseDataFromTableSection(self.la_symbol_ptr_section_index.?, rebase, self.stub_table);
3054
3279 try rebase.finalize(gpa);3055 try rebase.finalize(gpa);
3280}3056}
32813057
3058fn collectBindDataFromTableSection(self: *MachO, sect_id: u8, bind: anytype, table: anytype) !void {
3059 const header = self.sections.items(.header)[sect_id];
3060 const segment_index = self.sections.items(.segment_index)[sect_id];
3061 const segment = self.segments.items[segment_index];
3062 const base_offset = header.addr - segment.vmaddr;
3063
3064 try bind.entries.ensureUnusedCapacity(self.base.allocator, table.entries.items.len);
3065
3066 for (table.entries.items, 0..) |entry, i| {
3067 if (!table.lookup.contains(entry)) continue;
3068 const bind_sym = self.getSymbol(entry);
3069 if (!bind_sym.undf()) continue;
3070 const offset = i * @sizeOf(u64);
3071 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
3072 base_offset + offset,
3073 self.getSymbolName(entry),
3074 @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER),
3075 });
3076 if (bind_sym.weakRef()) {
3077 log.debug(" | marking as weak ref ", .{});
3078 }
3079 bind.entries.appendAssumeCapacity(.{
3080 .target = entry,
3081 .offset = base_offset + offset,
3082 .segment_id = segment_index,
3083 .addend = 0,
3084 });
3085 }
3086}
3087
3282fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {3088fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {
3283 const gpa = self.base.allocator;3089 const gpa = self.base.allocator;
3284 const slice = self.sections.slice();3090 const slice = self.sections.slice();
...@@ -3320,9 +3126,16 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {...@@ -3320,9 +3126,16 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {
3320 }3126 }
3321 }3127 }
33223128
3129 // Gather GOT pointers
3130 try self.collectBindDataFromTableSection(self.got_section_index.?, bind, self.got_table);
3323 try bind.finalize(gpa, self);3131 try bind.finalize(gpa, self);
3324}3132}
33253133
3134fn collectLazyBindData(self: *MachO, bind: anytype) !void {
3135 try self.collectBindDataFromTableSection(self.la_symbol_ptr_section_index.?, bind, self.stub_table);
3136 try bind.finalize(self.base.allocator, self);
3137}
3138
3326fn collectExportData(self: *MachO, trie: *Trie) !void {3139fn collectExportData(self: *MachO, trie: *Trie) !void {
3327 const gpa = self.base.allocator;3140 const gpa = self.base.allocator;
33283141
...@@ -3366,7 +3179,7 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -3366,7 +3179,7 @@ fn writeDyldInfoData(self: *MachO) !void {
33663179
3367 var lazy_bind = LazyBind{};3180 var lazy_bind = LazyBind{};
3368 defer lazy_bind.deinit(gpa);3181 defer lazy_bind.deinit(gpa);
3369 try self.collectBindData(&lazy_bind, self.lazy_bindings);3182 try self.collectLazyBindData(&lazy_bind);
33703183
3371 var trie: Trie = .{};3184 var trie: Trie = .{};
3372 defer trie.deinit(gpa);3185 defer trie.deinit(gpa);
...@@ -3442,34 +3255,26 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void...@@ -3442,34 +3255,26 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void
3442 if (lazy_bind.size() == 0) return;3255 if (lazy_bind.size() == 0) return;
34433256
3444 const stub_helper_section_index = self.stub_helper_section_index.?;3257 const stub_helper_section_index = self.stub_helper_section_index.?;
3445 assert(self.stub_helper_preamble_atom_index != null);3258 assert(self.stub_helper_preamble_allocated);
34463259
3447 const section = self.sections.get(stub_helper_section_index);3260 const header = self.sections.items(.header)[stub_helper_section_index];
34483261
3449 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {3262 const cpu_arch = self.base.options.target.cpu.arch;
3450 .x86_64 => 1,3263 const preamble_size = stubs.calcStubHelperPreambleSize(cpu_arch);
3451 .aarch64 => 2 * @sizeOf(u32),3264 const stub_size = stubs.calcStubHelperEntrySize(cpu_arch);
3452 else => unreachable,3265 const stub_offset = stubs.calcStubOffsetInStubHelper(cpu_arch);
3453 };3266 const base_offset = header.offset + preamble_size;
3454 const header = section.header;
3455 var atom_index = section.last_atom_index.?;
34563267
3457 var index: usize = lazy_bind.offsets.items.len;3268 for (lazy_bind.offsets.items, 0..) |bind_offset, index| {
3458 while (index > 0) : (index -= 1) {3269 const file_offset = base_offset + index * stub_size + stub_offset;
3459 const atom = self.getAtom(atom_index);
3460 const sym = atom.getSymbol(self);
3461 const file_offset = header.offset + sym.n_value - header.addr + stub_offset;
3462 const bind_offset = lazy_bind.offsets.items[index - 1];
34633270
3464 log.debug("writing lazy bind offset 0x{x} ({s}) in stub helper at 0x{x}", .{3271 log.debug("writing lazy bind offset 0x{x} ({s}) in stub helper at 0x{x}", .{
3465 bind_offset,3272 bind_offset,
3466 self.getSymbolName(lazy_bind.entries.items[index - 1].target),3273 self.getSymbolName(lazy_bind.entries.items[index].target),
3467 file_offset,3274 file_offset,
3468 });3275 });
34693276
3470 try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset);3277 try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset);
3471
3472 atom_index = atom.prev_index.?;
3473 }3278 }
3474}3279}
34753280
...@@ -3585,7 +3390,7 @@ const SymtabCtx = struct {...@@ -3585,7 +3390,7 @@ const SymtabCtx = struct {
35853390
3586fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {3391fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
3587 const gpa = self.base.allocator;3392 const gpa = self.base.allocator;
3588 const nstubs = @intCast(u32, self.stubs_table.lookup.count());3393 const nstubs = @intCast(u32, self.stub_table.lookup.count());
3589 const ngot_entries = @intCast(u32, self.got_table.lookup.count());3394 const ngot_entries = @intCast(u32, self.got_table.lookup.count());
3590 const nindirectsyms = nstubs * 2 + ngot_entries;3395 const nindirectsyms = nstubs * 2 + ngot_entries;
3591 const iextdefsym = ctx.nlocalsym;3396 const iextdefsym = ctx.nlocalsym;
...@@ -3606,13 +3411,13 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {...@@ -3606,13 +3411,13 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
3606 const writer = buf.writer();3411 const writer = buf.writer();
36073412
3608 if (self.stubs_section_index) |sect_id| {3413 if (self.stubs_section_index) |sect_id| {
3609 const stubs = &self.sections.items(.header)[sect_id];3414 const stubs_header = &self.sections.items(.header)[sect_id];
3610 stubs.reserved1 = 0;3415 stubs_header.reserved1 = 0;
3611 for (self.stubs_table.entries.items) |entry| {3416 for (self.stub_table.entries.items) |entry| {
3612 if (entry.sym_index == 0) continue;3417 if (!self.stub_table.lookup.contains(entry)) continue;
3613 const target_sym = self.getSymbol(entry.target);3418 const target_sym = self.getSymbol(entry);
3614 assert(target_sym.undf());3419 assert(target_sym.undf());
3615 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);3420 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?);
3616 }3421 }
3617 }3422 }
36183423
...@@ -3620,10 +3425,10 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {...@@ -3620,10 +3425,10 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
3620 const got = &self.sections.items(.header)[sect_id];3425 const got = &self.sections.items(.header)[sect_id];
3621 got.reserved1 = nstubs;3426 got.reserved1 = nstubs;
3622 for (self.got_table.entries.items) |entry| {3427 for (self.got_table.entries.items) |entry| {
3623 if (entry.sym_index == 0) continue;3428 if (!self.got_table.lookup.contains(entry)) continue;
3624 const target_sym = self.getSymbol(entry.target);3429 const target_sym = self.getSymbol(entry);
3625 if (target_sym.undf()) {3430 if (target_sym.undf()) {
3626 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);3431 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?);
3627 } else {3432 } else {
3628 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);3433 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
3629 }3434 }
...@@ -3633,11 +3438,11 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {...@@ -3633,11 +3438,11 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
3633 if (self.la_symbol_ptr_section_index) |sect_id| {3438 if (self.la_symbol_ptr_section_index) |sect_id| {
3634 const la_symbol_ptr = &self.sections.items(.header)[sect_id];3439 const la_symbol_ptr = &self.sections.items(.header)[sect_id];
3635 la_symbol_ptr.reserved1 = nstubs + ngot_entries;3440 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
3636 for (self.stubs_table.entries.items) |entry| {3441 for (self.stub_table.entries.items) |entry| {
3637 if (entry.sym_index == 0) continue;3442 if (!self.stub_table.lookup.contains(entry)) continue;
3638 const target_sym = self.getSymbol(entry.target);3443 const target_sym = self.getSymbol(entry);
3639 assert(target_sym.undf());3444 assert(target_sym.undf());
3640 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);3445 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?);
3641 }3446 }
3642 }3447 }
36433448
...@@ -4321,13 +4126,13 @@ pub fn logSymtab(self: *MachO) void {...@@ -4321,13 +4126,13 @@ pub fn logSymtab(self: *MachO) void {
4321 }4126 }
43224127
4323 log.debug("GOT entries:", .{});4128 log.debug("GOT entries:", .{});
4324 log.debug("{}", .{self.got_table.fmtDebug(self)});4129 log.debug("{}", .{self.got_table});
43254130
4326 log.debug("stubs entries:", .{});4131 log.debug("stubs entries:", .{});
4327 log.debug("{}", .{self.stubs_table.fmtDebug(self)});4132 log.debug("{}", .{self.stub_table});
43284133
4329 log.debug("threadlocal entries:", .{});4134 // log.debug("threadlocal entries:", .{});
4330 log.debug("{}", .{self.tlv_table.fmtDebug(self)});4135 // log.debug("{}", .{self.tlv_table});
4331}4136}
43324137
4333pub fn logAtoms(self: *MachO) void {4138pub fn logAtoms(self: *MachO) void {
src/link/MachO/Atom.zig-17
...@@ -158,21 +158,6 @@ pub fn addBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !void...@@ -158,21 +158,6 @@ pub fn addBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !void
158 try gop.value_ptr.append(gpa, binding);158 try gop.value_ptr.append(gpa, binding);
159}159}
160160
161pub fn addLazyBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !void {
162 const gpa = macho_file.base.allocator;
163 const atom = macho_file.getAtom(atom_index);
164 log.debug(" (adding lazy binding to symbol {s} at offset 0x{x} in %{?d})", .{
165 macho_file.getSymbolName(binding.target),
166 binding.offset,
167 atom.getSymbolIndex(),
168 });
169 const gop = try macho_file.lazy_bindings.getOrPut(gpa, atom_index);
170 if (!gop.found_existing) {
171 gop.value_ptr.* = .{};
172 }
173 try gop.value_ptr.append(gpa, binding);
174}
175
176pub fn resolveRelocations(161pub fn resolveRelocations(
177 macho_file: *MachO,162 macho_file: *MachO,
178 atom_index: Index,163 atom_index: Index,
...@@ -193,6 +178,4 @@ pub fn freeRelocations(macho_file: *MachO, atom_index: Index) void {...@@ -193,6 +178,4 @@ pub fn freeRelocations(macho_file: *MachO, atom_index: Index) void {
193 if (removed_rebases) |*rebases| rebases.value.deinit(gpa);178 if (removed_rebases) |*rebases| rebases.value.deinit(gpa);
194 var removed_bindings = macho_file.bindings.fetchOrderedRemove(atom_index);179 var removed_bindings = macho_file.bindings.fetchOrderedRemove(atom_index);
195 if (removed_bindings) |*bindings| bindings.value.deinit(gpa);180 if (removed_bindings) |*bindings| bindings.value.deinit(gpa);
196 var removed_lazy_bindings = macho_file.lazy_bindings.fetchOrderedRemove(atom_index);
197 if (removed_lazy_bindings) |*lazy_bindings| lazy_bindings.value.deinit(gpa);
198}181}
src/link/MachO/DebugSymbols.zig+2-2
...@@ -230,7 +230,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {...@@ -230,7 +230,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
230 .got_load => blk: {230 .got_load => blk: {
231 const got_index = macho_file.got_table.lookup.get(.{ .sym_index = reloc.target }).?;231 const got_index = macho_file.got_table.lookup.get(.{ .sym_index = reloc.target }).?;
232 const got_entry = macho_file.got_table.entries.items[got_index];232 const got_entry = macho_file.got_table.entries.items[got_index];
233 break :blk got_entry.getSymbol(macho_file);233 break :blk macho_file.getSymbol(got_entry);
234 },234 },
235 };235 };
236 if (sym.n_value == reloc.prev_vaddr) continue;236 if (sym.n_value == reloc.prev_vaddr) continue;
...@@ -240,7 +240,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {...@@ -240,7 +240,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
240 .got_load => blk: {240 .got_load => blk: {
241 const got_index = macho_file.got_table.lookup.get(.{ .sym_index = reloc.target }).?;241 const got_index = macho_file.got_table.lookup.get(.{ .sym_index = reloc.target }).?;
242 const got_entry = macho_file.got_table.entries.items[got_index];242 const got_entry = macho_file.got_table.entries.items[got_index];
243 break :blk got_entry.getName(macho_file);243 break :blk macho_file.getSymbolName(got_entry);
244 },244 },
245 };245 };
246 const sect = &self.sections.items[self.debug_info_section_index.?];246 const sect = &self.sections.items[self.debug_info_section_index.?];
src/link/MachO/Relocation.zig+66-22
...@@ -15,7 +15,7 @@ pub const Type = enum {...@@ -15,7 +15,7 @@ pub const Type = enum {
15 got,15 got,
16 /// RIP-relative displacement16 /// RIP-relative displacement
17 signed,17 signed,
18 /// RIP-relative displacement to GOT pointer to TLV thunk18 /// RIP-relative displacement to a TLV thunk
19 tlv,19 tlv,
2020
21 // aarch6421 // aarch64
...@@ -39,25 +39,35 @@ pub const Type = enum {...@@ -39,25 +39,35 @@ pub const Type = enum {
3939
40/// Returns true if and only if the reloc is dirty AND the target address is available.40/// Returns true if and only if the reloc is dirty AND the target address is available.
41pub fn isResolvable(self: Relocation, macho_file: *MachO) bool {41pub fn isResolvable(self: Relocation, macho_file: *MachO) bool {
42 _ = self.getTargetAtomIndex(macho_file) orelse return false;42 const addr = self.getTargetBaseAddress(macho_file) orelse return false;
43 if (addr == 0) return false;
43 return self.dirty;44 return self.dirty;
44}45}
4546
46pub fn getTargetAtomIndex(self: Relocation, macho_file: *MachO) ?Atom.Index {47pub fn getTargetBaseAddress(self: Relocation, macho_file: *MachO) ?u64 {
47 return switch (self.type) {48 switch (self.type) {
48 .got, .got_page, .got_pageoff => macho_file.got_table.getAtomIndex(macho_file, self.target),49 .got, .got_page, .got_pageoff => {
50 const got_index = macho_file.got_table.lookup.get(self.target) orelse return null;
51 const header = macho_file.sections.items(.header)[macho_file.got_section_index.?];
52 return header.addr + got_index * @sizeOf(u64);
53 },
49 .tlv => {54 .tlv => {
50 const thunk_atom_index = macho_file.tlv_table.getAtomIndex(macho_file, self.target) orelse55 const atom_index = macho_file.tlv_table.get(self.target) orelse return null;
51 return null;56 const atom = macho_file.getAtom(atom_index);
52 const thunk_atom = macho_file.getAtom(thunk_atom_index);57 return atom.getSymbol(macho_file).n_value;
53 return macho_file.got_table.getAtomIndex(macho_file, thunk_atom.getSymbolWithLoc());
54 },58 },
55 .branch => if (macho_file.stubs_table.getAtomIndex(macho_file, self.target)) |index|59 .branch => {
56 index60 if (macho_file.stub_table.lookup.get(self.target)) |index| {
57 else61 const header = macho_file.sections.items(.header)[macho_file.stubs_section_index.?];
58 macho_file.getAtomIndexForSymbol(self.target),62 return header.addr +
59 else => macho_file.getAtomIndexForSymbol(self.target),63 index * @import("stubs.zig").calcStubEntrySize(macho_file.base.options.target.cpu.arch);
60 };64 }
65 const atom_index = macho_file.getAtomIndexForSymbol(self.target) orelse return null;
66 const atom = macho_file.getAtom(atom_index);
67 return atom.getSymbol(macho_file).n_value;
68 },
69 else => return macho_file.getSymbol(self.target).n_value,
70 }
61}71}
6272
63pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, code: []u8) void {73pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, code: []u8) void {
...@@ -66,17 +76,14 @@ pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, cod...@@ -66,17 +76,14 @@ pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, cod
66 const source_sym = atom.getSymbol(macho_file);76 const source_sym = atom.getSymbol(macho_file);
67 const source_addr = source_sym.n_value + self.offset;77 const source_addr = source_sym.n_value + self.offset;
6878
69 const target_atom_index = self.getTargetAtomIndex(macho_file).?; // Oops, you didn't check if the relocation can be resolved with isResolvable().79 const target_base_addr = self.getTargetBaseAddress(macho_file).?; // Oops, you didn't check if the relocation can be resolved with isResolvable().
70 const target_atom = macho_file.getAtom(target_atom_index);
71
72 const target_addr: i64 = switch (self.type) {80 const target_addr: i64 = switch (self.type) {
73 .tlv_initializer => blk: {81 .tlv_initializer => blk: {
74 assert(self.addend == 0); // Addend here makes no sense.82 assert(self.addend == 0); // Addend here makes no sense.
75 const header = macho_file.sections.items(.header)[macho_file.thread_data_section_index.?];83 const header = macho_file.sections.items(.header)[macho_file.thread_data_section_index.?];
76 const target_sym = target_atom.getSymbol(macho_file);84 break :blk @intCast(i64, target_base_addr - header.addr);
77 break :blk @intCast(i64, target_sym.n_value - header.addr);
78 },85 },
79 else => @intCast(i64, target_atom.getSymbol(macho_file).n_value) + self.addend,86 else => @intCast(i64, target_base_addr) + self.addend,
80 };87 };
8188
82 log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{89 log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{
...@@ -189,11 +196,48 @@ fn resolveX8664(self: Relocation, source_addr: u64, target_addr: i64, code: []u8...@@ -189,11 +196,48 @@ fn resolveX8664(self: Relocation, source_addr: u64, target_addr: i64, code: []u8
189 }196 }
190}197}
191198
192inline fn isArithmeticOp(inst: *const [4]u8) bool {199pub inline fn isArithmeticOp(inst: *const [4]u8) bool {
193 const group_decode = @truncate(u5, inst[3]);200 const group_decode = @truncate(u5, inst[3]);
194 return ((group_decode >> 2) == 4);201 return ((group_decode >> 2) == 4);
195}202}
196203
204pub fn calcPcRelativeDisplacementX86(source_addr: u64, target_addr: u64, correction: u3) error{Overflow}!i32 {
205 const disp = @intCast(i64, target_addr) - @intCast(i64, source_addr + 4 + correction);
206 return math.cast(i32, disp) orelse error.Overflow;
207}
208
209pub fn calcPcRelativeDisplacementArm64(source_addr: u64, target_addr: u64) error{Overflow}!i28 {
210 const disp = @intCast(i64, target_addr) - @intCast(i64, source_addr);
211 return math.cast(i28, disp) orelse error.Overflow;
212}
213
214pub fn calcNumberOfPages(source_addr: u64, target_addr: u64) i21 {
215 const source_page = @intCast(i32, source_addr >> 12);
216 const target_page = @intCast(i32, target_addr >> 12);
217 const pages = @intCast(i21, target_page - source_page);
218 return pages;
219}
220
221pub const PageOffsetInstKind = enum {
222 arithmetic,
223 load_store_8,
224 load_store_16,
225 load_store_32,
226 load_store_64,
227 load_store_128,
228};
229
230pub fn calcPageOffset(target_addr: u64, kind: PageOffsetInstKind) !u12 {
231 const narrowed = @truncate(u12, target_addr);
232 return switch (kind) {
233 .arithmetic, .load_store_8 => narrowed,
234 .load_store_16 => try math.divExact(u12, narrowed, 2),
235 .load_store_32 => try math.divExact(u12, narrowed, 4),
236 .load_store_64 => try math.divExact(u12, narrowed, 8),
237 .load_store_128 => try math.divExact(u12, narrowed, 16),
238 };
239}
240
197const Relocation = @This();241const Relocation = @This();
198242
199const std = @import("std");243const std = @import("std");
src/link/MachO/ZldAtom.zig+17-58
...@@ -21,6 +21,7 @@ const Allocator = mem.Allocator;...@@ -21,6 +21,7 @@ const Allocator = mem.Allocator;
21const Arch = std.Target.Cpu.Arch;21const Arch = std.Target.Cpu.Arch;
22const AtomIndex = @import("zld.zig").AtomIndex;22const AtomIndex = @import("zld.zig").AtomIndex;
23const Object = @import("Object.zig");23const Object = @import("Object.zig");
24const Relocation = @import("Relocation.zig");
24const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;25const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
25const Zld = @import("zld.zig").Zld;26const Zld = @import("zld.zig").Zld;
2627
...@@ -571,7 +572,7 @@ fn resolveRelocsArm64(...@@ -571,7 +572,7 @@ fn resolveRelocsArm64(
571 zld.getAtom(getRelocTargetAtomIndex(zld, target, is_via_got).?).getFile(),572 zld.getAtom(getRelocTargetAtomIndex(zld, target, is_via_got).?).getFile(),
572 });573 });
573574
574 const displacement = if (calcPcRelativeDisplacementArm64(575 const displacement = if (Relocation.calcPcRelativeDisplacementArm64(
575 source_addr,576 source_addr,
576 zld.getSymbol(actual_target).n_value,577 zld.getSymbol(actual_target).n_value,
577 )) |disp| blk: {578 )) |disp| blk: {
...@@ -585,7 +586,7 @@ fn resolveRelocsArm64(...@@ -585,7 +586,7 @@ fn resolveRelocsArm64(
585 actual_target,586 actual_target,
586 ).?);587 ).?);
587 log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_sym.n_value});588 log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_sym.n_value});
588 break :blk try calcPcRelativeDisplacementArm64(source_addr, thunk_sym.n_value);589 break :blk try Relocation.calcPcRelativeDisplacementArm64(source_addr, thunk_sym.n_value);
589 };590 };
590591
591 const code = atom_code[rel_offset..][0..4];592 const code = atom_code[rel_offset..][0..4];
...@@ -607,7 +608,7 @@ fn resolveRelocsArm64(...@@ -607,7 +608,7 @@ fn resolveRelocsArm64(
607608
608 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});609 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
609610
610 const pages = @bitCast(u21, calcNumberOfPages(source_addr, adjusted_target_addr));611 const pages = @bitCast(u21, Relocation.calcNumberOfPages(source_addr, adjusted_target_addr));
611 const code = atom_code[rel_offset..][0..4];612 const code = atom_code[rel_offset..][0..4];
612 var inst = aarch64.Instruction{613 var inst = aarch64.Instruction{
613 .pc_relative_address = mem.bytesToValue(meta.TagPayload(614 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
...@@ -627,8 +628,8 @@ fn resolveRelocsArm64(...@@ -627,8 +628,8 @@ fn resolveRelocsArm64(
627 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});628 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
628629
629 const code = atom_code[rel_offset..][0..4];630 const code = atom_code[rel_offset..][0..4];
630 if (isArithmeticOp(code)) {631 if (Relocation.isArithmeticOp(code)) {
631 const off = try calcPageOffset(adjusted_target_addr, .arithmetic);632 const off = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic);
632 var inst = aarch64.Instruction{633 var inst = aarch64.Instruction{
633 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(634 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
634 aarch64.Instruction,635 aarch64.Instruction,
...@@ -644,11 +645,11 @@ fn resolveRelocsArm64(...@@ -644,11 +645,11 @@ fn resolveRelocsArm64(
644 aarch64.Instruction.load_store_register,645 aarch64.Instruction.load_store_register,
645 ), code),646 ), code),
646 };647 };
647 const off = try calcPageOffset(adjusted_target_addr, switch (inst.load_store_register.size) {648 const off = try Relocation.calcPageOffset(adjusted_target_addr, switch (inst.load_store_register.size) {
648 0 => if (inst.load_store_register.v == 1)649 0 => if (inst.load_store_register.v == 1)
649 PageOffsetInstKind.load_store_128650 Relocation.PageOffsetInstKind.load_store_128
650 else651 else
651 PageOffsetInstKind.load_store_8,652 Relocation.PageOffsetInstKind.load_store_8,
652 1 => .load_store_16,653 1 => .load_store_16,
653 2 => .load_store_32,654 2 => .load_store_32,
654 3 => .load_store_64,655 3 => .load_store_64,
...@@ -665,7 +666,7 @@ fn resolveRelocsArm64(...@@ -665,7 +666,7 @@ fn resolveRelocsArm64(
665666
666 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});667 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
667668
668 const off = try calcPageOffset(adjusted_target_addr, .load_store_64);669 const off = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64);
669 var inst: aarch64.Instruction = .{670 var inst: aarch64.Instruction = .{
670 .load_store_register = mem.bytesToValue(meta.TagPayload(671 .load_store_register = mem.bytesToValue(meta.TagPayload(
671 aarch64.Instruction,672 aarch64.Instruction,
...@@ -689,7 +690,7 @@ fn resolveRelocsArm64(...@@ -689,7 +690,7 @@ fn resolveRelocsArm64(
689 size: u2,690 size: u2,
690 };691 };
691 const reg_info: RegInfo = blk: {692 const reg_info: RegInfo = blk: {
692 if (isArithmeticOp(code)) {693 if (Relocation.isArithmeticOp(code)) {
693 const inst = mem.bytesToValue(meta.TagPayload(694 const inst = mem.bytesToValue(meta.TagPayload(
694 aarch64.Instruction,695 aarch64.Instruction,
695 aarch64.Instruction.add_subtract_immediate,696 aarch64.Instruction.add_subtract_immediate,
...@@ -716,7 +717,7 @@ fn resolveRelocsArm64(...@@ -716,7 +717,7 @@ fn resolveRelocsArm64(
716 .load_store_register = .{717 .load_store_register = .{
717 .rt = reg_info.rd,718 .rt = reg_info.rd,
718 .rn = reg_info.rn,719 .rn = reg_info.rn,
719 .offset = try calcPageOffset(adjusted_target_addr, .load_store_64),720 .offset = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64),
720 .opc = 0b01,721 .opc = 0b01,
721 .op1 = 0b01,722 .op1 = 0b01,
722 .v = 0,723 .v = 0,
...@@ -726,7 +727,7 @@ fn resolveRelocsArm64(...@@ -726,7 +727,7 @@ fn resolveRelocsArm64(
726 .add_subtract_immediate = .{727 .add_subtract_immediate = .{
727 .rd = reg_info.rd,728 .rd = reg_info.rd,
728 .rn = reg_info.rn,729 .rn = reg_info.rn,
729 .imm12 = try calcPageOffset(adjusted_target_addr, .arithmetic),730 .imm12 = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic),
730 .sh = 0,731 .sh = 0,
731 .s = 0,732 .s = 0,
732 .op = 0,733 .op = 0,
...@@ -858,7 +859,7 @@ fn resolveRelocsX86(...@@ -858,7 +859,7 @@ fn resolveRelocsX86(
858 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);859 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
859 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);860 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
860 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});861 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
861 const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);862 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
862 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);863 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
863 },864 },
864865
...@@ -868,7 +869,7 @@ fn resolveRelocsX86(...@@ -868,7 +869,7 @@ fn resolveRelocsX86(
868 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);869 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
869 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);870 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
870 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});871 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
871 const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);872 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
872 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);873 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
873 },874 },
874875
...@@ -876,7 +877,7 @@ fn resolveRelocsX86(...@@ -876,7 +877,7 @@ fn resolveRelocsX86(
876 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);877 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
877 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);878 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
878 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});879 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
879 const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);880 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
880881
881 if (zld.tlv_ptr_table.get(target) == null) {882 if (zld.tlv_ptr_table.get(target) == null) {
882 // We need to rewrite the opcode from movq to leaq.883 // We need to rewrite the opcode from movq to leaq.
...@@ -913,7 +914,7 @@ fn resolveRelocsX86(...@@ -913,7 +914,7 @@ fn resolveRelocsX86(
913914
914 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});915 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
915916
916 const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, correction);917 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, correction);
917 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);918 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
918 },919 },
919920
...@@ -955,11 +956,6 @@ fn resolveRelocsX86(...@@ -955,11 +956,6 @@ fn resolveRelocsX86(
955 }956 }
956}957}
957958
958inline fn isArithmeticOp(inst: *const [4]u8) bool {
959 const group_decode = @truncate(u5, inst[3]);
960 return ((group_decode >> 2) == 4);
961}
962
963pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {959pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {
964 const atom = zld.getAtom(atom_index);960 const atom = zld.getAtom(atom_index);
965 assert(atom.getFile() != null); // Synthetic atom shouldn't need to inquire for code.961 assert(atom.getFile() != null); // Synthetic atom shouldn't need to inquire for code.
...@@ -1006,43 +1002,6 @@ pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []const macho.relocation_...@@ -1006,43 +1002,6 @@ pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []const macho.relocation_
1006 return relocs[cache.start..][0..cache.len];1002 return relocs[cache.start..][0..cache.len];
1007}1003}
10081004
1009pub fn calcPcRelativeDisplacementX86(source_addr: u64, target_addr: u64, correction: u3) error{Overflow}!i32 {
1010 const disp = @intCast(i64, target_addr) - @intCast(i64, source_addr + 4 + correction);
1011 return math.cast(i32, disp) orelse error.Overflow;
1012}
1013
1014pub fn calcPcRelativeDisplacementArm64(source_addr: u64, target_addr: u64) error{Overflow}!i28 {
1015 const disp = @intCast(i64, target_addr) - @intCast(i64, source_addr);
1016 return math.cast(i28, disp) orelse error.Overflow;
1017}
1018
1019pub fn calcNumberOfPages(source_addr: u64, target_addr: u64) i21 {
1020 const source_page = @intCast(i32, source_addr >> 12);
1021 const target_page = @intCast(i32, target_addr >> 12);
1022 const pages = @intCast(i21, target_page - source_page);
1023 return pages;
1024}
1025
1026const PageOffsetInstKind = enum {
1027 arithmetic,
1028 load_store_8,
1029 load_store_16,
1030 load_store_32,
1031 load_store_64,
1032 load_store_128,
1033};
1034
1035pub fn calcPageOffset(target_addr: u64, kind: PageOffsetInstKind) !u12 {
1036 const narrowed = @truncate(u12, target_addr);
1037 return switch (kind) {
1038 .arithmetic, .load_store_8 => narrowed,
1039 .load_store_16 => try math.divExact(u12, narrowed, 2),
1040 .load_store_32 => try math.divExact(u12, narrowed, 4),
1041 .load_store_64 => try math.divExact(u12, narrowed, 8),
1042 .load_store_128 => try math.divExact(u12, narrowed, 16),
1043 };
1044}
1045
1046pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool {1005pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool {
1047 switch (zld.options.target.cpu.arch) {1006 switch (zld.options.target.cpu.arch) {
1048 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {1007 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
src/link/MachO/eh_frame.zig+2-1
...@@ -9,6 +9,7 @@ const log = std.log.scoped(.eh_frame);...@@ -9,6 +9,7 @@ const log = std.log.scoped(.eh_frame);
9const Allocator = mem.Allocator;9const Allocator = mem.Allocator;
10const AtomIndex = @import("zld.zig").AtomIndex;10const AtomIndex = @import("zld.zig").AtomIndex;
11const Atom = @import("ZldAtom.zig");11const Atom = @import("ZldAtom.zig");
12const Relocation = @import("Relocation.zig");
12const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;13const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
13const UnwindInfo = @import("UnwindInfo.zig");14const UnwindInfo = @import("UnwindInfo.zig");
14const Zld = @import("zld.zig").Zld;15const Zld = @import("zld.zig").Zld;
...@@ -368,7 +369,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {...@@ -368,7 +369,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
368 const target_addr = try Atom.getRelocTargetAddress(zld, target, true, false);369 const target_addr = try Atom.getRelocTargetAddress(zld, target, true, false);
369 const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]);370 const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]);
370 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);371 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
371 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);372 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
372 mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], disp);373 mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], disp);
373 },374 },
374 else => unreachable,375 else => unreachable,
src/link/MachO/stubs.zig created+161
...@@ -0,0 +1,161 @@
1const std = @import("std");
2const aarch64 = @import("../../arch/aarch64/bits.zig");
3
4const Relocation = @import("Relocation.zig");
5
6pub inline fn calcStubHelperPreambleSize(cpu_arch: std.Target.Cpu.Arch) u5 {
7 return switch (cpu_arch) {
8 .x86_64 => 15,
9 .aarch64 => 6 * @sizeOf(u32),
10 else => unreachable, // unhandled architecture type
11 };
12}
13
14pub inline fn calcStubHelperEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 {
15 return switch (cpu_arch) {
16 .x86_64 => 10,
17 .aarch64 => 3 * @sizeOf(u32),
18 else => unreachable, // unhandled architecture type
19 };
20}
21
22pub inline fn calcStubEntrySize(cpu_arch: std.Target.Cpu.Arch) u4 {
23 return switch (cpu_arch) {
24 .x86_64 => 6,
25 .aarch64 => 3 * @sizeOf(u32),
26 else => unreachable, // unhandled architecture type
27 };
28}
29
30pub inline fn calcStubOffsetInStubHelper(cpu_arch: std.Target.Cpu.Arch) u4 {
31 return switch (cpu_arch) {
32 .x86_64 => 1,
33 .aarch64 => 2 * @sizeOf(u32),
34 else => unreachable,
35 };
36}
37
38pub fn writeStubHelperPreambleCode(args: struct {
39 cpu_arch: std.Target.Cpu.Arch,
40 source_addr: u64,
41 dyld_private_addr: u64,
42 dyld_stub_binder_got_addr: u64,
43}, writer: anytype) !void {
44 switch (args.cpu_arch) {
45 .x86_64 => {
46 try writer.writeAll(&.{ 0x4c, 0x8d, 0x1d });
47 {
48 const disp = try Relocation.calcPcRelativeDisplacementX86(
49 args.source_addr + 3,
50 args.dyld_private_addr,
51 0,
52 );
53 try writer.writeIntLittle(i32, disp);
54 }
55 try writer.writeAll(&.{ 0x41, 0x53, 0xff, 0x25 });
56 {
57 const disp = try Relocation.calcPcRelativeDisplacementX86(
58 args.source_addr + 11,
59 args.dyld_stub_binder_got_addr,
60 0,
61 );
62 try writer.writeIntLittle(i32, disp);
63 }
64 },
65 .aarch64 => {
66 {
67 const pages = Relocation.calcNumberOfPages(args.source_addr, args.dyld_private_addr);
68 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x17, pages).toU32());
69 }
70 {
71 const off = try Relocation.calcPageOffset(args.dyld_private_addr, .arithmetic);
72 try writer.writeIntLittle(u32, aarch64.Instruction.add(.x17, .x17, off, false).toU32());
73 }
74 try writer.writeIntLittle(u32, aarch64.Instruction.stp(
75 .x16,
76 .x17,
77 aarch64.Register.sp,
78 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
79 ).toU32());
80 {
81 const pages = Relocation.calcNumberOfPages(args.source_addr + 12, args.dyld_stub_binder_got_addr);
82 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
83 }
84 {
85 const off = try Relocation.calcPageOffset(args.dyld_stub_binder_got_addr, .load_store_64);
86 try writer.writeIntLittle(u32, aarch64.Instruction.ldr(
87 .x16,
88 .x16,
89 aarch64.Instruction.LoadStoreOffset.imm(off),
90 ).toU32());
91 }
92 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
93 },
94 else => unreachable,
95 }
96}
97
98pub fn writeStubHelperCode(args: struct {
99 cpu_arch: std.Target.Cpu.Arch,
100 source_addr: u64,
101 target_addr: u64,
102}, writer: anytype) !void {
103 switch (args.cpu_arch) {
104 .x86_64 => {
105 try writer.writeAll(&.{ 0x68, 0x0, 0x0, 0x0, 0x0, 0xe9 });
106 {
107 const disp = try Relocation.calcPcRelativeDisplacementX86(args.source_addr + 6, args.target_addr, 0);
108 try writer.writeIntLittle(i32, disp);
109 }
110 },
111 .aarch64 => {
112 const stub_size: u4 = 3 * @sizeOf(u32);
113 const literal = blk: {
114 const div_res = try std.math.divExact(u64, stub_size - @sizeOf(u32), 4);
115 break :blk std.math.cast(u18, div_res) orelse return error.Overflow;
116 };
117 try writer.writeIntLittle(u32, aarch64.Instruction.ldrLiteral(
118 .w16,
119 literal,
120 ).toU32());
121 {
122 const disp = try Relocation.calcPcRelativeDisplacementArm64(args.source_addr + 4, args.target_addr);
123 try writer.writeIntLittle(u32, aarch64.Instruction.b(disp).toU32());
124 }
125 try writer.writeAll(&.{ 0x0, 0x0, 0x0, 0x0 });
126 },
127 else => unreachable,
128 }
129}
130
131pub fn writeStubCode(args: struct {
132 cpu_arch: std.Target.Cpu.Arch,
133 source_addr: u64,
134 target_addr: u64,
135}, writer: anytype) !void {
136 switch (args.cpu_arch) {
137 .x86_64 => {
138 try writer.writeAll(&.{ 0xff, 0x25 });
139 {
140 const disp = try Relocation.calcPcRelativeDisplacementX86(args.source_addr + 2, args.target_addr, 0);
141 try writer.writeIntLittle(i32, disp);
142 }
143 },
144 .aarch64 => {
145 {
146 const pages = Relocation.calcNumberOfPages(args.source_addr, args.target_addr);
147 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
148 }
149 {
150 const off = try Relocation.calcPageOffset(args.target_addr, .load_store_64);
151 try writer.writeIntLittle(u32, aarch64.Instruction.ldr(
152 .x16,
153 .x16,
154 aarch64.Instruction.LoadStoreOffset.imm(off),
155 ).toU32());
156 }
157 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
158 },
159 else => unreachable,
160 }
161}
src/link/MachO/thunks.zig+4-3
...@@ -17,6 +17,7 @@ const aarch64 = @import("../../arch/aarch64/bits.zig");...@@ -17,6 +17,7 @@ const aarch64 = @import("../../arch/aarch64/bits.zig");
17const Allocator = mem.Allocator;17const Allocator = mem.Allocator;
18const Atom = @import("ZldAtom.zig");18const Atom = @import("ZldAtom.zig");
19const AtomIndex = @import("zld.zig").AtomIndex;19const AtomIndex = @import("zld.zig").AtomIndex;
20const Relocation = @import("Relocation.zig");
20const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;21const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
21const Zld = @import("zld.zig").Zld;22const Zld = @import("zld.zig").Zld;
2223
...@@ -317,7 +318,7 @@ fn isReachable(...@@ -317,7 +318,7 @@ fn isReachable(
317 const source_addr = source_sym.n_value + @intCast(u32, rel.r_address - base_offset);318 const source_addr = source_sym.n_value + @intCast(u32, rel.r_address - base_offset);
318 const is_via_got = Atom.relocRequiresGot(zld, rel);319 const is_via_got = Atom.relocRequiresGot(zld, rel);
319 const target_addr = Atom.getRelocTargetAddress(zld, target, is_via_got, false) catch unreachable;320 const target_addr = Atom.getRelocTargetAddress(zld, target, is_via_got, false) catch unreachable;
320 _ = Atom.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch321 _ = Relocation.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch
321 return false;322 return false;
322323
323 return true;324 return true;
...@@ -364,9 +365,9 @@ pub fn writeThunkCode(zld: *Zld, atom_index: AtomIndex, writer: anytype) !void {...@@ -364,9 +365,9 @@ pub fn writeThunkCode(zld: *Zld, atom_index: AtomIndex, writer: anytype) !void {
364 if (atom_index == target_atom_index) break zld.getSymbol(target).n_value;365 if (atom_index == target_atom_index) break zld.getSymbol(target).n_value;
365 } else unreachable;366 } else unreachable;
366367
367 const pages = Atom.calcNumberOfPages(source_addr, target_addr);368 const pages = Relocation.calcNumberOfPages(source_addr, target_addr);
368 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());369 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
369 const off = try Atom.calcPageOffset(target_addr, .arithmetic);370 const off = try Relocation.calcPageOffset(target_addr, .arithmetic);
370 try writer.writeIntLittle(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32());371 try writer.writeIntLittle(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32());
371 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());372 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
372}373}
src/link/MachO/zld.zig+20-110
...@@ -16,6 +16,7 @@ const link = @import("../../link.zig");...@@ -16,6 +16,7 @@ const link = @import("../../link.zig");
16const load_commands = @import("load_commands.zig");16const load_commands = @import("load_commands.zig");
17const thunks = @import("thunks.zig");17const thunks = @import("thunks.zig");
18const trace = @import("../../tracy.zig").trace;18const trace = @import("../../tracy.zig").trace;
19const stub_helpers = @import("stubs.zig");
1920
20const Allocator = mem.Allocator;21const Allocator = mem.Allocator;
21const Archive = @import("Archive.zig");22const Archive = @import("Archive.zig");
...@@ -666,59 +667,17 @@ pub const Zld = struct {...@@ -666,59 +667,17 @@ pub const Zld = struct {
666 const entry = self.got_entries.items[index];667 const entry = self.got_entries.items[index];
667 break :blk entry.getAtomSymbol(self).n_value;668 break :blk entry.getAtomSymbol(self).n_value;
668 };669 };
669 switch (cpu_arch) {670 try stub_helpers.writeStubHelperPreambleCode(.{
670 .x86_64 => {671 .cpu_arch = cpu_arch,
671 try writer.writeAll(&.{ 0x4c, 0x8d, 0x1d });672 .source_addr = source_addr,
672 {673 .dyld_private_addr = dyld_private_addr,
673 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 3, dyld_private_addr, 0);674 .dyld_stub_binder_got_addr = dyld_stub_binder_got_addr,
674 try writer.writeIntLittle(i32, disp);675 }, writer);
675 }
676 try writer.writeAll(&.{ 0x41, 0x53, 0xff, 0x25 });
677 {
678 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 11, dyld_stub_binder_got_addr, 0);
679 try writer.writeIntLittle(i32, disp);
680 }
681 },
682 .aarch64 => {
683 {
684 const pages = Atom.calcNumberOfPages(source_addr, dyld_private_addr);
685 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x17, pages).toU32());
686 }
687 {
688 const off = try Atom.calcPageOffset(dyld_private_addr, .arithmetic);
689 try writer.writeIntLittle(u32, aarch64.Instruction.add(.x17, .x17, off, false).toU32());
690 }
691 try writer.writeIntLittle(u32, aarch64.Instruction.stp(
692 .x16,
693 .x17,
694 aarch64.Register.sp,
695 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
696 ).toU32());
697 {
698 const pages = Atom.calcNumberOfPages(source_addr + 12, dyld_stub_binder_got_addr);
699 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
700 }
701 {
702 const off = try Atom.calcPageOffset(dyld_stub_binder_got_addr, .load_store_64);
703 try writer.writeIntLittle(u32, aarch64.Instruction.ldr(
704 .x16,
705 .x16,
706 aarch64.Instruction.LoadStoreOffset.imm(off),
707 ).toU32());
708 }
709 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
710 },
711 else => unreachable,
712 }
713 }676 }
714677
715 pub fn createStubHelperAtom(self: *Zld) !AtomIndex {678 pub fn createStubHelperAtom(self: *Zld) !AtomIndex {
716 const cpu_arch = self.options.target.cpu.arch;679 const cpu_arch = self.options.target.cpu.arch;
717 const stub_size: u4 = switch (cpu_arch) {680 const stub_size = stub_helpers.calcStubHelperEntrySize(cpu_arch);
718 .x86_64 => 10,
719 .aarch64 => 3 * @sizeOf(u32),
720 else => unreachable,
721 };
722 const alignment: u2 = switch (cpu_arch) {681 const alignment: u2 = switch (cpu_arch) {
723 .x86_64 => 0,682 .x86_64 => 0,
724 .aarch64 => 2,683 .aarch64 => 2,
...@@ -749,32 +708,11 @@ pub const Zld = struct {...@@ -749,32 +708,11 @@ pub const Zld = struct {
749 const sym = self.getSymbol(.{ .sym_index = self.stub_helper_preamble_sym_index.? });708 const sym = self.getSymbol(.{ .sym_index = self.stub_helper_preamble_sym_index.? });
750 break :blk sym.n_value;709 break :blk sym.n_value;
751 };710 };
752 switch (cpu_arch) {711 try stub_helpers.writeStubHelperCode(.{
753 .x86_64 => {712 .cpu_arch = cpu_arch,
754 try writer.writeAll(&.{ 0x68, 0x0, 0x0, 0x0, 0x0, 0xe9 });713 .source_addr = source_addr,
755 {714 .target_addr = target_addr,
756 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 6, target_addr, 0);715 }, writer);
757 try writer.writeIntLittle(i32, disp);
758 }
759 },
760 .aarch64 => {
761 const stub_size: u4 = 3 * @sizeOf(u32);
762 const literal = blk: {
763 const div_res = try math.divExact(u64, stub_size - @sizeOf(u32), 4);
764 break :blk math.cast(u18, div_res) orelse return error.Overflow;
765 };
766 try writer.writeIntLittle(u32, aarch64.Instruction.ldrLiteral(
767 .w16,
768 literal,
769 ).toU32());
770 {
771 const disp = try Atom.calcPcRelativeDisplacementArm64(source_addr + 4, target_addr);
772 try writer.writeIntLittle(u32, aarch64.Instruction.b(disp).toU32());
773 }
774 try writer.writeAll(&.{ 0x0, 0x0, 0x0, 0x0 });
775 },
776 else => unreachable,
777 }
778 }716 }
779717
780 pub fn createLazyPointerAtom(self: *Zld) !AtomIndex {718 pub fn createLazyPointerAtom(self: *Zld) !AtomIndex {
...@@ -819,11 +757,7 @@ pub const Zld = struct {...@@ -819,11 +757,7 @@ pub const Zld = struct {
819 .aarch64 => 2,757 .aarch64 => 2,
820 else => unreachable, // unhandled architecture type758 else => unreachable, // unhandled architecture type
821 };759 };
822 const stub_size: u4 = switch (cpu_arch) {760 const stub_size = stub_helpers.calcStubEntrySize(cpu_arch);
823 .x86_64 => 6,
824 .aarch64 => 3 * @sizeOf(u32),
825 else => unreachable, // unhandled architecture type
826 };
827 const sym_index = try self.allocateSymbol();761 const sym_index = try self.allocateSymbol();
828 const atom_index = try self.createEmptyAtom(sym_index, stub_size, alignment);762 const atom_index = try self.createEmptyAtom(sym_index, stub_size, alignment);
829 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });763 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
...@@ -863,31 +797,11 @@ pub const Zld = struct {...@@ -863,31 +797,11 @@ pub const Zld = struct {
863 const sym = self.getSymbol(atom.getSymbolWithLoc());797 const sym = self.getSymbol(atom.getSymbolWithLoc());
864 break :blk sym.n_value;798 break :blk sym.n_value;
865 };799 };
866 switch (cpu_arch) {800 try stub_helpers.writeStubCode(.{
867 .x86_64 => {801 .cpu_arch = cpu_arch,
868 try writer.writeAll(&.{ 0xff, 0x25 });802 .source_addr = source_addr,
869 {803 .target_addr = target_addr,
870 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 2, target_addr, 0);804 }, writer);
871 try writer.writeIntLittle(i32, disp);
872 }
873 },
874 .aarch64 => {
875 {
876 const pages = Atom.calcNumberOfPages(source_addr, target_addr);
877 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
878 }
879 {
880 const off = try Atom.calcPageOffset(target_addr, .load_store_64);
881 try writer.writeIntLittle(u32, aarch64.Instruction.ldr(
882 .x16,
883 .x16,
884 aarch64.Instruction.LoadStoreOffset.imm(off),
885 ).toU32());
886 }
887 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
888 },
889 else => unreachable,
890 }
891 }805 }
892806
893 fn createTentativeDefAtoms(self: *Zld) !void {807 fn createTentativeDefAtoms(self: *Zld) !void {
...@@ -2267,11 +2181,7 @@ pub const Zld = struct {...@@ -2267,11 +2181,7 @@ pub const Zld = struct {
2267 assert(self.stub_helper_preamble_sym_index != null);2181 assert(self.stub_helper_preamble_sym_index != null);
22682182
2269 const section = self.sections.get(stub_helper_section_index);2183 const section = self.sections.get(stub_helper_section_index);
2270 const stub_offset: u4 = switch (self.options.target.cpu.arch) {2184 const stub_offset = stub_helpers.calcStubOffsetInStubHelper(self.options.target.cpu.arch);
2271 .x86_64 => 1,
2272 .aarch64 => 2 * @sizeOf(u32),
2273 else => unreachable,
2274 };
2275 const header = section.header;2185 const header = section.header;
2276 var atom_index = section.first_atom_index;2186 var atom_index = section.first_atom_index;
2277 atom_index = self.getAtom(atom_index).next_index.?; // skip preamble2187 atom_index = self.getAtom(atom_index).next_index.?; // skip preamble
src/link/table_section.zig created+65
...@@ -0,0 +1,65 @@
1pub fn TableSection(comptime Entry: type) type {
2 return struct {
3 entries: std.ArrayListUnmanaged(Entry) = .{},
4 free_list: std.ArrayListUnmanaged(Index) = .{},
5 lookup: std.AutoHashMapUnmanaged(Entry, Index) = .{},
6
7 pub fn deinit(self: *Self, allocator: Allocator) void {
8 self.entries.deinit(allocator);
9 self.free_list.deinit(allocator);
10 self.lookup.deinit(allocator);
11 }
12
13 pub fn allocateEntry(self: *Self, allocator: Allocator, entry: Entry) Allocator.Error!Index {
14 try self.entries.ensureUnusedCapacity(allocator, 1);
15 const index = blk: {
16 if (self.free_list.popOrNull()) |index| {
17 log.debug(" (reusing entry index {d})", .{index});
18 break :blk index;
19 } else {
20 log.debug(" (allocating entry at index {d})", .{self.entries.items.len});
21 const index = @intCast(u32, self.entries.items.len);
22 _ = self.entries.addOneAssumeCapacity();
23 break :blk index;
24 }
25 };
26 self.entries.items[index] = entry;
27 try self.lookup.putNoClobber(allocator, entry, index);
28 return index;
29 }
30
31 pub fn freeEntry(self: *Self, allocator: Allocator, entry: Entry) void {
32 const index = self.lookup.get(entry) orelse return;
33 self.free_list.append(allocator, index) catch {};
34 self.entries.items[index] = undefined;
35 _ = self.lookup.remove(entry);
36 }
37
38 pub fn count(self: Self) usize {
39 return self.entries.items.len;
40 }
41
42 pub fn format(
43 self: Self,
44 comptime unused_format_string: []const u8,
45 options: std.fmt.FormatOptions,
46 writer: anytype,
47 ) !void {
48 _ = options;
49 comptime assert(unused_format_string.len == 0);
50 try writer.writeAll("TableSection:\n");
51 for (self.entries.items, 0..) |entry, i| {
52 try writer.print(" {d} => {}\n", .{ i, entry });
53 }
54 }
55
56 const Self = @This();
57 pub const Index = u32;
58 };
59}
60
61const std = @import("std");
62const assert = std.debug.assert;
63const log = std.log.scoped(.link);
64
65const Allocator = std.mem.Allocator;