| ... | @@ -52,6 +52,8 @@ load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, | ... | @@ -52,6 +52,8 @@ load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| 52 | pagezero_segment_cmd_index: ?u16 = null, | 52 | pagezero_segment_cmd_index: ?u16 = null, |
| 53 | /// __TEXT segment | 53 | /// __TEXT segment |
| 54 | text_segment_cmd_index: ?u16 = null, | 54 | text_segment_cmd_index: ?u16 = null, |
| | 55 | /// __DATA_CONST segment |
| | 56 | data_const_segment_cmd_index: ?u16 = null, |
| 55 | /// __DATA segment | 57 | /// __DATA segment |
| 56 | data_segment_cmd_index: ?u16 = null, | 58 | data_segment_cmd_index: ?u16 = null, |
| 57 | /// __LINKEDIT segment | 59 | /// __LINKEDIT segment |
| ... | @@ -87,22 +89,34 @@ code_signature_cmd_index: ?u16 = null, | ... | @@ -87,22 +89,34 @@ code_signature_cmd_index: ?u16 = null, |
| 87 | text_section_index: ?u16 = null, | 89 | text_section_index: ?u16 = null, |
| 88 | /// Index into __TEXT,__ziggot section. | 90 | /// Index into __TEXT,__ziggot section. |
| 89 | got_section_index: ?u16 = null, | 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 | /// The absolute address of the entry point. | 102 | /// The absolute address of the entry point. |
| 91 | entry_addr: ?u64 = null, | 103 | entry_addr: ?u64 = null, |
| 92 | | 104 | |
| 93 | /// Table of all local symbols | 105 | /// Table of all local symbols |
| 94 | /// Internally references string table for names (which are optional). | 106 | /// Internally references string table for names (which are optional). |
| 95 | local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | 107 | local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 96 | /// Table of all defined global symbols | 108 | /// Table of all global symbols |
| 97 | global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | 109 | global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 98 | /// Table of all undefined symbols | 110 | /// Table of all extern nonlazy symbols, indexed by name. |
| 99 | undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | 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 | local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, | 115 | local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 102 | global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, | 116 | global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 103 | offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, | 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 | /// Table of symbol names aka the string table. | 121 | /// Table of symbol names aka the string table. |
| 108 | string_table: std.ArrayListUnmanaged(u8) = .{}, | 122 | string_table: std.ArrayListUnmanaged(u8) = .{}, |
| ... | @@ -110,16 +124,12 @@ string_table: std.ArrayListUnmanaged(u8) = .{}, | ... | @@ -110,16 +124,12 @@ string_table: std.ArrayListUnmanaged(u8) = .{}, |
| 110 | /// Table of trampolines to the actual symbols in __text section. | 124 | /// Table of trampolines to the actual symbols in __text section. |
| 111 | offset_table: std.ArrayListUnmanaged(u64) = .{}, | 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 | error_flags: File.ErrorFlags = File.ErrorFlags{}, | 127 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 119 | | 128 | |
| 120 | offset_table_count_dirty: bool = false, | 129 | offset_table_count_dirty: bool = false, |
| 121 | header_dirty: bool = false, | 130 | header_dirty: bool = false, |
| 122 | load_commands_dirty: bool = false, | 131 | load_commands_dirty: bool = false, |
| | 132 | rebase_info_dirty: bool = false, |
| 123 | binding_info_dirty: bool = false, | 133 | binding_info_dirty: bool = false, |
| 124 | lazy_binding_info_dirty: bool = false, | 134 | lazy_binding_info_dirty: bool = false, |
| 125 | export_info_dirty: bool = false, | 135 | export_info_dirty: bool = false, |
| ... | @@ -149,6 +159,12 @@ last_text_block: ?*TextBlock = null, | ... | @@ -149,6 +159,12 @@ last_text_block: ?*TextBlock = null, |
| 149 | /// prior to calling `generateSymbol`, and then immediately deallocated | 159 | /// prior to calling `generateSymbol`, and then immediately deallocated |
| 150 | /// rather than sitting in the global scope. | 160 | /// rather than sitting in the global scope. |
| 151 | pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{}, | 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 | pub const PieFixup = struct { | 169 | pub const PieFixup = struct { |
| 154 | /// Target address we wanted to address in absolute terms. | 170 | /// Target address we wanted to address in absolute terms. |
| ... | @@ -160,6 +176,19 @@ pub const PieFixup = struct { | ... | @@ -160,6 +176,19 @@ pub const PieFixup = struct { |
| 160 | len: usize, | 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 | /// `alloc_num / alloc_den` is the factor of padding when allocating. | 192 | /// `alloc_num / alloc_den` is the factor of padding when allocating. |
| 164 | pub const alloc_num = 4; | 193 | pub const alloc_num = 4; |
| 165 | pub const alloc_den = 3; | 194 | pub const alloc_den = 3; |
| ... | @@ -379,10 +408,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -379,10 +408,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 379 | main_cmd.entryoff = addr - text_segment.inner.vmaddr; | 408 | main_cmd.entryoff = addr - text_segment.inner.vmaddr; |
| 380 | self.load_commands_dirty = true; | 409 | self.load_commands_dirty = true; |
| 381 | } | 410 | } |
| | 411 | try self.writeRebaseInfoTable(); |
| 382 | try self.writeBindingInfoTable(); | 412 | try self.writeBindingInfoTable(); |
| 383 | try self.writeLazyBindingInfoTable(); | 413 | try self.writeLazyBindingInfoTable(); |
| 384 | try self.writeExportTrie(); | 414 | try self.writeExportTrie(); |
| 385 | try self.writeAllGlobalAndUndefSymbols(); | 415 | try self.writeAllGlobalAndUndefSymbols(); |
| | 416 | try self.writeIndirectSymbolTable(); |
| 386 | try self.writeStringTable(); | 417 | try self.writeStringTable(); |
| 387 | try self.updateLinkeditSegmentSizes(); | 418 | try self.updateLinkeditSegmentSizes(); |
| 388 | | 419 | |
| ... | @@ -418,6 +449,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -418,6 +449,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 418 | assert(!self.offset_table_count_dirty); | 449 | assert(!self.offset_table_count_dirty); |
| 419 | assert(!self.header_dirty); | 450 | assert(!self.header_dirty); |
| 420 | assert(!self.load_commands_dirty); | 451 | assert(!self.load_commands_dirty); |
| | 452 | assert(!self.rebase_info_dirty); |
| 421 | assert(!self.binding_info_dirty); | 453 | assert(!self.binding_info_dirty); |
| 422 | assert(!self.lazy_binding_info_dirty); | 454 | assert(!self.lazy_binding_info_dirty); |
| 423 | assert(!self.export_info_dirty); | 455 | assert(!self.export_info_dirty); |
| ... | @@ -891,42 +923,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | ... | @@ -891,42 +923,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| 891 | return error.NoSymbolTableFound; | 923 | return error.NoSymbolTableFound; |
| 892 | } | 924 | } |
| 893 | | 925 | |
| 894 | // Parse dyld info | 926 | // Patch dyld info |
| 895 | try self.parseBindingInfoTable(); | 927 | try self.fixupBindInfo(next_ordinal); |
| 896 | try self.parseLazyBindingInfoTable(); | 928 | try self.fixupLazyBindInfo(next_ordinal); |
| 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 | } | | |
| 930 | | 929 | |
| 931 | // Write updated load commands and the header | 930 | // Write updated load commands and the header |
| 932 | try self.writeLoadCommands(); | 931 | try self.writeLoadCommands(); |
| ... | @@ -1008,14 +1007,20 @@ pub fn deinit(self: *MachO) void { | ... | @@ -1008,14 +1007,20 @@ pub fn deinit(self: *MachO) void { |
| 1008 | if (self.d_sym) |*ds| { | 1007 | if (self.d_sym) |*ds| { |
| 1009 | ds.deinit(self.base.allocator); | 1008 | ds.deinit(self.base.allocator); |
| 1010 | } | 1009 | } |
| 1011 | self.binding_info_table.deinit(self.base.allocator); | 1010 | for (self.extern_lazy_symbols.items()) |*entry| { |
| 1012 | self.lazy_binding_info_table.deinit(self.base.allocator); | 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 | self.pie_fixups.deinit(self.base.allocator); | 1018 | self.pie_fixups.deinit(self.base.allocator); |
| | 1019 | self.stub_fixups.deinit(self.base.allocator); |
| 1014 | self.text_block_free_list.deinit(self.base.allocator); | 1020 | self.text_block_free_list.deinit(self.base.allocator); |
| 1015 | self.offset_table.deinit(self.base.allocator); | 1021 | self.offset_table.deinit(self.base.allocator); |
| 1016 | self.offset_table_free_list.deinit(self.base.allocator); | 1022 | self.offset_table_free_list.deinit(self.base.allocator); |
| 1017 | self.string_table.deinit(self.base.allocator); | 1023 | self.string_table.deinit(self.base.allocator); |
| 1018 | self.undef_symbols.deinit(self.base.allocator); | | |
| 1019 | self.global_symbols.deinit(self.base.allocator); | 1024 | self.global_symbols.deinit(self.base.allocator); |
| 1020 | self.global_symbol_free_list.deinit(self.base.allocator); | 1025 | self.global_symbol_free_list.deinit(self.base.allocator); |
| 1021 | self.local_symbols.deinit(self.base.allocator); | 1026 | self.local_symbols.deinit(self.base.allocator); |
| ... | @@ -1211,7 +1216,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -1211,7 +1216,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1211 | } | 1216 | } |
| 1212 | | 1217 | |
| 1213 | // Perform PIE fixups (if any) | 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 | const got_section = text_segment.sections.items[self.got_section_index.?]; | 1220 | const got_section = text_segment.sections.items[self.got_section_index.?]; |
| 1216 | while (self.pie_fixups.popOrNull()) |fixup| { | 1221 | while (self.pie_fixups.popOrNull()) |fixup| { |
| 1217 | const target_addr = fixup.address; | 1222 | const target_addr = fixup.address; |
| ... | @@ -1231,6 +1236,38 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -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 | const text_section = text_segment.sections.items[self.text_section_index.?]; | 1271 | const text_section = text_segment.sections.items[self.text_section_index.?]; |
| 1235 | const section_offset = symbol.n_value - text_section.addr; | 1272 | const section_offset = symbol.n_value - text_section.addr; |
| 1236 | const file_offset = text_section.offset + section_offset; | 1273 | const file_offset = text_section.offset + section_offset; |
| ... | @@ -1435,7 +1472,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1435,7 +1472,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1435 | | 1472 | |
| 1436 | const program_code_size_hint = self.base.options.program_code_size_hint; | 1473 | const program_code_size_hint = self.base.options.program_code_size_hint; |
| 1437 | const offset_table_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint; | 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 | const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size); | 1476 | const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size); |
| 1440 | | 1477 | |
| 1441 | log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size }); | 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,9 +1529,13 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1492 | } | 1529 | } |
| 1493 | if (self.got_section_index == null) { | 1530 | if (self.got_section_index == null) { |
| 1494 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 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 | self.got_section_index = @intCast(u16, text_segment.sections.items.len); | 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 | const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; | 1539 | const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; |
| 1499 | const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; | 1540 | const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint; |
| 1500 | const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad); | 1541 | const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad); |
| ... | @@ -1508,7 +1549,220 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1508,7 +1549,220 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1508 | .addr = text_segment.inner.vmaddr + off, | 1549 | .addr = text_segment.inner.vmaddr + off, |
| 1509 | .size = needed_size, | 1550 | .size = needed_size, |
| 1510 | .offset = @intCast(u32, off), | 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 | .reloff = 0, | 1766 | .reloff = 0, |
| 1513 | .nreloc = 0, | 1767 | .nreloc = 0, |
| 1514 | .flags = flags, | 1768 | .flags = flags, |
| ... | @@ -1549,12 +1803,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1549,12 +1803,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1549 | if (self.dyld_info_cmd_index == null) { | 1803 | if (self.dyld_info_cmd_index == null) { |
| 1550 | self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len); | 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 | try self.load_commands.append(self.base.allocator, .{ | 1806 | try self.load_commands.append(self.base.allocator, .{ |
| 1559 | .DyldInfoOnly = .{ | 1807 | .DyldInfoOnly = .{ |
| 1560 | .cmd = macho.LC_DYLD_INFO_ONLY, | 1808 | .cmd = macho.LC_DYLD_INFO_ONLY, |
| ... | @@ -1567,37 +1815,67 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1567,37 +1815,67 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1567 | .weak_bind_size = 0, | 1815 | .weak_bind_size = 0, |
| 1568 | .lazy_bind_off = 0, | 1816 | .lazy_bind_off = 0, |
| 1569 | .lazy_bind_size = 0, | 1817 | .lazy_bind_size = 0, |
| 1570 | .export_off = @intCast(u32, export_off), | 1818 | .export_off = 0, |
| 1571 | .export_size = export_size, | 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 | self.header_dirty = true; | 1847 | self.header_dirty = true; |
| 1575 | self.load_commands_dirty = true; | 1848 | self.load_commands_dirty = true; |
| 1576 | } | 1849 | } |
| 1577 | if (self.symtab_cmd_index == null) { | 1850 | if (self.symtab_cmd_index == null) { |
| 1578 | self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len); | 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 | const symtab_size = self.base.options.symbol_count_hint * @sizeOf(macho.nlist_64); | 1866 | const symtab_size = self.base.options.symbol_count_hint * @sizeOf(macho.nlist_64); |
| 1581 | const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64)); | 1867 | const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64)); |
| 1582 | | | |
| 1583 | log.debug("found symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size }); | 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 | try self.string_table.append(self.base.allocator, 0); // Need a null at position 0. | 1872 | try self.string_table.append(self.base.allocator, 0); // Need a null at position 0. |
| 1586 | const strtab_size = self.string_table.items.len; | 1873 | const strtab_size = self.string_table.items.len; |
| 1587 | const strtab_off = self.findFreeSpaceLinkedit(strtab_size, 1); | 1874 | const strtab_off = self.findFreeSpaceLinkedit(strtab_size, 1); |
| 1588 | | | |
| 1589 | log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + strtab_size }); | 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 | self.header_dirty = true; | 1879 | self.header_dirty = true; |
| 1602 | self.load_commands_dirty = true; | 1880 | self.load_commands_dirty = true; |
| 1603 | self.string_table_dirty = true; | 1881 | self.string_table_dirty = true; |
| ... | @@ -1605,7 +1883,11 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1605,7 +1883,11 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1605 | if (self.dysymtab_cmd_index == null) { | 1883 | if (self.dysymtab_cmd_index == null) { |
| 1606 | self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len); | 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 | try self.load_commands.append(self.base.allocator, .{ | 1892 | try self.load_commands.append(self.base.allocator, .{ |
| 1611 | .Dysymtab = .{ | 1893 | .Dysymtab = .{ |
| ... | @@ -1623,8 +1905,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1623,8 +1905,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1623 | .nmodtab = 0, | 1905 | .nmodtab = 0, |
| 1624 | .extrefsymoff = 0, | 1906 | .extrefsymoff = 0, |
| 1625 | .nextrefsyms = 0, | 1907 | .nextrefsyms = 0, |
| 1626 | .indirectsymoff = 0, | 1908 | .indirectsymoff = @intCast(u32, indsymtab_off), |
| 1627 | .nindirectsyms = 0, | 1909 | .nindirectsyms = @intCast(u32, self.base.options.symbol_count_hint), |
| 1628 | .extreloff = 0, | 1910 | .extreloff = 0, |
| 1629 | .nextrel = 0, | 1911 | .nextrel = 0, |
| 1630 | .locreloff = 0, | 1912 | .locreloff = 0, |
| ... | @@ -1752,16 +2034,73 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1752,16 +2034,73 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1752 | self.header_dirty = true; | 2034 | self.header_dirty = true; |
| 1753 | self.load_commands_dirty = true; | 2035 | self.load_commands_dirty = true; |
| 1754 | } | 2036 | } |
| 1755 | if (self.dyld_stub_binder_index == null) { | 2037 | if (!self.extern_nonlazy_symbols.contains("dyld_stub_binder")) { |
| 1756 | self.dyld_stub_binder_index = @intCast(u16, self.undef_symbols.items.len); | 2038 | const index = @intCast(u32, self.extern_nonlazy_symbols.items().len); |
| 1757 | const name = try self.makeString("dyld_stub_binder"); | 2039 | const name = try std.fmt.allocPrint(self.base.allocator, "dyld_stub_binder", .{}); |
| 1758 | try self.undef_symbols.append(self.base.allocator, .{ | 2040 | try self.extern_nonlazy_symbols.putNoClobber(self.base.allocator, name, .{ |
| 1759 | .n_strx = name, | 2041 | .name = name, |
| 1760 | .n_type = macho.N_UNDF | macho.N_EXT, | 2042 | .dylib_ordinal = 1, // TODO this is currently hardcoded. |
| 1761 | .n_sect = 0, | 2043 | .segment = self.data_const_segment_cmd_index.?, |
| 1762 | .n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER, | 2044 | .offset = index * @sizeOf(u64), |
| 1763 | .n_value = 0, | | |
| 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,7 +2216,7 @@ pub fn makeStaticString(comptime bytes: []const u8) [16]u8 { |
| 1877 | return buf; | 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 | try self.string_table.ensureCapacity(self.base.allocator, self.string_table.items.len + bytes.len + 1); | 2220 | try self.string_table.ensureCapacity(self.base.allocator, self.string_table.items.len + bytes.len + 1); |
| 1882 | const result = @intCast(u32, self.string_table.items.len); | 2221 | const result = @intCast(u32, self.string_table.items.len); |
| 1883 | self.string_table.appendSliceAssumeCapacity(bytes); | 2222 | self.string_table.appendSliceAssumeCapacity(bytes); |
| ... | @@ -1907,16 +2246,13 @@ const NextSegmentAddressAndOffset = struct { | ... | @@ -1907,16 +2246,13 @@ const NextSegmentAddressAndOffset = struct { |
| 1907 | }; | 2246 | }; |
| 1908 | | 2247 | |
| 1909 | fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset { | 2248 | fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset { |
| 1910 | const prev_segment_idx = blk: { | 2249 | var prev_segment_idx: ?usize = null; // We use optional here for safety. |
| 1911 | if (self.data_segment_cmd_index) |idx| { | 2250 | for (self.load_commands.items) |cmd, i| { |
| 1912 | break :blk idx; | 2251 | if (cmd == .Segment) { |
| 1913 | } else if (self.text_segment_cmd_index) |idx| { | 2252 | prev_segment_idx = i; |
| 1914 | break :blk idx; | | |
| 1915 | } else { | | |
| 1916 | unreachable; // unhandled LC_SEGMENT_64 load command before __TEXT | | |
| 1917 | } | 2253 | } |
| 1918 | }; | 2254 | } |
| 1919 | const prev_segment = self.load_commands.items[prev_segment_idx].Segment; | 2255 | const prev_segment = self.load_commands.items[prev_segment_idx.?].Segment; |
| 1920 | const address = prev_segment.inner.vmaddr + prev_segment.inner.vmsize; | 2256 | const address = prev_segment.inner.vmaddr + prev_segment.inner.vmsize; |
| 1921 | const offset = prev_segment.inner.fileoff + prev_segment.inner.filesize; | 2257 | const offset = prev_segment.inner.fileoff + prev_segment.inner.filesize; |
| 1922 | return .{ | 2258 | return .{ |
| ... | @@ -2100,11 +2436,98 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { | ... | @@ -2100,11 +2436,98 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void { |
| 2100 | try self.base.file.?.pwriteAll(&code, off); | 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 | fn relocateSymbolTable(self: *MachO) !void { | 2526 | fn relocateSymbolTable(self: *MachO) !void { |
| 2104 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | 2527 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 2105 | const nlocals = self.local_symbols.items.len; | 2528 | const nlocals = self.local_symbols.items.len; |
| 2106 | const nglobals = self.global_symbols.items.len; | 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 | const nsyms = nlocals + nglobals + nundefs; | 2531 | const nsyms = nlocals + nglobals + nundefs; |
| 2109 | | 2532 | |
| 2110 | if (symtab.nsyms < nsyms) { | 2533 | if (symtab.nsyms < nsyms) { |
| ... | @@ -2149,7 +2572,31 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { | ... | @@ -2149,7 +2572,31 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { |
| 2149 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | 2572 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 2150 | const nlocals = self.local_symbols.items.len; | 2573 | const nlocals = self.local_symbols.items.len; |
| 2151 | const nglobals = self.global_symbols.items.len; | 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 | const locals_off = symtab.symoff; | 2601 | const locals_off = symtab.symoff; |
| 2155 | const locals_size = nlocals * @sizeOf(macho.nlist_64); | 2602 | const locals_size = nlocals * @sizeOf(macho.nlist_64); |
| ... | @@ -2161,8 +2608,8 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { | ... | @@ -2161,8 +2608,8 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { |
| 2161 | | 2608 | |
| 2162 | const undefs_off = globals_off + globals_size; | 2609 | const undefs_off = globals_off + globals_size; |
| 2163 | const undefs_size = nundefs * @sizeOf(macho.nlist_64); | 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 }); | 2611 | log.debug("writing extern 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); | 2612 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off); |
| 2166 | | 2613 | |
| 2167 | // Update dynamic symbol table. | 2614 | // Update dynamic symbol table. |
| 2168 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; | 2615 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; |
| ... | @@ -2174,6 +2621,49 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { | ... | @@ -2174,6 +2621,49 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { |
| 2174 | self.load_commands_dirty = true; | 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 | fn writeCodeSignaturePadding(self: *MachO) !void { | 2667 | fn writeCodeSignaturePadding(self: *MachO) !void { |
| 2178 | const tracy = trace(@src()); | 2668 | const tracy = trace(@src()); |
| 2179 | defer tracy.end(); | 2669 | defer tracy.end(); |
| ... | @@ -2272,18 +2762,63 @@ fn writeExportTrie(self: *MachO) !void { | ... | @@ -2272,18 +2762,63 @@ fn writeExportTrie(self: *MachO) !void { |
| 2272 | self.export_info_dirty = false; | 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 | fn writeBindingInfoTable(self: *MachO) !void { | 2803 | fn writeBindingInfoTable(self: *MachO) !void { |
| 2276 | if (!self.binding_info_dirty) return; | 2804 | if (!self.binding_info_dirty) return; |
| 2277 | | 2805 | |
| 2278 | const tracy = trace(@src()); | 2806 | const tracy = trace(@src()); |
| 2279 | defer tracy.end(); | 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 | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); | 2817 | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); |
| 2283 | defer self.base.allocator.free(buffer); | 2818 | defer self.base.allocator.free(buffer); |
| 2284 | | 2819 | |
| 2285 | var stream = std.io.fixedBufferStream(buffer); | 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 | const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | 2823 | const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 2289 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | 2824 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| ... | @@ -2306,12 +2841,19 @@ fn writeBindingInfoTable(self: *MachO) !void { | ... | @@ -2306,12 +2841,19 @@ fn writeBindingInfoTable(self: *MachO) !void { |
| 2306 | fn writeLazyBindingInfoTable(self: *MachO) !void { | 2841 | fn writeLazyBindingInfoTable(self: *MachO) !void { |
| 2307 | if (!self.lazy_binding_info_dirty) return; | 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 | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); | 2852 | var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); |
| 2311 | defer self.base.allocator.free(buffer); | 2853 | defer self.base.allocator.free(buffer); |
| 2312 | | 2854 | |
| 2313 | var stream = std.io.fixedBufferStream(buffer); | 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 | const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | 2858 | const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| 2317 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | 2859 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| ... | @@ -2327,10 +2869,78 @@ fn writeLazyBindingInfoTable(self: *MachO) !void { | ... | @@ -2327,10 +2869,78 @@ fn writeLazyBindingInfoTable(self: *MachO) !void { |
| 2327 | 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 }); | 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 | try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off); | 2871 | try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off); |
| | 2872 | try self.populateLazyBindOffsetsInStubHelper(buffer); |
| 2330 | self.load_commands_dirty = true; | 2873 | self.load_commands_dirty = true; |
| 2331 | self.lazy_binding_info_dirty = false; | 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 | fn writeStringTable(self: *MachO) !void { | 2944 | fn writeStringTable(self: *MachO) !void { |
| 2335 | if (!self.string_table_dirty) return; | 2945 | if (!self.string_table_dirty) return; |
| 2336 | | 2946 | |
| ... | @@ -2438,8 +3048,6 @@ fn writeHeader(self: *MachO) !void { | ... | @@ -2438,8 +3048,6 @@ fn writeHeader(self: *MachO) !void { |
| 2438 | } | 3048 | } |
| 2439 | | 3049 | |
| 2440 | /// Parse MachO contents from existing binary file. | 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 | fn parseFromFile(self: *MachO, file: fs.File) !void { | 3051 | fn parseFromFile(self: *MachO, file: fs.File) !void { |
| 2444 | self.base.file = file; | 3052 | self.base.file = file; |
| 2445 | var reader = file.reader(); | 3053 | var reader = file.reader(); |
| ... | @@ -2464,6 +3072,8 @@ fn parseFromFile(self: *MachO, file: fs.File) !void { | ... | @@ -2464,6 +3072,8 @@ fn parseFromFile(self: *MachO, file: fs.File) !void { |
| 2464 | } | 3072 | } |
| 2465 | } else if (parseAndCmpName(&x.inner.segname, "__DATA")) { | 3073 | } else if (parseAndCmpName(&x.inner.segname, "__DATA")) { |
| 2466 | self.data_segment_cmd_index = i; | 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 | macho.LC_DYLD_INFO_ONLY => { | 3079 | macho.LC_DYLD_INFO_ONLY => { |
| ... | @@ -2549,24 +3159,61 @@ fn parseStringTable(self: *MachO) !void { | ... | @@ -2549,24 +3159,61 @@ fn parseStringTable(self: *MachO) !void { |
| 2549 | self.string_table.appendSliceAssumeCapacity(buffer); | 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 | const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | 3163 | const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 2554 | var buffer = try self.base.allocator.alloc(u8, dyld_info.bind_size); | 3164 | var buffer = try self.base.allocator.alloc(u8, dyld_info.bind_size); |
| 2555 | defer self.base.allocator.free(buffer); | 3165 | defer self.base.allocator.free(buffer); |
| 2556 | const nread = try self.base.file.?.preadAll(buffer, dyld_info.bind_off); | 3166 | const nread = try self.base.file.?.preadAll(buffer, dyld_info.bind_off); |
| 2557 | assert(nread == buffer.len); | 3167 | assert(nread == buffer.len); |
| 2558 | | 3168 | try self.fixupInfoCommon(buffer, dylib_ordinal); |
| 2559 | var stream = std.io.fixedBufferStream(buffer); | 3169 | try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off); |
| 2560 | try self.binding_info_table.read(stream.reader(), self.base.allocator); | | |
| 2561 | } | 3170 | } |
| 2562 | | 3171 | |
| 2563 | fn parseLazyBindingInfoTable(self: *MachO) !void { | 3172 | fn fixupLazyBindInfo(self: *MachO, dylib_ordinal: u32) !void { |
| 2564 | const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; | 3173 | const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; |
| 2565 | var buffer = try self.base.allocator.alloc(u8, dyld_info.lazy_bind_size); | 3174 | var buffer = try self.base.allocator.alloc(u8, dyld_info.lazy_bind_size); |
| 2566 | defer self.base.allocator.free(buffer); | 3175 | defer self.base.allocator.free(buffer); |
| 2567 | const nread = try self.base.file.?.preadAll(buffer, dyld_info.lazy_bind_off); | 3176 | const nread = try self.base.file.?.preadAll(buffer, dyld_info.lazy_bind_off); |
| 2568 | assert(nread == buffer.len); | 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 | var stream = std.io.fixedBufferStream(buffer); | 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 | } |