| author | |
| committer | |
| log | 9e8298b864e076221ca9c487412209d8a08c43b2 |
| tree | 50eca29c2dd63c025823f818e882631c94cf8053 |
| parent | ca3c4ff2d0afcdc8fe86e7e7b41a967c88779729 |
| parent | 5834a608fc629319772b1623a31b62dd49ac6d63 |
| signature |
macho: fully implement `-weak-lx` and `-weak_framework x` flags21 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 | }; |
| 1657 | 1658 | ||
| 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 | } |
| 1894 | 1900 | ||
| 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 | } |
| 1898 | 1904 | ||
| 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 | } |
| 1902 | 1916 | ||
| 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 | } |
| 2116 | 2149 | ||
| 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 | } |
| 2120 | 2153 | ||
| 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 | } |
| 2124 | 2157 | ||
| 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 | }, |
| 2514 | 2555 | ||
| 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); |
| 67 | 67 | ||
| 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, " "), " "); |
| 70 | 71 | ||
| ... | @@ -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 | } |
| 100 | 104 | ||
| 105 | if (candidate_var) |v| { | ||
| 106 | try global_vars.putNoClobber(v.name, v.value); | ||
| 107 | } | ||
| 108 | |||
| 101 | return true; | 109 | return true; |
| 102 | } | 110 | } |
| 103 | 111 | ||
| ... | @@ -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(); |
| 334 | 342 | ||
| 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); | ||
| 339 | 354 | ||
| 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 | } |
| 343 | 373 | ||
| 344 | try dumpLoadCommand(cmd, i, writer); | 374 | try dumpLoadCommand(cmd, i, writer); |
| 345 | try writer.writeByte('\n'); | 375 | try writer.writeByte('\n'); |
| 346 | } | 376 | } |
| 347 | 377 | ||
| 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 | } |
| 360 | 435 | ||
| ... | @@ -408,6 +483,8 @@ const MachODumper = struct { | ... | @@ -408,6 +483,8 @@ const MachODumper = struct { |
| 408 | 483 | ||
| 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 { |
| 2085 | 2085 | ||
| 2086 | pub fn createLoadDylibCommand( | 2086 | pub 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 nul | 2097 | @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 | )); |
| 2098 | 2100 | ||
| 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"); |
| 21 | 21 | ||
| 22 | pub const SystemLib = struct { | 22 | pub const SystemLib = struct { |
| 23 | needed: bool = false, | 23 | needed: bool = false, |
| 24 | weak: bool = false, | ||
| 24 | }; | 25 | }; |
| 25 | 26 | ||
| 26 | pub const CacheMode = enum { incremental, whole }; | 27 | pub 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 | }; |
| 54 | 54 | ||
| 55 | const SystemLib = struct { | ||
| 56 | needed: bool = false, | ||
| 57 | weak: bool = false, | ||
| 58 | }; | ||
| 59 | |||
| 55 | base: File, | 60 | base: File, |
| 56 | 61 | ||
| 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 | } |
| 769 | 774 | ||
| 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); |
| 772 | 777 | ||
| 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 | } |
| 782 | 787 | ||
| 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 | } |
| 786 | 794 | ||
| 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 | } |
| 795 | 803 | ||
| 796 | var libs = std.StringArrayHashMap(Compilation.SystemLib).init(arena); | 804 | var libs = std.StringArrayHashMap(SystemLib).init(arena); |
| 797 | 805 | ||
| 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"); |
| 1027 | 1039 | ||
| 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 | else | 1046 | 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 | } |
| 1040 | 1054 | ||
| 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 | else | 1061 | 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 | } |
| 1065 | 1081 | ||
| 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{ |
| 1389 | 1408 | ||
| 1390 | const DylibCreateOpts = struct { | 1409 | const 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 | }; |
| 1397 | 1416 | ||
| 1398 | pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDylibError!bool { | 1417 | pub 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); |
| 1407 | 1431 | ||
| 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 | }; |
| 1412 | 1438 | ||
| 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); |
| 1416 | 1447 | ||
| ... | @@ -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(); |
| 1422 | 1453 | ||
| 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 | } |
| 1440 | 1477 | ||
| 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); |
| 1444 | 1480 | ||
| 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 | }; |
| 1449 | 1485 | ||
| 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 |
| 1467 | 1503 | ||
| 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; |
| 1474 | 1509 | ||
| 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 |
| 1494 | fn parseLibs( | 1529 | fn 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; |
| 1510 | 1545 | ||
| ... | @@ -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(); |
| 1524 | 1559 | ||
| 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); |
| 1527 | 1562 | ||
| 1528 | if (self.dylibs_map.contains(id.name)) continue; | 1563 | if (self.dylibs_map.contains(dep_id.id.name)) continue; |
| 1529 | 1564 | ||
| 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; |
| 1539 | 1575 | ||
| 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 |
| 1543 | 1579 | ||
| 1544 | log.debug("trying dependency at fully resolved path {s}", .{full_path}); | 1580 | log.debug("trying dependency at fully resolved path {s}", .{full_path}); |
| 1545 | 1581 | ||
| 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; |
| 3083 | 3119 | ||
| 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, |
| 30 | id_cmd_index: ?u16 = null, | 30 | id_cmd_index: ?u16 = null, |
| 31 | 31 | ||
| 32 | id: ?Id = null, | 32 | id: ?Id = null, |
| 33 | weak: bool = false, | ||
| 33 | 34 | ||
| 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 until | 36 | /// 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 | } |
| 143 | 144 | ||
| 144 | pub fn parse(self: *Dylib, allocator: Allocator, target: std.Target, dependent_libs: anytype) !void { | 145 | pub 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}); |
| 146 | 153 | ||
| 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 | } |
| 165 | 172 | ||
| 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 | } |
| 170 | 177 | ||
| 171 | fn readLoadCommands(self: *Dylib, allocator: Allocator, reader: anytype, dependent_libs: anytype) !void { | 178 | fn 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; |
| 173 | 186 | ||
| 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}); |
| 418 | 432 | ||
| 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}); |
| 523 | 537 | ||
| 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 | }; |
| 11 | 12 | ||
| 12 | pub fn rebaseInfoSize(pointers: []const Pointer) !u64 { | 13 | pub 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)); |
| 75 | 76 | ||
| 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); |
| 79 | 80 | ||
| ... | @@ -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 | } |
| 129 | 130 | ||
| 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); |
| 133 | 134 |
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 linker | 443 | \\ --subsystem [subsystem] (Windows) /SUBSYSTEM:<subsystem> to the linker |
| 444 | \\ --stack [size] Override default stack size | 444 | \\ --stack [size] Override default stack size |
| 445 | \\ --image-base [addr] Set base address for executable image | 445 | \\ --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 framework | 448 | \\ -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 frameworks | 452 | \\ -F[dir] (Darwin) add search path for frameworks |
| 450 | \\ -install_name=[value] (Darwin) add dylib's install name | 453 | \\ -install_name=[value] (Darwin) add dylib's install name |
| 451 | \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature | 454 | \\ --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++ until | 971 | // 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") or | 974 | } else if (mem.eql(u8, arg, "--needed-library") or |
| 967 | mem.eql(u8, arg, "-needed-l") or | 975 | 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") or | 987 | } else if (mem.eql(u8, arg, "-D") or |
| 975 | mem.eql(u8, arg, "-isystem") or | 988 | mem.eql(u8, arg, "-isystem") or |
| 976 | mem.eql(u8, arg, "-I") or | 989 | 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++ until | 1314 | // 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") or | 1321 | } 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 | }); |
| 47 | 47 | ||
| 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 | }); |
| 51 | 55 | ||
| ... | @@ -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 | }); |
| 56 | 60 | ||
| 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}"); |
| 23 | 23 | ||
| 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"); |
| 26 | 26 | ||
| 27 | check_exe.checkComputeCompare("vmaddr entryoff +", .{ .op = .eq, .value = .{ .variable = "n_value" } }); | 27 | check_exe.checkComputeCompare("vmaddr entryoff +", .{ .op = .eq, .value = .{ .variable = "n_value" } }); |
| 28 | 28 |
test/link/macho/needed_l/a.c deleted-1| ... | @@ -1 +0,0 @@ | ||
| 1 | int a = 42; | ||
test/link/macho/needed_l/build.zig deleted-35| ... | @@ -1,35 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Builder = std.build.Builder; | ||
| 3 | const LibExeObjectStep = std.build.LibExeObjStep; | ||
| 4 | |||
| 5 | pub 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 @@ | ||
| 1 | int main(int argc, char* argv[]) { | ||
| 2 | return 0; | ||
| 3 | } | ||
test/link/macho/needed_library/a.c created+1| ... | @@ -0,0 +1 @@ | ||
| 1 | int a = 42; | ||
test/link/macho/needed_library/build.zig created+35| ... | @@ -0,0 +1,35 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Builder = std.build.Builder; | ||
| 3 | const LibExeObjectStep = std.build.LibExeObjStep; | ||
| 4 | |||
| 5 | pub 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 @@ | ||
| 1 | int main(int argc, char* argv[]) { | ||
| 2 | return 0; | ||
| 3 | } | ||
test/link/macho/weak_framework/build.zig created+24| ... | @@ -0,0 +1,24 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Builder = std.build.Builder; | ||
| 3 | const LibExeObjectStep = std.build.LibExeObjStep; | ||
| 4 | |||
| 5 | pub 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 @@ | ||
| 1 | int 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 | |||
| 3 | int a = 42; | ||
| 4 | |||
| 5 | const 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Builder = std.build.Builder; | ||
| 3 | const LibExeObjectStep = std.build.LibExeObjStep; | ||
| 4 | |||
| 5 | pub 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 | |||
| 3 | extern int a; | ||
| 4 | extern const char* asStr(); | ||
| 5 | |||
| 6 | int main(int argc, char* argv[]) { | ||
| 7 | printf("%d %s", a, asStr()); | ||
| 8 | return 0; | ||
| 9 | } | ||