| author | |
| committer | |
| log | 2c385e58f96ea080bd6c732de422f84c6c38c2a2 |
| tree | 34bd5fd2b65f02be3e822dd220557d0c36c1e1a5 |
| parent | 6d47b4f39e8cdd95ead71b1efdbf67a737174e97 |
| parent | 9b5a463111534a892177f731397cbab1f586e437 |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
zld: link against system libSystem when available4 files changed, 271 insertions(+), 258 deletions(-)
src/link/MachO.zig+160-120| ... | ... | @@ -514,117 +514,83 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 514 | 514 | } |
| 515 | 515 | } |
| 516 | 516 | |
| 517 | fn resolvePaths( | |
| 517 | fn resolveSearchDir( | |
| 518 | 518 | arena: *Allocator, |
| 519 | resolved_paths: *std.ArrayList([]const u8), | |
| 519 | dir: []const u8, | |
| 520 | 520 | syslibroot: ?[]const u8, |
| 521 | search_dirs: []const []const u8, | |
| 522 | lib_names: []const []const u8, | |
| 523 | kind: enum { lib, framework }, | |
| 524 | ) !void { | |
| 525 | var resolved_dirs = std.ArrayList([]const u8).init(arena); | |
| 526 | for (search_dirs) |dir| { | |
| 527 | if (fs.path.isAbsolute(dir)) { | |
| 528 | var candidates = std.ArrayList([]const u8).init(arena); | |
| 529 | if (syslibroot) |root| { | |
| 530 | const full_path = try fs.path.join(arena, &[_][]const u8{ root, dir }); | |
| 531 | try candidates.append(full_path); | |
| 532 | } | |
| 533 | try candidates.append(dir); | |
| 534 | ||
| 535 | var found = false; | |
| 536 | for (candidates.items) |candidate| { | |
| 537 | // Verify that search path actually exists | |
| 538 | var tmp = fs.cwd().openDir(candidate, .{}) catch |err| switch (err) { | |
| 539 | error.FileNotFound => continue, | |
| 540 | else => |e| return e, | |
| 541 | }; | |
| 542 | defer tmp.close(); | |
| 521 | ) !?[]const u8 { | |
| 522 | var candidates = std.ArrayList([]const u8).init(arena); | |
| 543 | 523 | |
| 544 | try resolved_dirs.append(candidate); | |
| 545 | found = true; | |
| 546 | break; | |
| 547 | } | |
| 524 | if (fs.path.isAbsolute(dir)) { | |
| 525 | if (syslibroot) |root| { | |
| 526 | const full_path = try fs.path.join(arena, &[_][]const u8{ root, dir }); | |
| 527 | try candidates.append(full_path); | |
| 528 | } | |
| 529 | } | |
| 548 | 530 | |
| 549 | if (!found) { | |
| 550 | switch (kind) { | |
| 551 | .lib => log.warn("directory not found for '-L{s}'", .{dir}), | |
| 552 | .framework => log.warn("directory not found for '-F{s}'", .{dir}), | |
| 553 | } | |
| 554 | } | |
| 555 | } else { | |
| 556 | // Verify that search path actually exists | |
| 557 | var tmp = fs.cwd().openDir(dir, .{}) catch |err| switch (err) { | |
| 558 | error.FileNotFound => { | |
| 559 | switch (kind) { | |
| 560 | .lib => log.warn("directory not found for '-L{s}'", .{dir}), | |
| 561 | .framework => log.warn("directory not found for '-F{s}'", .{dir}), | |
| 562 | } | |
| 563 | continue; | |
| 564 | }, | |
| 565 | else => |e| return e, | |
| 566 | }; | |
| 567 | defer tmp.close(); | |
| 531 | try candidates.append(dir); | |
| 568 | 532 | |
| 569 | try resolved_dirs.append(dir); | |
| 570 | } | |
| 533 | for (candidates.items) |candidate| { | |
| 534 | // Verify that search path actually exists | |
| 535 | var tmp = fs.cwd().openDir(candidate, .{}) catch |err| switch (err) { | |
| 536 | error.FileNotFound => continue, | |
| 537 | else => |e| return e, | |
| 538 | }; | |
| 539 | defer tmp.close(); | |
| 540 | ||
| 541 | return candidate; | |
| 571 | 542 | } |
| 572 | 543 | |
| 573 | // Assume ld64 default: -search_paths_first | |
| 574 | // Look in each directory for a dylib (next, tbd), and then for archive | |
| 575 | // TODO implement alternative: -search_dylibs_first | |
| 576 | const exts = switch (kind) { | |
| 577 | .lib => &[_][]const u8{ "dylib", "tbd", "a" }, | |
| 578 | .framework => &[_][]const u8{ "dylib", "tbd" }, | |
| 579 | }; | |
| 544 | return null; | |
| 545 | } | |
| 580 | 546 | |
| 581 | for (lib_names) |lib_name| { | |
| 582 | var found = false; | |
| 583 | ||
| 584 | ext: for (exts) |ext| { | |
| 585 | const lib_name_ext = blk: { | |
| 586 | switch (kind) { | |
| 587 | .lib => break :blk try std.fmt.allocPrint(arena, "lib{s}.{s}", .{ lib_name, ext }), | |
| 588 | .framework => { | |
| 589 | const prefix = try std.fmt.allocPrint(arena, "{s}.framework", .{lib_name}); | |
| 590 | const nn = try std.fmt.allocPrint(arena, "{s}.{s}", .{ lib_name, ext }); | |
| 591 | break :blk try fs.path.join(arena, &[_][]const u8{ prefix, nn }); | |
| 592 | }, | |
| 593 | } | |
| 594 | }; | |
| 547 | fn resolveLib( | |
| 548 | arena: *Allocator, | |
| 549 | search_dirs: []const []const u8, | |
| 550 | name: []const u8, | |
| 551 | ext: []const u8, | |
| 552 | ) !?[]const u8 { | |
| 553 | const search_name = try std.fmt.allocPrint(arena, "lib{s}{s}", .{ name, ext }); | |
| 595 | 554 | |
| 596 | for (resolved_dirs.items) |dir| { | |
| 597 | const full_path = try fs.path.join(arena, &[_][]const u8{ dir, lib_name_ext }); | |
| 555 | for (search_dirs) |dir| { | |
| 556 | const full_path = try fs.path.join(arena, &[_][]const u8{ dir, search_name }); | |
| 598 | 557 | |
| 599 | // Check if the lib file exists. | |
| 600 | const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { | |
| 601 | error.FileNotFound => continue, | |
| 602 | else => |e| return e, | |
| 603 | }; | |
| 604 | defer tmp.close(); | |
| 558 | // Check if the file exists. | |
| 559 | const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { | |
| 560 | error.FileNotFound => continue, | |
| 561 | else => |e| return e, | |
| 562 | }; | |
| 563 | defer tmp.close(); | |
| 605 | 564 | |
| 606 | try resolved_paths.append(full_path); | |
| 607 | found = true; | |
| 608 | break :ext; | |
| 609 | } | |
| 610 | } | |
| 565 | return full_path; | |
| 566 | } | |
| 611 | 567 | |
| 612 | if (!found) { | |
| 613 | switch (kind) { | |
| 614 | .lib => { | |
| 615 | log.warn("library not found for '-l{s}'", .{lib_name}); | |
| 616 | log.warn("Library search paths:", .{}); | |
| 617 | }, | |
| 618 | .framework => { | |
| 619 | log.warn("framework not found for '-f{s}'", .{lib_name}); | |
| 620 | log.warn("Framework search paths:", .{}); | |
| 621 | }, | |
| 622 | } | |
| 623 | for (resolved_dirs.items) |dir| { | |
| 624 | log.warn(" {s}", .{dir}); | |
| 625 | } | |
| 626 | } | |
| 568 | return null; | |
| 569 | } | |
| 570 | ||
| 571 | fn resolveFramework( | |
| 572 | arena: *Allocator, | |
| 573 | search_dirs: []const []const u8, | |
| 574 | name: []const u8, | |
| 575 | ext: []const u8, | |
| 576 | ) !?[]const u8 { | |
| 577 | const search_name = try std.fmt.allocPrint(arena, "{s}{s}", .{ name, ext }); | |
| 578 | const prefix_path = try std.fmt.allocPrint(arena, "{s}.framework", .{name}); | |
| 579 | ||
| 580 | for (search_dirs) |dir| { | |
| 581 | const full_path = try fs.path.join(arena, &[_][]const u8{ dir, prefix_path, search_name }); | |
| 582 | ||
| 583 | // Check if the file exists. | |
| 584 | const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { | |
| 585 | error.FileNotFound => continue, | |
| 586 | else => |e| return e, | |
| 587 | }; | |
| 588 | defer tmp.close(); | |
| 589 | ||
| 590 | return full_path; | |
| 627 | 591 | } |
| 592 | ||
| 593 | return null; | |
| 628 | 594 | } |
| 629 | 595 | |
| 630 | 596 | fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| ... | ... | @@ -789,7 +755,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 789 | 755 | zld.deinit(); |
| 790 | 756 | } |
| 791 | 757 | zld.arch = target.cpu.arch; |
| 792 | zld.syslibroot = self.base.options.sysroot; | |
| 793 | 758 | zld.stack_size = stack_size; |
| 794 | 759 | |
| 795 | 760 | // Positional arguments to the linker such as object files and static archives. |
| ... | ... | @@ -829,15 +794,97 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 829 | 794 | try search_lib_names.append(link_lib); |
| 830 | 795 | } |
| 831 | 796 | |
| 797 | var lib_dirs = std.ArrayList([]const u8).init(arena); | |
| 798 | for (self.base.options.lib_dirs) |dir| { | |
| 799 | if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| { | |
| 800 | try lib_dirs.append(search_dir); | |
| 801 | } else { | |
| 802 | log.warn("directory not found for '-L{s}'", .{dir}); | |
| 803 | } | |
| 804 | } | |
| 805 | ||
| 832 | 806 | var libs = std.ArrayList([]const u8).init(arena); |
| 833 | try resolvePaths( | |
| 834 | arena, | |
| 835 | &libs, | |
| 836 | self.base.options.sysroot, | |
| 837 | self.base.options.lib_dirs, | |
| 838 | search_lib_names.items, | |
| 839 | .lib, | |
| 840 | ); | |
| 807 | var lib_not_found = false; | |
| 808 | for (search_lib_names.items) |lib_name| { | |
| 809 | // Assume ld64 default: -search_paths_first | |
| 810 | // Look in each directory for a dylib (stub first), and then for archive | |
| 811 | // TODO implement alternative: -search_dylibs_first | |
| 812 | for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| { | |
| 813 | if (try resolveLib(arena, lib_dirs.items, lib_name, ext)) |full_path| { | |
| 814 | try libs.append(full_path); | |
| 815 | break; | |
| 816 | } | |
| 817 | } else { | |
| 818 | log.warn("library not found for '-l{s}'", .{lib_name}); | |
| 819 | lib_not_found = true; | |
| 820 | } | |
| 821 | } | |
| 822 | ||
| 823 | if (lib_not_found) { | |
| 824 | log.warn("Library search paths:", .{}); | |
| 825 | for (lib_dirs.items) |dir| { | |
| 826 | log.warn(" {s}", .{dir}); | |
| 827 | } | |
| 828 | } | |
| 829 | ||
| 830 | // If we're compiling native and we can find libSystem.B.{dylib, tbd}, | |
| 831 | // we link against that instead of embedded libSystem.B.tbd file. | |
| 832 | var native_libsystem_available = false; | |
| 833 | if (self.base.options.is_native_os) blk: { | |
| 834 | // Try stub file first. If we hit it, then we're done as the stub file | |
| 835 | // re-exports every single symbol definition. | |
| 836 | if (try resolveLib(arena, lib_dirs.items, "System", ".tbd")) |full_path| { | |
| 837 | try libs.append(full_path); | |
| 838 | native_libsystem_available = true; | |
| 839 | break :blk; | |
| 840 | } | |
| 841 | // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib | |
| 842 | // doesn't export libc.dylib which we'll need to resolve subsequently also. | |
| 843 | if (try resolveLib(arena, lib_dirs.items, "System", ".dylib")) |libsystem_path| { | |
| 844 | if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| { | |
| 845 | try libs.append(libsystem_path); | |
| 846 | try libs.append(libc_path); | |
| 847 | native_libsystem_available = true; | |
| 848 | break :blk; | |
| 849 | } | |
| 850 | } | |
| 851 | } | |
| 852 | if (!native_libsystem_available) { | |
| 853 | const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ | |
| 854 | "libc", "darwin", "libSystem.B.tbd", | |
| 855 | }); | |
| 856 | try libs.append(full_path); | |
| 857 | } | |
| 858 | ||
| 859 | // frameworks | |
| 860 | var framework_dirs = std.ArrayList([]const u8).init(arena); | |
| 861 | for (self.base.options.framework_dirs) |dir| { | |
| 862 | if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| { | |
| 863 | try framework_dirs.append(search_dir); | |
| 864 | } else { | |
| 865 | log.warn("directory not found for '-F{s}'", .{dir}); | |
| 866 | } | |
| 867 | } | |
| 868 | ||
| 869 | var framework_not_found = false; | |
| 870 | for (self.base.options.frameworks) |framework| { | |
| 871 | for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { | |
| 872 | if (try resolveFramework(arena, framework_dirs.items, framework, ext)) |full_path| { | |
| 873 | try libs.append(full_path); | |
| 874 | break; | |
| 875 | } | |
| 876 | } else { | |
| 877 | log.warn("framework not found for '-f{s}'", .{framework}); | |
| 878 | framework_not_found = true; | |
| 879 | } | |
| 880 | } | |
| 881 | ||
| 882 | if (framework_not_found) { | |
| 883 | log.warn("Framework search paths:", .{}); | |
| 884 | for (framework_dirs.items) |dir| { | |
| 885 | log.warn(" {s}", .{dir}); | |
| 886 | } | |
| 887 | } | |
| 841 | 888 | |
| 842 | 889 | // rpaths |
| 843 | 890 | var rpath_table = std.StringArrayHashMap(void).init(arena); |
| ... | ... | @@ -852,16 +899,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 852 | 899 | rpaths.appendAssumeCapacity(key.*); |
| 853 | 900 | } |
| 854 | 901 | |
| 855 | // frameworks | |
| 856 | try resolvePaths( | |
| 857 | arena, | |
| 858 | &libs, | |
| 859 | self.base.options.sysroot, | |
| 860 | self.base.options.framework_dirs, | |
| 861 | self.base.options.frameworks, | |
| 862 | .framework, | |
| 863 | ); | |
| 864 | ||
| 865 | 902 | if (self.base.options.verbose_link) { |
| 866 | 903 | var argv = std.ArrayList([]const u8).init(arena); |
| 867 | 904 | |
| ... | ... | @@ -883,6 +920,11 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 883 | 920 | try argv.append("-o"); |
| 884 | 921 | try argv.append(full_out_path); |
| 885 | 922 | |
| 923 | if (native_libsystem_available) { | |
| 924 | try argv.append("-lSystem"); | |
| 925 | try argv.append("-lc"); | |
| 926 | } | |
| 927 | ||
| 886 | 928 | for (search_lib_names.items) |l_name| { |
| 887 | 929 | try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name})); |
| 888 | 930 | } |
| ... | ... | @@ -895,11 +937,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 895 | 937 | } |
| 896 | 938 | |
| 897 | 939 | try zld.link(positionals.items, full_out_path, .{ |
| 940 | .syslibroot = self.base.options.sysroot, | |
| 898 | 941 | .libs = libs.items, |
| 899 | 942 | .rpaths = rpaths.items, |
| 900 | .libc_stub_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ | |
| 901 | "libc", "darwin", "libSystem.B.tbd", | |
| 902 | }), | |
| 903 | 943 | }); |
| 904 | 944 | |
| 905 | 945 | break :outer; |
src/link/MachO/Dylib.zig+82-51| ... | ... | @@ -45,8 +45,8 @@ id: ?Id = null, |
| 45 | 45 | /// a symbol is referenced by an object file. |
| 46 | 46 | symbols: std.StringArrayHashMapUnmanaged(void) = .{}, |
| 47 | 47 | |
| 48 | // TODO add parsing re-exported libs from binary dylibs | |
| 49 | dependent_libs: std.StringArrayHashMapUnmanaged(void) = .{}, | |
| 48 | /// Array list of all dependent libs of this dylib. | |
| 49 | dependent_libs: std.ArrayListUnmanaged(Id) = .{}, | |
| 50 | 50 | |
| 51 | 51 | pub const Id = struct { |
| 52 | 52 | name: []const u8, |
| ... | ... | @@ -54,15 +54,28 @@ pub const Id = struct { |
| 54 | 54 | current_version: u32, |
| 55 | 55 | compatibility_version: u32, |
| 56 | 56 | |
| 57 | pub fn default(name: []const u8) Id { | |
| 58 | return .{ | |
| 59 | .name = name, | |
| 57 | pub fn default(allocator: *Allocator, name: []const u8) !Id { | |
| 58 | return Id{ | |
| 59 | .name = try allocator.dupe(u8, name), | |
| 60 | 60 | .timestamp = 2, |
| 61 | 61 | .current_version = 0x10000, |
| 62 | 62 | .compatibility_version = 0x10000, |
| 63 | 63 | }; |
| 64 | 64 | } |
| 65 | 65 | |
| 66 | pub fn fromLoadCommand(allocator: *Allocator, lc: GenericCommandWithData(macho.dylib_command)) !Id { | |
| 67 | const dylib = lc.inner.dylib; | |
| 68 | const dylib_name = @ptrCast([*:0]const u8, lc.data[dylib.name - @sizeOf(macho.dylib_command) ..]); | |
| 69 | const name = try allocator.dupe(u8, mem.spanZ(dylib_name)); | |
| 70 | ||
| 71 | return Id{ | |
| 72 | .name = name, | |
| 73 | .timestamp = dylib.timestamp, | |
| 74 | .current_version = dylib.current_version, | |
| 75 | .compatibility_version = dylib.compatibility_version, | |
| 76 | }; | |
| 77 | } | |
| 78 | ||
| 66 | 79 | pub fn deinit(id: *Id, allocator: *Allocator) void { |
| 67 | 80 | allocator.free(id.name); |
| 68 | 81 | } |
| ... | ... | @@ -129,12 +142,12 @@ pub const Error = error{ |
| 129 | 142 | UnsupportedCpuArchitecture, |
| 130 | 143 | } || fs.File.OpenError || std.os.PReadError || Id.ParseError; |
| 131 | 144 | |
| 132 | pub fn createAndParseFromPath( | |
| 133 | allocator: *Allocator, | |
| 134 | arch: Arch, | |
| 135 | path: []const u8, | |
| 136 | syslibroot: ?[]const u8, | |
| 137 | ) Error!?[]*Dylib { | |
| 145 | pub const CreateOpts = struct { | |
| 146 | syslibroot: ?[]const u8 = null, | |
| 147 | id: ?Id = null, | |
| 148 | }; | |
| 149 | ||
| 150 | pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8, opts: CreateOpts) Error!?[]*Dylib { | |
| 138 | 151 | const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { |
| 139 | 152 | error.FileNotFound => return null, |
| 140 | 153 | else => |e| return e, |
| ... | ... | @@ -152,7 +165,7 @@ pub fn createAndParseFromPath( |
| 152 | 165 | .arch = arch, |
| 153 | 166 | .name = name, |
| 154 | 167 | .file = file, |
| 155 | .syslibroot = syslibroot, | |
| 168 | .syslibroot = opts.syslibroot, | |
| 156 | 169 | }; |
| 157 | 170 | |
| 158 | 171 | dylib.parse() catch |err| switch (err) { |
| ... | ... | @@ -171,6 +184,20 @@ pub fn createAndParseFromPath( |
| 171 | 184 | else => |e| return e, |
| 172 | 185 | }; |
| 173 | 186 | |
| 187 | if (opts.id) |id| { | |
| 188 | if (dylib.id.?.current_version < id.compatibility_version) { | |
| 189 | log.warn("found dylib is incompatible with the required minimum version", .{}); | |
| 190 | log.warn(" | dylib: {s}", .{id.name}); | |
| 191 | log.warn(" | required minimum version: {}", .{id.compatibility_version}); | |
| 192 | log.warn(" | dylib version: {}", .{dylib.id.?.current_version}); | |
| 193 | ||
| 194 | // TODO maybe this should be an error and facilitate auto-cleanup? | |
| 195 | dylib.deinit(); | |
| 196 | allocator.destroy(dylib); | |
| 197 | return null; | |
| 198 | } | |
| 199 | } | |
| 200 | ||
| 174 | 201 | var dylibs = std.ArrayList(*Dylib).init(allocator); |
| 175 | 202 | defer dylibs.deinit(); |
| 176 | 203 | |
| ... | ... | @@ -191,8 +218,8 @@ pub fn deinit(self: *Dylib) void { |
| 191 | 218 | } |
| 192 | 219 | self.symbols.deinit(self.allocator); |
| 193 | 220 | |
| 194 | for (self.dependent_libs.keys()) |key| { | |
| 195 | self.allocator.free(key); | |
| 221 | for (self.dependent_libs.items) |*id| { | |
| 222 | id.deinit(self.allocator); | |
| 196 | 223 | } |
| 197 | 224 | self.dependent_libs.deinit(self.allocator); |
| 198 | 225 | |
| ... | ... | @@ -287,6 +314,8 @@ fn readFatStruct(reader: anytype, comptime T: type) !T { |
| 287 | 314 | } |
| 288 | 315 | |
| 289 | 316 | fn readLoadCommands(self: *Dylib, reader: anytype) !void { |
| 317 | const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0; | |
| 318 | ||
| 290 | 319 | try self.load_commands.ensureCapacity(self.allocator, self.header.?.ncmds); |
| 291 | 320 | |
| 292 | 321 | var i: u16 = 0; |
| ... | ... | @@ -302,6 +331,13 @@ fn readLoadCommands(self: *Dylib, reader: anytype) !void { |
| 302 | 331 | macho.LC_ID_DYLIB => { |
| 303 | 332 | self.id_cmd_index = i; |
| 304 | 333 | }, |
| 334 | macho.LC_REEXPORT_DYLIB => { | |
| 335 | if (should_lookup_reexports) { | |
| 336 | // Parse install_name to dependent dylib. | |
| 337 | const id = try Id.fromLoadCommand(self.allocator, cmd.Dylib); | |
| 338 | try self.dependent_libs.append(self.allocator, id); | |
| 339 | } | |
| 340 | }, | |
| 305 | 341 | else => { |
| 306 | 342 | log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()}); |
| 307 | 343 | }, |
| ... | ... | @@ -313,22 +349,10 @@ fn readLoadCommands(self: *Dylib, reader: anytype) !void { |
| 313 | 349 | fn parseId(self: *Dylib) !void { |
| 314 | 350 | const index = self.id_cmd_index orelse { |
| 315 | 351 | log.debug("no LC_ID_DYLIB load command found; using hard-coded defaults...", .{}); |
| 316 | self.id = Id.default(try self.allocator.dupe(u8, self.name.?)); | |
| 352 | self.id = try Id.default(self.allocator, self.name.?); | |
| 317 | 353 | return; |
| 318 | 354 | }; |
| 319 | const id_cmd = self.load_commands.items[index].Dylib; | |
| 320 | const dylib = id_cmd.inner.dylib; | |
| 321 | ||
| 322 | // TODO should we compare the name from the dylib's id with the user-specified one? | |
| 323 | const dylib_name = @ptrCast([*:0]const u8, id_cmd.data[dylib.name - @sizeOf(macho.dylib_command) ..]); | |
| 324 | const name = try self.allocator.dupe(u8, mem.spanZ(dylib_name)); | |
| 325 | ||
| 326 | self.id = .{ | |
| 327 | .name = name, | |
| 328 | .timestamp = dylib.timestamp, | |
| 329 | .current_version = dylib.current_version, | |
| 330 | .compatibility_version = dylib.compatibility_version, | |
| 331 | }; | |
| 355 | self.id = try Id.fromLoadCommand(self.allocator, self.load_commands.items[index].Dylib); | |
| 332 | 356 | } |
| 333 | 357 | |
| 334 | 358 | fn parseSymbols(self: *Dylib) !void { |
| ... | ... | @@ -345,10 +369,11 @@ fn parseSymbols(self: *Dylib) !void { |
| 345 | 369 | _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff + self.library_offset); |
| 346 | 370 | |
| 347 | 371 | for (slice) |sym| { |
| 348 | const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx)); | |
| 372 | const add_to_symtab = Symbol.isExt(sym) and (Symbol.isSect(sym) or Symbol.isIndr(sym)); | |
| 349 | 373 | |
| 350 | if (!(Symbol.isSect(sym) and Symbol.isExt(sym))) continue; | |
| 374 | if (!add_to_symtab) continue; | |
| 351 | 375 | |
| 376 | const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx)); | |
| 352 | 377 | const name = try self.allocator.dupe(u8, sym_name); |
| 353 | 378 | try self.symbols.putNoClobber(self.allocator, name, {}); |
| 354 | 379 | } |
| ... | ... | @@ -380,7 +405,7 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { |
| 380 | 405 | |
| 381 | 406 | const umbrella_lib = lib_stub.inner[0]; |
| 382 | 407 | |
| 383 | var id = Id.default(try self.allocator.dupe(u8, umbrella_lib.install_name)); | |
| 408 | var id = try Id.default(self.allocator, umbrella_lib.install_name); | |
| 384 | 409 | if (umbrella_lib.current_version) |version| { |
| 385 | 410 | try id.parseCurrentVersion(version); |
| 386 | 411 | } |
| ... | ... | @@ -470,7 +495,9 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { |
| 470 | 495 | } |
| 471 | 496 | |
| 472 | 497 | log.debug(" | {s}", .{lib}); |
| 473 | try self.dependent_libs.put(self.allocator, try self.allocator.dupe(u8, lib), {}); | |
| 498 | ||
| 499 | const dep_id = try Id.default(self.allocator, lib); | |
| 500 | try self.dependent_libs.append(self.allocator, dep_id); | |
| 474 | 501 | } |
| 475 | 502 | } |
| 476 | 503 | } |
| ... | ... | @@ -478,36 +505,40 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { |
| 478 | 505 | } |
| 479 | 506 | |
| 480 | 507 | pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void { |
| 481 | outer: for (self.dependent_libs.keys()) |lib| { | |
| 482 | const dirname = fs.path.dirname(lib) orelse { | |
| 483 | log.warn("unable to resolve dependency {s}", .{lib}); | |
| 484 | continue; | |
| 508 | outer: for (self.dependent_libs.items) |id| { | |
| 509 | const has_ext = blk: { | |
| 510 | const basename = fs.path.basename(id.name); | |
| 511 | break :blk mem.lastIndexOfScalar(u8, basename, '.') != null; | |
| 485 | 512 | }; |
| 486 | const filename = fs.path.basename(lib); | |
| 487 | const without_ext = if (mem.lastIndexOfScalar(u8, filename, '.')) |index| | |
| 488 | filename[0..index] | |
| 489 | else | |
| 490 | filename; | |
| 491 | ||
| 492 | for (&[_][]const u8{ "dylib", "tbd" }) |ext| { | |
| 493 | const with_ext = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ | |
| 513 | const extension = if (has_ext) fs.path.extension(id.name) else ""; | |
| 514 | const without_ext = if (has_ext) blk: { | |
| 515 | const index = mem.lastIndexOfScalar(u8, id.name, '.') orelse unreachable; | |
| 516 | break :blk id.name[0..index]; | |
| 517 | } else id.name; | |
| 518 | ||
| 519 | for (&[_][]const u8{ extension, ".tbd" }) |ext| { | |
| 520 | const with_ext = try std.fmt.allocPrint(self.allocator, "{s}{s}", .{ | |
| 494 | 521 | without_ext, |
| 495 | 522 | ext, |
| 496 | 523 | }); |
| 497 | 524 | defer self.allocator.free(with_ext); |
| 498 | 525 | |
| 499 | const lib_path = if (self.syslibroot) |syslibroot| | |
| 500 | try fs.path.join(self.allocator, &.{ syslibroot, dirname, with_ext }) | |
| 526 | const full_path = if (self.syslibroot) |syslibroot| | |
| 527 | try fs.path.join(self.allocator, &.{ syslibroot, with_ext }) | |
| 501 | 528 | else |
| 502 | try fs.path.join(self.allocator, &.{ dirname, with_ext }); | |
| 529 | with_ext; | |
| 530 | defer if (self.syslibroot) |_| self.allocator.free(full_path); | |
| 503 | 531 | |
| 504 | log.debug("trying dependency at fully resolved path {s}", .{lib_path}); | |
| 532 | log.debug("trying dependency at fully resolved path {s}", .{full_path}); | |
| 505 | 533 | |
| 506 | 534 | const dylibs = (try createAndParseFromPath( |
| 507 | 535 | self.allocator, |
| 508 | 536 | self.arch.?, |
| 509 | lib_path, | |
| 510 | self.syslibroot, | |
| 537 | full_path, | |
| 538 | .{ | |
| 539 | .id = id, | |
| 540 | .syslibroot = self.syslibroot, | |
| 541 | }, | |
| 511 | 542 | )) orelse { |
| 512 | 543 | continue; |
| 513 | 544 | }; |
| ... | ... | @@ -516,7 +547,7 @@ pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void { |
| 516 | 547 | |
| 517 | 548 | continue :outer; |
| 518 | 549 | } else { |
| 519 | log.warn("unable to resolve dependency {s}", .{lib}); | |
| 550 | log.warn("unable to resolve dependency {s}", .{id.name}); | |
| 520 | 551 | } |
| 521 | 552 | } |
| 522 | 553 | } |
src/link/MachO/Symbol.zig+2-1| ... | ... | @@ -111,7 +111,8 @@ pub const Unresolved = struct { |
| 111 | 111 | base: Symbol, |
| 112 | 112 | |
| 113 | 113 | /// File where this symbol was referenced. |
| 114 | file: *Object, | |
| 114 | /// null means synthetic, e.g., dyld_stub_binder. | |
| 115 | file: ?*Object = null, | |
| 115 | 116 | |
| 116 | 117 | pub const base_type: Symbol.Type = .unresolved; |
| 117 | 118 | }; |
src/link/MachO/Zld.zig+27-86| ... | ... | @@ -32,14 +32,12 @@ out_path: ?[]const u8 = null, |
| 32 | 32 | |
| 33 | 33 | // TODO these args will become obselete once Zld is coalesced with incremental |
| 34 | 34 | // linker. |
| 35 | syslibroot: ?[]const u8 = null, | |
| 36 | 35 | stack_size: u64 = 0, |
| 37 | 36 | |
| 38 | 37 | objects: std.ArrayListUnmanaged(*Object) = .{}, |
| 39 | 38 | archives: std.ArrayListUnmanaged(*Archive) = .{}, |
| 40 | 39 | dylibs: std.ArrayListUnmanaged(*Dylib) = .{}, |
| 41 | 40 | |
| 42 | libsystem_dylib_index: ?u16 = null, | |
| 43 | 41 | next_dylib_ordinal: u16 = 1, |
| 44 | 42 | |
| 45 | 43 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| ... | ... | @@ -197,9 +195,9 @@ pub fn closeFiles(self: Zld) void { |
| 197 | 195 | } |
| 198 | 196 | |
| 199 | 197 | const LinkArgs = struct { |
| 198 | syslibroot: ?[]const u8, | |
| 200 | 199 | libs: []const []const u8, |
| 201 | 200 | rpaths: []const []const u8, |
| 202 | libc_stub_path: []const u8, | |
| 203 | 201 | }; |
| 204 | 202 | |
| 205 | 203 | pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: LinkArgs) !void { |
| ... | ... | @@ -238,9 +236,8 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L |
| 238 | 236 | }); |
| 239 | 237 | |
| 240 | 238 | try self.populateMetadata(); |
| 241 | try self.parseInputFiles(files); | |
| 242 | try self.parseLibs(args.libs); | |
| 243 | try self.parseLibSystem(args.libc_stub_path); | |
| 239 | try self.parseInputFiles(files, args.syslibroot); | |
| 240 | try self.parseLibs(args.libs, args.syslibroot); | |
| 244 | 241 | try self.resolveSymbols(); |
| 245 | 242 | try self.resolveStubsAndGotEntries(); |
| 246 | 243 | try self.updateMetadata(); |
| ... | ... | @@ -258,7 +255,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L |
| 258 | 255 | try self.flush(); |
| 259 | 256 | } |
| 260 | 257 | |
| 261 | fn parseInputFiles(self: *Zld, files: []const []const u8) !void { | |
| 258 | fn parseInputFiles(self: *Zld, files: []const []const u8, syslibroot: ?[]const u8) !void { | |
| 262 | 259 | for (files) |file_name| { |
| 263 | 260 | const full_path = full_path: { |
| 264 | 261 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; |
| ... | ... | @@ -280,7 +277,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { |
| 280 | 277 | self.allocator, |
| 281 | 278 | self.arch.?, |
| 282 | 279 | full_path, |
| 283 | self.syslibroot, | |
| 280 | .{ .syslibroot = syslibroot }, | |
| 284 | 281 | )) |dylibs| { |
| 285 | 282 | defer self.allocator.free(dylibs); |
| 286 | 283 | try self.dylibs.appendSlice(self.allocator, dylibs); |
| ... | ... | @@ -291,13 +288,13 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { |
| 291 | 288 | } |
| 292 | 289 | } |
| 293 | 290 | |
| 294 | fn parseLibs(self: *Zld, libs: []const []const u8) !void { | |
| 291 | fn parseLibs(self: *Zld, libs: []const []const u8, syslibroot: ?[]const u8) !void { | |
| 295 | 292 | for (libs) |lib| { |
| 296 | 293 | if (try Dylib.createAndParseFromPath( |
| 297 | 294 | self.allocator, |
| 298 | 295 | self.arch.?, |
| 299 | 296 | lib, |
| 300 | self.syslibroot, | |
| 297 | .{ .syslibroot = syslibroot }, | |
| 301 | 298 | )) |dylibs| { |
| 302 | 299 | defer self.allocator.free(dylibs); |
| 303 | 300 | try self.dylibs.appendSlice(self.allocator, dylibs); |
| ... | ... | @@ -313,36 +310,6 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void { |
| 313 | 310 | } |
| 314 | 311 | } |
| 315 | 312 | |
| 316 | fn parseLibSystem(self: *Zld, libc_stub_path: []const u8) !void { | |
| 317 | const dylibs = (try Dylib.createAndParseFromPath( | |
| 318 | self.allocator, | |
| 319 | self.arch.?, | |
| 320 | libc_stub_path, | |
| 321 | self.syslibroot, | |
| 322 | )) orelse return error.FailedToParseLibSystem; | |
| 323 | defer self.allocator.free(dylibs); | |
| 324 | ||
| 325 | assert(dylibs.len == 1); // More than one dylib output from parsing libSystem! | |
| 326 | const dylib = dylibs[0]; | |
| 327 | ||
| 328 | self.libsystem_dylib_index = @intCast(u16, self.dylibs.items.len); | |
| 329 | try self.dylibs.append(self.allocator, dylib); | |
| 330 | ||
| 331 | // Add LC_LOAD_DYLIB load command. | |
| 332 | dylib.ordinal = self.next_dylib_ordinal; | |
| 333 | const dylib_id = dylib.id orelse unreachable; | |
| 334 | var dylib_cmd = try createLoadDylibCommand( | |
| 335 | self.allocator, | |
| 336 | dylib_id.name, | |
| 337 | dylib_id.timestamp, | |
| 338 | dylib_id.current_version, | |
| 339 | dylib_id.compatibility_version, | |
| 340 | ); | |
| 341 | errdefer dylib_cmd.deinit(self.allocator); | |
| 342 | try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd }); | |
| 343 | self.next_dylib_ordinal += 1; | |
| 344 | } | |
| 345 | ||
| 346 | 313 | fn mapAndUpdateSections( |
| 347 | 314 | self: *Zld, |
| 348 | 315 | object: *Object, |
| ... | ... | @@ -1656,17 +1623,30 @@ fn resolveSymbols(self: *Zld) !void { |
| 1656 | 1623 | } |
| 1657 | 1624 | self.unresolved.clearRetainingCapacity(); |
| 1658 | 1625 | |
| 1626 | // Put dyld_stub_binder as an unresolved special symbol. | |
| 1627 | { | |
| 1628 | const name = try self.allocator.dupe(u8, "dyld_stub_binder"); | |
| 1629 | errdefer self.allocator.free(name); | |
| 1630 | const undef = try self.allocator.create(Symbol.Unresolved); | |
| 1631 | errdefer self.allocator.destroy(undef); | |
| 1632 | undef.* = .{ | |
| 1633 | .base = .{ | |
| 1634 | .@"type" = .unresolved, | |
| 1635 | .name = name, | |
| 1636 | }, | |
| 1637 | }; | |
| 1638 | try unresolved.append(&undef.base); | |
| 1639 | } | |
| 1640 | ||
| 1659 | 1641 | var referenced = std.AutoHashMap(*Dylib, void).init(self.allocator); |
| 1660 | 1642 | defer referenced.deinit(); |
| 1661 | 1643 | |
| 1662 | 1644 | loop: while (unresolved.popOrNull()) |undef| { |
| 1663 | 1645 | const proxy = self.imports.get(undef.name) orelse outer: { |
| 1664 | 1646 | const proxy = inner: { |
| 1665 | for (self.dylibs.items) |dylib, i| { | |
| 1647 | for (self.dylibs.items) |dylib| { | |
| 1666 | 1648 | const proxy = (try dylib.createProxy(undef.name)) orelse continue; |
| 1667 | if (self.libsystem_dylib_index.? != @intCast(u16, i)) { // LibSystem gets load command seperately. | |
| 1668 | try referenced.put(dylib, {}); | |
| 1669 | } | |
| 1649 | try referenced.put(dylib, {}); | |
| 1670 | 1650 | break :inner proxy; |
| 1671 | 1651 | } |
| 1672 | 1652 | if (mem.eql(u8, undef.name, "___dso_handle")) { |
| ... | ... | @@ -1681,7 +1661,6 @@ fn resolveSymbols(self: *Zld) !void { |
| 1681 | 1661 | .@"type" = .proxy, |
| 1682 | 1662 | .name = name, |
| 1683 | 1663 | }, |
| 1684 | .file = null, | |
| 1685 | 1664 | }; |
| 1686 | 1665 | break :inner &proxy.base; |
| 1687 | 1666 | } |
| ... | ... | @@ -1717,21 +1696,13 @@ fn resolveSymbols(self: *Zld) !void { |
| 1717 | 1696 | if (self.unresolved.count() > 0) { |
| 1718 | 1697 | for (self.unresolved.values()) |undef| { |
| 1719 | 1698 | log.err("undefined reference to symbol '{s}'", .{undef.name}); |
| 1720 | log.err(" | referenced in {s}", .{ | |
| 1721 | undef.cast(Symbol.Unresolved).?.file.name.?, | |
| 1722 | }); | |
| 1699 | if (undef.cast(Symbol.Unresolved).?.file) |file| { | |
| 1700 | log.err(" | referenced in {s}", .{file.name.?}); | |
| 1701 | } | |
| 1723 | 1702 | } |
| 1724 | 1703 | |
| 1725 | 1704 | return error.UndefinedSymbolReference; |
| 1726 | 1705 | } |
| 1727 | ||
| 1728 | // Finally put dyld_stub_binder as an Import | |
| 1729 | const libsystem_dylib = self.dylibs.items[self.libsystem_dylib_index.?]; | |
| 1730 | const proxy = (try libsystem_dylib.createProxy("dyld_stub_binder")) orelse { | |
| 1731 | log.err("undefined reference to symbol 'dyld_stub_binder'", .{}); | |
| 1732 | return error.UndefinedSymbolReference; | |
| 1733 | }; | |
| 1734 | try self.imports.putNoClobber(self.allocator, proxy.name, proxy); | |
| 1735 | 1706 | } |
| 1736 | 1707 | |
| 1737 | 1708 | fn resolveStubsAndGotEntries(self: *Zld) !void { |
| ... | ... | @@ -3173,33 +3144,3 @@ pub fn parseName(name: *const [16]u8) []const u8 { |
| 3173 | 3144 | const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len; |
| 3174 | 3145 | return name[0..len]; |
| 3175 | 3146 | } |
| 3176 | ||
| 3177 | fn printSymbols(self: *Zld) void { | |
| 3178 | log.debug("globals", .{}); | |
| 3179 | for (self.globals.values()) |value| { | |
| 3180 | const sym = value.cast(Symbol.Regular) orelse unreachable; | |
| 3181 | log.debug(" | {s} @ {*}", .{ sym.base.name, value }); | |
| 3182 | log.debug(" => alias of {*}", .{sym.base.alias}); | |
| 3183 | log.debug(" => linkage {s}", .{sym.linkage}); | |
| 3184 | log.debug(" => defined in {s}", .{sym.file.name.?}); | |
| 3185 | } | |
| 3186 | for (self.objects.items) |object| { | |
| 3187 | log.debug("locals in {s}", .{object.name.?}); | |
| 3188 | for (object.symbols.items) |sym| { | |
| 3189 | log.debug(" | {s} @ {*}", .{ sym.name, sym }); | |
| 3190 | log.debug(" => alias of {*}", .{sym.alias}); | |
| 3191 | if (sym.cast(Symbol.Regular)) |reg| { | |
| 3192 | log.debug(" => linkage {s}", .{reg.linkage}); | |
| 3193 | } else { | |
| 3194 | log.debug(" => unresolved", .{}); | |
| 3195 | } | |
| 3196 | } | |
| 3197 | } | |
| 3198 | log.debug("proxies", .{}); | |
| 3199 | for (self.imports.values()) |value| { | |
| 3200 | const sym = value.cast(Symbol.Proxy) orelse unreachable; | |
| 3201 | log.debug(" | {s} @ {*}", .{ sym.base.name, value }); | |
| 3202 | log.debug(" => alias of {*}", .{sym.base.alias}); | |
| 3203 | log.debug(" => defined in libSystem.B.dylib", .{}); | |
| 3204 | } | |
| 3205 | } |