| author | |
| committer | |
| log | b44dd599ad4a0f8a6684acd6e3c5a02f3c7b8f04 |
| tree | 8b6df85ad68443e229429d30ef9dd3f24484d3cf |
| parent | f87dd43c1285d38d7a0f3092f6487bf1e1f4faa6 |
3 files changed, 136 insertions(+), 141 deletions(-)
src/link/Elf.zig+77| ... | ... | @@ -675,6 +675,83 @@ pub fn markDirty(self: *Elf, shdr_index: u32) void { |
| 675 | 675 | } |
| 676 | 676 | } |
| 677 | 677 | |
| 678 | const AllocateChunkResult = struct { | |
| 679 | value: u64, | |
| 680 | placement: Ref, | |
| 681 | }; | |
| 682 | ||
| 683 | pub fn allocateChunk(self: *Elf, shndx: u32, size: u64, alignment: Atom.Alignment) !AllocateChunkResult { | |
| 684 | const slice = self.sections.slice(); | |
| 685 | const shdr = &slice.items(.shdr)[shndx]; | |
| 686 | const free_list = &slice.items(.free_list)[shndx]; | |
| 687 | const last_atom_ref = &slice.items(.last_atom)[shndx]; | |
| 688 | const new_atom_ideal_capacity = padToIdeal(size); | |
| 689 | ||
| 690 | // First we look for an appropriately sized free list node. | |
| 691 | // The list is unordered. We'll just take the first thing that works. | |
| 692 | const res: AllocateChunkResult = blk: { | |
| 693 | var i: usize = if (self.base.child_pid == null) 0 else free_list.items.len; | |
| 694 | while (i < free_list.items.len) { | |
| 695 | const big_atom_ref = free_list.items[i]; | |
| 696 | const big_atom = self.atom(big_atom_ref).?; | |
| 697 | // We now have a pointer to a live atom that has too much capacity. | |
| 698 | // Is it enough that we could fit this new atom? | |
| 699 | const cap = big_atom.capacity(self); | |
| 700 | const ideal_capacity = padToIdeal(cap); | |
| 701 | const ideal_capacity_end_vaddr = std.math.add(u64, @intCast(big_atom.value), ideal_capacity) catch ideal_capacity; | |
| 702 | const capacity_end_vaddr = @as(u64, @intCast(big_atom.value)) + cap; | |
| 703 | const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity; | |
| 704 | const new_start_vaddr = alignment.backward(new_start_vaddr_unaligned); | |
| 705 | if (new_start_vaddr < ideal_capacity_end_vaddr) { | |
| 706 | // Additional bookkeeping here to notice if this free list node | |
| 707 | // should be deleted because the block that it points to has grown to take up | |
| 708 | // more of the extra capacity. | |
| 709 | if (!big_atom.freeListEligible(self)) { | |
| 710 | _ = free_list.swapRemove(i); | |
| 711 | } else { | |
| 712 | i += 1; | |
| 713 | } | |
| 714 | continue; | |
| 715 | } | |
| 716 | // At this point we know that we will place the new block here. But the | |
| 717 | // remaining question is whether there is still yet enough capacity left | |
| 718 | // over for there to still be a free list node. | |
| 719 | const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr; | |
| 720 | const keep_free_list_node = remaining_capacity >= min_text_capacity; | |
| 721 | ||
| 722 | if (!keep_free_list_node) { | |
| 723 | _ = free_list.swapRemove(i); | |
| 724 | } | |
| 725 | break :blk .{ .value = new_start_vaddr, .placement = big_atom_ref }; | |
| 726 | } else if (self.atom(last_atom_ref.*)) |last_atom| { | |
| 727 | const ideal_capacity = padToIdeal(last_atom.size); | |
| 728 | const ideal_capacity_end_vaddr = @as(u64, @intCast(last_atom.value)) + ideal_capacity; | |
| 729 | const new_start_vaddr = alignment.forward(ideal_capacity_end_vaddr); | |
| 730 | break :blk .{ .value = new_start_vaddr, .placement = last_atom.ref() }; | |
| 731 | } else { | |
| 732 | break :blk .{ .value = 0, .placement = .{} }; | |
| 733 | } | |
| 734 | }; | |
| 735 | ||
| 736 | log.debug("allocated chunk (size({x}),align({x})) at 0x{x} (file(0x{x}))", .{ | |
| 737 | size, | |
| 738 | alignment.toByteUnits().?, | |
| 739 | shdr.sh_addr + res.value, | |
| 740 | shdr.sh_offset + res.value, | |
| 741 | }); | |
| 742 | ||
| 743 | const expand_section = if (self.atom(res.placement)) |placement_atom| | |
| 744 | placement_atom.nextAtom(self) == null | |
| 745 | else | |
| 746 | true; | |
| 747 | if (expand_section) { | |
| 748 | const needed_size = res.value + size; | |
| 749 | try self.growAllocSection(shndx, needed_size); | |
| 750 | } | |
| 751 | ||
| 752 | return res; | |
| 753 | } | |
| 754 | ||
| 678 | 755 | pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { |
| 679 | 756 | const use_lld = build_options.have_llvm and self.base.comp.config.use_lld; |
| 680 | 757 | if (use_lld) { |
src/link/Elf/Atom.zig-134| ... | ... | @@ -123,140 +123,6 @@ pub fn freeListEligible(self: Atom, elf_file: *Elf) bool { |
| 123 | 123 | return surplus >= Elf.min_text_capacity; |
| 124 | 124 | } |
| 125 | 125 | |
| 126 | pub fn allocate(self: *Atom, elf_file: *Elf) !void { | |
| 127 | const slice = elf_file.sections.slice(); | |
| 128 | const shdr = &slice.items(.shdr)[self.output_section_index]; | |
| 129 | const free_list = &slice.items(.free_list)[self.output_section_index]; | |
| 130 | const last_atom_ref = &slice.items(.last_atom)[self.output_section_index]; | |
| 131 | const new_atom_ideal_capacity = Elf.padToIdeal(self.size); | |
| 132 | ||
| 133 | // We use these to indicate our intention to update metadata, placing the new atom, | |
| 134 | // and possibly removing a free list node. | |
| 135 | // It would be simpler to do it inside the for loop below, but that would cause a | |
| 136 | // problem if an error was returned later in the function. So this action | |
| 137 | // is actually carried out at the end of the function, when errors are no longer possible. | |
| 138 | var atom_placement: ?Elf.Ref = null; | |
| 139 | var free_list_removal: ?usize = null; | |
| 140 | ||
| 141 | // First we look for an appropriately sized free list node. | |
| 142 | // The list is unordered. We'll just take the first thing that works. | |
| 143 | self.value = blk: { | |
| 144 | var i: usize = if (elf_file.base.child_pid == null) 0 else free_list.items.len; | |
| 145 | while (i < free_list.items.len) { | |
| 146 | const big_atom_ref = free_list.items[i]; | |
| 147 | const big_atom = elf_file.atom(big_atom_ref).?; | |
| 148 | // We now have a pointer to a live atom that has too much capacity. | |
| 149 | // Is it enough that we could fit this new atom? | |
| 150 | const cap = big_atom.capacity(elf_file); | |
| 151 | const ideal_capacity = Elf.padToIdeal(cap); | |
| 152 | const ideal_capacity_end_vaddr = std.math.add(u64, @intCast(big_atom.value), ideal_capacity) catch ideal_capacity; | |
| 153 | const capacity_end_vaddr = @as(u64, @intCast(big_atom.value)) + cap; | |
| 154 | const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity; | |
| 155 | const new_start_vaddr = self.alignment.backward(new_start_vaddr_unaligned); | |
| 156 | if (new_start_vaddr < ideal_capacity_end_vaddr) { | |
| 157 | // Additional bookkeeping here to notice if this free list node | |
| 158 | // should be deleted because the block that it points to has grown to take up | |
| 159 | // more of the extra capacity. | |
| 160 | if (!big_atom.freeListEligible(elf_file)) { | |
| 161 | _ = free_list.swapRemove(i); | |
| 162 | } else { | |
| 163 | i += 1; | |
| 164 | } | |
| 165 | continue; | |
| 166 | } | |
| 167 | // At this point we know that we will place the new block here. But the | |
| 168 | // remaining question is whether there is still yet enough capacity left | |
| 169 | // over for there to still be a free list node. | |
| 170 | const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr; | |
| 171 | const keep_free_list_node = remaining_capacity >= Elf.min_text_capacity; | |
| 172 | ||
| 173 | // Set up the metadata to be updated, after errors are no longer possible. | |
| 174 | atom_placement = big_atom_ref; | |
| 175 | if (!keep_free_list_node) { | |
| 176 | free_list_removal = i; | |
| 177 | } | |
| 178 | break :blk @intCast(new_start_vaddr); | |
| 179 | } else if (elf_file.atom(last_atom_ref.*)) |last_atom| { | |
| 180 | const ideal_capacity = Elf.padToIdeal(last_atom.size); | |
| 181 | const ideal_capacity_end_vaddr = @as(u64, @intCast(last_atom.value)) + ideal_capacity; | |
| 182 | const new_start_vaddr = self.alignment.forward(ideal_capacity_end_vaddr); | |
| 183 | // Set up the metadata to be updated, after errors are no longer possible. | |
| 184 | atom_placement = last_atom.ref(); | |
| 185 | break :blk @intCast(new_start_vaddr); | |
| 186 | } else { | |
| 187 | break :blk 0; | |
| 188 | } | |
| 189 | }; | |
| 190 | ||
| 191 | log.debug("allocated atom({}) : '{s}' at 0x{x} to 0x{x}", .{ | |
| 192 | self.ref(), | |
| 193 | self.name(elf_file), | |
| 194 | self.address(elf_file), | |
| 195 | self.address(elf_file) + @as(i64, @intCast(self.size)), | |
| 196 | }); | |
| 197 | ||
| 198 | const expand_section = if (atom_placement) |placement_ref| | |
| 199 | elf_file.atom(placement_ref).?.nextAtom(elf_file) == null | |
| 200 | else | |
| 201 | true; | |
| 202 | if (expand_section) { | |
| 203 | const needed_size: u64 = @intCast(self.value + @as(i64, @intCast(self.size))); | |
| 204 | try elf_file.growAllocSection(self.output_section_index, needed_size); | |
| 205 | last_atom_ref.* = self.ref(); | |
| 206 | ||
| 207 | switch (self.file(elf_file).?) { | |
| 208 | .zig_object => |zo| if (zo.dwarf) |_| { | |
| 209 | // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address | |
| 210 | // range of the compilation unit. When we expand the text section, this range changes, | |
| 211 | // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty. | |
| 212 | zo.debug_info_section_dirty = true; | |
| 213 | // This becomes dirty for the same reason. We could potentially make this more | |
| 214 | // fine-grained with the addition of support for more compilation units. It is planned to | |
| 215 | // model each package as a different compilation unit. | |
| 216 | zo.debug_aranges_section_dirty = true; | |
| 217 | zo.debug_rnglists_section_dirty = true; | |
| 218 | }, | |
| 219 | else => {}, | |
| 220 | } | |
| 221 | } | |
| 222 | shdr.sh_addralign = @max(shdr.sh_addralign, self.alignment.toByteUnits().?); | |
| 223 | ||
| 224 | // This function can also reallocate an atom. | |
| 225 | // In this case we need to "unplug" it from its previous location before | |
| 226 | // plugging it in to its new location. | |
| 227 | if (self.prevAtom(elf_file)) |prev| { | |
| 228 | prev.next_atom_ref = self.next_atom_ref; | |
| 229 | } | |
| 230 | if (self.nextAtom(elf_file)) |next| { | |
| 231 | next.prev_atom_ref = self.prev_atom_ref; | |
| 232 | } | |
| 233 | ||
| 234 | if (atom_placement) |big_atom_ref| { | |
| 235 | const big_atom = elf_file.atom(big_atom_ref).?; | |
| 236 | self.prev_atom_ref = big_atom_ref; | |
| 237 | self.next_atom_ref = big_atom.next_atom_ref; | |
| 238 | big_atom.next_atom_ref = self.ref(); | |
| 239 | } else { | |
| 240 | self.prev_atom_ref = .{ .index = 0, .file = 0 }; | |
| 241 | self.next_atom_ref = .{ .index = 0, .file = 0 }; | |
| 242 | } | |
| 243 | if (free_list_removal) |i| { | |
| 244 | _ = free_list.swapRemove(i); | |
| 245 | } | |
| 246 | ||
| 247 | self.alive = true; | |
| 248 | } | |
| 249 | ||
| 250 | pub fn shrink(self: *Atom, elf_file: *Elf) void { | |
| 251 | _ = self; | |
| 252 | _ = elf_file; | |
| 253 | } | |
| 254 | ||
| 255 | pub fn grow(self: *Atom, elf_file: *Elf) !void { | |
| 256 | if (!self.alignment.check(@intCast(self.value)) or self.size > self.capacity(elf_file)) | |
| 257 | try self.allocate(elf_file); | |
| 258 | } | |
| 259 | ||
| 260 | 126 | pub fn free(self: *Atom, elf_file: *Elf) void { |
| 261 | 127 | log.debug("freeAtom atom({}) ({s})", .{ self.ref(), self.name(elf_file) }); |
| 262 | 128 |
src/link/Elf/ZigObject.zig+59-7| ... | ... | @@ -1362,19 +1362,18 @@ fn updateNavCode( |
| 1362 | 1362 | const capacity = atom_ptr.capacity(elf_file); |
| 1363 | 1363 | const need_realloc = code.len > capacity or !required_alignment.check(@intCast(atom_ptr.value)); |
| 1364 | 1364 | if (need_realloc) { |
| 1365 | try atom_ptr.grow(elf_file); | |
| 1365 | try self.growAtom(atom_ptr, elf_file); | |
| 1366 | 1366 | log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom_ptr.value }); |
| 1367 | 1367 | if (old_vaddr != atom_ptr.value) { |
| 1368 | 1368 | sym.value = 0; |
| 1369 | 1369 | esym.st_value = 0; |
| 1370 | 1370 | } |
| 1371 | 1371 | } else if (code.len < old_size) { |
| 1372 | atom_ptr.shrink(elf_file); | |
| 1372 | // TODO shrink section size | |
| 1373 | 1373 | } |
| 1374 | 1374 | } else { |
| 1375 | try atom_ptr.allocate(elf_file); | |
| 1375 | try self.allocateAtom(atom_ptr, elf_file); | |
| 1376 | 1376 | errdefer self.freeNavMetadata(elf_file, sym_index); |
| 1377 | ||
| 1378 | 1377 | sym.value = 0; |
| 1379 | 1378 | esym.st_value = 0; |
| 1380 | 1379 | } |
| ... | ... | @@ -1739,7 +1738,7 @@ fn updateLazySymbol( |
| 1739 | 1738 | atom_ptr.size = code.len; |
| 1740 | 1739 | atom_ptr.output_section_index = output_section_index; |
| 1741 | 1740 | |
| 1742 | try atom_ptr.allocate(elf_file); | |
| 1741 | try self.allocateAtom(atom_ptr, elf_file); | |
| 1743 | 1742 | errdefer self.freeNavMetadata(elf_file, symbol_index); |
| 1744 | 1743 | |
| 1745 | 1744 | local_sym.value = 0; |
| ... | ... | @@ -1797,8 +1796,7 @@ fn lowerConst( |
| 1797 | 1796 | atom_ptr.size = code.len; |
| 1798 | 1797 | atom_ptr.output_section_index = output_section_index; |
| 1799 | 1798 | |
| 1800 | try atom_ptr.allocate(elf_file); | |
| 1801 | // TODO rename and re-audit this method | |
| 1799 | try self.allocateAtom(atom_ptr, elf_file); | |
| 1802 | 1800 | errdefer self.freeNavMetadata(elf_file, sym_index); |
| 1803 | 1801 | |
| 1804 | 1802 | const shdr = elf_file.sections.items(.shdr)[output_section_index]; |
| ... | ... | @@ -1998,6 +1996,60 @@ fn writeTrampoline(tr_sym: Symbol, target: Symbol, elf_file: *Elf) !void { |
| 1998 | 1996 | } |
| 1999 | 1997 | } |
| 2000 | 1998 | |
| 1999 | fn allocateAtom(self: *ZigObject, atom_ptr: *Atom, elf_file: *Elf) !void { | |
| 2000 | const alloc_res = try elf_file.allocateChunk(atom_ptr.output_section_index, atom_ptr.size, atom_ptr.alignment); | |
| 2001 | atom_ptr.value = @intCast(alloc_res.value); | |
| 2002 | ||
| 2003 | const slice = elf_file.sections.slice(); | |
| 2004 | const shdr = &slice.items(.shdr)[atom_ptr.output_section_index]; | |
| 2005 | const last_atom_ref = &slice.items(.last_atom)[atom_ptr.output_section_index]; | |
| 2006 | ||
| 2007 | const expand_section = if (elf_file.atom(alloc_res.placement)) |placement_atom| | |
| 2008 | placement_atom.nextAtom(elf_file) == null | |
| 2009 | else | |
| 2010 | true; | |
| 2011 | if (expand_section) { | |
| 2012 | last_atom_ref.* = atom_ptr.ref(); | |
| 2013 | if (self.dwarf) |_| { | |
| 2014 | // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address | |
| 2015 | // range of the compilation unit. When we expand the text section, this range changes, | |
| 2016 | // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty. | |
| 2017 | self.debug_info_section_dirty = true; | |
| 2018 | // This becomes dirty for the same reason. We could potentially make this more | |
| 2019 | // fine-grained with the addition of support for more compilation units. It is planned to | |
| 2020 | // model each package as a different compilation unit. | |
| 2021 | self.debug_aranges_section_dirty = true; | |
| 2022 | self.debug_rnglists_section_dirty = true; | |
| 2023 | } | |
| 2024 | } | |
| 2025 | shdr.sh_addralign = @max(shdr.sh_addralign, atom_ptr.alignment.toByteUnits().?); | |
| 2026 | ||
| 2027 | // This function can also reallocate an atom. | |
| 2028 | // In this case we need to "unplug" it from its previous location before | |
| 2029 | // plugging it in to its new location. | |
| 2030 | if (atom_ptr.prevAtom(elf_file)) |prev| { | |
| 2031 | prev.next_atom_ref = atom_ptr.next_atom_ref; | |
| 2032 | } | |
| 2033 | if (atom_ptr.nextAtom(elf_file)) |next| { | |
| 2034 | next.prev_atom_ref = atom_ptr.prev_atom_ref; | |
| 2035 | } | |
| 2036 | ||
| 2037 | if (elf_file.atom(alloc_res.placement)) |big_atom| { | |
| 2038 | atom_ptr.prev_atom_ref = alloc_res.placement; | |
| 2039 | atom_ptr.next_atom_ref = big_atom.next_atom_ref; | |
| 2040 | big_atom.next_atom_ref = atom_ptr.ref(); | |
| 2041 | } else { | |
| 2042 | atom_ptr.prev_atom_ref = .{ .index = 0, .file = 0 }; | |
| 2043 | atom_ptr.next_atom_ref = .{ .index = 0, .file = 0 }; | |
| 2044 | } | |
| 2045 | } | |
| 2046 | ||
| 2047 | fn growAtom(self: *ZigObject, atom_ptr: *Atom, elf_file: *Elf) !void { | |
| 2048 | if (!atom_ptr.alignment.check(@intCast(atom_ptr.value)) or atom_ptr.size > atom_ptr.capacity(elf_file)) { | |
| 2049 | try self.allocateAtom(atom_ptr, elf_file); | |
| 2050 | } | |
| 2051 | } | |
| 2052 | ||
| 2001 | 2053 | pub fn asFile(self: *ZigObject) File { |
| 2002 | 2054 | return .{ .zig_object = self }; |
| 2003 | 2055 | } |