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 {...@@ -1163,37 +1163,59 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool {
1163 return false;1163 return false;
1164}1164}
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);
1167 std.fs.cwd().access(path, .{}) catch |err| switch (err) {1174 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 },
1169 else => |e| return e,1179 else => |e| return e,
1170 };1180 };
1171 return true;1181 return true;
1172}1182}
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 {
1175 const path = try std.fmt.allocPrint(arena, "lib{s}", .{name});1191 const path = try std.fmt.allocPrint(arena, "lib{s}", .{name});
1176 for (search_dirs) |dir| {1192 for (search_dirs) |dir| {
1177 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {1193 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
1178 const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext });1194 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 });1195 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;
1181 }1197 }
1182 }1198 }
1183 return null;1199 return false;
1184}1200}
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 {
1187 const prefix = try std.fmt.allocPrint(arena, "{s}.framework", .{name});1209 const prefix = try std.fmt.allocPrint(arena, "{s}.framework", .{name});
1188 const path = try std.fs.path.join(arena, &[_][]const u8{ prefix, name });1210 const path = try std.fs.path.join(arena, &[_][]const u8{ prefix, name });
1189 for (search_dirs) |dir| {1211 for (search_dirs) |dir| {
1190 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {1212 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
1191 const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext });1213 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 });1214 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;
1194 }1216 }
1195 }1217 }
1196 return null;1218 return false;
1197}1219}
11981220
1199fn parseDependentDylibs(self: *MachO) !void {1221fn parseDependentDylibs(self: *MachO) !void {
...@@ -1204,8 +1226,9 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1204,8 +1226,9 @@ fn parseDependentDylibs(self: *MachO) !void {
1204 const lib_dirs = self.lib_dirs;1226 const lib_dirs = self.lib_dirs;
1205 const framework_dirs = self.framework_dirs;1227 const framework_dirs = self.framework_dirs;
12061228
1207 var arena = std.heap.ArenaAllocator.init(gpa);1229 var arena_alloc = std.heap.ArenaAllocator.init(gpa);
1208 defer arena.deinit();1230 defer arena_alloc.deinit();
1231 const arena = arena_alloc.allocator();
12091232
1210 // TODO handle duplicate dylibs - it is not uncommon to have the same dylib loaded multiple times1233 // 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.1234 // 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 {...@@ -1215,7 +1238,7 @@ fn parseDependentDylibs(self: *MachO) !void {
1215 while (index < self.dylibs.items.len) : (index += 1) {1238 while (index < self.dylibs.items.len) : (index += 1) {
1216 const dylib_index = self.dylibs.items[index];1239 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);
1219 defer dependents.deinit();1242 defer dependents.deinit();
1220 try dependents.ensureTotalCapacityPrecise(self.getFile(dylib_index).?.dylib.dependents.items.len);1243 try dependents.ensureTotalCapacityPrecise(self.getFile(dylib_index).?.dylib.dependents.items.len);
12211244
...@@ -1228,6 +1251,9 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1228,6 +1251,9 @@ fn parseDependentDylibs(self: *MachO) !void {
1228 // 3. If name is a relative path, substitute @rpath, @loader_path, @executable_path with1251 // 3. If name is a relative path, substitute @rpath, @loader_path, @executable_path with
1229 // dependees list of rpaths, and search there.1252 // dependees list of rpaths, and search there.
1230 // 4. Finally, just search the provided relative path directly in CWD.1253 // 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
1231 const full_path = full_path: {1257 const full_path = full_path: {
1232 fail: {1258 fail: {
1233 const stem = std.fs.path.stem(id.name);1259 const stem = std.fs.path.stem(id.name);
...@@ -1239,24 +1265,36 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1239,24 +1265,36 @@ fn parseDependentDylibs(self: *MachO) !void {
12391265
1240 if (mem.endsWith(u8, id.name, framework_name)) {1266 if (mem.endsWith(u8, id.name, framework_name)) {
1241 // Framework1267 // Framework
1242 const full_path = (try resolveFramework(arena.allocator(), framework_dirs, stem)) orelse break :fail;1268 if (try resolveFramework(
1243 break :full_path full_path;1269 arena,
1270 &test_path,
1271 &checked_paths,
1272 framework_dirs,
1273 stem,
1274 )) break :full_path test_path.items;
1275 break :fail;
1244 }1276 }
12451277
1246 // Library1278 // Library
1247 const lib_name = eatPrefix(stem, "lib") orelse stem;1279 const lib_name = eatPrefix(stem, "lib") orelse stem;
1248 const full_path = (try resolveLib(arena.allocator(), lib_dirs, lib_name)) orelse break :fail;1280 if (try resolveLib(
1249 break :full_path full_path;1281 arena,
1282 &test_path,
1283 &checked_paths,
1284 lib_dirs,
1285 lib_name,
1286 )) break :full_path test_path.items;
1287 break :fail;
1250 }1288 }
12511289
1252 if (std.fs.path.isAbsolute(id.name)) {1290 if (std.fs.path.isAbsolute(id.name)) {
1253 const path = if (self.base.comp.sysroot) |root|1291 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 })
1255 else1293 else
1256 id.name;1294 id.name;
1257 for (&[_][]const u8{ "", ".tbd", ".dylib" }) |ext| {1295 for (&[_][]const u8{ "", ".tbd", ".dylib" }) |ext| {
1258 const full_path = try std.fmt.allocPrint(arena.allocator(), "{s}{s}", .{ path, ext });1296 const full_path = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext });
1259 if (try accessPath(full_path)) break :full_path full_path;1297 if (try accessPath(arena, &test_path, &checked_paths, full_path)) break :full_path test_path.items;
1260 }1298 }
1261 }1299 }
12621300
...@@ -1264,7 +1302,7 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1264,7 +1302,7 @@ fn parseDependentDylibs(self: *MachO) !void {
1264 const dylib = self.getFile(dylib_index).?.dylib;1302 const dylib = self.getFile(dylib_index).?.dylib;
1265 for (self.getFile(dylib.umbrella).?.dylib.rpaths.keys()) |rpath| {1303 for (self.getFile(dylib.umbrella).?.dylib.rpaths.keys()) |rpath| {
1266 const prefix = eatPrefix(rpath, "@loader_path/") orelse rpath;1304 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 });
1268 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;1306 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
1269 const full_path = std.fs.realpath(rel_path, &buffer) catch continue;1307 const full_path = std.fs.realpath(rel_path, &buffer) catch continue;
1270 break :full_path full_path;1308 break :full_path full_path;
...@@ -1279,7 +1317,14 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1279,7 +1317,14 @@ fn parseDependentDylibs(self: *MachO) !void {
12791317
1280 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;1318 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
1281 const full_path = std.fs.realpath(id.name, &buffer) catch {1319 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;
1283 continue;1328 continue;
1284 };1329 };
1285 break :full_path full_path;1330 break :full_path full_path;
...@@ -1304,11 +1349,13 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1304,11 +1349,13 @@ fn parseDependentDylibs(self: *MachO) !void {
1304 break :file_index file_index;1349 break :file_index file_index;
1305 }1350 }
1306 };1351 };
1307 dependents.appendAssumeCapacity(file_index);1352 dependents.appendAssumeCapacity(.{ .id = id, .file = file_index });
1308 }1353 }
13091354
1310 const dylib = self.getFile(dylib_index).?.dylib;1355 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;
1312 if (self.getFile(file_index)) |file| {1359 if (self.getFile(file_index)) |file| {
1313 const dep_dylib = file.dylib;1360 const dep_dylib = file.dylib;
1314 dep_dylib.hoisted = self.isHoisted(id.name);1361 dep_dylib.hoisted = self.isHoisted(id.name);
...@@ -3857,18 +3904,33 @@ fn reportMissingLibraryError(...@@ -3857,18 +3904,33 @@ fn reportMissingLibraryError(
3857 }3904 }
3858}3905}
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
3860fn reportDependencyError(3924fn reportDependencyError(
3861 self: *MachO,3925 self: *MachO,
3862 parent: File.Index,3926 parent: File.Index,
3863 path: ?[]const u8,3927 path: []const u8,
3864 comptime format: []const u8,3928 comptime format: []const u8,
3865 args: anytype,3929 args: anytype,
3866) error{OutOfMemory}!void {3930) error{OutOfMemory}!void {
3867 var err = try self.addErrorWithNotes(2);3931 var err = try self.addErrorWithNotes(2);
3868 try err.addMsg(self, format, args);3932 try err.addMsg(self, format, args);
3869 if (path) |p| {3933 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()});3934 try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()});
3873}3935}
38743936