| ... | @@ -118,8 +118,12 @@ pub const ElfFile = struct { | ... | @@ -118,8 +118,12 @@ pub const ElfFile = struct { |
| 118 | symtab_section_index: ?u16 = null, | 118 | symtab_section_index: ?u16 = null, |
| 119 | got_section_index: ?u16 = null, | 119 | got_section_index: ?u16 = null, |
| 120 | | 120 | |
| 121 | /// The same order as in the file | 121 | /// The same order as in the file. ELF requires global symbols to all be after the |
| 122 | symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){}, | 122 | /// local symbols, they cannot be mixed. So we must buffer all the global symbols and |
| | 123 | /// write them at the end. These are only the local symbols. The length of this array |
| | 124 | /// is the value used for sh_info in the .symtab section. |
| | 125 | local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){}, |
| | 126 | global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){}, |
| 123 | | 127 | |
| 124 | /// Same order as in the file. The value is the absolute vaddr value. | 128 | /// Same order as in the file. The value is the absolute vaddr value. |
| 125 | /// If the vaddr of the executable program header changes, the entire | 129 | /// If the vaddr of the executable program header changes, the entire |
| ... | @@ -130,7 +134,6 @@ pub const ElfFile = struct { | ... | @@ -130,7 +134,6 @@ pub const ElfFile = struct { |
| 130 | shdr_table_dirty: bool = false, | 134 | shdr_table_dirty: bool = false, |
| 131 | shstrtab_dirty: bool = false, | 135 | shstrtab_dirty: bool = false, |
| 132 | offset_table_count_dirty: bool = false, | 136 | offset_table_count_dirty: bool = false, |
| 133 | symbol_count_dirty: bool = false, | | |
| 134 | | 137 | |
| 135 | error_flags: ErrorFlags = ErrorFlags{}, | 138 | error_flags: ErrorFlags = ErrorFlags{}, |
| 136 | | 139 | |
| ... | @@ -156,14 +159,15 @@ pub const ElfFile = struct { | ... | @@ -156,14 +159,15 @@ pub const ElfFile = struct { |
| 156 | }; | 159 | }; |
| 157 | | 160 | |
| 158 | pub const Export = struct { | 161 | pub const Export = struct { |
| 159 | sym_index: ?usize = null, | 162 | sym_index: ?u32 = null, |
| 160 | }; | 163 | }; |
| 161 | | 164 | |
| 162 | pub fn deinit(self: *ElfFile) void { | 165 | pub fn deinit(self: *ElfFile) void { |
| 163 | self.sections.deinit(self.allocator); | 166 | self.sections.deinit(self.allocator); |
| 164 | self.program_headers.deinit(self.allocator); | 167 | self.program_headers.deinit(self.allocator); |
| 165 | self.shstrtab.deinit(self.allocator); | 168 | self.shstrtab.deinit(self.allocator); |
| 166 | self.symbols.deinit(self.allocator); | 169 | self.local_symbols.deinit(self.allocator); |
| | 170 | self.global_symbols.deinit(self.allocator); |
| 167 | self.offset_table.deinit(self.allocator); | 171 | self.offset_table.deinit(self.allocator); |
| 168 | if (self.owns_file_handle) | 172 | if (self.owns_file_handle) |
| 169 | self.file.close(); | 173 | self.file.close(); |
| ... | @@ -298,7 +302,10 @@ pub const ElfFile = struct { | ... | @@ -298,7 +302,10 @@ pub const ElfFile = struct { |
| 298 | if (self.phdr_got_index == null) { | 302 | if (self.phdr_got_index == null) { |
| 299 | self.phdr_got_index = @intCast(u16, self.program_headers.items.len); | 303 | self.phdr_got_index = @intCast(u16, self.program_headers.items.len); |
| 300 | const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint; | 304 | const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint; |
| 301 | const off = self.findFreeSpace(file_size, ptr_size); | 305 | // We really only need ptr alignment but since we are using PROGBITS, linux requires |
| | 306 | // page align. |
| | 307 | const p_align = 0x1000; |
| | 308 | const off = self.findFreeSpace(file_size, p_align); |
| 302 | //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 309 | //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 303 | // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at. | 310 | // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at. |
| 304 | // we'll need to re-use that function anyway, in case the GOT grows and overlaps something | 311 | // we'll need to re-use that function anyway, in case the GOT grows and overlaps something |
| ... | @@ -311,7 +318,7 @@ pub const ElfFile = struct { | ... | @@ -311,7 +318,7 @@ pub const ElfFile = struct { |
| 311 | .p_vaddr = default_got_addr, | 318 | .p_vaddr = default_got_addr, |
| 312 | .p_paddr = default_got_addr, | 319 | .p_paddr = default_got_addr, |
| 313 | .p_memsz = file_size, | 320 | .p_memsz = file_size, |
| 314 | .p_align = ptr_size, | 321 | .p_align = p_align, |
| 315 | .p_flags = elf.PF_R, | 322 | .p_flags = elf.PF_R, |
| 316 | }); | 323 | }); |
| 317 | self.phdr_table_dirty = true; | 324 | self.phdr_table_dirty = true; |
| ... | @@ -369,7 +376,7 @@ pub const ElfFile = struct { | ... | @@ -369,7 +376,7 @@ pub const ElfFile = struct { |
| 369 | .sh_link = 0, | 376 | .sh_link = 0, |
| 370 | .sh_info = 0, | 377 | .sh_info = 0, |
| 371 | .sh_addralign = phdr.p_align, | 378 | .sh_addralign = phdr.p_align, |
| 372 | .sh_entsize = ptr_size, | 379 | .sh_entsize = 0, |
| 373 | }); | 380 | }); |
| 374 | self.shdr_table_dirty = true; | 381 | self.shdr_table_dirty = true; |
| 375 | } | 382 | } |
| ... | @@ -390,12 +397,12 @@ pub const ElfFile = struct { | ... | @@ -390,12 +397,12 @@ pub const ElfFile = struct { |
| 390 | .sh_size = file_size, | 397 | .sh_size = file_size, |
| 391 | // The section header index of the associated string table. | 398 | // The section header index of the associated string table. |
| 392 | .sh_link = self.shstrtab_index.?, | 399 | .sh_link = self.shstrtab_index.?, |
| 393 | .sh_info = @intCast(u32, self.symbols.items.len), | 400 | .sh_info = @intCast(u32, self.local_symbols.items.len), |
| 394 | .sh_addralign = min_align, | 401 | .sh_addralign = min_align, |
| 395 | .sh_entsize = each_size, | 402 | .sh_entsize = each_size, |
| 396 | }); | 403 | }); |
| 397 | self.shdr_table_dirty = true; | 404 | self.shdr_table_dirty = true; |
| 398 | try self.writeAllSymbols(); | 405 | try self.writeSymbol(0); |
| 399 | } | 406 | } |
| 400 | const shsize: u64 = switch (self.ptr_width) { | 407 | const shsize: u64 = switch (self.ptr_width) { |
| 401 | .p32 => @sizeOf(elf.Elf32_Shdr), | 408 | .p32 => @sizeOf(elf.Elf32_Shdr), |
| ... | @@ -427,6 +434,10 @@ pub const ElfFile = struct { | ... | @@ -427,6 +434,10 @@ pub const ElfFile = struct { |
| 427 | pub fn flush(self: *ElfFile) !void { | 434 | pub fn flush(self: *ElfFile) !void { |
| 428 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | 435 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); |
| 429 | | 436 | |
| | 437 | // Unfortunately these have to be buffered and done at the end because ELF does not allow |
| | 438 | // mixing local and global symbols within a symbol table. |
| | 439 | try self.writeAllGlobalSymbols(); |
| | 440 | |
| 430 | if (self.phdr_table_dirty) { | 441 | if (self.phdr_table_dirty) { |
| 431 | const phsize: u64 = switch (self.ptr_width) { | 442 | const phsize: u64 = switch (self.ptr_width) { |
| 432 | .p32 => @sizeOf(elf.Elf32_Phdr), | 443 | .p32 => @sizeOf(elf.Elf32_Phdr), |
| ... | @@ -552,8 +563,9 @@ pub const ElfFile = struct { | ... | @@ -552,8 +563,9 @@ pub const ElfFile = struct { |
| 552 | assert(!self.phdr_table_dirty); | 563 | assert(!self.phdr_table_dirty); |
| 553 | assert(!self.shdr_table_dirty); | 564 | assert(!self.shdr_table_dirty); |
| 554 | assert(!self.shstrtab_dirty); | 565 | assert(!self.shstrtab_dirty); |
| 555 | assert(!self.symbol_count_dirty); | | |
| 556 | assert(!self.offset_table_count_dirty); | 566 | assert(!self.offset_table_count_dirty); |
| | 567 | const syms_sect = &self.sections.items[self.symtab_section_index.?]; |
| | 568 | assert(syms_sect.sh_info == self.local_symbols.items.len); |
| 557 | } | 569 | } |
| 558 | | 570 | |
| 559 | fn writeElfHeader(self: *ElfFile) !void { | 571 | fn writeElfHeader(self: *ElfFile) !void { |
| ... | @@ -683,7 +695,7 @@ pub const ElfFile = struct { | ... | @@ -683,7 +695,7 @@ pub const ElfFile = struct { |
| 683 | size_capacity: u64, | 695 | size_capacity: u64, |
| 684 | }; | 696 | }; |
| 685 | | 697 | |
| 686 | fn allocateTextBlock(self: *ElfFile, new_block_size: u64) !AllocatedBlock { | 698 | fn allocateTextBlock(self: *ElfFile, new_block_size: u64, alignment: u64) !AllocatedBlock { |
| 687 | const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; | 699 | const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; |
| 688 | const shdr = &self.sections.items[self.text_section_index.?]; | 700 | const shdr = &self.sections.items[self.text_section_index.?]; |
| 689 | | 701 | |
| ... | @@ -692,14 +704,15 @@ pub const ElfFile = struct { | ... | @@ -692,14 +704,15 @@ pub const ElfFile = struct { |
| 692 | // TODO instead of looping here, maintain a free list and a pointer to the end. | 704 | // TODO instead of looping here, maintain a free list and a pointer to the end. |
| 693 | var last_start: u64 = phdr.p_vaddr; | 705 | var last_start: u64 = phdr.p_vaddr; |
| 694 | var last_size: u64 = 0; | 706 | var last_size: u64 = 0; |
| 695 | for (self.symbols.items) |sym| { | 707 | for (self.local_symbols.items) |sym| { |
| 696 | if (sym.st_value > last_start) { | 708 | if (sym.st_value + sym.st_size > last_start + last_size) { |
| 697 | last_start = sym.st_value; | 709 | last_start = sym.st_value; |
| 698 | last_size = sym.st_size; | 710 | last_size = sym.st_size; |
| 699 | } | 711 | } |
| 700 | } | 712 | } |
| 701 | const end_vaddr = last_start + (last_size * alloc_num / alloc_den); | 713 | const end_vaddr = last_start + (last_size * alloc_num / alloc_den); |
| 702 | const needed_size = (end_vaddr + new_block_size) - phdr.p_vaddr; | 714 | const aligned_start_vaddr = mem.alignForwardGeneric(u64, end_vaddr, alignment); |
| | 715 | const needed_size = (aligned_start_vaddr + new_block_size) - phdr.p_vaddr; |
| 703 | if (needed_size > text_capacity) { | 716 | if (needed_size > text_capacity) { |
| 704 | // Must move the entire text section. | 717 | // Must move the entire text section. |
| 705 | const new_offset = self.findFreeSpace(needed_size, 0x1000); | 718 | const new_offset = self.findFreeSpace(needed_size, 0x1000); |
| ... | @@ -717,9 +730,9 @@ pub const ElfFile = struct { | ... | @@ -717,9 +730,9 @@ pub const ElfFile = struct { |
| 717 | self.shdr_table_dirty = true; // TODO look into making only the one section dirty | 730 | self.shdr_table_dirty = true; // TODO look into making only the one section dirty |
| 718 | | 731 | |
| 719 | return AllocatedBlock{ | 732 | return AllocatedBlock{ |
| 720 | .vaddr = end_vaddr, | 733 | .vaddr = aligned_start_vaddr, |
| 721 | .file_offset = shdr.sh_offset + (end_vaddr - phdr.p_vaddr), | 734 | .file_offset = shdr.sh_offset + (aligned_start_vaddr - phdr.p_vaddr), |
| 722 | .size_capacity = text_capacity - end_vaddr, | 735 | .size_capacity = text_capacity - needed_size, |
| 723 | }; | 736 | }; |
| 724 | } | 737 | } |
| 725 | | 738 | |
| ... | @@ -731,7 +744,7 @@ pub const ElfFile = struct { | ... | @@ -731,7 +744,7 @@ pub const ElfFile = struct { |
| 731 | // TODO look into using a hash map to speed up perf. | 744 | // TODO look into using a hash map to speed up perf. |
| 732 | const text_capacity = self.allocatedSize(shdr.sh_offset); | 745 | const text_capacity = self.allocatedSize(shdr.sh_offset); |
| 733 | var next_vaddr_start = phdr.p_vaddr + text_capacity; | 746 | var next_vaddr_start = phdr.p_vaddr + text_capacity; |
| 734 | for (self.symbols.items) |elem| { | 747 | for (self.local_symbols.items) |elem| { |
| 735 | if (elem.st_value < sym.st_value) continue; | 748 | if (elem.st_value < sym.st_value) continue; |
| 736 | if (elem.st_value < next_vaddr_start) next_vaddr_start = elem.st_value; | 749 | if (elem.st_value < next_vaddr_start) next_vaddr_start = elem.st_value; |
| 737 | } | 750 | } |
| ... | @@ -748,7 +761,8 @@ pub const ElfFile = struct { | ... | @@ -748,7 +761,8 @@ pub const ElfFile = struct { |
| 748 | | 761 | |
| 749 | const typed_value = decl.typed_value.most_recent.typed_value; | 762 | const typed_value = decl.typed_value.most_recent.typed_value; |
| 750 | const code = switch (try codegen.generateSymbol(self, decl.src, typed_value, &code_buffer)) { | 763 | const code = switch (try codegen.generateSymbol(self, decl.src, typed_value, &code_buffer)) { |
| 751 | .ok => |x| x, | 764 | .externally_managed => |x| x, |
| | 765 | .appended => code_buffer.items, |
| 752 | .fail => |em| { | 766 | .fail => |em| { |
| 753 | decl.analysis = .codegen_failure; | 767 | decl.analysis = .codegen_failure; |
| 754 | _ = try module.failed_decls.put(decl, em); | 768 | _ = try module.failed_decls.put(decl, em); |
| ... | @@ -756,20 +770,23 @@ pub const ElfFile = struct { | ... | @@ -756,20 +770,23 @@ pub const ElfFile = struct { |
| 756 | }, | 770 | }, |
| 757 | }; | 771 | }; |
| 758 | | 772 | |
| | 773 | const required_alignment = typed_value.ty.abiAlignment(self.options.target); |
| | 774 | |
| 759 | const file_offset = blk: { | 775 | const file_offset = blk: { |
| 760 | const code_size = code.len; | | |
| 761 | const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) { | 776 | const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) { |
| 762 | .Fn => elf.STT_FUNC, | 777 | .Fn => elf.STT_FUNC, |
| 763 | else => elf.STT_OBJECT, | 778 | else => elf.STT_OBJECT, |
| 764 | }; | 779 | }; |
| 765 | | 780 | |
| 766 | if (decl.link.local_sym_index != 0) { | 781 | if (decl.link.local_sym_index != 0) { |
| 767 | const local_sym = &self.symbols.items[decl.link.local_sym_index]; | 782 | const local_sym = &self.local_symbols.items[decl.link.local_sym_index]; |
| 768 | const existing_block = self.findAllocatedTextBlock(local_sym.*); | 783 | const existing_block = self.findAllocatedTextBlock(local_sym.*); |
| 769 | const file_offset = if (code_size > existing_block.size_capacity) fo: { | 784 | const need_realloc = code.len > existing_block.size_capacity or |
| 770 | const new_block = try self.allocateTextBlock(code_size); | 785 | !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment); |
| | 786 | const file_offset = if (need_realloc) fo: { |
| | 787 | const new_block = try self.allocateTextBlock(code.len, required_alignment); |
| 771 | local_sym.st_value = new_block.vaddr; | 788 | local_sym.st_value = new_block.vaddr; |
| 772 | local_sym.st_size = code_size; | 789 | local_sym.st_size = code.len; |
| 773 | | 790 | |
| 774 | try self.writeOffsetTableEntry(decl.link.offset_table_index); | 791 | try self.writeOffsetTableEntry(decl.link.offset_table_index); |
| 775 | | 792 | |
| ... | @@ -781,27 +798,27 @@ pub const ElfFile = struct { | ... | @@ -781,27 +798,27 @@ pub const ElfFile = struct { |
| 781 | try self.writeSymbol(decl.link.local_sym_index); | 798 | try self.writeSymbol(decl.link.local_sym_index); |
| 782 | break :blk file_offset; | 799 | break :blk file_offset; |
| 783 | } else { | 800 | } else { |
| 784 | try self.symbols.ensureCapacity(self.allocator, self.symbols.items.len + 1); | 801 | try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1); |
| 785 | try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1); | 802 | try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1); |
| 786 | const decl_name = mem.spanZ(decl.name); | 803 | const decl_name = mem.spanZ(decl.name); |
| 787 | const name_str_index = try self.makeString(decl_name); | 804 | const name_str_index = try self.makeString(decl_name); |
| 788 | const new_block = try self.allocateTextBlock(code_size); | 805 | const new_block = try self.allocateTextBlock(code.len, required_alignment); |
| 789 | const local_sym_index = self.symbols.items.len; | 806 | const local_sym_index = self.local_symbols.items.len; |
| 790 | const offset_table_index = self.offset_table.items.len; | 807 | const offset_table_index = self.offset_table.items.len; |
| 791 | | 808 | |
| 792 | self.symbols.appendAssumeCapacity(.{ | 809 | //std.debug.warn("add symbol for {} at vaddr 0x{x}, size {}\n", .{ decl.name, new_block.vaddr, code.len }); |
| | 810 | self.local_symbols.appendAssumeCapacity(.{ |
| 793 | .st_name = name_str_index, | 811 | .st_name = name_str_index, |
| 794 | .st_info = (elf.STB_LOCAL << 4) | stt_bits, | 812 | .st_info = (elf.STB_LOCAL << 4) | stt_bits, |
| 795 | .st_other = 0, | 813 | .st_other = 0, |
| 796 | .st_shndx = self.text_section_index.?, | 814 | .st_shndx = self.text_section_index.?, |
| 797 | .st_value = new_block.vaddr, | 815 | .st_value = new_block.vaddr, |
| 798 | .st_size = code_size, | 816 | .st_size = code.len, |
| 799 | }); | 817 | }); |
| 800 | errdefer self.symbols.shrink(self.allocator, self.symbols.items.len - 1); | 818 | errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1); |
| 801 | self.offset_table.appendAssumeCapacity(new_block.vaddr); | 819 | self.offset_table.appendAssumeCapacity(new_block.vaddr); |
| 802 | errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1); | 820 | errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1); |
| 803 | | 821 | |
| 804 | self.symbol_count_dirty = true; | | |
| 805 | self.offset_table_count_dirty = true; | 822 | self.offset_table_count_dirty = true; |
| 806 | | 823 | |
| 807 | try self.writeSymbol(local_sym_index); | 824 | try self.writeSymbol(local_sym_index); |
| ... | @@ -830,10 +847,10 @@ pub const ElfFile = struct { | ... | @@ -830,10 +847,10 @@ pub const ElfFile = struct { |
| 830 | decl: *const ir.Module.Decl, | 847 | decl: *const ir.Module.Decl, |
| 831 | exports: []const *ir.Module.Export, | 848 | exports: []const *ir.Module.Export, |
| 832 | ) !void { | 849 | ) !void { |
| 833 | try self.symbols.ensureCapacity(self.allocator, self.symbols.items.len + exports.len); | 850 | try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len); |
| 834 | const typed_value = decl.typed_value.most_recent.typed_value; | 851 | const typed_value = decl.typed_value.most_recent.typed_value; |
| 835 | if (decl.link.local_sym_index == 0) return; | 852 | if (decl.link.local_sym_index == 0) return; |
| 836 | const decl_sym = self.symbols.items[decl.link.local_sym_index]; | 853 | const decl_sym = self.local_symbols.items[decl.link.local_sym_index]; |
| 837 | | 854 | |
| 838 | for (exports) |exp| { | 855 | for (exports) |exp| { |
| 839 | if (exp.options.section) |section_name| { | 856 | if (exp.options.section) |section_name| { |
| ... | @@ -866,7 +883,7 @@ pub const ElfFile = struct { | ... | @@ -866,7 +883,7 @@ pub const ElfFile = struct { |
| 866 | }; | 883 | }; |
| 867 | const stt_bits: u8 = @truncate(u4, decl_sym.st_info); | 884 | const stt_bits: u8 = @truncate(u4, decl_sym.st_info); |
| 868 | if (exp.link.sym_index) |i| { | 885 | if (exp.link.sym_index) |i| { |
| 869 | const sym = &self.symbols.items[i]; | 886 | const sym = &self.global_symbols.items[i]; |
| 870 | sym.* = .{ | 887 | sym.* = .{ |
| 871 | .st_name = try self.updateString(sym.st_name, exp.options.name), | 888 | .st_name = try self.updateString(sym.st_name, exp.options.name), |
| 872 | .st_info = (stb_bits << 4) | stt_bits, | 889 | .st_info = (stb_bits << 4) | stt_bits, |
| ... | @@ -875,11 +892,10 @@ pub const ElfFile = struct { | ... | @@ -875,11 +892,10 @@ pub const ElfFile = struct { |
| 875 | .st_value = decl_sym.st_value, | 892 | .st_value = decl_sym.st_value, |
| 876 | .st_size = decl_sym.st_size, | 893 | .st_size = decl_sym.st_size, |
| 877 | }; | 894 | }; |
| 878 | try self.writeSymbol(i); | | |
| 879 | } else { | 895 | } else { |
| 880 | const name = try self.makeString(exp.options.name); | 896 | const name = try self.makeString(exp.options.name); |
| 881 | const i = self.symbols.items.len; | 897 | const i = self.global_symbols.items.len; |
| 882 | self.symbols.appendAssumeCapacity(.{ | 898 | self.global_symbols.appendAssumeCapacity(.{ |
| 883 | .st_name = name, | 899 | .st_name = name, |
| 884 | .st_info = (stb_bits << 4) | stt_bits, | 900 | .st_info = (stb_bits << 4) | stt_bits, |
| 885 | .st_other = 0, | 901 | .st_other = 0, |
| ... | @@ -887,11 +903,9 @@ pub const ElfFile = struct { | ... | @@ -887,11 +903,9 @@ pub const ElfFile = struct { |
| 887 | .st_value = decl_sym.st_value, | 903 | .st_value = decl_sym.st_value, |
| 888 | .st_size = decl_sym.st_size, | 904 | .st_size = decl_sym.st_size, |
| 889 | }); | 905 | }); |
| 890 | errdefer self.symbols.shrink(self.allocator, self.symbols.items.len - 1); | 906 | errdefer self.global_symbols.shrink(self.allocator, self.global_symbols.items.len - 1); |
| 891 | try self.writeSymbol(i); | | |
| 892 | | 907 | |
| 893 | self.symbol_count_dirty = true; | 908 | exp.link.sym_index = @intCast(u32, i); |
| 894 | exp.link.sym_index = i; | | |
| 895 | } | 909 | } |
| 896 | } | 910 | } |
| 897 | } | 911 | } |
| ... | @@ -944,13 +958,17 @@ pub const ElfFile = struct { | ... | @@ -944,13 +958,17 @@ pub const ElfFile = struct { |
| 944 | fn writeOffsetTableEntry(self: *ElfFile, index: usize) !void { | 958 | fn writeOffsetTableEntry(self: *ElfFile, index: usize) !void { |
| 945 | const shdr = &self.sections.items[self.got_section_index.?]; | 959 | const shdr = &self.sections.items[self.got_section_index.?]; |
| 946 | const phdr = &self.program_headers.items[self.phdr_got_index.?]; | 960 | const phdr = &self.program_headers.items[self.phdr_got_index.?]; |
| | 961 | const entry_size: u16 = switch (self.ptr_width) { |
| | 962 | .p32 => 4, |
| | 963 | .p64 => 8, |
| | 964 | }; |
| 947 | if (self.offset_table_count_dirty) { | 965 | if (self.offset_table_count_dirty) { |
| 948 | // TODO Also detect virtual address collisions. | 966 | // TODO Also detect virtual address collisions. |
| 949 | const allocated_size = self.allocatedSize(shdr.sh_offset); | 967 | const allocated_size = self.allocatedSize(shdr.sh_offset); |
| 950 | const needed_size = self.symbols.items.len * shdr.sh_entsize; | 968 | const needed_size = self.local_symbols.items.len * entry_size; |
| 951 | if (needed_size > allocated_size) { | 969 | if (needed_size > allocated_size) { |
| 952 | // Must move the entire got section. | 970 | // Must move the entire got section. |
| 953 | const new_offset = self.findFreeSpace(needed_size, @intCast(u16, shdr.sh_entsize)); | 971 | const new_offset = self.findFreeSpace(needed_size, entry_size); |
| 954 | const amt = try self.file.copyRangeAll(shdr.sh_offset, self.file, new_offset, shdr.sh_size); | 972 | const amt = try self.file.copyRangeAll(shdr.sh_offset, self.file, new_offset, shdr.sh_size); |
| 955 | if (amt != shdr.sh_size) return error.InputOutput; | 973 | if (amt != shdr.sh_size) return error.InputOutput; |
| 956 | shdr.sh_offset = new_offset; | 974 | shdr.sh_offset = new_offset; |
| ... | @@ -965,7 +983,7 @@ pub const ElfFile = struct { | ... | @@ -965,7 +983,7 @@ pub const ElfFile = struct { |
| 965 | self.offset_table_count_dirty = false; | 983 | self.offset_table_count_dirty = false; |
| 966 | } | 984 | } |
| 967 | const endian = self.options.target.cpu.arch.endian(); | 985 | const endian = self.options.target.cpu.arch.endian(); |
| 968 | const off = shdr.sh_offset + shdr.sh_entsize * index; | 986 | const off = shdr.sh_offset + @as(u64, entry_size) * index; |
| 969 | switch (self.ptr_width) { | 987 | switch (self.ptr_width) { |
| 970 | .p32 => { | 988 | .p32 => { |
| 971 | var buf: [4]u8 = undefined; | 989 | var buf: [4]u8 = undefined; |
| ... | @@ -981,35 +999,42 @@ pub const ElfFile = struct { | ... | @@ -981,35 +999,42 @@ pub const ElfFile = struct { |
| 981 | } | 999 | } |
| 982 | | 1000 | |
| 983 | fn writeSymbol(self: *ElfFile, index: usize) !void { | 1001 | fn writeSymbol(self: *ElfFile, index: usize) !void { |
| 984 | assert(index != 0); | | |
| 985 | const syms_sect = &self.sections.items[self.symtab_section_index.?]; | 1002 | const syms_sect = &self.sections.items[self.symtab_section_index.?]; |
| 986 | // Make sure we are not pointlessly writing symbol data that will have to get relocated | 1003 | // Make sure we are not pointlessly writing symbol data that will have to get relocated |
| 987 | // due to running out of space. | 1004 | // due to running out of space. |
| 988 | if (self.symbol_count_dirty) { | 1005 | if (self.local_symbols.items.len != syms_sect.sh_info) { |
| 989 | const sym_size: u64 = switch (self.ptr_width) { | 1006 | const sym_size: u64 = switch (self.ptr_width) { |
| 990 | .p32 => @sizeOf(elf.Elf32_Sym), | 1007 | .p32 => @sizeOf(elf.Elf32_Sym), |
| 991 | .p64 => @sizeOf(elf.Elf64_Sym), | 1008 | .p64 => @sizeOf(elf.Elf64_Sym), |
| 992 | }; | 1009 | }; |
| 993 | const allocated_size = self.allocatedSize(syms_sect.sh_offset); | 1010 | const sym_align: u16 = switch (self.ptr_width) { |
| 994 | const needed_size = self.symbols.items.len * sym_size; | 1011 | .p32 => @alignOf(elf.Elf32_Sym), |
| 995 | if (needed_size > allocated_size) { | 1012 | .p64 => @alignOf(elf.Elf64_Sym), |
| 996 | return self.writeAllSymbols(); | 1013 | }; |
| | 1014 | const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size; |
| | 1015 | if (needed_size > self.allocatedSize(syms_sect.sh_offset)) { |
| | 1016 | // Move all the symbols to a new file location. |
| | 1017 | const new_offset = self.findFreeSpace(needed_size, sym_align); |
| | 1018 | const existing_size = @as(u64, syms_sect.sh_info) * sym_size; |
| | 1019 | const amt = try self.file.copyRangeAll(syms_sect.sh_offset, self.file, new_offset, existing_size); |
| | 1020 | if (amt != existing_size) return error.InputOutput; |
| | 1021 | syms_sect.sh_offset = new_offset; |
| 997 | } | 1022 | } |
| 998 | syms_sect.sh_info = @intCast(u32, self.symbols.items.len); | 1023 | syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len); |
| | 1024 | syms_sect.sh_size = needed_size; // anticipating adding the global symbols later |
| 999 | self.shdr_table_dirty = true; // TODO look into only writing one section | 1025 | self.shdr_table_dirty = true; // TODO look into only writing one section |
| 1000 | self.symbol_count_dirty = false; | | |
| 1001 | } | 1026 | } |
| 1002 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | 1027 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); |
| 1003 | switch (self.ptr_width) { | 1028 | switch (self.ptr_width) { |
| 1004 | .p32 => { | 1029 | .p32 => { |
| 1005 | var sym = [1]elf.Elf32_Sym{ | 1030 | var sym = [1]elf.Elf32_Sym{ |
| 1006 | .{ | 1031 | .{ |
| 1007 | .st_name = self.symbols.items[index].st_name, | 1032 | .st_name = self.local_symbols.items[index].st_name, |
| 1008 | .st_value = @intCast(u32, self.symbols.items[index].st_value), | 1033 | .st_value = @intCast(u32, self.local_symbols.items[index].st_value), |
| 1009 | .st_size = @intCast(u32, self.symbols.items[index].st_size), | 1034 | .st_size = @intCast(u32, self.local_symbols.items[index].st_size), |
| 1010 | .st_info = self.symbols.items[index].st_info, | 1035 | .st_info = self.local_symbols.items[index].st_info, |
| 1011 | .st_other = self.symbols.items[index].st_other, | 1036 | .st_other = self.local_symbols.items[index].st_other, |
| 1012 | .st_shndx = self.symbols.items[index].st_shndx, | 1037 | .st_shndx = self.local_symbols.items[index].st_shndx, |
| 1013 | }, | 1038 | }, |
| 1014 | }; | 1039 | }; |
| 1015 | if (foreign_endian) { | 1040 | if (foreign_endian) { |
| ... | @@ -1019,7 +1044,7 @@ pub const ElfFile = struct { | ... | @@ -1019,7 +1044,7 @@ pub const ElfFile = struct { |
| 1019 | try self.file.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); | 1044 | try self.file.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); |
| 1020 | }, | 1045 | }, |
| 1021 | .p64 => { | 1046 | .p64 => { |
| 1022 | var sym = [1]elf.Elf64_Sym{self.symbols.items[index]}; | 1047 | var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]}; |
| 1023 | if (foreign_endian) { | 1048 | if (foreign_endian) { |
| 1024 | bswapAllFields(elf.Elf64_Sym, &sym[0]); | 1049 | bswapAllFields(elf.Elf64_Sym, &sym[0]); |
| 1025 | } | 1050 | } |
| ... | @@ -1029,67 +1054,53 @@ pub const ElfFile = struct { | ... | @@ -1029,67 +1054,53 @@ pub const ElfFile = struct { |
| 1029 | } | 1054 | } |
| 1030 | } | 1055 | } |
| 1031 | | 1056 | |
| 1032 | fn writeAllSymbols(self: *ElfFile) !void { | 1057 | fn writeAllGlobalSymbols(self: *ElfFile) !void { |
| 1033 | const syms_sect = &self.sections.items[self.symtab_section_index.?]; | 1058 | const syms_sect = &self.sections.items[self.symtab_section_index.?]; |
| 1034 | const sym_align: u16 = switch (self.ptr_width) { | | |
| 1035 | .p32 => @alignOf(elf.Elf32_Sym), | | |
| 1036 | .p64 => @alignOf(elf.Elf64_Sym), | | |
| 1037 | }; | | |
| 1038 | const sym_size: u64 = switch (self.ptr_width) { | 1059 | const sym_size: u64 = switch (self.ptr_width) { |
| 1039 | .p32 => @sizeOf(elf.Elf32_Sym), | 1060 | .p32 => @sizeOf(elf.Elf32_Sym), |
| 1040 | .p64 => @sizeOf(elf.Elf64_Sym), | 1061 | .p64 => @sizeOf(elf.Elf64_Sym), |
| 1041 | }; | 1062 | }; |
| 1042 | const allocated_size = self.allocatedSize(syms_sect.sh_offset); | | |
| 1043 | const needed_size = self.symbols.items.len * sym_size; | | |
| 1044 | if (needed_size > allocated_size) { | | |
| 1045 | syms_sect.sh_size = 0; // free the space | | |
| 1046 | syms_sect.sh_offset = self.findFreeSpace(needed_size, sym_align); | | |
| 1047 | //std.debug.warn("moved symtab to 0x{x} to 0x{x}\n", .{ syms_sect.sh_offset, syms_sect.sh_offset + needed_size }); | | |
| 1048 | } | | |
| 1049 | //std.debug.warn("symtab start=0x{x} end=0x{x}\n", .{ syms_sect.sh_offset, syms_sect.sh_offset + needed_size }); | 1063 | //std.debug.warn("symtab start=0x{x} end=0x{x}\n", .{ syms_sect.sh_offset, syms_sect.sh_offset + needed_size }); |
| 1050 | syms_sect.sh_size = needed_size; | | |
| 1051 | syms_sect.sh_info = @intCast(u32, self.symbols.items.len); | | |
| 1052 | self.symbol_count_dirty = false; | | |
| 1053 | self.shdr_table_dirty = true; // TODO look into only writing one section | | |
| 1054 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | 1064 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); |
| | 1065 | const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size; |
| 1055 | switch (self.ptr_width) { | 1066 | switch (self.ptr_width) { |
| 1056 | .p32 => { | 1067 | .p32 => { |
| 1057 | const buf = try self.allocator.alloc(elf.Elf32_Sym, self.symbols.items.len); | 1068 | const buf = try self.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len); |
| 1058 | defer self.allocator.free(buf); | 1069 | defer self.allocator.free(buf); |
| 1059 | | 1070 | |
| 1060 | for (buf) |*sym, i| { | 1071 | for (buf) |*sym, i| { |
| 1061 | sym.* = .{ | 1072 | sym.* = .{ |
| 1062 | .st_name = self.symbols.items[i].st_name, | 1073 | .st_name = self.global_symbols.items[i].st_name, |
| 1063 | .st_value = @intCast(u32, self.symbols.items[i].st_value), | 1074 | .st_value = @intCast(u32, self.global_symbols.items[i].st_value), |
| 1064 | .st_size = @intCast(u32, self.symbols.items[i].st_size), | 1075 | .st_size = @intCast(u32, self.global_symbols.items[i].st_size), |
| 1065 | .st_info = self.symbols.items[i].st_info, | 1076 | .st_info = self.global_symbols.items[i].st_info, |
| 1066 | .st_other = self.symbols.items[i].st_other, | 1077 | .st_other = self.global_symbols.items[i].st_other, |
| 1067 | .st_shndx = self.symbols.items[i].st_shndx, | 1078 | .st_shndx = self.global_symbols.items[i].st_shndx, |
| 1068 | }; | 1079 | }; |
| 1069 | if (foreign_endian) { | 1080 | if (foreign_endian) { |
| 1070 | bswapAllFields(elf.Elf32_Sym, sym); | 1081 | bswapAllFields(elf.Elf32_Sym, sym); |
| 1071 | } | 1082 | } |
| 1072 | } | 1083 | } |
| 1073 | try self.file.pwriteAll(mem.sliceAsBytes(buf), syms_sect.sh_offset); | 1084 | try self.file.pwriteAll(mem.sliceAsBytes(buf), global_syms_off); |
| 1074 | }, | 1085 | }, |
| 1075 | .p64 => { | 1086 | .p64 => { |
| 1076 | const buf = try self.allocator.alloc(elf.Elf64_Sym, self.symbols.items.len); | 1087 | const buf = try self.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len); |
| 1077 | defer self.allocator.free(buf); | 1088 | defer self.allocator.free(buf); |
| 1078 | | 1089 | |
| 1079 | for (buf) |*sym, i| { | 1090 | for (buf) |*sym, i| { |
| 1080 | sym.* = .{ | 1091 | sym.* = .{ |
| 1081 | .st_name = self.symbols.items[i].st_name, | 1092 | .st_name = self.global_symbols.items[i].st_name, |
| 1082 | .st_value = self.symbols.items[i].st_value, | 1093 | .st_value = self.global_symbols.items[i].st_value, |
| 1083 | .st_size = self.symbols.items[i].st_size, | 1094 | .st_size = self.global_symbols.items[i].st_size, |
| 1084 | .st_info = self.symbols.items[i].st_info, | 1095 | .st_info = self.global_symbols.items[i].st_info, |
| 1085 | .st_other = self.symbols.items[i].st_other, | 1096 | .st_other = self.global_symbols.items[i].st_other, |
| 1086 | .st_shndx = self.symbols.items[i].st_shndx, | 1097 | .st_shndx = self.global_symbols.items[i].st_shndx, |
| 1087 | }; | 1098 | }; |
| 1088 | if (foreign_endian) { | 1099 | if (foreign_endian) { |
| 1089 | bswapAllFields(elf.Elf64_Sym, sym); | 1100 | bswapAllFields(elf.Elf64_Sym, sym); |
| 1090 | } | 1101 | } |
| 1091 | } | 1102 | } |
| 1092 | try self.file.pwriteAll(mem.sliceAsBytes(buf), syms_sect.sh_offset); | 1103 | try self.file.pwriteAll(mem.sliceAsBytes(buf), global_syms_off); |
| 1093 | }, | 1104 | }, |
| 1094 | } | 1105 | } |
| 1095 | } | 1106 | } |
| ... | @@ -1126,7 +1137,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El | ... | @@ -1126,7 +1137,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El |
| 1126 | errdefer self.deinit(); | 1137 | errdefer self.deinit(); |
| 1127 | | 1138 | |
| 1128 | // Index 0 is always a null symbol. | 1139 | // Index 0 is always a null symbol. |
| 1129 | try self.symbols.append(allocator, .{ | 1140 | try self.local_symbols.append(allocator, .{ |
| 1130 | .st_name = 0, | 1141 | .st_name = 0, |
| 1131 | .st_info = 0, | 1142 | .st_info = 0, |
| 1132 | .st_other = 0, | 1143 | .st_other = 0, |