authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-09-11 01:18:17+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-09-18 10:00:04+02:00
log53bd7bd044fccc70699b04d9ae37151423f70165
tree944ab4ac3c715b1ada6e4e804d5fefead89305df
parent34f9360ea20228b895df10b3950c72f40efb6843

macho: move to incremental writes and global relocs for incremental


8 files changed, 1326 insertions(+), 647 deletions(-)

src/arch/aarch64/Emit.zig+11-14
...@@ -680,16 +680,15 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -680,16 +680,15 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {
680 break :blk offset;680 break :blk offset;
681 };681 };
682 // Add relocation to the decl.682 // Add relocation to the decl.
683 const atom = macho_file.atom_by_index_table.get(relocation.atom_index).?;683 const atom = macho_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
684 const target = macho_file.getGlobalByIndex(relocation.sym_index);684 const target = macho_file.getGlobalByIndex(relocation.sym_index);
685 try atom.relocs.append(emit.bin_file.allocator, .{685 try atom.addRelocation(macho_file, .{
686 .offset = offset,686 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
687 .target = target,687 .target = target,
688 .offset = offset,
688 .addend = 0,689 .addend = 0,
689 .subtractor = null,
690 .pcrel = true,690 .pcrel = true,
691 .length = 2,691 .length = 2,
692 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
693 });692 });
694 } else {693 } else {
695 return emit.fail("Implement call_extern for linking backends != MachO", .{});694 return emit.fail("Implement call_extern for linking backends != MachO", .{});
...@@ -882,13 +881,13 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -882,13 +881,13 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
882 }881 }
883882
884 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {883 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
885 const atom = macho_file.atom_by_index_table.get(data.atom_index).?;884 const atom = macho_file.getAtomForSymbol(.{ .sym_index = data.atom_index, .file = null }).?;
886 // Page reloc for adrp instruction.885 // TODO this causes segfault in stage1
887 try atom.relocs.append(emit.bin_file.allocator, .{886 // try atom.addRelocations(macho_file, 2, .{
888 .offset = offset,887 try atom.addRelocation(macho_file, .{
889 .target = .{ .sym_index = data.sym_index, .file = null },888 .target = .{ .sym_index = data.sym_index, .file = null },
889 .offset = offset,
890 .addend = 0,890 .addend = 0,
891 .subtractor = null,
892 .pcrel = true,891 .pcrel = true,
893 .length = 2,892 .length = 2,
894 .@"type" = switch (tag) {893 .@"type" = switch (tag) {
...@@ -901,12 +900,10 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -901,12 +900,10 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
901 else => unreachable,900 else => unreachable,
902 },901 },
903 });902 });
904 // Pageoff reloc for adrp instruction.903 try atom.addRelocation(macho_file, .{
905 try atom.relocs.append(emit.bin_file.allocator, .{
906 .offset = offset + 4,
907 .target = .{ .sym_index = data.sym_index, .file = null },904 .target = .{ .sym_index = data.sym_index, .file = null },
905 .offset = offset + 4,
908 .addend = 0,906 .addend = 0,
909 .subtractor = null,
910 .pcrel = false,907 .pcrel = false,
911 .length = 2,908 .length = 2,
912 .@"type" = switch (tag) {909 .@"type" = switch (tag) {
src/arch/x86_64/Emit.zig+10-14
...@@ -996,7 +996,6 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -996,7 +996,6 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
996 );996 );
997997
998 const end_offset = emit.code.items.len;998 const end_offset = emit.code.items.len;
999 const gpa = emit.bin_file.allocator;
1000999
1001 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {1000 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
1002 const reloc_type = switch (ops.flags) {1001 const reloc_type = switch (ops.flags) {
...@@ -1004,19 +1003,17 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -1004,19 +1003,17 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1004 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),1003 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1005 else => unreachable,1004 else => unreachable,
1006 };1005 };
1007 const atom = macho_file.atom_by_index_table.get(relocation.atom_index).?;1006 const atom = macho_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
1008 log.debug("adding reloc of type {} to local @{d}", .{ reloc_type, relocation.sym_index });1007 try atom.addRelocation(macho_file, .{
1009 try atom.relocs.append(gpa, .{1008 .@"type" = reloc_type,
1010 .offset = @intCast(u32, end_offset - 4),
1011 .target = .{ .sym_index = relocation.sym_index, .file = null },1009 .target = .{ .sym_index = relocation.sym_index, .file = null },
1010 .offset = @intCast(u32, end_offset - 4),
1012 .addend = 0,1011 .addend = 0,
1013 .subtractor = null,
1014 .pcrel = true,1012 .pcrel = true,
1015 .length = 2,1013 .length = 2,
1016 .@"type" = reloc_type,
1017 });1014 });
1018 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {1015 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
1019 const atom = coff_file.atom_by_index_table.get(relocation.atom_index).?;1016 const atom = coff_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
1020 try atom.addRelocation(coff_file, .{1017 try atom.addRelocation(coff_file, .{
1021 .@"type" = switch (ops.flags) {1018 .@"type" = switch (ops.flags) {
1022 0b00 => .got,1019 0b00 => .got,
...@@ -1145,20 +1142,19 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -1145,20 +1142,19 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
11451142
1146 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {1143 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
1147 // Add relocation to the decl.1144 // Add relocation to the decl.
1148 const atom = macho_file.atom_by_index_table.get(relocation.atom_index).?;1145 const atom = macho_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
1149 const target = macho_file.getGlobalByIndex(relocation.sym_index);1146 const target = macho_file.getGlobalByIndex(relocation.sym_index);
1150 try atom.relocs.append(emit.bin_file.allocator, .{1147 try atom.addRelocation(macho_file, .{
1151 .offset = offset,1148 .@"type" = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1152 .target = target,1149 .target = target,
1150 .offset = offset,
1153 .addend = 0,1151 .addend = 0,
1154 .subtractor = null,
1155 .pcrel = true,1152 .pcrel = true,
1156 .length = 2,1153 .length = 2,
1157 .@"type" = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1158 });1154 });
1159 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {1155 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
1160 // Add relocation to the decl.1156 // Add relocation to the decl.
1161 const atom = coff_file.atom_by_index_table.get(relocation.atom_index).?;1157 const atom = coff_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
1162 const target = coff_file.getGlobalByIndex(relocation.sym_index);1158 const target = coff_file.getGlobalByIndex(relocation.sym_index);
1163 try atom.addRelocation(coff_file, .{1159 try atom.addRelocation(coff_file, .{
1164 .@"type" = .direct,1160 .@"type" = .direct,
src/link/Coff.zig+1-1
...@@ -1527,7 +1527,7 @@ pub fn getDeclVAddr(...@@ -1527,7 +1527,7 @@ pub fn getDeclVAddr(
1527 assert(self.llvm_object == null);1527 assert(self.llvm_object == null);
1528 assert(decl.link.coff.sym_index != 0);1528 assert(decl.link.coff.sym_index != 0);
15291529
1530 const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?;1530 const atom = self.getAtomForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;
1531 const target = SymbolWithLoc{ .sym_index = decl.link.coff.sym_index, .file = null };1531 const target = SymbolWithLoc{ .sym_index = decl.link.coff.sym_index, .file = null };
1532 try atom.addRelocation(self, .{1532 try atom.addRelocation(self, .{
1533 .@"type" = .direct,1533 .@"type" = .direct,
src/link/MachO.zig+781-486
...@@ -40,7 +40,6 @@ const Liveness = @import("../Liveness.zig");...@@ -40,7 +40,6 @@ const Liveness = @import("../Liveness.zig");
40const LlvmObject = @import("../codegen/llvm.zig").Object;40const LlvmObject = @import("../codegen/llvm.zig").Object;
41const Module = @import("../Module.zig");41const Module = @import("../Module.zig");
42const Relocation = @import("MachO/Relocation.zig");42const Relocation = @import("MachO/Relocation.zig");
43const RelocationTable = Relocation.Table;
44const StringTable = @import("strtab.zig").StringTable;43const StringTable = @import("strtab.zig").StringTable;
45const Trie = @import("MachO/Trie.zig");44const Trie = @import("MachO/Trie.zig");
46const Type = @import("../type.zig").Type;45const Type = @import("../type.zig").Type;
...@@ -196,6 +195,21 @@ unnamed_const_atoms: UnnamedConstTable = .{},...@@ -196,6 +195,21 @@ unnamed_const_atoms: UnnamedConstTable = .{},
196/// this will be a table indexed by index into the list of Atoms.195/// this will be a table indexed by index into the list of Atoms.
197relocs: RelocationTable = .{},196relocs: RelocationTable = .{},
198197
198/// A table of rebases indexed by the owning them `Atom`.
199/// Note that once we refactor `Atom`'s lifetime and ownership rules,
200/// this will be a table indexed by index into the list of Atoms.
201rebases: RebaseTable = .{},
202
203/// A table of bindings indexed by the owning them `Atom`.
204/// Note that once we refactor `Atom`'s lifetime and ownership rules,
205/// this will be a table indexed by index into the list of Atoms.
206bindings: BindingTable = .{},
207
208/// A table of lazy bindings indexed by the owning them `Atom`.
209/// Note that once we refactor `Atom`'s lifetime and ownership rules,
210/// this will be a table indexed by index into the list of Atoms.
211lazy_bindings: BindingTable = .{},
212
199/// Table of Decls that are currently alive.213/// Table of Decls that are currently alive.
200/// We store them here so that we can properly dispose of any allocated214/// We store them here so that we can properly dispose of any allocated
201/// memory within the atom in the incremental linker.215/// memory within the atom in the incremental linker.
...@@ -215,8 +229,8 @@ const Entry = struct {...@@ -215,8 +229,8 @@ const Entry = struct {
215 return macho_file.getSymbolPtr(.{ .sym_index = entry.sym_index, .file = null });229 return macho_file.getSymbolPtr(.{ .sym_index = entry.sym_index, .file = null });
216 }230 }
217231
218 pub fn getAtom(entry: Entry, macho_file: *MachO) *Atom {232 pub fn getAtom(entry: Entry, macho_file: *MachO) ?*Atom {
219 return macho_file.getAtomForSymbol(.{ .sym_index = entry.sym_index, .file = null }).?;233 return macho_file.getAtomForSymbol(.{ .sym_index = entry.sym_index, .file = null });
220 }234 }
221235
222 pub fn getName(entry: Entry, macho_file: *MachO) []const u8 {236 pub fn getName(entry: Entry, macho_file: *MachO) []const u8 {
...@@ -224,7 +238,10 @@ const Entry = struct {...@@ -224,7 +238,10 @@ const Entry = struct {
224 }238 }
225};239};
226240
241const BindingTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Atom.Binding));
227const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom));242const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom));
243const RebaseTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(u32));
244const RelocationTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Relocation));
228245
229const PendingUpdate = union(enum) {246const PendingUpdate = union(enum) {
230 resolve_undef: u32,247 resolve_undef: u32,
...@@ -238,6 +255,16 @@ pub const SymbolWithLoc = struct {...@@ -238,6 +255,16 @@ pub const SymbolWithLoc = struct {
238255
239 // null means it's a synthetic global.256 // null means it's a synthetic global.
240 file: ?u32 = null,257 file: ?u32 = null,
258
259 pub fn eql(this: SymbolWithLoc, other: SymbolWithLoc) bool {
260 if (this.file == null and other.file == null) {
261 return this.sym_index == other.sym_index;
262 }
263 if (this.file != null and other.file != null) {
264 return this.sym_index == other.sym_index and this.file.? == other.file.?;
265 }
266 return false;
267 }
241};268};
242269
243/// When allocating, the ideal_capacity is calculated by270/// When allocating, the ideal_capacity is calculated by
...@@ -436,7 +463,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -436,7 +463,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
436 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);463 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
437 try self.resolveLibSystem(arena, comp, &.{}, &libs);464 try self.resolveLibSystem(arena, comp, &.{}, &libs);
438465
439 const id_symlink_basename = "zld.id";466 const id_symlink_basename = "link.id";
440467
441 const cache_dir_handle = module.zig_cache_artifact_directory.handle;468 const cache_dir_handle = module.zig_cache_artifact_directory.handle;
442 var man: Cache.Manifest = undefined;469 var man: Cache.Manifest = undefined;
...@@ -517,14 +544,19 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -517,14 +544,19 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
517544
518 try self.allocateSpecialSymbols();545 try self.allocateSpecialSymbols();
519546
547 {
548 var it = self.relocs.keyIterator();
549 while (it.next()) |atom| {
550 try atom.*.resolveRelocations(self);
551 }
552 }
553
520 if (build_options.enable_logging) {554 if (build_options.enable_logging) {
521 self.logSymtab();555 self.logSymtab();
522 self.logSections();556 self.logSections();
523 self.logAtoms();557 self.logAtoms();
524 }558 }
525559
526 try self.writeAtoms();
527
528 var lc_buffer = std.ArrayList(u8).init(arena);560 var lc_buffer = std.ArrayList(u8).init(arena);
529 const lc_writer = lc_buffer.writer();561 const lc_writer = lc_buffer.writer();
530 var ncmds: u32 = 0;562 var ncmds: u32 = 0;
...@@ -1179,84 +1211,49 @@ pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32...@@ -1179,84 +1211,49 @@ pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32
1179 return atom;1211 return atom;
1180}1212}
11811213
1182pub fn writeAtom(self: *MachO, atom: *Atom) !void {1214pub fn writeAtom(self: *MachO, atom: *Atom, code: []const u8) !void {
1215 // TODO: temporary sanity check
1216 assert(atom.code.items.len == 0);
1217 assert(atom.relocs.items.len == 0);
1218 assert(atom.rebases.items.len == 0);
1219 assert(atom.bindings.items.len == 0);
1220 assert(atom.lazy_bindings.items.len == 0);
1221
1183 const sym = atom.getSymbol(self);1222 const sym = atom.getSymbol(self);
1184 const section = self.sections.get(sym.n_sect - 1);1223 const section = self.sections.get(sym.n_sect - 1);
1185 const file_offset = section.header.offset + sym.n_value - section.header.addr;1224 const file_offset = section.header.offset + sym.n_value - section.header.addr;
1186 try atom.resolveRelocs(self);
1187 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });1225 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });
1188 try self.base.file.?.pwriteAll(atom.code.items, file_offset);1226 try self.base.file.?.pwriteAll(code, file_offset);
1227 try atom.resolveRelocations(self);
1189}1228}
11901229
1191// fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {1230fn writePtrWidthAtom(self: *MachO, atom: *Atom) !void {
1192// // TODO: reverse-lookup might come in handy here1231 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
1193// var it = self.relocs.valueIterator();1232 try self.writeAtom(atom, &buffer);
1194// while (it.next()) |relocs| {1233}
1195// for (relocs.items) |*reloc| {
1196// if (!reloc.target.eql(target)) continue;
1197// reloc.dirty = true;
1198// }
1199// }
1200// }
1201
1202// fn markRelocsDirtyByAddress(self: *MachO, addr: u32) void {
1203// var it = self.relocs.valueIterator();
1204// while (it.next()) |relocs| {
1205// for (relocs.items) |*reloc| {
1206// const target_atom = reloc.getTargetAtom(self) orelse continue;
1207// const target_sym = target_atom.getSymbol(self);
1208// if (target_sym.value < addr) continue;
1209// reloc.dirty = true;
1210// }
1211// }
1212// }
1213
1214// fn resolveRelocs(self: *MachO, atom: *Atom) !void {
1215// const relocs = self.relocs.get(atom) orelse return;
1216// const source_sym = atom.getSymbol(self);
1217// const source_section = self.sections.get(@enumToInt(source_sym.section_number) - 1).header;
1218// const file_offset = section.offset + source_sym.n_value - section.addr;
1219
1220// log.debug("relocating '{s}'", .{atom.getName(self)});
1221
1222// for (relocs.items) |*reloc| {
1223// if (!reloc.dirty) continue;
1224
1225// const target_atom = reloc.getTargetAtom(self) orelse continue;
1226// const target_vaddr = target_atom.getSymbol(self).value;
1227// const target_vaddr_with_addend = target_vaddr + reloc.addend;
1228
1229// log.debug(" ({x}: [() => 0x{x} ({s})) ({s}) (in file at 0x{x})", .{
1230// source_sym.value + reloc.offset,
1231// target_vaddr_with_addend,
1232// self.getSymbolName(reloc.target),
1233// @tagName(reloc.@"type"),
1234// file_offset + reloc.offset,
1235// });
1236
1237// reloc.dirty = false;
12381234
1239// if (reloc.pcrel) {1235fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {
1240// const source_vaddr = source_sym.value + reloc.offset;1236 // TODO: reverse-lookup might come in handy here
1241// const disp =1237 var it = self.relocs.valueIterator();
1242// @intCast(i32, target_vaddr_with_addend) - @intCast(i32, source_vaddr) - 4;1238 while (it.next()) |relocs| {
1243// try self.base.file.?.pwriteAll(mem.asBytes(&disp), file_offset + reloc.offset);1239 for (relocs.items) |*reloc| {
1244// continue;1240 if (!reloc.target.eql(target)) continue;
1245// }1241 reloc.dirty = true;
1242 }
1243 }
1244}
12461245
1247// switch (reloc.length) {1246fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void {
1248// 2 => try self.base.file.?.pwriteAll(1247 var it = self.relocs.valueIterator();
1249// mem.asBytes(&@truncate(u32, target_vaddr_with_addend)),1248 while (it.next()) |relocs| {
1250// file_offset + reloc.offset,1249 for (relocs.items) |*reloc| {
1251// ),1250 const target_atom = reloc.getTargetAtom(self) orelse continue;
1252// 3 => try self.base.file.?.pwriteAll(1251 const target_sym = target_atom.getSymbol(self);
1253// mem.asBytes(&(target_vaddr_with_addend)),1252 if (target_sym.n_value < addr) continue;
1254// file_offset + reloc.offset,1253 reloc.dirty = true;
1255// ),1254 }
1256// else => unreachable,1255 }
1257// }1256}
1258// }
1259// }
12601257
1261pub fn allocateSpecialSymbols(self: *MachO) !void {1258pub fn allocateSpecialSymbols(self: *MachO) !void {
1262 for (&[_][]const u8{1259 for (&[_][]const u8{
...@@ -1277,74 +1274,92 @@ pub fn allocateSpecialSymbols(self: *MachO) !void {...@@ -1277,74 +1274,92 @@ pub fn allocateSpecialSymbols(self: *MachO) !void {
1277 }1274 }
1278}1275}
12791276
1280fn writeAtoms(self: *MachO) !void {
1281 assert(self.mode == .incremental);
1282
1283 const slice = self.sections.slice();
1284 for (slice.items(.last_atom)) |last, i| {
1285 var atom: *Atom = last orelse continue;
1286 const sect_i = @intCast(u8, i);
1287 const header = slice.items(.header)[sect_i];
1288
1289 if (header.isZerofill()) continue;
1290
1291 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
1292
1293 while (true) {
1294 if (atom.dirty) {
1295 try self.writeAtom(atom);
1296 atom.dirty = false;
1297 }
1298
1299 if (atom.prev) |prev| {
1300 atom = prev;
1301 } else break;
1302 }
1303 }
1304}
1305
1306pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {1277pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
1307 const gpa = self.base.allocator;1278 const gpa = self.base.allocator;
1279
1308 const sym_index = try self.allocateSymbol();1280 const sym_index = try self.allocateSymbol();
1309 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);1281 const atom = switch (self.mode) {
1282 .incremental => blk: {
1283 const atom = try gpa.create(Atom);
1284 atom.* = Atom.empty;
1285 atom.sym_index = sym_index;
1286 atom.size = @sizeOf(u64);
1287 atom.alignment = 3;
1288 break :blk atom;
1289 },
1290 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3),
1291 };
1292 errdefer gpa.destroy(atom);
1293
1294 try self.managed_atoms.append(gpa, atom);
1295 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
1296
1310 const sym = atom.getSymbolPtr(self);1297 const sym = atom.getSymbolPtr(self);
1311 sym.n_type = macho.N_SECT;1298 sym.n_type = macho.N_SECT;
1312 sym.n_sect = self.got_section_index.? + 1;1299 sym.n_sect = self.got_section_index.? + 1;
13131300
1314 try atom.relocs.append(gpa, .{1301 if (self.mode == .incremental) {
1315 .offset = 0,1302 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1316 .target = target,
1317 .addend = 0,
1318 .subtractor = null,
1319 .pcrel = false,
1320 .length = 3,
1321 .@"type" = switch (self.base.options.target.cpu.arch) {
1322 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1323 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1324 else => unreachable,
1325 },
1326 });
13271303
1328 const target_sym = self.getSymbol(target);1304 log.debug("allocated GOT atom at 0x{x}", .{sym.n_value});
1329 if (target_sym.undf()) {1305
1330 const global = self.getGlobal(self.getSymbolName(target)).?;1306 try atom.addRelocation(self, .{
1331 try atom.bindings.append(gpa, .{1307 .@"type" = switch (self.base.options.target.cpu.arch) {
1332 .target = global,1308 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1309 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1310 else => unreachable,
1311 },
1312 .target = target,
1333 .offset = 0,1313 .offset = 0,
1314 .addend = 0,
1315 .pcrel = false,
1316 .length = 3,
1334 });1317 });
1318
1319 const target_sym = self.getSymbol(target);
1320 if (target_sym.undf()) {
1321 try atom.addBinding(self, .{
1322 .target = self.getGlobal(self.getSymbolName(target)).?,
1323 .offset = 0,
1324 });
1325 } else {
1326 try atom.addRebase(self, 0);
1327 }
1335 } else {1328 } else {
1336 try atom.rebases.append(gpa, 0);1329 try atom.relocs.append(gpa, .{
1337 }1330 .offset = 0,
1331 .target = target,
1332 .addend = 0,
1333 .subtractor = null,
1334 .pcrel = false,
1335 .length = 3,
1336 .@"type" = switch (self.base.options.target.cpu.arch) {
1337 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1338 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1339 else => unreachable,
1340 },
1341 });
13381342
1339 try self.managed_atoms.append(gpa, atom);1343 const target_sym = self.getSymbol(target);
1340 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);1344 if (target_sym.undf()) {
1345 const global = self.getGlobal(self.getSymbolName(target)).?;
1346 try atom.bindings.append(gpa, .{
1347 .target = global,
1348 .offset = 0,
1349 });
1350 } else {
1351 try atom.rebases.append(gpa, 0);
1352 }
13411353
1342 try self.allocateAtomCommon(atom);1354 try self.addAtomToSection(atom);
1355 }
13431356
1344 return atom;1357 return atom;
1345}1358}
13461359
1347pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {1360pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
1361 assert(self.mode == .one_shot);
1362
1348 const gpa = self.base.allocator;1363 const gpa = self.base.allocator;
1349 const sym_index = try self.allocateSymbol();1364 const sym_index = try self.allocateSymbol();
1350 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);1365 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
...@@ -1368,14 +1383,9 @@ pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {...@@ -1368,14 +1383,9 @@ pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
1368 .sectname = makeStaticString("__thread_ptrs"),1383 .sectname = makeStaticString("__thread_ptrs"),
1369 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,1384 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
1370 })).?;1385 })).?;
1371 if (self.mode == .incremental and !gop.found_existing) {
1372 // TODO allocate section
1373 const needed_size: u64 = self.page_size;
1374 try self.allocateSection(gop.sect_id, needed_size, @alignOf(u64));
1375 }
1376 sym.n_sect = gop.sect_id + 1;1386 sym.n_sect = gop.sect_id + 1;
13771387
1378 try self.allocateAtomCommon(atom);1388 try self.addAtomToSection(atom);
13791389
1380 return atom;1390 return atom;
1381}1391}
...@@ -1385,17 +1395,36 @@ pub fn createDyldPrivateAtom(self: *MachO) !void {...@@ -1385,17 +1395,36 @@ pub fn createDyldPrivateAtom(self: *MachO) !void {
1385 if (self.dyld_private_atom != null) return;1395 if (self.dyld_private_atom != null) return;
13861396
1387 const gpa = self.base.allocator;1397 const gpa = self.base.allocator;
1398
1388 const sym_index = try self.allocateSymbol();1399 const sym_index = try self.allocateSymbol();
1389 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);1400 const atom = switch (self.mode) {
1401 .incremental => blk: {
1402 const atom = try gpa.create(Atom);
1403 atom.* = Atom.empty;
1404 atom.sym_index = sym_index;
1405 atom.size = @sizeOf(u64);
1406 atom.alignment = 3;
1407 break :blk atom;
1408 },
1409 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3),
1410 };
1411 errdefer gpa.destroy(atom);
1412
1390 const sym = atom.getSymbolPtr(self);1413 const sym = atom.getSymbolPtr(self);
1391 sym.n_type = macho.N_SECT;1414 sym.n_type = macho.N_SECT;
1392 sym.n_sect = self.data_section_index.? + 1;1415 sym.n_sect = self.data_section_index.? + 1;
1393 self.dyld_private_atom = atom;1416 self.dyld_private_atom = atom;
13941417
1395 try self.allocateAtomCommon(atom);
1396
1397 try self.managed_atoms.append(gpa, atom);1418 try self.managed_atoms.append(gpa, atom);
1398 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);1419 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
1420
1421 if (self.mode == .incremental) {
1422 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1423 log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});
1424 try self.writePtrWidthAtom(atom);
1425 } else {
1426 try self.addAtomToSection(atom);
1427 }
1399}1428}
14001429
1401pub fn createStubHelperPreambleAtom(self: *MachO) !void {1430pub fn createStubHelperPreambleAtom(self: *MachO) !void {
...@@ -1415,118 +1444,196 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -1415,118 +1444,196 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
1415 else => unreachable,1444 else => unreachable,
1416 };1445 };
1417 const sym_index = try self.allocateSymbol();1446 const sym_index = try self.allocateSymbol();
1418 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);1447 const atom = switch (self.mode) {
1448 .incremental => blk: {
1449 const atom = try gpa.create(Atom);
1450 atom.* = Atom.empty;
1451 atom.sym_index = sym_index;
1452 atom.size = size;
1453 atom.alignment = alignment;
1454 break :blk atom;
1455 },
1456 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, size, alignment),
1457 };
1458 errdefer gpa.destroy(atom);
1459
1419 const sym = atom.getSymbolPtr(self);1460 const sym = atom.getSymbolPtr(self);
1420 sym.n_type = macho.N_SECT;1461 sym.n_type = macho.N_SECT;
1421 sym.n_sect = self.stub_helper_section_index.? + 1;1462 sym.n_sect = self.stub_helper_section_index.? + 1;
14221463
1423 const dyld_private_sym_index = self.dyld_private_atom.?.sym_index;1464 const dyld_private_sym_index = self.dyld_private_atom.?.sym_index;
1465
1466 const code = try gpa.alloc(u8, size);
1467 defer gpa.free(code);
1468 mem.set(u8, code, 0);
1469
1424 switch (arch) {1470 switch (arch) {
1425 .x86_64 => {1471 .x86_64 => {
1426 try atom.relocs.ensureUnusedCapacity(self.base.allocator, 2);
1427 // lea %r11, [rip + disp]1472 // lea %r11, [rip + disp]
1428 atom.code.items[0] = 0x4c;1473 code[0] = 0x4c;
1429 atom.code.items[1] = 0x8d;1474 code[1] = 0x8d;
1430 atom.code.items[2] = 0x1d;1475 code[2] = 0x1d;
1431 atom.relocs.appendAssumeCapacity(.{
1432 .offset = 3,
1433 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1434 .addend = 0,
1435 .subtractor = null,
1436 .pcrel = true,
1437 .length = 2,
1438 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1439 });
1440 // push %r111476 // push %r11
1441 atom.code.items[7] = 0x41;1477 code[7] = 0x41;
1442 atom.code.items[8] = 0x53;1478 code[8] = 0x53;
1443 // jmp [rip + disp]1479 // jmp [rip + disp]
1444 atom.code.items[9] = 0xff;1480 code[9] = 0xff;
1445 atom.code.items[10] = 0x25;1481 code[10] = 0x25;
1446 atom.relocs.appendAssumeCapacity(.{1482
1447 .offset = 11,1483 if (self.mode == .incremental) {
1448 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },1484 try atom.addRelocations(self, 2, .{ .{
1449 .addend = 0,1485 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1450 .subtractor = null,1486 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1451 .pcrel = true,1487 .offset = 3,
1452 .length = 2,1488 .addend = 0,
1453 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_GOT),1489 .pcrel = true,
1454 });1490 .length = 2,
1491 }, .{
1492 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_GOT),
1493 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1494 .offset = 11,
1495 .addend = 0,
1496 .pcrel = true,
1497 .length = 2,
1498 } });
1499 } else {
1500 try atom.relocs.ensureUnusedCapacity(self.base.allocator, 2);
1501 atom.relocs.appendAssumeCapacity(.{
1502 .offset = 3,
1503 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1504 .addend = 0,
1505 .subtractor = null,
1506 .pcrel = true,
1507 .length = 2,
1508 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1509 });
1510 atom.relocs.appendAssumeCapacity(.{
1511 .offset = 11,
1512 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1513 .addend = 0,
1514 .subtractor = null,
1515 .pcrel = true,
1516 .length = 2,
1517 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_GOT),
1518 });
1519 }
1455 },1520 },
1521
1456 .aarch64 => {1522 .aarch64 => {
1457 try atom.relocs.ensureUnusedCapacity(self.base.allocator, 4);
1458 // adrp x17, 01523 // adrp x17, 0
1459 mem.writeIntLittle(u32, atom.code.items[0..][0..4], aarch64.Instruction.adrp(.x17, 0).toU32());1524 mem.writeIntLittle(u32, code[0..][0..4], aarch64.Instruction.adrp(.x17, 0).toU32());
1460 atom.relocs.appendAssumeCapacity(.{
1461 .offset = 0,
1462 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1463 .addend = 0,
1464 .subtractor = null,
1465 .pcrel = true,
1466 .length = 2,
1467 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1468 });
1469 // add x17, x17, 01525 // add x17, x17, 0
1470 mem.writeIntLittle(u32, atom.code.items[4..][0..4], aarch64.Instruction.add(.x17, .x17, 0, false).toU32());1526 mem.writeIntLittle(u32, code[4..][0..4], aarch64.Instruction.add(.x17, .x17, 0, false).toU32());
1471 atom.relocs.appendAssumeCapacity(.{
1472 .offset = 4,
1473 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1474 .addend = 0,
1475 .subtractor = null,
1476 .pcrel = false,
1477 .length = 2,
1478 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
1479 });
1480 // stp x16, x17, [sp, #-16]!1527 // stp x16, x17, [sp, #-16]!
1481 mem.writeIntLittle(u32, atom.code.items[8..][0..4], aarch64.Instruction.stp(1528 mem.writeIntLittle(u32, code[8..][0..4], aarch64.Instruction.stp(
1482 .x16,1529 .x16,
1483 .x17,1530 .x17,
1484 aarch64.Register.sp,1531 aarch64.Register.sp,
1485 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),1532 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
1486 ).toU32());1533 ).toU32());
1487 // adrp x16, 01534 // adrp x16, 0
1488 mem.writeIntLittle(u32, atom.code.items[12..][0..4], aarch64.Instruction.adrp(.x16, 0).toU32());1535 mem.writeIntLittle(u32, code[12..][0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
1489 atom.relocs.appendAssumeCapacity(.{
1490 .offset = 12,
1491 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1492 .addend = 0,
1493 .subtractor = null,
1494 .pcrel = true,
1495 .length = 2,
1496 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
1497 });
1498 // ldr x16, [x16, 0]1536 // ldr x16, [x16, 0]
1499 mem.writeIntLittle(u32, atom.code.items[16..][0..4], aarch64.Instruction.ldr(1537 mem.writeIntLittle(u32, code[16..][0..4], aarch64.Instruction.ldr(
1500 .x16,1538 .x16,
1501 .x16,1539 .x16,
1502 aarch64.Instruction.LoadStoreOffset.imm(0),1540 aarch64.Instruction.LoadStoreOffset.imm(0),
1503 ).toU32());1541 ).toU32());
1504 atom.relocs.appendAssumeCapacity(.{
1505 .offset = 16,
1506 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1507 .addend = 0,
1508 .subtractor = null,
1509 .pcrel = false,
1510 .length = 2,
1511 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
1512 });
1513 // br x161542 // br x16
1514 mem.writeIntLittle(u32, atom.code.items[20..][0..4], aarch64.Instruction.br(.x16).toU32());1543 mem.writeIntLittle(u32, code[20..][0..4], aarch64.Instruction.br(.x16).toU32());
1544
1545 if (self.mode == .incremental) {
1546 try atom.addRelocations(self, 4, .{ .{
1547 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1548 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1549 .offset = 0,
1550 .addend = 0,
1551 .pcrel = true,
1552 .length = 2,
1553 }, .{
1554 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
1555 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1556 .offset = 4,
1557 .addend = 0,
1558 .pcrel = false,
1559 .length = 2,
1560 }, .{
1561 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
1562 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1563 .offset = 12,
1564 .addend = 0,
1565 .pcrel = true,
1566 .length = 2,
1567 }, .{
1568 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
1569 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1570 .offset = 16,
1571 .addend = 0,
1572 .pcrel = false,
1573 .length = 2,
1574 } });
1575 } else {
1576 try atom.relocs.ensureUnusedCapacity(gpa, 4);
1577 atom.relocs.appendAssumeCapacity(.{
1578 .offset = 0,
1579 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1580 .addend = 0,
1581 .subtractor = null,
1582 .pcrel = true,
1583 .length = 2,
1584 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1585 });
1586 atom.relocs.appendAssumeCapacity(.{
1587 .offset = 4,
1588 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1589 .addend = 0,
1590 .subtractor = null,
1591 .pcrel = false,
1592 .length = 2,
1593 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
1594 });
1595 atom.relocs.appendAssumeCapacity(.{
1596 .offset = 12,
1597 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1598 .addend = 0,
1599 .subtractor = null,
1600 .pcrel = true,
1601 .length = 2,
1602 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
1603 });
1604 atom.relocs.appendAssumeCapacity(.{
1605 .offset = 16,
1606 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1607 .addend = 0,
1608 .subtractor = null,
1609 .pcrel = false,
1610 .length = 2,
1611 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
1612 });
1613 }
1515 },1614 },
1615
1516 else => unreachable,1616 else => unreachable,
1517 }1617 }
1518 self.stub_helper_preamble_atom = atom;1618 self.stub_helper_preamble_atom = atom;
15191619
1520 try self.allocateAtomCommon(atom);
1521
1522 try self.managed_atoms.append(gpa, atom);1620 try self.managed_atoms.append(gpa, atom);
1523 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);1621 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
1622
1623 if (self.mode == .incremental) {
1624 sym.n_value = try self.allocateAtom(atom, size, math.powi(u32, 2, alignment) catch unreachable);
1625 log.debug("allocated stub preamble atom at 0x{x}", .{sym.n_value});
1626 try self.writeAtom(atom, code);
1627 } else {
1628 mem.copy(u8, atom.code.items, code);
1629 try self.addAtomToSection(atom);
1630 }
1524}1631}
15251632
1526pub fn createStubHelperAtom(self: *MachO) !*Atom {1633pub fn createStubHelperAtom(self: *MachO) !*Atom {
1527 const gpa = self.base.allocator;1634 const gpa = self.base.allocator;
1528 const arch = self.base.options.target.cpu.arch;1635 const arch = self.base.options.target.cpu.arch;
1529 const stub_size: u4 = switch (arch) {1636 const size: u4 = switch (arch) {
1530 .x86_64 => 10,1637 .x86_64 => 10,
1531 .aarch64 => 3 * @sizeOf(u32),1638 .aarch64 => 3 * @sizeOf(u32),
1532 else => unreachable,1639 else => unreachable,
...@@ -1537,52 +1644,92 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -1537,52 +1644,92 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
1537 else => unreachable,1644 else => unreachable,
1538 };1645 };
1539 const sym_index = try self.allocateSymbol();1646 const sym_index = try self.allocateSymbol();
1540 const atom = try MachO.createEmptyAtom(gpa, sym_index, stub_size, alignment);1647 const atom = switch (self.mode) {
1648 .incremental => blk: {
1649 const atom = try gpa.create(Atom);
1650 atom.* = Atom.empty;
1651 atom.sym_index = sym_index;
1652 atom.size = size;
1653 atom.alignment = alignment;
1654 break :blk atom;
1655 },
1656 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, size, alignment),
1657 };
1658 errdefer gpa.destroy(atom);
1659
1541 const sym = atom.getSymbolPtr(self);1660 const sym = atom.getSymbolPtr(self);
1542 sym.n_type = macho.N_SECT;1661 sym.n_type = macho.N_SECT;
1543 sym.n_sect = self.stub_helper_section_index.? + 1;1662 sym.n_sect = self.stub_helper_section_index.? + 1;
15441663
1545 try atom.relocs.ensureTotalCapacity(gpa, 1);1664 const code = try gpa.alloc(u8, size);
1665 defer gpa.free(code);
1666 mem.set(u8, code, 0);
15461667
1547 switch (arch) {1668 switch (arch) {
1548 .x86_64 => {1669 .x86_64 => {
1549 // pushq1670 // pushq
1550 atom.code.items[0] = 0x68;1671 code[0] = 0x68;
1551 // Next 4 bytes 1..4 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.1672 // Next 4 bytes 1..4 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
1552 // jmpq1673 // jmpq
1553 atom.code.items[5] = 0xe9;1674 code[5] = 0xe9;
1554 atom.relocs.appendAssumeCapacity(.{1675
1555 .offset = 6,1676 if (self.mode == .incremental) {
1556 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },1677 try atom.addRelocation(self, .{
1557 .addend = 0,1678 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1558 .subtractor = null,1679 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
1559 .pcrel = true,1680 .offset = 6,
1560 .length = 2,1681 .addend = 0,
1561 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),1682 .pcrel = true,
1562 });1683 .length = 2,
1684 });
1685 } else {
1686 try atom.relocs.ensureTotalCapacity(gpa, 1);
1687 atom.relocs.appendAssumeCapacity(.{
1688 .offset = 6,
1689 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
1690 .addend = 0,
1691 .subtractor = null,
1692 .pcrel = true,
1693 .length = 2,
1694 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1695 });
1696 }
1563 },1697 },
1564 .aarch64 => {1698 .aarch64 => {
1565 const literal = blk: {1699 const literal = blk: {
1566 const div_res = try math.divExact(u64, stub_size - @sizeOf(u32), 4);1700 const div_res = try math.divExact(u64, size - @sizeOf(u32), 4);
1567 break :blk math.cast(u18, div_res) orelse return error.Overflow;1701 break :blk math.cast(u18, div_res) orelse return error.Overflow;
1568 };1702 };
1569 // ldr w16, literal1703 // ldr w16, literal
1570 mem.writeIntLittle(u32, atom.code.items[0..4], aarch64.Instruction.ldrLiteral(1704 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldrLiteral(
1571 .w16,1705 .w16,
1572 literal,1706 literal,
1573 ).toU32());1707 ).toU32());
1574 // b disp1708 // b disp
1575 mem.writeIntLittle(u32, atom.code.items[4..8], aarch64.Instruction.b(0).toU32());1709 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(0).toU32());
1576 atom.relocs.appendAssumeCapacity(.{
1577 .offset = 4,
1578 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
1579 .addend = 0,
1580 .subtractor = null,
1581 .pcrel = true,
1582 .length = 2,
1583 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
1584 });
1585 // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.1710 // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
1711
1712 if (self.mode == .incremental) {
1713 try atom.addRelocation(self, .{
1714 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
1715 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
1716 .offset = 4,
1717 .addend = 0,
1718 .pcrel = true,
1719 .length = 2,
1720 });
1721 } else {
1722 try atom.relocs.ensureTotalCapacity(gpa, 1);
1723 atom.relocs.appendAssumeCapacity(.{
1724 .offset = 4,
1725 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
1726 .addend = 0,
1727 .subtractor = null,
1728 .pcrel = true,
1729 .length = 2,
1730 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
1731 });
1732 }
1586 },1733 },
1587 else => unreachable,1734 else => unreachable,
1588 }1735 }
...@@ -1590,7 +1737,14 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -1590,7 +1737,14 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
1590 try self.managed_atoms.append(gpa, atom);1737 try self.managed_atoms.append(gpa, atom);
1591 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);1738 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
15921739
1593 try self.allocateAtomCommon(atom);1740 if (self.mode == .incremental) {
1741 sym.n_value = try self.allocateAtom(atom, size, math.powi(u32, 2, alignment) catch unreachable);
1742 log.debug("allocated stub helper atom at 0x{x}", .{sym.n_value});
1743 try self.writeAtom(atom, code);
1744 } else {
1745 mem.copy(u8, atom.code.items, code);
1746 try self.addAtomToSection(atom);
1747 }
15941748
1595 return atom;1749 return atom;
1596}1750}
...@@ -1598,36 +1752,73 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -1598,36 +1752,73 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
1598pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !*Atom {1752pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !*Atom {
1599 const gpa = self.base.allocator;1753 const gpa = self.base.allocator;
1600 const sym_index = try self.allocateSymbol();1754 const sym_index = try self.allocateSymbol();
1601 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);1755 const atom = switch (self.mode) {
1756 .incremental => blk: {
1757 const atom = try gpa.create(Atom);
1758 atom.* = Atom.empty;
1759 atom.sym_index = sym_index;
1760 atom.size = @sizeOf(u64);
1761 atom.alignment = 3;
1762 break :blk atom;
1763 },
1764 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3),
1765 };
1766 errdefer gpa.destroy(atom);
1767
1602 const sym = atom.getSymbolPtr(self);1768 const sym = atom.getSymbolPtr(self);
1603 sym.n_type = macho.N_SECT;1769 sym.n_type = macho.N_SECT;
1604 sym.n_sect = self.la_symbol_ptr_section_index.? + 1;1770 sym.n_sect = self.la_symbol_ptr_section_index.? + 1;
16051771
1606 try atom.relocs.append(gpa, .{1772 if (self.mode == .incremental) {
1607 .offset = 0,1773 try atom.addRelocation(self, .{
1608 .target = .{ .sym_index = stub_sym_index, .file = null },1774 .@"type" = switch (self.base.options.target.cpu.arch) {
1609 .addend = 0,1775 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1610 .subtractor = null,1776 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1611 .pcrel = false,1777 else => unreachable,
1612 .length = 3,1778 },
1613 .@"type" = switch (self.base.options.target.cpu.arch) {1779 .target = .{ .sym_index = stub_sym_index, .file = null },
1614 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),1780 .offset = 0,
1615 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),1781 .addend = 0,
1616 else => unreachable,1782 .pcrel = false,
1617 },1783 .length = 3,
1618 });1784 });
1619 try atom.rebases.append(gpa, 0);1785 try atom.addRebase(self, 0);
16201786 try atom.addLazyBinding(self, .{
1621 const global = self.getGlobal(self.getSymbolName(target)).?;1787 .target = self.getGlobal(self.getSymbolName(target)).?,
1622 try atom.lazy_bindings.append(gpa, .{1788 .offset = 0,
1623 .target = global,1789 });
1624 .offset = 0,1790 } else {
1625 });1791 try atom.relocs.append(gpa, .{
1792 .offset = 0,
1793 .target = .{ .sym_index = stub_sym_index, .file = null },
1794 .addend = 0,
1795 .subtractor = null,
1796 .pcrel = false,
1797 .length = 3,
1798 .@"type" = switch (self.base.options.target.cpu.arch) {
1799 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1800 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1801 else => unreachable,
1802 },
1803 });
1804 try atom.rebases.append(gpa, 0);
1805 const global = self.getGlobal(self.getSymbolName(target)).?;
1806 try atom.lazy_bindings.append(gpa, .{
1807 .target = global,
1808 .offset = 0,
1809 });
1810 }
16261811
1627 try self.managed_atoms.append(gpa, atom);1812 try self.managed_atoms.append(gpa, atom);
1628 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);1813 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
16291814
1630 try self.allocateAtomCommon(atom);1815 if (self.mode == .incremental) {
1816 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1817 log.debug("allocated lazy pointer atom at 0x{x}", .{sym.n_value});
1818 try self.writePtrWidthAtom(atom);
1819 } else {
1820 try self.addAtomToSection(atom);
1821 }
16311822
1632 return atom;1823 return atom;
1633}1824}
...@@ -1640,62 +1831,112 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -1640,62 +1831,112 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
1640 .aarch64 => 2,1831 .aarch64 => 2,
1641 else => unreachable, // unhandled architecture type1832 else => unreachable, // unhandled architecture type
1642 };1833 };
1643 const stub_size: u4 = switch (arch) {1834 const size: u4 = switch (arch) {
1644 .x86_64 => 6,1835 .x86_64 => 6,
1645 .aarch64 => 3 * @sizeOf(u32),1836 .aarch64 => 3 * @sizeOf(u32),
1646 else => unreachable, // unhandled architecture type1837 else => unreachable, // unhandled architecture type
1647 };1838 };
1648 const sym_index = try self.allocateSymbol();1839 const sym_index = try self.allocateSymbol();
1649 const atom = try MachO.createEmptyAtom(gpa, sym_index, stub_size, alignment);1840 const atom = switch (self.mode) {
1841 .incremental => blk: {
1842 const atom = try gpa.create(Atom);
1843 atom.* = Atom.empty;
1844 atom.sym_index = sym_index;
1845 atom.size = size;
1846 atom.alignment = alignment;
1847 break :blk atom;
1848 },
1849 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, size, alignment),
1850 };
1851 errdefer gpa.destroy(atom);
1852
1650 const sym = atom.getSymbolPtr(self);1853 const sym = atom.getSymbolPtr(self);
1651 sym.n_type = macho.N_SECT;1854 sym.n_type = macho.N_SECT;
1652 sym.n_sect = self.stubs_section_index.? + 1;1855 sym.n_sect = self.stubs_section_index.? + 1;
16531856
1857 const code = try gpa.alloc(u8, size);
1858 defer gpa.free(code);
1859 mem.set(u8, code, 0);
1860
1654 switch (arch) {1861 switch (arch) {
1655 .x86_64 => {1862 .x86_64 => {
1656 // jmp1863 // jmp
1657 atom.code.items[0] = 0xff;1864 code[0] = 0xff;
1658 atom.code.items[1] = 0x25;1865 code[1] = 0x25;
1659 try atom.relocs.append(gpa, .{1866
1660 .offset = 2,1867 if (self.mode == .incremental) {
1661 .target = .{ .sym_index = laptr_sym_index, .file = null },1868 try atom.addRelocation(self, .{
1662 .addend = 0,1869 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1663 .subtractor = null,1870 .target = .{ .sym_index = laptr_sym_index, .file = null },
1664 .pcrel = true,1871 .offset = 2,
1665 .length = 2,1872 .addend = 0,
1666 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),1873 .pcrel = true,
1667 });1874 .length = 2,
1875 });
1876 } else {
1877 try atom.relocs.append(gpa, .{
1878 .offset = 2,
1879 .target = .{ .sym_index = laptr_sym_index, .file = null },
1880 .addend = 0,
1881 .subtractor = null,
1882 .pcrel = true,
1883 .length = 2,
1884 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1885 });
1886 }
1668 },1887 },
1669 .aarch64 => {1888 .aarch64 => {
1670 try atom.relocs.ensureTotalCapacity(gpa, 2);
1671 // adrp x16, pages1889 // adrp x16, pages
1672 mem.writeIntLittle(u32, atom.code.items[0..4], aarch64.Instruction.adrp(.x16, 0).toU32());1890 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
1673 atom.relocs.appendAssumeCapacity(.{
1674 .offset = 0,
1675 .target = .{ .sym_index = laptr_sym_index, .file = null },
1676 .addend = 0,
1677 .subtractor = null,
1678 .pcrel = true,
1679 .length = 2,
1680 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1681 });
1682 // ldr x16, x16, offset1891 // ldr x16, x16, offset
1683 mem.writeIntLittle(u32, atom.code.items[4..8], aarch64.Instruction.ldr(1892 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(
1684 .x16,1893 .x16,
1685 .x16,1894 .x16,
1686 aarch64.Instruction.LoadStoreOffset.imm(0),1895 aarch64.Instruction.LoadStoreOffset.imm(0),
1687 ).toU32());1896 ).toU32());
1688 atom.relocs.appendAssumeCapacity(.{
1689 .offset = 4,
1690 .target = .{ .sym_index = laptr_sym_index, .file = null },
1691 .addend = 0,
1692 .subtractor = null,
1693 .pcrel = false,
1694 .length = 2,
1695 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
1696 });
1697 // br x161897 // br x16
1698 mem.writeIntLittle(u32, atom.code.items[8..12], aarch64.Instruction.br(.x16).toU32());1898 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32());
1899
1900 if (self.mode == .incremental) {
1901 try atom.addRelocations(self, 2, .{
1902 .{
1903 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1904 .target = .{ .sym_index = laptr_sym_index, .file = null },
1905 .offset = 0,
1906 .addend = 0,
1907 .pcrel = true,
1908 .length = 2,
1909 },
1910 .{
1911 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
1912 .target = .{ .sym_index = laptr_sym_index, .file = null },
1913 .offset = 4,
1914 .addend = 0,
1915 .pcrel = false,
1916 .length = 2,
1917 },
1918 });
1919 } else {
1920 try atom.relocs.ensureTotalCapacity(gpa, 2);
1921 atom.relocs.appendAssumeCapacity(.{
1922 .offset = 0,
1923 .target = .{ .sym_index = laptr_sym_index, .file = null },
1924 .addend = 0,
1925 .subtractor = null,
1926 .pcrel = true,
1927 .length = 2,
1928 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1929 });
1930 atom.relocs.appendAssumeCapacity(.{
1931 .offset = 4,
1932 .target = .{ .sym_index = laptr_sym_index, .file = null },
1933 .addend = 0,
1934 .subtractor = null,
1935 .pcrel = false,
1936 .length = 2,
1937 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
1938 });
1939 }
1699 },1940 },
1700 else => unreachable,1941 else => unreachable,
1701 }1942 }
...@@ -1703,7 +1944,14 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -1703,7 +1944,14 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
1703 try self.managed_atoms.append(gpa, atom);1944 try self.managed_atoms.append(gpa, atom);
1704 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);1945 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
17051946
1706 try self.allocateAtomCommon(atom);1947 if (self.mode == .incremental) {
1948 sym.n_value = try self.allocateAtom(atom, size, math.powi(u32, 2, alignment) catch unreachable);
1949 log.debug("allocated stub atom at 0x{x}", .{sym.n_value});
1950 try self.writeAtom(atom, code);
1951 } else {
1952 mem.copy(u8, atom.code.items, code);
1953 try self.addAtomToSection(atom);
1954 }
17071955
1708 return atom;1956 return atom;
1709}1957}
...@@ -1976,6 +2224,7 @@ pub fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -1976,6 +2224,7 @@ pub fn resolveSymbolsInDylibs(self: *MachO) !void {
1976 const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.sym_index, global);2224 const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.sym_index, global);
1977 const stub_atom = try self.createStubAtom(laptr_atom.sym_index);2225 const stub_atom = try self.createStubAtom(laptr_atom.sym_index);
1978 self.stubs.items[stub_index].sym_index = stub_atom.sym_index;2226 self.stubs.items[stub_index].sym_index = stub_atom.sym_index;
2227 self.markRelocsDirtyByTarget(global);
1979 }2228 }
19802229
1981 continue :loop;2230 continue :loop;
...@@ -2073,6 +2322,10 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {...@@ -2073,6 +2322,10 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {
2073 const got_index = try self.allocateGotEntry(global);2322 const got_index = try self.allocateGotEntry(global);
2074 const got_atom = try self.createGotAtom(global);2323 const got_atom = try self.createGotAtom(global);
2075 self.got_entries.items[got_index].sym_index = got_atom.sym_index;2324 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
2325
2326 if (self.mode == .incremental) {
2327 try self.writePtrWidthAtom(got_atom);
2328 }
2076}2329}
20772330
2078pub fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {2331pub fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
...@@ -2356,6 +2609,30 @@ pub fn deinit(self: *MachO) void {...@@ -2356,6 +2609,30 @@ pub fn deinit(self: *MachO) void {
2356 }2609 }
2357 self.relocs.deinit(gpa);2610 self.relocs.deinit(gpa);
2358 }2611 }
2612
2613 {
2614 var it = self.rebases.valueIterator();
2615 while (it.next()) |rebases| {
2616 rebases.deinit(gpa);
2617 }
2618 self.rebases.deinit(gpa);
2619 }
2620
2621 {
2622 var it = self.bindings.valueIterator();
2623 while (it.next()) |bindings| {
2624 bindings.deinit(gpa);
2625 }
2626 self.bindings.deinit(gpa);
2627 }
2628
2629 {
2630 var it = self.lazy_bindings.valueIterator();
2631 while (it.next()) |bindings| {
2632 bindings.deinit(gpa);
2633 }
2634 self.lazy_bindings.deinit(gpa);
2635 }
2359}2636}
23602637
2361fn freeAtom(self: *MachO, atom: *Atom, owns_atom: bool) void {2638fn freeAtom(self: *MachO, atom: *Atom, owns_atom: bool) void {
...@@ -2363,6 +2640,8 @@ fn freeAtom(self: *MachO, atom: *Atom, owns_atom: bool) void {...@@ -2363,6 +2640,8 @@ fn freeAtom(self: *MachO, atom: *Atom, owns_atom: bool) void {
2363 if (!owns_atom) {2640 if (!owns_atom) {
2364 atom.deinit(self.base.allocator);2641 atom.deinit(self.base.allocator);
2365 }2642 }
2643 // Remove any relocs and base relocs associated with this Atom
2644 self.freeRelocationsForAtom(atom);
23662645
2367 const sect_id = atom.getSymbol(self).n_sect - 1;2646 const sect_id = atom.getSymbol(self).n_sect - 1;
2368 const free_list = &self.sections.items(.free_list)[sect_id];2647 const free_list = &self.sections.items(.free_list)[sect_id];
...@@ -2569,13 +2848,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv...@@ -2569,13 +2848,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
2569 const decl_index = func.owner_decl;2848 const decl_index = func.owner_decl;
2570 const decl = module.declPtr(decl_index);2849 const decl = module.declPtr(decl_index);
2571 self.freeUnnamedConsts(decl_index);2850 self.freeUnnamedConsts(decl_index);
25722851 self.freeRelocationsForAtom(&decl.link.macho);
2573 // TODO clearing the code and relocs buffer should probably be orchestrated
2574 // in a different, smarter, more automatic way somewhere else, in a more centralised
2575 // way than this.
2576 // If we don't clear the buffers here, we are up for some nasty surprises when
2577 // this atom is reused later on and was not freed by freeAtom().
2578 decl.link.macho.clearRetainingCapacity();
25792852
2580 var code_buffer = std.ArrayList(u8).init(self.base.allocator);2853 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
2581 defer code_buffer.deinit();2854 defer code_buffer.deinit();
...@@ -2593,18 +2866,16 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv...@@ -2593,18 +2866,16 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
2593 else2866 else
2594 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);2867 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
25952868
2596 switch (res) {2869 const code = switch (res) {
2597 .appended => {2870 .appended => code_buffer.items,
2598 try decl.link.macho.code.appendSlice(self.base.allocator, code_buffer.items);
2599 },
2600 .fail => |em| {2871 .fail => |em| {
2601 decl.analysis = .codegen_failure;2872 decl.analysis = .codegen_failure;
2602 try module.failed_decls.put(module.gpa, decl_index, em);2873 try module.failed_decls.put(module.gpa, decl_index, em);
2603 return;2874 return;
2604 },2875 },
2605 }2876 };
26062877
2607 const addr = try self.placeDecl(decl_index, decl.link.macho.code.items.len);2878 const addr = try self.updateDeclCode(decl_index, code);
26082879
2609 if (decl_state) |*ds| {2880 if (decl_state) |*ds| {
2610 try self.d_sym.?.dwarf.commitDeclState(2881 try self.d_sym.?.dwarf.commitDeclState(
...@@ -2650,20 +2921,17 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -2650,20 +2921,17 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
26502921
2651 log.debug("allocating symbol indexes for {?s}", .{name});2922 log.debug("allocating symbol indexes for {?s}", .{name});
26522923
2653 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);2924 const atom = try gpa.create(Atom);
2654 const sym_index = try self.allocateSymbol();2925 errdefer gpa.destroy(atom);
2655 const atom = try MachO.createEmptyAtom(2926 atom.* = Atom.empty;
2656 gpa,2927
2657 sym_index,2928 atom.sym_index = try self.allocateSymbol();
2658 @sizeOf(u64),
2659 math.log2(required_alignment),
2660 );
26612929
2662 try self.managed_atoms.append(gpa, atom);2930 try self.managed_atoms.append(gpa, atom);
2663 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);2931 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
26642932
2665 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{2933 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{
2666 .parent_atom_index = sym_index,2934 .parent_atom_index = atom.sym_index,
2667 });2935 });
2668 const code = switch (res) {2936 const code = switch (res) {
2669 .externally_managed => |x| x,2937 .externally_managed => |x| x,
...@@ -2676,9 +2944,9 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -2676,9 +2944,9 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
2676 },2944 },
2677 };2945 };
26782946
2679 atom.code.clearRetainingCapacity();2947 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
2680 try atom.code.appendSlice(gpa, code);2948 atom.size = code.len;
26812949 atom.alignment = math.log2(required_alignment);
2682 const sect_id = try self.getOutputSectionAtom(2950 const sect_id = try self.getOutputSectionAtom(
2683 atom,2951 atom,
2684 decl_name,2952 decl_name,
...@@ -2691,13 +2959,14 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -2691,13 +2959,14 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
2691 symbol.n_type = macho.N_SECT;2959 symbol.n_type = macho.N_SECT;
2692 symbol.n_sect = sect_id + 1;2960 symbol.n_sect = sect_id + 1;
2693 symbol.n_value = try self.allocateAtom(atom, code.len, required_alignment);2961 symbol.n_value = try self.allocateAtom(atom, code.len, required_alignment);
2962 errdefer self.freeAtom(atom, true);
2963
2964 try unnamed_consts.append(gpa, atom);
26942965
2695 log.debug("allocated atom for {?s} at 0x{x}", .{ name, symbol.n_value });2966 log.debug("allocated atom for {?s} at 0x{x}", .{ name, symbol.n_value });
2696 log.debug(" (required alignment 0x{x})", .{required_alignment});2967 log.debug(" (required alignment 0x{x})", .{required_alignment});
26972968
2698 errdefer self.freeAtom(atom, true);2969 try self.writeAtom(atom, code);
2699
2700 try unnamed_consts.append(gpa, atom);
27012970
2702 return atom.sym_index;2971 return atom.sym_index;
2703}2972}
...@@ -2724,6 +2993,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)...@@ -2724,6 +2993,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
2724 }2993 }
2725 }2994 }
27262995
2996 self.freeRelocationsForAtom(&decl.link.macho);
2997
2727 var code_buffer = std.ArrayList(u8).init(self.base.allocator);2998 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
2728 defer code_buffer.deinit();2999 defer code_buffer.deinit();
27293000
...@@ -2751,27 +3022,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)...@@ -2751,27 +3022,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
2751 .parent_atom_index = decl.link.macho.sym_index,3022 .parent_atom_index = decl.link.macho.sym_index,
2752 });3023 });
27533024
2754 const code = blk: {3025 const code = switch (res) {
2755 switch (res) {3026 .externally_managed => |x| x,
2756 .externally_managed => |x| break :blk x,3027 .appended => code_buffer.items,
2757 .appended => {3028 .fail => |em| {
2758 // TODO clearing the code and relocs buffer should probably be orchestrated3029 decl.analysis = .codegen_failure;
2759 // in a different, smarter, more automatic way somewhere else, in a more centralised3030 try module.failed_decls.put(module.gpa, decl_index, em);
2760 // way than this.3031 return;
2761 // If we don't clear the buffers here, we are up for some nasty surprises when3032 },
2762 // this atom is reused later on and was not freed by freeAtom().
2763 decl.link.macho.code.clearAndFree(self.base.allocator);
2764 try decl.link.macho.code.appendSlice(self.base.allocator, code_buffer.items);
2765 break :blk decl.link.macho.code.items;
2766 },
2767 .fail => |em| {
2768 decl.analysis = .codegen_failure;
2769 try module.failed_decls.put(module.gpa, decl_index, em);
2770 return;
2771 },
2772 }
2773 };3033 };
2774 const addr = try self.placeDecl(decl_index, code.len);3034 const addr = try self.updateDeclCode(decl_index, code);
27753035
2776 if (decl_state) |*ds| {3036 if (decl_state) |*ds| {
2777 try self.d_sym.?.dwarf.commitDeclState(3037 try self.d_sym.?.dwarf.commitDeclState(
...@@ -2936,19 +3196,22 @@ fn getOutputSectionAtom(...@@ -2936,19 +3196,22 @@ fn getOutputSectionAtom(
2936 return sect_id;3196 return sect_id;
2937}3197}
29383198
2939fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64 {3199fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8) !u64 {
2940 const module = self.base.options.module.?;3200 const gpa = self.base.allocator;
2941 const decl = module.declPtr(decl_index);3201 const mod = self.base.options.module.?;
3202 const decl = mod.declPtr(decl_index);
3203
2942 const required_alignment = decl.getAlignment(self.base.options.target);3204 const required_alignment = decl.getAlignment(self.base.options.target);
2943 assert(decl.link.macho.sym_index != 0); // Caller forgot to call allocateDeclIndexes()3205 assert(decl.link.macho.sym_index != 0); // Caller forgot to call allocateDeclIndexes()
29443206
2945 const sym_name = try decl.getFullyQualifiedName(module);3207 const sym_name = try decl.getFullyQualifiedName(mod);
2946 defer self.base.allocator.free(sym_name);3208 defer self.base.allocator.free(sym_name);
29473209
3210 const atom = &decl.link.macho;
2948 const decl_ptr = self.decls.getPtr(decl_index).?;3211 const decl_ptr = self.decls.getPtr(decl_index).?;
2949 if (decl_ptr.* == null) {3212 if (decl_ptr.* == null) {
2950 decl_ptr.* = try self.getOutputSectionAtom(3213 decl_ptr.* = try self.getOutputSectionAtom(
2951 &decl.link.macho,3214 atom,
2952 sym_name,3215 sym_name,
2953 decl.ty,3216 decl.ty,
2954 decl.val,3217 decl.val,
...@@ -2956,55 +3219,63 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64...@@ -2956,55 +3219,63 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64
2956 );3219 );
2957 }3220 }
2958 const sect_id = decl_ptr.*.?;3221 const sect_id = decl_ptr.*.?;
3222 const code_len = code.len;
29593223
2960 if (decl.link.macho.size != 0) {3224 if (atom.size != 0) {
2961 const symbol = decl.link.macho.getSymbolPtr(self);3225 const sym = atom.getSymbolPtr(self);
2962 symbol.n_strx = try self.strtab.insert(self.base.allocator, sym_name);3226 sym.n_strx = try self.strtab.insert(gpa, sym_name);
2963 symbol.n_type = macho.N_SECT;3227 sym.n_type = macho.N_SECT;
2964 symbol.n_sect = sect_id + 1;3228 sym.n_sect = sect_id + 1;
2965 symbol.n_desc = 0;3229 sym.n_desc = 0;
29663230
2967 const capacity = decl.link.macho.capacity(self);3231 const capacity = decl.link.macho.capacity(self);
2968 const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);3232 const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, sym.n_value, required_alignment);
29693233
2970 if (need_realloc) {3234 if (need_realloc) {
2971 const vaddr = try self.growAtom(&decl.link.macho, code_len, required_alignment);3235 const vaddr = try self.growAtom(atom, code_len, required_alignment);
2972 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, symbol.n_value, vaddr });3236 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, sym.n_value, vaddr });
2973 log.debug(" (required alignment 0x{x})", .{required_alignment});3237 log.debug(" (required alignment 0x{x})", .{required_alignment});
2974 symbol.n_value = vaddr;
2975
2976 const got_atom = self.getGotAtomForSymbol(.{
2977 .sym_index = decl.link.macho.sym_index,
2978 .file = null,
2979 }).?;
2980 got_atom.dirty = true;
2981 } else if (code_len < decl.link.macho.size) {
2982 self.shrinkAtom(&decl.link.macho, code_len);
2983 }
29843238
2985 decl.link.macho.size = code_len;3239 if (vaddr != sym.n_value) {
2986 decl.link.macho.dirty = true;3240 sym.n_value = vaddr;
3241 log.debug(" (updating GOT entry)", .{});
3242 const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null };
3243 const got_atom = self.getGotAtomForSymbol(got_target).?;
3244 self.markRelocsDirtyByTarget(got_target);
3245 try self.writePtrWidthAtom(got_atom);
3246 }
3247 } else if (code_len < atom.size) {
3248 self.shrinkAtom(atom, code_len);
3249 }
3250 atom.size = code_len;
2987 } else {3251 } else {
2988 const name_str_index = try self.strtab.insert(self.base.allocator, sym_name);3252 const name_str_index = try self.strtab.insert(gpa, sym_name);
2989 const symbol = decl.link.macho.getSymbolPtr(self);3253 const sym = atom.getSymbolPtr(self);
2990 symbol.n_strx = name_str_index;3254 sym.n_strx = name_str_index;
2991 symbol.n_type = macho.N_SECT;3255 sym.n_type = macho.N_SECT;
2992 symbol.n_sect = sect_id + 1;3256 sym.n_sect = sect_id + 1;
2993 symbol.n_desc = 0;3257 sym.n_desc = 0;
2994 symbol.n_value = try self.allocateAtom(&decl.link.macho, code_len, required_alignment);3258
29953259 const vaddr = try self.allocateAtom(atom, code_len, required_alignment);
2996 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, symbol.n_value });3260 errdefer self.freeAtom(atom, false);
3261
3262 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, vaddr });
2997 log.debug(" (required alignment 0x{x})", .{required_alignment});3263 log.debug(" (required alignment 0x{x})", .{required_alignment});
29983264
2999 errdefer self.freeAtom(&decl.link.macho, false);3265 atom.size = code_len;
3266 sym.n_value = vaddr;
30003267
3001 const got_target = SymbolWithLoc{ .sym_index = decl.link.macho.sym_index, .file = null };3268 const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null };
3002 const got_index = try self.allocateGotEntry(got_target);3269 const got_index = try self.allocateGotEntry(got_target);
3003 const got_atom = try self.createGotAtom(got_target);3270 const got_atom = try self.createGotAtom(got_target);
3004 self.got_entries.items[got_index].sym_index = got_atom.sym_index;3271 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
3272 try self.writePtrWidthAtom(got_atom);
3005 }3273 }
30063274
3007 return decl.link.macho.getSymbol(self).n_value;3275 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());
3276 try self.writeAtom(atom, code);
3277
3278 return atom.getSymbol(self).n_value;
3008}3279}
30093280
3010pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {3281pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {
...@@ -3152,17 +3423,25 @@ pub fn deleteExport(self: *MachO, exp: Export) void {...@@ -3152,17 +3423,25 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
3152 }3423 }
3153}3424}
31543425
3426fn freeRelocationsForAtom(self: *MachO, atom: *Atom) void {
3427 _ = self.relocs.remove(atom);
3428 _ = self.rebases.remove(atom);
3429 _ = self.bindings.remove(atom);
3430 _ = self.lazy_bindings.remove(atom);
3431}
3432
3155fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {3433fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
3434 const gpa = self.base.allocator;
3156 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;3435 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
3157 for (unnamed_consts.items) |atom| {3436 for (unnamed_consts.items) |atom| {
3158 self.freeAtom(atom, true);3437 self.freeAtom(atom, true);
3159 self.locals_free_list.append(self.base.allocator, atom.sym_index) catch {};3438 self.locals_free_list.append(gpa, atom.sym_index) catch {};
3160 self.locals.items[atom.sym_index].n_type = 0;3439 self.locals.items[atom.sym_index].n_type = 0;
3161 _ = self.atom_by_index_table.remove(atom.sym_index);3440 _ = self.atom_by_index_table.remove(atom.sym_index);
3162 log.debug(" adding local symbol index {d} to free list", .{atom.sym_index});3441 log.debug(" adding local symbol index {d} to free list", .{atom.sym_index});
3163 atom.sym_index = 0;3442 atom.sym_index = 0;
3164 }3443 }
3165 unnamed_consts.clearAndFree(self.base.allocator);3444 unnamed_consts.clearAndFree(gpa);
3166}3445}
31673446
3168pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {3447pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
...@@ -3171,20 +3450,25 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {...@@ -3171,20 +3450,25 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
3171 }3450 }
3172 const mod = self.base.options.module.?;3451 const mod = self.base.options.module.?;
3173 const decl = mod.declPtr(decl_index);3452 const decl = mod.declPtr(decl_index);
3453
3174 log.debug("freeDecl {*}", .{decl});3454 log.debug("freeDecl {*}", .{decl});
3455
3175 const kv = self.decls.fetchSwapRemove(decl_index);3456 const kv = self.decls.fetchSwapRemove(decl_index);
3176 if (kv.?.value) |_| {3457 if (kv.?.value) |_| {
3177 self.freeAtom(&decl.link.macho, false);3458 self.freeAtom(&decl.link.macho, false);
3178 self.freeUnnamedConsts(decl_index);3459 self.freeUnnamedConsts(decl_index);
3179 }3460 }
3461
3180 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.3462 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
3181 if (decl.link.macho.sym_index != 0) {3463 const gpa = self.base.allocator;
3182 self.locals_free_list.append(self.base.allocator, decl.link.macho.sym_index) catch {};3464 const sym_index = decl.link.macho.sym_index;
3465 if (sym_index != 0) {
3466 self.locals_free_list.append(gpa, sym_index) catch {};
31833467
3184 // Try freeing GOT atom if this decl had one3468 // Try freeing GOT atom if this decl had one
3185 const got_target = SymbolWithLoc{ .sym_index = decl.link.macho.sym_index, .file = null };3469 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
3186 if (self.got_entries_table.get(got_target)) |got_index| {3470 if (self.got_entries_table.get(got_target)) |got_index| {
3187 self.got_entries_free_list.append(self.base.allocator, @intCast(u32, got_index)) catch {};3471 self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {};
3188 self.got_entries.items[got_index] = .{3472 self.got_entries.items[got_index] = .{
3189 .target = .{ .sym_index = 0, .file = null },3473 .target = .{ .sym_index = 0, .file = null },
3190 .sym_index = 0,3474 .sym_index = 0,
...@@ -3192,20 +3476,18 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {...@@ -3192,20 +3476,18 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
3192 _ = self.got_entries_table.remove(got_target);3476 _ = self.got_entries_table.remove(got_target);
31933477
3194 if (self.d_sym) |*d_sym| {3478 if (self.d_sym) |*d_sym| {
3195 d_sym.swapRemoveRelocs(decl.link.macho.sym_index);3479 d_sym.swapRemoveRelocs(sym_index);
3196 }3480 }
31973481
3198 log.debug(" adding GOT index {d} to free list (target local@{d})", .{3482 log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
3199 got_index,
3200 decl.link.macho.sym_index,
3201 });
3202 }3483 }
32033484
3204 self.locals.items[decl.link.macho.sym_index].n_type = 0;3485 self.locals.items[sym_index].n_type = 0;
3205 _ = self.atom_by_index_table.remove(decl.link.macho.sym_index);3486 _ = self.atom_by_index_table.remove(sym_index);
3206 log.debug(" adding local symbol index {d} to free list", .{decl.link.macho.sym_index});3487 log.debug(" adding local symbol index {d} to free list", .{sym_index});
3207 decl.link.macho.sym_index = 0;3488 decl.link.macho.sym_index = 0;
3208 }3489 }
3490
3209 if (self.d_sym) |*d_sym| {3491 if (self.d_sym) |*d_sym| {
3210 d_sym.dwarf.freeDecl(decl);3492 d_sym.dwarf.freeDecl(decl);
3211 }3493 }
...@@ -3218,21 +3500,20 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil...@@ -3218,21 +3500,20 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil
3218 assert(self.llvm_object == null);3500 assert(self.llvm_object == null);
3219 assert(decl.link.macho.sym_index != 0);3501 assert(decl.link.macho.sym_index != 0);
32203502
3221 const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?;3503 const atom = self.getAtomForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;
3222 try atom.relocs.append(self.base.allocator, .{3504 try atom.addRelocation(self, .{
3223 .offset = @intCast(u32, reloc_info.offset),
3224 .target = .{ .sym_index = decl.link.macho.sym_index, .file = null },
3225 .addend = reloc_info.addend,
3226 .subtractor = null,
3227 .pcrel = false,
3228 .length = 3,
3229 .@"type" = switch (self.base.options.target.cpu.arch) {3505 .@"type" = switch (self.base.options.target.cpu.arch) {
3230 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),3506 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
3231 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),3507 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
3232 else => unreachable,3508 else => unreachable,
3233 },3509 },
3510 .target = .{ .sym_index = decl.link.macho.sym_index, .file = null },
3511 .offset = @intCast(u32, reloc_info.offset),
3512 .addend = reloc_info.addend,
3513 .pcrel = false,
3514 .length = 3,
3234 });3515 });
3235 try atom.rebases.append(self.base.allocator, reloc_info.offset);3516 try atom.addRebase(self, @intCast(u32, reloc_info.offset));
32363517
3237 return 0;3518 return 0;
3238}3519}
...@@ -3575,6 +3856,7 @@ fn allocateSection(self: *MachO, sect_id: u8, size: u64, alignment: u32) !void {...@@ -3575,6 +3856,7 @@ fn allocateSection(self: *MachO, sect_id: u8, size: u64, alignment: u32) !void {
35753856
3576 if (size > max_size) {3857 if (size > max_size) {
3577 try self.growSection(sect_id, @intCast(u32, size));3858 try self.growSection(sect_id, @intCast(u32, size));
3859 self.markRelocsDirtyByAddress(header.addr + size);
3578 }3860 }
35793861
3580 log.debug("allocating {s},{s} section at 0x{x}", .{ header.segName(), header.sectName(), off });3862 log.debug("allocating {s},{s} section at 0x{x}", .{ header.segName(), header.sectName(), off });
...@@ -3885,7 +4167,13 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !...@@ -3885,7 +4167,13 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
3885 const header = &self.sections.items(.header)[sect_id];4167 const header = &self.sections.items(.header)[sect_id];
3886 const free_list = &self.sections.items(.free_list)[sect_id];4168 const free_list = &self.sections.items(.free_list)[sect_id];
3887 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];4169 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
3888 const new_atom_ideal_capacity = if (header.isCode()) padToIdeal(new_atom_size) else new_atom_size;4170 const requires_padding = blk: {
4171 if (!header.isCode()) break :blk false;
4172 if (mem.eql(u8, "__stubs", header.sectName())) break :blk false;
4173 if (mem.eql(u8, "__stub_helper", header.sectName())) break :blk false;
4174 break :blk true;
4175 };
4176 const new_atom_ideal_capacity = if (requires_padding) padToIdeal(new_atom_size) else new_atom_size;
38894177
3890 // We use these to indicate our intention to update metadata, placing the new atom,4178 // We use these to indicate our intention to update metadata, placing the new atom,
3891 // and possibly removing a free list node.4179 // and possibly removing a free list node.
...@@ -3905,7 +4193,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !...@@ -3905,7 +4193,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
3905 // Is it enough that we could fit this new atom?4193 // Is it enough that we could fit this new atom?
3906 const sym = big_atom.getSymbol(self);4194 const sym = big_atom.getSymbol(self);
3907 const capacity = big_atom.capacity(self);4195 const capacity = big_atom.capacity(self);
3908 const ideal_capacity = if (header.isCode()) padToIdeal(capacity) else capacity;4196 const ideal_capacity = if (requires_padding) padToIdeal(capacity) else capacity;
3909 const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity;4197 const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity;
3910 const capacity_end_vaddr = sym.n_value + capacity;4198 const capacity_end_vaddr = sym.n_value + capacity;
3911 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;4199 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;
...@@ -3935,7 +4223,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !...@@ -3935,7 +4223,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
3935 break :blk new_start_vaddr;4223 break :blk new_start_vaddr;
3936 } else if (maybe_last_atom.*) |last| {4224 } else if (maybe_last_atom.*) |last| {
3937 const last_symbol = last.getSymbol(self);4225 const last_symbol = last.getSymbol(self);
3938 const ideal_capacity = if (header.isCode()) padToIdeal(last.size) else last.size;4226 const ideal_capacity = if (requires_padding) padToIdeal(last.size) else last.size;
3939 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;4227 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
3940 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);4228 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
3941 atom_placement = last;4229 atom_placement = last;
...@@ -3949,6 +4237,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !...@@ -3949,6 +4237,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
3949 if (expand_section) {4237 if (expand_section) {
3950 const needed_size = @intCast(u32, (vaddr + new_atom_size) - header.addr);4238 const needed_size = @intCast(u32, (vaddr + new_atom_size) - header.addr);
3951 try self.growSection(sect_id, needed_size);4239 try self.growSection(sect_id, needed_size);
4240 self.markRelocsDirtyByAddress(header.addr + needed_size);
3952 maybe_last_atom.* = atom;4241 maybe_last_atom.* = atom;
3953 header.size = needed_size;4242 header.size = needed_size;
3954 }4243 }
...@@ -4105,64 +4394,70 @@ pub fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {...@@ -4105,64 +4394,70 @@ pub fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4105 const sym = atom.getSymbol(self);4394 const sym = atom.getSymbol(self);
4106 const base_offset = sym.n_value - seg.vmaddr;4395 const base_offset = sym.n_value - seg.vmaddr;
41074396
4108 for (atom.rebases.items) |offset| {4397 if (self.rebases.get(atom)) |rebases| {
4109 log.debug(" | rebase at {x}", .{base_offset + offset});4398 for (rebases.items) |offset| {
4110 try rebase_pointers.append(.{4399 log.debug(" | rebase at {x}", .{base_offset + offset});
4111 .offset = base_offset + offset,4400 try rebase_pointers.append(.{
4112 .segment_id = segment_index,4401 .offset = base_offset + offset,
4113 });4402 .segment_id = segment_index,
4403 });
4404 }
4114 }4405 }
41154406
4116 for (atom.bindings.items) |binding| {4407 if (self.bindings.get(atom)) |bindings| {
4117 const bind_sym = self.getSymbol(binding.target);4408 for (bindings.items) |binding| {
4118 const bind_sym_name = self.getSymbolName(binding.target);4409 const bind_sym = self.getSymbol(binding.target);
4119 const dylib_ordinal = @divTrunc(4410 const bind_sym_name = self.getSymbolName(binding.target);
4120 @bitCast(i16, bind_sym.n_desc),4411 const dylib_ordinal = @divTrunc(
4121 macho.N_SYMBOL_RESOLVER,4412 @bitCast(i16, bind_sym.n_desc),
4122 );4413 macho.N_SYMBOL_RESOLVER,
4123 var flags: u4 = 0;4414 );
4124 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{4415 var flags: u4 = 0;
4125 binding.offset + base_offset,4416 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
4126 bind_sym_name,4417 binding.offset + base_offset,
4127 dylib_ordinal,4418 bind_sym_name,
4128 });4419 dylib_ordinal,
4129 if (bind_sym.weakRef()) {4420 });
4130 log.debug(" | marking as weak ref ", .{});4421 if (bind_sym.weakRef()) {
4131 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);4422 log.debug(" | marking as weak ref ", .{});
4423 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
4424 }
4425 try bind_pointers.append(.{
4426 .offset = binding.offset + base_offset,
4427 .segment_id = segment_index,
4428 .dylib_ordinal = dylib_ordinal,
4429 .name = bind_sym_name,
4430 .bind_flags = flags,
4431 });
4132 }4432 }
4133 try bind_pointers.append(.{
4134 .offset = binding.offset + base_offset,
4135 .segment_id = segment_index,
4136 .dylib_ordinal = dylib_ordinal,
4137 .name = bind_sym_name,
4138 .bind_flags = flags,
4139 });
4140 }4433 }
41414434
4142 for (atom.lazy_bindings.items) |binding| {4435 if (self.lazy_bindings.get(atom)) |lazy_bindings| {
4143 const bind_sym = self.getSymbol(binding.target);4436 for (lazy_bindings.items) |binding| {
4144 const bind_sym_name = self.getSymbolName(binding.target);4437 const bind_sym = self.getSymbol(binding.target);
4145 const dylib_ordinal = @divTrunc(4438 const bind_sym_name = self.getSymbolName(binding.target);
4146 @bitCast(i16, bind_sym.n_desc),4439 const dylib_ordinal = @divTrunc(
4147 macho.N_SYMBOL_RESOLVER,4440 @bitCast(i16, bind_sym.n_desc),
4148 );4441 macho.N_SYMBOL_RESOLVER,
4149 var flags: u4 = 0;4442 );
4150 log.debug(" | lazy bind at {x} import('{s}') ord({d})", .{4443 var flags: u4 = 0;
4151 binding.offset + base_offset,4444 log.debug(" | lazy bind at {x} import('{s}') ord({d})", .{
4152 bind_sym_name,4445 binding.offset + base_offset,
4153 dylib_ordinal,4446 bind_sym_name,
4154 });4447 dylib_ordinal,
4155 if (bind_sym.weakRef()) {4448 });
4156 log.debug(" | marking as weak ref ", .{});4449 if (bind_sym.weakRef()) {
4157 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);4450 log.debug(" | marking as weak ref ", .{});
4451 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
4452 }
4453 try lazy_bind_pointers.append(.{
4454 .offset = binding.offset + base_offset,
4455 .segment_id = segment_index,
4456 .dylib_ordinal = dylib_ordinal,
4457 .name = bind_sym_name,
4458 .bind_flags = flags,
4459 });
4158 }4460 }
4159 try lazy_bind_pointers.append(.{
4160 .offset = binding.offset + base_offset,
4161 .segment_id = segment_index,
4162 .dylib_ordinal = dylib_ordinal,
4163 .name = bind_sym_name,
4164 .bind_flags = flags,
4165 });
4166 }4461 }
41674462
4168 if (atom.prev) |prev| {4463 if (atom.prev) |prev| {
...@@ -4387,25 +4682,25 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -4387,25 +4682,25 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
43874682
4388const asc_u64 = std.sort.asc(u64);4683const asc_u64 = std.sort.asc(u64);
43894684
4390fn writeFunctionStarts(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {4685pub fn writeFunctionStarts(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4391 const tracy = trace(@src());4686 const tracy = trace(@src());
4392 defer tracy.end();4687 defer tracy.end();
43934688
4394 const text_seg_index = self.text_segment_cmd_index orelse return;4689 const text_seg_index = macho_file.text_segment_cmd_index orelse return;
4395 const text_sect_index = self.text_section_index orelse return;4690 const text_sect_index = macho_file.text_section_index orelse return;
4396 const text_seg = self.segments.items[text_seg_index];4691 const text_seg = macho_file.segments.items[text_seg_index];
43974692
4398 const gpa = self.base.allocator;4693 const gpa = macho_file.base.allocator;
43994694
4400 // We need to sort by address first4695 // We need to sort by address first
4401 var addresses = std.ArrayList(u64).init(gpa);4696 var addresses = std.ArrayList(u64).init(gpa);
4402 defer addresses.deinit();4697 defer addresses.deinit();
4403 try addresses.ensureTotalCapacityPrecise(self.globals.items.len);4698 try addresses.ensureTotalCapacityPrecise(macho_file.globals.items.len);
44044699
4405 for (self.globals.items) |global| {4700 for (macho_file.globals.items) |global| {
4406 const sym = self.getSymbol(global);4701 const sym = macho_file.getSymbol(global);
4407 if (sym.undf()) continue;4702 if (sym.undf()) continue;
4408 if (sym.n_desc == N_DESC_GCED) continue;4703 if (sym.n_desc == MachO.N_DESC_GCED) continue;
4409 const sect_id = sym.n_sect - 1;4704 const sect_id = sym.n_sect - 1;
4410 if (sect_id != text_sect_index) continue;4705 if (sect_id != text_sect_index) continue;
44114706
...@@ -4439,14 +4734,14 @@ fn writeFunctionStarts(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {...@@ -4439,14 +4734,14 @@ fn writeFunctionStarts(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4439 try std.leb.writeULEB128(buffer.writer(), offset);4734 try std.leb.writeULEB128(buffer.writer(), offset);
4440 }4735 }
44414736
4442 const link_seg = &self.segments.items[self.linkedit_segment_cmd_index.?];4737 const link_seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
4443 const offset = mem.alignForwardGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64));4738 const offset = mem.alignForwardGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64));
4444 const needed_size = buffer.items.len;4739 const needed_size = buffer.items.len;
4445 link_seg.filesize = offset + needed_size - link_seg.fileoff;4740 link_seg.filesize = offset + needed_size - link_seg.fileoff;
44464741
4447 log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size });4742 log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
44484743
4449 try self.base.file.?.pwriteAll(buffer.items, offset);4744 try macho_file.base.file.?.pwriteAll(buffer.items, offset);
44504745
4451 try lc_writer.writeStruct(macho.linkedit_data_command{4746 try lc_writer.writeStruct(macho.linkedit_data_command{
4452 .cmd = .FUNCTION_STARTS,4747 .cmd = .FUNCTION_STARTS,
...@@ -4465,8 +4760,8 @@ fn filterDataInCode(...@@ -4465,8 +4760,8 @@ fn filterDataInCode(
4465 const Predicate = struct {4760 const Predicate = struct {
4466 addr: u64,4761 addr: u64,
44674762
4468 pub fn predicate(self: @This(), dice: macho.data_in_code_entry) bool {4763 pub fn predicate(macho_file: @This(), dice: macho.data_in_code_entry) bool {
4469 return dice.offset >= self.addr;4764 return dice.offset >= macho_file.addr;
4470 }4765 }
4471 };4766 };
44724767
...@@ -4476,26 +4771,26 @@ fn filterDataInCode(...@@ -4476,26 +4771,26 @@ fn filterDataInCode(
4476 return dices[start..end];4771 return dices[start..end];
4477}4772}
44784773
4479fn writeDataInCode(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {4774pub fn writeDataInCode(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4480 const tracy = trace(@src());4775 const tracy = trace(@src());
4481 defer tracy.end();4776 defer tracy.end();
44824777
4483 var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.base.allocator);4778 var out_dice = std.ArrayList(macho.data_in_code_entry).init(macho_file.base.allocator);
4484 defer out_dice.deinit();4779 defer out_dice.deinit();
44854780
4486 const text_sect_id = self.text_section_index orelse return;4781 const text_sect_id = macho_file.text_section_index orelse return;
4487 const text_sect_header = self.sections.items(.header)[text_sect_id];4782 const text_sect_header = macho_file.sections.items(.header)[text_sect_id];
44884783
4489 for (self.objects.items) |object| {4784 for (macho_file.objects.items) |object| {
4490 const dice = object.parseDataInCode() orelse continue;4785 const dice = object.parseDataInCode() orelse continue;
4491 try out_dice.ensureUnusedCapacity(dice.len);4786 try out_dice.ensureUnusedCapacity(dice.len);
44924787
4493 for (object.managed_atoms.items) |atom| {4788 for (object.managed_atoms.items) |atom| {
4494 const sym = atom.getSymbol(self);4789 const sym = atom.getSymbol(macho_file);
4495 if (sym.n_desc == N_DESC_GCED) continue;4790 if (sym.n_desc == MachO.N_DESC_GCED) continue;
44964791
4497 const sect_id = sym.n_sect - 1;4792 const sect_id = sym.n_sect - 1;
4498 if (sect_id != self.text_section_index.?) {4793 if (sect_id != macho_file.text_section_index.?) {
4499 continue;4794 continue;
4500 }4795 }
45014796
...@@ -4516,14 +4811,14 @@ fn writeDataInCode(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {...@@ -4516,14 +4811,14 @@ fn writeDataInCode(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4516 }4811 }
4517 }4812 }
45184813
4519 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];4814 const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
4520 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));4815 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
4521 const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);4816 const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
4522 seg.filesize = offset + needed_size - seg.fileoff;4817 seg.filesize = offset + needed_size - seg.fileoff;
45234818
4524 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size });4819 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
45254820
4526 try self.base.file.?.pwriteAll(mem.sliceAsBytes(out_dice.items), offset);4821 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(out_dice.items), offset);
4527 try lc_writer.writeStruct(macho.linkedit_data_command{4822 try lc_writer.writeStruct(macho.linkedit_data_command{
4528 .cmd = .DATA_IN_CODE,4823 .cmd = .DATA_IN_CODE,
4529 .cmdsize = @sizeOf(macho.linkedit_data_command),4824 .cmdsize = @sizeOf(macho.linkedit_data_command),
...@@ -4533,7 +4828,7 @@ fn writeDataInCode(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {...@@ -4533,7 +4828,7 @@ fn writeDataInCode(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4533 ncmds.* += 1;4828 ncmds.* += 1;
4534}4829}
45354830
4536fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {4831pub fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4537 var symtab_cmd = macho.symtab_command{4832 var symtab_cmd = macho.symtab_command{
4538 .cmdsize = @sizeOf(macho.symtab_command),4833 .cmdsize = @sizeOf(macho.symtab_command),
4539 .symoff = 0,4834 .symoff = 0,
...@@ -4571,7 +4866,7 @@ fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {...@@ -4571,7 +4866,7 @@ fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4571 ncmds.* += 2;4866 ncmds.* += 2;
4572}4867}
45734868
4574fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {4869pub fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
4575 const gpa = self.base.allocator;4870 const gpa = self.base.allocator;
45764871
4577 var locals = std.ArrayList(macho.nlist_64).init(gpa);4872 var locals = std.ArrayList(macho.nlist_64).init(gpa);
...@@ -5485,7 +5780,7 @@ pub fn logSymtab(self: *MachO) void {...@@ -5485,7 +5780,7 @@ pub fn logSymtab(self: *MachO) void {
5485 const def_index = if (sym.undf() and !sym.tentative())5780 const def_index = if (sym.undf() and !sym.tentative())
5486 @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER)5781 @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER)
5487 else5782 else
5488 sym.n_sect;5783 sym.n_sect + 1;
5489 log.debug(" %{d}: {s} @{x} in {s}({d}), {s}", .{5784 log.debug(" %{d}: {s} @{x} in {s}({d}), {s}", .{
5490 sym_id,5785 sym_id,
5491 object.getString(sym.n_strx),5786 object.getString(sym.n_strx),
...@@ -5502,7 +5797,7 @@ pub fn logSymtab(self: *MachO) void {...@@ -5502,7 +5797,7 @@ pub fn logSymtab(self: *MachO) void {
5502 const def_index = if (sym.undf() and !sym.tentative())5797 const def_index = if (sym.undf() and !sym.tentative())
5503 @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER)5798 @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER)
5504 else5799 else
5505 sym.n_sect;5800 sym.n_sect + 1;
5506 log.debug(" %{d}: {?s} @{x} in {s}({d}), {s}", .{5801 log.debug(" %{d}: {?s} @{x} in {s}({d}), {s}", .{
5507 sym_id,5802 sym_id,
5508 self.strtab.get(sym.n_strx),5803 self.strtab.get(sym.n_strx),
...@@ -5633,6 +5928,6 @@ pub fn copyRangeAllOverlappingAlloc(...@@ -5633,6 +5928,6 @@ pub fn copyRangeAllOverlappingAlloc(
5633) !void {5928) !void {
5634 const buf = try allocator.alloc(u8, len);5929 const buf = try allocator.alloc(u8, len);
5635 defer allocator.free(buf);5930 defer allocator.free(buf);
5636 _ = try file.preadAll(buf, in_offset);5931 const amt = try file.preadAll(buf, in_offset);
5637 try file.pwriteAll(buf, out_offset);5932 try file.pwriteAll(buf[0..amt], out_offset);
5638}5933}
src/link/MachO/Atom.zig+55-22
...@@ -66,8 +66,6 @@ prev: ?*Atom,...@@ -66,8 +66,6 @@ prev: ?*Atom,
6666
67dbg_info_atom: Dwarf.Atom,67dbg_info_atom: Dwarf.Atom,
6868
69dirty: bool = true,
70
71pub const Binding = struct {69pub const Binding = struct {
72 target: SymbolWithLoc,70 target: SymbolWithLoc,
73 offset: u64,71 offset: u64,
...@@ -898,46 +896,81 @@ inline fn isArithmeticOp(inst: *const [4]u8) bool {...@@ -898,46 +896,81 @@ inline fn isArithmeticOp(inst: *const [4]u8) bool {
898}896}
899897
900pub fn addRelocation(self: *Atom, macho_file: *MachO, reloc: RelocationIncr) !void {898pub fn addRelocation(self: *Atom, macho_file: *MachO, reloc: RelocationIncr) !void {
899 return self.addRelocations(macho_file, 1, .{reloc});
900}
901
902pub fn addRelocations(
903 self: *Atom,
904 macho_file: *MachO,
905 comptime count: comptime_int,
906 relocs: [count]RelocationIncr,
907) !void {
901 const gpa = macho_file.base.allocator;908 const gpa = macho_file.base.allocator;
902 log.debug(" (adding reloc of type {s} to target %{d})", .{ @tagName(reloc.@"type"), reloc.target.sym_index });909 const target = macho_file.base.options.target;
903 const gop = try macho_file.relocs.getOrPut(gpa, self);910 const gop = try macho_file.relocs.getOrPut(gpa, self);
904 if (!gop.found_existing) {911 if (!gop.found_existing) {
905 gop.value_ptr.* = .{};912 gop.value_ptr.* = .{};
906 }913 }
907 try gop.value_ptr.append(gpa, reloc);914 try gop.value_ptr.ensureUnusedCapacity(gpa, count);
915 for (relocs) |reloc| {
916 log.debug(" (adding reloc of type {s} to target %{d})", .{
917 reloc.fmtType(target),
918 reloc.target.sym_index,
919 });
920 gop.value_ptr.appendAssumeCapacity(reloc);
921 }
908}922}
909923
910pub fn resolveRelocationsInCodeBuffer(self: *Atom, macho_file: *MachO, code: []u8) !void {924pub fn addRebase(self: *Atom, macho_file: *MachO, offset: u32) !void {
911 const relocs = macho_file.relocs.get(self) orelse return;925 const gpa = macho_file.base.allocator;
912926 log.debug(" (adding rebase at offset 0x{x} in %{d})", .{ offset, self.sym_index });
913 log.debug("relocating '{s}'", .{self.getName(macho_file)});927 const gop = try macho_file.rebases.getOrPut(gpa, self);
928 if (!gop.found_existing) {
929 gop.value_ptr.* = .{};
930 }
931 try gop.value_ptr.append(gpa, offset);
932}
914933
915 for (relocs.items) |*reloc| {934pub fn addBinding(self: *Atom, macho_file: *MachO, binding: Binding) !void {
916 // We don't check for dirty relocation as we resolve in memory so it's effectively free.935 const gpa = macho_file.base.allocator;
917 try reloc.resolve(self, macho_file, code);936 log.debug(" (adding binding to symbol {s} at offset 0x{x} in %{d})", .{
918 reloc.dirty = false;937 macho_file.getSymbolName(binding.target),
938 binding.offset,
939 self.sym_index,
940 });
941 const gop = try macho_file.bindings.getOrPut(gpa, self);
942 if (!gop.found_existing) {
943 gop.value_ptr.* = .{};
919 }944 }
945 try gop.value_ptr.append(gpa, binding);
920}946}
921947
922pub fn resolveRelocationsInFile(self: *Atom, macho_file: *MachO) !void {948pub fn addLazyBinding(self: *Atom, macho_file: *MachO, binding: Binding) !void {
923 const relocs = macho_file.relocs.get(self) orelse return;
924 const gpa = macho_file.base.allocator;949 const gpa = macho_file.base.allocator;
950 log.debug(" (adding lazy binding to symbol {s} at offset 0x{x} in %{d})", .{
951 macho_file.getSymbolName(binding.target),
952 binding.offset,
953 self.sym_index,
954 });
955 const gop = try macho_file.lazy_bindings.getOrPut(gpa, self);
956 if (!gop.found_existing) {
957 gop.value_ptr.* = .{};
958 }
959 try gop.value_ptr.append(gpa, binding);
960}
925961
926 // No code available in a buffer; we need to read it in from the binary.962pub fn resolveRelocations(self: *Atom, macho_file: *MachO) !void {
963 const relocs = macho_file.relocs.get(self) orelse return;
927 const source_sym = self.getSymbol(macho_file);964 const source_sym = self.getSymbol(macho_file);
928 const source_section = macho_file.sections.get(source_sym.n_sect - 1).header;965 const source_section = macho_file.sections.get(source_sym.n_sect - 1).header;
929 const file_offset = source_section.offset + source_sym.value - source_section.addr;966 const file_offset = source_section.offset + source_sym.n_value - source_section.addr;
930 const code = try gpa.alloc(u8, self.size);
931 try self.base.file.?.preadAll(code, file_offset);
932 defer gpa.free(code);
933967
934 log.debug("relocating '{s}'", .{self.getName(macho_file)});968 log.debug("relocating '{s}'", .{self.getName(macho_file)});
935969
936 for (relocs.items) |*reloc| {970 for (relocs.items) |*reloc| {
937 if (!reloc.dirty) continue;971 if (!reloc.dirty) continue;
938 try reloc.resolve(self, macho_file, code);972
973 try reloc.resolve(self, macho_file, file_offset);
939 reloc.dirty = false;974 reloc.dirty = false;
940 }975 }
941
942 try self.base.file.?.pwriteAll(code, file_offset);
943}976}
src/link/MachO/Relocation.zig+136-106
...@@ -13,31 +13,35 @@ const Atom = @import("Atom.zig");...@@ -13,31 +13,35 @@ const Atom = @import("Atom.zig");
13const MachO = @import("../MachO.zig");13const MachO = @import("../MachO.zig");
14const SymbolWithLoc = MachO.SymbolWithLoc;14const SymbolWithLoc = MachO.SymbolWithLoc;
1515
16pub const Table = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Relocation));16@"type": u4,
17
18/// Offset within the atom's code buffer.
19/// Note relocation size can be inferred by relocation's kind.
20offset: u32,
21target: SymbolWithLoc,17target: SymbolWithLoc,
18offset: u32,
22addend: i64,19addend: i64,
23pcrel: bool,20pcrel: bool,
24length: u2,21length: u2,
25@"type": u4,
26dirty: bool = true,22dirty: bool = true,
2723
24pub fn fmtType(self: Relocation, target: std.Target) []const u8 {
25 switch (target.cpu.arch) {
26 .aarch64 => return @tagName(@intToEnum(macho.reloc_type_arm64, self.@"type")),
27 .x86_64 => return @tagName(@intToEnum(macho.reloc_type_x86_64, self.@"type")),
28 else => unreachable,
29 }
30}
31
28pub fn getTargetAtom(self: Relocation, macho_file: *MachO) ?*Atom {32pub fn getTargetAtom(self: Relocation, macho_file: *MachO) ?*Atom {
29 switch (macho_file.base.options.target.cpu.arch) {33 switch (macho_file.base.options.target.cpu.arch) {
30 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, self.@"type")) {34 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, self.@"type")) {
31 .ARM64_RELOC_GOT_LOAD_PAGE21,35 .ARM64_RELOC_GOT_LOAD_PAGE21,
32 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,36 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
33 .ARM64_RELOC_POINTER_TO_GOT,37 .ARM64_RELOC_POINTER_TO_GOT,
34 => return macho_file.getGotAtomForSymbol(self.target).?,38 => return macho_file.getGotAtomForSymbol(self.target),
35 else => {},39 else => {},
36 },40 },
37 .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, self.@"type")) {41 .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, self.@"type")) {
38 .X86_64_RELOC_GOT,42 .X86_64_RELOC_GOT,
39 .X86_64_RELOC_GOT_LOAD,43 .X86_64_RELOC_GOT_LOAD,
40 => return macho_file.getGotAtomForSymbol(self.target).?,44 => return macho_file.getGotAtomForSymbol(self.target),
41 else => {},45 else => {},
42 },46 },
43 else => unreachable,47 else => unreachable,
...@@ -47,54 +51,72 @@ pub fn getTargetAtom(self: Relocation, macho_file: *MachO) ?*Atom {...@@ -47,54 +51,72 @@ pub fn getTargetAtom(self: Relocation, macho_file: *MachO) ?*Atom {
47 return macho_file.getAtomForSymbol(self.target);51 return macho_file.getAtomForSymbol(self.target);
48}52}
4953
50pub fn resolve(self: Relocation, atom: *Atom, macho_file: *MachO, code: []u8) !void {54pub fn resolve(self: Relocation, atom: *Atom, macho_file: *MachO, base_offset: u64) !void {
51 const arch = macho_file.base.options.target.cpu.arch;55 const arch = macho_file.base.options.target.cpu.arch;
52 const source_sym = atom.getSymbol(macho_file);56 const source_sym = atom.getSymbol(macho_file);
53 const source_addr = source_sym.n_value + self.offset;57 const source_addr = source_sym.n_value + self.offset;
5458
55 const target_atom = self.getTargetAtom(macho_file) orelse return;59 const target_atom = self.getTargetAtom(macho_file) orelse return;
56 const target_addr = target_atom.getSymbol(macho_file).n_value + self.addend;60 const target_addr = @intCast(i64, target_atom.getSymbol(macho_file).n_value) + self.addend;
5761
58 log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{62 log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{
59 source_addr,63 source_addr,
60 target_addr,64 target_addr,
61 macho_file.getSymbolName(self.target),65 macho_file.getSymbolName(self.target),
62 switch (arch) {66 self.fmtType(macho_file.base.options.target),
63 .aarch64 => @tagName(@intToEnum(macho.reloc_type_arm64, self.@"type")),
64 .x86_64 => @tagName(@intToEnum(macho.reloc_type_x86_64, self.@"type")),
65 else => unreachable,
66 },
67 });67 });
6868
69 switch (arch) {69 switch (arch) {
70 .aarch64 => return self.resolveAarch64(source_addr, target_addr, macho_file, code),70 .aarch64 => return self.resolveAarch64(macho_file, source_addr, target_addr, base_offset),
71 .x86_64 => return self.resolveX8664(source_addr, target_addr, code),71 .x86_64 => return self.resolveX8664(macho_file, source_addr, target_addr, base_offset),
72 else => unreachable,72 else => unreachable,
73 }73 }
74}74}
7575
76fn resolveAarch64(self: Relocation, source_addr: u64, target_addr: u64, macho_file: *MachO, code: []u8) !void {76fn resolveAarch64(
77 self: Relocation,
78 macho_file: *MachO,
79 source_addr: u64,
80 target_addr: i64,
81 base_offset: u64,
82) !void {
77 const rel_type = @intToEnum(macho.reloc_type_arm64, self.@"type");83 const rel_type = @intToEnum(macho.reloc_type_arm64, self.@"type");
84 if (rel_type == .ARM64_RELOC_UNSIGNED) {
85 var buffer: [@sizeOf(u64)]u8 = undefined;
86 const code = blk: {
87 switch (self.length) {
88 2 => {
89 mem.writeIntLittle(u32, buffer[0..4], @truncate(u32, @bitCast(u64, target_addr)));
90 break :blk buffer[0..4];
91 },
92 3 => {
93 mem.writeIntLittle(u64, &buffer, @bitCast(u64, target_addr));
94 break :blk &buffer;
95 },
96 else => unreachable,
97 }
98 };
99 return macho_file.base.file.?.pwriteAll(code, base_offset + self.offset);
100 }
101
102 var buffer: [@sizeOf(u32)]u8 = undefined;
103 const amt = try macho_file.base.file.?.preadAll(&buffer, base_offset + self.offset);
104 if (amt != buffer.len) return error.InputOutput;
105
78 switch (rel_type) {106 switch (rel_type) {
79 .ARM64_RELOC_BRANCH26 => {107 .ARM64_RELOC_BRANCH26 => {
80 const displacement = math.cast(i28, @intCast(i64, target_addr) - @intCast(i64, source_addr)) orelse {108 const displacement = math.cast(
81 log.err("jump too big to encode as i28 displacement value", .{});109 i28,
82 log.err(" (target - source) = displacement => 0x{x} - 0x{x} = 0x{x}", .{110 @intCast(i64, target_addr) - @intCast(i64, source_addr),
83 target_addr,111 ) orelse unreachable; // TODO codegen should never allow for jump larger than i28 displacement
84 source_addr,
85 @intCast(i64, target_addr) - @intCast(i64, source_addr),
86 });
87 log.err(" TODO implement branch islands to extend jump distance for arm64", .{});
88 return error.TODOImplementBranchIslands;
89 };
90 var inst = aarch64.Instruction{112 var inst = aarch64.Instruction{
91 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(113 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
92 aarch64.Instruction,114 aarch64.Instruction,
93 aarch64.Instruction.unconditional_branch_immediate,115 aarch64.Instruction.unconditional_branch_immediate,
94 ), code),116 ), &buffer),
95 };117 };
96 inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));118 inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));
97 mem.writeIntLittle(u32, code, inst.toU32());119 mem.writeIntLittle(u32, &buffer, inst.toU32());
98 },120 },
99 .ARM64_RELOC_PAGE21,121 .ARM64_RELOC_PAGE21,
100 .ARM64_RELOC_GOT_LOAD_PAGE21,122 .ARM64_RELOC_GOT_LOAD_PAGE21,
...@@ -107,45 +129,45 @@ fn resolveAarch64(self: Relocation, source_addr: u64, target_addr: u64, macho_fi...@@ -107,45 +129,45 @@ fn resolveAarch64(self: Relocation, source_addr: u64, target_addr: u64, macho_fi
107 .pc_relative_address = mem.bytesToValue(meta.TagPayload(129 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
108 aarch64.Instruction,130 aarch64.Instruction,
109 aarch64.Instruction.pc_relative_address,131 aarch64.Instruction.pc_relative_address,
110 ), code),132 ), &buffer),
111 };133 };
112 inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);134 inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
113 inst.pc_relative_address.immlo = @truncate(u2, pages);135 inst.pc_relative_address.immlo = @truncate(u2, pages);
114 mem.writeIntLittle(u32, code, inst.toU32());136 mem.writeIntLittle(u32, &buffer, inst.toU32());
115 },137 },
116 .ARM64_RELOC_PAGEOFF12 => {138 .ARM64_RELOC_PAGEOFF12 => {
117 const narrowed = @truncate(u12, @intCast(u64, target_addr));139 const narrowed = @truncate(u12, @intCast(u64, target_addr));
118 if (isArithmeticOp(code)) {140 if (isArithmeticOp(&buffer)) {
119 var inst = aarch64.Instruction{141 var inst = aarch64.Instruction{
120 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(142 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
121 aarch64.Instruction,143 aarch64.Instruction,
122 aarch64.Instruction.add_subtract_immediate,144 aarch64.Instruction.add_subtract_immediate,
123 ), code),145 ), &buffer),
124 };146 };
125 inst.add_subtract_immediate.imm12 = narrowed;147 inst.add_subtract_immediate.imm12 = narrowed;
126 mem.writeIntLittle(u32, code, inst.toU32());148 mem.writeIntLittle(u32, &buffer, inst.toU32());
127 } else {149 } else {
128 var inst = aarch64.Instruction{150 var inst = aarch64.Instruction{
129 .load_store_register = mem.bytesToValue(meta.TagPayload(151 .load_store_register = mem.bytesToValue(meta.TagPayload(
130 aarch64.Instruction,152 aarch64.Instruction,
131 aarch64.Instruction.load_store_register,153 aarch64.Instruction.load_store_register,
132 ), code),154 ), &buffer),
133 };155 };
134 const offset: u12 = blk: {156 const offset: u12 = blk: {
135 if (inst.load_store_register.size == 0) {157 if (inst.load_store_register.size == 0) {
136 if (inst.load_store_register.v == 1) {158 if (inst.load_store_register.v == 1) {
137 // 128-bit SIMD is scaled by 16.159 // 128-bit SIMD is scaled by 16.
138 break :blk try math.divExact(u12, narrowed, 16);160 break :blk @divExact(narrowed, 16);
139 }161 }
140 // Otherwise, 8-bit SIMD or ldrb.162 // Otherwise, 8-bit SIMD or ldrb.
141 break :blk narrowed;163 break :blk narrowed;
142 } else {164 } else {
143 const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size);165 const denom: u4 = math.powi(u4, 2, inst.load_store_register.size) catch unreachable;
144 break :blk try math.divExact(u12, narrowed, denom);166 break :blk @divExact(narrowed, denom);
145 }167 }
146 };168 };
147 inst.load_store_register.offset = offset;169 inst.load_store_register.offset = offset;
148 mem.writeIntLittle(u32, code, inst.toU32());170 mem.writeIntLittle(u32, &buffer, inst.toU32());
149 }171 }
150 },172 },
151 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {173 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {
...@@ -154,11 +176,12 @@ fn resolveAarch64(self: Relocation, source_addr: u64, target_addr: u64, macho_fi...@@ -154,11 +176,12 @@ fn resolveAarch64(self: Relocation, source_addr: u64, target_addr: u64, macho_fi
154 .load_store_register = mem.bytesToValue(meta.TagPayload(176 .load_store_register = mem.bytesToValue(meta.TagPayload(
155 aarch64.Instruction,177 aarch64.Instruction,
156 aarch64.Instruction.load_store_register,178 aarch64.Instruction.load_store_register,
157 ), code),179 ), &buffer),
158 };180 };
159 const offset = try math.divExact(u12, narrowed, 8);181 const offset = @divExact(narrowed, 8);
160 inst.load_store_register.offset = offset;182 inst.load_store_register.offset = offset;
161 mem.writeIntLittle(u32, code, inst.toU32());183 mem.writeIntLittle(u32, &buffer, inst.toU32());
184 log.debug("HMM = {x}", .{std.fmt.fmtSliceHexLower(&buffer)});
162 },185 },
163 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {186 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
164 const RegInfo = struct {187 const RegInfo = struct {
...@@ -167,11 +190,11 @@ fn resolveAarch64(self: Relocation, source_addr: u64, target_addr: u64, macho_fi...@@ -167,11 +190,11 @@ fn resolveAarch64(self: Relocation, source_addr: u64, target_addr: u64, macho_fi
167 size: u2,190 size: u2,
168 };191 };
169 const reg_info: RegInfo = blk: {192 const reg_info: RegInfo = blk: {
170 if (isArithmeticOp(code)) {193 if (isArithmeticOp(&buffer)) {
171 const inst = mem.bytesToValue(meta.TagPayload(194 const inst = mem.bytesToValue(meta.TagPayload(
172 aarch64.Instruction,195 aarch64.Instruction,
173 aarch64.Instruction.add_subtract_immediate,196 aarch64.Instruction.add_subtract_immediate,
174 ), code);197 ), &buffer);
175 break :blk .{198 break :blk .{
176 .rd = inst.rd,199 .rd = inst.rd,
177 .rn = inst.rn,200 .rn = inst.rn,
...@@ -181,7 +204,7 @@ fn resolveAarch64(self: Relocation, source_addr: u64, target_addr: u64, macho_fi...@@ -181,7 +204,7 @@ fn resolveAarch64(self: Relocation, source_addr: u64, target_addr: u64, macho_fi
181 const inst = mem.bytesToValue(meta.TagPayload(204 const inst = mem.bytesToValue(meta.TagPayload(
182 aarch64.Instruction,205 aarch64.Instruction,
183 aarch64.Instruction.load_store_register,206 aarch64.Instruction.load_store_register,
184 ), code);207 ), &buffer);
185 break :blk .{208 break :blk .{
186 .rd = inst.rt,209 .rd = inst.rt,
187 .rn = inst.rn,210 .rn = inst.rn,
...@@ -190,20 +213,7 @@ fn resolveAarch64(self: Relocation, source_addr: u64, target_addr: u64, macho_fi...@@ -190,20 +213,7 @@ fn resolveAarch64(self: Relocation, source_addr: u64, target_addr: u64, macho_fi
190 }213 }
191 };214 };
192 const narrowed = @truncate(u12, @intCast(u64, target_addr));215 const narrowed = @truncate(u12, @intCast(u64, target_addr));
193 var inst = if (macho_file.tlv_ptr_entries_table.contains(self.target)) blk: {216 var inst = aarch64.Instruction{
194 const offset = try math.divExact(u12, narrowed, 8);
195 break :blk aarch64.Instruction{
196 .load_store_register = .{
197 .rt = reg_info.rd,
198 .rn = reg_info.rn,
199 .offset = offset,
200 .opc = 0b01,
201 .op1 = 0b01,
202 .v = 0,
203 .size = reg_info.size,
204 },
205 };
206 } else aarch64.Instruction{
207 .add_subtract_immediate = .{217 .add_subtract_immediate = .{
208 .rd = reg_info.rd,218 .rd = reg_info.rd,
209 .rn = reg_info.rn,219 .rn = reg_info.rn,
...@@ -214,61 +224,81 @@ fn resolveAarch64(self: Relocation, source_addr: u64, target_addr: u64, macho_fi...@@ -214,61 +224,81 @@ fn resolveAarch64(self: Relocation, source_addr: u64, target_addr: u64, macho_fi
214 .sf = @truncate(u1, reg_info.size),224 .sf = @truncate(u1, reg_info.size),
215 },225 },
216 };226 };
217 mem.writeIntLittle(u32, code, inst.toU32());227 mem.writeIntLittle(u32, &buffer, inst.toU32());
218 },228 },
219 .ARM64_RELOC_POINTER_TO_GOT => {229 .ARM64_RELOC_POINTER_TO_GOT => {
220 const result = math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr)) orelse230 const result = math.cast(
221 return error.Overflow;231 i32,
222 mem.writeIntLittle(u32, code, @bitCast(u32, result));232 @intCast(i64, target_addr) - @intCast(i64, source_addr),
223 },233 ) orelse return error.Overflow;
224 .ARM64_RELOC_UNSIGNED => {234 mem.writeIntLittle(u32, &buffer, @bitCast(u32, result));
225 switch (self.length) {
226 2 => mem.writeIntLittle(u32, code, @truncate(u32, @bitCast(u64, target_addr))),
227 3 => mem.writeIntLittle(u64, code, target_addr),
228 else => unreachable,
229 }
230 },235 },
231 .ARM64_RELOC_SUBTRACTOR => unreachable,236 .ARM64_RELOC_SUBTRACTOR => unreachable,
232 .ARM64_RELOC_ADDEND => unreachable,237 .ARM64_RELOC_ADDEND => unreachable,
238 .ARM64_RELOC_UNSIGNED => unreachable,
233 }239 }
240 try macho_file.base.file.?.pwriteAll(&buffer, base_offset + self.offset);
234}241}
235242
236fn resolveX8664(self: Relocation, source_addr: u64, target_addr: u64, code: []u8) !void {243fn resolveX8664(
244 self: Relocation,
245 macho_file: *MachO,
246 source_addr: u64,
247 target_addr: i64,
248 base_offset: u64,
249) !void {
237 const rel_type = @intToEnum(macho.reloc_type_x86_64, self.@"type");250 const rel_type = @intToEnum(macho.reloc_type_x86_64, self.@"type");
238 switch (rel_type) {251 var buffer: [@sizeOf(u64)]u8 = undefined;
239 .X86_64_RELOC_BRANCH,252 const code = blk: {
240 .X86_64_RELOC_GOT,253 switch (rel_type) {
241 .X86_64_RELOC_GOT_LOAD,254 .X86_64_RELOC_BRANCH,
242 .X86_64_RELOC_TLV,255 .X86_64_RELOC_GOT,
243 => {256 .X86_64_RELOC_GOT_LOAD,
244 const displacement = math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4) orelse257 .X86_64_RELOC_TLV,
245 return error.Overflow;258 => {
246 mem.writeIntLittle(u32, code, @bitCast(u32, displacement));259 const displacement = math.cast(
247 },260 i32,
248 .X86_64_RELOC_SIGNED,261 @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4,
249 .X86_64_RELOC_SIGNED_1,262 ) orelse return error.Overflow;
250 .X86_64_RELOC_SIGNED_2,263 mem.writeIntLittle(u32, buffer[0..4], @bitCast(u32, displacement));
251 .X86_64_RELOC_SIGNED_4,264 break :blk buffer[0..4];
252 => {265 },
253 const correction: u3 = switch (rel_type) {266 .X86_64_RELOC_SIGNED,
254 .X86_64_RELOC_SIGNED => 0,267 .X86_64_RELOC_SIGNED_1,
255 .X86_64_RELOC_SIGNED_1 => 1,268 .X86_64_RELOC_SIGNED_2,
256 .X86_64_RELOC_SIGNED_2 => 2,269 .X86_64_RELOC_SIGNED_4,
257 .X86_64_RELOC_SIGNED_4 => 4,270 => {
258 else => unreachable,271 const correction: u3 = switch (rel_type) {
259 };272 .X86_64_RELOC_SIGNED => 0,
260 const displacement = math.cast(i32, target_addr - @intCast(i64, source_addr + correction + 4)) orelse273 .X86_64_RELOC_SIGNED_1 => 1,
261 return error.Overflow;274 .X86_64_RELOC_SIGNED_2 => 2,
262 mem.writeIntLittle(u32, code, @bitCast(u32, displacement));275 .X86_64_RELOC_SIGNED_4 => 4,
263 },276 else => unreachable,
264 .X86_64_RELOC_UNSIGNED => {277 };
265 switch (self.length) {278 const displacement = math.cast(
266 2 => mem.writeIntLittle(u32, code, @truncate(u32, @bitCast(u64, target_addr))),279 i32,
267 3 => mem.writeIntLittle(u64, code, target_addr),280 target_addr - @intCast(i64, source_addr + correction + 4),
268 }281 ) orelse return error.Overflow;
269 },282 mem.writeIntLittle(u32, buffer[0..4], @bitCast(u32, displacement));
270 .X86_64_RELOC_SUBTRACTOR => unreachable,283 break :blk buffer[0..4];
271 }284 },
285 .X86_64_RELOC_UNSIGNED => {
286 switch (self.length) {
287 2 => {
288 mem.writeIntLittle(u32, buffer[0..4], @truncate(u32, @bitCast(u64, target_addr)));
289 break :blk buffer[0..4];
290 },
291 3 => {
292 mem.writeIntLittle(u64, buffer[0..8], @bitCast(u64, target_addr));
293 break :blk &buffer;
294 },
295 else => unreachable,
296 }
297 },
298 .X86_64_RELOC_SUBTRACTOR => unreachable,
299 }
300 };
301 try macho_file.base.file.?.pwriteAll(code, base_offset + self.offset);
272}302}
273303
274inline fn isArithmeticOp(inst: *const [4]u8) bool {304inline fn isArithmeticOp(inst: *const [4]u8) bool {
src/link/MachO/dead_strip.zig+3-3
...@@ -233,7 +233,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac...@@ -233,7 +233,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
233 if (sym.n_desc != MachO.N_DESC_GCED) continue;233 if (sym.n_desc != MachO.N_DESC_GCED) continue;
234234
235 // TODO tombstone235 // TODO tombstone
236 const atom = entry.getAtom(macho_file);236 const atom = entry.getAtom(macho_file).?;
237 const match = sym.n_sect - 1;237 const match = sym.n_sect - 1;
238 removeAtomFromSection(atom, match, macho_file);238 removeAtomFromSection(atom, match, macho_file);
239 _ = try gc_sections.put(match, {});239 _ = try gc_sections.put(match, {});
...@@ -245,7 +245,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac...@@ -245,7 +245,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
245 if (sym.n_desc != MachO.N_DESC_GCED) continue;245 if (sym.n_desc != MachO.N_DESC_GCED) continue;
246246
247 // TODO tombstone247 // TODO tombstone
248 const atom = entry.getAtom(macho_file);248 const atom = entry.getAtom(macho_file).?;
249 const match = sym.n_sect - 1;249 const match = sym.n_sect - 1;
250 removeAtomFromSection(atom, match, macho_file);250 removeAtomFromSection(atom, match, macho_file);
251 _ = try gc_sections.put(match, {});251 _ = try gc_sections.put(match, {});
...@@ -257,7 +257,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac...@@ -257,7 +257,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
257 if (sym.n_desc != MachO.N_DESC_GCED) continue;257 if (sym.n_desc != MachO.N_DESC_GCED) continue;
258258
259 // TODO tombstone259 // TODO tombstone
260 const atom = entry.getAtom(macho_file);260 const atom = entry.getAtom(macho_file).?;
261 const match = sym.n_sect - 1;261 const match = sym.n_sect - 1;
262 removeAtomFromSection(atom, match, macho_file);262 removeAtomFromSection(atom, match, macho_file);
263 _ = try gc_sections.put(match, {});263 _ = try gc_sections.put(match, {});
src/link/MachO/zld.zig+329-1
...@@ -7,14 +7,19 @@ const macho = std.macho;...@@ -7,14 +7,19 @@ const macho = std.macho;
7const math = std.math;7const math = std.math;
8const mem = std.mem;8const mem = std.mem;
99
10const aarch64 = @import("../../arch/aarch64/bits.zig");
11const bind = @import("bind.zig");
10const link = @import("../../link.zig");12const link = @import("../../link.zig");
11const trace = @import("../../tracy.zig").trace;13const trace = @import("../../tracy.zig").trace;
1214
15const Atom = MachO.Atom;
13const Cache = @import("../../Cache.zig");16const Cache = @import("../../Cache.zig");
14const CodeSignature = @import("CodeSignature.zig");17const CodeSignature = @import("CodeSignature.zig");
15const Compilation = @import("../../Compilation.zig");18const Compilation = @import("../../Compilation.zig");
16const Dylib = @import("Dylib.zig");19const Dylib = @import("Dylib.zig");
17const MachO = @import("../MachO.zig");20const MachO = @import("../MachO.zig");
21const SymbolWithLoc = MachO.SymbolWithLoc;
22const Trie = @import("Trie.zig");
1823
19const dead_strip = @import("dead_strip.zig");24const dead_strip = @import("dead_strip.zig");
2025
...@@ -545,7 +550,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -545,7 +550,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
545 const lc_writer = lc_buffer.writer();550 const lc_writer = lc_buffer.writer();
546 var ncmds: u32 = 0;551 var ncmds: u32 = 0;
547552
548 try macho_file.writeLinkeditSegmentData(&ncmds, lc_writer);553 try writeLinkeditSegmentData(macho_file, &ncmds, lc_writer);
549554
550 // If the last section of __DATA segment is zerofill section, we need to ensure555 // If the last section of __DATA segment is zerofill section, we need to ensure
551 // that the free space between the end of the last non-zerofill section of __DATA556 // that the free space between the end of the last non-zerofill section of __DATA
...@@ -952,3 +957,326 @@ fn allocateSymbols(macho_file: *MachO) !void {...@@ -952,3 +957,326 @@ fn allocateSymbols(macho_file: *MachO) !void {
952 }957 }
953 }958 }
954}959}
960
961fn writeLinkeditSegmentData(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
962 const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
963 seg.filesize = 0;
964 seg.vmsize = 0;
965
966 try writeDyldInfoData(macho_file, ncmds, lc_writer);
967 try macho_file.writeFunctionStarts(ncmds, lc_writer);
968 try macho_file.writeDataInCode(ncmds, lc_writer);
969 try macho_file.writeSymtabs(ncmds, lc_writer);
970
971 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, macho_file.page_size);
972}
973
974fn writeDyldInfoData(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
975 const tracy = trace(@src());
976 defer tracy.end();
977
978 const gpa = macho_file.base.allocator;
979
980 var rebase_pointers = std.ArrayList(bind.Pointer).init(gpa);
981 defer rebase_pointers.deinit();
982 var bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
983 defer bind_pointers.deinit();
984 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
985 defer lazy_bind_pointers.deinit();
986
987 const slice = macho_file.sections.slice();
988 for (slice.items(.last_atom)) |last_atom, sect_id| {
989 var atom = last_atom orelse continue;
990 const segment_index = slice.items(.segment_index)[sect_id];
991 const header = slice.items(.header)[sect_id];
992
993 if (mem.eql(u8, header.segName(), "__TEXT")) continue; // __TEXT is non-writable
994
995 log.debug("dyld info for {s},{s}", .{ header.segName(), header.sectName() });
996
997 const seg = macho_file.segments.items[segment_index];
998
999 while (true) {
1000 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(macho_file) });
1001 const sym = atom.getSymbol(macho_file);
1002 const base_offset = sym.n_value - seg.vmaddr;
1003
1004 for (atom.rebases.items) |offset| {
1005 log.debug(" | rebase at {x}", .{base_offset + offset});
1006 try rebase_pointers.append(.{
1007 .offset = base_offset + offset,
1008 .segment_id = segment_index,
1009 });
1010 }
1011
1012 for (atom.bindings.items) |binding| {
1013 const bind_sym = macho_file.getSymbol(binding.target);
1014 const bind_sym_name = macho_file.getSymbolName(binding.target);
1015 const dylib_ordinal = @divTrunc(
1016 @bitCast(i16, bind_sym.n_desc),
1017 macho.N_SYMBOL_RESOLVER,
1018 );
1019 var flags: u4 = 0;
1020 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
1021 binding.offset + base_offset,
1022 bind_sym_name,
1023 dylib_ordinal,
1024 });
1025 if (bind_sym.weakRef()) {
1026 log.debug(" | marking as weak ref ", .{});
1027 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
1028 }
1029 try bind_pointers.append(.{
1030 .offset = binding.offset + base_offset,
1031 .segment_id = segment_index,
1032 .dylib_ordinal = dylib_ordinal,
1033 .name = bind_sym_name,
1034 .bind_flags = flags,
1035 });
1036 }
1037
1038 for (atom.lazy_bindings.items) |binding| {
1039 const bind_sym = macho_file.getSymbol(binding.target);
1040 const bind_sym_name = macho_file.getSymbolName(binding.target);
1041 const dylib_ordinal = @divTrunc(
1042 @bitCast(i16, bind_sym.n_desc),
1043 macho.N_SYMBOL_RESOLVER,
1044 );
1045 var flags: u4 = 0;
1046 log.debug(" | lazy bind at {x} import('{s}') ord({d})", .{
1047 binding.offset + base_offset,
1048 bind_sym_name,
1049 dylib_ordinal,
1050 });
1051 if (bind_sym.weakRef()) {
1052 log.debug(" | marking as weak ref ", .{});
1053 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
1054 }
1055 try lazy_bind_pointers.append(.{
1056 .offset = binding.offset + base_offset,
1057 .segment_id = segment_index,
1058 .dylib_ordinal = dylib_ordinal,
1059 .name = bind_sym_name,
1060 .bind_flags = flags,
1061 });
1062 }
1063
1064 if (atom.prev) |prev| {
1065 atom = prev;
1066 } else break;
1067 }
1068 }
1069
1070 var trie: Trie = .{};
1071 defer trie.deinit(gpa);
1072
1073 {
1074 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
1075 log.debug("generating export trie", .{});
1076
1077 const text_segment = macho_file.segments.items[macho_file.text_segment_cmd_index.?];
1078 const base_address = text_segment.vmaddr;
1079
1080 if (macho_file.base.options.output_mode == .Exe) {
1081 for (&[_]SymbolWithLoc{
1082 try macho_file.getEntryPoint(),
1083 macho_file.getGlobal("__mh_execute_header").?,
1084 }) |global| {
1085 const sym = macho_file.getSymbol(global);
1086 const sym_name = macho_file.getSymbolName(global);
1087 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
1088 try trie.put(gpa, .{
1089 .name = sym_name,
1090 .vmaddr_offset = sym.n_value - base_address,
1091 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
1092 });
1093 }
1094 } else {
1095 assert(macho_file.base.options.output_mode == .Lib);
1096 for (macho_file.globals.items) |global| {
1097 const sym = macho_file.getSymbol(global);
1098
1099 if (sym.undf()) continue;
1100 if (!sym.ext()) continue;
1101 if (sym.n_desc == MachO.N_DESC_GCED) continue;
1102
1103 const sym_name = macho_file.getSymbolName(global);
1104 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
1105 try trie.put(gpa, .{
1106 .name = sym_name,
1107 .vmaddr_offset = sym.n_value - base_address,
1108 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
1109 });
1110 }
1111 }
1112
1113 try trie.finalize(gpa);
1114 }
1115
1116 const link_seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1117 const rebase_off = mem.alignForwardGeneric(u64, link_seg.fileoff, @alignOf(u64));
1118 assert(rebase_off == link_seg.fileoff);
1119 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);
1120 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size });
1121
1122 const bind_off = mem.alignForwardGeneric(u64, rebase_off + rebase_size, @alignOf(u64));
1123 const bind_size = try bind.bindInfoSize(bind_pointers.items);
1124 log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size });
1125
1126 const lazy_bind_off = mem.alignForwardGeneric(u64, bind_off + bind_size, @alignOf(u64));
1127 const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);
1128 log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{ lazy_bind_off, lazy_bind_off + lazy_bind_size });
1129
1130 const export_off = mem.alignForwardGeneric(u64, lazy_bind_off + lazy_bind_size, @alignOf(u64));
1131 const export_size = trie.size;
1132 log.debug("writing export trie from 0x{x} to 0x{x}", .{ export_off, export_off + export_size });
1133
1134 const needed_size = export_off + export_size - rebase_off;
1135 link_seg.filesize = needed_size;
1136
1137 var buffer = try gpa.alloc(u8, math.cast(usize, needed_size) orelse return error.Overflow);
1138 defer gpa.free(buffer);
1139 mem.set(u8, buffer, 0);
1140
1141 var stream = std.io.fixedBufferStream(buffer);
1142 const writer = stream.writer();
1143
1144 try bind.writeRebaseInfo(rebase_pointers.items, writer);
1145 try stream.seekTo(bind_off - rebase_off);
1146
1147 try bind.writeBindInfo(bind_pointers.items, writer);
1148 try stream.seekTo(lazy_bind_off - rebase_off);
1149
1150 try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer);
1151 try stream.seekTo(export_off - rebase_off);
1152
1153 _ = try trie.write(writer);
1154
1155 log.debug("writing dyld info from 0x{x} to 0x{x}", .{
1156 rebase_off,
1157 rebase_off + needed_size,
1158 });
1159
1160 try macho_file.base.file.?.pwriteAll(buffer, rebase_off);
1161 const start = math.cast(usize, lazy_bind_off - rebase_off) orelse return error.Overflow;
1162 const end = start + (math.cast(usize, lazy_bind_size) orelse return error.Overflow);
1163 try populateLazyBindOffsetsInStubHelper(macho_file, buffer[start..end]);
1164
1165 try lc_writer.writeStruct(macho.dyld_info_command{
1166 .cmd = .DYLD_INFO_ONLY,
1167 .cmdsize = @sizeOf(macho.dyld_info_command),
1168 .rebase_off = @intCast(u32, rebase_off),
1169 .rebase_size = @intCast(u32, rebase_size),
1170 .bind_off = @intCast(u32, bind_off),
1171 .bind_size = @intCast(u32, bind_size),
1172 .weak_bind_off = 0,
1173 .weak_bind_size = 0,
1174 .lazy_bind_off = @intCast(u32, lazy_bind_off),
1175 .lazy_bind_size = @intCast(u32, lazy_bind_size),
1176 .export_off = @intCast(u32, export_off),
1177 .export_size = @intCast(u32, export_size),
1178 });
1179 ncmds.* += 1;
1180}
1181
1182fn populateLazyBindOffsetsInStubHelper(macho_file: *MachO, buffer: []const u8) !void {
1183 const gpa = macho_file.base.allocator;
1184
1185 const stub_helper_section_index = macho_file.stub_helper_section_index orelse return;
1186 if (macho_file.stub_helper_preamble_atom == null) return;
1187
1188 const section = macho_file.sections.get(stub_helper_section_index);
1189 const last_atom = section.last_atom orelse return;
1190 if (last_atom == macho_file.stub_helper_preamble_atom.?) return; // TODO is this a redundant check?
1191
1192 var table = std.AutoHashMap(i64, *Atom).init(gpa);
1193 defer table.deinit();
1194
1195 {
1196 var stub_atom = last_atom;
1197 var laptr_atom = macho_file.sections.items(.last_atom)[macho_file.la_symbol_ptr_section_index.?].?;
1198 const base_addr = blk: {
1199 const seg = macho_file.segments.items[macho_file.data_segment_cmd_index.?];
1200 break :blk seg.vmaddr;
1201 };
1202
1203 while (true) {
1204 const laptr_off = blk: {
1205 const sym = laptr_atom.getSymbol(macho_file);
1206 break :blk @intCast(i64, sym.n_value - base_addr);
1207 };
1208 try table.putNoClobber(laptr_off, stub_atom);
1209 if (laptr_atom.prev) |prev| {
1210 laptr_atom = prev;
1211 stub_atom = stub_atom.prev.?;
1212 } else break;
1213 }
1214 }
1215
1216 var stream = std.io.fixedBufferStream(buffer);
1217 var reader = stream.reader();
1218 var offsets = std.ArrayList(struct { sym_offset: i64, offset: u32 }).init(gpa);
1219 try offsets.append(.{ .sym_offset = undefined, .offset = 0 });
1220 defer offsets.deinit();
1221 var valid_block = false;
1222
1223 while (true) {
1224 const inst = reader.readByte() catch |err| switch (err) {
1225 error.EndOfStream => break,
1226 };
1227 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
1228
1229 switch (opcode) {
1230 macho.BIND_OPCODE_DO_BIND => {
1231 valid_block = true;
1232 },
1233 macho.BIND_OPCODE_DONE => {
1234 if (valid_block) {
1235 const offset = try stream.getPos();
1236 try offsets.append(.{ .sym_offset = undefined, .offset = @intCast(u32, offset) });
1237 }
1238 valid_block = false;
1239 },
1240 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
1241 var next = try reader.readByte();
1242 while (next != @as(u8, 0)) {
1243 next = try reader.readByte();
1244 }
1245 },
1246 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
1247 var inserted = offsets.pop();
1248 inserted.sym_offset = try std.leb.readILEB128(i64, reader);
1249 try offsets.append(inserted);
1250 },
1251 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
1252 _ = try std.leb.readULEB128(u64, reader);
1253 },
1254 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
1255 _ = try std.leb.readILEB128(i64, reader);
1256 },
1257 else => {},
1258 }
1259 }
1260
1261 const header = macho_file.sections.items(.header)[stub_helper_section_index];
1262 const stub_offset: u4 = switch (macho_file.base.options.target.cpu.arch) {
1263 .x86_64 => 1,
1264 .aarch64 => 2 * @sizeOf(u32),
1265 else => unreachable,
1266 };
1267 var buf: [@sizeOf(u32)]u8 = undefined;
1268 _ = offsets.pop();
1269
1270 while (offsets.popOrNull()) |bind_offset| {
1271 const atom = table.get(bind_offset.sym_offset).?;
1272 const sym = atom.getSymbol(macho_file);
1273 const file_offset = header.offset + sym.n_value - header.addr + stub_offset;
1274 mem.writeIntLittle(u32, &buf, bind_offset.offset);
1275 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{
1276 bind_offset.offset,
1277 atom.getName(macho_file),
1278 file_offset,
1279 });
1280 try macho_file.base.file.?.pwriteAll(&buf, file_offset);
1281 }
1282}