authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-08-26 14:11:14+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-08-30 10:42:21+02:00
log90b3599c6846b29a6162a670783d6c2763683f36
treeb31fdcc12c63080777894a2fb3d61d443b048f1c
parent580bfe01c89c4e95cfb341b73514b8ec769ce635

coff: reorganize the linker


14 files changed, 1116 insertions(+), 1257 deletions(-)

CMakeLists.txt+2
......@@ -753,6 +753,8 @@ set(ZIG_STAGE2_SOURCES
753753 "${CMAKE_SOURCE_DIR}/src/link.zig"
754754 "${CMAKE_SOURCE_DIR}/src/link/C.zig"
755755 "${CMAKE_SOURCE_DIR}/src/link/Coff.zig"
756 "${CMAKE_SOURCE_DIR}/src/link/Coff/Atom.zig"
757 "${CMAKE_SOURCE_DIR}/src/link/Coff/lld.zig"
756758 "${CMAKE_SOURCE_DIR}/src/link/Elf.zig"
757759 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
758760 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"
lib/std/coff.zig+9
......@@ -303,6 +303,15 @@ pub const SectionHeader = extern struct {
303303 return std.math.powi(u16, 2, self.flags.ALIGN - 1) catch unreachable;
304304 }
305305
306 pub fn setAlignment(self: *SectionHeader, new_alignment: u16) void {
307 assert(new_alignment > 0 and new_alignment <= 8192);
308 self.flags.ALIGN = std.math.log2(new_alignment);
309 }
310
311 pub fn isCode(self: SectionHeader) bool {
312 return self.flags.CNT_CODE == 0b1;
313 }
314
306315 pub fn isComdat(self: SectionHeader) bool {
307316 return self.flags.LNK_COMDAT == 0b1;
308317 }
src/Module.zig+4-4
......@@ -5259,9 +5259,9 @@ pub fn clearDecl(
52595259 // TODO instead of a union, put this memory trailing Decl objects,
52605260 // and allow it to be variably sized.
52615261 decl.link = switch (mod.comp.bin_file.tag) {
5262 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
5262 .coff => .{ .coff = link.File.Coff.Atom.empty },
52635263 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
5264 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
5264 .macho => .{ .macho = link.File.MachO.Atom.empty },
52655265 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
52665266 .c => .{ .c = {} },
52675267 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
......@@ -5680,9 +5680,9 @@ pub fn allocateNewDecl(
56805680 .zir_decl_index = 0,
56815681 .src_scope = src_scope,
56825682 .link = switch (mod.comp.bin_file.tag) {
5683 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
5683 .coff => .{ .coff = link.File.Coff.Atom.empty },
56845684 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
5685 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
5685 .macho => .{ .macho = link.File.MachO.Atom.empty },
56865686 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
56875687 .c => .{ .c = {} },
56885688 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
src/Sema.zig+1-1
......@@ -5076,7 +5076,7 @@ pub fn analyzeExport(
50765076 },
50775077 .src = src,
50785078 .link = switch (mod.comp.bin_file.tag) {
5079 .coff => .{ .coff = {} },
5079 .coff => .{ .coff = .{} },
50805080 .elf => .{ .elf = .{} },
50815081 .macho => .{ .macho = .{} },
50825082 .plan9 => .{ .plan9 = null },
src/arch/aarch64/CodeGen.zig+8-6
......@@ -3475,10 +3475,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
34753475 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
34763476 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
34773477 break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes);
3478 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
3479 coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes
3480 else
3481 unreachable;
3478 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| blk: {
3479 const got_atom = coff_file.getGotAtomForSymbol(.{ .sym_index = fn_owner_decl.link.coff.sym_index, .file = null }).?;
3480 const got_sym = coff_file.getSymbol(got_atom.getSymbolWithLoc());
3481 break :blk got_sym.value;
3482 } else unreachable;
34823483
34833484 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr });
34843485
......@@ -5110,8 +5111,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
51105111 assert(decl.link.macho.sym_index != 0);
51115112 return MCValue{ .got_load = decl.link.macho.sym_index };
51125113 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
5113 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
5114 return MCValue{ .memory = got_addr };
5114 const got_atom = coff_file.getGotAtomForSymbol(.{ .sym_index = decl.link.coff.sym_index, .file = null }).?;
5115 const got_sym = coff_file.getSymbol(got_atom.getSymbolWithLoc());
5116 return MCValue{ .memory = got_sym.value };
51155117 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
51165118 try p9.seeDecl(decl_index);
51175119 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
src/arch/arm/CodeGen.zig+8-6
......@@ -3709,10 +3709,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
37093709 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
37103710 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
37113711 break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes);
3712 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
3713 coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes
3714 else
3715 unreachable;
3712 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| blk: {
3713 const got_atom = coff_file.getGotAtomForSymbol(.{ .sym_index = fn_owner_decl.link.coff.sym_index, .file = null }).?;
3714 const got_sym = coff_file.getSymbol(got_atom.getSymbolWithLoc());
3715 break :blk @intCast(u32, got_sym.value);
3716 } else unreachable;
37163717
37173718 try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr });
37183719 } else if (func_value.castTag(.extern_fn)) |_| {
......@@ -5549,8 +5550,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
55495550 } else if (self.bin_file.cast(link.File.MachO)) |_| {
55505551 unreachable; // unsupported architecture for MachO
55515552 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
5552 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
5553 return MCValue{ .memory = got_addr };
5553 const got_atom = coff_file.getGotAtomForSymbol(.{ .sym_index = decl.link.coff.sym_index, .file = null }).?;
5554 const got_sym = coff_file.getSymbol(got_atom.getSymbolWithLoc());
5555 return MCValue{ .memory = got_sym.value };
55545556 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
55555557 try p9.seeDecl(decl_index);
55565558 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
src/arch/riscv64/CodeGen.zig+8-6
......@@ -1755,10 +1755,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
17551755 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
17561756 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
17571757 break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes);
1758 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
1759 coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes
1760 else
1761 unreachable;
1758 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| blk: {
1759 const got_atom = coff_file.getGotAtomForSymbol(.{ .sym_index = fn_owner_decl.link.coff.sym_index, .file = null }).?;
1760 const got_sym = coff_file.getSymbol(got_atom.getSymbolWithLoc());
1761 break :blk got_sym.value;
1762 } else unreachable;
17621763
17631764 try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });
17641765 _ = try self.addInst(.{
......@@ -2592,8 +2593,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
25922593 // index to the GOT target symbol index.
25932594 return MCValue{ .memory = decl.link.macho.sym_index };
25942595 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
2595 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
2596 return MCValue{ .memory = got_addr };
2596 const got_atom = coff_file.getGotAtomForSymbol(.{ .sym_index = decl.link.coff.sym_index, .file = null }).?;
2597 const got_sym = coff_file.getSymbol(got_atom.getSymbolWithLoc());
2598 return MCValue{ .memory = got_sym.value };
25972599 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
25982600 try p9.seeDecl(decl_index);
25992601 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
src/arch/x86_64/CodeGen.zig+8-6
......@@ -3971,10 +3971,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
39713971 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
39723972 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
39733973 break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes);
3974 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
3975 @intCast(u32, coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes)
3976 else
3977 unreachable;
3974 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| blk: {
3975 const got_atom = coff_file.getGotAtomForSymbol(.{ .sym_index = fn_owner_decl.link.coff.sym_index, .file = null }).?;
3976 const got_sym = coff_file.getSymbol(got_atom.getSymbolWithLoc());
3977 break :blk got_sym.value;
3978 } else unreachable;
39783979 _ = try self.addInst(.{
39793980 .tag = .call,
39803981 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
......@@ -6847,8 +6848,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
68476848 assert(decl.link.macho.sym_index != 0);
68486849 return MCValue{ .got_load = decl.link.macho.sym_index };
68496850 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
6850 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
6851 return MCValue{ .memory = got_addr };
6851 const got_atom = coff_file.getGotAtomForSymbol(.{ .sym_index = decl.link.coff.sym_index, .file = null }).?;
6852 const got_sym = coff_file.getSymbol(got_atom.getSymbolWithLoc());
6853 return MCValue{ .memory = got_sym.value };
68526854 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
68536855 try p9.seeDecl(decl_index);
68546856 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
src/link.zig+3-3
......@@ -245,8 +245,8 @@ pub const File = struct {
245245
246246 pub const LinkBlock = union {
247247 elf: Elf.TextBlock,
248 coff: Coff.TextBlock,
249 macho: MachO.TextBlock,
248 coff: Coff.Atom,
249 macho: MachO.Atom,
250250 plan9: Plan9.DeclBlock,
251251 c: void,
252252 wasm: Wasm.DeclBlock,
......@@ -267,7 +267,7 @@ pub const File = struct {
267267
268268 pub const Export = union {
269269 elf: Elf.Export,
270 coff: void,
270 coff: Coff.Export,
271271 macho: MachO.Export,
272272 plan9: Plan9.Export,
273273 c: void,
src/link/Coff.zig+377-1222
......@@ -1,39 +1,30 @@
11const Coff = @This();
22
33const std = @import("std");
4const build_options = @import("build_options");
45const builtin = @import("builtin");
5const log = std.log.scoped(.link);
6const Allocator = std.mem.Allocator;
76const assert = std.debug.assert;
8const fs = std.fs;
9const allocPrint = std.fmt.allocPrint;
7const coff = std.coff;
8const log = std.log.scoped(.link);
9const math = std.math;
1010const mem = std.mem;
1111
12const lldMain = @import("../main.zig").lldMain;
13const trace = @import("../tracy.zig").trace;
14const Module = @import("../Module.zig");
15const Compilation = @import("../Compilation.zig");
12const Allocator = std.mem.Allocator;
13
1614const codegen = @import("../codegen.zig");
1715const link = @import("../link.zig");
18const build_options = @import("build_options");
19const Cache = @import("../Cache.zig");
20const mingw = @import("../mingw.zig");
16const lld = @import("Coff/lld.zig");
17const trace = @import("../tracy.zig").trace;
18
2119const Air = @import("../Air.zig");
20pub const Atom = @import("Coff/Atom.zig");
21const Compilation = @import("../Compilation.zig");
2222const Liveness = @import("../Liveness.zig");
2323const LlvmObject = @import("../codegen/llvm.zig").Object;
24const Module = @import("../Module.zig");
25const StringTable = @import("strtab.zig").StringTable;
2426const TypedValue = @import("../TypedValue.zig");
2527
26const allocation_padding = 4 / 3;
27const minimum_text_block_size = 64 * allocation_padding;
28
29const section_alignment = 4096;
30const file_alignment = 512;
31const default_image_base = 0x400_000;
32const section_table_size = 2 * 40;
33comptime {
34 assert(mem.isAligned(default_image_base, section_alignment));
35}
36
3728pub const base_tag: link.File.Tag = .coff;
3829
3930const msdos_stub = @embedFile("msdos-stub.bin");
......@@ -42,91 +33,94 @@ const msdos_stub = @embedFile("msdos-stub.bin");
4233llvm_object: ?*LlvmObject = null,
4334
4435base: link.File,
45ptr_width: PtrWidth,
4636error_flags: link.File.ErrorFlags = .{},
4737
48text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
49last_text_block: ?*TextBlock = null,
50
51/// Section table file pointer.
52section_table_offset: u32 = 0,
53/// Section data file pointer.
54section_data_offset: u32 = 0,
55/// Optional header file pointer.
56optional_header_offset: u32 = 0,
57
58/// Absolute virtual address of the offset table when the executable is loaded in memory.
59offset_table_virtual_address: u32 = 0,
60/// Current size of the offset table on disk, must be a multiple of `file_alignment`
61offset_table_size: u32 = 0,
62/// Contains absolute virtual addresses
63offset_table: std.ArrayListUnmanaged(u64) = .{},
64/// Free list of offset table indices
65offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
66
67/// Virtual address of the entry point procedure relative to image base.
68entry_addr: ?u32 = null,
38ptr_width: PtrWidth,
6939
70/// Absolute virtual address of the text section when the executable is loaded in memory.
71text_section_virtual_address: u32 = 0,
72/// Current size of the `.text` section on disk, must be a multiple of `file_alignment`
73text_section_size: u32 = 0,
40sections: std.MultiArrayList(Section) = .{},
7441
75offset_table_size_dirty: bool = false,
76text_section_size_dirty: bool = false,
77/// This flag is set when the virtual size of the whole image file when loaded in memory has changed
78/// and needs to be updated in the optional header.
79size_of_image_dirty: bool = false,
42text_section_index: ?u16 = null,
43got_section_index: ?u16 = null,
8044
81pub const PtrWidth = enum { p32, p64 };
45locals: std.ArrayListUnmanaged(coff.Symbol) = .{},
46globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
8247
83pub const TextBlock = struct {
84 /// Offset of the code relative to the start of the text section
85 text_offset: u32,
86 /// Used size of the text block
87 size: u32,
88 /// This field is undefined for symbols with size = 0.
89 offset_table_index: u32,
90 /// Points to the previous and next neighbors, based on the `text_offset`.
91 /// This can be used to find, for example, the capacity of this `TextBlock`.
92 prev: ?*TextBlock,
93 next: ?*TextBlock,
94
95 pub const empty = TextBlock{
96 .text_offset = 0,
97 .size = 0,
98 .offset_table_index = undefined,
99 .prev = null,
100 .next = null,
101 };
48locals_free_list: std.ArrayListUnmanaged(u32) = .{},
10249
103 /// Returns how much room there is to grow in virtual address space.
104 fn capacity(self: TextBlock) u64 {
105 if (self.next) |next| {
106 return next.text_offset - self.text_offset;
107 }
108 // This is the last block, the capacity is only limited by the address space.
109 return std.math.maxInt(u32) - self.text_offset;
110 }
50strtab: StringTable(.strtab) = .{},
11151
112 fn freeListEligible(self: TextBlock) bool {
113 // No need to keep a free list node for the last block.
114 const next = self.next orelse return false;
115 const cap = next.text_offset - self.text_offset;
116 const ideal_cap = self.size * allocation_padding;
117 if (cap <= ideal_cap) return false;
118 const surplus = cap - ideal_cap;
119 return surplus >= minimum_text_block_size;
120 }
52got_entries: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{},
53got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
12154
122 /// Absolute virtual address of the text block when the file is loaded in memory.
123 fn getVAddr(self: TextBlock, coff: Coff) u32 {
124 return coff.text_section_virtual_address + self.text_offset;
125 }
55/// Virtual address of the entry point procedure relative to image base.
56entry_addr: ?u64 = null,
57
58/// Table of Decls that are currently alive.
59/// We store them here so that we can properly dispose of any allocated
60/// memory within the atom in the incremental linker.
61/// TODO consolidate this.
62decls: std.AutoHashMapUnmanaged(Module.Decl.Index, ?u16) = .{},
63
64/// List of atoms that are either synthetic or map directly to the Zig source program.
65managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
66
67/// Table of atoms indexed by the symbol index.
68atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
69
70const page_size: u16 = 0x1000;
71
72const Section = struct {
73 header: coff.SectionHeader,
74
75 last_atom: ?*Atom = null,
76
77 /// A list of atoms that have surplus capacity. This list can have false
78 /// positives, as functions grow and shrink over time, only sometimes being added
79 /// or removed from the freelist.
80 ///
81 /// An atom has surplus capacity when its overcapacity value is greater than
82 /// padToIdeal(minimum_atom_size). That is, when it has so
83 /// much extra capacity, that we could fit a small new symbol in it, itself with
84 /// ideal_capacity or more.
85 ///
86 /// Ideal capacity is defined by size + (size / ideal_factor).
87 ///
88 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
89 /// overcapacity can be negative. A simple way to have negative overcapacity is to
90 /// allocate a fresh atom, which will have ideal capacity, and then grow it
91 /// by 1 byte. It will then have -1 overcapacity.
92 free_list: std.ArrayListUnmanaged(*Atom) = .{},
12693};
12794
95pub const PtrWidth = enum { p32, p64 };
12896pub const SrcFn = void;
12997
98pub const Export = struct {
99 sym_index: ?u32 = null,
100};
101
102pub const SymbolWithLoc = struct {
103 // Index into the respective symbol table.
104 sym_index: u32,
105
106 // null means it's a synthetic global or Zig source.
107 file: ?u32 = null,
108};
109
110/// When allocating, the ideal_capacity is calculated by
111/// actual_capacity + (actual_capacity / ideal_factor)
112const ideal_factor = 3;
113
114/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
115/// it as a possible place to put new symbols, it must have enough room for this many bytes
116/// (plus extra for reserved capacity).
117const minimum_text_block_size = 64;
118pub const min_text_capacity = padToIdeal(minimum_text_block_size);
119
120/// We commit 0x1000 = 4096 bytes of space to the headers.
121/// This should be plenty for any potential future extensions.
122const default_headerpad_size: u32 = 0x1000;
123
130124pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Coff {
131125 assert(options.target.ofmt == .coff);
132126
......@@ -144,25 +138,18 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
144138 });
145139 self.base.file = file;
146140
147 const coff_file_header_offset: u32 = if (options.output_mode == .Exe) msdos_stub.len + 4 else 0;
148 const default_offset_table_size = file_alignment;
149 const data_directory_count = 0;
150 const default_size_of_code = 0;
151 const optional_header_size = switch (options.output_mode) {
152 .Exe => data_directory_count * 8 + switch (self.ptr_width) {
153 .p32 => @as(u16, 96),
154 .p64 => 112,
155 },
156 else => 0,
157 };
158 const section_table_offset = coff_file_header_offset + 20 + optional_header_size;
159 self.section_data_offset = mem.alignForwardGeneric(u32, section_table_offset + section_table_size, file_alignment);
160 const section_data_relative_virtual_address = mem.alignForwardGeneric(u32, section_table_offset + section_table_size, section_alignment);
161 self.offset_table_virtual_address = default_image_base + section_data_relative_virtual_address;
162 self.offset_table_size = default_offset_table_size;
163 self.section_table_offset = section_table_offset;
164 self.text_section_virtual_address = default_image_base + section_data_relative_virtual_address + section_alignment;
165 self.text_section_size = default_size_of_code;
141 // Index 0 is always a null symbol.
142 try self.locals.append(allocator, .{
143 .name = [_]u8{0} ** 8,
144 .value = 0,
145 .section_number = @intToEnum(coff.SectionNumber, 0),
146 .@"type" = .{ .base_type = .NULL, .complex_type = .NULL },
147 .storage_class = .NULL,
148 .number_of_aux_symbols = 0,
149 });
150 try self.strtab.buffer.append(allocator, 0);
151
152 try self.populateMissingMetadata();
166153
167154 return self;
168155}
......@@ -193,245 +180,259 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {
193180 return self;
194181}
195182
196pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void {
197 if (self.llvm_object) |_| return;
183pub fn deinit(self: *Coff) void {
184 const gpa = self.base.allocator;
198185
199 try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1);
186 if (build_options.have_llvm) {
187 if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa);
188 }
200189
201 const decl = self.base.options.module.?.declPtr(decl_index);
202 if (self.offset_table_free_list.popOrNull()) |i| {
203 decl.link.coff.offset_table_index = i;
204 } else {
205 decl.link.coff.offset_table_index = @intCast(u32, self.offset_table.items.len);
206 _ = self.offset_table.addOneAssumeCapacity();
190 for (self.sections.items(.free_list)) |*free_list| {
191 free_list.deinit(gpa);
192 }
193 self.sections.deinit(gpa);
207194
208 const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8;
209 if (self.offset_table.items.len > self.offset_table_size / entry_size) {
210 self.offset_table_size_dirty = true;
211 }
195 for (self.managed_atoms.items) |atom| {
196 gpa.destroy(atom);
212197 }
198 self.managed_atoms.deinit(gpa);
213199
214 self.offset_table.items[decl.link.coff.offset_table_index] = 0;
200 self.locals.deinit(gpa);
201 self.globals.deinit(gpa);
202 self.locals_free_list.deinit(gpa);
203 self.strtab.deinit(gpa);
204 self.got_entries.deinit(gpa);
205 self.got_entries_free_list.deinit(gpa);
206 self.decls.deinit(gpa);
207 self.atom_by_index_table.deinit(gpa);
215208}
216209
217fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
218 const new_block_min_capacity = new_block_size * allocation_padding;
210fn populateMissingMetadata(self: *Coff) !void {
211 _ = self;
212 @panic("TODO populateMissingMetadata");
213}
214
215pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void {
216 if (self.llvm_object) |_| return;
217 const decl = self.base.options.module.?.declPtr(decl_index);
218 if (decl.link.coff.sym_index != 0) return;
219 decl.link.coff.sym_index = try self.allocateSymbol();
220 const gpa = self.base.allocator;
221 try self.atom_by_index_table.putNoClobber(gpa, decl.link.coff.sym_index, &decl.link.coff);
222 try self.decls.putNoClobber(gpa, decl_index, null);
223}
224
225fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u64, alignment: u64, sect_id: u16) !u64 {
226 const tracy = trace(@src());
227 defer tracy.end();
219228
220 // We use these to indicate our intention to update metadata, placing the new block,
229 const header = &self.sections.items(.header)[sect_id];
230 const free_list = &self.sections.items(.free_list)[sect_id];
231 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
232 const new_atom_ideal_capacity = if (header.isCode()) padToIdeal(new_atom_size) else new_atom_size;
233
234 // We use these to indicate our intention to update metadata, placing the new atom,
221235 // and possibly removing a free list node.
222236 // It would be simpler to do it inside the for loop below, but that would cause a
223237 // problem if an error was returned later in the function. So this action
224238 // is actually carried out at the end of the function, when errors are no longer possible.
225 var block_placement: ?*TextBlock = null;
239 var atom_placement: ?*Atom = null;
226240 var free_list_removal: ?usize = null;
227241
228 const vaddr = blk: {
242 // First we look for an appropriately sized free list node.
243 // The list is unordered. We'll just take the first thing that works.
244 var vaddr = blk: {
229245 var i: usize = 0;
230 while (i < self.text_block_free_list.items.len) {
231 const free_block = self.text_block_free_list.items[i];
232
233 const next_block_text_offset = free_block.text_offset + free_block.capacity();
234 const new_block_text_offset = mem.alignForwardGeneric(u64, free_block.getVAddr(self.*) + free_block.size, alignment) - self.text_section_virtual_address;
235 if (new_block_text_offset < next_block_text_offset and next_block_text_offset - new_block_text_offset >= new_block_min_capacity) {
236 block_placement = free_block;
237
238 const remaining_capacity = next_block_text_offset - new_block_text_offset - new_block_min_capacity;
239 if (remaining_capacity < minimum_text_block_size) {
240 free_list_removal = i;
241 }
242
243 break :blk new_block_text_offset + self.text_section_virtual_address;
244 } else {
245 if (!free_block.freeListEligible()) {
246 _ = self.text_block_free_list.swapRemove(i);
246 while (i < free_list.items.len) {
247 const big_atom = free_list.items[i];
248 // We now have a pointer to a live atom that has too much capacity.
249 // Is it enough that we could fit this new atom?
250 const sym = big_atom.getSymbol(self);
251 const capacity = big_atom.capacity(self);
252 const ideal_capacity = if (header.isCode()) padToIdeal(capacity) else capacity;
253 const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity;
254 const capacity_end_vaddr = sym.n_value + capacity;
255 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;
256 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
257 if (new_start_vaddr < ideal_capacity_end_vaddr) {
258 // Additional bookkeeping here to notice if this free list node
259 // should be deleted because the atom that it points to has grown to take up
260 // more of the extra capacity.
261 if (!big_atom.freeListEligible(self)) {
262 _ = free_list.swapRemove(i);
247263 } else {
248264 i += 1;
249265 }
250266 continue;
251267 }
252 } else if (self.last_text_block) |last| {
253 const new_block_vaddr = mem.alignForwardGeneric(u64, last.getVAddr(self.*) + last.size, alignment);
254 block_placement = last;
255 break :blk new_block_vaddr;
268 // At this point we know that we will place the new atom here. But the
269 // remaining question is whether there is still yet enough capacity left
270 // over for there to still be a free list node.
271 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
272 const keep_free_list_node = remaining_capacity >= min_text_capacity;
273
274 // Set up the metadata to be updated, after errors are no longer possible.
275 atom_placement = big_atom;
276 if (!keep_free_list_node) {
277 free_list_removal = i;
278 }
279 break :blk new_start_vaddr;
280 } else if (maybe_last_atom.*) |last| {
281 const last_symbol = last.getSymbol(self);
282 const ideal_capacity = if (header.isCode()) padToIdeal(last.size) else last.size;
283 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
284 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
285 atom_placement = last;
286 break :blk new_start_vaddr;
256287 } else {
257 break :blk self.text_section_virtual_address;
288 break :blk mem.alignForwardGeneric(u64, header.addr, alignment);
258289 }
259290 };
260291
261 const expand_text_section = block_placement == null or block_placement.?.next == null;
262 if (expand_text_section) {
263 const needed_size = @intCast(u32, mem.alignForwardGeneric(u64, vaddr + new_block_size - self.text_section_virtual_address, file_alignment));
264 if (needed_size > self.text_section_size) {
265 const current_text_section_virtual_size = mem.alignForwardGeneric(u32, self.text_section_size, section_alignment);
266 const new_text_section_virtual_size = mem.alignForwardGeneric(u32, needed_size, section_alignment);
267 if (current_text_section_virtual_size != new_text_section_virtual_size) {
268 self.size_of_image_dirty = true;
269 // Write new virtual size
270 var buf: [4]u8 = undefined;
271 mem.writeIntLittle(u32, &buf, new_text_section_virtual_size);
272 try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 8);
273 }
292 const expand_section = atom_placement == null or atom_placement.?.next == null;
293 if (expand_section) {
294 @panic("TODO expand section in allocateAtom");
295 }
274296
275 self.text_section_size = needed_size;
276 self.text_section_size_dirty = true;
277 }
278 self.last_text_block = text_block;
297 if (header.getAlignment() < alignment) {
298 header.setAlignment(alignment);
279299 }
280 text_block.text_offset = @intCast(u32, vaddr - self.text_section_virtual_address);
281 text_block.size = @intCast(u32, new_block_size);
282
283 // This function can also reallocate a text block.
284 // In this case we need to "unplug" it from its previous location before
285 // plugging it in to its new location.
286 if (text_block.prev) |prev| {
287 prev.next = text_block.next;
300 atom.size = new_atom_size;
301 atom.alignment = alignment;
302
303 if (atom.prev) |prev| {
304 prev.next = atom.next;
288305 }
289 if (text_block.next) |next| {
290 next.prev = text_block.prev;
306 if (atom.next) |next| {
307 next.prev = atom.prev;
291308 }
292309
293 if (block_placement) |big_block| {
294 text_block.prev = big_block;
295 text_block.next = big_block.next;
296 big_block.next = text_block;
310 if (atom_placement) |big_atom| {
311 atom.prev = big_atom;
312 atom.next = big_atom.next;
313 big_atom.next = atom;
297314 } else {
298 text_block.prev = null;
299 text_block.next = null;
315 atom.prev = null;
316 atom.next = null;
300317 }
301318 if (free_list_removal) |i| {
302 _ = self.text_block_free_list.swapRemove(i);
319 _ = free_list.swapRemove(i);
303320 }
321
304322 return vaddr;
305323}
306324
307fn growTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
308 const block_vaddr = text_block.getVAddr(self.*);
309 const align_ok = mem.alignBackwardGeneric(u64, block_vaddr, alignment) == block_vaddr;
310 const need_realloc = !align_ok or new_block_size > text_block.capacity();
311 if (!need_realloc) return @as(u64, block_vaddr);
312 return self.allocateTextBlock(text_block, new_block_size, alignment);
325fn allocateSymbol(self: *Coff) !u32 {
326 const gpa = self.base.allocator;
327 try self.locals.ensureUnusedCapacity(gpa, 1);
328
329 const index = blk: {
330 if (self.locals_free_list.popOrNull()) |index| {
331 log.debug(" (reusing symbol index {d})", .{index});
332 break :blk index;
333 } else {
334 log.debug(" (allocating symbol index {d})", .{self.locals.items.len});
335 const index = @intCast(u32, self.locals.items.len);
336 _ = self.locals.addOneAssumeCapacity();
337 break :blk index;
338 }
339 };
340
341 self.locals.items[index] = .{
342 .name = [_]u8{0} ** 8,
343 .value = 0,
344 .section_number = @intToEnum(coff.SectionNumber, 0),
345 .@"type" = .{ .base_type = .NULL, .complex_type = .NULL },
346 .storage_class = .NULL,
347 .number_of_aux_symbols = 0,
348 };
349
350 return index;
313351}
314352
315fn shrinkTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64) void {
316 text_block.size = @intCast(u32, new_block_size);
317 if (text_block.capacity() - text_block.size >= minimum_text_block_size) {
318 self.text_block_free_list.append(self.base.allocator, text_block) catch {};
353pub fn allocateGotEntry(self: *Coff, target: SymbolWithLoc) !u32 {
354 const gpa = self.base.allocator;
355 try self.got_entries.ensureUnusedCapacity(gpa, 1);
356 if (self.got_entries_free_list.popOrNull()) |index| {
357 log.debug(" (reusing GOT entry index {d})", .{index});
358 if (self.got_entries.getIndex(target)) |existing| {
359 assert(existing == index);
360 }
361 self.got_entries.keys()[index] = target;
362 return index;
363 } else {
364 log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.keys().len});
365 const index = @intCast(u32, self.got_entries.keys().len);
366 try self.got_entries.putAssumeCapacityNoClobber(target, 0);
367 return index;
319368 }
320369}
321370
322fn freeTextBlock(self: *Coff, text_block: *TextBlock) void {
371fn growAtom(self: *Coff, atom: *Atom, new_atom_size: u64, alignment: u64, sect_id: u16) !u64 {
372 const sym = atom.getSymbol(self);
373 const align_ok = mem.alignBackwardGeneric(u64, sym.value, alignment) == sym.value;
374 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);
375 if (!need_realloc) return sym.value;
376 return self.allocateAtom(atom, new_atom_size, alignment, sect_id);
377}
378
379fn shrinkAtom(self: *Coff, atom: *Atom, new_block_size: u64, sect_id: u16) void {
380 _ = self;
381 _ = atom;
382 _ = new_block_size;
383 _ = sect_id;
384 // TODO check the new capacity, and if it crosses the size threshold into a big enough
385 // capacity, insert a free list node for it.
386}
387
388fn freeAtom(self: *Coff, atom: *Atom, sect_id: u16) void {
389 log.debug("freeAtom {*}", .{atom});
390
391 const free_list = &self.sections.items(.free_list)[sect_id];
323392 var already_have_free_list_node = false;
324393 {
325394 var i: usize = 0;
326 // TODO turn text_block_free_list into a hash map
327 while (i < self.text_block_free_list.items.len) {
328 if (self.text_block_free_list.items[i] == text_block) {
329 _ = self.text_block_free_list.swapRemove(i);
395 // TODO turn free_list into a hash map
396 while (i < free_list.items.len) {
397 if (free_list.items[i] == atom) {
398 _ = free_list.swapRemove(i);
330399 continue;
331400 }
332 if (self.text_block_free_list.items[i] == text_block.prev) {
401 if (free_list.items[i] == atom.prev) {
333402 already_have_free_list_node = true;
334403 }
335404 i += 1;
336405 }
337406 }
338 if (self.last_text_block == text_block) {
339 self.last_text_block = text_block.prev;
340 }
341 if (text_block.prev) |prev| {
342 prev.next = text_block.next;
343407
344 if (!already_have_free_list_node and prev.freeListEligible()) {
345 // The free list is heuristics, it doesn't have to be perfect, so we can
346 // ignore the OOM here.
347 self.text_block_free_list.append(self.base.allocator, prev) catch {};
408 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
409 if (maybe_last_atom.*) |last_atom| {
410 if (last_atom == atom) {
411 if (atom.prev) |prev| {
412 // TODO shrink the section size here
413 maybe_last_atom.* = prev;
414 } else {
415 maybe_last_atom.* = null;
416 }
348417 }
349418 }
350419
351 if (text_block.next) |next| {
352 next.prev = text_block.prev;
353 }
354}
420 if (atom.prev) |prev| {
421 prev.next = atom.next;
355422
356fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
357 const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8;
358 const endian = self.base.options.target.cpu.arch.endian();
359
360 const offset_table_start = self.section_data_offset;
361 if (self.offset_table_size_dirty) {
362 const current_raw_size = self.offset_table_size;
363 const new_raw_size = self.offset_table_size * 2;
364 log.debug("growing offset table from raw size {} to {}\n", .{ current_raw_size, new_raw_size });
365
366 // Move the text section to a new place in the executable
367 const current_text_section_start = self.section_data_offset + current_raw_size;
368 const new_text_section_start = self.section_data_offset + new_raw_size;
369
370 const amt = try self.base.file.?.copyRangeAll(current_text_section_start, self.base.file.?, new_text_section_start, self.text_section_size);
371 if (amt != self.text_section_size) return error.InputOutput;
372
373 // Write the new raw size in the .got header
374 var buf: [8]u8 = undefined;
375 mem.writeIntLittle(u32, buf[0..4], new_raw_size);
376 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 16);
377 // Write the new .text section file offset in the .text section header
378 mem.writeIntLittle(u32, buf[0..4], new_text_section_start);
379 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 20);
380
381 const current_virtual_size = mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment);
382 const new_virtual_size = mem.alignForwardGeneric(u32, new_raw_size, section_alignment);
383 // If we had to move in the virtual address space, we need to fix the VAs in the offset table, as well as the virtual address of the `.text` section
384 // and the virtual size of the `.got` section
385
386 if (new_virtual_size != current_virtual_size) {
387 log.debug("growing offset table from virtual size {} to {}\n", .{ current_virtual_size, new_virtual_size });
388 self.size_of_image_dirty = true;
389 const va_offset = new_virtual_size - current_virtual_size;
390
391 // Write .got virtual size
392 mem.writeIntLittle(u32, buf[0..4], new_virtual_size);
393 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 8);
394
395 // Write .text new virtual address
396 self.text_section_virtual_address = self.text_section_virtual_address + va_offset;
397 mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - default_image_base);
398 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12);
399
400 // Fix the VAs in the offset table
401 for (self.offset_table.items) |*va, idx| {
402 if (va.* != 0) {
403 va.* += va_offset;
404
405 switch (entry_size) {
406 4 => {
407 mem.writeInt(u32, buf[0..4], @intCast(u32, va.*), endian);
408 try self.base.file.?.pwriteAll(buf[0..4], offset_table_start + idx * entry_size);
409 },
410 8 => {
411 mem.writeInt(u64, &buf, va.*, endian);
412 try self.base.file.?.pwriteAll(&buf, offset_table_start + idx * entry_size);
413 },
414 else => unreachable,
415 }
416 }
417 }
423 if (!already_have_free_list_node and prev.freeListEligible(self)) {
424 // The free list is heuristics, it doesn't have to be perfect, so we can
425 // ignore the OOM here.
426 free_list.append(self.base.allocator, prev) catch {};
418427 }
419 self.offset_table_size = new_raw_size;
420 self.offset_table_size_dirty = false;
428 } else {
429 atom.prev = null;
421430 }
422 // Write the new entry
423 switch (entry_size) {
424 4 => {
425 var buf: [4]u8 = undefined;
426 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
427 try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size);
428 },
429 8 => {
430 var buf: [8]u8 = undefined;
431 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
432 try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size);
433 },
434 else => unreachable,
431
432 if (atom.next) |next| {
433 next.prev = atom.prev;
434 } else {
435 atom.next = null;
435436 }
436437}
437438
......@@ -470,15 +471,19 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
470471 },
471472 };
472473
473 return self.finishUpdateDecl(module, func.owner_decl, code);
474 const sym = try self.updateDeclCode(decl_index, code);
475 log.debug("updated decl code has sym {}", .{sym});
476
477 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
478 const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};
479 return self.updateDeclExports(module, decl_index, decl_exports);
474480}
475481
476482pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 {
477483 _ = self;
478484 _ = tv;
479485 _ = decl_index;
480 log.debug("TODO lowerUnnamedConst for Coff", .{});
481 return error.AnalysisFail;
486 @panic("TODO lowerUnnamedConst");
482487}
483488
484489pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void {
......@@ -503,9 +508,6 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
503508 }
504509 }
505510
506 // TODO COFF/PE debug information
507 // TODO Implement exports
508
509511 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
510512 defer code_buffer.deinit();
511513
......@@ -526,49 +528,21 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
526528 },
527529 };
528530
529 return self.finishUpdateDecl(module, decl_index, code);
530}
531
532fn finishUpdateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index, code: []const u8) !void {
533 const decl = module.declPtr(decl_index);
534 const required_alignment = decl.ty.abiAlignment(self.base.options.target);
535 const curr_size = decl.link.coff.size;
536 if (curr_size != 0) {
537 const capacity = decl.link.coff.capacity();
538 const need_realloc = code.len > capacity or
539 !mem.isAlignedGeneric(u32, decl.link.coff.text_offset, required_alignment);
540 if (need_realloc) {
541 const curr_vaddr = self.text_section_virtual_address + decl.link.coff.text_offset;
542 const vaddr = try self.growTextBlock(&decl.link.coff, code.len, required_alignment);
543 log.debug("growing {s} from 0x{x} to 0x{x}\n", .{ decl.name, curr_vaddr, vaddr });
544 if (vaddr != curr_vaddr) {
545 log.debug(" (writing new offset table entry)\n", .{});
546 self.offset_table.items[decl.link.coff.offset_table_index] = vaddr;
547 try self.writeOffsetTableEntry(decl.link.coff.offset_table_index);
548 }
549 } else if (code.len < curr_size) {
550 self.shrinkTextBlock(&decl.link.coff, code.len);
551 }
552 } else {
553 const vaddr = try self.allocateTextBlock(&decl.link.coff, code.len, required_alignment);
554 log.debug("allocated text block for {s} at 0x{x} (size: {Bi})\n", .{
555 mem.sliceTo(decl.name, 0),
556 vaddr,
557 std.fmt.fmtIntSizeDec(code.len),
558 });
559 errdefer self.freeTextBlock(&decl.link.coff);
560 self.offset_table.items[decl.link.coff.offset_table_index] = vaddr;
561 try self.writeOffsetTableEntry(decl.link.coff.offset_table_index);
562 }
563
564 // Write the code into the file
565 try self.base.file.?.pwriteAll(code, self.section_data_offset + self.offset_table_size + decl.link.coff.text_offset);
531 const sym = try self.updateDeclCode(decl_index, code);
532 log.debug("updated decl code for {}", .{sym});
566533
567534 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
568535 const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};
569536 return self.updateDeclExports(module, decl_index, decl_exports);
570537}
571538
539fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8) !*coff.Symbol {
540 _ = self;
541 _ = decl_index;
542 _ = code;
543 @panic("TODO updateDeclCode");
544}
545
572546pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {
573547 if (build_options.have_llvm) {
574548 if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);
......@@ -577,9 +551,31 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {
577551 const mod = self.base.options.module.?;
578552 const decl = mod.declPtr(decl_index);
579553
554 log.debug("freeDecl {*}", .{decl});
555
556 const kv = self.decls.fetchRemove(decl_index);
557 if (kv.?.value) |index| {
558 self.freeAtom(&decl.link.coff, index);
559 }
560
580561 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
581 self.freeTextBlock(&decl.link.coff);
582 self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {};
562 const gpa = self.base.allocator;
563 const sym_index = decl.link.coff.sym_index;
564 if (sym_index != 0) {
565 self.locals_free_list.append(gpa, sym_index) catch {};
566
567 // Try freeing GOT atom if this decl had one
568 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
569 if (self.got_entries.getIndex(got_target)) |got_index| {
570 self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {};
571 self.got_entries.values()[got_index] = 0;
572 log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
573 }
574
575 self.locals.items[sym_index].section_number = @intToEnum(coff.SectionNumber, 0);
576 _ = self.atom_by_index_table.remove(sym_index);
577 decl.link.coff.sym_index = 0;
578 }
583579}
584580
585581pub fn updateDeclExports(
......@@ -625,28 +621,7 @@ pub fn updateDeclExports(
625621 if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl_index, exports);
626622 }
627623
628 const decl = module.declPtr(decl_index);
629 for (exports) |exp| {
630 if (exp.options.section) |section_name| {
631 if (!mem.eql(u8, section_name, ".text")) {
632 try module.failed_exports.ensureUnusedCapacity(module.gpa, 1);
633 module.failed_exports.putAssumeCapacityNoClobber(
634 exp,
635 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}),
636 );
637 continue;
638 }
639 }
640 if (mem.eql(u8, exp.options.name, "wWinMainCRTStartup")) {
641 self.entry_addr = decl.link.coff.getVAddr(self.*) - default_image_base;
642 } else {
643 try module.failed_exports.ensureUnusedCapacity(module.gpa, 1);
644 module.failed_exports.putAssumeCapacityNoClobber(
645 exp,
646 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: Exports other than 'wWinMainCRTStartup'", .{}),
647 );
648 }
649 }
624 @panic("TODO updateDeclExports");
650625}
651626
652627pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void {
......@@ -660,7 +635,7 @@ pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !vo
660635 }
661636 const use_lld = build_options.have_llvm and self.base.options.use_lld;
662637 if (use_lld) {
663 return self.linkWithLLD(comp, prog_node);
638 return lld.linkWithLLD(self, comp, prog_node);
664639 }
665640 switch (self.base.options.output_mode) {
666641 .Exe, .Obj => return self.flushModule(comp, prog_node),
......@@ -682,888 +657,68 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
682657 sub_prog_node.activate();
683658 defer sub_prog_node.end();
684659
685 const output_mode = self.base.options.output_mode;
686 log.debug("in flushModule with {}", .{output_mode});
687
688 var coff_file_header_offset: u32 = 0;
689 if (output_mode == .Exe) {
690 // Write the MS-DOS stub and the PE signature
691 try self.base.file.?.pwriteAll(msdos_stub ++ "PE\x00\x00", 0);
692 coff_file_header_offset = msdos_stub.len + 4;
693 }
694
695 // COFF file header
696 const data_directory_count = 0;
697 var hdr_data: [112 + data_directory_count * 8 + section_table_size]u8 = undefined;
698 var index: usize = 0;
699
700 const machine = self.base.options.target.cpu.arch.toCoffMachine();
701 if (machine == .Unknown) {
702 return error.UnsupportedCOFFArchitecture;
703 }
704 mem.writeIntLittle(u16, hdr_data[0..2], @enumToInt(machine));
705 index += 2;
706
707 // Number of sections (we only use .got, .text)
708 mem.writeIntLittle(u16, hdr_data[index..][0..2], 2);
709 index += 2;
710 // TimeDateStamp (u32), PointerToSymbolTable (u32), NumberOfSymbols (u32)
711 mem.set(u8, hdr_data[index..][0..12], 0);
712 index += 12;
713
714 const optional_header_size = switch (output_mode) {
715 .Exe => data_directory_count * 8 + switch (self.ptr_width) {
716 .p32 => @as(u16, 96),
717 .p64 => 112,
718 },
719 else => 0,
720 };
721
722 const default_offset_table_size = file_alignment;
723 const default_size_of_code = 0;
724
725 // Size of file when loaded in memory
726 const size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - default_image_base + default_size_of_code, section_alignment);
727
728 mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size);
729 index += 2;
730
731 // Characteristics
732 var flags: std.coff.CoffHeaderFlags = .{
733 // TODO Remove debug info stripped flag when necessary
734 .DEBUG_STRIPPED = 1,
735 .RELOCS_STRIPPED = 1,
736 };
737 if (output_mode == .Exe) {
738 flags.EXECUTABLE_IMAGE = 1;
739 }
740 switch (self.ptr_width) {
741 .p32 => flags.@"32BIT_MACHINE" = 1,
742 .p64 => flags.LARGE_ADDRESS_AWARE = 1,
743 }
744 mem.writeIntLittle(u16, hdr_data[index..][0..2], @bitCast(u16, flags));
745 index += 2;
746
747 assert(index == 20);
748 try self.base.file.?.pwriteAll(hdr_data[0..index], coff_file_header_offset);
749
750 if (output_mode == .Exe) {
751 self.optional_header_offset = coff_file_header_offset + 20;
752 // Optional header
753 index = 0;
754 mem.writeIntLittle(u16, hdr_data[0..2], switch (self.ptr_width) {
755 .p32 => @as(u16, 0x10b),
756 .p64 => 0x20b,
757 });
758 index += 2;
759
760 // Linker version (u8 + u8)
761 mem.set(u8, hdr_data[index..][0..2], 0);
762 index += 2;
763
764 // SizeOfCode (UNUSED, u32), SizeOfInitializedData (u32), SizeOfUninitializedData (u32), AddressOfEntryPoint (u32), BaseOfCode (UNUSED, u32)
765 mem.set(u8, hdr_data[index..][0..20], 0);
766 index += 20;
767
768 if (self.ptr_width == .p32) {
769 // Base of data relative to the image base (UNUSED)
770 mem.set(u8, hdr_data[index..][0..4], 0);
771 index += 4;
772
773 // Image base address
774 mem.writeIntLittle(u32, hdr_data[index..][0..4], default_image_base);
775 index += 4;
776 } else {
777 // Image base address
778 mem.writeIntLittle(u64, hdr_data[index..][0..8], default_image_base);
779 index += 8;
780 }
781
782 // Section alignment
783 mem.writeIntLittle(u32, hdr_data[index..][0..4], section_alignment);
784 index += 4;
785 // File alignment
786 mem.writeIntLittle(u32, hdr_data[index..][0..4], file_alignment);
787 index += 4;
788 // Required OS version, 6.0 is vista
789 mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
790 index += 2;
791 mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
792 index += 2;
793 // Image version
794 mem.set(u8, hdr_data[index..][0..4], 0);
795 index += 4;
796 // Required subsystem version, same as OS version
797 mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
798 index += 2;
799 mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
800 index += 2;
801 // Reserved zeroes (u32)
802 mem.set(u8, hdr_data[index..][0..4], 0);
803 index += 4;
804 mem.writeIntLittle(u32, hdr_data[index..][0..4], size_of_image);
805 index += 4;
806 mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
807 index += 4;
808 // CheckSum (u32)
809 mem.set(u8, hdr_data[index..][0..4], 0);
810 index += 4;
811 // Subsystem, TODO: Let users specify the subsystem, always CUI for now
812 mem.writeIntLittle(u16, hdr_data[index..][0..2], 3);
813 index += 2;
814 // DLL characteristics
815 mem.writeIntLittle(u16, hdr_data[index..][0..2], 0x0);
816 index += 2;
817
818 switch (self.ptr_width) {
819 .p32 => {
820 // Size of stack reserve + commit
821 mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000_000);
822 index += 4;
823 mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
824 index += 4;
825 // Size of heap reserve + commit
826 mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x100_000);
827 index += 4;
828 mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
829 index += 4;
830 },
831 .p64 => {
832 // Size of stack reserve + commit
833 mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000_000);
834 index += 8;
835 mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
836 index += 8;
837 // Size of heap reserve + commit
838 mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x100_000);
839 index += 8;
840 mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
841 index += 8;
842 },
843 }
844
845 // Reserved zeroes
846 mem.set(u8, hdr_data[index..][0..4], 0);
847 index += 4;
848
849 // Number of data directories
850 mem.writeIntLittle(u32, hdr_data[index..][0..4], data_directory_count);
851 index += 4;
852 // Initialize data directories to zero
853 mem.set(u8, hdr_data[index..][0 .. data_directory_count * 8], 0);
854 index += data_directory_count * 8;
855
856 assert(index == optional_header_size);
857 }
858
859 // Write section table.
860 // First, the .got section
861 hdr_data[index..][0..8].* = ".got\x00\x00\x00\x00".*;
862 index += 8;
863 if (output_mode == .Exe) {
864 // Virtual size (u32)
865 mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
866 index += 4;
867 // Virtual address (u32)
868 mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - default_image_base);
869 index += 4;
870 } else {
871 mem.set(u8, hdr_data[index..][0..8], 0);
872 index += 8;
873 }
874 // Size of raw data (u32)
875 mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
876 index += 4;
877 // File pointer to the start of the section
878 mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
879 index += 4;
880 // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16)
881 mem.set(u8, hdr_data[index..][0..12], 0);
882 index += 12;
883 // Section flags
884 mem.writeIntLittle(u32, hdr_data[index..][0..4], @bitCast(u32, std.coff.SectionHeaderFlags{
885 .CNT_INITIALIZED_DATA = 1,
886 .MEM_READ = 1,
887 }));
888 index += 4;
889 // Then, the .text section
890 hdr_data[index..][0..8].* = ".text\x00\x00\x00".*;
891 index += 8;
892 if (output_mode == .Exe) {
893 // Virtual size (u32)
894 mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
895 index += 4;
896 // Virtual address (u32)
897 mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - default_image_base);
898 index += 4;
899 } else {
900 mem.set(u8, hdr_data[index..][0..8], 0);
901 index += 8;
902 }
903 // Size of raw data (u32)
904 mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
905 index += 4;
906 // File pointer to the start of the section
907 mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset + default_offset_table_size);
908 index += 4;
909 // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16)
910 mem.set(u8, hdr_data[index..][0..12], 0);
911 index += 12;
912 // Section flags
913 mem.writeIntLittle(u32, hdr_data[index..][0..4], @bitCast(u32, std.coff.SectionHeaderFlags{
914 .CNT_CODE = 1,
915 .MEM_EXECUTE = 1,
916 .MEM_READ = 1,
917 .MEM_WRITE = 1,
918 }));
919 index += 4;
920
921 assert(index == optional_header_size + section_table_size);
922 try self.base.file.?.pwriteAll(hdr_data[0..index], self.optional_header_offset);
923 try self.base.file.?.setEndPos(self.section_data_offset + default_offset_table_size + default_size_of_code);
924
925 if (self.text_section_size_dirty) {
926 // Write the new raw size in the .text header
927 var buf: [4]u8 = undefined;
928 mem.writeIntLittle(u32, &buf, self.text_section_size);
929 try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 16);
930 try self.base.file.?.setEndPos(self.section_data_offset + self.offset_table_size + self.text_section_size);
931 self.text_section_size_dirty = false;
932 }
933
934 if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) {
935 const new_size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - default_image_base + self.text_section_size, section_alignment);
936 var buf: [4]u8 = undefined;
937 mem.writeIntLittle(u32, &buf, new_size_of_image);
938 try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56);
939 self.size_of_image_dirty = false;
940 }
941
942660 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
943661 log.debug("flushing. no_entry_point_found = true\n", .{});
944662 self.error_flags.no_entry_point_found = true;
945663 } else {
946664 log.debug("flushing. no_entry_point_found = false\n", .{});
947665 self.error_flags.no_entry_point_found = false;
948
949 if (self.base.options.output_mode == .Exe) {
950 // Write AddressOfEntryPoint
951 var buf: [4]u8 = undefined;
952 mem.writeIntLittle(u32, &buf, self.entry_addr.?);
953 try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 16);
954 }
955666 }
956667}
957668
958fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void {
959 const tracy = trace(@src());
960 defer tracy.end();
961
962 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
963 defer arena_allocator.deinit();
964 const arena = arena_allocator.allocator();
965
966 const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
967 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
968
969 // If there is no Zig code to compile, then we should skip flushing the output file because it
970 // will not be part of the linker line anyway.
971 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
972 const use_stage1 = build_options.have_stage1 and self.base.options.use_stage1;
973 if (use_stage1) {
974 const obj_basename = try std.zig.binNameAlloc(arena, .{
975 .root_name = self.base.options.root_name,
976 .target = self.base.options.target,
977 .output_mode = .Obj,
978 });
979 switch (self.base.options.cache_mode) {
980 .incremental => break :blk try module.zig_cache_artifact_directory.join(
981 arena,
982 &[_][]const u8{obj_basename},
983 ),
984 .whole => break :blk try fs.path.join(arena, &.{
985 fs.path.dirname(full_out_path).?, obj_basename,
986 }),
987 }
988 }
989
990 try self.flushModule(comp, prog_node);
991
992 if (fs.path.dirname(full_out_path)) |dirname| {
993 break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
994 } else {
995 break :blk self.base.intermediary_basename.?;
996 }
997 } else null;
998
999 var sub_prog_node = prog_node.start("LLD Link", 0);
1000 sub_prog_node.activate();
1001 sub_prog_node.context.refresh();
1002 defer sub_prog_node.end();
1003
1004 const is_lib = self.base.options.output_mode == .Lib;
1005 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
1006 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
1007 const link_in_crt = self.base.options.link_libc and is_exe_or_dyn_lib;
1008 const target = self.base.options.target;
1009
1010 // See link/Elf.zig for comments on how this mechanism works.
1011 const id_symlink_basename = "lld.id";
1012
1013 var man: Cache.Manifest = undefined;
1014 defer if (!self.base.options.disable_lld_caching) man.deinit();
1015
1016 var digest: [Cache.hex_digest_len]u8 = undefined;
1017
1018 if (!self.base.options.disable_lld_caching) {
1019 man = comp.cache_parent.obtain();
1020 self.base.releaseLock();
1021
1022 comptime assert(Compilation.link_hash_implementation_version == 7);
1023
1024 for (self.base.options.objects) |obj| {
1025 _ = try man.addFile(obj.path, null);
1026 man.hash.add(obj.must_link);
1027 }
1028 for (comp.c_object_table.keys()) |key| {
1029 _ = try man.addFile(key.status.success.object_path, null);
1030 }
1031 try man.addOptionalFile(module_obj_path);
1032 man.hash.addOptionalBytes(self.base.options.entry);
1033 man.hash.addOptional(self.base.options.stack_size_override);
1034 man.hash.addOptional(self.base.options.image_base_override);
1035 man.hash.addListOfBytes(self.base.options.lib_dirs);
1036 man.hash.add(self.base.options.skip_linker_dependencies);
1037 if (self.base.options.link_libc) {
1038 man.hash.add(self.base.options.libc_installation != null);
1039 if (self.base.options.libc_installation) |libc_installation| {
1040 man.hash.addBytes(libc_installation.crt_dir.?);
1041 if (target.abi == .msvc) {
1042 man.hash.addBytes(libc_installation.msvc_lib_dir.?);
1043 man.hash.addBytes(libc_installation.kernel32_lib_dir.?);
1044 }
1045 }
1046 }
1047 link.hashAddSystemLibs(&man.hash, self.base.options.system_libs);
1048 man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys());
1049 man.hash.addOptional(self.base.options.subsystem);
1050 man.hash.add(self.base.options.is_test);
1051 man.hash.add(self.base.options.tsaware);
1052 man.hash.add(self.base.options.nxcompat);
1053 man.hash.add(self.base.options.dynamicbase);
1054 // strip does not need to go into the linker hash because it is part of the hash namespace
1055 man.hash.addOptional(self.base.options.major_subsystem_version);
1056 man.hash.addOptional(self.base.options.minor_subsystem_version);
1057
1058 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
1059 _ = try man.hit();
1060 digest = man.final();
1061 var prev_digest_buf: [digest.len]u8 = undefined;
1062 const prev_digest: []u8 = Cache.readSmallFile(
1063 directory.handle,
1064 id_symlink_basename,
1065 &prev_digest_buf,
1066 ) catch |err| blk: {
1067 log.debug("COFF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
1068 // Handle this as a cache miss.
1069 break :blk prev_digest_buf[0..0];
1070 };
1071 if (mem.eql(u8, prev_digest, &digest)) {
1072 log.debug("COFF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
1073 // Hot diggity dog! The output binary is already there.
1074 self.base.lock = man.toOwnedLock();
1075 return;
1076 }
1077 log.debug("COFF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
1078
1079 // We are about to change the output file to be different, so we invalidate the build hash now.
1080 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
1081 error.FileNotFound => {},
1082 else => |e| return e,
1083 };
1084 }
1085
1086 if (self.base.options.output_mode == .Obj) {
1087 // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy
1088 // here. TODO: think carefully about how we can avoid this redundant operation when doing
1089 // build-obj. See also the corresponding TODO in linkAsArchive.
1090 const the_object_path = blk: {
1091 if (self.base.options.objects.len != 0)
1092 break :blk self.base.options.objects[0].path;
1093
1094 if (comp.c_object_table.count() != 0)
1095 break :blk comp.c_object_table.keys()[0].status.success.object_path;
1096
1097 if (module_obj_path) |p|
1098 break :blk p;
1099
1100 // TODO I think this is unreachable. Audit this situation when solving the above TODO
1101 // regarding eliding redundant object -> object transformations.
1102 return error.NoObjectsToLink;
1103 };
1104 // This can happen when using --enable-cache and using the stage1 backend. In this case
1105 // we can skip the file copy.
1106 if (!mem.eql(u8, the_object_path, full_out_path)) {
1107 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
1108 }
1109 } else {
1110 // Create an LLD command line and invoke it.
1111 var argv = std.ArrayList([]const u8).init(self.base.allocator);
1112 defer argv.deinit();
1113 // We will invoke ourselves as a child process to gain access to LLD.
1114 // This is necessary because LLD does not behave properly as a library -
1115 // it calls exit() and does not reset all global data between invocations.
1116 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "lld-link" });
1117
1118 try argv.append("-ERRORLIMIT:0");
1119 try argv.append("-NOLOGO");
1120 if (!self.base.options.strip) {
1121 try argv.append("-DEBUG");
1122 }
1123 if (self.base.options.lto) {
1124 switch (self.base.options.optimize_mode) {
1125 .Debug => {},
1126 .ReleaseSmall => try argv.append("-OPT:lldlto=2"),
1127 .ReleaseFast, .ReleaseSafe => try argv.append("-OPT:lldlto=3"),
1128 }
1129 }
1130 if (self.base.options.output_mode == .Exe) {
1131 const stack_size = self.base.options.stack_size_override orelse 16777216;
1132 try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size}));
1133 }
1134 if (self.base.options.image_base_override) |image_base| {
1135 try argv.append(try std.fmt.allocPrint(arena, "-BASE:{d}", .{image_base}));
1136 }
1137
1138 if (target.cpu.arch == .i386) {
1139 try argv.append("-MACHINE:X86");
1140 } else if (target.cpu.arch == .x86_64) {
1141 try argv.append("-MACHINE:X64");
1142 } else if (target.cpu.arch.isARM()) {
1143 if (target.cpu.arch.ptrBitWidth() == 32) {
1144 try argv.append("-MACHINE:ARM");
1145 } else {
1146 try argv.append("-MACHINE:ARM64");
1147 }
1148 }
1149
1150 for (self.base.options.force_undefined_symbols.keys()) |symbol| {
1151 try argv.append(try allocPrint(arena, "-INCLUDE:{s}", .{symbol}));
1152 }
1153
1154 if (is_dyn_lib) {
1155 try argv.append("-DLL");
1156 }
1157
1158 if (self.base.options.entry) |entry| {
1159 try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{entry}));
1160 }
1161
1162 if (self.base.options.tsaware) {
1163 try argv.append("-tsaware");
1164 }
1165 if (self.base.options.nxcompat) {
1166 try argv.append("-nxcompat");
1167 }
1168 if (self.base.options.dynamicbase) {
1169 try argv.append("-dynamicbase");
1170 }
1171
1172 try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path}));
1173
1174 if (self.base.options.implib_emit) |emit| {
1175 const implib_out_path = try emit.directory.join(arena, &[_][]const u8{emit.sub_path});
1176 try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path}));
1177 }
1178
1179 if (self.base.options.link_libc) {
1180 if (self.base.options.libc_installation) |libc_installation| {
1181 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?}));
1182
1183 if (target.abi == .msvc) {
1184 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?}));
1185 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?}));
1186 }
1187 }
1188 }
1189
1190 for (self.base.options.lib_dirs) |lib_dir| {
1191 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir}));
1192 }
1193
1194 try argv.ensureUnusedCapacity(self.base.options.objects.len);
1195 for (self.base.options.objects) |obj| {
1196 if (obj.must_link) {
1197 argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{s}", .{obj.path}));
1198 } else {
1199 argv.appendAssumeCapacity(obj.path);
1200 }
1201 }
1202
1203 for (comp.c_object_table.keys()) |key| {
1204 try argv.append(key.status.success.object_path);
1205 }
1206
1207 if (module_obj_path) |p| {
1208 try argv.append(p);
1209 }
1210
1211 const resolved_subsystem: ?std.Target.SubSystem = blk: {
1212 if (self.base.options.subsystem) |explicit| break :blk explicit;
1213 switch (target.os.tag) {
1214 .windows => {
1215 if (self.base.options.module) |module| {
1216 if (module.stage1_flags.have_dllmain_crt_startup or is_dyn_lib)
1217 break :blk null;
1218 if (module.stage1_flags.have_c_main or self.base.options.is_test or
1219 module.stage1_flags.have_winmain_crt_startup or
1220 module.stage1_flags.have_wwinmain_crt_startup)
1221 {
1222 break :blk .Console;
1223 }
1224 if (module.stage1_flags.have_winmain or module.stage1_flags.have_wwinmain)
1225 break :blk .Windows;
1226 }
1227 },
1228 .uefi => break :blk .EfiApplication,
1229 else => {},
1230 }
1231 break :blk null;
1232 };
1233
1234 const Mode = enum { uefi, win32 };
1235 const mode: Mode = mode: {
1236 if (resolved_subsystem) |subsystem| {
1237 const subsystem_suffix = ss: {
1238 if (self.base.options.major_subsystem_version) |major| {
1239 if (self.base.options.minor_subsystem_version) |minor| {
1240 break :ss try allocPrint(arena, ",{d}.{d}", .{ major, minor });
1241 } else {
1242 break :ss try allocPrint(arena, ",{d}", .{major});
1243 }
1244 }
1245 break :ss "";
1246 };
1247
1248 switch (subsystem) {
1249 .Console => {
1250 try argv.append(try allocPrint(arena, "-SUBSYSTEM:console{s}", .{
1251 subsystem_suffix,
1252 }));
1253 break :mode .win32;
1254 },
1255 .EfiApplication => {
1256 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_application{s}", .{
1257 subsystem_suffix,
1258 }));
1259 break :mode .uefi;
1260 },
1261 .EfiBootServiceDriver => {
1262 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_boot_service_driver{s}", .{
1263 subsystem_suffix,
1264 }));
1265 break :mode .uefi;
1266 },
1267 .EfiRom => {
1268 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_rom{s}", .{
1269 subsystem_suffix,
1270 }));
1271 break :mode .uefi;
1272 },
1273 .EfiRuntimeDriver => {
1274 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_runtime_driver{s}", .{
1275 subsystem_suffix,
1276 }));
1277 break :mode .uefi;
1278 },
1279 .Native => {
1280 try argv.append(try allocPrint(arena, "-SUBSYSTEM:native{s}", .{
1281 subsystem_suffix,
1282 }));
1283 break :mode .win32;
1284 },
1285 .Posix => {
1286 try argv.append(try allocPrint(arena, "-SUBSYSTEM:posix{s}", .{
1287 subsystem_suffix,
1288 }));
1289 break :mode .win32;
1290 },
1291 .Windows => {
1292 try argv.append(try allocPrint(arena, "-SUBSYSTEM:windows{s}", .{
1293 subsystem_suffix,
1294 }));
1295 break :mode .win32;
1296 },
1297 }
1298 } else if (target.os.tag == .uefi) {
1299 break :mode .uefi;
1300 } else {
1301 break :mode .win32;
1302 }
1303 };
1304
1305 switch (mode) {
1306 .uefi => try argv.appendSlice(&[_][]const u8{
1307 "-BASE:0",
1308 "-ENTRY:EfiMain",
1309 "-OPT:REF",
1310 "-SAFESEH:NO",
1311 "-MERGE:.rdata=.data",
1312 "-ALIGN:32",
1313 "-NODEFAULTLIB",
1314 "-SECTION:.xdata,D",
1315 }),
1316 .win32 => {
1317 if (link_in_crt) {
1318 if (target.abi.isGnu()) {
1319 try argv.append("-lldmingw");
1320
1321 if (target.cpu.arch == .i386) {
1322 try argv.append("-ALTERNATENAME:__image_base__=___ImageBase");
1323 } else {
1324 try argv.append("-ALTERNATENAME:__image_base__=__ImageBase");
1325 }
1326
1327 if (is_dyn_lib) {
1328 try argv.append(try comp.get_libc_crt_file(arena, "dllcrt2.obj"));
1329 if (target.cpu.arch == .i386) {
1330 try argv.append("-ALTERNATENAME:__DllMainCRTStartup@12=_DllMainCRTStartup@12");
1331 } else {
1332 try argv.append("-ALTERNATENAME:_DllMainCRTStartup=DllMainCRTStartup");
1333 }
1334 } else {
1335 try argv.append(try comp.get_libc_crt_file(arena, "crt2.obj"));
1336 }
1337
1338 try argv.append(try comp.get_libc_crt_file(arena, "mingw32.lib"));
1339 try argv.append(try comp.get_libc_crt_file(arena, "mingwex.lib"));
1340 try argv.append(try comp.get_libc_crt_file(arena, "msvcrt-os.lib"));
1341
1342 for (mingw.always_link_libs) |name| {
1343 if (!self.base.options.system_libs.contains(name)) {
1344 const lib_basename = try allocPrint(arena, "{s}.lib", .{name});
1345 try argv.append(try comp.get_libc_crt_file(arena, lib_basename));
1346 }
1347 }
1348 } else {
1349 const lib_str = switch (self.base.options.link_mode) {
1350 .Dynamic => "",
1351 .Static => "lib",
1352 };
1353 const d_str = switch (self.base.options.optimize_mode) {
1354 .Debug => "d",
1355 else => "",
1356 };
1357 switch (self.base.options.link_mode) {
1358 .Static => try argv.append(try allocPrint(arena, "libcmt{s}.lib", .{d_str})),
1359 .Dynamic => try argv.append(try allocPrint(arena, "msvcrt{s}.lib", .{d_str})),
1360 }
1361
1362 try argv.append(try allocPrint(arena, "{s}vcruntime{s}.lib", .{ lib_str, d_str }));
1363 try argv.append(try allocPrint(arena, "{s}ucrt{s}.lib", .{ lib_str, d_str }));
1364
1365 //Visual C++ 2015 Conformance Changes
1366 //https://msdn.microsoft.com/en-us/library/bb531344.aspx
1367 try argv.append("legacy_stdio_definitions.lib");
1368
1369 // msvcrt depends on kernel32 and ntdll
1370 try argv.append("kernel32.lib");
1371 try argv.append("ntdll.lib");
1372 }
1373 } else {
1374 try argv.append("-NODEFAULTLIB");
1375 if (!is_lib) {
1376 if (self.base.options.module) |module| {
1377 if (module.stage1_flags.have_winmain_crt_startup) {
1378 try argv.append("-ENTRY:WinMainCRTStartup");
1379 } else {
1380 try argv.append("-ENTRY:wWinMainCRTStartup");
1381 }
1382 } else {
1383 try argv.append("-ENTRY:wWinMainCRTStartup");
1384 }
1385 }
1386 }
1387 },
1388 }
1389
1390 // libc++ dep
1391 if (self.base.options.link_libcpp) {
1392 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
1393 try argv.append(comp.libcxx_static_lib.?.full_object_path);
1394 }
1395
1396 // libunwind dep
1397 if (self.base.options.link_libunwind) {
1398 try argv.append(comp.libunwind_static_lib.?.full_object_path);
1399 }
1400
1401 if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies) {
1402 if (!self.base.options.link_libc) {
1403 if (comp.libc_static_lib) |lib| {
1404 try argv.append(lib.full_object_path);
1405 }
1406 }
1407 // MinGW doesn't provide libssp symbols
1408 if (target.abi.isGnu()) {
1409 if (comp.libssp_static_lib) |lib| {
1410 try argv.append(lib.full_object_path);
1411 }
1412 }
1413 // MSVC compiler_rt is missing some stuff, so we build it unconditionally but
1414 // and rely on weak linkage to allow MSVC compiler_rt functions to override ours.
1415 if (comp.compiler_rt_lib) |lib| {
1416 try argv.append(lib.full_object_path);
1417 }
1418 }
1419
1420 try argv.ensureUnusedCapacity(self.base.options.system_libs.count());
1421 for (self.base.options.system_libs.keys()) |key| {
1422 const lib_basename = try allocPrint(arena, "{s}.lib", .{key});
1423 if (comp.crt_files.get(lib_basename)) |crt_file| {
1424 argv.appendAssumeCapacity(crt_file.full_object_path);
1425 continue;
1426 }
1427 if (try self.findLib(arena, lib_basename)) |full_path| {
1428 argv.appendAssumeCapacity(full_path);
1429 continue;
1430 }
1431 if (target.abi.isGnu()) {
1432 const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key});
1433 if (try self.findLib(arena, fallback_name)) |full_path| {
1434 argv.appendAssumeCapacity(full_path);
1435 continue;
1436 }
1437 }
1438 log.err("DLL import library for -l{s} not found", .{key});
1439 return error.DllImportLibraryNotFound;
1440 }
1441
1442 if (self.base.options.verbose_link) {
1443 // Skip over our own name so that the LLD linker name is the first argv item.
1444 Compilation.dump_argv(argv.items[1..]);
1445 }
1446
1447 if (std.process.can_spawn) {
1448 // If possible, we run LLD as a child process because it does not always
1449 // behave properly as a library, unfortunately.
1450 // https://github.com/ziglang/zig/issues/3825
1451 var child = std.ChildProcess.init(argv.items, arena);
1452 if (comp.clang_passthrough_mode) {
1453 child.stdin_behavior = .Inherit;
1454 child.stdout_behavior = .Inherit;
1455 child.stderr_behavior = .Inherit;
1456
1457 const term = child.spawnAndWait() catch |err| {
1458 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1459 return error.UnableToSpawnSelf;
1460 };
1461 switch (term) {
1462 .Exited => |code| {
1463 if (code != 0) {
1464 std.process.exit(code);
1465 }
1466 },
1467 else => std.process.abort(),
1468 }
1469 } else {
1470 child.stdin_behavior = .Ignore;
1471 child.stdout_behavior = .Ignore;
1472 child.stderr_behavior = .Pipe;
1473
1474 try child.spawn();
1475
1476 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
1477
1478 const term = child.wait() catch |err| {
1479 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1480 return error.UnableToSpawnSelf;
1481 };
1482
1483 switch (term) {
1484 .Exited => |code| {
1485 if (code != 0) {
1486 // TODO parse this output and surface with the Compilation API rather than
1487 // directly outputting to stderr here.
1488 std.debug.print("{s}", .{stderr});
1489 return error.LLDReportedFailure;
1490 }
1491 },
1492 else => {
1493 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
1494 return error.LLDCrashed;
1495 },
1496 }
1497
1498 if (stderr.len != 0) {
1499 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1500 }
1501 }
1502 } else {
1503 const exit_code = try lldMain(arena, argv.items, false);
1504 if (exit_code != 0) {
1505 if (comp.clang_passthrough_mode) {
1506 std.process.exit(exit_code);
1507 } else {
1508 return error.LLDReportedFailure;
1509 }
1510 }
1511 }
1512 }
1513
1514 if (!self.base.options.disable_lld_caching) {
1515 // Update the file with the digest. If it fails we can continue; it only
1516 // means that the next invocation will have an unnecessary cache miss.
1517 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
1518 log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)});
1519 };
1520 // Again failure here only means an unnecessary cache miss.
1521 man.writeManifest() catch |err| {
1522 log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
1523 };
1524 // We hang on to this lock so that the output file path can be used without
1525 // other processes clobbering it.
1526 self.base.lock = man.toOwnedLock();
1527 }
1528}
1529
1530fn findLib(self: *Coff, arena: Allocator, name: []const u8) !?[]const u8 {
1531 for (self.base.options.lib_dirs) |lib_dir| {
1532 const full_path = try fs.path.join(arena, &.{ lib_dir, name });
1533 fs.cwd().access(full_path, .{}) catch |err| switch (err) {
1534 error.FileNotFound => continue,
1535 else => |e| return e,
1536 };
1537 return full_path;
1538 }
1539 return null;
1540}
1541
1542669pub fn getDeclVAddr(
1543670 self: *Coff,
1544671 decl_index: Module.Decl.Index,
1545672 reloc_info: link.File.RelocInfo,
1546673) !u64 {
674 _ = self;
675 _ = decl_index;
1547676 _ = reloc_info;
1548 const mod = self.base.options.module.?;
1549 const decl = mod.declPtr(decl_index);
1550 assert(self.llvm_object == null);
1551 return self.text_section_virtual_address + decl.link.coff.text_offset;
677 @panic("TODO getDeclVAddr");
1552678}
1553679
1554680pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {
1555681 _ = self;
1556682 _ = module;
1557683 _ = decl;
1558 // TODO Implement this
684 log.debug("TODO implement updateDeclLineNumber", .{});
1559685}
1560686
1561pub fn deinit(self: *Coff) void {
1562 if (build_options.have_llvm) {
1563 if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
1564 }
687pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
688 // TODO https://github.com/ziglang/zig/issues/1284
689 return math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
690 math.maxInt(@TypeOf(actual_size));
691}
692
693/// Returns pointer-to-symbol described by `sym_with_loc` descriptor.
694pub fn getSymbolPtr(self: *Coff, sym_loc: SymbolWithLoc) *coff.Symbol {
695 assert(sym_loc.file == null); // TODO linking object files
696 return &self.locals.items[sym_loc.sym_index];
697}
698
699/// Returns symbol described by `sym_with_loc` descriptor.
700pub fn getSymbol(self: *Coff, sym_loc: SymbolWithLoc) coff.Symbol {
701 return self.getSymbolPtr(sym_loc).*;
702}
703
704/// Returns name of the symbol described by `sym_with_loc` descriptor.
705pub fn getSymbolName(self: *Coff, sym_loc: SymbolWithLoc) []const u8 {
706 assert(sym_loc.file == null); // TODO linking object files
707 const sym = self.locals.items[sym_loc.sym_index];
708 const offset = sym.getNameOffset() orelse return sym.getName().?;
709 return self.strtab.get(offset).?;
710}
711
712/// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor.
713/// Returns null on failure.
714pub fn getAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {
715 assert(sym_loc.file == null); // TODO linking with object files
716 return self.atom_by_index_table.get(sym_loc.sym_index);
717}
1565718
1566 self.text_block_free_list.deinit(self.base.allocator);
1567 self.offset_table.deinit(self.base.allocator);
1568 self.offset_table_free_list.deinit(self.base.allocator);
719/// Returns GOT atom that references `sym_with_loc` if one exists.
720/// Returns null otherwise.
721pub fn getGotAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {
722 const got_index = self.got_entries.get(sym_loc) orelse return null;
723 return self.atom_by_index_table.get(got_index);
1569724}
src/link/Coff/Atom.zig created+85
......@@ -0,0 +1,85 @@
1const Atom = @This();
2
3const std = @import("std");
4const coff = std.coff;
5
6const Allocator = std.mem.Allocator;
7
8const Coff = @import("../Coff.zig");
9const SymbolWithLoc = Coff.SymbolWithLoc;
10
11/// Each decl always gets a local symbol with the fully qualified name.
12/// The vaddr and size are found here directly.
13/// The file offset is found by computing the vaddr offset from the section vaddr
14/// the symbol references, and adding that to the file offset of the section.
15/// If this field is 0, it means the codegen size = 0 and there is no symbol or
16/// offset table entry.
17sym_index: u32,
18
19/// null means symbol defined by Zig source.
20file: ?u32,
21
22/// Used size of the atom
23size: u64,
24
25/// Alignment of the atom
26alignment: u32,
27
28/// Points to the previous and next neighbors, based on the `text_offset`.
29/// This can be used to find, for example, the capacity of this `Atom`.
30prev: ?*Atom,
31next: ?*Atom,
32
33pub const empty = Atom{
34 .sym_index = 0,
35 .file = null,
36 .size = 0,
37 .alignment = 0,
38 .prev = null,
39 .next = null,
40};
41
42pub fn deinit(self: *Atom, gpa: Allocator) void {
43 _ = self;
44 _ = gpa;
45}
46
47pub fn getSymbol(self: Atom, coff_file: *Coff) coff.Symbol {
48 return self.getSymbolPtr(coff_file).*;
49}
50
51pub fn getSymbolPtr(self: Atom, coff_file: *Coff) *coff.Symbol {
52 return coff_file.getSymbolPtr(.{
53 .sym_index = self.sym_index,
54 .file = self.file,
55 });
56}
57
58pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
59 return .{ .sym_index = self.sym_index, .file = self.file };
60}
61
62/// Returns how much room there is to grow in virtual address space.
63pub fn capacity(self: Atom, coff_file: *Coff) u64 {
64 const self_sym = self.getSymbol(coff_file);
65 if (self.next) |next| {
66 const next_sym = next.getSymbol(coff_file);
67 return next_sym.value - self_sym.value;
68 } else {
69 // We are the last atom.
70 // The capacity is limited only by virtual address space.
71 return std.math.maxInt(u64) - self_sym.value;
72 }
73}
74
75pub fn freeListEligible(self: Atom, coff_file: *Coff) bool {
76 // No need to keep a free list node for the last atom.
77 const next = self.next orelse return false;
78 const self_sym = self.getSymbol(coff_file);
79 const next_sym = next.getSymbol(coff_file);
80 const cap = next_sym.value - self_sym.value;
81 const ideal_cap = Coff.padToIdeal(self.size);
82 if (cap <= ideal_cap) return false;
83 const surplus = cap - ideal_cap;
84 return surplus >= Coff.min_text_capacity;
85}
src/link/Coff/lld.zig created+602
......@@ -0,0 +1,602 @@
1const std = @import("std");
2const build_options = @import("build_options");
3const allocPrint = std.fmt.allocPrint;
4const assert = std.debug.assert;
5const fs = std.fs;
6const log = std.log.scoped(.link);
7const mem = std.mem;
8
9const mingw = @import("../../mingw.zig");
10const link = @import("../../link.zig");
11const lldMain = @import("../../main.zig").lldMain;
12const trace = @import("../../tracy.zig").trace;
13
14const Allocator = mem.Allocator;
15
16const Cache = @import("../../Cache.zig");
17const Coff = @import("../Coff.zig");
18const Compilation = @import("../../Compilation.zig");
19
20pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void {
21 const tracy = trace(@src());
22 defer tracy.end();
23
24 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
25 defer arena_allocator.deinit();
26 const arena = arena_allocator.allocator();
27
28 const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
29 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
30
31 // If there is no Zig code to compile, then we should skip flushing the output file because it
32 // will not be part of the linker line anyway.
33 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
34 const use_stage1 = build_options.have_stage1 and self.base.options.use_stage1;
35 if (use_stage1) {
36 const obj_basename = try std.zig.binNameAlloc(arena, .{
37 .root_name = self.base.options.root_name,
38 .target = self.base.options.target,
39 .output_mode = .Obj,
40 });
41 switch (self.base.options.cache_mode) {
42 .incremental => break :blk try module.zig_cache_artifact_directory.join(
43 arena,
44 &[_][]const u8{obj_basename},
45 ),
46 .whole => break :blk try fs.path.join(arena, &.{
47 fs.path.dirname(full_out_path).?, obj_basename,
48 }),
49 }
50 }
51
52 try self.flushModule(comp, prog_node);
53
54 if (fs.path.dirname(full_out_path)) |dirname| {
55 break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
56 } else {
57 break :blk self.base.intermediary_basename.?;
58 }
59 } else null;
60
61 var sub_prog_node = prog_node.start("LLD Link", 0);
62 sub_prog_node.activate();
63 sub_prog_node.context.refresh();
64 defer sub_prog_node.end();
65
66 const is_lib = self.base.options.output_mode == .Lib;
67 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
68 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
69 const link_in_crt = self.base.options.link_libc and is_exe_or_dyn_lib;
70 const target = self.base.options.target;
71
72 // See link/Elf.zig for comments on how this mechanism works.
73 const id_symlink_basename = "lld.id";
74
75 var man: Cache.Manifest = undefined;
76 defer if (!self.base.options.disable_lld_caching) man.deinit();
77
78 var digest: [Cache.hex_digest_len]u8 = undefined;
79
80 if (!self.base.options.disable_lld_caching) {
81 man = comp.cache_parent.obtain();
82 self.base.releaseLock();
83
84 comptime assert(Compilation.link_hash_implementation_version == 7);
85
86 for (self.base.options.objects) |obj| {
87 _ = try man.addFile(obj.path, null);
88 man.hash.add(obj.must_link);
89 }
90 for (comp.c_object_table.keys()) |key| {
91 _ = try man.addFile(key.status.success.object_path, null);
92 }
93 try man.addOptionalFile(module_obj_path);
94 man.hash.addOptionalBytes(self.base.options.entry);
95 man.hash.addOptional(self.base.options.stack_size_override);
96 man.hash.addOptional(self.base.options.image_base_override);
97 man.hash.addListOfBytes(self.base.options.lib_dirs);
98 man.hash.add(self.base.options.skip_linker_dependencies);
99 if (self.base.options.link_libc) {
100 man.hash.add(self.base.options.libc_installation != null);
101 if (self.base.options.libc_installation) |libc_installation| {
102 man.hash.addBytes(libc_installation.crt_dir.?);
103 if (target.abi == .msvc) {
104 man.hash.addBytes(libc_installation.msvc_lib_dir.?);
105 man.hash.addBytes(libc_installation.kernel32_lib_dir.?);
106 }
107 }
108 }
109 link.hashAddSystemLibs(&man.hash, self.base.options.system_libs);
110 man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys());
111 man.hash.addOptional(self.base.options.subsystem);
112 man.hash.add(self.base.options.is_test);
113 man.hash.add(self.base.options.tsaware);
114 man.hash.add(self.base.options.nxcompat);
115 man.hash.add(self.base.options.dynamicbase);
116 // strip does not need to go into the linker hash because it is part of the hash namespace
117 man.hash.addOptional(self.base.options.major_subsystem_version);
118 man.hash.addOptional(self.base.options.minor_subsystem_version);
119
120 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
121 _ = try man.hit();
122 digest = man.final();
123 var prev_digest_buf: [digest.len]u8 = undefined;
124 const prev_digest: []u8 = Cache.readSmallFile(
125 directory.handle,
126 id_symlink_basename,
127 &prev_digest_buf,
128 ) catch |err| blk: {
129 log.debug("COFF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
130 // Handle this as a cache miss.
131 break :blk prev_digest_buf[0..0];
132 };
133 if (mem.eql(u8, prev_digest, &digest)) {
134 log.debug("COFF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
135 // Hot diggity dog! The output binary is already there.
136 self.base.lock = man.toOwnedLock();
137 return;
138 }
139 log.debug("COFF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
140
141 // We are about to change the output file to be different, so we invalidate the build hash now.
142 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
143 error.FileNotFound => {},
144 else => |e| return e,
145 };
146 }
147
148 if (self.base.options.output_mode == .Obj) {
149 // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy
150 // here. TODO: think carefully about how we can avoid this redundant operation when doing
151 // build-obj. See also the corresponding TODO in linkAsArchive.
152 const the_object_path = blk: {
153 if (self.base.options.objects.len != 0)
154 break :blk self.base.options.objects[0].path;
155
156 if (comp.c_object_table.count() != 0)
157 break :blk comp.c_object_table.keys()[0].status.success.object_path;
158
159 if (module_obj_path) |p|
160 break :blk p;
161
162 // TODO I think this is unreachable. Audit this situation when solving the above TODO
163 // regarding eliding redundant object -> object transformations.
164 return error.NoObjectsToLink;
165 };
166 // This can happen when using --enable-cache and using the stage1 backend. In this case
167 // we can skip the file copy.
168 if (!mem.eql(u8, the_object_path, full_out_path)) {
169 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
170 }
171 } else {
172 // Create an LLD command line and invoke it.
173 var argv = std.ArrayList([]const u8).init(self.base.allocator);
174 defer argv.deinit();
175 // We will invoke ourselves as a child process to gain access to LLD.
176 // This is necessary because LLD does not behave properly as a library -
177 // it calls exit() and does not reset all global data between invocations.
178 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "lld-link" });
179
180 try argv.append("-ERRORLIMIT:0");
181 try argv.append("-NOLOGO");
182 if (!self.base.options.strip) {
183 try argv.append("-DEBUG");
184 }
185 if (self.base.options.lto) {
186 switch (self.base.options.optimize_mode) {
187 .Debug => {},
188 .ReleaseSmall => try argv.append("-OPT:lldlto=2"),
189 .ReleaseFast, .ReleaseSafe => try argv.append("-OPT:lldlto=3"),
190 }
191 }
192 if (self.base.options.output_mode == .Exe) {
193 const stack_size = self.base.options.stack_size_override orelse 16777216;
194 try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size}));
195 }
196 if (self.base.options.image_base_override) |image_base| {
197 try argv.append(try std.fmt.allocPrint(arena, "-BASE:{d}", .{image_base}));
198 }
199
200 if (target.cpu.arch == .i386) {
201 try argv.append("-MACHINE:X86");
202 } else if (target.cpu.arch == .x86_64) {
203 try argv.append("-MACHINE:X64");
204 } else if (target.cpu.arch.isARM()) {
205 if (target.cpu.arch.ptrBitWidth() == 32) {
206 try argv.append("-MACHINE:ARM");
207 } else {
208 try argv.append("-MACHINE:ARM64");
209 }
210 }
211
212 for (self.base.options.force_undefined_symbols.keys()) |symbol| {
213 try argv.append(try allocPrint(arena, "-INCLUDE:{s}", .{symbol}));
214 }
215
216 if (is_dyn_lib) {
217 try argv.append("-DLL");
218 }
219
220 if (self.base.options.entry) |entry| {
221 try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{entry}));
222 }
223
224 if (self.base.options.tsaware) {
225 try argv.append("-tsaware");
226 }
227 if (self.base.options.nxcompat) {
228 try argv.append("-nxcompat");
229 }
230 if (self.base.options.dynamicbase) {
231 try argv.append("-dynamicbase");
232 }
233
234 try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path}));
235
236 if (self.base.options.implib_emit) |emit| {
237 const implib_out_path = try emit.directory.join(arena, &[_][]const u8{emit.sub_path});
238 try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path}));
239 }
240
241 if (self.base.options.link_libc) {
242 if (self.base.options.libc_installation) |libc_installation| {
243 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?}));
244
245 if (target.abi == .msvc) {
246 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?}));
247 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?}));
248 }
249 }
250 }
251
252 for (self.base.options.lib_dirs) |lib_dir| {
253 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir}));
254 }
255
256 try argv.ensureUnusedCapacity(self.base.options.objects.len);
257 for (self.base.options.objects) |obj| {
258 if (obj.must_link) {
259 argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{s}", .{obj.path}));
260 } else {
261 argv.appendAssumeCapacity(obj.path);
262 }
263 }
264
265 for (comp.c_object_table.keys()) |key| {
266 try argv.append(key.status.success.object_path);
267 }
268
269 if (module_obj_path) |p| {
270 try argv.append(p);
271 }
272
273 const resolved_subsystem: ?std.Target.SubSystem = blk: {
274 if (self.base.options.subsystem) |explicit| break :blk explicit;
275 switch (target.os.tag) {
276 .windows => {
277 if (self.base.options.module) |module| {
278 if (module.stage1_flags.have_dllmain_crt_startup or is_dyn_lib)
279 break :blk null;
280 if (module.stage1_flags.have_c_main or self.base.options.is_test or
281 module.stage1_flags.have_winmain_crt_startup or
282 module.stage1_flags.have_wwinmain_crt_startup)
283 {
284 break :blk .Console;
285 }
286 if (module.stage1_flags.have_winmain or module.stage1_flags.have_wwinmain)
287 break :blk .Windows;
288 }
289 },
290 .uefi => break :blk .EfiApplication,
291 else => {},
292 }
293 break :blk null;
294 };
295
296 const Mode = enum { uefi, win32 };
297 const mode: Mode = mode: {
298 if (resolved_subsystem) |subsystem| {
299 const subsystem_suffix = ss: {
300 if (self.base.options.major_subsystem_version) |major| {
301 if (self.base.options.minor_subsystem_version) |minor| {
302 break :ss try allocPrint(arena, ",{d}.{d}", .{ major, minor });
303 } else {
304 break :ss try allocPrint(arena, ",{d}", .{major});
305 }
306 }
307 break :ss "";
308 };
309
310 switch (subsystem) {
311 .Console => {
312 try argv.append(try allocPrint(arena, "-SUBSYSTEM:console{s}", .{
313 subsystem_suffix,
314 }));
315 break :mode .win32;
316 },
317 .EfiApplication => {
318 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_application{s}", .{
319 subsystem_suffix,
320 }));
321 break :mode .uefi;
322 },
323 .EfiBootServiceDriver => {
324 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_boot_service_driver{s}", .{
325 subsystem_suffix,
326 }));
327 break :mode .uefi;
328 },
329 .EfiRom => {
330 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_rom{s}", .{
331 subsystem_suffix,
332 }));
333 break :mode .uefi;
334 },
335 .EfiRuntimeDriver => {
336 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_runtime_driver{s}", .{
337 subsystem_suffix,
338 }));
339 break :mode .uefi;
340 },
341 .Native => {
342 try argv.append(try allocPrint(arena, "-SUBSYSTEM:native{s}", .{
343 subsystem_suffix,
344 }));
345 break :mode .win32;
346 },
347 .Posix => {
348 try argv.append(try allocPrint(arena, "-SUBSYSTEM:posix{s}", .{
349 subsystem_suffix,
350 }));
351 break :mode .win32;
352 },
353 .Windows => {
354 try argv.append(try allocPrint(arena, "-SUBSYSTEM:windows{s}", .{
355 subsystem_suffix,
356 }));
357 break :mode .win32;
358 },
359 }
360 } else if (target.os.tag == .uefi) {
361 break :mode .uefi;
362 } else {
363 break :mode .win32;
364 }
365 };
366
367 switch (mode) {
368 .uefi => try argv.appendSlice(&[_][]const u8{
369 "-BASE:0",
370 "-ENTRY:EfiMain",
371 "-OPT:REF",
372 "-SAFESEH:NO",
373 "-MERGE:.rdata=.data",
374 "-ALIGN:32",
375 "-NODEFAULTLIB",
376 "-SECTION:.xdata,D",
377 }),
378 .win32 => {
379 if (link_in_crt) {
380 if (target.abi.isGnu()) {
381 try argv.append("-lldmingw");
382
383 if (target.cpu.arch == .i386) {
384 try argv.append("-ALTERNATENAME:__image_base__=___ImageBase");
385 } else {
386 try argv.append("-ALTERNATENAME:__image_base__=__ImageBase");
387 }
388
389 if (is_dyn_lib) {
390 try argv.append(try comp.get_libc_crt_file(arena, "dllcrt2.obj"));
391 if (target.cpu.arch == .i386) {
392 try argv.append("-ALTERNATENAME:__DllMainCRTStartup@12=_DllMainCRTStartup@12");
393 } else {
394 try argv.append("-ALTERNATENAME:_DllMainCRTStartup=DllMainCRTStartup");
395 }
396 } else {
397 try argv.append(try comp.get_libc_crt_file(arena, "crt2.obj"));
398 }
399
400 try argv.append(try comp.get_libc_crt_file(arena, "mingw32.lib"));
401 try argv.append(try comp.get_libc_crt_file(arena, "mingwex.lib"));
402 try argv.append(try comp.get_libc_crt_file(arena, "msvcrt-os.lib"));
403
404 for (mingw.always_link_libs) |name| {
405 if (!self.base.options.system_libs.contains(name)) {
406 const lib_basename = try allocPrint(arena, "{s}.lib", .{name});
407 try argv.append(try comp.get_libc_crt_file(arena, lib_basename));
408 }
409 }
410 } else {
411 const lib_str = switch (self.base.options.link_mode) {
412 .Dynamic => "",
413 .Static => "lib",
414 };
415 const d_str = switch (self.base.options.optimize_mode) {
416 .Debug => "d",
417 else => "",
418 };
419 switch (self.base.options.link_mode) {
420 .Static => try argv.append(try allocPrint(arena, "libcmt{s}.lib", .{d_str})),
421 .Dynamic => try argv.append(try allocPrint(arena, "msvcrt{s}.lib", .{d_str})),
422 }
423
424 try argv.append(try allocPrint(arena, "{s}vcruntime{s}.lib", .{ lib_str, d_str }));
425 try argv.append(try allocPrint(arena, "{s}ucrt{s}.lib", .{ lib_str, d_str }));
426
427 //Visual C++ 2015 Conformance Changes
428 //https://msdn.microsoft.com/en-us/library/bb531344.aspx
429 try argv.append("legacy_stdio_definitions.lib");
430
431 // msvcrt depends on kernel32 and ntdll
432 try argv.append("kernel32.lib");
433 try argv.append("ntdll.lib");
434 }
435 } else {
436 try argv.append("-NODEFAULTLIB");
437 if (!is_lib) {
438 if (self.base.options.module) |module| {
439 if (module.stage1_flags.have_winmain_crt_startup) {
440 try argv.append("-ENTRY:WinMainCRTStartup");
441 } else {
442 try argv.append("-ENTRY:wWinMainCRTStartup");
443 }
444 } else {
445 try argv.append("-ENTRY:wWinMainCRTStartup");
446 }
447 }
448 }
449 },
450 }
451
452 // libc++ dep
453 if (self.base.options.link_libcpp) {
454 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
455 try argv.append(comp.libcxx_static_lib.?.full_object_path);
456 }
457
458 // libunwind dep
459 if (self.base.options.link_libunwind) {
460 try argv.append(comp.libunwind_static_lib.?.full_object_path);
461 }
462
463 if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies) {
464 if (!self.base.options.link_libc) {
465 if (comp.libc_static_lib) |lib| {
466 try argv.append(lib.full_object_path);
467 }
468 }
469 // MinGW doesn't provide libssp symbols
470 if (target.abi.isGnu()) {
471 if (comp.libssp_static_lib) |lib| {
472 try argv.append(lib.full_object_path);
473 }
474 }
475 // MSVC compiler_rt is missing some stuff, so we build it unconditionally but
476 // and rely on weak linkage to allow MSVC compiler_rt functions to override ours.
477 if (comp.compiler_rt_lib) |lib| {
478 try argv.append(lib.full_object_path);
479 }
480 }
481
482 try argv.ensureUnusedCapacity(self.base.options.system_libs.count());
483 for (self.base.options.system_libs.keys()) |key| {
484 const lib_basename = try allocPrint(arena, "{s}.lib", .{key});
485 if (comp.crt_files.get(lib_basename)) |crt_file| {
486 argv.appendAssumeCapacity(crt_file.full_object_path);
487 continue;
488 }
489 if (try findLib(arena, lib_basename, self.base.options.lib_dirs)) |full_path| {
490 argv.appendAssumeCapacity(full_path);
491 continue;
492 }
493 if (target.abi.isGnu()) {
494 const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key});
495 if (try findLib(arena, fallback_name, self.base.options.lib_dirs)) |full_path| {
496 argv.appendAssumeCapacity(full_path);
497 continue;
498 }
499 }
500 log.err("DLL import library for -l{s} not found", .{key});
501 return error.DllImportLibraryNotFound;
502 }
503
504 if (self.base.options.verbose_link) {
505 // Skip over our own name so that the LLD linker name is the first argv item.
506 Compilation.dump_argv(argv.items[1..]);
507 }
508
509 if (std.process.can_spawn) {
510 // If possible, we run LLD as a child process because it does not always
511 // behave properly as a library, unfortunately.
512 // https://github.com/ziglang/zig/issues/3825
513 var child = std.ChildProcess.init(argv.items, arena);
514 if (comp.clang_passthrough_mode) {
515 child.stdin_behavior = .Inherit;
516 child.stdout_behavior = .Inherit;
517 child.stderr_behavior = .Inherit;
518
519 const term = child.spawnAndWait() catch |err| {
520 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
521 return error.UnableToSpawnSelf;
522 };
523 switch (term) {
524 .Exited => |code| {
525 if (code != 0) {
526 std.process.exit(code);
527 }
528 },
529 else => std.process.abort(),
530 }
531 } else {
532 child.stdin_behavior = .Ignore;
533 child.stdout_behavior = .Ignore;
534 child.stderr_behavior = .Pipe;
535
536 try child.spawn();
537
538 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
539
540 const term = child.wait() catch |err| {
541 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
542 return error.UnableToSpawnSelf;
543 };
544
545 switch (term) {
546 .Exited => |code| {
547 if (code != 0) {
548 // TODO parse this output and surface with the Compilation API rather than
549 // directly outputting to stderr here.
550 std.debug.print("{s}", .{stderr});
551 return error.LLDReportedFailure;
552 }
553 },
554 else => {
555 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
556 return error.LLDCrashed;
557 },
558 }
559
560 if (stderr.len != 0) {
561 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
562 }
563 }
564 } else {
565 const exit_code = try lldMain(arena, argv.items, false);
566 if (exit_code != 0) {
567 if (comp.clang_passthrough_mode) {
568 std.process.exit(exit_code);
569 } else {
570 return error.LLDReportedFailure;
571 }
572 }
573 }
574 }
575
576 if (!self.base.options.disable_lld_caching) {
577 // Update the file with the digest. If it fails we can continue; it only
578 // means that the next invocation will have an unnecessary cache miss.
579 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
580 log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)});
581 };
582 // Again failure here only means an unnecessary cache miss.
583 man.writeManifest() catch |err| {
584 log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
585 };
586 // We hang on to this lock so that the output file path can be used without
587 // other processes clobbering it.
588 self.base.lock = man.toOwnedLock();
589 }
590}
591
592fn findLib(arena: Allocator, name: []const u8, lib_dirs: []const []const u8) !?[]const u8 {
593 for (lib_dirs) |lib_dir| {
594 const full_path = try fs.path.join(arena, &.{ lib_dir, name });
595 fs.cwd().access(full_path, .{}) catch |err| switch (err) {
596 error.FileNotFound => continue,
597 else => |e| return e,
598 };
599 return full_path;
600 }
601 return null;
602}
src/link/MachO.zig+1-2
......@@ -26,7 +26,7 @@ const trace = @import("../tracy.zig").trace;
2626const Air = @import("../Air.zig");
2727const Allocator = mem.Allocator;
2828const Archive = @import("MachO/Archive.zig");
29const Atom = @import("MachO/Atom.zig");
29pub const Atom = @import("MachO/Atom.zig");
3030const Cache = @import("../Cache.zig");
3131const CodeSignature = @import("MachO/CodeSignature.zig");
3232const Compilation = @import("../Compilation.zig");
......@@ -44,7 +44,6 @@ const Type = @import("../type.zig").Type;
4444const TypedValue = @import("../TypedValue.zig");
4545const Value = @import("../value.zig").Value;
4646
47pub const TextBlock = Atom;
4847pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
4948
5049pub const base_tag: File.Tag = File.Tag.macho;
src/link/MachO/DebugSymbols.zig-1
......@@ -18,7 +18,6 @@ const Dwarf = @import("../Dwarf.zig");
1818const MachO = @import("../MachO.zig");
1919const Module = @import("../../Module.zig");
2020const StringTable = @import("../strtab.zig").StringTable;
21const TextBlock = MachO.TextBlock;
2221const Type = @import("../../type.zig").Type;
2322
2423base: *MachO,