authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-08-25 15:11:21+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-08-25 15:11:21+02:00
logaf57ccbe279d73f91358ec28fb4afd54868650e8
treea23a15e7337b088c8757e1a8968add84b05ee123
parentea4bd2b87962794233df1693cdea3da266e27b86

macho: generalise free list usage to all sections


1 files changed, 63 insertions(+), 32 deletions(-)

src/link/MachO.zig+63-32
...@@ -194,10 +194,10 @@ section_ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{},...@@ -194,10 +194,10 @@ section_ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{},
194/// overcapacity can be negative. A simple way to have negative overcapacity is to194/// overcapacity can be negative. A simple way to have negative overcapacity is to
195/// allocate a fresh text block, which will have ideal capacity, and then grow it195/// allocate a fresh text block, which will have ideal capacity, and then grow it
196/// by 1 byte. It will then have -1 overcapacity.196/// by 1 byte. It will then have -1 overcapacity.
197text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},197block_free_lists: std.AutoHashMapUnmanaged(MatchingSection, std.ArrayListUnmanaged(*TextBlock)) = .{},
198198
199/// Pointer to the last allocated text block199/// Pointer to the last allocated text block
200last_text_block: ?*TextBlock = null,200blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{},
201201
202/// List of TextBlocks that are owned directly by the linker.202/// List of TextBlocks that are owned directly by the linker.
203/// Currently these are only TextBlocks that are the result of linking203/// Currently these are only TextBlocks that are the result of linking
...@@ -206,8 +206,6 @@ last_text_block: ?*TextBlock = null,...@@ -206,8 +206,6 @@ last_text_block: ?*TextBlock = null,
206/// TODO consolidate this.206/// TODO consolidate this.
207managed_blocks: std.ArrayListUnmanaged(*TextBlock) = .{},207managed_blocks: std.ArrayListUnmanaged(*TextBlock) = .{},
208208
209blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{},
210
211/// Table of Decls that are currently alive.209/// Table of Decls that are currently alive.
212/// We store them here so that we can properly dispose of any allocated210/// We store them here so that we can properly dispose of any allocated
213/// memory within the TextBlock in the incremental linker.211/// memory within the TextBlock in the incremental linker.
...@@ -1535,6 +1533,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -1535,6 +1533,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
15351533
1536 if (res) |match| {1534 if (res) |match| {
1537 _ = try self.section_ordinals.getOrPut(self.base.allocator, match);1535 _ = try self.section_ordinals.getOrPut(self.base.allocator, match);
1536 _ = try self.block_free_lists.getOrPutValue(self.base.allocator, match, .{});
1538 }1537 }
15391538
1540 return res;1539 return res;
...@@ -3417,8 +3416,13 @@ pub fn deinit(self: *MachO) void {...@@ -3417,8 +3416,13 @@ pub fn deinit(self: *MachO) void {
3417 }3416 }
3418 self.managed_blocks.deinit(self.base.allocator);3417 self.managed_blocks.deinit(self.base.allocator);
3419 self.blocks.deinit(self.base.allocator);3418 self.blocks.deinit(self.base.allocator);
3420 self.text_block_free_list.deinit(self.base.allocator);3419 {
34213420 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 for (self.decls.keys()) |decl| {3426 for (self.decls.keys()) |decl| {
3423 decl.link.macho.deinit(self.base.allocator);3427 decl.link.macho.deinit(self.base.allocator);
3424 }3428 }
...@@ -3441,16 +3445,21 @@ fn freeTextBlock(self: *MachO, text_block: *TextBlock) void {...@@ -3441,16 +3445,21 @@ fn freeTextBlock(self: *MachO, text_block: *TextBlock) void {
3441 log.debug("freeTextBlock {*}", .{text_block});3445 log.debug("freeTextBlock {*}", .{text_block});
3442 text_block.deinit(self.base.allocator);3446 text_block.deinit(self.base.allocator);
34433447
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 var already_have_free_list_node = false;3453 var already_have_free_list_node = false;
3445 {3454 {
3446 var i: usize = 0;3455 var i: usize = 0;
3447 // TODO turn text_block_free_list into a hash map3456 // TODO turn text_block_free_list into a hash map
3448 while (i < self.text_block_free_list.items.len) {3457 while (i < text_block_free_list.items.len) {
3449 if (self.text_block_free_list.items[i] == text_block) {3458 if (text_block_free_list.items[i] == text_block) {
3450 _ = self.text_block_free_list.swapRemove(i);3459 _ = text_block_free_list.swapRemove(i);
3451 continue;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 already_have_free_list_node = true;3463 already_have_free_list_node = true;
3455 }3464 }
3456 i += 1;3465 i += 1;
...@@ -3458,10 +3467,15 @@ fn freeTextBlock(self: *MachO, text_block: *TextBlock) void {...@@ -3458,10 +3467,15 @@ fn freeTextBlock(self: *MachO, text_block: *TextBlock) void {
3458 }3467 }
3459 // TODO process free list for dbg info just like we do above for vaddrs3468 // TODO process free list for dbg info just like we do above for vaddrs
34603469
3461 if (self.last_text_block == text_block) {3470 if (self.blocks.getPtr(match)) |last_text_block| {
3462 // TODO shrink the __text section size here3471 if (last_text_block.* == text_block) {
3463 self.last_text_block = text_block.prev;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 if (self.d_sym) |*ds| {3479 if (self.d_sym) |*ds| {
3466 if (ds.dbg_info_decl_first == text_block) {3480 if (ds.dbg_info_decl_first == text_block) {
3467 ds.dbg_info_decl_first = text_block.dbg_info_next;3481 ds.dbg_info_decl_first = text_block.dbg_info_next;
...@@ -3478,7 +3492,7 @@ fn freeTextBlock(self: *MachO, text_block: *TextBlock) void {...@@ -3478,7 +3492,7 @@ fn freeTextBlock(self: *MachO, text_block: *TextBlock) void {
3478 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {3492 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
3479 // The free list is heuristics, it doesn't have to be perfect, so we can ignore3493 // The free list is heuristics, it doesn't have to be perfect, so we can ignore
3480 // the OOM here.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 } else {3497 } else {
3484 text_block.prev = null;3498 text_block.prev = null;
...@@ -4035,10 +4049,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -4035,10 +4049,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {
4035 .@"align" = alignment,4049 .@"align" = alignment,
4036 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,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 .seg = self.text_segment_cmd_index.?,4053 .seg = self.text_segment_cmd_index.?,
4040 .sect = self.text_section_index.?,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 self.load_commands_dirty = true;4058 self.load_commands_dirty = true;
4043 }4059 }
40444060
...@@ -4070,10 +4086,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -4070,10 +4086,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {
4070 .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,4086 .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
4071 .reserved2 = stub_size,4087 .reserved2 = stub_size,
4072 });4088 });
4073 _ = try self.section_ordinals.getOrPut(self.base.allocator, .{4089 const match = MatchingSection{
4074 .seg = self.text_segment_cmd_index.?,4090 .seg = self.text_segment_cmd_index.?,
4075 .sect = self.stubs_section_index.?,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 self.load_commands_dirty = true;4095 self.load_commands_dirty = true;
4078 }4096 }
40794097
...@@ -4103,10 +4121,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -4103,10 +4121,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {
4103 .@"align" = alignment,4121 .@"align" = alignment,
4104 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,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 .seg = self.text_segment_cmd_index.?,4125 .seg = self.text_segment_cmd_index.?,
4108 .sect = self.stub_helper_section_index.?,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 self.load_commands_dirty = true;4130 self.load_commands_dirty = true;
4111 }4131 }
41124132
...@@ -4148,10 +4168,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -4148,10 +4168,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {
4148 .@"align" = 3, // 2^3 = @sizeOf(u64)4168 .@"align" = 3, // 2^3 = @sizeOf(u64)
4149 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,4169 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
4150 });4170 });
4151 _ = try self.section_ordinals.getOrPut(self.base.allocator, .{4171 const match = MatchingSection{
4152 .seg = self.data_const_segment_cmd_index.?,4172 .seg = self.data_const_segment_cmd_index.?,
4153 .sect = self.got_section_index.?,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 self.load_commands_dirty = true;4177 self.load_commands_dirty = true;
4156 }4178 }
41574179
...@@ -4193,10 +4215,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -4193,10 +4215,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {
4193 .@"align" = 3, // 2^3 = @sizeOf(u64)4215 .@"align" = 3, // 2^3 = @sizeOf(u64)
4194 .flags = macho.S_LAZY_SYMBOL_POINTERS,4216 .flags = macho.S_LAZY_SYMBOL_POINTERS,
4195 });4217 });
4196 _ = try self.section_ordinals.getOrPut(self.base.allocator, .{4218 const match = MatchingSection{
4197 .seg = self.data_segment_cmd_index.?,4219 .seg = self.data_segment_cmd_index.?,
4198 .sect = self.la_symbol_ptr_section_index.?,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 self.load_commands_dirty = true;4224 self.load_commands_dirty = true;
4201 }4225 }
42024226
...@@ -4216,10 +4240,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -4216,10 +4240,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {
4216 .offset = @intCast(u32, off),4240 .offset = @intCast(u32, off),
4217 .@"align" = 3, // 2^3 = @sizeOf(u64)4241 .@"align" = 3, // 2^3 = @sizeOf(u64)
4218 });4242 });
4219 _ = try self.section_ordinals.getOrPut(self.base.allocator, .{4243 const match = MatchingSection{
4220 .seg = self.data_segment_cmd_index.?,4244 .seg = self.data_segment_cmd_index.?,
4221 .sect = self.data_section_index.?,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 self.load_commands_dirty = true;4249 self.load_commands_dirty = true;
4224 }4250 }
42254251
...@@ -4470,6 +4496,11 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -4470,6 +4496,11 @@ pub fn populateMissingMetadata(self: *MachO) !void {
4470fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {4496fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
4471 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;4497 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
4472 const text_section = &text_segment.sections.items[self.text_section_index.?];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 const new_block_ideal_capacity = padToIdeal(new_block_size);4504 const new_block_ideal_capacity = padToIdeal(new_block_size);
44744505
4475 // We use these to indicate our intention to update metadata, placing the new block,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,8 +4515,8 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
4484 // The list is unordered. We'll just take the first thing that works.4515 // The list is unordered. We'll just take the first thing that works.
4485 const vaddr = blk: {4516 const vaddr = blk: {
4486 var i: usize = 0;4517 var i: usize = 0;
4487 while (i < self.text_block_free_list.items.len) {4518 while (i < text_block_free_list.items.len) {
4488 const big_block = self.text_block_free_list.items[i];4519 const big_block = text_block_free_list.items[i];
4489 // We now have a pointer to a live text block that has too much capacity.4520 // We now have a pointer to a live text block that has too much capacity.
4490 // Is it enough that we could fit this new text block?4521 // Is it enough that we could fit this new text block?
4491 const sym = self.locals.items[big_block.local_sym_index];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,7 +4531,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
4500 // should be deleted because the block that it points to has grown to take up4531 // should be deleted because the block that it points to has grown to take up
4501 // more of the extra capacity.4532 // more of the extra capacity.
4502 if (!big_block.freeListEligible(self.*)) {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 bl.deinit(self.base.allocator);4535 bl.deinit(self.base.allocator);
4505 } else {4536 } else {
4506 i += 1;4537 i += 1;
...@@ -4519,7 +4550,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,...@@ -4519,7 +4550,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
4519 free_list_removal = i;4550 free_list_removal = i;
4520 }4551 }
4521 break :blk new_start_vaddr;4552 break :blk new_start_vaddr;
4522 } else if (self.last_text_block) |last| {4553 } else if (self.blocks.get(match)) |last| {
4523 const last_symbol = self.locals.items[last.local_sym_index];4554 const last_symbol = self.locals.items[last.local_sym_index];
4524 // TODO We should pad out the excess capacity with NOPs. For executables,4555 // TODO We should pad out the excess capacity with NOPs. For executables,
4525 // no padding seems to be OK, but it will probably not be for objects.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,7 +4569,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
4538 const needed_size = (vaddr + new_block_size) - text_section.addr;4569 const needed_size = (vaddr + new_block_size) - text_section.addr;
4539 assert(needed_size <= text_segment.inner.filesize); // TODO must move the entire text section.4570 assert(needed_size <= text_segment.inner.filesize); // TODO must move the entire text section.
45404571
4541 self.last_text_block = text_block;4572 _ = try self.blocks.getOrPutValue(self.base.allocator, match, text_block);
4542 text_section.size = needed_size;4573 text_section.size = needed_size;
4543 self.load_commands_dirty = true; // TODO Make more granular.4574 self.load_commands_dirty = true; // TODO Make more granular.
45444575
...@@ -4567,7 +4598,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,...@@ -4567,7 +4598,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
4567 text_block.next = null;4598 text_block.next = null;
4568 }4599 }
4569 if (free_list_removal) |i| {4600 if (free_list_removal) |i| {
4570 _ = self.text_block_free_list.swapRemove(i);4601 _ = text_block_free_list.swapRemove(i);
4571 }4602 }
45724603
4573 return vaddr;4604 return vaddr;