From ae8fb2151425dc7f61ad46e4c13b6c61f67cc68b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 20 Apr 2023 11:18:27 +0200 Subject: [PATCH] coff: use TableSection for GOT --- src/link/Coff.zig | 247 ++++++++++++++++++----------------- src/link/Coff/Relocation.zig | 15 +-- 2 files changed, 131 insertions(+), 131 deletions(-) diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 825afff36d..00da6f02e0 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -37,13 +37,14 @@ strtab_offset: ?u32 = null, temp_strtab: StringTable(.temp_strtab) = .{}, -got_entries: std.ArrayListUnmanaged(Entry) = .{}, -got_entries_free_list: std.ArrayListUnmanaged(u32) = .{}, -got_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{}, +got_table: TableSection(SymbolWithLoc) = .{}, /// A table of ImportTables partitioned by the library name. /// Key is an offset into the interning string table `temp_strtab`. import_tables: std.AutoArrayHashMapUnmanaged(u32, ImportTable) = .{}, + +got_table_count_dirty: bool = true, +got_table_contents_dirty: bool = true, imports_count_dirty: bool = true, /// Virtual address of the entry point procedure relative to image base. @@ -188,7 +189,8 @@ pub const PtrWidth = enum { p32, p64, - fn abiSize(pw: PtrWidth) u4 { + /// Size in bytes. + pub fn size(pw: PtrWidth) u4 { return switch (pw) { .p32 => 4, .p64 => 8, @@ -310,9 +312,7 @@ pub fn deinit(self: *Coff) void { self.globals_free_list.deinit(gpa); self.strtab.deinit(gpa); self.temp_strtab.deinit(gpa); - self.got_entries.deinit(gpa); - self.got_entries_free_list.deinit(gpa); - self.got_entries_table.deinit(gpa); + self.got_table.deinit(gpa); for (self.import_tables.values()) |*itab| { itab.deinit(gpa); @@ -371,7 +371,7 @@ fn populateMissingMetadata(self: *Coff) !void { } if (self.got_section_index == null) { - const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize(); + const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.size(); self.got_section_index = try self.allocateSection(".got", file_size, .{ .CNT_INITIALIZED_DATA = 1, .MEM_READ = 1, @@ -396,7 +396,7 @@ fn populateMissingMetadata(self: *Coff) !void { } if (self.idata_section_index == null) { - const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.abiSize(); + const file_size = @intCast(u32, self.base.options.symbol_count_hint) * self.ptr_width.size(); self.idata_section_index = try self.allocateSection(".idata", file_size, .{ .CNT_INITIALIZED_DATA = 1, .MEM_READ = 1, @@ -698,26 +698,11 @@ fn allocateGlobal(self: *Coff) !u32 { return index; } -pub fn allocateGotEntry(self: *Coff, target: SymbolWithLoc) !u32 { - const gpa = self.base.allocator; - try self.got_entries.ensureUnusedCapacity(gpa, 1); - - const index: u32 = blk: { - if (self.got_entries_free_list.popOrNull()) |index| { - log.debug(" (reusing GOT entry index {d})", .{index}); - break :blk index; - } else { - log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.items.len}); - const index = @intCast(u32, self.got_entries.items.len); - _ = self.got_entries.addOneAssumeCapacity(); - break :blk index; - } - }; - - self.got_entries.items[index] = .{ .target = target, .sym_index = 0 }; - try self.got_entries_table.putNoClobber(gpa, target, index); - - return index; +fn addGotEntry(self: *Coff, target: SymbolWithLoc) !void { + if (self.got_table.lookup.contains(target)) return; + const got_index = try self.got_table.allocateEntry(self.base.allocator, target); + try self.writeOffsetTableEntry(got_index); + self.got_table_count_dirty = true; } pub fn createAtom(self: *Coff) !Atom.Index { @@ -737,37 +722,6 @@ pub fn createAtom(self: *Coff) !Atom.Index { return atom_index; } -fn createGotAtom(self: *Coff, target: SymbolWithLoc) !Atom.Index { - const atom_index = try self.createAtom(); - const atom = self.getAtomPtr(atom_index); - atom.size = @sizeOf(u64); - - const sym = atom.getSymbolPtr(self); - sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1); - sym.value = try self.allocateAtom(atom_index, atom.size, @sizeOf(u64)); - - log.debug("allocated GOT atom at 0x{x}", .{sym.value}); - - try Atom.addRelocation(self, atom_index, .{ - .type = .direct, - .target = target, - .offset = 0, - .addend = 0, - .pcrel = false, - .length = 3, - }); - - const target_sym = self.getSymbol(target); - switch (target_sym.section_number) { - .UNDEFINED => @panic("TODO generate a binding for undefined GOT target"), - .ABSOLUTE => {}, - .DEBUG => unreachable, // not possible - else => try Atom.addBaseRelocation(self, atom_index, 0), - } - - return atom_index; -} - fn growAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignment: u32) !u32 { const atom = self.getAtom(atom_index); const sym = atom.getSymbol(self); @@ -873,17 +827,73 @@ fn writeMem(handle: std.ChildProcess.Id, pvaddr: std.os.windows.LPVOID, code: [] if (amt != code.len) return error.InputOutput; } -fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void { +fn writeOffsetTableEntry(self: *Coff, index: usize) !void { + const sect_id = self.got_section_index.?; + + if (self.got_table_count_dirty) { + const needed_size = @intCast(u32, self.got_table.entries.items.len * self.ptr_width.size()); + try self.growSection(sect_id, needed_size); + self.got_table_count_dirty = false; + } + + const header = &self.sections.items(.header)[sect_id]; + const entry = self.got_table.entries.items[index]; + const entry_value = self.getSymbol(entry).value; + const entry_offset = index * self.ptr_width.size(); + const file_offset = header.pointer_to_raw_data + entry_offset; + const vmaddr = header.virtual_address + entry_offset; + + log.debug("writing GOT entry {d}: @{x} => {x}", .{ index, vmaddr, entry_value }); + switch (self.ptr_width) { .p32 => { - var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32); - try self.writeAtom(atom_index, &buffer); + var buf: [4]u8 = undefined; + mem.writeIntLittle(u32, &buf, @intCast(u32, entry_value)); + try self.base.file.?.pwriteAll(&buf, file_offset); }, .p64 => { - var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64); - try self.writeAtom(atom_index, &buffer); + var buf: [8]u8 = undefined; + mem.writeIntLittle(u64, &buf, entry_value); + try self.base.file.?.pwriteAll(&buf, file_offset); }, } + + if (is_hot_update_compatible) { + if (self.base.child_pid) |handle| { + const gpa = self.base.allocator; + const slide = @ptrToInt(self.hot_state.loaded_base_address.?); + const actual_vmaddr = vmaddr + slide; + const pvaddr = @intToPtr(*anyopaque, actual_vmaddr); + log.debug("writing GOT entry to memory at address {x}", .{actual_vmaddr}); + if (build_options.enable_logging) { + switch (self.ptr_width) { + .p32 => { + var buf: [4]u8 = undefined; + try debugMem(gpa, handle, pvaddr, &buf); + }, + .p64 => { + var buf: [8]u8 = undefined; + try debugMem(gpa, handle, pvaddr, &buf); + }, + } + } + + switch (self.ptr_width) { + .p32 => { + var buf: [4]u8 = undefined; + writeMem(handle, pvaddr, &buf) catch |err| { + log.warn("writing to protected memory failed with error: {s}", .{@errorName(err)}); + }; + }, + .p64 => { + var buf: [8]u8 = undefined; + writeMem(handle, pvaddr, &buf) catch |err| { + log.warn("writing to protected memory failed with error: {s}", .{@errorName(err)}); + }; + }, + } + } + } } fn markRelocsDirtyByTarget(self: *Coff, target: SymbolWithLoc) void { @@ -904,6 +914,15 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void { reloc.dirty = true; } } + + // TODO: dirty only really affected GOT cells + for (self.got_table.entries.items) |entry| { + const target_addr = self.getSymbol(entry).value; + if (target_addr >= addr) { + self.got_table_contents_dirty = true; + break; + } + } } 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 { self.locals_free_list.append(gpa, sym_index) catch {}; // Try freeing GOT atom if this decl had one - const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null }; - if (self.got_entries_table.get(got_target)) |got_index| { - self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {}; - self.got_entries.items[got_index] = .{ - .target = .{ .sym_index = 0, .file = null }, - .sym_index = 0, - }; - _ = self.got_entries_table.remove(got_target); - - log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index }); - } + self.got_table.freeEntry(gpa, .{ .sym_index = sym_index }); self.locals.items[sym_index].section_number = .UNDEFINED; _ = self.atom_by_index_table.remove(sym_index); @@ -1243,14 +1252,7 @@ fn updateLazySymbolAtom( atom.size = code_len; symbol.value = vaddr; - const got_target = SymbolWithLoc{ .sym_index = local_sym_index, .file = null }; - const got_index = try self.allocateGotEntry(got_target); - const got_atom_index = try self.createGotAtom(got_target); - const got_atom = self.getAtom(got_atom_index); - self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?; - try self.writePtrWidthAtom(got_atom_index); - - self.markRelocsDirtyByTarget(atom.getSymbolWithLoc()); + try self.addGotEntry(.{ .sym_index = local_sym_index }); try self.writeAtom(atom_index, code); } @@ -1321,6 +1323,7 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple const decl_metadata = self.decls.get(decl_index).?; const atom_index = decl_metadata.atom; const atom = self.getAtom(atom_index); + const sym_index = atom.getSymbolIndex().?; const sect_index = decl_metadata.section; const code_len = @intCast(u32, code.len); @@ -1340,10 +1343,8 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple if (vaddr != sym.value) { sym.value = vaddr; log.debug(" (updating GOT entry)", .{}); - const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null }; - const got_atom_index = self.getGotAtomIndexForSymbol(got_target).?; - self.markRelocsDirtyByTarget(got_target); - try self.writePtrWidthAtom(got_atom_index); + const got_entry_index = self.got_table.lookup.get(.{ .sym_index = sym_index }).?; + try self.writeOffsetTableEntry(got_entry_index); } } else if (code_len < atom.size) { self.shrinkAtom(atom_index, code_len); @@ -1361,15 +1362,9 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []u8, comple self.getAtomPtr(atom_index).size = code_len; sym.value = vaddr; - const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null }; - const got_index = try self.allocateGotEntry(got_target); - const got_atom_index = try self.createGotAtom(got_target); - const got_atom = self.getAtom(got_atom_index); - self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?; - try self.writePtrWidthAtom(got_atom_index); + try self.addGotEntry(.{ .sym_index = sym_index }); } - self.markRelocsDirtyByTarget(atom.getSymbolWithLoc()); try self.writeAtom(atom_index, code); } @@ -1651,6 +1646,16 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod try self.writeAtom(atom_index, code.items); } + // Update GOT if it got moved in memory. + if (self.got_table_contents_dirty) { + for (self.got_table.entries.items, 0..) |entry, i| { + if (!self.got_table.lookup.contains(entry)) continue; + // TODO: write all in one go rather than incrementally. + try self.writeOffsetTableEntry(i); + } + self.got_table_contents_dirty = false; + } + try self.writeBaseRelocations(); if (self.getEntryPoint()) |entry_sym_loc| { @@ -1752,10 +1757,10 @@ fn writeBaseRelocations(self: *Coff) !void { while (it.next()) |entry| { const atom_index = entry.key_ptr.*; const atom = self.getAtom(atom_index); + const sym = atom.getSymbol(self); const offsets = entry.value_ptr.*; for (offsets.items) |offset| { - const sym = atom.getSymbol(self); const rva = sym.value + offset; const page = mem.alignBackwardGeneric(u32, rva, self.page_size); const gop = try pages.getOrPut(page); @@ -1769,6 +1774,27 @@ fn writeBaseRelocations(self: *Coff) !void { } } + { + const header = &self.sections.items(.header)[self.got_section_index.?]; + for (self.got_table.entries.items, 0..) |entry, index| { + if (!self.got_table.lookup.contains(entry)) continue; + + const sym = self.getSymbol(entry); + if (sym.section_number == .UNDEFINED) continue; + + const rva = @intCast(u32, header.virtual_address + index * self.ptr_width.size()); + const page = mem.alignBackwardGeneric(u32, rva, self.page_size); + const gop = try pages.getOrPut(page); + if (!gop.found_existing) { + gop.value_ptr.* = std.ArrayList(coff.BaseRelocation).init(gpa); + } + try gop.value_ptr.append(.{ + .offset = @intCast(u12, rva - page), + .type = .DIR64, + }); + } + } + var buffer = std.ArrayList(u8).init(gpa); defer buffer.deinit(); @@ -2315,14 +2341,6 @@ pub fn getAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.In return self.atom_by_index_table.get(sym_loc.sym_index); } -/// Returns GOT atom that references `sym_loc` if one exists. -/// Returns null otherwise. -pub fn getGotAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index { - const got_index = self.got_entries_table.get(sym_loc) orelse return null; - const got_entry = self.got_entries.items[got_index]; - return self.getAtomIndexForSymbol(.{ .sym_index = got_entry.sym_index, .file = null }); -} - fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void { if (name.len <= 8) { mem.copy(u8, &header.name, name); @@ -2410,25 +2428,7 @@ fn logSymtab(self: *Coff) void { } log.debug("GOT entries:", .{}); - for (self.got_entries.items, 0..) |entry, i| { - const got_sym = self.getSymbol(.{ .sym_index = entry.sym_index, .file = null }); - const target_sym = self.getSymbol(entry.target); - if (target_sym.section_number == .UNDEFINED) { - log.debug(" {d}@{x} => import('{s}')", .{ - i, - got_sym.value, - self.getSymbolName(entry.target), - }); - } else { - log.debug(" {d}@{x} => local(%{d}) in object({?d}) {s}", .{ - i, - got_sym.value, - entry.target.sym_index, - entry.target.file, - logSymAttributes(target_sym, &buf), - }); - } - } + log.debug("{}", .{self.got_table}); } fn logSections(self: *Coff) void { @@ -2484,6 +2484,7 @@ const LlvmObject = @import("../codegen/llvm.zig").Object; const Module = @import("../Module.zig"); const Object = @import("Coff/Object.zig"); const Relocation = @import("Coff/Relocation.zig"); +const TableSection = @import("table_section.zig").TableSection; const StringTable = @import("strtab.zig").StringTable; const Type = @import("../type.zig").Type; const TypedValue = @import("../TypedValue.zig"); diff --git a/src/link/Coff/Relocation.zig b/src/link/Coff/Relocation.zig index 2fafa0bbdc..815f2cc7ee 100644 --- a/src/link/Coff/Relocation.zig +++ b/src/link/Coff/Relocation.zig @@ -48,17 +48,16 @@ dirty: bool = true, /// Returns address of the target if any. pub fn getTargetAddress(self: Relocation, coff_file: *const Coff) ?u32 { switch (self.type) { - .got, .got_page, .got_pageoff, .direct, .page, .pageoff => { - const maybe_target_atom_index = switch (self.type) { - .got, .got_page, .got_pageoff => coff_file.getGotAtomIndexForSymbol(self.target), - .direct, .page, .pageoff => coff_file.getAtomIndexForSymbol(self.target), - else => unreachable, - }; - const target_atom_index = maybe_target_atom_index orelse return null; + .got, .got_page, .got_pageoff => { + const got_index = coff_file.got_table.lookup.get(self.target) orelse return null; + const header = coff_file.sections.items(.header)[coff_file.got_section_index.?]; + return header.virtual_address + got_index * coff_file.ptr_width.size(); + }, + .direct, .page, .pageoff => { + const target_atom_index = coff_file.getAtomIndexForSymbol(self.target) orelse return null; const target_atom = coff_file.getAtom(target_atom_index); return target_atom.getSymbol(coff_file).value; }, - .import, .import_page, .import_pageoff => { const sym = coff_file.getSymbol(self.target); const index = coff_file.import_tables.getIndex(sym.value) orelse return null;