Skip to content

Commit

Permalink
Merge pull request ziglang#11950 from ziglang/macho-weak-libs-frameworks
Browse files Browse the repository at this point in the history
macho: fully implement `-weak-lx` and `-weak_framework x` flags
  • Loading branch information
kubkon authored Jun 28, 2022
2 parents ca3c4ff + 5834a60 commit 9e8298b
Show file tree
Hide file tree
Showing 18 changed files with 391 additions and 74 deletions.
72 changes: 61 additions & 11 deletions lib/std/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1483,7 +1483,7 @@ pub const LibExeObjStep = struct {
lib_paths: ArrayList([]const u8),
rpaths: ArrayList([]const u8),
framework_dirs: ArrayList([]const u8),
frameworks: StringHashMap(bool),
frameworks: StringHashMap(FrameworkLinkInfo),
verbose_link: bool,
verbose_cc: bool,
emit_analysis: EmitOption = .default,
Expand Down Expand Up @@ -1643,6 +1643,7 @@ pub const LibExeObjStep = struct {
pub const SystemLib = struct {
name: []const u8,
needed: bool,
weak: bool,
use_pkg_config: enum {
/// Don't use pkg-config, just pass -lfoo where foo is name.
no,
Expand All @@ -1655,6 +1656,11 @@ pub const LibExeObjStep = struct {
},
};

const FrameworkLinkInfo = struct {
needed: bool = false,
weak: bool = false,
};

pub const IncludeDir = union(enum) {
raw_path: []const u8,
raw_path_system: []const u8,
Expand Down Expand Up @@ -1744,7 +1750,7 @@ pub const LibExeObjStep = struct {
.kind = kind,
.root_src = root_src,
.name = name,
.frameworks = StringHashMap(bool).init(builder.allocator),
.frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator),
.step = Step.init(base_id, name, builder.allocator, make),
.version = ver,
.out_filename = undefined,
Expand Down Expand Up @@ -1893,11 +1899,19 @@ pub const LibExeObjStep = struct {
}

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

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

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

/// Returns whether the library, executable, or object depends on a particular system library.
Expand Down Expand Up @@ -1939,6 +1953,7 @@ pub const LibExeObjStep = struct {
.system_lib = .{
.name = "c",
.needed = false,
.weak = false,
.use_pkg_config = .no,
},
}) catch unreachable;
Expand All @@ -1952,6 +1967,7 @@ pub const LibExeObjStep = struct {
.system_lib = .{
.name = "c++",
.needed = false,
.weak = false,
.use_pkg_config = .no,
},
}) catch unreachable;
Expand All @@ -1977,6 +1993,7 @@ pub const LibExeObjStep = struct {
.system_lib = .{
.name = self.builder.dupe(name),
.needed = false,
.weak = false,
.use_pkg_config = .no,
},
}) catch unreachable;
Expand All @@ -1989,6 +2006,20 @@ pub const LibExeObjStep = struct {
.system_lib = .{
.name = self.builder.dupe(name),
.needed = true,
.weak = false,
.use_pkg_config = .no,
},
}) catch unreachable;
}

/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
/// command line. Prefer to use `linkSystemLibraryWeak` instead.
pub fn linkSystemLibraryWeakName(self: *LibExeObjStep, name: []const u8) void {
self.link_objects.append(.{
.system_lib = .{
.name = self.builder.dupe(name),
.needed = false,
.weak = true,
.use_pkg_config = .no,
},
}) catch unreachable;
Expand All @@ -2001,6 +2032,7 @@ pub const LibExeObjStep = struct {
.system_lib = .{
.name = self.builder.dupe(lib_name),
.needed = false,
.weak = false,
.use_pkg_config = .force,
},
}) catch unreachable;
Expand All @@ -2013,6 +2045,7 @@ pub const LibExeObjStep = struct {
.system_lib = .{
.name = self.builder.dupe(lib_name),
.needed = true,
.weak = false,
.use_pkg_config = .force,
},
}) catch unreachable;
Expand Down Expand Up @@ -2115,14 +2148,21 @@ pub const LibExeObjStep = struct {
}

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

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

fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, needed: bool) void {
pub fn linkSystemLibraryWeak(self: *LibExeObjStep, name: []const u8) void {
self.linkSystemLibraryInner(name, .{ .weak = true });
}

fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, opts: struct {
needed: bool = false,
weak: bool = false,
}) void {
if (isLibCLibrary(name)) {
self.linkLibC();
return;
Expand All @@ -2135,7 +2175,8 @@ pub const LibExeObjStep = struct {
self.link_objects.append(.{
.system_lib = .{
.name = self.builder.dupe(name),
.needed = needed,
.needed = opts.needed,
.weak = opts.weak,
.use_pkg_config = .yes,
},
}) catch unreachable;
Expand Down Expand Up @@ -2513,7 +2554,14 @@ pub const LibExeObjStep = struct {
},

.system_lib => |system_lib| {
const prefix: []const u8 = if (system_lib.needed) "-needed-l" else "-l";
const prefix: []const u8 = prefix: {
if (system_lib.needed) break :prefix "-needed-l";
if (system_lib.weak) {
if (self.target.isDarwin()) break :prefix "-weak-l";
warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`\n", .{});
}
break :prefix "-l";
};
switch (system_lib.use_pkg_config) {
.no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
.yes, .force => {
Expand Down Expand Up @@ -3018,9 +3066,11 @@ pub const LibExeObjStep = struct {
var it = self.frameworks.iterator();
while (it.next()) |entry| {
const name = entry.key_ptr.*;
const needed = entry.value_ptr.*;
if (needed) {
const info = entry.value_ptr.*;
if (info.needed) {
zig_args.append("-needed_framework") catch unreachable;
} else if (info.weak) {
zig_args.append("-weak_framework") catch unreachable;
} else {
zig_args.append("-framework") catch unreachable;
}
Expand Down
89 changes: 83 additions & 6 deletions lib/std/build/CheckObjectStep.zig
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const Action = struct {
fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool {
assert(act.tag == .match);

var candidate_var: ?struct { name: []const u8, value: u64 } = null;
var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " ");
var needle_it = mem.tokenize(u8, mem.trim(u8, act.phrase, " "), " ");

Expand Down Expand Up @@ -92,12 +93,19 @@ const Action = struct {
const name = needle_tok[1..closing_brace];
if (name.len == 0) return error.MissingBraceValue;
const value = try std.fmt.parseInt(u64, hay_tok, 16);
try global_vars.putNoClobber(name, value);
candidate_var = .{
.name = name,
.value = value,
};
} else {
if (!mem.eql(u8, hay_tok, needle_tok)) return false;
}
}

if (candidate_var) |v| {
try global_vars.putNoClobber(v.name, v.value);
}

return true;
}

Expand Down Expand Up @@ -332,20 +340,43 @@ const MachODumper = struct {
var output = std.ArrayList(u8).init(gpa);
const writer = output.writer();

var symtab_cmd: ?macho.symtab_command = null;
var load_commands = std.ArrayList(macho.LoadCommand).init(gpa);
try load_commands.ensureTotalCapacity(hdr.ncmds);

var sections = std.ArrayList(struct { seg: u16, sect: u16 }).init(gpa);
var imports = std.ArrayList(u16).init(gpa);

var symtab_cmd: ?u16 = null;
var i: u16 = 0;
while (i < hdr.ncmds) : (i += 1) {
var cmd = try macho.LoadCommand.read(gpa, reader);
load_commands.appendAssumeCapacity(cmd);

if (opts.dump_symtab and cmd.cmd() == .SYMTAB) {
symtab_cmd = cmd.symtab;
switch (cmd.cmd()) {
.SEGMENT_64 => {
const seg = cmd.segment;
for (seg.sections.items) |_, j| {
try sections.append(.{ .seg = i, .sect = @intCast(u16, j) });
}
},
.SYMTAB => {
symtab_cmd = i;
},
.LOAD_DYLIB,
.LOAD_WEAK_DYLIB,
.REEXPORT_DYLIB,
=> {
try imports.append(i);
},
else => {},
}

try dumpLoadCommand(cmd, i, writer);
try writer.writeByte('\n');
}

if (symtab_cmd) |cmd| {
if (opts.dump_symtab) {
const cmd = load_commands.items[symtab_cmd.?].symtab;
try writer.writeAll(symtab_label ++ "\n");
const strtab = bytes[cmd.stroff..][0..cmd.strsize];
const raw_symtab = bytes[cmd.symoff..][0 .. cmd.nsyms * @sizeOf(macho.nlist_64)];
Expand All @@ -354,7 +385,51 @@ const MachODumper = struct {
for (symtab) |sym| {
if (sym.stab()) continue;
const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
try writer.print("{s} {x}\n", .{ sym_name, sym.n_value });
if (sym.sect()) {
const map = sections.items[sym.n_sect - 1];
const seg = load_commands.items[map.seg].segment;
const sect = seg.sections.items[map.sect];
try writer.print("{x} ({s},{s})", .{
sym.n_value,
sect.segName(),
sect.sectName(),
});
if (sym.ext()) {
try writer.writeAll(" external");
}
try writer.print(" {s}\n", .{sym_name});
} else if (sym.undf()) {
const ordinal = @divTrunc(@bitCast(i16, sym.n_desc), macho.N_SYMBOL_RESOLVER);
const import_name = blk: {
if (ordinal <= 0) {
if (ordinal == macho.BIND_SPECIAL_DYLIB_SELF)
break :blk "self import";
if (ordinal == macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE)
break :blk "main executable";
if (ordinal == macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP)
break :blk "flat lookup";
unreachable;
}
const import_id = imports.items[@bitCast(u16, ordinal) - 1];
const import = load_commands.items[import_id].dylib;
const full_path = mem.sliceTo(import.data, 0);
const basename = fs.path.basename(full_path);
assert(basename.len > 0);
const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;
break :blk basename[0..ext];
};
try writer.writeAll("(undefined)");
if (sym.weakRef()) {
try writer.writeAll(" weak");
}
if (sym.ext()) {
try writer.writeAll(" external");
}
try writer.print(" {s} (from {s})\n", .{
sym_name,
import_name,
});
} else unreachable;
}
}

Expand Down Expand Up @@ -408,6 +483,8 @@ const MachODumper = struct {

.ID_DYLIB,
.LOAD_DYLIB,
.LOAD_WEAK_DYLIB,
.REEXPORT_DYLIB,
=> {
const dylib = lc.dylib.inner.dylib;
try writer.writeByte('\n');
Expand Down
4 changes: 3 additions & 1 deletion lib/std/macho.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2085,19 +2085,21 @@ pub fn GenericCommandWithData(comptime Cmd: type) type {

pub fn createLoadDylibCommand(
allocator: Allocator,
cmd_id: LC,
name: []const u8,
timestamp: u32,
current_version: u32,
compatibility_version: u32,
) !GenericCommandWithData(dylib_command) {
assert(cmd_id == .LOAD_DYLIB or cmd_id == .LOAD_WEAK_DYLIB or cmd_id == .REEXPORT_DYLIB or cmd_id == .ID_DYLIB);
const cmdsize = @intCast(u32, mem.alignForwardGeneric(
u64,
@sizeOf(dylib_command) + name.len + 1, // +1 for nul
@sizeOf(u64),
));

var dylib_cmd = emptyGenericCommandWithData(dylib_command{
.cmd = .LOAD_DYLIB,
.cmd = cmd_id,
.cmdsize = cmdsize,
.dylib = .{
.name = @sizeOf(dylib_command),
Expand Down
1 change: 1 addition & 0 deletions src/link.zig
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const TypedValue = @import("TypedValue.zig");

pub const SystemLib = struct {
needed: bool = false,
weak: bool = false,
};

pub const CacheMode = enum { incremental, whole };
Expand Down
Loading

0 comments on commit 9e8298b

Please sign in to comment.