authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-08-19 09:07:33+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-08-29 11:39:34+02:00
log702bcfecf5732eceada9bfbca0804c706c238d49
tree7a54653dd6e7dd77c0e5c59b98689bf0cb185853
parent69193a4ae421a1d69481addbba03e459df8d2a14

macho: simplify input file parsing for both drivers


6 files changed, 489 insertions(+), 554 deletions(-)

src/link/MachO.zig+376-127
......@@ -111,6 +111,8 @@ dysymtab_cmd: macho.dysymtab_command = .{},
111111uuid_cmd: macho.uuid_command = .{},
112112codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE },
113113
114objects: std.ArrayListUnmanaged(Object) = .{},
115archives: std.ArrayListUnmanaged(Archive) = .{},
114116dylibs: std.ArrayListUnmanaged(Dylib) = .{},
115117dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
116118referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},
......@@ -586,8 +588,30 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
586588 parent: u16,
587589 }, .Dynamic).init(arena);
588590
589 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
590 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
591 for (libs.keys(), libs.values()) |path, lib| {
592 const in_file = try std.fs.cwd().openFile(path, .{});
593 defer in_file.close();
594
595 parseLibrary(
596 self,
597 self.base.allocator,
598 in_file,
599 path,
600 lib,
601 false,
602 &dependent_libs,
603 &self.base.options,
604 ) catch |err| {
605 // TODO convert to error
606 log.err("{s}: parsing library failed with err {s}", .{ path, @errorName(err) });
607 continue;
608 };
609 }
610
611 parseDependentLibs(self, self.base.allocator, &dependent_libs, &self.base.options) catch |err| {
612 // TODO convert to error
613 log.err("parsing dependent libraries failed with err {s}", .{@errorName(err)});
614 };
591615 }
592616
593617 if (self.dyld_stub_binder_index == null) {
......@@ -880,175 +904,373 @@ fn resolveLib(
880904 return full_path;
881905}
882906
883const ParseDylibError = error{
884 OutOfMemory,
885 EmptyStubFile,
886 MismatchedCpuArchitecture,
887 UnsupportedCpuArchitecture,
888 EndOfStream,
889} || fs.File.OpenError || std.os.PReadError || Dylib.Id.ParseError;
907pub fn parsePositional(
908 ctx: anytype,
909 gpa: Allocator,
910 file: std.fs.File,
911 path: []const u8,
912 must_link: bool,
913 dependent_libs: anytype,
914 link_options: *const link.Options,
915) !void {
916 const tracy = trace(@src());
917 defer tracy.end();
890918
891const DylibCreateOpts = struct {
892 syslibroot: ?[]const u8,
919 if (Object.isObject(file)) {
920 try parseObject(ctx, gpa, file, path, link_options);
921 } else {
922 try parseLibrary(ctx, gpa, file, path, .{
923 .path = null,
924 .needed = false,
925 .weak = false,
926 }, must_link, dependent_libs, link_options);
927 }
928}
929
930fn parseObject(
931 ctx: anytype,
932 gpa: Allocator,
933 file: std.fs.File,
934 path: []const u8,
935 link_options: *const link.Options,
936) !void {
937 const tracy = trace(@src());
938 defer tracy.end();
939
940 const mtime: u64 = mtime: {
941 const stat = file.stat() catch break :mtime 0;
942 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
943 };
944 const file_stat = try file.stat();
945 const file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
946 const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null);
947
948 var object = Object{
949 .name = try gpa.dupe(u8, path),
950 .mtime = mtime,
951 .contents = contents,
952 };
953 errdefer object.deinit(gpa);
954 try object.parse(gpa);
955 try ctx.objects.append(gpa, object);
956
957 const cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) {
958 macho.CPU_TYPE_ARM64 => .aarch64,
959 macho.CPU_TYPE_X86_64 => .x86_64,
960 else => unreachable,
961 };
962 const self_cpu_arch = link_options.target.cpu.arch;
963
964 if (self_cpu_arch != cpu_arch) {
965 // TODO convert into an error
966 log.err("{s}: invalid architecture '{s}', expected '{s}'", .{
967 path,
968 @tagName(cpu_arch),
969 @tagName(self_cpu_arch),
970 });
971 }
972}
973
974pub fn parseLibrary(
975 ctx: anytype,
976 gpa: Allocator,
977 file: std.fs.File,
978 path: []const u8,
979 lib: link.SystemLib,
980 must_link: bool,
981 dependent_libs: anytype,
982 link_options: *const link.Options,
983) !void {
984 const tracy = trace(@src());
985 defer tracy.end();
986
987 const cpu_arch = link_options.target.cpu.arch;
988
989 if (fat.isFatLibrary(file)) {
990 const offset = parseFatLibrary(ctx, file, path, cpu_arch) catch |err| switch (err) {
991 error.MissingArch => return,
992 else => |e| return e,
993 };
994 try file.seekTo(offset);
995
996 if (Archive.isArchive(file, offset)) {
997 try parseArchive(ctx, gpa, path, offset, must_link, cpu_arch);
998 } else if (Dylib.isDylib(file, offset)) {
999 try parseDylib(ctx, gpa, file, path, offset, dependent_libs, link_options, .{
1000 .needed = lib.needed,
1001 .weak = lib.weak,
1002 });
1003 } else {
1004 // TODO convert into an error
1005 log.err("{s}: unknown file type", .{path});
1006 return;
1007 }
1008 } else if (Archive.isArchive(file, 0)) {
1009 try parseArchive(ctx, gpa, path, 0, must_link, cpu_arch);
1010 } else if (Dylib.isDylib(file, 0)) {
1011 try parseDylib(ctx, gpa, file, path, 0, dependent_libs, link_options, .{
1012 .needed = lib.needed,
1013 .weak = lib.weak,
1014 });
1015 } else {
1016 parseLibStub(ctx, gpa, file, path, dependent_libs, link_options, .{
1017 .needed = lib.needed,
1018 .weak = lib.weak,
1019 }) catch |err| switch (err) {
1020 error.NotLibStub, error.UnexpectedToken => {
1021 // TODO convert into an error
1022 log.err("{s}: unknown file type", .{path});
1023 return;
1024 },
1025 else => |e| return e,
1026 };
1027 }
1028}
1029
1030pub fn parseFatLibrary(
1031 ctx: anytype,
1032 file: std.fs.File,
1033 path: []const u8,
1034 cpu_arch: std.Target.Cpu.Arch,
1035) !u64 {
1036 _ = ctx;
1037 var buffer: [2]fat.Arch = undefined;
1038 const fat_archs = try fat.parseArchs(file, &buffer);
1039 const offset = for (fat_archs) |arch| {
1040 if (arch.tag == cpu_arch) break arch.offset;
1041 } else {
1042 // TODO convert into an error
1043 log.err("{s}: missing arch in universal file: expected {s}", .{ path, @tagName(cpu_arch) });
1044 return error.MissingArch;
1045 };
1046 return offset;
1047}
1048
1049fn parseArchive(
1050 ctx: anytype,
1051 gpa: Allocator,
1052 path: []const u8,
1053 fat_offset: u64,
1054 must_link: bool,
1055 cpu_arch: std.Target.Cpu.Arch,
1056) !void {
1057
1058 // We take ownership of the file so that we can store it for the duration of symbol resolution.
1059 // TODO we shouldn't need to do that and could pre-parse the archive like we do for zld/ELF?
1060 const file = try std.fs.cwd().openFile(path, .{});
1061 errdefer file.close();
1062 try file.seekTo(fat_offset);
1063
1064 var archive = Archive{
1065 .file = file,
1066 .fat_offset = fat_offset,
1067 .name = try gpa.dupe(u8, path),
1068 };
1069 errdefer archive.deinit(gpa);
1070
1071 try archive.parse(gpa, file.reader());
1072
1073 // Verify arch and platform
1074 if (archive.toc.values().len > 0) {
1075 const offsets = archive.toc.values()[0].items;
1076 assert(offsets.len > 0);
1077 const off = offsets[0];
1078 var object = try archive.parseObject(gpa, off); // TODO we are doing all this work to pull the header only!
1079 defer object.deinit(gpa);
1080
1081 const parsed_cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) {
1082 macho.CPU_TYPE_ARM64 => .aarch64,
1083 macho.CPU_TYPE_X86_64 => .x86_64,
1084 else => unreachable,
1085 };
1086 if (cpu_arch != parsed_cpu_arch) {
1087 // TODO convert into an error
1088 log.err("{s}: invalid architecture in archive '{s}', expected '{s}'", .{
1089 path,
1090 @tagName(parsed_cpu_arch),
1091 @tagName(cpu_arch),
1092 });
1093 return error.MissingArch;
1094 }
1095 }
1096
1097 if (must_link) {
1098 // Get all offsets from the ToC
1099 var offsets = std.AutoArrayHashMap(u32, void).init(gpa);
1100 defer offsets.deinit();
1101 for (archive.toc.values()) |offs| {
1102 for (offs.items) |off| {
1103 _ = try offsets.getOrPut(off);
1104 }
1105 }
1106 for (offsets.keys()) |off| {
1107 const object = try archive.parseObject(gpa, off);
1108 try ctx.objects.append(gpa, object);
1109 }
1110 } else {
1111 try ctx.archives.append(gpa, archive);
1112 }
1113}
1114
1115const DylibOpts = struct {
8931116 id: ?Dylib.Id = null,
8941117 dependent: bool = false,
8951118 needed: bool = false,
8961119 weak: bool = false,
8971120};
8981121
899pub fn parseDylib(
900 self: *MachO,
1122fn parseDylib(
1123 ctx: anytype,
1124 gpa: Allocator,
1125 file: std.fs.File,
9011126 path: []const u8,
1127 offset: u64,
9021128 dependent_libs: anytype,
903 opts: DylibCreateOpts,
904) ParseDylibError!bool {
905 const gpa = self.base.allocator;
906 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
907 error.FileNotFound => return false,
908 else => |e| return e,
909 };
910 defer file.close();
1129 link_options: *const link.Options,
1130 dylib_options: DylibOpts,
1131) !void {
1132 const self_cpu_arch = link_options.target.cpu.arch;
9111133
912 const cpu_arch = self.base.options.target.cpu.arch;
9131134 const file_stat = try file.stat();
9141135 var file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
9151136
916 const reader = file.reader();
917 const fat_offset = math.cast(usize, try fat.getLibraryOffset(reader, cpu_arch)) orelse
918 return error.Overflow;
919 try file.seekTo(fat_offset);
920 file_size -= fat_offset;
1137 file_size -= offset;
9211138
9221139 const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null);
9231140 defer gpa.free(contents);
9241141
925 const dylib_id = @as(u16, @intCast(self.dylibs.items.len));
926 var dylib = Dylib{ .weak = opts.weak };
1142 var dylib = Dylib{ .weak = dylib_options.weak };
1143 errdefer dylib.deinit(gpa);
9271144
928 dylib.parseFromBinary(
1145 try dylib.parseFromBinary(
9291146 gpa,
930 cpu_arch,
931 dylib_id,
1147 @intCast(ctx.dylibs.items.len), // TODO defer it till later
9321148 dependent_libs,
9331149 path,
9341150 contents,
935 ) catch |err| switch (err) {
936 error.EndOfStream, error.NotDylib => {
937 try file.seekTo(0);
1151 );
9381152
939 var lib_stub = LibStub.loadFromFile(gpa, file) catch {
940 dylib.deinit(gpa);
941 return false;
942 };
943 defer lib_stub.deinit();
944
945 try dylib.parseFromStub(
946 gpa,
947 self.base.options.target,
948 lib_stub,
949 dylib_id,
950 dependent_libs,
951 path,
952 );
953 },
1153 const cpu_arch: std.Target.Cpu.Arch = switch (dylib.header.?.cputype) {
1154 macho.CPU_TYPE_ARM64 => .aarch64,
1155 macho.CPU_TYPE_X86_64 => .x86_64,
1156 else => unreachable,
1157 };
1158 if (self_cpu_arch != cpu_arch) {
1159 // TODO convert into an error
1160 log.err("{s}: invalid architecture '{s}', expected '{s}'", .{
1161 path,
1162 @tagName(cpu_arch),
1163 @tagName(self_cpu_arch),
1164 });
1165 return error.MissingArch;
1166 }
1167
1168 // TODO verify platform
1169
1170 addDylib(ctx, gpa, dylib, link_options, .{
1171 .needed = dylib_options.needed,
1172 .weak = dylib_options.weak,
1173 }) catch |err| switch (err) {
1174 error.DylibAlreadyExists => dylib.deinit(gpa),
9541175 else => |e| return e,
9551176 };
1177}
9561178
957 if (opts.id) |id| {
1179fn parseLibStub(
1180 ctx: anytype,
1181 gpa: Allocator,
1182 file: std.fs.File,
1183 path: []const u8,
1184 dependent_libs: anytype,
1185 link_options: *const link.Options,
1186 dylib_options: DylibOpts,
1187) !void {
1188 var lib_stub = try LibStub.loadFromFile(gpa, file);
1189 defer lib_stub.deinit();
1190
1191 if (lib_stub.inner.len == 0) return error.NotLibStub;
1192
1193 // TODO verify platform
1194
1195 var dylib = Dylib{ .weak = dylib_options.weak };
1196 errdefer dylib.deinit(gpa);
1197
1198 try dylib.parseFromStub(
1199 gpa,
1200 link_options.target,
1201 lib_stub,
1202 @intCast(ctx.dylibs.items.len), // TODO defer it till later
1203 dependent_libs,
1204 path,
1205 );
1206
1207 addDylib(ctx, gpa, dylib, link_options, .{
1208 .needed = dylib_options.needed,
1209 .weak = dylib_options.weak,
1210 }) catch |err| switch (err) {
1211 error.DylibAlreadyExists => dylib.deinit(gpa),
1212 else => |e| return e,
1213 };
1214}
1215
1216fn addDylib(
1217 ctx: anytype,
1218 gpa: Allocator,
1219 dylib: Dylib,
1220 link_options: *const link.Options,
1221 dylib_options: DylibOpts,
1222) !void {
1223 if (dylib_options.id) |id| {
9581224 if (dylib.id.?.current_version < id.compatibility_version) {
1225 // TODO convert into an error
9591226 log.warn("found dylib is incompatible with the required minimum version", .{});
9601227 log.warn(" dylib: {s}", .{id.name});
9611228 log.warn(" required minimum version: {}", .{id.compatibility_version});
9621229 log.warn(" dylib version: {}", .{dylib.id.?.current_version});
963
964 // TODO maybe this should be an error and facilitate auto-cleanup?
965 dylib.deinit(gpa);
966 return false;
1230 return error.IncompatibleDylibVersion;
9671231 }
9681232 }
9691233
970 try self.dylibs.append(gpa, dylib);
971 try self.dylibs_map.putNoClobber(gpa, dylib.id.?.name, dylib_id);
1234 const gop = try ctx.dylibs_map.getOrPut(gpa, dylib.id.?.name);
1235 if (gop.found_existing) return error.DylibAlreadyExists;
1236
1237 gop.value_ptr.* = @as(u16, @intCast(ctx.dylibs.items.len));
1238 try ctx.dylibs.append(gpa, dylib);
9721239
9731240 const should_link_dylib_even_if_unreachable = blk: {
974 if (self.base.options.dead_strip_dylibs and !opts.needed) break :blk false;
975 break :blk !(opts.dependent or self.referenced_dylibs.contains(dylib_id));
1241 if (link_options.dead_strip_dylibs and !dylib_options.needed) break :blk false;
1242 break :blk !(dylib_options.dependent or ctx.referenced_dylibs.contains(gop.value_ptr.*));
9761243 };
9771244
9781245 if (should_link_dylib_even_if_unreachable) {
979 try self.referenced_dylibs.putNoClobber(gpa, dylib_id, {});
1246 try ctx.referenced_dylibs.putNoClobber(gpa, gop.value_ptr.*, {});
9801247 }
981
982 return true;
9831248}
9841249
985pub fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8, dependent_libs: anytype) !void {
986 for (files) |file_name| {
987 const full_path = full_path: {
988 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
989 break :full_path try fs.realpath(file_name, &buffer);
990 };
991 log.debug("parsing input file path '{s}'", .{full_path});
992
993 if (try self.parseObject(full_path)) continue;
994 if (try self.parseArchive(full_path, false)) continue;
995 if (try self.parseDylib(full_path, dependent_libs, .{
996 .syslibroot = syslibroot,
997 })) continue;
998
999 log.debug("unknown filetype for positional input file: '{s}'", .{file_name});
1000 }
1001}
1002
1003pub fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !void {
1004 for (files) |file_name| {
1005 const full_path = full_path: {
1006 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
1007 break :full_path try fs.realpath(file_name, &buffer);
1008 };
1009 log.debug("parsing and force loading static archive '{s}'", .{full_path});
1010
1011 if (try self.parseArchive(full_path, true)) continue;
1012 log.debug("unknown filetype: expected static archive: '{s}'", .{file_name});
1013 }
1014}
1015
1016pub fn parseLibs(
1017 self: *MachO,
1018 lib_names: []const []const u8,
1019 lib_infos: []const link.SystemLib,
1020 syslibroot: ?[]const u8,
1250pub fn parseDependentLibs(
1251 ctx: anytype,
1252 gpa: Allocator,
10211253 dependent_libs: anytype,
1254 link_options: *const link.Options,
10221255) !void {
1023 for (lib_names, 0..) |lib, i| {
1024 const lib_info = lib_infos[i];
1025 log.debug("parsing lib path '{s}'", .{lib});
1026 if (try self.parseDylib(lib, dependent_libs, .{
1027 .syslibroot = syslibroot,
1028 .needed = lib_info.needed,
1029 .weak = lib_info.weak,
1030 })) continue;
1031
1032 log.debug("unknown filetype for a library: '{s}'", .{lib});
1033 }
1034}
1256 const tracy = trace(@src());
1257 defer tracy.end();
10351258
1036pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: anytype) !void {
10371259 // At this point, we can now parse dependents of dylibs preserving the inclusion order of:
10381260 // 1) anything on the linker line is parsed first
10391261 // 2) afterwards, we parse dependents of the included dylibs
10401262 // TODO this should not be performed if the user specifies `-flat_namespace` flag.
10411263 // See ld64 manpages.
1042 var arena_alloc = std.heap.ArenaAllocator.init(self.base.allocator);
1264 var arena_alloc = std.heap.ArenaAllocator.init(gpa);
10431265 const arena = arena_alloc.allocator();
10441266 defer arena_alloc.deinit();
10451267
1046 while (dependent_libs.readItem()) |*dep_id| {
1047 defer dep_id.id.deinit(self.base.allocator);
1268 outer: while (dependent_libs.readItem()) |dep_id| {
1269 defer dep_id.id.deinit(gpa);
10481270
1049 if (self.dylibs_map.contains(dep_id.id.name)) continue;
1271 if (ctx.dylibs_map.contains(dep_id.id.name)) continue;
10501272
1051 const weak = self.dylibs.items[dep_id.parent].weak;
1273 const weak = ctx.dylibs.items[dep_id.parent].weak;
10521274 const has_ext = blk: {
10531275 const basename = fs.path.basename(dep_id.id.name);
10541276 break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;
......@@ -1061,20 +1283,47 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs:
10611283
10621284 for (&[_][]const u8{ extension, ".tbd" }) |ext| {
10631285 const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ without_ext, ext });
1064 const full_path = if (syslibroot) |root| try fs.path.join(arena, &.{ root, with_ext }) else with_ext;
1286 const full_path = if (link_options.sysroot) |root|
1287 try fs.path.join(arena, &.{ root, with_ext })
1288 else
1289 with_ext;
1290
1291 const file = std.fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
1292 error.FileNotFound => continue,
1293 else => |e| return e,
1294 };
1295 defer file.close();
10651296
10661297 log.debug("trying dependency at fully resolved path {s}", .{full_path});
10671298
1068 const did_parse_successfully = try self.parseDylib(full_path, dependent_libs, .{
1069 .id = dep_id.id,
1070 .syslibroot = syslibroot,
1071 .dependent = true,
1072 .weak = weak,
1073 });
1074 if (did_parse_successfully) break;
1075 } else {
1076 log.debug("unable to resolve dependency {s}", .{dep_id.id.name});
1299 const offset: u64 = if (fat.isFatLibrary(file)) blk: {
1300 const offset = parseFatLibrary(ctx, file, full_path, link_options.target.cpu.arch) catch |err| switch (err) {
1301 error.MissingArch => break,
1302 else => |e| return e,
1303 };
1304 try file.seekTo(offset);
1305 break :blk offset;
1306 } else 0;
1307
1308 if (Dylib.isDylib(file, offset)) {
1309 try parseDylib(ctx, gpa, file, full_path, offset, dependent_libs, link_options, .{
1310 .dependent = true,
1311 .weak = weak,
1312 });
1313 } else {
1314 parseLibStub(ctx, gpa, file, full_path, dependent_libs, link_options, .{
1315 .dependent = true,
1316 .weak = weak,
1317 }) catch |err| switch (err) {
1318 error.NotLibStub, error.UnexpectedToken => continue,
1319 else => |e| return e,
1320 };
1321 }
1322 continue :outer;
10771323 }
1324
1325 // TODO convert into an error
1326 log.err("{s}: unable to resolve dependency", .{dep_id.id.name});
10781327 }
10791328}
10801329
......@@ -2517,7 +2766,7 @@ fn populateMissingMetadata(self: *MachO) !void {
25172766 // The first __TEXT segment is immovable and covers MachO header and load commands.
25182767 self.header_segment_cmd_index = @as(u8, @intCast(self.segments.items.len));
25192768 const ideal_size = @max(self.base.options.headerpad_size orelse 0, default_headerpad_size);
2520 const needed_size = mem.alignForward(u64, padToIdeal(ideal_size), getPageSize(self.base.options.target.cpu.arch));
2769 const needed_size = mem.alignForward(u64, padToIdeal(ideal_size), getPageSize(cpu_arch));
25212770
25222771 log.debug("found __TEXT segment (header-only) free space 0x{x} to 0x{x}", .{ 0, needed_size });
25232772
src/link/MachO/Archive.zig+10-26
......@@ -87,6 +87,13 @@ const ar_hdr = extern struct {
8787 }
8888};
8989
90pub fn isArchive(file: fs.File, fat_offset: u64) bool {
91 const reader = file.reader();
92 const magic = reader.readBytesNoEof(SARMAG) catch return false;
93 defer file.seekTo(fat_offset) catch {};
94 return mem.eql(u8, &magic, ARMAG);
95}
96
9097pub fn deinit(self: *Archive, allocator: Allocator) void {
9198 self.file.close();
9299 for (self.toc.keys()) |*key| {
......@@ -100,21 +107,8 @@ pub fn deinit(self: *Archive, allocator: Allocator) void {
100107}
101108
102109pub fn parse(self: *Archive, allocator: Allocator, reader: anytype) !void {
103 const magic = try reader.readBytesNoEof(SARMAG);
104 if (!mem.eql(u8, &magic, ARMAG)) {
105 log.debug("invalid magic: expected '{s}', found '{s}'", .{ ARMAG, magic });
106 return error.NotArchive;
107 }
108
110 _ = try reader.readBytesNoEof(SARMAG);
109111 self.header = try reader.readStruct(ar_hdr);
110 if (!mem.eql(u8, &self.header.ar_fmag, ARFMAG)) {
111 log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{
112 ARFMAG,
113 self.header.ar_fmag,
114 });
115 return error.NotArchive;
116 }
117
118112 const name_or_length = try self.header.nameOrLength();
119113 var embedded_name = try parseName(allocator, name_or_length, reader);
120114 log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, self.name });
......@@ -182,22 +176,12 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !
182176 }
183177}
184178
185pub fn parseObject(
186 self: Archive,
187 gpa: Allocator,
188 cpu_arch: std.Target.Cpu.Arch,
189 offset: u32,
190) !Object {
179pub fn parseObject(self: Archive, gpa: Allocator, offset: u32) !Object {
191180 const reader = self.file.reader();
192181 try reader.context.seekTo(self.fat_offset + offset);
193182
194183 const object_header = try reader.readStruct(ar_hdr);
195184
196 if (!mem.eql(u8, &object_header.ar_fmag, ARFMAG)) {
197 log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, object_header.ar_fmag });
198 return error.MalformedArchive;
199 }
200
201185 const name_or_length = try object_header.nameOrLength();
202186 const object_name = try parseName(gpa, name_or_length, reader);
203187 defer gpa.free(object_name);
......@@ -227,7 +211,7 @@ pub fn parseObject(
227211 .contents = contents,
228212 };
229213
230 try object.parse(gpa, cpu_arch);
214 try object.parse(gpa);
231215
232216 return object;
233217}
src/link/MachO/Dylib.zig+13-20
......@@ -20,6 +20,8 @@ const Tbd = tapi.Tbd;
2020
2121id: ?Id = null,
2222weak: bool = false,
23/// Header is only set if Dylib is parsed directly from a binary and not a stub file.
24header: ?macho.mach_header_64 = null,
2325
2426/// Parsed symbol table represented as hash map of symbols'
2527/// names. We can and should defer creating *Symbols until
......@@ -116,6 +118,13 @@ pub const Id = struct {
116118 }
117119};
118120
121pub fn isDylib(file: std.fs.File, fat_offset: u64) bool {
122 const reader = file.reader();
123 const hdr = reader.readStruct(macho.mach_header_64) catch return false;
124 defer file.seekTo(fat_offset) catch {};
125 return hdr.filetype == macho.MH_DYLIB;
126}
127
119128pub fn deinit(self: *Dylib, allocator: Allocator) void {
120129 for (self.symbols.keys()) |key| {
121130 allocator.free(key);
......@@ -129,7 +138,6 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {
129138pub fn parseFromBinary(
130139 self: *Dylib,
131140 allocator: Allocator,
132 cpu_arch: std.Target.Cpu.Arch,
133141 dylib_id: u16,
134142 dependent_libs: anytype,
135143 name: []const u8,
......@@ -140,27 +148,12 @@ pub fn parseFromBinary(
140148
141149 log.debug("parsing shared library '{s}'", .{name});
142150
143 const header = try reader.readStruct(macho.mach_header_64);
144
145 if (header.filetype != macho.MH_DYLIB) {
146 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, header.filetype });
147 return error.NotDylib;
148 }
149
150 const this_arch: std.Target.Cpu.Arch = try fat.decodeArch(header.cputype, true);
151
152 if (this_arch != cpu_arch) {
153 log.err("mismatched cpu architecture: expected {s}, found {s}", .{
154 @tagName(cpu_arch),
155 @tagName(this_arch),
156 });
157 return error.MismatchedCpuArchitecture;
158 }
151 self.header = try reader.readStruct(macho.mach_header_64);
159152
160 const should_lookup_reexports = header.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
153 const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
161154 var it = LoadCommandIterator{
162 .ncmds = header.ncmds,
163 .buffer = data[@sizeOf(macho.mach_header_64)..][0..header.sizeofcmds],
155 .ncmds = self.header.?.ncmds,
156 .buffer = data[@sizeOf(macho.mach_header_64)..][0..self.header.?.sizeofcmds],
164157 };
165158 while (it.next()) |cmd| {
166159 switch (cmd.cmd()) {
src/link/MachO/Object.zig+11-30
......@@ -91,6 +91,13 @@ const Record = struct {
9191 reloc: Entry,
9292};
9393
94pub fn isObject(file: std.fs.File) bool {
95 const reader = file.reader();
96 const hdr = reader.readStruct(macho.mach_header_64) catch return false;
97 defer file.seekTo(0) catch {};
98 return hdr.filetype == macho.MH_OBJECT;
99}
100
94101pub fn deinit(self: *Object, gpa: Allocator) void {
95102 self.atoms.deinit(gpa);
96103 self.exec_atoms.deinit(gpa);
......@@ -118,36 +125,12 @@ pub fn deinit(self: *Object, gpa: Allocator) void {
118125 self.data_in_code.deinit(gpa);
119126}
120127
121pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {
128pub fn parse(self: *Object, allocator: Allocator) !void {
122129 var stream = std.io.fixedBufferStream(self.contents);
123130 const reader = stream.reader();
124131
125132 self.header = try reader.readStruct(macho.mach_header_64);
126133
127 if (self.header.filetype != macho.MH_OBJECT) {
128 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{
129 macho.MH_OBJECT,
130 self.header.filetype,
131 });
132 return error.NotObject;
133 }
134
135 const this_arch: std.Target.Cpu.Arch = switch (self.header.cputype) {
136 macho.CPU_TYPE_ARM64 => .aarch64,
137 macho.CPU_TYPE_X86_64 => .x86_64,
138 else => |value| {
139 log.err("unsupported cpu architecture 0x{x}", .{value});
140 return error.UnsupportedCpuArchitecture;
141 },
142 };
143 if (this_arch != cpu_arch) {
144 log.err("mismatched cpu architecture: expected {s}, found {s}", .{
145 @tagName(cpu_arch),
146 @tagName(this_arch),
147 });
148 return error.MismatchedCpuArchitecture;
149 }
150
151134 var it = LoadCommandIterator{
152135 .ncmds = self.header.ncmds,
153136 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
......@@ -437,7 +420,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
437420 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we
438421 // have to infer the start of undef section in the symtab ourselves.
439422 const iundefsym = blk: {
440 const dysymtab = self.parseDysymtab() orelse {
423 const dysymtab = self.getDysymtab() orelse {
441424 var iundefsym: usize = self.in_symtab.?.len;
442425 while (iundefsym > 0) : (iundefsym -= 1) {
443426 const sym = self.symtab[iundefsym - 1];
......@@ -945,16 +928,14 @@ fn diceLessThan(ctx: void, lhs: macho.data_in_code_entry, rhs: macho.data_in_cod
945928 return lhs.offset < rhs.offset;
946929}
947930
948fn parseDysymtab(self: Object) ?macho.dysymtab_command {
931fn getDysymtab(self: Object) ?macho.dysymtab_command {
949932 var it = LoadCommandIterator{
950933 .ncmds = self.header.ncmds,
951934 .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
952935 };
953936 while (it.next()) |cmd| {
954937 switch (cmd.cmd()) {
955 .DYSYMTAB => {
956 return cmd.cast(macho.dysymtab_command).?;
957 },
938 .DYSYMTAB => return cmd.cast(macho.dysymtab_command).?,
958939 else => {},
959940 }
960941 } else return null;
src/link/MachO/fat.zig+23-25
......@@ -1,42 +1,40 @@
11const std = @import("std");
2const assert = std.debug.assert;
23const log = std.log.scoped(.archive);
34const macho = std.macho;
45const mem = std.mem;
56
6pub fn decodeArch(cputype: macho.cpu_type_t, comptime logError: bool) !std.Target.Cpu.Arch {
7 const cpu_arch: std.Target.Cpu.Arch = switch (cputype) {
8 macho.CPU_TYPE_ARM64 => .aarch64,
9 macho.CPU_TYPE_X86_64 => .x86_64,
10 else => {
11 if (logError) {
12 log.err("unsupported cpu architecture 0x{x}", .{cputype});
13 }
14 return error.UnsupportedCpuArchitecture;
15 },
16 };
17 return cpu_arch;
7pub fn isFatLibrary(file: std.fs.File) bool {
8 const reader = file.reader();
9 const hdr = reader.readStructBig(macho.fat_header) catch return false;
10 defer file.seekTo(0) catch {};
11 return hdr.magic == macho.FAT_MAGIC;
1812}
1913
20pub fn getLibraryOffset(reader: anytype, cpu_arch: std.Target.Cpu.Arch) !u64 {
14pub const Arch = struct {
15 tag: std.Target.Cpu.Arch,
16 offset: u64,
17};
18
19pub fn parseArchs(file: std.fs.File, buffer: *[2]Arch) ![]const Arch {
20 const reader = file.reader();
2121 const fat_header = try reader.readStructBig(macho.fat_header);
22 if (fat_header.magic != macho.FAT_MAGIC) return 0;
22 assert(fat_header.magic == macho.FAT_MAGIC);
2323
24 var count: usize = 0;
2425 var fat_arch_index: u32 = 0;
2526 while (fat_arch_index < fat_header.nfat_arch) : (fat_arch_index += 1) {
2627 const fat_arch = try reader.readStructBig(macho.fat_arch);
2728 // If we come across an architecture that we do not know how to handle, that's
2829 // fine because we can keep looking for one that might match.
29 const lib_arch = decodeArch(fat_arch.cputype, false) catch |err| switch (err) {
30 error.UnsupportedCpuArchitecture => continue,
30 const arch: std.Target.Cpu.Arch = switch (fat_arch.cputype) {
31 macho.CPU_TYPE_ARM64 => if (fat_arch.cpusubtype == macho.CPU_SUBTYPE_ARM_ALL) .aarch64 else continue,
32 macho.CPU_TYPE_X86_64 => if (fat_arch.cpusubtype == macho.CPU_SUBTYPE_X86_64_ALL) .x86_64 else continue,
33 else => continue,
3134 };
32 if (lib_arch == cpu_arch) {
33 // We have found a matching architecture!
34 return fat_arch.offset;
35 }
36 } else {
37 log.err("Could not find matching cpu architecture in fat library: expected {s}", .{
38 @tagName(cpu_arch),
39 });
40 return error.MismatchedCpuArchitecture;
35 buffer[count] = .{ .tag = arch, .offset = fat_arch.offset };
36 count += 1;
4137 }
38
39 return buffer[0..count];
4240}
src/link/MachO/zld.zig+56-326
......@@ -89,298 +89,6 @@ pub const Zld = struct {
8989
9090 atoms: std.ArrayListUnmanaged(Atom) = .{},
9191
92 fn parseObject(self: *Zld, path: []const u8) !bool {
93 const gpa = self.gpa;
94 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
95 error.FileNotFound => return false,
96 else => |e| return e,
97 };
98 defer file.close();
99
100 const name = try gpa.dupe(u8, path);
101 errdefer gpa.free(name);
102 const cpu_arch = self.options.target.cpu.arch;
103 const mtime: u64 = mtime: {
104 const stat = file.stat() catch break :mtime 0;
105 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
106 };
107 const file_stat = try file.stat();
108 const file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
109 const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null);
110
111 var object = Object{
112 .name = name,
113 .mtime = mtime,
114 .contents = contents,
115 };
116
117 object.parse(gpa, cpu_arch) catch |err| switch (err) {
118 error.EndOfStream, error.NotObject => {
119 object.deinit(gpa);
120 return false;
121 },
122 else => |e| return e,
123 };
124
125 try self.objects.append(gpa, object);
126
127 return true;
128 }
129
130 fn parseArchive(self: *Zld, path: []const u8, force_load: bool) !bool {
131 const gpa = self.gpa;
132 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
133 error.FileNotFound => return false,
134 else => |e| return e,
135 };
136 errdefer file.close();
137
138 const name = try gpa.dupe(u8, path);
139 errdefer gpa.free(name);
140 const cpu_arch = self.options.target.cpu.arch;
141 const reader = file.reader();
142 const fat_offset = try fat.getLibraryOffset(reader, cpu_arch);
143 try reader.context.seekTo(fat_offset);
144
145 var archive = Archive{
146 .name = name,
147 .fat_offset = fat_offset,
148 .file = file,
149 };
150
151 archive.parse(gpa, reader) catch |err| switch (err) {
152 error.EndOfStream, error.NotArchive => {
153 archive.deinit(gpa);
154 return false;
155 },
156 else => |e| return e,
157 };
158
159 if (force_load) {
160 defer archive.deinit(gpa);
161 // Get all offsets from the ToC
162 var offsets = std.AutoArrayHashMap(u32, void).init(gpa);
163 defer offsets.deinit();
164 for (archive.toc.values()) |offs| {
165 for (offs.items) |off| {
166 _ = try offsets.getOrPut(off);
167 }
168 }
169 for (offsets.keys()) |off| {
170 const object = try archive.parseObject(gpa, cpu_arch, off);
171 try self.objects.append(gpa, object);
172 }
173 } else {
174 try self.archives.append(gpa, archive);
175 }
176
177 return true;
178 }
179
180 const ParseDylibError = error{
181 OutOfMemory,
182 EmptyStubFile,
183 MismatchedCpuArchitecture,
184 UnsupportedCpuArchitecture,
185 EndOfStream,
186 } || fs.File.OpenError || std.os.PReadError || Dylib.Id.ParseError;
187
188 const DylibCreateOpts = struct {
189 syslibroot: ?[]const u8,
190 id: ?Dylib.Id = null,
191 dependent: bool = false,
192 needed: bool = false,
193 weak: bool = false,
194 };
195
196 fn parseDylib(
197 self: *Zld,
198 path: []const u8,
199 dependent_libs: anytype,
200 opts: DylibCreateOpts,
201 ) ParseDylibError!bool {
202 const gpa = self.gpa;
203 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
204 error.FileNotFound => return false,
205 else => |e| return e,
206 };
207 defer file.close();
208
209 const cpu_arch = self.options.target.cpu.arch;
210 const file_stat = try file.stat();
211 var file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
212
213 const reader = file.reader();
214 const fat_offset = math.cast(usize, try fat.getLibraryOffset(reader, cpu_arch)) orelse
215 return error.Overflow;
216 try file.seekTo(fat_offset);
217 file_size -= fat_offset;
218
219 const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null);
220 defer gpa.free(contents);
221
222 const dylib_id = @as(u16, @intCast(self.dylibs.items.len));
223 var dylib = Dylib{ .weak = opts.weak };
224
225 dylib.parseFromBinary(
226 gpa,
227 cpu_arch,
228 dylib_id,
229 dependent_libs,
230 path,
231 contents,
232 ) catch |err| switch (err) {
233 error.EndOfStream, error.NotDylib => {
234 try file.seekTo(0);
235
236 var lib_stub = LibStub.loadFromFile(gpa, file) catch {
237 dylib.deinit(gpa);
238 return false;
239 };
240 defer lib_stub.deinit();
241
242 try dylib.parseFromStub(
243 gpa,
244 self.options.target,
245 lib_stub,
246 dylib_id,
247 dependent_libs,
248 path,
249 );
250 },
251 else => |e| return e,
252 };
253
254 if (opts.id) |id| {
255 if (dylib.id.?.current_version < id.compatibility_version) {
256 log.warn("found dylib is incompatible with the required minimum version", .{});
257 log.warn(" dylib: {s}", .{id.name});
258 log.warn(" required minimum version: {}", .{id.compatibility_version});
259 log.warn(" dylib version: {}", .{dylib.id.?.current_version});
260
261 // TODO maybe this should be an error and facilitate auto-cleanup?
262 dylib.deinit(gpa);
263 return false;
264 }
265 }
266
267 try self.dylibs.append(gpa, dylib);
268 try self.dylibs_map.putNoClobber(gpa, dylib.id.?.name, dylib_id);
269
270 const should_link_dylib_even_if_unreachable = blk: {
271 if (self.options.dead_strip_dylibs and !opts.needed) break :blk false;
272 break :blk !(opts.dependent or self.referenced_dylibs.contains(dylib_id));
273 };
274
275 if (should_link_dylib_even_if_unreachable) {
276 try self.referenced_dylibs.putNoClobber(gpa, dylib_id, {});
277 }
278
279 return true;
280 }
281
282 fn parseInputFiles(
283 self: *Zld,
284 files: []const []const u8,
285 syslibroot: ?[]const u8,
286 dependent_libs: anytype,
287 ) !void {
288 for (files) |file_name| {
289 const full_path = full_path: {
290 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
291 break :full_path try fs.realpath(file_name, &buffer);
292 };
293 log.debug("parsing input file path '{s}'", .{full_path});
294
295 if (try self.parseObject(full_path)) continue;
296 if (try self.parseArchive(full_path, false)) continue;
297 if (try self.parseDylib(full_path, dependent_libs, .{
298 .syslibroot = syslibroot,
299 })) continue;
300
301 log.debug("unknown filetype for positional input file: '{s}'", .{file_name});
302 }
303 }
304
305 fn parseAndForceLoadStaticArchives(self: *Zld, files: []const []const u8) !void {
306 for (files) |file_name| {
307 const full_path = full_path: {
308 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
309 break :full_path try fs.realpath(file_name, &buffer);
310 };
311 log.debug("parsing and force loading static archive '{s}'", .{full_path});
312
313 if (try self.parseArchive(full_path, true)) continue;
314 log.debug("unknown filetype: expected static archive: '{s}'", .{file_name});
315 }
316 }
317
318 fn parseLibs(
319 self: *Zld,
320 lib_names: []const []const u8,
321 lib_infos: []const link.SystemLib,
322 syslibroot: ?[]const u8,
323 dependent_libs: anytype,
324 ) !void {
325 for (lib_names, 0..) |lib, i| {
326 const lib_info = lib_infos[i];
327 log.debug("parsing lib path '{s}'", .{lib});
328 if (try self.parseDylib(lib, dependent_libs, .{
329 .syslibroot = syslibroot,
330 .needed = lib_info.needed,
331 .weak = lib_info.weak,
332 })) continue;
333 if (try self.parseArchive(lib, false)) continue;
334
335 log.debug("unknown filetype for a library: '{s}'", .{lib});
336 }
337 }
338
339 fn parseDependentLibs(self: *Zld, syslibroot: ?[]const u8, dependent_libs: anytype) !void {
340 // At this point, we can now parse dependents of dylibs preserving the inclusion order of:
341 // 1) anything on the linker line is parsed first
342 // 2) afterwards, we parse dependents of the included dylibs
343 // TODO this should not be performed if the user specifies `-flat_namespace` flag.
344 // See ld64 manpages.
345 var arena_alloc = std.heap.ArenaAllocator.init(self.gpa);
346 const arena = arena_alloc.allocator();
347 defer arena_alloc.deinit();
348
349 while (dependent_libs.readItem()) |*dep_id| {
350 defer dep_id.id.deinit(self.gpa);
351
352 if (self.dylibs_map.contains(dep_id.id.name)) continue;
353
354 const weak = self.dylibs.items[dep_id.parent].weak;
355 const has_ext = blk: {
356 const basename = fs.path.basename(dep_id.id.name);
357 break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;
358 };
359 const extension = if (has_ext) fs.path.extension(dep_id.id.name) else "";
360 const without_ext = if (has_ext) blk: {
361 const index = mem.lastIndexOfScalar(u8, dep_id.id.name, '.') orelse unreachable;
362 break :blk dep_id.id.name[0..index];
363 } else dep_id.id.name;
364
365 for (&[_][]const u8{ extension, ".tbd" }) |ext| {
366 const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ without_ext, ext });
367 const full_path = if (syslibroot) |root| try fs.path.join(arena, &.{ root, with_ext }) else with_ext;
368
369 log.debug("trying dependency at fully resolved path {s}", .{full_path});
370
371 const did_parse_successfully = try self.parseDylib(full_path, dependent_libs, .{
372 .id = dep_id.id,
373 .syslibroot = syslibroot,
374 .dependent = true,
375 .weak = weak,
376 });
377 if (did_parse_successfully) break;
378 } else {
379 log.debug("unable to resolve dependency {s}", .{dep_id.id.name});
380 }
381 }
382 }
383
38492 pub fn getOutputSection(self: *Zld, sect: macho.section_64) !?u8 {
38593 const segname = sect.segName();
38694 const sectname = sect.sectName();
......@@ -1009,7 +717,7 @@ pub const Zld = struct {
1009717 if (self.archives.items.len == 0) return;
1010718
1011719 const gpa = self.gpa;
1012 const cpu_arch = self.options.target.cpu.arch;
720
1013721 var next_sym: usize = 0;
1014722 loop: while (next_sym < resolver.unresolved.count()) {
1015723 const global = self.globals.items[resolver.unresolved.keys()[next_sym]];
......@@ -1024,13 +732,7 @@ pub const Zld = struct {
1024732 assert(offsets.items.len > 0);
1025733
1026734 const object_id = @as(u16, @intCast(self.objects.items.len));
1027 const object = archive.parseObject(gpa, cpu_arch, offsets.items[0]) catch |e| switch (e) {
1028 error.MismatchedCpuArchitecture => {
1029 log.err("CPU architecture mismatch found in {s}", .{archive.name});
1030 return e;
1031 },
1032 else => return e,
1033 };
735 const object = try archive.parseObject(gpa, offsets.items[0]);
1034736 try self.objects.append(gpa, object);
1035737 try self.resolveSymbolsInObject(object_id, resolver);
1036738
......@@ -3512,37 +3214,27 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
35123214 try zld.strtab.buffer.append(gpa, 0);
35133215
35143216 // Positional arguments to the linker such as object files and static archives.
3515 var positionals = std.ArrayList([]const u8).init(arena);
3217 var positionals = std.ArrayList(Compilation.LinkObject).init(arena);
35163218 try positionals.ensureUnusedCapacity(options.objects.len);
3517
3518 var must_link_archives = std.StringArrayHashMap(void).init(arena);
3519 try must_link_archives.ensureUnusedCapacity(options.objects.len);
3520
3521 for (options.objects) |obj| {
3522 if (must_link_archives.contains(obj.path)) continue;
3523 if (obj.must_link) {
3524 _ = must_link_archives.getOrPutAssumeCapacity(obj.path);
3525 } else {
3526 _ = positionals.appendAssumeCapacity(obj.path);
3527 }
3528 }
3219 positionals.appendSliceAssumeCapacity(options.objects);
35293220
35303221 for (comp.c_object_table.keys()) |key| {
3531 try positionals.append(key.status.success.object_path);
3222 try positionals.append(.{ .path = key.status.success.object_path });
35323223 }
35333224
35343225 if (module_obj_path) |p| {
3535 try positionals.append(p);
3226 try positionals.append(.{ .path = p });
35363227 }
35373228
35383229 if (comp.compiler_rt_lib) |lib| {
3539 try positionals.append(lib.full_object_path);
3230 try positionals.append(.{ .path = lib.full_object_path });
35403231 }
35413232
35423233 // libc++ dep
35433234 if (options.link_libcpp) {
3544 try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
3545 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
3235 try positionals.ensureUnusedCapacity(2);
3236 positionals.appendAssumeCapacity(.{ .path = comp.libcxxabi_static_lib.?.full_object_path });
3237 positionals.appendAssumeCapacity(.{ .path = comp.libcxx_static_lib.?.full_object_path });
35463238 }
35473239
35483240 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
......@@ -3621,6 +3313,9 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
36213313 }
36223314
36233315 for (options.objects) |obj| {
3316 if (obj.must_link) {
3317 try argv.append("-force_load");
3318 }
36243319 try argv.append(obj.path);
36253320 }
36263321
......@@ -3682,10 +3377,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
36823377 try argv.append("dynamic_lookup");
36833378 }
36843379
3685 for (must_link_archives.keys()) |lib| {
3686 try argv.append(try std.fmt.allocPrint(arena, "-force_load {s}", .{lib}));
3687 }
3688
36893380 Compilation.dump_argv(argv.items);
36903381 }
36913382
......@@ -3694,10 +3385,49 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
36943385 parent: u16,
36953386 }, .Dynamic).init(arena);
36963387
3697 try zld.parseInputFiles(positionals.items, options.sysroot, &dependent_libs);
3698 try zld.parseAndForceLoadStaticArchives(must_link_archives.keys());
3699 try zld.parseLibs(libs.keys(), libs.values(), options.sysroot, &dependent_libs);
3700 try zld.parseDependentLibs(options.sysroot, &dependent_libs);
3388 for (positionals.items) |obj| {
3389 const in_file = try std.fs.cwd().openFile(obj.path, .{});
3390 defer in_file.close();
3391
3392 MachO.parsePositional(
3393 &zld,
3394 gpa,
3395 in_file,
3396 obj.path,
3397 obj.must_link,
3398 &dependent_libs,
3399 options,
3400 ) catch |err| {
3401 // TODO convert to error
3402 log.err("{s}: parsing positional failed with err {s}", .{ obj.path, @errorName(err) });
3403 continue;
3404 };
3405 }
3406
3407 for (libs.keys(), libs.values()) |path, lib| {
3408 const in_file = try std.fs.cwd().openFile(path, .{});
3409 defer in_file.close();
3410
3411 MachO.parseLibrary(
3412 &zld,
3413 gpa,
3414 in_file,
3415 path,
3416 lib,
3417 false,
3418 &dependent_libs,
3419 options,
3420 ) catch |err| {
3421 // TODO convert to error
3422 log.err("{s}: parsing library failed with err {s}", .{ path, @errorName(err) });
3423 continue;
3424 };
3425 }
3426
3427 MachO.parseDependentLibs(&zld, gpa, &dependent_libs, options) catch |err| {
3428 // TODO convert to error
3429 log.err("parsing dependent libraries failed with err {s}", .{@errorName(err)});
3430 };
37013431
37023432 var resolver = SymbolResolver{
37033433 .arena = arena,