commit 969f2cff8258fc91d7037704ddac4e6f4f037029 (tree)
parent 2962db333f43c8bb10a1e2ad4cdd19dfab26515b
Author: Jacob Young <jacobly0@users.noreply.github.com>
Date: Sun, 5 Oct 2025 06:56:07 -0400
Elf2: implement virtual allocation
This allows segments to be moved around in the output file without
needing to reapply relocations until virtual address space is exhaused.
Diffstat:
6 files changed, 2659 insertions(+), 2679 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -561,6 +561,7 @@ set(ZIG_STAGE2_SOURCES
src/libs/libunwind.zig
src/link.zig
src/link/C.zig
+ src/link/Coff.zig
src/link/Dwarf.zig
src/link/Elf.zig
src/link/Elf/Archive.zig
diff --git a/src/Compilation.zig b/src/Compilation.zig
@@ -2718,35 +2718,17 @@ pub fn destroy(comp: *Compilation) void {
}
comp.crt_files.deinit(gpa);
}
-
- if (comp.libunwind_static_lib) |*crt_file| {
- crt_file.deinit(gpa);
- }
- if (comp.libcxx_static_lib) |*crt_file| {
- crt_file.deinit(gpa);
- }
- if (comp.libcxxabi_static_lib) |*crt_file| {
- crt_file.deinit(gpa);
- }
- if (comp.compiler_rt_lib) |*crt_file| {
- crt_file.deinit(gpa);
- }
- if (comp.compiler_rt_obj) |*crt_file| {
- crt_file.deinit(gpa);
- }
- if (comp.ubsan_rt_lib) |*crt_file| {
- crt_file.deinit(gpa);
- }
- if (comp.ubsan_rt_obj) |*crt_file| {
- crt_file.deinit(gpa);
- }
- if (comp.fuzzer_lib) |*crt_file| {
- crt_file.deinit(gpa);
- }
-
- if (comp.zigc_static_lib) |*crt_file| {
- crt_file.deinit(gpa);
- }
+ if (comp.libcxx_static_lib) |*crt_file| crt_file.deinit(gpa);
+ if (comp.libcxxabi_static_lib) |*crt_file| crt_file.deinit(gpa);
+ if (comp.libunwind_static_lib) |*crt_file| crt_file.deinit(gpa);
+ if (comp.tsan_lib) |*crt_file| crt_file.deinit(gpa);
+ if (comp.ubsan_rt_lib) |*crt_file| crt_file.deinit(gpa);
+ if (comp.ubsan_rt_obj) |*crt_file| crt_file.deinit(gpa);
+ if (comp.zigc_static_lib) |*crt_file| crt_file.deinit(gpa);
+ if (comp.compiler_rt_lib) |*crt_file| crt_file.deinit(gpa);
+ if (comp.compiler_rt_obj) |*crt_file| crt_file.deinit(gpa);
+ if (comp.compiler_rt_dyn_lib) |*crt_file| crt_file.deinit(gpa);
+ if (comp.fuzzer_lib) |*crt_file| crt_file.deinit(gpa);
if (comp.glibc_so_files) |*glibc_file| {
glibc_file.deinit(gpa);
diff --git a/src/link.zig b/src/link.zig
@@ -1265,7 +1265,7 @@ pub const File = struct {
pub const Lld = @import("link/Lld.zig");
pub const C = @import("link/C.zig");
- pub const Coff2 = @import("link/Coff2.zig");
+ pub const Coff2 = @import("link/Coff.zig");
pub const Elf = @import("link/Elf.zig");
pub const Elf2 = @import("link/Elf2.zig");
pub const MachO = @import("link/MachO.zig");
diff --git a/src/link/Coff.zig b/src/link/Coff.zig
@@ -0,0 +1,2206 @@
+base: link.File,
+mf: MappedFile,
+nodes: std.MultiArrayList(Node),
+import_table: ImportTable,
+strings: std.HashMapUnmanaged(
+ u32,
+ void,
+ std.hash_map.StringIndexContext,
+ std.hash_map.default_max_load_percentage,
+),
+string_bytes: std.ArrayList(u8),
+section_table: std.ArrayList(Symbol.Index),
+symbol_table: std.ArrayList(Symbol),
+globals: std.AutoArrayHashMapUnmanaged(GlobalName, Symbol.Index),
+global_pending_index: u32,
+navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, Symbol.Index),
+uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Symbol.Index),
+lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
+ map: std.AutoArrayHashMapUnmanaged(InternPool.Index, Symbol.Index),
+ pending_index: u32,
+}),
+pending_uavs: std.AutoArrayHashMapUnmanaged(Node.UavMapIndex, struct {
+ alignment: InternPool.Alignment,
+ src_loc: Zcu.LazySrcLoc,
+}),
+relocs: std.ArrayList(Reloc),
+/// This is hiding actual bugs with global symbols! Reconsider once they are implemented correctly.
+entry_hack: Symbol.Index,
+
+pub const default_file_alignment: u16 = 0x200;
+pub const default_size_of_stack_reserve: u32 = 0x1000000;
+pub const default_size_of_stack_commit: u32 = 0x1000;
+pub const default_size_of_heap_reserve: u32 = 0x100000;
+pub const default_size_of_heap_commit: u32 = 0x1000;
+
+/// This is the start of a Portable Executable (PE) file.
+/// It starts with a MS-DOS header followed by a MS-DOS stub program.
+/// This data does not change so we include it as follows in all binaries.
+///
+/// In this context,
+/// A "paragraph" is 16 bytes.
+/// A "page" is 512 bytes.
+/// A "long" is 4 bytes.
+/// A "word" is 2 bytes.
+pub const msdos_stub: [120]u8 = .{
+ 'M', 'Z', // Magic number. Stands for Mark Zbikowski (designer of the MS-DOS executable format).
+ 0x78, 0x00, // Number of bytes in the last page. This matches the size of this entire MS-DOS stub.
+ 0x01, 0x00, // Number of pages.
+ 0x00, 0x00, // Number of entries in the relocation table.
+ 0x04, 0x00, // The number of paragraphs taken up by the header. 4 * 16 = 64, which matches the header size (all bytes before the MS-DOS stub program).
+ 0x00, 0x00, // The number of paragraphs required by the program.
+ 0x00, 0x00, // The number of paragraphs requested by the program.
+ 0x00, 0x00, // Initial value for SS (relocatable segment address).
+ 0x00, 0x00, // Initial value for SP.
+ 0x00, 0x00, // Checksum.
+ 0x00, 0x00, // Initial value for IP.
+ 0x00, 0x00, // Initial value for CS (relocatable segment address).
+ 0x40, 0x00, // Absolute offset to relocation table. 64 matches the header size (all bytes before the MS-DOS stub program).
+ 0x00, 0x00, // Overlay number. Zero means this is the main executable.
+}
+ // Reserved words.
+ ++ .{ 0x00, 0x00 } ** 4
+ // OEM-related fields.
+ ++ .{
+ 0x00, 0x00, // OEM identifier.
+ 0x00, 0x00, // OEM information.
+ }
+ // Reserved words.
+ ++ .{ 0x00, 0x00 } ** 10
+ // Address of the PE header (a long). This matches the size of this entire MS-DOS stub, so that's the address of what's after this MS-DOS stub.
+ ++ .{ 0x78, 0x00, 0x00, 0x00 }
+ // What follows is a 16-bit x86 MS-DOS program of 7 instructions that prints the bytes after these instructions and then exits.
+ ++ .{
+ // Set the value of the data segment to the same value as the code segment.
+ 0x0e, // push cs
+ 0x1f, // pop ds
+ // Set the DX register to the address of the message.
+ // If you count all bytes of these 7 instructions you get 14, so that's the address of what's after these instructions.
+ 0xba, 14, 0x00, // mov dx, 14
+ // Set AH to the system call code for printing a message.
+ 0xb4, 0x09, // mov ah, 0x09
+ // Perform the system call to print the message.
+ 0xcd, 0x21, // int 0x21
+ // Set AH to 0x4c which is the system call code for exiting, and set AL to 0x01 which is the exit code.
+ 0xb8, 0x01, 0x4c, // mov ax, 0x4c01
+ // Peform the system call to exit the program with exit code 1.
+ 0xcd, 0x21, // int 0x21
+ }
+ // Message to print.
+ ++ "This program cannot be run in DOS mode.".*
+ // Message terminators.
+ ++ .{
+ '$', // We do not pass a length to the print system call; the string is terminated by this character.
+ 0x00, 0x00, // Terminating zero bytes.
+ };
+
+pub const Node = union(enum) {
+ file,
+ header,
+ signature,
+ coff_header,
+ optional_header,
+ data_directories,
+ section_table,
+ section: Symbol.Index,
+ import_directory_table,
+ import_lookup_table: ImportTable.Index,
+ import_address_table: ImportTable.Index,
+ import_hint_name_table: ImportTable.Index,
+ global: GlobalMapIndex,
+ nav: NavMapIndex,
+ uav: UavMapIndex,
+ lazy_code: LazyMapRef.Index(.code),
+ lazy_const_data: LazyMapRef.Index(.const_data),
+
+ pub const GlobalMapIndex = enum(u32) {
+ _,
+
+ pub fn globalName(gmi: GlobalMapIndex, coff: *const Coff) GlobalName {
+ return coff.globals.keys()[@intFromEnum(gmi)];
+ }
+
+ pub fn symbol(gmi: GlobalMapIndex, coff: *const Coff) Symbol.Index {
+ return coff.globals.values()[@intFromEnum(gmi)];
+ }
+ };
+
+ pub const NavMapIndex = enum(u32) {
+ _,
+
+ pub fn navIndex(nmi: NavMapIndex, coff: *const Coff) InternPool.Nav.Index {
+ return coff.navs.keys()[@intFromEnum(nmi)];
+ }
+
+ pub fn symbol(nmi: NavMapIndex, coff: *const Coff) Symbol.Index {
+ return coff.navs.values()[@intFromEnum(nmi)];
+ }
+ };
+
+ pub const UavMapIndex = enum(u32) {
+ _,
+
+ pub fn uavValue(umi: UavMapIndex, coff: *const Coff) InternPool.Index {
+ return coff.uavs.keys()[@intFromEnum(umi)];
+ }
+
+ pub fn symbol(umi: UavMapIndex, coff: *const Coff) Symbol.Index {
+ return coff.uavs.values()[@intFromEnum(umi)];
+ }
+ };
+
+ pub const LazyMapRef = struct {
+ kind: link.File.LazySymbol.Kind,
+ index: u32,
+
+ pub fn Index(comptime kind: link.File.LazySymbol.Kind) type {
+ return enum(u32) {
+ _,
+
+ pub fn ref(lmi: @This()) LazyMapRef {
+ return .{ .kind = kind, .index = @intFromEnum(lmi) };
+ }
+
+ pub fn lazySymbol(lmi: @This(), coff: *const Coff) link.File.LazySymbol {
+ return lmi.ref().lazySymbol(coff);
+ }
+
+ pub fn symbol(lmi: @This(), coff: *const Coff) Symbol.Index {
+ return lmi.ref().symbol(coff);
+ }
+ };
+ }
+
+ pub fn lazySymbol(lmr: LazyMapRef, coff: *const Coff) link.File.LazySymbol {
+ return .{ .kind = lmr.kind, .ty = coff.lazy.getPtrConst(lmr.kind).map.keys()[lmr.index] };
+ }
+
+ pub fn symbol(lmr: LazyMapRef, coff: *const Coff) Symbol.Index {
+ return coff.lazy.getPtrConst(lmr.kind).map.values()[lmr.index];
+ }
+ };
+
+ pub const Tag = @typeInfo(Node).@"union".tag_type.?;
+
+ const known_count = @typeInfo(@TypeOf(known)).@"struct".fields.len;
+ const known = known: {
+ const Known = enum {
+ file,
+ header,
+ signature,
+ coff_header,
+ optional_header,
+ data_directories,
+ section_table,
+ };
+ var mut_known: std.enums.EnumFieldStruct(Known, MappedFile.Node.Index, null) = undefined;
+ for (@typeInfo(Known).@"enum".fields) |field|
+ @field(mut_known, field.name) = @enumFromInt(field.value);
+ break :known mut_known;
+ };
+
+ comptime {
+ if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Node) == 8);
+ }
+};
+
+pub const DataDirectory = enum {
+ export_table,
+ import_table,
+ resorce_table,
+ exception_table,
+ certificate_table,
+ base_relocation_table,
+ debug,
+ architecture,
+ global_ptr,
+ tls_table,
+ load_config_table,
+ bound_import,
+ import_address_table,
+ delay_import_descriptor,
+ clr_runtime_header,
+ reserved,
+
+ pub const len = @typeInfo(DataDirectory).@"enum".fields.len;
+};
+
+pub const ImportTable = struct {
+ ni: MappedFile.Node.Index,
+ entries: std.AutoArrayHashMapUnmanaged(void, Entry),
+
+ pub const Entry = struct {
+ import_lookup_table_ni: MappedFile.Node.Index,
+ import_address_table_si: Symbol.Index,
+ import_hint_name_table_ni: MappedFile.Node.Index,
+ len: u32,
+ hint_name_len: u32,
+ };
+
+ const Adapter = struct {
+ coff: *Coff,
+
+ pub fn eql(adapter: Adapter, lhs_key: []const u8, _: void, rhs_index: usize) bool {
+ const coff = adapter.coff;
+ const dll_name = coff.import_table.entries.values()[rhs_index]
+ .import_hint_name_table_ni.sliceConst(&coff.mf);
+ return std.mem.startsWith(u8, dll_name, lhs_key) and
+ std.mem.startsWith(u8, dll_name[lhs_key.len..], ".dll\x00");
+ }
+
+ pub fn hash(_: Adapter, key: []const u8) u32 {
+ assert(std.mem.indexOfScalar(u8, key, 0) == null);
+ return std.array_hash_map.hashString(key);
+ }
+ };
+
+ pub const Index = enum(u32) {
+ _,
+
+ pub fn get(import_index: ImportTable.Index, coff: *Coff) *Entry {
+ return &coff.import_table.entries.values()[@intFromEnum(import_index)];
+ }
+ };
+};
+
+pub const String = enum(u32) {
+ _,
+
+ pub const Optional = enum(u32) {
+ none = std.math.maxInt(u32),
+ _,
+
+ pub fn unwrap(os: String.Optional) ?String {
+ return switch (os) {
+ else => |s| @enumFromInt(@intFromEnum(s)),
+ .none => null,
+ };
+ }
+
+ pub fn toSlice(os: String.Optional, coff: *Coff) ?[:0]const u8 {
+ return (os.unwrap() orelse return null).toSlice(coff);
+ }
+ };
+
+ pub fn toSlice(s: String, coff: *Coff) [:0]const u8 {
+ const slice = coff.string_bytes.items[@intFromEnum(s)..];
+ return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0];
+ }
+
+ pub fn toOptional(s: String) String.Optional {
+ return @enumFromInt(@intFromEnum(s));
+ }
+};
+
+pub const GlobalName = struct { name: String, lib_name: String.Optional };
+
+pub const Symbol = struct {
+ ni: MappedFile.Node.Index,
+ rva: u32,
+ size: u32,
+ /// Relocations contained within this symbol
+ loc_relocs: Reloc.Index,
+ /// Relocations targeting this symbol
+ target_relocs: Reloc.Index,
+ section_number: SectionNumber,
+ unused0: u32 = 0,
+ unused1: u32 = 0,
+ unused2: u16 = 0,
+
+ pub const SectionNumber = enum(i16) {
+ UNDEFINED = 0,
+ ABSOLUTE = -1,
+ DEBUG = -2,
+ _,
+
+ fn toIndex(sn: SectionNumber) u15 {
+ return @intCast(@intFromEnum(sn) - 1);
+ }
+
+ pub fn symbol(sn: SectionNumber, coff: *const Coff) Symbol.Index {
+ return coff.section_table.items[sn.toIndex()];
+ }
+
+ pub fn header(sn: SectionNumber, coff: *Coff) *std.coff.SectionHeader {
+ return &coff.sectionTableSlice()[sn.toIndex()];
+ }
+ };
+
+ pub const Index = enum(u32) {
+ null,
+ data,
+ idata,
+ rdata,
+ text,
+ _,
+
+ const known_count = @typeInfo(Index).@"enum".fields.len;
+
+ pub fn get(si: Symbol.Index, coff: *Coff) *Symbol {
+ return &coff.symbol_table.items[@intFromEnum(si)];
+ }
+
+ pub fn node(si: Symbol.Index, coff: *Coff) MappedFile.Node.Index {
+ const ni = si.get(coff).ni;
+ assert(ni != .none);
+ return ni;
+ }
+
+ pub fn flushMoved(si: Symbol.Index, coff: *Coff) void {
+ const sym = si.get(coff);
+ sym.rva = coff.computeNodeRva(sym.ni);
+ if (si == coff.entry_hack) {
+ @branchHint(.unlikely);
+ coff.targetStore(&coff.optionalHeaderStandardPtr().address_of_entry_point, sym.rva);
+ }
+ si.applyLocationRelocs(coff);
+ si.applyTargetRelocs(coff);
+ }
+
+ pub fn applyLocationRelocs(si: Symbol.Index, coff: *Coff) void {
+ for (coff.relocs.items[@intFromEnum(si.get(coff).loc_relocs)..]) |*reloc| {
+ if (reloc.loc != si) break;
+ reloc.apply(coff);
+ }
+ }
+
+ pub fn applyTargetRelocs(si: Symbol.Index, coff: *Coff) void {
+ var ri = si.get(coff).target_relocs;
+ while (ri != .none) {
+ const reloc = ri.get(coff);
+ assert(reloc.target == si);
+ reloc.apply(coff);
+ ri = reloc.next;
+ }
+ }
+
+ pub fn deleteLocationRelocs(si: Symbol.Index, coff: *Coff) void {
+ const sym = si.get(coff);
+ for (coff.relocs.items[@intFromEnum(sym.loc_relocs)..]) |*reloc| {
+ if (reloc.loc != si) break;
+ reloc.delete(coff);
+ }
+ sym.loc_relocs = .none;
+ }
+ };
+
+ comptime {
+ if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Symbol) == 32);
+ }
+};
+
+pub const Reloc = extern struct {
+ type: Reloc.Type,
+ prev: Reloc.Index,
+ next: Reloc.Index,
+ loc: Symbol.Index,
+ target: Symbol.Index,
+ unused: u32,
+ offset: u64,
+ addend: i64,
+
+ pub const Type = extern union {
+ AMD64: std.coff.IMAGE.REL.AMD64,
+ ARM: std.coff.IMAGE.REL.ARM,
+ ARM64: std.coff.IMAGE.REL.ARM64,
+ SH: std.coff.IMAGE.REL.SH,
+ PPC: std.coff.IMAGE.REL.PPC,
+ I386: std.coff.IMAGE.REL.I386,
+ IA64: std.coff.IMAGE.REL.IA64,
+ MIPS: std.coff.IMAGE.REL.MIPS,
+ M32R: std.coff.IMAGE.REL.M32R,
+ };
+
+ pub const Index = enum(u32) {
+ none = std.math.maxInt(u32),
+ _,
+
+ pub fn get(si: Reloc.Index, coff: *Coff) *Reloc {
+ return &coff.relocs.items[@intFromEnum(si)];
+ }
+ };
+
+ pub fn apply(reloc: *const Reloc, coff: *Coff) void {
+ const loc_sym = reloc.loc.get(coff);
+ switch (loc_sym.ni) {
+ .none => return,
+ else => |ni| if (ni.hasMoved(&coff.mf)) return,
+ }
+ const target_sym = reloc.target.get(coff);
+ switch (target_sym.ni) {
+ .none => return,
+ else => |ni| if (ni.hasMoved(&coff.mf)) return,
+ }
+ const loc_slice = loc_sym.ni.slice(&coff.mf)[@intCast(reloc.offset)..];
+ const target_rva = target_sym.rva +% @as(u64, @bitCast(reloc.addend));
+ const target_endian = coff.targetEndian();
+ switch (coff.targetLoad(&coff.headerPtr().machine)) {
+ else => |machine| @panic(@tagName(machine)),
+ .AMD64 => switch (reloc.type.AMD64) {
+ else => |kind| @panic(@tagName(kind)),
+ .ABSOLUTE => {},
+ .ADDR64 => std.mem.writeInt(
+ u64,
+ loc_slice[0..8],
+ coff.optionalHeaderField(.image_base) + target_rva,
+ target_endian,
+ ),
+ .ADDR32 => std.mem.writeInt(
+ u32,
+ loc_slice[0..4],
+ @intCast(coff.optionalHeaderField(.image_base) + target_rva),
+ target_endian,
+ ),
+ .ADDR32NB => std.mem.writeInt(
+ u32,
+ loc_slice[0..4],
+ @intCast(target_rva),
+ target_endian,
+ ),
+ .REL32 => std.mem.writeInt(
+ i32,
+ loc_slice[0..4],
+ @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 4)))),
+ target_endian,
+ ),
+ .REL32_1 => std.mem.writeInt(
+ i32,
+ loc_slice[0..4],
+ @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 5)))),
+ target_endian,
+ ),
+ .REL32_2 => std.mem.writeInt(
+ i32,
+ loc_slice[0..4],
+ @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 6)))),
+ target_endian,
+ ),
+ .REL32_3 => std.mem.writeInt(
+ i32,
+ loc_slice[0..4],
+ @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 7)))),
+ target_endian,
+ ),
+ .REL32_4 => std.mem.writeInt(
+ i32,
+ loc_slice[0..4],
+ @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 8)))),
+ target_endian,
+ ),
+ .REL32_5 => std.mem.writeInt(
+ i32,
+ loc_slice[0..4],
+ @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 9)))),
+ target_endian,
+ ),
+ },
+ .I386 => switch (reloc.type.I386) {
+ else => |kind| @panic(@tagName(kind)),
+ .ABSOLUTE => {},
+ .DIR16 => std.mem.writeInt(
+ u16,
+ loc_slice[0..2],
+ @intCast(coff.optionalHeaderField(.image_base) + target_rva),
+ target_endian,
+ ),
+ .REL16 => std.mem.writeInt(
+ i16,
+ loc_slice[0..2],
+ @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 2)))),
+ target_endian,
+ ),
+ .DIR32 => std.mem.writeInt(
+ u32,
+ loc_slice[0..4],
+ @intCast(coff.optionalHeaderField(.image_base) + target_rva),
+ target_endian,
+ ),
+ .DIR32NB => std.mem.writeInt(
+ u32,
+ loc_slice[0..4],
+ @intCast(target_rva),
+ target_endian,
+ ),
+ .REL32 => std.mem.writeInt(
+ i32,
+ loc_slice[0..4],
+ @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 4)))),
+ target_endian,
+ ),
+ },
+ }
+ }
+
+ pub fn delete(reloc: *Reloc, coff: *Coff) void {
+ switch (reloc.prev) {
+ .none => {
+ const target = reloc.target.get(coff);
+ assert(target.target_relocs.get(coff) == reloc);
+ target.target_relocs = reloc.next;
+ },
+ else => |prev| prev.get(coff).next = reloc.next,
+ }
+ switch (reloc.next) {
+ .none => {},
+ else => |next| next.get(coff).prev = reloc.prev,
+ }
+ reloc.* = undefined;
+ }
+
+ comptime {
+ if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Reloc) == 40);
+ }
+};
+
+pub fn open(
+ arena: std.mem.Allocator,
+ comp: *Compilation,
+ path: std.Build.Cache.Path,
+ options: link.File.OpenOptions,
+) !*Coff {
+ return create(arena, comp, path, options);
+}
+pub fn createEmpty(
+ arena: std.mem.Allocator,
+ comp: *Compilation,
+ path: std.Build.Cache.Path,
+ options: link.File.OpenOptions,
+) !*Coff {
+ return create(arena, comp, path, options);
+}
+fn create(
+ arena: std.mem.Allocator,
+ comp: *Compilation,
+ path: std.Build.Cache.Path,
+ options: link.File.OpenOptions,
+) !*Coff {
+ const target = &comp.root_mod.resolved_target.result;
+ assert(target.ofmt == .coff);
+ if (target.cpu.arch.endian() != comptime targetEndian(undefined))
+ return error.UnsupportedCOFFArchitecture;
+ const is_image = switch (comp.config.output_mode) {
+ .Exe => true,
+ .Lib => switch (comp.config.link_mode) {
+ .static => false,
+ .dynamic => true,
+ },
+ .Obj => false,
+ };
+ const machine = target.toCoffMachine();
+ const timestamp: u32 = if (options.repro) 0 else @truncate(@as(u64, @bitCast(std.time.timestamp())));
+ const major_subsystem_version = options.major_subsystem_version orelse 6;
+ const minor_subsystem_version = options.minor_subsystem_version orelse 0;
+ const magic: std.coff.OptionalHeader.Magic = switch (target.ptrBitWidth()) {
+ 0...32 => .PE32,
+ 33...64 => .@"PE32+",
+ else => return error.UnsupportedCOFFArchitecture,
+ };
+ const section_align: std.mem.Alignment = switch (machine) {
+ .AMD64, .I386 => @enumFromInt(12),
+ .SH3, .SH3DSP, .SH4, .SH5 => @enumFromInt(12),
+ .MIPS16, .MIPSFPU, .MIPSFPU16, .WCEMIPSV2 => @enumFromInt(12),
+ .POWERPC, .POWERPCFP => @enumFromInt(12),
+ .ALPHA, .ALPHA64 => @enumFromInt(13),
+ .IA64 => @enumFromInt(13),
+ .ARM => @enumFromInt(12),
+ else => return error.UnsupportedCOFFArchitecture,
+ };
+
+ const coff = try arena.create(Coff);
+ const file = try path.root_dir.handle.createFile(path.sub_path, .{
+ .read = true,
+ .mode = link.File.determineMode(comp.config.output_mode, comp.config.link_mode),
+ });
+ errdefer file.close();
+ coff.* = .{
+ .base = .{
+ .tag = .coff2,
+
+ .comp = comp,
+ .emit = path,
+
+ .file = file,
+ .gc_sections = false,
+ .print_gc_sections = false,
+ .build_id = .none,
+ .allow_shlib_undefined = false,
+ .stack_size = 0,
+ },
+ .mf = try .init(file, comp.gpa),
+ .nodes = .empty,
+ .import_table = .{
+ .ni = .none,
+ .entries = .empty,
+ },
+ .strings = .empty,
+ .string_bytes = .empty,
+ .section_table = .empty,
+ .symbol_table = .empty,
+ .globals = .empty,
+ .global_pending_index = 0,
+ .navs = .empty,
+ .uavs = .empty,
+ .lazy = .initFill(.{
+ .map = .empty,
+ .pending_index = 0,
+ }),
+ .pending_uavs = .empty,
+ .relocs = .empty,
+ .entry_hack = .null,
+ };
+ errdefer coff.deinit();
+
+ try coff.initHeaders(
+ is_image,
+ machine,
+ timestamp,
+ major_subsystem_version,
+ minor_subsystem_version,
+ magic,
+ section_align,
+ );
+ return coff;
+}
+
+pub fn deinit(coff: *Coff) void {
+ const gpa = coff.base.comp.gpa;
+ coff.mf.deinit(gpa);
+ coff.nodes.deinit(gpa);
+ coff.import_table.entries.deinit(gpa);
+ coff.strings.deinit(gpa);
+ coff.string_bytes.deinit(gpa);
+ coff.section_table.deinit(gpa);
+ coff.symbol_table.deinit(gpa);
+ coff.globals.deinit(gpa);
+ coff.navs.deinit(gpa);
+ coff.uavs.deinit(gpa);
+ for (&coff.lazy.values) |*lazy| lazy.map.deinit(gpa);
+ coff.pending_uavs.deinit(gpa);
+ coff.relocs.deinit(gpa);
+ coff.* = undefined;
+}
+
+fn initHeaders(
+ coff: *Coff,
+ is_image: bool,
+ machine: std.coff.IMAGE.FILE.MACHINE,
+ timestamp: u32,
+ major_subsystem_version: u16,
+ minor_subsystem_version: u16,
+ magic: std.coff.OptionalHeader.Magic,
+ section_align: std.mem.Alignment,
+) !void {
+ const comp = coff.base.comp;
+ const gpa = comp.gpa;
+ const file_align: std.mem.Alignment = comptime .fromByteUnits(default_file_alignment);
+ const target_endian = coff.targetEndian();
+
+ const optional_header_size: u16 = if (is_image) switch (magic) {
+ _ => unreachable,
+ inline else => |ct_magic| @sizeOf(@field(std.coff.OptionalHeader, @tagName(ct_magic))),
+ } else 0;
+ const data_directories_size: u16 = if (is_image)
+ @sizeOf(std.coff.ImageDataDirectory) * DataDirectory.len
+ else
+ 0;
+
+ try coff.nodes.ensureTotalCapacity(gpa, Node.known_count);
+ coff.nodes.appendAssumeCapacity(.file);
+
+ const header_ni = Node.known.header;
+ assert(header_ni == try coff.mf.addOnlyChildNode(gpa, .root, .{
+ .alignment = coff.mf.flags.block_size,
+ .fixed = true,
+ }));
+ coff.nodes.appendAssumeCapacity(.header);
+
+ const signature_ni = Node.known.signature;
+ assert(signature_ni == try coff.mf.addOnlyChildNode(gpa, header_ni, .{
+ .size = (if (is_image) msdos_stub.len else 0) + "PE\x00\x00".len,
+ .alignment = .@"4",
+ .fixed = true,
+ }));
+ coff.nodes.appendAssumeCapacity(.signature);
+ {
+ const signature_slice = signature_ni.slice(&coff.mf);
+ if (is_image) @memcpy(signature_slice[0..msdos_stub.len], &msdos_stub);
+ @memcpy(signature_slice[signature_slice.len - 4 ..], "PE\x00\x00");
+ }
+
+ const coff_header_ni = Node.known.coff_header;
+ assert(coff_header_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
+ .size = @sizeOf(std.coff.Header),
+ .alignment = .@"4",
+ .fixed = true,
+ }));
+ coff.nodes.appendAssumeCapacity(.coff_header);
+ {
+ const coff_header = coff.headerPtr();
+ coff_header.* = .{
+ .machine = machine,
+ .number_of_sections = 0,
+ .time_date_stamp = timestamp,
+ .pointer_to_symbol_table = 0,
+ .number_of_symbols = 0,
+ .size_of_optional_header = optional_header_size + data_directories_size,
+ .flags = .{
+ .RELOCS_STRIPPED = is_image,
+ .EXECUTABLE_IMAGE = is_image,
+ .DEBUG_STRIPPED = true,
+ .@"32BIT_MACHINE" = magic == .PE32,
+ .LARGE_ADDRESS_AWARE = magic == .@"PE32+",
+ .DLL = comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic,
+ },
+ };
+ if (target_endian != native_endian) std.mem.byteSwapAllFields(std.coff.Header, coff_header);
+ }
+
+ const optional_header_ni = Node.known.optional_header;
+ assert(optional_header_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
+ .size = optional_header_size,
+ .alignment = .@"4",
+ .fixed = true,
+ }));
+ coff.nodes.appendAssumeCapacity(.optional_header);
+ coff.targetStore(&coff.optionalHeaderStandardPtr().magic, magic);
+ if (is_image) switch (coff.optionalHeaderPtr()) {
+ .PE32 => |optional_header| {
+ optional_header.* = .{
+ .standard = .{
+ .magic = .PE32,
+ .major_linker_version = 0,
+ .minor_linker_version = 0,
+ .size_of_code = 0,
+ .size_of_initialized_data = 0,
+ .size_of_uninitialized_data = 0,
+ .address_of_entry_point = 0,
+ .base_of_code = 0,
+ },
+ .base_of_data = 0,
+ .image_base = switch (coff.base.comp.config.output_mode) {
+ .Exe => 0x400000,
+ .Lib => switch (coff.base.comp.config.link_mode) {
+ .static => 0,
+ .dynamic => 0x10000000,
+ },
+ .Obj => 0,
+ },
+ .section_alignment = @intCast(section_align.toByteUnits()),
+ .file_alignment = @intCast(file_align.toByteUnits()),
+ .major_operating_system_version = 6,
+ .minor_operating_system_version = 0,
+ .major_image_version = 0,
+ .minor_image_version = 0,
+ .major_subsystem_version = major_subsystem_version,
+ .minor_subsystem_version = minor_subsystem_version,
+ .win32_version_value = 0,
+ .size_of_image = 0,
+ .size_of_headers = 0,
+ .checksum = 0,
+ .subsystem = .WINDOWS_CUI,
+ .dll_flags = .{
+ .HIGH_ENTROPY_VA = true,
+ .DYNAMIC_BASE = true,
+ .TERMINAL_SERVER_AWARE = true,
+ .NX_COMPAT = true,
+ },
+ .size_of_stack_reserve = default_size_of_stack_reserve,
+ .size_of_stack_commit = default_size_of_stack_commit,
+ .size_of_heap_reserve = default_size_of_heap_reserve,
+ .size_of_heap_commit = default_size_of_heap_commit,
+ .loader_flags = 0,
+ .number_of_rva_and_sizes = DataDirectory.len,
+ };
+ if (target_endian != native_endian)
+ std.mem.byteSwapAllFields(std.coff.OptionalHeader.PE32, optional_header);
+ },
+ .@"PE32+" => |optional_header| {
+ optional_header.* = .{
+ .standard = .{
+ .magic = .@"PE32+",
+ .major_linker_version = 0,
+ .minor_linker_version = 0,
+ .size_of_code = 0,
+ .size_of_initialized_data = 0,
+ .size_of_uninitialized_data = 0,
+ .address_of_entry_point = 0,
+ .base_of_code = 0,
+ },
+ .image_base = switch (coff.base.comp.config.output_mode) {
+ .Exe => 0x140000000,
+ .Lib => switch (coff.base.comp.config.link_mode) {
+ .static => 0,
+ .dynamic => 0x180000000,
+ },
+ .Obj => 0,
+ },
+ .section_alignment = @intCast(section_align.toByteUnits()),
+ .file_alignment = @intCast(file_align.toByteUnits()),
+ .major_operating_system_version = 6,
+ .minor_operating_system_version = 0,
+ .major_image_version = 0,
+ .minor_image_version = 0,
+ .major_subsystem_version = major_subsystem_version,
+ .minor_subsystem_version = minor_subsystem_version,
+ .win32_version_value = 0,
+ .size_of_image = 0,
+ .size_of_headers = 0,
+ .checksum = 0,
+ .subsystem = .WINDOWS_CUI,
+ .dll_flags = .{
+ .HIGH_ENTROPY_VA = true,
+ .DYNAMIC_BASE = true,
+ .TERMINAL_SERVER_AWARE = true,
+ .NX_COMPAT = true,
+ },
+ .size_of_stack_reserve = default_size_of_stack_reserve,
+ .size_of_stack_commit = default_size_of_stack_commit,
+ .size_of_heap_reserve = default_size_of_heap_reserve,
+ .size_of_heap_commit = default_size_of_heap_commit,
+ .loader_flags = 0,
+ .number_of_rva_and_sizes = DataDirectory.len,
+ };
+ if (target_endian != native_endian)
+ std.mem.byteSwapAllFields(std.coff.OptionalHeader.@"PE32+", optional_header);
+ },
+ };
+
+ const data_directories_ni = Node.known.data_directories;
+ assert(data_directories_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
+ .size = data_directories_size,
+ .alignment = .@"4",
+ .fixed = true,
+ }));
+ coff.nodes.appendAssumeCapacity(.data_directories);
+ {
+ const data_directories = coff.dataDirectorySlice();
+ @memset(data_directories, .{ .virtual_address = 0, .size = 0 });
+ if (target_endian != native_endian)
+ std.mem.byteSwapAllFields([DataDirectory.len]std.coff.ImageDataDirectory, data_directories);
+ }
+
+ const section_table_ni = Node.known.section_table;
+ assert(section_table_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
+ .alignment = .@"4",
+ .fixed = true,
+ }));
+ coff.nodes.appendAssumeCapacity(.section_table);
+
+ assert(coff.nodes.len == Node.known_count);
+
+ try coff.symbol_table.ensureTotalCapacity(gpa, Symbol.Index.known_count);
+ coff.symbol_table.addOneAssumeCapacity().* = .{
+ .ni = .none,
+ .rva = 0,
+ .size = 0,
+ .loc_relocs = .none,
+ .target_relocs = .none,
+ .section_number = .UNDEFINED,
+ };
+ assert(try coff.addSection(".data", .{
+ .CNT_INITIALIZED_DATA = true,
+ .MEM_READ = true,
+ .MEM_WRITE = true,
+ }) == .data);
+ assert(try coff.addSection(".idata", .{
+ .CNT_INITIALIZED_DATA = true,
+ .MEM_READ = true,
+ }) == .idata);
+ assert(try coff.addSection(".rdata", .{
+ .CNT_INITIALIZED_DATA = true,
+ .MEM_READ = true,
+ }) == .rdata);
+ assert(try coff.addSection(".text", .{
+ .CNT_CODE = true,
+ .MEM_EXECUTE = true,
+ .MEM_READ = true,
+ }) == .text);
+ coff.import_table.ni = try coff.mf.addLastChildNode(
+ gpa,
+ Symbol.Index.idata.node(coff),
+ .{ .alignment = .@"4" },
+ );
+ coff.nodes.appendAssumeCapacity(.import_directory_table);
+ assert(coff.symbol_table.items.len == Symbol.Index.known_count);
+}
+
+fn getNode(coff: *const Coff, ni: MappedFile.Node.Index) Node {
+ return coff.nodes.get(@intFromEnum(ni));
+}
+fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {
+ const parent_rva = parent_rva: {
+ const parent_si = switch (coff.getNode(ni.parent(&coff.mf))) {
+ .file,
+ .header,
+ .signature,
+ .coff_header,
+ .optional_header,
+ .data_directories,
+ .section_table,
+ => unreachable,
+ .section => |si| si,
+ .import_directory_table => unreachable,
+ .import_lookup_table => |import_index| break :parent_rva coff.targetLoad(
+ &coff.importDirectoryEntryPtr(import_index).import_lookup_table_rva,
+ ),
+ .import_address_table => |import_index| break :parent_rva coff.targetLoad(
+ &coff.importDirectoryEntryPtr(import_index).import_address_table_rva,
+ ),
+ .import_hint_name_table => |import_index| break :parent_rva coff.targetLoad(
+ &coff.importDirectoryEntryPtr(import_index).name_rva,
+ ),
+ inline .global, .nav, .uav, .lazy_code, .lazy_const_data => |mi| mi.symbol(coff),
+ };
+ break :parent_rva parent_si.get(coff).rva;
+ };
+ const offset, _ = ni.location(&coff.mf).resolve(&coff.mf);
+ return @intCast(parent_rva + offset);
+}
+
+pub inline fn targetEndian(_: *const Coff) std.builtin.Endian {
+ return .little;
+}
+fn targetLoad(coff: *const Coff, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {
+ const Child = @typeInfo(@TypeOf(ptr)).pointer.child;
+ return switch (@typeInfo(Child)) {
+ else => @compileError(@typeName(Child)),
+ .int => std.mem.toNative(Child, ptr.*, coff.targetEndian()),
+ .@"enum" => |@"enum"| @enumFromInt(coff.targetLoad(@as(*@"enum".tag_type, @ptrCast(ptr)))),
+ .@"struct" => |@"struct"| @bitCast(
+ coff.targetLoad(@as(*@"struct".backing_integer.?, @ptrCast(ptr))),
+ ),
+ };
+}
+fn targetStore(coff: *const Coff, ptr: anytype, val: @typeInfo(@TypeOf(ptr)).pointer.child) void {
+ const Child = @typeInfo(@TypeOf(ptr)).pointer.child;
+ return switch (@typeInfo(Child)) {
+ else => @compileError(@typeName(Child)),
+ .int => ptr.* = std.mem.nativeTo(Child, val, coff.targetEndian()),
+ .@"enum" => |@"enum"| coff.targetStore(
+ @as(*@"enum".tag_type, @ptrCast(ptr)),
+ @intFromEnum(val),
+ ),
+ .@"struct" => |@"struct"| coff.targetStore(
+ @as(*@"struct".backing_integer.?, @ptrCast(ptr)),
+ @bitCast(val),
+ ),
+ };
+}
+
+pub fn headerPtr(coff: *Coff) *std.coff.Header {
+ return @ptrCast(@alignCast(Node.known.coff_header.slice(&coff.mf)));
+}
+
+pub fn optionalHeaderStandardPtr(coff: *Coff) *std.coff.OptionalHeader {
+ return @ptrCast(@alignCast(
+ Node.known.optional_header.slice(&coff.mf)[0..@sizeOf(std.coff.OptionalHeader)],
+ ));
+}
+
+pub const OptionalHeaderPtr = union(std.coff.OptionalHeader.Magic) {
+ PE32: *std.coff.OptionalHeader.PE32,
+ @"PE32+": *std.coff.OptionalHeader.@"PE32+",
+};
+pub fn optionalHeaderPtr(coff: *Coff) OptionalHeaderPtr {
+ const slice = Node.known.optional_header.slice(&coff.mf);
+ return switch (coff.targetLoad(&coff.optionalHeaderStandardPtr().magic)) {
+ _ => unreachable,
+ inline else => |magic| @unionInit(
+ OptionalHeaderPtr,
+ @tagName(magic),
+ @ptrCast(@alignCast(slice)),
+ ),
+ };
+}
+pub fn optionalHeaderField(
+ coff: *Coff,
+ comptime field: std.meta.FieldEnum(std.coff.OptionalHeader.@"PE32+"),
+) @FieldType(std.coff.OptionalHeader.@"PE32+", @tagName(field)) {
+ return switch (coff.optionalHeaderPtr()) {
+ inline else => |optional_header| coff.targetLoad(&@field(optional_header, @tagName(field))),
+ };
+}
+
+pub fn dataDirectorySlice(coff: *Coff) *[DataDirectory.len]std.coff.ImageDataDirectory {
+ return @ptrCast(@alignCast(Node.known.data_directories.slice(&coff.mf)));
+}
+pub fn dataDirectoryPtr(coff: *Coff, data_directory: DataDirectory) *std.coff.ImageDataDirectory {
+ return &coff.dataDirectorySlice()[@intFromEnum(data_directory)];
+}
+
+pub fn sectionTableSlice(coff: *Coff) []std.coff.SectionHeader {
+ return @ptrCast(@alignCast(Node.known.section_table.slice(&coff.mf)));
+}
+
+pub fn importDirectoryTableSlice(coff: *Coff) []std.coff.ImportDirectoryEntry {
+ return @ptrCast(@alignCast(coff.import_table.ni.slice(&coff.mf)));
+}
+pub fn importDirectoryEntryPtr(
+ coff: *Coff,
+ import_index: ImportTable.Index,
+) *std.coff.ImportDirectoryEntry {
+ return &coff.importDirectoryTableSlice()[@intFromEnum(import_index)];
+}
+
+fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index {
+ defer coff.symbol_table.addOneAssumeCapacity().* = .{
+ .ni = .none,
+ .rva = 0,
+ .size = 0,
+ .loc_relocs = .none,
+ .target_relocs = .none,
+ .section_number = .UNDEFINED,
+ };
+ return @enumFromInt(coff.symbol_table.items.len);
+}
+
+fn initSymbolAssumeCapacity(coff: *Coff) !Symbol.Index {
+ const si = coff.addSymbolAssumeCapacity();
+ return si;
+}
+
+fn getOrPutString(coff: *Coff, string: []const u8) !String {
+ const gpa = coff.base.comp.gpa;
+ try coff.string_bytes.ensureUnusedCapacity(gpa, string.len + 1);
+ const gop = try coff.strings.getOrPutContextAdapted(
+ gpa,
+ string,
+ std.hash_map.StringIndexAdapter{ .bytes = &coff.string_bytes },
+ .{ .bytes = &coff.string_bytes },
+ );
+ if (!gop.found_existing) {
+ gop.key_ptr.* = @intCast(coff.string_bytes.items.len);
+ gop.value_ptr.* = {};
+ coff.string_bytes.appendSliceAssumeCapacity(string);
+ coff.string_bytes.appendAssumeCapacity(0);
+ }
+ return @enumFromInt(gop.key_ptr.*);
+}
+
+fn getOrPutOptionalString(coff: *Coff, string: ?[]const u8) !String.Optional {
+ return (try coff.getOrPutString(string orelse return .none)).toOptional();
+}
+
+pub fn globalSymbol(coff: *Coff, name: []const u8, lib_name: ?[]const u8) !Symbol.Index {
+ const gpa = coff.base.comp.gpa;
+ try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
+ const sym_gop = try coff.globals.getOrPut(gpa, .{
+ .name = try coff.getOrPutString(name),
+ .lib_name = try coff.getOrPutOptionalString(lib_name),
+ });
+ if (!sym_gop.found_existing) {
+ sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
+ coff.base.comp.link_synth_prog_node.increaseEstimatedTotalItems(1);
+ }
+ return sym_gop.value_ptr.*;
+}
+
+fn navMapIndex(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavMapIndex {
+ const gpa = zcu.gpa;
+ try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
+ const sym_gop = try coff.navs.getOrPut(gpa, nav_index);
+ if (!sym_gop.found_existing) sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
+ return @enumFromInt(sym_gop.index);
+}
+pub fn navSymbol(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbol.Index {
+ const ip = &zcu.intern_pool;
+ const nav = ip.getNav(nav_index);
+ if (nav.getExtern(ip)) |@"extern"| return coff.globalSymbol(
+ @"extern".name.toSlice(ip),
+ @"extern".lib_name.toSlice(ip),
+ );
+ const nmi = try coff.navMapIndex(zcu, nav_index);
+ return nmi.symbol(coff);
+}
+
+fn uavMapIndex(coff: *Coff, uav_val: InternPool.Index) !Node.UavMapIndex {
+ const gpa = coff.base.comp.gpa;
+ try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
+ const sym_gop = try coff.uavs.getOrPut(gpa, uav_val);
+ if (!sym_gop.found_existing) sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
+ return @enumFromInt(sym_gop.index);
+}
+pub fn uavSymbol(coff: *Coff, uav_val: InternPool.Index) !Symbol.Index {
+ const umi = try coff.uavMapIndex(uav_val);
+ return umi.symbol(coff);
+}
+
+pub fn lazySymbol(coff: *Coff, lazy: link.File.LazySymbol) !Symbol.Index {
+ const gpa = coff.base.comp.gpa;
+ try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
+ const sym_gop = try coff.lazy.getPtr(lazy.kind).map.getOrPut(gpa, lazy.ty);
+ if (!sym_gop.found_existing) {
+ sym_gop.value_ptr.* = try coff.initSymbolAssumeCapacity();
+ coff.base.comp.link_synth_prog_node.increaseEstimatedTotalItems(1);
+ }
+ return sym_gop.value_ptr.*;
+}
+
+pub fn getNavVAddr(
+ coff: *Coff,
+ pt: Zcu.PerThread,
+ nav: InternPool.Nav.Index,
+ reloc_info: link.File.RelocInfo,
+) !u64 {
+ return coff.getVAddr(reloc_info, try coff.navSymbol(pt.zcu, nav));
+}
+
+pub fn getUavVAddr(
+ coff: *Coff,
+ uav: InternPool.Index,
+ reloc_info: link.File.RelocInfo,
+) !u64 {
+ return coff.getVAddr(reloc_info, try coff.uavSymbol(uav));
+}
+
+pub fn getVAddr(coff: *Coff, reloc_info: link.File.RelocInfo, target_si: Symbol.Index) !u64 {
+ try coff.addReloc(
+ @enumFromInt(reloc_info.parent.atom_index),
+ reloc_info.offset,
+ target_si,
+ reloc_info.addend,
+ switch (coff.targetLoad(&coff.headerPtr().machine)) {
+ else => unreachable,
+ .AMD64 => .{ .AMD64 = .ADDR64 },
+ .I386 => .{ .I386 = .DIR32 },
+ },
+ );
+ return coff.optionalHeaderField(.image_base) + target_si.get(coff).rva;
+}
+
+fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags) !Symbol.Index {
+ const gpa = coff.base.comp.gpa;
+ try coff.nodes.ensureUnusedCapacity(gpa, 1);
+ try coff.section_table.ensureUnusedCapacity(gpa, 1);
+ try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
+
+ const coff_header = coff.headerPtr();
+ const section_index = coff.targetLoad(&coff_header.number_of_sections);
+ const section_table_len = section_index + 1;
+ coff.targetStore(&coff_header.number_of_sections, section_table_len);
+ try Node.known.section_table.resize(
+ &coff.mf,
+ gpa,
+ @sizeOf(std.coff.SectionHeader) * section_table_len,
+ );
+ const ni = try coff.mf.addLastChildNode(gpa, .root, .{
+ .alignment = coff.mf.flags.block_size,
+ .moved = true,
+ .bubbles_moved = false,
+ });
+ const si = coff.addSymbolAssumeCapacity();
+ coff.section_table.appendAssumeCapacity(si);
+ coff.nodes.appendAssumeCapacity(.{ .section = si });
+ const section_table = coff.sectionTableSlice();
+ const virtual_size = coff.optionalHeaderField(.section_alignment);
+ const rva: u32 = switch (section_index) {
+ 0 => @intCast(Node.known.header.location(&coff.mf).resolve(&coff.mf)[1]),
+ else => coff.section_table.items[section_index - 1].get(coff).rva +
+ coff.targetLoad(§ion_table[section_index - 1].virtual_size),
+ };
+ {
+ const sym = si.get(coff);
+ sym.ni = ni;
+ sym.rva = rva;
+ sym.section_number = @enumFromInt(section_table_len);
+ }
+ const section = §ion_table[section_index];
+ section.* = .{
+ .name = undefined,
+ .virtual_size = virtual_size,
+ .virtual_address = rva,
+ .size_of_raw_data = 0,
+ .pointer_to_raw_data = 0,
+ .pointer_to_relocations = 0,
+ .pointer_to_linenumbers = 0,
+ .number_of_relocations = 0,
+ .number_of_linenumbers = 0,
+ .flags = flags,
+ };
+ @memcpy(section.name[0..name.len], name);
+ @memset(section.name[name.len..], 0);
+ if (coff.targetEndian() != native_endian)
+ std.mem.byteSwapAllFields(std.coff.SectionHeader, section);
+ switch (coff.optionalHeaderPtr()) {
+ inline else => |optional_header| coff.targetStore(
+ &optional_header.size_of_image,
+ @intCast(rva + virtual_size),
+ ),
+ }
+ return si;
+}
+
+pub fn addReloc(
+ coff: *Coff,
+ loc_si: Symbol.Index,
+ offset: u64,
+ target_si: Symbol.Index,
+ addend: i64,
+ @"type": Reloc.Type,
+) !void {
+ const gpa = coff.base.comp.gpa;
+ const target = target_si.get(coff);
+ const ri: Reloc.Index = @enumFromInt(coff.relocs.items.len);
+ (try coff.relocs.addOne(gpa)).* = .{
+ .type = @"type",
+ .prev = .none,
+ .next = target.target_relocs,
+ .loc = loc_si,
+ .target = target_si,
+ .unused = 0,
+ .offset = offset,
+ .addend = addend,
+ };
+ switch (target.target_relocs) {
+ .none => {},
+ else => |target_ri| target_ri.get(coff).prev = ri,
+ }
+ target.target_relocs = ri;
+}
+
+pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) void {
+ _ = coff;
+ _ = prog_node;
+}
+
+pub fn updateNav(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
+ coff.updateNavInner(pt, nav_index) catch |err| switch (err) {
+ error.OutOfMemory,
+ error.Overflow,
+ error.RelocationNotByteAligned,
+ => |e| return e,
+ else => |e| return coff.base.cgFail(nav_index, "linker failed to update variable: {t}", .{e}),
+ };
+}
+fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
+
+ const nav = ip.getNav(nav_index);
+ const nav_val = nav.status.fully_resolved.val;
+ const nav_init, const is_threadlocal = switch (ip.indexToKey(nav_val)) {
+ else => .{ nav_val, false },
+ .variable => |variable| .{ variable.init, variable.is_threadlocal },
+ .@"extern" => return,
+ .func => .{ .none, false },
+ };
+ if (nav_init == .none or !Type.fromInterned(ip.typeOf(nav_init)).hasRuntimeBits(zcu)) return;
+
+ const nmi = try coff.navMapIndex(zcu, nav_index);
+ const si = nmi.symbol(coff);
+ const ni = ni: {
+ const sym = si.get(coff);
+ switch (sym.ni) {
+ .none => {
+ try coff.nodes.ensureUnusedCapacity(gpa, 1);
+ _ = is_threadlocal;
+ const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.data.node(coff), .{
+ .alignment = pt.navAlignment(nav_index).toStdMem(),
+ .moved = true,
+ });
+ coff.nodes.appendAssumeCapacity(.{ .nav = nmi });
+ sym.ni = ni;
+ sym.section_number = Symbol.Index.data.get(coff).section_number;
+ },
+ else => si.deleteLocationRelocs(coff),
+ }
+ assert(sym.loc_relocs == .none);
+ sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
+ break :ni sym.ni;
+ };
+
+ var nw: MappedFile.Node.Writer = undefined;
+ ni.writer(&coff.mf, gpa, &nw);
+ defer nw.deinit();
+ codegen.generateSymbol(
+ &coff.base,
+ pt,
+ zcu.navSrcLoc(nav_index),
+ .fromInterned(nav_init),
+ &nw.interface,
+ .{ .atom_index = @intFromEnum(si) },
+ ) catch |err| switch (err) {
+ error.WriteFailed => return error.OutOfMemory,
+ else => |e| return e,
+ };
+ si.get(coff).size = @intCast(nw.interface.end);
+ si.applyLocationRelocs(coff);
+}
+
+pub fn lowerUav(
+ coff: *Coff,
+ pt: Zcu.PerThread,
+ uav_val: InternPool.Index,
+ uav_align: InternPool.Alignment,
+ src_loc: Zcu.LazySrcLoc,
+) !codegen.SymbolResult {
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+
+ try coff.pending_uavs.ensureUnusedCapacity(gpa, 1);
+ const umi = try coff.uavMapIndex(uav_val);
+ const si = umi.symbol(coff);
+ if (switch (si.get(coff).ni) {
+ .none => true,
+ else => |ni| uav_align.toStdMem().order(ni.alignment(&coff.mf)).compare(.gt),
+ }) {
+ const gop = coff.pending_uavs.getOrPutAssumeCapacity(umi);
+ if (gop.found_existing) {
+ gop.value_ptr.alignment = gop.value_ptr.alignment.max(uav_align);
+ } else {
+ gop.value_ptr.* = .{
+ .alignment = uav_align,
+ .src_loc = src_loc,
+ };
+ coff.base.comp.link_const_prog_node.increaseEstimatedTotalItems(1);
+ }
+ }
+ return .{ .sym_index = @intFromEnum(si) };
+}
+
+pub fn updateFunc(
+ coff: *Coff,
+ pt: Zcu.PerThread,
+ func_index: InternPool.Index,
+ mir: *const codegen.AnyMir,
+) !void {
+ coff.updateFuncInner(pt, func_index, mir) catch |err| switch (err) {
+ error.OutOfMemory,
+ error.Overflow,
+ error.RelocationNotByteAligned,
+ error.CodegenFail,
+ => |e| return e,
+ else => |e| return coff.base.cgFail(
+ pt.zcu.funcInfo(func_index).owner_nav,
+ "linker failed to update function: {s}",
+ .{@errorName(e)},
+ ),
+ };
+}
+fn updateFuncInner(
+ coff: *Coff,
+ pt: Zcu.PerThread,
+ func_index: InternPool.Index,
+ mir: *const codegen.AnyMir,
+) !void {
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
+ const func = zcu.funcInfo(func_index);
+ const nav = ip.getNav(func.owner_nav);
+
+ const nmi = try coff.navMapIndex(zcu, func.owner_nav);
+ const si = nmi.symbol(coff);
+ log.debug("updateFunc({f}) = {d}", .{ nav.fqn.fmt(ip), si });
+ const ni = ni: {
+ const sym = si.get(coff);
+ switch (sym.ni) {
+ .none => {
+ try coff.nodes.ensureUnusedCapacity(gpa, 1);
+ const mod = zcu.navFileScope(func.owner_nav).mod.?;
+ const target = &mod.resolved_target.result;
+ const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.text.node(coff), .{
+ .alignment = switch (nav.status.fully_resolved.alignment) {
+ .none => switch (mod.optimize_mode) {
+ .Debug,
+ .ReleaseSafe,
+ .ReleaseFast,
+ => target_util.defaultFunctionAlignment(target),
+ .ReleaseSmall => target_util.minFunctionAlignment(target),
+ },
+ else => |a| a.maxStrict(target_util.minFunctionAlignment(target)),
+ }.toStdMem(),
+ .moved = true,
+ });
+ coff.nodes.appendAssumeCapacity(.{ .nav = nmi });
+ sym.ni = ni;
+ sym.section_number = Symbol.Index.text.get(coff).section_number;
+ },
+ else => si.deleteLocationRelocs(coff),
+ }
+ assert(sym.loc_relocs == .none);
+ sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
+ break :ni sym.ni;
+ };
+
+ var nw: MappedFile.Node.Writer = undefined;
+ ni.writer(&coff.mf, gpa, &nw);
+ defer nw.deinit();
+ codegen.emitFunction(
+ &coff.base,
+ pt,
+ zcu.navSrcLoc(func.owner_nav),
+ func_index,
+ @intFromEnum(si),
+ mir,
+ &nw.interface,
+ .none,
+ ) catch |err| switch (err) {
+ error.WriteFailed => return nw.err.?,
+ else => |e| return e,
+ };
+ si.get(coff).size = @intCast(nw.interface.end);
+ si.applyLocationRelocs(coff);
+}
+
+pub fn updateErrorData(coff: *Coff, pt: Zcu.PerThread) !void {
+ coff.flushLazy(pt, .{
+ .kind = .const_data,
+ .index = @intCast(coff.lazy.getPtr(.const_data).map.getIndex(.anyerror_type) orelse return),
+ }) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.CodegenFail => return error.LinkFailure,
+ else => |e| return coff.base.comp.link_diags.fail("updateErrorData failed {t}", .{e}),
+ };
+}
+
+pub fn flush(
+ coff: *Coff,
+ arena: std.mem.Allocator,
+ tid: Zcu.PerThread.Id,
+ prog_node: std.Progress.Node,
+) !void {
+ _ = arena;
+ _ = prog_node;
+ while (try coff.idle(tid)) {}
+
+ // hack for stage2_x86_64 + coff
+ const comp = coff.base.comp;
+ if (comp.compiler_rt_dyn_lib) |crt_file| {
+ const gpa = comp.gpa;
+ const compiler_rt_sub_path = try std.fs.path.join(gpa, &.{
+ std.fs.path.dirname(coff.base.emit.sub_path) orelse "",
+ std.fs.path.basename(crt_file.full_object_path.sub_path),
+ });
+ defer gpa.free(compiler_rt_sub_path);
+ crt_file.full_object_path.root_dir.handle.copyFile(
+ crt_file.full_object_path.sub_path,
+ coff.base.emit.root_dir.handle,
+ compiler_rt_sub_path,
+ .{},
+ ) catch |err| switch (err) {
+ else => |e| return comp.link_diags.fail("Copy '{s}' failed: {s}", .{
+ compiler_rt_sub_path,
+ @errorName(e),
+ }),
+ };
+ }
+}
+
+pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
+ const comp = coff.base.comp;
+ task: {
+ while (coff.pending_uavs.pop()) |pending_uav| {
+ const sub_prog_node = coff.idleProgNode(
+ tid,
+ comp.link_const_prog_node,
+ .{ .uav = pending_uav.key },
+ );
+ defer sub_prog_node.end();
+ coff.flushUav(
+ .{ .zcu = coff.base.comp.zcu.?, .tid = tid },
+ pending_uav.key,
+ pending_uav.value.alignment,
+ pending_uav.value.src_loc,
+ ) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ else => |e| return coff.base.comp.link_diags.fail(
+ "linker failed to lower constant: {t}",
+ .{e},
+ ),
+ };
+ break :task;
+ }
+ if (coff.global_pending_index < coff.globals.count()) {
+ const pt: Zcu.PerThread = .{ .zcu = coff.base.comp.zcu.?, .tid = tid };
+ const gmi: Node.GlobalMapIndex = @enumFromInt(coff.global_pending_index);
+ coff.global_pending_index += 1;
+ const sub_prog_node = comp.link_synth_prog_node.start(
+ gmi.globalName(coff).name.toSlice(coff),
+ 0,
+ );
+ defer sub_prog_node.end();
+ coff.flushGlobal(pt, gmi) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ else => |e| return coff.base.comp.link_diags.fail(
+ "linker failed to lower constant: {t}",
+ .{e},
+ ),
+ };
+ break :task;
+ }
+ var lazy_it = coff.lazy.iterator();
+ while (lazy_it.next()) |lazy| if (lazy.value.pending_index < lazy.value.map.count()) {
+ const pt: Zcu.PerThread = .{ .zcu = coff.base.comp.zcu.?, .tid = tid };
+ const lmr: Node.LazyMapRef = .{ .kind = lazy.key, .index = lazy.value.pending_index };
+ lazy.value.pending_index += 1;
+ const kind = switch (lmr.kind) {
+ .code => "code",
+ .const_data => "data",
+ };
+ var name: [std.Progress.Node.max_name_len]u8 = undefined;
+ const sub_prog_node = comp.link_synth_prog_node.start(
+ std.fmt.bufPrint(&name, "lazy {s} for {f}", .{
+ kind,
+ Type.fromInterned(lmr.lazySymbol(coff).ty).fmt(pt),
+ }) catch &name,
+ 0,
+ );
+ defer sub_prog_node.end();
+ coff.flushLazy(pt, lmr) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ else => |e| return coff.base.comp.link_diags.fail(
+ "linker failed to lower lazy {s}: {t}",
+ .{ kind, e },
+ ),
+ };
+ break :task;
+ };
+ while (coff.mf.updates.pop()) |ni| {
+ const clean_moved = ni.cleanMoved(&coff.mf);
+ const clean_resized = ni.cleanResized(&coff.mf);
+ if (clean_moved or clean_resized) {
+ const sub_prog_node = coff.idleProgNode(tid, coff.mf.update_prog_node, coff.getNode(ni));
+ defer sub_prog_node.end();
+ if (clean_moved) try coff.flushMoved(ni);
+ if (clean_resized) try coff.flushResized(ni);
+ break :task;
+ } else coff.mf.update_prog_node.completeOne();
+ }
+ }
+ if (coff.pending_uavs.count() > 0) return true;
+ for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
+ if (coff.mf.updates.items.len > 0) return true;
+ return false;
+}
+
+fn idleProgNode(
+ coff: *Coff,
+ tid: Zcu.PerThread.Id,
+ prog_node: std.Progress.Node,
+ node: Node,
+) std.Progress.Node {
+ var name: [std.Progress.Node.max_name_len]u8 = undefined;
+ return prog_node.start(name: switch (node) {
+ else => |tag| @tagName(tag),
+ .section => |si| std.mem.sliceTo(&si.get(coff).section_number.header(coff).name, 0),
+ .nav => |nmi| {
+ const ip = &coff.base.comp.zcu.?.intern_pool;
+ break :name ip.getNav(nmi.navIndex(coff)).fqn.toSlice(ip);
+ },
+ .uav => |umi| std.fmt.bufPrint(&name, "{f}", .{
+ Value.fromInterned(umi.uavValue(coff)).fmtValue(.{
+ .zcu = coff.base.comp.zcu.?,
+ .tid = tid,
+ }),
+ }) catch &name,
+ }, 0);
+}
+
+fn flushUav(
+ coff: *Coff,
+ pt: Zcu.PerThread,
+ umi: Node.UavMapIndex,
+ uav_align: InternPool.Alignment,
+ src_loc: Zcu.LazySrcLoc,
+) !void {
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+
+ const uav_val = umi.uavValue(coff);
+ const si = umi.symbol(coff);
+ const ni = ni: {
+ const sym = si.get(coff);
+ switch (sym.ni) {
+ .none => {
+ try coff.nodes.ensureUnusedCapacity(gpa, 1);
+ const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.data.node(coff), .{
+ .alignment = uav_align.toStdMem(),
+ .moved = true,
+ });
+ coff.nodes.appendAssumeCapacity(.{ .uav = umi });
+ sym.ni = ni;
+ sym.section_number = Symbol.Index.data.get(coff).section_number;
+ },
+ else => {
+ if (sym.ni.alignment(&coff.mf).order(uav_align.toStdMem()).compare(.gte)) return;
+ si.deleteLocationRelocs(coff);
+ },
+ }
+ assert(sym.loc_relocs == .none);
+ sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
+ break :ni sym.ni;
+ };
+
+ var nw: MappedFile.Node.Writer = undefined;
+ ni.writer(&coff.mf, gpa, &nw);
+ defer nw.deinit();
+ codegen.generateSymbol(
+ &coff.base,
+ pt,
+ src_loc,
+ .fromInterned(uav_val),
+ &nw.interface,
+ .{ .atom_index = @intFromEnum(si) },
+ ) catch |err| switch (err) {
+ error.WriteFailed => return error.OutOfMemory,
+ else => |e| return e,
+ };
+ si.get(coff).size = @intCast(nw.interface.end);
+ si.applyLocationRelocs(coff);
+}
+
+fn flushGlobal(coff: *Coff, pt: Zcu.PerThread, gmi: Node.GlobalMapIndex) !void {
+ const zcu = pt.zcu;
+ const comp = zcu.comp;
+ const gpa = zcu.gpa;
+ const gn = gmi.globalName(coff);
+ if (gn.lib_name.toSlice(coff)) |lib_name| {
+ const name = gn.name.toSlice(coff);
+ try coff.nodes.ensureUnusedCapacity(gpa, 4);
+ try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
+
+ const target_endian = coff.targetEndian();
+ const magic = coff.targetLoad(&coff.optionalHeaderStandardPtr().magic);
+ const addr_size: u64, const addr_align: std.mem.Alignment = switch (magic) {
+ _ => unreachable,
+ .PE32 => .{ 4, .@"4" },
+ .@"PE32+" => .{ 8, .@"8" },
+ };
+
+ const gop = try coff.import_table.entries.getOrPutAdapted(
+ gpa,
+ lib_name,
+ ImportTable.Adapter{ .coff = coff },
+ );
+ const import_hint_name_align: std.mem.Alignment = .@"2";
+ if (!gop.found_existing) {
+ errdefer _ = coff.import_table.entries.pop();
+ try coff.import_table.ni.resize(
+ &coff.mf,
+ gpa,
+ @sizeOf(std.coff.ImportDirectoryEntry) * (gop.index + 2),
+ );
+ const import_hint_name_table_len =
+ import_hint_name_align.forward(lib_name.len + ".dll".len + 1);
+ const idata_section_ni = Symbol.Index.idata.node(coff);
+ const import_lookup_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{
+ .size = addr_size * 2,
+ .alignment = addr_align,
+ .moved = true,
+ });
+ const import_address_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{
+ .size = addr_size * 2,
+ .alignment = addr_align,
+ .moved = true,
+ });
+ const import_address_table_si = coff.addSymbolAssumeCapacity();
+ {
+ const import_address_table_sym = import_address_table_si.get(coff);
+ import_address_table_sym.ni = import_address_table_ni;
+ assert(import_address_table_sym.loc_relocs == .none);
+ import_address_table_sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
+ import_address_table_sym.section_number = Symbol.Index.idata.get(coff).section_number;
+ }
+ const import_hint_name_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{
+ .size = import_hint_name_table_len,
+ .alignment = import_hint_name_align,
+ .moved = true,
+ });
+ gop.value_ptr.* = .{
+ .import_lookup_table_ni = import_lookup_table_ni,
+ .import_address_table_si = import_address_table_si,
+ .import_hint_name_table_ni = import_hint_name_table_ni,
+ .len = 0,
+ .hint_name_len = @intCast(import_hint_name_table_len),
+ };
+ const import_hint_name_slice = import_hint_name_table_ni.slice(&coff.mf);
+ @memcpy(import_hint_name_slice[0..lib_name.len], lib_name);
+ @memcpy(import_hint_name_slice[lib_name.len..][0..".dll".len], ".dll");
+ @memset(import_hint_name_slice[lib_name.len + ".dll".len ..], 0);
+ coff.nodes.appendAssumeCapacity(.{ .import_lookup_table = @enumFromInt(gop.index) });
+ coff.nodes.appendAssumeCapacity(.{ .import_address_table = @enumFromInt(gop.index) });
+ coff.nodes.appendAssumeCapacity(.{ .import_hint_name_table = @enumFromInt(gop.index) });
+
+ const import_directory_entries = coff.importDirectoryTableSlice()[gop.index..][0..2];
+ import_directory_entries.* = .{ .{
+ .import_lookup_table_rva = coff.computeNodeRva(import_lookup_table_ni),
+ .time_date_stamp = 0,
+ .forwarder_chain = 0,
+ .name_rva = coff.computeNodeRva(import_hint_name_table_ni),
+ .import_address_table_rva = coff.computeNodeRva(import_address_table_ni),
+ }, .{
+ .import_lookup_table_rva = 0,
+ .time_date_stamp = 0,
+ .forwarder_chain = 0,
+ .name_rva = 0,
+ .import_address_table_rva = 0,
+ } };
+ if (target_endian != native_endian)
+ std.mem.byteSwapAllFields([2]std.coff.ImportDirectoryEntry, import_directory_entries);
+ }
+ const import_symbol_index = gop.value_ptr.len;
+ gop.value_ptr.len = import_symbol_index + 1;
+ const new_symbol_table_size = addr_size * (import_symbol_index + 2);
+ const import_hint_name_index = gop.value_ptr.hint_name_len;
+ gop.value_ptr.hint_name_len = @intCast(
+ import_hint_name_align.forward(import_hint_name_index + 2 + name.len + 1),
+ );
+ try gop.value_ptr.import_lookup_table_ni.resize(&coff.mf, gpa, new_symbol_table_size);
+ const import_address_table_ni = gop.value_ptr.import_address_table_si.node(coff);
+ try import_address_table_ni.resize(&coff.mf, gpa, new_symbol_table_size);
+ try gop.value_ptr.import_hint_name_table_ni.resize(&coff.mf, gpa, gop.value_ptr.hint_name_len);
+ const import_lookup_slice = gop.value_ptr.import_lookup_table_ni.slice(&coff.mf);
+ const import_address_slice = import_address_table_ni.slice(&coff.mf);
+ const import_hint_name_slice = gop.value_ptr.import_hint_name_table_ni.slice(&coff.mf);
+ @memset(import_hint_name_slice[import_hint_name_index..][0..2], 0);
+ @memcpy(import_hint_name_slice[import_hint_name_index + 2 ..][0..name.len], name);
+ @memset(import_hint_name_slice[import_hint_name_index + 2 + name.len ..], 0);
+ const import_hint_name_rva =
+ coff.computeNodeRva(gop.value_ptr.import_hint_name_table_ni) + import_hint_name_index;
+ switch (magic) {
+ _ => unreachable,
+ inline .PE32, .@"PE32+" => |ct_magic| {
+ const Addr = switch (ct_magic) {
+ _ => comptime unreachable,
+ .PE32 => u32,
+ .@"PE32+" => u64,
+ };
+ const import_lookup_table: []Addr = @ptrCast(@alignCast(import_lookup_slice));
+ const import_address_table: []Addr = @ptrCast(@alignCast(import_address_slice));
+ const import_hint_name_rvas: [2]Addr = .{
+ std.mem.nativeTo(Addr, @intCast(import_hint_name_rva), target_endian),
+ std.mem.nativeTo(Addr, 0, target_endian),
+ };
+ import_lookup_table[import_symbol_index..][0..2].* = import_hint_name_rvas;
+ import_address_table[import_symbol_index..][0..2].* = import_hint_name_rvas;
+ },
+ }
+ const si = gmi.symbol(coff);
+ const sym = si.get(coff);
+ sym.section_number = Symbol.Index.text.get(coff).section_number;
+ assert(sym.loc_relocs == .none);
+ sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
+ switch (coff.targetLoad(&coff.headerPtr().machine)) {
+ else => |tag| @panic(@tagName(tag)),
+ .AMD64 => {
+ const init = [_]u8{ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 };
+ const target = &comp.root_mod.resolved_target.result;
+ const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.text.node(coff), .{
+ .alignment = switch (comp.root_mod.optimize_mode) {
+ .Debug,
+ .ReleaseSafe,
+ .ReleaseFast,
+ => target_util.defaultFunctionAlignment(target),
+ .ReleaseSmall => target_util.minFunctionAlignment(target),
+ }.toStdMem(),
+ .size = init.len,
+ });
+ @memcpy(ni.slice(&coff.mf)[0..init.len], &init);
+ sym.ni = ni;
+ sym.size = init.len;
+ try coff.addReloc(
+ si,
+ init.len - 4,
+ gop.value_ptr.import_address_table_si,
+ @intCast(addr_size * import_symbol_index),
+ .{ .AMD64 = .REL32 },
+ );
+ },
+ }
+ coff.nodes.appendAssumeCapacity(.{ .global = gmi });
+ sym.rva = coff.computeNodeRva(sym.ni);
+ si.applyLocationRelocs(coff);
+ }
+}
+
+fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+
+ const lazy = lmr.lazySymbol(coff);
+ const si = lmr.symbol(coff);
+ const ni = ni: {
+ const sym = si.get(coff);
+ switch (sym.ni) {
+ .none => {
+ try coff.nodes.ensureUnusedCapacity(gpa, 1);
+ const sec_si: Symbol.Index = switch (lazy.kind) {
+ .code => .text,
+ .const_data => .rdata,
+ };
+ const ni = try coff.mf.addLastChildNode(gpa, sec_si.node(coff), .{ .moved = true });
+ coff.nodes.appendAssumeCapacity(switch (lazy.kind) {
+ .code => .{ .lazy_code = @enumFromInt(lmr.index) },
+ .const_data => .{ .lazy_const_data = @enumFromInt(lmr.index) },
+ });
+ sym.ni = ni;
+ sym.section_number = sec_si.get(coff).section_number;
+ },
+ else => si.deleteLocationRelocs(coff),
+ }
+ assert(sym.loc_relocs == .none);
+ sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
+ break :ni sym.ni;
+ };
+
+ var required_alignment: InternPool.Alignment = .none;
+ var nw: MappedFile.Node.Writer = undefined;
+ ni.writer(&coff.mf, gpa, &nw);
+ defer nw.deinit();
+ try codegen.generateLazySymbol(
+ &coff.base,
+ pt,
+ Type.fromInterned(lazy.ty).srcLocOrNull(pt.zcu) orelse .unneeded,
+ lazy,
+ &required_alignment,
+ &nw.interface,
+ .none,
+ .{ .atom_index = @intFromEnum(si) },
+ );
+ si.get(coff).size = @intCast(nw.interface.end);
+ si.applyLocationRelocs(coff);
+}
+
+fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
+ switch (coff.getNode(ni)) {
+ .file,
+ .header,
+ .signature,
+ .coff_header,
+ .optional_header,
+ .data_directories,
+ .section_table,
+ => unreachable,
+ .section => |si| return coff.targetStore(
+ &si.get(coff).section_number.header(coff).pointer_to_raw_data,
+ @intCast(ni.fileLocation(&coff.mf, false).offset),
+ ),
+ .import_directory_table => coff.targetStore(
+ &coff.dataDirectoryPtr(.import_table).virtual_address,
+ coff.computeNodeRva(ni),
+ ),
+ .import_lookup_table => |import_index| coff.targetStore(
+ &coff.importDirectoryEntryPtr(import_index).import_lookup_table_rva,
+ coff.computeNodeRva(ni),
+ ),
+ .import_address_table => |import_index| {
+ const import_address_table_si = import_index.get(coff).import_address_table_si;
+ import_address_table_si.flushMoved(coff);
+ coff.targetStore(
+ &coff.importDirectoryEntryPtr(import_index).import_address_table_rva,
+ import_address_table_si.get(coff).rva,
+ );
+ },
+ .import_hint_name_table => |import_index| {
+ const target_endian = coff.targetEndian();
+ const magic = coff.targetLoad(&coff.optionalHeaderStandardPtr().magic);
+ const import_hint_name_rva = coff.computeNodeRva(ni);
+ coff.targetStore(
+ &coff.importDirectoryEntryPtr(import_index).name_rva,
+ import_hint_name_rva,
+ );
+ const import_entry = import_index.get(coff);
+ const import_lookup_slice = import_entry.import_lookup_table_ni.slice(&coff.mf);
+ const import_address_slice =
+ import_entry.import_address_table_si.node(coff).slice(&coff.mf);
+ const import_hint_name_slice = ni.slice(&coff.mf);
+ const import_hint_name_align = ni.alignment(&coff.mf);
+ var import_hint_name_index: u32 = 0;
+ for (0..import_entry.len) |import_symbol_index| {
+ import_hint_name_index = @intCast(import_hint_name_align.forward(
+ std.mem.indexOfScalarPos(
+ u8,
+ import_hint_name_slice,
+ import_hint_name_index,
+ 0,
+ ).? + 1,
+ ));
+ switch (magic) {
+ _ => unreachable,
+ inline .PE32, .@"PE32+" => |ct_magic| {
+ const Addr = switch (ct_magic) {
+ _ => comptime unreachable,
+ .PE32 => u32,
+ .@"PE32+" => u64,
+ };
+ const import_lookup_table: []Addr = @ptrCast(@alignCast(import_lookup_slice));
+ const import_address_table: []Addr = @ptrCast(@alignCast(import_address_slice));
+ const rva = std.mem.nativeTo(
+ Addr,
+ import_hint_name_rva + import_hint_name_index,
+ target_endian,
+ );
+ import_lookup_table[import_symbol_index] = rva;
+ import_address_table[import_symbol_index] = rva;
+ },
+ }
+ import_hint_name_index += 2;
+ }
+ },
+ inline .global,
+ .nav,
+ .uav,
+ .lazy_code,
+ .lazy_const_data,
+ => |mi| mi.symbol(coff).flushMoved(coff),
+ }
+ try ni.childrenMoved(coff.base.comp.gpa, &coff.mf);
+}
+
+fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
+ _, const size = ni.location(&coff.mf).resolve(&coff.mf);
+ switch (coff.getNode(ni)) {
+ .file => {},
+ .header => {
+ switch (coff.optionalHeaderPtr()) {
+ inline else => |optional_header| coff.targetStore(
+ &optional_header.size_of_headers,
+ @intCast(size),
+ ),
+ }
+ if (size > coff.section_table.items[0].get(coff).rva) try coff.virtualSlide(
+ 0,
+ std.mem.alignForward(
+ u32,
+ @intCast(size * 4),
+ coff.optionalHeaderField(.section_alignment),
+ ),
+ );
+ },
+ .signature, .coff_header, .optional_header, .data_directories => unreachable,
+ .section_table => {},
+ .section => |si| {
+ const sym = si.get(coff);
+ const section_index = sym.section_number.toIndex();
+ const section = &coff.sectionTableSlice()[section_index];
+ coff.targetStore(§ion.size_of_raw_data, @intCast(size));
+ if (size > coff.targetLoad(§ion.virtual_size)) {
+ const virtual_size = std.mem.alignForward(
+ u32,
+ @intCast(size * 4),
+ coff.optionalHeaderField(.section_alignment),
+ );
+ coff.targetStore(§ion.virtual_size, virtual_size);
+ try coff.virtualSlide(section_index + 1, sym.rva + virtual_size);
+ }
+ },
+ .import_directory_table => coff.targetStore(
+ &coff.dataDirectoryPtr(.import_table).size,
+ @intCast(size),
+ ),
+ .import_lookup_table,
+ .import_address_table,
+ .import_hint_name_table,
+ .global,
+ .nav,
+ .uav,
+ .lazy_code,
+ .lazy_const_data,
+ => {},
+ }
+}
+fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void {
+ var rva = start_rva;
+ for (
+ coff.section_table.items[start_section_index..],
+ coff.sectionTableSlice()[start_section_index..],
+ ) |section_si, *section| {
+ const section_sym = section_si.get(coff);
+ section_sym.rva = rva;
+ coff.targetStore(§ion.virtual_address, rva);
+ try section_sym.ni.childrenMoved(coff.base.comp.gpa, &coff.mf);
+ rva += coff.targetLoad(§ion.virtual_size);
+ }
+ switch (coff.optionalHeaderPtr()) {
+ inline else => |optional_header| coff.targetStore(
+ &optional_header.size_of_image,
+ @intCast(rva),
+ ),
+ }
+}
+
+pub fn updateExports(
+ coff: *Coff,
+ pt: Zcu.PerThread,
+ exported: Zcu.Exported,
+ export_indices: []const Zcu.Export.Index,
+) !void {
+ return coff.updateExportsInner(pt, exported, export_indices) catch |err| switch (err) {
+ error.OutOfMemory => error.OutOfMemory,
+ error.LinkFailure => error.AnalysisFail,
+ };
+}
+fn updateExportsInner(
+ coff: *Coff,
+ pt: Zcu.PerThread,
+ exported: Zcu.Exported,
+ export_indices: []const Zcu.Export.Index,
+) !void {
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
+
+ switch (exported) {
+ .nav => |nav| log.debug("updateExports({f})", .{ip.getNav(nav).fqn.fmt(ip)}),
+ .uav => |uav| log.debug("updateExports(@as({f}, {f}))", .{
+ Type.fromInterned(ip.typeOf(uav)).fmt(pt),
+ Value.fromInterned(uav).fmtValue(pt),
+ }),
+ }
+ try coff.symbol_table.ensureUnusedCapacity(gpa, export_indices.len);
+ const exported_si: Symbol.Index = switch (exported) {
+ .nav => |nav| try coff.navSymbol(zcu, nav),
+ .uav => |uav| @enumFromInt(switch (try coff.lowerUav(
+ pt,
+ uav,
+ Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),
+ export_indices[0].ptr(zcu).src,
+ )) {
+ .sym_index => |si| si,
+ .fail => |em| {
+ defer em.destroy(gpa);
+ return coff.base.comp.link_diags.fail("{s}", .{em.msg});
+ },
+ }),
+ };
+ while (try coff.idle(pt.tid)) {}
+ const exported_ni = exported_si.node(coff);
+ const exported_sym = exported_si.get(coff);
+ for (export_indices) |export_index| {
+ const @"export" = export_index.ptr(zcu);
+ const export_si = try coff.globalSymbol(@"export".opts.name.toSlice(ip), null);
+ const export_sym = export_si.get(coff);
+ export_sym.ni = exported_ni;
+ export_sym.rva = exported_sym.rva;
+ export_sym.size = exported_sym.size;
+ export_sym.section_number = exported_sym.section_number;
+ export_si.applyTargetRelocs(coff);
+ if (@"export".opts.name.eqlSlice("wWinMainCRTStartup", ip)) {
+ coff.entry_hack = exported_si;
+ coff.optionalHeaderStandardPtr().address_of_entry_point = exported_sym.rva;
+ }
+ }
+}
+
+pub fn deleteExport(coff: *Coff, exported: Zcu.Exported, name: InternPool.NullTerminatedString) void {
+ _ = coff;
+ _ = exported;
+ _ = name;
+}
+
+pub fn dump(coff: *Coff, tid: Zcu.PerThread.Id) void {
+ const w = std.debug.lockStderrWriter(&.{});
+ defer std.debug.unlockStderrWriter();
+ coff.printNode(tid, w, .root, 0) catch {};
+}
+
+pub fn printNode(
+ coff: *Coff,
+ tid: Zcu.PerThread.Id,
+ w: *std.Io.Writer,
+ ni: MappedFile.Node.Index,
+ indent: usize,
+) !void {
+ const node = coff.getNode(ni);
+ try w.splatByteAll(' ', indent);
+ try w.writeAll(@tagName(node));
+ switch (node) {
+ else => {},
+ .section => |si| try w.print("({s})", .{
+ std.mem.sliceTo(&si.get(coff).section_number.header(coff).name, 0),
+ }),
+ .import_lookup_table,
+ .import_address_table,
+ .import_hint_name_table,
+ => |import_index| try w.print("({s})", .{
+ std.mem.sliceTo(import_index.get(coff).import_hint_name_table_ni.sliceConst(&coff.mf), 0),
+ }),
+ .global => |gmi| {
+ const gn = gmi.globalName(coff);
+ try w.writeByte('(');
+ if (gn.lib_name.toSlice(coff)) |lib_name| try w.print("{s}.dll, ", .{lib_name});
+ try w.print("{s})", .{gn.name.toSlice(coff)});
+ },
+ .nav => |nmi| {
+ const zcu = coff.base.comp.zcu.?;
+ const ip = &zcu.intern_pool;
+ const nav = ip.getNav(nmi.navIndex(coff));
+ try w.print("({f}, {f})", .{
+ Type.fromInterned(nav.typeOf(ip)).fmt(.{ .zcu = zcu, .tid = tid }),
+ nav.fqn.fmt(ip),
+ });
+ },
+ .uav => |umi| {
+ const zcu = coff.base.comp.zcu.?;
+ const val: Value = .fromInterned(umi.uavValue(coff));
+ try w.print("({f}, {f})", .{
+ val.typeOf(zcu).fmt(.{ .zcu = zcu, .tid = tid }),
+ val.fmtValue(.{ .zcu = zcu, .tid = tid }),
+ });
+ },
+ inline .lazy_code, .lazy_const_data => |lmi| try w.print("({f})", .{
+ Type.fromInterned(lmi.lazySymbol(coff).ty).fmt(.{
+ .zcu = coff.base.comp.zcu.?,
+ .tid = tid,
+ }),
+ }),
+ }
+ {
+ const mf_node = &coff.mf.nodes.items[@intFromEnum(ni)];
+ const off, const size = mf_node.location().resolve(&coff.mf);
+ try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x}{s}{s}{s}{s}\n", .{
+ @intFromEnum(ni),
+ off,
+ size,
+ mf_node.flags.alignment.toByteUnits(),
+ if (mf_node.flags.fixed) " fixed" else "",
+ if (mf_node.flags.moved) " moved" else "",
+ if (mf_node.flags.resized) " resized" else "",
+ if (mf_node.flags.has_content) " has_content" else "",
+ });
+ }
+ var leaf = true;
+ var child_it = ni.children(&coff.mf);
+ while (child_it.next()) |child_ni| {
+ leaf = false;
+ try coff.printNode(tid, w, child_ni, indent + 1);
+ }
+ if (leaf) {
+ const file_loc = ni.fileLocation(&coff.mf, false);
+ if (file_loc.size == 0) return;
+ var address = file_loc.offset;
+ const line_len = 0x10;
+ var line_it = std.mem.window(
+ u8,
+ coff.mf.contents[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)],
+ line_len,
+ line_len,
+ );
+ while (line_it.next()) |line_bytes| : (address += line_len) {
+ try w.splatByteAll(' ', indent + 1);
+ try w.print("{x:0>8} ", .{address});
+ for (line_bytes) |byte| try w.print("{x:0>2} ", .{byte});
+ try w.splatByteAll(' ', 3 * (line_len - line_bytes.len) + 1);
+ for (line_bytes) |byte| try w.writeByte(if (std.ascii.isPrint(byte)) byte else '.');
+ try w.writeByte('\n');
+ }
+ }
+}
+
+const assert = std.debug.assert;
+const builtin = @import("builtin");
+const codegen = @import("../codegen.zig");
+const Compilation = @import("../Compilation.zig");
+const Coff = @This();
+const InternPool = @import("../InternPool.zig");
+const link = @import("../link.zig");
+const log = std.log.scoped(.link);
+const MappedFile = @import("MappedFile.zig");
+const native_endian = builtin.cpu.arch.endian();
+const std = @import("std");
+const target_util = @import("../target.zig");
+const Type = @import("../Type.zig");
+const Value = @import("../Value.zig");
+const Zcu = @import("../Zcu.zig");
diff --git a/src/link/Coff2.zig b/src/link/Coff2.zig
@@ -1,2193 +0,0 @@
-base: link.File,
-endian: std.builtin.Endian,
-mf: MappedFile,
-nodes: std.MultiArrayList(Node),
-import_table: ImportTable,
-strings: std.HashMapUnmanaged(
- u32,
- void,
- std.hash_map.StringIndexContext,
- std.hash_map.default_max_load_percentage,
-),
-string_bytes: std.ArrayList(u8),
-section_table: std.ArrayList(Symbol.Index),
-symbol_table: std.ArrayList(Symbol),
-globals: std.AutoArrayHashMapUnmanaged(GlobalName, Symbol.Index),
-global_pending_index: u32,
-navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, Symbol.Index),
-uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Symbol.Index),
-lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
- map: std.AutoArrayHashMapUnmanaged(InternPool.Index, Symbol.Index),
- pending_index: u32,
-}),
-pending_uavs: std.AutoArrayHashMapUnmanaged(Node.UavMapIndex, struct {
- alignment: InternPool.Alignment,
- src_loc: Zcu.LazySrcLoc,
-}),
-relocs: std.ArrayList(Reloc),
-/// This is hiding actual bugs with global symbols! Reconsider once they are implemented correctly.
-entry_hack: Symbol.Index,
-
-pub const default_file_alignment: u16 = 0x200;
-pub const default_size_of_stack_reserve: u32 = 0x1000000;
-pub const default_size_of_stack_commit: u32 = 0x1000;
-pub const default_size_of_heap_reserve: u32 = 0x100000;
-pub const default_size_of_heap_commit: u32 = 0x1000;
-
-/// This is the start of a Portable Executable (PE) file.
-/// It starts with a MS-DOS header followed by a MS-DOS stub program.
-/// This data does not change so we include it as follows in all binaries.
-///
-/// In this context,
-/// A "paragraph" is 16 bytes.
-/// A "page" is 512 bytes.
-/// A "long" is 4 bytes.
-/// A "word" is 2 bytes.
-pub const msdos_stub: [120]u8 = .{
- 'M', 'Z', // Magic number. Stands for Mark Zbikowski (designer of the MS-DOS executable format).
- 0x78, 0x00, // Number of bytes in the last page. This matches the size of this entire MS-DOS stub.
- 0x01, 0x00, // Number of pages.
- 0x00, 0x00, // Number of entries in the relocation table.
- 0x04, 0x00, // The number of paragraphs taken up by the header. 4 * 16 = 64, which matches the header size (all bytes before the MS-DOS stub program).
- 0x00, 0x00, // The number of paragraphs required by the program.
- 0x00, 0x00, // The number of paragraphs requested by the program.
- 0x00, 0x00, // Initial value for SS (relocatable segment address).
- 0x00, 0x00, // Initial value for SP.
- 0x00, 0x00, // Checksum.
- 0x00, 0x00, // Initial value for IP.
- 0x00, 0x00, // Initial value for CS (relocatable segment address).
- 0x40, 0x00, // Absolute offset to relocation table. 64 matches the header size (all bytes before the MS-DOS stub program).
- 0x00, 0x00, // Overlay number. Zero means this is the main executable.
-}
- // Reserved words.
- ++ .{ 0x00, 0x00 } ** 4
- // OEM-related fields.
- ++ .{
- 0x00, 0x00, // OEM identifier.
- 0x00, 0x00, // OEM information.
- }
- // Reserved words.
- ++ .{ 0x00, 0x00 } ** 10
- // Address of the PE header (a long). This matches the size of this entire MS-DOS stub, so that's the address of what's after this MS-DOS stub.
- ++ .{ 0x78, 0x00, 0x00, 0x00 }
- // What follows is a 16-bit x86 MS-DOS program of 7 instructions that prints the bytes after these instructions and then exits.
- ++ .{
- // Set the value of the data segment to the same value as the code segment.
- 0x0e, // push cs
- 0x1f, // pop ds
- // Set the DX register to the address of the message.
- // If you count all bytes of these 7 instructions you get 14, so that's the address of what's after these instructions.
- 0xba, 14, 0x00, // mov dx, 14
- // Set AH to the system call code for printing a message.
- 0xb4, 0x09, // mov ah, 0x09
- // Perform the system call to print the message.
- 0xcd, 0x21, // int 0x21
- // Set AH to 0x4c which is the system call code for exiting, and set AL to 0x01 which is the exit code.
- 0xb8, 0x01, 0x4c, // mov ax, 0x4c01
- // Peform the system call to exit the program with exit code 1.
- 0xcd, 0x21, // int 0x21
- }
- // Message to print.
- ++ "This program cannot be run in DOS mode.".*
- // Message terminators.
- ++ .{
- '$', // We do not pass a length to the print system call; the string is terminated by this character.
- 0x00, 0x00, // Terminating zero bytes.
- };
-
-pub const Node = union(enum) {
- file,
- header,
- signature,
- coff_header,
- optional_header,
- data_directories,
- section_table,
- section: Symbol.Index,
- import_directory_table,
- import_lookup_table: u32,
- import_address_table: u32,
- import_hint_name_table: u32,
- global: GlobalMapIndex,
- nav: NavMapIndex,
- uav: UavMapIndex,
- lazy_code: LazyMapRef.Index(.code),
- lazy_const_data: LazyMapRef.Index(.const_data),
-
- pub const GlobalMapIndex = enum(u32) {
- _,
-
- pub fn globalName(gmi: GlobalMapIndex, coff: *const Coff) GlobalName {
- return coff.globals.keys()[@intFromEnum(gmi)];
- }
-
- pub fn symbol(gmi: GlobalMapIndex, coff: *const Coff) Symbol.Index {
- return coff.globals.values()[@intFromEnum(gmi)];
- }
- };
-
- pub const NavMapIndex = enum(u32) {
- _,
-
- pub fn navIndex(nmi: NavMapIndex, coff: *const Coff) InternPool.Nav.Index {
- return coff.navs.keys()[@intFromEnum(nmi)];
- }
-
- pub fn symbol(nmi: NavMapIndex, coff: *const Coff) Symbol.Index {
- return coff.navs.values()[@intFromEnum(nmi)];
- }
- };
-
- pub const UavMapIndex = enum(u32) {
- _,
-
- pub fn uavValue(umi: UavMapIndex, coff: *const Coff) InternPool.Index {
- return coff.uavs.keys()[@intFromEnum(umi)];
- }
-
- pub fn symbol(umi: UavMapIndex, coff: *const Coff) Symbol.Index {
- return coff.uavs.values()[@intFromEnum(umi)];
- }
- };
-
- pub const LazyMapRef = struct {
- kind: link.File.LazySymbol.Kind,
- index: u32,
-
- pub fn Index(comptime kind: link.File.LazySymbol.Kind) type {
- return enum(u32) {
- _,
-
- pub fn ref(lmi: @This()) LazyMapRef {
- return .{ .kind = kind, .index = @intFromEnum(lmi) };
- }
-
- pub fn lazySymbol(lmi: @This(), coff: *const Coff) link.File.LazySymbol {
- return lmi.ref().lazySymbol(coff);
- }
-
- pub fn symbol(lmi: @This(), coff: *const Coff) Symbol.Index {
- return lmi.ref().symbol(coff);
- }
- };
- }
-
- pub fn lazySymbol(lmr: LazyMapRef, coff: *const Coff) link.File.LazySymbol {
- return .{ .kind = lmr.kind, .ty = coff.lazy.getPtrConst(lmr.kind).map.keys()[lmr.index] };
- }
-
- pub fn symbol(lmr: LazyMapRef, coff: *const Coff) Symbol.Index {
- return coff.lazy.getPtrConst(lmr.kind).map.values()[lmr.index];
- }
- };
-
- pub const Tag = @typeInfo(Node).@"union".tag_type.?;
-
- const known_count = @typeInfo(@TypeOf(known)).@"struct".fields.len;
- const known = known: {
- const Known = enum {
- file,
- header,
- signature,
- coff_header,
- optional_header,
- data_directories,
- section_table,
- };
- var mut_known: std.enums.EnumFieldStruct(Known, MappedFile.Node.Index, null) = undefined;
- for (@typeInfo(Known).@"enum".fields) |field|
- @field(mut_known, field.name) = @enumFromInt(field.value);
- break :known mut_known;
- };
-
- comptime {
- if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Node) == 8);
- }
-};
-
-pub const DataDirectory = enum {
- export_table,
- import_table,
- resorce_table,
- exception_table,
- certificate_table,
- base_relocation_table,
- debug,
- architecture,
- global_ptr,
- tls_table,
- load_config_table,
- bound_import,
- import_address_table,
- delay_import_descriptor,
- clr_runtime_header,
- reserved,
-};
-
-pub const ImportTable = struct {
- directory_table_ni: MappedFile.Node.Index,
- dlls: std.AutoArrayHashMapUnmanaged(void, Dll),
-
- pub const Dll = struct {
- import_lookup_table_ni: MappedFile.Node.Index,
- import_address_table_si: Symbol.Index,
- import_hint_name_table_ni: MappedFile.Node.Index,
- len: u32,
- hint_name_len: u32,
- };
-
- const Adapter = struct {
- coff: *Coff,
-
- pub fn eql(adapter: Adapter, lhs_key: []const u8, _: void, rhs_index: usize) bool {
- const coff = adapter.coff;
- const dll_name = coff.import_table.dlls.values()[rhs_index]
- .import_hint_name_table_ni.sliceConst(&coff.mf);
- return std.mem.startsWith(u8, dll_name, lhs_key) and
- std.mem.startsWith(u8, dll_name[lhs_key.len..], ".dll\x00");
- }
-
- pub fn hash(_: Adapter, key: []const u8) u32 {
- assert(std.mem.indexOfScalar(u8, key, 0) == null);
- return std.array_hash_map.hashString(key);
- }
- };
-};
-
-pub const String = enum(u32) {
- _,
-
- pub const Optional = enum(u32) {
- none = std.math.maxInt(u32),
- _,
-
- pub fn unwrap(os: String.Optional) ?String {
- return switch (os) {
- else => |s| @enumFromInt(@intFromEnum(s)),
- .none => null,
- };
- }
-
- pub fn toSlice(os: String.Optional, coff: *Coff) ?[:0]const u8 {
- return (os.unwrap() orelse return null).toSlice(coff);
- }
- };
-
- pub fn toSlice(s: String, coff: *Coff) [:0]const u8 {
- const slice = coff.string_bytes.items[@intFromEnum(s)..];
- return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0];
- }
-
- pub fn toOptional(s: String) String.Optional {
- return @enumFromInt(@intFromEnum(s));
- }
-};
-
-pub const GlobalName = struct { name: String, lib_name: String.Optional };
-
-pub const Symbol = struct {
- ni: MappedFile.Node.Index,
- rva: u32,
- size: u32,
- /// Relocations contained within this symbol
- loc_relocs: Reloc.Index,
- /// Relocations targeting this symbol
- target_relocs: Reloc.Index,
- section_number: SectionNumber,
- data_directory: ?DataDirectory,
- unused0: u32 = 0,
- unused1: u32 = 0,
-
- pub const SectionNumber = enum(i16) {
- UNDEFINED = 0,
- ABSOLUTE = -1,
- DEBUG = -2,
- _,
-
- fn toIndex(sn: SectionNumber) u15 {
- return @intCast(@intFromEnum(sn) - 1);
- }
-
- pub fn symbol(sn: SectionNumber, coff: *const Coff) Symbol.Index {
- return coff.section_table.items[sn.toIndex()];
- }
-
- pub fn header(sn: SectionNumber, coff: *Coff) *std.coff.SectionHeader {
- return &coff.sectionTableSlice()[sn.toIndex()];
- }
- };
-
- pub const Index = enum(u32) {
- null,
- data,
- idata,
- rdata,
- text,
- _,
-
- const known_count = @typeInfo(Index).@"enum".fields.len;
-
- pub fn get(si: Symbol.Index, coff: *Coff) *Symbol {
- return &coff.symbol_table.items[@intFromEnum(si)];
- }
-
- pub fn node(si: Symbol.Index, coff: *Coff) MappedFile.Node.Index {
- const ni = si.get(coff).ni;
- assert(ni != .none);
- return ni;
- }
-
- pub fn flushMoved(si: Symbol.Index, coff: *Coff) void {
- const sym = si.get(coff);
- sym.rva = coff.computeNodeRva(sym.ni);
- if (si == coff.entry_hack)
- coff.targetStore(&coff.optionalHeaderStandardPtr().address_of_entry_point, sym.rva);
- si.applyLocationRelocs(coff);
- si.applyTargetRelocs(coff);
- }
-
- pub fn applyLocationRelocs(si: Symbol.Index, coff: *Coff) void {
- for (coff.relocs.items[@intFromEnum(si.get(coff).loc_relocs)..]) |*reloc| {
- if (reloc.loc != si) break;
- reloc.apply(coff);
- }
- }
-
- pub fn applyTargetRelocs(si: Symbol.Index, coff: *Coff) void {
- var ri = si.get(coff).target_relocs;
- while (ri != .none) {
- const reloc = ri.get(coff);
- assert(reloc.target == si);
- reloc.apply(coff);
- ri = reloc.next;
- }
- }
-
- pub fn deleteLocationRelocs(si: Symbol.Index, coff: *Coff) void {
- const sym = si.get(coff);
- for (coff.relocs.items[@intFromEnum(sym.loc_relocs)..]) |*reloc| {
- if (reloc.loc != si) break;
- reloc.delete(coff);
- }
- sym.loc_relocs = .none;
- }
- };
-
- comptime {
- if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Symbol) == 32);
- }
-};
-
-pub const Reloc = extern struct {
- type: Reloc.Type,
- prev: Reloc.Index,
- next: Reloc.Index,
- loc: Symbol.Index,
- target: Symbol.Index,
- unused: u32,
- offset: u64,
- addend: i64,
-
- pub const Type = extern union {
- AMD64: std.coff.IMAGE.REL.AMD64,
- ARM: std.coff.IMAGE.REL.ARM,
- ARM64: std.coff.IMAGE.REL.ARM64,
- SH: std.coff.IMAGE.REL.SH,
- PPC: std.coff.IMAGE.REL.PPC,
- I386: std.coff.IMAGE.REL.I386,
- IA64: std.coff.IMAGE.REL.IA64,
- MIPS: std.coff.IMAGE.REL.MIPS,
- M32R: std.coff.IMAGE.REL.M32R,
- };
-
- pub const Index = enum(u32) {
- none = std.math.maxInt(u32),
- _,
-
- pub fn get(si: Reloc.Index, coff: *Coff) *Reloc {
- return &coff.relocs.items[@intFromEnum(si)];
- }
- };
-
- pub fn apply(reloc: *const Reloc, coff: *Coff) void {
- const loc_sym = reloc.loc.get(coff);
- switch (loc_sym.ni) {
- .none => return,
- else => |ni| if (ni.hasMoved(&coff.mf)) return,
- }
- const target_sym = reloc.target.get(coff);
- switch (target_sym.ni) {
- .none => return,
- else => |ni| if (ni.hasMoved(&coff.mf)) return,
- }
- const loc_slice = loc_sym.ni.slice(&coff.mf)[@intCast(reloc.offset)..];
- const target_rva = target_sym.rva +% @as(u64, @bitCast(reloc.addend));
- const target_endian = coff.targetEndian();
- switch (coff.targetLoad(&coff.headerPtr().machine)) {
- else => |machine| @panic(@tagName(machine)),
- .AMD64 => switch (reloc.type.AMD64) {
- else => |kind| @panic(@tagName(kind)),
- .ABSOLUTE => {},
- .ADDR64 => std.mem.writeInt(
- u64,
- loc_slice[0..8],
- coff.optionalHeaderField(.image_base) + target_rva,
- target_endian,
- ),
- .ADDR32 => std.mem.writeInt(
- u32,
- loc_slice[0..4],
- @intCast(coff.optionalHeaderField(.image_base) + target_rva),
- target_endian,
- ),
- .ADDR32NB => std.mem.writeInt(
- u32,
- loc_slice[0..4],
- @intCast(target_rva),
- target_endian,
- ),
- .REL32 => std.mem.writeInt(
- i32,
- loc_slice[0..4],
- @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 4)))),
- target_endian,
- ),
- .REL32_1 => std.mem.writeInt(
- i32,
- loc_slice[0..4],
- @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 5)))),
- target_endian,
- ),
- .REL32_2 => std.mem.writeInt(
- i32,
- loc_slice[0..4],
- @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 6)))),
- target_endian,
- ),
- .REL32_3 => std.mem.writeInt(
- i32,
- loc_slice[0..4],
- @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 7)))),
- target_endian,
- ),
- .REL32_4 => std.mem.writeInt(
- i32,
- loc_slice[0..4],
- @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 8)))),
- target_endian,
- ),
- .REL32_5 => std.mem.writeInt(
- i32,
- loc_slice[0..4],
- @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 9)))),
- target_endian,
- ),
- },
- .I386 => switch (reloc.type.I386) {
- else => |kind| @panic(@tagName(kind)),
- .ABSOLUTE => {},
- .DIR16 => std.mem.writeInt(
- u16,
- loc_slice[0..2],
- @intCast(coff.optionalHeaderField(.image_base) + target_rva),
- target_endian,
- ),
- .REL16 => std.mem.writeInt(
- i16,
- loc_slice[0..2],
- @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 2)))),
- target_endian,
- ),
- .DIR32 => std.mem.writeInt(
- u32,
- loc_slice[0..4],
- @intCast(coff.optionalHeaderField(.image_base) + target_rva),
- target_endian,
- ),
- .DIR32NB => std.mem.writeInt(
- u32,
- loc_slice[0..4],
- @intCast(target_rva),
- target_endian,
- ),
- .REL32 => std.mem.writeInt(
- i32,
- loc_slice[0..4],
- @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 4)))),
- target_endian,
- ),
- },
- }
- }
-
- pub fn delete(reloc: *Reloc, coff: *Coff) void {
- switch (reloc.prev) {
- .none => {
- const target = reloc.target.get(coff);
- assert(target.target_relocs.get(coff) == reloc);
- target.target_relocs = reloc.next;
- },
- else => |prev| prev.get(coff).next = reloc.next,
- }
- switch (reloc.next) {
- .none => {},
- else => |next| next.get(coff).prev = reloc.prev,
- }
- reloc.* = undefined;
- }
-
- comptime {
- if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Reloc) == 40);
- }
-};
-
-pub fn open(
- arena: std.mem.Allocator,
- comp: *Compilation,
- path: std.Build.Cache.Path,
- options: link.File.OpenOptions,
-) !*Coff {
- return create(arena, comp, path, options);
-}
-pub fn createEmpty(
- arena: std.mem.Allocator,
- comp: *Compilation,
- path: std.Build.Cache.Path,
- options: link.File.OpenOptions,
-) !*Coff {
- return create(arena, comp, path, options);
-}
-fn create(
- arena: std.mem.Allocator,
- comp: *Compilation,
- path: std.Build.Cache.Path,
- options: link.File.OpenOptions,
-) !*Coff {
- const target = &comp.root_mod.resolved_target.result;
- assert(target.ofmt == .coff);
- const is_image = switch (comp.config.output_mode) {
- .Exe => true,
- .Lib => switch (comp.config.link_mode) {
- .static => false,
- .dynamic => true,
- },
- .Obj => false,
- };
- const machine = target.toCoffMachine();
- const timestamp: u32 = if (options.repro) 0 else @truncate(@as(u64, @bitCast(std.time.timestamp())));
- const major_subsystem_version = options.major_subsystem_version orelse 6;
- const minor_subsystem_version = options.minor_subsystem_version orelse 0;
- const magic: std.coff.OptionalHeader.Magic = switch (target.ptrBitWidth()) {
- 0...32 => .PE32,
- 33...64 => .@"PE32+",
- else => return error.UnsupportedCOFFArchitecture,
- };
- const section_align: std.mem.Alignment = switch (machine) {
- .AMD64, .I386 => @enumFromInt(12),
- .SH3, .SH3DSP, .SH4, .SH5 => @enumFromInt(12),
- .MIPS16, .MIPSFPU, .MIPSFPU16, .WCEMIPSV2 => @enumFromInt(12),
- .POWERPC, .POWERPCFP => @enumFromInt(12),
- .ALPHA, .ALPHA64 => @enumFromInt(13),
- .IA64 => @enumFromInt(13),
- .ARM => @enumFromInt(12),
- else => return error.UnsupportedCOFFArchitecture,
- };
-
- const coff = try arena.create(Coff);
- const file = try path.root_dir.handle.createFile(path.sub_path, .{
- .read = true,
- .mode = link.File.determineMode(comp.config.output_mode, comp.config.link_mode),
- });
- errdefer file.close();
- coff.* = .{
- .base = .{
- .tag = .coff2,
-
- .comp = comp,
- .emit = path,
-
- .file = file,
- .gc_sections = false,
- .print_gc_sections = false,
- .build_id = .none,
- .allow_shlib_undefined = false,
- .stack_size = 0,
- },
- .endian = target.cpu.arch.endian(),
- .mf = try .init(file, comp.gpa),
- .nodes = .empty,
- .import_table = .{
- .directory_table_ni = .none,
- .dlls = .empty,
- },
- .strings = .empty,
- .string_bytes = .empty,
- .section_table = .empty,
- .symbol_table = .empty,
- .globals = .empty,
- .global_pending_index = 0,
- .navs = .empty,
- .uavs = .empty,
- .lazy = .initFill(.{
- .map = .empty,
- .pending_index = 0,
- }),
- .pending_uavs = .empty,
- .relocs = .empty,
- .entry_hack = .null,
- };
- errdefer coff.deinit();
-
- try coff.initHeaders(
- is_image,
- machine,
- timestamp,
- major_subsystem_version,
- minor_subsystem_version,
- magic,
- section_align,
- );
- return coff;
-}
-
-pub fn deinit(coff: *Coff) void {
- const gpa = coff.base.comp.gpa;
- coff.mf.deinit(gpa);
- coff.nodes.deinit(gpa);
- coff.import_table.dlls.deinit(gpa);
- coff.strings.deinit(gpa);
- coff.string_bytes.deinit(gpa);
- coff.section_table.deinit(gpa);
- coff.symbol_table.deinit(gpa);
- coff.globals.deinit(gpa);
- coff.navs.deinit(gpa);
- coff.uavs.deinit(gpa);
- for (&coff.lazy.values) |*lazy| lazy.map.deinit(gpa);
- coff.pending_uavs.deinit(gpa);
- coff.relocs.deinit(gpa);
- coff.* = undefined;
-}
-
-fn initHeaders(
- coff: *Coff,
- is_image: bool,
- machine: std.coff.IMAGE.FILE.MACHINE,
- timestamp: u32,
- major_subsystem_version: u16,
- minor_subsystem_version: u16,
- magic: std.coff.OptionalHeader.Magic,
- section_align: std.mem.Alignment,
-) !void {
- const comp = coff.base.comp;
- const gpa = comp.gpa;
- const file_align: std.mem.Alignment = comptime .fromByteUnits(default_file_alignment);
- const target_endian = coff.targetEndian();
-
- const optional_header_size: u16 = if (is_image) switch (magic) {
- _ => unreachable,
- inline else => |ct_magic| @sizeOf(@field(std.coff.OptionalHeader, @tagName(ct_magic))),
- } else 0;
- const data_directories_len = @typeInfo(DataDirectory).@"enum".fields.len;
- const data_directories_size: u16 = if (is_image)
- @sizeOf(std.coff.ImageDataDirectory) * data_directories_len
- else
- 0;
-
- try coff.nodes.ensureTotalCapacity(gpa, Node.known_count);
- coff.nodes.appendAssumeCapacity(.file);
-
- const header_ni = Node.known.header;
- assert(header_ni == try coff.mf.addOnlyChildNode(gpa, .root, .{
- .alignment = coff.mf.flags.block_size,
- .fixed = true,
- }));
- coff.nodes.appendAssumeCapacity(.header);
-
- const signature_ni = Node.known.signature;
- assert(signature_ni == try coff.mf.addOnlyChildNode(gpa, header_ni, .{
- .size = (if (is_image) msdos_stub.len else 0) + "PE\x00\x00".len,
- .alignment = .@"4",
- .fixed = true,
- }));
- coff.nodes.appendAssumeCapacity(.signature);
- {
- const signature_slice = signature_ni.slice(&coff.mf);
- if (is_image) @memcpy(signature_slice[0..msdos_stub.len], &msdos_stub);
- @memcpy(signature_slice[signature_slice.len - 4 ..], "PE\x00\x00");
- }
-
- const coff_header_ni = Node.known.coff_header;
- assert(coff_header_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
- .size = @sizeOf(std.coff.Header),
- .alignment = .@"4",
- .fixed = true,
- }));
- coff.nodes.appendAssumeCapacity(.coff_header);
- {
- const coff_header: *std.coff.Header = @ptrCast(@alignCast(coff_header_ni.slice(&coff.mf)));
- coff_header.* = .{
- .machine = machine,
- .number_of_sections = 0,
- .time_date_stamp = timestamp,
- .pointer_to_symbol_table = 0,
- .number_of_symbols = 0,
- .size_of_optional_header = optional_header_size + data_directories_size,
- .flags = .{
- .RELOCS_STRIPPED = is_image,
- .EXECUTABLE_IMAGE = is_image,
- .DEBUG_STRIPPED = true,
- .@"32BIT_MACHINE" = magic == .PE32,
- .LARGE_ADDRESS_AWARE = magic == .@"PE32+",
- .DLL = comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic,
- },
- };
- if (target_endian != native_endian) std.mem.byteSwapAllFields(std.coff.Header, coff_header);
- }
-
- const optional_header_ni = Node.known.optional_header;
- assert(optional_header_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
- .size = optional_header_size,
- .alignment = .@"4",
- .fixed = true,
- }));
- coff.nodes.appendAssumeCapacity(.optional_header);
- if (is_image) switch (magic) {
- _ => unreachable,
- .PE32 => {
- const optional_header: *std.coff.OptionalHeader.PE32 =
- @ptrCast(@alignCast(optional_header_ni.slice(&coff.mf)));
- optional_header.* = .{
- .standard = .{
- .magic = .PE32,
- .major_linker_version = 0,
- .minor_linker_version = 0,
- .size_of_code = 0,
- .size_of_initialized_data = 0,
- .size_of_uninitialized_data = 0,
- .address_of_entry_point = 0,
- .base_of_code = 0,
- },
- .base_of_data = 0,
- .image_base = switch (coff.base.comp.config.output_mode) {
- .Exe => 0x400000,
- .Lib => switch (coff.base.comp.config.link_mode) {
- .static => 0,
- .dynamic => 0x10000000,
- },
- .Obj => 0,
- },
- .section_alignment = @intCast(section_align.toByteUnits()),
- .file_alignment = @intCast(file_align.toByteUnits()),
- .major_operating_system_version = 6,
- .minor_operating_system_version = 0,
- .major_image_version = 0,
- .minor_image_version = 0,
- .major_subsystem_version = major_subsystem_version,
- .minor_subsystem_version = minor_subsystem_version,
- .win32_version_value = 0,
- .size_of_image = 0,
- .size_of_headers = 0,
- .checksum = 0,
- .subsystem = .WINDOWS_CUI,
- .dll_flags = .{
- .HIGH_ENTROPY_VA = true,
- .DYNAMIC_BASE = true,
- .TERMINAL_SERVER_AWARE = true,
- .NX_COMPAT = true,
- },
- .size_of_stack_reserve = default_size_of_stack_reserve,
- .size_of_stack_commit = default_size_of_stack_commit,
- .size_of_heap_reserve = default_size_of_heap_reserve,
- .size_of_heap_commit = default_size_of_heap_commit,
- .loader_flags = 0,
- .number_of_rva_and_sizes = data_directories_len,
- };
- if (target_endian != native_endian)
- std.mem.byteSwapAllFields(std.coff.OptionalHeader.PE32, optional_header);
- },
- .@"PE32+" => {
- const header: *std.coff.OptionalHeader.@"PE32+" =
- @ptrCast(@alignCast(optional_header_ni.slice(&coff.mf)));
- header.* = .{
- .standard = .{
- .magic = .@"PE32+",
- .major_linker_version = 0,
- .minor_linker_version = 0,
- .size_of_code = 0,
- .size_of_initialized_data = 0,
- .size_of_uninitialized_data = 0,
- .address_of_entry_point = 0,
- .base_of_code = 0,
- },
- .image_base = switch (coff.base.comp.config.output_mode) {
- .Exe => 0x140000000,
- .Lib => switch (coff.base.comp.config.link_mode) {
- .static => 0,
- .dynamic => 0x180000000,
- },
- .Obj => 0,
- },
- .section_alignment = @intCast(section_align.toByteUnits()),
- .file_alignment = @intCast(file_align.toByteUnits()),
- .major_operating_system_version = 6,
- .minor_operating_system_version = 0,
- .major_image_version = 0,
- .minor_image_version = 0,
- .major_subsystem_version = major_subsystem_version,
- .minor_subsystem_version = minor_subsystem_version,
- .win32_version_value = 0,
- .size_of_image = 0,
- .size_of_headers = 0,
- .checksum = 0,
- .subsystem = .WINDOWS_CUI,
- .dll_flags = .{
- .HIGH_ENTROPY_VA = true,
- .DYNAMIC_BASE = true,
- .TERMINAL_SERVER_AWARE = true,
- .NX_COMPAT = true,
- },
- .size_of_stack_reserve = default_size_of_stack_reserve,
- .size_of_stack_commit = default_size_of_stack_commit,
- .size_of_heap_reserve = default_size_of_heap_reserve,
- .size_of_heap_commit = default_size_of_heap_commit,
- .loader_flags = 0,
- .number_of_rva_and_sizes = data_directories_len,
- };
- if (target_endian != native_endian)
- std.mem.byteSwapAllFields(std.coff.OptionalHeader.@"PE32+", header);
- },
- };
-
- const data_directories_ni = Node.known.data_directories;
- assert(data_directories_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
- .size = data_directories_size,
- .alignment = .@"4",
- .fixed = true,
- }));
- coff.nodes.appendAssumeCapacity(.data_directories);
- {
- const data_directories: *[data_directories_len]std.coff.ImageDataDirectory =
- @ptrCast(@alignCast(data_directories_ni.slice(&coff.mf)));
- @memset(data_directories, .{ .virtual_address = 0, .size = 0 });
- if (target_endian != native_endian) for (data_directories) |*data_directory|
- std.mem.byteSwapAllFields(std.coff.ImageDataDirectory, data_directory);
- }
-
- const section_table_ni = Node.known.section_table;
- assert(section_table_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
- .alignment = .@"4",
- .fixed = true,
- }));
- coff.nodes.appendAssumeCapacity(.section_table);
-
- assert(coff.nodes.len == Node.known_count);
-
- try coff.symbol_table.ensureTotalCapacity(gpa, Symbol.Index.known_count);
- coff.symbol_table.addOneAssumeCapacity().* = .{
- .ni = .none,
- .rva = 0,
- .size = 0,
- .loc_relocs = .none,
- .target_relocs = .none,
- .section_number = .UNDEFINED,
- .data_directory = null,
- };
- assert(try coff.addSection(".data", null, .{
- .CNT_INITIALIZED_DATA = true,
- .MEM_READ = true,
- .MEM_WRITE = true,
- }) == .data);
- assert(try coff.addSection(".idata", .import_table, .{
- .CNT_INITIALIZED_DATA = true,
- .MEM_READ = true,
- }) == .idata);
- assert(try coff.addSection(".rdata", null, .{
- .CNT_INITIALIZED_DATA = true,
- .MEM_READ = true,
- }) == .rdata);
- assert(try coff.addSection(".text", null, .{
- .CNT_CODE = true,
- .MEM_EXECUTE = true,
- .MEM_READ = true,
- }) == .text);
- coff.import_table.directory_table_ni = try coff.mf.addLastChildNode(
- gpa,
- Symbol.Index.idata.node(coff),
- .{
- .alignment = .@"4",
- .fixed = true,
- },
- );
- coff.nodes.appendAssumeCapacity(.import_directory_table);
- assert(coff.symbol_table.items.len == Symbol.Index.known_count);
-}
-
-fn getNode(coff: *const Coff, ni: MappedFile.Node.Index) Node {
- return coff.nodes.get(@intFromEnum(ni));
-}
-fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {
- var section_offset: u32 = 0;
- var parent_ni = ni;
- while (true) {
- assert(parent_ni != .none);
- switch (coff.getNode(parent_ni)) {
- else => {},
- .section => |si| return si.get(coff).rva + section_offset,
- }
- const parent_offset, _ = parent_ni.location(&coff.mf).resolve(&coff.mf);
- section_offset += @intCast(parent_offset);
- parent_ni = parent_ni.parent(&coff.mf);
- }
-}
-
-pub inline fn targetEndian(coff: *const Coff) std.builtin.Endian {
- return coff.endian;
-}
-fn targetLoad(coff: *const Coff, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {
- const Child = @typeInfo(@TypeOf(ptr)).pointer.child;
- return switch (@typeInfo(Child)) {
- else => @compileError(@typeName(Child)),
- .int => std.mem.toNative(Child, ptr.*, coff.targetEndian()),
- .@"enum" => |@"enum"| @enumFromInt(coff.targetLoad(@as(*@"enum".tag_type, @ptrCast(ptr)))),
- .@"struct" => |@"struct"| @bitCast(
- coff.targetLoad(@as(*@"struct".backing_integer.?, @ptrCast(ptr))),
- ),
- };
-}
-fn targetStore(coff: *const Coff, ptr: anytype, val: @typeInfo(@TypeOf(ptr)).pointer.child) void {
- const Child = @typeInfo(@TypeOf(ptr)).pointer.child;
- return switch (@typeInfo(Child)) {
- else => @compileError(@typeName(Child)),
- .int => ptr.* = std.mem.nativeTo(Child, val, coff.targetEndian()),
- .@"enum" => |@"enum"| coff.targetStore(
- @as(*@"enum".tag_type, @ptrCast(ptr)),
- @intFromEnum(val),
- ),
- .@"struct" => |@"struct"| coff.targetStore(
- @as(*@"struct".backing_integer.?, @ptrCast(ptr)),
- @bitCast(val),
- ),
- };
-}
-
-pub fn headerPtr(coff: *Coff) *std.coff.Header {
- return @ptrCast(@alignCast(Node.known.coff_header.slice(&coff.mf)));
-}
-
-pub fn optionalHeaderStandardPtr(coff: *Coff) *std.coff.OptionalHeader {
- return @ptrCast(@alignCast(
- Node.known.optional_header.slice(&coff.mf)[0..@sizeOf(std.coff.OptionalHeader)],
- ));
-}
-
-pub const OptionalHeaderPtr = union(std.coff.OptionalHeader.Magic) {
- PE32: *std.coff.OptionalHeader.PE32,
- @"PE32+": *std.coff.OptionalHeader.@"PE32+",
-};
-pub fn optionalHeaderPtr(coff: *Coff) OptionalHeaderPtr {
- const slice = Node.known.optional_header.slice(&coff.mf);
- return switch (coff.targetLoad(&coff.optionalHeaderStandardPtr().magic)) {
- _ => unreachable,
- inline else => |magic| @unionInit(
- OptionalHeaderPtr,
- @tagName(magic),
- @ptrCast(@alignCast(slice)),
- ),
- };
-}
-pub fn optionalHeaderField(
- coff: *Coff,
- comptime field: std.meta.FieldEnum(std.coff.OptionalHeader.@"PE32+"),
-) @FieldType(std.coff.OptionalHeader.@"PE32+", @tagName(field)) {
- return switch (coff.optionalHeaderPtr()) {
- inline else => |optional_header| coff.targetLoad(&@field(optional_header, @tagName(field))),
- };
-}
-
-pub fn dataDirectoriesSlice(coff: *Coff) []std.coff.ImageDataDirectory {
- return @ptrCast(@alignCast(Node.known.data_directories.slice(&coff.mf)));
-}
-
-pub fn sectionTableSlice(coff: *Coff) []std.coff.SectionHeader {
- return @ptrCast(@alignCast(Node.known.section_table.slice(&coff.mf)));
-}
-
-fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index {
- defer coff.symbol_table.addOneAssumeCapacity().* = .{
- .ni = .none,
- .rva = 0,
- .size = 0,
- .loc_relocs = .none,
- .target_relocs = .none,
- .section_number = .UNDEFINED,
- .data_directory = null,
- };
- return @enumFromInt(coff.symbol_table.items.len);
-}
-
-fn initSymbolAssumeCapacity(coff: *Coff) !Symbol.Index {
- const si = coff.addSymbolAssumeCapacity();
- return si;
-}
-
-fn getOrPutString(coff: *Coff, string: []const u8) !String {
- const gpa = coff.base.comp.gpa;
- try coff.string_bytes.ensureUnusedCapacity(gpa, string.len + 1);
- const gop = try coff.strings.getOrPutContextAdapted(
- gpa,
- string,
- std.hash_map.StringIndexAdapter{ .bytes = &coff.string_bytes },
- .{ .bytes = &coff.string_bytes },
- );
- if (!gop.found_existing) {
- gop.key_ptr.* = @intCast(coff.string_bytes.items.len);
- gop.value_ptr.* = {};
- coff.string_bytes.appendSliceAssumeCapacity(string);
- coff.string_bytes.appendAssumeCapacity(0);
- }
- return @enumFromInt(gop.key_ptr.*);
-}
-
-fn getOrPutOptionalString(coff: *Coff, string: ?[]const u8) !String.Optional {
- return (try coff.getOrPutString(string orelse return .none)).toOptional();
-}
-
-pub fn globalSymbol(coff: *Coff, name: []const u8, lib_name: ?[]const u8) !Symbol.Index {
- const gpa = coff.base.comp.gpa;
- try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
- const sym_gop = try coff.globals.getOrPut(gpa, .{
- .name = try coff.getOrPutString(name),
- .lib_name = try coff.getOrPutOptionalString(lib_name),
- });
- if (!sym_gop.found_existing) {
- sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
- coff.base.comp.link_synth_prog_node.increaseEstimatedTotalItems(1);
- }
- return sym_gop.value_ptr.*;
-}
-
-fn navMapIndex(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavMapIndex {
- const gpa = zcu.gpa;
- try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
- const sym_gop = try coff.navs.getOrPut(gpa, nav_index);
- if (!sym_gop.found_existing) sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
- return @enumFromInt(sym_gop.index);
-}
-pub fn navSymbol(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbol.Index {
- const ip = &zcu.intern_pool;
- const nav = ip.getNav(nav_index);
- if (nav.getExtern(ip)) |@"extern"| return coff.globalSymbol(
- @"extern".name.toSlice(ip),
- @"extern".lib_name.toSlice(ip),
- );
- const nmi = try coff.navMapIndex(zcu, nav_index);
- return nmi.symbol(coff);
-}
-
-fn uavMapIndex(coff: *Coff, uav_val: InternPool.Index) !Node.UavMapIndex {
- const gpa = coff.base.comp.gpa;
- try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
- const sym_gop = try coff.uavs.getOrPut(gpa, uav_val);
- if (!sym_gop.found_existing) sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
- return @enumFromInt(sym_gop.index);
-}
-pub fn uavSymbol(coff: *Coff, uav_val: InternPool.Index) !Symbol.Index {
- const umi = try coff.uavMapIndex(uav_val);
- return umi.symbol(coff);
-}
-
-pub fn lazySymbol(coff: *Coff, lazy: link.File.LazySymbol) !Symbol.Index {
- const gpa = coff.base.comp.gpa;
- try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
- const sym_gop = try coff.lazy.getPtr(lazy.kind).map.getOrPut(gpa, lazy.ty);
- if (!sym_gop.found_existing) {
- sym_gop.value_ptr.* = try coff.initSymbolAssumeCapacity();
- coff.base.comp.link_synth_prog_node.increaseEstimatedTotalItems(1);
- }
- return sym_gop.value_ptr.*;
-}
-
-pub fn getNavVAddr(
- coff: *Coff,
- pt: Zcu.PerThread,
- nav: InternPool.Nav.Index,
- reloc_info: link.File.RelocInfo,
-) !u64 {
- return coff.getVAddr(reloc_info, try coff.navSymbol(pt.zcu, nav));
-}
-
-pub fn getUavVAddr(
- coff: *Coff,
- uav: InternPool.Index,
- reloc_info: link.File.RelocInfo,
-) !u64 {
- return coff.getVAddr(reloc_info, try coff.uavSymbol(uav));
-}
-
-pub fn getVAddr(coff: *Coff, reloc_info: link.File.RelocInfo, target_si: Symbol.Index) !u64 {
- try coff.addReloc(
- @enumFromInt(reloc_info.parent.atom_index),
- reloc_info.offset,
- target_si,
- reloc_info.addend,
- switch (coff.targetLoad(&coff.headerPtr().machine)) {
- else => unreachable,
- .AMD64 => .{ .AMD64 = .ADDR64 },
- .I386 => .{ .I386 = .DIR32 },
- },
- );
- return coff.optionalHeaderField(.image_base) + target_si.get(coff).rva;
-}
-
-fn addSection(
- coff: *Coff,
- name: []const u8,
- maybe_data_directory: ?DataDirectory,
- flags: std.coff.SectionHeader.Flags,
-) !Symbol.Index {
- const gpa = coff.base.comp.gpa;
- try coff.nodes.ensureUnusedCapacity(gpa, 1);
- try coff.section_table.ensureUnusedCapacity(gpa, 1);
- try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
-
- const coff_header = coff.headerPtr();
- const section_index = coff.targetLoad(&coff_header.number_of_sections);
- const section_table_len = section_index + 1;
- coff.targetStore(&coff_header.number_of_sections, section_table_len);
- try Node.known.section_table.resize(
- &coff.mf,
- gpa,
- @sizeOf(std.coff.SectionHeader) * section_table_len,
- );
- const ni = try coff.mf.addLastChildNode(gpa, .root, .{
- .alignment = coff.mf.flags.block_size,
- .moved = true,
- .bubbles_moved = false,
- });
- const si = coff.addSymbolAssumeCapacity();
- coff.section_table.appendAssumeCapacity(si);
- coff.nodes.appendAssumeCapacity(.{ .section = si });
- const section_table = coff.sectionTableSlice();
- const virtual_size = coff.optionalHeaderField(.section_alignment);
- const rva: u32 = switch (section_index) {
- 0 => @intCast(Node.known.header.location(&coff.mf).resolve(&coff.mf)[1]),
- else => coff.section_table.items[section_index - 1].get(coff).rva +
- coff.targetLoad(§ion_table[section_index - 1].virtual_size),
- };
- {
- const sym = si.get(coff);
- sym.ni = ni;
- sym.rva = rva;
- sym.section_number = @enumFromInt(section_table_len);
- sym.data_directory = maybe_data_directory;
- }
- const section = §ion_table[section_index];
- section.* = .{
- .name = undefined,
- .virtual_size = virtual_size,
- .virtual_address = rva,
- .size_of_raw_data = 0,
- .pointer_to_raw_data = 0,
- .pointer_to_relocations = 0,
- .pointer_to_linenumbers = 0,
- .number_of_relocations = 0,
- .number_of_linenumbers = 0,
- .flags = flags,
- };
- @memcpy(section.name[0..name.len], name);
- @memset(section.name[name.len..], 0);
- if (coff.targetEndian() != native_endian)
- std.mem.byteSwapAllFields(std.coff.SectionHeader, section);
- if (maybe_data_directory) |data_directory|
- coff.dataDirectoriesSlice()[@intFromEnum(data_directory)] = .{
- .virtual_address = section.virtual_address,
- .size = section.virtual_size,
- };
- switch (coff.optionalHeaderPtr()) {
- inline else => |optional_header| coff.targetStore(
- &optional_header.size_of_image,
- @intCast(rva + virtual_size),
- ),
- }
- return si;
-}
-
-pub fn addReloc(
- coff: *Coff,
- loc_si: Symbol.Index,
- offset: u64,
- target_si: Symbol.Index,
- addend: i64,
- @"type": Reloc.Type,
-) !void {
- const gpa = coff.base.comp.gpa;
- const target = target_si.get(coff);
- const ri: Reloc.Index = @enumFromInt(coff.relocs.items.len);
- (try coff.relocs.addOne(gpa)).* = .{
- .type = @"type",
- .prev = .none,
- .next = target.target_relocs,
- .loc = loc_si,
- .target = target_si,
- .unused = 0,
- .offset = offset,
- .addend = addend,
- };
- switch (target.target_relocs) {
- .none => {},
- else => |target_ri| target_ri.get(coff).prev = ri,
- }
- target.target_relocs = ri;
-}
-
-pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) void {
- _ = coff;
- _ = prog_node;
-}
-
-pub fn updateNav(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
- coff.updateNavInner(pt, nav_index) catch |err| switch (err) {
- error.OutOfMemory,
- error.Overflow,
- error.RelocationNotByteAligned,
- => |e| return e,
- else => |e| return coff.base.cgFail(nav_index, "linker failed to update variable: {t}", .{e}),
- };
-}
-fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
- const zcu = pt.zcu;
- const gpa = zcu.gpa;
- const ip = &zcu.intern_pool;
-
- const nav = ip.getNav(nav_index);
- const nav_val = nav.status.fully_resolved.val;
- const nav_init, const is_threadlocal = switch (ip.indexToKey(nav_val)) {
- else => .{ nav_val, false },
- .variable => |variable| .{ variable.init, variable.is_threadlocal },
- .@"extern" => return,
- .func => .{ .none, false },
- };
- if (nav_init == .none or !Type.fromInterned(ip.typeOf(nav_init)).hasRuntimeBits(zcu)) return;
-
- const nmi = try coff.navMapIndex(zcu, nav_index);
- const si = nmi.symbol(coff);
- const ni = ni: {
- const sym = si.get(coff);
- switch (sym.ni) {
- .none => {
- try coff.nodes.ensureUnusedCapacity(gpa, 1);
- _ = is_threadlocal;
- const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.data.node(coff), .{
- .alignment = pt.navAlignment(nav_index).toStdMem(),
- .moved = true,
- });
- coff.nodes.appendAssumeCapacity(.{ .nav = nmi });
- sym.ni = ni;
- sym.section_number = Symbol.Index.data.get(coff).section_number;
- },
- else => si.deleteLocationRelocs(coff),
- }
- assert(sym.loc_relocs == .none);
- sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
- break :ni sym.ni;
- };
-
- var nw: MappedFile.Node.Writer = undefined;
- ni.writer(&coff.mf, gpa, &nw);
- defer nw.deinit();
- codegen.generateSymbol(
- &coff.base,
- pt,
- zcu.navSrcLoc(nav_index),
- .fromInterned(nav_init),
- &nw.interface,
- .{ .atom_index = @intFromEnum(si) },
- ) catch |err| switch (err) {
- error.WriteFailed => return error.OutOfMemory,
- else => |e| return e,
- };
- si.get(coff).size = @intCast(nw.interface.end);
- si.applyLocationRelocs(coff);
-}
-
-pub fn lowerUav(
- coff: *Coff,
- pt: Zcu.PerThread,
- uav_val: InternPool.Index,
- uav_align: InternPool.Alignment,
- src_loc: Zcu.LazySrcLoc,
-) !codegen.SymbolResult {
- const zcu = pt.zcu;
- const gpa = zcu.gpa;
-
- try coff.pending_uavs.ensureUnusedCapacity(gpa, 1);
- const umi = try coff.uavMapIndex(uav_val);
- const si = umi.symbol(coff);
- if (switch (si.get(coff).ni) {
- .none => true,
- else => |ni| uav_align.toStdMem().order(ni.alignment(&coff.mf)).compare(.gt),
- }) {
- const gop = coff.pending_uavs.getOrPutAssumeCapacity(umi);
- if (gop.found_existing) {
- gop.value_ptr.alignment = gop.value_ptr.alignment.max(uav_align);
- } else {
- gop.value_ptr.* = .{
- .alignment = uav_align,
- .src_loc = src_loc,
- };
- coff.base.comp.link_const_prog_node.increaseEstimatedTotalItems(1);
- }
- }
- return .{ .sym_index = @intFromEnum(si) };
-}
-
-pub fn updateFunc(
- coff: *Coff,
- pt: Zcu.PerThread,
- func_index: InternPool.Index,
- mir: *const codegen.AnyMir,
-) !void {
- coff.updateFuncInner(pt, func_index, mir) catch |err| switch (err) {
- error.OutOfMemory,
- error.Overflow,
- error.RelocationNotByteAligned,
- error.CodegenFail,
- => |e| return e,
- else => |e| return coff.base.cgFail(
- pt.zcu.funcInfo(func_index).owner_nav,
- "linker failed to update function: {s}",
- .{@errorName(e)},
- ),
- };
-}
-fn updateFuncInner(
- coff: *Coff,
- pt: Zcu.PerThread,
- func_index: InternPool.Index,
- mir: *const codegen.AnyMir,
-) !void {
- const zcu = pt.zcu;
- const gpa = zcu.gpa;
- const ip = &zcu.intern_pool;
- const func = zcu.funcInfo(func_index);
- const nav = ip.getNav(func.owner_nav);
-
- const nmi = try coff.navMapIndex(zcu, func.owner_nav);
- const si = nmi.symbol(coff);
- log.debug("updateFunc({f}) = {d}", .{ nav.fqn.fmt(ip), si });
- const ni = ni: {
- const sym = si.get(coff);
- switch (sym.ni) {
- .none => {
- try coff.nodes.ensureUnusedCapacity(gpa, 1);
- const mod = zcu.navFileScope(func.owner_nav).mod.?;
- const target = &mod.resolved_target.result;
- const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.text.node(coff), .{
- .alignment = switch (nav.status.fully_resolved.alignment) {
- .none => switch (mod.optimize_mode) {
- .Debug,
- .ReleaseSafe,
- .ReleaseFast,
- => target_util.defaultFunctionAlignment(target),
- .ReleaseSmall => target_util.minFunctionAlignment(target),
- },
- else => |a| a.maxStrict(target_util.minFunctionAlignment(target)),
- }.toStdMem(),
- .moved = true,
- });
- coff.nodes.appendAssumeCapacity(.{ .nav = nmi });
- sym.ni = ni;
- sym.section_number = Symbol.Index.text.get(coff).section_number;
- },
- else => si.deleteLocationRelocs(coff),
- }
- assert(sym.loc_relocs == .none);
- sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
- break :ni sym.ni;
- };
-
- var nw: MappedFile.Node.Writer = undefined;
- ni.writer(&coff.mf, gpa, &nw);
- defer nw.deinit();
- codegen.emitFunction(
- &coff.base,
- pt,
- zcu.navSrcLoc(func.owner_nav),
- func_index,
- @intFromEnum(si),
- mir,
- &nw.interface,
- .none,
- ) catch |err| switch (err) {
- error.WriteFailed => return nw.err.?,
- else => |e| return e,
- };
- si.get(coff).size = @intCast(nw.interface.end);
- si.applyLocationRelocs(coff);
-}
-
-pub fn updateErrorData(coff: *Coff, pt: Zcu.PerThread) !void {
- coff.flushLazy(pt, .{
- .kind = .const_data,
- .index = @intCast(coff.lazy.getPtr(.const_data).map.getIndex(.anyerror_type) orelse return),
- }) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.CodegenFail => return error.LinkFailure,
- else => |e| return coff.base.comp.link_diags.fail("updateErrorData failed {t}", .{e}),
- };
-}
-
-pub fn flush(
- coff: *Coff,
- arena: std.mem.Allocator,
- tid: Zcu.PerThread.Id,
- prog_node: std.Progress.Node,
-) !void {
- _ = arena;
- _ = prog_node;
- while (try coff.idle(tid)) {}
-
- // hack for stage2_x86_64 + coff
- const comp = coff.base.comp;
- if (comp.compiler_rt_dyn_lib) |crt_file| {
- const gpa = comp.gpa;
- const compiler_rt_sub_path = try std.fs.path.join(gpa, &.{
- std.fs.path.dirname(coff.base.emit.sub_path) orelse "",
- std.fs.path.basename(crt_file.full_object_path.sub_path),
- });
- defer gpa.free(compiler_rt_sub_path);
- crt_file.full_object_path.root_dir.handle.copyFile(
- crt_file.full_object_path.sub_path,
- coff.base.emit.root_dir.handle,
- compiler_rt_sub_path,
- .{},
- ) catch |err| switch (err) {
- else => |e| return comp.link_diags.fail("Copy '{s}' failed: {s}", .{
- compiler_rt_sub_path,
- @errorName(e),
- }),
- };
- }
-}
-
-pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
- const comp = coff.base.comp;
- task: {
- while (coff.pending_uavs.pop()) |pending_uav| {
- const sub_prog_node = coff.idleProgNode(
- tid,
- comp.link_const_prog_node,
- .{ .uav = pending_uav.key },
- );
- defer sub_prog_node.end();
- coff.flushUav(
- .{ .zcu = coff.base.comp.zcu.?, .tid = tid },
- pending_uav.key,
- pending_uav.value.alignment,
- pending_uav.value.src_loc,
- ) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- else => |e| return coff.base.comp.link_diags.fail(
- "linker failed to lower constant: {t}",
- .{e},
- ),
- };
- break :task;
- }
- if (coff.global_pending_index < coff.globals.count()) {
- const pt: Zcu.PerThread = .{ .zcu = coff.base.comp.zcu.?, .tid = tid };
- const gmi: Node.GlobalMapIndex = @enumFromInt(coff.global_pending_index);
- coff.global_pending_index += 1;
- const sub_prog_node = comp.link_synth_prog_node.start(
- gmi.globalName(coff).name.toSlice(coff),
- 0,
- );
- defer sub_prog_node.end();
- coff.flushGlobal(pt, gmi) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- else => |e| return coff.base.comp.link_diags.fail(
- "linker failed to lower constant: {t}",
- .{e},
- ),
- };
- break :task;
- }
- var lazy_it = coff.lazy.iterator();
- while (lazy_it.next()) |lazy| if (lazy.value.pending_index < lazy.value.map.count()) {
- const pt: Zcu.PerThread = .{ .zcu = coff.base.comp.zcu.?, .tid = tid };
- const lmr: Node.LazyMapRef = .{ .kind = lazy.key, .index = lazy.value.pending_index };
- lazy.value.pending_index += 1;
- const kind = switch (lmr.kind) {
- .code => "code",
- .const_data => "data",
- };
- var name: [std.Progress.Node.max_name_len]u8 = undefined;
- const sub_prog_node = comp.link_synth_prog_node.start(
- std.fmt.bufPrint(&name, "lazy {s} for {f}", .{
- kind,
- Type.fromInterned(lmr.lazySymbol(coff).ty).fmt(pt),
- }) catch &name,
- 0,
- );
- defer sub_prog_node.end();
- coff.flushLazy(pt, lmr) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- else => |e| return coff.base.comp.link_diags.fail(
- "linker failed to lower lazy {s}: {t}",
- .{ kind, e },
- ),
- };
- break :task;
- };
- while (coff.mf.updates.pop()) |ni| {
- const clean_moved = ni.cleanMoved(&coff.mf);
- const clean_resized = ni.cleanResized(&coff.mf);
- if (clean_moved or clean_resized) {
- const sub_prog_node = coff.idleProgNode(tid, coff.mf.update_prog_node, coff.getNode(ni));
- defer sub_prog_node.end();
- if (clean_moved) try coff.flushMoved(ni);
- if (clean_resized) try coff.flushResized(ni);
- break :task;
- } else coff.mf.update_prog_node.completeOne();
- }
- }
- if (coff.pending_uavs.count() > 0) return true;
- for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
- if (coff.mf.updates.items.len > 0) return true;
- return false;
-}
-
-fn idleProgNode(
- coff: *Coff,
- tid: Zcu.PerThread.Id,
- prog_node: std.Progress.Node,
- node: Node,
-) std.Progress.Node {
- var name: [std.Progress.Node.max_name_len]u8 = undefined;
- return prog_node.start(name: switch (node) {
- else => |tag| @tagName(tag),
- .section => |si| std.mem.sliceTo(&si.get(coff).section_number.header(coff).name, 0),
- .nav => |nmi| {
- const ip = &coff.base.comp.zcu.?.intern_pool;
- break :name ip.getNav(nmi.navIndex(coff)).fqn.toSlice(ip);
- },
- .uav => |umi| std.fmt.bufPrint(&name, "{f}", .{
- Value.fromInterned(umi.uavValue(coff)).fmtValue(.{
- .zcu = coff.base.comp.zcu.?,
- .tid = tid,
- }),
- }) catch &name,
- }, 0);
-}
-
-fn flushUav(
- coff: *Coff,
- pt: Zcu.PerThread,
- umi: Node.UavMapIndex,
- uav_align: InternPool.Alignment,
- src_loc: Zcu.LazySrcLoc,
-) !void {
- const zcu = pt.zcu;
- const gpa = zcu.gpa;
-
- const uav_val = umi.uavValue(coff);
- const si = umi.symbol(coff);
- const ni = ni: {
- const sym = si.get(coff);
- switch (sym.ni) {
- .none => {
- try coff.nodes.ensureUnusedCapacity(gpa, 1);
- const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.data.node(coff), .{
- .alignment = uav_align.toStdMem(),
- .moved = true,
- });
- coff.nodes.appendAssumeCapacity(.{ .uav = umi });
- sym.ni = ni;
- sym.section_number = Symbol.Index.data.get(coff).section_number;
- },
- else => {
- if (sym.ni.alignment(&coff.mf).order(uav_align.toStdMem()).compare(.gte)) return;
- si.deleteLocationRelocs(coff);
- },
- }
- assert(sym.loc_relocs == .none);
- sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
- break :ni sym.ni;
- };
-
- var nw: MappedFile.Node.Writer = undefined;
- ni.writer(&coff.mf, gpa, &nw);
- defer nw.deinit();
- codegen.generateSymbol(
- &coff.base,
- pt,
- src_loc,
- .fromInterned(uav_val),
- &nw.interface,
- .{ .atom_index = @intFromEnum(si) },
- ) catch |err| switch (err) {
- error.WriteFailed => return error.OutOfMemory,
- else => |e| return e,
- };
- si.get(coff).size = @intCast(nw.interface.end);
- si.applyLocationRelocs(coff);
-}
-
-fn flushGlobal(coff: *Coff, pt: Zcu.PerThread, gmi: Node.GlobalMapIndex) !void {
- const zcu = pt.zcu;
- const comp = zcu.comp;
- const gpa = zcu.gpa;
- const gn = gmi.globalName(coff);
- if (gn.lib_name.toSlice(coff)) |lib_name| {
- const name = gn.name.toSlice(coff);
- try coff.nodes.ensureUnusedCapacity(gpa, 4);
- try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
-
- const target_endian = coff.targetEndian();
- const magic = coff.targetLoad(&coff.optionalHeaderStandardPtr().magic);
- const addr_size: u64, const addr_align: std.mem.Alignment = switch (magic) {
- _ => unreachable,
- .PE32 => .{ 4, .@"4" },
- .@"PE32+" => .{ 8, .@"8" },
- };
-
- const gop = try coff.import_table.dlls.getOrPutAdapted(
- gpa,
- lib_name,
- ImportTable.Adapter{ .coff = coff },
- );
- const import_hint_name_align: std.mem.Alignment = .@"2";
- if (!gop.found_existing) {
- errdefer _ = coff.import_table.dlls.pop();
- try coff.import_table.directory_table_ni.resize(
- &coff.mf,
- gpa,
- @sizeOf(std.coff.ImportDirectoryEntry) * (gop.index + 2),
- );
- const import_hint_name_table_len =
- import_hint_name_align.forward(lib_name.len + ".dll".len + 1);
- const idata_section_ni = Symbol.Index.idata.node(coff);
- const import_lookup_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{
- .size = addr_size * 2,
- .alignment = addr_align,
- .moved = true,
- });
- const import_address_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{
- .size = addr_size * 2,
- .alignment = addr_align,
- .moved = true,
- });
- const import_address_table_si = coff.addSymbolAssumeCapacity();
- {
- const import_address_table_sym = import_address_table_si.get(coff);
- import_address_table_sym.ni = import_address_table_ni;
- assert(import_address_table_sym.loc_relocs == .none);
- import_address_table_sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
- import_address_table_sym.section_number = Symbol.Index.idata.get(coff).section_number;
- }
- const import_hint_name_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{
- .size = import_hint_name_table_len,
- .alignment = import_hint_name_align,
- .moved = true,
- });
- gop.value_ptr.* = .{
- .import_lookup_table_ni = import_lookup_table_ni,
- .import_address_table_si = import_address_table_si,
- .import_hint_name_table_ni = import_hint_name_table_ni,
- .len = 0,
- .hint_name_len = @intCast(import_hint_name_table_len),
- };
- const import_hint_name_slice = import_hint_name_table_ni.slice(&coff.mf);
- @memcpy(import_hint_name_slice[0..lib_name.len], lib_name);
- @memcpy(import_hint_name_slice[lib_name.len..][0..".dll".len], ".dll");
- @memset(import_hint_name_slice[lib_name.len + ".dll".len ..], 0);
- coff.nodes.appendAssumeCapacity(.{ .import_lookup_table = @intCast(gop.index) });
- coff.nodes.appendAssumeCapacity(.{ .import_address_table = @intCast(gop.index) });
- coff.nodes.appendAssumeCapacity(.{ .import_hint_name_table = @intCast(gop.index) });
-
- const import_directory_table: []std.coff.ImportDirectoryEntry =
- @ptrCast(@alignCast(coff.import_table.directory_table_ni.slice(&coff.mf)));
- import_directory_table[gop.index..][0..2].* = .{ .{
- .import_lookup_table_rva = coff.computeNodeRva(import_lookup_table_ni),
- .time_date_stamp = 0,
- .forwarder_chain = 0,
- .name_rva = coff.computeNodeRva(import_hint_name_table_ni),
- .import_address_table_rva = coff.computeNodeRva(import_address_table_ni),
- }, .{
- .import_lookup_table_rva = 0,
- .time_date_stamp = 0,
- .forwarder_chain = 0,
- .name_rva = 0,
- .import_address_table_rva = 0,
- } };
- }
- const import_symbol_index = gop.value_ptr.len;
- gop.value_ptr.len = import_symbol_index + 1;
- const new_symbol_table_size = addr_size * (import_symbol_index + 2);
- const import_hint_name_index = gop.value_ptr.hint_name_len;
- gop.value_ptr.hint_name_len = @intCast(
- import_hint_name_align.forward(import_hint_name_index + 2 + name.len + 1),
- );
- try gop.value_ptr.import_lookup_table_ni.resize(&coff.mf, gpa, new_symbol_table_size);
- const import_address_table_ni = gop.value_ptr.import_address_table_si.node(coff);
- try import_address_table_ni.resize(&coff.mf, gpa, new_symbol_table_size);
- try gop.value_ptr.import_hint_name_table_ni.resize(&coff.mf, gpa, gop.value_ptr.hint_name_len);
- const import_lookup_slice = gop.value_ptr.import_lookup_table_ni.slice(&coff.mf);
- const import_address_slice = import_address_table_ni.slice(&coff.mf);
- const import_hint_name_slice = gop.value_ptr.import_hint_name_table_ni.slice(&coff.mf);
- @memset(import_hint_name_slice[import_hint_name_index..][0..2], 0);
- @memcpy(import_hint_name_slice[import_hint_name_index + 2 ..][0..name.len], name);
- @memset(import_hint_name_slice[import_hint_name_index + 2 + name.len ..], 0);
- const import_hint_name_rva =
- coff.computeNodeRva(gop.value_ptr.import_hint_name_table_ni) + import_hint_name_index;
- switch (magic) {
- _ => unreachable,
- inline .PE32, .@"PE32+" => |ct_magic| {
- const Addr = switch (ct_magic) {
- _ => comptime unreachable,
- .PE32 => u32,
- .@"PE32+" => u64,
- };
- const import_lookup_table: []Addr = @ptrCast(@alignCast(import_lookup_slice));
- const import_address_table: []Addr = @ptrCast(@alignCast(import_address_slice));
- const import_hint_name_rvas: [2]Addr = .{
- std.mem.nativeTo(Addr, @intCast(import_hint_name_rva), target_endian),
- std.mem.nativeTo(Addr, 0, target_endian),
- };
- import_lookup_table[import_symbol_index..][0..2].* = import_hint_name_rvas;
- import_address_table[import_symbol_index..][0..2].* = import_hint_name_rvas;
- },
- }
- const si = gmi.symbol(coff);
- const sym = si.get(coff);
- sym.section_number = Symbol.Index.text.get(coff).section_number;
- assert(sym.loc_relocs == .none);
- sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
- switch (coff.targetLoad(&coff.headerPtr().machine)) {
- else => |tag| @panic(@tagName(tag)),
- .AMD64 => {
- const init = [_]u8{ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 };
- const target = &comp.root_mod.resolved_target.result;
- const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.text.node(coff), .{
- .alignment = switch (comp.root_mod.optimize_mode) {
- .Debug,
- .ReleaseSafe,
- .ReleaseFast,
- => target_util.defaultFunctionAlignment(target),
- .ReleaseSmall => target_util.minFunctionAlignment(target),
- }.toStdMem(),
- .size = init.len,
- });
- @memcpy(ni.slice(&coff.mf)[0..init.len], &init);
- sym.ni = ni;
- sym.size = init.len;
- try coff.addReloc(
- si,
- init.len - 4,
- gop.value_ptr.import_address_table_si,
- @intCast(addr_size * import_symbol_index),
- .{ .AMD64 = .REL32 },
- );
- },
- }
- coff.nodes.appendAssumeCapacity(.{ .global = gmi });
- sym.rva = coff.computeNodeRva(sym.ni);
- si.applyLocationRelocs(coff);
- }
-}
-
-fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
- const zcu = pt.zcu;
- const gpa = zcu.gpa;
-
- const lazy = lmr.lazySymbol(coff);
- const si = lmr.symbol(coff);
- const ni = ni: {
- const sym = si.get(coff);
- switch (sym.ni) {
- .none => {
- try coff.nodes.ensureUnusedCapacity(gpa, 1);
- const sec_si: Symbol.Index = switch (lazy.kind) {
- .code => .text,
- .const_data => .rdata,
- };
- const ni = try coff.mf.addLastChildNode(gpa, sec_si.node(coff), .{ .moved = true });
- coff.nodes.appendAssumeCapacity(switch (lazy.kind) {
- .code => .{ .lazy_code = @enumFromInt(lmr.index) },
- .const_data => .{ .lazy_const_data = @enumFromInt(lmr.index) },
- });
- sym.ni = ni;
- sym.section_number = sec_si.get(coff).section_number;
- },
- else => si.deleteLocationRelocs(coff),
- }
- assert(sym.loc_relocs == .none);
- sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
- break :ni sym.ni;
- };
-
- var required_alignment: InternPool.Alignment = .none;
- var nw: MappedFile.Node.Writer = undefined;
- ni.writer(&coff.mf, gpa, &nw);
- defer nw.deinit();
- try codegen.generateLazySymbol(
- &coff.base,
- pt,
- Type.fromInterned(lazy.ty).srcLocOrNull(pt.zcu) orelse .unneeded,
- lazy,
- &required_alignment,
- &nw.interface,
- .none,
- .{ .atom_index = @intFromEnum(si) },
- );
- si.get(coff).size = @intCast(nw.interface.end);
- si.applyLocationRelocs(coff);
-}
-
-fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
- const node = coff.getNode(ni);
- switch (node) {
- else => |tag| @panic(@tagName(tag)),
- .section => |si| return coff.targetStore(
- &si.get(coff).section_number.header(coff).pointer_to_raw_data,
- @intCast(ni.fileLocation(&coff.mf, false).offset),
- ),
- .import_directory_table => {},
- .import_lookup_table => |import_directory_table_index| {
- const import_directory_table: []std.coff.ImportDirectoryEntry =
- @ptrCast(@alignCast(coff.import_table.directory_table_ni.slice(&coff.mf)));
- const import_directory_entry = &import_directory_table[import_directory_table_index];
- coff.targetStore(&import_directory_entry.import_lookup_table_rva, coff.computeNodeRva(ni));
- },
- .import_address_table => |import_directory_table_index| {
- const import_directory_table: []std.coff.ImportDirectoryEntry =
- @ptrCast(@alignCast(coff.import_table.directory_table_ni.slice(&coff.mf)));
- const import_directory_entry = &import_directory_table[import_directory_table_index];
- coff.targetStore(&import_directory_entry.import_lookup_table_rva, coff.computeNodeRva(ni));
- const import_address_table_si =
- coff.import_table.dlls.values()[import_directory_table_index].import_address_table_si;
- import_address_table_si.flushMoved(coff);
- coff.targetStore(
- &import_directory_entry.import_address_table_rva,
- import_address_table_si.get(coff).rva,
- );
- },
- .import_hint_name_table => |import_directory_table_index| {
- const target_endian = coff.targetEndian();
- const magic = coff.targetLoad(&coff.optionalHeaderStandardPtr().magic);
- const import_directory_table: []std.coff.ImportDirectoryEntry =
- @ptrCast(@alignCast(coff.import_table.directory_table_ni.slice(&coff.mf)));
- const import_directory_entry = &import_directory_table[import_directory_table_index];
- const import_hint_name_rva = coff.computeNodeRva(ni);
- coff.targetStore(&import_directory_entry.name_rva, import_hint_name_rva);
- const import_entry = &coff.import_table.dlls.values()[import_directory_table_index];
- const import_lookup_slice = import_entry.import_lookup_table_ni.slice(&coff.mf);
- const import_address_slice =
- import_entry.import_address_table_si.node(coff).slice(&coff.mf);
- const import_hint_name_slice = ni.slice(&coff.mf);
- const import_hint_name_align = ni.alignment(&coff.mf);
- var import_hint_name_index: u32 = 0;
- for (0..import_entry.len) |import_symbol_index| {
- import_hint_name_index = @intCast(import_hint_name_align.forward(
- std.mem.indexOfScalarPos(
- u8,
- import_hint_name_slice,
- import_hint_name_index,
- 0,
- ).? + 1,
- ));
- switch (magic) {
- _ => unreachable,
- inline .PE32, .@"PE32+" => |ct_magic| {
- const Addr = switch (ct_magic) {
- _ => comptime unreachable,
- .PE32 => u32,
- .@"PE32+" => u64,
- };
- const import_lookup_table: []Addr = @ptrCast(@alignCast(import_lookup_slice));
- const import_address_table: []Addr = @ptrCast(@alignCast(import_address_slice));
- const rva = std.mem.nativeTo(
- Addr,
- import_hint_name_rva + import_hint_name_index,
- target_endian,
- );
- import_lookup_table[import_symbol_index] = rva;
- import_address_table[import_symbol_index] = rva;
- },
- }
- import_hint_name_index += 2;
- }
- },
- inline .global,
- .nav,
- .uav,
- .lazy_code,
- .lazy_const_data,
- => |mi| mi.symbol(coff).flushMoved(coff),
- }
- try ni.childrenMoved(coff.base.comp.gpa, &coff.mf);
-}
-
-fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
- _, const size = ni.location(&coff.mf).resolve(&coff.mf);
- const node = coff.getNode(ni);
- switch (node) {
- else => |tag| @panic(@tagName(tag)),
- .file => {},
- .header => {
- switch (coff.optionalHeaderPtr()) {
- inline else => |optional_header| coff.targetStore(
- &optional_header.size_of_headers,
- @intCast(size),
- ),
- }
- if (size > coff.section_table.items[0].get(coff).rva) try coff.virtualSlide(
- 0,
- std.mem.alignForward(
- u32,
- @intCast(size * 4),
- coff.optionalHeaderField(.section_alignment),
- ),
- );
- },
- .section_table => {},
- .section => |si| {
- const sym = si.get(coff);
- const section_table = coff.sectionTableSlice();
- const section_index = sym.section_number.toIndex();
- const section = §ion_table[section_index];
- coff.targetStore(§ion.size_of_raw_data, @intCast(size));
- if (size > coff.targetLoad(§ion.virtual_size)) {
- const virtual_size = std.mem.alignForward(
- u32,
- @intCast(size * 4),
- coff.optionalHeaderField(.section_alignment),
- );
- coff.targetStore(§ion.virtual_size, virtual_size);
- if (sym.data_directory) |data_directory|
- coff.dataDirectoriesSlice()[@intFromEnum(data_directory)].size =
- section.virtual_size;
- try coff.virtualSlide(section_index + 1, sym.rva + virtual_size);
- }
- },
- .import_directory_table,
- .import_lookup_table,
- .import_address_table,
- .import_hint_name_table,
- .global,
- .nav,
- .uav,
- .lazy_code,
- .lazy_const_data,
- => {},
- }
-}
-
-fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void {
- const section_table = coff.sectionTableSlice();
- var rva = start_rva;
- for (
- coff.section_table.items[start_section_index..],
- section_table[start_section_index..],
- ) |section_si, *section| {
- const section_sym = section_si.get(coff);
- section_sym.rva = rva;
- coff.targetStore(§ion.virtual_address, rva);
- if (section_sym.data_directory) |data_directory|
- coff.dataDirectoriesSlice()[@intFromEnum(data_directory)].virtual_address =
- section.virtual_address;
- try section_sym.ni.childrenMoved(coff.base.comp.gpa, &coff.mf);
- rva += coff.targetLoad(§ion.virtual_size);
- }
- switch (coff.optionalHeaderPtr()) {
- inline else => |optional_header| coff.targetStore(
- &optional_header.size_of_image,
- @intCast(rva),
- ),
- }
-}
-
-pub fn updateExports(
- coff: *Coff,
- pt: Zcu.PerThread,
- exported: Zcu.Exported,
- export_indices: []const Zcu.Export.Index,
-) !void {
- return coff.updateExportsInner(pt, exported, export_indices) catch |err| switch (err) {
- error.OutOfMemory => error.OutOfMemory,
- error.LinkFailure => error.AnalysisFail,
- };
-}
-fn updateExportsInner(
- coff: *Coff,
- pt: Zcu.PerThread,
- exported: Zcu.Exported,
- export_indices: []const Zcu.Export.Index,
-) !void {
- const zcu = pt.zcu;
- const gpa = zcu.gpa;
- const ip = &zcu.intern_pool;
-
- switch (exported) {
- .nav => |nav| log.debug("updateExports({f})", .{ip.getNav(nav).fqn.fmt(ip)}),
- .uav => |uav| log.debug("updateExports(@as({f}, {f}))", .{
- Type.fromInterned(ip.typeOf(uav)).fmt(pt),
- Value.fromInterned(uav).fmtValue(pt),
- }),
- }
- try coff.symbol_table.ensureUnusedCapacity(gpa, export_indices.len);
- const exported_si: Symbol.Index = switch (exported) {
- .nav => |nav| try coff.navSymbol(zcu, nav),
- .uav => |uav| @enumFromInt(switch (try coff.lowerUav(
- pt,
- uav,
- Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),
- export_indices[0].ptr(zcu).src,
- )) {
- .sym_index => |si| si,
- .fail => |em| {
- defer em.destroy(gpa);
- return coff.base.comp.link_diags.fail("{s}", .{em.msg});
- },
- }),
- };
- while (try coff.idle(pt.tid)) {}
- const exported_ni = exported_si.node(coff);
- const exported_sym = exported_si.get(coff);
- for (export_indices) |export_index| {
- const @"export" = export_index.ptr(zcu);
- const export_si = try coff.globalSymbol(@"export".opts.name.toSlice(ip), null);
- const export_sym = export_si.get(coff);
- export_sym.ni = exported_ni;
- export_sym.rva = exported_sym.rva;
- export_sym.size = exported_sym.size;
- export_sym.section_number = exported_sym.section_number;
- export_si.applyTargetRelocs(coff);
- if (@"export".opts.name.eqlSlice("wWinMainCRTStartup", ip)) {
- coff.entry_hack = exported_si;
- coff.optionalHeaderStandardPtr().address_of_entry_point = exported_sym.rva;
- }
- }
-}
-
-pub fn deleteExport(coff: *Coff, exported: Zcu.Exported, name: InternPool.NullTerminatedString) void {
- _ = coff;
- _ = exported;
- _ = name;
-}
-
-pub fn dump(coff: *Coff, tid: Zcu.PerThread.Id) void {
- const w = std.debug.lockStderrWriter(&.{});
- defer std.debug.unlockStderrWriter();
- coff.printNode(tid, w, .root, 0) catch {};
-}
-
-pub fn printNode(
- coff: *Coff,
- tid: Zcu.PerThread.Id,
- w: *std.Io.Writer,
- ni: MappedFile.Node.Index,
- indent: usize,
-) !void {
- const node = coff.getNode(ni);
- try w.splatByteAll(' ', indent);
- try w.writeAll(@tagName(node));
- switch (node) {
- else => {},
- .section => |si| try w.print("({s})", .{
- std.mem.sliceTo(&si.get(coff).section_number.header(coff).name, 0),
- }),
- .import_lookup_table,
- .import_address_table,
- .import_hint_name_table,
- => |import_directory_table_index| try w.print("({s})", .{
- std.mem.sliceTo(coff.import_table.dlls.values()[import_directory_table_index]
- .import_hint_name_table_ni.sliceConst(&coff.mf), 0),
- }),
- .global => |gmi| {
- const gn = gmi.globalName(coff);
- try w.writeByte('(');
- if (gn.lib_name.toSlice(coff)) |lib_name| try w.print("{s}.dll, ", .{lib_name});
- try w.print("{s})", .{gn.name.toSlice(coff)});
- },
- .nav => |nmi| {
- const zcu = coff.base.comp.zcu.?;
- const ip = &zcu.intern_pool;
- const nav = ip.getNav(nmi.navIndex(coff));
- try w.print("({f}, {f})", .{
- Type.fromInterned(nav.typeOf(ip)).fmt(.{ .zcu = zcu, .tid = tid }),
- nav.fqn.fmt(ip),
- });
- },
- .uav => |umi| {
- const zcu = coff.base.comp.zcu.?;
- const val: Value = .fromInterned(umi.uavValue(coff));
- try w.print("({f}, {f})", .{
- val.typeOf(zcu).fmt(.{ .zcu = zcu, .tid = tid }),
- val.fmtValue(.{ .zcu = zcu, .tid = tid }),
- });
- },
- inline .lazy_code, .lazy_const_data => |lmi| try w.print("({f})", .{
- Type.fromInterned(lmi.lazySymbol(coff).ty).fmt(.{
- .zcu = coff.base.comp.zcu.?,
- .tid = tid,
- }),
- }),
- }
- {
- const mf_node = &coff.mf.nodes.items[@intFromEnum(ni)];
- const off, const size = mf_node.location().resolve(&coff.mf);
- try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x}{s}{s}{s}{s}\n", .{
- @intFromEnum(ni),
- off,
- size,
- mf_node.flags.alignment.toByteUnits(),
- if (mf_node.flags.fixed) " fixed" else "",
- if (mf_node.flags.moved) " moved" else "",
- if (mf_node.flags.resized) " resized" else "",
- if (mf_node.flags.has_content) " has_content" else "",
- });
- }
- var leaf = true;
- var child_it = ni.children(&coff.mf);
- while (child_it.next()) |child_ni| {
- leaf = false;
- try coff.printNode(tid, w, child_ni, indent + 1);
- }
- if (leaf) {
- const file_loc = ni.fileLocation(&coff.mf, false);
- if (file_loc.size == 0) return;
- var address = file_loc.offset;
- const line_len = 0x10;
- var line_it = std.mem.window(
- u8,
- coff.mf.contents[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)],
- line_len,
- line_len,
- );
- while (line_it.next()) |line_bytes| : (address += line_len) {
- try w.splatByteAll(' ', indent + 1);
- try w.print("{x:0>8} ", .{address});
- for (line_bytes) |byte| try w.print("{x:0>2} ", .{byte});
- try w.splatByteAll(' ', 3 * (line_len - line_bytes.len) + 1);
- for (line_bytes) |byte| try w.writeByte(if (std.ascii.isPrint(byte)) byte else '.');
- try w.writeByte('\n');
- }
- }
-}
-
-const assert = std.debug.assert;
-const builtin = @import("builtin");
-const codegen = @import("../codegen.zig");
-const Compilation = @import("../Compilation.zig");
-const Coff = @This();
-const InternPool = @import("../InternPool.zig");
-const link = @import("../link.zig");
-const log = std.log.scoped(.link);
-const MappedFile = @import("MappedFile.zig");
-const native_endian = builtin.cpu.arch.endian();
-const std = @import("std");
-const target_util = @import("../target.zig");
-const Type = @import("../Type.zig");
-const Value = @import("../Value.zig");
-const Zcu = @import("../Zcu.zig");
diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig
@@ -1,6 +1,8 @@
base: link.File,
mf: MappedFile,
+known: Node.Known,
nodes: std.MultiArrayList(Node),
+phdrs: std.ArrayList(MappedFile.Node.Index),
symtab: std.ArrayList(Symbol),
shstrtab: StringTable,
strtab: StringTable,
@@ -85,23 +87,15 @@ pub const Node = union(enum) {
}
};
- pub const Tag = @typeInfo(Node).@"union".tag_type.?;
-
- const known_count = @typeInfo(@TypeOf(known)).@"struct".fields.len;
- const known = known: {
- const Known = enum {
- file,
- seg_rodata,
- ehdr,
- phdr,
- shdr,
- seg_text,
- seg_data,
- };
- var mut_known: std.enums.EnumFieldStruct(Known, MappedFile.Node.Index, null) = undefined;
- for (@typeInfo(Known).@"enum".fields) |field|
- @field(mut_known, field.name) = @enumFromInt(field.value);
- break :known mut_known;
+ pub const Known = struct {
+ pub const rodata: MappedFile.Node.Index = @enumFromInt(1);
+ pub const ehdr: MappedFile.Node.Index = @enumFromInt(2);
+ pub const phdr: MappedFile.Node.Index = @enumFromInt(3);
+ pub const shdr: MappedFile.Node.Index = @enumFromInt(4);
+ pub const text: MappedFile.Node.Index = @enumFromInt(5);
+ pub const data: MappedFile.Node.Index = @enumFromInt(6);
+
+ tls: MappedFile.Node.Index,
};
comptime {
@@ -231,6 +225,21 @@ pub const Symbol = struct {
}
}
+ pub fn flushMoved(si: Symbol.Index, elf: *Elf) void {
+ const value = elf.computeNodeVAddr(si.node(elf));
+ switch (elf.symPtr(si)) {
+ inline else => |sym, class| {
+ elf.targetStore(&sym.value, @intCast(value));
+ if (si == elf.entry_hack) {
+ @branchHint(.unlikely);
+ @field(elf.ehdrPtr(), @tagName(class)).entry = sym.value;
+ }
+ },
+ }
+ si.applyLocationRelocs(elf);
+ si.applyTargetRelocs(elf);
+ }
+
pub fn applyLocationRelocs(si: Symbol.Index, elf: *Elf) void {
for (elf.relocs.items[@intFromEnum(si.get(elf).loc_relocs)..]) |*reloc| {
if (reloc.loc != si) break;
@@ -290,8 +299,8 @@ pub const Reloc = extern struct {
};
pub fn apply(reloc: *const Reloc, elf: *Elf) void {
- const target_endian = elf.targetEndian();
- switch (reloc.loc.get(elf).ni) {
+ const loc_ni = reloc.loc.get(elf).ni;
+ switch (loc_ni) {
.none => return,
else => |ni| if (ni.hasMoved(&elf.mf)) return,
}
@@ -299,68 +308,47 @@ pub const Reloc = extern struct {
.none => return,
else => |ni| if (ni.hasMoved(&elf.mf)) return,
}
- switch (elf.shdrSlice()) {
- inline else => |shdr, class| {
- const sym = @field(elf.symSlice(), @tagName(class));
- const loc_sym = &sym[@intFromEnum(reloc.loc)];
- const loc_shndx =
- std.mem.toNative(@TypeOf(loc_sym.shndx), loc_sym.shndx, target_endian);
+ const loc_slice = loc_ni.slice(&elf.mf)[@intCast(reloc.offset)..];
+ const target_endian = elf.targetEndian();
+ switch (elf.symtabSlice()) {
+ inline else => |symtab, class| {
+ const loc_sym = &symtab[@intFromEnum(reloc.loc)];
+ const loc_shndx = elf.targetLoad(&loc_sym.shndx);
assert(loc_shndx != std.elf.SHN_UNDEF);
- const loc_sh = &shdr[loc_shndx];
- const loc_value = std.mem.toNative(
- @TypeOf(loc_sym.value),
- loc_sym.value,
- target_endian,
- ) + reloc.offset;
- const loc_sh_addr =
- std.mem.toNative(@TypeOf(loc_sh.addr), loc_sh.addr, target_endian);
- const loc_sh_offset =
- std.mem.toNative(@TypeOf(loc_sh.offset), loc_sh.offset, target_endian);
- const loc_file_offset: usize = @intCast(loc_value - loc_sh_addr + loc_sh_offset);
- const target_sym = &sym[@intFromEnum(reloc.target)];
- const target_value = std.mem.toNative(
- @TypeOf(target_sym.value),
- target_sym.value,
- target_endian,
- ) +% @as(u64, @bitCast(reloc.addend));
+ const loc_value = elf.targetLoad(&loc_sym.value) + reloc.offset;
+ const target_sym = &symtab[@intFromEnum(reloc.target)];
+ const target_value =
+ elf.targetLoad(&target_sym.value) +% @as(u64, @bitCast(reloc.addend));
switch (elf.ehdrField(.machine)) {
else => |machine| @panic(@tagName(machine)),
.X86_64 => switch (reloc.type.X86_64) {
else => |kind| @panic(@tagName(kind)),
.@"64" => std.mem.writeInt(
u64,
- elf.mf.contents[loc_file_offset..][0..8],
+ loc_slice[0..8],
target_value,
target_endian,
),
.PC32 => std.mem.writeInt(
i32,
- elf.mf.contents[loc_file_offset..][0..4],
+ loc_slice[0..4],
@intCast(@as(i64, @bitCast(target_value -% loc_value))),
target_endian,
),
.@"32" => std.mem.writeInt(
u32,
- elf.mf.contents[loc_file_offset..][0..4],
+ loc_slice[0..4],
@intCast(target_value),
target_endian,
),
.TPOFF32 => {
const phdr = @field(elf.phdrSlice(), @tagName(class));
- const ph = &phdr[4];
- assert(std.mem.toNative(
- @TypeOf(ph.type),
- ph.type,
- target_endian,
- ) == std.elf.PT_TLS);
+ const ph = &phdr[elf.getNode(elf.known.tls).segment];
+ assert(elf.targetLoad(&ph.type) == std.elf.PT_TLS);
std.mem.writeInt(
i32,
- elf.mf.contents[loc_file_offset..][0..4],
- @intCast(@as(i64, @bitCast(target_value -% std.mem.toNative(
- @TypeOf(ph.memsz),
- ph.memsz,
- target_endian,
- )))),
+ loc_slice[0..4],
+ @intCast(@as(i64, @bitCast(target_value -% elf.targetLoad(&ph.memsz)))),
target_endian,
);
},
@@ -475,7 +463,11 @@ fn create(
.stack_size = 0,
},
.mf = try .init(file, comp.gpa),
+ .known = .{
+ .tls = .none,
+ },
.nodes = .empty,
+ .phdrs = .empty,
.symtab = .empty,
.shstrtab = .{
.map = .empty,
@@ -488,7 +480,7 @@ fn create(
.globals = .empty,
.navs = .empty,
.uavs = .empty,
- .lazy = .initFill(.{
+ .lazy = comptime .initFill(.{
.map = .empty,
.pending_index = 0,
}),
@@ -498,18 +490,7 @@ fn create(
};
errdefer elf.deinit();
- switch (class) {
- .NONE, _ => unreachable,
- inline else => |ct_class| try elf.initHeaders(
- ct_class,
- data,
- osabi,
- @"type",
- machine,
- maybe_interp,
- ),
- }
-
+ try elf.initHeaders(class, data, osabi, @"type", machine, maybe_interp);
return elf;
}
@@ -517,6 +498,7 @@ pub fn deinit(elf: *Elf) void {
const gpa = elf.base.comp.gpa;
elf.mf.deinit(gpa);
elf.nodes.deinit(gpa);
+ elf.phdrs.deinit(gpa);
elf.symtab.deinit(gpa);
elf.shstrtab.map.deinit(gpa);
elf.strtab.map.deinit(gpa);
@@ -531,7 +513,7 @@ pub fn deinit(elf: *Elf) void {
fn initHeaders(
elf: *Elf,
- comptime class: std.elf.CLASS,
+ class: std.elf.CLASS,
data: std.elf.DATA,
osabi: std.elf.OSABI,
@"type": std.elf.ET,
@@ -540,16 +522,10 @@ fn initHeaders(
) !void {
const comp = elf.base.comp;
const gpa = comp.gpa;
- const ElfN = switch (class) {
- .NONE, _ => comptime unreachable,
- .@"32" => std.elf.Elf32,
- .@"64" => std.elf.Elf64,
- };
- const addr_align: std.mem.Alignment = comptime .fromByteUnits(@sizeOf(ElfN.Addr));
- const target_endian: std.builtin.Endian = switch (data) {
+ const addr_align: std.mem.Alignment = switch (class) {
.NONE, _ => unreachable,
- .@"2LSB" => .little,
- .@"2MSB" => .big,
+ .@"32" => .@"4",
+ .@"64" => .@"8",
};
var phnum: u32 = 0;
@@ -570,218 +546,257 @@ fn initHeaders(
break :phndx phnum;
} else undefined;
- try elf.nodes.ensureTotalCapacity(gpa, Node.known_count);
+ const expected_nodes_len = 15;
+ try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len);
+ try elf.phdrs.resize(gpa, phnum);
elf.nodes.appendAssumeCapacity(.file);
- const seg_rodata_ni = Node.known.seg_rodata;
- assert(seg_rodata_ni == try elf.mf.addOnlyChildNode(gpa, .root, .{
+ assert(Node.Known.rodata == try elf.mf.addOnlyChildNode(gpa, .root, .{
.alignment = elf.mf.flags.block_size,
.fixed = true,
.moved = true,
+ .bubbles_moved = false,
}));
elf.nodes.appendAssumeCapacity(.{ .segment = rodata_phndx });
+ elf.phdrs.items[rodata_phndx] = Node.Known.rodata;
- const ehdr_ni = Node.known.ehdr;
- assert(ehdr_ni == try elf.mf.addOnlyChildNode(gpa, seg_rodata_ni, .{
- .size = @sizeOf(ElfN.Ehdr),
- .alignment = addr_align,
- .fixed = true,
- }));
- elf.nodes.appendAssumeCapacity(.ehdr);
- {
- const ehdr: *ElfN.Ehdr = @ptrCast(@alignCast(ehdr_ni.slice(&elf.mf)));
- const EI = std.elf.EI;
- @memcpy(ehdr.ident[0..std.elf.MAGIC.len], std.elf.MAGIC);
- ehdr.ident[EI.CLASS] = @intFromEnum(class);
- ehdr.ident[EI.DATA] = @intFromEnum(data);
- ehdr.ident[EI.VERSION] = 1;
- ehdr.ident[EI.OSABI] = @intFromEnum(osabi);
- ehdr.ident[EI.ABIVERSION] = 0;
- @memset(ehdr.ident[EI.PAD..], 0);
- ehdr.type = @"type";
- ehdr.machine = machine;
- ehdr.version = 1;
- ehdr.entry = 0;
- ehdr.phoff = 0;
- ehdr.shoff = 0;
- ehdr.flags = 0;
- ehdr.ehsize = @sizeOf(ElfN.Ehdr);
- ehdr.phentsize = @sizeOf(ElfN.Phdr);
- ehdr.phnum = @min(phnum, std.elf.PN_XNUM);
- ehdr.shentsize = @sizeOf(ElfN.Shdr);
- ehdr.shnum = 1;
- ehdr.shstrndx = 0;
- if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);
+ switch (class) {
+ .NONE, _ => unreachable,
+ inline else => |ct_class| {
+ const ElfN = switch (ct_class) {
+ .NONE, _ => comptime unreachable,
+ .@"32" => std.elf.Elf32,
+ .@"64" => std.elf.Elf64,
+ };
+
+ assert(Node.Known.ehdr == try elf.mf.addOnlyChildNode(gpa, Node.Known.rodata, .{
+ .size = @sizeOf(ElfN.Ehdr),
+ .alignment = addr_align,
+ .fixed = true,
+ }));
+ elf.nodes.appendAssumeCapacity(.ehdr);
+
+ const ehdr: *ElfN.Ehdr = @ptrCast(@alignCast(Node.Known.ehdr.slice(&elf.mf)));
+ const EI = std.elf.EI;
+ @memcpy(ehdr.ident[0..std.elf.MAGIC.len], std.elf.MAGIC);
+ ehdr.ident[EI.CLASS] = @intFromEnum(class);
+ ehdr.ident[EI.DATA] = @intFromEnum(data);
+ ehdr.ident[EI.VERSION] = 1;
+ ehdr.ident[EI.OSABI] = @intFromEnum(osabi);
+ ehdr.ident[EI.ABIVERSION] = 0;
+ @memset(ehdr.ident[EI.PAD..], 0);
+ ehdr.type = @"type";
+ ehdr.machine = machine;
+ ehdr.version = 1;
+ ehdr.entry = 0;
+ ehdr.phoff = 0;
+ ehdr.shoff = 0;
+ ehdr.flags = 0;
+ ehdr.ehsize = @sizeOf(ElfN.Ehdr);
+ ehdr.phentsize = @sizeOf(ElfN.Phdr);
+ ehdr.phnum = @min(phnum, std.elf.PN_XNUM);
+ ehdr.shentsize = @sizeOf(ElfN.Shdr);
+ ehdr.shnum = 1;
+ ehdr.shstrndx = std.elf.SHN_UNDEF;
+ if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);
+ },
}
- const phdr_ni = Node.known.phdr;
- assert(phdr_ni == try elf.mf.addLastChildNode(gpa, seg_rodata_ni, .{
- .size = @sizeOf(ElfN.Phdr) * phnum,
+ assert(Node.Known.phdr == try elf.mf.addLastChildNode(gpa, Node.Known.rodata, .{
+ .size = elf.ehdrField(.phentsize) * elf.ehdrField(.phnum),
.alignment = addr_align,
.moved = true,
.resized = true,
+ .bubbles_moved = false,
}));
elf.nodes.appendAssumeCapacity(.{ .segment = phdr_phndx });
+ elf.phdrs.items[phdr_phndx] = Node.Known.phdr;
- const shdr_ni = Node.known.shdr;
- assert(shdr_ni == try elf.mf.addLastChildNode(gpa, seg_rodata_ni, .{
- .size = @sizeOf(ElfN.Shdr),
+ assert(Node.Known.shdr == try elf.mf.addLastChildNode(gpa, Node.Known.rodata, .{
+ .size = elf.ehdrField(.shentsize) * elf.ehdrField(.shnum),
.alignment = addr_align,
}));
elf.nodes.appendAssumeCapacity(.shdr);
- const seg_text_ni = Node.known.seg_text;
- assert(seg_text_ni == try elf.mf.addLastChildNode(gpa, .root, .{
+ assert(Node.Known.text == try elf.mf.addLastChildNode(gpa, .root, .{
.alignment = elf.mf.flags.block_size,
.moved = true,
+ .bubbles_moved = false,
}));
elf.nodes.appendAssumeCapacity(.{ .segment = text_phndx });
+ elf.phdrs.items[text_phndx] = Node.Known.text;
- const seg_data_ni = Node.known.seg_data;
- assert(seg_data_ni == try elf.mf.addLastChildNode(gpa, .root, .{
+ assert(Node.Known.data == try elf.mf.addLastChildNode(gpa, .root, .{
.alignment = elf.mf.flags.block_size,
.moved = true,
+ .bubbles_moved = false,
}));
elf.nodes.appendAssumeCapacity(.{ .segment = data_phndx });
+ elf.phdrs.items[data_phndx] = Node.Known.data;
- assert(elf.nodes.len == Node.known_count);
-
- {
- const phdr: []ElfN.Phdr = @ptrCast(@alignCast(phdr_ni.slice(&elf.mf)));
- const ph_phdr = &phdr[phdr_phndx];
- ph_phdr.* = .{
- .type = std.elf.PT_PHDR,
- .offset = 0,
- .vaddr = 0,
- .paddr = 0,
- .filesz = 0,
- .memsz = 0,
- .flags = .{ .R = true },
- .@"align" = @intCast(phdr_ni.alignment(&elf.mf).toByteUnits()),
- };
- if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr);
+ var ph_vaddr: u32 = switch (elf.ehdrField(.type)) {
+ else => 0,
+ .EXEC => switch (elf.ehdrField(.machine)) {
+ .@"386" => 0x400000,
+ .AARCH64, .X86_64 => 0x200000,
+ .PPC, .PPC64 => 0x10000000,
+ .S390, .S390_OLD => 0x1000000,
+ .OLD_SPARCV9, .SPARCV9 => 0x100000,
+ else => 0x10000,
+ },
+ };
+ switch (class) {
+ .NONE, _ => unreachable,
+ inline else => |ct_class| {
+ const ElfN = switch (ct_class) {
+ .NONE, _ => comptime unreachable,
+ .@"32" => std.elf.Elf32,
+ .@"64" => std.elf.Elf64,
+ };
+ const target_endian = elf.targetEndian();
- if (maybe_interp) |_| {
- const ph_interp = &phdr[interp_phndx];
- ph_interp.* = .{
- .type = std.elf.PT_INTERP,
+ const phdr: []ElfN.Phdr = @ptrCast(@alignCast(Node.Known.phdr.slice(&elf.mf)));
+ const ph_phdr = &phdr[phdr_phndx];
+ ph_phdr.* = .{
+ .type = std.elf.PT_PHDR,
.offset = 0,
.vaddr = 0,
.paddr = 0,
.filesz = 0,
.memsz = 0,
.flags = .{ .R = true },
- .@"align" = 1,
+ .@"align" = @intCast(Node.Known.phdr.alignment(&elf.mf).toByteUnits()),
};
- if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_interp);
- }
-
- const ph_rodata = &phdr[rodata_phndx];
- ph_rodata.* = .{
- .type = std.elf.PT_NULL,
- .offset = 0,
- .vaddr = 0,
- .paddr = 0,
- .filesz = 0,
- .memsz = 0,
- .flags = .{ .R = true },
- .@"align" = @intCast(seg_rodata_ni.alignment(&elf.mf).toByteUnits()),
- };
- if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_rodata);
-
- const ph_text = &phdr[text_phndx];
- ph_text.* = .{
- .type = std.elf.PT_NULL,
- .offset = 0,
- .vaddr = 0,
- .paddr = 0,
- .filesz = 0,
- .memsz = 0,
- .flags = .{ .R = true, .X = true },
- .@"align" = @intCast(seg_text_ni.alignment(&elf.mf).toByteUnits()),
- };
- if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_text);
-
- const ph_data = &phdr[data_phndx];
- ph_data.* = .{
- .type = std.elf.PT_NULL,
- .offset = 0,
- .vaddr = 0,
- .paddr = 0,
- .filesz = 0,
- .memsz = 0,
- .flags = .{ .R = true, .W = true },
- .@"align" = @intCast(seg_data_ni.alignment(&elf.mf).toByteUnits()),
- };
- if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_data);
+ if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr);
+
+ if (maybe_interp) |_| {
+ const ph_interp = &phdr[interp_phndx];
+ ph_interp.* = .{
+ .type = std.elf.PT_INTERP,
+ .offset = 0,
+ .vaddr = 0,
+ .paddr = 0,
+ .filesz = 0,
+ .memsz = 0,
+ .flags = .{ .R = true },
+ .@"align" = 1,
+ };
+ if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_interp);
+ }
- if (comp.config.any_non_single_threaded) {
- const ph_tls = &phdr[tls_phndx];
- ph_tls.* = .{
- .type = std.elf.PT_TLS,
+ _, const rodata_size = Node.Known.rodata.location(&elf.mf).resolve(&elf.mf);
+ const ph_rodata = &phdr[rodata_phndx];
+ ph_rodata.* = .{
+ .type = std.elf.PT_NULL,
.offset = 0,
- .vaddr = 0,
- .paddr = 0,
- .filesz = 0,
- .memsz = 0,
+ .vaddr = ph_vaddr,
+ .paddr = ph_vaddr,
+ .filesz = @intCast(rodata_size),
+ .memsz = @intCast(rodata_size),
.flags = .{ .R = true },
- .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()),
+ .@"align" = @intCast(Node.Known.rodata.alignment(&elf.mf).toByteUnits()),
};
- if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_tls);
- }
+ if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_rodata);
+ ph_vaddr += @intCast(rodata_size);
- const sh_null: *ElfN.Shdr = @ptrCast(@alignCast(shdr_ni.slice(&elf.mf)));
- sh_null.* = .{
- .name = try elf.string(.shstrtab, ""),
- .type = std.elf.SHT_NULL,
- .flags = .{ .shf = .{} },
- .addr = 0,
- .offset = 0,
- .size = 0,
- .link = 0,
- .info = if (phnum >= std.elf.PN_XNUM) phnum else 0,
- .addralign = 0,
- .entsize = 0,
- };
- if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_null);
- }
+ _, const text_size = Node.Known.text.location(&elf.mf).resolve(&elf.mf);
+ const ph_text = &phdr[text_phndx];
+ ph_text.* = .{
+ .type = std.elf.PT_NULL,
+ .offset = 0,
+ .vaddr = ph_vaddr,
+ .paddr = ph_vaddr,
+ .filesz = @intCast(text_size),
+ .memsz = @intCast(text_size),
+ .flags = .{ .R = true, .X = true },
+ .@"align" = @intCast(Node.Known.text.alignment(&elf.mf).toByteUnits()),
+ };
+ if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_text);
+ ph_vaddr += @intCast(text_size);
- try elf.symtab.ensureTotalCapacity(gpa, 1);
- elf.symtab.addOneAssumeCapacity().* = .{
- .ni = .none,
- .loc_relocs = .none,
- .target_relocs = .none,
- .unused = 0,
- };
- assert(try elf.addSection(seg_rodata_ni, .{
- .type = std.elf.SHT_SYMTAB,
- .addralign = addr_align,
- .entsize = @sizeOf(ElfN.Sym),
- }) == .symtab);
- const symtab: *ElfN.Sym = @ptrCast(@alignCast(Symbol.Index.symtab.node(elf).slice(&elf.mf)));
- symtab.* = .{
- .name = try elf.string(.strtab, ""),
- .value = 0,
- .size = 0,
- .info = .{
- .type = .NOTYPE,
- .bind = .LOCAL,
- },
- .other = .{
- .visibility = .DEFAULT,
+ _, const data_size = Node.Known.data.location(&elf.mf).resolve(&elf.mf);
+ const ph_data = &phdr[data_phndx];
+ ph_data.* = .{
+ .type = std.elf.PT_NULL,
+ .offset = 0,
+ .vaddr = ph_vaddr,
+ .paddr = ph_vaddr,
+ .filesz = @intCast(data_size),
+ .memsz = @intCast(data_size),
+ .flags = .{ .R = true, .W = true },
+ .@"align" = @intCast(Node.Known.data.alignment(&elf.mf).toByteUnits()),
+ };
+ if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_data);
+ ph_vaddr += @intCast(data_size);
+
+ if (comp.config.any_non_single_threaded) {
+ const ph_tls = &phdr[tls_phndx];
+ ph_tls.* = .{
+ .type = std.elf.PT_TLS,
+ .offset = 0,
+ .vaddr = 0,
+ .paddr = 0,
+ .filesz = 0,
+ .memsz = 0,
+ .flags = .{ .R = true },
+ .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()),
+ };
+ if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_tls);
+ }
+
+ const sh_null: *ElfN.Shdr = @ptrCast(@alignCast(Node.Known.shdr.slice(&elf.mf)));
+ sh_null.* = .{
+ .name = try elf.string(.shstrtab, ""),
+ .type = std.elf.SHT_NULL,
+ .flags = .{ .shf = .{} },
+ .addr = 0,
+ .offset = 0,
+ .size = 0,
+ .link = 0,
+ .info = if (phnum >= std.elf.PN_XNUM) phnum else 0,
+ .addralign = 0,
+ .entsize = 0,
+ };
+ if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_null);
+
+ try elf.symtab.ensureTotalCapacity(gpa, 1);
+ elf.symtab.addOneAssumeCapacity().* = .{
+ .ni = .none,
+ .loc_relocs = .none,
+ .target_relocs = .none,
+ .unused = 0,
+ };
+ assert(try elf.addSection(Node.Known.rodata, .{
+ .type = std.elf.SHT_SYMTAB,
+ .addralign = addr_align,
+ .entsize = @sizeOf(ElfN.Sym),
+ }) == .symtab);
+
+ const symtab: *ElfN.Sym = @ptrCast(@alignCast(Symbol.Index.symtab.node(elf).slice(&elf.mf)));
+ symtab.* = .{
+ .name = try elf.string(.strtab, ""),
+ .value = 0,
+ .size = 0,
+ .info = .{
+ .type = .NOTYPE,
+ .bind = .LOCAL,
+ },
+ .other = .{
+ .visibility = .DEFAULT,
+ },
+ .shndx = std.elf.SHN_UNDEF,
+ };
+
+ const ehdr = @field(elf.ehdrPtr(), @tagName(ct_class));
+ ehdr.shstrndx = ehdr.shnum;
},
- .shndx = std.elf.SHN_UNDEF,
- };
- {
- const ehdr = @field(elf.ehdrPtr(), @tagName(class));
- ehdr.shstrndx = ehdr.shnum;
}
- assert(try elf.addSection(seg_rodata_ni, .{
+ assert(try elf.addSection(Node.Known.rodata, .{
.type = std.elf.SHT_STRTAB,
.addralign = elf.mf.flags.block_size,
.entsize = 1,
}) == .shstrtab);
- assert(try elf.addSection(seg_rodata_ni, .{
+ assert(try elf.addSection(Node.Known.rodata, .{
.type = std.elf.SHT_STRTAB,
.addralign = elf.mf.flags.block_size,
.entsize = 1,
@@ -793,75 +808,118 @@ fn initHeaders(
Symbol.Index.shstrtab.node(elf).slice(&elf.mf)[0] = 0;
Symbol.Index.strtab.node(elf).slice(&elf.mf)[0] = 0;
- assert(try elf.addSection(seg_rodata_ni, .{
+ if (maybe_interp) |interp| {
+ try elf.nodes.ensureUnusedCapacity(gpa, 1);
+ const interp_ni = try elf.mf.addLastChildNode(gpa, Node.Known.rodata, .{
+ .size = interp.len + 1,
+ .moved = true,
+ .resized = true,
+ });
+ elf.nodes.appendAssumeCapacity(.{ .segment = interp_phndx });
+ elf.phdrs.items[interp_phndx] = interp_ni;
+
+ const sec_interp_si = try elf.addSection(interp_ni, .{
+ .name = ".interp",
+ .size = @intCast(interp.len + 1),
+ .flags = .{ .ALLOC = true },
+ });
+ const sec_interp = sec_interp_si.node(elf).slice(&elf.mf);
+ @memcpy(sec_interp[0..interp.len], interp);
+ sec_interp[interp.len] = 0;
+ }
+ assert(try elf.addSection(Node.Known.rodata, .{
.name = ".rodata",
.flags = .{ .ALLOC = true },
.addralign = elf.mf.flags.block_size,
}) == .rodata);
- assert(try elf.addSection(seg_text_ni, .{
+ assert(try elf.addSection(Node.Known.text, .{
.name = ".text",
.flags = .{ .ALLOC = true, .EXECINSTR = true },
.addralign = elf.mf.flags.block_size,
}) == .text);
- assert(try elf.addSection(seg_data_ni, .{
+ assert(try elf.addSection(Node.Known.data, .{
.name = ".data",
.flags = .{ .WRITE = true, .ALLOC = true },
.addralign = elf.mf.flags.block_size,
}) == .data);
if (comp.config.any_non_single_threaded) {
try elf.nodes.ensureUnusedCapacity(gpa, 1);
- const seg_tls_ni = try elf.mf.addLastChildNode(gpa, seg_data_ni, .{
+ elf.known.tls = try elf.mf.addLastChildNode(gpa, Node.Known.rodata, .{
.alignment = elf.mf.flags.block_size,
.moved = true,
});
elf.nodes.appendAssumeCapacity(.{ .segment = tls_phndx });
+ elf.phdrs.items[tls_phndx] = elf.known.tls;
- assert(try elf.addSection(seg_tls_ni, .{
+ assert(try elf.addSection(elf.known.tls, .{
.name = ".tdata",
.flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
.addralign = elf.mf.flags.block_size,
}) == .tdata);
}
- if (maybe_interp) |interp| {
- try elf.nodes.ensureUnusedCapacity(gpa, 1);
- const seg_interp_ni = try elf.mf.addLastChildNode(gpa, seg_rodata_ni, .{
- .size = interp.len + 1,
- .moved = true,
- .resized = true,
- });
- elf.nodes.appendAssumeCapacity(.{ .segment = interp_phndx });
-
- const sec_interp_si = try elf.addSection(seg_interp_ni, .{
- .name = ".interp",
- .size = @intCast(interp.len + 1),
- .flags = .{ .ALLOC = true },
- });
- const sec_interp = sec_interp_si.node(elf).slice(&elf.mf);
- @memcpy(sec_interp[0..interp.len], interp);
- sec_interp[interp.len] = 0;
- }
+ assert(elf.nodes.len == expected_nodes_len);
}
-fn getNode(elf: *Elf, ni: MappedFile.Node.Index) Node {
+fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node {
return elf.nodes.get(@intFromEnum(ni));
}
+fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
+ const parent_vaddr = parent_vaddr: {
+ const parent_si = switch (elf.getNode(ni.parent(&elf.mf))) {
+ .file, .ehdr, .shdr => unreachable,
+ .segment => |phndx| break :parent_vaddr switch (elf.phdrSlice()) {
+ inline else => |ph| elf.targetLoad(&ph[phndx].vaddr),
+ },
+ .section => |si| si,
+ inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| mi.symbol(elf),
+ };
+ break :parent_vaddr switch (elf.symPtr(parent_si)) {
+ inline else => |sym| elf.targetLoad(&sym.value),
+ };
+ };
+ const offset, _ = ni.location(&elf.mf).resolve(&elf.mf);
+ return parent_vaddr + offset;
+}
-pub fn identClass(elf: *Elf) std.elf.CLASS {
+pub fn identClass(elf: *const Elf) std.elf.CLASS {
return @enumFromInt(elf.mf.contents[std.elf.EI.CLASS]);
}
-
-pub fn identData(elf: *Elf) std.elf.DATA {
+pub fn identData(elf: *const Elf) std.elf.DATA {
return @enumFromInt(elf.mf.contents[std.elf.EI.DATA]);
}
-fn endianForData(data: std.elf.DATA) std.builtin.Endian {
- return switch (data) {
+
+pub fn targetEndian(elf: *const Elf) std.builtin.Endian {
+ return switch (elf.identData()) {
.NONE, _ => unreachable,
.@"2LSB" => .little,
.@"2MSB" => .big,
};
}
-pub fn targetEndian(elf: *Elf) std.builtin.Endian {
- return endianForData(elf.identData());
+fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {
+ const Child = @typeInfo(@TypeOf(ptr)).pointer.child;
+ return switch (@typeInfo(Child)) {
+ else => @compileError(@typeName(Child)),
+ .int => std.mem.toNative(Child, ptr.*, elf.targetEndian()),
+ .@"enum" => |@"enum"| @enumFromInt(elf.targetLoad(@as(*@"enum".tag_type, @ptrCast(ptr)))),
+ .@"struct" => |@"struct"| @bitCast(
+ elf.targetLoad(@as(*@"struct".backing_integer.?, @ptrCast(ptr))),
+ ),
+ };
+}
+fn targetStore(elf: *const Elf, ptr: anytype, val: @typeInfo(@TypeOf(ptr)).pointer.child) void {
+ const Child = @typeInfo(@TypeOf(ptr)).pointer.child;
+ return switch (@typeInfo(Child)) {
+ else => @compileError(@typeName(Child)),
+ .int => ptr.* = std.mem.nativeTo(Child, val, elf.targetEndian()),
+ .@"enum" => |@"enum"| elf.targetStore(
+ @as(*@"enum".tag_type, @ptrCast(ptr)),
+ @intFromEnum(val),
+ ),
+ .@"struct" => |@"struct"| elf.targetStore(
+ @as(*@"struct".backing_integer.?, @ptrCast(ptr)),
+ @bitCast(val),
+ ),
+ };
}
pub const EhdrPtr = union(std.elf.CLASS) {
@@ -870,7 +928,7 @@ pub const EhdrPtr = union(std.elf.CLASS) {
@"64": *std.elf.Elf64.Ehdr,
};
pub fn ehdrPtr(elf: *Elf) EhdrPtr {
- const slice = Node.known.ehdr.slice(&elf.mf);
+ const slice = Node.Known.ehdr.slice(&elf.mf);
return switch (elf.identClass()) {
.NONE, _ => unreachable,
inline else => |class| @unionInit(
@@ -882,26 +940,12 @@ pub fn ehdrPtr(elf: *Elf) EhdrPtr {
}
pub fn ehdrField(
elf: *Elf,
- comptime field: enum { type, machine },
-) @FieldType(std.elf.Elf32.Ehdr, @tagName(field)) {
- return @enumFromInt(std.mem.toNative(
- @typeInfo(@FieldType(std.elf.Elf32.Ehdr, @tagName(field))).@"enum".tag_type,
- @intFromEnum(switch (elf.ehdrPtr()) {
- inline else => |ehdr| @field(ehdr, @tagName(field)),
- }),
- elf.targetEndian(),
- ));
-}
-
-fn baseAddrForType(@"type": std.elf.ET) u64 {
- return switch (@"type") {
- else => 0,
- .EXEC => 0x1000000,
+ comptime field: std.meta.FieldEnum(std.elf.Elf64.Ehdr),
+) @FieldType(std.elf.Elf64.Ehdr, @tagName(field)) {
+ return switch (elf.ehdrPtr()) {
+ inline else => |ehdr| elf.targetLoad(&@field(ehdr, @tagName(field))),
};
}
-pub fn baseAddr(elf: *Elf) u64 {
- return baseAddrForType(elf.ehdrField(.type));
-}
pub const PhdrSlice = union(std.elf.CLASS) {
NONE: noreturn,
@@ -909,7 +953,7 @@ pub const PhdrSlice = union(std.elf.CLASS) {
@"64": []std.elf.Elf64.Phdr,
};
pub fn phdrSlice(elf: *Elf) PhdrSlice {
- const slice = Node.known.phdr.slice(&elf.mf);
+ const slice = Node.Known.phdr.slice(&elf.mf);
return switch (elf.identClass()) {
.NONE, _ => unreachable,
inline else => |class| @unionInit(
@@ -926,7 +970,7 @@ pub const ShdrSlice = union(std.elf.CLASS) {
@"64": []std.elf.Elf64.Shdr,
};
pub fn shdrSlice(elf: *Elf) ShdrSlice {
- const slice = Node.known.shdr.slice(&elf.mf);
+ const slice = Node.Known.shdr.slice(&elf.mf);
return switch (elf.identClass()) {
.NONE, _ => unreachable,
inline else => |class| @unionInit(
@@ -937,17 +981,17 @@ pub fn shdrSlice(elf: *Elf) ShdrSlice {
};
}
-pub const SymSlice = union(std.elf.CLASS) {
+pub const SymtabSlice = union(std.elf.CLASS) {
NONE: noreturn,
@"32": []std.elf.Elf32.Sym,
@"64": []std.elf.Elf64.Sym,
};
-pub fn symSlice(elf: *Elf) SymSlice {
+pub fn symtabSlice(elf: *Elf) SymtabSlice {
const slice = Symbol.Index.symtab.node(elf).slice(&elf.mf);
return switch (elf.identClass()) {
.NONE, _ => unreachable,
inline else => |class| @unionInit(
- SymSlice,
+ SymtabSlice,
@tagName(class),
@ptrCast(@alignCast(slice)),
),
@@ -960,7 +1004,7 @@ pub const SymPtr = union(std.elf.CLASS) {
@"64": *std.elf.Elf64.Sym,
};
pub fn symPtr(elf: *Elf, si: Symbol.Index) SymPtr {
- return switch (elf.symSlice()) {
+ return switch (elf.symtabSlice()) {
inline else => |sym, class| @unionInit(SymPtr, @tagName(class), &sym[@intFromEnum(si)]),
};
}
@@ -1123,7 +1167,9 @@ pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target_si: Symbol.In
} },
},
);
- return 0;
+ return switch (elf.symPtr(target_si)) {
+ inline else => |sym| elf.targetLoad(&sym.value),
+ };
}
fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
@@ -1135,21 +1181,19 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
entsize: std.elf.Word = 0,
}) !Symbol.Index {
const gpa = elf.base.comp.gpa;
- const target_endian = elf.targetEndian();
try elf.nodes.ensureUnusedCapacity(gpa, 1);
try elf.symtab.ensureUnusedCapacity(gpa, 1);
const shstrtab_entry = try elf.string(.shstrtab, opts.name);
const shndx, const shdr_size = shndx: switch (elf.ehdrPtr()) {
inline else => |ehdr| {
- const shentsize = std.mem.toNative(@TypeOf(ehdr.shentsize), ehdr.shentsize, target_endian);
- const shndx = std.mem.toNative(@TypeOf(ehdr.shnum), ehdr.shnum, target_endian);
+ const shndx = elf.targetLoad(&ehdr.shnum);
const shnum = shndx + 1;
- ehdr.shnum = std.mem.nativeTo(@TypeOf(ehdr.shnum), shnum, target_endian);
- break :shndx .{ shndx, shentsize * shnum };
+ elf.targetStore(&ehdr.shnum, shnum);
+ break :shndx .{ shndx, elf.targetLoad(&ehdr.shentsize) * shnum };
},
};
- try Node.known.shdr.resize(&elf.mf, gpa, shdr_size);
+ try Node.Known.shdr.resize(&elf.mf, gpa, shdr_size);
const ni = try elf.mf.addLastChildNode(gpa, segment_ni, .{
.alignment = opts.addralign,
.size = opts.size,
@@ -1179,7 +1223,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
.addralign = @intCast(opts.addralign.toByteUnits()),
.entsize = opts.entsize,
};
- if (target_endian != native_endian) std.mem.byteSwapAllFields(@TypeOf(sh.*), sh);
+ if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(@TypeOf(sh.*), sh);
},
}
return si;
@@ -1188,37 +1232,29 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
fn renameSection(elf: *Elf, si: Symbol.Index, name: []const u8) !void {
const strtab_entry = try elf.string(.strtab, name);
const shstrtab_entry = try elf.string(.shstrtab, name);
- const target_endian = elf.targetEndian();
switch (elf.shdrSlice()) {
inline else => |shdr, class| {
const sym = @field(elf.symPtr(si), @tagName(class));
- sym.name = std.mem.nativeTo(@TypeOf(sym.name), strtab_entry, target_endian);
- const shndx = std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian);
- const sh = &shdr[shndx];
- sh.name = std.mem.nativeTo(@TypeOf(sh.name), shstrtab_entry, target_endian);
+ elf.targetStore(&sym.name, strtab_entry);
+ const sh = &shdr[elf.targetLoad(&sym.shndx)];
+ elf.targetStore(&sh.name, shstrtab_entry);
},
}
}
fn linkSections(elf: *Elf, si: Symbol.Index, link_si: Symbol.Index) !void {
- const target_endian = elf.targetEndian();
switch (elf.shdrSlice()) {
- inline else => |shdr, class| {
- const sym = @field(elf.symPtr(si), @tagName(class));
- const shndx = std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian);
- shdr[shndx].link = @field(elf.symPtr(link_si), @tagName(class)).shndx;
- },
+ inline else => |shdr, class| shdr[
+ elf.targetLoad(&@field(elf.symPtr(si), @tagName(class)).shndx)
+ ].link = @field(elf.symPtr(link_si), @tagName(class)).shndx,
}
}
fn sectionName(elf: *Elf, si: Symbol.Index) [:0]const u8 {
- const target_endian = elf.targetEndian();
- const name = Symbol.Index.shstrtab.node(elf).slice(&elf.mf)[name: switch (elf.shdrSlice()) {
- inline else => |shndx, class| {
- const sym = @field(elf.symPtr(si), @tagName(class));
- const sh = &shndx[std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian)];
- break :name std.mem.toNative(@TypeOf(sh.name), sh.name, target_endian);
- },
+ const name = Symbol.Index.shstrtab.node(elf).slice(&elf.mf)[switch (elf.shdrSlice()) {
+ inline else => |shndx, class| elf.targetLoad(
+ &shndx[elf.targetLoad(&@field(elf.symPtr(si), @tagName(class)).shndx)].name,
+ ),
}..];
return name[0..std.mem.indexOfScalar(u8, name, 0).? :0];
}
@@ -1243,7 +1279,7 @@ pub fn addReloc(
) !void {
const gpa = elf.base.comp.gpa;
const target = target_si.get(elf);
- const ri: link.File.Elf2.Reloc.Index = @enumFromInt(elf.relocs.items.len);
+ const ri: Reloc.Index = @enumFromInt(elf.relocs.items.len);
(try elf.relocs.addOne(gpa)).* = .{
.type = @"type",
.prev = .none,
@@ -1332,10 +1368,8 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
error.WriteFailed => return error.OutOfMemory,
else => |e| return e,
};
- const target_endian = elf.targetEndian();
switch (elf.symPtr(si)) {
- inline else => |sym| sym.size =
- std.mem.nativeTo(@TypeOf(sym.size), @intCast(nw.interface.end), target_endian),
+ inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)),
}
si.applyLocationRelocs(elf);
}
@@ -1463,10 +1497,8 @@ fn updateFuncInner(
error.WriteFailed => return nw.err.?,
else => |e| return e,
};
- const target_endian = elf.targetEndian();
switch (elf.symPtr(si)) {
- inline else => |sym| sym.size =
- std.mem.nativeTo(@TypeOf(sym.size), @intCast(nw.interface.end), target_endian),
+ inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)),
}
si.applyLocationRelocs(elf);
}
@@ -1634,10 +1666,8 @@ fn flushUav(
error.WriteFailed => return error.OutOfMemory,
else => |e| return e,
};
- const target_endian = elf.targetEndian();
switch (elf.symPtr(si)) {
- inline else => |sym| sym.size =
- std.mem.nativeTo(@TypeOf(sym.size), @intCast(nw.interface.end), target_endian),
+ inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)),
}
si.applyLocationRelocs(elf);
}
@@ -1689,167 +1719,121 @@ fn flushLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
.none,
.{ .atom_index = @intFromEnum(si) },
);
- const target_endian = elf.targetEndian();
switch (elf.symPtr(si)) {
- inline else => |sym| sym.size =
- std.mem.nativeTo(@TypeOf(sym.size), @intCast(nw.interface.end), target_endian),
+ inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)),
}
si.applyLocationRelocs(elf);
}
fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
- const target_endian = elf.targetEndian();
- const file_offset = ni.fileLocation(&elf.mf, false).offset;
- const node = elf.getNode(ni);
- switch (node) {
- else => |tag| @panic(@tagName(tag)),
- .ehdr => assert(file_offset == 0),
+ switch (elf.getNode(ni)) {
+ .file => unreachable,
+ .ehdr => assert(ni.fileLocation(&elf.mf, false).offset == 0),
.shdr => switch (elf.ehdrPtr()) {
- inline else => |ehdr| ehdr.shoff =
- std.mem.nativeTo(@TypeOf(ehdr.shoff), @intCast(file_offset), target_endian),
+ inline else => |ehdr| elf.targetStore(
+ &ehdr.shoff,
+ @intCast(ni.fileLocation(&elf.mf, false).offset),
+ ),
},
.segment => |phndx| switch (elf.phdrSlice()) {
inline else => |phdr, class| {
const ph = &phdr[phndx];
- switch (std.mem.toNative(@TypeOf(ph.type), ph.type, target_endian)) {
+ elf.targetStore(&ph.offset, @intCast(ni.fileLocation(&elf.mf, false).offset));
+ switch (elf.targetLoad(&ph.type)) {
else => unreachable,
- std.elf.PT_NULL, std.elf.PT_LOAD, std.elf.PT_DYNAMIC, std.elf.PT_INTERP => {},
- std.elf.PT_PHDR => {
- const ehdr = @field(elf.ehdrPtr(), @tagName(class));
- ehdr.phoff =
- std.mem.nativeTo(@TypeOf(ehdr.phoff), @intCast(file_offset), target_endian);
- },
+ std.elf.PT_NULL, std.elf.PT_LOAD => return,
+ std.elf.PT_DYNAMIC, std.elf.PT_INTERP => {},
+ std.elf.PT_PHDR => @field(elf.ehdrPtr(), @tagName(class)).phoff = ph.offset,
std.elf.PT_TLS => {},
}
- ph.offset = std.mem.nativeTo(@TypeOf(ph.offset), @intCast(file_offset), target_endian);
- ph.vaddr = std.mem.nativeTo(
- @TypeOf(ph.vaddr),
- @intCast(elf.baseAddr() + file_offset),
- target_endian,
- );
+ elf.targetStore(&ph.vaddr, @intCast(elf.computeNodeVAddr(ni)));
ph.paddr = ph.vaddr;
},
},
.section => |si| switch (elf.shdrSlice()) {
inline else => |shdr, class| {
const sym = @field(elf.symPtr(si), @tagName(class));
- const shndx = std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian);
- const sh = &shdr[shndx];
- const flags: @TypeOf(sh.flags) = @bitCast(std.mem.toNative(
- @typeInfo(@TypeOf(sh.flags)).@"struct".backing_integer.?,
- @bitCast(sh.flags),
- target_endian,
- ));
- if (flags.shf.ALLOC) {
- sym.value = std.mem.nativeTo(
- @TypeOf(sym.value),
- @intCast(elf.baseAddr() + file_offset),
- target_endian,
- );
- sh.addr = sym.value;
+ const sh = &shdr[elf.targetLoad(&sym.shndx)];
+ elf.targetStore(&sh.offset, @intCast(ni.fileLocation(&elf.mf, false).offset));
+ const flags = elf.targetLoad(&sh.flags).shf;
+ if (flags.ALLOC) {
+ elf.targetStore(&sh.addr, @intCast(elf.computeNodeVAddr(ni)));
+ if (!flags.TLS) sym.value = sh.addr;
}
- sh.offset = std.mem.nativeTo(@TypeOf(sh.offset), @intCast(file_offset), target_endian);
},
},
- .nav, .uav, .lazy_code, .lazy_const_data => {
- const si = switch (node) {
- else => unreachable,
- inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| mi.symbol(elf),
- };
- switch (elf.shdrSlice()) {
- inline else => |shdr, class| {
- const sym = @field(elf.symPtr(si), @tagName(class));
- const sh = &shdr[std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian)];
- const flags: @TypeOf(sh.flags) = @bitCast(std.mem.toNative(
- @typeInfo(@TypeOf(sh.flags)).@"struct".backing_integer.?,
- @bitCast(sh.flags),
- target_endian,
- ));
- const sh_addr = if (flags.shf.TLS)
- 0
- else
- std.mem.toNative(@TypeOf(sh.addr), sh.addr, target_endian);
- const sh_offset = std.mem.toNative(@TypeOf(sh.offset), sh.offset, target_endian);
- sym.value = std.mem.nativeTo(
- @TypeOf(sym.value),
- @intCast(file_offset - sh_offset + sh_addr),
- target_endian,
- );
- if (si == elf.entry_hack) @field(elf.ehdrPtr(), @tagName(class)).entry = sym.value;
- },
- }
- si.applyLocationRelocs(elf);
- si.applyTargetRelocs(elf);
- },
+ inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| mi.symbol(elf).flushMoved(elf),
}
try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
}
fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
- const target_endian = elf.targetEndian();
_, const size = ni.location(&elf.mf).resolve(&elf.mf);
- const node = elf.getNode(ni);
- switch (node) {
- else => |tag| @panic(@tagName(tag)),
- .file, .shdr => {},
+ switch (elf.getNode(ni)) {
+ .file => {},
+ .ehdr => unreachable,
+ .shdr => {},
.segment => |phndx| switch (elf.phdrSlice()) {
inline else => |phdr| {
+ assert(elf.phdrs.items[phndx] == ni);
const ph = &phdr[phndx];
- ph.filesz = std.mem.nativeTo(@TypeOf(ph.filesz), @intCast(size), target_endian);
- ph.memsz = ph.filesz;
- switch (std.mem.toNative(@TypeOf(ph.type), ph.type, target_endian)) {
- else => unreachable,
- std.elf.PT_NULL => {
- if (size > 0) ph.type = std.mem.nativeTo(
- @TypeOf(ph.type),
- std.elf.PT_LOAD,
- target_endian,
- );
- },
- std.elf.PT_LOAD => {
- if (size == 0) ph.type = std.mem.nativeTo(
- @TypeOf(ph.type),
- std.elf.PT_NULL,
- target_endian,
- );
- },
- std.elf.PT_DYNAMIC, std.elf.PT_INTERP, std.elf.PT_PHDR => {},
- std.elf.PT_TLS => try ni.childrenMoved(elf.base.comp.gpa, &elf.mf),
+ elf.targetStore(&ph.filesz, @intCast(size));
+ if (size > elf.targetLoad(&ph.memsz)) {
+ const memsz = ni.alignment(&elf.mf).forward(@intCast(size * 4));
+ elf.targetStore(&ph.memsz, @intCast(memsz));
+ switch (elf.targetLoad(&ph.type)) {
+ else => unreachable,
+ std.elf.PT_NULL => if (size > 0) elf.targetStore(&ph.type, std.elf.PT_LOAD),
+ std.elf.PT_LOAD => if (size == 0) elf.targetStore(&ph.type, std.elf.PT_NULL),
+ std.elf.PT_DYNAMIC, std.elf.PT_INTERP, std.elf.PT_PHDR => return,
+ std.elf.PT_TLS => return ni.childrenMoved(elf.base.comp.gpa, &elf.mf),
+ }
+ var vaddr = elf.targetLoad(&ph.vaddr);
+ var new_phndx = phndx;
+ for (phdr[phndx + 1 ..], phndx + 1..) |*next_ph, next_phndx| {
+ switch (elf.targetLoad(&next_ph.type)) {
+ else => unreachable,
+ std.elf.PT_NULL, std.elf.PT_LOAD => {},
+ std.elf.PT_DYNAMIC,
+ std.elf.PT_INTERP,
+ std.elf.PT_PHDR,
+ std.elf.PT_TLS,
+ => break,
+ }
+ const next_vaddr = elf.targetLoad(&next_ph.vaddr);
+ if (vaddr + memsz <= next_vaddr) break;
+ vaddr = next_vaddr + elf.targetLoad(&next_ph.memsz);
+ std.mem.swap(@TypeOf(ph.*), &phdr[new_phndx], next_ph);
+ const next_ni = elf.phdrs.items[next_phndx];
+ elf.phdrs.items[new_phndx] = next_ni;
+ elf.nodes.items(.data)[@intFromEnum(next_ni)] = .{ .segment = new_phndx };
+ new_phndx = @intCast(next_phndx);
+ }
+ if (new_phndx != phndx) {
+ const new_ph = &phdr[new_phndx];
+ elf.targetStore(&new_ph.vaddr, vaddr);
+ new_ph.paddr = new_ph.vaddr;
+ elf.phdrs.items[new_phndx] = ni;
+ elf.nodes.items(.data)[@intFromEnum(ni)] = .{ .segment = new_phndx };
+ try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
+ }
}
},
},
- .section => |si| switch (elf.shdrSlice()) {
- inline else => |shdr, class| {
- const sym = @field(elf.symPtr(si), @tagName(class));
- const shndx = std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian);
- const sh = &shdr[shndx];
- switch (std.mem.toNative(@TypeOf(sh.type), sh.type, target_endian)) {
+ .section => |si| switch (elf.symPtr(si)) {
+ inline else => |sym, class| {
+ const sh = &@field(elf.shdrSlice(), @tagName(class))[elf.targetLoad(&sym.shndx)];
+ elf.targetStore(&sh.size, @intCast(size));
+ switch (elf.targetLoad(&sh.type)) {
else => unreachable,
- std.elf.SHT_NULL => {
- if (size > 0) sh.type = std.mem.nativeTo(
- @TypeOf(sh.type),
- std.elf.SHT_PROGBITS,
- target_endian,
- );
- },
- std.elf.SHT_PROGBITS => {
- if (size == 0) sh.type = std.mem.nativeTo(
- @TypeOf(sh.type),
- std.elf.SHT_NULL,
- target_endian,
- );
- },
- std.elf.SHT_SYMTAB => sh.info = std.mem.nativeTo(
- @TypeOf(sh.info),
- @intCast(@divExact(
- size,
- std.mem.toNative(@TypeOf(sh.entsize), sh.entsize, target_endian),
- )),
- target_endian,
+ std.elf.SHT_NULL => if (size > 0) elf.targetStore(&sh.type, std.elf.SHT_PROGBITS),
+ std.elf.SHT_PROGBITS => if (size == 0) elf.targetStore(&sh.type, std.elf.SHT_NULL),
+ std.elf.SHT_SYMTAB => elf.targetStore(
+ &sh.info,
+ @intCast(@divExact(size, elf.targetLoad(&sh.entsize))),
),
std.elf.SHT_STRTAB => {},
}
- sh.size = std.mem.nativeTo(@TypeOf(sh.size), @intCast(size), target_endian);
},
},
.nav, .uav, .lazy_code, .lazy_const_data => {},