authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-24 14:30:53+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-25 12:07:04+01:00
logd8d6ea6af25af7ecfea1351c652c51de49169536
treee285f4a32e693330f0bb9c431e3ceba3cd7e5c91
parentdcaf43674e35372e1d28ab12c4c4ff9af9f3d646

macho: print tried paths for unresolved dylib deps


1 files changed, 88 insertions(+), 26 deletions(-)

src/link/MachO.zig+88-26
......@@ -1163,37 +1163,59 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool {
11631163 return false;
11641164}
11651165
1166fn accessPath(path: []const u8) !bool {
1166fn accessPath(
1167 arena: Allocator,
1168 test_path: *std.ArrayList(u8),
1169 checked_paths: *std.ArrayList([]const u8),
1170 path: []const u8,
1171) !bool {
1172 test_path.clearRetainingCapacity();
1173 try test_path.appendSlice(path);
11671174 std.fs.cwd().access(path, .{}) catch |err| switch (err) {
1168 error.FileNotFound => return false,
1175 error.FileNotFound => {
1176 try checked_paths.append(try arena.dupe(u8, test_path.items));
1177 return false;
1178 },
11691179 else => |e| return e,
11701180 };
11711181 return true;
11721182}
11731183
1174fn resolveLib(arena: Allocator, search_dirs: []const []const u8, name: []const u8) !?[]const u8 {
1184fn resolveLib(
1185 arena: Allocator,
1186 test_path: *std.ArrayList(u8),
1187 checked_paths: *std.ArrayList([]const u8),
1188 search_dirs: []const []const u8,
1189 name: []const u8,
1190) !bool {
11751191 const path = try std.fmt.allocPrint(arena, "lib{s}", .{name});
11761192 for (search_dirs) |dir| {
11771193 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
11781194 const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext });
11791195 const full_path = try std.fs.path.join(arena, &[_][]const u8{ dir, with_ext });
1180 if (try accessPath(full_path)) return full_path;
1196 if (try accessPath(arena, test_path, checked_paths, full_path)) return true;
11811197 }
11821198 }
1183 return null;
1199 return false;
11841200}
11851201
1186fn resolveFramework(arena: Allocator, search_dirs: []const []const u8, name: []const u8) !?[]const u8 {
1202fn resolveFramework(
1203 arena: Allocator,
1204 test_path: *std.ArrayList(u8),
1205 checked_paths: *std.ArrayList([]const u8),
1206 search_dirs: []const []const u8,
1207 name: []const u8,
1208) !bool {
11871209 const prefix = try std.fmt.allocPrint(arena, "{s}.framework", .{name});
11881210 const path = try std.fs.path.join(arena, &[_][]const u8{ prefix, name });
11891211 for (search_dirs) |dir| {
11901212 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
11911213 const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext });
11921214 const full_path = try std.fs.path.join(arena, &[_][]const u8{ dir, with_ext });
1193 if (try accessPath(full_path)) return full_path;
1215 if (try accessPath(arena, test_path, checked_paths, full_path)) return true;
11941216 }
11951217 }
1196 return null;
1218 return false;
11971219}
11981220
11991221fn parseDependentDylibs(self: *MachO) !void {
......@@ -1204,8 +1226,9 @@ fn parseDependentDylibs(self: *MachO) !void {
12041226 const lib_dirs = self.lib_dirs;
12051227 const framework_dirs = self.framework_dirs;
12061228
1207 var arena = std.heap.ArenaAllocator.init(gpa);
1208 defer arena.deinit();
1229 var arena_alloc = std.heap.ArenaAllocator.init(gpa);
1230 defer arena_alloc.deinit();
1231 const arena = arena_alloc.allocator();
12091232
12101233 // TODO handle duplicate dylibs - it is not uncommon to have the same dylib loaded multiple times
12111234 // in which case we should track that and return File.Index immediately instead re-parsing paths.
......@@ -1215,7 +1238,7 @@ fn parseDependentDylibs(self: *MachO) !void {
12151238 while (index < self.dylibs.items.len) : (index += 1) {
12161239 const dylib_index = self.dylibs.items[index];
12171240
1218 var dependents = std.ArrayList(File.Index).init(gpa);
1241 var dependents = std.ArrayList(struct { id: Dylib.Id, file: File.Index }).init(gpa);
12191242 defer dependents.deinit();
12201243 try dependents.ensureTotalCapacityPrecise(self.getFile(dylib_index).?.dylib.dependents.items.len);
12211244
......@@ -1228,6 +1251,9 @@ fn parseDependentDylibs(self: *MachO) !void {
12281251 // 3. If name is a relative path, substitute @rpath, @loader_path, @executable_path with
12291252 // dependees list of rpaths, and search there.
12301253 // 4. Finally, just search the provided relative path directly in CWD.
1254 var test_path = std.ArrayList(u8).init(arena);
1255 var checked_paths = std.ArrayList([]const u8).init(arena);
1256
12311257 const full_path = full_path: {
12321258 fail: {
12331259 const stem = std.fs.path.stem(id.name);
......@@ -1239,24 +1265,36 @@ fn parseDependentDylibs(self: *MachO) !void {
12391265
12401266 if (mem.endsWith(u8, id.name, framework_name)) {
12411267 // Framework
1242 const full_path = (try resolveFramework(arena.allocator(), framework_dirs, stem)) orelse break :fail;
1243 break :full_path full_path;
1268 if (try resolveFramework(
1269 arena,
1270 &test_path,
1271 &checked_paths,
1272 framework_dirs,
1273 stem,
1274 )) break :full_path test_path.items;
1275 break :fail;
12441276 }
12451277
12461278 // Library
12471279 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;
1280 if (try resolveLib(
1281 arena,
1282 &test_path,
1283 &checked_paths,
1284 lib_dirs,
1285 lib_name,
1286 )) break :full_path test_path.items;
1287 break :fail;
12501288 }
12511289
12521290 if (std.fs.path.isAbsolute(id.name)) {
12531291 const path = if (self.base.comp.sysroot) |root|
1254 try std.fs.path.join(arena.allocator(), &.{ root, id.name })
1292 try std.fs.path.join(arena, &.{ root, id.name })
12551293 else
12561294 id.name;
12571295 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;
1296 const full_path = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext });
1297 if (try accessPath(arena, &test_path, &checked_paths, full_path)) break :full_path test_path.items;
12601298 }
12611299 }
12621300
......@@ -1264,7 +1302,7 @@ fn parseDependentDylibs(self: *MachO) !void {
12641302 const dylib = self.getFile(dylib_index).?.dylib;
12651303 for (self.getFile(dylib.umbrella).?.dylib.rpaths.keys()) |rpath| {
12661304 const prefix = eatPrefix(rpath, "@loader_path/") orelse rpath;
1267 const rel_path = try std.fs.path.join(arena.allocator(), &.{ prefix, path });
1305 const rel_path = try std.fs.path.join(arena, &.{ prefix, path });
12681306 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
12691307 const full_path = std.fs.realpath(rel_path, &buffer) catch continue;
12701308 break :full_path full_path;
......@@ -1279,7 +1317,14 @@ fn parseDependentDylibs(self: *MachO) !void {
12791317
12801318 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
12811319 const full_path = std.fs.realpath(id.name, &buffer) catch {
1282 dependents.appendAssumeCapacity(0);
1320 try self.reportMissingDependencyError(
1321 self.getFile(dylib_index).?.dylib.getUmbrella(self).index,
1322 id.name,
1323 checked_paths.items,
1324 "unable to resolve dependency",
1325 .{},
1326 );
1327 has_errors = true;
12831328 continue;
12841329 };
12851330 break :full_path full_path;
......@@ -1304,11 +1349,13 @@ fn parseDependentDylibs(self: *MachO) !void {
13041349 break :file_index file_index;
13051350 }
13061351 };
1307 dependents.appendAssumeCapacity(file_index);
1352 dependents.appendAssumeCapacity(.{ .id = id, .file = file_index });
13081353 }
13091354
13101355 const dylib = self.getFile(dylib_index).?.dylib;
1311 for (dylib.dependents.items, dependents.items) |id, file_index| {
1356 for (dependents.items) |entry| {
1357 const id = entry.id;
1358 const file_index = entry.file;
13121359 if (self.getFile(file_index)) |file| {
13131360 const dep_dylib = file.dylib;
13141361 dep_dylib.hoisted = self.isHoisted(id.name);
......@@ -3857,18 +3904,33 @@ fn reportMissingLibraryError(
38573904 }
38583905}
38593906
3907fn reportMissingDependencyError(
3908 self: *MachO,
3909 parent: File.Index,
3910 path: []const u8,
3911 checked_paths: []const []const u8,
3912 comptime format: []const u8,
3913 args: anytype,
3914) error{OutOfMemory}!void {
3915 var err = try self.addErrorWithNotes(2 + checked_paths.len);
3916 try err.addMsg(self, format, args);
3917 try err.addNote(self, "while resolving {s}", .{path});
3918 try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()});
3919 for (checked_paths) |p| {
3920 try err.addNote(self, "tried {s}", .{p});
3921 }
3922}
3923
38603924fn reportDependencyError(
38613925 self: *MachO,
38623926 parent: File.Index,
3863 path: ?[]const u8,
3927 path: []const u8,
38643928 comptime format: []const u8,
38653929 args: anytype,
38663930) error{OutOfMemory}!void {
38673931 var err = try self.addErrorWithNotes(2);
38683932 try err.addMsg(self, format, args);
3869 if (path) |p| {
3870 try err.addNote(self, "while parsing {s}", .{p});
3871 }
3933 try err.addNote(self, "while parsing {s}", .{path});
38723934 try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()});
38733935}
38743936