| author | |
| committer | |
| log | 138cecc0283fdc3ec1b4343c15001e2802ed29de |
| tree | 135a487f95e754102c35d5a9f470d776870acc7e |
| parent | 35c694d614c8687ffa99ac187e60d831f9d7f29d |
The support is minimalistic in the sense that we only support actual
dylib files and not stubs/tbds yet, and we also don't support re-exports
just yet.6 files changed, 386 insertions(+), 54 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" |
src/link/MachO.zig+117-14| ... | @@ -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,22 +675,114 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -675,22 +675,114 @@ 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; | ||
| 678 | 679 | ||
| 679 | var input_files = std.ArrayList([]const u8).init(self.base.allocator); | 680 | // Positional arguments to the linker such as object files and static archives. |
| 680 | defer input_files.deinit(); | 681 | var positionals = std.ArrayList([]const u8).init(self.base.allocator); |
| 681 | // Positional arguments to the linker such as object files. | 682 | defer positionals.deinit(); |
| 682 | try input_files.appendSlice(self.base.options.objects); | 683 | |
| 684 | try positionals.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 | } |
| 686 | if (module_obj_path) |p| { | 688 | if (module_obj_path) |p| { |
| 687 | try input_files.append(p); | 689 | try positionals.append(p); |
| 688 | } | 690 | } |
| 689 | try input_files.append(comp.compiler_rt_static_lib.?.full_object_path); | 691 | try positionals.append(comp.compiler_rt_static_lib.?.full_object_path); |
| 692 | |||
| 690 | // libc++ dep | 693 | // libc++ dep |
| 691 | if (self.base.options.link_libcpp) { | 694 | if (self.base.options.link_libcpp) { |
| 692 | try input_files.append(comp.libcxxabi_static_lib.?.full_object_path); | 695 | try positionals.append(comp.libcxxabi_static_lib.?.full_object_path); |
| 693 | try input_files.append(comp.libcxx_static_lib.?.full_object_path); | 696 | try positionals.append(comp.libcxx_static_lib.?.full_object_path); |
| 697 | } | ||
| 698 | |||
| 699 | if (self.base.options.is_native_os) {} | ||
| 700 | |||
| 701 | // Shared libraries. | ||
| 702 | var shared_libs = std.ArrayList([]const u8).init(self.base.allocator); | ||
| 703 | defer { | ||
| 704 | for (shared_libs.items) |sh| { | ||
| 705 | self.base.allocator.free(sh); | ||
| 706 | } | ||
| 707 | shared_libs.deinit(); | ||
| 708 | } | ||
| 709 | |||
| 710 | var search_lib_names = std.ArrayList([]const u8).init(self.base.allocator); | ||
| 711 | defer search_lib_names.deinit(); | ||
| 712 | |||
| 713 | const system_libs = self.base.options.system_libs.items(); | ||
| 714 | for (system_libs) |entry| { | ||
| 715 | const link_lib = entry.key; | ||
| 716 | // By this time, we depend on these libs being dynamically linked libraries and not static libraries | ||
| 717 | // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which | ||
| 718 | // case we want to avoid prepending "-l". | ||
| 719 | if (Compilation.classifyFileExt(link_lib) == .shared_library) { | ||
| 720 | const path = try self.base.allocator.dupe(u8, link_lib); | ||
| 721 | try shared_libs.append(path); | ||
| 722 | continue; | ||
| 723 | } | ||
| 724 | |||
| 725 | try search_lib_names.append(link_lib); | ||
| 726 | } | ||
| 727 | |||
| 728 | for (search_lib_names.items) |l_name| { | ||
| 729 | // TODO text-based API, or .tbd files. | ||
| 730 | const l_name_ext = try std.fmt.allocPrint(self.base.allocator, "lib{s}.dylib", .{l_name}); | ||
| 731 | defer self.base.allocator.free(l_name_ext); | ||
| 732 | |||
| 733 | var found = false; | ||
| 734 | if (self.base.options.syslibroot) |syslibroot| { | ||
| 735 | for (self.base.options.lib_dirs) |lib_dir| { | ||
| 736 | const path = try fs.path.join(self.base.allocator, &[_][]const u8{ | ||
| 737 | syslibroot, | ||
| 738 | lib_dir, | ||
| 739 | l_name_ext, | ||
| 740 | }); | ||
| 741 | |||
| 742 | const tmp = fs.cwd().openFile(path, .{}) catch |err| switch (err) { | ||
| 743 | error.FileNotFound => { | ||
| 744 | self.base.allocator.free(path); | ||
| 745 | continue; | ||
| 746 | }, | ||
| 747 | else => |e| return e, | ||
| 748 | }; | ||
| 749 | defer tmp.close(); | ||
| 750 | |||
| 751 | try shared_libs.append(path); | ||
| 752 | found = true; | ||
| 753 | break; | ||
| 754 | } | ||
| 755 | } | ||
| 756 | |||
| 757 | for (self.base.options.lib_dirs) |lib_dir| { | ||
| 758 | const path = try fs.path.join(self.base.allocator, &[_][]const u8{ lib_dir, l_name_ext }); | ||
| 759 | |||
| 760 | const tmp = fs.cwd().openFile(path, .{}) catch |err| switch (err) { | ||
| 761 | error.FileNotFound => { | ||
| 762 | self.base.allocator.free(path); | ||
| 763 | continue; | ||
| 764 | }, | ||
| 765 | else => |e| return e, | ||
| 766 | }; | ||
| 767 | defer tmp.close(); | ||
| 768 | |||
| 769 | try shared_libs.append(path); | ||
| 770 | found = true; | ||
| 771 | break; | ||
| 772 | } | ||
| 773 | |||
| 774 | if (!found) { | ||
| 775 | log.warn("library '-l{s}' not found", .{l_name}); | ||
| 776 | log.warn("searched paths:", .{}); | ||
| 777 | if (self.base.options.syslibroot) |syslibroot| { | ||
| 778 | for (self.base.options.lib_dirs) |lib_dir| { | ||
| 779 | log.warn(" {s}/{s}", .{ syslibroot, lib_dir }); | ||
| 780 | } | ||
| 781 | } | ||
| 782 | for (self.base.options.lib_dirs) |lib_dir| { | ||
| 783 | log.warn(" {s}/", .{lib_dir}); | ||
| 784 | } | ||
| 785 | } | ||
| 694 | } | 786 | } |
| 695 | 787 | ||
| 696 | if (self.base.options.verbose_link) { | 788 | if (self.base.options.verbose_link) { |
| ... | @@ -700,17 +792,28 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -700,17 +792,28 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 700 | try argv.append("zig"); | 792 | try argv.append("zig"); |
| 701 | try argv.append("ld"); | 793 | try argv.append("ld"); |
| 702 | 794 | ||
| 703 | try argv.appendSlice(input_files.items); | 795 | if (self.base.options.syslibroot) |syslibroot| { |
| 796 | try argv.append("-syslibroot"); | ||
| 797 | try argv.append(syslibroot); | ||
| 798 | } | ||
| 799 | |||
| 800 | try argv.appendSlice(positionals.items); | ||
| 704 | 801 | ||
| 705 | try argv.append("-o"); | 802 | try argv.append("-o"); |
| 706 | try argv.append(full_out_path); | 803 | try argv.append(full_out_path); |
| 707 | 804 | ||
| 805 | for (search_lib_names.items) |l_name| { | ||
| 806 | try argv.append(try std.fmt.allocPrint(self.base.allocator, "-l{s}", .{l_name})); | ||
| 807 | } | ||
| 808 | |||
| 809 | for (self.base.options.lib_dirs) |lib_dir| { | ||
| 810 | try argv.append(try std.fmt.allocPrint(self.base.allocator, "-L{s}", .{lib_dir})); | ||
| 811 | } | ||
| 812 | |||
| 708 | Compilation.dump_argv(argv.items); | 813 | Compilation.dump_argv(argv.items); |
| 709 | } | 814 | } |
| 710 | 815 | ||
| 711 | try zld.link(input_files.items, full_out_path, .{ | 816 | try zld.link(positionals.items, shared_libs.items, full_out_path); |
| 712 | .stack_size = self.base.options.stack_size_override, | ||
| 713 | }); | ||
| 714 | 817 | ||
| 715 | break :outer; | 818 | break :outer; |
| 716 | } | 819 | } |
src/link/MachO/Dylib.zig created+137| ... | @@ -0,0 +1,137 @@ | ||
| 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 | |||
| 27 | symbols: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, | ||
| 28 | |||
| 29 | pub fn init(allocator: *Allocator) Dylib { | ||
| 30 | return .{ .allocator = allocator }; | ||
| 31 | } | ||
| 32 | |||
| 33 | pub fn deinit(self: *Dylib) void { | ||
| 34 | for (self.load_commands.items) |*lc| { | ||
| 35 | lc.deinit(self.allocator); | ||
| 36 | } | ||
| 37 | self.load_commands.deinit(self.allocator); | ||
| 38 | |||
| 39 | for (self.symbols.items()) |entry| { | ||
| 40 | entry.value.deinit(self.allocator); | ||
| 41 | self.allocator.destroy(entry.value); | ||
| 42 | } | ||
| 43 | self.symbols.deinit(self.allocator); | ||
| 44 | |||
| 45 | if (self.name) |name| { | ||
| 46 | self.allocator.free(name); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | pub fn closeFile(self: Dylib) void { | ||
| 51 | if (self.file) |file| { | ||
| 52 | file.close(); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | pub fn parse(self: *Dylib) !void { | ||
| 57 | log.warn("parsing shared library '{s}'", .{self.name.?}); | ||
| 58 | |||
| 59 | var reader = self.file.?.reader(); | ||
| 60 | self.header = try reader.readStruct(macho.mach_header_64); | ||
| 61 | |||
| 62 | if (self.header.?.filetype != macho.MH_DYLIB) { | ||
| 63 | log.err("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, self.header.?.filetype }); | ||
| 64 | return error.MalformedDylib; | ||
| 65 | } | ||
| 66 | |||
| 67 | const this_arch: std.Target.Cpu.Arch = switch (self.header.?.cputype) { | ||
| 68 | macho.CPU_TYPE_ARM64 => .aarch64, | ||
| 69 | macho.CPU_TYPE_X86_64 => .x86_64, | ||
| 70 | else => |value| { | ||
| 71 | log.err("unsupported cpu architecture 0x{x}", .{value}); | ||
| 72 | return error.UnsupportedCpuArchitecture; | ||
| 73 | }, | ||
| 74 | }; | ||
| 75 | if (this_arch != self.arch.?) { | ||
| 76 | log.err("mismatched cpu architecture: expected {s}, found {s}", .{ self.arch.?, this_arch }); | ||
| 77 | return error.MismatchedCpuArchitecture; | ||
| 78 | } | ||
| 79 | |||
| 80 | try self.readLoadCommands(reader); | ||
| 81 | try self.parseSymbols(); | ||
| 82 | } | ||
| 83 | |||
| 84 | pub fn readLoadCommands(self: *Dylib, reader: anytype) !void { | ||
| 85 | try self.load_commands.ensureCapacity(self.allocator, self.header.?.ncmds); | ||
| 86 | |||
| 87 | var i: u16 = 0; | ||
| 88 | while (i < self.header.?.ncmds) : (i += 1) { | ||
| 89 | var cmd = try LoadCommand.read(self.allocator, reader); | ||
| 90 | switch (cmd.cmd()) { | ||
| 91 | macho.LC_SYMTAB => { | ||
| 92 | self.symtab_cmd_index = i; | ||
| 93 | }, | ||
| 94 | macho.LC_DYSYMTAB => { | ||
| 95 | self.dysymtab_cmd_index = i; | ||
| 96 | }, | ||
| 97 | else => { | ||
| 98 | log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()}); | ||
| 99 | }, | ||
| 100 | } | ||
| 101 | self.load_commands.appendAssumeCapacity(cmd); | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | pub fn parseSymbols(self: *Dylib) !void { | ||
| 106 | const index = self.symtab_cmd_index orelse return; | ||
| 107 | const symtab_cmd = self.load_commands.items[index].Symtab; | ||
| 108 | |||
| 109 | var symtab = try self.allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms); | ||
| 110 | defer self.allocator.free(symtab); | ||
| 111 | _ = try self.file.?.preadAll(symtab, symtab_cmd.symoff); | ||
| 112 | const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, symtab)); | ||
| 113 | |||
| 114 | var strtab = try self.allocator.alloc(u8, symtab_cmd.strsize); | ||
| 115 | defer self.allocator.free(strtab); | ||
| 116 | _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff); | ||
| 117 | |||
| 118 | for (slice) |sym| { | ||
| 119 | const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx)); | ||
| 120 | |||
| 121 | if (!(Symbol.isSect(sym) and Symbol.isExt(sym))) continue; | ||
| 122 | |||
| 123 | const name = try self.allocator.dupe(u8, sym_name); | ||
| 124 | const proxy = try self.allocator.create(Symbol.Proxy); | ||
| 125 | errdefer self.allocator.destroy(proxy); | ||
| 126 | |||
| 127 | proxy.* = .{ | ||
| 128 | .base = .{ | ||
| 129 | .@"type" = .proxy, | ||
| 130 | .name = name, | ||
| 131 | }, | ||
| 132 | .dylib = self, | ||
| 133 | }; | ||
| 134 | |||
| 135 | try self.symbols.putNoClobber(self.allocator, name, &proxy.base); | ||
| 136 | } | ||
| 137 | } | ||
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+106-32| ... | @@ -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 | { |
| ... | @@ -176,11 +186,7 @@ pub fn closeFiles(self: Zld) void { | ... | @@ -176,11 +186,7 @@ pub fn closeFiles(self: Zld) void { |
| 176 | if (self.file) |f| f.close(); | 186 | if (self.file) |f| f.close(); |
| 177 | } | 187 | } |
| 178 | 188 | ||
| 179 | const LinkArgs = struct { | 189 | pub fn link(self: *Zld, files: []const []const u8, shared_libs: []const []const u8, out_path: []const u8) !void { |
| 180 | stack_size: ?u64 = null, | ||
| 181 | }; | ||
| 182 | |||
| 183 | pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: LinkArgs) !void { | ||
| 184 | if (files.len == 0) return error.NoInputFiles; | 190 | if (files.len == 0) return error.NoInputFiles; |
| 185 | if (out_path.len == 0) return error.EmptyOutputPath; | 191 | if (out_path.len == 0) return error.EmptyOutputPath; |
| 186 | 192 | ||
| ... | @@ -214,10 +220,10 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L | ... | @@ -214,10 +220,10 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L |
| 214 | .read = true, | 220 | .read = true, |
| 215 | .mode = if (std.Target.current.os.tag == .windows) 0 else 0o777, | 221 | .mode = if (std.Target.current.os.tag == .windows) 0 else 0o777, |
| 216 | }); | 222 | }); |
| 217 | self.stack_size = args.stack_size orelse 0; | ||
| 218 | 223 | ||
| 219 | try self.populateMetadata(); | 224 | try self.populateMetadata(); |
| 220 | try self.parseInputFiles(files); | 225 | try self.parseInputFiles(files); |
| 226 | try self.parseDylibs(shared_libs); | ||
| 221 | try self.resolveSymbols(); | 227 | try self.resolveSymbols(); |
| 222 | try self.resolveStubsAndGotEntries(); | 228 | try self.resolveStubsAndGotEntries(); |
| 223 | try self.updateMetadata(); | 229 | try self.updateMetadata(); |
| ... | @@ -315,6 +321,48 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { | ... | @@ -315,6 +321,48 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { |
| 315 | } | 321 | } |
| 316 | } | 322 | } |
| 317 | 323 | ||
| 324 | fn parseDylibs(self: *Zld, shared_libs: []const []const u8) !void { | ||
| 325 | for (shared_libs) |lib| { | ||
| 326 | const dylib = try self.allocator.create(Dylib); | ||
| 327 | errdefer self.allocator.destroy(dylib); | ||
| 328 | |||
| 329 | dylib.* = Dylib.init(self.allocator); | ||
| 330 | dylib.arch = self.arch.?; | ||
| 331 | dylib.name = try self.allocator.dupe(u8, lib); | ||
| 332 | dylib.file = try fs.cwd().openFile(lib, .{}); | ||
| 333 | |||
| 334 | const ordinal = @intCast(u16, self.dylibs.items.len); | ||
| 335 | dylib.ordinal = ordinal + 2; // TODO +2 since 1 is reserved for libSystem | ||
| 336 | |||
| 337 | // TODO Defer parsing of the dylibs until they are actually needed | ||
| 338 | try dylib.parse(); | ||
| 339 | try self.dylibs.append(self.allocator, dylib); | ||
| 340 | |||
| 341 | // Add LC_LOAD_DYLIB command | ||
| 342 | const cmdsize = @intCast(u32, mem.alignForwardGeneric( | ||
| 343 | u64, | ||
| 344 | @sizeOf(macho.dylib_command) + dylib.name.?.len, | ||
| 345 | @sizeOf(u64), | ||
| 346 | )); | ||
| 347 | // TODO Read the min version from the dylib itself. | ||
| 348 | const min_version = 0x0; | ||
| 349 | var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{ | ||
| 350 | .cmd = macho.LC_LOAD_DYLIB, | ||
| 351 | .cmdsize = cmdsize, | ||
| 352 | .dylib = .{ | ||
| 353 | .name = @sizeOf(macho.dylib_command), | ||
| 354 | .timestamp = 2, // TODO parse from the dylib. | ||
| 355 | .current_version = min_version, | ||
| 356 | .compatibility_version = min_version, | ||
| 357 | }, | ||
| 358 | }); | ||
| 359 | dylib_cmd.data = try self.allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name); | ||
| 360 | mem.set(u8, dylib_cmd.data, 0); | ||
| 361 | mem.copy(u8, dylib_cmd.data, dylib.name.?); | ||
| 362 | try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd }); | ||
| 363 | } | ||
| 364 | } | ||
| 365 | |||
| 318 | fn mapAndUpdateSections( | 366 | fn mapAndUpdateSections( |
| 319 | self: *Zld, | 367 | self: *Zld, |
| 320 | object_id: u16, | 368 | object_id: u16, |
| ... | @@ -1398,35 +1446,51 @@ fn resolveSymbols(self: *Zld) !void { | ... | @@ -1398,35 +1446,51 @@ fn resolveSymbols(self: *Zld) !void { |
| 1398 | // Third pass, resolve symbols in dynamic libraries. | 1446 | // Third pass, resolve symbols in dynamic libraries. |
| 1399 | // TODO Implement libSystem as a hard-coded library, or ship with | 1447 | // TODO Implement libSystem as a hard-coded library, or ship with |
| 1400 | // a libSystem.B.tbd definition file? | 1448 | // a libSystem.B.tbd definition file? |
| 1401 | try self.imports.ensureCapacity(self.allocator, self.unresolved.count()); | 1449 | var unresolved = std.ArrayList(*Symbol).init(self.allocator); |
| 1402 | for (self.unresolved.items()) |entry| { | 1450 | defer unresolved.deinit(); |
| 1403 | const proxy = try self.allocator.create(Symbol.Proxy); | ||
| 1404 | errdefer self.allocator.destroy(proxy); | ||
| 1405 | 1451 | ||
| 1406 | proxy.* = .{ | 1452 | try unresolved.ensureCapacity(self.unresolved.count()); |
| 1407 | .base = .{ | 1453 | for (self.unresolved.items()) |entry| { |
| 1408 | .@"type" = .proxy, | 1454 | 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 | } | 1455 | } |
| 1417 | self.unresolved.clearAndFree(self.allocator); | 1456 | self.unresolved.clearAndFree(self.allocator); |
| 1418 | 1457 | ||
| 1419 | // If there are any undefs left, flag an error. | 1458 | var has_undefined = false; |
| 1420 | if (self.unresolved.count() > 0) { | 1459 | while (unresolved.popOrNull()) |undef| { |
| 1421 | for (self.unresolved.items()) |entry| { | 1460 | var found = false; |
| 1422 | log.err("undefined reference to symbol '{s}'", .{entry.key}); | 1461 | for (self.dylibs.items) |dylib| { |
| 1423 | log.err(" | referenced in {s}", .{ | 1462 | const proxy = dylib.symbols.get(undef.name) orelse continue; |
| 1424 | entry.value.cast(Symbol.Unresolved).?.file.name.?, | 1463 | try self.imports.putNoClobber(self.allocator, proxy.name, proxy); |
| 1425 | }); | 1464 | undef.alias = proxy; |
| 1465 | found = true; | ||
| 1466 | } | ||
| 1467 | |||
| 1468 | if (!found) { | ||
| 1469 | // TODO we currently hardcode all unresolved symbols to libSystem | ||
| 1470 | const proxy = try self.allocator.create(Symbol.Proxy); | ||
| 1471 | errdefer self.allocator.destroy(proxy); | ||
| 1472 | |||
| 1473 | proxy.* = .{ | ||
| 1474 | .base = .{ | ||
| 1475 | .@"type" = .proxy, | ||
| 1476 | .name = try self.allocator.dupe(u8, undef.name), | ||
| 1477 | }, | ||
| 1478 | .dylib = null, // TODO null means libSystem | ||
| 1479 | }; | ||
| 1480 | |||
| 1481 | try self.imports.putNoClobber(self.allocator, proxy.base.name, &proxy.base); | ||
| 1482 | undef.alias = &proxy.base; | ||
| 1483 | |||
| 1484 | // log.err("undefined reference to symbol '{s}'", .{undef.name}); | ||
| 1485 | // log.err(" | referenced in {s}", .{ | ||
| 1486 | // undef.cast(Symbol.Unresolved).?.file.name.?, | ||
| 1487 | // }); | ||
| 1488 | // has_undefined = true; | ||
| 1426 | } | 1489 | } |
| 1427 | return error.UndefinedSymbolReference; | ||
| 1428 | } | 1490 | } |
| 1429 | 1491 | ||
| 1492 | if (has_undefined) return error.UndefinedSymbolReference; | ||
| 1493 | |||
| 1430 | // Finally put dyld_stub_binder as an Import | 1494 | // Finally put dyld_stub_binder as an Import |
| 1431 | const dyld_stub_binder = try self.allocator.create(Symbol.Proxy); | 1495 | const dyld_stub_binder = try self.allocator.create(Symbol.Proxy); |
| 1432 | errdefer self.allocator.destroy(dyld_stub_binder); | 1496 | errdefer self.allocator.destroy(dyld_stub_binder); |
| ... | @@ -1436,7 +1500,7 @@ fn resolveSymbols(self: *Zld) !void { | ... | @@ -1436,7 +1500,7 @@ fn resolveSymbols(self: *Zld) !void { |
| 1436 | .@"type" = .proxy, | 1500 | .@"type" = .proxy, |
| 1437 | .name = try self.allocator.dupe(u8, "dyld_stub_binder"), | 1501 | .name = try self.allocator.dupe(u8, "dyld_stub_binder"), |
| 1438 | }, | 1502 | }, |
| 1439 | .dylib = 0, | 1503 | .dylib = null, // TODO null means libSystem |
| 1440 | }; | 1504 | }; |
| 1441 | 1505 | ||
| 1442 | try self.imports.putNoClobber( | 1506 | try self.imports.putNoClobber( |
| ... | @@ -2303,7 +2367,10 @@ fn writeBindInfoTable(self: *Zld) !void { | ... | @@ -2303,7 +2367,10 @@ fn writeBindInfoTable(self: *Zld) !void { |
| 2303 | 2367 | ||
| 2304 | for (self.got_entries.items) |sym| { | 2368 | for (self.got_entries.items) |sym| { |
| 2305 | if (sym.cast(Symbol.Proxy)) |proxy| { | 2369 | if (sym.cast(Symbol.Proxy)) |proxy| { |
| 2306 | const dylib_ordinal = proxy.dylib + 1; | 2370 | const dylib_ordinal = ordinal: { |
| 2371 | const dylib = proxy.dylib orelse break :ordinal 1; // TODO embedded libSystem | ||
| 2372 | break :ordinal dylib.ordinal.?; | ||
| 2373 | }; | ||
| 2307 | try pointers.append(.{ | 2374 | try pointers.append(.{ |
| 2308 | .offset = base_offset + proxy.base.got_index.? * @sizeOf(u64), | 2375 | .offset = base_offset + proxy.base.got_index.? * @sizeOf(u64), |
| 2309 | .segment_id = segment_id, | 2376 | .segment_id = segment_id, |
| ... | @@ -2322,7 +2389,10 @@ fn writeBindInfoTable(self: *Zld) !void { | ... | @@ -2322,7 +2389,10 @@ fn writeBindInfoTable(self: *Zld) !void { |
| 2322 | 2389 | ||
| 2323 | const sym = self.imports.get("__tlv_bootstrap") orelse unreachable; | 2390 | const sym = self.imports.get("__tlv_bootstrap") orelse unreachable; |
| 2324 | const proxy = sym.cast(Symbol.Proxy) orelse unreachable; | 2391 | const proxy = sym.cast(Symbol.Proxy) orelse unreachable; |
| 2325 | const dylib_ordinal = proxy.dylib + 1; | 2392 | const dylib_ordinal = ordinal: { |
| 2393 | const dylib = proxy.dylib orelse break :ordinal 1; // TODO embedded libSystem | ||
| 2394 | break :ordinal dylib.ordinal.?; | ||
| 2395 | }; | ||
| 2326 | 2396 | ||
| 2327 | try pointers.append(.{ | 2397 | try pointers.append(.{ |
| 2328 | .offset = base_offset, | 2398 | .offset = base_offset, |
| ... | @@ -2364,7 +2434,11 @@ fn writeLazyBindInfoTable(self: *Zld) !void { | ... | @@ -2364,7 +2434,11 @@ fn writeLazyBindInfoTable(self: *Zld) !void { |
| 2364 | 2434 | ||
| 2365 | for (self.stubs.items) |sym| { | 2435 | for (self.stubs.items) |sym| { |
| 2366 | const proxy = sym.cast(Symbol.Proxy) orelse unreachable; | 2436 | const proxy = sym.cast(Symbol.Proxy) orelse unreachable; |
| 2367 | const dylib_ordinal = proxy.dylib + 1; | 2437 | const dylib_ordinal = ordinal: { |
| 2438 | const dylib = proxy.dylib orelse break :ordinal 1; // TODO embedded libSystem | ||
| 2439 | break :ordinal dylib.ordinal.?; | ||
| 2440 | }; | ||
| 2441 | |||
| 2368 | pointers.appendAssumeCapacity(.{ | 2442 | pointers.appendAssumeCapacity(.{ |
| 2369 | .offset = base_offset + sym.stubs_index.? * @sizeOf(u64), | 2443 | .offset = base_offset + sym.stubs_index.? * @sizeOf(u64), |
| 2370 | .segment_id = segment_id, | 2444 | .segment_id = segment_id, |
src/link/MachO/commands.zig+21-5| ... | @@ -38,7 +38,9 @@ pub const LoadCommand = union(enum) { | ... | @@ -38,7 +38,9 @@ pub const LoadCommand = union(enum) { |
| 38 | macho.LC_SEGMENT_64 => LoadCommand{ | 38 | macho.LC_SEGMENT_64 => LoadCommand{ |
| 39 | .Segment = try SegmentCommand.read(allocator, stream.reader()), | 39 | .Segment = try SegmentCommand.read(allocator, stream.reader()), |
| 40 | }, | 40 | }, |
| 41 | macho.LC_DYLD_INFO, macho.LC_DYLD_INFO_ONLY => LoadCommand{ | 41 | macho.LC_DYLD_INFO, |
| 42 | macho.LC_DYLD_INFO_ONLY, | ||
| 43 | => LoadCommand{ | ||
| 42 | .DyldInfoOnly = try stream.reader().readStruct(macho.dyld_info_command), | 44 | .DyldInfoOnly = try stream.reader().readStruct(macho.dyld_info_command), |
| 43 | }, | 45 | }, |
| 44 | macho.LC_SYMTAB => LoadCommand{ | 46 | macho.LC_SYMTAB => LoadCommand{ |
| ... | @@ -47,16 +49,27 @@ pub const LoadCommand = union(enum) { | ... | @@ -47,16 +49,27 @@ pub const LoadCommand = union(enum) { |
| 47 | macho.LC_DYSYMTAB => LoadCommand{ | 49 | macho.LC_DYSYMTAB => LoadCommand{ |
| 48 | .Dysymtab = try stream.reader().readStruct(macho.dysymtab_command), | 50 | .Dysymtab = try stream.reader().readStruct(macho.dysymtab_command), |
| 49 | }, | 51 | }, |
| 50 | macho.LC_ID_DYLINKER, macho.LC_LOAD_DYLINKER, macho.LC_DYLD_ENVIRONMENT => LoadCommand{ | 52 | macho.LC_ID_DYLINKER, |
| 53 | macho.LC_LOAD_DYLINKER, | ||
| 54 | macho.LC_DYLD_ENVIRONMENT, | ||
| 55 | => LoadCommand{ | ||
| 51 | .Dylinker = try GenericCommandWithData(macho.dylinker_command).read(allocator, stream.reader()), | 56 | .Dylinker = try GenericCommandWithData(macho.dylinker_command).read(allocator, stream.reader()), |
| 52 | }, | 57 | }, |
| 53 | macho.LC_ID_DYLIB, macho.LC_LOAD_WEAK_DYLIB, macho.LC_LOAD_DYLIB, macho.LC_REEXPORT_DYLIB => LoadCommand{ | 58 | macho.LC_ID_DYLIB, |
| 59 | macho.LC_LOAD_WEAK_DYLIB, | ||
| 60 | macho.LC_LOAD_DYLIB, | ||
| 61 | macho.LC_REEXPORT_DYLIB, | ||
| 62 | => LoadCommand{ | ||
| 54 | .Dylib = try GenericCommandWithData(macho.dylib_command).read(allocator, stream.reader()), | 63 | .Dylib = try GenericCommandWithData(macho.dylib_command).read(allocator, stream.reader()), |
| 55 | }, | 64 | }, |
| 56 | macho.LC_MAIN => LoadCommand{ | 65 | macho.LC_MAIN => LoadCommand{ |
| 57 | .Main = try stream.reader().readStruct(macho.entry_point_command), | 66 | .Main = try stream.reader().readStruct(macho.entry_point_command), |
| 58 | }, | 67 | }, |
| 59 | macho.LC_VERSION_MIN_MACOSX, macho.LC_VERSION_MIN_IPHONEOS, macho.LC_VERSION_MIN_WATCHOS, macho.LC_VERSION_MIN_TVOS => LoadCommand{ | 68 | macho.LC_VERSION_MIN_MACOSX, |
| 69 | macho.LC_VERSION_MIN_IPHONEOS, | ||
| 70 | macho.LC_VERSION_MIN_WATCHOS, | ||
| 71 | macho.LC_VERSION_MIN_TVOS, | ||
| 72 | => LoadCommand{ | ||
| 60 | .VersionMin = try stream.reader().readStruct(macho.version_min_command), | 73 | .VersionMin = try stream.reader().readStruct(macho.version_min_command), |
| 61 | }, | 74 | }, |
| 62 | macho.LC_SOURCE_VERSION => LoadCommand{ | 75 | macho.LC_SOURCE_VERSION => LoadCommand{ |
| ... | @@ -65,7 +78,10 @@ pub const LoadCommand = union(enum) { | ... | @@ -65,7 +78,10 @@ pub const LoadCommand = union(enum) { |
| 65 | macho.LC_UUID => LoadCommand{ | 78 | macho.LC_UUID => LoadCommand{ |
| 66 | .Uuid = try stream.reader().readStruct(macho.uuid_command), | 79 | .Uuid = try stream.reader().readStruct(macho.uuid_command), |
| 67 | }, | 80 | }, |
| 68 | macho.LC_FUNCTION_STARTS, macho.LC_DATA_IN_CODE, macho.LC_CODE_SIGNATURE => LoadCommand{ | 81 | macho.LC_FUNCTION_STARTS, |
| 82 | macho.LC_DATA_IN_CODE, | ||
| 83 | macho.LC_CODE_SIGNATURE, | ||
| 84 | => LoadCommand{ | ||
| 69 | .LinkeditData = try stream.reader().readStruct(macho.linkedit_data_command), | 85 | .LinkeditData = try stream.reader().readStruct(macho.linkedit_data_command), |
| 70 | }, | 86 | }, |
| 71 | else => LoadCommand{ | 87 | else => LoadCommand{ |