| ... | @@ -931,6 +931,94 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -931,6 +931,94 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 931 | } | 931 | } |
| 932 | } | 932 | } |
| 933 | | 933 | |
| | 934 | fn growAllocSection(self: *Elf, shdr_index: u16, phdr_index: u16, needed_size: u64) !void { |
| | 935 | // TODO Also detect virtual address collisions. |
| | 936 | const shdr = &self.sections.items[shdr_index]; |
| | 937 | const phdr = &self.program_headers.items[phdr_index]; |
| | 938 | |
| | 939 | if (needed_size > self.allocatedSize(shdr.sh_offset)) { |
| | 940 | // Must move the entire section. |
| | 941 | const new_offset = self.findFreeSpace(needed_size, self.page_size); |
| | 942 | const existing_size = if (self.atoms.get(phdr_index)) |last| blk: { |
| | 943 | const sym = self.local_symbols.items[last.local_sym_index]; |
| | 944 | break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr; |
| | 945 | } else if (shdr_index == self.got_section_index.?) blk: { |
| | 946 | break :blk shdr.sh_size; |
| | 947 | } else 0; |
| | 948 | shdr.sh_size = 0; |
| | 949 | |
| | 950 | log.debug("new '{s}' file offset 0x{x} to 0x{x}", .{ |
| | 951 | self.getString(shdr.sh_name), |
| | 952 | new_offset, |
| | 953 | new_offset + existing_size, |
| | 954 | }); |
| | 955 | |
| | 956 | const amt = try self.base.file.?.copyRangeAll(shdr.sh_offset, self.base.file.?, new_offset, existing_size); |
| | 957 | if (amt != existing_size) return error.InputOutput; |
| | 958 | |
| | 959 | shdr.sh_offset = new_offset; |
| | 960 | phdr.p_offset = new_offset; |
| | 961 | } |
| | 962 | |
| | 963 | shdr.sh_size = needed_size; |
| | 964 | phdr.p_memsz = needed_size; |
| | 965 | phdr.p_filesz = needed_size; |
| | 966 | |
| | 967 | self.markDirty(shdr_index, phdr_index); |
| | 968 | } |
| | 969 | |
| | 970 | pub fn growNonAllocSection(self: *Elf, shdr_index: u16, needed_size: u64, min_alignment: u32) !void { |
| | 971 | const shdr = &self.sections.items[shdr_index]; |
| | 972 | |
| | 973 | if (needed_size > self.allocatedSize(shdr.sh_offset)) { |
| | 974 | const existing_size = if (self.symtab_section_index.? == shdr_index) blk: { |
| | 975 | const sym_size: u64 = switch (self.ptr_width) { |
| | 976 | .p32 => @sizeOf(elf.Elf32_Sym), |
| | 977 | .p64 => @sizeOf(elf.Elf64_Sym), |
| | 978 | }; |
| | 979 | break :blk @as(u64, shdr.sh_info) * sym_size; |
| | 980 | } else shdr.sh_size; |
| | 981 | shdr.sh_size = 0; |
| | 982 | // Move all the symbols to a new file location. |
| | 983 | const new_offset = self.findFreeSpace(needed_size, min_alignment); |
| | 984 | log.debug("moving '{s}' from 0x{x} to 0x{x}", .{ self.getString(shdr.sh_name), shdr.sh_offset, new_offset }); |
| | 985 | const amt = try self.base.file.?.copyRangeAll( |
| | 986 | shdr.sh_offset, |
| | 987 | self.base.file.?, |
| | 988 | new_offset, |
| | 989 | existing_size, |
| | 990 | ); |
| | 991 | if (amt != existing_size) return error.InputOutput; |
| | 992 | shdr.sh_offset = new_offset; |
| | 993 | } |
| | 994 | |
| | 995 | shdr.sh_size = needed_size; // anticipating adding the global symbols later |
| | 996 | |
| | 997 | self.markDirty(shdr_index, null); |
| | 998 | } |
| | 999 | |
| | 1000 | pub fn markDirty(self: *Elf, shdr_index: u16, phdr_index: ?u16) void { |
| | 1001 | self.shdr_table_dirty = true; // TODO look into only writing one section |
| | 1002 | |
| | 1003 | if (phdr_index) |_| { |
| | 1004 | self.phdr_table_dirty = true; // TODO look into making only the one program header dirty |
| | 1005 | } |
| | 1006 | |
| | 1007 | if (self.dwarf) |_| { |
| | 1008 | if (self.debug_info_section_index.? == shdr_index) { |
| | 1009 | self.debug_info_header_dirty = true; |
| | 1010 | } else if (self.debug_line_section_index.? == shdr_index) { |
| | 1011 | self.debug_line_header_dirty = true; |
| | 1012 | } else if (self.debug_abbrev_section_index.? == shdr_index) { |
| | 1013 | self.debug_abbrev_section_dirty = true; |
| | 1014 | } else if (self.debug_str_section_index.? == shdr_index) { |
| | 1015 | self.debug_strtab_dirty = true; |
| | 1016 | } else if (self.debug_aranges_section_index.? == shdr_index) { |
| | 1017 | self.debug_aranges_section_dirty = true; |
| | 1018 | } |
| | 1019 | } |
| | 1020 | } |
| | 1021 | |
| 934 | pub fn flush(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { | 1022 | pub fn flush(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { |
| 935 | if (self.base.options.emit == null) { | 1023 | if (self.base.options.emit == null) { |
| 936 | if (build_options.have_llvm) { | 1024 | if (build_options.have_llvm) { |
| ... | @@ -2134,27 +2222,10 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al | ... | @@ -2134,27 +2222,10 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al |
| 2134 | | 2222 | |
| 2135 | const expand_text_section = block_placement == null or block_placement.?.next == null; | 2223 | const expand_text_section = block_placement == null or block_placement.?.next == null; |
| 2136 | if (expand_text_section) { | 2224 | if (expand_text_section) { |
| 2137 | const text_capacity = self.allocatedSize(shdr.sh_offset); | | |
| 2138 | const needed_size = (vaddr + new_block_size) - phdr.p_vaddr; | 2225 | const needed_size = (vaddr + new_block_size) - phdr.p_vaddr; |
| 2139 | if (needed_size > text_capacity) { | 2226 | try self.growAllocSection(shdr_index, phdr_index, needed_size); |
| 2140 | // Must move the entire section. | | |
| 2141 | const new_offset = self.findFreeSpace(needed_size, self.page_size); | | |
| 2142 | const text_size = if (self.atoms.get(phdr_index)) |last| blk: { | | |
| 2143 | const sym = self.local_symbols.items[last.local_sym_index]; | | |
| 2144 | break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr; | | |
| 2145 | } else 0; | | |
| 2146 | log.debug("new PT_LOAD file offset 0x{x} to 0x{x}", .{ new_offset, new_offset + text_size }); | | |
| 2147 | const amt = try self.base.file.?.copyRangeAll(shdr.sh_offset, self.base.file.?, new_offset, text_size); | | |
| 2148 | if (amt != text_size) return error.InputOutput; | | |
| 2149 | shdr.sh_offset = new_offset; | | |
| 2150 | phdr.p_offset = new_offset; | | |
| 2151 | } | | |
| 2152 | _ = try self.atoms.put(self.base.allocator, phdr_index, text_block); | 2227 | _ = try self.atoms.put(self.base.allocator, phdr_index, text_block); |
| 2153 | | 2228 | |
| 2154 | shdr.sh_size = needed_size; | | |
| 2155 | phdr.p_memsz = needed_size; | | |
| 2156 | phdr.p_filesz = needed_size; | | |
| 2157 | | | |
| 2158 | if (self.dwarf) |_| { | 2229 | if (self.dwarf) |_| { |
| 2159 | // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address | 2230 | // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address |
| 2160 | // range of the compilation unit. When we expand the text section, this range changes, | 2231 | // range of the compilation unit. When we expand the text section, this range changes, |
| ... | @@ -2165,9 +2236,6 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al | ... | @@ -2165,9 +2236,6 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al |
| 2165 | // model each package as a different compilation unit. | 2236 | // model each package as a different compilation unit. |
| 2166 | self.debug_aranges_section_dirty = true; | 2237 | self.debug_aranges_section_dirty = true; |
| 2167 | } | 2238 | } |
| 2168 | | | |
| 2169 | self.phdr_table_dirty = true; // TODO look into making only the one program header dirty | | |
| 2170 | self.shdr_table_dirty = true; // TODO look into making only the one section dirty | | |
| 2171 | } | 2239 | } |
| 2172 | shdr.sh_addralign = math.max(shdr.sh_addralign, alignment); | 2240 | shdr.sh_addralign = math.max(shdr.sh_addralign, alignment); |
| 2173 | | 2241 | |
| ... | @@ -2747,31 +2815,14 @@ fn writeSectHeader(self: *Elf, index: usize) !void { | ... | @@ -2747,31 +2815,14 @@ fn writeSectHeader(self: *Elf, index: usize) !void { |
| 2747 | } | 2815 | } |
| 2748 | | 2816 | |
| 2749 | fn writeOffsetTableEntry(self: *Elf, index: usize) !void { | 2817 | fn writeOffsetTableEntry(self: *Elf, index: usize) !void { |
| 2750 | const shdr = &self.sections.items[self.got_section_index.?]; | | |
| 2751 | const phdr = &self.program_headers.items[self.phdr_got_index.?]; | | |
| 2752 | const entry_size: u16 = self.archPtrWidthBytes(); | 2818 | const entry_size: u16 = self.archPtrWidthBytes(); |
| 2753 | if (self.offset_table_count_dirty) { | 2819 | if (self.offset_table_count_dirty) { |
| 2754 | // TODO Also detect virtual address collisions. | | |
| 2755 | const allocated_size = self.allocatedSize(shdr.sh_offset); | | |
| 2756 | const needed_size = self.offset_table.items.len * entry_size; | 2820 | const needed_size = self.offset_table.items.len * entry_size; |
| 2757 | if (needed_size > allocated_size) { | 2821 | try self.growAllocSection(self.got_section_index.?, self.phdr_got_index.?, needed_size); |
| 2758 | // Must move the entire got section. | | |
| 2759 | const new_offset = self.findFreeSpace(needed_size, self.page_size); | | |
| 2760 | const amt = try self.base.file.?.copyRangeAll(shdr.sh_offset, self.base.file.?, new_offset, shdr.sh_size); | | |
| 2761 | if (amt != shdr.sh_size) return error.InputOutput; | | |
| 2762 | shdr.sh_offset = new_offset; | | |
| 2763 | phdr.p_offset = new_offset; | | |
| 2764 | } | | |
| 2765 | shdr.sh_size = needed_size; | | |
| 2766 | phdr.p_memsz = needed_size; | | |
| 2767 | phdr.p_filesz = needed_size; | | |
| 2768 | | | |
| 2769 | self.shdr_table_dirty = true; // TODO look into making only the one section dirty | | |
| 2770 | self.phdr_table_dirty = true; // TODO look into making only the one program header dirty | | |
| 2771 | | | |
| 2772 | self.offset_table_count_dirty = false; | 2822 | self.offset_table_count_dirty = false; |
| 2773 | } | 2823 | } |
| 2774 | const endian = self.base.options.target.cpu.arch.endian(); | 2824 | const endian = self.base.options.target.cpu.arch.endian(); |
| | 2825 | const shdr = &self.sections.items[self.got_section_index.?]; |
| 2775 | const off = shdr.sh_offset + @as(u64, entry_size) * index; | 2826 | const off = shdr.sh_offset + @as(u64, entry_size) * index; |
| 2776 | switch (entry_size) { | 2827 | switch (entry_size) { |
| 2777 | 2 => { | 2828 | 2 => { |
| ... | @@ -2810,23 +2861,8 @@ fn writeSymbol(self: *Elf, index: usize) !void { | ... | @@ -2810,23 +2861,8 @@ fn writeSymbol(self: *Elf, index: usize) !void { |
| 2810 | .p64 => @alignOf(elf.Elf64_Sym), | 2861 | .p64 => @alignOf(elf.Elf64_Sym), |
| 2811 | }; | 2862 | }; |
| 2812 | const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size; | 2863 | const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size; |
| 2813 | if (needed_size > self.allocatedSize(syms_sect.sh_offset)) { | 2864 | try self.growNonAllocSection(self.symtab_section_index.?, needed_size, sym_align); |
| 2814 | // Move all the symbols to a new file location. | | |
| 2815 | const new_offset = self.findFreeSpace(needed_size, sym_align); | | |
| 2816 | log.debug("moving '.symtab' from 0x{x} to 0x{x}", .{ syms_sect.sh_offset, new_offset }); | | |
| 2817 | const existing_size = @as(u64, syms_sect.sh_info) * sym_size; | | |
| 2818 | const amt = try self.base.file.?.copyRangeAll( | | |
| 2819 | syms_sect.sh_offset, | | |
| 2820 | self.base.file.?, | | |
| 2821 | new_offset, | | |
| 2822 | existing_size, | | |
| 2823 | ); | | |
| 2824 | if (amt != existing_size) return error.InputOutput; | | |
| 2825 | syms_sect.sh_offset = new_offset; | | |
| 2826 | } | | |
| 2827 | syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len); | 2865 | syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len); |
| 2828 | syms_sect.sh_size = needed_size; // anticipating adding the global symbols later | | |
| 2829 | self.shdr_table_dirty = true; // TODO look into only writing one section | | |
| 2830 | } | 2866 | } |
| 2831 | const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); | 2867 | const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); |
| 2832 | const off = switch (self.ptr_width) { | 2868 | const off = switch (self.ptr_width) { |
| ... | @@ -2874,22 +2910,7 @@ fn writeAllGlobalSymbols(self: *Elf) !void { | ... | @@ -2874,22 +2910,7 @@ fn writeAllGlobalSymbols(self: *Elf) !void { |
| 2874 | .p64 => @alignOf(elf.Elf64_Sym), | 2910 | .p64 => @alignOf(elf.Elf64_Sym), |
| 2875 | }; | 2911 | }; |
| 2876 | const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size; | 2912 | const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size; |
| 2877 | if (needed_size > self.allocatedSize(syms_sect.sh_offset)) { | 2913 | try self.growNonAllocSection(self.symtab_section_index.?, needed_size, sym_align); |
| 2878 | // Move all the symbols to a new file location. | | |
| 2879 | const new_offset = self.findFreeSpace(needed_size, sym_align); | | |
| 2880 | log.debug("moving '.symtab' from 0x{x} to 0x{x}", .{ syms_sect.sh_offset, new_offset }); | | |
| 2881 | const existing_size = @as(u64, syms_sect.sh_info) * sym_size; | | |
| 2882 | const amt = try self.base.file.?.copyRangeAll( | | |
| 2883 | syms_sect.sh_offset, | | |
| 2884 | self.base.file.?, | | |
| 2885 | new_offset, | | |
| 2886 | existing_size, | | |
| 2887 | ); | | |
| 2888 | if (amt != existing_size) return error.InputOutput; | | |
| 2889 | syms_sect.sh_offset = new_offset; | | |
| 2890 | } | | |
| 2891 | syms_sect.sh_size = needed_size; // anticipating adding the global symbols later | | |
| 2892 | self.shdr_table_dirty = true; // TODO look into only writing one section | | |
| 2893 | | 2914 | |
| 2894 | const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); | 2915 | const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); |
| 2895 | const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size; | 2916 | const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size; |