| ... | ... | @@ -37,13 +37,14 @@ strtab_offset: ?u32 = null, |
| 37 | 37 | |
| 38 | 38 | temp_strtab: StringTable(.temp_strtab) = .{}, |
| 39 | 39 | |
| 40 | | got_entries: std.ArrayListUnmanaged(Entry) = .{}, |
| 41 | | got_entries_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 42 | | got_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{}, |
| 40 | got_table: TableSection(SymbolWithLoc) = .{}, |
| 43 | 41 | |
| 44 | 42 | /// A table of ImportTables partitioned by the library name. |
| 45 | 43 | /// Key is an offset into the interning string table `temp_strtab`. |
| 46 | 44 | import_tables: std.AutoArrayHashMapUnmanaged(u32, ImportTable) = .{}, |
| 45 | |
| 46 | got_table_count_dirty: bool = true, |
| 47 | got_table_contents_dirty: bool = true, |
| 47 | 48 | imports_count_dirty: bool = true, |
| 48 | 49 | |
| 49 | 50 | /// Virtual address of the entry point procedure relative to image base. |
| ... | ... | @@ -188,7 +189,8 @@ pub const PtrWidth = enum { |
| 188 | 189 | p32, |
| 189 | 190 | p64, |
| 190 | 191 | |
| 191 | | fn abiSize(pw: PtrWidth) u4 { |
| 192 | /// Size in bytes. |
| 193 | pub fn size(pw: PtrWidth) u4 { |
| 192 | 194 | return switch (pw) { |
| 193 | 195 | .p32 => 4, |
| 194 | 196 | .p64 => 8, |
| ... | ... | @@ -310,9 +312,7 @@ pub fn deinit(self: *Coff) void { |
| 310 | 312 | self.globals_free_list.deinit(gpa); |
| 311 | 313 | self.strtab.deinit(gpa); |
| 312 | 314 | self.temp_strtab.deinit(gpa); |
| 313 | | self.got_entries.deinit(gpa); |
| 314 | | self.got_entries_free_list.deinit(gpa); |
| 315 | | self.got_entries_table.deinit(gpa); |
| 315 | self.got_table.deinit(gpa); |
| 316 | 316 | |
| 317 | 317 | for (self.import_tables.values()) |*itab| { |
| 318 | 318 | itab.deinit(gpa); |
| ... | ... | @@ -371,7 +371,7 @@ fn populateMissingMetadata(self: *Coff) !void { |
| 371 | 371 | } |
| 372 | 372 | |
| 373 | 373 | if (self.got_section_index == null) { |
| 374 | | const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize(); |
| 374 | const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.size(); |
| 375 | 375 | self.got_section_index = try self.allocateSection(".got", file_size, .{ |
| 376 | 376 | .CNT_INITIALIZED_DATA = 1, |
| 377 | 377 | .MEM_READ = 1, |
| ... | ... | @@ -396,7 +396,7 @@ fn populateMissingMetadata(self: *Coff) !void { |
| 396 | 396 | } |
| 397 | 397 | |
| 398 | 398 | if (self.idata_section_index == null) { |
| 399 | | const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize(); |
| 399 | const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.size(); |
| 400 | 400 | self.idata_section_index = try self.allocateSection(".idata", file_size, .{ |
| 401 | 401 | .CNT_INITIALIZED_DATA = 1, |
| 402 | 402 | .MEM_READ = 1, |
| ... | ... | @@ -698,26 +698,11 @@ fn allocateGlobal(self: *Coff) !u32 { |
| 698 | 698 | return index; |
| 699 | 699 | } |
| 700 | 700 | |
| 701 | | pub fn allocateGotEntry(self: *Coff, target: SymbolWithLoc) !u32 { |
| 702 | | const gpa = self.base.allocator; |
| 703 | | try self.got_entries.ensureUnusedCapacity(gpa, 1); |
| 704 | | |
| 705 | | const index: u32 = blk: { |
| 706 | | if (self.got_entries_free_list.popOrNull()) |index| { |
| 707 | | log.debug(" (reusing GOT entry index {d})", .{index}); |
| 708 | | break :blk index; |
| 709 | | } else { |
| 710 | | log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.items.len}); |
| 711 | | const index = @intCast(u32, self.got_entries.items.len); |
| 712 | | _ = self.got_entries.addOneAssumeCapacity(); |
| 713 | | break :blk index; |
| 714 | | } |
| 715 | | }; |
| 716 | | |
| 717 | | self.got_entries.items[index] = .{ .target = target, .sym_index = 0 }; |
| 718 | | try self.got_entries_table.putNoClobber(gpa, target, index); |
| 719 | | |
| 720 | | return index; |
| 701 | fn addGotEntry(self: *Coff, target: SymbolWithLoc) !void { |
| 702 | if (self.got_table.lookup.contains(target)) return; |
| 703 | const got_index = try self.got_table.allocateEntry(self.base.allocator, target); |
| 704 | try self.writeOffsetTableEntry(got_index); |
| 705 | self.got_table_count_dirty = true; |
| 721 | 706 | } |
| 722 | 707 | |
| 723 | 708 | pub fn createAtom(self: *Coff) !Atom.Index { |
| ... | ... | @@ -737,37 +722,6 @@ pub fn createAtom(self: *Coff) !Atom.Index { |
| 737 | 722 | return atom_index; |
| 738 | 723 | } |
| 739 | 724 | |
| 740 | | fn createGotAtom(self: *Coff, target: SymbolWithLoc) !Atom.Index { |
| 741 | | const atom_index = try self.createAtom(); |
| 742 | | const atom = self.getAtomPtr(atom_index); |
| 743 | | atom.size = @sizeOf(u64); |
| 744 | | |
| 745 | | const sym = atom.getSymbolPtr(self); |
| 746 | | sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1); |
| 747 | | sym.value = try self.allocateAtom(atom_index, atom.size, @sizeOf(u64)); |
| 748 | | |
| 749 | | log.debug("allocated GOT atom at 0x{x}", .{sym.value}); |
| 750 | | |
| 751 | | try Atom.addRelocation(self, atom_index, .{ |
| 752 | | .type = .direct, |
| 753 | | .target = target, |
| 754 | | .offset = 0, |
| 755 | | .addend = 0, |
| 756 | | .pcrel = false, |
| 757 | | .length = 3, |
| 758 | | }); |
| 759 | | |
| 760 | | const target_sym = self.getSymbol(target); |
| 761 | | switch (target_sym.section_number) { |
| 762 | | .UNDEFINED => @panic("TODO generate a binding for undefined GOT target"), |
| 763 | | .ABSOLUTE => {}, |
| 764 | | .DEBUG => unreachable, // not possible |
| 765 | | else => try Atom.addBaseRelocation(self, atom_index, 0), |
| 766 | | } |
| 767 | | |
| 768 | | return atom_index; |
| 769 | | } |
| 770 | | |
| 771 | 725 | fn growAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignment: u32) !u32 { |
| 772 | 726 | const atom = self.getAtom(atom_index); |
| 773 | 727 | const sym = atom.getSymbol(self); |
| ... | ... | @@ -873,17 +827,73 @@ fn writeMem(handle: std.ChildProcess.Id, pvaddr: std.os.windows.LPVOID, code: [] |
| 873 | 827 | if (amt != code.len) return error.InputOutput; |
| 874 | 828 | } |
| 875 | 829 | |
| 876 | | fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void { |
| 830 | fn writeOffsetTableEntry(self: *Coff, index: usize) !void { |
| 831 | const sect_id = self.got_section_index.?; |
| 832 | |
| 833 | if (self.got_table_count_dirty) { |
| 834 | const needed_size = @intCast(u32, self.got_table.entries.items.len * self.ptr_width.size()); |
| 835 | try self.growSection(sect_id, needed_size); |
| 836 | self.got_table_count_dirty = false; |
| 837 | } |
| 838 | |
| 839 | const header = &self.sections.items(.header)[sect_id]; |
| 840 | const entry = self.got_table.entries.items[index]; |
| 841 | const entry_value = self.getSymbol(entry).value; |
| 842 | const entry_offset = index * self.ptr_width.size(); |
| 843 | const file_offset = header.pointer_to_raw_data + entry_offset; |
| 844 | const vmaddr = header.virtual_address + entry_offset; |
| 845 | |
| 846 | log.debug("writing GOT entry {d}: @{x} => {x}", .{ index, vmaddr, entry_value }); |
| 847 | |
| 877 | 848 | switch (self.ptr_width) { |
| 878 | 849 | .p32 => { |
| 879 | | var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32); |
| 880 | | try self.writeAtom(atom_index, &buffer); |
| 850 | var buf: [4]u8 = undefined; |
| 851 | mem.writeIntLittle(u32, &buf, @intCast(u32, entry_value)); |
| 852 | try self.base.file.?.pwriteAll(&buf, file_offset); |
| 881 | 853 | }, |
| 882 | 854 | .p64 => { |
| 883 | | var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64); |
| 884 | | try self.writeAtom(atom_index, &buffer); |
| 855 | var buf: [8]u8 = undefined; |
| 856 | mem.writeIntLittle(u64, &buf, entry_value); |
| 857 | try self.base.file.?.pwriteAll(&buf, file_offset); |
| 885 | 858 | }, |
| 886 | 859 | } |
| 860 | |
| 861 | if (is_hot_update_compatible) { |
| 862 | if (self.base.child_pid) |handle| { |
| 863 | const gpa = self.base.allocator; |
| 864 | const slide = @ptrToInt(self.hot_state.loaded_base_address.?); |
| 865 | const actual_vmaddr = vmaddr + slide; |
| 866 | const pvaddr = @intToPtr(*anyopaque, actual_vmaddr); |
| 867 | log.debug("writing GOT entry to memory at address {x}", .{actual_vmaddr}); |
| 868 | if (build_options.enable_logging) { |
| 869 | switch (self.ptr_width) { |
| 870 | .p32 => { |
| 871 | var buf: [4]u8 = undefined; |
| 872 | try debugMem(gpa, handle, pvaddr, &buf); |
| 873 | }, |
| 874 | .p64 => { |
| 875 | var buf: [8]u8 = undefined; |
| 876 | try debugMem(gpa, handle, pvaddr, &buf); |
| 877 | }, |
| 878 | } |
| 879 | } |
| 880 | |
| 881 | switch (self.ptr_width) { |
| 882 | .p32 => { |
| 883 | var buf: [4]u8 = undefined; |
| 884 | writeMem(handle, pvaddr, &buf) catch |err| { |
| 885 | log.warn("writing to protected memory failed with error: {s}", .{@errorName(err)}); |
| 886 | }; |
| 887 | }, |
| 888 | .p64 => { |
| 889 | var buf: [8]u8 = undefined; |
| 890 | writeMem(handle, pvaddr, &buf) catch |err| { |
| 891 | log.warn("writing to protected memory failed with error: {s}", .{@errorName(err)}); |
| 892 | }; |
| 893 | }, |
| 894 | } |
| 895 | } |
| 896 | } |
| 887 | 897 | } |
| 888 | 898 | |
| 889 | 899 | fn markRelocsDirtyByTarget(self: *Coff, target: SymbolWithLoc) void { |
| ... | ... | @@ -904,6 +914,15 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void { |
| 904 | 914 | reloc.dirty = true; |
| 905 | 915 | } |
| 906 | 916 | } |
| 917 | |
| 918 | // TODO: dirty only really affected GOT cells |
| 919 | for (self.got_table.entries.items) |entry| { |
| 920 | const target_addr = self.getSymbol(entry).value; |
| 921 | if (target_addr >= addr) { |
| 922 | self.got_table_contents_dirty = true; |
| 923 | break; |
| 924 | } |
| 925 | } |
| 907 | 926 | } |
| 908 | 927 | |
| 909 | 928 | fn resolveRelocs(self: *Coff, atom_index: Atom.Index, relocs: []*const Relocation, code: []u8, image_base: u64) void { |
| ... | ... | @@ -994,17 +1013,7 @@ fn freeAtom(self: *Coff, atom_index: Atom.Index) void { |
| 994 | 1013 | self.locals_free_list.append(gpa, sym_index) catch {}; |
| 995 | 1014 | |
| 996 | 1015 | // Try freeing GOT atom if this decl had one |
| 997 | | const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null }; |
| 998 | | if (self.got_entries_table.get(got_target)) |got_index| { |
| 999 | | self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {}; |
| 1000 | | self.got_entries.items[got_index] = .{ |
| 1001 | | .target = .{ .sym_index = 0, .file = null }, |
| 1002 | | .sym_index = 0, |
| 1003 | | }; |
| 1004 | | _ = self.got_entries_table.remove(got_target); |
| 1005 | | |
| 1006 | | log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index }); |
| 1007 | | } |
| 1016 | self.got_table.freeEntry(gpa, .{ .sym_index = sym_index }); |
| 1008 | 1017 | |
| 1009 | 1018 | self.locals.items[sym_index].section_number = .UNDEFINED; |
| 1010 | 1019 | _ = self.atom_by_index_table.remove(sym_index); |
| ... | ... | @@ -1243,14 +1252,7 @@ fn updateLazySymbolAtom( |
| 1243 | 1252 | atom.size = code_len; |
| 1244 | 1253 | symbol.value = vaddr; |
| 1245 | 1254 | |
| 1246 | | const got_target = SymbolWithLoc{ .sym_index = local_sym_index, .file = null }; |
| 1247 | | const got_index = try self.allocateGotEntry(got_target); |
| 1248 | | const got_atom_index = try self.createGotAtom(got_target); |
| 1249 | | const got_atom = self.getAtom(got_atom_index); |
| 1250 | | self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?; |
| 1251 | | try self.writePtrWidthAtom(got_atom_index); |
| 1252 | | |
| 1253 | | self.markRelocsDirtyByTarget(atom.getSymbolWithLoc()); |
| 1255 | try self.addGotEntry(.{ .sym_index = local_sym_index }); |
| 1254 | 1256 | try self.writeAtom(atom_index, code); |
| 1255 | 1257 | } |
| 1256 | 1258 | |
| ... | ... | @@ -1321,6 +1323,7 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple |
| 1321 | 1323 | const decl_metadata = self.decls.get(decl_index).?; |
| 1322 | 1324 | const atom_index = decl_metadata.atom; |
| 1323 | 1325 | const atom = self.getAtom(atom_index); |
| 1326 | const sym_index = atom.getSymbolIndex().?; |
| 1324 | 1327 | const sect_index = decl_metadata.section; |
| 1325 | 1328 | const code_len = @intCast(u32, code.len); |
| 1326 | 1329 | |
| ... | ... | @@ -1340,10 +1343,8 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple |
| 1340 | 1343 | if (vaddr != sym.value) { |
| 1341 | 1344 | sym.value = vaddr; |
| 1342 | 1345 | log.debug(" (updating GOT entry)", .{}); |
| 1343 | | const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null }; |
| 1344 | | const got_atom_index = self.getGotAtomIndexForSymbol(got_target).?; |
| 1345 | | self.markRelocsDirtyByTarget(got_target); |
| 1346 | | try self.writePtrWidthAtom(got_atom_index); |
| 1346 | const got_entry_index = self.got_table.lookup.get(.{ .sym_index = sym_index }).?; |
| 1347 | try self.writeOffsetTableEntry(got_entry_index); |
| 1347 | 1348 | } |
| 1348 | 1349 | } else if (code_len < atom.size) { |
| 1349 | 1350 | self.shrinkAtom(atom_index, code_len); |
| ... | ... | @@ -1361,15 +1362,9 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple |
| 1361 | 1362 | self.getAtomPtr(atom_index).size = code_len; |
| 1362 | 1363 | sym.value = vaddr; |
| 1363 | 1364 | |
| 1364 | | const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null }; |
| 1365 | | const got_index = try self.allocateGotEntry(got_target); |
| 1366 | | const got_atom_index = try self.createGotAtom(got_target); |
| 1367 | | const got_atom = self.getAtom(got_atom_index); |
| 1368 | | self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?; |
| 1369 | | try self.writePtrWidthAtom(got_atom_index); |
| 1365 | try self.addGotEntry(.{ .sym_index = sym_index }); |
| 1370 | 1366 | } |
| 1371 | 1367 | |
| 1372 | | self.markRelocsDirtyByTarget(atom.getSymbolWithLoc()); |
| 1373 | 1368 | try self.writeAtom(atom_index, code); |
| 1374 | 1369 | } |
| 1375 | 1370 | |
| ... | ... | @@ -1651,6 +1646,16 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod |
| 1651 | 1646 | try self.writeAtom(atom_index, code.items); |
| 1652 | 1647 | } |
| 1653 | 1648 | |
| 1649 | // Update GOT if it got moved in memory. |
| 1650 | if (self.got_table_contents_dirty) { |
| 1651 | for (self.got_table.entries.items, 0..) |entry, i| { |
| 1652 | if (!self.got_table.lookup.contains(entry)) continue; |
| 1653 | // TODO: write all in one go rather than incrementally. |
| 1654 | try self.writeOffsetTableEntry(i); |
| 1655 | } |
| 1656 | self.got_table_contents_dirty = false; |
| 1657 | } |
| 1658 | |
| 1654 | 1659 | try self.writeBaseRelocations(); |
| 1655 | 1660 | |
| 1656 | 1661 | if (self.getEntryPoint()) |entry_sym_loc| { |
| ... | ... | @@ -1752,10 +1757,10 @@ fn writeBaseRelocations(self: *Coff) !void { |
| 1752 | 1757 | while (it.next()) |entry| { |
| 1753 | 1758 | const atom_index = entry.key_ptr.*; |
| 1754 | 1759 | const atom = self.getAtom(atom_index); |
| 1760 | const sym = atom.getSymbol(self); |
| 1755 | 1761 | const offsets = entry.value_ptr.*; |
| 1756 | 1762 | |
| 1757 | 1763 | for (offsets.items) |offset| { |
| 1758 | | const sym = atom.getSymbol(self); |
| 1759 | 1764 | const rva = sym.value + offset; |
| 1760 | 1765 | const page = mem.alignBackwardGeneric(u32, rva, self.page_size); |
| 1761 | 1766 | const gop = try pages.getOrPut(page); |
| ... | ... | @@ -1769,6 +1774,27 @@ fn writeBaseRelocations(self: *Coff) !void { |
| 1769 | 1774 | } |
| 1770 | 1775 | } |
| 1771 | 1776 | |
| 1777 | { |
| 1778 | const header = &self.sections.items(.header)[self.got_section_index.?]; |
| 1779 | for (self.got_table.entries.items, 0..) |entry, index| { |
| 1780 | if (!self.got_table.lookup.contains(entry)) continue; |
| 1781 | |
| 1782 | const sym = self.getSymbol(entry); |
| 1783 | if (sym.section_number == .UNDEFINED) continue; |
| 1784 | |
| 1785 | const rva = @intCast(u32, header.virtual_address + index * self.ptr_width.size()); |
| 1786 | const page = mem.alignBackwardGeneric(u32, rva, self.page_size); |
| 1787 | const gop = try pages.getOrPut(page); |
| 1788 | if (!gop.found_existing) { |
| 1789 | gop.value_ptr.* = std.ArrayList(coff.BaseRelocation).init(gpa); |
| 1790 | } |
| 1791 | try gop.value_ptr.append(.{ |
| 1792 | .offset = @intCast(u12, rva - page), |
| 1793 | .type = .DIR64, |
| 1794 | }); |
| 1795 | } |
| 1796 | } |
| 1797 | |
| 1772 | 1798 | var buffer = std.ArrayList(u8).init(gpa); |
| 1773 | 1799 | defer buffer.deinit(); |
| 1774 | 1800 | |
| ... | ... | @@ -2315,14 +2341,6 @@ pub fn getAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.In |
| 2315 | 2341 | return self.atom_by_index_table.get(sym_loc.sym_index); |
| 2316 | 2342 | } |
| 2317 | 2343 | |
| 2318 | | /// Returns GOT atom that references `sym_loc` if one exists. |
| 2319 | | /// Returns null otherwise. |
| 2320 | | pub fn getGotAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index { |
| 2321 | | const got_index = self.got_entries_table.get(sym_loc) orelse return null; |
| 2322 | | const got_entry = self.got_entries.items[got_index]; |
| 2323 | | return self.getAtomIndexForSymbol(.{ .sym_index = got_entry.sym_index, .file = null }); |
| 2324 | | } |
| 2325 | | |
| 2326 | 2344 | fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void { |
| 2327 | 2345 | if (name.len <= 8) { |
| 2328 | 2346 | mem.copy(u8, &header.name, name); |
| ... | ... | @@ -2410,25 +2428,7 @@ fn logSymtab(self: *Coff) void { |
| 2410 | 2428 | } |
| 2411 | 2429 | |
| 2412 | 2430 | log.debug("GOT entries:", .{}); |
| 2413 | | for (self.got_entries.items, 0..) |entry, i| { |
| 2414 | | const got_sym = self.getSymbol(.{ .sym_index = entry.sym_index, .file = null }); |
| 2415 | | const target_sym = self.getSymbol(entry.target); |
| 2416 | | if (target_sym.section_number == .UNDEFINED) { |
| 2417 | | log.debug(" {d}@{x} => import('{s}')", .{ |
| 2418 | | i, |
| 2419 | | got_sym.value, |
| 2420 | | self.getSymbolName(entry.target), |
| 2421 | | }); |
| 2422 | | } else { |
| 2423 | | log.debug(" {d}@{x} => local(%{d}) in object({?d}) {s}", .{ |
| 2424 | | i, |
| 2425 | | got_sym.value, |
| 2426 | | entry.target.sym_index, |
| 2427 | | entry.target.file, |
| 2428 | | logSymAttributes(target_sym, &buf), |
| 2429 | | }); |
| 2430 | | } |
| 2431 | | } |
| 2431 | log.debug("{}", .{self.got_table}); |
| 2432 | 2432 | } |
| 2433 | 2433 | |
| 2434 | 2434 | fn logSections(self: *Coff) void { |
| ... | ... | @@ -2484,6 +2484,7 @@ const LlvmObject = @import("../codegen/llvm.zig").Object; |
| 2484 | 2484 | const Module = @import("../Module.zig"); |
| 2485 | 2485 | const Object = @import("Coff/Object.zig"); |
| 2486 | 2486 | const Relocation = @import("Coff/Relocation.zig"); |
| 2487 | const TableSection = @import("table_section.zig").TableSection; |
| 2487 | 2488 | const StringTable = @import("strtab.zig").StringTable; |
| 2488 | 2489 | const Type = @import("../type.zig").Type; |
| 2489 | 2490 | const TypedValue = @import("../TypedValue.zig"); |