aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2024-01-27 01:49:54 +0100
committerGitHub <noreply@github.com>2024-01-27 01:49:54 +0100
commitd5fc3c635ad94b6934fb9b90549d7f36eb9fa56b (patch)
tree81760465cf971d1a268baba8bb7b13df48b61e27 /src
parentb96fb858c88b97617e6fa0e3c6966a8e900caf7f (diff)
parentd62e7bbefde641065eb726c3b561defd594ddae8 (diff)
downloadzig-d5fc3c635ad94b6934fb9b90549d7f36eb9fa56b.tar.gz
zig-d5fc3c635ad94b6934fb9b90549d7f36eb9fa56b.zip
Merge pull request #18677 from ziglang/post-new-macho
macho: fix logic for parsing dependent dylibs aka re-exports
Diffstat (limited to 'src')
-rw-r--r--src/link/MachO.zig231
1 files changed, 125 insertions, 106 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
index c9f655fd19..9ace2e3b82 100644
--- a/src/link/MachO.zig
+++ b/src/link/MachO.zig
@@ -844,6 +844,11 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
try argv.append(arg);
}
+ for (self.lib_dirs) |lib_dir| {
+ const arg = try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir});
+ try argv.append(arg);
+ }
+
for (self.frameworks) |framework| {
const name = std.fs.path.stem(framework.path);
const arg = if (framework.needed)
@@ -855,6 +860,11 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
try argv.append(arg);
}
+ for (self.framework_dirs) |f_dir| {
+ try argv.append("-F");
+ try argv.append(f_dir);
+ }
+
if (self.base.isDynLib() and self.base.allow_shlib_undefined) {
try argv.append("-undefined");
try argv.append("dynamic_lookup");
@@ -887,11 +897,11 @@ pub fn resolveLibSystem(
if (self.sdk_layout) |sdk_layout| switch (sdk_layout) {
.sdk => {
const dir = try fs.path.join(arena, &[_][]const u8{ comp.sysroot.?, "usr", "lib" });
- if (try accessLibPath(arena, &test_path, &checked_paths, dir, "libSystem")) break :success;
+ if (try accessLibPath(arena, &test_path, &checked_paths, dir, "System")) break :success;
},
.vendored => {
const dir = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "darwin" });
- if (try accessLibPath(arena, &test_path, &checked_paths, dir, "libSystem")) break :success;
+ if (try accessLibPath(arena, &test_path, &checked_paths, dir, "System")) break :success;
},
};
@@ -906,51 +916,6 @@ pub fn resolveLibSystem(
});
}
-fn accessLibPath(
- gpa: Allocator,
- test_path: *std.ArrayList(u8),
- checked_paths: *std.ArrayList([]const u8),
- search_dir: []const u8,
- lib_name: []const u8,
-) !bool {
- const sep = fs.path.sep_str;
-
- tbd: {
- test_path.clearRetainingCapacity();
- try test_path.writer().print("{s}" ++ sep ++ "{s}.tbd", .{ search_dir, lib_name });
- try checked_paths.append(try gpa.dupe(u8, test_path.items));
- fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
- error.FileNotFound => break :tbd,
- else => |e| return e,
- };
- return true;
- }
-
- dylib: {
- test_path.clearRetainingCapacity();
- try test_path.writer().print("{s}" ++ sep ++ "{s}.dylib", .{ search_dir, lib_name });
- try checked_paths.append(try gpa.dupe(u8, test_path.items));
- fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
- error.FileNotFound => break :dylib,
- else => |e| return e,
- };
- return true;
- }
-
- noextension: {
- test_path.clearRetainingCapacity();
- try test_path.writer().print("{s}" ++ sep ++ "{s}", .{ search_dir, lib_name });
- try checked_paths.append(try gpa.dupe(u8, test_path.items));
- fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
- error.FileNotFound => break :noextension,
- else => |e| return e,
- };
- return true;
- }
-
- return false;
-}
-
const ParseError = error{
MalformedObject,
MalformedArchive,
@@ -1163,37 +1128,55 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool {
return false;
}
-fn accessPath(path: []const u8) !bool {
- std.fs.cwd().access(path, .{}) catch |err| switch (err) {
- error.FileNotFound => return false,
- else => |e| return e,
- };
- return true;
-}
+fn accessLibPath(
+ arena: Allocator,
+ test_path: *std.ArrayList(u8),
+ checked_paths: *std.ArrayList([]const u8),
+ search_dir: []const u8,
+ name: []const u8,
+) !bool {
+ const sep = fs.path.sep_str;
-fn resolveLib(arena: Allocator, search_dirs: []const []const u8, name: []const u8) !?[]const u8 {
- const path = try std.fmt.allocPrint(arena, "lib{s}", .{name});
- for (search_dirs) |dir| {
- for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
- const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext });
- const full_path = try std.fs.path.join(arena, &[_][]const u8{ dir, with_ext });
- if (try accessPath(full_path)) return full_path;
- }
+ for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
+ test_path.clearRetainingCapacity();
+ try test_path.writer().print("{s}" ++ sep ++ "lib{s}{s}", .{ search_dir, name, ext });
+ try checked_paths.append(try arena.dupe(u8, test_path.items));
+ std.fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
+ error.FileNotFound => continue,
+ else => |e| return e,
+ };
+ return true;
}
- return null;
+
+ return false;
}
-fn resolveFramework(arena: Allocator, search_dirs: []const []const u8, name: []const u8) !?[]const u8 {
- const prefix = try std.fmt.allocPrint(arena, "{s}.framework", .{name});
- const path = try std.fs.path.join(arena, &[_][]const u8{ prefix, name });
- for (search_dirs) |dir| {
- for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
- const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ path, ext });
- const full_path = try std.fs.path.join(arena, &[_][]const u8{ dir, with_ext });
- if (try accessPath(full_path)) return full_path;
- }
+fn accessFrameworkPath(
+ arena: Allocator,
+ test_path: *std.ArrayList(u8),
+ checked_paths: *std.ArrayList([]const u8),
+ search_dir: []const u8,
+ name: []const u8,
+) !bool {
+ const sep = fs.path.sep_str;
+
+ for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
+ test_path.clearRetainingCapacity();
+ try test_path.writer().print("{s}" ++ sep ++ "{s}.framework" ++ sep ++ "{s}{s}", .{
+ search_dir,
+ name,
+ name,
+ ext,
+ });
+ try checked_paths.append(try arena.dupe(u8, test_path.items));
+ std.fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
+ error.FileNotFound => continue,
+ else => |e| return e,
+ };
+ return true;
}
- return null;
+
+ return false;
}
fn parseDependentDylibs(self: *MachO) !void {
@@ -1204,8 +1187,9 @@ fn parseDependentDylibs(self: *MachO) !void {
const lib_dirs = self.lib_dirs;
const framework_dirs = self.framework_dirs;
- var arena = std.heap.ArenaAllocator.init(gpa);
- defer arena.deinit();
+ var arena_alloc = std.heap.ArenaAllocator.init(gpa);
+ defer arena_alloc.deinit();
+ const arena = arena_alloc.allocator();
// TODO handle duplicate dylibs - it is not uncommon to have the same dylib loaded multiple times
// 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 {
while (index < self.dylibs.items.len) : (index += 1) {
const dylib_index = self.dylibs.items[index];
- var dependents = std.ArrayList(File.Index).init(gpa);
+ var dependents = std.ArrayList(struct { id: Dylib.Id, file: File.Index }).init(gpa);
defer dependents.deinit();
try dependents.ensureTotalCapacityPrecise(self.getFile(dylib_index).?.dylib.dependents.items.len);
@@ -1228,35 +1212,43 @@ fn parseDependentDylibs(self: *MachO) !void {
// 3. If name is a relative path, substitute @rpath, @loader_path, @executable_path with
// dependees list of rpaths, and search there.
// 4. Finally, just search the provided relative path directly in CWD.
+ var test_path = std.ArrayList(u8).init(arena);
+ var checked_paths = std.ArrayList([]const u8).init(arena);
+
const full_path = full_path: {
- fail: {
+ {
const stem = std.fs.path.stem(id.name);
- const framework_name = try std.fmt.allocPrint(gpa, "{s}.framework" ++ std.fs.path.sep_str ++ "{s}", .{
- stem,
- stem,
- });
- defer gpa.free(framework_name);
- if (mem.endsWith(u8, id.name, framework_name)) {
- // Framework
- const full_path = (try resolveFramework(arena.allocator(), framework_dirs, stem)) orelse break :fail;
- break :full_path full_path;
+ // Framework
+ for (framework_dirs) |dir| {
+ test_path.clearRetainingCapacity();
+ if (try accessFrameworkPath(arena, &test_path, &checked_paths, dir, stem)) break :full_path test_path.items;
}
// Library
const lib_name = eatPrefix(stem, "lib") orelse stem;
- const full_path = (try resolveLib(arena.allocator(), lib_dirs, lib_name)) orelse break :fail;
- break :full_path full_path;
+ for (lib_dirs) |dir| {
+ test_path.clearRetainingCapacity();
+ if (try accessLibPath(arena, &test_path, &checked_paths, dir, lib_name)) break :full_path test_path.items;
+ }
}
if (std.fs.path.isAbsolute(id.name)) {
- const path = if (self.base.comp.sysroot) |root|
- try std.fs.path.join(arena.allocator(), &.{ root, id.name })
- else
- id.name;
- for (&[_][]const u8{ "", ".tbd", ".dylib" }) |ext| {
- const full_path = try std.fmt.allocPrint(arena.allocator(), "{s}{s}", .{ path, ext });
- if (try accessPath(full_path)) break :full_path full_path;
+ const existing_ext = std.fs.path.extension(id.name);
+ const path = if (existing_ext.len > 0) id.name[0 .. id.name.len - existing_ext.len] else id.name;
+ for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
+ test_path.clearRetainingCapacity();
+ if (self.base.comp.sysroot) |root| {
+ try test_path.writer().print("{s}" ++ std.fs.path.sep_str ++ "{s}{s}", .{ root, path, ext });
+ } else {
+ try test_path.writer().print("{s}{s}", .{ path, ext });
+ }
+ try checked_paths.append(try arena.dupe(u8, test_path.items));
+ std.fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
+ error.FileNotFound => continue,
+ else => |e| return e,
+ };
+ break :full_path test_path.items;
}
}
@@ -1264,7 +1256,8 @@ fn parseDependentDylibs(self: *MachO) !void {
const dylib = self.getFile(dylib_index).?.dylib;
for (self.getFile(dylib.umbrella).?.dylib.rpaths.keys()) |rpath| {
const prefix = eatPrefix(rpath, "@loader_path/") orelse rpath;
- const rel_path = try std.fs.path.join(arena.allocator(), &.{ prefix, path });
+ const rel_path = try std.fs.path.join(arena, &.{ prefix, path });
+ try checked_paths.append(rel_path);
var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
const full_path = std.fs.realpath(rel_path, &buffer) catch continue;
break :full_path full_path;
@@ -1277,12 +1270,21 @@ fn parseDependentDylibs(self: *MachO) !void {
return error.Unhandled;
}
+ try checked_paths.append(try arena.dupe(u8, id.name));
var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
- const full_path = std.fs.realpath(id.name, &buffer) catch {
- dependents.appendAssumeCapacity(0);
+ if (std.fs.realpath(id.name, &buffer)) |full_path| {
+ break :full_path full_path;
+ } else |_| {
+ try self.reportMissingDependencyError(
+ self.getFile(dylib_index).?.dylib.getUmbrella(self).index,
+ id.name,
+ checked_paths.items,
+ "unable to resolve dependency",
+ .{},
+ );
+ has_errors = true;
continue;
- };
- break :full_path full_path;
+ }
};
const lib = SystemLib{
.path = full_path,
@@ -1304,11 +1306,13 @@ fn parseDependentDylibs(self: *MachO) !void {
break :file_index file_index;
}
};
- dependents.appendAssumeCapacity(file_index);
+ dependents.appendAssumeCapacity(.{ .id = id, .file = file_index });
}
const dylib = self.getFile(dylib_index).?.dylib;
- for (dylib.dependents.items, dependents.items) |id, file_index| {
+ for (dependents.items) |entry| {
+ const id = entry.id;
+ const file_index = entry.file;
if (self.getFile(file_index)) |file| {
const dep_dylib = file.dylib;
dep_dylib.hoisted = self.isHoisted(id.name);
@@ -3857,18 +3861,33 @@ fn reportMissingLibraryError(
}
}
+fn reportMissingDependencyError(
+ self: *MachO,
+ parent: File.Index,
+ path: []const u8,
+ checked_paths: []const []const u8,
+ comptime format: []const u8,
+ args: anytype,
+) error{OutOfMemory}!void {
+ var err = try self.addErrorWithNotes(2 + checked_paths.len);
+ try err.addMsg(self, format, args);
+ try err.addNote(self, "while resolving {s}", .{path});
+ try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()});
+ for (checked_paths) |p| {
+ try err.addNote(self, "tried {s}", .{p});
+ }
+}
+
fn reportDependencyError(
self: *MachO,
parent: File.Index,
- path: ?[]const u8,
+ path: []const u8,
comptime format: []const u8,
args: anytype,
) error{OutOfMemory}!void {
var err = try self.addErrorWithNotes(2);
try err.addMsg(self, format, args);
- if (path) |p| {
- try err.addNote(self, "while parsing {s}", .{p});
- }
+ try err.addNote(self, "while parsing {s}", .{path});
try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()});
}