authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-06-24 20:25:16+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-06-24 20:25:16+02:00
log0df7ed79d304afc7d379482005e892979d4a5e4d
tree34fd8af5325aeec01f7d1f0aca18f80850791b41
parentd589047e80133c5f673a7d40dd1cfa50258dcc4f

macho: implement -search_dylibs_first linker option


5 files changed, 114 insertions(+), 56 deletions(-)

lib/std/build.zig+11
......@@ -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+3
......@@ -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
910912fn 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.* = .{
src/link.zig+3
......@@ -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/MachO.zig+86-55
......@@ -47,6 +47,11 @@ pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
4747
4848pub const base_tag: File.Tag = File.Tag.macho;
4949
50pub const SearchStrategy = enum {
51 paths_first,
52 dylibs_first,
53};
54
5055base: File,
5156
5257/// 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
784789 }
785790
786791 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;
792
793 // Assume ld64 default -search_paths_first if no strategy specified.
794 const search_strategy = self.base.options.search_strategy orelse .paths_first;
795 outer: for (search_lib_names.items) |lib_name| {
796 switch (search_strategy) {
797 .paths_first => {
798 // Look in each directory for a dylib (stub first), and then for archive
799 for (lib_dirs.items) |dir| {
800 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
801 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {
802 try libs.append(full_path);
803 continue :outer;
804 }
805 }
806 } else {
807 log.warn("library not found for '-l{s}'", .{lib_name});
808 lib_not_found = true;
809 }
810 },
811 .dylibs_first => {
812 // First, look for a dylib in each search dir
813 for (lib_dirs.items) |dir| {
814 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
815 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {
816 try libs.append(full_path);
817 continue :outer;
818 }
819 }
820 } else for (lib_dirs.items) |dir| {
821 if (try resolveLib(arena, dir, lib_name, ".a")) |full_path| {
822 try libs.append(full_path);
823 } else {
824 log.warn("library not found for '-l{s}'", .{lib_name});
825 lib_not_found = true;
826 }
827 }
828 },
799829 }
800830 }
801831
......@@ -811,19 +841,23 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
811841 if (self.base.options.sysroot != null) blk: {
812842 // Try stub file first. If we hit it, then we're done as the stub file
813843 // 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;
844 for (lib_dirs.items) |dir| {
845 if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {
846 try libs.append(full_path);
847 libsystem_available = true;
848 break :blk;
849 }
818850 }
819851 // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
820852 // 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;
853 for (lib_dirs.items) |dir| {
854 if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {
855 if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {
856 try libs.append(libsystem_path);
857 try libs.append(libc_path);
858 libsystem_available = true;
859 break :blk;
860 }
827861 }
828862 }
829863 }
......@@ -847,11 +881,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
847881 }
848882 }
849883
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;
884 outer: for (self.base.options.frameworks) |framework| {
885 for (framework_dirs.items) |dir| {
886 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
887 if (try resolveFramework(arena, dir, framework, ext)) |full_path| {
888 try libs.append(full_path);
889 continue :outer;
890 }
855891 }
856892 } else {
857893 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
934970 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
935971 }
936972
973 if (self.base.options.search_strategy) |strat| switch (strat) {
974 .paths_first => try argv.append("-search_paths_first"),
975 .dylibs_first => try argv.append("-search_dylibs_first"),
976 };
977
937978 if (self.base.options.entry) |entry| {
938979 try argv.append("-e");
939980 try argv.append(entry);
......@@ -1183,51 +1224,41 @@ fn resolveSearchDir(
11831224
11841225fn resolveLib(
11851226 arena: Allocator,
1186 search_dirs: []const []const u8,
1227 search_dir: []const u8,
11871228 name: []const u8,
11881229 ext: []const u8,
11891230) !?[]const u8 {
11901231 const search_name = try std.fmt.allocPrint(arena, "lib{s}{s}", .{ name, ext });
1232 const full_path = try fs.path.join(arena, &[_][]const u8{ search_dir, search_name });
11911233
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 }
1234 // Check if the file exists.
1235 const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
1236 error.FileNotFound => return null,
1237 else => |e| return e,
1238 };
1239 defer tmp.close();
12041240
1205 return null;
1241 return full_path;
12061242}
12071243
12081244fn resolveFramework(
12091245 arena: Allocator,
1210 search_dirs: []const []const u8,
1246 search_dir: []const u8,
12111247 name: []const u8,
12121248 ext: []const u8,
12131249) !?[]const u8 {
12141250 const search_name = try std.fmt.allocPrint(arena, "{s}{s}", .{ name, ext });
12151251 const prefix_path = try std.fmt.allocPrint(arena, "{s}.framework", .{name});
1252 const full_path = try fs.path.join(arena, &[_][]const u8{ search_dir, prefix_path, search_name });
12161253
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 }
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();
12291260
1230 return null;
1261 return full_path;
12311262}
12321263
12331264fn parseObject(self: *MachO, path: []const u8) !bool {
src/main.zig+11-1
......@@ -448,6 +448,8 @@ const usage_build_generic =
448448 \\ -install_name=[value] (Darwin) add dylib's install name
449449 \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature
450450 \\ -pagezero_size [value] (Darwin) size of the __PAGEZERO segment in hexadecimal notation
451 \\ -search_paths_first (Darwin) search each dir in library search paths for `libx.dylib` then `libx.a`
452 \\ -search_dylibs_first (Darwin) search `libx.dylib` in each dir in library search paths, then `libx.a`
451453 \\ --import-memory (WebAssembly) import memory from the environment
452454 \\ --import-table (WebAssembly) import function table from the host environment
453455 \\ --export-table (WebAssembly) export function table to the host environment
......@@ -696,6 +698,7 @@ fn buildOutputType(
696698 var hash_style: link.HashStyle = .both;
697699 var entitlements: ?[]const u8 = null;
698700 var pagezero_size: ?u64 = null;
701 var search_strategy: ?link.File.MachO.SearchStrategy = null;
699702
700703 // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.
701704 // This array is populated by zig cc frontend and then has to be converted to zig-style
......@@ -917,6 +920,10 @@ fn buildOutputType(
917920 pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {
918921 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
919922 };
923 } else if (mem.eql(u8, arg, "-search_paths_first")) {
924 search_strategy = .paths_first;
925 } else if (mem.eql(u8, arg, "-search_dylibs_first")) {
926 search_strategy = .dylibs_first;
920927 } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {
921928 linker_script = args_iter.next() orelse {
922929 fatal("expected parameter after {s}", .{arg});
......@@ -1476,7 +1483,9 @@ fn buildOutputType(
14761483 {
14771484 force_static_libs = true;
14781485 } else if (mem.eql(u8, linker_arg, "-search_paths_first")) {
1479 // ignore, since it's the default behavior in both ld64 and zld
1486 search_strategy = .paths_first;
1487 } else if (mem.eql(u8, linker_arg, "-search_dylibs_first")) {
1488 search_strategy = .dylibs_first;
14801489 } else {
14811490 try linker_args.append(linker_arg);
14821491 }
......@@ -2784,6 +2793,7 @@ fn buildOutputType(
27842793 .install_name = install_name,
27852794 .entitlements = entitlements,
27862795 .pagezero_size = pagezero_size,
2796 .search_strategy = search_strategy,
27872797 }) catch |err| switch (err) {
27882798 error.LibCUnavailable => {
27892799 const target = target_info.target;