authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-27 15:23:27-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-27 15:23:27-04:00
logc7ca1fe6f7b8796a42de908faeaa6ec24e8eb118
treece90d5e83280aea66fcad38382840d326ec2a9fb
parent2ae9e06363b294f60b62e765d31fce4417e14a9d

self-hosted: introduce a virtual address allocation scheme

The binary file abstraction changed its struct named "Decl" to "TextBlock" and it now represents an allocated slice of memory in the .text section. It has two new fields: prev and next, making it a linked list node. This allows a TextBlock to find its neighbors. The ElfFile struct now has free_list and last_text_block fields. Doc comments for free_list are reproduced here: A list of text blocks that have surplus capacity. This list can have false positives, as functions grow and shrink over time, only sometimes being added or removed from the freelist. A text block has surplus capacity when its overcapacity value is greater than minimum_text_block_size * alloc_num / alloc_den. That is, when it has so much extra capacity, that we could fit a small new symbol in it, itself with ideal_capacity or more. Ideal capacity is defined by size * alloc_num / alloc_den. Overcapacity is measured by actual_capacity - ideal_capacity. Note that overcapacity can be negative. A simple way to have negative overcapacity is to allocate a fresh text block, which will have ideal capacity, and then grow it by 1 byte. It will then have -1 overcapacity. The last_text_block keeps track of the end of the .text section. Allocation, freeing, and resizing decls are all now more sophisticated, and participate in the virtual address allocation scheme. There is no longer the possibility for virtual address collisions.

3 files changed, 351 insertions(+), 142 deletions(-)

src-self-hosted/Module.zig+5-5
......@@ -134,7 +134,7 @@ pub const Decl = struct {
134134
135135 /// Represents the position of the code in the output file.
136136 /// This is populated regardless of semantic analysis and code generation.
137 link: link.ElfFile.Decl = link.ElfFile.Decl.empty,
137 link: link.ElfFile.TextBlock = link.ElfFile.TextBlock.empty,
138138
139139 /// The shallow set of other decls whose typed_value could possibly change if this Decl's
140140 /// typed_value is modified.
......@@ -759,7 +759,7 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {
759759
760760 for (src_module.decls) |decl| {
761761 if (decl.cast(zir.Inst.Export)) |export_inst| {
762 _ = try self.resolveDecl(&root_scope.base, &export_inst.base, link.ElfFile.Decl.empty);
762 _ = try self.resolveDecl(&root_scope.base, &export_inst.base, link.ElfFile.TextBlock.empty);
763763 }
764764 }
765765 },
......@@ -800,7 +800,7 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {
800800 }
801801 }
802802 } else if (src_decl.cast(zir.Inst.Export)) |export_inst| {
803 _ = try self.resolveDecl(&root_scope.base, &export_inst.base, link.ElfFile.Decl.empty);
803 _ = try self.resolveDecl(&root_scope.base, &export_inst.base, link.ElfFile.TextBlock.empty);
804804 }
805805 }
806806 },
......@@ -840,7 +840,7 @@ fn resolveDecl(
840840 self: *Module,
841841 scope: *Scope,
842842 old_inst: *zir.Inst,
843 bin_file_link: link.ElfFile.Decl,
843 bin_file_link: link.ElfFile.TextBlock,
844844) InnerError!*Decl {
845845 const hash = Decl.hashSimpleName(old_inst.name);
846846 if (self.decl_table.get(hash)) |kv| {
......@@ -907,7 +907,7 @@ fn resolveDecl(
907907}
908908
909909fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl {
910 const decl = try self.resolveDecl(scope, old_inst, link.ElfFile.Decl.empty);
910 const decl = try self.resolveDecl(scope, old_inst, link.ElfFile.TextBlock.empty);
911911 switch (decl.analysis) {
912912 .initial_in_progress => unreachable,
913913 .repeat_in_progress => unreachable,
src-self-hosted/link.zig+275-137
......@@ -138,11 +138,39 @@ pub const ElfFile = struct {
138138
139139 error_flags: ErrorFlags = ErrorFlags{},
140140
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
141169 pub const ErrorFlags = struct {
142170 no_entry_point_found: bool = false,
143171 };
144172
145 pub const Decl = struct {
173 pub const TextBlock = struct {
146174 /// Each decl always gets a local symbol with the fully qualified name.
147175 /// The vaddr and size are found here directly.
148176 /// The file offset is found by computing the vaddr offset from the section vaddr
......@@ -152,11 +180,43 @@ pub const ElfFile = struct {
152180 local_sym_index: u32,
153181 /// This field is undefined for symbols with size = 0.
154182 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,
155187
156 pub const empty = Decl{
188 pub const empty = TextBlock{
157189 .local_sym_index = 0,
158190 .offset_table_index = undefined,
191 .prev = null,
192 .next = null,
159193 };
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 }
160220 };
161221
162222 pub const Export = struct {
......@@ -193,10 +253,6 @@ pub const ElfFile = struct {
193253 });
194254 }
195255
196 // `alloc_num / alloc_den` is the factor of padding when allocation
197 const alloc_num = 4;
198 const alloc_den = 3;
199
200256 /// Returns end pos of collision, if any.
201257 fn detectAllocCollision(self: *ElfFile, start: u64, size: u64) ?u64 {
202258 const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32;
......@@ -448,6 +504,13 @@ pub const ElfFile = struct {
448504 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
449505 self.phdr_table_dirty = true;
450506 }
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 }
451514 }
452515
453516 /// Commit pending changes and write headers.
......@@ -577,7 +640,6 @@ pub const ElfFile = struct {
577640 self.error_flags.no_entry_point_found = false;
578641 try self.writeElfHeader();
579642 }
580 // TODO find end pos and truncate
581643
582644 // The point of flush() is to commit changes, so nothing should be dirty after this.
583645 assert(!self.phdr_table_dirty);
......@@ -709,71 +771,170 @@ pub const ElfFile = struct {
709771 try self.file.?.pwriteAll(hdr_buf[0..index], 0);
710772 }
711773
712 const AllocatedBlock = struct {
713 vaddr: u64,
714 file_offset: u64,
715 size_capacity: u64,
716 };
774 fn freeTextBlock(self: *ElfFile, text_block: *TextBlock) void {
775 var already_have_free_list_node = false;
776 {
777 var i: usize = 0;
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 }
717789
718 fn allocateTextBlock(self: *ElfFile, new_block_size: u64, alignment: u64) !AllocatedBlock {
719 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
720 const shdr = &self.sections.items[self.text_section_index.?];
790 if (self.last_text_block == text_block) {
791 self.last_text_block = text_block.prev;
792 }
793
794 if (text_block.prev) |prev| {
795 prev.next = text_block.next;
721796
722 // TODO Also detect virtual address collisions.
723 const text_capacity = self.allocatedSize(shdr.sh_offset);
724 // TODO instead of looping here, maintain a free list and a pointer to the end.
725 var last_start: u64 = phdr.p_vaddr;
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;
797 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
798 // The free list is heuristics, it doesn't have to be perfect, so we can
799 // ignore the OOM here.
800 self.free_list.append(self.allocator, prev) catch {};
731801 }
802 } else {
803 text_block.prev = null;
732804 }
733 const end_vaddr = last_start + (last_size * alloc_num / alloc_den);
734 const aligned_start_vaddr = mem.alignForwardGeneric(u64, end_vaddr, alignment);
735 const needed_size = (aligned_start_vaddr + new_block_size) - phdr.p_vaddr;
736 if (needed_size > text_capacity) {
737 // Must move the entire text section.
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;
805
806 if (text_block.next) |next| {
807 next.prev = text_block.prev;
808 } else {
809 text_block.next = null;
744810 }
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 };
758811 }
759812
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 {
761827 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
762828 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;
763911
764 // Find the next sym after this one.
765 // TODO look into using a hash map to speed up perf.
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;
912 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
913 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
771914 }
772 return .{
773 .vaddr = sym.st_value,
774 .file_offset = shdr.sh_offset + (sym.st_value - phdr.p_vaddr),
775 .size_capacity = next_vaddr_start - sym.st_value,
776 };
915
916 // This function can also reallocate a text block.
917 // In this case we need to "unplug" it from its previous location before
918 // plugging it in to its new location.
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;
777938 }
778939
779940 pub fn allocateDeclIndexes(self: *ElfFile, decl: *Module.Decl) !void {
......@@ -793,16 +954,13 @@ pub const ElfFile = struct {
793954 .st_value = phdr.p_vaddr,
794955 .st_size = 0,
795956 });
796 errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1);
797957 self.offset_table.appendAssumeCapacity(0);
798 errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1);
799958
800959 self.offset_table_count_dirty = true;
801960
802 decl.link = .{
803 .local_sym_index = @intCast(u32, local_sym_index),
804 .offset_table_index = @intCast(u32, offset_table_index),
805 };
961 //std.debug.warn("allocating symbol index {}\n", .{local_sym_index});
962 decl.link.local_sym_index = @intCast(u32, local_sym_index);
963 decl.link.offset_table_index = @intCast(u32, offset_table_index);
806964 }
807965
808966 pub fn updateDecl(self: *ElfFile, module: *Module, decl: *Module.Decl) !void {
......@@ -822,80 +980,60 @@ pub const ElfFile = struct {
822980
823981 const required_alignment = typed_value.ty.abiAlignment(self.options.target);
824982
825 const file_offset = blk: {
826 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
827 .Fn => elf.STT_FUNC,
828 else => elf.STT_OBJECT,
829 };
983 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
984 .Fn => elf.STT_FUNC,
985 else => elf.STT_OBJECT,
986 };
830987
831 if (decl.link.local_sym_index != 0) {
832 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
833 const existing_block = self.findAllocatedTextBlock(local_sym.*);
834 const need_realloc = local_sym.st_size == 0 or
835 code.len > existing_block.size_capacity or
836 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
837 // TODO check for collision with another symbol
838 const file_offset = if (need_realloc) fo: {
839 const new_block = try self.allocateTextBlock(code.len, required_alignment);
840 local_sym.st_value = new_block.vaddr;
841 self.offset_table.items[decl.link.offset_table_index] = new_block.vaddr;
842
843 //std.debug.warn("{}: writing got index {}=0x{x}\n", .{
844 // decl.name,
845 // decl.link.offset_table_index,
846 // self.offset_table.items[decl.link.offset_table_index],
847 //});
988 assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
989 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
990 if (local_sym.st_size != 0) {
991 const capacity = decl.link.capacity(self.*);
992 const need_realloc = code.len > capacity or
993 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
994 if (need_realloc) {
995 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
996 //std.debug.warn("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
997 if (vaddr != local_sym.st_value) {
998 local_sym.st_value = vaddr;
999
1000 //std.debug.warn(" (writing new offset table entry)\n", .{});
1001 self.offset_table.items[decl.link.offset_table_index] = vaddr;
8481002 try self.writeOffsetTableEntry(decl.link.offset_table_index);
849
850 break :fo new_block.file_offset;
851 } else existing_block.file_offset;
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;
1003 }
1004 } else if (code.len < local_sym.st_size) {
1005 self.shrinkTextBlock(&decl.link, code.len);
8961006 }
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 }
8981034
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;
8991037 try self.file.?.pwriteAll(code, file_offset);
9001038
9011039 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
test/stage2/zir.zig+71
......@@ -200,6 +200,73 @@ pub fn addCases(ctx: *TestContext) void {
200200 \\@9 = str("_start")
201201 \\@10 = ref(@9)
202202 \\@11 = export(@10, @start)
203// ,
204// \\@noreturn = primitive(noreturn)
205// \\@void = primitive(void)
206// \\@usize = primitive(usize)
207// \\@0 = int(0)
208// \\@1 = int(1)
209// \\@2 = int(2)
210// \\@3 = int(3)
211// \\
212// \\@syscall_array = str("syscall")
213// \\@sysoutreg_array = str("={rax}")
214// \\@rax_array = str("{rax}")
215// \\@rdi_array = str("{rdi}")
216// \\@rcx_array = str("rcx")
217// \\@r11_array = str("r11")
218// \\@rdx_array = str("{rdx}")
219// \\@rsi_array = str("{rsi}")
220// \\@memory_array = str("memory")
221// \\@len_array = str("len")
222// \\
223// \\@msg = str("Hello, world!\n")
224// \\@msg2 = str("Editing the same msg2 decl but this time with a much longer message which will\ncause the data to need to be relocated in virtual address space.\n")
225// \\
226// \\@start_fnty = fntype([], @noreturn, cc=Naked)
227// \\@start = fn(@start_fnty, {
228// \\ %SYS_exit_group = int(231)
229// \\ %exit_code = as(@usize, @0)
230// \\
231// \\ %syscall = ref(@syscall_array)
232// \\ %sysoutreg = ref(@sysoutreg_array)
233// \\ %rax = ref(@rax_array)
234// \\ %rdi = ref(@rdi_array)
235// \\ %rcx = ref(@rcx_array)
236// \\ %rdx = ref(@rdx_array)
237// \\ %rsi = ref(@rsi_array)
238// \\ %r11 = ref(@r11_array)
239// \\ %memory = ref(@memory_array)
240// \\
241// \\ %SYS_write = as(@usize, @1)
242// \\ %STDOUT_FILENO = as(@usize, @1)
243// \\
244// \\ %msg_ptr = ref(@msg2)
245// \\ %msg_addr = ptrtoint(%msg_ptr)
246// \\
247// \\ %len_name = ref(@len_array)
248// \\ %msg_len_ptr = fieldptr(%msg_ptr, %len_name)
249// \\ %msg_len = deref(%msg_len_ptr)
250// \\ %rc_write = asm(%syscall, @usize,
251// \\ volatile=1,
252// \\ output=%sysoutreg,
253// \\ inputs=[%rax, %rdi, %rsi, %rdx],
254// \\ clobbers=[%rcx, %r11, %memory],
255// \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])
256// \\
257// \\ %rc_exit = asm(%syscall, @usize,
258// \\ volatile=1,
259// \\ output=%sysoutreg,
260// \\ inputs=[%rax, %rdi],
261// \\ clobbers=[%rcx, %r11, %memory],
262// \\ args=[%SYS_exit_group, %exit_code])
263// \\
264// \\ %99 = unreachable()
265// \\});
266// \\
267// \\@9 = str("_start")
268// \\@10 = ref(@9)
269// \\@11 = export(@10, @start)
203270 },
204271 &[_][]const u8{
205272 \\Hello, world!
......@@ -207,6 +274,10 @@ pub fn addCases(ctx: *TestContext) void {
207274 ,
208275 \\HELL WORLD
209276 \\
277// ,
278// \\Editing the same msg2 decl but this time with a much longer message which will
279// \\cause the data to need to be relocated in virtual address space.
280// \\
210281 },
211282 );
212283