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 {
844844 try argv.append(arg);
845845 }
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
847852 for (self.frameworks) |framework| {
848853 const name = std.fs.path.stem(framework.path);
849854 const arg = if (framework.needed)
......@@ -855,6 +860,11 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
855860 try argv.append(arg);
856861 }
857862
863 for (self.framework_dirs) |f_dir| {
864 try argv.append("-F");
865 try argv.append(f_dir);
866 }
867
858868 if (self.base.isDynLib() and self.base.allow_shlib_undefined) {
859869 try argv.append("-undefined");
860870 try argv.append("dynamic_lookup");
......@@ -887,11 +897,11 @@ pub fn resolveLibSystem(
887897 if (self.sdk_layout) |sdk_layout| switch (sdk_layout) {
888898 .sdk => {
889899 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;
891901 },
892902 .vendored => {
893903 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;
895905 },
896906 };
897907
......@@ -906,51 +916,6 @@ pub fn resolveLibSystem(
906916 });
907917}
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
954919const ParseError = error{
955920 MalformedObject,
956921 MalformedArchive,
......@@ -1163,37 +1128,55 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool {
11631128 return false;
11641129}
11651130
1166fn accessPath(path: []const u8) !bool {
1167 std.fs.cwd().access(path, .{}) catch |err| switch (err) {
1168 error.FileNotFound => return false,
1169 else => |e| return e,
1170 };
1171 return true;
1172}
1131fn accessLibPath(
1132 arena: Allocator,
1133 test_path: *std.ArrayList(u8),
1134 checked_paths: *std.ArrayList([]const u8),
1135 search_dir: []const u8,
1136 name: []const u8,
1137) !bool {
1138 const sep = fs.path.sep_str;
11731139
1174fn resolveLib(arena: Allocator, search_dirs: []const []const u8, name: []const u8) !?[]const u8 {
1175 const path = try std.fmt.allocPrint(arena, "lib{s}", .{name});
1176 for (search_dirs) |dir| {
1177 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
1178 const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext });
1179 const full_path = try std.fs.path.join(arena, &[_][]const u8{ dir, with_ext });
1180 if (try accessPath(full_path)) return full_path;
1181 }
1140 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
1141 test_path.clearRetainingCapacity();
1142 try test_path.writer().print("{s}" ++ sep ++ "lib{s}{s}", .{ search_dir, name, ext });
1143 try checked_paths.append(try arena.dupe(u8, test_path.items));
1144 std.fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
1145 error.FileNotFound => continue,
1146 else => |e| return e,
1147 };
1148 return true;
11821149 }
1183 return null;
1150
1151 return false;
11841152}
11851153
1186fn resolveFramework(arena: Allocator, search_dirs: []const []const u8, name: []const u8) !?[]const u8 {
1187 const prefix = try std.fmt.allocPrint(arena, "{s}.framework", .{name});
1188 const path = try std.fs.path.join(arena, &[_][]const u8{ prefix, name });
1189 for (search_dirs) |dir| {
1190 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
1191 const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext });
1192 const full_path = try std.fs.path.join(arena, &[_][]const u8{ dir, with_ext });
1193 if (try accessPath(full_path)) return full_path;
1194 }
1154fn accessFrameworkPath(
1155 arena: Allocator,
1156 test_path: *std.ArrayList(u8),
1157 checked_paths: *std.ArrayList([]const u8),
1158 search_dir: []const u8,
1159 name: []const u8,
1160) !bool {
1161 const sep = fs.path.sep_str;
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;
11951177 }
1196 return null;
1178
1179 return false;
11971180}
11981181
11991182fn parseDependentDylibs(self: *MachO) !void {
......@@ -1204,8 +1187,9 @@ fn parseDependentDylibs(self: *MachO) !void {
12041187 const lib_dirs = self.lib_dirs;
12051188 const framework_dirs = self.framework_dirs;
12061189
1207 var arena = std.heap.ArenaAllocator.init(gpa);
1208 defer arena.deinit();
1190 var arena_alloc = std.heap.ArenaAllocator.init(gpa);
1191 defer arena_alloc.deinit();
1192 const arena = arena_alloc.allocator();
12091193
12101194 // TODO handle duplicate dylibs - it is not uncommon to have the same dylib loaded multiple times
12111195 // 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 {
12151199 while (index < self.dylibs.items.len) : (index += 1) {
12161200 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);
12191203 defer dependents.deinit();
12201204 try dependents.ensureTotalCapacityPrecise(self.getFile(dylib_index).?.dylib.dependents.items.len);
12211205
......@@ -1228,35 +1212,43 @@ fn parseDependentDylibs(self: *MachO) !void {
12281212 // 3. If name is a relative path, substitute @rpath, @loader_path, @executable_path with
12291213 // dependees list of rpaths, and search there.
12301214 // 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
12311218 const full_path = full_path: {
1232 fail: {
1219 {
12331220 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)) {
1241 // Framework
1242 const full_path = (try resolveFramework(arena.allocator(), framework_dirs, stem)) orelse break :fail;
1243 break :full_path full_path;
1222 // Framework
1223 for (framework_dirs) |dir| {
1224 test_path.clearRetainingCapacity();
1225 if (try accessFrameworkPath(arena, &test_path, &checked_paths, dir, stem)) break :full_path test_path.items;
12441226 }
12451227
12461228 // Library
12471229 const lib_name = eatPrefix(stem, "lib") orelse stem;
1248 const full_path = (try resolveLib(arena.allocator(), lib_dirs, lib_name)) orelse break :fail;
1249 break :full_path full_path;
1230 for (lib_dirs) |dir| {
1231 test_path.clearRetainingCapacity();
1232 if (try accessLibPath(arena, &test_path, &checked_paths, dir, lib_name)) break :full_path test_path.items;
1233 }
12501234 }
12511235
12521236 if (std.fs.path.isAbsolute(id.name)) {
1253 const path = if (self.base.comp.sysroot) |root|
1254 try std.fs.path.join(arena.allocator(), &.{ root, id.name })
1255 else
1256 id.name;
1257 for (&[_][]const u8{ "", ".tbd", ".dylib" }) |ext| {
1258 const full_path = try std.fmt.allocPrint(arena.allocator(), "{s}{s}", .{ path, ext });
1259 if (try accessPath(full_path)) break :full_path full_path;
1237 const existing_ext = std.fs.path.extension(id.name);
1238 const path = if (existing_ext.len > 0) id.name[0 .. id.name.len - existing_ext.len] else id.name;
1239 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
1240 test_path.clearRetainingCapacity();
1241 if (self.base.comp.sysroot) |root| {
1242 try test_path.writer().print("{s}" ++ std.fs.path.sep_str ++ "{s}{s}", .{ root, path, ext });
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;
12601252 }
12611253 }
12621254
......@@ -1264,7 +1256,8 @@ fn parseDependentDylibs(self: *MachO) !void {
12641256 const dylib = self.getFile(dylib_index).?.dylib;
12651257 for (self.getFile(dylib.umbrella).?.dylib.rpaths.keys()) |rpath| {
12661258 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);
12681261 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
12691262 const full_path = std.fs.realpath(rel_path, &buffer) catch continue;
12701263 break :full_path full_path;
......@@ -1277,12 +1270,21 @@ fn parseDependentDylibs(self: *MachO) !void {
12771270 return error.Unhandled;
12781271 }
12791272
1273 try checked_paths.append(try arena.dupe(u8, id.name));
12801274 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
1281 const full_path = std.fs.realpath(id.name, &buffer) catch {
1282 dependents.appendAssumeCapacity(0);
1275 if (std.fs.realpath(id.name, &buffer)) |full_path| {
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;
12831286 continue;
1284 };
1285 break :full_path full_path;
1287 }
12861288 };
12871289 const lib = SystemLib{
12881290 .path = full_path,
......@@ -1304,11 +1306,13 @@ fn parseDependentDylibs(self: *MachO) !void {
13041306 break :file_index file_index;
13051307 }
13061308 };
1307 dependents.appendAssumeCapacity(file_index);
1309 dependents.appendAssumeCapacity(.{ .id = id, .file = file_index });
13081310 }
13091311
13101312 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;
13121316 if (self.getFile(file_index)) |file| {
13131317 const dep_dylib = file.dylib;
13141318 dep_dylib.hoisted = self.isHoisted(id.name);
......@@ -3857,18 +3861,33 @@ fn reportMissingLibraryError(
38573861 }
38583862}
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
38603881fn reportDependencyError(
38613882 self: *MachO,
38623883 parent: File.Index,
3863 path: ?[]const u8,
3884 path: []const u8,
38643885 comptime format: []const u8,
38653886 args: anytype,
38663887) error{OutOfMemory}!void {
38673888 var err = try self.addErrorWithNotes(2);
38683889 try err.addMsg(self, format, args);
3869 if (path) |p| {
3870 try err.addNote(self, "while parsing {s}", .{p});
3871 }
3890 try err.addNote(self, "while parsing {s}", .{path});
38723891 try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()});
38733892}
38743893