Skip to content

macho: handle -dead_strip_dylibs, -needed-lx and -needed_framework x flags #11941

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 67 additions & 11 deletions lib/std/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const ArrayList = std.ArrayList;
const StringHashMap = std.StringHashMap;
const Allocator = mem.Allocator;
const process = std.process;
const BufSet = std.BufSet;
const EnvMap = std.process.EnvMap;
const fmt_lib = std.fmt;
const File = std.fs.File;
Expand Down Expand Up @@ -1484,7 +1483,7 @@ pub const LibExeObjStep = struct {
lib_paths: ArrayList([]const u8),
rpaths: ArrayList([]const u8),
framework_dirs: ArrayList([]const u8),
frameworks: BufSet,
frameworks: StringHashMap(bool),
verbose_link: bool,
verbose_cc: bool,
emit_analysis: EmitOption = .default,
Expand Down Expand Up @@ -1601,6 +1600,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,

Expand Down Expand Up @@ -1640,6 +1642,7 @@ pub const LibExeObjStep = struct {

pub const SystemLib = struct {
name: []const u8,
needed: bool,
use_pkg_config: enum {
/// Don't use pkg-config, just pass -lfoo where foo is name.
no,
Expand Down Expand Up @@ -1741,7 +1744,7 @@ pub const LibExeObjStep = struct {
.kind = kind,
.root_src = root_src,
.name = name,
.frameworks = BufSet.init(builder.allocator),
.frameworks = StringHashMap(bool).init(builder.allocator),
.step = Step.init(base_id, name, builder.allocator, make),
.version = ver,
.out_filename = undefined,
Expand Down Expand Up @@ -1890,8 +1893,11 @@ pub const LibExeObjStep = struct {
}

pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {
// Note: No need to dupe because frameworks dupes internally.
self.frameworks.insert(framework_name) catch unreachable;
self.frameworks.put(self.builder.dupe(framework_name), false) catch unreachable;
}

pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void {
self.frameworks.put(self.builder.dupe(framework_name), true) catch unreachable;
}

/// Returns whether the library, executable, or object depends on a particular system library.
Expand Down Expand Up @@ -1932,6 +1938,7 @@ pub const LibExeObjStep = struct {
self.link_objects.append(.{
.system_lib = .{
.name = "c",
.needed = false,
.use_pkg_config = .no,
},
}) catch unreachable;
Expand All @@ -1944,6 +1951,7 @@ pub const LibExeObjStep = struct {
self.link_objects.append(.{
.system_lib = .{
.name = "c++",
.needed = false,
.use_pkg_config = .no,
},
}) catch unreachable;
Expand All @@ -1968,6 +1976,19 @@ pub const LibExeObjStep = struct {
self.link_objects.append(.{
.system_lib = .{
.name = self.builder.dupe(name),
.needed = false,
.use_pkg_config = .no,
},
}) catch unreachable;
}

/// This one has no integration with anything, it just puts -needed-lname on the command line.
/// Prefer to use `linkSystemLibraryNeeded` instead.
pub fn linkSystemLibraryNeededName(self: *LibExeObjStep, name: []const u8) void {
self.link_objects.append(.{
.system_lib = .{
.name = self.builder.dupe(name),
.needed = true,
.use_pkg_config = .no,
},
}) catch unreachable;
Expand All @@ -1979,6 +2000,19 @@ pub const LibExeObjStep = struct {
self.link_objects.append(.{
.system_lib = .{
.name = self.builder.dupe(lib_name),
.needed = false,
.use_pkg_config = .force,
},
}) catch unreachable;
}

/// This links against a system library, exclusively using pkg-config to find the library.
/// Prefer to use `linkSystemLibraryNeeded` instead.
pub fn linkSystemLibraryNeededPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
self.link_objects.append(.{
.system_lib = .{
.name = self.builder.dupe(lib_name),
.needed = true,
.use_pkg_config = .force,
},
}) catch unreachable;
Expand Down Expand Up @@ -2081,6 +2115,14 @@ pub const LibExeObjStep = struct {
}

pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {
self.linkSystemLibraryInner(name, false);
}

pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void {
self.linkSystemLibraryInner(name, true);
}

fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, needed: bool) void {
if (isLibCLibrary(name)) {
self.linkLibC();
return;
Expand All @@ -2093,6 +2135,7 @@ pub const LibExeObjStep = struct {
self.link_objects.append(.{
.system_lib = .{
.name = self.builder.dupe(name),
.needed = needed,
.use_pkg_config = .yes,
},
}) catch unreachable;
Expand Down Expand Up @@ -2434,7 +2477,7 @@ pub const LibExeObjStep = struct {
if (!other.isDynamicLibrary()) {
var it = other.frameworks.iterator();
while (it.next()) |framework| {
self.frameworks.insert(framework.*) catch unreachable;
self.frameworks.put(framework.key_ptr.*, framework.value_ptr.*) catch unreachable;
}
}
},
Expand Down Expand Up @@ -2470,8 +2513,9 @@ pub const LibExeObjStep = struct {
},

.system_lib => |system_lib| {
const prefix: []const u8 = if (system_lib.needed) "-needed-l" else "-l";
switch (system_lib.use_pkg_config) {
.no => try zig_args.append(builder.fmt("-l{s}", .{system_lib.name})),
.no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
.yes, .force => {
if (self.runPkgConfig(system_lib.name)) |args| {
try zig_args.appendSlice(args);
Expand All @@ -2485,7 +2529,10 @@ pub const LibExeObjStep = struct {
.yes => {
// pkg-config failed, so fall back to linking the library
// by name directly.
try zig_args.append(builder.fmt("-l{s}", .{system_lib.name}));
try zig_args.append(builder.fmt("{s}{s}", .{
prefix,
system_lib.name,
}));
},
.force => {
panic("pkg-config failed for library {s}", .{system_lib.name});
Expand Down Expand Up @@ -2676,6 +2723,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) {
Expand Down Expand Up @@ -2966,9 +3016,15 @@ pub const LibExeObjStep = struct {
}

var it = self.frameworks.iterator();
while (it.next()) |framework| {
zig_args.append("-framework") catch unreachable;
zig_args.append(framework.*) catch unreachable;
while (it.next()) |entry| {
const name = entry.key_ptr.*;
const needed = entry.value_ptr.*;
if (needed) {
zig_args.append("-needed_framework") catch unreachable;
} else {
zig_args.append("-framework") catch unreachable;
}
zig_args.append(name) catch unreachable;
}
} else {
if (self.framework_dirs.items.len > 0) {
Expand Down
16 changes: 10 additions & 6 deletions src/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,7 @@ pub const InitOptions = struct {
c_source_files: []const CSourceFile = &[0]CSourceFile{},
link_objects: []LinkObject = &[0]LinkObject{},
framework_dirs: []const []const u8 = &[0][]const u8{},
frameworks: []const []const u8 = &[0][]const u8{},
frameworks: std.StringArrayHashMapUnmanaged(SystemLib) = .{},
system_lib_names: []const []const u8 = &.{},
system_lib_infos: []const SystemLib = &.{},
/// These correspond to the WASI libc emulated subcomponents including:
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -1095,7 +1097,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
// Our linker can't handle objects or most advanced options yet.
if (options.link_objects.len != 0 or
options.c_source_files.len != 0 or
options.frameworks.len != 0 or
options.frameworks.count() != 0 or
options.system_lib_names.len != 0 or
options.link_libc or options.link_libcpp or
link_eh_frame_hdr or
Expand Down Expand Up @@ -1213,7 +1215,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
options.target,
options.is_native_abi,
link_libc,
options.system_lib_names.len != 0 or options.frameworks.len != 0,
options.system_lib_names.len != 0 or options.frameworks.count() != 0,
options.libc_installation,
options.native_darwin_sdk != null,
);
Expand Down Expand Up @@ -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.* = .{
Expand Down Expand Up @@ -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;
Expand All @@ -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{
Expand Down Expand Up @@ -2482,12 +2485,13 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes

// Mach-O specific stuff
man.hash.addListOfBytes(comp.bin_file.options.framework_dirs);
man.hash.addListOfBytes(comp.bin_file.options.frameworks);
link.hashAddSystemLibs(&man.hash, comp.bin_file.options.frameworks);
try man.addOptionalFile(comp.bin_file.options.entitlements);
man.hash.addOptional(comp.bin_file.options.pagezero_size);
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);
Expand Down
5 changes: 4 additions & 1 deletion src/link.zig
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ pub const Options = struct {

objects: []Compilation.LinkObject,
framework_dirs: []const []const u8,
frameworks: []const []const u8,
frameworks: std.StringArrayHashMapUnmanaged(SystemLib),
system_libs: std.StringArrayHashMapUnmanaged(SystemLib),
wasi_emulated_libs: []const wasi_libc.CRTFile,
lib_dirs: []const []const u8,
Expand Down Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion src/link/Coff.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/link/Elf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading