| ... | ... | @@ -110,7 +110,6 @@ pub const ElfFile = struct { |
| 110 | 110 | /// The index into the program headers of the global offset table. |
| 111 | 111 | /// It needs PT_LOAD and Read flags. |
| 112 | 112 | phdr_got_index: ?u16 = null, |
| 113 | | phdr_got_plt_index: ?u16 = null, |
| 114 | 113 | entry_addr: ?u64 = null, |
| 115 | 114 | |
| 116 | 115 | shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){}, |
| ... | ... | @@ -119,7 +118,6 @@ pub const ElfFile = struct { |
| 119 | 118 | text_section_index: ?u16 = null, |
| 120 | 119 | symtab_section_index: ?u16 = null, |
| 121 | 120 | got_section_index: ?u16 = null, |
| 122 | | got_plt_section_index: ?u16 = null, |
| 123 | 121 | |
| 124 | 122 | /// The same order as in the file. ELF requires global symbols to all be after the |
| 125 | 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 | 130 | /// If the vaddr of the executable program header changes, the entire |
| 133 | 131 | /// offset table needs to be rewritten. |
| 134 | 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 | 134 | phdr_table_dirty: bool = false, |
| 141 | 135 | shdr_table_dirty: bool = false, |
| 142 | 136 | shstrtab_dirty: bool = false, |
| 143 | 137 | offset_table_count_dirty: bool = false, |
| 144 | | fn_trampoline_table_count_dirty: bool = false, |
| 145 | 138 | |
| 146 | 139 | error_flags: ErrorFlags = ErrorFlags{}, |
| 147 | 140 | |
| ... | ... | @@ -157,18 +150,12 @@ pub const ElfFile = struct { |
| 157 | 150 | /// If this field is 0, it means the codegen size = 0 and there is no symbol or |
| 158 | 151 | /// offset table entry. |
| 159 | 152 | local_sym_index: u32, |
| 160 | | /// when size = 0 and there is no offset table index |
| 161 | | offset_table_index: union { |
| 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 | | }, |
| 153 | /// This field is undefined for symbols with size = 0. |
| 154 | offset_table_index: u32, |
| 168 | 155 | |
| 169 | 156 | pub const empty = Decl{ |
| 170 | 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 | 170 | self.local_symbols.deinit(self.allocator); |
| 184 | 171 | self.global_symbols.deinit(self.allocator); |
| 185 | 172 | self.offset_table.deinit(self.allocator); |
| 186 | | self.fn_trampoline_table.deinit(self.allocator); |
| 187 | 173 | if (self.owns_file_handle) { |
| 188 | 174 | if (self.file) |f| f.close(); |
| 189 | 175 | } |
| ... | ... | @@ -357,30 +343,6 @@ pub const ElfFile = struct { |
| 357 | 343 | }); |
| 358 | 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 | 346 | if (self.shstrtab_index == null) { |
| 385 | 347 | self.shstrtab_index = @intCast(u16, self.sections.items.len); |
| 386 | 348 | assert(self.shstrtab.items.len == 0); |
| ... | ... | @@ -438,24 +400,6 @@ pub const ElfFile = struct { |
| 438 | 400 | }); |
| 439 | 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 | 403 | if (self.symtab_section_index == null) { |
| 460 | 404 | self.symtab_section_index = @intCast(u16, self.sections.items.len); |
| 461 | 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 | 584 | assert(!self.shdr_table_dirty); |
| 641 | 585 | assert(!self.shstrtab_dirty); |
| 642 | 586 | assert(!self.offset_table_count_dirty); |
| 643 | | assert(!self.fn_trampoline_table_count_dirty); |
| 644 | 587 | const syms_sect = &self.sections.items[self.symtab_section_index.?]; |
| 645 | 588 | assert(syms_sect.sh_info == self.local_symbols.items.len); |
| 646 | 589 | } |
| ... | ... | @@ -836,14 +779,10 @@ pub const ElfFile = struct { |
| 836 | 779 | pub fn allocateDeclIndexes(self: *ElfFile, decl: *Module.Decl) !void { |
| 837 | 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 | 782 | try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1); |
| 842 | 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 | 784 | const local_sym_index = self.local_symbols.items.len; |
| 845 | 785 | const offset_table_index = self.offset_table.items.len; |
| 846 | | const fn_trampoline_table_index = self.fn_trampoline_table.items.len; |
| 847 | 786 | const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; |
| 848 | 787 | |
| 849 | 788 | self.local_symbols.appendAssumeCapacity(.{ |
| ... | ... | @@ -854,20 +793,15 @@ pub const ElfFile = struct { |
| 854 | 793 | .st_value = phdr.p_vaddr, |
| 855 | 794 | .st_size = 0, |
| 856 | 795 | }); |
| 857 | | if (is_fn) { |
| 858 | | self.fn_trampoline_table.appendAssumeCapacity(0); |
| 859 | | self.fn_trampoline_table_count_dirty = true; |
| 860 | | } else { |
| 861 | | self.offset_table.appendAssumeCapacity(0); |
| 862 | | self.offset_table_count_dirty = true; |
| 863 | | } |
| 796 | errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1); |
| 797 | self.offset_table.appendAssumeCapacity(0); |
| 798 | errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1); |
| 799 | |
| 800 | self.offset_table_count_dirty = true; |
| 864 | 801 | |
| 865 | 802 | decl.link = .{ |
| 866 | 803 | .local_sym_index = @intCast(u32, local_sym_index), |
| 867 | | .offset_table_index = if (is_fn) |
| 868 | | .{ .plt = @intCast(u32, fn_trampoline_table_index) } |
| 869 | | else |
| 870 | | .{ .got = @intCast(u32, offset_table_index) }, |
| 804 | .offset_table_index = @intCast(u32, offset_table_index), |
| 871 | 805 | }; |
| 872 | 806 | } |
| 873 | 807 | |
| ... | ... | @@ -885,7 +819,6 @@ pub const ElfFile = struct { |
| 885 | 819 | return; |
| 886 | 820 | }, |
| 887 | 821 | }; |
| 888 | | const is_fn = (typed_value.ty.zigTypeTag() == .Fn); |
| 889 | 822 | |
| 890 | 823 | const required_alignment = typed_value.ty.abiAlignment(self.options.target); |
| 891 | 824 | |
| ... | ... | @@ -905,13 +838,14 @@ pub const ElfFile = struct { |
| 905 | 838 | const file_offset = if (need_realloc) fo: { |
| 906 | 839 | const new_block = try self.allocateTextBlock(code.len, required_alignment); |
| 907 | 840 | local_sym.st_value = new_block.vaddr; |
| 908 | | if (is_fn) { |
| 909 | | self.fn_trampoline_table.items[decl.link.offset_table_index.plt] = new_block.vaddr; |
| 910 | | try self.writeFnTrampolineEntry(decl.link.offset_table_index.plt); |
| 911 | | } else { |
| 912 | | self.offset_table.items[decl.link.offset_table_index.got] = new_block.vaddr; |
| 913 | | try self.writeOffsetTableEntry(decl.link.offset_table_index.got); |
| 914 | | } |
| 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 | //}); |
| 848 | try self.writeOffsetTableEntry(decl.link.offset_table_index); |
| 915 | 849 | |
| 916 | 850 | break :fo new_block.file_offset; |
| 917 | 851 | } else existing_block.file_offset; |
| ... | ... | @@ -928,13 +862,11 @@ pub const ElfFile = struct { |
| 928 | 862 | } else { |
| 929 | 863 | try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1); |
| 930 | 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 | 865 | const decl_name = mem.spanZ(decl.name); |
| 933 | 866 | const name_str_index = try self.makeString(decl_name); |
| 934 | 867 | const new_block = try self.allocateTextBlock(code.len, required_alignment); |
| 935 | 868 | const local_sym_index = self.local_symbols.items.len; |
| 936 | 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 | 871 | //std.debug.warn("add symbol for {} at vaddr 0x{x}, size {}\n", .{ decl.name, new_block.vaddr, code.len }); |
| 940 | 872 | self.local_symbols.appendAssumeCapacity(.{ |
| ... | ... | @@ -946,32 +878,17 @@ pub const ElfFile = struct { |
| 946 | 878 | .st_size = code.len, |
| 947 | 879 | }); |
| 948 | 880 | errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1); |
| 949 | | if (is_fn) { |
| 950 | | self.fn_trampoline_table.appendAssumeCapacity(new_block.vaddr); |
| 951 | | } else { |
| 952 | | self.offset_table.appendAssumeCapacity(new_block.vaddr); |
| 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 | | }; |
| 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; |
| 959 | 885 | |
| 960 | 886 | try self.writeSymbol(local_sym_index); |
| 961 | | if (is_fn) { |
| 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 | | } |
| 887 | try self.writeOffsetTableEntry(offset_table_index); |
| 968 | 888 | |
| 969 | 889 | decl.link = .{ |
| 970 | 890 | .local_sym_index = @intCast(u32, local_sym_index), |
| 971 | | .offset_table_index = if (is_fn) |
| 972 | | .{ .plt = @intCast(u32, fn_trampoline_table_index) } |
| 973 | | else |
| 974 | | .{ .got = @intCast(u32, offset_table_index) }, |
| 891 | .offset_table_index = @intCast(u32, offset_table_index), |
| 975 | 892 | }; |
| 976 | 893 | |
| 977 | 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 | 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 | 1021 | fn writeOffsetTableEntry(self: *ElfFile, index: usize) !void { |
| 1139 | 1022 | const shdr = &self.sections.items[self.got_section_index.?]; |
| 1140 | 1023 | const phdr = &self.program_headers.items[self.phdr_got_index.?]; |