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 {
680680 break :blk offset;
681681 };
682682 // 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 }).?;
684684 const target = macho_file.getGlobalByIndex(relocation.sym_index);
685 try atom.relocs.append(emit.bin_file.allocator, .{
686 .offset = offset,
685 try atom.addRelocation(macho_file, .{
686 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
687687 .target = target,
688 .offset = offset,
688689 .addend = 0,
689 .subtractor = null,
690690 .pcrel = true,
691691 .length = 2,
692 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
693692 });
694693 } else {
695694 return emit.fail("Implement call_extern for linking backends != MachO", .{});
......@@ -882,13 +881,13 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
882881 }
883882
884883 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
885 const atom = macho_file.atom_by_index_table.get(data.atom_index).?;
886 // Page reloc for adrp instruction.
887 try atom.relocs.append(emit.bin_file.allocator, .{
888 .offset = offset,
884 const atom = macho_file.getAtomForSymbol(.{ .sym_index = data.atom_index, .file = null }).?;
885 // TODO this causes segfault in stage1
886 // try atom.addRelocations(macho_file, 2, .{
887 try atom.addRelocation(macho_file, .{
889888 .target = .{ .sym_index = data.sym_index, .file = null },
889 .offset = offset,
890890 .addend = 0,
891 .subtractor = null,
892891 .pcrel = true,
893892 .length = 2,
894893 .@"type" = switch (tag) {
......@@ -901,12 +900,10 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
901900 else => unreachable,
902901 },
903902 });
904 // Pageoff reloc for adrp instruction.
905 try atom.relocs.append(emit.bin_file.allocator, .{
906 .offset = offset + 4,
903 try atom.addRelocation(macho_file, .{
907904 .target = .{ .sym_index = data.sym_index, .file = null },
905 .offset = offset + 4,
908906 .addend = 0,
909 .subtractor = null,
910907 .pcrel = false,
911908 .length = 2,
912909 .@"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 {
996996 );
997997
998998 const end_offset = emit.code.items.len;
999 const gpa = emit.bin_file.allocator;
1000999
10011000 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
10021001 const reloc_type = switch (ops.flags) {
......@@ -1004,19 +1003,17 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
10041003 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
10051004 else => unreachable,
10061005 };
1007 const atom = macho_file.atom_by_index_table.get(relocation.atom_index).?;
1008 log.debug("adding reloc of type {} to local @{d}", .{ reloc_type, relocation.sym_index });
1009 try atom.relocs.append(gpa, .{
1010 .offset = @intCast(u32, end_offset - 4),
1006 const atom = macho_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
1007 try atom.addRelocation(macho_file, .{
1008 .@"type" = reloc_type,
10111009 .target = .{ .sym_index = relocation.sym_index, .file = null },
1010 .offset = @intCast(u32, end_offset - 4),
10121011 .addend = 0,
1013 .subtractor = null,
10141012 .pcrel = true,
10151013 .length = 2,
1016 .@"type" = reloc_type,
10171014 });
10181015 } 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 }).?;
10201017 try atom.addRelocation(coff_file, .{
10211018 .@"type" = switch (ops.flags) {
10221019 0b00 => .got,
......@@ -1145,20 +1142,19 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
11451142
11461143 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
11471144 // 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 }).?;
11491146 const target = macho_file.getGlobalByIndex(relocation.sym_index);
1150 try atom.relocs.append(emit.bin_file.allocator, .{
1151 .offset = offset,
1147 try atom.addRelocation(macho_file, .{
1148 .@"type" = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
11521149 .target = target,
1150 .offset = offset,
11531151 .addend = 0,
1154 .subtractor = null,
11551152 .pcrel = true,
11561153 .length = 2,
1157 .@"type" = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
11581154 });
11591155 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
11601156 // 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 }).?;
11621158 const target = coff_file.getGlobalByIndex(relocation.sym_index);
11631159 try atom.addRelocation(coff_file, .{
11641160 .@"type" = .direct,
src/link/Coff.zig+1-1
......@@ -1527,7 +1527,7 @@ pub fn getDeclVAddr(
15271527 assert(self.llvm_object == null);
15281528 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 }).?;
15311531 const target = SymbolWithLoc{ .sym_index = decl.link.coff.sym_index, .file = null };
15321532 try atom.addRelocation(self, .{
15331533 .@"type" = .direct,
src/link/MachO.zig+781-486
......@@ -40,7 +40,6 @@ const Liveness = @import("../Liveness.zig");
4040const LlvmObject = @import("../codegen/llvm.zig").Object;
4141const Module = @import("../Module.zig");
4242const Relocation = @import("MachO/Relocation.zig");
43const RelocationTable = Relocation.Table;
4443const StringTable = @import("strtab.zig").StringTable;
4544const Trie = @import("MachO/Trie.zig");
4645const Type = @import("../type.zig").Type;
......@@ -196,6 +195,21 @@ unnamed_const_atoms: UnnamedConstTable = .{},
196195/// this will be a table indexed by index into the list of Atoms.
197196relocs: 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
199213/// Table of Decls that are currently alive.
200214/// We store them here so that we can properly dispose of any allocated
201215/// memory within the atom in the incremental linker.
......@@ -215,8 +229,8 @@ const Entry = struct {
215229 return macho_file.getSymbolPtr(.{ .sym_index = entry.sym_index, .file = null });
216230 }
217231
218 pub fn getAtom(entry: Entry, macho_file: *MachO) *Atom {
219 return macho_file.getAtomForSymbol(.{ .sym_index = entry.sym_index, .file = null }).?;
232 pub fn getAtom(entry: Entry, macho_file: *MachO) ?*Atom {
233 return macho_file.getAtomForSymbol(.{ .sym_index = entry.sym_index, .file = null });
220234 }
221235
222236 pub fn getName(entry: Entry, macho_file: *MachO) []const u8 {
......@@ -224,7 +238,10 @@ const Entry = struct {
224238 }
225239};
226240
241const BindingTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Atom.Binding));
227242const 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
229246const PendingUpdate = union(enum) {
230247 resolve_undef: u32,
......@@ -238,6 +255,16 @@ pub const SymbolWithLoc = struct {
238255
239256 // null means it's a synthetic global.
240257 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 }
241268};
242269
243270/// When allocating, the ideal_capacity is calculated by
......@@ -436,7 +463,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
436463 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
437464 try self.resolveLibSystem(arena, comp, &.{}, &libs);
438465
439 const id_symlink_basename = "zld.id";
466 const id_symlink_basename = "link.id";
440467
441468 const cache_dir_handle = module.zig_cache_artifact_directory.handle;
442469 var man: Cache.Manifest = undefined;
......@@ -517,14 +544,19 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
517544
518545 try self.allocateSpecialSymbols();
519546
547 {
548 var it = self.relocs.keyIterator();
549 while (it.next()) |atom| {
550 try atom.*.resolveRelocations(self);
551 }
552 }
553
520554 if (build_options.enable_logging) {
521555 self.logSymtab();
522556 self.logSections();
523557 self.logAtoms();
524558 }
525559
526 try self.writeAtoms();
527
528560 var lc_buffer = std.ArrayList(u8).init(arena);
529561 const lc_writer = lc_buffer.writer();
530562 var ncmds: u32 = 0;
......@@ -1179,84 +1211,49 @@ pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32
11791211 return atom;
11801212}
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
11831222 const sym = atom.getSymbol(self);
11841223 const section = self.sections.get(sym.n_sect - 1);
11851224 const file_offset = section.header.offset + sym.n_value - section.header.addr;
1186 try atom.resolveRelocs(self);
11871225 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);
11891228}
11901229
1191// fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {
1192// // TODO: reverse-lookup might come in handy here
1193// var it = self.relocs.valueIterator();
1194// while (it.next()) |relocs| {
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;
1230fn writePtrWidthAtom(self: *MachO, atom: *Atom) !void {
1231 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
1232 try self.writeAtom(atom, &buffer);
1233}
12381234
1239// if (reloc.pcrel) {
1240// const source_vaddr = source_sym.value + reloc.offset;
1241// const disp =
1242// @intCast(i32, target_vaddr_with_addend) - @intCast(i32, source_vaddr) - 4;
1243// try self.base.file.?.pwriteAll(mem.asBytes(&disp), file_offset + reloc.offset);
1244// continue;
1245// }
1235fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {
1236 // TODO: reverse-lookup might come in handy here
1237 var it = self.relocs.valueIterator();
1238 while (it.next()) |relocs| {
1239 for (relocs.items) |*reloc| {
1240 if (!reloc.target.eql(target)) continue;
1241 reloc.dirty = true;
1242 }
1243 }
1244}
12461245
1247// switch (reloc.length) {
1248// 2 => try self.base.file.?.pwriteAll(
1249// mem.asBytes(&@truncate(u32, target_vaddr_with_addend)),
1250// file_offset + reloc.offset,
1251// ),
1252// 3 => try self.base.file.?.pwriteAll(
1253// mem.asBytes(&(target_vaddr_with_addend)),
1254// file_offset + reloc.offset,
1255// ),
1256// else => unreachable,
1257// }
1258// }
1259// }
1246fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void {
1247 var it = self.relocs.valueIterator();
1248 while (it.next()) |relocs| {
1249 for (relocs.items) |*reloc| {
1250 const target_atom = reloc.getTargetAtom(self) orelse continue;
1251 const target_sym = target_atom.getSymbol(self);
1252 if (target_sym.n_value < addr) continue;
1253 reloc.dirty = true;
1254 }
1255 }
1256}
12601257
12611258pub fn allocateSpecialSymbols(self: *MachO) !void {
12621259 for (&[_][]const u8{
......@@ -1277,74 +1274,92 @@ pub fn allocateSpecialSymbols(self: *MachO) !void {
12771274 }
12781275}
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
13061277pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
13071278 const gpa = self.base.allocator;
1279
13081280 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
13101297 const sym = atom.getSymbolPtr(self);
13111298 sym.n_type = macho.N_SECT;
13121299 sym.n_sect = self.got_section_index.? + 1;
13131300
1314 try atom.relocs.append(gpa, .{
1315 .offset = 0,
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 });
1301 if (self.mode == .incremental) {
1302 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
13271303
1328 const target_sym = self.getSymbol(target);
1329 if (target_sym.undf()) {
1330 const global = self.getGlobal(self.getSymbolName(target)).?;
1331 try atom.bindings.append(gpa, .{
1332 .target = global,
1304 log.debug("allocated GOT atom at 0x{x}", .{sym.n_value});
1305
1306 try atom.addRelocation(self, .{
1307 .@"type" = switch (self.base.options.target.cpu.arch) {
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,
13331313 .offset = 0,
1314 .addend = 0,
1315 .pcrel = false,
1316 .length = 3,
13341317 });
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 }
13351328 } else {
1336 try atom.rebases.append(gpa, 0);
1337 }
1329 try atom.relocs.append(gpa, .{
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);
1340 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
1343 const target_sym = self.getSymbol(target);
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
13441357 return atom;
13451358}
13461359
13471360pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
1361 assert(self.mode == .one_shot);
1362
13481363 const gpa = self.base.allocator;
13491364 const sym_index = try self.allocateSymbol();
13501365 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
......@@ -1368,14 +1383,9 @@ pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
13681383 .sectname = makeStaticString("__thread_ptrs"),
13691384 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
13701385 })).?;
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 }
13761386 sym.n_sect = gop.sect_id + 1;
13771387
1378 try self.allocateAtomCommon(atom);
1388 try self.addAtomToSection(atom);
13791389
13801390 return atom;
13811391}
......@@ -1385,17 +1395,36 @@ pub fn createDyldPrivateAtom(self: *MachO) !void {
13851395 if (self.dyld_private_atom != null) return;
13861396
13871397 const gpa = self.base.allocator;
1398
13881399 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
13901413 const sym = atom.getSymbolPtr(self);
13911414 sym.n_type = macho.N_SECT;
13921415 sym.n_sect = self.data_section_index.? + 1;
13931416 self.dyld_private_atom = atom;
13941417
1395 try self.allocateAtomCommon(atom);
1396
13971418 try self.managed_atoms.append(gpa, atom);
13981419 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 }
13991428}
14001429
14011430pub fn createStubHelperPreambleAtom(self: *MachO) !void {
......@@ -1415,118 +1444,196 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
14151444 else => unreachable,
14161445 };
14171446 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
14191460 const sym = atom.getSymbolPtr(self);
14201461 sym.n_type = macho.N_SECT;
14211462 sym.n_sect = self.stub_helper_section_index.? + 1;
14221463
14231464 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
14241470 switch (arch) {
14251471 .x86_64 => {
1426 try atom.relocs.ensureUnusedCapacity(self.base.allocator, 2);
14271472 // lea %r11, [rip + disp]
1428 atom.code.items[0] = 0x4c;
1429 atom.code.items[1] = 0x8d;
1430 atom.code.items[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 });
1473 code[0] = 0x4c;
1474 code[1] = 0x8d;
1475 code[2] = 0x1d;
14401476 // push %r11
1441 atom.code.items[7] = 0x41;
1442 atom.code.items[8] = 0x53;
1477 code[7] = 0x41;
1478 code[8] = 0x53;
14431479 // jmp [rip + disp]
1444 atom.code.items[9] = 0xff;
1445 atom.code.items[10] = 0x25;
1446 atom.relocs.appendAssumeCapacity(.{
1447 .offset = 11,
1448 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1449 .addend = 0,
1450 .subtractor = null,
1451 .pcrel = true,
1452 .length = 2,
1453 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_GOT),
1454 });
1480 code[9] = 0xff;
1481 code[10] = 0x25;
1482
1483 if (self.mode == .incremental) {
1484 try atom.addRelocations(self, 2, .{ .{
1485 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1486 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1487 .offset = 3,
1488 .addend = 0,
1489 .pcrel = true,
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 }
14551520 },
1521
14561522 .aarch64 => {
1457 try atom.relocs.ensureUnusedCapacity(self.base.allocator, 4);
14581523 // adrp x17, 0
1459 mem.writeIntLittle(u32, atom.code.items[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 });
1524 mem.writeIntLittle(u32, code[0..][0..4], aarch64.Instruction.adrp(.x17, 0).toU32());
14691525 // add x17, x17, 0
1470 mem.writeIntLittle(u32, atom.code.items[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 });
1526 mem.writeIntLittle(u32, code[4..][0..4], aarch64.Instruction.add(.x17, .x17, 0, false).toU32());
14801527 // 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(
14821529 .x16,
14831530 .x17,
14841531 aarch64.Register.sp,
14851532 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
14861533 ).toU32());
14871534 // adrp x16, 0
1488 mem.writeIntLittle(u32, atom.code.items[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 });
1535 mem.writeIntLittle(u32, code[12..][0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
14981536 // 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(
15001538 .x16,
15011539 .x16,
15021540 aarch64.Instruction.LoadStoreOffset.imm(0),
15031541 ).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 });
15131542 // 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 }
15151614 },
1615
15161616 else => unreachable,
15171617 }
15181618 self.stub_helper_preamble_atom = atom;
15191619
1520 try self.allocateAtomCommon(atom);
1521
15221620 try self.managed_atoms.append(gpa, atom);
15231621 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 }
15241631}
15251632
15261633pub fn createStubHelperAtom(self: *MachO) !*Atom {
15271634 const gpa = self.base.allocator;
15281635 const arch = self.base.options.target.cpu.arch;
1529 const stub_size: u4 = switch (arch) {
1636 const size: u4 = switch (arch) {
15301637 .x86_64 => 10,
15311638 .aarch64 => 3 * @sizeOf(u32),
15321639 else => unreachable,
......@@ -1537,52 +1644,92 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
15371644 else => unreachable,
15381645 };
15391646 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
15411660 const sym = atom.getSymbolPtr(self);
15421661 sym.n_type = macho.N_SECT;
15431662 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
15471668 switch (arch) {
15481669 .x86_64 => {
15491670 // pushq
1550 atom.code.items[0] = 0x68;
1671 code[0] = 0x68;
15511672 // Next 4 bytes 1..4 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
15521673 // jmpq
1553 atom.code.items[5] = 0xe9;
1554 atom.relocs.appendAssumeCapacity(.{
1555 .offset = 6,
1556 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
1557 .addend = 0,
1558 .subtractor = null,
1559 .pcrel = true,
1560 .length = 2,
1561 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1562 });
1674 code[5] = 0xe9;
1675
1676 if (self.mode == .incremental) {
1677 try atom.addRelocation(self, .{
1678 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1679 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
1680 .offset = 6,
1681 .addend = 0,
1682 .pcrel = true,
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 }
15631697 },
15641698 .aarch64 => {
15651699 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);
15671701 break :blk math.cast(u18, div_res) orelse return error.Overflow;
15681702 };
15691703 // 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(
15711705 .w16,
15721706 literal,
15731707 ).toU32());
15741708 // b disp
1575 mem.writeIntLittle(u32, atom.code.items[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 });
1709 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(0).toU32());
15851710 // 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 }
15861733 },
15871734 else => unreachable,
15881735 }
......@@ -1590,7 +1737,14 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
15901737 try self.managed_atoms.append(gpa, atom);
15911738 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
15951749 return atom;
15961750}
......@@ -1598,36 +1752,73 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
15981752pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !*Atom {
15991753 const gpa = self.base.allocator;
16001754 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
16021768 const sym = atom.getSymbolPtr(self);
16031769 sym.n_type = macho.N_SECT;
16041770 sym.n_sect = self.la_symbol_ptr_section_index.? + 1;
16051771
1606 try atom.relocs.append(gpa, .{
1607 .offset = 0,
1608 .target = .{ .sym_index = stub_sym_index, .file = null },
1609 .addend = 0,
1610 .subtractor = null,
1611 .pcrel = false,
1612 .length = 3,
1613 .@"type" = switch (self.base.options.target.cpu.arch) {
1614 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1615 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1616 else => unreachable,
1617 },
1618 });
1619 try atom.rebases.append(gpa, 0);
1620
1621 const global = self.getGlobal(self.getSymbolName(target)).?;
1622 try atom.lazy_bindings.append(gpa, .{
1623 .target = global,
1624 .offset = 0,
1625 });
1772 if (self.mode == .incremental) {
1773 try atom.addRelocation(self, .{
1774 .@"type" = switch (self.base.options.target.cpu.arch) {
1775 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1776 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1777 else => unreachable,
1778 },
1779 .target = .{ .sym_index = stub_sym_index, .file = null },
1780 .offset = 0,
1781 .addend = 0,
1782 .pcrel = false,
1783 .length = 3,
1784 });
1785 try atom.addRebase(self, 0);
1786 try atom.addLazyBinding(self, .{
1787 .target = self.getGlobal(self.getSymbolName(target)).?,
1788 .offset = 0,
1789 });
1790 } else {
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
16271812 try self.managed_atoms.append(gpa, atom);
16281813 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
16321823 return atom;
16331824}
......@@ -1640,62 +1831,112 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
16401831 .aarch64 => 2,
16411832 else => unreachable, // unhandled architecture type
16421833 };
1643 const stub_size: u4 = switch (arch) {
1834 const size: u4 = switch (arch) {
16441835 .x86_64 => 6,
16451836 .aarch64 => 3 * @sizeOf(u32),
16461837 else => unreachable, // unhandled architecture type
16471838 };
16481839 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
16501853 const sym = atom.getSymbolPtr(self);
16511854 sym.n_type = macho.N_SECT;
16521855 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
16541861 switch (arch) {
16551862 .x86_64 => {
16561863 // jmp
1657 atom.code.items[0] = 0xff;
1658 atom.code.items[1] = 0x25;
1659 try atom.relocs.append(gpa, .{
1660 .offset = 2,
1661 .target = .{ .sym_index = laptr_sym_index, .file = null },
1662 .addend = 0,
1663 .subtractor = null,
1664 .pcrel = true,
1665 .length = 2,
1666 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1667 });
1864 code[0] = 0xff;
1865 code[1] = 0x25;
1866
1867 if (self.mode == .incremental) {
1868 try atom.addRelocation(self, .{
1869 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1870 .target = .{ .sym_index = laptr_sym_index, .file = null },
1871 .offset = 2,
1872 .addend = 0,
1873 .pcrel = true,
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 }
16681887 },
16691888 .aarch64 => {
1670 try atom.relocs.ensureTotalCapacity(gpa, 2);
16711889 // adrp x16, pages
1672 mem.writeIntLittle(u32, atom.code.items[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 });
1890 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
16821891 // 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(
16841893 .x16,
16851894 .x16,
16861895 aarch64.Instruction.LoadStoreOffset.imm(0),
16871896 ).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 });
16971897 // 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 }
16991940 },
17001941 else => unreachable,
17011942 }
......@@ -1703,7 +1944,14 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
17031944 try self.managed_atoms.append(gpa, atom);
17041945 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
17081956 return atom;
17091957}
......@@ -1976,6 +2224,7 @@ pub fn resolveSymbolsInDylibs(self: *MachO) !void {
19762224 const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.sym_index, global);
19772225 const stub_atom = try self.createStubAtom(laptr_atom.sym_index);
19782226 self.stubs.items[stub_index].sym_index = stub_atom.sym_index;
2227 self.markRelocsDirtyByTarget(global);
19792228 }
19802229
19812230 continue :loop;
......@@ -2073,6 +2322,10 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {
20732322 const got_index = try self.allocateGotEntry(global);
20742323 const got_atom = try self.createGotAtom(global);
20752324 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 }
20762329}
20772330
20782331pub fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
......@@ -2356,6 +2609,30 @@ pub fn deinit(self: *MachO) void {
23562609 }
23572610 self.relocs.deinit(gpa);
23582611 }
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 }
23592636}
23602637
23612638fn freeAtom(self: *MachO, atom: *Atom, owns_atom: bool) void {
......@@ -2363,6 +2640,8 @@ fn freeAtom(self: *MachO, atom: *Atom, owns_atom: bool) void {
23632640 if (!owns_atom) {
23642641 atom.deinit(self.base.allocator);
23652642 }
2643 // Remove any relocs and base relocs associated with this Atom
2644 self.freeRelocationsForAtom(atom);
23662645
23672646 const sect_id = atom.getSymbol(self).n_sect - 1;
23682647 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
25692848 const decl_index = func.owner_decl;
25702849 const decl = module.declPtr(decl_index);
25712850 self.freeUnnamedConsts(decl_index);
2572
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();
2851 self.freeRelocationsForAtom(&decl.link.macho);
25792852
25802853 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
25812854 defer code_buffer.deinit();
......@@ -2593,18 +2866,16 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
25932866 else
25942867 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
25952868
2596 switch (res) {
2597 .appended => {
2598 try decl.link.macho.code.appendSlice(self.base.allocator, code_buffer.items);
2599 },
2869 const code = switch (res) {
2870 .appended => code_buffer.items,
26002871 .fail => |em| {
26012872 decl.analysis = .codegen_failure;
26022873 try module.failed_decls.put(module.gpa, decl_index, em);
26032874 return;
26042875 },
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
26092880 if (decl_state) |*ds| {
26102881 try self.d_sym.?.dwarf.commitDeclState(
......@@ -2650,20 +2921,17 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
26502921
26512922 log.debug("allocating symbol indexes for {?s}", .{name});
26522923
2653 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
2654 const sym_index = try self.allocateSymbol();
2655 const atom = try MachO.createEmptyAtom(
2656 gpa,
2657 sym_index,
2658 @sizeOf(u64),
2659 math.log2(required_alignment),
2660 );
2924 const atom = try gpa.create(Atom);
2925 errdefer gpa.destroy(atom);
2926 atom.* = Atom.empty;
2927
2928 atom.sym_index = try self.allocateSymbol();
26612929
26622930 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
26652933 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,
26672935 });
26682936 const code = switch (res) {
26692937 .externally_managed => |x| x,
......@@ -2676,9 +2944,9 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
26762944 },
26772945 };
26782946
2679 atom.code.clearRetainingCapacity();
2680 try atom.code.appendSlice(gpa, code);
2681
2947 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
2948 atom.size = code.len;
2949 atom.alignment = math.log2(required_alignment);
26822950 const sect_id = try self.getOutputSectionAtom(
26832951 atom,
26842952 decl_name,
......@@ -2691,13 +2959,14 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
26912959 symbol.n_type = macho.N_SECT;
26922960 symbol.n_sect = sect_id + 1;
26932961 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
26952966 log.debug("allocated atom for {?s} at 0x{x}", .{ name, symbol.n_value });
26962967 log.debug(" (required alignment 0x{x})", .{required_alignment});
26972968
2698 errdefer self.freeAtom(atom, true);
2699
2700 try unnamed_consts.append(gpa, atom);
2969 try self.writeAtom(atom, code);
27012970
27022971 return atom.sym_index;
27032972}
......@@ -2724,6 +2993,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
27242993 }
27252994 }
27262995
2996 self.freeRelocationsForAtom(&decl.link.macho);
2997
27272998 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
27282999 defer code_buffer.deinit();
27293000
......@@ -2751,27 +3022,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
27513022 .parent_atom_index = decl.link.macho.sym_index,
27523023 });
27533024
2754 const code = blk: {
2755 switch (res) {
2756 .externally_managed => |x| break :blk x,
2757 .appended => {
2758 // TODO clearing the code and relocs buffer should probably be orchestrated
2759 // in a different, smarter, more automatic way somewhere else, in a more centralised
2760 // way than this.
2761 // If we don't clear the buffers here, we are up for some nasty surprises when
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 }
3025 const code = switch (res) {
3026 .externally_managed => |x| x,
3027 .appended => code_buffer.items,
3028 .fail => |em| {
3029 decl.analysis = .codegen_failure;
3030 try module.failed_decls.put(module.gpa, decl_index, em);
3031 return;
3032 },
27733033 };
2774 const addr = try self.placeDecl(decl_index, code.len);
3034 const addr = try self.updateDeclCode(decl_index, code);
27753035
27763036 if (decl_state) |*ds| {
27773037 try self.d_sym.?.dwarf.commitDeclState(
......@@ -2936,19 +3196,22 @@ fn getOutputSectionAtom(
29363196 return sect_id;
29373197}
29383198
2939fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64 {
2940 const module = self.base.options.module.?;
2941 const decl = module.declPtr(decl_index);
3199fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8) !u64 {
3200 const gpa = self.base.allocator;
3201 const mod = self.base.options.module.?;
3202 const decl = mod.declPtr(decl_index);
3203
29423204 const required_alignment = decl.getAlignment(self.base.options.target);
29433205 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);
29463208 defer self.base.allocator.free(sym_name);
29473209
3210 const atom = &decl.link.macho;
29483211 const decl_ptr = self.decls.getPtr(decl_index).?;
29493212 if (decl_ptr.* == null) {
29503213 decl_ptr.* = try self.getOutputSectionAtom(
2951 &decl.link.macho,
3214 atom,
29523215 sym_name,
29533216 decl.ty,
29543217 decl.val,
......@@ -2956,55 +3219,63 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64
29563219 );
29573220 }
29583221 const sect_id = decl_ptr.*.?;
3222 const code_len = code.len;
29593223
2960 if (decl.link.macho.size != 0) {
2961 const symbol = decl.link.macho.getSymbolPtr(self);
2962 symbol.n_strx = try self.strtab.insert(self.base.allocator, sym_name);
2963 symbol.n_type = macho.N_SECT;
2964 symbol.n_sect = sect_id + 1;
2965 symbol.n_desc = 0;
3224 if (atom.size != 0) {
3225 const sym = atom.getSymbolPtr(self);
3226 sym.n_strx = try self.strtab.insert(gpa, sym_name);
3227 sym.n_type = macho.N_SECT;
3228 sym.n_sect = sect_id + 1;
3229 sym.n_desc = 0;
29663230
29673231 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
29703234 if (need_realloc) {
2971 const vaddr = try self.growAtom(&decl.link.macho, code_len, required_alignment);
2972 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, symbol.n_value, vaddr });
3235 const vaddr = try self.growAtom(atom, code_len, required_alignment);
3236 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, sym.n_value, vaddr });
29733237 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;
2986 decl.link.macho.dirty = true;
3239 if (vaddr != sym.n_value) {
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;
29873251 } else {
2988 const name_str_index = try self.strtab.insert(self.base.allocator, sym_name);
2989 const symbol = decl.link.macho.getSymbolPtr(self);
2990 symbol.n_strx = name_str_index;
2991 symbol.n_type = macho.N_SECT;
2992 symbol.n_sect = sect_id + 1;
2993 symbol.n_desc = 0;
2994 symbol.n_value = try self.allocateAtom(&decl.link.macho, code_len, required_alignment);
2995
2996 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, symbol.n_value });
3252 const name_str_index = try self.strtab.insert(gpa, sym_name);
3253 const sym = atom.getSymbolPtr(self);
3254 sym.n_strx = name_str_index;
3255 sym.n_type = macho.N_SECT;
3256 sym.n_sect = sect_id + 1;
3257 sym.n_desc = 0;
3258
3259 const vaddr = try self.allocateAtom(atom, code_len, required_alignment);
3260 errdefer self.freeAtom(atom, false);
3261
3262 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, vaddr });
29973263 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 };
30023269 const got_index = try self.allocateGotEntry(got_target);
30033270 const got_atom = try self.createGotAtom(got_target);
30043271 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
3272 try self.writePtrWidthAtom(got_atom);
30053273 }
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;
30083279}
30093280
30103281pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {
......@@ -3152,17 +3423,25 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
31523423 }
31533424}
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
31553433fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
3434 const gpa = self.base.allocator;
31563435 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
31573436 for (unnamed_consts.items) |atom| {
31583437 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 {};
31603439 self.locals.items[atom.sym_index].n_type = 0;
31613440 _ = self.atom_by_index_table.remove(atom.sym_index);
31623441 log.debug(" adding local symbol index {d} to free list", .{atom.sym_index});
31633442 atom.sym_index = 0;
31643443 }
3165 unnamed_consts.clearAndFree(self.base.allocator);
3444 unnamed_consts.clearAndFree(gpa);
31663445}
31673446
31683447pub 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 {
31713450 }
31723451 const mod = self.base.options.module.?;
31733452 const decl = mod.declPtr(decl_index);
3453
31743454 log.debug("freeDecl {*}", .{decl});
3455
31753456 const kv = self.decls.fetchSwapRemove(decl_index);
31763457 if (kv.?.value) |_| {
31773458 self.freeAtom(&decl.link.macho, false);
31783459 self.freeUnnamedConsts(decl_index);
31793460 }
3461
31803462 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
3181 if (decl.link.macho.sym_index != 0) {
3182 self.locals_free_list.append(self.base.allocator, decl.link.macho.sym_index) catch {};
3463 const gpa = self.base.allocator;
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
31843468 // 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 };
31863470 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 {};
31883472 self.got_entries.items[got_index] = .{
31893473 .target = .{ .sym_index = 0, .file = null },
31903474 .sym_index = 0,
......@@ -3192,20 +3476,18 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
31923476 _ = self.got_entries_table.remove(got_target);
31933477
31943478 if (self.d_sym) |*d_sym| {
3195 d_sym.swapRemoveRelocs(decl.link.macho.sym_index);
3479 d_sym.swapRemoveRelocs(sym_index);
31963480 }
31973481
3198 log.debug(" adding GOT index {d} to free list (target local@{d})", .{
3199 got_index,
3200 decl.link.macho.sym_index,
3201 });
3482 log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
32023483 }
32033484
3204 self.locals.items[decl.link.macho.sym_index].n_type = 0;
3205 _ = self.atom_by_index_table.remove(decl.link.macho.sym_index);
3206 log.debug(" adding local symbol index {d} to free list", .{decl.link.macho.sym_index});
3485 self.locals.items[sym_index].n_type = 0;
3486 _ = self.atom_by_index_table.remove(sym_index);
3487 log.debug(" adding local symbol index {d} to free list", .{sym_index});
32073488 decl.link.macho.sym_index = 0;
32083489 }
3490
32093491 if (self.d_sym) |*d_sym| {
32103492 d_sym.dwarf.freeDecl(decl);
32113493 }
......@@ -3218,21 +3500,20 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil
32183500 assert(self.llvm_object == null);
32193501 assert(decl.link.macho.sym_index != 0);
32203502
3221 const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?;
3222 try atom.relocs.append(self.base.allocator, .{
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,
3503 const atom = self.getAtomForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;
3504 try atom.addRelocation(self, .{
32293505 .@"type" = switch (self.base.options.target.cpu.arch) {
32303506 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
32313507 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
32323508 else => unreachable,
32333509 },
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,
32343515 });
3235 try atom.rebases.append(self.base.allocator, reloc_info.offset);
3516 try atom.addRebase(self, @intCast(u32, reloc_info.offset));
32363517
32373518 return 0;
32383519}
......@@ -3575,6 +3856,7 @@ fn allocateSection(self: *MachO, sect_id: u8, size: u64, alignment: u32) !void {
35753856
35763857 if (size > max_size) {
35773858 try self.growSection(sect_id, @intCast(u32, size));
3859 self.markRelocsDirtyByAddress(header.addr + size);
35783860 }
35793861
35803862 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) !
38854167 const header = &self.sections.items(.header)[sect_id];
38864168 const free_list = &self.sections.items(.free_list)[sect_id];
38874169 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
38904178 // We use these to indicate our intention to update metadata, placing the new atom,
38914179 // and possibly removing a free list node.
......@@ -3905,7 +4193,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
39054193 // Is it enough that we could fit this new atom?
39064194 const sym = big_atom.getSymbol(self);
39074195 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;
39094197 const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity;
39104198 const capacity_end_vaddr = sym.n_value + capacity;
39114199 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) !
39354223 break :blk new_start_vaddr;
39364224 } else if (maybe_last_atom.*) |last| {
39374225 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;
39394227 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
39404228 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
39414229 atom_placement = last;
......@@ -3949,6 +4237,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
39494237 if (expand_section) {
39504238 const needed_size = @intCast(u32, (vaddr + new_atom_size) - header.addr);
39514239 try self.growSection(sect_id, needed_size);
4240 self.markRelocsDirtyByAddress(header.addr + needed_size);
39524241 maybe_last_atom.* = atom;
39534242 header.size = needed_size;
39544243 }
......@@ -4105,64 +4394,70 @@ pub fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
41054394 const sym = atom.getSymbol(self);
41064395 const base_offset = sym.n_value - seg.vmaddr;
41074396
4108 for (atom.rebases.items) |offset| {
4109 log.debug(" | rebase at {x}", .{base_offset + offset});
4110 try rebase_pointers.append(.{
4111 .offset = base_offset + offset,
4112 .segment_id = segment_index,
4113 });
4397 if (self.rebases.get(atom)) |rebases| {
4398 for (rebases.items) |offset| {
4399 log.debug(" | rebase at {x}", .{base_offset + offset});
4400 try rebase_pointers.append(.{
4401 .offset = base_offset + offset,
4402 .segment_id = segment_index,
4403 });
4404 }
41144405 }
41154406
4116 for (atom.bindings.items) |binding| {
4117 const bind_sym = self.getSymbol(binding.target);
4118 const bind_sym_name = self.getSymbolName(binding.target);
4119 const dylib_ordinal = @divTrunc(
4120 @bitCast(i16, bind_sym.n_desc),
4121 macho.N_SYMBOL_RESOLVER,
4122 );
4123 var flags: u4 = 0;
4124 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
4125 binding.offset + base_offset,
4126 bind_sym_name,
4127 dylib_ordinal,
4128 });
4129 if (bind_sym.weakRef()) {
4130 log.debug(" | marking as weak ref ", .{});
4131 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
4407 if (self.bindings.get(atom)) |bindings| {
4408 for (bindings.items) |binding| {
4409 const bind_sym = self.getSymbol(binding.target);
4410 const bind_sym_name = self.getSymbolName(binding.target);
4411 const dylib_ordinal = @divTrunc(
4412 @bitCast(i16, bind_sym.n_desc),
4413 macho.N_SYMBOL_RESOLVER,
4414 );
4415 var flags: u4 = 0;
4416 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
4417 binding.offset + base_offset,
4418 bind_sym_name,
4419 dylib_ordinal,
4420 });
4421 if (bind_sym.weakRef()) {
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 });
41324432 }
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 });
41404433 }
41414434
4142 for (atom.lazy_bindings.items) |binding| {
4143 const bind_sym = self.getSymbol(binding.target);
4144 const bind_sym_name = self.getSymbolName(binding.target);
4145 const dylib_ordinal = @divTrunc(
4146 @bitCast(i16, bind_sym.n_desc),
4147 macho.N_SYMBOL_RESOLVER,
4148 );
4149 var flags: u4 = 0;
4150 log.debug(" | lazy bind at {x} import('{s}') ord({d})", .{
4151 binding.offset + base_offset,
4152 bind_sym_name,
4153 dylib_ordinal,
4154 });
4155 if (bind_sym.weakRef()) {
4156 log.debug(" | marking as weak ref ", .{});
4157 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
4435 if (self.lazy_bindings.get(atom)) |lazy_bindings| {
4436 for (lazy_bindings.items) |binding| {
4437 const bind_sym = self.getSymbol(binding.target);
4438 const bind_sym_name = self.getSymbolName(binding.target);
4439 const dylib_ordinal = @divTrunc(
4440 @bitCast(i16, bind_sym.n_desc),
4441 macho.N_SYMBOL_RESOLVER,
4442 );
4443 var flags: u4 = 0;
4444 log.debug(" | lazy bind at {x} import('{s}') ord({d})", .{
4445 binding.offset + base_offset,
4446 bind_sym_name,
4447 dylib_ordinal,
4448 });
4449 if (bind_sym.weakRef()) {
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 });
41584460 }
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 });
41664461 }
41674462
41684463 if (atom.prev) |prev| {
......@@ -4387,25 +4682,25 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
43874682
43884683const 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 {
43914686 const tracy = trace(@src());
43924687 defer tracy.end();
43934688
4394 const text_seg_index = self.text_segment_cmd_index orelse return;
4395 const text_sect_index = self.text_section_index orelse return;
4396 const text_seg = self.segments.items[text_seg_index];
4689 const text_seg_index = macho_file.text_segment_cmd_index orelse return;
4690 const text_sect_index = macho_file.text_section_index orelse return;
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
44004695 // We need to sort by address first
44014696 var addresses = std.ArrayList(u64).init(gpa);
44024697 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| {
4406 const sym = self.getSymbol(global);
4700 for (macho_file.globals.items) |global| {
4701 const sym = macho_file.getSymbol(global);
44074702 if (sym.undf()) continue;
4408 if (sym.n_desc == N_DESC_GCED) continue;
4703 if (sym.n_desc == MachO.N_DESC_GCED) continue;
44094704 const sect_id = sym.n_sect - 1;
44104705 if (sect_id != text_sect_index) continue;
44114706
......@@ -4439,14 +4734,14 @@ fn writeFunctionStarts(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
44394734 try std.leb.writeULEB128(buffer.writer(), offset);
44404735 }
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.?];
44434738 const offset = mem.alignForwardGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64));
44444739 const needed_size = buffer.items.len;
44454740 link_seg.filesize = offset + needed_size - link_seg.fileoff;
44464741
44474742 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
44514746 try lc_writer.writeStruct(macho.linkedit_data_command{
44524747 .cmd = .FUNCTION_STARTS,
......@@ -4465,8 +4760,8 @@ fn filterDataInCode(
44654760 const Predicate = struct {
44664761 addr: u64,
44674762
4468 pub fn predicate(self: @This(), dice: macho.data_in_code_entry) bool {
4469 return dice.offset >= self.addr;
4763 pub fn predicate(macho_file: @This(), dice: macho.data_in_code_entry) bool {
4764 return dice.offset >= macho_file.addr;
44704765 }
44714766 };
44724767
......@@ -4476,26 +4771,26 @@ fn filterDataInCode(
44764771 return dices[start..end];
44774772}
44784773
4479fn writeDataInCode(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4774pub fn writeDataInCode(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
44804775 const tracy = trace(@src());
44814776 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);
44844779 defer out_dice.deinit();
44854780
4486 const text_sect_id = self.text_section_index orelse return;
4487 const text_sect_header = self.sections.items(.header)[text_sect_id];
4781 const text_sect_id = macho_file.text_section_index orelse return;
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| {
44904785 const dice = object.parseDataInCode() orelse continue;
44914786 try out_dice.ensureUnusedCapacity(dice.len);
44924787
44934788 for (object.managed_atoms.items) |atom| {
4494 const sym = atom.getSymbol(self);
4495 if (sym.n_desc == N_DESC_GCED) continue;
4789 const sym = atom.getSymbol(macho_file);
4790 if (sym.n_desc == MachO.N_DESC_GCED) continue;
44964791
44974792 const sect_id = sym.n_sect - 1;
4498 if (sect_id != self.text_section_index.?) {
4793 if (sect_id != macho_file.text_section_index.?) {
44994794 continue;
45004795 }
45014796
......@@ -4516,14 +4811,14 @@ fn writeDataInCode(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
45164811 }
45174812 }
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.?];
45204815 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
45214816 const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
45224817 seg.filesize = offset + needed_size - seg.fileoff;
45234818
45244819 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);
45274822 try lc_writer.writeStruct(macho.linkedit_data_command{
45284823 .cmd = .DATA_IN_CODE,
45294824 .cmdsize = @sizeOf(macho.linkedit_data_command),
......@@ -4533,7 +4828,7 @@ fn writeDataInCode(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
45334828 ncmds.* += 1;
45344829}
45354830
4536fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4831pub fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
45374832 var symtab_cmd = macho.symtab_command{
45384833 .cmdsize = @sizeOf(macho.symtab_command),
45394834 .symoff = 0,
......@@ -4571,7 +4866,7 @@ fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
45714866 ncmds.* += 2;
45724867}
45734868
4574fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
4869pub fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
45754870 const gpa = self.base.allocator;
45764871
45774872 var locals = std.ArrayList(macho.nlist_64).init(gpa);
......@@ -5485,7 +5780,7 @@ pub fn logSymtab(self: *MachO) void {
54855780 const def_index = if (sym.undf() and !sym.tentative())
54865781 @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER)
54875782 else
5488 sym.n_sect;
5783 sym.n_sect + 1;
54895784 log.debug(" %{d}: {s} @{x} in {s}({d}), {s}", .{
54905785 sym_id,
54915786 object.getString(sym.n_strx),
......@@ -5502,7 +5797,7 @@ pub fn logSymtab(self: *MachO) void {
55025797 const def_index = if (sym.undf() and !sym.tentative())
55035798 @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER)
55045799 else
5505 sym.n_sect;
5800 sym.n_sect + 1;
55065801 log.debug(" %{d}: {?s} @{x} in {s}({d}), {s}", .{
55075802 sym_id,
55085803 self.strtab.get(sym.n_strx),
......@@ -5633,6 +5928,6 @@ pub fn copyRangeAllOverlappingAlloc(
56335928) !void {
56345929 const buf = try allocator.alloc(u8, len);
56355930 defer allocator.free(buf);
5636 _ = try file.preadAll(buf, in_offset);
5637 try file.pwriteAll(buf, out_offset);
5931 const amt = try file.preadAll(buf, in_offset);
5932 try file.pwriteAll(buf[0..amt], out_offset);
56385933}
src/link/MachO/Atom.zig+55-22
......@@ -66,8 +66,6 @@ prev: ?*Atom,
6666
6767dbg_info_atom: Dwarf.Atom,
6868
69dirty: bool = true,
70
7169pub const Binding = struct {
7270 target: SymbolWithLoc,
7371 offset: u64,
......@@ -898,46 +896,81 @@ inline fn isArithmeticOp(inst: *const [4]u8) bool {
898896}
899897
900898pub 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 {
901908 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;
903910 const gop = try macho_file.relocs.getOrPut(gpa, self);
904911 if (!gop.found_existing) {
905912 gop.value_ptr.* = .{};
906913 }
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 }
908922}
909923
910pub fn resolveRelocationsInCodeBuffer(self: *Atom, macho_file: *MachO, code: []u8) !void {
911 const relocs = macho_file.relocs.get(self) orelse return;
912
913 log.debug("relocating '{s}'", .{self.getName(macho_file)});
924pub fn addRebase(self: *Atom, macho_file: *MachO, offset: u32) !void {
925 const gpa = macho_file.base.allocator;
926 log.debug(" (adding rebase at offset 0x{x} in %{d})", .{ offset, self.sym_index });
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| {
916 // We don't check for dirty relocation as we resolve in memory so it's effectively free.
917 try reloc.resolve(self, macho_file, code);
918 reloc.dirty = false;
934pub fn addBinding(self: *Atom, macho_file: *MachO, binding: Binding) !void {
935 const gpa = macho_file.base.allocator;
936 log.debug(" (adding binding to symbol {s} at offset 0x{x} in %{d})", .{
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.* = .{};
919944 }
945 try gop.value_ptr.append(gpa, binding);
920946}
921947
922pub fn resolveRelocationsInFile(self: *Atom, macho_file: *MachO) !void {
923 const relocs = macho_file.relocs.get(self) orelse return;
948pub fn addLazyBinding(self: *Atom, macho_file: *MachO, binding: Binding) !void {
924949 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;
927964 const source_sym = self.getSymbol(macho_file);
928965 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;
930 const code = try gpa.alloc(u8, self.size);
931 try self.base.file.?.preadAll(code, file_offset);
932 defer gpa.free(code);
966 const file_offset = source_section.offset + source_sym.n_value - source_section.addr;
933967
934968 log.debug("relocating '{s}'", .{self.getName(macho_file)});
935969
936970 for (relocs.items) |*reloc| {
937971 if (!reloc.dirty) continue;
938 try reloc.resolve(self, macho_file, code);
972
973 try reloc.resolve(self, macho_file, file_offset);
939974 reloc.dirty = false;
940975 }
941
942 try self.base.file.?.pwriteAll(code, file_offset);
943976}
src/link/MachO/Relocation.zig+136-106
......@@ -13,31 +13,35 @@ const Atom = @import("Atom.zig");
1313const MachO = @import("../MachO.zig");
1414const SymbolWithLoc = MachO.SymbolWithLoc;
1515
16pub const Table = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Relocation));
17
18/// Offset within the atom's code buffer.
19/// Note relocation size can be inferred by relocation's kind.
20offset: u32,
16@"type": u4,
2117target: SymbolWithLoc,
18offset: u32,
2219addend: i64,
2320pcrel: bool,
2421length: u2,
25@"type": u4,
2622dirty: 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
2832pub fn getTargetAtom(self: Relocation, macho_file: *MachO) ?*Atom {
2933 switch (macho_file.base.options.target.cpu.arch) {
3034 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, self.@"type")) {
3135 .ARM64_RELOC_GOT_LOAD_PAGE21,
3236 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
3337 .ARM64_RELOC_POINTER_TO_GOT,
34 => return macho_file.getGotAtomForSymbol(self.target).?,
38 => return macho_file.getGotAtomForSymbol(self.target),
3539 else => {},
3640 },
3741 .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, self.@"type")) {
3842 .X86_64_RELOC_GOT,
3943 .X86_64_RELOC_GOT_LOAD,
40 => return macho_file.getGotAtomForSymbol(self.target).?,
44 => return macho_file.getGotAtomForSymbol(self.target),
4145 else => {},
4246 },
4347 else => unreachable,
......@@ -47,54 +51,72 @@ pub fn getTargetAtom(self: Relocation, macho_file: *MachO) ?*Atom {
4751 return macho_file.getAtomForSymbol(self.target);
4852}
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 {
5155 const arch = macho_file.base.options.target.cpu.arch;
5256 const source_sym = atom.getSymbol(macho_file);
5357 const source_addr = source_sym.n_value + self.offset;
5458
5559 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
5862 log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{
5963 source_addr,
6064 target_addr,
6165 macho_file.getSymbolName(self.target),
62 switch (arch) {
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 },
66 self.fmtType(macho_file.base.options.target),
6767 });
6868
6969 switch (arch) {
70 .aarch64 => return self.resolveAarch64(source_addr, target_addr, macho_file, code),
71 .x86_64 => return self.resolveX8664(source_addr, target_addr, code),
70 .aarch64 => return self.resolveAarch64(macho_file, source_addr, target_addr, base_offset),
71 .x86_64 => return self.resolveX8664(macho_file, source_addr, target_addr, base_offset),
7272 else => unreachable,
7373 }
7474}
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 {
7783 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
78106 switch (rel_type) {
79107 .ARM64_RELOC_BRANCH26 => {
80 const displacement = math.cast(i28, @intCast(i64, target_addr) - @intCast(i64, source_addr)) orelse {
81 log.err("jump too big to encode as i28 displacement value", .{});
82 log.err(" (target - source) = displacement => 0x{x} - 0x{x} = 0x{x}", .{
83 target_addr,
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 };
108 const displacement = math.cast(
109 i28,
110 @intCast(i64, target_addr) - @intCast(i64, source_addr),
111 ) orelse unreachable; // TODO codegen should never allow for jump larger than i28 displacement
90112 var inst = aarch64.Instruction{
91113 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
92114 aarch64.Instruction,
93115 aarch64.Instruction.unconditional_branch_immediate,
94 ), code),
116 ), &buffer),
95117 };
96118 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());
98120 },
99121 .ARM64_RELOC_PAGE21,
100122 .ARM64_RELOC_GOT_LOAD_PAGE21,
......@@ -107,45 +129,45 @@ fn resolveAarch64(self: Relocation, source_addr: u64, target_addr: u64, macho_fi
107129 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
108130 aarch64.Instruction,
109131 aarch64.Instruction.pc_relative_address,
110 ), code),
132 ), &buffer),
111133 };
112134 inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
113135 inst.pc_relative_address.immlo = @truncate(u2, pages);
114 mem.writeIntLittle(u32, code, inst.toU32());
136 mem.writeIntLittle(u32, &buffer, inst.toU32());
115137 },
116138 .ARM64_RELOC_PAGEOFF12 => {
117139 const narrowed = @truncate(u12, @intCast(u64, target_addr));
118 if (isArithmeticOp(code)) {
140 if (isArithmeticOp(&buffer)) {
119141 var inst = aarch64.Instruction{
120142 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
121143 aarch64.Instruction,
122144 aarch64.Instruction.add_subtract_immediate,
123 ), code),
145 ), &buffer),
124146 };
125147 inst.add_subtract_immediate.imm12 = narrowed;
126 mem.writeIntLittle(u32, code, inst.toU32());
148 mem.writeIntLittle(u32, &buffer, inst.toU32());
127149 } else {
128150 var inst = aarch64.Instruction{
129151 .load_store_register = mem.bytesToValue(meta.TagPayload(
130152 aarch64.Instruction,
131153 aarch64.Instruction.load_store_register,
132 ), code),
154 ), &buffer),
133155 };
134156 const offset: u12 = blk: {
135157 if (inst.load_store_register.size == 0) {
136158 if (inst.load_store_register.v == 1) {
137159 // 128-bit SIMD is scaled by 16.
138 break :blk try math.divExact(u12, narrowed, 16);
160 break :blk @divExact(narrowed, 16);
139161 }
140162 // Otherwise, 8-bit SIMD or ldrb.
141163 break :blk narrowed;
142164 } else {
143 const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size);
144 break :blk try math.divExact(u12, narrowed, denom);
165 const denom: u4 = math.powi(u4, 2, inst.load_store_register.size) catch unreachable;
166 break :blk @divExact(narrowed, denom);
145167 }
146168 };
147169 inst.load_store_register.offset = offset;
148 mem.writeIntLittle(u32, code, inst.toU32());
170 mem.writeIntLittle(u32, &buffer, inst.toU32());
149171 }
150172 },
151173 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {
......@@ -154,11 +176,12 @@ fn resolveAarch64(self: Relocation, source_addr: u64, target_addr: u64, macho_fi
154176 .load_store_register = mem.bytesToValue(meta.TagPayload(
155177 aarch64.Instruction,
156178 aarch64.Instruction.load_store_register,
157 ), code),
179 ), &buffer),
158180 };
159 const offset = try math.divExact(u12, narrowed, 8);
181 const offset = @divExact(narrowed, 8);
160182 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)});
162185 },
163186 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
164187 const RegInfo = struct {
......@@ -167,11 +190,11 @@ fn resolveAarch64(self: Relocation, source_addr: u64, target_addr: u64, macho_fi
167190 size: u2,
168191 };
169192 const reg_info: RegInfo = blk: {
170 if (isArithmeticOp(code)) {
193 if (isArithmeticOp(&buffer)) {
171194 const inst = mem.bytesToValue(meta.TagPayload(
172195 aarch64.Instruction,
173196 aarch64.Instruction.add_subtract_immediate,
174 ), code);
197 ), &buffer);
175198 break :blk .{
176199 .rd = inst.rd,
177200 .rn = inst.rn,
......@@ -181,7 +204,7 @@ fn resolveAarch64(self: Relocation, source_addr: u64, target_addr: u64, macho_fi
181204 const inst = mem.bytesToValue(meta.TagPayload(
182205 aarch64.Instruction,
183206 aarch64.Instruction.load_store_register,
184 ), code);
207 ), &buffer);
185208 break :blk .{
186209 .rd = inst.rt,
187210 .rn = inst.rn,
......@@ -190,20 +213,7 @@ fn resolveAarch64(self: Relocation, source_addr: u64, target_addr: u64, macho_fi
190213 }
191214 };
192215 const narrowed = @truncate(u12, @intCast(u64, target_addr));
193 var inst = if (macho_file.tlv_ptr_entries_table.contains(self.target)) blk: {
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{
216 var inst = aarch64.Instruction{
207217 .add_subtract_immediate = .{
208218 .rd = reg_info.rd,
209219 .rn = reg_info.rn,
......@@ -214,61 +224,81 @@ fn resolveAarch64(self: Relocation, source_addr: u64, target_addr: u64, macho_fi
214224 .sf = @truncate(u1, reg_info.size),
215225 },
216226 };
217 mem.writeIntLittle(u32, code, inst.toU32());
227 mem.writeIntLittle(u32, &buffer, inst.toU32());
218228 },
219229 .ARM64_RELOC_POINTER_TO_GOT => {
220 const result = math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr)) orelse
221 return error.Overflow;
222 mem.writeIntLittle(u32, code, @bitCast(u32, result));
223 },
224 .ARM64_RELOC_UNSIGNED => {
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 const result = math.cast(
231 i32,
232 @intCast(i64, target_addr) - @intCast(i64, source_addr),
233 ) orelse return error.Overflow;
234 mem.writeIntLittle(u32, &buffer, @bitCast(u32, result));
230235 },
231236 .ARM64_RELOC_SUBTRACTOR => unreachable,
232237 .ARM64_RELOC_ADDEND => unreachable,
238 .ARM64_RELOC_UNSIGNED => unreachable,
233239 }
240 try macho_file.base.file.?.pwriteAll(&buffer, base_offset + self.offset);
234241}
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 {
237250 const rel_type = @intToEnum(macho.reloc_type_x86_64, self.@"type");
238 switch (rel_type) {
239 .X86_64_RELOC_BRANCH,
240 .X86_64_RELOC_GOT,
241 .X86_64_RELOC_GOT_LOAD,
242 .X86_64_RELOC_TLV,
243 => {
244 const displacement = math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4) orelse
245 return error.Overflow;
246 mem.writeIntLittle(u32, code, @bitCast(u32, displacement));
247 },
248 .X86_64_RELOC_SIGNED,
249 .X86_64_RELOC_SIGNED_1,
250 .X86_64_RELOC_SIGNED_2,
251 .X86_64_RELOC_SIGNED_4,
252 => {
253 const correction: u3 = switch (rel_type) {
254 .X86_64_RELOC_SIGNED => 0,
255 .X86_64_RELOC_SIGNED_1 => 1,
256 .X86_64_RELOC_SIGNED_2 => 2,
257 .X86_64_RELOC_SIGNED_4 => 4,
258 else => unreachable,
259 };
260 const displacement = math.cast(i32, target_addr - @intCast(i64, source_addr + correction + 4)) orelse
261 return error.Overflow;
262 mem.writeIntLittle(u32, code, @bitCast(u32, displacement));
263 },
264 .X86_64_RELOC_UNSIGNED => {
265 switch (self.length) {
266 2 => mem.writeIntLittle(u32, code, @truncate(u32, @bitCast(u64, target_addr))),
267 3 => mem.writeIntLittle(u64, code, target_addr),
268 }
269 },
270 .X86_64_RELOC_SUBTRACTOR => unreachable,
271 }
251 var buffer: [@sizeOf(u64)]u8 = undefined;
252 const code = blk: {
253 switch (rel_type) {
254 .X86_64_RELOC_BRANCH,
255 .X86_64_RELOC_GOT,
256 .X86_64_RELOC_GOT_LOAD,
257 .X86_64_RELOC_TLV,
258 => {
259 const displacement = math.cast(
260 i32,
261 @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4,
262 ) orelse return error.Overflow;
263 mem.writeIntLittle(u32, buffer[0..4], @bitCast(u32, displacement));
264 break :blk buffer[0..4];
265 },
266 .X86_64_RELOC_SIGNED,
267 .X86_64_RELOC_SIGNED_1,
268 .X86_64_RELOC_SIGNED_2,
269 .X86_64_RELOC_SIGNED_4,
270 => {
271 const correction: u3 = switch (rel_type) {
272 .X86_64_RELOC_SIGNED => 0,
273 .X86_64_RELOC_SIGNED_1 => 1,
274 .X86_64_RELOC_SIGNED_2 => 2,
275 .X86_64_RELOC_SIGNED_4 => 4,
276 else => unreachable,
277 };
278 const displacement = math.cast(
279 i32,
280 target_addr - @intCast(i64, source_addr + correction + 4),
281 ) orelse return error.Overflow;
282 mem.writeIntLittle(u32, buffer[0..4], @bitCast(u32, displacement));
283 break :blk buffer[0..4];
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);
272302}
273303
274304inline 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
233233 if (sym.n_desc != MachO.N_DESC_GCED) continue;
234234
235235 // TODO tombstone
236 const atom = entry.getAtom(macho_file);
236 const atom = entry.getAtom(macho_file).?;
237237 const match = sym.n_sect - 1;
238238 removeAtomFromSection(atom, match, macho_file);
239239 _ = try gc_sections.put(match, {});
......@@ -245,7 +245,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
245245 if (sym.n_desc != MachO.N_DESC_GCED) continue;
246246
247247 // TODO tombstone
248 const atom = entry.getAtom(macho_file);
248 const atom = entry.getAtom(macho_file).?;
249249 const match = sym.n_sect - 1;
250250 removeAtomFromSection(atom, match, macho_file);
251251 _ = try gc_sections.put(match, {});
......@@ -257,7 +257,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
257257 if (sym.n_desc != MachO.N_DESC_GCED) continue;
258258
259259 // TODO tombstone
260 const atom = entry.getAtom(macho_file);
260 const atom = entry.getAtom(macho_file).?;
261261 const match = sym.n_sect - 1;
262262 removeAtomFromSection(atom, match, macho_file);
263263 _ = try gc_sections.put(match, {});
src/link/MachO/zld.zig+329-1
......@@ -7,14 +7,19 @@ const macho = std.macho;
77const math = std.math;
88const mem = std.mem;
99
10const aarch64 = @import("../../arch/aarch64/bits.zig");
11const bind = @import("bind.zig");
1012const link = @import("../../link.zig");
1113const trace = @import("../../tracy.zig").trace;
1214
15const Atom = MachO.Atom;
1316const Cache = @import("../../Cache.zig");
1417const CodeSignature = @import("CodeSignature.zig");
1518const Compilation = @import("../../Compilation.zig");
1619const Dylib = @import("Dylib.zig");
1720const MachO = @import("../MachO.zig");
21const SymbolWithLoc = MachO.SymbolWithLoc;
22const Trie = @import("Trie.zig");
1823
1924const dead_strip = @import("dead_strip.zig");
2025
......@@ -545,7 +550,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
545550 const lc_writer = lc_buffer.writer();
546551 var ncmds: u32 = 0;
547552
548 try macho_file.writeLinkeditSegmentData(&ncmds, lc_writer);
553 try writeLinkeditSegmentData(macho_file, &ncmds, lc_writer);
549554
550555 // If the last section of __DATA segment is zerofill section, we need to ensure
551556 // 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 {
952957 }
953958 }
954959}
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}