Skip to content

Commit d4623a8

Browse files
authored
Merge pull request #11941 from ziglang/macho-stripping-dylibs
macho: handle `-dead_strip_dylibs`, `-needed-lx` and `-needed_framework x` flags
2 parents a76775b + 0dd2892 commit d4623a8

File tree

19 files changed

+297
-79
lines changed

19 files changed

+297
-79
lines changed

lib/std/build.zig

+67-11
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ const ArrayList = std.ArrayList;
1111
const StringHashMap = std.StringHashMap;
1212
const Allocator = mem.Allocator;
1313
const process = std.process;
14-
const BufSet = std.BufSet;
1514
const EnvMap = std.process.EnvMap;
1615
const fmt_lib = std.fmt;
1716
const File = std.fs.File;
@@ -1484,7 +1483,7 @@ pub const LibExeObjStep = struct {
14841483
lib_paths: ArrayList([]const u8),
14851484
rpaths: ArrayList([]const u8),
14861485
framework_dirs: ArrayList([]const u8),
1487-
frameworks: BufSet,
1486+
frameworks: StringHashMap(bool),
14881487
verbose_link: bool,
14891488
verbose_cc: bool,
14901489
emit_analysis: EmitOption = .default,
@@ -1601,6 +1600,9 @@ pub const LibExeObjStep = struct {
16011600
/// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN.
16021601
headerpad_max_install_names: bool = false,
16031602

1603+
/// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols.
1604+
dead_strip_dylibs: bool = false,
1605+
16041606
/// Position Independent Code
16051607
force_pic: ?bool = null,
16061608

@@ -1640,6 +1642,7 @@ pub const LibExeObjStep = struct {
16401642

16411643
pub const SystemLib = struct {
16421644
name: []const u8,
1645+
needed: bool,
16431646
use_pkg_config: enum {
16441647
/// Don't use pkg-config, just pass -lfoo where foo is name.
16451648
no,
@@ -1741,7 +1744,7 @@ pub const LibExeObjStep = struct {
17411744
.kind = kind,
17421745
.root_src = root_src,
17431746
.name = name,
1744-
.frameworks = BufSet.init(builder.allocator),
1747+
.frameworks = StringHashMap(bool).init(builder.allocator),
17451748
.step = Step.init(base_id, name, builder.allocator, make),
17461749
.version = ver,
17471750
.out_filename = undefined,
@@ -1890,8 +1893,11 @@ pub const LibExeObjStep = struct {
18901893
}
18911894

18921895
pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {
1893-
// Note: No need to dupe because frameworks dupes internally.
1894-
self.frameworks.insert(framework_name) catch unreachable;
1896+
self.frameworks.put(self.builder.dupe(framework_name), false) catch unreachable;
1897+
}
1898+
1899+
pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void {
1900+
self.frameworks.put(self.builder.dupe(framework_name), true) catch unreachable;
18951901
}
18961902

18971903
/// Returns whether the library, executable, or object depends on a particular system library.
@@ -1932,6 +1938,7 @@ pub const LibExeObjStep = struct {
19321938
self.link_objects.append(.{
19331939
.system_lib = .{
19341940
.name = "c",
1941+
.needed = false,
19351942
.use_pkg_config = .no,
19361943
},
19371944
}) catch unreachable;
@@ -1944,6 +1951,7 @@ pub const LibExeObjStep = struct {
19441951
self.link_objects.append(.{
19451952
.system_lib = .{
19461953
.name = "c++",
1954+
.needed = false,
19471955
.use_pkg_config = .no,
19481956
},
19491957
}) catch unreachable;
@@ -1968,6 +1976,19 @@ pub const LibExeObjStep = struct {
19681976
self.link_objects.append(.{
19691977
.system_lib = .{
19701978
.name = self.builder.dupe(name),
1979+
.needed = false,
1980+
.use_pkg_config = .no,
1981+
},
1982+
}) catch unreachable;
1983+
}
1984+
1985+
/// This one has no integration with anything, it just puts -needed-lname on the command line.
1986+
/// Prefer to use `linkSystemLibraryNeeded` instead.
1987+
pub fn linkSystemLibraryNeededName(self: *LibExeObjStep, name: []const u8) void {
1988+
self.link_objects.append(.{
1989+
.system_lib = .{
1990+
.name = self.builder.dupe(name),
1991+
.needed = true,
19711992
.use_pkg_config = .no,
19721993
},
19731994
}) catch unreachable;
@@ -1979,6 +2000,19 @@ pub const LibExeObjStep = struct {
19792000
self.link_objects.append(.{
19802001
.system_lib = .{
19812002
.name = self.builder.dupe(lib_name),
2003+
.needed = false,
2004+
.use_pkg_config = .force,
2005+
},
2006+
}) catch unreachable;
2007+
}
2008+
2009+
/// This links against a system library, exclusively using pkg-config to find the library.
2010+
/// Prefer to use `linkSystemLibraryNeeded` instead.
2011+
pub fn linkSystemLibraryNeededPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
2012+
self.link_objects.append(.{
2013+
.system_lib = .{
2014+
.name = self.builder.dupe(lib_name),
2015+
.needed = true,
19822016
.use_pkg_config = .force,
19832017
},
19842018
}) catch unreachable;
@@ -2081,6 +2115,14 @@ pub const LibExeObjStep = struct {
20812115
}
20822116

20832117
pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {
2118+
self.linkSystemLibraryInner(name, false);
2119+
}
2120+
2121+
pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void {
2122+
self.linkSystemLibraryInner(name, true);
2123+
}
2124+
2125+
fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, needed: bool) void {
20842126
if (isLibCLibrary(name)) {
20852127
self.linkLibC();
20862128
return;
@@ -2093,6 +2135,7 @@ pub const LibExeObjStep = struct {
20932135
self.link_objects.append(.{
20942136
.system_lib = .{
20952137
.name = self.builder.dupe(name),
2138+
.needed = needed,
20962139
.use_pkg_config = .yes,
20972140
},
20982141
}) catch unreachable;
@@ -2434,7 +2477,7 @@ pub const LibExeObjStep = struct {
24342477
if (!other.isDynamicLibrary()) {
24352478
var it = other.frameworks.iterator();
24362479
while (it.next()) |framework| {
2437-
self.frameworks.insert(framework.*) catch unreachable;
2480+
self.frameworks.put(framework.key_ptr.*, framework.value_ptr.*) catch unreachable;
24382481
}
24392482
}
24402483
},
@@ -2470,8 +2513,9 @@ pub const LibExeObjStep = struct {
24702513
},
24712514

24722515
.system_lib => |system_lib| {
2516+
const prefix: []const u8 = if (system_lib.needed) "-needed-l" else "-l";
24732517
switch (system_lib.use_pkg_config) {
2474-
.no => try zig_args.append(builder.fmt("-l{s}", .{system_lib.name})),
2518+
.no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
24752519
.yes, .force => {
24762520
if (self.runPkgConfig(system_lib.name)) |args| {
24772521
try zig_args.appendSlice(args);
@@ -2485,7 +2529,10 @@ pub const LibExeObjStep = struct {
24852529
.yes => {
24862530
// pkg-config failed, so fall back to linking the library
24872531
// by name directly.
2488-
try zig_args.append(builder.fmt("-l{s}", .{system_lib.name}));
2532+
try zig_args.append(builder.fmt("{s}{s}", .{
2533+
prefix,
2534+
system_lib.name,
2535+
}));
24892536
},
24902537
.force => {
24912538
panic("pkg-config failed for library {s}", .{system_lib.name});
@@ -2676,6 +2723,9 @@ pub const LibExeObjStep = struct {
26762723
if (self.headerpad_max_install_names) {
26772724
try zig_args.append("-headerpad_max_install_names");
26782725
}
2726+
if (self.dead_strip_dylibs) {
2727+
try zig_args.append("-dead_strip_dylibs");
2728+
}
26792729

26802730
if (self.bundle_compiler_rt) |x| {
26812731
if (x) {
@@ -2966,9 +3016,15 @@ pub const LibExeObjStep = struct {
29663016
}
29673017

29683018
var it = self.frameworks.iterator();
2969-
while (it.next()) |framework| {
2970-
zig_args.append("-framework") catch unreachable;
2971-
zig_args.append(framework.*) catch unreachable;
3019+
while (it.next()) |entry| {
3020+
const name = entry.key_ptr.*;
3021+
const needed = entry.value_ptr.*;
3022+
if (needed) {
3023+
zig_args.append("-needed_framework") catch unreachable;
3024+
} else {
3025+
zig_args.append("-framework") catch unreachable;
3026+
}
3027+
zig_args.append(name) catch unreachable;
29723028
}
29733029
} else {
29743030
if (self.framework_dirs.items.len > 0) {

src/Compilation.zig

+10-6
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,7 @@ pub const InitOptions = struct {
791791
c_source_files: []const CSourceFile = &[0]CSourceFile{},
792792
link_objects: []LinkObject = &[0]LinkObject{},
793793
framework_dirs: []const []const u8 = &[0][]const u8{},
794-
frameworks: []const []const u8 = &[0][]const u8{},
794+
frameworks: std.StringArrayHashMapUnmanaged(SystemLib) = .{},
795795
system_lib_names: []const []const u8 = &.{},
796796
system_lib_infos: []const SystemLib = &.{},
797797
/// These correspond to the WASI libc emulated subcomponents including:
@@ -911,6 +911,8 @@ pub const InitOptions = struct {
911911
headerpad_size: ?u32 = null,
912912
/// (Darwin) set enough space as if all paths were MATPATHLEN
913913
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,
914916
};
915917

916918
fn addPackageTableToCacheHash(
@@ -1095,7 +1097,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
10951097
// Our linker can't handle objects or most advanced options yet.
10961098
if (options.link_objects.len != 0 or
10971099
options.c_source_files.len != 0 or
1098-
options.frameworks.len != 0 or
1100+
options.frameworks.count() != 0 or
10991101
options.system_lib_names.len != 0 or
11001102
options.link_libc or options.link_libcpp or
11011103
link_eh_frame_hdr or
@@ -1213,7 +1215,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
12131215
options.target,
12141216
options.is_native_abi,
12151217
link_libc,
1216-
options.system_lib_names.len != 0 or options.frameworks.len != 0,
1218+
options.system_lib_names.len != 0 or options.frameworks.count() != 0,
12171219
options.libc_installation,
12181220
options.native_darwin_sdk != null,
12191221
);
@@ -1754,6 +1756,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
17541756
.search_strategy = options.search_strategy,
17551757
.headerpad_size = options.headerpad_size,
17561758
.headerpad_max_install_names = options.headerpad_max_install_names,
1759+
.dead_strip_dylibs = options.dead_strip_dylibs,
17571760
});
17581761
errdefer bin_file.destroy();
17591762
comp.* = .{
@@ -2369,7 +2372,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo
23692372
/// to remind the programmer to update multiple related pieces of code that
23702373
/// are in different locations. Bump this number when adding or deleting
23712374
/// anything from the link cache manifest.
2372-
pub const link_hash_implementation_version = 6;
2375+
pub const link_hash_implementation_version = 7;
23732376

23742377
fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void {
23752378
const gpa = comp.gpa;
@@ -2379,7 +2382,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
23792382
defer arena_allocator.deinit();
23802383
const arena = arena_allocator.allocator();
23812384

2382-
comptime assert(link_hash_implementation_version == 6);
2385+
comptime assert(link_hash_implementation_version == 7);
23832386

23842387
if (comp.bin_file.options.module) |mod| {
23852388
const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{
@@ -2482,12 +2485,13 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
24822485

24832486
// Mach-O specific stuff
24842487
man.hash.addListOfBytes(comp.bin_file.options.framework_dirs);
2485-
man.hash.addListOfBytes(comp.bin_file.options.frameworks);
2488+
link.hashAddSystemLibs(&man.hash, comp.bin_file.options.frameworks);
24862489
try man.addOptionalFile(comp.bin_file.options.entitlements);
24872490
man.hash.addOptional(comp.bin_file.options.pagezero_size);
24882491
man.hash.addOptional(comp.bin_file.options.search_strategy);
24892492
man.hash.addOptional(comp.bin_file.options.headerpad_size);
24902493
man.hash.add(comp.bin_file.options.headerpad_max_install_names);
2494+
man.hash.add(comp.bin_file.options.dead_strip_dylibs);
24912495

24922496
// COFF specific stuff
24932497
man.hash.addOptional(comp.bin_file.options.subsystem);

src/link.zig

+4-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ pub const Options = struct {
162162

163163
objects: []Compilation.LinkObject,
164164
framework_dirs: []const []const u8,
165-
frameworks: []const []const u8,
165+
frameworks: std.StringArrayHashMapUnmanaged(SystemLib),
166166
system_libs: std.StringArrayHashMapUnmanaged(SystemLib),
167167
wasi_emulated_libs: []const wasi_libc.CRTFile,
168168
lib_dirs: []const []const u8,
@@ -199,6 +199,9 @@ pub const Options = struct {
199199
/// (Darwin) set enough space as if all paths were MATPATHLEN
200200
headerpad_max_install_names: bool = false,
201201

202+
/// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols
203+
dead_strip_dylibs: bool = false,
204+
202205
pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
203206
return if (options.use_lld) .Obj else options.output_mode;
204207
}

src/link/Coff.zig

+1-1
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 == 6);
972+
comptime assert(Compilation.link_hash_implementation_version == 7);
973973

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

src/link/Elf.zig

+1-1
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 == 6);
1301+
comptime assert(Compilation.link_hash_implementation_version == 7);
13021302

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

0 commit comments

Comments
 (0)