| author | |
| committer | |
| log | 7588fcdd2a7c232286f2c298bd14cbfb65e54604 |
| tree | 5f2a7bf072f90ee1e95e508307642a97a9e172c0 |
| parent | 35c694d614c8687ffa99ac187e60d831f9d7f29d |
| parent | cb45c5521ab5360e2b8c5d1ce16d351d526a3fe5 |
| signature |
zig ld: add preliminary mechanism for linking dylibs8 files changed, 617 insertions(+), 99 deletions(-)
CMakeLists.txt+1| ... | @@ -568,6 +568,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -568,6 +568,7 @@ set(ZIG_STAGE2_SOURCES |
| 568 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig" | 568 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig" |
| 569 | "${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig" | 569 | "${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig" |
| 570 | "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig" | 570 | "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig" |
| 571 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig" | ||
| 571 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig" | 572 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig" |
| 572 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Symbol.zig" | 573 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Symbol.zig" |
| 573 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig" | 574 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig" |
lib/std/macho.zig+45| ... | @@ -71,6 +71,38 @@ pub const source_version_command = extern struct { | ... | @@ -71,6 +71,38 @@ pub const source_version_command = extern struct { |
| 71 | version: u64, | 71 | version: u64, |
| 72 | }; | 72 | }; |
| 73 | 73 | ||
| 74 | /// The build_version_command contains the min OS version on which this | ||
| 75 | /// binary was built to run for its platform. The list of known platforms and | ||
| 76 | /// tool values following it. | ||
| 77 | pub const build_version_command = extern struct { | ||
| 78 | /// LC_BUILD_VERSION | ||
| 79 | cmd: u32, | ||
| 80 | |||
| 81 | /// sizeof(struct build_version_command) plus | ||
| 82 | /// ntools * sizeof(struct build_version_command) | ||
| 83 | cmdsize: u32, | ||
| 84 | |||
| 85 | /// platform | ||
| 86 | platform: u32, | ||
| 87 | |||
| 88 | /// X.Y.Z is encoded in nibbles xxxx.yy.zz | ||
| 89 | minos: u32, | ||
| 90 | |||
| 91 | /// X.Y.Z is encoded in nibbles xxxx.yy.zz | ||
| 92 | sdk: u32, | ||
| 93 | |||
| 94 | /// number of tool entries following this | ||
| 95 | ntools: u32, | ||
| 96 | }; | ||
| 97 | |||
| 98 | pub const build_tool_version = extern struct { | ||
| 99 | /// enum for the tool | ||
| 100 | tool: u32, | ||
| 101 | |||
| 102 | /// version number of the tool | ||
| 103 | version: u32, | ||
| 104 | }; | ||
| 105 | |||
| 74 | /// The entry_point_command is a replacement for thread_command. | 106 | /// The entry_point_command is a replacement for thread_command. |
| 75 | /// It is used for main executables to specify the location (file offset) | 107 | /// It is used for main executables to specify the location (file offset) |
| 76 | /// of main(). If -stack_size was used at link time, the stacksize | 108 | /// of main(). If -stack_size was used at link time, the stacksize |
| ... | @@ -484,6 +516,19 @@ pub const dylib = extern struct { | ... | @@ -484,6 +516,19 @@ pub const dylib = extern struct { |
| 484 | compatibility_version: u32, | 516 | compatibility_version: u32, |
| 485 | }; | 517 | }; |
| 486 | 518 | ||
| 519 | /// The rpath_command contains a path which at runtime should be added to the current | ||
| 520 | /// run path used to find @rpath prefixed dylibs. | ||
| 521 | pub const rpath_command = extern struct { | ||
| 522 | /// LC_RPATH | ||
| 523 | cmd: u32, | ||
| 524 | |||
| 525 | /// includes string | ||
| 526 | cmdsize: u32, | ||
| 527 | |||
| 528 | /// path to add to run path | ||
| 529 | path: u32, | ||
| 530 | }; | ||
| 531 | |||
| 487 | /// The segment load command indicates that a part of this file is to be | 532 | /// The segment load command indicates that a part of this file is to be |
| 488 | /// mapped into the task's address space. The size of this segment in memory, | 533 | /// mapped into the task's address space. The size of this segment in memory, |
| 489 | /// vmsize, maybe equal to or larger than the amount to map from this file, | 534 | /// vmsize, maybe equal to or larger than the amount to map from this file, |
src/link/MachO.zig+147-36| ... | @@ -548,7 +548,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -548,7 +548,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 548 | const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; | 548 | const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; |
| 549 | const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; | 549 | const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; |
| 550 | const target = self.base.options.target; | 550 | const target = self.base.options.target; |
| 551 | const stack_size = self.base.options.stack_size_override orelse 16777216; | 551 | const stack_size = self.base.options.stack_size_override orelse 0; |
| 552 | const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os; | 552 | const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os; |
| 553 | 553 | ||
| 554 | const id_symlink_basename = "lld.id"; | 554 | const id_symlink_basename = "lld.id"; |
| ... | @@ -675,41 +675,168 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -675,41 +675,168 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 675 | zld.deinit(); | 675 | zld.deinit(); |
| 676 | } | 676 | } |
| 677 | zld.arch = target.cpu.arch; | 677 | zld.arch = target.cpu.arch; |
| 678 | zld.stack_size = stack_size; | ||
| 679 | |||
| 680 | // Positional arguments to the linker such as object files and static archives. | ||
| 681 | var positionals = std.ArrayList([]const u8).init(arena); | ||
| 682 | |||
| 683 | try positionals.appendSlice(self.base.options.objects); | ||
| 678 | 684 | ||
| 679 | var input_files = std.ArrayList([]const u8).init(self.base.allocator); | ||
| 680 | defer input_files.deinit(); | ||
| 681 | // Positional arguments to the linker such as object files. | ||
| 682 | try input_files.appendSlice(self.base.options.objects); | ||
| 683 | for (comp.c_object_table.items()) |entry| { | 685 | for (comp.c_object_table.items()) |entry| { |
| 684 | try input_files.append(entry.key.status.success.object_path); | 686 | try positionals.append(entry.key.status.success.object_path); |
| 685 | } | 687 | } |
| 688 | |||
| 686 | if (module_obj_path) |p| { | 689 | if (module_obj_path) |p| { |
| 687 | try input_files.append(p); | 690 | try positionals.append(p); |
| 688 | } | 691 | } |
| 689 | try input_files.append(comp.compiler_rt_static_lib.?.full_object_path); | 692 | |
| 693 | try positionals.append(comp.compiler_rt_static_lib.?.full_object_path); | ||
| 694 | |||
| 690 | // libc++ dep | 695 | // libc++ dep |
| 691 | if (self.base.options.link_libcpp) { | 696 | if (self.base.options.link_libcpp) { |
| 692 | try input_files.append(comp.libcxxabi_static_lib.?.full_object_path); | 697 | try positionals.append(comp.libcxxabi_static_lib.?.full_object_path); |
| 693 | try input_files.append(comp.libcxx_static_lib.?.full_object_path); | 698 | try positionals.append(comp.libcxx_static_lib.?.full_object_path); |
| 699 | } | ||
| 700 | |||
| 701 | // Shared libraries. | ||
| 702 | var shared_libs = std.ArrayList([]const u8).init(arena); | ||
| 703 | var search_lib_names = std.ArrayList([]const u8).init(arena); | ||
| 704 | |||
| 705 | const system_libs = self.base.options.system_libs.items(); | ||
| 706 | for (system_libs) |entry| { | ||
| 707 | const link_lib = entry.key; | ||
| 708 | // By this time, we depend on these libs being dynamically linked libraries and not static libraries | ||
| 709 | // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which | ||
| 710 | // case we want to avoid prepending "-l". | ||
| 711 | // TODO I think they should go as an input file instead of via shared_libs. | ||
| 712 | if (Compilation.classifyFileExt(link_lib) == .shared_library) { | ||
| 713 | try shared_libs.append(link_lib); | ||
| 714 | continue; | ||
| 715 | } | ||
| 716 | |||
| 717 | try search_lib_names.append(link_lib); | ||
| 718 | } | ||
| 719 | |||
| 720 | var search_lib_dirs = std.ArrayList([]const u8).init(arena); | ||
| 721 | |||
| 722 | for (self.base.options.lib_dirs) |path| { | ||
| 723 | if (fs.path.isAbsolute(path)) { | ||
| 724 | var candidates = std.ArrayList([]const u8).init(arena); | ||
| 725 | if (self.base.options.syslibroot) |syslibroot| { | ||
| 726 | const full_path = try fs.path.join(arena, &[_][]const u8{ syslibroot, path }); | ||
| 727 | try candidates.append(full_path); | ||
| 728 | } | ||
| 729 | try candidates.append(path); | ||
| 730 | |||
| 731 | var found = false; | ||
| 732 | for (candidates.items) |candidate| { | ||
| 733 | // Verify that search path actually exists | ||
| 734 | var tmp = fs.cwd().openDir(candidate, .{}) catch |err| switch (err) { | ||
| 735 | error.FileNotFound => continue, | ||
| 736 | else => |e| return e, | ||
| 737 | }; | ||
| 738 | defer tmp.close(); | ||
| 739 | |||
| 740 | try search_lib_dirs.append(candidate); | ||
| 741 | found = true; | ||
| 742 | break; | ||
| 743 | } | ||
| 744 | |||
| 745 | if (!found) { | ||
| 746 | log.warn("directory not found for '-L{s}'", .{path}); | ||
| 747 | } | ||
| 748 | } else { | ||
| 749 | // Verify that search path actually exists | ||
| 750 | var tmp = fs.cwd().openDir(path, .{}) catch |err| switch (err) { | ||
| 751 | error.FileNotFound => { | ||
| 752 | log.warn("directory not found for '-L{s}'", .{path}); | ||
| 753 | continue; | ||
| 754 | }, | ||
| 755 | else => |e| return e, | ||
| 756 | }; | ||
| 757 | defer tmp.close(); | ||
| 758 | |||
| 759 | try search_lib_dirs.append(path); | ||
| 760 | } | ||
| 761 | } | ||
| 762 | |||
| 763 | for (search_lib_names.items) |l_name| { | ||
| 764 | // TODO text-based API, or .tbd files. | ||
| 765 | const l_name_ext = try std.fmt.allocPrint(arena, "lib{s}.dylib", .{l_name}); | ||
| 766 | |||
| 767 | var found = false; | ||
| 768 | for (search_lib_dirs.items) |lib_dir| { | ||
| 769 | const full_path = try fs.path.join(arena, &[_][]const u8{ lib_dir, l_name_ext }); | ||
| 770 | |||
| 771 | // Check if the dylib file exists. | ||
| 772 | const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { | ||
| 773 | error.FileNotFound => continue, | ||
| 774 | else => |e| return e, | ||
| 775 | }; | ||
| 776 | defer tmp.close(); | ||
| 777 | |||
| 778 | try shared_libs.append(full_path); | ||
| 779 | found = true; | ||
| 780 | break; | ||
| 781 | } | ||
| 782 | |||
| 783 | if (!found) { | ||
| 784 | log.warn("library not found for '-l{s}'", .{l_name}); | ||
| 785 | log.warn("Library search paths:", .{}); | ||
| 786 | for (search_lib_dirs.items) |lib_dir| { | ||
| 787 | log.warn(" {s}", .{lib_dir}); | ||
| 788 | } | ||
| 789 | } | ||
| 790 | } | ||
| 791 | |||
| 792 | // rpaths | ||
| 793 | var rpath_table = std.StringArrayHashMap(void).init(arena); | ||
| 794 | for (self.base.options.rpath_list) |rpath| { | ||
| 795 | if (rpath_table.contains(rpath)) continue; | ||
| 796 | try rpath_table.putNoClobber(rpath, {}); | ||
| 797 | } | ||
| 798 | |||
| 799 | var rpaths = std.ArrayList([]const u8).init(arena); | ||
| 800 | try rpaths.ensureCapacity(rpath_table.count()); | ||
| 801 | for (rpath_table.items()) |entry| { | ||
| 802 | rpaths.appendAssumeCapacity(entry.key); | ||
| 694 | } | 803 | } |
| 695 | 804 | ||
| 696 | if (self.base.options.verbose_link) { | 805 | if (self.base.options.verbose_link) { |
| 697 | var argv = std.ArrayList([]const u8).init(self.base.allocator); | 806 | var argv = std.ArrayList([]const u8).init(arena); |
| 698 | defer argv.deinit(); | ||
| 699 | 807 | ||
| 700 | try argv.append("zig"); | 808 | try argv.append("zig"); |
| 701 | try argv.append("ld"); | 809 | try argv.append("ld"); |
| 702 | 810 | ||
| 703 | try argv.appendSlice(input_files.items); | 811 | if (self.base.options.syslibroot) |syslibroot| { |
| 812 | try argv.append("-syslibroot"); | ||
| 813 | try argv.append(syslibroot); | ||
| 814 | } | ||
| 815 | |||
| 816 | for (rpaths.items) |rpath| { | ||
| 817 | try argv.append("-rpath"); | ||
| 818 | try argv.append(rpath); | ||
| 819 | } | ||
| 820 | |||
| 821 | try argv.appendSlice(positionals.items); | ||
| 704 | 822 | ||
| 705 | try argv.append("-o"); | 823 | try argv.append("-o"); |
| 706 | try argv.append(full_out_path); | 824 | try argv.append(full_out_path); |
| 707 | 825 | ||
| 826 | for (search_lib_names.items) |l_name| { | ||
| 827 | try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name})); | ||
| 828 | } | ||
| 829 | |||
| 830 | for (self.base.options.lib_dirs) |lib_dir| { | ||
| 831 | try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir})); | ||
| 832 | } | ||
| 833 | |||
| 708 | Compilation.dump_argv(argv.items); | 834 | Compilation.dump_argv(argv.items); |
| 709 | } | 835 | } |
| 710 | 836 | ||
| 711 | try zld.link(input_files.items, full_out_path, .{ | 837 | try zld.link(positionals.items, full_out_path, .{ |
| 712 | .stack_size = self.base.options.stack_size_override, | 838 | .shared_libs = shared_libs.items, |
| 839 | .rpaths = rpaths.items, | ||
| 713 | }); | 840 | }); |
| 714 | 841 | ||
| 715 | break :outer; | 842 | break :outer; |
| ... | @@ -1993,28 +2120,12 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1993,28 +2120,12 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1993 | } | 2120 | } |
| 1994 | if (self.libsystem_cmd_index == null) { | 2121 | if (self.libsystem_cmd_index == null) { |
| 1995 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); | 2122 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1996 | const cmdsize = @intCast(u32, mem.alignForwardGeneric( | 2123 | |
| 1997 | u64, | 2124 | var dylib_cmd = try createLoadDylibCommand(self.base.allocator, mem.spanZ(LIB_SYSTEM_PATH), 2, 0, 0); |
| 1998 | @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH), | 2125 | errdefer dylib_cmd.deinit(self.base.allocator); |
| 1999 | @sizeOf(u64), | 2126 | |
| 2000 | )); | ||
| 2001 | // TODO Find a way to work out runtime version from the OS version triple stored in std.Target. | ||
| 2002 | // In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0. | ||
| 2003 | const min_version = 0x0; | ||
| 2004 | var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{ | ||
| 2005 | .cmd = macho.LC_LOAD_DYLIB, | ||
| 2006 | .cmdsize = cmdsize, | ||
| 2007 | .dylib = .{ | ||
| 2008 | .name = @sizeOf(macho.dylib_command), | ||
| 2009 | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files | ||
| 2010 | .current_version = min_version, | ||
| 2011 | .compatibility_version = min_version, | ||
| 2012 | }, | ||
| 2013 | }); | ||
| 2014 | dylib_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name); | ||
| 2015 | mem.set(u8, dylib_cmd.data, 0); | ||
| 2016 | mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH)); | ||
| 2017 | try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); | 2127 | try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); |
| 2128 | |||
| 2018 | self.header_dirty = true; | 2129 | self.header_dirty = true; |
| 2019 | self.load_commands_dirty = true; | 2130 | self.load_commands_dirty = true; |
| 2020 | } | 2131 | } |
src/link/MachO/Dylib.zig created+185| ... | @@ -0,0 +1,185 @@ | ||
| 1 | const Dylib = @This(); | ||
| 2 | |||
| 3 | const std = @import("std"); | ||
| 4 | const fs = std.fs; | ||
| 5 | const log = std.log.scoped(.dylib); | ||
| 6 | const macho = std.macho; | ||
| 7 | const mem = std.mem; | ||
| 8 | |||
| 9 | const Allocator = mem.Allocator; | ||
| 10 | const Symbol = @import("Symbol.zig"); | ||
| 11 | |||
| 12 | usingnamespace @import("commands.zig"); | ||
| 13 | |||
| 14 | allocator: *Allocator, | ||
| 15 | arch: ?std.Target.Cpu.Arch = null, | ||
| 16 | header: ?macho.mach_header_64 = null, | ||
| 17 | file: ?fs.File = null, | ||
| 18 | name: ?[]const u8 = null, | ||
| 19 | |||
| 20 | ordinal: ?u16 = null, | ||
| 21 | |||
| 22 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, | ||
| 23 | |||
| 24 | symtab_cmd_index: ?u16 = null, | ||
| 25 | dysymtab_cmd_index: ?u16 = null, | ||
| 26 | id_cmd_index: ?u16 = null, | ||
| 27 | |||
| 28 | id: ?Id = null, | ||
| 29 | |||
| 30 | symbols: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, | ||
| 31 | |||
| 32 | pub const Id = struct { | ||
| 33 | name: []const u8, | ||
| 34 | timestamp: u32, | ||
| 35 | current_version: u32, | ||
| 36 | compatibility_version: u32, | ||
| 37 | |||
| 38 | pub fn deinit(id: *Id, allocator: *Allocator) void { | ||
| 39 | allocator.free(id.name); | ||
| 40 | } | ||
| 41 | }; | ||
| 42 | |||
| 43 | pub fn init(allocator: *Allocator) Dylib { | ||
| 44 | return .{ .allocator = allocator }; | ||
| 45 | } | ||
| 46 | |||
| 47 | pub fn deinit(self: *Dylib) void { | ||
| 48 | for (self.load_commands.items) |*lc| { | ||
| 49 | lc.deinit(self.allocator); | ||
| 50 | } | ||
| 51 | self.load_commands.deinit(self.allocator); | ||
| 52 | |||
| 53 | for (self.symbols.items()) |entry| { | ||
| 54 | entry.value.deinit(self.allocator); | ||
| 55 | self.allocator.destroy(entry.value); | ||
| 56 | } | ||
| 57 | self.symbols.deinit(self.allocator); | ||
| 58 | |||
| 59 | if (self.name) |name| { | ||
| 60 | self.allocator.free(name); | ||
| 61 | } | ||
| 62 | |||
| 63 | if (self.id) |*id| { | ||
| 64 | id.deinit(self.allocator); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | pub fn closeFile(self: Dylib) void { | ||
| 69 | if (self.file) |file| { | ||
| 70 | file.close(); | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | pub fn parse(self: *Dylib) !void { | ||
| 75 | log.debug("parsing shared library '{s}'", .{self.name.?}); | ||
| 76 | |||
| 77 | var reader = self.file.?.reader(); | ||
| 78 | self.header = try reader.readStruct(macho.mach_header_64); | ||
| 79 | |||
| 80 | if (self.header.?.filetype != macho.MH_DYLIB) { | ||
| 81 | log.err("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, self.header.?.filetype }); | ||
| 82 | return error.MalformedDylib; | ||
| 83 | } | ||
| 84 | |||
| 85 | const this_arch: std.Target.Cpu.Arch = switch (self.header.?.cputype) { | ||
| 86 | macho.CPU_TYPE_ARM64 => .aarch64, | ||
| 87 | macho.CPU_TYPE_X86_64 => .x86_64, | ||
| 88 | else => |value| { | ||
| 89 | log.err("unsupported cpu architecture 0x{x}", .{value}); | ||
| 90 | return error.UnsupportedCpuArchitecture; | ||
| 91 | }, | ||
| 92 | }; | ||
| 93 | if (this_arch != self.arch.?) { | ||
| 94 | log.err("mismatched cpu architecture: expected {s}, found {s}", .{ self.arch.?, this_arch }); | ||
| 95 | return error.MismatchedCpuArchitecture; | ||
| 96 | } | ||
| 97 | |||
| 98 | try self.readLoadCommands(reader); | ||
| 99 | try self.parseId(); | ||
| 100 | try self.parseSymbols(); | ||
| 101 | } | ||
| 102 | |||
| 103 | pub fn readLoadCommands(self: *Dylib, reader: anytype) !void { | ||
| 104 | try self.load_commands.ensureCapacity(self.allocator, self.header.?.ncmds); | ||
| 105 | |||
| 106 | var i: u16 = 0; | ||
| 107 | while (i < self.header.?.ncmds) : (i += 1) { | ||
| 108 | var cmd = try LoadCommand.read(self.allocator, reader); | ||
| 109 | switch (cmd.cmd()) { | ||
| 110 | macho.LC_SYMTAB => { | ||
| 111 | self.symtab_cmd_index = i; | ||
| 112 | }, | ||
| 113 | macho.LC_DYSYMTAB => { | ||
| 114 | self.dysymtab_cmd_index = i; | ||
| 115 | }, | ||
| 116 | macho.LC_ID_DYLIB => { | ||
| 117 | self.id_cmd_index = i; | ||
| 118 | }, | ||
| 119 | else => { | ||
| 120 | log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()}); | ||
| 121 | }, | ||
| 122 | } | ||
| 123 | self.load_commands.appendAssumeCapacity(cmd); | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | pub fn parseId(self: *Dylib) !void { | ||
| 128 | const index = self.id_cmd_index orelse { | ||
| 129 | log.debug("no LC_ID_DYLIB load command found; using hard-coded defaults...", .{}); | ||
| 130 | self.id = .{ | ||
| 131 | .name = try self.allocator.dupe(u8, self.name.?), | ||
| 132 | .timestamp = 2, | ||
| 133 | .current_version = 0, | ||
| 134 | .compatibility_version = 0, | ||
| 135 | }; | ||
| 136 | return; | ||
| 137 | }; | ||
| 138 | const id_cmd = self.load_commands.items[index].Dylib; | ||
| 139 | const dylib = id_cmd.inner.dylib; | ||
| 140 | |||
| 141 | // TODO should we compare the name from the dylib's id with the user-specified one? | ||
| 142 | const dylib_name = @ptrCast([*:0]const u8, id_cmd.data[dylib.name - @sizeOf(macho.dylib_command) ..]); | ||
| 143 | const name = try self.allocator.dupe(u8, mem.spanZ(dylib_name)); | ||
| 144 | |||
| 145 | self.id = .{ | ||
| 146 | .name = name, | ||
| 147 | .timestamp = dylib.timestamp, | ||
| 148 | .current_version = dylib.current_version, | ||
| 149 | .compatibility_version = dylib.compatibility_version, | ||
| 150 | }; | ||
| 151 | } | ||
| 152 | |||
| 153 | pub fn parseSymbols(self: *Dylib) !void { | ||
| 154 | const index = self.symtab_cmd_index orelse return; | ||
| 155 | const symtab_cmd = self.load_commands.items[index].Symtab; | ||
| 156 | |||
| 157 | var symtab = try self.allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms); | ||
| 158 | defer self.allocator.free(symtab); | ||
| 159 | _ = try self.file.?.preadAll(symtab, symtab_cmd.symoff); | ||
| 160 | const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, symtab)); | ||
| 161 | |||
| 162 | var strtab = try self.allocator.alloc(u8, symtab_cmd.strsize); | ||
| 163 | defer self.allocator.free(strtab); | ||
| 164 | _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff); | ||
| 165 | |||
| 166 | for (slice) |sym| { | ||
| 167 | const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx)); | ||
| 168 | |||
| 169 | if (!(Symbol.isSect(sym) and Symbol.isExt(sym))) continue; | ||
| 170 | |||
| 171 | const name = try self.allocator.dupe(u8, sym_name); | ||
| 172 | const proxy = try self.allocator.create(Symbol.Proxy); | ||
| 173 | errdefer self.allocator.destroy(proxy); | ||
| 174 | |||
| 175 | proxy.* = .{ | ||
| 176 | .base = .{ | ||
| 177 | .@"type" = .proxy, | ||
| 178 | .name = name, | ||
| 179 | }, | ||
| 180 | .dylib = self, | ||
| 181 | }; | ||
| 182 | |||
| 183 | try self.symbols.putNoClobber(self.allocator, name, &proxy.base); | ||
| 184 | } | ||
| 185 | } | ||
src/link/MachO/Symbol.zig+4-3| ... | @@ -5,6 +5,7 @@ const macho = std.macho; | ... | @@ -5,6 +5,7 @@ const macho = std.macho; |
| 5 | const mem = std.mem; | 5 | const mem = std.mem; |
| 6 | 6 | ||
| 7 | const Allocator = mem.Allocator; | 7 | const Allocator = mem.Allocator; |
| 8 | const Dylib = @import("Dylib.zig"); | ||
| 8 | const Object = @import("Object.zig"); | 9 | const Object = @import("Object.zig"); |
| 9 | 10 | ||
| 10 | pub const Type = enum { | 11 | pub const Type = enum { |
| ... | @@ -43,7 +44,7 @@ pub const Regular = struct { | ... | @@ -43,7 +44,7 @@ pub const Regular = struct { |
| 43 | /// Whether the symbol is a weak ref. | 44 | /// Whether the symbol is a weak ref. |
| 44 | weak_ref: bool, | 45 | weak_ref: bool, |
| 45 | 46 | ||
| 46 | /// File where to locate this symbol. | 47 | /// Object file where to locate this symbol. |
| 47 | file: *Object, | 48 | file: *Object, |
| 48 | 49 | ||
| 49 | /// Debug stab if defined. | 50 | /// Debug stab if defined. |
| ... | @@ -78,8 +79,8 @@ pub const Regular = struct { | ... | @@ -78,8 +79,8 @@ pub const Regular = struct { |
| 78 | pub const Proxy = struct { | 79 | pub const Proxy = struct { |
| 79 | base: Symbol, | 80 | base: Symbol, |
| 80 | 81 | ||
| 81 | /// Dylib ordinal. | 82 | /// Dylib where to locate this symbol. |
| 82 | dylib: u16, | 83 | dylib: ?*Dylib = null, |
| 83 | 84 | ||
| 84 | pub const base_type: Symbol.Type = .proxy; | 85 | pub const base_type: Symbol.Type = .proxy; |
| 85 | }; | 86 | }; |
src/link/MachO/Zld.zig+173-51| ... | @@ -15,6 +15,7 @@ const reloc = @import("reloc.zig"); | ... | @@ -15,6 +15,7 @@ const reloc = @import("reloc.zig"); |
| 15 | const Allocator = mem.Allocator; | 15 | const Allocator = mem.Allocator; |
| 16 | const Archive = @import("Archive.zig"); | 16 | const Archive = @import("Archive.zig"); |
| 17 | const CodeSignature = @import("CodeSignature.zig"); | 17 | const CodeSignature = @import("CodeSignature.zig"); |
| 18 | const Dylib = @import("Dylib.zig"); | ||
| 18 | const Object = @import("Object.zig"); | 19 | const Object = @import("Object.zig"); |
| 19 | const Symbol = @import("Symbol.zig"); | 20 | const Symbol = @import("Symbol.zig"); |
| 20 | const Trie = @import("Trie.zig"); | 21 | const Trie = @import("Trie.zig"); |
| ... | @@ -35,6 +36,7 @@ stack_size: u64 = 0, | ... | @@ -35,6 +36,7 @@ stack_size: u64 = 0, |
| 35 | 36 | ||
| 36 | objects: std.ArrayListUnmanaged(*Object) = .{}, | 37 | objects: std.ArrayListUnmanaged(*Object) = .{}, |
| 37 | archives: std.ArrayListUnmanaged(*Archive) = .{}, | 38 | archives: std.ArrayListUnmanaged(*Archive) = .{}, |
| 39 | dylibs: std.ArrayListUnmanaged(*Dylib) = .{}, | ||
| 38 | 40 | ||
| 39 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, | 41 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| 40 | 42 | ||
| ... | @@ -151,10 +153,18 @@ pub fn deinit(self: *Zld) void { | ... | @@ -151,10 +153,18 @@ pub fn deinit(self: *Zld) void { |
| 151 | } | 153 | } |
| 152 | self.archives.deinit(self.allocator); | 154 | self.archives.deinit(self.allocator); |
| 153 | 155 | ||
| 156 | for (self.dylibs.items) |dylib| { | ||
| 157 | dylib.deinit(); | ||
| 158 | self.allocator.destroy(dylib); | ||
| 159 | } | ||
| 160 | self.dylibs.deinit(self.allocator); | ||
| 161 | |||
| 154 | self.mappings.deinit(self.allocator); | 162 | self.mappings.deinit(self.allocator); |
| 155 | self.unhandled_sections.deinit(self.allocator); | 163 | self.unhandled_sections.deinit(self.allocator); |
| 156 | 164 | ||
| 157 | self.globals.deinit(self.allocator); | 165 | self.globals.deinit(self.allocator); |
| 166 | self.imports.deinit(self.allocator); | ||
| 167 | self.unresolved.deinit(self.allocator); | ||
| 158 | self.strtab.deinit(self.allocator); | 168 | self.strtab.deinit(self.allocator); |
| 159 | 169 | ||
| 160 | { | 170 | { |
| ... | @@ -177,7 +187,8 @@ pub fn closeFiles(self: Zld) void { | ... | @@ -177,7 +187,8 @@ pub fn closeFiles(self: Zld) void { |
| 177 | } | 187 | } |
| 178 | 188 | ||
| 179 | const LinkArgs = struct { | 189 | const LinkArgs = struct { |
| 180 | stack_size: ?u64 = null, | 190 | shared_libs: []const []const u8, |
| 191 | rpaths: []const []const u8, | ||
| 181 | }; | 192 | }; |
| 182 | 193 | ||
| 183 | pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: LinkArgs) !void { | 194 | pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: LinkArgs) !void { |
| ... | @@ -214,10 +225,11 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L | ... | @@ -214,10 +225,11 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L |
| 214 | .read = true, | 225 | .read = true, |
| 215 | .mode = if (std.Target.current.os.tag == .windows) 0 else 0o777, | 226 | .mode = if (std.Target.current.os.tag == .windows) 0 else 0o777, |
| 216 | }); | 227 | }); |
| 217 | self.stack_size = args.stack_size orelse 0; | ||
| 218 | 228 | ||
| 219 | try self.populateMetadata(); | 229 | try self.populateMetadata(); |
| 230 | try self.addRpaths(args.rpaths); | ||
| 220 | try self.parseInputFiles(files); | 231 | try self.parseInputFiles(files); |
| 232 | try self.parseDylibs(args.shared_libs); | ||
| 221 | try self.resolveSymbols(); | 233 | try self.resolveSymbols(); |
| 222 | try self.resolveStubsAndGotEntries(); | 234 | try self.resolveStubsAndGotEntries(); |
| 223 | try self.updateMetadata(); | 235 | try self.updateMetadata(); |
| ... | @@ -235,6 +247,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { | ... | @@ -235,6 +247,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { |
| 235 | kind: enum { | 247 | kind: enum { |
| 236 | object, | 248 | object, |
| 237 | archive, | 249 | archive, |
| 250 | dylib, | ||
| 238 | }, | 251 | }, |
| 239 | file: fs.File, | 252 | file: fs.File, |
| 240 | name: []const u8, | 253 | name: []const u8, |
| ... | @@ -242,7 +255,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { | ... | @@ -242,7 +255,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { |
| 242 | var classified = std.ArrayList(Input).init(self.allocator); | 255 | var classified = std.ArrayList(Input).init(self.allocator); |
| 243 | defer classified.deinit(); | 256 | defer classified.deinit(); |
| 244 | 257 | ||
| 245 | // First, classify input files as either object or archive. | 258 | // First, classify input files: object, archive or dylib. |
| 246 | for (files) |file_name| { | 259 | for (files) |file_name| { |
| 247 | const file = try fs.cwd().openFile(file_name, .{}); | 260 | const file = try fs.cwd().openFile(file_name, .{}); |
| 248 | const full_path = full_path: { | 261 | const full_path = full_path: { |
| ... | @@ -283,6 +296,22 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { | ... | @@ -283,6 +296,22 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { |
| 283 | continue; | 296 | continue; |
| 284 | } | 297 | } |
| 285 | 298 | ||
| 299 | try_dylib: { | ||
| 300 | const header = try file.reader().readStruct(macho.mach_header_64); | ||
| 301 | if (header.filetype != macho.MH_DYLIB) { | ||
| 302 | try file.seekTo(0); | ||
| 303 | break :try_dylib; | ||
| 304 | } | ||
| 305 | |||
| 306 | try file.seekTo(0); | ||
| 307 | try classified.append(.{ | ||
| 308 | .kind = .dylib, | ||
| 309 | .file = file, | ||
| 310 | .name = full_path, | ||
| 311 | }); | ||
| 312 | continue; | ||
| 313 | } | ||
| 314 | |||
| 286 | log.debug("unexpected input file of unknown type '{s}'", .{file_name}); | 315 | log.debug("unexpected input file of unknown type '{s}'", .{file_name}); |
| 287 | } | 316 | } |
| 288 | 317 | ||
| ... | @@ -311,10 +340,71 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { | ... | @@ -311,10 +340,71 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { |
| 311 | try archive.parse(); | 340 | try archive.parse(); |
| 312 | try self.archives.append(self.allocator, archive); | 341 | try self.archives.append(self.allocator, archive); |
| 313 | }, | 342 | }, |
| 343 | .dylib => { | ||
| 344 | const dylib = try self.allocator.create(Dylib); | ||
| 345 | errdefer self.allocator.destroy(dylib); | ||
| 346 | |||
| 347 | dylib.* = Dylib.init(self.allocator); | ||
| 348 | dylib.arch = self.arch.?; | ||
| 349 | dylib.name = input.name; | ||
| 350 | dylib.file = input.file; | ||
| 351 | |||
| 352 | const ordinal = @intCast(u16, self.dylibs.items.len); | ||
| 353 | dylib.ordinal = ordinal + 2; // TODO +2 since 1 is reserved for libSystem | ||
| 354 | |||
| 355 | // TODO Defer parsing of the dylibs until they are actually needed | ||
| 356 | try dylib.parse(); | ||
| 357 | try self.dylibs.append(self.allocator, dylib); | ||
| 358 | |||
| 359 | // Add LC_LOAD_DYLIB command | ||
| 360 | const dylib_id = dylib.id orelse unreachable; | ||
| 361 | var dylib_cmd = try createLoadDylibCommand( | ||
| 362 | self.allocator, | ||
| 363 | dylib_id.name, | ||
| 364 | dylib_id.timestamp, | ||
| 365 | dylib_id.current_version, | ||
| 366 | dylib_id.compatibility_version, | ||
| 367 | ); | ||
| 368 | errdefer dylib_cmd.deinit(self.allocator); | ||
| 369 | |||
| 370 | try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd }); | ||
| 371 | }, | ||
| 314 | } | 372 | } |
| 315 | } | 373 | } |
| 316 | } | 374 | } |
| 317 | 375 | ||
| 376 | fn parseDylibs(self: *Zld, shared_libs: []const []const u8) !void { | ||
| 377 | for (shared_libs) |lib| { | ||
| 378 | const dylib = try self.allocator.create(Dylib); | ||
| 379 | errdefer self.allocator.destroy(dylib); | ||
| 380 | |||
| 381 | dylib.* = Dylib.init(self.allocator); | ||
| 382 | dylib.arch = self.arch.?; | ||
| 383 | dylib.name = try self.allocator.dupe(u8, lib); | ||
| 384 | dylib.file = try fs.cwd().openFile(lib, .{}); | ||
| 385 | |||
| 386 | const ordinal = @intCast(u16, self.dylibs.items.len); | ||
| 387 | dylib.ordinal = ordinal + 2; // TODO +2 since 1 is reserved for libSystem | ||
| 388 | |||
| 389 | // TODO Defer parsing of the dylibs until they are actually needed | ||
| 390 | try dylib.parse(); | ||
| 391 | try self.dylibs.append(self.allocator, dylib); | ||
| 392 | |||
| 393 | // Add LC_LOAD_DYLIB command | ||
| 394 | const dylib_id = dylib.id orelse unreachable; | ||
| 395 | var dylib_cmd = try createLoadDylibCommand( | ||
| 396 | self.allocator, | ||
| 397 | dylib_id.name, | ||
| 398 | dylib_id.timestamp, | ||
| 399 | dylib_id.current_version, | ||
| 400 | dylib_id.compatibility_version, | ||
| 401 | ); | ||
| 402 | errdefer dylib_cmd.deinit(self.allocator); | ||
| 403 | |||
| 404 | try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd }); | ||
| 405 | } | ||
| 406 | } | ||
| 407 | |||
| 318 | fn mapAndUpdateSections( | 408 | fn mapAndUpdateSections( |
| 319 | self: *Zld, | 409 | self: *Zld, |
| 320 | object_id: u16, | 410 | object_id: u16, |
| ... | @@ -1398,35 +1488,51 @@ fn resolveSymbols(self: *Zld) !void { | ... | @@ -1398,35 +1488,51 @@ fn resolveSymbols(self: *Zld) !void { |
| 1398 | // Third pass, resolve symbols in dynamic libraries. | 1488 | // Third pass, resolve symbols in dynamic libraries. |
| 1399 | // TODO Implement libSystem as a hard-coded library, or ship with | 1489 | // TODO Implement libSystem as a hard-coded library, or ship with |
| 1400 | // a libSystem.B.tbd definition file? | 1490 | // a libSystem.B.tbd definition file? |
| 1401 | try self.imports.ensureCapacity(self.allocator, self.unresolved.count()); | 1491 | var unresolved = std.ArrayList(*Symbol).init(self.allocator); |
| 1402 | for (self.unresolved.items()) |entry| { | 1492 | defer unresolved.deinit(); |
| 1403 | const proxy = try self.allocator.create(Symbol.Proxy); | ||
| 1404 | errdefer self.allocator.destroy(proxy); | ||
| 1405 | 1493 | ||
| 1406 | proxy.* = .{ | 1494 | try unresolved.ensureCapacity(self.unresolved.count()); |
| 1407 | .base = .{ | 1495 | for (self.unresolved.items()) |entry| { |
| 1408 | .@"type" = .proxy, | 1496 | unresolved.appendAssumeCapacity(entry.value); |
| 1409 | .name = try self.allocator.dupe(u8, entry.key), | ||
| 1410 | }, | ||
| 1411 | .dylib = 0, | ||
| 1412 | }; | ||
| 1413 | |||
| 1414 | self.imports.putAssumeCapacityNoClobber(proxy.base.name, &proxy.base); | ||
| 1415 | entry.value.alias = &proxy.base; | ||
| 1416 | } | 1497 | } |
| 1417 | self.unresolved.clearAndFree(self.allocator); | 1498 | self.unresolved.clearAndFree(self.allocator); |
| 1418 | 1499 | ||
| 1419 | // If there are any undefs left, flag an error. | 1500 | var has_undefined = false; |
| 1420 | if (self.unresolved.count() > 0) { | 1501 | while (unresolved.popOrNull()) |undef| { |
| 1421 | for (self.unresolved.items()) |entry| { | 1502 | var found = false; |
| 1422 | log.err("undefined reference to symbol '{s}'", .{entry.key}); | 1503 | for (self.dylibs.items) |dylib| { |
| 1423 | log.err(" | referenced in {s}", .{ | 1504 | const proxy = dylib.symbols.get(undef.name) orelse continue; |
| 1424 | entry.value.cast(Symbol.Unresolved).?.file.name.?, | 1505 | try self.imports.putNoClobber(self.allocator, proxy.name, proxy); |
| 1425 | }); | 1506 | undef.alias = proxy; |
| 1507 | found = true; | ||
| 1508 | } | ||
| 1509 | |||
| 1510 | if (!found) { | ||
| 1511 | // TODO we currently hardcode all unresolved symbols to libSystem | ||
| 1512 | const proxy = try self.allocator.create(Symbol.Proxy); | ||
| 1513 | errdefer self.allocator.destroy(proxy); | ||
| 1514 | |||
| 1515 | proxy.* = .{ | ||
| 1516 | .base = .{ | ||
| 1517 | .@"type" = .proxy, | ||
| 1518 | .name = try self.allocator.dupe(u8, undef.name), | ||
| 1519 | }, | ||
| 1520 | .dylib = null, // TODO null means libSystem | ||
| 1521 | }; | ||
| 1522 | |||
| 1523 | try self.imports.putNoClobber(self.allocator, proxy.base.name, &proxy.base); | ||
| 1524 | undef.alias = &proxy.base; | ||
| 1525 | |||
| 1526 | // log.err("undefined reference to symbol '{s}'", .{undef.name}); | ||
| 1527 | // log.err(" | referenced in {s}", .{ | ||
| 1528 | // undef.cast(Symbol.Unresolved).?.file.name.?, | ||
| 1529 | // }); | ||
| 1530 | // has_undefined = true; | ||
| 1426 | } | 1531 | } |
| 1427 | return error.UndefinedSymbolReference; | ||
| 1428 | } | 1532 | } |
| 1429 | 1533 | ||
| 1534 | if (has_undefined) return error.UndefinedSymbolReference; | ||
| 1535 | |||
| 1430 | // Finally put dyld_stub_binder as an Import | 1536 | // Finally put dyld_stub_binder as an Import |
| 1431 | const dyld_stub_binder = try self.allocator.create(Symbol.Proxy); | 1537 | const dyld_stub_binder = try self.allocator.create(Symbol.Proxy); |
| 1432 | errdefer self.allocator.destroy(dyld_stub_binder); | 1538 | errdefer self.allocator.destroy(dyld_stub_binder); |
| ... | @@ -1436,7 +1542,7 @@ fn resolveSymbols(self: *Zld) !void { | ... | @@ -1436,7 +1542,7 @@ fn resolveSymbols(self: *Zld) !void { |
| 1436 | .@"type" = .proxy, | 1542 | .@"type" = .proxy, |
| 1437 | .name = try self.allocator.dupe(u8, "dyld_stub_binder"), | 1543 | .name = try self.allocator.dupe(u8, "dyld_stub_binder"), |
| 1438 | }, | 1544 | }, |
| 1439 | .dylib = 0, | 1545 | .dylib = null, // TODO null means libSystem |
| 1440 | }; | 1546 | }; |
| 1441 | 1547 | ||
| 1442 | try self.imports.putNoClobber( | 1548 | try self.imports.putNoClobber( |
| ... | @@ -1997,27 +2103,10 @@ fn populateMetadata(self: *Zld) !void { | ... | @@ -1997,27 +2103,10 @@ fn populateMetadata(self: *Zld) !void { |
| 1997 | 2103 | ||
| 1998 | if (self.libsystem_cmd_index == null) { | 2104 | if (self.libsystem_cmd_index == null) { |
| 1999 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); | 2105 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 2000 | const cmdsize = @intCast(u32, mem.alignForwardGeneric( | 2106 | |
| 2001 | u64, | 2107 | var dylib_cmd = try createLoadDylibCommand(self.allocator, mem.spanZ(LIB_SYSTEM_PATH), 2, 0, 0); |
| 2002 | @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH), | 2108 | errdefer dylib_cmd.deinit(self.allocator); |
| 2003 | @sizeOf(u64), | 2109 | |
| 2004 | )); | ||
| 2005 | // TODO Find a way to work out runtime version from the OS version triple stored in std.Target. | ||
| 2006 | // In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0. | ||
| 2007 | const min_version = 0x0; | ||
| 2008 | var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{ | ||
| 2009 | .cmd = macho.LC_LOAD_DYLIB, | ||
| 2010 | .cmdsize = cmdsize, | ||
| 2011 | .dylib = .{ | ||
| 2012 | .name = @sizeOf(macho.dylib_command), | ||
| 2013 | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files | ||
| 2014 | .current_version = min_version, | ||
| 2015 | .compatibility_version = min_version, | ||
| 2016 | }, | ||
| 2017 | }); | ||
| 2018 | dylib_cmd.data = try self.allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name); | ||
| 2019 | mem.set(u8, dylib_cmd.data, 0); | ||
| 2020 | mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH)); | ||
| 2021 | try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd }); | 2110 | try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd }); |
| 2022 | } | 2111 | } |
| 2023 | 2112 | ||
| ... | @@ -2080,6 +2169,25 @@ fn populateMetadata(self: *Zld) !void { | ... | @@ -2080,6 +2169,25 @@ fn populateMetadata(self: *Zld) !void { |
| 2080 | } | 2169 | } |
| 2081 | } | 2170 | } |
| 2082 | 2171 | ||
| 2172 | fn addRpaths(self: *Zld, rpaths: []const []const u8) !void { | ||
| 2173 | for (rpaths) |rpath| { | ||
| 2174 | const cmdsize = @intCast(u32, mem.alignForwardGeneric( | ||
| 2175 | u64, | ||
| 2176 | @sizeOf(macho.rpath_command) + rpath.len, | ||
| 2177 | @sizeOf(u64), | ||
| 2178 | )); | ||
| 2179 | var rpath_cmd = emptyGenericCommandWithData(macho.rpath_command{ | ||
| 2180 | .cmd = macho.LC_RPATH, | ||
| 2181 | .cmdsize = cmdsize, | ||
| 2182 | .path = @sizeOf(macho.rpath_command), | ||
| 2183 | }); | ||
| 2184 | rpath_cmd.data = try self.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path); | ||
| 2185 | mem.set(u8, rpath_cmd.data, 0); | ||
| 2186 | mem.copy(u8, rpath_cmd.data, rpath); | ||
| 2187 | try self.load_commands.append(self.allocator, .{ .Rpath = rpath_cmd }); | ||
| 2188 | } | ||
| 2189 | } | ||
| 2190 | |||
| 2083 | fn flush(self: *Zld) !void { | 2191 | fn flush(self: *Zld) !void { |
| 2084 | try self.writeStubHelperCommon(); | 2192 | try self.writeStubHelperCommon(); |
| 2085 | try self.resolveRelocsAndWriteSections(); | 2193 | try self.resolveRelocsAndWriteSections(); |
| ... | @@ -2303,7 +2411,10 @@ fn writeBindInfoTable(self: *Zld) !void { | ... | @@ -2303,7 +2411,10 @@ fn writeBindInfoTable(self: *Zld) !void { |
| 2303 | 2411 | ||
| 2304 | for (self.got_entries.items) |sym| { | 2412 | for (self.got_entries.items) |sym| { |
| 2305 | if (sym.cast(Symbol.Proxy)) |proxy| { | 2413 | if (sym.cast(Symbol.Proxy)) |proxy| { |
| 2306 | const dylib_ordinal = proxy.dylib + 1; | 2414 | const dylib_ordinal = ordinal: { |
| 2415 | const dylib = proxy.dylib orelse break :ordinal 1; // TODO embedded libSystem | ||
| 2416 | break :ordinal dylib.ordinal.?; | ||
| 2417 | }; | ||
| 2307 | try pointers.append(.{ | 2418 | try pointers.append(.{ |
| 2308 | .offset = base_offset + proxy.base.got_index.? * @sizeOf(u64), | 2419 | .offset = base_offset + proxy.base.got_index.? * @sizeOf(u64), |
| 2309 | .segment_id = segment_id, | 2420 | .segment_id = segment_id, |
| ... | @@ -2322,7 +2433,10 @@ fn writeBindInfoTable(self: *Zld) !void { | ... | @@ -2322,7 +2433,10 @@ fn writeBindInfoTable(self: *Zld) !void { |
| 2322 | 2433 | ||
| 2323 | const sym = self.imports.get("__tlv_bootstrap") orelse unreachable; | 2434 | const sym = self.imports.get("__tlv_bootstrap") orelse unreachable; |
| 2324 | const proxy = sym.cast(Symbol.Proxy) orelse unreachable; | 2435 | const proxy = sym.cast(Symbol.Proxy) orelse unreachable; |
| 2325 | const dylib_ordinal = proxy.dylib + 1; | 2436 | const dylib_ordinal = ordinal: { |
| 2437 | const dylib = proxy.dylib orelse break :ordinal 1; // TODO embedded libSystem | ||
| 2438 | break :ordinal dylib.ordinal.?; | ||
| 2439 | }; | ||
| 2326 | 2440 | ||
| 2327 | try pointers.append(.{ | 2441 | try pointers.append(.{ |
| 2328 | .offset = base_offset, | 2442 | .offset = base_offset, |
| ... | @@ -2364,7 +2478,11 @@ fn writeLazyBindInfoTable(self: *Zld) !void { | ... | @@ -2364,7 +2478,11 @@ fn writeLazyBindInfoTable(self: *Zld) !void { |
| 2364 | 2478 | ||
| 2365 | for (self.stubs.items) |sym| { | 2479 | for (self.stubs.items) |sym| { |
| 2366 | const proxy = sym.cast(Symbol.Proxy) orelse unreachable; | 2480 | const proxy = sym.cast(Symbol.Proxy) orelse unreachable; |
| 2367 | const dylib_ordinal = proxy.dylib + 1; | 2481 | const dylib_ordinal = ordinal: { |
| 2482 | const dylib = proxy.dylib orelse break :ordinal 1; // TODO embedded libSystem | ||
| 2483 | break :ordinal dylib.ordinal.?; | ||
| 2484 | }; | ||
| 2485 | |||
| 2368 | pointers.appendAssumeCapacity(.{ | 2486 | pointers.appendAssumeCapacity(.{ |
| 2369 | .offset = base_offset + sym.stubs_index.? * @sizeOf(u64), | 2487 | .offset = base_offset + sym.stubs_index.? * @sizeOf(u64), |
| 2370 | .segment_id = segment_id, | 2488 | .segment_id = segment_id, |
| ... | @@ -2664,11 +2782,15 @@ fn writeSymbolTable(self: *Zld) !void { | ... | @@ -2664,11 +2782,15 @@ fn writeSymbolTable(self: *Zld) !void { |
| 2664 | 2782 | ||
| 2665 | for (self.imports.items()) |entry| { | 2783 | for (self.imports.items()) |entry| { |
| 2666 | const sym = entry.value; | 2784 | const sym = entry.value; |
| 2785 | const ordinal = ordinal: { | ||
| 2786 | const dylib = sym.cast(Symbol.Proxy).?.dylib orelse break :ordinal 1; // TODO handle libSystem | ||
| 2787 | break :ordinal dylib.ordinal.?; | ||
| 2788 | }; | ||
| 2667 | try undefs.append(.{ | 2789 | try undefs.append(.{ |
| 2668 | .n_strx = try self.makeString(sym.name), | 2790 | .n_strx = try self.makeString(sym.name), |
| 2669 | .n_type = macho.N_UNDF | macho.N_EXT, | 2791 | .n_type = macho.N_UNDF | macho.N_EXT, |
| 2670 | .n_sect = 0, | 2792 | .n_sect = 0, |
| 2671 | .n_desc = macho.N_SYMBOL_RESOLVER | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY, | 2793 | .n_desc = (ordinal * macho.N_SYMBOL_RESOLVER) | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY, |
| 2672 | .n_value = 0, | 2794 | .n_value = 0, |
| 2673 | }); | 2795 | }); |
| 2674 | } | 2796 | } |
src/link/MachO/commands.zig+61-5| ... | @@ -24,6 +24,7 @@ pub const LoadCommand = union(enum) { | ... | @@ -24,6 +24,7 @@ pub const LoadCommand = union(enum) { |
| 24 | SourceVersion: macho.source_version_command, | 24 | SourceVersion: macho.source_version_command, |
| 25 | Uuid: macho.uuid_command, | 25 | Uuid: macho.uuid_command, |
| 26 | LinkeditData: macho.linkedit_data_command, | 26 | LinkeditData: macho.linkedit_data_command, |
| 27 | Rpath: GenericCommandWithData(macho.rpath_command), | ||
| 27 | Unknown: GenericCommandWithData(macho.load_command), | 28 | Unknown: GenericCommandWithData(macho.load_command), |
| 28 | 29 | ||
| 29 | pub fn read(allocator: *Allocator, reader: anytype) !LoadCommand { | 30 | pub fn read(allocator: *Allocator, reader: anytype) !LoadCommand { |
| ... | @@ -38,7 +39,9 @@ pub const LoadCommand = union(enum) { | ... | @@ -38,7 +39,9 @@ pub const LoadCommand = union(enum) { |
| 38 | macho.LC_SEGMENT_64 => LoadCommand{ | 39 | macho.LC_SEGMENT_64 => LoadCommand{ |
| 39 | .Segment = try SegmentCommand.read(allocator, stream.reader()), | 40 | .Segment = try SegmentCommand.read(allocator, stream.reader()), |
| 40 | }, | 41 | }, |
| 41 | macho.LC_DYLD_INFO, macho.LC_DYLD_INFO_ONLY => LoadCommand{ | 42 | macho.LC_DYLD_INFO, |
| 43 | macho.LC_DYLD_INFO_ONLY, | ||
| 44 | => LoadCommand{ | ||
| 42 | .DyldInfoOnly = try stream.reader().readStruct(macho.dyld_info_command), | 45 | .DyldInfoOnly = try stream.reader().readStruct(macho.dyld_info_command), |
| 43 | }, | 46 | }, |
| 44 | macho.LC_SYMTAB => LoadCommand{ | 47 | macho.LC_SYMTAB => LoadCommand{ |
| ... | @@ -47,16 +50,27 @@ pub const LoadCommand = union(enum) { | ... | @@ -47,16 +50,27 @@ pub const LoadCommand = union(enum) { |
| 47 | macho.LC_DYSYMTAB => LoadCommand{ | 50 | macho.LC_DYSYMTAB => LoadCommand{ |
| 48 | .Dysymtab = try stream.reader().readStruct(macho.dysymtab_command), | 51 | .Dysymtab = try stream.reader().readStruct(macho.dysymtab_command), |
| 49 | }, | 52 | }, |
| 50 | macho.LC_ID_DYLINKER, macho.LC_LOAD_DYLINKER, macho.LC_DYLD_ENVIRONMENT => LoadCommand{ | 53 | macho.LC_ID_DYLINKER, |
| 54 | macho.LC_LOAD_DYLINKER, | ||
| 55 | macho.LC_DYLD_ENVIRONMENT, | ||
| 56 | => LoadCommand{ | ||
| 51 | .Dylinker = try GenericCommandWithData(macho.dylinker_command).read(allocator, stream.reader()), | 57 | .Dylinker = try GenericCommandWithData(macho.dylinker_command).read(allocator, stream.reader()), |
| 52 | }, | 58 | }, |
| 53 | macho.LC_ID_DYLIB, macho.LC_LOAD_WEAK_DYLIB, macho.LC_LOAD_DYLIB, macho.LC_REEXPORT_DYLIB => LoadCommand{ | 59 | macho.LC_ID_DYLIB, |
| 60 | macho.LC_LOAD_WEAK_DYLIB, | ||
| 61 | macho.LC_LOAD_DYLIB, | ||
| 62 | macho.LC_REEXPORT_DYLIB, | ||
| 63 | => LoadCommand{ | ||
| 54 | .Dylib = try GenericCommandWithData(macho.dylib_command).read(allocator, stream.reader()), | 64 | .Dylib = try GenericCommandWithData(macho.dylib_command).read(allocator, stream.reader()), |
| 55 | }, | 65 | }, |
| 56 | macho.LC_MAIN => LoadCommand{ | 66 | macho.LC_MAIN => LoadCommand{ |
| 57 | .Main = try stream.reader().readStruct(macho.entry_point_command), | 67 | .Main = try stream.reader().readStruct(macho.entry_point_command), |
| 58 | }, | 68 | }, |
| 59 | macho.LC_VERSION_MIN_MACOSX, macho.LC_VERSION_MIN_IPHONEOS, macho.LC_VERSION_MIN_WATCHOS, macho.LC_VERSION_MIN_TVOS => LoadCommand{ | 69 | macho.LC_VERSION_MIN_MACOSX, |
| 70 | macho.LC_VERSION_MIN_IPHONEOS, | ||
| 71 | macho.LC_VERSION_MIN_WATCHOS, | ||
| 72 | macho.LC_VERSION_MIN_TVOS, | ||
| 73 | => LoadCommand{ | ||
| 60 | .VersionMin = try stream.reader().readStruct(macho.version_min_command), | 74 | .VersionMin = try stream.reader().readStruct(macho.version_min_command), |
| 61 | }, | 75 | }, |
| 62 | macho.LC_SOURCE_VERSION => LoadCommand{ | 76 | macho.LC_SOURCE_VERSION => LoadCommand{ |
| ... | @@ -65,9 +79,15 @@ pub const LoadCommand = union(enum) { | ... | @@ -65,9 +79,15 @@ pub const LoadCommand = union(enum) { |
| 65 | macho.LC_UUID => LoadCommand{ | 79 | macho.LC_UUID => LoadCommand{ |
| 66 | .Uuid = try stream.reader().readStruct(macho.uuid_command), | 80 | .Uuid = try stream.reader().readStruct(macho.uuid_command), |
| 67 | }, | 81 | }, |
| 68 | macho.LC_FUNCTION_STARTS, macho.LC_DATA_IN_CODE, macho.LC_CODE_SIGNATURE => LoadCommand{ | 82 | macho.LC_FUNCTION_STARTS, |
| 83 | macho.LC_DATA_IN_CODE, | ||
| 84 | macho.LC_CODE_SIGNATURE, | ||
| 85 | => LoadCommand{ | ||
| 69 | .LinkeditData = try stream.reader().readStruct(macho.linkedit_data_command), | 86 | .LinkeditData = try stream.reader().readStruct(macho.linkedit_data_command), |
| 70 | }, | 87 | }, |
| 88 | macho.LC_RPATH => LoadCommand{ | ||
| 89 | .Rpath = try GenericCommandWithData(macho.rpath_command).read(allocator, stream.reader()), | ||
| 90 | }, | ||
| 71 | else => LoadCommand{ | 91 | else => LoadCommand{ |
| 72 | .Unknown = try GenericCommandWithData(macho.load_command).read(allocator, stream.reader()), | 92 | .Unknown = try GenericCommandWithData(macho.load_command).read(allocator, stream.reader()), |
| 73 | }, | 93 | }, |
| ... | @@ -87,6 +107,7 @@ pub const LoadCommand = union(enum) { | ... | @@ -87,6 +107,7 @@ pub const LoadCommand = union(enum) { |
| 87 | .Segment => |x| x.write(writer), | 107 | .Segment => |x| x.write(writer), |
| 88 | .Dylinker => |x| x.write(writer), | 108 | .Dylinker => |x| x.write(writer), |
| 89 | .Dylib => |x| x.write(writer), | 109 | .Dylib => |x| x.write(writer), |
| 110 | .Rpath => |x| x.write(writer), | ||
| 90 | .Unknown => |x| x.write(writer), | 111 | .Unknown => |x| x.write(writer), |
| 91 | }; | 112 | }; |
| 92 | } | 113 | } |
| ... | @@ -104,6 +125,7 @@ pub const LoadCommand = union(enum) { | ... | @@ -104,6 +125,7 @@ pub const LoadCommand = union(enum) { |
| 104 | .Segment => |x| x.inner.cmd, | 125 | .Segment => |x| x.inner.cmd, |
| 105 | .Dylinker => |x| x.inner.cmd, | 126 | .Dylinker => |x| x.inner.cmd, |
| 106 | .Dylib => |x| x.inner.cmd, | 127 | .Dylib => |x| x.inner.cmd, |
| 128 | .Rpath => |x| x.inner.cmd, | ||
| 107 | .Unknown => |x| x.inner.cmd, | 129 | .Unknown => |x| x.inner.cmd, |
| 108 | }; | 130 | }; |
| 109 | } | 131 | } |
| ... | @@ -121,6 +143,7 @@ pub const LoadCommand = union(enum) { | ... | @@ -121,6 +143,7 @@ pub const LoadCommand = union(enum) { |
| 121 | .Segment => |x| x.inner.cmdsize, | 143 | .Segment => |x| x.inner.cmdsize, |
| 122 | .Dylinker => |x| x.inner.cmdsize, | 144 | .Dylinker => |x| x.inner.cmdsize, |
| 123 | .Dylib => |x| x.inner.cmdsize, | 145 | .Dylib => |x| x.inner.cmdsize, |
| 146 | .Rpath => |x| x.inner.cmdsize, | ||
| 124 | .Unknown => |x| x.inner.cmdsize, | 147 | .Unknown => |x| x.inner.cmdsize, |
| 125 | }; | 148 | }; |
| 126 | } | 149 | } |
| ... | @@ -130,6 +153,7 @@ pub const LoadCommand = union(enum) { | ... | @@ -130,6 +153,7 @@ pub const LoadCommand = union(enum) { |
| 130 | .Segment => |*x| x.deinit(allocator), | 153 | .Segment => |*x| x.deinit(allocator), |
| 131 | .Dylinker => |*x| x.deinit(allocator), | 154 | .Dylinker => |*x| x.deinit(allocator), |
| 132 | .Dylib => |*x| x.deinit(allocator), | 155 | .Dylib => |*x| x.deinit(allocator), |
| 156 | .Rpath => |*x| x.deinit(allocator), | ||
| 133 | .Unknown => |*x| x.deinit(allocator), | 157 | .Unknown => |*x| x.deinit(allocator), |
| 134 | else => {}, | 158 | else => {}, |
| 135 | }; | 159 | }; |
| ... | @@ -153,6 +177,7 @@ pub const LoadCommand = union(enum) { | ... | @@ -153,6 +177,7 @@ pub const LoadCommand = union(enum) { |
| 153 | .Segment => |x| x.eql(other.Segment), | 177 | .Segment => |x| x.eql(other.Segment), |
| 154 | .Dylinker => |x| x.eql(other.Dylinker), | 178 | .Dylinker => |x| x.eql(other.Dylinker), |
| 155 | .Dylib => |x| x.eql(other.Dylib), | 179 | .Dylib => |x| x.eql(other.Dylib), |
| 180 | .Rpath => |x| x.eql(other.Rpath), | ||
| 156 | .Unknown => |x| x.eql(other.Unknown), | 181 | .Unknown => |x| x.eql(other.Unknown), |
| 157 | }; | 182 | }; |
| 158 | } | 183 | } |
| ... | @@ -282,6 +307,37 @@ pub fn GenericCommandWithData(comptime Cmd: type) type { | ... | @@ -282,6 +307,37 @@ pub fn GenericCommandWithData(comptime Cmd: type) type { |
| 282 | }; | 307 | }; |
| 283 | } | 308 | } |
| 284 | 309 | ||
| 310 | pub fn createLoadDylibCommand( | ||
| 311 | allocator: *Allocator, | ||
| 312 | name: []const u8, | ||
| 313 | timestamp: u32, | ||
| 314 | current_version: u32, | ||
| 315 | compatibility_version: u32, | ||
| 316 | ) !GenericCommandWithData(macho.dylib_command) { | ||
| 317 | const cmdsize = @intCast(u32, mem.alignForwardGeneric( | ||
| 318 | u64, | ||
| 319 | @sizeOf(macho.dylib_command) + name.len, | ||
| 320 | @sizeOf(u64), | ||
| 321 | )); | ||
| 322 | |||
| 323 | var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{ | ||
| 324 | .cmd = macho.LC_LOAD_DYLIB, | ||
| 325 | .cmdsize = cmdsize, | ||
| 326 | .dylib = .{ | ||
| 327 | .name = @sizeOf(macho.dylib_command), | ||
| 328 | .timestamp = timestamp, | ||
| 329 | .current_version = current_version, | ||
| 330 | .compatibility_version = compatibility_version, | ||
| 331 | }, | ||
| 332 | }); | ||
| 333 | dylib_cmd.data = try allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name); | ||
| 334 | |||
| 335 | mem.set(u8, dylib_cmd.data, 0); | ||
| 336 | mem.copy(u8, dylib_cmd.data, name); | ||
| 337 | |||
| 338 | return dylib_cmd; | ||
| 339 | } | ||
| 340 | |||
| 285 | fn testRead(allocator: *Allocator, buffer: []const u8, expected: anytype) !void { | 341 | fn testRead(allocator: *Allocator, buffer: []const u8, expected: anytype) !void { |
| 286 | var stream = io.fixedBufferStream(buffer); | 342 | var stream = io.fixedBufferStream(buffer); |
| 287 | var given = try LoadCommand.read(allocator, stream.reader()); | 343 | var given = try LoadCommand.read(allocator, stream.reader()); |
test/standalone.zig+1-4| ... | @@ -9,10 +9,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void { | ... | @@ -9,10 +9,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void { |
| 9 | cases.add("test/standalone/main_return_error/error_u8.zig"); | 9 | cases.add("test/standalone/main_return_error/error_u8.zig"); |
| 10 | cases.add("test/standalone/main_return_error/error_u8_non_zero.zig"); | 10 | cases.add("test/standalone/main_return_error/error_u8_non_zero.zig"); |
| 11 | cases.addBuildFile("test/standalone/main_pkg_path/build.zig"); | 11 | cases.addBuildFile("test/standalone/main_pkg_path/build.zig"); |
| 12 | if (std.Target.current.os.tag != .macos) { | 12 | cases.addBuildFile("test/standalone/shared_library/build.zig"); |
| 13 | // TODO zld cannot link shared libraries yet. | ||
| 14 | cases.addBuildFile("test/standalone/shared_library/build.zig"); | ||
| 15 | } | ||
| 16 | cases.addBuildFile("test/standalone/mix_o_files/build.zig"); | 13 | cases.addBuildFile("test/standalone/mix_o_files/build.zig"); |
| 17 | cases.addBuildFile("test/standalone/global_linkage/build.zig"); | 14 | cases.addBuildFile("test/standalone/global_linkage/build.zig"); |
| 18 | cases.addBuildFile("test/standalone/static_c_lib/build.zig"); | 15 | cases.addBuildFile("test/standalone/static_c_lib/build.zig"); |