Skip to content

Commit 0078d36

Browse files
authored
Merge pull request #11917 from motiejus/wl-search-paths
macho: implement `-search_paths_first` and `-search_dylibs_first`
2 parents 905a188 + 8f00bc9 commit 0078d36

File tree

12 files changed

+233
-63
lines changed

12 files changed

+233
-63
lines changed

lib/std/build.zig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1586,6 +1586,13 @@ pub const LibExeObjStep = struct {
15861586
/// (Darwin) Size of the pagezero segment.
15871587
pagezero_size: ?u64 = null,
15881588

1589+
/// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`.
1590+
/// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first`
1591+
/// option.
1592+
/// By default, if no option is specified, the linker assumes `paths_first` as the default
1593+
/// search strategy.
1594+
search_strategy: ?enum { paths_first, dylibs_first } = null,
1595+
15891596
/// Position Independent Code
15901597
force_pic: ?bool = null,
15911598

@@ -2650,6 +2657,10 @@ pub const LibExeObjStep = struct {
26502657
const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size});
26512658
try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
26522659
}
2660+
if (self.search_strategy) |strat| switch (strat) {
2661+
.paths_first => try zig_args.append("-search_paths_first"),
2662+
.dylibs_first => try zig_args.append("-search_dylibs_first"),
2663+
};
26532664

26542665
if (self.bundle_compiler_rt) |x| {
26552666
if (x) {

src/Compilation.zig

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,8 @@ pub const InitOptions = struct {
905905
entitlements: ?[]const u8 = null,
906906
/// (Darwin) size of the __PAGEZERO segment
907907
pagezero_size: ?u64 = null,
908+
/// (Darwin) search strategy for system libraries
909+
search_strategy: ?link.File.MachO.SearchStrategy = null,
908910
};
909911

910912
fn addPackageTableToCacheHash(
@@ -1745,6 +1747,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
17451747
.install_name = options.install_name,
17461748
.entitlements = options.entitlements,
17471749
.pagezero_size = options.pagezero_size,
1750+
.search_strategy = options.search_strategy,
17481751
});
17491752
errdefer bin_file.destroy();
17501753
comp.* = .{
@@ -2360,7 +2363,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo
23602363
/// to remind the programmer to update multiple related pieces of code that
23612364
/// are in different locations. Bump this number when adding or deleting
23622365
/// anything from the link cache manifest.
2363-
pub const link_hash_implementation_version = 4;
2366+
pub const link_hash_implementation_version = 5;
23642367

23652368
fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void {
23662369
const gpa = comp.gpa;
@@ -2370,7 +2373,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
23702373
defer arena_allocator.deinit();
23712374
const arena = arena_allocator.allocator();
23722375

2373-
comptime assert(link_hash_implementation_version == 4);
2376+
comptime assert(link_hash_implementation_version == 5);
23742377

23752378
if (comp.bin_file.options.module) |mod| {
23762379
const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{
@@ -2476,6 +2479,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
24762479
man.hash.addListOfBytes(comp.bin_file.options.frameworks);
24772480
try man.addOptionalFile(comp.bin_file.options.entitlements);
24782481
man.hash.addOptional(comp.bin_file.options.pagezero_size);
2482+
man.hash.addOptional(comp.bin_file.options.search_strategy);
24792483

24802484
// COFF specific stuff
24812485
man.hash.addOptional(comp.bin_file.options.subsystem);

src/link.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,9 @@ pub const Options = struct {
190190
/// (Darwin) size of the __PAGEZERO segment
191191
pagezero_size: ?u64 = null,
192192

193+
/// (Darwin) search strategy for system libraries
194+
search_strategy: ?File.MachO.SearchStrategy = null,
195+
193196
pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
194197
return if (options.use_lld) .Obj else options.output_mode;
195198
}

src/link/Coff.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -969,7 +969,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !
969969
man = comp.cache_parent.obtain();
970970
self.base.releaseLock();
971971

972-
comptime assert(Compilation.link_hash_implementation_version == 4);
972+
comptime assert(Compilation.link_hash_implementation_version == 5);
973973

974974
for (self.base.options.objects) |obj| {
975975
_ = try man.addFile(obj.path, null);

src/link/Elf.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1298,7 +1298,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
12981298
// We are about to obtain this lock, so here we give other processes a chance first.
12991299
self.base.releaseLock();
13001300

1301-
comptime assert(Compilation.link_hash_implementation_version == 4);
1301+
comptime assert(Compilation.link_hash_implementation_version == 5);
13021302

13031303
try man.addOptionalFile(self.base.options.linker_script);
13041304
try man.addOptionalFile(self.base.options.version_script);

src/link/MachO.zig

Lines changed: 109 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
4747

4848
pub const base_tag: File.Tag = File.Tag.macho;
4949

50+
pub const SearchStrategy = enum {
51+
paths_first,
52+
dylibs_first,
53+
};
54+
5055
base: File,
5156

5257
/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
@@ -536,7 +541,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
536541
// We are about to obtain this lock, so here we give other processes a chance first.
537542
self.base.releaseLock();
538543

539-
comptime assert(Compilation.link_hash_implementation_version == 4);
544+
comptime assert(Compilation.link_hash_implementation_version == 5);
540545

541546
for (self.base.options.objects) |obj| {
542547
_ = try man.addFile(obj.path, null);
@@ -550,6 +555,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
550555
// installation sources because they are always a product of the compiler version + target information.
551556
man.hash.add(stack_size);
552557
man.hash.addOptional(self.base.options.pagezero_size);
558+
man.hash.addOptional(self.base.options.search_strategy);
553559
man.hash.addListOfBytes(self.base.options.lib_dirs);
554560
man.hash.addListOfBytes(self.base.options.framework_dirs);
555561
man.hash.addListOfBytes(self.base.options.frameworks);
@@ -784,18 +790,43 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
784790
}
785791

786792
var libs = std.ArrayList([]const u8).init(arena);
787-
for (search_lib_names.items) |lib_name| {
788-
// Assume ld64 default: -search_paths_first
789-
// Look in each directory for a dylib (stub first), and then for archive
790-
// TODO implement alternative: -search_dylibs_first
791-
for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
792-
if (try resolveLib(arena, lib_dirs.items, lib_name, ext)) |full_path| {
793-
try libs.append(full_path);
794-
break;
795-
}
796-
} else {
797-
log.warn("library not found for '-l{s}'", .{lib_name});
798-
lib_not_found = true;
793+
794+
// Assume ld64 default -search_paths_first if no strategy specified.
795+
const search_strategy = self.base.options.search_strategy orelse .paths_first;
796+
outer: for (search_lib_names.items) |lib_name| {
797+
switch (search_strategy) {
798+
.paths_first => {
799+
// Look in each directory for a dylib (stub first), and then for archive
800+
for (lib_dirs.items) |dir| {
801+
for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
802+
if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {
803+
try libs.append(full_path);
804+
continue :outer;
805+
}
806+
}
807+
} else {
808+
log.warn("library not found for '-l{s}'", .{lib_name});
809+
lib_not_found = true;
810+
}
811+
},
812+
.dylibs_first => {
813+
// First, look for a dylib in each search dir
814+
for (lib_dirs.items) |dir| {
815+
for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
816+
if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {
817+
try libs.append(full_path);
818+
continue :outer;
819+
}
820+
}
821+
} else for (lib_dirs.items) |dir| {
822+
if (try resolveLib(arena, dir, lib_name, ".a")) |full_path| {
823+
try libs.append(full_path);
824+
} else {
825+
log.warn("library not found for '-l{s}'", .{lib_name});
826+
lib_not_found = true;
827+
}
828+
}
829+
},
799830
}
800831
}
801832

@@ -811,19 +842,23 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
811842
if (self.base.options.sysroot != null) blk: {
812843
// Try stub file first. If we hit it, then we're done as the stub file
813844
// re-exports every single symbol definition.
814-
if (try resolveLib(arena, lib_dirs.items, "System", ".tbd")) |full_path| {
815-
try libs.append(full_path);
816-
libsystem_available = true;
817-
break :blk;
845+
for (lib_dirs.items) |dir| {
846+
if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {
847+
try libs.append(full_path);
848+
libsystem_available = true;
849+
break :blk;
850+
}
818851
}
819852
// If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
820853
// doesn't export libc.dylib which we'll need to resolve subsequently also.
821-
if (try resolveLib(arena, lib_dirs.items, "System", ".dylib")) |libsystem_path| {
822-
if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| {
823-
try libs.append(libsystem_path);
824-
try libs.append(libc_path);
825-
libsystem_available = true;
826-
break :blk;
854+
for (lib_dirs.items) |dir| {
855+
if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {
856+
if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {
857+
try libs.append(libsystem_path);
858+
try libs.append(libc_path);
859+
libsystem_available = true;
860+
break :blk;
861+
}
827862
}
828863
}
829864
}
@@ -847,11 +882,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
847882
}
848883
}
849884

850-
for (self.base.options.frameworks) |framework| {
851-
for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
852-
if (try resolveFramework(arena, framework_dirs.items, framework, ext)) |full_path| {
853-
try libs.append(full_path);
854-
break;
885+
outer: for (self.base.options.frameworks) |framework| {
886+
for (framework_dirs.items) |dir| {
887+
for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
888+
if (try resolveFramework(arena, dir, framework, ext)) |full_path| {
889+
try libs.append(full_path);
890+
continue :outer;
891+
}
855892
}
856893
} else {
857894
log.warn("framework not found for '-framework {s}'", .{framework});
@@ -934,20 +971,44 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
934971
try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
935972
}
936973

974+
if (self.base.options.search_strategy) |strat| switch (strat) {
975+
.paths_first => try argv.append("-search_paths_first"),
976+
.dylibs_first => try argv.append("-search_dylibs_first"),
977+
};
978+
937979
if (self.base.options.entry) |entry| {
938980
try argv.append("-e");
939981
try argv.append(entry);
940982
}
941983

942-
try argv.appendSlice(positionals.items);
984+
for (self.base.options.objects) |obj| {
985+
try argv.append(obj.path);
986+
}
987+
988+
for (comp.c_object_table.keys()) |key| {
989+
try argv.append(key.status.success.object_path);
990+
}
991+
992+
if (module_obj_path) |p| {
993+
try argv.append(p);
994+
}
995+
996+
if (comp.compiler_rt_lib) |lib| {
997+
try argv.append(lib.full_object_path);
998+
}
999+
1000+
if (self.base.options.link_libcpp) {
1001+
try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
1002+
try argv.append(comp.libcxx_static_lib.?.full_object_path);
1003+
}
9431004

9441005
try argv.append("-o");
9451006
try argv.append(full_out_path);
9461007

9471008
try argv.append("-lSystem");
9481009
try argv.append("-lc");
9491010

950-
for (search_lib_names.items) |l_name| {
1011+
for (self.base.options.system_libs.keys()) |l_name| {
9511012
try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name}));
9521013
}
9531014

@@ -1183,51 +1244,41 @@ fn resolveSearchDir(
11831244

11841245
fn resolveLib(
11851246
arena: Allocator,
1186-
search_dirs: []const []const u8,
1247+
search_dir: []const u8,
11871248
name: []const u8,
11881249
ext: []const u8,
11891250
) !?[]const u8 {
11901251
const search_name = try std.fmt.allocPrint(arena, "lib{s}{s}", .{ name, ext });
1252+
const full_path = try fs.path.join(arena, &[_][]const u8{ search_dir, search_name });
11911253

1192-
for (search_dirs) |dir| {
1193-
const full_path = try fs.path.join(arena, &[_][]const u8{ dir, search_name });
1194-
1195-
// Check if the file exists.
1196-
const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
1197-
error.FileNotFound => continue,
1198-
else => |e| return e,
1199-
};
1200-
defer tmp.close();
1201-
1202-
return full_path;
1203-
}
1254+
// Check if the file exists.
1255+
const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
1256+
error.FileNotFound => return null,
1257+
else => |e| return e,
1258+
};
1259+
defer tmp.close();
12041260

1205-
return null;
1261+
return full_path;
12061262
}
12071263

12081264
fn resolveFramework(
12091265
arena: Allocator,
1210-
search_dirs: []const []const u8,
1266+
search_dir: []const u8,
12111267
name: []const u8,
12121268
ext: []const u8,
12131269
) !?[]const u8 {
12141270
const search_name = try std.fmt.allocPrint(arena, "{s}{s}", .{ name, ext });
12151271
const prefix_path = try std.fmt.allocPrint(arena, "{s}.framework", .{name});
1272+
const full_path = try fs.path.join(arena, &[_][]const u8{ search_dir, prefix_path, search_name });
12161273

1217-
for (search_dirs) |dir| {
1218-
const full_path = try fs.path.join(arena, &[_][]const u8{ dir, prefix_path, search_name });
1219-
1220-
// Check if the file exists.
1221-
const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
1222-
error.FileNotFound => continue,
1223-
else => |e| return e,
1224-
};
1225-
defer tmp.close();
1226-
1227-
return full_path;
1228-
}
1274+
// Check if the file exists.
1275+
const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
1276+
error.FileNotFound => return null,
1277+
else => |e| return e,
1278+
};
1279+
defer tmp.close();
12291280

1230-
return null;
1281+
return full_path;
12311282
}
12321283

12331284
fn parseObject(self: *MachO, path: []const u8) !bool {

src/link/Wasm.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2481,7 +2481,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
24812481
// We are about to obtain this lock, so here we give other processes a chance first.
24822482
self.base.releaseLock();
24832483

2484-
comptime assert(Compilation.link_hash_implementation_version == 4);
2484+
comptime assert(Compilation.link_hash_implementation_version == 5);
24852485

24862486
for (self.base.options.objects) |obj| {
24872487
_ = try man.addFile(obj.path, null);

0 commit comments

Comments
 (0)