authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-04-20 11:18:27+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-04-21 22:44:28+02:00
logae8fb2151425dc7f61ad46e4c13b6c61f67cc68b
tree0ceac67dddaddc4ace94b850fa0c59cc4dbc655a
parentd6e66cff12d9f446c592e90db31642d503e29ade

coff: use TableSection for GOT


2 files changed, 131 insertions(+), 131 deletions(-)

src/link/Coff.zig+124-123
......@@ -37,13 +37,14 @@ strtab_offset: ?u32 = null,
3737
3838temp_strtab: StringTable(.temp_strtab) = .{},
3939
40got_entries: std.ArrayListUnmanaged(Entry) = .{},
41got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
42got_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
40got_table: TableSection(SymbolWithLoc) = .{},
4341
4442/// A table of ImportTables partitioned by the library name.
4543/// Key is an offset into the interning string table `temp_strtab`.
4644import_tables: std.AutoArrayHashMapUnmanaged(u32, ImportTable) = .{},
45
46got_table_count_dirty: bool = true,
47got_table_contents_dirty: bool = true,
4748imports_count_dirty: bool = true,
4849
4950/// Virtual address of the entry point procedure relative to image base.
......@@ -188,7 +189,8 @@ pub const PtrWidth = enum {
188189 p32,
189190 p64,
190191
191 fn abiSize(pw: PtrWidth) u4 {
192 /// Size in bytes.
193 pub fn size(pw: PtrWidth) u4 {
192194 return switch (pw) {
193195 .p32 => 4,
194196 .p64 => 8,
......@@ -310,9 +312,7 @@ pub fn deinit(self: *Coff) void {
310312 self.globals_free_list.deinit(gpa);
311313 self.strtab.deinit(gpa);
312314 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);
316316
317317 for (self.import_tables.values()) |*itab| {
318318 itab.deinit(gpa);
......@@ -371,7 +371,7 @@ fn populateMissingMetadata(self: *Coff) !void {
371371 }
372372
373373 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();
375375 self.got_section_index = try self.allocateSection(".got", file_size, .{
376376 .CNT_INITIALIZED_DATA = 1,
377377 .MEM_READ = 1,
......@@ -396,7 +396,7 @@ fn populateMissingMetadata(self: *Coff) !void {
396396 }
397397
398398 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();
400400 self.idata_section_index = try self.allocateSection(".idata", file_size, .{
401401 .CNT_INITIALIZED_DATA = 1,
402402 .MEM_READ = 1,
......@@ -698,26 +698,11 @@ fn allocateGlobal(self: *Coff) !u32 {
698698 return index;
699699}
700700
701pub 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;
701fn 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;
721706}
722707
723708pub fn createAtom(self: *Coff) !Atom.Index {
......@@ -737,37 +722,6 @@ pub fn createAtom(self: *Coff) !Atom.Index {
737722 return atom_index;
738723}
739724
740fn 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
771725fn growAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignment: u32) !u32 {
772726 const atom = self.getAtom(atom_index);
773727 const sym = atom.getSymbol(self);
......@@ -873,17 +827,73 @@ fn writeMem(handle: std.ChildProcess.Id, pvaddr: std.os.windows.LPVOID, code: []
873827 if (amt != code.len) return error.InputOutput;
874828}
875829
876fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void {
830fn 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
877848 switch (self.ptr_width) {
878849 .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);
881853 },
882854 .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);
885858 },
886859 }
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 }
887897}
888898
889899fn markRelocsDirtyByTarget(self: *Coff, target: SymbolWithLoc) void {
......@@ -904,6 +914,15 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {
904914 reloc.dirty = true;
905915 }
906916 }
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 }
907926}
908927
909928fn 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 {
9941013 self.locals_free_list.append(gpa, sym_index) catch {};
9951014
9961015 // 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 });
10081017
10091018 self.locals.items[sym_index].section_number = .UNDEFINED;
10101019 _ = self.atom_by_index_table.remove(sym_index);
......@@ -1243,14 +1252,7 @@ fn updateLazySymbolAtom(
12431252 atom.size = code_len;
12441253 symbol.value = vaddr;
12451254
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 });
12541256 try self.writeAtom(atom_index, code);
12551257}
12561258
......@@ -1321,6 +1323,7 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple
13211323 const decl_metadata = self.decls.get(decl_index).?;
13221324 const atom_index = decl_metadata.atom;
13231325 const atom = self.getAtom(atom_index);
1326 const sym_index = atom.getSymbolIndex().?;
13241327 const sect_index = decl_metadata.section;
13251328 const code_len = @intCast(u32, code.len);
13261329
......@@ -1340,10 +1343,8 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple
13401343 if (vaddr != sym.value) {
13411344 sym.value = vaddr;
13421345 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);
13471348 }
13481349 } else if (code_len < atom.size) {
13491350 self.shrinkAtom(atom_index, code_len);
......@@ -1361,15 +1362,9 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple
13611362 self.getAtomPtr(atom_index).size = code_len;
13621363 sym.value = vaddr;
13631364
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 });
13701366 }
13711367
1372 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());
13731368 try self.writeAtom(atom_index, code);
13741369}
13751370
......@@ -1651,6 +1646,16 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
16511646 try self.writeAtom(atom_index, code.items);
16521647 }
16531648
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
16541659 try self.writeBaseRelocations();
16551660
16561661 if (self.getEntryPoint()) |entry_sym_loc| {
......@@ -1752,10 +1757,10 @@ fn writeBaseRelocations(self: *Coff) !void {
17521757 while (it.next()) |entry| {
17531758 const atom_index = entry.key_ptr.*;
17541759 const atom = self.getAtom(atom_index);
1760 const sym = atom.getSymbol(self);
17551761 const offsets = entry.value_ptr.*;
17561762
17571763 for (offsets.items) |offset| {
1758 const sym = atom.getSymbol(self);
17591764 const rva = sym.value + offset;
17601765 const page = mem.alignBackwardGeneric(u32, rva, self.page_size);
17611766 const gop = try pages.getOrPut(page);
......@@ -1769,6 +1774,27 @@ fn writeBaseRelocations(self: *Coff) !void {
17691774 }
17701775 }
17711776
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
17721798 var buffer = std.ArrayList(u8).init(gpa);
17731799 defer buffer.deinit();
17741800
......@@ -2315,14 +2341,6 @@ pub fn getAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.In
23152341 return self.atom_by_index_table.get(sym_loc.sym_index);
23162342}
23172343
2318/// Returns GOT atom that references `sym_loc` if one exists.
2319/// Returns null otherwise.
2320pub 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
23262344fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void {
23272345 if (name.len <= 8) {
23282346 mem.copy(u8, &header.name, name);
......@@ -2410,25 +2428,7 @@ fn logSymtab(self: *Coff) void {
24102428 }
24112429
24122430 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});
24322432}
24332433
24342434fn logSections(self: *Coff) void {
......@@ -2484,6 +2484,7 @@ const LlvmObject = @import("../codegen/llvm.zig").Object;
24842484const Module = @import("../Module.zig");
24852485const Object = @import("Coff/Object.zig");
24862486const Relocation = @import("Coff/Relocation.zig");
2487const TableSection = @import("table_section.zig").TableSection;
24872488const StringTable = @import("strtab.zig").StringTable;
24882489const Type = @import("../type.zig").Type;
24892490const TypedValue = @import("../TypedValue.zig");
src/link/Coff/Relocation.zig+7-8
......@@ -48,17 +48,16 @@ dirty: bool = true,
4848/// Returns address of the target if any.
4949pub fn getTargetAddress(self: Relocation, coff_file: *const Coff) ?u32 {
5050 switch (self.type) {
51 .got, .got_page, .got_pageoff, .direct, .page, .pageoff => {
52 const maybe_target_atom_index = switch (self.type) {
53 .got, .got_page, .got_pageoff => coff_file.getGotAtomIndexForSymbol(self.target),
54 .direct, .page, .pageoff => coff_file.getAtomIndexForSymbol(self.target),
55 else => unreachable,
56 };
57 const target_atom_index = maybe_target_atom_index orelse return null;
51 .got, .got_page, .got_pageoff => {
52 const got_index = coff_file.got_table.lookup.get(self.target) orelse return null;
53 const header = coff_file.sections.items(.header)[coff_file.got_section_index.?];
54 return header.virtual_address + got_index * coff_file.ptr_width.size();
55 },
56 .direct, .page, .pageoff => {
57 const target_atom_index = coff_file.getAtomIndexForSymbol(self.target) orelse return null;
5858 const target_atom = coff_file.getAtom(target_atom_index);
5959 return target_atom.getSymbol(coff_file).value;
6060 },
61
6261 .import, .import_page, .import_pageoff => {
6362 const sym = coff_file.getSymbol(self.target);
6463 const index = coff_file.import_tables.getIndex(sym.value) orelse return null;