authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-10-18 22:10:00+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-10-22 07:59:23+02:00
logd57639a30802ac9b42d8528c466f7b3123dbc469
treeecd92df964c92da27f10714bf1f81e55bf44f592
parent09236d29b7722d71533478aa7080706acde28d0d

macho: upstream rewritten traditional linker, zld

kubkon/zld gitrev 5733ed87abe2f07e1330c3232a252e9defec638a

11 files changed, 6682 insertions(+), 3883 deletions(-)

CMakeLists.txt+4
......@@ -768,11 +768,15 @@ set(ZIG_STAGE2_SOURCES
768768 "${CMAKE_SOURCE_DIR}/src/link/MachO/Atom.zig"
769769 "${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig"
770770 "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig"
771 "${CMAKE_SOURCE_DIR}/src/link/MachO/DwarfInfo.zig"
771772 "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig"
772773 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
773774 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
775 "${CMAKE_SOURCE_DIR}/src/link/MachO/ZldAtom.zig"
774776 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
775777 "${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig"
778 "${CMAKE_SOURCE_DIR}/src/link/MachO/thunks.zig"
779 "${CMAKE_SOURCE_DIR}/src/link/MachO/zld.zig"
776780 "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig"
777781 "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig"
778782 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
src/link/MachO.zig+222-876
......@@ -105,8 +105,6 @@ uuid: macho.uuid_command = .{
105105 .uuid = undefined,
106106},
107107
108objects: std.ArrayListUnmanaged(Object) = .{},
109archives: std.ArrayListUnmanaged(Archive) = .{},
110108dylibs: std.ArrayListUnmanaged(Dylib) = .{},
111109dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
112110referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},
......@@ -143,12 +141,6 @@ stub_helper_preamble_atom: ?*Atom = null,
143141
144142strtab: StringTable(.strtab) = .{},
145143
146// TODO I think synthetic tables are a perfect match for some generic refactoring,
147// and probably reusable between linker backends too.
148tlv_ptr_entries: std.ArrayListUnmanaged(Entry) = .{},
149tlv_ptr_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
150tlv_ptr_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
151
152144got_entries: std.ArrayListUnmanaged(Entry) = .{},
153145got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
154146got_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
......@@ -276,7 +268,7 @@ pub const SymbolWithLoc = struct {
276268const ideal_factor = 3;
277269
278270/// Default path to dyld
279const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
271pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
280272
281273/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
282274/// it as a possible place to put new symbols, it must have enough room for this many bytes
......@@ -286,12 +278,12 @@ pub const min_text_capacity = padToIdeal(minimum_text_block_size);
286278
287279/// Default virtual memory offset corresponds to the size of __PAGEZERO segment and
288280/// start of __TEXT segment.
289const default_pagezero_vmsize: u64 = 0x100000000;
281pub const default_pagezero_vmsize: u64 = 0x100000000;
290282
291283/// We commit 0x1000 = 4096 bytes of space to the header and
292284/// the table of load commands. This should be plenty for any
293285/// potential future extensions.
294const default_headerpad_size: u32 = 0x1000;
286pub const default_headerpad_size: u32 = 0x1000;
295287
296288pub const Export = struct {
297289 sym_index: ?u32 = null,
......@@ -465,7 +457,14 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
465457 }
466458
467459 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
468 try self.resolveLibSystem(arena, comp, &.{}, &libs);
460 try resolveLibSystem(
461 arena,
462 comp,
463 self.base.options.sysroot,
464 self.base.options.target,
465 &.{},
466 &libs,
467 );
469468
470469 const id_symlink_basename = "link.id";
471470
......@@ -660,15 +659,16 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
660659}
661660
662661pub fn resolveLibSystem(
663 self: *MachO,
664662 arena: Allocator,
665663 comp: *Compilation,
664 syslibroot: ?[]const u8,
665 target: std.Target,
666666 search_dirs: []const []const u8,
667667 out_libs: anytype,
668668) !void {
669669 // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}.
670670 var libsystem_available = false;
671 if (self.base.options.sysroot != null) blk: {
671 if (syslibroot != null) blk: {
672672 // Try stub file first. If we hit it, then we're done as the stub file
673673 // re-exports every single symbol definition.
674674 for (search_dirs) |dir| {
......@@ -693,7 +693,7 @@ pub fn resolveLibSystem(
693693 }
694694 if (!libsystem_available) {
695695 const libsystem_name = try std.fmt.allocPrint(arena, "libSystem.{d}.tbd", .{
696 self.base.options.target.os.version_range.semver.min.major,
696 target.os.version_range.semver.min.major,
697697 });
698698 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
699699 "libc", "darwin", libsystem_name,
......@@ -783,94 +783,6 @@ pub fn resolveFramework(
783783 return full_path;
784784}
785785
786fn parseObject(self: *MachO, path: []const u8) !bool {
787 const gpa = self.base.allocator;
788 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
789 error.FileNotFound => return false,
790 else => |e| return e,
791 };
792 defer file.close();
793
794 const name = try gpa.dupe(u8, path);
795 errdefer gpa.free(name);
796 const cpu_arch = self.base.options.target.cpu.arch;
797 const mtime: u64 = mtime: {
798 const stat = file.stat() catch break :mtime 0;
799 break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000));
800 };
801 const file_stat = try file.stat();
802 const file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
803 const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null);
804
805 var object = Object{
806 .name = name,
807 .mtime = mtime,
808 .contents = contents,
809 };
810
811 object.parse(gpa, cpu_arch) catch |err| switch (err) {
812 error.EndOfStream, error.NotObject => {
813 object.deinit(gpa);
814 return false;
815 },
816 else => |e| return e,
817 };
818
819 try self.objects.append(gpa, object);
820
821 return true;
822}
823
824fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool {
825 const gpa = self.base.allocator;
826 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
827 error.FileNotFound => return false,
828 else => |e| return e,
829 };
830 errdefer file.close();
831
832 const name = try gpa.dupe(u8, path);
833 errdefer gpa.free(name);
834 const cpu_arch = self.base.options.target.cpu.arch;
835 const reader = file.reader();
836 const fat_offset = try fat.getLibraryOffset(reader, cpu_arch);
837 try reader.context.seekTo(fat_offset);
838
839 var archive = Archive{
840 .name = name,
841 .fat_offset = fat_offset,
842 .file = file,
843 };
844
845 archive.parse(gpa, reader) catch |err| switch (err) {
846 error.EndOfStream, error.NotArchive => {
847 archive.deinit(gpa);
848 return false;
849 },
850 else => |e| return e,
851 };
852
853 if (force_load) {
854 defer archive.deinit(gpa);
855 // Get all offsets from the ToC
856 var offsets = std.AutoArrayHashMap(u32, void).init(gpa);
857 defer offsets.deinit();
858 for (archive.toc.values()) |offs| {
859 for (offs.items) |off| {
860 _ = try offsets.getOrPut(off);
861 }
862 }
863 for (offsets.keys()) |off| {
864 const object = try archive.parseObject(gpa, cpu_arch, off);
865 try self.objects.append(gpa, object);
866 }
867 } else {
868 try self.archives.append(gpa, archive);
869 }
870
871 return true;
872}
873
874786const ParseDylibError = error{
875787 OutOfMemory,
876788 EmptyStubFile,
......@@ -1019,7 +931,6 @@ pub fn parseLibs(
1019931 .needed = lib_info.needed,
1020932 .weak = lib_info.weak,
1021933 })) continue;
1022 if (try self.parseArchive(lib, false)) continue;
1023934
1024935 log.debug("unknown filetype for a library: '{s}'", .{lib});
1025936 }
......@@ -1070,29 +981,7 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs:
1070981 }
1071982}
1072983
1073pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32) !*Atom {
1074 const size_usize = math.cast(usize, size) orelse return error.Overflow;
1075 const atom = try gpa.create(Atom);
1076 errdefer gpa.destroy(atom);
1077 atom.* = Atom.empty;
1078 atom.sym_index = sym_index;
1079 atom.size = size;
1080 atom.alignment = alignment;
1081
1082 try atom.code.resize(gpa, size_usize);
1083 mem.set(u8, atom.code.items, 0);
1084
1085 return atom;
1086}
1087
1088984pub fn writeAtom(self: *MachO, atom: *Atom, code: []const u8) !void {
1089 // TODO: temporary sanity check
1090 assert(atom.code.items.len == 0);
1091 assert(atom.relocs.items.len == 0);
1092 assert(atom.rebases.items.len == 0);
1093 assert(atom.bindings.items.len == 0);
1094 assert(atom.lazy_bindings.items.len == 0);
1095
1096985 const sym = atom.getSymbol(self);
1097986 const section = self.sections.get(sym.n_sect - 1);
1098987 const file_offset = section.header.offset + sym.n_value - section.header.addr;
......@@ -1137,10 +1026,7 @@ pub fn allocateSpecialSymbols(self: *MachO) !void {
11371026 const global = self.getGlobal(name) orelse continue;
11381027 if (global.file != null) continue;
11391028 const sym = self.getSymbolPtr(global);
1140 const seg = switch (self.mode) {
1141 .incremental => self.getSegment(self.text_section_index.?),
1142 .one_shot => self.segments.items[self.text_segment_cmd_index.?],
1143 };
1029 const seg = self.getSegment(self.text_section_index.?);
11441030 sym.n_sect = 1;
11451031 sym.n_value = seg.vmaddr;
11461032
......@@ -1155,16 +1041,13 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
11551041 const gpa = self.base.allocator;
11561042
11571043 const sym_index = try self.allocateSymbol();
1158 const atom = switch (self.mode) {
1159 .incremental => blk: {
1160 const atom = try gpa.create(Atom);
1161 atom.* = Atom.empty;
1162 atom.sym_index = sym_index;
1163 atom.size = @sizeOf(u64);
1164 atom.alignment = @alignOf(u64);
1165 break :blk atom;
1166 },
1167 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3),
1044 const atom = blk: {
1045 const atom = try gpa.create(Atom);
1046 atom.* = Atom.empty;
1047 atom.sym_index = sym_index;
1048 atom.size = @sizeOf(u64);
1049 atom.alignment = @alignOf(u64);
1050 break :blk atom;
11681051 };
11691052 errdefer gpa.destroy(atom);
11701053
......@@ -1174,61 +1057,31 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
11741057 const sym = atom.getSymbolPtr(self);
11751058 sym.n_type = macho.N_SECT;
11761059 sym.n_sect = self.got_section_index.? + 1;
1060 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
11771061
1178 if (self.mode == .incremental) {
1179 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1062 log.debug("allocated GOT atom at 0x{x}", .{sym.n_value});
11801063
1181 log.debug("allocated GOT atom at 0x{x}", .{sym.n_value});
1064 try atom.addRelocation(self, .{
1065 .@"type" = switch (self.base.options.target.cpu.arch) {
1066 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1067 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1068 else => unreachable,
1069 },
1070 .target = target,
1071 .offset = 0,
1072 .addend = 0,
1073 .pcrel = false,
1074 .length = 3,
1075 });
11821076
1183 try atom.addRelocation(self, .{
1184 .@"type" = switch (self.base.options.target.cpu.arch) {
1185 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1186 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1187 else => unreachable,
1188 },
1189 .target = target,
1077 const target_sym = self.getSymbol(target);
1078 if (target_sym.undf()) {
1079 try atom.addBinding(self, .{
1080 .target = self.getGlobal(self.getSymbolName(target)).?,
11901081 .offset = 0,
1191 .addend = 0,
1192 .pcrel = false,
1193 .length = 3,
11941082 });
1195
1196 const target_sym = self.getSymbol(target);
1197 if (target_sym.undf()) {
1198 try atom.addBinding(self, .{
1199 .target = self.getGlobal(self.getSymbolName(target)).?,
1200 .offset = 0,
1201 });
1202 } else {
1203 try atom.addRebase(self, 0);
1204 }
12051083 } else {
1206 try atom.relocs.append(gpa, .{
1207 .offset = 0,
1208 .target = target,
1209 .addend = 0,
1210 .subtractor = null,
1211 .pcrel = false,
1212 .length = 3,
1213 .@"type" = switch (self.base.options.target.cpu.arch) {
1214 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1215 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1216 else => unreachable,
1217 },
1218 });
1219
1220 const target_sym = self.getSymbol(target);
1221 if (target_sym.undf()) {
1222 const global = self.getGlobal(self.getSymbolName(target)).?;
1223 try atom.bindings.append(gpa, .{
1224 .target = global,
1225 .offset = 0,
1226 });
1227 } else {
1228 try atom.rebases.append(gpa, 0);
1229 }
1230
1231 try self.addAtomToSection(atom);
1084 try atom.addRebase(self, 0);
12321085 }
12331086
12341087 return atom;
......@@ -1241,16 +1094,13 @@ pub fn createDyldPrivateAtom(self: *MachO) !void {
12411094 const gpa = self.base.allocator;
12421095
12431096 const sym_index = try self.allocateSymbol();
1244 const atom = switch (self.mode) {
1245 .incremental => blk: {
1246 const atom = try gpa.create(Atom);
1247 atom.* = Atom.empty;
1248 atom.sym_index = sym_index;
1249 atom.size = @sizeOf(u64);
1250 atom.alignment = @alignOf(u64);
1251 break :blk atom;
1252 },
1253 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3),
1097 const atom = blk: {
1098 const atom = try gpa.create(Atom);
1099 atom.* = Atom.empty;
1100 atom.sym_index = sym_index;
1101 atom.size = @sizeOf(u64);
1102 atom.alignment = @alignOf(u64);
1103 break :blk atom;
12541104 };
12551105 errdefer gpa.destroy(atom);
12561106
......@@ -1262,13 +1112,9 @@ pub fn createDyldPrivateAtom(self: *MachO) !void {
12621112 try self.managed_atoms.append(gpa, atom);
12631113 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
12641114
1265 if (self.mode == .incremental) {
1266 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1267 log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});
1268 try self.writePtrWidthAtom(atom);
1269 } else {
1270 try self.addAtomToSection(atom);
1271 }
1115 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1116 log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});
1117 try self.writePtrWidthAtom(atom);
12721118}
12731119
12741120pub fn createStubHelperPreambleAtom(self: *MachO) !void {
......@@ -1282,26 +1128,18 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
12821128 .aarch64 => 6 * @sizeOf(u32),
12831129 else => unreachable,
12841130 };
1285 const alignment: u32 = switch (arch) {
1286 .x86_64 => 0,
1287 .aarch64 => 2,
1288 else => unreachable,
1289 };
12901131 const sym_index = try self.allocateSymbol();
1291 const atom = switch (self.mode) {
1292 .incremental => blk: {
1293 const atom = try gpa.create(Atom);
1294 atom.* = Atom.empty;
1295 atom.sym_index = sym_index;
1296 atom.size = size;
1297 atom.alignment = switch (arch) {
1298 .x86_64 => 1,
1299 .aarch64 => @alignOf(u32),
1300 else => unreachable,
1301 };
1302 break :blk atom;
1303 },
1304 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, size, alignment),
1132 const atom = blk: {
1133 const atom = try gpa.create(Atom);
1134 atom.* = Atom.empty;
1135 atom.sym_index = sym_index;
1136 atom.size = size;
1137 atom.alignment = switch (arch) {
1138 .x86_64 => 1,
1139 .aarch64 => @alignOf(u32),
1140 else => unreachable,
1141 };
1142 break :blk atom;
13051143 };
13061144 errdefer gpa.destroy(atom);
13071145
......@@ -1328,43 +1166,21 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
13281166 code[9] = 0xff;
13291167 code[10] = 0x25;
13301168
1331 if (self.mode == .incremental) {
1332 try atom.addRelocations(self, 2, .{ .{
1333 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1334 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1335 .offset = 3,
1336 .addend = 0,
1337 .pcrel = true,
1338 .length = 2,
1339 }, .{
1340 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_GOT),
1341 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1342 .offset = 11,
1343 .addend = 0,
1344 .pcrel = true,
1345 .length = 2,
1346 } });
1347 } else {
1348 try atom.relocs.ensureUnusedCapacity(self.base.allocator, 2);
1349 atom.relocs.appendAssumeCapacity(.{
1350 .offset = 3,
1351 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1352 .addend = 0,
1353 .subtractor = null,
1354 .pcrel = true,
1355 .length = 2,
1356 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1357 });
1358 atom.relocs.appendAssumeCapacity(.{
1359 .offset = 11,
1360 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1361 .addend = 0,
1362 .subtractor = null,
1363 .pcrel = true,
1364 .length = 2,
1365 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_GOT),
1366 });
1367 }
1169 try atom.addRelocations(self, 2, .{ .{
1170 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1171 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1172 .offset = 3,
1173 .addend = 0,
1174 .pcrel = true,
1175 .length = 2,
1176 }, .{
1177 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_GOT),
1178 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1179 .offset = 11,
1180 .addend = 0,
1181 .pcrel = true,
1182 .length = 2,
1183 } });
13681184 },
13691185
13701186 .aarch64 => {
......@@ -1390,75 +1206,35 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
13901206 // br x16
13911207 mem.writeIntLittle(u32, code[20..][0..4], aarch64.Instruction.br(.x16).toU32());
13921208
1393 if (self.mode == .incremental) {
1394 try atom.addRelocations(self, 4, .{ .{
1395 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1396 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1397 .offset = 0,
1398 .addend = 0,
1399 .pcrel = true,
1400 .length = 2,
1401 }, .{
1402 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
1403 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1404 .offset = 4,
1405 .addend = 0,
1406 .pcrel = false,
1407 .length = 2,
1408 }, .{
1409 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
1410 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1411 .offset = 12,
1412 .addend = 0,
1413 .pcrel = true,
1414 .length = 2,
1415 }, .{
1416 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
1417 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1418 .offset = 16,
1419 .addend = 0,
1420 .pcrel = false,
1421 .length = 2,
1422 } });
1423 } else {
1424 try atom.relocs.ensureUnusedCapacity(gpa, 4);
1425 atom.relocs.appendAssumeCapacity(.{
1426 .offset = 0,
1427 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1428 .addend = 0,
1429 .subtractor = null,
1430 .pcrel = true,
1431 .length = 2,
1432 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1433 });
1434 atom.relocs.appendAssumeCapacity(.{
1435 .offset = 4,
1436 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1437 .addend = 0,
1438 .subtractor = null,
1439 .pcrel = false,
1440 .length = 2,
1441 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
1442 });
1443 atom.relocs.appendAssumeCapacity(.{
1444 .offset = 12,
1445 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1446 .addend = 0,
1447 .subtractor = null,
1448 .pcrel = true,
1449 .length = 2,
1450 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
1451 });
1452 atom.relocs.appendAssumeCapacity(.{
1453 .offset = 16,
1454 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1455 .addend = 0,
1456 .subtractor = null,
1457 .pcrel = false,
1458 .length = 2,
1459 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
1460 });
1461 }
1209 try atom.addRelocations(self, 4, .{ .{
1210 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1211 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1212 .offset = 0,
1213 .addend = 0,
1214 .pcrel = true,
1215 .length = 2,
1216 }, .{
1217 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
1218 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1219 .offset = 4,
1220 .addend = 0,
1221 .pcrel = false,
1222 .length = 2,
1223 }, .{
1224 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
1225 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1226 .offset = 12,
1227 .addend = 0,
1228 .pcrel = true,
1229 .length = 2,
1230 }, .{
1231 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
1232 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1233 .offset = 16,
1234 .addend = 0,
1235 .pcrel = false,
1236 .length = 2,
1237 } });
14621238 },
14631239
14641240 else => unreachable,
......@@ -1468,14 +1244,9 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
14681244 try self.managed_atoms.append(gpa, atom);
14691245 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
14701246
1471 if (self.mode == .incremental) {
1472 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1473 log.debug("allocated stub preamble atom at 0x{x}", .{sym.n_value});
1474 try self.writeAtom(atom, code);
1475 } else {
1476 mem.copy(u8, atom.code.items, code);
1477 try self.addAtomToSection(atom);
1478 }
1247 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1248 log.debug("allocated stub preamble atom at 0x{x}", .{sym.n_value});
1249 try self.writeAtom(atom, code);
14791250}
14801251
14811252pub fn createStubHelperAtom(self: *MachO) !*Atom {
......@@ -1486,26 +1257,18 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
14861257 .aarch64 => 3 * @sizeOf(u32),
14871258 else => unreachable,
14881259 };
1489 const alignment: u2 = switch (arch) {
1490 .x86_64 => 0,
1491 .aarch64 => 2,
1492 else => unreachable,
1493 };
14941260 const sym_index = try self.allocateSymbol();
1495 const atom = switch (self.mode) {
1496 .incremental => blk: {
1497 const atom = try gpa.create(Atom);
1498 atom.* = Atom.empty;
1499 atom.sym_index = sym_index;
1500 atom.size = size;
1501 atom.alignment = switch (arch) {
1502 .x86_64 => 1,
1503 .aarch64 => @alignOf(u32),
1504 else => unreachable,
1505 };
1506 break :blk atom;
1507 },
1508 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, size, alignment),
1261 const atom = blk: {
1262 const atom = try gpa.create(Atom);
1263 atom.* = Atom.empty;
1264 atom.sym_index = sym_index;
1265 atom.size = size;
1266 atom.alignment = switch (arch) {
1267 .x86_64 => 1,
1268 .aarch64 => @alignOf(u32),
1269 else => unreachable,
1270 };
1271 break :blk atom;
15091272 };
15101273 errdefer gpa.destroy(atom);
15111274
......@@ -1525,27 +1288,14 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
15251288 // jmpq
15261289 code[5] = 0xe9;
15271290
1528 if (self.mode == .incremental) {
1529 try atom.addRelocation(self, .{
1530 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1531 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
1532 .offset = 6,
1533 .addend = 0,
1534 .pcrel = true,
1535 .length = 2,
1536 });
1537 } else {
1538 try atom.relocs.ensureTotalCapacity(gpa, 1);
1539 atom.relocs.appendAssumeCapacity(.{
1540 .offset = 6,
1541 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
1542 .addend = 0,
1543 .subtractor = null,
1544 .pcrel = true,
1545 .length = 2,
1546 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1547 });
1548 }
1291 try atom.addRelocation(self, .{
1292 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1293 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
1294 .offset = 6,
1295 .addend = 0,
1296 .pcrel = true,
1297 .length = 2,
1298 });
15491299 },
15501300 .aarch64 => {
15511301 const literal = blk: {
......@@ -1561,27 +1311,14 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
15611311 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(0).toU32());
15621312 // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
15631313
1564 if (self.mode == .incremental) {
1565 try atom.addRelocation(self, .{
1566 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
1567 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
1568 .offset = 4,
1569 .addend = 0,
1570 .pcrel = true,
1571 .length = 2,
1572 });
1573 } else {
1574 try atom.relocs.ensureTotalCapacity(gpa, 1);
1575 atom.relocs.appendAssumeCapacity(.{
1576 .offset = 4,
1577 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
1578 .addend = 0,
1579 .subtractor = null,
1580 .pcrel = true,
1581 .length = 2,
1582 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
1583 });
1584 }
1314 try atom.addRelocation(self, .{
1315 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
1316 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
1317 .offset = 4,
1318 .addend = 0,
1319 .pcrel = true,
1320 .length = 2,
1321 });
15851322 },
15861323 else => unreachable,
15871324 }
......@@ -1589,14 +1326,9 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
15891326 try self.managed_atoms.append(gpa, atom);
15901327 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
15911328
1592 if (self.mode == .incremental) {
1593 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1594 log.debug("allocated stub helper atom at 0x{x}", .{sym.n_value});
1595 try self.writeAtom(atom, code);
1596 } else {
1597 mem.copy(u8, atom.code.items, code);
1598 try self.addAtomToSection(atom);
1599 }
1329 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1330 log.debug("allocated stub helper atom at 0x{x}", .{sym.n_value});
1331 try self.writeAtom(atom, code);
16001332
16011333 return atom;
16021334}
......@@ -1604,16 +1336,13 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
16041336pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !*Atom {
16051337 const gpa = self.base.allocator;
16061338 const sym_index = try self.allocateSymbol();
1607 const atom = switch (self.mode) {
1608 .incremental => blk: {
1609 const atom = try gpa.create(Atom);
1610 atom.* = Atom.empty;
1611 atom.sym_index = sym_index;
1612 atom.size = @sizeOf(u64);
1613 atom.alignment = @alignOf(u64);
1614 break :blk atom;
1615 },
1616 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3),
1339 const atom = blk: {
1340 const atom = try gpa.create(Atom);
1341 atom.* = Atom.empty;
1342 atom.sym_index = sym_index;
1343 atom.size = @sizeOf(u64);
1344 atom.alignment = @alignOf(u64);
1345 break :blk atom;
16171346 };
16181347 errdefer gpa.destroy(atom);
16191348
......@@ -1621,56 +1350,30 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi
16211350 sym.n_type = macho.N_SECT;
16221351 sym.n_sect = self.la_symbol_ptr_section_index.? + 1;
16231352
1624 if (self.mode == .incremental) {
1625 try atom.addRelocation(self, .{
1626 .@"type" = switch (self.base.options.target.cpu.arch) {
1627 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1628 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1629 else => unreachable,
1630 },
1631 .target = .{ .sym_index = stub_sym_index, .file = null },
1632 .offset = 0,
1633 .addend = 0,
1634 .pcrel = false,
1635 .length = 3,
1636 });
1637 try atom.addRebase(self, 0);
1638 try atom.addLazyBinding(self, .{
1639 .target = self.getGlobal(self.getSymbolName(target)).?,
1640 .offset = 0,
1641 });
1642 } else {
1643 try atom.relocs.append(gpa, .{
1644 .offset = 0,
1645 .target = .{ .sym_index = stub_sym_index, .file = null },
1646 .addend = 0,
1647 .subtractor = null,
1648 .pcrel = false,
1649 .length = 3,
1650 .@"type" = switch (self.base.options.target.cpu.arch) {
1651 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1652 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1653 else => unreachable,
1654 },
1655 });
1656 try atom.rebases.append(gpa, 0);
1657 const global = self.getGlobal(self.getSymbolName(target)).?;
1658 try atom.lazy_bindings.append(gpa, .{
1659 .target = global,
1660 .offset = 0,
1661 });
1662 }
1353 try atom.addRelocation(self, .{
1354 .@"type" = switch (self.base.options.target.cpu.arch) {
1355 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1356 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1357 else => unreachable,
1358 },
1359 .target = .{ .sym_index = stub_sym_index, .file = null },
1360 .offset = 0,
1361 .addend = 0,
1362 .pcrel = false,
1363 .length = 3,
1364 });
1365 try atom.addRebase(self, 0);
1366 try atom.addLazyBinding(self, .{
1367 .target = self.getGlobal(self.getSymbolName(target)).?,
1368 .offset = 0,
1369 });
16631370
16641371 try self.managed_atoms.append(gpa, atom);
16651372 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
16661373
1667 if (self.mode == .incremental) {
1668 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1669 log.debug("allocated lazy pointer atom at 0x{x}", .{sym.n_value});
1670 try self.writePtrWidthAtom(atom);
1671 } else {
1672 try self.addAtomToSection(atom);
1673 }
1374 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1375 log.debug("allocated lazy pointer atom at 0x{x}", .{sym.n_value});
1376 try self.writePtrWidthAtom(atom);
16741377
16751378 return atom;
16761379}
......@@ -1678,32 +1381,24 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi
16781381pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
16791382 const gpa = self.base.allocator;
16801383 const arch = self.base.options.target.cpu.arch;
1681 const alignment: u2 = switch (arch) {
1682 .x86_64 => 0,
1683 .aarch64 => 2,
1684 else => unreachable, // unhandled architecture type
1685 };
16861384 const size: u4 = switch (arch) {
16871385 .x86_64 => 6,
16881386 .aarch64 => 3 * @sizeOf(u32),
16891387 else => unreachable, // unhandled architecture type
16901388 };
16911389 const sym_index = try self.allocateSymbol();
1692 const atom = switch (self.mode) {
1693 .incremental => blk: {
1694 const atom = try gpa.create(Atom);
1695 atom.* = Atom.empty;
1696 atom.sym_index = sym_index;
1697 atom.size = size;
1698 atom.alignment = switch (arch) {
1699 .x86_64 => 1,
1700 .aarch64 => @alignOf(u32),
1701 else => unreachable, // unhandled architecture type
1390 const atom = blk: {
1391 const atom = try gpa.create(Atom);
1392 atom.* = Atom.empty;
1393 atom.sym_index = sym_index;
1394 atom.size = size;
1395 atom.alignment = switch (arch) {
1396 .x86_64 => 1,
1397 .aarch64 => @alignOf(u32),
1398 else => unreachable, // unhandled architecture type
17021399
1703 };
1704 break :blk atom;
1705 },
1706 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, size, alignment),
1400 };
1401 break :blk atom;
17071402 };
17081403 errdefer gpa.destroy(atom);
17091404
......@@ -1721,26 +1416,14 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
17211416 code[0] = 0xff;
17221417 code[1] = 0x25;
17231418
1724 if (self.mode == .incremental) {
1725 try atom.addRelocation(self, .{
1726 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1727 .target = .{ .sym_index = laptr_sym_index, .file = null },
1728 .offset = 2,
1729 .addend = 0,
1730 .pcrel = true,
1731 .length = 2,
1732 });
1733 } else {
1734 try atom.relocs.append(gpa, .{
1735 .offset = 2,
1736 .target = .{ .sym_index = laptr_sym_index, .file = null },
1737 .addend = 0,
1738 .subtractor = null,
1739 .pcrel = true,
1740 .length = 2,
1741 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1742 });
1743 }
1419 try atom.addRelocation(self, .{
1420 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1421 .target = .{ .sym_index = laptr_sym_index, .file = null },
1422 .offset = 2,
1423 .addend = 0,
1424 .pcrel = true,
1425 .length = 2,
1426 });
17441427 },
17451428 .aarch64 => {
17461429 // adrp x16, pages
......@@ -1754,46 +1437,24 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
17541437 // br x16
17551438 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32());
17561439
1757 if (self.mode == .incremental) {
1758 try atom.addRelocations(self, 2, .{
1759 .{
1760 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1761 .target = .{ .sym_index = laptr_sym_index, .file = null },
1762 .offset = 0,
1763 .addend = 0,
1764 .pcrel = true,
1765 .length = 2,
1766 },
1767 .{
1768 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
1769 .target = .{ .sym_index = laptr_sym_index, .file = null },
1770 .offset = 4,
1771 .addend = 0,
1772 .pcrel = false,
1773 .length = 2,
1774 },
1775 });
1776 } else {
1777 try atom.relocs.ensureTotalCapacity(gpa, 2);
1778 atom.relocs.appendAssumeCapacity(.{
1779 .offset = 0,
1440 try atom.addRelocations(self, 2, .{
1441 .{
1442 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
17801443 .target = .{ .sym_index = laptr_sym_index, .file = null },
1444 .offset = 0,
17811445 .addend = 0,
1782 .subtractor = null,
17831446 .pcrel = true,
17841447 .length = 2,
1785 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1786 });
1787 atom.relocs.appendAssumeCapacity(.{
1788 .offset = 4,
1448 },
1449 .{
1450 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
17891451 .target = .{ .sym_index = laptr_sym_index, .file = null },
1452 .offset = 4,
17901453 .addend = 0,
1791 .subtractor = null,
17921454 .pcrel = false,
17931455 .length = 2,
1794 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
1795 });
1796 }
1456 },
1457 });
17971458 },
17981459 else => unreachable,
17991460 }
......@@ -1801,96 +1462,13 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
18011462 try self.managed_atoms.append(gpa, atom);
18021463 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
18031464
1804 if (self.mode == .incremental) {
1805 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1806 log.debug("allocated stub atom at 0x{x}", .{sym.n_value});
1807 try self.writeAtom(atom, code);
1808 } else {
1809 mem.copy(u8, atom.code.items, code);
1810 try self.addAtomToSection(atom);
1811 }
1812
1813 return atom;
1814}
1815
1816pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
1817 assert(self.mode == .one_shot);
1818
1819 const gpa = self.base.allocator;
1820 const sym_index = try self.allocateSymbol();
1821 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
1822
1823 const target_sym = self.getSymbol(target);
1824 assert(target_sym.undf());
1825
1826 const global = self.getGlobal(self.getSymbolName(target)).?;
1827 try atom.bindings.append(gpa, .{
1828 .target = global,
1829 .offset = 0,
1830 });
1831
1832 try self.managed_atoms.append(gpa, atom);
1833 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
1834
1835 const sym = atom.getSymbolPtr(self);
1836 sym.n_type = macho.N_SECT;
1837 const sect_id = (try self.getOutputSection(.{
1838 .segname = makeStaticString("__DATA"),
1839 .sectname = makeStaticString("__thread_ptrs"),
1840 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
1841 })).?;
1842 sym.n_sect = sect_id + 1;
1843
1844 try self.addAtomToSection(atom);
1465 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1466 log.debug("allocated stub atom at 0x{x}", .{sym.n_value});
1467 try self.writeAtom(atom, code);
18451468
18461469 return atom;
18471470}
18481471
1849pub fn createTentativeDefAtoms(self: *MachO) !void {
1850 assert(self.mode == .one_shot);
1851 const gpa = self.base.allocator;
1852
1853 for (self.globals.items) |global| {
1854 const sym = self.getSymbolPtr(global);
1855 if (!sym.tentative()) continue;
1856
1857 log.debug("creating tentative definition for ATOM(%{d}, '{s}') in object({?d})", .{
1858 global.sym_index, self.getSymbolName(global), global.file,
1859 });
1860
1861 // Convert any tentative definition into a regular symbol and allocate
1862 // text blocks for each tentative definition.
1863 const size = sym.n_value;
1864 const alignment = (sym.n_desc >> 8) & 0x0f;
1865 const sect_id = (try self.getOutputSection(.{
1866 .segname = makeStaticString("__DATA"),
1867 .sectname = makeStaticString("__bss"),
1868 .flags = macho.S_ZEROFILL,
1869 })).?;
1870 sym.* = .{
1871 .n_strx = sym.n_strx,
1872 .n_type = macho.N_SECT | macho.N_EXT,
1873 .n_sect = sect_id + 1,
1874 .n_desc = 0,
1875 .n_value = 0,
1876 };
1877
1878 const atom = try MachO.createEmptyAtom(gpa, global.sym_index, size, alignment);
1879 atom.file = global.file;
1880
1881 try self.addAtomToSection(atom);
1882
1883 if (global.file) |file| {
1884 const object = &self.objects.items[file];
1885 try object.managed_atoms.append(gpa, atom);
1886 try object.atom_by_index_table.putNoClobber(gpa, global.sym_index, atom);
1887 } else {
1888 try self.managed_atoms.append(gpa, atom);
1889 try self.atom_by_index_table.putNoClobber(gpa, global.sym_index, atom);
1890 }
1891 }
1892}
1893
18941472pub fn createMhExecuteHeaderSymbol(self: *MachO) !void {
18951473 if (self.base.options.output_mode != .Exe) return;
18961474 if (self.getGlobal("__mh_execute_header")) |global| {
......@@ -1989,90 +1567,6 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
19891567 gop.value_ptr.* = current;
19901568}
19911569
1992pub fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
1993 const object = &self.objects.items[object_id];
1994 log.debug("resolving symbols in '{s}'", .{object.name});
1995
1996 for (object.symtab.items) |sym, index| {
1997 const sym_index = @intCast(u32, index);
1998 const sym_name = object.getString(sym.n_strx);
1999
2000 if (sym.stab()) {
2001 log.err("unhandled symbol type: stab", .{});
2002 log.err(" symbol '{s}'", .{sym_name});
2003 log.err(" first definition in '{s}'", .{object.name});
2004 return error.UnhandledSymbolType;
2005 }
2006
2007 if (sym.indr()) {
2008 log.err("unhandled symbol type: indirect", .{});
2009 log.err(" symbol '{s}'", .{sym_name});
2010 log.err(" first definition in '{s}'", .{object.name});
2011 return error.UnhandledSymbolType;
2012 }
2013
2014 if (sym.abs()) {
2015 log.err("unhandled symbol type: absolute", .{});
2016 log.err(" symbol '{s}'", .{sym_name});
2017 log.err(" first definition in '{s}'", .{object.name});
2018 return error.UnhandledSymbolType;
2019 }
2020
2021 if (sym.sect() and !sym.ext()) {
2022 log.debug("symbol '{s}' local to object {s}; skipping...", .{
2023 sym_name,
2024 object.name,
2025 });
2026 continue;
2027 }
2028
2029 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id };
2030 self.resolveGlobalSymbol(sym_loc) catch |err| switch (err) {
2031 error.MultipleSymbolDefinitions => {
2032 const global = self.getGlobal(sym_name).?;
2033 log.err("symbol '{s}' defined multiple times", .{sym_name});
2034 if (global.file) |file| {
2035 log.err(" first definition in '{s}'", .{self.objects.items[file].name});
2036 }
2037 log.err(" next definition in '{s}'", .{self.objects.items[object_id].name});
2038 return error.MultipleSymbolDefinitions;
2039 },
2040 else => |e| return e,
2041 };
2042 }
2043}
2044
2045pub fn resolveSymbolsInArchives(self: *MachO) !void {
2046 if (self.archives.items.len == 0) return;
2047
2048 const gpa = self.base.allocator;
2049 const cpu_arch = self.base.options.target.cpu.arch;
2050 var next_sym: usize = 0;
2051 loop: while (next_sym < self.unresolved.count()) {
2052 const global_index = self.unresolved.keys()[next_sym];
2053 const global = self.globals.items[global_index];
2054 const sym_name = self.getSymbolName(global);
2055
2056 for (self.archives.items) |archive| {
2057 // Check if the entry exists in a static archive.
2058 const offsets = archive.toc.get(sym_name) orelse {
2059 // No hit.
2060 continue;
2061 };
2062 assert(offsets.items.len > 0);
2063
2064 const object_id = @intCast(u16, self.objects.items.len);
2065 const object = try archive.parseObject(gpa, cpu_arch, offsets.items[0]);
2066 try self.objects.append(gpa, object);
2067 try self.resolveSymbolsInObject(object_id);
2068
2069 continue :loop;
2070 }
2071
2072 next_sym += 1;
2073 }
2074}
2075
20761570pub fn resolveSymbolsInDylibs(self: *MachO) !void {
20771571 if (self.dylibs.items.len == 0) return;
20781572
......@@ -2209,9 +1703,7 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {
22091703 const got_atom = try self.createGotAtom(global);
22101704 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
22111705
2212 if (self.mode == .incremental) {
2213 try self.writePtrWidthAtom(got_atom);
2214 }
1706 try self.writePtrWidthAtom(got_atom);
22151707}
22161708
22171709pub fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
......@@ -2236,10 +1728,7 @@ pub fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
22361728
22371729pub fn writeMainLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
22381730 if (self.base.options.output_mode != .Exe) return;
2239 const seg_id = switch (self.mode) {
2240 .incremental => self.header_segment_cmd_index.?,
2241 .one_shot => self.text_segment_cmd_index.?,
2242 };
1731 const seg_id = self.header_segment_cmd_index.?;
22431732 const seg = self.segments.items[seg_id];
22441733 const global = try self.getEntryPoint();
22451734 const sym = self.getSymbol(global);
......@@ -2417,9 +1906,6 @@ pub fn deinit(self: *MachO) void {
24171906 d_sym.deinit(gpa);
24181907 }
24191908
2420 self.tlv_ptr_entries.deinit(gpa);
2421 self.tlv_ptr_entries_free_list.deinit(gpa);
2422 self.tlv_ptr_entries_table.deinit(gpa);
24231909 self.got_entries.deinit(gpa);
24241910 self.got_entries_free_list.deinit(gpa);
24251911 self.got_entries_table.deinit(gpa);
......@@ -2442,16 +1928,6 @@ pub fn deinit(self: *MachO) void {
24421928 self.resolver.deinit(gpa);
24431929 }
24441930
2445 for (self.objects.items) |*object| {
2446 object.deinit(gpa);
2447 }
2448 self.objects.deinit(gpa);
2449
2450 for (self.archives.items) |*archive| {
2451 archive.deinit(gpa);
2452 }
2453 self.archives.deinit(gpa);
2454
24551931 for (self.dylibs.items) |*dylib| {
24561932 dylib.deinit(gpa);
24571933 }
......@@ -2467,16 +1943,11 @@ pub fn deinit(self: *MachO) void {
24671943 self.sections.deinit(gpa);
24681944
24691945 for (self.managed_atoms.items) |atom| {
2470 atom.deinit(gpa);
24711946 gpa.destroy(atom);
24721947 }
24731948 self.managed_atoms.deinit(gpa);
24741949
2475 if (self.base.options.module) |mod| {
2476 for (self.decls.keys()) |decl_index| {
2477 const decl = mod.declPtr(decl_index);
2478 decl.link.macho.deinit(gpa);
2479 }
1950 if (self.base.options.module) |_| {
24801951 self.decls.deinit(gpa);
24811952 } else {
24821953 assert(self.decls.count() == 0);
......@@ -2525,11 +1996,9 @@ pub fn deinit(self: *MachO) void {
25251996 }
25261997}
25271998
2528fn freeAtom(self: *MachO, atom: *Atom, owns_atom: bool) void {
1999fn freeAtom(self: *MachO, atom: *Atom) void {
25292000 log.debug("freeAtom {*}", .{atom});
2530 if (!owns_atom) {
2531 atom.deinit(self.base.allocator);
2532 }
2001
25332002 // Remove any relocs and base relocs associated with this Atom
25342003 self.freeRelocationsForAtom(atom);
25352004
......@@ -2694,27 +2163,6 @@ pub fn allocateStubEntry(self: *MachO, target: SymbolWithLoc) !u32 {
26942163 return index;
26952164}
26962165
2697pub fn allocateTlvPtrEntry(self: *MachO, target: SymbolWithLoc) !u32 {
2698 try self.tlv_ptr_entries.ensureUnusedCapacity(self.base.allocator, 1);
2699
2700 const index = blk: {
2701 if (self.tlv_ptr_entries_free_list.popOrNull()) |index| {
2702 log.debug(" (reusing TLV ptr entry index {d})", .{index});
2703 break :blk index;
2704 } else {
2705 log.debug(" (allocating TLV ptr entry at index {d})", .{self.tlv_ptr_entries.items.len});
2706 const index = @intCast(u32, self.tlv_ptr_entries.items.len);
2707 _ = self.tlv_ptr_entries.addOneAssumeCapacity();
2708 break :blk index;
2709 }
2710 };
2711
2712 self.tlv_ptr_entries.items[index] = .{ .target = target, .sym_index = 0 };
2713 try self.tlv_ptr_entries_table.putNoClobber(self.base.allocator, target, index);
2714
2715 return index;
2716}
2717
27182166pub fn allocateDeclIndexes(self: *MachO, decl_index: Module.Decl.Index) !void {
27192167 if (self.llvm_object) |_| return;
27202168 const decl = self.base.options.module.?.declPtr(decl_index);
......@@ -2845,7 +2293,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
28452293 symbol.n_type = macho.N_SECT;
28462294 symbol.n_sect = sect_id + 1;
28472295 symbol.n_value = try self.allocateAtom(atom, code.len, required_alignment);
2848 errdefer self.freeAtom(atom, true);
2296 errdefer self.freeAtom(atom);
28492297
28502298 try unnamed_consts.append(gpa, atom);
28512299
......@@ -3137,7 +2585,7 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)
31372585 sym.n_desc = 0;
31382586
31392587 const vaddr = try self.allocateAtom(atom, code_len, required_alignment);
3140 errdefer self.freeAtom(atom, false);
2588 errdefer self.freeAtom(atom);
31412589
31422590 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, vaddr });
31432591 log.debug(" (required alignment 0x{x})", .{required_alignment});
......@@ -3256,17 +2704,13 @@ pub fn updateDeclExports(
32562704
32572705 self.resolveGlobalSymbol(sym_loc) catch |err| switch (err) {
32582706 error.MultipleSymbolDefinitions => {
3259 const global = self.getGlobal(exp_name).?;
3260 if (sym_loc.sym_index != global.sym_index and global.file != null) {
3261 _ = try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(
3262 gpa,
3263 decl.srcLoc(),
3264 \\LinkError: symbol '{s}' defined multiple times
3265 \\ first definition in '{s}'
3266 ,
3267 .{ exp_name, self.objects.items[global.file.?].name },
3268 ));
3269 }
2707 _ = try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(
2708 gpa,
2709 decl.srcLoc(),
2710 \\LinkError: symbol '{s}' defined multiple times
2711 ,
2712 .{exp_name},
2713 ));
32702714 },
32712715 else => |e| return e,
32722716 };
......@@ -3314,7 +2758,7 @@ fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
33142758 const gpa = self.base.allocator;
33152759 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
33162760 for (unnamed_consts.items) |atom| {
3317 self.freeAtom(atom, true);
2761 self.freeAtom(atom);
33182762 self.locals_free_list.append(gpa, atom.sym_index) catch {};
33192763 self.locals.items[atom.sym_index].n_type = 0;
33202764 _ = self.atom_by_index_table.remove(atom.sym_index);
......@@ -3335,7 +2779,7 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
33352779
33362780 const kv = self.decls.fetchSwapRemove(decl_index);
33372781 if (kv.?.value) |_| {
3338 self.freeAtom(&decl.link.macho, false);
2782 self.freeAtom(&decl.link.macho);
33392783 self.freeUnnamedConsts(decl_index);
33402784 }
33412785
......@@ -3969,22 +3413,6 @@ fn insertSection(self: *MachO, segment_index: u8, header: macho.section_64) !u8
39693413 return insertion_index;
39703414}
39713415
3972pub fn addAtomToSection(self: *MachO, atom: *Atom) !void {
3973 const sect_id = atom.getSymbol(self).n_sect - 1;
3974 var section = self.sections.get(sect_id);
3975 if (section.header.size > 0) {
3976 section.last_atom.?.next = atom;
3977 atom.prev = section.last_atom.?;
3978 }
3979 section.last_atom = atom;
3980 const atom_alignment = try math.powi(u32, 2, atom.alignment);
3981 const aligned_end_addr = mem.alignForwardGeneric(u64, section.header.size, atom_alignment);
3982 const padding = aligned_end_addr - section.header.size;
3983 section.header.size += padding + atom.size;
3984 section.header.@"align" = @max(section.header.@"align", atom.alignment);
3985 self.sections.set(sect_id, section);
3986}
3987
39883416pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
39893417 const gpa = self.base.allocator;
39903418
......@@ -4429,19 +3857,6 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
44293857 try locals.append(sym);
44303858 }
44313859
4432 for (self.objects.items) |object, object_id| {
4433 for (object.symtab.items) |sym, sym_id| {
4434 if (sym.n_strx == 0) continue; // no name, skip
4435 if (sym.n_desc == N_DESC_GCED) continue; // GCed, skip
4436 const sym_loc = SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = @intCast(u32, object_id) };
4437 if (self.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
4438 if (self.getGlobal(self.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip
4439 var out_sym = sym;
4440 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(sym_loc));
4441 try locals.append(out_sym);
4442 }
4443 }
4444
44453860 var exports = std.ArrayList(macho.nlist_64).init(gpa);
44463861 defer exports.deinit();
44473862
......@@ -4807,12 +4222,8 @@ pub fn symbolIsTemp(self: *MachO, sym_with_loc: SymbolWithLoc) bool {
48074222
48084223/// Returns pointer-to-symbol described by `sym_with_loc` descriptor.
48094224pub fn getSymbolPtr(self: *MachO, sym_with_loc: SymbolWithLoc) *macho.nlist_64 {
4810 if (sym_with_loc.file) |file| {
4811 const object = &self.objects.items[file];
4812 return &object.symtab.items[sym_with_loc.sym_index];
4813 } else {
4814 return &self.locals.items[sym_with_loc.sym_index];
4815 }
4225 assert(sym_with_loc.file == null);
4226 return &self.locals.items[sym_with_loc.sym_index];
48164227}
48174228
48184229/// Returns symbol described by `sym_with_loc` descriptor.
......@@ -4822,14 +4233,9 @@ pub fn getSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
48224233
48234234/// Returns name of the symbol described by `sym_with_loc` descriptor.
48244235pub fn getSymbolName(self: *MachO, sym_with_loc: SymbolWithLoc) []const u8 {
4825 if (sym_with_loc.file) |file| {
4826 const object = self.objects.items[file];
4827 const sym = object.symtab.items[sym_with_loc.sym_index];
4828 return object.getString(sym.n_strx);
4829 } else {
4830 const sym = self.locals.items[sym_with_loc.sym_index];
4831 return self.strtab.get(sym.n_strx).?;
4832 }
4236 assert(sym_with_loc.file == null);
4237 const sym = self.locals.items[sym_with_loc.sym_index];
4238 return self.strtab.get(sym.n_strx).?;
48334239}
48344240
48354241/// Returns pointer to the global entry for `name` if one exists.
......@@ -4878,12 +4284,8 @@ pub fn getOrPutGlobalPtr(self: *MachO, name: []const u8) !GetOrPutGlobalPtrResul
48784284/// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor.
48794285/// Returns null on failure.
48804286pub fn getAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
4881 if (sym_with_loc.file) |file| {
4882 const object = self.objects.items[file];
4883 return object.getAtomForSymbol(sym_with_loc.sym_index);
4884 } else {
4885 return self.atom_by_index_table.get(sym_with_loc.sym_index);
4886 }
4287 assert(sym_with_loc.file == null);
4288 return self.atom_by_index_table.get(sym_with_loc.sym_index);
48874289}
48884290
48894291/// Returns GOT atom that references `sym_with_loc` if one exists.
......@@ -4900,13 +4302,6 @@ pub fn getStubsAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
49004302 return self.stubs.items[stubs_index].getAtom(self);
49014303}
49024304
4903/// Returns TLV pointer atom that references `sym_with_loc` if one exists.
4904/// Returns null otherwise.
4905pub fn getTlvPtrAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
4906 const tlv_ptr_index = self.tlv_ptr_entries_table.get(sym_with_loc) orelse return null;
4907 return self.tlv_ptr_entries.items[tlv_ptr_index].getAtom(self);
4908}
4909
49104305/// Returns symbol location corresponding to the set entrypoint.
49114306/// Asserts output mode is executable.
49124307pub fn getEntryPoint(self: MachO) error{MissingMainEntrypoint}!SymbolWithLoc {
......@@ -5228,25 +4623,6 @@ pub fn logSymtab(self: *MachO) void {
52284623 var buf: [9]u8 = undefined;
52294624
52304625 log.debug("symtab:", .{});
5231 for (self.objects.items) |object, id| {
5232 log.debug(" object({d}): {s}", .{ id, object.name });
5233 for (object.symtab.items) |sym, sym_id| {
5234 const where = if (sym.undf() and !sym.tentative()) "ord" else "sect";
5235 const def_index = if (sym.undf() and !sym.tentative())
5236 @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER)
5237 else
5238 sym.n_sect + 1;
5239 log.debug(" %{d}: {s} @{x} in {s}({d}), {s}", .{
5240 sym_id,
5241 object.getString(sym.n_strx),
5242 sym.n_value,
5243 where,
5244 def_index,
5245 logSymAttributes(sym, &buf),
5246 });
5247 }
5248 }
5249 log.debug(" object(null)", .{});
52504626 for (self.locals.items) |sym, sym_id| {
52514627 const where = if (sym.undf() and !sym.tentative()) "ord" else "sect";
52524628 const def_index = if (sym.undf() and !sym.tentative())
......@@ -5291,19 +4667,6 @@ pub fn logSymtab(self: *MachO) void {
52914667 }
52924668 }
52934669
5294 log.debug("__thread_ptrs entries:", .{});
5295 for (self.tlv_ptr_entries.items) |entry, i| {
5296 const atom_sym = entry.getSymbol(self);
5297 if (atom_sym.n_desc == N_DESC_GCED) continue;
5298 const target_sym = self.getSymbol(entry.target);
5299 assert(target_sym.undf());
5300 log.debug(" {d}@{x} => import('{s}')", .{
5301 i,
5302 atom_sym.n_value,
5303 self.getSymbolName(entry.target),
5304 });
5305 }
5306
53074670 log.debug("stubs entries:", .{});
53084671 for (self.stubs.items) |entry, i| {
53094672 const target_sym = self.getSymbol(entry.target);
......@@ -5352,21 +4715,4 @@ pub fn logAtom(self: *MachO, atom: *const Atom) void {
53524715 atom.file,
53534716 sym.n_sect,
53544717 });
5355
5356 for (atom.contained.items) |sym_off| {
5357 const inner_sym = self.getSymbol(.{
5358 .sym_index = sym_off.sym_index,
5359 .file = atom.file,
5360 });
5361 const inner_sym_name = self.getSymbolName(.{
5362 .sym_index = sym_off.sym_index,
5363 .file = atom.file,
5364 });
5365 log.debug(" (%{d}, '{s}') @ {x} ({x})", .{
5366 sym_off.sym_index,
5367 inner_sym_name,
5368 inner_sym.n_value,
5369 sym_off.offset,
5370 });
5371 }
53724718}
src/link/MachO/Archive.zig+10-10
......@@ -3,7 +3,7 @@ const Archive = @This();
33const std = @import("std");
44const assert = std.debug.assert;
55const fs = std.fs;
6const log = std.log.scoped(.link);
6const log = std.log.scoped(.macho);
77const macho = std.macho;
88const mem = std.mem;
99
......@@ -88,7 +88,6 @@ const ar_hdr = extern struct {
8888};
8989
9090pub fn deinit(self: *Archive, allocator: Allocator) void {
91 self.file.close();
9291 for (self.toc.keys()) |*key| {
9392 allocator.free(key.*);
9493 }
......@@ -165,6 +164,7 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !
165164 while (true) {
166165 const n_strx = symtab_reader.readIntLittle(u32) catch |err| switch (err) {
167166 error.EndOfStream => break,
167 else => |e| return e,
168168 };
169169 const object_offset = try symtab_reader.readIntLittle(u32);
170170
......@@ -183,7 +183,7 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !
183183
184184pub fn parseObject(
185185 self: Archive,
186 allocator: Allocator,
186 gpa: Allocator,
187187 cpu_arch: std.Target.Cpu.Arch,
188188 offset: u32,
189189) !Object {
......@@ -198,15 +198,15 @@ pub fn parseObject(
198198 }
199199
200200 const name_or_length = try object_header.nameOrLength();
201 const object_name = try parseName(allocator, name_or_length, reader);
202 defer allocator.free(object_name);
201 const object_name = try parseName(gpa, name_or_length, reader);
202 defer gpa.free(object_name);
203203
204204 log.debug("extracting object '{s}' from archive '{s}'", .{ object_name, self.name });
205205
206206 const name = name: {
207207 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
208208 const path = try std.os.realpath(self.name, &buffer);
209 break :name try std.fmt.allocPrint(allocator, "{s}({s})", .{ path, object_name });
209 break :name try std.fmt.allocPrint(gpa, "{s}({s})", .{ path, object_name });
210210 };
211211
212212 const object_name_len = switch (name_or_length) {
......@@ -214,19 +214,19 @@ pub fn parseObject(
214214 .Length => |len| len,
215215 };
216216 const object_size = (try object_header.size()) - object_name_len;
217 const contents = try allocator.allocWithOptions(u8, object_size, @alignOf(u64), null);
217 const contents = try gpa.allocWithOptions(u8, object_size, @alignOf(u64), null);
218218 const amt = try reader.readAll(contents);
219219 if (amt != object_size) {
220 return error.InputOutput;
220 return error.Io;
221221 }
222222
223223 var object = Object{
224224 .name = name,
225 .mtime = try self.header.date(),
225 .mtime = object_header.date() catch 0,
226226 .contents = contents,
227227 };
228228
229 try object.parse(allocator, cpu_arch);
229 try object.parse(gpa, cpu_arch);
230230
231231 return object;
232232}
src/link/MachO/Atom.zig+3-781
......@@ -15,8 +15,7 @@ const Allocator = mem.Allocator;
1515const Arch = std.Target.Cpu.Arch;
1616const Dwarf = @import("../Dwarf.zig");
1717const MachO = @import("../MachO.zig");
18const Object = @import("Object.zig");
19const RelocationIncr = @import("Relocation.zig"); // temporary name until we clean up object-file relocation scanning
18const Relocation = @import("Relocation.zig");
2019const SymbolWithLoc = MachO.SymbolWithLoc;
2120
2221/// Each decl always gets a local symbol with the fully qualified name.
......@@ -30,12 +29,6 @@ sym_index: u32,
3029/// null means symbol defined by Zig source.
3130file: ?u32,
3231
33/// List of symbols contained within this atom
34contained: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
35
36/// Code (may be non-relocated) this atom represents
37code: std.ArrayListUnmanaged(u8) = .{},
38
3932/// Size and alignment of this atom
4033/// Unlike in Elf, we need to store the size of this symbol as part of
4134/// the atom since macho.nlist_64 lacks this information.
......@@ -45,21 +38,6 @@ size: u64,
4538/// For instance, alignment of 0 should be read as 2^0 = 1 byte aligned.
4639alignment: u32,
4740
48/// List of relocations belonging to this atom.
49relocs: std.ArrayListUnmanaged(Relocation) = .{},
50
51/// List of offsets contained within this atom that need rebasing by the dynamic
52/// loader for example in presence of ASLR.
53rebases: std.ArrayListUnmanaged(u64) = .{},
54
55/// List of offsets contained within this atom that will be dynamically bound
56/// by the dynamic loader and contain pointers to resolved (at load time) extern
57/// symbols (aka proxies aka imports).
58bindings: std.ArrayListUnmanaged(Binding) = .{},
59
60/// List of lazy bindings (cf bindings above).
61lazy_bindings: std.ArrayListUnmanaged(Binding) = .{},
62
6341/// Points to the previous and next neighbours
6442next: ?*Atom,
6543prev: ?*Atom,
......@@ -76,50 +54,6 @@ pub const SymbolAtOffset = struct {
7654 offset: u64,
7755};
7856
79pub const Relocation = struct {
80 /// Offset within the atom's code buffer.
81 /// Note relocation size can be inferred by relocation's kind.
82 offset: u32,
83
84 target: MachO.SymbolWithLoc,
85
86 addend: i64,
87
88 subtractor: ?MachO.SymbolWithLoc,
89
90 pcrel: bool,
91
92 length: u2,
93
94 @"type": u4,
95
96 pub fn getTargetAtom(self: Relocation, macho_file: *MachO) ?*Atom {
97 const is_via_got = got: {
98 switch (macho_file.base.options.target.cpu.arch) {
99 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, self.@"type")) {
100 .ARM64_RELOC_GOT_LOAD_PAGE21,
101 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
102 .ARM64_RELOC_POINTER_TO_GOT,
103 => true,
104 else => false,
105 },
106 .x86_64 => break :got switch (@intToEnum(macho.reloc_type_x86_64, self.@"type")) {
107 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => true,
108 else => false,
109 },
110 else => unreachable,
111 }
112 };
113
114 if (is_via_got) {
115 return macho_file.getGotAtomForSymbol(self.target).?; // panic means fatal error
116 }
117 if (macho_file.getStubsAtomForSymbol(self.target)) |stubs_atom| return stubs_atom;
118 if (macho_file.getTlvPtrAtomForSymbol(self.target)) |tlv_ptr_atom| return tlv_ptr_atom;
119 return macho_file.getAtomForSymbol(self.target);
120 }
121};
122
12357pub const empty = Atom{
12458 .sym_index = 0,
12559 .file = null,
......@@ -130,24 +64,6 @@ pub const empty = Atom{
13064 .dbg_info_atom = undefined,
13165};
13266
133pub fn deinit(self: *Atom, allocator: Allocator) void {
134 self.lazy_bindings.deinit(allocator);
135 self.bindings.deinit(allocator);
136 self.rebases.deinit(allocator);
137 self.relocs.deinit(allocator);
138 self.contained.deinit(allocator);
139 self.code.deinit(allocator);
140}
141
142pub fn clearRetainingCapacity(self: *Atom) void {
143 self.lazy_bindings.clearRetainingCapacity();
144 self.bindings.clearRetainingCapacity();
145 self.rebases.clearRetainingCapacity();
146 self.relocs.clearRetainingCapacity();
147 self.contained.clearRetainingCapacity();
148 self.code.clearRetainingCapacity();
149}
150
15167/// Returns symbol referencing this atom.
15268pub fn getSymbol(self: Atom, macho_file: *MachO) macho.nlist_64 {
15369 return self.getSymbolPtr(macho_file).*;
......@@ -165,17 +81,6 @@ pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
16581 return .{ .sym_index = self.sym_index, .file = self.file };
16682}
16783
168/// Returns true if the symbol pointed at with `sym_loc` is contained within this atom.
169/// WARNING this function assumes all atoms have been allocated in the virtual memory.
170/// Calling it without allocating with `MachO.allocateSymbols` (or equivalent) will
171/// give bogus results.
172pub fn isSymbolContained(self: Atom, sym_loc: SymbolWithLoc, macho_file: *MachO) bool {
173 const sym = macho_file.getSymbol(sym_loc);
174 if (!sym.sect()) return false;
175 const self_sym = self.getSymbol(macho_file);
176 return sym.n_value >= self_sym.n_value and sym.n_value < self_sym.n_value + self.size;
177}
178
17984/// Returns the name of this atom.
18085pub fn getName(self: Atom, macho_file: *MachO) []const u8 {
18186 return macho_file.getSymbolName(.{
......@@ -211,690 +116,7 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
211116 return surplus >= MachO.min_text_capacity;
212117}
213118
214const RelocContext = struct {
215 macho_file: *MachO,
216 base_addr: u64 = 0,
217 base_offset: i32 = 0,
218};
219
220pub fn parseRelocs(self: *Atom, relocs: []align(1) const macho.relocation_info, context: RelocContext) !void {
221 const tracy = trace(@src());
222 defer tracy.end();
223
224 const gpa = context.macho_file.base.allocator;
225
226 const arch = context.macho_file.base.options.target.cpu.arch;
227 var addend: i64 = 0;
228 var subtractor: ?SymbolWithLoc = null;
229
230 for (relocs) |rel, i| {
231 blk: {
232 switch (arch) {
233 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
234 .ARM64_RELOC_ADDEND => {
235 assert(addend == 0);
236 addend = rel.r_symbolnum;
237 // Verify that it's followed by ARM64_RELOC_PAGE21 or ARM64_RELOC_PAGEOFF12.
238 if (relocs.len <= i + 1) {
239 log.err("no relocation after ARM64_RELOC_ADDEND", .{});
240 return error.UnexpectedRelocationType;
241 }
242 const next = @intToEnum(macho.reloc_type_arm64, relocs[i + 1].r_type);
243 switch (next) {
244 .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {},
245 else => {
246 log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});
247 log.err(" expected ARM64_RELOC_PAGE21 or ARM64_RELOC_PAGEOFF12", .{});
248 log.err(" found {s}", .{@tagName(next)});
249 return error.UnexpectedRelocationType;
250 },
251 }
252 continue;
253 },
254 .ARM64_RELOC_SUBTRACTOR => {},
255 else => break :blk,
256 },
257 .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
258 .X86_64_RELOC_SUBTRACTOR => {},
259 else => break :blk,
260 },
261 else => unreachable,
262 }
263
264 assert(subtractor == null);
265 const sym_loc = MachO.SymbolWithLoc{
266 .sym_index = rel.r_symbolnum,
267 .file = self.file,
268 };
269 const sym = context.macho_file.getSymbol(sym_loc);
270 if (sym.sect() and !sym.ext()) {
271 subtractor = sym_loc;
272 } else {
273 const sym_name = context.macho_file.getSymbolName(sym_loc);
274 subtractor = context.macho_file.getGlobal(sym_name).?;
275 }
276 // Verify that *_SUBTRACTOR is followed by *_UNSIGNED.
277 if (relocs.len <= i + 1) {
278 log.err("no relocation after *_RELOC_SUBTRACTOR", .{});
279 return error.UnexpectedRelocationType;
280 }
281 switch (arch) {
282 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, relocs[i + 1].r_type)) {
283 .ARM64_RELOC_UNSIGNED => {},
284 else => {
285 log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});
286 log.err(" expected ARM64_RELOC_UNSIGNED", .{});
287 log.err(" found {s}", .{
288 @tagName(@intToEnum(macho.reloc_type_arm64, relocs[i + 1].r_type)),
289 });
290 return error.UnexpectedRelocationType;
291 },
292 },
293 .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, relocs[i + 1].r_type)) {
294 .X86_64_RELOC_UNSIGNED => {},
295 else => {
296 log.err("unexpected relocation type after X86_64_RELOC_ADDEND", .{});
297 log.err(" expected X86_64_RELOC_UNSIGNED", .{});
298 log.err(" found {s}", .{
299 @tagName(@intToEnum(macho.reloc_type_x86_64, relocs[i + 1].r_type)),
300 });
301 return error.UnexpectedRelocationType;
302 },
303 },
304 else => unreachable,
305 }
306 continue;
307 }
308
309 const object = &context.macho_file.objects.items[self.file.?];
310 const target = target: {
311 if (rel.r_extern == 0) {
312 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
313 const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {
314 const sect = object.getSourceSection(sect_id);
315 const out_sect_id = (try context.macho_file.getOutputSection(sect)) orelse
316 unreachable;
317 const sym_index = @intCast(u32, object.symtab.items.len);
318 try object.symtab.append(gpa, .{
319 .n_strx = 0,
320 .n_type = macho.N_SECT,
321 .n_sect = out_sect_id + 1,
322 .n_desc = 0,
323 .n_value = sect.addr,
324 });
325 try object.sections_as_symbols.putNoClobber(gpa, sect_id, sym_index);
326 break :blk sym_index;
327 };
328 break :target MachO.SymbolWithLoc{ .sym_index = sym_index, .file = self.file };
329 }
330
331 const sym_loc = MachO.SymbolWithLoc{
332 .sym_index = rel.r_symbolnum,
333 .file = self.file,
334 };
335 const sym = context.macho_file.getSymbol(sym_loc);
336
337 if (sym.sect() and !sym.ext()) {
338 break :target sym_loc;
339 } else {
340 const sym_name = context.macho_file.getSymbolName(sym_loc);
341 break :target context.macho_file.getGlobal(sym_name).?;
342 }
343 };
344 const offset = @intCast(u32, rel.r_address - context.base_offset);
345
346 switch (arch) {
347 .aarch64 => {
348 switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
349 .ARM64_RELOC_BRANCH26 => {
350 // TODO rewrite relocation
351 try addStub(target, context);
352 },
353 .ARM64_RELOC_GOT_LOAD_PAGE21,
354 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
355 .ARM64_RELOC_POINTER_TO_GOT,
356 => {
357 // TODO rewrite relocation
358 try addGotEntry(target, context);
359 },
360 .ARM64_RELOC_UNSIGNED => {
361 addend = if (rel.r_length == 3)
362 mem.readIntLittle(i64, self.code.items[offset..][0..8])
363 else
364 mem.readIntLittle(i32, self.code.items[offset..][0..4]);
365 if (rel.r_extern == 0) {
366 const target_sect_base_addr = object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
367 addend -= @intCast(i64, target_sect_base_addr);
368 }
369 try self.addPtrBindingOrRebase(rel, target, context);
370 },
371 .ARM64_RELOC_TLVP_LOAD_PAGE21,
372 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
373 => {
374 try addTlvPtrEntry(target, context);
375 },
376 else => {},
377 }
378 },
379 .x86_64 => {
380 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
381 switch (rel_type) {
382 .X86_64_RELOC_BRANCH => {
383 // TODO rewrite relocation
384 try addStub(target, context);
385 addend = mem.readIntLittle(i32, self.code.items[offset..][0..4]);
386 },
387 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => {
388 // TODO rewrite relocation
389 try addGotEntry(target, context);
390 addend = mem.readIntLittle(i32, self.code.items[offset..][0..4]);
391 },
392 .X86_64_RELOC_UNSIGNED => {
393 addend = if (rel.r_length == 3)
394 mem.readIntLittle(i64, self.code.items[offset..][0..8])
395 else
396 mem.readIntLittle(i32, self.code.items[offset..][0..4]);
397 if (rel.r_extern == 0) {
398 const target_sect_base_addr = object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
399 addend -= @intCast(i64, target_sect_base_addr);
400 }
401 try self.addPtrBindingOrRebase(rel, target, context);
402 },
403 .X86_64_RELOC_SIGNED,
404 .X86_64_RELOC_SIGNED_1,
405 .X86_64_RELOC_SIGNED_2,
406 .X86_64_RELOC_SIGNED_4,
407 => {
408 const correction: u3 = switch (rel_type) {
409 .X86_64_RELOC_SIGNED => 0,
410 .X86_64_RELOC_SIGNED_1 => 1,
411 .X86_64_RELOC_SIGNED_2 => 2,
412 .X86_64_RELOC_SIGNED_4 => 4,
413 else => unreachable,
414 };
415 addend = mem.readIntLittle(i32, self.code.items[offset..][0..4]) + correction;
416 if (rel.r_extern == 0) {
417 // Note for the future self: when r_extern == 0, we should subtract correction from the
418 // addend.
419 const target_sect_base_addr = object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
420 // We need to add base_offset, i.e., offset of this atom wrt to the source
421 // section. Otherwise, the addend will over-/under-shoot.
422 addend += @intCast(i64, context.base_addr + offset + 4) -
423 @intCast(i64, target_sect_base_addr) + context.base_offset;
424 }
425 },
426 .X86_64_RELOC_TLV => {
427 try addTlvPtrEntry(target, context);
428 },
429 else => {},
430 }
431 },
432 else => unreachable,
433 }
434
435 try self.relocs.append(gpa, .{
436 .offset = offset,
437 .target = target,
438 .addend = addend,
439 .subtractor = subtractor,
440 .pcrel = rel.r_pcrel == 1,
441 .length = rel.r_length,
442 .@"type" = rel.r_type,
443 });
444
445 addend = 0;
446 subtractor = null;
447 }
448}
449
450fn addPtrBindingOrRebase(
451 self: *Atom,
452 rel: macho.relocation_info,
453 target: MachO.SymbolWithLoc,
454 context: RelocContext,
455) !void {
456 const gpa = context.macho_file.base.allocator;
457 const sym = context.macho_file.getSymbol(target);
458 if (sym.undf()) {
459 try self.bindings.append(gpa, .{
460 .target = target,
461 .offset = @intCast(u32, rel.r_address - context.base_offset),
462 });
463 } else {
464 const source_sym = self.getSymbol(context.macho_file);
465 const section = context.macho_file.sections.get(source_sym.n_sect - 1);
466 const header = section.header;
467 const segment_index = section.segment_index;
468 const sect_type = header.@"type"();
469
470 const should_rebase = rebase: {
471 if (rel.r_length != 3) break :rebase false;
472
473 // TODO actually, a check similar to what dyld is doing, that is, verifying
474 // that the segment is writable should be enough here.
475 const is_right_segment = blk: {
476 if (context.macho_file.data_segment_cmd_index) |idx| {
477 if (segment_index == idx) {
478 break :blk true;
479 }
480 }
481 if (context.macho_file.data_const_segment_cmd_index) |idx| {
482 if (segment_index == idx) {
483 break :blk true;
484 }
485 }
486 break :blk false;
487 };
488
489 if (!is_right_segment) break :rebase false;
490 if (sect_type != macho.S_LITERAL_POINTERS and
491 sect_type != macho.S_REGULAR and
492 sect_type != macho.S_MOD_INIT_FUNC_POINTERS and
493 sect_type != macho.S_MOD_TERM_FUNC_POINTERS)
494 {
495 break :rebase false;
496 }
497
498 break :rebase true;
499 };
500
501 if (should_rebase) {
502 try self.rebases.append(gpa, @intCast(u32, rel.r_address - context.base_offset));
503 }
504 }
505}
506
507fn addTlvPtrEntry(target: MachO.SymbolWithLoc, context: RelocContext) !void {
508 const target_sym = context.macho_file.getSymbol(target);
509 if (!target_sym.undf()) return;
510 if (context.macho_file.tlv_ptr_entries_table.contains(target)) return;
511
512 const index = try context.macho_file.allocateTlvPtrEntry(target);
513 const atom = try context.macho_file.createTlvPtrAtom(target);
514 context.macho_file.tlv_ptr_entries.items[index].sym_index = atom.sym_index;
515}
516
517fn addGotEntry(target: MachO.SymbolWithLoc, context: RelocContext) !void {
518 if (context.macho_file.got_entries_table.contains(target)) return;
519
520 const index = try context.macho_file.allocateGotEntry(target);
521 const atom = try context.macho_file.createGotAtom(target);
522 context.macho_file.got_entries.items[index].sym_index = atom.sym_index;
523}
524
525fn addStub(target: MachO.SymbolWithLoc, context: RelocContext) !void {
526 const target_sym = context.macho_file.getSymbol(target);
527 if (!target_sym.undf()) return;
528 if (context.macho_file.stubs_table.contains(target)) return;
529
530 const stub_index = try context.macho_file.allocateStubEntry(target);
531
532 const stub_helper_atom = try context.macho_file.createStubHelperAtom();
533 const laptr_atom = try context.macho_file.createLazyPointerAtom(stub_helper_atom.sym_index, target);
534 const stub_atom = try context.macho_file.createStubAtom(laptr_atom.sym_index);
535
536 context.macho_file.stubs.items[stub_index].sym_index = stub_atom.sym_index;
537}
538
539pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
540 const tracy = trace(@src());
541 defer tracy.end();
542
543 log.debug("ATOM(%{d}, '{s}')", .{ self.sym_index, self.getName(macho_file) });
544
545 for (self.relocs.items) |rel| {
546 const arch = macho_file.base.options.target.cpu.arch;
547 switch (arch) {
548 .aarch64 => {
549 log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{
550 @tagName(@intToEnum(macho.reloc_type_arm64, rel.@"type")),
551 rel.offset,
552 rel.target.sym_index,
553 rel.target.file,
554 });
555 },
556 .x86_64 => {
557 log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{
558 @tagName(@intToEnum(macho.reloc_type_x86_64, rel.@"type")),
559 rel.offset,
560 rel.target.sym_index,
561 rel.target.file,
562 });
563 },
564 else => unreachable,
565 }
566
567 const source_addr = blk: {
568 const source_sym = self.getSymbol(macho_file);
569 break :blk source_sym.n_value + rel.offset;
570 };
571 const is_tlv = is_tlv: {
572 const source_sym = self.getSymbol(macho_file);
573 const header = macho_file.sections.items(.header)[source_sym.n_sect - 1];
574 break :is_tlv header.@"type"() == macho.S_THREAD_LOCAL_VARIABLES;
575 };
576 const target_addr = blk: {
577 const target_atom = rel.getTargetAtom(macho_file) orelse {
578 // If there is no atom for target, we still need to check for special, atom-less
579 // symbols such as `___dso_handle`.
580 const target_name = macho_file.getSymbolName(rel.target);
581 assert(macho_file.getGlobal(target_name) != null);
582 const atomless_sym = macho_file.getSymbol(rel.target);
583 log.debug(" | atomless target '{s}'", .{target_name});
584 break :blk atomless_sym.n_value;
585 };
586 log.debug(" | target ATOM(%{d}, '{s}') in object({?d})", .{
587 target_atom.sym_index,
588 target_atom.getName(macho_file),
589 target_atom.file,
590 });
591 // If `rel.target` is contained within the target atom, pull its address value.
592 const target_sym = if (target_atom.isSymbolContained(rel.target, macho_file))
593 macho_file.getSymbol(rel.target)
594 else
595 target_atom.getSymbol(macho_file);
596 assert(target_sym.n_desc != MachO.N_DESC_GCED);
597 const base_address: u64 = if (is_tlv) base_address: {
598 // For TLV relocations, the value specified as a relocation is the displacement from the
599 // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
600 // defined TLV template init section in the following order:
601 // * wrt to __thread_data if defined, then
602 // * wrt to __thread_bss
603 const sect_id: u16 = sect_id: {
604 if (macho_file.getSectionByName("__DATA", "__thread_data")) |i| {
605 break :sect_id i;
606 } else if (macho_file.getSectionByName("__DATA", "__thread_bss")) |i| {
607 break :sect_id i;
608 } else {
609 log.err("threadlocal variables present but no initializer sections found", .{});
610 log.err(" __thread_data not found", .{});
611 log.err(" __thread_bss not found", .{});
612 return error.FailedToResolveRelocationTarget;
613 }
614 };
615 break :base_address macho_file.sections.items(.header)[sect_id].addr;
616 } else 0;
617 break :blk target_sym.n_value - base_address;
618 };
619
620 log.debug(" | source_addr = 0x{x}", .{source_addr});
621
622 switch (arch) {
623 .aarch64 => {
624 switch (@intToEnum(macho.reloc_type_arm64, rel.@"type")) {
625 .ARM64_RELOC_BRANCH26 => {
626 log.debug(" | target_addr = 0x{x}", .{target_addr});
627 const displacement = math.cast(
628 i28,
629 @intCast(i64, target_addr) - @intCast(i64, source_addr),
630 ) orelse {
631 log.err("jump too big to encode as i28 displacement value", .{});
632 log.err(" (target - source) = displacement => 0x{x} - 0x{x} = 0x{x}", .{
633 target_addr,
634 source_addr,
635 @intCast(i64, target_addr) - @intCast(i64, source_addr),
636 });
637 log.err(" TODO implement branch islands to extend jump distance for arm64", .{});
638 return error.TODOImplementBranchIslands;
639 };
640 const code = self.code.items[rel.offset..][0..4];
641 var inst = aarch64.Instruction{
642 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
643 aarch64.Instruction,
644 aarch64.Instruction.unconditional_branch_immediate,
645 ), code),
646 };
647 inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));
648 mem.writeIntLittle(u32, code, inst.toU32());
649 },
650 .ARM64_RELOC_PAGE21,
651 .ARM64_RELOC_GOT_LOAD_PAGE21,
652 .ARM64_RELOC_TLVP_LOAD_PAGE21,
653 => {
654 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
655 log.debug(" | target_addr = 0x{x}", .{actual_target_addr});
656 const source_page = @intCast(i32, source_addr >> 12);
657 const target_page = @intCast(i32, actual_target_addr >> 12);
658 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
659 const code = self.code.items[rel.offset..][0..4];
660 var inst = aarch64.Instruction{
661 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
662 aarch64.Instruction,
663 aarch64.Instruction.pc_relative_address,
664 ), code),
665 };
666 inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
667 inst.pc_relative_address.immlo = @truncate(u2, pages);
668 mem.writeIntLittle(u32, code, inst.toU32());
669 },
670 .ARM64_RELOC_PAGEOFF12 => {
671 const code = self.code.items[rel.offset..][0..4];
672 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
673 log.debug(" | target_addr = 0x{x}", .{actual_target_addr});
674 const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));
675 if (isArithmeticOp(self.code.items[rel.offset..][0..4])) {
676 var inst = aarch64.Instruction{
677 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
678 aarch64.Instruction,
679 aarch64.Instruction.add_subtract_immediate,
680 ), code),
681 };
682 inst.add_subtract_immediate.imm12 = narrowed;
683 mem.writeIntLittle(u32, code, inst.toU32());
684 } else {
685 var inst = aarch64.Instruction{
686 .load_store_register = mem.bytesToValue(meta.TagPayload(
687 aarch64.Instruction,
688 aarch64.Instruction.load_store_register,
689 ), code),
690 };
691 const offset: u12 = blk: {
692 if (inst.load_store_register.size == 0) {
693 if (inst.load_store_register.v == 1) {
694 // 128-bit SIMD is scaled by 16.
695 break :blk try math.divExact(u12, narrowed, 16);
696 }
697 // Otherwise, 8-bit SIMD or ldrb.
698 break :blk narrowed;
699 } else {
700 const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size);
701 break :blk try math.divExact(u12, narrowed, denom);
702 }
703 };
704 inst.load_store_register.offset = offset;
705 mem.writeIntLittle(u32, code, inst.toU32());
706 }
707 },
708 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {
709 const code = self.code.items[rel.offset..][0..4];
710 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
711 log.debug(" | target_addr = 0x{x}", .{actual_target_addr});
712 const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));
713 var inst: aarch64.Instruction = .{
714 .load_store_register = mem.bytesToValue(meta.TagPayload(
715 aarch64.Instruction,
716 aarch64.Instruction.load_store_register,
717 ), code),
718 };
719 const offset = try math.divExact(u12, narrowed, 8);
720 inst.load_store_register.offset = offset;
721 mem.writeIntLittle(u32, code, inst.toU32());
722 },
723 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
724 const code = self.code.items[rel.offset..][0..4];
725 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
726 log.debug(" | target_addr = 0x{x}", .{actual_target_addr});
727
728 const RegInfo = struct {
729 rd: u5,
730 rn: u5,
731 size: u2,
732 };
733 const reg_info: RegInfo = blk: {
734 if (isArithmeticOp(code)) {
735 const inst = mem.bytesToValue(meta.TagPayload(
736 aarch64.Instruction,
737 aarch64.Instruction.add_subtract_immediate,
738 ), code);
739 break :blk .{
740 .rd = inst.rd,
741 .rn = inst.rn,
742 .size = inst.sf,
743 };
744 } else {
745 const inst = mem.bytesToValue(meta.TagPayload(
746 aarch64.Instruction,
747 aarch64.Instruction.load_store_register,
748 ), code);
749 break :blk .{
750 .rd = inst.rt,
751 .rn = inst.rn,
752 .size = inst.size,
753 };
754 }
755 };
756 const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));
757 var inst = if (macho_file.tlv_ptr_entries_table.contains(rel.target)) blk: {
758 const offset = try math.divExact(u12, narrowed, 8);
759 break :blk aarch64.Instruction{
760 .load_store_register = .{
761 .rt = reg_info.rd,
762 .rn = reg_info.rn,
763 .offset = offset,
764 .opc = 0b01,
765 .op1 = 0b01,
766 .v = 0,
767 .size = reg_info.size,
768 },
769 };
770 } else aarch64.Instruction{
771 .add_subtract_immediate = .{
772 .rd = reg_info.rd,
773 .rn = reg_info.rn,
774 .imm12 = narrowed,
775 .sh = 0,
776 .s = 0,
777 .op = 0,
778 .sf = @truncate(u1, reg_info.size),
779 },
780 };
781 mem.writeIntLittle(u32, code, inst.toU32());
782 },
783 .ARM64_RELOC_POINTER_TO_GOT => {
784 log.debug(" | target_addr = 0x{x}", .{target_addr});
785 const result = math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr)) orelse return error.Overflow;
786 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, result));
787 },
788 .ARM64_RELOC_UNSIGNED => {
789 const result = blk: {
790 if (rel.subtractor) |subtractor| {
791 const sym = macho_file.getSymbol(subtractor);
792 break :blk @intCast(i64, target_addr) - @intCast(i64, sym.n_value) + rel.addend;
793 } else {
794 break :blk @intCast(i64, target_addr) + rel.addend;
795 }
796 };
797 log.debug(" | target_addr = 0x{x}", .{result});
798
799 if (rel.length == 3) {
800 mem.writeIntLittle(u64, self.code.items[rel.offset..][0..8], @bitCast(u64, result));
801 } else {
802 mem.writeIntLittle(
803 u32,
804 self.code.items[rel.offset..][0..4],
805 @truncate(u32, @bitCast(u64, result)),
806 );
807 }
808 },
809 .ARM64_RELOC_SUBTRACTOR => unreachable,
810 .ARM64_RELOC_ADDEND => unreachable,
811 }
812 },
813 .x86_64 => {
814 switch (@intToEnum(macho.reloc_type_x86_64, rel.@"type")) {
815 .X86_64_RELOC_BRANCH => {
816 log.debug(" | target_addr = 0x{x}", .{target_addr});
817 const displacement = math.cast(
818 i32,
819 @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend,
820 ) orelse return error.Overflow;
821 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));
822 },
823 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => {
824 log.debug(" | target_addr = 0x{x}", .{target_addr});
825 const displacement = math.cast(
826 i32,
827 @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend,
828 ) orelse return error.Overflow;
829 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));
830 },
831 .X86_64_RELOC_TLV => {
832 log.debug(" | target_addr = 0x{x}", .{target_addr});
833 if (!macho_file.tlv_ptr_entries_table.contains(rel.target)) {
834 // We need to rewrite the opcode from movq to leaq.
835 self.code.items[rel.offset - 2] = 0x8d;
836 }
837 const displacement = math.cast(
838 i32,
839 @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend,
840 ) orelse return error.Overflow;
841 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));
842 },
843 .X86_64_RELOC_SIGNED,
844 .X86_64_RELOC_SIGNED_1,
845 .X86_64_RELOC_SIGNED_2,
846 .X86_64_RELOC_SIGNED_4,
847 => {
848 const correction: u3 = switch (@intToEnum(macho.reloc_type_x86_64, rel.@"type")) {
849 .X86_64_RELOC_SIGNED => 0,
850 .X86_64_RELOC_SIGNED_1 => 1,
851 .X86_64_RELOC_SIGNED_2 => 2,
852 .X86_64_RELOC_SIGNED_4 => 4,
853 else => unreachable,
854 };
855 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
856 log.debug(" | target_addr = 0x{x}", .{actual_target_addr});
857 const displacement = math.cast(
858 i32,
859 actual_target_addr - @intCast(i64, source_addr + correction + 4),
860 ) orelse return error.Overflow;
861 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));
862 },
863 .X86_64_RELOC_UNSIGNED => {
864 const result = blk: {
865 if (rel.subtractor) |subtractor| {
866 const sym = macho_file.getSymbol(subtractor);
867 break :blk @intCast(i64, target_addr) - @intCast(i64, sym.n_value) + rel.addend;
868 } else {
869 break :blk @intCast(i64, target_addr) + rel.addend;
870 }
871 };
872 log.debug(" | target_addr = 0x{x}", .{result});
873
874 if (rel.length == 3) {
875 mem.writeIntLittle(u64, self.code.items[rel.offset..][0..8], @bitCast(u64, result));
876 } else {
877 mem.writeIntLittle(
878 u32,
879 self.code.items[rel.offset..][0..4],
880 @truncate(u32, @bitCast(u64, result)),
881 );
882 }
883 },
884 .X86_64_RELOC_SUBTRACTOR => unreachable,
885 }
886 },
887 else => unreachable,
888 }
889 }
890}
891
892inline fn isArithmeticOp(inst: *const [4]u8) bool {
893 const group_decode = @truncate(u5, inst[3]);
894 return ((group_decode >> 2) == 4);
895}
896
897pub fn addRelocation(self: *Atom, macho_file: *MachO, reloc: RelocationIncr) !void {
119pub fn addRelocation(self: *Atom, macho_file: *MachO, reloc: Relocation) !void {
898120 return self.addRelocations(macho_file, 1, .{reloc});
899121}
900122
......@@ -902,7 +124,7 @@ pub fn addRelocations(
902124 self: *Atom,
903125 macho_file: *MachO,
904126 comptime count: comptime_int,
905 relocs: [count]RelocationIncr,
127 relocs: [count]Relocation,
906128) !void {
907129 const gpa = macho_file.base.allocator;
908130 const target = macho_file.base.options.target;
src/link/MachO/DwarfInfo.zig created+461
......@@ -0,0 +1,461 @@
1const DwarfInfo = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const dwarf = std.dwarf;
6const leb = std.leb;
7const log = std.log.scoped(.macho);
8const mem = std.mem;
9
10const Allocator = mem.Allocator;
11pub const AbbrevLookupTable = std.AutoHashMap(u64, struct { pos: usize, len: usize });
12pub const SubprogramLookupByName = std.StringHashMap(struct { addr: u64, size: u64 });
13
14debug_info: []const u8,
15debug_abbrev: []const u8,
16debug_str: []const u8,
17
18pub fn getCompileUnitIterator(self: DwarfInfo) CompileUnitIterator {
19 return .{ .ctx = self };
20}
21
22const CompileUnitIterator = struct {
23 ctx: DwarfInfo,
24 pos: usize = 0,
25
26 pub fn next(self: *CompileUnitIterator) !?CompileUnit {
27 if (self.pos >= self.ctx.debug_info.len) return null;
28
29 var stream = std.io.fixedBufferStream(self.ctx.debug_info);
30 var creader = std.io.countingReader(stream.reader());
31 const reader = creader.reader();
32
33 const cuh = try CompileUnit.Header.read(reader);
34 const total_length = cuh.length + @as(u64, if (cuh.is_64bit) @sizeOf(u64) else @sizeOf(u32));
35
36 const cu = CompileUnit{
37 .cuh = cuh,
38 .debug_info_off = creader.bytes_read,
39 };
40
41 self.pos += total_length;
42
43 return cu;
44 }
45};
46
47pub fn genSubprogramLookupByName(
48 self: DwarfInfo,
49 compile_unit: CompileUnit,
50 abbrev_lookup: AbbrevLookupTable,
51 lookup: *SubprogramLookupByName,
52) !void {
53 var abbrev_it = compile_unit.getAbbrevEntryIterator(self);
54 while (try abbrev_it.next(abbrev_lookup)) |entry| switch (entry.tag) {
55 dwarf.TAG.subprogram => {
56 var attr_it = entry.getAttributeIterator(self, compile_unit.cuh);
57
58 var name: ?[]const u8 = null;
59 var low_pc: ?u64 = null;
60 var high_pc: ?u64 = null;
61
62 while (try attr_it.next()) |attr| switch (attr.name) {
63 dwarf.AT.name => if (attr.getString(self, compile_unit.cuh)) |str| {
64 log.warn("subprogram: {s}", .{str});
65 name = str;
66 },
67 dwarf.AT.low_pc => {
68 if (attr.getAddr(self, compile_unit.cuh)) |addr| {
69 low_pc = addr;
70 }
71 if (try attr.getConstant(self)) |constant| {
72 low_pc = @intCast(u64, constant);
73 }
74 },
75 dwarf.AT.high_pc => {
76 if (attr.getAddr(self, compile_unit.cuh)) |addr| {
77 high_pc = addr;
78 }
79 if (try attr.getConstant(self)) |constant| {
80 high_pc = @intCast(u64, constant);
81 }
82 },
83 else => {},
84 };
85
86 if (name == null or low_pc == null or high_pc == null) continue;
87
88 try lookup.putNoClobber(name.?, .{ .addr = low_pc.?, .size = high_pc.? });
89 },
90 else => {},
91 };
92}
93
94pub fn genAbbrevLookupByKind(self: DwarfInfo, off: usize, lookup: *AbbrevLookupTable) !void {
95 const data = self.debug_abbrev[off..];
96 var stream = std.io.fixedBufferStream(data);
97 var creader = std.io.countingReader(stream.reader());
98 const reader = creader.reader();
99
100 while (true) {
101 const kind = try leb.readULEB128(u64, reader);
102
103 if (kind == 0) break;
104
105 const pos = creader.bytes_read;
106 _ = try leb.readULEB128(u64, reader); // TAG
107 _ = try reader.readByte(); // CHILDREN
108
109 while (true) {
110 const name = try leb.readULEB128(u64, reader);
111 const form = try leb.readULEB128(u64, reader);
112
113 if (name == 0 and form == 0) break;
114 }
115
116 try lookup.putNoClobber(kind, .{
117 .pos = pos,
118 .len = creader.bytes_read - pos - 2,
119 });
120 }
121}
122
123pub const CompileUnit = struct {
124 cuh: Header,
125 debug_info_off: usize,
126
127 pub const Header = struct {
128 is_64bit: bool,
129 length: u64,
130 version: u16,
131 debug_abbrev_offset: u64,
132 address_size: u8,
133
134 fn read(reader: anytype) !Header {
135 var length: u64 = try reader.readIntLittle(u32);
136
137 const is_64bit = length == 0xffffffff;
138 if (is_64bit) {
139 length = try reader.readIntLittle(u64);
140 }
141
142 const version = try reader.readIntLittle(u16);
143 const debug_abbrev_offset = if (is_64bit)
144 try reader.readIntLittle(u64)
145 else
146 try reader.readIntLittle(u32);
147 const address_size = try reader.readIntLittle(u8);
148
149 return Header{
150 .is_64bit = is_64bit,
151 .length = length,
152 .version = version,
153 .debug_abbrev_offset = debug_abbrev_offset,
154 .address_size = address_size,
155 };
156 }
157 };
158
159 inline fn getDebugInfo(self: CompileUnit, ctx: DwarfInfo) []const u8 {
160 return ctx.debug_info[self.debug_info_off..][0..self.cuh.length];
161 }
162
163 pub fn getAbbrevEntryIterator(self: CompileUnit, ctx: DwarfInfo) AbbrevEntryIterator {
164 return .{ .cu = self, .ctx = ctx };
165 }
166};
167
168const AbbrevEntryIterator = struct {
169 cu: CompileUnit,
170 ctx: DwarfInfo,
171 pos: usize = 0,
172
173 pub fn next(self: *AbbrevEntryIterator, lookup: AbbrevLookupTable) !?AbbrevEntry {
174 if (self.pos + self.cu.debug_info_off >= self.ctx.debug_info.len) return null;
175
176 const debug_info = self.ctx.debug_info[self.pos + self.cu.debug_info_off ..];
177 var stream = std.io.fixedBufferStream(debug_info);
178 var creader = std.io.countingReader(stream.reader());
179 const reader = creader.reader();
180
181 const kind = try leb.readULEB128(u64, reader);
182 self.pos += creader.bytes_read;
183
184 if (kind == 0) {
185 return AbbrevEntry.@"null"();
186 }
187
188 const abbrev_pos = lookup.get(kind) orelse return error.MalformedDwarf;
189 const len = try findAbbrevEntrySize(
190 self.ctx,
191 abbrev_pos.pos,
192 abbrev_pos.len,
193 self.pos + self.cu.debug_info_off,
194 self.cu.cuh,
195 );
196 const entry = try getAbbrevEntry(
197 self.ctx,
198 abbrev_pos.pos,
199 abbrev_pos.len,
200 self.pos + self.cu.debug_info_off,
201 len,
202 );
203
204 self.pos += len;
205
206 return entry;
207 }
208};
209
210pub const AbbrevEntry = struct {
211 tag: u64,
212 children: u8,
213 debug_abbrev_off: usize,
214 debug_abbrev_len: usize,
215 debug_info_off: usize,
216 debug_info_len: usize,
217
218 fn @"null"() AbbrevEntry {
219 return .{
220 .tag = 0,
221 .children = dwarf.CHILDREN.no,
222 .debug_abbrev_off = 0,
223 .debug_abbrev_len = 0,
224 .debug_info_off = 0,
225 .debug_info_len = 0,
226 };
227 }
228
229 pub fn hasChildren(self: AbbrevEntry) bool {
230 return self.children == dwarf.CHILDREN.yes;
231 }
232
233 inline fn getDebugInfo(self: AbbrevEntry, ctx: DwarfInfo) []const u8 {
234 return ctx.debug_info[self.debug_info_off..][0..self.debug_info_len];
235 }
236
237 inline fn getDebugAbbrev(self: AbbrevEntry, ctx: DwarfInfo) []const u8 {
238 return ctx.debug_abbrev[self.debug_abbrev_off..][0..self.debug_abbrev_len];
239 }
240
241 pub fn getAttributeIterator(self: AbbrevEntry, ctx: DwarfInfo, cuh: CompileUnit.Header) AttributeIterator {
242 return .{ .entry = self, .ctx = ctx, .cuh = cuh };
243 }
244};
245
246pub const Attribute = struct {
247 name: u64,
248 form: u64,
249 debug_info_off: usize,
250 debug_info_len: usize,
251
252 inline fn getDebugInfo(self: Attribute, ctx: DwarfInfo) []const u8 {
253 return ctx.debug_info[self.debug_info_off..][0..self.debug_info_len];
254 }
255
256 pub fn getString(self: Attribute, ctx: DwarfInfo, cuh: CompileUnit.Header) ?[]const u8 {
257 if (self.form != dwarf.FORM.strp) return null;
258 const debug_info = self.getDebugInfo(ctx);
259 const off = if (cuh.is_64bit)
260 mem.readIntLittle(u64, debug_info[0..8])
261 else
262 mem.readIntLittle(u32, debug_info[0..4]);
263 return ctx.getString(off);
264 }
265
266 pub fn getConstant(self: Attribute, ctx: DwarfInfo) !?i128 {
267 const debug_info = self.getDebugInfo(ctx);
268 var stream = std.io.fixedBufferStream(debug_info);
269 const reader = stream.reader();
270
271 return switch (self.form) {
272 dwarf.FORM.data1 => debug_info[0],
273 dwarf.FORM.data2 => mem.readIntLittle(u16, debug_info[0..2]),
274 dwarf.FORM.data4 => mem.readIntLittle(u32, debug_info[0..4]),
275 dwarf.FORM.data8 => mem.readIntLittle(u64, debug_info[0..8]),
276 dwarf.FORM.udata => try leb.readULEB128(u64, reader),
277 dwarf.FORM.sdata => try leb.readILEB128(i64, reader),
278 else => null,
279 };
280 }
281
282 pub fn getReference(self: Attribute, ctx: DwarfInfo) !?u64 {
283 const debug_info = self.getDebugInfo(ctx);
284 var stream = std.io.fixedBufferStream(debug_info);
285 const reader = stream.reader();
286
287 return switch (self.form) {
288 dwarf.FORM.ref1 => debug_info[0],
289 dwarf.FORM.ref2 => mem.readIntLittle(u16, debug_info[0..2]),
290 dwarf.FORM.ref4 => mem.readIntLittle(u32, debug_info[0..4]),
291 dwarf.FORM.ref8 => mem.readIntLittle(u64, debug_info[0..8]),
292 dwarf.FORM.ref_udata => try leb.readULEB128(u64, reader),
293 else => null,
294 };
295 }
296
297 pub fn getAddr(self: Attribute, ctx: DwarfInfo, cuh: CompileUnit.Header) ?u64 {
298 if (self.form != dwarf.FORM.addr) return null;
299 const debug_info = self.getDebugInfo(ctx);
300 return switch (cuh.address_size) {
301 1 => debug_info[0],
302 2 => mem.readIntLittle(u16, debug_info[0..2]),
303 4 => mem.readIntLittle(u32, debug_info[0..4]),
304 8 => mem.readIntLittle(u64, debug_info[0..8]),
305 else => unreachable,
306 };
307 }
308};
309
310const AttributeIterator = struct {
311 entry: AbbrevEntry,
312 ctx: DwarfInfo,
313 cuh: CompileUnit.Header,
314 debug_abbrev_pos: usize = 0,
315 debug_info_pos: usize = 0,
316
317 pub fn next(self: *AttributeIterator) !?Attribute {
318 const debug_abbrev = self.entry.getDebugAbbrev(self.ctx);
319 if (self.debug_abbrev_pos >= debug_abbrev.len) return null;
320
321 var stream = std.io.fixedBufferStream(debug_abbrev[self.debug_abbrev_pos..]);
322 var creader = std.io.countingReader(stream.reader());
323 const reader = creader.reader();
324
325 const name = try leb.readULEB128(u64, reader);
326 const form = try leb.readULEB128(u64, reader);
327
328 self.debug_abbrev_pos += creader.bytes_read;
329
330 const len = try findFormSize(
331 self.ctx,
332 form,
333 self.debug_info_pos + self.entry.debug_info_off,
334 self.cuh,
335 );
336 const attr = Attribute{
337 .name = name,
338 .form = form,
339 .debug_info_off = self.debug_info_pos + self.entry.debug_info_off,
340 .debug_info_len = len,
341 };
342
343 self.debug_info_pos += len;
344
345 return attr;
346 }
347};
348
349fn getAbbrevEntry(self: DwarfInfo, da_off: usize, da_len: usize, di_off: usize, di_len: usize) !AbbrevEntry {
350 const debug_abbrev = self.debug_abbrev[da_off..][0..da_len];
351 var stream = std.io.fixedBufferStream(debug_abbrev);
352 var creader = std.io.countingReader(stream.reader());
353 const reader = creader.reader();
354
355 const tag = try leb.readULEB128(u64, reader);
356 const children = switch (tag) {
357 std.dwarf.TAG.const_type,
358 std.dwarf.TAG.packed_type,
359 std.dwarf.TAG.pointer_type,
360 std.dwarf.TAG.reference_type,
361 std.dwarf.TAG.restrict_type,
362 std.dwarf.TAG.rvalue_reference_type,
363 std.dwarf.TAG.shared_type,
364 std.dwarf.TAG.volatile_type,
365 => if (creader.bytes_read == da_len) std.dwarf.CHILDREN.no else try reader.readByte(),
366 else => try reader.readByte(),
367 };
368
369 return AbbrevEntry{
370 .tag = tag,
371 .children = children,
372 .debug_abbrev_off = creader.bytes_read + da_off,
373 .debug_abbrev_len = da_len - creader.bytes_read,
374 .debug_info_off = di_off,
375 .debug_info_len = di_len,
376 };
377}
378
379fn findFormSize(self: DwarfInfo, form: u64, di_off: usize, cuh: CompileUnit.Header) !usize {
380 const debug_info = self.debug_info[di_off..];
381 var stream = std.io.fixedBufferStream(debug_info);
382 var creader = std.io.countingReader(stream.reader());
383 const reader = creader.reader();
384
385 switch (form) {
386 dwarf.FORM.strp => return if (cuh.is_64bit) @sizeOf(u64) else @sizeOf(u32),
387 dwarf.FORM.sec_offset => return if (cuh.is_64bit) @sizeOf(u64) else @sizeOf(u32),
388 dwarf.FORM.addr => return cuh.address_size,
389 dwarf.FORM.exprloc => {
390 const expr_len = try leb.readULEB128(u64, reader);
391 var i: u64 = 0;
392 while (i < expr_len) : (i += 1) {
393 _ = try reader.readByte();
394 }
395 return creader.bytes_read;
396 },
397 dwarf.FORM.flag_present => return 0,
398
399 dwarf.FORM.data1 => return @sizeOf(u8),
400 dwarf.FORM.data2 => return @sizeOf(u16),
401 dwarf.FORM.data4 => return @sizeOf(u32),
402 dwarf.FORM.data8 => return @sizeOf(u64),
403 dwarf.FORM.udata => {
404 _ = try leb.readULEB128(u64, reader);
405 return creader.bytes_read;
406 },
407 dwarf.FORM.sdata => {
408 _ = try leb.readILEB128(i64, reader);
409 return creader.bytes_read;
410 },
411
412 dwarf.FORM.ref1 => return @sizeOf(u8),
413 dwarf.FORM.ref2 => return @sizeOf(u16),
414 dwarf.FORM.ref4 => return @sizeOf(u32),
415 dwarf.FORM.ref8 => return @sizeOf(u64),
416 dwarf.FORM.ref_udata => {
417 _ = try leb.readULEB128(u64, reader);
418 return creader.bytes_read;
419 },
420
421 else => return error.ToDo,
422 }
423}
424
425fn findAbbrevEntrySize(self: DwarfInfo, da_off: usize, da_len: usize, di_off: usize, cuh: CompileUnit.Header) !usize {
426 const debug_abbrev = self.debug_abbrev[da_off..][0..da_len];
427 var stream = std.io.fixedBufferStream(debug_abbrev);
428 var creader = std.io.countingReader(stream.reader());
429 const reader = creader.reader();
430
431 const tag = try leb.readULEB128(u64, reader);
432 switch (tag) {
433 std.dwarf.TAG.const_type,
434 std.dwarf.TAG.packed_type,
435 std.dwarf.TAG.pointer_type,
436 std.dwarf.TAG.reference_type,
437 std.dwarf.TAG.restrict_type,
438 std.dwarf.TAG.rvalue_reference_type,
439 std.dwarf.TAG.shared_type,
440 std.dwarf.TAG.volatile_type,
441 => if (creader.bytes_read != da_len) {
442 _ = try reader.readByte();
443 },
444 else => _ = try reader.readByte(),
445 }
446
447 var len: usize = 0;
448 while (creader.bytes_read < debug_abbrev.len) {
449 _ = try leb.readULEB128(u64, reader);
450 const form = try leb.readULEB128(u64, reader);
451 const form_len = try self.findFormSize(form, di_off + len, cuh);
452 len += form_len;
453 }
454
455 return len;
456}
457
458fn getString(self: DwarfInfo, off: u64) []const u8 {
459 assert(off < self.debug_str.len);
460 return mem.sliceTo(@ptrCast([*:0]const u8, self.debug_str.ptr + off), 0);
461}
src/link/MachO/Object.zig+353-404
......@@ -6,7 +6,7 @@ const assert = std.debug.assert;
66const dwarf = std.dwarf;
77const fs = std.fs;
88const io = std.io;
9const log = std.log.scoped(.link);
9const log = std.log.scoped(.macho);
1010const macho = std.macho;
1111const math = std.math;
1212const mem = std.mem;
......@@ -14,10 +14,12 @@ const sort = std.sort;
1414const trace = @import("../../tracy.zig").trace;
1515
1616const Allocator = mem.Allocator;
17const Atom = @import("Atom.zig");
17const Atom = @import("ZldAtom.zig");
18const AtomIndex = @import("zld.zig").AtomIndex;
19const DwarfInfo = @import("DwarfInfo.zig");
1820const LoadCommandIterator = macho.LoadCommandIterator;
19const MachO = @import("../MachO.zig");
20const SymbolWithLoc = MachO.SymbolWithLoc;
21const Zld = @import("zld.zig").Zld;
22const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
2123
2224name: []const u8,
2325mtime: u64,
......@@ -30,31 +32,33 @@ header: macho.mach_header_64 = undefined,
3032in_symtab: ?[]align(1) const macho.nlist_64 = null,
3133in_strtab: ?[]const u8 = null,
3234
33symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
34sections: std.ArrayListUnmanaged(macho.section_64) = .{},
35
36sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
37
38/// List of atoms that map to the symbols parsed from this object file.
39managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
40
41/// Table of atoms belonging to this object file indexed by the symbol index.
42atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
35/// Output symtab is sorted so that we can easily reference symbols following each
36/// other in address space.
37/// The length of the symtab is at least of the input symtab length however there
38/// can be trailing section symbols.
39symtab: []macho.nlist_64 = undefined,
40/// Can be undefined as set together with in_symtab.
41source_symtab_lookup: []u32 = undefined,
42/// Can be undefined as set together with in_symtab.
43strtab_lookup: []u32 = undefined,
44/// Can be undefined as set together with in_symtab.
45atom_by_index_table: []AtomIndex = undefined,
46/// Can be undefined as set together with in_symtab.
47globals_lookup: []i64 = undefined,
48
49atoms: std.ArrayListUnmanaged(AtomIndex) = .{},
4350
4451pub fn deinit(self: *Object, gpa: Allocator) void {
45 self.symtab.deinit(gpa);
46 self.sections.deinit(gpa);
47 self.sections_as_symbols.deinit(gpa);
48 self.atom_by_index_table.deinit(gpa);
49
50 for (self.managed_atoms.items) |atom| {
51 atom.deinit(gpa);
52 gpa.destroy(atom);
53 }
54 self.managed_atoms.deinit(gpa);
55
52 self.atoms.deinit(gpa);
5653 gpa.free(self.name);
5754 gpa.free(self.contents);
55 if (self.in_symtab) |_| {
56 gpa.free(self.source_symtab_lookup);
57 gpa.free(self.strtab_lookup);
58 gpa.free(self.symtab);
59 gpa.free(self.atom_by_index_table);
60 gpa.free(self.globals_lookup);
61 }
5862}
5963
6064pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {
......@@ -93,230 +97,244 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
9397 };
9498 while (it.next()) |cmd| {
9599 switch (cmd.cmd()) {
96 .SEGMENT_64 => {
97 const segment = cmd.cast(macho.segment_command_64).?;
98 try self.sections.ensureUnusedCapacity(allocator, segment.nsects);
99 for (cmd.getSections()) |sect| {
100 self.sections.appendAssumeCapacity(sect);
101 }
102 },
103100 .SYMTAB => {
104101 const symtab = cmd.cast(macho.symtab_command).?;
105 // Sadly, SYMTAB may be at an unaligned offset within the object file.
106102 self.in_symtab = @ptrCast(
107 [*]align(1) const macho.nlist_64,
108 self.contents.ptr + symtab.symoff,
103 [*]const macho.nlist_64,
104 @alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]),
109105 )[0..symtab.nsyms];
110106 self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize];
111 try self.symtab.appendUnalignedSlice(allocator, self.in_symtab.?);
107
108 const nsects = self.getSourceSections().len;
109
110 self.symtab = try allocator.alloc(macho.nlist_64, self.in_symtab.?.len + nsects);
111 self.source_symtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
112 self.strtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
113 self.globals_lookup = try allocator.alloc(i64, self.in_symtab.?.len);
114 self.atom_by_index_table = try allocator.alloc(AtomIndex, self.in_symtab.?.len + nsects);
115
116 for (self.symtab) |*sym| {
117 sym.* = .{
118 .n_value = 0,
119 .n_sect = 0,
120 .n_desc = 0,
121 .n_strx = 0,
122 .n_type = 0,
123 };
124 }
125
126 mem.set(i64, self.globals_lookup, -1);
127 mem.set(AtomIndex, self.atom_by_index_table, 0);
128
129 // You would expect that the symbol table is at least pre-sorted based on symbol's type:
130 // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance,
131 // the GO compiler does not necessarily respect that therefore we sort immediately by type
132 // and address within.
133 var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(allocator, self.in_symtab.?.len);
134 defer sorted_all_syms.deinit();
135
136 for (self.in_symtab.?) |_, index| {
137 sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });
138 }
139
140 // We sort by type: defined < undefined, and
141 // afterwards by address in each group. Normally, dysymtab should
142 // be enough to guarantee the sort, but turns out not every compiler
143 // is kind enough to specify the symbols in the correct order.
144 sort.sort(SymbolAtIndex, sorted_all_syms.items, self, SymbolAtIndex.lessThan);
145
146 for (sorted_all_syms.items) |sym_id, i| {
147 const sym = sym_id.getSymbol(self);
148
149 self.symtab[i] = sym;
150 self.source_symtab_lookup[i] = sym_id.index;
151
152 const sym_name_len = mem.sliceTo(@ptrCast([*:0]const u8, self.in_strtab.?.ptr + sym.n_strx), 0).len + 1;
153 self.strtab_lookup[i] = @intCast(u32, sym_name_len);
154 }
112155 },
113156 else => {},
114157 }
115158 }
116159}
117160
118const Context = struct {
119 object: *const Object,
120};
121
122161const SymbolAtIndex = struct {
123162 index: u32,
124163
164 const Context = *const Object;
165
125166 fn getSymbol(self: SymbolAtIndex, ctx: Context) macho.nlist_64 {
126 return ctx.object.getSourceSymbol(self.index).?;
167 return ctx.in_symtab.?[self.index];
127168 }
128169
129170 fn getSymbolName(self: SymbolAtIndex, ctx: Context) []const u8 {
130 const sym = self.getSymbol(ctx);
131 return ctx.object.getString(sym.n_strx);
171 const off = self.getSymbol(ctx).n_strx;
172 return mem.sliceTo(@ptrCast([*:0]const u8, ctx.in_strtab.?.ptr + off), 0);
132173 }
133174
134 /// Returns whether lhs is less than rhs by allocated address in object file.
135 /// Undefined symbols are pushed to the back (always evaluate to true).
175 /// Performs lexicographic-like check.
176 /// * lhs and rhs defined
177 /// * if lhs == rhs
178 /// * if lhs.n_sect == rhs.n_sect
179 /// * ext < weak < local < temp
180 /// * lhs.n_sect < rhs.n_sect
181 /// * lhs < rhs
182 /// * !rhs is undefined
136183 fn lessThan(ctx: Context, lhs_index: SymbolAtIndex, rhs_index: SymbolAtIndex) bool {
137184 const lhs = lhs_index.getSymbol(ctx);
138185 const rhs = rhs_index.getSymbol(ctx);
139 if (lhs.sect()) {
140 if (rhs.sect()) {
141 // Same group, sort by address.
142 return lhs.n_value < rhs.n_value;
143 } else {
144 return true;
145 }
146 } else {
147 return false;
148 }
149 }
150
151 /// Returns whether lhs is less senior than rhs. The rules are:
152 /// 1. ext
153 /// 2. weak
154 /// 3. local
155 /// 4. temp (local starting with `l` prefix).
156 fn lessThanBySeniority(ctx: Context, lhs_index: SymbolAtIndex, rhs_index: SymbolAtIndex) bool {
157 const lhs = lhs_index.getSymbol(ctx);
158 const rhs = rhs_index.getSymbol(ctx);
159 if (!rhs.ext()) {
160 const lhs_name = lhs_index.getSymbolName(ctx);
161 return mem.startsWith(u8, lhs_name, "l") or mem.startsWith(u8, lhs_name, "L");
162 } else if (rhs.pext() or rhs.weakDef()) {
163 return !lhs.ext();
164 } else {
186 if (lhs.sect() and rhs.sect()) {
187 if (lhs.n_value == rhs.n_value) {
188 if (lhs.n_sect == rhs.n_sect) {
189 if (lhs.ext() and rhs.ext()) {
190 if ((lhs.pext() or lhs.weakDef()) and (rhs.pext() or rhs.weakDef())) {
191 return false;
192 } else return rhs.pext() or rhs.weakDef();
193 } else {
194 const lhs_name = lhs_index.getSymbolName(ctx);
195 const lhs_temp = mem.startsWith(u8, lhs_name, "l") or mem.startsWith(u8, lhs_name, "L");
196 const rhs_name = rhs_index.getSymbolName(ctx);
197 const rhs_temp = mem.startsWith(u8, rhs_name, "l") or mem.startsWith(u8, rhs_name, "L");
198 if (lhs_temp and rhs_temp) {
199 return false;
200 } else return rhs_temp;
201 }
202 } else return lhs.n_sect < rhs.n_sect;
203 } else return lhs.n_value < rhs.n_value;
204 } else if (lhs.undf() and rhs.undf()) {
165205 return false;
166 }
206 } else return rhs.undf();
167207 }
168208
169 /// Like lessThanBySeniority but negated.
170 fn greaterThanBySeniority(ctx: Context, lhs_index: SymbolAtIndex, rhs_index: SymbolAtIndex) bool {
171 return !lessThanBySeniority(ctx, lhs_index, rhs_index);
209 fn lessThanByNStrx(ctx: Context, lhs: SymbolAtIndex, rhs: SymbolAtIndex) bool {
210 return lhs.getSymbol(ctx).n_strx < rhs.getSymbol(ctx).n_strx;
172211 }
173212};
174213
175fn filterSymbolsByAddress(
176 indexes: []SymbolAtIndex,
177 start_addr: u64,
178 end_addr: u64,
179 ctx: Context,
180) []SymbolAtIndex {
181 const Predicate = struct {
182 addr: u64,
183 ctx: Context,
214fn filterSymbolsBySection(symbols: []macho.nlist_64, n_sect: u8) struct {
215 index: u32,
216 len: u32,
217} {
218 const FirstMatch = struct {
219 n_sect: u8,
184220
185 pub fn predicate(pred: @This(), index: SymbolAtIndex) bool {
186 return index.getSymbol(pred.ctx).n_value >= pred.addr;
221 pub fn predicate(pred: @This(), symbol: macho.nlist_64) bool {
222 return symbol.n_sect == pred.n_sect;
187223 }
188224 };
225 const FirstNonMatch = struct {
226 n_sect: u8,
189227
190 const start = MachO.findFirst(SymbolAtIndex, indexes, 0, Predicate{
191 .addr = start_addr,
192 .ctx = ctx,
228 pub fn predicate(pred: @This(), symbol: macho.nlist_64) bool {
229 return symbol.n_sect != pred.n_sect;
230 }
231 };
232
233 const index = @import("zld.zig").lsearch(macho.nlist_64, symbols, FirstMatch{
234 .n_sect = n_sect,
193235 });
194 const end = MachO.findFirst(SymbolAtIndex, indexes, start, Predicate{
195 .addr = end_addr,
196 .ctx = ctx,
236 const len = @import("zld.zig").lsearch(macho.nlist_64, symbols[index..], FirstNonMatch{
237 .n_sect = n_sect,
197238 });
198239
199 return indexes[start..end];
240 return .{ .index = @intCast(u32, index), .len = @intCast(u32, len) };
200241}
201242
202fn filterRelocs(
203 relocs: []align(1) const macho.relocation_info,
204 start_addr: u64,
205 end_addr: u64,
206) []align(1) const macho.relocation_info {
243fn filterSymbolsByAddress(symbols: []macho.nlist_64, n_sect: u8, start_addr: u64, end_addr: u64) struct {
244 index: u32,
245 len: u32,
246} {
207247 const Predicate = struct {
208248 addr: u64,
249 n_sect: u8,
209250
210 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
211 return rel.r_address < self.addr;
251 pub fn predicate(pred: @This(), symbol: macho.nlist_64) bool {
252 return symbol.n_value >= pred.addr;
212253 }
213254 };
214255
215 const start = MachO.findFirst(macho.relocation_info, relocs, 0, Predicate{ .addr = end_addr });
216 const end = MachO.findFirst(macho.relocation_info, relocs, start, Predicate{ .addr = start_addr });
256 const index = @import("zld.zig").lsearch(macho.nlist_64, symbols, Predicate{
257 .addr = start_addr,
258 .n_sect = n_sect,
259 });
260 const len = @import("zld.zig").lsearch(macho.nlist_64, symbols[index..], Predicate{
261 .addr = end_addr,
262 .n_sect = n_sect,
263 });
217264
218 return relocs[start..end];
265 return .{ .index = @intCast(u32, index), .len = @intCast(u32, len) };
219266}
220267
221pub fn scanInputSections(self: Object, macho_file: *MachO) !void {
222 for (self.sections.items) |sect| {
223 const sect_id = (try macho_file.getOutputSection(sect)) orelse {
224 log.debug(" unhandled section", .{});
225 continue;
226 };
227 const output = macho_file.sections.items(.header)[sect_id];
228 log.debug("mapping '{s},{s}' into output sect({d}, '{s},{s}')", .{
229 sect.segName(),
230 sect.sectName(),
231 sect_id + 1,
232 output.segName(),
233 output.sectName(),
234 });
268const SortedSection = struct {
269 header: macho.section_64,
270 id: u8,
271};
272
273fn sectionLessThanByAddress(ctx: void, lhs: SortedSection, rhs: SortedSection) bool {
274 _ = ctx;
275 if (lhs.header.addr == rhs.header.addr) {
276 return lhs.id < rhs.id;
235277 }
278 return lhs.header.addr < rhs.header.addr;
236279}
237280
238/// Splits object into atoms assuming one-shot linking mode.
239pub fn splitIntoAtoms(self: *Object, macho_file: *MachO, object_id: u32) !void {
240 assert(macho_file.mode == .one_shot);
281pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {
282 const gpa = zld.gpa;
241283
242 const tracy = trace(@src());
243 defer tracy.end();
284 log.debug("splitting object({d}, {s}) into atoms", .{ object_id, self.name });
244285
245 const gpa = macho_file.base.allocator;
286 const sections = self.getSourceSections();
287 for (sections) |sect, id| {
288 if (sect.isDebug()) continue;
289 const out_sect_id = (try zld.getOutputSection(sect)) orelse {
290 log.debug(" unhandled section", .{});
291 continue;
292 };
293 if (sect.size == 0) continue;
246294
247 log.debug("splitting object({d}, {s}) into atoms: one-shot mode", .{ object_id, self.name });
295 const sect_id = @intCast(u8, id);
296 const sym = self.getSectionAliasSymbolPtr(sect_id);
297 sym.* = .{
298 .n_strx = 0,
299 .n_type = macho.N_SECT,
300 .n_sect = out_sect_id + 1,
301 .n_desc = 0,
302 .n_value = sect.addr,
303 };
304 }
248305
249 const in_symtab = self.in_symtab orelse {
250 for (self.sections.items) |sect, id| {
306 if (self.in_symtab == null) {
307 for (sections) |sect, id| {
251308 if (sect.isDebug()) continue;
252 const out_sect_id = (try macho_file.getOutputSection(sect)) orelse {
309 const out_sect_id = (try zld.getOutputSection(sect)) orelse {
253310 log.debug(" unhandled section", .{});
254311 continue;
255312 };
256313 if (sect.size == 0) continue;
257314
258315 const sect_id = @intCast(u8, id);
259 const sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
260 const sym_index = @intCast(u32, self.symtab.items.len);
261 try self.symtab.append(gpa, .{
262 .n_strx = 0,
263 .n_type = macho.N_SECT,
264 .n_sect = out_sect_id + 1,
265 .n_desc = 0,
266 .n_value = sect.addr,
267 });
268 try self.sections_as_symbols.putNoClobber(gpa, sect_id, sym_index);
269 break :blk sym_index;
270 };
271 const code: ?[]const u8 = if (!sect.isZerofill()) try self.getSectionContents(sect) else null;
272 const relocs = @ptrCast(
273 [*]align(1) const macho.relocation_info,
274 self.contents.ptr + sect.reloff,
275 )[0..sect.nreloc];
276 const atom = try self.createAtomFromSubsection(
277 macho_file,
316 const sym_index = self.getSectionAliasSymbolIndex(sect_id);
317 const atom_index = try self.createAtomFromSubsection(
318 zld,
278319 object_id,
279320 sym_index,
321 0,
280322 sect.size,
281323 sect.@"align",
282 code,
283 relocs,
284 &.{},
285324 out_sect_id,
286 sect,
287325 );
288 try macho_file.addAtomToSection(atom);
326 zld.addAtomToSection(atom_index);
289327 }
290328 return;
291 };
292
293 // You would expect that the symbol table is at least pre-sorted based on symbol's type:
294 // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance,
295 // the GO compiler does not necessarily respect that therefore we sort immediately by type
296 // and address within.
297 const context = Context{
298 .object = self,
299 };
300 var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(gpa, in_symtab.len);
301 defer sorted_all_syms.deinit();
302
303 for (in_symtab) |_, index| {
304 sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });
305329 }
306330
307 // We sort by type: defined < undefined, and
308 // afterwards by address in each group. Normally, dysymtab should
309 // be enough to guarantee the sort, but turns out not every compiler
310 // is kind enough to specify the symbols in the correct order.
311 sort.sort(SymbolAtIndex, sorted_all_syms.items, context, SymbolAtIndex.lessThan);
312
313331 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we
314332 // have to infer the start of undef section in the symtab ourselves.
315333 const iundefsym = blk: {
316334 const dysymtab = self.parseDysymtab() orelse {
317 var iundefsym: usize = sorted_all_syms.items.len;
335 var iundefsym: usize = self.in_symtab.?.len;
318336 while (iundefsym > 0) : (iundefsym -= 1) {
319 const sym = sorted_all_syms.items[iundefsym - 1].getSymbol(context);
337 const sym = self.symtab[iundefsym - 1];
320338 if (sym.sect()) break;
321339 }
322340 break :blk iundefsym;
......@@ -325,271 +343,209 @@ pub fn splitIntoAtoms(self: *Object, macho_file: *MachO, object_id: u32) !void {
325343 };
326344
327345 // We only care about defined symbols, so filter every other out.
328 const sorted_syms = sorted_all_syms.items[0..iundefsym];
346 const symtab = try gpa.dupe(macho.nlist_64, self.symtab[0..iundefsym]);
347 defer gpa.free(symtab);
348
329349 const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
330350
331 for (self.sections.items) |sect, id| {
351 // Sort section headers by address.
352 var sorted_sections = try gpa.alloc(SortedSection, sections.len);
353 defer gpa.free(sorted_sections);
354
355 for (sections) |sect, id| {
356 sorted_sections[id] = .{ .header = sect, .id = @intCast(u8, id) };
357 }
358
359 std.sort.sort(SortedSection, sorted_sections, {}, sectionLessThanByAddress);
360
361 var sect_sym_index: u32 = 0;
362 for (sorted_sections) |section| {
363 const sect = section.header;
332364 if (sect.isDebug()) continue;
333365
334 const sect_id = @intCast(u8, id);
366 const sect_id = section.id;
335367 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
336368
337 // Get matching segment/section in the final artifact.
338 const out_sect_id = (try macho_file.getOutputSection(sect)) orelse {
369 // Get output segment/section in the final artifact.
370 const out_sect_id = (try zld.getOutputSection(sect)) orelse {
339371 log.debug(" unhandled section", .{});
340372 continue;
341373 };
342374
343375 log.debug(" output sect({d}, '{s},{s}')", .{
344376 out_sect_id + 1,
345 macho_file.sections.items(.header)[out_sect_id].segName(),
346 macho_file.sections.items(.header)[out_sect_id].sectName(),
377 zld.sections.items(.header)[out_sect_id].segName(),
378 zld.sections.items(.header)[out_sect_id].sectName(),
347379 });
348380
349 const cpu_arch = macho_file.base.options.target.cpu.arch;
381 const cpu_arch = zld.options.target.cpu.arch;
382 const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1);
383 const sect_start_index = sect_sym_index + sect_loc.index;
350384
351 // Read section's code
352 const code: ?[]const u8 = if (!sect.isZerofill()) try self.getSectionContents(sect) else null;
385 sect_sym_index += sect_loc.len;
353386
354 // Read section's list of relocations
355 const relocs = @ptrCast(
356 [*]align(1) const macho.relocation_info,
357 self.contents.ptr + sect.reloff,
358 )[0..sect.nreloc];
359
360 // Symbols within this section only.
361 const filtered_syms = filterSymbolsByAddress(
362 sorted_syms,
363 sect.addr,
364 sect.addr + sect.size,
365 context,
366 );
367
368 if (subsections_via_symbols and filtered_syms.len > 0) {
387 if (sect.size == 0) continue;
388 if (subsections_via_symbols and sect_loc.len > 0) {
369389 // If the first nlist does not match the start of the section,
370390 // then we need to encapsulate the memory range [section start, first symbol)
371391 // as a temporary symbol and insert the matching Atom.
372 const first_sym = filtered_syms[0].getSymbol(context);
392 const first_sym = symtab[sect_start_index];
373393 if (first_sym.n_value > sect.addr) {
374 const sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
375 const sym_index = @intCast(u32, self.symtab.items.len);
376 try self.symtab.append(gpa, .{
377 .n_strx = 0,
378 .n_type = macho.N_SECT,
379 .n_sect = out_sect_id + 1,
380 .n_desc = 0,
381 .n_value = sect.addr,
382 });
383 try self.sections_as_symbols.putNoClobber(gpa, sect_id, sym_index);
384 break :blk sym_index;
385 };
394 const sym_index = self.getSectionAliasSymbolIndex(sect_id);
386395 const atom_size = first_sym.n_value - sect.addr;
387 const atom_code: ?[]const u8 = if (code) |cc| blk: {
388 const size = math.cast(usize, atom_size) orelse return error.Overflow;
389 break :blk cc[0..size];
390 } else null;
391 const atom = try self.createAtomFromSubsection(
392 macho_file,
396 const atom_index = try self.createAtomFromSubsection(
397 zld,
393398 object_id,
394399 sym_index,
400 0,
395401 atom_size,
396402 sect.@"align",
397 atom_code,
398 relocs,
399 &.{},
400403 out_sect_id,
401 sect,
402404 );
403 try macho_file.addAtomToSection(atom);
405 zld.addAtomToSection(atom_index);
404406 }
405407
406 var next_sym_count: usize = 0;
407 while (next_sym_count < filtered_syms.len) {
408 const next_sym = filtered_syms[next_sym_count].getSymbol(context);
408 var next_sym_index = sect_start_index;
409 while (next_sym_index < sect_start_index + sect_loc.len) {
410 const next_sym = symtab[next_sym_index];
409411 const addr = next_sym.n_value;
410 const atom_syms = filterSymbolsByAddress(
411 filtered_syms[next_sym_count..],
412 const atom_loc = filterSymbolsByAddress(
413 symtab[next_sym_index..],
414 sect_id + 1,
412415 addr,
413416 addr + 1,
414 context,
415 );
416 next_sym_count += atom_syms.len;
417
418 // We want to bubble up the first externally defined symbol here.
419 assert(atom_syms.len > 0);
420 var sorted_atom_syms = std.ArrayList(SymbolAtIndex).init(gpa);
421 defer sorted_atom_syms.deinit();
422 try sorted_atom_syms.appendSlice(atom_syms);
423 sort.sort(
424 SymbolAtIndex,
425 sorted_atom_syms.items,
426 context,
427 SymbolAtIndex.greaterThanBySeniority,
428417 );
418 assert(atom_loc.len > 0);
419 const atom_sym_index = atom_loc.index + next_sym_index;
420 const nsyms_trailing = atom_loc.len - 1;
421 next_sym_index += atom_loc.len;
422
423 // TODO: We want to bubble up the first externally defined symbol here.
424 const atom_size = if (next_sym_index < sect_start_index + sect_loc.len)
425 symtab[next_sym_index].n_value - addr
426 else
427 sect.addr + sect.size - addr;
429428
430 const atom_size = blk: {
431 const end_addr = if (next_sym_count < filtered_syms.len)
432 filtered_syms[next_sym_count].getSymbol(context).n_value
433 else
434 sect.addr + sect.size;
435 break :blk end_addr - addr;
436 };
437 const atom_code: ?[]const u8 = if (code) |cc| blk: {
438 const start = math.cast(usize, addr - sect.addr) orelse return error.Overflow;
439 const size = math.cast(usize, atom_size) orelse return error.Overflow;
440 break :blk cc[start..][0..size];
441 } else null;
442429 const atom_align = if (addr > 0)
443430 math.min(@ctz(addr), sect.@"align")
444431 else
445432 sect.@"align";
446 const atom = try self.createAtomFromSubsection(
447 macho_file,
433
434 const atom_index = try self.createAtomFromSubsection(
435 zld,
448436 object_id,
449 sorted_atom_syms.items[0].index,
437 atom_sym_index,
438 nsyms_trailing,
450439 atom_size,
451440 atom_align,
452 atom_code,
453 relocs,
454 sorted_atom_syms.items[1..],
455441 out_sect_id,
456 sect,
457442 );
458443
444 // TODO rework this at the relocation level
459445 if (cpu_arch == .x86_64 and addr == sect.addr) {
460446 // In x86_64 relocs, it can so happen that the compiler refers to the same
461447 // atom by both the actual assigned symbol and the start of the section. In this
462448 // case, we need to link the two together so add an alias.
463 const alias = self.sections_as_symbols.get(sect_id) orelse blk: {
464 const alias = @intCast(u32, self.symtab.items.len);
465 try self.symtab.append(gpa, .{
466 .n_strx = 0,
467 .n_type = macho.N_SECT,
468 .n_sect = out_sect_id + 1,
469 .n_desc = 0,
470 .n_value = addr,
471 });
472 try self.sections_as_symbols.putNoClobber(gpa, sect_id, alias);
473 break :blk alias;
474 };
475 try atom.contained.append(gpa, .{
476 .sym_index = alias,
477 .offset = 0,
478 });
479 try self.atom_by_index_table.put(gpa, alias, atom);
449 const alias_index = self.getSectionAliasSymbolIndex(sect_id);
450 self.atom_by_index_table[alias_index] = atom_index;
480451 }
481452
482 try macho_file.addAtomToSection(atom);
453 zld.addAtomToSection(atom_index);
483454 }
484455 } else {
485 // If there is no symbol to refer to this atom, we create
486 // a temp one, unless we already did that when working out the relocations
487 // of other atoms.
488 const sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
489 const sym_index = @intCast(u32, self.symtab.items.len);
490 try self.symtab.append(gpa, .{
491 .n_strx = 0,
492 .n_type = macho.N_SECT,
493 .n_sect = out_sect_id + 1,
494 .n_desc = 0,
495 .n_value = sect.addr,
496 });
497 try self.sections_as_symbols.putNoClobber(gpa, sect_id, sym_index);
498 break :blk sym_index;
499 };
500 const atom = try self.createAtomFromSubsection(
501 macho_file,
456 const sym_index = self.getSectionAliasSymbolIndex(sect_id);
457 const atom_index = try self.createAtomFromSubsection(
458 zld,
502459 object_id,
503460 sym_index,
461 0,
504462 sect.size,
505463 sect.@"align",
506 code,
507 relocs,
508 filtered_syms,
509464 out_sect_id,
510 sect,
511465 );
512 try macho_file.addAtomToSection(atom);
466 // If there is no symbol to refer to this atom, we create
467 // a temp one, unless we already did that when working out the relocations
468 // of other atoms.
469 zld.addAtomToSection(atom_index);
513470 }
514471 }
515472}
516473
517474fn createAtomFromSubsection(
518475 self: *Object,
519 macho_file: *MachO,
520 object_id: u32,
476 zld: *Zld,
477 object_id: u31,
521478 sym_index: u32,
479 nsyms_trailing: u32,
522480 size: u64,
523481 alignment: u32,
524 code: ?[]const u8,
525 relocs: []align(1) const macho.relocation_info,
526 indexes: []const SymbolAtIndex,
527482 out_sect_id: u8,
528 sect: macho.section_64,
529) !*Atom {
530 const gpa = macho_file.base.allocator;
531 const sym = self.symtab.items[sym_index];
532 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);
483) !AtomIndex {
484 const gpa = zld.gpa;
485 const atom_index = try zld.createEmptyAtom(sym_index, size, alignment);
486 const atom = zld.getAtomPtr(atom_index);
487 atom.nsyms_trailing = nsyms_trailing;
533488 atom.file = object_id;
534 self.symtab.items[sym_index].n_sect = out_sect_id + 1;
489 self.symtab[sym_index].n_sect = out_sect_id + 1;
535490
536491 log.debug("creating ATOM(%{d}, '{s}') in sect({d}, '{s},{s}') in object({d})", .{
537492 sym_index,
538 self.getString(sym.n_strx),
493 self.getSymbolName(sym_index),
539494 out_sect_id + 1,
540 macho_file.sections.items(.header)[out_sect_id].segName(),
541 macho_file.sections.items(.header)[out_sect_id].sectName(),
495 zld.sections.items(.header)[out_sect_id].segName(),
496 zld.sections.items(.header)[out_sect_id].sectName(),
542497 object_id,
543498 });
544499
545 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
546 try self.managed_atoms.append(gpa, atom);
500 try self.atoms.append(gpa, atom_index);
501 self.atom_by_index_table[sym_index] = atom_index;
547502
548 if (code) |cc| {
549 assert(size == cc.len);
550 mem.copy(u8, atom.code.items, cc);
503 var it = Atom.getInnerSymbolsIterator(zld, atom_index);
504 while (it.next()) |sym_loc| {
505 const inner = zld.getSymbolPtr(sym_loc);
506 inner.n_sect = out_sect_id + 1;
507 self.atom_by_index_table[sym_loc.sym_index] = atom_index;
551508 }
552509
553 const base_offset = sym.n_value - sect.addr;
554 const filtered_relocs = filterRelocs(relocs, base_offset, base_offset + size);
555 try atom.parseRelocs(filtered_relocs, .{
556 .macho_file = macho_file,
557 .base_addr = sect.addr,
558 .base_offset = @intCast(i32, base_offset),
559 });
560
561 // Since this is atom gets a helper local temporary symbol that didn't exist
562 // in the object file which encompasses the entire section, we need traverse
563 // the filtered symbols and note which symbol is contained within so that
564 // we can properly allocate addresses down the line.
565 // While we're at it, we need to update segment,section mapping of each symbol too.
566 try atom.contained.ensureTotalCapacity(gpa, indexes.len);
567 for (indexes) |inner_sym_index| {
568 const inner_sym = &self.symtab.items[inner_sym_index.index];
569 inner_sym.n_sect = out_sect_id + 1;
570 atom.contained.appendAssumeCapacity(.{
571 .sym_index = inner_sym_index.index,
572 .offset = inner_sym.n_value - sym.n_value,
573 });
574
575 try self.atom_by_index_table.putNoClobber(gpa, inner_sym_index.index, atom);
576 }
577
578 return atom;
510 return atom_index;
579511}
580512
581513pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
582514 const symtab = self.in_symtab.?;
583515 if (index >= symtab.len) return null;
584 return symtab[index];
516 const mapped_index = self.source_symtab_lookup[index];
517 return symtab[mapped_index];
518}
519
520/// Caller owns memory.
521pub fn createReverseSymbolLookup(self: Object, gpa: Allocator) ![]u32 {
522 const lookup = try gpa.alloc(u32, self.in_symtab.?.len);
523 for (self.source_symtab_lookup) |source_id, id| {
524 lookup[source_id] = @intCast(u32, id);
525 }
526 return lookup;
585527}
586528
587529pub fn getSourceSection(self: Object, index: u16) macho.section_64 {
588 assert(index < self.sections.items.len);
589 return self.sections.items[index];
530 const sections = self.getSourceSections();
531 assert(index < sections.len);
532 return sections[index];
533}
534
535pub fn getSourceSections(self: Object) []const macho.section_64 {
536 var it = LoadCommandIterator{
537 .ncmds = self.header.ncmds,
538 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
539 };
540 while (it.next()) |cmd| switch (cmd.cmd()) {
541 .SEGMENT_64 => {
542 return cmd.getSections();
543 },
544 else => {},
545 } else unreachable;
590546}
591547
592pub fn parseDataInCode(self: Object) ?[]align(1) const macho.data_in_code_entry {
548pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry {
593549 var it = LoadCommandIterator{
594550 .ncmds = self.header.ncmds,
595551 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
......@@ -600,8 +556,8 @@ pub fn parseDataInCode(self: Object) ?[]align(1) const macho.data_in_code_entry
600556 const dice = cmd.cast(macho.linkedit_data_command).?;
601557 const ndice = @divExact(dice.datasize, @sizeOf(macho.data_in_code_entry));
602558 return @ptrCast(
603 [*]align(1) const macho.data_in_code_entry,
604 self.contents.ptr + dice.dataoff,
559 [*]const macho.data_in_code_entry,
560 @alignCast(@alignOf(macho.data_in_code_entry), &self.contents[dice.dataoff]),
605561 )[0..ndice];
606562 },
607563 else => {},
......@@ -624,73 +580,66 @@ fn parseDysymtab(self: Object) ?macho.dysymtab_command {
624580 } else return null;
625581}
626582
627pub fn parseDwarfInfo(self: Object) error{Overflow}!dwarf.DwarfInfo {
628 var di = dwarf.DwarfInfo{
629 .endian = .Little,
583pub fn parseDwarfInfo(self: Object) DwarfInfo {
584 var di = DwarfInfo{
630585 .debug_info = &[0]u8{},
631586 .debug_abbrev = &[0]u8{},
632587 .debug_str = &[0]u8{},
633 .debug_str_offsets = &[0]u8{},
634 .debug_line = &[0]u8{},
635 .debug_line_str = &[0]u8{},
636 .debug_ranges = &[0]u8{},
637 .debug_loclists = &[0]u8{},
638 .debug_rnglists = &[0]u8{},
639 .debug_addr = &[0]u8{},
640 .debug_names = &[0]u8{},
641 .debug_frame = &[0]u8{},
642588 };
643 for (self.sections.items) |sect| {
644 const segname = sect.segName();
589 for (self.getSourceSections()) |sect| {
590 if (!sect.isDebug()) continue;
645591 const sectname = sect.sectName();
646 if (mem.eql(u8, segname, "__DWARF")) {
647 if (mem.eql(u8, sectname, "__debug_info")) {
648 di.debug_info = try self.getSectionContents(sect);
649 } else if (mem.eql(u8, sectname, "__debug_abbrev")) {
650 di.debug_abbrev = try self.getSectionContents(sect);
651 } else if (mem.eql(u8, sectname, "__debug_str")) {
652 di.debug_str = try self.getSectionContents(sect);
653 } else if (mem.eql(u8, sectname, "__debug_str_offsets")) {
654 di.debug_str_offsets = try self.getSectionContents(sect);
655 } else if (mem.eql(u8, sectname, "__debug_line")) {
656 di.debug_line = try self.getSectionContents(sect);
657 } else if (mem.eql(u8, sectname, "__debug_line_str")) {
658 di.debug_line_str = try self.getSectionContents(sect);
659 } else if (mem.eql(u8, sectname, "__debug_ranges")) {
660 di.debug_ranges = try self.getSectionContents(sect);
661 } else if (mem.eql(u8, sectname, "__debug_loclists")) {
662 di.debug_loclists = try self.getSectionContents(sect);
663 } else if (mem.eql(u8, sectname, "__debug_rnglists")) {
664 di.debug_rnglists = try self.getSectionContents(sect);
665 } else if (mem.eql(u8, sectname, "__debug_addr")) {
666 di.debug_addr = try self.getSectionContents(sect);
667 } else if (mem.eql(u8, sectname, "__debug_names")) {
668 di.debug_names = try self.getSectionContents(sect);
669 } else if (mem.eql(u8, sectname, "__debug_frame")) {
670 di.debug_frame = try self.getSectionContents(sect);
671 }
592 if (mem.eql(u8, sectname, "__debug_info")) {
593 di.debug_info = self.getSectionContents(sect);
594 } else if (mem.eql(u8, sectname, "__debug_abbrev")) {
595 di.debug_abbrev = self.getSectionContents(sect);
596 } else if (mem.eql(u8, sectname, "__debug_str")) {
597 di.debug_str = self.getSectionContents(sect);
672598 }
673599 }
674600 return di;
675601}
676602
677pub fn getSectionContents(self: Object, sect: macho.section_64) error{Overflow}![]const u8 {
678 const size = math.cast(usize, sect.size) orelse return error.Overflow;
679 log.debug("getting {s},{s} data at 0x{x} - 0x{x}", .{
680 sect.segName(),
681 sect.sectName(),
682 sect.offset,
683 sect.offset + sect.size,
684 });
603pub fn getSectionContents(self: Object, sect: macho.section_64) []const u8 {
604 const size = @intCast(usize, sect.size);
685605 return self.contents[sect.offset..][0..size];
686606}
687607
688pub fn getString(self: Object, off: u32) []const u8 {
608pub fn getSectionAliasSymbolIndex(self: Object, sect_id: u8) u32 {
609 const start = @intCast(u32, self.in_symtab.?.len);
610 return start + sect_id;
611}
612
613pub fn getSectionAliasSymbol(self: *Object, sect_id: u8) macho.nlist_64 {
614 return self.symtab[self.getSectionAliasSymbolIndex(sect_id)];
615}
616
617pub fn getSectionAliasSymbolPtr(self: *Object, sect_id: u8) *macho.nlist_64 {
618 return &self.symtab[self.getSectionAliasSymbolIndex(sect_id)];
619}
620
621pub fn getRelocs(self: Object, sect: macho.section_64) []align(1) const macho.relocation_info {
622 if (sect.nreloc == 0) return &[0]macho.relocation_info{};
623 return @ptrCast([*]align(1) const macho.relocation_info, self.contents.ptr + sect.reloff)[0..sect.nreloc];
624}
625
626pub fn getSymbolName(self: Object, index: u32) []const u8 {
689627 const strtab = self.in_strtab.?;
690 assert(off < strtab.len);
691 return mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + off), 0);
628 const sym = self.symtab[index];
629
630 if (self.getSourceSymbol(index) == null) {
631 assert(sym.n_strx == 0);
632 return "";
633 }
634
635 const start = sym.n_strx;
636 const len = self.strtab_lookup[index];
637
638 return strtab[start..][0 .. len - 1 :0];
692639}
693640
694pub fn getAtomForSymbol(self: Object, sym_index: u32) ?*Atom {
695 return self.atom_by_index_table.get(sym_index);
641pub fn getAtomIndexForSymbol(self: Object, sym_index: u32) ?AtomIndex {
642 const atom_index = self.atom_by_index_table[sym_index];
643 if (atom_index == 0) return null;
644 return atom_index;
696645}
src/link/MachO/Relocation.zig-1
......@@ -47,7 +47,6 @@ pub fn getTargetAtom(self: Relocation, macho_file: *MachO) ?*Atom {
4747 else => unreachable,
4848 }
4949 if (macho_file.getStubsAtomForSymbol(self.target)) |stubs_atom| return stubs_atom;
50 if (macho_file.getTlvPtrAtomForSymbol(self.target)) |tlv_ptr_atom| return tlv_ptr_atom;
5150 return macho_file.getAtomForSymbol(self.target);
5251}
5352
src/link/MachO/ZldAtom.zig created+1033
......@@ -0,0 +1,1033 @@
1const Atom = @This();
2
3const std = @import("std");
4const build_options = @import("build_options");
5const aarch64 = @import("../../arch/aarch64/bits.zig");
6const assert = std.debug.assert;
7const log = std.log.scoped(.atom);
8const macho = std.macho;
9const math = std.math;
10const mem = std.mem;
11const meta = std.meta;
12
13const Allocator = mem.Allocator;
14const Arch = std.Target.Cpu.Arch;
15const AtomIndex = @import("zld.zig").AtomIndex;
16const Object = @import("Object.zig");
17const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
18const Zld = @import("zld.zig").Zld;
19
20/// Each decl always gets a local symbol with the fully qualified name.
21/// The vaddr and size are found here directly.
22/// The file offset is found by computing the vaddr offset from the section vaddr
23/// the symbol references, and adding that to the file offset of the section.
24/// If this field is 0, it means the codegen size = 0 and there is no symbol or
25/// offset table entry.
26sym_index: u32,
27
28/// If this Atom references a subsection in an Object file, `nsyms_trailing`
29/// tells how many symbols trailing `sym_index` fall within this Atom's address
30/// range.
31nsyms_trailing: u32,
32
33/// -1 means symbol defined by the linker.
34/// Otherwise, it is the index into appropriate object file.
35file: i32,
36
37/// Size and alignment of this atom
38/// Unlike in Elf, we need to store the size of this symbol as part of
39/// the atom since macho.nlist_64 lacks this information.
40size: u64,
41
42/// Alignment of this atom as a power of 2.
43/// For instance, aligmment of 0 should be read as 2^0 = 1 byte aligned.
44alignment: u32,
45
46cached_relocs_start: i32,
47cached_relocs_len: u32,
48
49/// Points to the previous and next neighbours
50next_index: ?AtomIndex,
51prev_index: ?AtomIndex,
52
53pub const empty = Atom{
54 .sym_index = 0,
55 .nsyms_trailing = 0,
56 .file = -1,
57 .size = 0,
58 .alignment = 0,
59 .cached_relocs_start = -1,
60 .cached_relocs_len = 0,
61 .prev_index = null,
62 .next_index = null,
63};
64
65pub inline fn getFile(self: Atom) ?u31 {
66 if (self.file == -1) return null;
67 return @intCast(u31, self.file);
68}
69
70pub inline fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
71 return .{
72 .sym_index = self.sym_index,
73 .file = self.file,
74 };
75}
76
77const InnerSymIterator = struct {
78 sym_index: u32,
79 count: u32,
80 file: i32,
81
82 pub fn next(it: *@This()) ?SymbolWithLoc {
83 if (it.count == 0) return null;
84 it.sym_index += 1;
85 it.count -= 1;
86 return SymbolWithLoc{ .sym_index = it.sym_index, .file = it.file };
87 }
88};
89
90pub fn getInnerSymbolsIterator(zld: *Zld, atom_index: AtomIndex) InnerSymIterator {
91 const atom = zld.getAtom(atom_index);
92 assert(atom.getFile() != null);
93 return .{
94 .sym_index = atom.sym_index,
95 .count = atom.nsyms_trailing,
96 .file = atom.file,
97 };
98}
99
100pub fn getSectionAlias(zld: *Zld, atom_index: AtomIndex) ?SymbolWithLoc {
101 const atom = zld.getAtom(atom_index);
102 assert(atom.getFile() != null);
103
104 const object = zld.objects.items[atom.getFile().?];
105 const nbase = @intCast(u32, object.in_symtab.?.len);
106 const ntotal = @intCast(u32, object.symtab.len);
107 var sym_index: u32 = nbase;
108 while (sym_index < ntotal) : (sym_index += 1) {
109 if (object.getAtomIndexForSymbol(sym_index)) |other_atom_index| {
110 if (other_atom_index == atom_index) return SymbolWithLoc{
111 .sym_index = sym_index,
112 .file = atom.file,
113 };
114 }
115 }
116 return null;
117}
118
119pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: AtomIndex, sym_index: u32) u64 {
120 const atom = zld.getAtom(atom_index);
121 assert(atom.getFile() != null);
122
123 if (atom.sym_index == sym_index) return 0;
124
125 const object = zld.objects.items[atom.getFile().?];
126 const source_atom_sym = object.getSourceSymbol(atom.sym_index).?;
127 const source_sym = object.getSourceSymbol(sym_index).?;
128 return source_sym.n_value - source_atom_sym.n_value;
129}
130
131pub fn scanAtomRelocs(
132 zld: *Zld,
133 atom_index: AtomIndex,
134 relocs: []align(1) const macho.relocation_info,
135 reverse_lookup: []u32,
136) !void {
137 const arch = zld.options.target.cpu.arch;
138 const atom = zld.getAtom(atom_index);
139 assert(atom.getFile() != null); // synthetic atoms do not have relocs
140
141 return switch (arch) {
142 .aarch64 => scanAtomRelocsArm64(zld, atom_index, relocs, reverse_lookup),
143 .x86_64 => scanAtomRelocsX86(zld, atom_index, relocs, reverse_lookup),
144 else => unreachable,
145 };
146}
147
148const RelocContext = struct {
149 base_addr: u64 = 0,
150 base_offset: i32 = 0,
151};
152
153pub fn parseRelocTarget(
154 zld: *Zld,
155 atom_index: AtomIndex,
156 rel: macho.relocation_info,
157 reverse_lookup: []u32,
158) !SymbolWithLoc {
159 const atom = zld.getAtom(atom_index);
160 const object = &zld.objects.items[atom.getFile().?];
161
162 if (rel.r_extern == 0) {
163 const sect_id = @intCast(u8, rel.r_symbolnum - 1);
164 const sym_index = object.getSectionAliasSymbolIndex(sect_id);
165 return SymbolWithLoc{ .sym_index = sym_index, .file = atom.file };
166 }
167
168 const sym_index = reverse_lookup[rel.r_symbolnum];
169 const sym_loc = SymbolWithLoc{
170 .sym_index = sym_index,
171 .file = atom.file,
172 };
173 const sym = zld.getSymbol(sym_loc);
174
175 if (sym.sect() and !sym.ext()) {
176 return sym_loc;
177 } else if (object.globals_lookup[sym_index] > -1) {
178 const global_index = @intCast(u32, object.globals_lookup[sym_index]);
179 return zld.globals.items[global_index];
180 } else return sym_loc;
181}
182
183pub fn getRelocTargetAtomIndex(zld: *Zld, rel: macho.relocation_info, target: SymbolWithLoc) ?AtomIndex {
184 const is_via_got = got: {
185 switch (zld.options.target.cpu.arch) {
186 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
187 .ARM64_RELOC_GOT_LOAD_PAGE21,
188 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
189 .ARM64_RELOC_POINTER_TO_GOT,
190 => true,
191 else => false,
192 },
193 .x86_64 => break :got switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
194 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => true,
195 else => false,
196 },
197 else => unreachable,
198 }
199 };
200
201 if (is_via_got) {
202 return zld.getGotAtomIndexForSymbol(target).?; // panic means fatal error
203 }
204 if (zld.getStubsAtomIndexForSymbol(target)) |stubs_atom| return stubs_atom;
205 if (zld.getTlvPtrAtomIndexForSymbol(target)) |tlv_ptr_atom| return tlv_ptr_atom;
206
207 if (target.getFile() == null) {
208 const target_sym_name = zld.getSymbolName(target);
209 if (mem.eql(u8, "__mh_execute_header", target_sym_name)) return null;
210 if (mem.eql(u8, "___dso_handle", target_sym_name)) return null;
211
212 unreachable; // referenced symbol not found
213 }
214
215 const object = zld.objects.items[target.getFile().?];
216 return object.getAtomIndexForSymbol(target.sym_index);
217}
218
219fn scanAtomRelocsArm64(
220 zld: *Zld,
221 atom_index: AtomIndex,
222 relocs: []align(1) const macho.relocation_info,
223 reverse_lookup: []u32,
224) !void {
225 for (relocs) |rel| {
226 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
227
228 switch (rel_type) {
229 .ARM64_RELOC_ADDEND, .ARM64_RELOC_SUBTRACTOR => continue,
230 else => {},
231 }
232
233 if (rel.r_extern == 0) continue;
234
235 const atom = zld.getAtom(atom_index);
236 const object = &zld.objects.items[atom.getFile().?];
237 const sym_index = reverse_lookup[rel.r_symbolnum];
238 const sym_loc = SymbolWithLoc{
239 .sym_index = sym_index,
240 .file = atom.file,
241 };
242 const sym = zld.getSymbol(sym_loc);
243
244 if (sym.sect() and !sym.ext()) continue;
245
246 const target = if (object.globals_lookup[sym_index] > -1) blk: {
247 const global_index = @intCast(u32, object.globals_lookup[sym_index]);
248 break :blk zld.globals.items[global_index];
249 } else sym_loc;
250
251 switch (rel_type) {
252 .ARM64_RELOC_BRANCH26 => {
253 // TODO rewrite relocation
254 try addStub(zld, target);
255 },
256 .ARM64_RELOC_GOT_LOAD_PAGE21,
257 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
258 .ARM64_RELOC_POINTER_TO_GOT,
259 => {
260 // TODO rewrite relocation
261 try addGotEntry(zld, target);
262 },
263 .ARM64_RELOC_TLVP_LOAD_PAGE21,
264 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
265 => {
266 try addTlvPtrEntry(zld, target);
267 },
268 else => {},
269 }
270 }
271}
272
273fn scanAtomRelocsX86(
274 zld: *Zld,
275 atom_index: AtomIndex,
276 relocs: []align(1) const macho.relocation_info,
277 reverse_lookup: []u32,
278) !void {
279 for (relocs) |rel| {
280 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
281
282 switch (rel_type) {
283 .X86_64_RELOC_SUBTRACTOR => continue,
284 else => {},
285 }
286
287 if (rel.r_extern == 0) continue;
288
289 const atom = zld.getAtom(atom_index);
290 const object = &zld.objects.items[atom.getFile().?];
291 const sym_index = reverse_lookup[rel.r_symbolnum];
292 const sym_loc = SymbolWithLoc{
293 .sym_index = sym_index,
294 .file = atom.file,
295 };
296 const sym = zld.getSymbol(sym_loc);
297
298 if (sym.sect() and !sym.ext()) continue;
299
300 const target = if (object.globals_lookup[sym_index] > -1) blk: {
301 const global_index = @intCast(u32, object.globals_lookup[sym_index]);
302 break :blk zld.globals.items[global_index];
303 } else sym_loc;
304
305 switch (rel_type) {
306 .X86_64_RELOC_BRANCH => {
307 // TODO rewrite relocation
308 try addStub(zld, target);
309 },
310 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => {
311 // TODO rewrite relocation
312 try addGotEntry(zld, target);
313 },
314 .X86_64_RELOC_TLV => {
315 try addTlvPtrEntry(zld, target);
316 },
317 else => {},
318 }
319 }
320}
321
322fn addTlvPtrEntry(zld: *Zld, target: SymbolWithLoc) !void {
323 const target_sym = zld.getSymbol(target);
324 if (!target_sym.undf()) return;
325 if (zld.tlv_ptr_table.contains(target)) return;
326
327 const gpa = zld.gpa;
328 const atom_index = try zld.createTlvPtrAtom();
329 const tlv_ptr_index = @intCast(u32, zld.tlv_ptr_entries.items.len);
330 try zld.tlv_ptr_entries.append(gpa, .{
331 .target = target,
332 .atom_index = atom_index,
333 });
334 try zld.tlv_ptr_table.putNoClobber(gpa, target, tlv_ptr_index);
335}
336
337fn addGotEntry(zld: *Zld, target: SymbolWithLoc) !void {
338 if (zld.got_table.contains(target)) return;
339 const gpa = zld.gpa;
340 const atom_index = try zld.createGotAtom();
341 const got_index = @intCast(u32, zld.got_entries.items.len);
342 try zld.got_entries.append(gpa, .{
343 .target = target,
344 .atom_index = atom_index,
345 });
346 try zld.got_table.putNoClobber(gpa, target, got_index);
347}
348
349fn addStub(zld: *Zld, target: SymbolWithLoc) !void {
350 const target_sym = zld.getSymbol(target);
351 if (!target_sym.undf()) return;
352 if (zld.stubs_table.contains(target)) return;
353
354 const gpa = zld.gpa;
355 _ = try zld.createStubHelperAtom();
356 _ = try zld.createLazyPointerAtom();
357 const atom_index = try zld.createStubAtom();
358 const stubs_index = @intCast(u32, zld.stubs.items.len);
359 try zld.stubs.append(gpa, .{
360 .target = target,
361 .atom_index = atom_index,
362 });
363 try zld.stubs_table.putNoClobber(gpa, target, stubs_index);
364}
365
366pub fn resolveRelocs(
367 zld: *Zld,
368 atom_index: AtomIndex,
369 atom_code: []u8,
370 atom_relocs: []align(1) const macho.relocation_info,
371 reverse_lookup: []u32,
372) !void {
373 const arch = zld.options.target.cpu.arch;
374 const atom = zld.getAtom(atom_index);
375 assert(atom.getFile() != null); // synthetic atoms do not have relocs
376
377 const object = zld.objects.items[atom.getFile().?];
378 const ctx: RelocContext = blk: {
379 if (object.getSourceSymbol(atom.sym_index)) |source_sym| {
380 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
381 break :blk .{
382 .base_addr = source_sect.addr,
383 .base_offset = @intCast(i32, source_sym.n_value - source_sect.addr),
384 };
385 }
386 for (object.getSourceSections()) |source_sect, i| {
387 const sym_index = object.getSectionAliasSymbolIndex(@intCast(u8, i));
388 if (sym_index == atom.sym_index) break :blk .{
389 .base_addr = source_sect.addr,
390 .base_offset = 0,
391 };
392 } else unreachable;
393 };
394
395 log.debug("resolving relocations in ATOM(%{d}, '{s}')", .{
396 atom.sym_index,
397 zld.getSymbolName(atom.getSymbolWithLoc()),
398 });
399
400 return switch (arch) {
401 .aarch64 => resolveRelocsArm64(zld, atom_index, atom_code, atom_relocs, reverse_lookup, ctx),
402 .x86_64 => resolveRelocsX86(zld, atom_index, atom_code, atom_relocs, reverse_lookup, ctx),
403 else => unreachable,
404 };
405}
406
407pub fn getRelocTargetAddress(zld: *Zld, rel: macho.relocation_info, target: SymbolWithLoc, is_tlv: bool) !u64 {
408 const target_atom_index = getRelocTargetAtomIndex(zld, rel, target) orelse {
409 // If there is no atom for target, we still need to check for special, atom-less
410 // symbols such as `___dso_handle`.
411 const target_name = zld.getSymbolName(target);
412 const atomless_sym = zld.getSymbol(target);
413 log.debug(" | atomless target '{s}'", .{target_name});
414 return atomless_sym.n_value;
415 };
416 const target_atom = zld.getAtom(target_atom_index);
417 log.debug(" | target ATOM(%{d}, '{s}') in object({?})", .{
418 target_atom.sym_index,
419 zld.getSymbolName(target_atom.getSymbolWithLoc()),
420 target_atom.file,
421 });
422 // If `target` is contained within the target atom, pull its address value.
423 const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());
424 const offset = if (target_atom.getFile() != null) blk: {
425 const object = zld.objects.items[target_atom.getFile().?];
426 break :blk if (object.getSourceSymbol(target.sym_index)) |_|
427 Atom.calcInnerSymbolOffset(zld, target_atom_index, target.sym_index)
428 else
429 0; // section alias
430
431 } else 0;
432 const base_address: u64 = if (is_tlv) base_address: {
433 // For TLV relocations, the value specified as a relocation is the displacement from the
434 // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
435 // defined TLV template init section in the following order:
436 // * wrt to __thread_data if defined, then
437 // * wrt to __thread_bss
438 const sect_id: u16 = sect_id: {
439 if (zld.getSectionByName("__DATA", "__thread_data")) |i| {
440 break :sect_id i;
441 } else if (zld.getSectionByName("__DATA", "__thread_bss")) |i| {
442 break :sect_id i;
443 } else {
444 log.err("threadlocal variables present but no initializer sections found", .{});
445 log.err(" __thread_data not found", .{});
446 log.err(" __thread_bss not found", .{});
447 return error.FailedToResolveRelocationTarget;
448 }
449 };
450 break :base_address zld.sections.items(.header)[sect_id].addr;
451 } else 0;
452 return target_sym.n_value + offset - base_address;
453}
454
455fn resolveRelocsArm64(
456 zld: *Zld,
457 atom_index: AtomIndex,
458 atom_code: []u8,
459 atom_relocs: []align(1) const macho.relocation_info,
460 reverse_lookup: []u32,
461 context: RelocContext,
462) !void {
463 const atom = zld.getAtom(atom_index);
464 const object = zld.objects.items[atom.getFile().?];
465
466 var addend: ?i64 = null;
467 var subtractor: ?SymbolWithLoc = null;
468
469 for (atom_relocs) |rel| {
470 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
471
472 switch (rel_type) {
473 .ARM64_RELOC_ADDEND => {
474 assert(addend == null);
475
476 log.debug(" RELA({s}) @ {x} => {x}", .{ @tagName(rel_type), rel.r_address, rel.r_symbolnum });
477
478 addend = rel.r_symbolnum;
479 continue;
480 },
481 .ARM64_RELOC_SUBTRACTOR => {
482 assert(subtractor == null);
483
484 log.debug(" RELA({s}) @ {x} => %{d} in object({d})", .{
485 @tagName(rel_type),
486 rel.r_address,
487 rel.r_symbolnum,
488 atom.file,
489 });
490
491 const sym_loc = SymbolWithLoc{
492 .sym_index = rel.r_symbolnum,
493 .file = atom.file,
494 };
495 const sym = zld.getSymbol(sym_loc);
496 assert(sym.sect());
497 subtractor = sym_loc;
498 continue;
499 },
500 else => {},
501 }
502
503 const target = try parseRelocTarget(zld, atom_index, rel, reverse_lookup);
504 const rel_offset = @intCast(u32, rel.r_address - context.base_offset);
505
506 log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{
507 @tagName(rel_type),
508 rel.r_address,
509 target.sym_index,
510 zld.getSymbolName(target),
511 target.file,
512 });
513
514 const source_addr = blk: {
515 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
516 break :blk source_sym.n_value + rel_offset;
517 };
518 const is_tlv = is_tlv: {
519 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
520 const header = zld.sections.items(.header)[source_sym.n_sect - 1];
521 break :is_tlv header.@"type"() == macho.S_THREAD_LOCAL_VARIABLES;
522 };
523 const target_addr = try getRelocTargetAddress(zld, rel, target, is_tlv);
524
525 log.debug(" | source_addr = 0x{x}", .{source_addr});
526
527 switch (rel_type) {
528 .ARM64_RELOC_BRANCH26 => {
529 const actual_target = if (zld.getStubsAtomIndexForSymbol(target)) |stub_atom_index| inner: {
530 const stub_atom = zld.getAtom(stub_atom_index);
531 break :inner stub_atom.getSymbolWithLoc();
532 } else target;
533 log.debug(" source {s} (object({?})), target {s} (object({?}))", .{
534 zld.getSymbolName(atom.getSymbolWithLoc()),
535 atom.file,
536 zld.getSymbolName(target),
537 zld.getAtom(getRelocTargetAtomIndex(zld, rel, target).?).file,
538 });
539
540 const displacement = if (calcPcRelativeDisplacementArm64(
541 source_addr,
542 zld.getSymbol(actual_target).n_value,
543 )) |disp| blk: {
544 log.debug(" | target_addr = 0x{x}", .{zld.getSymbol(actual_target).n_value});
545 break :blk disp;
546 } else |_| blk: {
547 const thunk_index = zld.thunk_table.get(atom_index).?;
548 const thunk = zld.thunks.items[thunk_index];
549 const thunk_sym = zld.getSymbol(thunk.getTrampolineForSymbol(
550 zld,
551 actual_target,
552 ).?);
553 log.debug(" | target_addr = 0x{x}", .{thunk_sym.n_value});
554 break :blk try calcPcRelativeDisplacementArm64(source_addr, thunk_sym.n_value);
555 };
556
557 const code = atom_code[rel_offset..][0..4];
558 var inst = aarch64.Instruction{
559 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
560 aarch64.Instruction,
561 aarch64.Instruction.unconditional_branch_immediate,
562 ), code),
563 };
564 inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));
565 mem.writeIntLittle(u32, code, inst.toU32());
566 },
567
568 .ARM64_RELOC_PAGE21,
569 .ARM64_RELOC_GOT_LOAD_PAGE21,
570 .ARM64_RELOC_TLVP_LOAD_PAGE21,
571 => {
572 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + (addend orelse 0));
573
574 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
575
576 const pages = @bitCast(u21, calcNumberOfPages(source_addr, adjusted_target_addr));
577 const code = atom_code[rel_offset..][0..4];
578 var inst = aarch64.Instruction{
579 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
580 aarch64.Instruction,
581 aarch64.Instruction.pc_relative_address,
582 ), code),
583 };
584 inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
585 inst.pc_relative_address.immlo = @truncate(u2, pages);
586 mem.writeIntLittle(u32, code, inst.toU32());
587 addend = null;
588 },
589
590 .ARM64_RELOC_PAGEOFF12 => {
591 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + (addend orelse 0));
592
593 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
594
595 const code = atom_code[rel_offset..][0..4];
596 if (isArithmeticOp(code)) {
597 const off = try calcPageOffset(adjusted_target_addr, .arithmetic);
598 var inst = aarch64.Instruction{
599 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
600 aarch64.Instruction,
601 aarch64.Instruction.add_subtract_immediate,
602 ), code),
603 };
604 inst.add_subtract_immediate.imm12 = off;
605 mem.writeIntLittle(u32, code, inst.toU32());
606 } else {
607 var inst = aarch64.Instruction{
608 .load_store_register = mem.bytesToValue(meta.TagPayload(
609 aarch64.Instruction,
610 aarch64.Instruction.load_store_register,
611 ), code),
612 };
613 const off = try calcPageOffset(adjusted_target_addr, switch (inst.load_store_register.size) {
614 0 => if (inst.load_store_register.v == 1)
615 PageOffsetInstKind.load_store_128
616 else
617 PageOffsetInstKind.load_store_8,
618 1 => .load_store_16,
619 2 => .load_store_32,
620 3 => .load_store_64,
621 });
622 inst.load_store_register.offset = off;
623 mem.writeIntLittle(u32, code, inst.toU32());
624 }
625 addend = null;
626 },
627
628 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {
629 const code = atom_code[rel_offset..][0..4];
630 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + (addend orelse 0));
631
632 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
633
634 const off = try calcPageOffset(adjusted_target_addr, .load_store_64);
635 var inst: aarch64.Instruction = .{
636 .load_store_register = mem.bytesToValue(meta.TagPayload(
637 aarch64.Instruction,
638 aarch64.Instruction.load_store_register,
639 ), code),
640 };
641 inst.load_store_register.offset = off;
642 mem.writeIntLittle(u32, code, inst.toU32());
643 addend = null;
644 },
645
646 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
647 const code = atom_code[rel_offset..][0..4];
648 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + (addend orelse 0));
649
650 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
651
652 const RegInfo = struct {
653 rd: u5,
654 rn: u5,
655 size: u2,
656 };
657 const reg_info: RegInfo = blk: {
658 if (isArithmeticOp(code)) {
659 const inst = mem.bytesToValue(meta.TagPayload(
660 aarch64.Instruction,
661 aarch64.Instruction.add_subtract_immediate,
662 ), code);
663 break :blk .{
664 .rd = inst.rd,
665 .rn = inst.rn,
666 .size = inst.sf,
667 };
668 } else {
669 const inst = mem.bytesToValue(meta.TagPayload(
670 aarch64.Instruction,
671 aarch64.Instruction.load_store_register,
672 ), code);
673 break :blk .{
674 .rd = inst.rt,
675 .rn = inst.rn,
676 .size = inst.size,
677 };
678 }
679 };
680
681 var inst = if (zld.tlv_ptr_table.contains(target)) aarch64.Instruction{
682 .load_store_register = .{
683 .rt = reg_info.rd,
684 .rn = reg_info.rn,
685 .offset = try calcPageOffset(adjusted_target_addr, .load_store_64),
686 .opc = 0b01,
687 .op1 = 0b01,
688 .v = 0,
689 .size = reg_info.size,
690 },
691 } else aarch64.Instruction{
692 .add_subtract_immediate = .{
693 .rd = reg_info.rd,
694 .rn = reg_info.rn,
695 .imm12 = try calcPageOffset(adjusted_target_addr, .arithmetic),
696 .sh = 0,
697 .s = 0,
698 .op = 0,
699 .sf = @truncate(u1, reg_info.size),
700 },
701 };
702 mem.writeIntLittle(u32, code, inst.toU32());
703 addend = null;
704 },
705
706 .ARM64_RELOC_POINTER_TO_GOT => {
707 log.debug(" | target_addr = 0x{x}", .{target_addr});
708 const result = math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr)) orelse
709 return error.Overflow;
710 mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @bitCast(u32, result));
711 },
712
713 .ARM64_RELOC_UNSIGNED => {
714 var ptr_addend = if (rel.r_length == 3)
715 mem.readIntLittle(i64, atom_code[rel_offset..][0..8])
716 else
717 mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
718
719 if (rel.r_extern == 0) {
720 const target_sect_base_addr = object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
721 ptr_addend -= @intCast(i64, target_sect_base_addr);
722 }
723
724 const result = blk: {
725 if (subtractor) |sub| {
726 const sym = zld.getSymbol(sub);
727 break :blk @intCast(i64, target_addr) - @intCast(i64, sym.n_value) + ptr_addend;
728 } else {
729 break :blk @intCast(i64, target_addr) + ptr_addend;
730 }
731 };
732 log.debug(" | target_addr = 0x{x}", .{result});
733
734 if (rel.r_length == 3) {
735 mem.writeIntLittle(u64, atom_code[rel_offset..][0..8], @bitCast(u64, result));
736 } else {
737 mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @truncate(u32, @bitCast(u64, result)));
738 }
739
740 subtractor = null;
741 },
742
743 .ARM64_RELOC_ADDEND => unreachable,
744 .ARM64_RELOC_SUBTRACTOR => unreachable,
745 }
746 }
747}
748
749fn resolveRelocsX86(
750 zld: *Zld,
751 atom_index: AtomIndex,
752 atom_code: []u8,
753 atom_relocs: []align(1) const macho.relocation_info,
754 reverse_lookup: []u32,
755 context: RelocContext,
756) !void {
757 const atom = zld.getAtom(atom_index);
758 const object = zld.objects.items[atom.getFile().?];
759
760 var subtractor: ?SymbolWithLoc = null;
761
762 for (atom_relocs) |rel| {
763 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
764
765 switch (rel_type) {
766 .X86_64_RELOC_SUBTRACTOR => {
767 assert(subtractor == null);
768
769 log.debug(" RELA({s}) @ {x} => %{d} in object({d})", .{
770 @tagName(rel_type),
771 rel.r_address,
772 rel.r_symbolnum,
773 atom.file,
774 });
775
776 const sym_loc = SymbolWithLoc{
777 .sym_index = rel.r_symbolnum,
778 .file = atom.file,
779 };
780 const sym = zld.getSymbol(sym_loc);
781 assert(sym.sect() and !sym.ext());
782 subtractor = sym_loc;
783 continue;
784 },
785 else => {},
786 }
787
788 const target = try parseRelocTarget(zld, atom_index, rel, reverse_lookup);
789 const rel_offset = @intCast(u32, rel.r_address - context.base_offset);
790
791 log.debug(" RELA({s}) @ {x} => %{d} in object({?})", .{
792 @tagName(rel_type),
793 rel.r_address,
794 target.sym_index,
795 target.file,
796 });
797
798 const source_addr = blk: {
799 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
800 break :blk source_sym.n_value + rel_offset;
801 };
802 const is_tlv = is_tlv: {
803 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
804 const header = zld.sections.items(.header)[source_sym.n_sect - 1];
805 break :is_tlv header.@"type"() == macho.S_THREAD_LOCAL_VARIABLES;
806 };
807 const target_addr = try getRelocTargetAddress(zld, rel, target, is_tlv);
808
809 log.debug(" | source_addr = 0x{x}", .{source_addr});
810
811 switch (rel_type) {
812 .X86_64_RELOC_BRANCH => {
813 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
814 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
815 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
816 const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
817 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
818 },
819
820 .X86_64_RELOC_GOT,
821 .X86_64_RELOC_GOT_LOAD,
822 => {
823 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
824 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
825 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
826 const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
827 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
828 },
829
830 .X86_64_RELOC_TLV => {
831 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
832 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
833 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
834 const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
835
836 // We need to rewrite the opcode from movq to leaq.
837 atom_code[rel_offset - 2] = 0x8d;
838
839 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
840 },
841
842 .X86_64_RELOC_SIGNED,
843 .X86_64_RELOC_SIGNED_1,
844 .X86_64_RELOC_SIGNED_2,
845 .X86_64_RELOC_SIGNED_4,
846 => {
847 const correction: u3 = switch (rel_type) {
848 .X86_64_RELOC_SIGNED => 0,
849 .X86_64_RELOC_SIGNED_1 => 1,
850 .X86_64_RELOC_SIGNED_2 => 2,
851 .X86_64_RELOC_SIGNED_4 => 4,
852 else => unreachable,
853 };
854 var addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]) + correction;
855
856 if (rel.r_extern == 0) {
857 // Note for the future self: when r_extern == 0, we should subtract correction from the
858 // addend.
859 const target_sect_base_addr = object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
860 // We need to add base_offset, i.e., offset of this atom wrt to the source
861 // section. Otherwise, the addend will over-/under-shoot.
862 addend += @intCast(i32, @intCast(i64, context.base_addr + rel_offset + 4) -
863 @intCast(i64, target_sect_base_addr) + context.base_offset);
864 }
865
866 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
867 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
868
869 const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, correction);
870 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
871 },
872
873 .X86_64_RELOC_UNSIGNED => {
874 var addend = if (rel.r_length == 3)
875 mem.readIntLittle(i64, atom_code[rel_offset..][0..8])
876 else
877 mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
878
879 if (rel.r_extern == 0) {
880 const target_sect_base_addr = object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
881 addend -= @intCast(i64, target_sect_base_addr);
882 }
883
884 const result = blk: {
885 if (subtractor) |sub| {
886 const sym = zld.getSymbol(sub);
887 break :blk @intCast(i64, target_addr) - @intCast(i64, sym.n_value) + addend;
888 } else {
889 break :blk @intCast(i64, target_addr) + addend;
890 }
891 };
892 log.debug(" | target_addr = 0x{x}", .{result});
893
894 if (rel.r_length == 3) {
895 mem.writeIntLittle(u64, atom_code[rel_offset..][0..8], @bitCast(u64, result));
896 } else {
897 mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @truncate(u32, @bitCast(u64, result)));
898 }
899
900 subtractor = null;
901 },
902
903 .X86_64_RELOC_SUBTRACTOR => unreachable,
904 }
905 }
906}
907
908inline fn isArithmeticOp(inst: *const [4]u8) bool {
909 const group_decode = @truncate(u5, inst[3]);
910 return ((group_decode >> 2) == 4);
911}
912
913pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {
914 const atom = zld.getAtom(atom_index);
915 assert(atom.getFile() != null); // Synthetic atom shouldn't need to inquire for code.
916 const object = zld.objects.items[atom.getFile().?];
917 const source_sym = object.getSourceSymbol(atom.sym_index) orelse {
918 // If there was no matching symbol present in the source symtab, this means
919 // we are dealing with either an entire section, or part of it, but also
920 // starting at the beginning.
921 const source_sect = for (object.getSourceSections()) |source_sect, sect_id| {
922 const sym_index = object.getSectionAliasSymbolIndex(@intCast(u8, sect_id));
923 if (sym_index == atom.sym_index) break source_sect;
924 } else unreachable;
925
926 assert(!source_sect.isZerofill());
927 const code = object.getSectionContents(source_sect);
928 return code[0..atom.size];
929 };
930 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
931 assert(!source_sect.isZerofill());
932 const offset = source_sym.n_value - source_sect.addr;
933 const code = object.getSectionContents(source_sect);
934 return code[offset..][0..atom.size];
935}
936
937pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []align(1) const macho.relocation_info {
938 const atom = zld.getAtomPtr(atom_index);
939 assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs.
940 const object = zld.objects.items[atom.getFile().?];
941
942 const source_sect = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
943 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
944 assert(!source_sect.isZerofill());
945 break :blk source_sect;
946 } else blk: {
947 // If there was no matching symbol present in the source symtab, this means
948 // we are dealing with either an entire section, or part of it, but also
949 // starting at the beginning.
950 const source_sect = for (object.getSourceSections()) |source_sect, sect_id| {
951 const sym_index = object.getSectionAliasSymbolIndex(@intCast(u8, sect_id));
952 if (sym_index == atom.sym_index) break source_sect;
953 } else unreachable;
954 assert(!source_sect.isZerofill());
955 break :blk source_sect;
956 };
957
958 const relocs = object.getRelocs(source_sect);
959
960 if (atom.cached_relocs_start == -1) {
961 const indexes = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
962 const offset = source_sym.n_value - source_sect.addr;
963 break :blk filterRelocs(relocs, offset, offset + atom.size);
964 } else filterRelocs(relocs, 0, atom.size);
965 atom.cached_relocs_start = indexes.start;
966 atom.cached_relocs_len = indexes.len;
967 }
968
969 return relocs[@intCast(u32, atom.cached_relocs_start)..][0..atom.cached_relocs_len];
970}
971
972fn filterRelocs(
973 relocs: []align(1) const macho.relocation_info,
974 start_addr: u64,
975 end_addr: u64,
976) struct { start: i32, len: u32 } {
977 const Predicate = struct {
978 addr: u64,
979
980 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
981 return rel.r_address >= self.addr;
982 }
983 };
984 const LPredicate = struct {
985 addr: u64,
986
987 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
988 return rel.r_address < self.addr;
989 }
990 };
991
992 const start = @import("zld.zig").bsearch(macho.relocation_info, relocs, Predicate{ .addr = end_addr });
993 const len = @import("zld.zig").lsearch(macho.relocation_info, relocs[start..], LPredicate{ .addr = start_addr });
994
995 return .{ .start = @intCast(i32, start), .len = @intCast(u32, len) };
996}
997
998pub fn calcPcRelativeDisplacementX86(source_addr: u64, target_addr: u64, correction: u3) error{Overflow}!i32 {
999 const disp = @intCast(i64, target_addr) - @intCast(i64, source_addr + 4 + correction);
1000 return math.cast(i32, disp) orelse error.Overflow;
1001}
1002
1003pub fn calcPcRelativeDisplacementArm64(source_addr: u64, target_addr: u64) error{Overflow}!i28 {
1004 const disp = @intCast(i64, target_addr) - @intCast(i64, source_addr);
1005 return math.cast(i28, disp) orelse error.Overflow;
1006}
1007
1008pub fn calcNumberOfPages(source_addr: u64, target_addr: u64) i21 {
1009 const source_page = @intCast(i32, source_addr >> 12);
1010 const target_page = @intCast(i32, target_addr >> 12);
1011 const pages = @intCast(i21, target_page - source_page);
1012 return pages;
1013}
1014
1015const PageOffsetInstKind = enum {
1016 arithmetic,
1017 load_store_8,
1018 load_store_16,
1019 load_store_32,
1020 load_store_64,
1021 load_store_128,
1022};
1023
1024pub fn calcPageOffset(target_addr: u64, kind: PageOffsetInstKind) !u12 {
1025 const narrowed = @truncate(u12, target_addr);
1026 return switch (kind) {
1027 .arithmetic, .load_store_8 => narrowed,
1028 .load_store_16 => try math.divExact(u12, narrowed, 2),
1029 .load_store_32 => try math.divExact(u12, narrowed, 4),
1030 .load_store_64 => try math.divExact(u12, narrowed, 8),
1031 .load_store_128 => try math.divExact(u12, narrowed, 16),
1032 };
1033}
src/link/MachO/dead_strip.zig+197-207
......@@ -6,89 +6,78 @@ const math = std.math;
66const mem = std.mem;
77
88const Allocator = mem.Allocator;
9const Atom = @import("Atom.zig");
10const MachO = @import("../MachO.zig");
9const AtomIndex = @import("zld.zig").AtomIndex;
10const Atom = @import("ZldAtom.zig");
11const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
12const Zld = @import("zld.zig").Zld;
1113
12pub fn gcAtoms(macho_file: *MachO) !void {
13 const gpa = macho_file.base.allocator;
14 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
15 defer arena_allocator.deinit();
16 const arena = arena_allocator.allocator();
14const N_DEAD = @import("zld.zig").N_DEAD;
1715
18 var roots = std.AutoHashMap(*Atom, void).init(arena);
19 try collectRoots(&roots, macho_file);
16const AtomTable = std.AutoHashMap(AtomIndex, void);
2017
21 var alive = std.AutoHashMap(*Atom, void).init(arena);
22 try mark(roots, &alive, macho_file);
18pub fn gcAtoms(zld: *Zld, reverse_lookups: [][]u32) !void {
19 const gpa = zld.gpa;
2320
24 try prune(arena, alive, macho_file);
25}
26
27fn removeAtomFromSection(atom: *Atom, match: u8, macho_file: *MachO) void {
28 var section = macho_file.sections.get(match);
21 var arena = std.heap.ArenaAllocator.init(gpa);
22 defer arena.deinit();
2923
30 // If we want to enable GC for incremental codepath, we need to take into
31 // account any padding that might have been left here.
32 section.header.size -= atom.size;
24 var roots = AtomTable.init(arena.allocator());
25 try roots.ensureUnusedCapacity(@intCast(u32, zld.globals.items.len));
3326
34 if (atom.prev) |prev| {
35 prev.next = atom.next;
36 }
37 if (atom.next) |next| {
38 next.prev = atom.prev;
39 } else {
40 if (atom.prev) |prev| {
41 section.last_atom = prev;
42 } else {
43 // The section will be GCed in the next step.
44 section.last_atom = null;
45 section.header.size = 0;
46 }
47 }
27 var alive = AtomTable.init(arena.allocator());
28 try alive.ensureTotalCapacity(@intCast(u32, zld.atoms.items.len));
4829
49 macho_file.sections.set(match, section);
30 try collectRoots(zld, &roots);
31 try mark(zld, roots, &alive, reverse_lookups);
32 try prune(zld, alive);
5033}
5134
52fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {
53 const output_mode = macho_file.base.options.output_mode;
35fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
36 log.debug("collecting roots", .{});
5437
55 switch (output_mode) {
38 switch (zld.options.output_mode) {
5639 .Exe => {
5740 // Add entrypoint as GC root
58 const global = try macho_file.getEntryPoint();
59 const atom = macho_file.getAtomForSymbol(global).?; // panic here means fatal error
60 _ = try roots.getOrPut(atom);
41 const global: SymbolWithLoc = zld.getEntryPoint();
42 const object = zld.objects.items[global.getFile().?];
43 const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error
44 _ = try roots.getOrPut(atom_index);
45 log.debug("adding root", .{});
46 zld.logAtom(atom_index, log);
6147 },
6248 else => |other| {
6349 assert(other == .Lib);
6450 // Add exports as GC roots
65 for (macho_file.globals.items) |global| {
66 const sym = macho_file.getSymbol(global);
67 if (!sym.sect()) continue;
68 const atom = macho_file.getAtomForSymbol(global) orelse {
69 log.debug("skipping {s}", .{macho_file.getSymbolName(global)});
70 continue;
71 };
72 _ = try roots.getOrPut(atom);
51 for (zld.globals.items) |global| {
52 const sym = zld.getSymbol(global);
53 if (sym.undf()) continue;
54
55 const object = zld.objects.items[global.getFile().?];
56 const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error
57 _ = try roots.getOrPut(atom_index);
7358 log.debug("adding root", .{});
74 macho_file.logAtom(atom);
59 zld.logAtom(atom_index, log);
7560 }
7661 },
7762 }
7863
7964 // TODO just a temp until we learn how to parse unwind records
80 if (macho_file.getGlobal("___gxx_personality_v0")) |global| {
81 if (macho_file.getAtomForSymbol(global)) |atom| {
82 _ = try roots.getOrPut(atom);
83 log.debug("adding root", .{});
84 macho_file.logAtom(atom);
65 for (zld.globals.items) |global| {
66 if (mem.eql(u8, "___gxx_personality_v0", zld.getSymbolName(global))) {
67 const object = zld.objects.items[global.getFile().?];
68 if (object.getAtomIndexForSymbol(global.sym_index)) |atom_index| {
69 _ = try roots.getOrPut(atom_index);
70 log.debug("adding root", .{});
71 zld.logAtom(atom_index, log);
72 }
73 break;
8574 }
8675 }
8776
88 for (macho_file.objects.items) |object| {
89 for (object.managed_atoms.items) |atom| {
77 for (zld.objects.items) |object| {
78 for (object.atoms.items) |atom_index| {
79 const atom = zld.getAtom(atom_index);
9080 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
91 if (source_sym.tentative()) continue;
9281 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
9382 const is_gc_root = blk: {
9483 if (source_sect.isDontDeadStrip()) break :blk true;
......@@ -101,196 +90,197 @@ fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void
10190 }
10291 };
10392 if (is_gc_root) {
104 try roots.putNoClobber(atom, {});
93 try roots.putNoClobber(atom_index, {});
10594 log.debug("adding root", .{});
106 macho_file.logAtom(atom);
95 zld.logAtom(atom_index, log);
10796 }
10897 }
10998 }
11099}
111100
112fn markLive(atom: *Atom, alive: *std.AutoHashMap(*Atom, void), macho_file: *MachO) anyerror!void {
113 const gop = try alive.getOrPut(atom);
114 if (gop.found_existing) return;
101fn markLive(
102 zld: *Zld,
103 atom_index: AtomIndex,
104 alive: *AtomTable,
105 reverse_lookups: [][]u32,
106) anyerror!void {
107 if (alive.contains(atom_index)) return;
108
109 alive.putAssumeCapacityNoClobber(atom_index, {});
115110
116111 log.debug("marking live", .{});
117 macho_file.logAtom(atom);
112 zld.logAtom(atom_index, log);
113
114 const cpu_arch = zld.options.target.cpu.arch;
115
116 const atom = zld.getAtom(atom_index);
117 const sym = zld.getSymbol(atom.getSymbolWithLoc());
118 const header = zld.sections.items(.header)[sym.n_sect - 1];
119 if (header.isZerofill()) return;
120
121 const relocs = Atom.getAtomRelocs(zld, atom_index);
122 const reverse_lookup = reverse_lookups[atom.getFile().?];
123 for (relocs) |rel| {
124 switch (cpu_arch) {
125 .aarch64 => {
126 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
127 switch (rel_type) {
128 .ARM64_RELOC_ADDEND, .ARM64_RELOC_SUBTRACTOR => continue,
129 else => {},
130 }
131 },
132 .x86_64 => {
133 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
134 switch (rel_type) {
135 .X86_64_RELOC_SUBTRACTOR => continue,
136 else => {},
137 }
138 },
139 else => unreachable,
140 }
118141
119 for (atom.relocs.items) |rel| {
120 const target_atom = rel.getTargetAtom(macho_file) orelse continue;
121 try markLive(target_atom, alive, macho_file);
122 }
123}
142 const target = try Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup);
143 const target_sym = zld.getSymbol(target);
144 if (target_sym.undf()) continue;
145 if (target.getFile() == null) {
146 const target_sym_name = zld.getSymbolName(target);
147 if (mem.eql(u8, "__mh_execute_header", target_sym_name)) continue;
148 if (mem.eql(u8, "___dso_handle", target_sym_name)) continue;
124149
125fn refersLive(atom: *Atom, alive: std.AutoHashMap(*Atom, void), macho_file: *MachO) bool {
126 for (atom.relocs.items) |rel| {
127 const target_atom = rel.getTargetAtom(macho_file) orelse continue;
128 if (alive.contains(target_atom)) return true;
150 unreachable; // referenced symbol not found
151 }
152
153 const object = zld.objects.items[target.getFile().?];
154 const target_atom_index = object.getAtomIndexForSymbol(target.sym_index).?;
155 try markLive(zld, target_atom_index, alive, reverse_lookups);
129156 }
130 return false;
131157}
132158
133fn refersDead(atom: *Atom, macho_file: *MachO) bool {
134 for (atom.relocs.items) |rel| {
135 const target_atom = rel.getTargetAtom(macho_file) orelse continue;
136 const target_sym = target_atom.getSymbol(macho_file);
137 if (target_sym.n_desc == MachO.N_DESC_GCED) return true;
159fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable, reverse_lookups: [][]u32) !bool {
160 const cpu_arch = zld.options.target.cpu.arch;
161
162 const atom = zld.getAtom(atom_index);
163 const sym = zld.getSymbol(atom.getSymbolWithLoc());
164 const header = zld.sections.items(.header)[sym.n_sect - 1];
165 if (header.isZerofill()) return false;
166
167 const relocs = Atom.getAtomRelocs(zld, atom_index);
168 const reverse_lookup = reverse_lookups[atom.getFile().?];
169 for (relocs) |rel| {
170 switch (cpu_arch) {
171 .aarch64 => {
172 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
173 switch (rel_type) {
174 .ARM64_RELOC_ADDEND, .ARM64_RELOC_SUBTRACTOR => continue,
175 else => {},
176 }
177 },
178 .x86_64 => {
179 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
180 switch (rel_type) {
181 .X86_64_RELOC_SUBTRACTOR => continue,
182 else => {},
183 }
184 },
185 else => unreachable,
186 }
187
188 const target = try Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup);
189 const object = zld.objects.items[target.getFile().?];
190 const target_atom_index = object.getAtomIndexForSymbol(target.sym_index) orelse {
191 log.debug("atom for symbol '{s}' not found; skipping...", .{zld.getSymbolName(target)});
192 continue;
193 };
194 if (alive.contains(target_atom_index)) return true;
138195 }
196
139197 return false;
140198}
141199
142fn mark(
143 roots: std.AutoHashMap(*Atom, void),
144 alive: *std.AutoHashMap(*Atom, void),
145 macho_file: *MachO,
146) !void {
147 try alive.ensureUnusedCapacity(roots.count());
148
200fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable, reverse_lookups: [][]u32) !void {
149201 var it = roots.keyIterator();
150202 while (it.next()) |root| {
151 try markLive(root.*, alive, macho_file);
203 try markLive(zld, root.*, alive, reverse_lookups);
152204 }
153205
154206 var loop: bool = true;
155207 while (loop) {
156208 loop = false;
157209
158 for (macho_file.objects.items) |object| {
159 for (object.managed_atoms.items) |atom| {
160 if (alive.contains(atom)) continue;
210 for (zld.objects.items) |object| {
211 for (object.atoms.items) |atom_index| {
212 if (alive.contains(atom_index)) continue;
213
214 const atom = zld.getAtom(atom_index);
161215 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
162 if (source_sym.tentative()) continue;
163216 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
164 if (source_sect.isDontDeadStripIfReferencesLive() and refersLive(atom, alive.*, macho_file)) {
165 try markLive(atom, alive, macho_file);
166 loop = true;
217
218 if (source_sect.isDontDeadStripIfReferencesLive()) {
219 if (try refersLive(zld, atom_index, alive.*, reverse_lookups)) {
220 try markLive(zld, atom_index, alive, reverse_lookups);
221 loop = true;
222 }
167223 }
168224 }
169225 }
170226 }
171227}
172228
173fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {
174 // Any section that ends up here will be updated, that is,
175 // its size and alignment recalculated.
176 var gc_sections = std.AutoHashMap(u8, void).init(arena);
177 var loop: bool = true;
178 while (loop) {
179 loop = false;
180
181 for (macho_file.objects.items) |object| {
182 const in_symtab = object.in_symtab orelse continue;
183
184 for (in_symtab) |_, source_index| {
185 const atom = object.getAtomForSymbol(@intCast(u32, source_index)) orelse continue;
186 if (alive.contains(atom)) continue;
187
188 const global = atom.getSymbolWithLoc();
189 const sym = atom.getSymbolPtr(macho_file);
190 const match = sym.n_sect - 1;
191
192 if (sym.n_desc == MachO.N_DESC_GCED) continue;
193 if (!sym.ext() and !refersDead(atom, macho_file)) continue;
194
195 macho_file.logAtom(atom);
196 sym.n_desc = MachO.N_DESC_GCED;
197 removeAtomFromSection(atom, match, macho_file);
198 _ = try gc_sections.put(match, {});
199
200 for (atom.contained.items) |sym_off| {
201 const inner = macho_file.getSymbolPtr(.{
202 .sym_index = sym_off.sym_index,
203 .file = atom.file,
204 });
205 inner.n_desc = MachO.N_DESC_GCED;
206 }
207
208 if (macho_file.got_entries_table.contains(global)) {
209 const got_atom = macho_file.getGotAtomForSymbol(global).?;
210 const got_sym = got_atom.getSymbolPtr(macho_file);
211 got_sym.n_desc = MachO.N_DESC_GCED;
212 }
229fn prune(zld: *Zld, alive: AtomTable) !void {
230 log.debug("pruning dead atoms", .{});
231 for (zld.objects.items) |*object| {
232 var i: usize = 0;
233 while (i < object.atoms.items.len) {
234 const atom_index = object.atoms.items[i];
235 if (alive.contains(atom_index)) {
236 i += 1;
237 continue;
238 }
213239
214 if (macho_file.stubs_table.contains(global)) {
215 const stubs_atom = macho_file.getStubsAtomForSymbol(global).?;
216 const stubs_sym = stubs_atom.getSymbolPtr(macho_file);
217 stubs_sym.n_desc = MachO.N_DESC_GCED;
240 zld.logAtom(atom_index, log);
241
242 const atom = zld.getAtom(atom_index);
243 const sym_loc = atom.getSymbolWithLoc();
244 const sym = zld.getSymbolPtr(sym_loc);
245 const sect_id = sym.n_sect - 1;
246 var section = zld.sections.get(sect_id);
247 section.header.size -= atom.size;
248
249 if (atom.prev_index) |prev_index| {
250 const prev = zld.getAtomPtr(prev_index);
251 prev.next_index = atom.next_index;
252 } else {
253 if (atom.next_index) |next_index| {
254 section.first_atom_index = next_index;
218255 }
219
220 if (macho_file.tlv_ptr_entries_table.contains(global)) {
221 const tlv_ptr_atom = macho_file.getTlvPtrAtomForSymbol(global).?;
222 const tlv_ptr_sym = tlv_ptr_atom.getSymbolPtr(macho_file);
223 tlv_ptr_sym.n_desc = MachO.N_DESC_GCED;
256 }
257 if (atom.next_index) |next_index| {
258 const next = zld.getAtomPtr(next_index);
259 next.prev_index = atom.prev_index;
260 } else {
261 if (atom.prev_index) |prev_index| {
262 section.last_atom_index = prev_index;
263 } else {
264 assert(section.header.size == 0);
265 section.first_atom_index = undefined;
266 section.last_atom_index = undefined;
224267 }
225
226 loop = true;
227268 }
228 }
229 }
230269
231 for (macho_file.got_entries.items) |entry| {
232 const sym = entry.getSymbol(macho_file);
233 if (sym.n_desc != MachO.N_DESC_GCED) continue;
234
235 // TODO tombstone
236 const atom = entry.getAtom(macho_file).?;
237 const match = sym.n_sect - 1;
238 removeAtomFromSection(atom, match, macho_file);
239 _ = try gc_sections.put(match, {});
240 _ = macho_file.got_entries_table.remove(entry.target);
241 }
270 zld.sections.set(sect_id, section);
271 _ = object.atoms.swapRemove(i);
242272
243 for (macho_file.stubs.items) |entry| {
244 const sym = entry.getSymbol(macho_file);
245 if (sym.n_desc != MachO.N_DESC_GCED) continue;
246
247 // TODO tombstone
248 const atom = entry.getAtom(macho_file).?;
249 const match = sym.n_sect - 1;
250 removeAtomFromSection(atom, match, macho_file);
251 _ = try gc_sections.put(match, {});
252 _ = macho_file.stubs_table.remove(entry.target);
253 }
254
255 for (macho_file.tlv_ptr_entries.items) |entry| {
256 const sym = entry.getSymbol(macho_file);
257 if (sym.n_desc != MachO.N_DESC_GCED) continue;
258
259 // TODO tombstone
260 const atom = entry.getAtom(macho_file).?;
261 const match = sym.n_sect - 1;
262 removeAtomFromSection(atom, match, macho_file);
263 _ = try gc_sections.put(match, {});
264 _ = macho_file.tlv_ptr_entries_table.remove(entry.target);
265 }
266
267 var gc_sections_it = gc_sections.iterator();
268 while (gc_sections_it.next()) |entry| {
269 const match = entry.key_ptr.*;
270 var section = macho_file.sections.get(match);
271 if (section.header.size == 0) continue; // Pruning happens automatically in next step.
272
273 section.header.@"align" = 0;
274 section.header.size = 0;
275
276 var atom = section.last_atom.?;
277
278 while (atom.prev) |prev| {
279 atom = prev;
280 }
281
282 while (true) {
283 const atom_alignment = try math.powi(u32, 2, atom.alignment);
284 const aligned_end_addr = mem.alignForwardGeneric(u64, section.header.size, atom_alignment);
285 const padding = aligned_end_addr - section.header.size;
286 section.header.size += padding + atom.size;
287 section.header.@"align" = @max(section.header.@"align", atom.alignment);
273 if (sym.ext()) {
274 sym.n_desc = N_DEAD;
275 }
288276
289 if (atom.next) |next| {
290 atom = next;
291 } else break;
277 var inner_sym_it = Atom.getInnerSymbolsIterator(zld, atom_index);
278 while (inner_sym_it.next()) |inner| {
279 const inner_sym = zld.getSymbolPtr(inner);
280 if (inner_sym.ext()) {
281 inner_sym.n_desc = N_DEAD;
282 }
283 }
292284 }
293
294 macho_file.sections.set(match, section);
295285 }
296286}
src/link/MachO/thunks.zig created+357
......@@ -0,0 +1,357 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const log = std.log.scoped(.thunks);
4const macho = std.macho;
5const math = std.math;
6const mem = std.mem;
7
8const aarch64 = @import("../../arch/aarch64/bits.zig");
9
10const Allocator = mem.Allocator;
11const Atom = @import("ZldAtom.zig");
12const AtomIndex = @import("zld.zig").AtomIndex;
13const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
14const Zld = @import("zld.zig").Zld;
15
16pub const ThunkIndex = u32;
17
18/// Branch instruction has 26 bits immediate but 4 byte aligned.
19const jump_bits = @bitSizeOf(i28);
20
21const max_distance = (1 << (jump_bits - 1));
22
23/// A branch will need an extender if its target is larger than
24/// `2^(jump_bits - 1) - margin` where margin is some arbitrary number.
25/// mold uses 5MiB margin, while ld64 uses 4MiB margin. We will follow mold
26/// and assume margin to be 5MiB.
27const max_allowed_distance = max_distance - 0x500_000;
28
29pub const Thunk = struct {
30 start_index: AtomIndex,
31 len: u32,
32
33 lookup: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, AtomIndex) = .{},
34
35 pub fn deinit(self: *Thunk, gpa: Allocator) void {
36 self.lookup.deinit(gpa);
37 }
38
39 pub fn getStartAtomIndex(self: Thunk) AtomIndex {
40 assert(self.len != 0);
41 return self.start_index;
42 }
43
44 pub fn getEndAtomIndex(self: Thunk) AtomIndex {
45 assert(self.len != 0);
46 return self.start_index + self.len - 1;
47 }
48
49 pub fn getSize(self: Thunk) u64 {
50 return 12 * self.len;
51 }
52
53 pub fn getAlignment() u32 {
54 return @alignOf(u32);
55 }
56
57 pub fn getTrampolineForSymbol(self: Thunk, zld: *Zld, target: SymbolWithLoc) ?SymbolWithLoc {
58 const atom_index = self.lookup.get(target) orelse return null;
59 const atom = zld.getAtom(atom_index);
60 return atom.getSymbolWithLoc();
61 }
62};
63
64pub fn createThunks(zld: *Zld, sect_id: u8, reverse_lookups: [][]u32) !void {
65 const header = &zld.sections.items(.header)[sect_id];
66 if (header.size == 0) return;
67
68 const gpa = zld.gpa;
69 const first_atom_index = zld.sections.items(.first_atom_index)[sect_id];
70
71 header.size = 0;
72 header.@"align" = 0;
73
74 var atom_count: u32 = 0;
75
76 {
77 var atom_index = first_atom_index;
78 while (true) {
79 const atom = zld.getAtom(atom_index);
80 const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());
81 sym.n_value = 0;
82 atom_count += 1;
83
84 if (atom.next_index) |next_index| {
85 atom_index = next_index;
86 } else break;
87 }
88 }
89
90 var allocated = std.AutoHashMap(AtomIndex, void).init(gpa);
91 defer allocated.deinit();
92 try allocated.ensureTotalCapacity(atom_count);
93
94 var group_start = first_atom_index;
95 var group_end = first_atom_index;
96 var offset: u64 = 0;
97
98 while (true) {
99 const group_start_atom = zld.getAtom(group_start);
100 log.debug("GROUP START at {d}", .{group_start});
101
102 while (true) {
103 const atom = zld.getAtom(group_end);
104 offset = mem.alignForwardGeneric(u64, offset, try math.powi(u32, 2, atom.alignment));
105
106 const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());
107 sym.n_value = offset;
108 offset += atom.size;
109
110 zld.logAtom(group_end, log);
111
112 header.@"align" = @max(header.@"align", atom.alignment);
113
114 allocated.putAssumeCapacityNoClobber(group_end, {});
115
116 const group_start_sym = zld.getSymbol(group_start_atom.getSymbolWithLoc());
117 if (offset - group_start_sym.n_value >= max_allowed_distance) break;
118
119 if (atom.next_index) |next_index| {
120 group_end = next_index;
121 } else break;
122 }
123 log.debug("GROUP END at {d}", .{group_end});
124
125 // Insert thunk at group_end
126 const thunk_index = @intCast(u32, zld.thunks.items.len);
127 try zld.thunks.append(gpa, .{ .start_index = undefined, .len = 0 });
128
129 // Scan relocs in the group and create trampolines for any unreachable callsite.
130 var atom_index = group_start;
131 while (true) {
132 const atom = zld.getAtom(atom_index);
133 try scanRelocs(
134 zld,
135 atom_index,
136 reverse_lookups[atom.getFile().?],
137 allocated,
138 thunk_index,
139 group_end,
140 );
141
142 if (atom_index == group_end) break;
143
144 if (atom.next_index) |next_index| {
145 atom_index = next_index;
146 } else break;
147 }
148
149 offset = mem.alignForwardGeneric(u64, offset, Thunk.getAlignment());
150 allocateThunk(zld, thunk_index, offset, header);
151 offset += zld.thunks.items[thunk_index].getSize();
152
153 const thunk = zld.thunks.items[thunk_index];
154 if (thunk.len == 0) {
155 const group_end_atom = zld.getAtom(group_end);
156 if (group_end_atom.next_index) |next_index| {
157 group_start = next_index;
158 group_end = next_index;
159 } else break;
160 } else {
161 const thunk_end_atom_index = thunk.getEndAtomIndex();
162 const thunk_end_atom = zld.getAtom(thunk_end_atom_index);
163 if (thunk_end_atom.next_index) |next_index| {
164 group_start = next_index;
165 group_end = next_index;
166 } else break;
167 }
168 }
169
170 header.size = @intCast(u32, offset);
171}
172
173fn allocateThunk(
174 zld: *Zld,
175 thunk_index: ThunkIndex,
176 base_offset: u64,
177 header: *macho.section_64,
178) void {
179 const thunk = zld.thunks.items[thunk_index];
180 if (thunk.len == 0) return;
181
182 const first_atom_index = thunk.getStartAtomIndex();
183 const end_atom_index = thunk.getEndAtomIndex();
184
185 var atom_index = first_atom_index;
186 var offset = base_offset;
187 while (true) {
188 const atom = zld.getAtom(atom_index);
189 offset = mem.alignForwardGeneric(u64, offset, Thunk.getAlignment());
190
191 const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());
192 sym.n_value = offset;
193 offset += atom.size;
194
195 zld.logAtom(atom_index, log);
196
197 header.@"align" = @max(header.@"align", atom.alignment);
198
199 if (end_atom_index == atom_index) break;
200
201 if (atom.next_index) |next_index| {
202 atom_index = next_index;
203 } else break;
204 }
205}
206
207fn scanRelocs(
208 zld: *Zld,
209 atom_index: AtomIndex,
210 reverse_lookup: []u32,
211 allocated: std.AutoHashMap(AtomIndex, void),
212 thunk_index: ThunkIndex,
213 group_end: AtomIndex,
214) !void {
215 const atom = zld.getAtom(atom_index);
216 const object = zld.objects.items[atom.getFile().?];
217
218 const base_offset = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
219 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
220 break :blk @intCast(i32, source_sym.n_value - source_sect.addr);
221 } else 0;
222
223 const relocs = Atom.getAtomRelocs(zld, atom_index);
224 for (relocs) |rel| {
225 if (!relocNeedsThunk(rel)) continue;
226
227 const target = Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup) catch unreachable;
228 if (isReachable(zld, atom_index, rel, base_offset, target, allocated)) continue;
229
230 log.debug("{x}: source = {s}@{x}, target = {s}@{x} unreachable", .{
231 rel.r_address - base_offset,
232 zld.getSymbolName(atom.getSymbolWithLoc()),
233 zld.getSymbol(atom.getSymbolWithLoc()).n_value,
234 zld.getSymbolName(target),
235 zld.getSymbol(target).n_value,
236 });
237
238 const gpa = zld.gpa;
239 const target_sym = zld.getSymbol(target);
240
241 const actual_target: SymbolWithLoc = if (target_sym.undf()) blk: {
242 const stub_atom_index = zld.getStubsAtomIndexForSymbol(target).?;
243 break :blk .{ .sym_index = zld.getAtom(stub_atom_index).sym_index };
244 } else target;
245
246 const thunk = &zld.thunks.items[thunk_index];
247 const gop = try thunk.lookup.getOrPut(gpa, actual_target);
248 if (!gop.found_existing) {
249 const thunk_atom_index = try createThunkAtom(zld);
250 gop.value_ptr.* = thunk_atom_index;
251
252 const thunk_atom = zld.getAtomPtr(thunk_atom_index);
253 const end_atom_index = if (thunk.len == 0) group_end else thunk.getEndAtomIndex();
254 const end_atom = zld.getAtomPtr(end_atom_index);
255
256 if (end_atom.next_index) |first_after_index| {
257 const first_after_atom = zld.getAtomPtr(first_after_index);
258 first_after_atom.prev_index = thunk_atom_index;
259 thunk_atom.next_index = first_after_index;
260 }
261
262 end_atom.next_index = thunk_atom_index;
263 thunk_atom.prev_index = end_atom_index;
264
265 if (thunk.len == 0) {
266 thunk.start_index = thunk_atom_index;
267 }
268
269 thunk.len += 1;
270 }
271
272 try zld.thunk_table.put(gpa, atom_index, thunk_index);
273 }
274}
275
276inline fn relocNeedsThunk(rel: macho.relocation_info) bool {
277 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
278 return rel_type == .ARM64_RELOC_BRANCH26;
279}
280
281fn isReachable(
282 zld: *Zld,
283 atom_index: AtomIndex,
284 rel: macho.relocation_info,
285 base_offset: i32,
286 target: SymbolWithLoc,
287 allocated: std.AutoHashMap(AtomIndex, void),
288) bool {
289 if (zld.getStubsAtomIndexForSymbol(target)) |_| return false;
290
291 const source_atom = zld.getAtom(atom_index);
292 const source_sym = zld.getSymbol(source_atom.getSymbolWithLoc());
293
294 const target_object = zld.objects.items[target.getFile().?];
295 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
296 const target_atom = zld.getAtom(target_atom_index);
297 const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());
298
299 if (source_sym.n_sect != target_sym.n_sect) return false;
300
301 if (!allocated.contains(target_atom_index)) return false;
302
303 const source_addr = source_sym.n_value + @intCast(u32, rel.r_address - base_offset);
304 const target_addr = Atom.getRelocTargetAddress(zld, rel, target, false) catch unreachable;
305 _ = Atom.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch
306 return false;
307
308 return true;
309}
310
311fn createThunkAtom(zld: *Zld) !AtomIndex {
312 const sym_index = try zld.allocateSymbol();
313 const atom_index = try zld.createEmptyAtom(sym_index, @sizeOf(u32) * 3, 2);
314 const sym = zld.getSymbolPtr(.{ .sym_index = sym_index });
315 sym.n_type = macho.N_SECT;
316
317 const sect_id = zld.getSectionByName("__TEXT", "__text") orelse unreachable;
318 sym.n_sect = sect_id + 1;
319
320 return atom_index;
321}
322
323fn getThunkIndex(zld: *Zld, atom_index: AtomIndex) ?ThunkIndex {
324 const atom = zld.getAtom(atom_index);
325 const sym = zld.getSymbol(atom.getSymbolWithLoc());
326 for (zld.thunks.items) |thunk, i| {
327 if (thunk.len == 0) continue;
328
329 const thunk_atom_index = thunk.getStartAtomIndex();
330 const thunk_atom = zld.getAtom(thunk_atom_index);
331 const thunk_sym = zld.getSymbol(thunk_atom.getSymbolWithLoc());
332 const start_addr = thunk_sym.n_value;
333 const end_addr = start_addr + thunk.getSize();
334
335 if (start_addr <= sym.n_value and sym.n_value < end_addr) {
336 return @intCast(u32, i);
337 }
338 }
339 return null;
340}
341
342pub fn writeThunkCode(zld: *Zld, atom_index: AtomIndex, writer: anytype) !void {
343 const atom = zld.getAtom(atom_index);
344 const sym = zld.getSymbol(atom.getSymbolWithLoc());
345 const source_addr = sym.n_value;
346 const thunk = zld.thunks.items[getThunkIndex(zld, atom_index).?];
347 const target_addr = for (thunk.lookup.keys()) |target| {
348 const target_atom_index = thunk.lookup.get(target).?;
349 if (atom_index == target_atom_index) break zld.getSymbol(target).n_value;
350 } else unreachable;
351
352 const pages = Atom.calcNumberOfPages(source_addr, target_addr);
353 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
354 const off = try Atom.calcPageOffset(target_addr, .arithmetic);
355 try writer.writeIntLittle(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32());
356 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
357}
src/link/MachO/zld.zig+4042-1604
......@@ -10,1944 +10,4382 @@ const mem = std.mem;
1010
1111const aarch64 = @import("../../arch/aarch64/bits.zig");
1212const bind = @import("bind.zig");
13const dead_strip = @import("dead_strip.zig");
14const fat = @import("fat.zig");
1315const link = @import("../../link.zig");
16const thunks = @import("thunks.zig");
1417const trace = @import("../../tracy.zig").trace;
1518
16const Atom = MachO.Atom;
19const Allocator = mem.Allocator;
20const Archive = @import("Archive.zig");
21const Atom = @import("ZldAtom.zig");
1722const Cache = @import("../../Cache.zig");
1823const CodeSignature = @import("CodeSignature.zig");
1924const Compilation = @import("../../Compilation.zig");
25const DwarfInfo = @import("DwarfInfo.zig");
2026const Dylib = @import("Dylib.zig");
2127const MachO = @import("../MachO.zig");
28const LibStub = @import("../tapi.zig").LibStub;
2229const Object = @import("Object.zig");
23const SymbolWithLoc = MachO.SymbolWithLoc;
30const StringTable = @import("../strtab.zig").StringTable;
2431const Trie = @import("Trie.zig");
2532
26const dead_strip = @import("dead_strip.zig");
27
28pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {
29 const tracy = trace(@src());
30 defer tracy.end();
31
32 const gpa = macho_file.base.allocator;
33 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
34 defer arena_allocator.deinit();
35 const arena = arena_allocator.allocator();
33pub const Zld = struct {
34 gpa: Allocator,
35 file: fs.File,
36 page_size: u16,
37 options: link.Options,
3638
37 const directory = macho_file.base.options.emit.?.directory; // Just an alias to make it shorter to type.
38 const full_out_path = try directory.join(arena, &[_][]const u8{macho_file.base.options.emit.?.sub_path});
39 objects: std.ArrayListUnmanaged(Object) = .{},
40 archives: std.ArrayListUnmanaged(Archive) = .{},
41 dylibs: std.ArrayListUnmanaged(Dylib) = .{},
42 dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
43 referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},
3944
40 // If there is no Zig code to compile, then we should skip flushing the output file because it
41 // will not be part of the linker line anyway.
42 const module_obj_path: ?[]const u8 = if (macho_file.base.options.module) |module| blk: {
43 if (macho_file.base.options.use_stage1) {
44 const obj_basename = try std.zig.binNameAlloc(arena, .{
45 .root_name = macho_file.base.options.root_name,
46 .target = macho_file.base.options.target,
47 .output_mode = .Obj,
48 });
49 switch (macho_file.base.options.cache_mode) {
50 .incremental => break :blk try module.zig_cache_artifact_directory.join(
51 arena,
52 &[_][]const u8{obj_basename},
53 ),
54 .whole => break :blk try fs.path.join(arena, &.{
55 fs.path.dirname(full_out_path).?, obj_basename,
56 }),
57 }
58 }
45 segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
46 sections: std.MultiArrayList(Section) = .{},
5947
60 try macho_file.flushModule(comp, prog_node);
48 locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
49 globals: std.ArrayListUnmanaged(SymbolWithLoc) = .{},
6150
62 if (fs.path.dirname(full_out_path)) |dirname| {
63 break :blk try fs.path.join(arena, &.{ dirname, macho_file.base.intermediary_basename.? });
64 } else {
65 break :blk macho_file.base.intermediary_basename.?;
66 }
67 } else null;
51 entry_index: ?u32 = null,
52 mh_execute_header_index: ?u32 = null,
53 dso_handle_index: ?u32 = null,
54 dyld_stub_binder_index: ?u32 = null,
55 dyld_private_sym_index: ?u32 = null,
56 stub_helper_preamble_sym_index: ?u32 = null,
6857
69 var sub_prog_node = prog_node.start("MachO Flush", 0);
70 sub_prog_node.activate();
71 sub_prog_node.context.refresh();
72 defer sub_prog_node.end();
58 strtab: StringTable(.strtab) = .{},
7359
74 const cpu_arch = macho_file.base.options.target.cpu.arch;
75 const os_tag = macho_file.base.options.target.os.tag;
76 const abi = macho_file.base.options.target.abi;
77 const is_lib = macho_file.base.options.output_mode == .Lib;
78 const is_dyn_lib = macho_file.base.options.link_mode == .Dynamic and is_lib;
79 const is_exe_or_dyn_lib = is_dyn_lib or macho_file.base.options.output_mode == .Exe;
80 const stack_size = macho_file.base.options.stack_size_override orelse 0;
81 const is_debug_build = macho_file.base.options.optimize_mode == .Debug;
82 const gc_sections = macho_file.base.options.gc_sections orelse !is_debug_build;
60 tlv_ptr_entries: std.ArrayListUnmanaged(IndirectPointer) = .{},
61 tlv_ptr_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
8362
84 const id_symlink_basename = "zld.id";
63 got_entries: std.ArrayListUnmanaged(IndirectPointer) = .{},
64 got_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
8565
86 var man: Cache.Manifest = undefined;
87 defer if (!macho_file.base.options.disable_lld_caching) man.deinit();
66 stubs: std.ArrayListUnmanaged(IndirectPointer) = .{},
67 stubs_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
8868
89 var digest: [Cache.hex_digest_len]u8 = undefined;
69 thunk_table: std.AutoHashMapUnmanaged(AtomIndex, thunks.ThunkIndex) = .{},
70 thunks: std.ArrayListUnmanaged(thunks.Thunk) = .{},
9071
91 if (!macho_file.base.options.disable_lld_caching) {
92 man = comp.cache_parent.obtain();
72 atoms: std.ArrayListUnmanaged(Atom) = .{},
9373
94 // We are about to obtain this lock, so here we give other processes a chance first.
95 macho_file.base.releaseLock();
74 fn parseObject(self: *Zld, path: []const u8) !bool {
75 const gpa = self.gpa;
76 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
77 error.FileNotFound => return false,
78 else => |e| return e,
79 };
80 defer file.close();
81
82 const name = try gpa.dupe(u8, path);
83 errdefer gpa.free(name);
84 const cpu_arch = self.options.target.cpu.arch;
85 const mtime: u64 = mtime: {
86 const stat = file.stat() catch break :mtime 0;
87 break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000));
88 };
89 const file_stat = try file.stat();
90 const file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
91 const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null);
92
93 var object = Object{
94 .name = name,
95 .mtime = mtime,
96 .contents = contents,
97 };
9698
97 comptime assert(Compilation.link_hash_implementation_version == 7);
99 object.parse(gpa, cpu_arch) catch |err| switch (err) {
100 error.EndOfStream, error.NotObject => {
101 object.deinit(gpa);
102 return false;
103 },
104 else => |e| return e,
105 };
98106
99 for (macho_file.base.options.objects) |obj| {
100 _ = try man.addFile(obj.path, null);
101 man.hash.add(obj.must_link);
102 }
103 for (comp.c_object_table.keys()) |key| {
104 _ = try man.addFile(key.status.success.object_path, null);
105 }
106 try man.addOptionalFile(module_obj_path);
107 // We can skip hashing libc and libc++ components that we are in charge of building from Zig
108 // installation sources because they are always a product of the compiler version + target information.
109 man.hash.add(stack_size);
110 man.hash.addOptional(macho_file.base.options.pagezero_size);
111 man.hash.addOptional(macho_file.base.options.search_strategy);
112 man.hash.addOptional(macho_file.base.options.headerpad_size);
113 man.hash.add(macho_file.base.options.headerpad_max_install_names);
114 man.hash.add(gc_sections);
115 man.hash.add(macho_file.base.options.dead_strip_dylibs);
116 man.hash.add(macho_file.base.options.strip);
117 man.hash.addListOfBytes(macho_file.base.options.lib_dirs);
118 man.hash.addListOfBytes(macho_file.base.options.framework_dirs);
119 link.hashAddSystemLibs(&man.hash, macho_file.base.options.frameworks);
120 man.hash.addListOfBytes(macho_file.base.options.rpath_list);
121 if (is_dyn_lib) {
122 man.hash.addOptionalBytes(macho_file.base.options.install_name);
123 man.hash.addOptional(macho_file.base.options.version);
124 }
125 link.hashAddSystemLibs(&man.hash, macho_file.base.options.system_libs);
126 man.hash.addOptionalBytes(macho_file.base.options.sysroot);
127 try man.addOptionalFile(macho_file.base.options.entitlements);
107 try self.objects.append(gpa, object);
128108
129 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
130 _ = try man.hit();
131 digest = man.final();
109 return true;
110 }
132111
133 var prev_digest_buf: [digest.len]u8 = undefined;
134 const prev_digest: []u8 = Cache.readSmallFile(
135 directory.handle,
136 id_symlink_basename,
137 &prev_digest_buf,
138 ) catch |err| blk: {
139 log.debug("MachO Zld new_digest={s} error: {s}", .{
140 std.fmt.fmtSliceHexLower(&digest),
141 @errorName(err),
142 });
143 // Handle this as a cache miss.
144 break :blk prev_digest_buf[0..0];
112 fn parseArchive(self: *Zld, path: []const u8, force_load: bool) !bool {
113 const gpa = self.gpa;
114 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
115 error.FileNotFound => return false,
116 else => |e| return e,
117 };
118 errdefer file.close();
119
120 const name = try gpa.dupe(u8, path);
121 errdefer gpa.free(name);
122 const cpu_arch = self.options.target.cpu.arch;
123 const reader = file.reader();
124 const fat_offset = try fat.getLibraryOffset(reader, cpu_arch);
125 try reader.context.seekTo(fat_offset);
126
127 var archive = Archive{
128 .name = name,
129 .fat_offset = fat_offset,
130 .file = file,
145131 };
146 if (mem.eql(u8, prev_digest, &digest)) {
147 // Hot diggity dog! The output binary is already there.
148 log.debug("MachO Zld digest={s} match - skipping invocation", .{
149 std.fmt.fmtSliceHexLower(&digest),
150 });
151 macho_file.base.lock = man.toOwnedLock();
152 return;
153 }
154 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{
155 std.fmt.fmtSliceHexLower(prev_digest),
156 std.fmt.fmtSliceHexLower(&digest),
157 });
158132
159 // We are about to change the output file to be different, so we invalidate the build hash now.
160 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
161 error.FileNotFound => {},
133 archive.parse(gpa, reader) catch |err| switch (err) {
134 error.EndOfStream, error.NotArchive => {
135 archive.deinit(gpa);
136 return false;
137 },
162138 else => |e| return e,
163139 };
164 }
165140
166 if (macho_file.base.options.output_mode == .Obj) {
167 // LLD's MachO driver does not support the equivalent of `-r` so we do a simple file copy
168 // here. TODO: think carefully about how we can avoid this redundant operation when doing
169 // build-obj. See also the corresponding TODO in linkAsArchive.
170 const the_object_path = blk: {
171 if (macho_file.base.options.objects.len != 0) {
172 break :blk macho_file.base.options.objects[0].path;
141 if (force_load) {
142 defer archive.deinit(gpa);
143 // Get all offsets from the ToC
144 var offsets = std.AutoArrayHashMap(u32, void).init(gpa);
145 defer offsets.deinit();
146 for (archive.toc.values()) |offs| {
147 for (offs.items) |off| {
148 _ = try offsets.getOrPut(off);
149 }
150 }
151 for (offsets.keys()) |off| {
152 const object = try archive.parseObject(gpa, cpu_arch, off);
153 try self.objects.append(gpa, object);
173154 }
155 } else {
156 try self.archives.append(gpa, archive);
157 }
174158
175 if (comp.c_object_table.count() != 0)
176 break :blk comp.c_object_table.keys()[0].status.success.object_path;
159 return true;
160 }
177161
178 if (module_obj_path) |p|
179 break :blk p;
162 const ParseDylibError = error{
163 OutOfMemory,
164 EmptyStubFile,
165 MismatchedCpuArchitecture,
166 UnsupportedCpuArchitecture,
167 EndOfStream,
168 } || fs.File.OpenError || std.os.PReadError || Dylib.Id.ParseError;
169
170 const DylibCreateOpts = struct {
171 syslibroot: ?[]const u8,
172 id: ?Dylib.Id = null,
173 dependent: bool = false,
174 needed: bool = false,
175 weak: bool = false,
176 };
180177
181 // TODO I think this is unreachable. Audit this situation when solving the above TODO
182 // regarding eliding redundant object -> object transformations.
183 return error.NoObjectsToLink;
178 fn parseDylib(
179 self: *Zld,
180 path: []const u8,
181 dependent_libs: anytype,
182 opts: DylibCreateOpts,
183 ) ParseDylibError!bool {
184 const gpa = self.gpa;
185 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
186 error.FileNotFound => return false,
187 else => |e| return e,
188 };
189 defer file.close();
190
191 const cpu_arch = self.options.target.cpu.arch;
192 const file_stat = try file.stat();
193 var file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
194
195 const reader = file.reader();
196 const fat_offset = math.cast(usize, try fat.getLibraryOffset(reader, cpu_arch)) orelse
197 return error.Overflow;
198 try file.seekTo(fat_offset);
199 file_size -= fat_offset;
200
201 const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null);
202 defer gpa.free(contents);
203
204 const dylib_id = @intCast(u16, self.dylibs.items.len);
205 var dylib = Dylib{ .weak = opts.weak };
206
207 dylib.parseFromBinary(
208 gpa,
209 cpu_arch,
210 dylib_id,
211 dependent_libs,
212 path,
213 contents,
214 ) catch |err| switch (err) {
215 error.EndOfStream, error.NotDylib => {
216 try file.seekTo(0);
217
218 var lib_stub = LibStub.loadFromFile(gpa, file) catch {
219 dylib.deinit(gpa);
220 return false;
221 };
222 defer lib_stub.deinit();
223
224 try dylib.parseFromStub(
225 gpa,
226 self.options.target,
227 lib_stub,
228 dylib_id,
229 dependent_libs,
230 path,
231 );
232 },
233 else => |e| return e,
184234 };
185 // This can happen when using --enable-cache and using the stage1 backend. In this case
186 // we can skip the file copy.
187 if (!mem.eql(u8, the_object_path, full_out_path)) {
188 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
189 }
190 } else {
191 const sub_path = macho_file.base.options.emit.?.sub_path;
192 if (macho_file.base.file == null) {
193 macho_file.base.file = try directory.handle.createFile(sub_path, .{
194 .truncate = true,
195 .read = true,
196 .mode = link.determineMode(macho_file.base.options),
197 });
198 }
199 // Index 0 is always a null symbol.
200 try macho_file.locals.append(gpa, .{
201 .n_strx = 0,
202 .n_type = 0,
203 .n_sect = 0,
204 .n_desc = 0,
205 .n_value = 0,
206 });
207 try macho_file.strtab.buffer.append(gpa, 0);
208 try initSections(macho_file);
209
210 var lib_not_found = false;
211 var framework_not_found = false;
212
213 // Positional arguments to the linker such as object files and static archives.
214 var positionals = std.ArrayList([]const u8).init(arena);
215 try positionals.ensureUnusedCapacity(macho_file.base.options.objects.len);
216235
217 var must_link_archives = std.StringArrayHashMap(void).init(arena);
218 try must_link_archives.ensureUnusedCapacity(macho_file.base.options.objects.len);
236 if (opts.id) |id| {
237 if (dylib.id.?.current_version < id.compatibility_version) {
238 log.warn("found dylib is incompatible with the required minimum version", .{});
239 log.warn(" dylib: {s}", .{id.name});
240 log.warn(" required minimum version: {}", .{id.compatibility_version});
241 log.warn(" dylib version: {}", .{dylib.id.?.current_version});
219242
220 for (macho_file.base.options.objects) |obj| {
221 if (must_link_archives.contains(obj.path)) continue;
222 if (obj.must_link) {
223 _ = must_link_archives.getOrPutAssumeCapacity(obj.path);
224 } else {
225 _ = positionals.appendAssumeCapacity(obj.path);
243 // TODO maybe this should be an error and facilitate auto-cleanup?
244 dylib.deinit(gpa);
245 return false;
226246 }
227247 }
228248
229 for (comp.c_object_table.keys()) |key| {
230 try positionals.append(key.status.success.object_path);
231 }
249 try self.dylibs.append(gpa, dylib);
250 try self.dylibs_map.putNoClobber(gpa, dylib.id.?.name, dylib_id);
232251
233 if (module_obj_path) |p| {
234 try positionals.append(p);
235 }
252 const should_link_dylib_even_if_unreachable = blk: {
253 if (self.options.dead_strip_dylibs and !opts.needed) break :blk false;
254 break :blk !(opts.dependent or self.referenced_dylibs.contains(dylib_id));
255 };
236256
237 if (comp.compiler_rt_lib) |lib| {
238 try positionals.append(lib.full_object_path);
257 if (should_link_dylib_even_if_unreachable) {
258 try self.referenced_dylibs.putNoClobber(gpa, dylib_id, {});
239259 }
240260
241 // libc++ dep
242 if (macho_file.base.options.link_libcpp) {
243 try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
244 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
261 return true;
262 }
263
264 fn parseInputFiles(
265 self: *Zld,
266 files: []const []const u8,
267 syslibroot: ?[]const u8,
268 dependent_libs: anytype,
269 ) !void {
270 for (files) |file_name| {
271 const full_path = full_path: {
272 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
273 break :full_path try fs.realpath(file_name, &buffer);
274 };
275 log.debug("parsing input file path '{s}'", .{full_path});
276
277 if (try self.parseObject(full_path)) continue;
278 if (try self.parseArchive(full_path, false)) continue;
279 if (try self.parseDylib(full_path, dependent_libs, .{
280 .syslibroot = syslibroot,
281 })) continue;
282
283 log.debug("unknown filetype for positional input file: '{s}'", .{file_name});
245284 }
285 }
246286
247 // Shared and static libraries passed via `-l` flag.
248 var candidate_libs = std.StringArrayHashMap(link.SystemLib).init(arena);
287 fn parseAndForceLoadStaticArchives(self: *Zld, files: []const []const u8) !void {
288 for (files) |file_name| {
289 const full_path = full_path: {
290 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
291 break :full_path try fs.realpath(file_name, &buffer);
292 };
293 log.debug("parsing and force loading static archive '{s}'", .{full_path});
249294
250 const system_lib_names = macho_file.base.options.system_libs.keys();
251 for (system_lib_names) |system_lib_name| {
252 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
253 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
254 // case we want to avoid prepending "-l".
255 if (Compilation.classifyFileExt(system_lib_name) == .shared_library) {
256 try positionals.append(system_lib_name);
257 continue;
258 }
295 if (try self.parseArchive(full_path, true)) continue;
296 log.debug("unknown filetype: expected static archive: '{s}'", .{file_name});
297 }
298 }
259299
260 const system_lib_info = macho_file.base.options.system_libs.get(system_lib_name).?;
261 try candidate_libs.put(system_lib_name, .{
262 .needed = system_lib_info.needed,
263 .weak = system_lib_info.weak,
264 });
300 fn parseLibs(
301 self: *Zld,
302 lib_names: []const []const u8,
303 lib_infos: []const link.SystemLib,
304 syslibroot: ?[]const u8,
305 dependent_libs: anytype,
306 ) !void {
307 for (lib_names) |lib, i| {
308 const lib_info = lib_infos[i];
309 log.debug("parsing lib path '{s}'", .{lib});
310 if (try self.parseDylib(lib, dependent_libs, .{
311 .syslibroot = syslibroot,
312 .needed = lib_info.needed,
313 .weak = lib_info.weak,
314 })) continue;
315 if (try self.parseArchive(lib, false)) continue;
316
317 log.debug("unknown filetype for a library: '{s}'", .{lib});
265318 }
319 }
266320
267 var lib_dirs = std.ArrayList([]const u8).init(arena);
268 for (macho_file.base.options.lib_dirs) |dir| {
269 if (try MachO.resolveSearchDir(arena, dir, macho_file.base.options.sysroot)) |search_dir| {
270 try lib_dirs.append(search_dir);
321 fn parseDependentLibs(self: *Zld, syslibroot: ?[]const u8, dependent_libs: anytype) !void {
322 // At this point, we can now parse dependents of dylibs preserving the inclusion order of:
323 // 1) anything on the linker line is parsed first
324 // 2) afterwards, we parse dependents of the included dylibs
325 // TODO this should not be performed if the user specifies `-flat_namespace` flag.
326 // See ld64 manpages.
327 var arena_alloc = std.heap.ArenaAllocator.init(self.gpa);
328 const arena = arena_alloc.allocator();
329 defer arena_alloc.deinit();
330
331 while (dependent_libs.readItem()) |*dep_id| {
332 defer dep_id.id.deinit(self.gpa);
333
334 if (self.dylibs_map.contains(dep_id.id.name)) continue;
335
336 const weak = self.dylibs.items[dep_id.parent].weak;
337 const has_ext = blk: {
338 const basename = fs.path.basename(dep_id.id.name);
339 break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;
340 };
341 const extension = if (has_ext) fs.path.extension(dep_id.id.name) else "";
342 const without_ext = if (has_ext) blk: {
343 const index = mem.lastIndexOfScalar(u8, dep_id.id.name, '.') orelse unreachable;
344 break :blk dep_id.id.name[0..index];
345 } else dep_id.id.name;
346
347 for (&[_][]const u8{ extension, ".tbd" }) |ext| {
348 const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ without_ext, ext });
349 const full_path = if (syslibroot) |root| try fs.path.join(arena, &.{ root, with_ext }) else with_ext;
350
351 log.debug("trying dependency at fully resolved path {s}", .{full_path});
352
353 const did_parse_successfully = try self.parseDylib(full_path, dependent_libs, .{
354 .id = dep_id.id,
355 .syslibroot = syslibroot,
356 .dependent = true,
357 .weak = weak,
358 });
359 if (did_parse_successfully) break;
271360 } else {
272 log.warn("directory not found for '-L{s}'", .{dir});
361 log.debug("unable to resolve dependency {s}", .{dep_id.id.name});
273362 }
274363 }
364 }
275365
276 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
366 pub fn getOutputSection(self: *Zld, sect: macho.section_64) !?u8 {
367 const segname = sect.segName();
368 const sectname = sect.sectName();
369 const res: ?u8 = blk: {
370 if (mem.eql(u8, "__LLVM", segname)) {
371 log.debug("TODO LLVM section: type 0x{x}, name '{s},{s}'", .{
372 sect.flags, segname, sectname,
373 });
374 break :blk null;
375 }
277376
278 // Assume ld64 default -search_paths_first if no strategy specified.
279 const search_strategy = macho_file.base.options.search_strategy orelse .paths_first;
280 outer: for (candidate_libs.keys()) |lib_name| {
281 switch (search_strategy) {
282 .paths_first => {
283 // Look in each directory for a dylib (stub first), and then for archive
284 for (lib_dirs.items) |dir| {
285 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
286 if (try MachO.resolveLib(arena, dir, lib_name, ext)) |full_path| {
287 try libs.put(full_path, candidate_libs.get(lib_name).?);
288 continue :outer;
289 }
290 }
291 } else {
292 log.warn("library not found for '-l{s}'", .{lib_name});
293 lib_not_found = true;
377 if (sect.isCode()) {
378 break :blk self.getSectionByName("__TEXT", "__text") orelse try self.initSection(
379 "__TEXT",
380 "__text",
381 .{
382 .flags = macho.S_REGULAR |
383 macho.S_ATTR_PURE_INSTRUCTIONS |
384 macho.S_ATTR_SOME_INSTRUCTIONS,
385 },
386 );
387 }
388
389 if (sect.isDebug()) {
390 // TODO debug attributes
391 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
392 log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{
393 sect.flags, segname, sectname,
394 });
395 }
396 break :blk null;
397 }
398
399 switch (sect.@"type"()) {
400 macho.S_4BYTE_LITERALS,
401 macho.S_8BYTE_LITERALS,
402 macho.S_16BYTE_LITERALS,
403 => {
404 break :blk self.getSectionByName("__TEXT", "__const") orelse try self.initSection(
405 "__TEXT",
406 "__const",
407 .{},
408 );
409 },
410 macho.S_CSTRING_LITERALS => {
411 if (mem.startsWith(u8, sectname, "__objc")) {
412 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
413 segname,
414 sectname,
415 .{},
416 );
294417 }
418 break :blk self.getSectionByName("__TEXT", "__cstring") orelse try self.initSection(
419 "__TEXT",
420 "__cstring",
421 .{ .flags = macho.S_CSTRING_LITERALS },
422 );
295423 },
296 .dylibs_first => {
297 // First, look for a dylib in each search dir
298 for (lib_dirs.items) |dir| {
299 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
300 if (try MachO.resolveLib(arena, dir, lib_name, ext)) |full_path| {
301 try libs.put(full_path, candidate_libs.get(lib_name).?);
302 continue :outer;
303 }
424 macho.S_MOD_INIT_FUNC_POINTERS,
425 macho.S_MOD_TERM_FUNC_POINTERS,
426 => {
427 break :blk self.getSectionByName("__DATA_CONST", sectname) orelse try self.initSection(
428 "__DATA_CONST",
429 sectname,
430 .{ .flags = sect.flags },
431 );
432 },
433 macho.S_LITERAL_POINTERS,
434 macho.S_ZEROFILL,
435 macho.S_THREAD_LOCAL_VARIABLES,
436 macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
437 macho.S_THREAD_LOCAL_REGULAR,
438 macho.S_THREAD_LOCAL_ZEROFILL,
439 => {
440 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
441 segname,
442 sectname,
443 .{ .flags = sect.flags },
444 );
445 },
446 macho.S_COALESCED => {
447 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
448 segname,
449 sectname,
450 .{},
451 );
452 },
453 macho.S_REGULAR => {
454 if (mem.eql(u8, segname, "__TEXT")) {
455 if (mem.eql(u8, sectname, "__rodata") or
456 mem.eql(u8, sectname, "__typelink") or
457 mem.eql(u8, sectname, "__itablink") or
458 mem.eql(u8, sectname, "__gosymtab") or
459 mem.eql(u8, sectname, "__gopclntab"))
460 {
461 break :blk self.getSectionByName("__DATA_CONST", "__const") orelse try self.initSection(
462 "__DATA_CONST",
463 "__const",
464 .{},
465 );
304466 }
305 } else for (lib_dirs.items) |dir| {
306 if (try MachO.resolveLib(arena, dir, lib_name, ".a")) |full_path| {
307 try libs.put(full_path, candidate_libs.get(lib_name).?);
308 } else {
309 log.warn("library not found for '-l{s}'", .{lib_name});
310 lib_not_found = true;
467 }
468 if (mem.eql(u8, segname, "__DATA")) {
469 if (mem.eql(u8, sectname, "__const") or
470 mem.eql(u8, sectname, "__cfstring") or
471 mem.eql(u8, sectname, "__objc_classlist") or
472 mem.eql(u8, sectname, "__objc_imageinfo"))
473 {
474 break :blk self.getSectionByName("__DATA_CONST", sectname) orelse
475 try self.initSection(
476 "__DATA_CONST",
477 sectname,
478 .{},
479 );
480 } else if (mem.eql(u8, sectname, "__data")) {
481 break :blk self.getSectionByName("__DATA", "__data") orelse
482 try self.initSection(
483 "__DATA",
484 "__data",
485 .{},
486 );
311487 }
312488 }
489 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
490 segname,
491 sectname,
492 .{},
493 );
313494 },
495 else => break :blk null,
314496 }
315 }
497 };
498 return res;
499 }
316500
317 if (lib_not_found) {
318 log.warn("Library search paths:", .{});
319 for (lib_dirs.items) |dir| {
320 log.warn(" {s}", .{dir});
321 }
501 pub fn addAtomToSection(self: *Zld, atom_index: AtomIndex) void {
502 const atom = self.getAtomPtr(atom_index);
503 const sym = self.getSymbol(atom.getSymbolWithLoc());
504 var section = self.sections.get(sym.n_sect - 1);
505 if (section.header.size > 0) {
506 const last_atom = self.getAtomPtr(section.last_atom_index);
507 last_atom.next_index = atom_index;
508 atom.prev_index = section.last_atom_index;
509 } else {
510 section.first_atom_index = atom_index;
322511 }
512 section.last_atom_index = atom_index;
513 section.header.size += atom.size;
514 self.sections.set(sym.n_sect - 1, section);
515 }
323516
324 try macho_file.resolveLibSystem(arena, comp, lib_dirs.items, &libs);
517 pub fn createEmptyAtom(self: *Zld, sym_index: u32, size: u64, alignment: u32) !AtomIndex {
518 const gpa = self.gpa;
519 const index = @intCast(AtomIndex, self.atoms.items.len);
520 const atom = try self.atoms.addOne(gpa);
521 atom.* = Atom.empty;
522 atom.sym_index = sym_index;
523 atom.size = size;
524 atom.alignment = alignment;
325525
326 // frameworks
327 var framework_dirs = std.ArrayList([]const u8).init(arena);
328 for (macho_file.base.options.framework_dirs) |dir| {
329 if (try MachO.resolveSearchDir(arena, dir, macho_file.base.options.sysroot)) |search_dir| {
330 try framework_dirs.append(search_dir);
331 } else {
332 log.warn("directory not found for '-F{s}'", .{dir});
333 }
334 }
526 log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, index });
335527
336 outer: for (macho_file.base.options.frameworks.keys()) |f_name| {
337 for (framework_dirs.items) |dir| {
338 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
339 if (try MachO.resolveFramework(arena, dir, f_name, ext)) |full_path| {
340 const info = macho_file.base.options.frameworks.get(f_name).?;
341 try libs.put(full_path, .{
342 .needed = info.needed,
343 .weak = info.weak,
344 });
345 continue :outer;
346 }
528 return index;
529 }
530
531 pub fn createGotAtom(self: *Zld) !AtomIndex {
532 const sym_index = try self.allocateSymbol();
533 const atom_index = try self.createEmptyAtom(sym_index, @sizeOf(u64), 3);
534 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
535 sym.n_type = macho.N_SECT;
536
537 const sect_id = self.getSectionByName("__DATA_CONST", "__got") orelse
538 try self.initSection("__DATA_CONST", "__got", .{
539 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
540 });
541 sym.n_sect = sect_id + 1;
542
543 self.addAtomToSection(atom_index);
544
545 return atom_index;
546 }
547
548 fn writeGotPointer(self: *Zld, got_index: u32, writer: anytype) !void {
549 const target_addr = blk: {
550 const entry = self.got_entries.items[got_index];
551 const sym = entry.getTargetSymbol(self);
552 break :blk sym.n_value;
553 };
554 try writer.writeIntLittle(u64, target_addr);
555 }
556
557 pub fn createTlvPtrAtom(self: *Zld) !AtomIndex {
558 const sym_index = try self.allocateSymbol();
559 const atom_index = try self.createEmptyAtom(sym_index, @sizeOf(u64), 3);
560 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
561 sym.n_type = macho.N_SECT;
562
563 const sect_id = (try self.getOutputSection(.{
564 .segname = makeStaticString("__DATA"),
565 .sectname = makeStaticString("__thread_ptrs"),
566 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
567 })).?;
568 sym.n_sect = sect_id + 1;
569
570 self.addAtomToSection(atom_index);
571
572 return atom_index;
573 }
574
575 fn createDyldStubBinderGotAtom(self: *Zld) !void {
576 const sym_index = self.dyld_stub_binder_index orelse return;
577 const gpa = self.gpa;
578 const target = SymbolWithLoc{ .sym_index = sym_index };
579 const atom_index = try self.createGotAtom();
580 const got_index = @intCast(u32, self.got_entries.items.len);
581 try self.got_entries.append(gpa, .{
582 .target = target,
583 .atom_index = atom_index,
584 });
585 try self.got_table.putNoClobber(gpa, target, got_index);
586 }
587
588 fn createDyldPrivateAtom(self: *Zld) !void {
589 if (self.dyld_stub_binder_index == null) return;
590
591 const sym_index = try self.allocateSymbol();
592 const atom_index = try self.createEmptyAtom(sym_index, @sizeOf(u64), 3);
593 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
594 sym.n_type = macho.N_SECT;
595
596 const sect_id = self.getSectionByName("__DATA", "__data") orelse try self.initSection("__DATA", "__data", .{});
597 sym.n_sect = sect_id + 1;
598
599 self.dyld_private_sym_index = sym_index;
600
601 self.addAtomToSection(atom_index);
602 }
603
604 fn createStubHelperPreambleAtom(self: *Zld) !void {
605 if (self.dyld_stub_binder_index == null) return;
606
607 const cpu_arch = self.options.target.cpu.arch;
608 const size: u64 = switch (cpu_arch) {
609 .x86_64 => 15,
610 .aarch64 => 6 * @sizeOf(u32),
611 else => unreachable,
612 };
613 const alignment: u32 = switch (cpu_arch) {
614 .x86_64 => 0,
615 .aarch64 => 2,
616 else => unreachable,
617 };
618 const sym_index = try self.allocateSymbol();
619 const atom_index = try self.createEmptyAtom(sym_index, size, alignment);
620 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
621 sym.n_type = macho.N_SECT;
622
623 const sect_id = self.getSectionByName("__TEXT", "__stub_helper") orelse
624 try self.initSection("__TEXT", "__stub_helper", .{
625 .flags = macho.S_REGULAR |
626 macho.S_ATTR_PURE_INSTRUCTIONS |
627 macho.S_ATTR_SOME_INSTRUCTIONS,
628 });
629 sym.n_sect = sect_id + 1;
630
631 self.stub_helper_preamble_sym_index = sym_index;
632
633 self.addAtomToSection(atom_index);
634 }
635
636 fn writeStubHelperPreambleCode(self: *Zld, writer: anytype) !void {
637 const cpu_arch = self.options.target.cpu.arch;
638 const source_addr = blk: {
639 const sym = self.getSymbol(.{ .sym_index = self.stub_helper_preamble_sym_index.? });
640 break :blk sym.n_value;
641 };
642 const dyld_private_addr = blk: {
643 const sym = self.getSymbol(.{ .sym_index = self.dyld_private_sym_index.? });
644 break :blk sym.n_value;
645 };
646 const dyld_stub_binder_got_addr = blk: {
647 const index = self.got_table.get(.{ .sym_index = self.dyld_stub_binder_index.? }).?;
648 const entry = self.got_entries.items[index];
649 break :blk entry.getAtomSymbol(self).n_value;
650 };
651 switch (cpu_arch) {
652 .x86_64 => {
653 try writer.writeAll(&.{ 0x4c, 0x8d, 0x1d });
654 {
655 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 3, dyld_private_addr, 0);
656 try writer.writeIntLittle(i32, disp);
347657 }
348 } else {
349 log.warn("framework not found for '-framework {s}'", .{f_name});
350 framework_not_found = true;
351 }
658 try writer.writeAll(&.{ 0x41, 0x53, 0xff, 0x25 });
659 {
660 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 11, dyld_stub_binder_got_addr, 0);
661 try writer.writeIntLittle(i32, disp);
662 }
663 },
664 .aarch64 => {
665 {
666 const pages = Atom.calcNumberOfPages(source_addr, dyld_private_addr);
667 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x17, pages).toU32());
668 }
669 {
670 const off = try Atom.calcPageOffset(dyld_private_addr, .arithmetic);
671 try writer.writeIntLittle(u32, aarch64.Instruction.add(.x17, .x17, off, false).toU32());
672 }
673 try writer.writeIntLittle(u32, aarch64.Instruction.stp(
674 .x16,
675 .x17,
676 aarch64.Register.sp,
677 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
678 ).toU32());
679 {
680 const pages = Atom.calcNumberOfPages(source_addr + 12, dyld_stub_binder_got_addr);
681 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
682 }
683 {
684 const off = try Atom.calcPageOffset(dyld_stub_binder_got_addr, .load_store_64);
685 try writer.writeIntLittle(u32, aarch64.Instruction.ldr(
686 .x16,
687 .x16,
688 aarch64.Instruction.LoadStoreOffset.imm(off),
689 ).toU32());
690 }
691 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
692 },
693 else => unreachable,
352694 }
695 }
353696
354 if (framework_not_found) {
355 log.warn("Framework search paths:", .{});
356 for (framework_dirs.items) |dir| {
357 log.warn(" {s}", .{dir});
358 }
697 pub fn createStubHelperAtom(self: *Zld) !AtomIndex {
698 const cpu_arch = self.options.target.cpu.arch;
699 const stub_size: u4 = switch (cpu_arch) {
700 .x86_64 => 10,
701 .aarch64 => 3 * @sizeOf(u32),
702 else => unreachable,
703 };
704 const alignment: u2 = switch (cpu_arch) {
705 .x86_64 => 0,
706 .aarch64 => 2,
707 else => unreachable,
708 };
709
710 const sym_index = try self.allocateSymbol();
711 const atom_index = try self.createEmptyAtom(sym_index, stub_size, alignment);
712 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
713 sym.n_sect = macho.N_SECT;
714
715 const sect_id = self.getSectionByName("__TEXT", "__stub_helper").?;
716 sym.n_sect = sect_id + 1;
717
718 self.addAtomToSection(atom_index);
719
720 return atom_index;
721 }
722
723 fn writeStubHelperCode(self: *Zld, atom_index: AtomIndex, writer: anytype) !void {
724 const cpu_arch = self.options.target.cpu.arch;
725 const source_addr = blk: {
726 const atom = self.getAtom(atom_index);
727 const sym = self.getSymbol(atom.getSymbolWithLoc());
728 break :blk sym.n_value;
729 };
730 const target_addr = blk: {
731 const sym = self.getSymbol(.{ .sym_index = self.stub_helper_preamble_sym_index.? });
732 break :blk sym.n_value;
733 };
734 switch (cpu_arch) {
735 .x86_64 => {
736 try writer.writeAll(&.{ 0x68, 0x0, 0x0, 0x0, 0x0, 0xe9 });
737 {
738 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 6, target_addr, 0);
739 try writer.writeIntLittle(i32, disp);
740 }
741 },
742 .aarch64 => {
743 const stub_size: u4 = 3 * @sizeOf(u32);
744 const literal = blk: {
745 const div_res = try math.divExact(u64, stub_size - @sizeOf(u32), 4);
746 break :blk math.cast(u18, div_res) orelse return error.Overflow;
747 };
748 try writer.writeIntLittle(u32, aarch64.Instruction.ldrLiteral(
749 .w16,
750 literal,
751 ).toU32());
752 {
753 const disp = try Atom.calcPcRelativeDisplacementArm64(source_addr + 4, target_addr);
754 try writer.writeIntLittle(u32, aarch64.Instruction.b(disp).toU32());
755 }
756 try writer.writeAll(&.{ 0x0, 0x0, 0x0, 0x0 });
757 },
758 else => unreachable,
359759 }
760 }
360761
361 if (macho_file.base.options.verbose_link) {
362 var argv = std.ArrayList([]const u8).init(arena);
762 pub fn createLazyPointerAtom(self: *Zld) !AtomIndex {
763 const sym_index = try self.allocateSymbol();
764 const atom_index = try self.createEmptyAtom(sym_index, @sizeOf(u64), 3);
765 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
766 sym.n_type = macho.N_SECT;
363767
364 try argv.append("zig");
365 try argv.append("ld");
768 const sect_id = self.getSectionByName("__DATA", "__la_symbol_ptr") orelse
769 try self.initSection("__DATA", "__la_symbol_ptr", .{
770 .flags = macho.S_LAZY_SYMBOL_POINTERS,
771 });
772 sym.n_sect = sect_id + 1;
366773
367 if (is_exe_or_dyn_lib) {
368 try argv.append("-dynamic");
369 }
774 self.addAtomToSection(atom_index);
370775
371 if (is_dyn_lib) {
372 try argv.append("-dylib");
776 return atom_index;
777 }
373778
374 if (macho_file.base.options.install_name) |install_name| {
375 try argv.append("-install_name");
376 try argv.append(install_name);
779 fn writeLazyPointer(self: *Zld, stub_helper_index: u32, writer: anytype) !void {
780 const target_addr = blk: {
781 const sect_id = self.getSectionByName("__TEXT", "__stub_helper").?;
782 var atom_index = self.sections.items(.first_atom_index)[sect_id];
783 var count: u32 = 0;
784 while (count < stub_helper_index + 1) : (count += 1) {
785 const atom = self.getAtom(atom_index);
786 if (atom.next_index) |next_index| {
787 atom_index = next_index;
377788 }
378789 }
790 const atom = self.getAtom(atom_index);
791 const sym = self.getSymbol(atom.getSymbolWithLoc());
792 break :blk sym.n_value;
793 };
794 try writer.writeIntLittle(u64, target_addr);
795 }
379796
380 if (macho_file.base.options.sysroot) |syslibroot| {
381 try argv.append("-syslibroot");
382 try argv.append(syslibroot);
383 }
797 pub fn createStubAtom(self: *Zld) !AtomIndex {
798 const cpu_arch = self.options.target.cpu.arch;
799 const alignment: u2 = switch (cpu_arch) {
800 .x86_64 => 0,
801 .aarch64 => 2,
802 else => unreachable, // unhandled architecture type
803 };
804 const stub_size: u4 = switch (cpu_arch) {
805 .x86_64 => 6,
806 .aarch64 => 3 * @sizeOf(u32),
807 else => unreachable, // unhandled architecture type
808 };
809 const sym_index = try self.allocateSymbol();
810 const atom_index = try self.createEmptyAtom(sym_index, stub_size, alignment);
811 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
812 sym.n_type = macho.N_SECT;
813
814 const sect_id = self.getSectionByName("__TEXT", "__stubs") orelse
815 try self.initSection("__TEXT", "__stubs", .{
816 .flags = macho.S_SYMBOL_STUBS |
817 macho.S_ATTR_PURE_INSTRUCTIONS |
818 macho.S_ATTR_SOME_INSTRUCTIONS,
819 .reserved2 = stub_size,
820 });
821 sym.n_sect = sect_id + 1;
384822
385 for (macho_file.base.options.rpath_list) |rpath| {
386 try argv.append("-rpath");
387 try argv.append(rpath);
388 }
823 self.addAtomToSection(atom_index);
389824
390 if (macho_file.base.options.pagezero_size) |pagezero_size| {
391 try argv.append("-pagezero_size");
392 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
825 return atom_index;
826 }
827
828 fn writeStubCode(self: *Zld, atom_index: AtomIndex, stub_index: u32, writer: anytype) !void {
829 const cpu_arch = self.options.target.cpu.arch;
830 const source_addr = blk: {
831 const atom = self.getAtom(atom_index);
832 const sym = self.getSymbol(atom.getSymbolWithLoc());
833 break :blk sym.n_value;
834 };
835 const target_addr = blk: {
836 // TODO: cache this at stub atom creation; they always go in pairs anyhow
837 const la_sect_id = self.getSectionByName("__DATA", "__la_symbol_ptr").?;
838 var la_atom_index = self.sections.items(.first_atom_index)[la_sect_id];
839 var count: u32 = 0;
840 while (count < stub_index) : (count += 1) {
841 const la_atom = self.getAtom(la_atom_index);
842 la_atom_index = la_atom.next_index.?;
393843 }
844 const atom = self.getAtom(la_atom_index);
845 const sym = self.getSymbol(atom.getSymbolWithLoc());
846 break :blk sym.n_value;
847 };
848 switch (cpu_arch) {
849 .x86_64 => {
850 try writer.writeAll(&.{ 0xff, 0x25 });
851 {
852 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 2, target_addr, 0);
853 try writer.writeIntLittle(i32, disp);
854 }
855 },
856 .aarch64 => {
857 {
858 const pages = Atom.calcNumberOfPages(source_addr, target_addr);
859 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
860 }
861 {
862 const off = try Atom.calcPageOffset(target_addr, .load_store_64);
863 try writer.writeIntLittle(u32, aarch64.Instruction.ldr(
864 .x16,
865 .x16,
866 aarch64.Instruction.LoadStoreOffset.imm(off),
867 ).toU32());
868 }
869 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
870 },
871 else => unreachable,
872 }
873 }
394874
395 if (macho_file.base.options.search_strategy) |strat| switch (strat) {
396 .paths_first => try argv.append("-search_paths_first"),
397 .dylibs_first => try argv.append("-search_dylibs_first"),
875 fn createTentativeDefAtoms(self: *Zld) !void {
876 const gpa = self.gpa;
877
878 for (self.globals.items) |global| {
879 const sym = self.getSymbolPtr(global);
880 if (!sym.tentative()) continue;
881 if (sym.n_desc == N_DEAD) continue;
882
883 log.debug("creating tentative definition for ATOM(%{d}, '{s}') in object({?})", .{
884 global.sym_index, self.getSymbolName(global), global.file,
885 });
886
887 // Convert any tentative definition into a regular symbol and allocate
888 // text blocks for each tentative definition.
889 const size = sym.n_value;
890 const alignment = (sym.n_desc >> 8) & 0x0f;
891 const n_sect = (try self.getOutputSection(.{
892 .segname = makeStaticString("__DATA"),
893 .sectname = makeStaticString("__bss"),
894 .flags = macho.S_ZEROFILL,
895 })).? + 1;
896
897 sym.* = .{
898 .n_strx = sym.n_strx,
899 .n_type = macho.N_SECT | macho.N_EXT,
900 .n_sect = n_sect,
901 .n_desc = 0,
902 .n_value = 0,
398903 };
399904
400 if (macho_file.base.options.headerpad_size) |headerpad_size| {
401 try argv.append("-headerpad_size");
402 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));
403 }
905 const atom_index = try self.createEmptyAtom(global.sym_index, size, alignment);
906 const atom = self.getAtomPtr(atom_index);
907 atom.file = global.file;
404908
405 if (macho_file.base.options.headerpad_max_install_names) {
406 try argv.append("-headerpad_max_install_names");
407 }
909 self.addAtomToSection(atom_index);
408910
409 if (gc_sections) {
410 try argv.append("-dead_strip");
411 }
911 assert(global.getFile() != null);
912 const object = &self.objects.items[global.getFile().?];
913 try object.atoms.append(gpa, atom_index);
914 object.atom_by_index_table[global.sym_index] = atom_index;
915 }
916 }
412917
413 if (macho_file.base.options.dead_strip_dylibs) {
414 try argv.append("-dead_strip_dylibs");
415 }
918 fn resolveSymbolsInObject(self: *Zld, object_id: u16, resolver: *SymbolResolver) !void {
919 const object = &self.objects.items[object_id];
920 const in_symtab = object.in_symtab orelse return;
416921
417 if (macho_file.base.options.entry) |entry| {
418 try argv.append("-e");
419 try argv.append(entry);
420 }
922 log.debug("resolving symbols in '{s}'", .{object.name});
421923
422 for (macho_file.base.options.objects) |obj| {
423 try argv.append(obj.path);
424 }
924 var sym_index: u32 = 0;
925 while (sym_index < in_symtab.len) : (sym_index += 1) {
926 const sym = &object.symtab[sym_index];
927 const sym_name = object.getSymbolName(sym_index);
425928
426 for (comp.c_object_table.keys()) |key| {
427 try argv.append(key.status.success.object_path);
929 if (sym.stab()) {
930 log.err("unhandled symbol type: stab", .{});
931 log.err(" symbol '{s}'", .{sym_name});
932 log.err(" first definition in '{s}'", .{object.name});
933 return error.UnhandledSymbolType;
428934 }
429935
430 if (module_obj_path) |p| {
431 try argv.append(p);
936 if (sym.indr()) {
937 log.err("unhandled symbol type: indirect", .{});
938 log.err(" symbol '{s}'", .{sym_name});
939 log.err(" first definition in '{s}'", .{object.name});
940 return error.UnhandledSymbolType;
432941 }
433942
434 if (comp.compiler_rt_lib) |lib| {
435 try argv.append(lib.full_object_path);
943 if (sym.abs()) {
944 log.err("unhandled symbol type: absolute", .{});
945 log.err(" symbol '{s}'", .{sym_name});
946 log.err(" first definition in '{s}'", .{object.name});
947 return error.UnhandledSymbolType;
436948 }
437949
438 if (macho_file.base.options.link_libcpp) {
439 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
440 try argv.append(comp.libcxx_static_lib.?.full_object_path);
950 if (sym.sect() and !sym.ext()) {
951 log.debug("symbol '{s}' local to object {s}; skipping...", .{
952 sym_name,
953 object.name,
954 });
955 continue;
441956 }
442957
443 try argv.append("-o");
444 try argv.append(full_out_path);
445
446 try argv.append("-lSystem");
447 try argv.append("-lc");
958 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id };
448959
449 for (macho_file.base.options.system_libs.keys()) |l_name| {
450 const info = macho_file.base.options.system_libs.get(l_name).?;
451 const arg = if (info.needed)
452 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})
453 else if (info.weak)
454 try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})
455 else
456 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
457 try argv.append(arg);
960 const global_index = resolver.table.get(sym_name) orelse {
961 const gpa = self.gpa;
962 const name = try resolver.arena.dupe(u8, sym_name);
963 const global_index = @intCast(u32, self.globals.items.len);
964 try self.globals.append(gpa, sym_loc);
965 try resolver.table.putNoClobber(name, global_index);
966 if (sym.undf() and !sym.tentative()) {
967 try resolver.unresolved.putNoClobber(global_index, {});
968 }
969 continue;
970 };
971 const global = &self.globals.items[global_index];
972 const global_sym = self.getSymbol(global.*);
973
974 // Cases to consider: sym vs global_sym
975 // 1. strong(sym) and strong(global_sym) => error
976 // 2. strong(sym) and weak(global_sym) => sym
977 // 3. strong(sym) and tentative(global_sym) => sym
978 // 4. strong(sym) and undf(global_sym) => sym
979 // 5. weak(sym) and strong(global_sym) => global_sym
980 // 6. weak(sym) and tentative(global_sym) => sym
981 // 7. weak(sym) and undf(global_sym) => sym
982 // 8. tentative(sym) and strong(global_sym) => global_sym
983 // 9. tentative(sym) and weak(global_sym) => global_sym
984 // 10. tentative(sym) and tentative(global_sym) => pick larger
985 // 11. tentative(sym) and undf(global_sym) => sym
986 // 12. undf(sym) and * => global_sym
987 //
988 // Reduces to:
989 // 1. strong(sym) and strong(global_sym) => error
990 // 2. * and strong(global_sym) => global_sym
991 // 3. weak(sym) and weak(global_sym) => global_sym
992 // 4. tentative(sym) and tentative(global_sym) => pick larger
993 // 5. undf(sym) and * => global_sym
994 // 6. else => sym
995
996 const sym_is_strong = sym.sect() and !(sym.weakDef() or sym.pext());
997 const global_is_strong = global_sym.sect() and !(global_sym.weakDef() or global_sym.pext());
998 const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext());
999 const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext());
1000
1001 if (sym_is_strong and global_is_strong) {
1002 log.err("symbol '{s}' defined multiple times", .{sym_name});
1003 if (global.getFile()) |file| {
1004 log.err(" first definition in '{s}'", .{self.objects.items[file].name});
1005 }
1006 log.err(" next definition in '{s}'", .{self.objects.items[object_id].name});
1007 return error.MultipleSymbolDefinitions;
4581008 }
4591009
460 for (macho_file.base.options.lib_dirs) |lib_dir| {
461 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
462 }
1010 const update_global = blk: {
1011 if (global_is_strong) break :blk false;
1012 if (sym_is_weak and global_is_weak) break :blk false;
1013 if (sym.tentative() and global_sym.tentative()) {
1014 if (global_sym.n_value >= sym.n_value) break :blk false;
1015 }
1016 if (sym.undf() and !sym.tentative()) break :blk false;
1017 break :blk true;
1018 };
4631019
464 for (macho_file.base.options.frameworks.keys()) |framework| {
465 const info = macho_file.base.options.frameworks.get(framework).?;
466 const arg = if (info.needed)
467 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework})
468 else if (info.weak)
469 try std.fmt.allocPrint(arena, "-weak_framework {s}", .{framework})
470 else
471 try std.fmt.allocPrint(arena, "-framework {s}", .{framework});
472 try argv.append(arg);
1020 if (update_global) {
1021 const global_object = &self.objects.items[global.getFile().?];
1022 global_object.globals_lookup[global.sym_index] = global_index;
1023 _ = resolver.unresolved.swapRemove(resolver.table.get(sym_name).?);
1024 global.* = sym_loc;
1025 } else {
1026 object.globals_lookup[sym_index] = global_index;
4731027 }
1028 }
1029 }
4741030
475 for (macho_file.base.options.framework_dirs) |framework_dir| {
476 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
1031 fn resolveSymbolsInArchives(self: *Zld, resolver: *SymbolResolver) !void {
1032 if (self.archives.items.len == 0) return;
1033
1034 const gpa = self.gpa;
1035 const cpu_arch = self.options.target.cpu.arch;
1036 var next_sym: usize = 0;
1037 loop: while (next_sym < resolver.unresolved.count()) {
1038 const global = self.globals.items[resolver.unresolved.keys()[next_sym]];
1039 const sym_name = self.getSymbolName(global);
1040
1041 for (self.archives.items) |archive| {
1042 // Check if the entry exists in a static archive.
1043 const offsets = archive.toc.get(sym_name) orelse {
1044 // No hit.
1045 continue;
1046 };
1047 assert(offsets.items.len > 0);
1048
1049 const object_id = @intCast(u16, self.objects.items.len);
1050 const object = try archive.parseObject(gpa, cpu_arch, offsets.items[0]);
1051 try self.objects.append(gpa, object);
1052 try self.resolveSymbolsInObject(object_id, resolver);
1053
1054 continue :loop;
4771055 }
4781056
479 if (is_dyn_lib and (macho_file.base.options.allow_shlib_undefined orelse false)) {
480 try argv.append("-undefined");
481 try argv.append("dynamic_lookup");
482 }
1057 next_sym += 1;
1058 }
1059 }
4831060
484 for (must_link_archives.keys()) |lib| {
485 try argv.append(try std.fmt.allocPrint(arena, "-force_load {s}", .{lib}));
486 }
1061 fn resolveSymbolsInDylibs(self: *Zld, resolver: *SymbolResolver) !void {
1062 if (self.dylibs.items.len == 0) return;
4871063
488 Compilation.dump_argv(argv.items);
489 }
1064 var next_sym: usize = 0;
1065 loop: while (next_sym < resolver.unresolved.count()) {
1066 const global_index = resolver.unresolved.keys()[next_sym];
1067 const global = self.globals.items[global_index];
1068 const sym = self.getSymbolPtr(global);
1069 const sym_name = self.getSymbolName(global);
4901070
491 var dependent_libs = std.fifo.LinearFifo(struct {
492 id: Dylib.Id,
493 parent: u16,
494 }, .Dynamic).init(arena);
1071 for (self.dylibs.items) |dylib, id| {
1072 if (!dylib.symbols.contains(sym_name)) continue;
4951073
496 try macho_file.parseInputFiles(positionals.items, macho_file.base.options.sysroot, &dependent_libs);
497 try macho_file.parseAndForceLoadStaticArchives(must_link_archives.keys());
498 try macho_file.parseLibs(libs.keys(), libs.values(), macho_file.base.options.sysroot, &dependent_libs);
499 try macho_file.parseDependentLibs(macho_file.base.options.sysroot, &dependent_libs);
1074 const dylib_id = @intCast(u16, id);
1075 if (!self.referenced_dylibs.contains(dylib_id)) {
1076 try self.referenced_dylibs.putNoClobber(self.gpa, dylib_id, {});
1077 }
5001078
501 for (macho_file.objects.items) |_, object_id| {
502 try macho_file.resolveSymbolsInObject(@intCast(u16, object_id));
503 }
1079 const ordinal = self.referenced_dylibs.getIndex(dylib_id) orelse unreachable;
1080 sym.n_type |= macho.N_EXT;
1081 sym.n_desc = @intCast(u16, ordinal + 1) * macho.N_SYMBOL_RESOLVER;
5041082
505 try macho_file.resolveSymbolsInArchives();
506 try macho_file.resolveDyldStubBinder();
507 try macho_file.resolveSymbolsInDylibs();
508 try macho_file.createMhExecuteHeaderSymbol();
509 try macho_file.createDsoHandleSymbol();
510 try macho_file.resolveSymbolsAtLoading();
1083 if (dylib.weak) {
1084 sym.n_desc |= macho.N_WEAK_REF;
1085 }
5111086
512 if (macho_file.unresolved.count() > 0) {
513 return error.UndefinedSymbolReference;
514 }
515 if (lib_not_found) {
516 return error.LibraryNotFound;
517 }
518 if (framework_not_found) {
519 return error.FrameworkNotFound;
520 }
1087 assert(resolver.unresolved.swapRemove(global_index));
1088 continue :loop;
1089 }
5211090
522 for (macho_file.objects.items) |*object| {
523 try object.scanInputSections(macho_file);
1091 next_sym += 1;
5241092 }
1093 }
1094
1095 fn resolveSymbolsAtLoading(self: *Zld, resolver: *SymbolResolver) !void {
1096 const is_lib = self.options.output_mode == .Lib;
1097 const is_dyn_lib = self.options.link_mode == .Dynamic and is_lib;
1098 const allow_undef = is_dyn_lib and (self.options.allow_shlib_undefined orelse false);
1099
1100 var next_sym: usize = 0;
1101 while (next_sym < resolver.unresolved.count()) {
1102 const global_index = resolver.unresolved.keys()[next_sym];
1103 const global = self.globals.items[global_index];
1104 const sym = self.getSymbolPtr(global);
1105 const sym_name = self.getSymbolName(global);
1106
1107 if (sym.discarded()) {
1108 sym.* = .{
1109 .n_strx = 0,
1110 .n_type = macho.N_UNDF,
1111 .n_sect = 0,
1112 .n_desc = 0,
1113 .n_value = 0,
1114 };
1115 _ = resolver.unresolved.swapRemove(global_index);
1116 continue;
1117 } else if (allow_undef) {
1118 const n_desc = @bitCast(
1119 u16,
1120 macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP * @intCast(i16, macho.N_SYMBOL_RESOLVER),
1121 );
1122 sym.n_type = macho.N_EXT;
1123 sym.n_desc = n_desc;
1124 _ = resolver.unresolved.swapRemove(global_index);
1125 continue;
1126 }
5251127
526 try macho_file.createDyldPrivateAtom();
527 try macho_file.createTentativeDefAtoms();
528 try macho_file.createStubHelperPreambleAtom();
1128 log.err("undefined reference to symbol '{s}'", .{sym_name});
1129 if (global.getFile()) |file| {
1130 log.err(" first referenced in '{s}'", .{self.objects.items[file].name});
1131 }
5291132
530 for (macho_file.objects.items) |*object, object_id| {
531 try object.splitIntoAtoms(macho_file, @intCast(u32, object_id));
1133 next_sym += 1;
5321134 }
1135 }
5331136
534 if (gc_sections) {
535 try dead_strip.gcAtoms(macho_file);
1137 fn createMhExecuteHeaderSymbol(self: *Zld, resolver: *SymbolResolver) !void {
1138 if (self.options.output_mode != .Exe) return;
1139 if (resolver.table.get("__mh_execute_header")) |global_index| {
1140 const global = self.globals.items[global_index];
1141 const sym = self.getSymbol(global);
1142 self.mh_execute_header_index = global_index;
1143 if (!sym.undf() and !(sym.pext() or sym.weakDef())) return;
5361144 }
5371145
538 try allocateSegments(macho_file);
539 try allocateSymbols(macho_file);
1146 const gpa = self.gpa;
1147 const sym_index = try self.allocateSymbol();
1148 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
1149 const sym = self.getSymbolPtr(sym_loc);
1150 sym.n_strx = try self.strtab.insert(gpa, "__mh_execute_header");
1151 sym.n_type = macho.N_SECT | macho.N_EXT;
1152 sym.n_desc = macho.REFERENCED_DYNAMICALLY;
1153
1154 if (resolver.table.get("__mh_execute_header")) |global_index| {
1155 const global = &self.globals.items[global_index];
1156 const global_object = &self.objects.items[global.getFile().?];
1157 global_object.globals_lookup[global.sym_index] = global_index;
1158 global.* = sym_loc;
1159 self.mh_execute_header_index = global_index;
1160 } else {
1161 const global_index = @intCast(u32, self.globals.items.len);
1162 try self.globals.append(gpa, sym_loc);
1163 self.mh_execute_header_index = global_index;
1164 }
1165 }
5401166
541 try macho_file.allocateSpecialSymbols();
1167 fn createDsoHandleSymbol(self: *Zld, resolver: *SymbolResolver) !void {
1168 const global_index = resolver.table.get("___dso_handle") orelse return;
1169 const global = &self.globals.items[global_index];
1170 self.dso_handle_index = global_index;
1171 if (!self.getSymbol(global.*).undf()) return;
1172
1173 const gpa = self.gpa;
1174 const sym_index = try self.allocateSymbol();
1175 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
1176 const sym = self.getSymbolPtr(sym_loc);
1177 sym.n_strx = try self.strtab.insert(gpa, "___dso_handle");
1178 sym.n_type = macho.N_SECT | macho.N_EXT;
1179 sym.n_desc = macho.N_WEAK_DEF;
1180
1181 const global_object = &self.objects.items[global.getFile().?];
1182 global_object.globals_lookup[global.sym_index] = global_index;
1183 _ = resolver.unresolved.swapRemove(resolver.table.get("___dso_handle").?);
1184 global.* = sym_loc;
1185 }
5421186
543 if (build_options.enable_logging or true) {
544 macho_file.logSymtab();
545 macho_file.logSections();
546 macho_file.logAtoms();
547 }
1187 fn resolveDyldStubBinder(self: *Zld, resolver: *SymbolResolver) !void {
1188 if (self.dyld_stub_binder_index != null) return;
1189 if (resolver.unresolved.count() == 0) return; // no need for a stub binder if we don't have any imports
5481190
549 try writeAtoms(macho_file);
1191 const gpa = self.gpa;
1192 const sym_name = "dyld_stub_binder";
1193 const sym_index = try self.allocateSymbol();
1194 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
1195 const sym = self.getSymbolPtr(sym_loc);
1196 sym.n_strx = try self.strtab.insert(gpa, sym_name);
1197 sym.n_type = macho.N_UNDF;
5501198
551 var lc_buffer = std.ArrayList(u8).init(arena);
552 const lc_writer = lc_buffer.writer();
553 var ncmds: u32 = 0;
1199 const global = SymbolWithLoc{ .sym_index = sym_index };
1200 try self.globals.append(gpa, global);
5541201
555 try writeLinkeditSegmentData(macho_file, &ncmds, lc_writer);
1202 for (self.dylibs.items) |dylib, id| {
1203 if (!dylib.symbols.contains(sym_name)) continue;
5561204
557 // If the last section of __DATA segment is zerofill section, we need to ensure
558 // that the free space between the end of the last non-zerofill section of __DATA
559 // segment and the beginning of __LINKEDIT segment is zerofilled as the loader will
560 // copy-paste this space into memory for quicker zerofill operation.
561 if (macho_file.data_segment_cmd_index) |data_seg_id| blk: {
562 var physical_zerofill_start: u64 = 0;
563 const section_indexes = macho_file.getSectionIndexes(data_seg_id);
564 for (macho_file.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| {
565 if (header.isZerofill() and header.size > 0) break;
566 physical_zerofill_start = header.offset + header.size;
567 } else break :blk;
568 const linkedit = macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
569 const physical_zerofill_size = math.cast(usize, linkedit.fileoff - physical_zerofill_start) orelse
570 return error.Overflow;
571 if (physical_zerofill_size > 0) {
572 var padding = try macho_file.base.allocator.alloc(u8, physical_zerofill_size);
573 defer macho_file.base.allocator.free(padding);
574 mem.set(u8, padding, 0);
575 try macho_file.base.file.?.pwriteAll(padding, physical_zerofill_start);
1205 const dylib_id = @intCast(u16, id);
1206 if (!self.referenced_dylibs.contains(dylib_id)) {
1207 try self.referenced_dylibs.putNoClobber(gpa, dylib_id, {});
5761208 }
577 }
5781209
579 try MachO.writeDylinkerLC(&ncmds, lc_writer);
580 try macho_file.writeMainLC(&ncmds, lc_writer);
581 try macho_file.writeDylibIdLC(&ncmds, lc_writer);
582 try macho_file.writeRpathLCs(&ncmds, lc_writer);
1210 const ordinal = self.referenced_dylibs.getIndex(dylib_id) orelse unreachable;
1211 sym.n_type |= macho.N_EXT;
1212 sym.n_desc = @intCast(u16, ordinal + 1) * macho.N_SYMBOL_RESOLVER;
1213 self.dyld_stub_binder_index = sym_index;
5831214
584 {
585 try lc_writer.writeStruct(macho.source_version_command{
586 .cmdsize = @sizeOf(macho.source_version_command),
587 .version = 0x0,
588 });
589 ncmds += 1;
1215 break;
5901216 }
5911217
592 try macho_file.writeBuildVersionLC(&ncmds, lc_writer);
593
594 {
595 var uuid_lc = macho.uuid_command{
596 .cmdsize = @sizeOf(macho.uuid_command),
597 .uuid = undefined,
598 };
599 std.crypto.random.bytes(&uuid_lc.uuid);
600 try lc_writer.writeStruct(uuid_lc);
601 ncmds += 1;
1218 if (self.dyld_stub_binder_index == null) {
1219 log.err("undefined reference to symbol '{s}'", .{sym_name});
1220 return error.UndefinedSymbolReference;
6021221 }
1222 }
6031223
604 try macho_file.writeLoadDylibLCs(&ncmds, lc_writer);
605
606 const requires_codesig = blk: {
607 if (macho_file.base.options.entitlements) |_| break :blk true;
608 if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true;
609 break :blk false;
610 };
611 var codesig_offset: ?u32 = null;
612 var codesig: ?CodeSignature = if (requires_codesig) blk: {
613 // Preallocate space for the code signature.
614 // We need to do this at this stage so that we have the load commands with proper values
615 // written out to the file.
616 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
617 // where the code signature goes into.
618 var codesig = CodeSignature.init(macho_file.page_size);
619 codesig.code_directory.ident = macho_file.base.options.emit.?.sub_path;
620 if (macho_file.base.options.entitlements) |path| {
621 try codesig.addEntitlements(arena, path);
622 }
623 codesig_offset = try writeCodeSignaturePadding(macho_file, &codesig, &ncmds, lc_writer);
624 break :blk codesig;
625 } else null;
626
627 var headers_buf = std.ArrayList(u8).init(arena);
628 try writeSegmentHeaders(macho_file, &ncmds, headers_buf.writer());
1224 fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
1225 const name_len = mem.sliceTo(MachO.default_dyld_path, 0).len;
1226 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
1227 u64,
1228 @sizeOf(macho.dylinker_command) + name_len,
1229 @sizeOf(u64),
1230 ));
1231 try lc_writer.writeStruct(macho.dylinker_command{
1232 .cmd = .LOAD_DYLINKER,
1233 .cmdsize = cmdsize,
1234 .name = @sizeOf(macho.dylinker_command),
1235 });
1236 try lc_writer.writeAll(mem.sliceTo(MachO.default_dyld_path, 0));
1237 const padding = cmdsize - @sizeOf(macho.dylinker_command) - name_len;
1238 if (padding > 0) {
1239 try lc_writer.writeByteNTimes(0, padding);
1240 }
1241 ncmds.* += 1;
1242 }
6291243
630 try macho_file.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
631 try macho_file.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
1244 fn writeMainLC(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
1245 if (self.options.output_mode != .Exe) return;
1246 const seg_id = self.getSegmentByName("__TEXT").?;
1247 const seg = self.segments.items[seg_id];
1248 const global = self.getEntryPoint();
1249 const sym = self.getSymbol(global);
1250 try lc_writer.writeStruct(macho.entry_point_command{
1251 .cmd = .MAIN,
1252 .cmdsize = @sizeOf(macho.entry_point_command),
1253 .entryoff = @intCast(u32, sym.n_value - seg.vmaddr),
1254 .stacksize = self.options.stack_size_override orelse 0,
1255 });
1256 ncmds.* += 1;
1257 }
6321258
633 try writeHeader(macho_file, ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
1259 const WriteDylibLCCtx = struct {
1260 cmd: macho.LC,
1261 name: []const u8,
1262 timestamp: u32 = 2,
1263 current_version: u32 = 0x10000,
1264 compatibility_version: u32 = 0x10000,
1265 };
6341266
635 if (codesig) |*csig| {
636 try writeCodeSignature(macho_file, csig, codesig_offset.?); // code signing always comes last
1267 fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {
1268 const name_len = ctx.name.len + 1;
1269 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
1270 u64,
1271 @sizeOf(macho.dylib_command) + name_len,
1272 @sizeOf(u64),
1273 ));
1274 try lc_writer.writeStruct(macho.dylib_command{
1275 .cmd = ctx.cmd,
1276 .cmdsize = cmdsize,
1277 .dylib = .{
1278 .name = @sizeOf(macho.dylib_command),
1279 .timestamp = ctx.timestamp,
1280 .current_version = ctx.current_version,
1281 .compatibility_version = ctx.compatibility_version,
1282 },
1283 });
1284 try lc_writer.writeAll(ctx.name);
1285 try lc_writer.writeByte(0);
1286 const padding = cmdsize - @sizeOf(macho.dylib_command) - name_len;
1287 if (padding > 0) {
1288 try lc_writer.writeByteNTimes(0, padding);
6371289 }
1290 ncmds.* += 1;
6381291 }
6391292
640 if (!macho_file.base.options.disable_lld_caching) {
641 // Update the file with the digest. If it fails we can continue; it only
642 // means that the next invocation will have an unnecessary cache miss.
643 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
644 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});
1293 fn writeDylibIdLC(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
1294 if (self.options.output_mode != .Lib) return;
1295 const install_name = self.options.install_name orelse self.options.emit.?.sub_path;
1296 const curr = self.options.version orelse std.builtin.Version{
1297 .major = 1,
1298 .minor = 0,
1299 .patch = 0,
6451300 };
646 // Again failure here only means an unnecessary cache miss.
647 man.writeManifest() catch |err| {
648 log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)});
1301 const compat = self.options.compatibility_version orelse std.builtin.Version{
1302 .major = 1,
1303 .minor = 0,
1304 .patch = 0,
6491305 };
650 // We hang on to this lock so that the output file path can be used without
651 // other processes clobbering it.
652 macho_file.base.lock = man.toOwnedLock();
1306 try writeDylibLC(.{
1307 .cmd = .ID_DYLIB,
1308 .name = install_name,
1309 .current_version = curr.major << 16 | curr.minor << 8 | curr.patch,
1310 .compatibility_version = compat.major << 16 | compat.minor << 8 | compat.patch,
1311 }, ncmds, lc_writer);
6531312 }
654}
6551313
656fn initSections(macho_file: *MachO) !void {
657 const gpa = macho_file.base.allocator;
658 const cpu_arch = macho_file.base.options.target.cpu.arch;
659 const pagezero_vmsize = macho_file.calcPagezeroSize();
660
661 if (macho_file.pagezero_segment_cmd_index == null) {
662 if (pagezero_vmsize > 0) {
663 macho_file.pagezero_segment_cmd_index = @intCast(u8, macho_file.segments.items.len);
664 try macho_file.segments.append(gpa, .{
665 .segname = MachO.makeStaticString("__PAGEZERO"),
666 .vmsize = pagezero_vmsize,
667 .cmdsize = @sizeOf(macho.segment_command_64),
668 });
1314 const RpathIterator = struct {
1315 buffer: []const []const u8,
1316 table: std.StringHashMap(void),
1317 count: usize = 0,
1318
1319 fn init(gpa: Allocator, rpaths: []const []const u8) RpathIterator {
1320 return .{ .buffer = rpaths, .table = std.StringHashMap(void).init(gpa) };
6691321 }
670 }
6711322
672 if (macho_file.text_segment_cmd_index == null) {
673 macho_file.text_segment_cmd_index = @intCast(u8, macho_file.segments.items.len);
674 try macho_file.segments.append(gpa, .{
675 .segname = MachO.makeStaticString("__TEXT"),
676 .vmaddr = pagezero_vmsize,
677 .vmsize = 0,
678 .filesize = 0,
679 .maxprot = macho.PROT.READ | macho.PROT.EXEC,
680 .initprot = macho.PROT.READ | macho.PROT.EXEC,
681 .cmdsize = @sizeOf(macho.segment_command_64),
682 });
683 }
1323 fn deinit(it: *RpathIterator) void {
1324 it.table.deinit();
1325 }
6841326
685 if (macho_file.text_section_index == null) {
686 macho_file.text_section_index = try macho_file.initSection("__TEXT", "__text", .{
687 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
688 });
1327 fn next(it: *RpathIterator) !?[]const u8 {
1328 while (true) {
1329 if (it.count >= it.buffer.len) return null;
1330 const rpath = it.buffer[it.count];
1331 it.count += 1;
1332 const gop = try it.table.getOrPut(rpath);
1333 if (gop.found_existing) continue;
1334 return rpath;
1335 }
1336 }
1337 };
1338
1339 fn writeRpathLCs(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
1340 const gpa = self.gpa;
1341
1342 var it = RpathIterator.init(gpa, self.options.rpath_list);
1343 defer it.deinit();
1344
1345 while (try it.next()) |rpath| {
1346 const rpath_len = rpath.len + 1;
1347 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
1348 u64,
1349 @sizeOf(macho.rpath_command) + rpath_len,
1350 @sizeOf(u64),
1351 ));
1352 try lc_writer.writeStruct(macho.rpath_command{
1353 .cmdsize = cmdsize,
1354 .path = @sizeOf(macho.rpath_command),
1355 });
1356 try lc_writer.writeAll(rpath);
1357 try lc_writer.writeByte(0);
1358 const padding = cmdsize - @sizeOf(macho.rpath_command) - rpath_len;
1359 if (padding > 0) {
1360 try lc_writer.writeByteNTimes(0, padding);
1361 }
1362 ncmds.* += 1;
1363 }
6891364 }
6901365
691 if (macho_file.stubs_section_index == null) {
692 const stub_size: u4 = switch (cpu_arch) {
693 .x86_64 => 6,
694 .aarch64 => 3 * @sizeOf(u32),
695 else => unreachable, // unhandled architecture type
1366 fn writeBuildVersionLC(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
1367 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
1368 const platform_version = blk: {
1369 const ver = self.options.target.os.version_range.semver.min;
1370 const platform_version = ver.major << 16 | ver.minor << 8;
1371 break :blk platform_version;
6961372 };
697 macho_file.stubs_section_index = try macho_file.initSection("__TEXT", "__stubs", .{
698 .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
699 .reserved2 = stub_size,
1373 const sdk_version = if (self.options.native_darwin_sdk) |sdk| blk: {
1374 const ver = sdk.version;
1375 const sdk_version = ver.major << 16 | ver.minor << 8;
1376 break :blk sdk_version;
1377 } else platform_version;
1378 const is_simulator_abi = self.options.target.abi == .simulator;
1379 try lc_writer.writeStruct(macho.build_version_command{
1380 .cmdsize = cmdsize,
1381 .platform = switch (self.options.target.os.tag) {
1382 .macos => .MACOS,
1383 .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,
1384 .watchos => if (is_simulator_abi) macho.PLATFORM.WATCHOSSIMULATOR else macho.PLATFORM.WATCHOS,
1385 .tvos => if (is_simulator_abi) macho.PLATFORM.TVOSSIMULATOR else macho.PLATFORM.TVOS,
1386 else => unreachable,
1387 },
1388 .minos = platform_version,
1389 .sdk = sdk_version,
1390 .ntools = 1,
7001391 });
1392 try lc_writer.writeAll(mem.asBytes(&macho.build_tool_version{
1393 .tool = .LD,
1394 .version = 0x0,
1395 }));
1396 ncmds.* += 1;
7011397 }
7021398
703 if (macho_file.stub_helper_section_index == null) {
704 macho_file.stub_helper_section_index = try macho_file.initSection("__TEXT", "__stub_helper", .{
705 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
706 });
1399 fn writeLoadDylibLCs(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
1400 for (self.referenced_dylibs.keys()) |id| {
1401 const dylib = self.dylibs.items[id];
1402 const dylib_id = dylib.id orelse unreachable;
1403 try writeDylibLC(.{
1404 .cmd = if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,
1405 .name = dylib_id.name,
1406 .timestamp = dylib_id.timestamp,
1407 .current_version = dylib_id.current_version,
1408 .compatibility_version = dylib_id.compatibility_version,
1409 }, ncmds, lc_writer);
1410 }
7071411 }
7081412
709 if (macho_file.data_const_segment_cmd_index == null) {
710 macho_file.data_const_segment_cmd_index = @intCast(u8, macho_file.segments.items.len);
711 try macho_file.segments.append(gpa, .{
712 .segname = MachO.makeStaticString("__DATA_CONST"),
713 .vmaddr = 0,
714 .vmsize = 0,
715 .fileoff = 0,
716 .filesize = 0,
717 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
718 .initprot = macho.PROT.READ | macho.PROT.WRITE,
719 .cmdsize = @sizeOf(macho.segment_command_64),
720 });
721 }
1413 pub fn deinit(self: *Zld) void {
1414 const gpa = self.gpa;
7221415
723 if (macho_file.got_section_index == null) {
724 macho_file.got_section_index = try macho_file.initSection("__DATA_CONST", "__got", .{
725 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
726 });
727 }
1416 for (self.archives.items) |archive| {
1417 archive.file.close();
1418 }
7281419
729 if (macho_file.data_segment_cmd_index == null) {
730 macho_file.data_segment_cmd_index = @intCast(u8, macho_file.segments.items.len);
731 try macho_file.segments.append(gpa, .{
732 .segname = MachO.makeStaticString("__DATA"),
733 .vmaddr = 0,
734 .vmsize = 0,
735 .fileoff = 0,
736 .filesize = 0,
737 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
738 .initprot = macho.PROT.READ | macho.PROT.WRITE,
739 .cmdsize = @sizeOf(macho.segment_command_64),
740 });
741 }
1420 self.tlv_ptr_entries.deinit(gpa);
1421 self.tlv_ptr_table.deinit(gpa);
1422 self.got_entries.deinit(gpa);
1423 self.got_table.deinit(gpa);
1424 self.stubs.deinit(gpa);
1425 self.stubs_table.deinit(gpa);
1426 self.thunk_table.deinit(gpa);
7421427
743 if (macho_file.la_symbol_ptr_section_index == null) {
744 macho_file.la_symbol_ptr_section_index = try macho_file.initSection("__DATA", "__la_symbol_ptr", .{
745 .flags = macho.S_LAZY_SYMBOL_POINTERS,
746 });
747 }
1428 for (self.thunks.items) |*thunk| {
1429 thunk.deinit(gpa);
1430 }
1431 self.thunks.deinit(gpa);
7481432
749 if (macho_file.data_section_index == null) {
750 macho_file.data_section_index = try macho_file.initSection("__DATA", "__data", .{});
751 }
1433 self.strtab.deinit(gpa);
1434 self.locals.deinit(gpa);
1435 self.globals.deinit(gpa);
7521436
753 if (macho_file.linkedit_segment_cmd_index == null) {
754 macho_file.linkedit_segment_cmd_index = @intCast(u8, macho_file.segments.items.len);
755 try macho_file.segments.append(gpa, .{
756 .segname = MachO.makeStaticString("__LINKEDIT"),
757 .vmaddr = 0,
758 .fileoff = 0,
759 .maxprot = macho.PROT.READ,
760 .initprot = macho.PROT.READ,
761 .cmdsize = @sizeOf(macho.segment_command_64),
762 });
763 }
764}
1437 for (self.objects.items) |*object| {
1438 object.deinit(gpa);
1439 }
1440 self.objects.deinit(gpa);
1441 for (self.archives.items) |*archive| {
1442 archive.deinit(gpa);
1443 }
1444 self.archives.deinit(gpa);
1445 for (self.dylibs.items) |*dylib| {
1446 dylib.deinit(gpa);
1447 }
1448 self.dylibs.deinit(gpa);
1449 self.dylibs_map.deinit(gpa);
1450 self.referenced_dylibs.deinit(gpa);
7651451
766fn writeAtoms(macho_file: *MachO) !void {
767 assert(macho_file.mode == .one_shot);
1452 self.segments.deinit(gpa);
1453 self.sections.deinit(gpa);
1454 self.atoms.deinit(gpa);
1455 }
7681456
769 const gpa = macho_file.base.allocator;
770 const slice = macho_file.sections.slice();
1457 fn createSegments(self: *Zld) !void {
1458 const pagezero_vmsize = self.options.pagezero_size orelse MachO.default_pagezero_vmsize;
1459 const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);
1460 if (self.options.output_mode != .Lib and aligned_pagezero_vmsize > 0) {
1461 if (aligned_pagezero_vmsize != pagezero_vmsize) {
1462 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});
1463 log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize});
1464 }
1465 try self.segments.append(self.gpa, .{
1466 .cmdsize = @sizeOf(macho.segment_command_64),
1467 .segname = makeStaticString("__PAGEZERO"),
1468 .vmsize = aligned_pagezero_vmsize,
1469 });
1470 }
7711471
772 for (slice.items(.last_atom)) |last_atom, sect_id| {
773 const header = slice.items(.header)[sect_id];
774 if (header.size == 0) continue;
775 var atom = last_atom.?;
1472 for (self.sections.items(.header)) |header, sect_id| {
1473 if (header.size == 0) continue; // empty section
1474
1475 const segname = header.segName();
1476 const segment_id = self.getSegmentByName(segname) orelse blk: {
1477 log.debug("creating segment '{s}'", .{segname});
1478 const segment_id = @intCast(u8, self.segments.items.len);
1479 const protection = getSegmentMemoryProtection(segname);
1480 try self.segments.append(self.gpa, .{
1481 .cmdsize = @sizeOf(macho.segment_command_64),
1482 .segname = makeStaticString(segname),
1483 .maxprot = protection,
1484 .initprot = protection,
1485 });
1486 break :blk segment_id;
1487 };
1488 const segment = &self.segments.items[segment_id];
1489 segment.cmdsize += @sizeOf(macho.section_64);
1490 segment.nsects += 1;
1491 self.sections.items(.segment_index)[sect_id] = segment_id;
1492 }
7761493
777 if (header.isZerofill()) continue;
1494 {
1495 const protection = getSegmentMemoryProtection("__LINKEDIT");
1496 const base = self.getSegmentAllocBase(@intCast(u8, self.segments.items.len));
1497 try self.segments.append(self.gpa, .{
1498 .cmdsize = @sizeOf(macho.segment_command_64),
1499 .segname = makeStaticString("__LINKEDIT"),
1500 .vmaddr = base.vmaddr,
1501 .fileoff = base.fileoff,
1502 .maxprot = protection,
1503 .initprot = protection,
1504 });
1505 }
1506 }
7781507
779 var buffer = std.ArrayList(u8).init(gpa);
780 defer buffer.deinit();
781 try buffer.ensureTotalCapacity(math.cast(usize, header.size) orelse return error.Overflow);
1508 inline fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 {
1509 const name_len = if (assume_max_path_len) std.os.PATH_MAX else std.mem.len(name) + 1;
1510 return mem.alignForwardGeneric(u64, cmd_size + name_len, @alignOf(u64));
1511 }
7821512
783 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
1513 fn calcLCsSize(self: *Zld, assume_max_path_len: bool) !u32 {
1514 const gpa = self.gpa;
7841515
785 while (atom.prev) |prev| {
786 atom = prev;
1516 var sizeofcmds: u64 = 0;
1517 for (self.segments.items) |seg| {
1518 sizeofcmds += seg.nsects * @sizeOf(macho.section_64) + @sizeOf(macho.segment_command_64);
7871519 }
7881520
789 while (true) {
790 const this_sym = atom.getSymbol(macho_file);
791 const padding_size: usize = if (atom.next) |next| blk: {
792 const next_sym = next.getSymbol(macho_file);
793 const size = next_sym.n_value - (this_sym.n_value + atom.size);
794 break :blk math.cast(usize, size) orelse return error.Overflow;
795 } else 0;
796
797 log.debug(" (adding ATOM(%{d}, '{s}') from object({?d}) to buffer)", .{
798 atom.sym_index,
799 atom.getName(macho_file),
800 atom.file,
801 });
802 if (padding_size > 0) {
803 log.debug(" (with padding {x})", .{padding_size});
804 }
805
806 try atom.resolveRelocs(macho_file);
807 buffer.appendSliceAssumeCapacity(atom.code.items);
808
809 var i: usize = 0;
810 while (i < padding_size) : (i += 1) {
811 // TODO with NOPs
812 buffer.appendAssumeCapacity(0);
1521 // LC_DYLD_INFO_ONLY
1522 sizeofcmds += @sizeOf(macho.dyld_info_command);
1523 // LC_FUNCTION_STARTS
1524 if (self.getSectionByName("__TEXT", "__text")) |_| {
1525 sizeofcmds += @sizeOf(macho.linkedit_data_command);
1526 }
1527 // LC_DATA_IN_CODE
1528 sizeofcmds += @sizeOf(macho.linkedit_data_command);
1529 // LC_SYMTAB
1530 sizeofcmds += @sizeOf(macho.symtab_command);
1531 // LC_DYSYMTAB
1532 sizeofcmds += @sizeOf(macho.dysymtab_command);
1533 // LC_LOAD_DYLINKER
1534 sizeofcmds += calcInstallNameLen(
1535 @sizeOf(macho.dylinker_command),
1536 mem.sliceTo(MachO.default_dyld_path, 0),
1537 false,
1538 );
1539 // LC_MAIN
1540 if (self.options.output_mode == .Exe) {
1541 sizeofcmds += @sizeOf(macho.entry_point_command);
1542 }
1543 // LC_ID_DYLIB
1544 if (self.options.output_mode == .Lib) {
1545 sizeofcmds += blk: {
1546 const install_name = self.options.install_name orelse self.options.emit.?.sub_path;
1547 break :blk calcInstallNameLen(
1548 @sizeOf(macho.dylib_command),
1549 install_name,
1550 assume_max_path_len,
1551 );
1552 };
1553 }
1554 // LC_RPATH
1555 {
1556 var it = RpathIterator.init(gpa, self.options.rpath_list);
1557 defer it.deinit();
1558 while (try it.next()) |rpath| {
1559 sizeofcmds += calcInstallNameLen(
1560 @sizeOf(macho.rpath_command),
1561 rpath,
1562 assume_max_path_len,
1563 );
8131564 }
814
815 if (atom.next) |next| {
816 atom = next;
817 } else {
818 assert(buffer.items.len == header.size);
819 log.debug(" (writing at file offset 0x{x})", .{header.offset});
820 try macho_file.base.file.?.pwriteAll(buffer.items, header.offset);
821 break;
1565 }
1566 // LC_SOURCE_VERSION
1567 sizeofcmds += @sizeOf(macho.source_version_command);
1568 // LC_BUILD_VERSION
1569 sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
1570 // LC_UUID
1571 sizeofcmds += @sizeOf(macho.uuid_command);
1572 // LC_LOAD_DYLIB
1573 for (self.referenced_dylibs.keys()) |id| {
1574 const dylib = self.dylibs.items[id];
1575 const dylib_id = dylib.id orelse unreachable;
1576 sizeofcmds += calcInstallNameLen(
1577 @sizeOf(macho.dylib_command),
1578 dylib_id.name,
1579 assume_max_path_len,
1580 );
1581 }
1582 // LC_CODE_SIGNATURE
1583 {
1584 const target = self.options.target;
1585 const requires_codesig = blk: {
1586 if (self.options.entitlements) |_| break :blk true;
1587 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
1588 break :blk true;
1589 break :blk false;
1590 };
1591 if (requires_codesig) {
1592 sizeofcmds += @sizeOf(macho.linkedit_data_command);
8221593 }
8231594 }
824 }
825}
8261595
827fn allocateSegments(macho_file: *MachO) !void {
828 try allocateSegment(macho_file, macho_file.text_segment_cmd_index, &.{
829 macho_file.pagezero_segment_cmd_index,
830 }, try macho_file.calcMinHeaderPad());
1596 return @intCast(u32, sizeofcmds);
1597 }
8311598
832 if (macho_file.text_segment_cmd_index) |index| blk: {
833 const indexes = macho_file.getSectionIndexes(index);
834 if (indexes.start == indexes.end) break :blk;
835 const seg = macho_file.segments.items[index];
1599 fn calcMinHeaderPad(self: *Zld) !u64 {
1600 var padding: u32 = (try self.calcLCsSize(false)) + (self.options.headerpad_size orelse 0);
1601 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
8361602
837 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
838 var min_alignment: u32 = 0;
839 for (macho_file.sections.items(.header)[indexes.start..indexes.end]) |header| {
840 const alignment = try math.powi(u32, 2, header.@"align");
841 min_alignment = math.max(min_alignment, alignment);
1603 if (self.options.headerpad_max_install_names) {
1604 var min_headerpad_size: u32 = try self.calcLCsSize(true);
1605 log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{
1606 min_headerpad_size + @sizeOf(macho.mach_header_64),
1607 });
1608 padding = @max(padding, min_headerpad_size);
8421609 }
8431610
844 assert(min_alignment > 0);
845 const last_header = macho_file.sections.items(.header)[indexes.end - 1];
846 const shift: u32 = shift: {
847 const diff = seg.filesize - last_header.offset - last_header.size;
848 const factor = @divTrunc(diff, min_alignment);
849 break :shift @intCast(u32, factor * min_alignment);
1611 const offset = @sizeOf(macho.mach_header_64) + padding;
1612 log.debug("actual headerpad size 0x{x}", .{offset});
1613
1614 return offset;
1615 }
1616
1617 pub fn allocateSymbol(self: *Zld) !u32 {
1618 try self.locals.ensureUnusedCapacity(self.gpa, 1);
1619 log.debug(" (allocating symbol index {d})", .{self.locals.items.len});
1620 const index = @intCast(u32, self.locals.items.len);
1621 _ = self.locals.addOneAssumeCapacity();
1622 self.locals.items[index] = .{
1623 .n_strx = 0,
1624 .n_type = 0,
1625 .n_sect = 0,
1626 .n_desc = 0,
1627 .n_value = 0,
8501628 };
1629 return index;
1630 }
8511631
852 if (shift > 0) {
853 for (macho_file.sections.items(.header)[indexes.start..indexes.end]) |*header| {
854 header.offset += shift;
855 header.addr += shift;
856 }
1632 fn allocateSpecialSymbols(self: *Zld) !void {
1633 for (&[_]?u32{
1634 self.dso_handle_index,
1635 self.mh_execute_header_index,
1636 }) |maybe_index| {
1637 const global_index = maybe_index orelse continue;
1638 const global = self.globals.items[global_index];
1639 if (global.getFile() != null) continue;
1640 const name = self.getSymbolName(global);
1641 const sym = self.getSymbolPtr(global);
1642 const segment_index = self.getSegmentByName("__TEXT").?;
1643 const seg = self.segments.items[segment_index];
1644 sym.n_sect = 1;
1645 sym.n_value = seg.vmaddr;
1646 log.debug("allocating {s} at the start of {s}", .{
1647 name,
1648 seg.segName(),
1649 });
8571650 }
8581651 }
8591652
860 try allocateSegment(macho_file, macho_file.data_const_segment_cmd_index, &.{
861 macho_file.text_segment_cmd_index,
862 macho_file.pagezero_segment_cmd_index,
863 }, 0);
1653 fn writeAtoms(self: *Zld, reverse_lookups: [][]u32) !void {
1654 const gpa = self.gpa;
1655 const slice = self.sections.slice();
8641656
865 try allocateSegment(macho_file, macho_file.data_segment_cmd_index, &.{
866 macho_file.data_const_segment_cmd_index,
867 macho_file.text_segment_cmd_index,
868 macho_file.pagezero_segment_cmd_index,
869 }, 0);
1657 for (slice.items(.first_atom_index)) |first_atom_index, sect_id| {
1658 const header = slice.items(.header)[sect_id];
1659 var atom_index = first_atom_index;
8701660
871 try allocateSegment(macho_file, macho_file.linkedit_segment_cmd_index, &.{
872 macho_file.data_segment_cmd_index,
873 macho_file.data_const_segment_cmd_index,
874 macho_file.text_segment_cmd_index,
875 macho_file.pagezero_segment_cmd_index,
876 }, 0);
877}
1661 if (header.isZerofill()) continue;
8781662
879fn getSegmentAllocBase(macho_file: *MachO, indices: []const ?u8) struct { vmaddr: u64, fileoff: u64 } {
880 for (indices) |maybe_prev_id| {
881 const prev_id = maybe_prev_id orelse continue;
882 const prev = macho_file.segments.items[prev_id];
883 return .{
884 .vmaddr = prev.vmaddr + prev.vmsize,
885 .fileoff = prev.fileoff + prev.filesize,
886 };
887 }
888 return .{ .vmaddr = 0, .fileoff = 0 };
889}
1663 var buffer = std.ArrayList(u8).init(gpa);
1664 defer buffer.deinit();
1665 try buffer.ensureTotalCapacity(math.cast(usize, header.size) orelse return error.Overflow);
1666
1667 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
8901668
891fn allocateSegment(macho_file: *MachO, maybe_index: ?u8, indices: []const ?u8, init_size: u64) !void {
892 const index = maybe_index orelse return;
893 const seg = &macho_file.segments.items[index];
1669 var count: u32 = 0;
1670 while (true) : (count += 1) {
1671 const atom = self.getAtom(atom_index);
1672 const this_sym = self.getSymbol(atom.getSymbolWithLoc());
1673 const padding_size: usize = if (atom.next_index) |next_index| blk: {
1674 const next_sym = self.getSymbol(self.getAtom(next_index).getSymbolWithLoc());
1675 const size = next_sym.n_value - (this_sym.n_value + atom.size);
1676 break :blk math.cast(usize, size) orelse return error.Overflow;
1677 } else 0;
8941678
895 const base = getSegmentAllocBase(macho_file, indices);
896 seg.vmaddr = base.vmaddr;
897 seg.fileoff = base.fileoff;
898 seg.filesize = init_size;
899 seg.vmsize = init_size;
1679 log.debug(" (adding ATOM(%{d}, '{s}') from object({?}) to buffer)", .{
1680 atom.sym_index,
1681 self.getSymbolName(atom.getSymbolWithLoc()),
1682 atom.file,
1683 });
1684 if (padding_size > 0) {
1685 log.debug(" (with padding {x})", .{padding_size});
1686 }
9001687
901 // Allocate the sections according to their alignment at the beginning of the segment.
902 const indexes = macho_file.getSectionIndexes(index);
903 var start = init_size;
904 const slice = macho_file.sections.slice();
905 for (slice.items(.header)[indexes.start..indexes.end]) |*header| {
906 const alignment = try math.powi(u32, 2, header.@"align");
907 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
1688 const offset = buffer.items.len;
9081689
909 header.offset = if (header.isZerofill())
910 0
911 else
912 @intCast(u32, seg.fileoff + start_aligned);
913 header.addr = seg.vmaddr + start_aligned;
1690 // TODO: move writing synthetic sections into a separate function
1691 if (atom.getFile() == null) outer: {
1692 if (self.dyld_private_sym_index) |sym_index| {
1693 if (atom.sym_index == sym_index) {
1694 buffer.appendSliceAssumeCapacity(&[_]u8{0} ** @sizeOf(u64));
1695 break :outer;
1696 }
1697 }
1698 switch (header.@"type"()) {
1699 macho.S_NON_LAZY_SYMBOL_POINTERS => {
1700 try self.writeGotPointer(count, buffer.writer());
1701 },
1702 macho.S_LAZY_SYMBOL_POINTERS => {
1703 try self.writeLazyPointer(count, buffer.writer());
1704 },
1705 macho.S_THREAD_LOCAL_VARIABLE_POINTERS => {
1706 buffer.appendSliceAssumeCapacity(&[_]u8{0} ** @sizeOf(u64));
1707 },
1708 else => {
1709 if (self.stub_helper_preamble_sym_index) |sym_index| {
1710 if (sym_index == atom.sym_index) {
1711 try self.writeStubHelperPreambleCode(buffer.writer());
1712 break :outer;
1713 }
1714 }
1715 if (header.@"type"() == macho.S_SYMBOL_STUBS) {
1716 try self.writeStubCode(atom_index, count, buffer.writer());
1717 } else if (mem.eql(u8, header.sectName(), "__stub_helper")) {
1718 try self.writeStubHelperCode(atom_index, buffer.writer());
1719 } else if (header.isCode()) {
1720 // A thunk
1721 try thunks.writeThunkCode(self, atom_index, buffer.writer());
1722 } else unreachable;
1723 },
1724 }
1725 } else {
1726 const code = Atom.getAtomCode(self, atom_index);
1727 const relocs = Atom.getAtomRelocs(self, atom_index);
1728 buffer.appendSliceAssumeCapacity(code);
1729 try Atom.resolveRelocs(
1730 self,
1731 atom_index,
1732 buffer.items[offset..][0..atom.size],
1733 relocs,
1734 reverse_lookups[atom.getFile().?],
1735 );
1736 }
9141737
915 start = start_aligned + header.size;
1738 var i: usize = 0;
1739 while (i < padding_size) : (i += 1) {
1740 // TODO with NOPs
1741 buffer.appendAssumeCapacity(0);
1742 }
9161743
917 if (!header.isZerofill()) {
918 seg.filesize = start;
1744 if (atom.next_index) |next_index| {
1745 atom_index = next_index;
1746 } else {
1747 assert(buffer.items.len == header.size);
1748 log.debug(" (writing at file offset 0x{x})", .{header.offset});
1749 try self.file.pwriteAll(buffer.items, header.offset);
1750 break;
1751 }
1752 }
9191753 }
920 seg.vmsize = start;
9211754 }
9221755
923 seg.filesize = mem.alignForwardGeneric(u64, seg.filesize, macho_file.page_size);
924 seg.vmsize = mem.alignForwardGeneric(u64, seg.vmsize, macho_file.page_size);
925}
1756 fn pruneAndSortSections(self: *Zld) !void {
1757 const gpa = self.gpa;
1758
1759 const SortSection = struct {
1760 pub fn lessThan(_: void, lhs: Section, rhs: Section) bool {
1761 return getSectionPrecedence(lhs.header) < getSectionPrecedence(rhs.header);
1762 }
1763 };
9261764
927fn allocateSymbols(macho_file: *MachO) !void {
928 const slice = macho_file.sections.slice();
929 for (slice.items(.last_atom)) |last_atom, sect_id| {
930 const header = slice.items(.header)[sect_id];
931 var atom = last_atom orelse continue;
1765 const slice = self.sections.slice();
1766 var sections = std.ArrayList(Section).init(gpa);
1767 defer sections.deinit();
1768 try sections.ensureTotalCapacity(slice.len);
9321769
933 while (atom.prev) |prev| {
934 atom = prev;
1770 {
1771 var i: u8 = 0;
1772 while (i < slice.len) : (i += 1) {
1773 const section = self.sections.get(i);
1774 if (section.header.size == 0) {
1775 log.debug("pruning section {s},{s}", .{
1776 section.header.segName(),
1777 section.header.sectName(),
1778 });
1779 continue;
1780 }
1781 sections.appendAssumeCapacity(section);
1782 }
9351783 }
9361784
937 const n_sect = @intCast(u8, sect_id + 1);
938 var base_vaddr = header.addr;
1785 std.sort.sort(Section, sections.items, {}, SortSection.lessThan);
9391786
940 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
941 n_sect,
942 header.segName(),
943 header.sectName(),
944 });
1787 self.sections.shrinkRetainingCapacity(0);
1788 for (sections.items) |out| {
1789 self.sections.appendAssumeCapacity(out);
1790 }
1791 }
9451792
946 while (true) {
947 const alignment = try math.powi(u32, 2, atom.alignment);
948 base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment);
1793 fn calcSectionSizes(self: *Zld, reverse_lookups: [][]u32) !void {
1794 const slice = self.sections.slice();
1795 for (slice.items(.header)) |*header, sect_id| {
1796 if (header.size == 0) continue;
1797 if (self.requiresThunks()) {
1798 if (header.isCode() and !(header.@"type"() == macho.S_SYMBOL_STUBS) and !mem.eql(u8, header.sectName(), "__stub_helper")) continue;
1799 }
9491800
950 const sym = atom.getSymbolPtr(macho_file);
951 sym.n_value = base_vaddr;
952 sym.n_sect = n_sect;
1801 var atom_index = slice.items(.first_atom_index)[sect_id];
1802 header.size = 0;
1803 header.@"align" = 0;
9531804
954 log.debug(" ATOM(%{d}, '{s}') @{x}", .{ atom.sym_index, atom.getName(macho_file), base_vaddr });
1805 while (true) {
1806 const atom = self.getAtom(atom_index);
1807 const atom_alignment = try math.powi(u32, 2, atom.alignment);
1808 const atom_offset = mem.alignForwardGeneric(u64, header.size, atom_alignment);
1809 const padding = atom_offset - header.size;
9551810
956 // Update each symbol contained within the atom
957 for (atom.contained.items) |sym_at_off| {
958 const contained_sym = macho_file.getSymbolPtr(.{
959 .sym_index = sym_at_off.sym_index,
960 .file = atom.file,
961 });
962 contained_sym.n_value = base_vaddr + sym_at_off.offset;
963 contained_sym.n_sect = n_sect;
964 }
1811 const sym = self.getSymbolPtr(atom.getSymbolWithLoc());
1812 sym.n_value = atom_offset;
9651813
966 base_vaddr += atom.size;
1814 header.size += padding + atom.size;
1815 header.@"align" = @max(header.@"align", atom.alignment);
9671816
968 if (atom.next) |next| {
969 atom = next;
970 } else break;
1817 if (atom.next_index) |next_index| {
1818 atom_index = next_index;
1819 } else break;
1820 }
9711821 }
972 }
973}
9741822
975fn writeLinkeditSegmentData(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
976 const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
977 seg.filesize = 0;
978 seg.vmsize = 0;
1823 if (self.requiresThunks()) {
1824 for (slice.items(.header)) |header, sect_id| {
1825 if (!header.isCode()) continue;
1826 if (header.@"type"() == macho.S_SYMBOL_STUBS) continue;
1827 if (mem.eql(u8, header.sectName(), "__stub_helper")) continue;
9791828
980 try writeDyldInfoData(macho_file, ncmds, lc_writer);
981 try writeFunctionStarts(macho_file, ncmds, lc_writer);
982 try writeDataInCode(macho_file, ncmds, lc_writer);
983 try writeSymtabs(macho_file, ncmds, lc_writer);
1829 // Create jump/branch range extenders if needed.
1830 try thunks.createThunks(self, @intCast(u8, sect_id), reverse_lookups);
1831 }
1832 }
1833 }
9841834
985 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, macho_file.page_size);
986}
1835 fn allocateSegments(self: *Zld) !void {
1836 for (self.segments.items) |*segment, segment_index| {
1837 const is_text_segment = mem.eql(u8, segment.segName(), "__TEXT");
1838 const base_size = if (is_text_segment) try self.calcMinHeaderPad() else 0;
1839 try self.allocateSegment(@intCast(u8, segment_index), base_size);
1840 }
1841 }
9871842
988fn writeDyldInfoData(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
989 const tracy = trace(@src());
990 defer tracy.end();
1843 fn getSegmentAllocBase(self: Zld, segment_index: u8) struct { vmaddr: u64, fileoff: u64 } {
1844 if (segment_index > 0) {
1845 const prev_segment = self.segments.items[segment_index - 1];
1846 return .{
1847 .vmaddr = prev_segment.vmaddr + prev_segment.vmsize,
1848 .fileoff = prev_segment.fileoff + prev_segment.filesize,
1849 };
1850 }
1851 return .{ .vmaddr = 0, .fileoff = 0 };
1852 }
9911853
992 const gpa = macho_file.base.allocator;
1854 fn allocateSegment(self: *Zld, segment_index: u8, init_size: u64) !void {
1855 const segment = &self.segments.items[segment_index];
9931856
994 var rebase_pointers = std.ArrayList(bind.Pointer).init(gpa);
995 defer rebase_pointers.deinit();
996 var bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
997 defer bind_pointers.deinit();
998 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
999 defer lazy_bind_pointers.deinit();
1857 if (mem.eql(u8, segment.segName(), "__PAGEZERO")) return; // allocated upon creation
10001858
1001 const slice = macho_file.sections.slice();
1002 for (slice.items(.last_atom)) |last_atom, sect_id| {
1003 var atom = last_atom orelse continue;
1004 const segment_index = slice.items(.segment_index)[sect_id];
1005 const header = slice.items(.header)[sect_id];
1859 const base = self.getSegmentAllocBase(segment_index);
1860 segment.vmaddr = base.vmaddr;
1861 segment.fileoff = base.fileoff;
1862 segment.filesize = init_size;
1863 segment.vmsize = init_size;
10061864
1007 if (mem.eql(u8, header.segName(), "__TEXT")) continue; // __TEXT is non-writable
1865 // Allocate the sections according to their alignment at the beginning of the segment.
1866 const indexes = self.getSectionIndexes(segment_index);
1867 var start = init_size;
10081868
1009 log.debug("dyld info for {s},{s}", .{ header.segName(), header.sectName() });
1869 const slice = self.sections.slice();
1870 for (slice.items(.header)[indexes.start..indexes.end]) |*header, sect_id| {
1871 var atom_index = slice.items(.first_atom_index)[indexes.start + sect_id];
10101872
1011 const seg = macho_file.segments.items[segment_index];
1873 const alignment = try math.powi(u32, 2, header.@"align");
1874 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
1875 const n_sect = @intCast(u8, indexes.start + sect_id + 1);
1876
1877 header.offset = if (header.isZerofill())
1878 0
1879 else
1880 @intCast(u32, segment.fileoff + start_aligned);
1881 header.addr = segment.vmaddr + start_aligned;
1882
1883 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
1884 n_sect,
1885 header.segName(),
1886 header.sectName(),
1887 });
10121888
1013 while (true) {
1014 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(macho_file) });
1015 const sym = atom.getSymbol(macho_file);
1016 const base_offset = sym.n_value - seg.vmaddr;
1889 while (true) {
1890 const atom = self.getAtom(atom_index);
1891 const sym = self.getSymbolPtr(atom.getSymbolWithLoc());
1892 sym.n_value += header.addr;
1893 sym.n_sect = n_sect;
10171894
1018 for (atom.rebases.items) |offset| {
1019 log.debug(" | rebase at {x}", .{base_offset + offset});
1020 try rebase_pointers.append(.{
1021 .offset = base_offset + offset,
1022 .segment_id = segment_index,
1895 log.debug(" ATOM(%{d}, '{s}') @{x}", .{
1896 atom.sym_index,
1897 self.getSymbolName(atom.getSymbolWithLoc()),
1898 sym.n_value,
10231899 });
1024 }
10251900
1026 for (atom.bindings.items) |binding| {
1027 const bind_sym = macho_file.getSymbol(binding.target);
1028 const bind_sym_name = macho_file.getSymbolName(binding.target);
1029 const dylib_ordinal = @divTrunc(
1030 @bitCast(i16, bind_sym.n_desc),
1031 macho.N_SYMBOL_RESOLVER,
1032 );
1033 var flags: u4 = 0;
1034 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
1035 binding.offset + base_offset,
1036 bind_sym_name,
1037 dylib_ordinal,
1038 });
1039 if (bind_sym.weakRef()) {
1040 log.debug(" | marking as weak ref ", .{});
1041 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
1042 }
1043 try bind_pointers.append(.{
1044 .offset = binding.offset + base_offset,
1045 .segment_id = segment_index,
1046 .dylib_ordinal = dylib_ordinal,
1047 .name = bind_sym_name,
1048 .bind_flags = flags,
1049 });
1050 }
1901 if (atom.getFile()) |_| {
1902 // Update each symbol contained within the atom
1903 var it = Atom.getInnerSymbolsIterator(self, atom_index);
1904 while (it.next()) |sym_loc| {
1905 const inner_sym = self.getSymbolPtr(sym_loc);
1906 inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(
1907 self,
1908 atom_index,
1909 sym_loc.sym_index,
1910 );
1911 inner_sym.n_sect = n_sect;
1912 }
10511913
1052 for (atom.lazy_bindings.items) |binding| {
1053 const bind_sym = macho_file.getSymbol(binding.target);
1054 const bind_sym_name = macho_file.getSymbolName(binding.target);
1055 const dylib_ordinal = @divTrunc(
1056 @bitCast(i16, bind_sym.n_desc),
1057 macho.N_SYMBOL_RESOLVER,
1058 );
1059 var flags: u4 = 0;
1060 log.debug(" | lazy bind at {x} import('{s}') ord({d})", .{
1061 binding.offset + base_offset,
1062 bind_sym_name,
1063 dylib_ordinal,
1064 });
1065 if (bind_sym.weakRef()) {
1066 log.debug(" | marking as weak ref ", .{});
1067 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
1914 // If there is a section alias, update it now too
1915 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
1916 const alias = self.getSymbolPtr(sym_loc);
1917 alias.n_value = sym.n_value;
1918 alias.n_sect = n_sect;
1919 }
10681920 }
1069 try lazy_bind_pointers.append(.{
1070 .offset = binding.offset + base_offset,
1071 .segment_id = segment_index,
1072 .dylib_ordinal = dylib_ordinal,
1073 .name = bind_sym_name,
1074 .bind_flags = flags,
1075 });
1921
1922 if (atom.next_index) |next_index| {
1923 atom_index = next_index;
1924 } else break;
10761925 }
10771926
1078 if (atom.prev) |prev| {
1079 atom = prev;
1080 } else break;
1927 start = start_aligned + header.size;
1928
1929 if (!header.isZerofill()) {
1930 segment.filesize = start;
1931 }
1932 segment.vmsize = start;
10811933 }
1934
1935 segment.filesize = mem.alignForwardGeneric(u64, segment.filesize, self.page_size);
1936 segment.vmsize = mem.alignForwardGeneric(u64, segment.vmsize, self.page_size);
10821937 }
10831938
1084 var trie: Trie = .{};
1085 defer trie.deinit(gpa);
1939 const InitSectionOpts = struct {
1940 flags: u32 = macho.S_REGULAR,
1941 reserved1: u32 = 0,
1942 reserved2: u32 = 0,
1943 };
10861944
1087 {
1088 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
1089 log.debug("generating export trie", .{});
1945 fn initSection(
1946 self: *Zld,
1947 segname: []const u8,
1948 sectname: []const u8,
1949 opts: InitSectionOpts,
1950 ) !u8 {
1951 const gpa = self.gpa;
1952 log.debug("creating section '{s},{s}'", .{ segname, sectname });
1953 const index = @intCast(u8, self.sections.slice().len);
1954 try self.sections.append(gpa, .{
1955 .segment_index = undefined,
1956 .header = .{
1957 .sectname = makeStaticString(sectname),
1958 .segname = makeStaticString(segname),
1959 .flags = opts.flags,
1960 .reserved1 = opts.reserved1,
1961 .reserved2 = opts.reserved2,
1962 },
1963 .first_atom_index = undefined,
1964 .last_atom_index = undefined,
1965 });
1966 return index;
1967 }
10901968
1091 const text_segment = macho_file.segments.items[macho_file.text_segment_cmd_index.?];
1092 const base_address = text_segment.vmaddr;
1969 inline fn getSegmentPrecedence(segname: []const u8) u4 {
1970 if (mem.eql(u8, segname, "__PAGEZERO")) return 0x0;
1971 if (mem.eql(u8, segname, "__TEXT")) return 0x1;
1972 if (mem.eql(u8, segname, "__DATA_CONST")) return 0x2;
1973 if (mem.eql(u8, segname, "__DATA")) return 0x3;
1974 if (mem.eql(u8, segname, "__LINKEDIT")) return 0x5;
1975 return 0x4;
1976 }
10931977
1094 if (macho_file.base.options.output_mode == .Exe) {
1095 for (&[_]SymbolWithLoc{
1096 try macho_file.getEntryPoint(),
1097 macho_file.getGlobal("__mh_execute_header").?,
1098 }) |global| {
1099 const sym = macho_file.getSymbol(global);
1100 const sym_name = macho_file.getSymbolName(global);
1101 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
1102 try trie.put(gpa, .{
1103 .name = sym_name,
1104 .vmaddr_offset = sym.n_value - base_address,
1105 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
1106 });
1978 inline fn getSegmentMemoryProtection(segname: []const u8) macho.vm_prot_t {
1979 if (mem.eql(u8, segname, "__PAGEZERO")) return macho.PROT.NONE;
1980 if (mem.eql(u8, segname, "__TEXT")) return macho.PROT.READ | macho.PROT.EXEC;
1981 if (mem.eql(u8, segname, "__LINKEDIT")) return macho.PROT.READ;
1982 return macho.PROT.READ | macho.PROT.WRITE;
1983 }
1984
1985 inline fn getSectionPrecedence(header: macho.section_64) u8 {
1986 const segment_precedence: u4 = getSegmentPrecedence(header.segName());
1987 const section_precedence: u4 = blk: {
1988 if (header.isCode()) {
1989 if (mem.eql(u8, "__text", header.sectName())) break :blk 0x0;
1990 if (header.@"type"() == macho.S_SYMBOL_STUBS) break :blk 0x1;
1991 break :blk 0x2;
11071992 }
1108 } else {
1109 assert(macho_file.base.options.output_mode == .Lib);
1110 for (macho_file.globals.items) |global| {
1111 const sym = macho_file.getSymbol(global);
1993 switch (header.@"type"()) {
1994 macho.S_NON_LAZY_SYMBOL_POINTERS,
1995 macho.S_LAZY_SYMBOL_POINTERS,
1996 => break :blk 0x0,
1997 macho.S_MOD_INIT_FUNC_POINTERS => break :blk 0x1,
1998 macho.S_MOD_TERM_FUNC_POINTERS => break :blk 0x2,
1999 macho.S_ZEROFILL => break :blk 0xf,
2000 macho.S_THREAD_LOCAL_REGULAR => break :blk 0xd,
2001 macho.S_THREAD_LOCAL_ZEROFILL => break :blk 0xe,
2002 else => if (mem.eql(u8, "__eh_frame", header.sectName()))
2003 break :blk 0xf
2004 else
2005 break :blk 0x3,
2006 }
2007 };
2008 return (@intCast(u8, segment_precedence) << 4) + section_precedence;
2009 }
11122010
1113 if (sym.undf()) continue;
1114 if (!sym.ext()) continue;
1115 if (sym.n_desc == MachO.N_DESC_GCED) continue;
2011 fn writeSegmentHeaders(self: *Zld, ncmds: *u32, writer: anytype) !void {
2012 for (self.segments.items) |seg, i| {
2013 const indexes = self.getSectionIndexes(@intCast(u8, i));
2014 var out_seg = seg;
2015 out_seg.cmdsize = @sizeOf(macho.segment_command_64);
2016 out_seg.nsects = 0;
2017
2018 // Update section headers count; any section with size of 0 is excluded
2019 // since it doesn't have any data in the final binary file.
2020 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
2021 if (header.size == 0) continue;
2022 out_seg.cmdsize += @sizeOf(macho.section_64);
2023 out_seg.nsects += 1;
2024 }
11162025
1117 const sym_name = macho_file.getSymbolName(global);
1118 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
1119 try trie.put(gpa, .{
1120 .name = sym_name,
1121 .vmaddr_offset = sym.n_value - base_address,
1122 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
1123 });
2026 if (out_seg.nsects == 0 and
2027 (mem.eql(u8, out_seg.segName(), "__DATA_CONST") or
2028 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;
2029
2030 try writer.writeStruct(out_seg);
2031 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
2032 if (header.size == 0) continue;
2033 try writer.writeStruct(header);
11242034 }
1125 }
11262035
1127 try trie.finalize(gpa);
2036 ncmds.* += 1;
2037 }
11282038 }
11292039
1130 const link_seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1131 const rebase_off = mem.alignForwardGeneric(u64, link_seg.fileoff, @alignOf(u64));
1132 assert(rebase_off == link_seg.fileoff);
1133 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);
1134 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size });
1135
1136 const bind_off = mem.alignForwardGeneric(u64, rebase_off + rebase_size, @alignOf(u64));
1137 const bind_size = try bind.bindInfoSize(bind_pointers.items);
1138 log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size });
1139
1140 const lazy_bind_off = mem.alignForwardGeneric(u64, bind_off + bind_size, @alignOf(u64));
1141 const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);
1142 log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{ lazy_bind_off, lazy_bind_off + lazy_bind_size });
1143
1144 const export_off = mem.alignForwardGeneric(u64, lazy_bind_off + lazy_bind_size, @alignOf(u64));
1145 const export_size = trie.size;
1146 log.debug("writing export trie from 0x{x} to 0x{x}", .{ export_off, export_off + export_size });
1147
1148 const needed_size = export_off + export_size - rebase_off;
1149 link_seg.filesize = needed_size;
1150
1151 var buffer = try gpa.alloc(u8, math.cast(usize, needed_size) orelse return error.Overflow);
1152 defer gpa.free(buffer);
1153 mem.set(u8, buffer, 0);
1154
1155 var stream = std.io.fixedBufferStream(buffer);
1156 const writer = stream.writer();
1157
1158 try bind.writeRebaseInfo(rebase_pointers.items, writer);
1159 try stream.seekTo(bind_off - rebase_off);
1160
1161 try bind.writeBindInfo(bind_pointers.items, writer);
1162 try stream.seekTo(lazy_bind_off - rebase_off);
1163
1164 try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer);
1165 try stream.seekTo(export_off - rebase_off);
1166
1167 _ = try trie.write(writer);
1168
1169 log.debug("writing dyld info from 0x{x} to 0x{x}", .{
1170 rebase_off,
1171 rebase_off + needed_size,
1172 });
1173
1174 try macho_file.base.file.?.pwriteAll(buffer, rebase_off);
1175 const start = math.cast(usize, lazy_bind_off - rebase_off) orelse return error.Overflow;
1176 const end = start + (math.cast(usize, lazy_bind_size) orelse return error.Overflow);
1177 try populateLazyBindOffsetsInStubHelper(macho_file, buffer[start..end]);
1178
1179 try lc_writer.writeStruct(macho.dyld_info_command{
1180 .cmd = .DYLD_INFO_ONLY,
1181 .cmdsize = @sizeOf(macho.dyld_info_command),
1182 .rebase_off = @intCast(u32, rebase_off),
1183 .rebase_size = @intCast(u32, rebase_size),
1184 .bind_off = @intCast(u32, bind_off),
1185 .bind_size = @intCast(u32, bind_size),
1186 .weak_bind_off = 0,
1187 .weak_bind_size = 0,
1188 .lazy_bind_off = @intCast(u32, lazy_bind_off),
1189 .lazy_bind_size = @intCast(u32, lazy_bind_size),
1190 .export_off = @intCast(u32, export_off),
1191 .export_size = @intCast(u32, export_size),
1192 });
1193 ncmds.* += 1;
1194}
2040 fn writeLinkeditSegmentData(self: *Zld, ncmds: *u32, lc_writer: anytype, reverse_lookups: [][]u32) !void {
2041 try self.writeDyldInfoData(ncmds, lc_writer, reverse_lookups);
2042 try self.writeFunctionStarts(ncmds, lc_writer);
2043 try self.writeDataInCode(ncmds, lc_writer);
2044 try self.writeSymtabs(ncmds, lc_writer);
11952045
1196fn populateLazyBindOffsetsInStubHelper(macho_file: *MachO, buffer: []const u8) !void {
1197 const gpa = macho_file.base.allocator;
2046 const seg = self.getLinkeditSegmentPtr();
2047 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
2048 }
11982049
1199 const stub_helper_section_index = macho_file.stub_helper_section_index orelse return;
1200 if (macho_file.stub_helper_preamble_atom == null) return;
2050 fn collectRebaseDataFromContainer(
2051 self: *Zld,
2052 sect_id: u8,
2053 pointers: *std.ArrayList(bind.Pointer),
2054 container: anytype,
2055 ) !void {
2056 const slice = self.sections.slice();
2057 const segment_index = slice.items(.segment_index)[sect_id];
2058 const seg = self.getSegment(sect_id);
12012059
1202 const section = macho_file.sections.get(stub_helper_section_index);
1203 const last_atom = section.last_atom orelse return;
1204 if (last_atom == macho_file.stub_helper_preamble_atom.?) return; // TODO is this a redundant check?
2060 try pointers.ensureUnusedCapacity(container.items.len);
12052061
1206 var table = std.AutoHashMap(i64, *Atom).init(gpa);
1207 defer table.deinit();
2062 for (container.items) |entry| {
2063 const target_sym = entry.getTargetSymbol(self);
2064 if (target_sym.undf()) continue;
12082065
1209 {
1210 var stub_atom = last_atom;
1211 var laptr_atom = macho_file.sections.items(.last_atom)[macho_file.la_symbol_ptr_section_index.?].?;
1212 const base_addr = blk: {
1213 const seg = macho_file.segments.items[macho_file.data_segment_cmd_index.?];
1214 break :blk seg.vmaddr;
1215 };
2066 const atom_sym = entry.getAtomSymbol(self);
2067 const base_offset = atom_sym.n_value - seg.vmaddr;
12162068
1217 while (true) {
1218 const laptr_off = blk: {
1219 const sym = laptr_atom.getSymbol(macho_file);
1220 break :blk @intCast(i64, sym.n_value - base_addr);
1221 };
1222 try table.putNoClobber(laptr_off, stub_atom);
1223 if (laptr_atom.prev) |prev| {
1224 laptr_atom = prev;
1225 stub_atom = stub_atom.prev.?;
1226 } else break;
2069 log.debug(" | rebase at {x}", .{base_offset});
2070
2071 pointers.appendAssumeCapacity(.{
2072 .offset = base_offset,
2073 .segment_id = segment_index,
2074 });
12272075 }
12282076 }
12292077
1230 var stream = std.io.fixedBufferStream(buffer);
1231 var reader = stream.reader();
1232 var offsets = std.ArrayList(struct { sym_offset: i64, offset: u32 }).init(gpa);
1233 try offsets.append(.{ .sym_offset = undefined, .offset = 0 });
1234 defer offsets.deinit();
1235 var valid_block = false;
2078 fn collectRebaseData(self: *Zld, pointers: *std.ArrayList(bind.Pointer)) !void {
2079 log.debug("collecting rebase data", .{});
12362080
1237 while (true) {
1238 const inst = reader.readByte() catch |err| switch (err) {
1239 error.EndOfStream => break,
1240 };
1241 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
2081 // First, unpack GOT entries
2082 if (self.getSectionByName("__DATA_CONST", "__got")) |sect_id| {
2083 try self.collectRebaseDataFromContainer(sect_id, pointers, self.got_entries);
2084 }
12422085
1243 switch (opcode) {
1244 macho.BIND_OPCODE_DO_BIND => {
1245 valid_block = true;
1246 },
1247 macho.BIND_OPCODE_DONE => {
1248 if (valid_block) {
1249 const offset = try stream.getPos();
1250 try offsets.append(.{ .sym_offset = undefined, .offset = @intCast(u32, offset) });
1251 }
1252 valid_block = false;
1253 },
1254 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
1255 var next = try reader.readByte();
1256 while (next != @as(u8, 0)) {
1257 next = try reader.readByte();
1258 }
1259 },
1260 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
1261 var inserted = offsets.pop();
1262 inserted.sym_offset = try std.leb.readILEB128(i64, reader);
1263 try offsets.append(inserted);
1264 },
1265 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
1266 _ = try std.leb.readULEB128(u64, reader);
1267 },
1268 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
1269 _ = try std.leb.readILEB128(i64, reader);
1270 },
1271 else => {},
2086 const slice = self.sections.slice();
2087
2088 // Next, unpact lazy pointers
2089 // TODO: save la_ptr in a container so that we can re-use the helper
2090 if (self.getSectionByName("__DATA", "__la_symbol_ptr")) |sect_id| {
2091 const segment_index = slice.items(.segment_index)[sect_id];
2092 const seg = self.getSegment(sect_id);
2093 var atom_index = slice.items(.first_atom_index)[sect_id];
2094
2095 try pointers.ensureUnusedCapacity(self.stubs.items.len);
2096
2097 while (true) {
2098 const atom = self.getAtom(atom_index);
2099 const sym = self.getSymbol(atom.getSymbolWithLoc());
2100 const base_offset = sym.n_value - seg.vmaddr;
2101
2102 log.debug(" | rebase at {x}", .{base_offset});
2103
2104 pointers.appendAssumeCapacity(.{
2105 .offset = base_offset,
2106 .segment_id = segment_index,
2107 });
2108
2109 if (atom.next_index) |next_index| {
2110 atom_index = next_index;
2111 } else break;
2112 }
2113 }
2114
2115 // Finally, unpack the rest.
2116 for (slice.items(.header)) |header, sect_id| {
2117 switch (header.@"type"()) {
2118 macho.S_LITERAL_POINTERS,
2119 macho.S_REGULAR,
2120 macho.S_MOD_INIT_FUNC_POINTERS,
2121 macho.S_MOD_TERM_FUNC_POINTERS,
2122 => {},
2123 else => continue,
2124 }
2125
2126 const segment_index = slice.items(.segment_index)[sect_id];
2127 const segment = self.getSegment(@intCast(u8, sect_id));
2128 if (segment.maxprot & macho.PROT.WRITE == 0) continue;
2129
2130 const cpu_arch = self.options.target.cpu.arch;
2131 var atom_index = slice.items(.first_atom_index)[sect_id];
2132
2133 while (true) {
2134 const atom = self.getAtom(atom_index);
2135 const sym = self.getSymbol(atom.getSymbolWithLoc());
2136
2137 const should_rebase = blk: {
2138 if (self.dyld_private_sym_index) |sym_index| {
2139 if (atom.sym_index == sym_index) break :blk false;
2140 }
2141 break :blk !sym.undf();
2142 };
2143
2144 if (should_rebase) {
2145 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, self.getSymbolName(atom.getSymbolWithLoc()) });
2146
2147 const object = self.objects.items[atom.getFile().?];
2148 const source_sym = object.getSourceSymbol(atom.sym_index).?;
2149 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
2150 const relocs = Atom.getAtomRelocs(self, atom_index);
2151
2152 for (relocs) |rel| {
2153 switch (cpu_arch) {
2154 .aarch64 => {
2155 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
2156 if (rel_type != .ARM64_RELOC_UNSIGNED) continue;
2157 if (rel.r_length != 3) continue;
2158 },
2159 .x86_64 => {
2160 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
2161 if (rel_type != .X86_64_RELOC_UNSIGNED) continue;
2162 if (rel.r_length != 3) continue;
2163 },
2164 else => unreachable,
2165 }
2166
2167 const base_offset = @intCast(i32, sym.n_value - segment.vmaddr);
2168 const rel_offset = rel.r_address - @intCast(i32, source_sym.n_value - source_sect.addr);
2169 const offset = @intCast(u64, base_offset + rel_offset);
2170 log.debug(" | rebase at {x}", .{offset});
2171
2172 try pointers.append(.{
2173 .offset = offset,
2174 .segment_id = segment_index,
2175 });
2176 }
2177 }
2178
2179 if (atom.next_index) |next_index| {
2180 atom_index = next_index;
2181 } else break;
2182 }
12722183 }
12732184 }
12742185
1275 const header = macho_file.sections.items(.header)[stub_helper_section_index];
1276 const stub_offset: u4 = switch (macho_file.base.options.target.cpu.arch) {
1277 .x86_64 => 1,
1278 .aarch64 => 2 * @sizeOf(u32),
1279 else => unreachable,
1280 };
1281 var buf: [@sizeOf(u32)]u8 = undefined;
1282 _ = offsets.pop();
1283
1284 while (offsets.popOrNull()) |bind_offset| {
1285 const atom = table.get(bind_offset.sym_offset).?;
1286 const sym = atom.getSymbol(macho_file);
1287 const file_offset = header.offset + sym.n_value - header.addr + stub_offset;
1288 mem.writeIntLittle(u32, &buf, bind_offset.offset);
1289 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{
1290 bind_offset.offset,
1291 atom.getName(macho_file),
1292 file_offset,
2186 fn collectBindDataFromContainer(
2187 self: *Zld,
2188 sect_id: u8,
2189 pointers: *std.ArrayList(bind.Pointer),
2190 container: anytype,
2191 ) !void {
2192 const slice = self.sections.slice();
2193 const segment_index = slice.items(.segment_index)[sect_id];
2194 const seg = self.getSegment(sect_id);
2195
2196 try pointers.ensureUnusedCapacity(container.items.len);
2197
2198 for (container.items) |entry| {
2199 const bind_sym_name = entry.getTargetSymbolName(self);
2200 const bind_sym = entry.getTargetSymbol(self);
2201 if (bind_sym.sect()) continue;
2202
2203 const sym = entry.getAtomSymbol(self);
2204 const base_offset = sym.n_value - seg.vmaddr;
2205
2206 const dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER);
2207 var flags: u4 = 0;
2208 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
2209 base_offset,
2210 bind_sym_name,
2211 dylib_ordinal,
2212 });
2213 if (bind_sym.weakRef()) {
2214 log.debug(" | marking as weak ref ", .{});
2215 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
2216 }
2217 pointers.appendAssumeCapacity(.{
2218 .offset = base_offset,
2219 .segment_id = segment_index,
2220 .dylib_ordinal = dylib_ordinal,
2221 .name = bind_sym_name,
2222 .bind_flags = flags,
2223 });
2224 }
2225 }
2226
2227 fn collectBindData(self: *Zld, pointers: *std.ArrayList(bind.Pointer), reverse_lookups: [][]u32) !void {
2228 log.debug("collecting bind data", .{});
2229
2230 // First, unpack GOT section
2231 if (self.getSectionByName("__DATA_CONST", "__got")) |sect_id| {
2232 try self.collectBindDataFromContainer(sect_id, pointers, self.got_entries);
2233 }
2234
2235 // Next, unpack TLV pointers section
2236 if (self.getSectionByName("__DATA", "__thread_ptrs")) |sect_id| {
2237 try self.collectBindDataFromContainer(sect_id, pointers, self.tlv_ptr_entries);
2238 }
2239
2240 // Finally, unpack the rest.
2241 const slice = self.sections.slice();
2242 for (slice.items(.header)) |header, sect_id| {
2243 switch (header.@"type"()) {
2244 macho.S_LITERAL_POINTERS,
2245 macho.S_REGULAR,
2246 macho.S_MOD_INIT_FUNC_POINTERS,
2247 macho.S_MOD_TERM_FUNC_POINTERS,
2248 => {},
2249 else => continue,
2250 }
2251
2252 const segment_index = slice.items(.segment_index)[sect_id];
2253 const segment = self.getSegment(@intCast(u8, sect_id));
2254 if (segment.maxprot & macho.PROT.WRITE == 0) continue;
2255
2256 const cpu_arch = self.options.target.cpu.arch;
2257 var atom_index = slice.items(.first_atom_index)[sect_id];
2258
2259 while (true) {
2260 const atom = self.getAtom(atom_index);
2261 const sym = self.getSymbol(atom.getSymbolWithLoc());
2262
2263 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, self.getSymbolName(atom.getSymbolWithLoc()) });
2264
2265 const should_bind = blk: {
2266 if (self.dyld_private_sym_index) |sym_index| {
2267 if (atom.sym_index == sym_index) break :blk false;
2268 }
2269 break :blk true;
2270 };
2271
2272 if (should_bind) {
2273 const object = self.objects.items[atom.getFile().?];
2274 const source_sym = object.getSourceSymbol(atom.sym_index).?;
2275 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
2276 const relocs = Atom.getAtomRelocs(self, atom_index);
2277
2278 for (relocs) |rel| {
2279 switch (cpu_arch) {
2280 .aarch64 => {
2281 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
2282 if (rel_type != .ARM64_RELOC_UNSIGNED) continue;
2283 if (rel.r_length != 3) continue;
2284 },
2285 .x86_64 => {
2286 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
2287 if (rel_type != .X86_64_RELOC_UNSIGNED) continue;
2288 if (rel.r_length != 3) continue;
2289 },
2290 else => unreachable,
2291 }
2292
2293 const global = try Atom.parseRelocTarget(self, atom_index, rel, reverse_lookups[atom.getFile().?]);
2294 const bind_sym_name = self.getSymbolName(global);
2295 const bind_sym = self.getSymbol(global);
2296 if (!bind_sym.undf()) continue;
2297
2298 const base_offset = @intCast(i32, sym.n_value - segment.vmaddr);
2299 const rel_offset = rel.r_address - @intCast(i32, source_sym.n_value - source_sect.addr);
2300 const offset = @intCast(u64, base_offset + rel_offset);
2301
2302 const dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER);
2303 var flags: u4 = 0;
2304 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
2305 base_offset,
2306 bind_sym_name,
2307 dylib_ordinal,
2308 });
2309 if (bind_sym.weakRef()) {
2310 log.debug(" | marking as weak ref ", .{});
2311 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
2312 }
2313 try pointers.append(.{
2314 .offset = offset,
2315 .segment_id = segment_index,
2316 .dylib_ordinal = dylib_ordinal,
2317 .name = bind_sym_name,
2318 .bind_flags = flags,
2319 });
2320 }
2321 }
2322 if (atom.next_index) |next_index| {
2323 atom_index = next_index;
2324 } else break;
2325 }
2326 }
2327 }
2328
2329 fn collectLazyBindData(self: *Zld, pointers: *std.ArrayList(bind.Pointer)) !void {
2330 const sect_id = self.getSectionByName("__DATA", "__la_symbol_ptr") orelse return;
2331
2332 log.debug("collecting lazy bind data", .{});
2333
2334 const slice = self.sections.slice();
2335 const segment_index = slice.items(.segment_index)[sect_id];
2336 const seg = self.getSegment(sect_id);
2337 var atom_index = slice.items(.first_atom_index)[sect_id];
2338
2339 // TODO: we actually don't need to store lazy pointer atoms as they are synthetically generated by the linker
2340 try pointers.ensureUnusedCapacity(self.stubs.items.len);
2341
2342 var count: u32 = 0;
2343 while (true) : (count += 1) {
2344 const atom = self.getAtom(atom_index);
2345
2346 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, self.getSymbolName(atom.getSymbolWithLoc()) });
2347
2348 const sym = self.getSymbol(atom.getSymbolWithLoc());
2349 const base_offset = sym.n_value - seg.vmaddr;
2350
2351 const stub_entry = self.stubs.items[count];
2352 const bind_sym = stub_entry.getTargetSymbol(self);
2353 const bind_sym_name = stub_entry.getTargetSymbolName(self);
2354 const dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER);
2355 var flags: u4 = 0;
2356 log.debug(" | lazy bind at {x}, import('{s}') in dylib({d})", .{
2357 base_offset,
2358 bind_sym_name,
2359 dylib_ordinal,
2360 });
2361 if (bind_sym.weakRef()) {
2362 log.debug(" | marking as weak ref ", .{});
2363 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
2364 }
2365 pointers.appendAssumeCapacity(.{
2366 .offset = base_offset,
2367 .segment_id = segment_index,
2368 .dylib_ordinal = dylib_ordinal,
2369 .name = bind_sym_name,
2370 .bind_flags = flags,
2371 });
2372
2373 if (atom.next_index) |next_index| {
2374 atom_index = next_index;
2375 } else break;
2376 }
2377 }
2378
2379 fn collectExportData(self: *Zld, trie: *Trie) !void {
2380 const gpa = self.gpa;
2381
2382 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
2383 log.debug("collecting export data", .{});
2384
2385 const segment_index = self.getSegmentByName("__TEXT").?;
2386 const exec_segment = self.segments.items[segment_index];
2387 const base_address = exec_segment.vmaddr;
2388
2389 if (self.options.output_mode == .Exe) {
2390 for (&[_]SymbolWithLoc{
2391 self.getEntryPoint(),
2392 self.globals.items[self.mh_execute_header_index.?],
2393 }) |global| {
2394 const sym = self.getSymbol(global);
2395 const sym_name = self.getSymbolName(global);
2396 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
2397 try trie.put(gpa, .{
2398 .name = sym_name,
2399 .vmaddr_offset = sym.n_value - base_address,
2400 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
2401 });
2402 }
2403 } else {
2404 assert(self.options.output_mode == .Lib);
2405 for (self.globals.items) |global| {
2406 const sym = self.getSymbol(global);
2407 if (sym.undf()) continue;
2408 if (sym.n_desc == N_DEAD) continue;
2409
2410 const sym_name = self.getSymbolName(global);
2411 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
2412 try trie.put(gpa, .{
2413 .name = sym_name,
2414 .vmaddr_offset = sym.n_value - base_address,
2415 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
2416 });
2417 }
2418 }
2419
2420 try trie.finalize(gpa);
2421 }
2422
2423 fn writeDyldInfoData(self: *Zld, ncmds: *u32, lc_writer: anytype, reverse_lookups: [][]u32) !void {
2424 const gpa = self.gpa;
2425
2426 var rebase_pointers = std.ArrayList(bind.Pointer).init(gpa);
2427 defer rebase_pointers.deinit();
2428 try self.collectRebaseData(&rebase_pointers);
2429
2430 var bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
2431 defer bind_pointers.deinit();
2432 try self.collectBindData(&bind_pointers, reverse_lookups);
2433
2434 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
2435 defer lazy_bind_pointers.deinit();
2436 try self.collectLazyBindData(&lazy_bind_pointers);
2437
2438 var trie = Trie{};
2439 defer trie.deinit(gpa);
2440 try self.collectExportData(&trie);
2441
2442 const link_seg = self.getLinkeditSegmentPtr();
2443 const rebase_off = mem.alignForwardGeneric(u64, link_seg.fileoff, @alignOf(u64));
2444 assert(rebase_off == link_seg.fileoff);
2445 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);
2446 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size });
2447
2448 const bind_off = mem.alignForwardGeneric(u64, rebase_off + rebase_size, @alignOf(u64));
2449 const bind_size = try bind.bindInfoSize(bind_pointers.items);
2450 log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size });
2451
2452 const lazy_bind_off = mem.alignForwardGeneric(u64, bind_off + bind_size, @alignOf(u64));
2453 const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);
2454 log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{ lazy_bind_off, lazy_bind_off + lazy_bind_size });
2455
2456 const export_off = mem.alignForwardGeneric(u64, lazy_bind_off + lazy_bind_size, @alignOf(u64));
2457 const export_size = trie.size;
2458 log.debug("writing export trie from 0x{x} to 0x{x}", .{ export_off, export_off + export_size });
2459
2460 const needed_size = export_off + export_size - rebase_off;
2461 link_seg.filesize = needed_size;
2462
2463 var buffer = try gpa.alloc(u8, needed_size);
2464 defer gpa.free(buffer);
2465 mem.set(u8, buffer, 0);
2466
2467 var stream = std.io.fixedBufferStream(buffer);
2468 const writer = stream.writer();
2469
2470 try bind.writeRebaseInfo(rebase_pointers.items, writer);
2471 try stream.seekTo(bind_off - rebase_off);
2472
2473 try bind.writeBindInfo(bind_pointers.items, writer);
2474 try stream.seekTo(lazy_bind_off - rebase_off);
2475
2476 try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer);
2477 try stream.seekTo(export_off - rebase_off);
2478
2479 _ = try trie.write(writer);
2480
2481 log.debug("writing dyld info from 0x{x} to 0x{x}", .{
2482 rebase_off,
2483 rebase_off + needed_size,
12932484 });
1294 try macho_file.base.file.?.pwriteAll(&buf, file_offset);
2485
2486 try self.file.pwriteAll(buffer, rebase_off);
2487 try self.populateLazyBindOffsetsInStubHelper(buffer[lazy_bind_off - rebase_off ..][0..lazy_bind_size]);
2488
2489 try lc_writer.writeStruct(macho.dyld_info_command{
2490 .cmd = .DYLD_INFO_ONLY,
2491 .cmdsize = @sizeOf(macho.dyld_info_command),
2492 .rebase_off = @intCast(u32, rebase_off),
2493 .rebase_size = @intCast(u32, rebase_size),
2494 .bind_off = @intCast(u32, bind_off),
2495 .bind_size = @intCast(u32, bind_size),
2496 .weak_bind_off = 0,
2497 .weak_bind_size = 0,
2498 .lazy_bind_off = @intCast(u32, lazy_bind_off),
2499 .lazy_bind_size = @intCast(u32, lazy_bind_size),
2500 .export_off = @intCast(u32, export_off),
2501 .export_size = @intCast(u32, export_size),
2502 });
2503 ncmds.* += 1;
12952504 }
1296}
12972505
1298const asc_u64 = std.sort.asc(u64);
2506 fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void {
2507 const gpa = self.gpa;
12992508
1300fn writeFunctionStarts(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1301 const tracy = trace(@src());
1302 defer tracy.end();
2509 const stub_helper_section_index = self.getSectionByName("__TEXT", "__stub_helper") orelse return;
2510 if (self.stub_helper_preamble_sym_index == null) return;
13032511
1304 const text_seg_index = macho_file.text_segment_cmd_index orelse return;
1305 const text_sect_index = macho_file.text_section_index orelse return;
1306 const text_seg = macho_file.segments.items[text_seg_index];
2512 const section = self.sections.get(stub_helper_section_index);
2513 const last_atom_index = section.last_atom_index;
13072514
1308 const gpa = macho_file.base.allocator;
2515 var table = std.AutoHashMap(i64, AtomIndex).init(gpa);
2516 defer table.deinit();
2517
2518 {
2519 var stub_atom_index = last_atom_index;
2520 const la_symbol_ptr_section_index = self.getSectionByName("__DATA", "__la_symbol_ptr").?;
2521 var laptr_atom_index = self.sections.items(.last_atom_index)[la_symbol_ptr_section_index];
2522
2523 const base_addr = blk: {
2524 const segment_index = self.getSegmentByName("__DATA").?;
2525 const seg = self.segments.items[segment_index];
2526 break :blk seg.vmaddr;
2527 };
2528
2529 while (true) {
2530 const stub_atom = self.getAtom(stub_atom_index);
2531 const laptr_atom = self.getAtom(laptr_atom_index);
2532 const laptr_off = blk: {
2533 const sym = self.getSymbolPtr(laptr_atom.getSymbolWithLoc());
2534 break :blk @intCast(i64, sym.n_value - base_addr);
2535 };
2536
2537 try table.putNoClobber(laptr_off, stub_atom_index);
2538
2539 if (laptr_atom.prev_index) |prev_index| {
2540 laptr_atom_index = prev_index;
2541 stub_atom_index = stub_atom.prev_index.?;
2542 } else break;
2543 }
2544 }
2545
2546 var stream = std.io.fixedBufferStream(buffer);
2547 var reader = stream.reader();
2548 var offsets = std.ArrayList(struct { sym_offset: i64, offset: u32 }).init(gpa);
2549 try offsets.append(.{ .sym_offset = undefined, .offset = 0 });
2550 defer offsets.deinit();
2551 var valid_block = false;
2552
2553 while (true) {
2554 const inst = reader.readByte() catch |err| switch (err) {
2555 error.EndOfStream => break,
2556 };
2557 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
2558
2559 switch (opcode) {
2560 macho.BIND_OPCODE_DO_BIND => {
2561 valid_block = true;
2562 },
2563 macho.BIND_OPCODE_DONE => {
2564 if (valid_block) {
2565 const offset = try stream.getPos();
2566 try offsets.append(.{ .sym_offset = undefined, .offset = @intCast(u32, offset) });
2567 }
2568 valid_block = false;
2569 },
2570 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
2571 var next = try reader.readByte();
2572 while (next != @as(u8, 0)) {
2573 next = try reader.readByte();
2574 }
2575 },
2576 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
2577 var inserted = offsets.pop();
2578 inserted.sym_offset = try std.leb.readILEB128(i64, reader);
2579 try offsets.append(inserted);
2580 },
2581 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
2582 _ = try std.leb.readULEB128(u64, reader);
2583 },
2584 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
2585 _ = try std.leb.readILEB128(i64, reader);
2586 },
2587 else => {},
2588 }
2589 }
2590
2591 const header = self.sections.items(.header)[stub_helper_section_index];
2592 const stub_offset: u4 = switch (self.options.target.cpu.arch) {
2593 .x86_64 => 1,
2594 .aarch64 => 2 * @sizeOf(u32),
2595 else => unreachable,
2596 };
2597 var buf: [@sizeOf(u32)]u8 = undefined;
2598 _ = offsets.pop();
2599
2600 while (offsets.popOrNull()) |bind_offset| {
2601 const atom_index = table.get(bind_offset.sym_offset).?;
2602 const atom = self.getAtom(atom_index);
2603 const sym = self.getSymbol(atom.getSymbolWithLoc());
2604
2605 const file_offset = header.offset + sym.n_value - header.addr + stub_offset;
2606 mem.writeIntLittle(u32, &buf, bind_offset.offset);
2607
2608 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{
2609 bind_offset.offset,
2610 self.getSymbolName(atom.getSymbolWithLoc()),
2611 file_offset,
2612 });
2613
2614 try self.file.pwriteAll(&buf, file_offset);
2615 }
2616 }
2617
2618 const asc_u64 = std.sort.asc(u64);
2619
2620 fn writeFunctionStarts(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
2621 const text_seg_index = self.getSegmentByName("__TEXT") orelse return;
2622 const text_sect_index = self.getSectionByName("__TEXT", "__text") orelse return;
2623 const text_seg = self.segments.items[text_seg_index];
2624
2625 const gpa = self.gpa;
2626
2627 // We need to sort by address first
2628 var addresses = std.ArrayList(u64).init(gpa);
2629 defer addresses.deinit();
2630 try addresses.ensureTotalCapacityPrecise(self.globals.items.len);
2631
2632 for (self.globals.items) |global| {
2633 const sym = self.getSymbol(global);
2634 if (sym.undf()) continue;
2635 if (sym.n_desc == N_DEAD) continue;
2636
2637 const sect_id = sym.n_sect - 1;
2638 if (sect_id != text_sect_index) continue;
2639
2640 addresses.appendAssumeCapacity(sym.n_value);
2641 }
2642
2643 std.sort.sort(u64, addresses.items, {}, asc_u64);
2644
2645 var offsets = std.ArrayList(u32).init(gpa);
2646 defer offsets.deinit();
2647 try offsets.ensureTotalCapacityPrecise(addresses.items.len);
2648
2649 var last_off: u32 = 0;
2650 for (addresses.items) |addr| {
2651 const offset = @intCast(u32, addr - text_seg.vmaddr);
2652 const diff = offset - last_off;
2653
2654 if (diff == 0) continue;
2655
2656 offsets.appendAssumeCapacity(diff);
2657 last_off = offset;
2658 }
2659
2660 var buffer = std.ArrayList(u8).init(gpa);
2661 defer buffer.deinit();
2662
2663 const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64));
2664 try buffer.ensureTotalCapacity(max_size);
2665
2666 for (offsets.items) |offset| {
2667 try std.leb.writeULEB128(buffer.writer(), offset);
2668 }
2669
2670 const link_seg = self.getLinkeditSegmentPtr();
2671 const offset = mem.alignForwardGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64));
2672 const needed_size = buffer.items.len;
2673 link_seg.filesize = offset + needed_size - link_seg.fileoff;
2674
2675 log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
2676
2677 try self.file.pwriteAll(buffer.items, offset);
2678
2679 try lc_writer.writeStruct(macho.linkedit_data_command{
2680 .cmd = .FUNCTION_STARTS,
2681 .cmdsize = @sizeOf(macho.linkedit_data_command),
2682 .dataoff = @intCast(u32, offset),
2683 .datasize = @intCast(u32, needed_size),
2684 });
2685 ncmds.* += 1;
2686 }
2687
2688 fn filterDataInCode(
2689 dices: []const macho.data_in_code_entry,
2690 start_addr: u64,
2691 end_addr: u64,
2692 ) []const macho.data_in_code_entry {
2693 const Predicate = struct {
2694 addr: u64,
13092695
1310 // We need to sort by address first
1311 var addresses = std.ArrayList(u64).init(gpa);
1312 defer addresses.deinit();
1313 try addresses.ensureTotalCapacityPrecise(macho_file.globals.items.len);
2696 pub fn predicate(self: @This(), dice: macho.data_in_code_entry) bool {
2697 return dice.offset >= self.addr;
2698 }
2699 };
2700
2701 const start = lsearch(macho.data_in_code_entry, dices, Predicate{ .addr = start_addr });
2702 const end = lsearch(macho.data_in_code_entry, dices[start..], Predicate{ .addr = end_addr }) + start;
2703
2704 return dices[start..end];
2705 }
2706
2707 fn writeDataInCode(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
2708 var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.gpa);
2709 defer out_dice.deinit();
2710
2711 const text_sect_id = self.getSectionByName("__TEXT", "__text") orelse return;
2712 const text_sect_header = self.sections.items(.header)[text_sect_id];
2713
2714 for (self.objects.items) |object| {
2715 const dice = object.parseDataInCode() orelse continue;
2716 try out_dice.ensureUnusedCapacity(dice.len);
2717
2718 for (object.atoms.items) |atom_index| {
2719 const atom = self.getAtom(atom_index);
2720 const sym = self.getSymbol(atom.getSymbolWithLoc());
2721 const sect_id = sym.n_sect - 1;
2722 if (sect_id != text_sect_id) {
2723 continue;
2724 }
2725
2726 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
2727 const source_addr = math.cast(u32, source_sym.n_value) orelse return error.Overflow;
2728 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);
2729 const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse
2730 return error.Overflow;
2731
2732 for (filtered_dice) |single| {
2733 const offset = single.offset - source_addr + base;
2734 out_dice.appendAssumeCapacity(.{
2735 .offset = offset,
2736 .length = single.length,
2737 .kind = single.kind,
2738 });
2739 }
2740 }
2741 }
2742
2743 const seg = self.getLinkeditSegmentPtr();
2744 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
2745 const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
2746 seg.filesize = offset + needed_size - seg.fileoff;
2747
2748 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
2749
2750 try self.file.pwriteAll(mem.sliceAsBytes(out_dice.items), offset);
2751 try lc_writer.writeStruct(macho.linkedit_data_command{
2752 .cmd = .DATA_IN_CODE,
2753 .cmdsize = @sizeOf(macho.linkedit_data_command),
2754 .dataoff = @intCast(u32, offset),
2755 .datasize = @intCast(u32, needed_size),
2756 });
2757 ncmds.* += 1;
2758 }
2759
2760 fn writeSymtabs(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
2761 var symtab_cmd = macho.symtab_command{
2762 .cmdsize = @sizeOf(macho.symtab_command),
2763 .symoff = 0,
2764 .nsyms = 0,
2765 .stroff = 0,
2766 .strsize = 0,
2767 };
2768 var dysymtab_cmd = macho.dysymtab_command{
2769 .cmdsize = @sizeOf(macho.dysymtab_command),
2770 .ilocalsym = 0,
2771 .nlocalsym = 0,
2772 .iextdefsym = 0,
2773 .nextdefsym = 0,
2774 .iundefsym = 0,
2775 .nundefsym = 0,
2776 .tocoff = 0,
2777 .ntoc = 0,
2778 .modtaboff = 0,
2779 .nmodtab = 0,
2780 .extrefsymoff = 0,
2781 .nextrefsyms = 0,
2782 .indirectsymoff = 0,
2783 .nindirectsyms = 0,
2784 .extreloff = 0,
2785 .nextrel = 0,
2786 .locreloff = 0,
2787 .nlocrel = 0,
2788 };
2789 var ctx = try self.writeSymtab(&symtab_cmd);
2790 defer ctx.imports_table.deinit();
2791 try self.writeDysymtab(ctx, &dysymtab_cmd);
2792 try self.writeStrtab(&symtab_cmd);
2793 try lc_writer.writeStruct(symtab_cmd);
2794 try lc_writer.writeStruct(dysymtab_cmd);
2795 ncmds.* += 2;
2796 }
2797
2798 fn writeSymtab(self: *Zld, lc: *macho.symtab_command) !SymtabCtx {
2799 const gpa = self.gpa;
2800
2801 var locals = std.ArrayList(macho.nlist_64).init(gpa);
2802 defer locals.deinit();
2803
2804 for (self.objects.items) |object| {
2805 for (object.atoms.items) |atom_index| {
2806 const atom = self.getAtom(atom_index);
2807 const sym_loc = atom.getSymbolWithLoc();
2808 const sym = self.getSymbol(sym_loc);
2809 if (sym.n_strx == 0) continue; // no name, skip
2810 if (sym.ext()) continue; // an export lands in its own symtab section, skip
2811 if (self.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
2812
2813 var out_sym = sym;
2814 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(sym_loc));
2815 try locals.append(out_sym);
2816 }
2817 }
2818
2819 if (!self.options.strip) {
2820 for (self.objects.items) |object| {
2821 try self.generateSymbolStabs(object, &locals);
2822 }
2823 }
2824
2825 var exports = std.ArrayList(macho.nlist_64).init(gpa);
2826 defer exports.deinit();
2827
2828 for (self.globals.items) |global| {
2829 const sym = self.getSymbol(global);
2830 if (sym.undf()) continue; // import, skip
2831 if (sym.n_desc == N_DEAD) continue;
2832
2833 var out_sym = sym;
2834 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
2835 try exports.append(out_sym);
2836 }
2837
2838 var imports = std.ArrayList(macho.nlist_64).init(gpa);
2839 defer imports.deinit();
2840
2841 var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);
2842
2843 for (self.globals.items) |global| {
2844 const sym = self.getSymbol(global);
2845 if (!sym.undf()) continue; // not an import, skip
2846 if (sym.n_desc == N_DEAD) continue;
2847
2848 const new_index = @intCast(u32, imports.items.len);
2849 var out_sym = sym;
2850 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
2851 try imports.append(out_sym);
2852 try imports_table.putNoClobber(global, new_index);
2853 }
2854
2855 const nlocals = @intCast(u32, locals.items.len);
2856 const nexports = @intCast(u32, exports.items.len);
2857 const nimports = @intCast(u32, imports.items.len);
2858 const nsyms = nlocals + nexports + nimports;
2859
2860 const seg = self.getLinkeditSegmentPtr();
2861 const offset = mem.alignForwardGeneric(
2862 u64,
2863 seg.fileoff + seg.filesize,
2864 @alignOf(macho.nlist_64),
2865 );
2866 const needed_size = nsyms * @sizeOf(macho.nlist_64);
2867 seg.filesize = offset + needed_size - seg.fileoff;
2868
2869 var buffer = std.ArrayList(u8).init(gpa);
2870 defer buffer.deinit();
2871 try buffer.ensureTotalCapacityPrecise(needed_size);
2872 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items));
2873 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items));
2874 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(imports.items));
2875
2876 log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
2877 try self.file.pwriteAll(buffer.items, offset);
2878
2879 lc.symoff = @intCast(u32, offset);
2880 lc.nsyms = nsyms;
2881
2882 return SymtabCtx{
2883 .nlocalsym = nlocals,
2884 .nextdefsym = nexports,
2885 .nundefsym = nimports,
2886 .imports_table = imports_table,
2887 };
2888 }
13142889
1315 for (macho_file.globals.items) |global| {
1316 const sym = macho_file.getSymbol(global);
1317 if (sym.undf()) continue;
1318 if (sym.n_desc == MachO.N_DESC_GCED) continue;
1319 const sect_id = sym.n_sect - 1;
1320 if (sect_id != text_sect_index) continue;
2890 fn writeStrtab(self: *Zld, lc: *macho.symtab_command) !void {
2891 const seg = self.getLinkeditSegmentPtr();
2892 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
2893 const needed_size = self.strtab.buffer.items.len;
2894 seg.filesize = offset + needed_size - seg.fileoff;
13212895
1322 addresses.appendAssumeCapacity(sym.n_value);
2896 log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
2897
2898 try self.file.pwriteAll(self.strtab.buffer.items, offset);
2899
2900 lc.stroff = @intCast(u32, offset);
2901 lc.strsize = @intCast(u32, needed_size);
13232902 }
13242903
1325 std.sort.sort(u64, addresses.items, {}, asc_u64);
2904 const SymtabCtx = struct {
2905 nlocalsym: u32,
2906 nextdefsym: u32,
2907 nundefsym: u32,
2908 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
2909 };
2910
2911 fn writeDysymtab(self: *Zld, ctx: SymtabCtx, lc: *macho.dysymtab_command) !void {
2912 const gpa = self.gpa;
2913 const nstubs = @intCast(u32, self.stubs.items.len);
2914 const ngot_entries = @intCast(u32, self.got_entries.items.len);
2915 const nindirectsyms = nstubs * 2 + ngot_entries;
2916 const iextdefsym = ctx.nlocalsym;
2917 const iundefsym = iextdefsym + ctx.nextdefsym;
2918
2919 const seg = self.getLinkeditSegmentPtr();
2920 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
2921 const needed_size = nindirectsyms * @sizeOf(u32);
2922 seg.filesize = offset + needed_size - seg.fileoff;
2923
2924 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
2925
2926 var buf = std.ArrayList(u8).init(gpa);
2927 defer buf.deinit();
2928 try buf.ensureTotalCapacity(needed_size);
2929 const writer = buf.writer();
2930
2931 if (self.getSectionByName("__TEXT", "__stubs")) |sect_id| {
2932 const stubs = &self.sections.items(.header)[sect_id];
2933 stubs.reserved1 = 0;
2934 for (self.stubs.items) |entry| {
2935 const target_sym = entry.getTargetSymbol(self);
2936 assert(target_sym.undf());
2937 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
2938 }
2939 }
2940
2941 if (self.getSectionByName("__DATA_CONST", "__got")) |sect_id| {
2942 const got = &self.sections.items(.header)[sect_id];
2943 got.reserved1 = nstubs;
2944 for (self.got_entries.items) |entry| {
2945 const target_sym = entry.getTargetSymbol(self);
2946 if (target_sym.undf()) {
2947 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
2948 } else {
2949 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
2950 }
2951 }
2952 }
2953
2954 if (self.getSectionByName("__DATA", "__la_symbol_ptr")) |sect_id| {
2955 const la_symbol_ptr = &self.sections.items(.header)[sect_id];
2956 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
2957 for (self.stubs.items) |entry| {
2958 const target_sym = entry.getTargetSymbol(self);
2959 assert(target_sym.undf());
2960 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
2961 }
2962 }
2963
2964 assert(buf.items.len == needed_size);
2965 try self.file.pwriteAll(buf.items, offset);
2966
2967 lc.nlocalsym = ctx.nlocalsym;
2968 lc.iextdefsym = iextdefsym;
2969 lc.nextdefsym = ctx.nextdefsym;
2970 lc.iundefsym = iundefsym;
2971 lc.nundefsym = ctx.nundefsym;
2972 lc.indirectsymoff = @intCast(u32, offset);
2973 lc.nindirectsyms = nindirectsyms;
2974 }
2975
2976 fn writeCodeSignaturePadding(
2977 self: *Zld,
2978 code_sig: *CodeSignature,
2979 ncmds: *u32,
2980 lc_writer: anytype,
2981 ) !u32 {
2982 const seg = self.getLinkeditSegmentPtr();
2983 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
2984 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
2985 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, 16);
2986 const needed_size = code_sig.estimateSize(offset);
2987 seg.filesize = offset + needed_size - seg.fileoff;
2988 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
2989 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
2990 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
2991 // except for code signature data.
2992 try self.file.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
2993
2994 try lc_writer.writeStruct(macho.linkedit_data_command{
2995 .cmd = .CODE_SIGNATURE,
2996 .cmdsize = @sizeOf(macho.linkedit_data_command),
2997 .dataoff = @intCast(u32, offset),
2998 .datasize = @intCast(u32, needed_size),
2999 });
3000 ncmds.* += 1;
3001
3002 return @intCast(u32, offset);
3003 }
3004
3005 fn writeCodeSignature(self: *Zld, code_sig: *CodeSignature, offset: u32) !void {
3006 const seg_id = self.getSegmentByName("__TEXT").?;
3007 const seg = self.segments.items[seg_id];
3008
3009 var buffer = std.ArrayList(u8).init(self.gpa);
3010 defer buffer.deinit();
3011 try buffer.ensureTotalCapacityPrecise(code_sig.size());
3012 try code_sig.writeAdhocSignature(self.gpa, .{
3013 .file = self.file,
3014 .exec_seg_base = seg.fileoff,
3015 .exec_seg_limit = seg.filesize,
3016 .file_size = offset,
3017 .output_mode = self.options.output_mode,
3018 }, buffer.writer());
3019 assert(buffer.items.len == code_sig.size());
3020
3021 log.debug("writing code signature from 0x{x} to 0x{x}", .{
3022 offset,
3023 offset + buffer.items.len,
3024 });
3025
3026 try self.file.pwriteAll(buffer.items, offset);
3027 }
3028
3029 /// Writes Mach-O file header.
3030 fn writeHeader(self: *Zld, ncmds: u32, sizeofcmds: u32) !void {
3031 var header: macho.mach_header_64 = .{};
3032 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
3033
3034 switch (self.options.target.cpu.arch) {
3035 .aarch64 => {
3036 header.cputype = macho.CPU_TYPE_ARM64;
3037 header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL;
3038 },
3039 .x86_64 => {
3040 header.cputype = macho.CPU_TYPE_X86_64;
3041 header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL;
3042 },
3043 else => return error.UnsupportedCpuArchitecture,
3044 }
3045
3046 switch (self.options.output_mode) {
3047 .Exe => {
3048 header.filetype = macho.MH_EXECUTE;
3049 },
3050 .Lib => {
3051 // By this point, it can only be a dylib.
3052 header.filetype = macho.MH_DYLIB;
3053 header.flags |= macho.MH_NO_REEXPORTED_DYLIBS;
3054 },
3055 else => unreachable,
3056 }
3057
3058 if (self.getSectionByName("__DATA", "__thread_vars")) |sect_id| {
3059 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
3060 if (self.sections.items(.header)[sect_id].size > 0) {
3061 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
3062 }
3063 }
3064
3065 header.ncmds = ncmds;
3066 header.sizeofcmds = sizeofcmds;
3067
3068 log.debug("writing Mach-O header {}", .{header});
3069
3070 try self.file.pwriteAll(mem.asBytes(&header), 0);
3071 }
3072
3073 pub fn makeStaticString(bytes: []const u8) [16]u8 {
3074 var buf = [_]u8{0} ** 16;
3075 assert(bytes.len <= buf.len);
3076 mem.copy(u8, &buf, bytes);
3077 return buf;
3078 }
3079
3080 pub inline fn getAtomPtr(self: *Zld, atom_index: AtomIndex) *Atom {
3081 assert(atom_index < self.atoms.items.len);
3082 return &self.atoms.items[atom_index];
3083 }
3084
3085 pub inline fn getAtom(self: Zld, atom_index: AtomIndex) Atom {
3086 assert(atom_index < self.atoms.items.len);
3087 return self.atoms.items[atom_index];
3088 }
3089
3090 fn getSegmentByName(self: Zld, segname: []const u8) ?u8 {
3091 for (self.segments.items) |seg, i| {
3092 if (mem.eql(u8, segname, seg.segName())) return @intCast(u8, i);
3093 } else return null;
3094 }
3095
3096 pub inline fn getSegment(self: Zld, sect_id: u8) macho.segment_command_64 {
3097 const index = self.sections.items(.segment_index)[sect_id];
3098 return self.segments.items[index];
3099 }
3100
3101 pub inline fn getSegmentPtr(self: *Zld, sect_id: u8) *macho.segment_command_64 {
3102 const index = self.sections.items(.segment_index)[sect_id];
3103 return &self.segments.items[index];
3104 }
3105
3106 pub inline fn getLinkeditSegmentPtr(self: *Zld) *macho.segment_command_64 {
3107 assert(self.segments.items.len > 0);
3108 const seg = &self.segments.items[self.segments.items.len - 1];
3109 assert(mem.eql(u8, seg.segName(), "__LINKEDIT"));
3110 return seg;
3111 }
3112
3113 pub fn getSectionByName(self: Zld, segname: []const u8, sectname: []const u8) ?u8 {
3114 // TODO investigate caching with a hashmap
3115 for (self.sections.items(.header)) |header, i| {
3116 if (mem.eql(u8, header.segName(), segname) and mem.eql(u8, header.sectName(), sectname))
3117 return @intCast(u8, i);
3118 } else return null;
3119 }
3120
3121 pub fn getSectionIndexes(self: Zld, segment_index: u8) struct { start: u8, end: u8 } {
3122 var start: u8 = 0;
3123 const nsects = for (self.segments.items) |seg, i| {
3124 if (i == segment_index) break @intCast(u8, seg.nsects);
3125 start += @intCast(u8, seg.nsects);
3126 } else 0;
3127 return .{ .start = start, .end = start + nsects };
3128 }
3129
3130 pub fn symbolIsTemp(self: *Zld, sym_with_loc: SymbolWithLoc) bool {
3131 const sym = self.getSymbol(sym_with_loc);
3132 if (!sym.sect()) return false;
3133 if (sym.ext()) return false;
3134 const sym_name = self.getSymbolName(sym_with_loc);
3135 return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L");
3136 }
3137
3138 /// Returns pointer-to-symbol described by `sym_with_loc` descriptor.
3139 pub fn getSymbolPtr(self: *Zld, sym_with_loc: SymbolWithLoc) *macho.nlist_64 {
3140 if (sym_with_loc.getFile()) |file| {
3141 const object = &self.objects.items[file];
3142 return &object.symtab[sym_with_loc.sym_index];
3143 } else {
3144 return &self.locals.items[sym_with_loc.sym_index];
3145 }
3146 }
3147
3148 /// Returns symbol described by `sym_with_loc` descriptor.
3149 pub fn getSymbol(self: *Zld, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
3150 return self.getSymbolPtr(sym_with_loc).*;
3151 }
3152
3153 /// Returns name of the symbol described by `sym_with_loc` descriptor.
3154 pub fn getSymbolName(self: *Zld, sym_with_loc: SymbolWithLoc) []const u8 {
3155 if (sym_with_loc.getFile()) |file| {
3156 const object = self.objects.items[file];
3157 return object.getSymbolName(sym_with_loc.sym_index);
3158 } else {
3159 const sym = self.locals.items[sym_with_loc.sym_index];
3160 return self.strtab.get(sym.n_strx).?;
3161 }
3162 }
3163
3164 /// Returns GOT atom that references `sym_with_loc` if one exists.
3165 /// Returns null otherwise.
3166 pub fn getGotAtomIndexForSymbol(self: *Zld, sym_with_loc: SymbolWithLoc) ?AtomIndex {
3167 const index = self.got_table.get(sym_with_loc) orelse return null;
3168 const entry = self.got_entries.items[index];
3169 return entry.atom_index;
3170 }
3171
3172 /// Returns stubs atom that references `sym_with_loc` if one exists.
3173 /// Returns null otherwise.
3174 pub fn getStubsAtomIndexForSymbol(self: *Zld, sym_with_loc: SymbolWithLoc) ?AtomIndex {
3175 const index = self.stubs_table.get(sym_with_loc) orelse return null;
3176 const entry = self.stubs.items[index];
3177 return entry.atom_index;
3178 }
3179
3180 /// Returns TLV pointer atom that references `sym_with_loc` if one exists.
3181 /// Returns null otherwise.
3182 pub fn getTlvPtrAtomIndexForSymbol(self: *Zld, sym_with_loc: SymbolWithLoc) ?AtomIndex {
3183 const index = self.tlv_ptr_table.get(sym_with_loc) orelse return null;
3184 const entry = self.tlv_ptr_entries.items[index];
3185 return entry.atom_index;
3186 }
3187
3188 /// Returns symbol location corresponding to the set entrypoint.
3189 /// Asserts output mode is executable.
3190 pub fn getEntryPoint(self: Zld) SymbolWithLoc {
3191 assert(self.options.output_mode == .Exe);
3192 const global_index = self.entry_index.?;
3193 return self.globals.items[global_index];
3194 }
3195
3196 inline fn requiresThunks(self: Zld) bool {
3197 return self.options.target.cpu.arch == .aarch64;
3198 }
3199
3200 pub fn generateSymbolStabs(self: *Zld, object: Object, locals: *std.ArrayList(macho.nlist_64)) !void {
3201 log.debug("generating stabs for '{s}'", .{object.name});
3202
3203 const gpa = self.gpa;
3204 var debug_info = object.parseDwarfInfo();
3205
3206 var lookup = DwarfInfo.AbbrevLookupTable.init(gpa);
3207 defer lookup.deinit();
3208 try lookup.ensureUnusedCapacity(std.math.maxInt(u8));
3209
3210 // We assume there is only one CU.
3211 var cu_it = debug_info.getCompileUnitIterator();
3212 const compile_unit = while (try cu_it.next()) |cu| {
3213 try debug_info.genAbbrevLookupByKind(cu.cuh.debug_abbrev_offset, &lookup);
3214 break cu;
3215 } else {
3216 log.debug("no compile unit found in debug info in {s}; skipping", .{object.name});
3217 return;
3218 };
3219
3220 var abbrev_it = compile_unit.getAbbrevEntryIterator(debug_info);
3221 const cu_entry: DwarfInfo.AbbrevEntry = while (try abbrev_it.next(lookup)) |entry| switch (entry.tag) {
3222 dwarf.TAG.compile_unit => break entry,
3223 else => continue,
3224 } else {
3225 log.debug("missing DWARF_TAG_compile_unit tag in {s}; skipping", .{object.name});
3226 return;
3227 };
3228
3229 var maybe_tu_name: ?[]const u8 = null;
3230 var maybe_tu_comp_dir: ?[]const u8 = null;
3231 var attr_it = cu_entry.getAttributeIterator(debug_info, compile_unit.cuh);
3232
3233 while (try attr_it.next()) |attr| switch (attr.name) {
3234 dwarf.AT.comp_dir => maybe_tu_comp_dir = attr.getString(debug_info, compile_unit.cuh) orelse continue,
3235 dwarf.AT.name => maybe_tu_name = attr.getString(debug_info, compile_unit.cuh) orelse continue,
3236 else => continue,
3237 };
3238
3239 if (maybe_tu_name == null or maybe_tu_comp_dir == null) {
3240 log.debug("missing DWARF_AT_comp_dir and DWARF_AT_name attributes {s}; skipping", .{object.name});
3241 return;
3242 }
3243
3244 const tu_name = maybe_tu_name.?;
3245 const tu_comp_dir = maybe_tu_comp_dir.?;
3246
3247 // Open scope
3248 try locals.ensureUnusedCapacity(3);
3249 locals.appendAssumeCapacity(.{
3250 .n_strx = try self.strtab.insert(gpa, tu_comp_dir),
3251 .n_type = macho.N_SO,
3252 .n_sect = 0,
3253 .n_desc = 0,
3254 .n_value = 0,
3255 });
3256 locals.appendAssumeCapacity(.{
3257 .n_strx = try self.strtab.insert(gpa, tu_name),
3258 .n_type = macho.N_SO,
3259 .n_sect = 0,
3260 .n_desc = 0,
3261 .n_value = 0,
3262 });
3263 locals.appendAssumeCapacity(.{
3264 .n_strx = try self.strtab.insert(gpa, object.name),
3265 .n_type = macho.N_OSO,
3266 .n_sect = 0,
3267 .n_desc = 1,
3268 .n_value = object.mtime,
3269 });
3270
3271 var stabs_buf: [4]macho.nlist_64 = undefined;
3272
3273 var name_lookup: ?DwarfInfo.SubprogramLookupByName = if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS == 0) blk: {
3274 var name_lookup = DwarfInfo.SubprogramLookupByName.init(gpa);
3275 errdefer name_lookup.deinit();
3276 try name_lookup.ensureUnusedCapacity(@intCast(u32, object.atoms.items.len));
3277 try debug_info.genSubprogramLookupByName(compile_unit, lookup, &name_lookup);
3278 break :blk name_lookup;
3279 } else null;
3280 defer if (name_lookup) |*nl| nl.deinit();
3281
3282 for (object.atoms.items) |atom_index| {
3283 const atom = self.getAtom(atom_index);
3284 const stabs = try self.generateSymbolStabsForSymbol(
3285 atom_index,
3286 atom.getSymbolWithLoc(),
3287 name_lookup,
3288 &stabs_buf,
3289 );
3290 try locals.appendSlice(stabs);
3291
3292 var it = Atom.getInnerSymbolsIterator(self, atom_index);
3293 while (it.next()) |sym_loc| {
3294 const contained_stabs = try self.generateSymbolStabsForSymbol(
3295 atom_index,
3296 sym_loc,
3297 name_lookup,
3298 &stabs_buf,
3299 );
3300 try locals.appendSlice(contained_stabs);
3301 }
3302 }
3303
3304 // Close scope
3305 try locals.append(.{
3306 .n_strx = 0,
3307 .n_type = macho.N_SO,
3308 .n_sect = 0,
3309 .n_desc = 0,
3310 .n_value = 0,
3311 });
3312 }
3313
3314 fn generateSymbolStabsForSymbol(
3315 self: *Zld,
3316 atom_index: AtomIndex,
3317 sym_loc: SymbolWithLoc,
3318 lookup: ?DwarfInfo.SubprogramLookupByName,
3319 buf: *[4]macho.nlist_64,
3320 ) ![]const macho.nlist_64 {
3321 const gpa = self.gpa;
3322 const object = self.objects.items[sym_loc.getFile().?];
3323 const sym = self.getSymbol(sym_loc);
3324 const sym_name = self.getSymbolName(sym_loc);
3325 const header = self.sections.items(.header)[sym.n_sect - 1];
3326
3327 if (sym.n_strx == 0) return buf[0..0];
3328 if (self.symbolIsTemp(sym_loc)) return buf[0..0];
3329
3330 if (!header.isCode()) {
3331 // Since we are not dealing with machine code, it's either a global or a static depending
3332 // on the linkage scope.
3333 if (sym.sect() and sym.ext()) {
3334 // Global gets an N_GSYM stab type.
3335 buf[0] = .{
3336 .n_strx = try self.strtab.insert(gpa, sym_name),
3337 .n_type = macho.N_GSYM,
3338 .n_sect = sym.n_sect,
3339 .n_desc = 0,
3340 .n_value = 0,
3341 };
3342 } else {
3343 // Local static gets an N_STSYM stab type.
3344 buf[0] = .{
3345 .n_strx = try self.strtab.insert(gpa, sym_name),
3346 .n_type = macho.N_STSYM,
3347 .n_sect = sym.n_sect,
3348 .n_desc = 0,
3349 .n_value = sym.n_value,
3350 };
3351 }
3352 return buf[0..1];
3353 }
3354
3355 const size: u64 = size: {
3356 if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) {
3357 break :size self.getAtom(atom_index).size;
3358 }
3359
3360 // Since we don't have subsections to work with, we need to infer the size of each function
3361 // the slow way by scanning the debug info for matching symbol names and extracting
3362 // the symbol's DWARF_AT_low_pc and DWARF_AT_high_pc values.
3363 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
3364 const subprogram = lookup.?.get(sym_name[1..]) orelse return buf[0..0];
3365
3366 if (subprogram.addr <= source_sym.n_value and source_sym.n_value < subprogram.addr + subprogram.size) {
3367 break :size subprogram.size;
3368 } else {
3369 log.debug("no stab found for {s}", .{sym_name});
3370 return buf[0..0];
3371 }
3372 };
3373
3374 buf[0] = .{
3375 .n_strx = 0,
3376 .n_type = macho.N_BNSYM,
3377 .n_sect = sym.n_sect,
3378 .n_desc = 0,
3379 .n_value = sym.n_value,
3380 };
3381 buf[1] = .{
3382 .n_strx = try self.strtab.insert(gpa, sym_name),
3383 .n_type = macho.N_FUN,
3384 .n_sect = sym.n_sect,
3385 .n_desc = 0,
3386 .n_value = sym.n_value,
3387 };
3388 buf[2] = .{
3389 .n_strx = 0,
3390 .n_type = macho.N_FUN,
3391 .n_sect = 0,
3392 .n_desc = 0,
3393 .n_value = size,
3394 };
3395 buf[3] = .{
3396 .n_strx = 0,
3397 .n_type = macho.N_ENSYM,
3398 .n_sect = sym.n_sect,
3399 .n_desc = 0,
3400 .n_value = size,
3401 };
3402
3403 return buf;
3404 }
3405
3406 fn logSegments(self: *Zld) void {
3407 log.debug("segments:", .{});
3408 for (self.segments.items) |segment, i| {
3409 log.debug(" segment({d}): {s} @{x} ({x}), sizeof({x})", .{
3410 i,
3411 segment.segName(),
3412 segment.fileoff,
3413 segment.vmaddr,
3414 segment.vmsize,
3415 });
3416 }
3417 }
3418
3419 fn logSections(self: *Zld) void {
3420 log.debug("sections:", .{});
3421 for (self.sections.items(.header)) |header, i| {
3422 log.debug(" sect({d}): {s},{s} @{x} ({x}), sizeof({x})", .{
3423 i + 1,
3424 header.segName(),
3425 header.sectName(),
3426 header.offset,
3427 header.addr,
3428 header.size,
3429 });
3430 }
3431 }
3432
3433 fn logSymAttributes(sym: macho.nlist_64, buf: []u8) []const u8 {
3434 if (sym.sect()) {
3435 buf[0] = 's';
3436 }
3437 if (sym.ext()) {
3438 if (sym.weakDef() or sym.pext()) {
3439 buf[1] = 'w';
3440 } else {
3441 buf[1] = 'e';
3442 }
3443 }
3444 if (sym.tentative()) {
3445 buf[2] = 't';
3446 }
3447 if (sym.undf()) {
3448 buf[3] = 'u';
3449 }
3450 return buf[0..];
3451 }
3452
3453 fn logSymtab(self: *Zld) void {
3454 var buf: [4]u8 = undefined;
3455
3456 const scoped_log = std.log.scoped(.symtab);
3457
3458 scoped_log.debug("locals:", .{});
3459 for (self.objects.items) |object, id| {
3460 scoped_log.debug(" object({d}): {s}", .{ id, object.name });
3461 for (object.symtab) |sym, sym_id| {
3462 mem.set(u8, &buf, '_');
3463 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{
3464 sym_id,
3465 object.getSymbolName(@intCast(u32, sym_id)),
3466 sym.n_value,
3467 sym.n_sect,
3468 logSymAttributes(sym, &buf),
3469 });
3470 }
3471 }
3472 scoped_log.debug(" object(null)", .{});
3473 for (self.locals.items) |sym, sym_id| {
3474 if (sym.undf()) continue;
3475 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{
3476 sym_id,
3477 self.strtab.get(sym.n_strx).?,
3478 sym.n_value,
3479 sym.n_sect,
3480 logSymAttributes(sym, &buf),
3481 });
3482 }
3483
3484 scoped_log.debug("exports:", .{});
3485 for (self.globals.items) |global, i| {
3486 const sym = self.getSymbol(global);
3487 if (sym.undf()) continue;
3488 if (sym.n_desc == N_DEAD) continue;
3489 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s} (def in object({?}))", .{
3490 i,
3491 self.getSymbolName(global),
3492 sym.n_value,
3493 sym.n_sect,
3494 logSymAttributes(sym, &buf),
3495 global.file,
3496 });
3497 }
3498
3499 scoped_log.debug("imports:", .{});
3500 for (self.globals.items) |global, i| {
3501 const sym = self.getSymbol(global);
3502 if (!sym.undf()) continue;
3503 if (sym.n_desc == N_DEAD) continue;
3504 const ord = @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER);
3505 scoped_log.debug(" %{d}: {s} @{x} in ord({d}), {s}", .{
3506 i,
3507 self.getSymbolName(global),
3508 sym.n_value,
3509 ord,
3510 logSymAttributes(sym, &buf),
3511 });
3512 }
3513
3514 scoped_log.debug("GOT entries:", .{});
3515 for (self.got_entries.items) |entry, i| {
3516 const atom_sym = entry.getAtomSymbol(self);
3517 const target_sym = entry.getTargetSymbol(self);
3518 const target_sym_name = entry.getTargetSymbolName(self);
3519 if (target_sym.undf()) {
3520 scoped_log.debug(" {d}@{x} => import('{s}')", .{
3521 i,
3522 atom_sym.n_value,
3523 target_sym_name,
3524 });
3525 } else {
3526 scoped_log.debug(" {d}@{x} => local(%{d}) in object({?}) {s}", .{
3527 i,
3528 atom_sym.n_value,
3529 entry.target.sym_index,
3530 entry.target.file,
3531 logSymAttributes(target_sym, buf[0..4]),
3532 });
3533 }
3534 }
3535
3536 scoped_log.debug("__thread_ptrs entries:", .{});
3537 for (self.tlv_ptr_entries.items) |entry, i| {
3538 const atom_sym = entry.getAtomSymbol(self);
3539 const target_sym = entry.getTargetSymbol(self);
3540 const target_sym_name = entry.getTargetSymbolName(self);
3541 assert(target_sym.undf());
3542 scoped_log.debug(" {d}@{x} => import('{s}')", .{
3543 i,
3544 atom_sym.n_value,
3545 target_sym_name,
3546 });
3547 }
3548
3549 scoped_log.debug("stubs entries:", .{});
3550 for (self.stubs.items) |entry, i| {
3551 const atom_sym = entry.getAtomSymbol(self);
3552 const target_sym = entry.getTargetSymbol(self);
3553 const target_sym_name = entry.getTargetSymbolName(self);
3554 assert(target_sym.undf());
3555 scoped_log.debug(" {d}@{x} => import('{s}')", .{
3556 i,
3557 atom_sym.n_value,
3558 target_sym_name,
3559 });
3560 }
3561
3562 scoped_log.debug("thunks:", .{});
3563 for (self.thunks.items) |thunk, i| {
3564 scoped_log.debug(" thunk({d})", .{i});
3565 for (thunk.lookup.keys()) |target, j| {
3566 const target_sym = self.getSymbol(target);
3567 const atom = self.getAtom(thunk.lookup.get(target).?);
3568 const atom_sym = self.getSymbol(atom.getSymbolWithLoc());
3569 scoped_log.debug(" {d}@{x} => thunk('{s}'@{x})", .{
3570 j,
3571 atom_sym.n_value,
3572 self.getSymbolName(target),
3573 target_sym.n_value,
3574 });
3575 }
3576 }
3577 }
3578
3579 fn logAtoms(self: *Zld) void {
3580 log.debug("atoms:", .{});
3581 const slice = self.sections.slice();
3582 for (slice.items(.first_atom_index)) |first_atom_index, sect_id| {
3583 var atom_index = first_atom_index;
3584 const header = slice.items(.header)[sect_id];
3585
3586 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
3587
3588 while (true) {
3589 const atom = self.getAtom(atom_index);
3590 self.logAtom(atom_index, log);
3591
3592 if (atom.next_index) |next_index| {
3593 atom_index = next_index;
3594 } else break;
3595 }
3596 }
3597 }
3598
3599 pub fn logAtom(self: *Zld, atom_index: AtomIndex, logger: anytype) void {
3600 if (!build_options.enable_logging) return;
3601
3602 const atom = self.getAtom(atom_index);
3603 const sym = self.getSymbol(atom.getSymbolWithLoc());
3604 const sym_name = self.getSymbolName(atom.getSymbolWithLoc());
3605 logger.debug(" ATOM({d}, %{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?}) in sect({d})", .{
3606 atom_index,
3607 atom.sym_index,
3608 sym_name,
3609 sym.n_value,
3610 atom.size,
3611 atom.alignment,
3612 atom.file,
3613 sym.n_sect,
3614 });
3615
3616 if (atom.getFile()) |_| {
3617 var it = Atom.getInnerSymbolsIterator(self, atom_index);
3618 while (it.next()) |sym_loc| {
3619 const inner = self.getSymbol(sym_loc);
3620 const inner_name = self.getSymbolName(sym_loc);
3621 const offset = Atom.calcInnerSymbolOffset(self, atom_index, sym_loc.sym_index);
3622
3623 logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{
3624 sym_loc.sym_index,
3625 inner_name,
3626 inner.n_value,
3627 offset,
3628 });
3629 }
3630
3631 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
3632 const alias = self.getSymbol(sym_loc);
3633 const alias_name = self.getSymbolName(sym_loc);
3634
3635 logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{
3636 sym_loc.sym_index,
3637 alias_name,
3638 alias.n_value,
3639 0,
3640 });
3641 }
3642 }
3643 }
3644};
3645
3646pub const N_DEAD: u16 = @bitCast(u16, @as(i16, -1));
3647
3648const Section = struct {
3649 header: macho.section_64,
3650 segment_index: u8,
3651 first_atom_index: AtomIndex,
3652 last_atom_index: AtomIndex,
3653};
3654
3655pub const AtomIndex = u32;
3656
3657const IndirectPointer = struct {
3658 target: SymbolWithLoc,
3659 atom_index: AtomIndex,
3660
3661 pub fn getTargetSymbol(self: @This(), zld: *Zld) macho.nlist_64 {
3662 return zld.getSymbol(self.target);
3663 }
3664
3665 pub fn getTargetSymbolName(self: @This(), zld: *Zld) []const u8 {
3666 return zld.getSymbolName(self.target);
3667 }
3668
3669 pub fn getAtomSymbol(self: @This(), zld: *Zld) macho.nlist_64 {
3670 const atom = zld.getAtom(self.atom_index);
3671 return zld.getSymbol(atom.getSymbolWithLoc());
3672 }
3673};
3674
3675pub const SymbolWithLoc = struct {
3676 // Index into the respective symbol table.
3677 sym_index: u32,
3678
3679 // -1 means it's a synthetic global.
3680 file: i32 = -1,
3681
3682 pub inline fn getFile(self: SymbolWithLoc) ?u31 {
3683 if (self.file == -1) return null;
3684 return @intCast(u31, self.file);
3685 }
3686
3687 pub inline fn eql(self: SymbolWithLoc, other: SymbolWithLoc) bool {
3688 return self.file == other.file and self.sym_index == other.sym_index;
3689 }
3690};
3691
3692const SymbolResolver = struct {
3693 arena: Allocator,
3694 table: std.StringHashMap(u32),
3695 unresolved: std.AutoArrayHashMap(u32, void),
3696};
3697
3698pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {
3699 const tracy = trace(@src());
3700 defer tracy.end();
3701
3702 const gpa = macho_file.base.allocator;
3703 const options = macho_file.base.options;
3704 const target = options.target;
3705
3706 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
3707 defer arena_allocator.deinit();
3708 const arena = arena_allocator.allocator();
3709
3710 const directory = options.emit.?.directory; // Just an alias to make it shorter to type.
3711 const full_out_path = try directory.join(arena, &[_][]const u8{options.emit.?.sub_path});
3712
3713 // If there is no Zig code to compile, then we should skip flushing the output file because it
3714 // will not be part of the linker line anyway.
3715 const module_obj_path: ?[]const u8 = if (options.module) |module| blk: {
3716 if (options.use_stage1) {
3717 const obj_basename = try std.zig.binNameAlloc(arena, .{
3718 .root_name = options.root_name,
3719 .target = target,
3720 .output_mode = .Obj,
3721 });
3722 switch (options.cache_mode) {
3723 .incremental => break :blk try module.zig_cache_artifact_directory.join(
3724 arena,
3725 &[_][]const u8{obj_basename},
3726 ),
3727 .whole => break :blk try fs.path.join(arena, &.{
3728 fs.path.dirname(full_out_path).?, obj_basename,
3729 }),
3730 }
3731 }
3732
3733 try macho_file.flushModule(comp, prog_node);
3734
3735 if (fs.path.dirname(full_out_path)) |dirname| {
3736 break :blk try fs.path.join(arena, &.{ dirname, macho_file.base.intermediary_basename.? });
3737 } else {
3738 break :blk macho_file.base.intermediary_basename.?;
3739 }
3740 } else null;
3741
3742 var sub_prog_node = prog_node.start("MachO Flush", 0);
3743 sub_prog_node.activate();
3744 sub_prog_node.context.refresh();
3745 defer sub_prog_node.end();
3746
3747 const cpu_arch = target.cpu.arch;
3748 const os_tag = target.os.tag;
3749 const abi = target.abi;
3750 const is_lib = options.output_mode == .Lib;
3751 const is_dyn_lib = options.link_mode == .Dynamic and is_lib;
3752 const is_exe_or_dyn_lib = is_dyn_lib or options.output_mode == .Exe;
3753 const stack_size = options.stack_size_override orelse 0;
3754 const is_debug_build = options.optimize_mode == .Debug;
3755 const gc_sections = options.gc_sections orelse !is_debug_build;
3756
3757 const id_symlink_basename = "zld.id";
3758
3759 var man: Cache.Manifest = undefined;
3760 defer if (!options.disable_lld_caching) man.deinit();
3761
3762 var digest: [Cache.hex_digest_len]u8 = undefined;
3763
3764 if (!options.disable_lld_caching) {
3765 man = comp.cache_parent.obtain();
3766
3767 // We are about to obtain this lock, so here we give other processes a chance first.
3768 macho_file.base.releaseLock();
3769
3770 comptime assert(Compilation.link_hash_implementation_version == 7);
3771
3772 for (options.objects) |obj| {
3773 _ = try man.addFile(obj.path, null);
3774 man.hash.add(obj.must_link);
3775 }
3776 for (comp.c_object_table.keys()) |key| {
3777 _ = try man.addFile(key.status.success.object_path, null);
3778 }
3779 try man.addOptionalFile(module_obj_path);
3780 // We can skip hashing libc and libc++ components that we are in charge of building from Zig
3781 // installation sources because they are always a product of the compiler version + target information.
3782 man.hash.add(stack_size);
3783 man.hash.addOptional(options.pagezero_size);
3784 man.hash.addOptional(options.search_strategy);
3785 man.hash.addOptional(options.headerpad_size);
3786 man.hash.add(options.headerpad_max_install_names);
3787 man.hash.add(gc_sections);
3788 man.hash.add(options.dead_strip_dylibs);
3789 man.hash.add(options.strip);
3790 man.hash.addListOfBytes(options.lib_dirs);
3791 man.hash.addListOfBytes(options.framework_dirs);
3792 link.hashAddSystemLibs(&man.hash, options.frameworks);
3793 man.hash.addListOfBytes(options.rpath_list);
3794 if (is_dyn_lib) {
3795 man.hash.addOptionalBytes(options.install_name);
3796 man.hash.addOptional(options.version);
3797 }
3798 link.hashAddSystemLibs(&man.hash, options.system_libs);
3799 man.hash.addOptionalBytes(options.sysroot);
3800 try man.addOptionalFile(options.entitlements);
3801
3802 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
3803 _ = try man.hit();
3804 digest = man.final();
3805
3806 var prev_digest_buf: [digest.len]u8 = undefined;
3807 const prev_digest: []u8 = Cache.readSmallFile(
3808 directory.handle,
3809 id_symlink_basename,
3810 &prev_digest_buf,
3811 ) catch |err| blk: {
3812 log.debug("MachO Zld new_digest={s} error: {s}", .{
3813 std.fmt.fmtSliceHexLower(&digest),
3814 @errorName(err),
3815 });
3816 // Handle this as a cache miss.
3817 break :blk prev_digest_buf[0..0];
3818 };
3819 if (mem.eql(u8, prev_digest, &digest)) {
3820 // Hot diggity dog! The output binary is already there.
3821 log.debug("MachO Zld digest={s} match - skipping invocation", .{
3822 std.fmt.fmtSliceHexLower(&digest),
3823 });
3824 macho_file.base.lock = man.toOwnedLock();
3825 return;
3826 }
3827 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{
3828 std.fmt.fmtSliceHexLower(prev_digest),
3829 std.fmt.fmtSliceHexLower(&digest),
3830 });
3831
3832 // We are about to change the output file to be different, so we invalidate the build hash now.
3833 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
3834 error.FileNotFound => {},
3835 else => |e| return e,
3836 };
3837 }
3838
3839 if (options.output_mode == .Obj) {
3840 // LLD's MachO driver does not support the equivalent of `-r` so we do a simple file copy
3841 // here. TODO: think carefully about how we can avoid this redundant operation when doing
3842 // build-obj. See also the corresponding TODO in linkAsArchive.
3843 const the_object_path = blk: {
3844 if (options.objects.len != 0) {
3845 break :blk options.objects[0].path;
3846 }
3847
3848 if (comp.c_object_table.count() != 0)
3849 break :blk comp.c_object_table.keys()[0].status.success.object_path;
3850
3851 if (module_obj_path) |p|
3852 break :blk p;
3853
3854 // TODO I think this is unreachable. Audit this situation when solving the above TODO
3855 // regarding eliding redundant object -> object transformations.
3856 return error.NoObjectsToLink;
3857 };
3858 // This can happen when using --enable-cache and using the stage1 backend. In this case
3859 // we can skip the file copy.
3860 if (!mem.eql(u8, the_object_path, full_out_path)) {
3861 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
3862 }
3863 } else {
3864 const page_size = macho_file.page_size;
3865 const sub_path = options.emit.?.sub_path;
3866 if (macho_file.base.file == null) {
3867 macho_file.base.file = try directory.handle.createFile(sub_path, .{
3868 .truncate = true,
3869 .read = true,
3870 .mode = link.determineMode(options),
3871 });
3872 }
3873 var zld = Zld{
3874 .gpa = gpa,
3875 .file = macho_file.base.file.?,
3876 .page_size = macho_file.page_size,
3877 .options = options,
3878 };
3879 defer zld.deinit();
3880
3881 try zld.atoms.append(gpa, Atom.empty); // AtomIndex at 0 is reserved as null atom
3882 try zld.strtab.buffer.append(gpa, 0);
3883
3884 var lib_not_found = false;
3885 var framework_not_found = false;
3886
3887 // Positional arguments to the linker such as object files and static archives.
3888 var positionals = std.ArrayList([]const u8).init(arena);
3889 try positionals.ensureUnusedCapacity(options.objects.len);
3890
3891 var must_link_archives = std.StringArrayHashMap(void).init(arena);
3892 try must_link_archives.ensureUnusedCapacity(options.objects.len);
3893
3894 for (options.objects) |obj| {
3895 if (must_link_archives.contains(obj.path)) continue;
3896 if (obj.must_link) {
3897 _ = must_link_archives.getOrPutAssumeCapacity(obj.path);
3898 } else {
3899 _ = positionals.appendAssumeCapacity(obj.path);
3900 }
3901 }
3902
3903 for (comp.c_object_table.keys()) |key| {
3904 try positionals.append(key.status.success.object_path);
3905 }
3906
3907 if (module_obj_path) |p| {
3908 try positionals.append(p);
3909 }
3910
3911 if (comp.compiler_rt_lib) |lib| {
3912 try positionals.append(lib.full_object_path);
3913 }
3914
3915 // libc++ dep
3916 if (options.link_libcpp) {
3917 try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
3918 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
3919 }
3920
3921 // Shared and static libraries passed via `-l` flag.
3922 var candidate_libs = std.StringArrayHashMap(link.SystemLib).init(arena);
3923
3924 const system_lib_names = options.system_libs.keys();
3925 for (system_lib_names) |system_lib_name| {
3926 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
3927 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
3928 // case we want to avoid prepending "-l".
3929 if (Compilation.classifyFileExt(system_lib_name) == .shared_library) {
3930 try positionals.append(system_lib_name);
3931 continue;
3932 }
3933
3934 const system_lib_info = options.system_libs.get(system_lib_name).?;
3935 try candidate_libs.put(system_lib_name, .{
3936 .needed = system_lib_info.needed,
3937 .weak = system_lib_info.weak,
3938 });
3939 }
3940
3941 var lib_dirs = std.ArrayList([]const u8).init(arena);
3942 for (options.lib_dirs) |dir| {
3943 if (try MachO.resolveSearchDir(arena, dir, options.sysroot)) |search_dir| {
3944 try lib_dirs.append(search_dir);
3945 } else {
3946 log.warn("directory not found for '-L{s}'", .{dir});
3947 }
3948 }
3949
3950 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
3951
3952 // Assume ld64 default -search_paths_first if no strategy specified.
3953 const search_strategy = options.search_strategy orelse .paths_first;
3954 outer: for (candidate_libs.keys()) |lib_name| {
3955 switch (search_strategy) {
3956 .paths_first => {
3957 // Look in each directory for a dylib (stub first), and then for archive
3958 for (lib_dirs.items) |dir| {
3959 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
3960 if (try MachO.resolveLib(arena, dir, lib_name, ext)) |full_path| {
3961 try libs.put(full_path, candidate_libs.get(lib_name).?);
3962 continue :outer;
3963 }
3964 }
3965 } else {
3966 log.warn("library not found for '-l{s}'", .{lib_name});
3967 lib_not_found = true;
3968 }
3969 },
3970 .dylibs_first => {
3971 // First, look for a dylib in each search dir
3972 for (lib_dirs.items) |dir| {
3973 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
3974 if (try MachO.resolveLib(arena, dir, lib_name, ext)) |full_path| {
3975 try libs.put(full_path, candidate_libs.get(lib_name).?);
3976 continue :outer;
3977 }
3978 }
3979 } else for (lib_dirs.items) |dir| {
3980 if (try MachO.resolveLib(arena, dir, lib_name, ".a")) |full_path| {
3981 try libs.put(full_path, candidate_libs.get(lib_name).?);
3982 } else {
3983 log.warn("library not found for '-l{s}'", .{lib_name});
3984 lib_not_found = true;
3985 }
3986 }
3987 },
3988 }
3989 }
3990
3991 if (lib_not_found) {
3992 log.warn("Library search paths:", .{});
3993 for (lib_dirs.items) |dir| {
3994 log.warn(" {s}", .{dir});
3995 }
3996 }
3997
3998 try MachO.resolveLibSystem(arena, comp, options.sysroot, target, lib_dirs.items, &libs);
3999
4000 // frameworks
4001 var framework_dirs = std.ArrayList([]const u8).init(arena);
4002 for (options.framework_dirs) |dir| {
4003 if (try MachO.resolveSearchDir(arena, dir, options.sysroot)) |search_dir| {
4004 try framework_dirs.append(search_dir);
4005 } else {
4006 log.warn("directory not found for '-F{s}'", .{dir});
4007 }
4008 }
4009
4010 outer: for (options.frameworks.keys()) |f_name| {
4011 for (framework_dirs.items) |dir| {
4012 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
4013 if (try MachO.resolveFramework(arena, dir, f_name, ext)) |full_path| {
4014 const info = options.frameworks.get(f_name).?;
4015 try libs.put(full_path, .{
4016 .needed = info.needed,
4017 .weak = info.weak,
4018 });
4019 continue :outer;
4020 }
4021 }
4022 } else {
4023 log.warn("framework not found for '-framework {s}'", .{f_name});
4024 framework_not_found = true;
4025 }
4026 }
4027
4028 if (framework_not_found) {
4029 log.warn("Framework search paths:", .{});
4030 for (framework_dirs.items) |dir| {
4031 log.warn(" {s}", .{dir});
4032 }
4033 }
4034
4035 if (options.verbose_link) {
4036 var argv = std.ArrayList([]const u8).init(arena);
4037
4038 try argv.append("zig");
4039 try argv.append("ld");
4040
4041 if (is_exe_or_dyn_lib) {
4042 try argv.append("-dynamic");
4043 }
4044
4045 if (is_dyn_lib) {
4046 try argv.append("-dylib");
13264047
1327 var offsets = std.ArrayList(u32).init(gpa);
1328 defer offsets.deinit();
1329 try offsets.ensureTotalCapacityPrecise(addresses.items.len);
4048 if (options.install_name) |install_name| {
4049 try argv.append("-install_name");
4050 try argv.append(install_name);
4051 }
4052 }
13304053
1331 var last_off: u32 = 0;
1332 for (addresses.items) |addr| {
1333 const offset = @intCast(u32, addr - text_seg.vmaddr);
1334 const diff = offset - last_off;
4054 if (options.sysroot) |syslibroot| {
4055 try argv.append("-syslibroot");
4056 try argv.append(syslibroot);
4057 }
13354058
1336 if (diff == 0) continue;
4059 for (options.rpath_list) |rpath| {
4060 try argv.append("-rpath");
4061 try argv.append(rpath);
4062 }
13374063
1338 offsets.appendAssumeCapacity(diff);
1339 last_off = offset;
1340 }
4064 if (options.pagezero_size) |pagezero_size| {
4065 try argv.append("-pagezero_size");
4066 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
4067 }
13414068
1342 var buffer = std.ArrayList(u8).init(gpa);
1343 defer buffer.deinit();
4069 if (options.search_strategy) |strat| switch (strat) {
4070 .paths_first => try argv.append("-search_paths_first"),
4071 .dylibs_first => try argv.append("-search_dylibs_first"),
4072 };
13444073
1345 const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64));
1346 try buffer.ensureTotalCapacity(max_size);
4074 if (options.headerpad_size) |headerpad_size| {
4075 try argv.append("-headerpad_size");
4076 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));
4077 }
13474078
1348 for (offsets.items) |offset| {
1349 try std.leb.writeULEB128(buffer.writer(), offset);
1350 }
4079 if (options.headerpad_max_install_names) {
4080 try argv.append("-headerpad_max_install_names");
4081 }
13514082
1352 const link_seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1353 const offset = mem.alignForwardGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64));
1354 const needed_size = buffer.items.len;
1355 link_seg.filesize = offset + needed_size - link_seg.fileoff;
4083 if (gc_sections) {
4084 try argv.append("-dead_strip");
4085 }
13564086
1357 log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
4087 if (options.dead_strip_dylibs) {
4088 try argv.append("-dead_strip_dylibs");
4089 }
13584090
1359 try macho_file.base.file.?.pwriteAll(buffer.items, offset);
4091 if (options.entry) |entry| {
4092 try argv.append("-e");
4093 try argv.append(entry);
4094 }
13604095
1361 try lc_writer.writeStruct(macho.linkedit_data_command{
1362 .cmd = .FUNCTION_STARTS,
1363 .cmdsize = @sizeOf(macho.linkedit_data_command),
1364 .dataoff = @intCast(u32, offset),
1365 .datasize = @intCast(u32, needed_size),
1366 });
1367 ncmds.* += 1;
1368}
4096 for (options.objects) |obj| {
4097 try argv.append(obj.path);
4098 }
13694099
1370fn filterDataInCode(
1371 dices: []align(1) const macho.data_in_code_entry,
1372 start_addr: u64,
1373 end_addr: u64,
1374) []align(1) const macho.data_in_code_entry {
1375 const Predicate = struct {
1376 addr: u64,
4100 for (comp.c_object_table.keys()) |key| {
4101 try argv.append(key.status.success.object_path);
4102 }
13774103
1378 pub fn predicate(macho_file: @This(), dice: macho.data_in_code_entry) bool {
1379 return dice.offset >= macho_file.addr;
1380 }
1381 };
4104 if (module_obj_path) |p| {
4105 try argv.append(p);
4106 }
13824107
1383 const start = MachO.findFirst(macho.data_in_code_entry, dices, 0, Predicate{ .addr = start_addr });
1384 const end = MachO.findFirst(macho.data_in_code_entry, dices, start, Predicate{ .addr = end_addr });
4108 if (comp.compiler_rt_lib) |lib| {
4109 try argv.append(lib.full_object_path);
4110 }
13854111
1386 return dices[start..end];
1387}
4112 if (options.link_libcpp) {
4113 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
4114 try argv.append(comp.libcxx_static_lib.?.full_object_path);
4115 }
13884116
1389fn writeDataInCode(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1390 const tracy = trace(@src());
1391 defer tracy.end();
4117 try argv.append("-o");
4118 try argv.append(full_out_path);
13924119
1393 var out_dice = std.ArrayList(macho.data_in_code_entry).init(macho_file.base.allocator);
1394 defer out_dice.deinit();
4120 try argv.append("-lSystem");
4121 try argv.append("-lc");
13954122
1396 const text_sect_id = macho_file.text_section_index orelse return;
1397 const text_sect_header = macho_file.sections.items(.header)[text_sect_id];
4123 for (options.system_libs.keys()) |l_name| {
4124 const info = options.system_libs.get(l_name).?;
4125 const arg = if (info.needed)
4126 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})
4127 else if (info.weak)
4128 try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})
4129 else
4130 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
4131 try argv.append(arg);
4132 }
13984133
1399 for (macho_file.objects.items) |object| {
1400 const dice = object.parseDataInCode() orelse continue;
1401 try out_dice.ensureUnusedCapacity(dice.len);
4134 for (options.lib_dirs) |lib_dir| {
4135 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
4136 }
14024137
1403 for (object.managed_atoms.items) |atom| {
1404 const sym = atom.getSymbol(macho_file);
1405 if (sym.n_desc == MachO.N_DESC_GCED) continue;
4138 for (options.frameworks.keys()) |framework| {
4139 const info = options.frameworks.get(framework).?;
4140 const arg = if (info.needed)
4141 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework})
4142 else if (info.weak)
4143 try std.fmt.allocPrint(arena, "-weak_framework {s}", .{framework})
4144 else
4145 try std.fmt.allocPrint(arena, "-framework {s}", .{framework});
4146 try argv.append(arg);
4147 }
14064148
1407 const sect_id = sym.n_sect - 1;
1408 if (sect_id != macho_file.text_section_index.?) {
1409 continue;
4149 for (options.framework_dirs) |framework_dir| {
4150 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
14104151 }
14114152
1412 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
1413 const source_addr = math.cast(u32, source_sym.n_value) orelse return error.Overflow;
1414 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);
1415 const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse
1416 return error.Overflow;
4153 if (is_dyn_lib and (options.allow_shlib_undefined orelse false)) {
4154 try argv.append("-undefined");
4155 try argv.append("dynamic_lookup");
4156 }
14174157
1418 for (filtered_dice) |single| {
1419 const offset = single.offset - source_addr + base;
1420 out_dice.appendAssumeCapacity(.{
1421 .offset = offset,
1422 .length = single.length,
1423 .kind = single.kind,
1424 });
4158 for (must_link_archives.keys()) |lib| {
4159 try argv.append(try std.fmt.allocPrint(arena, "-force_load {s}", .{lib}));
14254160 }
4161
4162 Compilation.dump_argv(argv.items);
14264163 }
1427 }
14284164
1429 const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1430 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
1431 const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
1432 seg.filesize = offset + needed_size - seg.fileoff;
4165 var dependent_libs = std.fifo.LinearFifo(struct {
4166 id: Dylib.Id,
4167 parent: u16,
4168 }, .Dynamic).init(arena);
14334169
1434 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
4170 try zld.parseInputFiles(positionals.items, options.sysroot, &dependent_libs);
4171 try zld.parseAndForceLoadStaticArchives(must_link_archives.keys());
4172 try zld.parseLibs(libs.keys(), libs.values(), options.sysroot, &dependent_libs);
4173 try zld.parseDependentLibs(options.sysroot, &dependent_libs);
14354174
1436 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(out_dice.items), offset);
1437 try lc_writer.writeStruct(macho.linkedit_data_command{
1438 .cmd = .DATA_IN_CODE,
1439 .cmdsize = @sizeOf(macho.linkedit_data_command),
1440 .dataoff = @intCast(u32, offset),
1441 .datasize = @intCast(u32, needed_size),
1442 });
1443 ncmds.* += 1;
1444}
4175 var resolver = SymbolResolver{
4176 .arena = arena,
4177 .table = std.StringHashMap(u32).init(arena),
4178 .unresolved = std.AutoArrayHashMap(u32, void).init(arena),
4179 };
14454180
1446fn writeSymtabs(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1447 var symtab_cmd = macho.symtab_command{
1448 .cmdsize = @sizeOf(macho.symtab_command),
1449 .symoff = 0,
1450 .nsyms = 0,
1451 .stroff = 0,
1452 .strsize = 0,
1453 };
1454 var dysymtab_cmd = macho.dysymtab_command{
1455 .cmdsize = @sizeOf(macho.dysymtab_command),
1456 .ilocalsym = 0,
1457 .nlocalsym = 0,
1458 .iextdefsym = 0,
1459 .nextdefsym = 0,
1460 .iundefsym = 0,
1461 .nundefsym = 0,
1462 .tocoff = 0,
1463 .ntoc = 0,
1464 .modtaboff = 0,
1465 .nmodtab = 0,
1466 .extrefsymoff = 0,
1467 .nextrefsyms = 0,
1468 .indirectsymoff = 0,
1469 .nindirectsyms = 0,
1470 .extreloff = 0,
1471 .nextrel = 0,
1472 .locreloff = 0,
1473 .nlocrel = 0,
1474 };
1475 var ctx = try writeSymtab(macho_file, &symtab_cmd);
1476 defer ctx.imports_table.deinit();
1477 try writeDysymtab(macho_file, ctx, &dysymtab_cmd);
1478 try writeStrtab(macho_file, &symtab_cmd);
1479 try lc_writer.writeStruct(symtab_cmd);
1480 try lc_writer.writeStruct(dysymtab_cmd);
1481 ncmds.* += 2;
1482}
4181 for (zld.objects.items) |_, object_id| {
4182 try zld.resolveSymbolsInObject(@intCast(u16, object_id), &resolver);
4183 }
14834184
1484fn writeSymtab(macho_file: *MachO, lc: *macho.symtab_command) !SymtabCtx {
1485 const gpa = macho_file.base.allocator;
4185 try zld.resolveSymbolsInArchives(&resolver);
4186 try zld.resolveDyldStubBinder(&resolver);
4187 try zld.resolveSymbolsInDylibs(&resolver);
4188 try zld.createMhExecuteHeaderSymbol(&resolver);
4189 try zld.createDsoHandleSymbol(&resolver);
4190 try zld.resolveSymbolsAtLoading(&resolver);
14864191
1487 var locals = std.ArrayList(macho.nlist_64).init(gpa);
1488 defer locals.deinit();
1489
1490 for (macho_file.locals.items) |sym, sym_id| {
1491 if (sym.n_strx == 0) continue; // no name, skip
1492 if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip
1493 const sym_loc = SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = null };
1494 if (macho_file.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
1495 if (macho_file.getGlobal(macho_file.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip
1496 try locals.append(sym);
1497 }
1498
1499 for (macho_file.objects.items) |object, object_id| {
1500 for (object.symtab.items) |sym, sym_id| {
1501 if (sym.n_strx == 0) continue; // no name, skip
1502 if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip
1503 const sym_loc = SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = @intCast(u32, object_id) };
1504 if (macho_file.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
1505 if (macho_file.getGlobal(macho_file.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip
1506 var out_sym = sym;
1507 out_sym.n_strx = try macho_file.strtab.insert(gpa, macho_file.getSymbolName(sym_loc));
1508 try locals.append(out_sym);
4192 if (resolver.unresolved.count() > 0) {
4193 return error.UndefinedSymbolReference;
15094194 }
1510
1511 if (!macho_file.base.options.strip) {
1512 try generateSymbolStabs(macho_file, object, &locals);
4195 if (lib_not_found) {
4196 return error.LibraryNotFound;
4197 }
4198 if (framework_not_found) {
4199 return error.FrameworkNotFound;
15134200 }
1514 }
1515
1516 var exports = std.ArrayList(macho.nlist_64).init(gpa);
1517 defer exports.deinit();
15184201
1519 for (macho_file.globals.items) |global| {
1520 const sym = macho_file.getSymbol(global);
1521 if (sym.undf()) continue; // import, skip
1522 if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip
1523 var out_sym = sym;
1524 out_sym.n_strx = try macho_file.strtab.insert(gpa, macho_file.getSymbolName(global));
1525 try exports.append(out_sym);
1526 }
4202 if (options.output_mode == .Exe) {
4203 const entry_name = options.entry orelse "_main";
4204 const global_index = resolver.table.get(entry_name) orelse {
4205 log.err("entrypoint '{s}' not found", .{entry_name});
4206 return error.MissingMainEntrypoint;
4207 };
4208 zld.entry_index = global_index;
4209 }
15274210
1528 var imports = std.ArrayList(macho.nlist_64).init(gpa);
1529 defer imports.deinit();
4211 for (zld.objects.items) |*object, object_id| {
4212 try object.splitIntoAtoms(&zld, @intCast(u31, object_id));
4213 }
15304214
1531 var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);
4215 var reverse_lookups: [][]u32 = try arena.alloc([]u32, zld.objects.items.len);
4216 for (zld.objects.items) |object, i| {
4217 reverse_lookups[i] = try object.createReverseSymbolLookup(arena);
4218 }
15324219
1533 for (macho_file.globals.items) |global| {
1534 const sym = macho_file.getSymbol(global);
1535 if (sym.n_strx == 0) continue; // no name, skip
1536 if (!sym.undf()) continue; // not an import, skip
1537 const new_index = @intCast(u32, imports.items.len);
1538 var out_sym = sym;
1539 out_sym.n_strx = try macho_file.strtab.insert(gpa, macho_file.getSymbolName(global));
1540 try imports.append(out_sym);
1541 try imports_table.putNoClobber(global, new_index);
1542 }
4220 if (gc_sections) {
4221 try dead_strip.gcAtoms(&zld, reverse_lookups);
4222 }
15434223
1544 const nlocals = @intCast(u32, locals.items.len);
1545 const nexports = @intCast(u32, exports.items.len);
1546 const nimports = @intCast(u32, imports.items.len);
1547 const nsyms = nlocals + nexports + nimports;
4224 try zld.createDyldPrivateAtom();
4225 try zld.createTentativeDefAtoms();
4226 try zld.createStubHelperPreambleAtom();
15484227
1549 const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1550 const offset = mem.alignForwardGeneric(
1551 u64,
1552 seg.fileoff + seg.filesize,
1553 @alignOf(macho.nlist_64),
1554 );
1555 const needed_size = nsyms * @sizeOf(macho.nlist_64);
1556 seg.filesize = offset + needed_size - seg.fileoff;
4228 for (zld.objects.items) |object| {
4229 for (object.atoms.items) |atom_index| {
4230 const atom = zld.getAtom(atom_index);
4231 const sym = zld.getSymbol(atom.getSymbolWithLoc());
4232 const header = zld.sections.items(.header)[sym.n_sect - 1];
4233 if (header.isZerofill()) continue;
15574234
1558 var buffer = std.ArrayList(u8).init(gpa);
1559 defer buffer.deinit();
1560 try buffer.ensureTotalCapacityPrecise(needed_size);
1561 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items));
1562 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items));
1563 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(imports.items));
4235 const relocs = Atom.getAtomRelocs(&zld, atom_index);
4236 try Atom.scanAtomRelocs(&zld, atom_index, relocs, reverse_lookups[atom.getFile().?]);
4237 }
4238 }
15644239
1565 log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
1566 try macho_file.base.file.?.pwriteAll(buffer.items, offset);
4240 try zld.createDyldStubBinderGotAtom();
15674241
1568 lc.symoff = @intCast(u32, offset);
1569 lc.nsyms = nsyms;
4242 try zld.calcSectionSizes(reverse_lookups);
4243 try zld.pruneAndSortSections();
4244 try zld.createSegments();
4245 try zld.allocateSegments();
15704246
1571 return SymtabCtx{
1572 .nlocalsym = nlocals,
1573 .nextdefsym = nexports,
1574 .nundefsym = nimports,
1575 .imports_table = imports_table,
1576 };
1577}
4247 try zld.allocateSpecialSymbols();
15784248
1579fn writeStrtab(macho_file: *MachO, lc: *macho.symtab_command) !void {
1580 const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1581 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
1582 const needed_size = macho_file.strtab.buffer.items.len;
1583 seg.filesize = offset + needed_size - seg.fileoff;
4249 if (build_options.enable_logging) {
4250 zld.logSymtab();
4251 zld.logSegments();
4252 zld.logSections();
4253 zld.logAtoms();
4254 }
15844255
1585 log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
4256 try zld.writeAtoms(reverse_lookups);
15864257
1587 try macho_file.base.file.?.pwriteAll(macho_file.strtab.buffer.items, offset);
4258 var lc_buffer = std.ArrayList(u8).init(arena);
4259 const lc_writer = lc_buffer.writer();
4260 var ncmds: u32 = 0;
15884261
1589 lc.stroff = @intCast(u32, offset);
1590 lc.strsize = @intCast(u32, needed_size);
1591}
4262 try zld.writeLinkeditSegmentData(&ncmds, lc_writer, reverse_lookups);
15924263
1593pub fn generateSymbolStabs(
1594 macho_file: *MachO,
1595 object: Object,
1596 locals: *std.ArrayList(macho.nlist_64),
1597) !void {
1598 assert(!macho_file.base.options.strip);
4264 // If the last section of __DATA segment is zerofill section, we need to ensure
4265 // that the free space between the end of the last non-zerofill section of __DATA
4266 // segment and the beginning of __LINKEDIT segment is zerofilled as the loader will
4267 // copy-paste this space into memory for quicker zerofill operation.
4268 if (zld.getSegmentByName("__DATA")) |data_seg_id| blk: {
4269 var physical_zerofill_start: u64 = 0;
4270 const section_indexes = zld.getSectionIndexes(data_seg_id);
4271 for (zld.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| {
4272 if (header.isZerofill() and header.size > 0) break;
4273 physical_zerofill_start = header.offset + header.size;
4274 } else break :blk;
4275 const linkedit = zld.getLinkeditSegmentPtr();
4276 const physical_zerofill_size = linkedit.fileoff - physical_zerofill_start;
4277 if (physical_zerofill_size > 0) {
4278 var padding = try zld.gpa.alloc(u8, physical_zerofill_size);
4279 defer zld.gpa.free(padding);
4280 mem.set(u8, padding, 0);
4281 try zld.file.pwriteAll(padding, physical_zerofill_start);
4282 }
4283 }
15994284
1600 log.debug("parsing debug info in '{s}'", .{object.name});
4285 try Zld.writeDylinkerLC(&ncmds, lc_writer);
4286 try zld.writeMainLC(&ncmds, lc_writer);
4287 try zld.writeDylibIdLC(&ncmds, lc_writer);
4288 try zld.writeRpathLCs(&ncmds, lc_writer);
16014289
1602 const gpa = macho_file.base.allocator;
1603 var debug_info = try object.parseDwarfInfo();
1604 defer debug_info.deinit(gpa);
1605 try dwarf.openDwarfDebugInfo(&debug_info, gpa);
1606
1607 // We assume there is only one CU.
1608 const compile_unit = debug_info.findCompileUnit(0x0) catch |err| switch (err) {
1609 error.MissingDebugInfo => {
1610 // TODO audit cases with missing debug info and audit our dwarf.zig module.
1611 log.debug("invalid or missing debug info in {s}; skipping", .{object.name});
1612 return;
1613 },
1614 else => |e| return e,
1615 };
4290 {
4291 try lc_writer.writeStruct(macho.source_version_command{
4292 .cmdsize = @sizeOf(macho.source_version_command),
4293 .version = 0x0,
4294 });
4295 ncmds += 1;
4296 }
16164297
1617 const tu_name = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.name, debug_info.debug_str, compile_unit.*);
1618 const tu_comp_dir = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.comp_dir, debug_info.debug_str, compile_unit.*);
1619
1620 // Open scope
1621 try locals.ensureUnusedCapacity(3);
1622 locals.appendAssumeCapacity(.{
1623 .n_strx = try macho_file.strtab.insert(gpa, tu_comp_dir),
1624 .n_type = macho.N_SO,
1625 .n_sect = 0,
1626 .n_desc = 0,
1627 .n_value = 0,
1628 });
1629 locals.appendAssumeCapacity(.{
1630 .n_strx = try macho_file.strtab.insert(gpa, tu_name),
1631 .n_type = macho.N_SO,
1632 .n_sect = 0,
1633 .n_desc = 0,
1634 .n_value = 0,
1635 });
1636 locals.appendAssumeCapacity(.{
1637 .n_strx = try macho_file.strtab.insert(gpa, object.name),
1638 .n_type = macho.N_OSO,
1639 .n_sect = 0,
1640 .n_desc = 1,
1641 .n_value = object.mtime,
1642 });
1643
1644 var stabs_buf: [4]macho.nlist_64 = undefined;
1645
1646 for (object.managed_atoms.items) |atom| {
1647 const stabs = try generateSymbolStabsForSymbol(
1648 macho_file,
1649 atom.getSymbolWithLoc(),
1650 debug_info,
1651 &stabs_buf,
1652 );
1653 try locals.appendSlice(stabs);
4298 try zld.writeBuildVersionLC(&ncmds, lc_writer);
16544299
1655 for (atom.contained.items) |sym_at_off| {
1656 const sym_loc = SymbolWithLoc{
1657 .sym_index = sym_at_off.sym_index,
1658 .file = atom.file,
4300 {
4301 var uuid_lc = macho.uuid_command{
4302 .cmdsize = @sizeOf(macho.uuid_command),
4303 .uuid = undefined,
16594304 };
1660 const contained_stabs = try generateSymbolStabsForSymbol(
1661 macho_file,
1662 sym_loc,
1663 debug_info,
1664 &stabs_buf,
1665 );
1666 try locals.appendSlice(contained_stabs);
4305 std.crypto.random.bytes(&uuid_lc.uuid);
4306 try lc_writer.writeStruct(uuid_lc);
4307 ncmds += 1;
16674308 }
1668 }
1669
1670 // Close scope
1671 try locals.append(.{
1672 .n_strx = 0,
1673 .n_type = macho.N_SO,
1674 .n_sect = 0,
1675 .n_desc = 0,
1676 .n_value = 0,
1677 });
1678}
16794309
1680fn generateSymbolStabsForSymbol(
1681 macho_file: *MachO,
1682 sym_loc: SymbolWithLoc,
1683 debug_info: dwarf.DwarfInfo,
1684 buf: *[4]macho.nlist_64,
1685) ![]const macho.nlist_64 {
1686 const gpa = macho_file.base.allocator;
1687 const object = macho_file.objects.items[sym_loc.file.?];
1688 const sym = macho_file.getSymbol(sym_loc);
1689 const sym_name = macho_file.getSymbolName(sym_loc);
1690
1691 if (sym.n_strx == 0) return buf[0..0];
1692 if (sym.n_desc == MachO.N_DESC_GCED) return buf[0..0];
1693 if (macho_file.symbolIsTemp(sym_loc)) return buf[0..0];
1694
1695 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
1696 const size: ?u64 = size: {
1697 if (source_sym.tentative()) break :size null;
1698 for (debug_info.func_list.items) |func| {
1699 if (func.pc_range) |range| {
1700 if (source_sym.n_value >= range.start and source_sym.n_value < range.end) {
1701 break :size range.end - range.start;
1702 }
1703 }
1704 }
1705 break :size null;
1706 };
4310 try zld.writeLoadDylibLCs(&ncmds, lc_writer);
17074311
1708 if (size) |ss| {
1709 buf[0] = .{
1710 .n_strx = 0,
1711 .n_type = macho.N_BNSYM,
1712 .n_sect = sym.n_sect,
1713 .n_desc = 0,
1714 .n_value = sym.n_value,
1715 };
1716 buf[1] = .{
1717 .n_strx = try macho_file.strtab.insert(gpa, sym_name),
1718 .n_type = macho.N_FUN,
1719 .n_sect = sym.n_sect,
1720 .n_desc = 0,
1721 .n_value = sym.n_value,
1722 };
1723 buf[2] = .{
1724 .n_strx = 0,
1725 .n_type = macho.N_FUN,
1726 .n_sect = 0,
1727 .n_desc = 0,
1728 .n_value = ss,
1729 };
1730 buf[3] = .{
1731 .n_strx = 0,
1732 .n_type = macho.N_ENSYM,
1733 .n_sect = sym.n_sect,
1734 .n_desc = 0,
1735 .n_value = ss,
1736 };
1737 return buf;
1738 } else {
1739 buf[0] = .{
1740 .n_strx = try macho_file.strtab.insert(gpa, sym_name),
1741 .n_type = macho.N_STSYM,
1742 .n_sect = sym.n_sect,
1743 .n_desc = 0,
1744 .n_value = sym.n_value,
4312 const requires_codesig = blk: {
4313 if (options.entitlements) |_| break :blk true;
4314 if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true;
4315 break :blk false;
17454316 };
1746 return buf[0..1];
1747 }
1748}
4317 var codesig_offset: ?u32 = null;
4318 var codesig: ?CodeSignature = if (requires_codesig) blk: {
4319 // Preallocate space for the code signature.
4320 // We need to do this at this stage so that we have the load commands with proper values
4321 // written out to the file.
4322 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
4323 // where the code signature goes into.
4324 var codesig = CodeSignature.init(page_size);
4325 codesig.code_directory.ident = options.emit.?.sub_path;
4326 if (options.entitlements) |path| {
4327 try codesig.addEntitlements(gpa, path);
4328 }
4329 codesig_offset = try zld.writeCodeSignaturePadding(&codesig, &ncmds, lc_writer);
4330 break :blk codesig;
4331 } else null;
4332 defer if (codesig) |*csig| csig.deinit(gpa);
17494333
1750const SymtabCtx = struct {
1751 nlocalsym: u32,
1752 nextdefsym: u32,
1753 nundefsym: u32,
1754 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
1755};
4334 var headers_buf = std.ArrayList(u8).init(arena);
4335 try zld.writeSegmentHeaders(&ncmds, headers_buf.writer());
17564336
1757fn writeDysymtab(macho_file: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !void {
1758 const gpa = macho_file.base.allocator;
1759 const nstubs = @intCast(u32, macho_file.stubs_table.count());
1760 const ngot_entries = @intCast(u32, macho_file.got_entries_table.count());
1761 const nindirectsyms = nstubs * 2 + ngot_entries;
1762 const iextdefsym = ctx.nlocalsym;
1763 const iundefsym = iextdefsym + ctx.nextdefsym;
1764
1765 const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1766 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
1767 const needed_size = nindirectsyms * @sizeOf(u32);
1768 seg.filesize = offset + needed_size - seg.fileoff;
1769
1770 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
1771
1772 var buf = std.ArrayList(u8).init(gpa);
1773 defer buf.deinit();
1774 try buf.ensureTotalCapacity(needed_size);
1775 const writer = buf.writer();
1776
1777 if (macho_file.stubs_section_index) |sect_id| {
1778 const stubs = &macho_file.sections.items(.header)[sect_id];
1779 stubs.reserved1 = 0;
1780 for (macho_file.stubs.items) |entry| {
1781 if (entry.sym_index == 0) continue;
1782 const atom_sym = entry.getSymbol(macho_file);
1783 if (atom_sym.n_desc == MachO.N_DESC_GCED) continue;
1784 const target_sym = macho_file.getSymbol(entry.target);
1785 assert(target_sym.undf());
1786 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
1787 }
1788 }
4337 try zld.file.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
4338 try zld.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
4339 try zld.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
17894340
1790 if (macho_file.got_section_index) |sect_id| {
1791 const got = &macho_file.sections.items(.header)[sect_id];
1792 got.reserved1 = nstubs;
1793 for (macho_file.got_entries.items) |entry| {
1794 if (entry.sym_index == 0) continue;
1795 const atom_sym = entry.getSymbol(macho_file);
1796 if (atom_sym.n_desc == MachO.N_DESC_GCED) continue;
1797 const target_sym = macho_file.getSymbol(entry.target);
1798 if (target_sym.undf()) {
1799 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
1800 } else {
1801 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
1802 }
4341 if (codesig) |*csig| {
4342 try zld.writeCodeSignature(csig, codesig_offset.?); // code signing always comes last
18034343 }
18044344 }
18054345
1806 if (macho_file.la_symbol_ptr_section_index) |sect_id| {
1807 const la_symbol_ptr = &macho_file.sections.items(.header)[sect_id];
1808 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
1809 for (macho_file.stubs.items) |entry| {
1810 if (entry.sym_index == 0) continue;
1811 const atom_sym = entry.getSymbol(macho_file);
1812 if (atom_sym.n_desc == MachO.N_DESC_GCED) continue;
1813 const target_sym = macho_file.getSymbol(entry.target);
1814 assert(target_sym.undf());
1815 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
1816 }
4346 if (!options.disable_lld_caching) {
4347 // Update the file with the digest. If it fails we can continue; it only
4348 // means that the next invocation will have an unnecessary cache miss.
4349 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
4350 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});
4351 };
4352 // Again failure here only means an unnecessary cache miss.
4353 man.writeManifest() catch |err| {
4354 log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)});
4355 };
4356 // We hang on to this lock so that the output file path can be used without
4357 // other processes clobbering it.
4358 macho_file.base.lock = man.toOwnedLock();
18174359 }
1818
1819 assert(buf.items.len == needed_size);
1820 try macho_file.base.file.?.pwriteAll(buf.items, offset);
1821
1822 lc.nlocalsym = ctx.nlocalsym;
1823 lc.iextdefsym = iextdefsym;
1824 lc.nextdefsym = ctx.nextdefsym;
1825 lc.iundefsym = iundefsym;
1826 lc.nundefsym = ctx.nundefsym;
1827 lc.indirectsymoff = @intCast(u32, offset);
1828 lc.nindirectsyms = nindirectsyms;
1829}
1830
1831fn writeCodeSignaturePadding(
1832 macho_file: *MachO,
1833 code_sig: *CodeSignature,
1834 ncmds: *u32,
1835 lc_writer: anytype,
1836) !u32 {
1837 const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1838 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
1839 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
1840 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, 16);
1841 const needed_size = code_sig.estimateSize(offset);
1842 seg.filesize = offset + needed_size - seg.fileoff;
1843 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, macho_file.page_size);
1844 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
1845 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
1846 // except for code signature data.
1847 try macho_file.base.file.?.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
1848
1849 try lc_writer.writeStruct(macho.linkedit_data_command{
1850 .cmd = .CODE_SIGNATURE,
1851 .cmdsize = @sizeOf(macho.linkedit_data_command),
1852 .dataoff = @intCast(u32, offset),
1853 .datasize = @intCast(u32, needed_size),
1854 });
1855 ncmds.* += 1;
1856
1857 return @intCast(u32, offset);
1858}
1859
1860fn writeCodeSignature(macho_file: *MachO, code_sig: *CodeSignature, offset: u32) !void {
1861 const seg = macho_file.segments.items[macho_file.text_segment_cmd_index.?];
1862
1863 var buffer = std.ArrayList(u8).init(macho_file.base.allocator);
1864 defer buffer.deinit();
1865 try buffer.ensureTotalCapacityPrecise(code_sig.size());
1866 try code_sig.writeAdhocSignature(macho_file.base.allocator, .{
1867 .file = macho_file.base.file.?,
1868 .exec_seg_base = seg.fileoff,
1869 .exec_seg_limit = seg.filesize,
1870 .file_size = offset,
1871 .output_mode = macho_file.base.options.output_mode,
1872 }, buffer.writer());
1873 assert(buffer.items.len == code_sig.size());
1874
1875 log.debug("writing code signature from 0x{x} to 0x{x}", .{
1876 offset,
1877 offset + buffer.items.len,
1878 });
1879
1880 try macho_file.base.file.?.pwriteAll(buffer.items, offset);
18814360}
18824361
1883fn writeSegmentHeaders(macho_file: *MachO, ncmds: *u32, writer: anytype) !void {
1884 for (macho_file.segments.items) |seg, i| {
1885 const indexes = macho_file.getSectionIndexes(@intCast(u8, i));
1886 var out_seg = seg;
1887 out_seg.cmdsize = @sizeOf(macho.segment_command_64);
1888 out_seg.nsects = 0;
1889
1890 // Update section headers count; any section with size of 0 is excluded
1891 // since it doesn't have any data in the final binary file.
1892 for (macho_file.sections.items(.header)[indexes.start..indexes.end]) |header| {
1893 if (header.size == 0) continue;
1894 out_seg.cmdsize += @sizeOf(macho.section_64);
1895 out_seg.nsects += 1;
1896 }
1897
1898 if (out_seg.nsects == 0 and
1899 (mem.eql(u8, out_seg.segName(), "__DATA_CONST") or
1900 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;
1901
1902 try writer.writeStruct(out_seg);
1903 for (macho_file.sections.items(.header)[indexes.start..indexes.end]) |header| {
1904 if (header.size == 0) continue;
1905 try writer.writeStruct(header);
4362/// Binary search
4363pub fn bsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {
4364 if (!@hasDecl(@TypeOf(predicate), "predicate"))
4365 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
4366
4367 var min: usize = 0;
4368 var max: usize = haystack.len;
4369 while (min < max) {
4370 const index = (min + max) / 2;
4371 const curr = haystack[index];
4372 if (predicate.predicate(curr)) {
4373 min = index + 1;
4374 } else {
4375 max = index;
19064376 }
1907
1908 ncmds.* += 1;
19094377 }
4378 return min;
19104379}
19114380
1912/// Writes Mach-O file header.
1913fn writeHeader(macho_file: *MachO, ncmds: u32, sizeofcmds: u32) !void {
1914 var header: macho.mach_header_64 = .{};
1915 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
1916
1917 switch (macho_file.base.options.target.cpu.arch) {
1918 .aarch64 => {
1919 header.cputype = macho.CPU_TYPE_ARM64;
1920 header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL;
1921 },
1922 .x86_64 => {
1923 header.cputype = macho.CPU_TYPE_X86_64;
1924 header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL;
1925 },
1926 else => return error.UnsupportedCpuArchitecture,
1927 }
1928
1929 switch (macho_file.base.options.output_mode) {
1930 .Exe => {
1931 header.filetype = macho.MH_EXECUTE;
1932 },
1933 .Lib => {
1934 // By this point, it can only be a dylib.
1935 header.filetype = macho.MH_DYLIB;
1936 header.flags |= macho.MH_NO_REEXPORTED_DYLIBS;
1937 },
1938 else => unreachable,
1939 }
1940
1941 if (macho_file.getSectionByName("__DATA", "__thread_vars")) |sect_id| {
1942 if (macho_file.sections.items(.header)[sect_id].size > 0) {
1943 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
1944 }
1945 }
1946
1947 header.ncmds = ncmds;
1948 header.sizeofcmds = sizeofcmds;
4381/// Linear search
4382pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {
4383 if (!@hasDecl(@TypeOf(predicate), "predicate"))
4384 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
19494385
1950 log.debug("writing Mach-O header {}", .{header});
1951
1952 try macho_file.base.file.?.pwriteAll(mem.asBytes(&header), 0);
4386 var i: usize = 0;
4387 while (i < haystack.len) : (i += 1) {
4388 if (predicate.predicate(haystack[i])) break;
4389 }
4390 return i;
19534391}