authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-06-28 20:29:20+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-06-28 20:29:20+02:00
log9e8298b864e076221ca9c487412209d8a08c43b2
tree50eca29c2dd63c025823f818e882631c94cf8053
parentca3c4ff2d0afcdc8fe86e7e7b41a967c88779729
parent5834a608fc629319772b1623a31b62dd49ac6d63
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11950 from ziglang/macho-weak-libs-frameworks

macho: fully implement `-weak-lx` and `-weak_framework x` flags

21 files changed, 430 insertions(+), 113 deletions(-)

lib/std/build.zig+61-11
...@@ -1483,7 +1483,7 @@ pub const LibExeObjStep = struct {...@@ -1483,7 +1483,7 @@ pub const LibExeObjStep = struct {
1483 lib_paths: ArrayList([]const u8),1483 lib_paths: ArrayList([]const u8),
1484 rpaths: ArrayList([]const u8),1484 rpaths: ArrayList([]const u8),
1485 framework_dirs: ArrayList([]const u8),1485 framework_dirs: ArrayList([]const u8),
1486 frameworks: StringHashMap(bool),1486 frameworks: StringHashMap(FrameworkLinkInfo),
1487 verbose_link: bool,1487 verbose_link: bool,
1488 verbose_cc: bool,1488 verbose_cc: bool,
1489 emit_analysis: EmitOption = .default,1489 emit_analysis: EmitOption = .default,
...@@ -1643,6 +1643,7 @@ pub const LibExeObjStep = struct {...@@ -1643,6 +1643,7 @@ pub const LibExeObjStep = struct {
1643 pub const SystemLib = struct {1643 pub const SystemLib = struct {
1644 name: []const u8,1644 name: []const u8,
1645 needed: bool,1645 needed: bool,
1646 weak: bool,
1646 use_pkg_config: enum {1647 use_pkg_config: enum {
1647 /// Don't use pkg-config, just pass -lfoo where foo is name.1648 /// Don't use pkg-config, just pass -lfoo where foo is name.
1648 no,1649 no,
...@@ -1655,6 +1656,11 @@ pub const LibExeObjStep = struct {...@@ -1655,6 +1656,11 @@ pub const LibExeObjStep = struct {
1655 },1656 },
1656 };1657 };
16571658
1659 const FrameworkLinkInfo = struct {
1660 needed: bool = false,
1661 weak: bool = false,
1662 };
1663
1658 pub const IncludeDir = union(enum) {1664 pub const IncludeDir = union(enum) {
1659 raw_path: []const u8,1665 raw_path: []const u8,
1660 raw_path_system: []const u8,1666 raw_path_system: []const u8,
...@@ -1744,7 +1750,7 @@ pub const LibExeObjStep = struct {...@@ -1744,7 +1750,7 @@ pub const LibExeObjStep = struct {
1744 .kind = kind,1750 .kind = kind,
1745 .root_src = root_src,1751 .root_src = root_src,
1746 .name = name,1752 .name = name,
1747 .frameworks = StringHashMap(bool).init(builder.allocator),1753 .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator),
1748 .step = Step.init(base_id, name, builder.allocator, make),1754 .step = Step.init(base_id, name, builder.allocator, make),
1749 .version = ver,1755 .version = ver,
1750 .out_filename = undefined,1756 .out_filename = undefined,
...@@ -1893,11 +1899,19 @@ pub const LibExeObjStep = struct {...@@ -1893,11 +1899,19 @@ pub const LibExeObjStep = struct {
1893 }1899 }
18941900
1895 pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {1901 pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {
1896 self.frameworks.put(self.builder.dupe(framework_name), false) catch unreachable;1902 self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable;
1897 }1903 }
18981904
1899 pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void {1905 pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void {
1900 self.frameworks.put(self.builder.dupe(framework_name), true) catch unreachable;1906 self.frameworks.put(self.builder.dupe(framework_name), .{
1907 .needed = true,
1908 }) catch unreachable;
1909 }
1910
1911 pub fn linkFrameworkWeak(self: *LibExeObjStep, framework_name: []const u8) void {
1912 self.frameworks.put(self.builder.dupe(framework_name), .{
1913 .weak = true,
1914 }) catch unreachable;
1901 }1915 }
19021916
1903 /// Returns whether the library, executable, or object depends on a particular system library.1917 /// Returns whether the library, executable, or object depends on a particular system library.
...@@ -1939,6 +1953,7 @@ pub const LibExeObjStep = struct {...@@ -1939,6 +1953,7 @@ pub const LibExeObjStep = struct {
1939 .system_lib = .{1953 .system_lib = .{
1940 .name = "c",1954 .name = "c",
1941 .needed = false,1955 .needed = false,
1956 .weak = false,
1942 .use_pkg_config = .no,1957 .use_pkg_config = .no,
1943 },1958 },
1944 }) catch unreachable;1959 }) catch unreachable;
...@@ -1952,6 +1967,7 @@ pub const LibExeObjStep = struct {...@@ -1952,6 +1967,7 @@ pub const LibExeObjStep = struct {
1952 .system_lib = .{1967 .system_lib = .{
1953 .name = "c++",1968 .name = "c++",
1954 .needed = false,1969 .needed = false,
1970 .weak = false,
1955 .use_pkg_config = .no,1971 .use_pkg_config = .no,
1956 },1972 },
1957 }) catch unreachable;1973 }) catch unreachable;
...@@ -1977,6 +1993,7 @@ pub const LibExeObjStep = struct {...@@ -1977,6 +1993,7 @@ pub const LibExeObjStep = struct {
1977 .system_lib = .{1993 .system_lib = .{
1978 .name = self.builder.dupe(name),1994 .name = self.builder.dupe(name),
1979 .needed = false,1995 .needed = false,
1996 .weak = false,
1980 .use_pkg_config = .no,1997 .use_pkg_config = .no,
1981 },1998 },
1982 }) catch unreachable;1999 }) catch unreachable;
...@@ -1989,6 +2006,20 @@ pub const LibExeObjStep = struct {...@@ -1989,6 +2006,20 @@ pub const LibExeObjStep = struct {
1989 .system_lib = .{2006 .system_lib = .{
1990 .name = self.builder.dupe(name),2007 .name = self.builder.dupe(name),
1991 .needed = true,2008 .needed = true,
2009 .weak = false,
2010 .use_pkg_config = .no,
2011 },
2012 }) catch unreachable;
2013 }
2014
2015 /// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
2016 /// command line. Prefer to use `linkSystemLibraryWeak` instead.
2017 pub fn linkSystemLibraryWeakName(self: *LibExeObjStep, name: []const u8) void {
2018 self.link_objects.append(.{
2019 .system_lib = .{
2020 .name = self.builder.dupe(name),
2021 .needed = false,
2022 .weak = true,
1992 .use_pkg_config = .no,2023 .use_pkg_config = .no,
1993 },2024 },
1994 }) catch unreachable;2025 }) catch unreachable;
...@@ -2001,6 +2032,7 @@ pub const LibExeObjStep = struct {...@@ -2001,6 +2032,7 @@ pub const LibExeObjStep = struct {
2001 .system_lib = .{2032 .system_lib = .{
2002 .name = self.builder.dupe(lib_name),2033 .name = self.builder.dupe(lib_name),
2003 .needed = false,2034 .needed = false,
2035 .weak = false,
2004 .use_pkg_config = .force,2036 .use_pkg_config = .force,
2005 },2037 },
2006 }) catch unreachable;2038 }) catch unreachable;
...@@ -2013,6 +2045,7 @@ pub const LibExeObjStep = struct {...@@ -2013,6 +2045,7 @@ pub const LibExeObjStep = struct {
2013 .system_lib = .{2045 .system_lib = .{
2014 .name = self.builder.dupe(lib_name),2046 .name = self.builder.dupe(lib_name),
2015 .needed = true,2047 .needed = true,
2048 .weak = false,
2016 .use_pkg_config = .force,2049 .use_pkg_config = .force,
2017 },2050 },
2018 }) catch unreachable;2051 }) catch unreachable;
...@@ -2115,14 +2148,21 @@ pub const LibExeObjStep = struct {...@@ -2115,14 +2148,21 @@ pub const LibExeObjStep = struct {
2115 }2148 }
21162149
2117 pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {2150 pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {
2118 self.linkSystemLibraryInner(name, false);2151 self.linkSystemLibraryInner(name, .{});
2119 }2152 }
21202153
2121 pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void {2154 pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void {
2122 self.linkSystemLibraryInner(name, true);2155 self.linkSystemLibraryInner(name, .{ .needed = true });
2123 }2156 }
21242157
2125 fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, needed: bool) void {2158 pub fn linkSystemLibraryWeak(self: *LibExeObjStep, name: []const u8) void {
2159 self.linkSystemLibraryInner(name, .{ .weak = true });
2160 }
2161
2162 fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, opts: struct {
2163 needed: bool = false,
2164 weak: bool = false,
2165 }) void {
2126 if (isLibCLibrary(name)) {2166 if (isLibCLibrary(name)) {
2127 self.linkLibC();2167 self.linkLibC();
2128 return;2168 return;
...@@ -2135,7 +2175,8 @@ pub const LibExeObjStep = struct {...@@ -2135,7 +2175,8 @@ pub const LibExeObjStep = struct {
2135 self.link_objects.append(.{2175 self.link_objects.append(.{
2136 .system_lib = .{2176 .system_lib = .{
2137 .name = self.builder.dupe(name),2177 .name = self.builder.dupe(name),
2138 .needed = needed,2178 .needed = opts.needed,
2179 .weak = opts.weak,
2139 .use_pkg_config = .yes,2180 .use_pkg_config = .yes,
2140 },2181 },
2141 }) catch unreachable;2182 }) catch unreachable;
...@@ -2513,7 +2554,14 @@ pub const LibExeObjStep = struct {...@@ -2513,7 +2554,14 @@ pub const LibExeObjStep = struct {
2513 },2554 },
25142555
2515 .system_lib => |system_lib| {2556 .system_lib => |system_lib| {
2516 const prefix: []const u8 = if (system_lib.needed) "-needed-l" else "-l";2557 const prefix: []const u8 = prefix: {
2558 if (system_lib.needed) break :prefix "-needed-l";
2559 if (system_lib.weak) {
2560 if (self.target.isDarwin()) break :prefix "-weak-l";
2561 warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`\n", .{});
2562 }
2563 break :prefix "-l";
2564 };
2517 switch (system_lib.use_pkg_config) {2565 switch (system_lib.use_pkg_config) {
2518 .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),2566 .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
2519 .yes, .force => {2567 .yes, .force => {
...@@ -3018,9 +3066,11 @@ pub const LibExeObjStep = struct {...@@ -3018,9 +3066,11 @@ pub const LibExeObjStep = struct {
3018 var it = self.frameworks.iterator();3066 var it = self.frameworks.iterator();
3019 while (it.next()) |entry| {3067 while (it.next()) |entry| {
3020 const name = entry.key_ptr.*;3068 const name = entry.key_ptr.*;
3021 const needed = entry.value_ptr.*;3069 const info = entry.value_ptr.*;
3022 if (needed) {3070 if (info.needed) {
3023 zig_args.append("-needed_framework") catch unreachable;3071 zig_args.append("-needed_framework") catch unreachable;
3072 } else if (info.weak) {
3073 zig_args.append("-weak_framework") catch unreachable;
3024 } else {3074 } else {
3025 zig_args.append("-framework") catch unreachable;3075 zig_args.append("-framework") catch unreachable;
3026 }3076 }
lib/std/build/CheckObjectStep.zig+83-6
...@@ -65,6 +65,7 @@ const Action = struct {...@@ -65,6 +65,7 @@ const Action = struct {
65 fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool {65 fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool {
66 assert(act.tag == .match);66 assert(act.tag == .match);
6767
68 var candidate_var: ?struct { name: []const u8, value: u64 } = null;
68 var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " ");69 var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " ");
69 var needle_it = mem.tokenize(u8, mem.trim(u8, act.phrase, " "), " ");70 var needle_it = mem.tokenize(u8, mem.trim(u8, act.phrase, " "), " ");
7071
...@@ -92,12 +93,19 @@ const Action = struct {...@@ -92,12 +93,19 @@ const Action = struct {
92 const name = needle_tok[1..closing_brace];93 const name = needle_tok[1..closing_brace];
93 if (name.len == 0) return error.MissingBraceValue;94 if (name.len == 0) return error.MissingBraceValue;
94 const value = try std.fmt.parseInt(u64, hay_tok, 16);95 const value = try std.fmt.parseInt(u64, hay_tok, 16);
95 try global_vars.putNoClobber(name, value);96 candidate_var = .{
97 .name = name,
98 .value = value,
99 };
96 } else {100 } else {
97 if (!mem.eql(u8, hay_tok, needle_tok)) return false;101 if (!mem.eql(u8, hay_tok, needle_tok)) return false;
98 }102 }
99 }103 }
100104
105 if (candidate_var) |v| {
106 try global_vars.putNoClobber(v.name, v.value);
107 }
108
101 return true;109 return true;
102 }110 }
103111
...@@ -332,20 +340,43 @@ const MachODumper = struct {...@@ -332,20 +340,43 @@ const MachODumper = struct {
332 var output = std.ArrayList(u8).init(gpa);340 var output = std.ArrayList(u8).init(gpa);
333 const writer = output.writer();341 const writer = output.writer();
334342
335 var symtab_cmd: ?macho.symtab_command = null;343 var load_commands = std.ArrayList(macho.LoadCommand).init(gpa);
344 try load_commands.ensureTotalCapacity(hdr.ncmds);
345
346 var sections = std.ArrayList(struct { seg: u16, sect: u16 }).init(gpa);
347 var imports = std.ArrayList(u16).init(gpa);
348
349 var symtab_cmd: ?u16 = null;
336 var i: u16 = 0;350 var i: u16 = 0;
337 while (i < hdr.ncmds) : (i += 1) {351 while (i < hdr.ncmds) : (i += 1) {
338 var cmd = try macho.LoadCommand.read(gpa, reader);352 var cmd = try macho.LoadCommand.read(gpa, reader);
353 load_commands.appendAssumeCapacity(cmd);
339354
340 if (opts.dump_symtab and cmd.cmd() == .SYMTAB) {355 switch (cmd.cmd()) {
341 symtab_cmd = cmd.symtab;356 .SEGMENT_64 => {
357 const seg = cmd.segment;
358 for (seg.sections.items) |_, j| {
359 try sections.append(.{ .seg = i, .sect = @intCast(u16, j) });
360 }
361 },
362 .SYMTAB => {
363 symtab_cmd = i;
364 },
365 .LOAD_DYLIB,
366 .LOAD_WEAK_DYLIB,
367 .REEXPORT_DYLIB,
368 => {
369 try imports.append(i);
370 },
371 else => {},
342 }372 }
343373
344 try dumpLoadCommand(cmd, i, writer);374 try dumpLoadCommand(cmd, i, writer);
345 try writer.writeByte('\n');375 try writer.writeByte('\n');
346 }376 }
347377
348 if (symtab_cmd) |cmd| {378 if (opts.dump_symtab) {
379 const cmd = load_commands.items[symtab_cmd.?].symtab;
349 try writer.writeAll(symtab_label ++ "\n");380 try writer.writeAll(symtab_label ++ "\n");
350 const strtab = bytes[cmd.stroff..][0..cmd.strsize];381 const strtab = bytes[cmd.stroff..][0..cmd.strsize];
351 const raw_symtab = bytes[cmd.symoff..][0 .. cmd.nsyms * @sizeOf(macho.nlist_64)];382 const raw_symtab = bytes[cmd.symoff..][0 .. cmd.nsyms * @sizeOf(macho.nlist_64)];
...@@ -354,7 +385,51 @@ const MachODumper = struct {...@@ -354,7 +385,51 @@ const MachODumper = struct {
354 for (symtab) |sym| {385 for (symtab) |sym| {
355 if (sym.stab()) continue;386 if (sym.stab()) continue;
356 const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);387 const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
357 try writer.print("{s} {x}\n", .{ sym_name, sym.n_value });388 if (sym.sect()) {
389 const map = sections.items[sym.n_sect - 1];
390 const seg = load_commands.items[map.seg].segment;
391 const sect = seg.sections.items[map.sect];
392 try writer.print("{x} ({s},{s})", .{
393 sym.n_value,
394 sect.segName(),
395 sect.sectName(),
396 });
397 if (sym.ext()) {
398 try writer.writeAll(" external");
399 }
400 try writer.print(" {s}\n", .{sym_name});
401 } else if (sym.undf()) {
402 const ordinal = @divTrunc(@bitCast(i16, sym.n_desc), macho.N_SYMBOL_RESOLVER);
403 const import_name = blk: {
404 if (ordinal <= 0) {
405 if (ordinal == macho.BIND_SPECIAL_DYLIB_SELF)
406 break :blk "self import";
407 if (ordinal == macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE)
408 break :blk "main executable";
409 if (ordinal == macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP)
410 break :blk "flat lookup";
411 unreachable;
412 }
413 const import_id = imports.items[@bitCast(u16, ordinal) - 1];
414 const import = load_commands.items[import_id].dylib;
415 const full_path = mem.sliceTo(import.data, 0);
416 const basename = fs.path.basename(full_path);
417 assert(basename.len > 0);
418 const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;
419 break :blk basename[0..ext];
420 };
421 try writer.writeAll("(undefined)");
422 if (sym.weakRef()) {
423 try writer.writeAll(" weak");
424 }
425 if (sym.ext()) {
426 try writer.writeAll(" external");
427 }
428 try writer.print(" {s} (from {s})\n", .{
429 sym_name,
430 import_name,
431 });
432 } else unreachable;
358 }433 }
359 }434 }
360435
...@@ -408,6 +483,8 @@ const MachODumper = struct {...@@ -408,6 +483,8 @@ const MachODumper = struct {
408483
409 .ID_DYLIB,484 .ID_DYLIB,
410 .LOAD_DYLIB,485 .LOAD_DYLIB,
486 .LOAD_WEAK_DYLIB,
487 .REEXPORT_DYLIB,
411 => {488 => {
412 const dylib = lc.dylib.inner.dylib;489 const dylib = lc.dylib.inner.dylib;
413 try writer.writeByte('\n');490 try writer.writeByte('\n');
lib/std/macho.zig+3-1
...@@ -2085,11 +2085,13 @@ pub fn GenericCommandWithData(comptime Cmd: type) type {...@@ -2085,11 +2085,13 @@ pub fn GenericCommandWithData(comptime Cmd: type) type {
20852085
2086pub fn createLoadDylibCommand(2086pub fn createLoadDylibCommand(
2087 allocator: Allocator,2087 allocator: Allocator,
2088 cmd_id: LC,
2088 name: []const u8,2089 name: []const u8,
2089 timestamp: u32,2090 timestamp: u32,
2090 current_version: u32,2091 current_version: u32,
2091 compatibility_version: u32,2092 compatibility_version: u32,
2092) !GenericCommandWithData(dylib_command) {2093) !GenericCommandWithData(dylib_command) {
2094 assert(cmd_id == .LOAD_DYLIB or cmd_id == .LOAD_WEAK_DYLIB or cmd_id == .REEXPORT_DYLIB or cmd_id == .ID_DYLIB);
2093 const cmdsize = @intCast(u32, mem.alignForwardGeneric(2095 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2094 u64,2096 u64,
2095 @sizeOf(dylib_command) + name.len + 1, // +1 for nul2097 @sizeOf(dylib_command) + name.len + 1, // +1 for nul
...@@ -2097,7 +2099,7 @@ pub fn createLoadDylibCommand(...@@ -2097,7 +2099,7 @@ pub fn createLoadDylibCommand(
2097 ));2099 ));
20982100
2099 var dylib_cmd = emptyGenericCommandWithData(dylib_command{2101 var dylib_cmd = emptyGenericCommandWithData(dylib_command{
2100 .cmd = .LOAD_DYLIB,2102 .cmd = cmd_id,
2101 .cmdsize = cmdsize,2103 .cmdsize = cmdsize,
2102 .dylib = .{2104 .dylib = .{
2103 .name = @sizeOf(dylib_command),2105 .name = @sizeOf(dylib_command),
src/link.zig+1
...@@ -21,6 +21,7 @@ const TypedValue = @import("TypedValue.zig");...@@ -21,6 +21,7 @@ const TypedValue = @import("TypedValue.zig");
2121
22pub const SystemLib = struct {22pub const SystemLib = struct {
23 needed: bool = false,23 needed: bool = false,
24 weak: bool = false,
24};25};
2526
26pub const CacheMode = enum { incremental, whole };27pub const CacheMode = enum { incremental, whole };
src/link/MachO.zig+91-40
...@@ -52,6 +52,11 @@ pub const SearchStrategy = enum {...@@ -52,6 +52,11 @@ pub const SearchStrategy = enum {
52 dylibs_first,52 dylibs_first,
53};53};
5454
55const SystemLib = struct {
56 needed: bool = false,
57 weak: bool = false,
58};
59
55base: File,60base: File,
5661
57/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.62/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
...@@ -768,7 +773,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -768,7 +773,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
768 }773 }
769774
770 // Shared and static libraries passed via `-l` flag.775 // Shared and static libraries passed via `-l` flag.
771 var candidate_libs = std.StringArrayHashMap(Compilation.SystemLib).init(arena);776 var candidate_libs = std.StringArrayHashMap(SystemLib).init(arena);
772777
773 const system_lib_names = self.base.options.system_libs.keys();778 const system_lib_names = self.base.options.system_libs.keys();
774 for (system_lib_names) |system_lib_name| {779 for (system_lib_names) |system_lib_name| {
...@@ -781,7 +786,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -781,7 +786,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
781 }786 }
782787
783 const system_lib_info = self.base.options.system_libs.get(system_lib_name).?;788 const system_lib_info = self.base.options.system_libs.get(system_lib_name).?;
784 try candidate_libs.put(system_lib_name, system_lib_info);789 try candidate_libs.put(system_lib_name, .{
790 .needed = system_lib_info.needed,
791 .weak = system_lib_info.weak,
792 });
785 }793 }
786794
787 var lib_dirs = std.ArrayList([]const u8).init(arena);795 var lib_dirs = std.ArrayList([]const u8).init(arena);
...@@ -793,7 +801,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -793,7 +801,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
793 }801 }
794 }802 }
795803
796 var libs = std.StringArrayHashMap(Compilation.SystemLib).init(arena);804 var libs = std.StringArrayHashMap(SystemLib).init(arena);
797805
798 // Assume ld64 default -search_paths_first if no strategy specified.806 // Assume ld64 default -search_paths_first if no strategy specified.
799 const search_strategy = self.base.options.search_strategy orelse .paths_first;807 const search_strategy = self.base.options.search_strategy orelse .paths_first;
...@@ -890,7 +898,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -890,7 +898,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
890 for (framework_dirs.items) |dir| {898 for (framework_dirs.items) |dir| {
891 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {899 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
892 if (try resolveFramework(arena, dir, f_name, ext)) |full_path| {900 if (try resolveFramework(arena, dir, f_name, ext)) |full_path| {
893 try libs.put(full_path, self.base.options.frameworks.get(f_name).?);901 const info = self.base.options.frameworks.get(f_name).?;
902 try libs.put(full_path, .{
903 .needed = info.needed,
904 .weak = info.weak,
905 });
894 continue :outer;906 continue :outer;
895 }907 }
896 }908 }
...@@ -1026,9 +1038,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1026,9 +1038,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1026 try argv.append("-lc");1038 try argv.append("-lc");
10271039
1028 for (self.base.options.system_libs.keys()) |l_name| {1040 for (self.base.options.system_libs.keys()) |l_name| {
1029 const needed = self.base.options.system_libs.get(l_name).?.needed;1041 const info = self.base.options.system_libs.get(l_name).?;
1030 const arg = if (needed)1042 const arg = if (info.needed)
1031 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})1043 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})
1044 else if (info.weak)
1045 try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})
1032 else1046 else
1033 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});1047 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
1034 try argv.append(arg);1048 try argv.append(arg);
...@@ -1039,9 +1053,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1039,9 +1053,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1039 }1053 }
10401054
1041 for (self.base.options.frameworks.keys()) |framework| {1055 for (self.base.options.frameworks.keys()) |framework| {
1042 const needed = self.base.options.frameworks.get(framework).?.needed;1056 const info = self.base.options.frameworks.get(framework).?;
1043 const arg = if (needed)1057 const arg = if (info.needed)
1044 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework})1058 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework})
1059 else if (info.weak)
1060 try std.fmt.allocPrint(arena, "-weak_framework {s}", .{framework})
1045 else1061 else
1046 try std.fmt.allocPrint(arena, "-framework {s}", .{framework});1062 try std.fmt.allocPrint(arena, "-framework {s}", .{framework});
1047 try argv.append(arg);1063 try argv.append(arg);
...@@ -1063,7 +1079,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1063,7 +1079,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1063 Compilation.dump_argv(argv.items);1079 Compilation.dump_argv(argv.items);
1064 }1080 }
10651081
1066 var dependent_libs = std.fifo.LinearFifo(Dylib.Id, .Dynamic).init(self.base.allocator);1082 var dependent_libs = std.fifo.LinearFifo(struct {
1083 id: Dylib.Id,
1084 parent: u16,
1085 }, .Dynamic).init(self.base.allocator);
1067 defer dependent_libs.deinit();1086 defer dependent_libs.deinit();
1068 try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);1087 try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);
1069 try self.parseAndForceLoadStaticArchives(must_link_archives.keys());1088 try self.parseAndForceLoadStaticArchives(must_link_archives.keys());
...@@ -1389,13 +1408,18 @@ const ParseDylibError = error{...@@ -1389,13 +1408,18 @@ const ParseDylibError = error{
13891408
1390const DylibCreateOpts = struct {1409const DylibCreateOpts = struct {
1391 syslibroot: ?[]const u8,1410 syslibroot: ?[]const u8,
1392 dependent_libs: *std.fifo.LinearFifo(Dylib.Id, .Dynamic),
1393 id: ?Dylib.Id = null,1411 id: ?Dylib.Id = null,
1394 is_dependent: bool = false,1412 dependent: bool = false,
1395 is_needed: bool = false,1413 needed: bool = false,
1414 weak: bool = false,
1396};1415};
13971416
1398pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDylibError!bool {1417pub fn parseDylib(
1418 self: *MachO,
1419 path: []const u8,
1420 dependent_libs: anytype,
1421 opts: DylibCreateOpts,
1422) ParseDylibError!bool {
1399 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {1423 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
1400 error.FileNotFound => return false,1424 error.FileNotFound => return false,
1401 else => |e| return e,1425 else => |e| return e,
...@@ -1405,12 +1429,19 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy...@@ -1405,12 +1429,19 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
1405 const name = try self.base.allocator.dupe(u8, path);1429 const name = try self.base.allocator.dupe(u8, path);
1406 errdefer self.base.allocator.free(name);1430 errdefer self.base.allocator.free(name);
14071431
1432 const dylib_id = @intCast(u16, self.dylibs.items.len);
1408 var dylib = Dylib{1433 var dylib = Dylib{
1409 .name = name,1434 .name = name,
1410 .file = file,1435 .file = file,
1436 .weak = opts.weak,
1411 };1437 };
14121438
1413 dylib.parse(self.base.allocator, self.base.options.target, opts.dependent_libs) catch |err| switch (err) {1439 dylib.parse(
1440 self.base.allocator,
1441 self.base.options.target,
1442 dylib_id,
1443 dependent_libs,
1444 ) catch |err| switch (err) {
1414 error.EndOfStream, error.NotDylib => {1445 error.EndOfStream, error.NotDylib => {
1415 try file.seekTo(0);1446 try file.seekTo(0);
14161447
...@@ -1420,7 +1451,13 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy...@@ -1420,7 +1451,13 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
1420 };1451 };
1421 defer lib_stub.deinit();1452 defer lib_stub.deinit();
14221453
1423 try dylib.parseFromStub(self.base.allocator, self.base.options.target, lib_stub, opts.dependent_libs);1454 try dylib.parseFromStub(
1455 self.base.allocator,
1456 self.base.options.target,
1457 lib_stub,
1458 dylib_id,
1459 dependent_libs,
1460 );
1424 },1461 },
1425 else => |e| return e,1462 else => |e| return e,
1426 };1463 };
...@@ -1438,13 +1475,12 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy...@@ -1438,13 +1475,12 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
1438 }1475 }
1439 }1476 }
14401477
1441 const dylib_id = @intCast(u16, self.dylibs.items.len);
1442 try self.dylibs.append(self.base.allocator, dylib);1478 try self.dylibs.append(self.base.allocator, dylib);
1443 try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id);1479 try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id);
14441480
1445 const should_link_dylib_even_if_unreachable = blk: {1481 const should_link_dylib_even_if_unreachable = blk: {
1446 if (self.base.options.dead_strip_dylibs and !opts.is_needed) break :blk false;1482 if (self.base.options.dead_strip_dylibs and !opts.needed) break :blk false;
1447 break :blk !(opts.is_dependent or self.referenced_dylibs.contains(dylib_id));1483 break :blk !(opts.dependent or self.referenced_dylibs.contains(dylib_id));
1448 };1484 };
14491485
1450 if (should_link_dylib_even_if_unreachable) {1486 if (should_link_dylib_even_if_unreachable) {
...@@ -1467,9 +1503,8 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const...@@ -1467,9 +1503,8 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
14671503
1468 if (try self.parseObject(full_path)) continue;1504 if (try self.parseObject(full_path)) continue;
1469 if (try self.parseArchive(full_path, false)) continue;1505 if (try self.parseArchive(full_path, false)) continue;
1470 if (try self.parseDylib(full_path, .{1506 if (try self.parseDylib(full_path, dependent_libs, .{
1471 .syslibroot = syslibroot,1507 .syslibroot = syslibroot,
1472 .dependent_libs = dependent_libs,
1473 })) continue;1508 })) continue;
14741509
1475 log.warn("unknown filetype for positional input file: '{s}'", .{file_name});1510 log.warn("unknown filetype for positional input file: '{s}'", .{file_name});
...@@ -1494,17 +1529,17 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi...@@ -1494,17 +1529,17 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi
1494fn parseLibs(1529fn parseLibs(
1495 self: *MachO,1530 self: *MachO,
1496 lib_names: []const []const u8,1531 lib_names: []const []const u8,
1497 lib_infos: []const Compilation.SystemLib,1532 lib_infos: []const SystemLib,
1498 syslibroot: ?[]const u8,1533 syslibroot: ?[]const u8,
1499 dependent_libs: anytype,1534 dependent_libs: anytype,
1500) !void {1535) !void {
1501 for (lib_names) |lib, i| {1536 for (lib_names) |lib, i| {
1502 const lib_info = lib_infos[i];1537 const lib_info = lib_infos[i];
1503 log.debug("parsing lib path '{s}'", .{lib});1538 log.debug("parsing lib path '{s}'", .{lib});
1504 if (try self.parseDylib(lib, .{1539 if (try self.parseDylib(lib, dependent_libs, .{
1505 .syslibroot = syslibroot,1540 .syslibroot = syslibroot,
1506 .dependent_libs = dependent_libs,1541 .needed = lib_info.needed,
1507 .is_needed = lib_info.needed,1542 .weak = lib_info.weak,
1508 })) continue;1543 })) continue;
1509 if (try self.parseArchive(lib, false)) continue;1544 if (try self.parseArchive(lib, false)) continue;
15101545
...@@ -1522,20 +1557,21 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any...@@ -1522,20 +1557,21 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any
1522 const arena = arena_alloc.allocator();1557 const arena = arena_alloc.allocator();
1523 defer arena_alloc.deinit();1558 defer arena_alloc.deinit();
15241559
1525 while (dependent_libs.readItem()) |*id| {1560 while (dependent_libs.readItem()) |*dep_id| {
1526 defer id.deinit(self.base.allocator);1561 defer dep_id.id.deinit(self.base.allocator);
15271562
1528 if (self.dylibs_map.contains(id.name)) continue;1563 if (self.dylibs_map.contains(dep_id.id.name)) continue;
15291564
1565 const weak = self.dylibs.items[dep_id.parent].weak;
1530 const has_ext = blk: {1566 const has_ext = blk: {
1531 const basename = fs.path.basename(id.name);1567 const basename = fs.path.basename(dep_id.id.name);
1532 break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;1568 break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;
1533 };1569 };
1534 const extension = if (has_ext) fs.path.extension(id.name) else "";1570 const extension = if (has_ext) fs.path.extension(dep_id.id.name) else "";
1535 const without_ext = if (has_ext) blk: {1571 const without_ext = if (has_ext) blk: {
1536 const index = mem.lastIndexOfScalar(u8, id.name, '.') orelse unreachable;1572 const index = mem.lastIndexOfScalar(u8, dep_id.id.name, '.') orelse unreachable;
1537 break :blk id.name[0..index];1573 break :blk dep_id.id.name[0..index];
1538 } else id.name;1574 } else dep_id.id.name;
15391575
1540 for (&[_][]const u8{ extension, ".tbd" }) |ext| {1576 for (&[_][]const u8{ extension, ".tbd" }) |ext| {
1541 const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ without_ext, ext });1577 const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ without_ext, ext });
...@@ -1543,15 +1579,15 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any...@@ -1543,15 +1579,15 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any
15431579
1544 log.debug("trying dependency at fully resolved path {s}", .{full_path});1580 log.debug("trying dependency at fully resolved path {s}", .{full_path});
15451581
1546 const did_parse_successfully = try self.parseDylib(full_path, .{1582 const did_parse_successfully = try self.parseDylib(full_path, dependent_libs, .{
1547 .id = id.*,1583 .id = dep_id.id,
1548 .syslibroot = syslibroot,1584 .syslibroot = syslibroot,
1549 .is_dependent = true,1585 .dependent = true,
1550 .dependent_libs = dependent_libs,1586 .weak = weak,
1551 });1587 });
1552 if (did_parse_successfully) break;1588 if (did_parse_successfully) break;
1553 } else {1589 } else {
1554 log.warn("unable to resolve dependency {s}", .{id.name});1590 log.warn("unable to resolve dependency {s}", .{dep_id.id.name});
1555 }1591 }
1556 }1592 }
1557}1593}
...@@ -3081,6 +3117,10 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -3081,6 +3117,10 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
3081 undef.n_type |= macho.N_EXT;3117 undef.n_type |= macho.N_EXT;
3082 undef.n_desc = @intCast(u16, ordinal + 1) * macho.N_SYMBOL_RESOLVER;3118 undef.n_desc = @intCast(u16, ordinal + 1) * macho.N_SYMBOL_RESOLVER;
30833119
3120 if (dylib.weak) {
3121 undef.n_desc |= macho.N_WEAK_REF;
3122 }
3123
3084 if (self.unresolved.fetchSwapRemove(resolv.where_index)) |entry| outer_blk: {3124 if (self.unresolved.fetchSwapRemove(resolv.where_index)) |entry| outer_blk: {
3085 switch (entry.value) {3125 switch (entry.value) {
3086 .none => {},3126 .none => {},
...@@ -3441,6 +3481,7 @@ fn addLoadDylibLC(self: *MachO, id: u16) !void {...@@ -3441,6 +3481,7 @@ fn addLoadDylibLC(self: *MachO, id: u16) !void {
3441 const dylib_id = dylib.id orelse unreachable;3481 const dylib_id = dylib.id orelse unreachable;
3442 var dylib_cmd = try macho.createLoadDylibCommand(3482 var dylib_cmd = try macho.createLoadDylibCommand(
3443 self.base.allocator,3483 self.base.allocator,
3484 if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB,
3444 dylib_id.name,3485 dylib_id.name,
3445 dylib_id.timestamp,3486 dylib_id.timestamp,
3446 dylib_id.current_version,3487 dylib_id.current_version,
...@@ -4885,13 +4926,13 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4885,13 +4926,13 @@ fn populateMissingMetadata(self: *MachO) !void {
4885 std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };4926 std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };
4886 var dylib_cmd = try macho.createLoadDylibCommand(4927 var dylib_cmd = try macho.createLoadDylibCommand(
4887 self.base.allocator,4928 self.base.allocator,
4929 .ID_DYLIB,
4888 install_name,4930 install_name,
4889 2,4931 2,
4890 current_version.major << 16 | current_version.minor << 8 | current_version.patch,4932 current_version.major << 16 | current_version.minor << 8 | current_version.patch,
4891 compat_version.major << 16 | compat_version.minor << 8 | compat_version.patch,4933 compat_version.major << 16 | compat_version.minor << 8 | compat_version.patch,
4892 );4934 );
4893 errdefer dylib_cmd.deinit(self.base.allocator);4935 errdefer dylib_cmd.deinit(self.base.allocator);
4894 dylib_cmd.inner.cmd = .ID_DYLIB;
4895 try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd });4936 try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd });
4896 self.load_commands_dirty = true;4937 self.load_commands_dirty = true;
4897 }4938 }
...@@ -5769,11 +5810,16 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5769,11 +5810,16 @@ fn writeDyldInfoData(self: *MachO) !void {
5769 },5810 },
5770 .undef => {5811 .undef => {
5771 const bind_sym = self.undefs.items[resolv.where_index];5812 const bind_sym = self.undefs.items[resolv.where_index];
5813 var flags: u4 = 0;
5814 if (bind_sym.weakRef()) {
5815 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
5816 }
5772 try bind_pointers.append(.{5817 try bind_pointers.append(.{
5773 .offset = binding.offset + base_offset,5818 .offset = binding.offset + base_offset,
5774 .segment_id = match.seg,5819 .segment_id = match.seg,
5775 .dylib_ordinal = @divExact(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER),5820 .dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER),
5776 .name = self.getString(bind_sym.n_strx),5821 .name = self.getString(bind_sym.n_strx),
5822 .bind_flags = flags,
5777 });5823 });
5778 },5824 },
5779 }5825 }
...@@ -5791,11 +5837,16 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5791,11 +5837,16 @@ fn writeDyldInfoData(self: *MachO) !void {
5791 },5837 },
5792 .undef => {5838 .undef => {
5793 const bind_sym = self.undefs.items[resolv.where_index];5839 const bind_sym = self.undefs.items[resolv.where_index];
5840 var flags: u4 = 0;
5841 if (bind_sym.weakRef()) {
5842 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
5843 }
5794 try lazy_bind_pointers.append(.{5844 try lazy_bind_pointers.append(.{
5795 .offset = binding.offset + base_offset,5845 .offset = binding.offset + base_offset,
5796 .segment_id = match.seg,5846 .segment_id = match.seg,
5797 .dylib_ordinal = @divExact(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER),5847 .dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER),
5798 .name = self.getString(bind_sym.n_strx),5848 .name = self.getString(bind_sym.n_strx),
5849 .bind_flags = flags,
5799 });5850 });
5800 },5851 },
5801 }5852 }
src/link/MachO/Dylib.zig+20-6
...@@ -30,6 +30,7 @@ dysymtab_cmd_index: ?u16 = null,...@@ -30,6 +30,7 @@ dysymtab_cmd_index: ?u16 = null,
30id_cmd_index: ?u16 = null,30id_cmd_index: ?u16 = null,
3131
32id: ?Id = null,32id: ?Id = null,
33weak: bool = false,
3334
34/// Parsed symbol table represented as hash map of symbols'35/// Parsed symbol table represented as hash map of symbols'
35/// names. We can and should defer creating *Symbols until36/// names. We can and should defer creating *Symbols until
...@@ -141,7 +142,13 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {...@@ -141,7 +142,13 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {
141 }142 }
142}143}
143144
144pub fn parse(self: *Dylib, allocator: Allocator, target: std.Target, dependent_libs: anytype) !void {145pub fn parse(
146 self: *Dylib,
147 allocator: Allocator,
148 target: std.Target,
149 dylib_id: u16,
150 dependent_libs: anytype,
151) !void {
145 log.debug("parsing shared library '{s}'", .{self.name});152 log.debug("parsing shared library '{s}'", .{self.name});
146153
147 self.library_offset = try fat.getLibraryOffset(self.file.reader(), target);154 self.library_offset = try fat.getLibraryOffset(self.file.reader(), target);
...@@ -163,12 +170,18 @@ pub fn parse(self: *Dylib, allocator: Allocator, target: std.Target, dependent_l...@@ -163,12 +170,18 @@ pub fn parse(self: *Dylib, allocator: Allocator, target: std.Target, dependent_l
163 return error.MismatchedCpuArchitecture;170 return error.MismatchedCpuArchitecture;
164 }171 }
165172
166 try self.readLoadCommands(allocator, reader, dependent_libs);173 try self.readLoadCommands(allocator, reader, dylib_id, dependent_libs);
167 try self.parseId(allocator);174 try self.parseId(allocator);
168 try self.parseSymbols(allocator);175 try self.parseSymbols(allocator);
169}176}
170177
171fn readLoadCommands(self: *Dylib, allocator: Allocator, reader: anytype, dependent_libs: anytype) !void {178fn readLoadCommands(
179 self: *Dylib,
180 allocator: Allocator,
181 reader: anytype,
182 dylib_id: u16,
183 dependent_libs: anytype,
184) !void {
172 const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;185 const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
173186
174 try self.load_commands.ensureUnusedCapacity(allocator, self.header.?.ncmds);187 try self.load_commands.ensureUnusedCapacity(allocator, self.header.?.ncmds);
...@@ -190,7 +203,7 @@ fn readLoadCommands(self: *Dylib, allocator: Allocator, reader: anytype, depende...@@ -190,7 +203,7 @@ fn readLoadCommands(self: *Dylib, allocator: Allocator, reader: anytype, depende
190 if (should_lookup_reexports) {203 if (should_lookup_reexports) {
191 // Parse install_name to dependent dylib.204 // Parse install_name to dependent dylib.
192 var id = try Id.fromLoadCommand(allocator, cmd.dylib);205 var id = try Id.fromLoadCommand(allocator, cmd.dylib);
193 try dependent_libs.writeItem(id);206 try dependent_libs.writeItem(.{ .id = id, .parent = dylib_id });
194 }207 }
195 },208 },
196 else => {209 else => {
...@@ -338,6 +351,7 @@ pub fn parseFromStub(...@@ -338,6 +351,7 @@ pub fn parseFromStub(
338 allocator: Allocator,351 allocator: Allocator,
339 target: std.Target,352 target: std.Target,
340 lib_stub: LibStub,353 lib_stub: LibStub,
354 dylib_id: u16,
341 dependent_libs: anytype,355 dependent_libs: anytype,
342) !void {356) !void {
343 if (lib_stub.inner.len == 0) return error.EmptyStubFile;357 if (lib_stub.inner.len == 0) return error.EmptyStubFile;
...@@ -417,7 +431,7 @@ pub fn parseFromStub(...@@ -417,7 +431,7 @@ pub fn parseFromStub(
417 log.debug(" (found re-export '{s}')", .{lib});431 log.debug(" (found re-export '{s}')", .{lib});
418432
419 var dep_id = try Id.default(allocator, lib);433 var dep_id = try Id.default(allocator, lib);
420 try dependent_libs.writeItem(dep_id);434 try dependent_libs.writeItem(.{ .id = dep_id, .parent = dylib_id });
421 }435 }
422 }436 }
423 }437 }
...@@ -522,7 +536,7 @@ pub fn parseFromStub(...@@ -522,7 +536,7 @@ pub fn parseFromStub(
522 log.debug(" (found re-export '{s}')", .{lib});536 log.debug(" (found re-export '{s}')", .{lib});
523537
524 var dep_id = try Id.default(allocator, lib);538 var dep_id = try Id.default(allocator, lib);
525 try dependent_libs.writeItem(dep_id);539 try dependent_libs.writeItem(.{ .id = dep_id, .parent = dylib_id });
526 }540 }
527 }541 }
528 }542 }
src/link/MachO/bind.zig+3-2
...@@ -7,6 +7,7 @@ pub const Pointer = struct {...@@ -7,6 +7,7 @@ pub const Pointer = struct {
7 segment_id: u16,7 segment_id: u16,
8 dylib_ordinal: ?i64 = null,8 dylib_ordinal: ?i64 = null,
9 name: ?[]const u8 = null,9 name: ?[]const u8 = null,
10 bind_flags: u4 = 0,
10};11};
1112
12pub fn rebaseInfoSize(pointers: []const Pointer) !u64 {13pub fn rebaseInfoSize(pointers: []const Pointer) !u64 {
...@@ -73,7 +74,7 @@ pub fn writeBindInfo(pointers: []const Pointer, writer: anytype) !void {...@@ -73,7 +74,7 @@ pub fn writeBindInfo(pointers: []const Pointer, writer: anytype) !void {
73 }74 }
74 try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER));75 try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER));
7576
76 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.77 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | pointer.bind_flags);
77 try writer.writeAll(pointer.name.?);78 try writer.writeAll(pointer.name.?);
78 try writer.writeByte(0);79 try writer.writeByte(0);
7980
...@@ -127,7 +128,7 @@ pub fn writeLazyBindInfo(pointers: []const Pointer, writer: anytype) !void {...@@ -127,7 +128,7 @@ pub fn writeLazyBindInfo(pointers: []const Pointer, writer: anytype) !void {
127 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));128 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?)));
128 }129 }
129130
130 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.131 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | pointer.bind_flags);
131 try writer.writeAll(pointer.name.?);132 try writer.writeAll(pointer.name.?);
132 try writer.writeByte(0);133 try writer.writeByte(0);
133134
src/main.zig+35-6
...@@ -443,9 +443,12 @@ const usage_build_generic =...@@ -443,9 +443,12 @@ const usage_build_generic =
443 \\ --subsystem [subsystem] (Windows) /SUBSYSTEM:<subsystem> to the linker443 \\ --subsystem [subsystem] (Windows) /SUBSYSTEM:<subsystem> to the linker
444 \\ --stack [size] Override default stack size444 \\ --stack [size] Override default stack size
445 \\ --image-base [addr] Set base address for executable image445 \\ --image-base [addr] Set base address for executable image
446 \\ -weak-l[lib] (Darwin) link against system library and mark it and all referenced symbols as weak
447 \\ -weak_library [lib]
446 \\ -framework [name] (Darwin) link against framework448 \\ -framework [name] (Darwin) link against framework
447 \\ -needed_framework [name] (Darwin) link against framework (even if unused)449 \\ -needed_framework [name] (Darwin) link against framework (even if unused)
448 \\ -needed_library [lib] (Darwin) link against system library (even if unused)450 \\ -needed_library [lib] (Darwin) link against system library (even if unused)
451 \\ -weak_framework [name] (Darwin) link against framework and mark it and all referenced symbols as weak
449 \\ -F[dir] (Darwin) add search path for frameworks452 \\ -F[dir] (Darwin) add search path for frameworks
450 \\ -install_name=[value] (Darwin) add dylib's install name453 \\ -install_name=[value] (Darwin) add dylib's install name
451 \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature454 \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature
...@@ -916,7 +919,12 @@ fn buildOutputType(...@@ -916,7 +919,12 @@ fn buildOutputType(
916 const path = args_iter.next() orelse {919 const path = args_iter.next() orelse {
917 fatal("expected parameter after {s}", .{arg});920 fatal("expected parameter after {s}", .{arg});
918 };921 };
919 try frameworks.put(gpa, path, .{ .needed = false });922 try frameworks.put(gpa, path, .{});
923 } else if (mem.eql(u8, arg, "-weak_framework")) {
924 const path = args_iter.next() orelse {
925 fatal("expected parameter after {s}", .{arg});
926 };
927 try frameworks.put(gpa, path, .{ .weak = true });
920 } else if (mem.eql(u8, arg, "-needed_framework")) {928 } else if (mem.eql(u8, arg, "-needed_framework")) {
921 const path = args_iter.next() orelse {929 const path = args_iter.next() orelse {
922 fatal("expected parameter after {s}", .{arg});930 fatal("expected parameter after {s}", .{arg});
...@@ -962,7 +970,7 @@ fn buildOutputType(...@@ -962,7 +970,7 @@ fn buildOutputType(
962 };970 };
963 // We don't know whether this library is part of libc or libc++ until971 // We don't know whether this library is part of libc or libc++ until
964 // we resolve the target, so we simply append to the list for now.972 // we resolve the target, so we simply append to the list for now.
965 try system_libs.put(next_arg, .{ .needed = false });973 try system_libs.put(next_arg, .{});
966 } else if (mem.eql(u8, arg, "--needed-library") or974 } else if (mem.eql(u8, arg, "--needed-library") or
967 mem.eql(u8, arg, "-needed-l") or975 mem.eql(u8, arg, "-needed-l") or
968 mem.eql(u8, arg, "-needed_library"))976 mem.eql(u8, arg, "-needed_library"))
...@@ -971,6 +979,11 @@ fn buildOutputType(...@@ -971,6 +979,11 @@ fn buildOutputType(
971 fatal("expected parameter after {s}", .{arg});979 fatal("expected parameter after {s}", .{arg});
972 };980 };
973 try system_libs.put(next_arg, .{ .needed = true });981 try system_libs.put(next_arg, .{ .needed = true });
982 } else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) {
983 const next_arg = args_iter.next() orelse {
984 fatal("expected parameter after {s}", .{arg});
985 };
986 try system_libs.put(next_arg, .{ .weak = true });
974 } else if (mem.eql(u8, arg, "-D") or987 } else if (mem.eql(u8, arg, "-D") or
975 mem.eql(u8, arg, "-isystem") or988 mem.eql(u8, arg, "-isystem") or
976 mem.eql(u8, arg, "-I") or989 mem.eql(u8, arg, "-I") or
...@@ -1300,9 +1313,11 @@ fn buildOutputType(...@@ -1300,9 +1313,11 @@ fn buildOutputType(
1300 } else if (mem.startsWith(u8, arg, "-l")) {1313 } else if (mem.startsWith(u8, arg, "-l")) {
1301 // We don't know whether this library is part of libc or libc++ until1314 // We don't know whether this library is part of libc or libc++ until
1302 // we resolve the target, so we simply append to the list for now.1315 // we resolve the target, so we simply append to the list for now.
1303 try system_libs.put(arg["-l".len..], .{ .needed = false });1316 try system_libs.put(arg["-l".len..], .{});
1304 } else if (mem.startsWith(u8, arg, "-needed-l")) {1317 } else if (mem.startsWith(u8, arg, "-needed-l")) {
1305 try system_libs.put(arg["-needed-l".len..], .{ .needed = true });1318 try system_libs.put(arg["-needed-l".len..], .{ .needed = true });
1319 } else if (mem.startsWith(u8, arg, "-weak-l")) {
1320 try system_libs.put(arg["-weak-l".len..], .{ .weak = true });
1306 } else if (mem.startsWith(u8, arg, "-D") or1321 } else if (mem.startsWith(u8, arg, "-D") or
1307 mem.startsWith(u8, arg, "-I"))1322 mem.startsWith(u8, arg, "-I"))
1308 {1323 {
...@@ -1596,7 +1611,7 @@ fn buildOutputType(...@@ -1596,7 +1611,7 @@ fn buildOutputType(
1596 try clang_argv.appendSlice(it.other_args);1611 try clang_argv.appendSlice(it.other_args);
1597 },1612 },
1598 .framework_dir => try framework_dirs.append(it.only_arg),1613 .framework_dir => try framework_dirs.append(it.only_arg),
1599 .framework => try frameworks.put(gpa, it.only_arg, .{ .needed = false }),1614 .framework => try frameworks.put(gpa, it.only_arg, .{}),
1600 .nostdlibinc => want_native_include_dirs = false,1615 .nostdlibinc => want_native_include_dirs = false,
1601 .strip => strip = true,1616 .strip => strip = true,
1602 .exec_model => {1617 .exec_model => {
...@@ -1879,12 +1894,18 @@ fn buildOutputType(...@@ -1879,12 +1894,18 @@ fn buildOutputType(
1879 ) catch |err| {1894 ) catch |err| {
1880 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });1895 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
1881 };1896 };
1882 } else if (mem.eql(u8, arg, "-framework") or mem.eql(u8, arg, "-weak_framework")) {1897 } else if (mem.eql(u8, arg, "-framework")) {
1898 i += 1;
1899 if (i >= linker_args.items.len) {
1900 fatal("expected linker arg after '{s}'", .{arg});
1901 }
1902 try frameworks.put(gpa, linker_args.items[i], .{});
1903 } else if (mem.eql(u8, arg, "-weak_framework")) {
1883 i += 1;1904 i += 1;
1884 if (i >= linker_args.items.len) {1905 if (i >= linker_args.items.len) {
1885 fatal("expected linker arg after '{s}'", .{arg});1906 fatal("expected linker arg after '{s}'", .{arg});
1886 }1907 }
1887 try frameworks.put(gpa, linker_args.items[i], .{ .needed = false });1908 try frameworks.put(gpa, linker_args.items[i], .{ .weak = true });
1888 } else if (mem.eql(u8, arg, "-needed_framework")) {1909 } else if (mem.eql(u8, arg, "-needed_framework")) {
1889 i += 1;1910 i += 1;
1890 if (i >= linker_args.items.len) {1911 if (i >= linker_args.items.len) {
...@@ -1897,6 +1918,14 @@ fn buildOutputType(...@@ -1897,6 +1918,14 @@ fn buildOutputType(
1897 fatal("expected linker arg after '{s}'", .{arg});1918 fatal("expected linker arg after '{s}'", .{arg});
1898 }1919 }
1899 try system_libs.put(linker_args.items[i], .{ .needed = true });1920 try system_libs.put(linker_args.items[i], .{ .needed = true });
1921 } else if (mem.startsWith(u8, arg, "-weak-l")) {
1922 try system_libs.put(arg["-weak-l".len..], .{ .weak = true });
1923 } else if (mem.eql(u8, arg, "-weak_library")) {
1924 i += 1;
1925 if (i >= linker_args.items.len) {
1926 fatal("expected linker arg after '{s}'", .{arg});
1927 }
1928 try system_libs.put(linker_args.items[i], .{ .weak = true });
1900 } else if (mem.eql(u8, arg, "-compatibility_version")) {1929 } else if (mem.eql(u8, arg, "-compatibility_version")) {
1901 i += 1;1930 i += 1;
1902 if (i >= linker_args.items.len) {1931 if (i >= linker_args.items.len) {
test/link.zig+10-1
...@@ -45,7 +45,11 @@ pub fn addCases(cases: *tests.StandaloneContext) void {...@@ -45,7 +45,11 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
45 .requires_macos_sdk = true,45 .requires_macos_sdk = true,
46 });46 });
4747
48 cases.addBuildFile("test/link/macho/needed_l/build.zig", .{48 cases.addBuildFile("test/link/macho/needed_library/build.zig", .{
49 .build_modes = true,
50 });
51
52 cases.addBuildFile("test/link/macho/weak_library/build.zig", .{
49 .build_modes = true,53 .build_modes = true,
50 });54 });
5155
...@@ -54,6 +58,11 @@ pub fn addCases(cases: *tests.StandaloneContext) void {...@@ -54,6 +58,11 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
54 .requires_macos_sdk = true,58 .requires_macos_sdk = true,
55 });59 });
5660
61 cases.addBuildFile("test/link/macho/weak_framework/build.zig", .{
62 .build_modes = true,
63 .requires_macos_sdk = true,
64 });
65
57 // Try to build and run an Objective-C executable.66 // Try to build and run an Objective-C executable.
58 cases.addBuildFile("test/link/macho/objc/build.zig", .{67 cases.addBuildFile("test/link/macho/objc/build.zig", .{
59 .build_modes = true,68 .build_modes = true,
test/link/macho/entry/build.zig+1-1
...@@ -22,7 +22,7 @@ pub fn build(b: *Builder) void {...@@ -22,7 +22,7 @@ pub fn build(b: *Builder) void {
22 check_exe.checkNext("entryoff {entryoff}");22 check_exe.checkNext("entryoff {entryoff}");
2323
24 check_exe.checkInSymtab();24 check_exe.checkInSymtab();
25 check_exe.checkNext("_non_main {n_value}");25 check_exe.checkNext("{n_value} (__TEXT,__text) external _non_main");
2626
27 check_exe.checkComputeCompare("vmaddr entryoff +", .{ .op = .eq, .value = .{ .variable = "n_value" } });27 check_exe.checkComputeCompare("vmaddr entryoff +", .{ .op = .eq, .value = .{ .variable = "n_value" } });
2828
test/link/macho/needed_l/a.c deleted-1
...@@ -1 +0,0 @@
1int a = 42;
test/link/macho/needed_l/build.zig deleted-35
...@@ -1,35 +0,0 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
4
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
7
8 const test_step = b.step("test", "Test the program");
9 test_step.dependOn(b.getInstallStep());
10
11 const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0));
12 dylib.setBuildMode(mode);
13 dylib.addCSourceFile("a.c", &.{});
14 dylib.linkLibC();
15 dylib.install();
16
17 // -dead_strip_dylibs
18 // -needed-la
19 const exe = b.addExecutable("test", null);
20 exe.addCSourceFile("main.c", &[0][]const u8{});
21 exe.setBuildMode(mode);
22 exe.linkLibC();
23 exe.linkSystemLibraryNeeded("a");
24 exe.addLibraryPath(b.pathFromRoot("zig-out/lib"));
25 exe.addRPath(b.pathFromRoot("zig-out/lib"));
26 exe.dead_strip_dylibs = true;
27
28 const check = exe.checkObject(.macho);
29 check.checkStart("cmd LOAD_DYLIB");
30 check.checkNext("name @rpath/liba.dylib");
31 test_step.dependOn(&check.step);
32
33 const run_cmd = exe.run();
34 test_step.dependOn(&run_cmd.step);
35}
test/link/macho/needed_l/main.c deleted-3
...@@ -1,3 +0,0 @@
1int main(int argc, char* argv[]) {
2 return 0;
3}
test/link/macho/needed_library/a.c created+1
...@@ -0,0 +1 @@
1int a = 42;
test/link/macho/needed_library/build.zig created+35
...@@ -0,0 +1,35 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
4
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
7
8 const test_step = b.step("test", "Test the program");
9 test_step.dependOn(b.getInstallStep());
10
11 const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0));
12 dylib.setBuildMode(mode);
13 dylib.addCSourceFile("a.c", &.{});
14 dylib.linkLibC();
15 dylib.install();
16
17 // -dead_strip_dylibs
18 // -needed-la
19 const exe = b.addExecutable("test", null);
20 exe.addCSourceFile("main.c", &[0][]const u8{});
21 exe.setBuildMode(mode);
22 exe.linkLibC();
23 exe.linkSystemLibraryNeeded("a");
24 exe.addLibraryPath(b.pathFromRoot("zig-out/lib"));
25 exe.addRPath(b.pathFromRoot("zig-out/lib"));
26 exe.dead_strip_dylibs = true;
27
28 const check = exe.checkObject(.macho);
29 check.checkStart("cmd LOAD_DYLIB");
30 check.checkNext("name @rpath/liba.dylib");
31 test_step.dependOn(&check.step);
32
33 const run_cmd = exe.run();
34 test_step.dependOn(&run_cmd.step);
35}
test/link/macho/needed_library/main.c created+3
...@@ -0,0 +1,3 @@
1int main(int argc, char* argv[]) {
2 return 0;
3}
test/link/macho/weak_framework/build.zig created+24
...@@ -0,0 +1,24 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
4
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
7
8 const test_step = b.step("test", "Test the program");
9 test_step.dependOn(b.getInstallStep());
10
11 const exe = b.addExecutable("test", null);
12 exe.addCSourceFile("main.c", &[0][]const u8{});
13 exe.setBuildMode(mode);
14 exe.linkLibC();
15 exe.linkFrameworkWeak("Cocoa");
16
17 const check = exe.checkObject(.macho);
18 check.checkStart("cmd LOAD_WEAK_DYLIB");
19 check.checkNext("name {*}Cocoa");
20 test_step.dependOn(&check.step);
21
22 const run_cmd = exe.run();
23 test_step.dependOn(&run_cmd.step);
24}
test/link/macho/weak_framework/main.c created+3
...@@ -0,0 +1,3 @@
1int main(int argc, char* argv[]) {
2 return 0;
3}
test/link/macho/weak_library/a.c created+9
...@@ -0,0 +1,9 @@
1#include <stdio.h>
2
3int a = 42;
4
5const char* asStr() {
6 static char str[3];
7 sprintf(str, "%d", 42);
8 return str;
9}
test/link/macho/weak_library/build.zig created+38
...@@ -0,0 +1,38 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
4
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
7
8 const test_step = b.step("test", "Test the program");
9 test_step.dependOn(b.getInstallStep());
10
11 const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0));
12 dylib.setBuildMode(mode);
13 dylib.addCSourceFile("a.c", &.{});
14 dylib.linkLibC();
15 dylib.install();
16
17 const exe = b.addExecutable("test", null);
18 exe.addCSourceFile("main.c", &[0][]const u8{});
19 exe.setBuildMode(mode);
20 exe.linkLibC();
21 exe.linkSystemLibraryWeak("a");
22 exe.addLibraryPath(b.pathFromRoot("zig-out/lib"));
23 exe.addRPath(b.pathFromRoot("zig-out/lib"));
24
25 const check = exe.checkObject(.macho);
26 check.checkStart("cmd LOAD_WEAK_DYLIB");
27 check.checkNext("name @rpath/liba.dylib");
28
29 check.checkInSymtab();
30 check.checkNext("(undefined) weak external _a (from liba)");
31 check.checkNext("(undefined) weak external _asStr (from liba)");
32
33 test_step.dependOn(&check.step);
34
35 const run_cmd = exe.run();
36 run_cmd.expectStdOutEqual("42 42");
37 test_step.dependOn(&run_cmd.step);
38}
test/link/macho/weak_library/main.c created+9
...@@ -0,0 +1,9 @@
1#include <stdio.h>
2
3extern int a;
4extern const char* asStr();
5
6int main(int argc, char* argv[]) {
7 printf("%d %s", a, asStr());
8 return 0;
9}