authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-27 01:49:54+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-01-27 01:49:54+01:00
logd5fc3c635ad94b6934fb9b90549d7f36eb9fa56b
tree81760465cf971d1a268baba8bb7b13df48b61e27
parentb96fb858c88b97617e6fa0e3c6966a8e900caf7f
parentd62e7bbefde641065eb726c3b561defd594ddae8
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #18677 from ziglang/post-new-macho

macho: fix logic for parsing dependent dylibs aka re-exports

1 files changed, 125 insertions(+), 106 deletions(-)

src/link/MachO.zig+125-106
...@@ -844,6 +844,11 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -844,6 +844,11 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
844 try argv.append(arg);844 try argv.append(arg);
845 }845 }
846846
847 for (self.lib_dirs) |lib_dir| {
848 const arg = try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir});
849 try argv.append(arg);
850 }
851
847 for (self.frameworks) |framework| {852 for (self.frameworks) |framework| {
848 const name = std.fs.path.stem(framework.path);853 const name = std.fs.path.stem(framework.path);
849 const arg = if (framework.needed)854 const arg = if (framework.needed)
...@@ -855,6 +860,11 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -855,6 +860,11 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
855 try argv.append(arg);860 try argv.append(arg);
856 }861 }
857862
863 for (self.framework_dirs) |f_dir| {
864 try argv.append("-F");
865 try argv.append(f_dir);
866 }
867
858 if (self.base.isDynLib() and self.base.allow_shlib_undefined) {868 if (self.base.isDynLib() and self.base.allow_shlib_undefined) {
859 try argv.append("-undefined");869 try argv.append("-undefined");
860 try argv.append("dynamic_lookup");870 try argv.append("dynamic_lookup");
...@@ -887,11 +897,11 @@ pub fn resolveLibSystem(...@@ -887,11 +897,11 @@ pub fn resolveLibSystem(
887 if (self.sdk_layout) |sdk_layout| switch (sdk_layout) {897 if (self.sdk_layout) |sdk_layout| switch (sdk_layout) {
888 .sdk => {898 .sdk => {
889 const dir = try fs.path.join(arena, &[_][]const u8{ comp.sysroot.?, "usr", "lib" });899 const dir = try fs.path.join(arena, &[_][]const u8{ comp.sysroot.?, "usr", "lib" });
890 if (try accessLibPath(arena, &test_path, &checked_paths, dir, "libSystem")) break :success;900 if (try accessLibPath(arena, &test_path, &checked_paths, dir, "System")) break :success;
891 },901 },
892 .vendored => {902 .vendored => {
893 const dir = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "darwin" });903 const dir = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "darwin" });
894 if (try accessLibPath(arena, &test_path, &checked_paths, dir, "libSystem")) break :success;904 if (try accessLibPath(arena, &test_path, &checked_paths, dir, "System")) break :success;
895 },905 },
896 };906 };
897907
...@@ -906,51 +916,6 @@ pub fn resolveLibSystem(...@@ -906,51 +916,6 @@ pub fn resolveLibSystem(
906 });916 });
907}917}
908918
909fn accessLibPath(
910 gpa: Allocator,
911 test_path: *std.ArrayList(u8),
912 checked_paths: *std.ArrayList([]const u8),
913 search_dir: []const u8,
914 lib_name: []const u8,
915) !bool {
916 const sep = fs.path.sep_str;
917
918 tbd: {
919 test_path.clearRetainingCapacity();
920 try test_path.writer().print("{s}" ++ sep ++ "{s}.tbd", .{ search_dir, lib_name });
921 try checked_paths.append(try gpa.dupe(u8, test_path.items));
922 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
923 error.FileNotFound => break :tbd,
924 else => |e| return e,
925 };
926 return true;
927 }
928
929 dylib: {
930 test_path.clearRetainingCapacity();
931 try test_path.writer().print("{s}" ++ sep ++ "{s}.dylib", .{ search_dir, lib_name });
932 try checked_paths.append(try gpa.dupe(u8, test_path.items));
933 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
934 error.FileNotFound => break :dylib,
935 else => |e| return e,
936 };
937 return true;
938 }
939
940 noextension: {
941 test_path.clearRetainingCapacity();
942 try test_path.writer().print("{s}" ++ sep ++ "{s}", .{ search_dir, lib_name });
943 try checked_paths.append(try gpa.dupe(u8, test_path.items));
944 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
945 error.FileNotFound => break :noextension,
946 else => |e| return e,
947 };
948 return true;
949 }
950
951 return false;
952}
953
954const ParseError = error{919const ParseError = error{
955 MalformedObject,920 MalformedObject,
956 MalformedArchive,921 MalformedArchive,
...@@ -1163,37 +1128,55 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool {...@@ -1163,37 +1128,55 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool {
1163 return false;1128 return false;
1164}1129}
11651130
1166fn accessPath(path: []const u8) !bool {1131fn accessLibPath(
1167 std.fs.cwd().access(path, .{}) catch |err| switch (err) {1132 arena: Allocator,
1168 error.FileNotFound => return false,1133 test_path: *std.ArrayList(u8),
1169 else => |e| return e,1134 checked_paths: *std.ArrayList([]const u8),
1170 };1135 search_dir: []const u8,
1171 return true;1136 name: []const u8,
1172}1137) !bool {
1138 const sep = fs.path.sep_str;
11731139
1174fn resolveLib(arena: Allocator, search_dirs: []const []const u8, name: []const u8) !?[]const u8 {1140 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
1175 const path = try std.fmt.allocPrint(arena, "lib{s}", .{name});1141 test_path.clearRetainingCapacity();
1176 for (search_dirs) |dir| {1142 try test_path.writer().print("{s}" ++ sep ++ "lib{s}{s}", .{ search_dir, name, ext });
1177 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {1143 try checked_paths.append(try arena.dupe(u8, test_path.items));
1178 const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext });1144 std.fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
1179 const full_path = try std.fs.path.join(arena, &[_][]const u8{ dir, with_ext });1145 error.FileNotFound => continue,
1180 if (try accessPath(full_path)) return full_path;1146 else => |e| return e,
1181 }1147 };
1148 return true;
1182 }1149 }
1183 return null;1150
1151 return false;
1184}1152}
11851153
1186fn resolveFramework(arena: Allocator, search_dirs: []const []const u8, name: []const u8) !?[]const u8 {1154fn accessFrameworkPath(
1187 const prefix = try std.fmt.allocPrint(arena, "{s}.framework", .{name});1155 arena: Allocator,
1188 const path = try std.fs.path.join(arena, &[_][]const u8{ prefix, name });1156 test_path: *std.ArrayList(u8),
1189 for (search_dirs) |dir| {1157 checked_paths: *std.ArrayList([]const u8),
1190 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {1158 search_dir: []const u8,
1191 const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext });1159 name: []const u8,
1192 const full_path = try std.fs.path.join(arena, &[_][]const u8{ dir, with_ext });1160) !bool {
1193 if (try accessPath(full_path)) return full_path;1161 const sep = fs.path.sep_str;
1194 }1162
1163 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
1164 test_path.clearRetainingCapacity();
1165 try test_path.writer().print("{s}" ++ sep ++ "{s}.framework" ++ sep ++ "{s}{s}", .{
1166 search_dir,
1167 name,
1168 name,
1169 ext,
1170 });
1171 try checked_paths.append(try arena.dupe(u8, test_path.items));
1172 std.fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
1173 error.FileNotFound => continue,
1174 else => |e| return e,
1175 };
1176 return true;
1195 }1177 }
1196 return null;1178
1179 return false;
1197}1180}
11981181
1199fn parseDependentDylibs(self: *MachO) !void {1182fn parseDependentDylibs(self: *MachO) !void {
...@@ -1204,8 +1187,9 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1204,8 +1187,9 @@ fn parseDependentDylibs(self: *MachO) !void {
1204 const lib_dirs = self.lib_dirs;1187 const lib_dirs = self.lib_dirs;
1205 const framework_dirs = self.framework_dirs;1188 const framework_dirs = self.framework_dirs;
12061189
1207 var arena = std.heap.ArenaAllocator.init(gpa);1190 var arena_alloc = std.heap.ArenaAllocator.init(gpa);
1208 defer arena.deinit();1191 defer arena_alloc.deinit();
1192 const arena = arena_alloc.allocator();
12091193
1210 // TODO handle duplicate dylibs - it is not uncommon to have the same dylib loaded multiple times1194 // TODO handle duplicate dylibs - it is not uncommon to have the same dylib loaded multiple times
1211 // in which case we should track that and return File.Index immediately instead re-parsing paths.1195 // in which case we should track that and return File.Index immediately instead re-parsing paths.
...@@ -1215,7 +1199,7 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1215,7 +1199,7 @@ fn parseDependentDylibs(self: *MachO) !void {
1215 while (index < self.dylibs.items.len) : (index += 1) {1199 while (index < self.dylibs.items.len) : (index += 1) {
1216 const dylib_index = self.dylibs.items[index];1200 const dylib_index = self.dylibs.items[index];
12171201
1218 var dependents = std.ArrayList(File.Index).init(gpa);1202 var dependents = std.ArrayList(struct { id: Dylib.Id, file: File.Index }).init(gpa);
1219 defer dependents.deinit();1203 defer dependents.deinit();
1220 try dependents.ensureTotalCapacityPrecise(self.getFile(dylib_index).?.dylib.dependents.items.len);1204 try dependents.ensureTotalCapacityPrecise(self.getFile(dylib_index).?.dylib.dependents.items.len);
12211205
...@@ -1228,35 +1212,43 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1228,35 +1212,43 @@ fn parseDependentDylibs(self: *MachO) !void {
1228 // 3. If name is a relative path, substitute @rpath, @loader_path, @executable_path with1212 // 3. If name is a relative path, substitute @rpath, @loader_path, @executable_path with
1229 // dependees list of rpaths, and search there.1213 // dependees list of rpaths, and search there.
1230 // 4. Finally, just search the provided relative path directly in CWD.1214 // 4. Finally, just search the provided relative path directly in CWD.
1215 var test_path = std.ArrayList(u8).init(arena);
1216 var checked_paths = std.ArrayList([]const u8).init(arena);
1217
1231 const full_path = full_path: {1218 const full_path = full_path: {
1232 fail: {1219 {
1233 const stem = std.fs.path.stem(id.name);1220 const stem = std.fs.path.stem(id.name);
1234 const framework_name = try std.fmt.allocPrint(gpa, "{s}.framework" ++ std.fs.path.sep_str ++ "{s}", .{
1235 stem,
1236 stem,
1237 });
1238 defer gpa.free(framework_name);
12391221
1240 if (mem.endsWith(u8, id.name, framework_name)) {1222 // Framework
1241 // Framework1223 for (framework_dirs) |dir| {
1242 const full_path = (try resolveFramework(arena.allocator(), framework_dirs, stem)) orelse break :fail;1224 test_path.clearRetainingCapacity();
1243 break :full_path full_path;1225 if (try accessFrameworkPath(arena, &test_path, &checked_paths, dir, stem)) break :full_path test_path.items;
1244 }1226 }
12451227
1246 // Library1228 // Library
1247 const lib_name = eatPrefix(stem, "lib") orelse stem;1229 const lib_name = eatPrefix(stem, "lib") orelse stem;
1248 const full_path = (try resolveLib(arena.allocator(), lib_dirs, lib_name)) orelse break :fail;1230 for (lib_dirs) |dir| {
1249 break :full_path full_path;1231 test_path.clearRetainingCapacity();
1232 if (try accessLibPath(arena, &test_path, &checked_paths, dir, lib_name)) break :full_path test_path.items;
1233 }
1250 }1234 }
12511235
1252 if (std.fs.path.isAbsolute(id.name)) {1236 if (std.fs.path.isAbsolute(id.name)) {
1253 const path = if (self.base.comp.sysroot) |root|1237 const existing_ext = std.fs.path.extension(id.name);
1254 try std.fs.path.join(arena.allocator(), &.{ root, id.name })1238 const path = if (existing_ext.len > 0) id.name[0 .. id.name.len - existing_ext.len] else id.name;
1255 else1239 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
1256 id.name;1240 test_path.clearRetainingCapacity();
1257 for (&[_][]const u8{ "", ".tbd", ".dylib" }) |ext| {1241 if (self.base.comp.sysroot) |root| {
1258 const full_path = try std.fmt.allocPrint(arena.allocator(), "{s}{s}", .{ path, ext });1242 try test_path.writer().print("{s}" ++ std.fs.path.sep_str ++ "{s}{s}", .{ root, path, ext });
1259 if (try accessPath(full_path)) break :full_path full_path;1243 } else {
1244 try test_path.writer().print("{s}{s}", .{ path, ext });
1245 }
1246 try checked_paths.append(try arena.dupe(u8, test_path.items));
1247 std.fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
1248 error.FileNotFound => continue,
1249 else => |e| return e,
1250 };
1251 break :full_path test_path.items;
1260 }1252 }
1261 }1253 }
12621254
...@@ -1264,7 +1256,8 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1264,7 +1256,8 @@ fn parseDependentDylibs(self: *MachO) !void {
1264 const dylib = self.getFile(dylib_index).?.dylib;1256 const dylib = self.getFile(dylib_index).?.dylib;
1265 for (self.getFile(dylib.umbrella).?.dylib.rpaths.keys()) |rpath| {1257 for (self.getFile(dylib.umbrella).?.dylib.rpaths.keys()) |rpath| {
1266 const prefix = eatPrefix(rpath, "@loader_path/") orelse rpath;1258 const prefix = eatPrefix(rpath, "@loader_path/") orelse rpath;
1267 const rel_path = try std.fs.path.join(arena.allocator(), &.{ prefix, path });1259 const rel_path = try std.fs.path.join(arena, &.{ prefix, path });
1260 try checked_paths.append(rel_path);
1268 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;1261 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
1269 const full_path = std.fs.realpath(rel_path, &buffer) catch continue;1262 const full_path = std.fs.realpath(rel_path, &buffer) catch continue;
1270 break :full_path full_path;1263 break :full_path full_path;
...@@ -1277,12 +1270,21 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1277,12 +1270,21 @@ fn parseDependentDylibs(self: *MachO) !void {
1277 return error.Unhandled;1270 return error.Unhandled;
1278 }1271 }
12791272
1273 try checked_paths.append(try arena.dupe(u8, id.name));
1280 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;1274 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
1281 const full_path = std.fs.realpath(id.name, &buffer) catch {1275 if (std.fs.realpath(id.name, &buffer)) |full_path| {
1282 dependents.appendAssumeCapacity(0);1276 break :full_path full_path;
1277 } else |_| {
1278 try self.reportMissingDependencyError(
1279 self.getFile(dylib_index).?.dylib.getUmbrella(self).index,
1280 id.name,
1281 checked_paths.items,
1282 "unable to resolve dependency",
1283 .{},
1284 );
1285 has_errors = true;
1283 continue;1286 continue;
1284 };1287 }
1285 break :full_path full_path;
1286 };1288 };
1287 const lib = SystemLib{1289 const lib = SystemLib{
1288 .path = full_path,1290 .path = full_path,
...@@ -1304,11 +1306,13 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1304,11 +1306,13 @@ fn parseDependentDylibs(self: *MachO) !void {
1304 break :file_index file_index;1306 break :file_index file_index;
1305 }1307 }
1306 };1308 };
1307 dependents.appendAssumeCapacity(file_index);1309 dependents.appendAssumeCapacity(.{ .id = id, .file = file_index });
1308 }1310 }
13091311
1310 const dylib = self.getFile(dylib_index).?.dylib;1312 const dylib = self.getFile(dylib_index).?.dylib;
1311 for (dylib.dependents.items, dependents.items) |id, file_index| {1313 for (dependents.items) |entry| {
1314 const id = entry.id;
1315 const file_index = entry.file;
1312 if (self.getFile(file_index)) |file| {1316 if (self.getFile(file_index)) |file| {
1313 const dep_dylib = file.dylib;1317 const dep_dylib = file.dylib;
1314 dep_dylib.hoisted = self.isHoisted(id.name);1318 dep_dylib.hoisted = self.isHoisted(id.name);
...@@ -3857,18 +3861,33 @@ fn reportMissingLibraryError(...@@ -3857,18 +3861,33 @@ fn reportMissingLibraryError(
3857 }3861 }
3858}3862}
38593863
3864fn reportMissingDependencyError(
3865 self: *MachO,
3866 parent: File.Index,
3867 path: []const u8,
3868 checked_paths: []const []const u8,
3869 comptime format: []const u8,
3870 args: anytype,
3871) error{OutOfMemory}!void {
3872 var err = try self.addErrorWithNotes(2 + checked_paths.len);
3873 try err.addMsg(self, format, args);
3874 try err.addNote(self, "while resolving {s}", .{path});
3875 try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()});
3876 for (checked_paths) |p| {
3877 try err.addNote(self, "tried {s}", .{p});
3878 }
3879}
3880
3860fn reportDependencyError(3881fn reportDependencyError(
3861 self: *MachO,3882 self: *MachO,
3862 parent: File.Index,3883 parent: File.Index,
3863 path: ?[]const u8,3884 path: []const u8,
3864 comptime format: []const u8,3885 comptime format: []const u8,
3865 args: anytype,3886 args: anytype,
3866) error{OutOfMemory}!void {3887) error{OutOfMemory}!void {
3867 var err = try self.addErrorWithNotes(2);3888 var err = try self.addErrorWithNotes(2);
3868 try err.addMsg(self, format, args);3889 try err.addMsg(self, format, args);
3869 if (path) |p| {3890 try err.addNote(self, "while parsing {s}", .{path});
3870 try err.addNote(self, "while parsing {s}", .{p});
3871 }
3872 try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()});3891 try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()});
3873}3892}
38743893