mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
macho: implement -dead_strip_dylibs linker flag
This commit is contained in:
parent
a76775b50a
commit
efc5c97bff
@ -1601,6 +1601,9 @@ pub const LibExeObjStep = struct {
|
||||
/// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN.
|
||||
headerpad_max_install_names: bool = false,
|
||||
|
||||
/// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols.
|
||||
dead_strip_dylibs: bool = false,
|
||||
|
||||
/// Position Independent Code
|
||||
force_pic: ?bool = null,
|
||||
|
||||
@ -2676,6 +2679,9 @@ pub const LibExeObjStep = struct {
|
||||
if (self.headerpad_max_install_names) {
|
||||
try zig_args.append("-headerpad_max_install_names");
|
||||
}
|
||||
if (self.dead_strip_dylibs) {
|
||||
try zig_args.append("-dead_strip_dylibs");
|
||||
}
|
||||
|
||||
if (self.bundle_compiler_rt) |x| {
|
||||
if (x) {
|
||||
|
||||
@ -911,6 +911,8 @@ pub const InitOptions = struct {
|
||||
headerpad_size: ?u32 = null,
|
||||
/// (Darwin) set enough space as if all paths were MATPATHLEN
|
||||
headerpad_max_install_names: bool = false,
|
||||
/// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols
|
||||
dead_strip_dylibs: bool = false,
|
||||
};
|
||||
|
||||
fn addPackageTableToCacheHash(
|
||||
@ -1754,6 +1756,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
|
||||
.search_strategy = options.search_strategy,
|
||||
.headerpad_size = options.headerpad_size,
|
||||
.headerpad_max_install_names = options.headerpad_max_install_names,
|
||||
.dead_strip_dylibs = options.dead_strip_dylibs,
|
||||
});
|
||||
errdefer bin_file.destroy();
|
||||
comp.* = .{
|
||||
@ -2369,7 +2372,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo
|
||||
/// to remind the programmer to update multiple related pieces of code that
|
||||
/// are in different locations. Bump this number when adding or deleting
|
||||
/// anything from the link cache manifest.
|
||||
pub const link_hash_implementation_version = 6;
|
||||
pub const link_hash_implementation_version = 7;
|
||||
|
||||
fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void {
|
||||
const gpa = comp.gpa;
|
||||
@ -2379,7 +2382,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
|
||||
defer arena_allocator.deinit();
|
||||
const arena = arena_allocator.allocator();
|
||||
|
||||
comptime assert(link_hash_implementation_version == 6);
|
||||
comptime assert(link_hash_implementation_version == 7);
|
||||
|
||||
if (comp.bin_file.options.module) |mod| {
|
||||
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
|
||||
man.hash.addOptional(comp.bin_file.options.search_strategy);
|
||||
man.hash.addOptional(comp.bin_file.options.headerpad_size);
|
||||
man.hash.add(comp.bin_file.options.headerpad_max_install_names);
|
||||
man.hash.add(comp.bin_file.options.dead_strip_dylibs);
|
||||
|
||||
// COFF specific stuff
|
||||
man.hash.addOptional(comp.bin_file.options.subsystem);
|
||||
|
||||
@ -199,6 +199,9 @@ pub const Options = struct {
|
||||
/// (Darwin) set enough space as if all paths were MATPATHLEN
|
||||
headerpad_max_install_names: bool = false,
|
||||
|
||||
/// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols
|
||||
dead_strip_dylibs: bool = false,
|
||||
|
||||
pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
|
||||
return if (options.use_lld) .Obj else options.output_mode;
|
||||
}
|
||||
|
||||
@ -969,7 +969,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !
|
||||
man = comp.cache_parent.obtain();
|
||||
self.base.releaseLock();
|
||||
|
||||
comptime assert(Compilation.link_hash_implementation_version == 6);
|
||||
comptime assert(Compilation.link_hash_implementation_version == 7);
|
||||
|
||||
for (self.base.options.objects) |obj| {
|
||||
_ = try man.addFile(obj.path, null);
|
||||
|
||||
@ -1298,7 +1298,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
|
||||
// We are about to obtain this lock, so here we give other processes a chance first.
|
||||
self.base.releaseLock();
|
||||
|
||||
comptime assert(Compilation.link_hash_implementation_version == 6);
|
||||
comptime assert(Compilation.link_hash_implementation_version == 7);
|
||||
|
||||
try man.addOptionalFile(self.base.options.linker_script);
|
||||
try man.addOptionalFile(self.base.options.version_script);
|
||||
|
||||
@ -541,7 +541,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
|
||||
// We are about to obtain this lock, so here we give other processes a chance first.
|
||||
self.base.releaseLock();
|
||||
|
||||
comptime assert(Compilation.link_hash_implementation_version == 6);
|
||||
comptime assert(Compilation.link_hash_implementation_version == 7);
|
||||
|
||||
for (self.base.options.objects) |obj| {
|
||||
_ = try man.addFile(obj.path, null);
|
||||
@ -558,6 +558,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
|
||||
man.hash.addOptional(self.base.options.search_strategy);
|
||||
man.hash.addOptional(self.base.options.headerpad_size);
|
||||
man.hash.add(self.base.options.headerpad_max_install_names);
|
||||
man.hash.add(self.base.options.dead_strip_dylibs);
|
||||
man.hash.addListOfBytes(self.base.options.lib_dirs);
|
||||
man.hash.addListOfBytes(self.base.options.framework_dirs);
|
||||
man.hash.addListOfBytes(self.base.options.frameworks);
|
||||
@ -987,6 +988,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
|
||||
try argv.append("-headerpad_max_install_names");
|
||||
}
|
||||
|
||||
if (self.base.options.dead_strip_dylibs) {
|
||||
try argv.append("-dead_strip_dylibs");
|
||||
}
|
||||
|
||||
if (self.base.options.entry) |entry| {
|
||||
try argv.append("-e");
|
||||
try argv.append(entry);
|
||||
@ -1425,7 +1430,12 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
|
||||
try self.dylibs.append(self.base.allocator, dylib);
|
||||
try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id);
|
||||
|
||||
if (!(opts.is_dependent or self.referenced_dylibs.contains(dylib_id))) {
|
||||
const should_link_dylib_even_if_unreachable = blk: {
|
||||
if (self.base.options.dead_strip_dylibs) break :blk false;
|
||||
break :blk !(opts.is_dependent or self.referenced_dylibs.contains(dylib_id));
|
||||
};
|
||||
|
||||
if (should_link_dylib_even_if_unreachable) {
|
||||
try self.addLoadDylibLC(dylib_id);
|
||||
try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
|
||||
}
|
||||
|
||||
@ -2546,7 +2546,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
|
||||
// We are about to obtain this lock, so here we give other processes a chance first.
|
||||
self.base.releaseLock();
|
||||
|
||||
comptime assert(Compilation.link_hash_implementation_version == 6);
|
||||
comptime assert(Compilation.link_hash_implementation_version == 7);
|
||||
|
||||
for (self.base.options.objects) |obj| {
|
||||
_ = try man.addFile(obj.path, null);
|
||||
|
||||
@ -452,6 +452,7 @@ const usage_build_generic =
|
||||
\\ -search_dylibs_first (Darwin) search `libx.dylib` in each dir in library search paths, then `libx.a`
|
||||
\\ -headerpad [value] (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation
|
||||
\\ -headerpad_max_install_names (Darwin) set enough space as if all paths were MAXPATHLEN
|
||||
\\ -dead_strip_dylibs (Darwin) remove dylibs that are unreachable by the entry point or exported symbols
|
||||
\\ --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
|
||||
@ -703,6 +704,7 @@ fn buildOutputType(
|
||||
var search_strategy: ?link.File.MachO.SearchStrategy = null;
|
||||
var headerpad_size: ?u32 = null;
|
||||
var headerpad_max_install_names: bool = false;
|
||||
var dead_strip_dylibs: bool = false;
|
||||
|
||||
// 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
|
||||
@ -937,6 +939,8 @@ fn buildOutputType(
|
||||
};
|
||||
} else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {
|
||||
headerpad_max_install_names = true;
|
||||
} else if (mem.eql(u8, arg, "-dead_strip_dylibs")) {
|
||||
dead_strip_dylibs = true;
|
||||
} 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});
|
||||
@ -1700,6 +1704,8 @@ fn buildOutputType(
|
||||
};
|
||||
} else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {
|
||||
headerpad_max_install_names = true;
|
||||
} else if (mem.eql(u8, arg, "-dead_strip_dylibs")) {
|
||||
dead_strip_dylibs = true;
|
||||
} else if (mem.eql(u8, arg, "--gc-sections")) {
|
||||
linker_gc_sections = true;
|
||||
} else if (mem.eql(u8, arg, "--no-gc-sections")) {
|
||||
@ -2821,6 +2827,7 @@ fn buildOutputType(
|
||||
.search_strategy = search_strategy,
|
||||
.headerpad_size = headerpad_size,
|
||||
.headerpad_max_install_names = headerpad_max_install_names,
|
||||
.dead_strip_dylibs = dead_strip_dylibs,
|
||||
}) catch |err| switch (err) {
|
||||
error.LibCUnavailable => {
|
||||
const target = target_info.target;
|
||||
|
||||
@ -40,7 +40,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
|
||||
.build_modes = true,
|
||||
});
|
||||
|
||||
cases.addBuildFile("test/link/macho/frameworks/build.zig", .{
|
||||
cases.addBuildFile("test/link/macho/dead_strip_dylibs/build.zig", .{
|
||||
.build_modes = true,
|
||||
.requires_macos_sdk = true,
|
||||
});
|
||||
|
||||
46
test/link/macho/dead_strip_dylibs/build.zig
Normal file
46
test/link/macho/dead_strip_dylibs/build.zig
Normal file
@ -0,0 +1,46 @@
|
||||
const std = @import("std");
|
||||
const Builder = std.build.Builder;
|
||||
const LibExeObjectStep = std.build.LibExeObjStep;
|
||||
|
||||
pub fn build(b: *Builder) void {
|
||||
const mode = b.standardReleaseOptions();
|
||||
|
||||
const test_step = b.step("test", "Test the program");
|
||||
|
||||
{
|
||||
// Without -dead_strip_dylibs we expect `-la` to include liba.dylib in the final executable
|
||||
const exe = createScenario(b, mode);
|
||||
|
||||
const check = exe.checkObject(.macho);
|
||||
check.checkStart("cmd LOAD_DYLIB");
|
||||
check.checkNext("name {*}Cocoa");
|
||||
|
||||
check.checkStart("cmd LOAD_DYLIB");
|
||||
check.checkNext("name {*}libobjc{*}.dylib");
|
||||
|
||||
test_step.dependOn(&check.step);
|
||||
|
||||
const run_cmd = exe.run();
|
||||
test_step.dependOn(&run_cmd.step);
|
||||
}
|
||||
|
||||
{
|
||||
// With -dead_strip_dylibs, we should include liba.dylib as it's unreachable
|
||||
const exe = createScenario(b, mode);
|
||||
exe.dead_strip_dylibs = true;
|
||||
|
||||
const run_cmd = exe.run();
|
||||
run_cmd.expected_exit_code = @bitCast(u8, @as(i8, -2)); // should fail
|
||||
test_step.dependOn(&run_cmd.step);
|
||||
}
|
||||
}
|
||||
|
||||
fn createScenario(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep {
|
||||
const exe = b.addExecutable("test", null);
|
||||
b.default_step.dependOn(&exe.step);
|
||||
exe.addCSourceFile("main.c", &[0][]const u8{});
|
||||
exe.setBuildMode(mode);
|
||||
exe.linkLibC();
|
||||
exe.linkFramework("Cocoa");
|
||||
return exe;
|
||||
}
|
||||
10
test/link/macho/dead_strip_dylibs/main.c
Normal file
10
test/link/macho/dead_strip_dylibs/main.c
Normal file
@ -0,0 +1,10 @@
|
||||
#include <objc/runtime.h>
|
||||
|
||||
int main() {
|
||||
if (objc_getClass("NSObject") == 0) {
|
||||
return -1;
|
||||
}
|
||||
if (objc_getClass("NSApplication") == 0) {
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
const std = @import("std");
|
||||
const Builder = std.build.Builder;
|
||||
|
||||
pub fn build(b: *Builder) void {
|
||||
const mode = b.standardReleaseOptions();
|
||||
|
||||
const test_step = b.step("test", "Test the program");
|
||||
|
||||
const exe = b.addExecutable("test", null);
|
||||
b.default_step.dependOn(&exe.step);
|
||||
exe.addCSourceFile("main.c", &[0][]const u8{});
|
||||
exe.setBuildMode(mode);
|
||||
exe.linkLibC();
|
||||
exe.linkFramework("Cocoa");
|
||||
|
||||
const check = exe.checkObject(.macho);
|
||||
check.checkStart("cmd LOAD_DYLIB");
|
||||
check.checkNext("name {*}Cocoa");
|
||||
|
||||
switch (mode) {
|
||||
.Debug, .ReleaseSafe => {
|
||||
check.checkStart("cmd LOAD_DYLIB");
|
||||
check.checkNext("name {*}libobjc{*}.dylib");
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
test_step.dependOn(&check.step);
|
||||
|
||||
const run_cmd = exe.run();
|
||||
test_step.dependOn(&run_cmd.step);
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
#include <assert.h>
|
||||
#include <objc/runtime.h>
|
||||
|
||||
int main() {
|
||||
assert(objc_getClass("NSObject") > 0);
|
||||
assert(objc_getClass("NSApplication") > 0);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user