authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-08-27 09:55:24+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-08-29 11:40:18+02:00
log2c68fb3d7ce077fba711747ee7b05b2fa0df6bcc
tree291b3aec840358c08a36c78c468c54fdc34727f0
parent42e0850d78e63fcc602dd0e167ac90dfb3cfec02

macho: merge Zld state with MachO state


21 files changed, 3042 insertions(+), 4119 deletions(-)

src/link/MachO.zig+1173-218
......@@ -405,9 +405,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
405405 const in_file = try std.fs.cwd().openFile(path, .{});
406406 defer in_file.close();
407407
408 parseLibrary(
409 self,
410 self.base.allocator,
408 self.parseLibrary(
411409 in_file,
412410 path,
413411 lib,
......@@ -421,24 +419,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
421419 };
422420 }
423421
424 parseDependentLibs(self, self.base.allocator, &dependent_libs, &self.base.options) catch |err| {
422 self.parseDependentLibs(&dependent_libs, &self.base.options) catch |err| {
425423 // TODO convert to error
426424 log.err("parsing dependent libraries failed with err {s}", .{@errorName(err)});
427425 };
428426 }
429427
430 if (self.dyld_stub_binder_index == null) {
431 self.dyld_stub_binder_index = try self.addUndefined("dyld_stub_binder", .add_got);
432 }
433 if (!self.base.options.single_threaded) {
434 _ = try self.addUndefined("__tlv_bootstrap", .none);
435 }
436
437 try self.createMhExecuteHeaderSymbol();
438
439428 var actions = std.ArrayList(ResolveAction).init(self.base.allocator);
440429 defer actions.deinit();
441 try self.resolveSymbolsInDylibs(&actions);
430 try self.resolveSymbols(&actions);
442431
443432 if (self.getEntryPoint() == null) {
444433 self.error_flags.no_entry_point_found = true;
......@@ -527,14 +516,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
527516
528517 try self.writeLinkeditSegmentData();
529518
530 const target = self.base.options.target;
531 const requires_codesig = blk: {
532 if (self.base.options.entitlements) |_| break :blk true;
533 if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
534 break :blk true;
535 break :blk false;
536 };
537 var codesig: ?CodeSignature = if (requires_codesig) blk: {
519 var codesig: ?CodeSignature = if (self.requiresCodeSignature()) blk: {
538520 // Preallocate space for the code signature.
539521 // We need to do this at this stage so that we have the load commands with proper values
540522 // written out to the file.
......@@ -596,14 +578,14 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
596578
597579 try load_commands.writeLoadDylibLCs(self.dylibs.items, self.referenced_dylibs.keys(), lc_writer);
598580
599 if (requires_codesig) {
581 if (codesig != null) {
600582 try lc_writer.writeStruct(self.codesig_cmd);
601583 }
602584
603585 const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);
604586 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
605587 try self.writeHeader(ncmds, @as(u32, @intCast(lc_buffer.items.len)));
606 try self.writeUuid(comp, uuid_cmd_offset, requires_codesig);
588 try self.writeUuid(comp, uuid_cmd_offset, codesig != null);
607589
608590 if (codesig) |*csig| {
609591 try self.writeCodeSignature(comp, csig); // code signing always comes last
......@@ -729,8 +711,7 @@ fn resolveLib(
729711}
730712
731713pub fn parsePositional(
732 ctx: anytype,
733 gpa: Allocator,
714 self: *MachO,
734715 file: std.fs.File,
735716 path: []const u8,
736717 must_link: bool,
......@@ -741,9 +722,9 @@ pub fn parsePositional(
741722 defer tracy.end();
742723
743724 if (Object.isObject(file)) {
744 try parseObject(ctx, gpa, file, path, link_options);
725 try self.parseObject(file, path, link_options);
745726 } else {
746 try parseLibrary(ctx, gpa, file, path, .{
727 try self.parseLibrary(file, path, .{
747728 .path = null,
748729 .needed = false,
749730 .weak = false,
......@@ -752,8 +733,7 @@ pub fn parsePositional(
752733}
753734
754735fn parseObject(
755 ctx: anytype,
756 gpa: Allocator,
736 self: *MachO,
757737 file: std.fs.File,
758738 path: []const u8,
759739 link_options: *const link.Options,
......@@ -761,6 +741,7 @@ fn parseObject(
761741 const tracy = trace(@src());
762742 defer tracy.end();
763743
744 const gpa = self.base.allocator;
764745 const mtime: u64 = mtime: {
765746 const stat = file.stat() catch break :mtime 0;
766747 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
......@@ -776,7 +757,7 @@ fn parseObject(
776757 };
777758 errdefer object.deinit(gpa);
778759 try object.parse(gpa);
779 try ctx.objects.append(gpa, object);
760 try self.objects.append(gpa, object);
780761
781762 const cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) {
782763 macho.CPU_TYPE_ARM64 => .aarch64,
......@@ -796,8 +777,7 @@ fn parseObject(
796777}
797778
798779pub fn parseLibrary(
799 ctx: anytype,
800 gpa: Allocator,
780 self: *MachO,
801781 file: std.fs.File,
802782 path: []const u8,
803783 lib: link.SystemLib,
......@@ -811,16 +791,16 @@ pub fn parseLibrary(
811791 const cpu_arch = link_options.target.cpu.arch;
812792
813793 if (fat.isFatLibrary(file)) {
814 const offset = parseFatLibrary(ctx, file, path, cpu_arch) catch |err| switch (err) {
794 const offset = self.parseFatLibrary(file, path, cpu_arch) catch |err| switch (err) {
815795 error.MissingArch => return,
816796 else => |e| return e,
817797 };
818798 try file.seekTo(offset);
819799
820800 if (Archive.isArchive(file, offset)) {
821 try parseArchive(ctx, gpa, path, offset, must_link, cpu_arch);
801 try self.parseArchive(path, offset, must_link, cpu_arch);
822802 } else if (Dylib.isDylib(file, offset)) {
823 try parseDylib(ctx, gpa, file, path, offset, dependent_libs, link_options, .{
803 try self.parseDylib(file, path, offset, dependent_libs, link_options, .{
824804 .needed = lib.needed,
825805 .weak = lib.weak,
826806 });
......@@ -830,14 +810,14 @@ pub fn parseLibrary(
830810 return;
831811 }
832812 } else if (Archive.isArchive(file, 0)) {
833 try parseArchive(ctx, gpa, path, 0, must_link, cpu_arch);
813 try self.parseArchive(path, 0, must_link, cpu_arch);
834814 } else if (Dylib.isDylib(file, 0)) {
835 try parseDylib(ctx, gpa, file, path, 0, dependent_libs, link_options, .{
815 try self.parseDylib(file, path, 0, dependent_libs, link_options, .{
836816 .needed = lib.needed,
837817 .weak = lib.weak,
838818 });
839819 } else {
840 parseLibStub(ctx, gpa, file, path, dependent_libs, link_options, .{
820 self.parseLibStub(file, path, dependent_libs, link_options, .{
841821 .needed = lib.needed,
842822 .weak = lib.weak,
843823 }) catch |err| switch (err) {
......@@ -852,12 +832,12 @@ pub fn parseLibrary(
852832}
853833
854834pub fn parseFatLibrary(
855 ctx: anytype,
835 self: *MachO,
856836 file: std.fs.File,
857837 path: []const u8,
858838 cpu_arch: std.Target.Cpu.Arch,
859839) !u64 {
860 _ = ctx;
840 _ = self;
861841 var buffer: [2]fat.Arch = undefined;
862842 const fat_archs = try fat.parseArchs(file, &buffer);
863843 const offset = for (fat_archs) |arch| {
......@@ -871,13 +851,13 @@ pub fn parseFatLibrary(
871851}
872852
873853fn parseArchive(
874 ctx: anytype,
875 gpa: Allocator,
854 self: *MachO,
876855 path: []const u8,
877856 fat_offset: u64,
878857 must_link: bool,
879858 cpu_arch: std.Target.Cpu.Arch,
880859) !void {
860 const gpa = self.base.allocator;
881861
882862 // We take ownership of the file so that we can store it for the duration of symbol resolution.
883863 // TODO we shouldn't need to do that and could pre-parse the archive like we do for zld/ELF?
......@@ -929,10 +909,10 @@ fn parseArchive(
929909 }
930910 for (offsets.keys()) |off| {
931911 const object = try archive.parseObject(gpa, off);
932 try ctx.objects.append(gpa, object);
912 try self.objects.append(gpa, object);
933913 }
934914 } else {
935 try ctx.archives.append(gpa, archive);
915 try self.archives.append(gpa, archive);
936916 }
937917}
938918
......@@ -944,8 +924,7 @@ const DylibOpts = struct {
944924};
945925
946926fn parseDylib(
947 ctx: anytype,
948 gpa: Allocator,
927 self: *MachO,
949928 file: std.fs.File,
950929 path: []const u8,
951930 offset: u64,
......@@ -953,6 +932,7 @@ fn parseDylib(
953932 link_options: *const link.Options,
954933 dylib_options: DylibOpts,
955934) !void {
935 const gpa = self.base.allocator;
956936 const self_cpu_arch = link_options.target.cpu.arch;
957937
958938 const file_stat = try file.stat();
......@@ -968,7 +948,7 @@ fn parseDylib(
968948
969949 try dylib.parseFromBinary(
970950 gpa,
971 @intCast(ctx.dylibs.items.len), // TODO defer it till later
951 @intCast(self.dylibs.items.len), // TODO defer it till later
972952 dependent_libs,
973953 path,
974954 contents,
......@@ -991,7 +971,7 @@ fn parseDylib(
991971
992972 // TODO verify platform
993973
994 addDylib(ctx, gpa, dylib, link_options, .{
974 self.addDylib(dylib, link_options, .{
995975 .needed = dylib_options.needed,
996976 .weak = dylib_options.weak,
997977 }) catch |err| switch (err) {
......@@ -1001,14 +981,14 @@ fn parseDylib(
1001981}
1002982
1003983fn parseLibStub(
1004 ctx: anytype,
1005 gpa: Allocator,
984 self: *MachO,
1006985 file: std.fs.File,
1007986 path: []const u8,
1008987 dependent_libs: anytype,
1009988 link_options: *const link.Options,
1010989 dylib_options: DylibOpts,
1011990) !void {
991 const gpa = self.base.allocator;
1012992 var lib_stub = try LibStub.loadFromFile(gpa, file);
1013993 defer lib_stub.deinit();
1014994
......@@ -1023,12 +1003,12 @@ fn parseLibStub(
10231003 gpa,
10241004 link_options.target,
10251005 lib_stub,
1026 @intCast(ctx.dylibs.items.len), // TODO defer it till later
1006 @intCast(self.dylibs.items.len), // TODO defer it till later
10271007 dependent_libs,
10281008 path,
10291009 );
10301010
1031 addDylib(ctx, gpa, dylib, link_options, .{
1011 self.addDylib(dylib, link_options, .{
10321012 .needed = dylib_options.needed,
10331013 .weak = dylib_options.weak,
10341014 }) catch |err| switch (err) {
......@@ -1038,8 +1018,7 @@ fn parseLibStub(
10381018}
10391019
10401020fn addDylib(
1041 ctx: anytype,
1042 gpa: Allocator,
1021 self: *MachO,
10431022 dylib: Dylib,
10441023 link_options: *const link.Options,
10451024 dylib_options: DylibOpts,
......@@ -1055,28 +1034,24 @@ fn addDylib(
10551034 }
10561035 }
10571036
1058 const gop = try ctx.dylibs_map.getOrPut(gpa, dylib.id.?.name);
1037 const gpa = self.base.allocator;
1038 const gop = try self.dylibs_map.getOrPut(gpa, dylib.id.?.name);
10591039 if (gop.found_existing) return error.DylibAlreadyExists;
10601040
1061 gop.value_ptr.* = @as(u16, @intCast(ctx.dylibs.items.len));
1062 try ctx.dylibs.append(gpa, dylib);
1041 gop.value_ptr.* = @as(u16, @intCast(self.dylibs.items.len));
1042 try self.dylibs.append(gpa, dylib);
10631043
10641044 const should_link_dylib_even_if_unreachable = blk: {
10651045 if (link_options.dead_strip_dylibs and !dylib_options.needed) break :blk false;
1066 break :blk !(dylib_options.dependent or ctx.referenced_dylibs.contains(gop.value_ptr.*));
1046 break :blk !(dylib_options.dependent or self.referenced_dylibs.contains(gop.value_ptr.*));
10671047 };
10681048
10691049 if (should_link_dylib_even_if_unreachable) {
1070 try ctx.referenced_dylibs.putNoClobber(gpa, gop.value_ptr.*, {});
1050 try self.referenced_dylibs.putNoClobber(gpa, gop.value_ptr.*, {});
10711051 }
10721052}
10731053
1074pub fn parseDependentLibs(
1075 ctx: anytype,
1076 gpa: Allocator,
1077 dependent_libs: anytype,
1078 link_options: *const link.Options,
1079) !void {
1054pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype, link_options: *const link.Options) !void {
10801055 const tracy = trace(@src());
10811056 defer tracy.end();
10821057
......@@ -1085,6 +1060,7 @@ pub fn parseDependentLibs(
10851060 // 2) afterwards, we parse dependents of the included dylibs
10861061 // TODO this should not be performed if the user specifies `-flat_namespace` flag.
10871062 // See ld64 manpages.
1063 const gpa = self.base.allocator;
10881064 var arena_alloc = std.heap.ArenaAllocator.init(gpa);
10891065 const arena = arena_alloc.allocator();
10901066 defer arena_alloc.deinit();
......@@ -1092,9 +1068,9 @@ pub fn parseDependentLibs(
10921068 outer: while (dependent_libs.readItem()) |dep_id| {
10931069 defer dep_id.id.deinit(gpa);
10941070
1095 if (ctx.dylibs_map.contains(dep_id.id.name)) continue;
1071 if (self.dylibs_map.contains(dep_id.id.name)) continue;
10961072
1097 const weak = ctx.dylibs.items[dep_id.parent].weak;
1073 const weak = self.dylibs.items[dep_id.parent].weak;
10981074 const has_ext = blk: {
10991075 const basename = fs.path.basename(dep_id.id.name);
11001076 break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;
......@@ -1121,7 +1097,7 @@ pub fn parseDependentLibs(
11211097 log.debug("trying dependency at fully resolved path {s}", .{full_path});
11221098
11231099 const offset: u64 = if (fat.isFatLibrary(file)) blk: {
1124 const offset = parseFatLibrary(ctx, file, full_path, link_options.target.cpu.arch) catch |err| switch (err) {
1100 const offset = self.parseFatLibrary(file, full_path, link_options.target.cpu.arch) catch |err| switch (err) {
11251101 error.MissingArch => break,
11261102 else => |e| return e,
11271103 };
......@@ -1130,12 +1106,12 @@ pub fn parseDependentLibs(
11301106 } else 0;
11311107
11321108 if (Dylib.isDylib(file, offset)) {
1133 try parseDylib(ctx, gpa, file, full_path, offset, dependent_libs, link_options, .{
1109 try self.parseDylib(file, full_path, offset, dependent_libs, link_options, .{
11341110 .dependent = true,
11351111 .weak = weak,
11361112 });
11371113 } else {
1138 parseLibStub(ctx, gpa, file, full_path, dependent_libs, link_options, .{
1114 self.parseLibStub(file, full_path, dependent_libs, link_options, .{
11391115 .dependent = true,
11401116 .weak = weak,
11411117 }) catch |err| switch (err) {
......@@ -1394,7 +1370,7 @@ fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void {
13941370 }
13951371}
13961372
1397pub fn allocateSpecialSymbols(self: anytype) !void {
1373pub fn allocateSpecialSymbols(self: *MachO) !void {
13981374 for (&[_][]const u8{
13991375 "___dso_handle",
14001376 "__mh_execute_header",
......@@ -1432,24 +1408,82 @@ pub fn createAtom(self: *MachO, sym_index: u32, opts: CreateAtomOpts) !Atom.Inde
14321408 return index;
14331409}
14341410
1411pub fn createTentativeDefAtoms(self: *MachO) !void {
1412 const gpa = self.base.allocator;
1413
1414 for (self.globals.items) |global| {
1415 const sym = self.getSymbolPtr(global);
1416 if (!sym.tentative()) continue;
1417 if (sym.n_desc == N_DEAD) continue;
1418
1419 log.debug("creating tentative definition for ATOM(%{d}, '{s}') in object({?})", .{
1420 global.sym_index, self.getSymbolName(global), global.file,
1421 });
1422
1423 // Convert any tentative definition into a regular symbol and allocate
1424 // text blocks for each tentative definition.
1425 const size = sym.n_value;
1426 const alignment = (sym.n_desc >> 8) & 0x0f;
1427
1428 if (self.bss_section_index == null) {
1429 self.bss_section_index = try self.initSection("__DATA", "__bss", .{
1430 .flags = macho.S_ZEROFILL,
1431 });
1432 }
1433
1434 sym.* = .{
1435 .n_strx = sym.n_strx,
1436 .n_type = macho.N_SECT | macho.N_EXT,
1437 .n_sect = self.bss_section_index.? + 1,
1438 .n_desc = 0,
1439 .n_value = 0,
1440 };
1441
1442 const atom_index = try self.createAtom(global.sym_index, .{
1443 .size = size,
1444 .alignment = alignment,
1445 });
1446 const atom = self.getAtomPtr(atom_index);
1447 atom.file = global.file;
1448
1449 self.addAtomToSection(atom_index);
1450
1451 assert(global.getFile() != null);
1452 const object = &self.objects.items[global.getFile().?];
1453 try object.atoms.append(gpa, atom_index);
1454 object.atom_by_index_table[global.sym_index] = atom_index;
1455 }
1456}
1457
14351458fn createDyldPrivateAtom(self: *MachO) !void {
14361459 if (self.dyld_private_atom_index != null) return;
14371460
14381461 const sym_index = try self.allocateSymbol();
1439 const atom_index = try self.createAtom(sym_index, .{});
1462 const atom_index = try self.createAtom(sym_index, .{
1463 .size = @sizeOf(u64),
1464 .alignment = 3,
1465 });
14401466 try self.atom_by_index_table.putNoClobber(self.base.allocator, sym_index, atom_index);
1441 const atom = self.getAtomPtr(atom_index);
1442 atom.size = @sizeOf(u64);
14431467
1468 if (self.data_section_index == null) {
1469 self.data_section_index = try self.initSection("__DATA", "__data", .{});
1470 }
1471
1472 const atom = self.getAtom(atom_index);
14441473 const sym = atom.getSymbolPtr(self);
14451474 sym.n_type = macho.N_SECT;
14461475 sym.n_sect = self.data_section_index.? + 1;
14471476 self.dyld_private_atom_index = atom_index;
14481477
1449 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
1450 log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});
1451 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
1452 try self.writeAtom(atom_index, &buffer);
1478 switch (self.mode) {
1479 .zld => self.addAtomToSection(atom_index),
1480 .incremental => {
1481 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
1482 log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});
1483 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
1484 try self.writeAtom(atom_index, &buffer);
1485 },
1486 }
14531487}
14541488
14551489fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: SymbolWithLoc) !Atom.Index {
......@@ -1485,7 +1519,7 @@ fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: S
14851519 return atom_index;
14861520}
14871521
1488fn createMhExecuteHeaderSymbol(self: *MachO) !void {
1522pub fn createMhExecuteHeaderSymbol(self: *MachO) !void {
14891523 if (self.base.options.output_mode != .Exe) return;
14901524
14911525 const gpa = self.base.allocator;
......@@ -1501,10 +1535,17 @@ fn createMhExecuteHeaderSymbol(self: *MachO) !void {
15011535 };
15021536
15031537 const gop = try self.getOrPutGlobalPtr("__mh_execute_header");
1538 if (gop.found_existing) {
1539 const global = gop.value_ptr.*;
1540 if (global.getFile()) |file| {
1541 const global_object = &self.objects.items[file];
1542 global_object.globals_lookup[global.sym_index] = self.getGlobalIndex("__mh_execute_header").?;
1543 }
1544 }
15041545 gop.value_ptr.* = sym_loc;
15051546}
15061547
1507fn createDsoHandleSymbol(self: *MachO) !void {
1548pub fn createDsoHandleSymbol(self: *MachO) !void {
15081549 const global = self.getGlobalPtr("___dso_handle") orelse return;
15091550 if (!self.getSymbol(global.*).undf()) return;
15101551
......@@ -1519,10 +1560,51 @@ fn createDsoHandleSymbol(self: *MachO) !void {
15191560 .n_desc = macho.N_WEAK_DEF,
15201561 .n_value = 0,
15211562 };
1563 const global_index = self.getGlobalIndex("___dso_handle").?;
1564 if (global.getFile()) |file| {
1565 const global_object = &self.objects.items[file];
1566 global_object.globals_lookup[global.sym_index] = global_index;
1567 }
15221568 global.* = sym_loc;
15231569 _ = self.unresolved.swapRemove(self.getGlobalIndex("___dso_handle").?);
15241570}
15251571
1572pub fn resolveSymbols(self: *MachO, actions: *std.ArrayList(ResolveAction)) !void {
1573 // We add the specified entrypoint as the first unresolved symbols so that
1574 // we search for it in libraries should there be no object files specified
1575 // on the linker line.
1576 if (self.base.options.output_mode == .Exe) {
1577 const entry_name = self.base.options.entry orelse load_commands.default_entry_point;
1578 _ = try self.addUndefined(entry_name, .none);
1579 }
1580
1581 // Force resolution of any symbols requested by the user.
1582 for (self.base.options.force_undefined_symbols.keys()) |sym_name| {
1583 _ = try self.addUndefined(sym_name, .none);
1584 }
1585
1586 for (self.objects.items, 0..) |_, object_id| {
1587 try self.resolveSymbolsInObject(@as(u32, @intCast(object_id)));
1588 }
1589
1590 try self.resolveSymbolsInArchives();
1591
1592 // Finally, force resolution of dyld_stub_binder if there are imports
1593 // requested.
1594 if (self.unresolved.count() > 0 and self.dyld_stub_binder_index == null) {
1595 self.dyld_stub_binder_index = try self.addUndefined("dyld_stub_binder", .add_got);
1596 }
1597 if (!self.base.options.single_threaded and self.mode == .incremental) {
1598 _ = try self.addUndefined("__tlv_bootstrap", .none);
1599 }
1600
1601 try self.resolveSymbolsInDylibs(actions);
1602
1603 try self.createMhExecuteHeaderSymbol();
1604 try self.createDsoHandleSymbol();
1605 try self.resolveSymbolsAtLoading();
1606}
1607
15261608fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
15271609 const gpa = self.base.allocator;
15281610 const sym = self.getSymbol(current);
......@@ -1536,6 +1618,7 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
15361618 }
15371619 return;
15381620 }
1621 const global_index = self.getGlobalIndex(sym_name).?;
15391622 const global = gop.value_ptr.*;
15401623 const global_sym = self.getSymbol(global);
15411624
......@@ -1566,7 +1649,22 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
15661649 const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext());
15671650 const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext());
15681651
1569 if (sym_is_strong and global_is_strong) return error.MultipleSymbolDefinitions;
1652 if (sym_is_strong and global_is_strong) {
1653 log.err("symbol '{s}' defined multiple times", .{sym_name});
1654 if (global.getFile()) |file| {
1655 log.err(" first definition in '{s}'", .{self.objects.items[file].name});
1656 }
1657 if (current.getFile()) |file| {
1658 log.err(" next definition in '{s}'", .{self.objects.items[file].name});
1659 }
1660 return error.MultipleSymbolDefinitions;
1661 }
1662
1663 if (current.getFile()) |file| {
1664 const object = &self.objects.items[file];
1665 object.globals_lookup[current.sym_index] = global_index;
1666 }
1667
15701668 if (global_is_strong) return;
15711669 if (sym_is_weak and global_is_weak) return;
15721670 if (sym.tentative() and global_sym.tentative()) {
......@@ -1574,11 +1672,88 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
15741672 }
15751673 if (sym.undf() and !sym.tentative()) return;
15761674
1577 _ = self.unresolved.swapRemove(self.getGlobalIndex(sym_name).?);
1675 if (global.getFile()) |file| {
1676 const global_object = &self.objects.items[file];
1677 global_object.globals_lookup[global.sym_index] = global_index;
1678 }
1679 _ = self.unresolved.swapRemove(global_index);
15781680
15791681 gop.value_ptr.* = current;
15801682}
15811683
1684fn resolveSymbolsInObject(self: *MachO, object_id: u32) !void {
1685 const object = &self.objects.items[object_id];
1686 const in_symtab = object.in_symtab orelse return;
1687
1688 log.debug("resolving symbols in '{s}'", .{object.name});
1689
1690 var sym_index: u32 = 0;
1691 while (sym_index < in_symtab.len) : (sym_index += 1) {
1692 const sym = &object.symtab[sym_index];
1693 const sym_name = object.getSymbolName(sym_index);
1694
1695 if (sym.stab()) {
1696 log.err("unhandled symbol type: stab", .{});
1697 log.err(" symbol '{s}'", .{sym_name});
1698 log.err(" first definition in '{s}'", .{object.name});
1699 return error.UnhandledSymbolType;
1700 }
1701
1702 if (sym.indr()) {
1703 log.err("unhandled symbol type: indirect", .{});
1704 log.err(" symbol '{s}'", .{sym_name});
1705 log.err(" first definition in '{s}'", .{object.name});
1706 return error.UnhandledSymbolType;
1707 }
1708
1709 if (sym.abs()) {
1710 log.err("unhandled symbol type: absolute", .{});
1711 log.err(" symbol '{s}'", .{sym_name});
1712 log.err(" first definition in '{s}'", .{object.name});
1713 return error.UnhandledSymbolType;
1714 }
1715
1716 if (sym.sect() and !sym.ext()) {
1717 log.debug("symbol '{s}' local to object {s}; skipping...", .{
1718 sym_name,
1719 object.name,
1720 });
1721 continue;
1722 }
1723
1724 try self.resolveGlobalSymbol(.{ .sym_index = sym_index, .file = object_id + 1 });
1725 }
1726}
1727
1728fn resolveSymbolsInArchives(self: *MachO) !void {
1729 if (self.archives.items.len == 0) return;
1730
1731 const gpa = self.base.allocator;
1732 var next_sym: usize = 0;
1733 loop: while (next_sym < self.unresolved.count()) {
1734 const global = self.globals.items[self.unresolved.keys()[next_sym]];
1735 const sym_name = self.getSymbolName(global);
1736
1737 for (self.archives.items) |archive| {
1738 // Check if the entry exists in a static archive.
1739 const offsets = archive.toc.get(sym_name) orelse {
1740 // No hit.
1741 continue;
1742 };
1743 assert(offsets.items.len > 0);
1744
1745 const object_id = @as(u16, @intCast(self.objects.items.len));
1746 const object = try archive.parseObject(gpa, offsets.items[0]);
1747 try self.objects.append(gpa, object);
1748 try self.resolveSymbolsInObject(object_id);
1749
1750 continue :loop;
1751 }
1752
1753 next_sym += 1;
1754 }
1755}
1756
15821757fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction)) !void {
15831758 if (self.dylibs.items.len == 0) return;
15841759
......@@ -1608,6 +1783,7 @@ fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction))
16081783
16091784 if (self.unresolved.fetchSwapRemove(global_index)) |entry| blk: {
16101785 if (!sym.undf()) break :blk;
1786 if (self.mode == .zld) break :blk;
16111787 try actions.append(.{ .kind = entry.value, .target = global });
16121788 }
16131789
......@@ -1618,6 +1794,42 @@ fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction))
16181794 }
16191795}
16201796
1797fn resolveSymbolsAtLoading(self: *MachO) !void {
1798 const is_lib = self.base.options.output_mode == .Lib;
1799 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
1800 const allow_undef = is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false);
1801
1802 var next_sym: usize = 0;
1803 while (next_sym < self.unresolved.count()) {
1804 const global_index = self.unresolved.keys()[next_sym];
1805 const global = self.globals.items[global_index];
1806 const sym = self.getSymbolPtr(global);
1807
1808 if (sym.discarded()) {
1809 sym.* = .{
1810 .n_strx = 0,
1811 .n_type = macho.N_UNDF,
1812 .n_sect = 0,
1813 .n_desc = 0,
1814 .n_value = 0,
1815 };
1816 _ = self.unresolved.swapRemove(global_index);
1817 continue;
1818 } else if (allow_undef) {
1819 const n_desc = @as(
1820 u16,
1821 @bitCast(macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP * @as(i16, @intCast(macho.N_SYMBOL_RESOLVER))),
1822 );
1823 sym.n_type = macho.N_EXT;
1824 sym.n_desc = n_desc;
1825 _ = self.unresolved.swapRemove(global_index);
1826 continue;
1827 }
1828
1829 next_sym += 1;
1830 }
1831}
1832
16211833pub fn deinit(self: *MachO) void {
16221834 const gpa = self.base.allocator;
16231835
......@@ -1638,7 +1850,6 @@ pub fn deinit(self: *MachO) void {
16381850 self.thunks.deinit(gpa);
16391851
16401852 self.strtab.deinit(gpa);
1641
16421853 self.locals.deinit(gpa);
16431854 self.globals.deinit(gpa);
16441855 self.locals_free_list.deinit(gpa);
......@@ -1653,6 +1864,14 @@ pub fn deinit(self: *MachO) void {
16531864 self.resolver.deinit(gpa);
16541865 }
16551866
1867 for (self.objects.items) |*object| {
1868 object.deinit(gpa);
1869 }
1870 self.objects.deinit(gpa);
1871 for (self.archives.items) |*archive| {
1872 archive.deinit(gpa);
1873 }
1874 self.archives.deinit(gpa);
16561875 for (self.dylibs.items) |*dylib| {
16571876 dylib.deinit(gpa);
16581877 }
......@@ -1842,20 +2061,55 @@ fn allocateGlobal(self: *MachO) !u32 {
18422061 return index;
18432062}
18442063
1845fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void {
2064pub fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void {
18462065 if (self.got_table.lookup.contains(target)) return;
18472066 const got_index = try self.got_table.allocateEntry(self.base.allocator, target);
1848 try self.writeOffsetTableEntry(got_index);
1849 self.got_table_count_dirty = true;
1850 self.markRelocsDirtyByTarget(target);
2067 if (self.got_section_index == null) {
2068 self.got_section_index = try self.initSection("__DATA_CONST", "__got", .{
2069 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
2070 });
2071 }
2072 if (self.mode == .incremental) {
2073 try self.writeOffsetTableEntry(got_index);
2074 self.got_table_count_dirty = true;
2075 self.markRelocsDirtyByTarget(target);
2076 }
18512077}
18522078
1853fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void {
2079pub fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void {
18542080 if (self.stub_table.lookup.contains(target)) return;
18552081 const stub_index = try self.stub_table.allocateEntry(self.base.allocator, target);
1856 try self.writeStubTableEntry(stub_index);
1857 self.stub_table_count_dirty = true;
1858 self.markRelocsDirtyByTarget(target);
2082 if (self.stubs_section_index == null) {
2083 self.stubs_section_index = try self.initSection("__TEXT", "__stubs", .{
2084 .flags = macho.S_SYMBOL_STUBS |
2085 macho.S_ATTR_PURE_INSTRUCTIONS |
2086 macho.S_ATTR_SOME_INSTRUCTIONS,
2087 .reserved2 = stubs.stubSize(self.base.options.target.cpu.arch),
2088 });
2089 self.stub_helper_section_index = try self.initSection("__TEXT", "__stub_helper", .{
2090 .flags = macho.S_REGULAR |
2091 macho.S_ATTR_PURE_INSTRUCTIONS |
2092 macho.S_ATTR_SOME_INSTRUCTIONS,
2093 });
2094 self.la_symbol_ptr_section_index = try self.initSection("__DATA", "__la_symbol_ptr", .{
2095 .flags = macho.S_LAZY_SYMBOL_POINTERS,
2096 });
2097 }
2098 if (self.mode == .incremental) {
2099 try self.writeStubTableEntry(stub_index);
2100 self.stub_table_count_dirty = true;
2101 self.markRelocsDirtyByTarget(target);
2102 }
2103}
2104
2105pub fn addTlvPtrEntry(self: *MachO, target: SymbolWithLoc) !void {
2106 if (self.tlv_ptr_table.lookup.contains(target)) return;
2107 _ = try self.tlv_ptr_table.allocateEntry(self.gpa, target);
2108 if (self.tlv_ptr_section_index == null) {
2109 self.tlv_ptr_section_index = try self.initSection("__DATA", "__thread_ptrs", .{
2110 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
2111 });
2112 }
18592113}
18602114
18612115pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
......@@ -2758,16 +3012,10 @@ const InitSectionOpts = struct {
27583012 reserved2: u32 = 0,
27593013};
27603014
2761pub fn initSection(
2762 gpa: Allocator,
2763 ctx: anytype,
2764 segname: []const u8,
2765 sectname: []const u8,
2766 opts: InitSectionOpts,
2767) !u8 {
3015pub fn initSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: InitSectionOpts) !u8 {
27683016 log.debug("creating section '{s},{s}'", .{ segname, sectname });
2769 const index = @as(u8, @intCast(ctx.sections.slice().len));
2770 try ctx.sections.append(gpa, .{
3017 const index = @as(u8, @intCast(self.sections.slice().len));
3018 try self.sections.append(self.base.allocator, .{
27713019 .segment_index = undefined, // Segments will be created automatically later down the pipeline
27723020 .header = .{
27733021 .sectname = makeStaticString(sectname),
......@@ -2822,7 +3070,7 @@ fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts
28223070 .cmdsize = @sizeOf(macho.segment_command_64) + @sizeOf(macho.section_64),
28233071 };
28243072
2825 const sect_id = try initSection(gpa, self, sectname, segname, .{
3073 const sect_id = try self.initSection(sectname, segname, .{
28263074 .flags = opts.flags,
28273075 .reserved2 = opts.reserved2,
28283076 });
......@@ -2918,10 +3166,29 @@ fn growSectionVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void {
29183166 }
29193167}
29203168
3169pub fn addAtomToSection(self: *MachO, atom_index: Atom.Index) void {
3170 assert(self.mode == .zld);
3171 const atom = self.getAtomPtr(atom_index);
3172 const sym = self.getSymbol(atom.getSymbolWithLoc());
3173 var section = self.sections.get(sym.n_sect - 1);
3174 if (section.header.size > 0) {
3175 const last_atom = self.getAtomPtr(section.last_atom_index.?);
3176 last_atom.next_index = atom_index;
3177 atom.prev_index = section.last_atom_index;
3178 } else {
3179 section.first_atom_index = atom_index;
3180 }
3181 section.last_atom_index = atom_index;
3182 section.header.size += atom.size;
3183 self.sections.set(sym.n_sect - 1, section);
3184}
3185
29213186fn allocateAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: u64) !u64 {
29223187 const tracy = trace(@src());
29233188 defer tracy.end();
29243189
3190 assert(self.mode == .incremental);
3191
29253192 const atom = self.getAtom(atom_index);
29263193 const sect_id = atom.getSymbol(self).n_sect - 1;
29273194 const segment = self.getSegmentPtr(sect_id);
......@@ -3048,7 +3315,7 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u
30483315 return self.addUndefined(sym_name, .add_stub);
30493316}
30503317
3051pub fn writeSegmentHeaders(self: anytype, writer: anytype) !void {
3318pub fn writeSegmentHeaders(self: *MachO, writer: anytype) !void {
30523319 for (self.segments.items, 0..) |seg, i| {
30533320 const indexes = self.getSectionIndexes(@as(u8, @intCast(i)));
30543321 var out_seg = seg;
......@@ -3075,7 +3342,7 @@ pub fn writeSegmentHeaders(self: anytype, writer: anytype) !void {
30753342 }
30763343}
30773344
3078fn writeLinkeditSegmentData(self: *MachO) !void {
3345pub fn writeLinkeditSegmentData(self: *MachO) !void {
30793346 const page_size = getPageSize(self.base.options.target.cpu.arch);
30803347 const seg = self.getLinkeditSegmentPtr();
30813348 seg.filesize = 0;
......@@ -3092,29 +3359,29 @@ fn writeLinkeditSegmentData(self: *MachO) !void {
30923359 }
30933360
30943361 try self.writeDyldInfoData();
3362 // TODO handle this better
3363 if (self.mode == .zld) {
3364 try self.writeFunctionStarts();
3365 try self.writeDataInCode();
3366 }
30953367 try self.writeSymtabs();
30963368
30973369 seg.vmsize = mem.alignForward(u64, seg.filesize, page_size);
30983370}
30993371
3100pub fn collectRebaseDataFromTableSection(
3101 gpa: Allocator,
3102 ctx: anytype,
3103 sect_id: u8,
3104 rebase: *Rebase,
3105 table: anytype,
3106) !void {
3107 const header = ctx.sections.items(.header)[sect_id];
3108 const segment_index = ctx.sections.items(.segment_index)[sect_id];
3109 const segment = ctx.segments.items[segment_index];
3372fn collectRebaseDataFromTableSection(self: *MachO, sect_id: u8, rebase: *Rebase, table: anytype) !void {
3373 const gpa = self.base.allocator;
3374 const header = self.sections.items(.header)[sect_id];
3375 const segment_index = self.sections.items(.segment_index)[sect_id];
3376 const segment = self.segments.items[segment_index];
31103377 const base_offset = header.addr - segment.vmaddr;
3111 const is_got = if (ctx.got_section_index) |index| index == sect_id else false;
3378 const is_got = if (self.got_section_index) |index| index == sect_id else false;
31123379
31133380 try rebase.entries.ensureUnusedCapacity(gpa, table.entries.items.len);
31143381
31153382 for (table.entries.items, 0..) |entry, i| {
31163383 if (!table.lookup.contains(entry)) continue;
3117 const sym = ctx.getSymbol(entry);
3384 const sym = self.getSymbol(entry);
31183385 if (is_got and sym.undf()) continue;
31193386 const offset = i * @sizeOf(u64);
31203387 log.debug(" | rebase at {x}", .{base_offset + offset});
......@@ -3152,34 +3419,105 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
31523419 }
31533420 }
31543421
3155 try collectRebaseDataFromTableSection(gpa, self, self.got_section_index.?, rebase, self.got_table);
3156 try collectRebaseDataFromTableSection(gpa, self, self.la_symbol_ptr_section_index.?, rebase, self.stub_table);
3422 // Unpack GOT entries
3423 if (self.got_section_index) |sect_id| {
3424 try self.collectRebaseDataFromTableSection(sect_id, rebase, self.got_table);
3425 }
3426
3427 // Next, unpack __la_symbol_ptr entries
3428 if (self.la_symbol_ptr_section_index) |sect_id| {
3429 try self.collectRebaseDataFromTableSection(sect_id, rebase, self.stub_table);
3430 }
3431
3432 // Finally, unpack the rest.
3433 const cpu_arch = self.base.options.target.cpu.arch;
3434 for (self.objects.items) |*object| {
3435 for (object.atoms.items) |atom_index| {
3436 const atom = self.getAtom(atom_index);
3437 const sym = self.getSymbol(atom.getSymbolWithLoc());
3438 if (sym.n_desc == N_DEAD) continue;
3439
3440 const sect_id = sym.n_sect - 1;
3441 const section = self.sections.items(.header)[sect_id];
3442 const segment_id = self.sections.items(.segment_index)[sect_id];
3443 const segment = self.segments.items[segment_id];
3444 if (segment.maxprot & macho.PROT.WRITE == 0) continue;
3445 switch (section.type()) {
3446 macho.S_LITERAL_POINTERS,
3447 macho.S_REGULAR,
3448 macho.S_MOD_INIT_FUNC_POINTERS,
3449 macho.S_MOD_TERM_FUNC_POINTERS,
3450 => {},
3451 else => continue,
3452 }
3453
3454 log.debug(" ATOM({d}, %{d}, '{s}')", .{
3455 atom_index,
3456 atom.sym_index,
3457 self.getSymbolName(atom.getSymbolWithLoc()),
3458 });
3459
3460 const code = Atom.getAtomCode(self, atom_index);
3461 const relocs = Atom.getAtomRelocs(self, atom_index);
3462 const ctx = Atom.getRelocContext(self, atom_index);
3463
3464 for (relocs) |rel| {
3465 switch (cpu_arch) {
3466 .aarch64 => {
3467 const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
3468 if (rel_type != .ARM64_RELOC_UNSIGNED) continue;
3469 if (rel.r_length != 3) continue;
3470 },
3471 .x86_64 => {
3472 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
3473 if (rel_type != .X86_64_RELOC_UNSIGNED) continue;
3474 if (rel.r_length != 3) continue;
3475 },
3476 else => unreachable,
3477 }
3478 const target = Atom.parseRelocTarget(self, .{
3479 .object_id = atom.getFile().?,
3480 .rel = rel,
3481 .code = code,
3482 .base_offset = ctx.base_offset,
3483 .base_addr = ctx.base_addr,
3484 });
3485 const target_sym = self.getSymbol(target);
3486 if (target_sym.undf()) continue;
3487
3488 const base_offset = @as(i32, @intCast(sym.n_value - segment.vmaddr));
3489 const rel_offset = rel.r_address - ctx.base_offset;
3490 const offset = @as(u64, @intCast(base_offset + rel_offset));
3491 log.debug(" | rebase at {x}", .{offset});
3492
3493 try rebase.entries.append(self.gpa, .{
3494 .offset = offset,
3495 .segment_id = segment_id,
3496 });
3497 }
3498 }
3499 }
31573500
31583501 try rebase.finalize(gpa);
31593502}
31603503
3161pub fn collectBindDataFromTableSection(
3162 gpa: Allocator,
3163 ctx: anytype,
3164 sect_id: u8,
3165 bind: anytype,
3166 table: anytype,
3167) !void {
3168 const header = ctx.sections.items(.header)[sect_id];
3169 const segment_index = ctx.sections.items(.segment_index)[sect_id];
3170 const segment = ctx.segments.items[segment_index];
3504fn collectBindDataFromTableSection(self: *MachO, sect_id: u8, bind: anytype, table: anytype) !void {
3505 const gpa = self.base.allocator;
3506 const header = self.sections.items(.header)[sect_id];
3507 const segment_index = self.sections.items(.segment_index)[sect_id];
3508 const segment = self.segments.items[segment_index];
31713509 const base_offset = header.addr - segment.vmaddr;
31723510
31733511 try bind.entries.ensureUnusedCapacity(gpa, table.entries.items.len);
31743512
31753513 for (table.entries.items, 0..) |entry, i| {
31763514 if (!table.lookup.contains(entry)) continue;
3177 const bind_sym = ctx.getSymbol(entry);
3515 const bind_sym = self.getSymbol(entry);
31783516 if (!bind_sym.undf()) continue;
31793517 const offset = i * @sizeOf(u64);
31803518 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
31813519 base_offset + offset,
3182 ctx.getSymbolName(entry),
3520 self.getSymbolName(entry),
31833521 @divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER),
31843522 });
31853523 if (bind_sym.weakRef()) {
......@@ -3235,13 +3573,105 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {
32353573 }
32363574 }
32373575
3238 // Gather GOT pointers
3239 try collectBindDataFromTableSection(gpa, self, self.got_section_index.?, bind, self.got_table);
3576 // Unpack GOT pointers
3577 if (self.got_section_index) |sect_id| {
3578 try self.collectBindDataFromTableSection(sect_id, bind, self.got_table);
3579 }
3580
3581 // Next, unpack TLV pointers section
3582 if (self.tlv_ptr_section_index) |sect_id| {
3583 try self.collectBindDataFromTableSection(sect_id, bind, self.tlv_ptr_table);
3584 }
3585
3586 // Finally, unpack the rest.
3587 const cpu_arch = self.base.options.target.cpu.arch;
3588 for (self.objects.items) |*object| {
3589 for (object.atoms.items) |atom_index| {
3590 const atom = self.getAtom(atom_index);
3591 const sym = self.getSymbol(atom.getSymbolWithLoc());
3592 if (sym.n_desc == N_DEAD) continue;
3593
3594 const sect_id = sym.n_sect - 1;
3595 const section = self.sections.items(.header)[sect_id];
3596 const segment_id = self.sections.items(.segment_index)[sect_id];
3597 const segment = self.segments.items[segment_id];
3598 if (segment.maxprot & macho.PROT.WRITE == 0) continue;
3599 switch (section.type()) {
3600 macho.S_LITERAL_POINTERS,
3601 macho.S_REGULAR,
3602 macho.S_MOD_INIT_FUNC_POINTERS,
3603 macho.S_MOD_TERM_FUNC_POINTERS,
3604 => {},
3605 else => continue,
3606 }
3607
3608 log.debug(" ATOM({d}, %{d}, '{s}')", .{
3609 atom_index,
3610 atom.sym_index,
3611 self.getSymbolName(atom.getSymbolWithLoc()),
3612 });
3613
3614 const code = Atom.getAtomCode(self, atom_index);
3615 const relocs = Atom.getAtomRelocs(self, atom_index);
3616 const ctx = Atom.getRelocContext(self, atom_index);
3617
3618 for (relocs) |rel| {
3619 switch (cpu_arch) {
3620 .aarch64 => {
3621 const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
3622 if (rel_type != .ARM64_RELOC_UNSIGNED) continue;
3623 if (rel.r_length != 3) continue;
3624 },
3625 .x86_64 => {
3626 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
3627 if (rel_type != .X86_64_RELOC_UNSIGNED) continue;
3628 if (rel.r_length != 3) continue;
3629 },
3630 else => unreachable,
3631 }
3632
3633 const global = Atom.parseRelocTarget(self, .{
3634 .object_id = atom.getFile().?,
3635 .rel = rel,
3636 .code = code,
3637 .base_offset = ctx.base_offset,
3638 .base_addr = ctx.base_addr,
3639 });
3640 const bind_sym_name = self.getSymbolName(global);
3641 const bind_sym = self.getSymbol(global);
3642 if (!bind_sym.undf()) continue;
3643
3644 const base_offset = sym.n_value - segment.vmaddr;
3645 const rel_offset = @as(u32, @intCast(rel.r_address - ctx.base_offset));
3646 const offset = @as(u64, @intCast(base_offset + rel_offset));
3647 const addend = mem.readIntLittle(i64, code[rel_offset..][0..8]);
3648
3649 const dylib_ordinal = @divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER);
3650 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
3651 base_offset,
3652 bind_sym_name,
3653 dylib_ordinal,
3654 });
3655 log.debug(" | with addend {x}", .{addend});
3656 if (bind_sym.weakRef()) {
3657 log.debug(" | marking as weak ref ", .{});
3658 }
3659 try bind.entries.append(self.gpa, .{
3660 .target = global,
3661 .offset = offset,
3662 .segment_id = segment_id,
3663 .addend = addend,
3664 });
3665 }
3666 }
3667 }
3668
32403669 try bind.finalize(gpa, self);
32413670}
32423671
32433672fn collectLazyBindData(self: *MachO, bind: anytype) !void {
3244 try collectBindDataFromTableSection(self.base.allocator, self, self.la_symbol_ptr_section_index.?, bind, self.stub_table);
3673 const sect_id = self.la_symbol_ptr_section_index orelse return;
3674 try self.collectBindDataFromTableSection(sect_id, bind, self.stub_table);
32453675 try bind.finalize(self.base.allocator, self);
32463676}
32473677
......@@ -3259,6 +3689,7 @@ fn collectExportData(self: *MachO, trie: *Trie) !void {
32593689
32603690 if (sym.undf()) continue;
32613691 assert(sym.ext());
3692 if (sym.n_desc == N_DEAD) continue;
32623693
32633694 const sym_name = self.getSymbolName(global);
32643695 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
......@@ -3349,12 +3780,7 @@ fn writeDyldInfoData(self: *MachO) !void {
33493780 });
33503781
33513782 try self.base.file.?.pwriteAll(buffer, rebase_off);
3352 try populateLazyBindOffsetsInStubHelper(
3353 self,
3354 self.base.options.target.cpu.arch,
3355 self.base.file.?,
3356 lazy_bind,
3357 );
3783 try self.populateLazyBindOffsetsInStubHelper(lazy_bind);
33583784
33593785 self.dyld_info_cmd.rebase_off = @as(u32, @intCast(rebase_off));
33603786 self.dyld_info_cmd.rebase_size = @as(u32, @intCast(rebase_size_aligned));
......@@ -3366,19 +3792,15 @@ fn writeDyldInfoData(self: *MachO) !void {
33663792 self.dyld_info_cmd.export_size = @as(u32, @intCast(export_size_aligned));
33673793}
33683794
3369pub fn populateLazyBindOffsetsInStubHelper(
3370 ctx: anytype,
3371 cpu_arch: std.Target.Cpu.Arch,
3372 file: fs.File,
3373 lazy_bind: anytype,
3374) !void {
3795fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: anytype) !void {
33753796 if (lazy_bind.size() == 0) return;
33763797
3377 const stub_helper_section_index = ctx.stub_helper_section_index.?;
3798 const stub_helper_section_index = self.stub_helper_section_index.?;
33783799 // assert(ctx.stub_helper_preamble_allocated);
33793800
3380 const header = ctx.sections.items(.header)[stub_helper_section_index];
3801 const header = self.sections.items(.header)[stub_helper_section_index];
33813802
3803 const cpu_arch = self.base.options.target.cpu.arch;
33823804 const preamble_size = stubs.stubHelperPreambleSize(cpu_arch);
33833805 const stub_size = stubs.stubHelperSize(cpu_arch);
33843806 const stub_offset = stubs.stubOffsetInStubHelper(cpu_arch);
......@@ -3389,14 +3811,175 @@ pub fn populateLazyBindOffsetsInStubHelper(
33893811
33903812 log.debug("writing lazy bind offset 0x{x} ({s}) in stub helper at 0x{x}", .{
33913813 bind_offset,
3392 ctx.getSymbolName(lazy_bind.entries.items[index].target),
3814 self.getSymbolName(lazy_bind.entries.items[index].target),
33933815 file_offset,
33943816 });
33953817
3396 try file.pwriteAll(mem.asBytes(&bind_offset), file_offset);
3818 try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset);
33973819 }
33983820}
33993821
3822const asc_u64 = std.sort.asc(u64);
3823
3824fn addSymbolToFunctionStarts(self: *MachO, sym_loc: SymbolWithLoc, addresses: *std.ArrayList(u64)) !void {
3825 const sym = self.getSymbol(sym_loc);
3826 if (sym.n_strx == 0) return;
3827 if (sym.n_desc == MachO.N_DEAD) return;
3828 if (self.symbolIsTemp(sym_loc)) return;
3829 try addresses.append(sym.n_value);
3830}
3831
3832fn writeFunctionStarts(self: *MachO) !void {
3833 const gpa = self.base.allocator;
3834 const seg = self.segments.items[self.header_segment_cmd_index.?];
3835
3836 // We need to sort by address first
3837 var addresses = std.ArrayList(u64).init(gpa);
3838 defer addresses.deinit();
3839
3840 for (self.objects.items) |object| {
3841 for (object.exec_atoms.items) |atom_index| {
3842 const atom = self.getAtom(atom_index);
3843 const sym_loc = atom.getSymbolWithLoc();
3844 try self.addSymbolToFunctionStarts(sym_loc, &addresses);
3845
3846 var it = Atom.getInnerSymbolsIterator(self, atom_index);
3847 while (it.next()) |inner_sym_loc| {
3848 try self.addSymbolToFunctionStarts(inner_sym_loc, &addresses);
3849 }
3850 }
3851 }
3852
3853 mem.sort(u64, addresses.items, {}, asc_u64);
3854
3855 var offsets = std.ArrayList(u32).init(gpa);
3856 defer offsets.deinit();
3857 try offsets.ensureTotalCapacityPrecise(addresses.items.len);
3858
3859 var last_off: u32 = 0;
3860 for (addresses.items) |addr| {
3861 const offset = @as(u32, @intCast(addr - seg.vmaddr));
3862 const diff = offset - last_off;
3863
3864 if (diff == 0) continue;
3865
3866 offsets.appendAssumeCapacity(diff);
3867 last_off = offset;
3868 }
3869
3870 var buffer = std.ArrayList(u8).init(gpa);
3871 defer buffer.deinit();
3872
3873 const max_size = @as(usize, @intCast(offsets.items.len * @sizeOf(u64)));
3874 try buffer.ensureTotalCapacity(max_size);
3875
3876 for (offsets.items) |offset| {
3877 try std.leb.writeULEB128(buffer.writer(), offset);
3878 }
3879
3880 const link_seg = self.getLinkeditSegmentPtr();
3881 const offset = link_seg.fileoff + link_seg.filesize;
3882 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
3883 const needed_size = buffer.items.len;
3884 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
3885 const padding = math.cast(usize, needed_size_aligned - needed_size) orelse return error.Overflow;
3886 if (padding > 0) {
3887 try buffer.ensureUnusedCapacity(padding);
3888 buffer.appendNTimesAssumeCapacity(0, padding);
3889 }
3890 link_seg.filesize = offset + needed_size_aligned - link_seg.fileoff;
3891
3892 log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
3893
3894 try self.base.file.?.pwriteAll(buffer.items, offset);
3895
3896 self.function_starts_cmd.dataoff = @as(u32, @intCast(offset));
3897 self.function_starts_cmd.datasize = @as(u32, @intCast(needed_size_aligned));
3898}
3899
3900fn filterDataInCode(
3901 dices: []const macho.data_in_code_entry,
3902 start_addr: u64,
3903 end_addr: u64,
3904) []const macho.data_in_code_entry {
3905 const Predicate = struct {
3906 addr: u64,
3907
3908 pub fn predicate(self: @This(), dice: macho.data_in_code_entry) bool {
3909 return dice.offset >= self.addr;
3910 }
3911 };
3912
3913 const start = MachO.lsearch(macho.data_in_code_entry, dices, Predicate{ .addr = start_addr });
3914 const end = MachO.lsearch(macho.data_in_code_entry, dices[start..], Predicate{ .addr = end_addr }) + start;
3915
3916 return dices[start..end];
3917}
3918
3919pub fn writeDataInCode(self: *MachO) !void {
3920 const gpa = self.base.allocator;
3921 var out_dice = std.ArrayList(macho.data_in_code_entry).init(gpa);
3922 defer out_dice.deinit();
3923
3924 const text_sect_id = self.text_section_index orelse return;
3925 const text_sect_header = self.sections.items(.header)[text_sect_id];
3926
3927 for (self.objects.items) |object| {
3928 if (!object.hasDataInCode()) continue;
3929 const dice = object.data_in_code.items;
3930 try out_dice.ensureUnusedCapacity(dice.len);
3931
3932 for (object.exec_atoms.items) |atom_index| {
3933 const atom = self.getAtom(atom_index);
3934 const sym = self.getSymbol(atom.getSymbolWithLoc());
3935 if (sym.n_desc == MachO.N_DEAD) continue;
3936
3937 const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
3938 source_sym.n_value
3939 else blk: {
3940 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
3941 const source_sect_id = @as(u8, @intCast(atom.sym_index - nbase));
3942 break :blk object.getSourceSection(source_sect_id).addr;
3943 };
3944 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);
3945 const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse
3946 return error.Overflow;
3947
3948 for (filtered_dice) |single| {
3949 const offset = math.cast(u32, single.offset - source_addr + base) orelse
3950 return error.Overflow;
3951 out_dice.appendAssumeCapacity(.{
3952 .offset = offset,
3953 .length = single.length,
3954 .kind = single.kind,
3955 });
3956 }
3957 }
3958 }
3959
3960 const seg = self.getLinkeditSegmentPtr();
3961 const offset = seg.fileoff + seg.filesize;
3962 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
3963 const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
3964 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
3965 seg.filesize = offset + needed_size_aligned - seg.fileoff;
3966
3967 const buffer = try gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow);
3968 defer gpa.free(buffer);
3969 {
3970 const src = mem.sliceAsBytes(out_dice.items);
3971 @memcpy(buffer[0..src.len], src);
3972 @memset(buffer[src.len..], 0);
3973 }
3974
3975 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
3976
3977 try self.base.file.?.pwriteAll(buffer, offset);
3978
3979 self.data_in_code_cmd.dataoff = @as(u32, @intCast(offset));
3980 self.data_in_code_cmd.datasize = @as(u32, @intCast(needed_size_aligned));
3981}
3982
34003983fn writeSymtabs(self: *MachO) !void {
34013984 var ctx = try self.writeSymtab();
34023985 defer ctx.imports_table.deinit();
......@@ -3404,18 +3987,38 @@ fn writeSymtabs(self: *MachO) !void {
34043987 try self.writeStrtab();
34053988}
34063989
3990fn addLocalToSymtab(self: *MachO, sym_loc: SymbolWithLoc, locals: *std.ArrayList(macho.nlist_64)) !void {
3991 const sym = self.getSymbol(sym_loc);
3992 if (sym.n_strx == 0) return; // no name, skip
3993 if (sym.n_desc == MachO.N_DEAD) return; // garbage-collected, skip
3994 if (sym.ext()) return; // an export lands in its own symtab section, skip
3995 if (self.symbolIsTemp(sym_loc)) return; // local temp symbol, skip
3996 var out_sym = sym;
3997 out_sym.n_strx = try self.strtab.insert(self.base.allocator, self.getSymbolName(sym_loc));
3998 try locals.append(out_sym);
3999}
4000
34074001fn writeSymtab(self: *MachO) !SymtabCtx {
34084002 const gpa = self.base.allocator;
34094003
34104004 var locals = std.ArrayList(macho.nlist_64).init(gpa);
34114005 defer locals.deinit();
34124006
3413 for (self.locals.items, 0..) |sym, sym_id| {
3414 if (sym.n_strx == 0) continue; // no name, skip
3415 const sym_loc = SymbolWithLoc{ .sym_index = @as(u32, @intCast(sym_id)) };
3416 if (self.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
3417 if (self.getGlobal(self.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip
3418 try locals.append(sym);
4007 for (0..self.locals.items) |sym_id| {
4008 try self.addLocalToSymtab(.{ .sym_index = @intCast(sym_id) });
4009 }
4010
4011 for (self.objects.items) |object| {
4012 for (object.atoms.items) |atom_index| {
4013 const atom = self.getAtom(atom_index);
4014 const sym_loc = atom.getSymbolWithLoc();
4015 try self.addLocalToSymtab(sym_loc, &locals);
4016
4017 var it = Atom.getInnerSymbolsIterator(self, atom_index);
4018 while (it.next()) |inner_sym_loc| {
4019 try self.addLocalToSymtab(inner_sym_loc, &locals);
4020 }
4021 }
34194022 }
34204023
34214024 var exports = std.ArrayList(macho.nlist_64).init(gpa);
......@@ -3424,6 +4027,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
34244027 for (self.globals.items) |global| {
34254028 const sym = self.getSymbol(global);
34264029 if (sym.undf()) continue; // import, skip
4030 if (sym.n_desc == N_DEAD) continue;
34274031 var out_sym = sym;
34284032 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
34294033 try exports.append(out_sym);
......@@ -3438,6 +4042,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
34384042 const sym = self.getSymbol(global);
34394043 if (sym.n_strx == 0) continue; // no name, skip
34404044 if (!sym.undf()) continue; // not an import, skip
4045 if (sym.n_desc == N_DEAD) continue;
34414046 const new_index = @as(u32, @intCast(imports.items.len));
34424047 var out_sym = sym;
34434048 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
......@@ -3445,6 +4050,15 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
34454050 try imports_table.putNoClobber(global, new_index);
34464051 }
34474052
4053 // We generate stabs last in order to ensure that the strtab always has debug info
4054 // strings trailing
4055 if (!self.base.options.strip) {
4056 assert(self.d_sym == null); // TODO
4057 for (self.objects.items) |object| {
4058 try self.generateSymbolStabs(object, &locals);
4059 }
4060 }
4061
34484062 const nlocals = @as(u32, @intCast(locals.items.len));
34494063 const nexports = @as(u32, @intCast(exports.items.len));
34504064 const nimports = @as(u32, @intCast(imports.items.len));
......@@ -3478,7 +4092,218 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
34784092 };
34794093}
34804094
3481fn writeStrtab(self: *MachO) !void {
4095fn generateSymbolStabs(
4096 self: *MachO,
4097 object: Object,
4098 locals: *std.ArrayList(macho.nlist_64),
4099) !void {
4100 log.debug("generating stabs for '{s}'", .{object.name});
4101
4102 const gpa = self.base.allocator;
4103 var debug_info = object.parseDwarfInfo();
4104
4105 var lookup = DwarfInfo.AbbrevLookupTable.init(gpa);
4106 defer lookup.deinit();
4107 try lookup.ensureUnusedCapacity(std.math.maxInt(u8));
4108
4109 // We assume there is only one CU.
4110 var cu_it = debug_info.getCompileUnitIterator();
4111 const compile_unit = while (try cu_it.next()) |cu| {
4112 const offset = math.cast(usize, cu.cuh.debug_abbrev_offset) orelse return error.Overflow;
4113 try debug_info.genAbbrevLookupByKind(offset, &lookup);
4114 break cu;
4115 } else {
4116 log.debug("no compile unit found in debug info in {s}; skipping", .{object.name});
4117 return;
4118 };
4119
4120 var abbrev_it = compile_unit.getAbbrevEntryIterator(debug_info);
4121 const cu_entry: DwarfInfo.AbbrevEntry = while (try abbrev_it.next(lookup)) |entry| switch (entry.tag) {
4122 dwarf.TAG.compile_unit => break entry,
4123 else => continue,
4124 } else {
4125 log.debug("missing DWARF_TAG_compile_unit tag in {s}; skipping", .{object.name});
4126 return;
4127 };
4128
4129 var maybe_tu_name: ?[]const u8 = null;
4130 var maybe_tu_comp_dir: ?[]const u8 = null;
4131 var attr_it = cu_entry.getAttributeIterator(debug_info, compile_unit.cuh);
4132
4133 while (try attr_it.next()) |attr| switch (attr.name) {
4134 dwarf.AT.comp_dir => maybe_tu_comp_dir = attr.getString(debug_info, compile_unit.cuh) orelse continue,
4135 dwarf.AT.name => maybe_tu_name = attr.getString(debug_info, compile_unit.cuh) orelse continue,
4136 else => continue,
4137 };
4138
4139 if (maybe_tu_name == null or maybe_tu_comp_dir == null) {
4140 log.debug("missing DWARF_AT_comp_dir and DWARF_AT_name attributes {s}; skipping", .{object.name});
4141 return;
4142 }
4143
4144 const tu_name = maybe_tu_name.?;
4145 const tu_comp_dir = maybe_tu_comp_dir.?;
4146
4147 // Open scope
4148 try locals.ensureUnusedCapacity(3);
4149 locals.appendAssumeCapacity(.{
4150 .n_strx = try self.strtab.insert(gpa, tu_comp_dir),
4151 .n_type = macho.N_SO,
4152 .n_sect = 0,
4153 .n_desc = 0,
4154 .n_value = 0,
4155 });
4156 locals.appendAssumeCapacity(.{
4157 .n_strx = try self.strtab.insert(gpa, tu_name),
4158 .n_type = macho.N_SO,
4159 .n_sect = 0,
4160 .n_desc = 0,
4161 .n_value = 0,
4162 });
4163 locals.appendAssumeCapacity(.{
4164 .n_strx = try self.strtab.insert(gpa, object.name),
4165 .n_type = macho.N_OSO,
4166 .n_sect = 0,
4167 .n_desc = 1,
4168 .n_value = object.mtime,
4169 });
4170
4171 var stabs_buf: [4]macho.nlist_64 = undefined;
4172
4173 var name_lookup: ?DwarfInfo.SubprogramLookupByName = if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS == 0) blk: {
4174 var name_lookup = DwarfInfo.SubprogramLookupByName.init(gpa);
4175 errdefer name_lookup.deinit();
4176 try name_lookup.ensureUnusedCapacity(@as(u32, @intCast(object.atoms.items.len)));
4177 try debug_info.genSubprogramLookupByName(compile_unit, lookup, &name_lookup);
4178 break :blk name_lookup;
4179 } else null;
4180 defer if (name_lookup) |*nl| nl.deinit();
4181
4182 for (object.atoms.items) |atom_index| {
4183 const atom = self.getAtom(atom_index);
4184 const stabs = try self.generateSymbolStabsForSymbol(
4185 atom_index,
4186 atom.getSymbolWithLoc(),
4187 name_lookup,
4188 &stabs_buf,
4189 );
4190 try locals.appendSlice(stabs);
4191
4192 var it = Atom.getInnerSymbolsIterator(self, atom_index);
4193 while (it.next()) |sym_loc| {
4194 const contained_stabs = try self.generateSymbolStabsForSymbol(
4195 atom_index,
4196 sym_loc,
4197 name_lookup,
4198 &stabs_buf,
4199 );
4200 try locals.appendSlice(contained_stabs);
4201 }
4202 }
4203
4204 // Close scope
4205 try locals.append(.{
4206 .n_strx = 0,
4207 .n_type = macho.N_SO,
4208 .n_sect = 0,
4209 .n_desc = 0,
4210 .n_value = 0,
4211 });
4212}
4213
4214fn generateSymbolStabsForSymbol(
4215 self: *MachO,
4216 atom_index: Atom.Index,
4217 sym_loc: SymbolWithLoc,
4218 lookup: ?DwarfInfo.SubprogramLookupByName,
4219 buf: *[4]macho.nlist_64,
4220) ![]const macho.nlist_64 {
4221 const gpa = self.base.allocator;
4222 const object = self.objects.items[sym_loc.getFile().?];
4223 const sym = self.getSymbol(sym_loc);
4224 const sym_name = self.getSymbolName(sym_loc);
4225 const header = self.sections.items(.header)[sym.n_sect - 1];
4226
4227 if (sym.n_strx == 0) return buf[0..0];
4228 if (self.symbolIsTemp(sym_loc)) return buf[0..0];
4229
4230 if (!header.isCode()) {
4231 // Since we are not dealing with machine code, it's either a global or a static depending
4232 // on the linkage scope.
4233 if (sym.sect() and sym.ext()) {
4234 // Global gets an N_GSYM stab type.
4235 buf[0] = .{
4236 .n_strx = try self.strtab.insert(gpa, sym_name),
4237 .n_type = macho.N_GSYM,
4238 .n_sect = sym.n_sect,
4239 .n_desc = 0,
4240 .n_value = 0,
4241 };
4242 } else {
4243 // Local static gets an N_STSYM stab type.
4244 buf[0] = .{
4245 .n_strx = try self.strtab.insert(gpa, sym_name),
4246 .n_type = macho.N_STSYM,
4247 .n_sect = sym.n_sect,
4248 .n_desc = 0,
4249 .n_value = sym.n_value,
4250 };
4251 }
4252 return buf[0..1];
4253 }
4254
4255 const size: u64 = size: {
4256 if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) {
4257 break :size self.getAtom(atom_index).size;
4258 }
4259
4260 // Since we don't have subsections to work with, we need to infer the size of each function
4261 // the slow way by scanning the debug info for matching symbol names and extracting
4262 // the symbol's DWARF_AT_low_pc and DWARF_AT_high_pc values.
4263 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
4264 const subprogram = lookup.?.get(sym_name[1..]) orelse return buf[0..0];
4265
4266 if (subprogram.addr <= source_sym.n_value and source_sym.n_value < subprogram.addr + subprogram.size) {
4267 break :size subprogram.size;
4268 } else {
4269 log.debug("no stab found for {s}", .{sym_name});
4270 return buf[0..0];
4271 }
4272 };
4273
4274 buf[0] = .{
4275 .n_strx = 0,
4276 .n_type = macho.N_BNSYM,
4277 .n_sect = sym.n_sect,
4278 .n_desc = 0,
4279 .n_value = sym.n_value,
4280 };
4281 buf[1] = .{
4282 .n_strx = try self.strtab.insert(gpa, sym_name),
4283 .n_type = macho.N_FUN,
4284 .n_sect = sym.n_sect,
4285 .n_desc = 0,
4286 .n_value = sym.n_value,
4287 };
4288 buf[2] = .{
4289 .n_strx = 0,
4290 .n_type = macho.N_FUN,
4291 .n_sect = 0,
4292 .n_desc = 0,
4293 .n_value = size,
4294 };
4295 buf[3] = .{
4296 .n_strx = 0,
4297 .n_type = macho.N_ENSYM,
4298 .n_sect = sym.n_sect,
4299 .n_desc = 0,
4300 .n_value = size,
4301 };
4302
4303 return buf;
4304}
4305
4306pub fn writeStrtab(self: *MachO) !void {
34824307 const gpa = self.base.allocator;
34834308 const seg = self.getLinkeditSegmentPtr();
34844309 const offset = seg.fileoff + seg.filesize;
......@@ -3507,7 +4332,7 @@ const SymtabCtx = struct {
35074332 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
35084333};
35094334
3510fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
4335pub fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
35114336 const gpa = self.base.allocator;
35124337 const nstubs = @as(u32, @intCast(self.stub_table.lookup.count()));
35134338 const ngot_entries = @as(u32, @intCast(self.got_table.lookup.count()));
......@@ -3582,7 +4407,7 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
35824407 self.dysymtab_cmd.nindirectsyms = nindirectsyms;
35834408}
35844409
3585fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, has_codesig: bool) !void {
4410pub fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, has_codesig: bool) !void {
35864411 const file_size = if (!has_codesig) blk: {
35874412 const seg = self.getLinkeditSegmentPtr();
35884413 break :blk seg.fileoff + seg.filesize;
......@@ -3592,7 +4417,7 @@ fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, has_c
35924417 try self.base.file.?.pwriteAll(&self.uuid_cmd.uuid, offset);
35934418}
35944419
3595fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
4420pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
35964421 const seg = self.getLinkeditSegmentPtr();
35974422 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
35984423 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
......@@ -3609,8 +4434,9 @@ fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
36094434 self.codesig_cmd.datasize = @as(u32, @intCast(needed_size));
36104435}
36114436
3612fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSignature) !void {
3613 const seg = self.getSegment(self.text_section_index.?);
4437pub fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSignature) !void {
4438 const seg_id = self.header_segment_cmd_index.?;
4439 const seg = self.segments.items[seg_id];
36144440 const offset = self.codesig_cmd.dataoff;
36154441
36164442 var buffer = std.ArrayList(u8).init(self.base.allocator);
......@@ -3634,14 +4460,10 @@ fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSig
36344460}
36354461
36364462/// Writes Mach-O file header.
3637fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
4463pub fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
36384464 var header: macho.mach_header_64 = .{};
36394465 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
36404466
3641 if (!self.base.options.single_threaded) {
3642 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
3643 }
3644
36454467 switch (self.base.options.target.cpu.arch) {
36464468 .aarch64 => {
36474469 header.cputype = macho.CPU_TYPE_ARM64;
......@@ -3666,6 +4488,13 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
36664488 else => unreachable,
36674489 }
36684490
4491 if (self.thread_vars_section_index) |sect_id| {
4492 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
4493 if (self.sections.items(.header)[sect_id].size > 0) {
4494 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
4495 }
4496 }
4497
36694498 header.ncmds = ncmds;
36704499 header.sizeofcmds = sizeofcmds;
36714500
......@@ -3830,20 +4659,33 @@ pub fn symbolIsTemp(self: *MachO, sym_with_loc: SymbolWithLoc) bool {
38304659
38314660/// Returns pointer-to-symbol described by `sym_with_loc` descriptor.
38324661pub fn getSymbolPtr(self: *MachO, sym_with_loc: SymbolWithLoc) *macho.nlist_64 {
3833 assert(sym_with_loc.getFile() == null);
3834 return &self.locals.items[sym_with_loc.sym_index];
4662 if (sym_with_loc.getFile()) |file| {
4663 const object = &self.objects.items[file];
4664 return &object.symtab[sym_with_loc.sym_index];
4665 } else {
4666 return &self.locals.items[sym_with_loc.sym_index];
4667 }
38354668}
38364669
38374670/// Returns symbol described by `sym_with_loc` descriptor.
38384671pub fn getSymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
3839 assert(sym_with_loc.getFile() == null);
3840 return self.locals.items[sym_with_loc.sym_index];
4672 if (sym_with_loc.getFile()) |file| {
4673 const object = &self.objects.items[file];
4674 return object.symtab[sym_with_loc.sym_index];
4675 } else {
4676 return self.locals.items[sym_with_loc.sym_index];
4677 }
38414678}
38424679
38434680/// Returns name of the symbol described by `sym_with_loc` descriptor.
38444681pub fn getSymbolName(self: *const MachO, sym_with_loc: SymbolWithLoc) []const u8 {
3845 const sym = self.getSymbol(sym_with_loc);
3846 return self.strtab.get(sym.n_strx).?;
4682 if (sym_with_loc.getFile()) |file| {
4683 const object = self.objects.items[file];
4684 return object.getSymbolName(sym_with_loc.sym_index);
4685 } else {
4686 const sym = self.locals.items[sym_with_loc.sym_index];
4687 return self.strtab.get(sym.n_strx).?;
4688 }
38474689}
38484690
38494691/// Returns pointer to the global entry for `name` if one exists.
......@@ -3945,6 +4787,19 @@ pub inline fn getPageSize(cpu_arch: std.Target.Cpu.Arch) u16 {
39454787 };
39464788}
39474789
4790pub inline fn requiresThunks(self: MachO) bool {
4791 return self.base.options.target.cpu.arch == .aarch64;
4792}
4793
4794pub fn requiresCodeSignature(self: MachO) bool {
4795 if (self.base.options.entitlements) |_| return true;
4796 const cpu_arch = self.base.options.target.cpu.arch;
4797 const os_tag = self.base.options.target.os.tag;
4798 const abi = self.base.options.target.abi;
4799 if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) return true;
4800 return false;
4801}
4802
39484803pub fn getSegmentPrecedence(segname: []const u8) u4 {
39494804 if (mem.eql(u8, segname, "__PAGEZERO")) return 0x0;
39504805 if (mem.eql(u8, segname, "__TEXT")) return 0x1;
......@@ -3988,24 +4843,26 @@ pub fn getSectionPrecedence(header: macho.section_64) u8 {
39884843 return (@as(u8, @intCast(segment_precedence)) << 4) + section_precedence;
39894844}
39904845
3991pub fn reportUndefined(self: *MachO, ctx: anytype) !void {
3992 const count = ctx.unresolved.count();
4846pub fn reportUndefined(self: *MachO) !void {
4847 const count = self.unresolved.count();
39934848 if (count == 0) return;
39944849
39954850 const gpa = self.base.allocator;
39964851
39974852 try self.misc_errors.ensureUnusedCapacity(gpa, count);
39984853
3999 for (ctx.unresolved.keys()) |global_index| {
4000 const global = ctx.globals.items[global_index];
4001 const sym_name = ctx.getSymbolName(global);
4854 for (self.unresolved.keys()) |global_index| {
4855 const global = self.globals.items[global_index];
4856 const sym_name = self.getSymbolName(global);
40024857
40034858 const nnotes: usize = if (global.getFile() == null) @as(usize, 0) else 1;
40044859 var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, nnotes);
40054860 defer notes.deinit();
40064861
40074862 if (global.getFile()) |file| {
4008 const note = try std.fmt.allocPrint(gpa, "referenced in {s}", .{ctx.objects.items[file].name});
4863 const note = try std.fmt.allocPrint(gpa, "referenced in {s}", .{
4864 self.objects.items[file].name,
4865 });
40094866 notes.appendAssumeCapacity(.{ .msg = note });
40104867 }
40114868
......@@ -4051,6 +4908,19 @@ pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytyp
40514908 return i;
40524909}
40534910
4911pub fn logSegments(self: *MachO) void {
4912 log.debug("segments:", .{});
4913 for (self.segments.items, 0..) |segment, i| {
4914 log.debug(" segment({d}): {s} @{x} ({x}), sizeof({x})", .{
4915 i,
4916 segment.segName(),
4917 segment.fileoff,
4918 segment.vmaddr,
4919 segment.vmsize,
4920 });
4921 }
4922}
4923
40544924pub fn logSections(self: *MachO) void {
40554925 log.debug("sections:", .{});
40564926 for (self.sections.items(.header), 0..) |header, i| {
......@@ -4065,9 +4935,7 @@ pub fn logSections(self: *MachO) void {
40654935 }
40664936}
40674937
4068fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 {
4069 @memset(buf[0..4], '_');
4070 @memset(buf[4..], ' ');
4938fn logSymAttributes(sym: macho.nlist_64, buf: []u8) []const u8 {
40714939 if (sym.sect()) {
40724940 buf[0] = 's';
40734941 }
......@@ -4090,56 +4958,110 @@ fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 {
40904958pub fn logSymtab(self: *MachO) void {
40914959 var buf: [4]u8 = undefined;
40924960
4093 log.debug("symtab:", .{});
4961 const scoped_log = std.log.scoped(.symtab);
4962
4963 scoped_log.debug("locals:", .{});
4964 for (self.objects.items, 0..) |object, id| {
4965 scoped_log.debug(" object({d}): {s}", .{ id, object.name });
4966 if (object.in_symtab == null) continue;
4967 for (object.symtab, 0..) |sym, sym_id| {
4968 @memset(&buf, '_');
4969 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{
4970 sym_id,
4971 object.getSymbolName(@as(u32, @intCast(sym_id))),
4972 sym.n_value,
4973 sym.n_sect,
4974 logSymAttributes(sym, &buf),
4975 });
4976 }
4977 }
4978 scoped_log.debug(" object(-1)", .{});
40944979 for (self.locals.items, 0..) |sym, sym_id| {
4095 const where = if (sym.undf() and !sym.tentative()) "ord" else "sect";
4096 const def_index = if (sym.undf() and !sym.tentative())
4097 @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER)
4098 else
4099 sym.n_sect + 1;
4100 log.debug(" %{d}: {?s} @{x} in {s}({d}), {s}", .{
4980 if (sym.undf()) continue;
4981 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{
41014982 sym_id,
4102 self.strtab.get(sym.n_strx),
4983 self.strtab.get(sym.n_strx).?,
41034984 sym.n_value,
4104 where,
4105 def_index,
4985 sym.n_sect,
41064986 logSymAttributes(sym, &buf),
41074987 });
41084988 }
41094989
4110 log.debug("globals table:", .{});
4111 for (self.globals.items) |global| {
4112 const name = self.getSymbolName(global);
4113 log.debug(" {s} => %{d} in object({?d})", .{ name, global.sym_index, global.file });
4990 scoped_log.debug("exports:", .{});
4991 for (self.globals.items, 0..) |global, i| {
4992 const sym = self.getSymbol(global);
4993 if (sym.undf()) continue;
4994 if (sym.n_desc == MachO.N_DEAD) continue;
4995 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s} (def in object({?}))", .{
4996 i,
4997 self.getSymbolName(global),
4998 sym.n_value,
4999 sym.n_sect,
5000 logSymAttributes(sym, &buf),
5001 global.file,
5002 });
5003 }
5004
5005 scoped_log.debug("imports:", .{});
5006 for (self.globals.items, 0..) |global, i| {
5007 const sym = self.getSymbol(global);
5008 if (!sym.undf()) continue;
5009 if (sym.n_desc == MachO.N_DEAD) continue;
5010 const ord = @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER);
5011 scoped_log.debug(" %{d}: {s} @{x} in ord({d}), {s}", .{
5012 i,
5013 self.getSymbolName(global),
5014 sym.n_value,
5015 ord,
5016 logSymAttributes(sym, &buf),
5017 });
41145018 }
41155019
4116 log.debug("GOT entries:", .{});
4117 log.debug("{}", .{self.got_table});
5020 scoped_log.debug("GOT entries:", .{});
5021 scoped_log.debug("{}", .{self.got_table});
5022
5023 scoped_log.debug("TLV pointers:", .{});
5024 scoped_log.debug("{}", .{self.tlv_ptr_table});
41185025
4119 log.debug("stubs entries:", .{});
4120 log.debug("{}", .{self.stub_table});
5026 scoped_log.debug("stubs entries:", .{});
5027 scoped_log.debug("{}", .{self.stubs_table});
5028
5029 scoped_log.debug("thunks:", .{});
5030 for (self.thunks.items, 0..) |thunk, i| {
5031 scoped_log.debug(" thunk({d})", .{i});
5032 const slice = thunk.targets.slice();
5033 for (slice.items(.tag), slice.items(.target), 0..) |tag, target, j| {
5034 const atom_index = @as(u32, @intCast(thunk.getStartAtomIndex() + j));
5035 const atom = self.getAtom(atom_index);
5036 const atom_sym = self.getSymbol(atom.getSymbolWithLoc());
5037 const target_addr = switch (tag) {
5038 .stub => self.getStubsEntryAddress(target).?,
5039 .atom => self.getSymbol(target).n_value,
5040 };
5041 scoped_log.debug(" {d}@{x} => {s}({s}@{x})", .{
5042 j,
5043 atom_sym.n_value,
5044 @tagName(tag),
5045 self.getSymbolName(target),
5046 target_addr,
5047 });
5048 }
5049 }
41215050}
41225051
41235052pub fn logAtoms(self: *MachO) void {
41245053 log.debug("atoms:", .{});
4125
41265054 const slice = self.sections.slice();
4127 for (slice.items(.last_atom_index), 0..) |last_atom_index, i| {
4128 var atom_index = last_atom_index orelse continue;
4129 const header = slice.items(.header)[i];
4130
4131 while (true) {
4132 const atom = self.getAtom(atom_index);
4133 if (atom.prev_index) |prev_index| {
4134 atom_index = prev_index;
4135 } else break;
4136 }
5055 for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| {
5056 var atom_index = first_atom_index orelse continue;
5057 const header = slice.items(.header)[sect_id];
41375058
41385059 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
41395060
41405061 while (true) {
4141 self.logAtom(atom_index);
41425062 const atom = self.getAtom(atom_index);
5063 self.logAtom(atom_index, log);
5064
41435065 if (atom.next_index) |next_index| {
41445066 atom_index = next_index;
41455067 } else break;
......@@ -4147,18 +5069,50 @@ pub fn logAtoms(self: *MachO) void {
41475069 }
41485070}
41495071
4150pub fn logAtom(self: *MachO, atom_index: Atom.Index) void {
5072pub fn logAtom(self: *MachO, atom_index: Atom.Index, logger: anytype) void {
5073 if (!build_options.enable_logging) return;
5074
41515075 const atom = self.getAtom(atom_index);
4152 const sym = atom.getSymbol(self);
4153 const sym_name = atom.getName(self);
4154 log.debug(" ATOM(%{?d}, '{s}') @ {x} sizeof({x}) in object({?d}) in sect({d})", .{
4155 atom.getSymbolIndex(),
5076 const sym = self.getSymbol(atom.getSymbolWithLoc());
5077 const sym_name = self.getSymbolName(atom.getSymbolWithLoc());
5078 logger.debug(" ATOM({d}, %{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?}) in sect({d})", .{
5079 atom_index,
5080 atom.sym_index,
41565081 sym_name,
41575082 sym.n_value,
41585083 atom.size,
4159 atom.file,
4160 sym.n_sect + 1,
5084 atom.alignment,
5085 atom.getFile(),
5086 sym.n_sect,
41615087 });
5088
5089 if (atom.getFile() != null) {
5090 var it = Atom.getInnerSymbolsIterator(self, atom_index);
5091 while (it.next()) |sym_loc| {
5092 const inner = self.getSymbol(sym_loc);
5093 const inner_name = self.getSymbolName(sym_loc);
5094 const offset = Atom.calcInnerSymbolOffset(self, atom_index, sym_loc.sym_index);
5095
5096 logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{
5097 sym_loc.sym_index,
5098 inner_name,
5099 inner.n_value,
5100 offset,
5101 });
5102 }
5103
5104 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
5105 const alias = self.getSymbol(sym_loc);
5106 const alias_name = self.getSymbolName(sym_loc);
5107
5108 logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{
5109 sym_loc.sym_index,
5110 alias_name,
5111 alias.n_value,
5112 0,
5113 });
5114 }
5115 }
41625116}
41635117
41645118const MachO = @This();
......@@ -4197,6 +5151,7 @@ const Cache = std.Build.Cache;
41975151const CodeSignature = @import("MachO/CodeSignature.zig");
41985152const Compilation = @import("../Compilation.zig");
41995153const Dwarf = File.Dwarf;
5154const DwarfInfo = @import("DwarfInfo.zig");
42005155const Dylib = @import("MachO/Dylib.zig");
42015156const File = link.File;
42025157const Object = @import("MachO/Object.zig");
src/link/MachO/Archive.zig+12-12
......@@ -1,15 +1,3 @@
1const Archive = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const fs = std.fs;
6const log = std.log.scoped(.link);
7const macho = std.macho;
8const mem = std.mem;
9
10const Allocator = mem.Allocator;
11const Object = @import("Object.zig");
12
131file: fs.File,
142fat_offset: u64,
153name: []const u8,
......@@ -215,3 +203,15 @@ pub fn parseObject(self: Archive, gpa: Allocator, offset: u32) !Object {
215203
216204 return object;
217205}
206
207const Archive = @This();
208
209const std = @import("std");
210const assert = std.debug.assert;
211const fs = std.fs;
212const log = std.log.scoped(.link);
213const macho = std.macho;
214const mem = std.mem;
215
216const Allocator = mem.Allocator;
217const Object = @import("Object.zig");
src/link/MachO/Atom.zig+174-220
......@@ -105,8 +105,7 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
105105 return surplus >= MachO.min_text_capacity;
106106}
107107
108pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {
109 const gpa = zld.gpa;
108pub fn getOutputSection(macho_file: *MachO, sect: macho.section_64) !?u8 {
110109 const segname = sect.segName();
111110 const sectname = sect.sectName();
112111 const res: ?u8 = blk: {
......@@ -126,20 +125,14 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {
126125 }
127126
128127 if (sect.isCode()) {
129 if (zld.text_section_index == null) {
130 zld.text_section_index = try MachO.initSection(
131 gpa,
132 zld,
133 "__TEXT",
134 "__text",
135 .{
136 .flags = macho.S_REGULAR |
137 macho.S_ATTR_PURE_INSTRUCTIONS |
138 macho.S_ATTR_SOME_INSTRUCTIONS,
139 },
140 );
128 if (macho_file.text_section_index == null) {
129 macho_file.text_section_index = try macho_file.initSection("__TEXT", "__text", .{
130 .flags = macho.S_REGULAR |
131 macho.S_ATTR_PURE_INSTRUCTIONS |
132 macho.S_ATTR_SOME_INSTRUCTIONS,
133 });
141134 }
142 break :blk zld.text_section_index.?;
135 break :blk macho_file.text_section_index.?;
143136 }
144137
145138 if (sect.isDebug()) {
......@@ -151,42 +144,26 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {
151144 macho.S_8BYTE_LITERALS,
152145 macho.S_16BYTE_LITERALS,
153146 => {
154 break :blk zld.getSectionByName("__TEXT", "__const") orelse try MachO.initSection(
155 gpa,
156 zld,
157 "__TEXT",
158 "__const",
159 .{},
160 );
147 break :blk macho_file.getSectionByName("__TEXT", "__const") orelse
148 try macho_file.initSection("__TEXT", "__const", .{});
161149 },
162150 macho.S_CSTRING_LITERALS => {
163151 if (mem.startsWith(u8, sectname, "__objc")) {
164 break :blk zld.getSectionByName(segname, sectname) orelse try MachO.initSection(
165 gpa,
166 zld,
167 segname,
168 sectname,
169 .{},
170 );
152 break :blk macho_file.getSectionByName(segname, sectname) orelse
153 try macho_file.initSection(segname, sectname, .{});
171154 }
172 break :blk zld.getSectionByName("__TEXT", "__cstring") orelse try MachO.initSection(
173 gpa,
174 zld,
175 "__TEXT",
176 "__cstring",
177 .{ .flags = macho.S_CSTRING_LITERALS },
178 );
155 break :blk macho_file.getSectionByName("__TEXT", "__cstring") orelse
156 try macho_file.initSection("__TEXT", "__cstring", .{
157 .flags = macho.S_CSTRING_LITERALS,
158 });
179159 },
180160 macho.S_MOD_INIT_FUNC_POINTERS,
181161 macho.S_MOD_TERM_FUNC_POINTERS,
182162 => {
183 break :blk zld.getSectionByName("__DATA_CONST", sectname) orelse try MachO.initSection(
184 gpa,
185 zld,
186 "__DATA_CONST",
187 sectname,
188 .{ .flags = sect.flags },
189 );
163 break :blk macho_file.getSectionByName("__DATA_CONST", sectname) orelse
164 try macho_file.initSection("__DATA_CONST", sectname, .{
165 .flags = sect.flags,
166 });
190167 },
191168 macho.S_LITERAL_POINTERS,
192169 macho.S_ZEROFILL,
......@@ -195,23 +172,14 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {
195172 macho.S_THREAD_LOCAL_REGULAR,
196173 macho.S_THREAD_LOCAL_ZEROFILL,
197174 => {
198 break :blk zld.getSectionByName(segname, sectname) orelse try MachO.initSection(
199 gpa,
200 zld,
201 segname,
202 sectname,
203 .{ .flags = sect.flags },
204 );
175 break :blk macho_file.getSectionByName(segname, sectname) orelse
176 try macho_file.initSection(segname, sectname, .{
177 .flags = sect.flags,
178 });
205179 },
206180 macho.S_COALESCED => {
207 break :blk zld.getSectionByName(segname, sectname) orelse try MachO.initSection(
208 gpa,
209 zld,
210
211 segname,
212 sectname,
213 .{},
214 );
181 break :blk macho_file.getSectionByName(segname, sectname) orelse
182 try macho_file.initSection(segname, sectname, .{});
215183 },
216184 macho.S_REGULAR => {
217185 if (mem.eql(u8, segname, "__TEXT")) {
......@@ -221,13 +189,8 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {
221189 mem.eql(u8, sectname, "__gosymtab") or
222190 mem.eql(u8, sectname, "__gopclntab"))
223191 {
224 break :blk zld.getSectionByName("__TEXT", sectname) orelse try MachO.initSection(
225 gpa,
226 zld,
227 "__TEXT",
228 sectname,
229 .{},
230 );
192 break :blk macho_file.getSectionByName("__TEXT", sectname) orelse
193 try macho_file.initSection("__TEXT", sectname, .{});
231194 }
232195 }
233196 if (mem.eql(u8, segname, "__DATA")) {
......@@ -236,33 +199,17 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {
236199 mem.eql(u8, sectname, "__objc_classlist") or
237200 mem.eql(u8, sectname, "__objc_imageinfo"))
238201 {
239 break :blk zld.getSectionByName("__DATA_CONST", sectname) orelse try MachO.initSection(
240 gpa,
241 zld,
242 "__DATA_CONST",
243 sectname,
244 .{},
245 );
202 break :blk macho_file.getSectionByName("__DATA_CONST", sectname) orelse
203 try macho_file.initSection("__DATA_CONST", sectname, .{});
246204 } else if (mem.eql(u8, sectname, "__data")) {
247 if (zld.data_section_index == null) {
248 zld.data_section_index = try MachO.initSection(
249 gpa,
250 zld,
251 "__DATA",
252 "__data",
253 .{},
254 );
205 if (macho_file.data_section_index == null) {
206 macho_file.data_section_index = try macho_file.initSection("__DATA", "__data", .{});
255207 }
256 break :blk zld.data_section_index.?;
208 break :blk macho_file.data_section_index.?;
257209 }
258210 }
259 break :blk zld.getSectionByName(segname, sectname) orelse try MachO.initSection(
260 gpa,
261 zld,
262 segname,
263 sectname,
264 .{},
265 );
211 break :blk macho_file.getSectionByName(segname, sectname) orelse
212 try macho_file.initSection(segname, sectname, .{});
266213 },
267214 else => break :blk null,
268215 }
......@@ -270,29 +217,29 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {
270217
271218 // TODO we can do this directly in the selection logic above.
272219 // Or is it not worth it?
273 if (zld.data_const_section_index == null) {
274 if (zld.getSectionByName("__DATA_CONST", "__const")) |index| {
275 zld.data_const_section_index = index;
220 if (macho_file.data_const_section_index == null) {
221 if (macho_file.getSectionByName("__DATA_CONST", "__const")) |index| {
222 macho_file.data_const_section_index = index;
276223 }
277224 }
278 if (zld.thread_vars_section_index == null) {
279 if (zld.getSectionByName("__DATA", "__thread_vars")) |index| {
280 zld.thread_vars_section_index = index;
225 if (macho_file.thread_vars_section_index == null) {
226 if (macho_file.getSectionByName("__DATA", "__thread_vars")) |index| {
227 macho_file.thread_vars_section_index = index;
281228 }
282229 }
283 if (zld.thread_data_section_index == null) {
284 if (zld.getSectionByName("__DATA", "__thread_data")) |index| {
285 zld.thread_data_section_index = index;
230 if (macho_file.thread_data_section_index == null) {
231 if (macho_file.getSectionByName("__DATA", "__thread_data")) |index| {
232 macho_file.thread_data_section_index = index;
286233 }
287234 }
288 if (zld.thread_bss_section_index == null) {
289 if (zld.getSectionByName("__DATA", "__thread_bss")) |index| {
290 zld.thread_bss_section_index = index;
235 if (macho_file.thread_bss_section_index == null) {
236 if (macho_file.getSectionByName("__DATA", "__thread_bss")) |index| {
237 macho_file.thread_bss_section_index = index;
291238 }
292239 }
293 if (zld.bss_section_index == null) {
294 if (zld.getSectionByName("__DATA", "__bss")) |index| {
295 zld.bss_section_index = index;
240 if (macho_file.bss_section_index == null) {
241 if (macho_file.getSectionByName("__DATA", "__bss")) |index| {
242 macho_file.bss_section_index = index;
296243 }
297244 }
298245
......@@ -383,8 +330,8 @@ const InnerSymIterator = struct {
383330
384331/// Returns an iterator over potentially contained symbols.
385332/// Panics when called on a synthetic Atom.
386pub fn getInnerSymbolsIterator(zld: *Zld, atom_index: Index) InnerSymIterator {
387 const atom = zld.getAtom(atom_index);
333pub fn getInnerSymbolsIterator(macho_file: *MachO, atom_index: Index) InnerSymIterator {
334 const atom = macho_file.getAtom(atom_index);
388335 assert(atom.getFile() != null);
389336 return .{
390337 .sym_index = atom.inner_sym_index,
......@@ -397,11 +344,11 @@ pub fn getInnerSymbolsIterator(zld: *Zld, atom_index: Index) InnerSymIterator {
397344/// An alias symbol is used to represent the start of an input section
398345/// if there were no symbols defined within that range.
399346/// Alias symbols are only used on x86_64.
400pub fn getSectionAlias(zld: *Zld, atom_index: Index) ?SymbolWithLoc {
401 const atom = zld.getAtom(atom_index);
347pub fn getSectionAlias(macho_file: *MachO, atom_index: Index) ?SymbolWithLoc {
348 const atom = macho_file.getAtom(atom_index);
402349 assert(atom.getFile() != null);
403350
404 const object = zld.objects.items[atom.getFile().?];
351 const object = macho_file.objects.items[atom.getFile().?];
405352 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
406353 const ntotal = @as(u32, @intCast(object.symtab.len));
407354 var sym_index: u32 = nbase;
......@@ -418,13 +365,13 @@ pub fn getSectionAlias(zld: *Zld, atom_index: Index) ?SymbolWithLoc {
418365
419366/// Given an index into a contained symbol within, calculates an offset wrt
420367/// the start of this Atom.
421pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: Index, sym_index: u32) u64 {
422 const atom = zld.getAtom(atom_index);
368pub fn calcInnerSymbolOffset(macho_file: *MachO, atom_index: Index, sym_index: u32) u64 {
369 const atom = macho_file.getAtom(atom_index);
423370 assert(atom.getFile() != null);
424371
425372 if (atom.sym_index == sym_index) return 0;
426373
427 const object = zld.objects.items[atom.getFile().?];
374 const object = macho_file.objects.items[atom.getFile().?];
428375 const source_sym = object.getSourceSymbol(sym_index).?;
429376 const base_addr = if (object.getSourceSymbol(atom.sym_index)) |sym|
430377 sym.n_value
......@@ -437,14 +384,14 @@ pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: Index, sym_index: u32) u64 {
437384 return source_sym.n_value - base_addr;
438385}
439386
440pub fn scanAtomRelocs(zld: *Zld, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void {
441 const arch = zld.options.target.cpu.arch;
442 const atom = zld.getAtom(atom_index);
387pub fn scanAtomRelocs(macho_file: *MachO, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void {
388 const arch = macho_file.base.options.target.cpu.arch;
389 const atom = macho_file.getAtom(atom_index);
443390 assert(atom.getFile() != null); // synthetic atoms do not have relocs
444391
445392 return switch (arch) {
446 .aarch64 => scanAtomRelocsArm64(zld, atom_index, relocs),
447 .x86_64 => scanAtomRelocsX86(zld, atom_index, relocs),
393 .aarch64 => scanAtomRelocsArm64(macho_file, atom_index, relocs),
394 .x86_64 => scanAtomRelocsX86(macho_file, atom_index, relocs),
448395 else => unreachable,
449396 };
450397}
......@@ -454,11 +401,11 @@ const RelocContext = struct {
454401 base_offset: i32 = 0,
455402};
456403
457pub fn getRelocContext(zld: *Zld, atom_index: Index) RelocContext {
458 const atom = zld.getAtom(atom_index);
404pub fn getRelocContext(macho_file: *MachO, atom_index: Index) RelocContext {
405 const atom = macho_file.getAtom(atom_index);
459406 assert(atom.getFile() != null); // synthetic atoms do not have relocs
460407
461 const object = zld.objects.items[atom.getFile().?];
408 const object = macho_file.objects.items[atom.getFile().?];
462409 if (object.getSourceSymbol(atom.sym_index)) |source_sym| {
463410 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
464411 return .{
......@@ -475,7 +422,7 @@ pub fn getRelocContext(zld: *Zld, atom_index: Index) RelocContext {
475422 };
476423}
477424
478pub fn parseRelocTarget(zld: *Zld, ctx: struct {
425pub fn parseRelocTarget(macho_file: *MachO, ctx: struct {
479426 object_id: u32,
480427 rel: macho.relocation_info,
481428 code: []const u8,
......@@ -485,7 +432,7 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct {
485432 const tracy = trace(@src());
486433 defer tracy.end();
487434
488 const object = &zld.objects.items[ctx.object_id];
435 const object = &macho_file.objects.items[ctx.object_id];
489436 log.debug("parsing reloc target in object({d}) '{s}' ", .{ ctx.object_id, object.name });
490437
491438 const sym_index = if (ctx.rel.r_extern == 0) sym_index: {
......@@ -498,7 +445,7 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct {
498445 else
499446 mem.readIntLittle(u32, ctx.code[rel_offset..][0..4]);
500447 } else blk: {
501 assert(zld.options.target.cpu.arch == .x86_64);
448 assert(macho_file.base.options.target.cpu.arch == .x86_64);
502449 const correction: u3 = switch (@as(macho.reloc_type_x86_64, @enumFromInt(ctx.rel.r_type))) {
503450 .X86_64_RELOC_SIGNED => 0,
504451 .X86_64_RELOC_SIGNED_1 => 1,
......@@ -517,35 +464,39 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct {
517464 } else object.reverse_symtab_lookup[ctx.rel.r_symbolnum];
518465
519466 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = ctx.object_id + 1 };
520 const sym = zld.getSymbol(sym_loc);
467 const sym = macho_file.getSymbol(sym_loc);
521468 const target = if (sym.sect() and !sym.ext())
522469 sym_loc
523470 else if (object.getGlobal(sym_index)) |global_index|
524 zld.globals.items[global_index]
471 macho_file.globals.items[global_index]
525472 else
526473 sym_loc;
527474 log.debug(" | target %{d} ('{s}') in object({?d})", .{
528475 target.sym_index,
529 zld.getSymbolName(target),
476 macho_file.getSymbolName(target),
530477 target.getFile(),
531478 });
532479 return target;
533480}
534481
535pub fn getRelocTargetAtomIndex(zld: *Zld, target: SymbolWithLoc) ?Index {
482pub fn getRelocTargetAtomIndex(macho_file: *MachO, target: SymbolWithLoc) ?Index {
536483 if (target.getFile() == null) {
537 const target_sym_name = zld.getSymbolName(target);
484 const target_sym_name = macho_file.getSymbolName(target);
538485 if (mem.eql(u8, "__mh_execute_header", target_sym_name)) return null;
539486 if (mem.eql(u8, "___dso_handle", target_sym_name)) return null;
540487
541488 unreachable; // referenced symbol not found
542489 }
543490
544 const object = zld.objects.items[target.getFile().?];
491 const object = macho_file.objects.items[target.getFile().?];
545492 return object.getAtomIndexForSymbol(target.sym_index);
546493}
547494
548fn scanAtomRelocsArm64(zld: *Zld, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void {
495fn scanAtomRelocsArm64(
496 macho_file: *MachO,
497 atom_index: Index,
498 relocs: []align(1) const macho.relocation_info,
499) !void {
549500 for (relocs) |rel| {
550501 const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
551502
......@@ -556,8 +507,8 @@ fn scanAtomRelocsArm64(zld: *Zld, atom_index: Index, relocs: []align(1) const ma
556507
557508 if (rel.r_extern == 0) continue;
558509
559 const atom = zld.getAtom(atom_index);
560 const object = &zld.objects.items[atom.getFile().?];
510 const atom = macho_file.getAtom(atom_index);
511 const object = &macho_file.objects.items[atom.getFile().?];
561512 const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
562513 const sym_loc = SymbolWithLoc{
563514 .sym_index = sym_index,
......@@ -565,35 +516,39 @@ fn scanAtomRelocsArm64(zld: *Zld, atom_index: Index, relocs: []align(1) const ma
565516 };
566517
567518 const target = if (object.getGlobal(sym_index)) |global_index|
568 zld.globals.items[global_index]
519 macho_file.globals.items[global_index]
569520 else
570521 sym_loc;
571522
572523 switch (rel_type) {
573524 .ARM64_RELOC_BRANCH26 => {
574525 // TODO rewrite relocation
575 const sym = zld.getSymbol(target);
576 if (sym.undf()) try zld.addStubEntry(target);
526 const sym = macho_file.getSymbol(target);
527 if (sym.undf()) try macho_file.addStubEntry(target);
577528 },
578529 .ARM64_RELOC_GOT_LOAD_PAGE21,
579530 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
580531 .ARM64_RELOC_POINTER_TO_GOT,
581532 => {
582533 // TODO rewrite relocation
583 try zld.addGotEntry(target);
534 try macho_file.addGotEntry(target);
584535 },
585536 .ARM64_RELOC_TLVP_LOAD_PAGE21,
586537 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
587538 => {
588 const sym = zld.getSymbol(target);
589 if (sym.undf()) try zld.addTlvPtrEntry(target);
539 const sym = macho_file.getSymbol(target);
540 if (sym.undf()) try macho_file.addTlvPtrEntry(target);
590541 },
591542 else => {},
592543 }
593544 }
594545}
595546
596fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void {
547fn scanAtomRelocsX86(
548 macho_file: *MachO,
549 atom_index: Index,
550 relocs: []align(1) const macho.relocation_info,
551) !void {
597552 for (relocs) |rel| {
598553 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
599554
......@@ -604,8 +559,8 @@ fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const mach
604559
605560 if (rel.r_extern == 0) continue;
606561
607 const atom = zld.getAtom(atom_index);
608 const object = &zld.objects.items[atom.getFile().?];
562 const atom = macho_file.getAtom(atom_index);
563 const object = &macho_file.objects.items[atom.getFile().?];
609564 const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
610565 const sym_loc = SymbolWithLoc{
611566 .sym_index = sym_index,
......@@ -613,23 +568,23 @@ fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const mach
613568 };
614569
615570 const target = if (object.getGlobal(sym_index)) |global_index|
616 zld.globals.items[global_index]
571 macho_file.globals.items[global_index]
617572 else
618573 sym_loc;
619574
620575 switch (rel_type) {
621576 .X86_64_RELOC_BRANCH => {
622577 // TODO rewrite relocation
623 const sym = zld.getSymbol(target);
624 if (sym.undf()) try zld.addStubEntry(target);
578 const sym = macho_file.getSymbol(target);
579 if (sym.undf()) try macho_file.addStubEntry(target);
625580 },
626581 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => {
627582 // TODO rewrite relocation
628 try zld.addGotEntry(target);
583 try macho_file.addGotEntry(target);
629584 },
630585 .X86_64_RELOC_TLV => {
631 const sym = zld.getSymbol(target);
632 if (sym.undf()) try zld.addTlvPtrEntry(target);
586 const sym = macho_file.getSymbol(target);
587 if (sym.undf()) try macho_file.addTlvPtrEntry(target);
633588 },
634589 else => {},
635590 }
......@@ -637,53 +592,53 @@ fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const mach
637592}
638593
639594pub fn resolveRelocs(
640 zld: *Zld,
595 macho_file: *MachO,
641596 atom_index: Index,
642597 atom_code: []u8,
643598 atom_relocs: []align(1) const macho.relocation_info,
644599) !void {
645 const arch = zld.options.target.cpu.arch;
646 const atom = zld.getAtom(atom_index);
600 const arch = macho_file.base.options.target.cpu.arch;
601 const atom = macho_file.getAtom(atom_index);
647602 assert(atom.getFile() != null); // synthetic atoms do not have relocs
648603
649604 log.debug("resolving relocations in ATOM(%{d}, '{s}')", .{
650605 atom.sym_index,
651 zld.getSymbolName(atom.getSymbolWithLoc()),
606 macho_file.getSymbolName(atom.getSymbolWithLoc()),
652607 });
653608
654 const ctx = getRelocContext(zld, atom_index);
609 const ctx = getRelocContext(macho_file, atom_index);
655610
656611 return switch (arch) {
657 .aarch64 => resolveRelocsArm64(zld, atom_index, atom_code, atom_relocs, ctx),
658 .x86_64 => resolveRelocsX86(zld, atom_index, atom_code, atom_relocs, ctx),
612 .aarch64 => resolveRelocsArm64(macho_file, atom_index, atom_code, atom_relocs, ctx),
613 .x86_64 => resolveRelocsX86(macho_file, atom_index, atom_code, atom_relocs, ctx),
659614 else => unreachable,
660615 };
661616}
662617
663pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_tlv: bool) !u64 {
664 const target_atom_index = getRelocTargetAtomIndex(zld, target) orelse {
618pub fn getRelocTargetAddress(macho_file: *MachO, target: SymbolWithLoc, is_tlv: bool) !u64 {
619 const target_atom_index = getRelocTargetAtomIndex(macho_file, target) orelse {
665620 // If there is no atom for target, we still need to check for special, atom-less
666621 // symbols such as `___dso_handle`.
667 const target_name = zld.getSymbolName(target);
668 const atomless_sym = zld.getSymbol(target);
622 const target_name = macho_file.getSymbolName(target);
623 const atomless_sym = macho_file.getSymbol(target);
669624 log.debug(" | atomless target '{s}'", .{target_name});
670625 return atomless_sym.n_value;
671626 };
672 const target_atom = zld.getAtom(target_atom_index);
627 const target_atom = macho_file.getAtom(target_atom_index);
673628 log.debug(" | target ATOM(%{d}, '{s}') in object({?})", .{
674629 target_atom.sym_index,
675 zld.getSymbolName(target_atom.getSymbolWithLoc()),
630 macho_file.getSymbolName(target_atom.getSymbolWithLoc()),
676631 target_atom.getFile(),
677632 });
678633
679 const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());
634 const target_sym = macho_file.getSymbol(target_atom.getSymbolWithLoc());
680635 assert(target_sym.n_desc != MachO.N_DEAD);
681636
682637 // If `target` is contained within the target atom, pull its address value.
683638 const offset = if (target_atom.getFile() != null) blk: {
684 const object = zld.objects.items[target_atom.getFile().?];
639 const object = macho_file.objects.items[target_atom.getFile().?];
685640 break :blk if (object.getSourceSymbol(target.sym_index)) |_|
686 Atom.calcInnerSymbolOffset(zld, target_atom_index, target.sym_index)
641 Atom.calcInnerSymbolOffset(macho_file, target_atom_index, target.sym_index)
687642 else
688643 0; // section alias
689644 } else 0;
......@@ -694,9 +649,9 @@ pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_tlv: bool) !u6
694649 // * wrt to __thread_data if defined, then
695650 // * wrt to __thread_bss
696651 const sect_id: u16 = sect_id: {
697 if (zld.thread_data_section_index) |i| {
652 if (macho_file.thread_data_section_index) |i| {
698653 break :sect_id i;
699 } else if (zld.thread_bss_section_index) |i| {
654 } else if (macho_file.thread_bss_section_index) |i| {
700655 break :sect_id i;
701656 } else {
702657 log.err("threadlocal variables present but no initializer sections found", .{});
......@@ -705,20 +660,20 @@ pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_tlv: bool) !u6
705660 return error.FailedToResolveRelocationTarget;
706661 }
707662 };
708 break :base_address zld.sections.items(.header)[sect_id].addr;
663 break :base_address macho_file.sections.items(.header)[sect_id].addr;
709664 } else 0;
710665 return target_sym.n_value + offset - base_address;
711666}
712667
713668fn resolveRelocsArm64(
714 zld: *Zld,
669 macho_file: *MachO,
715670 atom_index: Index,
716671 atom_code: []u8,
717672 atom_relocs: []align(1) const macho.relocation_info,
718673 context: RelocContext,
719674) !void {
720 const atom = zld.getAtom(atom_index);
721 const object = zld.objects.items[atom.getFile().?];
675 const atom = macho_file.getAtom(atom_index);
676 const object = macho_file.objects.items[atom.getFile().?];
722677
723678 var addend: ?i64 = null;
724679 var subtractor: ?SymbolWithLoc = null;
......@@ -745,7 +700,7 @@ fn resolveRelocsArm64(
745700 atom.getFile(),
746701 });
747702
748 subtractor = parseRelocTarget(zld, .{
703 subtractor = parseRelocTarget(macho_file, .{
749704 .object_id = atom.getFile().?,
750705 .rel = rel,
751706 .code = atom_code,
......@@ -757,7 +712,7 @@ fn resolveRelocsArm64(
757712 else => {},
758713 }
759714
760 const target = parseRelocTarget(zld, .{
715 const target = parseRelocTarget(macho_file, .{
761716 .object_id = atom.getFile().?,
762717 .rel = rel,
763718 .code = atom_code,
......@@ -770,26 +725,26 @@ fn resolveRelocsArm64(
770725 @tagName(rel_type),
771726 rel.r_address,
772727 target.sym_index,
773 zld.getSymbolName(target),
728 macho_file.getSymbolName(target),
774729 target.getFile(),
775730 });
776731
777732 const source_addr = blk: {
778 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
733 const source_sym = macho_file.getSymbol(atom.getSymbolWithLoc());
779734 break :blk source_sym.n_value + rel_offset;
780735 };
781736 const target_addr = blk: {
782 if (relocRequiresGot(zld, rel)) break :blk zld.getGotEntryAddress(target).?;
783 if (relocIsTlv(zld, rel) and zld.getSymbol(target).undf())
784 break :blk zld.getTlvPtrEntryAddress(target).?;
785 if (relocIsStub(zld, rel) and zld.getSymbol(target).undf())
786 break :blk zld.getStubsEntryAddress(target).?;
737 if (relocRequiresGot(macho_file, rel)) break :blk macho_file.getGotEntryAddress(target).?;
738 if (relocIsTlv(macho_file, rel) and macho_file.getSymbol(target).undf())
739 break :blk macho_file.getTlvPtrEntryAddress(target).?;
740 if (relocIsStub(macho_file, rel) and macho_file.getSymbol(target).undf())
741 break :blk macho_file.getStubsEntryAddress(target).?;
787742 const is_tlv = is_tlv: {
788 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
789 const header = zld.sections.items(.header)[source_sym.n_sect - 1];
743 const source_sym = macho_file.getSymbol(atom.getSymbolWithLoc());
744 const header = macho_file.sections.items(.header)[source_sym.n_sect - 1];
790745 break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
791746 };
792 break :blk try getRelocTargetAddress(zld, target, is_tlv);
747 break :blk try getRelocTargetAddress(macho_file, target, is_tlv);
793748 };
794749
795750 log.debug(" | source_addr = 0x{x}", .{source_addr});
......@@ -797,9 +752,9 @@ fn resolveRelocsArm64(
797752 switch (rel_type) {
798753 .ARM64_RELOC_BRANCH26 => {
799754 log.debug(" source {s} (object({?})), target {s}", .{
800 zld.getSymbolName(atom.getSymbolWithLoc()),
755 macho_file.getSymbolName(atom.getSymbolWithLoc()),
801756 atom.getFile(),
802 zld.getSymbolName(target),
757 macho_file.getSymbolName(target),
803758 });
804759
805760 const displacement = if (Relocation.calcPcRelativeDisplacementArm64(
......@@ -809,13 +764,13 @@ fn resolveRelocsArm64(
809764 log.debug(" | target_addr = 0x{x}", .{target_addr});
810765 break :blk disp;
811766 } else |_| blk: {
812 const thunk_index = zld.thunk_table.get(atom_index).?;
813 const thunk = zld.thunks.items[thunk_index];
814 const thunk_sym_loc = if (zld.getSymbol(target).undf())
815 thunk.getTrampoline(zld, .stub, target).?
767 const thunk_index = macho_file.thunk_table.get(atom_index).?;
768 const thunk = macho_file.thunks.items[thunk_index];
769 const thunk_sym_loc = if (macho_file.getSymbol(target).undf())
770 thunk.getTrampoline(macho_file, .stub, target).?
816771 else
817 thunk.getTrampoline(zld, .atom, target).?;
818 const thunk_addr = zld.getSymbol(thunk_sym_loc).n_value;
772 thunk.getTrampoline(macho_file, .atom, target).?;
773 const thunk_addr = macho_file.getSymbol(thunk_sym_loc).n_value;
819774 log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_addr});
820775 break :blk try Relocation.calcPcRelativeDisplacementArm64(source_addr, thunk_addr);
821776 };
......@@ -944,7 +899,7 @@ fn resolveRelocsArm64(
944899 }
945900 };
946901
947 var inst = if (zld.tlv_ptr_table.lookup.contains(target)) aarch64.Instruction{
902 var inst = if (macho_file.tlv_ptr_table.lookup.contains(target)) aarch64.Instruction{
948903 .load_store_register = .{
949904 .rt = reg_info.rd,
950905 .rn = reg_info.rn,
......@@ -992,7 +947,7 @@ fn resolveRelocsArm64(
992947
993948 const result = blk: {
994949 if (subtractor) |sub| {
995 const sym = zld.getSymbol(sub);
950 const sym = macho_file.getSymbol(sub);
996951 break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + ptr_addend;
997952 } else {
998953 break :blk @as(i64, @intCast(target_addr)) + ptr_addend;
......@@ -1016,14 +971,14 @@ fn resolveRelocsArm64(
1016971}
1017972
1018973fn resolveRelocsX86(
1019 zld: *Zld,
974 macho_file: *MachO,
1020975 atom_index: Index,
1021976 atom_code: []u8,
1022977 atom_relocs: []align(1) const macho.relocation_info,
1023978 context: RelocContext,
1024979) !void {
1025 const atom = zld.getAtom(atom_index);
1026 const object = zld.objects.items[atom.getFile().?];
980 const atom = macho_file.getAtom(atom_index);
981 const object = macho_file.objects.items[atom.getFile().?];
1027982
1028983 var subtractor: ?SymbolWithLoc = null;
1029984
......@@ -1041,7 +996,7 @@ fn resolveRelocsX86(
1041996 atom.getFile(),
1042997 });
1043998
1044 subtractor = parseRelocTarget(zld, .{
999 subtractor = parseRelocTarget(macho_file, .{
10451000 .object_id = atom.getFile().?,
10461001 .rel = rel,
10471002 .code = atom_code,
......@@ -1053,7 +1008,7 @@ fn resolveRelocsX86(
10531008 else => {},
10541009 }
10551010
1056 const target = parseRelocTarget(zld, .{
1011 const target = parseRelocTarget(macho_file, .{
10571012 .object_id = atom.getFile().?,
10581013 .rel = rel,
10591014 .code = atom_code,
......@@ -1066,26 +1021,26 @@ fn resolveRelocsX86(
10661021 @tagName(rel_type),
10671022 rel.r_address,
10681023 target.sym_index,
1069 zld.getSymbolName(target),
1024 macho_file.getSymbolName(target),
10701025 target.getFile(),
10711026 });
10721027
10731028 const source_addr = blk: {
1074 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
1029 const source_sym = macho_file.getSymbol(atom.getSymbolWithLoc());
10751030 break :blk source_sym.n_value + rel_offset;
10761031 };
10771032 const target_addr = blk: {
1078 if (relocRequiresGot(zld, rel)) break :blk zld.getGotEntryAddress(target).?;
1079 if (relocIsStub(zld, rel) and zld.getSymbol(target).undf())
1080 break :blk zld.getStubsEntryAddress(target).?;
1081 if (relocIsTlv(zld, rel) and zld.getSymbol(target).undf())
1082 break :blk zld.getTlvPtrEntryAddress(target).?;
1033 if (relocRequiresGot(macho_file, rel)) break :blk macho_file.getGotEntryAddress(target).?;
1034 if (relocIsStub(macho_file, rel) and macho_file.getSymbol(target).undf())
1035 break :blk macho_file.getStubsEntryAddress(target).?;
1036 if (relocIsTlv(macho_file, rel) and macho_file.getSymbol(target).undf())
1037 break :blk macho_file.getTlvPtrEntryAddress(target).?;
10831038 const is_tlv = is_tlv: {
1084 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
1085 const header = zld.sections.items(.header)[source_sym.n_sect - 1];
1039 const source_sym = macho_file.getSymbol(atom.getSymbolWithLoc());
1040 const header = macho_file.sections.items(.header)[source_sym.n_sect - 1];
10861041 break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
10871042 };
1088 break :blk try getRelocTargetAddress(zld, target, is_tlv);
1043 break :blk try getRelocTargetAddress(macho_file, target, is_tlv);
10891044 };
10901045
10911046 log.debug(" | source_addr = 0x{x}", .{source_addr});
......@@ -1115,7 +1070,7 @@ fn resolveRelocsX86(
11151070 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
11161071 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
11171072
1118 if (zld.tlv_ptr_table.lookup.get(target) == null) {
1073 if (macho_file.tlv_ptr_table.lookup.get(target) == null) {
11191074 // We need to rewrite the opcode from movq to leaq.
11201075 atom_code[rel_offset - 2] = 0x8d;
11211076 }
......@@ -1170,7 +1125,7 @@ fn resolveRelocsX86(
11701125
11711126 const result = blk: {
11721127 if (subtractor) |sub| {
1173 const sym = zld.getSymbol(sub);
1128 const sym = macho_file.getSymbol(sub);
11741129 break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + addend;
11751130 } else {
11761131 break :blk @as(i64, @intCast(target_addr)) + addend;
......@@ -1192,10 +1147,10 @@ fn resolveRelocsX86(
11921147 }
11931148}
11941149
1195pub fn getAtomCode(zld: *Zld, atom_index: Index) []const u8 {
1196 const atom = zld.getAtom(atom_index);
1150pub fn getAtomCode(macho_file: *MachO, atom_index: Index) []const u8 {
1151 const atom = macho_file.getAtom(atom_index);
11971152 assert(atom.getFile() != null); // Synthetic atom shouldn't need to inquire for code.
1198 const object = zld.objects.items[atom.getFile().?];
1153 const object = macho_file.objects.items[atom.getFile().?];
11991154 const source_sym = object.getSourceSymbol(atom.sym_index) orelse {
12001155 // If there was no matching symbol present in the source symtab, this means
12011156 // we are dealing with either an entire section, or part of it, but also
......@@ -1216,10 +1171,10 @@ pub fn getAtomCode(zld: *Zld, atom_index: Index) []const u8 {
12161171 return code[offset..][0..code_len];
12171172}
12181173
1219pub fn getAtomRelocs(zld: *Zld, atom_index: Index) []const macho.relocation_info {
1220 const atom = zld.getAtom(atom_index);
1174pub fn getAtomRelocs(macho_file: *MachO, atom_index: Index) []const macho.relocation_info {
1175 const atom = macho_file.getAtom(atom_index);
12211176 assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs.
1222 const object = zld.objects.items[atom.getFile().?];
1177 const object = macho_file.objects.items[atom.getFile().?];
12231178 const cache = object.relocs_lookup[atom.sym_index];
12241179
12251180 const source_sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
......@@ -1238,8 +1193,8 @@ pub fn getAtomRelocs(zld: *Zld, atom_index: Index) []const macho.relocation_info
12381193 return relocs[cache.start..][0..cache.len];
12391194}
12401195
1241pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool {
1242 switch (zld.options.target.cpu.arch) {
1196pub fn relocRequiresGot(macho_file: *MachO, rel: macho.relocation_info) bool {
1197 switch (macho_file.base.options.target.cpu.arch) {
12431198 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
12441199 .ARM64_RELOC_GOT_LOAD_PAGE21,
12451200 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
......@@ -1257,8 +1212,8 @@ pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool {
12571212 }
12581213}
12591214
1260pub fn relocIsTlv(zld: *Zld, rel: macho.relocation_info) bool {
1261 switch (zld.options.target.cpu.arch) {
1215pub fn relocIsTlv(macho_file: *MachO, rel: macho.relocation_info) bool {
1216 switch (macho_file.base.options.target.cpu.arch) {
12621217 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
12631218 .ARM64_RELOC_TLVP_LOAD_PAGE21,
12641219 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
......@@ -1273,8 +1228,8 @@ pub fn relocIsTlv(zld: *Zld, rel: macho.relocation_info) bool {
12731228 }
12741229}
12751230
1276pub fn relocIsStub(zld: *Zld, rel: macho.relocation_info) bool {
1277 switch (zld.options.target.cpu.arch) {
1231pub fn relocIsStub(macho_file: *MachO, rel: macho.relocation_info) bool {
1232 switch (macho_file.base.options.target.cpu.arch) {
12781233 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
12791234 .ARM64_RELOC_BRANCH26 => return true,
12801235 else => return false,
......@@ -1305,4 +1260,3 @@ const Arch = std.Target.Cpu.Arch;
13051260const MachO = @import("../MachO.zig");
13061261pub const Relocation = @import("Relocation.zig");
13071262const SymbolWithLoc = MachO.SymbolWithLoc;
1308const Zld = @import("zld.zig").Zld;
src/link/MachO/CodeSignature.zig+182-182
......@@ -1,17 +1,175 @@
1const CodeSignature = @This();
1page_size: u16,
2code_directory: CodeDirectory,
3requirements: ?Requirements = null,
4entitlements: ?Entitlements = null,
5signature: ?Signature = null,
26
3const std = @import("std");
4const assert = std.debug.assert;
5const fs = std.fs;
6const log = std.log.scoped(.link);
7const macho = std.macho;
8const mem = std.mem;
9const testing = std.testing;
7pub fn init(page_size: u16) CodeSignature {
8 return .{
9 .page_size = page_size,
10 .code_directory = CodeDirectory.init(page_size),
11 };
12}
1013
11const Allocator = mem.Allocator;
12const Compilation = @import("../../Compilation.zig");
13const Hasher = @import("hasher.zig").ParallelHasher;
14const Sha256 = std.crypto.hash.sha2.Sha256;
14pub fn deinit(self: *CodeSignature, allocator: Allocator) void {
15 self.code_directory.deinit(allocator);
16 if (self.requirements) |*req| {
17 req.deinit(allocator);
18 }
19 if (self.entitlements) |*ents| {
20 ents.deinit(allocator);
21 }
22 if (self.signature) |*sig| {
23 sig.deinit(allocator);
24 }
25}
26
27pub fn addEntitlements(self: *CodeSignature, allocator: Allocator, path: []const u8) !void {
28 const file = try fs.cwd().openFile(path, .{});
29 defer file.close();
30 const inner = try file.readToEndAlloc(allocator, std.math.maxInt(u32));
31 self.entitlements = .{ .inner = inner };
32}
33
34pub const WriteOpts = struct {
35 file: fs.File,
36 exec_seg_base: u64,
37 exec_seg_limit: u64,
38 file_size: u32,
39 output_mode: std.builtin.OutputMode,
40};
41
42pub fn writeAdhocSignature(
43 self: *CodeSignature,
44 comp: *const Compilation,
45 opts: WriteOpts,
46 writer: anytype,
47) !void {
48 const gpa = comp.gpa;
49
50 var header: macho.SuperBlob = .{
51 .magic = macho.CSMAGIC_EMBEDDED_SIGNATURE,
52 .length = @sizeOf(macho.SuperBlob),
53 .count = 0,
54 };
55
56 var blobs = std.ArrayList(Blob).init(gpa);
57 defer blobs.deinit();
58
59 self.code_directory.inner.execSegBase = opts.exec_seg_base;
60 self.code_directory.inner.execSegLimit = opts.exec_seg_limit;
61 self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0;
62 self.code_directory.inner.codeLimit = opts.file_size;
63
64 const total_pages = @as(u32, @intCast(mem.alignForward(usize, opts.file_size, self.page_size) / self.page_size));
65
66 try self.code_directory.code_slots.ensureTotalCapacityPrecise(gpa, total_pages);
67 self.code_directory.code_slots.items.len = total_pages;
68 self.code_directory.inner.nCodeSlots = total_pages;
69
70 // Calculate hash for each page (in file) and write it to the buffer
71 var hasher = Hasher(Sha256){ .allocator = gpa, .thread_pool = comp.thread_pool };
72 try hasher.hash(opts.file, self.code_directory.code_slots.items, .{
73 .chunk_size = self.page_size,
74 .max_file_size = opts.file_size,
75 });
76
77 try blobs.append(.{ .code_directory = &self.code_directory });
78 header.length += @sizeOf(macho.BlobIndex);
79 header.count += 1;
80
81 var hash: [hash_size]u8 = undefined;
82
83 if (self.requirements) |*req| {
84 var buf = std.ArrayList(u8).init(gpa);
85 defer buf.deinit();
86 try req.write(buf.writer());
87 Sha256.hash(buf.items, &hash, .{});
88 self.code_directory.addSpecialHash(req.slotType(), hash);
89
90 try blobs.append(.{ .requirements = req });
91 header.count += 1;
92 header.length += @sizeOf(macho.BlobIndex) + req.size();
93 }
94
95 if (self.entitlements) |*ents| {
96 var buf = std.ArrayList(u8).init(gpa);
97 defer buf.deinit();
98 try ents.write(buf.writer());
99 Sha256.hash(buf.items, &hash, .{});
100 self.code_directory.addSpecialHash(ents.slotType(), hash);
101
102 try blobs.append(.{ .entitlements = ents });
103 header.count += 1;
104 header.length += @sizeOf(macho.BlobIndex) + ents.size();
105 }
106
107 if (self.signature) |*sig| {
108 try blobs.append(.{ .signature = sig });
109 header.count += 1;
110 header.length += @sizeOf(macho.BlobIndex) + sig.size();
111 }
112
113 self.code_directory.inner.hashOffset =
114 @sizeOf(macho.CodeDirectory) + @as(u32, @intCast(self.code_directory.ident.len + 1 + self.code_directory.inner.nSpecialSlots * hash_size));
115 self.code_directory.inner.length = self.code_directory.size();
116 header.length += self.code_directory.size();
117
118 try writer.writeIntBig(u32, header.magic);
119 try writer.writeIntBig(u32, header.length);
120 try writer.writeIntBig(u32, header.count);
121
122 var offset: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) * @as(u32, @intCast(blobs.items.len));
123 for (blobs.items) |blob| {
124 try writer.writeIntBig(u32, blob.slotType());
125 try writer.writeIntBig(u32, offset);
126 offset += blob.size();
127 }
128
129 for (blobs.items) |blob| {
130 try blob.write(writer);
131 }
132}
133
134pub fn size(self: CodeSignature) u32 {
135 var ssize: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size();
136 if (self.requirements) |req| {
137 ssize += @sizeOf(macho.BlobIndex) + req.size();
138 }
139 if (self.entitlements) |ent| {
140 ssize += @sizeOf(macho.BlobIndex) + ent.size();
141 }
142 if (self.signature) |sig| {
143 ssize += @sizeOf(macho.BlobIndex) + sig.size();
144 }
145 return ssize;
146}
147
148pub fn estimateSize(self: CodeSignature, file_size: u64) u32 {
149 var ssize: u64 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size();
150 // Approx code slots
151 const total_pages = mem.alignForward(u64, file_size, self.page_size) / self.page_size;
152 ssize += total_pages * hash_size;
153 var n_special_slots: u32 = 0;
154 if (self.requirements) |req| {
155 ssize += @sizeOf(macho.BlobIndex) + req.size();
156 n_special_slots = @max(n_special_slots, req.slotType());
157 }
158 if (self.entitlements) |ent| {
159 ssize += @sizeOf(macho.BlobIndex) + ent.size() + hash_size;
160 n_special_slots = @max(n_special_slots, ent.slotType());
161 }
162 if (self.signature) |sig| {
163 ssize += @sizeOf(macho.BlobIndex) + sig.size();
164 }
165 ssize += n_special_slots * hash_size;
166 return @as(u32, @intCast(mem.alignForward(u64, ssize, @sizeOf(u64))));
167}
168
169pub fn clear(self: *CodeSignature, allocator: Allocator) void {
170 self.code_directory.deinit(allocator);
171 self.code_directory = CodeDirectory.init(self.page_size);
172}
15173
16174const hash_size = Sha256.digest_length;
17175
......@@ -218,175 +376,17 @@ const Signature = struct {
218376 }
219377};
220378
221page_size: u16,
222code_directory: CodeDirectory,
223requirements: ?Requirements = null,
224entitlements: ?Entitlements = null,
225signature: ?Signature = null,
226
227pub fn init(page_size: u16) CodeSignature {
228 return .{
229 .page_size = page_size,
230 .code_directory = CodeDirectory.init(page_size),
231 };
232}
233
234pub fn deinit(self: *CodeSignature, allocator: Allocator) void {
235 self.code_directory.deinit(allocator);
236 if (self.requirements) |*req| {
237 req.deinit(allocator);
238 }
239 if (self.entitlements) |*ents| {
240 ents.deinit(allocator);
241 }
242 if (self.signature) |*sig| {
243 sig.deinit(allocator);
244 }
245}
246
247pub fn addEntitlements(self: *CodeSignature, allocator: Allocator, path: []const u8) !void {
248 const file = try fs.cwd().openFile(path, .{});
249 defer file.close();
250 const inner = try file.readToEndAlloc(allocator, std.math.maxInt(u32));
251 self.entitlements = .{ .inner = inner };
252}
253
254pub const WriteOpts = struct {
255 file: fs.File,
256 exec_seg_base: u64,
257 exec_seg_limit: u64,
258 file_size: u32,
259 output_mode: std.builtin.OutputMode,
260};
261
262pub fn writeAdhocSignature(
263 self: *CodeSignature,
264 comp: *const Compilation,
265 opts: WriteOpts,
266 writer: anytype,
267) !void {
268 const gpa = comp.gpa;
269
270 var header: macho.SuperBlob = .{
271 .magic = macho.CSMAGIC_EMBEDDED_SIGNATURE,
272 .length = @sizeOf(macho.SuperBlob),
273 .count = 0,
274 };
275
276 var blobs = std.ArrayList(Blob).init(gpa);
277 defer blobs.deinit();
278
279 self.code_directory.inner.execSegBase = opts.exec_seg_base;
280 self.code_directory.inner.execSegLimit = opts.exec_seg_limit;
281 self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0;
282 self.code_directory.inner.codeLimit = opts.file_size;
283
284 const total_pages = @as(u32, @intCast(mem.alignForward(usize, opts.file_size, self.page_size) / self.page_size));
285
286 try self.code_directory.code_slots.ensureTotalCapacityPrecise(gpa, total_pages);
287 self.code_directory.code_slots.items.len = total_pages;
288 self.code_directory.inner.nCodeSlots = total_pages;
289
290 // Calculate hash for each page (in file) and write it to the buffer
291 var hasher = Hasher(Sha256){ .allocator = gpa, .thread_pool = comp.thread_pool };
292 try hasher.hash(opts.file, self.code_directory.code_slots.items, .{
293 .chunk_size = self.page_size,
294 .max_file_size = opts.file_size,
295 });
296
297 try blobs.append(.{ .code_directory = &self.code_directory });
298 header.length += @sizeOf(macho.BlobIndex);
299 header.count += 1;
300
301 var hash: [hash_size]u8 = undefined;
302
303 if (self.requirements) |*req| {
304 var buf = std.ArrayList(u8).init(gpa);
305 defer buf.deinit();
306 try req.write(buf.writer());
307 Sha256.hash(buf.items, &hash, .{});
308 self.code_directory.addSpecialHash(req.slotType(), hash);
309
310 try blobs.append(.{ .requirements = req });
311 header.count += 1;
312 header.length += @sizeOf(macho.BlobIndex) + req.size();
313 }
314
315 if (self.entitlements) |*ents| {
316 var buf = std.ArrayList(u8).init(gpa);
317 defer buf.deinit();
318 try ents.write(buf.writer());
319 Sha256.hash(buf.items, &hash, .{});
320 self.code_directory.addSpecialHash(ents.slotType(), hash);
321
322 try blobs.append(.{ .entitlements = ents });
323 header.count += 1;
324 header.length += @sizeOf(macho.BlobIndex) + ents.size();
325 }
326
327 if (self.signature) |*sig| {
328 try blobs.append(.{ .signature = sig });
329 header.count += 1;
330 header.length += @sizeOf(macho.BlobIndex) + sig.size();
331 }
332
333 self.code_directory.inner.hashOffset =
334 @sizeOf(macho.CodeDirectory) + @as(u32, @intCast(self.code_directory.ident.len + 1 + self.code_directory.inner.nSpecialSlots * hash_size));
335 self.code_directory.inner.length = self.code_directory.size();
336 header.length += self.code_directory.size();
337
338 try writer.writeIntBig(u32, header.magic);
339 try writer.writeIntBig(u32, header.length);
340 try writer.writeIntBig(u32, header.count);
341
342 var offset: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) * @as(u32, @intCast(blobs.items.len));
343 for (blobs.items) |blob| {
344 try writer.writeIntBig(u32, blob.slotType());
345 try writer.writeIntBig(u32, offset);
346 offset += blob.size();
347 }
348
349 for (blobs.items) |blob| {
350 try blob.write(writer);
351 }
352}
353
354pub fn size(self: CodeSignature) u32 {
355 var ssize: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size();
356 if (self.requirements) |req| {
357 ssize += @sizeOf(macho.BlobIndex) + req.size();
358 }
359 if (self.entitlements) |ent| {
360 ssize += @sizeOf(macho.BlobIndex) + ent.size();
361 }
362 if (self.signature) |sig| {
363 ssize += @sizeOf(macho.BlobIndex) + sig.size();
364 }
365 return ssize;
366}
379const CodeSignature = @This();
367380
368pub fn estimateSize(self: CodeSignature, file_size: u64) u32 {
369 var ssize: u64 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size();
370 // Approx code slots
371 const total_pages = mem.alignForward(u64, file_size, self.page_size) / self.page_size;
372 ssize += total_pages * hash_size;
373 var n_special_slots: u32 = 0;
374 if (self.requirements) |req| {
375 ssize += @sizeOf(macho.BlobIndex) + req.size();
376 n_special_slots = @max(n_special_slots, req.slotType());
377 }
378 if (self.entitlements) |ent| {
379 ssize += @sizeOf(macho.BlobIndex) + ent.size() + hash_size;
380 n_special_slots = @max(n_special_slots, ent.slotType());
381 }
382 if (self.signature) |sig| {
383 ssize += @sizeOf(macho.BlobIndex) + sig.size();
384 }
385 ssize += n_special_slots * hash_size;
386 return @as(u32, @intCast(mem.alignForward(u64, ssize, @sizeOf(u64))));
387}
381const std = @import("std");
382const assert = std.debug.assert;
383const fs = std.fs;
384const log = std.log.scoped(.link);
385const macho = std.macho;
386const mem = std.mem;
387const testing = std.testing;
388388
389pub fn clear(self: *CodeSignature, allocator: Allocator) void {
390 self.code_directory.deinit(allocator);
391 self.code_directory = CodeDirectory.init(self.page_size);
392}
389const Allocator = mem.Allocator;
390const Compilation = @import("../../Compilation.zig");
391const Hasher = @import("hasher.zig").ParallelHasher;
392const Sha256 = std.crypto.hash.sha2.Sha256;
src/link/MachO/DebugSymbols.zig+23-23
......@@ -1,26 +1,3 @@
1const DebugSymbols = @This();
2
3const std = @import("std");
4const build_options = @import("build_options");
5const assert = std.debug.assert;
6const fs = std.fs;
7const link = @import("../../link.zig");
8const load_commands = @import("load_commands.zig");
9const log = std.log.scoped(.dsym);
10const macho = std.macho;
11const makeStaticString = MachO.makeStaticString;
12const math = std.math;
13const mem = std.mem;
14const padToIdeal = MachO.padToIdeal;
15const trace = @import("../../tracy.zig").trace;
16
17const Allocator = mem.Allocator;
18const Dwarf = @import("../Dwarf.zig");
19const MachO = @import("../MachO.zig");
20const Module = @import("../../Module.zig");
21const StringTable = @import("../strtab.zig").StringTable;
22const Type = @import("../../type.zig").Type;
23
241allocator: Allocator,
252dwarf: Dwarf,
263file: fs.File,
......@@ -569,3 +546,26 @@ pub fn getSection(self: DebugSymbols, sect: u8) macho.section_64 {
569546 assert(sect < self.sections.items.len);
570547 return self.sections.items[sect];
571548}
549
550const DebugSymbols = @This();
551
552const std = @import("std");
553const build_options = @import("build_options");
554const assert = std.debug.assert;
555const fs = std.fs;
556const link = @import("../../link.zig");
557const load_commands = @import("load_commands.zig");
558const log = std.log.scoped(.dsym);
559const macho = std.macho;
560const makeStaticString = MachO.makeStaticString;
561const math = std.math;
562const mem = std.mem;
563const padToIdeal = MachO.padToIdeal;
564const trace = @import("../../tracy.zig").trace;
565
566const Allocator = mem.Allocator;
567const Dwarf = @import("../Dwarf.zig");
568const MachO = @import("../MachO.zig");
569const Module = @import("../../Module.zig");
570const StringTable = @import("../strtab.zig").StringTable;
571const Type = @import("../../type.zig").Type;
src/link/MachO/DwarfInfo.zig+14-14
......@@ -1,17 +1,3 @@
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
151debug_info: []const u8,
162debug_abbrev: []const u8,
173debug_str: []const u8,
......@@ -501,3 +487,17 @@ fn getString(self: DwarfInfo, off: u64) []const u8 {
501487 assert(off < self.debug_str.len);
502488 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.debug_str.ptr + @as(usize, @intCast(off)))), 0);
503489}
490
491const DwarfInfo = @This();
492
493const std = @import("std");
494const assert = std.debug.assert;
495const dwarf = std.dwarf;
496const leb = std.leb;
497const log = std.log.scoped(.macho);
498const math = std.math;
499const mem = std.mem;
500
501const Allocator = mem.Allocator;
502pub const AbbrevLookupTable = std.AutoHashMap(u64, struct { pos: usize, len: usize });
503pub const SubprogramLookupByName = std.StringHashMap(struct { addr: u64, size: u64 });
src/link/MachO/Dylib.zig+20-20
......@@ -1,23 +1,3 @@
1const Dylib = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const fs = std.fs;
6const fmt = std.fmt;
7const log = std.log.scoped(.link);
8const macho = std.macho;
9const math = std.math;
10const mem = std.mem;
11const fat = @import("fat.zig");
12const tapi = @import("../tapi.zig");
13
14const Allocator = mem.Allocator;
15const CrossTarget = std.zig.CrossTarget;
16const LibStub = tapi.LibStub;
17const LoadCommandIterator = macho.LoadCommandIterator;
18const MachO = @import("../MachO.zig");
19const Tbd = tapi.Tbd;
20
211id: ?Id = null,
222weak: bool = false,
233/// Header is only set if Dylib is parsed directly from a binary and not a stub file.
......@@ -546,3 +526,23 @@ pub fn parseFromStub(
546526 }
547527 }
548528}
529
530const Dylib = @This();
531
532const std = @import("std");
533const assert = std.debug.assert;
534const fs = std.fs;
535const fmt = std.fmt;
536const log = std.log.scoped(.link);
537const macho = std.macho;
538const math = std.math;
539const mem = std.mem;
540const fat = @import("fat.zig");
541const tapi = @import("../tapi.zig");
542
543const Allocator = mem.Allocator;
544const CrossTarget = std.zig.CrossTarget;
545const LibStub = tapi.LibStub;
546const LoadCommandIterator = macho.LoadCommandIterator;
547const MachO = @import("../MachO.zig");
548const Tbd = tapi.Tbd;
src/link/MachO/Object.zig+76-79
......@@ -2,31 +2,6 @@
22//! Each Object is fully loaded into memory for easier
33//! access into different data within.
44
5const Object = @This();
6
7const std = @import("std");
8const build_options = @import("build_options");
9const assert = std.debug.assert;
10const dwarf = std.dwarf;
11const eh_frame = @import("eh_frame.zig");
12const fs = std.fs;
13const io = std.io;
14const log = std.log.scoped(.link);
15const macho = std.macho;
16const math = std.math;
17const mem = std.mem;
18const sort = std.sort;
19const trace = @import("../../tracy.zig").trace;
20
21const Allocator = mem.Allocator;
22const Atom = @import("Atom.zig");
23const DwarfInfo = @import("DwarfInfo.zig");
24const LoadCommandIterator = macho.LoadCommandIterator;
25const MachO = @import("../MachO.zig");
26const SymbolWithLoc = MachO.SymbolWithLoc;
27const UnwindInfo = @import("UnwindInfo.zig");
28const Zld = @import("zld.zig").Zld;
29
305name: []const u8,
316mtime: u64,
327contents: []align(@alignOf(u64)) const u8,
......@@ -359,25 +334,25 @@ fn sectionLessThanByAddress(ctx: void, lhs: SortedSection, rhs: SortedSection) b
359334 return lhs.header.addr < rhs.header.addr;
360335}
361336
362pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u32) !void {
337pub fn splitIntoAtoms(self: *Object, macho_file: *MachO, object_id: u32) !void {
363338 log.debug("splitting object({d}, {s}) into atoms", .{ object_id, self.name });
364339
365 try self.splitRegularSections(zld, object_id);
366 try self.parseEhFrameSection(zld, object_id);
367 try self.parseUnwindInfo(zld, object_id);
368 try self.parseDataInCode(zld.gpa);
340 try self.splitRegularSections(macho_file, object_id);
341 try self.parseEhFrameSection(macho_file, object_id);
342 try self.parseUnwindInfo(macho_file, object_id);
343 try self.parseDataInCode(macho_file.base.allocator);
369344}
370345
371346/// Splits input regular sections into Atoms.
372347/// If the Object was compiled with `MH_SUBSECTIONS_VIA_SYMBOLS`, splits section
373348/// into subsections where each subsection then represents an Atom.
374pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
375 const gpa = zld.gpa;
349pub fn splitRegularSections(self: *Object, macho_file: *MachO, object_id: u32) !void {
350 const gpa = macho_file.base.allocator;
376351
377352 const sections = self.getSourceSections();
378353 for (sections, 0..) |sect, id| {
379354 if (sect.isDebug()) continue;
380 const out_sect_id = (try Atom.getOutputSection(zld, sect)) orelse {
355 const out_sect_id = (try Atom.getOutputSection(macho_file, sect)) orelse {
381356 log.debug(" unhandled section '{s},{s}'", .{ sect.segName(), sect.sectName() });
382357 continue;
383358 };
......@@ -397,13 +372,13 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
397372 if (self.in_symtab == null) {
398373 for (sections, 0..) |sect, id| {
399374 if (sect.isDebug()) continue;
400 const out_sect_id = (try Atom.getOutputSection(zld, sect)) orelse continue;
375 const out_sect_id = (try Atom.getOutputSection(macho_file, sect)) orelse continue;
401376 if (sect.size == 0) continue;
402377
403378 const sect_id = @as(u8, @intCast(id));
404379 const sym_index = self.getSectionAliasSymbolIndex(sect_id);
405380 const atom_index = try self.createAtomFromSubsection(
406 zld,
381 macho_file,
407382 object_id,
408383 sym_index,
409384 sym_index,
......@@ -412,7 +387,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
412387 sect.@"align",
413388 out_sect_id,
414389 );
415 zld.addAtomToSection(atom_index);
390 macho_file.addAtomToSection(atom_index);
416391 }
417392 return;
418393 }
......@@ -456,17 +431,17 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
456431 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
457432
458433 // Get output segment/section in the final artifact.
459 const out_sect_id = (try Atom.getOutputSection(zld, sect)) orelse continue;
434 const out_sect_id = (try Atom.getOutputSection(macho_file, sect)) orelse continue;
460435
461436 log.debug(" output sect({d}, '{s},{s}')", .{
462437 out_sect_id + 1,
463 zld.sections.items(.header)[out_sect_id].segName(),
464 zld.sections.items(.header)[out_sect_id].sectName(),
438 macho_file.sections.items(.header)[out_sect_id].segName(),
439 macho_file.sections.items(.header)[out_sect_id].sectName(),
465440 });
466441
467442 try self.parseRelocs(gpa, section.id);
468443
469 const cpu_arch = zld.options.target.cpu.arch;
444 const cpu_arch = macho_file.base.options.target.cpu.arch;
470445 const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1);
471446 const sect_start_index = sect_sym_index + sect_loc.index;
472447
......@@ -482,7 +457,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
482457 const sym_index = self.getSectionAliasSymbolIndex(sect_id);
483458 const atom_size = first_sym.n_value - sect.addr;
484459 const atom_index = try self.createAtomFromSubsection(
485 zld,
460 macho_file,
486461 object_id,
487462 sym_index,
488463 sym_index,
......@@ -492,9 +467,9 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
492467 out_sect_id,
493468 );
494469 if (!sect.isZerofill()) {
495 try self.cacheRelocs(zld, atom_index);
470 try self.cacheRelocs(macho_file, atom_index);
496471 }
497 zld.addAtomToSection(atom_index);
472 macho_file.addAtomToSection(atom_index);
498473 }
499474
500475 var next_sym_index = sect_start_index;
......@@ -518,7 +493,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
518493 sect.@"align";
519494
520495 const atom_index = try self.createAtomFromSubsection(
521 zld,
496 macho_file,
522497 object_id,
523498 atom_sym_index,
524499 atom_sym_index,
......@@ -537,14 +512,14 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
537512 self.atom_by_index_table[alias_index] = atom_index;
538513 }
539514 if (!sect.isZerofill()) {
540 try self.cacheRelocs(zld, atom_index);
515 try self.cacheRelocs(macho_file, atom_index);
541516 }
542 zld.addAtomToSection(atom_index);
517 macho_file.addAtomToSection(atom_index);
543518 }
544519 } else {
545520 const alias_index = self.getSectionAliasSymbolIndex(sect_id);
546521 const atom_index = try self.createAtomFromSubsection(
547 zld,
522 macho_file,
548523 object_id,
549524 alias_index,
550525 sect_start_index,
......@@ -554,16 +529,16 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
554529 out_sect_id,
555530 );
556531 if (!sect.isZerofill()) {
557 try self.cacheRelocs(zld, atom_index);
532 try self.cacheRelocs(macho_file, atom_index);
558533 }
559 zld.addAtomToSection(atom_index);
534 macho_file.addAtomToSection(atom_index);
560535 }
561536 }
562537}
563538
564539fn createAtomFromSubsection(
565540 self: *Object,
566 zld: *Zld,
541 macho_file: *MachO,
567542 object_id: u32,
568543 sym_index: u32,
569544 inner_sym_index: u32,
......@@ -572,9 +547,9 @@ fn createAtomFromSubsection(
572547 alignment: u32,
573548 out_sect_id: u8,
574549) !Atom.Index {
575 const gpa = zld.gpa;
576 const atom_index = try zld.createAtom(sym_index, .{ .size = size, .alignment = alignment });
577 const atom = zld.getAtomPtr(atom_index);
550 const gpa = macho_file.base.allocator;
551 const atom_index = try macho_file.createAtom(sym_index, .{ .size = size, .alignment = alignment });
552 const atom = macho_file.getAtomPtr(atom_index);
578553 atom.inner_sym_index = inner_sym_index;
579554 atom.inner_nsyms_trailing = inner_nsyms_trailing;
580555 atom.file = object_id + 1;
......@@ -584,22 +559,22 @@ fn createAtomFromSubsection(
584559 sym_index,
585560 self.getSymbolName(sym_index),
586561 out_sect_id + 1,
587 zld.sections.items(.header)[out_sect_id].segName(),
588 zld.sections.items(.header)[out_sect_id].sectName(),
562 macho_file.sections.items(.header)[out_sect_id].segName(),
563 macho_file.sections.items(.header)[out_sect_id].sectName(),
589564 object_id,
590565 });
591566
592567 try self.atoms.append(gpa, atom_index);
593568 self.atom_by_index_table[sym_index] = atom_index;
594569
595 var it = Atom.getInnerSymbolsIterator(zld, atom_index);
570 var it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
596571 while (it.next()) |sym_loc| {
597 const inner = zld.getSymbolPtr(sym_loc);
572 const inner = macho_file.getSymbolPtr(sym_loc);
598573 inner.n_sect = out_sect_id + 1;
599574 self.atom_by_index_table[sym_loc.sym_index] = atom_index;
600575 }
601576
602 const out_sect = zld.sections.items(.header)[out_sect_id];
577 const out_sect = macho_file.sections.items(.header)[out_sect_id];
603578 if (out_sect.isCode() and
604579 mem.eql(u8, "__TEXT", out_sect.segName()) and
605580 mem.eql(u8, "__text", out_sect.sectName()))
......@@ -651,8 +626,8 @@ fn parseRelocs(self: *Object, gpa: Allocator, sect_id: u8) !void {
651626 self.section_relocs_lookup.items[sect_id] = start;
652627}
653628
654fn cacheRelocs(self: *Object, zld: *Zld, atom_index: Atom.Index) !void {
655 const atom = zld.getAtom(atom_index);
629fn cacheRelocs(self: *Object, macho_file: *MachO, atom_index: Atom.Index) !void {
630 const atom = macho_file.getAtom(atom_index);
656631
657632 const source_sect_id = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
658633 break :blk source_sym.n_sect - 1;
......@@ -679,19 +654,19 @@ fn relocGreaterThan(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation
679654 return lhs.r_address > rhs.r_address;
680655}
681656
682fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
657fn parseEhFrameSection(self: *Object, macho_file: *MachO, object_id: u32) !void {
683658 const sect_id = self.eh_frame_sect_id orelse return;
684659 const sect = self.getSourceSection(sect_id);
685660
686661 log.debug("parsing __TEXT,__eh_frame section", .{});
687662
688 const gpa = zld.gpa;
663 const gpa = macho_file.base.allocator;
689664
690 if (zld.eh_frame_section_index == null) {
691 zld.eh_frame_section_index = try MachO.initSection(gpa, zld, "__TEXT", "__eh_frame", .{});
665 if (macho_file.eh_frame_section_index == null) {
666 macho_file.eh_frame_section_index = try macho_file.initSection("__TEXT", "__eh_frame", .{});
692667 }
693668
694 const cpu_arch = zld.options.target.cpu.arch;
669 const cpu_arch = macho_file.base.options.target.cpu.arch;
695670 try self.parseRelocs(gpa, sect_id);
696671 const relocs = self.getRelocs(sect_id);
697672
......@@ -729,7 +704,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
729704 @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_UNSIGNED)
730705 break rel;
731706 } else unreachable;
732 const target = Atom.parseRelocTarget(zld, .{
707 const target = Atom.parseRelocTarget(macho_file, .{
733708 .object_id = object_id,
734709 .rel = rel,
735710 .code = it.data[offset..],
......@@ -744,7 +719,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
744719 });
745720 const target_sym_index = self.getSymbolByAddress(target_address, null);
746721 const target = if (self.getGlobal(target_sym_index)) |global_index|
747 zld.globals.items[global_index]
722 macho_file.globals.items[global_index]
748723 else
749724 SymbolWithLoc{ .sym_index = target_sym_index, .file = object_id + 1 };
750725 break :blk target;
......@@ -770,7 +745,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
770745 };
771746 log.debug("FDE at offset {x} tracks {s}", .{
772747 offset,
773 zld.getSymbolName(actual_target),
748 macho_file.getSymbolName(actual_target),
774749 });
775750 try self.eh_frame_records_lookup.putNoClobber(gpa, actual_target, offset);
776751 }
......@@ -779,19 +754,17 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
779754 }
780755}
781756
782fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
783 const gpa = zld.gpa;
784 const cpu_arch = zld.options.target.cpu.arch;
757fn parseUnwindInfo(self: *Object, macho_file: *MachO, object_id: u32) !void {
758 const gpa = macho_file.base.allocator;
759 const cpu_arch = macho_file.base.options.target.cpu.arch;
785760 const sect_id = self.unwind_info_sect_id orelse {
786761 // If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`,
787762 // we will try fully synthesising unwind info records to somewhat match Apple ld's
788763 // approach. However, we will only synthesise DWARF records and nothing more. For this reason,
789764 // we still create the output `__TEXT,__unwind_info` section.
790765 if (self.hasEhFrameRecords()) {
791 if (zld.unwind_info_section_index == null) {
792 zld.unwind_info_section_index = try MachO.initSection(
793 gpa,
794 zld,
766 if (macho_file.unwind_info_section_index == null) {
767 macho_file.unwind_info_section_index = try macho_file.initSection(
795768 "__TEXT",
796769 "__unwind_info",
797770 .{},
......@@ -803,8 +776,8 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
803776
804777 log.debug("parsing unwind info in {s}", .{self.name});
805778
806 if (zld.unwind_info_section_index == null) {
807 zld.unwind_info_section_index = try MachO.initSection(gpa, zld, "__TEXT", "__unwind_info", .{});
779 if (macho_file.unwind_info_section_index == null) {
780 macho_file.unwind_info_section_index = try macho_file.initSection("__TEXT", "__unwind_info", .{});
808781 }
809782
810783 const unwind_records = self.getUnwindRecords();
......@@ -839,7 +812,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
839812
840813 // Find function symbol that this record describes
841814 const rel = relocs[rel_pos.start..][rel_pos.len - 1];
842 const target = Atom.parseRelocTarget(zld, .{
815 const target = Atom.parseRelocTarget(macho_file, .{
843816 .object_id = object_id,
844817 .rel = rel,
845818 .code = mem.asBytes(&record),
......@@ -863,7 +836,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
863836 };
864837 log.debug("unwind record {d} tracks {s}", .{
865838 record_id,
866 zld.getSymbolName(actual_target),
839 macho_file.getSymbolName(actual_target),
867840 });
868841 try self.unwind_records_lookup.putNoClobber(gpa, actual_target, @intCast(record_id));
869842 }
......@@ -1094,3 +1067,27 @@ pub fn getEhFrameRecordsIterator(self: Object) eh_frame.Iterator {
10941067pub fn hasDataInCode(self: Object) bool {
10951068 return self.data_in_code.items.len > 0;
10961069}
1070
1071const Object = @This();
1072
1073const std = @import("std");
1074const build_options = @import("build_options");
1075const assert = std.debug.assert;
1076const dwarf = std.dwarf;
1077const eh_frame = @import("eh_frame.zig");
1078const fs = std.fs;
1079const io = std.io;
1080const log = std.log.scoped(.link);
1081const macho = std.macho;
1082const math = std.math;
1083const mem = std.mem;
1084const sort = std.sort;
1085const trace = @import("../../tracy.zig").trace;
1086
1087const Allocator = mem.Allocator;
1088const Atom = @import("Atom.zig");
1089const DwarfInfo = @import("DwarfInfo.zig");
1090const LoadCommandIterator = macho.LoadCommandIterator;
1091const MachO = @import("../MachO.zig");
1092const SymbolWithLoc = MachO.SymbolWithLoc;
1093const UnwindInfo = @import("UnwindInfo.zig");
src/link/MachO/Trie.zig+242-242
......@@ -28,248 +28,6 @@
2828//! After the optional exported symbol information is a byte of how many edges (0-255) that
2929//! this node has leaving it, followed by each edge. Each edge is a zero terminated UTF8 of
3030//! the addition chars in the symbol, followed by a uleb128 offset for the node that edge points to.
31const Trie = @This();
32
33const std = @import("std");
34const mem = std.mem;
35const leb = std.leb;
36const log = std.log.scoped(.link);
37const macho = std.macho;
38const testing = std.testing;
39const assert = std.debug.assert;
40const Allocator = mem.Allocator;
41
42pub const Node = struct {
43 base: *Trie,
44
45 /// Terminal info associated with this node.
46 /// If this node is not a terminal node, info is null.
47 terminal_info: ?struct {
48 /// Export flags associated with this exported symbol.
49 export_flags: u64,
50 /// VM address offset wrt to the section this symbol is defined against.
51 vmaddr_offset: u64,
52 } = null,
53
54 /// Offset of this node in the trie output byte stream.
55 trie_offset: ?u64 = null,
56
57 /// List of all edges originating from this node.
58 edges: std.ArrayListUnmanaged(Edge) = .{},
59
60 node_dirty: bool = true,
61
62 /// Edge connecting to nodes in the trie.
63 pub const Edge = struct {
64 from: *Node,
65 to: *Node,
66 label: []u8,
67
68 fn deinit(self: *Edge, allocator: Allocator) void {
69 self.to.deinit(allocator);
70 allocator.destroy(self.to);
71 allocator.free(self.label);
72 self.from = undefined;
73 self.to = undefined;
74 self.label = undefined;
75 }
76 };
77
78 fn deinit(self: *Node, allocator: Allocator) void {
79 for (self.edges.items) |*edge| {
80 edge.deinit(allocator);
81 }
82 self.edges.deinit(allocator);
83 }
84
85 /// Inserts a new node starting from `self`.
86 fn put(self: *Node, allocator: Allocator, label: []const u8) !*Node {
87 // Check for match with edges from this node.
88 for (self.edges.items) |*edge| {
89 const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.to;
90 if (match == 0) continue;
91 if (match == edge.label.len) return edge.to.put(allocator, label[match..]);
92
93 // Found a match, need to splice up nodes.
94 // From: A -> B
95 // To: A -> C -> B
96 const mid = try allocator.create(Node);
97 mid.* = .{ .base = self.base };
98 var to_label = try allocator.dupe(u8, edge.label[match..]);
99 allocator.free(edge.label);
100 const to_node = edge.to;
101 edge.to = mid;
102 edge.label = try allocator.dupe(u8, label[0..match]);
103 self.base.node_count += 1;
104
105 try mid.edges.append(allocator, .{
106 .from = mid,
107 .to = to_node,
108 .label = to_label,
109 });
110
111 return if (match == label.len) mid else mid.put(allocator, label[match..]);
112 }
113
114 // Add a new node.
115 const node = try allocator.create(Node);
116 node.* = .{ .base = self.base };
117 self.base.node_count += 1;
118
119 try self.edges.append(allocator, .{
120 .from = self,
121 .to = node,
122 .label = try allocator.dupe(u8, label),
123 });
124
125 return node;
126 }
127
128 /// Recursively parses the node from the input byte stream.
129 fn read(self: *Node, allocator: Allocator, reader: anytype) Trie.ReadError!usize {
130 self.node_dirty = true;
131 const trie_offset = try reader.context.getPos();
132 self.trie_offset = trie_offset;
133
134 var nread: usize = 0;
135
136 const node_size = try leb.readULEB128(u64, reader);
137 if (node_size > 0) {
138 const export_flags = try leb.readULEB128(u64, reader);
139 // TODO Parse special flags.
140 assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
141 export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
142
143 const vmaddr_offset = try leb.readULEB128(u64, reader);
144
145 self.terminal_info = .{
146 .export_flags = export_flags,
147 .vmaddr_offset = vmaddr_offset,
148 };
149 }
150
151 const nedges = try reader.readByte();
152 self.base.node_count += nedges;
153
154 nread += (try reader.context.getPos()) - trie_offset;
155
156 var i: usize = 0;
157 while (i < nedges) : (i += 1) {
158 const edge_start_pos = try reader.context.getPos();
159
160 const label = blk: {
161 var label_buf = std.ArrayList(u8).init(allocator);
162 while (true) {
163 const next = try reader.readByte();
164 if (next == @as(u8, 0))
165 break;
166 try label_buf.append(next);
167 }
168 break :blk try label_buf.toOwnedSlice();
169 };
170
171 const seek_to = try leb.readULEB128(u64, reader);
172 const return_pos = try reader.context.getPos();
173
174 nread += return_pos - edge_start_pos;
175 try reader.context.seekTo(seek_to);
176
177 const node = try allocator.create(Node);
178 node.* = .{ .base = self.base };
179
180 nread += try node.read(allocator, reader);
181 try self.edges.append(allocator, .{
182 .from = self,
183 .to = node,
184 .label = label,
185 });
186 try reader.context.seekTo(return_pos);
187 }
188
189 return nread;
190 }
191
192 /// Writes this node to a byte stream.
193 /// The children of this node *are* not written to the byte stream
194 /// recursively. To write all nodes to a byte stream in sequence,
195 /// iterate over `Trie.ordered_nodes` and call this method on each node.
196 /// This is one of the requirements of the MachO.
197 /// Panics if `finalize` was not called before calling this method.
198 fn write(self: Node, writer: anytype) !void {
199 assert(!self.node_dirty);
200 if (self.terminal_info) |info| {
201 // Terminal node info: encode export flags and vmaddr offset of this symbol.
202 var info_buf: [@sizeOf(u64) * 2]u8 = undefined;
203 var info_stream = std.io.fixedBufferStream(&info_buf);
204 // TODO Implement for special flags.
205 assert(info.export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
206 info.export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
207 try leb.writeULEB128(info_stream.writer(), info.export_flags);
208 try leb.writeULEB128(info_stream.writer(), info.vmaddr_offset);
209
210 // Encode the size of the terminal node info.
211 var size_buf: [@sizeOf(u64)]u8 = undefined;
212 var size_stream = std.io.fixedBufferStream(&size_buf);
213 try leb.writeULEB128(size_stream.writer(), info_stream.pos);
214
215 // Now, write them to the output stream.
216 try writer.writeAll(size_buf[0..size_stream.pos]);
217 try writer.writeAll(info_buf[0..info_stream.pos]);
218 } else {
219 // Non-terminal node is delimited by 0 byte.
220 try writer.writeByte(0);
221 }
222 // Write number of edges (max legal number of edges is 256).
223 try writer.writeByte(@as(u8, @intCast(self.edges.items.len)));
224
225 for (self.edges.items) |edge| {
226 // Write edge label and offset to next node in trie.
227 try writer.writeAll(edge.label);
228 try writer.writeByte(0);
229 try leb.writeULEB128(writer, edge.to.trie_offset.?);
230 }
231 }
232
233 const FinalizeResult = struct {
234 /// Current size of this node in bytes.
235 node_size: u64,
236
237 /// True if the trie offset of this node in the output byte stream
238 /// would need updating; false otherwise.
239 updated: bool,
240 };
241
242 /// Updates offset of this node in the output byte stream.
243 fn finalize(self: *Node, offset_in_trie: u64) !FinalizeResult {
244 var stream = std.io.countingWriter(std.io.null_writer);
245 var writer = stream.writer();
246
247 var node_size: u64 = 0;
248 if (self.terminal_info) |info| {
249 try leb.writeULEB128(writer, info.export_flags);
250 try leb.writeULEB128(writer, info.vmaddr_offset);
251 try leb.writeULEB128(writer, stream.bytes_written);
252 } else {
253 node_size += 1; // 0x0 for non-terminal nodes
254 }
255 node_size += 1; // 1 byte for edge count
256
257 for (self.edges.items) |edge| {
258 const next_node_offset = edge.to.trie_offset orelse 0;
259 node_size += edge.label.len + 1;
260 try leb.writeULEB128(writer, next_node_offset);
261 }
262
263 const trie_offset = self.trie_offset orelse 0;
264 const updated = offset_in_trie != trie_offset;
265 self.trie_offset = offset_in_trie;
266 self.node_dirty = false;
267 node_size += stream.bytes_written;
268
269 return FinalizeResult{ .node_size = node_size, .updated = updated };
270 }
271};
272
27331/// The root node of the trie.
27432root: ?*Node = null,
27533
......@@ -611,3 +369,245 @@ test "ordering bug" {
611369 _ = try trie.write(stream.writer());
612370 try expectEqualHexStrings(&exp_buffer, buffer);
613371}
372
373pub const Node = struct {
374 base: *Trie,
375
376 /// Terminal info associated with this node.
377 /// If this node is not a terminal node, info is null.
378 terminal_info: ?struct {
379 /// Export flags associated with this exported symbol.
380 export_flags: u64,
381 /// VM address offset wrt to the section this symbol is defined against.
382 vmaddr_offset: u64,
383 } = null,
384
385 /// Offset of this node in the trie output byte stream.
386 trie_offset: ?u64 = null,
387
388 /// List of all edges originating from this node.
389 edges: std.ArrayListUnmanaged(Edge) = .{},
390
391 node_dirty: bool = true,
392
393 /// Edge connecting to nodes in the trie.
394 pub const Edge = struct {
395 from: *Node,
396 to: *Node,
397 label: []u8,
398
399 fn deinit(self: *Edge, allocator: Allocator) void {
400 self.to.deinit(allocator);
401 allocator.destroy(self.to);
402 allocator.free(self.label);
403 self.from = undefined;
404 self.to = undefined;
405 self.label = undefined;
406 }
407 };
408
409 fn deinit(self: *Node, allocator: Allocator) void {
410 for (self.edges.items) |*edge| {
411 edge.deinit(allocator);
412 }
413 self.edges.deinit(allocator);
414 }
415
416 /// Inserts a new node starting from `self`.
417 fn put(self: *Node, allocator: Allocator, label: []const u8) !*Node {
418 // Check for match with edges from this node.
419 for (self.edges.items) |*edge| {
420 const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.to;
421 if (match == 0) continue;
422 if (match == edge.label.len) return edge.to.put(allocator, label[match..]);
423
424 // Found a match, need to splice up nodes.
425 // From: A -> B
426 // To: A -> C -> B
427 const mid = try allocator.create(Node);
428 mid.* = .{ .base = self.base };
429 var to_label = try allocator.dupe(u8, edge.label[match..]);
430 allocator.free(edge.label);
431 const to_node = edge.to;
432 edge.to = mid;
433 edge.label = try allocator.dupe(u8, label[0..match]);
434 self.base.node_count += 1;
435
436 try mid.edges.append(allocator, .{
437 .from = mid,
438 .to = to_node,
439 .label = to_label,
440 });
441
442 return if (match == label.len) mid else mid.put(allocator, label[match..]);
443 }
444
445 // Add a new node.
446 const node = try allocator.create(Node);
447 node.* = .{ .base = self.base };
448 self.base.node_count += 1;
449
450 try self.edges.append(allocator, .{
451 .from = self,
452 .to = node,
453 .label = try allocator.dupe(u8, label),
454 });
455
456 return node;
457 }
458
459 /// Recursively parses the node from the input byte stream.
460 fn read(self: *Node, allocator: Allocator, reader: anytype) Trie.ReadError!usize {
461 self.node_dirty = true;
462 const trie_offset = try reader.context.getPos();
463 self.trie_offset = trie_offset;
464
465 var nread: usize = 0;
466
467 const node_size = try leb.readULEB128(u64, reader);
468 if (node_size > 0) {
469 const export_flags = try leb.readULEB128(u64, reader);
470 // TODO Parse special flags.
471 assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
472 export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
473
474 const vmaddr_offset = try leb.readULEB128(u64, reader);
475
476 self.terminal_info = .{
477 .export_flags = export_flags,
478 .vmaddr_offset = vmaddr_offset,
479 };
480 }
481
482 const nedges = try reader.readByte();
483 self.base.node_count += nedges;
484
485 nread += (try reader.context.getPos()) - trie_offset;
486
487 var i: usize = 0;
488 while (i < nedges) : (i += 1) {
489 const edge_start_pos = try reader.context.getPos();
490
491 const label = blk: {
492 var label_buf = std.ArrayList(u8).init(allocator);
493 while (true) {
494 const next = try reader.readByte();
495 if (next == @as(u8, 0))
496 break;
497 try label_buf.append(next);
498 }
499 break :blk try label_buf.toOwnedSlice();
500 };
501
502 const seek_to = try leb.readULEB128(u64, reader);
503 const return_pos = try reader.context.getPos();
504
505 nread += return_pos - edge_start_pos;
506 try reader.context.seekTo(seek_to);
507
508 const node = try allocator.create(Node);
509 node.* = .{ .base = self.base };
510
511 nread += try node.read(allocator, reader);
512 try self.edges.append(allocator, .{
513 .from = self,
514 .to = node,
515 .label = label,
516 });
517 try reader.context.seekTo(return_pos);
518 }
519
520 return nread;
521 }
522
523 /// Writes this node to a byte stream.
524 /// The children of this node *are* not written to the byte stream
525 /// recursively. To write all nodes to a byte stream in sequence,
526 /// iterate over `Trie.ordered_nodes` and call this method on each node.
527 /// This is one of the requirements of the MachO.
528 /// Panics if `finalize` was not called before calling this method.
529 fn write(self: Node, writer: anytype) !void {
530 assert(!self.node_dirty);
531 if (self.terminal_info) |info| {
532 // Terminal node info: encode export flags and vmaddr offset of this symbol.
533 var info_buf: [@sizeOf(u64) * 2]u8 = undefined;
534 var info_stream = std.io.fixedBufferStream(&info_buf);
535 // TODO Implement for special flags.
536 assert(info.export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
537 info.export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
538 try leb.writeULEB128(info_stream.writer(), info.export_flags);
539 try leb.writeULEB128(info_stream.writer(), info.vmaddr_offset);
540
541 // Encode the size of the terminal node info.
542 var size_buf: [@sizeOf(u64)]u8 = undefined;
543 var size_stream = std.io.fixedBufferStream(&size_buf);
544 try leb.writeULEB128(size_stream.writer(), info_stream.pos);
545
546 // Now, write them to the output stream.
547 try writer.writeAll(size_buf[0..size_stream.pos]);
548 try writer.writeAll(info_buf[0..info_stream.pos]);
549 } else {
550 // Non-terminal node is delimited by 0 byte.
551 try writer.writeByte(0);
552 }
553 // Write number of edges (max legal number of edges is 256).
554 try writer.writeByte(@as(u8, @intCast(self.edges.items.len)));
555
556 for (self.edges.items) |edge| {
557 // Write edge label and offset to next node in trie.
558 try writer.writeAll(edge.label);
559 try writer.writeByte(0);
560 try leb.writeULEB128(writer, edge.to.trie_offset.?);
561 }
562 }
563
564 const FinalizeResult = struct {
565 /// Current size of this node in bytes.
566 node_size: u64,
567
568 /// True if the trie offset of this node in the output byte stream
569 /// would need updating; false otherwise.
570 updated: bool,
571 };
572
573 /// Updates offset of this node in the output byte stream.
574 fn finalize(self: *Node, offset_in_trie: u64) !FinalizeResult {
575 var stream = std.io.countingWriter(std.io.null_writer);
576 var writer = stream.writer();
577
578 var node_size: u64 = 0;
579 if (self.terminal_info) |info| {
580 try leb.writeULEB128(writer, info.export_flags);
581 try leb.writeULEB128(writer, info.vmaddr_offset);
582 try leb.writeULEB128(writer, stream.bytes_written);
583 } else {
584 node_size += 1; // 0x0 for non-terminal nodes
585 }
586 node_size += 1; // 1 byte for edge count
587
588 for (self.edges.items) |edge| {
589 const next_node_offset = edge.to.trie_offset orelse 0;
590 node_size += edge.label.len + 1;
591 try leb.writeULEB128(writer, next_node_offset);
592 }
593
594 const trie_offset = self.trie_offset orelse 0;
595 const updated = offset_in_trie != trie_offset;
596 self.trie_offset = offset_in_trie;
597 self.node_dirty = false;
598 node_size += stream.bytes_written;
599
600 return FinalizeResult{ .node_size = node_size, .updated = updated };
601 }
602};
603
604const Trie = @This();
605
606const std = @import("std");
607const mem = std.mem;
608const leb = std.leb;
609const log = std.log.scoped(.link);
610const macho = std.macho;
611const testing = std.testing;
612const assert = std.debug.assert;
613const Allocator = mem.Allocator;
src/link/MachO/UnwindInfo.zig+69-71
......@@ -1,25 +1,3 @@
1const UnwindInfo = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const eh_frame = @import("eh_frame.zig");
6const fs = std.fs;
7const leb = std.leb;
8const log = std.log.scoped(.unwind_info);
9const macho = std.macho;
10const math = std.math;
11const mem = std.mem;
12const trace = @import("../../tracy.zig").trace;
13
14const Allocator = mem.Allocator;
15const Atom = @import("Atom.zig");
16const AtomIndex = @import("zld.zig").AtomIndex;
17const EhFrameRecord = eh_frame.EhFrameRecord;
18const MachO = @import("../MachO.zig");
19const Object = @import("Object.zig");
20const SymbolWithLoc = MachO.SymbolWithLoc;
21const Zld = @import("zld.zig").Zld;
22
231gpa: Allocator,
242
253/// List of all unwind records gathered from all objects and sorted
......@@ -203,28 +181,28 @@ pub fn deinit(info: *UnwindInfo) void {
203181 info.lsdas_lookup.deinit(info.gpa);
204182}
205183
206pub fn scanRelocs(zld: *Zld) !void {
207 if (zld.unwind_info_section_index == null) return;
184pub fn scanRelocs(macho_file: *MachO) !void {
185 if (macho_file.unwind_info_section_index == null) return;
208186
209 const cpu_arch = zld.options.target.cpu.arch;
210 for (zld.objects.items, 0..) |*object, object_id| {
187 const cpu_arch = macho_file.base.options.target.cpu.arch;
188 for (macho_file.objects.items, 0..) |*object, object_id| {
211189 const unwind_records = object.getUnwindRecords();
212190 for (object.exec_atoms.items) |atom_index| {
213 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
191 var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
214192 while (inner_syms_it.next()) |sym| {
215193 const record_id = object.unwind_records_lookup.get(sym) orelse continue;
216194 if (object.unwind_relocs_lookup[record_id].dead) continue;
217195 const record = unwind_records[record_id];
218196 if (!UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
219 if (getPersonalityFunctionReloc(zld, @as(u32, @intCast(object_id)), record_id)) |rel| {
197 if (getPersonalityFunctionReloc(macho_file, @as(u32, @intCast(object_id)), record_id)) |rel| {
220198 // Personality function; add GOT pointer.
221 const target = Atom.parseRelocTarget(zld, .{
199 const target = Atom.parseRelocTarget(macho_file, .{
222200 .object_id = @as(u32, @intCast(object_id)),
223201 .rel = rel,
224202 .code = mem.asBytes(&record),
225203 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
226204 });
227 try zld.addGotEntry(target);
205 try macho_file.addGotEntry(target);
228206 }
229207 }
230208 }
......@@ -232,10 +210,10 @@ pub fn scanRelocs(zld: *Zld) !void {
232210 }
233211}
234212
235pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
236 if (zld.unwind_info_section_index == null) return;
213pub fn collect(info: *UnwindInfo, macho_file: *MachO) !void {
214 if (macho_file.unwind_info_section_index == null) return;
237215
238 const cpu_arch = zld.options.target.cpu.arch;
216 const cpu_arch = macho_file.base.options.target.cpu.arch;
239217
240218 var records = std.ArrayList(macho.compact_unwind_entry).init(info.gpa);
241219 defer records.deinit();
......@@ -244,7 +222,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
244222 defer sym_indexes.deinit();
245223
246224 // TODO handle dead stripping
247 for (zld.objects.items, 0..) |*object, object_id| {
225 for (macho_file.objects.items, 0..) |*object, object_id| {
248226 log.debug("collecting unwind records in {s} ({d})", .{ object.name, object_id });
249227 const unwind_records = object.getUnwindRecords();
250228
......@@ -254,7 +232,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
254232 try sym_indexes.ensureUnusedCapacity(object.exec_atoms.items.len);
255233
256234 for (object.exec_atoms.items) |atom_index| {
257 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
235 var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
258236 var prev_symbol: ?SymbolWithLoc = null;
259237 while (inner_syms_it.next()) |symbol| {
260238 var record = if (object.unwind_records_lookup.get(symbol)) |record_id| blk: {
......@@ -262,14 +240,14 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
262240 var record = unwind_records[record_id];
263241
264242 if (UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
265 try info.collectPersonalityFromDwarf(zld, @as(u32, @intCast(object_id)), symbol, &record);
243 try info.collectPersonalityFromDwarf(macho_file, @as(u32, @intCast(object_id)), symbol, &record);
266244 } else {
267245 if (getPersonalityFunctionReloc(
268 zld,
246 macho_file,
269247 @as(u32, @intCast(object_id)),
270248 record_id,
271249 )) |rel| {
272 const target = Atom.parseRelocTarget(zld, .{
250 const target = Atom.parseRelocTarget(macho_file, .{
273251 .object_id = @as(u32, @intCast(object_id)),
274252 .rel = rel,
275253 .code = mem.asBytes(&record),
......@@ -286,8 +264,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
286264 UnwindEncoding.setPersonalityIndex(&record.compactUnwindEncoding, personality_index + 1);
287265 }
288266
289 if (getLsdaReloc(zld, @as(u32, @intCast(object_id)), record_id)) |rel| {
290 const target = Atom.parseRelocTarget(zld, .{
267 if (getLsdaReloc(macho_file, @as(u32, @intCast(object_id)), record_id)) |rel| {
268 const target = Atom.parseRelocTarget(macho_file, .{
291269 .object_id = @as(u32, @intCast(object_id)),
292270 .rel = rel,
293271 .code = mem.asBytes(&record),
......@@ -298,7 +276,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
298276 }
299277 break :blk record;
300278 } else blk: {
301 const sym = zld.getSymbol(symbol);
279 const sym = macho_file.getSymbol(symbol);
302280 if (sym.n_desc == MachO.N_DEAD) continue;
303281 if (prev_symbol) |prev_sym| {
304282 const prev_addr = object.getSourceSymbol(prev_sym.sym_index).?.n_value;
......@@ -310,7 +288,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
310288 if (object.eh_frame_records_lookup.get(symbol)) |fde_offset| {
311289 if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
312290 var record = nullRecord();
313 try info.collectPersonalityFromDwarf(zld, @as(u32, @intCast(object_id)), symbol, &record);
291 try info.collectPersonalityFromDwarf(macho_file, @as(u32, @intCast(object_id)), symbol, &record);
314292 switch (cpu_arch) {
315293 .aarch64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_ARM64_MODE.DWARF),
316294 .x86_64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_X86_64_MODE.DWARF),
......@@ -323,8 +301,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
323301 break :blk nullRecord();
324302 };
325303
326 const atom = zld.getAtom(atom_index);
327 const sym = zld.getSymbol(symbol);
304 const atom = macho_file.getAtom(atom_index);
305 const sym = macho_file.getSymbol(symbol);
328306 assert(sym.n_desc != MachO.N_DEAD);
329307 const size = if (inner_syms_it.next()) |next_sym| blk: {
330308 // All this trouble to account for symbol aliases.
......@@ -336,8 +314,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
336314 const curr_addr = object.getSourceSymbol(symbol.sym_index).?.n_value;
337315 const next_addr = object.getSourceSymbol(next_sym.sym_index).?.n_value;
338316 if (next_addr > curr_addr) break :blk next_addr - curr_addr;
339 break :blk zld.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
340 } else zld.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
317 break :blk macho_file.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
318 } else macho_file.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
341319 record.rangeStart = sym.n_value;
342320 record.rangeLength = @as(u32, @intCast(size));
343321
......@@ -518,23 +496,23 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
518496
519497fn collectPersonalityFromDwarf(
520498 info: *UnwindInfo,
521 zld: *Zld,
499 macho_file: *MachO,
522500 object_id: u32,
523501 sym_loc: SymbolWithLoc,
524502 record: *macho.compact_unwind_entry,
525503) !void {
526 const object = &zld.objects.items[object_id];
504 const object = &macho_file.objects.items[object_id];
527505 var it = object.getEhFrameRecordsIterator();
528506 const fde_offset = object.eh_frame_records_lookup.get(sym_loc).?;
529507 it.seekTo(fde_offset);
530508 const fde = (try it.next()).?;
531 const cie_ptr = fde.getCiePointerSource(object_id, zld, fde_offset);
509 const cie_ptr = fde.getCiePointerSource(object_id, macho_file, fde_offset);
532510 const cie_offset = fde_offset + 4 - cie_ptr;
533511 it.seekTo(cie_offset);
534512 const cie = (try it.next()).?;
535513
536514 if (cie.getPersonalityPointerReloc(
537 zld,
515 macho_file,
538516 @as(u32, @intCast(object_id)),
539517 cie_offset,
540518 )) |target| {
......@@ -550,9 +528,9 @@ fn collectPersonalityFromDwarf(
550528 }
551529}
552530
553pub fn calcSectionSize(info: UnwindInfo, zld: *Zld) !void {
554 const sect_id = zld.unwind_info_section_index orelse return;
555 const sect = &zld.sections.items(.header)[sect_id];
531pub fn calcSectionSize(info: UnwindInfo, macho_file: *MachO) !void {
532 const sect_id = macho_file.unwind_info_section_index orelse return;
533 const sect = &macho_file.sections.items(.header)[sect_id];
556534 sect.@"align" = 2;
557535 sect.size = info.calcRequiredSize();
558536}
......@@ -569,23 +547,23 @@ fn calcRequiredSize(info: UnwindInfo) usize {
569547 return total_size;
570548}
571549
572pub fn write(info: *UnwindInfo, zld: *Zld) !void {
573 const sect_id = zld.unwind_info_section_index orelse return;
574 const sect = &zld.sections.items(.header)[sect_id];
575 const seg_id = zld.sections.items(.segment_index)[sect_id];
576 const seg = zld.segments.items[seg_id];
550pub fn write(info: *UnwindInfo, macho_file: *MachO) !void {
551 const sect_id = macho_file.unwind_info_section_index orelse return;
552 const sect = &macho_file.sections.items(.header)[sect_id];
553 const seg_id = macho_file.sections.items(.segment_index)[sect_id];
554 const seg = macho_file.segments.items[seg_id];
577555
578 const text_sect_id = zld.text_section_index.?;
579 const text_sect = zld.sections.items(.header)[text_sect_id];
556 const text_sect_id = macho_file.text_section_index.?;
557 const text_sect = macho_file.sections.items(.header)[text_sect_id];
580558
581559 var personalities: [max_personalities]u32 = undefined;
582 const cpu_arch = zld.options.target.cpu.arch;
560 const cpu_arch = macho_file.base.options.target.cpu.arch;
583561
584562 log.debug("Personalities:", .{});
585563 for (info.personalities[0..info.personalities_count], 0..) |target, i| {
586 const addr = zld.getGotEntryAddress(target).?;
564 const addr = macho_file.getGotEntryAddress(target).?;
587565 personalities[i] = @as(u32, @intCast(addr - seg.vmaddr));
588 log.debug(" {d}: 0x{x} ({s})", .{ i, personalities[i], zld.getSymbolName(target) });
566 log.debug(" {d}: 0x{x} ({s})", .{ i, personalities[i], macho_file.getSymbolName(target) });
589567 }
590568
591569 for (info.records.items) |*rec| {
......@@ -599,7 +577,7 @@ pub fn write(info: *UnwindInfo, zld: *Zld) !void {
599577 if (rec.compactUnwindEncoding > 0 and !UnwindEncoding.isDwarf(rec.compactUnwindEncoding, cpu_arch)) {
600578 const lsda_target = @as(SymbolWithLoc, @bitCast(rec.lsda));
601579 if (lsda_target.getFile()) |_| {
602 const sym = zld.getSymbol(lsda_target);
580 const sym = macho_file.getSymbol(lsda_target);
603581 rec.lsda = sym.n_value - seg.vmaddr;
604582 }
605583 }
......@@ -689,11 +667,11 @@ pub fn write(info: *UnwindInfo, zld: *Zld) !void {
689667 @memset(buffer.items[offset..], 0);
690668 }
691669
692 try zld.file.pwriteAll(buffer.items, sect.offset);
670 try macho_file.base.file.?.pwriteAll(buffer.items, sect.offset);
693671}
694672
695fn getRelocs(zld: *Zld, object_id: u32, record_id: usize) []const macho.relocation_info {
696 const object = &zld.objects.items[object_id];
673fn getRelocs(macho_file: *MachO, object_id: u32, record_id: usize) []const macho.relocation_info {
674 const object = &macho_file.objects.items[object_id];
697675 assert(object.hasUnwindRecords());
698676 const rel_pos = object.unwind_relocs_lookup[record_id].reloc;
699677 const relocs = object.getRelocs(object.unwind_info_sect_id.?);
......@@ -707,11 +685,11 @@ fn isPersonalityFunction(record_id: usize, rel: macho.relocation_info) bool {
707685}
708686
709687pub fn getPersonalityFunctionReloc(
710 zld: *Zld,
688 macho_file: *MachO,
711689 object_id: u32,
712690 record_id: usize,
713691) ?macho.relocation_info {
714 const relocs = getRelocs(zld, object_id, record_id);
692 const relocs = getRelocs(macho_file, object_id, record_id);
715693 for (relocs) |rel| {
716694 if (isPersonalityFunction(record_id, rel)) return rel;
717695 }
......@@ -735,8 +713,8 @@ fn isLsda(record_id: usize, rel: macho.relocation_info) bool {
735713 return rel_offset == 24;
736714}
737715
738pub fn getLsdaReloc(zld: *Zld, object_id: u32, record_id: usize) ?macho.relocation_info {
739 const relocs = getRelocs(zld, object_id, record_id);
716pub fn getLsdaReloc(macho_file: *MachO, object_id: u32, record_id: usize) ?macho.relocation_info {
717 const relocs = getRelocs(macho_file, object_id, record_id);
740718 for (relocs) |rel| {
741719 if (isLsda(record_id, rel)) return rel;
742720 }
......@@ -828,3 +806,23 @@ pub const UnwindEncoding = struct {
828806 enc.* |= offset;
829807 }
830808};
809
810const UnwindInfo = @This();
811
812const std = @import("std");
813const assert = std.debug.assert;
814const eh_frame = @import("eh_frame.zig");
815const fs = std.fs;
816const leb = std.leb;
817const log = std.log.scoped(.unwind_info);
818const macho = std.macho;
819const math = std.math;
820const mem = std.mem;
821const trace = @import("../../tracy.zig").trace;
822
823const Allocator = mem.Allocator;
824const Atom = @import("Atom.zig");
825const EhFrameRecord = eh_frame.EhFrameRecord;
826const MachO = @import("../MachO.zig");
827const Object = @import("Object.zig");
828const SymbolWithLoc = MachO.SymbolWithLoc;
src/link/MachO/dead_strip.zig+125-126
......@@ -1,89 +1,72 @@
11//! An algorithm for dead stripping of unreferenced Atoms.
22
3const std = @import("std");
4const assert = std.debug.assert;
5const eh_frame = @import("eh_frame.zig");
6const log = std.log.scoped(.dead_strip);
7const macho = std.macho;
8const math = std.math;
9const mem = std.mem;
10
11const Allocator = mem.Allocator;
12const Atom = @import("Atom.zig");
13const MachO = @import("../MachO.zig");
14const SymbolWithLoc = MachO.SymbolWithLoc;
15const UnwindInfo = @import("UnwindInfo.zig");
16const Zld = @import("zld.zig").Zld;
17
18const AtomTable = std.AutoHashMap(Atom.Index, void);
19
20pub fn gcAtoms(zld: *Zld) !void {
21 const gpa = zld.gpa;
3pub fn gcAtoms(macho_file: *MachO) !void {
4 const gpa = macho_file.base.allocator;
225
236 var arena = std.heap.ArenaAllocator.init(gpa);
247 defer arena.deinit();
258
269 var roots = AtomTable.init(arena.allocator());
27 try roots.ensureUnusedCapacity(@as(u32, @intCast(zld.globals.items.len)));
10 try roots.ensureUnusedCapacity(@as(u32, @intCast(macho_file.globals.items.len)));
2811
2912 var alive = AtomTable.init(arena.allocator());
30 try alive.ensureTotalCapacity(@as(u32, @intCast(zld.atoms.items.len)));
13 try alive.ensureTotalCapacity(@as(u32, @intCast(macho_file.atoms.items.len)));
3114
32 try collectRoots(zld, &roots);
33 try mark(zld, roots, &alive);
34 prune(zld, alive);
15 try collectRoots(macho_file, &roots);
16 try mark(macho_file, roots, &alive);
17 prune(macho_file, alive);
3518}
3619
37fn addRoot(zld: *Zld, roots: *AtomTable, file: u32, sym_loc: SymbolWithLoc) !void {
38 const sym = zld.getSymbol(sym_loc);
20fn addRoot(macho_file: *MachO, roots: *AtomTable, file: u32, sym_loc: SymbolWithLoc) !void {
21 const sym = macho_file.getSymbol(sym_loc);
3922 assert(!sym.undf());
40 const object = &zld.objects.items[file];
23 const object = &macho_file.objects.items[file];
4124 const atom_index = object.getAtomIndexForSymbol(sym_loc.sym_index).?; // panic here means fatal error
4225 log.debug("root(ATOM({d}, %{d}, {d}))", .{
4326 atom_index,
44 zld.getAtom(atom_index).sym_index,
27 macho_file.getAtom(atom_index).sym_index,
4528 file,
4629 });
4730 _ = try roots.getOrPut(atom_index);
4831}
4932
50fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
33fn collectRoots(macho_file: *MachO, roots: *AtomTable) !void {
5134 log.debug("collecting roots", .{});
5235
53 switch (zld.options.output_mode) {
36 switch (macho_file.base.options.output_mode) {
5437 .Exe => {
5538 // Add entrypoint as GC root
56 const global: SymbolWithLoc = zld.getEntryPoint();
39 const global: SymbolWithLoc = macho_file.getEntryPoint();
5740 if (global.getFile()) |file| {
58 try addRoot(zld, roots, file, global);
41 try addRoot(macho_file, roots, file, global);
5942 } else {
60 assert(zld.getSymbol(global).undf()); // Stub as our entrypoint is in a dylib.
43 assert(macho_file.getSymbol(global).undf()); // Stub as our entrypoint is in a dylib.
6144 }
6245 },
6346 else => |other| {
6447 assert(other == .Lib);
6548 // Add exports as GC roots
66 for (zld.globals.items) |global| {
67 const sym = zld.getSymbol(global);
49 for (macho_file.globals.items) |global| {
50 const sym = macho_file.getSymbol(global);
6851 if (sym.undf()) continue;
6952
7053 if (global.getFile()) |file| {
71 try addRoot(zld, roots, file, global);
54 try addRoot(macho_file, roots, file, global);
7255 }
7356 }
7457 },
7558 }
7659
7760 // Add all symbols force-defined by the user.
78 for (zld.options.force_undefined_symbols.keys()) |sym_name| {
79 const global_index = zld.resolver.get(sym_name).?;
80 const global = zld.globals.items[global_index];
81 const sym = zld.getSymbol(global);
61 for (macho_file.base.options.force_undefined_symbols.keys()) |sym_name| {
62 const global_index = macho_file.resolver.get(sym_name).?;
63 const global = macho_file.globals.items[global_index];
64 const sym = macho_file.getSymbol(global);
8265 assert(!sym.undf());
83 try addRoot(zld, roots, global.getFile().?, global);
66 try addRoot(macho_file, roots, global.getFile().?, global);
8467 }
8568
86 for (zld.objects.items) |object| {
69 for (macho_file.objects.items) |object| {
8770 const has_subsections = object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
8871
8972 for (object.atoms.items) |atom_index| {
......@@ -92,7 +75,7 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
9275 // as a root.
9376 if (!has_subsections) break :blk true;
9477
95 const atom = zld.getAtom(atom_index);
78 const atom = macho_file.getAtom(atom_index);
9679 const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
9780 source_sym.n_sect - 1
9881 else sect_id: {
......@@ -115,39 +98,39 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
11598
11699 log.debug("root(ATOM({d}, %{d}, {?d}))", .{
117100 atom_index,
118 zld.getAtom(atom_index).sym_index,
119 zld.getAtom(atom_index).getFile(),
101 macho_file.getAtom(atom_index).sym_index,
102 macho_file.getAtom(atom_index).getFile(),
120103 });
121104 }
122105 }
123106 }
124107}
125108
126fn markLive(zld: *Zld, atom_index: Atom.Index, alive: *AtomTable) void {
109fn markLive(macho_file: *MachO, atom_index: Atom.Index, alive: *AtomTable) void {
127110 if (alive.contains(atom_index)) return;
128111
129 const atom = zld.getAtom(atom_index);
112 const atom = macho_file.getAtom(atom_index);
130113 const sym_loc = atom.getSymbolWithLoc();
131114
132115 log.debug("mark(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() });
133116
134117 alive.putAssumeCapacityNoClobber(atom_index, {});
135118
136 const cpu_arch = zld.options.target.cpu.arch;
119 const cpu_arch = macho_file.options.target.cpu.arch;
137120
138 const sym = zld.getSymbol(atom.getSymbolWithLoc());
139 const header = zld.sections.items(.header)[sym.n_sect - 1];
121 const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
122 const header = macho_file.sections.items(.header)[sym.n_sect - 1];
140123 if (header.isZerofill()) return;
141124
142 const code = Atom.getAtomCode(zld, atom_index);
143 const relocs = Atom.getAtomRelocs(zld, atom_index);
144 const ctx = Atom.getRelocContext(zld, atom_index);
125 const code = Atom.getAtomCode(macho_file, atom_index);
126 const relocs = Atom.getAtomRelocs(macho_file, atom_index);
127 const ctx = Atom.getRelocContext(macho_file, atom_index);
145128
146129 for (relocs) |rel| {
147130 const target = switch (cpu_arch) {
148131 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
149132 .ARM64_RELOC_ADDEND => continue,
150 else => Atom.parseRelocTarget(zld, .{
133 else => Atom.parseRelocTarget(macho_file, .{
151134 .object_id = atom.getFile().?,
152135 .rel = rel,
153136 .code = code,
......@@ -155,7 +138,7 @@ fn markLive(zld: *Zld, atom_index: Atom.Index, alive: *AtomTable) void {
155138 .base_addr = ctx.base_addr,
156139 }),
157140 },
158 .x86_64 => Atom.parseRelocTarget(zld, .{
141 .x86_64 => Atom.parseRelocTarget(macho_file, .{
159142 .object_id = atom.getFile().?,
160143 .rel = rel,
161144 .code = code,
......@@ -164,50 +147,50 @@ fn markLive(zld: *Zld, atom_index: Atom.Index, alive: *AtomTable) void {
164147 }),
165148 else => unreachable,
166149 };
167 const target_sym = zld.getSymbol(target);
150 const target_sym = macho_file.getSymbol(target);
168151
169152 if (target_sym.undf()) continue;
170153 if (target.getFile() == null) {
171 const target_sym_name = zld.getSymbolName(target);
154 const target_sym_name = macho_file.getSymbolName(target);
172155 if (mem.eql(u8, "__mh_execute_header", target_sym_name)) continue;
173156 if (mem.eql(u8, "___dso_handle", target_sym_name)) continue;
174157
175158 unreachable; // referenced symbol not found
176159 }
177160
178 const object = zld.objects.items[target.getFile().?];
161 const object = macho_file.objects.items[target.getFile().?];
179162 const target_atom_index = object.getAtomIndexForSymbol(target.sym_index).?;
180163 log.debug(" following ATOM({d}, %{d}, {?d})", .{
181164 target_atom_index,
182 zld.getAtom(target_atom_index).sym_index,
183 zld.getAtom(target_atom_index).getFile(),
165 macho_file.getAtom(target_atom_index).sym_index,
166 macho_file.getAtom(target_atom_index).getFile(),
184167 });
185168
186 markLive(zld, target_atom_index, alive);
169 markLive(macho_file, target_atom_index, alive);
187170 }
188171}
189172
190fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool {
191 const atom = zld.getAtom(atom_index);
173fn refersLive(macho_file: *MachO, atom_index: Atom.Index, alive: AtomTable) bool {
174 const atom = macho_file.getAtom(atom_index);
192175 const sym_loc = atom.getSymbolWithLoc();
193176
194177 log.debug("refersLive(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() });
195178
196 const cpu_arch = zld.options.target.cpu.arch;
179 const cpu_arch = macho_file.base.options.target.cpu.arch;
197180
198 const sym = zld.getSymbol(sym_loc);
199 const header = zld.sections.items(.header)[sym.n_sect - 1];
181 const sym = macho_file.getSymbol(sym_loc);
182 const header = macho_file.sections.items(.header)[sym.n_sect - 1];
200183 assert(!header.isZerofill());
201184
202 const code = Atom.getAtomCode(zld, atom_index);
203 const relocs = Atom.getAtomRelocs(zld, atom_index);
204 const ctx = Atom.getRelocContext(zld, atom_index);
185 const code = Atom.getAtomCode(macho_file, atom_index);
186 const relocs = Atom.getAtomRelocs(macho_file, atom_index);
187 const ctx = Atom.getRelocContext(macho_file, atom_index);
205188
206189 for (relocs) |rel| {
207190 const target = switch (cpu_arch) {
208191 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
209192 .ARM64_RELOC_ADDEND => continue,
210 else => Atom.parseRelocTarget(zld, .{
193 else => Atom.parseRelocTarget(macho_file, .{
211194 .object_id = atom.getFile().?,
212195 .rel = rel,
213196 .code = code,
......@@ -215,7 +198,7 @@ fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool {
215198 .base_addr = ctx.base_addr,
216199 }),
217200 },
218 .x86_64 => Atom.parseRelocTarget(zld, .{
201 .x86_64 => Atom.parseRelocTarget(macho_file, .{
219202 .object_id = atom.getFile().?,
220203 .rel = rel,
221204 .code = code,
......@@ -225,16 +208,16 @@ fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool {
225208 else => unreachable,
226209 };
227210
228 const object = zld.objects.items[target.getFile().?];
211 const object = macho_file.objects.items[target.getFile().?];
229212 const target_atom_index = object.getAtomIndexForSymbol(target.sym_index) orelse {
230 log.debug("atom for symbol '{s}' not found; skipping...", .{zld.getSymbolName(target)});
213 log.debug("atom for symbol '{s}' not found; skipping...", .{macho_file.getSymbolName(target)});
231214 continue;
232215 };
233216 if (alive.contains(target_atom_index)) {
234217 log.debug(" refers live ATOM({d}, %{d}, {?d})", .{
235218 target_atom_index,
236 zld.getAtom(target_atom_index).sym_index,
237 zld.getAtom(target_atom_index).getFile(),
219 macho_file.getAtom(target_atom_index).sym_index,
220 macho_file.getAtom(target_atom_index).getFile(),
238221 });
239222 return true;
240223 }
......@@ -243,21 +226,21 @@ fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool {
243226 return false;
244227}
245228
246fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
229fn mark(macho_file: *MachO, roots: AtomTable, alive: *AtomTable) !void {
247230 var it = roots.keyIterator();
248231 while (it.next()) |root| {
249 markLive(zld, root.*, alive);
232 markLive(macho_file, root.*, alive);
250233 }
251234
252235 var loop: bool = true;
253236 while (loop) {
254237 loop = false;
255238
256 for (zld.objects.items) |object| {
239 for (macho_file.objects.items) |object| {
257240 for (object.atoms.items) |atom_index| {
258241 if (alive.contains(atom_index)) continue;
259242
260 const atom = zld.getAtom(atom_index);
243 const atom = macho_file.getAtom(atom_index);
261244 const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
262245 source_sym.n_sect - 1
263246 else blk: {
......@@ -268,8 +251,8 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
268251 const source_sect = object.getSourceSection(sect_id);
269252
270253 if (source_sect.isDontDeadStripIfReferencesLive()) {
271 if (refersLive(zld, atom_index, alive.*)) {
272 markLive(zld, atom_index, alive);
254 if (refersLive(macho_file, atom_index, alive.*)) {
255 markLive(macho_file, atom_index, alive);
273256 loop = true;
274257 }
275258 }
......@@ -277,26 +260,26 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
277260 }
278261 }
279262
280 for (zld.objects.items, 0..) |_, object_id| {
263 for (macho_file.objects.items, 0..) |_, object_id| {
281264 // Traverse unwind and eh_frame records noting if the source symbol has been marked, and if so,
282265 // marking all references as live.
283 try markUnwindRecords(zld, @as(u32, @intCast(object_id)), alive);
266 try markUnwindRecords(macho_file, @as(u32, @intCast(object_id)), alive);
284267 }
285268}
286269
287fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void {
288 const object = &zld.objects.items[object_id];
289 const cpu_arch = zld.options.target.cpu.arch;
270fn markUnwindRecords(macho_file: *MachO, object_id: u32, alive: *AtomTable) !void {
271 const object = &macho_file.objects.items[object_id];
272 const cpu_arch = macho_file.base.options.target.cpu.arch;
290273
291274 const unwind_records = object.getUnwindRecords();
292275
293276 for (object.exec_atoms.items) |atom_index| {
294 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
277 var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
295278
296279 if (!object.hasUnwindRecords()) {
297280 if (alive.contains(atom_index)) {
298281 // Mark references live and continue.
299 try markEhFrameRecords(zld, object_id, atom_index, alive);
282 try markEhFrameRecords(macho_file, object_id, atom_index, alive);
300283 } else {
301284 while (inner_syms_it.next()) |sym| {
302285 if (object.eh_frame_records_lookup.get(sym)) |fde_offset| {
......@@ -322,51 +305,51 @@ fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void {
322305
323306 const record = unwind_records[record_id];
324307 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
325 try markEhFrameRecords(zld, object_id, atom_index, alive);
308 try markEhFrameRecords(macho_file, object_id, atom_index, alive);
326309 } else {
327 if (UnwindInfo.getPersonalityFunctionReloc(zld, object_id, record_id)) |rel| {
328 const target = Atom.parseRelocTarget(zld, .{
310 if (UnwindInfo.getPersonalityFunctionReloc(macho_file, object_id, record_id)) |rel| {
311 const target = Atom.parseRelocTarget(macho_file, .{
329312 .object_id = object_id,
330313 .rel = rel,
331314 .code = mem.asBytes(&record),
332315 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
333316 });
334 const target_sym = zld.getSymbol(target);
317 const target_sym = macho_file.getSymbol(target);
335318 if (!target_sym.undf()) {
336 const target_object = zld.objects.items[target.getFile().?];
319 const target_object = macho_file.objects.items[target.getFile().?];
337320 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
338 markLive(zld, target_atom_index, alive);
321 markLive(macho_file, target_atom_index, alive);
339322 }
340323 }
341324
342 if (UnwindInfo.getLsdaReloc(zld, object_id, record_id)) |rel| {
343 const target = Atom.parseRelocTarget(zld, .{
325 if (UnwindInfo.getLsdaReloc(macho_file, object_id, record_id)) |rel| {
326 const target = Atom.parseRelocTarget(macho_file, .{
344327 .object_id = object_id,
345328 .rel = rel,
346329 .code = mem.asBytes(&record),
347330 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
348331 });
349 const target_object = zld.objects.items[target.getFile().?];
332 const target_object = macho_file.objects.items[target.getFile().?];
350333 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
351 markLive(zld, target_atom_index, alive);
334 markLive(macho_file, target_atom_index, alive);
352335 }
353336 }
354337 }
355338 }
356339}
357340
358fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive: *AtomTable) !void {
359 const cpu_arch = zld.options.target.cpu.arch;
360 const object = &zld.objects.items[object_id];
341fn markEhFrameRecords(macho_file: *MachO, object_id: u32, atom_index: Atom.Index, alive: *AtomTable) !void {
342 const cpu_arch = macho_file.base.options.target.cpu.arch;
343 const object = &macho_file.objects.items[object_id];
361344 var it = object.getEhFrameRecordsIterator();
362 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
345 var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
363346
364347 while (inner_syms_it.next()) |sym| {
365348 const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue; // Continue in case we hit a temp symbol alias
366349 it.seekTo(fde_offset);
367350 const fde = (try it.next()).?;
368351
369 const cie_ptr = fde.getCiePointerSource(object_id, zld, fde_offset);
352 const cie_ptr = fde.getCiePointerSource(object_id, macho_file, fde_offset);
370353 const cie_offset = fde_offset + 4 - cie_ptr;
371354 it.seekTo(cie_offset);
372355 const cie = (try it.next()).?;
......@@ -374,20 +357,20 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive:
374357 switch (cpu_arch) {
375358 .aarch64 => {
376359 // Mark FDE references which should include any referenced LSDA record
377 const relocs = eh_frame.getRelocs(zld, object_id, fde_offset);
360 const relocs = eh_frame.getRelocs(macho_file, object_id, fde_offset);
378361 for (relocs) |rel| {
379 const target = Atom.parseRelocTarget(zld, .{
362 const target = Atom.parseRelocTarget(macho_file, .{
380363 .object_id = object_id,
381364 .rel = rel,
382365 .code = fde.data,
383366 .base_offset = @as(i32, @intCast(fde_offset)) + 4,
384367 });
385 const target_sym = zld.getSymbol(target);
368 const target_sym = macho_file.getSymbol(target);
386369 if (!target_sym.undf()) blk: {
387 const target_object = zld.objects.items[target.getFile().?];
370 const target_object = macho_file.objects.items[target.getFile().?];
388371 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index) orelse
389372 break :blk;
390 markLive(zld, target_atom_index, alive);
373 markLive(macho_file, target_atom_index, alive);
391374 }
392375 }
393376 },
......@@ -401,7 +384,7 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive:
401384 // Mark LSDA record as live
402385 const sym_index = object.getSymbolByAddress(lsda_address, null);
403386 const target_atom_index = object.getAtomIndexForSymbol(sym_index).?;
404 markLive(zld, target_atom_index, alive);
387 markLive(macho_file, target_atom_index, alive);
405388 }
406389 },
407390 else => unreachable,
......@@ -409,20 +392,20 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive:
409392
410393 // Mark CIE references which should include any referenced personalities
411394 // that are defined locally.
412 if (cie.getPersonalityPointerReloc(zld, object_id, cie_offset)) |target| {
413 const target_sym = zld.getSymbol(target);
395 if (cie.getPersonalityPointerReloc(macho_file, object_id, cie_offset)) |target| {
396 const target_sym = macho_file.getSymbol(target);
414397 if (!target_sym.undf()) {
415 const target_object = zld.objects.items[target.getFile().?];
398 const target_object = macho_file.objects.items[target.getFile().?];
416399 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
417 markLive(zld, target_atom_index, alive);
400 markLive(macho_file, target_atom_index, alive);
418401 }
419402 }
420403 }
421404}
422405
423fn prune(zld: *Zld, alive: AtomTable) void {
406fn prune(macho_file: *MachO, alive: AtomTable) void {
424407 log.debug("pruning dead atoms", .{});
425 for (zld.objects.items) |*object| {
408 for (macho_file.objects.items) |*object| {
426409 var i: usize = 0;
427410 while (i < object.atoms.items.len) {
428411 const atom_index = object.atoms.items[i];
......@@ -431,7 +414,7 @@ fn prune(zld: *Zld, alive: AtomTable) void {
431414 continue;
432415 }
433416
434 const atom = zld.getAtom(atom_index);
417 const atom = macho_file.getAtom(atom_index);
435418 const sym_loc = atom.getSymbolWithLoc();
436419
437420 log.debug("prune(ATOM({d}, %{d}, {?d}))", .{
......@@ -439,15 +422,15 @@ fn prune(zld: *Zld, alive: AtomTable) void {
439422 sym_loc.sym_index,
440423 sym_loc.getFile(),
441424 });
442 log.debug(" {s} in {s}", .{ zld.getSymbolName(sym_loc), object.name });
425 log.debug(" {s} in {s}", .{ macho_file.getSymbolName(sym_loc), object.name });
443426
444 const sym = zld.getSymbolPtr(sym_loc);
427 const sym = macho_file.getSymbolPtr(sym_loc);
445428 const sect_id = sym.n_sect - 1;
446 var section = zld.sections.get(sect_id);
429 var section = macho_file.sections.get(sect_id);
447430 section.header.size -= atom.size;
448431
449432 if (atom.prev_index) |prev_index| {
450 const prev = zld.getAtomPtr(prev_index);
433 const prev = macho_file.getAtomPtr(prev_index);
451434 prev.next_index = atom.next_index;
452435 } else {
453436 if (atom.next_index) |next_index| {
......@@ -455,7 +438,7 @@ fn prune(zld: *Zld, alive: AtomTable) void {
455438 }
456439 }
457440 if (atom.next_index) |next_index| {
458 const next = zld.getAtomPtr(next_index);
441 const next = macho_file.getAtomPtr(next_index);
459442 next.prev_index = atom.prev_index;
460443 } else {
461444 if (atom.prev_index) |prev_index| {
......@@ -467,21 +450,37 @@ fn prune(zld: *Zld, alive: AtomTable) void {
467450 }
468451 }
469452
470 zld.sections.set(sect_id, section);
453 macho_file.sections.set(sect_id, section);
471454 _ = object.atoms.swapRemove(i);
472455
473456 sym.n_desc = MachO.N_DEAD;
474457
475 var inner_sym_it = Atom.getInnerSymbolsIterator(zld, atom_index);
458 var inner_sym_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
476459 while (inner_sym_it.next()) |inner| {
477 const inner_sym = zld.getSymbolPtr(inner);
460 const inner_sym = macho_file.getSymbolPtr(inner);
478461 inner_sym.n_desc = MachO.N_DEAD;
479462 }
480463
481 if (Atom.getSectionAlias(zld, atom_index)) |alias| {
482 const alias_sym = zld.getSymbolPtr(alias);
464 if (Atom.getSectionAlias(macho_file, atom_index)) |alias| {
465 const alias_sym = macho_file.getSymbolPtr(alias);
483466 alias_sym.n_desc = MachO.N_DEAD;
484467 }
485468 }
486469 }
487470}
471
472const std = @import("std");
473const assert = std.debug.assert;
474const eh_frame = @import("eh_frame.zig");
475const log = std.log.scoped(.dead_strip);
476const macho = std.macho;
477const math = std.math;
478const mem = std.mem;
479
480const Allocator = mem.Allocator;
481const Atom = @import("Atom.zig");
482const MachO = @import("../MachO.zig");
483const SymbolWithLoc = MachO.SymbolWithLoc;
484const UnwindInfo = @import("UnwindInfo.zig");
485
486const AtomTable = std.AutoHashMap(Atom.Index, void);
src/link/MachO/dyld_info/Rebase.zig+11-11
......@@ -1,14 +1,3 @@
1const Rebase = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const leb = std.leb;
6const log = std.log.scoped(.dyld_info);
7const macho = std.macho;
8const testing = std.testing;
9
10const Allocator = std.mem.Allocator;
11
121entries: std.ArrayListUnmanaged(Entry) = .{},
132buffer: std.ArrayListUnmanaged(u8) = .{},
143
......@@ -572,3 +561,14 @@ test "rebase - composite" {
572561 macho.REBASE_OPCODE_DONE,
573562 }, rebase.buffer.items);
574563}
564
565const Rebase = @This();
566
567const std = @import("std");
568const assert = std.debug.assert;
569const leb = std.leb;
570const log = std.log.scoped(.dyld_info);
571const macho = std.macho;
572const testing = std.testing;
573
574const Allocator = std.mem.Allocator;
src/link/MachO/dyld_info/bind.zig+9-9
......@@ -1,12 +1,3 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const leb = std.leb;
4const log = std.log.scoped(.dyld_info);
5const macho = std.macho;
6const testing = std.testing;
7
8const Allocator = std.mem.Allocator;
9
101pub fn Bind(comptime Ctx: type, comptime Target: type) type {
112 return struct {
123 entries: std.ArrayListUnmanaged(Entry) = .{},
......@@ -738,3 +729,12 @@ test "lazy bind" {
738729 macho.BIND_OPCODE_DONE,
739730 }, bind.buffer.items);
740731}
732
733const std = @import("std");
734const assert = std.debug.assert;
735const leb = std.leb;
736const log = std.log.scoped(.dyld_info);
737const macho = std.macho;
738const testing = std.testing;
739
740const Allocator = std.mem.Allocator;
src/link/MachO/eh_frame.zig+63-64
......@@ -1,68 +1,52 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const macho = std.macho;
4const math = std.math;
5const mem = std.mem;
6const leb = std.leb;
7const log = std.log.scoped(.eh_frame);
8
9const Allocator = mem.Allocator;
10const Atom = @import("Atom.zig");
11const MachO = @import("../MachO.zig");
12const Relocation = @import("Relocation.zig");
13const SymbolWithLoc = MachO.SymbolWithLoc;
14const UnwindInfo = @import("UnwindInfo.zig");
15const Zld = @import("zld.zig").Zld;
16
17pub fn scanRelocs(zld: *Zld) !void {
18 const gpa = zld.gpa;
1pub fn scanRelocs(macho_file: *MachO) !void {
2 const gpa = macho_file.base.allocator;
193
20 for (zld.objects.items, 0..) |*object, object_id| {
4 for (macho_file.objects.items, 0..) |*object, object_id| {
215 var cies = std.AutoHashMap(u32, void).init(gpa);
226 defer cies.deinit();
237
248 var it = object.getEhFrameRecordsIterator();
259
2610 for (object.exec_atoms.items) |atom_index| {
27 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
11 var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
2812 while (inner_syms_it.next()) |sym| {
2913 const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue;
3014 if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
3115 it.seekTo(fde_offset);
3216 const fde = (try it.next()).?;
3317
34 const cie_ptr = fde.getCiePointerSource(@intCast(object_id), zld, fde_offset);
18 const cie_ptr = fde.getCiePointerSource(@intCast(object_id), macho_file, fde_offset);
3519 const cie_offset = fde_offset + 4 - cie_ptr;
3620
3721 if (!cies.contains(cie_offset)) {
3822 try cies.putNoClobber(cie_offset, {});
3923 it.seekTo(cie_offset);
4024 const cie = (try it.next()).?;
41 try cie.scanRelocs(zld, @as(u32, @intCast(object_id)), cie_offset);
25 try cie.scanRelocs(macho_file, @as(u32, @intCast(object_id)), cie_offset);
4226 }
4327 }
4428 }
4529 }
4630}
4731
48pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
49 const sect_id = zld.eh_frame_section_index orelse return;
50 const sect = &zld.sections.items(.header)[sect_id];
32pub fn calcSectionSize(macho_file: *MachO, unwind_info: *const UnwindInfo) !void {
33 const sect_id = macho_file.eh_frame_section_index orelse return;
34 const sect = &macho_file.sections.items(.header)[sect_id];
5135 sect.@"align" = 3;
5236 sect.size = 0;
5337
54 const cpu_arch = zld.options.target.cpu.arch;
55 const gpa = zld.gpa;
38 const cpu_arch = macho_file.base.options.target.cpu.arch;
39 const gpa = macho_file.base.allocator;
5640 var size: u32 = 0;
5741
58 for (zld.objects.items, 0..) |*object, object_id| {
42 for (macho_file.objects.items, 0..) |*object, object_id| {
5943 var cies = std.AutoHashMap(u32, u32).init(gpa);
6044 defer cies.deinit();
6145
6246 var eh_it = object.getEhFrameRecordsIterator();
6347
6448 for (object.exec_atoms.items) |atom_index| {
65 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
49 var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
6650 while (inner_syms_it.next()) |sym| {
6751 const fde_record_offset = object.eh_frame_records_lookup.get(sym) orelse continue;
6852 if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;
......@@ -77,7 +61,7 @@ pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
7761 eh_it.seekTo(fde_record_offset);
7862 const source_fde_record = (try eh_it.next()).?;
7963
80 const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset);
64 const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), macho_file, fde_record_offset);
8165 const cie_offset = fde_record_offset + 4 - cie_ptr;
8266
8367 const gop = try cies.getOrPut(cie_offset);
......@@ -96,14 +80,14 @@ pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
9680 }
9781}
9882
99pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
100 const sect_id = zld.eh_frame_section_index orelse return;
101 const sect = zld.sections.items(.header)[sect_id];
102 const seg_id = zld.sections.items(.segment_index)[sect_id];
103 const seg = zld.segments.items[seg_id];
83pub fn write(macho_file: *MachO, unwind_info: *UnwindInfo) !void {
84 const sect_id = macho_file.eh_frame_section_index orelse return;
85 const sect = macho_file.sections.items(.header)[sect_id];
86 const seg_id = macho_file.sections.items(.segment_index)[sect_id];
87 const seg = macho_file.segments.items[seg_id];
10488
105 const cpu_arch = zld.options.target.cpu.arch;
106 const gpa = zld.gpa;
89 const cpu_arch = macho_file.base.options.target.cpu.arch;
90 const gpa = macho_file.base.allocator;
10791
10892 var eh_records = std.AutoArrayHashMap(u32, EhFrameRecord(true)).init(gpa);
10993 defer {
......@@ -115,7 +99,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
11599
116100 var eh_frame_offset: u32 = 0;
117101
118 for (zld.objects.items, 0..) |*object, object_id| {
102 for (macho_file.objects.items, 0..) |*object, object_id| {
119103 try eh_records.ensureUnusedCapacity(2 * @as(u32, @intCast(object.exec_atoms.items.len)));
120104
121105 var cies = std.AutoHashMap(u32, u32).init(gpa);
......@@ -124,7 +108,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
124108 var eh_it = object.getEhFrameRecordsIterator();
125109
126110 for (object.exec_atoms.items) |atom_index| {
127 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
111 var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
128112 while (inner_syms_it.next()) |target| {
129113 const fde_record_offset = object.eh_frame_records_lookup.get(target) orelse continue;
130114 if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;
......@@ -139,7 +123,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
139123 eh_it.seekTo(fde_record_offset);
140124 const source_fde_record = (try eh_it.next()).?;
141125
142 const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset);
126 const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), macho_file, fde_record_offset);
143127 const cie_offset = fde_record_offset + 4 - cie_ptr;
144128
145129 const gop = try cies.getOrPut(cie_offset);
......@@ -147,7 +131,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
147131 eh_it.seekTo(cie_offset);
148132 const source_cie_record = (try eh_it.next()).?;
149133 var cie_record = try source_cie_record.toOwned(gpa);
150 try cie_record.relocate(zld, @as(u32, @intCast(object_id)), .{
134 try cie_record.relocate(macho_file, @as(u32, @intCast(object_id)), .{
151135 .source_offset = cie_offset,
152136 .out_offset = eh_frame_offset,
153137 .sect_addr = sect.addr,
......@@ -158,7 +142,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
158142 }
159143
160144 var fde_record = try source_fde_record.toOwned(gpa);
161 try fde_record.relocate(zld, @as(u32, @intCast(object_id)), .{
145 try fde_record.relocate(macho_file, @as(u32, @intCast(object_id)), .{
162146 .source_offset = fde_record_offset,
163147 .out_offset = eh_frame_offset,
164148 .sect_addr = sect.addr,
......@@ -169,7 +153,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
169153 .aarch64 => {}, // relocs take care of LSDA pointers
170154 .x86_64 => {
171155 // We need to relocate target symbol address ourselves.
172 const atom_sym = zld.getSymbol(target);
156 const atom_sym = macho_file.getSymbol(target);
173157 try fde_record.setTargetSymbolAddress(atom_sym.n_value, .{
174158 .base_addr = sect.addr,
175159 .base_offset = eh_frame_offset,
......@@ -229,7 +213,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
229213 try buffer.appendSlice(record.data);
230214 }
231215
232 try zld.file.pwriteAll(buffer.items, sect.offset);
216 try macho_file.base.file.?.pwriteAll(buffer.items, sect.offset);
233217}
234218const EhFrameRecordTag = enum { cie, fde };
235219
......@@ -261,12 +245,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
261245
262246 pub fn scanRelocs(
263247 rec: Record,
264 zld: *Zld,
248 macho_file: *MachO,
265249 object_id: u32,
266250 source_offset: u32,
267251 ) !void {
268 if (rec.getPersonalityPointerReloc(zld, object_id, source_offset)) |target| {
269 try zld.addGotEntry(target);
252 if (rec.getPersonalityPointerReloc(macho_file, object_id, source_offset)) |target| {
253 try macho_file.addGotEntry(target);
270254 }
271255 }
272256
......@@ -290,12 +274,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
290274
291275 pub fn getPersonalityPointerReloc(
292276 rec: Record,
293 zld: *Zld,
277 macho_file: *MachO,
294278 object_id: u32,
295279 source_offset: u32,
296280 ) ?SymbolWithLoc {
297 const cpu_arch = zld.options.target.cpu.arch;
298 const relocs = getRelocs(zld, object_id, source_offset);
281 const cpu_arch = macho_file.base.options.target.cpu.arch;
282 const relocs = getRelocs(macho_file, object_id, source_offset);
299283 for (relocs) |rel| {
300284 switch (cpu_arch) {
301285 .aarch64 => {
......@@ -317,7 +301,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
317301 },
318302 else => unreachable,
319303 }
320 const target = Atom.parseRelocTarget(zld, .{
304 const target = Atom.parseRelocTarget(macho_file, .{
321305 .object_id = object_id,
322306 .rel = rel,
323307 .code = rec.data,
......@@ -328,18 +312,18 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
328312 return null;
329313 }
330314
331 pub fn relocate(rec: *Record, zld: *Zld, object_id: u32, ctx: struct {
315 pub fn relocate(rec: *Record, macho_file: *MachO, object_id: u32, ctx: struct {
332316 source_offset: u32,
333317 out_offset: u32,
334318 sect_addr: u64,
335319 }) !void {
336320 comptime assert(is_mutable);
337321
338 const cpu_arch = zld.options.target.cpu.arch;
339 const relocs = getRelocs(zld, object_id, ctx.source_offset);
322 const cpu_arch = macho_file.base.options.target.cpu.arch;
323 const relocs = getRelocs(macho_file, object_id, ctx.source_offset);
340324
341325 for (relocs) |rel| {
342 const target = Atom.parseRelocTarget(zld, .{
326 const target = Atom.parseRelocTarget(macho_file, .{
343327 .object_id = object_id,
344328 .rel = rel,
345329 .code = rec.data,
......@@ -356,14 +340,14 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
356340 // Address of the __eh_frame in the source object file
357341 },
358342 .ARM64_RELOC_POINTER_TO_GOT => {
359 const target_addr = zld.getGotEntryAddress(target).?;
343 const target_addr = macho_file.getGotEntryAddress(target).?;
360344 const result = math.cast(i32, @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr))) orelse
361345 return error.Overflow;
362346 mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], result);
363347 },
364348 .ARM64_RELOC_UNSIGNED => {
365349 assert(rel.r_extern == 1);
366 const target_addr = try Atom.getRelocTargetAddress(zld, target, false);
350 const target_addr = try Atom.getRelocTargetAddress(macho_file, target, false);
367351 const result = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr));
368352 mem.writeIntLittle(i64, rec.data[rel_offset..][0..8], @as(i64, @intCast(result)));
369353 },
......@@ -374,7 +358,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
374358 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
375359 switch (rel_type) {
376360 .X86_64_RELOC_GOT => {
377 const target_addr = zld.getGotEntryAddress(target).?;
361 const target_addr = macho_file.getGotEntryAddress(target).?;
378362 const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]);
379363 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
380364 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
......@@ -388,20 +372,20 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
388372 }
389373 }
390374
391 pub fn getCiePointerSource(rec: Record, object_id: u32, zld: *Zld, offset: u32) u32 {
375 pub fn getCiePointerSource(rec: Record, object_id: u32, macho_file: *MachO, offset: u32) u32 {
392376 assert(rec.tag == .fde);
393 const cpu_arch = zld.options.target.cpu.arch;
377 const cpu_arch = macho_file.base.options.target.cpu.arch;
394378 const addend = mem.readIntLittle(u32, rec.data[0..4]);
395379 switch (cpu_arch) {
396380 .aarch64 => {
397 const relocs = getRelocs(zld, object_id, offset);
381 const relocs = getRelocs(macho_file, object_id, offset);
398382 const maybe_rel = for (relocs) |rel| {
399383 if (rel.r_address - @as(i32, @intCast(offset)) == 4 and
400384 @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_SUBTRACTOR)
401385 break rel;
402386 } else null;
403387 const rel = maybe_rel orelse return addend;
404 const object = &zld.objects.items[object_id];
388 const object = &macho_file.objects.items[object_id];
405389 const target_addr = object.in_symtab.?[rel.r_symbolnum].n_value;
406390 const sect = object.getSourceSection(object.eh_frame_sect_id.?);
407391 return @intCast(sect.addr + offset - target_addr + addend);
......@@ -583,8 +567,8 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
583567 };
584568}
585569
586pub fn getRelocs(zld: *Zld, object_id: u32, source_offset: u32) []const macho.relocation_info {
587 const object = &zld.objects.items[object_id];
570pub fn getRelocs(macho_file: *MachO, object_id: u32, source_offset: u32) []const macho.relocation_info {
571 const object = &macho_file.objects.items[object_id];
588572 assert(object.hasEhFrameRecords());
589573 const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse
590574 return &[0]macho.relocation_info{};
......@@ -650,3 +634,18 @@ pub const EH_PE = struct {
650634 pub const indirect = 0x80;
651635 pub const omit = 0xFF;
652636};
637
638const std = @import("std");
639const assert = std.debug.assert;
640const macho = std.macho;
641const math = std.math;
642const mem = std.mem;
643const leb = std.leb;
644const log = std.log.scoped(.eh_frame);
645
646const Allocator = mem.Allocator;
647const Atom = @import("Atom.zig");
648const MachO = @import("../MachO.zig");
649const Relocation = @import("Relocation.zig");
650const SymbolWithLoc = MachO.SymbolWithLoc;
651const UnwindInfo = @import("UnwindInfo.zig");
src/link/MachO/fat.zig+6-6
......@@ -1,9 +1,3 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const log = std.log.scoped(.archive);
4const macho = std.macho;
5const mem = std.mem;
6
71pub fn isFatLibrary(file: std.fs.File) bool {
82 const reader = file.reader();
93 const hdr = reader.readStructBig(macho.fat_header) catch return false;
......@@ -38,3 +32,9 @@ pub fn parseArchs(file: std.fs.File, buffer: *[2]Arch) ![]const Arch {
3832
3933 return buffer[0..count];
4034}
35
36const std = @import("std");
37const assert = std.debug.assert;
38const log = std.log.scoped(.archive);
39const macho = std.macho;
40const mem = std.mem;
src/link/MachO/hasher.zig+9-9
......@@ -1,12 +1,3 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const fs = std.fs;
4const mem = std.mem;
5
6const Allocator = mem.Allocator;
7const ThreadPool = std.Thread.Pool;
8const WaitGroup = std.Thread.WaitGroup;
9
101pub fn ParallelHasher(comptime Hasher: type) type {
112 const hash_size = Hasher.digest_length;
123
......@@ -69,3 +60,12 @@ pub fn ParallelHasher(comptime Hasher: type) type {
6960 const Self = @This();
7061 };
7162}
63
64const std = @import("std");
65const assert = std.debug.assert;
66const fs = std.fs;
67const mem = std.mem;
68
69const Allocator = mem.Allocator;
70const ThreadPool = std.Thread.Pool;
71const WaitGroup = std.Thread.WaitGroup;
src/link/MachO/load_commands.zig+10-10
......@@ -1,13 +1,3 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const link = @import("../../link.zig");
4const log = std.log.scoped(.link);
5const macho = std.macho;
6const mem = std.mem;
7
8const Allocator = mem.Allocator;
9const Dylib = @import("Dylib.zig");
10
111/// Default implicit entrypoint symbol name.
122pub const default_entry_point: []const u8 = "_main";
133
......@@ -374,3 +364,13 @@ test "parseSdkVersion" {
374364
375365 try expect(parseSdkVersion("11") == null);
376366}
367
368const std = @import("std");
369const assert = std.debug.assert;
370const link = @import("../../link.zig");
371const log = std.log.scoped(.link);
372const macho = std.macho;
373const mem = std.mem;
374
375const Allocator = mem.Allocator;
376const Dylib = @import("Dylib.zig");
src/link/MachO/stubs.zig+5-5
......@@ -1,8 +1,3 @@
1const std = @import("std");
2const aarch64 = @import("../../arch/aarch64/bits.zig");
3
4const Relocation = @import("Relocation.zig");
5
61pub inline fn stubHelperPreambleSize(cpu_arch: std.Target.Cpu.Arch) u8 {
72 return switch (cpu_arch) {
83 .x86_64 => 15,
......@@ -167,3 +162,8 @@ pub fn writeStubCode(args: struct {
167162 else => unreachable,
168163 }
169164}
165
166const std = @import("std");
167const aarch64 = @import("../../arch/aarch64/bits.zig");
168
169const Relocation = @import("Relocation.zig");
src/link/MachO/thunks.zig+84-85
......@@ -5,22 +5,6 @@
55//! The algorithm works pessimistically and assumes that any reference to an Atom in
66//! another output section is out of range.
77
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("Atom.zig");
19const MachO = @import("../MachO.zig");
20const Relocation = @import("Relocation.zig");
21const SymbolWithLoc = MachO.SymbolWithLoc;
22const Zld = @import("zld.zig").Zld;
23
248/// Branch instruction has 26 bits immediate but 4 byte aligned.
259const jump_bits = @bitSizeOf(i28);
2610
......@@ -74,18 +58,18 @@ pub const Thunk = struct {
7458 return @alignOf(u32);
7559 }
7660
77 pub fn getTrampoline(self: Thunk, zld: *Zld, tag: Tag, target: SymbolWithLoc) ?SymbolWithLoc {
61 pub fn getTrampoline(self: Thunk, macho_file: *MachO, tag: Tag, target: SymbolWithLoc) ?SymbolWithLoc {
7862 const atom_index = self.lookup.get(.{ .tag = tag, .target = target }) orelse return null;
79 return zld.getAtom(atom_index).getSymbolWithLoc();
63 return macho_file.getAtom(atom_index).getSymbolWithLoc();
8064 }
8165};
8266
83pub fn createThunks(zld: *Zld, sect_id: u8) !void {
84 const header = &zld.sections.items(.header)[sect_id];
67pub fn createThunks(macho_file: *MachO, sect_id: u8) !void {
68 const header = &macho_file.sections.items(.header)[sect_id];
8569 if (header.size == 0) return;
8670
87 const gpa = zld.gpa;
88 const first_atom_index = zld.sections.items(.first_atom_index)[sect_id].?;
71 const gpa = macho_file.base.allocator;
72 const first_atom_index = macho_file.sections.items(.first_atom_index)[sect_id].?;
8973
9074 header.size = 0;
9175 header.@"align" = 0;
......@@ -95,8 +79,8 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
9579 {
9680 var atom_index = first_atom_index;
9781 while (true) {
98 const atom = zld.getAtom(atom_index);
99 const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());
82 const atom = macho_file.getAtom(atom_index);
83 const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
10084 sym.n_value = 0;
10185 atom_count += 1;
10286
......@@ -115,24 +99,24 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
11599 var offset: u64 = 0;
116100
117101 while (true) {
118 const group_start_atom = zld.getAtom(group_start);
102 const group_start_atom = macho_file.getAtom(group_start);
119103 log.debug("GROUP START at {d}", .{group_start});
120104
121105 while (true) {
122 const atom = zld.getAtom(group_end);
106 const atom = macho_file.getAtom(group_end);
123107 offset = mem.alignForward(u64, offset, try math.powi(u32, 2, atom.alignment));
124108
125 const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());
109 const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
126110 sym.n_value = offset;
127111 offset += atom.size;
128112
129 zld.logAtom(group_end, log);
113 macho_file.logAtom(group_end, log);
130114
131115 header.@"align" = @max(header.@"align", atom.alignment);
132116
133117 allocated.putAssumeCapacityNoClobber(group_end, {});
134118
135 const group_start_sym = zld.getSymbol(group_start_atom.getSymbolWithLoc());
119 const group_start_sym = macho_file.getSymbol(group_start_atom.getSymbolWithLoc());
136120 if (offset - group_start_sym.n_value >= max_allowed_distance) break;
137121
138122 if (atom.next_index) |next_index| {
......@@ -142,15 +126,15 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
142126 log.debug("GROUP END at {d}", .{group_end});
143127
144128 // Insert thunk at group_end
145 const thunk_index = @as(u32, @intCast(zld.thunks.items.len));
146 try zld.thunks.append(gpa, .{ .start_index = undefined, .len = 0 });
129 const thunk_index = @as(u32, @intCast(macho_file.thunks.items.len));
130 try macho_file.thunks.append(gpa, .{ .start_index = undefined, .len = 0 });
147131
148132 // Scan relocs in the group and create trampolines for any unreachable callsite.
149133 var atom_index = group_start;
150134 while (true) {
151 const atom = zld.getAtom(atom_index);
135 const atom = macho_file.getAtom(atom_index);
152136 try scanRelocs(
153 zld,
137 macho_file,
154138 atom_index,
155139 allocated,
156140 thunk_index,
......@@ -165,19 +149,19 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
165149 }
166150
167151 offset = mem.alignForward(u64, offset, Thunk.getAlignment());
168 allocateThunk(zld, thunk_index, offset, header);
169 offset += zld.thunks.items[thunk_index].getSize();
152 allocateThunk(macho_file, thunk_index, offset, header);
153 offset += macho_file.thunks.items[thunk_index].getSize();
170154
171 const thunk = zld.thunks.items[thunk_index];
155 const thunk = macho_file.thunks.items[thunk_index];
172156 if (thunk.len == 0) {
173 const group_end_atom = zld.getAtom(group_end);
157 const group_end_atom = macho_file.getAtom(group_end);
174158 if (group_end_atom.next_index) |next_index| {
175159 group_start = next_index;
176160 group_end = next_index;
177161 } else break;
178162 } else {
179163 const thunk_end_atom_index = thunk.getEndAtomIndex();
180 const thunk_end_atom = zld.getAtom(thunk_end_atom_index);
164 const thunk_end_atom = macho_file.getAtom(thunk_end_atom_index);
181165 if (thunk_end_atom.next_index) |next_index| {
182166 group_start = next_index;
183167 group_end = next_index;
......@@ -189,12 +173,12 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
189173}
190174
191175fn allocateThunk(
192 zld: *Zld,
176 macho_file: *MachO,
193177 thunk_index: Thunk.Index,
194178 base_offset: u64,
195179 header: *macho.section_64,
196180) void {
197 const thunk = zld.thunks.items[thunk_index];
181 const thunk = macho_file.thunks.items[thunk_index];
198182 if (thunk.len == 0) return;
199183
200184 const first_atom_index = thunk.getStartAtomIndex();
......@@ -203,14 +187,14 @@ fn allocateThunk(
203187 var atom_index = first_atom_index;
204188 var offset = base_offset;
205189 while (true) {
206 const atom = zld.getAtom(atom_index);
190 const atom = macho_file.getAtom(atom_index);
207191 offset = mem.alignForward(u64, offset, Thunk.getAlignment());
208192
209 const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());
193 const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
210194 sym.n_value = offset;
211195 offset += atom.size;
212196
213 zld.logAtom(atom_index, log);
197 macho_file.logAtom(atom_index, log);
214198
215199 header.@"align" = @max(header.@"align", atom.alignment);
216200
......@@ -223,69 +207,69 @@ fn allocateThunk(
223207}
224208
225209fn scanRelocs(
226 zld: *Zld,
210 macho_file: *MachO,
227211 atom_index: Atom.Index,
228212 allocated: std.AutoHashMap(Atom.Index, void),
229213 thunk_index: Thunk.Index,
230214 group_end: Atom.Index,
231215) !void {
232 const atom = zld.getAtom(atom_index);
233 const object = zld.objects.items[atom.getFile().?];
216 const atom = macho_file.getAtom(atom_index);
217 const object = macho_file.objects.items[atom.getFile().?];
234218
235219 const base_offset = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
236220 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
237221 break :blk @as(i32, @intCast(source_sym.n_value - source_sect.addr));
238222 } else 0;
239223
240 const code = Atom.getAtomCode(zld, atom_index);
241 const relocs = Atom.getAtomRelocs(zld, atom_index);
242 const ctx = Atom.getRelocContext(zld, atom_index);
224 const code = Atom.getAtomCode(macho_file, atom_index);
225 const relocs = Atom.getAtomRelocs(macho_file, atom_index);
226 const ctx = Atom.getRelocContext(macho_file, atom_index);
243227
244228 for (relocs) |rel| {
245229 if (!relocNeedsThunk(rel)) continue;
246230
247 const target = Atom.parseRelocTarget(zld, .{
231 const target = Atom.parseRelocTarget(macho_file, .{
248232 .object_id = atom.getFile().?,
249233 .rel = rel,
250234 .code = code,
251235 .base_offset = ctx.base_offset,
252236 .base_addr = ctx.base_addr,
253237 });
254 if (isReachable(zld, atom_index, rel, base_offset, target, allocated)) continue;
238 if (isReachable(macho_file, atom_index, rel, base_offset, target, allocated)) continue;
255239
256240 log.debug("{x}: source = {s}@{x}, target = {s}@{x} unreachable", .{
257241 rel.r_address - base_offset,
258 zld.getSymbolName(atom.getSymbolWithLoc()),
259 zld.getSymbol(atom.getSymbolWithLoc()).n_value,
260 zld.getSymbolName(target),
261 zld.getSymbol(target).n_value,
242 macho_file.getSymbolName(atom.getSymbolWithLoc()),
243 macho_file.getSymbol(atom.getSymbolWithLoc()).n_value,
244 macho_file.getSymbolName(target),
245 macho_file.getSymbol(target).n_value,
262246 });
263247
264 const gpa = zld.gpa;
265 const target_sym = zld.getSymbol(target);
266 const thunk = &zld.thunks.items[thunk_index];
248 const gpa = macho_file.base.allocator;
249 const target_sym = macho_file.getSymbol(target);
250 const thunk = &macho_file.thunks.items[thunk_index];
267251
268252 const tag: Thunk.Tag = if (target_sym.undf()) .stub else .atom;
269253 const thunk_target: Thunk.Target = .{ .tag = tag, .target = target };
270254 const gop = try thunk.lookup.getOrPut(gpa, thunk_target);
271255 if (!gop.found_existing) {
272 gop.value_ptr.* = try pushThunkAtom(zld, thunk, group_end);
256 gop.value_ptr.* = try pushThunkAtom(macho_file, thunk, group_end);
273257 try thunk.targets.append(gpa, thunk_target);
274258 }
275259
276 try zld.thunk_table.put(gpa, atom_index, thunk_index);
260 try macho_file.thunk_table.put(gpa, atom_index, thunk_index);
277261 }
278262}
279263
280fn pushThunkAtom(zld: *Zld, thunk: *Thunk, group_end: Atom.Index) !Atom.Index {
281 const thunk_atom_index = try createThunkAtom(zld);
264fn pushThunkAtom(macho_file: *MachO, thunk: *Thunk, group_end: Atom.Index) !Atom.Index {
265 const thunk_atom_index = try createThunkAtom(macho_file);
282266
283 const thunk_atom = zld.getAtomPtr(thunk_atom_index);
267 const thunk_atom = macho_file.getAtomPtr(thunk_atom_index);
284268 const end_atom_index = if (thunk.len == 0) group_end else thunk.getEndAtomIndex();
285 const end_atom = zld.getAtomPtr(end_atom_index);
269 const end_atom = macho_file.getAtomPtr(end_atom_index);
286270
287271 if (end_atom.next_index) |first_after_index| {
288 const first_after_atom = zld.getAtomPtr(first_after_index);
272 const first_after_atom = macho_file.getAtomPtr(first_after_index);
289273 first_after_atom.prev_index = thunk_atom_index;
290274 thunk_atom.next_index = first_after_index;
291275 }
......@@ -308,58 +292,58 @@ inline fn relocNeedsThunk(rel: macho.relocation_info) bool {
308292}
309293
310294fn isReachable(
311 zld: *Zld,
295 macho_file: *MachO,
312296 atom_index: Atom.Index,
313297 rel: macho.relocation_info,
314298 base_offset: i32,
315299 target: SymbolWithLoc,
316300 allocated: std.AutoHashMap(Atom.Index, void),
317301) bool {
318 if (zld.stubs_table.lookup.contains(target)) return false;
302 if (macho_file.stub_table.lookup.contains(target)) return false;
319303
320 const source_atom = zld.getAtom(atom_index);
321 const source_sym = zld.getSymbol(source_atom.getSymbolWithLoc());
304 const source_atom = macho_file.getAtom(atom_index);
305 const source_sym = macho_file.getSymbol(source_atom.getSymbolWithLoc());
322306
323 const target_object = zld.objects.items[target.getFile().?];
307 const target_object = macho_file.objects.items[target.getFile().?];
324308 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
325 const target_atom = zld.getAtom(target_atom_index);
326 const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());
309 const target_atom = macho_file.getAtom(target_atom_index);
310 const target_sym = macho_file.getSymbol(target_atom.getSymbolWithLoc());
327311
328312 if (source_sym.n_sect != target_sym.n_sect) return false;
329313
330314 if (!allocated.contains(target_atom_index)) return false;
331315
332316 const source_addr = source_sym.n_value + @as(u32, @intCast(rel.r_address - base_offset));
333 const target_addr = if (Atom.relocRequiresGot(zld, rel))
334 zld.getGotEntryAddress(target).?
317 const target_addr = if (Atom.relocRequiresGot(macho_file, rel))
318 macho_file.getGotEntryAddress(target).?
335319 else
336 Atom.getRelocTargetAddress(zld, target, false) catch unreachable;
320 Atom.getRelocTargetAddress(macho_file, target, false) catch unreachable;
337321 _ = Relocation.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch
338322 return false;
339323
340324 return true;
341325}
342326
343fn createThunkAtom(zld: *Zld) !Atom.Index {
344 const sym_index = try zld.allocateSymbol();
345 const atom_index = try zld.createAtom(sym_index, .{ .size = @sizeOf(u32) * 3, .alignment = 2 });
346 const sym = zld.getSymbolPtr(.{ .sym_index = sym_index });
327fn createThunkAtom(macho_file: *MachO) !Atom.Index {
328 const sym_index = try macho_file.allocateSymbol();
329 const atom_index = try macho_file.createAtom(sym_index, .{ .size = @sizeOf(u32) * 3, .alignment = 2 });
330 const sym = macho_file.getSymbolPtr(.{ .sym_index = sym_index });
347331 sym.n_type = macho.N_SECT;
348 sym.n_sect = zld.text_section_index.? + 1;
332 sym.n_sect = macho_file.text_section_index.? + 1;
349333 return atom_index;
350334}
351335
352pub fn writeThunkCode(zld: *Zld, thunk: *const Thunk, writer: anytype) !void {
336pub fn writeThunkCode(macho_file: *MachO, thunk: *const Thunk, writer: anytype) !void {
353337 const slice = thunk.targets.slice();
354338 for (thunk.getStartAtomIndex()..thunk.getEndAtomIndex(), 0..) |atom_index, target_index| {
355 const atom = zld.getAtom(@intCast(atom_index));
356 const sym = zld.getSymbol(atom.getSymbolWithLoc());
339 const atom = macho_file.getAtom(@intCast(atom_index));
340 const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
357341 const source_addr = sym.n_value;
358342 const tag = slice.items(.tag)[target_index];
359343 const target = slice.items(.target)[target_index];
360344 const target_addr = switch (tag) {
361 .stub => zld.getStubsEntryAddress(target).?,
362 .atom => zld.getSymbol(target).n_value,
345 .stub => macho_file.getStubsEntryAddress(target).?,
346 .atom => macho_file.getSymbol(target).n_value,
363347 };
364348 const pages = Relocation.calcNumberOfPages(source_addr, target_addr);
365349 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
......@@ -368,3 +352,18 @@ pub fn writeThunkCode(zld: *Zld, thunk: *const Thunk, writer: anytype) !void {
368352 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
369353 }
370354}
355
356const std = @import("std");
357const assert = std.debug.assert;
358const log = std.log.scoped(.thunks);
359const macho = std.macho;
360const math = std.math;
361const mem = std.mem;
362
363const aarch64 = @import("../../arch/aarch64/bits.zig");
364
365const Allocator = mem.Allocator;
366const Atom = @import("Atom.zig");
367const MachO = @import("../MachO.zig");
368const Relocation = @import("Relocation.zig");
369const SymbolWithLoc = MachO.SymbolWithLoc;
src/link/MachO/uuid.zig+9-9
......@@ -1,12 +1,3 @@
1const std = @import("std");
2const fs = std.fs;
3const mem = std.mem;
4
5const Allocator = mem.Allocator;
6const Compilation = @import("../../Compilation.zig");
7const Md5 = std.crypto.hash.Md5;
8const Hasher = @import("hasher.zig").ParallelHasher;
9
101/// Calculates Md5 hash of each chunk in parallel and then hashes all Md5 hashes to produce
112/// the final digest.
123/// While this is NOT a correct MD5 hash of the contents, this methodology is used by LLVM/LLD
......@@ -43,3 +34,12 @@ inline fn conform(out: *[Md5.digest_length]u8) void {
4334 out[6] = (out[6] & 0x0F) | (3 << 4);
4435 out[8] = (out[8] & 0x3F) | 0x80;
4536}
37
38const std = @import("std");
39const fs = std.fs;
40const mem = std.mem;
41
42const Allocator = mem.Allocator;
43const Compilation = @import("../../Compilation.zig");
44const Md5 = std.crypto.hash.Md5;
45const Hasher = @import("hasher.zig").ParallelHasher;
src/link/MachO/zld.zig+726-2704
......@@ -1,2584 +1,8 @@
1const std = @import("std");
2const build_options = @import("build_options");
3const assert = std.debug.assert;
4const dwarf = std.dwarf;
5const fs = std.fs;
6const log = std.log.scoped(.link);
7const macho = std.macho;
8const math = std.math;
9const mem = std.mem;
10
11const aarch64 = @import("../../arch/aarch64/bits.zig");
12const calcUuid = @import("uuid.zig").calcUuid;
13const dead_strip = @import("dead_strip.zig");
14const eh_frame = @import("eh_frame.zig");
15const fat = @import("fat.zig");
16const link = @import("../../link.zig");
17const load_commands = @import("load_commands.zig");
18const stubs = @import("stubs.zig");
19const thunks = @import("thunks.zig");
20const trace = @import("../../tracy.zig").trace;
21
22const Allocator = mem.Allocator;
23const Archive = @import("Archive.zig");
24const Atom = @import("Atom.zig");
25const Cache = std.Build.Cache;
26const CodeSignature = @import("CodeSignature.zig");
27const Compilation = @import("../../Compilation.zig");
28const DwarfInfo = @import("DwarfInfo.zig");
29const Dylib = @import("Dylib.zig");
30const MachO = @import("../MachO.zig");
31const Md5 = std.crypto.hash.Md5;
32const LibStub = @import("../tapi.zig").LibStub;
33const Object = @import("Object.zig");
34const Section = MachO.Section;
35const StringTable = @import("../strtab.zig").StringTable;
36const SymbolWithLoc = MachO.SymbolWithLoc;
37const TableSection = @import("../table_section.zig").TableSection;
38const Trie = @import("Trie.zig");
39const UnwindInfo = @import("UnwindInfo.zig");
40
41const Bind = @import("dyld_info/bind.zig").Bind(*const Zld, SymbolWithLoc);
42const LazyBind = @import("dyld_info/bind.zig").LazyBind(*const Zld, SymbolWithLoc);
43const Rebase = @import("dyld_info/Rebase.zig");
44
45pub const Zld = struct {
46 gpa: Allocator,
47 file: fs.File,
48 options: *const link.Options,
49
50 dyld_info_cmd: macho.dyld_info_command = .{},
51 symtab_cmd: macho.symtab_command = .{},
52 dysymtab_cmd: macho.dysymtab_command = .{},
53 function_starts_cmd: macho.linkedit_data_command = .{ .cmd = .FUNCTION_STARTS },
54 data_in_code_cmd: macho.linkedit_data_command = .{ .cmd = .DATA_IN_CODE },
55 uuid_cmd: macho.uuid_command = .{
56 .uuid = [_]u8{0} ** 16,
57 },
58 codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE },
59
60 objects: std.ArrayListUnmanaged(Object) = .{},
61 archives: std.ArrayListUnmanaged(Archive) = .{},
62 dylibs: std.ArrayListUnmanaged(Dylib) = .{},
63 dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
64 referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},
65
66 segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
67 sections: std.MultiArrayList(Section) = .{},
68
69 pagezero_segment_cmd_index: ?u8 = null,
70 header_segment_cmd_index: ?u8 = null,
71 text_segment_cmd_index: ?u8 = null,
72 data_const_segment_cmd_index: ?u8 = null,
73 data_segment_cmd_index: ?u8 = null,
74 linkedit_segment_cmd_index: ?u8 = null,
75
76 text_section_index: ?u8 = null,
77 data_const_section_index: ?u8 = null,
78 data_section_index: ?u8 = null,
79 bss_section_index: ?u8 = null,
80 thread_vars_section_index: ?u8 = null,
81 thread_data_section_index: ?u8 = null,
82 thread_bss_section_index: ?u8 = null,
83 eh_frame_section_index: ?u8 = null,
84 unwind_info_section_index: ?u8 = null,
85 got_section_index: ?u8 = null,
86 tlv_ptr_section_index: ?u8 = null,
87 stubs_section_index: ?u8 = null,
88 stub_helper_section_index: ?u8 = null,
89 la_symbol_ptr_section_index: ?u8 = null,
90
91 locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
92 globals: std.ArrayListUnmanaged(SymbolWithLoc) = .{},
93 resolver: std.StringHashMapUnmanaged(u32) = .{},
94 unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{},
95
96 locals_free_list: std.ArrayListUnmanaged(u32) = .{},
97 globals_free_list: std.ArrayListUnmanaged(u32) = .{},
98
99 entry_index: ?u32 = null,
100 dyld_stub_binder_index: ?u32 = null,
101 dyld_private_atom_index: ?Atom.Index = null,
102
103 strtab: StringTable(.strtab) = .{},
104
105 tlv_ptr_table: TableSection(SymbolWithLoc) = .{},
106 got_table: TableSection(SymbolWithLoc) = .{},
107 stubs_table: TableSection(SymbolWithLoc) = .{},
108
109 thunk_table: std.AutoHashMapUnmanaged(Atom.Index, thunks.Thunk.Index) = .{},
110 thunks: std.ArrayListUnmanaged(thunks.Thunk) = .{},
111
112 atoms: std.ArrayListUnmanaged(Atom) = .{},
113
114 pub fn addAtomToSection(self: *Zld, atom_index: Atom.Index) void {
115 const atom = self.getAtomPtr(atom_index);
116 const sym = self.getSymbol(atom.getSymbolWithLoc());
117 var section = self.sections.get(sym.n_sect - 1);
118 if (section.header.size > 0) {
119 const last_atom = self.getAtomPtr(section.last_atom_index.?);
120 last_atom.next_index = atom_index;
121 atom.prev_index = section.last_atom_index;
122 } else {
123 section.first_atom_index = atom_index;
124 }
125 section.last_atom_index = atom_index;
126 section.header.size += atom.size;
127 self.sections.set(sym.n_sect - 1, section);
128 }
129
130 const CreateAtomOpts = struct {
131 size: u64 = 0,
132 alignment: u32 = 0,
133 };
134
135 pub fn createAtom(self: *Zld, sym_index: u32, opts: CreateAtomOpts) !Atom.Index {
136 const gpa = self.gpa;
137 const index = @as(Atom.Index, @intCast(self.atoms.items.len));
138 const atom = try self.atoms.addOne(gpa);
139 atom.* = .{};
140 atom.sym_index = sym_index;
141 atom.size = opts.size;
142 atom.alignment = opts.alignment;
143 log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, index });
144 return index;
145 }
146
147 fn createDyldPrivateAtom(self: *Zld) !void {
148 const sym_index = try self.allocateSymbol();
149 const atom_index = try self.createAtom(sym_index, .{ .size = @sizeOf(u64), .alignment = 3 });
150 const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
151 sym.n_type = macho.N_SECT;
152
153 if (self.data_section_index == null) {
154 self.data_section_index = try MachO.initSection(self.gpa, self, "__DATA", "__data", .{});
155 }
156 sym.n_sect = self.data_section_index.? + 1;
157 self.dyld_private_atom_index = atom_index;
158
159 self.addAtomToSection(atom_index);
160 }
161
162 fn createTentativeDefAtoms(self: *Zld) !void {
163 const gpa = self.gpa;
164
165 for (self.globals.items) |global| {
166 const sym = self.getSymbolPtr(global);
167 if (!sym.tentative()) continue;
168 if (sym.n_desc == MachO.N_DEAD) continue;
169
170 log.debug("creating tentative definition for ATOM(%{d}, '{s}') in object({?})", .{
171 global.sym_index, self.getSymbolName(global), global.file,
172 });
173
174 // Convert any tentative definition into a regular symbol and allocate
175 // text blocks for each tentative definition.
176 const size = sym.n_value;
177 const alignment = (sym.n_desc >> 8) & 0x0f;
178
179 if (self.bss_section_index == null) {
180 self.bss_section_index = try MachO.initSection(gpa, self, "__DATA", "__bss", .{
181 .flags = macho.S_ZEROFILL,
182 });
183 }
184
185 sym.* = .{
186 .n_strx = sym.n_strx,
187 .n_type = macho.N_SECT | macho.N_EXT,
188 .n_sect = self.bss_section_index.? + 1,
189 .n_desc = 0,
190 .n_value = 0,
191 };
192
193 const atom_index = try self.createAtom(global.sym_index, .{
194 .size = size,
195 .alignment = alignment,
196 });
197 const atom = self.getAtomPtr(atom_index);
198 atom.file = global.file;
199
200 self.addAtomToSection(atom_index);
201
202 assert(global.getFile() != null);
203 const object = &self.objects.items[global.getFile().?];
204 try object.atoms.append(gpa, atom_index);
205 object.atom_by_index_table[global.sym_index] = atom_index;
206 }
207 }
208
209 fn addUndefined(self: *Zld, name: []const u8) !u32 {
210 const gop = try self.getOrPutGlobalPtr(name);
211 const global_index = self.getGlobalIndex(name).?;
212
213 if (gop.found_existing) return global_index;
214
215 const sym_index = try self.allocateSymbol();
216 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
217 gop.value_ptr.* = sym_loc;
218
219 const sym = self.getSymbolPtr(sym_loc);
220 sym.n_strx = try self.strtab.insert(self.gpa, name);
221 sym.n_type = macho.N_UNDF;
222
223 try self.unresolved.putNoClobber(self.gpa, global_index, {});
224
225 return global_index;
226 }
227
228 fn resolveSymbols(self: *Zld) !void {
229 // We add the specified entrypoint as the first unresolved symbols so that
230 // we search for it in libraries should there be no object files specified
231 // on the linker line.
232 if (self.options.output_mode == .Exe) {
233 const entry_name = self.options.entry orelse load_commands.default_entry_point;
234 _ = try self.addUndefined(entry_name);
235 }
236
237 // Force resolution of any symbols requested by the user.
238 for (self.options.force_undefined_symbols.keys()) |sym_name| {
239 _ = try self.addUndefined(sym_name);
240 }
241
242 for (self.objects.items, 0..) |_, object_id| {
243 try self.resolveSymbolsInObject(@as(u32, @intCast(object_id)));
244 }
245
246 try self.resolveSymbolsInArchives();
247
248 // Finally, force resolution of dyld_stub_binder if there are imports
249 // requested.
250 if (self.unresolved.count() > 0) {
251 self.dyld_stub_binder_index = try self.addUndefined("dyld_stub_binder");
252 }
253
254 try self.resolveSymbolsInDylibs();
255
256 try self.createMhExecuteHeaderSymbol();
257 try self.createDsoHandleSymbol();
258 try self.resolveSymbolsAtLoading();
259 }
260
261 fn resolveGlobalSymbol(self: *Zld, current: SymbolWithLoc) !void {
262 const gpa = self.gpa;
263 const sym = self.getSymbol(current);
264 const sym_name = self.getSymbolName(current);
265
266 const gop = try self.getOrPutGlobalPtr(sym_name);
267 if (!gop.found_existing) {
268 gop.value_ptr.* = current;
269 if (sym.undf() and !sym.tentative()) {
270 try self.unresolved.putNoClobber(gpa, self.getGlobalIndex(sym_name).?, {});
271 }
272 return;
273 }
274 const global_index = self.getGlobalIndex(sym_name).?;
275 const global = gop.value_ptr.*;
276 const global_sym = self.getSymbol(global);
277
278 // Cases to consider: sym vs global_sym
279 // 1. strong(sym) and strong(global_sym) => error
280 // 2. strong(sym) and weak(global_sym) => sym
281 // 3. strong(sym) and tentative(global_sym) => sym
282 // 4. strong(sym) and undf(global_sym) => sym
283 // 5. weak(sym) and strong(global_sym) => global_sym
284 // 6. weak(sym) and tentative(global_sym) => sym
285 // 7. weak(sym) and undf(global_sym) => sym
286 // 8. tentative(sym) and strong(global_sym) => global_sym
287 // 9. tentative(sym) and weak(global_sym) => global_sym
288 // 10. tentative(sym) and tentative(global_sym) => pick larger
289 // 11. tentative(sym) and undf(global_sym) => sym
290 // 12. undf(sym) and * => global_sym
291 //
292 // Reduces to:
293 // 1. strong(sym) and strong(global_sym) => error
294 // 2. * and strong(global_sym) => global_sym
295 // 3. weak(sym) and weak(global_sym) => global_sym
296 // 4. tentative(sym) and tentative(global_sym) => pick larger
297 // 5. undf(sym) and * => global_sym
298 // 6. else => sym
299
300 const sym_is_strong = sym.sect() and !(sym.weakDef() or sym.pext());
301 const global_is_strong = global_sym.sect() and !(global_sym.weakDef() or global_sym.pext());
302 const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext());
303 const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext());
304
305 if (sym_is_strong and global_is_strong) {
306 log.err("symbol '{s}' defined multiple times", .{sym_name});
307 if (global.getFile()) |file| {
308 log.err(" first definition in '{s}'", .{self.objects.items[file].name});
309 }
310 if (current.getFile()) |file| {
311 log.err(" next definition in '{s}'", .{self.objects.items[file].name});
312 }
313 return error.MultipleSymbolDefinitions;
314 }
315
316 if (current.getFile()) |file| {
317 const object = &self.objects.items[file];
318 object.globals_lookup[current.sym_index] = global_index;
319 }
320
321 if (global_is_strong) return;
322 if (sym_is_weak and global_is_weak) return;
323 if (sym.tentative() and global_sym.tentative()) {
324 if (global_sym.n_value >= sym.n_value) return;
325 }
326 if (sym.undf() and !sym.tentative()) return;
327
328 if (global.getFile()) |file| {
329 const global_object = &self.objects.items[file];
330 global_object.globals_lookup[global.sym_index] = global_index;
331 }
332 _ = self.unresolved.swapRemove(global_index);
333
334 gop.value_ptr.* = current;
335 }
336
337 fn resolveSymbolsInObject(self: *Zld, object_id: u32) !void {
338 const object = &self.objects.items[object_id];
339 const in_symtab = object.in_symtab orelse return;
340
341 log.debug("resolving symbols in '{s}'", .{object.name});
342
343 var sym_index: u32 = 0;
344 while (sym_index < in_symtab.len) : (sym_index += 1) {
345 const sym = &object.symtab[sym_index];
346 const sym_name = object.getSymbolName(sym_index);
347
348 if (sym.stab()) {
349 log.err("unhandled symbol type: stab", .{});
350 log.err(" symbol '{s}'", .{sym_name});
351 log.err(" first definition in '{s}'", .{object.name});
352 return error.UnhandledSymbolType;
353 }
354
355 if (sym.indr()) {
356 log.err("unhandled symbol type: indirect", .{});
357 log.err(" symbol '{s}'", .{sym_name});
358 log.err(" first definition in '{s}'", .{object.name});
359 return error.UnhandledSymbolType;
360 }
361
362 if (sym.abs()) {
363 log.err("unhandled symbol type: absolute", .{});
364 log.err(" symbol '{s}'", .{sym_name});
365 log.err(" first definition in '{s}'", .{object.name});
366 return error.UnhandledSymbolType;
367 }
368
369 if (sym.sect() and !sym.ext()) {
370 log.debug("symbol '{s}' local to object {s}; skipping...", .{
371 sym_name,
372 object.name,
373 });
374 continue;
375 }
376
377 try self.resolveGlobalSymbol(.{ .sym_index = sym_index, .file = object_id + 1 });
378 }
379 }
380
381 fn resolveSymbolsInArchives(self: *Zld) !void {
382 if (self.archives.items.len == 0) return;
383
384 const gpa = self.gpa;
385
386 var next_sym: usize = 0;
387 loop: while (next_sym < self.unresolved.count()) {
388 const global = self.globals.items[self.unresolved.keys()[next_sym]];
389 const sym_name = self.getSymbolName(global);
390
391 for (self.archives.items) |archive| {
392 // Check if the entry exists in a static archive.
393 const offsets = archive.toc.get(sym_name) orelse {
394 // No hit.
395 continue;
396 };
397 assert(offsets.items.len > 0);
398
399 const object_id = @as(u16, @intCast(self.objects.items.len));
400 const object = try archive.parseObject(gpa, offsets.items[0]);
401 try self.objects.append(gpa, object);
402 try self.resolveSymbolsInObject(object_id);
403
404 continue :loop;
405 }
406
407 next_sym += 1;
408 }
409 }
410
411 fn resolveSymbolsInDylibs(self: *Zld) !void {
412 if (self.dylibs.items.len == 0) return;
413
414 var next_sym: usize = 0;
415 loop: while (next_sym < self.unresolved.count()) {
416 const global_index = self.unresolved.keys()[next_sym];
417 const global = self.globals.items[global_index];
418 const sym = self.getSymbolPtr(global);
419 const sym_name = self.getSymbolName(global);
420
421 for (self.dylibs.items, 0..) |dylib, id| {
422 if (!dylib.symbols.contains(sym_name)) continue;
423
424 const dylib_id = @as(u16, @intCast(id));
425 if (!self.referenced_dylibs.contains(dylib_id)) {
426 try self.referenced_dylibs.putNoClobber(self.gpa, dylib_id, {});
427 }
428
429 const ordinal = self.referenced_dylibs.getIndex(dylib_id) orelse unreachable;
430 sym.n_type |= macho.N_EXT;
431 sym.n_desc = @as(u16, @intCast(ordinal + 1)) * macho.N_SYMBOL_RESOLVER;
432
433 if (dylib.weak) {
434 sym.n_desc |= macho.N_WEAK_REF;
435 }
436
437 assert(self.unresolved.swapRemove(global_index));
438 continue :loop;
439 }
440
441 next_sym += 1;
442 }
443 }
444
445 fn resolveSymbolsAtLoading(self: *Zld) !void {
446 const is_lib = self.options.output_mode == .Lib;
447 const is_dyn_lib = self.options.link_mode == .Dynamic and is_lib;
448 const allow_undef = is_dyn_lib and (self.options.allow_shlib_undefined orelse false);
449
450 var next_sym: usize = 0;
451 while (next_sym < self.unresolved.count()) {
452 const global_index = self.unresolved.keys()[next_sym];
453 const global = self.globals.items[global_index];
454 const sym = self.getSymbolPtr(global);
455
456 if (sym.discarded()) {
457 sym.* = .{
458 .n_strx = 0,
459 .n_type = macho.N_UNDF,
460 .n_sect = 0,
461 .n_desc = 0,
462 .n_value = 0,
463 };
464 _ = self.unresolved.swapRemove(global_index);
465 continue;
466 } else if (allow_undef) {
467 const n_desc = @as(
468 u16,
469 @bitCast(macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP * @as(i16, @intCast(macho.N_SYMBOL_RESOLVER))),
470 );
471 sym.n_type = macho.N_EXT;
472 sym.n_desc = n_desc;
473 _ = self.unresolved.swapRemove(global_index);
474 continue;
475 }
476
477 next_sym += 1;
478 }
479 }
480
481 fn createMhExecuteHeaderSymbol(self: *Zld) !void {
482 if (self.options.output_mode != .Exe) return;
483
484 const gpa = self.gpa;
485 const sym_index = try self.allocateSymbol();
486 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
487 const sym = self.getSymbolPtr(sym_loc);
488 sym.* = .{
489 .n_strx = try self.strtab.insert(gpa, "__mh_execute_header"),
490 .n_type = macho.N_SECT | macho.N_EXT,
491 .n_sect = 0,
492 .n_desc = macho.REFERENCED_DYNAMICALLY,
493 .n_value = 0,
494 };
495
496 const gop = try self.getOrPutGlobalPtr("__mh_execute_header");
497 if (gop.found_existing) {
498 const global = gop.value_ptr.*;
499 if (global.getFile()) |file| {
500 const global_object = &self.objects.items[file];
501 global_object.globals_lookup[global.sym_index] = self.getGlobalIndex("__mh_execute_header").?;
502 }
503 }
504 gop.value_ptr.* = sym_loc;
505 }
506
507 fn createDsoHandleSymbol(self: *Zld) !void {
508 const global = self.getGlobalPtr("___dso_handle") orelse return;
509 if (!self.getSymbol(global.*).undf()) return;
510
511 const sym_index = try self.allocateSymbol();
512 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
513 const sym = self.getSymbolPtr(sym_loc);
514 sym.* = .{
515 .n_strx = try self.strtab.insert(self.gpa, "___dso_handle"),
516 .n_type = macho.N_SECT | macho.N_EXT,
517 .n_sect = 0,
518 .n_desc = macho.N_WEAK_DEF,
519 .n_value = 0,
520 };
521 const global_index = self.getGlobalIndex("___dso_handle").?;
522 if (global.getFile()) |file| {
523 const global_object = &self.objects.items[file];
524 global_object.globals_lookup[global.sym_index] = global_index;
525 }
526 global.* = sym_loc;
527 _ = self.unresolved.swapRemove(global_index);
528 }
529
530 pub fn deinit(self: *Zld) void {
531 const gpa = self.gpa;
532
533 self.tlv_ptr_table.deinit(gpa);
534 self.got_table.deinit(gpa);
535 self.stubs_table.deinit(gpa);
536 self.thunk_table.deinit(gpa);
537
538 for (self.thunks.items) |*thunk| {
539 thunk.deinit(gpa);
540 }
541 self.thunks.deinit(gpa);
542
543 self.strtab.deinit(gpa);
544 self.locals.deinit(gpa);
545 self.globals.deinit(gpa);
546 self.resolver.deinit(gpa);
547 self.unresolved.deinit(gpa);
548 self.locals_free_list.deinit(gpa);
549 self.globals_free_list.deinit(gpa);
550
551 for (self.objects.items) |*object| {
552 object.deinit(gpa);
553 }
554 self.objects.deinit(gpa);
555 for (self.archives.items) |*archive| {
556 archive.deinit(gpa);
557 }
558 self.archives.deinit(gpa);
559 for (self.dylibs.items) |*dylib| {
560 dylib.deinit(gpa);
561 }
562 self.dylibs.deinit(gpa);
563 self.dylibs_map.deinit(gpa);
564 self.referenced_dylibs.deinit(gpa);
565
566 self.segments.deinit(gpa);
567 self.sections.deinit(gpa);
568 self.atoms.deinit(gpa);
569 }
570
571 fn createSegments(self: *Zld) !void {
572 const pagezero_vmsize = self.options.pagezero_size orelse MachO.default_pagezero_vmsize;
573 const page_size = MachO.getPageSize(self.options.target.cpu.arch);
574 const aligned_pagezero_vmsize = mem.alignBackward(u64, pagezero_vmsize, page_size);
575 if (self.options.output_mode != .Lib and aligned_pagezero_vmsize > 0) {
576 if (aligned_pagezero_vmsize != pagezero_vmsize) {
577 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});
578 log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize});
579 }
580 self.pagezero_segment_cmd_index = @intCast(self.segments.items.len);
581 try self.segments.append(self.gpa, .{
582 .cmdsize = @sizeOf(macho.segment_command_64),
583 .segname = makeStaticString("__PAGEZERO"),
584 .vmsize = aligned_pagezero_vmsize,
585 });
586 }
587
588 // __TEXT segment is non-optional
589 {
590 const protection = MachO.getSegmentMemoryProtection("__TEXT");
591 self.text_segment_cmd_index = @intCast(self.segments.items.len);
592 self.header_segment_cmd_index = self.text_segment_cmd_index.?;
593 try self.segments.append(self.gpa, .{
594 .cmdsize = @sizeOf(macho.segment_command_64),
595 .segname = makeStaticString("__TEXT"),
596 .maxprot = protection,
597 .initprot = protection,
598 });
599 }
600
601 for (self.sections.items(.header), 0..) |header, sect_id| {
602 if (header.size == 0) continue; // empty section
603
604 const segname = header.segName();
605 const segment_id = self.getSegmentByName(segname) orelse blk: {
606 log.debug("creating segment '{s}'", .{segname});
607 const segment_id = @as(u8, @intCast(self.segments.items.len));
608 const protection = MachO.getSegmentMemoryProtection(segname);
609 try self.segments.append(self.gpa, .{
610 .cmdsize = @sizeOf(macho.segment_command_64),
611 .segname = makeStaticString(segname),
612 .maxprot = protection,
613 .initprot = protection,
614 });
615 break :blk segment_id;
616 };
617 const segment = &self.segments.items[segment_id];
618 segment.cmdsize += @sizeOf(macho.section_64);
619 segment.nsects += 1;
620 self.sections.items(.segment_index)[sect_id] = segment_id;
621 }
622
623 if (self.getSegmentByName("__DATA_CONST")) |index| {
624 self.data_const_segment_cmd_index = index;
625 }
626
627 if (self.getSegmentByName("__DATA")) |index| {
628 self.data_segment_cmd_index = index;
629 }
630
631 // __LINKEDIT always comes last
632 {
633 const protection = MachO.getSegmentMemoryProtection("__LINKEDIT");
634 self.linkedit_segment_cmd_index = @intCast(self.segments.items.len);
635 try self.segments.append(self.gpa, .{
636 .cmdsize = @sizeOf(macho.segment_command_64),
637 .segname = makeStaticString("__LINKEDIT"),
638 .maxprot = protection,
639 .initprot = protection,
640 });
641 }
642 }
643
644 pub fn allocateSymbol(self: *Zld) !u32 {
645 try self.locals.ensureUnusedCapacity(self.gpa, 1);
646 log.debug(" (allocating symbol index {d})", .{self.locals.items.len});
647 const index = @as(u32, @intCast(self.locals.items.len));
648 _ = self.locals.addOneAssumeCapacity();
649 self.locals.items[index] = .{
650 .n_strx = 0,
651 .n_type = 0,
652 .n_sect = 0,
653 .n_desc = 0,
654 .n_value = 0,
655 };
656 return index;
657 }
658
659 fn allocateGlobal(self: *Zld) !u32 {
660 try self.globals.ensureUnusedCapacity(self.gpa, 1);
661
662 const index = blk: {
663 if (self.globals_free_list.popOrNull()) |index| {
664 log.debug(" (reusing global index {d})", .{index});
665 break :blk index;
666 } else {
667 log.debug(" (allocating symbol index {d})", .{self.globals.items.len});
668 const index = @as(u32, @intCast(self.globals.items.len));
669 _ = self.globals.addOneAssumeCapacity();
670 break :blk index;
671 }
672 };
673
674 self.globals.items[index] = .{ .sym_index = 0 };
675
676 return index;
677 }
678
679 pub fn addGotEntry(self: *Zld, target: SymbolWithLoc) !void {
680 if (self.got_table.lookup.contains(target)) return;
681 _ = try self.got_table.allocateEntry(self.gpa, target);
682 if (self.got_section_index == null) {
683 self.got_section_index = try MachO.initSection(self.gpa, self, "__DATA_CONST", "__got", .{
684 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
685 });
686 }
687 }
688
689 pub fn addTlvPtrEntry(self: *Zld, target: SymbolWithLoc) !void {
690 if (self.tlv_ptr_table.lookup.contains(target)) return;
691 _ = try self.tlv_ptr_table.allocateEntry(self.gpa, target);
692 if (self.tlv_ptr_section_index == null) {
693 self.tlv_ptr_section_index = try MachO.initSection(self.gpa, self, "__DATA", "__thread_ptrs", .{
694 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
695 });
696 }
697 }
698
699 pub fn addStubEntry(self: *Zld, target: SymbolWithLoc) !void {
700 if (self.stubs_table.lookup.contains(target)) return;
701 _ = try self.stubs_table.allocateEntry(self.gpa, target);
702 if (self.stubs_section_index == null) {
703 self.stubs_section_index = try MachO.initSection(self.gpa, self, "__TEXT", "__stubs", .{
704 .flags = macho.S_SYMBOL_STUBS |
705 macho.S_ATTR_PURE_INSTRUCTIONS |
706 macho.S_ATTR_SOME_INSTRUCTIONS,
707 .reserved2 = stubs.stubSize(self.options.target.cpu.arch),
708 });
709 self.stub_helper_section_index = try MachO.initSection(self.gpa, self, "__TEXT", "__stub_helper", .{
710 .flags = macho.S_REGULAR |
711 macho.S_ATTR_PURE_INSTRUCTIONS |
712 macho.S_ATTR_SOME_INSTRUCTIONS,
713 });
714 self.la_symbol_ptr_section_index = try MachO.initSection(self.gpa, self, "__DATA", "__la_symbol_ptr", .{
715 .flags = macho.S_LAZY_SYMBOL_POINTERS,
716 });
717 }
718 }
719
720 fn writeAtoms(self: *Zld) !void {
721 const gpa = self.gpa;
722 const slice = self.sections.slice();
723
724 for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| {
725 const header = slice.items(.header)[sect_id];
726 if (header.isZerofill()) continue;
727
728 var atom_index = first_atom_index orelse continue;
729
730 var buffer = try gpa.alloc(u8, math.cast(usize, header.size) orelse return error.Overflow);
731 defer gpa.free(buffer);
732 @memset(buffer, 0); // TODO with NOPs
733
734 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
735
736 while (true) {
737 const atom = self.getAtom(atom_index);
738 if (atom.getFile()) |file| {
739 const this_sym = self.getSymbol(atom.getSymbolWithLoc());
740 const padding_size: usize = if (atom.next_index) |next_index| blk: {
741 const next_sym = self.getSymbol(self.getAtom(next_index).getSymbolWithLoc());
742 const size = next_sym.n_value - (this_sym.n_value + atom.size);
743 break :blk math.cast(usize, size) orelse return error.Overflow;
744 } else 0;
745
746 log.debug(" (adding ATOM(%{d}, '{s}') from object({d}) to buffer)", .{
747 atom.sym_index,
748 self.getSymbolName(atom.getSymbolWithLoc()),
749 file,
750 });
751 if (padding_size > 0) {
752 log.debug(" (with padding {x})", .{padding_size});
753 }
754
755 const offset = this_sym.n_value - header.addr;
756 log.debug(" (at offset 0x{x})", .{offset});
757
758 const code = Atom.getAtomCode(self, atom_index);
759 const relocs = Atom.getAtomRelocs(self, atom_index);
760 const size = math.cast(usize, atom.size) orelse return error.Overflow;
761 @memcpy(buffer[offset .. offset + size], code);
762 try Atom.resolveRelocs(
763 self,
764 atom_index,
765 buffer[offset..][0..size],
766 relocs,
767 );
768 }
769
770 if (atom.next_index) |next_index| {
771 atom_index = next_index;
772 } else break;
773 }
774
775 log.debug(" (writing at file offset 0x{x})", .{header.offset});
776 try self.file.pwriteAll(buffer, header.offset);
777 }
778 }
779
780 fn writeDyldPrivateAtom(self: *Zld) !void {
781 const atom_index = self.dyld_private_atom_index orelse return;
782 const atom = self.getAtom(atom_index);
783 const sym = self.getSymbol(atom.getSymbolWithLoc());
784 const sect_id = self.data_section_index.?;
785 const header = self.sections.items(.header)[sect_id];
786 const offset = sym.n_value - header.addr + header.offset;
787 log.debug("writing __dyld_private at offset 0x{x}", .{offset});
788 const buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
789 try self.file.pwriteAll(&buffer, offset);
790 }
791
792 fn writeThunks(self: *Zld) !void {
793 assert(self.requiresThunks());
794 const gpa = self.gpa;
795
796 const sect_id = self.text_section_index orelse return;
797 const header = self.sections.items(.header)[sect_id];
798
799 for (self.thunks.items, 0..) |*thunk, i| {
800 if (thunk.getSize() == 0) continue;
801 var buffer = try std.ArrayList(u8).initCapacity(gpa, thunk.getSize());
802 defer buffer.deinit();
803 try thunks.writeThunkCode(self, thunk, buffer.writer());
804 const thunk_atom = self.getAtom(thunk.getStartAtomIndex());
805 const thunk_sym = self.getSymbol(thunk_atom.getSymbolWithLoc());
806 const offset = thunk_sym.n_value - header.addr + header.offset;
807 log.debug("writing thunk({d}) at offset 0x{x}", .{ i, offset });
808 try self.file.pwriteAll(buffer.items, offset);
809 }
810 }
811
812 fn writePointerEntries(self: *Zld, sect_id: u8, table: anytype) !void {
813 const header = self.sections.items(.header)[sect_id];
814 var buffer = try std.ArrayList(u8).initCapacity(self.gpa, header.size);
815 defer buffer.deinit();
816 for (table.entries.items) |entry| {
817 const sym = self.getSymbol(entry);
818 buffer.writer().writeIntLittle(u64, sym.n_value) catch unreachable;
819 }
820 log.debug("writing __DATA_CONST,__got contents at file offset 0x{x}", .{header.offset});
821 try self.file.pwriteAll(buffer.items, header.offset);
822 }
823
824 fn writeStubs(self: *Zld) !void {
825 const gpa = self.gpa;
826 const cpu_arch = self.options.target.cpu.arch;
827 const stubs_header = self.sections.items(.header)[self.stubs_section_index.?];
828 const la_symbol_ptr_header = self.sections.items(.header)[self.la_symbol_ptr_section_index.?];
829
830 var buffer = try std.ArrayList(u8).initCapacity(gpa, stubs_header.size);
831 defer buffer.deinit();
832
833 for (0..self.stubs_table.count()) |index| {
834 try stubs.writeStubCode(.{
835 .cpu_arch = cpu_arch,
836 .source_addr = stubs_header.addr + stubs.stubSize(cpu_arch) * index,
837 .target_addr = la_symbol_ptr_header.addr + index * @sizeOf(u64),
838 }, buffer.writer());
839 }
840
841 log.debug("writing __TEXT,__stubs contents at file offset 0x{x}", .{stubs_header.offset});
842 try self.file.pwriteAll(buffer.items, stubs_header.offset);
843 }
844
845 fn writeStubHelpers(self: *Zld) !void {
846 const gpa = self.gpa;
847 const cpu_arch = self.options.target.cpu.arch;
848 const stub_helper_header = self.sections.items(.header)[self.stub_helper_section_index.?];
849
850 var buffer = try std.ArrayList(u8).initCapacity(gpa, stub_helper_header.size);
851 defer buffer.deinit();
852
853 {
854 const dyld_private_addr = blk: {
855 const atom = self.getAtom(self.dyld_private_atom_index.?);
856 const sym = self.getSymbol(atom.getSymbolWithLoc());
857 break :blk sym.n_value;
858 };
859 const dyld_stub_binder_got_addr = blk: {
860 const sym_loc = self.globals.items[self.dyld_stub_binder_index.?];
861 break :blk self.getGotEntryAddress(sym_loc).?;
862 };
863 try stubs.writeStubHelperPreambleCode(.{
864 .cpu_arch = cpu_arch,
865 .source_addr = stub_helper_header.addr,
866 .dyld_private_addr = dyld_private_addr,
867 .dyld_stub_binder_got_addr = dyld_stub_binder_got_addr,
868 }, buffer.writer());
869 }
870
871 for (0..self.stubs_table.count()) |index| {
872 const source_addr = stub_helper_header.addr + stubs.stubHelperPreambleSize(cpu_arch) +
873 stubs.stubHelperSize(cpu_arch) * index;
874 try stubs.writeStubHelperCode(.{
875 .cpu_arch = cpu_arch,
876 .source_addr = source_addr,
877 .target_addr = stub_helper_header.addr,
878 }, buffer.writer());
879 }
880
881 log.debug("writing __TEXT,__stub_helper contents at file offset 0x{x}", .{
882 stub_helper_header.offset,
883 });
884 try self.file.pwriteAll(buffer.items, stub_helper_header.offset);
885 }
886
887 fn writeLaSymbolPtrs(self: *Zld) !void {
888 const gpa = self.gpa;
889 const cpu_arch = self.options.target.cpu.arch;
890 const la_symbol_ptr_header = self.sections.items(.header)[self.la_symbol_ptr_section_index.?];
891 const stub_helper_header = self.sections.items(.header)[self.stub_helper_section_index.?];
892
893 var buffer = try std.ArrayList(u8).initCapacity(gpa, la_symbol_ptr_header.size);
894 defer buffer.deinit();
895
896 for (0..self.stubs_table.count()) |index| {
897 const target_addr = stub_helper_header.addr + stubs.stubHelperPreambleSize(cpu_arch) +
898 stubs.stubHelperSize(cpu_arch) * index;
899 buffer.writer().writeIntLittle(u64, target_addr) catch unreachable;
900 }
901
902 log.debug("writing __DATA,__la_symbol_ptr contents at file offset 0x{x}", .{
903 la_symbol_ptr_header.offset,
904 });
905 try self.file.pwriteAll(buffer.items, la_symbol_ptr_header.offset);
906 }
907
908 fn pruneAndSortSections(self: *Zld) !void {
909 const Entry = struct {
910 index: u8,
911
912 pub fn lessThan(zld: *Zld, lhs: @This(), rhs: @This()) bool {
913 const lhs_header = zld.sections.items(.header)[lhs.index];
914 const rhs_header = zld.sections.items(.header)[rhs.index];
915 return MachO.getSectionPrecedence(lhs_header) < MachO.getSectionPrecedence(rhs_header);
916 }
917 };
918
919 const gpa = self.gpa;
920
921 var entries = try std.ArrayList(Entry).initCapacity(gpa, self.sections.slice().len);
922 defer entries.deinit();
923
924 for (0..self.sections.slice().len) |index| {
925 const section = self.sections.get(index);
926 if (section.header.size == 0) {
927 log.debug("pruning section {s},{s} {?d}", .{
928 section.header.segName(),
929 section.header.sectName(),
930 section.first_atom_index,
931 });
932 for (&[_]*?u8{
933 &self.text_section_index,
934 &self.data_const_section_index,
935 &self.data_section_index,
936 &self.bss_section_index,
937 &self.thread_vars_section_index,
938 &self.thread_data_section_index,
939 &self.thread_bss_section_index,
940 &self.eh_frame_section_index,
941 &self.unwind_info_section_index,
942 &self.got_section_index,
943 &self.tlv_ptr_section_index,
944 &self.stubs_section_index,
945 &self.stub_helper_section_index,
946 &self.la_symbol_ptr_section_index,
947 }) |maybe_index| {
948 if (maybe_index.* != null and maybe_index.*.? == index) {
949 maybe_index.* = null;
950 }
951 }
952 continue;
953 }
954 entries.appendAssumeCapacity(.{ .index = @intCast(index) });
955 }
956
957 mem.sort(Entry, entries.items, self, Entry.lessThan);
958
959 var slice = self.sections.toOwnedSlice();
960 defer slice.deinit(gpa);
961
962 const backlinks = try gpa.alloc(u8, slice.len);
963 defer gpa.free(backlinks);
964 for (entries.items, 0..) |entry, i| {
965 backlinks[entry.index] = @as(u8, @intCast(i));
966 }
967
968 try self.sections.ensureTotalCapacity(gpa, entries.items.len);
969 for (entries.items) |entry| {
970 self.sections.appendAssumeCapacity(slice.get(entry.index));
971 }
972
973 for (&[_]*?u8{
974 &self.text_section_index,
975 &self.data_const_section_index,
976 &self.data_section_index,
977 &self.bss_section_index,
978 &self.thread_vars_section_index,
979 &self.thread_data_section_index,
980 &self.thread_bss_section_index,
981 &self.eh_frame_section_index,
982 &self.unwind_info_section_index,
983 &self.got_section_index,
984 &self.tlv_ptr_section_index,
985 &self.stubs_section_index,
986 &self.stub_helper_section_index,
987 &self.la_symbol_ptr_section_index,
988 }) |maybe_index| {
989 if (maybe_index.*) |*index| {
990 index.* = backlinks[index.*];
991 }
992 }
993 }
994
995 fn calcSectionSizes(self: *Zld) !void {
996 const slice = self.sections.slice();
997 for (slice.items(.header), 0..) |*header, sect_id| {
998 if (header.size == 0) continue;
999 if (self.text_section_index) |txt| {
1000 if (txt == sect_id and self.requiresThunks()) continue;
1001 }
1002
1003 var atom_index = slice.items(.first_atom_index)[sect_id] orelse continue;
1004
1005 header.size = 0;
1006 header.@"align" = 0;
1007
1008 while (true) {
1009 const atom = self.getAtom(atom_index);
1010 const atom_alignment = try math.powi(u32, 2, atom.alignment);
1011 const atom_offset = mem.alignForward(u64, header.size, atom_alignment);
1012 const padding = atom_offset - header.size;
1013
1014 const sym = self.getSymbolPtr(atom.getSymbolWithLoc());
1015 sym.n_value = atom_offset;
1016
1017 header.size += padding + atom.size;
1018 header.@"align" = @max(header.@"align", atom.alignment);
1019
1020 if (atom.next_index) |next_index| {
1021 atom_index = next_index;
1022 } else break;
1023 }
1024 }
1025
1026 if (self.text_section_index != null and self.requiresThunks()) {
1027 // Create jump/branch range extenders if needed.
1028 try thunks.createThunks(self, self.text_section_index.?);
1029 }
1030
1031 // Update offsets of all symbols contained within each Atom.
1032 // We need to do this since our unwind info synthesiser relies on
1033 // traversing the symbols when synthesising unwind info and DWARF CFI records.
1034 for (slice.items(.first_atom_index)) |first_atom_index| {
1035 var atom_index = first_atom_index orelse continue;
1036
1037 while (true) {
1038 const atom = self.getAtom(atom_index);
1039 const sym = self.getSymbol(atom.getSymbolWithLoc());
1040
1041 if (atom.getFile() != null) {
1042 // Update each symbol contained within the atom
1043 var it = Atom.getInnerSymbolsIterator(self, atom_index);
1044 while (it.next()) |sym_loc| {
1045 const inner_sym = self.getSymbolPtr(sym_loc);
1046 inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(
1047 self,
1048 atom_index,
1049 sym_loc.sym_index,
1050 );
1051 }
1052
1053 // If there is a section alias, update it now too
1054 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
1055 const alias = self.getSymbolPtr(sym_loc);
1056 alias.n_value = sym.n_value;
1057 }
1058 }
1059
1060 if (atom.next_index) |next_index| {
1061 atom_index = next_index;
1062 } else break;
1063 }
1064 }
1065
1066 if (self.got_section_index) |sect_id| {
1067 const header = &self.sections.items(.header)[sect_id];
1068 header.size = self.got_table.count() * @sizeOf(u64);
1069 header.@"align" = 3;
1070 }
1071
1072 if (self.tlv_ptr_section_index) |sect_id| {
1073 const header = &self.sections.items(.header)[sect_id];
1074 header.size = self.tlv_ptr_table.count() * @sizeOf(u64);
1075 header.@"align" = 3;
1076 }
1077
1078 const cpu_arch = self.options.target.cpu.arch;
1079
1080 if (self.stubs_section_index) |sect_id| {
1081 const header = &self.sections.items(.header)[sect_id];
1082 header.size = self.stubs_table.count() * stubs.stubSize(cpu_arch);
1083 header.@"align" = stubs.stubAlignment(cpu_arch);
1084 }
1085
1086 if (self.stub_helper_section_index) |sect_id| {
1087 const header = &self.sections.items(.header)[sect_id];
1088 header.size = self.stubs_table.count() * stubs.stubHelperSize(cpu_arch) +
1089 stubs.stubHelperPreambleSize(cpu_arch);
1090 header.@"align" = stubs.stubAlignment(cpu_arch);
1091 }
1092
1093 if (self.la_symbol_ptr_section_index) |sect_id| {
1094 const header = &self.sections.items(.header)[sect_id];
1095 header.size = self.stubs_table.count() * @sizeOf(u64);
1096 header.@"align" = 3;
1097 }
1098 }
1099
1100 fn allocateSegments(self: *Zld) !void {
1101 for (self.segments.items, 0..) |*segment, segment_index| {
1102 const is_text_segment = mem.eql(u8, segment.segName(), "__TEXT");
1103 const base_size = if (is_text_segment) try load_commands.calcMinHeaderPad(self.gpa, self.options, .{
1104 .segments = self.segments.items,
1105 .dylibs = self.dylibs.items,
1106 .referenced_dylibs = self.referenced_dylibs.keys(),
1107 }) else 0;
1108 try self.allocateSegment(@as(u8, @intCast(segment_index)), base_size);
1109 }
1110 }
1111
1112 fn getSegmentAllocBase(self: Zld, segment_index: u8) struct { vmaddr: u64, fileoff: u64 } {
1113 if (segment_index > 0) {
1114 const prev_segment = self.segments.items[segment_index - 1];
1115 return .{
1116 .vmaddr = prev_segment.vmaddr + prev_segment.vmsize,
1117 .fileoff = prev_segment.fileoff + prev_segment.filesize,
1118 };
1119 }
1120 return .{ .vmaddr = 0, .fileoff = 0 };
1121 }
1122
1123 fn allocateSegment(self: *Zld, segment_index: u8, init_size: u64) !void {
1124 const segment = &self.segments.items[segment_index];
1125
1126 if (mem.eql(u8, segment.segName(), "__PAGEZERO")) return; // allocated upon creation
1127
1128 const base = self.getSegmentAllocBase(segment_index);
1129 segment.vmaddr = base.vmaddr;
1130 segment.fileoff = base.fileoff;
1131 segment.filesize = init_size;
1132 segment.vmsize = init_size;
1133
1134 // Allocate the sections according to their alignment at the beginning of the segment.
1135 const indexes = self.getSectionIndexes(segment_index);
1136 var start = init_size;
1137
1138 const slice = self.sections.slice();
1139 for (slice.items(.header)[indexes.start..indexes.end], 0..) |*header, sect_id| {
1140 const alignment = try math.powi(u32, 2, header.@"align");
1141 const start_aligned = mem.alignForward(u64, start, alignment);
1142 const n_sect = @as(u8, @intCast(indexes.start + sect_id + 1));
1143
1144 header.offset = if (header.isZerofill())
1145 0
1146 else
1147 @as(u32, @intCast(segment.fileoff + start_aligned));
1148 header.addr = segment.vmaddr + start_aligned;
1149
1150 if (slice.items(.first_atom_index)[indexes.start + sect_id]) |first_atom_index| {
1151 var atom_index = first_atom_index;
1152
1153 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
1154 n_sect,
1155 header.segName(),
1156 header.sectName(),
1157 });
1158
1159 while (true) {
1160 const atom = self.getAtom(atom_index);
1161 const sym = self.getSymbolPtr(atom.getSymbolWithLoc());
1162 sym.n_value += header.addr;
1163 sym.n_sect = n_sect;
1164
1165 log.debug(" ATOM(%{d}, '{s}') @{x}", .{
1166 atom.sym_index,
1167 self.getSymbolName(atom.getSymbolWithLoc()),
1168 sym.n_value,
1169 });
1170
1171 if (atom.getFile() != null) {
1172 // Update each symbol contained within the atom
1173 var it = Atom.getInnerSymbolsIterator(self, atom_index);
1174 while (it.next()) |sym_loc| {
1175 const inner_sym = self.getSymbolPtr(sym_loc);
1176 inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(
1177 self,
1178 atom_index,
1179 sym_loc.sym_index,
1180 );
1181 inner_sym.n_sect = n_sect;
1182 }
1183
1184 // If there is a section alias, update it now too
1185 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
1186 const alias = self.getSymbolPtr(sym_loc);
1187 alias.n_value = sym.n_value;
1188 alias.n_sect = n_sect;
1189 }
1190 }
1191
1192 if (atom.next_index) |next_index| {
1193 atom_index = next_index;
1194 } else break;
1195 }
1196 }
1197
1198 start = start_aligned + header.size;
1199
1200 if (!header.isZerofill()) {
1201 segment.filesize = start;
1202 }
1203 segment.vmsize = start;
1204 }
1205
1206 const page_size = MachO.getPageSize(self.options.target.cpu.arch);
1207 segment.filesize = mem.alignForward(u64, segment.filesize, page_size);
1208 segment.vmsize = mem.alignForward(u64, segment.vmsize, page_size);
1209 }
1210
1211 fn writeLinkeditSegmentData(self: *Zld) !void {
1212 const page_size = MachO.getPageSize(self.options.target.cpu.arch);
1213 const seg = self.getLinkeditSegmentPtr();
1214 seg.filesize = 0;
1215 seg.vmsize = 0;
1216
1217 for (self.segments.items, 0..) |segment, id| {
1218 if (self.linkedit_segment_cmd_index.? == @as(u8, @intCast(id))) continue;
1219 if (seg.vmaddr < segment.vmaddr + segment.vmsize) {
1220 seg.vmaddr = mem.alignForward(u64, segment.vmaddr + segment.vmsize, page_size);
1221 }
1222 if (seg.fileoff < segment.fileoff + segment.filesize) {
1223 seg.fileoff = mem.alignForward(u64, segment.fileoff + segment.filesize, page_size);
1224 }
1225 }
1226 try self.writeDyldInfoData();
1227 try self.writeFunctionStarts();
1228 try self.writeDataInCode();
1229 try self.writeSymtabs();
1230
1231 seg.vmsize = mem.alignForward(u64, seg.filesize, page_size);
1232 }
1233
1234 fn collectRebaseData(self: *Zld, rebase: *Rebase) !void {
1235 log.debug("collecting rebase data", .{});
1236
1237 // First, unpack GOT entries
1238 if (self.got_section_index) |sect_id| {
1239 try MachO.collectRebaseDataFromTableSection(self.gpa, self, sect_id, rebase, self.got_table);
1240 }
1241
1242 // Next, unpack __la_symbol_ptr entries
1243 if (self.la_symbol_ptr_section_index) |sect_id| {
1244 try MachO.collectRebaseDataFromTableSection(self.gpa, self, sect_id, rebase, self.stubs_table);
1245 }
1246
1247 // Finally, unpack the rest.
1248 const cpu_arch = self.options.target.cpu.arch;
1249 for (self.objects.items) |*object| {
1250 for (object.atoms.items) |atom_index| {
1251 const atom = self.getAtom(atom_index);
1252 const sym = self.getSymbol(atom.getSymbolWithLoc());
1253 if (sym.n_desc == MachO.N_DEAD) continue;
1254
1255 const sect_id = sym.n_sect - 1;
1256 const section = self.sections.items(.header)[sect_id];
1257 const segment_id = self.sections.items(.segment_index)[sect_id];
1258 const segment = self.segments.items[segment_id];
1259 if (segment.maxprot & macho.PROT.WRITE == 0) continue;
1260 switch (section.type()) {
1261 macho.S_LITERAL_POINTERS,
1262 macho.S_REGULAR,
1263 macho.S_MOD_INIT_FUNC_POINTERS,
1264 macho.S_MOD_TERM_FUNC_POINTERS,
1265 => {},
1266 else => continue,
1267 }
1268
1269 log.debug(" ATOM({d}, %{d}, '{s}')", .{
1270 atom_index,
1271 atom.sym_index,
1272 self.getSymbolName(atom.getSymbolWithLoc()),
1273 });
1274
1275 const code = Atom.getAtomCode(self, atom_index);
1276 const relocs = Atom.getAtomRelocs(self, atom_index);
1277 const ctx = Atom.getRelocContext(self, atom_index);
1278
1279 for (relocs) |rel| {
1280 switch (cpu_arch) {
1281 .aarch64 => {
1282 const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
1283 if (rel_type != .ARM64_RELOC_UNSIGNED) continue;
1284 if (rel.r_length != 3) continue;
1285 },
1286 .x86_64 => {
1287 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
1288 if (rel_type != .X86_64_RELOC_UNSIGNED) continue;
1289 if (rel.r_length != 3) continue;
1290 },
1291 else => unreachable,
1292 }
1293 const target = Atom.parseRelocTarget(self, .{
1294 .object_id = atom.getFile().?,
1295 .rel = rel,
1296 .code = code,
1297 .base_offset = ctx.base_offset,
1298 .base_addr = ctx.base_addr,
1299 });
1300 const target_sym = self.getSymbol(target);
1301 if (target_sym.undf()) continue;
1302
1303 const base_offset = @as(i32, @intCast(sym.n_value - segment.vmaddr));
1304 const rel_offset = rel.r_address - ctx.base_offset;
1305 const offset = @as(u64, @intCast(base_offset + rel_offset));
1306 log.debug(" | rebase at {x}", .{offset});
1307
1308 try rebase.entries.append(self.gpa, .{
1309 .offset = offset,
1310 .segment_id = segment_id,
1311 });
1312 }
1313 }
1314 }
1315
1316 try rebase.finalize(self.gpa);
1317 }
1318
1319 fn collectBindData(
1320 self: *Zld,
1321 bind: *Bind,
1322 ) !void {
1323 log.debug("collecting bind data", .{});
1324
1325 // First, unpack GOT section
1326 if (self.got_section_index) |sect_id| {
1327 try MachO.collectBindDataFromTableSection(self.gpa, self, sect_id, bind, self.got_table);
1328 }
1329
1330 // Next, unpack TLV pointers section
1331 if (self.tlv_ptr_section_index) |sect_id| {
1332 try MachO.collectBindDataFromTableSection(self.gpa, self, sect_id, bind, self.tlv_ptr_table);
1333 }
1334
1335 // Finally, unpack the rest.
1336 const cpu_arch = self.options.target.cpu.arch;
1337 for (self.objects.items) |*object| {
1338 for (object.atoms.items) |atom_index| {
1339 const atom = self.getAtom(atom_index);
1340 const sym = self.getSymbol(atom.getSymbolWithLoc());
1341 if (sym.n_desc == MachO.N_DEAD) continue;
1342
1343 const sect_id = sym.n_sect - 1;
1344 const section = self.sections.items(.header)[sect_id];
1345 const segment_id = self.sections.items(.segment_index)[sect_id];
1346 const segment = self.segments.items[segment_id];
1347 if (segment.maxprot & macho.PROT.WRITE == 0) continue;
1348 switch (section.type()) {
1349 macho.S_LITERAL_POINTERS,
1350 macho.S_REGULAR,
1351 macho.S_MOD_INIT_FUNC_POINTERS,
1352 macho.S_MOD_TERM_FUNC_POINTERS,
1353 => {},
1354 else => continue,
1355 }
1356
1357 log.debug(" ATOM({d}, %{d}, '{s}')", .{
1358 atom_index,
1359 atom.sym_index,
1360 self.getSymbolName(atom.getSymbolWithLoc()),
1361 });
1362
1363 const code = Atom.getAtomCode(self, atom_index);
1364 const relocs = Atom.getAtomRelocs(self, atom_index);
1365 const ctx = Atom.getRelocContext(self, atom_index);
1366
1367 for (relocs) |rel| {
1368 switch (cpu_arch) {
1369 .aarch64 => {
1370 const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
1371 if (rel_type != .ARM64_RELOC_UNSIGNED) continue;
1372 if (rel.r_length != 3) continue;
1373 },
1374 .x86_64 => {
1375 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
1376 if (rel_type != .X86_64_RELOC_UNSIGNED) continue;
1377 if (rel.r_length != 3) continue;
1378 },
1379 else => unreachable,
1380 }
1381
1382 const global = Atom.parseRelocTarget(self, .{
1383 .object_id = atom.getFile().?,
1384 .rel = rel,
1385 .code = code,
1386 .base_offset = ctx.base_offset,
1387 .base_addr = ctx.base_addr,
1388 });
1389 const bind_sym_name = self.getSymbolName(global);
1390 const bind_sym = self.getSymbol(global);
1391 if (!bind_sym.undf()) continue;
1392
1393 const base_offset = sym.n_value - segment.vmaddr;
1394 const rel_offset = @as(u32, @intCast(rel.r_address - ctx.base_offset));
1395 const offset = @as(u64, @intCast(base_offset + rel_offset));
1396 const addend = mem.readIntLittle(i64, code[rel_offset..][0..8]);
1397
1398 const dylib_ordinal = @divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER);
1399 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
1400 base_offset,
1401 bind_sym_name,
1402 dylib_ordinal,
1403 });
1404 log.debug(" | with addend {x}", .{addend});
1405 if (bind_sym.weakRef()) {
1406 log.debug(" | marking as weak ref ", .{});
1407 }
1408 try bind.entries.append(self.gpa, .{
1409 .target = global,
1410 .offset = offset,
1411 .segment_id = segment_id,
1412 .addend = addend,
1413 });
1414 }
1415 }
1416 }
1417
1418 try bind.finalize(self.gpa, self);
1419 }
1420
1421 fn collectLazyBindData(self: *Zld, lazy_bind: *LazyBind) !void {
1422 const sect_id = self.la_symbol_ptr_section_index orelse return;
1423 try MachO.collectBindDataFromTableSection(self.gpa, self, sect_id, lazy_bind, self.stubs_table);
1424 try lazy_bind.finalize(self.gpa, self);
1425 }
1426
1427 fn collectExportData(self: *Zld, trie: *Trie) !void {
1428 const gpa = self.gpa;
1429
1430 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
1431 log.debug("collecting export data", .{});
1432
1433 const exec_segment = self.segments.items[self.header_segment_cmd_index.?];
1434 const base_address = exec_segment.vmaddr;
1435
1436 for (self.globals.items) |global| {
1437 const sym = self.getSymbol(global);
1438 if (sym.undf()) continue;
1439 if (sym.n_desc == MachO.N_DEAD) continue;
1440
1441 const sym_name = self.getSymbolName(global);
1442 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
1443 try trie.put(gpa, .{
1444 .name = sym_name,
1445 .vmaddr_offset = sym.n_value - base_address,
1446 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
1447 });
1448 }
1449
1450 try trie.finalize(gpa);
1451 }
1452
1453 fn writeDyldInfoData(self: *Zld) !void {
1454 const gpa = self.gpa;
1455
1456 var rebase = Rebase{};
1457 defer rebase.deinit(gpa);
1458 try self.collectRebaseData(&rebase);
1459
1460 var bind = Bind{};
1461 defer bind.deinit(gpa);
1462 try self.collectBindData(&bind);
1463
1464 var lazy_bind = LazyBind{};
1465 defer lazy_bind.deinit(gpa);
1466 try self.collectLazyBindData(&lazy_bind);
1467
1468 var trie = Trie{};
1469 defer trie.deinit(gpa);
1470 try trie.init(gpa);
1471 try self.collectExportData(&trie);
1472
1473 const link_seg = self.getLinkeditSegmentPtr();
1474 assert(mem.isAlignedGeneric(u64, link_seg.fileoff, @alignOf(u64)));
1475 const rebase_off = link_seg.fileoff;
1476 const rebase_size = rebase.size();
1477 const rebase_size_aligned = mem.alignForward(u64, rebase_size, @alignOf(u64));
1478 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size_aligned });
1479
1480 const bind_off = rebase_off + rebase_size_aligned;
1481 const bind_size = bind.size();
1482 const bind_size_aligned = mem.alignForward(u64, bind_size, @alignOf(u64));
1483 log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size_aligned });
1484
1485 const lazy_bind_off = bind_off + bind_size_aligned;
1486 const lazy_bind_size = lazy_bind.size();
1487 const lazy_bind_size_aligned = mem.alignForward(u64, lazy_bind_size, @alignOf(u64));
1488 log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{
1489 lazy_bind_off,
1490 lazy_bind_off + lazy_bind_size_aligned,
1491 });
1492
1493 const export_off = lazy_bind_off + lazy_bind_size_aligned;
1494 const export_size = trie.size;
1495 const export_size_aligned = mem.alignForward(u64, export_size, @alignOf(u64));
1496 log.debug("writing export trie from 0x{x} to 0x{x}", .{ export_off, export_off + export_size_aligned });
1497
1498 const needed_size = math.cast(usize, export_off + export_size_aligned - rebase_off) orelse
1499 return error.Overflow;
1500 link_seg.filesize = needed_size;
1501 assert(mem.isAlignedGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64)));
1502
1503 var buffer = try gpa.alloc(u8, needed_size);
1504 defer gpa.free(buffer);
1505 @memset(buffer, 0);
1506
1507 var stream = std.io.fixedBufferStream(buffer);
1508 const writer = stream.writer();
1509
1510 try rebase.write(writer);
1511 try stream.seekTo(bind_off - rebase_off);
1512
1513 try bind.write(writer);
1514 try stream.seekTo(lazy_bind_off - rebase_off);
1515
1516 try lazy_bind.write(writer);
1517 try stream.seekTo(export_off - rebase_off);
1518
1519 _ = try trie.write(writer);
1520
1521 log.debug("writing dyld info from 0x{x} to 0x{x}", .{
1522 rebase_off,
1523 rebase_off + needed_size,
1524 });
1525
1526 try self.file.pwriteAll(buffer, rebase_off);
1527 try MachO.populateLazyBindOffsetsInStubHelper(
1528 self,
1529 self.options.target.cpu.arch,
1530 self.file,
1531 lazy_bind,
1532 );
1533
1534 self.dyld_info_cmd.rebase_off = @as(u32, @intCast(rebase_off));
1535 self.dyld_info_cmd.rebase_size = @as(u32, @intCast(rebase_size_aligned));
1536 self.dyld_info_cmd.bind_off = @as(u32, @intCast(bind_off));
1537 self.dyld_info_cmd.bind_size = @as(u32, @intCast(bind_size_aligned));
1538 self.dyld_info_cmd.lazy_bind_off = @as(u32, @intCast(lazy_bind_off));
1539 self.dyld_info_cmd.lazy_bind_size = @as(u32, @intCast(lazy_bind_size_aligned));
1540 self.dyld_info_cmd.export_off = @as(u32, @intCast(export_off));
1541 self.dyld_info_cmd.export_size = @as(u32, @intCast(export_size_aligned));
1542 }
1543
1544 const asc_u64 = std.sort.asc(u64);
1545
1546 fn addSymbolToFunctionStarts(self: *Zld, sym_loc: SymbolWithLoc, addresses: *std.ArrayList(u64)) !void {
1547 const sym = self.getSymbol(sym_loc);
1548 if (sym.n_strx == 0) return;
1549 if (sym.n_desc == MachO.N_DEAD) return;
1550 if (self.symbolIsTemp(sym_loc)) return;
1551 try addresses.append(sym.n_value);
1552 }
1553
1554 fn writeFunctionStarts(self: *Zld) !void {
1555 const gpa = self.gpa;
1556 const seg = self.segments.items[self.header_segment_cmd_index.?];
1557
1558 // We need to sort by address first
1559 var addresses = std.ArrayList(u64).init(gpa);
1560 defer addresses.deinit();
1561
1562 for (self.objects.items) |object| {
1563 for (object.exec_atoms.items) |atom_index| {
1564 const atom = self.getAtom(atom_index);
1565 const sym_loc = atom.getSymbolWithLoc();
1566 try self.addSymbolToFunctionStarts(sym_loc, &addresses);
1567
1568 var it = Atom.getInnerSymbolsIterator(self, atom_index);
1569 while (it.next()) |inner_sym_loc| {
1570 try self.addSymbolToFunctionStarts(inner_sym_loc, &addresses);
1571 }
1572 }
1573 }
1574
1575 mem.sort(u64, addresses.items, {}, asc_u64);
1576
1577 var offsets = std.ArrayList(u32).init(gpa);
1578 defer offsets.deinit();
1579 try offsets.ensureTotalCapacityPrecise(addresses.items.len);
1580
1581 var last_off: u32 = 0;
1582 for (addresses.items) |addr| {
1583 const offset = @as(u32, @intCast(addr - seg.vmaddr));
1584 const diff = offset - last_off;
1585
1586 if (diff == 0) continue;
1587
1588 offsets.appendAssumeCapacity(diff);
1589 last_off = offset;
1590 }
1591
1592 var buffer = std.ArrayList(u8).init(gpa);
1593 defer buffer.deinit();
1594
1595 const max_size = @as(usize, @intCast(offsets.items.len * @sizeOf(u64)));
1596 try buffer.ensureTotalCapacity(max_size);
1597
1598 for (offsets.items) |offset| {
1599 try std.leb.writeULEB128(buffer.writer(), offset);
1600 }
1601
1602 const link_seg = self.getLinkeditSegmentPtr();
1603 const offset = link_seg.fileoff + link_seg.filesize;
1604 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
1605 const needed_size = buffer.items.len;
1606 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
1607 const padding = math.cast(usize, needed_size_aligned - needed_size) orelse return error.Overflow;
1608 if (padding > 0) {
1609 try buffer.ensureUnusedCapacity(padding);
1610 buffer.appendNTimesAssumeCapacity(0, padding);
1611 }
1612 link_seg.filesize = offset + needed_size_aligned - link_seg.fileoff;
1613
1614 log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
1615
1616 try self.file.pwriteAll(buffer.items, offset);
1617
1618 self.function_starts_cmd.dataoff = @as(u32, @intCast(offset));
1619 self.function_starts_cmd.datasize = @as(u32, @intCast(needed_size_aligned));
1620 }
1621
1622 fn filterDataInCode(
1623 dices: []const macho.data_in_code_entry,
1624 start_addr: u64,
1625 end_addr: u64,
1626 ) []const macho.data_in_code_entry {
1627 const Predicate = struct {
1628 addr: u64,
1629
1630 pub fn predicate(self: @This(), dice: macho.data_in_code_entry) bool {
1631 return dice.offset >= self.addr;
1632 }
1633 };
1634
1635 const start = MachO.lsearch(macho.data_in_code_entry, dices, Predicate{ .addr = start_addr });
1636 const end = MachO.lsearch(macho.data_in_code_entry, dices[start..], Predicate{ .addr = end_addr }) + start;
1637
1638 return dices[start..end];
1639 }
1640
1641 fn writeDataInCode(self: *Zld) !void {
1642 var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.gpa);
1643 defer out_dice.deinit();
1644
1645 const text_sect_id = self.text_section_index orelse return;
1646 const text_sect_header = self.sections.items(.header)[text_sect_id];
1647
1648 for (self.objects.items) |object| {
1649 if (!object.hasDataInCode()) continue;
1650 const dice = object.data_in_code.items;
1651 try out_dice.ensureUnusedCapacity(dice.len);
1652
1653 for (object.exec_atoms.items) |atom_index| {
1654 const atom = self.getAtom(atom_index);
1655 const sym = self.getSymbol(atom.getSymbolWithLoc());
1656 if (sym.n_desc == MachO.N_DEAD) continue;
1657
1658 const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
1659 source_sym.n_value
1660 else blk: {
1661 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
1662 const source_sect_id = @as(u8, @intCast(atom.sym_index - nbase));
1663 break :blk object.getSourceSection(source_sect_id).addr;
1664 };
1665 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);
1666 const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse
1667 return error.Overflow;
1668
1669 for (filtered_dice) |single| {
1670 const offset = math.cast(u32, single.offset - source_addr + base) orelse
1671 return error.Overflow;
1672 out_dice.appendAssumeCapacity(.{
1673 .offset = offset,
1674 .length = single.length,
1675 .kind = single.kind,
1676 });
1677 }
1678 }
1679 }
1680
1681 const seg = self.getLinkeditSegmentPtr();
1682 const offset = seg.fileoff + seg.filesize;
1683 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
1684 const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
1685 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
1686 seg.filesize = offset + needed_size_aligned - seg.fileoff;
1687
1688 const buffer = try self.gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow);
1689 defer self.gpa.free(buffer);
1690 {
1691 const src = mem.sliceAsBytes(out_dice.items);
1692 @memcpy(buffer[0..src.len], src);
1693 @memset(buffer[src.len..], 0);
1694 }
1695
1696 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
1697
1698 try self.file.pwriteAll(buffer, offset);
1699
1700 self.data_in_code_cmd.dataoff = @as(u32, @intCast(offset));
1701 self.data_in_code_cmd.datasize = @as(u32, @intCast(needed_size_aligned));
1702 }
1703
1704 fn writeSymtabs(self: *Zld) !void {
1705 var ctx = try self.writeSymtab();
1706 defer ctx.imports_table.deinit();
1707 try self.writeDysymtab(ctx);
1708 try self.writeStrtab();
1709 }
1710
1711 fn addLocalToSymtab(self: *Zld, sym_loc: SymbolWithLoc, locals: *std.ArrayList(macho.nlist_64)) !void {
1712 const sym = self.getSymbol(sym_loc);
1713 if (sym.n_strx == 0) return; // no name, skip
1714 if (sym.n_desc == MachO.N_DEAD) return; // garbage-collected, skip
1715 if (sym.ext()) return; // an export lands in its own symtab section, skip
1716 if (self.symbolIsTemp(sym_loc)) return; // local temp symbol, skip
1717
1718 var out_sym = sym;
1719 out_sym.n_strx = try self.strtab.insert(self.gpa, self.getSymbolName(sym_loc));
1720 try locals.append(out_sym);
1721 }
1722
1723 fn writeSymtab(self: *Zld) !SymtabCtx {
1724 const gpa = self.gpa;
1725
1726 var locals = std.ArrayList(macho.nlist_64).init(gpa);
1727 defer locals.deinit();
1728
1729 for (self.objects.items) |object| {
1730 for (object.atoms.items) |atom_index| {
1731 const atom = self.getAtom(atom_index);
1732 const sym_loc = atom.getSymbolWithLoc();
1733 try self.addLocalToSymtab(sym_loc, &locals);
1734
1735 var it = Atom.getInnerSymbolsIterator(self, atom_index);
1736 while (it.next()) |inner_sym_loc| {
1737 try self.addLocalToSymtab(inner_sym_loc, &locals);
1738 }
1739 }
1740 }
1741
1742 var exports = std.ArrayList(macho.nlist_64).init(gpa);
1743 defer exports.deinit();
1744
1745 for (self.globals.items) |global| {
1746 const sym = self.getSymbol(global);
1747 if (sym.undf()) continue; // import, skip
1748 if (sym.n_desc == MachO.N_DEAD) continue;
1749
1750 var out_sym = sym;
1751 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
1752 try exports.append(out_sym);
1753 }
1754
1755 var imports = std.ArrayList(macho.nlist_64).init(gpa);
1756 defer imports.deinit();
1757
1758 var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);
1759
1760 for (self.globals.items) |global| {
1761 const sym = self.getSymbol(global);
1762 if (!sym.undf()) continue; // not an import, skip
1763 if (sym.n_desc == MachO.N_DEAD) continue;
1764
1765 const new_index = @as(u32, @intCast(imports.items.len));
1766 var out_sym = sym;
1767 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
1768 try imports.append(out_sym);
1769 try imports_table.putNoClobber(global, new_index);
1770 }
1771
1772 // We generate stabs last in order to ensure that the strtab always has debug info
1773 // strings trailing
1774 if (!self.options.strip) {
1775 for (self.objects.items) |object| {
1776 try self.generateSymbolStabs(object, &locals);
1777 }
1778 }
1779
1780 const nlocals = @as(u32, @intCast(locals.items.len));
1781 const nexports = @as(u32, @intCast(exports.items.len));
1782 const nimports = @as(u32, @intCast(imports.items.len));
1783 const nsyms = nlocals + nexports + nimports;
1784
1785 const seg = self.getLinkeditSegmentPtr();
1786 const offset = seg.fileoff + seg.filesize;
1787 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
1788 const needed_size = nsyms * @sizeOf(macho.nlist_64);
1789 seg.filesize = offset + needed_size - seg.fileoff;
1790 assert(mem.isAlignedGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64)));
1791
1792 var buffer = std.ArrayList(u8).init(gpa);
1793 defer buffer.deinit();
1794 try buffer.ensureTotalCapacityPrecise(needed_size);
1795 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items));
1796 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items));
1797 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(imports.items));
1798
1799 log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
1800 try self.file.pwriteAll(buffer.items, offset);
1801
1802 self.symtab_cmd.symoff = @as(u32, @intCast(offset));
1803 self.symtab_cmd.nsyms = nsyms;
1804
1805 return SymtabCtx{
1806 .nlocalsym = nlocals,
1807 .nextdefsym = nexports,
1808 .nundefsym = nimports,
1809 .imports_table = imports_table,
1810 };
1811 }
1812
1813 fn writeStrtab(self: *Zld) !void {
1814 const seg = self.getLinkeditSegmentPtr();
1815 const offset = seg.fileoff + seg.filesize;
1816 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
1817 const needed_size = self.strtab.buffer.items.len;
1818 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
1819 seg.filesize = offset + needed_size_aligned - seg.fileoff;
1820
1821 log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
1822
1823 const buffer = try self.gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow);
1824 defer self.gpa.free(buffer);
1825 @memcpy(buffer[0..self.strtab.buffer.items.len], self.strtab.buffer.items);
1826 @memset(buffer[self.strtab.buffer.items.len..], 0);
1827
1828 try self.file.pwriteAll(buffer, offset);
1829
1830 self.symtab_cmd.stroff = @as(u32, @intCast(offset));
1831 self.symtab_cmd.strsize = @as(u32, @intCast(needed_size_aligned));
1832 }
1833
1834 const SymtabCtx = struct {
1835 nlocalsym: u32,
1836 nextdefsym: u32,
1837 nundefsym: u32,
1838 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
1839 };
1840
1841 fn writeDysymtab(self: *Zld, ctx: SymtabCtx) !void {
1842 const gpa = self.gpa;
1843 const nstubs = @as(u32, @intCast(self.stubs_table.lookup.count()));
1844 const ngot_entries = @as(u32, @intCast(self.got_table.lookup.count()));
1845 const nindirectsyms = nstubs * 2 + ngot_entries;
1846 const iextdefsym = ctx.nlocalsym;
1847 const iundefsym = iextdefsym + ctx.nextdefsym;
1848
1849 const seg = self.getLinkeditSegmentPtr();
1850 const offset = seg.fileoff + seg.filesize;
1851 assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
1852 const needed_size = nindirectsyms * @sizeOf(u32);
1853 const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
1854 seg.filesize = offset + needed_size_aligned - seg.fileoff;
1855
1856 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
1857
1858 var buf = std.ArrayList(u8).init(gpa);
1859 defer buf.deinit();
1860 try buf.ensureTotalCapacityPrecise(math.cast(usize, needed_size_aligned) orelse return error.Overflow);
1861 const writer = buf.writer();
1862
1863 if (self.stubs_section_index) |sect_id| {
1864 const header = &self.sections.items(.header)[sect_id];
1865 header.reserved1 = 0;
1866 for (self.stubs_table.entries.items) |entry| {
1867 if (!self.stubs_table.lookup.contains(entry)) continue;
1868 const target_sym = self.getSymbol(entry);
1869 assert(target_sym.undf());
1870 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?);
1871 }
1872 }
1873
1874 if (self.got_section_index) |sect_id| {
1875 const header = &self.sections.items(.header)[sect_id];
1876 header.reserved1 = nstubs;
1877 for (self.got_table.entries.items) |entry| {
1878 if (!self.got_table.lookup.contains(entry)) continue;
1879 const target_sym = self.getSymbol(entry);
1880 if (target_sym.undf()) {
1881 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?);
1882 } else {
1883 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
1884 }
1885 }
1886 }
1887
1888 if (self.la_symbol_ptr_section_index) |sect_id| {
1889 const header = &self.sections.items(.header)[sect_id];
1890 header.reserved1 = nstubs + ngot_entries;
1891 for (self.stubs_table.entries.items) |entry| {
1892 if (!self.stubs_table.lookup.contains(entry)) continue;
1893 const target_sym = self.getSymbol(entry);
1894 assert(target_sym.undf());
1895 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?);
1896 }
1897 }
1898
1899 const padding = math.cast(usize, needed_size_aligned - needed_size) orelse return error.Overflow;
1900 if (padding > 0) {
1901 buf.appendNTimesAssumeCapacity(0, padding);
1902 }
1903
1904 assert(buf.items.len == needed_size_aligned);
1905 try self.file.pwriteAll(buf.items, offset);
1906
1907 self.dysymtab_cmd.nlocalsym = ctx.nlocalsym;
1908 self.dysymtab_cmd.iextdefsym = iextdefsym;
1909 self.dysymtab_cmd.nextdefsym = ctx.nextdefsym;
1910 self.dysymtab_cmd.iundefsym = iundefsym;
1911 self.dysymtab_cmd.nundefsym = ctx.nundefsym;
1912 self.dysymtab_cmd.indirectsymoff = @as(u32, @intCast(offset));
1913 self.dysymtab_cmd.nindirectsyms = nindirectsyms;
1914 }
1915
1916 fn writeUuid(self: *Zld, comp: *const Compilation, uuid_cmd_offset: u32, has_codesig: bool) !void {
1917 const file_size = if (!has_codesig) blk: {
1918 const seg = self.getLinkeditSegmentPtr();
1919 break :blk seg.fileoff + seg.filesize;
1920 } else self.codesig_cmd.dataoff;
1921 try calcUuid(comp, self.file, file_size, &self.uuid_cmd.uuid);
1922 const offset = uuid_cmd_offset + @sizeOf(macho.load_command);
1923 try self.file.pwriteAll(&self.uuid_cmd.uuid, offset);
1924 }
1925
1926 fn writeCodeSignaturePadding(self: *Zld, code_sig: *CodeSignature) !void {
1927 const seg = self.getLinkeditSegmentPtr();
1928 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
1929 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
1930 const offset = mem.alignForward(u64, seg.fileoff + seg.filesize, 16);
1931 const needed_size = code_sig.estimateSize(offset);
1932 seg.filesize = offset + needed_size - seg.fileoff;
1933 seg.vmsize = mem.alignForward(u64, seg.filesize, MachO.getPageSize(self.options.target.cpu.arch));
1934 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
1935 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
1936 // except for code signature data.
1937 try self.file.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
1938
1939 self.codesig_cmd.dataoff = @as(u32, @intCast(offset));
1940 self.codesig_cmd.datasize = @as(u32, @intCast(needed_size));
1941 }
1942
1943 fn writeCodeSignature(self: *Zld, comp: *const Compilation, code_sig: *CodeSignature) !void {
1944 const seg_id = self.header_segment_cmd_index.?;
1945 const seg = self.segments.items[seg_id];
1946
1947 var buffer = std.ArrayList(u8).init(self.gpa);
1948 defer buffer.deinit();
1949 try buffer.ensureTotalCapacityPrecise(code_sig.size());
1950 try code_sig.writeAdhocSignature(comp, .{
1951 .file = self.file,
1952 .exec_seg_base = seg.fileoff,
1953 .exec_seg_limit = seg.filesize,
1954 .file_size = self.codesig_cmd.dataoff,
1955 .output_mode = self.options.output_mode,
1956 }, buffer.writer());
1957 assert(buffer.items.len == code_sig.size());
1958
1959 log.debug("writing code signature from 0x{x} to 0x{x}", .{
1960 self.codesig_cmd.dataoff,
1961 self.codesig_cmd.dataoff + buffer.items.len,
1962 });
1963
1964 try self.file.pwriteAll(buffer.items, self.codesig_cmd.dataoff);
1965 }
1966
1967 /// Writes Mach-O file header.
1968 fn writeHeader(self: *Zld, ncmds: u32, sizeofcmds: u32) !void {
1969 var header: macho.mach_header_64 = .{};
1970 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
1971
1972 switch (self.options.target.cpu.arch) {
1973 .aarch64 => {
1974 header.cputype = macho.CPU_TYPE_ARM64;
1975 header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL;
1976 },
1977 .x86_64 => {
1978 header.cputype = macho.CPU_TYPE_X86_64;
1979 header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL;
1980 },
1981 else => return error.UnsupportedCpuArchitecture,
1982 }
1983
1984 switch (self.options.output_mode) {
1985 .Exe => {
1986 header.filetype = macho.MH_EXECUTE;
1987 },
1988 .Lib => {
1989 // By this point, it can only be a dylib.
1990 header.filetype = macho.MH_DYLIB;
1991 header.flags |= macho.MH_NO_REEXPORTED_DYLIBS;
1992 },
1993 else => unreachable,
1994 }
1995
1996 if (self.thread_vars_section_index) |sect_id| {
1997 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
1998 if (self.sections.items(.header)[sect_id].size > 0) {
1999 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
2000 }
2001 }
2002
2003 header.ncmds = ncmds;
2004 header.sizeofcmds = sizeofcmds;
2005
2006 log.debug("writing Mach-O header {}", .{header});
2007
2008 try self.file.pwriteAll(mem.asBytes(&header), 0);
2009 }
2010
2011 pub fn makeStaticString(bytes: []const u8) [16]u8 {
2012 var buf = [_]u8{0} ** 16;
2013 @memcpy(buf[0..bytes.len], bytes);
2014 return buf;
2015 }
2016
2017 pub fn getAtomPtr(self: *Zld, atom_index: Atom.Index) *Atom {
2018 assert(atom_index < self.atoms.items.len);
2019 return &self.atoms.items[atom_index];
2020 }
2021
2022 pub fn getAtom(self: Zld, atom_index: Atom.Index) Atom {
2023 assert(atom_index < self.atoms.items.len);
2024 return self.atoms.items[atom_index];
2025 }
2026
2027 fn getSegmentByName(self: Zld, segname: []const u8) ?u8 {
2028 for (self.segments.items, 0..) |seg, i| {
2029 if (mem.eql(u8, segname, seg.segName())) return @as(u8, @intCast(i));
2030 } else return null;
2031 }
2032
2033 pub fn getSegment(self: Zld, sect_id: u8) macho.segment_command_64 {
2034 const index = self.sections.items(.segment_index)[sect_id];
2035 return self.segments.items[index];
2036 }
2037
2038 pub fn getSegmentPtr(self: *Zld, sect_id: u8) *macho.segment_command_64 {
2039 const index = self.sections.items(.segment_index)[sect_id];
2040 return &self.segments.items[index];
2041 }
2042
2043 pub fn getLinkeditSegmentPtr(self: *Zld) *macho.segment_command_64 {
2044 assert(self.segments.items.len > 0);
2045 const seg = &self.segments.items[self.segments.items.len - 1];
2046 assert(mem.eql(u8, seg.segName(), "__LINKEDIT"));
2047 return seg;
2048 }
2049
2050 pub fn getSectionByName(self: Zld, segname: []const u8, sectname: []const u8) ?u8 {
2051 // TODO investigate caching with a hashmap
2052 for (self.sections.items(.header), 0..) |header, i| {
2053 if (mem.eql(u8, header.segName(), segname) and mem.eql(u8, header.sectName(), sectname))
2054 return @as(u8, @intCast(i));
2055 } else return null;
2056 }
2057
2058 pub fn getSectionIndexes(self: Zld, segment_index: u8) struct { start: u8, end: u8 } {
2059 var start: u8 = 0;
2060 const nsects = for (self.segments.items, 0..) |seg, i| {
2061 if (i == segment_index) break @as(u8, @intCast(seg.nsects));
2062 start += @as(u8, @intCast(seg.nsects));
2063 } else 0;
2064 return .{ .start = start, .end = start + nsects };
2065 }
2066
2067 pub fn symbolIsTemp(self: *Zld, sym_with_loc: SymbolWithLoc) bool {
2068 const sym = self.getSymbol(sym_with_loc);
2069 if (!sym.sect()) return false;
2070 if (sym.ext()) return false;
2071 const sym_name = self.getSymbolName(sym_with_loc);
2072 return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L");
2073 }
2074
2075 /// Returns pointer-to-symbol described by `sym_with_loc` descriptor.
2076 pub fn getSymbolPtr(self: *Zld, sym_with_loc: SymbolWithLoc) *macho.nlist_64 {
2077 if (sym_with_loc.getFile()) |file| {
2078 const object = &self.objects.items[file];
2079 return &object.symtab[sym_with_loc.sym_index];
2080 } else {
2081 return &self.locals.items[sym_with_loc.sym_index];
2082 }
2083 }
2084
2085 /// Returns symbol described by `sym_with_loc` descriptor.
2086 pub fn getSymbol(self: *const Zld, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
2087 if (sym_with_loc.getFile()) |file| {
2088 const object = &self.objects.items[file];
2089 return object.symtab[sym_with_loc.sym_index];
2090 } else {
2091 return self.locals.items[sym_with_loc.sym_index];
2092 }
2093 }
2094
2095 /// Returns name of the symbol described by `sym_with_loc` descriptor.
2096 pub fn getSymbolName(self: *const Zld, sym_with_loc: SymbolWithLoc) []const u8 {
2097 if (sym_with_loc.getFile()) |file| {
2098 const object = self.objects.items[file];
2099 return object.getSymbolName(sym_with_loc.sym_index);
2100 } else {
2101 const sym = self.locals.items[sym_with_loc.sym_index];
2102 return self.strtab.get(sym.n_strx).?;
2103 }
2104 }
2105
2106 pub fn getGlobalIndex(self: *const Zld, name: []const u8) ?u32 {
2107 return self.resolver.get(name);
2108 }
2109
2110 pub fn getGlobalPtr(self: *Zld, name: []const u8) ?*SymbolWithLoc {
2111 const global_index = self.resolver.get(name) orelse return null;
2112 return &self.globals.items[global_index];
2113 }
2114
2115 pub fn getGlobal(self: *const Zld, name: []const u8) ?SymbolWithLoc {
2116 const global_index = self.resolver.get(name) orelse return null;
2117 return self.globals.items[global_index];
2118 }
2119
2120 const GetOrPutGlobalPtrResult = struct {
2121 found_existing: bool,
2122 value_ptr: *SymbolWithLoc,
2123 };
2124
2125 pub fn getOrPutGlobalPtr(self: *Zld, name: []const u8) !GetOrPutGlobalPtrResult {
2126 if (self.getGlobalPtr(name)) |ptr| {
2127 return GetOrPutGlobalPtrResult{ .found_existing = true, .value_ptr = ptr };
2128 }
2129 const global_index = try self.allocateGlobal();
2130 const global_name = try self.gpa.dupe(u8, name);
2131 _ = try self.resolver.put(self.gpa, global_name, global_index);
2132 const ptr = &self.globals.items[global_index];
2133 return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr };
2134 }
2135
2136 pub fn getGotEntryAddress(self: *Zld, sym_with_loc: SymbolWithLoc) ?u64 {
2137 const index = self.got_table.lookup.get(sym_with_loc) orelse return null;
2138 const header = self.sections.items(.header)[self.got_section_index.?];
2139 return header.addr + @sizeOf(u64) * index;
2140 }
2141
2142 pub fn getTlvPtrEntryAddress(self: *Zld, sym_with_loc: SymbolWithLoc) ?u64 {
2143 const index = self.tlv_ptr_table.lookup.get(sym_with_loc) orelse return null;
2144 const header = self.sections.items(.header)[self.tlv_ptr_section_index.?];
2145 return header.addr + @sizeOf(u64) * index;
2146 }
2147
2148 pub fn getStubsEntryAddress(self: *Zld, sym_with_loc: SymbolWithLoc) ?u64 {
2149 const index = self.stubs_table.lookup.get(sym_with_loc) orelse return null;
2150 const header = self.sections.items(.header)[self.stubs_section_index.?];
2151 return header.addr + stubs.stubSize(self.options.target.cpu.arch) * index;
2152 }
2153
2154 /// Returns symbol location corresponding to the set entrypoint.
2155 /// Asserts output mode is executable.
2156 pub fn getEntryPoint(self: Zld) SymbolWithLoc {
2157 assert(self.options.output_mode == .Exe);
2158 const global_index = self.entry_index.?;
2159 return self.globals.items[global_index];
2160 }
2161
2162 inline fn requiresThunks(self: Zld) bool {
2163 return self.options.target.cpu.arch == .aarch64;
2164 }
2165
2166 pub fn generateSymbolStabs(self: *Zld, object: Object, locals: *std.ArrayList(macho.nlist_64)) !void {
2167 log.debug("generating stabs for '{s}'", .{object.name});
2168
2169 const gpa = self.gpa;
2170 var debug_info = object.parseDwarfInfo();
2171
2172 var lookup = DwarfInfo.AbbrevLookupTable.init(gpa);
2173 defer lookup.deinit();
2174 try lookup.ensureUnusedCapacity(std.math.maxInt(u8));
2175
2176 // We assume there is only one CU.
2177 var cu_it = debug_info.getCompileUnitIterator();
2178 const compile_unit = while (try cu_it.next()) |cu| {
2179 const offset = math.cast(usize, cu.cuh.debug_abbrev_offset) orelse return error.Overflow;
2180 try debug_info.genAbbrevLookupByKind(offset, &lookup);
2181 break cu;
2182 } else {
2183 log.debug("no compile unit found in debug info in {s}; skipping", .{object.name});
2184 return;
2185 };
2186
2187 var abbrev_it = compile_unit.getAbbrevEntryIterator(debug_info);
2188 const cu_entry: DwarfInfo.AbbrevEntry = while (try abbrev_it.next(lookup)) |entry| switch (entry.tag) {
2189 dwarf.TAG.compile_unit => break entry,
2190 else => continue,
2191 } else {
2192 log.debug("missing DWARF_TAG_compile_unit tag in {s}; skipping", .{object.name});
2193 return;
2194 };
2195
2196 var maybe_tu_name: ?[]const u8 = null;
2197 var maybe_tu_comp_dir: ?[]const u8 = null;
2198 var attr_it = cu_entry.getAttributeIterator(debug_info, compile_unit.cuh);
2199
2200 while (try attr_it.next()) |attr| switch (attr.name) {
2201 dwarf.AT.comp_dir => maybe_tu_comp_dir = attr.getString(debug_info, compile_unit.cuh) orelse continue,
2202 dwarf.AT.name => maybe_tu_name = attr.getString(debug_info, compile_unit.cuh) orelse continue,
2203 else => continue,
2204 };
2205
2206 if (maybe_tu_name == null or maybe_tu_comp_dir == null) {
2207 log.debug("missing DWARF_AT_comp_dir and DWARF_AT_name attributes {s}; skipping", .{object.name});
2208 return;
2209 }
2210
2211 const tu_name = maybe_tu_name.?;
2212 const tu_comp_dir = maybe_tu_comp_dir.?;
2213
2214 // Open scope
2215 try locals.ensureUnusedCapacity(3);
2216 locals.appendAssumeCapacity(.{
2217 .n_strx = try self.strtab.insert(gpa, tu_comp_dir),
2218 .n_type = macho.N_SO,
2219 .n_sect = 0,
2220 .n_desc = 0,
2221 .n_value = 0,
2222 });
2223 locals.appendAssumeCapacity(.{
2224 .n_strx = try self.strtab.insert(gpa, tu_name),
2225 .n_type = macho.N_SO,
2226 .n_sect = 0,
2227 .n_desc = 0,
2228 .n_value = 0,
2229 });
2230 locals.appendAssumeCapacity(.{
2231 .n_strx = try self.strtab.insert(gpa, object.name),
2232 .n_type = macho.N_OSO,
2233 .n_sect = 0,
2234 .n_desc = 1,
2235 .n_value = object.mtime,
2236 });
2237
2238 var stabs_buf: [4]macho.nlist_64 = undefined;
2239
2240 var name_lookup: ?DwarfInfo.SubprogramLookupByName = if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS == 0) blk: {
2241 var name_lookup = DwarfInfo.SubprogramLookupByName.init(gpa);
2242 errdefer name_lookup.deinit();
2243 try name_lookup.ensureUnusedCapacity(@as(u32, @intCast(object.atoms.items.len)));
2244 try debug_info.genSubprogramLookupByName(compile_unit, lookup, &name_lookup);
2245 break :blk name_lookup;
2246 } else null;
2247 defer if (name_lookup) |*nl| nl.deinit();
2248
2249 for (object.atoms.items) |atom_index| {
2250 const atom = self.getAtom(atom_index);
2251 const stabs = try self.generateSymbolStabsForSymbol(
2252 atom_index,
2253 atom.getSymbolWithLoc(),
2254 name_lookup,
2255 &stabs_buf,
2256 );
2257 try locals.appendSlice(stabs);
2258
2259 var it = Atom.getInnerSymbolsIterator(self, atom_index);
2260 while (it.next()) |sym_loc| {
2261 const contained_stabs = try self.generateSymbolStabsForSymbol(
2262 atom_index,
2263 sym_loc,
2264 name_lookup,
2265 &stabs_buf,
2266 );
2267 try locals.appendSlice(contained_stabs);
2268 }
2269 }
2270
2271 // Close scope
2272 try locals.append(.{
2273 .n_strx = 0,
2274 .n_type = macho.N_SO,
2275 .n_sect = 0,
2276 .n_desc = 0,
2277 .n_value = 0,
2278 });
2279 }
2280
2281 fn generateSymbolStabsForSymbol(
2282 self: *Zld,
2283 atom_index: Atom.Index,
2284 sym_loc: SymbolWithLoc,
2285 lookup: ?DwarfInfo.SubprogramLookupByName,
2286 buf: *[4]macho.nlist_64,
2287 ) ![]const macho.nlist_64 {
2288 const gpa = self.gpa;
2289 const object = self.objects.items[sym_loc.getFile().?];
2290 const sym = self.getSymbol(sym_loc);
2291 const sym_name = self.getSymbolName(sym_loc);
2292 const header = self.sections.items(.header)[sym.n_sect - 1];
2293
2294 if (sym.n_strx == 0) return buf[0..0];
2295 if (self.symbolIsTemp(sym_loc)) return buf[0..0];
2296
2297 if (!header.isCode()) {
2298 // Since we are not dealing with machine code, it's either a global or a static depending
2299 // on the linkage scope.
2300 if (sym.sect() and sym.ext()) {
2301 // Global gets an N_GSYM stab type.
2302 buf[0] = .{
2303 .n_strx = try self.strtab.insert(gpa, sym_name),
2304 .n_type = macho.N_GSYM,
2305 .n_sect = sym.n_sect,
2306 .n_desc = 0,
2307 .n_value = 0,
2308 };
2309 } else {
2310 // Local static gets an N_STSYM stab type.
2311 buf[0] = .{
2312 .n_strx = try self.strtab.insert(gpa, sym_name),
2313 .n_type = macho.N_STSYM,
2314 .n_sect = sym.n_sect,
2315 .n_desc = 0,
2316 .n_value = sym.n_value,
2317 };
2318 }
2319 return buf[0..1];
2320 }
2321
2322 const size: u64 = size: {
2323 if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) {
2324 break :size self.getAtom(atom_index).size;
2325 }
2326
2327 // Since we don't have subsections to work with, we need to infer the size of each function
2328 // the slow way by scanning the debug info for matching symbol names and extracting
2329 // the symbol's DWARF_AT_low_pc and DWARF_AT_high_pc values.
2330 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
2331 const subprogram = lookup.?.get(sym_name[1..]) orelse return buf[0..0];
2332
2333 if (subprogram.addr <= source_sym.n_value and source_sym.n_value < subprogram.addr + subprogram.size) {
2334 break :size subprogram.size;
2335 } else {
2336 log.debug("no stab found for {s}", .{sym_name});
2337 return buf[0..0];
2338 }
2339 };
2340
2341 buf[0] = .{
2342 .n_strx = 0,
2343 .n_type = macho.N_BNSYM,
2344 .n_sect = sym.n_sect,
2345 .n_desc = 0,
2346 .n_value = sym.n_value,
2347 };
2348 buf[1] = .{
2349 .n_strx = try self.strtab.insert(gpa, sym_name),
2350 .n_type = macho.N_FUN,
2351 .n_sect = sym.n_sect,
2352 .n_desc = 0,
2353 .n_value = sym.n_value,
2354 };
2355 buf[2] = .{
2356 .n_strx = 0,
2357 .n_type = macho.N_FUN,
2358 .n_sect = 0,
2359 .n_desc = 0,
2360 .n_value = size,
2361 };
2362 buf[3] = .{
2363 .n_strx = 0,
2364 .n_type = macho.N_ENSYM,
2365 .n_sect = sym.n_sect,
2366 .n_desc = 0,
2367 .n_value = size,
2368 };
2369
2370 return buf;
2371 }
2372
2373 fn logSegments(self: *Zld) void {
2374 log.debug("segments:", .{});
2375 for (self.segments.items, 0..) |segment, i| {
2376 log.debug(" segment({d}): {s} @{x} ({x}), sizeof({x})", .{
2377 i,
2378 segment.segName(),
2379 segment.fileoff,
2380 segment.vmaddr,
2381 segment.vmsize,
2382 });
2383 }
2384 }
2385
2386 fn logSections(self: *Zld) void {
2387 log.debug("sections:", .{});
2388 for (self.sections.items(.header), 0..) |header, i| {
2389 log.debug(" sect({d}): {s},{s} @{x} ({x}), sizeof({x})", .{
2390 i + 1,
2391 header.segName(),
2392 header.sectName(),
2393 header.offset,
2394 header.addr,
2395 header.size,
2396 });
2397 }
2398 }
2399
2400 fn logSymAttributes(sym: macho.nlist_64, buf: []u8) []const u8 {
2401 if (sym.sect()) {
2402 buf[0] = 's';
2403 }
2404 if (sym.ext()) {
2405 if (sym.weakDef() or sym.pext()) {
2406 buf[1] = 'w';
2407 } else {
2408 buf[1] = 'e';
2409 }
2410 }
2411 if (sym.tentative()) {
2412 buf[2] = 't';
2413 }
2414 if (sym.undf()) {
2415 buf[3] = 'u';
2416 }
2417 return buf[0..];
2418 }
2419
2420 fn logSymtab(self: *Zld) void {
2421 var buf: [4]u8 = undefined;
2422
2423 const scoped_log = std.log.scoped(.symtab);
2424
2425 scoped_log.debug("locals:", .{});
2426 for (self.objects.items, 0..) |object, id| {
2427 scoped_log.debug(" object({d}): {s}", .{ id, object.name });
2428 if (object.in_symtab == null) continue;
2429 for (object.symtab, 0..) |sym, sym_id| {
2430 @memset(&buf, '_');
2431 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{
2432 sym_id,
2433 object.getSymbolName(@as(u32, @intCast(sym_id))),
2434 sym.n_value,
2435 sym.n_sect,
2436 logSymAttributes(sym, &buf),
2437 });
2438 }
2439 }
2440 scoped_log.debug(" object(-1)", .{});
2441 for (self.locals.items, 0..) |sym, sym_id| {
2442 if (sym.undf()) continue;
2443 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{
2444 sym_id,
2445 self.strtab.get(sym.n_strx).?,
2446 sym.n_value,
2447 sym.n_sect,
2448 logSymAttributes(sym, &buf),
2449 });
2450 }
2451
2452 scoped_log.debug("exports:", .{});
2453 for (self.globals.items, 0..) |global, i| {
2454 const sym = self.getSymbol(global);
2455 if (sym.undf()) continue;
2456 if (sym.n_desc == MachO.N_DEAD) continue;
2457 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s} (def in object({?}))", .{
2458 i,
2459 self.getSymbolName(global),
2460 sym.n_value,
2461 sym.n_sect,
2462 logSymAttributes(sym, &buf),
2463 global.file,
2464 });
2465 }
2466
2467 scoped_log.debug("imports:", .{});
2468 for (self.globals.items, 0..) |global, i| {
2469 const sym = self.getSymbol(global);
2470 if (!sym.undf()) continue;
2471 if (sym.n_desc == MachO.N_DEAD) continue;
2472 const ord = @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER);
2473 scoped_log.debug(" %{d}: {s} @{x} in ord({d}), {s}", .{
2474 i,
2475 self.getSymbolName(global),
2476 sym.n_value,
2477 ord,
2478 logSymAttributes(sym, &buf),
2479 });
2480 }
2481
2482 scoped_log.debug("GOT entries:", .{});
2483 scoped_log.debug("{}", .{self.got_table});
2484
2485 scoped_log.debug("TLV pointers:", .{});
2486 scoped_log.debug("{}", .{self.tlv_ptr_table});
2487
2488 scoped_log.debug("stubs entries:", .{});
2489 scoped_log.debug("{}", .{self.stubs_table});
2490
2491 scoped_log.debug("thunks:", .{});
2492 for (self.thunks.items, 0..) |thunk, i| {
2493 scoped_log.debug(" thunk({d})", .{i});
2494 const slice = thunk.targets.slice();
2495 for (slice.items(.tag), slice.items(.target), 0..) |tag, target, j| {
2496 const atom_index = @as(u32, @intCast(thunk.getStartAtomIndex() + j));
2497 const atom = self.getAtom(atom_index);
2498 const atom_sym = self.getSymbol(atom.getSymbolWithLoc());
2499 const target_addr = switch (tag) {
2500 .stub => self.getStubsEntryAddress(target).?,
2501 .atom => self.getSymbol(target).n_value,
2502 };
2503 scoped_log.debug(" {d}@{x} => {s}({s}@{x})", .{
2504 j,
2505 atom_sym.n_value,
2506 @tagName(tag),
2507 self.getSymbolName(target),
2508 target_addr,
2509 });
2510 }
2511 }
2512 }
2513
2514 fn logAtoms(self: *Zld) void {
2515 log.debug("atoms:", .{});
2516 const slice = self.sections.slice();
2517 for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| {
2518 var atom_index = first_atom_index orelse continue;
2519 const header = slice.items(.header)[sect_id];
2520
2521 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
2522
2523 while (true) {
2524 const atom = self.getAtom(atom_index);
2525 self.logAtom(atom_index, log);
2526
2527 if (atom.next_index) |next_index| {
2528 atom_index = next_index;
2529 } else break;
2530 }
2531 }
2532 }
2533
2534 pub fn logAtom(self: *Zld, atom_index: Atom.Index, logger: anytype) void {
2535 if (!build_options.enable_logging) return;
2536
2537 const atom = self.getAtom(atom_index);
2538 const sym = self.getSymbol(atom.getSymbolWithLoc());
2539 const sym_name = self.getSymbolName(atom.getSymbolWithLoc());
2540 logger.debug(" ATOM({d}, %{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?}) in sect({d})", .{
2541 atom_index,
2542 atom.sym_index,
2543 sym_name,
2544 sym.n_value,
2545 atom.size,
2546 atom.alignment,
2547 atom.getFile(),
2548 sym.n_sect,
2549 });
2550
2551 if (atom.getFile() != null) {
2552 var it = Atom.getInnerSymbolsIterator(self, atom_index);
2553 while (it.next()) |sym_loc| {
2554 const inner = self.getSymbol(sym_loc);
2555 const inner_name = self.getSymbolName(sym_loc);
2556 const offset = Atom.calcInnerSymbolOffset(self, atom_index, sym_loc.sym_index);
2557
2558 logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{
2559 sym_loc.sym_index,
2560 inner_name,
2561 inner.n_value,
2562 offset,
2563 });
2564 }
2565
2566 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
2567 const alias = self.getSymbol(sym_loc);
2568 const alias_name = self.getSymbolName(sym_loc);
2569
2570 logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{
2571 sym_loc.sym_index,
2572 alias_name,
2573 alias.n_value,
2574 0,
2575 });
2576 }
2577 }
2578 }
2579};
2580
2581pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {
1pub fn linkWithZld(
2 macho_file: *MachO,
3 comp: *Compilation,
4 prog_node: *std.Progress.Node,
5) link.File.FlushError!void {
25826 const tracy = trace(@src());
25837 defer tracy.end();
25848
......@@ -2611,8 +35,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
261135 defer sub_prog_node.end();
261236
261337 const cpu_arch = target.cpu.arch;
2614 const os_tag = target.os.tag;
2615 const abi = target.abi;
261638 const is_lib = options.output_mode == .Lib;
261739 const is_dyn_lib = options.link_mode == .Dynamic and is_lib;
261840 const is_exe_or_dyn_lib = is_dyn_lib or options.output_mode == .Exe;
......@@ -2730,29 +152,26 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
2730152 } else {
2731153 const sub_path = options.emit.?.sub_path;
2732154
155 const old_file = macho_file.base.file; // TODO is this needed at all?
156 defer macho_file.base.file = old_file;
157
2733158 const file = try directory.handle.createFile(sub_path, .{
2734159 .truncate = true,
2735160 .read = true,
2736161 .mode = link.determineMode(options.*),
2737162 });
2738163 defer file.close();
2739
2740 var zld = Zld{
2741 .gpa = gpa,
2742 .file = file,
2743 .options = options,
2744 };
2745 defer zld.deinit();
164 macho_file.base.file = file;
2746165
2747166 // Index 0 is always a null symbol.
2748 try zld.locals.append(gpa, .{
167 try macho_file.locals.append(gpa, .{
2749168 .n_strx = 0,
2750169 .n_type = 0,
2751170 .n_sect = 0,
2752171 .n_desc = 0,
2753172 .n_value = 0,
2754173 });
2755 try zld.strtab.buffer.append(gpa, 0);
174 try macho_file.strtab.buffer.append(gpa, 0);
2756175
2757176 // Positional arguments to the linker such as object files and static archives.
2758177 var positionals = std.ArrayList(Compilation.LinkObject).init(arena);
......@@ -2930,9 +349,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
2930349 const in_file = try std.fs.cwd().openFile(obj.path, .{});
2931350 defer in_file.close();
2932351
2933 MachO.parsePositional(
2934 &zld,
2935 gpa,
352 macho_file.parsePositional(
2936353 in_file,
2937354 obj.path,
2938355 obj.must_link,
......@@ -2949,9 +366,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
2949366 const in_file = try std.fs.cwd().openFile(path, .{});
2950367 defer in_file.close();
2951368
2952 MachO.parseLibrary(
2953 &zld,
2954 gpa,
369 macho_file.parseLibrary(
2955370 in_file,
2956371 path,
2957372 lib,
......@@ -2965,198 +380,199 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
2965380 };
2966381 }
2967382
2968 MachO.parseDependentLibs(&zld, gpa, &dependent_libs, options) catch |err| {
383 macho_file.parseDependentLibs(&dependent_libs, options) catch |err| {
2969384 // TODO convert to error
2970385 log.err("parsing dependent libraries failed with err {s}", .{@errorName(err)});
2971386 };
2972387
2973 try zld.resolveSymbols();
2974 try macho_file.reportUndefined(&zld);
2975
2976 if (options.output_mode == .Exe) {
2977 const entry_name = options.entry orelse load_commands.default_entry_point;
2978 const global_index = zld.resolver.get(entry_name).?; // Error was flagged earlier
2979 zld.entry_index = global_index;
2980 }
388 var actions = std.ArrayList(MachO.ResolveAction).init(gpa);
389 defer actions.deinit();
390 try macho_file.resolveSymbols(&actions);
391 try macho_file.reportUndefined();
2981392
2982 for (zld.objects.items, 0..) |*object, object_id| {
2983 try object.splitIntoAtoms(&zld, @as(u32, @intCast(object_id)));
393 for (macho_file.objects.items, 0..) |*object, object_id| {
394 try object.splitIntoAtoms(macho_file, @as(u32, @intCast(object_id)));
2984395 }
2985396
2986397 if (gc_sections) {
2987 try dead_strip.gcAtoms(&zld);
398 try dead_strip.gcAtoms(macho_file);
2988399 }
2989400
2990 try zld.createDyldPrivateAtom();
2991 try zld.createTentativeDefAtoms();
401 try macho_file.createDyldPrivateAtom();
402 try macho_file.createTentativeDefAtoms();
2992403
2993 if (zld.options.output_mode == .Exe) {
2994 const global = zld.getEntryPoint();
2995 if (zld.getSymbol(global).undf()) {
404 if (macho_file.options.output_mode == .Exe) {
405 const global = macho_file.getEntryPoint().?;
406 if (macho_file.getSymbol(global).undf()) {
2996407 // We do one additional check here in case the entry point was found in one of the dylibs.
2997408 // (I actually have no idea what this would imply but it is a possible outcome and so we
2998409 // support it.)
2999 try zld.addStubEntry(global);
410 try macho_file.addStubEntry(global);
3000411 }
3001412 }
3002413
3003 for (zld.objects.items) |object| {
414 for (macho_file.objects.items) |object| {
3004415 for (object.atoms.items) |atom_index| {
3005 const atom = zld.getAtom(atom_index);
3006 const sym = zld.getSymbol(atom.getSymbolWithLoc());
3007 const header = zld.sections.items(.header)[sym.n_sect - 1];
416 const atom = macho_file.getAtom(atom_index);
417 const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
418 const header = macho_file.sections.items(.header)[sym.n_sect - 1];
3008419 if (header.isZerofill()) continue;
3009420
3010 const relocs = Atom.getAtomRelocs(&zld, atom_index);
3011 try Atom.scanAtomRelocs(&zld, atom_index, relocs);
421 const relocs = Atom.getAtomRelocs(macho_file, atom_index);
422 try Atom.scanAtomRelocs(macho_file, atom_index, relocs);
3012423 }
3013424 }
3014425
3015 try eh_frame.scanRelocs(&zld);
3016 try UnwindInfo.scanRelocs(&zld);
426 try eh_frame.scanRelocs(macho_file);
427 try UnwindInfo.scanRelocs(macho_file);
3017428
3018 if (zld.dyld_stub_binder_index) |index| try zld.addGotEntry(zld.globals.items[index]);
429 if (macho_file.dyld_stub_binder_index) |index|
430 try macho_file.addGotEntry(macho_file.globals.items[index]);
3019431
3020 try zld.calcSectionSizes();
432 try macho_file.calcSectionSizes();
3021433
3022 var unwind_info = UnwindInfo{ .gpa = zld.gpa };
434 var unwind_info = UnwindInfo{ .gpa = gpa };
3023435 defer unwind_info.deinit();
3024 try unwind_info.collect(&zld);
436 try unwind_info.collect(macho_file);
3025437
3026 try eh_frame.calcSectionSize(&zld, &unwind_info);
3027 try unwind_info.calcSectionSize(&zld);
438 try eh_frame.calcSectionSize(macho_file, &unwind_info);
439 try unwind_info.calcSectionSize(macho_file);
3028440
3029 try zld.pruneAndSortSections();
3030 try zld.createSegments();
3031 try zld.allocateSegments();
441 try pruneAndSortSections(macho_file);
442 try createSegments(macho_file);
443 try allocateSegments(macho_file);
3032444
3033 try MachO.allocateSpecialSymbols(&zld);
445 try macho_file.allocateSpecialSymbols();
3034446
3035447 if (build_options.enable_logging) {
3036 zld.logSymtab();
3037 zld.logSegments();
3038 zld.logSections();
3039 zld.logAtoms();
448 macho_file.logSymtab();
449 macho_file.logSegments();
450 macho_file.logSections();
451 macho_file.logAtoms();
3040452 }
3041453
3042 try zld.writeAtoms();
3043 if (zld.requiresThunks()) try zld.writeThunks();
3044 try zld.writeDyldPrivateAtom();
454 try writeAtoms(macho_file);
455 if (macho_file.requiresThunks()) try writeThunks(macho_file);
456 try writeDyldPrivateAtom(macho_file);
3045457
3046 if (zld.stubs_section_index) |_| {
3047 try zld.writeStubs();
3048 try zld.writeStubHelpers();
3049 try zld.writeLaSymbolPtrs();
458 if (macho_file.stubs_section_index) |_| {
459 try writeStubs(macho_file);
460 try writeStubHelpers(macho_file);
461 try writeLaSymbolPtrs(macho_file);
3050462 }
3051 if (zld.got_section_index) |sect_id| try zld.writePointerEntries(sect_id, &zld.got_table);
3052 if (zld.tlv_ptr_section_index) |sect_id| try zld.writePointerEntries(sect_id, &zld.tlv_ptr_table);
463 if (macho_file.got_section_index) |sect_id|
464 try macho_file.writePointerEntries(sect_id, &macho_file.got_table);
465 if (macho_file.tlv_ptr_section_index) |sect_id|
466 try macho_file.writePointerEntries(sect_id, &macho_file.tlv_ptr_table);
3053467
3054 try eh_frame.write(&zld, &unwind_info);
3055 try unwind_info.write(&zld);
3056 try zld.writeLinkeditSegmentData();
468 try eh_frame.write(macho_file, &unwind_info);
469 try unwind_info.write(macho_file);
470 try macho_file.writeLinkeditSegmentData();
3057471
3058472 // If the last section of __DATA segment is zerofill section, we need to ensure
3059473 // that the free space between the end of the last non-zerofill section of __DATA
3060474 // segment and the beginning of __LINKEDIT segment is zerofilled as the loader will
3061475 // copy-paste this space into memory for quicker zerofill operation.
3062 if (zld.data_segment_cmd_index) |data_seg_id| blk: {
476 if (macho_file.data_segment_cmd_index) |data_seg_id| blk: {
3063477 var physical_zerofill_start: ?u64 = null;
3064 const section_indexes = zld.getSectionIndexes(data_seg_id);
3065 for (zld.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| {
478 const section_indexes = macho_file.getSectionIndexes(data_seg_id);
479 for (macho_file.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| {
3066480 if (header.isZerofill() and header.size > 0) break;
3067481 physical_zerofill_start = header.offset + header.size;
3068482 } else break :blk;
3069483 const start = physical_zerofill_start orelse break :blk;
3070 const linkedit = zld.getLinkeditSegmentPtr();
484 const linkedit = macho_file.getLinkeditSegmentPtr();
3071485 const size = math.cast(usize, linkedit.fileoff - start) orelse return error.Overflow;
3072486 if (size > 0) {
3073487 log.debug("zeroing out zerofill area of length {x} at {x}", .{ size, start });
3074 var padding = try zld.gpa.alloc(u8, size);
3075 defer zld.gpa.free(padding);
488 var padding = try gpa.alloc(u8, size);
489 defer gpa.free(padding);
3076490 @memset(padding, 0);
3077 try zld.file.pwriteAll(padding, start);
491 try macho_file.base.file.?.pwriteAll(padding, start);
3078492 }
3079493 }
3080494
3081495 // Write code signature padding if required
3082 const requires_codesig = blk: {
3083 if (options.entitlements) |_| break :blk true;
3084 if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true;
3085 break :blk false;
3086 };
3087 var codesig: ?CodeSignature = if (requires_codesig) blk: {
496 var codesig: ?CodeSignature = if (macho_file.requiresCodeSignature()) blk: {
3088497 // Preallocate space for the code signature.
3089498 // We need to do this at this stage so that we have the load commands with proper values
3090499 // written out to the file.
3091500 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
3092501 // where the code signature goes into.
3093 var codesig = CodeSignature.init(MachO.getPageSize(zld.options.target.cpu.arch));
502 var codesig = CodeSignature.init(MachO.getPageSize(cpu_arch));
3094503 codesig.code_directory.ident = fs.path.basename(full_out_path);
3095504 if (options.entitlements) |path| {
3096 try codesig.addEntitlements(zld.gpa, path);
505 try codesig.addEntitlements(gpa, path);
3097506 }
3098 try zld.writeCodeSignaturePadding(&codesig);
507 try macho_file.writeCodeSignaturePadding(&codesig);
3099508 break :blk codesig;
3100509 } else null;
3101 defer if (codesig) |*csig| csig.deinit(zld.gpa);
510 defer if (codesig) |*csig| csig.deinit(gpa);
3102511
3103512 // Write load commands
3104513 var lc_buffer = std.ArrayList(u8).init(arena);
3105514 const lc_writer = lc_buffer.writer();
3106515
3107 try MachO.writeSegmentHeaders(&zld, lc_writer);
3108 try lc_writer.writeStruct(zld.dyld_info_cmd);
3109 try lc_writer.writeStruct(zld.function_starts_cmd);
3110 try lc_writer.writeStruct(zld.data_in_code_cmd);
3111 try lc_writer.writeStruct(zld.symtab_cmd);
3112 try lc_writer.writeStruct(zld.dysymtab_cmd);
516 try macho_file.writeSegmentHeaders(lc_writer);
517 try lc_writer.writeStruct(macho_file.dyld_info_cmd);
518 try lc_writer.writeStruct(macho_file.function_starts_cmd);
519 try lc_writer.writeStruct(macho_file.data_in_code_cmd);
520 try lc_writer.writeStruct(macho_file.symtab_cmd);
521 try lc_writer.writeStruct(macho_file.dysymtab_cmd);
3113522 try load_commands.writeDylinkerLC(lc_writer);
3114523
3115 if (zld.options.output_mode == .Exe) {
3116 const seg_id = zld.header_segment_cmd_index.?;
3117 const seg = zld.segments.items[seg_id];
3118 const global = zld.getEntryPoint();
3119 const sym = zld.getSymbol(global);
3120
3121 const addr: u64 = if (sym.undf())
3122 // In this case, the symbol has been resolved in one of dylibs and so we point
3123 // to the stub as its vmaddr value.
3124 zld.getStubsEntryAddress(global).?
3125 else
3126 sym.n_value;
3127
3128 try lc_writer.writeStruct(macho.entry_point_command{
3129 .entryoff = @as(u32, @intCast(addr - seg.vmaddr)),
3130 .stacksize = options.stack_size_override orelse 0,
3131 });
3132 } else {
3133 assert(zld.options.output_mode == .Lib);
3134 try load_commands.writeDylibIdLC(zld.gpa, zld.options, lc_writer);
524 switch (macho_file.base.options.output_mode) {
525 .Exe => blk: {
526 const seg_id = macho_file.header_segment_cmd_index.?;
527 const seg = macho_file.segments.items[seg_id];
528 const global = macho_file.getEntryPoint() orelse break :blk;
529 const sym = macho_file.getSymbol(global);
530
531 const addr: u64 = if (sym.undf())
532 // In this case, the symbol has been resolved in one of dylibs and so we point
533 // to the stub as its vmaddr value.
534 macho_file.getStubsEntryAddress(global).?
535 else
536 sym.n_value;
537
538 try lc_writer.writeStruct(macho.entry_point_command{
539 .entryoff = @as(u32, @intCast(addr - seg.vmaddr)),
540 .stacksize = macho_file.base.options.stack_size_override orelse 0,
541 });
542 },
543 .Lib => if (macho_file.base.options.link_mode == .Dynamic) {
544 try load_commands.writeDylibIdLC(gpa, &macho_file.base.options, lc_writer);
545 },
546 else => {},
3135547 }
3136548
3137 try load_commands.writeRpathLCs(zld.gpa, zld.options, lc_writer);
549 try load_commands.writeRpathLCs(gpa, macho_file.base.options, lc_writer);
3138550 try lc_writer.writeStruct(macho.source_version_command{
3139551 .version = 0,
3140552 });
3141 try load_commands.writeBuildVersionLC(zld.options, lc_writer);
553 try load_commands.writeBuildVersionLC(macho_file.base.options, lc_writer);
3142554
3143555 const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + @as(u32, @intCast(lc_buffer.items.len));
3144 try lc_writer.writeStruct(zld.uuid_cmd);
556 try lc_writer.writeStruct(macho_file.uuid_cmd);
3145557
3146 try load_commands.writeLoadDylibLCs(zld.dylibs.items, zld.referenced_dylibs.keys(), lc_writer);
558 try load_commands.writeLoadDylibLCs(
559 macho_file.dylibs.items,
560 macho_file.referenced_dylibs.keys(),
561 lc_writer,
562 );
3147563
3148 if (requires_codesig) {
3149 try lc_writer.writeStruct(zld.codesig_cmd);
564 if (codesig != null) {
565 try lc_writer.writeStruct(macho_file.codesig_cmd);
3150566 }
3151567
3152568 const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);
3153 try zld.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
3154 try zld.writeHeader(ncmds, @as(u32, @intCast(lc_buffer.items.len)));
3155 try zld.writeUuid(comp, uuid_cmd_offset, requires_codesig);
569 try macho_file.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
570 try macho_file.writeHeader(ncmds, @as(u32, @intCast(lc_buffer.items.len)));
571 try macho_file.writeUuid(comp, uuid_cmd_offset, codesig != null);
3156572
3157573 if (codesig) |*csig| {
3158 try zld.writeCodeSignature(comp, csig); // code signing always comes last
3159 try MachO.invalidateKernelCache(directory.handle, zld.options.emit.?.sub_path);
574 try macho_file.writeCodeSignature(comp, csig); // code signing always comes last
575 try MachO.invalidateKernelCache(directory.handle, macho_file.base.options.emit.?.sub_path);
3160576 }
3161577 }
3162578
......@@ -3177,3 +593,609 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3177593 macho_file.base.lock = man.toOwnedLock();
3178594 }
3179595}
596
597fn createSegments(macho_file: *MachO) !void {
598 const gpa = macho_file.base.allocator;
599 const pagezero_vmsize = macho_file.base.options.pagezero_size orelse MachO.default_pagezero_vmsize;
600 const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch);
601 const aligned_pagezero_vmsize = mem.alignBackward(u64, pagezero_vmsize, page_size);
602 if (macho_file.base.options.output_mode != .Lib and aligned_pagezero_vmsize > 0) {
603 if (aligned_pagezero_vmsize != pagezero_vmsize) {
604 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});
605 log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize});
606 }
607 macho_file.pagezero_segment_cmd_index = @intCast(macho_file.segments.items.len);
608 try macho_file.segments.append(gpa, .{
609 .cmdsize = @sizeOf(macho.segment_command_64),
610 .segname = MachO.makeStaticString("__PAGEZERO"),
611 .vmsize = aligned_pagezero_vmsize,
612 });
613 }
614
615 // __TEXT segment is non-optional
616 {
617 const protection = MachO.getSegmentMemoryProtection("__TEXT");
618 macho_file.text_segment_cmd_index = @intCast(macho_file.segments.items.len);
619 macho_file.header_segment_cmd_index = macho_file.text_segment_cmd_index.?;
620 try macho_file.segments.append(gpa, .{
621 .cmdsize = @sizeOf(macho.segment_command_64),
622 .segname = MachO.makeStaticString("__TEXT"),
623 .maxprot = protection,
624 .initprot = protection,
625 });
626 }
627
628 for (macho_file.sections.items(.header), 0..) |header, sect_id| {
629 if (header.size == 0) continue; // empty section
630
631 const segname = header.segName();
632 const segment_id = macho_file.getSegmentByName(segname) orelse blk: {
633 log.debug("creating segment '{s}'", .{segname});
634 const segment_id = @as(u8, @intCast(macho_file.segments.items.len));
635 const protection = MachO.getSegmentMemoryProtection(segname);
636 try macho_file.segments.append(gpa, .{
637 .cmdsize = @sizeOf(macho.segment_command_64),
638 .segname = MachO.makeStaticString(segname),
639 .maxprot = protection,
640 .initprot = protection,
641 });
642 break :blk segment_id;
643 };
644 const segment = &macho_file.segments.items[segment_id];
645 segment.cmdsize += @sizeOf(macho.section_64);
646 segment.nsects += 1;
647 macho_file.sections.items(.segment_index)[sect_id] = segment_id;
648 }
649
650 if (macho_file.getSegmentByName("__DATA_CONST")) |index| {
651 macho_file.data_const_segment_cmd_index = index;
652 }
653
654 if (macho_file.getSegmentByName("__DATA")) |index| {
655 macho_file.data_segment_cmd_index = index;
656 }
657
658 // __LINKEDIT always comes last
659 {
660 const protection = MachO.getSegmentMemoryProtection("__LINKEDIT");
661 macho_file.linkedit_segment_cmd_index = @intCast(macho_file.segments.items.len);
662 try macho_file.segments.append(gpa, .{
663 .cmdsize = @sizeOf(macho.segment_command_64),
664 .segname = MachO.makeStaticString("__LINKEDIT"),
665 .maxprot = protection,
666 .initprot = protection,
667 });
668 }
669}
670
671fn writeAtoms(macho_file: *MachO) !void {
672 const gpa = macho_file.base.allocator;
673 const slice = macho_file.sections.slice();
674
675 for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| {
676 const header = slice.items(.header)[sect_id];
677 if (header.isZerofill()) continue;
678
679 var atom_index = first_atom_index orelse continue;
680
681 var buffer = try gpa.alloc(u8, math.cast(usize, header.size) orelse return error.Overflow);
682 defer gpa.free(buffer);
683 @memset(buffer, 0); // TODO with NOPs
684
685 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
686
687 while (true) {
688 const atom = macho_file.getAtom(atom_index);
689 if (atom.getFile()) |file| {
690 const this_sym = macho_file.getSymbol(atom.getSymbolWithLoc());
691 const padding_size: usize = if (atom.next_index) |next_index| blk: {
692 const next_sym = macho_file.getSymbol(macho_file.getAtom(next_index).getSymbolWithLoc());
693 const size = next_sym.n_value - (this_sym.n_value + atom.size);
694 break :blk math.cast(usize, size) orelse return error.Overflow;
695 } else 0;
696
697 log.debug(" (adding ATOM(%{d}, '{s}') from object({d}) to buffer)", .{
698 atom.sym_index,
699 macho_file.getSymbolName(atom.getSymbolWithLoc()),
700 file,
701 });
702 if (padding_size > 0) {
703 log.debug(" (with padding {x})", .{padding_size});
704 }
705
706 const offset = this_sym.n_value - header.addr;
707 log.debug(" (at offset 0x{x})", .{offset});
708
709 const code = Atom.getAtomCode(macho_file, atom_index);
710 const relocs = Atom.getAtomRelocs(macho_file, atom_index);
711 const size = math.cast(usize, atom.size) orelse return error.Overflow;
712 @memcpy(buffer[offset .. offset + size], code);
713 try Atom.resolveRelocs(
714 macho_file,
715 atom_index,
716 buffer[offset..][0..size],
717 relocs,
718 );
719 }
720
721 if (atom.next_index) |next_index| {
722 atom_index = next_index;
723 } else break;
724 }
725
726 log.debug(" (writing at file offset 0x{x})", .{header.offset});
727 try macho_file.base.file.?.pwriteAll(buffer, header.offset);
728 }
729}
730
731fn writeDyldPrivateAtom(macho_file: *MachO) !void {
732 const atom_index = macho_file.dyld_private_atom_index orelse return;
733 const atom = macho_file.getAtom(atom_index);
734 const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
735 const sect_id = macho_file.data_section_index.?;
736 const header = macho_file.sections.items(.header)[sect_id];
737 const offset = sym.n_value - header.addr + header.offset;
738 log.debug("writing __dyld_private at offset 0x{x}", .{offset});
739 const buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
740 try macho_file.base.file.?.pwriteAll(&buffer, offset);
741}
742
743fn writeThunks(macho_file: *MachO) !void {
744 assert(macho_file.requiresThunks());
745 const gpa = macho_file.base.allocator;
746
747 const sect_id = macho_file.text_section_index orelse return;
748 const header = macho_file.sections.items(.header)[sect_id];
749
750 for (macho_file.thunks.items, 0..) |*thunk, i| {
751 if (thunk.getSize() == 0) continue;
752 var buffer = try std.ArrayList(u8).initCapacity(gpa, thunk.getSize());
753 defer buffer.deinit();
754 try thunks.writeThunkCode(macho_file, thunk, buffer.writer());
755 const thunk_atom = macho_file.getAtom(thunk.getStartAtomIndex());
756 const thunk_sym = macho_file.getSymbol(thunk_atom.getSymbolWithLoc());
757 const offset = thunk_sym.n_value - header.addr + header.offset;
758 log.debug("writing thunk({d}) at offset 0x{x}", .{ i, offset });
759 try macho_file.base.file.?.pwriteAll(buffer.items, offset);
760 }
761}
762
763fn writePointerEntries(macho_file: *MachO, sect_id: u8, table: anytype) !void {
764 const gpa = macho_file.base.allocator;
765 const header = macho_file.sections.items(.header)[sect_id];
766 var buffer = try std.ArrayList(u8).initCapacity(gpa, header.size);
767 defer buffer.deinit();
768 for (table.entries.items) |entry| {
769 const sym = macho_file.getSymbol(entry);
770 buffer.writer().writeIntLittle(u64, sym.n_value) catch unreachable;
771 }
772 log.debug("writing __DATA_CONST,__got contents at file offset 0x{x}", .{header.offset});
773 try macho_file.base.file.?.pwriteAll(buffer.items, header.offset);
774}
775
776fn writeStubs(macho_file: *MachO) !void {
777 const gpa = macho_file.base.allocator;
778 const cpu_arch = macho_file.base.options.target.cpu.arch;
779 const stubs_header = macho_file.sections.items(.header)[macho_file.stubs_section_index.?];
780 const la_symbol_ptr_header = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_section_index.?];
781
782 var buffer = try std.ArrayList(u8).initCapacity(gpa, stubs_header.size);
783 defer buffer.deinit();
784
785 for (0..macho_file.stub_table.count()) |index| {
786 try stubs.writeStubCode(.{
787 .cpu_arch = cpu_arch,
788 .source_addr = stubs_header.addr + stubs.stubSize(cpu_arch) * index,
789 .target_addr = la_symbol_ptr_header.addr + index * @sizeOf(u64),
790 }, buffer.writer());
791 }
792
793 log.debug("writing __TEXT,__stubs contents at file offset 0x{x}", .{stubs_header.offset});
794 try macho_file.base.file.?.pwriteAll(buffer.items, stubs_header.offset);
795}
796
797fn writeStubHelpers(macho_file: *MachO) !void {
798 const gpa = macho_file.base.allocator;
799 const cpu_arch = macho_file.base.options.target.cpu.arch;
800 const stub_helper_header = macho_file.sections.items(.header)[macho_file.stub_helper_section_index.?];
801
802 var buffer = try std.ArrayList(u8).initCapacity(gpa, stub_helper_header.size);
803 defer buffer.deinit();
804
805 {
806 const dyld_private_addr = blk: {
807 const atom = macho_file.getAtom(macho_file.dyld_private_atom_index.?);
808 const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
809 break :blk sym.n_value;
810 };
811 const dyld_stub_binder_got_addr = blk: {
812 const sym_loc = macho_file.globals.items[macho_file.dyld_stub_binder_index.?];
813 break :blk macho_file.getGotEntryAddress(sym_loc).?;
814 };
815 try stubs.writeStubHelperPreambleCode(.{
816 .cpu_arch = cpu_arch,
817 .source_addr = stub_helper_header.addr,
818 .dyld_private_addr = dyld_private_addr,
819 .dyld_stub_binder_got_addr = dyld_stub_binder_got_addr,
820 }, buffer.writer());
821 }
822
823 for (0..macho_file.stub_table.count()) |index| {
824 const source_addr = stub_helper_header.addr + stubs.stubHelperPreambleSize(cpu_arch) +
825 stubs.stubHelperSize(cpu_arch) * index;
826 try stubs.writeStubHelperCode(.{
827 .cpu_arch = cpu_arch,
828 .source_addr = source_addr,
829 .target_addr = stub_helper_header.addr,
830 }, buffer.writer());
831 }
832
833 log.debug("writing __TEXT,__stub_helper contents at file offset 0x{x}", .{
834 stub_helper_header.offset,
835 });
836 try macho_file.base.file.?.pwriteAll(buffer.items, stub_helper_header.offset);
837}
838
839fn writeLaSymbolPtrs(macho_file: *MachO) !void {
840 const gpa = macho_file.base.allocator;
841 const cpu_arch = macho_file.base.options.target.cpu.arch;
842 const la_symbol_ptr_header = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_section_index.?];
843 const stub_helper_header = macho_file.sections.items(.header)[macho_file.stub_helper_section_index.?];
844
845 var buffer = try std.ArrayList(u8).initCapacity(gpa, la_symbol_ptr_header.size);
846 defer buffer.deinit();
847
848 for (0..macho_file.stub_table.count()) |index| {
849 const target_addr = stub_helper_header.addr + stubs.stubHelperPreambleSize(cpu_arch) +
850 stubs.stubHelperSize(cpu_arch) * index;
851 buffer.writer().writeIntLittle(u64, target_addr) catch unreachable;
852 }
853
854 log.debug("writing __DATA,__la_symbol_ptr contents at file offset 0x{x}", .{
855 la_symbol_ptr_header.offset,
856 });
857 try macho_file.base.file.?.pwriteAll(buffer.items, la_symbol_ptr_header.offset);
858}
859
860fn pruneAndSortSections(macho_file: *MachO) !void {
861 const Entry = struct {
862 index: u8,
863
864 pub fn lessThan(ctx: *MachO, lhs: @This(), rhs: @This()) bool {
865 const lhs_header = ctx.sections.items(.header)[lhs.index];
866 const rhs_header = ctx.sections.items(.header)[rhs.index];
867 return MachO.getSectionPrecedence(lhs_header) < MachO.getSectionPrecedence(rhs_header);
868 }
869 };
870
871 const gpa = macho_file.base.allocator;
872
873 var entries = try std.ArrayList(Entry).initCapacity(gpa, macho_file.sections.slice().len);
874 defer entries.deinit();
875
876 for (0..macho_file.sections.slice().len) |index| {
877 const section = macho_file.sections.get(index);
878 if (section.header.size == 0) {
879 log.debug("pruning section {s},{s} {?d}", .{
880 section.header.segName(),
881 section.header.sectName(),
882 section.first_atom_index,
883 });
884 for (&[_]*?u8{
885 &macho_file.text_section_index,
886 &macho_file.data_const_section_index,
887 &macho_file.data_section_index,
888 &macho_file.bss_section_index,
889 &macho_file.thread_vars_section_index,
890 &macho_file.thread_data_section_index,
891 &macho_file.thread_bss_section_index,
892 &macho_file.eh_frame_section_index,
893 &macho_file.unwind_info_section_index,
894 &macho_file.got_section_index,
895 &macho_file.tlv_ptr_section_index,
896 &macho_file.stubs_section_index,
897 &macho_file.stub_helper_section_index,
898 &macho_file.la_symbol_ptr_section_index,
899 }) |maybe_index| {
900 if (maybe_index.* != null and maybe_index.*.? == index) {
901 maybe_index.* = null;
902 }
903 }
904 continue;
905 }
906 entries.appendAssumeCapacity(.{ .index = @intCast(index) });
907 }
908
909 mem.sort(Entry, entries.items, macho_file, Entry.lessThan);
910
911 var slice = macho_file.sections.toOwnedSlice();
912 defer slice.deinit(gpa);
913
914 const backlinks = try gpa.alloc(u8, slice.len);
915 defer gpa.free(backlinks);
916 for (entries.items, 0..) |entry, i| {
917 backlinks[entry.index] = @as(u8, @intCast(i));
918 }
919
920 try macho_file.sections.ensureTotalCapacity(gpa, entries.items.len);
921 for (entries.items) |entry| {
922 macho_file.sections.appendAssumeCapacity(slice.get(entry.index));
923 }
924
925 for (&[_]*?u8{
926 &macho_file.text_section_index,
927 &macho_file.data_const_section_index,
928 &macho_file.data_section_index,
929 &macho_file.bss_section_index,
930 &macho_file.thread_vars_section_index,
931 &macho_file.thread_data_section_index,
932 &macho_file.thread_bss_section_index,
933 &macho_file.eh_frame_section_index,
934 &macho_file.unwind_info_section_index,
935 &macho_file.got_section_index,
936 &macho_file.tlv_ptr_section_index,
937 &macho_file.stubs_section_index,
938 &macho_file.stub_helper_section_index,
939 &macho_file.la_symbol_ptr_section_index,
940 }) |maybe_index| {
941 if (maybe_index.*) |*index| {
942 index.* = backlinks[index.*];
943 }
944 }
945}
946
947fn calcSectionSizes(macho_file: *MachO) !void {
948 const slice = macho_file.sections.slice();
949 for (slice.items(.header), 0..) |*header, sect_id| {
950 if (header.size == 0) continue;
951 if (macho_file.text_section_index) |txt| {
952 if (txt == sect_id and macho_file.requiresThunks()) continue;
953 }
954
955 var atom_index = slice.items(.first_atom_index)[sect_id] orelse continue;
956
957 header.size = 0;
958 header.@"align" = 0;
959
960 while (true) {
961 const atom = macho_file.getAtom(atom_index);
962 const atom_alignment = try math.powi(u32, 2, atom.alignment);
963 const atom_offset = mem.alignForward(u64, header.size, atom_alignment);
964 const padding = atom_offset - header.size;
965
966 const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
967 sym.n_value = atom_offset;
968
969 header.size += padding + atom.size;
970 header.@"align" = @max(header.@"align", atom.alignment);
971
972 if (atom.next_index) |next_index| {
973 atom_index = next_index;
974 } else break;
975 }
976 }
977
978 if (macho_file.text_section_index != null and macho_file.requiresThunks()) {
979 // Create jump/branch range extenders if needed.
980 try thunks.createThunks(macho_file, macho_file.text_section_index.?);
981 }
982
983 // Update offsets of all symbols contained within each Atom.
984 // We need to do this since our unwind info synthesiser relies on
985 // traversing the symbols when synthesising unwind info and DWARF CFI records.
986 for (slice.items(.first_atom_index)) |first_atom_index| {
987 var atom_index = first_atom_index orelse continue;
988
989 while (true) {
990 const atom = macho_file.getAtom(atom_index);
991 const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
992
993 if (atom.getFile() != null) {
994 // Update each symbol contained within the atom
995 var it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
996 while (it.next()) |sym_loc| {
997 const inner_sym = macho_file.getSymbolPtr(sym_loc);
998 inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(
999 macho_file,
1000 atom_index,
1001 sym_loc.sym_index,
1002 );
1003 }
1004
1005 // If there is a section alias, update it now too
1006 if (Atom.getSectionAlias(macho_file, atom_index)) |sym_loc| {
1007 const alias = macho_file.getSymbolPtr(sym_loc);
1008 alias.n_value = sym.n_value;
1009 }
1010 }
1011
1012 if (atom.next_index) |next_index| {
1013 atom_index = next_index;
1014 } else break;
1015 }
1016 }
1017
1018 if (macho_file.got_section_index) |sect_id| {
1019 const header = &macho_file.sections.items(.header)[sect_id];
1020 header.size = macho_file.got_table.count() * @sizeOf(u64);
1021 header.@"align" = 3;
1022 }
1023
1024 if (macho_file.tlv_ptr_section_index) |sect_id| {
1025 const header = &macho_file.sections.items(.header)[sect_id];
1026 header.size = macho_file.tlv_ptr_table.count() * @sizeOf(u64);
1027 header.@"align" = 3;
1028 }
1029
1030 const cpu_arch = macho_file.base.options.target.cpu.arch;
1031
1032 if (macho_file.stubs_section_index) |sect_id| {
1033 const header = &macho_file.sections.items(.header)[sect_id];
1034 header.size = macho_file.stub_table.count() * stubs.stubSize(cpu_arch);
1035 header.@"align" = stubs.stubAlignment(cpu_arch);
1036 }
1037
1038 if (macho_file.stub_helper_section_index) |sect_id| {
1039 const header = &macho_file.sections.items(.header)[sect_id];
1040 header.size = macho_file.stub_table.count() * stubs.stubHelperSize(cpu_arch) +
1041 stubs.stubHelperPreambleSize(cpu_arch);
1042 header.@"align" = stubs.stubAlignment(cpu_arch);
1043 }
1044
1045 if (macho_file.la_symbol_ptr_section_index) |sect_id| {
1046 const header = &macho_file.sections.items(.header)[sect_id];
1047 header.size = macho_file.stub_table.count() * @sizeOf(u64);
1048 header.@"align" = 3;
1049 }
1050}
1051
1052fn allocateSegments(macho_file: *MachO) !void {
1053 const gpa = macho_file.base.allocator;
1054 for (macho_file.segments.items, 0..) |*segment, segment_index| {
1055 const is_text_segment = mem.eql(u8, segment.segName(), "__TEXT");
1056 const base_size = if (is_text_segment) try load_commands.calcMinHeaderPad(gpa, macho_file.base.options, .{
1057 .segments = macho_file.segments.items,
1058 .dylibs = macho_file.dylibs.items,
1059 .referenced_dylibs = macho_file.referenced_dylibs.keys(),
1060 }) else 0;
1061 try allocateSegment(macho_file, @as(u8, @intCast(segment_index)), base_size);
1062 }
1063}
1064
1065fn getSegmentAllocBase(macho_file: *MachO, segment_index: u8) struct { vmaddr: u64, fileoff: u64 } {
1066 if (segment_index > 0) {
1067 const prev_segment = macho_file.segments.items[segment_index - 1];
1068 return .{
1069 .vmaddr = prev_segment.vmaddr + prev_segment.vmsize,
1070 .fileoff = prev_segment.fileoff + prev_segment.filesize,
1071 };
1072 }
1073 return .{ .vmaddr = 0, .fileoff = 0 };
1074}
1075
1076fn allocateSegment(macho_file: *MachO, segment_index: u8, init_size: u64) !void {
1077 const segment = &macho_file.segments.items[segment_index];
1078
1079 if (mem.eql(u8, segment.segName(), "__PAGEZERO")) return; // allocated upon creation
1080
1081 const base = getSegmentAllocBase(macho_file, segment_index);
1082 segment.vmaddr = base.vmaddr;
1083 segment.fileoff = base.fileoff;
1084 segment.filesize = init_size;
1085 segment.vmsize = init_size;
1086
1087 // Allocate the sections according to their alignment at the beginning of the segment.
1088 const indexes = macho_file.getSectionIndexes(segment_index);
1089 var start = init_size;
1090
1091 const slice = macho_file.sections.slice();
1092 for (slice.items(.header)[indexes.start..indexes.end], 0..) |*header, sect_id| {
1093 const alignment = try math.powi(u32, 2, header.@"align");
1094 const start_aligned = mem.alignForward(u64, start, alignment);
1095 const n_sect = @as(u8, @intCast(indexes.start + sect_id + 1));
1096
1097 header.offset = if (header.isZerofill())
1098 0
1099 else
1100 @as(u32, @intCast(segment.fileoff + start_aligned));
1101 header.addr = segment.vmaddr + start_aligned;
1102
1103 if (slice.items(.first_atom_index)[indexes.start + sect_id]) |first_atom_index| {
1104 var atom_index = first_atom_index;
1105
1106 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
1107 n_sect,
1108 header.segName(),
1109 header.sectName(),
1110 });
1111
1112 while (true) {
1113 const atom = macho_file.getAtom(atom_index);
1114 const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
1115 sym.n_value += header.addr;
1116 sym.n_sect = n_sect;
1117
1118 log.debug(" ATOM(%{d}, '{s}') @{x}", .{
1119 atom.sym_index,
1120 macho_file.getSymbolName(atom.getSymbolWithLoc()),
1121 sym.n_value,
1122 });
1123
1124 if (atom.getFile() != null) {
1125 // Update each symbol contained within the atom
1126 var it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
1127 while (it.next()) |sym_loc| {
1128 const inner_sym = macho_file.getSymbolPtr(sym_loc);
1129 inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(
1130 macho_file,
1131 atom_index,
1132 sym_loc.sym_index,
1133 );
1134 inner_sym.n_sect = n_sect;
1135 }
1136
1137 // If there is a section alias, update it now too
1138 if (Atom.getSectionAlias(macho_file, atom_index)) |sym_loc| {
1139 const alias = macho_file.getSymbolPtr(sym_loc);
1140 alias.n_value = sym.n_value;
1141 alias.n_sect = n_sect;
1142 }
1143 }
1144
1145 if (atom.next_index) |next_index| {
1146 atom_index = next_index;
1147 } else break;
1148 }
1149 }
1150
1151 start = start_aligned + header.size;
1152
1153 if (!header.isZerofill()) {
1154 segment.filesize = start;
1155 }
1156 segment.vmsize = start;
1157 }
1158
1159 const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch);
1160 segment.filesize = mem.alignForward(u64, segment.filesize, page_size);
1161 segment.vmsize = mem.alignForward(u64, segment.vmsize, page_size);
1162}
1163
1164const std = @import("std");
1165const build_options = @import("build_options");
1166const assert = std.debug.assert;
1167const dwarf = std.dwarf;
1168const fs = std.fs;
1169const log = std.log.scoped(.link);
1170const macho = std.macho;
1171const math = std.math;
1172const mem = std.mem;
1173
1174const aarch64 = @import("../../arch/aarch64/bits.zig");
1175const calcUuid = @import("uuid.zig").calcUuid;
1176const dead_strip = @import("dead_strip.zig");
1177const eh_frame = @import("eh_frame.zig");
1178const fat = @import("fat.zig");
1179const link = @import("../../link.zig");
1180const load_commands = @import("load_commands.zig");
1181const stubs = @import("stubs.zig");
1182const thunks = @import("thunks.zig");
1183const trace = @import("../../tracy.zig").trace;
1184
1185const Allocator = mem.Allocator;
1186const Archive = @import("Archive.zig");
1187const Atom = @import("Atom.zig");
1188const Cache = std.Build.Cache;
1189const CodeSignature = @import("CodeSignature.zig");
1190const Compilation = @import("../../Compilation.zig");
1191const Dylib = @import("Dylib.zig");
1192const MachO = @import("../MachO.zig");
1193const Md5 = std.crypto.hash.Md5;
1194const LibStub = @import("../tapi.zig").LibStub;
1195const Object = @import("Object.zig");
1196const Section = MachO.Section;
1197const StringTable = @import("../strtab.zig").StringTable;
1198const SymbolWithLoc = MachO.SymbolWithLoc;
1199const TableSection = @import("../table_section.zig").TableSection;
1200const Trie = @import("Trie.zig");
1201const UnwindInfo = @import("UnwindInfo.zig");