Skip to content

Commit d5fc3c6

Browse files
authored
Merge pull request #18677 from ziglang/post-new-macho
macho: fix logic for parsing dependent dylibs aka re-exports
2 parents b96fb85 + d62e7bb commit d5fc3c6

File tree

1 file changed

+125
-106
lines changed

1 file changed

+125
-106
lines changed

src/link/MachO.zig

Lines changed: 125 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -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

909-
fn 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-
954919
const ParseError = error{
955920
MalformedObject,
956921
MalformedArchive,
@@ -1163,37 +1128,55 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool {
11631128
return false;
11641129
}
11651130

1166-
fn 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-
}
1131+
fn 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

1174-
fn 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

1186-
fn 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-
}
1154+
fn 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

11991182
fn 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,43 +1212,52 @@ 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

12631255
if (eatPrefix(id.name, "@rpath/")) |path| {
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

3864+
fn 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+
38603881
fn 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

0 commit comments

Comments
 (0)