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...@@ -405,9 +405,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
405 const in_file = try std.fs.cwd().openFile(path, .{});405 const in_file = try std.fs.cwd().openFile(path, .{});
406 defer in_file.close();406 defer in_file.close();
407407
408 parseLibrary(408 self.parseLibrary(
409 self,
410 self.base.allocator,
411 in_file,409 in_file,
412 path,410 path,
413 lib,411 lib,
...@@ -421,24 +419,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -421,24 +419,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
421 };419 };
422 }420 }
423421
424 parseDependentLibs(self, self.base.allocator, &dependent_libs, &self.base.options) catch |err| {422 self.parseDependentLibs(&dependent_libs, &self.base.options) catch |err| {
425 // TODO convert to error423 // TODO convert to error
426 log.err("parsing dependent libraries failed with err {s}", .{@errorName(err)});424 log.err("parsing dependent libraries failed with err {s}", .{@errorName(err)});
427 };425 };
428 }426 }
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
439 var actions = std.ArrayList(ResolveAction).init(self.base.allocator);428 var actions = std.ArrayList(ResolveAction).init(self.base.allocator);
440 defer actions.deinit();429 defer actions.deinit();
441 try self.resolveSymbolsInDylibs(&actions);430 try self.resolveSymbols(&actions);
442431
443 if (self.getEntryPoint() == null) {432 if (self.getEntryPoint() == null) {
444 self.error_flags.no_entry_point_found = true;433 self.error_flags.no_entry_point_found = true;
...@@ -527,14 +516,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -527,14 +516,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
527516
528 try self.writeLinkeditSegmentData();517 try self.writeLinkeditSegmentData();
529518
530 const target = self.base.options.target;519 var codesig: ?CodeSignature = if (self.requiresCodeSignature()) blk: {
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: {
538 // Preallocate space for the code signature.520 // Preallocate space for the code signature.
539 // We need to do this at this stage so that we have the load commands with proper values521 // We need to do this at this stage so that we have the load commands with proper values
540 // written out to the file.522 // written out to the file.
...@@ -596,14 +578,14 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -596,14 +578,14 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
596578
597 try load_commands.writeLoadDylibLCs(self.dylibs.items, self.referenced_dylibs.keys(), lc_writer);579 try load_commands.writeLoadDylibLCs(self.dylibs.items, self.referenced_dylibs.keys(), lc_writer);
598580
599 if (requires_codesig) {581 if (codesig != null) {
600 try lc_writer.writeStruct(self.codesig_cmd);582 try lc_writer.writeStruct(self.codesig_cmd);
601 }583 }
602584
603 const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);585 const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);
604 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));586 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
605 try self.writeHeader(ncmds, @as(u32, @intCast(lc_buffer.items.len)));587 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
608 if (codesig) |*csig| {590 if (codesig) |*csig| {
609 try self.writeCodeSignature(comp, csig); // code signing always comes last591 try self.writeCodeSignature(comp, csig); // code signing always comes last
...@@ -729,8 +711,7 @@ fn resolveLib(...@@ -729,8 +711,7 @@ fn resolveLib(
729}711}
730712
731pub fn parsePositional(713pub fn parsePositional(
732 ctx: anytype,714 self: *MachO,
733 gpa: Allocator,
734 file: std.fs.File,715 file: std.fs.File,
735 path: []const u8,716 path: []const u8,
736 must_link: bool,717 must_link: bool,
...@@ -741,9 +722,9 @@ pub fn parsePositional(...@@ -741,9 +722,9 @@ pub fn parsePositional(
741 defer tracy.end();722 defer tracy.end();
742723
743 if (Object.isObject(file)) {724 if (Object.isObject(file)) {
744 try parseObject(ctx, gpa, file, path, link_options);725 try self.parseObject(file, path, link_options);
745 } else {726 } else {
746 try parseLibrary(ctx, gpa, file, path, .{727 try self.parseLibrary(file, path, .{
747 .path = null,728 .path = null,
748 .needed = false,729 .needed = false,
749 .weak = false,730 .weak = false,
...@@ -752,8 +733,7 @@ pub fn parsePositional(...@@ -752,8 +733,7 @@ pub fn parsePositional(
752}733}
753734
754fn parseObject(735fn parseObject(
755 ctx: anytype,736 self: *MachO,
756 gpa: Allocator,
757 file: std.fs.File,737 file: std.fs.File,
758 path: []const u8,738 path: []const u8,
759 link_options: *const link.Options,739 link_options: *const link.Options,
...@@ -761,6 +741,7 @@ fn parseObject(...@@ -761,6 +741,7 @@ fn parseObject(
761 const tracy = trace(@src());741 const tracy = trace(@src());
762 defer tracy.end();742 defer tracy.end();
763743
744 const gpa = self.base.allocator;
764 const mtime: u64 = mtime: {745 const mtime: u64 = mtime: {
765 const stat = file.stat() catch break :mtime 0;746 const stat = file.stat() catch break :mtime 0;
766 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));747 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
...@@ -776,7 +757,7 @@ fn parseObject(...@@ -776,7 +757,7 @@ fn parseObject(
776 };757 };
777 errdefer object.deinit(gpa);758 errdefer object.deinit(gpa);
778 try object.parse(gpa);759 try object.parse(gpa);
779 try ctx.objects.append(gpa, object);760 try self.objects.append(gpa, object);
780761
781 const cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) {762 const cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) {
782 macho.CPU_TYPE_ARM64 => .aarch64,763 macho.CPU_TYPE_ARM64 => .aarch64,
...@@ -796,8 +777,7 @@ fn parseObject(...@@ -796,8 +777,7 @@ fn parseObject(
796}777}
797778
798pub fn parseLibrary(779pub fn parseLibrary(
799 ctx: anytype,780 self: *MachO,
800 gpa: Allocator,
801 file: std.fs.File,781 file: std.fs.File,
802 path: []const u8,782 path: []const u8,
803 lib: link.SystemLib,783 lib: link.SystemLib,
...@@ -811,16 +791,16 @@ pub fn parseLibrary(...@@ -811,16 +791,16 @@ pub fn parseLibrary(
811 const cpu_arch = link_options.target.cpu.arch;791 const cpu_arch = link_options.target.cpu.arch;
812792
813 if (fat.isFatLibrary(file)) {793 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) {
815 error.MissingArch => return,795 error.MissingArch => return,
816 else => |e| return e,796 else => |e| return e,
817 };797 };
818 try file.seekTo(offset);798 try file.seekTo(offset);
819799
820 if (Archive.isArchive(file, offset)) {800 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);
822 } else if (Dylib.isDylib(file, offset)) {802 } 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, .{
824 .needed = lib.needed,804 .needed = lib.needed,
825 .weak = lib.weak,805 .weak = lib.weak,
826 });806 });
...@@ -830,14 +810,14 @@ pub fn parseLibrary(...@@ -830,14 +810,14 @@ pub fn parseLibrary(
830 return;810 return;
831 }811 }
832 } else if (Archive.isArchive(file, 0)) {812 } 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);
834 } else if (Dylib.isDylib(file, 0)) {814 } 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, .{
836 .needed = lib.needed,816 .needed = lib.needed,
837 .weak = lib.weak,817 .weak = lib.weak,
838 });818 });
839 } else {819 } else {
840 parseLibStub(ctx, gpa, file, path, dependent_libs, link_options, .{820 self.parseLibStub(file, path, dependent_libs, link_options, .{
841 .needed = lib.needed,821 .needed = lib.needed,
842 .weak = lib.weak,822 .weak = lib.weak,
843 }) catch |err| switch (err) {823 }) catch |err| switch (err) {
...@@ -852,12 +832,12 @@ pub fn parseLibrary(...@@ -852,12 +832,12 @@ pub fn parseLibrary(
852}832}
853833
854pub fn parseFatLibrary(834pub fn parseFatLibrary(
855 ctx: anytype,835 self: *MachO,
856 file: std.fs.File,836 file: std.fs.File,
857 path: []const u8,837 path: []const u8,
858 cpu_arch: std.Target.Cpu.Arch,838 cpu_arch: std.Target.Cpu.Arch,
859) !u64 {839) !u64 {
860 _ = ctx;840 _ = self;
861 var buffer: [2]fat.Arch = undefined;841 var buffer: [2]fat.Arch = undefined;
862 const fat_archs = try fat.parseArchs(file, &buffer);842 const fat_archs = try fat.parseArchs(file, &buffer);
863 const offset = for (fat_archs) |arch| {843 const offset = for (fat_archs) |arch| {
...@@ -871,13 +851,13 @@ pub fn parseFatLibrary(...@@ -871,13 +851,13 @@ pub fn parseFatLibrary(
871}851}
872852
873fn parseArchive(853fn parseArchive(
874 ctx: anytype,854 self: *MachO,
875 gpa: Allocator,
876 path: []const u8,855 path: []const u8,
877 fat_offset: u64,856 fat_offset: u64,
878 must_link: bool,857 must_link: bool,
879 cpu_arch: std.Target.Cpu.Arch,858 cpu_arch: std.Target.Cpu.Arch,
880) !void {859) !void {
860 const gpa = self.base.allocator;
881861
882 // We take ownership of the file so that we can store it for the duration of symbol resolution.862 // We take ownership of the file so that we can store it for the duration of symbol resolution.
883 // TODO we shouldn't need to do that and could pre-parse the archive like we do for zld/ELF?863 // 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(...@@ -929,10 +909,10 @@ fn parseArchive(
929 }909 }
930 for (offsets.keys()) |off| {910 for (offsets.keys()) |off| {
931 const object = try archive.parseObject(gpa, off);911 const object = try archive.parseObject(gpa, off);
932 try ctx.objects.append(gpa, object);912 try self.objects.append(gpa, object);
933 }913 }
934 } else {914 } else {
935 try ctx.archives.append(gpa, archive);915 try self.archives.append(gpa, archive);
936 }916 }
937}917}
938918
...@@ -944,8 +924,7 @@ const DylibOpts = struct {...@@ -944,8 +924,7 @@ const DylibOpts = struct {
944};924};
945925
946fn parseDylib(926fn parseDylib(
947 ctx: anytype,927 self: *MachO,
948 gpa: Allocator,
949 file: std.fs.File,928 file: std.fs.File,
950 path: []const u8,929 path: []const u8,
951 offset: u64,930 offset: u64,
...@@ -953,6 +932,7 @@ fn parseDylib(...@@ -953,6 +932,7 @@ fn parseDylib(
953 link_options: *const link.Options,932 link_options: *const link.Options,
954 dylib_options: DylibOpts,933 dylib_options: DylibOpts,
955) !void {934) !void {
935 const gpa = self.base.allocator;
956 const self_cpu_arch = link_options.target.cpu.arch;936 const self_cpu_arch = link_options.target.cpu.arch;
957937
958 const file_stat = try file.stat();938 const file_stat = try file.stat();
...@@ -968,7 +948,7 @@ fn parseDylib(...@@ -968,7 +948,7 @@ fn parseDylib(
968948
969 try dylib.parseFromBinary(949 try dylib.parseFromBinary(
970 gpa,950 gpa,
971 @intCast(ctx.dylibs.items.len), // TODO defer it till later951 @intCast(self.dylibs.items.len), // TODO defer it till later
972 dependent_libs,952 dependent_libs,
973 path,953 path,
974 contents,954 contents,
...@@ -991,7 +971,7 @@ fn parseDylib(...@@ -991,7 +971,7 @@ fn parseDylib(
991971
992 // TODO verify platform972 // TODO verify platform
993973
994 addDylib(ctx, gpa, dylib, link_options, .{974 self.addDylib(dylib, link_options, .{
995 .needed = dylib_options.needed,975 .needed = dylib_options.needed,
996 .weak = dylib_options.weak,976 .weak = dylib_options.weak,
997 }) catch |err| switch (err) {977 }) catch |err| switch (err) {
...@@ -1001,14 +981,14 @@ fn parseDylib(...@@ -1001,14 +981,14 @@ fn parseDylib(
1001}981}
1002982
1003fn parseLibStub(983fn parseLibStub(
1004 ctx: anytype,984 self: *MachO,
1005 gpa: Allocator,
1006 file: std.fs.File,985 file: std.fs.File,
1007 path: []const u8,986 path: []const u8,
1008 dependent_libs: anytype,987 dependent_libs: anytype,
1009 link_options: *const link.Options,988 link_options: *const link.Options,
1010 dylib_options: DylibOpts,989 dylib_options: DylibOpts,
1011) !void {990) !void {
991 const gpa = self.base.allocator;
1012 var lib_stub = try LibStub.loadFromFile(gpa, file);992 var lib_stub = try LibStub.loadFromFile(gpa, file);
1013 defer lib_stub.deinit();993 defer lib_stub.deinit();
1014994
...@@ -1023,12 +1003,12 @@ fn parseLibStub(...@@ -1023,12 +1003,12 @@ fn parseLibStub(
1023 gpa,1003 gpa,
1024 link_options.target,1004 link_options.target,
1025 lib_stub,1005 lib_stub,
1026 @intCast(ctx.dylibs.items.len), // TODO defer it till later1006 @intCast(self.dylibs.items.len), // TODO defer it till later
1027 dependent_libs,1007 dependent_libs,
1028 path,1008 path,
1029 );1009 );
10301010
1031 addDylib(ctx, gpa, dylib, link_options, .{1011 self.addDylib(dylib, link_options, .{
1032 .needed = dylib_options.needed,1012 .needed = dylib_options.needed,
1033 .weak = dylib_options.weak,1013 .weak = dylib_options.weak,
1034 }) catch |err| switch (err) {1014 }) catch |err| switch (err) {
...@@ -1038,8 +1018,7 @@ fn parseLibStub(...@@ -1038,8 +1018,7 @@ fn parseLibStub(
1038}1018}
10391019
1040fn addDylib(1020fn addDylib(
1041 ctx: anytype,1021 self: *MachO,
1042 gpa: Allocator,
1043 dylib: Dylib,1022 dylib: Dylib,
1044 link_options: *const link.Options,1023 link_options: *const link.Options,
1045 dylib_options: DylibOpts,1024 dylib_options: DylibOpts,
...@@ -1055,28 +1034,24 @@ fn addDylib(...@@ -1055,28 +1034,24 @@ fn addDylib(
1055 }1034 }
1056 }1035 }
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);
1059 if (gop.found_existing) return error.DylibAlreadyExists;1039 if (gop.found_existing) return error.DylibAlreadyExists;
10601040
1061 gop.value_ptr.* = @as(u16, @intCast(ctx.dylibs.items.len));1041 gop.value_ptr.* = @as(u16, @intCast(self.dylibs.items.len));
1062 try ctx.dylibs.append(gpa, dylib);1042 try self.dylibs.append(gpa, dylib);
10631043
1064 const should_link_dylib_even_if_unreachable = blk: {1044 const should_link_dylib_even_if_unreachable = blk: {
1065 if (link_options.dead_strip_dylibs and !dylib_options.needed) break :blk false;1045 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.*));
1067 };1047 };
10681048
1069 if (should_link_dylib_even_if_unreachable) {1049 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.*, {});
1071 }1051 }
1072}1052}
10731053
1074pub fn parseDependentLibs(1054pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype, link_options: *const link.Options) !void {
1075 ctx: anytype,
1076 gpa: Allocator,
1077 dependent_libs: anytype,
1078 link_options: *const link.Options,
1079) !void {
1080 const tracy = trace(@src());1055 const tracy = trace(@src());
1081 defer tracy.end();1056 defer tracy.end();
10821057
...@@ -1085,6 +1060,7 @@ pub fn parseDependentLibs(...@@ -1085,6 +1060,7 @@ pub fn parseDependentLibs(
1085 // 2) afterwards, we parse dependents of the included dylibs1060 // 2) afterwards, we parse dependents of the included dylibs
1086 // TODO this should not be performed if the user specifies `-flat_namespace` flag.1061 // TODO this should not be performed if the user specifies `-flat_namespace` flag.
1087 // See ld64 manpages.1062 // See ld64 manpages.
1063 const gpa = self.base.allocator;
1088 var arena_alloc = std.heap.ArenaAllocator.init(gpa);1064 var arena_alloc = std.heap.ArenaAllocator.init(gpa);
1089 const arena = arena_alloc.allocator();1065 const arena = arena_alloc.allocator();
1090 defer arena_alloc.deinit();1066 defer arena_alloc.deinit();
...@@ -1092,9 +1068,9 @@ pub fn parseDependentLibs(...@@ -1092,9 +1068,9 @@ pub fn parseDependentLibs(
1092 outer: while (dependent_libs.readItem()) |dep_id| {1068 outer: while (dependent_libs.readItem()) |dep_id| {
1093 defer dep_id.id.deinit(gpa);1069 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;
1098 const has_ext = blk: {1074 const has_ext = blk: {
1099 const basename = fs.path.basename(dep_id.id.name);1075 const basename = fs.path.basename(dep_id.id.name);
1100 break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;1076 break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;
...@@ -1121,7 +1097,7 @@ pub fn parseDependentLibs(...@@ -1121,7 +1097,7 @@ pub fn parseDependentLibs(
1121 log.debug("trying dependency at fully resolved path {s}", .{full_path});1097 log.debug("trying dependency at fully resolved path {s}", .{full_path});
11221098
1123 const offset: u64 = if (fat.isFatLibrary(file)) blk: {1099 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) {
1125 error.MissingArch => break,1101 error.MissingArch => break,
1126 else => |e| return e,1102 else => |e| return e,
1127 };1103 };
...@@ -1130,12 +1106,12 @@ pub fn parseDependentLibs(...@@ -1130,12 +1106,12 @@ pub fn parseDependentLibs(
1130 } else 0;1106 } else 0;
11311107
1132 if (Dylib.isDylib(file, offset)) {1108 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, .{
1134 .dependent = true,1110 .dependent = true,
1135 .weak = weak,1111 .weak = weak,
1136 });1112 });
1137 } else {1113 } else {
1138 parseLibStub(ctx, gpa, file, full_path, dependent_libs, link_options, .{1114 self.parseLibStub(file, full_path, dependent_libs, link_options, .{
1139 .dependent = true,1115 .dependent = true,
1140 .weak = weak,1116 .weak = weak,
1141 }) catch |err| switch (err) {1117 }) catch |err| switch (err) {
...@@ -1394,7 +1370,7 @@ fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void {...@@ -1394,7 +1370,7 @@ fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void {
1394 }1370 }
1395}1371}
13961372
1397pub fn allocateSpecialSymbols(self: anytype) !void {1373pub fn allocateSpecialSymbols(self: *MachO) !void {
1398 for (&[_][]const u8{1374 for (&[_][]const u8{
1399 "___dso_handle",1375 "___dso_handle",
1400 "__mh_execute_header",1376 "__mh_execute_header",
...@@ -1432,24 +1408,82 @@ pub fn createAtom(self: *MachO, sym_index: u32, opts: CreateAtomOpts) !Atom.Inde...@@ -1432,24 +1408,82 @@ pub fn createAtom(self: *MachO, sym_index: u32, opts: CreateAtomOpts) !Atom.Inde
1432 return index;1408 return index;
1433}1409}
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
1435fn createDyldPrivateAtom(self: *MachO) !void {1458fn createDyldPrivateAtom(self: *MachO) !void {
1436 if (self.dyld_private_atom_index != null) return;1459 if (self.dyld_private_atom_index != null) return;
14371460
1438 const sym_index = try self.allocateSymbol();1461 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 });
1440 try self.atom_by_index_table.putNoClobber(self.base.allocator, sym_index, atom_index);1466 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);
1444 const sym = atom.getSymbolPtr(self);1473 const sym = atom.getSymbolPtr(self);
1445 sym.n_type = macho.N_SECT;1474 sym.n_type = macho.N_SECT;
1446 sym.n_sect = self.data_section_index.? + 1;1475 sym.n_sect = self.data_section_index.? + 1;
1447 self.dyld_private_atom_index = atom_index;1476 self.dyld_private_atom_index = atom_index;
14481477
1449 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));1478 switch (self.mode) {
1450 log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});1479 .zld => self.addAtomToSection(atom_index),
1451 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);1480 .incremental => {
1452 try self.writeAtom(atom_index, &buffer);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 }
1453}1487}
14541488
1455fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: SymbolWithLoc) !Atom.Index {1489fn 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...@@ -1485,7 +1519,7 @@ fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: S
1485 return atom_index;1519 return atom_index;
1486}1520}
14871521
1488fn createMhExecuteHeaderSymbol(self: *MachO) !void {1522pub fn createMhExecuteHeaderSymbol(self: *MachO) !void {
1489 if (self.base.options.output_mode != .Exe) return;1523 if (self.base.options.output_mode != .Exe) return;
14901524
1491 const gpa = self.base.allocator;1525 const gpa = self.base.allocator;
...@@ -1501,10 +1535,17 @@ fn createMhExecuteHeaderSymbol(self: *MachO) !void {...@@ -1501,10 +1535,17 @@ fn createMhExecuteHeaderSymbol(self: *MachO) !void {
1501 };1535 };
15021536
1503 const gop = try self.getOrPutGlobalPtr("__mh_execute_header");1537 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 }
1504 gop.value_ptr.* = sym_loc;1545 gop.value_ptr.* = sym_loc;
1505}1546}
15061547
1507fn createDsoHandleSymbol(self: *MachO) !void {1548pub fn createDsoHandleSymbol(self: *MachO) !void {
1508 const global = self.getGlobalPtr("___dso_handle") orelse return;1549 const global = self.getGlobalPtr("___dso_handle") orelse return;
1509 if (!self.getSymbol(global.*).undf()) return;1550 if (!self.getSymbol(global.*).undf()) return;
15101551
...@@ -1519,10 +1560,51 @@ fn createDsoHandleSymbol(self: *MachO) !void {...@@ -1519,10 +1560,51 @@ fn createDsoHandleSymbol(self: *MachO) !void {
1519 .n_desc = macho.N_WEAK_DEF,1560 .n_desc = macho.N_WEAK_DEF,
1520 .n_value = 0,1561 .n_value = 0,
1521 };1562 };
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 }
1522 global.* = sym_loc;1568 global.* = sym_loc;
1523 _ = self.unresolved.swapRemove(self.getGlobalIndex("___dso_handle").?);1569 _ = self.unresolved.swapRemove(self.getGlobalIndex("___dso_handle").?);
1524}1570}
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
1526fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {1608fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
1527 const gpa = self.base.allocator;1609 const gpa = self.base.allocator;
1528 const sym = self.getSymbol(current);1610 const sym = self.getSymbol(current);
...@@ -1536,6 +1618,7 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {...@@ -1536,6 +1618,7 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
1536 }1618 }
1537 return;1619 return;
1538 }1620 }
1621 const global_index = self.getGlobalIndex(sym_name).?;
1539 const global = gop.value_ptr.*;1622 const global = gop.value_ptr.*;
1540 const global_sym = self.getSymbol(global);1623 const global_sym = self.getSymbol(global);
15411624
...@@ -1566,7 +1649,22 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {...@@ -1566,7 +1649,22 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
1566 const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext());1649 const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext());
1567 const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext());1650 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
1570 if (global_is_strong) return;1668 if (global_is_strong) return;
1571 if (sym_is_weak and global_is_weak) return;1669 if (sym_is_weak and global_is_weak) return;
1572 if (sym.tentative() and global_sym.tentative()) {1670 if (sym.tentative() and global_sym.tentative()) {
...@@ -1574,11 +1672,88 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {...@@ -1574,11 +1672,88 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
1574 }1672 }
1575 if (sym.undf() and !sym.tentative()) return;1673 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
1579 gop.value_ptr.* = current;1681 gop.value_ptr.* = current;
1580}1682}
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
1582fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction)) !void {1757fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction)) !void {
1583 if (self.dylibs.items.len == 0) return;1758 if (self.dylibs.items.len == 0) return;
15841759
...@@ -1608,6 +1783,7 @@ fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction))...@@ -1608,6 +1783,7 @@ fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction))
16081783
1609 if (self.unresolved.fetchSwapRemove(global_index)) |entry| blk: {1784 if (self.unresolved.fetchSwapRemove(global_index)) |entry| blk: {
1610 if (!sym.undf()) break :blk;1785 if (!sym.undf()) break :blk;
1786 if (self.mode == .zld) break :blk;
1611 try actions.append(.{ .kind = entry.value, .target = global });1787 try actions.append(.{ .kind = entry.value, .target = global });
1612 }1788 }
16131789
...@@ -1618,6 +1794,42 @@ fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction))...@@ -1618,6 +1794,42 @@ fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction))
1618 }1794 }
1619}1795}
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
1621pub fn deinit(self: *MachO) void {1833pub fn deinit(self: *MachO) void {
1622 const gpa = self.base.allocator;1834 const gpa = self.base.allocator;
16231835
...@@ -1638,7 +1850,6 @@ pub fn deinit(self: *MachO) void {...@@ -1638,7 +1850,6 @@ pub fn deinit(self: *MachO) void {
1638 self.thunks.deinit(gpa);1850 self.thunks.deinit(gpa);
16391851
1640 self.strtab.deinit(gpa);1852 self.strtab.deinit(gpa);
1641
1642 self.locals.deinit(gpa);1853 self.locals.deinit(gpa);
1643 self.globals.deinit(gpa);1854 self.globals.deinit(gpa);
1644 self.locals_free_list.deinit(gpa);1855 self.locals_free_list.deinit(gpa);
...@@ -1653,6 +1864,14 @@ pub fn deinit(self: *MachO) void {...@@ -1653,6 +1864,14 @@ pub fn deinit(self: *MachO) void {
1653 self.resolver.deinit(gpa);1864 self.resolver.deinit(gpa);
1654 }1865 }
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);
1656 for (self.dylibs.items) |*dylib| {1875 for (self.dylibs.items) |*dylib| {
1657 dylib.deinit(gpa);1876 dylib.deinit(gpa);
1658 }1877 }
...@@ -1842,20 +2061,55 @@ fn allocateGlobal(self: *MachO) !u32 {...@@ -1842,20 +2061,55 @@ fn allocateGlobal(self: *MachO) !u32 {
1842 return index;2061 return index;
1843}2062}
18442063
1845fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void {2064pub fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void {
1846 if (self.got_table.lookup.contains(target)) return;2065 if (self.got_table.lookup.contains(target)) return;
1847 const got_index = try self.got_table.allocateEntry(self.base.allocator, target);2066 const got_index = try self.got_table.allocateEntry(self.base.allocator, target);
1848 try self.writeOffsetTableEntry(got_index);2067 if (self.got_section_index == null) {
1849 self.got_table_count_dirty = true;2068 self.got_section_index = try self.initSection("__DATA_CONST", "__got", .{
1850 self.markRelocsDirtyByTarget(target);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 }
1851}2077}
18522078
1853fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void {2079pub fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void {
1854 if (self.stub_table.lookup.contains(target)) return;2080 if (self.stub_table.lookup.contains(target)) return;
1855 const stub_index = try self.stub_table.allocateEntry(self.base.allocator, target);2081 const stub_index = try self.stub_table.allocateEntry(self.base.allocator, target);
1856 try self.writeStubTableEntry(stub_index);2082 if (self.stubs_section_index == null) {
1857 self.stub_table_count_dirty = true;2083 self.stubs_section_index = try self.initSection("__TEXT", "__stubs", .{
1858 self.markRelocsDirtyByTarget(target);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 }
1859}2113}
18602114
1861pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {2115pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
...@@ -2758,16 +3012,10 @@ const InitSectionOpts = struct {...@@ -2758,16 +3012,10 @@ const InitSectionOpts = struct {
2758 reserved2: u32 = 0,3012 reserved2: u32 = 0,
2759};3013};
27603014
2761pub fn initSection(3015pub fn initSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: InitSectionOpts) !u8 {
2762 gpa: Allocator,
2763 ctx: anytype,
2764 segname: []const u8,
2765 sectname: []const u8,
2766 opts: InitSectionOpts,
2767) !u8 {
2768 log.debug("creating section '{s},{s}'", .{ segname, sectname });3016 log.debug("creating section '{s},{s}'", .{ segname, sectname });
2769 const index = @as(u8, @intCast(ctx.sections.slice().len));3017 const index = @as(u8, @intCast(self.sections.slice().len));
2770 try ctx.sections.append(gpa, .{3018 try self.sections.append(self.base.allocator, .{
2771 .segment_index = undefined, // Segments will be created automatically later down the pipeline3019 .segment_index = undefined, // Segments will be created automatically later down the pipeline
2772 .header = .{3020 .header = .{
2773 .sectname = makeStaticString(sectname),3021 .sectname = makeStaticString(sectname),
...@@ -2822,7 +3070,7 @@ fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts...@@ -2822,7 +3070,7 @@ fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts
2822 .cmdsize = @sizeOf(macho.segment_command_64) + @sizeOf(macho.section_64),3070 .cmdsize = @sizeOf(macho.segment_command_64) + @sizeOf(macho.section_64),
2823 };3071 };
28243072
2825 const sect_id = try initSection(gpa, self, sectname, segname, .{3073 const sect_id = try self.initSection(sectname, segname, .{
2826 .flags = opts.flags,3074 .flags = opts.flags,
2827 .reserved2 = opts.reserved2,3075 .reserved2 = opts.reserved2,
2828 });3076 });
...@@ -2918,10 +3166,29 @@ fn growSectionVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void {...@@ -2918,10 +3166,29 @@ fn growSectionVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void {
2918 }3166 }
2919}3167}
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
2921fn allocateAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: u64) !u64 {3186fn allocateAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: u64) !u64 {
2922 const tracy = trace(@src());3187 const tracy = trace(@src());
2923 defer tracy.end();3188 defer tracy.end();
29243189
3190 assert(self.mode == .incremental);
3191
2925 const atom = self.getAtom(atom_index);3192 const atom = self.getAtom(atom_index);
2926 const sect_id = atom.getSymbol(self).n_sect - 1;3193 const sect_id = atom.getSymbol(self).n_sect - 1;
2927 const segment = self.getSegmentPtr(sect_id);3194 const segment = self.getSegmentPtr(sect_id);
...@@ -3048,7 +3315,7 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u...@@ -3048,7 +3315,7 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u
3048 return self.addUndefined(sym_name, .add_stub);3315 return self.addUndefined(sym_name, .add_stub);
3049}3316}
30503317
3051pub fn writeSegmentHeaders(self: anytype, writer: anytype) !void {3318pub fn writeSegmentHeaders(self: *MachO, writer: anytype) !void {
3052 for (self.segments.items, 0..) |seg, i| {3319 for (self.segments.items, 0..) |seg, i| {
3053 const indexes = self.getSectionIndexes(@as(u8, @intCast(i)));3320 const indexes = self.getSectionIndexes(@as(u8, @intCast(i)));
3054 var out_seg = seg;3321 var out_seg = seg;
...@@ -3075,7 +3342,7 @@ pub fn writeSegmentHeaders(self: anytype, writer: anytype) !void {...@@ -3075,7 +3342,7 @@ pub fn writeSegmentHeaders(self: anytype, writer: anytype) !void {
3075 }3342 }
3076}3343}
30773344
3078fn writeLinkeditSegmentData(self: *MachO) !void {3345pub fn writeLinkeditSegmentData(self: *MachO) !void {
3079 const page_size = getPageSize(self.base.options.target.cpu.arch);3346 const page_size = getPageSize(self.base.options.target.cpu.arch);
3080 const seg = self.getLinkeditSegmentPtr();3347 const seg = self.getLinkeditSegmentPtr();
3081 seg.filesize = 0;3348 seg.filesize = 0;
...@@ -3092,29 +3359,29 @@ fn writeLinkeditSegmentData(self: *MachO) !void {...@@ -3092,29 +3359,29 @@ fn writeLinkeditSegmentData(self: *MachO) !void {
3092 }3359 }
30933360
3094 try self.writeDyldInfoData();3361 try self.writeDyldInfoData();
3362 // TODO handle this better
3363 if (self.mode == .zld) {
3364 try self.writeFunctionStarts();
3365 try self.writeDataInCode();
3366 }
3095 try self.writeSymtabs();3367 try self.writeSymtabs();
30963368
3097 seg.vmsize = mem.alignForward(u64, seg.filesize, page_size);3369 seg.vmsize = mem.alignForward(u64, seg.filesize, page_size);
3098}3370}
30993371
3100pub fn collectRebaseDataFromTableSection(3372fn collectRebaseDataFromTableSection(self: *MachO, sect_id: u8, rebase: *Rebase, table: anytype) !void {
3101 gpa: Allocator,3373 const gpa = self.base.allocator;
3102 ctx: anytype,3374 const header = self.sections.items(.header)[sect_id];
3103 sect_id: u8,3375 const segment_index = self.sections.items(.segment_index)[sect_id];
3104 rebase: *Rebase,3376 const segment = self.segments.items[segment_index];
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];
3110 const base_offset = header.addr - segment.vmaddr;3377 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
3113 try rebase.entries.ensureUnusedCapacity(gpa, table.entries.items.len);3380 try rebase.entries.ensureUnusedCapacity(gpa, table.entries.items.len);
31143381
3115 for (table.entries.items, 0..) |entry, i| {3382 for (table.entries.items, 0..) |entry, i| {
3116 if (!table.lookup.contains(entry)) continue;3383 if (!table.lookup.contains(entry)) continue;
3117 const sym = ctx.getSymbol(entry);3384 const sym = self.getSymbol(entry);
3118 if (is_got and sym.undf()) continue;3385 if (is_got and sym.undf()) continue;
3119 const offset = i * @sizeOf(u64);3386 const offset = i * @sizeOf(u64);
3120 log.debug(" | rebase at {x}", .{base_offset + offset});3387 log.debug(" | rebase at {x}", .{base_offset + offset});
...@@ -3152,34 +3419,105 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {...@@ -3152,34 +3419,105 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
3152 }3419 }
3153 }3420 }
31543421
3155 try collectRebaseDataFromTableSection(gpa, self, self.got_section_index.?, rebase, self.got_table);3422 // Unpack GOT entries
3156 try collectRebaseDataFromTableSection(gpa, self, self.la_symbol_ptr_section_index.?, rebase, self.stub_table);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
3158 try rebase.finalize(gpa);3501 try rebase.finalize(gpa);
3159}3502}
31603503
3161pub fn collectBindDataFromTableSection(3504fn collectBindDataFromTableSection(self: *MachO, sect_id: u8, bind: anytype, table: anytype) !void {
3162 gpa: Allocator,3505 const gpa = self.base.allocator;
3163 ctx: anytype,3506 const header = self.sections.items(.header)[sect_id];
3164 sect_id: u8,3507 const segment_index = self.sections.items(.segment_index)[sect_id];
3165 bind: anytype,3508 const segment = self.segments.items[segment_index];
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];
3171 const base_offset = header.addr - segment.vmaddr;3509 const base_offset = header.addr - segment.vmaddr;
31723510
3173 try bind.entries.ensureUnusedCapacity(gpa, table.entries.items.len);3511 try bind.entries.ensureUnusedCapacity(gpa, table.entries.items.len);
31743512
3175 for (table.entries.items, 0..) |entry, i| {3513 for (table.entries.items, 0..) |entry, i| {
3176 if (!table.lookup.contains(entry)) continue;3514 if (!table.lookup.contains(entry)) continue;
3177 const bind_sym = ctx.getSymbol(entry);3515 const bind_sym = self.getSymbol(entry);
3178 if (!bind_sym.undf()) continue;3516 if (!bind_sym.undf()) continue;
3179 const offset = i * @sizeOf(u64);3517 const offset = i * @sizeOf(u64);
3180 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{3518 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
3181 base_offset + offset,3519 base_offset + offset,
3182 ctx.getSymbolName(entry),3520 self.getSymbolName(entry),
3183 @divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER),3521 @divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER),
3184 });3522 });
3185 if (bind_sym.weakRef()) {3523 if (bind_sym.weakRef()) {
...@@ -3235,13 +3573,105 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {...@@ -3235,13 +3573,105 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {
3235 }3573 }
3236 }3574 }
32373575
3238 // Gather GOT pointers3576 // Unpack GOT pointers
3239 try collectBindDataFromTableSection(gpa, self, self.got_section_index.?, bind, self.got_table);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
3240 try bind.finalize(gpa, self);3669 try bind.finalize(gpa, self);
3241}3670}
32423671
3243fn collectLazyBindData(self: *MachO, bind: anytype) !void {3672fn 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);
3245 try bind.finalize(self.base.allocator, self);3675 try bind.finalize(self.base.allocator, self);
3246}3676}
32473677
...@@ -3259,6 +3689,7 @@ fn collectExportData(self: *MachO, trie: *Trie) !void {...@@ -3259,6 +3689,7 @@ fn collectExportData(self: *MachO, trie: *Trie) !void {
32593689
3260 if (sym.undf()) continue;3690 if (sym.undf()) continue;
3261 assert(sym.ext());3691 assert(sym.ext());
3692 if (sym.n_desc == N_DEAD) continue;
32623693
3263 const sym_name = self.getSymbolName(global);3694 const sym_name = self.getSymbolName(global);
3264 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });3695 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
...@@ -3349,12 +3780,7 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -3349,12 +3780,7 @@ fn writeDyldInfoData(self: *MachO) !void {
3349 });3780 });
33503781
3351 try self.base.file.?.pwriteAll(buffer, rebase_off);3782 try self.base.file.?.pwriteAll(buffer, rebase_off);
3352 try populateLazyBindOffsetsInStubHelper(3783 try self.populateLazyBindOffsetsInStubHelper(lazy_bind);
3353 self,
3354 self.base.options.target.cpu.arch,
3355 self.base.file.?,
3356 lazy_bind,
3357 );
33583784
3359 self.dyld_info_cmd.rebase_off = @as(u32, @intCast(rebase_off));3785 self.dyld_info_cmd.rebase_off = @as(u32, @intCast(rebase_off));
3360 self.dyld_info_cmd.rebase_size = @as(u32, @intCast(rebase_size_aligned));3786 self.dyld_info_cmd.rebase_size = @as(u32, @intCast(rebase_size_aligned));
...@@ -3366,19 +3792,15 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -3366,19 +3792,15 @@ fn writeDyldInfoData(self: *MachO) !void {
3366 self.dyld_info_cmd.export_size = @as(u32, @intCast(export_size_aligned));3792 self.dyld_info_cmd.export_size = @as(u32, @intCast(export_size_aligned));
3367}3793}
33683794
3369pub fn populateLazyBindOffsetsInStubHelper(3795fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: anytype) !void {
3370 ctx: anytype,
3371 cpu_arch: std.Target.Cpu.Arch,
3372 file: fs.File,
3373 lazy_bind: anytype,
3374) !void {
3375 if (lazy_bind.size() == 0) return;3796 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.?;
3378 // assert(ctx.stub_helper_preamble_allocated);3799 // 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;
3382 const preamble_size = stubs.stubHelperPreambleSize(cpu_arch);3804 const preamble_size = stubs.stubHelperPreambleSize(cpu_arch);
3383 const stub_size = stubs.stubHelperSize(cpu_arch);3805 const stub_size = stubs.stubHelperSize(cpu_arch);
3384 const stub_offset = stubs.stubOffsetInStubHelper(cpu_arch);3806 const stub_offset = stubs.stubOffsetInStubHelper(cpu_arch);
...@@ -3389,14 +3811,175 @@ pub fn populateLazyBindOffsetsInStubHelper(...@@ -3389,14 +3811,175 @@ pub fn populateLazyBindOffsetsInStubHelper(
33893811
3390 log.debug("writing lazy bind offset 0x{x} ({s}) in stub helper at 0x{x}", .{3812 log.debug("writing lazy bind offset 0x{x} ({s}) in stub helper at 0x{x}", .{
3391 bind_offset,3813 bind_offset,
3392 ctx.getSymbolName(lazy_bind.entries.items[index].target),3814 self.getSymbolName(lazy_bind.entries.items[index].target),
3393 file_offset,3815 file_offset,
3394 });3816 });
33953817
3396 try file.pwriteAll(mem.asBytes(&bind_offset), file_offset);3818 try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset);
3397 }3819 }
3398}3820}
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
3400fn writeSymtabs(self: *MachO) !void {3983fn writeSymtabs(self: *MachO) !void {
3401 var ctx = try self.writeSymtab();3984 var ctx = try self.writeSymtab();
3402 defer ctx.imports_table.deinit();3985 defer ctx.imports_table.deinit();
...@@ -3404,18 +3987,38 @@ fn writeSymtabs(self: *MachO) !void {...@@ -3404,18 +3987,38 @@ fn writeSymtabs(self: *MachO) !void {
3404 try self.writeStrtab();3987 try self.writeStrtab();
3405}3988}
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
3407fn writeSymtab(self: *MachO) !SymtabCtx {4001fn writeSymtab(self: *MachO) !SymtabCtx {
3408 const gpa = self.base.allocator;4002 const gpa = self.base.allocator;
34094003
3410 var locals = std.ArrayList(macho.nlist_64).init(gpa);4004 var locals = std.ArrayList(macho.nlist_64).init(gpa);
3411 defer locals.deinit();4005 defer locals.deinit();
34124006
3413 for (self.locals.items, 0..) |sym, sym_id| {4007 for (0..self.locals.items) |sym_id| {
3414 if (sym.n_strx == 0) continue; // no name, skip4008 try self.addLocalToSymtab(.{ .sym_index = @intCast(sym_id) });
3415 const sym_loc = SymbolWithLoc{ .sym_index = @as(u32, @intCast(sym_id)) };4009 }
3416 if (self.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip4010
3417 if (self.getGlobal(self.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip4011 for (self.objects.items) |object| {
3418 try locals.append(sym);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 }
3419 }4022 }
34204023
3421 var exports = std.ArrayList(macho.nlist_64).init(gpa);4024 var exports = std.ArrayList(macho.nlist_64).init(gpa);
...@@ -3424,6 +4027,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {...@@ -3424,6 +4027,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
3424 for (self.globals.items) |global| {4027 for (self.globals.items) |global| {
3425 const sym = self.getSymbol(global);4028 const sym = self.getSymbol(global);
3426 if (sym.undf()) continue; // import, skip4029 if (sym.undf()) continue; // import, skip
4030 if (sym.n_desc == N_DEAD) continue;
3427 var out_sym = sym;4031 var out_sym = sym;
3428 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));4032 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
3429 try exports.append(out_sym);4033 try exports.append(out_sym);
...@@ -3438,6 +4042,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {...@@ -3438,6 +4042,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
3438 const sym = self.getSymbol(global);4042 const sym = self.getSymbol(global);
3439 if (sym.n_strx == 0) continue; // no name, skip4043 if (sym.n_strx == 0) continue; // no name, skip
3440 if (!sym.undf()) continue; // not an import, skip4044 if (!sym.undf()) continue; // not an import, skip
4045 if (sym.n_desc == N_DEAD) continue;
3441 const new_index = @as(u32, @intCast(imports.items.len));4046 const new_index = @as(u32, @intCast(imports.items.len));
3442 var out_sym = sym;4047 var out_sym = sym;
3443 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));4048 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
...@@ -3445,6 +4050,15 @@ fn writeSymtab(self: *MachO) !SymtabCtx {...@@ -3445,6 +4050,15 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
3445 try imports_table.putNoClobber(global, new_index);4050 try imports_table.putNoClobber(global, new_index);
3446 }4051 }
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
3448 const nlocals = @as(u32, @intCast(locals.items.len));4062 const nlocals = @as(u32, @intCast(locals.items.len));
3449 const nexports = @as(u32, @intCast(exports.items.len));4063 const nexports = @as(u32, @intCast(exports.items.len));
3450 const nimports = @as(u32, @intCast(imports.items.len));4064 const nimports = @as(u32, @intCast(imports.items.len));
...@@ -3478,7 +4092,218 @@ fn writeSymtab(self: *MachO) !SymtabCtx {...@@ -3478,7 +4092,218 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
3478 };4092 };
3479}4093}
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 {
3482 const gpa = self.base.allocator;4307 const gpa = self.base.allocator;
3483 const seg = self.getLinkeditSegmentPtr();4308 const seg = self.getLinkeditSegmentPtr();
3484 const offset = seg.fileoff + seg.filesize;4309 const offset = seg.fileoff + seg.filesize;
...@@ -3507,7 +4332,7 @@ const SymtabCtx = struct {...@@ -3507,7 +4332,7 @@ const SymtabCtx = struct {
3507 imports_table: std.AutoHashMap(SymbolWithLoc, u32),4332 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
3508};4333};
35094334
3510fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {4335pub fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
3511 const gpa = self.base.allocator;4336 const gpa = self.base.allocator;
3512 const nstubs = @as(u32, @intCast(self.stub_table.lookup.count()));4337 const nstubs = @as(u32, @intCast(self.stub_table.lookup.count()));
3513 const ngot_entries = @as(u32, @intCast(self.got_table.lookup.count()));4338 const ngot_entries = @as(u32, @intCast(self.got_table.lookup.count()));
...@@ -3582,7 +4407,7 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {...@@ -3582,7 +4407,7 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
3582 self.dysymtab_cmd.nindirectsyms = nindirectsyms;4407 self.dysymtab_cmd.nindirectsyms = nindirectsyms;
3583}4408}
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 {
3586 const file_size = if (!has_codesig) blk: {4411 const file_size = if (!has_codesig) blk: {
3587 const seg = self.getLinkeditSegmentPtr();4412 const seg = self.getLinkeditSegmentPtr();
3588 break :blk seg.fileoff + seg.filesize;4413 break :blk seg.fileoff + seg.filesize;
...@@ -3592,7 +4417,7 @@ fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, has_c...@@ -3592,7 +4417,7 @@ fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, has_c
3592 try self.base.file.?.pwriteAll(&self.uuid_cmd.uuid, offset);4417 try self.base.file.?.pwriteAll(&self.uuid_cmd.uuid, offset);
3593}4418}
35944419
3595fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {4420pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
3596 const seg = self.getLinkeditSegmentPtr();4421 const seg = self.getLinkeditSegmentPtr();
3597 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file4422 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
3598 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L2714423 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
...@@ -3609,8 +4434,9 @@ fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {...@@ -3609,8 +4434,9 @@ fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
3609 self.codesig_cmd.datasize = @as(u32, @intCast(needed_size));4434 self.codesig_cmd.datasize = @as(u32, @intCast(needed_size));
3610}4435}
36114436
3612fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSignature) !void {4437pub fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSignature) !void {
3613 const seg = self.getSegment(self.text_section_index.?);4438 const seg_id = self.header_segment_cmd_index.?;
4439 const seg = self.segments.items[seg_id];
3614 const offset = self.codesig_cmd.dataoff;4440 const offset = self.codesig_cmd.dataoff;
36154441
3616 var buffer = std.ArrayList(u8).init(self.base.allocator);4442 var buffer = std.ArrayList(u8).init(self.base.allocator);
...@@ -3634,14 +4460,10 @@ fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSig...@@ -3634,14 +4460,10 @@ fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSig
3634}4460}
36354461
3636/// Writes Mach-O file header.4462/// Writes Mach-O file header.
3637fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {4463pub fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
3638 var header: macho.mach_header_64 = .{};4464 var header: macho.mach_header_64 = .{};
3639 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;4465 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
3645 switch (self.base.options.target.cpu.arch) {4467 switch (self.base.options.target.cpu.arch) {
3646 .aarch64 => {4468 .aarch64 => {
3647 header.cputype = macho.CPU_TYPE_ARM64;4469 header.cputype = macho.CPU_TYPE_ARM64;
...@@ -3666,6 +4488,13 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {...@@ -3666,6 +4488,13 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
3666 else => unreachable,4488 else => unreachable,
3667 }4489 }
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
3669 header.ncmds = ncmds;4498 header.ncmds = ncmds;
3670 header.sizeofcmds = sizeofcmds;4499 header.sizeofcmds = sizeofcmds;
36714500
...@@ -3830,20 +4659,33 @@ pub fn symbolIsTemp(self: *MachO, sym_with_loc: SymbolWithLoc) bool {...@@ -3830,20 +4659,33 @@ pub fn symbolIsTemp(self: *MachO, sym_with_loc: SymbolWithLoc) bool {
38304659
3831/// Returns pointer-to-symbol described by `sym_with_loc` descriptor.4660/// Returns pointer-to-symbol described by `sym_with_loc` descriptor.
3832pub fn getSymbolPtr(self: *MachO, sym_with_loc: SymbolWithLoc) *macho.nlist_64 {4661pub fn getSymbolPtr(self: *MachO, sym_with_loc: SymbolWithLoc) *macho.nlist_64 {
3833 assert(sym_with_loc.getFile() == null);4662 if (sym_with_loc.getFile()) |file| {
3834 return &self.locals.items[sym_with_loc.sym_index];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 }
3835}4668}
38364669
3837/// Returns symbol described by `sym_with_loc` descriptor.4670/// Returns symbol described by `sym_with_loc` descriptor.
3838pub fn getSymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) macho.nlist_64 {4671pub fn getSymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
3839 assert(sym_with_loc.getFile() == null);4672 if (sym_with_loc.getFile()) |file| {
3840 return self.locals.items[sym_with_loc.sym_index];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 }
3841}4678}
38424679
3843/// Returns name of the symbol described by `sym_with_loc` descriptor.4680/// Returns name of the symbol described by `sym_with_loc` descriptor.
3844pub fn getSymbolName(self: *const MachO, sym_with_loc: SymbolWithLoc) []const u8 {4681pub fn getSymbolName(self: *const MachO, sym_with_loc: SymbolWithLoc) []const u8 {
3845 const sym = self.getSymbol(sym_with_loc);4682 if (sym_with_loc.getFile()) |file| {
3846 return self.strtab.get(sym.n_strx).?;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 }
3847}4689}
38484690
3849/// Returns pointer to the global entry for `name` if one exists.4691/// 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 {...@@ -3945,6 +4787,19 @@ pub inline fn getPageSize(cpu_arch: std.Target.Cpu.Arch) u16 {
3945 };4787 };
3946}4788}
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
3948pub fn getSegmentPrecedence(segname: []const u8) u4 {4803pub fn getSegmentPrecedence(segname: []const u8) u4 {
3949 if (mem.eql(u8, segname, "__PAGEZERO")) return 0x0;4804 if (mem.eql(u8, segname, "__PAGEZERO")) return 0x0;
3950 if (mem.eql(u8, segname, "__TEXT")) return 0x1;4805 if (mem.eql(u8, segname, "__TEXT")) return 0x1;
...@@ -3988,24 +4843,26 @@ pub fn getSectionPrecedence(header: macho.section_64) u8 {...@@ -3988,24 +4843,26 @@ pub fn getSectionPrecedence(header: macho.section_64) u8 {
3988 return (@as(u8, @intCast(segment_precedence)) << 4) + section_precedence;4843 return (@as(u8, @intCast(segment_precedence)) << 4) + section_precedence;
3989}4844}
39904845
3991pub fn reportUndefined(self: *MachO, ctx: anytype) !void {4846pub fn reportUndefined(self: *MachO) !void {
3992 const count = ctx.unresolved.count();4847 const count = self.unresolved.count();
3993 if (count == 0) return;4848 if (count == 0) return;
39944849
3995 const gpa = self.base.allocator;4850 const gpa = self.base.allocator;
39964851
3997 try self.misc_errors.ensureUnusedCapacity(gpa, count);4852 try self.misc_errors.ensureUnusedCapacity(gpa, count);
39984853
3999 for (ctx.unresolved.keys()) |global_index| {4854 for (self.unresolved.keys()) |global_index| {
4000 const global = ctx.globals.items[global_index];4855 const global = self.globals.items[global_index];
4001 const sym_name = ctx.getSymbolName(global);4856 const sym_name = self.getSymbolName(global);
40024857
4003 const nnotes: usize = if (global.getFile() == null) @as(usize, 0) else 1;4858 const nnotes: usize = if (global.getFile() == null) @as(usize, 0) else 1;
4004 var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, nnotes);4859 var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, nnotes);
4005 defer notes.deinit();4860 defer notes.deinit();
40064861
4007 if (global.getFile()) |file| {4862 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 });
4009 notes.appendAssumeCapacity(.{ .msg = note });4866 notes.appendAssumeCapacity(.{ .msg = note });
4010 }4867 }
40114868
...@@ -4051,6 +4908,19 @@ pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytyp...@@ -4051,6 +4908,19 @@ pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytyp
4051 return i;4908 return i;
4052}4909}
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
4054pub fn logSections(self: *MachO) void {4924pub fn logSections(self: *MachO) void {
4055 log.debug("sections:", .{});4925 log.debug("sections:", .{});
4056 for (self.sections.items(.header), 0..) |header, i| {4926 for (self.sections.items(.header), 0..) |header, i| {
...@@ -4065,9 +4935,7 @@ pub fn logSections(self: *MachO) void {...@@ -4065,9 +4935,7 @@ pub fn logSections(self: *MachO) void {
4065 }4935 }
4066}4936}
40674937
4068fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 {4938fn logSymAttributes(sym: macho.nlist_64, buf: []u8) []const u8 {
4069 @memset(buf[0..4], '_');
4070 @memset(buf[4..], ' ');
4071 if (sym.sect()) {4939 if (sym.sect()) {
4072 buf[0] = 's';4940 buf[0] = 's';
4073 }4941 }
...@@ -4090,56 +4958,110 @@ fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 {...@@ -4090,56 +4958,110 @@ fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 {
4090pub fn logSymtab(self: *MachO) void {4958pub fn logSymtab(self: *MachO) void {
4091 var buf: [4]u8 = undefined;4959 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)", .{});
4094 for (self.locals.items, 0..) |sym, sym_id| {4979 for (self.locals.items, 0..) |sym, sym_id| {
4095 const where = if (sym.undf() and !sym.tentative()) "ord" else "sect";4980 if (sym.undf()) continue;
4096 const def_index = if (sym.undf() and !sym.tentative())4981 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{
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}", .{
4101 sym_id,4982 sym_id,
4102 self.strtab.get(sym.n_strx),4983 self.strtab.get(sym.n_strx).?,
4103 sym.n_value,4984 sym.n_value,
4104 where,4985 sym.n_sect,
4105 def_index,
4106 logSymAttributes(sym, &buf),4986 logSymAttributes(sym, &buf),
4107 });4987 });
4108 }4988 }
41094989
4110 log.debug("globals table:", .{});4990 scoped_log.debug("exports:", .{});
4111 for (self.globals.items) |global| {4991 for (self.globals.items, 0..) |global, i| {
4112 const name = self.getSymbolName(global);4992 const sym = self.getSymbol(global);
4113 log.debug(" {s} => %{d} in object({?d})", .{ name, global.sym_index, global.file });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 });
4114 }5018 }
41155019
4116 log.debug("GOT entries:", .{});5020 scoped_log.debug("GOT entries:", .{});
4117 log.debug("{}", .{self.got_table});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:", .{});5026 scoped_log.debug("stubs entries:", .{});
4120 log.debug("{}", .{self.stub_table});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 }
4121}5050}
41225051
4123pub fn logAtoms(self: *MachO) void {5052pub fn logAtoms(self: *MachO) void {
4124 log.debug("atoms:", .{});5053 log.debug("atoms:", .{});
4125
4126 const slice = self.sections.slice();5054 const slice = self.sections.slice();
4127 for (slice.items(.last_atom_index), 0..) |last_atom_index, i| {5055 for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| {
4128 var atom_index = last_atom_index orelse continue;5056 var atom_index = first_atom_index orelse continue;
4129 const header = slice.items(.header)[i];5057 const header = slice.items(.header)[sect_id];
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 }
41375058
4138 log.debug("{s},{s}", .{ header.segName(), header.sectName() });5059 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
41395060
4140 while (true) {5061 while (true) {
4141 self.logAtom(atom_index);
4142 const atom = self.getAtom(atom_index);5062 const atom = self.getAtom(atom_index);
5063 self.logAtom(atom_index, log);
5064
4143 if (atom.next_index) |next_index| {5065 if (atom.next_index) |next_index| {
4144 atom_index = next_index;5066 atom_index = next_index;
4145 } else break;5067 } else break;
...@@ -4147,18 +5069,50 @@ pub fn logAtoms(self: *MachO) void {...@@ -4147,18 +5069,50 @@ pub fn logAtoms(self: *MachO) void {
4147 }5069 }
4148}5070}
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
4151 const atom = self.getAtom(atom_index);5075 const atom = self.getAtom(atom_index);
4152 const sym = atom.getSymbol(self);5076 const sym = self.getSymbol(atom.getSymbolWithLoc());
4153 const sym_name = atom.getName(self);5077 const sym_name = self.getSymbolName(atom.getSymbolWithLoc());
4154 log.debug(" ATOM(%{?d}, '{s}') @ {x} sizeof({x}) in object({?d}) in sect({d})", .{5078 logger.debug(" ATOM({d}, %{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?}) in sect({d})", .{
4155 atom.getSymbolIndex(),5079 atom_index,
5080 atom.sym_index,
4156 sym_name,5081 sym_name,
4157 sym.n_value,5082 sym.n_value,
4158 atom.size,5083 atom.size,
4159 atom.file,5084 atom.alignment,
4160 sym.n_sect + 1,5085 atom.getFile(),
5086 sym.n_sect,
4161 });5087 });
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 }
4162}5116}
41635117
4164const MachO = @This();5118const MachO = @This();
...@@ -4197,6 +5151,7 @@ const Cache = std.Build.Cache;...@@ -4197,6 +5151,7 @@ const Cache = std.Build.Cache;
4197const CodeSignature = @import("MachO/CodeSignature.zig");5151const CodeSignature = @import("MachO/CodeSignature.zig");
4198const Compilation = @import("../Compilation.zig");5152const Compilation = @import("../Compilation.zig");
4199const Dwarf = File.Dwarf;5153const Dwarf = File.Dwarf;
5154const DwarfInfo = @import("DwarfInfo.zig");
4200const Dylib = @import("MachO/Dylib.zig");5155const Dylib = @import("MachO/Dylib.zig");
4201const File = link.File;5156const File = link.File;
4202const Object = @import("MachO/Object.zig");5157const Object = @import("MachO/Object.zig");
src/link/MachO/Archive.zig+12-12
...@@ -1,15 +1,3 @@...@@ -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
13file: fs.File,1file: fs.File,
14fat_offset: u64,2fat_offset: u64,
15name: []const u8,3name: []const u8,
...@@ -215,3 +203,15 @@ pub fn parseObject(self: Archive, gpa: Allocator, offset: u32) !Object {...@@ -215,3 +203,15 @@ pub fn parseObject(self: Archive, gpa: Allocator, offset: u32) !Object {
215203
216 return object;204 return object;
217}205}
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 {...@@ -105,8 +105,7 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
105 return surplus >= MachO.min_text_capacity;105 return surplus >= MachO.min_text_capacity;
106}106}
107107
108pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {108pub fn getOutputSection(macho_file: *MachO, sect: macho.section_64) !?u8 {
109 const gpa = zld.gpa;
110 const segname = sect.segName();109 const segname = sect.segName();
111 const sectname = sect.sectName();110 const sectname = sect.sectName();
112 const res: ?u8 = blk: {111 const res: ?u8 = blk: {
...@@ -126,20 +125,14 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {...@@ -126,20 +125,14 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {
126 }125 }
127126
128 if (sect.isCode()) {127 if (sect.isCode()) {
129 if (zld.text_section_index == null) {128 if (macho_file.text_section_index == null) {
130 zld.text_section_index = try MachO.initSection(129 macho_file.text_section_index = try macho_file.initSection("__TEXT", "__text", .{
131 gpa,130 .flags = macho.S_REGULAR |
132 zld,131 macho.S_ATTR_PURE_INSTRUCTIONS |
133 "__TEXT",132 macho.S_ATTR_SOME_INSTRUCTIONS,
134 "__text",133 });
135 .{
136 .flags = macho.S_REGULAR |
137 macho.S_ATTR_PURE_INSTRUCTIONS |
138 macho.S_ATTR_SOME_INSTRUCTIONS,
139 },
140 );
141 }134 }
142 break :blk zld.text_section_index.?;135 break :blk macho_file.text_section_index.?;
143 }136 }
144137
145 if (sect.isDebug()) {138 if (sect.isDebug()) {
...@@ -151,42 +144,26 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {...@@ -151,42 +144,26 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {
151 macho.S_8BYTE_LITERALS,144 macho.S_8BYTE_LITERALS,
152 macho.S_16BYTE_LITERALS,145 macho.S_16BYTE_LITERALS,
153 => {146 => {
154 break :blk zld.getSectionByName("__TEXT", "__const") orelse try MachO.initSection(147 break :blk macho_file.getSectionByName("__TEXT", "__const") orelse
155 gpa,148 try macho_file.initSection("__TEXT", "__const", .{});
156 zld,
157 "__TEXT",
158 "__const",
159 .{},
160 );
161 },149 },
162 macho.S_CSTRING_LITERALS => {150 macho.S_CSTRING_LITERALS => {
163 if (mem.startsWith(u8, sectname, "__objc")) {151 if (mem.startsWith(u8, sectname, "__objc")) {
164 break :blk zld.getSectionByName(segname, sectname) orelse try MachO.initSection(152 break :blk macho_file.getSectionByName(segname, sectname) orelse
165 gpa,153 try macho_file.initSection(segname, sectname, .{});
166 zld,
167 segname,
168 sectname,
169 .{},
170 );
171 }154 }
172 break :blk zld.getSectionByName("__TEXT", "__cstring") orelse try MachO.initSection(155 break :blk macho_file.getSectionByName("__TEXT", "__cstring") orelse
173 gpa,156 try macho_file.initSection("__TEXT", "__cstring", .{
174 zld,157 .flags = macho.S_CSTRING_LITERALS,
175 "__TEXT",158 });
176 "__cstring",
177 .{ .flags = macho.S_CSTRING_LITERALS },
178 );
179 },159 },
180 macho.S_MOD_INIT_FUNC_POINTERS,160 macho.S_MOD_INIT_FUNC_POINTERS,
181 macho.S_MOD_TERM_FUNC_POINTERS,161 macho.S_MOD_TERM_FUNC_POINTERS,
182 => {162 => {
183 break :blk zld.getSectionByName("__DATA_CONST", sectname) orelse try MachO.initSection(163 break :blk macho_file.getSectionByName("__DATA_CONST", sectname) orelse
184 gpa,164 try macho_file.initSection("__DATA_CONST", sectname, .{
185 zld,165 .flags = sect.flags,
186 "__DATA_CONST",166 });
187 sectname,
188 .{ .flags = sect.flags },
189 );
190 },167 },
191 macho.S_LITERAL_POINTERS,168 macho.S_LITERAL_POINTERS,
192 macho.S_ZEROFILL,169 macho.S_ZEROFILL,
...@@ -195,23 +172,14 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {...@@ -195,23 +172,14 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {
195 macho.S_THREAD_LOCAL_REGULAR,172 macho.S_THREAD_LOCAL_REGULAR,
196 macho.S_THREAD_LOCAL_ZEROFILL,173 macho.S_THREAD_LOCAL_ZEROFILL,
197 => {174 => {
198 break :blk zld.getSectionByName(segname, sectname) orelse try MachO.initSection(175 break :blk macho_file.getSectionByName(segname, sectname) orelse
199 gpa,176 try macho_file.initSection(segname, sectname, .{
200 zld,177 .flags = sect.flags,
201 segname,178 });
202 sectname,
203 .{ .flags = sect.flags },
204 );
205 },179 },
206 macho.S_COALESCED => {180 macho.S_COALESCED => {
207 break :blk zld.getSectionByName(segname, sectname) orelse try MachO.initSection(181 break :blk macho_file.getSectionByName(segname, sectname) orelse
208 gpa,182 try macho_file.initSection(segname, sectname, .{});
209 zld,
210
211 segname,
212 sectname,
213 .{},
214 );
215 },183 },
216 macho.S_REGULAR => {184 macho.S_REGULAR => {
217 if (mem.eql(u8, segname, "__TEXT")) {185 if (mem.eql(u8, segname, "__TEXT")) {
...@@ -221,13 +189,8 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {...@@ -221,13 +189,8 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {
221 mem.eql(u8, sectname, "__gosymtab") or189 mem.eql(u8, sectname, "__gosymtab") or
222 mem.eql(u8, sectname, "__gopclntab"))190 mem.eql(u8, sectname, "__gopclntab"))
223 {191 {
224 break :blk zld.getSectionByName("__TEXT", sectname) orelse try MachO.initSection(192 break :blk macho_file.getSectionByName("__TEXT", sectname) orelse
225 gpa,193 try macho_file.initSection("__TEXT", sectname, .{});
226 zld,
227 "__TEXT",
228 sectname,
229 .{},
230 );
231 }194 }
232 }195 }
233 if (mem.eql(u8, segname, "__DATA")) {196 if (mem.eql(u8, segname, "__DATA")) {
...@@ -236,33 +199,17 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {...@@ -236,33 +199,17 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {
236 mem.eql(u8, sectname, "__objc_classlist") or199 mem.eql(u8, sectname, "__objc_classlist") or
237 mem.eql(u8, sectname, "__objc_imageinfo"))200 mem.eql(u8, sectname, "__objc_imageinfo"))
238 {201 {
239 break :blk zld.getSectionByName("__DATA_CONST", sectname) orelse try MachO.initSection(202 break :blk macho_file.getSectionByName("__DATA_CONST", sectname) orelse
240 gpa,203 try macho_file.initSection("__DATA_CONST", sectname, .{});
241 zld,
242 "__DATA_CONST",
243 sectname,
244 .{},
245 );
246 } else if (mem.eql(u8, sectname, "__data")) {204 } else if (mem.eql(u8, sectname, "__data")) {
247 if (zld.data_section_index == null) {205 if (macho_file.data_section_index == null) {
248 zld.data_section_index = try MachO.initSection(206 macho_file.data_section_index = try macho_file.initSection("__DATA", "__data", .{});
249 gpa,
250 zld,
251 "__DATA",
252 "__data",
253 .{},
254 );
255 }207 }
256 break :blk zld.data_section_index.?;208 break :blk macho_file.data_section_index.?;
257 }209 }
258 }210 }
259 break :blk zld.getSectionByName(segname, sectname) orelse try MachO.initSection(211 break :blk macho_file.getSectionByName(segname, sectname) orelse
260 gpa,212 try macho_file.initSection(segname, sectname, .{});
261 zld,
262 segname,
263 sectname,
264 .{},
265 );
266 },213 },
267 else => break :blk null,214 else => break :blk null,
268 }215 }
...@@ -270,29 +217,29 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {...@@ -270,29 +217,29 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {
270217
271 // TODO we can do this directly in the selection logic above.218 // TODO we can do this directly in the selection logic above.
272 // Or is it not worth it?219 // Or is it not worth it?
273 if (zld.data_const_section_index == null) {220 if (macho_file.data_const_section_index == null) {
274 if (zld.getSectionByName("__DATA_CONST", "__const")) |index| {221 if (macho_file.getSectionByName("__DATA_CONST", "__const")) |index| {
275 zld.data_const_section_index = index;222 macho_file.data_const_section_index = index;
276 }223 }
277 }224 }
278 if (zld.thread_vars_section_index == null) {225 if (macho_file.thread_vars_section_index == null) {
279 if (zld.getSectionByName("__DATA", "__thread_vars")) |index| {226 if (macho_file.getSectionByName("__DATA", "__thread_vars")) |index| {
280 zld.thread_vars_section_index = index;227 macho_file.thread_vars_section_index = index;
281 }228 }
282 }229 }
283 if (zld.thread_data_section_index == null) {230 if (macho_file.thread_data_section_index == null) {
284 if (zld.getSectionByName("__DATA", "__thread_data")) |index| {231 if (macho_file.getSectionByName("__DATA", "__thread_data")) |index| {
285 zld.thread_data_section_index = index;232 macho_file.thread_data_section_index = index;
286 }233 }
287 }234 }
288 if (zld.thread_bss_section_index == null) {235 if (macho_file.thread_bss_section_index == null) {
289 if (zld.getSectionByName("__DATA", "__thread_bss")) |index| {236 if (macho_file.getSectionByName("__DATA", "__thread_bss")) |index| {
290 zld.thread_bss_section_index = index;237 macho_file.thread_bss_section_index = index;
291 }238 }
292 }239 }
293 if (zld.bss_section_index == null) {240 if (macho_file.bss_section_index == null) {
294 if (zld.getSectionByName("__DATA", "__bss")) |index| {241 if (macho_file.getSectionByName("__DATA", "__bss")) |index| {
295 zld.bss_section_index = index;242 macho_file.bss_section_index = index;
296 }243 }
297 }244 }
298245
...@@ -383,8 +330,8 @@ const InnerSymIterator = struct {...@@ -383,8 +330,8 @@ const InnerSymIterator = struct {
383330
384/// Returns an iterator over potentially contained symbols.331/// Returns an iterator over potentially contained symbols.
385/// Panics when called on a synthetic Atom.332/// Panics when called on a synthetic Atom.
386pub fn getInnerSymbolsIterator(zld: *Zld, atom_index: Index) InnerSymIterator {333pub fn getInnerSymbolsIterator(macho_file: *MachO, atom_index: Index) InnerSymIterator {
387 const atom = zld.getAtom(atom_index);334 const atom = macho_file.getAtom(atom_index);
388 assert(atom.getFile() != null);335 assert(atom.getFile() != null);
389 return .{336 return .{
390 .sym_index = atom.inner_sym_index,337 .sym_index = atom.inner_sym_index,
...@@ -397,11 +344,11 @@ pub fn getInnerSymbolsIterator(zld: *Zld, atom_index: Index) InnerSymIterator {...@@ -397,11 +344,11 @@ pub fn getInnerSymbolsIterator(zld: *Zld, atom_index: Index) InnerSymIterator {
397/// An alias symbol is used to represent the start of an input section344/// An alias symbol is used to represent the start of an input section
398/// if there were no symbols defined within that range.345/// if there were no symbols defined within that range.
399/// Alias symbols are only used on x86_64.346/// Alias symbols are only used on x86_64.
400pub fn getSectionAlias(zld: *Zld, atom_index: Index) ?SymbolWithLoc {347pub fn getSectionAlias(macho_file: *MachO, atom_index: Index) ?SymbolWithLoc {
401 const atom = zld.getAtom(atom_index);348 const atom = macho_file.getAtom(atom_index);
402 assert(atom.getFile() != null);349 assert(atom.getFile() != null);
403350
404 const object = zld.objects.items[atom.getFile().?];351 const object = macho_file.objects.items[atom.getFile().?];
405 const nbase = @as(u32, @intCast(object.in_symtab.?.len));352 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
406 const ntotal = @as(u32, @intCast(object.symtab.len));353 const ntotal = @as(u32, @intCast(object.symtab.len));
407 var sym_index: u32 = nbase;354 var sym_index: u32 = nbase;
...@@ -418,13 +365,13 @@ pub fn getSectionAlias(zld: *Zld, atom_index: Index) ?SymbolWithLoc {...@@ -418,13 +365,13 @@ pub fn getSectionAlias(zld: *Zld, atom_index: Index) ?SymbolWithLoc {
418365
419/// Given an index into a contained symbol within, calculates an offset wrt366/// Given an index into a contained symbol within, calculates an offset wrt
420/// the start of this Atom.367/// the start of this Atom.
421pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: Index, sym_index: u32) u64 {368pub fn calcInnerSymbolOffset(macho_file: *MachO, atom_index: Index, sym_index: u32) u64 {
422 const atom = zld.getAtom(atom_index);369 const atom = macho_file.getAtom(atom_index);
423 assert(atom.getFile() != null);370 assert(atom.getFile() != null);
424371
425 if (atom.sym_index == sym_index) return 0;372 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().?];
428 const source_sym = object.getSourceSymbol(sym_index).?;375 const source_sym = object.getSourceSymbol(sym_index).?;
429 const base_addr = if (object.getSourceSymbol(atom.sym_index)) |sym|376 const base_addr = if (object.getSourceSymbol(atom.sym_index)) |sym|
430 sym.n_value377 sym.n_value
...@@ -437,14 +384,14 @@ pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: Index, sym_index: u32) u64 {...@@ -437,14 +384,14 @@ pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: Index, sym_index: u32) u64 {
437 return source_sym.n_value - base_addr;384 return source_sym.n_value - base_addr;
438}385}
439386
440pub fn scanAtomRelocs(zld: *Zld, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void {387pub fn scanAtomRelocs(macho_file: *MachO, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void {
441 const arch = zld.options.target.cpu.arch;388 const arch = macho_file.base.options.target.cpu.arch;
442 const atom = zld.getAtom(atom_index);389 const atom = macho_file.getAtom(atom_index);
443 assert(atom.getFile() != null); // synthetic atoms do not have relocs390 assert(atom.getFile() != null); // synthetic atoms do not have relocs
444391
445 return switch (arch) {392 return switch (arch) {
446 .aarch64 => scanAtomRelocsArm64(zld, atom_index, relocs),393 .aarch64 => scanAtomRelocsArm64(macho_file, atom_index, relocs),
447 .x86_64 => scanAtomRelocsX86(zld, atom_index, relocs),394 .x86_64 => scanAtomRelocsX86(macho_file, atom_index, relocs),
448 else => unreachable,395 else => unreachable,
449 };396 };
450}397}
...@@ -454,11 +401,11 @@ const RelocContext = struct {...@@ -454,11 +401,11 @@ const RelocContext = struct {
454 base_offset: i32 = 0,401 base_offset: i32 = 0,
455};402};
456403
457pub fn getRelocContext(zld: *Zld, atom_index: Index) RelocContext {404pub fn getRelocContext(macho_file: *MachO, atom_index: Index) RelocContext {
458 const atom = zld.getAtom(atom_index);405 const atom = macho_file.getAtom(atom_index);
459 assert(atom.getFile() != null); // synthetic atoms do not have relocs406 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().?];
462 if (object.getSourceSymbol(atom.sym_index)) |source_sym| {409 if (object.getSourceSymbol(atom.sym_index)) |source_sym| {
463 const source_sect = object.getSourceSection(source_sym.n_sect - 1);410 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
464 return .{411 return .{
...@@ -475,7 +422,7 @@ pub fn getRelocContext(zld: *Zld, atom_index: Index) RelocContext {...@@ -475,7 +422,7 @@ pub fn getRelocContext(zld: *Zld, atom_index: Index) RelocContext {
475 };422 };
476}423}
477424
478pub fn parseRelocTarget(zld: *Zld, ctx: struct {425pub fn parseRelocTarget(macho_file: *MachO, ctx: struct {
479 object_id: u32,426 object_id: u32,
480 rel: macho.relocation_info,427 rel: macho.relocation_info,
481 code: []const u8,428 code: []const u8,
...@@ -485,7 +432,7 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct {...@@ -485,7 +432,7 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct {
485 const tracy = trace(@src());432 const tracy = trace(@src());
486 defer tracy.end();433 defer tracy.end();
487434
488 const object = &zld.objects.items[ctx.object_id];435 const object = &macho_file.objects.items[ctx.object_id];
489 log.debug("parsing reloc target in object({d}) '{s}' ", .{ ctx.object_id, object.name });436 log.debug("parsing reloc target in object({d}) '{s}' ", .{ ctx.object_id, object.name });
490437
491 const sym_index = if (ctx.rel.r_extern == 0) sym_index: {438 const sym_index = if (ctx.rel.r_extern == 0) sym_index: {
...@@ -498,7 +445,7 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct {...@@ -498,7 +445,7 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct {
498 else445 else
499 mem.readIntLittle(u32, ctx.code[rel_offset..][0..4]);446 mem.readIntLittle(u32, ctx.code[rel_offset..][0..4]);
500 } else blk: {447 } else blk: {
501 assert(zld.options.target.cpu.arch == .x86_64);448 assert(macho_file.base.options.target.cpu.arch == .x86_64);
502 const correction: u3 = switch (@as(macho.reloc_type_x86_64, @enumFromInt(ctx.rel.r_type))) {449 const correction: u3 = switch (@as(macho.reloc_type_x86_64, @enumFromInt(ctx.rel.r_type))) {
503 .X86_64_RELOC_SIGNED => 0,450 .X86_64_RELOC_SIGNED => 0,
504 .X86_64_RELOC_SIGNED_1 => 1,451 .X86_64_RELOC_SIGNED_1 => 1,
...@@ -517,35 +464,39 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct {...@@ -517,35 +464,39 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct {
517 } else object.reverse_symtab_lookup[ctx.rel.r_symbolnum];464 } else object.reverse_symtab_lookup[ctx.rel.r_symbolnum];
518465
519 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = ctx.object_id + 1 };466 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);
521 const target = if (sym.sect() and !sym.ext())468 const target = if (sym.sect() and !sym.ext())
522 sym_loc469 sym_loc
523 else if (object.getGlobal(sym_index)) |global_index|470 else if (object.getGlobal(sym_index)) |global_index|
524 zld.globals.items[global_index]471 macho_file.globals.items[global_index]
525 else472 else
526 sym_loc;473 sym_loc;
527 log.debug(" | target %{d} ('{s}') in object({?d})", .{474 log.debug(" | target %{d} ('{s}') in object({?d})", .{
528 target.sym_index,475 target.sym_index,
529 zld.getSymbolName(target),476 macho_file.getSymbolName(target),
530 target.getFile(),477 target.getFile(),
531 });478 });
532 return target;479 return target;
533}480}
534481
535pub fn getRelocTargetAtomIndex(zld: *Zld, target: SymbolWithLoc) ?Index {482pub fn getRelocTargetAtomIndex(macho_file: *MachO, target: SymbolWithLoc) ?Index {
536 if (target.getFile() == null) {483 if (target.getFile() == null) {
537 const target_sym_name = zld.getSymbolName(target);484 const target_sym_name = macho_file.getSymbolName(target);
538 if (mem.eql(u8, "__mh_execute_header", target_sym_name)) return null;485 if (mem.eql(u8, "__mh_execute_header", target_sym_name)) return null;
539 if (mem.eql(u8, "___dso_handle", target_sym_name)) return null;486 if (mem.eql(u8, "___dso_handle", target_sym_name)) return null;
540487
541 unreachable; // referenced symbol not found488 unreachable; // referenced symbol not found
542 }489 }
543490
544 const object = zld.objects.items[target.getFile().?];491 const object = macho_file.objects.items[target.getFile().?];
545 return object.getAtomIndexForSymbol(target.sym_index);492 return object.getAtomIndexForSymbol(target.sym_index);
546}493}
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 {
549 for (relocs) |rel| {500 for (relocs) |rel| {
550 const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));501 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...@@ -556,8 +507,8 @@ fn scanAtomRelocsArm64(zld: *Zld, atom_index: Index, relocs: []align(1) const ma
556507
557 if (rel.r_extern == 0) continue;508 if (rel.r_extern == 0) continue;
558509
559 const atom = zld.getAtom(atom_index);510 const atom = macho_file.getAtom(atom_index);
560 const object = &zld.objects.items[atom.getFile().?];511 const object = &macho_file.objects.items[atom.getFile().?];
561 const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];512 const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
562 const sym_loc = SymbolWithLoc{513 const sym_loc = SymbolWithLoc{
563 .sym_index = sym_index,514 .sym_index = sym_index,
...@@ -565,35 +516,39 @@ fn scanAtomRelocsArm64(zld: *Zld, atom_index: Index, relocs: []align(1) const ma...@@ -565,35 +516,39 @@ fn scanAtomRelocsArm64(zld: *Zld, atom_index: Index, relocs: []align(1) const ma
565 };516 };
566517
567 const target = if (object.getGlobal(sym_index)) |global_index|518 const target = if (object.getGlobal(sym_index)) |global_index|
568 zld.globals.items[global_index]519 macho_file.globals.items[global_index]
569 else520 else
570 sym_loc;521 sym_loc;
571522
572 switch (rel_type) {523 switch (rel_type) {
573 .ARM64_RELOC_BRANCH26 => {524 .ARM64_RELOC_BRANCH26 => {
574 // TODO rewrite relocation525 // TODO rewrite relocation
575 const sym = zld.getSymbol(target);526 const sym = macho_file.getSymbol(target);
576 if (sym.undf()) try zld.addStubEntry(target);527 if (sym.undf()) try macho_file.addStubEntry(target);
577 },528 },
578 .ARM64_RELOC_GOT_LOAD_PAGE21,529 .ARM64_RELOC_GOT_LOAD_PAGE21,
579 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,530 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
580 .ARM64_RELOC_POINTER_TO_GOT,531 .ARM64_RELOC_POINTER_TO_GOT,
581 => {532 => {
582 // TODO rewrite relocation533 // TODO rewrite relocation
583 try zld.addGotEntry(target);534 try macho_file.addGotEntry(target);
584 },535 },
585 .ARM64_RELOC_TLVP_LOAD_PAGE21,536 .ARM64_RELOC_TLVP_LOAD_PAGE21,
586 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,537 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
587 => {538 => {
588 const sym = zld.getSymbol(target);539 const sym = macho_file.getSymbol(target);
589 if (sym.undf()) try zld.addTlvPtrEntry(target);540 if (sym.undf()) try macho_file.addTlvPtrEntry(target);
590 },541 },
591 else => {},542 else => {},
592 }543 }
593 }544 }
594}545}
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 {
597 for (relocs) |rel| {552 for (relocs) |rel| {
598 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));553 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...@@ -604,8 +559,8 @@ fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const mach
604559
605 if (rel.r_extern == 0) continue;560 if (rel.r_extern == 0) continue;
606561
607 const atom = zld.getAtom(atom_index);562 const atom = macho_file.getAtom(atom_index);
608 const object = &zld.objects.items[atom.getFile().?];563 const object = &macho_file.objects.items[atom.getFile().?];
609 const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];564 const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
610 const sym_loc = SymbolWithLoc{565 const sym_loc = SymbolWithLoc{
611 .sym_index = sym_index,566 .sym_index = sym_index,
...@@ -613,23 +568,23 @@ fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const mach...@@ -613,23 +568,23 @@ fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const mach
613 };568 };
614569
615 const target = if (object.getGlobal(sym_index)) |global_index|570 const target = if (object.getGlobal(sym_index)) |global_index|
616 zld.globals.items[global_index]571 macho_file.globals.items[global_index]
617 else572 else
618 sym_loc;573 sym_loc;
619574
620 switch (rel_type) {575 switch (rel_type) {
621 .X86_64_RELOC_BRANCH => {576 .X86_64_RELOC_BRANCH => {
622 // TODO rewrite relocation577 // TODO rewrite relocation
623 const sym = zld.getSymbol(target);578 const sym = macho_file.getSymbol(target);
624 if (sym.undf()) try zld.addStubEntry(target);579 if (sym.undf()) try macho_file.addStubEntry(target);
625 },580 },
626 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => {581 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => {
627 // TODO rewrite relocation582 // TODO rewrite relocation
628 try zld.addGotEntry(target);583 try macho_file.addGotEntry(target);
629 },584 },
630 .X86_64_RELOC_TLV => {585 .X86_64_RELOC_TLV => {
631 const sym = zld.getSymbol(target);586 const sym = macho_file.getSymbol(target);
632 if (sym.undf()) try zld.addTlvPtrEntry(target);587 if (sym.undf()) try macho_file.addTlvPtrEntry(target);
633 },588 },
634 else => {},589 else => {},
635 }590 }
...@@ -637,53 +592,53 @@ fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const mach...@@ -637,53 +592,53 @@ fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const mach
637}592}
638593
639pub fn resolveRelocs(594pub fn resolveRelocs(
640 zld: *Zld,595 macho_file: *MachO,
641 atom_index: Index,596 atom_index: Index,
642 atom_code: []u8,597 atom_code: []u8,
643 atom_relocs: []align(1) const macho.relocation_info,598 atom_relocs: []align(1) const macho.relocation_info,
644) !void {599) !void {
645 const arch = zld.options.target.cpu.arch;600 const arch = macho_file.base.options.target.cpu.arch;
646 const atom = zld.getAtom(atom_index);601 const atom = macho_file.getAtom(atom_index);
647 assert(atom.getFile() != null); // synthetic atoms do not have relocs602 assert(atom.getFile() != null); // synthetic atoms do not have relocs
648603
649 log.debug("resolving relocations in ATOM(%{d}, '{s}')", .{604 log.debug("resolving relocations in ATOM(%{d}, '{s}')", .{
650 atom.sym_index,605 atom.sym_index,
651 zld.getSymbolName(atom.getSymbolWithLoc()),606 macho_file.getSymbolName(atom.getSymbolWithLoc()),
652 });607 });
653608
654 const ctx = getRelocContext(zld, atom_index);609 const ctx = getRelocContext(macho_file, atom_index);
655610
656 return switch (arch) {611 return switch (arch) {
657 .aarch64 => resolveRelocsArm64(zld, atom_index, atom_code, atom_relocs, ctx),612 .aarch64 => resolveRelocsArm64(macho_file, atom_index, atom_code, atom_relocs, ctx),
658 .x86_64 => resolveRelocsX86(zld, atom_index, atom_code, atom_relocs, ctx),613 .x86_64 => resolveRelocsX86(macho_file, atom_index, atom_code, atom_relocs, ctx),
659 else => unreachable,614 else => unreachable,
660 };615 };
661}616}
662617
663pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_tlv: bool) !u64 {618pub fn getRelocTargetAddress(macho_file: *MachO, target: SymbolWithLoc, is_tlv: bool) !u64 {
664 const target_atom_index = getRelocTargetAtomIndex(zld, target) orelse {619 const target_atom_index = getRelocTargetAtomIndex(macho_file, target) orelse {
665 // If there is no atom for target, we still need to check for special, atom-less620 // If there is no atom for target, we still need to check for special, atom-less
666 // symbols such as `___dso_handle`.621 // symbols such as `___dso_handle`.
667 const target_name = zld.getSymbolName(target);622 const target_name = macho_file.getSymbolName(target);
668 const atomless_sym = zld.getSymbol(target);623 const atomless_sym = macho_file.getSymbol(target);
669 log.debug(" | atomless target '{s}'", .{target_name});624 log.debug(" | atomless target '{s}'", .{target_name});
670 return atomless_sym.n_value;625 return atomless_sym.n_value;
671 };626 };
672 const target_atom = zld.getAtom(target_atom_index);627 const target_atom = macho_file.getAtom(target_atom_index);
673 log.debug(" | target ATOM(%{d}, '{s}') in object({?})", .{628 log.debug(" | target ATOM(%{d}, '{s}') in object({?})", .{
674 target_atom.sym_index,629 target_atom.sym_index,
675 zld.getSymbolName(target_atom.getSymbolWithLoc()),630 macho_file.getSymbolName(target_atom.getSymbolWithLoc()),
676 target_atom.getFile(),631 target_atom.getFile(),
677 });632 });
678633
679 const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());634 const target_sym = macho_file.getSymbol(target_atom.getSymbolWithLoc());
680 assert(target_sym.n_desc != MachO.N_DEAD);635 assert(target_sym.n_desc != MachO.N_DEAD);
681636
682 // If `target` is contained within the target atom, pull its address value.637 // If `target` is contained within the target atom, pull its address value.
683 const offset = if (target_atom.getFile() != null) blk: {638 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().?];
685 break :blk if (object.getSourceSymbol(target.sym_index)) |_|640 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)
687 else642 else
688 0; // section alias643 0; // section alias
689 } else 0;644 } else 0;
...@@ -694,9 +649,9 @@ pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_tlv: bool) !u6...@@ -694,9 +649,9 @@ pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_tlv: bool) !u6
694 // * wrt to __thread_data if defined, then649 // * wrt to __thread_data if defined, then
695 // * wrt to __thread_bss650 // * wrt to __thread_bss
696 const sect_id: u16 = sect_id: {651 const sect_id: u16 = sect_id: {
697 if (zld.thread_data_section_index) |i| {652 if (macho_file.thread_data_section_index) |i| {
698 break :sect_id i;653 break :sect_id i;
699 } else if (zld.thread_bss_section_index) |i| {654 } else if (macho_file.thread_bss_section_index) |i| {
700 break :sect_id i;655 break :sect_id i;
701 } else {656 } else {
702 log.err("threadlocal variables present but no initializer sections found", .{});657 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...@@ -705,20 +660,20 @@ pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_tlv: bool) !u6
705 return error.FailedToResolveRelocationTarget;660 return error.FailedToResolveRelocationTarget;
706 }661 }
707 };662 };
708 break :base_address zld.sections.items(.header)[sect_id].addr;663 break :base_address macho_file.sections.items(.header)[sect_id].addr;
709 } else 0;664 } else 0;
710 return target_sym.n_value + offset - base_address;665 return target_sym.n_value + offset - base_address;
711}666}
712667
713fn resolveRelocsArm64(668fn resolveRelocsArm64(
714 zld: *Zld,669 macho_file: *MachO,
715 atom_index: Index,670 atom_index: Index,
716 atom_code: []u8,671 atom_code: []u8,
717 atom_relocs: []align(1) const macho.relocation_info,672 atom_relocs: []align(1) const macho.relocation_info,
718 context: RelocContext,673 context: RelocContext,
719) !void {674) !void {
720 const atom = zld.getAtom(atom_index);675 const atom = macho_file.getAtom(atom_index);
721 const object = zld.objects.items[atom.getFile().?];676 const object = macho_file.objects.items[atom.getFile().?];
722677
723 var addend: ?i64 = null;678 var addend: ?i64 = null;
724 var subtractor: ?SymbolWithLoc = null;679 var subtractor: ?SymbolWithLoc = null;
...@@ -745,7 +700,7 @@ fn resolveRelocsArm64(...@@ -745,7 +700,7 @@ fn resolveRelocsArm64(
745 atom.getFile(),700 atom.getFile(),
746 });701 });
747702
748 subtractor = parseRelocTarget(zld, .{703 subtractor = parseRelocTarget(macho_file, .{
749 .object_id = atom.getFile().?,704 .object_id = atom.getFile().?,
750 .rel = rel,705 .rel = rel,
751 .code = atom_code,706 .code = atom_code,
...@@ -757,7 +712,7 @@ fn resolveRelocsArm64(...@@ -757,7 +712,7 @@ fn resolveRelocsArm64(
757 else => {},712 else => {},
758 }713 }
759714
760 const target = parseRelocTarget(zld, .{715 const target = parseRelocTarget(macho_file, .{
761 .object_id = atom.getFile().?,716 .object_id = atom.getFile().?,
762 .rel = rel,717 .rel = rel,
763 .code = atom_code,718 .code = atom_code,
...@@ -770,26 +725,26 @@ fn resolveRelocsArm64(...@@ -770,26 +725,26 @@ fn resolveRelocsArm64(
770 @tagName(rel_type),725 @tagName(rel_type),
771 rel.r_address,726 rel.r_address,
772 target.sym_index,727 target.sym_index,
773 zld.getSymbolName(target),728 macho_file.getSymbolName(target),
774 target.getFile(),729 target.getFile(),
775 });730 });
776731
777 const source_addr = blk: {732 const source_addr = blk: {
778 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());733 const source_sym = macho_file.getSymbol(atom.getSymbolWithLoc());
779 break :blk source_sym.n_value + rel_offset;734 break :blk source_sym.n_value + rel_offset;
780 };735 };
781 const target_addr = blk: {736 const target_addr = blk: {
782 if (relocRequiresGot(zld, rel)) break :blk zld.getGotEntryAddress(target).?;737 if (relocRequiresGot(macho_file, rel)) break :blk macho_file.getGotEntryAddress(target).?;
783 if (relocIsTlv(zld, rel) and zld.getSymbol(target).undf())738 if (relocIsTlv(macho_file, rel) and macho_file.getSymbol(target).undf())
784 break :blk zld.getTlvPtrEntryAddress(target).?;739 break :blk macho_file.getTlvPtrEntryAddress(target).?;
785 if (relocIsStub(zld, rel) and zld.getSymbol(target).undf())740 if (relocIsStub(macho_file, rel) and macho_file.getSymbol(target).undf())
786 break :blk zld.getStubsEntryAddress(target).?;741 break :blk macho_file.getStubsEntryAddress(target).?;
787 const is_tlv = is_tlv: {742 const is_tlv = is_tlv: {
788 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());743 const source_sym = macho_file.getSymbol(atom.getSymbolWithLoc());
789 const header = zld.sections.items(.header)[source_sym.n_sect - 1];744 const header = macho_file.sections.items(.header)[source_sym.n_sect - 1];
790 break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;745 break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
791 };746 };
792 break :blk try getRelocTargetAddress(zld, target, is_tlv);747 break :blk try getRelocTargetAddress(macho_file, target, is_tlv);
793 };748 };
794749
795 log.debug(" | source_addr = 0x{x}", .{source_addr});750 log.debug(" | source_addr = 0x{x}", .{source_addr});
...@@ -797,9 +752,9 @@ fn resolveRelocsArm64(...@@ -797,9 +752,9 @@ fn resolveRelocsArm64(
797 switch (rel_type) {752 switch (rel_type) {
798 .ARM64_RELOC_BRANCH26 => {753 .ARM64_RELOC_BRANCH26 => {
799 log.debug(" source {s} (object({?})), target {s}", .{754 log.debug(" source {s} (object({?})), target {s}", .{
800 zld.getSymbolName(atom.getSymbolWithLoc()),755 macho_file.getSymbolName(atom.getSymbolWithLoc()),
801 atom.getFile(),756 atom.getFile(),
802 zld.getSymbolName(target),757 macho_file.getSymbolName(target),
803 });758 });
804759
805 const displacement = if (Relocation.calcPcRelativeDisplacementArm64(760 const displacement = if (Relocation.calcPcRelativeDisplacementArm64(
...@@ -809,13 +764,13 @@ fn resolveRelocsArm64(...@@ -809,13 +764,13 @@ fn resolveRelocsArm64(
809 log.debug(" | target_addr = 0x{x}", .{target_addr});764 log.debug(" | target_addr = 0x{x}", .{target_addr});
810 break :blk disp;765 break :blk disp;
811 } else |_| blk: {766 } else |_| blk: {
812 const thunk_index = zld.thunk_table.get(atom_index).?;767 const thunk_index = macho_file.thunk_table.get(atom_index).?;
813 const thunk = zld.thunks.items[thunk_index];768 const thunk = macho_file.thunks.items[thunk_index];
814 const thunk_sym_loc = if (zld.getSymbol(target).undf())769 const thunk_sym_loc = if (macho_file.getSymbol(target).undf())
815 thunk.getTrampoline(zld, .stub, target).?770 thunk.getTrampoline(macho_file, .stub, target).?
816 else771 else
817 thunk.getTrampoline(zld, .atom, target).?;772 thunk.getTrampoline(macho_file, .atom, target).?;
818 const thunk_addr = zld.getSymbol(thunk_sym_loc).n_value;773 const thunk_addr = macho_file.getSymbol(thunk_sym_loc).n_value;
819 log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_addr});774 log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_addr});
820 break :blk try Relocation.calcPcRelativeDisplacementArm64(source_addr, thunk_addr);775 break :blk try Relocation.calcPcRelativeDisplacementArm64(source_addr, thunk_addr);
821 };776 };
...@@ -944,7 +899,7 @@ fn resolveRelocsArm64(...@@ -944,7 +899,7 @@ fn resolveRelocsArm64(
944 }899 }
945 };900 };
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{
948 .load_store_register = .{903 .load_store_register = .{
949 .rt = reg_info.rd,904 .rt = reg_info.rd,
950 .rn = reg_info.rn,905 .rn = reg_info.rn,
...@@ -992,7 +947,7 @@ fn resolveRelocsArm64(...@@ -992,7 +947,7 @@ fn resolveRelocsArm64(
992947
993 const result = blk: {948 const result = blk: {
994 if (subtractor) |sub| {949 if (subtractor) |sub| {
995 const sym = zld.getSymbol(sub);950 const sym = macho_file.getSymbol(sub);
996 break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + ptr_addend;951 break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + ptr_addend;
997 } else {952 } else {
998 break :blk @as(i64, @intCast(target_addr)) + ptr_addend;953 break :blk @as(i64, @intCast(target_addr)) + ptr_addend;
...@@ -1016,14 +971,14 @@ fn resolveRelocsArm64(...@@ -1016,14 +971,14 @@ fn resolveRelocsArm64(
1016}971}
1017972
1018fn resolveRelocsX86(973fn resolveRelocsX86(
1019 zld: *Zld,974 macho_file: *MachO,
1020 atom_index: Index,975 atom_index: Index,
1021 atom_code: []u8,976 atom_code: []u8,
1022 atom_relocs: []align(1) const macho.relocation_info,977 atom_relocs: []align(1) const macho.relocation_info,
1023 context: RelocContext,978 context: RelocContext,
1024) !void {979) !void {
1025 const atom = zld.getAtom(atom_index);980 const atom = macho_file.getAtom(atom_index);
1026 const object = zld.objects.items[atom.getFile().?];981 const object = macho_file.objects.items[atom.getFile().?];
1027982
1028 var subtractor: ?SymbolWithLoc = null;983 var subtractor: ?SymbolWithLoc = null;
1029984
...@@ -1041,7 +996,7 @@ fn resolveRelocsX86(...@@ -1041,7 +996,7 @@ fn resolveRelocsX86(
1041 atom.getFile(),996 atom.getFile(),
1042 });997 });
1043998
1044 subtractor = parseRelocTarget(zld, .{999 subtractor = parseRelocTarget(macho_file, .{
1045 .object_id = atom.getFile().?,1000 .object_id = atom.getFile().?,
1046 .rel = rel,1001 .rel = rel,
1047 .code = atom_code,1002 .code = atom_code,
...@@ -1053,7 +1008,7 @@ fn resolveRelocsX86(...@@ -1053,7 +1008,7 @@ fn resolveRelocsX86(
1053 else => {},1008 else => {},
1054 }1009 }
10551010
1056 const target = parseRelocTarget(zld, .{1011 const target = parseRelocTarget(macho_file, .{
1057 .object_id = atom.getFile().?,1012 .object_id = atom.getFile().?,
1058 .rel = rel,1013 .rel = rel,
1059 .code = atom_code,1014 .code = atom_code,
...@@ -1066,26 +1021,26 @@ fn resolveRelocsX86(...@@ -1066,26 +1021,26 @@ fn resolveRelocsX86(
1066 @tagName(rel_type),1021 @tagName(rel_type),
1067 rel.r_address,1022 rel.r_address,
1068 target.sym_index,1023 target.sym_index,
1069 zld.getSymbolName(target),1024 macho_file.getSymbolName(target),
1070 target.getFile(),1025 target.getFile(),
1071 });1026 });
10721027
1073 const source_addr = blk: {1028 const source_addr = blk: {
1074 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());1029 const source_sym = macho_file.getSymbol(atom.getSymbolWithLoc());
1075 break :blk source_sym.n_value + rel_offset;1030 break :blk source_sym.n_value + rel_offset;
1076 };1031 };
1077 const target_addr = blk: {1032 const target_addr = blk: {
1078 if (relocRequiresGot(zld, rel)) break :blk zld.getGotEntryAddress(target).?;1033 if (relocRequiresGot(macho_file, rel)) break :blk macho_file.getGotEntryAddress(target).?;
1079 if (relocIsStub(zld, rel) and zld.getSymbol(target).undf())1034 if (relocIsStub(macho_file, rel) and macho_file.getSymbol(target).undf())
1080 break :blk zld.getStubsEntryAddress(target).?;1035 break :blk macho_file.getStubsEntryAddress(target).?;
1081 if (relocIsTlv(zld, rel) and zld.getSymbol(target).undf())1036 if (relocIsTlv(macho_file, rel) and macho_file.getSymbol(target).undf())
1082 break :blk zld.getTlvPtrEntryAddress(target).?;1037 break :blk macho_file.getTlvPtrEntryAddress(target).?;
1083 const is_tlv = is_tlv: {1038 const is_tlv = is_tlv: {
1084 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());1039 const source_sym = macho_file.getSymbol(atom.getSymbolWithLoc());
1085 const header = zld.sections.items(.header)[source_sym.n_sect - 1];1040 const header = macho_file.sections.items(.header)[source_sym.n_sect - 1];
1086 break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;1041 break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
1087 };1042 };
1088 break :blk try getRelocTargetAddress(zld, target, is_tlv);1043 break :blk try getRelocTargetAddress(macho_file, target, is_tlv);
1089 };1044 };
10901045
1091 log.debug(" | source_addr = 0x{x}", .{source_addr});1046 log.debug(" | source_addr = 0x{x}", .{source_addr});
...@@ -1115,7 +1070,7 @@ fn resolveRelocsX86(...@@ -1115,7 +1070,7 @@ fn resolveRelocsX86(
1115 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});1070 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
1116 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);1071 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) {
1119 // We need to rewrite the opcode from movq to leaq.1074 // We need to rewrite the opcode from movq to leaq.
1120 atom_code[rel_offset - 2] = 0x8d;1075 atom_code[rel_offset - 2] = 0x8d;
1121 }1076 }
...@@ -1170,7 +1125,7 @@ fn resolveRelocsX86(...@@ -1170,7 +1125,7 @@ fn resolveRelocsX86(
11701125
1171 const result = blk: {1126 const result = blk: {
1172 if (subtractor) |sub| {1127 if (subtractor) |sub| {
1173 const sym = zld.getSymbol(sub);1128 const sym = macho_file.getSymbol(sub);
1174 break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + addend;1129 break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + addend;
1175 } else {1130 } else {
1176 break :blk @as(i64, @intCast(target_addr)) + addend;1131 break :blk @as(i64, @intCast(target_addr)) + addend;
...@@ -1192,10 +1147,10 @@ fn resolveRelocsX86(...@@ -1192,10 +1147,10 @@ fn resolveRelocsX86(
1192 }1147 }
1193}1148}
11941149
1195pub fn getAtomCode(zld: *Zld, atom_index: Index) []const u8 {1150pub fn getAtomCode(macho_file: *MachO, atom_index: Index) []const u8 {
1196 const atom = zld.getAtom(atom_index);1151 const atom = macho_file.getAtom(atom_index);
1197 assert(atom.getFile() != null); // Synthetic atom shouldn't need to inquire for code.1152 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().?];
1199 const source_sym = object.getSourceSymbol(atom.sym_index) orelse {1154 const source_sym = object.getSourceSymbol(atom.sym_index) orelse {
1200 // If there was no matching symbol present in the source symtab, this means1155 // If there was no matching symbol present in the source symtab, this means
1201 // we are dealing with either an entire section, or part of it, but also1156 // 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 {...@@ -1216,10 +1171,10 @@ pub fn getAtomCode(zld: *Zld, atom_index: Index) []const u8 {
1216 return code[offset..][0..code_len];1171 return code[offset..][0..code_len];
1217}1172}
12181173
1219pub fn getAtomRelocs(zld: *Zld, atom_index: Index) []const macho.relocation_info {1174pub fn getAtomRelocs(macho_file: *MachO, atom_index: Index) []const macho.relocation_info {
1220 const atom = zld.getAtom(atom_index);1175 const atom = macho_file.getAtom(atom_index);
1221 assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs.1176 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().?];
1223 const cache = object.relocs_lookup[atom.sym_index];1178 const cache = object.relocs_lookup[atom.sym_index];
12241179
1225 const source_sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {1180 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...@@ -1238,8 +1193,8 @@ pub fn getAtomRelocs(zld: *Zld, atom_index: Index) []const macho.relocation_info
1238 return relocs[cache.start..][0..cache.len];1193 return relocs[cache.start..][0..cache.len];
1239}1194}
12401195
1241pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool {1196pub fn relocRequiresGot(macho_file: *MachO, rel: macho.relocation_info) bool {
1242 switch (zld.options.target.cpu.arch) {1197 switch (macho_file.base.options.target.cpu.arch) {
1243 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {1198 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
1244 .ARM64_RELOC_GOT_LOAD_PAGE21,1199 .ARM64_RELOC_GOT_LOAD_PAGE21,
1245 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,1200 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
...@@ -1257,8 +1212,8 @@ pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool {...@@ -1257,8 +1212,8 @@ pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool {
1257 }1212 }
1258}1213}
12591214
1260pub fn relocIsTlv(zld: *Zld, rel: macho.relocation_info) bool {1215pub fn relocIsTlv(macho_file: *MachO, rel: macho.relocation_info) bool {
1261 switch (zld.options.target.cpu.arch) {1216 switch (macho_file.base.options.target.cpu.arch) {
1262 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {1217 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
1263 .ARM64_RELOC_TLVP_LOAD_PAGE21,1218 .ARM64_RELOC_TLVP_LOAD_PAGE21,
1264 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,1219 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
...@@ -1273,8 +1228,8 @@ pub fn relocIsTlv(zld: *Zld, rel: macho.relocation_info) bool {...@@ -1273,8 +1228,8 @@ pub fn relocIsTlv(zld: *Zld, rel: macho.relocation_info) bool {
1273 }1228 }
1274}1229}
12751230
1276pub fn relocIsStub(zld: *Zld, rel: macho.relocation_info) bool {1231pub fn relocIsStub(macho_file: *MachO, rel: macho.relocation_info) bool {
1277 switch (zld.options.target.cpu.arch) {1232 switch (macho_file.base.options.target.cpu.arch) {
1278 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {1233 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
1279 .ARM64_RELOC_BRANCH26 => return true,1234 .ARM64_RELOC_BRANCH26 => return true,
1280 else => return false,1235 else => return false,
...@@ -1305,4 +1260,3 @@ const Arch = std.Target.Cpu.Arch;...@@ -1305,4 +1260,3 @@ const Arch = std.Target.Cpu.Arch;
1305const MachO = @import("../MachO.zig");1260const MachO = @import("../MachO.zig");
1306pub const Relocation = @import("Relocation.zig");1261pub const Relocation = @import("Relocation.zig");
1307const SymbolWithLoc = MachO.SymbolWithLoc;1262const SymbolWithLoc = MachO.SymbolWithLoc;
1308const Zld = @import("zld.zig").Zld;
src/link/MachO/CodeSignature.zig+182-182
...@@ -1,17 +1,175 @@...@@ -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");7pub fn init(page_size: u16) CodeSignature {
4const assert = std.debug.assert;8 return .{
5const fs = std.fs;9 .page_size = page_size,
6const log = std.log.scoped(.link);10 .code_directory = CodeDirectory.init(page_size),
7const macho = std.macho;11 };
8const mem = std.mem;12}
9const testing = std.testing;
1013
11const Allocator = mem.Allocator;14pub fn deinit(self: *CodeSignature, allocator: Allocator) void {
12const Compilation = @import("../../Compilation.zig");15 self.code_directory.deinit(allocator);
13const Hasher = @import("hasher.zig").ParallelHasher;16 if (self.requirements) |*req| {
14const Sha256 = std.crypto.hash.sha2.Sha256;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
16const hash_size = Sha256.digest_length;174const hash_size = Sha256.digest_length;
17175
...@@ -218,175 +376,17 @@ const Signature = struct {...@@ -218,175 +376,17 @@ const Signature = struct {
218 }376 }
219};377};
220378
221page_size: u16,379const CodeSignature = @This();
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}
367380
368pub fn estimateSize(self: CodeSignature, file_size: u64) u32 {381const std = @import("std");
369 var ssize: u64 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size();382const assert = std.debug.assert;
370 // Approx code slots383const fs = std.fs;
371 const total_pages = mem.alignForward(u64, file_size, self.page_size) / self.page_size;384const log = std.log.scoped(.link);
372 ssize += total_pages * hash_size;385const macho = std.macho;
373 var n_special_slots: u32 = 0;386const mem = std.mem;
374 if (self.requirements) |req| {387const testing = std.testing;
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}
388388
389pub fn clear(self: *CodeSignature, allocator: Allocator) void {389const Allocator = mem.Allocator;
390 self.code_directory.deinit(allocator);390const Compilation = @import("../../Compilation.zig");
391 self.code_directory = CodeDirectory.init(self.page_size);391const Hasher = @import("hasher.zig").ParallelHasher;
392}392const Sha256 = std.crypto.hash.sha2.Sha256;
src/link/MachO/DebugSymbols.zig+23-23
...@@ -1,26 +1,3 @@...@@ -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
24allocator: Allocator,1allocator: Allocator,
25dwarf: Dwarf,2dwarf: Dwarf,
26file: fs.File,3file: fs.File,
...@@ -569,3 +546,26 @@ pub fn getSection(self: DebugSymbols, sect: u8) macho.section_64 {...@@ -569,3 +546,26 @@ pub fn getSection(self: DebugSymbols, sect: u8) macho.section_64 {
569 assert(sect < self.sections.items.len);546 assert(sect < self.sections.items.len);
570 return self.sections.items[sect];547 return self.sections.items[sect];
571}548}
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 @@...@@ -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
15debug_info: []const u8,1debug_info: []const u8,
16debug_abbrev: []const u8,2debug_abbrev: []const u8,
17debug_str: []const u8,3debug_str: []const u8,
...@@ -501,3 +487,17 @@ fn getString(self: DwarfInfo, off: u64) []const u8 {...@@ -501,3 +487,17 @@ fn getString(self: DwarfInfo, off: u64) []const u8 {
501 assert(off < self.debug_str.len);487 assert(off < self.debug_str.len);
502 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.debug_str.ptr + @as(usize, @intCast(off)))), 0);488 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.debug_str.ptr + @as(usize, @intCast(off)))), 0);
503}489}
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 @@...@@ -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
21id: ?Id = null,1id: ?Id = null,
22weak: bool = false,2weak: bool = false,
23/// Header is only set if Dylib is parsed directly from a binary and not a stub file.3/// Header is only set if Dylib is parsed directly from a binary and not a stub file.
...@@ -546,3 +526,23 @@ pub fn parseFromStub(...@@ -546,3 +526,23 @@ pub fn parseFromStub(
546 }526 }
547 }527 }
548}528}
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 @@...@@ -2,31 +2,6 @@
2//! Each Object is fully loaded into memory for easier2//! Each Object is fully loaded into memory for easier
3//! access into different data within.3//! 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
30name: []const u8,5name: []const u8,
31mtime: u64,6mtime: u64,
32contents: []align(@alignOf(u64)) const u8,7contents: []align(@alignOf(u64)) const u8,
...@@ -359,25 +334,25 @@ fn sectionLessThanByAddress(ctx: void, lhs: SortedSection, rhs: SortedSection) b...@@ -359,25 +334,25 @@ fn sectionLessThanByAddress(ctx: void, lhs: SortedSection, rhs: SortedSection) b
359 return lhs.header.addr < rhs.header.addr;334 return lhs.header.addr < rhs.header.addr;
360}335}
361336
362pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u32) !void {337pub fn splitIntoAtoms(self: *Object, macho_file: *MachO, object_id: u32) !void {
363 log.debug("splitting object({d}, {s}) into atoms", .{ object_id, self.name });338 log.debug("splitting object({d}, {s}) into atoms", .{ object_id, self.name });
364339
365 try self.splitRegularSections(zld, object_id);340 try self.splitRegularSections(macho_file, object_id);
366 try self.parseEhFrameSection(zld, object_id);341 try self.parseEhFrameSection(macho_file, object_id);
367 try self.parseUnwindInfo(zld, object_id);342 try self.parseUnwindInfo(macho_file, object_id);
368 try self.parseDataInCode(zld.gpa);343 try self.parseDataInCode(macho_file.base.allocator);
369}344}
370345
371/// Splits input regular sections into Atoms.346/// Splits input regular sections into Atoms.
372/// If the Object was compiled with `MH_SUBSECTIONS_VIA_SYMBOLS`, splits section347/// If the Object was compiled with `MH_SUBSECTIONS_VIA_SYMBOLS`, splits section
373/// into subsections where each subsection then represents an Atom.348/// into subsections where each subsection then represents an Atom.
374pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {349pub fn splitRegularSections(self: *Object, macho_file: *MachO, object_id: u32) !void {
375 const gpa = zld.gpa;350 const gpa = macho_file.base.allocator;
376351
377 const sections = self.getSourceSections();352 const sections = self.getSourceSections();
378 for (sections, 0..) |sect, id| {353 for (sections, 0..) |sect, id| {
379 if (sect.isDebug()) continue;354 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 {
381 log.debug(" unhandled section '{s},{s}'", .{ sect.segName(), sect.sectName() });356 log.debug(" unhandled section '{s},{s}'", .{ sect.segName(), sect.sectName() });
382 continue;357 continue;
383 };358 };
...@@ -397,13 +372,13 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -397,13 +372,13 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
397 if (self.in_symtab == null) {372 if (self.in_symtab == null) {
398 for (sections, 0..) |sect, id| {373 for (sections, 0..) |sect, id| {
399 if (sect.isDebug()) continue;374 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;
401 if (sect.size == 0) continue;376 if (sect.size == 0) continue;
402377
403 const sect_id = @as(u8, @intCast(id));378 const sect_id = @as(u8, @intCast(id));
404 const sym_index = self.getSectionAliasSymbolIndex(sect_id);379 const sym_index = self.getSectionAliasSymbolIndex(sect_id);
405 const atom_index = try self.createAtomFromSubsection(380 const atom_index = try self.createAtomFromSubsection(
406 zld,381 macho_file,
407 object_id,382 object_id,
408 sym_index,383 sym_index,
409 sym_index,384 sym_index,
...@@ -412,7 +387,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -412,7 +387,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
412 sect.@"align",387 sect.@"align",
413 out_sect_id,388 out_sect_id,
414 );389 );
415 zld.addAtomToSection(atom_index);390 macho_file.addAtomToSection(atom_index);
416 }391 }
417 return;392 return;
418 }393 }
...@@ -456,17 +431,17 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -456,17 +431,17 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
456 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });431 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
457432
458 // Get output segment/section in the final artifact.433 // 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
461 log.debug(" output sect({d}, '{s},{s}')", .{436 log.debug(" output sect({d}, '{s},{s}')", .{
462 out_sect_id + 1,437 out_sect_id + 1,
463 zld.sections.items(.header)[out_sect_id].segName(),438 macho_file.sections.items(.header)[out_sect_id].segName(),
464 zld.sections.items(.header)[out_sect_id].sectName(),439 macho_file.sections.items(.header)[out_sect_id].sectName(),
465 });440 });
466441
467 try self.parseRelocs(gpa, section.id);442 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;
470 const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1);445 const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1);
471 const sect_start_index = sect_sym_index + sect_loc.index;446 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 {...@@ -482,7 +457,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
482 const sym_index = self.getSectionAliasSymbolIndex(sect_id);457 const sym_index = self.getSectionAliasSymbolIndex(sect_id);
483 const atom_size = first_sym.n_value - sect.addr;458 const atom_size = first_sym.n_value - sect.addr;
484 const atom_index = try self.createAtomFromSubsection(459 const atom_index = try self.createAtomFromSubsection(
485 zld,460 macho_file,
486 object_id,461 object_id,
487 sym_index,462 sym_index,
488 sym_index,463 sym_index,
...@@ -492,9 +467,9 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -492,9 +467,9 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
492 out_sect_id,467 out_sect_id,
493 );468 );
494 if (!sect.isZerofill()) {469 if (!sect.isZerofill()) {
495 try self.cacheRelocs(zld, atom_index);470 try self.cacheRelocs(macho_file, atom_index);
496 }471 }
497 zld.addAtomToSection(atom_index);472 macho_file.addAtomToSection(atom_index);
498 }473 }
499474
500 var next_sym_index = sect_start_index;475 var next_sym_index = sect_start_index;
...@@ -518,7 +493,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -518,7 +493,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
518 sect.@"align";493 sect.@"align";
519494
520 const atom_index = try self.createAtomFromSubsection(495 const atom_index = try self.createAtomFromSubsection(
521 zld,496 macho_file,
522 object_id,497 object_id,
523 atom_sym_index,498 atom_sym_index,
524 atom_sym_index,499 atom_sym_index,
...@@ -537,14 +512,14 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -537,14 +512,14 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
537 self.atom_by_index_table[alias_index] = atom_index;512 self.atom_by_index_table[alias_index] = atom_index;
538 }513 }
539 if (!sect.isZerofill()) {514 if (!sect.isZerofill()) {
540 try self.cacheRelocs(zld, atom_index);515 try self.cacheRelocs(macho_file, atom_index);
541 }516 }
542 zld.addAtomToSection(atom_index);517 macho_file.addAtomToSection(atom_index);
543 }518 }
544 } else {519 } else {
545 const alias_index = self.getSectionAliasSymbolIndex(sect_id);520 const alias_index = self.getSectionAliasSymbolIndex(sect_id);
546 const atom_index = try self.createAtomFromSubsection(521 const atom_index = try self.createAtomFromSubsection(
547 zld,522 macho_file,
548 object_id,523 object_id,
549 alias_index,524 alias_index,
550 sect_start_index,525 sect_start_index,
...@@ -554,16 +529,16 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -554,16 +529,16 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
554 out_sect_id,529 out_sect_id,
555 );530 );
556 if (!sect.isZerofill()) {531 if (!sect.isZerofill()) {
557 try self.cacheRelocs(zld, atom_index);532 try self.cacheRelocs(macho_file, atom_index);
558 }533 }
559 zld.addAtomToSection(atom_index);534 macho_file.addAtomToSection(atom_index);
560 }535 }
561 }536 }
562}537}
563538
564fn createAtomFromSubsection(539fn createAtomFromSubsection(
565 self: *Object,540 self: *Object,
566 zld: *Zld,541 macho_file: *MachO,
567 object_id: u32,542 object_id: u32,
568 sym_index: u32,543 sym_index: u32,
569 inner_sym_index: u32,544 inner_sym_index: u32,
...@@ -572,9 +547,9 @@ fn createAtomFromSubsection(...@@ -572,9 +547,9 @@ fn createAtomFromSubsection(
572 alignment: u32,547 alignment: u32,
573 out_sect_id: u8,548 out_sect_id: u8,
574) !Atom.Index {549) !Atom.Index {
575 const gpa = zld.gpa;550 const gpa = macho_file.base.allocator;
576 const atom_index = try zld.createAtom(sym_index, .{ .size = size, .alignment = alignment });551 const atom_index = try macho_file.createAtom(sym_index, .{ .size = size, .alignment = alignment });
577 const atom = zld.getAtomPtr(atom_index);552 const atom = macho_file.getAtomPtr(atom_index);
578 atom.inner_sym_index = inner_sym_index;553 atom.inner_sym_index = inner_sym_index;
579 atom.inner_nsyms_trailing = inner_nsyms_trailing;554 atom.inner_nsyms_trailing = inner_nsyms_trailing;
580 atom.file = object_id + 1;555 atom.file = object_id + 1;
...@@ -584,22 +559,22 @@ fn createAtomFromSubsection(...@@ -584,22 +559,22 @@ fn createAtomFromSubsection(
584 sym_index,559 sym_index,
585 self.getSymbolName(sym_index),560 self.getSymbolName(sym_index),
586 out_sect_id + 1,561 out_sect_id + 1,
587 zld.sections.items(.header)[out_sect_id].segName(),562 macho_file.sections.items(.header)[out_sect_id].segName(),
588 zld.sections.items(.header)[out_sect_id].sectName(),563 macho_file.sections.items(.header)[out_sect_id].sectName(),
589 object_id,564 object_id,
590 });565 });
591566
592 try self.atoms.append(gpa, atom_index);567 try self.atoms.append(gpa, atom_index);
593 self.atom_by_index_table[sym_index] = atom_index;568 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);
596 while (it.next()) |sym_loc| {571 while (it.next()) |sym_loc| {
597 const inner = zld.getSymbolPtr(sym_loc);572 const inner = macho_file.getSymbolPtr(sym_loc);
598 inner.n_sect = out_sect_id + 1;573 inner.n_sect = out_sect_id + 1;
599 self.atom_by_index_table[sym_loc.sym_index] = atom_index;574 self.atom_by_index_table[sym_loc.sym_index] = atom_index;
600 }575 }
601576
602 const out_sect = zld.sections.items(.header)[out_sect_id];577 const out_sect = macho_file.sections.items(.header)[out_sect_id];
603 if (out_sect.isCode() and578 if (out_sect.isCode() and
604 mem.eql(u8, "__TEXT", out_sect.segName()) and579 mem.eql(u8, "__TEXT", out_sect.segName()) and
605 mem.eql(u8, "__text", out_sect.sectName()))580 mem.eql(u8, "__text", out_sect.sectName()))
...@@ -651,8 +626,8 @@ fn parseRelocs(self: *Object, gpa: Allocator, sect_id: u8) !void {...@@ -651,8 +626,8 @@ fn parseRelocs(self: *Object, gpa: Allocator, sect_id: u8) !void {
651 self.section_relocs_lookup.items[sect_id] = start;626 self.section_relocs_lookup.items[sect_id] = start;
652}627}
653628
654fn cacheRelocs(self: *Object, zld: *Zld, atom_index: Atom.Index) !void {629fn cacheRelocs(self: *Object, macho_file: *MachO, atom_index: Atom.Index) !void {
655 const atom = zld.getAtom(atom_index);630 const atom = macho_file.getAtom(atom_index);
656631
657 const source_sect_id = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {632 const source_sect_id = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
658 break :blk source_sym.n_sect - 1;633 break :blk source_sym.n_sect - 1;
...@@ -679,19 +654,19 @@ fn relocGreaterThan(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation...@@ -679,19 +654,19 @@ fn relocGreaterThan(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation
679 return lhs.r_address > rhs.r_address;654 return lhs.r_address > rhs.r_address;
680}655}
681656
682fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {657fn parseEhFrameSection(self: *Object, macho_file: *MachO, object_id: u32) !void {
683 const sect_id = self.eh_frame_sect_id orelse return;658 const sect_id = self.eh_frame_sect_id orelse return;
684 const sect = self.getSourceSection(sect_id);659 const sect = self.getSourceSection(sect_id);
685660
686 log.debug("parsing __TEXT,__eh_frame section", .{});661 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) {665 if (macho_file.eh_frame_section_index == null) {
691 zld.eh_frame_section_index = try MachO.initSection(gpa, zld, "__TEXT", "__eh_frame", .{});666 macho_file.eh_frame_section_index = try macho_file.initSection("__TEXT", "__eh_frame", .{});
692 }667 }
693668
694 const cpu_arch = zld.options.target.cpu.arch;669 const cpu_arch = macho_file.base.options.target.cpu.arch;
695 try self.parseRelocs(gpa, sect_id);670 try self.parseRelocs(gpa, sect_id);
696 const relocs = self.getRelocs(sect_id);671 const relocs = self.getRelocs(sect_id);
697672
...@@ -729,7 +704,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -729,7 +704,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
729 @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_UNSIGNED)704 @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_UNSIGNED)
730 break rel;705 break rel;
731 } else unreachable;706 } else unreachable;
732 const target = Atom.parseRelocTarget(zld, .{707 const target = Atom.parseRelocTarget(macho_file, .{
733 .object_id = object_id,708 .object_id = object_id,
734 .rel = rel,709 .rel = rel,
735 .code = it.data[offset..],710 .code = it.data[offset..],
...@@ -744,7 +719,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -744,7 +719,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
744 });719 });
745 const target_sym_index = self.getSymbolByAddress(target_address, null);720 const target_sym_index = self.getSymbolByAddress(target_address, null);
746 const target = if (self.getGlobal(target_sym_index)) |global_index|721 const target = if (self.getGlobal(target_sym_index)) |global_index|
747 zld.globals.items[global_index]722 macho_file.globals.items[global_index]
748 else723 else
749 SymbolWithLoc{ .sym_index = target_sym_index, .file = object_id + 1 };724 SymbolWithLoc{ .sym_index = target_sym_index, .file = object_id + 1 };
750 break :blk target;725 break :blk target;
...@@ -770,7 +745,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -770,7 +745,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
770 };745 };
771 log.debug("FDE at offset {x} tracks {s}", .{746 log.debug("FDE at offset {x} tracks {s}", .{
772 offset,747 offset,
773 zld.getSymbolName(actual_target),748 macho_file.getSymbolName(actual_target),
774 });749 });
775 try self.eh_frame_records_lookup.putNoClobber(gpa, actual_target, offset);750 try self.eh_frame_records_lookup.putNoClobber(gpa, actual_target, offset);
776 }751 }
...@@ -779,19 +754,17 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -779,19 +754,17 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
779 }754 }
780}755}
781756
782fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {757fn parseUnwindInfo(self: *Object, macho_file: *MachO, object_id: u32) !void {
783 const gpa = zld.gpa;758 const gpa = macho_file.base.allocator;
784 const cpu_arch = zld.options.target.cpu.arch;759 const cpu_arch = macho_file.base.options.target.cpu.arch;
785 const sect_id = self.unwind_info_sect_id orelse {760 const sect_id = self.unwind_info_sect_id orelse {
786 // If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`,761 // If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`,
787 // we will try fully synthesising unwind info records to somewhat match Apple ld's762 // we will try fully synthesising unwind info records to somewhat match Apple ld's
788 // approach. However, we will only synthesise DWARF records and nothing more. For this reason,763 // approach. However, we will only synthesise DWARF records and nothing more. For this reason,
789 // we still create the output `__TEXT,__unwind_info` section.764 // we still create the output `__TEXT,__unwind_info` section.
790 if (self.hasEhFrameRecords()) {765 if (self.hasEhFrameRecords()) {
791 if (zld.unwind_info_section_index == null) {766 if (macho_file.unwind_info_section_index == null) {
792 zld.unwind_info_section_index = try MachO.initSection(767 macho_file.unwind_info_section_index = try macho_file.initSection(
793 gpa,
794 zld,
795 "__TEXT",768 "__TEXT",
796 "__unwind_info",769 "__unwind_info",
797 .{},770 .{},
...@@ -803,8 +776,8 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -803,8 +776,8 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
803776
804 log.debug("parsing unwind info in {s}", .{self.name});777 log.debug("parsing unwind info in {s}", .{self.name});
805778
806 if (zld.unwind_info_section_index == null) {779 if (macho_file.unwind_info_section_index == null) {
807 zld.unwind_info_section_index = try MachO.initSection(gpa, zld, "__TEXT", "__unwind_info", .{});780 macho_file.unwind_info_section_index = try macho_file.initSection("__TEXT", "__unwind_info", .{});
808 }781 }
809782
810 const unwind_records = self.getUnwindRecords();783 const unwind_records = self.getUnwindRecords();
...@@ -839,7 +812,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -839,7 +812,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
839812
840 // Find function symbol that this record describes813 // Find function symbol that this record describes
841 const rel = relocs[rel_pos.start..][rel_pos.len - 1];814 const rel = relocs[rel_pos.start..][rel_pos.len - 1];
842 const target = Atom.parseRelocTarget(zld, .{815 const target = Atom.parseRelocTarget(macho_file, .{
843 .object_id = object_id,816 .object_id = object_id,
844 .rel = rel,817 .rel = rel,
845 .code = mem.asBytes(&record),818 .code = mem.asBytes(&record),
...@@ -863,7 +836,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -863,7 +836,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
863 };836 };
864 log.debug("unwind record {d} tracks {s}", .{837 log.debug("unwind record {d} tracks {s}", .{
865 record_id,838 record_id,
866 zld.getSymbolName(actual_target),839 macho_file.getSymbolName(actual_target),
867 });840 });
868 try self.unwind_records_lookup.putNoClobber(gpa, actual_target, @intCast(record_id));841 try self.unwind_records_lookup.putNoClobber(gpa, actual_target, @intCast(record_id));
869 }842 }
...@@ -1094,3 +1067,27 @@ pub fn getEhFrameRecordsIterator(self: Object) eh_frame.Iterator {...@@ -1094,3 +1067,27 @@ pub fn getEhFrameRecordsIterator(self: Object) eh_frame.Iterator {
1094pub fn hasDataInCode(self: Object) bool {1067pub fn hasDataInCode(self: Object) bool {
1095 return self.data_in_code.items.len > 0;1068 return self.data_in_code.items.len > 0;
1096}1069}
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 @@...@@ -28,248 +28,6 @@
28//! After the optional exported symbol information is a byte of how many edges (0-255) that28//! After the optional exported symbol information is a byte of how many edges (0-255) that
29//! this node has leaving it, followed by each edge. Each edge is a zero terminated UTF8 of29//! this node has leaving it, followed by each edge. Each edge is a zero terminated UTF8 of
30//! the addition chars in the symbol, followed by a uleb128 offset for the node that edge points to.30//! 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
273/// The root node of the trie.31/// The root node of the trie.
274root: ?*Node = null,32root: ?*Node = null,
27533
...@@ -611,3 +369,245 @@ test "ordering bug" {...@@ -611,3 +369,245 @@ test "ordering bug" {
611 _ = try trie.write(stream.writer());369 _ = try trie.write(stream.writer());
612 try expectEqualHexStrings(&exp_buffer, buffer);370 try expectEqualHexStrings(&exp_buffer, buffer);
613}371}
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 @@...@@ -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
23gpa: Allocator,1gpa: Allocator,
242
25/// List of all unwind records gathered from all objects and sorted3/// List of all unwind records gathered from all objects and sorted
...@@ -203,28 +181,28 @@ pub fn deinit(info: *UnwindInfo) void {...@@ -203,28 +181,28 @@ pub fn deinit(info: *UnwindInfo) void {
203 info.lsdas_lookup.deinit(info.gpa);181 info.lsdas_lookup.deinit(info.gpa);
204}182}
205183
206pub fn scanRelocs(zld: *Zld) !void {184pub fn scanRelocs(macho_file: *MachO) !void {
207 if (zld.unwind_info_section_index == null) return;185 if (macho_file.unwind_info_section_index == null) return;
208186
209 const cpu_arch = zld.options.target.cpu.arch;187 const cpu_arch = macho_file.base.options.target.cpu.arch;
210 for (zld.objects.items, 0..) |*object, object_id| {188 for (macho_file.objects.items, 0..) |*object, object_id| {
211 const unwind_records = object.getUnwindRecords();189 const unwind_records = object.getUnwindRecords();
212 for (object.exec_atoms.items) |atom_index| {190 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);
214 while (inner_syms_it.next()) |sym| {192 while (inner_syms_it.next()) |sym| {
215 const record_id = object.unwind_records_lookup.get(sym) orelse continue;193 const record_id = object.unwind_records_lookup.get(sym) orelse continue;
216 if (object.unwind_relocs_lookup[record_id].dead) continue;194 if (object.unwind_relocs_lookup[record_id].dead) continue;
217 const record = unwind_records[record_id];195 const record = unwind_records[record_id];
218 if (!UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {196 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| {
220 // Personality function; add GOT pointer.198 // Personality function; add GOT pointer.
221 const target = Atom.parseRelocTarget(zld, .{199 const target = Atom.parseRelocTarget(macho_file, .{
222 .object_id = @as(u32, @intCast(object_id)),200 .object_id = @as(u32, @intCast(object_id)),
223 .rel = rel,201 .rel = rel,
224 .code = mem.asBytes(&record),202 .code = mem.asBytes(&record),
225 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),203 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
226 });204 });
227 try zld.addGotEntry(target);205 try macho_file.addGotEntry(target);
228 }206 }
229 }207 }
230 }208 }
...@@ -232,10 +210,10 @@ pub fn scanRelocs(zld: *Zld) !void {...@@ -232,10 +210,10 @@ pub fn scanRelocs(zld: *Zld) !void {
232 }210 }
233}211}
234212
235pub fn collect(info: *UnwindInfo, zld: *Zld) !void {213pub fn collect(info: *UnwindInfo, macho_file: *MachO) !void {
236 if (zld.unwind_info_section_index == null) return;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
240 var records = std.ArrayList(macho.compact_unwind_entry).init(info.gpa);218 var records = std.ArrayList(macho.compact_unwind_entry).init(info.gpa);
241 defer records.deinit();219 defer records.deinit();
...@@ -244,7 +222,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {...@@ -244,7 +222,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
244 defer sym_indexes.deinit();222 defer sym_indexes.deinit();
245223
246 // TODO handle dead stripping224 // TODO handle dead stripping
247 for (zld.objects.items, 0..) |*object, object_id| {225 for (macho_file.objects.items, 0..) |*object, object_id| {
248 log.debug("collecting unwind records in {s} ({d})", .{ object.name, object_id });226 log.debug("collecting unwind records in {s} ({d})", .{ object.name, object_id });
249 const unwind_records = object.getUnwindRecords();227 const unwind_records = object.getUnwindRecords();
250228
...@@ -254,7 +232,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {...@@ -254,7 +232,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
254 try sym_indexes.ensureUnusedCapacity(object.exec_atoms.items.len);232 try sym_indexes.ensureUnusedCapacity(object.exec_atoms.items.len);
255233
256 for (object.exec_atoms.items) |atom_index| {234 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);
258 var prev_symbol: ?SymbolWithLoc = null;236 var prev_symbol: ?SymbolWithLoc = null;
259 while (inner_syms_it.next()) |symbol| {237 while (inner_syms_it.next()) |symbol| {
260 var record = if (object.unwind_records_lookup.get(symbol)) |record_id| blk: {238 var record = if (object.unwind_records_lookup.get(symbol)) |record_id| blk: {
...@@ -262,14 +240,14 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {...@@ -262,14 +240,14 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
262 var record = unwind_records[record_id];240 var record = unwind_records[record_id];
263241
264 if (UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {242 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);
266 } else {244 } else {
267 if (getPersonalityFunctionReloc(245 if (getPersonalityFunctionReloc(
268 zld,246 macho_file,
269 @as(u32, @intCast(object_id)),247 @as(u32, @intCast(object_id)),
270 record_id,248 record_id,
271 )) |rel| {249 )) |rel| {
272 const target = Atom.parseRelocTarget(zld, .{250 const target = Atom.parseRelocTarget(macho_file, .{
273 .object_id = @as(u32, @intCast(object_id)),251 .object_id = @as(u32, @intCast(object_id)),
274 .rel = rel,252 .rel = rel,
275 .code = mem.asBytes(&record),253 .code = mem.asBytes(&record),
...@@ -286,8 +264,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {...@@ -286,8 +264,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
286 UnwindEncoding.setPersonalityIndex(&record.compactUnwindEncoding, personality_index + 1);264 UnwindEncoding.setPersonalityIndex(&record.compactUnwindEncoding, personality_index + 1);
287 }265 }
288266
289 if (getLsdaReloc(zld, @as(u32, @intCast(object_id)), record_id)) |rel| {267 if (getLsdaReloc(macho_file, @as(u32, @intCast(object_id)), record_id)) |rel| {
290 const target = Atom.parseRelocTarget(zld, .{268 const target = Atom.parseRelocTarget(macho_file, .{
291 .object_id = @as(u32, @intCast(object_id)),269 .object_id = @as(u32, @intCast(object_id)),
292 .rel = rel,270 .rel = rel,
293 .code = mem.asBytes(&record),271 .code = mem.asBytes(&record),
...@@ -298,7 +276,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {...@@ -298,7 +276,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
298 }276 }
299 break :blk record;277 break :blk record;
300 } else blk: {278 } else blk: {
301 const sym = zld.getSymbol(symbol);279 const sym = macho_file.getSymbol(symbol);
302 if (sym.n_desc == MachO.N_DEAD) continue;280 if (sym.n_desc == MachO.N_DEAD) continue;
303 if (prev_symbol) |prev_sym| {281 if (prev_symbol) |prev_sym| {
304 const prev_addr = object.getSourceSymbol(prev_sym.sym_index).?.n_value;282 const prev_addr = object.getSourceSymbol(prev_sym.sym_index).?.n_value;
...@@ -310,7 +288,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {...@@ -310,7 +288,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
310 if (object.eh_frame_records_lookup.get(symbol)) |fde_offset| {288 if (object.eh_frame_records_lookup.get(symbol)) |fde_offset| {
311 if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;289 if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
312 var record = nullRecord();290 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);
314 switch (cpu_arch) {292 switch (cpu_arch) {
315 .aarch64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_ARM64_MODE.DWARF),293 .aarch64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_ARM64_MODE.DWARF),
316 .x86_64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_X86_64_MODE.DWARF),294 .x86_64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_X86_64_MODE.DWARF),
...@@ -323,8 +301,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {...@@ -323,8 +301,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
323 break :blk nullRecord();301 break :blk nullRecord();
324 };302 };
325303
326 const atom = zld.getAtom(atom_index);304 const atom = macho_file.getAtom(atom_index);
327 const sym = zld.getSymbol(symbol);305 const sym = macho_file.getSymbol(symbol);
328 assert(sym.n_desc != MachO.N_DEAD);306 assert(sym.n_desc != MachO.N_DEAD);
329 const size = if (inner_syms_it.next()) |next_sym| blk: {307 const size = if (inner_syms_it.next()) |next_sym| blk: {
330 // All this trouble to account for symbol aliases.308 // All this trouble to account for symbol aliases.
...@@ -336,8 +314,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {...@@ -336,8 +314,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
336 const curr_addr = object.getSourceSymbol(symbol.sym_index).?.n_value;314 const curr_addr = object.getSourceSymbol(symbol.sym_index).?.n_value;
337 const next_addr = object.getSourceSymbol(next_sym.sym_index).?.n_value;315 const next_addr = object.getSourceSymbol(next_sym.sym_index).?.n_value;
338 if (next_addr > curr_addr) break :blk next_addr - curr_addr;316 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;317 break :blk macho_file.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
340 } else zld.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;318 } else macho_file.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
341 record.rangeStart = sym.n_value;319 record.rangeStart = sym.n_value;
342 record.rangeLength = @as(u32, @intCast(size));320 record.rangeLength = @as(u32, @intCast(size));
343321
...@@ -518,23 +496,23 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {...@@ -518,23 +496,23 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
518496
519fn collectPersonalityFromDwarf(497fn collectPersonalityFromDwarf(
520 info: *UnwindInfo,498 info: *UnwindInfo,
521 zld: *Zld,499 macho_file: *MachO,
522 object_id: u32,500 object_id: u32,
523 sym_loc: SymbolWithLoc,501 sym_loc: SymbolWithLoc,
524 record: *macho.compact_unwind_entry,502 record: *macho.compact_unwind_entry,
525) !void {503) !void {
526 const object = &zld.objects.items[object_id];504 const object = &macho_file.objects.items[object_id];
527 var it = object.getEhFrameRecordsIterator();505 var it = object.getEhFrameRecordsIterator();
528 const fde_offset = object.eh_frame_records_lookup.get(sym_loc).?;506 const fde_offset = object.eh_frame_records_lookup.get(sym_loc).?;
529 it.seekTo(fde_offset);507 it.seekTo(fde_offset);
530 const fde = (try it.next()).?;508 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);
532 const cie_offset = fde_offset + 4 - cie_ptr;510 const cie_offset = fde_offset + 4 - cie_ptr;
533 it.seekTo(cie_offset);511 it.seekTo(cie_offset);
534 const cie = (try it.next()).?;512 const cie = (try it.next()).?;
535513
536 if (cie.getPersonalityPointerReloc(514 if (cie.getPersonalityPointerReloc(
537 zld,515 macho_file,
538 @as(u32, @intCast(object_id)),516 @as(u32, @intCast(object_id)),
539 cie_offset,517 cie_offset,
540 )) |target| {518 )) |target| {
...@@ -550,9 +528,9 @@ fn collectPersonalityFromDwarf(...@@ -550,9 +528,9 @@ fn collectPersonalityFromDwarf(
550 }528 }
551}529}
552530
553pub fn calcSectionSize(info: UnwindInfo, zld: *Zld) !void {531pub fn calcSectionSize(info: UnwindInfo, macho_file: *MachO) !void {
554 const sect_id = zld.unwind_info_section_index orelse return;532 const sect_id = macho_file.unwind_info_section_index orelse return;
555 const sect = &zld.sections.items(.header)[sect_id];533 const sect = &macho_file.sections.items(.header)[sect_id];
556 sect.@"align" = 2;534 sect.@"align" = 2;
557 sect.size = info.calcRequiredSize();535 sect.size = info.calcRequiredSize();
558}536}
...@@ -569,23 +547,23 @@ fn calcRequiredSize(info: UnwindInfo) usize {...@@ -569,23 +547,23 @@ fn calcRequiredSize(info: UnwindInfo) usize {
569 return total_size;547 return total_size;
570}548}
571549
572pub fn write(info: *UnwindInfo, zld: *Zld) !void {550pub fn write(info: *UnwindInfo, macho_file: *MachO) !void {
573 const sect_id = zld.unwind_info_section_index orelse return;551 const sect_id = macho_file.unwind_info_section_index orelse return;
574 const sect = &zld.sections.items(.header)[sect_id];552 const sect = &macho_file.sections.items(.header)[sect_id];
575 const seg_id = zld.sections.items(.segment_index)[sect_id];553 const seg_id = macho_file.sections.items(.segment_index)[sect_id];
576 const seg = zld.segments.items[seg_id];554 const seg = macho_file.segments.items[seg_id];
577555
578 const text_sect_id = zld.text_section_index.?;556 const text_sect_id = macho_file.text_section_index.?;
579 const text_sect = zld.sections.items(.header)[text_sect_id];557 const text_sect = macho_file.sections.items(.header)[text_sect_id];
580558
581 var personalities: [max_personalities]u32 = undefined;559 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
584 log.debug("Personalities:", .{});562 log.debug("Personalities:", .{});
585 for (info.personalities[0..info.personalities_count], 0..) |target, i| {563 for (info.personalities[0..info.personalities_count], 0..) |target, i| {
586 const addr = zld.getGotEntryAddress(target).?;564 const addr = macho_file.getGotEntryAddress(target).?;
587 personalities[i] = @as(u32, @intCast(addr - seg.vmaddr));565 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) });
589 }567 }
590568
591 for (info.records.items) |*rec| {569 for (info.records.items) |*rec| {
...@@ -599,7 +577,7 @@ pub fn write(info: *UnwindInfo, zld: *Zld) !void {...@@ -599,7 +577,7 @@ pub fn write(info: *UnwindInfo, zld: *Zld) !void {
599 if (rec.compactUnwindEncoding > 0 and !UnwindEncoding.isDwarf(rec.compactUnwindEncoding, cpu_arch)) {577 if (rec.compactUnwindEncoding > 0 and !UnwindEncoding.isDwarf(rec.compactUnwindEncoding, cpu_arch)) {
600 const lsda_target = @as(SymbolWithLoc, @bitCast(rec.lsda));578 const lsda_target = @as(SymbolWithLoc, @bitCast(rec.lsda));
601 if (lsda_target.getFile()) |_| {579 if (lsda_target.getFile()) |_| {
602 const sym = zld.getSymbol(lsda_target);580 const sym = macho_file.getSymbol(lsda_target);
603 rec.lsda = sym.n_value - seg.vmaddr;581 rec.lsda = sym.n_value - seg.vmaddr;
604 }582 }
605 }583 }
...@@ -689,11 +667,11 @@ pub fn write(info: *UnwindInfo, zld: *Zld) !void {...@@ -689,11 +667,11 @@ pub fn write(info: *UnwindInfo, zld: *Zld) !void {
689 @memset(buffer.items[offset..], 0);667 @memset(buffer.items[offset..], 0);
690 }668 }
691669
692 try zld.file.pwriteAll(buffer.items, sect.offset);670 try macho_file.base.file.?.pwriteAll(buffer.items, sect.offset);
693}671}
694672
695fn getRelocs(zld: *Zld, object_id: u32, record_id: usize) []const macho.relocation_info {673fn getRelocs(macho_file: *MachO, object_id: u32, record_id: usize) []const macho.relocation_info {
696 const object = &zld.objects.items[object_id];674 const object = &macho_file.objects.items[object_id];
697 assert(object.hasUnwindRecords());675 assert(object.hasUnwindRecords());
698 const rel_pos = object.unwind_relocs_lookup[record_id].reloc;676 const rel_pos = object.unwind_relocs_lookup[record_id].reloc;
699 const relocs = object.getRelocs(object.unwind_info_sect_id.?);677 const relocs = object.getRelocs(object.unwind_info_sect_id.?);
...@@ -707,11 +685,11 @@ fn isPersonalityFunction(record_id: usize, rel: macho.relocation_info) bool {...@@ -707,11 +685,11 @@ fn isPersonalityFunction(record_id: usize, rel: macho.relocation_info) bool {
707}685}
708686
709pub fn getPersonalityFunctionReloc(687pub fn getPersonalityFunctionReloc(
710 zld: *Zld,688 macho_file: *MachO,
711 object_id: u32,689 object_id: u32,
712 record_id: usize,690 record_id: usize,
713) ?macho.relocation_info {691) ?macho.relocation_info {
714 const relocs = getRelocs(zld, object_id, record_id);692 const relocs = getRelocs(macho_file, object_id, record_id);
715 for (relocs) |rel| {693 for (relocs) |rel| {
716 if (isPersonalityFunction(record_id, rel)) return rel;694 if (isPersonalityFunction(record_id, rel)) return rel;
717 }695 }
...@@ -735,8 +713,8 @@ fn isLsda(record_id: usize, rel: macho.relocation_info) bool {...@@ -735,8 +713,8 @@ fn isLsda(record_id: usize, rel: macho.relocation_info) bool {
735 return rel_offset == 24;713 return rel_offset == 24;
736}714}
737715
738pub fn getLsdaReloc(zld: *Zld, object_id: u32, record_id: usize) ?macho.relocation_info {716pub fn getLsdaReloc(macho_file: *MachO, object_id: u32, record_id: usize) ?macho.relocation_info {
739 const relocs = getRelocs(zld, object_id, record_id);717 const relocs = getRelocs(macho_file, object_id, record_id);
740 for (relocs) |rel| {718 for (relocs) |rel| {
741 if (isLsda(record_id, rel)) return rel;719 if (isLsda(record_id, rel)) return rel;
742 }720 }
...@@ -828,3 +806,23 @@ pub const UnwindEncoding = struct {...@@ -828,3 +806,23 @@ pub const UnwindEncoding = struct {
828 enc.* |= offset;806 enc.* |= offset;
829 }807 }
830};808};
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 @@...@@ -1,89 +1,72 @@
1//! An algorithm for dead stripping of unreferenced Atoms.1//! An algorithm for dead stripping of unreferenced Atoms.
22
3const std = @import("std");3pub fn gcAtoms(macho_file: *MachO) !void {
4const assert = std.debug.assert;4 const gpa = macho_file.base.allocator;
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;
225
23 var arena = std.heap.ArenaAllocator.init(gpa);6 var arena = std.heap.ArenaAllocator.init(gpa);
24 defer arena.deinit();7 defer arena.deinit();
258
26 var roots = AtomTable.init(arena.allocator());9 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
29 var alive = AtomTable.init(arena.allocator());12 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);15 try collectRoots(macho_file, &roots);
33 try mark(zld, roots, &alive);16 try mark(macho_file, roots, &alive);
34 prune(zld, alive);17 prune(macho_file, alive);
35}18}
3619
37fn addRoot(zld: *Zld, roots: *AtomTable, file: u32, sym_loc: SymbolWithLoc) !void {20fn addRoot(macho_file: *MachO, roots: *AtomTable, file: u32, sym_loc: SymbolWithLoc) !void {
38 const sym = zld.getSymbol(sym_loc);21 const sym = macho_file.getSymbol(sym_loc);
39 assert(!sym.undf());22 assert(!sym.undf());
40 const object = &zld.objects.items[file];23 const object = &macho_file.objects.items[file];
41 const atom_index = object.getAtomIndexForSymbol(sym_loc.sym_index).?; // panic here means fatal error24 const atom_index = object.getAtomIndexForSymbol(sym_loc.sym_index).?; // panic here means fatal error
42 log.debug("root(ATOM({d}, %{d}, {d}))", .{25 log.debug("root(ATOM({d}, %{d}, {d}))", .{
43 atom_index,26 atom_index,
44 zld.getAtom(atom_index).sym_index,27 macho_file.getAtom(atom_index).sym_index,
45 file,28 file,
46 });29 });
47 _ = try roots.getOrPut(atom_index);30 _ = try roots.getOrPut(atom_index);
48}31}
4932
50fn collectRoots(zld: *Zld, roots: *AtomTable) !void {33fn collectRoots(macho_file: *MachO, roots: *AtomTable) !void {
51 log.debug("collecting roots", .{});34 log.debug("collecting roots", .{});
5235
53 switch (zld.options.output_mode) {36 switch (macho_file.base.options.output_mode) {
54 .Exe => {37 .Exe => {
55 // Add entrypoint as GC root38 // Add entrypoint as GC root
56 const global: SymbolWithLoc = zld.getEntryPoint();39 const global: SymbolWithLoc = macho_file.getEntryPoint();
57 if (global.getFile()) |file| {40 if (global.getFile()) |file| {
58 try addRoot(zld, roots, file, global);41 try addRoot(macho_file, roots, file, global);
59 } else {42 } 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.
61 }44 }
62 },45 },
63 else => |other| {46 else => |other| {
64 assert(other == .Lib);47 assert(other == .Lib);
65 // Add exports as GC roots48 // Add exports as GC roots
66 for (zld.globals.items) |global| {49 for (macho_file.globals.items) |global| {
67 const sym = zld.getSymbol(global);50 const sym = macho_file.getSymbol(global);
68 if (sym.undf()) continue;51 if (sym.undf()) continue;
6952
70 if (global.getFile()) |file| {53 if (global.getFile()) |file| {
71 try addRoot(zld, roots, file, global);54 try addRoot(macho_file, roots, file, global);
72 }55 }
73 }56 }
74 },57 },
75 }58 }
7659
77 // Add all symbols force-defined by the user.60 // Add all symbols force-defined by the user.
78 for (zld.options.force_undefined_symbols.keys()) |sym_name| {61 for (macho_file.base.options.force_undefined_symbols.keys()) |sym_name| {
79 const global_index = zld.resolver.get(sym_name).?;62 const global_index = macho_file.resolver.get(sym_name).?;
80 const global = zld.globals.items[global_index];63 const global = macho_file.globals.items[global_index];
81 const sym = zld.getSymbol(global);64 const sym = macho_file.getSymbol(global);
82 assert(!sym.undf());65 assert(!sym.undf());
83 try addRoot(zld, roots, global.getFile().?, global);66 try addRoot(macho_file, roots, global.getFile().?, global);
84 }67 }
8568
86 for (zld.objects.items) |object| {69 for (macho_file.objects.items) |object| {
87 const has_subsections = object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;70 const has_subsections = object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
8871
89 for (object.atoms.items) |atom_index| {72 for (object.atoms.items) |atom_index| {
...@@ -92,7 +75,7 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {...@@ -92,7 +75,7 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
92 // as a root.75 // as a root.
93 if (!has_subsections) break :blk true;76 if (!has_subsections) break :blk true;
9477
95 const atom = zld.getAtom(atom_index);78 const atom = macho_file.getAtom(atom_index);
96 const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym|79 const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
97 source_sym.n_sect - 180 source_sym.n_sect - 1
98 else sect_id: {81 else sect_id: {
...@@ -115,39 +98,39 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {...@@ -115,39 +98,39 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
11598
116 log.debug("root(ATOM({d}, %{d}, {?d}))", .{99 log.debug("root(ATOM({d}, %{d}, {?d}))", .{
117 atom_index,100 atom_index,
118 zld.getAtom(atom_index).sym_index,101 macho_file.getAtom(atom_index).sym_index,
119 zld.getAtom(atom_index).getFile(),102 macho_file.getAtom(atom_index).getFile(),
120 });103 });
121 }104 }
122 }105 }
123 }106 }
124}107}
125108
126fn markLive(zld: *Zld, atom_index: Atom.Index, alive: *AtomTable) void {109fn markLive(macho_file: *MachO, atom_index: Atom.Index, alive: *AtomTable) void {
127 if (alive.contains(atom_index)) return;110 if (alive.contains(atom_index)) return;
128111
129 const atom = zld.getAtom(atom_index);112 const atom = macho_file.getAtom(atom_index);
130 const sym_loc = atom.getSymbolWithLoc();113 const sym_loc = atom.getSymbolWithLoc();
131114
132 log.debug("mark(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() });115 log.debug("mark(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() });
133116
134 alive.putAssumeCapacityNoClobber(atom_index, {});117 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());121 const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
139 const header = zld.sections.items(.header)[sym.n_sect - 1];122 const header = macho_file.sections.items(.header)[sym.n_sect - 1];
140 if (header.isZerofill()) return;123 if (header.isZerofill()) return;
141124
142 const code = Atom.getAtomCode(zld, atom_index);125 const code = Atom.getAtomCode(macho_file, atom_index);
143 const relocs = Atom.getAtomRelocs(zld, atom_index);126 const relocs = Atom.getAtomRelocs(macho_file, atom_index);
144 const ctx = Atom.getRelocContext(zld, atom_index);127 const ctx = Atom.getRelocContext(macho_file, atom_index);
145128
146 for (relocs) |rel| {129 for (relocs) |rel| {
147 const target = switch (cpu_arch) {130 const target = switch (cpu_arch) {
148 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {131 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
149 .ARM64_RELOC_ADDEND => continue,132 .ARM64_RELOC_ADDEND => continue,
150 else => Atom.parseRelocTarget(zld, .{133 else => Atom.parseRelocTarget(macho_file, .{
151 .object_id = atom.getFile().?,134 .object_id = atom.getFile().?,
152 .rel = rel,135 .rel = rel,
153 .code = code,136 .code = code,
...@@ -155,7 +138,7 @@ fn markLive(zld: *Zld, atom_index: Atom.Index, alive: *AtomTable) void {...@@ -155,7 +138,7 @@ fn markLive(zld: *Zld, atom_index: Atom.Index, alive: *AtomTable) void {
155 .base_addr = ctx.base_addr,138 .base_addr = ctx.base_addr,
156 }),139 }),
157 },140 },
158 .x86_64 => Atom.parseRelocTarget(zld, .{141 .x86_64 => Atom.parseRelocTarget(macho_file, .{
159 .object_id = atom.getFile().?,142 .object_id = atom.getFile().?,
160 .rel = rel,143 .rel = rel,
161 .code = code,144 .code = code,
...@@ -164,50 +147,50 @@ fn markLive(zld: *Zld, atom_index: Atom.Index, alive: *AtomTable) void {...@@ -164,50 +147,50 @@ fn markLive(zld: *Zld, atom_index: Atom.Index, alive: *AtomTable) void {
164 }),147 }),
165 else => unreachable,148 else => unreachable,
166 };149 };
167 const target_sym = zld.getSymbol(target);150 const target_sym = macho_file.getSymbol(target);
168151
169 if (target_sym.undf()) continue;152 if (target_sym.undf()) continue;
170 if (target.getFile() == null) {153 if (target.getFile() == null) {
171 const target_sym_name = zld.getSymbolName(target);154 const target_sym_name = macho_file.getSymbolName(target);
172 if (mem.eql(u8, "__mh_execute_header", target_sym_name)) continue;155 if (mem.eql(u8, "__mh_execute_header", target_sym_name)) continue;
173 if (mem.eql(u8, "___dso_handle", target_sym_name)) continue;156 if (mem.eql(u8, "___dso_handle", target_sym_name)) continue;
174157
175 unreachable; // referenced symbol not found158 unreachable; // referenced symbol not found
176 }159 }
177160
178 const object = zld.objects.items[target.getFile().?];161 const object = macho_file.objects.items[target.getFile().?];
179 const target_atom_index = object.getAtomIndexForSymbol(target.sym_index).?;162 const target_atom_index = object.getAtomIndexForSymbol(target.sym_index).?;
180 log.debug(" following ATOM({d}, %{d}, {?d})", .{163 log.debug(" following ATOM({d}, %{d}, {?d})", .{
181 target_atom_index,164 target_atom_index,
182 zld.getAtom(target_atom_index).sym_index,165 macho_file.getAtom(target_atom_index).sym_index,
183 zld.getAtom(target_atom_index).getFile(),166 macho_file.getAtom(target_atom_index).getFile(),
184 });167 });
185168
186 markLive(zld, target_atom_index, alive);169 markLive(macho_file, target_atom_index, alive);
187 }170 }
188}171}
189172
190fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool {173fn refersLive(macho_file: *MachO, atom_index: Atom.Index, alive: AtomTable) bool {
191 const atom = zld.getAtom(atom_index);174 const atom = macho_file.getAtom(atom_index);
192 const sym_loc = atom.getSymbolWithLoc();175 const sym_loc = atom.getSymbolWithLoc();
193176
194 log.debug("refersLive(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() });177 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);181 const sym = macho_file.getSymbol(sym_loc);
199 const header = zld.sections.items(.header)[sym.n_sect - 1];182 const header = macho_file.sections.items(.header)[sym.n_sect - 1];
200 assert(!header.isZerofill());183 assert(!header.isZerofill());
201184
202 const code = Atom.getAtomCode(zld, atom_index);185 const code = Atom.getAtomCode(macho_file, atom_index);
203 const relocs = Atom.getAtomRelocs(zld, atom_index);186 const relocs = Atom.getAtomRelocs(macho_file, atom_index);
204 const ctx = Atom.getRelocContext(zld, atom_index);187 const ctx = Atom.getRelocContext(macho_file, atom_index);
205188
206 for (relocs) |rel| {189 for (relocs) |rel| {
207 const target = switch (cpu_arch) {190 const target = switch (cpu_arch) {
208 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {191 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
209 .ARM64_RELOC_ADDEND => continue,192 .ARM64_RELOC_ADDEND => continue,
210 else => Atom.parseRelocTarget(zld, .{193 else => Atom.parseRelocTarget(macho_file, .{
211 .object_id = atom.getFile().?,194 .object_id = atom.getFile().?,
212 .rel = rel,195 .rel = rel,
213 .code = code,196 .code = code,
...@@ -215,7 +198,7 @@ fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool {...@@ -215,7 +198,7 @@ fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool {
215 .base_addr = ctx.base_addr,198 .base_addr = ctx.base_addr,
216 }),199 }),
217 },200 },
218 .x86_64 => Atom.parseRelocTarget(zld, .{201 .x86_64 => Atom.parseRelocTarget(macho_file, .{
219 .object_id = atom.getFile().?,202 .object_id = atom.getFile().?,
220 .rel = rel,203 .rel = rel,
221 .code = code,204 .code = code,
...@@ -225,16 +208,16 @@ fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool {...@@ -225,16 +208,16 @@ fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool {
225 else => unreachable,208 else => unreachable,
226 };209 };
227210
228 const object = zld.objects.items[target.getFile().?];211 const object = macho_file.objects.items[target.getFile().?];
229 const target_atom_index = object.getAtomIndexForSymbol(target.sym_index) orelse {212 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)});
231 continue;214 continue;
232 };215 };
233 if (alive.contains(target_atom_index)) {216 if (alive.contains(target_atom_index)) {
234 log.debug(" refers live ATOM({d}, %{d}, {?d})", .{217 log.debug(" refers live ATOM({d}, %{d}, {?d})", .{
235 target_atom_index,218 target_atom_index,
236 zld.getAtom(target_atom_index).sym_index,219 macho_file.getAtom(target_atom_index).sym_index,
237 zld.getAtom(target_atom_index).getFile(),220 macho_file.getAtom(target_atom_index).getFile(),
238 });221 });
239 return true;222 return true;
240 }223 }
...@@ -243,21 +226,21 @@ fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool {...@@ -243,21 +226,21 @@ fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool {
243 return false;226 return false;
244}227}
245228
246fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {229fn mark(macho_file: *MachO, roots: AtomTable, alive: *AtomTable) !void {
247 var it = roots.keyIterator();230 var it = roots.keyIterator();
248 while (it.next()) |root| {231 while (it.next()) |root| {
249 markLive(zld, root.*, alive);232 markLive(macho_file, root.*, alive);
250 }233 }
251234
252 var loop: bool = true;235 var loop: bool = true;
253 while (loop) {236 while (loop) {
254 loop = false;237 loop = false;
255238
256 for (zld.objects.items) |object| {239 for (macho_file.objects.items) |object| {
257 for (object.atoms.items) |atom_index| {240 for (object.atoms.items) |atom_index| {
258 if (alive.contains(atom_index)) continue;241 if (alive.contains(atom_index)) continue;
259242
260 const atom = zld.getAtom(atom_index);243 const atom = macho_file.getAtom(atom_index);
261 const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym|244 const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
262 source_sym.n_sect - 1245 source_sym.n_sect - 1
263 else blk: {246 else blk: {
...@@ -268,8 +251,8 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {...@@ -268,8 +251,8 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
268 const source_sect = object.getSourceSection(sect_id);251 const source_sect = object.getSourceSection(sect_id);
269252
270 if (source_sect.isDontDeadStripIfReferencesLive()) {253 if (source_sect.isDontDeadStripIfReferencesLive()) {
271 if (refersLive(zld, atom_index, alive.*)) {254 if (refersLive(macho_file, atom_index, alive.*)) {
272 markLive(zld, atom_index, alive);255 markLive(macho_file, atom_index, alive);
273 loop = true;256 loop = true;
274 }257 }
275 }258 }
...@@ -277,26 +260,26 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {...@@ -277,26 +260,26 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
277 }260 }
278 }261 }
279262
280 for (zld.objects.items, 0..) |_, object_id| {263 for (macho_file.objects.items, 0..) |_, object_id| {
281 // Traverse unwind and eh_frame records noting if the source symbol has been marked, and if so,264 // Traverse unwind and eh_frame records noting if the source symbol has been marked, and if so,
282 // marking all references as live.265 // 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);
284 }267 }
285}268}
286269
287fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void {270fn markUnwindRecords(macho_file: *MachO, object_id: u32, alive: *AtomTable) !void {
288 const object = &zld.objects.items[object_id];271 const object = &macho_file.objects.items[object_id];
289 const cpu_arch = zld.options.target.cpu.arch;272 const cpu_arch = macho_file.base.options.target.cpu.arch;
290273
291 const unwind_records = object.getUnwindRecords();274 const unwind_records = object.getUnwindRecords();
292275
293 for (object.exec_atoms.items) |atom_index| {276 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
296 if (!object.hasUnwindRecords()) {279 if (!object.hasUnwindRecords()) {
297 if (alive.contains(atom_index)) {280 if (alive.contains(atom_index)) {
298 // Mark references live and continue.281 // Mark references live and continue.
299 try markEhFrameRecords(zld, object_id, atom_index, alive);282 try markEhFrameRecords(macho_file, object_id, atom_index, alive);
300 } else {283 } else {
301 while (inner_syms_it.next()) |sym| {284 while (inner_syms_it.next()) |sym| {
302 if (object.eh_frame_records_lookup.get(sym)) |fde_offset| {285 if (object.eh_frame_records_lookup.get(sym)) |fde_offset| {
...@@ -322,51 +305,51 @@ fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void {...@@ -322,51 +305,51 @@ fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void {
322305
323 const record = unwind_records[record_id];306 const record = unwind_records[record_id];
324 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {307 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);
326 } else {309 } else {
327 if (UnwindInfo.getPersonalityFunctionReloc(zld, object_id, record_id)) |rel| {310 if (UnwindInfo.getPersonalityFunctionReloc(macho_file, object_id, record_id)) |rel| {
328 const target = Atom.parseRelocTarget(zld, .{311 const target = Atom.parseRelocTarget(macho_file, .{
329 .object_id = object_id,312 .object_id = object_id,
330 .rel = rel,313 .rel = rel,
331 .code = mem.asBytes(&record),314 .code = mem.asBytes(&record),
332 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),315 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
333 });316 });
334 const target_sym = zld.getSymbol(target);317 const target_sym = macho_file.getSymbol(target);
335 if (!target_sym.undf()) {318 if (!target_sym.undf()) {
336 const target_object = zld.objects.items[target.getFile().?];319 const target_object = macho_file.objects.items[target.getFile().?];
337 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;320 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);
339 }322 }
340 }323 }
341324
342 if (UnwindInfo.getLsdaReloc(zld, object_id, record_id)) |rel| {325 if (UnwindInfo.getLsdaReloc(macho_file, object_id, record_id)) |rel| {
343 const target = Atom.parseRelocTarget(zld, .{326 const target = Atom.parseRelocTarget(macho_file, .{
344 .object_id = object_id,327 .object_id = object_id,
345 .rel = rel,328 .rel = rel,
346 .code = mem.asBytes(&record),329 .code = mem.asBytes(&record),
347 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),330 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
348 });331 });
349 const target_object = zld.objects.items[target.getFile().?];332 const target_object = macho_file.objects.items[target.getFile().?];
350 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;333 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);
352 }335 }
353 }336 }
354 }337 }
355 }338 }
356}339}
357340
358fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive: *AtomTable) !void {341fn markEhFrameRecords(macho_file: *MachO, object_id: u32, atom_index: Atom.Index, alive: *AtomTable) !void {
359 const cpu_arch = zld.options.target.cpu.arch;342 const cpu_arch = macho_file.base.options.target.cpu.arch;
360 const object = &zld.objects.items[object_id];343 const object = &macho_file.objects.items[object_id];
361 var it = object.getEhFrameRecordsIterator();344 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
364 while (inner_syms_it.next()) |sym| {347 while (inner_syms_it.next()) |sym| {
365 const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue; // Continue in case we hit a temp symbol alias348 const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue; // Continue in case we hit a temp symbol alias
366 it.seekTo(fde_offset);349 it.seekTo(fde_offset);
367 const fde = (try it.next()).?;350 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);
370 const cie_offset = fde_offset + 4 - cie_ptr;353 const cie_offset = fde_offset + 4 - cie_ptr;
371 it.seekTo(cie_offset);354 it.seekTo(cie_offset);
372 const cie = (try it.next()).?;355 const cie = (try it.next()).?;
...@@ -374,20 +357,20 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive:...@@ -374,20 +357,20 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive:
374 switch (cpu_arch) {357 switch (cpu_arch) {
375 .aarch64 => {358 .aarch64 => {
376 // Mark FDE references which should include any referenced LSDA record359 // 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);
378 for (relocs) |rel| {361 for (relocs) |rel| {
379 const target = Atom.parseRelocTarget(zld, .{362 const target = Atom.parseRelocTarget(macho_file, .{
380 .object_id = object_id,363 .object_id = object_id,
381 .rel = rel,364 .rel = rel,
382 .code = fde.data,365 .code = fde.data,
383 .base_offset = @as(i32, @intCast(fde_offset)) + 4,366 .base_offset = @as(i32, @intCast(fde_offset)) + 4,
384 });367 });
385 const target_sym = zld.getSymbol(target);368 const target_sym = macho_file.getSymbol(target);
386 if (!target_sym.undf()) blk: {369 if (!target_sym.undf()) blk: {
387 const target_object = zld.objects.items[target.getFile().?];370 const target_object = macho_file.objects.items[target.getFile().?];
388 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index) orelse371 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index) orelse
389 break :blk;372 break :blk;
390 markLive(zld, target_atom_index, alive);373 markLive(macho_file, target_atom_index, alive);
391 }374 }
392 }375 }
393 },376 },
...@@ -401,7 +384,7 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive:...@@ -401,7 +384,7 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive:
401 // Mark LSDA record as live384 // Mark LSDA record as live
402 const sym_index = object.getSymbolByAddress(lsda_address, null);385 const sym_index = object.getSymbolByAddress(lsda_address, null);
403 const target_atom_index = object.getAtomIndexForSymbol(sym_index).?;386 const target_atom_index = object.getAtomIndexForSymbol(sym_index).?;
404 markLive(zld, target_atom_index, alive);387 markLive(macho_file, target_atom_index, alive);
405 }388 }
406 },389 },
407 else => unreachable,390 else => unreachable,
...@@ -409,20 +392,20 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive:...@@ -409,20 +392,20 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive:
409392
410 // Mark CIE references which should include any referenced personalities393 // Mark CIE references which should include any referenced personalities
411 // that are defined locally.394 // that are defined locally.
412 if (cie.getPersonalityPointerReloc(zld, object_id, cie_offset)) |target| {395 if (cie.getPersonalityPointerReloc(macho_file, object_id, cie_offset)) |target| {
413 const target_sym = zld.getSymbol(target);396 const target_sym = macho_file.getSymbol(target);
414 if (!target_sym.undf()) {397 if (!target_sym.undf()) {
415 const target_object = zld.objects.items[target.getFile().?];398 const target_object = macho_file.objects.items[target.getFile().?];
416 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;399 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);
418 }401 }
419 }402 }
420 }403 }
421}404}
422405
423fn prune(zld: *Zld, alive: AtomTable) void {406fn prune(macho_file: *MachO, alive: AtomTable) void {
424 log.debug("pruning dead atoms", .{});407 log.debug("pruning dead atoms", .{});
425 for (zld.objects.items) |*object| {408 for (macho_file.objects.items) |*object| {
426 var i: usize = 0;409 var i: usize = 0;
427 while (i < object.atoms.items.len) {410 while (i < object.atoms.items.len) {
428 const atom_index = object.atoms.items[i];411 const atom_index = object.atoms.items[i];
...@@ -431,7 +414,7 @@ fn prune(zld: *Zld, alive: AtomTable) void {...@@ -431,7 +414,7 @@ fn prune(zld: *Zld, alive: AtomTable) void {
431 continue;414 continue;
432 }415 }
433416
434 const atom = zld.getAtom(atom_index);417 const atom = macho_file.getAtom(atom_index);
435 const sym_loc = atom.getSymbolWithLoc();418 const sym_loc = atom.getSymbolWithLoc();
436419
437 log.debug("prune(ATOM({d}, %{d}, {?d}))", .{420 log.debug("prune(ATOM({d}, %{d}, {?d}))", .{
...@@ -439,15 +422,15 @@ fn prune(zld: *Zld, alive: AtomTable) void {...@@ -439,15 +422,15 @@ fn prune(zld: *Zld, alive: AtomTable) void {
439 sym_loc.sym_index,422 sym_loc.sym_index,
440 sym_loc.getFile(),423 sym_loc.getFile(),
441 });424 });
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);
445 const sect_id = sym.n_sect - 1;428 const sect_id = sym.n_sect - 1;
446 var section = zld.sections.get(sect_id);429 var section = macho_file.sections.get(sect_id);
447 section.header.size -= atom.size;430 section.header.size -= atom.size;
448431
449 if (atom.prev_index) |prev_index| {432 if (atom.prev_index) |prev_index| {
450 const prev = zld.getAtomPtr(prev_index);433 const prev = macho_file.getAtomPtr(prev_index);
451 prev.next_index = atom.next_index;434 prev.next_index = atom.next_index;
452 } else {435 } else {
453 if (atom.next_index) |next_index| {436 if (atom.next_index) |next_index| {
...@@ -455,7 +438,7 @@ fn prune(zld: *Zld, alive: AtomTable) void {...@@ -455,7 +438,7 @@ fn prune(zld: *Zld, alive: AtomTable) void {
455 }438 }
456 }439 }
457 if (atom.next_index) |next_index| {440 if (atom.next_index) |next_index| {
458 const next = zld.getAtomPtr(next_index);441 const next = macho_file.getAtomPtr(next_index);
459 next.prev_index = atom.prev_index;442 next.prev_index = atom.prev_index;
460 } else {443 } else {
461 if (atom.prev_index) |prev_index| {444 if (atom.prev_index) |prev_index| {
...@@ -467,21 +450,37 @@ fn prune(zld: *Zld, alive: AtomTable) void {...@@ -467,21 +450,37 @@ fn prune(zld: *Zld, alive: AtomTable) void {
467 }450 }
468 }451 }
469452
470 zld.sections.set(sect_id, section);453 macho_file.sections.set(sect_id, section);
471 _ = object.atoms.swapRemove(i);454 _ = object.atoms.swapRemove(i);
472455
473 sym.n_desc = MachO.N_DEAD;456 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);
476 while (inner_sym_it.next()) |inner| {459 while (inner_sym_it.next()) |inner| {
477 const inner_sym = zld.getSymbolPtr(inner);460 const inner_sym = macho_file.getSymbolPtr(inner);
478 inner_sym.n_desc = MachO.N_DEAD;461 inner_sym.n_desc = MachO.N_DEAD;
479 }462 }
480463
481 if (Atom.getSectionAlias(zld, atom_index)) |alias| {464 if (Atom.getSectionAlias(macho_file, atom_index)) |alias| {
482 const alias_sym = zld.getSymbolPtr(alias);465 const alias_sym = macho_file.getSymbolPtr(alias);
483 alias_sym.n_desc = MachO.N_DEAD;466 alias_sym.n_desc = MachO.N_DEAD;
484 }467 }
485 }468 }
486 }469 }
487}470}
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 @@...@@ -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
12entries: std.ArrayListUnmanaged(Entry) = .{},1entries: std.ArrayListUnmanaged(Entry) = .{},
13buffer: std.ArrayListUnmanaged(u8) = .{},2buffer: std.ArrayListUnmanaged(u8) = .{},
143
...@@ -572,3 +561,14 @@ test "rebase - composite" {...@@ -572,3 +561,14 @@ test "rebase - composite" {
572 macho.REBASE_OPCODE_DONE,561 macho.REBASE_OPCODE_DONE,
573 }, rebase.buffer.items);562 }, rebase.buffer.items);
574}563}
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 @@...@@ -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
10pub fn Bind(comptime Ctx: type, comptime Target: type) type {1pub fn Bind(comptime Ctx: type, comptime Target: type) type {
11 return struct {2 return struct {
12 entries: std.ArrayListUnmanaged(Entry) = .{},3 entries: std.ArrayListUnmanaged(Entry) = .{},
...@@ -738,3 +729,12 @@ test "lazy bind" {...@@ -738,3 +729,12 @@ test "lazy bind" {
738 macho.BIND_OPCODE_DONE,729 macho.BIND_OPCODE_DONE,
739 }, bind.buffer.items);730 }, bind.buffer.items);
740}731}
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 @@...@@ -1,68 +1,52 @@
1const std = @import("std");1pub fn scanRelocs(macho_file: *MachO) !void {
2const assert = std.debug.assert;2 const gpa = macho_file.base.allocator;
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;
193
20 for (zld.objects.items, 0..) |*object, object_id| {4 for (macho_file.objects.items, 0..) |*object, object_id| {
21 var cies = std.AutoHashMap(u32, void).init(gpa);5 var cies = std.AutoHashMap(u32, void).init(gpa);
22 defer cies.deinit();6 defer cies.deinit();
237
24 var it = object.getEhFrameRecordsIterator();8 var it = object.getEhFrameRecordsIterator();
259
26 for (object.exec_atoms.items) |atom_index| {10 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);
28 while (inner_syms_it.next()) |sym| {12 while (inner_syms_it.next()) |sym| {
29 const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue;13 const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue;
30 if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;14 if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
31 it.seekTo(fde_offset);15 it.seekTo(fde_offset);
32 const fde = (try it.next()).?;16 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);
35 const cie_offset = fde_offset + 4 - cie_ptr;19 const cie_offset = fde_offset + 4 - cie_ptr;
3620
37 if (!cies.contains(cie_offset)) {21 if (!cies.contains(cie_offset)) {
38 try cies.putNoClobber(cie_offset, {});22 try cies.putNoClobber(cie_offset, {});
39 it.seekTo(cie_offset);23 it.seekTo(cie_offset);
40 const cie = (try it.next()).?;24 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);
42 }26 }
43 }27 }
44 }28 }
45 }29 }
46}30}
4731
48pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {32pub fn calcSectionSize(macho_file: *MachO, unwind_info: *const UnwindInfo) !void {
49 const sect_id = zld.eh_frame_section_index orelse return;33 const sect_id = macho_file.eh_frame_section_index orelse return;
50 const sect = &zld.sections.items(.header)[sect_id];34 const sect = &macho_file.sections.items(.header)[sect_id];
51 sect.@"align" = 3;35 sect.@"align" = 3;
52 sect.size = 0;36 sect.size = 0;
5337
54 const cpu_arch = zld.options.target.cpu.arch;38 const cpu_arch = macho_file.base.options.target.cpu.arch;
55 const gpa = zld.gpa;39 const gpa = macho_file.base.allocator;
56 var size: u32 = 0;40 var size: u32 = 0;
5741
58 for (zld.objects.items, 0..) |*object, object_id| {42 for (macho_file.objects.items, 0..) |*object, object_id| {
59 var cies = std.AutoHashMap(u32, u32).init(gpa);43 var cies = std.AutoHashMap(u32, u32).init(gpa);
60 defer cies.deinit();44 defer cies.deinit();
6145
62 var eh_it = object.getEhFrameRecordsIterator();46 var eh_it = object.getEhFrameRecordsIterator();
6347
64 for (object.exec_atoms.items) |atom_index| {48 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);
66 while (inner_syms_it.next()) |sym| {50 while (inner_syms_it.next()) |sym| {
67 const fde_record_offset = object.eh_frame_records_lookup.get(sym) orelse continue;51 const fde_record_offset = object.eh_frame_records_lookup.get(sym) orelse continue;
68 if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;52 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 {...@@ -77,7 +61,7 @@ pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
77 eh_it.seekTo(fde_record_offset);61 eh_it.seekTo(fde_record_offset);
78 const source_fde_record = (try eh_it.next()).?;62 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);
81 const cie_offset = fde_record_offset + 4 - cie_ptr;65 const cie_offset = fde_record_offset + 4 - cie_ptr;
8266
83 const gop = try cies.getOrPut(cie_offset);67 const gop = try cies.getOrPut(cie_offset);
...@@ -96,14 +80,14 @@ pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {...@@ -96,14 +80,14 @@ pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
96 }80 }
97}81}
9882
99pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {83pub fn write(macho_file: *MachO, unwind_info: *UnwindInfo) !void {
100 const sect_id = zld.eh_frame_section_index orelse return;84 const sect_id = macho_file.eh_frame_section_index orelse return;
101 const sect = zld.sections.items(.header)[sect_id];85 const sect = macho_file.sections.items(.header)[sect_id];
102 const seg_id = zld.sections.items(.segment_index)[sect_id];86 const seg_id = macho_file.sections.items(.segment_index)[sect_id];
103 const seg = zld.segments.items[seg_id];87 const seg = macho_file.segments.items[seg_id];
10488
105 const cpu_arch = zld.options.target.cpu.arch;89 const cpu_arch = macho_file.base.options.target.cpu.arch;
106 const gpa = zld.gpa;90 const gpa = macho_file.base.allocator;
10791
108 var eh_records = std.AutoArrayHashMap(u32, EhFrameRecord(true)).init(gpa);92 var eh_records = std.AutoArrayHashMap(u32, EhFrameRecord(true)).init(gpa);
109 defer {93 defer {
...@@ -115,7 +99,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {...@@ -115,7 +99,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
11599
116 var eh_frame_offset: u32 = 0;100 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| {
119 try eh_records.ensureUnusedCapacity(2 * @as(u32, @intCast(object.exec_atoms.items.len)));103 try eh_records.ensureUnusedCapacity(2 * @as(u32, @intCast(object.exec_atoms.items.len)));
120104
121 var cies = std.AutoHashMap(u32, u32).init(gpa);105 var cies = std.AutoHashMap(u32, u32).init(gpa);
...@@ -124,7 +108,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {...@@ -124,7 +108,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
124 var eh_it = object.getEhFrameRecordsIterator();108 var eh_it = object.getEhFrameRecordsIterator();
125109
126 for (object.exec_atoms.items) |atom_index| {110 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);
128 while (inner_syms_it.next()) |target| {112 while (inner_syms_it.next()) |target| {
129 const fde_record_offset = object.eh_frame_records_lookup.get(target) orelse continue;113 const fde_record_offset = object.eh_frame_records_lookup.get(target) orelse continue;
130 if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;114 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 {...@@ -139,7 +123,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
139 eh_it.seekTo(fde_record_offset);123 eh_it.seekTo(fde_record_offset);
140 const source_fde_record = (try eh_it.next()).?;124 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);
143 const cie_offset = fde_record_offset + 4 - cie_ptr;127 const cie_offset = fde_record_offset + 4 - cie_ptr;
144128
145 const gop = try cies.getOrPut(cie_offset);129 const gop = try cies.getOrPut(cie_offset);
...@@ -147,7 +131,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {...@@ -147,7 +131,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
147 eh_it.seekTo(cie_offset);131 eh_it.seekTo(cie_offset);
148 const source_cie_record = (try eh_it.next()).?;132 const source_cie_record = (try eh_it.next()).?;
149 var cie_record = try source_cie_record.toOwned(gpa);133 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)), .{
151 .source_offset = cie_offset,135 .source_offset = cie_offset,
152 .out_offset = eh_frame_offset,136 .out_offset = eh_frame_offset,
153 .sect_addr = sect.addr,137 .sect_addr = sect.addr,
...@@ -158,7 +142,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {...@@ -158,7 +142,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
158 }142 }
159143
160 var fde_record = try source_fde_record.toOwned(gpa);144 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)), .{
162 .source_offset = fde_record_offset,146 .source_offset = fde_record_offset,
163 .out_offset = eh_frame_offset,147 .out_offset = eh_frame_offset,
164 .sect_addr = sect.addr,148 .sect_addr = sect.addr,
...@@ -169,7 +153,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {...@@ -169,7 +153,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
169 .aarch64 => {}, // relocs take care of LSDA pointers153 .aarch64 => {}, // relocs take care of LSDA pointers
170 .x86_64 => {154 .x86_64 => {
171 // We need to relocate target symbol address ourselves.155 // We need to relocate target symbol address ourselves.
172 const atom_sym = zld.getSymbol(target);156 const atom_sym = macho_file.getSymbol(target);
173 try fde_record.setTargetSymbolAddress(atom_sym.n_value, .{157 try fde_record.setTargetSymbolAddress(atom_sym.n_value, .{
174 .base_addr = sect.addr,158 .base_addr = sect.addr,
175 .base_offset = eh_frame_offset,159 .base_offset = eh_frame_offset,
...@@ -229,7 +213,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {...@@ -229,7 +213,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
229 try buffer.appendSlice(record.data);213 try buffer.appendSlice(record.data);
230 }214 }
231215
232 try zld.file.pwriteAll(buffer.items, sect.offset);216 try macho_file.base.file.?.pwriteAll(buffer.items, sect.offset);
233}217}
234const EhFrameRecordTag = enum { cie, fde };218const EhFrameRecordTag = enum { cie, fde };
235219
...@@ -261,12 +245,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {...@@ -261,12 +245,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
261245
262 pub fn scanRelocs(246 pub fn scanRelocs(
263 rec: Record,247 rec: Record,
264 zld: *Zld,248 macho_file: *MachO,
265 object_id: u32,249 object_id: u32,
266 source_offset: u32,250 source_offset: u32,
267 ) !void {251 ) !void {
268 if (rec.getPersonalityPointerReloc(zld, object_id, source_offset)) |target| {252 if (rec.getPersonalityPointerReloc(macho_file, object_id, source_offset)) |target| {
269 try zld.addGotEntry(target);253 try macho_file.addGotEntry(target);
270 }254 }
271 }255 }
272256
...@@ -290,12 +274,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {...@@ -290,12 +274,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
290274
291 pub fn getPersonalityPointerReloc(275 pub fn getPersonalityPointerReloc(
292 rec: Record,276 rec: Record,
293 zld: *Zld,277 macho_file: *MachO,
294 object_id: u32,278 object_id: u32,
295 source_offset: u32,279 source_offset: u32,
296 ) ?SymbolWithLoc {280 ) ?SymbolWithLoc {
297 const cpu_arch = zld.options.target.cpu.arch;281 const cpu_arch = macho_file.base.options.target.cpu.arch;
298 const relocs = getRelocs(zld, object_id, source_offset);282 const relocs = getRelocs(macho_file, object_id, source_offset);
299 for (relocs) |rel| {283 for (relocs) |rel| {
300 switch (cpu_arch) {284 switch (cpu_arch) {
301 .aarch64 => {285 .aarch64 => {
...@@ -317,7 +301,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {...@@ -317,7 +301,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
317 },301 },
318 else => unreachable,302 else => unreachable,
319 }303 }
320 const target = Atom.parseRelocTarget(zld, .{304 const target = Atom.parseRelocTarget(macho_file, .{
321 .object_id = object_id,305 .object_id = object_id,
322 .rel = rel,306 .rel = rel,
323 .code = rec.data,307 .code = rec.data,
...@@ -328,18 +312,18 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {...@@ -328,18 +312,18 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
328 return null;312 return null;
329 }313 }
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 {
332 source_offset: u32,316 source_offset: u32,
333 out_offset: u32,317 out_offset: u32,
334 sect_addr: u64,318 sect_addr: u64,
335 }) !void {319 }) !void {
336 comptime assert(is_mutable);320 comptime assert(is_mutable);
337321
338 const cpu_arch = zld.options.target.cpu.arch;322 const cpu_arch = macho_file.base.options.target.cpu.arch;
339 const relocs = getRelocs(zld, object_id, ctx.source_offset);323 const relocs = getRelocs(macho_file, object_id, ctx.source_offset);
340324
341 for (relocs) |rel| {325 for (relocs) |rel| {
342 const target = Atom.parseRelocTarget(zld, .{326 const target = Atom.parseRelocTarget(macho_file, .{
343 .object_id = object_id,327 .object_id = object_id,
344 .rel = rel,328 .rel = rel,
345 .code = rec.data,329 .code = rec.data,
...@@ -356,14 +340,14 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {...@@ -356,14 +340,14 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
356 // Address of the __eh_frame in the source object file340 // Address of the __eh_frame in the source object file
357 },341 },
358 .ARM64_RELOC_POINTER_TO_GOT => {342 .ARM64_RELOC_POINTER_TO_GOT => {
359 const target_addr = zld.getGotEntryAddress(target).?;343 const target_addr = macho_file.getGotEntryAddress(target).?;
360 const result = math.cast(i32, @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr))) orelse344 const result = math.cast(i32, @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr))) orelse
361 return error.Overflow;345 return error.Overflow;
362 mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], result);346 mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], result);
363 },347 },
364 .ARM64_RELOC_UNSIGNED => {348 .ARM64_RELOC_UNSIGNED => {
365 assert(rel.r_extern == 1);349 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);
367 const result = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr));351 const result = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr));
368 mem.writeIntLittle(i64, rec.data[rel_offset..][0..8], @as(i64, @intCast(result)));352 mem.writeIntLittle(i64, rec.data[rel_offset..][0..8], @as(i64, @intCast(result)));
369 },353 },
...@@ -374,7 +358,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {...@@ -374,7 +358,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
374 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));358 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
375 switch (rel_type) {359 switch (rel_type) {
376 .X86_64_RELOC_GOT => {360 .X86_64_RELOC_GOT => {
377 const target_addr = zld.getGotEntryAddress(target).?;361 const target_addr = macho_file.getGotEntryAddress(target).?;
378 const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]);362 const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]);
379 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));363 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
380 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);364 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
...@@ -388,20 +372,20 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {...@@ -388,20 +372,20 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
388 }372 }
389 }373 }
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 {
392 assert(rec.tag == .fde);376 assert(rec.tag == .fde);
393 const cpu_arch = zld.options.target.cpu.arch;377 const cpu_arch = macho_file.base.options.target.cpu.arch;
394 const addend = mem.readIntLittle(u32, rec.data[0..4]);378 const addend = mem.readIntLittle(u32, rec.data[0..4]);
395 switch (cpu_arch) {379 switch (cpu_arch) {
396 .aarch64 => {380 .aarch64 => {
397 const relocs = getRelocs(zld, object_id, offset);381 const relocs = getRelocs(macho_file, object_id, offset);
398 const maybe_rel = for (relocs) |rel| {382 const maybe_rel = for (relocs) |rel| {
399 if (rel.r_address - @as(i32, @intCast(offset)) == 4 and383 if (rel.r_address - @as(i32, @intCast(offset)) == 4 and
400 @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_SUBTRACTOR)384 @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_SUBTRACTOR)
401 break rel;385 break rel;
402 } else null;386 } else null;
403 const rel = maybe_rel orelse return addend;387 const rel = maybe_rel orelse return addend;
404 const object = &zld.objects.items[object_id];388 const object = &macho_file.objects.items[object_id];
405 const target_addr = object.in_symtab.?[rel.r_symbolnum].n_value;389 const target_addr = object.in_symtab.?[rel.r_symbolnum].n_value;
406 const sect = object.getSourceSection(object.eh_frame_sect_id.?);390 const sect = object.getSourceSection(object.eh_frame_sect_id.?);
407 return @intCast(sect.addr + offset - target_addr + addend);391 return @intCast(sect.addr + offset - target_addr + addend);
...@@ -583,8 +567,8 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {...@@ -583,8 +567,8 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
583 };567 };
584}568}
585569
586pub fn getRelocs(zld: *Zld, object_id: u32, source_offset: u32) []const macho.relocation_info {570pub fn getRelocs(macho_file: *MachO, object_id: u32, source_offset: u32) []const macho.relocation_info {
587 const object = &zld.objects.items[object_id];571 const object = &macho_file.objects.items[object_id];
588 assert(object.hasEhFrameRecords());572 assert(object.hasEhFrameRecords());
589 const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse573 const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse
590 return &[0]macho.relocation_info{};574 return &[0]macho.relocation_info{};
...@@ -650,3 +634,18 @@ pub const EH_PE = struct {...@@ -650,3 +634,18 @@ pub const EH_PE = struct {
650 pub const indirect = 0x80;634 pub const indirect = 0x80;
651 pub const omit = 0xFF;635 pub const omit = 0xFF;
652};636};
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 @@...@@ -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
7pub fn isFatLibrary(file: std.fs.File) bool {1pub fn isFatLibrary(file: std.fs.File) bool {
8 const reader = file.reader();2 const reader = file.reader();
9 const hdr = reader.readStructBig(macho.fat_header) catch return false;3 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 {...@@ -38,3 +32,9 @@ pub fn parseArchs(file: std.fs.File, buffer: *[2]Arch) ![]const Arch {
3832
39 return buffer[0..count];33 return buffer[0..count];
40}34}
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 @@...@@ -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
10pub fn ParallelHasher(comptime Hasher: type) type {1pub fn ParallelHasher(comptime Hasher: type) type {
11 const hash_size = Hasher.digest_length;2 const hash_size = Hasher.digest_length;
123
...@@ -69,3 +60,12 @@ pub fn ParallelHasher(comptime Hasher: type) type {...@@ -69,3 +60,12 @@ pub fn ParallelHasher(comptime Hasher: type) type {
69 const Self = @This();60 const Self = @This();
70 };61 };
71}62}
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 @@...@@ -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
11/// Default implicit entrypoint symbol name.1/// Default implicit entrypoint symbol name.
12pub const default_entry_point: []const u8 = "_main";2pub const default_entry_point: []const u8 = "_main";
133
...@@ -374,3 +364,13 @@ test "parseSdkVersion" {...@@ -374,3 +364,13 @@ test "parseSdkVersion" {
374364
375 try expect(parseSdkVersion("11") == null);365 try expect(parseSdkVersion("11") == null);
376}366}
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 @@...@@ -1,8 +1,3 @@
1const std = @import("std");
2const aarch64 = @import("../../arch/aarch64/bits.zig");
3
4const Relocation = @import("Relocation.zig");
5
6pub inline fn stubHelperPreambleSize(cpu_arch: std.Target.Cpu.Arch) u8 {1pub inline fn stubHelperPreambleSize(cpu_arch: std.Target.Cpu.Arch) u8 {
7 return switch (cpu_arch) {2 return switch (cpu_arch) {
8 .x86_64 => 15,3 .x86_64 => 15,
...@@ -167,3 +162,8 @@ pub fn writeStubCode(args: struct {...@@ -167,3 +162,8 @@ pub fn writeStubCode(args: struct {
167 else => unreachable,162 else => unreachable,
168 }163 }
169}164}
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 @@...@@ -5,22 +5,6 @@
5//! The algorithm works pessimistically and assumes that any reference to an Atom in5//! The algorithm works pessimistically and assumes that any reference to an Atom in
6//! another output section is out of range.6//! 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
24/// Branch instruction has 26 bits immediate but 4 byte aligned.8/// Branch instruction has 26 bits immediate but 4 byte aligned.
25const jump_bits = @bitSizeOf(i28);9const jump_bits = @bitSizeOf(i28);
2610
...@@ -74,18 +58,18 @@ pub const Thunk = struct {...@@ -74,18 +58,18 @@ pub const Thunk = struct {
74 return @alignOf(u32);58 return @alignOf(u32);
75 }59 }
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 {
78 const atom_index = self.lookup.get(.{ .tag = tag, .target = target }) orelse return null;62 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();
80 }64 }
81};65};
8266
83pub fn createThunks(zld: *Zld, sect_id: u8) !void {67pub fn createThunks(macho_file: *MachO, sect_id: u8) !void {
84 const header = &zld.sections.items(.header)[sect_id];68 const header = &macho_file.sections.items(.header)[sect_id];
85 if (header.size == 0) return;69 if (header.size == 0) return;
8670
87 const gpa = zld.gpa;71 const gpa = macho_file.base.allocator;
88 const first_atom_index = zld.sections.items(.first_atom_index)[sect_id].?;72 const first_atom_index = macho_file.sections.items(.first_atom_index)[sect_id].?;
8973
90 header.size = 0;74 header.size = 0;
91 header.@"align" = 0;75 header.@"align" = 0;
...@@ -95,8 +79,8 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {...@@ -95,8 +79,8 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
95 {79 {
96 var atom_index = first_atom_index;80 var atom_index = first_atom_index;
97 while (true) {81 while (true) {
98 const atom = zld.getAtom(atom_index);82 const atom = macho_file.getAtom(atom_index);
99 const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());83 const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
100 sym.n_value = 0;84 sym.n_value = 0;
101 atom_count += 1;85 atom_count += 1;
10286
...@@ -115,24 +99,24 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {...@@ -115,24 +99,24 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
115 var offset: u64 = 0;99 var offset: u64 = 0;
116100
117 while (true) {101 while (true) {
118 const group_start_atom = zld.getAtom(group_start);102 const group_start_atom = macho_file.getAtom(group_start);
119 log.debug("GROUP START at {d}", .{group_start});103 log.debug("GROUP START at {d}", .{group_start});
120104
121 while (true) {105 while (true) {
122 const atom = zld.getAtom(group_end);106 const atom = macho_file.getAtom(group_end);
123 offset = mem.alignForward(u64, offset, try math.powi(u32, 2, atom.alignment));107 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());
126 sym.n_value = offset;110 sym.n_value = offset;
127 offset += atom.size;111 offset += atom.size;
128112
129 zld.logAtom(group_end, log);113 macho_file.logAtom(group_end, log);
130114
131 header.@"align" = @max(header.@"align", atom.alignment);115 header.@"align" = @max(header.@"align", atom.alignment);
132116
133 allocated.putAssumeCapacityNoClobber(group_end, {});117 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());
136 if (offset - group_start_sym.n_value >= max_allowed_distance) break;120 if (offset - group_start_sym.n_value >= max_allowed_distance) break;
137121
138 if (atom.next_index) |next_index| {122 if (atom.next_index) |next_index| {
...@@ -142,15 +126,15 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {...@@ -142,15 +126,15 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
142 log.debug("GROUP END at {d}", .{group_end});126 log.debug("GROUP END at {d}", .{group_end});
143127
144 // Insert thunk at group_end128 // Insert thunk at group_end
145 const thunk_index = @as(u32, @intCast(zld.thunks.items.len));129 const thunk_index = @as(u32, @intCast(macho_file.thunks.items.len));
146 try zld.thunks.append(gpa, .{ .start_index = undefined, .len = 0 });130 try macho_file.thunks.append(gpa, .{ .start_index = undefined, .len = 0 });
147131
148 // Scan relocs in the group and create trampolines for any unreachable callsite.132 // Scan relocs in the group and create trampolines for any unreachable callsite.
149 var atom_index = group_start;133 var atom_index = group_start;
150 while (true) {134 while (true) {
151 const atom = zld.getAtom(atom_index);135 const atom = macho_file.getAtom(atom_index);
152 try scanRelocs(136 try scanRelocs(
153 zld,137 macho_file,
154 atom_index,138 atom_index,
155 allocated,139 allocated,
156 thunk_index,140 thunk_index,
...@@ -165,19 +149,19 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {...@@ -165,19 +149,19 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
165 }149 }
166150
167 offset = mem.alignForward(u64, offset, Thunk.getAlignment());151 offset = mem.alignForward(u64, offset, Thunk.getAlignment());
168 allocateThunk(zld, thunk_index, offset, header);152 allocateThunk(macho_file, thunk_index, offset, header);
169 offset += zld.thunks.items[thunk_index].getSize();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];
172 if (thunk.len == 0) {156 if (thunk.len == 0) {
173 const group_end_atom = zld.getAtom(group_end);157 const group_end_atom = macho_file.getAtom(group_end);
174 if (group_end_atom.next_index) |next_index| {158 if (group_end_atom.next_index) |next_index| {
175 group_start = next_index;159 group_start = next_index;
176 group_end = next_index;160 group_end = next_index;
177 } else break;161 } else break;
178 } else {162 } else {
179 const thunk_end_atom_index = thunk.getEndAtomIndex();163 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);
181 if (thunk_end_atom.next_index) |next_index| {165 if (thunk_end_atom.next_index) |next_index| {
182 group_start = next_index;166 group_start = next_index;
183 group_end = next_index;167 group_end = next_index;
...@@ -189,12 +173,12 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {...@@ -189,12 +173,12 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
189}173}
190174
191fn allocateThunk(175fn allocateThunk(
192 zld: *Zld,176 macho_file: *MachO,
193 thunk_index: Thunk.Index,177 thunk_index: Thunk.Index,
194 base_offset: u64,178 base_offset: u64,
195 header: *macho.section_64,179 header: *macho.section_64,
196) void {180) void {
197 const thunk = zld.thunks.items[thunk_index];181 const thunk = macho_file.thunks.items[thunk_index];
198 if (thunk.len == 0) return;182 if (thunk.len == 0) return;
199183
200 const first_atom_index = thunk.getStartAtomIndex();184 const first_atom_index = thunk.getStartAtomIndex();
...@@ -203,14 +187,14 @@ fn allocateThunk(...@@ -203,14 +187,14 @@ fn allocateThunk(
203 var atom_index = first_atom_index;187 var atom_index = first_atom_index;
204 var offset = base_offset;188 var offset = base_offset;
205 while (true) {189 while (true) {
206 const atom = zld.getAtom(atom_index);190 const atom = macho_file.getAtom(atom_index);
207 offset = mem.alignForward(u64, offset, Thunk.getAlignment());191 offset = mem.alignForward(u64, offset, Thunk.getAlignment());
208192
209 const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());193 const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
210 sym.n_value = offset;194 sym.n_value = offset;
211 offset += atom.size;195 offset += atom.size;
212196
213 zld.logAtom(atom_index, log);197 macho_file.logAtom(atom_index, log);
214198
215 header.@"align" = @max(header.@"align", atom.alignment);199 header.@"align" = @max(header.@"align", atom.alignment);
216200
...@@ -223,69 +207,69 @@ fn allocateThunk(...@@ -223,69 +207,69 @@ fn allocateThunk(
223}207}
224208
225fn scanRelocs(209fn scanRelocs(
226 zld: *Zld,210 macho_file: *MachO,
227 atom_index: Atom.Index,211 atom_index: Atom.Index,
228 allocated: std.AutoHashMap(Atom.Index, void),212 allocated: std.AutoHashMap(Atom.Index, void),
229 thunk_index: Thunk.Index,213 thunk_index: Thunk.Index,
230 group_end: Atom.Index,214 group_end: Atom.Index,
231) !void {215) !void {
232 const atom = zld.getAtom(atom_index);216 const atom = macho_file.getAtom(atom_index);
233 const object = zld.objects.items[atom.getFile().?];217 const object = macho_file.objects.items[atom.getFile().?];
234218
235 const base_offset = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {219 const base_offset = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
236 const source_sect = object.getSourceSection(source_sym.n_sect - 1);220 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
237 break :blk @as(i32, @intCast(source_sym.n_value - source_sect.addr));221 break :blk @as(i32, @intCast(source_sym.n_value - source_sect.addr));
238 } else 0;222 } else 0;
239223
240 const code = Atom.getAtomCode(zld, atom_index);224 const code = Atom.getAtomCode(macho_file, atom_index);
241 const relocs = Atom.getAtomRelocs(zld, atom_index);225 const relocs = Atom.getAtomRelocs(macho_file, atom_index);
242 const ctx = Atom.getRelocContext(zld, atom_index);226 const ctx = Atom.getRelocContext(macho_file, atom_index);
243227
244 for (relocs) |rel| {228 for (relocs) |rel| {
245 if (!relocNeedsThunk(rel)) continue;229 if (!relocNeedsThunk(rel)) continue;
246230
247 const target = Atom.parseRelocTarget(zld, .{231 const target = Atom.parseRelocTarget(macho_file, .{
248 .object_id = atom.getFile().?,232 .object_id = atom.getFile().?,
249 .rel = rel,233 .rel = rel,
250 .code = code,234 .code = code,
251 .base_offset = ctx.base_offset,235 .base_offset = ctx.base_offset,
252 .base_addr = ctx.base_addr,236 .base_addr = ctx.base_addr,
253 });237 });
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
256 log.debug("{x}: source = {s}@{x}, target = {s}@{x} unreachable", .{240 log.debug("{x}: source = {s}@{x}, target = {s}@{x} unreachable", .{
257 rel.r_address - base_offset,241 rel.r_address - base_offset,
258 zld.getSymbolName(atom.getSymbolWithLoc()),242 macho_file.getSymbolName(atom.getSymbolWithLoc()),
259 zld.getSymbol(atom.getSymbolWithLoc()).n_value,243 macho_file.getSymbol(atom.getSymbolWithLoc()).n_value,
260 zld.getSymbolName(target),244 macho_file.getSymbolName(target),
261 zld.getSymbol(target).n_value,245 macho_file.getSymbol(target).n_value,
262 });246 });
263247
264 const gpa = zld.gpa;248 const gpa = macho_file.base.allocator;
265 const target_sym = zld.getSymbol(target);249 const target_sym = macho_file.getSymbol(target);
266 const thunk = &zld.thunks.items[thunk_index];250 const thunk = &macho_file.thunks.items[thunk_index];
267251
268 const tag: Thunk.Tag = if (target_sym.undf()) .stub else .atom;252 const tag: Thunk.Tag = if (target_sym.undf()) .stub else .atom;
269 const thunk_target: Thunk.Target = .{ .tag = tag, .target = target };253 const thunk_target: Thunk.Target = .{ .tag = tag, .target = target };
270 const gop = try thunk.lookup.getOrPut(gpa, thunk_target);254 const gop = try thunk.lookup.getOrPut(gpa, thunk_target);
271 if (!gop.found_existing) {255 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);
273 try thunk.targets.append(gpa, thunk_target);257 try thunk.targets.append(gpa, thunk_target);
274 }258 }
275259
276 try zld.thunk_table.put(gpa, atom_index, thunk_index);260 try macho_file.thunk_table.put(gpa, atom_index, thunk_index);
277 }261 }
278}262}
279263
280fn pushThunkAtom(zld: *Zld, thunk: *Thunk, group_end: Atom.Index) !Atom.Index {264fn pushThunkAtom(macho_file: *MachO, thunk: *Thunk, group_end: Atom.Index) !Atom.Index {
281 const thunk_atom_index = try createThunkAtom(zld);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);
284 const end_atom_index = if (thunk.len == 0) group_end else thunk.getEndAtomIndex();268 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
287 if (end_atom.next_index) |first_after_index| {271 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);
289 first_after_atom.prev_index = thunk_atom_index;273 first_after_atom.prev_index = thunk_atom_index;
290 thunk_atom.next_index = first_after_index;274 thunk_atom.next_index = first_after_index;
291 }275 }
...@@ -308,58 +292,58 @@ inline fn relocNeedsThunk(rel: macho.relocation_info) bool {...@@ -308,58 +292,58 @@ inline fn relocNeedsThunk(rel: macho.relocation_info) bool {
308}292}
309293
310fn isReachable(294fn isReachable(
311 zld: *Zld,295 macho_file: *MachO,
312 atom_index: Atom.Index,296 atom_index: Atom.Index,
313 rel: macho.relocation_info,297 rel: macho.relocation_info,
314 base_offset: i32,298 base_offset: i32,
315 target: SymbolWithLoc,299 target: SymbolWithLoc,
316 allocated: std.AutoHashMap(Atom.Index, void),300 allocated: std.AutoHashMap(Atom.Index, void),
317) bool {301) 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);304 const source_atom = macho_file.getAtom(atom_index);
321 const source_sym = zld.getSymbol(source_atom.getSymbolWithLoc());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().?];
324 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;308 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
325 const target_atom = zld.getAtom(target_atom_index);309 const target_atom = macho_file.getAtom(target_atom_index);
326 const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());310 const target_sym = macho_file.getSymbol(target_atom.getSymbolWithLoc());
327311
328 if (source_sym.n_sect != target_sym.n_sect) return false;312 if (source_sym.n_sect != target_sym.n_sect) return false;
329313
330 if (!allocated.contains(target_atom_index)) return false;314 if (!allocated.contains(target_atom_index)) return false;
331315
332 const source_addr = source_sym.n_value + @as(u32, @intCast(rel.r_address - base_offset));316 const source_addr = source_sym.n_value + @as(u32, @intCast(rel.r_address - base_offset));
333 const target_addr = if (Atom.relocRequiresGot(zld, rel))317 const target_addr = if (Atom.relocRequiresGot(macho_file, rel))
334 zld.getGotEntryAddress(target).?318 macho_file.getGotEntryAddress(target).?
335 else319 else
336 Atom.getRelocTargetAddress(zld, target, false) catch unreachable;320 Atom.getRelocTargetAddress(macho_file, target, false) catch unreachable;
337 _ = Relocation.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch321 _ = Relocation.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch
338 return false;322 return false;
339323
340 return true;324 return true;
341}325}
342326
343fn createThunkAtom(zld: *Zld) !Atom.Index {327fn createThunkAtom(macho_file: *MachO) !Atom.Index {
344 const sym_index = try zld.allocateSymbol();328 const sym_index = try macho_file.allocateSymbol();
345 const atom_index = try zld.createAtom(sym_index, .{ .size = @sizeOf(u32) * 3, .alignment = 2 });329 const atom_index = try macho_file.createAtom(sym_index, .{ .size = @sizeOf(u32) * 3, .alignment = 2 });
346 const sym = zld.getSymbolPtr(.{ .sym_index = sym_index });330 const sym = macho_file.getSymbolPtr(.{ .sym_index = sym_index });
347 sym.n_type = macho.N_SECT;331 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;
349 return atom_index;333 return atom_index;
350}334}
351335
352pub fn writeThunkCode(zld: *Zld, thunk: *const Thunk, writer: anytype) !void {336pub fn writeThunkCode(macho_file: *MachO, thunk: *const Thunk, writer: anytype) !void {
353 const slice = thunk.targets.slice();337 const slice = thunk.targets.slice();
354 for (thunk.getStartAtomIndex()..thunk.getEndAtomIndex(), 0..) |atom_index, target_index| {338 for (thunk.getStartAtomIndex()..thunk.getEndAtomIndex(), 0..) |atom_index, target_index| {
355 const atom = zld.getAtom(@intCast(atom_index));339 const atom = macho_file.getAtom(@intCast(atom_index));
356 const sym = zld.getSymbol(atom.getSymbolWithLoc());340 const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
357 const source_addr = sym.n_value;341 const source_addr = sym.n_value;
358 const tag = slice.items(.tag)[target_index];342 const tag = slice.items(.tag)[target_index];
359 const target = slice.items(.target)[target_index];343 const target = slice.items(.target)[target_index];
360 const target_addr = switch (tag) {344 const target_addr = switch (tag) {
361 .stub => zld.getStubsEntryAddress(target).?,345 .stub => macho_file.getStubsEntryAddress(target).?,
362 .atom => zld.getSymbol(target).n_value,346 .atom => macho_file.getSymbol(target).n_value,
363 };347 };
364 const pages = Relocation.calcNumberOfPages(source_addr, target_addr);348 const pages = Relocation.calcNumberOfPages(source_addr, target_addr);
365 try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());349 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 {...@@ -368,3 +352,18 @@ pub fn writeThunkCode(zld: *Zld, thunk: *const Thunk, writer: anytype) !void {
368 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());352 try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
369 }353 }
370}354}
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 @@...@@ -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
10/// Calculates Md5 hash of each chunk in parallel and then hashes all Md5 hashes to produce1/// Calculates Md5 hash of each chunk in parallel and then hashes all Md5 hashes to produce
11/// the final digest.2/// the final digest.
12/// While this is NOT a correct MD5 hash of the contents, this methodology is used by LLVM/LLD3/// 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 {...@@ -43,3 +34,12 @@ inline fn conform(out: *[Md5.digest_length]u8) void {
43 out[6] = (out[6] & 0x0F) | (3 << 4);34 out[6] = (out[6] & 0x0F) | (3 << 4);
44 out[8] = (out[8] & 0x3F) | 0x80;35 out[8] = (out[8] & 0x3F) | 0x80;
45}36}
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 @@...@@ -1,2584 +1,8 @@
1const std = @import("std");1pub fn linkWithZld(
2const build_options = @import("build_options");2 macho_file: *MachO,
3const assert = std.debug.assert;3 comp: *Compilation,
4const dwarf = std.dwarf;4 prog_node: *std.Progress.Node,
5const fs = std.fs;5) link.File.FlushError!void {
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 {
2582 const tracy = trace(@src());6 const tracy = trace(@src());
2583 defer tracy.end();7 defer tracy.end();
25848
...@@ -2611,8 +35,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -2611,8 +35,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
2611 defer sub_prog_node.end();35 defer sub_prog_node.end();
261236
2613 const cpu_arch = target.cpu.arch;37 const cpu_arch = target.cpu.arch;
2614 const os_tag = target.os.tag;
2615 const abi = target.abi;
2616 const is_lib = options.output_mode == .Lib;38 const is_lib = options.output_mode == .Lib;
2617 const is_dyn_lib = options.link_mode == .Dynamic and is_lib;39 const is_dyn_lib = options.link_mode == .Dynamic and is_lib;
2618 const is_exe_or_dyn_lib = is_dyn_lib or options.output_mode == .Exe;40 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...@@ -2730,29 +152,26 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
2730 } else {152 } else {
2731 const sub_path = options.emit.?.sub_path;153 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
2733 const file = try directory.handle.createFile(sub_path, .{158 const file = try directory.handle.createFile(sub_path, .{
2734 .truncate = true,159 .truncate = true,
2735 .read = true,160 .read = true,
2736 .mode = link.determineMode(options.*),161 .mode = link.determineMode(options.*),
2737 });162 });
2738 defer file.close();163 defer file.close();
2739164 macho_file.base.file = file;
2740 var zld = Zld{
2741 .gpa = gpa,
2742 .file = file,
2743 .options = options,
2744 };
2745 defer zld.deinit();
2746165
2747 // Index 0 is always a null symbol.166 // Index 0 is always a null symbol.
2748 try zld.locals.append(gpa, .{167 try macho_file.locals.append(gpa, .{
2749 .n_strx = 0,168 .n_strx = 0,
2750 .n_type = 0,169 .n_type = 0,
2751 .n_sect = 0,170 .n_sect = 0,
2752 .n_desc = 0,171 .n_desc = 0,
2753 .n_value = 0,172 .n_value = 0,
2754 });173 });
2755 try zld.strtab.buffer.append(gpa, 0);174 try macho_file.strtab.buffer.append(gpa, 0);
2756175
2757 // Positional arguments to the linker such as object files and static archives.176 // Positional arguments to the linker such as object files and static archives.
2758 var positionals = std.ArrayList(Compilation.LinkObject).init(arena);177 var positionals = std.ArrayList(Compilation.LinkObject).init(arena);
...@@ -2930,9 +349,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -2930,9 +349,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
2930 const in_file = try std.fs.cwd().openFile(obj.path, .{});349 const in_file = try std.fs.cwd().openFile(obj.path, .{});
2931 defer in_file.close();350 defer in_file.close();
2932351
2933 MachO.parsePositional(352 macho_file.parsePositional(
2934 &zld,
2935 gpa,
2936 in_file,353 in_file,
2937 obj.path,354 obj.path,
2938 obj.must_link,355 obj.must_link,
...@@ -2949,9 +366,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -2949,9 +366,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
2949 const in_file = try std.fs.cwd().openFile(path, .{});366 const in_file = try std.fs.cwd().openFile(path, .{});
2950 defer in_file.close();367 defer in_file.close();
2951368
2952 MachO.parseLibrary(369 macho_file.parseLibrary(
2953 &zld,
2954 gpa,
2955 in_file,370 in_file,
2956 path,371 path,
2957 lib,372 lib,
...@@ -2965,198 +380,199 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -2965,198 +380,199 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
2965 };380 };
2966 }381 }
2967382
2968 MachO.parseDependentLibs(&zld, gpa, &dependent_libs, options) catch |err| {383 macho_file.parseDependentLibs(&dependent_libs, options) catch |err| {
2969 // TODO convert to error384 // TODO convert to error
2970 log.err("parsing dependent libraries failed with err {s}", .{@errorName(err)});385 log.err("parsing dependent libraries failed with err {s}", .{@errorName(err)});
2971 };386 };
2972387
2973 try zld.resolveSymbols();388 var actions = std.ArrayList(MachO.ResolveAction).init(gpa);
2974 try macho_file.reportUndefined(&zld);389 defer actions.deinit();
2975390 try macho_file.resolveSymbols(&actions);
2976 if (options.output_mode == .Exe) {391 try macho_file.reportUndefined();
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 }
2981392
2982 for (zld.objects.items, 0..) |*object, object_id| {393 for (macho_file.objects.items, 0..) |*object, object_id| {
2983 try object.splitIntoAtoms(&zld, @as(u32, @intCast(object_id)));394 try object.splitIntoAtoms(macho_file, @as(u32, @intCast(object_id)));
2984 }395 }
2985396
2986 if (gc_sections) {397 if (gc_sections) {
2987 try dead_strip.gcAtoms(&zld);398 try dead_strip.gcAtoms(macho_file);
2988 }399 }
2989400
2990 try zld.createDyldPrivateAtom();401 try macho_file.createDyldPrivateAtom();
2991 try zld.createTentativeDefAtoms();402 try macho_file.createTentativeDefAtoms();
2992403
2993 if (zld.options.output_mode == .Exe) {404 if (macho_file.options.output_mode == .Exe) {
2994 const global = zld.getEntryPoint();405 const global = macho_file.getEntryPoint().?;
2995 if (zld.getSymbol(global).undf()) {406 if (macho_file.getSymbol(global).undf()) {
2996 // We do one additional check here in case the entry point was found in one of the dylibs.407 // We do one additional check here in case the entry point was found in one of the dylibs.
2997 // (I actually have no idea what this would imply but it is a possible outcome and so we408 // (I actually have no idea what this would imply but it is a possible outcome and so we
2998 // support it.)409 // support it.)
2999 try zld.addStubEntry(global);410 try macho_file.addStubEntry(global);
3000 }411 }
3001 }412 }
3002413
3003 for (zld.objects.items) |object| {414 for (macho_file.objects.items) |object| {
3004 for (object.atoms.items) |atom_index| {415 for (object.atoms.items) |atom_index| {
3005 const atom = zld.getAtom(atom_index);416 const atom = macho_file.getAtom(atom_index);
3006 const sym = zld.getSymbol(atom.getSymbolWithLoc());417 const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
3007 const header = zld.sections.items(.header)[sym.n_sect - 1];418 const header = macho_file.sections.items(.header)[sym.n_sect - 1];
3008 if (header.isZerofill()) continue;419 if (header.isZerofill()) continue;
3009420
3010 const relocs = Atom.getAtomRelocs(&zld, atom_index);421 const relocs = Atom.getAtomRelocs(macho_file, atom_index);
3011 try Atom.scanAtomRelocs(&zld, atom_index, relocs);422 try Atom.scanAtomRelocs(macho_file, atom_index, relocs);
3012 }423 }
3013 }424 }
3014425
3015 try eh_frame.scanRelocs(&zld);426 try eh_frame.scanRelocs(macho_file);
3016 try UnwindInfo.scanRelocs(&zld);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 };
3023 defer unwind_info.deinit();435 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);438 try eh_frame.calcSectionSize(macho_file, &unwind_info);
3027 try unwind_info.calcSectionSize(&zld);439 try unwind_info.calcSectionSize(macho_file);
3028440
3029 try zld.pruneAndSortSections();441 try pruneAndSortSections(macho_file);
3030 try zld.createSegments();442 try createSegments(macho_file);
3031 try zld.allocateSegments();443 try allocateSegments(macho_file);
3032444
3033 try MachO.allocateSpecialSymbols(&zld);445 try macho_file.allocateSpecialSymbols();
3034446
3035 if (build_options.enable_logging) {447 if (build_options.enable_logging) {
3036 zld.logSymtab();448 macho_file.logSymtab();
3037 zld.logSegments();449 macho_file.logSegments();
3038 zld.logSections();450 macho_file.logSections();
3039 zld.logAtoms();451 macho_file.logAtoms();
3040 }452 }
3041453
3042 try zld.writeAtoms();454 try writeAtoms(macho_file);
3043 if (zld.requiresThunks()) try zld.writeThunks();455 if (macho_file.requiresThunks()) try writeThunks(macho_file);
3044 try zld.writeDyldPrivateAtom();456 try writeDyldPrivateAtom(macho_file);
3045457
3046 if (zld.stubs_section_index) |_| {458 if (macho_file.stubs_section_index) |_| {
3047 try zld.writeStubs();459 try writeStubs(macho_file);
3048 try zld.writeStubHelpers();460 try writeStubHelpers(macho_file);
3049 try zld.writeLaSymbolPtrs();461 try writeLaSymbolPtrs(macho_file);
3050 }462 }
3051 if (zld.got_section_index) |sect_id| try zld.writePointerEntries(sect_id, &zld.got_table);463 if (macho_file.got_section_index) |sect_id|
3052 if (zld.tlv_ptr_section_index) |sect_id| try zld.writePointerEntries(sect_id, &zld.tlv_ptr_table);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);468 try eh_frame.write(macho_file, &unwind_info);
3055 try unwind_info.write(&zld);469 try unwind_info.write(macho_file);
3056 try zld.writeLinkeditSegmentData();470 try macho_file.writeLinkeditSegmentData();
3057471
3058 // If the last section of __DATA segment is zerofill section, we need to ensure472 // If the last section of __DATA segment is zerofill section, we need to ensure
3059 // that the free space between the end of the last non-zerofill section of __DATA473 // that the free space between the end of the last non-zerofill section of __DATA
3060 // segment and the beginning of __LINKEDIT segment is zerofilled as the loader will474 // segment and the beginning of __LINKEDIT segment is zerofilled as the loader will
3061 // copy-paste this space into memory for quicker zerofill operation.475 // 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: {
3063 var physical_zerofill_start: ?u64 = null;477 var physical_zerofill_start: ?u64 = null;
3064 const section_indexes = zld.getSectionIndexes(data_seg_id);478 const section_indexes = macho_file.getSectionIndexes(data_seg_id);
3065 for (zld.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| {479 for (macho_file.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| {
3066 if (header.isZerofill() and header.size > 0) break;480 if (header.isZerofill() and header.size > 0) break;
3067 physical_zerofill_start = header.offset + header.size;481 physical_zerofill_start = header.offset + header.size;
3068 } else break :blk;482 } else break :blk;
3069 const start = physical_zerofill_start orelse break :blk;483 const start = physical_zerofill_start orelse break :blk;
3070 const linkedit = zld.getLinkeditSegmentPtr();484 const linkedit = macho_file.getLinkeditSegmentPtr();
3071 const size = math.cast(usize, linkedit.fileoff - start) orelse return error.Overflow;485 const size = math.cast(usize, linkedit.fileoff - start) orelse return error.Overflow;
3072 if (size > 0) {486 if (size > 0) {
3073 log.debug("zeroing out zerofill area of length {x} at {x}", .{ size, start });487 log.debug("zeroing out zerofill area of length {x} at {x}", .{ size, start });
3074 var padding = try zld.gpa.alloc(u8, size);488 var padding = try gpa.alloc(u8, size);
3075 defer zld.gpa.free(padding);489 defer gpa.free(padding);
3076 @memset(padding, 0);490 @memset(padding, 0);
3077 try zld.file.pwriteAll(padding, start);491 try macho_file.base.file.?.pwriteAll(padding, start);
3078 }492 }
3079 }493 }
3080494
3081 // Write code signature padding if required495 // Write code signature padding if required
3082 const requires_codesig = blk: {496 var codesig: ?CodeSignature = if (macho_file.requiresCodeSignature()) 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: {
3088 // Preallocate space for the code signature.497 // Preallocate space for the code signature.
3089 // We need to do this at this stage so that we have the load commands with proper values498 // We need to do this at this stage so that we have the load commands with proper values
3090 // written out to the file.499 // written out to the file.
3091 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment500 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
3092 // where the code signature goes into.501 // 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));
3094 codesig.code_directory.ident = fs.path.basename(full_out_path);503 codesig.code_directory.ident = fs.path.basename(full_out_path);
3095 if (options.entitlements) |path| {504 if (options.entitlements) |path| {
3096 try codesig.addEntitlements(zld.gpa, path);505 try codesig.addEntitlements(gpa, path);
3097 }506 }
3098 try zld.writeCodeSignaturePadding(&codesig);507 try macho_file.writeCodeSignaturePadding(&codesig);
3099 break :blk codesig;508 break :blk codesig;
3100 } else null;509 } else null;
3101 defer if (codesig) |*csig| csig.deinit(zld.gpa);510 defer if (codesig) |*csig| csig.deinit(gpa);
3102511
3103 // Write load commands512 // Write load commands
3104 var lc_buffer = std.ArrayList(u8).init(arena);513 var lc_buffer = std.ArrayList(u8).init(arena);
3105 const lc_writer = lc_buffer.writer();514 const lc_writer = lc_buffer.writer();
3106515
3107 try MachO.writeSegmentHeaders(&zld, lc_writer);516 try macho_file.writeSegmentHeaders(lc_writer);
3108 try lc_writer.writeStruct(zld.dyld_info_cmd);517 try lc_writer.writeStruct(macho_file.dyld_info_cmd);
3109 try lc_writer.writeStruct(zld.function_starts_cmd);518 try lc_writer.writeStruct(macho_file.function_starts_cmd);
3110 try lc_writer.writeStruct(zld.data_in_code_cmd);519 try lc_writer.writeStruct(macho_file.data_in_code_cmd);
3111 try lc_writer.writeStruct(zld.symtab_cmd);520 try lc_writer.writeStruct(macho_file.symtab_cmd);
3112 try lc_writer.writeStruct(zld.dysymtab_cmd);521 try lc_writer.writeStruct(macho_file.dysymtab_cmd);
3113 try load_commands.writeDylinkerLC(lc_writer);522 try load_commands.writeDylinkerLC(lc_writer);
3114523
3115 if (zld.options.output_mode == .Exe) {524 switch (macho_file.base.options.output_mode) {
3116 const seg_id = zld.header_segment_cmd_index.?;525 .Exe => blk: {
3117 const seg = zld.segments.items[seg_id];526 const seg_id = macho_file.header_segment_cmd_index.?;
3118 const global = zld.getEntryPoint();527 const seg = macho_file.segments.items[seg_id];
3119 const sym = zld.getSymbol(global);528 const global = macho_file.getEntryPoint() orelse break :blk;
3120529 const sym = macho_file.getSymbol(global);
3121 const addr: u64 = if (sym.undf())530
3122 // In this case, the symbol has been resolved in one of dylibs and so we point531 const addr: u64 = if (sym.undf())
3123 // to the stub as its vmaddr value.532 // In this case, the symbol has been resolved in one of dylibs and so we point
3124 zld.getStubsEntryAddress(global).?533 // to the stub as its vmaddr value.
3125 else534 macho_file.getStubsEntryAddress(global).?
3126 sym.n_value;535 else
3127536 sym.n_value;
3128 try lc_writer.writeStruct(macho.entry_point_command{537
3129 .entryoff = @as(u32, @intCast(addr - seg.vmaddr)),538 try lc_writer.writeStruct(macho.entry_point_command{
3130 .stacksize = options.stack_size_override orelse 0,539 .entryoff = @as(u32, @intCast(addr - seg.vmaddr)),
3131 });540 .stacksize = macho_file.base.options.stack_size_override orelse 0,
3132 } else {541 });
3133 assert(zld.options.output_mode == .Lib);542 },
3134 try load_commands.writeDylibIdLC(zld.gpa, zld.options, lc_writer);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 => {},
3135 }547 }
3136548
3137 try load_commands.writeRpathLCs(zld.gpa, zld.options, lc_writer);549 try load_commands.writeRpathLCs(gpa, macho_file.base.options, lc_writer);
3138 try lc_writer.writeStruct(macho.source_version_command{550 try lc_writer.writeStruct(macho.source_version_command{
3139 .version = 0,551 .version = 0,
3140 });552 });
3141 try load_commands.writeBuildVersionLC(zld.options, lc_writer);553 try load_commands.writeBuildVersionLC(macho_file.base.options, lc_writer);
3142554
3143 const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + @as(u32, @intCast(lc_buffer.items.len));555 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) {564 if (codesig != null) {
3149 try lc_writer.writeStruct(zld.codesig_cmd);565 try lc_writer.writeStruct(macho_file.codesig_cmd);
3150 }566 }
3151567
3152 const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);568 const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);
3153 try zld.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));569 try macho_file.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
3154 try zld.writeHeader(ncmds, @as(u32, @intCast(lc_buffer.items.len)));570 try macho_file.writeHeader(ncmds, @as(u32, @intCast(lc_buffer.items.len)));
3155 try zld.writeUuid(comp, uuid_cmd_offset, requires_codesig);571 try macho_file.writeUuid(comp, uuid_cmd_offset, codesig != null);
3156572
3157 if (codesig) |*csig| {573 if (codesig) |*csig| {
3158 try zld.writeCodeSignature(comp, csig); // code signing always comes last574 try macho_file.writeCodeSignature(comp, csig); // code signing always comes last
3159 try MachO.invalidateKernelCache(directory.handle, zld.options.emit.?.sub_path);575 try MachO.invalidateKernelCache(directory.handle, macho_file.base.options.emit.?.sub_path);
3160 }576 }
3161 }577 }
3162578
...@@ -3177,3 +593,609 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3177,3 +593,609 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3177 macho_file.base.lock = man.toOwnedLock();593 macho_file.base.lock = man.toOwnedLock();
3178 }594 }
3179}595}
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");