| ... | @@ -116,7 +116,9 @@ global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | ... | @@ -116,7 +116,9 @@ global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 116 | /// Table of all undefined symbols | 116 | /// Table of all undefined symbols |
| 117 | undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | 117 | undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 118 | | 118 | |
| | 119 | local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 119 | global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, | 120 | global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| | 121 | offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 120 | | 122 | |
| 121 | dyld_stub_binder_index: ?u16 = null, | 123 | dyld_stub_binder_index: ?u16 = null, |
| 122 | | 124 | |
| ... | @@ -131,7 +133,25 @@ offset_table: std.ArrayListUnmanaged(u64) = .{}, | ... | @@ -131,7 +133,25 @@ offset_table: std.ArrayListUnmanaged(u64) = .{}, |
| 131 | error_flags: File.ErrorFlags = File.ErrorFlags{}, | 133 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 132 | | 134 | |
| 133 | cmd_table_dirty: bool = false, | 135 | cmd_table_dirty: bool = false, |
| 134 | | 136 | dylinker_cmd_dirty: bool = false, |
| | 137 | libsystem_cmd_dirty: bool = false, |
| | 138 | |
| | 139 | /// A list of text blocks that have surplus capacity. This list can have false |
| | 140 | /// positives, as functions grow and shrink over time, only sometimes being added |
| | 141 | /// or removed from the freelist. |
| | 142 | /// |
| | 143 | /// A text block has surplus capacity when its overcapacity value is greater than |
| | 144 | /// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so |
| | 145 | /// much extra capacity, that we could fit a small new symbol in it, itself with |
| | 146 | /// ideal_capacity or more. |
| | 147 | /// |
| | 148 | /// Ideal capacity is defined by size * alloc_num / alloc_den. |
| | 149 | /// |
| | 150 | /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that |
| | 151 | /// overcapacity can be negative. A simple way to have negative overcapacity is to |
| | 152 | /// allocate a fresh text block, which will have ideal capacity, and then grow it |
| | 153 | /// by 1 byte. It will then have -1 overcapacity. |
| | 154 | text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, |
| 135 | /// Pointer to the last allocated text block | 155 | /// Pointer to the last allocated text block |
| 136 | last_text_block: ?*TextBlock = null, | 156 | last_text_block: ?*TextBlock = null, |
| 137 | | 157 | |
| ... | @@ -153,6 +173,12 @@ const LIB_SYSTEM_NAME: [*:0]const u8 = "System"; | ... | @@ -153,6 +173,12 @@ const LIB_SYSTEM_NAME: [*:0]const u8 = "System"; |
| 153 | /// TODO we should search for libSystem and fail if it doesn't exist, instead of hardcoding it | 173 | /// TODO we should search for libSystem and fail if it doesn't exist, instead of hardcoding it |
| 154 | const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib"; | 174 | const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib"; |
| 155 | | 175 | |
| | 176 | /// In order for a slice of bytes to be considered eligible to keep metadata pointing at |
| | 177 | /// it as a possible place to put new symbols, it must have enough room for this many bytes |
| | 178 | /// (plus extra for reserved capacity). |
| | 179 | const minimum_text_block_size = 64; |
| | 180 | const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den; |
| | 181 | |
| 156 | pub const TextBlock = struct { | 182 | pub const TextBlock = struct { |
| 157 | /// Each decl always gets a local symbol with the fully qualified name. | 183 | /// Each decl always gets a local symbol with the fully qualified name. |
| 158 | /// The vaddr and size are found here directly. | 184 | /// The vaddr and size are found here directly. |
| ... | @@ -179,6 +205,33 @@ pub const TextBlock = struct { | ... | @@ -179,6 +205,33 @@ pub const TextBlock = struct { |
| 179 | .prev = null, | 205 | .prev = null, |
| 180 | .next = null, | 206 | .next = null, |
| 181 | }; | 207 | }; |
| | 208 | |
| | 209 | /// Returns how much room there is to grow in virtual address space. |
| | 210 | /// File offset relocation happens transparently, so it is not included in |
| | 211 | /// this calculation. |
| | 212 | fn capacity(self: TextBlock, macho_file: MachO) u64 { |
| | 213 | const self_sym = macho_file.local_symbols.items[self.local_sym_index]; |
| | 214 | if (self.next) |next| { |
| | 215 | const next_sym = macho_file.local_symbols.items[next.local_sym_index]; |
| | 216 | return next_sym.n_value - self_sym.n_value; |
| | 217 | } else { |
| | 218 | // We are the last block. |
| | 219 | // The capacity is limited only by virtual address space. |
| | 220 | return std.math.maxInt(u64) - self_sym.n_value; |
| | 221 | } |
| | 222 | } |
| | 223 | |
| | 224 | fn freeListEligible(self: TextBlock, macho_file: MachO) bool { |
| | 225 | // No need to keep a free list node for the last block. |
| | 226 | const next = self.next orelse return false; |
| | 227 | const self_sym = macho_file.local_symbols.items[self.local_sym_index]; |
| | 228 | const next_sym = macho_file.local_symbols.items[next.local_sym_index]; |
| | 229 | const cap = next_sym.n_value - self_sym.n_value; |
| | 230 | const ideal_cap = self.size * alloc_num / alloc_den; |
| | 231 | if (cap <= ideal_cap) return false; |
| | 232 | const surplus = cap - ideal_cap; |
| | 233 | return surplus >= min_text_capacity; |
| | 234 | } |
| 182 | }; | 235 | }; |
| 183 | | 236 | |
| 184 | pub const Export = struct { | 237 | pub const Export = struct { |
| ... | @@ -267,14 +320,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -267,14 +320,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 267 | .Exe => { | 320 | .Exe => { |
| 268 | // Write export trie. | 321 | // Write export trie. |
| 269 | try self.writeExportTrie(); | 322 | try self.writeExportTrie(); |
| 270 | | | |
| 271 | if (self.entry_addr) |addr| { | 323 | if (self.entry_addr) |addr| { |
| 272 | // Update LC_MAIN with entry offset | 324 | // Update LC_MAIN with entry offset. |
| 273 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 325 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 274 | const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint; | 326 | const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint; |
| 275 | main_cmd.entryoff = addr - text_segment.vmaddr; | 327 | main_cmd.entryoff = addr - text_segment.vmaddr; |
| 276 | } | 328 | } |
| 277 | | | |
| 278 | { | 329 | { |
| 279 | // Update dynamic symbol table. | 330 | // Update dynamic symbol table. |
| 280 | const nlocals = @intCast(u32, self.local_symbols.items.len); | 331 | const nlocals = @intCast(u32, self.local_symbols.items.len); |
| ... | @@ -287,7 +338,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -287,7 +338,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 287 | dysymtab.iundefsym = nlocals + nglobals; | 338 | dysymtab.iundefsym = nlocals + nglobals; |
| 288 | dysymtab.nundefsym = nundefs; | 339 | dysymtab.nundefsym = nundefs; |
| 289 | } | 340 | } |
| 290 | { | 341 | if (self.dylinker_cmd_dirty) { |
| 291 | // Write path to dyld loader. | 342 | // Write path to dyld loader. |
| 292 | var off: usize = @sizeOf(macho.mach_header_64); | 343 | var off: usize = @sizeOf(macho.mach_header_64); |
| 293 | for (self.load_commands.items) |cmd| { | 344 | for (self.load_commands.items) |cmd| { |
| ... | @@ -298,8 +349,9 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -298,8 +349,9 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 298 | off += cmd.name; | 349 | off += cmd.name; |
| 299 | log.debug("writing LC_LOAD_DYLINKER path to dyld at 0x{x}\n", .{off}); | 350 | log.debug("writing LC_LOAD_DYLINKER path to dyld at 0x{x}\n", .{off}); |
| 300 | try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), off); | 351 | try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), off); |
| | 352 | self.dylinker_cmd_dirty = false; |
| 301 | } | 353 | } |
| 302 | { | 354 | if (self.libsystem_cmd_dirty) { |
| 303 | // Write path to libSystem. | 355 | // Write path to libSystem. |
| 304 | var off: usize = @sizeOf(macho.mach_header_64); | 356 | var off: usize = @sizeOf(macho.mach_header_64); |
| 305 | for (self.load_commands.items) |cmd| { | 357 | for (self.load_commands.items) |cmd| { |
| ... | @@ -310,14 +362,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -310,14 +362,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 310 | off += cmd.dylib.name; | 362 | off += cmd.dylib.name; |
| 311 | log.debug("writing LC_LOAD_DYLIB path to libSystem at 0x{x}\n", .{off}); | 363 | log.debug("writing LC_LOAD_DYLIB path to libSystem at 0x{x}\n", .{off}); |
| 312 | try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), off); | 364 | try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), off); |
| | 365 | self.libsystem_cmd_dirty = false; |
| 313 | } | 366 | } |
| 314 | }, | 367 | }, |
| 315 | .Obj => {}, | 368 | .Obj => {}, |
| 316 | .Lib => return error.TODOImplementWritingLibFiles, | 369 | .Lib => return error.TODOImplementWritingLibFiles, |
| 317 | } | 370 | } |
| 318 | | 371 | |
| 319 | if (self.cmd_table_dirty) try self.writeCmdHeaders(); | | |
| 320 | | | |
| 321 | { | 372 | { |
| 322 | // Update symbol table. | 373 | // Update symbol table. |
| 323 | const nlocals = @intCast(u32, self.local_symbols.items.len); | 374 | const nlocals = @intCast(u32, self.local_symbols.items.len); |
| ... | @@ -327,14 +378,23 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -327,14 +378,23 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 327 | symtab.nsyms = nlocals + nglobals + nundefs; | 378 | symtab.nsyms = nlocals + nglobals + nundefs; |
| 328 | } | 379 | } |
| 329 | | 380 | |
| | 381 | if (self.cmd_table_dirty) { |
| | 382 | try self.writeCmdHeaders(); |
| | 383 | try self.writeMachOHeader(); |
| | 384 | self.cmd_table_dirty = false; |
| | 385 | } |
| | 386 | |
| 330 | if (self.entry_addr == null and self.base.options.output_mode == .Exe) { | 387 | if (self.entry_addr == null and self.base.options.output_mode == .Exe) { |
| 331 | log.debug("flushing. no_entry_point_found = true\n", .{}); | 388 | log.debug("flushing. no_entry_point_found = true\n", .{}); |
| 332 | self.error_flags.no_entry_point_found = true; | 389 | self.error_flags.no_entry_point_found = true; |
| 333 | } else { | 390 | } else { |
| 334 | log.debug("flushing. no_entry_point_found = false\n", .{}); | 391 | log.debug("flushing. no_entry_point_found = false\n", .{}); |
| 335 | self.error_flags.no_entry_point_found = false; | 392 | self.error_flags.no_entry_point_found = false; |
| 336 | try self.writeMachOHeader(); | | |
| 337 | } | 393 | } |
| | 394 | |
| | 395 | assert(!self.cmd_table_dirty); |
| | 396 | assert(!self.dylinker_cmd_dirty); |
| | 397 | assert(!self.libsystem_cmd_dirty); |
| 338 | } | 398 | } |
| 339 | | 399 | |
| 340 | fn linkWithLLD(self: *MachO, comp: *Compilation) !void { | 400 | fn linkWithLLD(self: *MachO, comp: *Compilation) !void { |
| ... | @@ -720,28 +780,95 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { | ... | @@ -720,28 +780,95 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { |
| 720 | } | 780 | } |
| 721 | | 781 | |
| 722 | pub fn deinit(self: *MachO) void { | 782 | pub fn deinit(self: *MachO) void { |
| | 783 | self.text_block_free_list.deinit(self.base.allocator); |
| 723 | self.offset_table.deinit(self.base.allocator); | 784 | self.offset_table.deinit(self.base.allocator); |
| | 785 | self.offset_table_free_list.deinit(self.base.allocator); |
| 724 | self.string_table.deinit(self.base.allocator); | 786 | self.string_table.deinit(self.base.allocator); |
| 725 | self.undef_symbols.deinit(self.base.allocator); | 787 | self.undef_symbols.deinit(self.base.allocator); |
| 726 | self.global_symbols.deinit(self.base.allocator); | 788 | self.global_symbols.deinit(self.base.allocator); |
| 727 | self.global_symbol_free_list.deinit(self.base.allocator); | 789 | self.global_symbol_free_list.deinit(self.base.allocator); |
| 728 | self.local_symbols.deinit(self.base.allocator); | 790 | self.local_symbols.deinit(self.base.allocator); |
| | 791 | self.local_symbol_free_list.deinit(self.base.allocator); |
| 729 | self.sections.deinit(self.base.allocator); | 792 | self.sections.deinit(self.base.allocator); |
| 730 | self.load_commands.deinit(self.base.allocator); | 793 | self.load_commands.deinit(self.base.allocator); |
| 731 | } | 794 | } |
| 732 | | 795 | |
| | 796 | fn freeTextBlock(self: *MachO, text_block: *TextBlock) void { |
| | 797 | var already_have_free_list_node = false; |
| | 798 | { |
| | 799 | var i: usize = 0; |
| | 800 | // TODO turn text_block_free_list into a hash map |
| | 801 | while (i < self.text_block_free_list.items.len) { |
| | 802 | if (self.text_block_free_list.items[i] == text_block) { |
| | 803 | _ = self.text_block_free_list.swapRemove(i); |
| | 804 | continue; |
| | 805 | } |
| | 806 | if (self.text_block_free_list.items[i] == text_block.prev) { |
| | 807 | already_have_free_list_node = true; |
| | 808 | } |
| | 809 | i += 1; |
| | 810 | } |
| | 811 | } |
| | 812 | // TODO process free list for dbg info just like we do above for vaddrs |
| | 813 | |
| | 814 | if (self.last_text_block == text_block) { |
| | 815 | // TODO shrink the __text section size here |
| | 816 | self.last_text_block = text_block.prev; |
| | 817 | } |
| | 818 | |
| | 819 | if (text_block.prev) |prev| { |
| | 820 | prev.next = text_block.next; |
| | 821 | |
| | 822 | if (!already_have_free_list_node and prev.freeListEligible(self.*)) { |
| | 823 | // The free list is heuristics, it doesn't have to be perfect, so we can ignore |
| | 824 | // the OOM here. |
| | 825 | self.text_block_free_list.append(self.base.allocator, prev) catch {}; |
| | 826 | } |
| | 827 | } else { |
| | 828 | text_block.prev = null; |
| | 829 | } |
| | 830 | |
| | 831 | if (text_block.next) |next| { |
| | 832 | next.prev = text_block.prev; |
| | 833 | } else { |
| | 834 | text_block.next = null; |
| | 835 | } |
| | 836 | } |
| | 837 | |
| | 838 | fn shrinkTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64) void { |
| | 839 | // TODO check the new capacity, and if it crosses the size threshold into a big enough |
| | 840 | // capacity, insert a free list node for it. |
| | 841 | } |
| | 842 | |
| | 843 | fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { |
| | 844 | const sym = self.local_symbols.items[text_block.local_sym_index]; |
| | 845 | const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value; |
| | 846 | const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*); |
| | 847 | if (!need_realloc) return sym.n_value; |
| | 848 | return self.allocateTextBlock(text_block, new_block_size, alignment); |
| | 849 | } |
| | 850 | |
| 733 | pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { | 851 | pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { |
| 734 | if (decl.link.macho.local_sym_index != 0) return; | 852 | if (decl.link.macho.local_sym_index != 0) return; |
| 735 | | 853 | |
| 736 | try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1); | 854 | try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1); |
| 737 | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); | 855 | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); |
| 738 | | 856 | |
| 739 | log.debug("allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name }); | 857 | if (self.local_symbol_free_list.popOrNull()) |i| { |
| 740 | decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len); | 858 | log.debug("reusing symbol index {} for {}\n", .{ i, decl.name }); |
| 741 | _ = self.local_symbols.addOneAssumeCapacity(); | 859 | decl.link.macho.local_sym_index = i; |
| | 860 | } else { |
| | 861 | log.debug("allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name }); |
| | 862 | decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len); |
| | 863 | _ = self.local_symbols.addOneAssumeCapacity(); |
| | 864 | } |
| 742 | | 865 | |
| 743 | decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len); | 866 | if (self.offset_table_free_list.popOrNull()) |i| { |
| 744 | _ = self.offset_table.addOneAssumeCapacity(); | 867 | decl.link.macho.offset_table_index = i; |
| | 868 | } else { |
| | 869 | decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len); |
| | 870 | _ = self.offset_table.addOneAssumeCapacity(); |
| | 871 | } |
| 745 | | 872 | |
| 746 | self.local_symbols.items[decl.link.macho.local_sym_index] = .{ | 873 | self.local_symbols.items[decl.link.macho.local_sym_index] = .{ |
| 747 | .n_strx = 0, | 874 | .n_strx = 0, |
| ... | @@ -774,24 +901,51 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -774,24 +901,51 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 774 | }; | 901 | }; |
| 775 | | 902 | |
| 776 | const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); | 903 | const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); |
| | 904 | assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes() |
| 777 | const symbol = &self.local_symbols.items[decl.link.macho.local_sym_index]; | 905 | const symbol = &self.local_symbols.items[decl.link.macho.local_sym_index]; |
| 778 | | 906 | |
| 779 | const decl_name = mem.spanZ(decl.name); | 907 | if (decl.link.macho.size != 0) { |
| 780 | const name_str_index = try self.makeString(decl_name); | 908 | const capacity = decl.link.macho.capacity(self.*); |
| 781 | const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment); | 909 | const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment); |
| 782 | log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, addr }); | 910 | if (need_realloc) { |
| 783 | | 911 | const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment); |
| 784 | symbol.* = .{ | 912 | log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, symbol.n_value, vaddr }); |
| 785 | .n_strx = name_str_index, | 913 | if (vaddr != symbol.n_value) { |
| 786 | .n_type = macho.N_SECT, | 914 | symbol.n_value = vaddr; |
| 787 | .n_sect = @intCast(u8, self.text_section_index.?) + 1, | 915 | |
| 788 | .n_desc = 0, | 916 | log.debug(" (writing new offset table entry)\n", .{}); |
| 789 | .n_value = addr, | 917 | self.offset_table.items[decl.link.macho.offset_table_index] = vaddr; |
| 790 | }; | 918 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); |
| 791 | self.offset_table.items[decl.link.macho.offset_table_index] = addr; | 919 | } |
| | 920 | } else if (code.len < decl.link.macho.size) { |
| | 921 | self.shrinkTextBlock(&decl.link.macho, code.len); |
| | 922 | } |
| | 923 | decl.link.macho.size = code.len; |
| | 924 | symbol.n_strx = try self.updateString(symbol.n_strx, mem.spanZ(decl.name)); |
| | 925 | symbol.n_type = macho.N_SECT; |
| | 926 | symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1; |
| | 927 | symbol.n_desc = 0; |
| | 928 | // TODO this write could be avoided if no fields of the symbol were changed. |
| | 929 | try self.writeSymbol(decl.link.macho.local_sym_index); |
| | 930 | } else { |
| | 931 | const decl_name = mem.spanZ(decl.name); |
| | 932 | const name_str_index = try self.makeString(decl_name); |
| | 933 | const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment); |
| | 934 | log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, addr }); |
| | 935 | errdefer self.freeTextBlock(&decl.link.macho); |
| | 936 | |
| | 937 | symbol.* = .{ |
| | 938 | .n_strx = name_str_index, |
| | 939 | .n_type = macho.N_SECT, |
| | 940 | .n_sect = @intCast(u8, self.text_section_index.?) + 1, |
| | 941 | .n_desc = 0, |
| | 942 | .n_value = addr, |
| | 943 | }; |
| | 944 | self.offset_table.items[decl.link.macho.offset_table_index] = addr; |
| 792 | | 945 | |
| 793 | try self.writeSymbol(decl.link.macho.local_sym_index); | 946 | try self.writeSymbol(decl.link.macho.local_sym_index); |
| 794 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); | 947 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); |
| | 948 | } |
| 795 | | 949 | |
| 796 | const text_section = self.sections.items[self.text_section_index.?]; | 950 | const text_section = self.sections.items[self.text_section_index.?]; |
| 797 | const section_offset = symbol.n_value - text_section.addr; | 951 | const section_offset = symbol.n_value - text_section.addr; |
| ... | @@ -835,6 +989,7 @@ pub fn updateDeclExports( | ... | @@ -835,6 +989,7 @@ pub fn updateDeclExports( |
| 835 | .Strong => blk: { | 989 | .Strong => blk: { |
| 836 | if (mem.eql(u8, exp.options.name, "_start")) { | 990 | if (mem.eql(u8, exp.options.name, "_start")) { |
| 837 | self.entry_addr = decl_sym.n_value; | 991 | self.entry_addr = decl_sym.n_value; |
| | 992 | self.cmd_table_dirty = true; // TODO This should be handled more granularly instead of invalidating all commands. |
| 838 | } | 993 | } |
| 839 | break :blk macho.REFERENCE_FLAG_DEFINED; | 994 | break :blk macho.REFERENCE_FLAG_DEFINED; |
| 840 | }, | 995 | }, |
| ... | @@ -883,7 +1038,18 @@ pub fn deleteExport(self: *MachO, exp: Export) void { | ... | @@ -883,7 +1038,18 @@ pub fn deleteExport(self: *MachO, exp: Export) void { |
| 883 | self.global_symbols.items[sym_index].n_type = 0; | 1038 | self.global_symbols.items[sym_index].n_type = 0; |
| 884 | } | 1039 | } |
| 885 | | 1040 | |
| 886 | pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {} | 1041 | pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { |
| | 1042 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. |
| | 1043 | self.freeTextBlock(&decl.link.macho); |
| | 1044 | if (decl.link.macho.local_sym_index != 0) { |
| | 1045 | self.local_symbol_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {}; |
| | 1046 | self.offset_table_free_list.append(self.base.allocator, decl.link.macho.offset_table_index) catch {}; |
| | 1047 | |
| | 1048 | self.local_symbols.items[decl.link.macho.local_sym_index].n_type = 0; |
| | 1049 | |
| | 1050 | decl.link.macho.local_sym_index = 0; |
| | 1051 | } |
| | 1052 | } |
| 887 | | 1053 | |
| 888 | pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 { | 1054 | pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 { |
| 889 | assert(decl.link.macho.local_sym_index != 0); | 1055 | assert(decl.link.macho.local_sym_index != 0); |
| ... | @@ -965,6 +1131,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -965,6 +1131,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 965 | | 1131 | |
| 966 | text_segment.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section. | 1132 | text_segment.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section. |
| 967 | text_segment.filesize = file_size + off; | 1133 | text_segment.filesize = file_size + off; |
| | 1134 | self.cmd_table_dirty = true; |
| 968 | } | 1135 | } |
| 969 | if (self.data_segment_cmd_index == null) { | 1136 | if (self.data_segment_cmd_index == null) { |
| 970 | self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | 1137 | self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| ... | @@ -1017,6 +1184,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1017,6 +1184,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1017 | data_segment.vmsize = segment_size; | 1184 | data_segment.vmsize = segment_size; |
| 1018 | data_segment.filesize = segment_size; | 1185 | data_segment.filesize = segment_size; |
| 1019 | data_segment.fileoff = off; | 1186 | data_segment.fileoff = off; |
| | 1187 | self.cmd_table_dirty = true; |
| 1020 | } | 1188 | } |
| 1021 | if (self.linkedit_segment_cmd_index == null) { | 1189 | if (self.linkedit_segment_cmd_index == null) { |
| 1022 | self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | 1190 | self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| ... | @@ -1112,6 +1280,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1112,6 +1280,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1112 | }, | 1280 | }, |
| 1113 | }); | 1281 | }); |
| 1114 | self.cmd_table_dirty = true; | 1282 | self.cmd_table_dirty = true; |
| | 1283 | self.dylinker_cmd_dirty = true; |
| 1115 | } | 1284 | } |
| 1116 | if (self.libsystem_cmd_index == null) { | 1285 | if (self.libsystem_cmd_index == null) { |
| 1117 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); | 1286 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); |
| ... | @@ -1133,6 +1302,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -1133,6 +1302,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 1133 | }, | 1302 | }, |
| 1134 | }); | 1303 | }); |
| 1135 | self.cmd_table_dirty = true; | 1304 | self.cmd_table_dirty = true; |
| | 1305 | self.libsystem_cmd_dirty = true; |
| 1136 | } | 1306 | } |
| 1137 | if (self.main_cmd_index == null) { | 1307 | if (self.main_cmd_index == null) { |
| 1138 | self.main_cmd_index = @intCast(u16, self.load_commands.items.len); | 1308 | self.main_cmd_index = @intCast(u16, self.load_commands.items.len); |
| ... | @@ -1205,18 +1375,61 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, | ... | @@ -1205,18 +1375,61 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 1205 | const text_section = &self.sections.items[self.text_section_index.?]; | 1375 | const text_section = &self.sections.items[self.text_section_index.?]; |
| 1206 | const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den; | 1376 | const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den; |
| 1207 | | 1377 | |
| | 1378 | // We use these to indicate our intention to update metadata, placing the new block, |
| | 1379 | // and possibly removing a free list node. |
| | 1380 | // It would be simpler to do it inside the for loop below, but that would cause a |
| | 1381 | // problem if an error was returned later in the function. So this action |
| | 1382 | // is actually carried out at the end of the function, when errors are no longer possible. |
| 1208 | var block_placement: ?*TextBlock = null; | 1383 | var block_placement: ?*TextBlock = null; |
| 1209 | const addr = blk: { | 1384 | var free_list_removal: ?usize = null; |
| 1210 | if (self.last_text_block) |last| { | 1385 | |
| | 1386 | // First we look for an appropriately sized free list node. |
| | 1387 | // The list is unordered. We'll just take the first thing that works. |
| | 1388 | const vaddr = blk: { |
| | 1389 | var i: usize = 0; |
| | 1390 | while (i < self.text_block_free_list.items.len) { |
| | 1391 | const big_block = self.text_block_free_list.items[i]; |
| | 1392 | // We now have a pointer to a live text block that has too much capacity. |
| | 1393 | // Is it enough that we could fit this new text block? |
| | 1394 | const sym = self.local_symbols.items[big_block.local_sym_index]; |
| | 1395 | const capacity = big_block.capacity(self.*); |
| | 1396 | const ideal_capacity = capacity * alloc_num / alloc_den; |
| | 1397 | const ideal_capacity_end_vaddr = sym.n_value + ideal_capacity; |
| | 1398 | const capacity_end_vaddr = sym.n_value + capacity; |
| | 1399 | const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity; |
| | 1400 | const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment); |
| | 1401 | if (new_start_vaddr < ideal_capacity_end_vaddr) { |
| | 1402 | // Additional bookkeeping here to notice if this free list node |
| | 1403 | // should be deleted because the block that it points to has grown to take up |
| | 1404 | // more of the extra capacity. |
| | 1405 | if (!big_block.freeListEligible(self.*)) { |
| | 1406 | _ = self.text_block_free_list.swapRemove(i); |
| | 1407 | } else { |
| | 1408 | i += 1; |
| | 1409 | } |
| | 1410 | continue; |
| | 1411 | } |
| | 1412 | // At this point we know that we will place the new block here. But the |
| | 1413 | // remaining question is whether there is still yet enough capacity left |
| | 1414 | // over for there to still be a free list node. |
| | 1415 | const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr; |
| | 1416 | const keep_free_list_node = remaining_capacity >= min_text_capacity; |
| | 1417 | |
| | 1418 | // Set up the metadata to be updated, after errors are no longer possible. |
| | 1419 | block_placement = big_block; |
| | 1420 | if (!keep_free_list_node) { |
| | 1421 | free_list_removal = i; |
| | 1422 | } |
| | 1423 | break :blk new_start_vaddr; |
| | 1424 | } else if (self.last_text_block) |last| { |
| 1211 | const last_symbol = self.local_symbols.items[last.local_sym_index]; | 1425 | const last_symbol = self.local_symbols.items[last.local_sym_index]; |
| 1212 | // TODO pad out with NOPs and reenable | 1426 | // TODO We should pad out the excess capacity with NOPs. For executables, |
| 1213 | // const ideal_capacity = last.size * alloc_num / alloc_den; | 1427 | // no padding seems to be OK, but it will probably not be for objects. |
| 1214 | // const ideal_capacity_end_addr = last_symbol.n_value + ideal_capacity; | 1428 | const ideal_capacity = last.size * alloc_num / alloc_den; |
| 1215 | // const new_start_addr = mem.alignForwardGeneric(u64, ideal_capacity_end_addr, alignment); | 1429 | const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity; |
| 1216 | const end_addr = last_symbol.n_value + last.size; | 1430 | const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment); |
| 1217 | const new_start_addr = mem.alignForwardGeneric(u64, end_addr, alignment); | | |
| 1218 | block_placement = last; | 1431 | block_placement = last; |
| 1219 | break :blk new_start_addr; | 1432 | break :blk new_start_vaddr; |
| 1220 | } else { | 1433 | } else { |
| 1221 | break :blk text_section.addr; | 1434 | break :blk text_section.addr; |
| 1222 | } | 1435 | } |
| ... | @@ -1225,11 +1438,13 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, | ... | @@ -1225,11 +1438,13 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 1225 | const expand_text_section = block_placement == null or block_placement.?.next == null; | 1438 | const expand_text_section = block_placement == null or block_placement.?.next == null; |
| 1226 | if (expand_text_section) { | 1439 | if (expand_text_section) { |
| 1227 | const text_capacity = self.allocatedSize(text_section.offset); | 1440 | const text_capacity = self.allocatedSize(text_section.offset); |
| 1228 | const needed_size = (addr + new_block_size) - text_section.addr; | 1441 | const needed_size = (vaddr + new_block_size) - text_section.addr; |
| 1229 | assert(needed_size <= text_capacity); // TODO handle growth | 1442 | assert(needed_size <= text_capacity); // TODO must move the entire text section. |
| 1230 | | 1443 | |
| 1231 | self.last_text_block = text_block; | 1444 | self.last_text_block = text_block; |
| 1232 | text_section.size = needed_size; // TODO temp until we pad out with NOPs | 1445 | text_section.size = needed_size; |
| | 1446 | |
| | 1447 | self.cmd_table_dirty = true; // TODO Make more granular. |
| 1233 | } | 1448 | } |
| 1234 | text_block.size = new_block_size; | 1449 | text_block.size = new_block_size; |
| 1235 | | 1450 | |
| ... | @@ -1248,8 +1463,11 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, | ... | @@ -1248,8 +1463,11 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 1248 | text_block.prev = null; | 1463 | text_block.prev = null; |
| 1249 | text_block.next = null; | 1464 | text_block.next = null; |
| 1250 | } | 1465 | } |
| | 1466 | if (free_list_removal) |i| { |
| | 1467 | _ = self.text_block_free_list.swapRemove(i); |
| | 1468 | } |
| 1251 | | 1469 | |
| 1252 | return addr; | 1470 | return vaddr; |
| 1253 | } | 1471 | } |
| 1254 | | 1472 | |
| 1255 | fn makeStaticString(comptime bytes: []const u8) [16]u8 { | 1473 | fn makeStaticString(comptime bytes: []const u8) [16]u8 { |
| ... | @@ -1479,7 +1697,7 @@ fn writeCmdHeaders(self: *MachO) !void { | ... | @@ -1479,7 +1697,7 @@ fn writeCmdHeaders(self: *MachO) !void { |
| 1479 | return error.TODOImplementWritingObjFiles; | 1697 | return error.TODOImplementWritingObjFiles; |
| 1480 | }; | 1698 | }; |
| 1481 | const idx = self.text_section_index.?; | 1699 | const idx = self.text_section_index.?; |
| 1482 | log.debug("writing text section {} at 0x{x}\n", .{ self.sections.items[idx .. idx + 1], off }); | 1700 | log.debug("writing text section header at 0x{x}\n", .{off}); |
| 1483 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off); | 1701 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off); |
| 1484 | } | 1702 | } |
| 1485 | { | 1703 | { |
| ... | @@ -1497,7 +1715,7 @@ fn writeCmdHeaders(self: *MachO) !void { | ... | @@ -1497,7 +1715,7 @@ fn writeCmdHeaders(self: *MachO) !void { |
| 1497 | return error.TODOImplementWritingObjFiles; | 1715 | return error.TODOImplementWritingObjFiles; |
| 1498 | }; | 1716 | }; |
| 1499 | const idx = self.got_section_index.?; | 1717 | const idx = self.got_section_index.?; |
| 1500 | log.debug("writing got section {} at 0x{x}\n", .{ self.sections.items[idx .. idx + 1], off }); | 1718 | log.debug("writing got section header at 0x{x}\n", .{off}); |
| 1501 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off); | 1719 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off); |
| 1502 | } | 1720 | } |
| 1503 | } | 1721 | } |