Commit db877df8eb

Jakub Konka <kubkon@jakubkonka.com>
2023-03-28 14:13:39
coff: move import table definition into a separate ImportTable.zig module
1 parent fb3345e
src/link/Coff/ImportTable.zig
@@ -0,0 +1,133 @@
+//! Represents an import table in the .idata section where each contained pointer
+//! is to a symbol from the same DLL.
+//!
+//! The layout of .idata section is as follows:
+//!
+//! --- ADDR1 : IAT (all import tables concatenated together)
+//!     ptr
+//!     ptr
+//!     0 sentinel
+//!     ptr
+//!     0 sentinel
+//! --- ADDR2: headers
+//!     ImportDirectoryEntry header
+//!     ImportDirectoryEntry header
+//!     sentinel
+//! --- ADDR2: lookup tables
+//!     Lookup table
+//!     0 sentinel
+//!     Lookup table
+//!     0 sentinel
+//! --- ADDR3: name hint tables
+//!     hint-symname
+//!     hint-symname
+//! --- ADDR4: DLL names
+//!     DLL#1 name
+//!     DLL#2 name
+//! --- END
+
+entries: std.ArrayListUnmanaged(SymbolWithLoc) = .{},
+free_list: std.ArrayListUnmanaged(u32) = .{},
+lookup: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
+
+pub fn deinit(itab: *ImportTable, allocator: Allocator) void {
+    itab.entries.deinit(allocator);
+    itab.free_list.deinit(allocator);
+    itab.lookup.deinit(allocator);
+}
+
+/// Size of the import table does not include the sentinel.
+pub fn size(itab: ImportTable) u32 {
+    return @intCast(u32, itab.entries.items.len) * @sizeOf(u64);
+}
+
+pub fn addImport(itab: *ImportTable, allocator: Allocator, target: SymbolWithLoc) !ImportIndex {
+    try itab.entries.ensureUnusedCapacity(allocator, 1);
+    const index: u32 = blk: {
+        if (itab.free_list.popOrNull()) |index| {
+            log.debug("  (reusing import entry index {d})", .{index});
+            break :blk index;
+        } else {
+            log.debug("  (allocating import entry at index {d})", .{itab.entries.items.len});
+            const index = @intCast(u32, itab.entries.items.len);
+            _ = itab.entries.addOneAssumeCapacity();
+            break :blk index;
+        }
+    };
+    itab.entries.items[index] = target;
+    try itab.lookup.putNoClobber(allocator, target, index);
+    return index;
+}
+
+const Context = struct {
+    coff_file: *const Coff,
+    /// Index of this ImportTable in a global list of all tables.
+    /// This is required in order to calculate the base vaddr of this ImportTable.
+    index: usize,
+    /// Offset into the string interning table of the DLL this ImportTable corresponds to.
+    name_off: u32,
+};
+
+fn getBaseAddress(ctx: Context) u32 {
+    const header = ctx.coff_file.sections.items(.header)[ctx.coff_file.idata_section_index.?];
+    var addr = header.virtual_address;
+    for (ctx.coff_file.import_tables.values(), 0..) |other_itab, i| {
+        if (ctx.index == i) break;
+        addr += @intCast(u32, other_itab.entries.items.len * @sizeOf(u64)) + 8;
+    }
+    return addr;
+}
+
+pub fn getImportAddress(itab: *const ImportTable, target: SymbolWithLoc, ctx: Context) ?u32 {
+    const index = itab.lookup.get(target) orelse return null;
+    const base_vaddr = getBaseAddress(ctx);
+    return base_vaddr + index * @sizeOf(u64);
+}
+
+const FormatContext = struct {
+    itab: ImportTable,
+    ctx: Context,
+};
+
+fn fmt(
+    fmt_ctx: FormatContext,
+    comptime unused_format_string: []const u8,
+    options: std.fmt.FormatOptions,
+    writer: anytype,
+) @TypeOf(writer).Error!void {
+    _ = options;
+    comptime assert(unused_format_string.len == 0);
+    const lib_name = fmt_ctx.ctx.coff_file.temp_strtab.getAssumeExists(fmt_ctx.ctx.name_off);
+    const base_vaddr = getBaseAddress(fmt_ctx.ctx);
+    try writer.print("IAT({s}.dll) @{x}:", .{ lib_name, base_vaddr });
+    for (fmt_ctx.itab.entries.items, 0..) |entry, i| {
+        try writer.print("\n  {d}@{?x} => {s}", .{
+            i,
+            fmt_ctx.itab.getImportAddress(entry, fmt_ctx.ctx),
+            fmt_ctx.ctx.coff_file.getSymbolName(entry),
+        });
+    }
+}
+
+fn format(itab: ImportTable, comptime unused_format_string: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
+    _ = itab;
+    _ = unused_format_string;
+    _ = options;
+    _ = writer;
+    @compileError("do not format ImportTable directly; use itab.fmtDebug()");
+}
+
+pub fn fmtDebug(itab: ImportTable, ctx: Context) std.fmt.Formatter(fmt) {
+    return .{ .data = .{ .itab = itab, .ctx = ctx } };
+}
+
+const ImportIndex = u32;
+const ImportTable = @This();
+
+const std = @import("std");
+const assert = std.debug.assert;
+const log = std.log.scoped(.link);
+
+const Allocator = std.mem.Allocator;
+const Coff = @import("../Coff.zig");
+const SymbolWithLoc = Coff.SymbolWithLoc;
src/link/Coff/Relocation.zig
@@ -61,8 +61,13 @@ pub fn getTargetAddress(self: Relocation, coff_file: *const Coff) ?u32 {
 
         .import, .import_page, .import_pageoff => {
             const sym = coff_file.getSymbol(self.target);
-            const itab = coff_file.import_tables.get(sym.value) orelse return null;
-            return itab.getImportAddress(coff_file, self.target);
+            const index = coff_file.import_tables.getIndex(sym.value) orelse return null;
+            const itab = coff_file.import_tables.values()[index];
+            return itab.getImportAddress(self.target, .{
+                .coff_file = coff_file,
+                .index = index,
+                .name_off = sym.value,
+            });
         },
     }
 }
src/link/Coff.zig
@@ -1,36 +1,7 @@
-const Coff = @This();
-
-const std = @import("std");
-const build_options = @import("build_options");
-const builtin = @import("builtin");
-const assert = std.debug.assert;
-const coff = std.coff;
-const fmt = std.fmt;
-const log = std.log.scoped(.link);
-const math = std.math;
-const mem = std.mem;
-
-const Allocator = std.mem.Allocator;
-
-const codegen = @import("../codegen.zig");
-const link = @import("../link.zig");
-const lld = @import("Coff/lld.zig");
-const trace = @import("../tracy.zig").trace;
-
-const Air = @import("../Air.zig");
-pub const Atom = @import("Coff/Atom.zig");
-const Compilation = @import("../Compilation.zig");
-const Liveness = @import("../Liveness.zig");
-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 StringTable = @import("strtab.zig").StringTable;
-const TypedValue = @import("../TypedValue.zig");
-
-pub const base_tag: link.File.Tag = .coff;
-
-const msdos_stub = @embedFile("msdos-stub.bin");
+//! The main driver of the COFF linker.
+//! Currently uses our own implementation for the incremental linker, and falls back to
+//! LLD for traditional linking (linking relocatable object files).
+//! LLD is also the default linker for LLVM.
 
 /// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
 llvm_object: ?*LlvmObject = null,
@@ -160,92 +131,6 @@ const Section = struct {
     free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
 };
 
-/// Represents an import table in the .idata section where each contained pointer
-/// is to a symbol from the same DLL.
-///
-/// The layout of .idata section is as follows:
-///
-/// --- ADDR1 : IAT (all import tables concatenated together)
-///     ptr
-///     ptr
-///     0 sentinel
-///     ptr
-///     0 sentinel
-/// --- ADDR2: headers
-///     ImportDirectoryEntry header
-///     ImportDirectoryEntry header
-///     sentinel
-/// --- ADDR2: lookup tables
-///     Lookup table
-///     0 sentinel
-///     Lookup table
-///     0 sentinel
-/// --- ADDR3: name hint tables
-///     hint-symname
-///     hint-symname
-/// --- ADDR4: DLL names
-///     DLL#1 name
-///     DLL#2 name
-/// --- END
-const ImportTable = struct {
-    entries: std.ArrayListUnmanaged(SymbolWithLoc) = .{},
-    free_list: std.ArrayListUnmanaged(u32) = .{},
-    lookup: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
-    index: u8,
-
-    const ITable = @This();
-
-    fn deinit(itab: *ITable, allocator: Allocator) void {
-        itab.entries.deinit(allocator);
-        itab.free_list.deinit(allocator);
-        itab.lookup.deinit(allocator);
-    }
-
-    fn size(itab: ITable) u32 {
-        return @intCast(u32, itab.entries.items.len) * @sizeOf(u64);
-    }
-
-    fn addImport(itab: *ITable, allocator: Allocator, target: SymbolWithLoc) !u32 {
-        try itab.entries.ensureUnusedCapacity(allocator, 1);
-        const index: u32 = blk: {
-            if (itab.free_list.popOrNull()) |index| {
-                log.debug("  (reusing import entry index {d})", .{index});
-                break :blk index;
-            } else {
-                log.debug("  (allocating import entry at index {d})", .{itab.entries.items.len});
-                const index = @intCast(u32, itab.entries.items.len);
-                _ = itab.entries.addOneAssumeCapacity();
-                break :blk index;
-            }
-        };
-        itab.entries.items[index] = target;
-        try itab.lookup.putNoClobber(allocator, target, index);
-        return index;
-    }
-
-    fn getBaseAddress(itab: *const ITable, coff_file: *const Coff) u32 {
-        const header = coff_file.sections.items(.header)[coff_file.idata_section_index.?];
-        var addr = header.virtual_address;
-        for (coff_file.import_tables.values(), 0..) |other_itab, i| {
-            if (itab.index == i) break;
-            addr += @intCast(u32, other_itab.entries.items.len * @sizeOf(u64)) + 8;
-        }
-        return addr;
-    }
-
-    pub fn getImportAddress(itab: *const ITable, coff_file: *const Coff, target: SymbolWithLoc) ?u32 {
-        const index = itab.lookup.get(target) orelse return null;
-        const base_vaddr = itab.getBaseAddress(coff_file);
-        return base_vaddr + index * @sizeOf(u64);
-    }
-
-    pub fn write(itab: ITable, writer: anytype) !void {
-        for (itab.entries.items) |_| {
-            try writer.writeIntLittle(u64, 0);
-        }
-    }
-};
-
 const DeclMetadata = struct {
     atom: Atom.Index,
     section: u16,
@@ -1527,7 +1412,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
         const res = try self.import_tables.getOrPut(gpa, sym.value);
         const itable = res.value_ptr;
         if (!res.found_existing) {
-            itable.* = .{ .index = @intCast(u8, self.import_tables.values().len - 1) };
+            itable.* = .{};
         }
         if (itable.lookup.contains(global)) continue;
         // TODO: we could technically write the pointer placeholder for to-be-bound import here,
@@ -2367,15 +2252,46 @@ fn logSections(self: *Coff) void {
 fn logImportTables(self: *const Coff) void {
     log.debug("import tables:", .{});
     for (self.import_tables.keys(), 0..) |off, i| {
-        const lib_name = self.temp_strtab.getAssumeExists(off);
         const itable = self.import_tables.values()[i];
-        log.debug("IAT({s}) @{x}:", .{ lib_name, itable.getBaseAddress(self) });
-        for (itable.entries.items, 0..) |entry, j| {
-            log.debug("  {d}@{?x} => {s}", .{
-                j,
-                itable.getImportAddress(self, entry),
-                self.getSymbolName(entry),
-            });
-        }
+        log.debug("{}", .{itable.fmtDebug(.{
+            .coff_file = self,
+            .index = i,
+            .name_off = off,
+        })});
     }
 }
+
+const Coff = @This();
+
+const std = @import("std");
+const build_options = @import("build_options");
+const builtin = @import("builtin");
+const assert = std.debug.assert;
+const coff = std.coff;
+const fmt = std.fmt;
+const log = std.log.scoped(.link);
+const math = std.math;
+const mem = std.mem;
+
+const Allocator = std.mem.Allocator;
+
+const codegen = @import("../codegen.zig");
+const link = @import("../link.zig");
+const lld = @import("Coff/lld.zig");
+const trace = @import("../tracy.zig").trace;
+
+const Air = @import("../Air.zig");
+pub const Atom = @import("Coff/Atom.zig");
+const Compilation = @import("../Compilation.zig");
+const ImportTable = @import("Coff/ImportTable.zig");
+const Liveness = @import("../Liveness.zig");
+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 StringTable = @import("strtab.zig").StringTable;
+const TypedValue = @import("../TypedValue.zig");
+
+pub const base_tag: link.File.Tag = .coff;
+
+const msdos_stub = @embedFile("msdos-stub.bin");
CMakeLists.txt
@@ -583,6 +583,7 @@ set(ZIG_STAGE2_SOURCES
     "${CMAKE_SOURCE_DIR}/src/link/C.zig"
     "${CMAKE_SOURCE_DIR}/src/link/Coff.zig"
     "${CMAKE_SOURCE_DIR}/src/link/Coff/Atom.zig"
+    "${CMAKE_SOURCE_DIR}/src/link/Coff/ImportTable.zig"
     "${CMAKE_SOURCE_DIR}/src/link/Coff/Object.zig"
     "${CMAKE_SOURCE_DIR}/src/link/Coff/lld.zig"
     "${CMAKE_SOURCE_DIR}/src/link/Elf.zig"