authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-05-18 11:04:06+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-05-18 11:04:06+02:00
log7588fcdd2a7c232286f2c298bd14cbfb65e54604
tree5f2a7bf072f90ee1e95e508307642a97a9e172c0
parent35c694d614c8687ffa99ac187e60d831f9d7f29d
parentcb45c5521ab5360e2b8c5d1ce16d351d526a3fe5
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #8806 from ziglang/zld-link-shared

zig ld: add preliminary mechanism for linking dylibs

8 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};
7373
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.
77pub 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
98pub 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 stacksize108/// 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};
486518
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.
521pub 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 be532/// 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;
553553
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);
678684
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++ dep695 // 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 }
695804
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();
699807
700 try argv.append("zig");808 try argv.append("zig");
701 try argv.append("ld");809 try argv.append("ld");
702810
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);
704822
705 try argv.append("-o");823 try argv.append("-o");
706 try argv.append(full_out_path);824 try argv.append(full_out_path);
707825
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 }
710836
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 });
714841
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 @@
1const Dylib = @This();
2
3const std = @import("std");
4const fs = std.fs;
5const log = std.log.scoped(.dylib);
6const macho = std.macho;
7const mem = std.mem;
8
9const Allocator = mem.Allocator;
10const Symbol = @import("Symbol.zig");
11
12usingnamespace @import("commands.zig");
13
14allocator: *Allocator,
15arch: ?std.Target.Cpu.Arch = null,
16header: ?macho.mach_header_64 = null,
17file: ?fs.File = null,
18name: ?[]const u8 = null,
19
20ordinal: ?u16 = null,
21
22load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
23
24symtab_cmd_index: ?u16 = null,
25dysymtab_cmd_index: ?u16 = null,
26id_cmd_index: ?u16 = null,
27
28id: ?Id = null,
29
30symbols: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
31
32pub 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
43pub fn init(allocator: *Allocator) Dylib {
44 return .{ .allocator = allocator };
45}
46
47pub 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
68pub fn closeFile(self: Dylib) void {
69 if (self.file) |file| {
70 file.close();
71 }
72}
73
74pub 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
103pub 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
127pub 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
153pub 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;
5const mem = std.mem;5const mem = std.mem;
66
7const Allocator = mem.Allocator;7const Allocator = mem.Allocator;
8const Dylib = @import("Dylib.zig");
8const Object = @import("Object.zig");9const Object = @import("Object.zig");
910
10pub const Type = enum {11pub 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,
4546
46 /// File where to locate this symbol.47 /// Object file where to locate this symbol.
47 file: *Object,48 file: *Object,
4849
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 {
78pub const Proxy = struct {79pub const Proxy = struct {
79 base: Symbol,80 base: Symbol,
8081
81 /// Dylib ordinal.82 /// Dylib where to locate this symbol.
82 dylib: u16,83 dylib: ?*Dylib = null,
8384
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");
15const Allocator = mem.Allocator;15const Allocator = mem.Allocator;
16const Archive = @import("Archive.zig");16const Archive = @import("Archive.zig");
17const CodeSignature = @import("CodeSignature.zig");17const CodeSignature = @import("CodeSignature.zig");
18const Dylib = @import("Dylib.zig");
18const Object = @import("Object.zig");19const Object = @import("Object.zig");
19const Symbol = @import("Symbol.zig");20const Symbol = @import("Symbol.zig");
20const Trie = @import("Trie.zig");21const Trie = @import("Trie.zig");
...@@ -35,6 +36,7 @@ stack_size: u64 = 0,...@@ -35,6 +36,7 @@ stack_size: u64 = 0,
3536
36objects: std.ArrayListUnmanaged(*Object) = .{},37objects: std.ArrayListUnmanaged(*Object) = .{},
37archives: std.ArrayListUnmanaged(*Archive) = .{},38archives: std.ArrayListUnmanaged(*Archive) = .{},
39dylibs: std.ArrayListUnmanaged(*Dylib) = .{},
3840
39load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},41load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
4042
...@@ -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);
153155
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);
156164
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);
159169
160 {170 {
...@@ -177,7 +187,8 @@ pub fn closeFiles(self: Zld) void {...@@ -177,7 +187,8 @@ pub fn closeFiles(self: Zld) void {
177}187}
178188
179const LinkArgs = struct {189const LinkArgs = struct {
180 stack_size: ?u64 = null,190 shared_libs: []const []const u8,
191 rpaths: []const []const u8,
181};192};
182193
183pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: LinkArgs) !void {194pub 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;
218228
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();
244257
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 }
285298
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 }
288317
...@@ -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}
317375
376fn 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
318fn mapAndUpdateSections(408fn 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 with1489 // 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);
14051493
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);
14181499
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 }
14291533
1534 if (has_undefined) return error.UndefinedSymbolReference;
1535
1430 // Finally put dyld_stub_binder as an Import1536 // 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 };
14411547
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 {
19972103
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 }
20232112
...@@ -2080,6 +2169,25 @@ fn populateMetadata(self: *Zld) !void {...@@ -2080,6 +2169,25 @@ fn populateMetadata(self: *Zld) !void {
2080 }2169 }
2081}2170}
20822171
2172fn 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
2083fn flush(self: *Zld) !void {2191fn 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 {
23032411
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 {
23222433
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 };
23262440
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 {
23642478
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 {
26642782
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),
2829
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}
284309
310pub 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
285fn testRead(allocator: *Allocator, buffer: []const u8, expected: anytype) !void {341fn 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");