| ... | @@ -110,7 +110,6 @@ pub const ElfFile = struct { | ... | @@ -110,7 +110,6 @@ pub const ElfFile = struct { |
| 110 | /// The index into the program headers of the global offset table. | 110 | /// The index into the program headers of the global offset table. |
| 111 | /// It needs PT_LOAD and Read flags. | 111 | /// It needs PT_LOAD and Read flags. |
| 112 | phdr_got_index: ?u16 = null, | 112 | phdr_got_index: ?u16 = null, |
| 113 | phdr_got_plt_index: ?u16 = null, | | |
| 114 | entry_addr: ?u64 = null, | 113 | entry_addr: ?u64 = null, |
| 115 | | 114 | |
| 116 | shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){}, | 115 | shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){}, |
| ... | @@ -119,7 +118,6 @@ pub const ElfFile = struct { | ... | @@ -119,7 +118,6 @@ pub const ElfFile = struct { |
| 119 | text_section_index: ?u16 = null, | 118 | text_section_index: ?u16 = null, |
| 120 | symtab_section_index: ?u16 = null, | 119 | symtab_section_index: ?u16 = null, |
| 121 | got_section_index: ?u16 = null, | 120 | got_section_index: ?u16 = null, |
| 122 | got_plt_section_index: ?u16 = null, | | |
| 123 | | 121 | |
| 124 | /// The same order as in the file. ELF requires global symbols to all be after the | 122 | /// The same order as in the file. ELF requires global symbols to all be after the |
| 125 | /// local symbols, they cannot be mixed. So we must buffer all the global symbols and | 123 | /// local symbols, they cannot be mixed. So we must buffer all the global symbols and |
| ... | @@ -132,16 +130,11 @@ pub const ElfFile = struct { | ... | @@ -132,16 +130,11 @@ pub const ElfFile = struct { |
| 132 | /// If the vaddr of the executable program header changes, the entire | 130 | /// If the vaddr of the executable program header changes, the entire |
| 133 | /// offset table needs to be rewritten. | 131 | /// offset table needs to be rewritten. |
| 134 | offset_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){}, | 132 | offset_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){}, |
| 135 | /// Same order as in the file. The value is the absolute vaddr value. | | |
| 136 | /// If the vaddr of the executable program header changes, the entire | | |
| 137 | /// fn trampoline table needs to be rewritten. | | |
| 138 | fn_trampoline_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){}, | | |
| 139 | | 133 | |
| 140 | phdr_table_dirty: bool = false, | 134 | phdr_table_dirty: bool = false, |
| 141 | shdr_table_dirty: bool = false, | 135 | shdr_table_dirty: bool = false, |
| 142 | shstrtab_dirty: bool = false, | 136 | shstrtab_dirty: bool = false, |
| 143 | offset_table_count_dirty: bool = false, | 137 | offset_table_count_dirty: bool = false, |
| 144 | fn_trampoline_table_count_dirty: bool = false, | | |
| 145 | | 138 | |
| 146 | error_flags: ErrorFlags = ErrorFlags{}, | 139 | error_flags: ErrorFlags = ErrorFlags{}, |
| 147 | | 140 | |
| ... | @@ -157,18 +150,12 @@ pub const ElfFile = struct { | ... | @@ -157,18 +150,12 @@ pub const ElfFile = struct { |
| 157 | /// If this field is 0, it means the codegen size = 0 and there is no symbol or | 150 | /// If this field is 0, it means the codegen size = 0 and there is no symbol or |
| 158 | /// offset table entry. | 151 | /// offset table entry. |
| 159 | local_sym_index: u32, | 152 | local_sym_index: u32, |
| 160 | /// when size = 0 and there is no offset table index | 153 | /// This field is undefined for symbols with size = 0. |
| 161 | offset_table_index: union { | 154 | offset_table_index: u32, |
| 162 | unallocated: void, | | |
| 163 | /// This is an index into offset_table | | |
| 164 | got: u32, | | |
| 165 | /// This is an index into fn_trampoline_table | | |
| 166 | plt: u32, | | |
| 167 | }, | | |
| 168 | | 155 | |
| 169 | pub const empty = Decl{ | 156 | pub const empty = Decl{ |
| 170 | .local_sym_index = 0, | 157 | .local_sym_index = 0, |
| 171 | .offset_table_index = .{ .unallocated = {} }, | 158 | .offset_table_index = undefined, |
| 172 | }; | 159 | }; |
| 173 | }; | 160 | }; |
| 174 | | 161 | |
| ... | @@ -183,7 +170,6 @@ pub const ElfFile = struct { | ... | @@ -183,7 +170,6 @@ pub const ElfFile = struct { |
| 183 | self.local_symbols.deinit(self.allocator); | 170 | self.local_symbols.deinit(self.allocator); |
| 184 | self.global_symbols.deinit(self.allocator); | 171 | self.global_symbols.deinit(self.allocator); |
| 185 | self.offset_table.deinit(self.allocator); | 172 | self.offset_table.deinit(self.allocator); |
| 186 | self.fn_trampoline_table.deinit(self.allocator); | | |
| 187 | if (self.owns_file_handle) { | 173 | if (self.owns_file_handle) { |
| 188 | if (self.file) |f| f.close(); | 174 | if (self.file) |f| f.close(); |
| 189 | } | 175 | } |
| ... | @@ -357,30 +343,6 @@ pub const ElfFile = struct { | ... | @@ -357,30 +343,6 @@ pub const ElfFile = struct { |
| 357 | }); | 343 | }); |
| 358 | self.phdr_table_dirty = true; | 344 | self.phdr_table_dirty = true; |
| 359 | } | 345 | } |
| 360 | if (self.phdr_got_plt_index == null) { | | |
| 361 | self.phdr_got_plt_index = @intCast(u16, self.program_headers.items.len); | | |
| 362 | const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint; | | |
| 363 | // We really only need ptr alignment but since we are using PROGBITS, linux requires | | |
| 364 | // page align. | | |
| 365 | const p_align = 0x1000; | | |
| 366 | const off = self.findFreeSpace(file_size, p_align); | | |
| 367 | //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | | |
| 368 | // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at. | | |
| 369 | // we'll need to re-use that function anyway, in case the GOT grows and overlaps something | | |
| 370 | // else in virtual memory. | | |
| 371 | const default_got_plt_addr = 0x6000000; | | |
| 372 | try self.program_headers.append(self.allocator, .{ | | |
| 373 | .p_type = elf.PT_LOAD, | | |
| 374 | .p_offset = off, | | |
| 375 | .p_filesz = file_size, | | |
| 376 | .p_vaddr = default_got_plt_addr, | | |
| 377 | .p_paddr = default_got_plt_addr, | | |
| 378 | .p_memsz = file_size, | | |
| 379 | .p_align = p_align, | | |
| 380 | .p_flags = elf.PF_R, | | |
| 381 | }); | | |
| 382 | self.phdr_table_dirty = true; | | |
| 383 | } | | |
| 384 | if (self.shstrtab_index == null) { | 346 | if (self.shstrtab_index == null) { |
| 385 | self.shstrtab_index = @intCast(u16, self.sections.items.len); | 347 | self.shstrtab_index = @intCast(u16, self.sections.items.len); |
| 386 | assert(self.shstrtab.items.len == 0); | 348 | assert(self.shstrtab.items.len == 0); |
| ... | @@ -438,24 +400,6 @@ pub const ElfFile = struct { | ... | @@ -438,24 +400,6 @@ pub const ElfFile = struct { |
| 438 | }); | 400 | }); |
| 439 | self.shdr_table_dirty = true; | 401 | self.shdr_table_dirty = true; |
| 440 | } | 402 | } |
| 441 | if (self.got_plt_section_index == null) { | | |
| 442 | self.got_plt_section_index = @intCast(u16, self.sections.items.len); | | |
| 443 | const phdr = &self.program_headers.items[self.phdr_got_plt_index.?]; | | |
| 444 | | | |
| 445 | try self.sections.append(self.allocator, .{ | | |
| 446 | .sh_name = try self.makeString(".got.plt"), | | |
| 447 | .sh_type = elf.SHT_PROGBITS, | | |
| 448 | .sh_flags = elf.SHF_ALLOC, | | |
| 449 | .sh_addr = phdr.p_vaddr, | | |
| 450 | .sh_offset = phdr.p_offset, | | |
| 451 | .sh_size = phdr.p_filesz, | | |
| 452 | .sh_link = 0, | | |
| 453 | .sh_info = 0, | | |
| 454 | .sh_addralign = phdr.p_align, | | |
| 455 | .sh_entsize = 0, | | |
| 456 | }); | | |
| 457 | self.shdr_table_dirty = true; | | |
| 458 | } | | |
| 459 | if (self.symtab_section_index == null) { | 403 | if (self.symtab_section_index == null) { |
| 460 | self.symtab_section_index = @intCast(u16, self.sections.items.len); | 404 | self.symtab_section_index = @intCast(u16, self.sections.items.len); |
| 461 | const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym); | 405 | const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym); |
| ... | @@ -640,7 +584,6 @@ pub const ElfFile = struct { | ... | @@ -640,7 +584,6 @@ pub const ElfFile = struct { |
| 640 | assert(!self.shdr_table_dirty); | 584 | assert(!self.shdr_table_dirty); |
| 641 | assert(!self.shstrtab_dirty); | 585 | assert(!self.shstrtab_dirty); |
| 642 | assert(!self.offset_table_count_dirty); | 586 | assert(!self.offset_table_count_dirty); |
| 643 | assert(!self.fn_trampoline_table_count_dirty); | | |
| 644 | const syms_sect = &self.sections.items[self.symtab_section_index.?]; | 587 | const syms_sect = &self.sections.items[self.symtab_section_index.?]; |
| 645 | assert(syms_sect.sh_info == self.local_symbols.items.len); | 588 | assert(syms_sect.sh_info == self.local_symbols.items.len); |
| 646 | } | 589 | } |
| ... | @@ -836,14 +779,10 @@ pub const ElfFile = struct { | ... | @@ -836,14 +779,10 @@ pub const ElfFile = struct { |
| 836 | pub fn allocateDeclIndexes(self: *ElfFile, decl: *Module.Decl) !void { | 779 | pub fn allocateDeclIndexes(self: *ElfFile, decl: *Module.Decl) !void { |
| 837 | if (decl.link.local_sym_index != 0) return; | 780 | if (decl.link.local_sym_index != 0) return; |
| 838 | | 781 | |
| 839 | const is_fn = (decl.typed_value.most_recent.typed_value.ty.zigTypeTag() == .Fn); | | |
| 840 | | | |
| 841 | try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1); | 782 | try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1); |
| 842 | try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1); | 783 | try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1); |
| 843 | try self.fn_trampoline_table.ensureCapacity(self.allocator, self.fn_trampoline_table.items.len + 1); | | |
| 844 | const local_sym_index = self.local_symbols.items.len; | 784 | const local_sym_index = self.local_symbols.items.len; |
| 845 | const offset_table_index = self.offset_table.items.len; | 785 | const offset_table_index = self.offset_table.items.len; |
| 846 | const fn_trampoline_table_index = self.fn_trampoline_table.items.len; | | |
| 847 | const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; | 786 | const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; |
| 848 | | 787 | |
| 849 | self.local_symbols.appendAssumeCapacity(.{ | 788 | self.local_symbols.appendAssumeCapacity(.{ |
| ... | @@ -854,20 +793,15 @@ pub const ElfFile = struct { | ... | @@ -854,20 +793,15 @@ pub const ElfFile = struct { |
| 854 | .st_value = phdr.p_vaddr, | 793 | .st_value = phdr.p_vaddr, |
| 855 | .st_size = 0, | 794 | .st_size = 0, |
| 856 | }); | 795 | }); |
| 857 | if (is_fn) { | 796 | errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1); |
| 858 | self.fn_trampoline_table.appendAssumeCapacity(0); | 797 | self.offset_table.appendAssumeCapacity(0); |
| 859 | self.fn_trampoline_table_count_dirty = true; | 798 | errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1); |
| 860 | } else { | 799 | |
| 861 | self.offset_table.appendAssumeCapacity(0); | 800 | self.offset_table_count_dirty = true; |
| 862 | self.offset_table_count_dirty = true; | | |
| 863 | } | | |
| 864 | | 801 | |
| 865 | decl.link = .{ | 802 | decl.link = .{ |
| 866 | .local_sym_index = @intCast(u32, local_sym_index), | 803 | .local_sym_index = @intCast(u32, local_sym_index), |
| 867 | .offset_table_index = if (is_fn) | 804 | .offset_table_index = @intCast(u32, offset_table_index), |
| 868 | .{ .plt = @intCast(u32, fn_trampoline_table_index) } | | |
| 869 | else | | |
| 870 | .{ .got = @intCast(u32, offset_table_index) }, | | |
| 871 | }; | 805 | }; |
| 872 | } | 806 | } |
| 873 | | 807 | |
| ... | @@ -885,7 +819,6 @@ pub const ElfFile = struct { | ... | @@ -885,7 +819,6 @@ pub const ElfFile = struct { |
| 885 | return; | 819 | return; |
| 886 | }, | 820 | }, |
| 887 | }; | 821 | }; |
| 888 | const is_fn = (typed_value.ty.zigTypeTag() == .Fn); | | |
| 889 | | 822 | |
| 890 | const required_alignment = typed_value.ty.abiAlignment(self.options.target); | 823 | const required_alignment = typed_value.ty.abiAlignment(self.options.target); |
| 891 | | 824 | |
| ... | @@ -905,13 +838,14 @@ pub const ElfFile = struct { | ... | @@ -905,13 +838,14 @@ pub const ElfFile = struct { |
| 905 | const file_offset = if (need_realloc) fo: { | 838 | const file_offset = if (need_realloc) fo: { |
| 906 | const new_block = try self.allocateTextBlock(code.len, required_alignment); | 839 | const new_block = try self.allocateTextBlock(code.len, required_alignment); |
| 907 | local_sym.st_value = new_block.vaddr; | 840 | local_sym.st_value = new_block.vaddr; |
| 908 | if (is_fn) { | 841 | self.offset_table.items[decl.link.offset_table_index] = new_block.vaddr; |
| 909 | self.fn_trampoline_table.items[decl.link.offset_table_index.plt] = new_block.vaddr; | 842 | |
| 910 | try self.writeFnTrampolineEntry(decl.link.offset_table_index.plt); | 843 | //std.debug.warn("{}: writing got index {}=0x{x}\n", .{ |
| 911 | } else { | 844 | // decl.name, |
| 912 | self.offset_table.items[decl.link.offset_table_index.got] = new_block.vaddr; | 845 | // decl.link.offset_table_index, |
| 913 | try self.writeOffsetTableEntry(decl.link.offset_table_index.got); | 846 | // self.offset_table.items[decl.link.offset_table_index], |
| 914 | } | 847 | //}); |
| | 848 | try self.writeOffsetTableEntry(decl.link.offset_table_index); |
| 915 | | 849 | |
| 916 | break :fo new_block.file_offset; | 850 | break :fo new_block.file_offset; |
| 917 | } else existing_block.file_offset; | 851 | } else existing_block.file_offset; |
| ... | @@ -928,13 +862,11 @@ pub const ElfFile = struct { | ... | @@ -928,13 +862,11 @@ pub const ElfFile = struct { |
| 928 | } else { | 862 | } else { |
| 929 | try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1); | 863 | try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1); |
| 930 | try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1); | 864 | try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1); |
| 931 | try self.fn_trampoline_table.ensureCapacity(self.allocator, self.fn_trampoline_table.items.len + 1); | | |
| 932 | const decl_name = mem.spanZ(decl.name); | 865 | const decl_name = mem.spanZ(decl.name); |
| 933 | const name_str_index = try self.makeString(decl_name); | 866 | const name_str_index = try self.makeString(decl_name); |
| 934 | const new_block = try self.allocateTextBlock(code.len, required_alignment); | 867 | const new_block = try self.allocateTextBlock(code.len, required_alignment); |
| 935 | const local_sym_index = self.local_symbols.items.len; | 868 | const local_sym_index = self.local_symbols.items.len; |
| 936 | const offset_table_index = self.offset_table.items.len; | 869 | const offset_table_index = self.offset_table.items.len; |
| 937 | const fn_trampoline_table_index = self.fn_trampoline_table.items.len; | | |
| 938 | | 870 | |
| 939 | //std.debug.warn("add symbol for {} at vaddr 0x{x}, size {}\n", .{ decl.name, new_block.vaddr, code.len }); | 871 | //std.debug.warn("add symbol for {} at vaddr 0x{x}, size {}\n", .{ decl.name, new_block.vaddr, code.len }); |
| 940 | self.local_symbols.appendAssumeCapacity(.{ | 872 | self.local_symbols.appendAssumeCapacity(.{ |
| ... | @@ -946,32 +878,17 @@ pub const ElfFile = struct { | ... | @@ -946,32 +878,17 @@ pub const ElfFile = struct { |
| 946 | .st_size = code.len, | 878 | .st_size = code.len, |
| 947 | }); | 879 | }); |
| 948 | errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1); | 880 | errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1); |
| 949 | if (is_fn) { | 881 | self.offset_table.appendAssumeCapacity(new_block.vaddr); |
| 950 | self.fn_trampoline_table.appendAssumeCapacity(new_block.vaddr); | 882 | errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1); |
| 951 | } else { | 883 | |
| 952 | self.offset_table.appendAssumeCapacity(new_block.vaddr); | 884 | self.offset_table_count_dirty = true; |
| 953 | } | | |
| 954 | errdefer if (is_fn) { | | |
| 955 | self.fn_trampoline_table.shrink(self.allocator, self.fn_trampoline_table.items.len - 1); | | |
| 956 | } else { | | |
| 957 | self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1); | | |
| 958 | }; | | |
| 959 | | 885 | |
| 960 | try self.writeSymbol(local_sym_index); | 886 | try self.writeSymbol(local_sym_index); |
| 961 | if (is_fn) { | 887 | try self.writeOffsetTableEntry(offset_table_index); |
| 962 | try self.writeFnTrampolineEntry(fn_trampoline_table_index); | | |
| 963 | self.fn_trampoline_table_count_dirty = true; | | |
| 964 | } else { | | |
| 965 | try self.writeOffsetTableEntry(offset_table_index); | | |
| 966 | self.offset_table_count_dirty = true; | | |
| 967 | } | | |
| 968 | | 888 | |
| 969 | decl.link = .{ | 889 | decl.link = .{ |
| 970 | .local_sym_index = @intCast(u32, local_sym_index), | 890 | .local_sym_index = @intCast(u32, local_sym_index), |
| 971 | .offset_table_index = if (is_fn) | 891 | .offset_table_index = @intCast(u32, offset_table_index), |
| 972 | .{ .plt = @intCast(u32, fn_trampoline_table_index) } | | |
| 973 | else | | |
| 974 | .{ .got = @intCast(u32, offset_table_index) }, | | |
| 975 | }; | 892 | }; |
| 976 | | 893 | |
| 977 | //std.debug.warn("writing new {} at vaddr 0x{x}\n", .{ decl.name, new_block.vaddr }); | 894 | //std.debug.warn("writing new {} at vaddr 0x{x}\n", .{ decl.name, new_block.vaddr }); |
| ... | @@ -1101,40 +1018,6 @@ pub const ElfFile = struct { | ... | @@ -1101,40 +1018,6 @@ pub const ElfFile = struct { |
| 1101 | } | 1018 | } |
| 1102 | } | 1019 | } |
| 1103 | | 1020 | |
| 1104 | fn writeFnTrampolineEntry(self: *ElfFile, index: usize) !void { | | |
| 1105 | const shdr = &self.sections.items[self.got_plt_section_index.?]; | | |
| 1106 | const phdr = &self.program_headers.items[self.phdr_got_plt_index.?]; | | |
| 1107 | const entry_size = codegen.pltEntrySize(self.options.target); | | |
| 1108 | var entry_buf: [16]u8 = undefined; | | |
| 1109 | assert(entry_size <= entry_buf.len); | | |
| 1110 | | | |
| 1111 | if (self.fn_trampoline_table_count_dirty) { | | |
| 1112 | // TODO Also detect virtual address collisions. | | |
| 1113 | const allocated_size = self.allocatedSize(shdr.sh_offset); | | |
| 1114 | const needed_size = self.local_symbols.items.len * entry_size; | | |
| 1115 | if (needed_size > allocated_size) { | | |
| 1116 | // Must move the entire .got.plt section. | | |
| 1117 | const new_offset = self.findFreeSpace(needed_size, entry_size); | | |
| 1118 | const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, shdr.sh_size); | | |
| 1119 | if (amt != shdr.sh_size) return error.InputOutput; | | |
| 1120 | shdr.sh_offset = new_offset; | | |
| 1121 | phdr.p_offset = new_offset; | | |
| 1122 | } | | |
| 1123 | shdr.sh_size = needed_size; | | |
| 1124 | phdr.p_memsz = needed_size; | | |
| 1125 | phdr.p_filesz = needed_size; | | |
| 1126 | | | |
| 1127 | self.shdr_table_dirty = true; // TODO look into making only the one section dirty | | |
| 1128 | self.phdr_table_dirty = true; // TODO look into making only the one program header dirty | | |
| 1129 | | | |
| 1130 | self.fn_trampoline_table_count_dirty = false; | | |
| 1131 | } | | |
| 1132 | const off = shdr.sh_offset + @as(u64, entry_size) * index; | | |
| 1133 | const vaddr = @intCast(u32, self.fn_trampoline_table.items[index]); | | |
| 1134 | codegen.writePltEntry(self.options.target, &entry_buf, vaddr); | | |
| 1135 | try self.file.?.pwriteAll(entry_buf[0..entry_size], off); | | |
| 1136 | } | | |
| 1137 | | | |
| 1138 | fn writeOffsetTableEntry(self: *ElfFile, index: usize) !void { | 1021 | fn writeOffsetTableEntry(self: *ElfFile, index: usize) !void { |
| 1139 | const shdr = &self.sections.items[self.got_section_index.?]; | 1022 | const shdr = &self.sections.items[self.got_section_index.?]; |
| 1140 | const phdr = &self.program_headers.items[self.phdr_got_index.?]; | 1023 | const phdr = &self.program_headers.items[self.phdr_got_index.?]; |