authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-18 17:48:00+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-18 17:48:00+02:00
logf6d13e9d6f7a4b9161f369544501ecbb447c1658
treea87db008c00c7be1ef979e6e3b2072c753890564
parente0b53ad3c99b8f38d2fdba7b9aa6bf3e638dbeb9

zld: move contents of Zld into MachO module


6 files changed, 2737 insertions(+), 3458 deletions(-)

CMakeLists.txt-1
...@@ -583,7 +583,6 @@ set(ZIG_STAGE2_SOURCES...@@ -583,7 +583,6 @@ set(ZIG_STAGE2_SOURCES
583 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"583 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
584 "${CMAKE_SOURCE_DIR}/src/link/MachO/TextBlock.zig"584 "${CMAKE_SOURCE_DIR}/src/link/MachO/TextBlock.zig"
585 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"585 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
586 "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"
587 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"586 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
588 "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig"587 "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig"
589 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"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,16 +20,19 @@ const target_util = @import("../target.zig");
20const trace = @import("../tracy.zig").trace;20const trace = @import("../tracy.zig").trace;
2121
22const Allocator = mem.Allocator;22const Allocator = mem.Allocator;
23const Archive = @import("MachO/Archive.zig");
23const Cache = @import("../Cache.zig");24const Cache = @import("../Cache.zig");
24const CodeSignature = @import("MachO/CodeSignature.zig");25const CodeSignature = @import("MachO/CodeSignature.zig");
25const Compilation = @import("../Compilation.zig");26const Compilation = @import("../Compilation.zig");
26const DebugSymbols = @import("MachO/DebugSymbols.zig");27const DebugSymbols = @import("MachO/DebugSymbols.zig");
28const Dylib = @import("MachO/Dylib.zig");
29const Object = @import("MachO/Object.zig");
27const LoadCommand = commands.LoadCommand;30const LoadCommand = commands.LoadCommand;
28const Module = @import("../Module.zig");31const Module = @import("../Module.zig");
29const File = link.File;32const File = link.File;
33pub const TextBlock = @import("MachO/TextBlock.zig");
30const Trie = @import("MachO/Trie.zig");34const Trie = @import("MachO/Trie.zig");
31const SegmentCommand = commands.SegmentCommand;35const SegmentCommand = commands.SegmentCommand;
32const Zld = @import("MachO/Zld.zig");
3336
34pub const base_tag: File.Tag = File.Tag.macho;37pub const base_tag: File.Tag = File.Tag.macho;
3538
...@@ -47,63 +50,83 @@ page_size: u16,...@@ -47,63 +50,83 @@ page_size: u16,
47/// potential future extensions.50/// potential future extensions.
48header_pad: u16 = 0x1000,51header_pad: u16 = 0x1000,
4952
50/// Table of all load commands53/// The absolute address of the entry point.
54entry_addr: ?u64 = null,
55
56objects: std.ArrayListUnmanaged(*Object) = .{},
57archives: std.ArrayListUnmanaged(*Archive) = .{},
58dylibs: std.ArrayListUnmanaged(*Dylib) = .{},
59
60next_dylib_ordinal: u16 = 1,
61
51load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},62load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
52/// __PAGEZERO segment63
53pagezero_segment_cmd_index: ?u16 = null,64pagezero_segment_cmd_index: ?u16 = null,
54/// __TEXT segment
55text_segment_cmd_index: ?u16 = null,65text_segment_cmd_index: ?u16 = null,
56/// __DATA_CONST segment
57data_const_segment_cmd_index: ?u16 = null,66data_const_segment_cmd_index: ?u16 = null,
58/// __DATA segment
59data_segment_cmd_index: ?u16 = null,67data_segment_cmd_index: ?u16 = null,
60/// __LINKEDIT segment
61linkedit_segment_cmd_index: ?u16 = null,68linkedit_segment_cmd_index: ?u16 = null,
62/// Dyld info
63dyld_info_cmd_index: ?u16 = null,69dyld_info_cmd_index: ?u16 = null,
64/// Symbol table
65symtab_cmd_index: ?u16 = null,70symtab_cmd_index: ?u16 = null,
66/// Dynamic symbol table
67dysymtab_cmd_index: ?u16 = null,71dysymtab_cmd_index: ?u16 = null,
68/// Path to dyld linker
69dylinker_cmd_index: ?u16 = null,72dylinker_cmd_index: ?u16 = null,
70/// Path to libSystem
71libsystem_cmd_index: ?u16 = null,
72/// Data-in-code section of __LINKEDIT segment
73data_in_code_cmd_index: ?u16 = null,73data_in_code_cmd_index: ?u16 = null,
74/// Address to entry point function
75function_starts_cmd_index: ?u16 = null,74function_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.
79main_cmd_index: ?u16 = null,75main_cmd_index: ?u16 = null,
80/// Minimum OS version76dylib_id_cmd_index: ?u16 = null,
81version_min_cmd_index: ?u16 = null,77version_min_cmd_index: ?u16 = null,
82/// Source version
83source_version_cmd_index: ?u16 = null,78source_version_cmd_index: ?u16 = null,
84/// UUID load command
85uuid_cmd_index: ?u16 = null,79uuid_cmd_index: ?u16 = null,
86/// Code signature
87code_signature_cmd_index: ?u16 = null,80code_signature_cmd_index: ?u16 = null,
81/// Path to libSystem
82/// TODO this is obsolete, remove it.
83libsystem_cmd_index: ?u16 = null,
8884
89/// Index into __TEXT,__text section.85// __TEXT segment sections
90text_section_index: ?u16 = null,86text_section_index: ?u16 = null,
91/// Index into __TEXT,__stubs section.
92stubs_section_index: ?u16 = null,87stubs_section_index: ?u16 = null,
93/// Index into __TEXT,__stub_helper section.
94stub_helper_section_index: ?u16 = null,88stub_helper_section_index: ?u16 = null,
95/// Index into __DATA_CONST,__got section.89text_const_section_index: ?u16 = null,
90cstring_section_index: ?u16 = null,
91ustring_section_index: ?u16 = null,
92gcc_except_tab_section_index: ?u16 = null,
93unwind_info_section_index: ?u16 = null,
94eh_frame_section_index: ?u16 = null,
95
96objc_methlist_section_index: ?u16 = null,
97objc_methname_section_index: ?u16 = null,
98objc_methtype_section_index: ?u16 = null,
99objc_classname_section_index: ?u16 = null,
100
101// __DATA_CONST segment sections
96got_section_index: ?u16 = null,102got_section_index: ?u16 = null,
97/// Index into __DATA,__la_symbol_ptr section.103mod_init_func_section_index: ?u16 = null,
104mod_term_func_section_index: ?u16 = null,
105data_const_section_index: ?u16 = null,
106
107objc_cfstring_section_index: ?u16 = null,
108objc_classlist_section_index: ?u16 = null,
109objc_imageinfo_section_index: ?u16 = null,
110
111// __DATA segment sections
112tlv_section_index: ?u16 = null,
113tlv_data_section_index: ?u16 = null,
114tlv_bss_section_index: ?u16 = null,
98la_symbol_ptr_section_index: ?u16 = null,115la_symbol_ptr_section_index: ?u16 = null,
99/// Index into __DATA,__data section.
100data_section_index: ?u16 = null,116data_section_index: ?u16 = null,
101/// The absolute address of the entry point.117bss_section_index: ?u16 = null,
102entry_addr: ?u64 = null,118common_section_index: ?u16 = null,
119
120objc_const_section_index: ?u16 = null,
121objc_selrefs_section_index: ?u16 = null,
122objc_classrefs_section_index: ?u16 = null,
123objc_data_section_index: ?u16 = null,
103124
104locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},125locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
105globals: std.ArrayListUnmanaged(macho.nlist_64) = .{},126globals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
106imports: std.ArrayListUnmanaged(macho.nlist_64) = .{},127imports: std.ArrayListUnmanaged(macho.nlist_64) = .{},
128undefs: std.ArrayListUnmanaged(macho.nlist_64) = .{},
129tentatives: std.ArrayListUnmanaged(macho.nlist_64) = .{},
107symbol_resolver: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},130symbol_resolver: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
108131
109locals_free_list: std.ArrayListUnmanaged(u32) = .{},132locals_free_list: std.ArrayListUnmanaged(u32) = .{},
...@@ -133,6 +156,9 @@ export_info_dirty: bool = false,...@@ -133,6 +156,9 @@ export_info_dirty: bool = false,
133strtab_dirty: bool = false,156strtab_dirty: bool = false,
134strtab_needs_relocation: bool = false,157strtab_needs_relocation: bool = false,
135158
159has_dices: bool = false,
160has_stabs: bool = false,
161
136/// A list of text blocks that have surplus capacity. This list can have false162/// A list of text blocks that have surplus capacity. This list can have false
137/// positives, as functions grow and shrink over time, only sometimes being added163/// positives, as functions grow and shrink over time, only sometimes being added
138/// or removed from the freelist.164/// or removed from the freelist.
...@@ -153,6 +179,8 @@ text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},...@@ -153,6 +179,8 @@ text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
153/// Pointer to the last allocated text block179/// Pointer to the last allocated text block
154last_text_block: ?*TextBlock = null,180last_text_block: ?*TextBlock = null,
155181
182blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{},
183
156/// A list of all PIE fixups required for this run of the linker.184/// A list of all PIE fixups required for this run of the linker.
157/// Warning, this is currently NOT thread-safe. See the TODO below.185/// Warning, this is currently NOT thread-safe. See the TODO below.
158/// TODO Move this list inside `updateDecl` where it should be allocated186/// 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,71 +264,7 @@ const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.
236/// it as a possible place to put new symbols, it must have enough room for this many bytes264/// it as a possible place to put new symbols, it must have enough room for this many bytes
237/// (plus extra for reserved capacity).265/// (plus extra for reserved capacity).
238const minimum_text_block_size = 64;266const minimum_text_block_size = 64;
239const min_text_capacity = padToIdeal(minimum_text_block_size);267pub const min_text_capacity = padToIdeal(minimum_text_block_size);
240
241pub 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};
304268
305pub const Export = struct {269pub const Export = struct {
306 sym_index: ?u32 = null,270 sym_index: ?u32 = null,
...@@ -452,9 +416,9 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -452,9 +416,9 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
452 self.load_commands_dirty = true;416 self.load_commands_dirty = true;
453 }417 }
454 try self.writeRebaseInfoTable();418 try self.writeRebaseInfoTable();
455 try self.writeBindingInfoTable();419 try self.writeBindInfoTable();
456 try self.writeLazyBindingInfoTable();420 try self.writeLazyBindInfoTable();
457 try self.writeExportTrie();421 try self.writeExportInfo();
458 try self.writeAllGlobalAndUndefSymbols();422 try self.writeAllGlobalAndUndefSymbols();
459 try self.writeIndirectSymbolTable();423 try self.writeIndirectSymbolTable();
460 try self.writeStringTable();424 try self.writeStringTable();
...@@ -718,14 +682,6 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {...@@ -718,14 +682,6 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
718 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});682 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
719 }683 }
720 } else {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 // Positional arguments to the linker such as object files and static archives.685 // Positional arguments to the linker such as object files and static archives.
730 var positionals = std.ArrayList([]const u8).init(arena);686 var positionals = std.ArrayList([]const u8).init(arena);
731687
...@@ -796,164 +752,2252 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {...@@ -796,164 +752,2252 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
796 }752 }
797 }753 }
798754
799 // If we're compiling native and we can find libSystem.B.{dylib, tbd},755 // 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.756 // we link against that instead of embedded libSystem.B.tbd file.
801 var native_libsystem_available = false;757 var native_libsystem_available = false;
802 if (self.base.options.is_native_os) blk: {758 if (self.base.options.is_native_os) blk: {
803 // Try stub file first. If we hit it, then we're done as the stub file759 // Try stub file first. If we hit it, then we're done as the stub file
804 // re-exports every single symbol definition.760 // re-exports every single symbol definition.
805 if (try resolveLib(arena, lib_dirs.items, "System", ".tbd")) |full_path| {761 if (try resolveLib(arena, lib_dirs.items, "System", ".tbd")) |full_path| {
806 try libs.append(full_path);762 try libs.append(full_path);
807 native_libsystem_available = true;763 native_libsystem_available = true;
808 break :blk;764 break :blk;
809 }765 }
810 // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib766 // 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.767 // 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| {768 if (try resolveLib(arena, lib_dirs.items, "System", ".dylib")) |libsystem_path| {
813 if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| {769 if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| {
814 try libs.append(libsystem_path);770 try libs.append(libsystem_path);
815 try libs.append(libc_path);771 try libs.append(libc_path);
816 native_libsystem_available = true;772 native_libsystem_available = true;
817 break :blk;773 break :blk;
818 }774 }
819 }775 }
820 }776 }
821 if (!native_libsystem_available) {777 if (!native_libsystem_available) {
822 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{778 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
823 "libc", "darwin", "libSystem.B.tbd",779 "libc", "darwin", "libSystem.B.tbd",
824 });780 });
825 try libs.append(full_path);781 try libs.append(full_path);
826 }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
989fn 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
1020fn 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
1040pub const MatchingSection = struct {
1041 seg: u16,
1042 sect: u16,
1043};
1044
1045pub 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
1446fn 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
1569fn 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
1620fn 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
1635fn 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
1652fn 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
1659fn 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
1678fn 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
1748fn 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
1807fn 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
1950fn 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
2160fn 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
2419fn parseTextBlocks(self: *MachO) !void {
2420 for (self.objects.items) |object| {
2421 try object.parseTextBlocks(self);
2422 }
2423}
2424
2425fn 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
2710fn 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
2724fn 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
2738fn 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
2757fn 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
2819fn 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
2842fn 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
2859fn 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;
8272862
828 // frameworks2863 var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator);
829 var framework_dirs = std.ArrayList([]const u8).init(arena);2864 defer locals.deinit();
830 for (self.base.options.framework_dirs) |dir| {2865 try locals.appendSlice(self.locals.items);
831 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {2866
832 try framework_dirs.append(search_dir);2867 if (self.has_stabs) {
833 } else {2868 for (self.objects.items) |object| {
834 log.warn("directory not found for '-F{s}'", .{dir});2869 if (object.debug_info == null) continue;
835 }2870
836 }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 });
8372894
838 var framework_not_found = false;2895 for (object.text_blocks.items) |block| {
839 for (self.base.options.frameworks) |framework| {2896 if (block.stab) |stab| {
840 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {2897 const nlists = try stab.asNlists(block.local_sym_index, self);
841 if (try resolveFramework(arena, framework_dirs.items, framework, ext)) |full_path| {2898 defer self.base.allocator.free(nlists);
842 try libs.append(full_path);2899 try locals.appendSlice(nlists);
843 break;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 }
8572909
858 // rpaths2910 // Close scope
859 var rpath_table = std.StringArrayHashMap(void).init(arena);2911 locals.appendAssumeCapacity(.{
860 for (self.base.options.rpath_list) |rpath| {2912 .n_strx = 0,
861 if (rpath_table.contains(rpath)) continue;2913 .n_type = macho.N_SO,
862 try rpath_table.putNoClobber(rpath, {});2914 .n_sect = 0,
2915 .n_desc = 0,
2916 .n_value = 0,
2917 });
863 }2918 }
2919 }
8642920
865 var rpaths = std.ArrayList([]const u8).init(arena);2921 const nlocals = locals.items.len;
866 try rpaths.ensureCapacity(rpath_table.count());2922 const nexports = self.globals.items.len;
867 for (rpath_table.keys()) |*key| {2923 const nundefs = self.imports.items.len;
868 rpaths.appendAssumeCapacity(key.*);
869 }
8702924
871 const output: Zld.Output = output: {2925 const locals_off = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64);
872 if (is_dyn_lib) {2926 const locals_size = nlocals * @sizeOf(macho.nlist_64);
873 const install_name = try std.fmt.allocPrint(arena, "@rpath/{s}", .{2927 log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });
874 self.base.options.emit.?.sub_path,2928 try self.base.file.?.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
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 };
8872929
888 if (self.base.options.verbose_link) {2930 const exports_off = locals_off + locals_size;
889 var argv = std.ArrayList([]const u8).init(arena);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);
8902934
891 try argv.append("zig");2935 const undefs_off = exports_off + exports_size;
892 try argv.append("ld");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);
8932939
894 if (is_exe_or_dyn_lib) {2940 symtab.nsyms += @intCast(u32, nlocals + nexports + nundefs);
895 try argv.append("-dynamic");2941 seg.inner.filesize += locals_size + exports_size + undefs_size;
896 }
8972942
898 if (is_dyn_lib) {2943 // Update dynamic symbol table.
899 try argv.append("-dylib");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);
9002950
901 try argv.append("-install_name");2951 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
902 try argv.append(output.install_name.?);2952 const stubs = &text_segment.sections.items[self.stubs_section_index.?];
903 }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.?];
9042957
905 if (self.base.options.sysroot) |syslibroot| {2958 const nstubs = @intCast(u32, self.stubs.items.len);
906 try argv.append("-syslibroot");2959 const ngot_entries = @intCast(u32, self.got_entries.items.len);
907 try argv.append(syslibroot);
908 }
9092960
910 for (rpaths.items) |rpath| {2961 dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
911 try argv.append("-rpath");2962 dysymtab.nindirectsyms = nstubs * 2 + ngot_entries;
912 try argv.append(rpath);
913 }
9142963
915 try argv.appendSlice(positionals.items);2964 const needed_size = dysymtab.nindirectsyms * @sizeOf(u32);
2965 seg.inner.filesize += needed_size;
9162966
917 try argv.append("-o");2967 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{
918 try argv.append(output.path);2968 dysymtab.indirectsymoff,
2969 dysymtab.indirectsymoff + needed_size,
2970 });
9192971
920 if (native_libsystem_available) {2972 var buf = try self.base.allocator.alloc(u8, needed_size);
921 try argv.append("-lSystem");2973 defer self.base.allocator.free(buf);
922 try argv.append("-lc");
923 }
9242974
925 for (search_lib_names.items) |l_name| {2975 var stream = std.io.fixedBufferStream(buf);
926 try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name}));2976 var writer = stream.writer();
927 }
9282977
929 for (self.base.options.lib_dirs) |lib_dir| {2978 stubs.reserved1 = 0;
930 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));2979 for (self.stubs.items) |id| {
931 }2980 try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
2981 }
9322982
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 }
9422994
943 if (!self.base.options.disable_lld_caching) {2995 la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;
944 // Update the file with the digest. If it fails we can continue; it only2996 for (self.stubs.items) |id| {
945 // means that the next invocation will have an unnecessary cache miss.2997 try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
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();
956 }2998 }
2999
3000 try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff);
957}3001}
9583002
959pub fn deinit(self: *MachO) void {3003pub fn deinit(self: *MachO) void {
...@@ -970,6 +3014,8 @@ pub fn deinit(self: *MachO) void {...@@ -970,6 +3014,8 @@ pub fn deinit(self: *MachO) void {
970 self.stubs.deinit(self.base.allocator);3014 self.stubs.deinit(self.base.allocator);
971 self.stubs_map.deinit(self.base.allocator);3015 self.stubs_map.deinit(self.base.allocator);
972 self.strtab.deinit(self.base.allocator);3016 self.strtab.deinit(self.base.allocator);
3017 self.undefs.deinit(self.base.allocator);
3018 self.tentatives.deinit(self.base.allocator);
973 self.imports.deinit(self.base.allocator);3019 self.imports.deinit(self.base.allocator);
974 self.globals.deinit(self.base.allocator);3020 self.globals.deinit(self.base.allocator);
975 self.globals_free_list.deinit(self.base.allocator);3021 self.globals_free_list.deinit(self.base.allocator);
...@@ -981,10 +3027,40 @@ pub fn deinit(self: *MachO) void {...@@ -981,10 +3027,40 @@ pub fn deinit(self: *MachO) void {
981 }3027 }
982 self.symbol_resolver.deinit(self.base.allocator);3028 self.symbol_resolver.deinit(self.base.allocator);
9833029
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 for (self.load_commands.items) |*lc| {3048 for (self.load_commands.items) |*lc| {
985 lc.deinit(self.base.allocator);3049 lc.deinit(self.base.allocator);
986 }3050 }
987 self.load_commands.deinit(self.base.allocator);3051 self.load_commands.deinit(self.base.allocator);
3052
3053 // TODO dealloc all blocks
3054 self.blocks.deinit(self.base.allocator);
3055}
3056
3057pub 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}
9893065
990fn freeTextBlock(self: *MachO, text_block: *TextBlock) void {3066fn freeTextBlock(self: *MachO, text_block: *TextBlock) void {
...@@ -2664,6 +4740,60 @@ fn writeIndirectSymbolTable(self: *MachO) !void {...@@ -2664,6 +4740,60 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
2664 self.load_commands_dirty = true;4740 self.load_commands_dirty = true;
2665}4741}
26664742
4743fn 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
2667fn writeCodeSignaturePadding(self: *MachO) !void {4797fn writeCodeSignaturePadding(self: *MachO) !void {
2668 // TODO figure out how not to rewrite padding every single time.4798 // TODO figure out how not to rewrite padding every single time.
2669 const tracy = trace(@src());4799 const tracy = trace(@src());
...@@ -2719,7 +4849,7 @@ fn writeCodeSignature(self: *MachO) !void {...@@ -2719,7 +4849,7 @@ fn writeCodeSignature(self: *MachO) !void {
2719 try self.base.file.?.pwriteAll(buffer, code_sig_cmd.dataoff);4849 try self.base.file.?.pwriteAll(buffer, code_sig_cmd.dataoff);
2720}4850}
27214851
2722fn writeExportTrie(self: *MachO) !void {4852fn writeExportInfo(self: *MachO) !void {
2723 if (!self.export_info_dirty) return;4853 if (!self.export_info_dirty) return;
2724 if (self.globals.items.len == 0) return;4854 if (self.globals.items.len == 0) return;
27254855
...@@ -2779,6 +4909,34 @@ fn writeRebaseInfoTable(self: *MachO) !void {...@@ -2779,6 +4909,34 @@ fn writeRebaseInfoTable(self: *MachO) !void {
2779 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);4909 var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
2780 defer pointers.deinit();4910 defer pointers.deinit();
27814911
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 if (self.got_section_index) |idx| {4940 if (self.got_section_index) |idx| {
2783 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;4941 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2784 const sect = seg.sections.items[idx];4942 const sect = seg.sections.items[idx];
...@@ -2837,7 +4995,7 @@ fn writeRebaseInfoTable(self: *MachO) !void {...@@ -2837,7 +4995,7 @@ fn writeRebaseInfoTable(self: *MachO) !void {
2837 self.rebase_info_dirty = false;4995 self.rebase_info_dirty = false;
2838}4996}
28394997
2840fn writeBindingInfoTable(self: *MachO) !void {4998fn writeBindInfoTable(self: *MachO) !void {
2841 if (!self.binding_info_dirty) return;4999 if (!self.binding_info_dirty) return;
28425000
2843 const tracy = trace(@src());5001 const tracy = trace(@src());
...@@ -2865,6 +5023,37 @@ fn writeBindingInfoTable(self: *MachO) !void {...@@ -2865,6 +5023,37 @@ fn writeBindingInfoTable(self: *MachO) !void {
2865 }5023 }
2866 }5024 }
28675025
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 const size = try bind.bindInfoSize(pointers.items);5057 const size = try bind.bindInfoSize(pointers.items);
2869 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));5058 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
2870 defer self.base.allocator.free(buffer);5059 defer self.base.allocator.free(buffer);
...@@ -2890,7 +5079,7 @@ fn writeBindingInfoTable(self: *MachO) !void {...@@ -2890,7 +5079,7 @@ fn writeBindingInfoTable(self: *MachO) !void {
2890 self.binding_info_dirty = false;5079 self.binding_info_dirty = false;
2891}5080}
28925081
2893fn writeLazyBindingInfoTable(self: *MachO) !void {5082fn writeLazyBindInfoTable(self: *MachO) !void {
2894 if (!self.lazy_binding_info_dirty) return;5083 if (!self.lazy_binding_info_dirty) return;
28955084
2896 const tracy = trace(@src());5085 const tracy = trace(@src());
...@@ -3134,7 +5323,7 @@ fn writeHeader(self: *MachO) !void {...@@ -3134,7 +5323,7 @@ fn writeHeader(self: *MachO) !void {
3134 else => unreachable,5323 else => unreachable,
3135 }5324 }
31365325
3137 if (self.hasTlvDescriptors()) {5326 if (self.tlv_section_index) |_| {
3138 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;5327 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
3139 }5328 }
31405329
...@@ -3156,10 +5345,6 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {...@@ -3156,10 +5345,6 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
3156 std.math.maxInt(@TypeOf(actual_size));5345 std.math.maxInt(@TypeOf(actual_size));
3157}5346}
31585347
3159fn hasTlvDescriptors(_: *MachO) bool {
3160 return false;
3161}
3162
3163pub fn makeString(self: *MachO, string: []const u8) !u32 {5348pub fn makeString(self: *MachO, string: []const u8) !u32 {
3164 try self.strtab.ensureUnusedCapacity(self.base.allocator, string.len + 1);5349 try self.strtab.ensureUnusedCapacity(self.base.allocator, string.len + 1);
3165 const new_off = @intCast(u32, self.strtab.items.len);5350 const new_off = @intCast(u32, self.strtab.items.len);
...@@ -3177,6 +5362,92 @@ pub fn getString(self: *MachO, off: u32) []const u8 {...@@ -3177,6 +5362,92 @@ pub fn getString(self: *MachO, off: u32) []const u8 {
3177 return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + off));5362 return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + off));
3178}5363}
31795364
5365pub fn symbolIsStab(sym: macho.nlist_64) bool {
5366 return (macho.N_STAB & sym.n_type) != 0;
5367}
5368
5369pub fn symbolIsPext(sym: macho.nlist_64) bool {
5370 return (macho.N_PEXT & sym.n_type) != 0;
5371}
5372
5373pub fn symbolIsExt(sym: macho.nlist_64) bool {
5374 return (macho.N_EXT & sym.n_type) != 0;
5375}
5376
5377pub fn symbolIsSect(sym: macho.nlist_64) bool {
5378 const type_ = macho.N_TYPE & sym.n_type;
5379 return type_ == macho.N_SECT;
5380}
5381
5382pub fn symbolIsUndf(sym: macho.nlist_64) bool {
5383 const type_ = macho.N_TYPE & sym.n_type;
5384 return type_ == macho.N_UNDF;
5385}
5386
5387pub fn symbolIsIndr(sym: macho.nlist_64) bool {
5388 const type_ = macho.N_TYPE & sym.n_type;
5389 return type_ == macho.N_INDR;
5390}
5391
5392pub fn symbolIsAbs(sym: macho.nlist_64) bool {
5393 const type_ = macho.N_TYPE & sym.n_type;
5394 return type_ == macho.N_ABS;
5395}
5396
5397pub fn symbolIsWeakDef(sym: macho.nlist_64) bool {
5398 return (sym.n_desc & macho.N_WEAK_DEF) != 0;
5399}
5400
5401pub fn symbolIsWeakRef(sym: macho.nlist_64) bool {
5402 return (sym.n_desc & macho.N_WEAK_REF) != 0;
5403}
5404
5405pub fn symbolIsTentative(sym: macho.nlist_64) bool {
5406 if (!symbolIsUndf(sym)) return false;
5407 return sym.n_value != 0;
5408}
5409
5410pub 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
5414pub 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
5420pub 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
5434pub 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
3180fn packDylibOrdinal(ordinal: u16) u16 {5451fn packDylibOrdinal(ordinal: u16) u16 {
3181 return ordinal * macho.N_SYMBOL_RESOLVER;5452 return ordinal * macho.N_SYMBOL_RESOLVER;
3182}5453}
...@@ -3184,3 +5455,16 @@ fn packDylibOrdinal(ordinal: u16) u16 {...@@ -3184,3 +5455,16 @@ fn packDylibOrdinal(ordinal: u16) u16 {
3184fn unpackDylibOrdinal(pack: u16) u16 {5455fn unpackDylibOrdinal(pack: u16) u16 {
3185 return @divExact(pack, macho.N_SYMBOL_RESOLVER);5456 return @divExact(pack, macho.N_SYMBOL_RESOLVER);
3186}5457}
5458
5459pub 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,7 +13,7 @@ const fat = @import("fat.zig");
13const Allocator = mem.Allocator;13const Allocator = mem.Allocator;
14const Arch = std.Target.Cpu.Arch;14const Arch = std.Target.Cpu.Arch;
15const LibStub = @import("../tapi.zig").LibStub;15const LibStub = @import("../tapi.zig").LibStub;
16const Zld = @import("Zld.zig");16const MachO = @import("../MachO.zig");
1717
18usingnamespace @import("commands.zig");18usingnamespace @import("commands.zig");
1919
...@@ -324,7 +324,7 @@ fn parseSymbols(self: *Dylib) !void {...@@ -324,7 +324,7 @@ fn parseSymbols(self: *Dylib) !void {
324 _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff + self.library_offset);324 _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff + self.library_offset);
325325
326 for (slice) |sym| {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));
328328
329 if (!add_to_symtab) continue;329 if (!add_to_symtab) continue;
330330
src/link/MachO/Object.zig+41-45
...@@ -13,8 +13,8 @@ const sort = std.sort;...@@ -13,8 +13,8 @@ const sort = std.sort;
1313
14const Allocator = mem.Allocator;14const Allocator = mem.Allocator;
15const Arch = std.Target.Cpu.Arch;15const Arch = std.Target.Cpu.Arch;
16const MachO = @import("../MachO.zig");
16const TextBlock = @import("TextBlock.zig");17const TextBlock = @import("TextBlock.zig");
17const Zld = @import("Zld.zig");
1818
19usingnamespace @import("commands.zig");19usingnamespace @import("commands.zig");
2020
...@@ -307,8 +307,8 @@ const NlistWithIndex = struct {...@@ -307,8 +307,8 @@ const NlistWithIndex = struct {
307 }307 }
308 };308 };
309309
310 const start = Zld.findFirst(NlistWithIndex, symbols, 0, Predicate{ .addr = sect.addr });310 const start = MachO.findFirst(NlistWithIndex, symbols, 0, Predicate{ .addr = sect.addr });
311 const end = Zld.findFirst(NlistWithIndex, symbols, start, Predicate{ .addr = sect.addr + sect.size });311 const end = MachO.findFirst(NlistWithIndex, symbols, start, Predicate{ .addr = sect.addr + sect.size });
312312
313 return symbols[start..end];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,8 +323,8 @@ fn filterDice(dices: []macho.data_in_code_entry, start_addr: u64, end_addr: u64)
323 }323 }
324 };324 };
325325
326 const start = Zld.findFirst(macho.data_in_code_entry, dices, 0, Predicate{ .addr = start_addr });326 const start = MachO.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 });327 const end = MachO.findFirst(macho.data_in_code_entry, dices, start, Predicate{ .addr = end_addr });
328328
329 return dices[start..end];329 return dices[start..end];
330}330}
...@@ -335,10 +335,10 @@ const TextBlockParser = struct {...@@ -335,10 +335,10 @@ const TextBlockParser = struct {
335 code: []u8,335 code: []u8,
336 relocs: []macho.relocation_info,336 relocs: []macho.relocation_info,
337 object: *Object,337 object: *Object,
338 zld: *Zld,338 macho_file: *MachO,
339 nlists: []NlistWithIndex,339 nlists: []NlistWithIndex,
340 index: u32 = 0,340 index: u32 = 0,
341 match: Zld.MatchingSection,341 match: MachO.MatchingSection,
342342
343 fn peek(self: *TextBlockParser) ?NlistWithIndex {343 fn peek(self: *TextBlockParser) ?NlistWithIndex {
344 return if (self.index + 1 < self.nlists.len) self.nlists[self.index + 1] else null;344 return if (self.index + 1 < self.nlists.len) self.nlists[self.index + 1] else null;
...@@ -349,10 +349,10 @@ const TextBlockParser = struct {...@@ -349,10 +349,10 @@ const TextBlockParser = struct {
349 };349 };
350350
351 fn lessThanBySeniority(context: SeniorityContext, lhs: NlistWithIndex, rhs: NlistWithIndex) bool {351 fn lessThanBySeniority(context: SeniorityContext, lhs: NlistWithIndex, rhs: NlistWithIndex) bool {
352 if (!Zld.symbolIsExt(rhs.nlist)) {352 if (!MachO.symbolIsExt(rhs.nlist)) {
353 return Zld.symbolIsTemp(lhs.nlist, context.object.getString(lhs.nlist.n_strx));353 return MachO.symbolIsTemp(lhs.nlist, context.object.getString(lhs.nlist.n_strx));
354 } else if (Zld.symbolIsPext(rhs.nlist) or Zld.symbolIsWeakDef(rhs.nlist)) {354 } else if (MachO.symbolIsPext(rhs.nlist) or MachO.symbolIsWeakDef(rhs.nlist)) {
355 return !Zld.symbolIsExt(lhs.nlist);355 return !MachO.symbolIsExt(lhs.nlist);
356 } else {356 } else {
357 return true;357 return true;
358 }358 }
...@@ -383,7 +383,7 @@ const TextBlockParser = struct {...@@ -383,7 +383,7 @@ const TextBlockParser = struct {
383 const sym = self.object.symbols.items[nlist_with_index.index];383 const sym = self.object.symbols.items[nlist_with_index.index];
384 if (sym.payload != .regular) {384 if (sym.payload != .regular) {
385 log.err("expected a regular symbol, found {s}", .{sym.payload});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 return error.SymbolIsNotRegular;387 return error.SymbolIsNotRegular;
388 }388 }
389 assert(sym.payload.regular.local_sym_index != 0); // This means the symbol has not been properly resolved.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,7 +401,7 @@ const TextBlockParser = struct {
401 }401 }
402402
403 const senior_nlist = aliases.pop();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 assert(senior_sym.payload == .regular);405 assert(senior_sym.payload == .regular);
406 senior_sym.payload.regular.segment_id = self.match.seg;406 senior_sym.payload.regular.segment_id = self.match.seg;
407 senior_sym.payload.regular.section_id = self.match.sect;407 senior_sym.payload.regular.section_id = self.match.sect;
...@@ -429,7 +429,7 @@ const TextBlockParser = struct {...@@ -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 break :blk .static;433 break :blk .static;
434 } else null;434 } else null;
435435
...@@ -448,19 +448,19 @@ const TextBlockParser = struct {...@@ -448,19 +448,19 @@ const TextBlockParser = struct {
448 for (aliases.items) |alias| {448 for (aliases.items) |alias| {
449 block.aliases.appendAssumeCapacity(alias.index);449 block.aliases.appendAssumeCapacity(alias.index);
450450
451 const sym = self.zld.locals.items[alias.index];451 const sym = self.macho_file.locals.items[alias.index];
452 const reg = &sym.payload.regular;452 const reg = &sym.payload.regular;
453 reg.segment_id = self.match.seg;453 reg.segment_id = self.match.seg;
454 reg.section_id = self.match.sect;454 reg.section_id = self.match.sect;
455 }455 }
456 }456 }
457457
458 try block.parseRelocsFromObject(relocs, object, .{458 try block.parseRelocsFromObject(self.allocator, relocs, object, .{
459 .base_addr = start_addr,459 .base_addr = start_addr,
460 .zld = self.zld,460 .macho_file = self.macho_file,
461 });461 });
462462
463 if (self.zld.has_dices) {463 if (self.macho_file.has_dices) {
464 const dices = filterDice(464 const dices = filterDice(
465 self.object.data_in_code_entries.items,465 self.object.data_in_code_entries.items,
466 senior_nlist.nlist.n_value,466 senior_nlist.nlist.n_value,
...@@ -483,7 +483,7 @@ const TextBlockParser = struct {...@@ -483,7 +483,7 @@ const TextBlockParser = struct {
483 }483 }
484};484};
485485
486pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {486pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void {
487 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;487 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
488488
489 log.debug("analysing {s}", .{self.name.?});489 log.debug("analysing {s}", .{self.name.?});
...@@ -513,7 +513,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {...@@ -513,7 +513,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
513 });513 });
514514
515 // Get matching segment/section in the final artifact.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 log.debug("unhandled section", .{});517 log.debug("unhandled section", .{});
518 continue;518 continue;
519 };519 };
...@@ -538,7 +538,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {...@@ -538,7 +538,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
538 // duplicates at all? Need some benchmarks!538 // duplicates at all? Need some benchmarks!
539 // const is_splittable = false;539 // const is_splittable = false;
540540
541 zld.has_dices = blk: {541 macho_file.has_dices = blk: {
542 if (self.text_section_index) |index| {542 if (self.text_section_index) |index| {
543 if (index != id) break :blk false;543 if (index != id) break :blk false;
544 if (self.data_in_code_entries.items.len == 0) break :blk false;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,7 +546,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
546 }546 }
547 break :blk false;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;
550550
551 {551 {
552 // next: {552 // next: {
...@@ -711,11 +711,11 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {...@@ -711,11 +711,11 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
711 defer self.allocator.free(sym_name);711 defer self.allocator.free(sym_name);
712712
713 const block_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {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);714 const block_local_sym_index = @intCast(u32, macho_file.locals.items.len);
715 try zld.locals.append(zld.allocator, .{715 try macho_file.locals.append(macho_file.base.allocator, .{
716 .n_strx = try zld.makeString(sym_name),716 .n_strx = try macho_file.makeString(sym_name),
717 .n_type = macho.N_SECT,717 .n_type = macho.N_SECT,
718 .n_sect = zld.sectionId(match),718 .n_sect = macho_file.sectionId(match),
719 .n_desc = 0,719 .n_desc = 0,
720 .n_value = sect.addr,720 .n_value = sect.addr,
721 });721 });
...@@ -726,20 +726,20 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {...@@ -726,20 +726,20 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
726 const block = try self.allocator.create(TextBlock);726 const block = try self.allocator.create(TextBlock);
727 errdefer self.allocator.destroy(block);727 errdefer self.allocator.destroy(block);
728728
729 block.* = TextBlock.init(self.allocator);729 block.* = TextBlock.empty;
730 block.local_sym_index = block_local_sym_index;730 block.local_sym_index = block_local_sym_index;
731 block.code = try self.allocator.dupe(u8, code);731 block.code = try self.allocator.dupe(u8, code);
732 block.size = sect.size;732 block.size = sect.size;
733 block.alignment = sect.@"align";733 block.alignment = sect.@"align";
734734
735 try block.parseRelocsFromObject(relocs, self, .{735 try block.parseRelocsFromObject(self.allocator, relocs, self, .{
736 .base_addr = 0,736 .base_addr = 0,
737 .zld = zld,737 .macho_file = macho_file,
738 });738 });
739739
740 if (zld.has_dices) {740 if (macho_file.has_dices) {
741 const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + sect.size);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);
743743
744 for (dices) |dice| {744 for (dices) |dice| {
745 block.dices.appendAssumeCapacity(.{745 block.dices.appendAssumeCapacity(.{
...@@ -755,15 +755,13 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {...@@ -755,15 +755,13 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
755 // the filtered symbols and note which symbol is contained within so that755 // the filtered symbols and note which symbol is contained within so that
756 // we can properly allocate addresses down the line.756 // we can properly allocate addresses down the line.
757 // While we're at it, we need to update segment,section mapping of each symbol too.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);758 try block.contained.ensureTotalCapacity(self.allocator, filtered_nlists.len);
759 defer contained.deinit();
760 try contained.ensureTotalCapacity(filtered_nlists.len);
761759
762 for (filtered_nlists) |nlist_with_index| {760 for (filtered_nlists) |nlist_with_index| {
763 const nlist = nlist_with_index.nlist;761 const nlist = nlist_with_index.nlist;
764 const local_sym_index = self.symbol_mapping.get(nlist_with_index.index) orelse unreachable;762 const local_sym_index = self.symbol_mapping.get(nlist_with_index.index) orelse unreachable;
765 const local = &zld.locals.items[local_sym_index];763 const local = &macho_file.locals.items[local_sym_index];
766 local.n_sect = zld.sectionId(match);764 local.n_sect = macho_file.sectionId(match);
767765
768 const stab: ?TextBlock.Stab = if (self.debug_info) |di| blk: {766 const stab: ?TextBlock.Stab = if (self.debug_info) |di| blk: {
769 // TODO there has to be a better to handle this.767 // TODO there has to be a better to handle this.
...@@ -781,19 +779,17 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {...@@ -781,19 +779,17 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
781 break :blk .static;779 break :blk .static;
782 } else null;780 } else null;
783781
784 contained.appendAssumeCapacity(.{782 block.contained.appendAssumeCapacity(.{
785 .local_sym_index = local_sym_index,783 .local_sym_index = local_sym_index,
786 .offset = nlist.n_value - sect.addr,784 .offset = nlist.n_value - sect.addr,
787 .stab = stab,785 .stab = stab,
788 });786 });
789 }787 }
790788
791 block.contained = contained.toOwnedSlice();
792
793 // Update target section's metadata789 // Update target section's metadata
794 // TODO should we update segment's size here too?790 // TODO should we update segment's size here too?
795 // How does it tie with incremental space allocs?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 const tsect = &tseg.sections.items[match.sect];793 const tsect = &tseg.sections.items[match.sect];
798 const new_alignment = math.max(tsect.@"align", block.alignment);794 const new_alignment = math.max(tsect.@"align", block.alignment);
799 const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment);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,12 +797,12 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
801 tsect.size = new_size;797 tsect.size = new_size;
802 tsect.@"align" = new_alignment;798 tsect.@"align" = new_alignment;
803799
804 if (zld.blocks.getPtr(match)) |last| {800 if (macho_file.blocks.getPtr(match)) |last| {
805 last.*.next = block;801 last.*.next = block;
806 block.prev = last.*;802 block.prev = last.*;
807 last.* = block;803 last.* = block;
808 } else {804 } else {
809 try zld.blocks.putNoClobber(zld.allocator, match, block);805 try macho_file.blocks.putNoClobber(self.allocator, match, block);
810 }806 }
811807
812 try self.text_blocks.append(self.allocator, block);808 try self.text_blocks.append(self.allocator, block);
...@@ -814,7 +810,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {...@@ -814,7 +810,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
814 }810 }
815}811}
816812
817pub fn symbolFromReloc(self: *Object, zld: *Zld, rel: macho.relocation_info) !*Symbol {813pub fn symbolFromReloc(self: *Object, macho_file: *MachO, rel: macho.relocation_info) !*Symbol {
818 const symbol = blk: {814 const symbol = blk: {
819 if (rel.r_extern == 1) {815 if (rel.r_extern == 1) {
820 break :blk self.symbols.items[rel.r_symbolnum];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,9 +828,9 @@ pub fn symbolFromReloc(self: *Object, zld: *Zld, rel: macho.relocation_info) !*S
832 sectionName(sect),828 sectionName(sect),
833 });829 });
834 defer self.allocator.free(name);830 defer self.allocator.free(name);
835 const symbol = try zld.allocator.create(Symbol);831 const symbol = try macho_file.allocator.create(Symbol);
836 symbol.* = .{832 symbol.* = .{
837 .strx = try zld.makeString(name),833 .strx = try macho_file.makeString(name),
838 .payload = .{834 .payload = .{
839 .regular = .{835 .regular = .{
840 .linkage = .translation_unit,836 .linkage = .translation_unit,
src/link/MachO/TextBlock.zig+163-101
...@@ -12,24 +12,64 @@ const meta = std.meta;...@@ -12,24 +12,64 @@ const meta = std.meta;
1212
13const Allocator = mem.Allocator;13const Allocator = mem.Allocator;
14const Arch = std.Target.Cpu.Arch;14const Arch = std.Target.Cpu.Arch;
15const MachO = @import("../MachO.zig");
15const Object = @import("Object.zig");16const Object = @import("Object.zig");
16const Zld = @import("Zld.zig");
1717
18allocator: *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.
19local_sym_index: u32,24local_sym_index: u32,
20stab: ?Stab = null,25
21aliases: std.ArrayList(u32),26/// List of symbol aliases pointing to the same block via different nlists
22references: std.AutoArrayHashMap(u32, void),27aliases: std.ArrayListUnmanaged(u32) = .{},
23contained: ?[]SymbolAtOffset = null,28
29/// List of symbols contained within this block
30contained: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
31
32/// Code (may be non-relocated) this block represents
24code: []u8,33code: []u8,
25relocs: 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.
26size: u64,38size: u64,
27alignment: u32,39alignment: u32,
28rebases: std.ArrayList(u64),40
29bindings: std.ArrayList(SymbolAtOffset),41relocs: std.ArrayListUnmanaged(Relocation) = .{},
30dices: std.ArrayList(macho.data_in_code_entry),42
31next: ?*TextBlock = null,43/// List of offsets contained within this block that need rebasing by the dynamic
32prev: ?*TextBlock = null,44/// loader in presence of ASLR
45rebases: 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)
50bindings: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
51
52/// List of data-in-code entries. This is currently specific to x86_64 only.
53dices: 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.
59stab: ?Stab = null,
60
61/// Points to the previous and next neighbours
62next: ?*TextBlock,
63prev: ?*TextBlock,
64
65/// Previous/next linked list pointers.
66/// This is the linked list node for this Decl's corresponding .debug_info tag.
67dbg_info_prev: ?*TextBlock,
68dbg_info_next: ?*TextBlock,
69/// Offset into .debug_info pointing to the tag for this Decl.
70dbg_info_off: u32,
71/// Size of the .debug_info tag for this Decl, not including padding.
72dbg_info_len: u32,
3373
34pub const SymbolAtOffset = struct {74pub const SymbolAtOffset = struct {
35 local_sym_index: u32,75 local_sym_index: u32,
...@@ -42,11 +82,11 @@ pub const Stab = union(enum) {...@@ -42,11 +82,11 @@ pub const Stab = union(enum) {
42 static,82 static,
43 global,83 global,
4484
45 pub fn asNlists(stab: Stab, local_sym_index: u32, zld: *Zld) ![]macho.nlist_64 {85 pub fn asNlists(stab: Stab, local_sym_index: u32, macho_file: anytype) ![]macho.nlist_64 {
46 var nlists = std.ArrayList(macho.nlist_64).init(zld.allocator);86 var nlists = std.ArrayList(macho.nlist_64).init(macho_file.base.allocator);
47 defer nlists.deinit();87 defer nlists.deinit();
4888
49 const sym = zld.locals.items[local_sym_index];89 const sym = macho_file.locals.items[local_sym_index];
50 switch (stab) {90 switch (stab) {
51 .function => |size| {91 .function => |size| {
52 try nlists.ensureUnusedCapacity(4);92 try nlists.ensureUnusedCapacity(4);
...@@ -130,7 +170,7 @@ pub const Relocation = struct {...@@ -130,7 +170,7 @@ pub const Relocation = struct {
130 offset: u32,170 offset: u32,
131 source_addr: u64,171 source_addr: u64,
132 target_addr: u64,172 target_addr: u64,
133 zld: *Zld,173 macho_file: *MachO,
134 };174 };
135175
136 pub const Unsigned = struct {176 pub const Unsigned = struct {
...@@ -148,7 +188,7 @@ pub const Relocation = struct {...@@ -148,7 +188,7 @@ pub const Relocation = struct {
148 pub fn resolve(self: Unsigned, args: ResolveArgs) !void {188 pub fn resolve(self: Unsigned, args: ResolveArgs) !void {
149 const result = blk: {189 const result = blk: {
150 if (self.subtractor) |subtractor| {190 if (self.subtractor) |subtractor| {
151 const sym = args.zld.locals.items[subtractor];191 const sym = args.macho_file.locals.items[subtractor];
152 break :blk @intCast(i64, args.target_addr) - @intCast(i64, sym.n_value) + self.addend;192 break :blk @intCast(i64, args.target_addr) - @intCast(i64, sym.n_value) + self.addend;
153 } else {193 } else {
154 break :blk @intCast(i64, args.target_addr) + self.addend;194 break :blk @intCast(i64, args.target_addr) + self.addend;
...@@ -500,38 +540,59 @@ pub const Relocation = struct {...@@ -500,38 +540,59 @@ pub const Relocation = struct {
500 }540 }
501};541};
502542
503pub fn init(allocator: *Allocator) TextBlock {543pub const empty = TextBlock{
504 return .{544 .local_sym_index = 0,
505 .allocator = allocator,545 .code = undefined,
506 .local_sym_index = undefined,546 .size = 0,
507 .aliases = std.ArrayList(u32).init(allocator),547 .alignment = 0,
508 .references = std.AutoArrayHashMap(u32, void).init(allocator),548 .prev = null,
509 .code = undefined,549 .next = null,
510 .relocs = std.ArrayList(Relocation).init(allocator),550 .dbg_info_prev = null,
511 .size = undefined,551 .dbg_info_next = null,
512 .alignment = undefined,552 .dbg_info_off = undefined,
513 .rebases = std.ArrayList(u64).init(allocator),553 .dbg_info_len = undefined,
514 .bindings = std.ArrayList(SymbolAtOffset).init(allocator),554};
515 .dices = std.ArrayList(macho.data_in_code_entry).init(allocator),555
516 };556pub 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}
518565
519pub fn deinit(self: *TextBlock) void {566/// Returns how much room there is to grow in virtual address space.
520 self.aliases.deinit();567/// File offset relocation happens transparently, so it is not included in
521 self.references.deinit();568/// this calculation.
522 if (self.contained) |contained| {569pub fn capacity(self: TextBlock, macho_file: MachO) u64 {
523 self.allocator.free(contained);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);579}
526 self.relocs.deinit();580
527 self.rebases.deinit();581pub fn freeListEligible(self: TextBlock, macho_file: MachO) bool {
528 self.bindings.deinit();582 // No need to keep a free list node for the last block.
529 self.dices.deinit();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}
531592
532const RelocContext = struct {593const RelocContext = struct {
533 base_addr: u64 = 0,594 base_addr: u64 = 0,
534 zld: *Zld,595 macho_file: *MachO,
535};596};
536597
537fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocContext) !Relocation {598fn 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,19 +609,19 @@ fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocCo
548 const local_sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {609 const local_sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {
549 const seg = object.load_commands.items[object.segment_cmd_index.?].Segment;610 const seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
550 const sect = seg.sections.items[sect_id];611 const sect = seg.sections.items[sect_id];
551 const match = (try ctx.zld.getMatchingSection(sect)) orelse unreachable;612 const match = (try ctx.macho_file.getMatchingSection(sect)) orelse unreachable;
552 const local_sym_index = @intCast(u32, ctx.zld.locals.items.len);613 const local_sym_index = @intCast(u32, ctx.macho_file.locals.items.len);
553 const sym_name = try std.fmt.allocPrint(ctx.zld.allocator, "l_{s}_{s}_{s}", .{614 const sym_name = try std.fmt.allocPrint(ctx.macho_file.base.allocator, "l_{s}_{s}_{s}", .{
554 object.name.?,615 object.name.?,
555 commands.segmentName(sect),616 commands.segmentName(sect),
556 commands.sectionName(sect),617 commands.sectionName(sect),
557 });618 });
558 defer ctx.zld.allocator.free(sym_name);619 defer ctx.macho_file.base.allocator.free(sym_name);
559620
560 try ctx.zld.locals.append(ctx.zld.allocator, .{621 try ctx.macho_file.locals.append(ctx.macho_file.base.allocator, .{
561 .n_strx = try ctx.zld.makeString(sym_name),622 .n_strx = try ctx.macho_file.makeString(sym_name),
562 .n_type = macho.N_SECT,623 .n_type = macho.N_SECT,
563 .n_sect = ctx.zld.sectionId(match),624 .n_sect = ctx.macho_file.sectionId(match),
564 .n_desc = 0,625 .n_desc = 0,
565 .n_value = sect.addr,626 .n_value = sect.addr,
566 });627 });
...@@ -574,12 +635,12 @@ fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocCo...@@ -574,12 +635,12 @@ fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocCo
574 const sym = object.symtab.items[rel.r_symbolnum];635 const sym = object.symtab.items[rel.r_symbolnum];
575 const sym_name = object.getString(sym.n_strx);636 const sym_name = object.getString(sym.n_strx);
576637
577 if (Zld.symbolIsSect(sym) and !Zld.symbolIsExt(sym)) {638 if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) {
578 const where_index = object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;639 const where_index = object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
579 parsed_rel.where = .local;640 parsed_rel.where = .local;
580 parsed_rel.where_index = where_index;641 parsed_rel.where_index = where_index;
581 } else {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 switch (resolv.where) {644 switch (resolv.where) {
584 .global => {645 .global => {
585 parsed_rel.where = .local;646 parsed_rel.where = .local;
...@@ -599,6 +660,7 @@ fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocCo...@@ -599,6 +660,7 @@ fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocCo
599660
600pub fn parseRelocsFromObject(661pub fn parseRelocsFromObject(
601 self: *TextBlock,662 self: *TextBlock,
663 allocator: *Allocator,
602 relocs: []macho.relocation_info,664 relocs: []macho.relocation_info,
603 object: *Object,665 object: *Object,
604 ctx: RelocContext,666 ctx: RelocContext,
...@@ -638,11 +700,11 @@ pub fn parseRelocsFromObject(...@@ -638,11 +700,11 @@ pub fn parseRelocsFromObject(
638 const sym = object.symtab.items[rel.r_symbolnum];700 const sym = object.symtab.items[rel.r_symbolnum];
639 const sym_name = object.getString(sym.n_strx);701 const sym_name = object.getString(sym.n_strx);
640702
641 if (Zld.symbolIsSect(sym) and !Zld.symbolIsExt(sym)) {703 if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) {
642 const where_index = object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;704 const where_index = object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
643 subtractor = where_index;705 subtractor = where_index;
644 } else {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 assert(resolv.where == .global);708 assert(resolv.where == .global);
647 subtractor = resolv.local_sym_index;709 subtractor = resolv.local_sym_index;
648 }710 }
...@@ -732,11 +794,7 @@ pub fn parseRelocsFromObject(...@@ -732,11 +794,7 @@ pub fn parseRelocsFromObject(
732 else => unreachable,794 else => unreachable,
733 }795 }
734796
735 try self.relocs.append(parsed_rel);797 try self.relocs.append(allocator, parsed_rel);
736
737 if (parsed_rel.where == .local) {
738 try self.references.put(parsed_rel.where_index, {});
739 }
740798
741 const is_via_got = switch (parsed_rel.payload) {799 const is_via_got = switch (parsed_rel.payload) {
742 .pointer_to_got => true,800 .pointer_to_got => true,
...@@ -747,28 +805,30 @@ pub fn parseRelocsFromObject(...@@ -747,28 +805,30 @@ pub fn parseRelocsFromObject(
747 };805 };
748806
749 if (is_via_got) blk: {807 if (is_via_got) blk: {
750 const key = Zld.GotIndirectionKey{808 const key = MachO.GotIndirectionKey{
751 .where = switch (parsed_rel.where) {809 .where = switch (parsed_rel.where) {
752 .local => .local,810 .local => .local,
753 .import => .import,811 .import => .import,
754 },812 },
755 .where_index = parsed_rel.where_index,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;
758816
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 } else if (parsed_rel.payload == .unsigned) {820 } else if (parsed_rel.payload == .unsigned) {
761 switch (parsed_rel.where) {821 switch (parsed_rel.where) {
762 .import => {822 .import => {
763 try self.bindings.append(.{823 try self.bindings.append(allocator, .{
764 .local_sym_index = parsed_rel.where_index,824 .local_sym_index = parsed_rel.where_index,
765 .offset = parsed_rel.offset,825 .offset = parsed_rel.offset,
766 });826 });
767 },827 },
768 .local => {828 .local => {
769 const source_sym = ctx.zld.locals.items[self.local_sym_index];829 const source_sym = ctx.macho_file.locals.items[self.local_sym_index];
770 const match = ctx.zld.unpackSectionId(source_sym.n_sect);830 const match = ctx.macho_file.unpackSectionId(source_sym.n_sect);
771 const seg = ctx.zld.load_commands.items[match.seg].Segment;831 const seg = ctx.macho_file.load_commands.items[match.seg].Segment;
772 const sect = seg.sections.items[match.sect];832 const sect = seg.sections.items[match.sect];
773 const sect_type = commands.sectionType(sect);833 const sect_type = commands.sectionType(sect);
774834
...@@ -778,12 +838,12 @@ pub fn parseRelocsFromObject(...@@ -778,12 +838,12 @@ pub fn parseRelocsFromObject(
778 // TODO actually, a check similar to what dyld is doing, that is, verifying838 // TODO actually, a check similar to what dyld is doing, that is, verifying
779 // that the segment is writable should be enough here.839 // that the segment is writable should be enough here.
780 const is_right_segment = blk: {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 if (match.seg == idx) {842 if (match.seg == idx) {
783 break :blk true;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 if (match.seg == idx) {847 if (match.seg == idx) {
788 break :blk true;848 break :blk true;
789 }849 }
...@@ -804,15 +864,17 @@ pub fn parseRelocsFromObject(...@@ -804,15 +864,17 @@ pub fn parseRelocsFromObject(
804 };864 };
805865
806 if (should_rebase) {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 } else if (parsed_rel.payload == .branch) blk: {871 } else if (parsed_rel.payload == .branch) blk: {
812 if (parsed_rel.where != .import) break :blk;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;
814874
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,7 +914,7 @@ fn parseUnsigned(
852914
853 if (rel.r_extern == 0) {915 if (rel.r_extern == 0) {
854 assert(out.where == .local);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 addend -= @intCast(i64, target_sym.n_value);918 addend -= @intCast(i64, target_sym.n_value);
857 }919 }
858920
...@@ -872,7 +934,7 @@ fn parseBranch(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ct...@@ -872,7 +934,7 @@ fn parseBranch(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ct
872934
873 out.payload = .{935 out.payload = .{
874 .branch = .{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,10 +1010,10 @@ fn parseSigned(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ct
948 var addend: i64 = mem.readIntLittle(i32, self.code[out.offset..][0..4]) + correction;1010 var addend: i64 = mem.readIntLittle(i32, self.code[out.offset..][0..4]) + correction;
9491011
950 if (rel.r_extern == 0) {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 const target_sym = switch (out.where) {1014 const target_sym = switch (out.where) {
953 .local => ctx.zld.locals.items[out.where_index],1015 .local => ctx.macho_file.locals.items[out.where_index],
954 .import => ctx.zld.imports.items[out.where_index],1016 .import => ctx.macho_file.imports.items[out.where_index],
955 };1017 };
956 addend = @intCast(i64, source_sym.n_value + out.offset + 4) + addend - @intCast(i64, target_sym.n_value);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,12 +1048,12 @@ fn parseLoad(self: TextBlock, rel: macho.relocation_info, out: *Relocation) void
986 };1048 };
987}1049}
9881050
989pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {1051pub fn resolveRelocs(self: *TextBlock, macho_file: *MachO) !void {
990 for (self.relocs.items) |rel| {1052 for (self.relocs.items) |rel| {
991 log.debug("relocating {}", .{rel});1053 log.debug("relocating {}", .{rel});
9921054
993 const source_addr = blk: {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 break :blk sym.n_value + rel.offset;1057 break :blk sym.n_value + rel.offset;
996 };1058 };
997 const target_addr = blk: {1059 const target_addr = blk: {
...@@ -1004,9 +1066,9 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {...@@ -1004,9 +1066,9 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
1004 };1066 };
10051067
1006 if (is_via_got) {1068 if (is_via_got) {
1007 const dc_seg = zld.load_commands.items[zld.data_const_segment_cmd_index.?].Segment;1069 const dc_seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
1008 const got = dc_seg.sections.items[zld.got_section_index.?];1070 const got = dc_seg.sections.items[macho_file.got_section_index.?];
1009 const got_index = zld.got_entries.getIndex(.{1071 const got_index = macho_file.got_entries_map.get(.{
1010 .where = switch (rel.where) {1072 .where = switch (rel.where) {
1011 .local => .local,1073 .local => .local,
1012 .import => .import,1074 .import => .import,
...@@ -1014,10 +1076,10 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {...@@ -1014,10 +1076,10 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
1014 .where_index = rel.where_index,1076 .where_index = rel.where_index,
1015 }) orelse {1077 }) orelse {
1016 const sym = switch (rel.where) {1078 const sym = switch (rel.where) {
1017 .local => zld.locals.items[rel.where_index],1079 .local => macho_file.locals.items[rel.where_index],
1018 .import => zld.imports.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 log.err(" this is an internal linker error", .{});1083 log.err(" this is an internal linker error", .{});
1022 return error.FailedToResolveRelocationTarget;1084 return error.FailedToResolveRelocationTarget;
1023 };1085 };
...@@ -1026,11 +1088,11 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {...@@ -1026,11 +1088,11 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
10261088
1027 switch (rel.where) {1089 switch (rel.where) {
1028 .local => {1090 .local => {
1029 const sym = zld.locals.items[rel.where_index];1091 const sym = macho_file.locals.items[rel.where_index];
1030 const is_tlv = is_tlv: {1092 const is_tlv = is_tlv: {
1031 const source_sym = zld.locals.items[self.local_sym_index];1093 const source_sym = macho_file.locals.items[self.local_sym_index];
1032 const match = zld.unpackSectionId(source_sym.n_sect);1094 const match = macho_file.unpackSectionId(source_sym.n_sect);
1033 const seg = zld.load_commands.items[match.seg].Segment;1095 const seg = macho_file.load_commands.items[match.seg].Segment;
1034 const sect = seg.sections.items[match.sect];1096 const sect = seg.sections.items[match.sect];
1035 break :is_tlv commands.sectionType(sect) == macho.S_THREAD_LOCAL_VARIABLES;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,11 +1102,11 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
1040 // defined TLV template init section in the following order:1102 // defined TLV template init section in the following order:
1041 // * wrt to __thread_data if defined, then1103 // * wrt to __thread_data if defined, then
1042 // * wrt to __thread_bss1104 // * 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 const base_address = inner: {1106 const base_address = inner: {
1045 if (zld.tlv_data_section_index) |i| {1107 if (macho_file.tlv_data_section_index) |i| {
1046 break :inner seg.sections.items[i].addr;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 break :inner seg.sections.items[i].addr;1110 break :inner seg.sections.items[i].addr;
1049 } else {1111 } else {
1050 log.err("threadlocal variables present but no initializer sections found", .{});1112 log.err("threadlocal variables present but no initializer sections found", .{});
...@@ -1059,12 +1121,12 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {...@@ -1059,12 +1121,12 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
1059 break :blk sym.n_value;1121 break :blk sym.n_value;
1060 },1122 },
1061 .import => {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 // TODO verify in TextBlock that the symbol is indeed dynamically bound.1125 // TODO verify in TextBlock that the symbol is indeed dynamically bound.
1064 break :blk 0; // Dynamically bound by dyld.1126 break :blk 0; // Dynamically bound by dyld.
1065 };1127 };
1066 const segment = zld.load_commands.items[zld.text_segment_cmd_index.?].Segment;1128 const segment = macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment;
1067 const stubs = segment.sections.items[zld.stubs_section_index.?];1129 const stubs = segment.sections.items[macho_file.stubs_section_index.?];
1068 break :blk stubs.addr + stubs_index * stubs.reserved2;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,14 +1140,14 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
1078 .offset = rel.offset,1140 .offset = rel.offset,
1079 .source_addr = source_addr,1141 .source_addr = source_addr,
1080 .target_addr = target_addr,1142 .target_addr = target_addr,
1081 .zld = zld,1143 .macho_file = macho_file,
1082 });1144 });
1083 }1145 }
1084}1146}
10851147
1086pub fn print_this(self: *const TextBlock, zld: *Zld) void {1148pub fn print_this(self: *const TextBlock, macho_file: MachO) void {
1087 log.warn("TextBlock", .{});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 if (self.stab) |stab| {1151 if (self.stab) |stab| {
1090 log.warn(" stab: {}", .{stab});1152 log.warn(" stab: {}", .{stab});
1091 }1153 }
...@@ -1125,11 +1187,11 @@ pub fn print_this(self: *const TextBlock, zld: *Zld) void {...@@ -1125,11 +1187,11 @@ pub fn print_this(self: *const TextBlock, zld: *Zld) void {
1125 log.warn(" align = {}", .{self.alignment});1187 log.warn(" align = {}", .{self.alignment});
1126}1188}
11271189
1128pub fn print(self: *const TextBlock, zld: *Zld) void {1190pub fn print(self: *const TextBlock, macho_file: MachO) void {
1129 if (self.prev) |prev| {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}
11341196
1135const RelocIterator = struct {1197const RelocIterator = struct {
...@@ -1159,8 +1221,8 @@ fn filterRelocs(relocs: []macho.relocation_info, start_addr: u64, end_addr: u64)...@@ -1159,8 +1221,8 @@ fn filterRelocs(relocs: []macho.relocation_info, start_addr: u64, end_addr: u64)
1159 }1221 }
1160 };1222 };
11611223
1162 const start = Zld.findFirst(macho.relocation_info, relocs, 0, Predicate{ .addr = end_addr });1224 const start = MachO.findFirst(macho.relocation_info, relocs, 0, Predicate{ .addr = end_addr });
1163 const end = Zld.findFirst(macho.relocation_info, relocs, start, Predicate{ .addr = start_addr });1225 const end = MachO.findFirst(macho.relocation_info, relocs, start, Predicate{ .addr = start_addr });
11641226
1165 return relocs[start..end];1227 return relocs[start..end];
1166}1228}
src/link/MachO/Zld.zig deleted-3062
...@@ -1,3062 +0,0 @@
1const Zld = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const leb = std.leb;
6const mem = std.mem;
7const meta = std.meta;
8const fs = std.fs;
9const macho = std.macho;
10const math = std.math;
11const log = std.log.scoped(.zld);
12const aarch64 = @import("../../codegen/aarch64.zig");
13
14const Allocator = mem.Allocator;
15const Archive = @import("Archive.zig");
16const CodeSignature = @import("CodeSignature.zig");
17const Dylib = @import("Dylib.zig");
18const Object = @import("Object.zig");
19const TextBlock = @import("TextBlock.zig");
20const Trie = @import("Trie.zig");
21
22usingnamespace @import("commands.zig");
23usingnamespace @import("bind.zig");
24
25allocator: *Allocator,
26
27target: ?std.Target = null,
28page_size: ?u16 = null,
29file: ?fs.File = null,
30output: ?Output = null,
31
32// TODO these args will become obselete once Zld is coalesced with incremental
33// linker.
34stack_size: u64 = 0,
35
36objects: std.ArrayListUnmanaged(*Object) = .{},
37archives: std.ArrayListUnmanaged(*Archive) = .{},
38dylibs: std.ArrayListUnmanaged(*Dylib) = .{},
39
40next_dylib_ordinal: u16 = 1,
41
42load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
43
44pagezero_segment_cmd_index: ?u16 = null,
45text_segment_cmd_index: ?u16 = null,
46data_const_segment_cmd_index: ?u16 = null,
47data_segment_cmd_index: ?u16 = null,
48linkedit_segment_cmd_index: ?u16 = null,
49dyld_info_cmd_index: ?u16 = null,
50symtab_cmd_index: ?u16 = null,
51dysymtab_cmd_index: ?u16 = null,
52dylinker_cmd_index: ?u16 = null,
53data_in_code_cmd_index: ?u16 = null,
54function_starts_cmd_index: ?u16 = null,
55main_cmd_index: ?u16 = null,
56dylib_id_cmd_index: ?u16 = null,
57version_min_cmd_index: ?u16 = null,
58source_version_cmd_index: ?u16 = null,
59uuid_cmd_index: ?u16 = null,
60code_signature_cmd_index: ?u16 = null,
61
62// __TEXT segment sections
63text_section_index: ?u16 = null,
64stubs_section_index: ?u16 = null,
65stub_helper_section_index: ?u16 = null,
66text_const_section_index: ?u16 = null,
67cstring_section_index: ?u16 = null,
68ustring_section_index: ?u16 = null,
69gcc_except_tab_section_index: ?u16 = null,
70unwind_info_section_index: ?u16 = null,
71eh_frame_section_index: ?u16 = null,
72
73objc_methlist_section_index: ?u16 = null,
74objc_methname_section_index: ?u16 = null,
75objc_methtype_section_index: ?u16 = null,
76objc_classname_section_index: ?u16 = null,
77
78// __DATA_CONST segment sections
79got_section_index: ?u16 = null,
80mod_init_func_section_index: ?u16 = null,
81mod_term_func_section_index: ?u16 = null,
82data_const_section_index: ?u16 = null,
83
84objc_cfstring_section_index: ?u16 = null,
85objc_classlist_section_index: ?u16 = null,
86objc_imageinfo_section_index: ?u16 = null,
87
88// __DATA segment sections
89tlv_section_index: ?u16 = null,
90tlv_data_section_index: ?u16 = null,
91tlv_bss_section_index: ?u16 = null,
92la_symbol_ptr_section_index: ?u16 = null,
93data_section_index: ?u16 = null,
94bss_section_index: ?u16 = null,
95common_section_index: ?u16 = null,
96
97objc_const_section_index: ?u16 = null,
98objc_selrefs_section_index: ?u16 = null,
99objc_classrefs_section_index: ?u16 = null,
100objc_data_section_index: ?u16 = null,
101
102locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
103globals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
104imports: std.ArrayListUnmanaged(macho.nlist_64) = .{},
105undefs: std.ArrayListUnmanaged(macho.nlist_64) = .{},
106tentatives: std.ArrayListUnmanaged(macho.nlist_64) = .{},
107symbol_resolver: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
108
109strtab: std.ArrayListUnmanaged(u8) = .{},
110
111stubs: std.AutoArrayHashMapUnmanaged(u32, void) = .{},
112got_entries: std.AutoArrayHashMapUnmanaged(GotIndirectionKey, void) = .{},
113
114stub_helper_stubs_start_off: ?u64 = null,
115
116blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{},
117
118has_dices: bool = false,
119has_stabs: bool = false,
120
121const 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
134pub const GotIndirectionKey = struct {
135 where: enum {
136 local,
137 import,
138 },
139 where_index: u32,
140};
141
142pub 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
149const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld";
150
151pub fn init(allocator: *Allocator) !Zld {
152 return Zld{ .allocator = allocator };
153}
154
155pub 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
199pub 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
209const LinkArgs = struct {
210 syslibroot: ?[]const u8,
211 libs: []const []const u8,
212 rpaths: []const []const u8,
213};
214
215pub 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
312fn 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
345fn 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
367pub const MatchingSection = struct {
368 seg: u16,
369 sect: u16,
370};
371
372pub 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
773fn 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
896fn 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
947fn 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
962fn 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
979fn 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
986fn 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
1005fn 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
1077fn 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
1136fn 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
1279fn 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
1299fn 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
1370fn 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
1411fn 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
1621fn 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
1880fn parseTextBlocks(self: *Zld) !void {
1881 for (self.objects.items) |object| {
1882 try object.parseTextBlocks(self);
1883 }
1884}
1885
1886fn 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
2167fn 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
2181fn 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
2195fn 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
2214fn 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
2276fn 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
2299fn 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
2316fn 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
2399fn 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
2471fn 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
2513fn 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
2577fn 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
2618fn 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
2763fn 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
2780fn 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
2834fn 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
2857fn 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
2880fn 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
2898fn 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
2940pub 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
2952pub 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
2957pub fn symbolIsStab(sym: macho.nlist_64) bool {
2958 return (macho.N_STAB & sym.n_type) != 0;
2959}
2960
2961pub fn symbolIsPext(sym: macho.nlist_64) bool {
2962 return (macho.N_PEXT & sym.n_type) != 0;
2963}
2964
2965pub fn symbolIsExt(sym: macho.nlist_64) bool {
2966 return (macho.N_EXT & sym.n_type) != 0;
2967}
2968
2969pub fn symbolIsSect(sym: macho.nlist_64) bool {
2970 const type_ = macho.N_TYPE & sym.n_type;
2971 return type_ == macho.N_SECT;
2972}
2973
2974pub fn symbolIsUndf(sym: macho.nlist_64) bool {
2975 const type_ = macho.N_TYPE & sym.n_type;
2976 return type_ == macho.N_UNDF;
2977}
2978
2979pub fn symbolIsIndr(sym: macho.nlist_64) bool {
2980 const type_ = macho.N_TYPE & sym.n_type;
2981 return type_ == macho.N_INDR;
2982}
2983
2984pub fn symbolIsAbs(sym: macho.nlist_64) bool {
2985 const type_ = macho.N_TYPE & sym.n_type;
2986 return type_ == macho.N_ABS;
2987}
2988
2989pub fn symbolIsWeakDef(sym: macho.nlist_64) bool {
2990 return (sym.n_desc & macho.N_WEAK_DEF) != 0;
2991}
2992
2993pub fn symbolIsWeakRef(sym: macho.nlist_64) bool {
2994 return (sym.n_desc & macho.N_WEAK_REF) != 0;
2995}
2996
2997pub fn symbolIsTentative(sym: macho.nlist_64) bool {
2998 if (!symbolIsUndf(sym)) return false;
2999 return sym.n_value != 0;
3000}
3001
3002pub 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
3006pub 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
3012pub 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
3026pub 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
3043fn packDylibOrdinal(ordinal: u16) u16 {
3044 return ordinal * macho.N_SYMBOL_RESOLVER;
3045}
3046
3047fn unpackDylibOrdinal(pack: u16) u16 {
3048 return @divExact(pack, macho.N_SYMBOL_RESOLVER);
3049}
3050
3051pub 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}