| author | |
| committer | |
| log | f6d13e9d6f7a4b9161f369544501ecbb447c1658 |
| tree | a87db008c00c7be1ef979e6e3b2072c753890564 |
| parent | e0b53ad3c99b8f38d2fdba7b9aa6bf3e638dbeb9 |
6 files changed, 2737 insertions(+), 3458 deletions(-)
CMakeLists.txt-1| ... | ... | @@ -583,7 +583,6 @@ set(ZIG_STAGE2_SOURCES |
| 583 | 583 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig" |
| 584 | 584 | "${CMAKE_SOURCE_DIR}/src/link/MachO/TextBlock.zig" |
| 585 | 585 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig" |
| 586 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig" | |
| 587 | 586 | "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig" |
| 588 | 587 | "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig" |
| 589 | 588 | "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig" |
src/link/MachO.zig+2531-247| ... | ... | @@ -20,16 +20,19 @@ const target_util = @import("../target.zig"); |
| 20 | 20 | const trace = @import("../tracy.zig").trace; |
| 21 | 21 | |
| 22 | 22 | const Allocator = mem.Allocator; |
| 23 | const Archive = @import("MachO/Archive.zig"); | |
| 23 | 24 | const Cache = @import("../Cache.zig"); |
| 24 | 25 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| 25 | 26 | const Compilation = @import("../Compilation.zig"); |
| 26 | 27 | const DebugSymbols = @import("MachO/DebugSymbols.zig"); |
| 28 | const Dylib = @import("MachO/Dylib.zig"); | |
| 29 | const Object = @import("MachO/Object.zig"); | |
| 27 | 30 | const LoadCommand = commands.LoadCommand; |
| 28 | 31 | const Module = @import("../Module.zig"); |
| 29 | 32 | const File = link.File; |
| 33 | pub const TextBlock = @import("MachO/TextBlock.zig"); | |
| 30 | 34 | const Trie = @import("MachO/Trie.zig"); |
| 31 | 35 | const SegmentCommand = commands.SegmentCommand; |
| 32 | const Zld = @import("MachO/Zld.zig"); | |
| 33 | 36 | |
| 34 | 37 | pub const base_tag: File.Tag = File.Tag.macho; |
| 35 | 38 | |
| ... | ... | @@ -47,63 +50,83 @@ page_size: u16, |
| 47 | 50 | /// potential future extensions. |
| 48 | 51 | header_pad: u16 = 0x1000, |
| 49 | 52 | |
| 50 | /// Table of all load commands | |
| 53 | /// The absolute address of the entry point. | |
| 54 | entry_addr: ?u64 = null, | |
| 55 | ||
| 56 | objects: std.ArrayListUnmanaged(*Object) = .{}, | |
| 57 | archives: std.ArrayListUnmanaged(*Archive) = .{}, | |
| 58 | dylibs: std.ArrayListUnmanaged(*Dylib) = .{}, | |
| 59 | ||
| 60 | next_dylib_ordinal: u16 = 1, | |
| 61 | ||
| 51 | 62 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| 52 | /// __PAGEZERO segment | |
| 63 | ||
| 53 | 64 | pagezero_segment_cmd_index: ?u16 = null, |
| 54 | /// __TEXT segment | |
| 55 | 65 | text_segment_cmd_index: ?u16 = null, |
| 56 | /// __DATA_CONST segment | |
| 57 | 66 | data_const_segment_cmd_index: ?u16 = null, |
| 58 | /// __DATA segment | |
| 59 | 67 | data_segment_cmd_index: ?u16 = null, |
| 60 | /// __LINKEDIT segment | |
| 61 | 68 | linkedit_segment_cmd_index: ?u16 = null, |
| 62 | /// Dyld info | |
| 63 | 69 | dyld_info_cmd_index: ?u16 = null, |
| 64 | /// Symbol table | |
| 65 | 70 | symtab_cmd_index: ?u16 = null, |
| 66 | /// Dynamic symbol table | |
| 67 | 71 | dysymtab_cmd_index: ?u16 = null, |
| 68 | /// Path to dyld linker | |
| 69 | 72 | dylinker_cmd_index: ?u16 = null, |
| 70 | /// Path to libSystem | |
| 71 | libsystem_cmd_index: ?u16 = null, | |
| 72 | /// Data-in-code section of __LINKEDIT segment | |
| 73 | 73 | data_in_code_cmd_index: ?u16 = null, |
| 74 | /// Address to entry point function | |
| 75 | 74 | function_starts_cmd_index: ?u16 = null, |
| 76 | /// Main/entry point | |
| 77 | /// Specifies offset wrt __TEXT segment start address to the main entry point | |
| 78 | /// of the binary. | |
| 79 | 75 | main_cmd_index: ?u16 = null, |
| 80 | /// Minimum OS version | |
| 76 | dylib_id_cmd_index: ?u16 = null, | |
| 81 | 77 | version_min_cmd_index: ?u16 = null, |
| 82 | /// Source version | |
| 83 | 78 | source_version_cmd_index: ?u16 = null, |
| 84 | /// UUID load command | |
| 85 | 79 | uuid_cmd_index: ?u16 = null, |
| 86 | /// Code signature | |
| 87 | 80 | code_signature_cmd_index: ?u16 = null, |
| 81 | /// Path to libSystem | |
| 82 | /// TODO this is obsolete, remove it. | |
| 83 | libsystem_cmd_index: ?u16 = null, | |
| 88 | 84 | |
| 89 | /// Index into __TEXT,__text section. | |
| 85 | // __TEXT segment sections | |
| 90 | 86 | text_section_index: ?u16 = null, |
| 91 | /// Index into __TEXT,__stubs section. | |
| 92 | 87 | stubs_section_index: ?u16 = null, |
| 93 | /// Index into __TEXT,__stub_helper section. | |
| 94 | 88 | stub_helper_section_index: ?u16 = null, |
| 95 | /// Index into __DATA_CONST,__got section. | |
| 89 | text_const_section_index: ?u16 = null, | |
| 90 | cstring_section_index: ?u16 = null, | |
| 91 | ustring_section_index: ?u16 = null, | |
| 92 | gcc_except_tab_section_index: ?u16 = null, | |
| 93 | unwind_info_section_index: ?u16 = null, | |
| 94 | eh_frame_section_index: ?u16 = null, | |
| 95 | ||
| 96 | objc_methlist_section_index: ?u16 = null, | |
| 97 | objc_methname_section_index: ?u16 = null, | |
| 98 | objc_methtype_section_index: ?u16 = null, | |
| 99 | objc_classname_section_index: ?u16 = null, | |
| 100 | ||
| 101 | // __DATA_CONST segment sections | |
| 96 | 102 | got_section_index: ?u16 = null, |
| 97 | /// Index into __DATA,__la_symbol_ptr section. | |
| 103 | mod_init_func_section_index: ?u16 = null, | |
| 104 | mod_term_func_section_index: ?u16 = null, | |
| 105 | data_const_section_index: ?u16 = null, | |
| 106 | ||
| 107 | objc_cfstring_section_index: ?u16 = null, | |
| 108 | objc_classlist_section_index: ?u16 = null, | |
| 109 | objc_imageinfo_section_index: ?u16 = null, | |
| 110 | ||
| 111 | // __DATA segment sections | |
| 112 | tlv_section_index: ?u16 = null, | |
| 113 | tlv_data_section_index: ?u16 = null, | |
| 114 | tlv_bss_section_index: ?u16 = null, | |
| 98 | 115 | la_symbol_ptr_section_index: ?u16 = null, |
| 99 | /// Index into __DATA,__data section. | |
| 100 | 116 | data_section_index: ?u16 = null, |
| 101 | /// The absolute address of the entry point. | |
| 102 | entry_addr: ?u64 = null, | |
| 117 | bss_section_index: ?u16 = null, | |
| 118 | common_section_index: ?u16 = null, | |
| 119 | ||
| 120 | objc_const_section_index: ?u16 = null, | |
| 121 | objc_selrefs_section_index: ?u16 = null, | |
| 122 | objc_classrefs_section_index: ?u16 = null, | |
| 123 | objc_data_section_index: ?u16 = null, | |
| 103 | 124 | |
| 104 | 125 | locals: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 105 | 126 | globals: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 106 | 127 | imports: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 128 | undefs: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | |
| 129 | tentatives: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | |
| 107 | 130 | symbol_resolver: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{}, |
| 108 | 131 | |
| 109 | 132 | locals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| ... | ... | @@ -133,6 +156,9 @@ export_info_dirty: bool = false, |
| 133 | 156 | strtab_dirty: bool = false, |
| 134 | 157 | strtab_needs_relocation: bool = false, |
| 135 | 158 | |
| 159 | has_dices: bool = false, | |
| 160 | has_stabs: bool = false, | |
| 161 | ||
| 136 | 162 | /// A list of text blocks that have surplus capacity. This list can have false |
| 137 | 163 | /// positives, as functions grow and shrink over time, only sometimes being added |
| 138 | 164 | /// or removed from the freelist. |
| ... | ... | @@ -153,6 +179,8 @@ text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, |
| 153 | 179 | /// Pointer to the last allocated text block |
| 154 | 180 | last_text_block: ?*TextBlock = null, |
| 155 | 181 | |
| 182 | blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{}, | |
| 183 | ||
| 156 | 184 | /// A list of all PIE fixups required for this run of the linker. |
| 157 | 185 | /// Warning, this is currently NOT thread-safe. See the TODO below. |
| 158 | 186 | /// TODO Move this list inside `updateDecl` where it should be allocated |
| ... | ... | @@ -236,71 +264,7 @@ const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B. |
| 236 | 264 | /// it as a possible place to put new symbols, it must have enough room for this many bytes |
| 237 | 265 | /// (plus extra for reserved capacity). |
| 238 | 266 | const minimum_text_block_size = 64; |
| 239 | const min_text_capacity = padToIdeal(minimum_text_block_size); | |
| 240 | ||
| 241 | pub const TextBlock = struct { | |
| 242 | /// Each decl always gets a local symbol with the fully qualified name. | |
| 243 | /// The vaddr and size are found here directly. | |
| 244 | /// The file offset is found by computing the vaddr offset from the section vaddr | |
| 245 | /// the symbol references, and adding that to the file offset of the section. | |
| 246 | /// If this field is 0, it means the codegen size = 0 and there is no symbol or | |
| 247 | /// offset table entry. | |
| 248 | local_sym_index: u32, | |
| 249 | /// Size of this text block | |
| 250 | /// Unlike in Elf, we need to store the size of this symbol as part of | |
| 251 | /// the TextBlock since macho.nlist_64 lacks this information. | |
| 252 | size: u64, | |
| 253 | /// Points to the previous and next neighbours | |
| 254 | prev: ?*TextBlock, | |
| 255 | next: ?*TextBlock, | |
| 256 | ||
| 257 | /// Previous/next linked list pointers. | |
| 258 | /// This is the linked list node for this Decl's corresponding .debug_info tag. | |
| 259 | dbg_info_prev: ?*TextBlock, | |
| 260 | dbg_info_next: ?*TextBlock, | |
| 261 | /// Offset into .debug_info pointing to the tag for this Decl. | |
| 262 | dbg_info_off: u32, | |
| 263 | /// Size of the .debug_info tag for this Decl, not including padding. | |
| 264 | dbg_info_len: u32, | |
| 265 | ||
| 266 | pub const empty = TextBlock{ | |
| 267 | .local_sym_index = 0, | |
| 268 | .size = 0, | |
| 269 | .prev = null, | |
| 270 | .next = null, | |
| 271 | .dbg_info_prev = null, | |
| 272 | .dbg_info_next = null, | |
| 273 | .dbg_info_off = undefined, | |
| 274 | .dbg_info_len = undefined, | |
| 275 | }; | |
| 276 | ||
| 277 | /// Returns how much room there is to grow in virtual address space. | |
| 278 | /// File offset relocation happens transparently, so it is not included in | |
| 279 | /// this calculation. | |
| 280 | fn capacity(self: TextBlock, macho_file: MachO) u64 { | |
| 281 | const self_sym = macho_file.locals.items[self.local_sym_index]; | |
| 282 | if (self.next) |next| { | |
| 283 | const next_sym = macho_file.locals.items[next.local_sym_index]; | |
| 284 | return next_sym.n_value - self_sym.n_value; | |
| 285 | } else { | |
| 286 | // We are the last block. | |
| 287 | // The capacity is limited only by virtual address space. | |
| 288 | return std.math.maxInt(u64) - self_sym.n_value; | |
| 289 | } | |
| 290 | } | |
| 291 | ||
| 292 | fn freeListEligible(self: TextBlock, macho_file: MachO) bool { | |
| 293 | // No need to keep a free list node for the last block. | |
| 294 | const next = self.next orelse return false; | |
| 295 | const self_sym = macho_file.locals.items[self.local_sym_index]; | |
| 296 | const next_sym = macho_file.locals.items[next.local_sym_index]; | |
| 297 | const cap = next_sym.n_value - self_sym.n_value; | |
| 298 | const ideal_cap = padToIdeal(self.size); | |
| 299 | if (cap <= ideal_cap) return false; | |
| 300 | const surplus = cap - ideal_cap; | |
| 301 | return surplus >= min_text_capacity; | |
| 302 | } | |
| 303 | }; | |
| 267 | pub const min_text_capacity = padToIdeal(minimum_text_block_size); | |
| 304 | 268 | |
| 305 | 269 | pub const Export = struct { |
| 306 | 270 | sym_index: ?u32 = null, |
| ... | ... | @@ -452,9 +416,9 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 452 | 416 | self.load_commands_dirty = true; |
| 453 | 417 | } |
| 454 | 418 | try self.writeRebaseInfoTable(); |
| 455 | try self.writeBindingInfoTable(); | |
| 456 | try self.writeLazyBindingInfoTable(); | |
| 457 | try self.writeExportTrie(); | |
| 419 | try self.writeBindInfoTable(); | |
| 420 | try self.writeLazyBindInfoTable(); | |
| 421 | try self.writeExportInfo(); | |
| 458 | 422 | try self.writeAllGlobalAndUndefSymbols(); |
| 459 | 423 | try self.writeIndirectSymbolTable(); |
| 460 | 424 | try self.writeStringTable(); |
| ... | ... | @@ -718,14 +682,6 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { |
| 718 | 682 | try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); |
| 719 | 683 | } |
| 720 | 684 | } else { |
| 721 | var zld = try Zld.init(self.base.allocator); | |
| 722 | defer { | |
| 723 | zld.closeFiles(); | |
| 724 | zld.deinit(); | |
| 725 | } | |
| 726 | zld.target = target; | |
| 727 | zld.stack_size = stack_size; | |
| 728 | ||
| 729 | 685 | // Positional arguments to the linker such as object files and static archives. |
| 730 | 686 | var positionals = std.ArrayList([]const u8).init(arena); |
| 731 | 687 | |
| ... | ... | @@ -796,164 +752,2252 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void { |
| 796 | 752 | } |
| 797 | 753 | } |
| 798 | 754 | |
| 799 | // If we're compiling native and we can find libSystem.B.{dylib, tbd}, | |
| 800 | // we link against that instead of embedded libSystem.B.tbd file. | |
| 801 | var native_libsystem_available = false; | |
| 802 | if (self.base.options.is_native_os) blk: { | |
| 803 | // Try stub file first. If we hit it, then we're done as the stub file | |
| 804 | // re-exports every single symbol definition. | |
| 805 | if (try resolveLib(arena, lib_dirs.items, "System", ".tbd")) |full_path| { | |
| 806 | try libs.append(full_path); | |
| 807 | native_libsystem_available = true; | |
| 808 | break :blk; | |
| 809 | } | |
| 810 | // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib | |
| 811 | // doesn't export libc.dylib which we'll need to resolve subsequently also. | |
| 812 | if (try resolveLib(arena, lib_dirs.items, "System", ".dylib")) |libsystem_path| { | |
| 813 | if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| { | |
| 814 | try libs.append(libsystem_path); | |
| 815 | try libs.append(libc_path); | |
| 816 | native_libsystem_available = true; | |
| 817 | break :blk; | |
| 818 | } | |
| 819 | } | |
| 820 | } | |
| 821 | if (!native_libsystem_available) { | |
| 822 | const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ | |
| 823 | "libc", "darwin", "libSystem.B.tbd", | |
| 824 | }); | |
| 825 | try libs.append(full_path); | |
| 826 | } | |
| 755 | // If we're compiling native and we can find libSystem.B.{dylib, tbd}, | |
| 756 | // we link against that instead of embedded libSystem.B.tbd file. | |
| 757 | var native_libsystem_available = false; | |
| 758 | if (self.base.options.is_native_os) blk: { | |
| 759 | // Try stub file first. If we hit it, then we're done as the stub file | |
| 760 | // re-exports every single symbol definition. | |
| 761 | if (try resolveLib(arena, lib_dirs.items, "System", ".tbd")) |full_path| { | |
| 762 | try libs.append(full_path); | |
| 763 | native_libsystem_available = true; | |
| 764 | break :blk; | |
| 765 | } | |
| 766 | // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib | |
| 767 | // doesn't export libc.dylib which we'll need to resolve subsequently also. | |
| 768 | if (try resolveLib(arena, lib_dirs.items, "System", ".dylib")) |libsystem_path| { | |
| 769 | if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| { | |
| 770 | try libs.append(libsystem_path); | |
| 771 | try libs.append(libc_path); | |
| 772 | native_libsystem_available = true; | |
| 773 | break :blk; | |
| 774 | } | |
| 775 | } | |
| 776 | } | |
| 777 | if (!native_libsystem_available) { | |
| 778 | const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ | |
| 779 | "libc", "darwin", "libSystem.B.tbd", | |
| 780 | }); | |
| 781 | try libs.append(full_path); | |
| 782 | } | |
| 783 | ||
| 784 | // frameworks | |
| 785 | var framework_dirs = std.ArrayList([]const u8).init(arena); | |
| 786 | for (self.base.options.framework_dirs) |dir| { | |
| 787 | if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| { | |
| 788 | try framework_dirs.append(search_dir); | |
| 789 | } else { | |
| 790 | log.warn("directory not found for '-F{s}'", .{dir}); | |
| 791 | } | |
| 792 | } | |
| 793 | ||
| 794 | var framework_not_found = false; | |
| 795 | for (self.base.options.frameworks) |framework| { | |
| 796 | for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { | |
| 797 | if (try resolveFramework(arena, framework_dirs.items, framework, ext)) |full_path| { | |
| 798 | try libs.append(full_path); | |
| 799 | break; | |
| 800 | } | |
| 801 | } else { | |
| 802 | log.warn("framework not found for '-f{s}'", .{framework}); | |
| 803 | framework_not_found = true; | |
| 804 | } | |
| 805 | } | |
| 806 | ||
| 807 | if (framework_not_found) { | |
| 808 | log.warn("Framework search paths:", .{}); | |
| 809 | for (framework_dirs.items) |dir| { | |
| 810 | log.warn(" {s}", .{dir}); | |
| 811 | } | |
| 812 | } | |
| 813 | ||
| 814 | // rpaths | |
| 815 | var rpath_table = std.StringArrayHashMap(void).init(arena); | |
| 816 | for (self.base.options.rpath_list) |rpath| { | |
| 817 | if (rpath_table.contains(rpath)) continue; | |
| 818 | try rpath_table.putNoClobber(rpath, {}); | |
| 819 | } | |
| 820 | ||
| 821 | var rpaths = std.ArrayList([]const u8).init(arena); | |
| 822 | try rpaths.ensureCapacity(rpath_table.count()); | |
| 823 | for (rpath_table.keys()) |*key| { | |
| 824 | rpaths.appendAssumeCapacity(key.*); | |
| 825 | } | |
| 826 | ||
| 827 | if (self.base.options.verbose_link) { | |
| 828 | var argv = std.ArrayList([]const u8).init(arena); | |
| 829 | ||
| 830 | try argv.append("zig"); | |
| 831 | try argv.append("ld"); | |
| 832 | ||
| 833 | if (is_exe_or_dyn_lib) { | |
| 834 | try argv.append("-dynamic"); | |
| 835 | } | |
| 836 | ||
| 837 | if (is_dyn_lib) { | |
| 838 | try argv.append("-dylib"); | |
| 839 | ||
| 840 | const install_name = try std.fmt.allocPrint(arena, "@rpath/{s}", .{ | |
| 841 | self.base.options.emit.?.sub_path, | |
| 842 | }); | |
| 843 | try argv.append("-install_name"); | |
| 844 | try argv.append(install_name); | |
| 845 | } | |
| 846 | ||
| 847 | if (self.base.options.sysroot) |syslibroot| { | |
| 848 | try argv.append("-syslibroot"); | |
| 849 | try argv.append(syslibroot); | |
| 850 | } | |
| 851 | ||
| 852 | for (rpaths.items) |rpath| { | |
| 853 | try argv.append("-rpath"); | |
| 854 | try argv.append(rpath); | |
| 855 | } | |
| 856 | ||
| 857 | try argv.appendSlice(positionals.items); | |
| 858 | ||
| 859 | try argv.append("-o"); | |
| 860 | try argv.append(full_out_path); | |
| 861 | ||
| 862 | if (native_libsystem_available) { | |
| 863 | try argv.append("-lSystem"); | |
| 864 | try argv.append("-lc"); | |
| 865 | } | |
| 866 | ||
| 867 | for (search_lib_names.items) |l_name| { | |
| 868 | try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name})); | |
| 869 | } | |
| 870 | ||
| 871 | for (self.base.options.lib_dirs) |lib_dir| { | |
| 872 | try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir})); | |
| 873 | } | |
| 874 | ||
| 875 | Compilation.dump_argv(argv.items); | |
| 876 | } | |
| 877 | ||
| 878 | self.base.file = try fs.cwd().createFile(full_out_path, .{ | |
| 879 | .truncate = true, | |
| 880 | .read = true, | |
| 881 | .mode = if (std.Target.current.os.tag == .windows) 0 else 0o777, | |
| 882 | }); | |
| 883 | self.page_size = switch (self.base.options.target.cpu.arch) { | |
| 884 | .aarch64 => 0x4000, | |
| 885 | .x86_64 => 0x1000, | |
| 886 | else => unreachable, | |
| 887 | }; | |
| 888 | ||
| 889 | try self.populateMetadata(); | |
| 890 | try self.parseInputFiles(positionals.items, self.base.options.sysroot); | |
| 891 | try self.parseLibs(libs.items, self.base.options.sysroot); | |
| 892 | try self.resolveSymbols(); | |
| 893 | try self.parseTextBlocks(); | |
| 894 | ||
| 895 | { | |
| 896 | // Add dyld_stub_binder as the final GOT entry. | |
| 897 | const resolv = self.symbol_resolver.get("dyld_stub_binder") orelse unreachable; | |
| 898 | const got_index = @intCast(u32, self.got_entries.items.len); | |
| 899 | const got_entry = GotIndirectionKey{ | |
| 900 | .where = .import, | |
| 901 | .where_index = resolv.where_index, | |
| 902 | }; | |
| 903 | try self.got_entries.append(self.base.allocator, got_entry); | |
| 904 | try self.got_entries_map.putNoClobber(self.base.allocator, got_entry, got_index); | |
| 905 | } | |
| 906 | ||
| 907 | try self.sortSections(); | |
| 908 | try self.addRpaths(rpaths.items); | |
| 909 | try self.addDataInCodeLC(); | |
| 910 | try self.addCodeSignatureLC(); | |
| 911 | try self.allocateTextSegment(); | |
| 912 | try self.allocateDataConstSegment(); | |
| 913 | try self.allocateDataSegment(); | |
| 914 | self.allocateLinkeditSegment(); | |
| 915 | try self.allocateTextBlocks(); | |
| 916 | ||
| 917 | // log.warn("locals", .{}); | |
| 918 | // for (self.locals.items) |sym, id| { | |
| 919 | // log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym }); | |
| 920 | // } | |
| 921 | ||
| 922 | // log.warn("globals", .{}); | |
| 923 | // for (self.globals.items) |sym, id| { | |
| 924 | // log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym }); | |
| 925 | // } | |
| 926 | ||
| 927 | // log.warn("tentatives", .{}); | |
| 928 | // for (self.tentatives.items) |sym, id| { | |
| 929 | // log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym }); | |
| 930 | // } | |
| 931 | ||
| 932 | // log.warn("undefines", .{}); | |
| 933 | // for (self.undefs.items) |sym, id| { | |
| 934 | // log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym }); | |
| 935 | // } | |
| 936 | ||
| 937 | // log.warn("imports", .{}); | |
| 938 | // for (self.imports.items) |sym, id| { | |
| 939 | // log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym }); | |
| 940 | // } | |
| 941 | ||
| 942 | // log.warn("symbol resolver", .{}); | |
| 943 | // for (self.symbol_resolver.keys()) |key| { | |
| 944 | // log.warn(" {s} => {}", .{ key, self.symbol_resolver.get(key).? }); | |
| 945 | // } | |
| 946 | ||
| 947 | // log.warn("mappings", .{}); | |
| 948 | // for (self.objects.items) |object, id| { | |
| 949 | // const object_id = @intCast(u16, id); | |
| 950 | // log.warn(" in object {s}", .{object.name.?}); | |
| 951 | // for (object.symtab.items) |sym, sym_id| { | |
| 952 | // if (object.symbol_mapping.get(@intCast(u32, sym_id))) |local_id| { | |
| 953 | // log.warn(" | {d} => {d}", .{ sym_id, local_id }); | |
| 954 | // } else { | |
| 955 | // log.warn(" | {d} no local mapping for {s}", .{ sym_id, object.getString(sym.n_strx) }); | |
| 956 | // } | |
| 957 | // } | |
| 958 | // } | |
| 959 | ||
| 960 | // var it = self.blocks.iterator(); | |
| 961 | // while (it.next()) |entry| { | |
| 962 | // const seg = self.load_commands.items[entry.key_ptr.seg].Segment; | |
| 963 | // const sect = seg.sections.items[entry.key_ptr.sect]; | |
| 964 | ||
| 965 | // log.warn("\n\n{s},{s} contents:", .{ segmentName(sect), sectionName(sect) }); | |
| 966 | // log.warn(" {}", .{sect}); | |
| 967 | // entry.value_ptr.*.print(self); | |
| 968 | // } | |
| 969 | ||
| 970 | try self.flushZld(); | |
| 971 | } | |
| 972 | ||
| 973 | if (!self.base.options.disable_lld_caching) { | |
| 974 | // Update the file with the digest. If it fails we can continue; it only | |
| 975 | // means that the next invocation will have an unnecessary cache miss. | |
| 976 | Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { | |
| 977 | log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)}); | |
| 978 | }; | |
| 979 | // Again failure here only means an unnecessary cache miss. | |
| 980 | man.writeManifest() catch |err| { | |
| 981 | log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); | |
| 982 | }; | |
| 983 | // We hang on to this lock so that the output file path can be used without | |
| 984 | // other processes clobbering it. | |
| 985 | self.base.lock = man.toOwnedLock(); | |
| 986 | } | |
| 987 | } | |
| 988 | ||
| 989 | fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8) !void { | |
| 990 | const arch = self.base.options.target.cpu.arch; | |
| 991 | for (files) |file_name| { | |
| 992 | const full_path = full_path: { | |
| 993 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; | |
| 994 | const path = try std.fs.realpath(file_name, &buffer); | |
| 995 | break :full_path try self.base.allocator.dupe(u8, path); | |
| 996 | }; | |
| 997 | ||
| 998 | if (try Object.createAndParseFromPath(self.base.allocator, arch, full_path)) |object| { | |
| 999 | try self.objects.append(self.base.allocator, object); | |
| 1000 | continue; | |
| 1001 | } | |
| 1002 | ||
| 1003 | if (try Archive.createAndParseFromPath(self.base.allocator, arch, full_path)) |archive| { | |
| 1004 | try self.archives.append(self.base.allocator, archive); | |
| 1005 | continue; | |
| 1006 | } | |
| 1007 | ||
| 1008 | if (try Dylib.createAndParseFromPath(self.base.allocator, arch, full_path, .{ | |
| 1009 | .syslibroot = syslibroot, | |
| 1010 | })) |dylibs| { | |
| 1011 | defer self.base.allocator.free(dylibs); | |
| 1012 | try self.dylibs.appendSlice(self.base.allocator, dylibs); | |
| 1013 | continue; | |
| 1014 | } | |
| 1015 | ||
| 1016 | log.warn("unknown filetype for positional input file: '{s}'", .{file_name}); | |
| 1017 | } | |
| 1018 | } | |
| 1019 | ||
| 1020 | fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !void { | |
| 1021 | const arch = self.base.options.target.cpu.arch; | |
| 1022 | for (libs) |lib| { | |
| 1023 | if (try Dylib.createAndParseFromPath(self.base.allocator, arch, lib, .{ | |
| 1024 | .syslibroot = syslibroot, | |
| 1025 | })) |dylibs| { | |
| 1026 | defer self.base.allocator.free(dylibs); | |
| 1027 | try self.dylibs.appendSlice(self.base.allocator, dylibs); | |
| 1028 | continue; | |
| 1029 | } | |
| 1030 | ||
| 1031 | if (try Archive.createAndParseFromPath(self.base.allocator, arch, lib)) |archive| { | |
| 1032 | try self.archives.append(self.base.allocator, archive); | |
| 1033 | continue; | |
| 1034 | } | |
| 1035 | ||
| 1036 | log.warn("unknown filetype for a library: '{s}'", .{lib}); | |
| 1037 | } | |
| 1038 | } | |
| 1039 | ||
| 1040 | pub const MatchingSection = struct { | |
| 1041 | seg: u16, | |
| 1042 | sect: u16, | |
| 1043 | }; | |
| 1044 | ||
| 1045 | pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSection { | |
| 1046 | const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1047 | const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 1048 | const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1049 | const segname = commands.segmentName(sect); | |
| 1050 | const sectname = commands.sectionName(sect); | |
| 1051 | ||
| 1052 | const res: ?MatchingSection = blk: { | |
| 1053 | switch (commands.sectionType(sect)) { | |
| 1054 | macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => { | |
| 1055 | if (self.text_const_section_index == null) { | |
| 1056 | self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1057 | try text_seg.addSection(self.base.allocator, "__const", .{}); | |
| 1058 | } | |
| 1059 | ||
| 1060 | break :blk .{ | |
| 1061 | .seg = self.text_segment_cmd_index.?, | |
| 1062 | .sect = self.text_const_section_index.?, | |
| 1063 | }; | |
| 1064 | }, | |
| 1065 | macho.S_CSTRING_LITERALS => { | |
| 1066 | if (mem.eql(u8, sectname, "__objc_methname")) { | |
| 1067 | // TODO it seems the common values within the sections in objects are deduplicated/merged | |
| 1068 | // on merging the sections' contents. | |
| 1069 | if (self.objc_methname_section_index == null) { | |
| 1070 | self.objc_methname_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1071 | try text_seg.addSection(self.base.allocator, "__objc_methname", .{ | |
| 1072 | .flags = macho.S_CSTRING_LITERALS, | |
| 1073 | }); | |
| 1074 | } | |
| 1075 | ||
| 1076 | break :blk .{ | |
| 1077 | .seg = self.text_segment_cmd_index.?, | |
| 1078 | .sect = self.objc_methname_section_index.?, | |
| 1079 | }; | |
| 1080 | } else if (mem.eql(u8, sectname, "__objc_methtype")) { | |
| 1081 | if (self.objc_methtype_section_index == null) { | |
| 1082 | self.objc_methtype_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1083 | try text_seg.addSection(self.base.allocator, "__objc_methtype", .{ | |
| 1084 | .flags = macho.S_CSTRING_LITERALS, | |
| 1085 | }); | |
| 1086 | } | |
| 1087 | ||
| 1088 | break :blk .{ | |
| 1089 | .seg = self.text_segment_cmd_index.?, | |
| 1090 | .sect = self.objc_methtype_section_index.?, | |
| 1091 | }; | |
| 1092 | } else if (mem.eql(u8, sectname, "__objc_classname")) { | |
| 1093 | if (self.objc_classname_section_index == null) { | |
| 1094 | self.objc_classname_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1095 | try text_seg.addSection(self.base.allocator, "__objc_classname", .{}); | |
| 1096 | } | |
| 1097 | ||
| 1098 | break :blk .{ | |
| 1099 | .seg = self.text_segment_cmd_index.?, | |
| 1100 | .sect = self.objc_classname_section_index.?, | |
| 1101 | }; | |
| 1102 | } | |
| 1103 | ||
| 1104 | if (self.cstring_section_index == null) { | |
| 1105 | self.cstring_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1106 | try text_seg.addSection(self.base.allocator, "__cstring", .{ | |
| 1107 | .flags = macho.S_CSTRING_LITERALS, | |
| 1108 | }); | |
| 1109 | } | |
| 1110 | ||
| 1111 | break :blk .{ | |
| 1112 | .seg = self.text_segment_cmd_index.?, | |
| 1113 | .sect = self.cstring_section_index.?, | |
| 1114 | }; | |
| 1115 | }, | |
| 1116 | macho.S_LITERAL_POINTERS => { | |
| 1117 | if (mem.eql(u8, segname, "__DATA") and mem.eql(u8, sectname, "__objc_selrefs")) { | |
| 1118 | if (self.objc_selrefs_section_index == null) { | |
| 1119 | self.objc_selrefs_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1120 | try data_seg.addSection(self.base.allocator, "__objc_selrefs", .{ | |
| 1121 | .flags = macho.S_LITERAL_POINTERS, | |
| 1122 | }); | |
| 1123 | } | |
| 1124 | ||
| 1125 | break :blk .{ | |
| 1126 | .seg = self.data_segment_cmd_index.?, | |
| 1127 | .sect = self.objc_selrefs_section_index.?, | |
| 1128 | }; | |
| 1129 | } | |
| 1130 | ||
| 1131 | // TODO investigate | |
| 1132 | break :blk null; | |
| 1133 | }, | |
| 1134 | macho.S_MOD_INIT_FUNC_POINTERS => { | |
| 1135 | if (self.mod_init_func_section_index == null) { | |
| 1136 | self.mod_init_func_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 1137 | try data_const_seg.addSection(self.base.allocator, "__mod_init_func", .{ | |
| 1138 | .flags = macho.S_MOD_INIT_FUNC_POINTERS, | |
| 1139 | }); | |
| 1140 | } | |
| 1141 | ||
| 1142 | break :blk .{ | |
| 1143 | .seg = self.data_const_segment_cmd_index.?, | |
| 1144 | .sect = self.mod_init_func_section_index.?, | |
| 1145 | }; | |
| 1146 | }, | |
| 1147 | macho.S_MOD_TERM_FUNC_POINTERS => { | |
| 1148 | if (self.mod_term_func_section_index == null) { | |
| 1149 | self.mod_term_func_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 1150 | try data_const_seg.addSection(self.base.allocator, "__mod_term_func", .{ | |
| 1151 | .flags = macho.S_MOD_TERM_FUNC_POINTERS, | |
| 1152 | }); | |
| 1153 | } | |
| 1154 | ||
| 1155 | break :blk .{ | |
| 1156 | .seg = self.data_const_segment_cmd_index.?, | |
| 1157 | .sect = self.mod_term_func_section_index.?, | |
| 1158 | }; | |
| 1159 | }, | |
| 1160 | macho.S_ZEROFILL => { | |
| 1161 | if (mem.eql(u8, sectname, "__common")) { | |
| 1162 | if (self.common_section_index == null) { | |
| 1163 | self.common_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1164 | try data_seg.addSection(self.base.allocator, "__common", .{ | |
| 1165 | .flags = macho.S_ZEROFILL, | |
| 1166 | }); | |
| 1167 | } | |
| 1168 | ||
| 1169 | break :blk .{ | |
| 1170 | .seg = self.data_segment_cmd_index.?, | |
| 1171 | .sect = self.common_section_index.?, | |
| 1172 | }; | |
| 1173 | } else { | |
| 1174 | if (self.bss_section_index == null) { | |
| 1175 | self.bss_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1176 | try data_seg.addSection(self.base.allocator, "__bss", .{ | |
| 1177 | .flags = macho.S_ZEROFILL, | |
| 1178 | }); | |
| 1179 | } | |
| 1180 | ||
| 1181 | break :blk .{ | |
| 1182 | .seg = self.data_segment_cmd_index.?, | |
| 1183 | .sect = self.bss_section_index.?, | |
| 1184 | }; | |
| 1185 | } | |
| 1186 | }, | |
| 1187 | macho.S_THREAD_LOCAL_VARIABLES => { | |
| 1188 | if (self.tlv_section_index == null) { | |
| 1189 | self.tlv_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1190 | try data_seg.addSection(self.base.allocator, "__thread_vars", .{ | |
| 1191 | .flags = macho.S_THREAD_LOCAL_VARIABLES, | |
| 1192 | }); | |
| 1193 | } | |
| 1194 | ||
| 1195 | break :blk .{ | |
| 1196 | .seg = self.data_segment_cmd_index.?, | |
| 1197 | .sect = self.tlv_section_index.?, | |
| 1198 | }; | |
| 1199 | }, | |
| 1200 | macho.S_THREAD_LOCAL_REGULAR => { | |
| 1201 | if (self.tlv_data_section_index == null) { | |
| 1202 | self.tlv_data_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1203 | try data_seg.addSection(self.base.allocator, "__thread_data", .{ | |
| 1204 | .flags = macho.S_THREAD_LOCAL_REGULAR, | |
| 1205 | }); | |
| 1206 | } | |
| 1207 | ||
| 1208 | break :blk .{ | |
| 1209 | .seg = self.data_segment_cmd_index.?, | |
| 1210 | .sect = self.tlv_data_section_index.?, | |
| 1211 | }; | |
| 1212 | }, | |
| 1213 | macho.S_THREAD_LOCAL_ZEROFILL => { | |
| 1214 | if (self.tlv_bss_section_index == null) { | |
| 1215 | self.tlv_bss_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1216 | try data_seg.addSection(self.base.allocator, "__thread_bss", .{ | |
| 1217 | .flags = macho.S_THREAD_LOCAL_ZEROFILL, | |
| 1218 | }); | |
| 1219 | } | |
| 1220 | ||
| 1221 | break :blk .{ | |
| 1222 | .seg = self.data_segment_cmd_index.?, | |
| 1223 | .sect = self.tlv_bss_section_index.?, | |
| 1224 | }; | |
| 1225 | }, | |
| 1226 | macho.S_COALESCED => { | |
| 1227 | if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) { | |
| 1228 | // TODO I believe __eh_frame is currently part of __unwind_info section | |
| 1229 | // in the latest ld64 output. | |
| 1230 | if (self.eh_frame_section_index == null) { | |
| 1231 | self.eh_frame_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1232 | try text_seg.addSection(self.base.allocator, "__eh_frame", .{}); | |
| 1233 | } | |
| 1234 | ||
| 1235 | break :blk .{ | |
| 1236 | .seg = self.text_segment_cmd_index.?, | |
| 1237 | .sect = self.eh_frame_section_index.?, | |
| 1238 | }; | |
| 1239 | } | |
| 1240 | ||
| 1241 | // TODO audit this: is this the right mapping? | |
| 1242 | if (self.data_const_section_index == null) { | |
| 1243 | self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 1244 | try data_const_seg.addSection(self.base.allocator, "__const", .{}); | |
| 1245 | } | |
| 1246 | ||
| 1247 | break :blk .{ | |
| 1248 | .seg = self.data_const_segment_cmd_index.?, | |
| 1249 | .sect = self.data_const_section_index.?, | |
| 1250 | }; | |
| 1251 | }, | |
| 1252 | macho.S_REGULAR => { | |
| 1253 | if (commands.sectionIsCode(sect)) { | |
| 1254 | if (self.text_section_index == null) { | |
| 1255 | self.text_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1256 | try text_seg.addSection(self.base.allocator, "__text", .{ | |
| 1257 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, | |
| 1258 | }); | |
| 1259 | } | |
| 1260 | ||
| 1261 | break :blk .{ | |
| 1262 | .seg = self.text_segment_cmd_index.?, | |
| 1263 | .sect = self.text_section_index.?, | |
| 1264 | }; | |
| 1265 | } | |
| 1266 | if (commands.sectionIsDebug(sect)) { | |
| 1267 | // TODO debug attributes | |
| 1268 | if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) { | |
| 1269 | log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{ | |
| 1270 | sect.flags, segname, sectname, | |
| 1271 | }); | |
| 1272 | } | |
| 1273 | break :blk null; | |
| 1274 | } | |
| 1275 | ||
| 1276 | if (mem.eql(u8, segname, "__TEXT")) { | |
| 1277 | if (mem.eql(u8, sectname, "__ustring")) { | |
| 1278 | if (self.ustring_section_index == null) { | |
| 1279 | self.ustring_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1280 | try text_seg.addSection(self.base.allocator, "__ustring", .{}); | |
| 1281 | } | |
| 1282 | ||
| 1283 | break :blk .{ | |
| 1284 | .seg = self.text_segment_cmd_index.?, | |
| 1285 | .sect = self.ustring_section_index.?, | |
| 1286 | }; | |
| 1287 | } else if (mem.eql(u8, sectname, "__gcc_except_tab")) { | |
| 1288 | if (self.gcc_except_tab_section_index == null) { | |
| 1289 | self.gcc_except_tab_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1290 | try text_seg.addSection(self.base.allocator, "__gcc_except_tab", .{}); | |
| 1291 | } | |
| 1292 | ||
| 1293 | break :blk .{ | |
| 1294 | .seg = self.text_segment_cmd_index.?, | |
| 1295 | .sect = self.gcc_except_tab_section_index.?, | |
| 1296 | }; | |
| 1297 | } else if (mem.eql(u8, sectname, "__objc_methlist")) { | |
| 1298 | if (self.objc_methlist_section_index == null) { | |
| 1299 | self.objc_methlist_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1300 | try text_seg.addSection(self.base.allocator, "__objc_methlist", .{}); | |
| 1301 | } | |
| 1302 | ||
| 1303 | break :blk .{ | |
| 1304 | .seg = self.text_segment_cmd_index.?, | |
| 1305 | .sect = self.objc_methlist_section_index.?, | |
| 1306 | }; | |
| 1307 | } else if (mem.eql(u8, sectname, "__rodata") or | |
| 1308 | mem.eql(u8, sectname, "__typelink") or | |
| 1309 | mem.eql(u8, sectname, "__itablink") or | |
| 1310 | mem.eql(u8, sectname, "__gosymtab") or | |
| 1311 | mem.eql(u8, sectname, "__gopclntab")) | |
| 1312 | { | |
| 1313 | if (self.data_const_section_index == null) { | |
| 1314 | self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 1315 | try data_const_seg.addSection(self.base.allocator, "__const", .{}); | |
| 1316 | } | |
| 1317 | ||
| 1318 | break :blk .{ | |
| 1319 | .seg = self.data_const_segment_cmd_index.?, | |
| 1320 | .sect = self.data_const_section_index.?, | |
| 1321 | }; | |
| 1322 | } else { | |
| 1323 | if (self.text_const_section_index == null) { | |
| 1324 | self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1325 | try text_seg.addSection(self.base.allocator, "__const", .{}); | |
| 1326 | } | |
| 1327 | ||
| 1328 | break :blk .{ | |
| 1329 | .seg = self.text_segment_cmd_index.?, | |
| 1330 | .sect = self.text_const_section_index.?, | |
| 1331 | }; | |
| 1332 | } | |
| 1333 | } | |
| 1334 | ||
| 1335 | if (mem.eql(u8, segname, "__DATA_CONST")) { | |
| 1336 | if (self.data_const_section_index == null) { | |
| 1337 | self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 1338 | try data_const_seg.addSection(self.base.allocator, "__const", .{}); | |
| 1339 | } | |
| 1340 | ||
| 1341 | break :blk .{ | |
| 1342 | .seg = self.data_const_segment_cmd_index.?, | |
| 1343 | .sect = self.data_const_section_index.?, | |
| 1344 | }; | |
| 1345 | } | |
| 1346 | ||
| 1347 | if (mem.eql(u8, segname, "__DATA")) { | |
| 1348 | if (mem.eql(u8, sectname, "__const")) { | |
| 1349 | if (self.data_const_section_index == null) { | |
| 1350 | self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 1351 | try data_const_seg.addSection(self.base.allocator, "__const", .{}); | |
| 1352 | } | |
| 1353 | ||
| 1354 | break :blk .{ | |
| 1355 | .seg = self.data_const_segment_cmd_index.?, | |
| 1356 | .sect = self.data_const_section_index.?, | |
| 1357 | }; | |
| 1358 | } else if (mem.eql(u8, sectname, "__cfstring")) { | |
| 1359 | if (self.objc_cfstring_section_index == null) { | |
| 1360 | self.objc_cfstring_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 1361 | try data_const_seg.addSection(self.base.allocator, "__cfstring", .{}); | |
| 1362 | } | |
| 1363 | ||
| 1364 | break :blk .{ | |
| 1365 | .seg = self.data_const_segment_cmd_index.?, | |
| 1366 | .sect = self.objc_cfstring_section_index.?, | |
| 1367 | }; | |
| 1368 | } else if (mem.eql(u8, sectname, "__objc_classlist")) { | |
| 1369 | if (self.objc_classlist_section_index == null) { | |
| 1370 | self.objc_classlist_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 1371 | try data_const_seg.addSection(self.base.allocator, "__objc_classlist", .{}); | |
| 1372 | } | |
| 1373 | ||
| 1374 | break :blk .{ | |
| 1375 | .seg = self.data_const_segment_cmd_index.?, | |
| 1376 | .sect = self.objc_classlist_section_index.?, | |
| 1377 | }; | |
| 1378 | } else if (mem.eql(u8, sectname, "__objc_imageinfo")) { | |
| 1379 | if (self.objc_imageinfo_section_index == null) { | |
| 1380 | self.objc_imageinfo_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 1381 | try data_const_seg.addSection(self.base.allocator, "__objc_imageinfo", .{}); | |
| 1382 | } | |
| 1383 | ||
| 1384 | break :blk .{ | |
| 1385 | .seg = self.data_const_segment_cmd_index.?, | |
| 1386 | .sect = self.objc_imageinfo_section_index.?, | |
| 1387 | }; | |
| 1388 | } else if (mem.eql(u8, sectname, "__objc_const")) { | |
| 1389 | if (self.objc_const_section_index == null) { | |
| 1390 | self.objc_const_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1391 | try data_seg.addSection(self.base.allocator, "__objc_const", .{}); | |
| 1392 | } | |
| 1393 | ||
| 1394 | break :blk .{ | |
| 1395 | .seg = self.data_segment_cmd_index.?, | |
| 1396 | .sect = self.objc_const_section_index.?, | |
| 1397 | }; | |
| 1398 | } else if (mem.eql(u8, sectname, "__objc_classrefs")) { | |
| 1399 | if (self.objc_classrefs_section_index == null) { | |
| 1400 | self.objc_classrefs_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1401 | try data_seg.addSection(self.base.allocator, "__objc_classrefs", .{}); | |
| 1402 | } | |
| 1403 | ||
| 1404 | break :blk .{ | |
| 1405 | .seg = self.data_segment_cmd_index.?, | |
| 1406 | .sect = self.objc_classrefs_section_index.?, | |
| 1407 | }; | |
| 1408 | } else if (mem.eql(u8, sectname, "__objc_data")) { | |
| 1409 | if (self.objc_data_section_index == null) { | |
| 1410 | self.objc_data_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1411 | try data_seg.addSection(self.base.allocator, "__objc_data", .{}); | |
| 1412 | } | |
| 1413 | ||
| 1414 | break :blk .{ | |
| 1415 | .seg = self.data_segment_cmd_index.?, | |
| 1416 | .sect = self.objc_data_section_index.?, | |
| 1417 | }; | |
| 1418 | } else { | |
| 1419 | if (self.data_section_index == null) { | |
| 1420 | self.data_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1421 | try data_seg.addSection(self.base.allocator, "__data", .{}); | |
| 1422 | } | |
| 1423 | ||
| 1424 | break :blk .{ | |
| 1425 | .seg = self.data_segment_cmd_index.?, | |
| 1426 | .sect = self.data_section_index.?, | |
| 1427 | }; | |
| 1428 | } | |
| 1429 | } | |
| 1430 | ||
| 1431 | if (mem.eql(u8, "__LLVM", segname) and mem.eql(u8, "__asm", sectname)) { | |
| 1432 | log.debug("TODO LLVM asm section: type 0x{x}, name '{s},{s}'", .{ | |
| 1433 | sect.flags, segname, sectname, | |
| 1434 | }); | |
| 1435 | } | |
| 1436 | ||
| 1437 | break :blk null; | |
| 1438 | }, | |
| 1439 | else => break :blk null, | |
| 1440 | } | |
| 1441 | }; | |
| 1442 | ||
| 1443 | return res; | |
| 1444 | } | |
| 1445 | ||
| 1446 | fn sortSections(self: *MachO) !void { | |
| 1447 | var text_index_mapping = std.AutoHashMap(u16, u16).init(self.base.allocator); | |
| 1448 | defer text_index_mapping.deinit(); | |
| 1449 | var data_const_index_mapping = std.AutoHashMap(u16, u16).init(self.base.allocator); | |
| 1450 | defer data_const_index_mapping.deinit(); | |
| 1451 | var data_index_mapping = std.AutoHashMap(u16, u16).init(self.base.allocator); | |
| 1452 | defer data_index_mapping.deinit(); | |
| 1453 | ||
| 1454 | { | |
| 1455 | // __TEXT segment | |
| 1456 | const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1457 | var sections = seg.sections.toOwnedSlice(self.base.allocator); | |
| 1458 | defer self.base.allocator.free(sections); | |
| 1459 | try seg.sections.ensureCapacity(self.base.allocator, sections.len); | |
| 1460 | ||
| 1461 | const indices = &[_]*?u16{ | |
| 1462 | &self.text_section_index, | |
| 1463 | &self.stubs_section_index, | |
| 1464 | &self.stub_helper_section_index, | |
| 1465 | &self.gcc_except_tab_section_index, | |
| 1466 | &self.cstring_section_index, | |
| 1467 | &self.ustring_section_index, | |
| 1468 | &self.text_const_section_index, | |
| 1469 | &self.objc_methname_section_index, | |
| 1470 | &self.objc_methtype_section_index, | |
| 1471 | &self.objc_classname_section_index, | |
| 1472 | &self.eh_frame_section_index, | |
| 1473 | }; | |
| 1474 | for (indices) |maybe_index| { | |
| 1475 | const new_index: u16 = if (maybe_index.*) |index| blk: { | |
| 1476 | const idx = @intCast(u16, seg.sections.items.len); | |
| 1477 | seg.sections.appendAssumeCapacity(sections[index]); | |
| 1478 | try text_index_mapping.putNoClobber(index, idx); | |
| 1479 | break :blk idx; | |
| 1480 | } else continue; | |
| 1481 | maybe_index.* = new_index; | |
| 1482 | } | |
| 1483 | } | |
| 1484 | ||
| 1485 | { | |
| 1486 | // __DATA_CONST segment | |
| 1487 | const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 1488 | var sections = seg.sections.toOwnedSlice(self.base.allocator); | |
| 1489 | defer self.base.allocator.free(sections); | |
| 1490 | try seg.sections.ensureCapacity(self.base.allocator, sections.len); | |
| 1491 | ||
| 1492 | const indices = &[_]*?u16{ | |
| 1493 | &self.got_section_index, | |
| 1494 | &self.mod_init_func_section_index, | |
| 1495 | &self.mod_term_func_section_index, | |
| 1496 | &self.data_const_section_index, | |
| 1497 | &self.objc_cfstring_section_index, | |
| 1498 | &self.objc_classlist_section_index, | |
| 1499 | &self.objc_imageinfo_section_index, | |
| 1500 | }; | |
| 1501 | for (indices) |maybe_index| { | |
| 1502 | const new_index: u16 = if (maybe_index.*) |index| blk: { | |
| 1503 | const idx = @intCast(u16, seg.sections.items.len); | |
| 1504 | seg.sections.appendAssumeCapacity(sections[index]); | |
| 1505 | try data_const_index_mapping.putNoClobber(index, idx); | |
| 1506 | break :blk idx; | |
| 1507 | } else continue; | |
| 1508 | maybe_index.* = new_index; | |
| 1509 | } | |
| 1510 | } | |
| 1511 | ||
| 1512 | { | |
| 1513 | // __DATA segment | |
| 1514 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1515 | var sections = seg.sections.toOwnedSlice(self.base.allocator); | |
| 1516 | defer self.base.allocator.free(sections); | |
| 1517 | try seg.sections.ensureCapacity(self.base.allocator, sections.len); | |
| 1518 | ||
| 1519 | // __DATA segment | |
| 1520 | const indices = &[_]*?u16{ | |
| 1521 | &self.la_symbol_ptr_section_index, | |
| 1522 | &self.objc_const_section_index, | |
| 1523 | &self.objc_selrefs_section_index, | |
| 1524 | &self.objc_classrefs_section_index, | |
| 1525 | &self.objc_data_section_index, | |
| 1526 | &self.data_section_index, | |
| 1527 | &self.tlv_section_index, | |
| 1528 | &self.tlv_data_section_index, | |
| 1529 | &self.tlv_bss_section_index, | |
| 1530 | &self.bss_section_index, | |
| 1531 | &self.common_section_index, | |
| 1532 | }; | |
| 1533 | for (indices) |maybe_index| { | |
| 1534 | const new_index: u16 = if (maybe_index.*) |index| blk: { | |
| 1535 | const idx = @intCast(u16, seg.sections.items.len); | |
| 1536 | seg.sections.appendAssumeCapacity(sections[index]); | |
| 1537 | try data_index_mapping.putNoClobber(index, idx); | |
| 1538 | break :blk idx; | |
| 1539 | } else continue; | |
| 1540 | maybe_index.* = new_index; | |
| 1541 | } | |
| 1542 | } | |
| 1543 | ||
| 1544 | { | |
| 1545 | var transient: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{}; | |
| 1546 | try transient.ensureCapacity(self.base.allocator, self.blocks.count()); | |
| 1547 | ||
| 1548 | var it = self.blocks.iterator(); | |
| 1549 | while (it.next()) |entry| { | |
| 1550 | const old = entry.key_ptr.*; | |
| 1551 | const sect = if (old.seg == self.text_segment_cmd_index.?) | |
| 1552 | text_index_mapping.get(old.sect).? | |
| 1553 | else if (old.seg == self.data_const_segment_cmd_index.?) | |
| 1554 | data_const_index_mapping.get(old.sect).? | |
| 1555 | else | |
| 1556 | data_index_mapping.get(old.sect).?; | |
| 1557 | transient.putAssumeCapacityNoClobber(.{ | |
| 1558 | .seg = old.seg, | |
| 1559 | .sect = sect, | |
| 1560 | }, entry.value_ptr.*); | |
| 1561 | } | |
| 1562 | ||
| 1563 | self.blocks.clearAndFree(self.base.allocator); | |
| 1564 | self.blocks.deinit(self.base.allocator); | |
| 1565 | self.blocks = transient; | |
| 1566 | } | |
| 1567 | } | |
| 1568 | ||
| 1569 | fn allocateTextSegment(self: *MachO) !void { | |
| 1570 | const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1571 | const nstubs = @intCast(u32, self.stubs.items.len); | |
| 1572 | ||
| 1573 | const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize; | |
| 1574 | seg.inner.fileoff = 0; | |
| 1575 | seg.inner.vmaddr = base_vmaddr; | |
| 1576 | ||
| 1577 | // Set stubs and stub_helper sizes | |
| 1578 | const stubs = &seg.sections.items[self.stubs_section_index.?]; | |
| 1579 | const stub_helper = &seg.sections.items[self.stub_helper_section_index.?]; | |
| 1580 | stubs.size += nstubs * stubs.reserved2; | |
| 1581 | ||
| 1582 | const stub_size: u4 = switch (self.base.options.target.cpu.arch) { | |
| 1583 | .x86_64 => 10, | |
| 1584 | .aarch64 => 3 * @sizeOf(u32), | |
| 1585 | else => unreachable, | |
| 1586 | }; | |
| 1587 | stub_helper.size += nstubs * stub_size; | |
| 1588 | ||
| 1589 | var sizeofcmds: u64 = 0; | |
| 1590 | for (self.load_commands.items) |lc| { | |
| 1591 | sizeofcmds += lc.cmdsize(); | |
| 1592 | } | |
| 1593 | ||
| 1594 | try self.allocateSegment(self.text_segment_cmd_index.?, @sizeOf(macho.mach_header_64) + sizeofcmds); | |
| 1595 | ||
| 1596 | // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments. | |
| 1597 | var min_alignment: u32 = 0; | |
| 1598 | for (seg.sections.items) |sect| { | |
| 1599 | const alignment = try math.powi(u32, 2, sect.@"align"); | |
| 1600 | min_alignment = math.max(min_alignment, alignment); | |
| 1601 | } | |
| 1602 | ||
| 1603 | assert(min_alignment > 0); | |
| 1604 | const last_sect_idx = seg.sections.items.len - 1; | |
| 1605 | const last_sect = seg.sections.items[last_sect_idx]; | |
| 1606 | const shift: u32 = blk: { | |
| 1607 | const diff = seg.inner.filesize - last_sect.offset - last_sect.size; | |
| 1608 | const factor = @divTrunc(diff, min_alignment); | |
| 1609 | break :blk @intCast(u32, factor * min_alignment); | |
| 1610 | }; | |
| 1611 | ||
| 1612 | if (shift > 0) { | |
| 1613 | for (seg.sections.items) |*sect| { | |
| 1614 | sect.offset += shift; | |
| 1615 | sect.addr += shift; | |
| 1616 | } | |
| 1617 | } | |
| 1618 | } | |
| 1619 | ||
| 1620 | fn allocateDataConstSegment(self: *MachO) !void { | |
| 1621 | const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 1622 | const nentries = @intCast(u32, self.got_entries.items.len); | |
| 1623 | ||
| 1624 | const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1625 | seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize; | |
| 1626 | seg.inner.vmaddr = text_seg.inner.vmaddr + text_seg.inner.vmsize; | |
| 1627 | ||
| 1628 | // Set got size | |
| 1629 | const got = &seg.sections.items[self.got_section_index.?]; | |
| 1630 | got.size += nentries * @sizeOf(u64); | |
| 1631 | ||
| 1632 | try self.allocateSegment(self.data_const_segment_cmd_index.?, 0); | |
| 1633 | } | |
| 1634 | ||
| 1635 | fn allocateDataSegment(self: *MachO) !void { | |
| 1636 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1637 | const nstubs = @intCast(u32, self.stubs.items.len); | |
| 1638 | ||
| 1639 | const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 1640 | seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize; | |
| 1641 | seg.inner.vmaddr = data_const_seg.inner.vmaddr + data_const_seg.inner.vmsize; | |
| 1642 | ||
| 1643 | // Set la_symbol_ptr and data size | |
| 1644 | const la_symbol_ptr = &seg.sections.items[self.la_symbol_ptr_section_index.?]; | |
| 1645 | const data = &seg.sections.items[self.data_section_index.?]; | |
| 1646 | la_symbol_ptr.size += nstubs * @sizeOf(u64); | |
| 1647 | data.size += @sizeOf(u64); // We need at least 8bytes for address of dyld_stub_binder | |
| 1648 | ||
| 1649 | try self.allocateSegment(self.data_segment_cmd_index.?, 0); | |
| 1650 | } | |
| 1651 | ||
| 1652 | fn allocateLinkeditSegment(self: *MachO) void { | |
| 1653 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 1654 | const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1655 | seg.inner.fileoff = data_seg.inner.fileoff + data_seg.inner.filesize; | |
| 1656 | seg.inner.vmaddr = data_seg.inner.vmaddr + data_seg.inner.vmsize; | |
| 1657 | } | |
| 1658 | ||
| 1659 | fn allocateSegment(self: *MachO, index: u16, offset: u64) !void { | |
| 1660 | const seg = &self.load_commands.items[index].Segment; | |
| 1661 | ||
| 1662 | // Allocate the sections according to their alignment at the beginning of the segment. | |
| 1663 | var start: u64 = offset; | |
| 1664 | for (seg.sections.items) |*sect| { | |
| 1665 | const alignment = try math.powi(u32, 2, sect.@"align"); | |
| 1666 | const start_aligned = mem.alignForwardGeneric(u64, start, alignment); | |
| 1667 | const end_aligned = mem.alignForwardGeneric(u64, start_aligned + sect.size, alignment); | |
| 1668 | sect.offset = @intCast(u32, seg.inner.fileoff + start_aligned); | |
| 1669 | sect.addr = seg.inner.vmaddr + start_aligned; | |
| 1670 | start = end_aligned; | |
| 1671 | } | |
| 1672 | ||
| 1673 | const seg_size_aligned = mem.alignForwardGeneric(u64, start, self.page_size); | |
| 1674 | seg.inner.filesize = seg_size_aligned; | |
| 1675 | seg.inner.vmsize = seg_size_aligned; | |
| 1676 | } | |
| 1677 | ||
| 1678 | fn allocateTextBlocks(self: *MachO) !void { | |
| 1679 | var it = self.blocks.iterator(); | |
| 1680 | while (it.next()) |entry| { | |
| 1681 | const match = entry.key_ptr.*; | |
| 1682 | var block: *TextBlock = entry.value_ptr.*; | |
| 1683 | ||
| 1684 | // Find the first block | |
| 1685 | while (block.prev) |prev| { | |
| 1686 | block = prev; | |
| 1687 | } | |
| 1688 | ||
| 1689 | const seg = self.load_commands.items[match.seg].Segment; | |
| 1690 | const sect = seg.sections.items[match.sect]; | |
| 1691 | ||
| 1692 | var base_addr: u64 = sect.addr; | |
| 1693 | const n_sect = self.sectionId(match); | |
| 1694 | ||
| 1695 | log.debug(" within section {s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) }); | |
| 1696 | log.debug(" {}", .{sect}); | |
| 1697 | ||
| 1698 | while (true) { | |
| 1699 | const block_alignment = try math.powi(u32, 2, block.alignment); | |
| 1700 | base_addr = mem.alignForwardGeneric(u64, base_addr, block_alignment); | |
| 1701 | ||
| 1702 | const sym = &self.locals.items[block.local_sym_index]; | |
| 1703 | sym.n_value = base_addr; | |
| 1704 | sym.n_sect = n_sect; | |
| 1705 | ||
| 1706 | log.debug(" {s}: start=0x{x}, end=0x{x}, size={}, align={}", .{ | |
| 1707 | self.getString(sym.n_strx), | |
| 1708 | base_addr, | |
| 1709 | base_addr + block.size, | |
| 1710 | block.size, | |
| 1711 | block.alignment, | |
| 1712 | }); | |
| 1713 | ||
| 1714 | // Update each alias (if any) | |
| 1715 | for (block.aliases.items) |index| { | |
| 1716 | const alias_sym = &self.locals.items[index]; | |
| 1717 | alias_sym.n_value = base_addr; | |
| 1718 | alias_sym.n_sect = n_sect; | |
| 1719 | } | |
| 1720 | ||
| 1721 | // Update each symbol contained within the TextBlock | |
| 1722 | for (block.contained.items) |sym_at_off| { | |
| 1723 | const contained_sym = &self.locals.items[sym_at_off.local_sym_index]; | |
| 1724 | contained_sym.n_value = base_addr + sym_at_off.offset; | |
| 1725 | contained_sym.n_sect = n_sect; | |
| 1726 | } | |
| 1727 | ||
| 1728 | base_addr += block.size; | |
| 1729 | ||
| 1730 | if (block.next) |next| { | |
| 1731 | block = next; | |
| 1732 | } else break; | |
| 1733 | } | |
| 1734 | } | |
| 1735 | ||
| 1736 | // Update globals | |
| 1737 | for (self.symbol_resolver.values()) |resolv| { | |
| 1738 | if (resolv.where != .global) continue; | |
| 1739 | ||
| 1740 | assert(resolv.local_sym_index != 0); | |
| 1741 | const local_sym = self.locals.items[resolv.local_sym_index]; | |
| 1742 | const sym = &self.globals.items[resolv.where_index]; | |
| 1743 | sym.n_value = local_sym.n_value; | |
| 1744 | sym.n_sect = local_sym.n_sect; | |
| 1745 | } | |
| 1746 | } | |
| 1747 | ||
| 1748 | fn writeTextBlocks(self: *MachO) !void { | |
| 1749 | var it = self.blocks.iterator(); | |
| 1750 | while (it.next()) |entry| { | |
| 1751 | const match = entry.key_ptr.*; | |
| 1752 | var block: *TextBlock = entry.value_ptr.*; | |
| 1753 | ||
| 1754 | while (block.prev) |prev| { | |
| 1755 | block = prev; | |
| 1756 | } | |
| 1757 | ||
| 1758 | const seg = self.load_commands.items[match.seg].Segment; | |
| 1759 | const sect = seg.sections.items[match.sect]; | |
| 1760 | const sect_type = commands.sectionType(sect); | |
| 1761 | ||
| 1762 | log.debug(" for section {s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) }); | |
| 1763 | log.debug(" {}", .{sect}); | |
| 1764 | ||
| 1765 | var code = try self.base.allocator.alloc(u8, sect.size); | |
| 1766 | defer self.base.allocator.free(code); | |
| 1767 | ||
| 1768 | if (sect_type == macho.S_ZEROFILL or sect_type == macho.S_THREAD_LOCAL_ZEROFILL) { | |
| 1769 | mem.set(u8, code, 0); | |
| 1770 | } else { | |
| 1771 | var base_off: u64 = 0; | |
| 1772 | ||
| 1773 | while (true) { | |
| 1774 | const block_alignment = try math.powi(u32, 2, block.alignment); | |
| 1775 | const aligned_base_off = mem.alignForwardGeneric(u64, base_off, block_alignment); | |
| 1776 | ||
| 1777 | const sym = self.locals.items[block.local_sym_index]; | |
| 1778 | log.debug(" {s}: start=0x{x}, end=0x{x}, size={}, align={}", .{ | |
| 1779 | self.getString(sym.n_strx), | |
| 1780 | aligned_base_off, | |
| 1781 | aligned_base_off + block.size, | |
| 1782 | block.size, | |
| 1783 | block.alignment, | |
| 1784 | }); | |
| 1785 | ||
| 1786 | try block.resolveRelocs(self); | |
| 1787 | mem.copy(u8, code[aligned_base_off..][0..block.size], block.code); | |
| 1788 | ||
| 1789 | // TODO NOP for machine code instead of just zeroing out | |
| 1790 | const padding_len = aligned_base_off - base_off; | |
| 1791 | mem.set(u8, code[base_off..][0..padding_len], 0); | |
| 1792 | ||
| 1793 | base_off = aligned_base_off + block.size; | |
| 1794 | ||
| 1795 | if (block.next) |next| { | |
| 1796 | block = next; | |
| 1797 | } else break; | |
| 1798 | } | |
| 1799 | ||
| 1800 | mem.set(u8, code[base_off..], 0); | |
| 1801 | } | |
| 1802 | ||
| 1803 | try self.base.file.?.pwriteAll(code, sect.offset); | |
| 1804 | } | |
| 1805 | } | |
| 1806 | ||
| 1807 | fn writeStubHelperCommon(self: *MachO) !void { | |
| 1808 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1809 | const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?]; | |
| 1810 | const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 1811 | const got = &data_const_segment.sections.items[self.got_section_index.?]; | |
| 1812 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1813 | const data = &data_segment.sections.items[self.data_section_index.?]; | |
| 1814 | ||
| 1815 | self.stub_helper_stubs_start_off = blk: { | |
| 1816 | switch (self.base.options.target.cpu.arch) { | |
| 1817 | .x86_64 => { | |
| 1818 | const code_size = 15; | |
| 1819 | var code: [code_size]u8 = undefined; | |
| 1820 | // lea %r11, [rip + disp] | |
| 1821 | code[0] = 0x4c; | |
| 1822 | code[1] = 0x8d; | |
| 1823 | code[2] = 0x1d; | |
| 1824 | { | |
| 1825 | const target_addr = data.addr + data.size - @sizeOf(u64); | |
| 1826 | const displacement = try math.cast(u32, target_addr - stub_helper.addr - 7); | |
| 1827 | mem.writeIntLittle(u32, code[3..7], displacement); | |
| 1828 | } | |
| 1829 | // push %r11 | |
| 1830 | code[7] = 0x41; | |
| 1831 | code[8] = 0x53; | |
| 1832 | // jmp [rip + disp] | |
| 1833 | code[9] = 0xff; | |
| 1834 | code[10] = 0x25; | |
| 1835 | { | |
| 1836 | const resolv = self.symbol_resolver.get("dyld_stub_binder") orelse unreachable; | |
| 1837 | const got_index = self.got_entries_map.get(.{ | |
| 1838 | .where = .import, | |
| 1839 | .where_index = resolv.where_index, | |
| 1840 | }) orelse unreachable; | |
| 1841 | const addr = got.addr + got_index * @sizeOf(u64); | |
| 1842 | const displacement = try math.cast(u32, addr - stub_helper.addr - code_size); | |
| 1843 | mem.writeIntLittle(u32, code[11..], displacement); | |
| 1844 | } | |
| 1845 | try self.base.file.?.pwriteAll(&code, stub_helper.offset); | |
| 1846 | break :blk stub_helper.offset + code_size; | |
| 1847 | }, | |
| 1848 | .aarch64 => { | |
| 1849 | var code: [6 * @sizeOf(u32)]u8 = undefined; | |
| 1850 | data_blk_outer: { | |
| 1851 | const this_addr = stub_helper.addr; | |
| 1852 | const target_addr = data.addr + data.size - @sizeOf(u64); | |
| 1853 | data_blk: { | |
| 1854 | const displacement = math.cast(i21, target_addr - this_addr) catch break :data_blk; | |
| 1855 | // adr x17, disp | |
| 1856 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32()); | |
| 1857 | // nop | |
| 1858 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32()); | |
| 1859 | break :data_blk_outer; | |
| 1860 | } | |
| 1861 | data_blk: { | |
| 1862 | const new_this_addr = this_addr + @sizeOf(u32); | |
| 1863 | const displacement = math.cast(i21, target_addr - new_this_addr) catch break :data_blk; | |
| 1864 | // nop | |
| 1865 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32()); | |
| 1866 | // adr x17, disp | |
| 1867 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.adr(.x17, displacement).toU32()); | |
| 1868 | break :data_blk_outer; | |
| 1869 | } | |
| 1870 | // Jump is too big, replace adr with adrp and add. | |
| 1871 | const this_page = @intCast(i32, this_addr >> 12); | |
| 1872 | const target_page = @intCast(i32, target_addr >> 12); | |
| 1873 | const pages = @intCast(i21, target_page - this_page); | |
| 1874 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x17, pages).toU32()); | |
| 1875 | const narrowed = @truncate(u12, target_addr); | |
| 1876 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.add(.x17, .x17, narrowed, false).toU32()); | |
| 1877 | } | |
| 1878 | // stp x16, x17, [sp, #-16]! | |
| 1879 | code[8] = 0xf0; | |
| 1880 | code[9] = 0x47; | |
| 1881 | code[10] = 0xbf; | |
| 1882 | code[11] = 0xa9; | |
| 1883 | binder_blk_outer: { | |
| 1884 | const resolv = self.symbol_resolver.get("dyld_stub_binder") orelse unreachable; | |
| 1885 | const got_index = self.got_entries_map.get(.{ | |
| 1886 | .where = .import, | |
| 1887 | .where_index = resolv.where_index, | |
| 1888 | }) orelse unreachable; | |
| 1889 | const this_addr = stub_helper.addr + 3 * @sizeOf(u32); | |
| 1890 | const target_addr = got.addr + got_index * @sizeOf(u64); | |
| 1891 | binder_blk: { | |
| 1892 | const displacement = math.divExact(u64, target_addr - this_addr, 4) catch break :binder_blk; | |
| 1893 | const literal = math.cast(u18, displacement) catch break :binder_blk; | |
| 1894 | // ldr x16, label | |
| 1895 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.ldr(.x16, .{ | |
| 1896 | .literal = literal, | |
| 1897 | }).toU32()); | |
| 1898 | // nop | |
| 1899 | mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32()); | |
| 1900 | break :binder_blk_outer; | |
| 1901 | } | |
| 1902 | binder_blk: { | |
| 1903 | const new_this_addr = this_addr + @sizeOf(u32); | |
| 1904 | const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch break :binder_blk; | |
| 1905 | const literal = math.cast(u18, displacement) catch break :binder_blk; | |
| 1906 | // Pad with nop to please division. | |
| 1907 | // nop | |
| 1908 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32()); | |
| 1909 | // ldr x16, label | |
| 1910 | mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{ | |
| 1911 | .literal = literal, | |
| 1912 | }).toU32()); | |
| 1913 | break :binder_blk_outer; | |
| 1914 | } | |
| 1915 | // Use adrp followed by ldr(immediate). | |
| 1916 | const this_page = @intCast(i32, this_addr >> 12); | |
| 1917 | const target_page = @intCast(i32, target_addr >> 12); | |
| 1918 | const pages = @intCast(i21, target_page - this_page); | |
| 1919 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.adrp(.x16, pages).toU32()); | |
| 1920 | const narrowed = @truncate(u12, target_addr); | |
| 1921 | const offset = try math.divExact(u12, narrowed, 8); | |
| 1922 | mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{ | |
| 1923 | .register = .{ | |
| 1924 | .rn = .x16, | |
| 1925 | .offset = aarch64.Instruction.LoadStoreOffset.imm(offset), | |
| 1926 | }, | |
| 1927 | }).toU32()); | |
| 1928 | } | |
| 1929 | // br x16 | |
| 1930 | code[20] = 0x00; | |
| 1931 | code[21] = 0x02; | |
| 1932 | code[22] = 0x1f; | |
| 1933 | code[23] = 0xd6; | |
| 1934 | try self.base.file.?.pwriteAll(&code, stub_helper.offset); | |
| 1935 | break :blk stub_helper.offset + 6 * @sizeOf(u32); | |
| 1936 | }, | |
| 1937 | else => unreachable, | |
| 1938 | } | |
| 1939 | }; | |
| 1940 | ||
| 1941 | for (self.stubs.items) |_, i| { | |
| 1942 | const index = @intCast(u32, i); | |
| 1943 | // TODO weak bound pointers | |
| 1944 | try self.writeLazySymbolPointer(index); | |
| 1945 | try self.writeStub(index); | |
| 1946 | try self.writeStubInStubHelper(index); | |
| 1947 | } | |
| 1948 | } | |
| 1949 | ||
| 1950 | fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void { | |
| 1951 | const object = self.objects.items[object_id]; | |
| 1952 | ||
| 1953 | log.debug("resolving symbols in '{s}'", .{object.name}); | |
| 1954 | ||
| 1955 | for (object.symtab.items) |sym, id| { | |
| 1956 | const sym_id = @intCast(u32, id); | |
| 1957 | const sym_name = object.getString(sym.n_strx); | |
| 1958 | ||
| 1959 | if (symbolIsStab(sym)) { | |
| 1960 | log.err("unhandled symbol type: stab", .{}); | |
| 1961 | log.err(" symbol '{s}'", .{sym_name}); | |
| 1962 | log.err(" first definition in '{s}'", .{object.name.?}); | |
| 1963 | return error.UnhandledSymbolType; | |
| 1964 | } | |
| 1965 | ||
| 1966 | if (symbolIsIndr(sym)) { | |
| 1967 | log.err("unhandled symbol type: indirect", .{}); | |
| 1968 | log.err(" symbol '{s}'", .{sym_name}); | |
| 1969 | log.err(" first definition in '{s}'", .{object.name.?}); | |
| 1970 | return error.UnhandledSymbolType; | |
| 1971 | } | |
| 1972 | ||
| 1973 | if (symbolIsAbs(sym)) { | |
| 1974 | log.err("unhandled symbol type: absolute", .{}); | |
| 1975 | log.err(" symbol '{s}'", .{sym_name}); | |
| 1976 | log.err(" first definition in '{s}'", .{object.name.?}); | |
| 1977 | return error.UnhandledSymbolType; | |
| 1978 | } | |
| 1979 | ||
| 1980 | if (symbolIsSect(sym)) { | |
| 1981 | // Defined symbol regardless of scope lands in the locals symbol table. | |
| 1982 | const n_strx = blk: { | |
| 1983 | if (self.symbol_resolver.get(sym_name)) |resolv| { | |
| 1984 | switch (resolv.where) { | |
| 1985 | .global => break :blk self.globals.items[resolv.where_index].n_strx, | |
| 1986 | .tentative => break :blk self.tentatives.items[resolv.where_index].n_strx, | |
| 1987 | .undef => break :blk self.undefs.items[resolv.where_index].n_strx, | |
| 1988 | .import => unreachable, | |
| 1989 | } | |
| 1990 | } | |
| 1991 | break :blk try self.makeString(sym_name); | |
| 1992 | }; | |
| 1993 | const local_sym_index = @intCast(u32, self.locals.items.len); | |
| 1994 | try self.locals.append(self.base.allocator, .{ | |
| 1995 | .n_strx = n_strx, | |
| 1996 | .n_type = macho.N_SECT, | |
| 1997 | .n_sect = 0, | |
| 1998 | .n_desc = 0, | |
| 1999 | .n_value = sym.n_value, | |
| 2000 | }); | |
| 2001 | try object.symbol_mapping.putNoClobber(self.base.allocator, sym_id, local_sym_index); | |
| 2002 | ||
| 2003 | // If the symbol's scope is not local aka translation unit, then we need work out | |
| 2004 | // if we should save the symbol as a global, or potentially flag the error. | |
| 2005 | if (!symbolIsExt(sym)) continue; | |
| 2006 | ||
| 2007 | const local = self.locals.items[local_sym_index]; | |
| 2008 | const resolv = self.symbol_resolver.getPtr(sym_name) orelse { | |
| 2009 | const global_sym_index = @intCast(u32, self.globals.items.len); | |
| 2010 | try self.globals.append(self.base.allocator, .{ | |
| 2011 | .n_strx = n_strx, | |
| 2012 | .n_type = sym.n_type, | |
| 2013 | .n_sect = 0, | |
| 2014 | .n_desc = sym.n_desc, | |
| 2015 | .n_value = sym.n_value, | |
| 2016 | }); | |
| 2017 | try self.symbol_resolver.putNoClobber(self.base.allocator, try self.base.allocator.dupe(u8, sym_name), .{ | |
| 2018 | .where = .global, | |
| 2019 | .where_index = global_sym_index, | |
| 2020 | .local_sym_index = local_sym_index, | |
| 2021 | .file = object_id, | |
| 2022 | }); | |
| 2023 | continue; | |
| 2024 | }; | |
| 2025 | ||
| 2026 | switch (resolv.where) { | |
| 2027 | .import => unreachable, | |
| 2028 | .global => { | |
| 2029 | const global = &self.globals.items[resolv.where_index]; | |
| 2030 | ||
| 2031 | if (!(symbolIsWeakDef(sym) or symbolIsPext(sym)) and | |
| 2032 | !(symbolIsWeakDef(global.*) or symbolIsPext(global.*))) | |
| 2033 | { | |
| 2034 | log.err("symbol '{s}' defined multiple times", .{sym_name}); | |
| 2035 | log.err(" first definition in '{s}'", .{self.objects.items[resolv.file].name.?}); | |
| 2036 | log.err(" next definition in '{s}'", .{object.name.?}); | |
| 2037 | return error.MultipleSymbolDefinitions; | |
| 2038 | } | |
| 2039 | ||
| 2040 | if (symbolIsWeakDef(sym) or symbolIsPext(sym)) continue; // Current symbol is weak, so skip it. | |
| 2041 | ||
| 2042 | // Otherwise, update the resolver and the global symbol. | |
| 2043 | global.n_type = sym.n_type; | |
| 2044 | resolv.local_sym_index = local_sym_index; | |
| 2045 | resolv.file = object_id; | |
| 2046 | ||
| 2047 | continue; | |
| 2048 | }, | |
| 2049 | .undef => { | |
| 2050 | const undef = &self.undefs.items[resolv.where_index]; | |
| 2051 | undef.* = .{ | |
| 2052 | .n_strx = 0, | |
| 2053 | .n_type = macho.N_UNDF, | |
| 2054 | .n_sect = 0, | |
| 2055 | .n_desc = 0, | |
| 2056 | .n_value = 0, | |
| 2057 | }; | |
| 2058 | }, | |
| 2059 | .tentative => { | |
| 2060 | const tentative = &self.tentatives.items[resolv.where_index]; | |
| 2061 | tentative.* = .{ | |
| 2062 | .n_strx = 0, | |
| 2063 | .n_type = macho.N_UNDF, | |
| 2064 | .n_sect = 0, | |
| 2065 | .n_desc = 0, | |
| 2066 | .n_value = 0, | |
| 2067 | }; | |
| 2068 | }, | |
| 2069 | } | |
| 2070 | ||
| 2071 | const global_sym_index = @intCast(u32, self.globals.items.len); | |
| 2072 | try self.globals.append(self.base.allocator, .{ | |
| 2073 | .n_strx = local.n_strx, | |
| 2074 | .n_type = sym.n_type, | |
| 2075 | .n_sect = 0, | |
| 2076 | .n_desc = sym.n_desc, | |
| 2077 | .n_value = sym.n_value, | |
| 2078 | }); | |
| 2079 | resolv.* = .{ | |
| 2080 | .where = .global, | |
| 2081 | .where_index = global_sym_index, | |
| 2082 | .local_sym_index = local_sym_index, | |
| 2083 | .file = object_id, | |
| 2084 | }; | |
| 2085 | } else if (symbolIsTentative(sym)) { | |
| 2086 | // Symbol is a tentative definition. | |
| 2087 | const resolv = self.symbol_resolver.getPtr(sym_name) orelse { | |
| 2088 | const tent_sym_index = @intCast(u32, self.tentatives.items.len); | |
| 2089 | try self.tentatives.append(self.base.allocator, .{ | |
| 2090 | .n_strx = try self.makeString(sym_name), | |
| 2091 | .n_type = sym.n_type, | |
| 2092 | .n_sect = 0, | |
| 2093 | .n_desc = sym.n_desc, | |
| 2094 | .n_value = sym.n_value, | |
| 2095 | }); | |
| 2096 | try self.symbol_resolver.putNoClobber(self.base.allocator, try self.base.allocator.dupe(u8, sym_name), .{ | |
| 2097 | .where = .tentative, | |
| 2098 | .where_index = tent_sym_index, | |
| 2099 | .file = object_id, | |
| 2100 | }); | |
| 2101 | continue; | |
| 2102 | }; | |
| 2103 | ||
| 2104 | switch (resolv.where) { | |
| 2105 | .import => unreachable, | |
| 2106 | .global => {}, | |
| 2107 | .undef => { | |
| 2108 | const undef = &self.undefs.items[resolv.where_index]; | |
| 2109 | const tent_sym_index = @intCast(u32, self.tentatives.items.len); | |
| 2110 | try self.tentatives.append(self.base.allocator, .{ | |
| 2111 | .n_strx = undef.n_strx, | |
| 2112 | .n_type = sym.n_type, | |
| 2113 | .n_sect = 0, | |
| 2114 | .n_desc = sym.n_desc, | |
| 2115 | .n_value = sym.n_value, | |
| 2116 | }); | |
| 2117 | resolv.* = .{ | |
| 2118 | .where = .tentative, | |
| 2119 | .where_index = tent_sym_index, | |
| 2120 | .file = object_id, | |
| 2121 | }; | |
| 2122 | undef.* = .{ | |
| 2123 | .n_strx = 0, | |
| 2124 | .n_type = macho.N_UNDF, | |
| 2125 | .n_sect = 0, | |
| 2126 | .n_desc = 0, | |
| 2127 | .n_value = 0, | |
| 2128 | }; | |
| 2129 | }, | |
| 2130 | .tentative => { | |
| 2131 | const tentative = &self.tentatives.items[resolv.where_index]; | |
| 2132 | if (tentative.n_value >= sym.n_value) continue; | |
| 2133 | ||
| 2134 | tentative.n_desc = sym.n_desc; | |
| 2135 | tentative.n_value = sym.n_value; | |
| 2136 | resolv.file = object_id; | |
| 2137 | }, | |
| 2138 | } | |
| 2139 | } else { | |
| 2140 | // Symbol is undefined. | |
| 2141 | if (self.symbol_resolver.contains(sym_name)) continue; | |
| 2142 | ||
| 2143 | const undef_sym_index = @intCast(u32, self.undefs.items.len); | |
| 2144 | try self.undefs.append(self.base.allocator, .{ | |
| 2145 | .n_strx = try self.makeString(sym_name), | |
| 2146 | .n_type = macho.N_UNDF, | |
| 2147 | .n_sect = 0, | |
| 2148 | .n_desc = 0, | |
| 2149 | .n_value = 0, | |
| 2150 | }); | |
| 2151 | try self.symbol_resolver.putNoClobber(self.base.allocator, try self.base.allocator.dupe(u8, sym_name), .{ | |
| 2152 | .where = .undef, | |
| 2153 | .where_index = undef_sym_index, | |
| 2154 | .file = object_id, | |
| 2155 | }); | |
| 2156 | } | |
| 2157 | } | |
| 2158 | } | |
| 2159 | ||
| 2160 | fn resolveSymbols(self: *MachO) !void { | |
| 2161 | // TODO mimicking insertion of null symbol from incremental linker. | |
| 2162 | // This will need to moved. | |
| 2163 | try self.locals.append(self.base.allocator, .{ | |
| 2164 | .n_strx = 0, | |
| 2165 | .n_type = macho.N_UNDF, | |
| 2166 | .n_sect = 0, | |
| 2167 | .n_desc = 0, | |
| 2168 | .n_value = 0, | |
| 2169 | }); | |
| 2170 | try self.strtab.append(self.base.allocator, 0); | |
| 2171 | ||
| 2172 | // First pass, resolve symbols in provided objects. | |
| 2173 | for (self.objects.items) |_, object_id| { | |
| 2174 | try self.resolveSymbolsInObject(@intCast(u16, object_id)); | |
| 2175 | } | |
| 2176 | ||
| 2177 | // Second pass, resolve symbols in static libraries. | |
| 2178 | var next_sym: usize = 0; | |
| 2179 | loop: while (true) : (next_sym += 1) { | |
| 2180 | if (next_sym == self.undefs.items.len) break; | |
| 2181 | ||
| 2182 | const sym = self.undefs.items[next_sym]; | |
| 2183 | if (symbolIsNull(sym)) continue; | |
| 2184 | ||
| 2185 | const sym_name = self.getString(sym.n_strx); | |
| 2186 | ||
| 2187 | for (self.archives.items) |archive| { | |
| 2188 | // Check if the entry exists in a static archive. | |
| 2189 | const offsets = archive.toc.get(sym_name) orelse { | |
| 2190 | // No hit. | |
| 2191 | continue; | |
| 2192 | }; | |
| 2193 | assert(offsets.items.len > 0); | |
| 2194 | ||
| 2195 | const object = try archive.parseObject(offsets.items[0]); | |
| 2196 | const object_id = @intCast(u16, self.objects.items.len); | |
| 2197 | try self.objects.append(self.base.allocator, object); | |
| 2198 | try self.resolveSymbolsInObject(object_id); | |
| 2199 | ||
| 2200 | continue :loop; | |
| 2201 | } | |
| 2202 | } | |
| 2203 | ||
| 2204 | // Convert any tentative definition into a regular symbol and allocate | |
| 2205 | // text blocks for each tentative defintion. | |
| 2206 | for (self.tentatives.items) |sym| { | |
| 2207 | if (symbolIsNull(sym)) continue; | |
| 2208 | ||
| 2209 | const sym_name = self.getString(sym.n_strx); | |
| 2210 | const match: MatchingSection = blk: { | |
| 2211 | if (self.common_section_index == null) { | |
| 2212 | const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2213 | self.common_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 2214 | try data_seg.addSection(self.base.allocator, "__common", .{ | |
| 2215 | .flags = macho.S_ZEROFILL, | |
| 2216 | }); | |
| 2217 | } | |
| 2218 | break :blk .{ | |
| 2219 | .seg = self.data_segment_cmd_index.?, | |
| 2220 | .sect = self.common_section_index.?, | |
| 2221 | }; | |
| 2222 | }; | |
| 2223 | ||
| 2224 | const size = sym.n_value; | |
| 2225 | const code = try self.base.allocator.alloc(u8, size); | |
| 2226 | mem.set(u8, code, 0); | |
| 2227 | const alignment = (sym.n_desc >> 8) & 0x0f; | |
| 2228 | ||
| 2229 | const resolv = self.symbol_resolver.getPtr(sym_name) orelse unreachable; | |
| 2230 | const local_sym_index = @intCast(u32, self.locals.items.len); | |
| 2231 | var nlist = macho.nlist_64{ | |
| 2232 | .n_strx = sym.n_strx, | |
| 2233 | .n_type = macho.N_SECT, | |
| 2234 | .n_sect = self.sectionId(match), | |
| 2235 | .n_desc = 0, | |
| 2236 | .n_value = 0, | |
| 2237 | }; | |
| 2238 | try self.locals.append(self.base.allocator, nlist); | |
| 2239 | const global_sym_index = @intCast(u32, self.globals.items.len); | |
| 2240 | nlist.n_type |= macho.N_EXT; | |
| 2241 | try self.globals.append(self.base.allocator, nlist); | |
| 2242 | resolv.* = .{ | |
| 2243 | .where = .global, | |
| 2244 | .where_index = global_sym_index, | |
| 2245 | .local_sym_index = local_sym_index, | |
| 2246 | }; | |
| 2247 | ||
| 2248 | const block = try self.base.allocator.create(TextBlock); | |
| 2249 | errdefer self.base.allocator.destroy(block); | |
| 2250 | ||
| 2251 | block.* = TextBlock.empty; | |
| 2252 | block.local_sym_index = local_sym_index; | |
| 2253 | block.code = code; | |
| 2254 | block.size = size; | |
| 2255 | block.alignment = alignment; | |
| 2256 | ||
| 2257 | // Update target section's metadata | |
| 2258 | // TODO should we update segment's size here too? | |
| 2259 | // How does it tie with incremental space allocs? | |
| 2260 | const tseg = &self.load_commands.items[match.seg].Segment; | |
| 2261 | const tsect = &tseg.sections.items[match.sect]; | |
| 2262 | const new_alignment = math.max(tsect.@"align", block.alignment); | |
| 2263 | const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment); | |
| 2264 | const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size; | |
| 2265 | tsect.size = new_size; | |
| 2266 | tsect.@"align" = new_alignment; | |
| 2267 | ||
| 2268 | if (self.blocks.getPtr(match)) |last| { | |
| 2269 | last.*.next = block; | |
| 2270 | block.prev = last.*; | |
| 2271 | last.* = block; | |
| 2272 | } else { | |
| 2273 | try self.blocks.putNoClobber(self.base.allocator, match, block); | |
| 2274 | } | |
| 2275 | } | |
| 2276 | ||
| 2277 | // Third pass, resolve symbols in dynamic libraries. | |
| 2278 | { | |
| 2279 | // Put dyld_stub_binder as an undefined special symbol. | |
| 2280 | const undef_sym_index = @intCast(u32, self.undefs.items.len); | |
| 2281 | try self.undefs.append(self.base.allocator, .{ | |
| 2282 | .n_strx = try self.makeString("dyld_stub_binder"), | |
| 2283 | .n_type = macho.N_UNDF, | |
| 2284 | .n_sect = 0, | |
| 2285 | .n_desc = 0, | |
| 2286 | .n_value = 0, | |
| 2287 | }); | |
| 2288 | try self.symbol_resolver.putNoClobber(self.base.allocator, try self.base.allocator.dupe(u8, "dyld_stub_binder"), .{ | |
| 2289 | .where = .undef, | |
| 2290 | .where_index = undef_sym_index, | |
| 2291 | }); | |
| 2292 | } | |
| 2293 | ||
| 2294 | var referenced = std.AutoHashMap(*Dylib, void).init(self.base.allocator); | |
| 2295 | defer referenced.deinit(); | |
| 2296 | ||
| 2297 | loop: for (self.undefs.items) |sym| { | |
| 2298 | if (symbolIsNull(sym)) continue; | |
| 2299 | ||
| 2300 | const sym_name = self.getString(sym.n_strx); | |
| 2301 | for (self.dylibs.items) |dylib| { | |
| 2302 | if (!dylib.symbols.contains(sym_name)) continue; | |
| 2303 | ||
| 2304 | if (!referenced.contains(dylib)) { | |
| 2305 | // Add LC_LOAD_DYLIB load command for each referenced dylib/stub. | |
| 2306 | dylib.ordinal = self.next_dylib_ordinal; | |
| 2307 | const dylib_id = dylib.id orelse unreachable; | |
| 2308 | var dylib_cmd = try commands.createLoadDylibCommand( | |
| 2309 | self.base.allocator, | |
| 2310 | dylib_id.name, | |
| 2311 | dylib_id.timestamp, | |
| 2312 | dylib_id.current_version, | |
| 2313 | dylib_id.compatibility_version, | |
| 2314 | ); | |
| 2315 | errdefer dylib_cmd.deinit(self.base.allocator); | |
| 2316 | try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); | |
| 2317 | self.next_dylib_ordinal += 1; | |
| 2318 | try referenced.putNoClobber(dylib, {}); | |
| 2319 | } | |
| 2320 | ||
| 2321 | const resolv = self.symbol_resolver.getPtr(sym_name) orelse unreachable; | |
| 2322 | const undef = &self.undefs.items[resolv.where_index]; | |
| 2323 | const import_sym_index = @intCast(u32, self.imports.items.len); | |
| 2324 | try self.imports.append(self.base.allocator, .{ | |
| 2325 | .n_strx = undef.n_strx, | |
| 2326 | .n_type = macho.N_UNDF | macho.N_EXT, | |
| 2327 | .n_sect = 0, | |
| 2328 | .n_desc = packDylibOrdinal(dylib.ordinal.?), | |
| 2329 | .n_value = 0, | |
| 2330 | }); | |
| 2331 | resolv.* = .{ | |
| 2332 | .where = .import, | |
| 2333 | .where_index = import_sym_index, | |
| 2334 | }; | |
| 2335 | undef.* = .{ | |
| 2336 | .n_strx = 0, | |
| 2337 | .n_type = macho.N_UNDF, | |
| 2338 | .n_sect = 0, | |
| 2339 | .n_desc = 0, | |
| 2340 | .n_value = 0, | |
| 2341 | }; | |
| 2342 | ||
| 2343 | continue :loop; | |
| 2344 | } | |
| 2345 | } | |
| 2346 | ||
| 2347 | // Fourth pass, handle synthetic symbols and flag any undefined references. | |
| 2348 | if (self.symbol_resolver.getPtr("___dso_handle")) |resolv| blk: { | |
| 2349 | if (resolv.where != .undef) break :blk; | |
| 2350 | ||
| 2351 | const undef = &self.undefs.items[resolv.where_index]; | |
| 2352 | const match: MatchingSection = .{ | |
| 2353 | .seg = self.text_segment_cmd_index.?, | |
| 2354 | .sect = self.text_section_index.?, | |
| 2355 | }; | |
| 2356 | const local_sym_index = @intCast(u32, self.locals.items.len); | |
| 2357 | var nlist = macho.nlist_64{ | |
| 2358 | .n_strx = undef.n_strx, | |
| 2359 | .n_type = macho.N_SECT, | |
| 2360 | .n_sect = self.sectionId(match), | |
| 2361 | .n_desc = 0, | |
| 2362 | .n_value = 0, | |
| 2363 | }; | |
| 2364 | try self.locals.append(self.base.allocator, nlist); | |
| 2365 | const global_sym_index = @intCast(u32, self.globals.items.len); | |
| 2366 | nlist.n_type |= macho.N_EXT; | |
| 2367 | nlist.n_desc = macho.N_WEAK_DEF; | |
| 2368 | try self.globals.append(self.base.allocator, nlist); | |
| 2369 | ||
| 2370 | undef.* = .{ | |
| 2371 | .n_strx = 0, | |
| 2372 | .n_type = macho.N_UNDF, | |
| 2373 | .n_sect = 0, | |
| 2374 | .n_desc = 0, | |
| 2375 | .n_value = 0, | |
| 2376 | }; | |
| 2377 | resolv.* = .{ | |
| 2378 | .where = .global, | |
| 2379 | .where_index = global_sym_index, | |
| 2380 | .local_sym_index = local_sym_index, | |
| 2381 | }; | |
| 2382 | ||
| 2383 | // We create an empty atom for this symbol. | |
| 2384 | // TODO perhaps we should special-case special symbols? Create a separate | |
| 2385 | // linked list of atoms? | |
| 2386 | const block = try self.base.allocator.create(TextBlock); | |
| 2387 | errdefer self.base.allocator.destroy(block); | |
| 2388 | ||
| 2389 | block.* = TextBlock.empty; | |
| 2390 | block.local_sym_index = local_sym_index; | |
| 2391 | block.code = try self.base.allocator.alloc(u8, 0); | |
| 2392 | block.size = 0; | |
| 2393 | block.alignment = 0; | |
| 2394 | ||
| 2395 | if (self.blocks.getPtr(match)) |last| { | |
| 2396 | last.*.next = block; | |
| 2397 | block.prev = last.*; | |
| 2398 | last.* = block; | |
| 2399 | } else { | |
| 2400 | try self.blocks.putNoClobber(self.base.allocator, match, block); | |
| 2401 | } | |
| 2402 | } | |
| 2403 | ||
| 2404 | var has_undefined = false; | |
| 2405 | for (self.undefs.items) |sym| { | |
| 2406 | if (symbolIsNull(sym)) continue; | |
| 2407 | ||
| 2408 | const sym_name = self.getString(sym.n_strx); | |
| 2409 | const resolv = self.symbol_resolver.get(sym_name) orelse unreachable; | |
| 2410 | ||
| 2411 | log.err("undefined reference to symbol '{s}'", .{sym_name}); | |
| 2412 | log.err(" first referenced in '{s}'", .{self.objects.items[resolv.file].name.?}); | |
| 2413 | has_undefined = true; | |
| 2414 | } | |
| 2415 | ||
| 2416 | if (has_undefined) return error.UndefinedSymbolReference; | |
| 2417 | } | |
| 2418 | ||
| 2419 | fn parseTextBlocks(self: *MachO) !void { | |
| 2420 | for (self.objects.items) |object| { | |
| 2421 | try object.parseTextBlocks(self); | |
| 2422 | } | |
| 2423 | } | |
| 2424 | ||
| 2425 | fn populateMetadata(self: *MachO) !void { | |
| 2426 | if (self.pagezero_segment_cmd_index == null) { | |
| 2427 | self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2428 | try self.load_commands.append(self.base.allocator, .{ | |
| 2429 | .Segment = SegmentCommand.empty("__PAGEZERO", .{ | |
| 2430 | .vmsize = 0x100000000, // size always set to 4GB | |
| 2431 | }), | |
| 2432 | }); | |
| 2433 | } | |
| 2434 | ||
| 2435 | if (self.text_segment_cmd_index == null) { | |
| 2436 | self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2437 | try self.load_commands.append(self.base.allocator, .{ | |
| 2438 | .Segment = SegmentCommand.empty("__TEXT", .{ | |
| 2439 | .vmaddr = 0x100000000, // always starts at 4GB | |
| 2440 | .maxprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE, | |
| 2441 | .initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE, | |
| 2442 | }), | |
| 2443 | }); | |
| 2444 | } | |
| 2445 | ||
| 2446 | if (self.text_section_index == null) { | |
| 2447 | const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 2448 | self.text_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 2449 | const alignment: u2 = switch (self.base.options.target.cpu.arch) { | |
| 2450 | .x86_64 => 0, | |
| 2451 | .aarch64 => 2, | |
| 2452 | else => unreachable, // unhandled architecture type | |
| 2453 | }; | |
| 2454 | try text_seg.addSection(self.base.allocator, "__text", .{ | |
| 2455 | .@"align" = alignment, | |
| 2456 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, | |
| 2457 | }); | |
| 2458 | } | |
| 2459 | ||
| 2460 | if (self.stubs_section_index == null) { | |
| 2461 | const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 2462 | self.stubs_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 2463 | const alignment: u2 = switch (self.base.options.target.cpu.arch) { | |
| 2464 | .x86_64 => 0, | |
| 2465 | .aarch64 => 2, | |
| 2466 | else => unreachable, // unhandled architecture type | |
| 2467 | }; | |
| 2468 | const stub_size: u4 = switch (self.base.options.target.cpu.arch) { | |
| 2469 | .x86_64 => 6, | |
| 2470 | .aarch64 => 3 * @sizeOf(u32), | |
| 2471 | else => unreachable, // unhandled architecture type | |
| 2472 | }; | |
| 2473 | try text_seg.addSection(self.base.allocator, "__stubs", .{ | |
| 2474 | .@"align" = alignment, | |
| 2475 | .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, | |
| 2476 | .reserved2 = stub_size, | |
| 2477 | }); | |
| 2478 | } | |
| 2479 | ||
| 2480 | if (self.stub_helper_section_index == null) { | |
| 2481 | const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 2482 | self.stub_helper_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 2483 | const alignment: u2 = switch (self.base.options.target.cpu.arch) { | |
| 2484 | .x86_64 => 0, | |
| 2485 | .aarch64 => 2, | |
| 2486 | else => unreachable, // unhandled architecture type | |
| 2487 | }; | |
| 2488 | const stub_helper_size: u6 = switch (self.base.options.target.cpu.arch) { | |
| 2489 | .x86_64 => 15, | |
| 2490 | .aarch64 => 6 * @sizeOf(u32), | |
| 2491 | else => unreachable, | |
| 2492 | }; | |
| 2493 | try text_seg.addSection(self.base.allocator, "__stub_helper", .{ | |
| 2494 | .size = stub_helper_size, | |
| 2495 | .@"align" = alignment, | |
| 2496 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, | |
| 2497 | }); | |
| 2498 | } | |
| 2499 | ||
| 2500 | if (self.data_const_segment_cmd_index == null) { | |
| 2501 | self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2502 | try self.load_commands.append(self.base.allocator, .{ | |
| 2503 | .Segment = SegmentCommand.empty("__DATA_CONST", .{ | |
| 2504 | .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, | |
| 2505 | .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, | |
| 2506 | }), | |
| 2507 | }); | |
| 2508 | } | |
| 2509 | ||
| 2510 | if (self.got_section_index == null) { | |
| 2511 | const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 2512 | self.got_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 2513 | try data_const_seg.addSection(self.base.allocator, "__got", .{ | |
| 2514 | .@"align" = 3, // 2^3 = @sizeOf(u64) | |
| 2515 | .flags = macho.S_NON_LAZY_SYMBOL_POINTERS, | |
| 2516 | }); | |
| 2517 | } | |
| 2518 | ||
| 2519 | if (self.data_segment_cmd_index == null) { | |
| 2520 | self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2521 | try self.load_commands.append(self.base.allocator, .{ | |
| 2522 | .Segment = SegmentCommand.empty("__DATA", .{ | |
| 2523 | .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, | |
| 2524 | .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, | |
| 2525 | }), | |
| 2526 | }); | |
| 2527 | } | |
| 2528 | ||
| 2529 | if (self.la_symbol_ptr_section_index == null) { | |
| 2530 | const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2531 | self.la_symbol_ptr_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 2532 | try data_seg.addSection(self.base.allocator, "__la_symbol_ptr", .{ | |
| 2533 | .@"align" = 3, // 2^3 = @sizeOf(u64) | |
| 2534 | .flags = macho.S_LAZY_SYMBOL_POINTERS, | |
| 2535 | }); | |
| 2536 | } | |
| 2537 | ||
| 2538 | if (self.data_section_index == null) { | |
| 2539 | const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2540 | self.data_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 2541 | try data_seg.addSection(self.base.allocator, "__data", .{ | |
| 2542 | .@"align" = 3, // 2^3 = @sizeOf(u64) | |
| 2543 | }); | |
| 2544 | } | |
| 2545 | ||
| 2546 | if (self.linkedit_segment_cmd_index == null) { | |
| 2547 | self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2548 | try self.load_commands.append(self.base.allocator, .{ | |
| 2549 | .Segment = SegmentCommand.empty("__LINKEDIT", .{ | |
| 2550 | .maxprot = macho.VM_PROT_READ, | |
| 2551 | .initprot = macho.VM_PROT_READ, | |
| 2552 | }), | |
| 2553 | }); | |
| 2554 | } | |
| 2555 | ||
| 2556 | if (self.dyld_info_cmd_index == null) { | |
| 2557 | self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2558 | try self.load_commands.append(self.base.allocator, .{ | |
| 2559 | .DyldInfoOnly = .{ | |
| 2560 | .cmd = macho.LC_DYLD_INFO_ONLY, | |
| 2561 | .cmdsize = @sizeOf(macho.dyld_info_command), | |
| 2562 | .rebase_off = 0, | |
| 2563 | .rebase_size = 0, | |
| 2564 | .bind_off = 0, | |
| 2565 | .bind_size = 0, | |
| 2566 | .weak_bind_off = 0, | |
| 2567 | .weak_bind_size = 0, | |
| 2568 | .lazy_bind_off = 0, | |
| 2569 | .lazy_bind_size = 0, | |
| 2570 | .export_off = 0, | |
| 2571 | .export_size = 0, | |
| 2572 | }, | |
| 2573 | }); | |
| 2574 | } | |
| 2575 | ||
| 2576 | if (self.symtab_cmd_index == null) { | |
| 2577 | self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2578 | try self.load_commands.append(self.base.allocator, .{ | |
| 2579 | .Symtab = .{ | |
| 2580 | .cmd = macho.LC_SYMTAB, | |
| 2581 | .cmdsize = @sizeOf(macho.symtab_command), | |
| 2582 | .symoff = 0, | |
| 2583 | .nsyms = 0, | |
| 2584 | .stroff = 0, | |
| 2585 | .strsize = 0, | |
| 2586 | }, | |
| 2587 | }); | |
| 2588 | } | |
| 2589 | ||
| 2590 | if (self.dysymtab_cmd_index == null) { | |
| 2591 | self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2592 | try self.load_commands.append(self.base.allocator, .{ | |
| 2593 | .Dysymtab = .{ | |
| 2594 | .cmd = macho.LC_DYSYMTAB, | |
| 2595 | .cmdsize = @sizeOf(macho.dysymtab_command), | |
| 2596 | .ilocalsym = 0, | |
| 2597 | .nlocalsym = 0, | |
| 2598 | .iextdefsym = 0, | |
| 2599 | .nextdefsym = 0, | |
| 2600 | .iundefsym = 0, | |
| 2601 | .nundefsym = 0, | |
| 2602 | .tocoff = 0, | |
| 2603 | .ntoc = 0, | |
| 2604 | .modtaboff = 0, | |
| 2605 | .nmodtab = 0, | |
| 2606 | .extrefsymoff = 0, | |
| 2607 | .nextrefsyms = 0, | |
| 2608 | .indirectsymoff = 0, | |
| 2609 | .nindirectsyms = 0, | |
| 2610 | .extreloff = 0, | |
| 2611 | .nextrel = 0, | |
| 2612 | .locreloff = 0, | |
| 2613 | .nlocrel = 0, | |
| 2614 | }, | |
| 2615 | }); | |
| 2616 | } | |
| 2617 | ||
| 2618 | if (self.dylinker_cmd_index == null) { | |
| 2619 | self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2620 | const cmdsize = @intCast(u32, mem.alignForwardGeneric( | |
| 2621 | u64, | |
| 2622 | @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH), | |
| 2623 | @sizeOf(u64), | |
| 2624 | )); | |
| 2625 | var dylinker_cmd = commands.emptyGenericCommandWithData(macho.dylinker_command{ | |
| 2626 | .cmd = macho.LC_LOAD_DYLINKER, | |
| 2627 | .cmdsize = cmdsize, | |
| 2628 | .name = @sizeOf(macho.dylinker_command), | |
| 2629 | }); | |
| 2630 | dylinker_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name); | |
| 2631 | mem.set(u8, dylinker_cmd.data, 0); | |
| 2632 | mem.copy(u8, dylinker_cmd.data, mem.spanZ(DEFAULT_DYLD_PATH)); | |
| 2633 | try self.load_commands.append(self.base.allocator, .{ .Dylinker = dylinker_cmd }); | |
| 2634 | } | |
| 2635 | ||
| 2636 | if (self.main_cmd_index == null and self.base.options.output_mode == .Exe) { | |
| 2637 | self.main_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2638 | try self.load_commands.append(self.base.allocator, .{ | |
| 2639 | .Main = .{ | |
| 2640 | .cmd = macho.LC_MAIN, | |
| 2641 | .cmdsize = @sizeOf(macho.entry_point_command), | |
| 2642 | .entryoff = 0x0, | |
| 2643 | .stacksize = 0, | |
| 2644 | }, | |
| 2645 | }); | |
| 2646 | } | |
| 2647 | ||
| 2648 | if (self.dylib_id_cmd_index == null and self.base.options.output_mode == .Lib) { | |
| 2649 | self.dylib_id_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2650 | const install_name = try std.fmt.allocPrint(self.base.allocator, "@rpath/{s}", .{ | |
| 2651 | self.base.options.emit.?.sub_path, | |
| 2652 | }); | |
| 2653 | defer self.base.allocator.free(install_name); | |
| 2654 | var dylib_cmd = try commands.createLoadDylibCommand( | |
| 2655 | self.base.allocator, | |
| 2656 | install_name, | |
| 2657 | 2, | |
| 2658 | 0x10000, // TODO forward user-provided versions | |
| 2659 | 0x10000, | |
| 2660 | ); | |
| 2661 | errdefer dylib_cmd.deinit(self.base.allocator); | |
| 2662 | dylib_cmd.inner.cmd = macho.LC_ID_DYLIB; | |
| 2663 | try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); | |
| 2664 | } | |
| 2665 | ||
| 2666 | if (self.version_min_cmd_index == null) { | |
| 2667 | self.version_min_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2668 | const cmd: u32 = switch (self.base.options.target.os.tag) { | |
| 2669 | .macos => macho.LC_VERSION_MIN_MACOSX, | |
| 2670 | .ios => macho.LC_VERSION_MIN_IPHONEOS, | |
| 2671 | .tvos => macho.LC_VERSION_MIN_TVOS, | |
| 2672 | .watchos => macho.LC_VERSION_MIN_WATCHOS, | |
| 2673 | else => unreachable, // wrong OS | |
| 2674 | }; | |
| 2675 | const ver = self.base.options.target.os.version_range.semver.min; | |
| 2676 | const version = ver.major << 16 | ver.minor << 8 | ver.patch; | |
| 2677 | try self.load_commands.append(self.base.allocator, .{ | |
| 2678 | .VersionMin = .{ | |
| 2679 | .cmd = cmd, | |
| 2680 | .cmdsize = @sizeOf(macho.version_min_command), | |
| 2681 | .version = version, | |
| 2682 | .sdk = version, | |
| 2683 | }, | |
| 2684 | }); | |
| 2685 | } | |
| 2686 | ||
| 2687 | if (self.source_version_cmd_index == null) { | |
| 2688 | self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2689 | try self.load_commands.append(self.base.allocator, .{ | |
| 2690 | .SourceVersion = .{ | |
| 2691 | .cmd = macho.LC_SOURCE_VERSION, | |
| 2692 | .cmdsize = @sizeOf(macho.source_version_command), | |
| 2693 | .version = 0x0, | |
| 2694 | }, | |
| 2695 | }); | |
| 2696 | } | |
| 2697 | ||
| 2698 | if (self.uuid_cmd_index == null) { | |
| 2699 | self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2700 | var uuid_cmd: macho.uuid_command = .{ | |
| 2701 | .cmd = macho.LC_UUID, | |
| 2702 | .cmdsize = @sizeOf(macho.uuid_command), | |
| 2703 | .uuid = undefined, | |
| 2704 | }; | |
| 2705 | std.crypto.random.bytes(&uuid_cmd.uuid); | |
| 2706 | try self.load_commands.append(self.base.allocator, .{ .Uuid = uuid_cmd }); | |
| 2707 | } | |
| 2708 | } | |
| 2709 | ||
| 2710 | fn addDataInCodeLC(self: *MachO) !void { | |
| 2711 | if (self.data_in_code_cmd_index == null) { | |
| 2712 | self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2713 | try self.load_commands.append(self.base.allocator, .{ | |
| 2714 | .LinkeditData = .{ | |
| 2715 | .cmd = macho.LC_DATA_IN_CODE, | |
| 2716 | .cmdsize = @sizeOf(macho.linkedit_data_command), | |
| 2717 | .dataoff = 0, | |
| 2718 | .datasize = 0, | |
| 2719 | }, | |
| 2720 | }); | |
| 2721 | } | |
| 2722 | } | |
| 2723 | ||
| 2724 | fn addCodeSignatureLC(self: *MachO) !void { | |
| 2725 | if (self.code_signature_cmd_index == null and self.base.options.target.cpu.arch == .aarch64) { | |
| 2726 | self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2727 | try self.load_commands.append(self.base.allocator, .{ | |
| 2728 | .LinkeditData = .{ | |
| 2729 | .cmd = macho.LC_CODE_SIGNATURE, | |
| 2730 | .cmdsize = @sizeOf(macho.linkedit_data_command), | |
| 2731 | .dataoff = 0, | |
| 2732 | .datasize = 0, | |
| 2733 | }, | |
| 2734 | }); | |
| 2735 | } | |
| 2736 | } | |
| 2737 | ||
| 2738 | fn addRpaths(self: *MachO, rpaths: []const []const u8) !void { | |
| 2739 | for (rpaths) |rpath| { | |
| 2740 | const cmdsize = @intCast(u32, mem.alignForwardGeneric( | |
| 2741 | u64, | |
| 2742 | @sizeOf(macho.rpath_command) + rpath.len + 1, | |
| 2743 | @sizeOf(u64), | |
| 2744 | )); | |
| 2745 | var rpath_cmd = commands.emptyGenericCommandWithData(macho.rpath_command{ | |
| 2746 | .cmd = macho.LC_RPATH, | |
| 2747 | .cmdsize = cmdsize, | |
| 2748 | .path = @sizeOf(macho.rpath_command), | |
| 2749 | }); | |
| 2750 | rpath_cmd.data = try self.base.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path); | |
| 2751 | mem.set(u8, rpath_cmd.data, 0); | |
| 2752 | mem.copy(u8, rpath_cmd.data, rpath); | |
| 2753 | try self.load_commands.append(self.base.allocator, .{ .Rpath = rpath_cmd }); | |
| 2754 | } | |
| 2755 | } | |
| 2756 | ||
| 2757 | fn flushZld(self: *MachO) !void { | |
| 2758 | try self.writeTextBlocks(); | |
| 2759 | try self.writeStubHelperCommon(); | |
| 2760 | ||
| 2761 | if (self.common_section_index) |index| { | |
| 2762 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2763 | const sect = &seg.sections.items[index]; | |
| 2764 | sect.offset = 0; | |
| 2765 | } | |
| 2766 | ||
| 2767 | if (self.bss_section_index) |index| { | |
| 2768 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2769 | const sect = &seg.sections.items[index]; | |
| 2770 | sect.offset = 0; | |
| 2771 | } | |
| 2772 | ||
| 2773 | if (self.tlv_bss_section_index) |index| { | |
| 2774 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2775 | const sect = &seg.sections.items[index]; | |
| 2776 | sect.offset = 0; | |
| 2777 | } | |
| 2778 | ||
| 2779 | try self.writeGotEntries(); | |
| 2780 | try self.setEntryPoint(); | |
| 2781 | try self.writeRebaseInfoTable(); | |
| 2782 | try self.writeBindInfoTable(); | |
| 2783 | try self.writeLazyBindInfoTable(); | |
| 2784 | try self.writeExportInfo(); | |
| 2785 | try self.writeDices(); | |
| 2786 | ||
| 2787 | { | |
| 2788 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2789 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | |
| 2790 | symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 2791 | } | |
| 2792 | ||
| 2793 | try self.writeSymbolTable(); | |
| 2794 | try self.writeStringTable(); | |
| 2795 | ||
| 2796 | { | |
| 2797 | // Seal __LINKEDIT size | |
| 2798 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2799 | seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size); | |
| 2800 | } | |
| 2801 | ||
| 2802 | if (self.base.options.target.cpu.arch == .aarch64) { | |
| 2803 | try self.writeCodeSignaturePadding(); | |
| 2804 | } | |
| 2805 | ||
| 2806 | try self.writeLoadCommands(); | |
| 2807 | try self.writeHeader(); | |
| 2808 | ||
| 2809 | if (self.base.options.target.cpu.arch == .aarch64) { | |
| 2810 | try self.writeCodeSignature(); | |
| 2811 | } | |
| 2812 | ||
| 2813 | // if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64) { | |
| 2814 | // const out_path = self.output.?.path; | |
| 2815 | // try fs.cwd().copyFile(out_path, fs.cwd(), out_path, .{}); | |
| 2816 | // } | |
| 2817 | } | |
| 2818 | ||
| 2819 | fn writeGotEntries(self: *MachO) !void { | |
| 2820 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 2821 | const sect = seg.sections.items[self.got_section_index.?]; | |
| 2822 | ||
| 2823 | var buffer = try self.base.allocator.alloc(u8, self.got_entries.items.len * @sizeOf(u64)); | |
| 2824 | defer self.base.allocator.free(buffer); | |
| 2825 | ||
| 2826 | var stream = std.io.fixedBufferStream(buffer); | |
| 2827 | var writer = stream.writer(); | |
| 2828 | ||
| 2829 | for (self.got_entries.items) |key| { | |
| 2830 | const address: u64 = switch (key.where) { | |
| 2831 | .local => self.locals.items[key.where_index].n_value, | |
| 2832 | .import => 0, | |
| 2833 | }; | |
| 2834 | try writer.writeIntLittle(u64, address); | |
| 2835 | } | |
| 2836 | ||
| 2837 | log.debug("writing GOT pointers at 0x{x} to 0x{x}", .{ sect.offset, sect.offset + buffer.len }); | |
| 2838 | ||
| 2839 | try self.base.file.?.pwriteAll(buffer, sect.offset); | |
| 2840 | } | |
| 2841 | ||
| 2842 | fn setEntryPoint(self: *MachO) !void { | |
| 2843 | if (self.base.options.output_mode != .Exe) return; | |
| 2844 | ||
| 2845 | // TODO we should respect the -entry flag passed in by the user to set a custom | |
| 2846 | // entrypoint. For now, assume default of `_main`. | |
| 2847 | const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 2848 | const resolv = self.symbol_resolver.get("_main") orelse { | |
| 2849 | log.err("'_main' export not found", .{}); | |
| 2850 | return error.MissingMainEntrypoint; | |
| 2851 | }; | |
| 2852 | assert(resolv.where == .global); | |
| 2853 | const sym = self.globals.items[resolv.where_index]; | |
| 2854 | const ec = &self.load_commands.items[self.main_cmd_index.?].Main; | |
| 2855 | ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr); | |
| 2856 | ec.stacksize = self.base.options.stack_size_override orelse 0; | |
| 2857 | } | |
| 2858 | ||
| 2859 | fn writeSymbolTable(self: *MachO) !void { | |
| 2860 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2861 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | |
| 827 | 2862 | |
| 828 | // frameworks | |
| 829 | var framework_dirs = std.ArrayList([]const u8).init(arena); | |
| 830 | for (self.base.options.framework_dirs) |dir| { | |
| 831 | if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| { | |
| 832 | try framework_dirs.append(search_dir); | |
| 833 | } else { | |
| 834 | log.warn("directory not found for '-F{s}'", .{dir}); | |
| 835 | } | |
| 836 | } | |
| 2863 | var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator); | |
| 2864 | defer locals.deinit(); | |
| 2865 | try locals.appendSlice(self.locals.items); | |
| 2866 | ||
| 2867 | if (self.has_stabs) { | |
| 2868 | for (self.objects.items) |object| { | |
| 2869 | if (object.debug_info == null) continue; | |
| 2870 | ||
| 2871 | // Open scope | |
| 2872 | try locals.ensureUnusedCapacity(4); | |
| 2873 | locals.appendAssumeCapacity(.{ | |
| 2874 | .n_strx = try self.makeString(object.tu_comp_dir.?), | |
| 2875 | .n_type = macho.N_SO, | |
| 2876 | .n_sect = 0, | |
| 2877 | .n_desc = 0, | |
| 2878 | .n_value = 0, | |
| 2879 | }); | |
| 2880 | locals.appendAssumeCapacity(.{ | |
| 2881 | .n_strx = try self.makeString(object.tu_name.?), | |
| 2882 | .n_type = macho.N_SO, | |
| 2883 | .n_sect = 0, | |
| 2884 | .n_desc = 0, | |
| 2885 | .n_value = 0, | |
| 2886 | }); | |
| 2887 | locals.appendAssumeCapacity(.{ | |
| 2888 | .n_strx = try self.makeString(object.name.?), | |
| 2889 | .n_type = macho.N_OSO, | |
| 2890 | .n_sect = 0, | |
| 2891 | .n_desc = 1, | |
| 2892 | .n_value = object.mtime orelse 0, | |
| 2893 | }); | |
| 837 | 2894 | |
| 838 | var framework_not_found = false; | |
| 839 | for (self.base.options.frameworks) |framework| { | |
| 840 | for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { | |
| 841 | if (try resolveFramework(arena, framework_dirs.items, framework, ext)) |full_path| { | |
| 842 | try libs.append(full_path); | |
| 843 | break; | |
| 2895 | for (object.text_blocks.items) |block| { | |
| 2896 | if (block.stab) |stab| { | |
| 2897 | const nlists = try stab.asNlists(block.local_sym_index, self); | |
| 2898 | defer self.base.allocator.free(nlists); | |
| 2899 | try locals.appendSlice(nlists); | |
| 2900 | } else { | |
| 2901 | for (block.contained.items) |sym_at_off| { | |
| 2902 | const stab = sym_at_off.stab orelse continue; | |
| 2903 | const nlists = try stab.asNlists(sym_at_off.local_sym_index, self); | |
| 2904 | defer self.base.allocator.free(nlists); | |
| 2905 | try locals.appendSlice(nlists); | |
| 2906 | } | |
| 844 | 2907 | } |
| 845 | } else { | |
| 846 | log.warn("framework not found for '-f{s}'", .{framework}); | |
| 847 | framework_not_found = true; | |
| 848 | } | |
| 849 | } | |
| 850 | ||
| 851 | if (framework_not_found) { | |
| 852 | log.warn("Framework search paths:", .{}); | |
| 853 | for (framework_dirs.items) |dir| { | |
| 854 | log.warn(" {s}", .{dir}); | |
| 855 | 2908 | } |
| 856 | } | |
| 857 | 2909 | |
| 858 | // rpaths | |
| 859 | var rpath_table = std.StringArrayHashMap(void).init(arena); | |
| 860 | for (self.base.options.rpath_list) |rpath| { | |
| 861 | if (rpath_table.contains(rpath)) continue; | |
| 862 | try rpath_table.putNoClobber(rpath, {}); | |
| 2910 | // Close scope | |
| 2911 | locals.appendAssumeCapacity(.{ | |
| 2912 | .n_strx = 0, | |
| 2913 | .n_type = macho.N_SO, | |
| 2914 | .n_sect = 0, | |
| 2915 | .n_desc = 0, | |
| 2916 | .n_value = 0, | |
| 2917 | }); | |
| 863 | 2918 | } |
| 2919 | } | |
| 864 | 2920 | |
| 865 | var rpaths = std.ArrayList([]const u8).init(arena); | |
| 866 | try rpaths.ensureCapacity(rpath_table.count()); | |
| 867 | for (rpath_table.keys()) |*key| { | |
| 868 | rpaths.appendAssumeCapacity(key.*); | |
| 869 | } | |
| 2921 | const nlocals = locals.items.len; | |
| 2922 | const nexports = self.globals.items.len; | |
| 2923 | const nundefs = self.imports.items.len; | |
| 870 | 2924 | |
| 871 | const output: Zld.Output = output: { | |
| 872 | if (is_dyn_lib) { | |
| 873 | const install_name = try std.fmt.allocPrint(arena, "@rpath/{s}", .{ | |
| 874 | self.base.options.emit.?.sub_path, | |
| 875 | }); | |
| 876 | break :output .{ | |
| 877 | .tag = .dylib, | |
| 878 | .path = full_out_path, | |
| 879 | .install_name = install_name, | |
| 880 | }; | |
| 881 | } | |
| 882 | break :output .{ | |
| 883 | .tag = .exe, | |
| 884 | .path = full_out_path, | |
| 885 | }; | |
| 886 | }; | |
| 2925 | const locals_off = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64); | |
| 2926 | const locals_size = nlocals * @sizeOf(macho.nlist_64); | |
| 2927 | log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off }); | |
| 2928 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(locals.items), locals_off); | |
| 887 | 2929 | |
| 888 | if (self.base.options.verbose_link) { | |
| 889 | var argv = std.ArrayList([]const u8).init(arena); | |
| 2930 | const exports_off = locals_off + locals_size; | |
| 2931 | const exports_size = nexports * @sizeOf(macho.nlist_64); | |
| 2932 | log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off }); | |
| 2933 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.globals.items), exports_off); | |
| 890 | 2934 | |
| 891 | try argv.append("zig"); | |
| 892 | try argv.append("ld"); | |
| 2935 | const undefs_off = exports_off + exports_size; | |
| 2936 | const undefs_size = nundefs * @sizeOf(macho.nlist_64); | |
| 2937 | log.debug("writing undefined symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off }); | |
| 2938 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.imports.items), undefs_off); | |
| 893 | 2939 | |
| 894 | if (is_exe_or_dyn_lib) { | |
| 895 | try argv.append("-dynamic"); | |
| 896 | } | |
| 2940 | symtab.nsyms += @intCast(u32, nlocals + nexports + nundefs); | |
| 2941 | seg.inner.filesize += locals_size + exports_size + undefs_size; | |
| 897 | 2942 | |
| 898 | if (is_dyn_lib) { | |
| 899 | try argv.append("-dylib"); | |
| 2943 | // Update dynamic symbol table. | |
| 2944 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; | |
| 2945 | dysymtab.nlocalsym += @intCast(u32, nlocals); | |
| 2946 | dysymtab.iextdefsym = dysymtab.nlocalsym; | |
| 2947 | dysymtab.nextdefsym = @intCast(u32, nexports); | |
| 2948 | dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym; | |
| 2949 | dysymtab.nundefsym = @intCast(u32, nundefs); | |
| 900 | 2950 | |
| 901 | try argv.append("-install_name"); | |
| 902 | try argv.append(output.install_name.?); | |
| 903 | } | |
| 2951 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 2952 | const stubs = &text_segment.sections.items[self.stubs_section_index.?]; | |
| 2953 | const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 2954 | const got = &data_const_segment.sections.items[self.got_section_index.?]; | |
| 2955 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2956 | const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?]; | |
| 904 | 2957 | |
| 905 | if (self.base.options.sysroot) |syslibroot| { | |
| 906 | try argv.append("-syslibroot"); | |
| 907 | try argv.append(syslibroot); | |
| 908 | } | |
| 2958 | const nstubs = @intCast(u32, self.stubs.items.len); | |
| 2959 | const ngot_entries = @intCast(u32, self.got_entries.items.len); | |
| 909 | 2960 | |
| 910 | for (rpaths.items) |rpath| { | |
| 911 | try argv.append("-rpath"); | |
| 912 | try argv.append(rpath); | |
| 913 | } | |
| 2961 | dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 2962 | dysymtab.nindirectsyms = nstubs * 2 + ngot_entries; | |
| 914 | 2963 | |
| 915 | try argv.appendSlice(positionals.items); | |
| 2964 | const needed_size = dysymtab.nindirectsyms * @sizeOf(u32); | |
| 2965 | seg.inner.filesize += needed_size; | |
| 916 | 2966 | |
| 917 | try argv.append("-o"); | |
| 918 | try argv.append(output.path); | |
| 2967 | log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ | |
| 2968 | dysymtab.indirectsymoff, | |
| 2969 | dysymtab.indirectsymoff + needed_size, | |
| 2970 | }); | |
| 919 | 2971 | |
| 920 | if (native_libsystem_available) { | |
| 921 | try argv.append("-lSystem"); | |
| 922 | try argv.append("-lc"); | |
| 923 | } | |
| 2972 | var buf = try self.base.allocator.alloc(u8, needed_size); | |
| 2973 | defer self.base.allocator.free(buf); | |
| 924 | 2974 | |
| 925 | for (search_lib_names.items) |l_name| { | |
| 926 | try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name})); | |
| 927 | } | |
| 2975 | var stream = std.io.fixedBufferStream(buf); | |
| 2976 | var writer = stream.writer(); | |
| 928 | 2977 | |
| 929 | for (self.base.options.lib_dirs) |lib_dir| { | |
| 930 | try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir})); | |
| 931 | } | |
| 2978 | stubs.reserved1 = 0; | |
| 2979 | for (self.stubs.items) |id| { | |
| 2980 | try writer.writeIntLittle(u32, dysymtab.iundefsym + id); | |
| 2981 | } | |
| 932 | 2982 | |
| 933 | Compilation.dump_argv(argv.items); | |
| 2983 | got.reserved1 = nstubs; | |
| 2984 | for (self.got_entries.items) |entry| { | |
| 2985 | switch (entry.where) { | |
| 2986 | .import => { | |
| 2987 | try writer.writeIntLittle(u32, dysymtab.iundefsym + entry.where_index); | |
| 2988 | }, | |
| 2989 | .local => { | |
| 2990 | try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); | |
| 2991 | }, | |
| 934 | 2992 | } |
| 935 | ||
| 936 | try zld.link(positionals.items, output, .{ | |
| 937 | .syslibroot = self.base.options.sysroot, | |
| 938 | .libs = libs.items, | |
| 939 | .rpaths = rpaths.items, | |
| 940 | }); | |
| 941 | 2993 | } |
| 942 | 2994 | |
| 943 | if (!self.base.options.disable_lld_caching) { | |
| 944 | // Update the file with the digest. If it fails we can continue; it only | |
| 945 | // means that the next invocation will have an unnecessary cache miss. | |
| 946 | Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { | |
| 947 | log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)}); | |
| 948 | }; | |
| 949 | // Again failure here only means an unnecessary cache miss. | |
| 950 | man.writeManifest() catch |err| { | |
| 951 | log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); | |
| 952 | }; | |
| 953 | // We hang on to this lock so that the output file path can be used without | |
| 954 | // other processes clobbering it. | |
| 955 | self.base.lock = man.toOwnedLock(); | |
| 2995 | la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries; | |
| 2996 | for (self.stubs.items) |id| { | |
| 2997 | try writer.writeIntLittle(u32, dysymtab.iundefsym + id); | |
| 956 | 2998 | } |
| 2999 | ||
| 3000 | try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff); | |
| 957 | 3001 | } |
| 958 | 3002 | |
| 959 | 3003 | pub fn deinit(self: *MachO) void { |
| ... | ... | @@ -970,6 +3014,8 @@ pub fn deinit(self: *MachO) void { |
| 970 | 3014 | self.stubs.deinit(self.base.allocator); |
| 971 | 3015 | self.stubs_map.deinit(self.base.allocator); |
| 972 | 3016 | self.strtab.deinit(self.base.allocator); |
| 3017 | self.undefs.deinit(self.base.allocator); | |
| 3018 | self.tentatives.deinit(self.base.allocator); | |
| 973 | 3019 | self.imports.deinit(self.base.allocator); |
| 974 | 3020 | self.globals.deinit(self.base.allocator); |
| 975 | 3021 | self.globals_free_list.deinit(self.base.allocator); |
| ... | ... | @@ -981,10 +3027,40 @@ pub fn deinit(self: *MachO) void { |
| 981 | 3027 | } |
| 982 | 3028 | self.symbol_resolver.deinit(self.base.allocator); |
| 983 | 3029 | |
| 3030 | for (self.objects.items) |object| { | |
| 3031 | object.deinit(); | |
| 3032 | self.base.allocator.destroy(object); | |
| 3033 | } | |
| 3034 | self.objects.deinit(self.base.allocator); | |
| 3035 | ||
| 3036 | for (self.archives.items) |archive| { | |
| 3037 | archive.deinit(); | |
| 3038 | self.base.allocator.destroy(archive); | |
| 3039 | } | |
| 3040 | self.archives.deinit(self.base.allocator); | |
| 3041 | ||
| 3042 | for (self.dylibs.items) |dylib| { | |
| 3043 | dylib.deinit(); | |
| 3044 | self.base.allocator.destroy(dylib); | |
| 3045 | } | |
| 3046 | self.dylibs.deinit(self.base.allocator); | |
| 3047 | ||
| 984 | 3048 | for (self.load_commands.items) |*lc| { |
| 985 | 3049 | lc.deinit(self.base.allocator); |
| 986 | 3050 | } |
| 987 | 3051 | self.load_commands.deinit(self.base.allocator); |
| 3052 | ||
| 3053 | // TODO dealloc all blocks | |
| 3054 | self.blocks.deinit(self.base.allocator); | |
| 3055 | } | |
| 3056 | ||
| 3057 | pub fn closeFiles(self: MachO) void { | |
| 3058 | for (self.objects.items) |object| { | |
| 3059 | object.closeFile(); | |
| 3060 | } | |
| 3061 | for (self.archives.items) |archive| { | |
| 3062 | archive.closeFile(); | |
| 3063 | } | |
| 988 | 3064 | } |
| 989 | 3065 | |
| 990 | 3066 | fn freeTextBlock(self: *MachO, text_block: *TextBlock) void { |
| ... | ... | @@ -2664,6 +4740,60 @@ fn writeIndirectSymbolTable(self: *MachO) !void { |
| 2664 | 4740 | self.load_commands_dirty = true; |
| 2665 | 4741 | } |
| 2666 | 4742 | |
| 4743 | fn writeDices(self: *MachO) !void { | |
| 4744 | if (!self.has_dices) return; | |
| 4745 | ||
| 4746 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 4747 | const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData; | |
| 4748 | const fileoff = seg.inner.fileoff + seg.inner.filesize; | |
| 4749 | ||
| 4750 | var buf = std.ArrayList(u8).init(self.base.allocator); | |
| 4751 | defer buf.deinit(); | |
| 4752 | ||
| 4753 | var block: *TextBlock = self.blocks.get(.{ | |
| 4754 | .seg = self.text_segment_cmd_index orelse return, | |
| 4755 | .sect = self.text_section_index orelse return, | |
| 4756 | }) orelse return; | |
| 4757 | ||
| 4758 | while (block.prev) |prev| { | |
| 4759 | block = prev; | |
| 4760 | } | |
| 4761 | ||
| 4762 | const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 4763 | const text_sect = text_seg.sections.items[self.text_section_index.?]; | |
| 4764 | ||
| 4765 | while (true) { | |
| 4766 | if (block.dices.items.len > 0) { | |
| 4767 | const sym = self.locals.items[block.local_sym_index]; | |
| 4768 | const base_off = try math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset); | |
| 4769 | ||
| 4770 | try buf.ensureUnusedCapacity(block.dices.items.len * @sizeOf(macho.data_in_code_entry)); | |
| 4771 | for (block.dices.items) |dice| { | |
| 4772 | const rebased_dice = macho.data_in_code_entry{ | |
| 4773 | .offset = base_off + dice.offset, | |
| 4774 | .length = dice.length, | |
| 4775 | .kind = dice.kind, | |
| 4776 | }; | |
| 4777 | buf.appendSliceAssumeCapacity(mem.asBytes(&rebased_dice)); | |
| 4778 | } | |
| 4779 | } | |
| 4780 | ||
| 4781 | if (block.next) |next| { | |
| 4782 | block = next; | |
| 4783 | } else break; | |
| 4784 | } | |
| 4785 | ||
| 4786 | const datasize = @intCast(u32, buf.items.len); | |
| 4787 | ||
| 4788 | dice_cmd.dataoff = @intCast(u32, fileoff); | |
| 4789 | dice_cmd.datasize = datasize; | |
| 4790 | seg.inner.filesize += datasize; | |
| 4791 | ||
| 4792 | log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ fileoff, fileoff + datasize }); | |
| 4793 | ||
| 4794 | try self.base.file.?.pwriteAll(buf.items, fileoff); | |
| 4795 | } | |
| 4796 | ||
| 2667 | 4797 | fn writeCodeSignaturePadding(self: *MachO) !void { |
| 2668 | 4798 | // TODO figure out how not to rewrite padding every single time. |
| 2669 | 4799 | const tracy = trace(@src()); |
| ... | ... | @@ -2719,7 +4849,7 @@ fn writeCodeSignature(self: *MachO) !void { |
| 2719 | 4849 | try self.base.file.?.pwriteAll(buffer, code_sig_cmd.dataoff); |
| 2720 | 4850 | } |
| 2721 | 4851 | |
| 2722 | fn writeExportTrie(self: *MachO) !void { | |
| 4852 | fn writeExportInfo(self: *MachO) !void { | |
| 2723 | 4853 | if (!self.export_info_dirty) return; |
| 2724 | 4854 | if (self.globals.items.len == 0) return; |
| 2725 | 4855 | |
| ... | ... | @@ -2779,6 +4909,34 @@ fn writeRebaseInfoTable(self: *MachO) !void { |
| 2779 | 4909 | var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator); |
| 2780 | 4910 | defer pointers.deinit(); |
| 2781 | 4911 | |
| 4912 | { | |
| 4913 | var it = self.blocks.iterator(); | |
| 4914 | while (it.next()) |entry| { | |
| 4915 | const match = entry.key_ptr.*; | |
| 4916 | var block: *TextBlock = entry.value_ptr.*; | |
| 4917 | ||
| 4918 | if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable | |
| 4919 | ||
| 4920 | const seg = self.load_commands.items[match.seg].Segment; | |
| 4921 | ||
| 4922 | while (true) { | |
| 4923 | const sym = self.locals.items[block.local_sym_index]; | |
| 4924 | const base_offset = sym.n_value - seg.inner.vmaddr; | |
| 4925 | ||
| 4926 | for (block.rebases.items) |offset| { | |
| 4927 | try pointers.append(.{ | |
| 4928 | .offset = base_offset + offset, | |
| 4929 | .segment_id = match.seg, | |
| 4930 | }); | |
| 4931 | } | |
| 4932 | ||
| 4933 | if (block.prev) |prev| { | |
| 4934 | block = prev; | |
| 4935 | } else break; | |
| 4936 | } | |
| 4937 | } | |
| 4938 | } | |
| 4939 | ||
| 2782 | 4940 | if (self.got_section_index) |idx| { |
| 2783 | 4941 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 2784 | 4942 | const sect = seg.sections.items[idx]; |
| ... | ... | @@ -2837,7 +4995,7 @@ fn writeRebaseInfoTable(self: *MachO) !void { |
| 2837 | 4995 | self.rebase_info_dirty = false; |
| 2838 | 4996 | } |
| 2839 | 4997 | |
| 2840 | fn writeBindingInfoTable(self: *MachO) !void { | |
| 4998 | fn writeBindInfoTable(self: *MachO) !void { | |
| 2841 | 4999 | if (!self.binding_info_dirty) return; |
| 2842 | 5000 | |
| 2843 | 5001 | const tracy = trace(@src()); |
| ... | ... | @@ -2865,6 +5023,37 @@ fn writeBindingInfoTable(self: *MachO) !void { |
| 2865 | 5023 | } |
| 2866 | 5024 | } |
| 2867 | 5025 | |
| 5026 | { | |
| 5027 | var it = self.blocks.iterator(); | |
| 5028 | while (it.next()) |entry| { | |
| 5029 | const match = entry.key_ptr.*; | |
| 5030 | var block: *TextBlock = entry.value_ptr.*; | |
| 5031 | ||
| 5032 | if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable | |
| 5033 | ||
| 5034 | const seg = self.load_commands.items[match.seg].Segment; | |
| 5035 | ||
| 5036 | while (true) { | |
| 5037 | const sym = self.locals.items[block.local_sym_index]; | |
| 5038 | const base_offset = sym.n_value - seg.inner.vmaddr; | |
| 5039 | ||
| 5040 | for (block.bindings.items) |binding| { | |
| 5041 | const bind_sym = self.imports.items[binding.local_sym_index]; | |
| 5042 | try pointers.append(.{ | |
| 5043 | .offset = binding.offset + base_offset, | |
| 5044 | .segment_id = match.seg, | |
| 5045 | .dylib_ordinal = unpackDylibOrdinal(bind_sym.n_desc), | |
| 5046 | .name = self.getString(bind_sym.n_strx), | |
| 5047 | }); | |
| 5048 | } | |
| 5049 | ||
| 5050 | if (block.prev) |prev| { | |
| 5051 | block = prev; | |
| 5052 | } else break; | |
| 5053 | } | |
| 5054 | } | |
| 5055 | } | |
| 5056 | ||
| 2868 | 5057 | const size = try bind.bindInfoSize(pointers.items); |
| 2869 | 5058 | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); |
| 2870 | 5059 | defer self.base.allocator.free(buffer); |
| ... | ... | @@ -2890,7 +5079,7 @@ fn writeBindingInfoTable(self: *MachO) !void { |
| 2890 | 5079 | self.binding_info_dirty = false; |
| 2891 | 5080 | } |
| 2892 | 5081 | |
| 2893 | fn writeLazyBindingInfoTable(self: *MachO) !void { | |
| 5082 | fn writeLazyBindInfoTable(self: *MachO) !void { | |
| 2894 | 5083 | if (!self.lazy_binding_info_dirty) return; |
| 2895 | 5084 | |
| 2896 | 5085 | const tracy = trace(@src()); |
| ... | ... | @@ -3134,7 +5323,7 @@ fn writeHeader(self: *MachO) !void { |
| 3134 | 5323 | else => unreachable, |
| 3135 | 5324 | } |
| 3136 | 5325 | |
| 3137 | if (self.hasTlvDescriptors()) { | |
| 5326 | if (self.tlv_section_index) |_| { | |
| 3138 | 5327 | header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; |
| 3139 | 5328 | } |
| 3140 | 5329 | |
| ... | ... | @@ -3156,10 +5345,6 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { |
| 3156 | 5345 | std.math.maxInt(@TypeOf(actual_size)); |
| 3157 | 5346 | } |
| 3158 | 5347 | |
| 3159 | fn hasTlvDescriptors(_: *MachO) bool { | |
| 3160 | return false; | |
| 3161 | } | |
| 3162 | ||
| 3163 | 5348 | pub fn makeString(self: *MachO, string: []const u8) !u32 { |
| 3164 | 5349 | try self.strtab.ensureUnusedCapacity(self.base.allocator, string.len + 1); |
| 3165 | 5350 | const new_off = @intCast(u32, self.strtab.items.len); |
| ... | ... | @@ -3177,6 +5362,92 @@ pub fn getString(self: *MachO, off: u32) []const u8 { |
| 3177 | 5362 | return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + off)); |
| 3178 | 5363 | } |
| 3179 | 5364 | |
| 5365 | pub fn symbolIsStab(sym: macho.nlist_64) bool { | |
| 5366 | return (macho.N_STAB & sym.n_type) != 0; | |
| 5367 | } | |
| 5368 | ||
| 5369 | pub fn symbolIsPext(sym: macho.nlist_64) bool { | |
| 5370 | return (macho.N_PEXT & sym.n_type) != 0; | |
| 5371 | } | |
| 5372 | ||
| 5373 | pub fn symbolIsExt(sym: macho.nlist_64) bool { | |
| 5374 | return (macho.N_EXT & sym.n_type) != 0; | |
| 5375 | } | |
| 5376 | ||
| 5377 | pub fn symbolIsSect(sym: macho.nlist_64) bool { | |
| 5378 | const type_ = macho.N_TYPE & sym.n_type; | |
| 5379 | return type_ == macho.N_SECT; | |
| 5380 | } | |
| 5381 | ||
| 5382 | pub fn symbolIsUndf(sym: macho.nlist_64) bool { | |
| 5383 | const type_ = macho.N_TYPE & sym.n_type; | |
| 5384 | return type_ == macho.N_UNDF; | |
| 5385 | } | |
| 5386 | ||
| 5387 | pub fn symbolIsIndr(sym: macho.nlist_64) bool { | |
| 5388 | const type_ = macho.N_TYPE & sym.n_type; | |
| 5389 | return type_ == macho.N_INDR; | |
| 5390 | } | |
| 5391 | ||
| 5392 | pub fn symbolIsAbs(sym: macho.nlist_64) bool { | |
| 5393 | const type_ = macho.N_TYPE & sym.n_type; | |
| 5394 | return type_ == macho.N_ABS; | |
| 5395 | } | |
| 5396 | ||
| 5397 | pub fn symbolIsWeakDef(sym: macho.nlist_64) bool { | |
| 5398 | return (sym.n_desc & macho.N_WEAK_DEF) != 0; | |
| 5399 | } | |
| 5400 | ||
| 5401 | pub fn symbolIsWeakRef(sym: macho.nlist_64) bool { | |
| 5402 | return (sym.n_desc & macho.N_WEAK_REF) != 0; | |
| 5403 | } | |
| 5404 | ||
| 5405 | pub fn symbolIsTentative(sym: macho.nlist_64) bool { | |
| 5406 | if (!symbolIsUndf(sym)) return false; | |
| 5407 | return sym.n_value != 0; | |
| 5408 | } | |
| 5409 | ||
| 5410 | pub fn symbolIsNull(sym: macho.nlist_64) bool { | |
| 5411 | return sym.n_value == 0 and sym.n_desc == 0 and sym.n_type == 0 and sym.n_strx == 0 and sym.n_sect == 0; | |
| 5412 | } | |
| 5413 | ||
| 5414 | pub fn symbolIsTemp(sym: macho.nlist_64, sym_name: []const u8) bool { | |
| 5415 | if (!symbolIsSect(sym)) return false; | |
| 5416 | if (symbolIsExt(sym)) return false; | |
| 5417 | return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L"); | |
| 5418 | } | |
| 5419 | ||
| 5420 | pub fn sectionId(self: MachO, match: MatchingSection) u8 { | |
| 5421 | // TODO there might be a more generic way of doing this. | |
| 5422 | var section: u8 = 0; | |
| 5423 | for (self.load_commands.items) |cmd, cmd_id| { | |
| 5424 | if (cmd != .Segment) break; | |
| 5425 | if (cmd_id == match.seg) { | |
| 5426 | section += @intCast(u8, match.sect) + 1; | |
| 5427 | break; | |
| 5428 | } | |
| 5429 | section += @intCast(u8, cmd.Segment.sections.items.len); | |
| 5430 | } | |
| 5431 | return section; | |
| 5432 | } | |
| 5433 | ||
| 5434 | pub fn unpackSectionId(self: MachO, section_id: u8) MatchingSection { | |
| 5435 | var match: MatchingSection = undefined; | |
| 5436 | var section: u8 = 0; | |
| 5437 | outer: for (self.load_commands.items) |cmd, cmd_id| { | |
| 5438 | assert(cmd == .Segment); | |
| 5439 | for (cmd.Segment.sections.items) |_, sect_id| { | |
| 5440 | section += 1; | |
| 5441 | if (section_id == section) { | |
| 5442 | match.seg = @intCast(u16, cmd_id); | |
| 5443 | match.sect = @intCast(u16, sect_id); | |
| 5444 | break :outer; | |
| 5445 | } | |
| 5446 | } | |
| 5447 | } | |
| 5448 | return match; | |
| 5449 | } | |
| 5450 | ||
| 3180 | 5451 | fn packDylibOrdinal(ordinal: u16) u16 { |
| 3181 | 5452 | return ordinal * macho.N_SYMBOL_RESOLVER; |
| 3182 | 5453 | } |
| ... | ... | @@ -3184,3 +5455,16 @@ fn packDylibOrdinal(ordinal: u16) u16 { |
| 3184 | 5455 | fn unpackDylibOrdinal(pack: u16) u16 { |
| 3185 | 5456 | return @divExact(pack, macho.N_SYMBOL_RESOLVER); |
| 3186 | 5457 | } |
| 5458 | ||
| 5459 | pub fn findFirst(comptime T: type, haystack: []T, start: usize, predicate: anytype) usize { | |
| 5460 | if (!@hasDecl(@TypeOf(predicate), "predicate")) | |
| 5461 | @compileError("Predicate is required to define fn predicate(@This(), T) bool"); | |
| 5462 | ||
| 5463 | if (start == haystack.len) return start; | |
| 5464 | ||
| 5465 | var i = start; | |
| 5466 | while (i < haystack.len) : (i += 1) { | |
| 5467 | if (predicate.predicate(haystack[i])) break; | |
| 5468 | } | |
| 5469 | return i; | |
| 5470 | } |
src/link/MachO/Dylib.zig+2-2| ... | ... | @@ -13,7 +13,7 @@ const fat = @import("fat.zig"); |
| 13 | 13 | const Allocator = mem.Allocator; |
| 14 | 14 | const Arch = std.Target.Cpu.Arch; |
| 15 | 15 | const LibStub = @import("../tapi.zig").LibStub; |
| 16 | const Zld = @import("Zld.zig"); | |
| 16 | const MachO = @import("../MachO.zig"); | |
| 17 | 17 | |
| 18 | 18 | usingnamespace @import("commands.zig"); |
| 19 | 19 | |
| ... | ... | @@ -324,7 +324,7 @@ fn parseSymbols(self: *Dylib) !void { |
| 324 | 324 | _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff + self.library_offset); |
| 325 | 325 | |
| 326 | 326 | for (slice) |sym| { |
| 327 | const add_to_symtab = Zld.symbolIsExt(sym) and (Zld.symbolIsSect(sym) or Zld.symbolIsIndr(sym)); | |
| 327 | const add_to_symtab = MachO.symbolIsExt(sym) and (MachO.symbolIsSect(sym) or MachO.symbolIsIndr(sym)); | |
| 328 | 328 | |
| 329 | 329 | if (!add_to_symtab) continue; |
| 330 | 330 |
src/link/MachO/Object.zig+41-45| ... | ... | @@ -13,8 +13,8 @@ const sort = std.sort; |
| 13 | 13 | |
| 14 | 14 | const Allocator = mem.Allocator; |
| 15 | 15 | const Arch = std.Target.Cpu.Arch; |
| 16 | const MachO = @import("../MachO.zig"); | |
| 16 | 17 | const TextBlock = @import("TextBlock.zig"); |
| 17 | const Zld = @import("Zld.zig"); | |
| 18 | 18 | |
| 19 | 19 | usingnamespace @import("commands.zig"); |
| 20 | 20 | |
| ... | ... | @@ -307,8 +307,8 @@ const NlistWithIndex = struct { |
| 307 | 307 | } |
| 308 | 308 | }; |
| 309 | 309 | |
| 310 | const start = Zld.findFirst(NlistWithIndex, symbols, 0, Predicate{ .addr = sect.addr }); | |
| 311 | const end = Zld.findFirst(NlistWithIndex, symbols, start, Predicate{ .addr = sect.addr + sect.size }); | |
| 310 | const start = MachO.findFirst(NlistWithIndex, symbols, 0, Predicate{ .addr = sect.addr }); | |
| 311 | const end = MachO.findFirst(NlistWithIndex, symbols, start, Predicate{ .addr = sect.addr + sect.size }); | |
| 312 | 312 | |
| 313 | 313 | return symbols[start..end]; |
| 314 | 314 | } |
| ... | ... | @@ -323,8 +323,8 @@ fn filterDice(dices: []macho.data_in_code_entry, start_addr: u64, end_addr: u64) |
| 323 | 323 | } |
| 324 | 324 | }; |
| 325 | 325 | |
| 326 | const start = Zld.findFirst(macho.data_in_code_entry, dices, 0, Predicate{ .addr = start_addr }); | |
| 327 | const end = Zld.findFirst(macho.data_in_code_entry, dices, start, Predicate{ .addr = end_addr }); | |
| 326 | const start = MachO.findFirst(macho.data_in_code_entry, dices, 0, Predicate{ .addr = start_addr }); | |
| 327 | const end = MachO.findFirst(macho.data_in_code_entry, dices, start, Predicate{ .addr = end_addr }); | |
| 328 | 328 | |
| 329 | 329 | return dices[start..end]; |
| 330 | 330 | } |
| ... | ... | @@ -335,10 +335,10 @@ const TextBlockParser = struct { |
| 335 | 335 | code: []u8, |
| 336 | 336 | relocs: []macho.relocation_info, |
| 337 | 337 | object: *Object, |
| 338 | zld: *Zld, | |
| 338 | macho_file: *MachO, | |
| 339 | 339 | nlists: []NlistWithIndex, |
| 340 | 340 | index: u32 = 0, |
| 341 | match: Zld.MatchingSection, | |
| 341 | match: MachO.MatchingSection, | |
| 342 | 342 | |
| 343 | 343 | fn peek(self: *TextBlockParser) ?NlistWithIndex { |
| 344 | 344 | return if (self.index + 1 < self.nlists.len) self.nlists[self.index + 1] else null; |
| ... | ... | @@ -349,10 +349,10 @@ const TextBlockParser = struct { |
| 349 | 349 | }; |
| 350 | 350 | |
| 351 | 351 | fn lessThanBySeniority(context: SeniorityContext, lhs: NlistWithIndex, rhs: NlistWithIndex) bool { |
| 352 | if (!Zld.symbolIsExt(rhs.nlist)) { | |
| 353 | return Zld.symbolIsTemp(lhs.nlist, context.object.getString(lhs.nlist.n_strx)); | |
| 354 | } else if (Zld.symbolIsPext(rhs.nlist) or Zld.symbolIsWeakDef(rhs.nlist)) { | |
| 355 | return !Zld.symbolIsExt(lhs.nlist); | |
| 352 | if (!MachO.symbolIsExt(rhs.nlist)) { | |
| 353 | return MachO.symbolIsTemp(lhs.nlist, context.object.getString(lhs.nlist.n_strx)); | |
| 354 | } else if (MachO.symbolIsPext(rhs.nlist) or MachO.symbolIsWeakDef(rhs.nlist)) { | |
| 355 | return !MachO.symbolIsExt(lhs.nlist); | |
| 356 | 356 | } else { |
| 357 | 357 | return true; |
| 358 | 358 | } |
| ... | ... | @@ -383,7 +383,7 @@ const TextBlockParser = struct { |
| 383 | 383 | const sym = self.object.symbols.items[nlist_with_index.index]; |
| 384 | 384 | if (sym.payload != .regular) { |
| 385 | 385 | log.err("expected a regular symbol, found {s}", .{sym.payload}); |
| 386 | log.err(" when remapping {s}", .{self.zld.getString(sym.strx)}); | |
| 386 | log.err(" when remapping {s}", .{self.macho_file.getString(sym.strx)}); | |
| 387 | 387 | return error.SymbolIsNotRegular; |
| 388 | 388 | } |
| 389 | 389 | assert(sym.payload.regular.local_sym_index != 0); // This means the symbol has not been properly resolved. |
| ... | ... | @@ -401,7 +401,7 @@ const TextBlockParser = struct { |
| 401 | 401 | } |
| 402 | 402 | |
| 403 | 403 | const senior_nlist = aliases.pop(); |
| 404 | const senior_sym = self.zld.locals.items[senior_nlist.index]; | |
| 404 | const senior_sym = self.macho_file.locals.items[senior_nlist.index]; | |
| 405 | 405 | assert(senior_sym.payload == .regular); |
| 406 | 406 | senior_sym.payload.regular.segment_id = self.match.seg; |
| 407 | 407 | senior_sym.payload.regular.section_id = self.match.sect; |
| ... | ... | @@ -429,7 +429,7 @@ const TextBlockParser = struct { |
| 429 | 429 | } |
| 430 | 430 | } |
| 431 | 431 | } |
| 432 | if (self.zld.globals.contains(self.zld.getString(senior_sym.strx))) break :blk .global; | |
| 432 | if (self.macho_file.globals.contains(self.macho_file.getString(senior_sym.strx))) break :blk .global; | |
| 433 | 433 | break :blk .static; |
| 434 | 434 | } else null; |
| 435 | 435 | |
| ... | ... | @@ -448,19 +448,19 @@ const TextBlockParser = struct { |
| 448 | 448 | for (aliases.items) |alias| { |
| 449 | 449 | block.aliases.appendAssumeCapacity(alias.index); |
| 450 | 450 | |
| 451 | const sym = self.zld.locals.items[alias.index]; | |
| 451 | const sym = self.macho_file.locals.items[alias.index]; | |
| 452 | 452 | const reg = &sym.payload.regular; |
| 453 | 453 | reg.segment_id = self.match.seg; |
| 454 | 454 | reg.section_id = self.match.sect; |
| 455 | 455 | } |
| 456 | 456 | } |
| 457 | 457 | |
| 458 | try block.parseRelocsFromObject(relocs, object, .{ | |
| 458 | try block.parseRelocsFromObject(self.allocator, relocs, object, .{ | |
| 459 | 459 | .base_addr = start_addr, |
| 460 | .zld = self.zld, | |
| 460 | .macho_file = self.macho_file, | |
| 461 | 461 | }); |
| 462 | 462 | |
| 463 | if (self.zld.has_dices) { | |
| 463 | if (self.macho_file.has_dices) { | |
| 464 | 464 | const dices = filterDice( |
| 465 | 465 | self.object.data_in_code_entries.items, |
| 466 | 466 | senior_nlist.nlist.n_value, |
| ... | ... | @@ -483,7 +483,7 @@ const TextBlockParser = struct { |
| 483 | 483 | } |
| 484 | 484 | }; |
| 485 | 485 | |
| 486 | pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { | |
| 486 | pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void { | |
| 487 | 487 | const seg = self.load_commands.items[self.segment_cmd_index.?].Segment; |
| 488 | 488 | |
| 489 | 489 | log.debug("analysing {s}", .{self.name.?}); |
| ... | ... | @@ -513,7 +513,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 513 | 513 | }); |
| 514 | 514 | |
| 515 | 515 | // Get matching segment/section in the final artifact. |
| 516 | const match = (try zld.getMatchingSection(sect)) orelse { | |
| 516 | const match = (try macho_file.getMatchingSection(sect)) orelse { | |
| 517 | 517 | log.debug("unhandled section", .{}); |
| 518 | 518 | continue; |
| 519 | 519 | }; |
| ... | ... | @@ -538,7 +538,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 538 | 538 | // duplicates at all? Need some benchmarks! |
| 539 | 539 | // const is_splittable = false; |
| 540 | 540 | |
| 541 | zld.has_dices = blk: { | |
| 541 | macho_file.has_dices = blk: { | |
| 542 | 542 | if (self.text_section_index) |index| { |
| 543 | 543 | if (index != id) break :blk false; |
| 544 | 544 | if (self.data_in_code_entries.items.len == 0) break :blk false; |
| ... | ... | @@ -546,7 +546,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 546 | 546 | } |
| 547 | 547 | break :blk false; |
| 548 | 548 | }; |
| 549 | zld.has_stabs = zld.has_stabs or self.debug_info != null; | |
| 549 | macho_file.has_stabs = macho_file.has_stabs or self.debug_info != null; | |
| 550 | 550 | |
| 551 | 551 | { |
| 552 | 552 | // next: { |
| ... | ... | @@ -711,11 +711,11 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 711 | 711 | defer self.allocator.free(sym_name); |
| 712 | 712 | |
| 713 | 713 | const block_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: { |
| 714 | const block_local_sym_index = @intCast(u32, zld.locals.items.len); | |
| 715 | try zld.locals.append(zld.allocator, .{ | |
| 716 | .n_strx = try zld.makeString(sym_name), | |
| 714 | const block_local_sym_index = @intCast(u32, macho_file.locals.items.len); | |
| 715 | try macho_file.locals.append(macho_file.base.allocator, .{ | |
| 716 | .n_strx = try macho_file.makeString(sym_name), | |
| 717 | 717 | .n_type = macho.N_SECT, |
| 718 | .n_sect = zld.sectionId(match), | |
| 718 | .n_sect = macho_file.sectionId(match), | |
| 719 | 719 | .n_desc = 0, |
| 720 | 720 | .n_value = sect.addr, |
| 721 | 721 | }); |
| ... | ... | @@ -726,20 +726,20 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 726 | 726 | const block = try self.allocator.create(TextBlock); |
| 727 | 727 | errdefer self.allocator.destroy(block); |
| 728 | 728 | |
| 729 | block.* = TextBlock.init(self.allocator); | |
| 729 | block.* = TextBlock.empty; | |
| 730 | 730 | block.local_sym_index = block_local_sym_index; |
| 731 | 731 | block.code = try self.allocator.dupe(u8, code); |
| 732 | 732 | block.size = sect.size; |
| 733 | 733 | block.alignment = sect.@"align"; |
| 734 | 734 | |
| 735 | try block.parseRelocsFromObject(relocs, self, .{ | |
| 735 | try block.parseRelocsFromObject(self.allocator, relocs, self, .{ | |
| 736 | 736 | .base_addr = 0, |
| 737 | .zld = zld, | |
| 737 | .macho_file = macho_file, | |
| 738 | 738 | }); |
| 739 | 739 | |
| 740 | if (zld.has_dices) { | |
| 740 | if (macho_file.has_dices) { | |
| 741 | 741 | const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + sect.size); |
| 742 | try block.dices.ensureTotalCapacity(dices.len); | |
| 742 | try block.dices.ensureTotalCapacity(self.allocator, dices.len); | |
| 743 | 743 | |
| 744 | 744 | for (dices) |dice| { |
| 745 | 745 | block.dices.appendAssumeCapacity(.{ |
| ... | ... | @@ -755,15 +755,13 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 755 | 755 | // the filtered symbols and note which symbol is contained within so that |
| 756 | 756 | // we can properly allocate addresses down the line. |
| 757 | 757 | // While we're at it, we need to update segment,section mapping of each symbol too. |
| 758 | var contained = std.ArrayList(TextBlock.SymbolAtOffset).init(self.allocator); | |
| 759 | defer contained.deinit(); | |
| 760 | try contained.ensureTotalCapacity(filtered_nlists.len); | |
| 758 | try block.contained.ensureTotalCapacity(self.allocator, filtered_nlists.len); | |
| 761 | 759 | |
| 762 | 760 | for (filtered_nlists) |nlist_with_index| { |
| 763 | 761 | const nlist = nlist_with_index.nlist; |
| 764 | 762 | const local_sym_index = self.symbol_mapping.get(nlist_with_index.index) orelse unreachable; |
| 765 | const local = &zld.locals.items[local_sym_index]; | |
| 766 | local.n_sect = zld.sectionId(match); | |
| 763 | const local = &macho_file.locals.items[local_sym_index]; | |
| 764 | local.n_sect = macho_file.sectionId(match); | |
| 767 | 765 | |
| 768 | 766 | const stab: ?TextBlock.Stab = if (self.debug_info) |di| blk: { |
| 769 | 767 | // TODO there has to be a better to handle this. |
| ... | ... | @@ -781,19 +779,17 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 781 | 779 | break :blk .static; |
| 782 | 780 | } else null; |
| 783 | 781 | |
| 784 | contained.appendAssumeCapacity(.{ | |
| 782 | block.contained.appendAssumeCapacity(.{ | |
| 785 | 783 | .local_sym_index = local_sym_index, |
| 786 | 784 | .offset = nlist.n_value - sect.addr, |
| 787 | 785 | .stab = stab, |
| 788 | 786 | }); |
| 789 | 787 | } |
| 790 | 788 | |
| 791 | block.contained = contained.toOwnedSlice(); | |
| 792 | ||
| 793 | 789 | // Update target section's metadata |
| 794 | 790 | // TODO should we update segment's size here too? |
| 795 | 791 | // How does it tie with incremental space allocs? |
| 796 | const tseg = &zld.load_commands.items[match.seg].Segment; | |
| 792 | const tseg = &macho_file.load_commands.items[match.seg].Segment; | |
| 797 | 793 | const tsect = &tseg.sections.items[match.sect]; |
| 798 | 794 | const new_alignment = math.max(tsect.@"align", block.alignment); |
| 799 | 795 | const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment); |
| ... | ... | @@ -801,12 +797,12 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 801 | 797 | tsect.size = new_size; |
| 802 | 798 | tsect.@"align" = new_alignment; |
| 803 | 799 | |
| 804 | if (zld.blocks.getPtr(match)) |last| { | |
| 800 | if (macho_file.blocks.getPtr(match)) |last| { | |
| 805 | 801 | last.*.next = block; |
| 806 | 802 | block.prev = last.*; |
| 807 | 803 | last.* = block; |
| 808 | 804 | } else { |
| 809 | try zld.blocks.putNoClobber(zld.allocator, match, block); | |
| 805 | try macho_file.blocks.putNoClobber(self.allocator, match, block); | |
| 810 | 806 | } |
| 811 | 807 | |
| 812 | 808 | try self.text_blocks.append(self.allocator, block); |
| ... | ... | @@ -814,7 +810,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { |
| 814 | 810 | } |
| 815 | 811 | } |
| 816 | 812 | |
| 817 | pub fn symbolFromReloc(self: *Object, zld: *Zld, rel: macho.relocation_info) !*Symbol { | |
| 813 | pub fn symbolFromReloc(self: *Object, macho_file: *MachO, rel: macho.relocation_info) !*Symbol { | |
| 818 | 814 | const symbol = blk: { |
| 819 | 815 | if (rel.r_extern == 1) { |
| 820 | 816 | break :blk self.symbols.items[rel.r_symbolnum]; |
| ... | ... | @@ -832,9 +828,9 @@ pub fn symbolFromReloc(self: *Object, zld: *Zld, rel: macho.relocation_info) !*S |
| 832 | 828 | sectionName(sect), |
| 833 | 829 | }); |
| 834 | 830 | defer self.allocator.free(name); |
| 835 | const symbol = try zld.allocator.create(Symbol); | |
| 831 | const symbol = try macho_file.allocator.create(Symbol); | |
| 836 | 832 | symbol.* = .{ |
| 837 | .strx = try zld.makeString(name), | |
| 833 | .strx = try macho_file.makeString(name), | |
| 838 | 834 | .payload = .{ |
| 839 | 835 | .regular = .{ |
| 840 | 836 | .linkage = .translation_unit, |
src/link/MachO/TextBlock.zig+163-101| ... | ... | @@ -12,24 +12,64 @@ const meta = std.meta; |
| 12 | 12 | |
| 13 | 13 | const Allocator = mem.Allocator; |
| 14 | 14 | const Arch = std.Target.Cpu.Arch; |
| 15 | const MachO = @import("../MachO.zig"); | |
| 15 | 16 | const Object = @import("Object.zig"); |
| 16 | const Zld = @import("Zld.zig"); | |
| 17 | 17 | |
| 18 | allocator: *Allocator, | |
| 18 | /// Each decl always gets a local symbol with the fully qualified name. | |
| 19 | /// The vaddr and size are found here directly. | |
| 20 | /// The file offset is found by computing the vaddr offset from the section vaddr | |
| 21 | /// the symbol references, and adding that to the file offset of the section. | |
| 22 | /// If this field is 0, it means the codegen size = 0 and there is no symbol or | |
| 23 | /// offset table entry. | |
| 19 | 24 | local_sym_index: u32, |
| 20 | stab: ?Stab = null, | |
| 21 | aliases: std.ArrayList(u32), | |
| 22 | references: std.AutoArrayHashMap(u32, void), | |
| 23 | contained: ?[]SymbolAtOffset = null, | |
| 25 | ||
| 26 | /// List of symbol aliases pointing to the same block via different nlists | |
| 27 | aliases: std.ArrayListUnmanaged(u32) = .{}, | |
| 28 | ||
| 29 | /// List of symbols contained within this block | |
| 30 | contained: std.ArrayListUnmanaged(SymbolAtOffset) = .{}, | |
| 31 | ||
| 32 | /// Code (may be non-relocated) this block represents | |
| 24 | 33 | code: []u8, |
| 25 | relocs: std.ArrayList(Relocation), | |
| 34 | ||
| 35 | /// Size and alignment of this text block | |
| 36 | /// Unlike in Elf, we need to store the size of this symbol as part of | |
| 37 | /// the TextBlock since macho.nlist_64 lacks this information. | |
| 26 | 38 | size: u64, |
| 27 | 39 | alignment: u32, |
| 28 | rebases: std.ArrayList(u64), | |
| 29 | bindings: std.ArrayList(SymbolAtOffset), | |
| 30 | dices: std.ArrayList(macho.data_in_code_entry), | |
| 31 | next: ?*TextBlock = null, | |
| 32 | prev: ?*TextBlock = null, | |
| 40 | ||
| 41 | relocs: std.ArrayListUnmanaged(Relocation) = .{}, | |
| 42 | ||
| 43 | /// List of offsets contained within this block that need rebasing by the dynamic | |
| 44 | /// loader in presence of ASLR | |
| 45 | rebases: std.ArrayListUnmanaged(u64) = .{}, | |
| 46 | ||
| 47 | /// List of offsets contained within this block that will be dynamically bound | |
| 48 | /// by the dynamic loader and contain pointers to resolved (at load time) extern | |
| 49 | /// symbols (aka proxies aka imports) | |
| 50 | bindings: std.ArrayListUnmanaged(SymbolAtOffset) = .{}, | |
| 51 | ||
| 52 | /// List of data-in-code entries. This is currently specific to x86_64 only. | |
| 53 | dices: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, | |
| 54 | ||
| 55 | /// Stab entry for this block. This is currently specific to a binary created | |
| 56 | /// by linking object files in a traditional sense - in incremental sense, we | |
| 57 | /// bypass stabs altogether to produce dSYM bundle directly with fully relocated | |
| 58 | /// DWARF sections. | |
| 59 | stab: ?Stab = null, | |
| 60 | ||
| 61 | /// Points to the previous and next neighbours | |
| 62 | next: ?*TextBlock, | |
| 63 | prev: ?*TextBlock, | |
| 64 | ||
| 65 | /// Previous/next linked list pointers. | |
| 66 | /// This is the linked list node for this Decl's corresponding .debug_info tag. | |
| 67 | dbg_info_prev: ?*TextBlock, | |
| 68 | dbg_info_next: ?*TextBlock, | |
| 69 | /// Offset into .debug_info pointing to the tag for this Decl. | |
| 70 | dbg_info_off: u32, | |
| 71 | /// Size of the .debug_info tag for this Decl, not including padding. | |
| 72 | dbg_info_len: u32, | |
| 33 | 73 | |
| 34 | 74 | pub const SymbolAtOffset = struct { |
| 35 | 75 | local_sym_index: u32, |
| ... | ... | @@ -42,11 +82,11 @@ pub const Stab = union(enum) { |
| 42 | 82 | static, |
| 43 | 83 | global, |
| 44 | 84 | |
| 45 | pub fn asNlists(stab: Stab, local_sym_index: u32, zld: *Zld) ![]macho.nlist_64 { | |
| 46 | var nlists = std.ArrayList(macho.nlist_64).init(zld.allocator); | |
| 85 | pub fn asNlists(stab: Stab, local_sym_index: u32, macho_file: anytype) ![]macho.nlist_64 { | |
| 86 | var nlists = std.ArrayList(macho.nlist_64).init(macho_file.base.allocator); | |
| 47 | 87 | defer nlists.deinit(); |
| 48 | 88 | |
| 49 | const sym = zld.locals.items[local_sym_index]; | |
| 89 | const sym = macho_file.locals.items[local_sym_index]; | |
| 50 | 90 | switch (stab) { |
| 51 | 91 | .function => |size| { |
| 52 | 92 | try nlists.ensureUnusedCapacity(4); |
| ... | ... | @@ -130,7 +170,7 @@ pub const Relocation = struct { |
| 130 | 170 | offset: u32, |
| 131 | 171 | source_addr: u64, |
| 132 | 172 | target_addr: u64, |
| 133 | zld: *Zld, | |
| 173 | macho_file: *MachO, | |
| 134 | 174 | }; |
| 135 | 175 | |
| 136 | 176 | pub const Unsigned = struct { |
| ... | ... | @@ -148,7 +188,7 @@ pub const Relocation = struct { |
| 148 | 188 | pub fn resolve(self: Unsigned, args: ResolveArgs) !void { |
| 149 | 189 | const result = blk: { |
| 150 | 190 | if (self.subtractor) |subtractor| { |
| 151 | const sym = args.zld.locals.items[subtractor]; | |
| 191 | const sym = args.macho_file.locals.items[subtractor]; | |
| 152 | 192 | break :blk @intCast(i64, args.target_addr) - @intCast(i64, sym.n_value) + self.addend; |
| 153 | 193 | } else { |
| 154 | 194 | break :blk @intCast(i64, args.target_addr) + self.addend; |
| ... | ... | @@ -500,38 +540,59 @@ pub const Relocation = struct { |
| 500 | 540 | } |
| 501 | 541 | }; |
| 502 | 542 | |
| 503 | pub fn init(allocator: *Allocator) TextBlock { | |
| 504 | return .{ | |
| 505 | .allocator = allocator, | |
| 506 | .local_sym_index = undefined, | |
| 507 | .aliases = std.ArrayList(u32).init(allocator), | |
| 508 | .references = std.AutoArrayHashMap(u32, void).init(allocator), | |
| 509 | .code = undefined, | |
| 510 | .relocs = std.ArrayList(Relocation).init(allocator), | |
| 511 | .size = undefined, | |
| 512 | .alignment = undefined, | |
| 513 | .rebases = std.ArrayList(u64).init(allocator), | |
| 514 | .bindings = std.ArrayList(SymbolAtOffset).init(allocator), | |
| 515 | .dices = std.ArrayList(macho.data_in_code_entry).init(allocator), | |
| 516 | }; | |
| 543 | pub const empty = TextBlock{ | |
| 544 | .local_sym_index = 0, | |
| 545 | .code = undefined, | |
| 546 | .size = 0, | |
| 547 | .alignment = 0, | |
| 548 | .prev = null, | |
| 549 | .next = null, | |
| 550 | .dbg_info_prev = null, | |
| 551 | .dbg_info_next = null, | |
| 552 | .dbg_info_off = undefined, | |
| 553 | .dbg_info_len = undefined, | |
| 554 | }; | |
| 555 | ||
| 556 | pub fn deinit(self: *TextBlock, allocator: *Allocator) void { | |
| 557 | self.dices.deinit(allocator); | |
| 558 | self.bindings.deinit(allocator); | |
| 559 | self.rebases.deinit(allocator); | |
| 560 | self.relocs.deinit(allocator); | |
| 561 | self.allocator.free(self.code); | |
| 562 | self.contained.deinit(allocator); | |
| 563 | self.aliases.deinit(allocator); | |
| 517 | 564 | } |
| 518 | 565 | |
| 519 | pub fn deinit(self: *TextBlock) void { | |
| 520 | self.aliases.deinit(); | |
| 521 | self.references.deinit(); | |
| 522 | if (self.contained) |contained| { | |
| 523 | self.allocator.free(contained); | |
| 566 | /// Returns how much room there is to grow in virtual address space. | |
| 567 | /// File offset relocation happens transparently, so it is not included in | |
| 568 | /// this calculation. | |
| 569 | pub fn capacity(self: TextBlock, macho_file: MachO) u64 { | |
| 570 | const self_sym = macho_file.locals.items[self.local_sym_index]; | |
| 571 | if (self.next) |next| { | |
| 572 | const next_sym = macho_file.locals.items[next.local_sym_index]; | |
| 573 | return next_sym.n_value - self_sym.n_value; | |
| 574 | } else { | |
| 575 | // We are the last block. | |
| 576 | // The capacity is limited only by virtual address space. | |
| 577 | return std.math.maxInt(u64) - self_sym.n_value; | |
| 524 | 578 | } |
| 525 | self.allocator.free(self.code); | |
| 526 | self.relocs.deinit(); | |
| 527 | self.rebases.deinit(); | |
| 528 | self.bindings.deinit(); | |
| 529 | self.dices.deinit(); | |
| 579 | } | |
| 580 | ||
| 581 | pub fn freeListEligible(self: TextBlock, macho_file: MachO) bool { | |
| 582 | // No need to keep a free list node for the last block. | |
| 583 | const next = self.next orelse return false; | |
| 584 | const self_sym = macho_file.locals.items[self.local_sym_index]; | |
| 585 | const next_sym = macho_file.locals.items[next.local_sym_index]; | |
| 586 | const cap = next_sym.n_value - self_sym.n_value; | |
| 587 | const ideal_cap = MachO.padToIdeal(self.size); | |
| 588 | if (cap <= ideal_cap) return false; | |
| 589 | const surplus = cap - ideal_cap; | |
| 590 | return surplus >= MachO.min_text_capacity; | |
| 530 | 591 | } |
| 531 | 592 | |
| 532 | 593 | const RelocContext = struct { |
| 533 | 594 | base_addr: u64 = 0, |
| 534 | zld: *Zld, | |
| 595 | macho_file: *MachO, | |
| 535 | 596 | }; |
| 536 | 597 | |
| 537 | 598 | fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocContext) !Relocation { |
| ... | ... | @@ -548,19 +609,19 @@ fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocCo |
| 548 | 609 | const local_sym_index = object.sections_as_symbols.get(sect_id) orelse blk: { |
| 549 | 610 | const seg = object.load_commands.items[object.segment_cmd_index.?].Segment; |
| 550 | 611 | const sect = seg.sections.items[sect_id]; |
| 551 | const match = (try ctx.zld.getMatchingSection(sect)) orelse unreachable; | |
| 552 | const local_sym_index = @intCast(u32, ctx.zld.locals.items.len); | |
| 553 | const sym_name = try std.fmt.allocPrint(ctx.zld.allocator, "l_{s}_{s}_{s}", .{ | |
| 612 | const match = (try ctx.macho_file.getMatchingSection(sect)) orelse unreachable; | |
| 613 | const local_sym_index = @intCast(u32, ctx.macho_file.locals.items.len); | |
| 614 | const sym_name = try std.fmt.allocPrint(ctx.macho_file.base.allocator, "l_{s}_{s}_{s}", .{ | |
| 554 | 615 | object.name.?, |
| 555 | 616 | commands.segmentName(sect), |
| 556 | 617 | commands.sectionName(sect), |
| 557 | 618 | }); |
| 558 | defer ctx.zld.allocator.free(sym_name); | |
| 619 | defer ctx.macho_file.base.allocator.free(sym_name); | |
| 559 | 620 | |
| 560 | try ctx.zld.locals.append(ctx.zld.allocator, .{ | |
| 561 | .n_strx = try ctx.zld.makeString(sym_name), | |
| 621 | try ctx.macho_file.locals.append(ctx.macho_file.base.allocator, .{ | |
| 622 | .n_strx = try ctx.macho_file.makeString(sym_name), | |
| 562 | 623 | .n_type = macho.N_SECT, |
| 563 | .n_sect = ctx.zld.sectionId(match), | |
| 624 | .n_sect = ctx.macho_file.sectionId(match), | |
| 564 | 625 | .n_desc = 0, |
| 565 | 626 | .n_value = sect.addr, |
| 566 | 627 | }); |
| ... | ... | @@ -574,12 +635,12 @@ fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocCo |
| 574 | 635 | const sym = object.symtab.items[rel.r_symbolnum]; |
| 575 | 636 | const sym_name = object.getString(sym.n_strx); |
| 576 | 637 | |
| 577 | if (Zld.symbolIsSect(sym) and !Zld.symbolIsExt(sym)) { | |
| 638 | if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) { | |
| 578 | 639 | const where_index = object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable; |
| 579 | 640 | parsed_rel.where = .local; |
| 580 | 641 | parsed_rel.where_index = where_index; |
| 581 | 642 | } else { |
| 582 | const resolv = ctx.zld.symbol_resolver.get(sym_name) orelse unreachable; | |
| 643 | const resolv = ctx.macho_file.symbol_resolver.get(sym_name) orelse unreachable; | |
| 583 | 644 | switch (resolv.where) { |
| 584 | 645 | .global => { |
| 585 | 646 | parsed_rel.where = .local; |
| ... | ... | @@ -599,6 +660,7 @@ fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocCo |
| 599 | 660 | |
| 600 | 661 | pub fn parseRelocsFromObject( |
| 601 | 662 | self: *TextBlock, |
| 663 | allocator: *Allocator, | |
| 602 | 664 | relocs: []macho.relocation_info, |
| 603 | 665 | object: *Object, |
| 604 | 666 | ctx: RelocContext, |
| ... | ... | @@ -638,11 +700,11 @@ pub fn parseRelocsFromObject( |
| 638 | 700 | const sym = object.symtab.items[rel.r_symbolnum]; |
| 639 | 701 | const sym_name = object.getString(sym.n_strx); |
| 640 | 702 | |
| 641 | if (Zld.symbolIsSect(sym) and !Zld.symbolIsExt(sym)) { | |
| 703 | if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) { | |
| 642 | 704 | const where_index = object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable; |
| 643 | 705 | subtractor = where_index; |
| 644 | 706 | } else { |
| 645 | const resolv = ctx.zld.symbol_resolver.get(sym_name) orelse unreachable; | |
| 707 | const resolv = ctx.macho_file.symbol_resolver.get(sym_name) orelse unreachable; | |
| 646 | 708 | assert(resolv.where == .global); |
| 647 | 709 | subtractor = resolv.local_sym_index; |
| 648 | 710 | } |
| ... | ... | @@ -732,11 +794,7 @@ pub fn parseRelocsFromObject( |
| 732 | 794 | else => unreachable, |
| 733 | 795 | } |
| 734 | 796 | |
| 735 | try self.relocs.append(parsed_rel); | |
| 736 | ||
| 737 | if (parsed_rel.where == .local) { | |
| 738 | try self.references.put(parsed_rel.where_index, {}); | |
| 739 | } | |
| 797 | try self.relocs.append(allocator, parsed_rel); | |
| 740 | 798 | |
| 741 | 799 | const is_via_got = switch (parsed_rel.payload) { |
| 742 | 800 | .pointer_to_got => true, |
| ... | ... | @@ -747,28 +805,30 @@ pub fn parseRelocsFromObject( |
| 747 | 805 | }; |
| 748 | 806 | |
| 749 | 807 | if (is_via_got) blk: { |
| 750 | const key = Zld.GotIndirectionKey{ | |
| 808 | const key = MachO.GotIndirectionKey{ | |
| 751 | 809 | .where = switch (parsed_rel.where) { |
| 752 | 810 | .local => .local, |
| 753 | 811 | .import => .import, |
| 754 | 812 | }, |
| 755 | 813 | .where_index = parsed_rel.where_index, |
| 756 | 814 | }; |
| 757 | if (ctx.zld.got_entries.contains(key)) break :blk; | |
| 815 | if (ctx.macho_file.got_entries_map.contains(key)) break :blk; | |
| 758 | 816 | |
| 759 | try ctx.zld.got_entries.putNoClobber(ctx.zld.allocator, key, {}); | |
| 817 | const got_index = @intCast(u32, ctx.macho_file.got_entries.items.len); | |
| 818 | try ctx.macho_file.got_entries.append(ctx.macho_file.base.allocator, key); | |
| 819 | try ctx.macho_file.got_entries_map.putNoClobber(ctx.macho_file.base.allocator, key, got_index); | |
| 760 | 820 | } else if (parsed_rel.payload == .unsigned) { |
| 761 | 821 | switch (parsed_rel.where) { |
| 762 | 822 | .import => { |
| 763 | try self.bindings.append(.{ | |
| 823 | try self.bindings.append(allocator, .{ | |
| 764 | 824 | .local_sym_index = parsed_rel.where_index, |
| 765 | 825 | .offset = parsed_rel.offset, |
| 766 | 826 | }); |
| 767 | 827 | }, |
| 768 | 828 | .local => { |
| 769 | const source_sym = ctx.zld.locals.items[self.local_sym_index]; | |
| 770 | const match = ctx.zld.unpackSectionId(source_sym.n_sect); | |
| 771 | const seg = ctx.zld.load_commands.items[match.seg].Segment; | |
| 829 | const source_sym = ctx.macho_file.locals.items[self.local_sym_index]; | |
| 830 | const match = ctx.macho_file.unpackSectionId(source_sym.n_sect); | |
| 831 | const seg = ctx.macho_file.load_commands.items[match.seg].Segment; | |
| 772 | 832 | const sect = seg.sections.items[match.sect]; |
| 773 | 833 | const sect_type = commands.sectionType(sect); |
| 774 | 834 | |
| ... | ... | @@ -778,12 +838,12 @@ pub fn parseRelocsFromObject( |
| 778 | 838 | // TODO actually, a check similar to what dyld is doing, that is, verifying |
| 779 | 839 | // that the segment is writable should be enough here. |
| 780 | 840 | const is_right_segment = blk: { |
| 781 | if (ctx.zld.data_segment_cmd_index) |idx| { | |
| 841 | if (ctx.macho_file.data_segment_cmd_index) |idx| { | |
| 782 | 842 | if (match.seg == idx) { |
| 783 | 843 | break :blk true; |
| 784 | 844 | } |
| 785 | 845 | } |
| 786 | if (ctx.zld.data_const_segment_cmd_index) |idx| { | |
| 846 | if (ctx.macho_file.data_const_segment_cmd_index) |idx| { | |
| 787 | 847 | if (match.seg == idx) { |
| 788 | 848 | break :blk true; |
| 789 | 849 | } |
| ... | ... | @@ -804,15 +864,17 @@ pub fn parseRelocsFromObject( |
| 804 | 864 | }; |
| 805 | 865 | |
| 806 | 866 | if (should_rebase) { |
| 807 | try self.rebases.append(parsed_rel.offset); | |
| 867 | try self.rebases.append(allocator, parsed_rel.offset); | |
| 808 | 868 | } |
| 809 | 869 | }, |
| 810 | 870 | } |
| 811 | 871 | } else if (parsed_rel.payload == .branch) blk: { |
| 812 | 872 | if (parsed_rel.where != .import) break :blk; |
| 813 | if (ctx.zld.stubs.contains(parsed_rel.where_index)) break :blk; | |
| 873 | if (ctx.macho_file.stubs_map.contains(parsed_rel.where_index)) break :blk; | |
| 814 | 874 | |
| 815 | try ctx.zld.stubs.putNoClobber(ctx.zld.allocator, parsed_rel.where_index, {}); | |
| 875 | const stubs_index = @intCast(u32, ctx.macho_file.stubs.items.len); | |
| 876 | try ctx.macho_file.stubs.append(ctx.macho_file.base.allocator, parsed_rel.where_index); | |
| 877 | try ctx.macho_file.stubs_map.putNoClobber(ctx.macho_file.base.allocator, parsed_rel.where_index, stubs_index); | |
| 816 | 878 | } |
| 817 | 879 | } |
| 818 | 880 | } |
| ... | ... | @@ -852,7 +914,7 @@ fn parseUnsigned( |
| 852 | 914 | |
| 853 | 915 | if (rel.r_extern == 0) { |
| 854 | 916 | assert(out.where == .local); |
| 855 | const target_sym = ctx.zld.locals.items[out.where_index]; | |
| 917 | const target_sym = ctx.macho_file.locals.items[out.where_index]; | |
| 856 | 918 | addend -= @intCast(i64, target_sym.n_value); |
| 857 | 919 | } |
| 858 | 920 | |
| ... | ... | @@ -872,7 +934,7 @@ fn parseBranch(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ct |
| 872 | 934 | |
| 873 | 935 | out.payload = .{ |
| 874 | 936 | .branch = .{ |
| 875 | .arch = ctx.zld.target.?.cpu.arch, | |
| 937 | .arch = ctx.macho_file.base.options.target.cpu.arch, | |
| 876 | 938 | }, |
| 877 | 939 | }; |
| 878 | 940 | } |
| ... | ... | @@ -948,10 +1010,10 @@ fn parseSigned(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ct |
| 948 | 1010 | var addend: i64 = mem.readIntLittle(i32, self.code[out.offset..][0..4]) + correction; |
| 949 | 1011 | |
| 950 | 1012 | if (rel.r_extern == 0) { |
| 951 | const source_sym = ctx.zld.locals.items[self.local_sym_index]; | |
| 1013 | const source_sym = ctx.macho_file.locals.items[self.local_sym_index]; | |
| 952 | 1014 | const target_sym = switch (out.where) { |
| 953 | .local => ctx.zld.locals.items[out.where_index], | |
| 954 | .import => ctx.zld.imports.items[out.where_index], | |
| 1015 | .local => ctx.macho_file.locals.items[out.where_index], | |
| 1016 | .import => ctx.macho_file.imports.items[out.where_index], | |
| 955 | 1017 | }; |
| 956 | 1018 | addend = @intCast(i64, source_sym.n_value + out.offset + 4) + addend - @intCast(i64, target_sym.n_value); |
| 957 | 1019 | } |
| ... | ... | @@ -986,12 +1048,12 @@ fn parseLoad(self: TextBlock, rel: macho.relocation_info, out: *Relocation) void |
| 986 | 1048 | }; |
| 987 | 1049 | } |
| 988 | 1050 | |
| 989 | pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void { | |
| 1051 | pub fn resolveRelocs(self: *TextBlock, macho_file: *MachO) !void { | |
| 990 | 1052 | for (self.relocs.items) |rel| { |
| 991 | 1053 | log.debug("relocating {}", .{rel}); |
| 992 | 1054 | |
| 993 | 1055 | const source_addr = blk: { |
| 994 | const sym = zld.locals.items[self.local_sym_index]; | |
| 1056 | const sym = macho_file.locals.items[self.local_sym_index]; | |
| 995 | 1057 | break :blk sym.n_value + rel.offset; |
| 996 | 1058 | }; |
| 997 | 1059 | const target_addr = blk: { |
| ... | ... | @@ -1004,9 +1066,9 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void { |
| 1004 | 1066 | }; |
| 1005 | 1067 | |
| 1006 | 1068 | if (is_via_got) { |
| 1007 | const dc_seg = zld.load_commands.items[zld.data_const_segment_cmd_index.?].Segment; | |
| 1008 | const got = dc_seg.sections.items[zld.got_section_index.?]; | |
| 1009 | const got_index = zld.got_entries.getIndex(.{ | |
| 1069 | const dc_seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment; | |
| 1070 | const got = dc_seg.sections.items[macho_file.got_section_index.?]; | |
| 1071 | const got_index = macho_file.got_entries_map.get(.{ | |
| 1010 | 1072 | .where = switch (rel.where) { |
| 1011 | 1073 | .local => .local, |
| 1012 | 1074 | .import => .import, |
| ... | ... | @@ -1014,10 +1076,10 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void { |
| 1014 | 1076 | .where_index = rel.where_index, |
| 1015 | 1077 | }) orelse { |
| 1016 | 1078 | const sym = switch (rel.where) { |
| 1017 | .local => zld.locals.items[rel.where_index], | |
| 1018 | .import => zld.imports.items[rel.where_index], | |
| 1079 | .local => macho_file.locals.items[rel.where_index], | |
| 1080 | .import => macho_file.imports.items[rel.where_index], | |
| 1019 | 1081 | }; |
| 1020 | log.err("expected GOT entry for symbol '{s}'", .{zld.getString(sym.n_strx)}); | |
| 1082 | log.err("expected GOT entry for symbol '{s}'", .{macho_file.getString(sym.n_strx)}); | |
| 1021 | 1083 | log.err(" this is an internal linker error", .{}); |
| 1022 | 1084 | return error.FailedToResolveRelocationTarget; |
| 1023 | 1085 | }; |
| ... | ... | @@ -1026,11 +1088,11 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void { |
| 1026 | 1088 | |
| 1027 | 1089 | switch (rel.where) { |
| 1028 | 1090 | .local => { |
| 1029 | const sym = zld.locals.items[rel.where_index]; | |
| 1091 | const sym = macho_file.locals.items[rel.where_index]; | |
| 1030 | 1092 | const is_tlv = is_tlv: { |
| 1031 | const source_sym = zld.locals.items[self.local_sym_index]; | |
| 1032 | const match = zld.unpackSectionId(source_sym.n_sect); | |
| 1033 | const seg = zld.load_commands.items[match.seg].Segment; | |
| 1093 | const source_sym = macho_file.locals.items[self.local_sym_index]; | |
| 1094 | const match = macho_file.unpackSectionId(source_sym.n_sect); | |
| 1095 | const seg = macho_file.load_commands.items[match.seg].Segment; | |
| 1034 | 1096 | const sect = seg.sections.items[match.sect]; |
| 1035 | 1097 | break :is_tlv commands.sectionType(sect) == macho.S_THREAD_LOCAL_VARIABLES; |
| 1036 | 1098 | }; |
| ... | ... | @@ -1040,11 +1102,11 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void { |
| 1040 | 1102 | // defined TLV template init section in the following order: |
| 1041 | 1103 | // * wrt to __thread_data if defined, then |
| 1042 | 1104 | // * wrt to __thread_bss |
| 1043 | const seg = zld.load_commands.items[zld.data_segment_cmd_index.?].Segment; | |
| 1105 | const seg = macho_file.load_commands.items[macho_file.data_segment_cmd_index.?].Segment; | |
| 1044 | 1106 | const base_address = inner: { |
| 1045 | if (zld.tlv_data_section_index) |i| { | |
| 1107 | if (macho_file.tlv_data_section_index) |i| { | |
| 1046 | 1108 | break :inner seg.sections.items[i].addr; |
| 1047 | } else if (zld.tlv_bss_section_index) |i| { | |
| 1109 | } else if (macho_file.tlv_bss_section_index) |i| { | |
| 1048 | 1110 | break :inner seg.sections.items[i].addr; |
| 1049 | 1111 | } else { |
| 1050 | 1112 | log.err("threadlocal variables present but no initializer sections found", .{}); |
| ... | ... | @@ -1059,12 +1121,12 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void { |
| 1059 | 1121 | break :blk sym.n_value; |
| 1060 | 1122 | }, |
| 1061 | 1123 | .import => { |
| 1062 | const stubs_index = zld.stubs.getIndex(rel.where_index) orelse { | |
| 1124 | const stubs_index = macho_file.stubs_map.get(rel.where_index) orelse { | |
| 1063 | 1125 | // TODO verify in TextBlock that the symbol is indeed dynamically bound. |
| 1064 | 1126 | break :blk 0; // Dynamically bound by dyld. |
| 1065 | 1127 | }; |
| 1066 | const segment = zld.load_commands.items[zld.text_segment_cmd_index.?].Segment; | |
| 1067 | const stubs = segment.sections.items[zld.stubs_section_index.?]; | |
| 1128 | const segment = macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment; | |
| 1129 | const stubs = segment.sections.items[macho_file.stubs_section_index.?]; | |
| 1068 | 1130 | break :blk stubs.addr + stubs_index * stubs.reserved2; |
| 1069 | 1131 | }, |
| 1070 | 1132 | } |
| ... | ... | @@ -1078,14 +1140,14 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void { |
| 1078 | 1140 | .offset = rel.offset, |
| 1079 | 1141 | .source_addr = source_addr, |
| 1080 | 1142 | .target_addr = target_addr, |
| 1081 | .zld = zld, | |
| 1143 | .macho_file = macho_file, | |
| 1082 | 1144 | }); |
| 1083 | 1145 | } |
| 1084 | 1146 | } |
| 1085 | 1147 | |
| 1086 | pub fn print_this(self: *const TextBlock, zld: *Zld) void { | |
| 1148 | pub fn print_this(self: *const TextBlock, macho_file: MachO) void { | |
| 1087 | 1149 | log.warn("TextBlock", .{}); |
| 1088 | log.warn(" {}: {}", .{ self.local_sym_index, zld.locals.items[self.local_sym_index] }); | |
| 1150 | log.warn(" {}: {}", .{ self.local_sym_index, macho_file.locals.items[self.local_sym_index] }); | |
| 1089 | 1151 | if (self.stab) |stab| { |
| 1090 | 1152 | log.warn(" stab: {}", .{stab}); |
| 1091 | 1153 | } |
| ... | ... | @@ -1125,11 +1187,11 @@ pub fn print_this(self: *const TextBlock, zld: *Zld) void { |
| 1125 | 1187 | log.warn(" align = {}", .{self.alignment}); |
| 1126 | 1188 | } |
| 1127 | 1189 | |
| 1128 | pub fn print(self: *const TextBlock, zld: *Zld) void { | |
| 1190 | pub fn print(self: *const TextBlock, macho_file: MachO) void { | |
| 1129 | 1191 | if (self.prev) |prev| { |
| 1130 | prev.print(zld); | |
| 1192 | prev.print(macho_file); | |
| 1131 | 1193 | } |
| 1132 | self.print_this(zld); | |
| 1194 | self.print_this(macho_file); | |
| 1133 | 1195 | } |
| 1134 | 1196 | |
| 1135 | 1197 | const RelocIterator = struct { |
| ... | ... | @@ -1159,8 +1221,8 @@ fn filterRelocs(relocs: []macho.relocation_info, start_addr: u64, end_addr: u64) |
| 1159 | 1221 | } |
| 1160 | 1222 | }; |
| 1161 | 1223 | |
| 1162 | const start = Zld.findFirst(macho.relocation_info, relocs, 0, Predicate{ .addr = end_addr }); | |
| 1163 | const end = Zld.findFirst(macho.relocation_info, relocs, start, Predicate{ .addr = start_addr }); | |
| 1224 | const start = MachO.findFirst(macho.relocation_info, relocs, 0, Predicate{ .addr = end_addr }); | |
| 1225 | const end = MachO.findFirst(macho.relocation_info, relocs, start, Predicate{ .addr = start_addr }); | |
| 1164 | 1226 | |
| 1165 | 1227 | return relocs[start..end]; |
| 1166 | 1228 | } |
src/link/MachO/Zld.zig deleted-3062| ... | ... | @@ -1,3062 +0,0 @@ |
| 1 | const Zld = @This(); | |
| 2 | ||
| 3 | const std = @import("std"); | |
| 4 | const assert = std.debug.assert; | |
| 5 | const leb = std.leb; | |
| 6 | const mem = std.mem; | |
| 7 | const meta = std.meta; | |
| 8 | const fs = std.fs; | |
| 9 | const macho = std.macho; | |
| 10 | const math = std.math; | |
| 11 | const log = std.log.scoped(.zld); | |
| 12 | const aarch64 = @import("../../codegen/aarch64.zig"); | |
| 13 | ||
| 14 | const Allocator = mem.Allocator; | |
| 15 | const Archive = @import("Archive.zig"); | |
| 16 | const CodeSignature = @import("CodeSignature.zig"); | |
| 17 | const Dylib = @import("Dylib.zig"); | |
| 18 | const Object = @import("Object.zig"); | |
| 19 | const TextBlock = @import("TextBlock.zig"); | |
| 20 | const Trie = @import("Trie.zig"); | |
| 21 | ||
| 22 | usingnamespace @import("commands.zig"); | |
| 23 | usingnamespace @import("bind.zig"); | |
| 24 | ||
| 25 | allocator: *Allocator, | |
| 26 | ||
| 27 | target: ?std.Target = null, | |
| 28 | page_size: ?u16 = null, | |
| 29 | file: ?fs.File = null, | |
| 30 | output: ?Output = null, | |
| 31 | ||
| 32 | // TODO these args will become obselete once Zld is coalesced with incremental | |
| 33 | // linker. | |
| 34 | stack_size: u64 = 0, | |
| 35 | ||
| 36 | objects: std.ArrayListUnmanaged(*Object) = .{}, | |
| 37 | archives: std.ArrayListUnmanaged(*Archive) = .{}, | |
| 38 | dylibs: std.ArrayListUnmanaged(*Dylib) = .{}, | |
| 39 | ||
| 40 | next_dylib_ordinal: u16 = 1, | |
| 41 | ||
| 42 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, | |
| 43 | ||
| 44 | pagezero_segment_cmd_index: ?u16 = null, | |
| 45 | text_segment_cmd_index: ?u16 = null, | |
| 46 | data_const_segment_cmd_index: ?u16 = null, | |
| 47 | data_segment_cmd_index: ?u16 = null, | |
| 48 | linkedit_segment_cmd_index: ?u16 = null, | |
| 49 | dyld_info_cmd_index: ?u16 = null, | |
| 50 | symtab_cmd_index: ?u16 = null, | |
| 51 | dysymtab_cmd_index: ?u16 = null, | |
| 52 | dylinker_cmd_index: ?u16 = null, | |
| 53 | data_in_code_cmd_index: ?u16 = null, | |
| 54 | function_starts_cmd_index: ?u16 = null, | |
| 55 | main_cmd_index: ?u16 = null, | |
| 56 | dylib_id_cmd_index: ?u16 = null, | |
| 57 | version_min_cmd_index: ?u16 = null, | |
| 58 | source_version_cmd_index: ?u16 = null, | |
| 59 | uuid_cmd_index: ?u16 = null, | |
| 60 | code_signature_cmd_index: ?u16 = null, | |
| 61 | ||
| 62 | // __TEXT segment sections | |
| 63 | text_section_index: ?u16 = null, | |
| 64 | stubs_section_index: ?u16 = null, | |
| 65 | stub_helper_section_index: ?u16 = null, | |
| 66 | text_const_section_index: ?u16 = null, | |
| 67 | cstring_section_index: ?u16 = null, | |
| 68 | ustring_section_index: ?u16 = null, | |
| 69 | gcc_except_tab_section_index: ?u16 = null, | |
| 70 | unwind_info_section_index: ?u16 = null, | |
| 71 | eh_frame_section_index: ?u16 = null, | |
| 72 | ||
| 73 | objc_methlist_section_index: ?u16 = null, | |
| 74 | objc_methname_section_index: ?u16 = null, | |
| 75 | objc_methtype_section_index: ?u16 = null, | |
| 76 | objc_classname_section_index: ?u16 = null, | |
| 77 | ||
| 78 | // __DATA_CONST segment sections | |
| 79 | got_section_index: ?u16 = null, | |
| 80 | mod_init_func_section_index: ?u16 = null, | |
| 81 | mod_term_func_section_index: ?u16 = null, | |
| 82 | data_const_section_index: ?u16 = null, | |
| 83 | ||
| 84 | objc_cfstring_section_index: ?u16 = null, | |
| 85 | objc_classlist_section_index: ?u16 = null, | |
| 86 | objc_imageinfo_section_index: ?u16 = null, | |
| 87 | ||
| 88 | // __DATA segment sections | |
| 89 | tlv_section_index: ?u16 = null, | |
| 90 | tlv_data_section_index: ?u16 = null, | |
| 91 | tlv_bss_section_index: ?u16 = null, | |
| 92 | la_symbol_ptr_section_index: ?u16 = null, | |
| 93 | data_section_index: ?u16 = null, | |
| 94 | bss_section_index: ?u16 = null, | |
| 95 | common_section_index: ?u16 = null, | |
| 96 | ||
| 97 | objc_const_section_index: ?u16 = null, | |
| 98 | objc_selrefs_section_index: ?u16 = null, | |
| 99 | objc_classrefs_section_index: ?u16 = null, | |
| 100 | objc_data_section_index: ?u16 = null, | |
| 101 | ||
| 102 | locals: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | |
| 103 | globals: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | |
| 104 | imports: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | |
| 105 | undefs: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | |
| 106 | tentatives: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | |
| 107 | symbol_resolver: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{}, | |
| 108 | ||
| 109 | strtab: std.ArrayListUnmanaged(u8) = .{}, | |
| 110 | ||
| 111 | stubs: std.AutoArrayHashMapUnmanaged(u32, void) = .{}, | |
| 112 | got_entries: std.AutoArrayHashMapUnmanaged(GotIndirectionKey, void) = .{}, | |
| 113 | ||
| 114 | stub_helper_stubs_start_off: ?u64 = null, | |
| 115 | ||
| 116 | blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{}, | |
| 117 | ||
| 118 | has_dices: bool = false, | |
| 119 | has_stabs: bool = false, | |
| 120 | ||
| 121 | const SymbolWithLoc = struct { | |
| 122 | // Table where the symbol can be found. | |
| 123 | where: enum { | |
| 124 | global, | |
| 125 | import, | |
| 126 | undef, | |
| 127 | tentative, | |
| 128 | }, | |
| 129 | where_index: u32, | |
| 130 | local_sym_index: u32 = 0, | |
| 131 | file: u16 = 0, | |
| 132 | }; | |
| 133 | ||
| 134 | pub const GotIndirectionKey = struct { | |
| 135 | where: enum { | |
| 136 | local, | |
| 137 | import, | |
| 138 | }, | |
| 139 | where_index: u32, | |
| 140 | }; | |
| 141 | ||
| 142 | pub const Output = struct { | |
| 143 | tag: enum { exe, dylib }, | |
| 144 | path: []const u8, | |
| 145 | install_name: ?[]const u8 = null, | |
| 146 | }; | |
| 147 | ||
| 148 | /// Default path to dyld | |
| 149 | const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld"; | |
| 150 | ||
| 151 | pub fn init(allocator: *Allocator) !Zld { | |
| 152 | return Zld{ .allocator = allocator }; | |
| 153 | } | |
| 154 | ||
| 155 | pub fn deinit(self: *Zld) void { | |
| 156 | self.stubs.deinit(self.allocator); | |
| 157 | self.got_entries.deinit(self.allocator); | |
| 158 | ||
| 159 | for (self.load_commands.items) |*lc| { | |
| 160 | lc.deinit(self.allocator); | |
| 161 | } | |
| 162 | self.load_commands.deinit(self.allocator); | |
| 163 | ||
| 164 | for (self.objects.items) |object| { | |
| 165 | object.deinit(); | |
| 166 | self.allocator.destroy(object); | |
| 167 | } | |
| 168 | self.objects.deinit(self.allocator); | |
| 169 | ||
| 170 | for (self.archives.items) |archive| { | |
| 171 | archive.deinit(); | |
| 172 | self.allocator.destroy(archive); | |
| 173 | } | |
| 174 | self.archives.deinit(self.allocator); | |
| 175 | ||
| 176 | for (self.dylibs.items) |dylib| { | |
| 177 | dylib.deinit(); | |
| 178 | self.allocator.destroy(dylib); | |
| 179 | } | |
| 180 | self.dylibs.deinit(self.allocator); | |
| 181 | ||
| 182 | self.locals.deinit(self.allocator); | |
| 183 | self.globals.deinit(self.allocator); | |
| 184 | self.imports.deinit(self.allocator); | |
| 185 | self.undefs.deinit(self.allocator); | |
| 186 | self.tentatives.deinit(self.allocator); | |
| 187 | ||
| 188 | for (self.symbol_resolver.keys()) |key| { | |
| 189 | self.allocator.free(key); | |
| 190 | } | |
| 191 | self.symbol_resolver.deinit(self.allocator); | |
| 192 | ||
| 193 | self.strtab.deinit(self.allocator); | |
| 194 | ||
| 195 | // TODO dealloc all blocks | |
| 196 | self.blocks.deinit(self.allocator); | |
| 197 | } | |
| 198 | ||
| 199 | pub fn closeFiles(self: Zld) void { | |
| 200 | for (self.objects.items) |object| { | |
| 201 | object.closeFile(); | |
| 202 | } | |
| 203 | for (self.archives.items) |archive| { | |
| 204 | archive.closeFile(); | |
| 205 | } | |
| 206 | if (self.file) |f| f.close(); | |
| 207 | } | |
| 208 | ||
| 209 | const LinkArgs = struct { | |
| 210 | syslibroot: ?[]const u8, | |
| 211 | libs: []const []const u8, | |
| 212 | rpaths: []const []const u8, | |
| 213 | }; | |
| 214 | ||
| 215 | pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArgs) !void { | |
| 216 | if (files.len == 0) return error.NoInputFiles; | |
| 217 | if (output.path.len == 0) return error.EmptyOutputPath; | |
| 218 | ||
| 219 | self.page_size = switch (self.target.?.cpu.arch) { | |
| 220 | .aarch64 => 0x4000, | |
| 221 | .x86_64 => 0x1000, | |
| 222 | else => unreachable, | |
| 223 | }; | |
| 224 | self.output = output; | |
| 225 | self.file = try fs.cwd().createFile(self.output.?.path, .{ | |
| 226 | .truncate = true, | |
| 227 | .read = true, | |
| 228 | .mode = if (std.Target.current.os.tag == .windows) 0 else 0o777, | |
| 229 | }); | |
| 230 | ||
| 231 | try self.populateMetadata(); | |
| 232 | try self.parseInputFiles(files, args.syslibroot); | |
| 233 | try self.parseLibs(args.libs, args.syslibroot); | |
| 234 | try self.resolveSymbols(); | |
| 235 | try self.parseTextBlocks(); | |
| 236 | ||
| 237 | { | |
| 238 | // Add dyld_stub_binder as the final GOT entry. | |
| 239 | const resolv = self.symbol_resolver.get("dyld_stub_binder") orelse unreachable; | |
| 240 | try self.got_entries.putNoClobber(self.allocator, .{ | |
| 241 | .where = .import, | |
| 242 | .where_index = resolv.where_index, | |
| 243 | }, {}); | |
| 244 | } | |
| 245 | ||
| 246 | try self.sortSections(); | |
| 247 | try self.addRpaths(args.rpaths); | |
| 248 | try self.addDataInCodeLC(); | |
| 249 | try self.addCodeSignatureLC(); | |
| 250 | try self.allocateTextSegment(); | |
| 251 | try self.allocateDataConstSegment(); | |
| 252 | try self.allocateDataSegment(); | |
| 253 | self.allocateLinkeditSegment(); | |
| 254 | try self.allocateTextBlocks(); | |
| 255 | ||
| 256 | // log.warn("locals", .{}); | |
| 257 | // for (self.locals.items) |sym, id| { | |
| 258 | // log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym }); | |
| 259 | // } | |
| 260 | ||
| 261 | // log.warn("globals", .{}); | |
| 262 | // for (self.globals.items) |sym, id| { | |
| 263 | // log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym }); | |
| 264 | // } | |
| 265 | ||
| 266 | // log.warn("tentatives", .{}); | |
| 267 | // for (self.tentatives.items) |sym, id| { | |
| 268 | // log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym }); | |
| 269 | // } | |
| 270 | ||
| 271 | // log.warn("undefines", .{}); | |
| 272 | // for (self.undefs.items) |sym, id| { | |
| 273 | // log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym }); | |
| 274 | // } | |
| 275 | ||
| 276 | // log.warn("imports", .{}); | |
| 277 | // for (self.imports.items) |sym, id| { | |
| 278 | // log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym }); | |
| 279 | // } | |
| 280 | ||
| 281 | // log.warn("symbol resolver", .{}); | |
| 282 | // for (self.symbol_resolver.keys()) |key| { | |
| 283 | // log.warn(" {s} => {}", .{ key, self.symbol_resolver.get(key).? }); | |
| 284 | // } | |
| 285 | ||
| 286 | // log.warn("mappings", .{}); | |
| 287 | // for (self.objects.items) |object, id| { | |
| 288 | // const object_id = @intCast(u16, id); | |
| 289 | // log.warn(" in object {s}", .{object.name.?}); | |
| 290 | // for (object.symtab.items) |sym, sym_id| { | |
| 291 | // if (object.symbol_mapping.get(@intCast(u32, sym_id))) |local_id| { | |
| 292 | // log.warn(" | {d} => {d}", .{ sym_id, local_id }); | |
| 293 | // } else { | |
| 294 | // log.warn(" | {d} no local mapping for {s}", .{ sym_id, object.getString(sym.n_strx) }); | |
| 295 | // } | |
| 296 | // } | |
| 297 | // } | |
| 298 | ||
| 299 | // var it = self.blocks.iterator(); | |
| 300 | // while (it.next()) |entry| { | |
| 301 | // const seg = self.load_commands.items[entry.key_ptr.seg].Segment; | |
| 302 | // const sect = seg.sections.items[entry.key_ptr.sect]; | |
| 303 | ||
| 304 | // log.warn("\n\n{s},{s} contents:", .{ segmentName(sect), sectionName(sect) }); | |
| 305 | // log.warn(" {}", .{sect}); | |
| 306 | // entry.value_ptr.*.print(self); | |
| 307 | // } | |
| 308 | ||
| 309 | try self.flush(); | |
| 310 | } | |
| 311 | ||
| 312 | fn parseInputFiles(self: *Zld, files: []const []const u8, syslibroot: ?[]const u8) !void { | |
| 313 | for (files) |file_name| { | |
| 314 | const full_path = full_path: { | |
| 315 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; | |
| 316 | const path = try std.fs.realpath(file_name, &buffer); | |
| 317 | break :full_path try self.allocator.dupe(u8, path); | |
| 318 | }; | |
| 319 | ||
| 320 | if (try Object.createAndParseFromPath(self.allocator, self.target.?.cpu.arch, full_path)) |object| { | |
| 321 | try self.objects.append(self.allocator, object); | |
| 322 | continue; | |
| 323 | } | |
| 324 | ||
| 325 | if (try Archive.createAndParseFromPath(self.allocator, self.target.?.cpu.arch, full_path)) |archive| { | |
| 326 | try self.archives.append(self.allocator, archive); | |
| 327 | continue; | |
| 328 | } | |
| 329 | ||
| 330 | if (try Dylib.createAndParseFromPath( | |
| 331 | self.allocator, | |
| 332 | self.target.?.cpu.arch, | |
| 333 | full_path, | |
| 334 | .{ .syslibroot = syslibroot }, | |
| 335 | )) |dylibs| { | |
| 336 | defer self.allocator.free(dylibs); | |
| 337 | try self.dylibs.appendSlice(self.allocator, dylibs); | |
| 338 | continue; | |
| 339 | } | |
| 340 | ||
| 341 | log.warn("unknown filetype for positional input file: '{s}'", .{file_name}); | |
| 342 | } | |
| 343 | } | |
| 344 | ||
| 345 | fn parseLibs(self: *Zld, libs: []const []const u8, syslibroot: ?[]const u8) !void { | |
| 346 | for (libs) |lib| { | |
| 347 | if (try Dylib.createAndParseFromPath( | |
| 348 | self.allocator, | |
| 349 | self.target.?.cpu.arch, | |
| 350 | lib, | |
| 351 | .{ .syslibroot = syslibroot }, | |
| 352 | )) |dylibs| { | |
| 353 | defer self.allocator.free(dylibs); | |
| 354 | try self.dylibs.appendSlice(self.allocator, dylibs); | |
| 355 | continue; | |
| 356 | } | |
| 357 | ||
| 358 | if (try Archive.createAndParseFromPath(self.allocator, self.target.?.cpu.arch, lib)) |archive| { | |
| 359 | try self.archives.append(self.allocator, archive); | |
| 360 | continue; | |
| 361 | } | |
| 362 | ||
| 363 | log.warn("unknown filetype for a library: '{s}'", .{lib}); | |
| 364 | } | |
| 365 | } | |
| 366 | ||
| 367 | pub const MatchingSection = struct { | |
| 368 | seg: u16, | |
| 369 | sect: u16, | |
| 370 | }; | |
| 371 | ||
| 372 | pub fn getMatchingSection(self: *Zld, sect: macho.section_64) !?MatchingSection { | |
| 373 | const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 374 | const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 375 | const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 376 | const segname = segmentName(sect); | |
| 377 | const sectname = sectionName(sect); | |
| 378 | ||
| 379 | const res: ?MatchingSection = blk: { | |
| 380 | switch (sectionType(sect)) { | |
| 381 | macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => { | |
| 382 | if (self.text_const_section_index == null) { | |
| 383 | self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 384 | try text_seg.addSection(self.allocator, "__const", .{}); | |
| 385 | } | |
| 386 | ||
| 387 | break :blk .{ | |
| 388 | .seg = self.text_segment_cmd_index.?, | |
| 389 | .sect = self.text_const_section_index.?, | |
| 390 | }; | |
| 391 | }, | |
| 392 | macho.S_CSTRING_LITERALS => { | |
| 393 | if (mem.eql(u8, sectname, "__objc_methname")) { | |
| 394 | // TODO it seems the common values within the sections in objects are deduplicated/merged | |
| 395 | // on merging the sections' contents. | |
| 396 | if (self.objc_methname_section_index == null) { | |
| 397 | self.objc_methname_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 398 | try text_seg.addSection(self.allocator, "__objc_methname", .{ | |
| 399 | .flags = macho.S_CSTRING_LITERALS, | |
| 400 | }); | |
| 401 | } | |
| 402 | ||
| 403 | break :blk .{ | |
| 404 | .seg = self.text_segment_cmd_index.?, | |
| 405 | .sect = self.objc_methname_section_index.?, | |
| 406 | }; | |
| 407 | } else if (mem.eql(u8, sectname, "__objc_methtype")) { | |
| 408 | if (self.objc_methtype_section_index == null) { | |
| 409 | self.objc_methtype_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 410 | try text_seg.addSection(self.allocator, "__objc_methtype", .{ | |
| 411 | .flags = macho.S_CSTRING_LITERALS, | |
| 412 | }); | |
| 413 | } | |
| 414 | ||
| 415 | break :blk .{ | |
| 416 | .seg = self.text_segment_cmd_index.?, | |
| 417 | .sect = self.objc_methtype_section_index.?, | |
| 418 | }; | |
| 419 | } else if (mem.eql(u8, sectname, "__objc_classname")) { | |
| 420 | if (self.objc_classname_section_index == null) { | |
| 421 | self.objc_classname_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 422 | try text_seg.addSection(self.allocator, "__objc_classname", .{}); | |
| 423 | } | |
| 424 | ||
| 425 | break :blk .{ | |
| 426 | .seg = self.text_segment_cmd_index.?, | |
| 427 | .sect = self.objc_classname_section_index.?, | |
| 428 | }; | |
| 429 | } | |
| 430 | ||
| 431 | if (self.cstring_section_index == null) { | |
| 432 | self.cstring_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 433 | try text_seg.addSection(self.allocator, "__cstring", .{ | |
| 434 | .flags = macho.S_CSTRING_LITERALS, | |
| 435 | }); | |
| 436 | } | |
| 437 | ||
| 438 | break :blk .{ | |
| 439 | .seg = self.text_segment_cmd_index.?, | |
| 440 | .sect = self.cstring_section_index.?, | |
| 441 | }; | |
| 442 | }, | |
| 443 | macho.S_LITERAL_POINTERS => { | |
| 444 | if (mem.eql(u8, segname, "__DATA") and mem.eql(u8, sectname, "__objc_selrefs")) { | |
| 445 | if (self.objc_selrefs_section_index == null) { | |
| 446 | self.objc_selrefs_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 447 | try data_seg.addSection(self.allocator, "__objc_selrefs", .{ | |
| 448 | .flags = macho.S_LITERAL_POINTERS, | |
| 449 | }); | |
| 450 | } | |
| 451 | ||
| 452 | break :blk .{ | |
| 453 | .seg = self.data_segment_cmd_index.?, | |
| 454 | .sect = self.objc_selrefs_section_index.?, | |
| 455 | }; | |
| 456 | } | |
| 457 | ||
| 458 | // TODO investigate | |
| 459 | break :blk null; | |
| 460 | }, | |
| 461 | macho.S_MOD_INIT_FUNC_POINTERS => { | |
| 462 | if (self.mod_init_func_section_index == null) { | |
| 463 | self.mod_init_func_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 464 | try data_const_seg.addSection(self.allocator, "__mod_init_func", .{ | |
| 465 | .flags = macho.S_MOD_INIT_FUNC_POINTERS, | |
| 466 | }); | |
| 467 | } | |
| 468 | ||
| 469 | break :blk .{ | |
| 470 | .seg = self.data_const_segment_cmd_index.?, | |
| 471 | .sect = self.mod_init_func_section_index.?, | |
| 472 | }; | |
| 473 | }, | |
| 474 | macho.S_MOD_TERM_FUNC_POINTERS => { | |
| 475 | if (self.mod_term_func_section_index == null) { | |
| 476 | self.mod_term_func_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 477 | try data_const_seg.addSection(self.allocator, "__mod_term_func", .{ | |
| 478 | .flags = macho.S_MOD_TERM_FUNC_POINTERS, | |
| 479 | }); | |
| 480 | } | |
| 481 | ||
| 482 | break :blk .{ | |
| 483 | .seg = self.data_const_segment_cmd_index.?, | |
| 484 | .sect = self.mod_term_func_section_index.?, | |
| 485 | }; | |
| 486 | }, | |
| 487 | macho.S_ZEROFILL => { | |
| 488 | if (mem.eql(u8, sectname, "__common")) { | |
| 489 | if (self.common_section_index == null) { | |
| 490 | self.common_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 491 | try data_seg.addSection(self.allocator, "__common", .{ | |
| 492 | .flags = macho.S_ZEROFILL, | |
| 493 | }); | |
| 494 | } | |
| 495 | ||
| 496 | break :blk .{ | |
| 497 | .seg = self.data_segment_cmd_index.?, | |
| 498 | .sect = self.common_section_index.?, | |
| 499 | }; | |
| 500 | } else { | |
| 501 | if (self.bss_section_index == null) { | |
| 502 | self.bss_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 503 | try data_seg.addSection(self.allocator, "__bss", .{ | |
| 504 | .flags = macho.S_ZEROFILL, | |
| 505 | }); | |
| 506 | } | |
| 507 | ||
| 508 | break :blk .{ | |
| 509 | .seg = self.data_segment_cmd_index.?, | |
| 510 | .sect = self.bss_section_index.?, | |
| 511 | }; | |
| 512 | } | |
| 513 | }, | |
| 514 | macho.S_THREAD_LOCAL_VARIABLES => { | |
| 515 | if (self.tlv_section_index == null) { | |
| 516 | self.tlv_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 517 | try data_seg.addSection(self.allocator, "__thread_vars", .{ | |
| 518 | .flags = macho.S_THREAD_LOCAL_VARIABLES, | |
| 519 | }); | |
| 520 | } | |
| 521 | ||
| 522 | break :blk .{ | |
| 523 | .seg = self.data_segment_cmd_index.?, | |
| 524 | .sect = self.tlv_section_index.?, | |
| 525 | }; | |
| 526 | }, | |
| 527 | macho.S_THREAD_LOCAL_REGULAR => { | |
| 528 | if (self.tlv_data_section_index == null) { | |
| 529 | self.tlv_data_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 530 | try data_seg.addSection(self.allocator, "__thread_data", .{ | |
| 531 | .flags = macho.S_THREAD_LOCAL_REGULAR, | |
| 532 | }); | |
| 533 | } | |
| 534 | ||
| 535 | break :blk .{ | |
| 536 | .seg = self.data_segment_cmd_index.?, | |
| 537 | .sect = self.tlv_data_section_index.?, | |
| 538 | }; | |
| 539 | }, | |
| 540 | macho.S_THREAD_LOCAL_ZEROFILL => { | |
| 541 | if (self.tlv_bss_section_index == null) { | |
| 542 | self.tlv_bss_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 543 | try data_seg.addSection(self.allocator, "__thread_bss", .{ | |
| 544 | .flags = macho.S_THREAD_LOCAL_ZEROFILL, | |
| 545 | }); | |
| 546 | } | |
| 547 | ||
| 548 | break :blk .{ | |
| 549 | .seg = self.data_segment_cmd_index.?, | |
| 550 | .sect = self.tlv_bss_section_index.?, | |
| 551 | }; | |
| 552 | }, | |
| 553 | macho.S_COALESCED => { | |
| 554 | if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) { | |
| 555 | // TODO I believe __eh_frame is currently part of __unwind_info section | |
| 556 | // in the latest ld64 output. | |
| 557 | if (self.eh_frame_section_index == null) { | |
| 558 | self.eh_frame_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 559 | try text_seg.addSection(self.allocator, "__eh_frame", .{}); | |
| 560 | } | |
| 561 | ||
| 562 | break :blk .{ | |
| 563 | .seg = self.text_segment_cmd_index.?, | |
| 564 | .sect = self.eh_frame_section_index.?, | |
| 565 | }; | |
| 566 | } | |
| 567 | ||
| 568 | // TODO audit this: is this the right mapping? | |
| 569 | if (self.data_const_section_index == null) { | |
| 570 | self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 571 | try data_const_seg.addSection(self.allocator, "__const", .{}); | |
| 572 | } | |
| 573 | ||
| 574 | break :blk .{ | |
| 575 | .seg = self.data_const_segment_cmd_index.?, | |
| 576 | .sect = self.data_const_section_index.?, | |
| 577 | }; | |
| 578 | }, | |
| 579 | macho.S_REGULAR => { | |
| 580 | if (sectionIsCode(sect)) { | |
| 581 | if (self.text_section_index == null) { | |
| 582 | self.text_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 583 | try text_seg.addSection(self.allocator, "__text", .{ | |
| 584 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, | |
| 585 | }); | |
| 586 | } | |
| 587 | ||
| 588 | break :blk .{ | |
| 589 | .seg = self.text_segment_cmd_index.?, | |
| 590 | .sect = self.text_section_index.?, | |
| 591 | }; | |
| 592 | } | |
| 593 | if (sectionIsDebug(sect)) { | |
| 594 | // TODO debug attributes | |
| 595 | if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) { | |
| 596 | log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{ | |
| 597 | sect.flags, segname, sectname, | |
| 598 | }); | |
| 599 | } | |
| 600 | break :blk null; | |
| 601 | } | |
| 602 | ||
| 603 | if (mem.eql(u8, segname, "__TEXT")) { | |
| 604 | if (mem.eql(u8, sectname, "__ustring")) { | |
| 605 | if (self.ustring_section_index == null) { | |
| 606 | self.ustring_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 607 | try text_seg.addSection(self.allocator, "__ustring", .{}); | |
| 608 | } | |
| 609 | ||
| 610 | break :blk .{ | |
| 611 | .seg = self.text_segment_cmd_index.?, | |
| 612 | .sect = self.ustring_section_index.?, | |
| 613 | }; | |
| 614 | } else if (mem.eql(u8, sectname, "__gcc_except_tab")) { | |
| 615 | if (self.gcc_except_tab_section_index == null) { | |
| 616 | self.gcc_except_tab_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 617 | try text_seg.addSection(self.allocator, "__gcc_except_tab", .{}); | |
| 618 | } | |
| 619 | ||
| 620 | break :blk .{ | |
| 621 | .seg = self.text_segment_cmd_index.?, | |
| 622 | .sect = self.gcc_except_tab_section_index.?, | |
| 623 | }; | |
| 624 | } else if (mem.eql(u8, sectname, "__objc_methlist")) { | |
| 625 | if (self.objc_methlist_section_index == null) { | |
| 626 | self.objc_methlist_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 627 | try text_seg.addSection(self.allocator, "__objc_methlist", .{}); | |
| 628 | } | |
| 629 | ||
| 630 | break :blk .{ | |
| 631 | .seg = self.text_segment_cmd_index.?, | |
| 632 | .sect = self.objc_methlist_section_index.?, | |
| 633 | }; | |
| 634 | } else if (mem.eql(u8, sectname, "__rodata") or | |
| 635 | mem.eql(u8, sectname, "__typelink") or | |
| 636 | mem.eql(u8, sectname, "__itablink") or | |
| 637 | mem.eql(u8, sectname, "__gosymtab") or | |
| 638 | mem.eql(u8, sectname, "__gopclntab")) | |
| 639 | { | |
| 640 | if (self.data_const_section_index == null) { | |
| 641 | self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 642 | try data_const_seg.addSection(self.allocator, "__const", .{}); | |
| 643 | } | |
| 644 | ||
| 645 | break :blk .{ | |
| 646 | .seg = self.data_const_segment_cmd_index.?, | |
| 647 | .sect = self.data_const_section_index.?, | |
| 648 | }; | |
| 649 | } else { | |
| 650 | if (self.text_const_section_index == null) { | |
| 651 | self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 652 | try text_seg.addSection(self.allocator, "__const", .{}); | |
| 653 | } | |
| 654 | ||
| 655 | break :blk .{ | |
| 656 | .seg = self.text_segment_cmd_index.?, | |
| 657 | .sect = self.text_const_section_index.?, | |
| 658 | }; | |
| 659 | } | |
| 660 | } | |
| 661 | ||
| 662 | if (mem.eql(u8, segname, "__DATA_CONST")) { | |
| 663 | if (self.data_const_section_index == null) { | |
| 664 | self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 665 | try data_const_seg.addSection(self.allocator, "__const", .{}); | |
| 666 | } | |
| 667 | ||
| 668 | break :blk .{ | |
| 669 | .seg = self.data_const_segment_cmd_index.?, | |
| 670 | .sect = self.data_const_section_index.?, | |
| 671 | }; | |
| 672 | } | |
| 673 | ||
| 674 | if (mem.eql(u8, segname, "__DATA")) { | |
| 675 | if (mem.eql(u8, sectname, "__const")) { | |
| 676 | if (self.data_const_section_index == null) { | |
| 677 | self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 678 | try data_const_seg.addSection(self.allocator, "__const", .{}); | |
| 679 | } | |
| 680 | ||
| 681 | break :blk .{ | |
| 682 | .seg = self.data_const_segment_cmd_index.?, | |
| 683 | .sect = self.data_const_section_index.?, | |
| 684 | }; | |
| 685 | } else if (mem.eql(u8, sectname, "__cfstring")) { | |
| 686 | if (self.objc_cfstring_section_index == null) { | |
| 687 | self.objc_cfstring_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 688 | try data_const_seg.addSection(self.allocator, "__cfstring", .{}); | |
| 689 | } | |
| 690 | ||
| 691 | break :blk .{ | |
| 692 | .seg = self.data_const_segment_cmd_index.?, | |
| 693 | .sect = self.objc_cfstring_section_index.?, | |
| 694 | }; | |
| 695 | } else if (mem.eql(u8, sectname, "__objc_classlist")) { | |
| 696 | if (self.objc_classlist_section_index == null) { | |
| 697 | self.objc_classlist_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 698 | try data_const_seg.addSection(self.allocator, "__objc_classlist", .{}); | |
| 699 | } | |
| 700 | ||
| 701 | break :blk .{ | |
| 702 | .seg = self.data_const_segment_cmd_index.?, | |
| 703 | .sect = self.objc_classlist_section_index.?, | |
| 704 | }; | |
| 705 | } else if (mem.eql(u8, sectname, "__objc_imageinfo")) { | |
| 706 | if (self.objc_imageinfo_section_index == null) { | |
| 707 | self.objc_imageinfo_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 708 | try data_const_seg.addSection(self.allocator, "__objc_imageinfo", .{}); | |
| 709 | } | |
| 710 | ||
| 711 | break :blk .{ | |
| 712 | .seg = self.data_const_segment_cmd_index.?, | |
| 713 | .sect = self.objc_imageinfo_section_index.?, | |
| 714 | }; | |
| 715 | } else if (mem.eql(u8, sectname, "__objc_const")) { | |
| 716 | if (self.objc_const_section_index == null) { | |
| 717 | self.objc_const_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 718 | try data_seg.addSection(self.allocator, "__objc_const", .{}); | |
| 719 | } | |
| 720 | ||
| 721 | break :blk .{ | |
| 722 | .seg = self.data_segment_cmd_index.?, | |
| 723 | .sect = self.objc_const_section_index.?, | |
| 724 | }; | |
| 725 | } else if (mem.eql(u8, sectname, "__objc_classrefs")) { | |
| 726 | if (self.objc_classrefs_section_index == null) { | |
| 727 | self.objc_classrefs_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 728 | try data_seg.addSection(self.allocator, "__objc_classrefs", .{}); | |
| 729 | } | |
| 730 | ||
| 731 | break :blk .{ | |
| 732 | .seg = self.data_segment_cmd_index.?, | |
| 733 | .sect = self.objc_classrefs_section_index.?, | |
| 734 | }; | |
| 735 | } else if (mem.eql(u8, sectname, "__objc_data")) { | |
| 736 | if (self.objc_data_section_index == null) { | |
| 737 | self.objc_data_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 738 | try data_seg.addSection(self.allocator, "__objc_data", .{}); | |
| 739 | } | |
| 740 | ||
| 741 | break :blk .{ | |
| 742 | .seg = self.data_segment_cmd_index.?, | |
| 743 | .sect = self.objc_data_section_index.?, | |
| 744 | }; | |
| 745 | } else { | |
| 746 | if (self.data_section_index == null) { | |
| 747 | self.data_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 748 | try data_seg.addSection(self.allocator, "__data", .{}); | |
| 749 | } | |
| 750 | ||
| 751 | break :blk .{ | |
| 752 | .seg = self.data_segment_cmd_index.?, | |
| 753 | .sect = self.data_section_index.?, | |
| 754 | }; | |
| 755 | } | |
| 756 | } | |
| 757 | ||
| 758 | if (mem.eql(u8, "__LLVM", segname) and mem.eql(u8, "__asm", sectname)) { | |
| 759 | log.debug("TODO LLVM asm section: type 0x{x}, name '{s},{s}'", .{ | |
| 760 | sect.flags, segname, sectname, | |
| 761 | }); | |
| 762 | } | |
| 763 | ||
| 764 | break :blk null; | |
| 765 | }, | |
| 766 | else => break :blk null, | |
| 767 | } | |
| 768 | }; | |
| 769 | ||
| 770 | return res; | |
| 771 | } | |
| 772 | ||
| 773 | fn sortSections(self: *Zld) !void { | |
| 774 | var text_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator); | |
| 775 | defer text_index_mapping.deinit(); | |
| 776 | var data_const_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator); | |
| 777 | defer data_const_index_mapping.deinit(); | |
| 778 | var data_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator); | |
| 779 | defer data_index_mapping.deinit(); | |
| 780 | ||
| 781 | { | |
| 782 | // __TEXT segment | |
| 783 | const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 784 | var sections = seg.sections.toOwnedSlice(self.allocator); | |
| 785 | defer self.allocator.free(sections); | |
| 786 | try seg.sections.ensureCapacity(self.allocator, sections.len); | |
| 787 | ||
| 788 | const indices = &[_]*?u16{ | |
| 789 | &self.text_section_index, | |
| 790 | &self.stubs_section_index, | |
| 791 | &self.stub_helper_section_index, | |
| 792 | &self.gcc_except_tab_section_index, | |
| 793 | &self.cstring_section_index, | |
| 794 | &self.ustring_section_index, | |
| 795 | &self.text_const_section_index, | |
| 796 | &self.objc_methname_section_index, | |
| 797 | &self.objc_methtype_section_index, | |
| 798 | &self.objc_classname_section_index, | |
| 799 | &self.eh_frame_section_index, | |
| 800 | }; | |
| 801 | for (indices) |maybe_index| { | |
| 802 | const new_index: u16 = if (maybe_index.*) |index| blk: { | |
| 803 | const idx = @intCast(u16, seg.sections.items.len); | |
| 804 | seg.sections.appendAssumeCapacity(sections[index]); | |
| 805 | try text_index_mapping.putNoClobber(index, idx); | |
| 806 | break :blk idx; | |
| 807 | } else continue; | |
| 808 | maybe_index.* = new_index; | |
| 809 | } | |
| 810 | } | |
| 811 | ||
| 812 | { | |
| 813 | // __DATA_CONST segment | |
| 814 | const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 815 | var sections = seg.sections.toOwnedSlice(self.allocator); | |
| 816 | defer self.allocator.free(sections); | |
| 817 | try seg.sections.ensureCapacity(self.allocator, sections.len); | |
| 818 | ||
| 819 | const indices = &[_]*?u16{ | |
| 820 | &self.got_section_index, | |
| 821 | &self.mod_init_func_section_index, | |
| 822 | &self.mod_term_func_section_index, | |
| 823 | &self.data_const_section_index, | |
| 824 | &self.objc_cfstring_section_index, | |
| 825 | &self.objc_classlist_section_index, | |
| 826 | &self.objc_imageinfo_section_index, | |
| 827 | }; | |
| 828 | for (indices) |maybe_index| { | |
| 829 | const new_index: u16 = if (maybe_index.*) |index| blk: { | |
| 830 | const idx = @intCast(u16, seg.sections.items.len); | |
| 831 | seg.sections.appendAssumeCapacity(sections[index]); | |
| 832 | try data_const_index_mapping.putNoClobber(index, idx); | |
| 833 | break :blk idx; | |
| 834 | } else continue; | |
| 835 | maybe_index.* = new_index; | |
| 836 | } | |
| 837 | } | |
| 838 | ||
| 839 | { | |
| 840 | // __DATA segment | |
| 841 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 842 | var sections = seg.sections.toOwnedSlice(self.allocator); | |
| 843 | defer self.allocator.free(sections); | |
| 844 | try seg.sections.ensureCapacity(self.allocator, sections.len); | |
| 845 | ||
| 846 | // __DATA segment | |
| 847 | const indices = &[_]*?u16{ | |
| 848 | &self.la_symbol_ptr_section_index, | |
| 849 | &self.objc_const_section_index, | |
| 850 | &self.objc_selrefs_section_index, | |
| 851 | &self.objc_classrefs_section_index, | |
| 852 | &self.objc_data_section_index, | |
| 853 | &self.data_section_index, | |
| 854 | &self.tlv_section_index, | |
| 855 | &self.tlv_data_section_index, | |
| 856 | &self.tlv_bss_section_index, | |
| 857 | &self.bss_section_index, | |
| 858 | &self.common_section_index, | |
| 859 | }; | |
| 860 | for (indices) |maybe_index| { | |
| 861 | const new_index: u16 = if (maybe_index.*) |index| blk: { | |
| 862 | const idx = @intCast(u16, seg.sections.items.len); | |
| 863 | seg.sections.appendAssumeCapacity(sections[index]); | |
| 864 | try data_index_mapping.putNoClobber(index, idx); | |
| 865 | break :blk idx; | |
| 866 | } else continue; | |
| 867 | maybe_index.* = new_index; | |
| 868 | } | |
| 869 | } | |
| 870 | ||
| 871 | { | |
| 872 | var transient: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{}; | |
| 873 | try transient.ensureCapacity(self.allocator, self.blocks.count()); | |
| 874 | ||
| 875 | var it = self.blocks.iterator(); | |
| 876 | while (it.next()) |entry| { | |
| 877 | const old = entry.key_ptr.*; | |
| 878 | const sect = if (old.seg == self.text_segment_cmd_index.?) | |
| 879 | text_index_mapping.get(old.sect).? | |
| 880 | else if (old.seg == self.data_const_segment_cmd_index.?) | |
| 881 | data_const_index_mapping.get(old.sect).? | |
| 882 | else | |
| 883 | data_index_mapping.get(old.sect).?; | |
| 884 | transient.putAssumeCapacityNoClobber(.{ | |
| 885 | .seg = old.seg, | |
| 886 | .sect = sect, | |
| 887 | }, entry.value_ptr.*); | |
| 888 | } | |
| 889 | ||
| 890 | self.blocks.clearAndFree(self.allocator); | |
| 891 | self.blocks.deinit(self.allocator); | |
| 892 | self.blocks = transient; | |
| 893 | } | |
| 894 | } | |
| 895 | ||
| 896 | fn allocateTextSegment(self: *Zld) !void { | |
| 897 | const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 898 | const nstubs = @intCast(u32, self.stubs.count()); | |
| 899 | ||
| 900 | const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize; | |
| 901 | seg.inner.fileoff = 0; | |
| 902 | seg.inner.vmaddr = base_vmaddr; | |
| 903 | ||
| 904 | // Set stubs and stub_helper sizes | |
| 905 | const stubs = &seg.sections.items[self.stubs_section_index.?]; | |
| 906 | const stub_helper = &seg.sections.items[self.stub_helper_section_index.?]; | |
| 907 | stubs.size += nstubs * stubs.reserved2; | |
| 908 | ||
| 909 | const stub_size: u4 = switch (self.target.?.cpu.arch) { | |
| 910 | .x86_64 => 10, | |
| 911 | .aarch64 => 3 * @sizeOf(u32), | |
| 912 | else => unreachable, | |
| 913 | }; | |
| 914 | stub_helper.size += nstubs * stub_size; | |
| 915 | ||
| 916 | var sizeofcmds: u64 = 0; | |
| 917 | for (self.load_commands.items) |lc| { | |
| 918 | sizeofcmds += lc.cmdsize(); | |
| 919 | } | |
| 920 | ||
| 921 | try self.allocateSegment(self.text_segment_cmd_index.?, @sizeOf(macho.mach_header_64) + sizeofcmds); | |
| 922 | ||
| 923 | // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments. | |
| 924 | var min_alignment: u32 = 0; | |
| 925 | for (seg.sections.items) |sect| { | |
| 926 | const alignment = try math.powi(u32, 2, sect.@"align"); | |
| 927 | min_alignment = math.max(min_alignment, alignment); | |
| 928 | } | |
| 929 | ||
| 930 | assert(min_alignment > 0); | |
| 931 | const last_sect_idx = seg.sections.items.len - 1; | |
| 932 | const last_sect = seg.sections.items[last_sect_idx]; | |
| 933 | const shift: u32 = blk: { | |
| 934 | const diff = seg.inner.filesize - last_sect.offset - last_sect.size; | |
| 935 | const factor = @divTrunc(diff, min_alignment); | |
| 936 | break :blk @intCast(u32, factor * min_alignment); | |
| 937 | }; | |
| 938 | ||
| 939 | if (shift > 0) { | |
| 940 | for (seg.sections.items) |*sect| { | |
| 941 | sect.offset += shift; | |
| 942 | sect.addr += shift; | |
| 943 | } | |
| 944 | } | |
| 945 | } | |
| 946 | ||
| 947 | fn allocateDataConstSegment(self: *Zld) !void { | |
| 948 | const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 949 | const nentries = @intCast(u32, self.got_entries.count()); | |
| 950 | ||
| 951 | const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 952 | seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize; | |
| 953 | seg.inner.vmaddr = text_seg.inner.vmaddr + text_seg.inner.vmsize; | |
| 954 | ||
| 955 | // Set got size | |
| 956 | const got = &seg.sections.items[self.got_section_index.?]; | |
| 957 | got.size += nentries * @sizeOf(u64); | |
| 958 | ||
| 959 | try self.allocateSegment(self.data_const_segment_cmd_index.?, 0); | |
| 960 | } | |
| 961 | ||
| 962 | fn allocateDataSegment(self: *Zld) !void { | |
| 963 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 964 | const nstubs = @intCast(u32, self.stubs.count()); | |
| 965 | ||
| 966 | const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 967 | seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize; | |
| 968 | seg.inner.vmaddr = data_const_seg.inner.vmaddr + data_const_seg.inner.vmsize; | |
| 969 | ||
| 970 | // Set la_symbol_ptr and data size | |
| 971 | const la_symbol_ptr = &seg.sections.items[self.la_symbol_ptr_section_index.?]; | |
| 972 | const data = &seg.sections.items[self.data_section_index.?]; | |
| 973 | la_symbol_ptr.size += nstubs * @sizeOf(u64); | |
| 974 | data.size += @sizeOf(u64); // We need at least 8bytes for address of dyld_stub_binder | |
| 975 | ||
| 976 | try self.allocateSegment(self.data_segment_cmd_index.?, 0); | |
| 977 | } | |
| 978 | ||
| 979 | fn allocateLinkeditSegment(self: *Zld) void { | |
| 980 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 981 | const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 982 | seg.inner.fileoff = data_seg.inner.fileoff + data_seg.inner.filesize; | |
| 983 | seg.inner.vmaddr = data_seg.inner.vmaddr + data_seg.inner.vmsize; | |
| 984 | } | |
| 985 | ||
| 986 | fn allocateSegment(self: *Zld, index: u16, offset: u64) !void { | |
| 987 | const seg = &self.load_commands.items[index].Segment; | |
| 988 | ||
| 989 | // Allocate the sections according to their alignment at the beginning of the segment. | |
| 990 | var start: u64 = offset; | |
| 991 | for (seg.sections.items) |*sect| { | |
| 992 | const alignment = try math.powi(u32, 2, sect.@"align"); | |
| 993 | const start_aligned = mem.alignForwardGeneric(u64, start, alignment); | |
| 994 | const end_aligned = mem.alignForwardGeneric(u64, start_aligned + sect.size, alignment); | |
| 995 | sect.offset = @intCast(u32, seg.inner.fileoff + start_aligned); | |
| 996 | sect.addr = seg.inner.vmaddr + start_aligned; | |
| 997 | start = end_aligned; | |
| 998 | } | |
| 999 | ||
| 1000 | const seg_size_aligned = mem.alignForwardGeneric(u64, start, self.page_size.?); | |
| 1001 | seg.inner.filesize = seg_size_aligned; | |
| 1002 | seg.inner.vmsize = seg_size_aligned; | |
| 1003 | } | |
| 1004 | ||
| 1005 | fn allocateTextBlocks(self: *Zld) !void { | |
| 1006 | var it = self.blocks.iterator(); | |
| 1007 | while (it.next()) |entry| { | |
| 1008 | const match = entry.key_ptr.*; | |
| 1009 | var block: *TextBlock = entry.value_ptr.*; | |
| 1010 | ||
| 1011 | // Find the first block | |
| 1012 | while (block.prev) |prev| { | |
| 1013 | block = prev; | |
| 1014 | } | |
| 1015 | ||
| 1016 | const seg = self.load_commands.items[match.seg].Segment; | |
| 1017 | const sect = seg.sections.items[match.sect]; | |
| 1018 | ||
| 1019 | var base_addr: u64 = sect.addr; | |
| 1020 | const n_sect = self.sectionId(match); | |
| 1021 | ||
| 1022 | log.debug(" within section {s},{s}", .{ segmentName(sect), sectionName(sect) }); | |
| 1023 | log.debug(" {}", .{sect}); | |
| 1024 | ||
| 1025 | while (true) { | |
| 1026 | const block_alignment = try math.powi(u32, 2, block.alignment); | |
| 1027 | base_addr = mem.alignForwardGeneric(u64, base_addr, block_alignment); | |
| 1028 | ||
| 1029 | const sym = &self.locals.items[block.local_sym_index]; | |
| 1030 | sym.n_value = base_addr; | |
| 1031 | sym.n_sect = n_sect; | |
| 1032 | ||
| 1033 | log.debug(" {s}: start=0x{x}, end=0x{x}, size={}, align={}", .{ | |
| 1034 | self.getString(sym.n_strx), | |
| 1035 | base_addr, | |
| 1036 | base_addr + block.size, | |
| 1037 | block.size, | |
| 1038 | block.alignment, | |
| 1039 | }); | |
| 1040 | ||
| 1041 | // Update each alias (if any) | |
| 1042 | for (block.aliases.items) |index| { | |
| 1043 | const alias_sym = &self.locals.items[index]; | |
| 1044 | alias_sym.n_value = base_addr; | |
| 1045 | alias_sym.n_sect = n_sect; | |
| 1046 | } | |
| 1047 | ||
| 1048 | // Update each symbol contained within the TextBlock | |
| 1049 | if (block.contained) |contained| { | |
| 1050 | for (contained) |sym_at_off| { | |
| 1051 | const contained_sym = &self.locals.items[sym_at_off.local_sym_index]; | |
| 1052 | contained_sym.n_value = base_addr + sym_at_off.offset; | |
| 1053 | contained_sym.n_sect = n_sect; | |
| 1054 | } | |
| 1055 | } | |
| 1056 | ||
| 1057 | base_addr += block.size; | |
| 1058 | ||
| 1059 | if (block.next) |next| { | |
| 1060 | block = next; | |
| 1061 | } else break; | |
| 1062 | } | |
| 1063 | } | |
| 1064 | ||
| 1065 | // Update globals | |
| 1066 | for (self.symbol_resolver.values()) |resolv| { | |
| 1067 | if (resolv.where != .global) continue; | |
| 1068 | ||
| 1069 | assert(resolv.local_sym_index != 0); | |
| 1070 | const local_sym = self.locals.items[resolv.local_sym_index]; | |
| 1071 | const sym = &self.globals.items[resolv.where_index]; | |
| 1072 | sym.n_value = local_sym.n_value; | |
| 1073 | sym.n_sect = local_sym.n_sect; | |
| 1074 | } | |
| 1075 | } | |
| 1076 | ||
| 1077 | fn writeTextBlocks(self: *Zld) !void { | |
| 1078 | var it = self.blocks.iterator(); | |
| 1079 | while (it.next()) |entry| { | |
| 1080 | const match = entry.key_ptr.*; | |
| 1081 | var block: *TextBlock = entry.value_ptr.*; | |
| 1082 | ||
| 1083 | while (block.prev) |prev| { | |
| 1084 | block = prev; | |
| 1085 | } | |
| 1086 | ||
| 1087 | const seg = self.load_commands.items[match.seg].Segment; | |
| 1088 | const sect = seg.sections.items[match.sect]; | |
| 1089 | const sect_type = sectionType(sect); | |
| 1090 | ||
| 1091 | log.debug(" for section {s},{s}", .{ segmentName(sect), sectionName(sect) }); | |
| 1092 | log.debug(" {}", .{sect}); | |
| 1093 | ||
| 1094 | var code = try self.allocator.alloc(u8, sect.size); | |
| 1095 | defer self.allocator.free(code); | |
| 1096 | ||
| 1097 | if (sect_type == macho.S_ZEROFILL or sect_type == macho.S_THREAD_LOCAL_ZEROFILL) { | |
| 1098 | mem.set(u8, code, 0); | |
| 1099 | } else { | |
| 1100 | var base_off: u64 = 0; | |
| 1101 | ||
| 1102 | while (true) { | |
| 1103 | const block_alignment = try math.powi(u32, 2, block.alignment); | |
| 1104 | const aligned_base_off = mem.alignForwardGeneric(u64, base_off, block_alignment); | |
| 1105 | ||
| 1106 | const sym = self.locals.items[block.local_sym_index]; | |
| 1107 | log.debug(" {s}: start=0x{x}, end=0x{x}, size={}, align={}", .{ | |
| 1108 | self.getString(sym.n_strx), | |
| 1109 | aligned_base_off, | |
| 1110 | aligned_base_off + block.size, | |
| 1111 | block.size, | |
| 1112 | block.alignment, | |
| 1113 | }); | |
| 1114 | ||
| 1115 | try block.resolveRelocs(self); | |
| 1116 | mem.copy(u8, code[aligned_base_off..][0..block.size], block.code); | |
| 1117 | ||
| 1118 | // TODO NOP for machine code instead of just zeroing out | |
| 1119 | const padding_len = aligned_base_off - base_off; | |
| 1120 | mem.set(u8, code[base_off..][0..padding_len], 0); | |
| 1121 | ||
| 1122 | base_off = aligned_base_off + block.size; | |
| 1123 | ||
| 1124 | if (block.next) |next| { | |
| 1125 | block = next; | |
| 1126 | } else break; | |
| 1127 | } | |
| 1128 | ||
| 1129 | mem.set(u8, code[base_off..], 0); | |
| 1130 | } | |
| 1131 | ||
| 1132 | try self.file.?.pwriteAll(code, sect.offset); | |
| 1133 | } | |
| 1134 | } | |
| 1135 | ||
| 1136 | fn writeStubHelperCommon(self: *Zld) !void { | |
| 1137 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1138 | const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?]; | |
| 1139 | const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 1140 | const got = &data_const_segment.sections.items[self.got_section_index.?]; | |
| 1141 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1142 | const data = &data_segment.sections.items[self.data_section_index.?]; | |
| 1143 | ||
| 1144 | self.stub_helper_stubs_start_off = blk: { | |
| 1145 | switch (self.target.?.cpu.arch) { | |
| 1146 | .x86_64 => { | |
| 1147 | const code_size = 15; | |
| 1148 | var code: [code_size]u8 = undefined; | |
| 1149 | // lea %r11, [rip + disp] | |
| 1150 | code[0] = 0x4c; | |
| 1151 | code[1] = 0x8d; | |
| 1152 | code[2] = 0x1d; | |
| 1153 | { | |
| 1154 | const target_addr = data.addr + data.size - @sizeOf(u64); | |
| 1155 | const displacement = try math.cast(u32, target_addr - stub_helper.addr - 7); | |
| 1156 | mem.writeIntLittle(u32, code[3..7], displacement); | |
| 1157 | } | |
| 1158 | // push %r11 | |
| 1159 | code[7] = 0x41; | |
| 1160 | code[8] = 0x53; | |
| 1161 | // jmp [rip + disp] | |
| 1162 | code[9] = 0xff; | |
| 1163 | code[10] = 0x25; | |
| 1164 | { | |
| 1165 | const resolv = self.symbol_resolver.get("dyld_stub_binder") orelse unreachable; | |
| 1166 | const got_index = self.got_entries.getIndex(.{ | |
| 1167 | .where = .import, | |
| 1168 | .where_index = resolv.where_index, | |
| 1169 | }) orelse unreachable; | |
| 1170 | const addr = got.addr + got_index * @sizeOf(u64); | |
| 1171 | const displacement = try math.cast(u32, addr - stub_helper.addr - code_size); | |
| 1172 | mem.writeIntLittle(u32, code[11..], displacement); | |
| 1173 | } | |
| 1174 | try self.file.?.pwriteAll(&code, stub_helper.offset); | |
| 1175 | break :blk stub_helper.offset + code_size; | |
| 1176 | }, | |
| 1177 | .aarch64 => { | |
| 1178 | var code: [6 * @sizeOf(u32)]u8 = undefined; | |
| 1179 | data_blk_outer: { | |
| 1180 | const this_addr = stub_helper.addr; | |
| 1181 | const target_addr = data.addr + data.size - @sizeOf(u64); | |
| 1182 | data_blk: { | |
| 1183 | const displacement = math.cast(i21, target_addr - this_addr) catch break :data_blk; | |
| 1184 | // adr x17, disp | |
| 1185 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32()); | |
| 1186 | // nop | |
| 1187 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32()); | |
| 1188 | break :data_blk_outer; | |
| 1189 | } | |
| 1190 | data_blk: { | |
| 1191 | const new_this_addr = this_addr + @sizeOf(u32); | |
| 1192 | const displacement = math.cast(i21, target_addr - new_this_addr) catch break :data_blk; | |
| 1193 | // nop | |
| 1194 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32()); | |
| 1195 | // adr x17, disp | |
| 1196 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.adr(.x17, displacement).toU32()); | |
| 1197 | break :data_blk_outer; | |
| 1198 | } | |
| 1199 | // Jump is too big, replace adr with adrp and add. | |
| 1200 | const this_page = @intCast(i32, this_addr >> 12); | |
| 1201 | const target_page = @intCast(i32, target_addr >> 12); | |
| 1202 | const pages = @intCast(i21, target_page - this_page); | |
| 1203 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x17, pages).toU32()); | |
| 1204 | const narrowed = @truncate(u12, target_addr); | |
| 1205 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.add(.x17, .x17, narrowed, false).toU32()); | |
| 1206 | } | |
| 1207 | // stp x16, x17, [sp, #-16]! | |
| 1208 | code[8] = 0xf0; | |
| 1209 | code[9] = 0x47; | |
| 1210 | code[10] = 0xbf; | |
| 1211 | code[11] = 0xa9; | |
| 1212 | binder_blk_outer: { | |
| 1213 | const resolv = self.symbol_resolver.get("dyld_stub_binder") orelse unreachable; | |
| 1214 | const got_index = self.got_entries.getIndex(.{ | |
| 1215 | .where = .import, | |
| 1216 | .where_index = resolv.where_index, | |
| 1217 | }) orelse unreachable; | |
| 1218 | const this_addr = stub_helper.addr + 3 * @sizeOf(u32); | |
| 1219 | const target_addr = got.addr + got_index * @sizeOf(u64); | |
| 1220 | binder_blk: { | |
| 1221 | const displacement = math.divExact(u64, target_addr - this_addr, 4) catch break :binder_blk; | |
| 1222 | const literal = math.cast(u18, displacement) catch break :binder_blk; | |
| 1223 | // ldr x16, label | |
| 1224 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.ldr(.x16, .{ | |
| 1225 | .literal = literal, | |
| 1226 | }).toU32()); | |
| 1227 | // nop | |
| 1228 | mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32()); | |
| 1229 | break :binder_blk_outer; | |
| 1230 | } | |
| 1231 | binder_blk: { | |
| 1232 | const new_this_addr = this_addr + @sizeOf(u32); | |
| 1233 | const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch break :binder_blk; | |
| 1234 | const literal = math.cast(u18, displacement) catch break :binder_blk; | |
| 1235 | // Pad with nop to please division. | |
| 1236 | // nop | |
| 1237 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32()); | |
| 1238 | // ldr x16, label | |
| 1239 | mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{ | |
| 1240 | .literal = literal, | |
| 1241 | }).toU32()); | |
| 1242 | break :binder_blk_outer; | |
| 1243 | } | |
| 1244 | // Use adrp followed by ldr(immediate). | |
| 1245 | const this_page = @intCast(i32, this_addr >> 12); | |
| 1246 | const target_page = @intCast(i32, target_addr >> 12); | |
| 1247 | const pages = @intCast(i21, target_page - this_page); | |
| 1248 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.adrp(.x16, pages).toU32()); | |
| 1249 | const narrowed = @truncate(u12, target_addr); | |
| 1250 | const offset = try math.divExact(u12, narrowed, 8); | |
| 1251 | mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{ | |
| 1252 | .register = .{ | |
| 1253 | .rn = .x16, | |
| 1254 | .offset = aarch64.Instruction.LoadStoreOffset.imm(offset), | |
| 1255 | }, | |
| 1256 | }).toU32()); | |
| 1257 | } | |
| 1258 | // br x16 | |
| 1259 | code[20] = 0x00; | |
| 1260 | code[21] = 0x02; | |
| 1261 | code[22] = 0x1f; | |
| 1262 | code[23] = 0xd6; | |
| 1263 | try self.file.?.pwriteAll(&code, stub_helper.offset); | |
| 1264 | break :blk stub_helper.offset + 6 * @sizeOf(u32); | |
| 1265 | }, | |
| 1266 | else => unreachable, | |
| 1267 | } | |
| 1268 | }; | |
| 1269 | ||
| 1270 | for (self.stubs.keys()) |_, i| { | |
| 1271 | const index = @intCast(u32, i); | |
| 1272 | // TODO weak bound pointers | |
| 1273 | try self.writeLazySymbolPointer(index); | |
| 1274 | try self.writeStub(index); | |
| 1275 | try self.writeStubInStubHelper(index); | |
| 1276 | } | |
| 1277 | } | |
| 1278 | ||
| 1279 | fn writeLazySymbolPointer(self: *Zld, index: u32) !void { | |
| 1280 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1281 | const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?]; | |
| 1282 | const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1283 | const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?]; | |
| 1284 | ||
| 1285 | const stub_size: u4 = switch (self.target.?.cpu.arch) { | |
| 1286 | .x86_64 => 10, | |
| 1287 | .aarch64 => 3 * @sizeOf(u32), | |
| 1288 | else => unreachable, | |
| 1289 | }; | |
| 1290 | const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size; | |
| 1291 | const end = stub_helper.addr + stub_off - stub_helper.offset; | |
| 1292 | var buf: [@sizeOf(u64)]u8 = undefined; | |
| 1293 | mem.writeIntLittle(u64, &buf, end); | |
| 1294 | const off = la_symbol_ptr.offset + index * @sizeOf(u64); | |
| 1295 | log.debug("writing lazy symbol pointer entry 0x{x} at 0x{x}", .{ end, off }); | |
| 1296 | try self.file.?.pwriteAll(&buf, off); | |
| 1297 | } | |
| 1298 | ||
| 1299 | fn writeStub(self: *Zld, index: u32) !void { | |
| 1300 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1301 | const stubs = text_segment.sections.items[self.stubs_section_index.?]; | |
| 1302 | const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1303 | const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?]; | |
| 1304 | ||
| 1305 | const stub_off = stubs.offset + index * stubs.reserved2; | |
| 1306 | const stub_addr = stubs.addr + index * stubs.reserved2; | |
| 1307 | const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64); | |
| 1308 | log.debug("writing stub at 0x{x}", .{stub_off}); | |
| 1309 | var code = try self.allocator.alloc(u8, stubs.reserved2); | |
| 1310 | defer self.allocator.free(code); | |
| 1311 | switch (self.target.?.cpu.arch) { | |
| 1312 | .x86_64 => { | |
| 1313 | assert(la_ptr_addr >= stub_addr + stubs.reserved2); | |
| 1314 | const displacement = try math.cast(u32, la_ptr_addr - stub_addr - stubs.reserved2); | |
| 1315 | // jmp | |
| 1316 | code[0] = 0xff; | |
| 1317 | code[1] = 0x25; | |
| 1318 | mem.writeIntLittle(u32, code[2..][0..4], displacement); | |
| 1319 | }, | |
| 1320 | .aarch64 => { | |
| 1321 | assert(la_ptr_addr >= stub_addr); | |
| 1322 | outer: { | |
| 1323 | const this_addr = stub_addr; | |
| 1324 | const target_addr = la_ptr_addr; | |
| 1325 | inner: { | |
| 1326 | const displacement = math.divExact(u64, target_addr - this_addr, 4) catch break :inner; | |
| 1327 | const literal = math.cast(u18, displacement) catch break :inner; | |
| 1328 | // ldr x16, literal | |
| 1329 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{ | |
| 1330 | .literal = literal, | |
| 1331 | }).toU32()); | |
| 1332 | // nop | |
| 1333 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32()); | |
| 1334 | break :outer; | |
| 1335 | } | |
| 1336 | inner: { | |
| 1337 | const new_this_addr = this_addr + @sizeOf(u32); | |
| 1338 | const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch break :inner; | |
| 1339 | const literal = math.cast(u18, displacement) catch break :inner; | |
| 1340 | // nop | |
| 1341 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32()); | |
| 1342 | // ldr x16, literal | |
| 1343 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{ | |
| 1344 | .literal = literal, | |
| 1345 | }).toU32()); | |
| 1346 | break :outer; | |
| 1347 | } | |
| 1348 | // Use adrp followed by ldr(immediate). | |
| 1349 | const this_page = @intCast(i32, this_addr >> 12); | |
| 1350 | const target_page = @intCast(i32, target_addr >> 12); | |
| 1351 | const pages = @intCast(i21, target_page - this_page); | |
| 1352 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x16, pages).toU32()); | |
| 1353 | const narrowed = @truncate(u12, target_addr); | |
| 1354 | const offset = try math.divExact(u12, narrowed, 8); | |
| 1355 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{ | |
| 1356 | .register = .{ | |
| 1357 | .rn = .x16, | |
| 1358 | .offset = aarch64.Instruction.LoadStoreOffset.imm(offset), | |
| 1359 | }, | |
| 1360 | }).toU32()); | |
| 1361 | } | |
| 1362 | // br x16 | |
| 1363 | mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32()); | |
| 1364 | }, | |
| 1365 | else => unreachable, | |
| 1366 | } | |
| 1367 | try self.file.?.pwriteAll(code, stub_off); | |
| 1368 | } | |
| 1369 | ||
| 1370 | fn writeStubInStubHelper(self: *Zld, index: u32) !void { | |
| 1371 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1372 | const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?]; | |
| 1373 | ||
| 1374 | const stub_size: u4 = switch (self.target.?.cpu.arch) { | |
| 1375 | .x86_64 => 10, | |
| 1376 | .aarch64 => 3 * @sizeOf(u32), | |
| 1377 | else => unreachable, | |
| 1378 | }; | |
| 1379 | const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size; | |
| 1380 | var code = try self.allocator.alloc(u8, stub_size); | |
| 1381 | defer self.allocator.free(code); | |
| 1382 | switch (self.target.?.cpu.arch) { | |
| 1383 | .x86_64 => { | |
| 1384 | const displacement = try math.cast( | |
| 1385 | i32, | |
| 1386 | @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - stub_size, | |
| 1387 | ); | |
| 1388 | // pushq | |
| 1389 | code[0] = 0x68; | |
| 1390 | mem.writeIntLittle(u32, code[1..][0..4], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. | |
| 1391 | // jmpq | |
| 1392 | code[5] = 0xe9; | |
| 1393 | mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement)); | |
| 1394 | }, | |
| 1395 | .aarch64 => { | |
| 1396 | const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4); | |
| 1397 | const literal = @divExact(stub_size - @sizeOf(u32), 4); | |
| 1398 | // ldr w16, literal | |
| 1399 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{ | |
| 1400 | .literal = literal, | |
| 1401 | }).toU32()); | |
| 1402 | // b disp | |
| 1403 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32()); | |
| 1404 | mem.writeIntLittle(u32, code[8..12], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. | |
| 1405 | }, | |
| 1406 | else => unreachable, | |
| 1407 | } | |
| 1408 | try self.file.?.pwriteAll(code, stub_off); | |
| 1409 | } | |
| 1410 | ||
| 1411 | fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void { | |
| 1412 | const object = self.objects.items[object_id]; | |
| 1413 | ||
| 1414 | log.debug("resolving symbols in '{s}'", .{object.name}); | |
| 1415 | ||
| 1416 | for (object.symtab.items) |sym, id| { | |
| 1417 | const sym_id = @intCast(u32, id); | |
| 1418 | const sym_name = object.getString(sym.n_strx); | |
| 1419 | ||
| 1420 | if (symbolIsStab(sym)) { | |
| 1421 | log.err("unhandled symbol type: stab", .{}); | |
| 1422 | log.err(" symbol '{s}'", .{sym_name}); | |
| 1423 | log.err(" first definition in '{s}'", .{object.name.?}); | |
| 1424 | return error.UnhandledSymbolType; | |
| 1425 | } | |
| 1426 | ||
| 1427 | if (symbolIsIndr(sym)) { | |
| 1428 | log.err("unhandled symbol type: indirect", .{}); | |
| 1429 | log.err(" symbol '{s}'", .{sym_name}); | |
| 1430 | log.err(" first definition in '{s}'", .{object.name.?}); | |
| 1431 | return error.UnhandledSymbolType; | |
| 1432 | } | |
| 1433 | ||
| 1434 | if (symbolIsAbs(sym)) { | |
| 1435 | log.err("unhandled symbol type: absolute", .{}); | |
| 1436 | log.err(" symbol '{s}'", .{sym_name}); | |
| 1437 | log.err(" first definition in '{s}'", .{object.name.?}); | |
| 1438 | return error.UnhandledSymbolType; | |
| 1439 | } | |
| 1440 | ||
| 1441 | if (symbolIsSect(sym)) { | |
| 1442 | // Defined symbol regardless of scope lands in the locals symbol table. | |
| 1443 | const n_strx = blk: { | |
| 1444 | if (self.symbol_resolver.get(sym_name)) |resolv| { | |
| 1445 | switch (resolv.where) { | |
| 1446 | .global => break :blk self.globals.items[resolv.where_index].n_strx, | |
| 1447 | .tentative => break :blk self.tentatives.items[resolv.where_index].n_strx, | |
| 1448 | .undef => break :blk self.undefs.items[resolv.where_index].n_strx, | |
| 1449 | .import => unreachable, | |
| 1450 | } | |
| 1451 | } | |
| 1452 | break :blk try self.makeString(sym_name); | |
| 1453 | }; | |
| 1454 | const local_sym_index = @intCast(u32, self.locals.items.len); | |
| 1455 | try self.locals.append(self.allocator, .{ | |
| 1456 | .n_strx = n_strx, | |
| 1457 | .n_type = macho.N_SECT, | |
| 1458 | .n_sect = 0, | |
| 1459 | .n_desc = 0, | |
| 1460 | .n_value = sym.n_value, | |
| 1461 | }); | |
| 1462 | try object.symbol_mapping.putNoClobber(self.allocator, sym_id, local_sym_index); | |
| 1463 | ||
| 1464 | // If the symbol's scope is not local aka translation unit, then we need work out | |
| 1465 | // if we should save the symbol as a global, or potentially flag the error. | |
| 1466 | if (!symbolIsExt(sym)) continue; | |
| 1467 | ||
| 1468 | const local = self.locals.items[local_sym_index]; | |
| 1469 | const resolv = self.symbol_resolver.getPtr(sym_name) orelse { | |
| 1470 | const global_sym_index = @intCast(u32, self.globals.items.len); | |
| 1471 | try self.globals.append(self.allocator, .{ | |
| 1472 | .n_strx = n_strx, | |
| 1473 | .n_type = sym.n_type, | |
| 1474 | .n_sect = 0, | |
| 1475 | .n_desc = sym.n_desc, | |
| 1476 | .n_value = sym.n_value, | |
| 1477 | }); | |
| 1478 | try self.symbol_resolver.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), .{ | |
| 1479 | .where = .global, | |
| 1480 | .where_index = global_sym_index, | |
| 1481 | .local_sym_index = local_sym_index, | |
| 1482 | .file = object_id, | |
| 1483 | }); | |
| 1484 | continue; | |
| 1485 | }; | |
| 1486 | ||
| 1487 | switch (resolv.where) { | |
| 1488 | .import => unreachable, | |
| 1489 | .global => { | |
| 1490 | const global = &self.globals.items[resolv.where_index]; | |
| 1491 | ||
| 1492 | if (!(symbolIsWeakDef(sym) or symbolIsPext(sym)) and | |
| 1493 | !(symbolIsWeakDef(global.*) or symbolIsPext(global.*))) | |
| 1494 | { | |
| 1495 | log.err("symbol '{s}' defined multiple times", .{sym_name}); | |
| 1496 | log.err(" first definition in '{s}'", .{self.objects.items[resolv.file].name.?}); | |
| 1497 | log.err(" next definition in '{s}'", .{object.name.?}); | |
| 1498 | return error.MultipleSymbolDefinitions; | |
| 1499 | } | |
| 1500 | ||
| 1501 | if (symbolIsWeakDef(sym) or symbolIsPext(sym)) continue; // Current symbol is weak, so skip it. | |
| 1502 | ||
| 1503 | // Otherwise, update the resolver and the global symbol. | |
| 1504 | global.n_type = sym.n_type; | |
| 1505 | resolv.local_sym_index = local_sym_index; | |
| 1506 | resolv.file = object_id; | |
| 1507 | ||
| 1508 | continue; | |
| 1509 | }, | |
| 1510 | .undef => { | |
| 1511 | const undef = &self.undefs.items[resolv.where_index]; | |
| 1512 | undef.* = .{ | |
| 1513 | .n_strx = 0, | |
| 1514 | .n_type = macho.N_UNDF, | |
| 1515 | .n_sect = 0, | |
| 1516 | .n_desc = 0, | |
| 1517 | .n_value = 0, | |
| 1518 | }; | |
| 1519 | }, | |
| 1520 | .tentative => { | |
| 1521 | const tentative = &self.tentatives.items[resolv.where_index]; | |
| 1522 | tentative.* = .{ | |
| 1523 | .n_strx = 0, | |
| 1524 | .n_type = macho.N_UNDF, | |
| 1525 | .n_sect = 0, | |
| 1526 | .n_desc = 0, | |
| 1527 | .n_value = 0, | |
| 1528 | }; | |
| 1529 | }, | |
| 1530 | } | |
| 1531 | ||
| 1532 | const global_sym_index = @intCast(u32, self.globals.items.len); | |
| 1533 | try self.globals.append(self.allocator, .{ | |
| 1534 | .n_strx = local.n_strx, | |
| 1535 | .n_type = sym.n_type, | |
| 1536 | .n_sect = 0, | |
| 1537 | .n_desc = sym.n_desc, | |
| 1538 | .n_value = sym.n_value, | |
| 1539 | }); | |
| 1540 | resolv.* = .{ | |
| 1541 | .where = .global, | |
| 1542 | .where_index = global_sym_index, | |
| 1543 | .local_sym_index = local_sym_index, | |
| 1544 | .file = object_id, | |
| 1545 | }; | |
| 1546 | } else if (symbolIsTentative(sym)) { | |
| 1547 | // Symbol is a tentative definition. | |
| 1548 | const resolv = self.symbol_resolver.getPtr(sym_name) orelse { | |
| 1549 | const tent_sym_index = @intCast(u32, self.tentatives.items.len); | |
| 1550 | try self.tentatives.append(self.allocator, .{ | |
| 1551 | .n_strx = try self.makeString(sym_name), | |
| 1552 | .n_type = sym.n_type, | |
| 1553 | .n_sect = 0, | |
| 1554 | .n_desc = sym.n_desc, | |
| 1555 | .n_value = sym.n_value, | |
| 1556 | }); | |
| 1557 | try self.symbol_resolver.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), .{ | |
| 1558 | .where = .tentative, | |
| 1559 | .where_index = tent_sym_index, | |
| 1560 | .file = object_id, | |
| 1561 | }); | |
| 1562 | continue; | |
| 1563 | }; | |
| 1564 | ||
| 1565 | switch (resolv.where) { | |
| 1566 | .import => unreachable, | |
| 1567 | .global => {}, | |
| 1568 | .undef => { | |
| 1569 | const undef = &self.undefs.items[resolv.where_index]; | |
| 1570 | const tent_sym_index = @intCast(u32, self.tentatives.items.len); | |
| 1571 | try self.tentatives.append(self.allocator, .{ | |
| 1572 | .n_strx = undef.n_strx, | |
| 1573 | .n_type = sym.n_type, | |
| 1574 | .n_sect = 0, | |
| 1575 | .n_desc = sym.n_desc, | |
| 1576 | .n_value = sym.n_value, | |
| 1577 | }); | |
| 1578 | resolv.* = .{ | |
| 1579 | .where = .tentative, | |
| 1580 | .where_index = tent_sym_index, | |
| 1581 | .file = object_id, | |
| 1582 | }; | |
| 1583 | undef.* = .{ | |
| 1584 | .n_strx = 0, | |
| 1585 | .n_type = macho.N_UNDF, | |
| 1586 | .n_sect = 0, | |
| 1587 | .n_desc = 0, | |
| 1588 | .n_value = 0, | |
| 1589 | }; | |
| 1590 | }, | |
| 1591 | .tentative => { | |
| 1592 | const tentative = &self.tentatives.items[resolv.where_index]; | |
| 1593 | if (tentative.n_value >= sym.n_value) continue; | |
| 1594 | ||
| 1595 | tentative.n_desc = sym.n_desc; | |
| 1596 | tentative.n_value = sym.n_value; | |
| 1597 | resolv.file = object_id; | |
| 1598 | }, | |
| 1599 | } | |
| 1600 | } else { | |
| 1601 | // Symbol is undefined. | |
| 1602 | if (self.symbol_resolver.contains(sym_name)) continue; | |
| 1603 | ||
| 1604 | const undef_sym_index = @intCast(u32, self.undefs.items.len); | |
| 1605 | try self.undefs.append(self.allocator, .{ | |
| 1606 | .n_strx = try self.makeString(sym_name), | |
| 1607 | .n_type = macho.N_UNDF, | |
| 1608 | .n_sect = 0, | |
| 1609 | .n_desc = 0, | |
| 1610 | .n_value = 0, | |
| 1611 | }); | |
| 1612 | try self.symbol_resolver.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), .{ | |
| 1613 | .where = .undef, | |
| 1614 | .where_index = undef_sym_index, | |
| 1615 | .file = object_id, | |
| 1616 | }); | |
| 1617 | } | |
| 1618 | } | |
| 1619 | } | |
| 1620 | ||
| 1621 | fn resolveSymbols(self: *Zld) !void { | |
| 1622 | // TODO mimicking insertion of null symbol from incremental linker. | |
| 1623 | // This will need to moved. | |
| 1624 | try self.locals.append(self.allocator, .{ | |
| 1625 | .n_strx = 0, | |
| 1626 | .n_type = macho.N_UNDF, | |
| 1627 | .n_sect = 0, | |
| 1628 | .n_desc = 0, | |
| 1629 | .n_value = 0, | |
| 1630 | }); | |
| 1631 | try self.strtab.append(self.allocator, 0); | |
| 1632 | ||
| 1633 | // First pass, resolve symbols in provided objects. | |
| 1634 | for (self.objects.items) |_, object_id| { | |
| 1635 | try self.resolveSymbolsInObject(@intCast(u16, object_id)); | |
| 1636 | } | |
| 1637 | ||
| 1638 | // Second pass, resolve symbols in static libraries. | |
| 1639 | var next_sym: usize = 0; | |
| 1640 | loop: while (true) : (next_sym += 1) { | |
| 1641 | if (next_sym == self.undefs.items.len) break; | |
| 1642 | ||
| 1643 | const sym = self.undefs.items[next_sym]; | |
| 1644 | if (symbolIsNull(sym)) continue; | |
| 1645 | ||
| 1646 | const sym_name = self.getString(sym.n_strx); | |
| 1647 | ||
| 1648 | for (self.archives.items) |archive| { | |
| 1649 | // Check if the entry exists in a static archive. | |
| 1650 | const offsets = archive.toc.get(sym_name) orelse { | |
| 1651 | // No hit. | |
| 1652 | continue; | |
| 1653 | }; | |
| 1654 | assert(offsets.items.len > 0); | |
| 1655 | ||
| 1656 | const object = try archive.parseObject(offsets.items[0]); | |
| 1657 | const object_id = @intCast(u16, self.objects.items.len); | |
| 1658 | try self.objects.append(self.allocator, object); | |
| 1659 | try self.resolveSymbolsInObject(object_id); | |
| 1660 | ||
| 1661 | continue :loop; | |
| 1662 | } | |
| 1663 | } | |
| 1664 | ||
| 1665 | // Convert any tentative definition into a regular symbol and allocate | |
| 1666 | // text blocks for each tentative defintion. | |
| 1667 | for (self.tentatives.items) |sym| { | |
| 1668 | if (symbolIsNull(sym)) continue; | |
| 1669 | ||
| 1670 | const sym_name = self.getString(sym.n_strx); | |
| 1671 | const match: MatchingSection = blk: { | |
| 1672 | if (self.common_section_index == null) { | |
| 1673 | const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1674 | self.common_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1675 | try data_seg.addSection(self.allocator, "__common", .{ | |
| 1676 | .flags = macho.S_ZEROFILL, | |
| 1677 | }); | |
| 1678 | } | |
| 1679 | break :blk .{ | |
| 1680 | .seg = self.data_segment_cmd_index.?, | |
| 1681 | .sect = self.common_section_index.?, | |
| 1682 | }; | |
| 1683 | }; | |
| 1684 | ||
| 1685 | const size = sym.n_value; | |
| 1686 | const code = try self.allocator.alloc(u8, size); | |
| 1687 | mem.set(u8, code, 0); | |
| 1688 | const alignment = (sym.n_desc >> 8) & 0x0f; | |
| 1689 | ||
| 1690 | const resolv = self.symbol_resolver.getPtr(sym_name) orelse unreachable; | |
| 1691 | const local_sym_index = @intCast(u32, self.locals.items.len); | |
| 1692 | var nlist = macho.nlist_64{ | |
| 1693 | .n_strx = sym.n_strx, | |
| 1694 | .n_type = macho.N_SECT, | |
| 1695 | .n_sect = self.sectionId(match), | |
| 1696 | .n_desc = 0, | |
| 1697 | .n_value = 0, | |
| 1698 | }; | |
| 1699 | try self.locals.append(self.allocator, nlist); | |
| 1700 | const global_sym_index = @intCast(u32, self.globals.items.len); | |
| 1701 | nlist.n_type |= macho.N_EXT; | |
| 1702 | try self.globals.append(self.allocator, nlist); | |
| 1703 | resolv.* = .{ | |
| 1704 | .where = .global, | |
| 1705 | .where_index = global_sym_index, | |
| 1706 | .local_sym_index = local_sym_index, | |
| 1707 | }; | |
| 1708 | ||
| 1709 | const block = try self.allocator.create(TextBlock); | |
| 1710 | errdefer self.allocator.destroy(block); | |
| 1711 | ||
| 1712 | block.* = TextBlock.init(self.allocator); | |
| 1713 | block.local_sym_index = local_sym_index; | |
| 1714 | block.code = code; | |
| 1715 | block.size = size; | |
| 1716 | block.alignment = alignment; | |
| 1717 | ||
| 1718 | // Update target section's metadata | |
| 1719 | // TODO should we update segment's size here too? | |
| 1720 | // How does it tie with incremental space allocs? | |
| 1721 | const tseg = &self.load_commands.items[match.seg].Segment; | |
| 1722 | const tsect = &tseg.sections.items[match.sect]; | |
| 1723 | const new_alignment = math.max(tsect.@"align", block.alignment); | |
| 1724 | const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment); | |
| 1725 | const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size; | |
| 1726 | tsect.size = new_size; | |
| 1727 | tsect.@"align" = new_alignment; | |
| 1728 | ||
| 1729 | if (self.blocks.getPtr(match)) |last| { | |
| 1730 | last.*.next = block; | |
| 1731 | block.prev = last.*; | |
| 1732 | last.* = block; | |
| 1733 | } else { | |
| 1734 | try self.blocks.putNoClobber(self.allocator, match, block); | |
| 1735 | } | |
| 1736 | } | |
| 1737 | ||
| 1738 | // Third pass, resolve symbols in dynamic libraries. | |
| 1739 | { | |
| 1740 | // Put dyld_stub_binder as an undefined special symbol. | |
| 1741 | const undef_sym_index = @intCast(u32, self.undefs.items.len); | |
| 1742 | try self.undefs.append(self.allocator, .{ | |
| 1743 | .n_strx = try self.makeString("dyld_stub_binder"), | |
| 1744 | .n_type = macho.N_UNDF, | |
| 1745 | .n_sect = 0, | |
| 1746 | .n_desc = 0, | |
| 1747 | .n_value = 0, | |
| 1748 | }); | |
| 1749 | try self.symbol_resolver.putNoClobber(self.allocator, try self.allocator.dupe(u8, "dyld_stub_binder"), .{ | |
| 1750 | .where = .undef, | |
| 1751 | .where_index = undef_sym_index, | |
| 1752 | }); | |
| 1753 | } | |
| 1754 | ||
| 1755 | var referenced = std.AutoHashMap(*Dylib, void).init(self.allocator); | |
| 1756 | defer referenced.deinit(); | |
| 1757 | ||
| 1758 | loop: for (self.undefs.items) |sym| { | |
| 1759 | if (symbolIsNull(sym)) continue; | |
| 1760 | ||
| 1761 | const sym_name = self.getString(sym.n_strx); | |
| 1762 | for (self.dylibs.items) |dylib| { | |
| 1763 | if (!dylib.symbols.contains(sym_name)) continue; | |
| 1764 | ||
| 1765 | if (!referenced.contains(dylib)) { | |
| 1766 | // Add LC_LOAD_DYLIB load command for each referenced dylib/stub. | |
| 1767 | dylib.ordinal = self.next_dylib_ordinal; | |
| 1768 | const dylib_id = dylib.id orelse unreachable; | |
| 1769 | var dylib_cmd = try createLoadDylibCommand( | |
| 1770 | self.allocator, | |
| 1771 | dylib_id.name, | |
| 1772 | dylib_id.timestamp, | |
| 1773 | dylib_id.current_version, | |
| 1774 | dylib_id.compatibility_version, | |
| 1775 | ); | |
| 1776 | errdefer dylib_cmd.deinit(self.allocator); | |
| 1777 | try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd }); | |
| 1778 | self.next_dylib_ordinal += 1; | |
| 1779 | try referenced.putNoClobber(dylib, {}); | |
| 1780 | } | |
| 1781 | ||
| 1782 | const resolv = self.symbol_resolver.getPtr(sym_name) orelse unreachable; | |
| 1783 | const undef = &self.undefs.items[resolv.where_index]; | |
| 1784 | const import_sym_index = @intCast(u32, self.imports.items.len); | |
| 1785 | try self.imports.append(self.allocator, .{ | |
| 1786 | .n_strx = undef.n_strx, | |
| 1787 | .n_type = macho.N_UNDF | macho.N_EXT, | |
| 1788 | .n_sect = 0, | |
| 1789 | .n_desc = packDylibOrdinal(dylib.ordinal.?), | |
| 1790 | .n_value = 0, | |
| 1791 | }); | |
| 1792 | resolv.* = .{ | |
| 1793 | .where = .import, | |
| 1794 | .where_index = import_sym_index, | |
| 1795 | }; | |
| 1796 | undef.* = .{ | |
| 1797 | .n_strx = 0, | |
| 1798 | .n_type = macho.N_UNDF, | |
| 1799 | .n_sect = 0, | |
| 1800 | .n_desc = 0, | |
| 1801 | .n_value = 0, | |
| 1802 | }; | |
| 1803 | ||
| 1804 | continue :loop; | |
| 1805 | } | |
| 1806 | } | |
| 1807 | ||
| 1808 | // Fourth pass, handle synthetic symbols and flag any undefined references. | |
| 1809 | if (self.symbol_resolver.getPtr("___dso_handle")) |resolv| blk: { | |
| 1810 | if (resolv.where != .undef) break :blk; | |
| 1811 | ||
| 1812 | const undef = &self.undefs.items[resolv.where_index]; | |
| 1813 | const match: MatchingSection = .{ | |
| 1814 | .seg = self.text_segment_cmd_index.?, | |
| 1815 | .sect = self.text_section_index.?, | |
| 1816 | }; | |
| 1817 | const local_sym_index = @intCast(u32, self.locals.items.len); | |
| 1818 | var nlist = macho.nlist_64{ | |
| 1819 | .n_strx = undef.n_strx, | |
| 1820 | .n_type = macho.N_SECT, | |
| 1821 | .n_sect = self.sectionId(match), | |
| 1822 | .n_desc = 0, | |
| 1823 | .n_value = 0, | |
| 1824 | }; | |
| 1825 | try self.locals.append(self.allocator, nlist); | |
| 1826 | const global_sym_index = @intCast(u32, self.globals.items.len); | |
| 1827 | nlist.n_type |= macho.N_EXT; | |
| 1828 | nlist.n_desc = macho.N_WEAK_DEF; | |
| 1829 | try self.globals.append(self.allocator, nlist); | |
| 1830 | ||
| 1831 | undef.* = .{ | |
| 1832 | .n_strx = 0, | |
| 1833 | .n_type = macho.N_UNDF, | |
| 1834 | .n_sect = 0, | |
| 1835 | .n_desc = 0, | |
| 1836 | .n_value = 0, | |
| 1837 | }; | |
| 1838 | resolv.* = .{ | |
| 1839 | .where = .global, | |
| 1840 | .where_index = global_sym_index, | |
| 1841 | .local_sym_index = local_sym_index, | |
| 1842 | }; | |
| 1843 | ||
| 1844 | // We create an empty atom for this symbol. | |
| 1845 | // TODO perhaps we should special-case special symbols? Create a separate | |
| 1846 | // linked list of atoms? | |
| 1847 | const block = try self.allocator.create(TextBlock); | |
| 1848 | errdefer self.allocator.destroy(block); | |
| 1849 | ||
| 1850 | block.* = TextBlock.init(self.allocator); | |
| 1851 | block.local_sym_index = local_sym_index; | |
| 1852 | block.code = try self.allocator.alloc(u8, 0); | |
| 1853 | block.size = 0; | |
| 1854 | block.alignment = 0; | |
| 1855 | ||
| 1856 | if (self.blocks.getPtr(match)) |last| { | |
| 1857 | last.*.next = block; | |
| 1858 | block.prev = last.*; | |
| 1859 | last.* = block; | |
| 1860 | } else { | |
| 1861 | try self.blocks.putNoClobber(self.allocator, match, block); | |
| 1862 | } | |
| 1863 | } | |
| 1864 | ||
| 1865 | var has_undefined = false; | |
| 1866 | for (self.undefs.items) |sym| { | |
| 1867 | if (symbolIsNull(sym)) continue; | |
| 1868 | ||
| 1869 | const sym_name = self.getString(sym.n_strx); | |
| 1870 | const resolv = self.symbol_resolver.get(sym_name) orelse unreachable; | |
| 1871 | ||
| 1872 | log.err("undefined reference to symbol '{s}'", .{sym_name}); | |
| 1873 | log.err(" first referenced in '{s}'", .{self.objects.items[resolv.file].name.?}); | |
| 1874 | has_undefined = true; | |
| 1875 | } | |
| 1876 | ||
| 1877 | if (has_undefined) return error.UndefinedSymbolReference; | |
| 1878 | } | |
| 1879 | ||
| 1880 | fn parseTextBlocks(self: *Zld) !void { | |
| 1881 | for (self.objects.items) |object| { | |
| 1882 | try object.parseTextBlocks(self); | |
| 1883 | } | |
| 1884 | } | |
| 1885 | ||
| 1886 | fn populateMetadata(self: *Zld) !void { | |
| 1887 | if (self.pagezero_segment_cmd_index == null) { | |
| 1888 | self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 1889 | try self.load_commands.append(self.allocator, .{ | |
| 1890 | .Segment = SegmentCommand.empty("__PAGEZERO", .{ | |
| 1891 | .vmsize = 0x100000000, // size always set to 4GB | |
| 1892 | }), | |
| 1893 | }); | |
| 1894 | } | |
| 1895 | ||
| 1896 | if (self.text_segment_cmd_index == null) { | |
| 1897 | self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 1898 | try self.load_commands.append(self.allocator, .{ | |
| 1899 | .Segment = SegmentCommand.empty("__TEXT", .{ | |
| 1900 | .vmaddr = 0x100000000, // always starts at 4GB | |
| 1901 | .maxprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE, | |
| 1902 | .initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE, | |
| 1903 | }), | |
| 1904 | }); | |
| 1905 | } | |
| 1906 | ||
| 1907 | if (self.text_section_index == null) { | |
| 1908 | const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1909 | self.text_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1910 | const alignment: u2 = switch (self.target.?.cpu.arch) { | |
| 1911 | .x86_64 => 0, | |
| 1912 | .aarch64 => 2, | |
| 1913 | else => unreachable, // unhandled architecture type | |
| 1914 | }; | |
| 1915 | try text_seg.addSection(self.allocator, "__text", .{ | |
| 1916 | .@"align" = alignment, | |
| 1917 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, | |
| 1918 | }); | |
| 1919 | } | |
| 1920 | ||
| 1921 | if (self.stubs_section_index == null) { | |
| 1922 | const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1923 | self.stubs_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1924 | const alignment: u2 = switch (self.target.?.cpu.arch) { | |
| 1925 | .x86_64 => 0, | |
| 1926 | .aarch64 => 2, | |
| 1927 | else => unreachable, // unhandled architecture type | |
| 1928 | }; | |
| 1929 | const stub_size: u4 = switch (self.target.?.cpu.arch) { | |
| 1930 | .x86_64 => 6, | |
| 1931 | .aarch64 => 3 * @sizeOf(u32), | |
| 1932 | else => unreachable, // unhandled architecture type | |
| 1933 | }; | |
| 1934 | try text_seg.addSection(self.allocator, "__stubs", .{ | |
| 1935 | .@"align" = alignment, | |
| 1936 | .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, | |
| 1937 | .reserved2 = stub_size, | |
| 1938 | }); | |
| 1939 | } | |
| 1940 | ||
| 1941 | if (self.stub_helper_section_index == null) { | |
| 1942 | const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1943 | self.stub_helper_section_index = @intCast(u16, text_seg.sections.items.len); | |
| 1944 | const alignment: u2 = switch (self.target.?.cpu.arch) { | |
| 1945 | .x86_64 => 0, | |
| 1946 | .aarch64 => 2, | |
| 1947 | else => unreachable, // unhandled architecture type | |
| 1948 | }; | |
| 1949 | const stub_helper_size: u6 = switch (self.target.?.cpu.arch) { | |
| 1950 | .x86_64 => 15, | |
| 1951 | .aarch64 => 6 * @sizeOf(u32), | |
| 1952 | else => unreachable, | |
| 1953 | }; | |
| 1954 | try text_seg.addSection(self.allocator, "__stub_helper", .{ | |
| 1955 | .size = stub_helper_size, | |
| 1956 | .@"align" = alignment, | |
| 1957 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, | |
| 1958 | }); | |
| 1959 | } | |
| 1960 | ||
| 1961 | if (self.data_const_segment_cmd_index == null) { | |
| 1962 | self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 1963 | try self.load_commands.append(self.allocator, .{ | |
| 1964 | .Segment = SegmentCommand.empty("__DATA_CONST", .{ | |
| 1965 | .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, | |
| 1966 | .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, | |
| 1967 | }), | |
| 1968 | }); | |
| 1969 | } | |
| 1970 | ||
| 1971 | if (self.got_section_index == null) { | |
| 1972 | const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 1973 | self.got_section_index = @intCast(u16, data_const_seg.sections.items.len); | |
| 1974 | try data_const_seg.addSection(self.allocator, "__got", .{ | |
| 1975 | .@"align" = 3, // 2^3 = @sizeOf(u64) | |
| 1976 | .flags = macho.S_NON_LAZY_SYMBOL_POINTERS, | |
| 1977 | }); | |
| 1978 | } | |
| 1979 | ||
| 1980 | if (self.data_segment_cmd_index == null) { | |
| 1981 | self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 1982 | try self.load_commands.append(self.allocator, .{ | |
| 1983 | .Segment = SegmentCommand.empty("__DATA", .{ | |
| 1984 | .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, | |
| 1985 | .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE, | |
| 1986 | }), | |
| 1987 | }); | |
| 1988 | } | |
| 1989 | ||
| 1990 | if (self.la_symbol_ptr_section_index == null) { | |
| 1991 | const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 1992 | self.la_symbol_ptr_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 1993 | try data_seg.addSection(self.allocator, "__la_symbol_ptr", .{ | |
| 1994 | .@"align" = 3, // 2^3 = @sizeOf(u64) | |
| 1995 | .flags = macho.S_LAZY_SYMBOL_POINTERS, | |
| 1996 | }); | |
| 1997 | } | |
| 1998 | ||
| 1999 | if (self.data_section_index == null) { | |
| 2000 | const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2001 | self.data_section_index = @intCast(u16, data_seg.sections.items.len); | |
| 2002 | try data_seg.addSection(self.allocator, "__data", .{ | |
| 2003 | .@"align" = 3, // 2^3 = @sizeOf(u64) | |
| 2004 | }); | |
| 2005 | } | |
| 2006 | ||
| 2007 | if (self.linkedit_segment_cmd_index == null) { | |
| 2008 | self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2009 | try self.load_commands.append(self.allocator, .{ | |
| 2010 | .Segment = SegmentCommand.empty("__LINKEDIT", .{ | |
| 2011 | .maxprot = macho.VM_PROT_READ, | |
| 2012 | .initprot = macho.VM_PROT_READ, | |
| 2013 | }), | |
| 2014 | }); | |
| 2015 | } | |
| 2016 | ||
| 2017 | if (self.dyld_info_cmd_index == null) { | |
| 2018 | self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2019 | try self.load_commands.append(self.allocator, .{ | |
| 2020 | .DyldInfoOnly = .{ | |
| 2021 | .cmd = macho.LC_DYLD_INFO_ONLY, | |
| 2022 | .cmdsize = @sizeOf(macho.dyld_info_command), | |
| 2023 | .rebase_off = 0, | |
| 2024 | .rebase_size = 0, | |
| 2025 | .bind_off = 0, | |
| 2026 | .bind_size = 0, | |
| 2027 | .weak_bind_off = 0, | |
| 2028 | .weak_bind_size = 0, | |
| 2029 | .lazy_bind_off = 0, | |
| 2030 | .lazy_bind_size = 0, | |
| 2031 | .export_off = 0, | |
| 2032 | .export_size = 0, | |
| 2033 | }, | |
| 2034 | }); | |
| 2035 | } | |
| 2036 | ||
| 2037 | if (self.symtab_cmd_index == null) { | |
| 2038 | self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2039 | try self.load_commands.append(self.allocator, .{ | |
| 2040 | .Symtab = .{ | |
| 2041 | .cmd = macho.LC_SYMTAB, | |
| 2042 | .cmdsize = @sizeOf(macho.symtab_command), | |
| 2043 | .symoff = 0, | |
| 2044 | .nsyms = 0, | |
| 2045 | .stroff = 0, | |
| 2046 | .strsize = 0, | |
| 2047 | }, | |
| 2048 | }); | |
| 2049 | } | |
| 2050 | ||
| 2051 | if (self.dysymtab_cmd_index == null) { | |
| 2052 | self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2053 | try self.load_commands.append(self.allocator, .{ | |
| 2054 | .Dysymtab = .{ | |
| 2055 | .cmd = macho.LC_DYSYMTAB, | |
| 2056 | .cmdsize = @sizeOf(macho.dysymtab_command), | |
| 2057 | .ilocalsym = 0, | |
| 2058 | .nlocalsym = 0, | |
| 2059 | .iextdefsym = 0, | |
| 2060 | .nextdefsym = 0, | |
| 2061 | .iundefsym = 0, | |
| 2062 | .nundefsym = 0, | |
| 2063 | .tocoff = 0, | |
| 2064 | .ntoc = 0, | |
| 2065 | .modtaboff = 0, | |
| 2066 | .nmodtab = 0, | |
| 2067 | .extrefsymoff = 0, | |
| 2068 | .nextrefsyms = 0, | |
| 2069 | .indirectsymoff = 0, | |
| 2070 | .nindirectsyms = 0, | |
| 2071 | .extreloff = 0, | |
| 2072 | .nextrel = 0, | |
| 2073 | .locreloff = 0, | |
| 2074 | .nlocrel = 0, | |
| 2075 | }, | |
| 2076 | }); | |
| 2077 | } | |
| 2078 | ||
| 2079 | if (self.dylinker_cmd_index == null) { | |
| 2080 | self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2081 | const cmdsize = @intCast(u32, mem.alignForwardGeneric( | |
| 2082 | u64, | |
| 2083 | @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH), | |
| 2084 | @sizeOf(u64), | |
| 2085 | )); | |
| 2086 | var dylinker_cmd = emptyGenericCommandWithData(macho.dylinker_command{ | |
| 2087 | .cmd = macho.LC_LOAD_DYLINKER, | |
| 2088 | .cmdsize = cmdsize, | |
| 2089 | .name = @sizeOf(macho.dylinker_command), | |
| 2090 | }); | |
| 2091 | dylinker_cmd.data = try self.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name); | |
| 2092 | mem.set(u8, dylinker_cmd.data, 0); | |
| 2093 | mem.copy(u8, dylinker_cmd.data, mem.spanZ(DEFAULT_DYLD_PATH)); | |
| 2094 | try self.load_commands.append(self.allocator, .{ .Dylinker = dylinker_cmd }); | |
| 2095 | } | |
| 2096 | ||
| 2097 | if (self.main_cmd_index == null and self.output.?.tag == .exe) { | |
| 2098 | self.main_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2099 | try self.load_commands.append(self.allocator, .{ | |
| 2100 | .Main = .{ | |
| 2101 | .cmd = macho.LC_MAIN, | |
| 2102 | .cmdsize = @sizeOf(macho.entry_point_command), | |
| 2103 | .entryoff = 0x0, | |
| 2104 | .stacksize = 0, | |
| 2105 | }, | |
| 2106 | }); | |
| 2107 | } | |
| 2108 | ||
| 2109 | if (self.dylib_id_cmd_index == null and self.output.?.tag == .dylib) { | |
| 2110 | self.dylib_id_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2111 | var dylib_cmd = try createLoadDylibCommand( | |
| 2112 | self.allocator, | |
| 2113 | self.output.?.install_name.?, | |
| 2114 | 2, | |
| 2115 | 0x10000, // TODO forward user-provided versions | |
| 2116 | 0x10000, | |
| 2117 | ); | |
| 2118 | errdefer dylib_cmd.deinit(self.allocator); | |
| 2119 | dylib_cmd.inner.cmd = macho.LC_ID_DYLIB; | |
| 2120 | try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd }); | |
| 2121 | } | |
| 2122 | ||
| 2123 | if (self.version_min_cmd_index == null) { | |
| 2124 | self.version_min_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2125 | const cmd: u32 = switch (self.target.?.os.tag) { | |
| 2126 | .macos => macho.LC_VERSION_MIN_MACOSX, | |
| 2127 | .ios => macho.LC_VERSION_MIN_IPHONEOS, | |
| 2128 | .tvos => macho.LC_VERSION_MIN_TVOS, | |
| 2129 | .watchos => macho.LC_VERSION_MIN_WATCHOS, | |
| 2130 | else => unreachable, // wrong OS | |
| 2131 | }; | |
| 2132 | const ver = self.target.?.os.version_range.semver.min; | |
| 2133 | const version = ver.major << 16 | ver.minor << 8 | ver.patch; | |
| 2134 | try self.load_commands.append(self.allocator, .{ | |
| 2135 | .VersionMin = .{ | |
| 2136 | .cmd = cmd, | |
| 2137 | .cmdsize = @sizeOf(macho.version_min_command), | |
| 2138 | .version = version, | |
| 2139 | .sdk = version, | |
| 2140 | }, | |
| 2141 | }); | |
| 2142 | } | |
| 2143 | ||
| 2144 | if (self.source_version_cmd_index == null) { | |
| 2145 | self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2146 | try self.load_commands.append(self.allocator, .{ | |
| 2147 | .SourceVersion = .{ | |
| 2148 | .cmd = macho.LC_SOURCE_VERSION, | |
| 2149 | .cmdsize = @sizeOf(macho.source_version_command), | |
| 2150 | .version = 0x0, | |
| 2151 | }, | |
| 2152 | }); | |
| 2153 | } | |
| 2154 | ||
| 2155 | if (self.uuid_cmd_index == null) { | |
| 2156 | self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2157 | var uuid_cmd: macho.uuid_command = .{ | |
| 2158 | .cmd = macho.LC_UUID, | |
| 2159 | .cmdsize = @sizeOf(macho.uuid_command), | |
| 2160 | .uuid = undefined, | |
| 2161 | }; | |
| 2162 | std.crypto.random.bytes(&uuid_cmd.uuid); | |
| 2163 | try self.load_commands.append(self.allocator, .{ .Uuid = uuid_cmd }); | |
| 2164 | } | |
| 2165 | } | |
| 2166 | ||
| 2167 | fn addDataInCodeLC(self: *Zld) !void { | |
| 2168 | if (self.data_in_code_cmd_index == null) { | |
| 2169 | self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2170 | try self.load_commands.append(self.allocator, .{ | |
| 2171 | .LinkeditData = .{ | |
| 2172 | .cmd = macho.LC_DATA_IN_CODE, | |
| 2173 | .cmdsize = @sizeOf(macho.linkedit_data_command), | |
| 2174 | .dataoff = 0, | |
| 2175 | .datasize = 0, | |
| 2176 | }, | |
| 2177 | }); | |
| 2178 | } | |
| 2179 | } | |
| 2180 | ||
| 2181 | fn addCodeSignatureLC(self: *Zld) !void { | |
| 2182 | if (self.code_signature_cmd_index == null and self.target.?.cpu.arch == .aarch64) { | |
| 2183 | self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); | |
| 2184 | try self.load_commands.append(self.allocator, .{ | |
| 2185 | .LinkeditData = .{ | |
| 2186 | .cmd = macho.LC_CODE_SIGNATURE, | |
| 2187 | .cmdsize = @sizeOf(macho.linkedit_data_command), | |
| 2188 | .dataoff = 0, | |
| 2189 | .datasize = 0, | |
| 2190 | }, | |
| 2191 | }); | |
| 2192 | } | |
| 2193 | } | |
| 2194 | ||
| 2195 | fn addRpaths(self: *Zld, rpaths: []const []const u8) !void { | |
| 2196 | for (rpaths) |rpath| { | |
| 2197 | const cmdsize = @intCast(u32, mem.alignForwardGeneric( | |
| 2198 | u64, | |
| 2199 | @sizeOf(macho.rpath_command) + rpath.len + 1, | |
| 2200 | @sizeOf(u64), | |
| 2201 | )); | |
| 2202 | var rpath_cmd = emptyGenericCommandWithData(macho.rpath_command{ | |
| 2203 | .cmd = macho.LC_RPATH, | |
| 2204 | .cmdsize = cmdsize, | |
| 2205 | .path = @sizeOf(macho.rpath_command), | |
| 2206 | }); | |
| 2207 | rpath_cmd.data = try self.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path); | |
| 2208 | mem.set(u8, rpath_cmd.data, 0); | |
| 2209 | mem.copy(u8, rpath_cmd.data, rpath); | |
| 2210 | try self.load_commands.append(self.allocator, .{ .Rpath = rpath_cmd }); | |
| 2211 | } | |
| 2212 | } | |
| 2213 | ||
| 2214 | fn flush(self: *Zld) !void { | |
| 2215 | try self.writeTextBlocks(); | |
| 2216 | try self.writeStubHelperCommon(); | |
| 2217 | ||
| 2218 | if (self.common_section_index) |index| { | |
| 2219 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2220 | const sect = &seg.sections.items[index]; | |
| 2221 | sect.offset = 0; | |
| 2222 | } | |
| 2223 | ||
| 2224 | if (self.bss_section_index) |index| { | |
| 2225 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2226 | const sect = &seg.sections.items[index]; | |
| 2227 | sect.offset = 0; | |
| 2228 | } | |
| 2229 | ||
| 2230 | if (self.tlv_bss_section_index) |index| { | |
| 2231 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2232 | const sect = &seg.sections.items[index]; | |
| 2233 | sect.offset = 0; | |
| 2234 | } | |
| 2235 | ||
| 2236 | try self.writeGotEntries(); | |
| 2237 | try self.setEntryPoint(); | |
| 2238 | try self.writeRebaseInfoTable(); | |
| 2239 | try self.writeBindInfoTable(); | |
| 2240 | try self.writeLazyBindInfoTable(); | |
| 2241 | try self.writeExportInfo(); | |
| 2242 | try self.writeDices(); | |
| 2243 | ||
| 2244 | { | |
| 2245 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2246 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | |
| 2247 | symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 2248 | } | |
| 2249 | ||
| 2250 | try self.writeSymbolTable(); | |
| 2251 | try self.writeStringTable(); | |
| 2252 | ||
| 2253 | { | |
| 2254 | // Seal __LINKEDIT size | |
| 2255 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2256 | seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size.?); | |
| 2257 | } | |
| 2258 | ||
| 2259 | if (self.target.?.cpu.arch == .aarch64) { | |
| 2260 | try self.writeCodeSignaturePadding(); | |
| 2261 | } | |
| 2262 | ||
| 2263 | try self.writeLoadCommands(); | |
| 2264 | try self.writeHeader(); | |
| 2265 | ||
| 2266 | if (self.target.?.cpu.arch == .aarch64) { | |
| 2267 | try self.writeCodeSignature(); | |
| 2268 | } | |
| 2269 | ||
| 2270 | if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64) { | |
| 2271 | const out_path = self.output.?.path; | |
| 2272 | try fs.cwd().copyFile(out_path, fs.cwd(), out_path, .{}); | |
| 2273 | } | |
| 2274 | } | |
| 2275 | ||
| 2276 | fn writeGotEntries(self: *Zld) !void { | |
| 2277 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 2278 | const sect = seg.sections.items[self.got_section_index.?]; | |
| 2279 | ||
| 2280 | var buffer = try self.allocator.alloc(u8, self.got_entries.count() * @sizeOf(u64)); | |
| 2281 | defer self.allocator.free(buffer); | |
| 2282 | ||
| 2283 | var stream = std.io.fixedBufferStream(buffer); | |
| 2284 | var writer = stream.writer(); | |
| 2285 | ||
| 2286 | for (self.got_entries.keys()) |key| { | |
| 2287 | const address: u64 = switch (key.where) { | |
| 2288 | .local => self.locals.items[key.where_index].n_value, | |
| 2289 | .import => 0, | |
| 2290 | }; | |
| 2291 | try writer.writeIntLittle(u64, address); | |
| 2292 | } | |
| 2293 | ||
| 2294 | log.debug("writing GOT pointers at 0x{x} to 0x{x}", .{ sect.offset, sect.offset + buffer.len }); | |
| 2295 | ||
| 2296 | try self.file.?.pwriteAll(buffer, sect.offset); | |
| 2297 | } | |
| 2298 | ||
| 2299 | fn setEntryPoint(self: *Zld) !void { | |
| 2300 | if (self.output.?.tag != .exe) return; | |
| 2301 | ||
| 2302 | // TODO we should respect the -entry flag passed in by the user to set a custom | |
| 2303 | // entrypoint. For now, assume default of `_main`. | |
| 2304 | const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 2305 | const resolv = self.symbol_resolver.get("_main") orelse { | |
| 2306 | log.err("'_main' export not found", .{}); | |
| 2307 | return error.MissingMainEntrypoint; | |
| 2308 | }; | |
| 2309 | assert(resolv.where == .global); | |
| 2310 | const sym = self.globals.items[resolv.where_index]; | |
| 2311 | const ec = &self.load_commands.items[self.main_cmd_index.?].Main; | |
| 2312 | ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr); | |
| 2313 | ec.stacksize = self.stack_size; | |
| 2314 | } | |
| 2315 | ||
| 2316 | fn writeRebaseInfoTable(self: *Zld) !void { | |
| 2317 | var pointers = std.ArrayList(Pointer).init(self.allocator); | |
| 2318 | defer pointers.deinit(); | |
| 2319 | ||
| 2320 | { | |
| 2321 | var it = self.blocks.iterator(); | |
| 2322 | while (it.next()) |entry| { | |
| 2323 | const match = entry.key_ptr.*; | |
| 2324 | var block: *TextBlock = entry.value_ptr.*; | |
| 2325 | ||
| 2326 | if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable | |
| 2327 | ||
| 2328 | const seg = self.load_commands.items[match.seg].Segment; | |
| 2329 | ||
| 2330 | while (true) { | |
| 2331 | const sym = self.locals.items[block.local_sym_index]; | |
| 2332 | const base_offset = sym.n_value - seg.inner.vmaddr; | |
| 2333 | ||
| 2334 | for (block.rebases.items) |offset| { | |
| 2335 | try pointers.append(.{ | |
| 2336 | .offset = base_offset + offset, | |
| 2337 | .segment_id = match.seg, | |
| 2338 | }); | |
| 2339 | } | |
| 2340 | ||
| 2341 | if (block.prev) |prev| { | |
| 2342 | block = prev; | |
| 2343 | } else break; | |
| 2344 | } | |
| 2345 | } | |
| 2346 | } | |
| 2347 | ||
| 2348 | if (self.got_section_index) |idx| { | |
| 2349 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 2350 | const sect = seg.sections.items[idx]; | |
| 2351 | const base_offset = sect.addr - seg.inner.vmaddr; | |
| 2352 | const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); | |
| 2353 | ||
| 2354 | for (self.got_entries.keys()) |key, i| { | |
| 2355 | if (key.where == .import) continue; | |
| 2356 | ||
| 2357 | try pointers.append(.{ | |
| 2358 | .offset = base_offset + i * @sizeOf(u64), | |
| 2359 | .segment_id = segment_id, | |
| 2360 | }); | |
| 2361 | } | |
| 2362 | } | |
| 2363 | ||
| 2364 | if (self.la_symbol_ptr_section_index) |idx| { | |
| 2365 | const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2366 | const sect = seg.sections.items[idx]; | |
| 2367 | const base_offset = sect.addr - seg.inner.vmaddr; | |
| 2368 | const segment_id = @intCast(u16, self.data_segment_cmd_index.?); | |
| 2369 | ||
| 2370 | try pointers.ensureUnusedCapacity(self.stubs.count()); | |
| 2371 | for (self.stubs.keys()) |_, i| { | |
| 2372 | pointers.appendAssumeCapacity(.{ | |
| 2373 | .offset = base_offset + i * @sizeOf(u64), | |
| 2374 | .segment_id = segment_id, | |
| 2375 | }); | |
| 2376 | } | |
| 2377 | } | |
| 2378 | ||
| 2379 | std.sort.sort(Pointer, pointers.items, {}, pointerCmp); | |
| 2380 | ||
| 2381 | const size = try rebaseInfoSize(pointers.items); | |
| 2382 | var buffer = try self.allocator.alloc(u8, @intCast(usize, size)); | |
| 2383 | defer self.allocator.free(buffer); | |
| 2384 | ||
| 2385 | var stream = std.io.fixedBufferStream(buffer); | |
| 2386 | try writeRebaseInfo(pointers.items, stream.writer()); | |
| 2387 | ||
| 2388 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2389 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | |
| 2390 | dyld_info.rebase_off = @intCast(u32, seg.inner.fileoff); | |
| 2391 | dyld_info.rebase_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @sizeOf(u64))); | |
| 2392 | seg.inner.filesize += dyld_info.rebase_size; | |
| 2393 | ||
| 2394 | log.debug("writing rebase info from 0x{x} to 0x{x}", .{ dyld_info.rebase_off, dyld_info.rebase_off + dyld_info.rebase_size }); | |
| 2395 | ||
| 2396 | try self.file.?.pwriteAll(buffer, dyld_info.rebase_off); | |
| 2397 | } | |
| 2398 | ||
| 2399 | fn writeBindInfoTable(self: *Zld) !void { | |
| 2400 | var pointers = std.ArrayList(Pointer).init(self.allocator); | |
| 2401 | defer pointers.deinit(); | |
| 2402 | ||
| 2403 | if (self.got_section_index) |idx| { | |
| 2404 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 2405 | const sect = seg.sections.items[idx]; | |
| 2406 | const base_offset = sect.addr - seg.inner.vmaddr; | |
| 2407 | const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); | |
| 2408 | ||
| 2409 | for (self.got_entries.keys()) |key, i| { | |
| 2410 | if (key.where == .local) continue; | |
| 2411 | ||
| 2412 | const sym = self.imports.items[key.where_index]; | |
| 2413 | try pointers.append(.{ | |
| 2414 | .offset = base_offset + i * @sizeOf(u64), | |
| 2415 | .segment_id = segment_id, | |
| 2416 | .dylib_ordinal = unpackDylibOrdinal(sym.n_desc), | |
| 2417 | .name = self.getString(sym.n_strx), | |
| 2418 | }); | |
| 2419 | } | |
| 2420 | } | |
| 2421 | ||
| 2422 | { | |
| 2423 | var it = self.blocks.iterator(); | |
| 2424 | while (it.next()) |entry| { | |
| 2425 | const match = entry.key_ptr.*; | |
| 2426 | var block: *TextBlock = entry.value_ptr.*; | |
| 2427 | ||
| 2428 | if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable | |
| 2429 | ||
| 2430 | const seg = self.load_commands.items[match.seg].Segment; | |
| 2431 | ||
| 2432 | while (true) { | |
| 2433 | const sym = self.locals.items[block.local_sym_index]; | |
| 2434 | const base_offset = sym.n_value - seg.inner.vmaddr; | |
| 2435 | ||
| 2436 | for (block.bindings.items) |binding| { | |
| 2437 | const bind_sym = self.imports.items[binding.local_sym_index]; | |
| 2438 | try pointers.append(.{ | |
| 2439 | .offset = binding.offset + base_offset, | |
| 2440 | .segment_id = match.seg, | |
| 2441 | .dylib_ordinal = unpackDylibOrdinal(bind_sym.n_desc), | |
| 2442 | .name = self.getString(bind_sym.n_strx), | |
| 2443 | }); | |
| 2444 | } | |
| 2445 | ||
| 2446 | if (block.prev) |prev| { | |
| 2447 | block = prev; | |
| 2448 | } else break; | |
| 2449 | } | |
| 2450 | } | |
| 2451 | } | |
| 2452 | ||
| 2453 | const size = try bindInfoSize(pointers.items); | |
| 2454 | var buffer = try self.allocator.alloc(u8, @intCast(usize, size)); | |
| 2455 | defer self.allocator.free(buffer); | |
| 2456 | ||
| 2457 | var stream = std.io.fixedBufferStream(buffer); | |
| 2458 | try writeBindInfo(pointers.items, stream.writer()); | |
| 2459 | ||
| 2460 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2461 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | |
| 2462 | dyld_info.bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 2463 | dyld_info.bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64))); | |
| 2464 | seg.inner.filesize += dyld_info.bind_size; | |
| 2465 | ||
| 2466 | log.debug("writing binding info from 0x{x} to 0x{x}", .{ dyld_info.bind_off, dyld_info.bind_off + dyld_info.bind_size }); | |
| 2467 | ||
| 2468 | try self.file.?.pwriteAll(buffer, dyld_info.bind_off); | |
| 2469 | } | |
| 2470 | ||
| 2471 | fn writeLazyBindInfoTable(self: *Zld) !void { | |
| 2472 | var pointers = std.ArrayList(Pointer).init(self.allocator); | |
| 2473 | defer pointers.deinit(); | |
| 2474 | ||
| 2475 | if (self.la_symbol_ptr_section_index) |idx| { | |
| 2476 | const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2477 | const sect = seg.sections.items[idx]; | |
| 2478 | const base_offset = sect.addr - seg.inner.vmaddr; | |
| 2479 | const segment_id = @intCast(u16, self.data_segment_cmd_index.?); | |
| 2480 | ||
| 2481 | try pointers.ensureUnusedCapacity(self.stubs.count()); | |
| 2482 | ||
| 2483 | for (self.stubs.keys()) |key, i| { | |
| 2484 | const sym = self.imports.items[key]; | |
| 2485 | pointers.appendAssumeCapacity(.{ | |
| 2486 | .offset = base_offset + i * @sizeOf(u64), | |
| 2487 | .segment_id = segment_id, | |
| 2488 | .dylib_ordinal = unpackDylibOrdinal(sym.n_desc), | |
| 2489 | .name = self.getString(sym.n_strx), | |
| 2490 | }); | |
| 2491 | } | |
| 2492 | } | |
| 2493 | ||
| 2494 | const size = try lazyBindInfoSize(pointers.items); | |
| 2495 | var buffer = try self.allocator.alloc(u8, @intCast(usize, size)); | |
| 2496 | defer self.allocator.free(buffer); | |
| 2497 | ||
| 2498 | var stream = std.io.fixedBufferStream(buffer); | |
| 2499 | try writeLazyBindInfo(pointers.items, stream.writer()); | |
| 2500 | ||
| 2501 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2502 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | |
| 2503 | dyld_info.lazy_bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 2504 | dyld_info.lazy_bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64))); | |
| 2505 | seg.inner.filesize += dyld_info.lazy_bind_size; | |
| 2506 | ||
| 2507 | log.debug("writing lazy binding info from 0x{x} to 0x{x}", .{ dyld_info.lazy_bind_off, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size }); | |
| 2508 | ||
| 2509 | try self.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off); | |
| 2510 | try self.populateLazyBindOffsetsInStubHelper(buffer); | |
| 2511 | } | |
| 2512 | ||
| 2513 | fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void { | |
| 2514 | var stream = std.io.fixedBufferStream(buffer); | |
| 2515 | var reader = stream.reader(); | |
| 2516 | var offsets = std.ArrayList(u32).init(self.allocator); | |
| 2517 | try offsets.append(0); | |
| 2518 | defer offsets.deinit(); | |
| 2519 | var valid_block = false; | |
| 2520 | ||
| 2521 | while (true) { | |
| 2522 | const inst = reader.readByte() catch |err| switch (err) { | |
| 2523 | error.EndOfStream => break, | |
| 2524 | else => return err, | |
| 2525 | }; | |
| 2526 | const opcode: u8 = inst & macho.BIND_OPCODE_MASK; | |
| 2527 | ||
| 2528 | switch (opcode) { | |
| 2529 | macho.BIND_OPCODE_DO_BIND => { | |
| 2530 | valid_block = true; | |
| 2531 | }, | |
| 2532 | macho.BIND_OPCODE_DONE => { | |
| 2533 | if (valid_block) { | |
| 2534 | const offset = try stream.getPos(); | |
| 2535 | try offsets.append(@intCast(u32, offset)); | |
| 2536 | } | |
| 2537 | valid_block = false; | |
| 2538 | }, | |
| 2539 | macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => { | |
| 2540 | var next = try reader.readByte(); | |
| 2541 | while (next != @as(u8, 0)) { | |
| 2542 | next = try reader.readByte(); | |
| 2543 | } | |
| 2544 | }, | |
| 2545 | macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => { | |
| 2546 | _ = try leb.readULEB128(u64, reader); | |
| 2547 | }, | |
| 2548 | macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => { | |
| 2549 | _ = try leb.readULEB128(u64, reader); | |
| 2550 | }, | |
| 2551 | macho.BIND_OPCODE_SET_ADDEND_SLEB => { | |
| 2552 | _ = try leb.readILEB128(i64, reader); | |
| 2553 | }, | |
| 2554 | else => {}, | |
| 2555 | } | |
| 2556 | } | |
| 2557 | assert(self.stubs.count() <= offsets.items.len); | |
| 2558 | ||
| 2559 | const stub_size: u4 = switch (self.target.?.cpu.arch) { | |
| 2560 | .x86_64 => 10, | |
| 2561 | .aarch64 => 3 * @sizeOf(u32), | |
| 2562 | else => unreachable, | |
| 2563 | }; | |
| 2564 | const off: u4 = switch (self.target.?.cpu.arch) { | |
| 2565 | .x86_64 => 1, | |
| 2566 | .aarch64 => 2 * @sizeOf(u32), | |
| 2567 | else => unreachable, | |
| 2568 | }; | |
| 2569 | var buf: [@sizeOf(u32)]u8 = undefined; | |
| 2570 | for (self.stubs.keys()) |_, index| { | |
| 2571 | const placeholder_off = self.stub_helper_stubs_start_off.? + index * stub_size + off; | |
| 2572 | mem.writeIntLittle(u32, &buf, offsets.items[index]); | |
| 2573 | try self.file.?.pwriteAll(&buf, placeholder_off); | |
| 2574 | } | |
| 2575 | } | |
| 2576 | ||
| 2577 | fn writeExportInfo(self: *Zld) !void { | |
| 2578 | var trie = Trie.init(self.allocator); | |
| 2579 | defer trie.deinit(); | |
| 2580 | ||
| 2581 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 2582 | const base_address = text_segment.inner.vmaddr; | |
| 2583 | ||
| 2584 | // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER. | |
| 2585 | log.debug("writing export trie", .{}); | |
| 2586 | ||
| 2587 | for (self.globals.items) |sym| { | |
| 2588 | const sym_name = self.getString(sym.n_strx); | |
| 2589 | log.debug(" | putting '{s}' defined at 0x{x}", .{ sym_name, sym.n_value }); | |
| 2590 | ||
| 2591 | try trie.put(.{ | |
| 2592 | .name = sym_name, | |
| 2593 | .vmaddr_offset = sym.n_value - base_address, | |
| 2594 | .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR, | |
| 2595 | }); | |
| 2596 | } | |
| 2597 | ||
| 2598 | try trie.finalize(); | |
| 2599 | ||
| 2600 | var buffer = try self.allocator.alloc(u8, @intCast(usize, trie.size)); | |
| 2601 | defer self.allocator.free(buffer); | |
| 2602 | ||
| 2603 | var stream = std.io.fixedBufferStream(buffer); | |
| 2604 | const nwritten = try trie.write(stream.writer()); | |
| 2605 | assert(nwritten == trie.size); | |
| 2606 | ||
| 2607 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2608 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | |
| 2609 | dyld_info.export_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 2610 | dyld_info.export_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64))); | |
| 2611 | seg.inner.filesize += dyld_info.export_size; | |
| 2612 | ||
| 2613 | log.debug("writing export info from 0x{x} to 0x{x}", .{ dyld_info.export_off, dyld_info.export_off + dyld_info.export_size }); | |
| 2614 | ||
| 2615 | try self.file.?.pwriteAll(buffer, dyld_info.export_off); | |
| 2616 | } | |
| 2617 | ||
| 2618 | fn writeSymbolTable(self: *Zld) !void { | |
| 2619 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2620 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | |
| 2621 | ||
| 2622 | var locals = std.ArrayList(macho.nlist_64).init(self.allocator); | |
| 2623 | defer locals.deinit(); | |
| 2624 | try locals.appendSlice(self.locals.items); | |
| 2625 | ||
| 2626 | if (self.has_stabs) { | |
| 2627 | for (self.objects.items) |object| { | |
| 2628 | if (object.debug_info == null) continue; | |
| 2629 | ||
| 2630 | // Open scope | |
| 2631 | try locals.ensureUnusedCapacity(4); | |
| 2632 | locals.appendAssumeCapacity(.{ | |
| 2633 | .n_strx = try self.makeString(object.tu_comp_dir.?), | |
| 2634 | .n_type = macho.N_SO, | |
| 2635 | .n_sect = 0, | |
| 2636 | .n_desc = 0, | |
| 2637 | .n_value = 0, | |
| 2638 | }); | |
| 2639 | locals.appendAssumeCapacity(.{ | |
| 2640 | .n_strx = try self.makeString(object.tu_name.?), | |
| 2641 | .n_type = macho.N_SO, | |
| 2642 | .n_sect = 0, | |
| 2643 | .n_desc = 0, | |
| 2644 | .n_value = 0, | |
| 2645 | }); | |
| 2646 | locals.appendAssumeCapacity(.{ | |
| 2647 | .n_strx = try self.makeString(object.name.?), | |
| 2648 | .n_type = macho.N_OSO, | |
| 2649 | .n_sect = 0, | |
| 2650 | .n_desc = 1, | |
| 2651 | .n_value = object.mtime orelse 0, | |
| 2652 | }); | |
| 2653 | ||
| 2654 | for (object.text_blocks.items) |block| { | |
| 2655 | if (block.stab) |stab| { | |
| 2656 | const nlists = try stab.asNlists(block.local_sym_index, self); | |
| 2657 | defer self.allocator.free(nlists); | |
| 2658 | try locals.appendSlice(nlists); | |
| 2659 | } else { | |
| 2660 | const contained = block.contained orelse continue; | |
| 2661 | for (contained) |sym_at_off| { | |
| 2662 | const stab = sym_at_off.stab orelse continue; | |
| 2663 | const nlists = try stab.asNlists(sym_at_off.local_sym_index, self); | |
| 2664 | defer self.allocator.free(nlists); | |
| 2665 | try locals.appendSlice(nlists); | |
| 2666 | } | |
| 2667 | } | |
| 2668 | } | |
| 2669 | ||
| 2670 | // Close scope | |
| 2671 | locals.appendAssumeCapacity(.{ | |
| 2672 | .n_strx = 0, | |
| 2673 | .n_type = macho.N_SO, | |
| 2674 | .n_sect = 0, | |
| 2675 | .n_desc = 0, | |
| 2676 | .n_value = 0, | |
| 2677 | }); | |
| 2678 | } | |
| 2679 | } | |
| 2680 | ||
| 2681 | const nlocals = locals.items.len; | |
| 2682 | const nexports = self.globals.items.len; | |
| 2683 | const nundefs = self.imports.items.len; | |
| 2684 | ||
| 2685 | const locals_off = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64); | |
| 2686 | const locals_size = nlocals * @sizeOf(macho.nlist_64); | |
| 2687 | log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off }); | |
| 2688 | try self.file.?.pwriteAll(mem.sliceAsBytes(locals.items), locals_off); | |
| 2689 | ||
| 2690 | const exports_off = locals_off + locals_size; | |
| 2691 | const exports_size = nexports * @sizeOf(macho.nlist_64); | |
| 2692 | log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off }); | |
| 2693 | try self.file.?.pwriteAll(mem.sliceAsBytes(self.globals.items), exports_off); | |
| 2694 | ||
| 2695 | const undefs_off = exports_off + exports_size; | |
| 2696 | const undefs_size = nundefs * @sizeOf(macho.nlist_64); | |
| 2697 | log.debug("writing undefined symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off }); | |
| 2698 | try self.file.?.pwriteAll(mem.sliceAsBytes(self.imports.items), undefs_off); | |
| 2699 | ||
| 2700 | symtab.nsyms += @intCast(u32, nlocals + nexports + nundefs); | |
| 2701 | seg.inner.filesize += locals_size + exports_size + undefs_size; | |
| 2702 | ||
| 2703 | // Update dynamic symbol table. | |
| 2704 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; | |
| 2705 | dysymtab.nlocalsym += @intCast(u32, nlocals); | |
| 2706 | dysymtab.iextdefsym = dysymtab.nlocalsym; | |
| 2707 | dysymtab.nextdefsym = @intCast(u32, nexports); | |
| 2708 | dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym; | |
| 2709 | dysymtab.nundefsym = @intCast(u32, nundefs); | |
| 2710 | ||
| 2711 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 2712 | const stubs = &text_segment.sections.items[self.stubs_section_index.?]; | |
| 2713 | const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; | |
| 2714 | const got = &data_const_segment.sections.items[self.got_section_index.?]; | |
| 2715 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | |
| 2716 | const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?]; | |
| 2717 | ||
| 2718 | const nstubs = @intCast(u32, self.stubs.count()); | |
| 2719 | const ngot_entries = @intCast(u32, self.got_entries.count()); | |
| 2720 | ||
| 2721 | dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 2722 | dysymtab.nindirectsyms = nstubs * 2 + ngot_entries; | |
| 2723 | ||
| 2724 | const needed_size = dysymtab.nindirectsyms * @sizeOf(u32); | |
| 2725 | seg.inner.filesize += needed_size; | |
| 2726 | ||
| 2727 | log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ | |
| 2728 | dysymtab.indirectsymoff, | |
| 2729 | dysymtab.indirectsymoff + needed_size, | |
| 2730 | }); | |
| 2731 | ||
| 2732 | var buf = try self.allocator.alloc(u8, needed_size); | |
| 2733 | defer self.allocator.free(buf); | |
| 2734 | ||
| 2735 | var stream = std.io.fixedBufferStream(buf); | |
| 2736 | var writer = stream.writer(); | |
| 2737 | ||
| 2738 | stubs.reserved1 = 0; | |
| 2739 | for (self.stubs.keys()) |key| { | |
| 2740 | try writer.writeIntLittle(u32, dysymtab.iundefsym + key); | |
| 2741 | } | |
| 2742 | ||
| 2743 | got.reserved1 = nstubs; | |
| 2744 | for (self.got_entries.keys()) |key| { | |
| 2745 | switch (key.where) { | |
| 2746 | .import => { | |
| 2747 | try writer.writeIntLittle(u32, dysymtab.iundefsym + key.where_index); | |
| 2748 | }, | |
| 2749 | .local => { | |
| 2750 | try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); | |
| 2751 | }, | |
| 2752 | } | |
| 2753 | } | |
| 2754 | ||
| 2755 | la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries; | |
| 2756 | for (self.stubs.keys()) |key| { | |
| 2757 | try writer.writeIntLittle(u32, dysymtab.iundefsym + key); | |
| 2758 | } | |
| 2759 | ||
| 2760 | try self.file.?.pwriteAll(buf, dysymtab.indirectsymoff); | |
| 2761 | } | |
| 2762 | ||
| 2763 | fn writeStringTable(self: *Zld) !void { | |
| 2764 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2765 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | |
| 2766 | symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | |
| 2767 | symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64))); | |
| 2768 | seg.inner.filesize += symtab.strsize; | |
| 2769 | ||
| 2770 | log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize }); | |
| 2771 | ||
| 2772 | try self.file.?.pwriteAll(self.strtab.items, symtab.stroff); | |
| 2773 | ||
| 2774 | if (symtab.strsize > self.strtab.items.len and self.target.?.cpu.arch == .x86_64) { | |
| 2775 | // This is the last section, so we need to pad it out. | |
| 2776 | try self.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1); | |
| 2777 | } | |
| 2778 | } | |
| 2779 | ||
| 2780 | fn writeDices(self: *Zld) !void { | |
| 2781 | if (!self.has_dices) return; | |
| 2782 | ||
| 2783 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2784 | const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData; | |
| 2785 | const fileoff = seg.inner.fileoff + seg.inner.filesize; | |
| 2786 | ||
| 2787 | var buf = std.ArrayList(u8).init(self.allocator); | |
| 2788 | defer buf.deinit(); | |
| 2789 | ||
| 2790 | var block: *TextBlock = self.blocks.get(.{ | |
| 2791 | .seg = self.text_segment_cmd_index orelse return, | |
| 2792 | .sect = self.text_section_index orelse return, | |
| 2793 | }) orelse return; | |
| 2794 | ||
| 2795 | while (block.prev) |prev| { | |
| 2796 | block = prev; | |
| 2797 | } | |
| 2798 | ||
| 2799 | const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 2800 | const text_sect = text_seg.sections.items[self.text_section_index.?]; | |
| 2801 | ||
| 2802 | while (true) { | |
| 2803 | if (block.dices.items.len > 0) { | |
| 2804 | const sym = self.locals.items[block.local_sym_index]; | |
| 2805 | const base_off = try math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset); | |
| 2806 | ||
| 2807 | try buf.ensureUnusedCapacity(block.dices.items.len * @sizeOf(macho.data_in_code_entry)); | |
| 2808 | for (block.dices.items) |dice| { | |
| 2809 | const rebased_dice = macho.data_in_code_entry{ | |
| 2810 | .offset = base_off + dice.offset, | |
| 2811 | .length = dice.length, | |
| 2812 | .kind = dice.kind, | |
| 2813 | }; | |
| 2814 | buf.appendSliceAssumeCapacity(mem.asBytes(&rebased_dice)); | |
| 2815 | } | |
| 2816 | } | |
| 2817 | ||
| 2818 | if (block.next) |next| { | |
| 2819 | block = next; | |
| 2820 | } else break; | |
| 2821 | } | |
| 2822 | ||
| 2823 | const datasize = @intCast(u32, buf.items.len); | |
| 2824 | ||
| 2825 | dice_cmd.dataoff = @intCast(u32, fileoff); | |
| 2826 | dice_cmd.datasize = datasize; | |
| 2827 | seg.inner.filesize += datasize; | |
| 2828 | ||
| 2829 | log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ fileoff, fileoff + datasize }); | |
| 2830 | ||
| 2831 | try self.file.?.pwriteAll(buf.items, fileoff); | |
| 2832 | } | |
| 2833 | ||
| 2834 | fn writeCodeSignaturePadding(self: *Zld) !void { | |
| 2835 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 2836 | const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; | |
| 2837 | const fileoff = seg.inner.fileoff + seg.inner.filesize; | |
| 2838 | const needed_size = CodeSignature.calcCodeSignaturePaddingSize( | |
| 2839 | self.output.?.path, | |
| 2840 | fileoff, | |
| 2841 | self.page_size.?, | |
| 2842 | ); | |
| 2843 | code_sig_cmd.dataoff = @intCast(u32, fileoff); | |
| 2844 | code_sig_cmd.datasize = needed_size; | |
| 2845 | ||
| 2846 | // Advance size of __LINKEDIT segment | |
| 2847 | seg.inner.filesize += needed_size; | |
| 2848 | seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size.?); | |
| 2849 | ||
| 2850 | log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size }); | |
| 2851 | ||
| 2852 | // Pad out the space. We need to do this to calculate valid hashes for everything in the file | |
| 2853 | // except for code signature data. | |
| 2854 | try self.file.?.pwriteAll(&[_]u8{0}, fileoff + needed_size - 1); | |
| 2855 | } | |
| 2856 | ||
| 2857 | fn writeCodeSignature(self: *Zld) !void { | |
| 2858 | const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 2859 | const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; | |
| 2860 | ||
| 2861 | var code_sig = CodeSignature.init(self.allocator, self.page_size.?); | |
| 2862 | defer code_sig.deinit(); | |
| 2863 | try code_sig.calcAdhocSignature( | |
| 2864 | self.file.?, | |
| 2865 | self.output.?.path, | |
| 2866 | text_seg.inner, | |
| 2867 | code_sig_cmd, | |
| 2868 | .Exe, | |
| 2869 | ); | |
| 2870 | ||
| 2871 | var buffer = try self.allocator.alloc(u8, code_sig.size()); | |
| 2872 | defer self.allocator.free(buffer); | |
| 2873 | var stream = std.io.fixedBufferStream(buffer); | |
| 2874 | try code_sig.write(stream.writer()); | |
| 2875 | ||
| 2876 | log.debug("writing code signature from 0x{x} to 0x{x}", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len }); | |
| 2877 | try self.file.?.pwriteAll(buffer, code_sig_cmd.dataoff); | |
| 2878 | } | |
| 2879 | ||
| 2880 | fn writeLoadCommands(self: *Zld) !void { | |
| 2881 | var sizeofcmds: u32 = 0; | |
| 2882 | for (self.load_commands.items) |lc| { | |
| 2883 | sizeofcmds += lc.cmdsize(); | |
| 2884 | } | |
| 2885 | ||
| 2886 | var buffer = try self.allocator.alloc(u8, sizeofcmds); | |
| 2887 | defer self.allocator.free(buffer); | |
| 2888 | var writer = std.io.fixedBufferStream(buffer).writer(); | |
| 2889 | for (self.load_commands.items) |lc| { | |
| 2890 | try lc.write(writer); | |
| 2891 | } | |
| 2892 | ||
| 2893 | const off = @sizeOf(macho.mach_header_64); | |
| 2894 | log.debug("writing {} load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds }); | |
| 2895 | try self.file.?.pwriteAll(buffer, off); | |
| 2896 | } | |
| 2897 | ||
| 2898 | fn writeHeader(self: *Zld) !void { | |
| 2899 | var header = emptyHeader(.{ | |
| 2900 | .flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL, | |
| 2901 | }); | |
| 2902 | ||
| 2903 | switch (self.target.?.cpu.arch) { | |
| 2904 | .aarch64 => { | |
| 2905 | header.cputype = macho.CPU_TYPE_ARM64; | |
| 2906 | header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL; | |
| 2907 | }, | |
| 2908 | .x86_64 => { | |
| 2909 | header.cputype = macho.CPU_TYPE_X86_64; | |
| 2910 | header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL; | |
| 2911 | }, | |
| 2912 | else => return error.UnsupportedCpuArchitecture, | |
| 2913 | } | |
| 2914 | ||
| 2915 | switch (self.output.?.tag) { | |
| 2916 | .exe => { | |
| 2917 | header.filetype = macho.MH_EXECUTE; | |
| 2918 | }, | |
| 2919 | .dylib => { | |
| 2920 | header.filetype = macho.MH_DYLIB; | |
| 2921 | header.flags |= macho.MH_NO_REEXPORTED_DYLIBS; | |
| 2922 | }, | |
| 2923 | } | |
| 2924 | ||
| 2925 | if (self.tlv_section_index) |_| | |
| 2926 | header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; | |
| 2927 | ||
| 2928 | header.ncmds = @intCast(u32, self.load_commands.items.len); | |
| 2929 | header.sizeofcmds = 0; | |
| 2930 | ||
| 2931 | for (self.load_commands.items) |cmd| { | |
| 2932 | header.sizeofcmds += cmd.cmdsize(); | |
| 2933 | } | |
| 2934 | ||
| 2935 | log.debug("writing Mach-O header {}", .{header}); | |
| 2936 | ||
| 2937 | try self.file.?.pwriteAll(mem.asBytes(&header), 0); | |
| 2938 | } | |
| 2939 | ||
| 2940 | pub fn makeString(self: *Zld, string: []const u8) !u32 { | |
| 2941 | try self.strtab.ensureUnusedCapacity(self.allocator, string.len + 1); | |
| 2942 | const new_off = @intCast(u32, self.strtab.items.len); | |
| 2943 | ||
| 2944 | log.debug("writing new string '{s}' at offset 0x{x}", .{ string, new_off }); | |
| 2945 | ||
| 2946 | self.strtab.appendSliceAssumeCapacity(string); | |
| 2947 | self.strtab.appendAssumeCapacity(0); | |
| 2948 | ||
| 2949 | return new_off; | |
| 2950 | } | |
| 2951 | ||
| 2952 | pub fn getString(self: *Zld, off: u32) []const u8 { | |
| 2953 | assert(off < self.strtab.items.len); | |
| 2954 | return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + off)); | |
| 2955 | } | |
| 2956 | ||
| 2957 | pub fn symbolIsStab(sym: macho.nlist_64) bool { | |
| 2958 | return (macho.N_STAB & sym.n_type) != 0; | |
| 2959 | } | |
| 2960 | ||
| 2961 | pub fn symbolIsPext(sym: macho.nlist_64) bool { | |
| 2962 | return (macho.N_PEXT & sym.n_type) != 0; | |
| 2963 | } | |
| 2964 | ||
| 2965 | pub fn symbolIsExt(sym: macho.nlist_64) bool { | |
| 2966 | return (macho.N_EXT & sym.n_type) != 0; | |
| 2967 | } | |
| 2968 | ||
| 2969 | pub fn symbolIsSect(sym: macho.nlist_64) bool { | |
| 2970 | const type_ = macho.N_TYPE & sym.n_type; | |
| 2971 | return type_ == macho.N_SECT; | |
| 2972 | } | |
| 2973 | ||
| 2974 | pub fn symbolIsUndf(sym: macho.nlist_64) bool { | |
| 2975 | const type_ = macho.N_TYPE & sym.n_type; | |
| 2976 | return type_ == macho.N_UNDF; | |
| 2977 | } | |
| 2978 | ||
| 2979 | pub fn symbolIsIndr(sym: macho.nlist_64) bool { | |
| 2980 | const type_ = macho.N_TYPE & sym.n_type; | |
| 2981 | return type_ == macho.N_INDR; | |
| 2982 | } | |
| 2983 | ||
| 2984 | pub fn symbolIsAbs(sym: macho.nlist_64) bool { | |
| 2985 | const type_ = macho.N_TYPE & sym.n_type; | |
| 2986 | return type_ == macho.N_ABS; | |
| 2987 | } | |
| 2988 | ||
| 2989 | pub fn symbolIsWeakDef(sym: macho.nlist_64) bool { | |
| 2990 | return (sym.n_desc & macho.N_WEAK_DEF) != 0; | |
| 2991 | } | |
| 2992 | ||
| 2993 | pub fn symbolIsWeakRef(sym: macho.nlist_64) bool { | |
| 2994 | return (sym.n_desc & macho.N_WEAK_REF) != 0; | |
| 2995 | } | |
| 2996 | ||
| 2997 | pub fn symbolIsTentative(sym: macho.nlist_64) bool { | |
| 2998 | if (!symbolIsUndf(sym)) return false; | |
| 2999 | return sym.n_value != 0; | |
| 3000 | } | |
| 3001 | ||
| 3002 | pub fn symbolIsNull(sym: macho.nlist_64) bool { | |
| 3003 | return sym.n_value == 0 and sym.n_desc == 0 and sym.n_type == 0 and sym.n_strx == 0 and sym.n_sect == 0; | |
| 3004 | } | |
| 3005 | ||
| 3006 | pub fn symbolIsTemp(sym: macho.nlist_64, sym_name: []const u8) bool { | |
| 3007 | if (!symbolIsSect(sym)) return false; | |
| 3008 | if (symbolIsExt(sym)) return false; | |
| 3009 | return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L"); | |
| 3010 | } | |
| 3011 | ||
| 3012 | pub fn sectionId(self: Zld, match: MatchingSection) u8 { | |
| 3013 | // TODO there might be a more generic way of doing this. | |
| 3014 | var section: u8 = 0; | |
| 3015 | for (self.load_commands.items) |cmd, cmd_id| { | |
| 3016 | if (cmd != .Segment) break; | |
| 3017 | if (cmd_id == match.seg) { | |
| 3018 | section += @intCast(u8, match.sect) + 1; | |
| 3019 | break; | |
| 3020 | } | |
| 3021 | section += @intCast(u8, cmd.Segment.sections.items.len); | |
| 3022 | } | |
| 3023 | return section; | |
| 3024 | } | |
| 3025 | ||
| 3026 | pub fn unpackSectionId(self: Zld, section_id: u8) MatchingSection { | |
| 3027 | var match: MatchingSection = undefined; | |
| 3028 | var section: u8 = 0; | |
| 3029 | outer: for (self.load_commands.items) |cmd, cmd_id| { | |
| 3030 | assert(cmd == .Segment); | |
| 3031 | for (cmd.Segment.sections.items) |_, sect_id| { | |
| 3032 | section += 1; | |
| 3033 | if (section_id == section) { | |
| 3034 | match.seg = @intCast(u16, cmd_id); | |
| 3035 | match.sect = @intCast(u16, sect_id); | |
| 3036 | break :outer; | |
| 3037 | } | |
| 3038 | } | |
| 3039 | } | |
| 3040 | return match; | |
| 3041 | } | |
| 3042 | ||
| 3043 | fn packDylibOrdinal(ordinal: u16) u16 { | |
| 3044 | return ordinal * macho.N_SYMBOL_RESOLVER; | |
| 3045 | } | |
| 3046 | ||
| 3047 | fn unpackDylibOrdinal(pack: u16) u16 { | |
| 3048 | return @divExact(pack, macho.N_SYMBOL_RESOLVER); | |
| 3049 | } | |
| 3050 | ||
| 3051 | pub fn findFirst(comptime T: type, haystack: []T, start: usize, predicate: anytype) usize { | |
| 3052 | if (!@hasDecl(@TypeOf(predicate), "predicate")) | |
| 3053 | @compileError("Predicate is required to define fn predicate(@This(), T) bool"); | |
| 3054 | ||
| 3055 | if (start == haystack.len) return start; | |
| 3056 | ||
| 3057 | var i = start; | |
| 3058 | while (i < haystack.len) : (i += 1) { | |
| 3059 | if (predicate.predicate(haystack[i])) break; | |
| 3060 | } | |
| 3061 | return i; | |
| 3062 | } |