| ... | @@ -138,11 +138,39 @@ pub const ElfFile = struct { | ... | @@ -138,11 +138,39 @@ pub const ElfFile = struct { |
| 138 | | 138 | |
| 139 | error_flags: ErrorFlags = ErrorFlags{}, | 139 | error_flags: ErrorFlags = ErrorFlags{}, |
| 140 | | 140 | |
| | 141 | /// A list of text blocks that have surplus capacity. This list can have false |
| | 142 | /// positives, as functions grow and shrink over time, only sometimes being added |
| | 143 | /// or removed from the freelist. |
| | 144 | /// |
| | 145 | /// A text block has surplus capacity when its overcapacity value is greater than |
| | 146 | /// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so |
| | 147 | /// much extra capacity, that we could fit a small new symbol in it, itself with |
| | 148 | /// ideal_capacity or more. |
| | 149 | /// |
| | 150 | /// Ideal capacity is defined by size * alloc_num / alloc_den. |
| | 151 | /// |
| | 152 | /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that |
| | 153 | /// overcapacity can be negative. A simple way to have negative overcapacity is to |
| | 154 | /// allocate a fresh text block, which will have ideal capacity, and then grow it |
| | 155 | /// by 1 byte. It will then have -1 overcapacity. |
| | 156 | free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){}, |
| | 157 | last_text_block: ?*TextBlock = null, |
| | 158 | |
| | 159 | /// `alloc_num / alloc_den` is the factor of padding when allocating. |
| | 160 | const alloc_num = 4; |
| | 161 | const alloc_den = 3; |
| | 162 | |
| | 163 | /// In order for a slice of bytes to be considered eligible to keep metadata pointing at |
| | 164 | /// it as a possible place to put new symbols, it must have enough room for this many bytes |
| | 165 | /// (plus extra for reserved capacity). |
| | 166 | const minimum_text_block_size = 64; |
| | 167 | const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den; |
| | 168 | |
| 141 | pub const ErrorFlags = struct { | 169 | pub const ErrorFlags = struct { |
| 142 | no_entry_point_found: bool = false, | 170 | no_entry_point_found: bool = false, |
| 143 | }; | 171 | }; |
| 144 | | 172 | |
| 145 | pub const Decl = struct { | 173 | pub const TextBlock = struct { |
| 146 | /// Each decl always gets a local symbol with the fully qualified name. | 174 | /// Each decl always gets a local symbol with the fully qualified name. |
| 147 | /// The vaddr and size are found here directly. | 175 | /// The vaddr and size are found here directly. |
| 148 | /// The file offset is found by computing the vaddr offset from the section vaddr | 176 | /// The file offset is found by computing the vaddr offset from the section vaddr |
| ... | @@ -152,11 +180,43 @@ pub const ElfFile = struct { | ... | @@ -152,11 +180,43 @@ pub const ElfFile = struct { |
| 152 | local_sym_index: u32, | 180 | local_sym_index: u32, |
| 153 | /// This field is undefined for symbols with size = 0. | 181 | /// This field is undefined for symbols with size = 0. |
| 154 | offset_table_index: u32, | 182 | offset_table_index: u32, |
| | 183 | /// Points to the previous and next neighbors, based on the `text_offset`. |
| | 184 | /// This can be used to find, for example, the capacity of this `TextBlock`. |
| | 185 | prev: ?*TextBlock, |
| | 186 | next: ?*TextBlock, |
| 155 | | 187 | |
| 156 | pub const empty = Decl{ | 188 | pub const empty = TextBlock{ |
| 157 | .local_sym_index = 0, | 189 | .local_sym_index = 0, |
| 158 | .offset_table_index = undefined, | 190 | .offset_table_index = undefined, |
| | 191 | .prev = null, |
| | 192 | .next = null, |
| 159 | }; | 193 | }; |
| | 194 | |
| | 195 | /// Returns how much room there is to grow in virtual address space. |
| | 196 | /// File offset relocation happens transparently, so it is not included in |
| | 197 | /// this calculation. |
| | 198 | fn capacity(self: TextBlock, elf_file: ElfFile) u64 { |
| | 199 | const self_sym = elf_file.local_symbols.items[self.local_sym_index]; |
| | 200 | if (self.next) |next| { |
| | 201 | const next_sym = elf_file.local_symbols.items[next.local_sym_index]; |
| | 202 | return next_sym.st_value - self_sym.st_value; |
| | 203 | } else { |
| | 204 | // We are the last block. The capacity is limited only by virtual address space. |
| | 205 | return std.math.maxInt(u32) - self_sym.st_value; |
| | 206 | } |
| | 207 | } |
| | 208 | |
| | 209 | fn freeListEligible(self: TextBlock, elf_file: ElfFile) bool { |
| | 210 | // No need to keep a free list node for the last block. |
| | 211 | const next = self.next orelse return false; |
| | 212 | const self_sym = elf_file.local_symbols.items[self.local_sym_index]; |
| | 213 | const next_sym = elf_file.local_symbols.items[next.local_sym_index]; |
| | 214 | const cap = next_sym.st_value - self_sym.st_value; |
| | 215 | const ideal_cap = self_sym.st_size * alloc_num / alloc_den; |
| | 216 | if (cap <= ideal_cap) return false; |
| | 217 | const surplus = cap - ideal_cap; |
| | 218 | return surplus >= min_text_capacity; |
| | 219 | } |
| 160 | }; | 220 | }; |
| 161 | | 221 | |
| 162 | pub const Export = struct { | 222 | pub const Export = struct { |
| ... | @@ -193,10 +253,6 @@ pub const ElfFile = struct { | ... | @@ -193,10 +253,6 @@ pub const ElfFile = struct { |
| 193 | }); | 253 | }); |
| 194 | } | 254 | } |
| 195 | | 255 | |
| 196 | // `alloc_num / alloc_den` is the factor of padding when allocation | | |
| 197 | const alloc_num = 4; | | |
| 198 | const alloc_den = 3; | | |
| 199 | | | |
| 200 | /// Returns end pos of collision, if any. | 256 | /// Returns end pos of collision, if any. |
| 201 | fn detectAllocCollision(self: *ElfFile, start: u64, size: u64) ?u64 { | 257 | fn detectAllocCollision(self: *ElfFile, start: u64, size: u64) ?u64 { |
| 202 | const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32; | 258 | const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32; |
| ... | @@ -448,6 +504,13 @@ pub const ElfFile = struct { | ... | @@ -448,6 +504,13 @@ pub const ElfFile = struct { |
| 448 | self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign); | 504 | self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign); |
| 449 | self.phdr_table_dirty = true; | 505 | self.phdr_table_dirty = true; |
| 450 | } | 506 | } |
| | 507 | { |
| | 508 | // Iterate over symbols, populating free_list and last_text_block. |
| | 509 | if (self.local_symbols.items.len != 1) { |
| | 510 | @panic("TODO implement setting up free_list and last_text_block from existing ELF file"); |
| | 511 | } |
| | 512 | // We are starting with an empty file. The default values are correct, null and empty list. |
| | 513 | } |
| 451 | } | 514 | } |
| 452 | | 515 | |
| 453 | /// Commit pending changes and write headers. | 516 | /// Commit pending changes and write headers. |
| ... | @@ -577,7 +640,6 @@ pub const ElfFile = struct { | ... | @@ -577,7 +640,6 @@ pub const ElfFile = struct { |
| 577 | self.error_flags.no_entry_point_found = false; | 640 | self.error_flags.no_entry_point_found = false; |
| 578 | try self.writeElfHeader(); | 641 | try self.writeElfHeader(); |
| 579 | } | 642 | } |
| 580 | // TODO find end pos and truncate | | |
| 581 | | 643 | |
| 582 | // The point of flush() is to commit changes, so nothing should be dirty after this. | 644 | // The point of flush() is to commit changes, so nothing should be dirty after this. |
| 583 | assert(!self.phdr_table_dirty); | 645 | assert(!self.phdr_table_dirty); |
| ... | @@ -709,71 +771,170 @@ pub const ElfFile = struct { | ... | @@ -709,71 +771,170 @@ pub const ElfFile = struct { |
| 709 | try self.file.?.pwriteAll(hdr_buf[0..index], 0); | 771 | try self.file.?.pwriteAll(hdr_buf[0..index], 0); |
| 710 | } | 772 | } |
| 711 | | 773 | |
| 712 | const AllocatedBlock = struct { | 774 | fn freeTextBlock(self: *ElfFile, text_block: *TextBlock) void { |
| 713 | vaddr: u64, | 775 | var already_have_free_list_node = false; |
| 714 | file_offset: u64, | 776 | { |
| 715 | size_capacity: u64, | 777 | var i: usize = 0; |
| 716 | }; | 778 | while (i < self.free_list.items.len) { |
| | 779 | if (self.free_list.items[i] == text_block) { |
| | 780 | _ = self.free_list.swapRemove(i); |
| | 781 | continue; |
| | 782 | } |
| | 783 | if (self.free_list.items[i] == text_block.prev) { |
| | 784 | already_have_free_list_node = true; |
| | 785 | } |
| | 786 | i += 1; |
| | 787 | } |
| | 788 | } |
| 717 | | 789 | |
| 718 | fn allocateTextBlock(self: *ElfFile, new_block_size: u64, alignment: u64) !AllocatedBlock { | 790 | if (self.last_text_block == text_block) { |
| 719 | const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; | 791 | self.last_text_block = text_block.prev; |
| 720 | const shdr = &self.sections.items[self.text_section_index.?]; | 792 | } |
| | 793 | |
| | 794 | if (text_block.prev) |prev| { |
| | 795 | prev.next = text_block.next; |
| 721 | | 796 | |
| 722 | // TODO Also detect virtual address collisions. | 797 | if (!already_have_free_list_node and prev.freeListEligible(self.*)) { |
| 723 | const text_capacity = self.allocatedSize(shdr.sh_offset); | 798 | // The free list is heuristics, it doesn't have to be perfect, so we can |
| 724 | // TODO instead of looping here, maintain a free list and a pointer to the end. | 799 | // ignore the OOM here. |
| 725 | var last_start: u64 = phdr.p_vaddr; | 800 | self.free_list.append(self.allocator, prev) catch {}; |
| 726 | var last_size: u64 = 0; | | |
| 727 | for (self.local_symbols.items) |sym| { | | |
| 728 | if (sym.st_value + sym.st_size > last_start + last_size) { | | |
| 729 | last_start = sym.st_value; | | |
| 730 | last_size = sym.st_size; | | |
| 731 | } | 801 | } |
| | 802 | } else { |
| | 803 | text_block.prev = null; |
| 732 | } | 804 | } |
| 733 | const end_vaddr = last_start + (last_size * alloc_num / alloc_den); | 805 | |
| 734 | const aligned_start_vaddr = mem.alignForwardGeneric(u64, end_vaddr, alignment); | 806 | if (text_block.next) |next| { |
| 735 | const needed_size = (aligned_start_vaddr + new_block_size) - phdr.p_vaddr; | 807 | next.prev = text_block.prev; |
| 736 | if (needed_size > text_capacity) { | 808 | } else { |
| 737 | // Must move the entire text section. | 809 | text_block.next = null; |
| 738 | const new_offset = self.findFreeSpace(needed_size, 0x1000); | | |
| 739 | const text_size = (last_start + last_size) - phdr.p_vaddr; | | |
| 740 | const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, text_size); | | |
| 741 | if (amt != text_size) return error.InputOutput; | | |
| 742 | shdr.sh_offset = new_offset; | | |
| 743 | phdr.p_offset = new_offset; | | |
| 744 | } | 810 | } |
| 745 | // Now that we know the code size, we need to update the program header for executable code | | |
| 746 | shdr.sh_size = needed_size; | | |
| 747 | phdr.p_memsz = needed_size; | | |
| 748 | phdr.p_filesz = needed_size; | | |
| 749 | | | |
| 750 | self.phdr_table_dirty = true; // TODO look into making only the one program header dirty | | |
| 751 | self.shdr_table_dirty = true; // TODO look into making only the one section dirty | | |
| 752 | | | |
| 753 | return AllocatedBlock{ | | |
| 754 | .vaddr = aligned_start_vaddr, | | |
| 755 | .file_offset = shdr.sh_offset + (aligned_start_vaddr - phdr.p_vaddr), | | |
| 756 | .size_capacity = text_capacity - needed_size, | | |
| 757 | }; | | |
| 758 | } | 811 | } |
| 759 | | 812 | |
| 760 | fn findAllocatedTextBlock(self: *ElfFile, sym: elf.Elf64_Sym) AllocatedBlock { | 813 | fn shrinkTextBlock(self: *ElfFile, text_block: *TextBlock, new_block_size: u64) void { |
| | 814 | // TODO check the new capacity, and if it crosses the size threshold into a big enough |
| | 815 | // capacity, insert a free list node for it. |
| | 816 | } |
| | 817 | |
| | 818 | fn growTextBlock(self: *ElfFile, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { |
| | 819 | const sym = self.local_symbols.items[text_block.local_sym_index]; |
| | 820 | const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value; |
| | 821 | const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*); |
| | 822 | if (!need_realloc) return sym.st_value; |
| | 823 | return self.allocateTextBlock(text_block, new_block_size, alignment); |
| | 824 | } |
| | 825 | |
| | 826 | fn allocateTextBlock(self: *ElfFile, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { |
| 761 | const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; | 827 | const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; |
| 762 | const shdr = &self.sections.items[self.text_section_index.?]; | 828 | const shdr = &self.sections.items[self.text_section_index.?]; |
| | 829 | const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den; |
| | 830 | |
| | 831 | // We use these to indicate our intention to update metadata, placing the new block, |
| | 832 | // and possibly removing a free list node. |
| | 833 | // It would be simpler to do it inside the for loop below, but that would cause a |
| | 834 | // problem if an error was returned later in the function. So this action |
| | 835 | // is actually carried out at the end of the function, when errors are no longer possible. |
| | 836 | var block_placement: ?*TextBlock = null; |
| | 837 | var free_list_removal: ?usize = null; |
| | 838 | |
| | 839 | // First we look for an appropriately sized free list node. |
| | 840 | // The list is unordered. We'll just take the first thing that works. |
| | 841 | const vaddr = blk: { |
| | 842 | var i: usize = 0; |
| | 843 | while (i < self.free_list.items.len) { |
| | 844 | const big_block = self.free_list.items[i]; |
| | 845 | // We now have a pointer to a live text block that has too much capacity. |
| | 846 | // Is it enough that we could fit this new text block? |
| | 847 | const sym = self.local_symbols.items[big_block.local_sym_index]; |
| | 848 | const capacity = big_block.capacity(self.*); |
| | 849 | const ideal_capacity = capacity * alloc_num / alloc_den; |
| | 850 | const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity; |
| | 851 | const capacity_end_vaddr = sym.st_value + capacity; |
| | 852 | const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity; |
| | 853 | const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment); |
| | 854 | if (new_start_vaddr < ideal_capacity_end_vaddr) { |
| | 855 | // Additional bookkeeping here to notice if this free list node |
| | 856 | // should be deleted because the block that it points to has grown to take up |
| | 857 | // more of the extra capacity. |
| | 858 | if (!big_block.freeListEligible(self.*)) { |
| | 859 | _ = self.free_list.swapRemove(i); |
| | 860 | } else { |
| | 861 | i += 1; |
| | 862 | } |
| | 863 | continue; |
| | 864 | } |
| | 865 | // At this point we know that we will place the new block here. But the |
| | 866 | // remaining question is whether there is still yet enough capacity left |
| | 867 | // over for there to still be a free list node. |
| | 868 | const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr; |
| | 869 | const keep_free_list_node = remaining_capacity >= min_text_capacity; |
| | 870 | |
| | 871 | // Set up the metadata to be updated, after errors are no longer possible. |
| | 872 | block_placement = big_block; |
| | 873 | if (!keep_free_list_node) { |
| | 874 | free_list_removal = i; |
| | 875 | } |
| | 876 | break :blk new_start_vaddr; |
| | 877 | } else if (self.last_text_block) |last| { |
| | 878 | const sym = self.local_symbols.items[last.local_sym_index]; |
| | 879 | const ideal_capacity = sym.st_size * alloc_num / alloc_den; |
| | 880 | const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity; |
| | 881 | const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment); |
| | 882 | // Set up the metadata to be updated, after errors are no longer possible. |
| | 883 | block_placement = last; |
| | 884 | break :blk new_start_vaddr; |
| | 885 | } else { |
| | 886 | break :blk phdr.p_vaddr; |
| | 887 | } |
| | 888 | }; |
| | 889 | |
| | 890 | const expand_text_section = block_placement == null or block_placement.?.next == null; |
| | 891 | if (expand_text_section) { |
| | 892 | const text_capacity = self.allocatedSize(shdr.sh_offset); |
| | 893 | const needed_size = (vaddr + new_block_size) - phdr.p_vaddr; |
| | 894 | if (needed_size > text_capacity) { |
| | 895 | // Must move the entire text section. |
| | 896 | const new_offset = self.findFreeSpace(needed_size, 0x1000); |
| | 897 | const text_size = if (self.last_text_block) |last| blk: { |
| | 898 | const sym = self.local_symbols.items[last.local_sym_index]; |
| | 899 | break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr; |
| | 900 | } else 0; |
| | 901 | const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, text_size); |
| | 902 | if (amt != text_size) return error.InputOutput; |
| | 903 | shdr.sh_offset = new_offset; |
| | 904 | phdr.p_offset = new_offset; |
| | 905 | } |
| | 906 | self.last_text_block = text_block; |
| | 907 | |
| | 908 | shdr.sh_size = needed_size; |
| | 909 | phdr.p_memsz = needed_size; |
| | 910 | phdr.p_filesz = needed_size; |
| 763 | | 911 | |
| 764 | // Find the next sym after this one. | 912 | self.phdr_table_dirty = true; // TODO look into making only the one program header dirty |
| 765 | // TODO look into using a hash map to speed up perf. | 913 | self.shdr_table_dirty = true; // TODO look into making only the one section dirty |
| 766 | const text_capacity = self.allocatedSize(shdr.sh_offset); | | |
| 767 | var next_vaddr_start = phdr.p_vaddr + text_capacity; | | |
| 768 | for (self.local_symbols.items) |elem| { | | |
| 769 | if (elem.st_value < sym.st_value) continue; | | |
| 770 | if (elem.st_value < next_vaddr_start) next_vaddr_start = elem.st_value; | | |
| 771 | } | 914 | } |
| 772 | return .{ | 915 | |
| 773 | .vaddr = sym.st_value, | 916 | // This function can also reallocate a text block. |
| 774 | .file_offset = shdr.sh_offset + (sym.st_value - phdr.p_vaddr), | 917 | // In this case we need to "unplug" it from its previous location before |
| 775 | .size_capacity = next_vaddr_start - sym.st_value, | 918 | // plugging it in to its new location. |
| 776 | }; | 919 | if (text_block.prev) |prev| { |
| | 920 | prev.next = text_block.next; |
| | 921 | } |
| | 922 | if (text_block.next) |next| { |
| | 923 | next.prev = text_block.prev; |
| | 924 | } |
| | 925 | |
| | 926 | if (block_placement) |big_block| { |
| | 927 | text_block.prev = big_block; |
| | 928 | text_block.next = big_block.next; |
| | 929 | big_block.next = text_block; |
| | 930 | } else { |
| | 931 | text_block.prev = null; |
| | 932 | text_block.next = null; |
| | 933 | } |
| | 934 | if (free_list_removal) |i| { |
| | 935 | _ = self.free_list.swapRemove(i); |
| | 936 | } |
| | 937 | return vaddr; |
| 777 | } | 938 | } |
| 778 | | 939 | |
| 779 | pub fn allocateDeclIndexes(self: *ElfFile, decl: *Module.Decl) !void { | 940 | pub fn allocateDeclIndexes(self: *ElfFile, decl: *Module.Decl) !void { |
| ... | @@ -793,16 +954,13 @@ pub const ElfFile = struct { | ... | @@ -793,16 +954,13 @@ pub const ElfFile = struct { |
| 793 | .st_value = phdr.p_vaddr, | 954 | .st_value = phdr.p_vaddr, |
| 794 | .st_size = 0, | 955 | .st_size = 0, |
| 795 | }); | 956 | }); |
| 796 | errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1); | | |
| 797 | self.offset_table.appendAssumeCapacity(0); | 957 | self.offset_table.appendAssumeCapacity(0); |
| 798 | errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1); | | |
| 799 | | 958 | |
| 800 | self.offset_table_count_dirty = true; | 959 | self.offset_table_count_dirty = true; |
| 801 | | 960 | |
| 802 | decl.link = .{ | 961 | //std.debug.warn("allocating symbol index {}\n", .{local_sym_index}); |
| 803 | .local_sym_index = @intCast(u32, local_sym_index), | 962 | decl.link.local_sym_index = @intCast(u32, local_sym_index); |
| 804 | .offset_table_index = @intCast(u32, offset_table_index), | 963 | decl.link.offset_table_index = @intCast(u32, offset_table_index); |
| 805 | }; | | |
| 806 | } | 964 | } |
| 807 | | 965 | |
| 808 | pub fn updateDecl(self: *ElfFile, module: *Module, decl: *Module.Decl) !void { | 966 | pub fn updateDecl(self: *ElfFile, module: *Module, decl: *Module.Decl) !void { |
| ... | @@ -822,80 +980,60 @@ pub const ElfFile = struct { | ... | @@ -822,80 +980,60 @@ pub const ElfFile = struct { |
| 822 | | 980 | |
| 823 | const required_alignment = typed_value.ty.abiAlignment(self.options.target); | 981 | const required_alignment = typed_value.ty.abiAlignment(self.options.target); |
| 824 | | 982 | |
| 825 | const file_offset = blk: { | 983 | const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) { |
| 826 | const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) { | 984 | .Fn => elf.STT_FUNC, |
| 827 | .Fn => elf.STT_FUNC, | 985 | else => elf.STT_OBJECT, |
| 828 | else => elf.STT_OBJECT, | 986 | }; |
| 829 | }; | | |
| 830 | | 987 | |
| 831 | if (decl.link.local_sym_index != 0) { | 988 | assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes() |
| 832 | const local_sym = &self.local_symbols.items[decl.link.local_sym_index]; | 989 | const local_sym = &self.local_symbols.items[decl.link.local_sym_index]; |
| 833 | const existing_block = self.findAllocatedTextBlock(local_sym.*); | 990 | if (local_sym.st_size != 0) { |
| 834 | const need_realloc = local_sym.st_size == 0 or | 991 | const capacity = decl.link.capacity(self.*); |
| 835 | code.len > existing_block.size_capacity or | 992 | const need_realloc = code.len > capacity or |
| 836 | !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment); | 993 | !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment); |
| 837 | // TODO check for collision with another symbol | 994 | if (need_realloc) { |
| 838 | const file_offset = if (need_realloc) fo: { | 995 | const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment); |
| 839 | const new_block = try self.allocateTextBlock(code.len, required_alignment); | 996 | //std.debug.warn("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr }); |
| 840 | local_sym.st_value = new_block.vaddr; | 997 | if (vaddr != local_sym.st_value) { |
| 841 | self.offset_table.items[decl.link.offset_table_index] = new_block.vaddr; | 998 | local_sym.st_value = vaddr; |
| 842 | | 999 | |
| 843 | //std.debug.warn("{}: writing got index {}=0x{x}\n", .{ | 1000 | //std.debug.warn(" (writing new offset table entry)\n", .{}); |
| 844 | // decl.name, | 1001 | self.offset_table.items[decl.link.offset_table_index] = vaddr; |
| 845 | // decl.link.offset_table_index, | | |
| 846 | // self.offset_table.items[decl.link.offset_table_index], | | |
| 847 | //}); | | |
| 848 | try self.writeOffsetTableEntry(decl.link.offset_table_index); | 1002 | try self.writeOffsetTableEntry(decl.link.offset_table_index); |
| 849 | | 1003 | } |
| 850 | break :fo new_block.file_offset; | 1004 | } else if (code.len < local_sym.st_size) { |
| 851 | } else existing_block.file_offset; | 1005 | self.shrinkTextBlock(&decl.link, code.len); |
| 852 | local_sym.st_size = code.len; | | |
| 853 | local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name)); | | |
| 854 | local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits; | | |
| 855 | local_sym.st_other = 0; | | |
| 856 | local_sym.st_shndx = self.text_section_index.?; | | |
| 857 | // TODO this write could be avoided if no fields of the symbol were changed. | | |
| 858 | try self.writeSymbol(decl.link.local_sym_index); | | |
| 859 | | | |
| 860 | //std.debug.warn("updating {} at vaddr 0x{x}\n", .{ decl.name, local_sym.st_value }); | | |
| 861 | break :blk file_offset; | | |
| 862 | } else { | | |
| 863 | try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1); | | |
| 864 | try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1); | | |
| 865 | const decl_name = mem.spanZ(decl.name); | | |
| 866 | const name_str_index = try self.makeString(decl_name); | | |
| 867 | const new_block = try self.allocateTextBlock(code.len, required_alignment); | | |
| 868 | const local_sym_index = self.local_symbols.items.len; | | |
| 869 | const offset_table_index = self.offset_table.items.len; | | |
| 870 | | | |
| 871 | //std.debug.warn("add symbol for {} at vaddr 0x{x}, size {}\n", .{ decl.name, new_block.vaddr, code.len }); | | |
| 872 | self.local_symbols.appendAssumeCapacity(.{ | | |
| 873 | .st_name = name_str_index, | | |
| 874 | .st_info = (elf.STB_LOCAL << 4) | stt_bits, | | |
| 875 | .st_other = 0, | | |
| 876 | .st_shndx = self.text_section_index.?, | | |
| 877 | .st_value = new_block.vaddr, | | |
| 878 | .st_size = code.len, | | |
| 879 | }); | | |
| 880 | errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1); | | |
| 881 | self.offset_table.appendAssumeCapacity(new_block.vaddr); | | |
| 882 | errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1); | | |
| 883 | | | |
| 884 | self.offset_table_count_dirty = true; | | |
| 885 | | | |
| 886 | try self.writeSymbol(local_sym_index); | | |
| 887 | try self.writeOffsetTableEntry(offset_table_index); | | |
| 888 | | | |
| 889 | decl.link = .{ | | |
| 890 | .local_sym_index = @intCast(u32, local_sym_index), | | |
| 891 | .offset_table_index = @intCast(u32, offset_table_index), | | |
| 892 | }; | | |
| 893 | | | |
| 894 | //std.debug.warn("writing new {} at vaddr 0x{x}\n", .{ decl.name, new_block.vaddr }); | | |
| 895 | break :blk new_block.file_offset; | | |
| 896 | } | 1006 | } |
| 897 | }; | 1007 | local_sym.st_size = code.len; |
| | 1008 | local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name)); |
| | 1009 | local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits; |
| | 1010 | local_sym.st_other = 0; |
| | 1011 | local_sym.st_shndx = self.text_section_index.?; |
| | 1012 | // TODO this write could be avoided if no fields of the symbol were changed. |
| | 1013 | try self.writeSymbol(decl.link.local_sym_index); |
| | 1014 | } else { |
| | 1015 | const decl_name = mem.spanZ(decl.name); |
| | 1016 | const name_str_index = try self.makeString(decl_name); |
| | 1017 | const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment); |
| | 1018 | //std.debug.warn("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr }); |
| | 1019 | errdefer self.freeTextBlock(&decl.link); |
| | 1020 | |
| | 1021 | local_sym.* = .{ |
| | 1022 | .st_name = name_str_index, |
| | 1023 | .st_info = (elf.STB_LOCAL << 4) | stt_bits, |
| | 1024 | .st_other = 0, |
| | 1025 | .st_shndx = self.text_section_index.?, |
| | 1026 | .st_value = vaddr, |
| | 1027 | .st_size = code.len, |
| | 1028 | }; |
| | 1029 | self.offset_table.items[decl.link.offset_table_index] = vaddr; |
| | 1030 | |
| | 1031 | try self.writeSymbol(decl.link.local_sym_index); |
| | 1032 | try self.writeOffsetTableEntry(decl.link.offset_table_index); |
| | 1033 | } |
| 898 | | 1034 | |
| | 1035 | const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr; |
| | 1036 | const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset; |
| 899 | try self.file.?.pwriteAll(code, file_offset); | 1037 | try self.file.?.pwriteAll(code, file_offset); |
| 900 | | 1038 | |
| 901 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. | 1039 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. |