zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

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:
MCMakeLists.txt | 1+
Msrc/Compilation.zig | 40+++++++++++-----------------------------
Msrc/link.zig | 2+-
Asrc/link/Coff.zig | 2206+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/link/Coff2.zig | 2193-------------------------------------------------------------------------------
Msrc/link/Elf2.zig | 896+++++++++++++++++++++++++++++++++++++++----------------------------------------
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(&section_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 = &section_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(&section.size_of_raw_data, @intCast(size)); + if (size > coff.targetLoad(&section.virtual_size)) { + const virtual_size = std.mem.alignForward( + u32, + @intCast(size * 4), + coff.optionalHeaderField(.section_alignment), + ); + coff.targetStore(&section.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(&section.virtual_address, rva); + try section_sym.ni.childrenMoved(coff.base.comp.gpa, &coff.mf); + rva += coff.targetLoad(&section.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(&section_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 = &section_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 = &section_table[section_index]; - coff.targetStore(&section.size_of_raw_data, @intCast(size)); - if (size > coff.targetLoad(&section.virtual_size)) { - const virtual_size = std.mem.alignForward( - u32, - @intCast(size * 4), - coff.optionalHeaderField(.section_alignment), - ); - coff.targetStore(&section.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(&section.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(&section.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 => {},