Commit 0df7ed79d3
Changed files (5)
lib
std
lib/std/build.zig
@@ -1586,6 +1586,13 @@ pub const LibExeObjStep = struct {
/// (Darwin) Size of the pagezero segment.
pagezero_size: ?u64 = null,
+ /// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`.
+ /// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first`
+ /// option.
+ /// By default, if no option is specified, the linker assumes `paths_first` as the default
+ /// search strategy.
+ search_strategy: ?enum { paths_first, dylibs_first } = null,
+
/// Position Independent Code
force_pic: ?bool = null,
@@ -2650,6 +2657,10 @@ pub const LibExeObjStep = struct {
const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size});
try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
}
+ if (self.search_strategy) |strat| switch (strat) {
+ .paths_first => try zig_args.append("-search_paths_first"),
+ .dylibs_first => try zig_args.append("-search_dylibs_first"),
+ };
if (self.bundle_compiler_rt) |x| {
if (x) {
src/link/MachO.zig
@@ -47,6 +47,11 @@ pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
pub const base_tag: File.Tag = File.Tag.macho;
+pub const SearchStrategy = enum {
+ paths_first,
+ dylibs_first,
+};
+
base: File,
/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
@@ -784,18 +789,43 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
}
var libs = std.ArrayList([]const u8).init(arena);
- for (search_lib_names.items) |lib_name| {
- // Assume ld64 default: -search_paths_first
- // Look in each directory for a dylib (stub first), and then for archive
- // TODO implement alternative: -search_dylibs_first
- for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
- if (try resolveLib(arena, lib_dirs.items, lib_name, ext)) |full_path| {
- try libs.append(full_path);
- break;
- }
- } else {
- log.warn("library not found for '-l{s}'", .{lib_name});
- lib_not_found = true;
+
+ // Assume ld64 default -search_paths_first if no strategy specified.
+ const search_strategy = self.base.options.search_strategy orelse .paths_first;
+ outer: for (search_lib_names.items) |lib_name| {
+ switch (search_strategy) {
+ .paths_first => {
+ // Look in each directory for a dylib (stub first), and then for archive
+ for (lib_dirs.items) |dir| {
+ for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
+ if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {
+ try libs.append(full_path);
+ continue :outer;
+ }
+ }
+ } else {
+ log.warn("library not found for '-l{s}'", .{lib_name});
+ lib_not_found = true;
+ }
+ },
+ .dylibs_first => {
+ // First, look for a dylib in each search dir
+ for (lib_dirs.items) |dir| {
+ for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
+ if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {
+ try libs.append(full_path);
+ continue :outer;
+ }
+ }
+ } else for (lib_dirs.items) |dir| {
+ if (try resolveLib(arena, dir, lib_name, ".a")) |full_path| {
+ try libs.append(full_path);
+ } else {
+ log.warn("library not found for '-l{s}'", .{lib_name});
+ lib_not_found = true;
+ }
+ }
+ },
}
}
@@ -811,19 +841,23 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
if (self.base.options.sysroot != null) blk: {
// Try stub file first. If we hit it, then we're done as the stub file
// re-exports every single symbol definition.
- if (try resolveLib(arena, lib_dirs.items, "System", ".tbd")) |full_path| {
- try libs.append(full_path);
- libsystem_available = true;
- break :blk;
+ for (lib_dirs.items) |dir| {
+ if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {
+ try libs.append(full_path);
+ libsystem_available = true;
+ break :blk;
+ }
}
// If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
// doesn't export libc.dylib which we'll need to resolve subsequently also.
- if (try resolveLib(arena, lib_dirs.items, "System", ".dylib")) |libsystem_path| {
- if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| {
- try libs.append(libsystem_path);
- try libs.append(libc_path);
- libsystem_available = true;
- break :blk;
+ for (lib_dirs.items) |dir| {
+ if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {
+ if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {
+ try libs.append(libsystem_path);
+ try libs.append(libc_path);
+ libsystem_available = true;
+ break :blk;
+ }
}
}
}
@@ -847,11 +881,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
}
}
- for (self.base.options.frameworks) |framework| {
- for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
- if (try resolveFramework(arena, framework_dirs.items, framework, ext)) |full_path| {
- try libs.append(full_path);
- break;
+ outer: for (self.base.options.frameworks) |framework| {
+ for (framework_dirs.items) |dir| {
+ for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
+ if (try resolveFramework(arena, dir, framework, ext)) |full_path| {
+ try libs.append(full_path);
+ continue :outer;
+ }
}
} else {
log.warn("framework not found for '-framework {s}'", .{framework});
@@ -934,6 +970,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
}
+ if (self.base.options.search_strategy) |strat| switch (strat) {
+ .paths_first => try argv.append("-search_paths_first"),
+ .dylibs_first => try argv.append("-search_dylibs_first"),
+ };
+
if (self.base.options.entry) |entry| {
try argv.append("-e");
try argv.append(entry);
@@ -1183,51 +1224,41 @@ fn resolveSearchDir(
fn resolveLib(
arena: Allocator,
- search_dirs: []const []const u8,
+ search_dir: []const u8,
name: []const u8,
ext: []const u8,
) !?[]const u8 {
const search_name = try std.fmt.allocPrint(arena, "lib{s}{s}", .{ name, ext });
+ const full_path = try fs.path.join(arena, &[_][]const u8{ search_dir, search_name });
- for (search_dirs) |dir| {
- const full_path = try fs.path.join(arena, &[_][]const u8{ dir, search_name });
-
- // Check if the file exists.
- const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
- error.FileNotFound => continue,
- else => |e| return e,
- };
- defer tmp.close();
-
- return full_path;
- }
+ // Check if the file exists.
+ const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
+ error.FileNotFound => return null,
+ else => |e| return e,
+ };
+ defer tmp.close();
- return null;
+ return full_path;
}
fn resolveFramework(
arena: Allocator,
- search_dirs: []const []const u8,
+ search_dir: []const u8,
name: []const u8,
ext: []const u8,
) !?[]const u8 {
const search_name = try std.fmt.allocPrint(arena, "{s}{s}", .{ name, ext });
const prefix_path = try std.fmt.allocPrint(arena, "{s}.framework", .{name});
+ const full_path = try fs.path.join(arena, &[_][]const u8{ search_dir, prefix_path, search_name });
- for (search_dirs) |dir| {
- const full_path = try fs.path.join(arena, &[_][]const u8{ dir, prefix_path, search_name });
-
- // Check if the file exists.
- const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
- error.FileNotFound => continue,
- else => |e| return e,
- };
- defer tmp.close();
-
- return full_path;
- }
+ // Check if the file exists.
+ const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
+ error.FileNotFound => return null,
+ else => |e| return e,
+ };
+ defer tmp.close();
- return null;
+ return full_path;
}
fn parseObject(self: *MachO, path: []const u8) !bool {
src/Compilation.zig
@@ -905,6 +905,8 @@ pub const InitOptions = struct {
entitlements: ?[]const u8 = null,
/// (Darwin) size of the __PAGEZERO segment
pagezero_size: ?u64 = null,
+ /// (Darwin) search strategy for system libraries
+ search_strategy: ?link.File.MachO.SearchStrategy = null,
};
fn addPackageTableToCacheHash(
@@ -1745,6 +1747,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
.install_name = options.install_name,
.entitlements = options.entitlements,
.pagezero_size = options.pagezero_size,
+ .search_strategy = options.search_strategy,
});
errdefer bin_file.destroy();
comp.* = .{
src/link.zig
@@ -190,6 +190,9 @@ pub const Options = struct {
/// (Darwin) size of the __PAGEZERO segment
pagezero_size: ?u64 = null,
+ /// (Darwin) search strategy for system libraries
+ search_strategy: ?File.MachO.SearchStrategy = null,
+
pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
return if (options.use_lld) .Obj else options.output_mode;
}
src/main.zig
@@ -448,6 +448,8 @@ const usage_build_generic =
\\ -install_name=[value] (Darwin) add dylib's install name
\\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature
\\ -pagezero_size [value] (Darwin) size of the __PAGEZERO segment in hexadecimal notation
+ \\ -search_paths_first (Darwin) search each dir in library search paths for `libx.dylib` then `libx.a`
+ \\ -search_dylibs_first (Darwin) search `libx.dylib` in each dir in library search paths, then `libx.a`
\\ --import-memory (WebAssembly) import memory from the environment
\\ --import-table (WebAssembly) import function table from the host environment
\\ --export-table (WebAssembly) export function table to the host environment
@@ -696,6 +698,7 @@ fn buildOutputType(
var hash_style: link.HashStyle = .both;
var entitlements: ?[]const u8 = null;
var pagezero_size: ?u64 = null;
+ var search_strategy: ?link.File.MachO.SearchStrategy = null;
// e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.
// This array is populated by zig cc frontend and then has to be converted to zig-style
@@ -917,6 +920,10 @@ fn buildOutputType(
pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {
fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
};
+ } else if (mem.eql(u8, arg, "-search_paths_first")) {
+ search_strategy = .paths_first;
+ } else if (mem.eql(u8, arg, "-search_dylibs_first")) {
+ search_strategy = .dylibs_first;
} else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {
linker_script = args_iter.next() orelse {
fatal("expected parameter after {s}", .{arg});
@@ -1476,7 +1483,9 @@ fn buildOutputType(
{
force_static_libs = true;
} else if (mem.eql(u8, linker_arg, "-search_paths_first")) {
- // ignore, since it's the default behavior in both ld64 and zld
+ search_strategy = .paths_first;
+ } else if (mem.eql(u8, linker_arg, "-search_dylibs_first")) {
+ search_strategy = .dylibs_first;
} else {
try linker_args.append(linker_arg);
}
@@ -2784,6 +2793,7 @@ fn buildOutputType(
.install_name = install_name,
.entitlements = entitlements,
.pagezero_size = pagezero_size,
+ .search_strategy = search_strategy,
}) catch |err| switch (err) {
error.LibCUnavailable => {
const target = target_info.target;