| author | |
| committer | |
| log | efc5c97bff87d4c28ae9642fe69d9bc2c7e9eeb7 |
| tree | 12b31bae7ed111c1f62541c7d46f20ad6d222717 |
| parent | a76775b50a65fd0ea0fd17d6ef3c42058df13997 |
13 files changed, 94 insertions(+), 47 deletions(-)
lib/std/build.zig+6| ... | ... | @@ -1601,6 +1601,9 @@ pub const LibExeObjStep = struct { |
| 1601 | 1601 | /// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN. |
| 1602 | 1602 | headerpad_max_install_names: bool = false, |
| 1603 | 1603 | |
| 1604 | /// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols. | |
| 1605 | dead_strip_dylibs: bool = false, | |
| 1606 | ||
| 1604 | 1607 | /// Position Independent Code |
| 1605 | 1608 | force_pic: ?bool = null, |
| 1606 | 1609 | |
| ... | ... | @@ -2676,6 +2679,9 @@ pub const LibExeObjStep = struct { |
| 2676 | 2679 | if (self.headerpad_max_install_names) { |
| 2677 | 2680 | try zig_args.append("-headerpad_max_install_names"); |
| 2678 | 2681 | } |
| 2682 | if (self.dead_strip_dylibs) { | |
| 2683 | try zig_args.append("-dead_strip_dylibs"); | |
| 2684 | } | |
| 2679 | 2685 | |
| 2680 | 2686 | if (self.bundle_compiler_rt) |x| { |
| 2681 | 2687 | if (x) { |
src/Compilation.zig+6-2| ... | ... | @@ -911,6 +911,8 @@ pub const InitOptions = struct { |
| 911 | 911 | headerpad_size: ?u32 = null, |
| 912 | 912 | /// (Darwin) set enough space as if all paths were MATPATHLEN |
| 913 | 913 | headerpad_max_install_names: bool = false, |
| 914 | /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols | |
| 915 | dead_strip_dylibs: bool = false, | |
| 914 | 916 | }; |
| 915 | 917 | |
| 916 | 918 | fn addPackageTableToCacheHash( |
| ... | ... | @@ -1754,6 +1756,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1754 | 1756 | .search_strategy = options.search_strategy, |
| 1755 | 1757 | .headerpad_size = options.headerpad_size, |
| 1756 | 1758 | .headerpad_max_install_names = options.headerpad_max_install_names, |
| 1759 | .dead_strip_dylibs = options.dead_strip_dylibs, | |
| 1757 | 1760 | }); |
| 1758 | 1761 | errdefer bin_file.destroy(); |
| 1759 | 1762 | comp.* = .{ |
| ... | ... | @@ -2369,7 +2372,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo |
| 2369 | 2372 | /// to remind the programmer to update multiple related pieces of code that |
| 2370 | 2373 | /// are in different locations. Bump this number when adding or deleting |
| 2371 | 2374 | /// anything from the link cache manifest. |
| 2372 | pub const link_hash_implementation_version = 6; | |
| 2375 | pub const link_hash_implementation_version = 7; | |
| 2373 | 2376 | |
| 2374 | 2377 | fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void { |
| 2375 | 2378 | const gpa = comp.gpa; |
| ... | ... | @@ -2379,7 +2382,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes |
| 2379 | 2382 | defer arena_allocator.deinit(); |
| 2380 | 2383 | const arena = arena_allocator.allocator(); |
| 2381 | 2384 | |
| 2382 | comptime assert(link_hash_implementation_version == 6); | |
| 2385 | comptime assert(link_hash_implementation_version == 7); | |
| 2383 | 2386 | |
| 2384 | 2387 | if (comp.bin_file.options.module) |mod| { |
| 2385 | 2388 | const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{ |
| ... | ... | @@ -2488,6 +2491,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes |
| 2488 | 2491 | man.hash.addOptional(comp.bin_file.options.search_strategy); |
| 2489 | 2492 | man.hash.addOptional(comp.bin_file.options.headerpad_size); |
| 2490 | 2493 | man.hash.add(comp.bin_file.options.headerpad_max_install_names); |
| 2494 | man.hash.add(comp.bin_file.options.dead_strip_dylibs); | |
| 2491 | 2495 | |
| 2492 | 2496 | // COFF specific stuff |
| 2493 | 2497 | man.hash.addOptional(comp.bin_file.options.subsystem); |
src/link.zig+3| ... | ... | @@ -199,6 +199,9 @@ pub const Options = struct { |
| 199 | 199 | /// (Darwin) set enough space as if all paths were MATPATHLEN |
| 200 | 200 | headerpad_max_install_names: bool = false, |
| 201 | 201 | |
| 202 | /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols | |
| 203 | dead_strip_dylibs: bool = false, | |
| 204 | ||
| 202 | 205 | pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode { |
| 203 | 206 | return if (options.use_lld) .Obj else options.output_mode; |
| 204 | 207 | } |
src/link/Coff.zig+1-1| ... | ... | @@ -969,7 +969,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) ! |
| 969 | 969 | man = comp.cache_parent.obtain(); |
| 970 | 970 | self.base.releaseLock(); |
| 971 | 971 | |
| 972 | comptime assert(Compilation.link_hash_implementation_version == 6); | |
| 972 | comptime assert(Compilation.link_hash_implementation_version == 7); | |
| 973 | 973 | |
| 974 | 974 | for (self.base.options.objects) |obj| { |
| 975 | 975 | _ = try man.addFile(obj.path, null); |
src/link/Elf.zig+1-1| ... | ... | @@ -1298,7 +1298,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v |
| 1298 | 1298 | // We are about to obtain this lock, so here we give other processes a chance first. |
| 1299 | 1299 | self.base.releaseLock(); |
| 1300 | 1300 | |
| 1301 | comptime assert(Compilation.link_hash_implementation_version == 6); | |
| 1301 | comptime assert(Compilation.link_hash_implementation_version == 7); | |
| 1302 | 1302 | |
| 1303 | 1303 | try man.addOptionalFile(self.base.options.linker_script); |
| 1304 | 1304 | try man.addOptionalFile(self.base.options.version_script); |
src/link/MachO.zig+12-2| ... | ... | @@ -541,7 +541,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 541 | 541 | // We are about to obtain this lock, so here we give other processes a chance first. |
| 542 | 542 | self.base.releaseLock(); |
| 543 | 543 | |
| 544 | comptime assert(Compilation.link_hash_implementation_version == 6); | |
| 544 | comptime assert(Compilation.link_hash_implementation_version == 7); | |
| 545 | 545 | |
| 546 | 546 | for (self.base.options.objects) |obj| { |
| 547 | 547 | _ = try man.addFile(obj.path, null); |
| ... | ... | @@ -558,6 +558,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 558 | 558 | man.hash.addOptional(self.base.options.search_strategy); |
| 559 | 559 | man.hash.addOptional(self.base.options.headerpad_size); |
| 560 | 560 | man.hash.add(self.base.options.headerpad_max_install_names); |
| 561 | man.hash.add(self.base.options.dead_strip_dylibs); | |
| 561 | 562 | man.hash.addListOfBytes(self.base.options.lib_dirs); |
| 562 | 563 | man.hash.addListOfBytes(self.base.options.framework_dirs); |
| 563 | 564 | man.hash.addListOfBytes(self.base.options.frameworks); |
| ... | ... | @@ -987,6 +988,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 987 | 988 | try argv.append("-headerpad_max_install_names"); |
| 988 | 989 | } |
| 989 | 990 | |
| 991 | if (self.base.options.dead_strip_dylibs) { | |
| 992 | try argv.append("-dead_strip_dylibs"); | |
| 993 | } | |
| 994 | ||
| 990 | 995 | if (self.base.options.entry) |entry| { |
| 991 | 996 | try argv.append("-e"); |
| 992 | 997 | try argv.append(entry); |
| ... | ... | @@ -1425,7 +1430,12 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy |
| 1425 | 1430 | try self.dylibs.append(self.base.allocator, dylib); |
| 1426 | 1431 | try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id); |
| 1427 | 1432 | |
| 1428 | if (!(opts.is_dependent or self.referenced_dylibs.contains(dylib_id))) { | |
| 1433 | const should_link_dylib_even_if_unreachable = blk: { | |
| 1434 | if (self.base.options.dead_strip_dylibs) break :blk false; | |
| 1435 | break :blk !(opts.is_dependent or self.referenced_dylibs.contains(dylib_id)); | |
| 1436 | }; | |
| 1437 | ||
| 1438 | if (should_link_dylib_even_if_unreachable) { | |
| 1429 | 1439 | try self.addLoadDylibLC(dylib_id); |
| 1430 | 1440 | try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {}); |
| 1431 | 1441 | } |
src/link/Wasm.zig+1-1| ... | ... | @@ -2546,7 +2546,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! |
| 2546 | 2546 | // We are about to obtain this lock, so here we give other processes a chance first. |
| 2547 | 2547 | self.base.releaseLock(); |
| 2548 | 2548 | |
| 2549 | comptime assert(Compilation.link_hash_implementation_version == 6); | |
| 2549 | comptime assert(Compilation.link_hash_implementation_version == 7); | |
| 2550 | 2550 | |
| 2551 | 2551 | for (self.base.options.objects) |obj| { |
| 2552 | 2552 | _ = try man.addFile(obj.path, null); |
src/main.zig+7| ... | ... | @@ -452,6 +452,7 @@ const usage_build_generic = |
| 452 | 452 | \\ -search_dylibs_first (Darwin) search `libx.dylib` in each dir in library search paths, then `libx.a` |
| 453 | 453 | \\ -headerpad [value] (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation |
| 454 | 454 | \\ -headerpad_max_install_names (Darwin) set enough space as if all paths were MAXPATHLEN |
| 455 | \\ -dead_strip_dylibs (Darwin) remove dylibs that are unreachable by the entry point or exported symbols | |
| 455 | 456 | \\ --import-memory (WebAssembly) import memory from the environment |
| 456 | 457 | \\ --import-table (WebAssembly) import function table from the host environment |
| 457 | 458 | \\ --export-table (WebAssembly) export function table to the host environment |
| ... | ... | @@ -703,6 +704,7 @@ fn buildOutputType( |
| 703 | 704 | var search_strategy: ?link.File.MachO.SearchStrategy = null; |
| 704 | 705 | var headerpad_size: ?u32 = null; |
| 705 | 706 | var headerpad_max_install_names: bool = false; |
| 707 | var dead_strip_dylibs: bool = false; | |
| 706 | 708 | |
| 707 | 709 | // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names. |
| 708 | 710 | // This array is populated by zig cc frontend and then has to be converted to zig-style |
| ... | ... | @@ -937,6 +939,8 @@ fn buildOutputType( |
| 937 | 939 | }; |
| 938 | 940 | } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) { |
| 939 | 941 | headerpad_max_install_names = true; |
| 942 | } else if (mem.eql(u8, arg, "-dead_strip_dylibs")) { | |
| 943 | dead_strip_dylibs = true; | |
| 940 | 944 | } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) { |
| 941 | 945 | linker_script = args_iter.next() orelse { |
| 942 | 946 | fatal("expected parameter after {s}", .{arg}); |
| ... | ... | @@ -1700,6 +1704,8 @@ fn buildOutputType( |
| 1700 | 1704 | }; |
| 1701 | 1705 | } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) { |
| 1702 | 1706 | headerpad_max_install_names = true; |
| 1707 | } else if (mem.eql(u8, arg, "-dead_strip_dylibs")) { | |
| 1708 | dead_strip_dylibs = true; | |
| 1703 | 1709 | } else if (mem.eql(u8, arg, "--gc-sections")) { |
| 1704 | 1710 | linker_gc_sections = true; |
| 1705 | 1711 | } else if (mem.eql(u8, arg, "--no-gc-sections")) { |
| ... | ... | @@ -2821,6 +2827,7 @@ fn buildOutputType( |
| 2821 | 2827 | .search_strategy = search_strategy, |
| 2822 | 2828 | .headerpad_size = headerpad_size, |
| 2823 | 2829 | .headerpad_max_install_names = headerpad_max_install_names, |
| 2830 | .dead_strip_dylibs = dead_strip_dylibs, | |
| 2824 | 2831 | }) catch |err| switch (err) { |
| 2825 | 2832 | error.LibCUnavailable => { |
| 2826 | 2833 | const target = target_info.target; |
test/link.zig+1-1| ... | ... | @@ -40,7 +40,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void { |
| 40 | 40 | .build_modes = true, |
| 41 | 41 | }); |
| 42 | 42 | |
| 43 | cases.addBuildFile("test/link/macho/frameworks/build.zig", .{ | |
| 43 | cases.addBuildFile("test/link/macho/dead_strip_dylibs/build.zig", .{ | |
| 44 | 44 | .build_modes = true, |
| 45 | 45 | .requires_macos_sdk = true, |
| 46 | 46 | }); |
test/link/macho/dead_strip_dylibs/build.zig created+46| ... | ... | @@ -0,0 +1,46 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Builder = std.build.Builder; | |
| 3 | const LibExeObjectStep = std.build.LibExeObjStep; | |
| 4 | ||
| 5 | pub fn build(b: *Builder) void { | |
| 6 | const mode = b.standardReleaseOptions(); | |
| 7 | ||
| 8 | const test_step = b.step("test", "Test the program"); | |
| 9 | ||
| 10 | { | |
| 11 | // Without -dead_strip_dylibs we expect `-la` to include liba.dylib in the final executable | |
| 12 | const exe = createScenario(b, mode); | |
| 13 | ||
| 14 | const check = exe.checkObject(.macho); | |
| 15 | check.checkStart("cmd LOAD_DYLIB"); | |
| 16 | check.checkNext("name {*}Cocoa"); | |
| 17 | ||
| 18 | check.checkStart("cmd LOAD_DYLIB"); | |
| 19 | check.checkNext("name {*}libobjc{*}.dylib"); | |
| 20 | ||
| 21 | test_step.dependOn(&check.step); | |
| 22 | ||
| 23 | const run_cmd = exe.run(); | |
| 24 | test_step.dependOn(&run_cmd.step); | |
| 25 | } | |
| 26 | ||
| 27 | { | |
| 28 | // With -dead_strip_dylibs, we should include liba.dylib as it's unreachable | |
| 29 | const exe = createScenario(b, mode); | |
| 30 | exe.dead_strip_dylibs = true; | |
| 31 | ||
| 32 | const run_cmd = exe.run(); | |
| 33 | run_cmd.expected_exit_code = @bitCast(u8, @as(i8, -2)); // should fail | |
| 34 | test_step.dependOn(&run_cmd.step); | |
| 35 | } | |
| 36 | } | |
| 37 | ||
| 38 | fn createScenario(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep { | |
| 39 | const exe = b.addExecutable("test", null); | |
| 40 | b.default_step.dependOn(&exe.step); | |
| 41 | exe.addCSourceFile("main.c", &[0][]const u8{}); | |
| 42 | exe.setBuildMode(mode); | |
| 43 | exe.linkLibC(); | |
| 44 | exe.linkFramework("Cocoa"); | |
| 45 | return exe; | |
| 46 | } |
test/link/macho/dead_strip_dylibs/main.c created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | #include <objc/runtime.h> | |
| 2 | ||
| 3 | int main() { | |
| 4 | if (objc_getClass("NSObject") == 0) { | |
| 5 | return -1; | |
| 6 | } | |
| 7 | if (objc_getClass("NSApplication") == 0) { | |
| 8 | return -2; | |
| 9 | } | |
| 10 | } |
test/link/macho/frameworks/build.zig deleted-32| ... | ... | @@ -1,32 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Builder = std.build.Builder; | |
| 3 | ||
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 6 | ||
| 7 | const test_step = b.step("test", "Test the program"); | |
| 8 | ||
| 9 | const exe = b.addExecutable("test", null); | |
| 10 | b.default_step.dependOn(&exe.step); | |
| 11 | exe.addCSourceFile("main.c", &[0][]const u8{}); | |
| 12 | exe.setBuildMode(mode); | |
| 13 | exe.linkLibC(); | |
| 14 | exe.linkFramework("Cocoa"); | |
| 15 | ||
| 16 | const check = exe.checkObject(.macho); | |
| 17 | check.checkStart("cmd LOAD_DYLIB"); | |
| 18 | check.checkNext("name {*}Cocoa"); | |
| 19 | ||
| 20 | switch (mode) { | |
| 21 | .Debug, .ReleaseSafe => { | |
| 22 | check.checkStart("cmd LOAD_DYLIB"); | |
| 23 | check.checkNext("name {*}libobjc{*}.dylib"); | |
| 24 | }, | |
| 25 | else => {}, | |
| 26 | } | |
| 27 | ||
| 28 | test_step.dependOn(&check.step); | |
| 29 | ||
| 30 | const run_cmd = exe.run(); | |
| 31 | test_step.dependOn(&run_cmd.step); | |
| 32 | } |
test/link/macho/frameworks/main.c deleted-7| ... | ... | @@ -1,7 +0,0 @@ |
| 1 | #include <assert.h> | |
| 2 | #include <objc/runtime.h> | |
| 3 | ||
| 4 | int main() { | |
| 5 | assert(objc_getClass("NSObject") > 0); | |
| 6 | assert(objc_getClass("NSApplication") > 0); | |
| 7 | } |