| ... | ... | @@ -194,10 +194,10 @@ section_ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{}, |
| 194 | 194 | /// overcapacity can be negative. A simple way to have negative overcapacity is to |
| 195 | 195 | /// allocate a fresh text block, which will have ideal capacity, and then grow it |
| 196 | 196 | /// by 1 byte. It will then have -1 overcapacity. |
| 197 | | text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, |
| 197 | block_free_lists: std.AutoHashMapUnmanaged(MatchingSection, std.ArrayListUnmanaged(*TextBlock)) = .{}, |
| 198 | 198 | |
| 199 | 199 | /// Pointer to the last allocated text block |
| 200 | | last_text_block: ?*TextBlock = null, |
| 200 | blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{}, |
| 201 | 201 | |
| 202 | 202 | /// List of TextBlocks that are owned directly by the linker. |
| 203 | 203 | /// Currently these are only TextBlocks that are the result of linking |
| ... | ... | @@ -206,8 +206,6 @@ last_text_block: ?*TextBlock = null, |
| 206 | 206 | /// TODO consolidate this. |
| 207 | 207 | managed_blocks: std.ArrayListUnmanaged(*TextBlock) = .{}, |
| 208 | 208 | |
| 209 | | blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{}, |
| 210 | | |
| 211 | 209 | /// Table of Decls that are currently alive. |
| 212 | 210 | /// We store them here so that we can properly dispose of any allocated |
| 213 | 211 | /// memory within the TextBlock in the incremental linker. |
| ... | ... | @@ -1535,6 +1533,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio |
| 1535 | 1533 | |
| 1536 | 1534 | if (res) |match| { |
| 1537 | 1535 | _ = try self.section_ordinals.getOrPut(self.base.allocator, match); |
| 1536 | _ = try self.block_free_lists.getOrPutValue(self.base.allocator, match, .{}); |
| 1538 | 1537 | } |
| 1539 | 1538 | |
| 1540 | 1539 | return res; |
| ... | ... | @@ -3417,8 +3416,13 @@ pub fn deinit(self: *MachO) void { |
| 3417 | 3416 | } |
| 3418 | 3417 | self.managed_blocks.deinit(self.base.allocator); |
| 3419 | 3418 | self.blocks.deinit(self.base.allocator); |
| 3420 | | self.text_block_free_list.deinit(self.base.allocator); |
| 3421 | | |
| 3419 | { |
| 3420 | var it = self.block_free_lists.valueIterator(); |
| 3421 | while (it.next()) |free_list| { |
| 3422 | free_list.deinit(self.base.allocator); |
| 3423 | } |
| 3424 | self.block_free_lists.deinit(self.base.allocator); |
| 3425 | } |
| 3422 | 3426 | for (self.decls.keys()) |decl| { |
| 3423 | 3427 | decl.link.macho.deinit(self.base.allocator); |
| 3424 | 3428 | } |
| ... | ... | @@ -3441,16 +3445,21 @@ fn freeTextBlock(self: *MachO, text_block: *TextBlock) void { |
| 3441 | 3445 | log.debug("freeTextBlock {*}", .{text_block}); |
| 3442 | 3446 | text_block.deinit(self.base.allocator); |
| 3443 | 3447 | |
| 3448 | const match = MatchingSection{ |
| 3449 | .seg = self.text_segment_cmd_index.?, |
| 3450 | .sect = self.text_section_index.?, |
| 3451 | }; |
| 3452 | const text_block_free_list = self.block_free_lists.getPtr(match).?; |
| 3444 | 3453 | var already_have_free_list_node = false; |
| 3445 | 3454 | { |
| 3446 | 3455 | var i: usize = 0; |
| 3447 | 3456 | // TODO turn text_block_free_list into a hash map |
| 3448 | | while (i < self.text_block_free_list.items.len) { |
| 3449 | | if (self.text_block_free_list.items[i] == text_block) { |
| 3450 | | _ = self.text_block_free_list.swapRemove(i); |
| 3457 | while (i < text_block_free_list.items.len) { |
| 3458 | if (text_block_free_list.items[i] == text_block) { |
| 3459 | _ = text_block_free_list.swapRemove(i); |
| 3451 | 3460 | continue; |
| 3452 | 3461 | } |
| 3453 | | if (self.text_block_free_list.items[i] == text_block.prev) { |
| 3462 | if (text_block_free_list.items[i] == text_block.prev) { |
| 3454 | 3463 | already_have_free_list_node = true; |
| 3455 | 3464 | } |
| 3456 | 3465 | i += 1; |
| ... | ... | @@ -3458,10 +3467,15 @@ fn freeTextBlock(self: *MachO, text_block: *TextBlock) void { |
| 3458 | 3467 | } |
| 3459 | 3468 | // TODO process free list for dbg info just like we do above for vaddrs |
| 3460 | 3469 | |
| 3461 | | if (self.last_text_block == text_block) { |
| 3462 | | // TODO shrink the __text section size here |
| 3463 | | self.last_text_block = text_block.prev; |
| 3470 | if (self.blocks.getPtr(match)) |last_text_block| { |
| 3471 | if (last_text_block.* == text_block) { |
| 3472 | if (text_block.prev) |prev| { |
| 3473 | // TODO shrink the __text section size here |
| 3474 | last_text_block.* = prev; |
| 3475 | } |
| 3476 | } |
| 3464 | 3477 | } |
| 3478 | |
| 3465 | 3479 | if (self.d_sym) |*ds| { |
| 3466 | 3480 | if (ds.dbg_info_decl_first == text_block) { |
| 3467 | 3481 | ds.dbg_info_decl_first = text_block.dbg_info_next; |
| ... | ... | @@ -3478,7 +3492,7 @@ fn freeTextBlock(self: *MachO, text_block: *TextBlock) void { |
| 3478 | 3492 | if (!already_have_free_list_node and prev.freeListEligible(self.*)) { |
| 3479 | 3493 | // The free list is heuristics, it doesn't have to be perfect, so we can ignore |
| 3480 | 3494 | // the OOM here. |
| 3481 | | self.text_block_free_list.append(self.base.allocator, prev) catch {}; |
| 3495 | text_block_free_list.append(self.base.allocator, prev) catch {}; |
| 3482 | 3496 | } |
| 3483 | 3497 | } else { |
| 3484 | 3498 | text_block.prev = null; |
| ... | ... | @@ -4035,10 +4049,12 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 4035 | 4049 | .@"align" = alignment, |
| 4036 | 4050 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, |
| 4037 | 4051 | }); |
| 4038 | | _ = try self.section_ordinals.getOrPut(self.base.allocator, .{ |
| 4052 | const match = MatchingSection{ |
| 4039 | 4053 | .seg = self.text_segment_cmd_index.?, |
| 4040 | 4054 | .sect = self.text_section_index.?, |
| 4041 | | }); |
| 4055 | }; |
| 4056 | _ = try self.section_ordinals.getOrPut(self.base.allocator, match); |
| 4057 | try self.block_free_lists.putNoClobber(self.base.allocator, match, .{}); |
| 4042 | 4058 | self.load_commands_dirty = true; |
| 4043 | 4059 | } |
| 4044 | 4060 | |
| ... | ... | @@ -4070,10 +4086,12 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 4070 | 4086 | .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, |
| 4071 | 4087 | .reserved2 = stub_size, |
| 4072 | 4088 | }); |
| 4073 | | _ = try self.section_ordinals.getOrPut(self.base.allocator, .{ |
| 4089 | const match = MatchingSection{ |
| 4074 | 4090 | .seg = self.text_segment_cmd_index.?, |
| 4075 | 4091 | .sect = self.stubs_section_index.?, |
| 4076 | | }); |
| 4092 | }; |
| 4093 | _ = try self.section_ordinals.getOrPut(self.base.allocator, match); |
| 4094 | try self.block_free_lists.putNoClobber(self.base.allocator, match, .{}); |
| 4077 | 4095 | self.load_commands_dirty = true; |
| 4078 | 4096 | } |
| 4079 | 4097 | |
| ... | ... | @@ -4103,10 +4121,12 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 4103 | 4121 | .@"align" = alignment, |
| 4104 | 4122 | .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, |
| 4105 | 4123 | }); |
| 4106 | | _ = try self.section_ordinals.getOrPut(self.base.allocator, .{ |
| 4124 | const match = MatchingSection{ |
| 4107 | 4125 | .seg = self.text_segment_cmd_index.?, |
| 4108 | 4126 | .sect = self.stub_helper_section_index.?, |
| 4109 | | }); |
| 4127 | }; |
| 4128 | _ = try self.section_ordinals.getOrPut(self.base.allocator, match); |
| 4129 | try self.block_free_lists.putNoClobber(self.base.allocator, match, .{}); |
| 4110 | 4130 | self.load_commands_dirty = true; |
| 4111 | 4131 | } |
| 4112 | 4132 | |
| ... | ... | @@ -4148,10 +4168,12 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 4148 | 4168 | .@"align" = 3, // 2^3 = @sizeOf(u64) |
| 4149 | 4169 | .flags = macho.S_NON_LAZY_SYMBOL_POINTERS, |
| 4150 | 4170 | }); |
| 4151 | | _ = try self.section_ordinals.getOrPut(self.base.allocator, .{ |
| 4171 | const match = MatchingSection{ |
| 4152 | 4172 | .seg = self.data_const_segment_cmd_index.?, |
| 4153 | 4173 | .sect = self.got_section_index.?, |
| 4154 | | }); |
| 4174 | }; |
| 4175 | _ = try self.section_ordinals.getOrPut(self.base.allocator, match); |
| 4176 | try self.block_free_lists.putNoClobber(self.base.allocator, match, .{}); |
| 4155 | 4177 | self.load_commands_dirty = true; |
| 4156 | 4178 | } |
| 4157 | 4179 | |
| ... | ... | @@ -4193,10 +4215,12 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 4193 | 4215 | .@"align" = 3, // 2^3 = @sizeOf(u64) |
| 4194 | 4216 | .flags = macho.S_LAZY_SYMBOL_POINTERS, |
| 4195 | 4217 | }); |
| 4196 | | _ = try self.section_ordinals.getOrPut(self.base.allocator, .{ |
| 4218 | const match = MatchingSection{ |
| 4197 | 4219 | .seg = self.data_segment_cmd_index.?, |
| 4198 | 4220 | .sect = self.la_symbol_ptr_section_index.?, |
| 4199 | | }); |
| 4221 | }; |
| 4222 | _ = try self.section_ordinals.getOrPut(self.base.allocator, match); |
| 4223 | try self.block_free_lists.putNoClobber(self.base.allocator, match, .{}); |
| 4200 | 4224 | self.load_commands_dirty = true; |
| 4201 | 4225 | } |
| 4202 | 4226 | |
| ... | ... | @@ -4216,10 +4240,12 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 4216 | 4240 | .offset = @intCast(u32, off), |
| 4217 | 4241 | .@"align" = 3, // 2^3 = @sizeOf(u64) |
| 4218 | 4242 | }); |
| 4219 | | _ = try self.section_ordinals.getOrPut(self.base.allocator, .{ |
| 4243 | const match = MatchingSection{ |
| 4220 | 4244 | .seg = self.data_segment_cmd_index.?, |
| 4221 | 4245 | .sect = self.data_section_index.?, |
| 4222 | | }); |
| 4246 | }; |
| 4247 | _ = try self.section_ordinals.getOrPut(self.base.allocator, match); |
| 4248 | try self.block_free_lists.putNoClobber(self.base.allocator, match, .{}); |
| 4223 | 4249 | self.load_commands_dirty = true; |
| 4224 | 4250 | } |
| 4225 | 4251 | |
| ... | ... | @@ -4470,6 +4496,11 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 4470 | 4496 | fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { |
| 4471 | 4497 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 4472 | 4498 | const text_section = &text_segment.sections.items[self.text_section_index.?]; |
| 4499 | const match = MatchingSection{ |
| 4500 | .seg = self.text_segment_cmd_index.?, |
| 4501 | .sect = self.text_section_index.?, |
| 4502 | }; |
| 4503 | const text_block_free_list = self.block_free_lists.getPtr(match).?; |
| 4473 | 4504 | const new_block_ideal_capacity = padToIdeal(new_block_size); |
| 4474 | 4505 | |
| 4475 | 4506 | // We use these to indicate our intention to update metadata, placing the new block, |
| ... | ... | @@ -4484,8 +4515,8 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 4484 | 4515 | // The list is unordered. We'll just take the first thing that works. |
| 4485 | 4516 | const vaddr = blk: { |
| 4486 | 4517 | var i: usize = 0; |
| 4487 | | while (i < self.text_block_free_list.items.len) { |
| 4488 | | const big_block = self.text_block_free_list.items[i]; |
| 4518 | while (i < text_block_free_list.items.len) { |
| 4519 | const big_block = text_block_free_list.items[i]; |
| 4489 | 4520 | // We now have a pointer to a live text block that has too much capacity. |
| 4490 | 4521 | // Is it enough that we could fit this new text block? |
| 4491 | 4522 | const sym = self.locals.items[big_block.local_sym_index]; |
| ... | ... | @@ -4500,7 +4531,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 4500 | 4531 | // should be deleted because the block that it points to has grown to take up |
| 4501 | 4532 | // more of the extra capacity. |
| 4502 | 4533 | if (!big_block.freeListEligible(self.*)) { |
| 4503 | | const bl = self.text_block_free_list.swapRemove(i); |
| 4534 | const bl = text_block_free_list.swapRemove(i); |
| 4504 | 4535 | bl.deinit(self.base.allocator); |
| 4505 | 4536 | } else { |
| 4506 | 4537 | i += 1; |
| ... | ... | @@ -4519,7 +4550,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 4519 | 4550 | free_list_removal = i; |
| 4520 | 4551 | } |
| 4521 | 4552 | break :blk new_start_vaddr; |
| 4522 | | } else if (self.last_text_block) |last| { |
| 4553 | } else if (self.blocks.get(match)) |last| { |
| 4523 | 4554 | const last_symbol = self.locals.items[last.local_sym_index]; |
| 4524 | 4555 | // TODO We should pad out the excess capacity with NOPs. For executables, |
| 4525 | 4556 | // no padding seems to be OK, but it will probably not be for objects. |
| ... | ... | @@ -4538,7 +4569,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 4538 | 4569 | const needed_size = (vaddr + new_block_size) - text_section.addr; |
| 4539 | 4570 | assert(needed_size <= text_segment.inner.filesize); // TODO must move the entire text section. |
| 4540 | 4571 | |
| 4541 | | self.last_text_block = text_block; |
| 4572 | _ = try self.blocks.getOrPutValue(self.base.allocator, match, text_block); |
| 4542 | 4573 | text_section.size = needed_size; |
| 4543 | 4574 | self.load_commands_dirty = true; // TODO Make more granular. |
| 4544 | 4575 | |
| ... | ... | @@ -4567,7 +4598,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 4567 | 4598 | text_block.next = null; |
| 4568 | 4599 | } |
| 4569 | 4600 | if (free_list_removal) |i| { |
| 4570 | | _ = self.text_block_free_list.swapRemove(i); |
| 4601 | _ = text_block_free_list.swapRemove(i); |
| 4571 | 4602 | } |
| 4572 | 4603 | |
| 4573 | 4604 | return vaddr; |