| 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 | 568 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig" |
| 569 | 569 | "${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig" |
| 570 | 570 | "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig" |
| 571 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig" | |
| 571 | 572 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig" |
| 572 | 573 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Symbol.zig" |
| 573 | 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 | 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 | 106 | /// The entry_point_command is a replacement for thread_command. |
| 75 | 107 | /// It is used for main executables to specify the location (file offset) |
| 76 | 108 | /// of main(). If -stack_size was used at link time, the stacksize |
| ... | ... | @@ -484,6 +516,19 @@ pub const dylib = extern struct { |
| 484 | 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 | 532 | /// The segment load command indicates that a part of this file is to be |
| 488 | 533 | /// mapped into the task's address space. The size of this segment in memory, |
| 489 | 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 | 548 | const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; |
| 549 | 549 | const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; |
| 550 | 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 | 552 | const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os; |
| 553 | 553 | |
| 554 | 554 | const id_symlink_basename = "lld.id"; |
| ... | ... | @@ -675,41 +675,168 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 675 | 675 | zld.deinit(); |
| 676 | 676 | } |
| 677 | 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 | 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 | 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 | 695 | // libc++ dep |
| 691 | 696 | if (self.base.options.link_libcpp) { |
| 692 | try input_files.append(comp.libcxxabi_static_lib.?.full_object_path); | |
| 693 | try input_files.append(comp.libcxx_static_lib.?.full_object_path); | |
| 697 | try positionals.append(comp.libcxxabi_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 | 805 | if (self.base.options.verbose_link) { |
| 697 | var argv = std.ArrayList([]const u8).init(self.base.allocator); | |
| 698 | defer argv.deinit(); | |
| 806 | var argv = std.ArrayList([]const u8).init(arena); | |
| 699 | 807 | |
| 700 | 808 | try argv.append("zig"); |
| 701 | 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 | 823 | try argv.append("-o"); |
| 706 | 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 | 834 | Compilation.dump_argv(argv.items); |
| 709 | 835 | } |
| 710 | 836 | |
| 711 | try zld.link(input_files.items, full_out_path, .{ | |
| 712 | .stack_size = self.base.options.stack_size_override, | |
| 837 | try zld.link(positionals.items, full_out_path, .{ | |
| 838 | .shared_libs = shared_libs.items, | |
| 839 | .rpaths = rpaths.items, | |
| 713 | 840 | }); |
| 714 | 841 | |
| 715 | 842 | break :outer; |
| ... | ... | @@ -1993,28 +2120,12 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1993 | 2120 | } |
| 1994 | 2121 | if (self.libsystem_cmd_index == null) { |
| 1995 | 2122 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1996 | const cmdsize = @intCast(u32, mem.alignForwardGeneric( | |
| 1997 | u64, | |
| 1998 | @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH), | |
| 1999 | @sizeOf(u64), | |
| 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)); | |
| 2123 | ||
| 2124 | var dylib_cmd = try createLoadDylibCommand(self.base.allocator, mem.spanZ(LIB_SYSTEM_PATH), 2, 0, 0); | |
| 2125 | errdefer dylib_cmd.deinit(self.base.allocator); | |
| 2126 | ||
| 2017 | 2127 | try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); |
| 2128 | ||
| 2018 | 2129 | self.header_dirty = true; |
| 2019 | 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 | 5 | const mem = std.mem; |
| 6 | 6 | |
| 7 | 7 | const Allocator = mem.Allocator; |
| 8 | const Dylib = @import("Dylib.zig"); | |
| 8 | 9 | const Object = @import("Object.zig"); |
| 9 | 10 | |
| 10 | 11 | pub const Type = enum { |
| ... | ... | @@ -43,7 +44,7 @@ pub const Regular = struct { |
| 43 | 44 | /// Whether the symbol is a weak ref. |
| 44 | 45 | weak_ref: bool, |
| 45 | 46 | |
| 46 | /// File where to locate this symbol. | |
| 47 | /// Object file where to locate this symbol. | |
| 47 | 48 | file: *Object, |
| 48 | 49 | |
| 49 | 50 | /// Debug stab if defined. |
| ... | ... | @@ -78,8 +79,8 @@ pub const Regular = struct { |
| 78 | 79 | pub const Proxy = struct { |
| 79 | 80 | base: Symbol, |
| 80 | 81 | |
| 81 | /// Dylib ordinal. | |
| 82 | dylib: u16, | |
| 82 | /// Dylib where to locate this symbol. | |
| 83 | dylib: ?*Dylib = null, | |
| 83 | 84 | |
| 84 | 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 | 15 | const Allocator = mem.Allocator; |
| 16 | 16 | const Archive = @import("Archive.zig"); |
| 17 | 17 | const CodeSignature = @import("CodeSignature.zig"); |
| 18 | const Dylib = @import("Dylib.zig"); | |
| 18 | 19 | const Object = @import("Object.zig"); |
| 19 | 20 | const Symbol = @import("Symbol.zig"); |
| 20 | 21 | const Trie = @import("Trie.zig"); |
| ... | ... | @@ -35,6 +36,7 @@ stack_size: u64 = 0, |
| 35 | 36 | |
| 36 | 37 | objects: std.ArrayListUnmanaged(*Object) = .{}, |
| 37 | 38 | archives: std.ArrayListUnmanaged(*Archive) = .{}, |
| 39 | dylibs: std.ArrayListUnmanaged(*Dylib) = .{}, | |
| 38 | 40 | |
| 39 | 41 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| 40 | 42 | |
| ... | ... | @@ -151,10 +153,18 @@ pub fn deinit(self: *Zld) void { |
| 151 | 153 | } |
| 152 | 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 | 162 | self.mappings.deinit(self.allocator); |
| 155 | 163 | self.unhandled_sections.deinit(self.allocator); |
| 156 | 164 | |
| 157 | 165 | self.globals.deinit(self.allocator); |
| 166 | self.imports.deinit(self.allocator); | |
| 167 | self.unresolved.deinit(self.allocator); | |
| 158 | 168 | self.strtab.deinit(self.allocator); |
| 159 | 169 | |
| 160 | 170 | { |
| ... | ... | @@ -177,7 +187,8 @@ pub fn closeFiles(self: Zld) void { |
| 177 | 187 | } |
| 178 | 188 | |
| 179 | 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 | 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 | 225 | .read = true, |
| 215 | 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 | 229 | try self.populateMetadata(); |
| 230 | try self.addRpaths(args.rpaths); | |
| 220 | 231 | try self.parseInputFiles(files); |
| 232 | try self.parseDylibs(args.shared_libs); | |
| 221 | 233 | try self.resolveSymbols(); |
| 222 | 234 | try self.resolveStubsAndGotEntries(); |
| 223 | 235 | try self.updateMetadata(); |
| ... | ... | @@ -235,6 +247,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { |
| 235 | 247 | kind: enum { |
| 236 | 248 | object, |
| 237 | 249 | archive, |
| 250 | dylib, | |
| 238 | 251 | }, |
| 239 | 252 | file: fs.File, |
| 240 | 253 | name: []const u8, |
| ... | ... | @@ -242,7 +255,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { |
| 242 | 255 | var classified = std.ArrayList(Input).init(self.allocator); |
| 243 | 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 | 259 | for (files) |file_name| { |
| 247 | 260 | const file = try fs.cwd().openFile(file_name, .{}); |
| 248 | 261 | const full_path = full_path: { |
| ... | ... | @@ -283,6 +296,22 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { |
| 283 | 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 | 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 | 340 | try archive.parse(); |
| 312 | 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 | 408 | fn mapAndUpdateSections( |
| 319 | 409 | self: *Zld, |
| 320 | 410 | object_id: u16, |
| ... | ... | @@ -1398,35 +1488,51 @@ fn resolveSymbols(self: *Zld) !void { |
| 1398 | 1488 | // Third pass, resolve symbols in dynamic libraries. |
| 1399 | 1489 | // TODO Implement libSystem as a hard-coded library, or ship with |
| 1400 | 1490 | // a libSystem.B.tbd definition file? |
| 1401 | try self.imports.ensureCapacity(self.allocator, self.unresolved.count()); | |
| 1402 | for (self.unresolved.items()) |entry| { | |
| 1403 | const proxy = try self.allocator.create(Symbol.Proxy); | |
| 1404 | errdefer self.allocator.destroy(proxy); | |
| 1491 | var unresolved = std.ArrayList(*Symbol).init(self.allocator); | |
| 1492 | defer unresolved.deinit(); | |
| 1405 | 1493 | |
| 1406 | proxy.* = .{ | |
| 1407 | .base = .{ | |
| 1408 | .@"type" = .proxy, | |
| 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; | |
| 1494 | try unresolved.ensureCapacity(self.unresolved.count()); | |
| 1495 | for (self.unresolved.items()) |entry| { | |
| 1496 | unresolved.appendAssumeCapacity(entry.value); | |
| 1416 | 1497 | } |
| 1417 | 1498 | self.unresolved.clearAndFree(self.allocator); |
| 1418 | 1499 | |
| 1419 | // If there are any undefs left, flag an error. | |
| 1420 | if (self.unresolved.count() > 0) { | |
| 1421 | for (self.unresolved.items()) |entry| { | |
| 1422 | log.err("undefined reference to symbol '{s}'", .{entry.key}); | |
| 1423 | log.err(" | referenced in {s}", .{ | |
| 1424 | entry.value.cast(Symbol.Unresolved).?.file.name.?, | |
| 1425 | }); | |
| 1500 | var has_undefined = false; | |
| 1501 | while (unresolved.popOrNull()) |undef| { | |
| 1502 | var found = false; | |
| 1503 | for (self.dylibs.items) |dylib| { | |
| 1504 | const proxy = dylib.symbols.get(undef.name) orelse continue; | |
| 1505 | try self.imports.putNoClobber(self.allocator, proxy.name, proxy); | |
| 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 | 1536 | // Finally put dyld_stub_binder as an Import |
| 1431 | 1537 | const dyld_stub_binder = try self.allocator.create(Symbol.Proxy); |
| 1432 | 1538 | errdefer self.allocator.destroy(dyld_stub_binder); |
| ... | ... | @@ -1436,7 +1542,7 @@ fn resolveSymbols(self: *Zld) !void { |
| 1436 | 1542 | .@"type" = .proxy, |
| 1437 | 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 | 1548 | try self.imports.putNoClobber( |
| ... | ... | @@ -1997,27 +2103,10 @@ fn populateMetadata(self: *Zld) !void { |
| 1997 | 2103 | |
| 1998 | 2104 | if (self.libsystem_cmd_index == null) { |
| 1999 | 2105 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 2000 | const cmdsize = @intCast(u32, mem.alignForwardGeneric( | |
| 2001 | u64, | |
| 2002 | @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH), | |
| 2003 | @sizeOf(u64), | |
| 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)); | |
| 2106 | ||
| 2107 | var dylib_cmd = try createLoadDylibCommand(self.allocator, mem.spanZ(LIB_SYSTEM_PATH), 2, 0, 0); | |
| 2108 | errdefer dylib_cmd.deinit(self.allocator); | |
| 2109 | ||
| 2021 | 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 | 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 | 2191 | fn flush(self: *Zld) !void { |
| 2084 | 2192 | try self.writeStubHelperCommon(); |
| 2085 | 2193 | try self.resolveRelocsAndWriteSections(); |
| ... | ... | @@ -2303,7 +2411,10 @@ fn writeBindInfoTable(self: *Zld) !void { |
| 2303 | 2411 | |
| 2304 | 2412 | for (self.got_entries.items) |sym| { |
| 2305 | 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 | 2418 | try pointers.append(.{ |
| 2308 | 2419 | .offset = base_offset + proxy.base.got_index.? * @sizeOf(u64), |
| 2309 | 2420 | .segment_id = segment_id, |
| ... | ... | @@ -2322,7 +2433,10 @@ fn writeBindInfoTable(self: *Zld) !void { |
| 2322 | 2433 | |
| 2323 | 2434 | const sym = self.imports.get("__tlv_bootstrap") orelse unreachable; |
| 2324 | 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 | 2441 | try pointers.append(.{ |
| 2328 | 2442 | .offset = base_offset, |
| ... | ... | @@ -2364,7 +2478,11 @@ fn writeLazyBindInfoTable(self: *Zld) !void { |
| 2364 | 2478 | |
| 2365 | 2479 | for (self.stubs.items) |sym| { |
| 2366 | 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 | 2486 | pointers.appendAssumeCapacity(.{ |
| 2369 | 2487 | .offset = base_offset + sym.stubs_index.? * @sizeOf(u64), |
| 2370 | 2488 | .segment_id = segment_id, |
| ... | ... | @@ -2664,11 +2782,15 @@ fn writeSymbolTable(self: *Zld) !void { |
| 2664 | 2782 | |
| 2665 | 2783 | for (self.imports.items()) |entry| { |
| 2666 | 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 | 2789 | try undefs.append(.{ |
| 2668 | 2790 | .n_strx = try self.makeString(sym.name), |
| 2669 | 2791 | .n_type = macho.N_UNDF | macho.N_EXT, |
| 2670 | 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 | 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 | 24 | SourceVersion: macho.source_version_command, |
| 25 | 25 | Uuid: macho.uuid_command, |
| 26 | 26 | LinkeditData: macho.linkedit_data_command, |
| 27 | Rpath: GenericCommandWithData(macho.rpath_command), | |
| 27 | 28 | Unknown: GenericCommandWithData(macho.load_command), |
| 28 | 29 | |
| 29 | 30 | pub fn read(allocator: *Allocator, reader: anytype) !LoadCommand { |
| ... | ... | @@ -38,7 +39,9 @@ pub const LoadCommand = union(enum) { |
| 38 | 39 | macho.LC_SEGMENT_64 => LoadCommand{ |
| 39 | 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 | 45 | .DyldInfoOnly = try stream.reader().readStruct(macho.dyld_info_command), |
| 43 | 46 | }, |
| 44 | 47 | macho.LC_SYMTAB => LoadCommand{ |
| ... | ... | @@ -47,16 +50,27 @@ pub const LoadCommand = union(enum) { |
| 47 | 50 | macho.LC_DYSYMTAB => LoadCommand{ |
| 48 | 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 | 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 | 64 | .Dylib = try GenericCommandWithData(macho.dylib_command).read(allocator, stream.reader()), |
| 55 | 65 | }, |
| 56 | 66 | macho.LC_MAIN => LoadCommand{ |
| 57 | 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 | 74 | .VersionMin = try stream.reader().readStruct(macho.version_min_command), |
| 61 | 75 | }, |
| 62 | 76 | macho.LC_SOURCE_VERSION => LoadCommand{ |
| ... | ... | @@ -65,9 +79,15 @@ pub const LoadCommand = union(enum) { |
| 65 | 79 | macho.LC_UUID => LoadCommand{ |
| 66 | 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 | 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 | 91 | else => LoadCommand{ |
| 72 | 92 | .Unknown = try GenericCommandWithData(macho.load_command).read(allocator, stream.reader()), |
| 73 | 93 | }, |
| ... | ... | @@ -87,6 +107,7 @@ pub const LoadCommand = union(enum) { |
| 87 | 107 | .Segment => |x| x.write(writer), |
| 88 | 108 | .Dylinker => |x| x.write(writer), |
| 89 | 109 | .Dylib => |x| x.write(writer), |
| 110 | .Rpath => |x| x.write(writer), | |
| 90 | 111 | .Unknown => |x| x.write(writer), |
| 91 | 112 | }; |
| 92 | 113 | } |
| ... | ... | @@ -104,6 +125,7 @@ pub const LoadCommand = union(enum) { |
| 104 | 125 | .Segment => |x| x.inner.cmd, |
| 105 | 126 | .Dylinker => |x| x.inner.cmd, |
| 106 | 127 | .Dylib => |x| x.inner.cmd, |
| 128 | .Rpath => |x| x.inner.cmd, | |
| 107 | 129 | .Unknown => |x| x.inner.cmd, |
| 108 | 130 | }; |
| 109 | 131 | } |
| ... | ... | @@ -121,6 +143,7 @@ pub const LoadCommand = union(enum) { |
| 121 | 143 | .Segment => |x| x.inner.cmdsize, |
| 122 | 144 | .Dylinker => |x| x.inner.cmdsize, |
| 123 | 145 | .Dylib => |x| x.inner.cmdsize, |
| 146 | .Rpath => |x| x.inner.cmdsize, | |
| 124 | 147 | .Unknown => |x| x.inner.cmdsize, |
| 125 | 148 | }; |
| 126 | 149 | } |
| ... | ... | @@ -130,6 +153,7 @@ pub const LoadCommand = union(enum) { |
| 130 | 153 | .Segment => |*x| x.deinit(allocator), |
| 131 | 154 | .Dylinker => |*x| x.deinit(allocator), |
| 132 | 155 | .Dylib => |*x| x.deinit(allocator), |
| 156 | .Rpath => |*x| x.deinit(allocator), | |
| 133 | 157 | .Unknown => |*x| x.deinit(allocator), |
| 134 | 158 | else => {}, |
| 135 | 159 | }; |
| ... | ... | @@ -153,6 +177,7 @@ pub const LoadCommand = union(enum) { |
| 153 | 177 | .Segment => |x| x.eql(other.Segment), |
| 154 | 178 | .Dylinker => |x| x.eql(other.Dylinker), |
| 155 | 179 | .Dylib => |x| x.eql(other.Dylib), |
| 180 | .Rpath => |x| x.eql(other.Rpath), | |
| 156 | 181 | .Unknown => |x| x.eql(other.Unknown), |
| 157 | 182 | }; |
| 158 | 183 | } |
| ... | ... | @@ -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 | 341 | fn testRead(allocator: *Allocator, buffer: []const u8, expected: anytype) !void { |
| 286 | 342 | var stream = io.fixedBufferStream(buffer); |
| 287 | 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 | 9 | cases.add("test/standalone/main_return_error/error_u8.zig"); |
| 10 | 10 | cases.add("test/standalone/main_return_error/error_u8_non_zero.zig"); |
| 11 | 11 | cases.addBuildFile("test/standalone/main_pkg_path/build.zig"); |
| 12 | if (std.Target.current.os.tag != .macos) { | |
| 13 | // TODO zld cannot link shared libraries yet. | |
| 14 | cases.addBuildFile("test/standalone/shared_library/build.zig"); | |
| 15 | } | |
| 12 | cases.addBuildFile("test/standalone/shared_library/build.zig"); | |
| 16 | 13 | cases.addBuildFile("test/standalone/mix_o_files/build.zig"); |
| 17 | 14 | cases.addBuildFile("test/standalone/global_linkage/build.zig"); |
| 18 | 15 | cases.addBuildFile("test/standalone/static_c_lib/build.zig"); |