zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob a4ee93cf (131871B) - Raw


      1 const Zld = @This();
      2 
      3 const std = @import("std");
      4 const assert = std.debug.assert;
      5 const leb = std.leb;
      6 const mem = std.mem;
      7 const meta = std.meta;
      8 const fs = std.fs;
      9 const macho = std.macho;
     10 const math = std.math;
     11 const log = std.log.scoped(.zld);
     12 const aarch64 = @import("../../codegen/aarch64.zig");
     13 const reloc = @import("reloc.zig");
     14 
     15 const Allocator = mem.Allocator;
     16 const Archive = @import("Archive.zig");
     17 const CodeSignature = @import("CodeSignature.zig");
     18 const Dylib = @import("Dylib.zig");
     19 const Object = @import("Object.zig");
     20 const Symbol = @import("Symbol.zig");
     21 const Trie = @import("Trie.zig");
     22 
     23 usingnamespace @import("commands.zig");
     24 usingnamespace @import("bind.zig");
     25 
     26 allocator: *Allocator,
     27 
     28 arch: ?std.Target.Cpu.Arch = null,
     29 page_size: ?u16 = null,
     30 file: ?fs.File = null,
     31 out_path: ?[]const u8 = null,
     32 
     33 // TODO these args will become obselete once Zld is coalesced with incremental
     34 // linker.
     35 syslibroot: ?[]const u8 = null,
     36 stack_size: u64 = 0,
     37 
     38 objects: std.ArrayListUnmanaged(*Object) = .{},
     39 archives: std.ArrayListUnmanaged(*Archive) = .{},
     40 dylibs: std.ArrayListUnmanaged(*Dylib) = .{},
     41 
     42 libsystem_dylib_index: ?u16 = null,
     43 next_dylib_ordinal: u16 = 1,
     44 
     45 load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
     46 
     47 pagezero_segment_cmd_index: ?u16 = null,
     48 text_segment_cmd_index: ?u16 = null,
     49 data_const_segment_cmd_index: ?u16 = null,
     50 data_segment_cmd_index: ?u16 = null,
     51 linkedit_segment_cmd_index: ?u16 = null,
     52 dyld_info_cmd_index: ?u16 = null,
     53 symtab_cmd_index: ?u16 = null,
     54 dysymtab_cmd_index: ?u16 = null,
     55 dylinker_cmd_index: ?u16 = null,
     56 data_in_code_cmd_index: ?u16 = null,
     57 function_starts_cmd_index: ?u16 = null,
     58 main_cmd_index: ?u16 = null,
     59 version_min_cmd_index: ?u16 = null,
     60 source_version_cmd_index: ?u16 = null,
     61 uuid_cmd_index: ?u16 = null,
     62 code_signature_cmd_index: ?u16 = null,
     63 
     64 // __TEXT segment sections
     65 text_section_index: ?u16 = null,
     66 stubs_section_index: ?u16 = null,
     67 stub_helper_section_index: ?u16 = null,
     68 text_const_section_index: ?u16 = null,
     69 cstring_section_index: ?u16 = null,
     70 ustring_section_index: ?u16 = null,
     71 gcc_except_tab_section_index: ?u16 = null,
     72 unwind_info_section_index: ?u16 = null,
     73 eh_frame_section_index: ?u16 = null,
     74 
     75 objc_methlist_section_index: ?u16 = null,
     76 objc_methname_section_index: ?u16 = null,
     77 objc_methtype_section_index: ?u16 = null,
     78 objc_classname_section_index: ?u16 = null,
     79 
     80 // __DATA_CONST segment sections
     81 got_section_index: ?u16 = null,
     82 mod_init_func_section_index: ?u16 = null,
     83 mod_term_func_section_index: ?u16 = null,
     84 data_const_section_index: ?u16 = null,
     85 
     86 objc_cfstring_section_index: ?u16 = null,
     87 objc_classlist_section_index: ?u16 = null,
     88 objc_imageinfo_section_index: ?u16 = null,
     89 
     90 // __DATA segment sections
     91 tlv_section_index: ?u16 = null,
     92 tlv_data_section_index: ?u16 = null,
     93 tlv_bss_section_index: ?u16 = null,
     94 la_symbol_ptr_section_index: ?u16 = null,
     95 data_section_index: ?u16 = null,
     96 bss_section_index: ?u16 = null,
     97 common_section_index: ?u16 = null,
     98 
     99 objc_const_section_index: ?u16 = null,
    100 objc_selrefs_section_index: ?u16 = null,
    101 objc_classrefs_section_index: ?u16 = null,
    102 objc_data_section_index: ?u16 = null,
    103 
    104 globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
    105 imports: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
    106 unresolved: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
    107 tentatives: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
    108 
    109 /// Offset into __DATA,__common section.
    110 /// Set if the linker found tentative definitions in any of the objects.
    111 tentative_defs_offset: u64 = 0,
    112 
    113 strtab: std.ArrayListUnmanaged(u8) = .{},
    114 strtab_dir: std.StringHashMapUnmanaged(u32) = .{},
    115 
    116 threadlocal_offsets: std.ArrayListUnmanaged(TlvOffset) = .{}, // TODO merge with Symbol abstraction
    117 local_rebases: std.ArrayListUnmanaged(Pointer) = .{},
    118 stubs: std.ArrayListUnmanaged(*Symbol) = .{},
    119 got_entries: std.ArrayListUnmanaged(*Symbol) = .{},
    120 
    121 stub_helper_stubs_start_off: ?u64 = null,
    122 
    123 const TlvOffset = struct {
    124     source_addr: u64,
    125     offset: u64,
    126 
    127     fn cmp(context: void, a: TlvOffset, b: TlvOffset) bool {
    128         _ = context;
    129         return a.source_addr < b.source_addr;
    130     }
    131 };
    132 
    133 /// Default path to dyld
    134 const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld";
    135 
    136 pub fn init(allocator: *Allocator) Zld {
    137     return .{ .allocator = allocator };
    138 }
    139 
    140 pub fn deinit(self: *Zld) void {
    141     self.threadlocal_offsets.deinit(self.allocator);
    142     self.local_rebases.deinit(self.allocator);
    143     self.stubs.deinit(self.allocator);
    144     self.got_entries.deinit(self.allocator);
    145 
    146     for (self.load_commands.items) |*lc| {
    147         lc.deinit(self.allocator);
    148     }
    149     self.load_commands.deinit(self.allocator);
    150 
    151     for (self.objects.items) |object| {
    152         object.deinit();
    153         self.allocator.destroy(object);
    154     }
    155     self.objects.deinit(self.allocator);
    156 
    157     for (self.archives.items) |archive| {
    158         archive.deinit();
    159         self.allocator.destroy(archive);
    160     }
    161     self.archives.deinit(self.allocator);
    162 
    163     for (self.dylibs.items) |dylib| {
    164         dylib.deinit();
    165         self.allocator.destroy(dylib);
    166     }
    167     self.dylibs.deinit(self.allocator);
    168 
    169     for (self.imports.values()) |proxy| {
    170         proxy.deinit(self.allocator);
    171         self.allocator.destroy(proxy);
    172     }
    173     self.imports.deinit(self.allocator);
    174 
    175     self.tentatives.deinit(self.allocator);
    176     self.globals.deinit(self.allocator);
    177     self.unresolved.deinit(self.allocator);
    178     self.strtab.deinit(self.allocator);
    179 
    180     {
    181         var it = self.strtab_dir.keyIterator();
    182         while (it.next()) |key| {
    183             self.allocator.free(key.*);
    184         }
    185     }
    186     self.strtab_dir.deinit(self.allocator);
    187 }
    188 
    189 pub fn closeFiles(self: Zld) void {
    190     for (self.objects.items) |object| {
    191         object.closeFile();
    192     }
    193     for (self.archives.items) |archive| {
    194         archive.closeFile();
    195     }
    196     if (self.file) |f| f.close();
    197 }
    198 
    199 const LinkArgs = struct {
    200     libs: []const []const u8,
    201     rpaths: []const []const u8,
    202     libc_stub_path: []const u8,
    203 };
    204 
    205 pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: LinkArgs) !void {
    206     if (files.len == 0) return error.NoInputFiles;
    207     if (out_path.len == 0) return error.EmptyOutputPath;
    208 
    209     if (self.arch == null) {
    210         // Try inferring the arch from the object files.
    211         self.arch = blk: {
    212             const file = try fs.cwd().openFile(files[0], .{});
    213             defer file.close();
    214             var reader = file.reader();
    215             const header = try reader.readStruct(macho.mach_header_64);
    216             const arch: std.Target.Cpu.Arch = switch (header.cputype) {
    217                 macho.CPU_TYPE_X86_64 => .x86_64,
    218                 macho.CPU_TYPE_ARM64 => .aarch64,
    219                 else => |value| {
    220                     log.err("unsupported cpu architecture 0x{x}", .{value});
    221                     return error.UnsupportedCpuArchitecture;
    222                 },
    223             };
    224             break :blk arch;
    225         };
    226     }
    227 
    228     self.page_size = switch (self.arch.?) {
    229         .aarch64 => 0x4000,
    230         .x86_64 => 0x1000,
    231         else => unreachable,
    232     };
    233     self.out_path = out_path;
    234     self.file = try fs.cwd().createFile(out_path, .{
    235         .truncate = true,
    236         .read = true,
    237         .mode = if (std.Target.current.os.tag == .windows) 0 else 0o777,
    238     });
    239 
    240     try self.populateMetadata();
    241     try self.addRpaths(args.rpaths);
    242     try self.parseInputFiles(files);
    243     try self.parseLibs(args.libs);
    244     try self.parseLibSystem(args.libc_stub_path);
    245     try self.resolveSymbols();
    246     try self.resolveStubsAndGotEntries();
    247     try self.updateMetadata();
    248     try self.sortSections();
    249     try self.allocateTextSegment();
    250     try self.allocateDataConstSegment();
    251     try self.allocateDataSegment();
    252     self.allocateLinkeditSegment();
    253     try self.allocateSymbols();
    254     try self.allocateTentativeSymbols();
    255     try self.allocateProxyBindAddresses();
    256     try self.flush();
    257 }
    258 
    259 fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
    260     for (files) |file_name| {
    261         const full_path = full_path: {
    262             var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
    263             const path = try std.fs.realpath(file_name, &buffer);
    264             break :full_path try self.allocator.dupe(u8, path);
    265         };
    266 
    267         if (try Object.createAndParseFromPath(self.allocator, self.arch.?, full_path)) |object| {
    268             try self.objects.append(self.allocator, object);
    269             continue;
    270         }
    271 
    272         if (try Archive.createAndParseFromPath(self.allocator, self.arch.?, full_path)) |archive| {
    273             try self.archives.append(self.allocator, archive);
    274             continue;
    275         }
    276 
    277         if (try Dylib.createAndParseFromPath(
    278             self.allocator,
    279             self.arch.?,
    280             full_path,
    281             self.syslibroot,
    282             true,
    283         )) |dylib| {
    284             try self.dylibs.append(self.allocator, dylib);
    285             continue;
    286         }
    287 
    288         log.warn("unknown filetype for positional input file: '{s}'", .{file_name});
    289     }
    290 }
    291 
    292 fn parseLibs(self: *Zld, libs: []const []const u8) !void {
    293     const DylibDeps = struct {
    294         fn bubbleUp(out: *std.ArrayList(*Dylib), next: *Dylib) error{OutOfMemory}!void {
    295             try out.ensureUnusedCapacity(next.dylibs.items.len);
    296             for (next.dylibs.items) |dylib| {
    297                 out.appendAssumeCapacity(dylib);
    298             }
    299             for (next.dylibs.items) |dylib| {
    300                 try bubbleUp(out, dylib);
    301             }
    302         }
    303     };
    304 
    305     for (libs) |lib| {
    306         if (try Dylib.createAndParseFromPath(
    307             self.allocator,
    308             self.arch.?,
    309             lib,
    310             self.syslibroot,
    311             true,
    312         )) |dylib| {
    313             try self.dylibs.append(self.allocator, dylib);
    314             continue;
    315         }
    316 
    317         if (try Archive.createAndParseFromPath(self.allocator, self.arch.?, lib)) |archive| {
    318             try self.archives.append(self.allocator, archive);
    319             continue;
    320         }
    321 
    322         log.warn("unknown filetype for a library: '{s}'", .{lib});
    323     }
    324 
    325     // Flatten out any parsed dependencies.
    326     var deps = std.ArrayList(*Dylib).init(self.allocator);
    327     defer deps.deinit();
    328 
    329     for (self.dylibs.items) |dylib| {
    330         try DylibDeps.bubbleUp(&deps, dylib);
    331     }
    332 
    333     try self.dylibs.appendSlice(self.allocator, deps.toOwnedSlice());
    334 }
    335 
    336 fn parseLibSystem(self: *Zld, libc_stub_path: []const u8) !void {
    337     const dylib = (try Dylib.createAndParseFromPath(
    338         self.allocator,
    339         self.arch.?,
    340         libc_stub_path,
    341         self.syslibroot,
    342         false,
    343     )) orelse return error.FailedToParseLibSystem;
    344     self.libsystem_dylib_index = @intCast(u16, self.dylibs.items.len);
    345     try self.dylibs.append(self.allocator, dylib);
    346 
    347     // Add LC_LOAD_DYLIB load command.
    348     dylib.ordinal = self.next_dylib_ordinal;
    349     const dylib_id = dylib.id orelse unreachable;
    350     var dylib_cmd = try createLoadDylibCommand(
    351         self.allocator,
    352         dylib_id.name,
    353         dylib_id.timestamp,
    354         dylib_id.current_version,
    355         dylib_id.compatibility_version,
    356     );
    357     errdefer dylib_cmd.deinit(self.allocator);
    358     try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
    359     self.next_dylib_ordinal += 1;
    360 }
    361 
    362 fn mapAndUpdateSections(
    363     self: *Zld,
    364     object: *Object,
    365     source_sect_id: u16,
    366     target_seg_id: u16,
    367     target_sect_id: u16,
    368 ) !void {
    369     const source_sect = &object.sections.items[source_sect_id];
    370     const target_seg = &self.load_commands.items[target_seg_id].Segment;
    371     const target_sect = &target_seg.sections.items[target_sect_id];
    372 
    373     const alignment = try math.powi(u32, 2, target_sect.@"align");
    374     const offset = mem.alignForwardGeneric(u64, target_sect.size, alignment);
    375     const size = mem.alignForwardGeneric(u64, source_sect.inner.size, alignment);
    376 
    377     log.debug("{s}: '{s},{s}' mapped to '{s},{s}' from 0x{x} to 0x{x}", .{
    378         object.name.?,
    379         parseName(&source_sect.inner.segname),
    380         parseName(&source_sect.inner.sectname),
    381         parseName(&target_sect.segname),
    382         parseName(&target_sect.sectname),
    383         offset,
    384         offset + size,
    385     });
    386 
    387     source_sect.target_map = .{
    388         .segment_id = target_seg_id,
    389         .section_id = target_sect_id,
    390         .offset = @intCast(u32, offset),
    391     };
    392     target_sect.size = offset + size;
    393 }
    394 
    395 fn updateMetadata(self: *Zld) !void {
    396     for (self.objects.items) |object| {
    397         // Find ideal section alignment and update section mappings
    398         for (object.sections.items) |sect, sect_id| {
    399             const match = (try self.getMatchingSection(sect)) orelse {
    400                 log.debug("{s}: unhandled section type 0x{x} for '{s},{s}'", .{
    401                     object.name.?,
    402                     sect.flags(),
    403                     sect.segname(),
    404                     sect.sectname(),
    405                 });
    406                 continue;
    407             };
    408             const target_seg = &self.load_commands.items[match.seg].Segment;
    409             const target_sect = &target_seg.sections.items[match.sect];
    410             target_sect.@"align" = math.max(target_sect.@"align", sect.inner.@"align");
    411 
    412             try self.mapAndUpdateSections(object, @intCast(u16, sect_id), match.seg, match.sect);
    413         }
    414     }
    415 
    416     // Ensure we have __DATA,__common section if we have tentative definitions.
    417     // Update size and alignment of __DATA,__common section.
    418     if (self.tentatives.values().len > 0) {
    419         const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
    420         const common_section_index = self.common_section_index orelse ind: {
    421             self.common_section_index = @intCast(u16, data_seg.sections.items.len);
    422             try data_seg.addSection(self.allocator, "__common", .{
    423                 .flags = macho.S_ZEROFILL,
    424             });
    425             break :ind self.common_section_index.?;
    426         };
    427         const common_sect = &data_seg.sections.items[common_section_index];
    428 
    429         var max_align: u16 = 0;
    430         var added_size: u64 = 0;
    431         for (self.tentatives.values()) |sym| {
    432             const tent = sym.cast(Symbol.Tentative) orelse unreachable;
    433             max_align = math.max(max_align, tent.alignment);
    434             added_size += tent.size;
    435         }
    436 
    437         common_sect.@"align" = math.max(common_sect.@"align", max_align);
    438 
    439         const alignment = try math.powi(u32, 2, common_sect.@"align");
    440         const offset = mem.alignForwardGeneric(u64, common_sect.size, alignment);
    441         const size = mem.alignForwardGeneric(u64, added_size, alignment);
    442 
    443         common_sect.size = offset + size;
    444         self.tentative_defs_offset = offset;
    445     }
    446 
    447     tlv_align: {
    448         const has_tlv =
    449             self.tlv_section_index != null or
    450             self.tlv_data_section_index != null or
    451             self.tlv_bss_section_index != null;
    452 
    453         if (!has_tlv) break :tlv_align;
    454 
    455         const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
    456 
    457         if (self.tlv_section_index) |index| {
    458             const sect = &seg.sections.items[index];
    459             sect.@"align" = 3; // __thread_vars is always 8byte aligned
    460         }
    461 
    462         // Apparently __tlv_data and __tlv_bss need to have matching alignment, so fix it up.
    463         // <rdar://problem/24221680> All __thread_data and __thread_bss sections must have same alignment
    464         // https://github.com/apple-opensource/ld64/blob/e28c028b20af187a16a7161d89e91868a450cadc/src/ld/ld.cpp#L1172
    465         const data_align: u32 = data: {
    466             if (self.tlv_data_section_index) |index| {
    467                 const sect = &seg.sections.items[index];
    468                 break :data sect.@"align";
    469             }
    470             break :tlv_align;
    471         };
    472         const bss_align: u32 = bss: {
    473             if (self.tlv_bss_section_index) |index| {
    474                 const sect = &seg.sections.items[index];
    475                 break :bss sect.@"align";
    476             }
    477             break :tlv_align;
    478         };
    479         const max_align = math.max(data_align, bss_align);
    480 
    481         if (self.tlv_data_section_index) |index| {
    482             const sect = &seg.sections.items[index];
    483             sect.@"align" = max_align;
    484         }
    485         if (self.tlv_bss_section_index) |index| {
    486             const sect = &seg.sections.items[index];
    487             sect.@"align" = max_align;
    488         }
    489     }
    490 }
    491 
    492 const MatchingSection = struct {
    493     seg: u16,
    494     sect: u16,
    495 };
    496 
    497 fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection {
    498     const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
    499     const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
    500     const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
    501     const segname = sect.segname();
    502     const sectname = sect.sectname();
    503 
    504     const res: ?MatchingSection = blk: {
    505         switch (sect.sectionType()) {
    506             macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {
    507                 if (self.text_const_section_index == null) {
    508                     self.text_const_section_index = @intCast(u16, text_seg.sections.items.len);
    509                     try text_seg.addSection(self.allocator, "__const", .{});
    510                 }
    511 
    512                 break :blk .{
    513                     .seg = self.text_segment_cmd_index.?,
    514                     .sect = self.text_const_section_index.?,
    515                 };
    516             },
    517             macho.S_CSTRING_LITERALS => {
    518                 if (mem.eql(u8, sectname, "__objc_methname")) {
    519                     // TODO it seems the common values within the sections in objects are deduplicated/merged
    520                     // on merging the sections' contents.
    521                     if (self.objc_methname_section_index == null) {
    522                         self.objc_methname_section_index = @intCast(u16, text_seg.sections.items.len);
    523                         try text_seg.addSection(self.allocator, "__objc_methname", .{
    524                             .flags = macho.S_CSTRING_LITERALS,
    525                         });
    526                     }
    527 
    528                     break :blk .{
    529                         .seg = self.text_segment_cmd_index.?,
    530                         .sect = self.objc_methname_section_index.?,
    531                     };
    532                 } else if (mem.eql(u8, sectname, "__objc_methtype")) {
    533                     if (self.objc_methtype_section_index == null) {
    534                         self.objc_methtype_section_index = @intCast(u16, text_seg.sections.items.len);
    535                         try text_seg.addSection(self.allocator, "__objc_methtype", .{
    536                             .flags = macho.S_CSTRING_LITERALS,
    537                         });
    538                     }
    539 
    540                     break :blk .{
    541                         .seg = self.text_segment_cmd_index.?,
    542                         .sect = self.objc_methtype_section_index.?,
    543                     };
    544                 } else if (mem.eql(u8, sectname, "__objc_classname")) {
    545                     if (self.objc_classname_section_index == null) {
    546                         self.objc_classname_section_index = @intCast(u16, text_seg.sections.items.len);
    547                         try text_seg.addSection(self.allocator, "__objc_classname", .{});
    548                     }
    549 
    550                     break :blk .{
    551                         .seg = self.text_segment_cmd_index.?,
    552                         .sect = self.objc_classname_section_index.?,
    553                     };
    554                 }
    555 
    556                 if (self.cstring_section_index == null) {
    557                     self.cstring_section_index = @intCast(u16, text_seg.sections.items.len);
    558                     try text_seg.addSection(self.allocator, "__cstring", .{
    559                         .flags = macho.S_CSTRING_LITERALS,
    560                     });
    561                 }
    562 
    563                 break :blk .{
    564                     .seg = self.text_segment_cmd_index.?,
    565                     .sect = self.cstring_section_index.?,
    566                 };
    567             },
    568             macho.S_LITERAL_POINTERS => {
    569                 if (mem.eql(u8, segname, "__DATA") and mem.eql(u8, sectname, "__objc_selrefs")) {
    570                     if (self.objc_selrefs_section_index == null) {
    571                         self.objc_selrefs_section_index = @intCast(u16, data_seg.sections.items.len);
    572                         try data_seg.addSection(self.allocator, "__objc_selrefs", .{
    573                             .flags = macho.S_LITERAL_POINTERS,
    574                         });
    575                     }
    576 
    577                     break :blk .{
    578                         .seg = self.data_segment_cmd_index.?,
    579                         .sect = self.objc_selrefs_section_index.?,
    580                     };
    581                 }
    582 
    583                 // TODO investigate
    584                 break :blk null;
    585             },
    586             macho.S_MOD_INIT_FUNC_POINTERS => {
    587                 if (self.mod_init_func_section_index == null) {
    588                     self.mod_init_func_section_index = @intCast(u16, data_const_seg.sections.items.len);
    589                     try data_const_seg.addSection(self.allocator, "__mod_init_func", .{
    590                         .flags = macho.S_MOD_INIT_FUNC_POINTERS,
    591                     });
    592                 }
    593 
    594                 break :blk .{
    595                     .seg = self.data_const_segment_cmd_index.?,
    596                     .sect = self.mod_init_func_section_index.?,
    597                 };
    598             },
    599             macho.S_MOD_TERM_FUNC_POINTERS => {
    600                 if (self.mod_term_func_section_index == null) {
    601                     self.mod_term_func_section_index = @intCast(u16, data_const_seg.sections.items.len);
    602                     try data_const_seg.addSection(self.allocator, "__mod_term_func", .{
    603                         .flags = macho.S_MOD_TERM_FUNC_POINTERS,
    604                     });
    605                 }
    606 
    607                 break :blk .{
    608                     .seg = self.data_const_segment_cmd_index.?,
    609                     .sect = self.mod_term_func_section_index.?,
    610                 };
    611             },
    612             macho.S_ZEROFILL => {
    613                 if (mem.eql(u8, sectname, "__common")) {
    614                     if (self.common_section_index == null) {
    615                         self.common_section_index = @intCast(u16, data_seg.sections.items.len);
    616                         try data_seg.addSection(self.allocator, "__common", .{
    617                             .flags = macho.S_ZEROFILL,
    618                         });
    619                     }
    620 
    621                     break :blk .{
    622                         .seg = self.data_segment_cmd_index.?,
    623                         .sect = self.common_section_index.?,
    624                     };
    625                 } else {
    626                     if (self.bss_section_index == null) {
    627                         self.bss_section_index = @intCast(u16, data_seg.sections.items.len);
    628                         try data_seg.addSection(self.allocator, "__bss", .{
    629                             .flags = macho.S_ZEROFILL,
    630                         });
    631                     }
    632 
    633                     break :blk .{
    634                         .seg = self.data_segment_cmd_index.?,
    635                         .sect = self.bss_section_index.?,
    636                     };
    637                 }
    638             },
    639             macho.S_THREAD_LOCAL_VARIABLES => {
    640                 if (self.tlv_section_index == null) {
    641                     self.tlv_section_index = @intCast(u16, data_seg.sections.items.len);
    642                     try data_seg.addSection(self.allocator, "__thread_vars", .{
    643                         .flags = macho.S_THREAD_LOCAL_VARIABLES,
    644                     });
    645                 }
    646 
    647                 break :blk .{
    648                     .seg = self.data_segment_cmd_index.?,
    649                     .sect = self.tlv_section_index.?,
    650                 };
    651             },
    652             macho.S_THREAD_LOCAL_REGULAR => {
    653                 if (self.tlv_data_section_index == null) {
    654                     self.tlv_data_section_index = @intCast(u16, data_seg.sections.items.len);
    655                     try data_seg.addSection(self.allocator, "__thread_data", .{
    656                         .flags = macho.S_THREAD_LOCAL_REGULAR,
    657                     });
    658                 }
    659 
    660                 break :blk .{
    661                     .seg = self.data_segment_cmd_index.?,
    662                     .sect = self.tlv_data_section_index.?,
    663                 };
    664             },
    665             macho.S_THREAD_LOCAL_ZEROFILL => {
    666                 if (self.tlv_bss_section_index == null) {
    667                     self.tlv_bss_section_index = @intCast(u16, data_seg.sections.items.len);
    668                     try data_seg.addSection(self.allocator, "__thread_bss", .{
    669                         .flags = macho.S_THREAD_LOCAL_ZEROFILL,
    670                     });
    671                 }
    672 
    673                 break :blk .{
    674                     .seg = self.data_segment_cmd_index.?,
    675                     .sect = self.tlv_bss_section_index.?,
    676                 };
    677             },
    678             macho.S_COALESCED => {
    679                 if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) {
    680                     // TODO I believe __eh_frame is currently part of __unwind_info section
    681                     // in the latest ld64 output.
    682                     if (self.eh_frame_section_index == null) {
    683                         self.eh_frame_section_index = @intCast(u16, text_seg.sections.items.len);
    684                         try text_seg.addSection(self.allocator, "__eh_frame", .{});
    685                     }
    686 
    687                     break :blk .{
    688                         .seg = self.text_segment_cmd_index.?,
    689                         .sect = self.eh_frame_section_index.?,
    690                     };
    691                 }
    692 
    693                 // TODO audit this: is this the right mapping?
    694                 if (self.data_const_section_index == null) {
    695                     self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len);
    696                     try data_const_seg.addSection(self.allocator, "__const", .{});
    697                 }
    698 
    699                 break :blk .{
    700                     .seg = self.data_const_segment_cmd_index.?,
    701                     .sect = self.data_const_section_index.?,
    702                 };
    703             },
    704             macho.S_REGULAR => {
    705                 if (sect.isCode()) {
    706                     if (self.text_section_index == null) {
    707                         self.text_section_index = @intCast(u16, text_seg.sections.items.len);
    708                         try text_seg.addSection(self.allocator, "__text", .{
    709                             .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
    710                         });
    711                     }
    712 
    713                     break :blk .{
    714                         .seg = self.text_segment_cmd_index.?,
    715                         .sect = self.text_section_index.?,
    716                     };
    717                 }
    718                 if (sect.isDebug()) {
    719                     // TODO debug attributes
    720                     if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
    721                         log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{
    722                             sect.flags(), segname, sectname,
    723                         });
    724                     }
    725                     break :blk null;
    726                 }
    727 
    728                 if (mem.eql(u8, segname, "__TEXT")) {
    729                     if (mem.eql(u8, sectname, "__ustring")) {
    730                         if (self.ustring_section_index == null) {
    731                             self.ustring_section_index = @intCast(u16, text_seg.sections.items.len);
    732                             try text_seg.addSection(self.allocator, "__ustring", .{});
    733                         }
    734 
    735                         break :blk .{
    736                             .seg = self.text_segment_cmd_index.?,
    737                             .sect = self.ustring_section_index.?,
    738                         };
    739                     } else if (mem.eql(u8, sectname, "__gcc_except_tab")) {
    740                         if (self.gcc_except_tab_section_index == null) {
    741                             self.gcc_except_tab_section_index = @intCast(u16, text_seg.sections.items.len);
    742                             try text_seg.addSection(self.allocator, "__gcc_except_tab", .{});
    743                         }
    744 
    745                         break :blk .{
    746                             .seg = self.text_segment_cmd_index.?,
    747                             .sect = self.gcc_except_tab_section_index.?,
    748                         };
    749                     } else if (mem.eql(u8, sectname, "__objc_methlist")) {
    750                         if (self.objc_methlist_section_index == null) {
    751                             self.objc_methlist_section_index = @intCast(u16, text_seg.sections.items.len);
    752                             try text_seg.addSection(self.allocator, "__objc_methlist", .{});
    753                         }
    754 
    755                         break :blk .{
    756                             .seg = self.text_segment_cmd_index.?,
    757                             .sect = self.objc_methlist_section_index.?,
    758                         };
    759                     } else {
    760                         if (self.text_const_section_index == null) {
    761                             self.text_const_section_index = @intCast(u16, text_seg.sections.items.len);
    762                             try text_seg.addSection(self.allocator, "__const", .{});
    763                         }
    764 
    765                         break :blk .{
    766                             .seg = self.text_segment_cmd_index.?,
    767                             .sect = self.text_const_section_index.?,
    768                         };
    769                     }
    770                 }
    771 
    772                 if (mem.eql(u8, segname, "__DATA_CONST")) {
    773                     if (self.data_const_section_index == null) {
    774                         self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len);
    775                         try data_const_seg.addSection(self.allocator, "__const", .{});
    776                     }
    777 
    778                     break :blk .{
    779                         .seg = self.data_const_segment_cmd_index.?,
    780                         .sect = self.data_const_section_index.?,
    781                     };
    782                 }
    783 
    784                 if (mem.eql(u8, segname, "__DATA")) {
    785                     if (mem.eql(u8, sectname, "__const")) {
    786                         if (self.data_const_section_index == null) {
    787                             self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len);
    788                             try data_const_seg.addSection(self.allocator, "__const", .{});
    789                         }
    790 
    791                         break :blk .{
    792                             .seg = self.data_const_segment_cmd_index.?,
    793                             .sect = self.data_const_section_index.?,
    794                         };
    795                     } else if (mem.eql(u8, sectname, "__cfstring")) {
    796                         if (self.objc_cfstring_section_index == null) {
    797                             self.objc_cfstring_section_index = @intCast(u16, data_const_seg.sections.items.len);
    798                             try data_const_seg.addSection(self.allocator, "__cfstring", .{});
    799                         }
    800 
    801                         break :blk .{
    802                             .seg = self.data_const_segment_cmd_index.?,
    803                             .sect = self.objc_cfstring_section_index.?,
    804                         };
    805                     } else if (mem.eql(u8, sectname, "__objc_classlist")) {
    806                         if (self.objc_classlist_section_index == null) {
    807                             self.objc_classlist_section_index = @intCast(u16, data_const_seg.sections.items.len);
    808                             try data_const_seg.addSection(self.allocator, "__objc_classlist", .{});
    809                         }
    810 
    811                         break :blk .{
    812                             .seg = self.data_const_segment_cmd_index.?,
    813                             .sect = self.objc_classlist_section_index.?,
    814                         };
    815                     } else if (mem.eql(u8, sectname, "__objc_imageinfo")) {
    816                         if (self.objc_imageinfo_section_index == null) {
    817                             self.objc_imageinfo_section_index = @intCast(u16, data_const_seg.sections.items.len);
    818                             try data_const_seg.addSection(self.allocator, "__objc_imageinfo", .{});
    819                         }
    820 
    821                         break :blk .{
    822                             .seg = self.data_const_segment_cmd_index.?,
    823                             .sect = self.objc_imageinfo_section_index.?,
    824                         };
    825                     } else if (mem.eql(u8, sectname, "__objc_const")) {
    826                         if (self.objc_const_section_index == null) {
    827                             self.objc_const_section_index = @intCast(u16, data_seg.sections.items.len);
    828                             try data_seg.addSection(self.allocator, "__objc_const", .{});
    829                         }
    830 
    831                         break :blk .{
    832                             .seg = self.data_segment_cmd_index.?,
    833                             .sect = self.objc_const_section_index.?,
    834                         };
    835                     } else if (mem.eql(u8, sectname, "__objc_classrefs")) {
    836                         if (self.objc_classrefs_section_index == null) {
    837                             self.objc_classrefs_section_index = @intCast(u16, data_seg.sections.items.len);
    838                             try data_seg.addSection(self.allocator, "__objc_classrefs", .{});
    839                         }
    840 
    841                         break :blk .{
    842                             .seg = self.data_segment_cmd_index.?,
    843                             .sect = self.objc_classrefs_section_index.?,
    844                         };
    845                     } else if (mem.eql(u8, sectname, "__objc_data")) {
    846                         if (self.objc_data_section_index == null) {
    847                             self.objc_data_section_index = @intCast(u16, data_seg.sections.items.len);
    848                             try data_seg.addSection(self.allocator, "__objc_data", .{});
    849                         }
    850 
    851                         break :blk .{
    852                             .seg = self.data_segment_cmd_index.?,
    853                             .sect = self.objc_data_section_index.?,
    854                         };
    855                     } else {
    856                         if (self.data_section_index == null) {
    857                             self.data_section_index = @intCast(u16, data_seg.sections.items.len);
    858                             try data_seg.addSection(self.allocator, "__data", .{});
    859                         }
    860 
    861                         break :blk .{
    862                             .seg = self.data_segment_cmd_index.?,
    863                             .sect = self.data_section_index.?,
    864                         };
    865                     }
    866                 }
    867 
    868                 if (mem.eql(u8, "__LLVM", segname) and mem.eql(u8, "__asm", sectname)) {
    869                     log.debug("TODO LLVM asm section: type 0x{x}, name '{s},{s}'", .{
    870                         sect.flags(), segname, sectname,
    871                     });
    872                 }
    873 
    874                 break :blk null;
    875             },
    876             else => break :blk null,
    877         }
    878     };
    879 
    880     return res;
    881 }
    882 
    883 fn sortSections(self: *Zld) !void {
    884     var text_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator);
    885     defer text_index_mapping.deinit();
    886     var data_const_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator);
    887     defer data_const_index_mapping.deinit();
    888     var data_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator);
    889     defer data_index_mapping.deinit();
    890 
    891     {
    892         // __TEXT segment
    893         const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
    894         var sections = seg.sections.toOwnedSlice(self.allocator);
    895         defer self.allocator.free(sections);
    896         try seg.sections.ensureCapacity(self.allocator, sections.len);
    897 
    898         const indices = &[_]*?u16{
    899             &self.text_section_index,
    900             &self.stubs_section_index,
    901             &self.stub_helper_section_index,
    902             &self.gcc_except_tab_section_index,
    903             &self.cstring_section_index,
    904             &self.ustring_section_index,
    905             &self.text_const_section_index,
    906             &self.objc_methname_section_index,
    907             &self.objc_methtype_section_index,
    908             &self.objc_classname_section_index,
    909             &self.eh_frame_section_index,
    910         };
    911         for (indices) |maybe_index| {
    912             const new_index: u16 = if (maybe_index.*) |index| blk: {
    913                 const idx = @intCast(u16, seg.sections.items.len);
    914                 seg.sections.appendAssumeCapacity(sections[index]);
    915                 try text_index_mapping.putNoClobber(index, idx);
    916                 break :blk idx;
    917             } else continue;
    918             maybe_index.* = new_index;
    919         }
    920     }
    921 
    922     {
    923         // __DATA_CONST segment
    924         const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
    925         var sections = seg.sections.toOwnedSlice(self.allocator);
    926         defer self.allocator.free(sections);
    927         try seg.sections.ensureCapacity(self.allocator, sections.len);
    928 
    929         const indices = &[_]*?u16{
    930             &self.got_section_index,
    931             &self.mod_init_func_section_index,
    932             &self.mod_term_func_section_index,
    933             &self.data_const_section_index,
    934             &self.objc_cfstring_section_index,
    935             &self.objc_classlist_section_index,
    936             &self.objc_imageinfo_section_index,
    937         };
    938         for (indices) |maybe_index| {
    939             const new_index: u16 = if (maybe_index.*) |index| blk: {
    940                 const idx = @intCast(u16, seg.sections.items.len);
    941                 seg.sections.appendAssumeCapacity(sections[index]);
    942                 try data_const_index_mapping.putNoClobber(index, idx);
    943                 break :blk idx;
    944             } else continue;
    945             maybe_index.* = new_index;
    946         }
    947     }
    948 
    949     {
    950         // __DATA segment
    951         const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
    952         var sections = seg.sections.toOwnedSlice(self.allocator);
    953         defer self.allocator.free(sections);
    954         try seg.sections.ensureCapacity(self.allocator, sections.len);
    955 
    956         // __DATA segment
    957         const indices = &[_]*?u16{
    958             &self.la_symbol_ptr_section_index,
    959             &self.objc_const_section_index,
    960             &self.objc_selrefs_section_index,
    961             &self.objc_classrefs_section_index,
    962             &self.objc_data_section_index,
    963             &self.data_section_index,
    964             &self.tlv_section_index,
    965             &self.tlv_data_section_index,
    966             &self.tlv_bss_section_index,
    967             &self.bss_section_index,
    968             &self.common_section_index,
    969         };
    970         for (indices) |maybe_index| {
    971             const new_index: u16 = if (maybe_index.*) |index| blk: {
    972                 const idx = @intCast(u16, seg.sections.items.len);
    973                 seg.sections.appendAssumeCapacity(sections[index]);
    974                 try data_index_mapping.putNoClobber(index, idx);
    975                 break :blk idx;
    976             } else continue;
    977             maybe_index.* = new_index;
    978         }
    979     }
    980 
    981     for (self.objects.items) |object| {
    982         for (object.sections.items) |*sect| {
    983             const target_map = sect.target_map orelse continue;
    984 
    985             const new_index = blk: {
    986                 if (self.text_segment_cmd_index.? == target_map.segment_id) {
    987                     break :blk text_index_mapping.get(target_map.section_id) orelse unreachable;
    988                 } else if (self.data_const_segment_cmd_index.? == target_map.segment_id) {
    989                     break :blk data_const_index_mapping.get(target_map.section_id) orelse unreachable;
    990                 } else if (self.data_segment_cmd_index.? == target_map.segment_id) {
    991                     break :blk data_index_mapping.get(target_map.section_id) orelse unreachable;
    992                 } else unreachable;
    993             };
    994 
    995             log.debug("remapping in {s}: '{s},{s}': {} => {}", .{
    996                 object.name.?,
    997                 parseName(&sect.inner.segname),
    998                 parseName(&sect.inner.sectname),
    999                 target_map.section_id,
   1000                 new_index,
   1001             });
   1002 
   1003             sect.target_map = .{
   1004                 .segment_id = target_map.segment_id,
   1005                 .section_id = new_index,
   1006                 .offset = target_map.offset,
   1007             };
   1008         }
   1009     }
   1010 }
   1011 
   1012 fn allocateTextSegment(self: *Zld) !void {
   1013     const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
   1014     const nstubs = @intCast(u32, self.stubs.items.len);
   1015 
   1016     const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize;
   1017     seg.inner.fileoff = 0;
   1018     seg.inner.vmaddr = base_vmaddr;
   1019 
   1020     // Set stubs and stub_helper sizes
   1021     const stubs = &seg.sections.items[self.stubs_section_index.?];
   1022     const stub_helper = &seg.sections.items[self.stub_helper_section_index.?];
   1023     stubs.size += nstubs * stubs.reserved2;
   1024 
   1025     const stub_size: u4 = switch (self.arch.?) {
   1026         .x86_64 => 10,
   1027         .aarch64 => 3 * @sizeOf(u32),
   1028         else => unreachable,
   1029     };
   1030     stub_helper.size += nstubs * stub_size;
   1031 
   1032     var sizeofcmds: u64 = 0;
   1033     for (self.load_commands.items) |lc| {
   1034         sizeofcmds += lc.cmdsize();
   1035     }
   1036 
   1037     try self.allocateSegment(self.text_segment_cmd_index.?, @sizeOf(macho.mach_header_64) + sizeofcmds);
   1038 
   1039     // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
   1040     var min_alignment: u32 = 0;
   1041     for (seg.sections.items) |sect| {
   1042         const alignment = try math.powi(u32, 2, sect.@"align");
   1043         min_alignment = math.max(min_alignment, alignment);
   1044     }
   1045 
   1046     assert(min_alignment > 0);
   1047     const last_sect_idx = seg.sections.items.len - 1;
   1048     const last_sect = seg.sections.items[last_sect_idx];
   1049     const shift: u32 = blk: {
   1050         const diff = seg.inner.filesize - last_sect.offset - last_sect.size;
   1051         const factor = @divTrunc(diff, min_alignment);
   1052         break :blk @intCast(u32, factor * min_alignment);
   1053     };
   1054 
   1055     if (shift > 0) {
   1056         for (seg.sections.items) |*sect| {
   1057             sect.offset += shift;
   1058             sect.addr += shift;
   1059         }
   1060     }
   1061 }
   1062 
   1063 fn allocateDataConstSegment(self: *Zld) !void {
   1064     const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
   1065     const nentries = @intCast(u32, self.got_entries.items.len);
   1066 
   1067     const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
   1068     seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize;
   1069     seg.inner.vmaddr = text_seg.inner.vmaddr + text_seg.inner.vmsize;
   1070 
   1071     // Set got size
   1072     const got = &seg.sections.items[self.got_section_index.?];
   1073     got.size += nentries * @sizeOf(u64);
   1074 
   1075     try self.allocateSegment(self.data_const_segment_cmd_index.?, 0);
   1076 }
   1077 
   1078 fn allocateDataSegment(self: *Zld) !void {
   1079     const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
   1080     const nstubs = @intCast(u32, self.stubs.items.len);
   1081 
   1082     const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
   1083     seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize;
   1084     seg.inner.vmaddr = data_const_seg.inner.vmaddr + data_const_seg.inner.vmsize;
   1085 
   1086     // Set la_symbol_ptr and data size
   1087     const la_symbol_ptr = &seg.sections.items[self.la_symbol_ptr_section_index.?];
   1088     const data = &seg.sections.items[self.data_section_index.?];
   1089     la_symbol_ptr.size += nstubs * @sizeOf(u64);
   1090     data.size += @sizeOf(u64); // We need at least 8bytes for address of dyld_stub_binder
   1091 
   1092     try self.allocateSegment(self.data_segment_cmd_index.?, 0);
   1093 }
   1094 
   1095 fn allocateLinkeditSegment(self: *Zld) void {
   1096     const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
   1097     const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
   1098     seg.inner.fileoff = data_seg.inner.fileoff + data_seg.inner.filesize;
   1099     seg.inner.vmaddr = data_seg.inner.vmaddr + data_seg.inner.vmsize;
   1100 }
   1101 
   1102 fn allocateSegment(self: *Zld, index: u16, offset: u64) !void {
   1103     const seg = &self.load_commands.items[index].Segment;
   1104 
   1105     // Allocate the sections according to their alignment at the beginning of the segment.
   1106     var start: u64 = offset;
   1107     for (seg.sections.items) |*sect| {
   1108         const alignment = try math.powi(u32, 2, sect.@"align");
   1109         const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
   1110         const end_aligned = mem.alignForwardGeneric(u64, start_aligned + sect.size, alignment);
   1111         sect.offset = @intCast(u32, seg.inner.fileoff + start_aligned);
   1112         sect.addr = seg.inner.vmaddr + start_aligned;
   1113         start = end_aligned;
   1114     }
   1115 
   1116     const seg_size_aligned = mem.alignForwardGeneric(u64, start, self.page_size.?);
   1117     seg.inner.filesize = seg_size_aligned;
   1118     seg.inner.vmsize = seg_size_aligned;
   1119 }
   1120 
   1121 fn allocateSymbols(self: *Zld) !void {
   1122     for (self.objects.items) |object| {
   1123         for (object.symbols.items) |sym| {
   1124             const reg = sym.cast(Symbol.Regular) orelse continue;
   1125 
   1126             const source_sect = &object.sections.items[reg.section];
   1127             const target_map = source_sect.target_map orelse {
   1128                 log.debug("section '{s},{s}' not mapped for symbol '{s}'", .{
   1129                     parseName(&source_sect.inner.segname),
   1130                     parseName(&source_sect.inner.sectname),
   1131                     sym.name,
   1132                 });
   1133                 continue;
   1134             };
   1135 
   1136             const target_seg = self.load_commands.items[target_map.segment_id].Segment;
   1137             const target_sect = target_seg.sections.items[target_map.section_id];
   1138             const target_addr = target_sect.addr + target_map.offset;
   1139             const address = reg.address - source_sect.inner.addr + target_addr;
   1140 
   1141             log.debug("resolving symbol '{s}' at 0x{x}", .{ sym.name, address });
   1142 
   1143             // TODO there might be a more generic way of doing this.
   1144             var section: u8 = 0;
   1145             for (self.load_commands.items) |cmd, cmd_id| {
   1146                 if (cmd != .Segment) break;
   1147                 if (cmd_id == target_map.segment_id) {
   1148                     section += @intCast(u8, target_map.section_id) + 1;
   1149                     break;
   1150                 }
   1151                 section += @intCast(u8, cmd.Segment.sections.items.len);
   1152             }
   1153 
   1154             reg.address = address;
   1155             reg.section = section;
   1156         }
   1157     }
   1158 }
   1159 
   1160 fn allocateTentativeSymbols(self: *Zld) !void {
   1161     if (self.tentatives.values().len == 0) return;
   1162 
   1163     const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
   1164     const common_sect = &data_seg.sections.items[self.common_section_index.?];
   1165 
   1166     const alignment = try math.powi(u32, 2, common_sect.@"align");
   1167     var base_address: u64 = common_sect.addr + self.tentative_defs_offset;
   1168 
   1169     log.debug("base address for tentative definitions 0x{x}", .{base_address});
   1170 
   1171     // TODO there might be a more generic way of doing this.
   1172     var section: u8 = 0;
   1173     for (self.load_commands.items) |cmd, cmd_id| {
   1174         if (cmd != .Segment) break;
   1175         if (cmd_id == self.data_segment_cmd_index.?) {
   1176             section += @intCast(u8, self.common_section_index.?) + 1;
   1177             break;
   1178         }
   1179         section += @intCast(u8, cmd.Segment.sections.items.len);
   1180     }
   1181 
   1182     // Convert tentative definitions into regular symbols.
   1183     for (self.tentatives.values()) |sym| {
   1184         const tent = sym.cast(Symbol.Tentative) orelse unreachable;
   1185         const reg = try self.allocator.create(Symbol.Regular);
   1186         errdefer self.allocator.destroy(reg);
   1187 
   1188         reg.* = .{
   1189             .base = .{
   1190                 .@"type" = .regular,
   1191                 .name = try self.allocator.dupe(u8, tent.base.name),
   1192                 .got_index = tent.base.got_index,
   1193                 .stubs_index = tent.base.stubs_index,
   1194             },
   1195             .linkage = .global,
   1196             .address = base_address,
   1197             .section = section,
   1198             .weak_ref = false,
   1199             .file = tent.file,
   1200             .stab = .{
   1201                 .kind = .global,
   1202                 .size = 0,
   1203             },
   1204         };
   1205 
   1206         try self.globals.putNoClobber(self.allocator, reg.base.name, &reg.base);
   1207         tent.base.alias = &reg.base;
   1208 
   1209         if (tent.base.got_index) |idx| {
   1210             self.got_entries.items[idx] = &reg.base;
   1211         }
   1212         if (tent.base.stubs_index) |idx| {
   1213             self.stubs.items[idx] = &reg.base;
   1214         }
   1215 
   1216         const address = mem.alignForwardGeneric(u64, base_address + tent.size, alignment);
   1217 
   1218         log.debug("tentative definition '{s}' allocated from 0x{x} to 0x{x}", .{
   1219             tent.base.name,
   1220             base_address,
   1221             address,
   1222         });
   1223 
   1224         base_address = address;
   1225     }
   1226 }
   1227 
   1228 fn allocateProxyBindAddresses(self: *Zld) !void {
   1229     for (self.objects.items) |object| {
   1230         for (object.sections.items) |sect| {
   1231             const relocs = sect.relocs orelse continue;
   1232 
   1233             for (relocs) |rel| {
   1234                 if (rel.@"type" != .unsigned) continue; // GOT is currently special-cased
   1235                 if (rel.target != .symbol) continue;
   1236 
   1237                 const sym = rel.target.symbol.getTopmostAlias();
   1238                 if (sym.cast(Symbol.Proxy)) |proxy| {
   1239                     const target_map = sect.target_map orelse continue;
   1240                     const target_seg = self.load_commands.items[target_map.segment_id].Segment;
   1241                     const target_sect = target_seg.sections.items[target_map.section_id];
   1242 
   1243                     try proxy.bind_info.append(self.allocator, .{
   1244                         .segment_id = target_map.segment_id,
   1245                         .address = target_sect.addr + target_map.offset + rel.offset,
   1246                     });
   1247                 }
   1248             }
   1249         }
   1250     }
   1251 }
   1252 
   1253 fn writeStubHelperCommon(self: *Zld) !void {
   1254     const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
   1255     const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
   1256     const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
   1257     const got = &data_const_segment.sections.items[self.got_section_index.?];
   1258     const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
   1259     const data = &data_segment.sections.items[self.data_section_index.?];
   1260 
   1261     self.stub_helper_stubs_start_off = blk: {
   1262         switch (self.arch.?) {
   1263             .x86_64 => {
   1264                 const code_size = 15;
   1265                 var code: [code_size]u8 = undefined;
   1266                 // lea %r11, [rip + disp]
   1267                 code[0] = 0x4c;
   1268                 code[1] = 0x8d;
   1269                 code[2] = 0x1d;
   1270                 {
   1271                     const target_addr = data.addr + data.size - @sizeOf(u64);
   1272                     const displacement = try math.cast(u32, target_addr - stub_helper.addr - 7);
   1273                     mem.writeIntLittle(u32, code[3..7], displacement);
   1274                 }
   1275                 // push %r11
   1276                 code[7] = 0x41;
   1277                 code[8] = 0x53;
   1278                 // jmp [rip + disp]
   1279                 code[9] = 0xff;
   1280                 code[10] = 0x25;
   1281                 {
   1282                     const dyld_stub_binder = self.imports.get("dyld_stub_binder").?;
   1283                     const addr = (got.addr + dyld_stub_binder.got_index.? * @sizeOf(u64));
   1284                     const displacement = try math.cast(u32, addr - stub_helper.addr - code_size);
   1285                     mem.writeIntLittle(u32, code[11..], displacement);
   1286                 }
   1287                 try self.file.?.pwriteAll(&code, stub_helper.offset);
   1288                 break :blk stub_helper.offset + code_size;
   1289             },
   1290             .aarch64 => {
   1291                 var code: [6 * @sizeOf(u32)]u8 = undefined;
   1292                 data_blk_outer: {
   1293                     const this_addr = stub_helper.addr;
   1294                     const target_addr = data.addr + data.size - @sizeOf(u64);
   1295                     data_blk: {
   1296                         const displacement = math.cast(i21, target_addr - this_addr) catch break :data_blk;
   1297                         // adr x17, disp
   1298                         mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, displacement).toU32());
   1299                         // nop
   1300                         mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
   1301                         break :data_blk_outer;
   1302                     }
   1303                     data_blk: {
   1304                         const new_this_addr = this_addr + @sizeOf(u32);
   1305                         const displacement = math.cast(i21, target_addr - new_this_addr) catch break :data_blk;
   1306                         // nop
   1307                         mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
   1308                         // adr x17, disp
   1309                         mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.adr(.x17, displacement).toU32());
   1310                         break :data_blk_outer;
   1311                     }
   1312                     // Jump is too big, replace adr with adrp and add.
   1313                     const this_page = @intCast(i32, this_addr >> 12);
   1314                     const target_page = @intCast(i32, target_addr >> 12);
   1315                     const pages = @intCast(i21, target_page - this_page);
   1316                     mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x17, pages).toU32());
   1317                     const narrowed = @truncate(u12, target_addr);
   1318                     mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.add(.x17, .x17, narrowed, false).toU32());
   1319                 }
   1320                 // stp x16, x17, [sp, #-16]!
   1321                 code[8] = 0xf0;
   1322                 code[9] = 0x47;
   1323                 code[10] = 0xbf;
   1324                 code[11] = 0xa9;
   1325                 binder_blk_outer: {
   1326                     const dyld_stub_binder = self.imports.get("dyld_stub_binder").?;
   1327                     const this_addr = stub_helper.addr + 3 * @sizeOf(u32);
   1328                     const target_addr = (got.addr + dyld_stub_binder.got_index.? * @sizeOf(u64));
   1329                     binder_blk: {
   1330                         const displacement = math.divExact(u64, target_addr - this_addr, 4) catch break :binder_blk;
   1331                         const literal = math.cast(u18, displacement) catch break :binder_blk;
   1332                         // ldr x16, label
   1333                         mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.ldr(.x16, .{
   1334                             .literal = literal,
   1335                         }).toU32());
   1336                         // nop
   1337                         mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32());
   1338                         break :binder_blk_outer;
   1339                     }
   1340                     binder_blk: {
   1341                         const new_this_addr = this_addr + @sizeOf(u32);
   1342                         const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch break :binder_blk;
   1343                         const literal = math.cast(u18, displacement) catch break :binder_blk;
   1344                         // Pad with nop to please division.
   1345                         // nop
   1346                         mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32());
   1347                         // ldr x16, label
   1348                         mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{
   1349                             .literal = literal,
   1350                         }).toU32());
   1351                         break :binder_blk_outer;
   1352                     }
   1353                     // Use adrp followed by ldr(immediate).
   1354                     const this_page = @intCast(i32, this_addr >> 12);
   1355                     const target_page = @intCast(i32, target_addr >> 12);
   1356                     const pages = @intCast(i21, target_page - this_page);
   1357                     mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.adrp(.x16, pages).toU32());
   1358                     const narrowed = @truncate(u12, target_addr);
   1359                     const offset = try math.divExact(u12, narrowed, 8);
   1360                     mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.ldr(.x16, .{
   1361                         .register = .{
   1362                             .rn = .x16,
   1363                             .offset = aarch64.Instruction.LoadStoreOffset.imm(offset),
   1364                         },
   1365                     }).toU32());
   1366                 }
   1367                 // br x16
   1368                 code[20] = 0x00;
   1369                 code[21] = 0x02;
   1370                 code[22] = 0x1f;
   1371                 code[23] = 0xd6;
   1372                 try self.file.?.pwriteAll(&code, stub_helper.offset);
   1373                 break :blk stub_helper.offset + 6 * @sizeOf(u32);
   1374             },
   1375             else => unreachable,
   1376         }
   1377     };
   1378 
   1379     for (self.stubs.items) |sym| {
   1380         // TODO weak bound pointers
   1381         const index = sym.stubs_index orelse unreachable;
   1382         try self.writeLazySymbolPointer(index);
   1383         try self.writeStub(index);
   1384         try self.writeStubInStubHelper(index);
   1385     }
   1386 }
   1387 
   1388 fn writeLazySymbolPointer(self: *Zld, index: u32) !void {
   1389     const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
   1390     const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?];
   1391     const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
   1392     const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?];
   1393 
   1394     const stub_size: u4 = switch (self.arch.?) {
   1395         .x86_64 => 10,
   1396         .aarch64 => 3 * @sizeOf(u32),
   1397         else => unreachable,
   1398     };
   1399     const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
   1400     const end = stub_helper.addr + stub_off - stub_helper.offset;
   1401     var buf: [@sizeOf(u64)]u8 = undefined;
   1402     mem.writeIntLittle(u64, &buf, end);
   1403     const off = la_symbol_ptr.offset + index * @sizeOf(u64);
   1404     log.debug("writing lazy symbol pointer entry 0x{x} at 0x{x}", .{ end, off });
   1405     try self.file.?.pwriteAll(&buf, off);
   1406 }
   1407 
   1408 fn writeStub(self: *Zld, index: u32) !void {
   1409     const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
   1410     const stubs = text_segment.sections.items[self.stubs_section_index.?];
   1411     const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
   1412     const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?];
   1413 
   1414     const stub_off = stubs.offset + index * stubs.reserved2;
   1415     const stub_addr = stubs.addr + index * stubs.reserved2;
   1416     const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64);
   1417     log.debug("writing stub at 0x{x}", .{stub_off});
   1418     var code = try self.allocator.alloc(u8, stubs.reserved2);
   1419     defer self.allocator.free(code);
   1420     switch (self.arch.?) {
   1421         .x86_64 => {
   1422             assert(la_ptr_addr >= stub_addr + stubs.reserved2);
   1423             const displacement = try math.cast(u32, la_ptr_addr - stub_addr - stubs.reserved2);
   1424             // jmp
   1425             code[0] = 0xff;
   1426             code[1] = 0x25;
   1427             mem.writeIntLittle(u32, code[2..][0..4], displacement);
   1428         },
   1429         .aarch64 => {
   1430             assert(la_ptr_addr >= stub_addr);
   1431             outer: {
   1432                 const this_addr = stub_addr;
   1433                 const target_addr = la_ptr_addr;
   1434                 inner: {
   1435                     const displacement = math.divExact(u64, target_addr - this_addr, 4) catch break :inner;
   1436                     const literal = math.cast(u18, displacement) catch break :inner;
   1437                     // ldr x16, literal
   1438                     mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{
   1439                         .literal = literal,
   1440                     }).toU32());
   1441                     // nop
   1442                     mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
   1443                     break :outer;
   1444                 }
   1445                 inner: {
   1446                     const new_this_addr = this_addr + @sizeOf(u32);
   1447                     const displacement = math.divExact(u64, target_addr - new_this_addr, 4) catch break :inner;
   1448                     const literal = math.cast(u18, displacement) catch break :inner;
   1449                     // nop
   1450                     mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
   1451                     // ldr x16, literal
   1452                     mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{
   1453                         .literal = literal,
   1454                     }).toU32());
   1455                     break :outer;
   1456                 }
   1457                 // Use adrp followed by ldr(immediate).
   1458                 const this_page = @intCast(i32, this_addr >> 12);
   1459                 const target_page = @intCast(i32, target_addr >> 12);
   1460                 const pages = @intCast(i21, target_page - this_page);
   1461                 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x16, pages).toU32());
   1462                 const narrowed = @truncate(u12, target_addr);
   1463                 const offset = try math.divExact(u12, narrowed, 8);
   1464                 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(.x16, .{
   1465                     .register = .{
   1466                         .rn = .x16,
   1467                         .offset = aarch64.Instruction.LoadStoreOffset.imm(offset),
   1468                     },
   1469                 }).toU32());
   1470             }
   1471             // br x16
   1472             mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32());
   1473         },
   1474         else => unreachable,
   1475     }
   1476     try self.file.?.pwriteAll(code, stub_off);
   1477 }
   1478 
   1479 fn writeStubInStubHelper(self: *Zld, index: u32) !void {
   1480     const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
   1481     const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?];
   1482 
   1483     const stub_size: u4 = switch (self.arch.?) {
   1484         .x86_64 => 10,
   1485         .aarch64 => 3 * @sizeOf(u32),
   1486         else => unreachable,
   1487     };
   1488     const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
   1489     var code = try self.allocator.alloc(u8, stub_size);
   1490     defer self.allocator.free(code);
   1491     switch (self.arch.?) {
   1492         .x86_64 => {
   1493             const displacement = try math.cast(
   1494                 i32,
   1495                 @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - stub_size,
   1496             );
   1497             // pushq
   1498             code[0] = 0x68;
   1499             mem.writeIntLittle(u32, code[1..][0..4], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
   1500             // jmpq
   1501             code[5] = 0xe9;
   1502             mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement));
   1503         },
   1504         .aarch64 => {
   1505             const displacement = try math.cast(i28, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4);
   1506             const literal = @divExact(stub_size - @sizeOf(u32), 4);
   1507             // ldr w16, literal
   1508             mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{
   1509                 .literal = literal,
   1510             }).toU32());
   1511             // b disp
   1512             mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(displacement).toU32());
   1513             mem.writeIntLittle(u32, code[8..12], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
   1514         },
   1515         else => unreachable,
   1516     }
   1517     try self.file.?.pwriteAll(code, stub_off);
   1518 }
   1519 
   1520 fn resolveSymbolsInObject(self: *Zld, object: *Object) !void {
   1521     log.debug("resolving symbols in '{s}'", .{object.name});
   1522 
   1523     for (object.symbols.items) |sym| {
   1524         if (sym.cast(Symbol.Regular)) |reg| {
   1525             if (reg.linkage == .translation_unit) continue; // Symbol local to TU.
   1526 
   1527             if (self.tentatives.fetchSwapRemove(sym.name)) |kv| {
   1528                 // Create link to the global.
   1529                 kv.value.alias = sym;
   1530             }
   1531             if (self.unresolved.fetchSwapRemove(sym.name)) |kv| {
   1532                 // Create link to the global.
   1533                 kv.value.alias = sym;
   1534             }
   1535             const sym_ptr = self.globals.getPtr(sym.name) orelse {
   1536                 // Put new global symbol into the symbol table.
   1537                 try self.globals.putNoClobber(self.allocator, sym.name, sym);
   1538                 continue;
   1539             };
   1540             const g_sym = sym_ptr.*;
   1541             const g_reg = g_sym.cast(Symbol.Regular) orelse unreachable;
   1542 
   1543             switch (g_reg.linkage) {
   1544                 .translation_unit => unreachable,
   1545                 .linkage_unit => {
   1546                     if (reg.linkage == .linkage_unit) {
   1547                         // Create link to the first encountered linkage_unit symbol.
   1548                         sym.alias = g_sym;
   1549                         continue;
   1550                     }
   1551                 },
   1552                 .global => {
   1553                     if (reg.linkage == .global) {
   1554                         log.debug("symbol '{s}' defined multiple times", .{reg.base.name});
   1555                         return error.MultipleSymbolDefinitions;
   1556                     }
   1557                     sym.alias = g_sym;
   1558                     continue;
   1559                 },
   1560             }
   1561 
   1562             g_sym.alias = sym;
   1563             sym_ptr.* = sym;
   1564         } else if (sym.cast(Symbol.Tentative)) |tent| {
   1565             if (self.globals.get(sym.name)) |g_sym| {
   1566                 sym.alias = g_sym;
   1567                 continue;
   1568             }
   1569 
   1570             if (self.unresolved.fetchSwapRemove(sym.name)) |kv| {
   1571                 kv.value.alias = sym;
   1572             }
   1573 
   1574             const sym_ptr = self.tentatives.getPtr(sym.name) orelse {
   1575                 // Put new tentative definition symbol into symbol table.
   1576                 try self.tentatives.putNoClobber(self.allocator, sym.name, sym);
   1577                 continue;
   1578             };
   1579 
   1580             // Compare by size and pick the largest tentative definition.
   1581             // We model this like a heap where the tentative definition with the
   1582             // largest size always washes up on top.
   1583             const t_sym = sym_ptr.*;
   1584             const t_tent = t_sym.cast(Symbol.Tentative) orelse unreachable;
   1585 
   1586             if (tent.size < t_tent.size) {
   1587                 sym.alias = t_sym;
   1588                 continue;
   1589             }
   1590 
   1591             t_sym.alias = sym;
   1592             sym_ptr.* = sym;
   1593         } else if (sym.cast(Symbol.Unresolved)) |_| {
   1594             if (self.globals.get(sym.name)) |g_sym| {
   1595                 sym.alias = g_sym;
   1596                 continue;
   1597             }
   1598             if (self.tentatives.get(sym.name)) |t_sym| {
   1599                 sym.alias = t_sym;
   1600                 continue;
   1601             }
   1602             if (self.unresolved.get(sym.name)) |u_sym| {
   1603                 sym.alias = u_sym;
   1604                 continue;
   1605             }
   1606 
   1607             try self.unresolved.putNoClobber(self.allocator, sym.name, sym);
   1608         } else unreachable;
   1609     }
   1610 }
   1611 
   1612 fn resolveSymbols(self: *Zld) !void {
   1613     // First pass, resolve symbols in provided objects.
   1614     for (self.objects.items) |object| {
   1615         try self.resolveSymbolsInObject(object);
   1616     }
   1617 
   1618     // Second pass, resolve symbols in static libraries.
   1619     var next_sym: usize = 0;
   1620     while (true) {
   1621         if (next_sym == self.unresolved.count()) break;
   1622 
   1623         const sym = self.unresolved.values()[next_sym];
   1624 
   1625         var reset: bool = false;
   1626         for (self.archives.items) |archive| {
   1627             // Check if the entry exists in a static archive.
   1628             const offsets = archive.toc.get(sym.name) orelse {
   1629                 // No hit.
   1630                 continue;
   1631             };
   1632             assert(offsets.items.len > 0);
   1633 
   1634             const object = try archive.parseObject(offsets.items[0]);
   1635             try self.objects.append(self.allocator, object);
   1636             try self.resolveSymbolsInObject(object);
   1637 
   1638             reset = true;
   1639             break;
   1640         }
   1641 
   1642         if (reset) {
   1643             next_sym = 0;
   1644         } else {
   1645             next_sym += 1;
   1646         }
   1647     }
   1648 
   1649     // Third pass, resolve symbols in dynamic libraries.
   1650     var unresolved = std.ArrayList(*Symbol).init(self.allocator);
   1651     defer unresolved.deinit();
   1652 
   1653     try unresolved.ensureCapacity(self.unresolved.count());
   1654     for (self.unresolved.values()) |value| {
   1655         unresolved.appendAssumeCapacity(value);
   1656     }
   1657     self.unresolved.clearRetainingCapacity();
   1658 
   1659     var referenced = std.AutoHashMap(*Dylib, void).init(self.allocator);
   1660     defer referenced.deinit();
   1661 
   1662     loop: while (unresolved.popOrNull()) |undef| {
   1663         const proxy = self.imports.get(undef.name) orelse outer: {
   1664             const proxy = inner: {
   1665                 for (self.dylibs.items) |dylib, i| {
   1666                     const proxy = (try dylib.createProxy(undef.name)) orelse continue;
   1667                     if (self.libsystem_dylib_index.? != @intCast(u16, i)) { // LibSystem gets load command seperately.
   1668                         try referenced.put(dylib, {});
   1669                     }
   1670                     break :inner proxy;
   1671                 }
   1672                 if (mem.eql(u8, undef.name, "___dso_handle")) {
   1673                     // TODO this is just a temp patch until I work out what to actually
   1674                     // do with ___dso_handle and __mh_execute_header symbols which are
   1675                     // synthetically created by the linker on macOS.
   1676                     const name = try self.allocator.dupe(u8, undef.name);
   1677                     const proxy = try self.allocator.create(Symbol.Proxy);
   1678                     errdefer self.allocator.destroy(proxy);
   1679                     proxy.* = .{
   1680                         .base = .{
   1681                             .@"type" = .proxy,
   1682                             .name = name,
   1683                         },
   1684                         .file = null,
   1685                     };
   1686                     break :inner &proxy.base;
   1687                 }
   1688 
   1689                 self.unresolved.putAssumeCapacityNoClobber(undef.name, undef);
   1690                 continue :loop;
   1691             };
   1692 
   1693             try self.imports.putNoClobber(self.allocator, proxy.name, proxy);
   1694             break :outer proxy;
   1695         };
   1696         undef.alias = proxy;
   1697     }
   1698 
   1699     // Add LC_LOAD_DYLIB load command for each referenced dylib/stub.
   1700     var it = referenced.iterator();
   1701     while (it.next()) |entry| {
   1702         const dylib = entry.key_ptr.*;
   1703         dylib.ordinal = self.next_dylib_ordinal;
   1704         const dylib_id = dylib.id orelse unreachable;
   1705         var dylib_cmd = try createLoadDylibCommand(
   1706             self.allocator,
   1707             dylib_id.name,
   1708             dylib_id.timestamp,
   1709             dylib_id.current_version,
   1710             dylib_id.compatibility_version,
   1711         );
   1712         errdefer dylib_cmd.deinit(self.allocator);
   1713         try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
   1714         self.next_dylib_ordinal += 1;
   1715     }
   1716 
   1717     if (self.unresolved.count() > 0) {
   1718         for (self.unresolved.values()) |undef| {
   1719             log.err("undefined reference to symbol '{s}'", .{undef.name});
   1720             log.err("    | referenced in {s}", .{
   1721                 undef.cast(Symbol.Unresolved).?.file.name.?,
   1722             });
   1723         }
   1724 
   1725         return error.UndefinedSymbolReference;
   1726     }
   1727 
   1728     // Finally put dyld_stub_binder as an Import
   1729     const libsystem_dylib = self.dylibs.items[self.libsystem_dylib_index.?];
   1730     const proxy = (try libsystem_dylib.createProxy("dyld_stub_binder")) orelse {
   1731         log.err("undefined reference to symbol 'dyld_stub_binder'", .{});
   1732         return error.UndefinedSymbolReference;
   1733     };
   1734     try self.imports.putNoClobber(self.allocator, proxy.name, proxy);
   1735 }
   1736 
   1737 fn resolveStubsAndGotEntries(self: *Zld) !void {
   1738     for (self.objects.items) |object| {
   1739         log.debug("resolving stubs and got entries from {s}", .{object.name});
   1740 
   1741         for (object.sections.items) |sect| {
   1742             const relocs = sect.relocs orelse continue;
   1743             for (relocs) |rel| {
   1744                 switch (rel.@"type") {
   1745                     .unsigned => continue,
   1746                     .got_page, .got_page_off, .got_load, .got, .pointer_to_got => {
   1747                         const sym = rel.target.symbol.getTopmostAlias();
   1748                         if (sym.got_index != null) continue;
   1749 
   1750                         const index = @intCast(u32, self.got_entries.items.len);
   1751                         sym.got_index = index;
   1752                         try self.got_entries.append(self.allocator, sym);
   1753 
   1754                         log.debug("    | found GOT entry {s}: {*}", .{ sym.name, sym });
   1755                     },
   1756                     else => {
   1757                         if (rel.target != .symbol) continue;
   1758 
   1759                         const sym = rel.target.symbol.getTopmostAlias();
   1760                         assert(sym.@"type" != .unresolved);
   1761 
   1762                         if (sym.stubs_index != null) continue;
   1763                         if (sym.@"type" != .proxy) continue;
   1764 
   1765                         const index = @intCast(u32, self.stubs.items.len);
   1766                         sym.stubs_index = index;
   1767                         try self.stubs.append(self.allocator, sym);
   1768 
   1769                         log.debug("    | found stub {s}: {*}", .{ sym.name, sym });
   1770                     },
   1771                 }
   1772             }
   1773         }
   1774     }
   1775 
   1776     // Finally, put dyld_stub_binder as the final GOT entry
   1777     const sym = self.imports.get("dyld_stub_binder") orelse unreachable;
   1778     const index = @intCast(u32, self.got_entries.items.len);
   1779     sym.got_index = index;
   1780     try self.got_entries.append(self.allocator, sym);
   1781 
   1782     log.debug("    | found GOT entry {s}: {*}", .{ sym.name, sym });
   1783 }
   1784 
   1785 fn resolveRelocsAndWriteSections(self: *Zld) !void {
   1786     for (self.objects.items) |object| {
   1787         log.debug("relocating object {s}", .{object.name});
   1788 
   1789         for (object.sections.items) |sect| {
   1790             if (sect.inner.flags == macho.S_MOD_INIT_FUNC_POINTERS or
   1791                 sect.inner.flags == macho.S_MOD_TERM_FUNC_POINTERS) continue;
   1792 
   1793             const segname = parseName(&sect.inner.segname);
   1794             const sectname = parseName(&sect.inner.sectname);
   1795 
   1796             log.debug("relocating section '{s},{s}'", .{ segname, sectname });
   1797 
   1798             // Get target mapping
   1799             const target_map = sect.target_map orelse {
   1800                 log.debug("no mapping for '{s},{s}'; skipping", .{ segname, sectname });
   1801                 continue;
   1802             };
   1803             const target_seg = self.load_commands.items[target_map.segment_id].Segment;
   1804             const target_sect = target_seg.sections.items[target_map.section_id];
   1805             const target_sect_addr = target_sect.addr + target_map.offset;
   1806             const target_sect_off = target_sect.offset + target_map.offset;
   1807 
   1808             if (sect.relocs) |relocs| {
   1809                 for (relocs) |rel| {
   1810                     const source_addr = target_sect_addr + rel.offset;
   1811 
   1812                     var args: reloc.Relocation.ResolveArgs = .{
   1813                         .source_addr = source_addr,
   1814                         .target_addr = undefined,
   1815                     };
   1816 
   1817                     switch (rel.@"type") {
   1818                         .unsigned => {
   1819                             args.target_addr = try self.relocTargetAddr(object, rel.target);
   1820 
   1821                             const unsigned = rel.cast(reloc.Unsigned) orelse unreachable;
   1822                             if (unsigned.subtractor) |subtractor| {
   1823                                 args.subtractor = try self.relocTargetAddr(object, subtractor);
   1824                             }
   1825                             if (rel.target == .section) {
   1826                                 const source_sect = object.sections.items[rel.target.section];
   1827                                 args.source_source_sect_addr = sect.inner.addr;
   1828                                 args.source_target_sect_addr = source_sect.inner.addr;
   1829                             }
   1830 
   1831                             const flags = @truncate(u8, target_sect.flags & 0xff);
   1832                             const should_rebase = rebase: {
   1833                                 if (!unsigned.is_64bit) break :rebase false;
   1834 
   1835                                 // TODO actually, a check similar to what dyld is doing, that is, verifying
   1836                                 // that the segment is writable should be enough here.
   1837                                 const is_right_segment = blk: {
   1838                                     if (self.data_segment_cmd_index) |idx| {
   1839                                         if (target_map.segment_id == idx) {
   1840                                             break :blk true;
   1841                                         }
   1842                                     }
   1843                                     if (self.data_const_segment_cmd_index) |idx| {
   1844                                         if (target_map.segment_id == idx) {
   1845                                             break :blk true;
   1846                                         }
   1847                                     }
   1848                                     break :blk false;
   1849                                 };
   1850 
   1851                                 if (!is_right_segment) break :rebase false;
   1852                                 if (flags != macho.S_LITERAL_POINTERS and
   1853                                     flags != macho.S_REGULAR)
   1854                                 {
   1855                                     break :rebase false;
   1856                                 }
   1857                                 if (rel.target == .symbol) {
   1858                                     const final = rel.target.symbol.getTopmostAlias();
   1859                                     if (final.cast(Symbol.Proxy)) |_| {
   1860                                         break :rebase false;
   1861                                     }
   1862                                 }
   1863 
   1864                                 break :rebase true;
   1865                             };
   1866 
   1867                             if (should_rebase) {
   1868                                 try self.local_rebases.append(self.allocator, .{
   1869                                     .offset = source_addr - target_seg.inner.vmaddr,
   1870                                     .segment_id = target_map.segment_id,
   1871                                 });
   1872                             }
   1873 
   1874                             // TLV is handled via a separate offset mechanism.
   1875                             // Calculate the offset to the initializer.
   1876                             if (flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: {
   1877                                 // TODO we don't want to save offset to tlv_bootstrap
   1878                                 if (mem.eql(u8, rel.target.symbol.name, "__tlv_bootstrap")) break :tlv;
   1879 
   1880                                 const base_addr = blk: {
   1881                                     if (self.tlv_data_section_index) |index| {
   1882                                         const tlv_data = target_seg.sections.items[index];
   1883                                         break :blk tlv_data.addr;
   1884                                     } else {
   1885                                         const tlv_bss = target_seg.sections.items[self.tlv_bss_section_index.?];
   1886                                         break :blk tlv_bss.addr;
   1887                                     }
   1888                                 };
   1889                                 // Since we require TLV data to always preceed TLV bss section, we calculate
   1890                                 // offsets wrt to the former if it is defined; otherwise, wrt to the latter.
   1891                                 try self.threadlocal_offsets.append(self.allocator, .{
   1892                                     .source_addr = args.source_addr,
   1893                                     .offset = args.target_addr - base_addr,
   1894                                 });
   1895                             }
   1896                         },
   1897                         .got_page, .got_page_off, .got_load, .got, .pointer_to_got => {
   1898                             const dc_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
   1899                             const got = dc_seg.sections.items[self.got_section_index.?];
   1900                             const final = rel.target.symbol.getTopmostAlias();
   1901                             const got_index = final.got_index orelse {
   1902                                 log.err("expected GOT index relocating symbol '{s}'", .{final.name});
   1903                                 log.err("this is an internal linker error", .{});
   1904                                 return error.FailedToResolveRelocationTarget;
   1905                             };
   1906                             args.target_addr = got.addr + got_index * @sizeOf(u64);
   1907                         },
   1908                         else => |tt| {
   1909                             if (tt == .signed and rel.target == .section) {
   1910                                 const source_sect = object.sections.items[rel.target.section];
   1911                                 args.source_source_sect_addr = sect.inner.addr;
   1912                                 args.source_target_sect_addr = source_sect.inner.addr;
   1913                             }
   1914                             args.target_addr = try self.relocTargetAddr(object, rel.target);
   1915                         },
   1916                     }
   1917 
   1918                     try rel.resolve(args);
   1919                 }
   1920             }
   1921 
   1922             log.debug("writing contents of '{s},{s}' section from '{s}' from 0x{x} to 0x{x}", .{
   1923                 segname,
   1924                 sectname,
   1925                 object.name,
   1926                 target_sect_off,
   1927                 target_sect_off + sect.code.len,
   1928             });
   1929 
   1930             if (target_sect.flags == macho.S_ZEROFILL or
   1931                 target_sect.flags == macho.S_THREAD_LOCAL_ZEROFILL or
   1932                 target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES)
   1933             {
   1934                 log.debug("zeroing out '{s},{s}' from 0x{x} to 0x{x}", .{
   1935                     parseName(&target_sect.segname),
   1936                     parseName(&target_sect.sectname),
   1937                     target_sect_off,
   1938                     target_sect_off + sect.code.len,
   1939                 });
   1940 
   1941                 // Zero-out the space
   1942                 var zeroes = try self.allocator.alloc(u8, sect.code.len);
   1943                 defer self.allocator.free(zeroes);
   1944                 mem.set(u8, zeroes, 0);
   1945                 try self.file.?.pwriteAll(zeroes, target_sect_off);
   1946             } else {
   1947                 try self.file.?.pwriteAll(sect.code, target_sect_off);
   1948             }
   1949         }
   1950     }
   1951 }
   1952 
   1953 fn relocTargetAddr(self: *Zld, object: *const Object, target: reloc.Relocation.Target) !u64 {
   1954     const target_addr = blk: {
   1955         switch (target) {
   1956             .symbol => |sym| {
   1957                 const final = sym.getTopmostAlias();
   1958                 if (final.cast(Symbol.Regular)) |reg| {
   1959                     log.debug("    | regular '{s}'", .{sym.name});
   1960                     break :blk reg.address;
   1961                 } else if (final.cast(Symbol.Proxy)) |proxy| {
   1962                     if (mem.eql(u8, sym.name, "__tlv_bootstrap")) {
   1963                         log.debug("    | symbol '__tlv_bootstrap'", .{});
   1964                         const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
   1965                         const tlv = segment.sections.items[self.tlv_section_index.?];
   1966                         break :blk tlv.addr;
   1967                     }
   1968 
   1969                     log.debug("    | symbol stub '{s}'", .{sym.name});
   1970                     const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
   1971                     const stubs = segment.sections.items[self.stubs_section_index.?];
   1972                     const stubs_index = proxy.base.stubs_index orelse {
   1973                         if (proxy.bind_info.items.len > 0) {
   1974                             break :blk 0; // Dynamically bound by dyld.
   1975                         }
   1976                         log.err(
   1977                             "expected stubs index or dynamic bind address when relocating symbol '{s}'",
   1978                             .{final.name},
   1979                         );
   1980                         log.err("this is an internal linker error", .{});
   1981                         return error.FailedToResolveRelocationTarget;
   1982                     };
   1983                     break :blk stubs.addr + stubs_index * stubs.reserved2;
   1984                 } else {
   1985                     log.err("failed to resolve symbol '{s}' as a relocation target", .{sym.name});
   1986                     log.err("this is an internal linker error", .{});
   1987                     return error.FailedToResolveRelocationTarget;
   1988                 }
   1989             },
   1990             .section => |sect_id| {
   1991                 log.debug("    | section offset", .{});
   1992                 const source_sect = object.sections.items[sect_id];
   1993                 log.debug("    | section '{s},{s}'", .{
   1994                     parseName(&source_sect.inner.segname),
   1995                     parseName(&source_sect.inner.sectname),
   1996                 });
   1997                 const target_map = source_sect.target_map orelse unreachable;
   1998                 const target_seg = self.load_commands.items[target_map.segment_id].Segment;
   1999                 const target_sect = target_seg.sections.items[target_map.section_id];
   2000                 break :blk target_sect.addr + target_map.offset;
   2001             },
   2002         }
   2003     };
   2004     return target_addr;
   2005 }
   2006 
   2007 fn populateMetadata(self: *Zld) !void {
   2008     if (self.pagezero_segment_cmd_index == null) {
   2009         self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
   2010         try self.load_commands.append(self.allocator, .{
   2011             .Segment = SegmentCommand.empty("__PAGEZERO", .{
   2012                 .vmsize = 0x100000000, // size always set to 4GB
   2013             }),
   2014         });
   2015     }
   2016 
   2017     if (self.text_segment_cmd_index == null) {
   2018         self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
   2019         try self.load_commands.append(self.allocator, .{
   2020             .Segment = SegmentCommand.empty("__TEXT", .{
   2021                 .vmaddr = 0x100000000, // always starts at 4GB
   2022                 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE,
   2023                 .initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE,
   2024             }),
   2025         });
   2026     }
   2027 
   2028     if (self.text_section_index == null) {
   2029         const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
   2030         self.text_section_index = @intCast(u16, text_seg.sections.items.len);
   2031         const alignment: u2 = switch (self.arch.?) {
   2032             .x86_64 => 0,
   2033             .aarch64 => 2,
   2034             else => unreachable, // unhandled architecture type
   2035         };
   2036         try text_seg.addSection(self.allocator, "__text", .{
   2037             .@"align" = alignment,
   2038             .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
   2039         });
   2040     }
   2041 
   2042     if (self.stubs_section_index == null) {
   2043         const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
   2044         self.stubs_section_index = @intCast(u16, text_seg.sections.items.len);
   2045         const alignment: u2 = switch (self.arch.?) {
   2046             .x86_64 => 0,
   2047             .aarch64 => 2,
   2048             else => unreachable, // unhandled architecture type
   2049         };
   2050         const stub_size: u4 = switch (self.arch.?) {
   2051             .x86_64 => 6,
   2052             .aarch64 => 3 * @sizeOf(u32),
   2053             else => unreachable, // unhandled architecture type
   2054         };
   2055         try text_seg.addSection(self.allocator, "__stubs", .{
   2056             .@"align" = alignment,
   2057             .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
   2058             .reserved2 = stub_size,
   2059         });
   2060     }
   2061 
   2062     if (self.stub_helper_section_index == null) {
   2063         const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
   2064         self.stub_helper_section_index = @intCast(u16, text_seg.sections.items.len);
   2065         const alignment: u2 = switch (self.arch.?) {
   2066             .x86_64 => 0,
   2067             .aarch64 => 2,
   2068             else => unreachable, // unhandled architecture type
   2069         };
   2070         const stub_helper_size: u6 = switch (self.arch.?) {
   2071             .x86_64 => 15,
   2072             .aarch64 => 6 * @sizeOf(u32),
   2073             else => unreachable,
   2074         };
   2075         try text_seg.addSection(self.allocator, "__stub_helper", .{
   2076             .size = stub_helper_size,
   2077             .@"align" = alignment,
   2078             .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
   2079         });
   2080     }
   2081 
   2082     if (self.data_const_segment_cmd_index == null) {
   2083         self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
   2084         try self.load_commands.append(self.allocator, .{
   2085             .Segment = SegmentCommand.empty("__DATA_CONST", .{
   2086                 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
   2087                 .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
   2088             }),
   2089         });
   2090     }
   2091 
   2092     if (self.got_section_index == null) {
   2093         const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
   2094         self.got_section_index = @intCast(u16, data_const_seg.sections.items.len);
   2095         try data_const_seg.addSection(self.allocator, "__got", .{
   2096             .@"align" = 3, // 2^3 = @sizeOf(u64)
   2097             .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
   2098         });
   2099     }
   2100 
   2101     if (self.data_segment_cmd_index == null) {
   2102         self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
   2103         try self.load_commands.append(self.allocator, .{
   2104             .Segment = SegmentCommand.empty("__DATA", .{
   2105                 .maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
   2106                 .initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
   2107             }),
   2108         });
   2109     }
   2110 
   2111     if (self.la_symbol_ptr_section_index == null) {
   2112         const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
   2113         self.la_symbol_ptr_section_index = @intCast(u16, data_seg.sections.items.len);
   2114         try data_seg.addSection(self.allocator, "__la_symbol_ptr", .{
   2115             .@"align" = 3, // 2^3 = @sizeOf(u64)
   2116             .flags = macho.S_LAZY_SYMBOL_POINTERS,
   2117         });
   2118     }
   2119 
   2120     if (self.data_section_index == null) {
   2121         const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
   2122         self.data_section_index = @intCast(u16, data_seg.sections.items.len);
   2123         try data_seg.addSection(self.allocator, "__data", .{
   2124             .@"align" = 3, // 2^3 = @sizeOf(u64)
   2125         });
   2126     }
   2127 
   2128     if (self.linkedit_segment_cmd_index == null) {
   2129         self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
   2130         try self.load_commands.append(self.allocator, .{
   2131             .Segment = SegmentCommand.empty("__LINKEDIT", .{
   2132                 .maxprot = macho.VM_PROT_READ,
   2133                 .initprot = macho.VM_PROT_READ,
   2134             }),
   2135         });
   2136     }
   2137 
   2138     if (self.dyld_info_cmd_index == null) {
   2139         self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);
   2140         try self.load_commands.append(self.allocator, .{
   2141             .DyldInfoOnly = .{
   2142                 .cmd = macho.LC_DYLD_INFO_ONLY,
   2143                 .cmdsize = @sizeOf(macho.dyld_info_command),
   2144                 .rebase_off = 0,
   2145                 .rebase_size = 0,
   2146                 .bind_off = 0,
   2147                 .bind_size = 0,
   2148                 .weak_bind_off = 0,
   2149                 .weak_bind_size = 0,
   2150                 .lazy_bind_off = 0,
   2151                 .lazy_bind_size = 0,
   2152                 .export_off = 0,
   2153                 .export_size = 0,
   2154             },
   2155         });
   2156     }
   2157 
   2158     if (self.symtab_cmd_index == null) {
   2159         self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
   2160         try self.load_commands.append(self.allocator, .{
   2161             .Symtab = .{
   2162                 .cmd = macho.LC_SYMTAB,
   2163                 .cmdsize = @sizeOf(macho.symtab_command),
   2164                 .symoff = 0,
   2165                 .nsyms = 0,
   2166                 .stroff = 0,
   2167                 .strsize = 0,
   2168             },
   2169         });
   2170         try self.strtab.append(self.allocator, 0);
   2171     }
   2172 
   2173     if (self.dysymtab_cmd_index == null) {
   2174         self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
   2175         try self.load_commands.append(self.allocator, .{
   2176             .Dysymtab = .{
   2177                 .cmd = macho.LC_DYSYMTAB,
   2178                 .cmdsize = @sizeOf(macho.dysymtab_command),
   2179                 .ilocalsym = 0,
   2180                 .nlocalsym = 0,
   2181                 .iextdefsym = 0,
   2182                 .nextdefsym = 0,
   2183                 .iundefsym = 0,
   2184                 .nundefsym = 0,
   2185                 .tocoff = 0,
   2186                 .ntoc = 0,
   2187                 .modtaboff = 0,
   2188                 .nmodtab = 0,
   2189                 .extrefsymoff = 0,
   2190                 .nextrefsyms = 0,
   2191                 .indirectsymoff = 0,
   2192                 .nindirectsyms = 0,
   2193                 .extreloff = 0,
   2194                 .nextrel = 0,
   2195                 .locreloff = 0,
   2196                 .nlocrel = 0,
   2197             },
   2198         });
   2199     }
   2200 
   2201     if (self.dylinker_cmd_index == null) {
   2202         self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len);
   2203         const cmdsize = @intCast(u32, mem.alignForwardGeneric(
   2204             u64,
   2205             @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH),
   2206             @sizeOf(u64),
   2207         ));
   2208         var dylinker_cmd = emptyGenericCommandWithData(macho.dylinker_command{
   2209             .cmd = macho.LC_LOAD_DYLINKER,
   2210             .cmdsize = cmdsize,
   2211             .name = @sizeOf(macho.dylinker_command),
   2212         });
   2213         dylinker_cmd.data = try self.allocator.alloc(u8, cmdsize - dylinker_cmd.inner.name);
   2214         mem.set(u8, dylinker_cmd.data, 0);
   2215         mem.copy(u8, dylinker_cmd.data, mem.spanZ(DEFAULT_DYLD_PATH));
   2216         try self.load_commands.append(self.allocator, .{ .Dylinker = dylinker_cmd });
   2217     }
   2218 
   2219     if (self.main_cmd_index == null) {
   2220         self.main_cmd_index = @intCast(u16, self.load_commands.items.len);
   2221         try self.load_commands.append(self.allocator, .{
   2222             .Main = .{
   2223                 .cmd = macho.LC_MAIN,
   2224                 .cmdsize = @sizeOf(macho.entry_point_command),
   2225                 .entryoff = 0x0,
   2226                 .stacksize = 0,
   2227             },
   2228         });
   2229     }
   2230 
   2231     if (self.source_version_cmd_index == null) {
   2232         self.source_version_cmd_index = @intCast(u16, self.load_commands.items.len);
   2233         try self.load_commands.append(self.allocator, .{
   2234             .SourceVersion = .{
   2235                 .cmd = macho.LC_SOURCE_VERSION,
   2236                 .cmdsize = @sizeOf(macho.source_version_command),
   2237                 .version = 0x0,
   2238             },
   2239         });
   2240     }
   2241 
   2242     if (self.uuid_cmd_index == null) {
   2243         self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
   2244         var uuid_cmd: macho.uuid_command = .{
   2245             .cmd = macho.LC_UUID,
   2246             .cmdsize = @sizeOf(macho.uuid_command),
   2247             .uuid = undefined,
   2248         };
   2249         std.crypto.random.bytes(&uuid_cmd.uuid);
   2250         try self.load_commands.append(self.allocator, .{ .Uuid = uuid_cmd });
   2251     }
   2252 
   2253     if (self.code_signature_cmd_index == null and self.arch.? == .aarch64) {
   2254         self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
   2255         try self.load_commands.append(self.allocator, .{
   2256             .LinkeditData = .{
   2257                 .cmd = macho.LC_CODE_SIGNATURE,
   2258                 .cmdsize = @sizeOf(macho.linkedit_data_command),
   2259                 .dataoff = 0,
   2260                 .datasize = 0,
   2261             },
   2262         });
   2263     }
   2264 
   2265     if (self.data_in_code_cmd_index == null and self.arch.? == .x86_64) {
   2266         self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);
   2267         try self.load_commands.append(self.allocator, .{
   2268             .LinkeditData = .{
   2269                 .cmd = macho.LC_DATA_IN_CODE,
   2270                 .cmdsize = @sizeOf(macho.linkedit_data_command),
   2271                 .dataoff = 0,
   2272                 .datasize = 0,
   2273             },
   2274         });
   2275     }
   2276 }
   2277 
   2278 fn addRpaths(self: *Zld, rpaths: []const []const u8) !void {
   2279     for (rpaths) |rpath| {
   2280         const cmdsize = @intCast(u32, mem.alignForwardGeneric(
   2281             u64,
   2282             @sizeOf(macho.rpath_command) + rpath.len + 1,
   2283             @sizeOf(u64),
   2284         ));
   2285         var rpath_cmd = emptyGenericCommandWithData(macho.rpath_command{
   2286             .cmd = macho.LC_RPATH,
   2287             .cmdsize = cmdsize,
   2288             .path = @sizeOf(macho.rpath_command),
   2289         });
   2290         rpath_cmd.data = try self.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path);
   2291         mem.set(u8, rpath_cmd.data, 0);
   2292         mem.copy(u8, rpath_cmd.data, rpath);
   2293         try self.load_commands.append(self.allocator, .{ .Rpath = rpath_cmd });
   2294     }
   2295 }
   2296 
   2297 fn flush(self: *Zld) !void {
   2298     try self.writeStubHelperCommon();
   2299     try self.resolveRelocsAndWriteSections();
   2300 
   2301     if (self.common_section_index) |index| {
   2302         const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
   2303         const sect = &seg.sections.items[index];
   2304         sect.offset = 0;
   2305     }
   2306 
   2307     if (self.bss_section_index) |index| {
   2308         const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
   2309         const sect = &seg.sections.items[index];
   2310         sect.offset = 0;
   2311     }
   2312 
   2313     if (self.tlv_bss_section_index) |index| {
   2314         const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
   2315         const sect = &seg.sections.items[index];
   2316         sect.offset = 0;
   2317     }
   2318 
   2319     if (self.tlv_section_index) |index| {
   2320         const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
   2321         const sect = &seg.sections.items[index];
   2322 
   2323         var buffer = try self.allocator.alloc(u8, @intCast(usize, sect.size));
   2324         defer self.allocator.free(buffer);
   2325         _ = try self.file.?.preadAll(buffer, sect.offset);
   2326 
   2327         var stream = std.io.fixedBufferStream(buffer);
   2328         var writer = stream.writer();
   2329 
   2330         std.sort.sort(TlvOffset, self.threadlocal_offsets.items, {}, TlvOffset.cmp);
   2331 
   2332         const seek_amt = 2 * @sizeOf(u64);
   2333         for (self.threadlocal_offsets.items) |tlv| {
   2334             try writer.context.seekBy(seek_amt);
   2335             try writer.writeIntLittle(u64, tlv.offset);
   2336         }
   2337 
   2338         try self.file.?.pwriteAll(buffer, sect.offset);
   2339     }
   2340 
   2341     if (self.mod_init_func_section_index) |index| {
   2342         const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
   2343         const sect = &seg.sections.items[index];
   2344 
   2345         var initializers = std.ArrayList(u64).init(self.allocator);
   2346         defer initializers.deinit();
   2347 
   2348         for (self.objects.items) |object| {
   2349             for (object.initializers.items) |initializer| {
   2350                 const address = initializer.cast(Symbol.Regular).?.address;
   2351                 try initializers.append(address);
   2352             }
   2353         }
   2354 
   2355         _ = try self.file.?.pwriteAll(mem.sliceAsBytes(initializers.items), sect.offset);
   2356         sect.size = @intCast(u32, initializers.items.len * @sizeOf(u64));
   2357     }
   2358 
   2359     try self.writeGotEntries();
   2360     try self.setEntryPoint();
   2361     try self.writeRebaseInfoTable();
   2362     try self.writeBindInfoTable();
   2363     try self.writeLazyBindInfoTable();
   2364     try self.writeExportInfo();
   2365     if (self.arch.? == .x86_64) {
   2366         try self.writeDataInCode();
   2367     }
   2368 
   2369     {
   2370         const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
   2371         const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
   2372         symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
   2373     }
   2374 
   2375     try self.writeDebugInfo();
   2376     try self.writeSymbolTable();
   2377     try self.writeStringTable();
   2378 
   2379     {
   2380         // Seal __LINKEDIT size
   2381         const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
   2382         seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size.?);
   2383     }
   2384 
   2385     if (self.arch.? == .aarch64) {
   2386         try self.writeCodeSignaturePadding();
   2387     }
   2388 
   2389     try self.writeLoadCommands();
   2390     try self.writeHeader();
   2391 
   2392     if (self.arch.? == .aarch64) {
   2393         try self.writeCodeSignature();
   2394     }
   2395 
   2396     if (comptime std.Target.current.isDarwin() and std.Target.current.cpu.arch == .aarch64) {
   2397         try fs.cwd().copyFile(self.out_path.?, fs.cwd(), self.out_path.?, .{});
   2398     }
   2399 }
   2400 
   2401 fn writeGotEntries(self: *Zld) !void {
   2402     const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
   2403     const sect = seg.sections.items[self.got_section_index.?];
   2404 
   2405     var buffer = try self.allocator.alloc(u8, self.got_entries.items.len * @sizeOf(u64));
   2406     defer self.allocator.free(buffer);
   2407 
   2408     var stream = std.io.fixedBufferStream(buffer);
   2409     var writer = stream.writer();
   2410 
   2411     for (self.got_entries.items) |sym| {
   2412         const address: u64 = if (sym.cast(Symbol.Regular)) |reg| reg.address else 0;
   2413         try writer.writeIntLittle(u64, address);
   2414     }
   2415 
   2416     log.debug("writing GOT pointers at 0x{x} to 0x{x}", .{ sect.offset, sect.offset + buffer.len });
   2417 
   2418     try self.file.?.pwriteAll(buffer, sect.offset);
   2419 }
   2420 
   2421 fn setEntryPoint(self: *Zld) !void {
   2422     // TODO we should respect the -entry flag passed in by the user to set a custom
   2423     // entrypoint. For now, assume default of `_main`.
   2424     const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
   2425     const sym = self.globals.get("_main") orelse return error.MissingMainEntrypoint;
   2426     const entry_sym = sym.cast(Symbol.Regular) orelse unreachable;
   2427     const ec = &self.load_commands.items[self.main_cmd_index.?].Main;
   2428     ec.entryoff = @intCast(u32, entry_sym.address - seg.inner.vmaddr);
   2429     ec.stacksize = self.stack_size;
   2430 }
   2431 
   2432 fn writeRebaseInfoTable(self: *Zld) !void {
   2433     var pointers = std.ArrayList(Pointer).init(self.allocator);
   2434     defer pointers.deinit();
   2435 
   2436     try pointers.ensureCapacity(self.local_rebases.items.len);
   2437     pointers.appendSliceAssumeCapacity(self.local_rebases.items);
   2438 
   2439     if (self.got_section_index) |idx| {
   2440         const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
   2441         const sect = seg.sections.items[idx];
   2442         const base_offset = sect.addr - seg.inner.vmaddr;
   2443         const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
   2444 
   2445         for (self.got_entries.items) |sym| {
   2446             if (sym.@"type" == .proxy) continue;
   2447             try pointers.append(.{
   2448                 .offset = base_offset + sym.got_index.? * @sizeOf(u64),
   2449                 .segment_id = segment_id,
   2450             });
   2451         }
   2452     }
   2453 
   2454     if (self.mod_init_func_section_index) |idx| {
   2455         const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
   2456         const sect = seg.sections.items[idx];
   2457         const base_offset = sect.addr - seg.inner.vmaddr;
   2458         const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
   2459 
   2460         var index: u64 = 0;
   2461         for (self.objects.items) |object| {
   2462             for (object.initializers.items) |_| {
   2463                 try pointers.append(.{
   2464                     .offset = base_offset + index * @sizeOf(u64),
   2465                     .segment_id = segment_id,
   2466                 });
   2467                 index += 1;
   2468             }
   2469         }
   2470     }
   2471 
   2472     if (self.la_symbol_ptr_section_index) |idx| {
   2473         const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
   2474         const sect = seg.sections.items[idx];
   2475         const base_offset = sect.addr - seg.inner.vmaddr;
   2476         const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
   2477 
   2478         try pointers.ensureCapacity(pointers.items.len + self.stubs.items.len);
   2479         for (self.stubs.items) |sym| {
   2480             pointers.appendAssumeCapacity(.{
   2481                 .offset = base_offset + sym.stubs_index.? * @sizeOf(u64),
   2482                 .segment_id = segment_id,
   2483             });
   2484         }
   2485     }
   2486 
   2487     std.sort.sort(Pointer, pointers.items, {}, pointerCmp);
   2488 
   2489     const size = try rebaseInfoSize(pointers.items);
   2490     var buffer = try self.allocator.alloc(u8, @intCast(usize, size));
   2491     defer self.allocator.free(buffer);
   2492 
   2493     var stream = std.io.fixedBufferStream(buffer);
   2494     try writeRebaseInfo(pointers.items, stream.writer());
   2495 
   2496     const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
   2497     const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
   2498     dyld_info.rebase_off = @intCast(u32, seg.inner.fileoff);
   2499     dyld_info.rebase_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @sizeOf(u64)));
   2500     seg.inner.filesize += dyld_info.rebase_size;
   2501 
   2502     log.debug("writing rebase info from 0x{x} to 0x{x}", .{ dyld_info.rebase_off, dyld_info.rebase_off + dyld_info.rebase_size });
   2503 
   2504     try self.file.?.pwriteAll(buffer, dyld_info.rebase_off);
   2505 }
   2506 
   2507 fn writeBindInfoTable(self: *Zld) !void {
   2508     var pointers = std.ArrayList(Pointer).init(self.allocator);
   2509     defer pointers.deinit();
   2510 
   2511     if (self.got_section_index) |idx| {
   2512         const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
   2513         const sect = seg.sections.items[idx];
   2514         const base_offset = sect.addr - seg.inner.vmaddr;
   2515         const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
   2516 
   2517         for (self.got_entries.items) |sym| {
   2518             if (sym.cast(Symbol.Proxy)) |proxy| {
   2519                 try pointers.append(.{
   2520                     .offset = base_offset + proxy.base.got_index.? * @sizeOf(u64),
   2521                     .segment_id = segment_id,
   2522                     .dylib_ordinal = proxy.dylibOrdinal(),
   2523                     .name = proxy.base.name,
   2524                 });
   2525             }
   2526         }
   2527     }
   2528 
   2529     for (self.imports.values()) |sym| {
   2530         if (sym.cast(Symbol.Proxy)) |proxy| {
   2531             for (proxy.bind_info.items) |info| {
   2532                 const seg = self.load_commands.items[info.segment_id].Segment;
   2533                 try pointers.append(.{
   2534                     .offset = info.address - seg.inner.vmaddr,
   2535                     .segment_id = info.segment_id,
   2536                     .dylib_ordinal = proxy.dylibOrdinal(),
   2537                     .name = proxy.base.name,
   2538                 });
   2539             }
   2540         }
   2541     }
   2542 
   2543     if (self.tlv_section_index) |idx| {
   2544         const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
   2545         const sect = seg.sections.items[idx];
   2546         const base_offset = sect.addr - seg.inner.vmaddr;
   2547         const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
   2548 
   2549         const sym = self.imports.get("__tlv_bootstrap") orelse unreachable;
   2550         const proxy = sym.cast(Symbol.Proxy) orelse unreachable;
   2551 
   2552         try pointers.append(.{
   2553             .offset = base_offset,
   2554             .segment_id = segment_id,
   2555             .dylib_ordinal = proxy.dylibOrdinal(),
   2556             .name = proxy.base.name,
   2557         });
   2558     }
   2559 
   2560     const size = try bindInfoSize(pointers.items);
   2561     var buffer = try self.allocator.alloc(u8, @intCast(usize, size));
   2562     defer self.allocator.free(buffer);
   2563 
   2564     var stream = std.io.fixedBufferStream(buffer);
   2565     try writeBindInfo(pointers.items, stream.writer());
   2566 
   2567     const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
   2568     const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
   2569     dyld_info.bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
   2570     dyld_info.bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
   2571     seg.inner.filesize += dyld_info.bind_size;
   2572 
   2573     log.debug("writing binding info from 0x{x} to 0x{x}", .{ dyld_info.bind_off, dyld_info.bind_off + dyld_info.bind_size });
   2574 
   2575     try self.file.?.pwriteAll(buffer, dyld_info.bind_off);
   2576 }
   2577 
   2578 fn writeLazyBindInfoTable(self: *Zld) !void {
   2579     var pointers = std.ArrayList(Pointer).init(self.allocator);
   2580     defer pointers.deinit();
   2581 
   2582     if (self.la_symbol_ptr_section_index) |idx| {
   2583         const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
   2584         const sect = seg.sections.items[idx];
   2585         const base_offset = sect.addr - seg.inner.vmaddr;
   2586         const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
   2587 
   2588         try pointers.ensureCapacity(self.stubs.items.len);
   2589 
   2590         for (self.stubs.items) |sym| {
   2591             const proxy = sym.cast(Symbol.Proxy) orelse unreachable;
   2592             pointers.appendAssumeCapacity(.{
   2593                 .offset = base_offset + sym.stubs_index.? * @sizeOf(u64),
   2594                 .segment_id = segment_id,
   2595                 .dylib_ordinal = proxy.dylibOrdinal(),
   2596                 .name = sym.name,
   2597             });
   2598         }
   2599     }
   2600 
   2601     const size = try lazyBindInfoSize(pointers.items);
   2602     var buffer = try self.allocator.alloc(u8, @intCast(usize, size));
   2603     defer self.allocator.free(buffer);
   2604 
   2605     var stream = std.io.fixedBufferStream(buffer);
   2606     try writeLazyBindInfo(pointers.items, stream.writer());
   2607 
   2608     const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
   2609     const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
   2610     dyld_info.lazy_bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
   2611     dyld_info.lazy_bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
   2612     seg.inner.filesize += dyld_info.lazy_bind_size;
   2613 
   2614     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 });
   2615 
   2616     try self.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
   2617     try self.populateLazyBindOffsetsInStubHelper(buffer);
   2618 }
   2619 
   2620 fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void {
   2621     var stream = std.io.fixedBufferStream(buffer);
   2622     var reader = stream.reader();
   2623     var offsets = std.ArrayList(u32).init(self.allocator);
   2624     try offsets.append(0);
   2625     defer offsets.deinit();
   2626     var valid_block = false;
   2627 
   2628     while (true) {
   2629         const inst = reader.readByte() catch |err| switch (err) {
   2630             error.EndOfStream => break,
   2631             else => return err,
   2632         };
   2633         const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
   2634 
   2635         switch (opcode) {
   2636             macho.BIND_OPCODE_DO_BIND => {
   2637                 valid_block = true;
   2638             },
   2639             macho.BIND_OPCODE_DONE => {
   2640                 if (valid_block) {
   2641                     const offset = try stream.getPos();
   2642                     try offsets.append(@intCast(u32, offset));
   2643                 }
   2644                 valid_block = false;
   2645             },
   2646             macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
   2647                 var next = try reader.readByte();
   2648                 while (next != @as(u8, 0)) {
   2649                     next = try reader.readByte();
   2650                 }
   2651             },
   2652             macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
   2653                 _ = try leb.readULEB128(u64, reader);
   2654             },
   2655             macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
   2656                 _ = try leb.readULEB128(u64, reader);
   2657             },
   2658             macho.BIND_OPCODE_SET_ADDEND_SLEB => {
   2659                 _ = try leb.readILEB128(i64, reader);
   2660             },
   2661             else => {},
   2662         }
   2663     }
   2664     assert(self.stubs.items.len <= offsets.items.len);
   2665 
   2666     const stub_size: u4 = switch (self.arch.?) {
   2667         .x86_64 => 10,
   2668         .aarch64 => 3 * @sizeOf(u32),
   2669         else => unreachable,
   2670     };
   2671     const off: u4 = switch (self.arch.?) {
   2672         .x86_64 => 1,
   2673         .aarch64 => 2 * @sizeOf(u32),
   2674         else => unreachable,
   2675     };
   2676     var buf: [@sizeOf(u32)]u8 = undefined;
   2677     for (self.stubs.items) |sym| {
   2678         const index = sym.stubs_index orelse unreachable;
   2679         const placeholder_off = self.stub_helper_stubs_start_off.? + index * stub_size + off;
   2680         mem.writeIntLittle(u32, &buf, offsets.items[index]);
   2681         try self.file.?.pwriteAll(&buf, placeholder_off);
   2682     }
   2683 }
   2684 
   2685 fn writeExportInfo(self: *Zld) !void {
   2686     var trie = Trie.init(self.allocator);
   2687     defer trie.deinit();
   2688 
   2689     const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
   2690 
   2691     // TODO export items for dylibs
   2692     const sym = self.globals.get("_main") orelse return error.MissingMainEntrypoint;
   2693     const reg = sym.cast(Symbol.Regular) orelse unreachable;
   2694     assert(reg.address >= text_segment.inner.vmaddr);
   2695 
   2696     try trie.put(.{
   2697         .name = sym.name,
   2698         .vmaddr_offset = reg.address - text_segment.inner.vmaddr,
   2699         .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
   2700     });
   2701 
   2702     try trie.finalize();
   2703 
   2704     var buffer = try self.allocator.alloc(u8, @intCast(usize, trie.size));
   2705     defer self.allocator.free(buffer);
   2706 
   2707     var stream = std.io.fixedBufferStream(buffer);
   2708     const nwritten = try trie.write(stream.writer());
   2709     assert(nwritten == trie.size);
   2710 
   2711     const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
   2712     const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
   2713     dyld_info.export_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
   2714     dyld_info.export_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
   2715     seg.inner.filesize += dyld_info.export_size;
   2716 
   2717     log.debug("writing export info from 0x{x} to 0x{x}", .{ dyld_info.export_off, dyld_info.export_off + dyld_info.export_size });
   2718 
   2719     try self.file.?.pwriteAll(buffer, dyld_info.export_off);
   2720 }
   2721 
   2722 fn writeDebugInfo(self: *Zld) !void {
   2723     var stabs = std.ArrayList(macho.nlist_64).init(self.allocator);
   2724     defer stabs.deinit();
   2725 
   2726     for (self.objects.items) |object| {
   2727         const tu_path = object.tu_path orelse continue;
   2728         const tu_mtime = object.tu_mtime orelse continue;
   2729         _ = tu_mtime;
   2730         const dirname = std.fs.path.dirname(tu_path) orelse "./";
   2731         // Current dir
   2732         try stabs.append(.{
   2733             .n_strx = try self.makeString(tu_path[0 .. dirname.len + 1]),
   2734             .n_type = macho.N_SO,
   2735             .n_sect = 0,
   2736             .n_desc = 0,
   2737             .n_value = 0,
   2738         });
   2739         // Artifact name
   2740         try stabs.append(.{
   2741             .n_strx = try self.makeString(tu_path[dirname.len + 1 ..]),
   2742             .n_type = macho.N_SO,
   2743             .n_sect = 0,
   2744             .n_desc = 0,
   2745             .n_value = 0,
   2746         });
   2747         // Path to object file with debug info
   2748         try stabs.append(.{
   2749             .n_strx = try self.makeString(object.name.?),
   2750             .n_type = macho.N_OSO,
   2751             .n_sect = 0,
   2752             .n_desc = 1,
   2753             .n_value = 0, //tu_mtime, TODO figure out why precalculated mtime value doesn't work
   2754         });
   2755 
   2756         for (object.symbols.items) |sym| {
   2757             const reg = reg: {
   2758                 switch (sym.@"type") {
   2759                     .regular => break :reg sym.cast(Symbol.Regular) orelse unreachable,
   2760                     .tentative => {
   2761                         const final = sym.getTopmostAlias().cast(Symbol.Regular) orelse unreachable;
   2762                         if (object != final.file) continue;
   2763                         break :reg final;
   2764                     },
   2765                     else => continue,
   2766                 }
   2767             };
   2768 
   2769             if (reg.isTemp() or reg.stab == null) continue;
   2770             const stab = reg.stab orelse unreachable;
   2771 
   2772             switch (stab.kind) {
   2773                 .function => {
   2774                     try stabs.append(.{
   2775                         .n_strx = 0,
   2776                         .n_type = macho.N_BNSYM,
   2777                         .n_sect = reg.section,
   2778                         .n_desc = 0,
   2779                         .n_value = reg.address,
   2780                     });
   2781                     try stabs.append(.{
   2782                         .n_strx = try self.makeString(sym.name),
   2783                         .n_type = macho.N_FUN,
   2784                         .n_sect = reg.section,
   2785                         .n_desc = 0,
   2786                         .n_value = reg.address,
   2787                     });
   2788                     try stabs.append(.{
   2789                         .n_strx = 0,
   2790                         .n_type = macho.N_FUN,
   2791                         .n_sect = 0,
   2792                         .n_desc = 0,
   2793                         .n_value = stab.size,
   2794                     });
   2795                     try stabs.append(.{
   2796                         .n_strx = 0,
   2797                         .n_type = macho.N_ENSYM,
   2798                         .n_sect = reg.section,
   2799                         .n_desc = 0,
   2800                         .n_value = stab.size,
   2801                     });
   2802                 },
   2803                 .global => {
   2804                     try stabs.append(.{
   2805                         .n_strx = try self.makeString(sym.name),
   2806                         .n_type = macho.N_GSYM,
   2807                         .n_sect = 0,
   2808                         .n_desc = 0,
   2809                         .n_value = 0,
   2810                     });
   2811                 },
   2812                 .static => {
   2813                     try stabs.append(.{
   2814                         .n_strx = try self.makeString(sym.name),
   2815                         .n_type = macho.N_STSYM,
   2816                         .n_sect = reg.section,
   2817                         .n_desc = 0,
   2818                         .n_value = reg.address,
   2819                     });
   2820                 },
   2821             }
   2822         }
   2823 
   2824         // Close the source file!
   2825         try stabs.append(.{
   2826             .n_strx = 0,
   2827             .n_type = macho.N_SO,
   2828             .n_sect = 0,
   2829             .n_desc = 0,
   2830             .n_value = 0,
   2831         });
   2832     }
   2833 
   2834     if (stabs.items.len == 0) return;
   2835 
   2836     // Write stabs into the symbol table
   2837     const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
   2838     const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
   2839 
   2840     symtab.nsyms = @intCast(u32, stabs.items.len);
   2841 
   2842     const stabs_off = symtab.symoff;
   2843     const stabs_size = symtab.nsyms * @sizeOf(macho.nlist_64);
   2844     log.debug("writing symbol stabs from 0x{x} to 0x{x}", .{ stabs_off, stabs_size + stabs_off });
   2845     try self.file.?.pwriteAll(mem.sliceAsBytes(stabs.items), stabs_off);
   2846 
   2847     linkedit.inner.filesize += stabs_size;
   2848 
   2849     // Update dynamic symbol table.
   2850     const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
   2851     dysymtab.nlocalsym = symtab.nsyms;
   2852 }
   2853 
   2854 fn writeSymbolTable(self: *Zld) !void {
   2855     const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
   2856     const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
   2857 
   2858     var locals = std.ArrayList(macho.nlist_64).init(self.allocator);
   2859     defer locals.deinit();
   2860 
   2861     var exports = std.ArrayList(macho.nlist_64).init(self.allocator);
   2862     defer exports.deinit();
   2863 
   2864     for (self.objects.items) |object| {
   2865         for (object.symbols.items) |sym| {
   2866             const final = sym.getTopmostAlias();
   2867             if (final.@"type" != .regular) continue;
   2868 
   2869             const reg = final.cast(Symbol.Regular) orelse unreachable;
   2870             if (reg.isTemp()) continue;
   2871             if (reg.visited) continue;
   2872 
   2873             switch (reg.linkage) {
   2874                 .translation_unit => {
   2875                     try locals.append(.{
   2876                         .n_strx = try self.makeString(sym.name),
   2877                         .n_type = macho.N_SECT,
   2878                         .n_sect = reg.section,
   2879                         .n_desc = 0,
   2880                         .n_value = reg.address,
   2881                     });
   2882                 },
   2883                 else => {
   2884                     try exports.append(.{
   2885                         .n_strx = try self.makeString(sym.name),
   2886                         .n_type = macho.N_SECT | macho.N_EXT,
   2887                         .n_sect = reg.section,
   2888                         .n_desc = 0,
   2889                         .n_value = reg.address,
   2890                     });
   2891                 },
   2892             }
   2893 
   2894             reg.visited = true;
   2895         }
   2896     }
   2897 
   2898     var undefs = std.ArrayList(macho.nlist_64).init(self.allocator);
   2899     defer undefs.deinit();
   2900 
   2901     for (self.imports.values()) |sym| {
   2902         const proxy = sym.cast(Symbol.Proxy) orelse unreachable;
   2903         try undefs.append(.{
   2904             .n_strx = try self.makeString(sym.name),
   2905             .n_type = macho.N_UNDF | macho.N_EXT,
   2906             .n_sect = 0,
   2907             .n_desc = (proxy.dylibOrdinal() * macho.N_SYMBOL_RESOLVER) | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY,
   2908             .n_value = 0,
   2909         });
   2910     }
   2911 
   2912     const nlocals = locals.items.len;
   2913     const nexports = exports.items.len;
   2914     const nundefs = undefs.items.len;
   2915 
   2916     const locals_off = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64);
   2917     const locals_size = nlocals * @sizeOf(macho.nlist_64);
   2918     log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });
   2919     try self.file.?.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
   2920 
   2921     const exports_off = locals_off + locals_size;
   2922     const exports_size = nexports * @sizeOf(macho.nlist_64);
   2923     log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });
   2924     try self.file.?.pwriteAll(mem.sliceAsBytes(exports.items), exports_off);
   2925 
   2926     const undefs_off = exports_off + exports_size;
   2927     const undefs_size = nundefs * @sizeOf(macho.nlist_64);
   2928     log.debug("writing undefined symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
   2929     try self.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off);
   2930 
   2931     symtab.nsyms += @intCast(u32, nlocals + nexports + nundefs);
   2932     seg.inner.filesize += locals_size + exports_size + undefs_size;
   2933 
   2934     // Update dynamic symbol table.
   2935     const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
   2936     dysymtab.nlocalsym += @intCast(u32, nlocals);
   2937     dysymtab.iextdefsym = dysymtab.nlocalsym;
   2938     dysymtab.nextdefsym = @intCast(u32, nexports);
   2939     dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;
   2940     dysymtab.nundefsym = @intCast(u32, nundefs);
   2941 
   2942     const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
   2943     const stubs = &text_segment.sections.items[self.stubs_section_index.?];
   2944     const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
   2945     const got = &data_const_segment.sections.items[self.got_section_index.?];
   2946     const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
   2947     const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
   2948 
   2949     const nstubs = @intCast(u32, self.stubs.items.len);
   2950     const ngot_entries = @intCast(u32, self.got_entries.items.len);
   2951 
   2952     dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
   2953     dysymtab.nindirectsyms = nstubs * 2 + ngot_entries;
   2954 
   2955     const needed_size = dysymtab.nindirectsyms * @sizeOf(u32);
   2956     seg.inner.filesize += needed_size;
   2957 
   2958     log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{
   2959         dysymtab.indirectsymoff,
   2960         dysymtab.indirectsymoff + needed_size,
   2961     });
   2962 
   2963     var buf = try self.allocator.alloc(u8, needed_size);
   2964     defer self.allocator.free(buf);
   2965 
   2966     var stream = std.io.fixedBufferStream(buf);
   2967     var writer = stream.writer();
   2968 
   2969     stubs.reserved1 = 0;
   2970     for (self.stubs.items) |sym| {
   2971         const id = self.imports.getIndex(sym.name) orelse unreachable;
   2972         try writer.writeIntLittle(u32, dysymtab.iundefsym + @intCast(u32, id));
   2973     }
   2974 
   2975     got.reserved1 = nstubs;
   2976     for (self.got_entries.items) |sym| {
   2977         if (sym.@"type" == .proxy) {
   2978             const id = self.imports.getIndex(sym.name) orelse unreachable;
   2979             try writer.writeIntLittle(u32, dysymtab.iundefsym + @intCast(u32, id));
   2980         } else {
   2981             try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
   2982         }
   2983     }
   2984 
   2985     la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;
   2986     for (self.stubs.items) |sym| {
   2987         const id = self.imports.getIndex(sym.name) orelse unreachable;
   2988         try writer.writeIntLittle(u32, dysymtab.iundefsym + @intCast(u32, id));
   2989     }
   2990 
   2991     try self.file.?.pwriteAll(buf, dysymtab.indirectsymoff);
   2992 }
   2993 
   2994 fn writeStringTable(self: *Zld) !void {
   2995     const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
   2996     const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
   2997     symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
   2998     symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64)));
   2999     seg.inner.filesize += symtab.strsize;
   3000 
   3001     log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
   3002 
   3003     try self.file.?.pwriteAll(self.strtab.items, symtab.stroff);
   3004 
   3005     if (symtab.strsize > self.strtab.items.len and self.arch.? == .x86_64) {
   3006         // This is the last section, so we need to pad it out.
   3007         try self.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1);
   3008     }
   3009 }
   3010 
   3011 fn writeDataInCode(self: *Zld) !void {
   3012     const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
   3013     const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData;
   3014     const fileoff = seg.inner.fileoff + seg.inner.filesize;
   3015 
   3016     var buf = std.ArrayList(u8).init(self.allocator);
   3017     defer buf.deinit();
   3018 
   3019     const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
   3020     const text_sect = text_seg.sections.items[self.text_section_index.?];
   3021     for (self.objects.items) |object| {
   3022         const source_sect = object.sections.items[object.text_section_index.?];
   3023         const target_map = source_sect.target_map orelse continue;
   3024 
   3025         try buf.ensureCapacity(
   3026             buf.items.len + object.data_in_code_entries.items.len * @sizeOf(macho.data_in_code_entry),
   3027         );
   3028         for (object.data_in_code_entries.items) |dice| {
   3029             const new_dice: macho.data_in_code_entry = .{
   3030                 .offset = text_sect.offset + target_map.offset + dice.offset,
   3031                 .length = dice.length,
   3032                 .kind = dice.kind,
   3033             };
   3034             buf.appendSliceAssumeCapacity(mem.asBytes(&new_dice));
   3035         }
   3036     }
   3037     const datasize = @intCast(u32, buf.items.len);
   3038 
   3039     dice_cmd.dataoff = @intCast(u32, fileoff);
   3040     dice_cmd.datasize = datasize;
   3041     seg.inner.filesize += datasize;
   3042 
   3043     log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ fileoff, fileoff + datasize });
   3044 
   3045     try self.file.?.pwriteAll(buf.items, fileoff);
   3046 }
   3047 
   3048 fn writeCodeSignaturePadding(self: *Zld) !void {
   3049     const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
   3050     const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
   3051     const fileoff = seg.inner.fileoff + seg.inner.filesize;
   3052     const needed_size = CodeSignature.calcCodeSignaturePaddingSize(
   3053         self.out_path.?,
   3054         fileoff,
   3055         self.page_size.?,
   3056     );
   3057     code_sig_cmd.dataoff = @intCast(u32, fileoff);
   3058     code_sig_cmd.datasize = needed_size;
   3059 
   3060     // Advance size of __LINKEDIT segment
   3061     seg.inner.filesize += needed_size;
   3062     seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size.?);
   3063 
   3064     log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size });
   3065 
   3066     // Pad out the space. We need to do this to calculate valid hashes for everything in the file
   3067     // except for code signature data.
   3068     try self.file.?.pwriteAll(&[_]u8{0}, fileoff + needed_size - 1);
   3069 }
   3070 
   3071 fn writeCodeSignature(self: *Zld) !void {
   3072     const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
   3073     const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData;
   3074 
   3075     var code_sig = CodeSignature.init(self.allocator, self.page_size.?);
   3076     defer code_sig.deinit();
   3077     try code_sig.calcAdhocSignature(
   3078         self.file.?,
   3079         self.out_path.?,
   3080         text_seg.inner,
   3081         code_sig_cmd,
   3082         .Exe,
   3083     );
   3084 
   3085     var buffer = try self.allocator.alloc(u8, code_sig.size());
   3086     defer self.allocator.free(buffer);
   3087     var stream = std.io.fixedBufferStream(buffer);
   3088     try code_sig.write(stream.writer());
   3089 
   3090     log.debug("writing code signature from 0x{x} to 0x{x}", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len });
   3091     try self.file.?.pwriteAll(buffer, code_sig_cmd.dataoff);
   3092 }
   3093 
   3094 fn writeLoadCommands(self: *Zld) !void {
   3095     var sizeofcmds: u32 = 0;
   3096     for (self.load_commands.items) |lc| {
   3097         sizeofcmds += lc.cmdsize();
   3098     }
   3099 
   3100     var buffer = try self.allocator.alloc(u8, sizeofcmds);
   3101     defer self.allocator.free(buffer);
   3102     var writer = std.io.fixedBufferStream(buffer).writer();
   3103     for (self.load_commands.items) |lc| {
   3104         try lc.write(writer);
   3105     }
   3106 
   3107     const off = @sizeOf(macho.mach_header_64);
   3108     log.debug("writing {} load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds });
   3109     try self.file.?.pwriteAll(buffer, off);
   3110 }
   3111 
   3112 fn writeHeader(self: *Zld) !void {
   3113     var header: macho.mach_header_64 = undefined;
   3114     header.magic = macho.MH_MAGIC_64;
   3115 
   3116     const CpuInfo = struct {
   3117         cpu_type: macho.cpu_type_t,
   3118         cpu_subtype: macho.cpu_subtype_t,
   3119     };
   3120 
   3121     const cpu_info: CpuInfo = switch (self.arch.?) {
   3122         .aarch64 => .{
   3123             .cpu_type = macho.CPU_TYPE_ARM64,
   3124             .cpu_subtype = macho.CPU_SUBTYPE_ARM_ALL,
   3125         },
   3126         .x86_64 => .{
   3127             .cpu_type = macho.CPU_TYPE_X86_64,
   3128             .cpu_subtype = macho.CPU_SUBTYPE_X86_64_ALL,
   3129         },
   3130         else => return error.UnsupportedCpuArchitecture,
   3131     };
   3132     header.cputype = cpu_info.cpu_type;
   3133     header.cpusubtype = cpu_info.cpu_subtype;
   3134     header.filetype = macho.MH_EXECUTE;
   3135     header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
   3136     header.reserved = 0;
   3137 
   3138     if (self.tlv_section_index) |_|
   3139         header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
   3140 
   3141     header.ncmds = @intCast(u32, self.load_commands.items.len);
   3142     header.sizeofcmds = 0;
   3143     for (self.load_commands.items) |cmd| {
   3144         header.sizeofcmds += cmd.cmdsize();
   3145     }
   3146     log.debug("writing Mach-O header {}", .{header});
   3147     try self.file.?.pwriteAll(mem.asBytes(&header), 0);
   3148 }
   3149 
   3150 fn makeString(self: *Zld, bytes: []const u8) !u32 {
   3151     if (self.strtab_dir.get(bytes)) |offset| {
   3152         log.debug("reusing '{s}' from string table at offset 0x{x}", .{ bytes, offset });
   3153         return offset;
   3154     }
   3155 
   3156     try self.strtab.ensureCapacity(self.allocator, self.strtab.items.len + bytes.len + 1);
   3157     const offset = @intCast(u32, self.strtab.items.len);
   3158     log.debug("writing new string '{s}' into string table at offset 0x{x}", .{ bytes, offset });
   3159     self.strtab.appendSliceAssumeCapacity(bytes);
   3160     self.strtab.appendAssumeCapacity(0);
   3161     try self.strtab_dir.putNoClobber(self.allocator, try self.allocator.dupe(u8, bytes), offset);
   3162     return offset;
   3163 }
   3164 
   3165 fn getString(self: *const Zld, str_off: u32) []const u8 {
   3166     assert(str_off < self.strtab.items.len);
   3167     return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + str_off));
   3168 }
   3169 
   3170 pub fn parseName(name: *const [16]u8) []const u8 {
   3171     const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
   3172     return name[0..len];
   3173 }
   3174 
   3175 fn printSymbols(self: *Zld) void {
   3176     log.debug("globals", .{});
   3177     for (self.globals.values()) |value| {
   3178         const sym = value.cast(Symbol.Regular) orelse unreachable;
   3179         log.debug("    | {s} @ {*}", .{ sym.base.name, value });
   3180         log.debug("      => alias of {*}", .{sym.base.alias});
   3181         log.debug("      => linkage {s}", .{sym.linkage});
   3182         log.debug("      => defined in {s}", .{sym.file.name.?});
   3183     }
   3184     for (self.objects.items) |object| {
   3185         log.debug("locals in {s}", .{object.name.?});
   3186         for (object.symbols.items) |sym| {
   3187             log.debug("    | {s} @ {*}", .{ sym.name, sym });
   3188             log.debug("      => alias of {*}", .{sym.alias});
   3189             if (sym.cast(Symbol.Regular)) |reg| {
   3190                 log.debug("      => linkage {s}", .{reg.linkage});
   3191             } else {
   3192                 log.debug("      => unresolved", .{});
   3193             }
   3194         }
   3195     }
   3196     log.debug("proxies", .{});
   3197     for (self.imports.values()) |value| {
   3198         const sym = value.cast(Symbol.Proxy) orelse unreachable;
   3199         log.debug("    | {s} @ {*}", .{ sym.base.name, value });
   3200         log.debug("      => alias of {*}", .{sym.base.alias});
   3201         log.debug("      => defined in libSystem.B.dylib", .{});
   3202     }
   3203 }