| ... | ... | @@ -52,6 +52,8 @@ load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| 52 | 52 | pagezero_segment_cmd_index: ?u16 = null, |
| 53 | 53 | /// __TEXT segment |
| 54 | 54 | text_segment_cmd_index: ?u16 = null, |
| 55 | /// __DATA_CONST segment |
| 56 | data_const_segment_cmd_index: ?u16 = null, |
| 55 | 57 | /// __DATA segment |
| 56 | 58 | data_segment_cmd_index: ?u16 = null, |
| 57 | 59 | /// __LINKEDIT segment |
| ... | ... | @@ -87,22 +89,34 @@ code_signature_cmd_index: ?u16 = null, |
| 87 | 89 | text_section_index: ?u16 = null, |
| 88 | 90 | /// Index into __TEXT,__ziggot section. |
| 89 | 91 | got_section_index: ?u16 = null, |
| 92 | /// Index into __TEXT,__stubs section. |
| 93 | stubs_section_index: ?u16 = null, |
| 94 | /// Index into __TEXT,__stub_helper section. |
| 95 | stub_helper_section_index: ?u16 = null, |
| 96 | /// Index into __DATA_CONST,__got section. |
| 97 | data_got_section_index: ?u16 = null, |
| 98 | /// Index into __DATA,__la_symbol_ptr section. |
| 99 | la_symbol_ptr_section_index: ?u16 = null, |
| 100 | /// Index into __DATA,__data section. |
| 101 | data_section_index: ?u16 = null, |
| 90 | 102 | /// The absolute address of the entry point. |
| 91 | 103 | entry_addr: ?u64 = null, |
| 92 | 104 | |
| 93 | 105 | /// Table of all local symbols |
| 94 | 106 | /// Internally references string table for names (which are optional). |
| 95 | 107 | local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 96 | | /// Table of all defined global symbols |
| 108 | /// Table of all global symbols |
| 97 | 109 | global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 98 | | /// Table of all undefined symbols |
| 99 | | undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 110 | /// Table of all extern nonlazy symbols, indexed by name. |
| 111 | extern_nonlazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{}, |
| 112 | /// Table of all extern lazy symbols, indexed by name. |
| 113 | extern_lazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{}, |
| 100 | 114 | |
| 101 | 115 | local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 102 | 116 | global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 103 | 117 | offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 104 | 118 | |
| 105 | | dyld_stub_binder_index: ?u16 = null, |
| 119 | stub_helper_stubs_start_off: ?u64 = null, |
| 106 | 120 | |
| 107 | 121 | /// Table of symbol names aka the string table. |
| 108 | 122 | string_table: std.ArrayListUnmanaged(u8) = .{}, |
| ... | ... | @@ -110,16 +124,12 @@ string_table: std.ArrayListUnmanaged(u8) = .{}, |
| 110 | 124 | /// Table of trampolines to the actual symbols in __text section. |
| 111 | 125 | offset_table: std.ArrayListUnmanaged(u64) = .{}, |
| 112 | 126 | |
| 113 | | /// Table of binding info entries. |
| 114 | | binding_info_table: BindingInfoTable = .{}, |
| 115 | | /// Table of lazy binding info entries. |
| 116 | | lazy_binding_info_table: LazyBindingInfoTable = .{}, |
| 117 | | |
| 118 | 127 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 119 | 128 | |
| 120 | 129 | offset_table_count_dirty: bool = false, |
| 121 | 130 | header_dirty: bool = false, |
| 122 | 131 | load_commands_dirty: bool = false, |
| 132 | rebase_info_dirty: bool = false, |
| 123 | 133 | binding_info_dirty: bool = false, |
| 124 | 134 | lazy_binding_info_dirty: bool = false, |
| 125 | 135 | export_info_dirty: bool = false, |
| ... | ... | @@ -149,6 +159,12 @@ last_text_block: ?*TextBlock = null, |
| 149 | 159 | /// prior to calling `generateSymbol`, and then immediately deallocated |
| 150 | 160 | /// rather than sitting in the global scope. |
| 151 | 161 | pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{}, |
| 162 | /// A list of all stub (extern decls) fixups required for this run of the linker. |
| 163 | /// Warning, this is currently NOT thread-safe. See the TODO below. |
| 164 | /// TODO Move this list inside `updateDecl` where it should be allocated |
| 165 | /// prior to calling `generateSymbol`, and then immediately deallocated |
| 166 | /// rather than sitting in the global scope. |
| 167 | stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{}, |
| 152 | 168 | |
| 153 | 169 | pub const PieFixup = struct { |
| 154 | 170 | /// Target address we wanted to address in absolute terms. |
| ... | ... | @@ -160,6 +176,19 @@ pub const PieFixup = struct { |
| 160 | 176 | len: usize, |
| 161 | 177 | }; |
| 162 | 178 | |
| 179 | pub const StubFixup = struct { |
| 180 | /// Id of extern (lazy) symbol. |
| 181 | symbol: u32, |
| 182 | /// Signals whether the symbol has already been declared before. If so, |
| 183 | /// then there is no need to rewrite the stub entry and related. |
| 184 | already_defined: bool, |
| 185 | /// Where in the byte stream we should perform the fixup. |
| 186 | start: usize, |
| 187 | /// The length of the byte stream. For x86_64, this will be |
| 188 | /// variable. For aarch64, it will be fixed at 4 bytes. |
| 189 | len: usize, |
| 190 | }; |
| 191 | |
| 163 | 192 | /// `alloc_num / alloc_den` is the factor of padding when allocating. |
| 164 | 193 | pub const alloc_num = 4; |
| 165 | 194 | pub const alloc_den = 3; |
| ... | ... | @@ -379,10 +408,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 379 | 408 | main_cmd.entryoff = addr - text_segment.inner.vmaddr; |
| 380 | 409 | self.load_commands_dirty = true; |
| 381 | 410 | } |
| 411 | try self.writeRebaseInfoTable(); |
| 382 | 412 | try self.writeBindingInfoTable(); |
| 383 | 413 | try self.writeLazyBindingInfoTable(); |
| 384 | 414 | try self.writeExportTrie(); |
| 385 | 415 | try self.writeAllGlobalAndUndefSymbols(); |
| 416 | try self.writeIndirectSymbolTable(); |
| 386 | 417 | try self.writeStringTable(); |
| 387 | 418 | try self.updateLinkeditSegmentSizes(); |
| 388 | 419 | |
| ... | ... | @@ -418,6 +449,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 418 | 449 | assert(!self.offset_table_count_dirty); |
| 419 | 450 | assert(!self.header_dirty); |
| 420 | 451 | assert(!self.load_commands_dirty); |
| 452 | assert(!self.rebase_info_dirty); |
| 421 | 453 | assert(!self.binding_info_dirty); |
| 422 | 454 | assert(!self.lazy_binding_info_dirty); |
| 423 | 455 | assert(!self.export_info_dirty); |
| ... | ... | @@ -891,42 +923,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 891 | 923 | return error.NoSymbolTableFound; |
| 892 | 924 | } |
| 893 | 925 | |
| 894 | | // Parse dyld info |
| 895 | | try self.parseBindingInfoTable(); |
| 896 | | try self.parseLazyBindingInfoTable(); |
| 897 | | |
| 898 | | // Update the dylib ordinals. |
| 899 | | self.binding_info_table.dylib_ordinal = next_ordinal; |
| 900 | | for (self.lazy_binding_info_table.symbols.items) |*symbol| { |
| 901 | | symbol.dylib_ordinal = next_ordinal; |
| 902 | | } |
| 903 | | |
| 904 | | // Write updated dyld info. |
| 905 | | const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 906 | | { |
| 907 | | const size = try self.binding_info_table.calcSize(); |
| 908 | | assert(dyld_info.bind_size >= size); |
| 909 | | |
| 910 | | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); |
| 911 | | defer self.base.allocator.free(buffer); |
| 912 | | |
| 913 | | var stream = std.io.fixedBufferStream(buffer); |
| 914 | | try self.binding_info_table.write(stream.writer()); |
| 915 | | |
| 916 | | try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off); |
| 917 | | } |
| 918 | | { |
| 919 | | const size = try self.lazy_binding_info_table.calcSize(); |
| 920 | | assert(dyld_info.lazy_bind_size >= size); |
| 921 | | |
| 922 | | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); |
| 923 | | defer self.base.allocator.free(buffer); |
| 924 | | |
| 925 | | var stream = std.io.fixedBufferStream(buffer); |
| 926 | | try self.lazy_binding_info_table.write(stream.writer()); |
| 927 | | |
| 928 | | try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off); |
| 929 | | } |
| 926 | // Patch dyld info |
| 927 | try self.fixupBindInfo(next_ordinal); |
| 928 | try self.fixupLazyBindInfo(next_ordinal); |
| 930 | 929 | |
| 931 | 930 | // Write updated load commands and the header |
| 932 | 931 | try self.writeLoadCommands(); |
| ... | ... | @@ -1008,14 +1007,20 @@ pub fn deinit(self: *MachO) void { |
| 1008 | 1007 | if (self.d_sym) |*ds| { |
| 1009 | 1008 | ds.deinit(self.base.allocator); |
| 1010 | 1009 | } |
| 1011 | | self.binding_info_table.deinit(self.base.allocator); |
| 1012 | | self.lazy_binding_info_table.deinit(self.base.allocator); |
| 1010 | for (self.extern_lazy_symbols.items()) |*entry| { |
| 1011 | entry.value.deinit(self.base.allocator); |
| 1012 | } |
| 1013 | self.extern_lazy_symbols.deinit(self.base.allocator); |
| 1014 | for (self.extern_nonlazy_symbols.items()) |*entry| { |
| 1015 | entry.value.deinit(self.base.allocator); |
| 1016 | } |
| 1017 | self.extern_nonlazy_symbols.deinit(self.base.allocator); |
| 1013 | 1018 | self.pie_fixups.deinit(self.base.allocator); |
| 1019 | self.stub_fixups.deinit(self.base.allocator); |
| 1014 | 1020 | self.text_block_free_list.deinit(self.base.allocator); |
| 1015 | 1021 | self.offset_table.deinit(self.base.allocator); |
| 1016 | 1022 | self.offset_table_free_list.deinit(self.base.allocator); |
| 1017 | 1023 | self.string_table.deinit(self.base.allocator); |
| 1018 | | self.undef_symbols.deinit(self.base.allocator); |
| 1019 | 1024 | self.global_symbols.deinit(self.base.allocator); |
| 1020 | 1025 | self.global_symbol_free_list.deinit(self.base.allocator); |
| 1021 | 1026 | self.local_symbols.deinit(self.base.allocator); |
| ... | ... | @@ -1211,7 +1216,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1211 | 1216 | } |
| 1212 | 1217 | |
| 1213 | 1218 | // Perform PIE fixups (if any) |
| 1214 | | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1219 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1215 | 1220 | const got_section = text_segment.sections.items[self.got_section_index.?]; |
| 1216 | 1221 | while (self.pie_fixups.popOrNull()) |fixup| { |
| 1217 | 1222 | const target_addr = fixup.address; |
| ... | ... | @@ -1231,6 +1236,38 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1231 | 1236 | } |
| 1232 | 1237 | } |
| 1233 | 1238 | |
| 1239 | // Resolve stubs (if any) |
| 1240 | const stubs = text_segment.sections.items[self.stubs_section_index.?]; |
| 1241 | for (self.stub_fixups.items) |fixup| { |
| 1242 | const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2; |
| 1243 | const text_addr = symbol.n_value + fixup.start; |
| 1244 | switch (self.base.options.target.cpu.arch) { |
| 1245 | .x86_64 => { |
| 1246 | const displacement = @intCast(u32, stub_addr - text_addr - fixup.len); |
| 1247 | var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)]; |
| 1248 | mem.writeIntSliceLittle(u32, placeholder, displacement); |
| 1249 | }, |
| 1250 | .aarch64 => { |
| 1251 | const displacement = @intCast(u32, stub_addr - text_addr); |
| 1252 | var placeholder = code_buffer.items[fixup.start..][0..fixup.len]; |
| 1253 | mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(@intCast(i28, displacement)).toU32()); |
| 1254 | }, |
| 1255 | else => unreachable, // unsupported target architecture |
| 1256 | } |
| 1257 | if (!fixup.already_defined) { |
| 1258 | try self.writeStub(fixup.symbol); |
| 1259 | try self.writeStubInStubHelper(fixup.symbol); |
| 1260 | try self.writeLazySymbolPointer(fixup.symbol); |
| 1261 | |
| 1262 | const extern_sym = &self.extern_lazy_symbols.items()[fixup.symbol].value; |
| 1263 | extern_sym.segment = self.data_segment_cmd_index.?; |
| 1264 | extern_sym.offset = fixup.symbol * @sizeOf(u64); |
| 1265 | self.rebase_info_dirty = true; |
| 1266 | self.lazy_binding_info_dirty = true; |
| 1267 | } |
| 1268 | } |
| 1269 | self.stub_fixups.shrinkRetainingCapacity(0); |
| 1270 | |
| 1234 | 1271 | const text_section = text_segment.sections.items[self.text_section_index.?]; |
| 1235 | 1272 | const section_offset = symbol.n_value - text_section.addr; |
| 1236 | 1273 | const file_offset = text_section.offset + section_offset; |
| ... | ... | @@ -1435,7 +1472,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1435 | 1472 | |
| 1436 | 1473 | const program_code_size_hint = self.base.options.program_code_size_hint; |
| 1437 | 1474 | const offset_table_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1438 | | const ideal_size = self.header_pad + program_code_size_hint + offset_table_size_hint; |
| 1475 | const ideal_size = self.header_pad + program_code_size_hint + 3 * offset_table_size_hint; |
| 1439 | 1476 | const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size); |
| 1440 | 1477 | |
| 1441 | 1478 | log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size }); |
| ... | ... | @@ -1492,9 +1529,13 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1492 | 1529 | } |
| 1493 | 1530 | if (self.got_section_index == null) { |
| 1494 | 1531 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1495 | | const text_section = &text_segment.sections.items[self.text_section_index.?]; |
| 1496 | 1532 | self.got_section_index = @intCast(u16, text_segment.sections.items.len); |
| 1497 | 1533 | |
| 1534 | const alignment: u2 = switch (self.base.options.target.cpu.arch) { |
| 1535 | .x86_64 => 0, |
| 1536 | .aarch64 => 2, |
| 1537 | else => unreachable, // unhandled architecture type |
| 1538 | }; |
| 1498 | 1539 | const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; |
| 1499 | 1540 | const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1500 | 1541 | const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad); |
| ... | ... | @@ -1508,7 +1549,220 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1508 | 1549 | .addr = text_segment.inner.vmaddr + off, |
| 1509 | 1550 | .size = needed_size, |
| 1510 | 1551 | .offset = @intCast(u32, off), |
| 1511 | | .@"align" = 3, // 2^@sizeOf(u64) |
| 1552 | .@"align" = alignment, |
| 1553 | .reloff = 0, |
| 1554 | .nreloc = 0, |
| 1555 | .flags = flags, |
| 1556 | .reserved1 = 0, |
| 1557 | .reserved2 = 0, |
| 1558 | .reserved3 = 0, |
| 1559 | }); |
| 1560 | self.header_dirty = true; |
| 1561 | self.load_commands_dirty = true; |
| 1562 | } |
| 1563 | if (self.stubs_section_index == null) { |
| 1564 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1565 | self.stubs_section_index = @intCast(u16, text_segment.sections.items.len); |
| 1566 | |
| 1567 | const alignment: u2 = switch (self.base.options.target.cpu.arch) { |
| 1568 | .x86_64 => 0, |
| 1569 | .aarch64 => 2, |
| 1570 | else => unreachable, // unhandled architecture type |
| 1571 | }; |
| 1572 | const stub_size: u4 = switch (self.base.options.target.cpu.arch) { |
| 1573 | .x86_64 => 6, |
| 1574 | .aarch64 => 2 * @sizeOf(u32), |
| 1575 | else => unreachable, // unhandled architecture type |
| 1576 | }; |
| 1577 | const flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; |
| 1578 | const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1579 | const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad); |
| 1580 | assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize); // TODO Must expand __TEXT segment. |
| 1581 | |
| 1582 | log.debug("found __stubs section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); |
| 1583 | |
| 1584 | try text_segment.addSection(self.base.allocator, .{ |
| 1585 | .sectname = makeStaticString("__stubs"), |
| 1586 | .segname = makeStaticString("__TEXT"), |
| 1587 | .addr = text_segment.inner.vmaddr + off, |
| 1588 | .size = needed_size, |
| 1589 | .offset = @intCast(u32, off), |
| 1590 | .@"align" = alignment, |
| 1591 | .reloff = 0, |
| 1592 | .nreloc = 0, |
| 1593 | .flags = flags, |
| 1594 | .reserved1 = 0, |
| 1595 | .reserved2 = stub_size, |
| 1596 | .reserved3 = 0, |
| 1597 | }); |
| 1598 | self.header_dirty = true; |
| 1599 | self.load_commands_dirty = true; |
| 1600 | } |
| 1601 | if (self.stub_helper_section_index == null) { |
| 1602 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 1603 | self.stub_helper_section_index = @intCast(u16, text_segment.sections.items.len); |
| 1604 | |
| 1605 | const alignment: u2 = switch (self.base.options.target.cpu.arch) { |
| 1606 | .x86_64 => 0, |
| 1607 | .aarch64 => 2, |
| 1608 | else => unreachable, // unhandled architecture type |
| 1609 | }; |
| 1610 | const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; |
| 1611 | const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1612 | const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad); |
| 1613 | assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize); // TODO Must expand __TEXT segment. |
| 1614 | |
| 1615 | log.debug("found __stub_helper section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); |
| 1616 | |
| 1617 | try text_segment.addSection(self.base.allocator, .{ |
| 1618 | .sectname = makeStaticString("__stub_helper"), |
| 1619 | .segname = makeStaticString("__TEXT"), |
| 1620 | .addr = text_segment.inner.vmaddr + off, |
| 1621 | .size = needed_size, |
| 1622 | .offset = @intCast(u32, off), |
| 1623 | .@"align" = alignment, |
| 1624 | .reloff = 0, |
| 1625 | .nreloc = 0, |
| 1626 | .flags = flags, |
| 1627 | .reserved1 = 0, |
| 1628 | .reserved2 = 0, |
| 1629 | .reserved3 = 0, |
| 1630 | }); |
| 1631 | self.header_dirty = true; |
| 1632 | self.load_commands_dirty = true; |
| 1633 | } |
| 1634 | if (self.data_const_segment_cmd_index == null) { |
| 1635 | self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1636 | const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; |
| 1637 | const initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE; |
| 1638 | const address_and_offset = self.nextSegmentAddressAndOffset(); |
| 1639 | |
| 1640 | const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1641 | const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size); |
| 1642 | |
| 1643 | log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size }); |
| 1644 | |
| 1645 | try self.load_commands.append(self.base.allocator, .{ |
| 1646 | .Segment = SegmentCommand.empty(.{ |
| 1647 | .cmd = macho.LC_SEGMENT_64, |
| 1648 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 1649 | .segname = makeStaticString("__DATA_CONST"), |
| 1650 | .vmaddr = address_and_offset.address, |
| 1651 | .vmsize = needed_size, |
| 1652 | .fileoff = address_and_offset.offset, |
| 1653 | .filesize = needed_size, |
| 1654 | .maxprot = maxprot, |
| 1655 | .initprot = initprot, |
| 1656 | .nsects = 0, |
| 1657 | .flags = 0, |
| 1658 | }), |
| 1659 | }); |
| 1660 | self.header_dirty = true; |
| 1661 | self.load_commands_dirty = true; |
| 1662 | } |
| 1663 | if (self.data_got_section_index == null) { |
| 1664 | const dc_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 1665 | self.data_got_section_index = @intCast(u16, dc_segment.sections.items.len); |
| 1666 | |
| 1667 | const flags = macho.S_NON_LAZY_SYMBOL_POINTERS; |
| 1668 | const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1669 | const off = dc_segment.findFreeSpace(needed_size, @alignOf(u64), null); |
| 1670 | assert(off + needed_size <= dc_segment.inner.fileoff + dc_segment.inner.filesize); // TODO Must expand __DATA_CONST segment. |
| 1671 | |
| 1672 | log.debug("found __got section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); |
| 1673 | |
| 1674 | try dc_segment.addSection(self.base.allocator, .{ |
| 1675 | .sectname = makeStaticString("__got"), |
| 1676 | .segname = makeStaticString("__DATA_CONST"), |
| 1677 | .addr = dc_segment.inner.vmaddr + off - dc_segment.inner.fileoff, |
| 1678 | .size = needed_size, |
| 1679 | .offset = @intCast(u32, off), |
| 1680 | .@"align" = 3, // 2^3 = @sizeOf(u64) |
| 1681 | .reloff = 0, |
| 1682 | .nreloc = 0, |
| 1683 | .flags = flags, |
| 1684 | .reserved1 = 0, |
| 1685 | .reserved2 = 0, |
| 1686 | .reserved3 = 0, |
| 1687 | }); |
| 1688 | self.header_dirty = true; |
| 1689 | self.load_commands_dirty = true; |
| 1690 | } |
| 1691 | if (self.data_segment_cmd_index == null) { |
| 1692 | self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1693 | const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; |
| 1694 | const initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE; |
| 1695 | const address_and_offset = self.nextSegmentAddressAndOffset(); |
| 1696 | |
| 1697 | const ideal_size = 2 * @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1698 | const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size); |
| 1699 | |
| 1700 | log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size }); |
| 1701 | |
| 1702 | try self.load_commands.append(self.base.allocator, .{ |
| 1703 | .Segment = SegmentCommand.empty(.{ |
| 1704 | .cmd = macho.LC_SEGMENT_64, |
| 1705 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 1706 | .segname = makeStaticString("__DATA"), |
| 1707 | .vmaddr = address_and_offset.address, |
| 1708 | .vmsize = needed_size, |
| 1709 | .fileoff = address_and_offset.offset, |
| 1710 | .filesize = needed_size, |
| 1711 | .maxprot = maxprot, |
| 1712 | .initprot = initprot, |
| 1713 | .nsects = 0, |
| 1714 | .flags = 0, |
| 1715 | }), |
| 1716 | }); |
| 1717 | self.header_dirty = true; |
| 1718 | self.load_commands_dirty = true; |
| 1719 | } |
| 1720 | if (self.la_symbol_ptr_section_index == null) { |
| 1721 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 1722 | self.la_symbol_ptr_section_index = @intCast(u16, data_segment.sections.items.len); |
| 1723 | |
| 1724 | const flags = macho.S_LAZY_SYMBOL_POINTERS; |
| 1725 | const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1726 | const off = data_segment.findFreeSpace(needed_size, @alignOf(u64), null); |
| 1727 | assert(off + needed_size <= data_segment.inner.fileoff + data_segment.inner.filesize); // TODO Must expand __DATA segment. |
| 1728 | |
| 1729 | log.debug("found __la_symbol_ptr section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); |
| 1730 | |
| 1731 | try data_segment.addSection(self.base.allocator, .{ |
| 1732 | .sectname = makeStaticString("__la_symbol_ptr"), |
| 1733 | .segname = makeStaticString("__DATA"), |
| 1734 | .addr = data_segment.inner.vmaddr + off - data_segment.inner.fileoff, |
| 1735 | .size = needed_size, |
| 1736 | .offset = @intCast(u32, off), |
| 1737 | .@"align" = 3, // 2^3 = @sizeOf(u64) |
| 1738 | .reloff = 0, |
| 1739 | .nreloc = 0, |
| 1740 | .flags = flags, |
| 1741 | .reserved1 = 0, |
| 1742 | .reserved2 = 0, |
| 1743 | .reserved3 = 0, |
| 1744 | }); |
| 1745 | self.header_dirty = true; |
| 1746 | self.load_commands_dirty = true; |
| 1747 | } |
| 1748 | if (self.data_section_index == null) { |
| 1749 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 1750 | self.data_section_index = @intCast(u16, data_segment.sections.items.len); |
| 1751 | |
| 1752 | const flags = macho.S_REGULAR; |
| 1753 | const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1754 | const off = data_segment.findFreeSpace(needed_size, @alignOf(u64), null); |
| 1755 | assert(off + needed_size <= data_segment.inner.fileoff + data_segment.inner.filesize); // TODO Must expand __DATA segment. |
| 1756 | |
| 1757 | log.debug("found __data section free space 0x{x} to 0x{x}", .{ off, off + needed_size }); |
| 1758 | |
| 1759 | try data_segment.addSection(self.base.allocator, .{ |
| 1760 | .sectname = makeStaticString("__data"), |
| 1761 | .segname = makeStaticString("__DATA"), |
| 1762 | .addr = data_segment.inner.vmaddr + off - data_segment.inner.fileoff, |
| 1763 | .size = needed_size, |
| 1764 | .offset = @intCast(u32, off), |
| 1765 | .@"align" = 3, // 2^3 = @sizeOf(u64) |
| 1512 | 1766 | .reloff = 0, |
| 1513 | 1767 | .nreloc = 0, |
| 1514 | 1768 | .flags = flags, |
| ... | ... | @@ -1549,12 +1803,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1549 | 1803 | if (self.dyld_info_cmd_index == null) { |
| 1550 | 1804 | self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1551 | 1805 | |
| 1552 | | // TODO Preallocate rebase, binding, and lazy binding info. |
| 1553 | | const export_size = 2; |
| 1554 | | const export_off = self.findFreeSpaceLinkedit(export_size, 1); |
| 1555 | | |
| 1556 | | log.debug("found export info free space 0x{x} to 0x{x}", .{ export_off, export_off + export_size }); |
| 1557 | | |
| 1558 | 1806 | try self.load_commands.append(self.base.allocator, .{ |
| 1559 | 1807 | .DyldInfoOnly = .{ |
| 1560 | 1808 | .cmd = macho.LC_DYLD_INFO_ONLY, |
| ... | ... | @@ -1567,37 +1815,67 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1567 | 1815 | .weak_bind_size = 0, |
| 1568 | 1816 | .lazy_bind_off = 0, |
| 1569 | 1817 | .lazy_bind_size = 0, |
| 1570 | | .export_off = @intCast(u32, export_off), |
| 1571 | | .export_size = export_size, |
| 1818 | .export_off = 0, |
| 1819 | .export_size = 0, |
| 1572 | 1820 | }, |
| 1573 | 1821 | }); |
| 1822 | |
| 1823 | const dyld = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 1824 | |
| 1825 | // Preallocate rebase, binding, lazy binding info, and export info. |
| 1826 | const expected_size = 48; // TODO This is totally random. |
| 1827 | const rebase_off = self.findFreeSpaceLinkedit(expected_size, 1); |
| 1828 | log.debug("found rebase info free space 0x{x} to 0x{x}", .{ rebase_off, rebase_off + expected_size }); |
| 1829 | dyld.rebase_off = @intCast(u32, rebase_off); |
| 1830 | dyld.rebase_size = expected_size; |
| 1831 | |
| 1832 | const bind_off = self.findFreeSpaceLinkedit(expected_size, 1); |
| 1833 | log.debug("found binding info free space 0x{x} to 0x{x}", .{ bind_off, bind_off + expected_size }); |
| 1834 | dyld.bind_off = @intCast(u32, bind_off); |
| 1835 | dyld.bind_size = expected_size; |
| 1836 | |
| 1837 | const lazy_bind_off = self.findFreeSpaceLinkedit(expected_size, 1); |
| 1838 | log.debug("found lazy binding info free space 0x{x} to 0x{x}", .{ lazy_bind_off, lazy_bind_off + expected_size }); |
| 1839 | dyld.lazy_bind_off = @intCast(u32, lazy_bind_off); |
| 1840 | dyld.lazy_bind_size = expected_size; |
| 1841 | |
| 1842 | const export_off = self.findFreeSpaceLinkedit(expected_size, 1); |
| 1843 | log.debug("found export info free space 0x{x} to 0x{x}", .{ export_off, export_off + expected_size }); |
| 1844 | dyld.export_off = @intCast(u32, export_off); |
| 1845 | dyld.export_size = expected_size; |
| 1846 | |
| 1574 | 1847 | self.header_dirty = true; |
| 1575 | 1848 | self.load_commands_dirty = true; |
| 1576 | 1849 | } |
| 1577 | 1850 | if (self.symtab_cmd_index == null) { |
| 1578 | 1851 | self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1579 | 1852 | |
| 1853 | try self.load_commands.append(self.base.allocator, .{ |
| 1854 | .Symtab = .{ |
| 1855 | .cmd = macho.LC_SYMTAB, |
| 1856 | .cmdsize = @sizeOf(macho.symtab_command), |
| 1857 | .symoff = 0, |
| 1858 | .nsyms = 0, |
| 1859 | .stroff = 0, |
| 1860 | .strsize = 0, |
| 1861 | }, |
| 1862 | }); |
| 1863 | |
| 1864 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 1865 | |
| 1580 | 1866 | const symtab_size = self.base.options.symbol_count_hint * @sizeOf(macho.nlist_64); |
| 1581 | 1867 | const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64)); |
| 1582 | | |
| 1583 | 1868 | log.debug("found symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size }); |
| 1869 | symtab.symoff = @intCast(u32, symtab_off); |
| 1870 | symtab.nsyms = @intCast(u32, self.base.options.symbol_count_hint); |
| 1584 | 1871 | |
| 1585 | 1872 | try self.string_table.append(self.base.allocator, 0); // Need a null at position 0. |
| 1586 | 1873 | const strtab_size = self.string_table.items.len; |
| 1587 | 1874 | const strtab_off = self.findFreeSpaceLinkedit(strtab_size, 1); |
| 1588 | | |
| 1589 | 1875 | log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + strtab_size }); |
| 1876 | symtab.stroff = @intCast(u32, strtab_off); |
| 1877 | symtab.strsize = @intCast(u32, strtab_size); |
| 1590 | 1878 | |
| 1591 | | try self.load_commands.append(self.base.allocator, .{ |
| 1592 | | .Symtab = .{ |
| 1593 | | .cmd = macho.LC_SYMTAB, |
| 1594 | | .cmdsize = @sizeOf(macho.symtab_command), |
| 1595 | | .symoff = @intCast(u32, symtab_off), |
| 1596 | | .nsyms = @intCast(u32, self.base.options.symbol_count_hint), |
| 1597 | | .stroff = @intCast(u32, strtab_off), |
| 1598 | | .strsize = @intCast(u32, strtab_size), |
| 1599 | | }, |
| 1600 | | }); |
| 1601 | 1879 | self.header_dirty = true; |
| 1602 | 1880 | self.load_commands_dirty = true; |
| 1603 | 1881 | self.string_table_dirty = true; |
| ... | ... | @@ -1605,7 +1883,11 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1605 | 1883 | if (self.dysymtab_cmd_index == null) { |
| 1606 | 1884 | self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 1607 | 1885 | |
| 1608 | | // TODO Preallocate space for indirect symbol table. |
| 1886 | // Preallocate space for indirect symbol table. |
| 1887 | const indsymtab_size = self.base.options.symbol_count_hint * @sizeOf(u64); // Each entry is just a u64. |
| 1888 | const indsymtab_off = self.findFreeSpaceLinkedit(indsymtab_size, @sizeOf(u64)); |
| 1889 | |
| 1890 | log.debug("found indirect symbol table free space 0x{x} to 0x{x}", .{ indsymtab_off, indsymtab_off + indsymtab_size }); |
| 1609 | 1891 | |
| 1610 | 1892 | try self.load_commands.append(self.base.allocator, .{ |
| 1611 | 1893 | .Dysymtab = .{ |
| ... | ... | @@ -1623,8 +1905,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1623 | 1905 | .nmodtab = 0, |
| 1624 | 1906 | .extrefsymoff = 0, |
| 1625 | 1907 | .nextrefsyms = 0, |
| 1626 | | .indirectsymoff = 0, |
| 1627 | | .nindirectsyms = 0, |
| 1908 | .indirectsymoff = @intCast(u32, indsymtab_off), |
| 1909 | .nindirectsyms = @intCast(u32, self.base.options.symbol_count_hint), |
| 1628 | 1910 | .extreloff = 0, |
| 1629 | 1911 | .nextrel = 0, |
| 1630 | 1912 | .locreloff = 0, |
| ... | ... | @@ -1752,16 +2034,73 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1752 | 2034 | self.header_dirty = true; |
| 1753 | 2035 | self.load_commands_dirty = true; |
| 1754 | 2036 | } |
| 1755 | | if (self.dyld_stub_binder_index == null) { |
| 1756 | | self.dyld_stub_binder_index = @intCast(u16, self.undef_symbols.items.len); |
| 1757 | | const name = try self.makeString("dyld_stub_binder"); |
| 1758 | | try self.undef_symbols.append(self.base.allocator, .{ |
| 1759 | | .n_strx = name, |
| 1760 | | .n_type = macho.N_UNDF | macho.N_EXT, |
| 1761 | | .n_sect = 0, |
| 1762 | | .n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER, |
| 1763 | | .n_value = 0, |
| 2037 | if (!self.extern_nonlazy_symbols.contains("dyld_stub_binder")) { |
| 2038 | const index = @intCast(u32, self.extern_nonlazy_symbols.items().len); |
| 2039 | const name = try std.fmt.allocPrint(self.base.allocator, "dyld_stub_binder", .{}); |
| 2040 | try self.extern_nonlazy_symbols.putNoClobber(self.base.allocator, name, .{ |
| 2041 | .name = name, |
| 2042 | .dylib_ordinal = 1, // TODO this is currently hardcoded. |
| 2043 | .segment = self.data_const_segment_cmd_index.?, |
| 2044 | .offset = index * @sizeOf(u64), |
| 1764 | 2045 | }); |
| 2046 | self.binding_info_dirty = true; |
| 2047 | } |
| 2048 | if (self.stub_helper_stubs_start_off == null) { |
| 2049 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2050 | const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?]; |
| 2051 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 2052 | const data = &data_segment.sections.items[self.data_section_index.?]; |
| 2053 | const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 2054 | const got = &data_const_segment.sections.items[self.data_got_section_index.?]; |
| 2055 | switch (self.base.options.target.cpu.arch) { |
| 2056 | .x86_64 => { |
| 2057 | const code_size = 15; |
| 2058 | var code: [code_size]u8 = undefined; |
| 2059 | // lea %r11, [rip + disp] |
| 2060 | code[0] = 0x4c; |
| 2061 | code[1] = 0x8d; |
| 2062 | code[2] = 0x1d; |
| 2063 | { |
| 2064 | const displacement = @intCast(u32, data.addr - stub_helper.addr - 7); |
| 2065 | mem.writeIntLittle(u32, code[3..7], displacement); |
| 2066 | } |
| 2067 | // push %r11 |
| 2068 | code[7] = 0x41; |
| 2069 | code[8] = 0x53; |
| 2070 | // jmp [rip + disp] |
| 2071 | code[9] = 0xff; |
| 2072 | code[10] = 0x25; |
| 2073 | { |
| 2074 | const displacement = @intCast(u32, got.addr - stub_helper.addr - code_size); |
| 2075 | mem.writeIntLittle(u32, code[11..], displacement); |
| 2076 | } |
| 2077 | self.stub_helper_stubs_start_off = stub_helper.offset + code_size; |
| 2078 | try self.base.file.?.pwriteAll(&code, stub_helper.offset); |
| 2079 | }, |
| 2080 | .aarch64 => { |
| 2081 | var code: [4 * @sizeOf(u32)]u8 = undefined; |
| 2082 | { |
| 2083 | const displacement = data.addr - stub_helper.addr; |
| 2084 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, @intCast(i21, displacement)).toU32()); |
| 2085 | } |
| 2086 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.stp( |
| 2087 | .x16, |
| 2088 | .x17, |
| 2089 | aarch64.Register.sp, |
| 2090 | aarch64.Instruction.LoadStorePairOffset.pre_index(-16), |
| 2091 | ).toU32()); |
| 2092 | { |
| 2093 | const displacement = got.addr - stub_helper.addr - 2 * @sizeOf(u32); |
| 2094 | mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.ldr(.x16, .{ |
| 2095 | .literal = @intCast(u19, displacement / 4), |
| 2096 | }).toU32()); |
| 2097 | } |
| 2098 | mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.br(.x16).toU32()); |
| 2099 | self.stub_helper_stubs_start_off = stub_helper.offset + 4 * @sizeOf(u32); |
| 2100 | try self.base.file.?.pwriteAll(&code, stub_helper.offset); |
| 2101 | }, |
| 2102 | else => unreachable, |
| 2103 | } |
| 1765 | 2104 | } |
| 1766 | 2105 | } |
| 1767 | 2106 | |
| ... | ... | @@ -1877,7 +2216,7 @@ pub fn makeStaticString(comptime bytes: []const u8) [16]u8 { |
| 1877 | 2216 | return buf; |
| 1878 | 2217 | } |
| 1879 | 2218 | |
| 1880 | | fn makeString(self: *MachO, bytes: []const u8) !u32 { |
| 2219 | pub fn makeString(self: *MachO, bytes: []const u8) !u32 { |
| 1881 | 2220 | try self.string_table.ensureCapacity(self.base.allocator, self.string_table.items.len + bytes.len + 1); |
| 1882 | 2221 | const result = @intCast(u32, self.string_table.items.len); |
| 1883 | 2222 | self.string_table.appendSliceAssumeCapacity(bytes); |
| ... | ... | @@ -1907,16 +2246,13 @@ const NextSegmentAddressAndOffset = struct { |
| 1907 | 2246 | }; |
| 1908 | 2247 | |
| 1909 | 2248 | fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset { |
| 1910 | | const prev_segment_idx = blk: { |
| 1911 | | if (self.data_segment_cmd_index) |idx| { |
| 1912 | | break :blk idx; |
| 1913 | | } else if (self.text_segment_cmd_index) |idx| { |
| 1914 | | break :blk idx; |
| 1915 | | } else { |
| 1916 | | unreachable; // unhandled LC_SEGMENT_64 load command before __TEXT |
| 2249 | var prev_segment_idx: ?usize = null; // We use optional here for safety. |
| 2250 | for (self.load_commands.items) |cmd, i| { |
| 2251 | if (cmd == .Segment) { |
| 2252 | prev_segment_idx = i; |
| 1917 | 2253 | } |
| 1918 | | }; |
| 1919 | | const prev_segment = self.load_commands.items[prev_segment_idx].Segment; |
| 2254 | } |
| 2255 | const prev_segment = self.load_commands.items[prev_segment_idx.?].Segment; |
| 1920 | 2256 | const address = prev_segment.inner.vmaddr + prev_segment.inner.vmsize; |
| 1921 | 2257 | const offset = prev_segment.inner.fileoff + prev_segment.inner.filesize; |
| 1922 | 2258 | return .{ |
| ... | ... | @@ -2100,11 +2436,98 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 2100 | 2436 | try self.base.file.?.pwriteAll(&code, off); |
| 2101 | 2437 | } |
| 2102 | 2438 | |
| 2439 | fn writeLazySymbolPointer(self: *MachO, index: u32) !void { |
| 2440 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2441 | const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?]; |
| 2442 | const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 2443 | const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?]; |
| 2444 | |
| 2445 | const stub_size: u4 = switch (self.base.options.target.cpu.arch) { |
| 2446 | .x86_64 => 10, |
| 2447 | .aarch64 => 3 * @sizeOf(u32), |
| 2448 | else => unreachable, |
| 2449 | }; |
| 2450 | const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size; |
| 2451 | const end = stub_helper.addr + stub_off - stub_helper.offset; |
| 2452 | var buf: [@sizeOf(u64)]u8 = undefined; |
| 2453 | mem.writeIntLittle(u64, &buf, end); |
| 2454 | const off = la_symbol_ptr.offset + index * @sizeOf(u64); |
| 2455 | log.debug("writing lazy symbol pointer entry 0x{x} at 0x{x}", .{ end, off }); |
| 2456 | try self.base.file.?.pwriteAll(&buf, off); |
| 2457 | } |
| 2458 | |
| 2459 | fn writeStub(self: *MachO, index: u32) !void { |
| 2460 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2461 | const stubs = text_segment.sections.items[self.stubs_section_index.?]; |
| 2462 | const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 2463 | const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?]; |
| 2464 | |
| 2465 | const stub_off = stubs.offset + index * stubs.reserved2; |
| 2466 | const stub_addr = stubs.addr + index * stubs.reserved2; |
| 2467 | const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64); |
| 2468 | log.debug("writing stub at 0x{x}", .{stub_off}); |
| 2469 | var code = try self.base.allocator.alloc(u8, stubs.reserved2); |
| 2470 | defer self.base.allocator.free(code); |
| 2471 | switch (self.base.options.target.cpu.arch) { |
| 2472 | .x86_64 => { |
| 2473 | const displacement = @intCast(u32, la_ptr_addr - stub_addr - stubs.reserved2); |
| 2474 | // jmp |
| 2475 | code[0] = 0xff; |
| 2476 | code[1] = 0x25; |
| 2477 | mem.writeIntLittle(u32, code[2..][0..4], displacement); |
| 2478 | }, |
| 2479 | .aarch64 => { |
| 2480 | const displacement = la_ptr_addr - stub_addr; |
| 2481 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{ |
| 2482 | .literal = @intCast(u19, displacement / 4), |
| 2483 | }).toU32()); |
| 2484 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.br(.x16).toU32()); |
| 2485 | }, |
| 2486 | else => unreachable, |
| 2487 | } |
| 2488 | try self.base.file.?.pwriteAll(code, stub_off); |
| 2489 | } |
| 2490 | |
| 2491 | fn writeStubInStubHelper(self: *MachO, index: u32) !void { |
| 2492 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2493 | const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?]; |
| 2494 | |
| 2495 | const stub_size: u4 = switch (self.base.options.target.cpu.arch) { |
| 2496 | .x86_64 => 10, |
| 2497 | .aarch64 => 3 * @sizeOf(u32), |
| 2498 | else => unreachable, |
| 2499 | }; |
| 2500 | const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size; |
| 2501 | var code = try self.base.allocator.alloc(u8, stub_size); |
| 2502 | defer self.base.allocator.free(code); |
| 2503 | switch (self.base.options.target.cpu.arch) { |
| 2504 | .x86_64 => { |
| 2505 | const displacement = @intCast(i32, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - stub_size); |
| 2506 | // pushq |
| 2507 | code[0] = 0x68; |
| 2508 | mem.writeIntLittle(u32, code[1..][0..4], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. |
| 2509 | // jmpq |
| 2510 | code[5] = 0xe9; |
| 2511 | mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement)); |
| 2512 | }, |
| 2513 | .aarch64 => { |
| 2514 | const displacement = @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4; |
| 2515 | mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{ |
| 2516 | .literal = 0x2, |
| 2517 | }).toU32()); |
| 2518 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(@intCast(i28, displacement)).toU32()); |
| 2519 | mem.writeIntLittle(u32, code[8..12], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. |
| 2520 | }, |
| 2521 | else => unreachable, |
| 2522 | } |
| 2523 | try self.base.file.?.pwriteAll(code, stub_off); |
| 2524 | } |
| 2525 | |
| 2103 | 2526 | fn relocateSymbolTable(self: *MachO) !void { |
| 2104 | 2527 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 2105 | 2528 | const nlocals = self.local_symbols.items.len; |
| 2106 | 2529 | const nglobals = self.global_symbols.items.len; |
| 2107 | | const nundefs = self.undef_symbols.items.len; |
| 2530 | const nundefs = self.extern_lazy_symbols.items().len + self.extern_nonlazy_symbols.items().len; |
| 2108 | 2531 | const nsyms = nlocals + nglobals + nundefs; |
| 2109 | 2532 | |
| 2110 | 2533 | if (symtab.nsyms < nsyms) { |
| ... | ... | @@ -2149,7 +2572,31 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { |
| 2149 | 2572 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 2150 | 2573 | const nlocals = self.local_symbols.items.len; |
| 2151 | 2574 | const nglobals = self.global_symbols.items.len; |
| 2152 | | const nundefs = self.undef_symbols.items.len; |
| 2575 | |
| 2576 | const nundefs = self.extern_lazy_symbols.items().len + self.extern_nonlazy_symbols.items().len; |
| 2577 | var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator); |
| 2578 | defer undefs.deinit(); |
| 2579 | try undefs.ensureCapacity(nundefs); |
| 2580 | for (self.extern_lazy_symbols.items()) |entry| { |
| 2581 | const name = try self.makeString(entry.key); |
| 2582 | undefs.appendAssumeCapacity(.{ |
| 2583 | .n_strx = name, |
| 2584 | .n_type = std.macho.N_UNDF | std.macho.N_EXT, |
| 2585 | .n_sect = 0, |
| 2586 | .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER, |
| 2587 | .n_value = 0, |
| 2588 | }); |
| 2589 | } |
| 2590 | for (self.extern_nonlazy_symbols.items()) |entry| { |
| 2591 | const name = try self.makeString(entry.key); |
| 2592 | undefs.appendAssumeCapacity(.{ |
| 2593 | .n_strx = name, |
| 2594 | .n_type = std.macho.N_UNDF | std.macho.N_EXT, |
| 2595 | .n_sect = 0, |
| 2596 | .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER, |
| 2597 | .n_value = 0, |
| 2598 | }); |
| 2599 | } |
| 2153 | 2600 | |
| 2154 | 2601 | const locals_off = symtab.symoff; |
| 2155 | 2602 | const locals_size = nlocals * @sizeOf(macho.nlist_64); |
| ... | ... | @@ -2161,8 +2608,8 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { |
| 2161 | 2608 | |
| 2162 | 2609 | const undefs_off = globals_off + globals_size; |
| 2163 | 2610 | const undefs_size = nundefs * @sizeOf(macho.nlist_64); |
| 2164 | | log.debug("writing undef symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off }); |
| 2165 | | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undef_symbols.items), undefs_off); |
| 2611 | log.debug("writing extern symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off }); |
| 2612 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off); |
| 2166 | 2613 | |
| 2167 | 2614 | // Update dynamic symbol table. |
| 2168 | 2615 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; |
| ... | ... | @@ -2174,6 +2621,49 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { |
| 2174 | 2621 | self.load_commands_dirty = true; |
| 2175 | 2622 | } |
| 2176 | 2623 | |
| 2624 | fn writeIndirectSymbolTable(self: *MachO) !void { |
| 2625 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 2626 | const stubs = &text_segment.sections.items[self.stubs_section_index.?]; |
| 2627 | const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 2628 | const got = &data_const_seg.sections.items[self.data_got_section_index.?]; |
| 2629 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 2630 | const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?]; |
| 2631 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; |
| 2632 | dysymtab.nindirectsyms = 0; |
| 2633 | // TODO check if we have allocated enough size. |
| 2634 | |
| 2635 | var buf: [@sizeOf(u32)]u8 = undefined; |
| 2636 | var off = dysymtab.indirectsymoff; |
| 2637 | |
| 2638 | stubs.reserved1 = 0; |
| 2639 | for (self.extern_lazy_symbols.items()) |_, i| { |
| 2640 | const symtab_idx = @intCast(u32, dysymtab.iundefsym + i); |
| 2641 | mem.writeIntLittle(u32, &buf, symtab_idx); |
| 2642 | try self.base.file.?.pwriteAll(&buf, off); |
| 2643 | off += @sizeOf(u32); |
| 2644 | dysymtab.nindirectsyms += 1; |
| 2645 | } |
| 2646 | |
| 2647 | const base_id = @intCast(u32, self.extern_lazy_symbols.items().len); |
| 2648 | got.reserved1 = base_id; |
| 2649 | for (self.extern_nonlazy_symbols.items()) |_, i| { |
| 2650 | const symtab_idx = @intCast(u32, dysymtab.iundefsym + i + base_id); |
| 2651 | mem.writeIntLittle(u32, &buf, symtab_idx); |
| 2652 | try self.base.file.?.pwriteAll(&buf, off); |
| 2653 | off += @sizeOf(u32); |
| 2654 | dysymtab.nindirectsyms += 1; |
| 2655 | } |
| 2656 | |
| 2657 | la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, self.extern_nonlazy_symbols.items().len); |
| 2658 | for (self.extern_lazy_symbols.items()) |_, i| { |
| 2659 | const symtab_idx = @intCast(u32, dysymtab.iundefsym + i); |
| 2660 | mem.writeIntLittle(u32, &buf, symtab_idx); |
| 2661 | try self.base.file.?.pwriteAll(&buf, off); |
| 2662 | off += @sizeOf(u32); |
| 2663 | dysymtab.nindirectsyms += 1; |
| 2664 | } |
| 2665 | } |
| 2666 | |
| 2177 | 2667 | fn writeCodeSignaturePadding(self: *MachO) !void { |
| 2178 | 2668 | const tracy = trace(@src()); |
| 2179 | 2669 | defer tracy.end(); |
| ... | ... | @@ -2272,18 +2762,63 @@ fn writeExportTrie(self: *MachO) !void { |
| 2272 | 2762 | self.export_info_dirty = false; |
| 2273 | 2763 | } |
| 2274 | 2764 | |
| 2765 | fn writeRebaseInfoTable(self: *MachO) !void { |
| 2766 | if (!self.rebase_info_dirty) return; |
| 2767 | |
| 2768 | const tracy = trace(@src()); |
| 2769 | defer tracy.end(); |
| 2770 | |
| 2771 | var symbols = try self.base.allocator.alloc(*const ExternSymbol, self.extern_lazy_symbols.items().len); |
| 2772 | defer self.base.allocator.free(symbols); |
| 2773 | |
| 2774 | for (self.extern_lazy_symbols.items()) |*entry, i| { |
| 2775 | symbols[i] = &entry.value; |
| 2776 | } |
| 2777 | |
| 2778 | const size = try rebaseInfoSize(symbols); |
| 2779 | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); |
| 2780 | defer self.base.allocator.free(buffer); |
| 2781 | |
| 2782 | var stream = std.io.fixedBufferStream(buffer); |
| 2783 | try writeRebaseInfo(symbols, stream.writer()); |
| 2784 | |
| 2785 | const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 2786 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 2787 | const allocated_size = self.allocatedSizeLinkedit(dyld_info.rebase_off); |
| 2788 | const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)); |
| 2789 | |
| 2790 | if (needed_size > allocated_size) { |
| 2791 | dyld_info.rebase_off = 0; |
| 2792 | dyld_info.rebase_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1)); |
| 2793 | } |
| 2794 | |
| 2795 | dyld_info.rebase_size = @intCast(u32, needed_size); |
| 2796 | log.debug("writing rebase info from 0x{x} to 0x{x}", .{ dyld_info.rebase_off, dyld_info.rebase_off + dyld_info.rebase_size }); |
| 2797 | |
| 2798 | try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off); |
| 2799 | self.load_commands_dirty = true; |
| 2800 | self.rebase_info_dirty = false; |
| 2801 | } |
| 2802 | |
| 2275 | 2803 | fn writeBindingInfoTable(self: *MachO) !void { |
| 2276 | 2804 | if (!self.binding_info_dirty) return; |
| 2277 | 2805 | |
| 2278 | 2806 | const tracy = trace(@src()); |
| 2279 | 2807 | defer tracy.end(); |
| 2280 | 2808 | |
| 2281 | | const size = try self.binding_info_table.calcSize(); |
| 2809 | var symbols = try self.base.allocator.alloc(*const ExternSymbol, self.extern_nonlazy_symbols.items().len); |
| 2810 | defer self.base.allocator.free(symbols); |
| 2811 | |
| 2812 | for (self.extern_nonlazy_symbols.items()) |*entry, i| { |
| 2813 | symbols[i] = &entry.value; |
| 2814 | } |
| 2815 | |
| 2816 | const size = try bindInfoSize(symbols); |
| 2282 | 2817 | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); |
| 2283 | 2818 | defer self.base.allocator.free(buffer); |
| 2284 | 2819 | |
| 2285 | 2820 | var stream = std.io.fixedBufferStream(buffer); |
| 2286 | | try self.binding_info_table.write(stream.writer()); |
| 2821 | try writeBindInfo(symbols, stream.writer()); |
| 2287 | 2822 | |
| 2288 | 2823 | const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 2289 | 2824 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| ... | ... | @@ -2306,12 +2841,19 @@ fn writeBindingInfoTable(self: *MachO) !void { |
| 2306 | 2841 | fn writeLazyBindingInfoTable(self: *MachO) !void { |
| 2307 | 2842 | if (!self.lazy_binding_info_dirty) return; |
| 2308 | 2843 | |
| 2309 | | const size = try self.lazy_binding_info_table.calcSize(); |
| 2844 | var symbols = try self.base.allocator.alloc(*const ExternSymbol, self.extern_lazy_symbols.items().len); |
| 2845 | defer self.base.allocator.free(symbols); |
| 2846 | |
| 2847 | for (self.extern_lazy_symbols.items()) |*entry, i| { |
| 2848 | symbols[i] = &entry.value; |
| 2849 | } |
| 2850 | |
| 2851 | const size = try lazyBindInfoSize(symbols); |
| 2310 | 2852 | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); |
| 2311 | 2853 | defer self.base.allocator.free(buffer); |
| 2312 | 2854 | |
| 2313 | 2855 | var stream = std.io.fixedBufferStream(buffer); |
| 2314 | | try self.lazy_binding_info_table.write(stream.writer()); |
| 2856 | try writeLazyBindInfo(symbols, stream.writer()); |
| 2315 | 2857 | |
| 2316 | 2858 | const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 2317 | 2859 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| ... | ... | @@ -2327,10 +2869,78 @@ fn writeLazyBindingInfoTable(self: *MachO) !void { |
| 2327 | 2869 | 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 }); |
| 2328 | 2870 | |
| 2329 | 2871 | try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off); |
| 2872 | try self.populateLazyBindOffsetsInStubHelper(buffer); |
| 2330 | 2873 | self.load_commands_dirty = true; |
| 2331 | 2874 | self.lazy_binding_info_dirty = false; |
| 2332 | 2875 | } |
| 2333 | 2876 | |
| 2877 | fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { |
| 2878 | if (self.extern_lazy_symbols.items().len == 0) return; |
| 2879 | |
| 2880 | var stream = std.io.fixedBufferStream(buffer); |
| 2881 | var reader = stream.reader(); |
| 2882 | var offsets = std.ArrayList(u32).init(self.base.allocator); |
| 2883 | try offsets.append(0); |
| 2884 | defer offsets.deinit(); |
| 2885 | var valid_block = false; |
| 2886 | |
| 2887 | while (true) { |
| 2888 | const inst = reader.readByte() catch |err| switch (err) { |
| 2889 | error.EndOfStream => break, |
| 2890 | else => return err, |
| 2891 | }; |
| 2892 | const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK; |
| 2893 | const opcode: u8 = inst & macho.BIND_OPCODE_MASK; |
| 2894 | |
| 2895 | switch (opcode) { |
| 2896 | macho.BIND_OPCODE_DO_BIND => { |
| 2897 | valid_block = true; |
| 2898 | }, |
| 2899 | macho.BIND_OPCODE_DONE => { |
| 2900 | if (valid_block) { |
| 2901 | const offset = try stream.getPos(); |
| 2902 | try offsets.append(@intCast(u32, offset)); |
| 2903 | } |
| 2904 | valid_block = false; |
| 2905 | }, |
| 2906 | macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => { |
| 2907 | var next = try reader.readByte(); |
| 2908 | while (next != @as(u8, 0)) { |
| 2909 | next = try reader.readByte(); |
| 2910 | } |
| 2911 | }, |
| 2912 | macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => { |
| 2913 | _ = try std.leb.readULEB128(u64, reader); |
| 2914 | }, |
| 2915 | macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => { |
| 2916 | _ = try std.leb.readULEB128(u64, reader); |
| 2917 | }, |
| 2918 | macho.BIND_OPCODE_SET_ADDEND_SLEB => { |
| 2919 | _ = try std.leb.readILEB128(i64, reader); |
| 2920 | }, |
| 2921 | else => {}, |
| 2922 | } |
| 2923 | } |
| 2924 | assert(self.extern_lazy_symbols.items().len <= offsets.items.len); |
| 2925 | |
| 2926 | const stub_size: u4 = switch (self.base.options.target.cpu.arch) { |
| 2927 | .x86_64 => 10, |
| 2928 | .aarch64 => 3 * @sizeOf(u32), |
| 2929 | else => unreachable, |
| 2930 | }; |
| 2931 | const off: u4 = switch (self.base.options.target.cpu.arch) { |
| 2932 | .x86_64 => 1, |
| 2933 | .aarch64 => 2 * @sizeOf(u32), |
| 2934 | else => unreachable, |
| 2935 | }; |
| 2936 | var buf: [@sizeOf(u32)]u8 = undefined; |
| 2937 | for (self.extern_lazy_symbols.items()) |_, i| { |
| 2938 | const placeholder_off = self.stub_helper_stubs_start_off.? + i * stub_size + off; |
| 2939 | mem.writeIntLittle(u32, &buf, offsets.items[i]); |
| 2940 | try self.base.file.?.pwriteAll(&buf, placeholder_off); |
| 2941 | } |
| 2942 | } |
| 2943 | |
| 2334 | 2944 | fn writeStringTable(self: *MachO) !void { |
| 2335 | 2945 | if (!self.string_table_dirty) return; |
| 2336 | 2946 | |
| ... | ... | @@ -2438,8 +3048,6 @@ fn writeHeader(self: *MachO) !void { |
| 2438 | 3048 | } |
| 2439 | 3049 | |
| 2440 | 3050 | /// Parse MachO contents from existing binary file. |
| 2441 | | /// TODO This method is incomplete and currently parses only the header |
| 2442 | | /// plus the load commands. |
| 2443 | 3051 | fn parseFromFile(self: *MachO, file: fs.File) !void { |
| 2444 | 3052 | self.base.file = file; |
| 2445 | 3053 | var reader = file.reader(); |
| ... | ... | @@ -2464,6 +3072,8 @@ fn parseFromFile(self: *MachO, file: fs.File) !void { |
| 2464 | 3072 | } |
| 2465 | 3073 | } else if (parseAndCmpName(&x.inner.segname, "__DATA")) { |
| 2466 | 3074 | self.data_segment_cmd_index = i; |
| 3075 | } else if (parseAndCmpName(&x.inner.segname, "__DATA_CONST")) { |
| 3076 | self.data_const_segment_cmd_index = i; |
| 2467 | 3077 | } |
| 2468 | 3078 | }, |
| 2469 | 3079 | macho.LC_DYLD_INFO_ONLY => { |
| ... | ... | @@ -2549,24 +3159,61 @@ fn parseStringTable(self: *MachO) !void { |
| 2549 | 3159 | self.string_table.appendSliceAssumeCapacity(buffer); |
| 2550 | 3160 | } |
| 2551 | 3161 | |
| 2552 | | fn parseBindingInfoTable(self: *MachO) !void { |
| 3162 | fn fixupBindInfo(self: *MachO, dylib_ordinal: u32) !void { |
| 2553 | 3163 | const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 2554 | 3164 | var buffer = try self.base.allocator.alloc(u8, dyld_info.bind_size); |
| 2555 | 3165 | defer self.base.allocator.free(buffer); |
| 2556 | 3166 | const nread = try self.base.file.?.preadAll(buffer, dyld_info.bind_off); |
| 2557 | 3167 | assert(nread == buffer.len); |
| 2558 | | |
| 2559 | | var stream = std.io.fixedBufferStream(buffer); |
| 2560 | | try self.binding_info_table.read(stream.reader(), self.base.allocator); |
| 3168 | try self.fixupInfoCommon(buffer, dylib_ordinal); |
| 3169 | try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off); |
| 2561 | 3170 | } |
| 2562 | 3171 | |
| 2563 | | fn parseLazyBindingInfoTable(self: *MachO) !void { |
| 3172 | fn fixupLazyBindInfo(self: *MachO, dylib_ordinal: u32) !void { |
| 2564 | 3173 | const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 2565 | 3174 | var buffer = try self.base.allocator.alloc(u8, dyld_info.lazy_bind_size); |
| 2566 | 3175 | defer self.base.allocator.free(buffer); |
| 2567 | 3176 | const nread = try self.base.file.?.preadAll(buffer, dyld_info.lazy_bind_off); |
| 2568 | 3177 | assert(nread == buffer.len); |
| 3178 | try self.fixupInfoCommon(buffer, dylib_ordinal); |
| 3179 | try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off); |
| 3180 | } |
| 2569 | 3181 | |
| 3182 | fn fixupInfoCommon(self: *MachO, buffer: []u8, dylib_ordinal: u32) !void { |
| 2570 | 3183 | var stream = std.io.fixedBufferStream(buffer); |
| 2571 | | try self.lazy_binding_info_table.read(stream.reader(), self.base.allocator); |
| 3184 | var reader = stream.reader(); |
| 3185 | |
| 3186 | while (true) { |
| 3187 | const inst = reader.readByte() catch |err| switch (err) { |
| 3188 | error.EndOfStream => break, |
| 3189 | else => return err, |
| 3190 | }; |
| 3191 | const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK; |
| 3192 | const opcode: u8 = inst & macho.BIND_OPCODE_MASK; |
| 3193 | |
| 3194 | switch (opcode) { |
| 3195 | macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => { |
| 3196 | var next = try reader.readByte(); |
| 3197 | while (next != @as(u8, 0)) { |
| 3198 | next = try reader.readByte(); |
| 3199 | } |
| 3200 | }, |
| 3201 | macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => { |
| 3202 | _ = try std.leb.readULEB128(u64, reader); |
| 3203 | }, |
| 3204 | macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => { |
| 3205 | // Perform the fixup. |
| 3206 | try stream.seekBy(-1); |
| 3207 | var writer = stream.writer(); |
| 3208 | try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, dylib_ordinal)); |
| 3209 | }, |
| 3210 | macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => { |
| 3211 | _ = try std.leb.readULEB128(u64, reader); |
| 3212 | }, |
| 3213 | macho.BIND_OPCODE_SET_ADDEND_SLEB => { |
| 3214 | _ = try std.leb.readILEB128(i64, reader); |
| 3215 | }, |
| 3216 | else => {}, |
| 3217 | } |
| 3218 | } |
| 2572 | 3219 | } |