| author | |
| committer | |
| log | 702bcfecf5732eceada9bfbca0804c706c238d49 |
| tree | 7a54653dd6e7dd77c0e5c59b98689bf0cb185853 |
| parent | 69193a4ae421a1d69481addbba03e459df8d2a14 |
6 files changed, 489 insertions(+), 554 deletions(-)
src/link/MachO.zig+376-127| ... | @@ -111,6 +111,8 @@ dysymtab_cmd: macho.dysymtab_command = .{}, | ... | @@ -111,6 +111,8 @@ dysymtab_cmd: macho.dysymtab_command = .{}, |
| 111 | uuid_cmd: macho.uuid_command = .{}, | 111 | uuid_cmd: macho.uuid_command = .{}, |
| 112 | codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE }, | 112 | codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE }, |
| 113 | 113 | ||
| 114 | objects: std.ArrayListUnmanaged(Object) = .{}, | ||
| 115 | archives: std.ArrayListUnmanaged(Archive) = .{}, | ||
| 114 | dylibs: std.ArrayListUnmanaged(Dylib) = .{}, | 116 | dylibs: std.ArrayListUnmanaged(Dylib) = .{}, |
| 115 | dylibs_map: std.StringHashMapUnmanaged(u16) = .{}, | 117 | dylibs_map: std.StringHashMapUnmanaged(u16) = .{}, |
| 116 | referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{}, | 118 | referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{}, |
| ... | @@ -586,8 +588,30 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -586,8 +588,30 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 586 | parent: u16, | 588 | parent: u16, |
| 587 | }, .Dynamic).init(arena); | 589 | }, .Dynamic).init(arena); |
| 588 | 590 | ||
| 589 | try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs); | 591 | for (libs.keys(), libs.values()) |path, lib| { |
| 590 | try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs); | 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 | }; | ||
| 591 | } | 615 | } |
| 592 | 616 | ||
| 593 | if (self.dyld_stub_binder_index == null) { | 617 | if (self.dyld_stub_binder_index == null) { |
| ... | @@ -880,175 +904,373 @@ fn resolveLib( | ... | @@ -880,175 +904,373 @@ fn resolveLib( |
| 880 | return full_path; | 904 | return full_path; |
| 881 | } | 905 | } |
| 882 | 906 | ||
| 883 | const ParseDylibError = error{ | 907 | pub fn parsePositional( |
| 884 | OutOfMemory, | 908 | ctx: anytype, |
| 885 | EmptyStubFile, | 909 | gpa: Allocator, |
| 886 | MismatchedCpuArchitecture, | 910 | file: std.fs.File, |
| 887 | UnsupportedCpuArchitecture, | 911 | path: []const u8, |
| 888 | EndOfStream, | 912 | must_link: bool, |
| 889 | } || fs.File.OpenError || std.os.PReadError || Dylib.Id.ParseError; | 913 | dependent_libs: anytype, |
| 914 | link_options: *const link.Options, | ||
| 915 | ) !void { | ||
| 916 | const tracy = trace(@src()); | ||
| 917 | defer tracy.end(); | ||
| 890 | 918 | ||
| 891 | const DylibCreateOpts = struct { | 919 | if (Object.isObject(file)) { |
| 892 | syslibroot: ?[]const u8, | 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 | |||
| 930 | fn 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 | |||
| 974 | pub 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 | |||
| 1030 | pub 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 | |||
| 1049 | fn 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 | |||
| 1115 | const DylibOpts = struct { | ||
| 893 | id: ?Dylib.Id = null, | 1116 | id: ?Dylib.Id = null, |
| 894 | dependent: bool = false, | 1117 | dependent: bool = false, |
| 895 | needed: bool = false, | 1118 | needed: bool = false, |
| 896 | weak: bool = false, | 1119 | weak: bool = false, |
| 897 | }; | 1120 | }; |
| 898 | 1121 | ||
| 899 | pub fn parseDylib( | 1122 | fn parseDylib( |
| 900 | self: *MachO, | 1123 | ctx: anytype, |
| 1124 | gpa: Allocator, | ||
| 1125 | file: std.fs.File, | ||
| 901 | path: []const u8, | 1126 | path: []const u8, |
| 1127 | offset: u64, | ||
| 902 | dependent_libs: anytype, | 1128 | dependent_libs: anytype, |
| 903 | opts: DylibCreateOpts, | 1129 | link_options: *const link.Options, |
| 904 | ) ParseDylibError!bool { | 1130 | dylib_options: DylibOpts, |
| 905 | const gpa = self.base.allocator; | 1131 | ) !void { |
| 906 | const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { | 1132 | const self_cpu_arch = link_options.target.cpu.arch; |
| 907 | error.FileNotFound => return false, | ||
| 908 | else => |e| return e, | ||
| 909 | }; | ||
| 910 | defer file.close(); | ||
| 911 | 1133 | ||
| 912 | const cpu_arch = self.base.options.target.cpu.arch; | ||
| 913 | const file_stat = try file.stat(); | 1134 | const file_stat = try file.stat(); |
| 914 | var file_size = math.cast(usize, file_stat.size) orelse return error.Overflow; | 1135 | var file_size = math.cast(usize, file_stat.size) orelse return error.Overflow; |
| 915 | 1136 | ||
| 916 | const reader = file.reader(); | 1137 | file_size -= offset; |
| 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; | ||
| 921 | 1138 | ||
| 922 | const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null); | 1139 | const contents = try file.readToEndAllocOptions(gpa, file_size, file_size, @alignOf(u64), null); |
| 923 | defer gpa.free(contents); | 1140 | defer gpa.free(contents); |
| 924 | 1141 | ||
| 925 | const dylib_id = @as(u16, @intCast(self.dylibs.items.len)); | 1142 | var dylib = Dylib{ .weak = dylib_options.weak }; |
| 926 | var dylib = Dylib{ .weak = opts.weak }; | 1143 | errdefer dylib.deinit(gpa); |
| 927 | 1144 | ||
| 928 | dylib.parseFromBinary( | 1145 | try dylib.parseFromBinary( |
| 929 | gpa, | 1146 | gpa, |
| 930 | cpu_arch, | 1147 | @intCast(ctx.dylibs.items.len), // TODO defer it till later |
| 931 | dylib_id, | ||
| 932 | dependent_libs, | 1148 | dependent_libs, |
| 933 | path, | 1149 | path, |
| 934 | contents, | 1150 | contents, |
| 935 | ) catch |err| switch (err) { | 1151 | ); |
| 936 | error.EndOfStream, error.NotDylib => { | ||
| 937 | try file.seekTo(0); | ||
| 938 | 1152 | ||
| 939 | var lib_stub = LibStub.loadFromFile(gpa, file) catch { | 1153 | const cpu_arch: std.Target.Cpu.Arch = switch (dylib.header.?.cputype) { |
| 940 | dylib.deinit(gpa); | 1154 | macho.CPU_TYPE_ARM64 => .aarch64, |
| 941 | return false; | 1155 | macho.CPU_TYPE_X86_64 => .x86_64, |
| 942 | }; | 1156 | else => unreachable, |
| 943 | defer lib_stub.deinit(); | 1157 | }; |
| 944 | 1158 | if (self_cpu_arch != cpu_arch) { | |
| 945 | try dylib.parseFromStub( | 1159 | // TODO convert into an error |
| 946 | gpa, | 1160 | log.err("{s}: invalid architecture '{s}', expected '{s}'", .{ |
| 947 | self.base.options.target, | 1161 | path, |
| 948 | lib_stub, | 1162 | @tagName(cpu_arch), |
| 949 | dylib_id, | 1163 | @tagName(self_cpu_arch), |
| 950 | dependent_libs, | 1164 | }); |
| 951 | path, | 1165 | return error.MissingArch; |
| 952 | ); | 1166 | } |
| 953 | }, | 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), | ||
| 954 | else => |e| return e, | 1175 | else => |e| return e, |
| 955 | }; | 1176 | }; |
| 1177 | } | ||
| 956 | 1178 | ||
| 957 | if (opts.id) |id| { | 1179 | fn 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 | |||
| 1216 | fn 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| { | ||
| 958 | if (dylib.id.?.current_version < id.compatibility_version) { | 1224 | if (dylib.id.?.current_version < id.compatibility_version) { |
| 1225 | // TODO convert into an error | ||
| 959 | log.warn("found dylib is incompatible with the required minimum version", .{}); | 1226 | log.warn("found dylib is incompatible with the required minimum version", .{}); |
| 960 | log.warn(" dylib: {s}", .{id.name}); | 1227 | log.warn(" dylib: {s}", .{id.name}); |
| 961 | log.warn(" required minimum version: {}", .{id.compatibility_version}); | 1228 | log.warn(" required minimum version: {}", .{id.compatibility_version}); |
| 962 | log.warn(" dylib version: {}", .{dylib.id.?.current_version}); | 1229 | log.warn(" dylib version: {}", .{dylib.id.?.current_version}); |
| 963 | 1230 | return error.IncompatibleDylibVersion; | |
| 964 | // TODO maybe this should be an error and facilitate auto-cleanup? | ||
| 965 | dylib.deinit(gpa); | ||
| 966 | return false; | ||
| 967 | } | 1231 | } |
| 968 | } | 1232 | } |
| 969 | 1233 | ||
| 970 | try self.dylibs.append(gpa, dylib); | 1234 | const gop = try ctx.dylibs_map.getOrPut(gpa, dylib.id.?.name); |
| 971 | try self.dylibs_map.putNoClobber(gpa, dylib.id.?.name, dylib_id); | 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); | ||
| 972 | 1239 | ||
| 973 | const should_link_dylib_even_if_unreachable = blk: { | 1240 | const should_link_dylib_even_if_unreachable = blk: { |
| 974 | if (self.base.options.dead_strip_dylibs and !opts.needed) break :blk false; | 1241 | if (link_options.dead_strip_dylibs and !dylib_options.needed) break :blk false; |
| 975 | break :blk !(opts.dependent or self.referenced_dylibs.contains(dylib_id)); | 1242 | break :blk !(dylib_options.dependent or ctx.referenced_dylibs.contains(gop.value_ptr.*)); |
| 976 | }; | 1243 | }; |
| 977 | 1244 | ||
| 978 | if (should_link_dylib_even_if_unreachable) { | 1245 | 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.*, {}); |
| 980 | } | 1247 | } |
| 981 | |||
| 982 | return true; | ||
| 983 | } | 1248 | } |
| 984 | 1249 | ||
| 985 | pub fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8, dependent_libs: anytype) !void { | 1250 | pub fn parseDependentLibs( |
| 986 | for (files) |file_name| { | 1251 | ctx: anytype, |
| 987 | const full_path = full_path: { | 1252 | gpa: Allocator, |
| 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 | |||
| 1003 | pub 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 | |||
| 1016 | pub fn parseLibs( | ||
| 1017 | self: *MachO, | ||
| 1018 | lib_names: []const []const u8, | ||
| 1019 | lib_infos: []const link.SystemLib, | ||
| 1020 | syslibroot: ?[]const u8, | ||
| 1021 | dependent_libs: anytype, | 1253 | dependent_libs: anytype, |
| 1254 | link_options: *const link.Options, | ||
| 1022 | ) !void { | 1255 | ) !void { |
| 1023 | for (lib_names, 0..) |lib, i| { | 1256 | const tracy = trace(@src()); |
| 1024 | const lib_info = lib_infos[i]; | 1257 | defer tracy.end(); |
| 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 | } | ||
| 1035 | 1258 | ||
| 1036 | pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: anytype) !void { | ||
| 1037 | // At this point, we can now parse dependents of dylibs preserving the inclusion order of: | 1259 | // At this point, we can now parse dependents of dylibs preserving the inclusion order of: |
| 1038 | // 1) anything on the linker line is parsed first | 1260 | // 1) anything on the linker line is parsed first |
| 1039 | // 2) afterwards, we parse dependents of the included dylibs | 1261 | // 2) afterwards, we parse dependents of the included dylibs |
| 1040 | // TODO this should not be performed if the user specifies `-flat_namespace` flag. | 1262 | // TODO this should not be performed if the user specifies `-flat_namespace` flag. |
| 1041 | // See ld64 manpages. | 1263 | // See ld64 manpages. |
| 1042 | var arena_alloc = std.heap.ArenaAllocator.init(self.base.allocator); | 1264 | var arena_alloc = std.heap.ArenaAllocator.init(gpa); |
| 1043 | const arena = arena_alloc.allocator(); | 1265 | const arena = arena_alloc.allocator(); |
| 1044 | defer arena_alloc.deinit(); | 1266 | defer arena_alloc.deinit(); |
| 1045 | 1267 | ||
| 1046 | while (dependent_libs.readItem()) |*dep_id| { | 1268 | outer: while (dependent_libs.readItem()) |dep_id| { |
| 1047 | defer dep_id.id.deinit(self.base.allocator); | 1269 | defer dep_id.id.deinit(gpa); |
| 1048 | 1270 | ||
| 1049 | if (self.dylibs_map.contains(dep_id.id.name)) continue; | 1271 | if (ctx.dylibs_map.contains(dep_id.id.name)) continue; |
| 1050 | 1272 | ||
| 1051 | const weak = self.dylibs.items[dep_id.parent].weak; | 1273 | const weak = ctx.dylibs.items[dep_id.parent].weak; |
| 1052 | const has_ext = blk: { | 1274 | const has_ext = blk: { |
| 1053 | const basename = fs.path.basename(dep_id.id.name); | 1275 | const basename = fs.path.basename(dep_id.id.name); |
| 1054 | break :blk mem.lastIndexOfScalar(u8, basename, '.') != null; | 1276 | break :blk mem.lastIndexOfScalar(u8, basename, '.') != null; |
| ... | @@ -1061,20 +1283,47 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: | ... | @@ -1061,20 +1283,47 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: |
| 1061 | 1283 | ||
| 1062 | for (&[_][]const u8{ extension, ".tbd" }) |ext| { | 1284 | for (&[_][]const u8{ extension, ".tbd" }) |ext| { |
| 1063 | const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ without_ext, ext }); | 1285 | 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(); | ||
| 1065 | 1296 | ||
| 1066 | log.debug("trying dependency at fully resolved path {s}", .{full_path}); | 1297 | log.debug("trying dependency at fully resolved path {s}", .{full_path}); |
| 1067 | 1298 | ||
| 1068 | const did_parse_successfully = try self.parseDylib(full_path, dependent_libs, .{ | 1299 | const offset: u64 = if (fat.isFatLibrary(file)) blk: { |
| 1069 | .id = dep_id.id, | 1300 | const offset = parseFatLibrary(ctx, file, full_path, link_options.target.cpu.arch) catch |err| switch (err) { |
| 1070 | .syslibroot = syslibroot, | 1301 | error.MissingArch => break, |
| 1071 | .dependent = true, | 1302 | else => |e| return e, |
| 1072 | .weak = weak, | 1303 | }; |
| 1073 | }); | 1304 | try file.seekTo(offset); |
| 1074 | if (did_parse_successfully) break; | 1305 | break :blk offset; |
| 1075 | } else { | 1306 | } else 0; |
| 1076 | log.debug("unable to resolve dependency {s}", .{dep_id.id.name}); | 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; | ||
| 1077 | } | 1323 | } |
| 1324 | |||
| 1325 | // TODO convert into an error | ||
| 1326 | log.err("{s}: unable to resolve dependency", .{dep_id.id.name}); | ||
| 1078 | } | 1327 | } |
| 1079 | } | 1328 | } |
| 1080 | 1329 | ||
| ... | @@ -2517,7 +2766,7 @@ fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -2517,7 +2766,7 @@ fn populateMissingMetadata(self: *MachO) !void { |
| 2517 | // The first __TEXT segment is immovable and covers MachO header and load commands. | 2766 | // The first __TEXT segment is immovable and covers MachO header and load commands. |
| 2518 | self.header_segment_cmd_index = @as(u8, @intCast(self.segments.items.len)); | 2767 | self.header_segment_cmd_index = @as(u8, @intCast(self.segments.items.len)); |
| 2519 | const ideal_size = @max(self.base.options.headerpad_size orelse 0, default_headerpad_size); | 2768 | 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)); |
| 2521 | 2770 | ||
| 2522 | log.debug("found __TEXT segment (header-only) free space 0x{x} to 0x{x}", .{ 0, needed_size }); | 2771 | log.debug("found __TEXT segment (header-only) free space 0x{x} to 0x{x}", .{ 0, needed_size }); |
| 2523 | 2772 |
src/link/MachO/Archive.zig+10-26| ... | @@ -87,6 +87,13 @@ const ar_hdr = extern struct { | ... | @@ -87,6 +87,13 @@ const ar_hdr = extern struct { |
| 87 | } | 87 | } |
| 88 | }; | 88 | }; |
| 89 | 89 | ||
| 90 | pub 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 | |||
| 90 | pub fn deinit(self: *Archive, allocator: Allocator) void { | 97 | pub fn deinit(self: *Archive, allocator: Allocator) void { |
| 91 | self.file.close(); | 98 | self.file.close(); |
| 92 | for (self.toc.keys()) |*key| { | 99 | for (self.toc.keys()) |*key| { |
| ... | @@ -100,21 +107,8 @@ pub fn deinit(self: *Archive, allocator: Allocator) void { | ... | @@ -100,21 +107,8 @@ pub fn deinit(self: *Archive, allocator: Allocator) void { |
| 100 | } | 107 | } |
| 101 | 108 | ||
| 102 | pub fn parse(self: *Archive, allocator: Allocator, reader: anytype) !void { | 109 | pub fn parse(self: *Archive, allocator: Allocator, reader: anytype) !void { |
| 103 | const magic = try reader.readBytesNoEof(SARMAG); | 110 | _ = 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 | |||
| 109 | self.header = try reader.readStruct(ar_hdr); | 111 | 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 | |||
| 118 | const name_or_length = try self.header.nameOrLength(); | 112 | const name_or_length = try self.header.nameOrLength(); |
| 119 | var embedded_name = try parseName(allocator, name_or_length, reader); | 113 | var embedded_name = try parseName(allocator, name_or_length, reader); |
| 120 | log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, self.name }); | 114 | log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, self.name }); |
| ... | @@ -182,22 +176,12 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) ! | ... | @@ -182,22 +176,12 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) ! |
| 182 | } | 176 | } |
| 183 | } | 177 | } |
| 184 | 178 | ||
| 185 | pub fn parseObject( | 179 | pub fn parseObject(self: Archive, gpa: Allocator, offset: u32) !Object { |
| 186 | self: Archive, | ||
| 187 | gpa: Allocator, | ||
| 188 | cpu_arch: std.Target.Cpu.Arch, | ||
| 189 | offset: u32, | ||
| 190 | ) !Object { | ||
| 191 | const reader = self.file.reader(); | 180 | const reader = self.file.reader(); |
| 192 | try reader.context.seekTo(self.fat_offset + offset); | 181 | try reader.context.seekTo(self.fat_offset + offset); |
| 193 | 182 | ||
| 194 | const object_header = try reader.readStruct(ar_hdr); | 183 | const object_header = try reader.readStruct(ar_hdr); |
| 195 | 184 | ||
| 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 | |||
| 201 | const name_or_length = try object_header.nameOrLength(); | 185 | const name_or_length = try object_header.nameOrLength(); |
| 202 | const object_name = try parseName(gpa, name_or_length, reader); | 186 | const object_name = try parseName(gpa, name_or_length, reader); |
| 203 | defer gpa.free(object_name); | 187 | defer gpa.free(object_name); |
| ... | @@ -227,7 +211,7 @@ pub fn parseObject( | ... | @@ -227,7 +211,7 @@ pub fn parseObject( |
| 227 | .contents = contents, | 211 | .contents = contents, |
| 228 | }; | 212 | }; |
| 229 | 213 | ||
| 230 | try object.parse(gpa, cpu_arch); | 214 | try object.parse(gpa); |
| 231 | 215 | ||
| 232 | return object; | 216 | return object; |
| 233 | } | 217 | } |
src/link/MachO/Dylib.zig+13-20| ... | @@ -20,6 +20,8 @@ const Tbd = tapi.Tbd; | ... | @@ -20,6 +20,8 @@ const Tbd = tapi.Tbd; |
| 20 | 20 | ||
| 21 | id: ?Id = null, | 21 | id: ?Id = null, |
| 22 | weak: bool = false, | 22 | weak: bool = false, |
| 23 | /// Header is only set if Dylib is parsed directly from a binary and not a stub file. | ||
| 24 | header: ?macho.mach_header_64 = null, | ||
| 23 | 25 | ||
| 24 | /// Parsed symbol table represented as hash map of symbols' | 26 | /// Parsed symbol table represented as hash map of symbols' |
| 25 | /// names. We can and should defer creating *Symbols until | 27 | /// names. We can and should defer creating *Symbols until |
| ... | @@ -116,6 +118,13 @@ pub const Id = struct { | ... | @@ -116,6 +118,13 @@ pub const Id = struct { |
| 116 | } | 118 | } |
| 117 | }; | 119 | }; |
| 118 | 120 | ||
| 121 | pub 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 | |||
| 119 | pub fn deinit(self: *Dylib, allocator: Allocator) void { | 128 | pub fn deinit(self: *Dylib, allocator: Allocator) void { |
| 120 | for (self.symbols.keys()) |key| { | 129 | for (self.symbols.keys()) |key| { |
| 121 | allocator.free(key); | 130 | allocator.free(key); |
| ... | @@ -129,7 +138,6 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void { | ... | @@ -129,7 +138,6 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void { |
| 129 | pub fn parseFromBinary( | 138 | pub fn parseFromBinary( |
| 130 | self: *Dylib, | 139 | self: *Dylib, |
| 131 | allocator: Allocator, | 140 | allocator: Allocator, |
| 132 | cpu_arch: std.Target.Cpu.Arch, | ||
| 133 | dylib_id: u16, | 141 | dylib_id: u16, |
| 134 | dependent_libs: anytype, | 142 | dependent_libs: anytype, |
| 135 | name: []const u8, | 143 | name: []const u8, |
| ... | @@ -140,27 +148,12 @@ pub fn parseFromBinary( | ... | @@ -140,27 +148,12 @@ pub fn parseFromBinary( |
| 140 | 148 | ||
| 141 | log.debug("parsing shared library '{s}'", .{name}); | 149 | log.debug("parsing shared library '{s}'", .{name}); |
| 142 | 150 | ||
| 143 | const header = try reader.readStruct(macho.mach_header_64); | 151 | self.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 | } | ||
| 159 | 152 | ||
| 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; |
| 161 | var it = LoadCommandIterator{ | 154 | var it = LoadCommandIterator{ |
| 162 | .ncmds = header.ncmds, | 155 | .ncmds = self.header.?.ncmds, |
| 163 | .buffer = data[@sizeOf(macho.mach_header_64)..][0..header.sizeofcmds], | 156 | .buffer = data[@sizeOf(macho.mach_header_64)..][0..self.header.?.sizeofcmds], |
| 164 | }; | 157 | }; |
| 165 | while (it.next()) |cmd| { | 158 | while (it.next()) |cmd| { |
| 166 | switch (cmd.cmd()) { | 159 | switch (cmd.cmd()) { |
src/link/MachO/Object.zig+11-30| ... | @@ -91,6 +91,13 @@ const Record = struct { | ... | @@ -91,6 +91,13 @@ const Record = struct { |
| 91 | reloc: Entry, | 91 | reloc: Entry, |
| 92 | }; | 92 | }; |
| 93 | 93 | ||
| 94 | pub 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 | |||
| 94 | pub fn deinit(self: *Object, gpa: Allocator) void { | 101 | pub fn deinit(self: *Object, gpa: Allocator) void { |
| 95 | self.atoms.deinit(gpa); | 102 | self.atoms.deinit(gpa); |
| 96 | self.exec_atoms.deinit(gpa); | 103 | self.exec_atoms.deinit(gpa); |
| ... | @@ -118,36 +125,12 @@ pub fn deinit(self: *Object, gpa: Allocator) void { | ... | @@ -118,36 +125,12 @@ pub fn deinit(self: *Object, gpa: Allocator) void { |
| 118 | self.data_in_code.deinit(gpa); | 125 | self.data_in_code.deinit(gpa); |
| 119 | } | 126 | } |
| 120 | 127 | ||
| 121 | pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void { | 128 | pub fn parse(self: *Object, allocator: Allocator) !void { |
| 122 | var stream = std.io.fixedBufferStream(self.contents); | 129 | var stream = std.io.fixedBufferStream(self.contents); |
| 123 | const reader = stream.reader(); | 130 | const reader = stream.reader(); |
| 124 | 131 | ||
| 125 | self.header = try reader.readStruct(macho.mach_header_64); | 132 | self.header = try reader.readStruct(macho.mach_header_64); |
| 126 | 133 | ||
| 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 | |||
| 151 | var it = LoadCommandIterator{ | 134 | var it = LoadCommandIterator{ |
| 152 | .ncmds = self.header.ncmds, | 135 | .ncmds = self.header.ncmds, |
| 153 | .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds], | 136 | .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 { | ... | @@ -437,7 +420,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { |
| 437 | // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we | 420 | // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we |
| 438 | // have to infer the start of undef section in the symtab ourselves. | 421 | // have to infer the start of undef section in the symtab ourselves. |
| 439 | const iundefsym = blk: { | 422 | const iundefsym = blk: { |
| 440 | const dysymtab = self.parseDysymtab() orelse { | 423 | const dysymtab = self.getDysymtab() orelse { |
| 441 | var iundefsym: usize = self.in_symtab.?.len; | 424 | var iundefsym: usize = self.in_symtab.?.len; |
| 442 | while (iundefsym > 0) : (iundefsym -= 1) { | 425 | while (iundefsym > 0) : (iundefsym -= 1) { |
| 443 | const sym = self.symtab[iundefsym - 1]; | 426 | 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 | ... | @@ -945,16 +928,14 @@ fn diceLessThan(ctx: void, lhs: macho.data_in_code_entry, rhs: macho.data_in_cod |
| 945 | return lhs.offset < rhs.offset; | 928 | return lhs.offset < rhs.offset; |
| 946 | } | 929 | } |
| 947 | 930 | ||
| 948 | fn parseDysymtab(self: Object) ?macho.dysymtab_command { | 931 | fn getDysymtab(self: Object) ?macho.dysymtab_command { |
| 949 | var it = LoadCommandIterator{ | 932 | var it = LoadCommandIterator{ |
| 950 | .ncmds = self.header.ncmds, | 933 | .ncmds = self.header.ncmds, |
| 951 | .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds], | 934 | .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds], |
| 952 | }; | 935 | }; |
| 953 | while (it.next()) |cmd| { | 936 | while (it.next()) |cmd| { |
| 954 | switch (cmd.cmd()) { | 937 | switch (cmd.cmd()) { |
| 955 | .DYSYMTAB => { | 938 | .DYSYMTAB => return cmd.cast(macho.dysymtab_command).?, |
| 956 | return cmd.cast(macho.dysymtab_command).?; | ||
| 957 | }, | ||
| 958 | else => {}, | 939 | else => {}, |
| 959 | } | 940 | } |
| 960 | } else return null; | 941 | } else return null; |
src/link/MachO/fat.zig+23-25| ... | @@ -1,42 +1,40 @@ | ... | @@ -1,42 +1,40 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const assert = std.debug.assert; | ||
| 2 | const log = std.log.scoped(.archive); | 3 | const log = std.log.scoped(.archive); |
| 3 | const macho = std.macho; | 4 | const macho = std.macho; |
| 4 | const mem = std.mem; | 5 | const mem = std.mem; |
| 5 | 6 | ||
| 6 | pub fn decodeArch(cputype: macho.cpu_type_t, comptime logError: bool) !std.Target.Cpu.Arch { | 7 | pub fn isFatLibrary(file: std.fs.File) bool { |
| 7 | const cpu_arch: std.Target.Cpu.Arch = switch (cputype) { | 8 | const reader = file.reader(); |
| 8 | macho.CPU_TYPE_ARM64 => .aarch64, | 9 | const hdr = reader.readStructBig(macho.fat_header) catch return false; |
| 9 | macho.CPU_TYPE_X86_64 => .x86_64, | 10 | defer file.seekTo(0) catch {}; |
| 10 | else => { | 11 | return hdr.magic == macho.FAT_MAGIC; |
| 11 | if (logError) { | ||
| 12 | log.err("unsupported cpu architecture 0x{x}", .{cputype}); | ||
| 13 | } | ||
| 14 | return error.UnsupportedCpuArchitecture; | ||
| 15 | }, | ||
| 16 | }; | ||
| 17 | return cpu_arch; | ||
| 18 | } | 12 | } |
| 19 | 13 | ||
| 20 | pub fn getLibraryOffset(reader: anytype, cpu_arch: std.Target.Cpu.Arch) !u64 { | 14 | pub const Arch = struct { |
| 15 | tag: std.Target.Cpu.Arch, | ||
| 16 | offset: u64, | ||
| 17 | }; | ||
| 18 | |||
| 19 | pub fn parseArchs(file: std.fs.File, buffer: *[2]Arch) ![]const Arch { | ||
| 20 | const reader = file.reader(); | ||
| 21 | const fat_header = try reader.readStructBig(macho.fat_header); | 21 | 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); |
| 23 | 23 | ||
| 24 | var count: usize = 0; | ||
| 24 | var fat_arch_index: u32 = 0; | 25 | var fat_arch_index: u32 = 0; |
| 25 | while (fat_arch_index < fat_header.nfat_arch) : (fat_arch_index += 1) { | 26 | while (fat_arch_index < fat_header.nfat_arch) : (fat_arch_index += 1) { |
| 26 | const fat_arch = try reader.readStructBig(macho.fat_arch); | 27 | const fat_arch = try reader.readStructBig(macho.fat_arch); |
| 27 | // If we come across an architecture that we do not know how to handle, that's | 28 | // If we come across an architecture that we do not know how to handle, that's |
| 28 | // fine because we can keep looking for one that might match. | 29 | // 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 | const arch: std.Target.Cpu.Arch = switch (fat_arch.cputype) { |
| 30 | error.UnsupportedCpuArchitecture => continue, | 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, | ||
| 31 | }; | 34 | }; |
| 32 | if (lib_arch == cpu_arch) { | 35 | buffer[count] = .{ .tag = arch, .offset = fat_arch.offset }; |
| 33 | // We have found a matching architecture! | 36 | count += 1; |
| 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; | ||
| 41 | } | 37 | } |
| 38 | |||
| 39 | return buffer[0..count]; | ||
| 42 | } | 40 | } |
src/link/MachO/zld.zig+56-326| ... | @@ -89,298 +89,6 @@ pub const Zld = struct { | ... | @@ -89,298 +89,6 @@ pub const Zld = struct { |
| 89 | 89 | ||
| 90 | atoms: std.ArrayListUnmanaged(Atom) = .{}, | 90 | atoms: std.ArrayListUnmanaged(Atom) = .{}, |
| 91 | 91 | ||
| 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 | |||
| 384 | pub fn getOutputSection(self: *Zld, sect: macho.section_64) !?u8 { | 92 | pub fn getOutputSection(self: *Zld, sect: macho.section_64) !?u8 { |
| 385 | const segname = sect.segName(); | 93 | const segname = sect.segName(); |
| 386 | const sectname = sect.sectName(); | 94 | const sectname = sect.sectName(); |
| ... | @@ -1009,7 +717,7 @@ pub const Zld = struct { | ... | @@ -1009,7 +717,7 @@ pub const Zld = struct { |
| 1009 | if (self.archives.items.len == 0) return; | 717 | if (self.archives.items.len == 0) return; |
| 1010 | 718 | ||
| 1011 | const gpa = self.gpa; | 719 | const gpa = self.gpa; |
| 1012 | const cpu_arch = self.options.target.cpu.arch; | 720 | |
| 1013 | var next_sym: usize = 0; | 721 | var next_sym: usize = 0; |
| 1014 | loop: while (next_sym < resolver.unresolved.count()) { | 722 | loop: while (next_sym < resolver.unresolved.count()) { |
| 1015 | const global = self.globals.items[resolver.unresolved.keys()[next_sym]]; | 723 | const global = self.globals.items[resolver.unresolved.keys()[next_sym]]; |
| ... | @@ -1024,13 +732,7 @@ pub const Zld = struct { | ... | @@ -1024,13 +732,7 @@ pub const Zld = struct { |
| 1024 | assert(offsets.items.len > 0); | 732 | assert(offsets.items.len > 0); |
| 1025 | 733 | ||
| 1026 | const object_id = @as(u16, @intCast(self.objects.items.len)); | 734 | 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) { | 735 | const object = try archive.parseObject(gpa, offsets.items[0]); |
| 1028 | error.MismatchedCpuArchitecture => { | ||
| 1029 | log.err("CPU architecture mismatch found in {s}", .{archive.name}); | ||
| 1030 | return e; | ||
| 1031 | }, | ||
| 1032 | else => return e, | ||
| 1033 | }; | ||
| 1034 | try self.objects.append(gpa, object); | 736 | try self.objects.append(gpa, object); |
| 1035 | try self.resolveSymbolsInObject(object_id, resolver); | 737 | try self.resolveSymbolsInObject(object_id, resolver); |
| 1036 | 738 | ||
| ... | @@ -3512,37 +3214,27 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr | ... | @@ -3512,37 +3214,27 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 3512 | try zld.strtab.buffer.append(gpa, 0); | 3214 | try zld.strtab.buffer.append(gpa, 0); |
| 3513 | 3215 | ||
| 3514 | // Positional arguments to the linker such as object files and static archives. | 3216 | // 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); |
| 3516 | try positionals.ensureUnusedCapacity(options.objects.len); | 3218 | try positionals.ensureUnusedCapacity(options.objects.len); |
| 3517 | 3219 | positionals.appendSliceAssumeCapacity(options.objects); | |
| 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 | } | ||
| 3529 | 3220 | ||
| 3530 | for (comp.c_object_table.keys()) |key| { | 3221 | 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 }); |
| 3532 | } | 3223 | } |
| 3533 | 3224 | ||
| 3534 | if (module_obj_path) |p| { | 3225 | if (module_obj_path) |p| { |
| 3535 | try positionals.append(p); | 3226 | try positionals.append(.{ .path = p }); |
| 3536 | } | 3227 | } |
| 3537 | 3228 | ||
| 3538 | if (comp.compiler_rt_lib) |lib| { | 3229 | if (comp.compiler_rt_lib) |lib| { |
| 3539 | try positionals.append(lib.full_object_path); | 3230 | try positionals.append(.{ .path = lib.full_object_path }); |
| 3540 | } | 3231 | } |
| 3541 | 3232 | ||
| 3542 | // libc++ dep | 3233 | // libc++ dep |
| 3543 | if (options.link_libcpp) { | 3234 | if (options.link_libcpp) { |
| 3544 | try positionals.append(comp.libcxxabi_static_lib.?.full_object_path); | 3235 | try positionals.ensureUnusedCapacity(2); |
| 3545 | try positionals.append(comp.libcxx_static_lib.?.full_object_path); | 3236 | positionals.appendAssumeCapacity(.{ .path = comp.libcxxabi_static_lib.?.full_object_path }); |
| 3237 | positionals.appendAssumeCapacity(.{ .path = comp.libcxx_static_lib.?.full_object_path }); | ||
| 3546 | } | 3238 | } |
| 3547 | 3239 | ||
| 3548 | var libs = std.StringArrayHashMap(link.SystemLib).init(arena); | 3240 | var libs = std.StringArrayHashMap(link.SystemLib).init(arena); |
| ... | @@ -3621,6 +3313,9 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr | ... | @@ -3621,6 +3313,9 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 3621 | } | 3313 | } |
| 3622 | 3314 | ||
| 3623 | for (options.objects) |obj| { | 3315 | for (options.objects) |obj| { |
| 3316 | if (obj.must_link) { | ||
| 3317 | try argv.append("-force_load"); | ||
| 3318 | } | ||
| 3624 | try argv.append(obj.path); | 3319 | try argv.append(obj.path); |
| 3625 | } | 3320 | } |
| 3626 | 3321 | ||
| ... | @@ -3682,10 +3377,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr | ... | @@ -3682,10 +3377,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 3682 | try argv.append("dynamic_lookup"); | 3377 | try argv.append("dynamic_lookup"); |
| 3683 | } | 3378 | } |
| 3684 | 3379 | ||
| 3685 | for (must_link_archives.keys()) |lib| { | ||
| 3686 | try argv.append(try std.fmt.allocPrint(arena, "-force_load {s}", .{lib})); | ||
| 3687 | } | ||
| 3688 | |||
| 3689 | Compilation.dump_argv(argv.items); | 3380 | Compilation.dump_argv(argv.items); |
| 3690 | } | 3381 | } |
| 3691 | 3382 | ||
| ... | @@ -3694,10 +3385,49 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr | ... | @@ -3694,10 +3385,49 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 3694 | parent: u16, | 3385 | parent: u16, |
| 3695 | }, .Dynamic).init(arena); | 3386 | }, .Dynamic).init(arena); |
| 3696 | 3387 | ||
| 3697 | try zld.parseInputFiles(positionals.items, options.sysroot, &dependent_libs); | 3388 | for (positionals.items) |obj| { |
| 3698 | try zld.parseAndForceLoadStaticArchives(must_link_archives.keys()); | 3389 | const in_file = try std.fs.cwd().openFile(obj.path, .{}); |
| 3699 | try zld.parseLibs(libs.keys(), libs.values(), options.sysroot, &dependent_libs); | 3390 | defer in_file.close(); |
| 3700 | try zld.parseDependentLibs(options.sysroot, &dependent_libs); | 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 | }; | ||
| 3701 | 3431 | ||
| 3702 | var resolver = SymbolResolver{ | 3432 | var resolver = SymbolResolver{ |
| 3703 | .arena = arena, | 3433 | .arena = arena, |