authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-10-22 10:14:46-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-10-22 10:14:46-07:00
loge67c756b9114debdaa566188f677f60e507dfac8
treeb28f0638e0115df6adbb1b402687211d5c37c4da
parent776f7de9673087597e1a549d3567c18c43c800ab
parent593b75b109f49e7afbe2fe6fa1d519dc5799a3fb
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #13260 from ziglang/zld-sync

macho: faster and more memory efficient linker

13 files changed, 6878 insertions(+), 3910 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+219-884
......@@ -55,8 +55,6 @@ pub const SearchStrategy = enum {
5555 dylibs_first,
5656};
5757
58pub const N_DESC_GCED: u16 = @bitCast(u16, @as(i16, -1));
59
6058const Section = struct {
6159 header: macho.section_64,
6260 segment_index: u8,
......@@ -105,8 +103,6 @@ uuid: macho.uuid_command = .{
105103 .uuid = undefined,
106104},
107105
108objects: std.ArrayListUnmanaged(Object) = .{},
109archives: std.ArrayListUnmanaged(Archive) = .{},
110106dylibs: std.ArrayListUnmanaged(Dylib) = .{},
111107dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
112108referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},
......@@ -143,12 +139,6 @@ stub_helper_preamble_atom: ?*Atom = null,
143139
144140strtab: StringTable(.strtab) = .{},
145141
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
152142got_entries: std.ArrayListUnmanaged(Entry) = .{},
153143got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
154144got_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
......@@ -276,7 +266,7 @@ pub const SymbolWithLoc = struct {
276266const ideal_factor = 3;
277267
278268/// Default path to dyld
279const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
269pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
280270
281271/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
282272/// it as a possible place to put new symbols, it must have enough room for this many bytes
......@@ -286,12 +276,12 @@ pub const min_text_capacity = padToIdeal(minimum_text_block_size);
286276
287277/// Default virtual memory offset corresponds to the size of __PAGEZERO segment and
288278/// start of __TEXT segment.
289const default_pagezero_vmsize: u64 = 0x100000000;
279pub const default_pagezero_vmsize: u64 = 0x100000000;
290280
291281/// We commit 0x1000 = 4096 bytes of space to the header and
292282/// the table of load commands. This should be plenty for any
293283/// potential future extensions.
294const default_headerpad_size: u32 = 0x1000;
284pub const default_headerpad_size: u32 = 0x1000;
295285
296286pub const Export = struct {
297287 sym_index: ?u32 = null,
......@@ -465,7 +455,14 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
465455 }
466456
467457 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
468 try self.resolveLibSystem(arena, comp, &.{}, &libs);
458 try resolveLibSystem(
459 arena,
460 comp,
461 self.base.options.sysroot,
462 self.base.options.target,
463 &.{},
464 &libs,
465 );
469466
470467 const id_symlink_basename = "link.id";
471468
......@@ -660,15 +657,16 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
660657}
661658
662659pub fn resolveLibSystem(
663 self: *MachO,
664660 arena: Allocator,
665661 comp: *Compilation,
662 syslibroot: ?[]const u8,
663 target: std.Target,
666664 search_dirs: []const []const u8,
667665 out_libs: anytype,
668666) !void {
669667 // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}.
670668 var libsystem_available = false;
671 if (self.base.options.sysroot != null) blk: {
669 if (syslibroot != null) blk: {
672670 // Try stub file first. If we hit it, then we're done as the stub file
673671 // re-exports every single symbol definition.
674672 for (search_dirs) |dir| {
......@@ -693,7 +691,7 @@ pub fn resolveLibSystem(
693691 }
694692 if (!libsystem_available) {
695693 const libsystem_name = try std.fmt.allocPrint(arena, "libSystem.{d}.tbd", .{
696 self.base.options.target.os.version_range.semver.min.major,
694 target.os.version_range.semver.min.major,
697695 });
698696 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
699697 "libc", "darwin", libsystem_name,
......@@ -783,94 +781,6 @@ pub fn resolveFramework(
783781 return full_path;
784782}
785783
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
874784const ParseDylibError = error{
875785 OutOfMemory,
876786 EmptyStubFile,
......@@ -1019,7 +929,6 @@ pub fn parseLibs(
1019929 .needed = lib_info.needed,
1020930 .weak = lib_info.weak,
1021931 })) continue;
1022 if (try self.parseArchive(lib, false)) continue;
1023932
1024933 log.debug("unknown filetype for a library: '{s}'", .{lib});
1025934 }
......@@ -1070,29 +979,7 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs:
1070979 }
1071980}
1072981
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
1088982pub 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
1096983 const sym = atom.getSymbol(self);
1097984 const section = self.sections.get(sym.n_sect - 1);
1098985 const file_offset = section.header.offset + sym.n_value - section.header.addr;
......@@ -1137,10 +1024,7 @@ pub fn allocateSpecialSymbols(self: *MachO) !void {
11371024 const global = self.getGlobal(name) orelse continue;
11381025 if (global.file != null) continue;
11391026 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 };
1027 const seg = self.getSegment(self.text_section_index.?);
11441028 sym.n_sect = 1;
11451029 sym.n_value = seg.vmaddr;
11461030
......@@ -1155,16 +1039,13 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
11551039 const gpa = self.base.allocator;
11561040
11571041 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),
1042 const atom = blk: {
1043 const atom = try gpa.create(Atom);
1044 atom.* = Atom.empty;
1045 atom.sym_index = sym_index;
1046 atom.size = @sizeOf(u64);
1047 atom.alignment = @alignOf(u64);
1048 break :blk atom;
11681049 };
11691050 errdefer gpa.destroy(atom);
11701051
......@@ -1174,61 +1055,31 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
11741055 const sym = atom.getSymbolPtr(self);
11751056 sym.n_type = macho.N_SECT;
11761057 sym.n_sect = self.got_section_index.? + 1;
1058 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
11771059
1178 if (self.mode == .incremental) {
1179 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1060 log.debug("allocated GOT atom at 0x{x}", .{sym.n_value});
11801061
1181 log.debug("allocated GOT atom at 0x{x}", .{sym.n_value});
1062 try atom.addRelocation(self, .{
1063 .@"type" = switch (self.base.options.target.cpu.arch) {
1064 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1065 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1066 else => unreachable,
1067 },
1068 .target = target,
1069 .offset = 0,
1070 .addend = 0,
1071 .pcrel = false,
1072 .length = 3,
1073 });
11821074
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,
1075 const target_sym = self.getSymbol(target);
1076 if (target_sym.undf()) {
1077 try atom.addBinding(self, .{
1078 .target = self.getGlobal(self.getSymbolName(target)).?,
11901079 .offset = 0,
1191 .addend = 0,
1192 .pcrel = false,
1193 .length = 3,
11941080 });
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 }
12051081 } 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);
1082 try atom.addRebase(self, 0);
12321083 }
12331084
12341085 return atom;
......@@ -1241,16 +1092,13 @@ pub fn createDyldPrivateAtom(self: *MachO) !void {
12411092 const gpa = self.base.allocator;
12421093
12431094 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),
1095 const atom = blk: {
1096 const atom = try gpa.create(Atom);
1097 atom.* = Atom.empty;
1098 atom.sym_index = sym_index;
1099 atom.size = @sizeOf(u64);
1100 atom.alignment = @alignOf(u64);
1101 break :blk atom;
12541102 };
12551103 errdefer gpa.destroy(atom);
12561104
......@@ -1262,13 +1110,9 @@ pub fn createDyldPrivateAtom(self: *MachO) !void {
12621110 try self.managed_atoms.append(gpa, atom);
12631111 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
12641112
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 }
1113 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1114 log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});
1115 try self.writePtrWidthAtom(atom);
12721116}
12731117
12741118pub fn createStubHelperPreambleAtom(self: *MachO) !void {
......@@ -1282,26 +1126,18 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
12821126 .aarch64 => 6 * @sizeOf(u32),
12831127 else => unreachable,
12841128 };
1285 const alignment: u32 = switch (arch) {
1286 .x86_64 => 0,
1287 .aarch64 => 2,
1288 else => unreachable,
1289 };
12901129 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),
1130 const atom = blk: {
1131 const atom = try gpa.create(Atom);
1132 atom.* = Atom.empty;
1133 atom.sym_index = sym_index;
1134 atom.size = size;
1135 atom.alignment = switch (arch) {
1136 .x86_64 => 1,
1137 .aarch64 => @alignOf(u32),
1138 else => unreachable,
1139 };
1140 break :blk atom;
13051141 };
13061142 errdefer gpa.destroy(atom);
13071143
......@@ -1328,43 +1164,21 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
13281164 code[9] = 0xff;
13291165 code[10] = 0x25;
13301166
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 }
1167 try atom.addRelocations(self, 2, .{ .{
1168 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1169 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1170 .offset = 3,
1171 .addend = 0,
1172 .pcrel = true,
1173 .length = 2,
1174 }, .{
1175 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_GOT),
1176 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1177 .offset = 11,
1178 .addend = 0,
1179 .pcrel = true,
1180 .length = 2,
1181 } });
13681182 },
13691183
13701184 .aarch64 => {
......@@ -1390,75 +1204,35 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
13901204 // br x16
13911205 mem.writeIntLittle(u32, code[20..][0..4], aarch64.Instruction.br(.x16).toU32());
13921206
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 }
1207 try atom.addRelocations(self, 4, .{ .{
1208 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1209 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1210 .offset = 0,
1211 .addend = 0,
1212 .pcrel = true,
1213 .length = 2,
1214 }, .{
1215 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
1216 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1217 .offset = 4,
1218 .addend = 0,
1219 .pcrel = false,
1220 .length = 2,
1221 }, .{
1222 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
1223 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1224 .offset = 12,
1225 .addend = 0,
1226 .pcrel = true,
1227 .length = 2,
1228 }, .{
1229 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
1230 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1231 .offset = 16,
1232 .addend = 0,
1233 .pcrel = false,
1234 .length = 2,
1235 } });
14621236 },
14631237
14641238 else => unreachable,
......@@ -1468,14 +1242,9 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
14681242 try self.managed_atoms.append(gpa, atom);
14691243 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
14701244
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 }
1245 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1246 log.debug("allocated stub preamble atom at 0x{x}", .{sym.n_value});
1247 try self.writeAtom(atom, code);
14791248}
14801249
14811250pub fn createStubHelperAtom(self: *MachO) !*Atom {
......@@ -1486,26 +1255,18 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
14861255 .aarch64 => 3 * @sizeOf(u32),
14871256 else => unreachable,
14881257 };
1489 const alignment: u2 = switch (arch) {
1490 .x86_64 => 0,
1491 .aarch64 => 2,
1492 else => unreachable,
1493 };
14941258 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),
1259 const atom = blk: {
1260 const atom = try gpa.create(Atom);
1261 atom.* = Atom.empty;
1262 atom.sym_index = sym_index;
1263 atom.size = size;
1264 atom.alignment = switch (arch) {
1265 .x86_64 => 1,
1266 .aarch64 => @alignOf(u32),
1267 else => unreachable,
1268 };
1269 break :blk atom;
15091270 };
15101271 errdefer gpa.destroy(atom);
15111272
......@@ -1525,27 +1286,14 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
15251286 // jmpq
15261287 code[5] = 0xe9;
15271288
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 }
1289 try atom.addRelocation(self, .{
1290 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1291 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
1292 .offset = 6,
1293 .addend = 0,
1294 .pcrel = true,
1295 .length = 2,
1296 });
15491297 },
15501298 .aarch64 => {
15511299 const literal = blk: {
......@@ -1561,27 +1309,14 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
15611309 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(0).toU32());
15621310 // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
15631311
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 }
1312 try atom.addRelocation(self, .{
1313 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
1314 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
1315 .offset = 4,
1316 .addend = 0,
1317 .pcrel = true,
1318 .length = 2,
1319 });
15851320 },
15861321 else => unreachable,
15871322 }
......@@ -1589,14 +1324,9 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
15891324 try self.managed_atoms.append(gpa, atom);
15901325 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
15911326
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 }
1327 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1328 log.debug("allocated stub helper atom at 0x{x}", .{sym.n_value});
1329 try self.writeAtom(atom, code);
16001330
16011331 return atom;
16021332}
......@@ -1604,16 +1334,13 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
16041334pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !*Atom {
16051335 const gpa = self.base.allocator;
16061336 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),
1337 const atom = blk: {
1338 const atom = try gpa.create(Atom);
1339 atom.* = Atom.empty;
1340 atom.sym_index = sym_index;
1341 atom.size = @sizeOf(u64);
1342 atom.alignment = @alignOf(u64);
1343 break :blk atom;
16171344 };
16181345 errdefer gpa.destroy(atom);
16191346
......@@ -1621,56 +1348,30 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi
16211348 sym.n_type = macho.N_SECT;
16221349 sym.n_sect = self.la_symbol_ptr_section_index.? + 1;
16231350
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 }
1351 try atom.addRelocation(self, .{
1352 .@"type" = switch (self.base.options.target.cpu.arch) {
1353 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1354 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1355 else => unreachable,
1356 },
1357 .target = .{ .sym_index = stub_sym_index, .file = null },
1358 .offset = 0,
1359 .addend = 0,
1360 .pcrel = false,
1361 .length = 3,
1362 });
1363 try atom.addRebase(self, 0);
1364 try atom.addLazyBinding(self, .{
1365 .target = self.getGlobal(self.getSymbolName(target)).?,
1366 .offset = 0,
1367 });
16631368
16641369 try self.managed_atoms.append(gpa, atom);
16651370 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
16661371
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 }
1372 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1373 log.debug("allocated lazy pointer atom at 0x{x}", .{sym.n_value});
1374 try self.writePtrWidthAtom(atom);
16741375
16751376 return atom;
16761377}
......@@ -1678,32 +1379,24 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi
16781379pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
16791380 const gpa = self.base.allocator;
16801381 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 };
16861382 const size: u4 = switch (arch) {
16871383 .x86_64 => 6,
16881384 .aarch64 => 3 * @sizeOf(u32),
16891385 else => unreachable, // unhandled architecture type
16901386 };
16911387 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
1388 const atom = blk: {
1389 const atom = try gpa.create(Atom);
1390 atom.* = Atom.empty;
1391 atom.sym_index = sym_index;
1392 atom.size = size;
1393 atom.alignment = switch (arch) {
1394 .x86_64 => 1,
1395 .aarch64 => @alignOf(u32),
1396 else => unreachable, // unhandled architecture type
17021397
1703 };
1704 break :blk atom;
1705 },
1706 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, size, alignment),
1398 };
1399 break :blk atom;
17071400 };
17081401 errdefer gpa.destroy(atom);
17091402
......@@ -1721,26 +1414,14 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
17211414 code[0] = 0xff;
17221415 code[1] = 0x25;
17231416
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 }
1417 try atom.addRelocation(self, .{
1418 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1419 .target = .{ .sym_index = laptr_sym_index, .file = null },
1420 .offset = 2,
1421 .addend = 0,
1422 .pcrel = true,
1423 .length = 2,
1424 });
17441425 },
17451426 .aarch64 => {
17461427 // adrp x16, pages
......@@ -1754,46 +1435,24 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
17541435 // br x16
17551436 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32());
17561437
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,
1438 try atom.addRelocations(self, 2, .{
1439 .{
1440 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
17801441 .target = .{ .sym_index = laptr_sym_index, .file = null },
1442 .offset = 0,
17811443 .addend = 0,
1782 .subtractor = null,
17831444 .pcrel = true,
17841445 .length = 2,
1785 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1786 });
1787 atom.relocs.appendAssumeCapacity(.{
1788 .offset = 4,
1446 },
1447 .{
1448 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
17891449 .target = .{ .sym_index = laptr_sym_index, .file = null },
1450 .offset = 4,
17901451 .addend = 0,
1791 .subtractor = null,
17921452 .pcrel = false,
17931453 .length = 2,
1794 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
1795 });
1796 }
1454 },
1455 });
17971456 },
17981457 else => unreachable,
17991458 }
......@@ -1801,96 +1460,13 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
18011460 try self.managed_atoms.append(gpa, atom);
18021461 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
18031462
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);
1463 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1464 log.debug("allocated stub atom at 0x{x}", .{sym.n_value});
1465 try self.writeAtom(atom, code);
18451466
18461467 return atom;
18471468}
18481469
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
18941470pub fn createMhExecuteHeaderSymbol(self: *MachO) !void {
18951471 if (self.base.options.output_mode != .Exe) return;
18961472 if (self.getGlobal("__mh_execute_header")) |global| {
......@@ -1989,90 +1565,6 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
19891565 gop.value_ptr.* = current;
19901566}
19911567
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
20761568pub fn resolveSymbolsInDylibs(self: *MachO) !void {
20771569 if (self.dylibs.items.len == 0) return;
20781570
......@@ -2209,9 +1701,7 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {
22091701 const got_atom = try self.createGotAtom(global);
22101702 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
22111703
2212 if (self.mode == .incremental) {
2213 try self.writePtrWidthAtom(got_atom);
2214 }
1704 try self.writePtrWidthAtom(got_atom);
22151705}
22161706
22171707pub fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
......@@ -2236,10 +1726,7 @@ pub fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
22361726
22371727pub fn writeMainLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
22381728 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 };
1729 const seg_id = self.header_segment_cmd_index.?;
22431730 const seg = self.segments.items[seg_id];
22441731 const global = try self.getEntryPoint();
22451732 const sym = self.getSymbol(global);
......@@ -2417,9 +1904,6 @@ pub fn deinit(self: *MachO) void {
24171904 d_sym.deinit(gpa);
24181905 }
24191906
2420 self.tlv_ptr_entries.deinit(gpa);
2421 self.tlv_ptr_entries_free_list.deinit(gpa);
2422 self.tlv_ptr_entries_table.deinit(gpa);
24231907 self.got_entries.deinit(gpa);
24241908 self.got_entries_free_list.deinit(gpa);
24251909 self.got_entries_table.deinit(gpa);
......@@ -2442,16 +1926,6 @@ pub fn deinit(self: *MachO) void {
24421926 self.resolver.deinit(gpa);
24431927 }
24441928
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
24551929 for (self.dylibs.items) |*dylib| {
24561930 dylib.deinit(gpa);
24571931 }
......@@ -2467,16 +1941,11 @@ pub fn deinit(self: *MachO) void {
24671941 self.sections.deinit(gpa);
24681942
24691943 for (self.managed_atoms.items) |atom| {
2470 atom.deinit(gpa);
24711944 gpa.destroy(atom);
24721945 }
24731946 self.managed_atoms.deinit(gpa);
24741947
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 }
1948 if (self.base.options.module) |_| {
24801949 self.decls.deinit(gpa);
24811950 } else {
24821951 assert(self.decls.count() == 0);
......@@ -2525,11 +1994,9 @@ pub fn deinit(self: *MachO) void {
25251994 }
25261995}
25271996
2528fn freeAtom(self: *MachO, atom: *Atom, owns_atom: bool) void {
1997fn freeAtom(self: *MachO, atom: *Atom) void {
25291998 log.debug("freeAtom {*}", .{atom});
2530 if (!owns_atom) {
2531 atom.deinit(self.base.allocator);
2532 }
1999
25332000 // Remove any relocs and base relocs associated with this Atom
25342001 self.freeRelocationsForAtom(atom);
25352002
......@@ -2694,27 +2161,6 @@ pub fn allocateStubEntry(self: *MachO, target: SymbolWithLoc) !u32 {
26942161 return index;
26952162}
26962163
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
27182164pub fn allocateDeclIndexes(self: *MachO, decl_index: Module.Decl.Index) !void {
27192165 if (self.llvm_object) |_| return;
27202166 const decl = self.base.options.module.?.declPtr(decl_index);
......@@ -2845,7 +2291,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
28452291 symbol.n_type = macho.N_SECT;
28462292 symbol.n_sect = sect_id + 1;
28472293 symbol.n_value = try self.allocateAtom(atom, code.len, required_alignment);
2848 errdefer self.freeAtom(atom, true);
2294 errdefer self.freeAtom(atom);
28492295
28502296 try unnamed_consts.append(gpa, atom);
28512297
......@@ -3137,7 +2583,7 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)
31372583 sym.n_desc = 0;
31382584
31392585 const vaddr = try self.allocateAtom(atom, code_len, required_alignment);
3140 errdefer self.freeAtom(atom, false);
2586 errdefer self.freeAtom(atom);
31412587
31422588 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, vaddr });
31432589 log.debug(" (required alignment 0x{x})", .{required_alignment});
......@@ -3256,15 +2702,15 @@ pub fn updateDeclExports(
32562702
32572703 self.resolveGlobalSymbol(sym_loc) catch |err| switch (err) {
32582704 error.MultipleSymbolDefinitions => {
2705 // TODO: this needs rethinking
32592706 const global = self.getGlobal(exp_name).?;
32602707 if (sym_loc.sym_index != global.sym_index and global.file != null) {
32612708 _ = try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(
32622709 gpa,
32632710 decl.srcLoc(),
32642711 \\LinkError: symbol '{s}' defined multiple times
3265 \\ first definition in '{s}'
32662712 ,
3267 .{ exp_name, self.objects.items[global.file.?].name },
2713 .{exp_name},
32682714 ));
32692715 }
32702716 },
......@@ -3314,7 +2760,7 @@ fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
33142760 const gpa = self.base.allocator;
33152761 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
33162762 for (unnamed_consts.items) |atom| {
3317 self.freeAtom(atom, true);
2763 self.freeAtom(atom);
33182764 self.locals_free_list.append(gpa, atom.sym_index) catch {};
33192765 self.locals.items[atom.sym_index].n_type = 0;
33202766 _ = self.atom_by_index_table.remove(atom.sym_index);
......@@ -3335,7 +2781,7 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
33352781
33362782 const kv = self.decls.fetchSwapRemove(decl_index);
33372783 if (kv.?.value) |_| {
3338 self.freeAtom(&decl.link.macho, false);
2784 self.freeAtom(&decl.link.macho);
33392785 self.freeUnnamedConsts(decl_index);
33402786 }
33412787
......@@ -3969,22 +3415,6 @@ fn insertSection(self: *MachO, segment_index: u8, header: macho.section_64) !u8
39693415 return insertion_index;
39703416}
39713417
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
39883418pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
39893419 const gpa = self.base.allocator;
39903420
......@@ -4174,7 +3604,6 @@ fn collectExportData(self: *MachO, trie: *Trie) !void {
41743604
41753605 if (sym.undf()) continue;
41763606 if (!sym.ext()) continue;
4177 if (sym.n_desc == N_DESC_GCED) continue;
41783607
41793608 const sym_name = self.getSymbolName(global);
41803609 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
......@@ -4422,33 +3851,18 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
44223851
44233852 for (self.locals.items) |sym, sym_id| {
44243853 if (sym.n_strx == 0) continue; // no name, skip
4425 if (sym.n_desc == N_DESC_GCED) continue; // GCed, skip
44263854 const sym_loc = SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = null };
44273855 if (self.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
44283856 if (self.getGlobal(self.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip
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
44483863 for (self.globals.items) |global| {
44493864 const sym = self.getSymbol(global);
44503865 if (sym.undf()) continue; // import, skip
4451 if (sym.n_desc == N_DESC_GCED) continue; // GCed, skip
44523866 var out_sym = sym;
44533867 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
44543868 try exports.append(out_sym);
......@@ -4551,8 +3965,6 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !voi
45513965 stubs.reserved1 = 0;
45523966 for (self.stubs.items) |entry| {
45533967 if (entry.sym_index == 0) continue;
4554 const atom_sym = entry.getSymbol(self);
4555 if (atom_sym.n_desc == N_DESC_GCED) continue;
45563968 const target_sym = self.getSymbol(entry.target);
45573969 assert(target_sym.undf());
45583970 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
......@@ -4564,8 +3976,6 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !voi
45643976 got.reserved1 = nstubs;
45653977 for (self.got_entries.items) |entry| {
45663978 if (entry.sym_index == 0) continue;
4567 const atom_sym = entry.getSymbol(self);
4568 if (atom_sym.n_desc == N_DESC_GCED) continue;
45693979 const target_sym = self.getSymbol(entry.target);
45703980 if (target_sym.undf()) {
45713981 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
......@@ -4580,8 +3990,6 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !voi
45803990 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
45813991 for (self.stubs.items) |entry| {
45823992 if (entry.sym_index == 0) continue;
4583 const atom_sym = entry.getSymbol(self);
4584 if (atom_sym.n_desc == N_DESC_GCED) continue;
45853993 const target_sym = self.getSymbol(entry.target);
45863994 assert(target_sym.undf());
45873995 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
......@@ -4807,12 +4215,8 @@ pub fn symbolIsTemp(self: *MachO, sym_with_loc: SymbolWithLoc) bool {
48074215
48084216/// Returns pointer-to-symbol described by `sym_with_loc` descriptor.
48094217pub 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 }
4218 assert(sym_with_loc.file == null);
4219 return &self.locals.items[sym_with_loc.sym_index];
48164220}
48174221
48184222/// Returns symbol described by `sym_with_loc` descriptor.
......@@ -4822,14 +4226,9 @@ pub fn getSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
48224226
48234227/// Returns name of the symbol described by `sym_with_loc` descriptor.
48244228pub 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 }
4229 assert(sym_with_loc.file == null);
4230 const sym = self.locals.items[sym_with_loc.sym_index];
4231 return self.strtab.get(sym.n_strx).?;
48334232}
48344233
48354234/// Returns pointer to the global entry for `name` if one exists.
......@@ -4878,12 +4277,8 @@ pub fn getOrPutGlobalPtr(self: *MachO, name: []const u8) !GetOrPutGlobalPtrResul
48784277/// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor.
48794278/// Returns null on failure.
48804279pub 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 }
4280 assert(sym_with_loc.file == null);
4281 return self.atom_by_index_table.get(sym_with_loc.sym_index);
48874282}
48884283
48894284/// Returns GOT atom that references `sym_with_loc` if one exists.
......@@ -4900,13 +4295,6 @@ pub fn getStubsAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
49004295 return self.stubs.items[stubs_index].getAtom(self);
49014296}
49024297
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
49104298/// Returns symbol location corresponding to the set entrypoint.
49114299/// Asserts output mode is executable.
49124300pub fn getEntryPoint(self: MachO) error{MissingMainEntrypoint}!SymbolWithLoc {
......@@ -5199,7 +4587,7 @@ pub fn logSections(self: *MachO) void {
51994587 }
52004588}
52014589
5202fn logSymAttributes(sym: macho.nlist_64, buf: *[9]u8) []const u8 {
4590fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 {
52034591 mem.set(u8, buf[0..4], '_');
52044592 mem.set(u8, buf[4..], ' ');
52054593 if (sym.sect()) {
......@@ -5218,35 +4606,13 @@ fn logSymAttributes(sym: macho.nlist_64, buf: *[9]u8) []const u8 {
52184606 if (sym.undf()) {
52194607 buf[3] = 'u';
52204608 }
5221 if (sym.n_desc == N_DESC_GCED) {
5222 mem.copy(u8, buf[5..], "DEAD");
5223 }
52244609 return buf[0..];
52254610}
52264611
52274612pub fn logSymtab(self: *MachO) void {
5228 var buf: [9]u8 = undefined;
4613 var buf: [4]u8 = undefined;
52294614
52304615 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)", .{});
52504616 for (self.locals.items) |sym, sym_id| {
52514617 const where = if (sym.undf() and !sym.tentative()) "ord" else "sect";
52524618 const def_index = if (sym.undf() and !sym.tentative())
......@@ -5272,7 +4638,6 @@ pub fn logSymtab(self: *MachO) void {
52724638 log.debug("GOT entries:", .{});
52734639 for (self.got_entries.items) |entry, i| {
52744640 const atom_sym = entry.getSymbol(self);
5275 if (atom_sym.n_desc == N_DESC_GCED) continue;
52764641 const target_sym = self.getSymbol(entry.target);
52774642 if (target_sym.undf()) {
52784643 log.debug(" {d}@{x} => import('{s}')", .{
......@@ -5291,19 +4656,6 @@ pub fn logSymtab(self: *MachO) void {
52914656 }
52924657 }
52934658
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
53074659 log.debug("stubs entries:", .{});
53084660 for (self.stubs.items) |entry, i| {
53094661 const target_sym = self.getSymbol(entry.target);
......@@ -5352,21 +4704,4 @@ pub fn logAtom(self: *MachO, atom: *const Atom) void {
53524704 atom.file,
53534705 sym.n_sect,
53544706 });
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 }
53724707}
src/link/MachO/Archive.zig+8-7
......@@ -165,6 +165,7 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !
165165 while (true) {
166166 const n_strx = symtab_reader.readIntLittle(u32) catch |err| switch (err) {
167167 error.EndOfStream => break,
168 else => |e| return e,
168169 };
169170 const object_offset = try symtab_reader.readIntLittle(u32);
170171
......@@ -183,7 +184,7 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !
183184
184185pub fn parseObject(
185186 self: Archive,
186 allocator: Allocator,
187 gpa: Allocator,
187188 cpu_arch: std.Target.Cpu.Arch,
188189 offset: u32,
189190) !Object {
......@@ -198,15 +199,15 @@ pub fn parseObject(
198199 }
199200
200201 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);
202 const object_name = try parseName(gpa, name_or_length, reader);
203 defer gpa.free(object_name);
203204
204205 log.debug("extracting object '{s}' from archive '{s}'", .{ object_name, self.name });
205206
206207 const name = name: {
207208 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
208209 const path = try std.os.realpath(self.name, &buffer);
209 break :name try std.fmt.allocPrint(allocator, "{s}({s})", .{ path, object_name });
210 break :name try std.fmt.allocPrint(gpa, "{s}({s})", .{ path, object_name });
210211 };
211212
212213 const object_name_len = switch (name_or_length) {
......@@ -214,7 +215,7 @@ pub fn parseObject(
214215 .Length => |len| len,
215216 };
216217 const object_size = (try object_header.size()) - object_name_len;
217 const contents = try allocator.allocWithOptions(u8, object_size, @alignOf(u64), null);
218 const contents = try gpa.allocWithOptions(u8, object_size, @alignOf(u64), null);
218219 const amt = try reader.readAll(contents);
219220 if (amt != object_size) {
220221 return error.InputOutput;
......@@ -222,11 +223,11 @@ pub fn parseObject(
222223
223224 var object = Object{
224225 .name = name,
225 .mtime = try self.header.date(),
226 .mtime = object_header.date() catch 0,
226227 .contents = contents,
227228 };
228229
229 try object.parse(allocator, cpu_arch);
230 try object.parse(gpa, cpu_arch);
230231
231232 return object;
232233}
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/DebugSymbols.zig-2
......@@ -477,7 +477,6 @@ fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
477477
478478 for (self.base.locals.items) |sym, sym_id| {
479479 if (sym.n_strx == 0) continue; // no name, skip
480 if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip
481480 const sym_loc = MachO.SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = null };
482481 if (self.base.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
483482 if (self.base.getGlobal(self.base.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip
......@@ -492,7 +491,6 @@ fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
492491 for (self.base.globals.items) |global| {
493492 const sym = self.base.getSymbol(global);
494493 if (sym.undf()) continue; // import, skip
495 if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip
496494 var out_sym = sym;
497495 out_sym.n_strx = try self.strtab.insert(gpa, self.base.getSymbolName(global));
498496 try exports.append(out_sym);
src/link/MachO/DwarfInfo.zig created+467
......@@ -0,0 +1,467 @@
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 math = std.math;
9const mem = std.mem;
10
11const Allocator = mem.Allocator;
12pub const AbbrevLookupTable = std.AutoHashMap(u64, struct { pos: usize, len: usize });
13pub const SubprogramLookupByName = std.StringHashMap(struct { addr: u64, size: u64 });
14
15debug_info: []const u8,
16debug_abbrev: []const u8,
17debug_str: []const u8,
18
19pub fn getCompileUnitIterator(self: DwarfInfo) CompileUnitIterator {
20 return .{ .ctx = self };
21}
22
23const CompileUnitIterator = struct {
24 ctx: DwarfInfo,
25 pos: usize = 0,
26
27 pub fn next(self: *CompileUnitIterator) !?CompileUnit {
28 if (self.pos >= self.ctx.debug_info.len) return null;
29
30 var stream = std.io.fixedBufferStream(self.ctx.debug_info);
31 var creader = std.io.countingReader(stream.reader());
32 const reader = creader.reader();
33
34 const cuh = try CompileUnit.Header.read(reader);
35 const total_length = cuh.length + @as(u64, if (cuh.is_64bit) @sizeOf(u64) else @sizeOf(u32));
36 const offset = math.cast(usize, creader.bytes_read) orelse return error.Overflow;
37
38 const cu = CompileUnit{
39 .cuh = cuh,
40 .debug_info_off = offset,
41 };
42
43 self.pos += (math.cast(usize, total_length) orelse return error.Overflow);
44
45 return cu;
46 }
47};
48
49pub fn genSubprogramLookupByName(
50 self: DwarfInfo,
51 compile_unit: CompileUnit,
52 abbrev_lookup: AbbrevLookupTable,
53 lookup: *SubprogramLookupByName,
54) !void {
55 var abbrev_it = compile_unit.getAbbrevEntryIterator(self);
56 while (try abbrev_it.next(abbrev_lookup)) |entry| switch (entry.tag) {
57 dwarf.TAG.subprogram => {
58 var attr_it = entry.getAttributeIterator(self, compile_unit.cuh);
59
60 var name: ?[]const u8 = null;
61 var low_pc: ?u64 = null;
62 var high_pc: ?u64 = null;
63
64 while (try attr_it.next()) |attr| switch (attr.name) {
65 dwarf.AT.name => if (attr.getString(self, compile_unit.cuh)) |str| {
66 log.warn("subprogram: {s}", .{str});
67 name = str;
68 },
69 dwarf.AT.low_pc => {
70 if (attr.getAddr(self, compile_unit.cuh)) |addr| {
71 low_pc = addr;
72 }
73 if (try attr.getConstant(self)) |constant| {
74 low_pc = @intCast(u64, constant);
75 }
76 },
77 dwarf.AT.high_pc => {
78 if (attr.getAddr(self, compile_unit.cuh)) |addr| {
79 high_pc = addr;
80 }
81 if (try attr.getConstant(self)) |constant| {
82 high_pc = @intCast(u64, constant);
83 }
84 },
85 else => {},
86 };
87
88 if (name == null or low_pc == null or high_pc == null) continue;
89
90 try lookup.putNoClobber(name.?, .{ .addr = low_pc.?, .size = high_pc.? });
91 },
92 else => {},
93 };
94}
95
96pub fn genAbbrevLookupByKind(self: DwarfInfo, off: usize, lookup: *AbbrevLookupTable) !void {
97 const data = self.debug_abbrev[off..];
98 var stream = std.io.fixedBufferStream(data);
99 var creader = std.io.countingReader(stream.reader());
100 const reader = creader.reader();
101
102 while (true) {
103 const kind = try leb.readULEB128(u64, reader);
104
105 if (kind == 0) break;
106
107 const pos = math.cast(usize, creader.bytes_read) orelse return error.Overflow;
108 _ = try leb.readULEB128(u64, reader); // TAG
109 _ = try reader.readByte(); // CHILDREN
110
111 while (true) {
112 const name = try leb.readULEB128(u64, reader);
113 const form = try leb.readULEB128(u64, reader);
114
115 if (name == 0 and form == 0) break;
116 }
117
118 const next_pos = math.cast(usize, creader.bytes_read) orelse return error.Overflow;
119
120 try lookup.putNoClobber(kind, .{
121 .pos = pos,
122 .len = next_pos - pos - 2,
123 });
124 }
125}
126
127pub const CompileUnit = struct {
128 cuh: Header,
129 debug_info_off: usize,
130
131 pub const Header = struct {
132 is_64bit: bool,
133 length: u64,
134 version: u16,
135 debug_abbrev_offset: u64,
136 address_size: u8,
137
138 fn read(reader: anytype) !Header {
139 var length: u64 = try reader.readIntLittle(u32);
140
141 const is_64bit = length == 0xffffffff;
142 if (is_64bit) {
143 length = try reader.readIntLittle(u64);
144 }
145
146 const version = try reader.readIntLittle(u16);
147 const debug_abbrev_offset = if (is_64bit)
148 try reader.readIntLittle(u64)
149 else
150 try reader.readIntLittle(u32);
151 const address_size = try reader.readIntLittle(u8);
152
153 return Header{
154 .is_64bit = is_64bit,
155 .length = length,
156 .version = version,
157 .debug_abbrev_offset = debug_abbrev_offset,
158 .address_size = address_size,
159 };
160 }
161 };
162
163 inline fn getDebugInfo(self: CompileUnit, ctx: DwarfInfo) []const u8 {
164 return ctx.debug_info[self.debug_info_off..][0..self.cuh.length];
165 }
166
167 pub fn getAbbrevEntryIterator(self: CompileUnit, ctx: DwarfInfo) AbbrevEntryIterator {
168 return .{ .cu = self, .ctx = ctx };
169 }
170};
171
172const AbbrevEntryIterator = struct {
173 cu: CompileUnit,
174 ctx: DwarfInfo,
175 pos: usize = 0,
176
177 pub fn next(self: *AbbrevEntryIterator, lookup: AbbrevLookupTable) !?AbbrevEntry {
178 if (self.pos + self.cu.debug_info_off >= self.ctx.debug_info.len) return null;
179
180 const debug_info = self.ctx.debug_info[self.pos + self.cu.debug_info_off ..];
181 var stream = std.io.fixedBufferStream(debug_info);
182 var creader = std.io.countingReader(stream.reader());
183 const reader = creader.reader();
184
185 const kind = try leb.readULEB128(u64, reader);
186 self.pos += (math.cast(usize, creader.bytes_read) orelse return error.Overflow);
187
188 if (kind == 0) {
189 return AbbrevEntry.@"null"();
190 }
191
192 const abbrev_pos = lookup.get(kind) orelse return error.MalformedDwarf;
193 const len = try findAbbrevEntrySize(
194 self.ctx,
195 abbrev_pos.pos,
196 abbrev_pos.len,
197 self.pos + self.cu.debug_info_off,
198 self.cu.cuh,
199 );
200 const entry = try getAbbrevEntry(
201 self.ctx,
202 abbrev_pos.pos,
203 abbrev_pos.len,
204 self.pos + self.cu.debug_info_off,
205 len,
206 );
207
208 self.pos += len;
209
210 return entry;
211 }
212};
213
214pub const AbbrevEntry = struct {
215 tag: u64,
216 children: u8,
217 debug_abbrev_off: usize,
218 debug_abbrev_len: usize,
219 debug_info_off: usize,
220 debug_info_len: usize,
221
222 fn @"null"() AbbrevEntry {
223 return .{
224 .tag = 0,
225 .children = dwarf.CHILDREN.no,
226 .debug_abbrev_off = 0,
227 .debug_abbrev_len = 0,
228 .debug_info_off = 0,
229 .debug_info_len = 0,
230 };
231 }
232
233 pub fn hasChildren(self: AbbrevEntry) bool {
234 return self.children == dwarf.CHILDREN.yes;
235 }
236
237 inline fn getDebugInfo(self: AbbrevEntry, ctx: DwarfInfo) []const u8 {
238 return ctx.debug_info[self.debug_info_off..][0..self.debug_info_len];
239 }
240
241 inline fn getDebugAbbrev(self: AbbrevEntry, ctx: DwarfInfo) []const u8 {
242 return ctx.debug_abbrev[self.debug_abbrev_off..][0..self.debug_abbrev_len];
243 }
244
245 pub fn getAttributeIterator(self: AbbrevEntry, ctx: DwarfInfo, cuh: CompileUnit.Header) AttributeIterator {
246 return .{ .entry = self, .ctx = ctx, .cuh = cuh };
247 }
248};
249
250pub const Attribute = struct {
251 name: u64,
252 form: u64,
253 debug_info_off: usize,
254 debug_info_len: usize,
255
256 inline fn getDebugInfo(self: Attribute, ctx: DwarfInfo) []const u8 {
257 return ctx.debug_info[self.debug_info_off..][0..self.debug_info_len];
258 }
259
260 pub fn getString(self: Attribute, ctx: DwarfInfo, cuh: CompileUnit.Header) ?[]const u8 {
261 if (self.form != dwarf.FORM.strp) return null;
262 const debug_info = self.getDebugInfo(ctx);
263 const off = if (cuh.is_64bit)
264 mem.readIntLittle(u64, debug_info[0..8])
265 else
266 mem.readIntLittle(u32, debug_info[0..4]);
267 return ctx.getString(off);
268 }
269
270 pub fn getConstant(self: Attribute, ctx: DwarfInfo) !?i128 {
271 const debug_info = self.getDebugInfo(ctx);
272 var stream = std.io.fixedBufferStream(debug_info);
273 const reader = stream.reader();
274
275 return switch (self.form) {
276 dwarf.FORM.data1 => debug_info[0],
277 dwarf.FORM.data2 => mem.readIntLittle(u16, debug_info[0..2]),
278 dwarf.FORM.data4 => mem.readIntLittle(u32, debug_info[0..4]),
279 dwarf.FORM.data8 => mem.readIntLittle(u64, debug_info[0..8]),
280 dwarf.FORM.udata => try leb.readULEB128(u64, reader),
281 dwarf.FORM.sdata => try leb.readILEB128(i64, reader),
282 else => null,
283 };
284 }
285
286 pub fn getReference(self: Attribute, ctx: DwarfInfo) !?u64 {
287 const debug_info = self.getDebugInfo(ctx);
288 var stream = std.io.fixedBufferStream(debug_info);
289 const reader = stream.reader();
290
291 return switch (self.form) {
292 dwarf.FORM.ref1 => debug_info[0],
293 dwarf.FORM.ref2 => mem.readIntLittle(u16, debug_info[0..2]),
294 dwarf.FORM.ref4 => mem.readIntLittle(u32, debug_info[0..4]),
295 dwarf.FORM.ref8 => mem.readIntLittle(u64, debug_info[0..8]),
296 dwarf.FORM.ref_udata => try leb.readULEB128(u64, reader),
297 else => null,
298 };
299 }
300
301 pub fn getAddr(self: Attribute, ctx: DwarfInfo, cuh: CompileUnit.Header) ?u64 {
302 if (self.form != dwarf.FORM.addr) return null;
303 const debug_info = self.getDebugInfo(ctx);
304 return switch (cuh.address_size) {
305 1 => debug_info[0],
306 2 => mem.readIntLittle(u16, debug_info[0..2]),
307 4 => mem.readIntLittle(u32, debug_info[0..4]),
308 8 => mem.readIntLittle(u64, debug_info[0..8]),
309 else => unreachable,
310 };
311 }
312};
313
314const AttributeIterator = struct {
315 entry: AbbrevEntry,
316 ctx: DwarfInfo,
317 cuh: CompileUnit.Header,
318 debug_abbrev_pos: usize = 0,
319 debug_info_pos: usize = 0,
320
321 pub fn next(self: *AttributeIterator) !?Attribute {
322 const debug_abbrev = self.entry.getDebugAbbrev(self.ctx);
323 if (self.debug_abbrev_pos >= debug_abbrev.len) return null;
324
325 var stream = std.io.fixedBufferStream(debug_abbrev[self.debug_abbrev_pos..]);
326 var creader = std.io.countingReader(stream.reader());
327 const reader = creader.reader();
328
329 const name = try leb.readULEB128(u64, reader);
330 const form = try leb.readULEB128(u64, reader);
331
332 self.debug_abbrev_pos += (math.cast(usize, creader.bytes_read) orelse return error.Overflow);
333
334 const len = try findFormSize(
335 self.ctx,
336 form,
337 self.debug_info_pos + self.entry.debug_info_off,
338 self.cuh,
339 );
340 const attr = Attribute{
341 .name = name,
342 .form = form,
343 .debug_info_off = self.debug_info_pos + self.entry.debug_info_off,
344 .debug_info_len = len,
345 };
346
347 self.debug_info_pos += len;
348
349 return attr;
350 }
351};
352
353fn getAbbrevEntry(self: DwarfInfo, da_off: usize, da_len: usize, di_off: usize, di_len: usize) !AbbrevEntry {
354 const debug_abbrev = self.debug_abbrev[da_off..][0..da_len];
355 var stream = std.io.fixedBufferStream(debug_abbrev);
356 var creader = std.io.countingReader(stream.reader());
357 const reader = creader.reader();
358
359 const tag = try leb.readULEB128(u64, reader);
360 const children = switch (tag) {
361 std.dwarf.TAG.const_type,
362 std.dwarf.TAG.packed_type,
363 std.dwarf.TAG.pointer_type,
364 std.dwarf.TAG.reference_type,
365 std.dwarf.TAG.restrict_type,
366 std.dwarf.TAG.rvalue_reference_type,
367 std.dwarf.TAG.shared_type,
368 std.dwarf.TAG.volatile_type,
369 => if (creader.bytes_read == da_len) std.dwarf.CHILDREN.no else try reader.readByte(),
370 else => try reader.readByte(),
371 };
372
373 const pos = math.cast(usize, creader.bytes_read) orelse return error.Overflow;
374
375 return AbbrevEntry{
376 .tag = tag,
377 .children = children,
378 .debug_abbrev_off = pos + da_off,
379 .debug_abbrev_len = da_len - pos,
380 .debug_info_off = di_off,
381 .debug_info_len = di_len,
382 };
383}
384
385fn findFormSize(self: DwarfInfo, form: u64, di_off: usize, cuh: CompileUnit.Header) !usize {
386 const debug_info = self.debug_info[di_off..];
387 var stream = std.io.fixedBufferStream(debug_info);
388 var creader = std.io.countingReader(stream.reader());
389 const reader = creader.reader();
390
391 switch (form) {
392 dwarf.FORM.strp => return if (cuh.is_64bit) @sizeOf(u64) else @sizeOf(u32),
393 dwarf.FORM.sec_offset => return if (cuh.is_64bit) @sizeOf(u64) else @sizeOf(u32),
394 dwarf.FORM.addr => return cuh.address_size,
395 dwarf.FORM.exprloc => {
396 const expr_len = try leb.readULEB128(u64, reader);
397 var i: u64 = 0;
398 while (i < expr_len) : (i += 1) {
399 _ = try reader.readByte();
400 }
401 return math.cast(usize, creader.bytes_read) orelse error.Overflow;
402 },
403 dwarf.FORM.flag_present => return 0,
404
405 dwarf.FORM.data1 => return @sizeOf(u8),
406 dwarf.FORM.data2 => return @sizeOf(u16),
407 dwarf.FORM.data4 => return @sizeOf(u32),
408 dwarf.FORM.data8 => return @sizeOf(u64),
409 dwarf.FORM.udata => {
410 _ = try leb.readULEB128(u64, reader);
411 return math.cast(usize, creader.bytes_read) orelse error.Overflow;
412 },
413 dwarf.FORM.sdata => {
414 _ = try leb.readILEB128(i64, reader);
415 return math.cast(usize, creader.bytes_read) orelse error.Overflow;
416 },
417
418 dwarf.FORM.ref1 => return @sizeOf(u8),
419 dwarf.FORM.ref2 => return @sizeOf(u16),
420 dwarf.FORM.ref4 => return @sizeOf(u32),
421 dwarf.FORM.ref8 => return @sizeOf(u64),
422 dwarf.FORM.ref_udata => {
423 _ = try leb.readULEB128(u64, reader);
424 return math.cast(usize, creader.bytes_read) orelse error.Overflow;
425 },
426
427 else => return error.ToDo,
428 }
429}
430
431fn findAbbrevEntrySize(self: DwarfInfo, da_off: usize, da_len: usize, di_off: usize, cuh: CompileUnit.Header) !usize {
432 const debug_abbrev = self.debug_abbrev[da_off..][0..da_len];
433 var stream = std.io.fixedBufferStream(debug_abbrev);
434 var creader = std.io.countingReader(stream.reader());
435 const reader = creader.reader();
436
437 const tag = try leb.readULEB128(u64, reader);
438 switch (tag) {
439 std.dwarf.TAG.const_type,
440 std.dwarf.TAG.packed_type,
441 std.dwarf.TAG.pointer_type,
442 std.dwarf.TAG.reference_type,
443 std.dwarf.TAG.restrict_type,
444 std.dwarf.TAG.rvalue_reference_type,
445 std.dwarf.TAG.shared_type,
446 std.dwarf.TAG.volatile_type,
447 => if (creader.bytes_read != da_len) {
448 _ = try reader.readByte();
449 },
450 else => _ = try reader.readByte(),
451 }
452
453 var len: usize = 0;
454 while (creader.bytes_read < debug_abbrev.len) {
455 _ = try leb.readULEB128(u64, reader);
456 const form = try leb.readULEB128(u64, reader);
457 const form_len = try self.findFormSize(form, di_off + len, cuh);
458 len += form_len;
459 }
460
461 return len;
462}
463
464fn getString(self: DwarfInfo, off: u64) []const u8 {
465 assert(off < self.debug_str.len);
466 return mem.sliceTo(@ptrCast([*:0]const u8, self.debug_str.ptr + @intCast(usize, off)), 0);
467}
src/link/MachO/Object.zig+365-410
......@@ -1,3 +1,7 @@
1//! Represents an input relocatable Object file.
2//! Each Object is fully loaded into memory for easier
3//! access into different data within.
4
15const Object = @This();
26
37const std = @import("std");
......@@ -14,10 +18,12 @@ const sort = std.sort;
1418const trace = @import("../../tracy.zig").trace;
1519
1620const Allocator = mem.Allocator;
17const Atom = @import("Atom.zig");
21const Atom = @import("ZldAtom.zig");
22const AtomIndex = @import("zld.zig").AtomIndex;
23const DwarfInfo = @import("DwarfInfo.zig");
1824const LoadCommandIterator = macho.LoadCommandIterator;
19const MachO = @import("../MachO.zig");
20const SymbolWithLoc = MachO.SymbolWithLoc;
25const Zld = @import("zld.zig").Zld;
26const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
2127
2228name: []const u8,
2329mtime: u64,
......@@ -30,31 +36,33 @@ header: macho.mach_header_64 = undefined,
3036in_symtab: ?[]align(1) const macho.nlist_64 = null,
3137in_strtab: ?[]const u8 = null,
3238
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) = .{},
39/// Output symtab is sorted so that we can easily reference symbols following each
40/// other in address space.
41/// The length of the symtab is at least of the input symtab length however there
42/// can be trailing section symbols.
43symtab: []macho.nlist_64 = undefined,
44/// Can be undefined as set together with in_symtab.
45source_symtab_lookup: []u32 = undefined,
46/// Can be undefined as set together with in_symtab.
47strtab_lookup: []u32 = undefined,
48/// Can be undefined as set together with in_symtab.
49atom_by_index_table: []AtomIndex = undefined,
50/// Can be undefined as set together with in_symtab.
51globals_lookup: []i64 = undefined,
52
53atoms: std.ArrayListUnmanaged(AtomIndex) = .{},
4354
4455pub 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
56 self.atoms.deinit(gpa);
5657 gpa.free(self.name);
5758 gpa.free(self.contents);
59 if (self.in_symtab) |_| {
60 gpa.free(self.source_symtab_lookup);
61 gpa.free(self.strtab_lookup);
62 gpa.free(self.symtab);
63 gpa.free(self.atom_by_index_table);
64 gpa.free(self.globals_lookup);
65 }
5866}
5967
6068pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {
......@@ -93,230 +101,245 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
93101 };
94102 while (it.next()) |cmd| {
95103 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 },
103104 .SYMTAB => {
104105 const symtab = cmd.cast(macho.symtab_command).?;
105 // Sadly, SYMTAB may be at an unaligned offset within the object file.
106106 self.in_symtab = @ptrCast(
107 [*]align(1) const macho.nlist_64,
108 self.contents.ptr + symtab.symoff,
107 [*]const macho.nlist_64,
108 @alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]),
109109 )[0..symtab.nsyms];
110110 self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize];
111 try self.symtab.appendUnalignedSlice(allocator, self.in_symtab.?);
111
112 const nsects = self.getSourceSections().len;
113
114 self.symtab = try allocator.alloc(macho.nlist_64, self.in_symtab.?.len + nsects);
115 self.source_symtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
116 self.strtab_lookup = try allocator.alloc(u32, self.in_symtab.?.len);
117 self.globals_lookup = try allocator.alloc(i64, self.in_symtab.?.len);
118 self.atom_by_index_table = try allocator.alloc(AtomIndex, self.in_symtab.?.len + nsects);
119
120 for (self.symtab) |*sym| {
121 sym.* = .{
122 .n_value = 0,
123 .n_sect = 0,
124 .n_desc = 0,
125 .n_strx = 0,
126 .n_type = 0,
127 };
128 }
129
130 mem.set(i64, self.globals_lookup, -1);
131 mem.set(AtomIndex, self.atom_by_index_table, 0);
132
133 // You would expect that the symbol table is at least pre-sorted based on symbol's type:
134 // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance,
135 // the GO compiler does not necessarily respect that therefore we sort immediately by type
136 // and address within.
137 var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(allocator, self.in_symtab.?.len);
138 defer sorted_all_syms.deinit();
139
140 for (self.in_symtab.?) |_, index| {
141 sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });
142 }
143
144 // We sort by type: defined < undefined, and
145 // afterwards by address in each group. Normally, dysymtab should
146 // be enough to guarantee the sort, but turns out not every compiler
147 // is kind enough to specify the symbols in the correct order.
148 sort.sort(SymbolAtIndex, sorted_all_syms.items, self, SymbolAtIndex.lessThan);
149
150 for (sorted_all_syms.items) |sym_id, i| {
151 const sym = sym_id.getSymbol(self);
152
153 self.symtab[i] = sym;
154 self.source_symtab_lookup[i] = sym_id.index;
155
156 const sym_name_len = mem.sliceTo(@ptrCast([*:0]const u8, self.in_strtab.?.ptr + sym.n_strx), 0).len + 1;
157 self.strtab_lookup[i] = @intCast(u32, sym_name_len);
158 }
112159 },
113160 else => {},
114161 }
115162 }
116163}
117164
118const Context = struct {
119 object: *const Object,
120};
121
122165const SymbolAtIndex = struct {
123166 index: u32,
124167
168 const Context = *const Object;
169
125170 fn getSymbol(self: SymbolAtIndex, ctx: Context) macho.nlist_64 {
126 return ctx.object.getSourceSymbol(self.index).?;
171 return ctx.in_symtab.?[self.index];
127172 }
128173
129174 fn getSymbolName(self: SymbolAtIndex, ctx: Context) []const u8 {
130 const sym = self.getSymbol(ctx);
131 return ctx.object.getString(sym.n_strx);
175 const off = self.getSymbol(ctx).n_strx;
176 return mem.sliceTo(@ptrCast([*:0]const u8, ctx.in_strtab.?.ptr + off), 0);
132177 }
133178
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).
179 /// Performs lexicographic-like check.
180 /// * lhs and rhs defined
181 /// * if lhs == rhs
182 /// * if lhs.n_sect == rhs.n_sect
183 /// * ext < weak < local < temp
184 /// * lhs.n_sect < rhs.n_sect
185 /// * lhs < rhs
186 /// * !rhs is undefined
136187 fn lessThan(ctx: Context, lhs_index: SymbolAtIndex, rhs_index: SymbolAtIndex) bool {
137188 const lhs = lhs_index.getSymbol(ctx);
138189 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 {
190 if (lhs.sect() and rhs.sect()) {
191 if (lhs.n_value == rhs.n_value) {
192 if (lhs.n_sect == rhs.n_sect) {
193 if (lhs.ext() and rhs.ext()) {
194 if ((lhs.pext() or lhs.weakDef()) and (rhs.pext() or rhs.weakDef())) {
195 return false;
196 } else return rhs.pext() or rhs.weakDef();
197 } else {
198 const lhs_name = lhs_index.getSymbolName(ctx);
199 const lhs_temp = mem.startsWith(u8, lhs_name, "l") or mem.startsWith(u8, lhs_name, "L");
200 const rhs_name = rhs_index.getSymbolName(ctx);
201 const rhs_temp = mem.startsWith(u8, rhs_name, "l") or mem.startsWith(u8, rhs_name, "L");
202 if (lhs_temp and rhs_temp) {
203 return false;
204 } else return rhs_temp;
205 }
206 } else return lhs.n_sect < rhs.n_sect;
207 } else return lhs.n_value < rhs.n_value;
208 } else if (lhs.undf() and rhs.undf()) {
165209 return false;
166 }
210 } else return rhs.undf();
167211 }
168212
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);
213 fn lessThanByNStrx(ctx: Context, lhs: SymbolAtIndex, rhs: SymbolAtIndex) bool {
214 return lhs.getSymbol(ctx).n_strx < rhs.getSymbol(ctx).n_strx;
172215 }
173216};
174217
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,
218fn filterSymbolsBySection(symbols: []macho.nlist_64, n_sect: u8) struct {
219 index: u32,
220 len: u32,
221} {
222 const FirstMatch = struct {
223 n_sect: u8,
184224
185 pub fn predicate(pred: @This(), index: SymbolAtIndex) bool {
186 return index.getSymbol(pred.ctx).n_value >= pred.addr;
225 pub fn predicate(pred: @This(), symbol: macho.nlist_64) bool {
226 return symbol.n_sect == pred.n_sect;
187227 }
188228 };
229 const FirstNonMatch = struct {
230 n_sect: u8,
189231
190 const start = MachO.findFirst(SymbolAtIndex, indexes, 0, Predicate{
191 .addr = start_addr,
192 .ctx = ctx,
232 pub fn predicate(pred: @This(), symbol: macho.nlist_64) bool {
233 return symbol.n_sect != pred.n_sect;
234 }
235 };
236
237 const index = @import("zld.zig").lsearch(macho.nlist_64, symbols, FirstMatch{
238 .n_sect = n_sect,
193239 });
194 const end = MachO.findFirst(SymbolAtIndex, indexes, start, Predicate{
195 .addr = end_addr,
196 .ctx = ctx,
240 const len = @import("zld.zig").lsearch(macho.nlist_64, symbols[index..], FirstNonMatch{
241 .n_sect = n_sect,
197242 });
198243
199 return indexes[start..end];
244 return .{ .index = @intCast(u32, index), .len = @intCast(u32, len) };
200245}
201246
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 {
247fn filterSymbolsByAddress(symbols: []macho.nlist_64, n_sect: u8, start_addr: u64, end_addr: u64) struct {
248 index: u32,
249 len: u32,
250} {
207251 const Predicate = struct {
208252 addr: u64,
253 n_sect: u8,
209254
210 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
211 return rel.r_address < self.addr;
255 pub fn predicate(pred: @This(), symbol: macho.nlist_64) bool {
256 return symbol.n_value >= pred.addr;
212257 }
213258 };
214259
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 });
260 const index = @import("zld.zig").lsearch(macho.nlist_64, symbols, Predicate{
261 .addr = start_addr,
262 .n_sect = n_sect,
263 });
264 const len = @import("zld.zig").lsearch(macho.nlist_64, symbols[index..], Predicate{
265 .addr = end_addr,
266 .n_sect = n_sect,
267 });
217268
218 return relocs[start..end];
269 return .{ .index = @intCast(u32, index), .len = @intCast(u32, len) };
219270}
220271
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 });
272const SortedSection = struct {
273 header: macho.section_64,
274 id: u8,
275};
276
277fn sectionLessThanByAddress(ctx: void, lhs: SortedSection, rhs: SortedSection) bool {
278 _ = ctx;
279 if (lhs.header.addr == rhs.header.addr) {
280 return lhs.id < rhs.id;
235281 }
282 return lhs.header.addr < rhs.header.addr;
236283}
237284
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);
285/// Splits input sections into Atoms.
286/// If the Object was compiled with `MH_SUBSECTIONS_VIA_SYMBOLS`, splits section
287/// into subsections where each subsection then represents an Atom.
288pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u31) !void {
289 const gpa = zld.gpa;
241290
242 const tracy = trace(@src());
243 defer tracy.end();
291 log.debug("splitting object({d}, {s}) into atoms", .{ object_id, self.name });
244292
245 const gpa = macho_file.base.allocator;
293 const sections = self.getSourceSections();
294 for (sections) |sect, id| {
295 if (sect.isDebug()) continue;
296 const out_sect_id = (try zld.getOutputSection(sect)) orelse {
297 log.debug(" unhandled section '{s},{s}'", .{ sect.segName(), sect.sectName() });
298 continue;
299 };
300 if (sect.size == 0) continue;
246301
247 log.debug("splitting object({d}, {s}) into atoms: one-shot mode", .{ object_id, self.name });
302 const sect_id = @intCast(u8, id);
303 const sym = self.getSectionAliasSymbolPtr(sect_id);
304 sym.* = .{
305 .n_strx = 0,
306 .n_type = macho.N_SECT,
307 .n_sect = out_sect_id + 1,
308 .n_desc = 0,
309 .n_value = sect.addr,
310 };
311 }
248312
249 const in_symtab = self.in_symtab orelse {
250 for (self.sections.items) |sect, id| {
313 if (self.in_symtab == null) {
314 for (sections) |sect, id| {
251315 if (sect.isDebug()) continue;
252 const out_sect_id = (try macho_file.getOutputSection(sect)) orelse {
253 log.debug(" unhandled section", .{});
254 continue;
255 };
316 const out_sect_id = (try zld.getOutputSection(sect)) orelse continue;
256317 if (sect.size == 0) continue;
257318
258319 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,
320 const sym_index = self.getSectionAliasSymbolIndex(sect_id);
321 const atom_index = try self.createAtomFromSubsection(
322 zld,
278323 object_id,
279324 sym_index,
325 0,
326 0,
280327 sect.size,
281328 sect.@"align",
282 code,
283 relocs,
284 &.{},
285329 out_sect_id,
286 sect,
287330 );
288 try macho_file.addAtomToSection(atom);
331 zld.addAtomToSection(atom_index);
289332 }
290333 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) });
305334 }
306335
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
313336 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we
314337 // have to infer the start of undef section in the symtab ourselves.
315338 const iundefsym = blk: {
316339 const dysymtab = self.parseDysymtab() orelse {
317 var iundefsym: usize = sorted_all_syms.items.len;
340 var iundefsym: usize = self.in_symtab.?.len;
318341 while (iundefsym > 0) : (iundefsym -= 1) {
319 const sym = sorted_all_syms.items[iundefsym - 1].getSymbol(context);
342 const sym = self.symtab[iundefsym - 1];
320343 if (sym.sect()) break;
321344 }
322345 break :blk iundefsym;
......@@ -325,271 +348,210 @@ pub fn splitIntoAtoms(self: *Object, macho_file: *MachO, object_id: u32) !void {
325348 };
326349
327350 // We only care about defined symbols, so filter every other out.
328 const sorted_syms = sorted_all_syms.items[0..iundefsym];
351 const symtab = try gpa.dupe(macho.nlist_64, self.symtab[0..iundefsym]);
352 defer gpa.free(symtab);
353
329354 const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
330355
331 for (self.sections.items) |sect, id| {
356 // Sort section headers by address.
357 var sorted_sections = try gpa.alloc(SortedSection, sections.len);
358 defer gpa.free(sorted_sections);
359
360 for (sections) |sect, id| {
361 sorted_sections[id] = .{ .header = sect, .id = @intCast(u8, id) };
362 }
363
364 std.sort.sort(SortedSection, sorted_sections, {}, sectionLessThanByAddress);
365
366 var sect_sym_index: u32 = 0;
367 for (sorted_sections) |section| {
368 const sect = section.header;
332369 if (sect.isDebug()) continue;
333370
334 const sect_id = @intCast(u8, id);
371 const sect_id = section.id;
335372 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
336373
337 // Get matching segment/section in the final artifact.
338 const out_sect_id = (try macho_file.getOutputSection(sect)) orelse {
339 log.debug(" unhandled section", .{});
340 continue;
341 };
374 // Get output segment/section in the final artifact.
375 const out_sect_id = (try zld.getOutputSection(sect)) orelse continue;
342376
343377 log.debug(" output sect({d}, '{s},{s}')", .{
344378 out_sect_id + 1,
345 macho_file.sections.items(.header)[out_sect_id].segName(),
346 macho_file.sections.items(.header)[out_sect_id].sectName(),
379 zld.sections.items(.header)[out_sect_id].segName(),
380 zld.sections.items(.header)[out_sect_id].sectName(),
347381 });
348382
349 const cpu_arch = macho_file.base.options.target.cpu.arch;
350
351 // Read section's code
352 const code: ?[]const u8 = if (!sect.isZerofill()) try self.getSectionContents(sect) else null;
383 const cpu_arch = zld.options.target.cpu.arch;
384 const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1);
385 const sect_start_index = sect_sym_index + sect_loc.index;
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];
387 sect_sym_index += sect_loc.len;
359388
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) {
389 if (sect.size == 0) continue;
390 if (subsections_via_symbols and sect_loc.len > 0) {
369391 // If the first nlist does not match the start of the section,
370392 // then we need to encapsulate the memory range [section start, first symbol)
371393 // as a temporary symbol and insert the matching Atom.
372 const first_sym = filtered_syms[0].getSymbol(context);
394 const first_sym = symtab[sect_start_index];
373395 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 };
396 const sym_index = self.getSectionAliasSymbolIndex(sect_id);
386397 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,
398 const atom_index = try self.createAtomFromSubsection(
399 zld,
393400 object_id,
394401 sym_index,
402 0,
403 0,
395404 atom_size,
396405 sect.@"align",
397 atom_code,
398 relocs,
399 &.{},
400406 out_sect_id,
401 sect,
402407 );
403 try macho_file.addAtomToSection(atom);
408 zld.addAtomToSection(atom_index);
404409 }
405410
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);
411 var next_sym_index = sect_start_index;
412 while (next_sym_index < sect_start_index + sect_loc.len) {
413 const next_sym = symtab[next_sym_index];
409414 const addr = next_sym.n_value;
410 const atom_syms = filterSymbolsByAddress(
411 filtered_syms[next_sym_count..],
415 const atom_loc = filterSymbolsByAddress(
416 symtab[next_sym_index..],
417 sect_id + 1,
412418 addr,
413419 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,
428420 );
421 assert(atom_loc.len > 0);
422 const atom_sym_index = atom_loc.index + next_sym_index;
423 const nsyms_trailing = atom_loc.len - 1;
424 next_sym_index += atom_loc.len;
425
426 // TODO: We want to bubble up the first externally defined symbol here.
427 const atom_size = if (next_sym_index < sect_start_index + sect_loc.len)
428 symtab[next_sym_index].n_value - addr
429 else
430 sect.addr + sect.size - addr;
429431
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;
442432 const atom_align = if (addr > 0)
443433 math.min(@ctz(addr), sect.@"align")
444434 else
445435 sect.@"align";
446 const atom = try self.createAtomFromSubsection(
447 macho_file,
436
437 const atom_index = try self.createAtomFromSubsection(
438 zld,
448439 object_id,
449 sorted_atom_syms.items[0].index,
440 atom_sym_index,
441 atom_sym_index + 1,
442 nsyms_trailing,
450443 atom_size,
451444 atom_align,
452 atom_code,
453 relocs,
454 sorted_atom_syms.items[1..],
455445 out_sect_id,
456 sect,
457446 );
458447
448 // TODO rework this at the relocation level
459449 if (cpu_arch == .x86_64 and addr == sect.addr) {
460450 // In x86_64 relocs, it can so happen that the compiler refers to the same
461451 // atom by both the actual assigned symbol and the start of the section. In this
462452 // 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);
453 const alias_index = self.getSectionAliasSymbolIndex(sect_id);
454 self.atom_by_index_table[alias_index] = atom_index;
480455 }
481456
482 try macho_file.addAtomToSection(atom);
457 zld.addAtomToSection(atom_index);
483458 }
484459 } 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,
460 const alias_index = self.getSectionAliasSymbolIndex(sect_id);
461 const atom_index = try self.createAtomFromSubsection(
462 zld,
502463 object_id,
503 sym_index,
464 alias_index,
465 sect_start_index,
466 sect_loc.len,
504467 sect.size,
505468 sect.@"align",
506 code,
507 relocs,
508 filtered_syms,
509469 out_sect_id,
510 sect,
511470 );
512 try macho_file.addAtomToSection(atom);
471 zld.addAtomToSection(atom_index);
513472 }
514473 }
515474}
516475
517476fn createAtomFromSubsection(
518477 self: *Object,
519 macho_file: *MachO,
520 object_id: u32,
478 zld: *Zld,
479 object_id: u31,
521480 sym_index: u32,
481 inner_sym_index: u32,
482 inner_nsyms_trailing: u32,
522483 size: u64,
523484 alignment: u32,
524 code: ?[]const u8,
525 relocs: []align(1) const macho.relocation_info,
526 indexes: []const SymbolAtIndex,
527485 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);
486) !AtomIndex {
487 const gpa = zld.gpa;
488 const atom_index = try zld.createEmptyAtom(sym_index, size, alignment);
489 const atom = zld.getAtomPtr(atom_index);
490 atom.inner_sym_index = inner_sym_index;
491 atom.inner_nsyms_trailing = inner_nsyms_trailing;
533492 atom.file = object_id;
534 self.symtab.items[sym_index].n_sect = out_sect_id + 1;
493 self.symtab[sym_index].n_sect = out_sect_id + 1;
535494
536495 log.debug("creating ATOM(%{d}, '{s}') in sect({d}, '{s},{s}') in object({d})", .{
537496 sym_index,
538 self.getString(sym.n_strx),
497 self.getSymbolName(sym_index),
539498 out_sect_id + 1,
540 macho_file.sections.items(.header)[out_sect_id].segName(),
541 macho_file.sections.items(.header)[out_sect_id].sectName(),
499 zld.sections.items(.header)[out_sect_id].segName(),
500 zld.sections.items(.header)[out_sect_id].sectName(),
542501 object_id,
543502 });
544503
545 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
546 try self.managed_atoms.append(gpa, atom);
504 try self.atoms.append(gpa, atom_index);
505 self.atom_by_index_table[sym_index] = atom_index;
547506
548 if (code) |cc| {
549 assert(size == cc.len);
550 mem.copy(u8, atom.code.items, cc);
507 var it = Atom.getInnerSymbolsIterator(zld, atom_index);
508 while (it.next()) |sym_loc| {
509 const inner = zld.getSymbolPtr(sym_loc);
510 inner.n_sect = out_sect_id + 1;
511 self.atom_by_index_table[sym_loc.sym_index] = atom_index;
551512 }
552513
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;
514 return atom_index;
579515}
580516
581517pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
582518 const symtab = self.in_symtab.?;
583519 if (index >= symtab.len) return null;
584 return symtab[index];
520 const mapped_index = self.source_symtab_lookup[index];
521 return symtab[mapped_index];
522}
523
524/// Expects an arena allocator.
525/// Caller owns memory.
526pub fn createReverseSymbolLookup(self: Object, arena: Allocator) ![]u32 {
527 const symtab = self.in_symtab orelse return &[0]u32{};
528 const lookup = try arena.alloc(u32, symtab.len);
529 for (self.source_symtab_lookup) |source_id, id| {
530 lookup[source_id] = @intCast(u32, id);
531 }
532 return lookup;
585533}
586534
587535pub fn getSourceSection(self: Object, index: u16) macho.section_64 {
588 assert(index < self.sections.items.len);
589 return self.sections.items[index];
536 const sections = self.getSourceSections();
537 assert(index < sections.len);
538 return sections[index];
539}
540
541pub fn getSourceSections(self: Object) []const macho.section_64 {
542 var it = LoadCommandIterator{
543 .ncmds = self.header.ncmds,
544 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
545 };
546 while (it.next()) |cmd| switch (cmd.cmd()) {
547 .SEGMENT_64 => {
548 return cmd.getSections();
549 },
550 else => {},
551 } else unreachable;
590552}
591553
592pub fn parseDataInCode(self: Object) ?[]align(1) const macho.data_in_code_entry {
554pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry {
593555 var it = LoadCommandIterator{
594556 .ncmds = self.header.ncmds,
595557 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
......@@ -600,8 +562,8 @@ pub fn parseDataInCode(self: Object) ?[]align(1) const macho.data_in_code_entry
600562 const dice = cmd.cast(macho.linkedit_data_command).?;
601563 const ndice = @divExact(dice.datasize, @sizeOf(macho.data_in_code_entry));
602564 return @ptrCast(
603 [*]align(1) const macho.data_in_code_entry,
604 self.contents.ptr + dice.dataoff,
565 [*]const macho.data_in_code_entry,
566 @alignCast(@alignOf(macho.data_in_code_entry), &self.contents[dice.dataoff]),
605567 )[0..ndice];
606568 },
607569 else => {},
......@@ -624,73 +586,66 @@ fn parseDysymtab(self: Object) ?macho.dysymtab_command {
624586 } else return null;
625587}
626588
627pub fn parseDwarfInfo(self: Object) error{Overflow}!dwarf.DwarfInfo {
628 var di = dwarf.DwarfInfo{
629 .endian = .Little,
589pub fn parseDwarfInfo(self: Object) DwarfInfo {
590 var di = DwarfInfo{
630591 .debug_info = &[0]u8{},
631592 .debug_abbrev = &[0]u8{},
632593 .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{},
642594 };
643 for (self.sections.items) |sect| {
644 const segname = sect.segName();
595 for (self.getSourceSections()) |sect| {
596 if (!sect.isDebug()) continue;
645597 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 }
598 if (mem.eql(u8, sectname, "__debug_info")) {
599 di.debug_info = self.getSectionContents(sect);
600 } else if (mem.eql(u8, sectname, "__debug_abbrev")) {
601 di.debug_abbrev = self.getSectionContents(sect);
602 } else if (mem.eql(u8, sectname, "__debug_str")) {
603 di.debug_str = self.getSectionContents(sect);
672604 }
673605 }
674606 return di;
675607}
676608
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 });
609pub fn getSectionContents(self: Object, sect: macho.section_64) []const u8 {
610 const size = @intCast(usize, sect.size);
685611 return self.contents[sect.offset..][0..size];
686612}
687613
688pub fn getString(self: Object, off: u32) []const u8 {
614pub fn getSectionAliasSymbolIndex(self: Object, sect_id: u8) u32 {
615 const start = @intCast(u32, self.in_symtab.?.len);
616 return start + sect_id;
617}
618
619pub fn getSectionAliasSymbol(self: *Object, sect_id: u8) macho.nlist_64 {
620 return self.symtab[self.getSectionAliasSymbolIndex(sect_id)];
621}
622
623pub fn getSectionAliasSymbolPtr(self: *Object, sect_id: u8) *macho.nlist_64 {
624 return &self.symtab[self.getSectionAliasSymbolIndex(sect_id)];
625}
626
627pub fn getRelocs(self: Object, sect: macho.section_64) []align(1) const macho.relocation_info {
628 if (sect.nreloc == 0) return &[0]macho.relocation_info{};
629 return @ptrCast([*]align(1) const macho.relocation_info, self.contents.ptr + sect.reloff)[0..sect.nreloc];
630}
631
632pub fn getSymbolName(self: Object, index: u32) []const u8 {
689633 const strtab = self.in_strtab.?;
690 assert(off < strtab.len);
691 return mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + off), 0);
634 const sym = self.symtab[index];
635
636 if (self.getSourceSymbol(index) == null) {
637 assert(sym.n_strx == 0);
638 return "";
639 }
640
641 const start = sym.n_strx;
642 const len = self.strtab_lookup[index];
643
644 return strtab[start..][0 .. len - 1 :0];
692645}
693646
694pub fn getAtomForSymbol(self: Object, sym_index: u32) ?*Atom {
695 return self.atom_by_index_table.get(sym_index);
647pub fn getAtomIndexForSymbol(self: Object, sym_index: u32) ?AtomIndex {
648 const atom_index = self.atom_by_index_table[sym_index];
649 if (atom_index == 0) return null;
650 return atom_index;
696651}
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/Trie.zig+52-10
......@@ -108,7 +108,7 @@ pub const Node = struct {
108108 .label = to_label,
109109 });
110110
111 return if (match == label.len) to_node else mid.put(allocator, label[match..]);
111 return if (match == label.len) mid else mid.put(allocator, label[match..]);
112112 }
113113
114114 // Add a new node.
......@@ -489,6 +489,21 @@ test "Trie basic" {
489489 }
490490}
491491
492fn expectEqualHexStrings(expected: []const u8, given: []const u8) !void {
493 assert(expected.len > 0);
494 if (mem.eql(u8, expected, given)) return;
495 const expected_fmt = try std.fmt.allocPrint(testing.allocator, "{x}", .{std.fmt.fmtSliceHexLower(expected)});
496 defer testing.allocator.free(expected_fmt);
497 const given_fmt = try std.fmt.allocPrint(testing.allocator, "{x}", .{std.fmt.fmtSliceHexLower(given)});
498 defer testing.allocator.free(given_fmt);
499 const idx = mem.indexOfDiff(u8, expected_fmt, given_fmt).?;
500 var padding = try testing.allocator.alloc(u8, idx + 5);
501 defer testing.allocator.free(padding);
502 mem.set(u8, padding, ' ');
503 std.debug.print("\nEXP: {s}\nGIV: {s}\n{s}^ -- first differing byte\n", .{ expected_fmt, given_fmt, padding });
504 return error.TestFailed;
505}
506
492507test "write Trie to a byte stream" {
493508 var gpa = testing.allocator;
494509 var trie: Trie = .{};
......@@ -523,16 +538,14 @@ test "write Trie to a byte stream" {
523538 defer gpa.free(buffer);
524539 var stream = std.io.fixedBufferStream(buffer);
525540 {
526 const nwritten = try trie.write(stream.writer());
527 try testing.expect(nwritten == trie.size);
528 try testing.expect(mem.eql(u8, buffer, &exp_buffer));
541 _ = try trie.write(stream.writer());
542 try expectEqualHexStrings(&exp_buffer, buffer);
529543 }
530544 {
531545 // Writing finalized trie again should yield the same result.
532546 try stream.seekTo(0);
533 const nwritten = try trie.write(stream.writer());
534 try testing.expect(nwritten == trie.size);
535 try testing.expect(mem.eql(u8, buffer, &exp_buffer));
547 _ = try trie.write(stream.writer());
548 try expectEqualHexStrings(&exp_buffer, buffer);
536549 }
537550}
538551
......@@ -562,8 +575,37 @@ test "parse Trie from byte stream" {
562575 var out_buffer = try gpa.alloc(u8, trie.size);
563576 defer gpa.free(out_buffer);
564577 var out_stream = std.io.fixedBufferStream(out_buffer);
565 const nwritten = try trie.write(out_stream.writer());
578 _ = try trie.write(out_stream.writer());
579 try expectEqualHexStrings(&in_buffer, out_buffer);
580}
566581
567 try testing.expect(nwritten == trie.size);
568 try testing.expect(mem.eql(u8, &in_buffer, out_buffer));
582test "ordering bug" {
583 var gpa = testing.allocator;
584 var trie: Trie = .{};
585 defer trie.deinit(gpa);
586
587 try trie.put(gpa, .{
588 .name = "_asStr",
589 .vmaddr_offset = 0x558,
590 .export_flags = 0,
591 });
592 try trie.put(gpa, .{
593 .name = "_a",
594 .vmaddr_offset = 0x8008,
595 .export_flags = 0,
596 });
597 try trie.finalize(gpa);
598
599 const exp_buffer = [_]u8{
600 0x00, 0x01, 0x5F, 0x61, 0x00, 0x06, 0x04, 0x00,
601 0x88, 0x80, 0x02, 0x01, 0x73, 0x53, 0x74, 0x72,
602 0x00, 0x12, 0x03, 0x00, 0xD8, 0x0A, 0x00,
603 };
604
605 var buffer = try gpa.alloc(u8, trie.size);
606 defer gpa.free(buffer);
607 var stream = std.io.fixedBufferStream(buffer);
608 // Writing finalized trie again should yield the same result.
609 _ = try trie.write(stream.writer());
610 try expectEqualHexStrings(&exp_buffer, buffer);
569611}
src/link/MachO/ZldAtom.zig created+1057
......@@ -0,0 +1,1057 @@
1//! An atom is a single smallest unit of measure that will get an
2//! allocated virtual memory address in the final linked image.
3//! For example, we parse each input section within an input relocatable
4//! object file into a set of atoms which are then laid out contiguously
5//! as they were defined in the input file.
6
7const Atom = @This();
8
9const std = @import("std");
10const build_options = @import("build_options");
11const aarch64 = @import("../../arch/aarch64/bits.zig");
12const assert = std.debug.assert;
13const log = std.log.scoped(.atom);
14const macho = std.macho;
15const math = std.math;
16const mem = std.mem;
17const meta = std.meta;
18
19const Allocator = mem.Allocator;
20const Arch = std.Target.Cpu.Arch;
21const AtomIndex = @import("zld.zig").AtomIndex;
22const Object = @import("Object.zig");
23const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
24const Zld = @import("zld.zig").Zld;
25
26/// Each Atom always gets a symbol with the fully qualified name.
27/// The symbol can reside in any object file context structure in `symtab` array
28/// (see `Object`), or if the symbol is a synthetic symbol such as a GOT cell or
29/// a stub trampoline, it can be found in the linkers `locals` arraylist.
30sym_index: u32,
31
32/// -1 means an Atom is a synthetic Atom such as a GOT cell defined by the linker.
33/// Otherwise, it is the index into appropriate object file.
34/// Prefer using `getFile()` helper to get the file index out rather than using
35/// the field directly.
36file: i32,
37
38/// If this Atom is not a synthetic Atom, i.e., references a subsection in an
39/// Object file, `inner_sym_index` and `inner_nsyms_trailing` tell where and if
40/// this Atom contains any additional symbol references that fall within this Atom's
41/// address range. These could for example be an alias symbol which can be used
42/// internally by the relocation records, or if the Object file couldn't be split
43/// into subsections, this Atom may encompass an entire input section.
44inner_sym_index: u32,
45inner_nsyms_trailing: u32,
46
47/// Size of this atom.
48size: u64,
49
50/// Alignment of this atom as a power of 2.
51/// For instance, aligmment of 0 should be read as 2^0 = 1 byte aligned.
52alignment: u32,
53
54/// Cached index and length into the relocations records array that correspond to
55/// this Atom and need to be resolved before the Atom can be committed into the
56/// final linked image.
57/// Do not use these fields directly. Instead, use `getAtomRelocs()` helper.
58cached_relocs_start: i32,
59cached_relocs_len: u32,
60
61/// Points to the previous and next neighbours
62next_index: ?AtomIndex,
63prev_index: ?AtomIndex,
64
65pub const empty = Atom{
66 .sym_index = 0,
67 .inner_sym_index = 0,
68 .inner_nsyms_trailing = 0,
69 .file = -1,
70 .size = 0,
71 .alignment = 0,
72 .cached_relocs_start = -1,
73 .cached_relocs_len = 0,
74 .prev_index = null,
75 .next_index = null,
76};
77
78/// Returns `null` if the Atom is a synthetic Atom.
79/// Otherwise, returns an index into an array of Objects.
80pub inline fn getFile(self: Atom) ?u31 {
81 if (self.file == -1) return null;
82 return @intCast(u31, self.file);
83}
84
85pub inline fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
86 return .{
87 .sym_index = self.sym_index,
88 .file = self.file,
89 };
90}
91
92const InnerSymIterator = struct {
93 sym_index: u32,
94 count: u32,
95 file: i32,
96
97 pub fn next(it: *@This()) ?SymbolWithLoc {
98 if (it.count == 0) return null;
99 const res = SymbolWithLoc{ .sym_index = it.sym_index, .file = it.file };
100 it.sym_index += 1;
101 it.count -= 1;
102 return res;
103 }
104};
105
106/// Returns an iterator over potentially contained symbols.
107/// Panics when called on a synthetic Atom.
108pub fn getInnerSymbolsIterator(zld: *Zld, atom_index: AtomIndex) InnerSymIterator {
109 const atom = zld.getAtom(atom_index);
110 assert(atom.getFile() != null);
111 return .{
112 .sym_index = atom.inner_sym_index,
113 .count = atom.inner_nsyms_trailing,
114 .file = atom.file,
115 };
116}
117
118/// Returns a section alias symbol if one is defined.
119/// An alias symbol is used to represent the start of an input section
120/// if there were no symbols defined within that range.
121/// Alias symbols are only used on x86_64.
122pub fn getSectionAlias(zld: *Zld, atom_index: AtomIndex) ?SymbolWithLoc {
123 const atom = zld.getAtom(atom_index);
124 assert(atom.getFile() != null);
125
126 const object = zld.objects.items[atom.getFile().?];
127 const nbase = @intCast(u32, object.in_symtab.?.len);
128 const ntotal = @intCast(u32, object.symtab.len);
129 var sym_index: u32 = nbase;
130 while (sym_index < ntotal) : (sym_index += 1) {
131 if (object.getAtomIndexForSymbol(sym_index)) |other_atom_index| {
132 if (other_atom_index == atom_index) return SymbolWithLoc{
133 .sym_index = sym_index,
134 .file = atom.file,
135 };
136 }
137 }
138 return null;
139}
140
141/// Given an index into a contained symbol within, calculates an offset wrt
142/// the start of this Atom.
143pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: AtomIndex, sym_index: u32) u64 {
144 const atom = zld.getAtom(atom_index);
145 assert(atom.getFile() != null);
146
147 if (atom.sym_index == sym_index) return 0;
148
149 const object = zld.objects.items[atom.getFile().?];
150 const source_sym = object.getSourceSymbol(sym_index).?;
151 const base_addr = if (object.getSourceSymbol(atom.sym_index)) |sym|
152 sym.n_value
153 else blk: {
154 const nbase = @intCast(u32, object.in_symtab.?.len);
155 const sect_id = @intCast(u16, atom.sym_index - nbase);
156 const source_sect = object.getSourceSection(sect_id);
157 break :blk source_sect.addr;
158 };
159 return source_sym.n_value - base_addr;
160}
161
162pub fn scanAtomRelocs(
163 zld: *Zld,
164 atom_index: AtomIndex,
165 relocs: []align(1) const macho.relocation_info,
166 reverse_lookup: []u32,
167) !void {
168 const arch = zld.options.target.cpu.arch;
169 const atom = zld.getAtom(atom_index);
170 assert(atom.getFile() != null); // synthetic atoms do not have relocs
171
172 return switch (arch) {
173 .aarch64 => scanAtomRelocsArm64(zld, atom_index, relocs, reverse_lookup),
174 .x86_64 => scanAtomRelocsX86(zld, atom_index, relocs, reverse_lookup),
175 else => unreachable,
176 };
177}
178
179const RelocContext = struct {
180 base_addr: u64 = 0,
181 base_offset: i32 = 0,
182};
183
184pub fn parseRelocTarget(
185 zld: *Zld,
186 atom_index: AtomIndex,
187 rel: macho.relocation_info,
188 reverse_lookup: []u32,
189) SymbolWithLoc {
190 const atom = zld.getAtom(atom_index);
191 const object = &zld.objects.items[atom.getFile().?];
192
193 if (rel.r_extern == 0) {
194 const sect_id = @intCast(u8, rel.r_symbolnum - 1);
195 const sym_index = object.getSectionAliasSymbolIndex(sect_id);
196 return SymbolWithLoc{ .sym_index = sym_index, .file = atom.file };
197 }
198
199 const sym_index = reverse_lookup[rel.r_symbolnum];
200 const sym_loc = SymbolWithLoc{
201 .sym_index = sym_index,
202 .file = atom.file,
203 };
204 const sym = zld.getSymbol(sym_loc);
205
206 if (sym.sect() and !sym.ext()) {
207 return sym_loc;
208 } else if (object.globals_lookup[sym_index] > -1) {
209 const global_index = @intCast(u32, object.globals_lookup[sym_index]);
210 return zld.globals.items[global_index];
211 } else return sym_loc;
212}
213
214pub fn getRelocTargetAtomIndex(zld: *Zld, rel: macho.relocation_info, target: SymbolWithLoc) ?AtomIndex {
215 const is_via_got = got: {
216 switch (zld.options.target.cpu.arch) {
217 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
218 .ARM64_RELOC_GOT_LOAD_PAGE21,
219 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
220 .ARM64_RELOC_POINTER_TO_GOT,
221 => true,
222 else => false,
223 },
224 .x86_64 => break :got switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
225 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => true,
226 else => false,
227 },
228 else => unreachable,
229 }
230 };
231
232 if (is_via_got) {
233 return zld.getGotAtomIndexForSymbol(target).?; // panic means fatal error
234 }
235 if (zld.getStubsAtomIndexForSymbol(target)) |stubs_atom| return stubs_atom;
236 if (zld.getTlvPtrAtomIndexForSymbol(target)) |tlv_ptr_atom| return tlv_ptr_atom;
237
238 if (target.getFile() == null) {
239 const target_sym_name = zld.getSymbolName(target);
240 if (mem.eql(u8, "__mh_execute_header", target_sym_name)) return null;
241 if (mem.eql(u8, "___dso_handle", target_sym_name)) return null;
242
243 unreachable; // referenced symbol not found
244 }
245
246 const object = zld.objects.items[target.getFile().?];
247 return object.getAtomIndexForSymbol(target.sym_index);
248}
249
250fn scanAtomRelocsArm64(
251 zld: *Zld,
252 atom_index: AtomIndex,
253 relocs: []align(1) const macho.relocation_info,
254 reverse_lookup: []u32,
255) !void {
256 for (relocs) |rel| {
257 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
258
259 switch (rel_type) {
260 .ARM64_RELOC_ADDEND, .ARM64_RELOC_SUBTRACTOR => continue,
261 else => {},
262 }
263
264 if (rel.r_extern == 0) continue;
265
266 const atom = zld.getAtom(atom_index);
267 const object = &zld.objects.items[atom.getFile().?];
268 const sym_index = reverse_lookup[rel.r_symbolnum];
269 const sym_loc = SymbolWithLoc{
270 .sym_index = sym_index,
271 .file = atom.file,
272 };
273 const sym = zld.getSymbol(sym_loc);
274
275 if (sym.sect() and !sym.ext()) continue;
276
277 const target = if (object.globals_lookup[sym_index] > -1) blk: {
278 const global_index = @intCast(u32, object.globals_lookup[sym_index]);
279 break :blk zld.globals.items[global_index];
280 } else sym_loc;
281
282 switch (rel_type) {
283 .ARM64_RELOC_BRANCH26 => {
284 // TODO rewrite relocation
285 try addStub(zld, target);
286 },
287 .ARM64_RELOC_GOT_LOAD_PAGE21,
288 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
289 .ARM64_RELOC_POINTER_TO_GOT,
290 => {
291 // TODO rewrite relocation
292 try addGotEntry(zld, target);
293 },
294 .ARM64_RELOC_TLVP_LOAD_PAGE21,
295 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
296 => {
297 try addTlvPtrEntry(zld, target);
298 },
299 else => {},
300 }
301 }
302}
303
304fn scanAtomRelocsX86(
305 zld: *Zld,
306 atom_index: AtomIndex,
307 relocs: []align(1) const macho.relocation_info,
308 reverse_lookup: []u32,
309) !void {
310 for (relocs) |rel| {
311 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
312
313 switch (rel_type) {
314 .X86_64_RELOC_SUBTRACTOR => continue,
315 else => {},
316 }
317
318 if (rel.r_extern == 0) continue;
319
320 const atom = zld.getAtom(atom_index);
321 const object = &zld.objects.items[atom.getFile().?];
322 const sym_index = reverse_lookup[rel.r_symbolnum];
323 const sym_loc = SymbolWithLoc{
324 .sym_index = sym_index,
325 .file = atom.file,
326 };
327 const sym = zld.getSymbol(sym_loc);
328
329 if (sym.sect() and !sym.ext()) continue;
330
331 const target = if (object.globals_lookup[sym_index] > -1) blk: {
332 const global_index = @intCast(u32, object.globals_lookup[sym_index]);
333 break :blk zld.globals.items[global_index];
334 } else sym_loc;
335
336 switch (rel_type) {
337 .X86_64_RELOC_BRANCH => {
338 // TODO rewrite relocation
339 try addStub(zld, target);
340 },
341 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => {
342 // TODO rewrite relocation
343 try addGotEntry(zld, target);
344 },
345 .X86_64_RELOC_TLV => {
346 try addTlvPtrEntry(zld, target);
347 },
348 else => {},
349 }
350 }
351}
352
353fn addTlvPtrEntry(zld: *Zld, target: SymbolWithLoc) !void {
354 const target_sym = zld.getSymbol(target);
355 if (!target_sym.undf()) return;
356 if (zld.tlv_ptr_table.contains(target)) return;
357
358 const gpa = zld.gpa;
359 const atom_index = try zld.createTlvPtrAtom();
360 const tlv_ptr_index = @intCast(u32, zld.tlv_ptr_entries.items.len);
361 try zld.tlv_ptr_entries.append(gpa, .{
362 .target = target,
363 .atom_index = atom_index,
364 });
365 try zld.tlv_ptr_table.putNoClobber(gpa, target, tlv_ptr_index);
366}
367
368fn addGotEntry(zld: *Zld, target: SymbolWithLoc) !void {
369 if (zld.got_table.contains(target)) return;
370 const gpa = zld.gpa;
371 const atom_index = try zld.createGotAtom();
372 const got_index = @intCast(u32, zld.got_entries.items.len);
373 try zld.got_entries.append(gpa, .{
374 .target = target,
375 .atom_index = atom_index,
376 });
377 try zld.got_table.putNoClobber(gpa, target, got_index);
378}
379
380fn addStub(zld: *Zld, target: SymbolWithLoc) !void {
381 const target_sym = zld.getSymbol(target);
382 if (!target_sym.undf()) return;
383 if (zld.stubs_table.contains(target)) return;
384
385 const gpa = zld.gpa;
386 _ = try zld.createStubHelperAtom();
387 _ = try zld.createLazyPointerAtom();
388 const atom_index = try zld.createStubAtom();
389 const stubs_index = @intCast(u32, zld.stubs.items.len);
390 try zld.stubs.append(gpa, .{
391 .target = target,
392 .atom_index = atom_index,
393 });
394 try zld.stubs_table.putNoClobber(gpa, target, stubs_index);
395}
396
397pub fn resolveRelocs(
398 zld: *Zld,
399 atom_index: AtomIndex,
400 atom_code: []u8,
401 atom_relocs: []align(1) const macho.relocation_info,
402 reverse_lookup: []u32,
403) !void {
404 const arch = zld.options.target.cpu.arch;
405 const atom = zld.getAtom(atom_index);
406 assert(atom.getFile() != null); // synthetic atoms do not have relocs
407
408 const object = zld.objects.items[atom.getFile().?];
409 const ctx: RelocContext = blk: {
410 if (object.getSourceSymbol(atom.sym_index)) |source_sym| {
411 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
412 break :blk .{
413 .base_addr = source_sect.addr,
414 .base_offset = @intCast(i32, source_sym.n_value - source_sect.addr),
415 };
416 }
417 const nbase = @intCast(u32, object.in_symtab.?.len);
418 const sect_id = @intCast(u16, atom.sym_index - nbase);
419 const source_sect = object.getSourceSection(sect_id);
420 break :blk .{
421 .base_addr = source_sect.addr,
422 .base_offset = 0,
423 };
424 };
425
426 log.debug("resolving relocations in ATOM(%{d}, '{s}')", .{
427 atom.sym_index,
428 zld.getSymbolName(atom.getSymbolWithLoc()),
429 });
430
431 return switch (arch) {
432 .aarch64 => resolveRelocsArm64(zld, atom_index, atom_code, atom_relocs, reverse_lookup, ctx),
433 .x86_64 => resolveRelocsX86(zld, atom_index, atom_code, atom_relocs, reverse_lookup, ctx),
434 else => unreachable,
435 };
436}
437
438pub fn getRelocTargetAddress(zld: *Zld, rel: macho.relocation_info, target: SymbolWithLoc, is_tlv: bool) !u64 {
439 const target_atom_index = getRelocTargetAtomIndex(zld, rel, target) orelse {
440 // If there is no atom for target, we still need to check for special, atom-less
441 // symbols such as `___dso_handle`.
442 const target_name = zld.getSymbolName(target);
443 const atomless_sym = zld.getSymbol(target);
444 log.debug(" | atomless target '{s}'", .{target_name});
445 return atomless_sym.n_value;
446 };
447 const target_atom = zld.getAtom(target_atom_index);
448 log.debug(" | target ATOM(%{d}, '{s}') in object({?})", .{
449 target_atom.sym_index,
450 zld.getSymbolName(target_atom.getSymbolWithLoc()),
451 target_atom.file,
452 });
453
454 const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());
455 assert(target_sym.n_desc != @import("zld.zig").N_DEAD);
456
457 // If `target` is contained within the target atom, pull its address value.
458 const offset = if (target_atom.getFile() != null) blk: {
459 const object = zld.objects.items[target_atom.getFile().?];
460 break :blk if (object.getSourceSymbol(target.sym_index)) |_|
461 Atom.calcInnerSymbolOffset(zld, target_atom_index, target.sym_index)
462 else
463 0; // section alias
464 } else 0;
465 const base_address: u64 = if (is_tlv) base_address: {
466 // For TLV relocations, the value specified as a relocation is the displacement from the
467 // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
468 // defined TLV template init section in the following order:
469 // * wrt to __thread_data if defined, then
470 // * wrt to __thread_bss
471 const sect_id: u16 = sect_id: {
472 if (zld.getSectionByName("__DATA", "__thread_data")) |i| {
473 break :sect_id i;
474 } else if (zld.getSectionByName("__DATA", "__thread_bss")) |i| {
475 break :sect_id i;
476 } else {
477 log.err("threadlocal variables present but no initializer sections found", .{});
478 log.err(" __thread_data not found", .{});
479 log.err(" __thread_bss not found", .{});
480 return error.FailedToResolveRelocationTarget;
481 }
482 };
483 break :base_address zld.sections.items(.header)[sect_id].addr;
484 } else 0;
485 return target_sym.n_value + offset - base_address;
486}
487
488fn resolveRelocsArm64(
489 zld: *Zld,
490 atom_index: AtomIndex,
491 atom_code: []u8,
492 atom_relocs: []align(1) const macho.relocation_info,
493 reverse_lookup: []u32,
494 context: RelocContext,
495) !void {
496 const atom = zld.getAtom(atom_index);
497 const object = zld.objects.items[atom.getFile().?];
498
499 var addend: ?i64 = null;
500 var subtractor: ?SymbolWithLoc = null;
501
502 for (atom_relocs) |rel| {
503 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
504
505 switch (rel_type) {
506 .ARM64_RELOC_ADDEND => {
507 assert(addend == null);
508
509 log.debug(" RELA({s}) @ {x} => {x}", .{ @tagName(rel_type), rel.r_address, rel.r_symbolnum });
510
511 addend = rel.r_symbolnum;
512 continue;
513 },
514 .ARM64_RELOC_SUBTRACTOR => {
515 assert(subtractor == null);
516
517 log.debug(" RELA({s}) @ {x} => %{d} in object({d})", .{
518 @tagName(rel_type),
519 rel.r_address,
520 rel.r_symbolnum,
521 atom.file,
522 });
523
524 subtractor = parseRelocTarget(zld, atom_index, rel, reverse_lookup);
525 continue;
526 },
527 else => {},
528 }
529
530 const target = parseRelocTarget(zld, atom_index, rel, reverse_lookup);
531 const rel_offset = @intCast(u32, rel.r_address - context.base_offset);
532
533 log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{
534 @tagName(rel_type),
535 rel.r_address,
536 target.sym_index,
537 zld.getSymbolName(target),
538 target.file,
539 });
540
541 const source_addr = blk: {
542 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
543 break :blk source_sym.n_value + rel_offset;
544 };
545 const is_tlv = is_tlv: {
546 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
547 const header = zld.sections.items(.header)[source_sym.n_sect - 1];
548 break :is_tlv header.@"type"() == macho.S_THREAD_LOCAL_VARIABLES;
549 };
550 const target_addr = try getRelocTargetAddress(zld, rel, target, is_tlv);
551
552 log.debug(" | source_addr = 0x{x}", .{source_addr});
553
554 switch (rel_type) {
555 .ARM64_RELOC_BRANCH26 => {
556 const actual_target = if (zld.getStubsAtomIndexForSymbol(target)) |stub_atom_index| inner: {
557 const stub_atom = zld.getAtom(stub_atom_index);
558 break :inner stub_atom.getSymbolWithLoc();
559 } else target;
560 log.debug(" source {s} (object({?})), target {s} (object({?}))", .{
561 zld.getSymbolName(atom.getSymbolWithLoc()),
562 atom.file,
563 zld.getSymbolName(target),
564 zld.getAtom(getRelocTargetAtomIndex(zld, rel, target).?).file,
565 });
566
567 const displacement = if (calcPcRelativeDisplacementArm64(
568 source_addr,
569 zld.getSymbol(actual_target).n_value,
570 )) |disp| blk: {
571 log.debug(" | target_addr = 0x{x}", .{zld.getSymbol(actual_target).n_value});
572 break :blk disp;
573 } else |_| blk: {
574 const thunk_index = zld.thunk_table.get(atom_index).?;
575 const thunk = zld.thunks.items[thunk_index];
576 const thunk_sym = zld.getSymbol(thunk.getTrampolineForSymbol(
577 zld,
578 actual_target,
579 ).?);
580 log.debug(" | target_addr = 0x{x}", .{thunk_sym.n_value});
581 break :blk try calcPcRelativeDisplacementArm64(source_addr, thunk_sym.n_value);
582 };
583
584 const code = atom_code[rel_offset..][0..4];
585 var inst = aarch64.Instruction{
586 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
587 aarch64.Instruction,
588 aarch64.Instruction.unconditional_branch_immediate,
589 ), code),
590 };
591 inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));
592 mem.writeIntLittle(u32, code, inst.toU32());
593 },
594
595 .ARM64_RELOC_PAGE21,
596 .ARM64_RELOC_GOT_LOAD_PAGE21,
597 .ARM64_RELOC_TLVP_LOAD_PAGE21,
598 => {
599 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + (addend orelse 0));
600
601 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
602
603 const pages = @bitCast(u21, calcNumberOfPages(source_addr, adjusted_target_addr));
604 const code = atom_code[rel_offset..][0..4];
605 var inst = aarch64.Instruction{
606 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
607 aarch64.Instruction,
608 aarch64.Instruction.pc_relative_address,
609 ), code),
610 };
611 inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
612 inst.pc_relative_address.immlo = @truncate(u2, pages);
613 mem.writeIntLittle(u32, code, inst.toU32());
614 addend = null;
615 },
616
617 .ARM64_RELOC_PAGEOFF12 => {
618 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + (addend orelse 0));
619
620 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
621
622 const code = atom_code[rel_offset..][0..4];
623 if (isArithmeticOp(code)) {
624 const off = try calcPageOffset(adjusted_target_addr, .arithmetic);
625 var inst = aarch64.Instruction{
626 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
627 aarch64.Instruction,
628 aarch64.Instruction.add_subtract_immediate,
629 ), code),
630 };
631 inst.add_subtract_immediate.imm12 = off;
632 mem.writeIntLittle(u32, code, inst.toU32());
633 } else {
634 var inst = aarch64.Instruction{
635 .load_store_register = mem.bytesToValue(meta.TagPayload(
636 aarch64.Instruction,
637 aarch64.Instruction.load_store_register,
638 ), code),
639 };
640 const off = try calcPageOffset(adjusted_target_addr, switch (inst.load_store_register.size) {
641 0 => if (inst.load_store_register.v == 1)
642 PageOffsetInstKind.load_store_128
643 else
644 PageOffsetInstKind.load_store_8,
645 1 => .load_store_16,
646 2 => .load_store_32,
647 3 => .load_store_64,
648 });
649 inst.load_store_register.offset = off;
650 mem.writeIntLittle(u32, code, inst.toU32());
651 }
652 addend = null;
653 },
654
655 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {
656 const code = atom_code[rel_offset..][0..4];
657 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + (addend orelse 0));
658
659 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
660
661 const off = try calcPageOffset(adjusted_target_addr, .load_store_64);
662 var inst: aarch64.Instruction = .{
663 .load_store_register = mem.bytesToValue(meta.TagPayload(
664 aarch64.Instruction,
665 aarch64.Instruction.load_store_register,
666 ), code),
667 };
668 inst.load_store_register.offset = off;
669 mem.writeIntLittle(u32, code, inst.toU32());
670 addend = null;
671 },
672
673 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
674 const code = atom_code[rel_offset..][0..4];
675 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + (addend orelse 0));
676
677 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
678
679 const RegInfo = struct {
680 rd: u5,
681 rn: u5,
682 size: u2,
683 };
684 const reg_info: RegInfo = blk: {
685 if (isArithmeticOp(code)) {
686 const inst = mem.bytesToValue(meta.TagPayload(
687 aarch64.Instruction,
688 aarch64.Instruction.add_subtract_immediate,
689 ), code);
690 break :blk .{
691 .rd = inst.rd,
692 .rn = inst.rn,
693 .size = inst.sf,
694 };
695 } else {
696 const inst = mem.bytesToValue(meta.TagPayload(
697 aarch64.Instruction,
698 aarch64.Instruction.load_store_register,
699 ), code);
700 break :blk .{
701 .rd = inst.rt,
702 .rn = inst.rn,
703 .size = inst.size,
704 };
705 }
706 };
707
708 var inst = if (zld.tlv_ptr_table.contains(target)) aarch64.Instruction{
709 .load_store_register = .{
710 .rt = reg_info.rd,
711 .rn = reg_info.rn,
712 .offset = try calcPageOffset(adjusted_target_addr, .load_store_64),
713 .opc = 0b01,
714 .op1 = 0b01,
715 .v = 0,
716 .size = reg_info.size,
717 },
718 } else aarch64.Instruction{
719 .add_subtract_immediate = .{
720 .rd = reg_info.rd,
721 .rn = reg_info.rn,
722 .imm12 = try calcPageOffset(adjusted_target_addr, .arithmetic),
723 .sh = 0,
724 .s = 0,
725 .op = 0,
726 .sf = @truncate(u1, reg_info.size),
727 },
728 };
729 mem.writeIntLittle(u32, code, inst.toU32());
730 addend = null;
731 },
732
733 .ARM64_RELOC_POINTER_TO_GOT => {
734 log.debug(" | target_addr = 0x{x}", .{target_addr});
735 const result = math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr)) orelse
736 return error.Overflow;
737 mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @bitCast(u32, result));
738 },
739
740 .ARM64_RELOC_UNSIGNED => {
741 var ptr_addend = if (rel.r_length == 3)
742 mem.readIntLittle(i64, atom_code[rel_offset..][0..8])
743 else
744 mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
745
746 if (rel.r_extern == 0) {
747 const target_sect_base_addr = object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
748 ptr_addend -= @intCast(i64, target_sect_base_addr);
749 }
750
751 const result = blk: {
752 if (subtractor) |sub| {
753 const sym = zld.getSymbol(sub);
754 break :blk @intCast(i64, target_addr) - @intCast(i64, sym.n_value) + ptr_addend;
755 } else {
756 break :blk @intCast(i64, target_addr) + ptr_addend;
757 }
758 };
759 log.debug(" | target_addr = 0x{x}", .{result});
760
761 if (rel.r_length == 3) {
762 mem.writeIntLittle(u64, atom_code[rel_offset..][0..8], @bitCast(u64, result));
763 } else {
764 mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @truncate(u32, @bitCast(u64, result)));
765 }
766
767 subtractor = null;
768 },
769
770 .ARM64_RELOC_ADDEND => unreachable,
771 .ARM64_RELOC_SUBTRACTOR => unreachable,
772 }
773 }
774}
775
776fn resolveRelocsX86(
777 zld: *Zld,
778 atom_index: AtomIndex,
779 atom_code: []u8,
780 atom_relocs: []align(1) const macho.relocation_info,
781 reverse_lookup: []u32,
782 context: RelocContext,
783) !void {
784 const atom = zld.getAtom(atom_index);
785 const object = zld.objects.items[atom.getFile().?];
786
787 var subtractor: ?SymbolWithLoc = null;
788
789 for (atom_relocs) |rel| {
790 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
791
792 switch (rel_type) {
793 .X86_64_RELOC_SUBTRACTOR => {
794 assert(subtractor == null);
795
796 log.debug(" RELA({s}) @ {x} => %{d} in object({d})", .{
797 @tagName(rel_type),
798 rel.r_address,
799 rel.r_symbolnum,
800 atom.file,
801 });
802
803 subtractor = parseRelocTarget(zld, atom_index, rel, reverse_lookup);
804 continue;
805 },
806 else => {},
807 }
808
809 const target = parseRelocTarget(zld, atom_index, rel, reverse_lookup);
810 const rel_offset = @intCast(u32, rel.r_address - context.base_offset);
811
812 log.debug(" RELA({s}) @ {x} => %{d} in object({?})", .{
813 @tagName(rel_type),
814 rel.r_address,
815 target.sym_index,
816 target.file,
817 });
818
819 const source_addr = blk: {
820 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
821 break :blk source_sym.n_value + rel_offset;
822 };
823 const is_tlv = is_tlv: {
824 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
825 const header = zld.sections.items(.header)[source_sym.n_sect - 1];
826 break :is_tlv header.@"type"() == macho.S_THREAD_LOCAL_VARIABLES;
827 };
828
829 log.debug(" | source_addr = 0x{x}", .{source_addr});
830
831 const target_addr = try getRelocTargetAddress(zld, rel, target, is_tlv);
832
833 switch (rel_type) {
834 .X86_64_RELOC_BRANCH => {
835 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
836 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
837 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
838 const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
839 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
840 },
841
842 .X86_64_RELOC_GOT,
843 .X86_64_RELOC_GOT_LOAD,
844 => {
845 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
846 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
847 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
848 const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
849 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
850 },
851
852 .X86_64_RELOC_TLV => {
853 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
854 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
855 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
856 const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
857
858 if (zld.tlv_ptr_table.get(target) == null) {
859 // We need to rewrite the opcode from movq to leaq.
860 atom_code[rel_offset - 2] = 0x8d;
861 }
862
863 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
864 },
865
866 .X86_64_RELOC_SIGNED,
867 .X86_64_RELOC_SIGNED_1,
868 .X86_64_RELOC_SIGNED_2,
869 .X86_64_RELOC_SIGNED_4,
870 => {
871 const correction: u3 = switch (rel_type) {
872 .X86_64_RELOC_SIGNED => 0,
873 .X86_64_RELOC_SIGNED_1 => 1,
874 .X86_64_RELOC_SIGNED_2 => 2,
875 .X86_64_RELOC_SIGNED_4 => 4,
876 else => unreachable,
877 };
878 var addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]) + correction;
879
880 if (rel.r_extern == 0) {
881 // Note for the future self: when r_extern == 0, we should subtract correction from the
882 // addend.
883 const target_sect_base_addr = object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
884 // We need to add base_offset, i.e., offset of this atom wrt to the source
885 // section. Otherwise, the addend will over-/under-shoot.
886 addend += @intCast(i32, @intCast(i64, context.base_addr + rel_offset + 4) -
887 @intCast(i64, target_sect_base_addr) + context.base_offset);
888 }
889
890 const adjusted_target_addr = @intCast(u64, @intCast(i64, target_addr) + addend);
891
892 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
893
894 const disp = try calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, correction);
895 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
896 },
897
898 .X86_64_RELOC_UNSIGNED => {
899 var addend = if (rel.r_length == 3)
900 mem.readIntLittle(i64, atom_code[rel_offset..][0..8])
901 else
902 mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
903
904 if (rel.r_extern == 0) {
905 const target_sect_base_addr = object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
906 addend -= @intCast(i64, target_sect_base_addr);
907 }
908
909 const result = blk: {
910 if (subtractor) |sub| {
911 const sym = zld.getSymbol(sub);
912 break :blk @intCast(i64, target_addr) - @intCast(i64, sym.n_value) + addend;
913 } else {
914 break :blk @intCast(i64, target_addr) + addend;
915 }
916 };
917 log.debug(" | target_addr = 0x{x}", .{result});
918
919 if (rel.r_length == 3) {
920 mem.writeIntLittle(u64, atom_code[rel_offset..][0..8], @bitCast(u64, result));
921 } else {
922 mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @truncate(u32, @bitCast(u64, result)));
923 }
924
925 subtractor = null;
926 },
927
928 .X86_64_RELOC_SUBTRACTOR => unreachable,
929 }
930 }
931}
932
933inline fn isArithmeticOp(inst: *const [4]u8) bool {
934 const group_decode = @truncate(u5, inst[3]);
935 return ((group_decode >> 2) == 4);
936}
937
938pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {
939 const atom = zld.getAtom(atom_index);
940 assert(atom.getFile() != null); // Synthetic atom shouldn't need to inquire for code.
941 const object = zld.objects.items[atom.getFile().?];
942 const source_sym = object.getSourceSymbol(atom.sym_index) orelse {
943 // If there was no matching symbol present in the source symtab, this means
944 // we are dealing with either an entire section, or part of it, but also
945 // starting at the beginning.
946 const nbase = @intCast(u32, object.in_symtab.?.len);
947 const sect_id = @intCast(u16, atom.sym_index - nbase);
948 const source_sect = object.getSourceSection(sect_id);
949 assert(!source_sect.isZerofill());
950 const code = object.getSectionContents(source_sect);
951 const code_len = @intCast(usize, atom.size);
952 return code[0..code_len];
953 };
954 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
955 assert(!source_sect.isZerofill());
956 const code = object.getSectionContents(source_sect);
957 const offset = @intCast(usize, source_sym.n_value - source_sect.addr);
958 const code_len = @intCast(usize, atom.size);
959 return code[offset..][0..code_len];
960}
961
962pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []align(1) const macho.relocation_info {
963 const atom = zld.getAtomPtr(atom_index);
964 assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs.
965 const object = zld.objects.items[atom.getFile().?];
966
967 const source_sect = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
968 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
969 assert(!source_sect.isZerofill());
970 break :blk source_sect;
971 } else blk: {
972 // If there was no matching symbol present in the source symtab, this means
973 // we are dealing with either an entire section, or part of it, but also
974 // starting at the beginning.
975 const nbase = @intCast(u32, object.in_symtab.?.len);
976 const sect_id = @intCast(u16, atom.sym_index - nbase);
977 const source_sect = object.getSourceSection(sect_id);
978 assert(!source_sect.isZerofill());
979 break :blk source_sect;
980 };
981
982 const relocs = object.getRelocs(source_sect);
983
984 if (atom.cached_relocs_start == -1) {
985 const indexes = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
986 const offset = source_sym.n_value - source_sect.addr;
987 break :blk filterRelocs(relocs, offset, offset + atom.size);
988 } else filterRelocs(relocs, 0, atom.size);
989 atom.cached_relocs_start = indexes.start;
990 atom.cached_relocs_len = indexes.len;
991 }
992
993 return relocs[@intCast(u32, atom.cached_relocs_start)..][0..atom.cached_relocs_len];
994}
995
996fn filterRelocs(
997 relocs: []align(1) const macho.relocation_info,
998 start_addr: u64,
999 end_addr: u64,
1000) struct { start: i32, len: u32 } {
1001 const Predicate = struct {
1002 addr: u64,
1003
1004 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
1005 return rel.r_address >= self.addr;
1006 }
1007 };
1008 const LPredicate = struct {
1009 addr: u64,
1010
1011 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
1012 return rel.r_address < self.addr;
1013 }
1014 };
1015
1016 const start = @import("zld.zig").bsearch(macho.relocation_info, relocs, Predicate{ .addr = end_addr });
1017 const len = @import("zld.zig").lsearch(macho.relocation_info, relocs[start..], LPredicate{ .addr = start_addr });
1018
1019 return .{ .start = @intCast(i32, start), .len = @intCast(u32, len) };
1020}
1021
1022pub fn calcPcRelativeDisplacementX86(source_addr: u64, target_addr: u64, correction: u3) error{Overflow}!i32 {
1023 const disp = @intCast(i64, target_addr) - @intCast(i64, source_addr + 4 + correction);
1024 return math.cast(i32, disp) orelse error.Overflow;
1025}
1026
1027pub fn calcPcRelativeDisplacementArm64(source_addr: u64, target_addr: u64) error{Overflow}!i28 {
1028 const disp = @intCast(i64, target_addr) - @intCast(i64, source_addr);
1029 return math.cast(i28, disp) orelse error.Overflow;
1030}
1031
1032pub fn calcNumberOfPages(source_addr: u64, target_addr: u64) i21 {
1033 const source_page = @intCast(i32, source_addr >> 12);
1034 const target_page = @intCast(i32, target_addr >> 12);
1035 const pages = @intCast(i21, target_page - source_page);
1036 return pages;
1037}
1038
1039const PageOffsetInstKind = enum {
1040 arithmetic,
1041 load_store_8,
1042 load_store_16,
1043 load_store_32,
1044 load_store_64,
1045 load_store_128,
1046};
1047
1048pub fn calcPageOffset(target_addr: u64, kind: PageOffsetInstKind) !u12 {
1049 const narrowed = @truncate(u12, target_addr);
1050 return switch (kind) {
1051 .arithmetic, .load_store_8 => narrowed,
1052 .load_store_16 => try math.divExact(u12, narrowed, 2),
1053 .load_store_32 => try math.divExact(u12, narrowed, 4),
1054 .load_store_64 => try math.divExact(u12, narrowed, 8),
1055 .load_store_128 => try math.divExact(u12, narrowed, 16),
1056 };
1057}
src/link/MachO/dead_strip.zig+260-214
......@@ -1,3 +1,5 @@
1//! An algorithm for dead stripping of unreferenced Atoms.
2
13const std = @import("std");
24const assert = std.debug.assert;
35const log = std.log.scoped(.dead_strip);
......@@ -6,93 +8,105 @@ const math = std.math;
68const mem = std.mem;
79
810const Allocator = mem.Allocator;
9const Atom = @import("Atom.zig");
10const MachO = @import("../MachO.zig");
11
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();
11const AtomIndex = @import("zld.zig").AtomIndex;
12const Atom = @import("ZldAtom.zig");
13const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
14const Zld = @import("zld.zig").Zld;
1715
18 var roots = std.AutoHashMap(*Atom, void).init(arena);
19 try collectRoots(&roots, macho_file);
16const N_DEAD = @import("zld.zig").N_DEAD;
2017
21 var alive = std.AutoHashMap(*Atom, void).init(arena);
22 try mark(roots, &alive, macho_file);
18const AtomTable = std.AutoHashMap(AtomIndex, void);
2319
24 try prune(arena, alive, macho_file);
25}
20pub fn gcAtoms(zld: *Zld, reverse_lookups: [][]u32) !void {
21 const gpa = zld.gpa;
2622
27fn removeAtomFromSection(atom: *Atom, match: u8, macho_file: *MachO) void {
28 var section = macho_file.sections.get(match);
23 var arena = std.heap.ArenaAllocator.init(gpa);
24 defer arena.deinit();
2925
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;
26 var roots = AtomTable.init(arena.allocator());
27 try roots.ensureUnusedCapacity(@intCast(u32, zld.globals.items.len));
3328
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 }
29 var alive = AtomTable.init(arena.allocator());
30 try alive.ensureTotalCapacity(@intCast(u32, zld.atoms.items.len));
4831
49 macho_file.sections.set(match, section);
32 try collectRoots(zld, &roots);
33 try mark(zld, roots, &alive, reverse_lookups);
34 try prune(zld, alive);
5035}
5136
52fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {
53 const output_mode = macho_file.base.options.output_mode;
37fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
38 log.debug("collecting roots", .{});
5439
55 switch (output_mode) {
40 switch (zld.options.output_mode) {
5641 .Exe => {
5742 // 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);
43 const global: SymbolWithLoc = zld.getEntryPoint();
44 const object = zld.objects.items[global.getFile().?];
45 const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error
46 _ = try roots.getOrPut(atom_index);
47
48 log.debug("root(ATOM({d}, %{d}, {d}))", .{
49 atom_index,
50 zld.getAtom(atom_index).sym_index,
51 zld.getAtom(atom_index).file,
52 });
6153 },
6254 else => |other| {
6355 assert(other == .Lib);
6456 // 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);
73 log.debug("adding root", .{});
74 macho_file.logAtom(atom);
57 for (zld.globals.items) |global| {
58 const sym = zld.getSymbol(global);
59 if (sym.undf()) continue;
60
61 const object = zld.objects.items[global.getFile().?];
62 const atom_index = object.getAtomIndexForSymbol(global.sym_index).?; // panic here means fatal error
63 _ = try roots.getOrPut(atom_index);
64
65 log.debug("root(ATOM({d}, %{d}, {d}))", .{
66 atom_index,
67 zld.getAtom(atom_index).sym_index,
68 zld.getAtom(atom_index).file,
69 });
7570 }
7671 },
7772 }
7873
7974 // 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);
75 for (zld.globals.items) |global| {
76 if (mem.eql(u8, "___gxx_personality_v0", zld.getSymbolName(global))) {
77 const object = zld.objects.items[global.getFile().?];
78 if (object.getAtomIndexForSymbol(global.sym_index)) |atom_index| {
79 _ = try roots.getOrPut(atom_index);
80
81 log.debug("root(ATOM({d}, %{d}, {d}))", .{
82 atom_index,
83 zld.getAtom(atom_index).sym_index,
84 zld.getAtom(atom_index).file,
85 });
86 }
87 break;
8588 }
8689 }
8790
88 for (macho_file.objects.items) |object| {
89 for (object.managed_atoms.items) |atom| {
90 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
91 if (source_sym.tentative()) continue;
92 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
91 for (zld.objects.items) |object| {
92 const has_subsections = object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
93
94 for (object.atoms.items) |atom_index| {
9395 const is_gc_root = blk: {
96 // Modelled after ld64 which treats each object file compiled without MH_SUBSECTIONS_VIA_SYMBOLS
97 // as a root.
98 if (!has_subsections) break :blk true;
99
100 const atom = zld.getAtom(atom_index);
101 const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
102 source_sym.n_sect - 1
103 else sect_id: {
104 const nbase = @intCast(u32, object.in_symtab.?.len);
105 const sect_id = @intCast(u16, atom.sym_index - nbase);
106 break :sect_id sect_id;
107 };
108 const source_sect = object.getSourceSection(sect_id);
94109 if (source_sect.isDontDeadStrip()) break :blk true;
95 if (mem.eql(u8, "__StaticInit", source_sect.sectName())) break :blk true;
96110 switch (source_sect.@"type"()) {
97111 macho.S_MOD_INIT_FUNC_POINTERS,
98112 macho.S_MOD_TERM_FUNC_POINTERS,
......@@ -100,197 +114,229 @@ fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void
100114 else => break :blk false,
101115 }
102116 };
117
103118 if (is_gc_root) {
104 try roots.putNoClobber(atom, {});
105 log.debug("adding root", .{});
106 macho_file.logAtom(atom);
119 try roots.putNoClobber(atom_index, {});
120
121 log.debug("root(ATOM({d}, %{d}, {d}))", .{
122 atom_index,
123 zld.getAtom(atom_index).sym_index,
124 zld.getAtom(atom_index).file,
125 });
107126 }
108127 }
109128 }
110129}
111130
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;
131fn markLive(
132 zld: *Zld,
133 atom_index: AtomIndex,
134 alive: *AtomTable,
135 reverse_lookups: [][]u32,
136) anyerror!void {
137 if (alive.contains(atom_index)) return;
138
139 const atom = zld.getAtom(atom_index);
140 const sym_loc = atom.getSymbolWithLoc();
141
142 log.debug("mark(ATOM({d}, %{d}, {d}))", .{ atom_index, sym_loc.sym_index, sym_loc.file });
143
144 alive.putAssumeCapacityNoClobber(atom_index, {});
145
146 const cpu_arch = zld.options.target.cpu.arch;
147
148 const sym = zld.getSymbol(atom.getSymbolWithLoc());
149 const header = zld.sections.items(.header)[sym.n_sect - 1];
150 if (header.isZerofill()) return;
151
152 const relocs = Atom.getAtomRelocs(zld, atom_index);
153 const reverse_lookup = reverse_lookups[atom.getFile().?];
154 for (relocs) |rel| {
155 const target = switch (cpu_arch) {
156 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
157 .ARM64_RELOC_ADDEND => continue,
158 else => Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup),
159 },
160 .x86_64 => Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup),
161 else => unreachable,
162 };
163 const target_sym = zld.getSymbol(target);
164
165 if (rel.r_extern == 0) {
166 // We are pessimistic and mark all atoms within the target section as live.
167 // TODO: this can be improved by marking only the relevant atoms.
168 const sect_id = target_sym.n_sect;
169 const object = zld.objects.items[target.getFile().?];
170 for (object.atoms.items) |other_atom_index| {
171 const other_atom = zld.getAtom(other_atom_index);
172 const other_sym = zld.getSymbol(other_atom.getSymbolWithLoc());
173 if (other_sym.n_sect == sect_id) {
174 try markLive(zld, other_atom_index, alive, reverse_lookups);
175 }
176 }
177 continue;
178 }
115179
116 log.debug("marking live", .{});
117 macho_file.logAtom(atom);
180 if (target_sym.undf()) continue;
181 if (target.getFile() == null) {
182 const target_sym_name = zld.getSymbolName(target);
183 if (mem.eql(u8, "__mh_execute_header", target_sym_name)) continue;
184 if (mem.eql(u8, "___dso_handle", target_sym_name)) continue;
118185
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}
186 unreachable; // referenced symbol not found
187 }
188
189 const object = zld.objects.items[target.getFile().?];
190 const target_atom_index = object.getAtomIndexForSymbol(target.sym_index).?;
191 log.debug(" following ATOM({d}, %{d}, {d})", .{
192 target_atom_index,
193 zld.getAtom(target_atom_index).sym_index,
194 zld.getAtom(target_atom_index).file,
195 });
124196
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;
197 try markLive(zld, target_atom_index, alive, reverse_lookups);
129198 }
130 return false;
131199}
132200
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;
201fn refersLive(zld: *Zld, atom_index: AtomIndex, alive: AtomTable, reverse_lookups: [][]u32) !bool {
202 const atom = zld.getAtom(atom_index);
203 const sym_loc = atom.getSymbolWithLoc();
204
205 log.debug("refersLive(ATOM({d}, %{d}, {d}))", .{ atom_index, sym_loc.sym_index, sym_loc.file });
206
207 const cpu_arch = zld.options.target.cpu.arch;
208
209 const sym = zld.getSymbol(sym_loc);
210 const header = zld.sections.items(.header)[sym.n_sect - 1];
211 assert(!header.isZerofill());
212
213 const relocs = Atom.getAtomRelocs(zld, atom_index);
214 const reverse_lookup = reverse_lookups[atom.getFile().?];
215 for (relocs) |rel| {
216 const target = switch (cpu_arch) {
217 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
218 .ARM64_RELOC_ADDEND => continue,
219 else => Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup),
220 },
221 .x86_64 => Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup),
222 else => unreachable,
223 };
224
225 const object = zld.objects.items[target.getFile().?];
226 const target_atom_index = object.getAtomIndexForSymbol(target.sym_index) orelse {
227 log.debug("atom for symbol '{s}' not found; skipping...", .{zld.getSymbolName(target)});
228 continue;
229 };
230 if (alive.contains(target_atom_index)) {
231 log.debug(" refers live ATOM({d}, %{d}, {d})", .{
232 target_atom_index,
233 zld.getAtom(target_atom_index).sym_index,
234 zld.getAtom(target_atom_index).file,
235 });
236 return true;
237 }
138238 }
239
139240 return false;
140241}
141242
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
243fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable, reverse_lookups: [][]u32) !void {
149244 var it = roots.keyIterator();
150245 while (it.next()) |root| {
151 try markLive(root.*, alive, macho_file);
246 try markLive(zld, root.*, alive, reverse_lookups);
152247 }
153248
154249 var loop: bool = true;
155250 while (loop) {
156251 loop = false;
157252
158 for (macho_file.objects.items) |object| {
159 for (object.managed_atoms.items) |atom| {
160 if (alive.contains(atom)) continue;
161 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
162 if (source_sym.tentative()) continue;
163 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;
253 for (zld.objects.items) |object| {
254 for (object.atoms.items) |atom_index| {
255 if (alive.contains(atom_index)) continue;
256
257 const atom = zld.getAtom(atom_index);
258 const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
259 source_sym.n_sect - 1
260 else blk: {
261 const nbase = @intCast(u32, object.in_symtab.?.len);
262 const sect_id = @intCast(u16, atom.sym_index - nbase);
263 break :blk sect_id;
264 };
265 const source_sect = object.getSourceSection(sect_id);
266
267 if (source_sect.isDontDeadStripIfReferencesLive()) {
268 if (try refersLive(zld, atom_index, alive.*, reverse_lookups)) {
269 try markLive(zld, atom_index, alive, reverse_lookups);
270 loop = true;
271 }
167272 }
168273 }
169274 }
170275 }
171276}
172277
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 }
278fn prune(zld: *Zld, alive: AtomTable) !void {
279 log.debug("pruning dead atoms", .{});
280 for (zld.objects.items) |*object| {
281 var i: usize = 0;
282 while (i < object.atoms.items.len) {
283 const atom_index = object.atoms.items[i];
284 if (alive.contains(atom_index)) {
285 i += 1;
286 continue;
287 }
213288
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;
289 const atom = zld.getAtom(atom_index);
290 const sym_loc = atom.getSymbolWithLoc();
291
292 log.debug("prune(ATOM({d}, %{d}, {d}))", .{
293 atom_index,
294 sym_loc.sym_index,
295 sym_loc.file,
296 });
297 log.debug(" {s} in {s}", .{ zld.getSymbolName(sym_loc), object.name });
298
299 const sym = zld.getSymbolPtr(sym_loc);
300 const sect_id = sym.n_sect - 1;
301 var section = zld.sections.get(sect_id);
302 section.header.size -= atom.size;
303
304 if (atom.prev_index) |prev_index| {
305 const prev = zld.getAtomPtr(prev_index);
306 prev.next_index = atom.next_index;
307 } else {
308 if (atom.next_index) |next_index| {
309 section.first_atom_index = next_index;
218310 }
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;
311 }
312 if (atom.next_index) |next_index| {
313 const next = zld.getAtomPtr(next_index);
314 next.prev_index = atom.prev_index;
315 } else {
316 if (atom.prev_index) |prev_index| {
317 section.last_atom_index = prev_index;
318 } else {
319 assert(section.header.size == 0);
320 section.first_atom_index = undefined;
321 section.last_atom_index = undefined;
224322 }
225
226 loop = true;
227323 }
228 }
229 }
230324
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;
325 zld.sections.set(sect_id, section);
326 _ = object.atoms.swapRemove(i);
234327
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 }
328 sym.n_desc = N_DEAD;
242329
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);
330 var inner_sym_it = Atom.getInnerSymbolsIterator(zld, atom_index);
331 while (inner_sym_it.next()) |inner| {
332 const inner_sym = zld.getSymbolPtr(inner);
333 inner_sym.n_desc = N_DEAD;
334 }
288335
289 if (atom.next) |next| {
290 atom = next;
291 } else break;
336 if (Atom.getSectionAlias(zld, atom_index)) |alias| {
337 const alias_sym = zld.getSymbolPtr(alias);
338 alias_sym.n_desc = N_DEAD;
339 }
292340 }
293
294 macho_file.sections.set(match, section);
295341 }
296342}
src/link/MachO/thunks.zig created+364
......@@ -0,0 +1,364 @@
1//! An algorithm for allocating output machine code section (aka `__TEXT,__text`),
2//! and insertion of range extending thunks. As such, this algorithm is only run
3//! for a target that requires range extenders such as arm64.
4//!
5//! The algorithm works pessimistically and assumes that any reference to an Atom in
6//! another output section is out of range.
7
8const std = @import("std");
9const assert = std.debug.assert;
10const log = std.log.scoped(.thunks);
11const macho = std.macho;
12const math = std.math;
13const mem = std.mem;
14
15const aarch64 = @import("../../arch/aarch64/bits.zig");
16
17const Allocator = mem.Allocator;
18const Atom = @import("ZldAtom.zig");
19const AtomIndex = @import("zld.zig").AtomIndex;
20const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
21const Zld = @import("zld.zig").Zld;
22
23pub const ThunkIndex = u32;
24
25/// Branch instruction has 26 bits immediate but 4 byte aligned.
26const jump_bits = @bitSizeOf(i28);
27
28const max_distance = (1 << (jump_bits - 1));
29
30/// A branch will need an extender if its target is larger than
31/// `2^(jump_bits - 1) - margin` where margin is some arbitrary number.
32/// mold uses 5MiB margin, while ld64 uses 4MiB margin. We will follow mold
33/// and assume margin to be 5MiB.
34const max_allowed_distance = max_distance - 0x500_000;
35
36pub const Thunk = struct {
37 start_index: AtomIndex,
38 len: u32,
39
40 lookup: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, AtomIndex) = .{},
41
42 pub fn deinit(self: *Thunk, gpa: Allocator) void {
43 self.lookup.deinit(gpa);
44 }
45
46 pub fn getStartAtomIndex(self: Thunk) AtomIndex {
47 assert(self.len != 0);
48 return self.start_index;
49 }
50
51 pub fn getEndAtomIndex(self: Thunk) AtomIndex {
52 assert(self.len != 0);
53 return self.start_index + self.len - 1;
54 }
55
56 pub fn getSize(self: Thunk) u64 {
57 return 12 * self.len;
58 }
59
60 pub fn getAlignment() u32 {
61 return @alignOf(u32);
62 }
63
64 pub fn getTrampolineForSymbol(self: Thunk, zld: *Zld, target: SymbolWithLoc) ?SymbolWithLoc {
65 const atom_index = self.lookup.get(target) orelse return null;
66 const atom = zld.getAtom(atom_index);
67 return atom.getSymbolWithLoc();
68 }
69};
70
71pub fn createThunks(zld: *Zld, sect_id: u8, reverse_lookups: [][]u32) !void {
72 const header = &zld.sections.items(.header)[sect_id];
73 if (header.size == 0) return;
74
75 const gpa = zld.gpa;
76 const first_atom_index = zld.sections.items(.first_atom_index)[sect_id];
77
78 header.size = 0;
79 header.@"align" = 0;
80
81 var atom_count: u32 = 0;
82
83 {
84 var atom_index = first_atom_index;
85 while (true) {
86 const atom = zld.getAtom(atom_index);
87 const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());
88 sym.n_value = 0;
89 atom_count += 1;
90
91 if (atom.next_index) |next_index| {
92 atom_index = next_index;
93 } else break;
94 }
95 }
96
97 var allocated = std.AutoHashMap(AtomIndex, void).init(gpa);
98 defer allocated.deinit();
99 try allocated.ensureTotalCapacity(atom_count);
100
101 var group_start = first_atom_index;
102 var group_end = first_atom_index;
103 var offset: u64 = 0;
104
105 while (true) {
106 const group_start_atom = zld.getAtom(group_start);
107 log.debug("GROUP START at {d}", .{group_start});
108
109 while (true) {
110 const atom = zld.getAtom(group_end);
111 offset = mem.alignForwardGeneric(u64, offset, try math.powi(u32, 2, atom.alignment));
112
113 const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());
114 sym.n_value = offset;
115 offset += atom.size;
116
117 zld.logAtom(group_end, log);
118
119 header.@"align" = @max(header.@"align", atom.alignment);
120
121 allocated.putAssumeCapacityNoClobber(group_end, {});
122
123 const group_start_sym = zld.getSymbol(group_start_atom.getSymbolWithLoc());
124 if (offset - group_start_sym.n_value >= max_allowed_distance) break;
125
126 if (atom.next_index) |next_index| {
127 group_end = next_index;
128 } else break;
129 }
130 log.debug("GROUP END at {d}", .{group_end});
131
132 // Insert thunk at group_end
133 const thunk_index = @intCast(u32, zld.thunks.items.len);
134 try zld.thunks.append(gpa, .{ .start_index = undefined, .len = 0 });
135
136 // Scan relocs in the group and create trampolines for any unreachable callsite.
137 var atom_index = group_start;
138 while (true) {
139 const atom = zld.getAtom(atom_index);
140 try scanRelocs(
141 zld,
142 atom_index,
143 reverse_lookups[atom.getFile().?],
144 allocated,
145 thunk_index,
146 group_end,
147 );
148
149 if (atom_index == group_end) break;
150
151 if (atom.next_index) |next_index| {
152 atom_index = next_index;
153 } else break;
154 }
155
156 offset = mem.alignForwardGeneric(u64, offset, Thunk.getAlignment());
157 allocateThunk(zld, thunk_index, offset, header);
158 offset += zld.thunks.items[thunk_index].getSize();
159
160 const thunk = zld.thunks.items[thunk_index];
161 if (thunk.len == 0) {
162 const group_end_atom = zld.getAtom(group_end);
163 if (group_end_atom.next_index) |next_index| {
164 group_start = next_index;
165 group_end = next_index;
166 } else break;
167 } else {
168 const thunk_end_atom_index = thunk.getEndAtomIndex();
169 const thunk_end_atom = zld.getAtom(thunk_end_atom_index);
170 if (thunk_end_atom.next_index) |next_index| {
171 group_start = next_index;
172 group_end = next_index;
173 } else break;
174 }
175 }
176
177 header.size = @intCast(u32, offset);
178}
179
180fn allocateThunk(
181 zld: *Zld,
182 thunk_index: ThunkIndex,
183 base_offset: u64,
184 header: *macho.section_64,
185) void {
186 const thunk = zld.thunks.items[thunk_index];
187 if (thunk.len == 0) return;
188
189 const first_atom_index = thunk.getStartAtomIndex();
190 const end_atom_index = thunk.getEndAtomIndex();
191
192 var atom_index = first_atom_index;
193 var offset = base_offset;
194 while (true) {
195 const atom = zld.getAtom(atom_index);
196 offset = mem.alignForwardGeneric(u64, offset, Thunk.getAlignment());
197
198 const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());
199 sym.n_value = offset;
200 offset += atom.size;
201
202 zld.logAtom(atom_index, log);
203
204 header.@"align" = @max(header.@"align", atom.alignment);
205
206 if (end_atom_index == atom_index) break;
207
208 if (atom.next_index) |next_index| {
209 atom_index = next_index;
210 } else break;
211 }
212}
213
214fn scanRelocs(
215 zld: *Zld,
216 atom_index: AtomIndex,
217 reverse_lookup: []u32,
218 allocated: std.AutoHashMap(AtomIndex, void),
219 thunk_index: ThunkIndex,
220 group_end: AtomIndex,
221) !void {
222 const atom = zld.getAtom(atom_index);
223 const object = zld.objects.items[atom.getFile().?];
224
225 const base_offset = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
226 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
227 break :blk @intCast(i32, source_sym.n_value - source_sect.addr);
228 } else 0;
229
230 const relocs = Atom.getAtomRelocs(zld, atom_index);
231 for (relocs) |rel| {
232 if (!relocNeedsThunk(rel)) continue;
233
234 const target = Atom.parseRelocTarget(zld, atom_index, rel, reverse_lookup);
235 if (isReachable(zld, atom_index, rel, base_offset, target, allocated)) continue;
236
237 log.debug("{x}: source = {s}@{x}, target = {s}@{x} unreachable", .{
238 rel.r_address - base_offset,
239 zld.getSymbolName(atom.getSymbolWithLoc()),
240 zld.getSymbol(atom.getSymbolWithLoc()).n_value,
241 zld.getSymbolName(target),
242 zld.getSymbol(target).n_value,
243 });
244
245 const gpa = zld.gpa;
246 const target_sym = zld.getSymbol(target);
247
248 const actual_target: SymbolWithLoc = if (target_sym.undf()) blk: {
249 const stub_atom_index = zld.getStubsAtomIndexForSymbol(target).?;
250 break :blk .{ .sym_index = zld.getAtom(stub_atom_index).sym_index };
251 } else target;
252
253 const thunk = &zld.thunks.items[thunk_index];
254 const gop = try thunk.lookup.getOrPut(gpa, actual_target);
255 if (!gop.found_existing) {
256 const thunk_atom_index = try createThunkAtom(zld);
257 gop.value_ptr.* = thunk_atom_index;
258
259 const thunk_atom = zld.getAtomPtr(thunk_atom_index);
260 const end_atom_index = if (thunk.len == 0) group_end else thunk.getEndAtomIndex();
261 const end_atom = zld.getAtomPtr(end_atom_index);
262
263 if (end_atom.next_index) |first_after_index| {
264 const first_after_atom = zld.getAtomPtr(first_after_index);
265 first_after_atom.prev_index = thunk_atom_index;
266 thunk_atom.next_index = first_after_index;
267 }
268
269 end_atom.next_index = thunk_atom_index;
270 thunk_atom.prev_index = end_atom_index;
271
272 if (thunk.len == 0) {
273 thunk.start_index = thunk_atom_index;
274 }
275
276 thunk.len += 1;
277 }
278
279 try zld.thunk_table.put(gpa, atom_index, thunk_index);
280 }
281}
282
283inline fn relocNeedsThunk(rel: macho.relocation_info) bool {
284 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
285 return rel_type == .ARM64_RELOC_BRANCH26;
286}
287
288fn isReachable(
289 zld: *Zld,
290 atom_index: AtomIndex,
291 rel: macho.relocation_info,
292 base_offset: i32,
293 target: SymbolWithLoc,
294 allocated: std.AutoHashMap(AtomIndex, void),
295) bool {
296 if (zld.getStubsAtomIndexForSymbol(target)) |_| return false;
297
298 const source_atom = zld.getAtom(atom_index);
299 const source_sym = zld.getSymbol(source_atom.getSymbolWithLoc());
300
301 const target_object = zld.objects.items[target.getFile().?];
302 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
303 const target_atom = zld.getAtom(target_atom_index);
304 const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());
305
306 if (source_sym.n_sect != target_sym.n_sect) return false;
307
308 if (!allocated.contains(target_atom_index)) return false;
309
310 const source_addr = source_sym.n_value + @intCast(u32, rel.r_address - base_offset);
311 const target_addr = Atom.getRelocTargetAddress(zld, rel, target, false) catch unreachable;
312 _ = Atom.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch
313 return false;
314
315 return true;
316}
317
318fn createThunkAtom(zld: *Zld) !AtomIndex {
319 const sym_index = try zld.allocateSymbol();
320 const atom_index = try zld.createEmptyAtom(sym_index, @sizeOf(u32) * 3, 2);
321 const sym = zld.getSymbolPtr(.{ .sym_index = sym_index });
322 sym.n_type = macho.N_SECT;
323
324 const sect_id = zld.getSectionByName("__TEXT", "__text") orelse unreachable;
325 sym.n_sect = sect_id + 1;
326
327 return atom_index;
328}
329
330fn getThunkIndex(zld: *Zld, atom_index: AtomIndex) ?ThunkIndex {
331 const atom = zld.getAtom(atom_index);
332 const sym = zld.getSymbol(atom.getSymbolWithLoc());
333 for (zld.thunks.items) |thunk, i| {
334 if (thunk.len == 0) continue;
335
336 const thunk_atom_index = thunk.getStartAtomIndex();
337 const thunk_atom = zld.getAtom(thunk_atom_index);
338 const thunk_sym = zld.getSymbol(thunk_atom.getSymbolWithLoc());
339 const start_addr = thunk_sym.n_value;
340 const end_addr = start_addr + thunk.getSize();
341
342 if (start_addr <= sym.n_value and sym.n_value < end_addr) {
343 return @intCast(u32, i);
344 }
345 }
346 return null;
347}
348
349pub fn writeThunkCode(zld: *Zld, atom_index: AtomIndex, writer: anytype) !void {
350 const atom = zld.getAtom(atom_index);
351 const sym = zld.getSymbol(atom.getSymbolWithLoc());
352 const source_addr = sym.n_value;
353 const thunk = zld.thunks.items[getThunkIndex(zld, atom_index).?];
354 const target_addr = for (thunk.lookup.keys()) |target| {
355 const target_atom_index = thunk.lookup.get(target).?;
356 if (atom_index == target_atom_index) break zld.getSymbol(target).n_value;
357 } else unreachable;
358
359 const pages = Atom.calcNumberOfPages(source_addr, target_addr);
360 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
361 const off = try Atom.calcPageOffset(target_addr, .arithmetic);
362 try writer.writeIntLittle(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32());
363 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
364}
src/link/MachO/zld.zig+4079-1601
......@@ -10,1944 +10,4422 @@ 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 // TODO unwind info
448 if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) {
449 log.debug("TODO eh frame section: type 0x{x}, name '{s},{s}'", .{
450 sect.flags, segname, sectname,
451 });
452 break :blk null;
453 }
454 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
455 segname,
456 sectname,
457 .{},
458 );
459 },
460 macho.S_REGULAR => {
461 if (mem.eql(u8, segname, "__TEXT")) {
462 if (mem.eql(u8, sectname, "__rodata") or
463 mem.eql(u8, sectname, "__typelink") or
464 mem.eql(u8, sectname, "__itablink") or
465 mem.eql(u8, sectname, "__gosymtab") or
466 mem.eql(u8, sectname, "__gopclntab"))
467 {
468 break :blk self.getSectionByName("__DATA_CONST", "__const") orelse try self.initSection(
469 "__DATA_CONST",
470 "__const",
471 .{},
472 );
304473 }
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;
474 }
475 if (mem.eql(u8, segname, "__DATA")) {
476 if (mem.eql(u8, sectname, "__const") or
477 mem.eql(u8, sectname, "__cfstring") or
478 mem.eql(u8, sectname, "__objc_classlist") or
479 mem.eql(u8, sectname, "__objc_imageinfo"))
480 {
481 break :blk self.getSectionByName("__DATA_CONST", sectname) orelse
482 try self.initSection(
483 "__DATA_CONST",
484 sectname,
485 .{},
486 );
487 } else if (mem.eql(u8, sectname, "__data")) {
488 break :blk self.getSectionByName("__DATA", "__data") orelse
489 try self.initSection(
490 "__DATA",
491 "__data",
492 .{},
493 );
311494 }
312495 }
496 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
497 segname,
498 sectname,
499 .{},
500 );
313501 },
502 else => break :blk null,
314503 }
315 }
504 };
505 return res;
506 }
316507
317 if (lib_not_found) {
318 log.warn("Library search paths:", .{});
319 for (lib_dirs.items) |dir| {
320 log.warn(" {s}", .{dir});
321 }
508 pub fn addAtomToSection(self: *Zld, atom_index: AtomIndex) void {
509 const atom = self.getAtomPtr(atom_index);
510 const sym = self.getSymbol(atom.getSymbolWithLoc());
511 var section = self.sections.get(sym.n_sect - 1);
512 if (section.header.size > 0) {
513 const last_atom = self.getAtomPtr(section.last_atom_index);
514 last_atom.next_index = atom_index;
515 atom.prev_index = section.last_atom_index;
516 } else {
517 section.first_atom_index = atom_index;
322518 }
519 section.last_atom_index = atom_index;
520 section.header.size += atom.size;
521 self.sections.set(sym.n_sect - 1, section);
522 }
323523
324 try macho_file.resolveLibSystem(arena, comp, lib_dirs.items, &libs);
524 pub fn createEmptyAtom(self: *Zld, sym_index: u32, size: u64, alignment: u32) !AtomIndex {
525 const gpa = self.gpa;
526 const index = @intCast(AtomIndex, self.atoms.items.len);
527 const atom = try self.atoms.addOne(gpa);
528 atom.* = Atom.empty;
529 atom.sym_index = sym_index;
530 atom.size = size;
531 atom.alignment = alignment;
325532
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 }
533 log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, index });
335534
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 }
535 return index;
536 }
537
538 pub fn createGotAtom(self: *Zld) !AtomIndex {
539 const sym_index = try self.allocateSymbol();
540 const atom_index = try self.createEmptyAtom(sym_index, @sizeOf(u64), 3);
541 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
542 sym.n_type = macho.N_SECT;
543
544 const sect_id = self.getSectionByName("__DATA_CONST", "__got") orelse
545 try self.initSection("__DATA_CONST", "__got", .{
546 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
547 });
548 sym.n_sect = sect_id + 1;
549
550 self.addAtomToSection(atom_index);
551
552 return atom_index;
553 }
554
555 fn writeGotPointer(self: *Zld, got_index: u32, writer: anytype) !void {
556 const target_addr = blk: {
557 const entry = self.got_entries.items[got_index];
558 const sym = entry.getTargetSymbol(self);
559 break :blk sym.n_value;
560 };
561 try writer.writeIntLittle(u64, target_addr);
562 }
563
564 pub fn createTlvPtrAtom(self: *Zld) !AtomIndex {
565 const sym_index = try self.allocateSymbol();
566 const atom_index = try self.createEmptyAtom(sym_index, @sizeOf(u64), 3);
567 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
568 sym.n_type = macho.N_SECT;
569
570 const sect_id = (try self.getOutputSection(.{
571 .segname = makeStaticString("__DATA"),
572 .sectname = makeStaticString("__thread_ptrs"),
573 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
574 })).?;
575 sym.n_sect = sect_id + 1;
576
577 self.addAtomToSection(atom_index);
578
579 return atom_index;
580 }
581
582 fn createDyldStubBinderGotAtom(self: *Zld) !void {
583 const sym_index = self.dyld_stub_binder_index orelse return;
584 const gpa = self.gpa;
585 const target = SymbolWithLoc{ .sym_index = sym_index };
586 const atom_index = try self.createGotAtom();
587 const got_index = @intCast(u32, self.got_entries.items.len);
588 try self.got_entries.append(gpa, .{
589 .target = target,
590 .atom_index = atom_index,
591 });
592 try self.got_table.putNoClobber(gpa, target, got_index);
593 }
594
595 fn createDyldPrivateAtom(self: *Zld) !void {
596 if (self.dyld_stub_binder_index == null) return;
597
598 const sym_index = try self.allocateSymbol();
599 const atom_index = try self.createEmptyAtom(sym_index, @sizeOf(u64), 3);
600 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
601 sym.n_type = macho.N_SECT;
602
603 const sect_id = self.getSectionByName("__DATA", "__data") orelse try self.initSection("__DATA", "__data", .{});
604 sym.n_sect = sect_id + 1;
605
606 self.dyld_private_sym_index = sym_index;
607
608 self.addAtomToSection(atom_index);
609 }
610
611 fn createStubHelperPreambleAtom(self: *Zld) !void {
612 if (self.dyld_stub_binder_index == null) return;
613
614 const cpu_arch = self.options.target.cpu.arch;
615 const size: u64 = switch (cpu_arch) {
616 .x86_64 => 15,
617 .aarch64 => 6 * @sizeOf(u32),
618 else => unreachable,
619 };
620 const alignment: u32 = switch (cpu_arch) {
621 .x86_64 => 0,
622 .aarch64 => 2,
623 else => unreachable,
624 };
625 const sym_index = try self.allocateSymbol();
626 const atom_index = try self.createEmptyAtom(sym_index, size, alignment);
627 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
628 sym.n_type = macho.N_SECT;
629
630 const sect_id = self.getSectionByName("__TEXT", "__stub_helper") orelse
631 try self.initSection("__TEXT", "__stub_helper", .{
632 .flags = macho.S_REGULAR |
633 macho.S_ATTR_PURE_INSTRUCTIONS |
634 macho.S_ATTR_SOME_INSTRUCTIONS,
635 });
636 sym.n_sect = sect_id + 1;
637
638 self.stub_helper_preamble_sym_index = sym_index;
639
640 self.addAtomToSection(atom_index);
641 }
642
643 fn writeStubHelperPreambleCode(self: *Zld, writer: anytype) !void {
644 const cpu_arch = self.options.target.cpu.arch;
645 const source_addr = blk: {
646 const sym = self.getSymbol(.{ .sym_index = self.stub_helper_preamble_sym_index.? });
647 break :blk sym.n_value;
648 };
649 const dyld_private_addr = blk: {
650 const sym = self.getSymbol(.{ .sym_index = self.dyld_private_sym_index.? });
651 break :blk sym.n_value;
652 };
653 const dyld_stub_binder_got_addr = blk: {
654 const index = self.got_table.get(.{ .sym_index = self.dyld_stub_binder_index.? }).?;
655 const entry = self.got_entries.items[index];
656 break :blk entry.getAtomSymbol(self).n_value;
657 };
658 switch (cpu_arch) {
659 .x86_64 => {
660 try writer.writeAll(&.{ 0x4c, 0x8d, 0x1d });
661 {
662 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 3, dyld_private_addr, 0);
663 try writer.writeIntLittle(i32, disp);
347664 }
348 } else {
349 log.warn("framework not found for '-framework {s}'", .{f_name});
350 framework_not_found = true;
351 }
665 try writer.writeAll(&.{ 0x41, 0x53, 0xff, 0x25 });
666 {
667 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 11, dyld_stub_binder_got_addr, 0);
668 try writer.writeIntLittle(i32, disp);
669 }
670 },
671 .aarch64 => {
672 {
673 const pages = Atom.calcNumberOfPages(source_addr, dyld_private_addr);
674 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x17, pages).toU32());
675 }
676 {
677 const off = try Atom.calcPageOffset(dyld_private_addr, .arithmetic);
678 try writer.writeIntLittle(u32, aarch64.Instruction.add(.x17, .x17, off, false).toU32());
679 }
680 try writer.writeIntLittle(u32, aarch64.Instruction.stp(
681 .x16,
682 .x17,
683 aarch64.Register.sp,
684 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
685 ).toU32());
686 {
687 const pages = Atom.calcNumberOfPages(source_addr + 12, dyld_stub_binder_got_addr);
688 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
689 }
690 {
691 const off = try Atom.calcPageOffset(dyld_stub_binder_got_addr, .load_store_64);
692 try writer.writeIntLittle(u32, aarch64.Instruction.ldr(
693 .x16,
694 .x16,
695 aarch64.Instruction.LoadStoreOffset.imm(off),
696 ).toU32());
697 }
698 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
699 },
700 else => unreachable,
352701 }
702 }
353703
354 if (framework_not_found) {
355 log.warn("Framework search paths:", .{});
356 for (framework_dirs.items) |dir| {
357 log.warn(" {s}", .{dir});
358 }
704 pub fn createStubHelperAtom(self: *Zld) !AtomIndex {
705 const cpu_arch = self.options.target.cpu.arch;
706 const stub_size: u4 = switch (cpu_arch) {
707 .x86_64 => 10,
708 .aarch64 => 3 * @sizeOf(u32),
709 else => unreachable,
710 };
711 const alignment: u2 = switch (cpu_arch) {
712 .x86_64 => 0,
713 .aarch64 => 2,
714 else => unreachable,
715 };
716
717 const sym_index = try self.allocateSymbol();
718 const atom_index = try self.createEmptyAtom(sym_index, stub_size, alignment);
719 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
720 sym.n_sect = macho.N_SECT;
721
722 const sect_id = self.getSectionByName("__TEXT", "__stub_helper").?;
723 sym.n_sect = sect_id + 1;
724
725 self.addAtomToSection(atom_index);
726
727 return atom_index;
728 }
729
730 fn writeStubHelperCode(self: *Zld, atom_index: AtomIndex, writer: anytype) !void {
731 const cpu_arch = self.options.target.cpu.arch;
732 const source_addr = blk: {
733 const atom = self.getAtom(atom_index);
734 const sym = self.getSymbol(atom.getSymbolWithLoc());
735 break :blk sym.n_value;
736 };
737 const target_addr = blk: {
738 const sym = self.getSymbol(.{ .sym_index = self.stub_helper_preamble_sym_index.? });
739 break :blk sym.n_value;
740 };
741 switch (cpu_arch) {
742 .x86_64 => {
743 try writer.writeAll(&.{ 0x68, 0x0, 0x0, 0x0, 0x0, 0xe9 });
744 {
745 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 6, target_addr, 0);
746 try writer.writeIntLittle(i32, disp);
747 }
748 },
749 .aarch64 => {
750 const stub_size: u4 = 3 * @sizeOf(u32);
751 const literal = blk: {
752 const div_res = try math.divExact(u64, stub_size - @sizeOf(u32), 4);
753 break :blk math.cast(u18, div_res) orelse return error.Overflow;
754 };
755 try writer.writeIntLittle(u32, aarch64.Instruction.ldrLiteral(
756 .w16,
757 literal,
758 ).toU32());
759 {
760 const disp = try Atom.calcPcRelativeDisplacementArm64(source_addr + 4, target_addr);
761 try writer.writeIntLittle(u32, aarch64.Instruction.b(disp).toU32());
762 }
763 try writer.writeAll(&.{ 0x0, 0x0, 0x0, 0x0 });
764 },
765 else => unreachable,
359766 }
767 }
360768
361 if (macho_file.base.options.verbose_link) {
362 var argv = std.ArrayList([]const u8).init(arena);
769 pub fn createLazyPointerAtom(self: *Zld) !AtomIndex {
770 const sym_index = try self.allocateSymbol();
771 const atom_index = try self.createEmptyAtom(sym_index, @sizeOf(u64), 3);
772 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
773 sym.n_type = macho.N_SECT;
363774
364 try argv.append("zig");
365 try argv.append("ld");
775 const sect_id = self.getSectionByName("__DATA", "__la_symbol_ptr") orelse
776 try self.initSection("__DATA", "__la_symbol_ptr", .{
777 .flags = macho.S_LAZY_SYMBOL_POINTERS,
778 });
779 sym.n_sect = sect_id + 1;
366780
367 if (is_exe_or_dyn_lib) {
368 try argv.append("-dynamic");
369 }
781 self.addAtomToSection(atom_index);
370782
371 if (is_dyn_lib) {
372 try argv.append("-dylib");
783 return atom_index;
784 }
373785
374 if (macho_file.base.options.install_name) |install_name| {
375 try argv.append("-install_name");
376 try argv.append(install_name);
786 fn writeLazyPointer(self: *Zld, stub_helper_index: u32, writer: anytype) !void {
787 const target_addr = blk: {
788 const sect_id = self.getSectionByName("__TEXT", "__stub_helper").?;
789 var atom_index = self.sections.items(.first_atom_index)[sect_id];
790 var count: u32 = 0;
791 while (count < stub_helper_index + 1) : (count += 1) {
792 const atom = self.getAtom(atom_index);
793 if (atom.next_index) |next_index| {
794 atom_index = next_index;
377795 }
378796 }
797 const atom = self.getAtom(atom_index);
798 const sym = self.getSymbol(atom.getSymbolWithLoc());
799 break :blk sym.n_value;
800 };
801 try writer.writeIntLittle(u64, target_addr);
802 }
379803
380 if (macho_file.base.options.sysroot) |syslibroot| {
381 try argv.append("-syslibroot");
382 try argv.append(syslibroot);
383 }
804 pub fn createStubAtom(self: *Zld) !AtomIndex {
805 const cpu_arch = self.options.target.cpu.arch;
806 const alignment: u2 = switch (cpu_arch) {
807 .x86_64 => 0,
808 .aarch64 => 2,
809 else => unreachable, // unhandled architecture type
810 };
811 const stub_size: u4 = switch (cpu_arch) {
812 .x86_64 => 6,
813 .aarch64 => 3 * @sizeOf(u32),
814 else => unreachable, // unhandled architecture type
815 };
816 const sym_index = try self.allocateSymbol();
817 const atom_index = try self.createEmptyAtom(sym_index, stub_size, alignment);
818 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
819 sym.n_type = macho.N_SECT;
820
821 const sect_id = self.getSectionByName("__TEXT", "__stubs") orelse
822 try self.initSection("__TEXT", "__stubs", .{
823 .flags = macho.S_SYMBOL_STUBS |
824 macho.S_ATTR_PURE_INSTRUCTIONS |
825 macho.S_ATTR_SOME_INSTRUCTIONS,
826 .reserved2 = stub_size,
827 });
828 sym.n_sect = sect_id + 1;
384829
385 for (macho_file.base.options.rpath_list) |rpath| {
386 try argv.append("-rpath");
387 try argv.append(rpath);
388 }
830 self.addAtomToSection(atom_index);
389831
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}));
832 return atom_index;
833 }
834
835 fn writeStubCode(self: *Zld, atom_index: AtomIndex, stub_index: u32, writer: anytype) !void {
836 const cpu_arch = self.options.target.cpu.arch;
837 const source_addr = blk: {
838 const atom = self.getAtom(atom_index);
839 const sym = self.getSymbol(atom.getSymbolWithLoc());
840 break :blk sym.n_value;
841 };
842 const target_addr = blk: {
843 // TODO: cache this at stub atom creation; they always go in pairs anyhow
844 const la_sect_id = self.getSectionByName("__DATA", "__la_symbol_ptr").?;
845 var la_atom_index = self.sections.items(.first_atom_index)[la_sect_id];
846 var count: u32 = 0;
847 while (count < stub_index) : (count += 1) {
848 const la_atom = self.getAtom(la_atom_index);
849 la_atom_index = la_atom.next_index.?;
393850 }
851 const atom = self.getAtom(la_atom_index);
852 const sym = self.getSymbol(atom.getSymbolWithLoc());
853 break :blk sym.n_value;
854 };
855 switch (cpu_arch) {
856 .x86_64 => {
857 try writer.writeAll(&.{ 0xff, 0x25 });
858 {
859 const disp = try Atom.calcPcRelativeDisplacementX86(source_addr + 2, target_addr, 0);
860 try writer.writeIntLittle(i32, disp);
861 }
862 },
863 .aarch64 => {
864 {
865 const pages = Atom.calcNumberOfPages(source_addr, target_addr);
866 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
867 }
868 {
869 const off = try Atom.calcPageOffset(target_addr, .load_store_64);
870 try writer.writeIntLittle(u32, aarch64.Instruction.ldr(
871 .x16,
872 .x16,
873 aarch64.Instruction.LoadStoreOffset.imm(off),
874 ).toU32());
875 }
876 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
877 },
878 else => unreachable,
879 }
880 }
394881
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"),
882 fn createTentativeDefAtoms(self: *Zld) !void {
883 const gpa = self.gpa;
884
885 for (self.globals.items) |global| {
886 const sym = self.getSymbolPtr(global);
887 if (!sym.tentative()) continue;
888 if (sym.n_desc == N_DEAD) continue;
889
890 log.debug("creating tentative definition for ATOM(%{d}, '{s}') in object({?})", .{
891 global.sym_index, self.getSymbolName(global), global.file,
892 });
893
894 // Convert any tentative definition into a regular symbol and allocate
895 // text blocks for each tentative definition.
896 const size = sym.n_value;
897 const alignment = (sym.n_desc >> 8) & 0x0f;
898 const n_sect = (try self.getOutputSection(.{
899 .segname = makeStaticString("__DATA"),
900 .sectname = makeStaticString("__bss"),
901 .flags = macho.S_ZEROFILL,
902 })).? + 1;
903
904 sym.* = .{
905 .n_strx = sym.n_strx,
906 .n_type = macho.N_SECT | macho.N_EXT,
907 .n_sect = n_sect,
908 .n_desc = 0,
909 .n_value = 0,
398910 };
399911
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 }
912 const atom_index = try self.createEmptyAtom(global.sym_index, size, alignment);
913 const atom = self.getAtomPtr(atom_index);
914 atom.file = global.file;
404915
405 if (macho_file.base.options.headerpad_max_install_names) {
406 try argv.append("-headerpad_max_install_names");
407 }
916 self.addAtomToSection(atom_index);
408917
409 if (gc_sections) {
410 try argv.append("-dead_strip");
411 }
918 assert(global.getFile() != null);
919 const object = &self.objects.items[global.getFile().?];
920 try object.atoms.append(gpa, atom_index);
921 object.atom_by_index_table[global.sym_index] = atom_index;
922 }
923 }
412924
413 if (macho_file.base.options.dead_strip_dylibs) {
414 try argv.append("-dead_strip_dylibs");
415 }
925 fn resolveSymbolsInObject(self: *Zld, object_id: u16, resolver: *SymbolResolver) !void {
926 const object = &self.objects.items[object_id];
927 const in_symtab = object.in_symtab orelse return;
416928
417 if (macho_file.base.options.entry) |entry| {
418 try argv.append("-e");
419 try argv.append(entry);
420 }
929 log.debug("resolving symbols in '{s}'", .{object.name});
421930
422 for (macho_file.base.options.objects) |obj| {
423 try argv.append(obj.path);
424 }
931 var sym_index: u32 = 0;
932 while (sym_index < in_symtab.len) : (sym_index += 1) {
933 const sym = &object.symtab[sym_index];
934 const sym_name = object.getSymbolName(sym_index);
425935
426 for (comp.c_object_table.keys()) |key| {
427 try argv.append(key.status.success.object_path);
936 if (sym.stab()) {
937 log.err("unhandled symbol type: stab", .{});
938 log.err(" symbol '{s}'", .{sym_name});
939 log.err(" first definition in '{s}'", .{object.name});
940 return error.UnhandledSymbolType;
428941 }
429942
430 if (module_obj_path) |p| {
431 try argv.append(p);
943 if (sym.indr()) {
944 log.err("unhandled symbol type: indirect", .{});
945 log.err(" symbol '{s}'", .{sym_name});
946 log.err(" first definition in '{s}'", .{object.name});
947 return error.UnhandledSymbolType;
432948 }
433949
434 if (comp.compiler_rt_lib) |lib| {
435 try argv.append(lib.full_object_path);
950 if (sym.abs()) {
951 log.err("unhandled symbol type: absolute", .{});
952 log.err(" symbol '{s}'", .{sym_name});
953 log.err(" first definition in '{s}'", .{object.name});
954 return error.UnhandledSymbolType;
436955 }
437956
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);
957 if (sym.sect() and !sym.ext()) {
958 log.debug("symbol '{s}' local to object {s}; skipping...", .{
959 sym_name,
960 object.name,
961 });
962 continue;
441963 }
442964
443 try argv.append("-o");
444 try argv.append(full_out_path);
445
446 try argv.append("-lSystem");
447 try argv.append("-lc");
965 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id };
448966
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);
967 const global_index = resolver.table.get(sym_name) orelse {
968 const gpa = self.gpa;
969 const name = try resolver.arena.dupe(u8, sym_name);
970 const global_index = @intCast(u32, self.globals.items.len);
971 try self.globals.append(gpa, sym_loc);
972 try resolver.table.putNoClobber(name, global_index);
973 if (sym.undf() and !sym.tentative()) {
974 try resolver.unresolved.putNoClobber(global_index, {});
975 }
976 continue;
977 };
978 const global = &self.globals.items[global_index];
979 const global_sym = self.getSymbol(global.*);
980
981 // Cases to consider: sym vs global_sym
982 // 1. strong(sym) and strong(global_sym) => error
983 // 2. strong(sym) and weak(global_sym) => sym
984 // 3. strong(sym) and tentative(global_sym) => sym
985 // 4. strong(sym) and undf(global_sym) => sym
986 // 5. weak(sym) and strong(global_sym) => global_sym
987 // 6. weak(sym) and tentative(global_sym) => sym
988 // 7. weak(sym) and undf(global_sym) => sym
989 // 8. tentative(sym) and strong(global_sym) => global_sym
990 // 9. tentative(sym) and weak(global_sym) => global_sym
991 // 10. tentative(sym) and tentative(global_sym) => pick larger
992 // 11. tentative(sym) and undf(global_sym) => sym
993 // 12. undf(sym) and * => global_sym
994 //
995 // Reduces to:
996 // 1. strong(sym) and strong(global_sym) => error
997 // 2. * and strong(global_sym) => global_sym
998 // 3. weak(sym) and weak(global_sym) => global_sym
999 // 4. tentative(sym) and tentative(global_sym) => pick larger
1000 // 5. undf(sym) and * => global_sym
1001 // 6. else => sym
1002
1003 const sym_is_strong = sym.sect() and !(sym.weakDef() or sym.pext());
1004 const global_is_strong = global_sym.sect() and !(global_sym.weakDef() or global_sym.pext());
1005 const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext());
1006 const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext());
1007
1008 if (sym_is_strong and global_is_strong) {
1009 log.err("symbol '{s}' defined multiple times", .{sym_name});
1010 if (global.getFile()) |file| {
1011 log.err(" first definition in '{s}'", .{self.objects.items[file].name});
1012 }
1013 log.err(" next definition in '{s}'", .{self.objects.items[object_id].name});
1014 return error.MultipleSymbolDefinitions;
4581015 }
4591016
460 for (macho_file.base.options.lib_dirs) |lib_dir| {
461 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
462 }
1017 const update_global = blk: {
1018 if (global_is_strong) break :blk false;
1019 if (sym_is_weak and global_is_weak) break :blk false;
1020 if (sym.tentative() and global_sym.tentative()) {
1021 if (global_sym.n_value >= sym.n_value) break :blk false;
1022 }
1023 if (sym.undf() and !sym.tentative()) break :blk false;
1024 break :blk true;
1025 };
4631026
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);
1027 if (update_global) {
1028 const global_object = &self.objects.items[global.getFile().?];
1029 global_object.globals_lookup[global.sym_index] = global_index;
1030 _ = resolver.unresolved.swapRemove(resolver.table.get(sym_name).?);
1031 global.* = sym_loc;
1032 } else {
1033 object.globals_lookup[sym_index] = global_index;
4731034 }
1035 }
1036 }
4741037
475 for (macho_file.base.options.framework_dirs) |framework_dir| {
476 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
1038 fn resolveSymbolsInArchives(self: *Zld, resolver: *SymbolResolver) !void {
1039 if (self.archives.items.len == 0) return;
1040
1041 const gpa = self.gpa;
1042 const cpu_arch = self.options.target.cpu.arch;
1043 var next_sym: usize = 0;
1044 loop: while (next_sym < resolver.unresolved.count()) {
1045 const global = self.globals.items[resolver.unresolved.keys()[next_sym]];
1046 const sym_name = self.getSymbolName(global);
1047
1048 for (self.archives.items) |archive| {
1049 // Check if the entry exists in a static archive.
1050 const offsets = archive.toc.get(sym_name) orelse {
1051 // No hit.
1052 continue;
1053 };
1054 assert(offsets.items.len > 0);
1055
1056 const object_id = @intCast(u16, self.objects.items.len);
1057 const object = try archive.parseObject(gpa, cpu_arch, offsets.items[0]);
1058 try self.objects.append(gpa, object);
1059 try self.resolveSymbolsInObject(object_id, resolver);
1060
1061 continue :loop;
4771062 }
4781063
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 }
1064 next_sym += 1;
1065 }
1066 }
4831067
484 for (must_link_archives.keys()) |lib| {
485 try argv.append(try std.fmt.allocPrint(arena, "-force_load {s}", .{lib}));
486 }
1068 fn resolveSymbolsInDylibs(self: *Zld, resolver: *SymbolResolver) !void {
1069 if (self.dylibs.items.len == 0) return;
4871070
488 Compilation.dump_argv(argv.items);
489 }
1071 var next_sym: usize = 0;
1072 loop: while (next_sym < resolver.unresolved.count()) {
1073 const global_index = resolver.unresolved.keys()[next_sym];
1074 const global = self.globals.items[global_index];
1075 const sym = self.getSymbolPtr(global);
1076 const sym_name = self.getSymbolName(global);
4901077
491 var dependent_libs = std.fifo.LinearFifo(struct {
492 id: Dylib.Id,
493 parent: u16,
494 }, .Dynamic).init(arena);
1078 for (self.dylibs.items) |dylib, id| {
1079 if (!dylib.symbols.contains(sym_name)) continue;
4951080
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);
1081 const dylib_id = @intCast(u16, id);
1082 if (!self.referenced_dylibs.contains(dylib_id)) {
1083 try self.referenced_dylibs.putNoClobber(self.gpa, dylib_id, {});
1084 }
5001085
501 for (macho_file.objects.items) |_, object_id| {
502 try macho_file.resolveSymbolsInObject(@intCast(u16, object_id));
503 }
1086 const ordinal = self.referenced_dylibs.getIndex(dylib_id) orelse unreachable;
1087 sym.n_type |= macho.N_EXT;
1088 sym.n_desc = @intCast(u16, ordinal + 1) * macho.N_SYMBOL_RESOLVER;
5041089
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();
1090 if (dylib.weak) {
1091 sym.n_desc |= macho.N_WEAK_REF;
1092 }
5111093
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 }
1094 assert(resolver.unresolved.swapRemove(global_index));
1095 continue :loop;
1096 }
5211097
522 for (macho_file.objects.items) |*object| {
523 try object.scanInputSections(macho_file);
1098 next_sym += 1;
5241099 }
1100 }
1101
1102 fn resolveSymbolsAtLoading(self: *Zld, resolver: *SymbolResolver) !void {
1103 const is_lib = self.options.output_mode == .Lib;
1104 const is_dyn_lib = self.options.link_mode == .Dynamic and is_lib;
1105 const allow_undef = is_dyn_lib and (self.options.allow_shlib_undefined orelse false);
1106
1107 var next_sym: usize = 0;
1108 while (next_sym < resolver.unresolved.count()) {
1109 const global_index = resolver.unresolved.keys()[next_sym];
1110 const global = self.globals.items[global_index];
1111 const sym = self.getSymbolPtr(global);
1112 const sym_name = self.getSymbolName(global);
1113
1114 if (sym.discarded()) {
1115 sym.* = .{
1116 .n_strx = 0,
1117 .n_type = macho.N_UNDF,
1118 .n_sect = 0,
1119 .n_desc = 0,
1120 .n_value = 0,
1121 };
1122 _ = resolver.unresolved.swapRemove(global_index);
1123 continue;
1124 } else if (allow_undef) {
1125 const n_desc = @bitCast(
1126 u16,
1127 macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP * @intCast(i16, macho.N_SYMBOL_RESOLVER),
1128 );
1129 sym.n_type = macho.N_EXT;
1130 sym.n_desc = n_desc;
1131 _ = resolver.unresolved.swapRemove(global_index);
1132 continue;
1133 }
5251134
526 try macho_file.createDyldPrivateAtom();
527 try macho_file.createTentativeDefAtoms();
528 try macho_file.createStubHelperPreambleAtom();
1135 log.err("undefined reference to symbol '{s}'", .{sym_name});
1136 if (global.getFile()) |file| {
1137 log.err(" first referenced in '{s}'", .{self.objects.items[file].name});
1138 }
5291139
530 for (macho_file.objects.items) |*object, object_id| {
531 try object.splitIntoAtoms(macho_file, @intCast(u32, object_id));
1140 next_sym += 1;
5321141 }
1142 }
5331143
534 if (gc_sections) {
535 try dead_strip.gcAtoms(macho_file);
1144 fn createMhExecuteHeaderSymbol(self: *Zld, resolver: *SymbolResolver) !void {
1145 if (self.options.output_mode != .Exe) return;
1146 if (resolver.table.get("__mh_execute_header")) |global_index| {
1147 const global = self.globals.items[global_index];
1148 const sym = self.getSymbol(global);
1149 self.mh_execute_header_index = global_index;
1150 if (!sym.undf() and !(sym.pext() or sym.weakDef())) return;
5361151 }
5371152
538 try allocateSegments(macho_file);
539 try allocateSymbols(macho_file);
1153 const gpa = self.gpa;
1154 const sym_index = try self.allocateSymbol();
1155 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
1156 const sym = self.getSymbolPtr(sym_loc);
1157 sym.n_strx = try self.strtab.insert(gpa, "__mh_execute_header");
1158 sym.n_type = macho.N_SECT | macho.N_EXT;
1159 sym.n_desc = macho.REFERENCED_DYNAMICALLY;
1160
1161 if (resolver.table.get("__mh_execute_header")) |global_index| {
1162 const global = &self.globals.items[global_index];
1163 const global_object = &self.objects.items[global.getFile().?];
1164 global_object.globals_lookup[global.sym_index] = global_index;
1165 global.* = sym_loc;
1166 self.mh_execute_header_index = global_index;
1167 } else {
1168 const global_index = @intCast(u32, self.globals.items.len);
1169 try self.globals.append(gpa, sym_loc);
1170 self.mh_execute_header_index = global_index;
1171 }
1172 }
5401173
541 try macho_file.allocateSpecialSymbols();
1174 fn createDsoHandleSymbol(self: *Zld, resolver: *SymbolResolver) !void {
1175 const global_index = resolver.table.get("___dso_handle") orelse return;
1176 const global = &self.globals.items[global_index];
1177 self.dso_handle_index = global_index;
1178 if (!self.getSymbol(global.*).undf()) return;
1179
1180 const gpa = self.gpa;
1181 const sym_index = try self.allocateSymbol();
1182 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
1183 const sym = self.getSymbolPtr(sym_loc);
1184 sym.n_strx = try self.strtab.insert(gpa, "___dso_handle");
1185 sym.n_type = macho.N_SECT | macho.N_EXT;
1186 sym.n_desc = macho.N_WEAK_DEF;
1187
1188 const global_object = &self.objects.items[global.getFile().?];
1189 global_object.globals_lookup[global.sym_index] = global_index;
1190 _ = resolver.unresolved.swapRemove(resolver.table.get("___dso_handle").?);
1191 global.* = sym_loc;
1192 }
5421193
543 if (build_options.enable_logging or true) {
544 macho_file.logSymtab();
545 macho_file.logSections();
546 macho_file.logAtoms();
547 }
1194 fn resolveDyldStubBinder(self: *Zld, resolver: *SymbolResolver) !void {
1195 if (self.dyld_stub_binder_index != null) return;
1196 if (resolver.unresolved.count() == 0) return; // no need for a stub binder if we don't have any imports
5481197
549 try writeAtoms(macho_file);
1198 const gpa = self.gpa;
1199 const sym_name = "dyld_stub_binder";
1200 const sym_index = try self.allocateSymbol();
1201 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
1202 const sym = self.getSymbolPtr(sym_loc);
1203 sym.n_strx = try self.strtab.insert(gpa, sym_name);
1204 sym.n_type = macho.N_UNDF;
5501205
551 var lc_buffer = std.ArrayList(u8).init(arena);
552 const lc_writer = lc_buffer.writer();
553 var ncmds: u32 = 0;
1206 const global = SymbolWithLoc{ .sym_index = sym_index };
1207 try self.globals.append(gpa, global);
5541208
555 try writeLinkeditSegmentData(macho_file, &ncmds, lc_writer);
1209 for (self.dylibs.items) |dylib, id| {
1210 if (!dylib.symbols.contains(sym_name)) continue;
5561211
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);
1212 const dylib_id = @intCast(u16, id);
1213 if (!self.referenced_dylibs.contains(dylib_id)) {
1214 try self.referenced_dylibs.putNoClobber(gpa, dylib_id, {});
5761215 }
577 }
5781216
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);
1217 const ordinal = self.referenced_dylibs.getIndex(dylib_id) orelse unreachable;
1218 sym.n_type |= macho.N_EXT;
1219 sym.n_desc = @intCast(u16, ordinal + 1) * macho.N_SYMBOL_RESOLVER;
1220 self.dyld_stub_binder_index = sym_index;
5831221
584 {
585 try lc_writer.writeStruct(macho.source_version_command{
586 .cmdsize = @sizeOf(macho.source_version_command),
587 .version = 0x0,
588 });
589 ncmds += 1;
1222 break;
5901223 }
5911224
592 try macho_file.writeBuildVersionLC(&ncmds, lc_writer);
1225 if (self.dyld_stub_binder_index == null) {
1226 log.err("undefined reference to symbol '{s}'", .{sym_name});
1227 return error.UndefinedSymbolReference;
1228 }
1229 }
5931230
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;
1231 fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
1232 const name_len = mem.sliceTo(MachO.default_dyld_path, 0).len;
1233 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
1234 u64,
1235 @sizeOf(macho.dylinker_command) + name_len,
1236 @sizeOf(u64),
1237 ));
1238 try lc_writer.writeStruct(macho.dylinker_command{
1239 .cmd = .LOAD_DYLINKER,
1240 .cmdsize = cmdsize,
1241 .name = @sizeOf(macho.dylinker_command),
1242 });
1243 try lc_writer.writeAll(mem.sliceTo(MachO.default_dyld_path, 0));
1244 const padding = cmdsize - @sizeOf(macho.dylinker_command) - name_len;
1245 if (padding > 0) {
1246 try lc_writer.writeByteNTimes(0, padding);
6021247 }
1248 ncmds.* += 1;
1249 }
6031250
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());
629
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);
1251 fn writeMainLC(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
1252 if (self.options.output_mode != .Exe) return;
1253 const seg_id = self.getSegmentByName("__TEXT").?;
1254 const seg = self.segments.items[seg_id];
1255 const global = self.getEntryPoint();
1256 const sym = self.getSymbol(global);
1257 try lc_writer.writeStruct(macho.entry_point_command{
1258 .cmd = .MAIN,
1259 .cmdsize = @sizeOf(macho.entry_point_command),
1260 .entryoff = @intCast(u32, sym.n_value - seg.vmaddr),
1261 .stacksize = self.options.stack_size_override orelse 0,
1262 });
1263 ncmds.* += 1;
1264 }
6321265
633 try writeHeader(macho_file, ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
1266 const WriteDylibLCCtx = struct {
1267 cmd: macho.LC,
1268 name: []const u8,
1269 timestamp: u32 = 2,
1270 current_version: u32 = 0x10000,
1271 compatibility_version: u32 = 0x10000,
1272 };
6341273
635 if (codesig) |*csig| {
636 try writeCodeSignature(macho_file, csig, codesig_offset.?); // code signing always comes last
1274 fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {
1275 const name_len = ctx.name.len + 1;
1276 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
1277 u64,
1278 @sizeOf(macho.dylib_command) + name_len,
1279 @sizeOf(u64),
1280 ));
1281 try lc_writer.writeStruct(macho.dylib_command{
1282 .cmd = ctx.cmd,
1283 .cmdsize = cmdsize,
1284 .dylib = .{
1285 .name = @sizeOf(macho.dylib_command),
1286 .timestamp = ctx.timestamp,
1287 .current_version = ctx.current_version,
1288 .compatibility_version = ctx.compatibility_version,
1289 },
1290 });
1291 try lc_writer.writeAll(ctx.name);
1292 try lc_writer.writeByte(0);
1293 const padding = cmdsize - @sizeOf(macho.dylib_command) - name_len;
1294 if (padding > 0) {
1295 try lc_writer.writeByteNTimes(0, padding);
6371296 }
1297 ncmds.* += 1;
6381298 }
6391299
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)});
1300 fn writeDylibIdLC(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
1301 if (self.options.output_mode != .Lib) return;
1302 const install_name = self.options.install_name orelse self.options.emit.?.sub_path;
1303 const curr = self.options.version orelse std.builtin.Version{
1304 .major = 1,
1305 .minor = 0,
1306 .patch = 0,
6451307 };
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)});
1308 const compat = self.options.compatibility_version orelse std.builtin.Version{
1309 .major = 1,
1310 .minor = 0,
1311 .patch = 0,
6491312 };
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();
1313 try writeDylibLC(.{
1314 .cmd = .ID_DYLIB,
1315 .name = install_name,
1316 .current_version = curr.major << 16 | curr.minor << 8 | curr.patch,
1317 .compatibility_version = compat.major << 16 | compat.minor << 8 | compat.patch,
1318 }, ncmds, lc_writer);
6531319 }
654}
6551320
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 });
1321 const RpathIterator = struct {
1322 buffer: []const []const u8,
1323 table: std.StringHashMap(void),
1324 count: usize = 0,
1325
1326 fn init(gpa: Allocator, rpaths: []const []const u8) RpathIterator {
1327 return .{ .buffer = rpaths, .table = std.StringHashMap(void).init(gpa) };
6691328 }
670 }
6711329
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 }
1330 fn deinit(it: *RpathIterator) void {
1331 it.table.deinit();
1332 }
6841333
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 });
1334 fn next(it: *RpathIterator) !?[]const u8 {
1335 while (true) {
1336 if (it.count >= it.buffer.len) return null;
1337 const rpath = it.buffer[it.count];
1338 it.count += 1;
1339 const gop = try it.table.getOrPut(rpath);
1340 if (gop.found_existing) continue;
1341 return rpath;
1342 }
1343 }
1344 };
1345
1346 fn writeRpathLCs(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
1347 const gpa = self.gpa;
1348
1349 var it = RpathIterator.init(gpa, self.options.rpath_list);
1350 defer it.deinit();
1351
1352 while (try it.next()) |rpath| {
1353 const rpath_len = rpath.len + 1;
1354 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
1355 u64,
1356 @sizeOf(macho.rpath_command) + rpath_len,
1357 @sizeOf(u64),
1358 ));
1359 try lc_writer.writeStruct(macho.rpath_command{
1360 .cmdsize = cmdsize,
1361 .path = @sizeOf(macho.rpath_command),
1362 });
1363 try lc_writer.writeAll(rpath);
1364 try lc_writer.writeByte(0);
1365 const padding = cmdsize - @sizeOf(macho.rpath_command) - rpath_len;
1366 if (padding > 0) {
1367 try lc_writer.writeByteNTimes(0, padding);
1368 }
1369 ncmds.* += 1;
1370 }
6891371 }
6901372
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
1373 fn writeBuildVersionLC(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
1374 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
1375 const platform_version = blk: {
1376 const ver = self.options.target.os.version_range.semver.min;
1377 const platform_version = ver.major << 16 | ver.minor << 8;
1378 break :blk platform_version;
6961379 };
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,
1380 const sdk_version = if (self.options.native_darwin_sdk) |sdk| blk: {
1381 const ver = sdk.version;
1382 const sdk_version = ver.major << 16 | ver.minor << 8;
1383 break :blk sdk_version;
1384 } else platform_version;
1385 const is_simulator_abi = self.options.target.abi == .simulator;
1386 try lc_writer.writeStruct(macho.build_version_command{
1387 .cmdsize = cmdsize,
1388 .platform = switch (self.options.target.os.tag) {
1389 .macos => .MACOS,
1390 .ios => if (is_simulator_abi) macho.PLATFORM.IOSSIMULATOR else macho.PLATFORM.IOS,
1391 .watchos => if (is_simulator_abi) macho.PLATFORM.WATCHOSSIMULATOR else macho.PLATFORM.WATCHOS,
1392 .tvos => if (is_simulator_abi) macho.PLATFORM.TVOSSIMULATOR else macho.PLATFORM.TVOS,
1393 else => unreachable,
1394 },
1395 .minos = platform_version,
1396 .sdk = sdk_version,
1397 .ntools = 1,
7001398 });
1399 try lc_writer.writeAll(mem.asBytes(&macho.build_tool_version{
1400 .tool = .LD,
1401 .version = 0x0,
1402 }));
1403 ncmds.* += 1;
7011404 }
7021405
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 });
1406 fn writeLoadDylibLCs(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
1407 for (self.referenced_dylibs.keys()) |id| {
1408 const dylib = self.dylibs.items[id];
1409 const dylib_id = dylib.id orelse unreachable;
1410 try writeDylibLC(.{
1411 .cmd = if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,
1412 .name = dylib_id.name,
1413 .timestamp = dylib_id.timestamp,
1414 .current_version = dylib_id.current_version,
1415 .compatibility_version = dylib_id.compatibility_version,
1416 }, ncmds, lc_writer);
1417 }
7071418 }
7081419
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 }
1420 pub fn deinit(self: *Zld) void {
1421 const gpa = self.gpa;
7221422
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 }
1423 self.tlv_ptr_entries.deinit(gpa);
1424 self.tlv_ptr_table.deinit(gpa);
1425 self.got_entries.deinit(gpa);
1426 self.got_table.deinit(gpa);
1427 self.stubs.deinit(gpa);
1428 self.stubs_table.deinit(gpa);
1429 self.thunk_table.deinit(gpa);
7281430
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 }
1431 for (self.thunks.items) |*thunk| {
1432 thunk.deinit(gpa);
1433 }
1434 self.thunks.deinit(gpa);
7421435
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 }
1436 self.strtab.deinit(gpa);
1437 self.locals.deinit(gpa);
1438 self.globals.deinit(gpa);
7481439
749 if (macho_file.data_section_index == null) {
750 macho_file.data_section_index = try macho_file.initSection("__DATA", "__data", .{});
751 }
1440 for (self.objects.items) |*object| {
1441 object.deinit(gpa);
1442 }
1443 self.objects.deinit(gpa);
1444 for (self.archives.items) |*archive| {
1445 archive.deinit(gpa);
1446 }
1447 self.archives.deinit(gpa);
1448 for (self.dylibs.items) |*dylib| {
1449 dylib.deinit(gpa);
1450 }
1451 self.dylibs.deinit(gpa);
1452 self.dylibs_map.deinit(gpa);
1453 self.referenced_dylibs.deinit(gpa);
7521454
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 });
1455 self.segments.deinit(gpa);
1456 self.sections.deinit(gpa);
1457 self.atoms.deinit(gpa);
7631458 }
764}
7651459
766fn writeAtoms(macho_file: *MachO) !void {
767 assert(macho_file.mode == .one_shot);
1460 fn createSegments(self: *Zld) !void {
1461 const pagezero_vmsize = self.options.pagezero_size orelse MachO.default_pagezero_vmsize;
1462 const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);
1463 if (self.options.output_mode != .Lib and aligned_pagezero_vmsize > 0) {
1464 if (aligned_pagezero_vmsize != pagezero_vmsize) {
1465 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});
1466 log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize});
1467 }
1468 try self.segments.append(self.gpa, .{
1469 .cmdsize = @sizeOf(macho.segment_command_64),
1470 .segname = makeStaticString("__PAGEZERO"),
1471 .vmsize = aligned_pagezero_vmsize,
1472 });
1473 }
7681474
769 const gpa = macho_file.base.allocator;
770 const slice = macho_file.sections.slice();
1475 // __TEXT segment is non-optional
1476 {
1477 const protection = getSegmentMemoryProtection("__TEXT");
1478 try self.segments.append(self.gpa, .{
1479 .cmdsize = @sizeOf(macho.segment_command_64),
1480 .segname = makeStaticString("__TEXT"),
1481 .maxprot = protection,
1482 .initprot = protection,
1483 });
1484 }
7711485
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.?;
1486 for (self.sections.items(.header)) |header, sect_id| {
1487 if (header.size == 0) continue; // empty section
1488
1489 const segname = header.segName();
1490 const segment_id = self.getSegmentByName(segname) orelse blk: {
1491 log.debug("creating segment '{s}'", .{segname});
1492 const segment_id = @intCast(u8, self.segments.items.len);
1493 const protection = getSegmentMemoryProtection(segname);
1494 try self.segments.append(self.gpa, .{
1495 .cmdsize = @sizeOf(macho.segment_command_64),
1496 .segname = makeStaticString(segname),
1497 .maxprot = protection,
1498 .initprot = protection,
1499 });
1500 break :blk segment_id;
1501 };
1502 const segment = &self.segments.items[segment_id];
1503 segment.cmdsize += @sizeOf(macho.section_64);
1504 segment.nsects += 1;
1505 self.sections.items(.segment_index)[sect_id] = segment_id;
1506 }
7761507
777 if (header.isZerofill()) continue;
1508 // __LINKEDIT always comes last
1509 {
1510 const protection = getSegmentMemoryProtection("__LINKEDIT");
1511 try self.segments.append(self.gpa, .{
1512 .cmdsize = @sizeOf(macho.segment_command_64),
1513 .segname = makeStaticString("__LINKEDIT"),
1514 .maxprot = protection,
1515 .initprot = protection,
1516 });
1517 }
1518 }
7781519
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);
1520 inline fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 {
1521 const name_len = if (assume_max_path_len) std.os.PATH_MAX else std.mem.len(name) + 1;
1522 return mem.alignForwardGeneric(u64, cmd_size + name_len, @alignOf(u64));
1523 }
7821524
783 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
1525 fn calcLCsSize(self: *Zld, assume_max_path_len: bool) !u32 {
1526 const gpa = self.gpa;
7841527
785 while (atom.prev) |prev| {
786 atom = prev;
1528 var sizeofcmds: u64 = 0;
1529 for (self.segments.items) |seg| {
1530 sizeofcmds += seg.nsects * @sizeOf(macho.section_64) + @sizeOf(macho.segment_command_64);
7871531 }
7881532
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);
1533 // LC_DYLD_INFO_ONLY
1534 sizeofcmds += @sizeOf(macho.dyld_info_command);
1535 // LC_FUNCTION_STARTS
1536 if (self.getSectionByName("__TEXT", "__text")) |_| {
1537 sizeofcmds += @sizeOf(macho.linkedit_data_command);
1538 }
1539 // LC_DATA_IN_CODE
1540 sizeofcmds += @sizeOf(macho.linkedit_data_command);
1541 // LC_SYMTAB
1542 sizeofcmds += @sizeOf(macho.symtab_command);
1543 // LC_DYSYMTAB
1544 sizeofcmds += @sizeOf(macho.dysymtab_command);
1545 // LC_LOAD_DYLINKER
1546 sizeofcmds += calcInstallNameLen(
1547 @sizeOf(macho.dylinker_command),
1548 mem.sliceTo(MachO.default_dyld_path, 0),
1549 false,
1550 );
1551 // LC_MAIN
1552 if (self.options.output_mode == .Exe) {
1553 sizeofcmds += @sizeOf(macho.entry_point_command);
1554 }
1555 // LC_ID_DYLIB
1556 if (self.options.output_mode == .Lib) {
1557 sizeofcmds += blk: {
1558 const install_name = self.options.install_name orelse self.options.emit.?.sub_path;
1559 break :blk calcInstallNameLen(
1560 @sizeOf(macho.dylib_command),
1561 install_name,
1562 assume_max_path_len,
1563 );
1564 };
1565 }
1566 // LC_RPATH
1567 {
1568 var it = RpathIterator.init(gpa, self.options.rpath_list);
1569 defer it.deinit();
1570 while (try it.next()) |rpath| {
1571 sizeofcmds += calcInstallNameLen(
1572 @sizeOf(macho.rpath_command),
1573 rpath,
1574 assume_max_path_len,
1575 );
8131576 }
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;
1577 }
1578 // LC_SOURCE_VERSION
1579 sizeofcmds += @sizeOf(macho.source_version_command);
1580 // LC_BUILD_VERSION
1581 sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
1582 // LC_UUID
1583 sizeofcmds += @sizeOf(macho.uuid_command);
1584 // LC_LOAD_DYLIB
1585 for (self.referenced_dylibs.keys()) |id| {
1586 const dylib = self.dylibs.items[id];
1587 const dylib_id = dylib.id orelse unreachable;
1588 sizeofcmds += calcInstallNameLen(
1589 @sizeOf(macho.dylib_command),
1590 dylib_id.name,
1591 assume_max_path_len,
1592 );
1593 }
1594 // LC_CODE_SIGNATURE
1595 {
1596 const target = self.options.target;
1597 const requires_codesig = blk: {
1598 if (self.options.entitlements) |_| break :blk true;
1599 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
1600 break :blk true;
1601 break :blk false;
1602 };
1603 if (requires_codesig) {
1604 sizeofcmds += @sizeOf(macho.linkedit_data_command);
8221605 }
8231606 }
824 }
825}
8261607
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());
1608 return @intCast(u32, sizeofcmds);
1609 }
8311610
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];
1611 fn calcMinHeaderPad(self: *Zld) !u64 {
1612 var padding: u32 = (try self.calcLCsSize(false)) + (self.options.headerpad_size orelse 0);
1613 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
8361614
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);
1615 if (self.options.headerpad_max_install_names) {
1616 var min_headerpad_size: u32 = try self.calcLCsSize(true);
1617 log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{
1618 min_headerpad_size + @sizeOf(macho.mach_header_64),
1619 });
1620 padding = @max(padding, min_headerpad_size);
8421621 }
8431622
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);
1623 const offset = @sizeOf(macho.mach_header_64) + padding;
1624 log.debug("actual headerpad size 0x{x}", .{offset});
1625
1626 return offset;
1627 }
1628
1629 pub fn allocateSymbol(self: *Zld) !u32 {
1630 try self.locals.ensureUnusedCapacity(self.gpa, 1);
1631 log.debug(" (allocating symbol index {d})", .{self.locals.items.len});
1632 const index = @intCast(u32, self.locals.items.len);
1633 _ = self.locals.addOneAssumeCapacity();
1634 self.locals.items[index] = .{
1635 .n_strx = 0,
1636 .n_type = 0,
1637 .n_sect = 0,
1638 .n_desc = 0,
1639 .n_value = 0,
8501640 };
1641 return index;
1642 }
8511643
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 }
1644 fn allocateSpecialSymbols(self: *Zld) !void {
1645 for (&[_]?u32{
1646 self.dso_handle_index,
1647 self.mh_execute_header_index,
1648 }) |maybe_index| {
1649 const global_index = maybe_index orelse continue;
1650 const global = self.globals.items[global_index];
1651 if (global.getFile() != null) continue;
1652 const name = self.getSymbolName(global);
1653 const sym = self.getSymbolPtr(global);
1654 const segment_index = self.getSegmentByName("__TEXT").?;
1655 const seg = self.segments.items[segment_index];
1656 sym.n_sect = 1;
1657 sym.n_value = seg.vmaddr;
1658 log.debug("allocating {s} at the start of {s}", .{
1659 name,
1660 seg.segName(),
1661 });
8571662 }
8581663 }
8591664
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);
1665 fn writeAtoms(self: *Zld, reverse_lookups: [][]u32) !void {
1666 const gpa = self.gpa;
1667 const slice = self.sections.slice();
8641668
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);
1669 for (slice.items(.first_atom_index)) |first_atom_index, sect_id| {
1670 const header = slice.items(.header)[sect_id];
1671 var atom_index = first_atom_index;
8701672
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}
1673 if (header.isZerofill()) continue;
8781674
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}
1675 var buffer = std.ArrayList(u8).init(gpa);
1676 defer buffer.deinit();
1677 try buffer.ensureTotalCapacity(math.cast(usize, header.size) orelse return error.Overflow);
8901678
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];
1679 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
8941680
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;
1681 var count: u32 = 0;
1682 while (true) : (count += 1) {
1683 const atom = self.getAtom(atom_index);
1684 const this_sym = self.getSymbol(atom.getSymbolWithLoc());
1685 const padding_size: usize = if (atom.next_index) |next_index| blk: {
1686 const next_sym = self.getSymbol(self.getAtom(next_index).getSymbolWithLoc());
1687 const size = next_sym.n_value - (this_sym.n_value + atom.size);
1688 break :blk math.cast(usize, size) orelse return error.Overflow;
1689 } else 0;
9001690
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);
1691 log.debug(" (adding ATOM(%{d}, '{s}') from object({?}) to buffer)", .{
1692 atom.sym_index,
1693 self.getSymbolName(atom.getSymbolWithLoc()),
1694 atom.file,
1695 });
1696 if (padding_size > 0) {
1697 log.debug(" (with padding {x})", .{padding_size});
1698 }
9081699
909 header.offset = if (header.isZerofill())
910 0
911 else
912 @intCast(u32, seg.fileoff + start_aligned);
913 header.addr = seg.vmaddr + start_aligned;
1700 const offset = buffer.items.len;
1701
1702 // TODO: move writing synthetic sections into a separate function
1703 if (atom.getFile() == null) outer: {
1704 if (self.dyld_private_sym_index) |sym_index| {
1705 if (atom.sym_index == sym_index) {
1706 buffer.appendSliceAssumeCapacity(&[_]u8{0} ** @sizeOf(u64));
1707 break :outer;
1708 }
1709 }
1710 switch (header.@"type"()) {
1711 macho.S_NON_LAZY_SYMBOL_POINTERS => {
1712 try self.writeGotPointer(count, buffer.writer());
1713 },
1714 macho.S_LAZY_SYMBOL_POINTERS => {
1715 try self.writeLazyPointer(count, buffer.writer());
1716 },
1717 macho.S_THREAD_LOCAL_VARIABLE_POINTERS => {
1718 buffer.appendSliceAssumeCapacity(&[_]u8{0} ** @sizeOf(u64));
1719 },
1720 else => {
1721 if (self.stub_helper_preamble_sym_index) |sym_index| {
1722 if (sym_index == atom.sym_index) {
1723 try self.writeStubHelperPreambleCode(buffer.writer());
1724 break :outer;
1725 }
1726 }
1727 if (header.@"type"() == macho.S_SYMBOL_STUBS) {
1728 try self.writeStubCode(atom_index, count, buffer.writer());
1729 } else if (mem.eql(u8, header.sectName(), "__stub_helper")) {
1730 try self.writeStubHelperCode(atom_index, buffer.writer());
1731 } else if (header.isCode()) {
1732 // A thunk
1733 try thunks.writeThunkCode(self, atom_index, buffer.writer());
1734 } else unreachable;
1735 },
1736 }
1737 } else {
1738 const code = Atom.getAtomCode(self, atom_index);
1739 const relocs = Atom.getAtomRelocs(self, atom_index);
1740 const size = math.cast(usize, atom.size) orelse return error.Overflow;
1741 buffer.appendSliceAssumeCapacity(code);
1742 try Atom.resolveRelocs(
1743 self,
1744 atom_index,
1745 buffer.items[offset..][0..size],
1746 relocs,
1747 reverse_lookups[atom.getFile().?],
1748 );
1749 }
9141750
915 start = start_aligned + header.size;
1751 var i: usize = 0;
1752 while (i < padding_size) : (i += 1) {
1753 // TODO with NOPs
1754 buffer.appendAssumeCapacity(0);
1755 }
9161756
917 if (!header.isZerofill()) {
918 seg.filesize = start;
1757 if (atom.next_index) |next_index| {
1758 atom_index = next_index;
1759 } else {
1760 assert(buffer.items.len == header.size);
1761 log.debug(" (writing at file offset 0x{x})", .{header.offset});
1762 try self.file.pwriteAll(buffer.items, header.offset);
1763 break;
1764 }
1765 }
9191766 }
920 seg.vmsize = start;
9211767 }
9221768
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}
1769 fn pruneAndSortSections(self: *Zld) !void {
1770 const gpa = self.gpa;
1771
1772 const SortSection = struct {
1773 pub fn lessThan(_: void, lhs: Section, rhs: Section) bool {
1774 return getSectionPrecedence(lhs.header) < getSectionPrecedence(rhs.header);
1775 }
1776 };
1777
1778 const slice = self.sections.slice();
1779 var sections = std.ArrayList(Section).init(gpa);
1780 defer sections.deinit();
1781 try sections.ensureTotalCapacity(slice.len);
9261782
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;
1783 {
1784 var i: u8 = 0;
1785 while (i < slice.len) : (i += 1) {
1786 const section = self.sections.get(i);
1787 if (section.header.size == 0) {
1788 log.debug("pruning section {s},{s}", .{
1789 section.header.segName(),
1790 section.header.sectName(),
1791 });
1792 continue;
1793 }
1794 sections.appendAssumeCapacity(section);
1795 }
1796 }
9321797
933 while (atom.prev) |prev| {
934 atom = prev;
1798 std.sort.sort(Section, sections.items, {}, SortSection.lessThan);
1799
1800 self.sections.shrinkRetainingCapacity(0);
1801 for (sections.items) |out| {
1802 self.sections.appendAssumeCapacity(out);
9351803 }
1804 }
9361805
937 const n_sect = @intCast(u8, sect_id + 1);
938 var base_vaddr = header.addr;
1806 fn calcSectionSizes(self: *Zld, reverse_lookups: [][]u32) !void {
1807 const slice = self.sections.slice();
1808 for (slice.items(.header)) |*header, sect_id| {
1809 if (header.size == 0) continue;
1810 if (self.requiresThunks()) {
1811 if (header.isCode() and !(header.@"type"() == macho.S_SYMBOL_STUBS) and !mem.eql(u8, header.sectName(), "__stub_helper")) continue;
1812 }
9391813
940 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
941 n_sect,
942 header.segName(),
943 header.sectName(),
944 });
1814 var atom_index = slice.items(.first_atom_index)[sect_id];
1815 header.size = 0;
1816 header.@"align" = 0;
9451817
946 while (true) {
947 const alignment = try math.powi(u32, 2, atom.alignment);
948 base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment);
1818 while (true) {
1819 const atom = self.getAtom(atom_index);
1820 const atom_alignment = try math.powi(u32, 2, atom.alignment);
1821 const atom_offset = mem.alignForwardGeneric(u64, header.size, atom_alignment);
1822 const padding = atom_offset - header.size;
9491823
950 const sym = atom.getSymbolPtr(macho_file);
951 sym.n_value = base_vaddr;
952 sym.n_sect = n_sect;
1824 const sym = self.getSymbolPtr(atom.getSymbolWithLoc());
1825 sym.n_value = atom_offset;
9531826
954 log.debug(" ATOM(%{d}, '{s}') @{x}", .{ atom.sym_index, atom.getName(macho_file), base_vaddr });
1827 header.size += padding + atom.size;
1828 header.@"align" = @max(header.@"align", atom.alignment);
9551829
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;
1830 if (atom.next_index) |next_index| {
1831 atom_index = next_index;
1832 } else break;
9641833 }
1834 }
9651835
966 base_vaddr += atom.size;
1836 if (self.requiresThunks()) {
1837 for (slice.items(.header)) |header, sect_id| {
1838 if (!header.isCode()) continue;
1839 if (header.@"type"() == macho.S_SYMBOL_STUBS) continue;
1840 if (mem.eql(u8, header.sectName(), "__stub_helper")) continue;
9671841
968 if (atom.next) |next| {
969 atom = next;
970 } else break;
1842 // Create jump/branch range extenders if needed.
1843 try thunks.createThunks(self, @intCast(u8, sect_id), reverse_lookups);
1844 }
9711845 }
9721846 }
973}
974
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;
9791847
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);
984
985 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, macho_file.page_size);
986}
1848 fn allocateSegments(self: *Zld) !void {
1849 for (self.segments.items) |*segment, segment_index| {
1850 const is_text_segment = mem.eql(u8, segment.segName(), "__TEXT");
1851 const base_size = if (is_text_segment) try self.calcMinHeaderPad() else 0;
1852 try self.allocateSegment(@intCast(u8, segment_index), base_size);
1853 }
1854 }
9871855
988fn writeDyldInfoData(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
989 const tracy = trace(@src());
990 defer tracy.end();
1856 fn getSegmentAllocBase(self: Zld, segment_index: u8) struct { vmaddr: u64, fileoff: u64 } {
1857 if (segment_index > 0) {
1858 const prev_segment = self.segments.items[segment_index - 1];
1859 return .{
1860 .vmaddr = prev_segment.vmaddr + prev_segment.vmsize,
1861 .fileoff = prev_segment.fileoff + prev_segment.filesize,
1862 };
1863 }
1864 return .{ .vmaddr = 0, .fileoff = 0 };
1865 }
9911866
992 const gpa = macho_file.base.allocator;
1867 fn allocateSegment(self: *Zld, segment_index: u8, init_size: u64) !void {
1868 const segment = &self.segments.items[segment_index];
9931869
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();
1870 if (mem.eql(u8, segment.segName(), "__PAGEZERO")) return; // allocated upon creation
10001871
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];
1872 const base = self.getSegmentAllocBase(segment_index);
1873 segment.vmaddr = base.vmaddr;
1874 segment.fileoff = base.fileoff;
1875 segment.filesize = init_size;
1876 segment.vmsize = init_size;
10061877
1007 if (mem.eql(u8, header.segName(), "__TEXT")) continue; // __TEXT is non-writable
1878 // Allocate the sections according to their alignment at the beginning of the segment.
1879 const indexes = self.getSectionIndexes(segment_index);
1880 var start = init_size;
10081881
1009 log.debug("dyld info for {s},{s}", .{ header.segName(), header.sectName() });
1882 const slice = self.sections.slice();
1883 for (slice.items(.header)[indexes.start..indexes.end]) |*header, sect_id| {
1884 var atom_index = slice.items(.first_atom_index)[indexes.start + sect_id];
10101885
1011 const seg = macho_file.segments.items[segment_index];
1886 const alignment = try math.powi(u32, 2, header.@"align");
1887 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
1888 const n_sect = @intCast(u8, indexes.start + sect_id + 1);
1889
1890 header.offset = if (header.isZerofill())
1891 0
1892 else
1893 @intCast(u32, segment.fileoff + start_aligned);
1894 header.addr = segment.vmaddr + start_aligned;
1895
1896 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
1897 n_sect,
1898 header.segName(),
1899 header.sectName(),
1900 });
10121901
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;
1902 while (true) {
1903 const atom = self.getAtom(atom_index);
1904 const sym = self.getSymbolPtr(atom.getSymbolWithLoc());
1905 sym.n_value += header.addr;
1906 sym.n_sect = n_sect;
10171907
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,
1908 log.debug(" ATOM(%{d}, '{s}') @{x}", .{
1909 atom.sym_index,
1910 self.getSymbolName(atom.getSymbolWithLoc()),
1911 sym.n_value,
10231912 });
1024 }
10251913
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 }
1914 if (atom.getFile() != null) {
1915 // Update each symbol contained within the atom
1916 var it = Atom.getInnerSymbolsIterator(self, atom_index);
1917 while (it.next()) |sym_loc| {
1918 const inner_sym = self.getSymbolPtr(sym_loc);
1919 inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(
1920 self,
1921 atom_index,
1922 sym_loc.sym_index,
1923 );
1924 inner_sym.n_sect = n_sect;
1925 }
10511926
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);
1927 // If there is a section alias, update it now too
1928 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
1929 const alias = self.getSymbolPtr(sym_loc);
1930 alias.n_value = sym.n_value;
1931 alias.n_sect = n_sect;
1932 }
10681933 }
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 });
1934
1935 if (atom.next_index) |next_index| {
1936 atom_index = next_index;
1937 } else break;
10761938 }
10771939
1078 if (atom.prev) |prev| {
1079 atom = prev;
1080 } else break;
1940 start = start_aligned + header.size;
1941
1942 if (!header.isZerofill()) {
1943 segment.filesize = start;
1944 }
1945 segment.vmsize = start;
10811946 }
1947
1948 segment.filesize = mem.alignForwardGeneric(u64, segment.filesize, self.page_size);
1949 segment.vmsize = mem.alignForwardGeneric(u64, segment.vmsize, self.page_size);
10821950 }
10831951
1084 var trie: Trie = .{};
1085 defer trie.deinit(gpa);
1952 const InitSectionOpts = struct {
1953 flags: u32 = macho.S_REGULAR,
1954 reserved1: u32 = 0,
1955 reserved2: u32 = 0,
1956 };
10861957
1087 {
1088 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
1089 log.debug("generating export trie", .{});
1958 fn initSection(
1959 self: *Zld,
1960 segname: []const u8,
1961 sectname: []const u8,
1962 opts: InitSectionOpts,
1963 ) !u8 {
1964 const gpa = self.gpa;
1965 log.debug("creating section '{s},{s}'", .{ segname, sectname });
1966 const index = @intCast(u8, self.sections.slice().len);
1967 try self.sections.append(gpa, .{
1968 .segment_index = undefined,
1969 .header = .{
1970 .sectname = makeStaticString(sectname),
1971 .segname = makeStaticString(segname),
1972 .flags = opts.flags,
1973 .reserved1 = opts.reserved1,
1974 .reserved2 = opts.reserved2,
1975 },
1976 .first_atom_index = undefined,
1977 .last_atom_index = undefined,
1978 });
1979 return index;
1980 }
10901981
1091 const text_segment = macho_file.segments.items[macho_file.text_segment_cmd_index.?];
1092 const base_address = text_segment.vmaddr;
1982 inline fn getSegmentPrecedence(segname: []const u8) u4 {
1983 if (mem.eql(u8, segname, "__PAGEZERO")) return 0x0;
1984 if (mem.eql(u8, segname, "__TEXT")) return 0x1;
1985 if (mem.eql(u8, segname, "__DATA_CONST")) return 0x2;
1986 if (mem.eql(u8, segname, "__DATA")) return 0x3;
1987 if (mem.eql(u8, segname, "__LINKEDIT")) return 0x5;
1988 return 0x4;
1989 }
10931990
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 });
1991 inline fn getSegmentMemoryProtection(segname: []const u8) macho.vm_prot_t {
1992 if (mem.eql(u8, segname, "__PAGEZERO")) return macho.PROT.NONE;
1993 if (mem.eql(u8, segname, "__TEXT")) return macho.PROT.READ | macho.PROT.EXEC;
1994 if (mem.eql(u8, segname, "__LINKEDIT")) return macho.PROT.READ;
1995 return macho.PROT.READ | macho.PROT.WRITE;
1996 }
1997
1998 inline fn getSectionPrecedence(header: macho.section_64) u8 {
1999 const segment_precedence: u4 = getSegmentPrecedence(header.segName());
2000 const section_precedence: u4 = blk: {
2001 if (header.isCode()) {
2002 if (mem.eql(u8, "__text", header.sectName())) break :blk 0x0;
2003 if (header.@"type"() == macho.S_SYMBOL_STUBS) break :blk 0x1;
2004 break :blk 0x2;
11072005 }
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);
2006 switch (header.@"type"()) {
2007 macho.S_NON_LAZY_SYMBOL_POINTERS,
2008 macho.S_LAZY_SYMBOL_POINTERS,
2009 => break :blk 0x0,
2010 macho.S_MOD_INIT_FUNC_POINTERS => break :blk 0x1,
2011 macho.S_MOD_TERM_FUNC_POINTERS => break :blk 0x2,
2012 macho.S_ZEROFILL => break :blk 0xf,
2013 macho.S_THREAD_LOCAL_REGULAR => break :blk 0xd,
2014 macho.S_THREAD_LOCAL_ZEROFILL => break :blk 0xe,
2015 else => if (mem.eql(u8, "__eh_frame", header.sectName()))
2016 break :blk 0xf
2017 else
2018 break :blk 0x3,
2019 }
2020 };
2021 return (@intCast(u8, segment_precedence) << 4) + section_precedence;
2022 }
11122023
1113 if (sym.undf()) continue;
1114 if (!sym.ext()) continue;
1115 if (sym.n_desc == MachO.N_DESC_GCED) continue;
2024 fn writeSegmentHeaders(self: *Zld, ncmds: *u32, writer: anytype) !void {
2025 for (self.segments.items) |seg, i| {
2026 const indexes = self.getSectionIndexes(@intCast(u8, i));
2027 var out_seg = seg;
2028 out_seg.cmdsize = @sizeOf(macho.segment_command_64);
2029 out_seg.nsects = 0;
2030
2031 // Update section headers count; any section with size of 0 is excluded
2032 // since it doesn't have any data in the final binary file.
2033 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
2034 if (header.size == 0) continue;
2035 out_seg.cmdsize += @sizeOf(macho.section_64);
2036 out_seg.nsects += 1;
2037 }
11162038
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 });
2039 if (out_seg.nsects == 0 and
2040 (mem.eql(u8, out_seg.segName(), "__DATA_CONST") or
2041 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;
2042
2043 try writer.writeStruct(out_seg);
2044 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
2045 if (header.size == 0) continue;
2046 try writer.writeStruct(header);
11242047 }
1125 }
11262048
1127 try trie.finalize(gpa);
2049 ncmds.* += 1;
2050 }
11282051 }
11292052
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}
2053 fn writeLinkeditSegmentData(self: *Zld, ncmds: *u32, lc_writer: anytype, reverse_lookups: [][]u32) !void {
2054 try self.writeDyldInfoData(ncmds, lc_writer, reverse_lookups);
2055 try self.writeFunctionStarts(ncmds, lc_writer);
2056 try self.writeDataInCode(ncmds, lc_writer);
2057 try self.writeSymtabs(ncmds, lc_writer);
11952058
1196fn populateLazyBindOffsetsInStubHelper(macho_file: *MachO, buffer: []const u8) !void {
1197 const gpa = macho_file.base.allocator;
2059 const seg = self.getLinkeditSegmentPtr();
2060 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
2061 }
11982062
1199 const stub_helper_section_index = macho_file.stub_helper_section_index orelse return;
1200 if (macho_file.stub_helper_preamble_atom == null) return;
2063 fn collectRebaseDataFromContainer(
2064 self: *Zld,
2065 sect_id: u8,
2066 pointers: *std.ArrayList(bind.Pointer),
2067 container: anytype,
2068 ) !void {
2069 const slice = self.sections.slice();
2070 const segment_index = slice.items(.segment_index)[sect_id];
2071 const seg = self.getSegment(sect_id);
12012072
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?
2073 try pointers.ensureUnusedCapacity(container.items.len);
12052074
1206 var table = std.AutoHashMap(i64, *Atom).init(gpa);
1207 defer table.deinit();
2075 for (container.items) |entry| {
2076 const target_sym = entry.getTargetSymbol(self);
2077 if (target_sym.undf()) continue;
12082078
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 };
2079 const atom_sym = entry.getAtomSymbol(self);
2080 const base_offset = atom_sym.n_value - seg.vmaddr;
12162081
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;
2082 log.debug(" | rebase at {x}", .{base_offset});
2083
2084 pointers.appendAssumeCapacity(.{
2085 .offset = base_offset,
2086 .segment_id = segment_index,
2087 });
12272088 }
12282089 }
12292090
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;
2091 fn collectRebaseData(self: *Zld, pointers: *std.ArrayList(bind.Pointer)) !void {
2092 log.debug("collecting rebase data", .{});
12362093
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;
2094 // First, unpack GOT entries
2095 if (self.getSectionByName("__DATA_CONST", "__got")) |sect_id| {
2096 try self.collectRebaseDataFromContainer(sect_id, pointers, self.got_entries);
2097 }
12422098
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) });
2099 const slice = self.sections.slice();
2100
2101 // Next, unpact lazy pointers
2102 // TODO: save la_ptr in a container so that we can re-use the helper
2103 if (self.getSectionByName("__DATA", "__la_symbol_ptr")) |sect_id| {
2104 const segment_index = slice.items(.segment_index)[sect_id];
2105 const seg = self.getSegment(sect_id);
2106 var atom_index = slice.items(.first_atom_index)[sect_id];
2107
2108 try pointers.ensureUnusedCapacity(self.stubs.items.len);
2109
2110 while (true) {
2111 const atom = self.getAtom(atom_index);
2112 const sym = self.getSymbol(atom.getSymbolWithLoc());
2113 const base_offset = sym.n_value - seg.vmaddr;
2114
2115 log.debug(" | rebase at {x}", .{base_offset});
2116
2117 pointers.appendAssumeCapacity(.{
2118 .offset = base_offset,
2119 .segment_id = segment_index,
2120 });
2121
2122 if (atom.next_index) |next_index| {
2123 atom_index = next_index;
2124 } else break;
2125 }
2126 }
2127
2128 // Finally, unpack the rest.
2129 for (slice.items(.header)) |header, sect_id| {
2130 switch (header.@"type"()) {
2131 macho.S_LITERAL_POINTERS,
2132 macho.S_REGULAR,
2133 macho.S_MOD_INIT_FUNC_POINTERS,
2134 macho.S_MOD_TERM_FUNC_POINTERS,
2135 => {},
2136 else => continue,
2137 }
2138
2139 const segment_index = slice.items(.segment_index)[sect_id];
2140 const segment = self.getSegment(@intCast(u8, sect_id));
2141 if (segment.maxprot & macho.PROT.WRITE == 0) continue;
2142
2143 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
2144
2145 const cpu_arch = self.options.target.cpu.arch;
2146 var atom_index = slice.items(.first_atom_index)[sect_id];
2147
2148 while (true) {
2149 const atom = self.getAtom(atom_index);
2150 const sym = self.getSymbol(atom.getSymbolWithLoc());
2151
2152 const should_rebase = blk: {
2153 if (self.dyld_private_sym_index) |sym_index| {
2154 if (atom.getFile() == null and atom.sym_index == sym_index) break :blk false;
2155 }
2156 break :blk !sym.undf();
2157 };
2158
2159 if (should_rebase) {
2160 log.debug(" ATOM({d}, %{d}, '{s}')", .{ atom_index, atom.sym_index, self.getSymbolName(atom.getSymbolWithLoc()) });
2161
2162 const object = self.objects.items[atom.getFile().?];
2163 const base_rel_offset: i32 = blk: {
2164 const source_sym = object.getSourceSymbol(atom.sym_index) orelse break :blk 0;
2165 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
2166 break :blk @intCast(i32, source_sym.n_value - source_sect.addr);
2167 };
2168 const relocs = Atom.getAtomRelocs(self, atom_index);
2169
2170 for (relocs) |rel| {
2171 switch (cpu_arch) {
2172 .aarch64 => {
2173 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
2174 if (rel_type != .ARM64_RELOC_UNSIGNED) continue;
2175 if (rel.r_length != 3) continue;
2176 },
2177 .x86_64 => {
2178 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
2179 if (rel_type != .X86_64_RELOC_UNSIGNED) continue;
2180 if (rel.r_length != 3) continue;
2181 },
2182 else => unreachable,
2183 }
2184
2185 const base_offset = @intCast(i32, sym.n_value - segment.vmaddr);
2186 const rel_offset = rel.r_address - base_rel_offset;
2187 const offset = @intCast(u64, base_offset + rel_offset);
2188 log.debug(" | rebase at {x}", .{offset});
2189
2190 try pointers.append(.{
2191 .offset = offset,
2192 .segment_id = segment_index,
2193 });
2194 }
12512195 }
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();
2196
2197 if (atom.next_index) |next_index| {
2198 atom_index = next_index;
2199 } else break;
2200 }
2201 }
2202 }
2203
2204 fn collectBindDataFromContainer(
2205 self: *Zld,
2206 sect_id: u8,
2207 pointers: *std.ArrayList(bind.Pointer),
2208 container: anytype,
2209 ) !void {
2210 const slice = self.sections.slice();
2211 const segment_index = slice.items(.segment_index)[sect_id];
2212 const seg = self.getSegment(sect_id);
2213
2214 try pointers.ensureUnusedCapacity(container.items.len);
2215
2216 for (container.items) |entry| {
2217 const bind_sym_name = entry.getTargetSymbolName(self);
2218 const bind_sym = entry.getTargetSymbol(self);
2219 if (bind_sym.sect()) continue;
2220
2221 const sym = entry.getAtomSymbol(self);
2222 const base_offset = sym.n_value - seg.vmaddr;
2223
2224 const dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER);
2225 var flags: u4 = 0;
2226 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
2227 base_offset,
2228 bind_sym_name,
2229 dylib_ordinal,
2230 });
2231 if (bind_sym.weakRef()) {
2232 log.debug(" | marking as weak ref ", .{});
2233 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
2234 }
2235 pointers.appendAssumeCapacity(.{
2236 .offset = base_offset,
2237 .segment_id = segment_index,
2238 .dylib_ordinal = dylib_ordinal,
2239 .name = bind_sym_name,
2240 .bind_flags = flags,
2241 });
2242 }
2243 }
2244
2245 fn collectBindData(self: *Zld, pointers: *std.ArrayList(bind.Pointer), reverse_lookups: [][]u32) !void {
2246 log.debug("collecting bind data", .{});
2247
2248 // First, unpack GOT section
2249 if (self.getSectionByName("__DATA_CONST", "__got")) |sect_id| {
2250 try self.collectBindDataFromContainer(sect_id, pointers, self.got_entries);
2251 }
2252
2253 // Next, unpack TLV pointers section
2254 if (self.getSectionByName("__DATA", "__thread_ptrs")) |sect_id| {
2255 try self.collectBindDataFromContainer(sect_id, pointers, self.tlv_ptr_entries);
2256 }
2257
2258 // Finally, unpack the rest.
2259 const slice = self.sections.slice();
2260 for (slice.items(.header)) |header, sect_id| {
2261 switch (header.@"type"()) {
2262 macho.S_LITERAL_POINTERS,
2263 macho.S_REGULAR,
2264 macho.S_MOD_INIT_FUNC_POINTERS,
2265 macho.S_MOD_TERM_FUNC_POINTERS,
2266 => {},
2267 else => continue,
2268 }
2269
2270 const segment_index = slice.items(.segment_index)[sect_id];
2271 const segment = self.getSegment(@intCast(u8, sect_id));
2272 if (segment.maxprot & macho.PROT.WRITE == 0) continue;
2273
2274 const cpu_arch = self.options.target.cpu.arch;
2275 var atom_index = slice.items(.first_atom_index)[sect_id];
2276
2277 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
2278
2279 while (true) {
2280 const atom = self.getAtom(atom_index);
2281 const sym = self.getSymbol(atom.getSymbolWithLoc());
2282
2283 log.debug(" ATOM({d}, %{d}, '{s}')", .{ atom_index, atom.sym_index, self.getSymbolName(atom.getSymbolWithLoc()) });
2284
2285 const should_bind = blk: {
2286 if (self.dyld_private_sym_index) |sym_index| {
2287 if (atom.getFile() == null and atom.sym_index == sym_index) break :blk false;
2288 }
2289 break :blk true;
2290 };
2291
2292 if (should_bind) {
2293 const object = self.objects.items[atom.getFile().?];
2294 const base_rel_offset: i32 = blk: {
2295 const source_sym = object.getSourceSymbol(atom.sym_index) orelse break :blk 0;
2296 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
2297 break :blk @intCast(i32, source_sym.n_value - source_sect.addr);
2298 };
2299 const relocs = Atom.getAtomRelocs(self, atom_index);
2300
2301 for (relocs) |rel| {
2302 switch (cpu_arch) {
2303 .aarch64 => {
2304 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
2305 if (rel_type != .ARM64_RELOC_UNSIGNED) continue;
2306 if (rel.r_length != 3) continue;
2307 },
2308 .x86_64 => {
2309 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
2310 if (rel_type != .X86_64_RELOC_UNSIGNED) continue;
2311 if (rel.r_length != 3) continue;
2312 },
2313 else => unreachable,
2314 }
2315
2316 const global = Atom.parseRelocTarget(self, atom_index, rel, reverse_lookups[atom.getFile().?]);
2317 const bind_sym_name = self.getSymbolName(global);
2318 const bind_sym = self.getSymbol(global);
2319 if (!bind_sym.undf()) continue;
2320
2321 const base_offset = @intCast(i32, sym.n_value - segment.vmaddr);
2322 const rel_offset = rel.r_address - base_rel_offset;
2323 const offset = @intCast(u64, base_offset + rel_offset);
2324
2325 const dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER);
2326 var flags: u4 = 0;
2327 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
2328 base_offset,
2329 bind_sym_name,
2330 dylib_ordinal,
2331 });
2332 if (bind_sym.weakRef()) {
2333 log.debug(" | marking as weak ref ", .{});
2334 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
2335 }
2336 try pointers.append(.{
2337 .offset = offset,
2338 .segment_id = segment_index,
2339 .dylib_ordinal = dylib_ordinal,
2340 .name = bind_sym_name,
2341 .bind_flags = flags,
2342 });
2343 }
12582344 }
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 => {},
2345 if (atom.next_index) |next_index| {
2346 atom_index = next_index;
2347 } else break;
2348 }
12722349 }
12732350 }
12742351
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,
2352 fn collectLazyBindData(self: *Zld, pointers: *std.ArrayList(bind.Pointer)) !void {
2353 const sect_id = self.getSectionByName("__DATA", "__la_symbol_ptr") orelse return;
2354
2355 log.debug("collecting lazy bind data", .{});
2356
2357 const slice = self.sections.slice();
2358 const segment_index = slice.items(.segment_index)[sect_id];
2359 const seg = self.getSegment(sect_id);
2360 var atom_index = slice.items(.first_atom_index)[sect_id];
2361
2362 // TODO: we actually don't need to store lazy pointer atoms as they are synthetically generated by the linker
2363 try pointers.ensureUnusedCapacity(self.stubs.items.len);
2364
2365 var count: u32 = 0;
2366 while (true) : (count += 1) {
2367 const atom = self.getAtom(atom_index);
2368
2369 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, self.getSymbolName(atom.getSymbolWithLoc()) });
2370
2371 const sym = self.getSymbol(atom.getSymbolWithLoc());
2372 const base_offset = sym.n_value - seg.vmaddr;
2373
2374 const stub_entry = self.stubs.items[count];
2375 const bind_sym = stub_entry.getTargetSymbol(self);
2376 const bind_sym_name = stub_entry.getTargetSymbolName(self);
2377 const dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER);
2378 var flags: u4 = 0;
2379 log.debug(" | lazy bind at {x}, import('{s}') in dylib({d})", .{
2380 base_offset,
2381 bind_sym_name,
2382 dylib_ordinal,
2383 });
2384 if (bind_sym.weakRef()) {
2385 log.debug(" | marking as weak ref ", .{});
2386 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
2387 }
2388 pointers.appendAssumeCapacity(.{
2389 .offset = base_offset,
2390 .segment_id = segment_index,
2391 .dylib_ordinal = dylib_ordinal,
2392 .name = bind_sym_name,
2393 .bind_flags = flags,
2394 });
2395
2396 if (atom.next_index) |next_index| {
2397 atom_index = next_index;
2398 } else break;
2399 }
2400 }
2401
2402 fn collectExportData(self: *Zld, trie: *Trie) !void {
2403 const gpa = self.gpa;
2404
2405 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
2406 log.debug("collecting export data", .{});
2407
2408 const segment_index = self.getSegmentByName("__TEXT").?;
2409 const exec_segment = self.segments.items[segment_index];
2410 const base_address = exec_segment.vmaddr;
2411
2412 if (self.options.output_mode == .Exe) {
2413 for (&[_]SymbolWithLoc{
2414 self.getEntryPoint(),
2415 self.globals.items[self.mh_execute_header_index.?],
2416 }) |global| {
2417 const sym = self.getSymbol(global);
2418 const sym_name = self.getSymbolName(global);
2419 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
2420 try trie.put(gpa, .{
2421 .name = sym_name,
2422 .vmaddr_offset = sym.n_value - base_address,
2423 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
2424 });
2425 }
2426 } else {
2427 assert(self.options.output_mode == .Lib);
2428 for (self.globals.items) |global| {
2429 const sym = self.getSymbol(global);
2430 if (sym.undf()) continue;
2431 if (sym.n_desc == N_DEAD) continue;
2432
2433 const sym_name = self.getSymbolName(global);
2434 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
2435 try trie.put(gpa, .{
2436 .name = sym_name,
2437 .vmaddr_offset = sym.n_value - base_address,
2438 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
2439 });
2440 }
2441 }
2442
2443 try trie.finalize(gpa);
2444 }
2445
2446 fn writeDyldInfoData(self: *Zld, ncmds: *u32, lc_writer: anytype, reverse_lookups: [][]u32) !void {
2447 const gpa = self.gpa;
2448
2449 var rebase_pointers = std.ArrayList(bind.Pointer).init(gpa);
2450 defer rebase_pointers.deinit();
2451 try self.collectRebaseData(&rebase_pointers);
2452
2453 var bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
2454 defer bind_pointers.deinit();
2455 try self.collectBindData(&bind_pointers, reverse_lookups);
2456
2457 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
2458 defer lazy_bind_pointers.deinit();
2459 try self.collectLazyBindData(&lazy_bind_pointers);
2460
2461 var trie = Trie{};
2462 defer trie.deinit(gpa);
2463 try self.collectExportData(&trie);
2464
2465 const link_seg = self.getLinkeditSegmentPtr();
2466 const rebase_off = mem.alignForwardGeneric(u64, link_seg.fileoff, @alignOf(u64));
2467 assert(rebase_off == link_seg.fileoff);
2468 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);
2469 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size });
2470
2471 const bind_off = mem.alignForwardGeneric(u64, rebase_off + rebase_size, @alignOf(u64));
2472 const bind_size = try bind.bindInfoSize(bind_pointers.items);
2473 log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size });
2474
2475 const lazy_bind_off = mem.alignForwardGeneric(u64, bind_off + bind_size, @alignOf(u64));
2476 const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);
2477 log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{ lazy_bind_off, lazy_bind_off + lazy_bind_size });
2478
2479 const export_off = mem.alignForwardGeneric(u64, lazy_bind_off + lazy_bind_size, @alignOf(u64));
2480 const export_size = trie.size;
2481 log.debug("writing export trie from 0x{x} to 0x{x}", .{ export_off, export_off + export_size });
2482
2483 const needed_size = math.cast(usize, export_off + export_size - rebase_off) orelse return error.Overflow;
2484 link_seg.filesize = needed_size;
2485
2486 var buffer = try gpa.alloc(u8, needed_size);
2487 defer gpa.free(buffer);
2488 mem.set(u8, buffer, 0);
2489
2490 var stream = std.io.fixedBufferStream(buffer);
2491 const writer = stream.writer();
2492
2493 try bind.writeRebaseInfo(rebase_pointers.items, writer);
2494 try stream.seekTo(bind_off - rebase_off);
2495
2496 try bind.writeBindInfo(bind_pointers.items, writer);
2497 try stream.seekTo(lazy_bind_off - rebase_off);
2498
2499 try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer);
2500 try stream.seekTo(export_off - rebase_off);
2501
2502 _ = try trie.write(writer);
2503
2504 log.debug("writing dyld info from 0x{x} to 0x{x}", .{
2505 rebase_off,
2506 rebase_off + needed_size,
2507 });
2508
2509 try self.file.pwriteAll(buffer, rebase_off);
2510
2511 const offset = math.cast(usize, lazy_bind_off - rebase_off) orelse return error.Overflow;
2512 const size = math.cast(usize, lazy_bind_size) orelse return error.Overflow;
2513 try self.populateLazyBindOffsetsInStubHelper(buffer[offset..][0..size]);
2514
2515 try lc_writer.writeStruct(macho.dyld_info_command{
2516 .cmd = .DYLD_INFO_ONLY,
2517 .cmdsize = @sizeOf(macho.dyld_info_command),
2518 .rebase_off = @intCast(u32, rebase_off),
2519 .rebase_size = @intCast(u32, rebase_size),
2520 .bind_off = @intCast(u32, bind_off),
2521 .bind_size = @intCast(u32, bind_size),
2522 .weak_bind_off = 0,
2523 .weak_bind_size = 0,
2524 .lazy_bind_off = @intCast(u32, lazy_bind_off),
2525 .lazy_bind_size = @intCast(u32, lazy_bind_size),
2526 .export_off = @intCast(u32, export_off),
2527 .export_size = @intCast(u32, export_size),
12932528 });
1294 try macho_file.base.file.?.pwriteAll(&buf, file_offset);
2529 ncmds.* += 1;
12952530 }
1296}
12972531
1298const asc_u64 = std.sort.asc(u64);
2532 fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void {
2533 const gpa = self.gpa;
12992534
1300fn writeFunctionStarts(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1301 const tracy = trace(@src());
1302 defer tracy.end();
2535 const stub_helper_section_index = self.getSectionByName("__TEXT", "__stub_helper") orelse return;
2536 const la_symbol_ptr_section_index = self.getSectionByName("__DATA", "__la_symbol_ptr") orelse return;
13032537
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];
2538 if (self.stub_helper_preamble_sym_index == null) return;
13072539
1308 const gpa = macho_file.base.allocator;
2540 const section = self.sections.get(stub_helper_section_index);
2541 const last_atom_index = section.last_atom_index;
2542
2543 var table = std.AutoHashMap(i64, AtomIndex).init(gpa);
2544 defer table.deinit();
2545
2546 {
2547 var stub_atom_index = last_atom_index;
2548 var laptr_atom_index = self.sections.items(.last_atom_index)[la_symbol_ptr_section_index];
2549
2550 const base_addr = blk: {
2551 const segment_index = self.getSegmentByName("__DATA").?;
2552 const seg = self.segments.items[segment_index];
2553 break :blk seg.vmaddr;
2554 };
2555
2556 while (true) {
2557 const stub_atom = self.getAtom(stub_atom_index);
2558 const laptr_atom = self.getAtom(laptr_atom_index);
2559 const laptr_off = blk: {
2560 const sym = self.getSymbolPtr(laptr_atom.getSymbolWithLoc());
2561 break :blk @intCast(i64, sym.n_value - base_addr);
2562 };
2563
2564 try table.putNoClobber(laptr_off, stub_atom_index);
2565
2566 if (laptr_atom.prev_index) |prev_index| {
2567 laptr_atom_index = prev_index;
2568 stub_atom_index = stub_atom.prev_index.?;
2569 } else break;
2570 }
2571 }
2572
2573 var stream = std.io.fixedBufferStream(buffer);
2574 var reader = stream.reader();
2575 var offsets = std.ArrayList(struct { sym_offset: i64, offset: u32 }).init(gpa);
2576 try offsets.append(.{ .sym_offset = undefined, .offset = 0 });
2577 defer offsets.deinit();
2578 var valid_block = false;
2579
2580 while (true) {
2581 const inst = reader.readByte() catch |err| switch (err) {
2582 error.EndOfStream => break,
2583 };
2584 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
2585
2586 switch (opcode) {
2587 macho.BIND_OPCODE_DO_BIND => {
2588 valid_block = true;
2589 },
2590 macho.BIND_OPCODE_DONE => {
2591 if (valid_block) {
2592 const offset = try stream.getPos();
2593 try offsets.append(.{ .sym_offset = undefined, .offset = @intCast(u32, offset) });
2594 }
2595 valid_block = false;
2596 },
2597 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
2598 var next = try reader.readByte();
2599 while (next != @as(u8, 0)) {
2600 next = try reader.readByte();
2601 }
2602 },
2603 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
2604 var inserted = offsets.pop();
2605 inserted.sym_offset = try std.leb.readILEB128(i64, reader);
2606 try offsets.append(inserted);
2607 },
2608 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
2609 _ = try std.leb.readULEB128(u64, reader);
2610 },
2611 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
2612 _ = try std.leb.readILEB128(i64, reader);
2613 },
2614 else => {},
2615 }
2616 }
2617
2618 const header = self.sections.items(.header)[stub_helper_section_index];
2619 const stub_offset: u4 = switch (self.options.target.cpu.arch) {
2620 .x86_64 => 1,
2621 .aarch64 => 2 * @sizeOf(u32),
2622 else => unreachable,
2623 };
2624 var buf: [@sizeOf(u32)]u8 = undefined;
2625 _ = offsets.pop();
2626
2627 while (offsets.popOrNull()) |bind_offset| {
2628 const atom_index = table.get(bind_offset.sym_offset).?;
2629 const atom = self.getAtom(atom_index);
2630 const sym = self.getSymbol(atom.getSymbolWithLoc());
2631
2632 const file_offset = header.offset + sym.n_value - header.addr + stub_offset;
2633 mem.writeIntLittle(u32, &buf, bind_offset.offset);
2634
2635 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{
2636 bind_offset.offset,
2637 self.getSymbolName(atom.getSymbolWithLoc()),
2638 file_offset,
2639 });
2640
2641 try self.file.pwriteAll(&buf, file_offset);
2642 }
2643 }
2644
2645 const asc_u64 = std.sort.asc(u64);
2646
2647 fn writeFunctionStarts(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
2648 const text_seg_index = self.getSegmentByName("__TEXT") orelse return;
2649 const text_sect_index = self.getSectionByName("__TEXT", "__text") orelse return;
2650 const text_seg = self.segments.items[text_seg_index];
2651
2652 const gpa = self.gpa;
2653
2654 // We need to sort by address first
2655 var addresses = std.ArrayList(u64).init(gpa);
2656 defer addresses.deinit();
2657 try addresses.ensureTotalCapacityPrecise(self.globals.items.len);
2658
2659 for (self.globals.items) |global| {
2660 const sym = self.getSymbol(global);
2661 if (sym.undf()) continue;
2662 if (sym.n_desc == N_DEAD) continue;
2663
2664 const sect_id = sym.n_sect - 1;
2665 if (sect_id != text_sect_index) continue;
2666
2667 addresses.appendAssumeCapacity(sym.n_value);
2668 }
2669
2670 std.sort.sort(u64, addresses.items, {}, asc_u64);
2671
2672 var offsets = std.ArrayList(u32).init(gpa);
2673 defer offsets.deinit();
2674 try offsets.ensureTotalCapacityPrecise(addresses.items.len);
2675
2676 var last_off: u32 = 0;
2677 for (addresses.items) |addr| {
2678 const offset = @intCast(u32, addr - text_seg.vmaddr);
2679 const diff = offset - last_off;
2680
2681 if (diff == 0) continue;
2682
2683 offsets.appendAssumeCapacity(diff);
2684 last_off = offset;
2685 }
2686
2687 var buffer = std.ArrayList(u8).init(gpa);
2688 defer buffer.deinit();
2689
2690 const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64));
2691 try buffer.ensureTotalCapacity(max_size);
2692
2693 for (offsets.items) |offset| {
2694 try std.leb.writeULEB128(buffer.writer(), offset);
2695 }
2696
2697 const link_seg = self.getLinkeditSegmentPtr();
2698 const offset = mem.alignForwardGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64));
2699 const needed_size = buffer.items.len;
2700 link_seg.filesize = offset + needed_size - link_seg.fileoff;
2701
2702 log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
2703
2704 try self.file.pwriteAll(buffer.items, offset);
13092705
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);
2706 try lc_writer.writeStruct(macho.linkedit_data_command{
2707 .cmd = .FUNCTION_STARTS,
2708 .cmdsize = @sizeOf(macho.linkedit_data_command),
2709 .dataoff = @intCast(u32, offset),
2710 .datasize = @intCast(u32, needed_size),
2711 });
2712 ncmds.* += 1;
2713 }
2714
2715 fn filterDataInCode(
2716 dices: []const macho.data_in_code_entry,
2717 start_addr: u64,
2718 end_addr: u64,
2719 ) []const macho.data_in_code_entry {
2720 const Predicate = struct {
2721 addr: u64,
2722
2723 pub fn predicate(self: @This(), dice: macho.data_in_code_entry) bool {
2724 return dice.offset >= self.addr;
2725 }
2726 };
2727
2728 const start = lsearch(macho.data_in_code_entry, dices, Predicate{ .addr = start_addr });
2729 const end = lsearch(macho.data_in_code_entry, dices[start..], Predicate{ .addr = end_addr }) + start;
2730
2731 return dices[start..end];
2732 }
2733
2734 fn writeDataInCode(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
2735 var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.gpa);
2736 defer out_dice.deinit();
2737
2738 const text_sect_id = self.getSectionByName("__TEXT", "__text") orelse return;
2739 const text_sect_header = self.sections.items(.header)[text_sect_id];
2740
2741 for (self.objects.items) |object| {
2742 const dice = object.parseDataInCode() orelse continue;
2743 try out_dice.ensureUnusedCapacity(dice.len);
2744
2745 for (object.atoms.items) |atom_index| {
2746 const atom = self.getAtom(atom_index);
2747 const sym = self.getSymbol(atom.getSymbolWithLoc());
2748 const sect_id = sym.n_sect - 1;
2749 if (sect_id != text_sect_id) {
2750 continue;
2751 }
2752
2753 const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
2754 source_sym.n_value
2755 else blk: {
2756 const nbase = @intCast(u32, object.in_symtab.?.len);
2757 const source_sect_id = @intCast(u16, atom.sym_index - nbase);
2758 break :blk object.getSourceSection(source_sect_id).addr;
2759 };
2760 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);
2761 const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse
2762 return error.Overflow;
2763
2764 for (filtered_dice) |single| {
2765 const offset = math.cast(u32, single.offset - source_addr + base) orelse
2766 return error.Overflow;
2767 out_dice.appendAssumeCapacity(.{
2768 .offset = offset,
2769 .length = single.length,
2770 .kind = single.kind,
2771 });
2772 }
2773 }
2774 }
2775
2776 const seg = self.getLinkeditSegmentPtr();
2777 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
2778 const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
2779 seg.filesize = offset + needed_size - seg.fileoff;
2780
2781 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
2782
2783 try self.file.pwriteAll(mem.sliceAsBytes(out_dice.items), offset);
2784 try lc_writer.writeStruct(macho.linkedit_data_command{
2785 .cmd = .DATA_IN_CODE,
2786 .cmdsize = @sizeOf(macho.linkedit_data_command),
2787 .dataoff = @intCast(u32, offset),
2788 .datasize = @intCast(u32, needed_size),
2789 });
2790 ncmds.* += 1;
2791 }
2792
2793 fn writeSymtabs(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
2794 var symtab_cmd = macho.symtab_command{
2795 .cmdsize = @sizeOf(macho.symtab_command),
2796 .symoff = 0,
2797 .nsyms = 0,
2798 .stroff = 0,
2799 .strsize = 0,
2800 };
2801 var dysymtab_cmd = macho.dysymtab_command{
2802 .cmdsize = @sizeOf(macho.dysymtab_command),
2803 .ilocalsym = 0,
2804 .nlocalsym = 0,
2805 .iextdefsym = 0,
2806 .nextdefsym = 0,
2807 .iundefsym = 0,
2808 .nundefsym = 0,
2809 .tocoff = 0,
2810 .ntoc = 0,
2811 .modtaboff = 0,
2812 .nmodtab = 0,
2813 .extrefsymoff = 0,
2814 .nextrefsyms = 0,
2815 .indirectsymoff = 0,
2816 .nindirectsyms = 0,
2817 .extreloff = 0,
2818 .nextrel = 0,
2819 .locreloff = 0,
2820 .nlocrel = 0,
2821 };
2822 var ctx = try self.writeSymtab(&symtab_cmd);
2823 defer ctx.imports_table.deinit();
2824 try self.writeDysymtab(ctx, &dysymtab_cmd);
2825 try self.writeStrtab(&symtab_cmd);
2826 try lc_writer.writeStruct(symtab_cmd);
2827 try lc_writer.writeStruct(dysymtab_cmd);
2828 ncmds.* += 2;
2829 }
2830
2831 fn writeSymtab(self: *Zld, lc: *macho.symtab_command) !SymtabCtx {
2832 const gpa = self.gpa;
2833
2834 var locals = std.ArrayList(macho.nlist_64).init(gpa);
2835 defer locals.deinit();
2836
2837 for (self.objects.items) |object| {
2838 for (object.atoms.items) |atom_index| {
2839 const atom = self.getAtom(atom_index);
2840 const sym_loc = atom.getSymbolWithLoc();
2841 const sym = self.getSymbol(sym_loc);
2842 if (sym.n_strx == 0) continue; // no name, skip
2843 if (sym.ext()) continue; // an export lands in its own symtab section, skip
2844 if (self.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
13142845
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;
2846 var out_sym = sym;
2847 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(sym_loc));
2848 try locals.append(out_sym);
2849 }
2850 }
2851
2852 if (!self.options.strip) {
2853 for (self.objects.items) |object| {
2854 try self.generateSymbolStabs(object, &locals);
2855 }
2856 }
2857
2858 var exports = std.ArrayList(macho.nlist_64).init(gpa);
2859 defer exports.deinit();
2860
2861 for (self.globals.items) |global| {
2862 const sym = self.getSymbol(global);
2863 if (sym.undf()) continue; // import, skip
2864 if (sym.n_desc == N_DEAD) continue;
2865
2866 var out_sym = sym;
2867 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
2868 try exports.append(out_sym);
2869 }
2870
2871 var imports = std.ArrayList(macho.nlist_64).init(gpa);
2872 defer imports.deinit();
2873
2874 var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);
2875
2876 for (self.globals.items) |global| {
2877 const sym = self.getSymbol(global);
2878 if (!sym.undf()) continue; // not an import, skip
2879 if (sym.n_desc == N_DEAD) continue;
2880
2881 const new_index = @intCast(u32, imports.items.len);
2882 var out_sym = sym;
2883 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
2884 try imports.append(out_sym);
2885 try imports_table.putNoClobber(global, new_index);
2886 }
13212887
1322 addresses.appendAssumeCapacity(sym.n_value);
2888 const nlocals = @intCast(u32, locals.items.len);
2889 const nexports = @intCast(u32, exports.items.len);
2890 const nimports = @intCast(u32, imports.items.len);
2891 const nsyms = nlocals + nexports + nimports;
2892
2893 const seg = self.getLinkeditSegmentPtr();
2894 const offset = mem.alignForwardGeneric(
2895 u64,
2896 seg.fileoff + seg.filesize,
2897 @alignOf(macho.nlist_64),
2898 );
2899 const needed_size = nsyms * @sizeOf(macho.nlist_64);
2900 seg.filesize = offset + needed_size - seg.fileoff;
2901
2902 var buffer = std.ArrayList(u8).init(gpa);
2903 defer buffer.deinit();
2904 try buffer.ensureTotalCapacityPrecise(needed_size);
2905 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items));
2906 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items));
2907 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(imports.items));
2908
2909 log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
2910 try self.file.pwriteAll(buffer.items, offset);
2911
2912 lc.symoff = @intCast(u32, offset);
2913 lc.nsyms = nsyms;
2914
2915 return SymtabCtx{
2916 .nlocalsym = nlocals,
2917 .nextdefsym = nexports,
2918 .nundefsym = nimports,
2919 .imports_table = imports_table,
2920 };
2921 }
2922
2923 fn writeStrtab(self: *Zld, lc: *macho.symtab_command) !void {
2924 const seg = self.getLinkeditSegmentPtr();
2925 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
2926 const needed_size = self.strtab.buffer.items.len;
2927 seg.filesize = offset + needed_size - seg.fileoff;
2928
2929 log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
2930
2931 try self.file.pwriteAll(self.strtab.buffer.items, offset);
2932
2933 lc.stroff = @intCast(u32, offset);
2934 lc.strsize = @intCast(u32, needed_size);
13232935 }
13242936
1325 std.sort.sort(u64, addresses.items, {}, asc_u64);
2937 const SymtabCtx = struct {
2938 nlocalsym: u32,
2939 nextdefsym: u32,
2940 nundefsym: u32,
2941 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
2942 };
2943
2944 fn writeDysymtab(self: *Zld, ctx: SymtabCtx, lc: *macho.dysymtab_command) !void {
2945 const gpa = self.gpa;
2946 const nstubs = @intCast(u32, self.stubs.items.len);
2947 const ngot_entries = @intCast(u32, self.got_entries.items.len);
2948 const nindirectsyms = nstubs * 2 + ngot_entries;
2949 const iextdefsym = ctx.nlocalsym;
2950 const iundefsym = iextdefsym + ctx.nextdefsym;
2951
2952 const seg = self.getLinkeditSegmentPtr();
2953 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
2954 const needed_size = nindirectsyms * @sizeOf(u32);
2955 seg.filesize = offset + needed_size - seg.fileoff;
2956
2957 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
2958
2959 var buf = std.ArrayList(u8).init(gpa);
2960 defer buf.deinit();
2961 try buf.ensureTotalCapacity(needed_size);
2962 const writer = buf.writer();
2963
2964 if (self.getSectionByName("__TEXT", "__stubs")) |sect_id| {
2965 const stubs = &self.sections.items(.header)[sect_id];
2966 stubs.reserved1 = 0;
2967 for (self.stubs.items) |entry| {
2968 const target_sym = entry.getTargetSymbol(self);
2969 assert(target_sym.undf());
2970 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
2971 }
2972 }
13262973
1327 var offsets = std.ArrayList(u32).init(gpa);
1328 defer offsets.deinit();
1329 try offsets.ensureTotalCapacityPrecise(addresses.items.len);
2974 if (self.getSectionByName("__DATA_CONST", "__got")) |sect_id| {
2975 const got = &self.sections.items(.header)[sect_id];
2976 got.reserved1 = nstubs;
2977 for (self.got_entries.items) |entry| {
2978 const target_sym = entry.getTargetSymbol(self);
2979 if (target_sym.undf()) {
2980 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
2981 } else {
2982 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
2983 }
2984 }
2985 }
2986
2987 if (self.getSectionByName("__DATA", "__la_symbol_ptr")) |sect_id| {
2988 const la_symbol_ptr = &self.sections.items(.header)[sect_id];
2989 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
2990 for (self.stubs.items) |entry| {
2991 const target_sym = entry.getTargetSymbol(self);
2992 assert(target_sym.undf());
2993 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
2994 }
2995 }
2996
2997 assert(buf.items.len == needed_size);
2998 try self.file.pwriteAll(buf.items, offset);
2999
3000 lc.nlocalsym = ctx.nlocalsym;
3001 lc.iextdefsym = iextdefsym;
3002 lc.nextdefsym = ctx.nextdefsym;
3003 lc.iundefsym = iundefsym;
3004 lc.nundefsym = ctx.nundefsym;
3005 lc.indirectsymoff = @intCast(u32, offset);
3006 lc.nindirectsyms = nindirectsyms;
3007 }
3008
3009 fn writeCodeSignaturePadding(
3010 self: *Zld,
3011 code_sig: *CodeSignature,
3012 ncmds: *u32,
3013 lc_writer: anytype,
3014 ) !u32 {
3015 const seg = self.getLinkeditSegmentPtr();
3016 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
3017 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
3018 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, 16);
3019 const needed_size = code_sig.estimateSize(offset);
3020 seg.filesize = offset + needed_size - seg.fileoff;
3021 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
3022 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
3023 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
3024 // except for code signature data.
3025 try self.file.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
3026
3027 try lc_writer.writeStruct(macho.linkedit_data_command{
3028 .cmd = .CODE_SIGNATURE,
3029 .cmdsize = @sizeOf(macho.linkedit_data_command),
3030 .dataoff = @intCast(u32, offset),
3031 .datasize = @intCast(u32, needed_size),
3032 });
3033 ncmds.* += 1;
3034
3035 return @intCast(u32, offset);
3036 }
3037
3038 fn writeCodeSignature(self: *Zld, code_sig: *CodeSignature, offset: u32) !void {
3039 const seg_id = self.getSegmentByName("__TEXT").?;
3040 const seg = self.segments.items[seg_id];
3041
3042 var buffer = std.ArrayList(u8).init(self.gpa);
3043 defer buffer.deinit();
3044 try buffer.ensureTotalCapacityPrecise(code_sig.size());
3045 try code_sig.writeAdhocSignature(self.gpa, .{
3046 .file = self.file,
3047 .exec_seg_base = seg.fileoff,
3048 .exec_seg_limit = seg.filesize,
3049 .file_size = offset,
3050 .output_mode = self.options.output_mode,
3051 }, buffer.writer());
3052 assert(buffer.items.len == code_sig.size());
3053
3054 log.debug("writing code signature from 0x{x} to 0x{x}", .{
3055 offset,
3056 offset + buffer.items.len,
3057 });
3058
3059 try self.file.pwriteAll(buffer.items, offset);
3060 }
3061
3062 /// Writes Mach-O file header.
3063 fn writeHeader(self: *Zld, ncmds: u32, sizeofcmds: u32) !void {
3064 var header: macho.mach_header_64 = .{};
3065 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
3066
3067 switch (self.options.target.cpu.arch) {
3068 .aarch64 => {
3069 header.cputype = macho.CPU_TYPE_ARM64;
3070 header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL;
3071 },
3072 .x86_64 => {
3073 header.cputype = macho.CPU_TYPE_X86_64;
3074 header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL;
3075 },
3076 else => return error.UnsupportedCpuArchitecture,
3077 }
3078
3079 switch (self.options.output_mode) {
3080 .Exe => {
3081 header.filetype = macho.MH_EXECUTE;
3082 },
3083 .Lib => {
3084 // By this point, it can only be a dylib.
3085 header.filetype = macho.MH_DYLIB;
3086 header.flags |= macho.MH_NO_REEXPORTED_DYLIBS;
3087 },
3088 else => unreachable,
3089 }
3090
3091 if (self.getSectionByName("__DATA", "__thread_vars")) |sect_id| {
3092 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
3093 if (self.sections.items(.header)[sect_id].size > 0) {
3094 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
3095 }
3096 }
3097
3098 header.ncmds = ncmds;
3099 header.sizeofcmds = sizeofcmds;
3100
3101 log.debug("writing Mach-O header {}", .{header});
3102
3103 try self.file.pwriteAll(mem.asBytes(&header), 0);
3104 }
3105
3106 pub fn makeStaticString(bytes: []const u8) [16]u8 {
3107 var buf = [_]u8{0} ** 16;
3108 assert(bytes.len <= buf.len);
3109 mem.copy(u8, &buf, bytes);
3110 return buf;
3111 }
3112
3113 pub inline fn getAtomPtr(self: *Zld, atom_index: AtomIndex) *Atom {
3114 assert(atom_index < self.atoms.items.len);
3115 return &self.atoms.items[atom_index];
3116 }
3117
3118 pub inline fn getAtom(self: Zld, atom_index: AtomIndex) Atom {
3119 assert(atom_index < self.atoms.items.len);
3120 return self.atoms.items[atom_index];
3121 }
3122
3123 fn getSegmentByName(self: Zld, segname: []const u8) ?u8 {
3124 for (self.segments.items) |seg, i| {
3125 if (mem.eql(u8, segname, seg.segName())) return @intCast(u8, i);
3126 } else return null;
3127 }
3128
3129 pub inline fn getSegment(self: Zld, sect_id: u8) macho.segment_command_64 {
3130 const index = self.sections.items(.segment_index)[sect_id];
3131 return self.segments.items[index];
3132 }
3133
3134 pub inline fn getSegmentPtr(self: *Zld, sect_id: u8) *macho.segment_command_64 {
3135 const index = self.sections.items(.segment_index)[sect_id];
3136 return &self.segments.items[index];
3137 }
3138
3139 pub inline fn getLinkeditSegmentPtr(self: *Zld) *macho.segment_command_64 {
3140 assert(self.segments.items.len > 0);
3141 const seg = &self.segments.items[self.segments.items.len - 1];
3142 assert(mem.eql(u8, seg.segName(), "__LINKEDIT"));
3143 return seg;
3144 }
3145
3146 pub fn getSectionByName(self: Zld, segname: []const u8, sectname: []const u8) ?u8 {
3147 // TODO investigate caching with a hashmap
3148 for (self.sections.items(.header)) |header, i| {
3149 if (mem.eql(u8, header.segName(), segname) and mem.eql(u8, header.sectName(), sectname))
3150 return @intCast(u8, i);
3151 } else return null;
3152 }
3153
3154 pub fn getSectionIndexes(self: Zld, segment_index: u8) struct { start: u8, end: u8 } {
3155 var start: u8 = 0;
3156 const nsects = for (self.segments.items) |seg, i| {
3157 if (i == segment_index) break @intCast(u8, seg.nsects);
3158 start += @intCast(u8, seg.nsects);
3159 } else 0;
3160 return .{ .start = start, .end = start + nsects };
3161 }
3162
3163 pub fn symbolIsTemp(self: *Zld, sym_with_loc: SymbolWithLoc) bool {
3164 const sym = self.getSymbol(sym_with_loc);
3165 if (!sym.sect()) return false;
3166 if (sym.ext()) return false;
3167 const sym_name = self.getSymbolName(sym_with_loc);
3168 return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L");
3169 }
3170
3171 /// Returns pointer-to-symbol described by `sym_with_loc` descriptor.
3172 pub fn getSymbolPtr(self: *Zld, sym_with_loc: SymbolWithLoc) *macho.nlist_64 {
3173 if (sym_with_loc.getFile()) |file| {
3174 const object = &self.objects.items[file];
3175 return &object.symtab[sym_with_loc.sym_index];
3176 } else {
3177 return &self.locals.items[sym_with_loc.sym_index];
3178 }
3179 }
3180
3181 /// Returns symbol described by `sym_with_loc` descriptor.
3182 pub fn getSymbol(self: *Zld, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
3183 return self.getSymbolPtr(sym_with_loc).*;
3184 }
3185
3186 /// Returns name of the symbol described by `sym_with_loc` descriptor.
3187 pub fn getSymbolName(self: *Zld, sym_with_loc: SymbolWithLoc) []const u8 {
3188 if (sym_with_loc.getFile()) |file| {
3189 const object = self.objects.items[file];
3190 return object.getSymbolName(sym_with_loc.sym_index);
3191 } else {
3192 const sym = self.locals.items[sym_with_loc.sym_index];
3193 return self.strtab.get(sym.n_strx).?;
3194 }
3195 }
3196
3197 /// Returns GOT atom that references `sym_with_loc` if one exists.
3198 /// Returns null otherwise.
3199 pub fn getGotAtomIndexForSymbol(self: *Zld, sym_with_loc: SymbolWithLoc) ?AtomIndex {
3200 const index = self.got_table.get(sym_with_loc) orelse return null;
3201 const entry = self.got_entries.items[index];
3202 return entry.atom_index;
3203 }
3204
3205 /// Returns stubs atom that references `sym_with_loc` if one exists.
3206 /// Returns null otherwise.
3207 pub fn getStubsAtomIndexForSymbol(self: *Zld, sym_with_loc: SymbolWithLoc) ?AtomIndex {
3208 const index = self.stubs_table.get(sym_with_loc) orelse return null;
3209 const entry = self.stubs.items[index];
3210 return entry.atom_index;
3211 }
3212
3213 /// Returns TLV pointer atom that references `sym_with_loc` if one exists.
3214 /// Returns null otherwise.
3215 pub fn getTlvPtrAtomIndexForSymbol(self: *Zld, sym_with_loc: SymbolWithLoc) ?AtomIndex {
3216 const index = self.tlv_ptr_table.get(sym_with_loc) orelse return null;
3217 const entry = self.tlv_ptr_entries.items[index];
3218 return entry.atom_index;
3219 }
3220
3221 /// Returns symbol location corresponding to the set entrypoint.
3222 /// Asserts output mode is executable.
3223 pub fn getEntryPoint(self: Zld) SymbolWithLoc {
3224 assert(self.options.output_mode == .Exe);
3225 const global_index = self.entry_index.?;
3226 return self.globals.items[global_index];
3227 }
3228
3229 inline fn requiresThunks(self: Zld) bool {
3230 return self.options.target.cpu.arch == .aarch64;
3231 }
3232
3233 pub fn generateSymbolStabs(self: *Zld, object: Object, locals: *std.ArrayList(macho.nlist_64)) !void {
3234 log.debug("generating stabs for '{s}'", .{object.name});
3235
3236 const gpa = self.gpa;
3237 var debug_info = object.parseDwarfInfo();
3238
3239 var lookup = DwarfInfo.AbbrevLookupTable.init(gpa);
3240 defer lookup.deinit();
3241 try lookup.ensureUnusedCapacity(std.math.maxInt(u8));
3242
3243 // We assume there is only one CU.
3244 var cu_it = debug_info.getCompileUnitIterator();
3245 const compile_unit = while (try cu_it.next()) |cu| {
3246 const offset = math.cast(usize, cu.cuh.debug_abbrev_offset) orelse return error.Overflow;
3247 try debug_info.genAbbrevLookupByKind(offset, &lookup);
3248 break cu;
3249 } else {
3250 log.debug("no compile unit found in debug info in {s}; skipping", .{object.name});
3251 return;
3252 };
3253
3254 var abbrev_it = compile_unit.getAbbrevEntryIterator(debug_info);
3255 const cu_entry: DwarfInfo.AbbrevEntry = while (try abbrev_it.next(lookup)) |entry| switch (entry.tag) {
3256 dwarf.TAG.compile_unit => break entry,
3257 else => continue,
3258 } else {
3259 log.debug("missing DWARF_TAG_compile_unit tag in {s}; skipping", .{object.name});
3260 return;
3261 };
3262
3263 var maybe_tu_name: ?[]const u8 = null;
3264 var maybe_tu_comp_dir: ?[]const u8 = null;
3265 var attr_it = cu_entry.getAttributeIterator(debug_info, compile_unit.cuh);
3266
3267 while (try attr_it.next()) |attr| switch (attr.name) {
3268 dwarf.AT.comp_dir => maybe_tu_comp_dir = attr.getString(debug_info, compile_unit.cuh) orelse continue,
3269 dwarf.AT.name => maybe_tu_name = attr.getString(debug_info, compile_unit.cuh) orelse continue,
3270 else => continue,
3271 };
3272
3273 if (maybe_tu_name == null or maybe_tu_comp_dir == null) {
3274 log.debug("missing DWARF_AT_comp_dir and DWARF_AT_name attributes {s}; skipping", .{object.name});
3275 return;
3276 }
3277
3278 const tu_name = maybe_tu_name.?;
3279 const tu_comp_dir = maybe_tu_comp_dir.?;
3280
3281 // Open scope
3282 try locals.ensureUnusedCapacity(3);
3283 locals.appendAssumeCapacity(.{
3284 .n_strx = try self.strtab.insert(gpa, tu_comp_dir),
3285 .n_type = macho.N_SO,
3286 .n_sect = 0,
3287 .n_desc = 0,
3288 .n_value = 0,
3289 });
3290 locals.appendAssumeCapacity(.{
3291 .n_strx = try self.strtab.insert(gpa, tu_name),
3292 .n_type = macho.N_SO,
3293 .n_sect = 0,
3294 .n_desc = 0,
3295 .n_value = 0,
3296 });
3297 locals.appendAssumeCapacity(.{
3298 .n_strx = try self.strtab.insert(gpa, object.name),
3299 .n_type = macho.N_OSO,
3300 .n_sect = 0,
3301 .n_desc = 1,
3302 .n_value = object.mtime,
3303 });
3304
3305 var stabs_buf: [4]macho.nlist_64 = undefined;
3306
3307 var name_lookup: ?DwarfInfo.SubprogramLookupByName = if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS == 0) blk: {
3308 var name_lookup = DwarfInfo.SubprogramLookupByName.init(gpa);
3309 errdefer name_lookup.deinit();
3310 try name_lookup.ensureUnusedCapacity(@intCast(u32, object.atoms.items.len));
3311 try debug_info.genSubprogramLookupByName(compile_unit, lookup, &name_lookup);
3312 break :blk name_lookup;
3313 } else null;
3314 defer if (name_lookup) |*nl| nl.deinit();
3315
3316 for (object.atoms.items) |atom_index| {
3317 const atom = self.getAtom(atom_index);
3318 const stabs = try self.generateSymbolStabsForSymbol(
3319 atom_index,
3320 atom.getSymbolWithLoc(),
3321 name_lookup,
3322 &stabs_buf,
3323 );
3324 try locals.appendSlice(stabs);
3325
3326 var it = Atom.getInnerSymbolsIterator(self, atom_index);
3327 while (it.next()) |sym_loc| {
3328 const contained_stabs = try self.generateSymbolStabsForSymbol(
3329 atom_index,
3330 sym_loc,
3331 name_lookup,
3332 &stabs_buf,
3333 );
3334 try locals.appendSlice(contained_stabs);
3335 }
3336 }
3337
3338 // Close scope
3339 try locals.append(.{
3340 .n_strx = 0,
3341 .n_type = macho.N_SO,
3342 .n_sect = 0,
3343 .n_desc = 0,
3344 .n_value = 0,
3345 });
3346 }
3347
3348 fn generateSymbolStabsForSymbol(
3349 self: *Zld,
3350 atom_index: AtomIndex,
3351 sym_loc: SymbolWithLoc,
3352 lookup: ?DwarfInfo.SubprogramLookupByName,
3353 buf: *[4]macho.nlist_64,
3354 ) ![]const macho.nlist_64 {
3355 const gpa = self.gpa;
3356 const object = self.objects.items[sym_loc.getFile().?];
3357 const sym = self.getSymbol(sym_loc);
3358 const sym_name = self.getSymbolName(sym_loc);
3359 const header = self.sections.items(.header)[sym.n_sect - 1];
3360
3361 if (sym.n_strx == 0) return buf[0..0];
3362 if (self.symbolIsTemp(sym_loc)) return buf[0..0];
3363
3364 if (!header.isCode()) {
3365 // Since we are not dealing with machine code, it's either a global or a static depending
3366 // on the linkage scope.
3367 if (sym.sect() and sym.ext()) {
3368 // Global gets an N_GSYM stab type.
3369 buf[0] = .{
3370 .n_strx = try self.strtab.insert(gpa, sym_name),
3371 .n_type = macho.N_GSYM,
3372 .n_sect = sym.n_sect,
3373 .n_desc = 0,
3374 .n_value = 0,
3375 };
3376 } else {
3377 // Local static gets an N_STSYM stab type.
3378 buf[0] = .{
3379 .n_strx = try self.strtab.insert(gpa, sym_name),
3380 .n_type = macho.N_STSYM,
3381 .n_sect = sym.n_sect,
3382 .n_desc = 0,
3383 .n_value = sym.n_value,
3384 };
3385 }
3386 return buf[0..1];
3387 }
3388
3389 const size: u64 = size: {
3390 if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) {
3391 break :size self.getAtom(atom_index).size;
3392 }
3393
3394 // Since we don't have subsections to work with, we need to infer the size of each function
3395 // the slow way by scanning the debug info for matching symbol names and extracting
3396 // the symbol's DWARF_AT_low_pc and DWARF_AT_high_pc values.
3397 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
3398 const subprogram = lookup.?.get(sym_name[1..]) orelse return buf[0..0];
3399
3400 if (subprogram.addr <= source_sym.n_value and source_sym.n_value < subprogram.addr + subprogram.size) {
3401 break :size subprogram.size;
3402 } else {
3403 log.debug("no stab found for {s}", .{sym_name});
3404 return buf[0..0];
3405 }
3406 };
3407
3408 buf[0] = .{
3409 .n_strx = 0,
3410 .n_type = macho.N_BNSYM,
3411 .n_sect = sym.n_sect,
3412 .n_desc = 0,
3413 .n_value = sym.n_value,
3414 };
3415 buf[1] = .{
3416 .n_strx = try self.strtab.insert(gpa, sym_name),
3417 .n_type = macho.N_FUN,
3418 .n_sect = sym.n_sect,
3419 .n_desc = 0,
3420 .n_value = sym.n_value,
3421 };
3422 buf[2] = .{
3423 .n_strx = 0,
3424 .n_type = macho.N_FUN,
3425 .n_sect = 0,
3426 .n_desc = 0,
3427 .n_value = size,
3428 };
3429 buf[3] = .{
3430 .n_strx = 0,
3431 .n_type = macho.N_ENSYM,
3432 .n_sect = sym.n_sect,
3433 .n_desc = 0,
3434 .n_value = size,
3435 };
3436
3437 return buf;
3438 }
3439
3440 fn logSegments(self: *Zld) void {
3441 log.debug("segments:", .{});
3442 for (self.segments.items) |segment, i| {
3443 log.debug(" segment({d}): {s} @{x} ({x}), sizeof({x})", .{
3444 i,
3445 segment.segName(),
3446 segment.fileoff,
3447 segment.vmaddr,
3448 segment.vmsize,
3449 });
3450 }
3451 }
3452
3453 fn logSections(self: *Zld) void {
3454 log.debug("sections:", .{});
3455 for (self.sections.items(.header)) |header, i| {
3456 log.debug(" sect({d}): {s},{s} @{x} ({x}), sizeof({x})", .{
3457 i + 1,
3458 header.segName(),
3459 header.sectName(),
3460 header.offset,
3461 header.addr,
3462 header.size,
3463 });
3464 }
3465 }
3466
3467 fn logSymAttributes(sym: macho.nlist_64, buf: []u8) []const u8 {
3468 if (sym.sect()) {
3469 buf[0] = 's';
3470 }
3471 if (sym.ext()) {
3472 if (sym.weakDef() or sym.pext()) {
3473 buf[1] = 'w';
3474 } else {
3475 buf[1] = 'e';
3476 }
3477 }
3478 if (sym.tentative()) {
3479 buf[2] = 't';
3480 }
3481 if (sym.undf()) {
3482 buf[3] = 'u';
3483 }
3484 return buf[0..];
3485 }
3486
3487 fn logSymtab(self: *Zld) void {
3488 var buf: [4]u8 = undefined;
3489
3490 const scoped_log = std.log.scoped(.symtab);
3491
3492 scoped_log.debug("locals:", .{});
3493 for (self.objects.items) |object, id| {
3494 scoped_log.debug(" object({d}): {s}", .{ id, object.name });
3495 if (object.in_symtab == null) continue;
3496 for (object.symtab) |sym, sym_id| {
3497 mem.set(u8, &buf, '_');
3498 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{
3499 sym_id,
3500 object.getSymbolName(@intCast(u32, sym_id)),
3501 sym.n_value,
3502 sym.n_sect,
3503 logSymAttributes(sym, &buf),
3504 });
3505 }
3506 }
3507 scoped_log.debug(" object(-1)", .{});
3508 for (self.locals.items) |sym, sym_id| {
3509 if (sym.undf()) continue;
3510 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{
3511 sym_id,
3512 self.strtab.get(sym.n_strx).?,
3513 sym.n_value,
3514 sym.n_sect,
3515 logSymAttributes(sym, &buf),
3516 });
3517 }
3518
3519 scoped_log.debug("exports:", .{});
3520 for (self.globals.items) |global, i| {
3521 const sym = self.getSymbol(global);
3522 if (sym.undf()) continue;
3523 if (sym.n_desc == N_DEAD) continue;
3524 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s} (def in object({?}))", .{
3525 i,
3526 self.getSymbolName(global),
3527 sym.n_value,
3528 sym.n_sect,
3529 logSymAttributes(sym, &buf),
3530 global.file,
3531 });
3532 }
3533
3534 scoped_log.debug("imports:", .{});
3535 for (self.globals.items) |global, i| {
3536 const sym = self.getSymbol(global);
3537 if (!sym.undf()) continue;
3538 if (sym.n_desc == N_DEAD) continue;
3539 const ord = @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER);
3540 scoped_log.debug(" %{d}: {s} @{x} in ord({d}), {s}", .{
3541 i,
3542 self.getSymbolName(global),
3543 sym.n_value,
3544 ord,
3545 logSymAttributes(sym, &buf),
3546 });
3547 }
3548
3549 scoped_log.debug("GOT entries:", .{});
3550 for (self.got_entries.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 if (target_sym.undf()) {
3555 scoped_log.debug(" {d}@{x} => import('{s}')", .{
3556 i,
3557 atom_sym.n_value,
3558 target_sym_name,
3559 });
3560 } else {
3561 scoped_log.debug(" {d}@{x} => local(%{d}) in object({?}) {s}", .{
3562 i,
3563 atom_sym.n_value,
3564 entry.target.sym_index,
3565 entry.target.file,
3566 logSymAttributes(target_sym, buf[0..4]),
3567 });
3568 }
3569 }
3570
3571 scoped_log.debug("__thread_ptrs entries:", .{});
3572 for (self.tlv_ptr_entries.items) |entry, i| {
3573 const atom_sym = entry.getAtomSymbol(self);
3574 const target_sym = entry.getTargetSymbol(self);
3575 const target_sym_name = entry.getTargetSymbolName(self);
3576 assert(target_sym.undf());
3577 scoped_log.debug(" {d}@{x} => import('{s}')", .{
3578 i,
3579 atom_sym.n_value,
3580 target_sym_name,
3581 });
3582 }
3583
3584 scoped_log.debug("stubs entries:", .{});
3585 for (self.stubs.items) |entry, i| {
3586 const atom_sym = entry.getAtomSymbol(self);
3587 const target_sym = entry.getTargetSymbol(self);
3588 const target_sym_name = entry.getTargetSymbolName(self);
3589 assert(target_sym.undf());
3590 scoped_log.debug(" {d}@{x} => import('{s}')", .{
3591 i,
3592 atom_sym.n_value,
3593 target_sym_name,
3594 });
3595 }
3596
3597 scoped_log.debug("thunks:", .{});
3598 for (self.thunks.items) |thunk, i| {
3599 scoped_log.debug(" thunk({d})", .{i});
3600 for (thunk.lookup.keys()) |target, j| {
3601 const target_sym = self.getSymbol(target);
3602 const atom = self.getAtom(thunk.lookup.get(target).?);
3603 const atom_sym = self.getSymbol(atom.getSymbolWithLoc());
3604 scoped_log.debug(" {d}@{x} => thunk('{s}'@{x})", .{
3605 j,
3606 atom_sym.n_value,
3607 self.getSymbolName(target),
3608 target_sym.n_value,
3609 });
3610 }
3611 }
3612 }
3613
3614 fn logAtoms(self: *Zld) void {
3615 log.debug("atoms:", .{});
3616 const slice = self.sections.slice();
3617 for (slice.items(.first_atom_index)) |first_atom_index, sect_id| {
3618 var atom_index = first_atom_index;
3619 const header = slice.items(.header)[sect_id];
3620
3621 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
3622
3623 while (true) {
3624 const atom = self.getAtom(atom_index);
3625 self.logAtom(atom_index, log);
3626
3627 if (atom.next_index) |next_index| {
3628 atom_index = next_index;
3629 } else break;
3630 }
3631 }
3632 }
3633
3634 pub fn logAtom(self: *Zld, atom_index: AtomIndex, logger: anytype) void {
3635 if (!build_options.enable_logging) return;
3636
3637 const atom = self.getAtom(atom_index);
3638 const sym = self.getSymbol(atom.getSymbolWithLoc());
3639 const sym_name = self.getSymbolName(atom.getSymbolWithLoc());
3640 logger.debug(" ATOM({d}, %{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?}) in sect({d})", .{
3641 atom_index,
3642 atom.sym_index,
3643 sym_name,
3644 sym.n_value,
3645 atom.size,
3646 atom.alignment,
3647 atom.file,
3648 sym.n_sect,
3649 });
3650
3651 if (atom.getFile() != null) {
3652 var it = Atom.getInnerSymbolsIterator(self, atom_index);
3653 while (it.next()) |sym_loc| {
3654 const inner = self.getSymbol(sym_loc);
3655 const inner_name = self.getSymbolName(sym_loc);
3656 const offset = Atom.calcInnerSymbolOffset(self, atom_index, sym_loc.sym_index);
3657
3658 logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{
3659 sym_loc.sym_index,
3660 inner_name,
3661 inner.n_value,
3662 offset,
3663 });
3664 }
3665
3666 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
3667 const alias = self.getSymbol(sym_loc);
3668 const alias_name = self.getSymbolName(sym_loc);
3669
3670 logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{
3671 sym_loc.sym_index,
3672 alias_name,
3673 alias.n_value,
3674 0,
3675 });
3676 }
3677 }
3678 }
3679};
3680
3681pub const N_DEAD: u16 = @bitCast(u16, @as(i16, -1));
3682
3683const Section = struct {
3684 header: macho.section_64,
3685 segment_index: u8,
3686 first_atom_index: AtomIndex,
3687 last_atom_index: AtomIndex,
3688};
3689
3690pub const AtomIndex = u32;
3691
3692const IndirectPointer = struct {
3693 target: SymbolWithLoc,
3694 atom_index: AtomIndex,
3695
3696 pub fn getTargetSymbol(self: @This(), zld: *Zld) macho.nlist_64 {
3697 return zld.getSymbol(self.target);
3698 }
3699
3700 pub fn getTargetSymbolName(self: @This(), zld: *Zld) []const u8 {
3701 return zld.getSymbolName(self.target);
3702 }
3703
3704 pub fn getAtomSymbol(self: @This(), zld: *Zld) macho.nlist_64 {
3705 const atom = zld.getAtom(self.atom_index);
3706 return zld.getSymbol(atom.getSymbolWithLoc());
3707 }
3708};
3709
3710pub const SymbolWithLoc = struct {
3711 // Index into the respective symbol table.
3712 sym_index: u32,
3713
3714 // -1 means it's a synthetic global.
3715 file: i32 = -1,
3716
3717 pub inline fn getFile(self: SymbolWithLoc) ?u31 {
3718 if (self.file == -1) return null;
3719 return @intCast(u31, self.file);
3720 }
3721
3722 pub inline fn eql(self: SymbolWithLoc, other: SymbolWithLoc) bool {
3723 return self.file == other.file and self.sym_index == other.sym_index;
3724 }
3725};
3726
3727const SymbolResolver = struct {
3728 arena: Allocator,
3729 table: std.StringHashMap(u32),
3730 unresolved: std.AutoArrayHashMap(u32, void),
3731};
3732
3733pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {
3734 const tracy = trace(@src());
3735 defer tracy.end();
3736
3737 const gpa = macho_file.base.allocator;
3738 const options = macho_file.base.options;
3739 const target = options.target;
3740
3741 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
3742 defer arena_allocator.deinit();
3743 const arena = arena_allocator.allocator();
3744
3745 const directory = options.emit.?.directory; // Just an alias to make it shorter to type.
3746 const full_out_path = try directory.join(arena, &[_][]const u8{options.emit.?.sub_path});
3747
3748 // If there is no Zig code to compile, then we should skip flushing the output file because it
3749 // will not be part of the linker line anyway.
3750 const module_obj_path: ?[]const u8 = if (options.module) |module| blk: {
3751 if (options.use_stage1) {
3752 const obj_basename = try std.zig.binNameAlloc(arena, .{
3753 .root_name = options.root_name,
3754 .target = target,
3755 .output_mode = .Obj,
3756 });
3757 switch (options.cache_mode) {
3758 .incremental => break :blk try module.zig_cache_artifact_directory.join(
3759 arena,
3760 &[_][]const u8{obj_basename},
3761 ),
3762 .whole => break :blk try fs.path.join(arena, &.{
3763 fs.path.dirname(full_out_path).?, obj_basename,
3764 }),
3765 }
3766 }
3767
3768 try macho_file.flushModule(comp, prog_node);
3769
3770 if (fs.path.dirname(full_out_path)) |dirname| {
3771 break :blk try fs.path.join(arena, &.{ dirname, macho_file.base.intermediary_basename.? });
3772 } else {
3773 break :blk macho_file.base.intermediary_basename.?;
3774 }
3775 } else null;
3776
3777 var sub_prog_node = prog_node.start("MachO Flush", 0);
3778 sub_prog_node.activate();
3779 sub_prog_node.context.refresh();
3780 defer sub_prog_node.end();
3781
3782 const cpu_arch = target.cpu.arch;
3783 const os_tag = target.os.tag;
3784 const abi = target.abi;
3785 const is_lib = options.output_mode == .Lib;
3786 const is_dyn_lib = options.link_mode == .Dynamic and is_lib;
3787 const is_exe_or_dyn_lib = is_dyn_lib or options.output_mode == .Exe;
3788 const stack_size = options.stack_size_override orelse 0;
3789 const is_debug_build = options.optimize_mode == .Debug;
3790 const gc_sections = options.gc_sections orelse !is_debug_build;
3791
3792 const id_symlink_basename = "zld.id";
3793
3794 var man: Cache.Manifest = undefined;
3795 defer if (!options.disable_lld_caching) man.deinit();
3796
3797 var digest: [Cache.hex_digest_len]u8 = undefined;
3798
3799 if (!options.disable_lld_caching) {
3800 man = comp.cache_parent.obtain();
3801
3802 // We are about to obtain this lock, so here we give other processes a chance first.
3803 macho_file.base.releaseLock();
3804
3805 comptime assert(Compilation.link_hash_implementation_version == 7);
3806
3807 for (options.objects) |obj| {
3808 _ = try man.addFile(obj.path, null);
3809 man.hash.add(obj.must_link);
3810 }
3811 for (comp.c_object_table.keys()) |key| {
3812 _ = try man.addFile(key.status.success.object_path, null);
3813 }
3814 try man.addOptionalFile(module_obj_path);
3815 // We can skip hashing libc and libc++ components that we are in charge of building from Zig
3816 // installation sources because they are always a product of the compiler version + target information.
3817 man.hash.add(stack_size);
3818 man.hash.addOptional(options.pagezero_size);
3819 man.hash.addOptional(options.search_strategy);
3820 man.hash.addOptional(options.headerpad_size);
3821 man.hash.add(options.headerpad_max_install_names);
3822 man.hash.add(gc_sections);
3823 man.hash.add(options.dead_strip_dylibs);
3824 man.hash.add(options.strip);
3825 man.hash.addListOfBytes(options.lib_dirs);
3826 man.hash.addListOfBytes(options.framework_dirs);
3827 link.hashAddSystemLibs(&man.hash, options.frameworks);
3828 man.hash.addListOfBytes(options.rpath_list);
3829 if (is_dyn_lib) {
3830 man.hash.addOptionalBytes(options.install_name);
3831 man.hash.addOptional(options.version);
3832 }
3833 link.hashAddSystemLibs(&man.hash, options.system_libs);
3834 man.hash.addOptionalBytes(options.sysroot);
3835 try man.addOptionalFile(options.entitlements);
3836
3837 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
3838 _ = try man.hit();
3839 digest = man.final();
3840
3841 var prev_digest_buf: [digest.len]u8 = undefined;
3842 const prev_digest: []u8 = Cache.readSmallFile(
3843 directory.handle,
3844 id_symlink_basename,
3845 &prev_digest_buf,
3846 ) catch |err| blk: {
3847 log.debug("MachO Zld new_digest={s} error: {s}", .{
3848 std.fmt.fmtSliceHexLower(&digest),
3849 @errorName(err),
3850 });
3851 // Handle this as a cache miss.
3852 break :blk prev_digest_buf[0..0];
3853 };
3854 if (mem.eql(u8, prev_digest, &digest)) {
3855 // Hot diggity dog! The output binary is already there.
3856 log.debug("MachO Zld digest={s} match - skipping invocation", .{
3857 std.fmt.fmtSliceHexLower(&digest),
3858 });
3859 macho_file.base.lock = man.toOwnedLock();
3860 return;
3861 }
3862 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{
3863 std.fmt.fmtSliceHexLower(prev_digest),
3864 std.fmt.fmtSliceHexLower(&digest),
3865 });
3866
3867 // We are about to change the output file to be different, so we invalidate the build hash now.
3868 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
3869 error.FileNotFound => {},
3870 else => |e| return e,
3871 };
3872 }
3873
3874 if (options.output_mode == .Obj) {
3875 // LLD's MachO driver does not support the equivalent of `-r` so we do a simple file copy
3876 // here. TODO: think carefully about how we can avoid this redundant operation when doing
3877 // build-obj. See also the corresponding TODO in linkAsArchive.
3878 const the_object_path = blk: {
3879 if (options.objects.len != 0) {
3880 break :blk options.objects[0].path;
3881 }
3882
3883 if (comp.c_object_table.count() != 0)
3884 break :blk comp.c_object_table.keys()[0].status.success.object_path;
3885
3886 if (module_obj_path) |p|
3887 break :blk p;
3888
3889 // TODO I think this is unreachable. Audit this situation when solving the above TODO
3890 // regarding eliding redundant object -> object transformations.
3891 return error.NoObjectsToLink;
3892 };
3893 // This can happen when using --enable-cache and using the stage1 backend. In this case
3894 // we can skip the file copy.
3895 if (!mem.eql(u8, the_object_path, full_out_path)) {
3896 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
3897 }
3898 } else {
3899 const page_size = macho_file.page_size;
3900 const sub_path = options.emit.?.sub_path;
3901 if (macho_file.base.file == null) {
3902 macho_file.base.file = try directory.handle.createFile(sub_path, .{
3903 .truncate = true,
3904 .read = true,
3905 .mode = link.determineMode(options),
3906 });
3907 }
3908 var zld = Zld{
3909 .gpa = gpa,
3910 .file = macho_file.base.file.?,
3911 .page_size = macho_file.page_size,
3912 .options = options,
3913 };
3914 defer zld.deinit();
3915
3916 try zld.atoms.append(gpa, Atom.empty); // AtomIndex at 0 is reserved as null atom
3917 try zld.strtab.buffer.append(gpa, 0);
3918
3919 var lib_not_found = false;
3920 var framework_not_found = false;
3921
3922 // Positional arguments to the linker such as object files and static archives.
3923 var positionals = std.ArrayList([]const u8).init(arena);
3924 try positionals.ensureUnusedCapacity(options.objects.len);
3925
3926 var must_link_archives = std.StringArrayHashMap(void).init(arena);
3927 try must_link_archives.ensureUnusedCapacity(options.objects.len);
3928
3929 for (options.objects) |obj| {
3930 if (must_link_archives.contains(obj.path)) continue;
3931 if (obj.must_link) {
3932 _ = must_link_archives.getOrPutAssumeCapacity(obj.path);
3933 } else {
3934 _ = positionals.appendAssumeCapacity(obj.path);
3935 }
3936 }
3937
3938 for (comp.c_object_table.keys()) |key| {
3939 try positionals.append(key.status.success.object_path);
3940 }
3941
3942 if (module_obj_path) |p| {
3943 try positionals.append(p);
3944 }
3945
3946 if (comp.compiler_rt_lib) |lib| {
3947 try positionals.append(lib.full_object_path);
3948 }
3949
3950 // libc++ dep
3951 if (options.link_libcpp) {
3952 try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
3953 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
3954 }
3955
3956 // Shared and static libraries passed via `-l` flag.
3957 var candidate_libs = std.StringArrayHashMap(link.SystemLib).init(arena);
3958
3959 const system_lib_names = options.system_libs.keys();
3960 for (system_lib_names) |system_lib_name| {
3961 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
3962 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
3963 // case we want to avoid prepending "-l".
3964 if (Compilation.classifyFileExt(system_lib_name) == .shared_library) {
3965 try positionals.append(system_lib_name);
3966 continue;
3967 }
3968
3969 const system_lib_info = options.system_libs.get(system_lib_name).?;
3970 try candidate_libs.put(system_lib_name, .{
3971 .needed = system_lib_info.needed,
3972 .weak = system_lib_info.weak,
3973 });
3974 }
3975
3976 var lib_dirs = std.ArrayList([]const u8).init(arena);
3977 for (options.lib_dirs) |dir| {
3978 if (try MachO.resolveSearchDir(arena, dir, options.sysroot)) |search_dir| {
3979 try lib_dirs.append(search_dir);
3980 } else {
3981 log.warn("directory not found for '-L{s}'", .{dir});
3982 }
3983 }
3984
3985 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
3986
3987 // Assume ld64 default -search_paths_first if no strategy specified.
3988 const search_strategy = options.search_strategy orelse .paths_first;
3989 outer: for (candidate_libs.keys()) |lib_name| {
3990 switch (search_strategy) {
3991 .paths_first => {
3992 // Look in each directory for a dylib (stub first), and then for archive
3993 for (lib_dirs.items) |dir| {
3994 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
3995 if (try MachO.resolveLib(arena, dir, lib_name, ext)) |full_path| {
3996 try libs.put(full_path, candidate_libs.get(lib_name).?);
3997 continue :outer;
3998 }
3999 }
4000 } else {
4001 log.warn("library not found for '-l{s}'", .{lib_name});
4002 lib_not_found = true;
4003 }
4004 },
4005 .dylibs_first => {
4006 // First, look for a dylib in each search dir
4007 for (lib_dirs.items) |dir| {
4008 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
4009 if (try MachO.resolveLib(arena, dir, lib_name, ext)) |full_path| {
4010 try libs.put(full_path, candidate_libs.get(lib_name).?);
4011 continue :outer;
4012 }
4013 }
4014 } else for (lib_dirs.items) |dir| {
4015 if (try MachO.resolveLib(arena, dir, lib_name, ".a")) |full_path| {
4016 try libs.put(full_path, candidate_libs.get(lib_name).?);
4017 } else {
4018 log.warn("library not found for '-l{s}'", .{lib_name});
4019 lib_not_found = true;
4020 }
4021 }
4022 },
4023 }
4024 }
4025
4026 if (lib_not_found) {
4027 log.warn("Library search paths:", .{});
4028 for (lib_dirs.items) |dir| {
4029 log.warn(" {s}", .{dir});
4030 }
4031 }
4032
4033 try MachO.resolveLibSystem(arena, comp, options.sysroot, target, lib_dirs.items, &libs);
4034
4035 // frameworks
4036 var framework_dirs = std.ArrayList([]const u8).init(arena);
4037 for (options.framework_dirs) |dir| {
4038 if (try MachO.resolveSearchDir(arena, dir, options.sysroot)) |search_dir| {
4039 try framework_dirs.append(search_dir);
4040 } else {
4041 log.warn("directory not found for '-F{s}'", .{dir});
4042 }
4043 }
4044
4045 outer: for (options.frameworks.keys()) |f_name| {
4046 for (framework_dirs.items) |dir| {
4047 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
4048 if (try MachO.resolveFramework(arena, dir, f_name, ext)) |full_path| {
4049 const info = options.frameworks.get(f_name).?;
4050 try libs.put(full_path, .{
4051 .needed = info.needed,
4052 .weak = info.weak,
4053 });
4054 continue :outer;
4055 }
4056 }
4057 } else {
4058 log.warn("framework not found for '-framework {s}'", .{f_name});
4059 framework_not_found = true;
4060 }
4061 }
4062
4063 if (framework_not_found) {
4064 log.warn("Framework search paths:", .{});
4065 for (framework_dirs.items) |dir| {
4066 log.warn(" {s}", .{dir});
4067 }
4068 }
4069
4070 if (options.verbose_link) {
4071 var argv = std.ArrayList([]const u8).init(arena);
4072
4073 try argv.append("zig");
4074 try argv.append("ld");
4075
4076 if (is_exe_or_dyn_lib) {
4077 try argv.append("-dynamic");
4078 }
4079
4080 if (is_dyn_lib) {
4081 try argv.append("-dylib");
4082
4083 if (options.install_name) |install_name| {
4084 try argv.append("-install_name");
4085 try argv.append(install_name);
4086 }
4087 }
13304088
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;
4089 if (options.sysroot) |syslibroot| {
4090 try argv.append("-syslibroot");
4091 try argv.append(syslibroot);
4092 }
13354093
1336 if (diff == 0) continue;
4094 for (options.rpath_list) |rpath| {
4095 try argv.append("-rpath");
4096 try argv.append(rpath);
4097 }
13374098
1338 offsets.appendAssumeCapacity(diff);
1339 last_off = offset;
1340 }
4099 if (options.pagezero_size) |pagezero_size| {
4100 try argv.append("-pagezero_size");
4101 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
4102 }
13414103
1342 var buffer = std.ArrayList(u8).init(gpa);
1343 defer buffer.deinit();
4104 if (options.search_strategy) |strat| switch (strat) {
4105 .paths_first => try argv.append("-search_paths_first"),
4106 .dylibs_first => try argv.append("-search_dylibs_first"),
4107 };
13444108
1345 const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64));
1346 try buffer.ensureTotalCapacity(max_size);
4109 if (options.headerpad_size) |headerpad_size| {
4110 try argv.append("-headerpad_size");
4111 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));
4112 }
13474113
1348 for (offsets.items) |offset| {
1349 try std.leb.writeULEB128(buffer.writer(), offset);
1350 }
4114 if (options.headerpad_max_install_names) {
4115 try argv.append("-headerpad_max_install_names");
4116 }
13514117
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;
4118 if (gc_sections) {
4119 try argv.append("-dead_strip");
4120 }
13564121
1357 log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
4122 if (options.dead_strip_dylibs) {
4123 try argv.append("-dead_strip_dylibs");
4124 }
13584125
1359 try macho_file.base.file.?.pwriteAll(buffer.items, offset);
4126 if (options.entry) |entry| {
4127 try argv.append("-e");
4128 try argv.append(entry);
4129 }
13604130
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}
4131 for (options.objects) |obj| {
4132 try argv.append(obj.path);
4133 }
13694134
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,
4135 for (comp.c_object_table.keys()) |key| {
4136 try argv.append(key.status.success.object_path);
4137 }
13774138
1378 pub fn predicate(macho_file: @This(), dice: macho.data_in_code_entry) bool {
1379 return dice.offset >= macho_file.addr;
1380 }
1381 };
4139 if (module_obj_path) |p| {
4140 try argv.append(p);
4141 }
13824142
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 });
4143 if (comp.compiler_rt_lib) |lib| {
4144 try argv.append(lib.full_object_path);
4145 }
13854146
1386 return dices[start..end];
1387}
4147 if (options.link_libcpp) {
4148 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
4149 try argv.append(comp.libcxx_static_lib.?.full_object_path);
4150 }
13884151
1389fn writeDataInCode(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1390 const tracy = trace(@src());
1391 defer tracy.end();
4152 try argv.append("-o");
4153 try argv.append(full_out_path);
13924154
1393 var out_dice = std.ArrayList(macho.data_in_code_entry).init(macho_file.base.allocator);
1394 defer out_dice.deinit();
4155 try argv.append("-lSystem");
4156 try argv.append("-lc");
13954157
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];
4158 for (options.system_libs.keys()) |l_name| {
4159 const info = options.system_libs.get(l_name).?;
4160 const arg = if (info.needed)
4161 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})
4162 else if (info.weak)
4163 try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})
4164 else
4165 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
4166 try argv.append(arg);
4167 }
13984168
1399 for (macho_file.objects.items) |object| {
1400 const dice = object.parseDataInCode() orelse continue;
1401 try out_dice.ensureUnusedCapacity(dice.len);
4169 for (options.lib_dirs) |lib_dir| {
4170 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
4171 }
14024172
1403 for (object.managed_atoms.items) |atom| {
1404 const sym = atom.getSymbol(macho_file);
1405 if (sym.n_desc == MachO.N_DESC_GCED) continue;
4173 for (options.frameworks.keys()) |framework| {
4174 const info = options.frameworks.get(framework).?;
4175 const arg = if (info.needed)
4176 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework})
4177 else if (info.weak)
4178 try std.fmt.allocPrint(arena, "-weak_framework {s}", .{framework})
4179 else
4180 try std.fmt.allocPrint(arena, "-framework {s}", .{framework});
4181 try argv.append(arg);
4182 }
14064183
1407 const sect_id = sym.n_sect - 1;
1408 if (sect_id != macho_file.text_section_index.?) {
1409 continue;
4184 for (options.framework_dirs) |framework_dir| {
4185 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
14104186 }
14114187
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;
4188 if (is_dyn_lib and (options.allow_shlib_undefined orelse false)) {
4189 try argv.append("-undefined");
4190 try argv.append("dynamic_lookup");
4191 }
14174192
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 });
4193 for (must_link_archives.keys()) |lib| {
4194 try argv.append(try std.fmt.allocPrint(arena, "-force_load {s}", .{lib}));
14254195 }
4196
4197 Compilation.dump_argv(argv.items);
14264198 }
1427 }
14284199
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;
4200 var dependent_libs = std.fifo.LinearFifo(struct {
4201 id: Dylib.Id,
4202 parent: u16,
4203 }, .Dynamic).init(arena);
14334204
1434 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
4205 try zld.parseInputFiles(positionals.items, options.sysroot, &dependent_libs);
4206 try zld.parseAndForceLoadStaticArchives(must_link_archives.keys());
4207 try zld.parseLibs(libs.keys(), libs.values(), options.sysroot, &dependent_libs);
4208 try zld.parseDependentLibs(options.sysroot, &dependent_libs);
14354209
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}
4210 var resolver = SymbolResolver{
4211 .arena = arena,
4212 .table = std.StringHashMap(u32).init(arena),
4213 .unresolved = std.AutoArrayHashMap(u32, void).init(arena),
4214 };
14454215
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}
4216 for (zld.objects.items) |_, object_id| {
4217 try zld.resolveSymbolsInObject(@intCast(u16, object_id), &resolver);
4218 }
14834219
1484fn writeSymtab(macho_file: *MachO, lc: *macho.symtab_command) !SymtabCtx {
1485 const gpa = macho_file.base.allocator;
4220 try zld.resolveSymbolsInArchives(&resolver);
4221 try zld.resolveDyldStubBinder(&resolver);
4222 try zld.resolveSymbolsInDylibs(&resolver);
4223 try zld.createMhExecuteHeaderSymbol(&resolver);
4224 try zld.createDsoHandleSymbol(&resolver);
4225 try zld.resolveSymbolsAtLoading(&resolver);
14864226
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);
4227 if (resolver.unresolved.count() > 0) {
4228 return error.UndefinedSymbolReference;
15094229 }
1510
1511 if (!macho_file.base.options.strip) {
1512 try generateSymbolStabs(macho_file, object, &locals);
4230 if (lib_not_found) {
4231 return error.LibraryNotFound;
4232 }
4233 if (framework_not_found) {
4234 return error.FrameworkNotFound;
15134235 }
1514 }
1515
1516 var exports = std.ArrayList(macho.nlist_64).init(gpa);
1517 defer exports.deinit();
15184236
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 }
4237 if (options.output_mode == .Exe) {
4238 const entry_name = options.entry orelse "_main";
4239 const global_index = resolver.table.get(entry_name) orelse {
4240 log.err("entrypoint '{s}' not found", .{entry_name});
4241 return error.MissingMainEntrypoint;
4242 };
4243 zld.entry_index = global_index;
4244 }
15274245
1528 var imports = std.ArrayList(macho.nlist_64).init(gpa);
1529 defer imports.deinit();
4246 for (zld.objects.items) |*object, object_id| {
4247 try object.splitIntoAtoms(&zld, @intCast(u31, object_id));
4248 }
15304249
1531 var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);
4250 var reverse_lookups: [][]u32 = try arena.alloc([]u32, zld.objects.items.len);
4251 for (zld.objects.items) |object, i| {
4252 reverse_lookups[i] = try object.createReverseSymbolLookup(arena);
4253 }
15324254
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 }
4255 if (gc_sections) {
4256 try dead_strip.gcAtoms(&zld, reverse_lookups);
4257 }
15434258
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;
4259 try zld.createDyldPrivateAtom();
4260 try zld.createTentativeDefAtoms();
4261 try zld.createStubHelperPreambleAtom();
15484262
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;
4263 for (zld.objects.items) |object| {
4264 for (object.atoms.items) |atom_index| {
4265 const atom = zld.getAtom(atom_index);
4266 const sym = zld.getSymbol(atom.getSymbolWithLoc());
4267 const header = zld.sections.items(.header)[sym.n_sect - 1];
4268 if (header.isZerofill()) continue;
15574269
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));
4270 const relocs = Atom.getAtomRelocs(&zld, atom_index);
4271 try Atom.scanAtomRelocs(&zld, atom_index, relocs, reverse_lookups[atom.getFile().?]);
4272 }
4273 }
15644274
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);
4275 try zld.createDyldStubBinderGotAtom();
15674276
1568 lc.symoff = @intCast(u32, offset);
1569 lc.nsyms = nsyms;
4277 try zld.calcSectionSizes(reverse_lookups);
4278 try zld.pruneAndSortSections();
4279 try zld.createSegments();
4280 try zld.allocateSegments();
15704281
1571 return SymtabCtx{
1572 .nlocalsym = nlocals,
1573 .nextdefsym = nexports,
1574 .nundefsym = nimports,
1575 .imports_table = imports_table,
1576 };
1577}
4282 try zld.allocateSpecialSymbols();
15784283
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;
4284 if (build_options.enable_logging) {
4285 zld.logSymtab();
4286 zld.logSegments();
4287 zld.logSections();
4288 zld.logAtoms();
4289 }
15844290
1585 log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
4291 try zld.writeAtoms(reverse_lookups);
15864292
1587 try macho_file.base.file.?.pwriteAll(macho_file.strtab.buffer.items, offset);
4293 var lc_buffer = std.ArrayList(u8).init(arena);
4294 const lc_writer = lc_buffer.writer();
4295 var ncmds: u32 = 0;
15884296
1589 lc.stroff = @intCast(u32, offset);
1590 lc.strsize = @intCast(u32, needed_size);
1591}
4297 try zld.writeLinkeditSegmentData(&ncmds, lc_writer, reverse_lookups);
15924298
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);
4299 // If the last section of __DATA segment is zerofill section, we need to ensure
4300 // that the free space between the end of the last non-zerofill section of __DATA
4301 // segment and the beginning of __LINKEDIT segment is zerofilled as the loader will
4302 // copy-paste this space into memory for quicker zerofill operation.
4303 if (zld.getSegmentByName("__DATA")) |data_seg_id| blk: {
4304 var physical_zerofill_start: u64 = 0;
4305 const section_indexes = zld.getSectionIndexes(data_seg_id);
4306 for (zld.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| {
4307 if (header.isZerofill() and header.size > 0) break;
4308 physical_zerofill_start = header.offset + header.size;
4309 } else break :blk;
4310 const linkedit = zld.getLinkeditSegmentPtr();
4311 const physical_zerofill_size = math.cast(usize, linkedit.fileoff - physical_zerofill_start) orelse
4312 return error.Overflow;
4313 if (physical_zerofill_size > 0) {
4314 log.debug("zeroing out zerofill area of length {x} at {x}", .{
4315 physical_zerofill_size,
4316 physical_zerofill_start,
4317 });
4318 var padding = try zld.gpa.alloc(u8, physical_zerofill_size);
4319 defer zld.gpa.free(padding);
4320 mem.set(u8, padding, 0);
4321 try zld.file.pwriteAll(padding, physical_zerofill_start);
4322 }
4323 }
15994324
1600 log.debug("parsing debug info in '{s}'", .{object.name});
4325 try Zld.writeDylinkerLC(&ncmds, lc_writer);
4326 try zld.writeMainLC(&ncmds, lc_writer);
4327 try zld.writeDylibIdLC(&ncmds, lc_writer);
4328 try zld.writeRpathLCs(&ncmds, lc_writer);
16014329
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 };
4330 {
4331 try lc_writer.writeStruct(macho.source_version_command{
4332 .cmdsize = @sizeOf(macho.source_version_command),
4333 .version = 0x0,
4334 });
4335 ncmds += 1;
4336 }
16164337
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);
4338 try zld.writeBuildVersionLC(&ncmds, lc_writer);
16544339
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,
4340 {
4341 var uuid_lc = macho.uuid_command{
4342 .cmdsize = @sizeOf(macho.uuid_command),
4343 .uuid = undefined,
16594344 };
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);
4345 std.crypto.random.bytes(&uuid_lc.uuid);
4346 try lc_writer.writeStruct(uuid_lc);
4347 ncmds += 1;
16674348 }
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}
16794349
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 };
4350 try zld.writeLoadDylibLCs(&ncmds, lc_writer);
17074351
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,
4352 const requires_codesig = blk: {
4353 if (options.entitlements) |_| break :blk true;
4354 if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true;
4355 break :blk false;
17454356 };
1746 return buf[0..1];
1747 }
1748}
4357 var codesig_offset: ?u32 = null;
4358 var codesig: ?CodeSignature = if (requires_codesig) blk: {
4359 // Preallocate space for the code signature.
4360 // We need to do this at this stage so that we have the load commands with proper values
4361 // written out to the file.
4362 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
4363 // where the code signature goes into.
4364 var codesig = CodeSignature.init(page_size);
4365 codesig.code_directory.ident = options.emit.?.sub_path;
4366 if (options.entitlements) |path| {
4367 try codesig.addEntitlements(gpa, path);
4368 }
4369 codesig_offset = try zld.writeCodeSignaturePadding(&codesig, &ncmds, lc_writer);
4370 break :blk codesig;
4371 } else null;
4372 defer if (codesig) |*csig| csig.deinit(gpa);
17494373
1750const SymtabCtx = struct {
1751 nlocalsym: u32,
1752 nextdefsym: u32,
1753 nundefsym: u32,
1754 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
1755};
4374 var headers_buf = std.ArrayList(u8).init(arena);
4375 try zld.writeSegmentHeaders(&ncmds, headers_buf.writer());
17564376
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 }
4377 try zld.file.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
4378 try zld.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
4379 try zld.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
17894380
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 }
4381 if (codesig) |*csig| {
4382 try zld.writeCodeSignature(csig, codesig_offset.?); // code signing always comes last
18034383 }
18044384 }
18054385
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 }
4386 if (!options.disable_lld_caching) {
4387 // Update the file with the digest. If it fails we can continue; it only
4388 // means that the next invocation will have an unnecessary cache miss.
4389 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
4390 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});
4391 };
4392 // Again failure here only means an unnecessary cache miss.
4393 man.writeManifest() catch |err| {
4394 log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)});
4395 };
4396 // We hang on to this lock so that the output file path can be used without
4397 // other processes clobbering it.
4398 macho_file.base.lock = man.toOwnedLock();
18174399 }
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);
18814400}
18824401
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);
4402/// Binary search
4403pub fn bsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {
4404 if (!@hasDecl(@TypeOf(predicate), "predicate"))
4405 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
4406
4407 var min: usize = 0;
4408 var max: usize = haystack.len;
4409 while (min < max) {
4410 const index = (min + max) / 2;
4411 const curr = haystack[index];
4412 if (predicate.predicate(curr)) {
4413 min = index + 1;
4414 } else {
4415 max = index;
19064416 }
1907
1908 ncmds.* += 1;
19094417 }
4418 return min;
19104419}
19114420
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;
4421/// Linear search
4422pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {
4423 if (!@hasDecl(@TypeOf(predicate), "predicate"))
4424 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
19494425
1950 log.debug("writing Mach-O header {}", .{header});
1951
1952 try macho_file.base.file.?.pwriteAll(mem.asBytes(&header), 0);
4426 var i: usize = 0;
4427 while (i < haystack.len) : (i += 1) {
4428 if (predicate.predicate(haystack[i])) break;
4429 }
4430 return i;
19534431}