authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-08-30 14:29:41+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-08-30 14:29:41+02:00
log7ef0c9d298d5645b4b6d1ffdfd34c69c04423ed2
tree12b54077e1533b1abcb8d9a9cd1222b24478f5fc
parentb64e4c5bf28286091ff97245e61f08b897e3eb5e
parente57fbe8069e672483b0c9ae1fa28c00812596306
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12677 from ziglang/coff-linker

coff: initial rewrite of the COFF/PE linker

23 files changed, 2120 insertions(+), 1265 deletions(-)

CMakeLists.txt+3
......@@ -753,6 +753,9 @@ 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/Object.zig"
758 "${CMAKE_SOURCE_DIR}/src/link/Coff/lld.zig"
756759 "${CMAKE_SOURCE_DIR}/src/link/Elf.zig"
757760 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
758761 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"
lib/std/coff.zig+24
......@@ -372,6 +372,15 @@ pub const SectionHeader = extern struct {
372372 return std.math.powi(u16, 2, self.flags.ALIGN - 1) catch unreachable;
373373 }
374374
375 pub fn setAlignment(self: *SectionHeader, new_alignment: u16) void {
376 assert(new_alignment > 0 and new_alignment <= 8192);
377 self.flags.ALIGN = std.math.log2(new_alignment);
378 }
379
380 pub fn isCode(self: SectionHeader) bool {
381 return self.flags.CNT_CODE == 0b1;
382 }
383
375384 pub fn isComdat(self: SectionHeader) bool {
376385 return self.flags.LNK_COMDAT == 0b1;
377386 }
......@@ -847,6 +856,21 @@ pub const MachineType = enum(u16) {
847856 /// MIPS little-endian WCE v2
848857 WCEMIPSV2 = 0x169,
849858
859 pub fn fromTargetCpuArch(arch: std.Target.Cpu.Arch) MachineType {
860 return switch (arch) {
861 .arm => .ARM,
862 .powerpc => .POWERPC,
863 .riscv32 => .RISCV32,
864 .thumb => .Thumb,
865 .i386 => .I386,
866 .aarch64 => .ARM64,
867 .riscv64 => .RISCV64,
868 .x86_64 => .X64,
869 // there's cases we don't (yet) handle
870 else => unreachable,
871 };
872 }
873
850874 pub fn toTargetCpuArch(machine_type: MachineType) ?std.Target.Cpu.Arch {
851875 return switch (machine_type) {
852876 .ARM => .arm,
lib/std/start.zig-2
......@@ -36,8 +36,6 @@ comptime {
3636 if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) {
3737 @export(main2, .{ .name = "main" });
3838 }
39 } else if (builtin.os.tag == .windows) {
40 @export(wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" });
4139 } else if (builtin.os.tag == .wasi and @hasDecl(root, "main")) {
4240 @export(wasiMain2, .{ .name = "_start" });
4341 } else {
src/Compilation.zig-1
......@@ -1127,7 +1127,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
11271127 link_eh_frame_hdr or
11281128 options.link_emit_relocs or
11291129 options.output_mode == .Lib or
1130 options.image_base_override != null or
11311130 options.linker_script != null or options.version_script != null or
11321131 options.emit_implib != null or
11331132 build_id)
src/Module.zig+7-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 },
......@@ -5391,6 +5391,9 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void {
53915391 if (mod.comp.bin_file.cast(link.File.Wasm)) |wasm| {
53925392 wasm.deleteExport(exp.link.wasm);
53935393 }
5394 if (mod.comp.bin_file.cast(link.File.Coff)) |coff| {
5395 coff.deleteExport(exp.link.coff);
5396 }
53945397 if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| {
53955398 failed_kv.value.destroy(mod.gpa);
53965399 }
......@@ -5680,9 +5683,9 @@ pub fn allocateNewDecl(
56805683 .zir_decl_index = 0,
56815684 .src_scope = src_scope,
56825685 .link = switch (mod.comp.bin_file.tag) {
5683 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
5686 .coff => .{ .coff = link.File.Coff.Atom.empty },
56845687 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
5685 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
5688 .macho => .{ .macho = link.File.MachO.Atom.empty },
56865689 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
56875690 .c => .{ .c = {} },
56885691 .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+7-9
......@@ -3466,19 +3466,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
34663466 // on linking.
34673467 const mod = self.bin_file.options.module.?;
34683468 if (self.air.value(callee)) |func_value| {
3469 if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {
3469 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
34703470 if (func_value.castTag(.function)) |func_payload| {
34713471 const func = func_payload.data;
34723472 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
34733473 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
34743474 const fn_owner_decl = mod.declPtr(func.owner_decl);
3475 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
3475 const got_addr = 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 };
34823479
34833480 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr });
34843481
......@@ -3546,6 +3543,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
35463543 } else {
35473544 return self.fail("TODO implement calling bitcasted functions", .{});
35483545 }
3546 } else if (self.bin_file.cast(link.File.Coff)) |_| {
3547 return self.fail("TODO implement calling in COFF for {}", .{self.target.cpu.arch});
35493548 } else unreachable;
35503549 } else {
35513550 assert(ty.zigTypeTag() == .Pointer);
......@@ -5109,9 +5108,8 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
51095108 // the linker has enough info to perform relocations.
51105109 assert(decl.link.macho.sym_index != 0);
51115110 return MCValue{ .got_load = decl.link.macho.sym_index };
5112 } 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 };
5111 } else if (self.bin_file.cast(link.File.Coff)) |_| {
5112 return self.fail("TODO codegen COFF const Decl pointer", .{});
51155113 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
51165114 try p9.seeDecl(decl_index);
51175115 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
src/arch/arm/CodeGen.zig+5-9
......@@ -3698,7 +3698,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
36983698 // Due to incremental compilation, how function calls are generated depends
36993699 // on linking.
37003700 switch (self.bin_file.tag) {
3701 .elf, .coff => {
3701 .elf => {
37023702 if (self.air.value(callee)) |func_value| {
37033703 if (func_value.castTag(.function)) |func_payload| {
37043704 const func = func_payload.data;
......@@ -3709,11 +3709,7 @@ 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;
3716
3712 } else unreachable;
37173713 try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr });
37183714 } else if (func_value.castTag(.extern_fn)) |_| {
37193715 return self.fail("TODO implement calling extern functions", .{});
......@@ -3751,6 +3747,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
37513747 }
37523748 },
37533749 .macho => unreachable, // unsupported architecture for MachO
3750 .coff => return self.fail("TODO implement call in COFF for {}", .{self.target.cpu.arch}),
37543751 .plan9 => return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch}),
37553752 else => unreachable,
37563753 }
......@@ -5548,9 +5545,8 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
55485545 return MCValue{ .memory = got_addr };
55495546 } else if (self.bin_file.cast(link.File.MachO)) |_| {
55505547 unreachable; // unsupported architecture for MachO
5551 } 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 };
5548 } else if (self.bin_file.cast(link.File.Coff)) |_| {
5549 return self.fail("TODO codegen COFF const Decl pointer", .{});
55545550 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
55555551 try p9.seeDecl(decl_index);
55565552 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
src/arch/riscv64/CodeGen.zig+7-9
......@@ -1718,7 +1718,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
17181718
17191719 // Due to incremental compilation, how function calls are generated depends
17201720 // on linking.
1721 if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {
1721 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
17221722 for (info.args) |mc_arg, arg_i| {
17231723 const arg = args[arg_i];
17241724 const arg_ty = self.air.typeOf(arg);
......@@ -1752,13 +1752,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
17521752 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
17531753 const mod = self.bin_file.options.module.?;
17541754 const fn_owner_decl = mod.declPtr(func.owner_decl);
1755 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
1755 const got_addr = 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 };
17621759
17631760 try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });
17641761 _ = try self.addInst(.{
......@@ -1777,6 +1774,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
17771774 } else {
17781775 return self.fail("TODO implement calling runtime known function pointer", .{});
17791776 }
1777 } else if (self.bin_file.cast(link.File.Coff)) |_| {
1778 return self.fail("TODO implement calling in COFF for {}", .{self.target.cpu.arch});
17801779 } else if (self.bin_file.cast(link.File.MachO)) |_| {
17811780 unreachable; // unsupported architecture for MachO
17821781 } else if (self.bin_file.cast(link.File.Plan9)) |_| {
......@@ -2591,9 +2590,8 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
25912590 // TODO I'm hacking my way through here by repurposing .memory for storing
25922591 // index to the GOT target symbol index.
25932592 return MCValue{ .memory = decl.link.macho.sym_index };
2594 } 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 };
2593 } else if (self.bin_file.cast(link.File.Coff)) |_| {
2594 return self.fail("TODO codegen COFF const Decl pointer", .{});
25972595 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
25982596 try p9.seeDecl(decl_index);
25992597 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
src/arch/x86_64/CodeGen.zig+49-17
......@@ -2657,22 +2657,26 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue
26572657 .direct_load,
26582658 => |sym_index| {
26592659 const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*));
2660 const mod = self.bin_file.options.module.?;
2661 const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl);
2662 const atom_index = if (self.bin_file.tag == link.File.MachO.base_tag)
2663 fn_owner_decl.link.macho.sym_index
2664 else
2665 fn_owner_decl.link.coff.sym_index;
26602666 const flags: u2 = switch (ptr) {
26612667 .got_load => 0b00,
26622668 .direct_load => 0b01,
26632669 else => unreachable,
26642670 };
2665 const mod = self.bin_file.options.module.?;
2666 const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl);
26672671 _ = try self.addInst(.{
2668 .tag = .lea_pie,
2672 .tag = .lea_pic,
26692673 .ops = Mir.Inst.Ops.encode(.{
26702674 .reg1 = registerAlias(reg, abi_size),
26712675 .flags = flags,
26722676 }),
26732677 .data = .{
26742678 .relocation = .{
2675 .atom_index = fn_owner_decl.link.macho.sym_index,
2679 .atom_index = atom_index,
26762680 .sym_index = sym_index,
26772681 },
26782682 },
......@@ -3961,20 +3965,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
39613965 // Due to incremental compilation, how function calls are generated depends
39623966 // on linking.
39633967 const mod = self.bin_file.options.module.?;
3964 if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {
3968 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
39653969 if (self.air.value(callee)) |func_value| {
39663970 if (func_value.castTag(.function)) |func_payload| {
39673971 const func = func_payload.data;
39683972 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
39693973 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
39703974 const fn_owner_decl = mod.declPtr(func.owner_decl);
3971 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
3975 const got_addr = blk: {
39723976 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
39733977 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;
3978 };
39783979 _ = try self.addInst(.{
39793980 .tag = .call,
39803981 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
......@@ -3998,14 +3999,47 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
39983999 .data = undefined,
39994000 });
40004001 }
4001 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4002 } else if (self.bin_file.cast(link.File.Coff)) |_| {
40024003 if (self.air.value(callee)) |func_value| {
40034004 if (func_value.castTag(.function)) |func_payload| {
40044005 const func = func_payload.data;
40054006 const fn_owner_decl = mod.declPtr(func.owner_decl);
40064007 try self.genSetReg(Type.initTag(.usize), .rax, .{
4007 .got_load = fn_owner_decl.link.macho.sym_index,
4008 .got_load = fn_owner_decl.link.coff.sym_index,
40084009 });
4010 _ = try self.addInst(.{
4011 .tag = .call,
4012 .ops = Mir.Inst.Ops.encode(.{
4013 .reg1 = .rax,
4014 .flags = 0b01,
4015 }),
4016 .data = undefined,
4017 });
4018 } else if (func_value.castTag(.extern_fn)) |_| {
4019 return self.fail("TODO implement calling extern functions", .{});
4020 } else {
4021 return self.fail("TODO implement calling bitcasted functions", .{});
4022 }
4023 } else {
4024 assert(ty.zigTypeTag() == .Pointer);
4025 const mcv = try self.resolveInst(callee);
4026 try self.genSetReg(Type.initTag(.usize), .rax, mcv);
4027 _ = try self.addInst(.{
4028 .tag = .call,
4029 .ops = Mir.Inst.Ops.encode(.{
4030 .reg1 = .rax,
4031 .flags = 0b01,
4032 }),
4033 .data = undefined,
4034 });
4035 }
4036 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4037 if (self.air.value(callee)) |func_value| {
4038 if (func_value.castTag(.function)) |func_payload| {
4039 const func = func_payload.data;
4040 const fn_owner_decl = mod.declPtr(func.owner_decl);
4041 const sym_index = fn_owner_decl.link.macho.sym_index;
4042 try self.genSetReg(Type.initTag(.usize), .rax, .{ .got_load = sym_index });
40094043 // callq *%rax
40104044 _ = try self.addInst(.{
40114045 .tag = .call,
......@@ -6842,13 +6876,11 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
68426876 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
68436877 return MCValue{ .memory = got_addr };
68446878 } else if (self.bin_file.cast(link.File.MachO)) |_| {
6845 // Because MachO is PIE-always-on, we defer memory address resolution until
6846 // the linker has enough info to perform relocations.
68476879 assert(decl.link.macho.sym_index != 0);
68486880 return MCValue{ .got_load = decl.link.macho.sym_index };
6849 } 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 };
6881 } else if (self.bin_file.cast(link.File.Coff)) |_| {
6882 assert(decl.link.coff.sym_index != 0);
6883 return MCValue{ .got_load = decl.link.coff.sym_index };
68526884 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
68536885 try p9.seeDecl(decl_index);
68546886 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
src/arch/x86_64/Emit.zig+29-11
......@@ -137,7 +137,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
137137 .fld => try emit.mirFld(inst),
138138
139139 .lea => try emit.mirLea(inst),
140 .lea_pie => try emit.mirLeaPie(inst),
140 .lea_pic => try emit.mirLeaPic(inst),
141141
142142 .shl => try emit.mirShift(.shl, inst),
143143 .sal => try emit.mirShift(.sal, inst),
......@@ -338,7 +338,7 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
338338 .base = ops.reg1,
339339 }), emit.code);
340340 },
341 0b11 => return emit.fail("TODO unused JMP/CALL variant 0b11", .{}),
341 0b11 => return emit.fail("TODO unused variant jmp/call 0b11", .{}),
342342 }
343343}
344344
......@@ -784,7 +784,7 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
784784 // FD
785785 return lowerToFdEnc(.mov, ops.reg1, imm, emit.code);
786786 },
787 else => return emit.fail("TODO unused variant: movabs 0b{b}", .{ops.flags}),
787 else => return emit.fail("TODO unused movabs variant", .{}),
788788 }
789789}
790790
......@@ -978,12 +978,17 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
978978 }
979979}
980980
981fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
981fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
982982 const tag = emit.mir.instructions.items(.tag)[inst];
983 assert(tag == .lea_pie);
983 assert(tag == .lea_pic);
984984 const ops = emit.mir.instructions.items(.ops)[inst].decode();
985985 const relocation = emit.mir.instructions.items(.data)[inst].relocation;
986986
987 switch (ops.flags) {
988 0b00, 0b01 => {},
989 else => return emit.fail("TODO unused LEA PIC variants 0b10 and 0b11", .{}),
990 }
991
987992 // lea reg1, [rip + reloc]
988993 // RM
989994 try lowerToRmEnc(
......@@ -994,16 +999,17 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
994999 );
9951000
9961001 const end_offset = emit.code.items.len;
1002 const gpa = emit.bin_file.allocator;
9971003
9981004 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
9991005 const reloc_type = switch (ops.flags) {
10001006 0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT),
10011007 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1002 else => return emit.fail("TODO unused LEA PIE variants 0b10 and 0b11", .{}),
1008 else => unreachable,
10031009 };
10041010 const atom = macho_file.atom_by_index_table.get(relocation.atom_index).?;
10051011 log.debug("adding reloc of type {} to local @{d}", .{ reloc_type, relocation.sym_index });
1006 try atom.relocs.append(emit.bin_file.allocator, .{
1012 try atom.relocs.append(gpa, .{
10071013 .offset = @intCast(u32, end_offset - 4),
10081014 .target = .{ .sym_index = relocation.sym_index, .file = null },
10091015 .addend = 0,
......@@ -1012,11 +1018,23 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
10121018 .length = 2,
10131019 .@"type" = reloc_type,
10141020 });
1021 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
1022 const atom = coff_file.atom_by_index_table.get(relocation.atom_index).?;
1023 try atom.addRelocation(coff_file, .{
1024 .@"type" = switch (ops.flags) {
1025 0b00 => .got,
1026 0b01 => .direct,
1027 else => unreachable,
1028 },
1029 .target = .{ .sym_index = relocation.sym_index, .file = null },
1030 .offset = @intCast(u32, end_offset - 4),
1031 .addend = 0,
1032 .pcrel = true,
1033 .length = 2,
1034 .prev_vaddr = atom.getSymbol(coff_file).value,
1035 });
10151036 } else {
1016 return emit.fail(
1017 "TODO implement lea reg, [rip + reloc] for linking backends different than MachO",
1018 .{},
1019 );
1037 return emit.fail("TODO implement lea reg, [rip + reloc] for linking backends different than MachO", .{});
10201038 }
10211039}
10221040
src/arch/x86_64/Mir.zig+6-7
......@@ -178,11 +178,11 @@ pub const Inst = struct {
178178 lea,
179179
180180 /// ops flags: form:
181 /// 0b00 reg1, [rip + reloc] // via GOT emits X86_64_RELOC_GOT relocation
182 /// 0b01 reg1, [rip + reloc] // direct load emits X86_64_RELOC_SIGNED relocation
181 /// 0b00 reg1, [rip + reloc] // via GOT PIC
182 /// 0b01 reg1, [rip + reloc] // direct load PIC
183183 /// Notes:
184184 /// * `Data` contains `relocation`
185 lea_pie,
185 lea_pic,
186186
187187 /// ops flags: form:
188188 /// 0b00 reg1, 1
......@@ -242,15 +242,14 @@ pub const Inst = struct {
242242 imul_complex,
243243
244244 /// ops flags: form:
245 /// 0bX0 reg1, imm64
246 /// 0bX1 rax, moffs64
245 /// 0b00 reg1, imm64
246 /// 0b01 rax, moffs64
247247 /// Notes:
248248 /// * If reg1 is 64-bit, the immediate is 64-bit and stored
249249 /// within extra data `Imm64`.
250 /// * For 0bX1, reg1 (or reg2) need to be
250 /// * For 0b01, reg1 (or reg2) need to be
251251 /// a version of rax. If reg1 == .none, then reg2 == .rax,
252252 /// or vice versa.
253 /// TODO handle scaling
254253 movabs,
255254
256255 /// ops flags: form:
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+1247-1186
......@@ -1,131 +1,179 @@
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 fmt = std.fmt;
9const log = std.log.scoped(.link);
10const math = std.math;
1011const mem = std.mem;
1112
12const lldMain = @import("../main.zig").lldMain;
13const trace = @import("../tracy.zig").trace;
14const Module = @import("../Module.zig");
15const Compilation = @import("../Compilation.zig");
13const Allocator = std.mem.Allocator;
14
1615const codegen = @import("../codegen.zig");
1716const link = @import("../link.zig");
18const build_options = @import("build_options");
19const Cache = @import("../Cache.zig");
20const mingw = @import("../mingw.zig");
17const lld = @import("Coff/lld.zig");
18const trace = @import("../tracy.zig").trace;
19
2120const Air = @import("../Air.zig");
21pub const Atom = @import("Coff/Atom.zig");
22const Compilation = @import("../Compilation.zig");
2223const Liveness = @import("../Liveness.zig");
2324const LlvmObject = @import("../codegen/llvm.zig").Object;
25const Module = @import("../Module.zig");
26const Object = @import("Coff/Object.zig");
27const StringTable = @import("strtab.zig").StringTable;
2428const TypedValue = @import("../TypedValue.zig");
2529
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
3730pub const base_tag: link.File.Tag = .coff;
3831
3932const msdos_stub = @embedFile("msdos-stub.bin");
33const N_DATA_DIRS: u5 = 16;
4034
4135/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
4236llvm_object: ?*LlvmObject = null,
4337
4438base: link.File,
45ptr_width: PtrWidth,
4639error_flags: link.File.ErrorFlags = .{},
4740
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) = .{},
41ptr_width: PtrWidth,
42page_size: u32,
43
44objects: std.ArrayListUnmanaged(Object) = .{},
45
46sections: std.MultiArrayList(Section) = .{},
47data_directories: [N_DATA_DIRS]coff.ImageDataDirectory,
48
49text_section_index: ?u16 = null,
50got_section_index: ?u16 = null,
51rdata_section_index: ?u16 = null,
52data_section_index: ?u16 = null,
53
54locals: std.ArrayListUnmanaged(coff.Symbol) = .{},
55globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
56
57locals_free_list: std.ArrayListUnmanaged(u32) = .{},
58
59strtab: StringTable(.strtab) = .{},
60strtab_offset: ?u32 = null,
61
62got_entries: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{},
63got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
6664
6765/// Virtual address of the entry point procedure relative to image base.
6866entry_addr: ?u32 = null,
6967
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,
68/// Table of Decls that are currently alive.
69/// We store them here so that we can properly dispose of any allocated
70/// memory within the atom in the incremental linker.
71/// TODO consolidate this.
72decls: std.AutoHashMapUnmanaged(Module.Decl.Index, ?u16) = .{},
73
74/// List of atoms that are either synthetic or map directly to the Zig source program.
75managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
76
77/// Table of atoms indexed by the symbol index.
78atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
79
80/// Table of unnamed constants associated with a parent `Decl`.
81/// We store them here so that we can free the constants whenever the `Decl`
82/// needs updating or is freed.
83///
84/// For example,
85///
86/// ```zig
87/// const Foo = struct{
88/// a: u8,
89/// };
90///
91/// pub fn main() void {
92/// var foo = Foo{ .a = 1 };
93/// _ = foo;
94/// }
95/// ```
96///
97/// value assigned to label `foo` is an unnamed constant belonging/associated
98/// with `Decl` `main`, and lives as long as that `Decl`.
99unnamed_const_atoms: UnnamedConstTable = .{},
100
101/// A table of relocations indexed by the owning them `TextBlock`.
102/// Note that once we refactor `TextBlock`'s lifetime and ownership rules,
103/// this will be a table indexed by index into the list of Atoms.
104relocs: RelocTable = .{},
105
106pub const Reloc = struct {
107 @"type": enum {
108 got,
109 direct,
110 },
111 target: SymbolWithLoc,
112 offset: u32,
113 addend: u32,
114 pcrel: bool,
115 length: u2,
116 prev_vaddr: u32,
117};
74118
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,
119const RelocTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Reloc));
120const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom));
121
122const default_file_alignment: u16 = 0x200;
123const default_image_base_dll: u64 = 0x10000000;
124const default_image_base_exe: u64 = 0x400000;
125const default_size_of_stack_reserve: u32 = 0x1000000;
126const default_size_of_stack_commit: u32 = 0x1000;
127const default_size_of_heap_reserve: u32 = 0x100000;
128const default_size_of_heap_commit: u32 = 0x1000;
129
130const Section = struct {
131 header: coff.SectionHeader,
132
133 last_atom: ?*Atom = null,
134
135 /// A list of atoms that have surplus capacity. This list can have false
136 /// positives, as functions grow and shrink over time, only sometimes being added
137 /// or removed from the freelist.
138 ///
139 /// An atom has surplus capacity when its overcapacity value is greater than
140 /// padToIdeal(minimum_atom_size). That is, when it has so
141 /// much extra capacity, that we could fit a small new symbol in it, itself with
142 /// ideal_capacity or more.
143 ///
144 /// Ideal capacity is defined by size + (size / ideal_factor).
145 ///
146 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
147 /// overcapacity can be negative. A simple way to have negative overcapacity is to
148 /// allocate a fresh atom, which will have ideal capacity, and then grow it
149 /// by 1 byte. It will then have -1 overcapacity.
150 free_list: std.ArrayListUnmanaged(*Atom) = .{},
151};
80152
81153pub const PtrWidth = enum { p32, p64 };
154pub const SrcFn = void;
82155
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 };
102
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 }
156pub const Export = struct {
157 sym_index: ?u32 = null,
158};
111159
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 }
160pub const SymbolWithLoc = struct {
161 // Index into the respective symbol table.
162 sym_index: u32,
121163
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 }
164 // null means it's a synthetic global or Zig source.
165 file: ?u32 = null,
126166};
127167
128pub const SrcFn = void;
168/// When allocating, the ideal_capacity is calculated by
169/// actual_capacity + (actual_capacity / ideal_factor)
170const ideal_factor = 3;
171
172/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
173/// it as a possible place to put new symbols, it must have enough room for this many bytes
174/// (plus extra for reserved capacity).
175const minimum_text_block_size = 64;
176pub const min_text_capacity = padToIdeal(minimum_text_block_size);
129177
130178pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Coff {
131179 assert(options.target.ofmt == .coff);
......@@ -144,257 +192,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
144192 });
145193 self.base.file = file;
146194
147 // TODO Write object specific relocations, COFF symbol table, then enable object file output.
148 switch (options.output_mode) {
149 .Exe => {},
150 .Obj => return error.TODOImplementWritingObjFiles,
151 .Lib => return error.TODOImplementWritingLibFiles,
152 }
153
154 var coff_file_header_offset: u32 = 0;
155 if (options.output_mode == .Exe) {
156 // Write the MS-DOS stub and the PE signature
157 try self.base.file.?.pwriteAll(msdos_stub ++ "PE\x00\x00", 0);
158 coff_file_header_offset = msdos_stub.len + 4;
159 }
160
161 // COFF file header
162 const data_directory_count = 0;
163 var hdr_data: [112 + data_directory_count * 8 + section_table_size]u8 = undefined;
164 var index: usize = 0;
165
166 const machine = self.base.options.target.cpu.arch.toCoffMachine();
167 if (machine == .Unknown) {
168 return error.UnsupportedCOFFArchitecture;
169 }
170 mem.writeIntLittle(u16, hdr_data[0..2], @enumToInt(machine));
171 index += 2;
172
173 // Number of sections (we only use .got, .text)
174 mem.writeIntLittle(u16, hdr_data[index..][0..2], 2);
175 index += 2;
176 // TimeDateStamp (u32), PointerToSymbolTable (u32), NumberOfSymbols (u32)
177 mem.set(u8, hdr_data[index..][0..12], 0);
178 index += 12;
179
180 const optional_header_size = switch (options.output_mode) {
181 .Exe => data_directory_count * 8 + switch (self.ptr_width) {
182 .p32 => @as(u16, 96),
183 .p64 => 112,
184 },
185 else => 0,
186 };
187
188 const section_table_offset = coff_file_header_offset + 20 + optional_header_size;
189 const default_offset_table_size = file_alignment;
190 const default_size_of_code = 0;
191
192 self.section_data_offset = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment);
193 const section_data_relative_virtual_address = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment);
194 self.offset_table_virtual_address = default_image_base + section_data_relative_virtual_address;
195 self.offset_table_size = default_offset_table_size;
196 self.section_table_offset = section_table_offset;
197 self.text_section_virtual_address = default_image_base + section_data_relative_virtual_address + section_alignment;
198 self.text_section_size = default_size_of_code;
199
200 // Size of file when loaded in memory
201 const size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - default_image_base + default_size_of_code, section_alignment);
202
203 mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size);
204 index += 2;
205
206 // Characteristics
207 var characteristics: std.coff.CoffHeaderFlags = .{
208 .DEBUG_STRIPPED = 1, // TODO remove debug info stripped flag when necessary
209 .RELOCS_STRIPPED = 1,
210 };
211 if (options.output_mode == .Exe) {
212 characteristics.EXECUTABLE_IMAGE = 1;
213 }
214 switch (self.ptr_width) {
215 .p32 => characteristics.@"32BIT_MACHINE" = 1,
216 .p64 => characteristics.LARGE_ADDRESS_AWARE = 1,
217 }
218 mem.writeIntLittle(u16, hdr_data[index..][0..2], @bitCast(u16, characteristics));
219 index += 2;
220
221 assert(index == 20);
222 try self.base.file.?.pwriteAll(hdr_data[0..index], coff_file_header_offset);
223
224 if (options.output_mode == .Exe) {
225 self.optional_header_offset = coff_file_header_offset + 20;
226 // Optional header
227 index = 0;
228 mem.writeIntLittle(u16, hdr_data[0..2], switch (self.ptr_width) {
229 .p32 => @as(u16, 0x10b),
230 .p64 => 0x20b,
231 });
232 index += 2;
233
234 // Linker version (u8 + u8)
235 mem.set(u8, hdr_data[index..][0..2], 0);
236 index += 2;
237
238 // SizeOfCode (UNUSED, u32), SizeOfInitializedData (u32), SizeOfUninitializedData (u32), AddressOfEntryPoint (u32), BaseOfCode (UNUSED, u32)
239 mem.set(u8, hdr_data[index..][0..20], 0);
240 index += 20;
241
242 if (self.ptr_width == .p32) {
243 // Base of data relative to the image base (UNUSED)
244 mem.set(u8, hdr_data[index..][0..4], 0);
245 index += 4;
246
247 // Image base address
248 mem.writeIntLittle(u32, hdr_data[index..][0..4], default_image_base);
249 index += 4;
250 } else {
251 // Image base address
252 mem.writeIntLittle(u64, hdr_data[index..][0..8], default_image_base);
253 index += 8;
254 }
255
256 // Section alignment
257 mem.writeIntLittle(u32, hdr_data[index..][0..4], section_alignment);
258 index += 4;
259 // File alignment
260 mem.writeIntLittle(u32, hdr_data[index..][0..4], file_alignment);
261 index += 4;
262 // Required OS version, 6.0 is vista
263 mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
264 index += 2;
265 mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
266 index += 2;
267 // Image version
268 mem.set(u8, hdr_data[index..][0..4], 0);
269 index += 4;
270 // Required subsystem version, same as OS version
271 mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
272 index += 2;
273 mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
274 index += 2;
275 // Reserved zeroes (u32)
276 mem.set(u8, hdr_data[index..][0..4], 0);
277 index += 4;
278 mem.writeIntLittle(u32, hdr_data[index..][0..4], size_of_image);
279 index += 4;
280 mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
281 index += 4;
282 // CheckSum (u32)
283 mem.set(u8, hdr_data[index..][0..4], 0);
284 index += 4;
285 // Subsystem, TODO: Let users specify the subsystem, always CUI for now
286 mem.writeIntLittle(u16, hdr_data[index..][0..2], 3);
287 index += 2;
288 // DLL characteristics
289 mem.writeIntLittle(u16, hdr_data[index..][0..2], 0x0);
290 index += 2;
291
292 switch (self.ptr_width) {
293 .p32 => {
294 // Size of stack reserve + commit
295 mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000_000);
296 index += 4;
297 mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
298 index += 4;
299 // Size of heap reserve + commit
300 mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x100_000);
301 index += 4;
302 mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
303 index += 4;
304 },
305 .p64 => {
306 // Size of stack reserve + commit
307 mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000_000);
308 index += 8;
309 mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
310 index += 8;
311 // Size of heap reserve + commit
312 mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x100_000);
313 index += 8;
314 mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
315 index += 8;
316 },
317 }
318
319 // Reserved zeroes
320 mem.set(u8, hdr_data[index..][0..4], 0);
321 index += 4;
322
323 // Number of data directories
324 mem.writeIntLittle(u32, hdr_data[index..][0..4], data_directory_count);
325 index += 4;
326 // Initialize data directories to zero
327 mem.set(u8, hdr_data[index..][0 .. data_directory_count * 8], 0);
328 index += data_directory_count * 8;
329
330 assert(index == optional_header_size);
331 }
332
333 // Write section table.
334 // First, the .got section
335 hdr_data[index..][0..8].* = ".got\x00\x00\x00\x00".*;
336 index += 8;
337 if (options.output_mode == .Exe) {
338 // Virtual size (u32)
339 mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
340 index += 4;
341 // Virtual address (u32)
342 mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - default_image_base);
343 index += 4;
344 } else {
345 mem.set(u8, hdr_data[index..][0..8], 0);
346 index += 8;
347 }
348 // Size of raw data (u32)
349 mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
350 index += 4;
351 // File pointer to the start of the section
352 mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
353 index += 4;
354 // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16)
355 mem.set(u8, hdr_data[index..][0..12], 0);
356 index += 12;
357 // Section flags
358 mem.writeIntLittle(u32, hdr_data[index..][0..4], @bitCast(u32, std.coff.SectionHeaderFlags{
359 .CNT_INITIALIZED_DATA = 1,
360 .MEM_READ = 1,
361 }));
362 index += 4;
363 // Then, the .text section
364 hdr_data[index..][0..8].* = ".text\x00\x00\x00".*;
365 index += 8;
366 if (options.output_mode == .Exe) {
367 // Virtual size (u32)
368 mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
369 index += 4;
370 // Virtual address (u32)
371 mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - default_image_base);
372 index += 4;
373 } else {
374 mem.set(u8, hdr_data[index..][0..8], 0);
375 index += 8;
376 }
377 // Size of raw data (u32)
378 mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
379 index += 4;
380 // File pointer to the start of the section
381 mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset + default_offset_table_size);
382 index += 4;
383 // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16)
384 mem.set(u8, hdr_data[index..][0..12], 0);
385 index += 12;
386 // Section flags
387 mem.writeIntLittle(u32, hdr_data[index..][0..4], @bitCast(u32, std.coff.SectionHeaderFlags{
388 .CNT_CODE = 1,
389 .MEM_EXECUTE = 1,
390 .MEM_READ = 1,
391 .MEM_WRITE = 1,
392 }));
393 index += 4;
394
395 assert(index == optional_header_size + section_table_size);
396 try self.base.file.?.pwriteAll(hdr_data[0..index], self.optional_header_offset);
397 try self.base.file.?.setEndPos(self.section_data_offset + default_offset_table_size + default_size_of_code);
195 try self.populateMissingMetadata();
398196
399197 return self;
400198}
......@@ -405,6 +203,9 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {
405203 33...64 => .p64,
406204 else => return error.UnsupportedCOFFArchitecture,
407205 };
206 const page_size: u32 = switch (options.target.cpu.arch) {
207 else => 0x1000,
208 };
408209 const self = try gpa.create(Coff);
409210 errdefer gpa.destroy(self);
410211 self.* = .{
......@@ -415,6 +216,8 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {
415216 .file = null,
416217 },
417218 .ptr_width = ptr_width,
219 .page_size = page_size,
220 .data_directories = comptime mem.zeroes([N_DATA_DIRS]coff.ImageDataDirectory),
418221 };
419222
420223 const use_llvm = build_options.have_llvm and options.use_llvm;
......@@ -425,245 +228,530 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {
425228 return self;
426229}
427230
428pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void {
429 if (self.llvm_object) |_| return;
231pub fn deinit(self: *Coff) void {
232 const gpa = self.base.allocator;
233
234 if (build_options.have_llvm) {
235 if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa);
236 }
430237
431 try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1);
238 for (self.objects.items) |*object| {
239 object.deinit(gpa);
240 }
241 self.objects.deinit(gpa);
432242
433 const decl = self.base.options.module.?.declPtr(decl_index);
434 if (self.offset_table_free_list.popOrNull()) |i| {
435 decl.link.coff.offset_table_index = i;
436 } else {
437 decl.link.coff.offset_table_index = @intCast(u32, self.offset_table.items.len);
438 _ = self.offset_table.addOneAssumeCapacity();
243 for (self.sections.items(.free_list)) |*free_list| {
244 free_list.deinit(gpa);
245 }
246 self.sections.deinit(gpa);
247
248 for (self.managed_atoms.items) |atom| {
249 gpa.destroy(atom);
250 }
251 self.managed_atoms.deinit(gpa);
439252
440 const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8;
441 if (self.offset_table.items.len > self.offset_table_size / entry_size) {
442 self.offset_table_size_dirty = true;
253 self.locals.deinit(gpa);
254 self.globals.deinit(gpa);
255 self.locals_free_list.deinit(gpa);
256 self.strtab.deinit(gpa);
257 self.got_entries.deinit(gpa);
258 self.got_entries_free_list.deinit(gpa);
259 self.decls.deinit(gpa);
260 self.atom_by_index_table.deinit(gpa);
261
262 {
263 var it = self.unnamed_const_atoms.valueIterator();
264 while (it.next()) |atoms| {
265 atoms.deinit(gpa);
443266 }
267 self.unnamed_const_atoms.deinit(gpa);
444268 }
445269
446 self.offset_table.items[decl.link.coff.offset_table_index] = 0;
270 {
271 var it = self.relocs.valueIterator();
272 while (it.next()) |relocs| {
273 relocs.deinit(gpa);
274 }
275 self.relocs.deinit(gpa);
276 }
447277}
448278
449fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
450 const new_block_min_capacity = new_block_size * allocation_padding;
279fn populateMissingMetadata(self: *Coff) !void {
280 assert(self.llvm_object == null);
281 const gpa = self.base.allocator;
282
283 if (self.text_section_index == null) {
284 self.text_section_index = @intCast(u16, self.sections.slice().len);
285 const file_size = @intCast(u32, self.base.options.program_code_size_hint);
286 const off = self.findFreeSpace(file_size, self.page_size); // TODO we are over-aligning in file; we should track both in file and in memory pointers
287 log.debug("found .text free space 0x{x} to 0x{x}", .{ off, off + file_size });
288 var header = coff.SectionHeader{
289 .name = undefined,
290 .virtual_size = file_size,
291 .virtual_address = off,
292 .size_of_raw_data = file_size,
293 .pointer_to_raw_data = off,
294 .pointer_to_relocations = 0,
295 .pointer_to_linenumbers = 0,
296 .number_of_relocations = 0,
297 .number_of_linenumbers = 0,
298 .flags = .{
299 .CNT_CODE = 1,
300 .MEM_EXECUTE = 1,
301 .MEM_READ = 1,
302 },
303 };
304 try self.setSectionName(&header, ".text");
305 try self.sections.append(gpa, .{ .header = header });
306 }
307
308 if (self.got_section_index == null) {
309 self.got_section_index = @intCast(u16, self.sections.slice().len);
310 const file_size = @intCast(u32, self.base.options.symbol_count_hint);
311 const off = self.findFreeSpace(file_size, self.page_size);
312 log.debug("found .got free space 0x{x} to 0x{x}", .{ off, off + file_size });
313 var header = coff.SectionHeader{
314 .name = undefined,
315 .virtual_size = file_size,
316 .virtual_address = off,
317 .size_of_raw_data = file_size,
318 .pointer_to_raw_data = off,
319 .pointer_to_relocations = 0,
320 .pointer_to_linenumbers = 0,
321 .number_of_relocations = 0,
322 .number_of_linenumbers = 0,
323 .flags = .{
324 .CNT_INITIALIZED_DATA = 1,
325 .MEM_READ = 1,
326 },
327 };
328 try self.setSectionName(&header, ".got");
329 try self.sections.append(gpa, .{ .header = header });
330 }
331
332 if (self.rdata_section_index == null) {
333 self.rdata_section_index = @intCast(u16, self.sections.slice().len);
334 const file_size: u32 = 1024;
335 const off = self.findFreeSpace(file_size, self.page_size);
336 log.debug("found .rdata free space 0x{x} to 0x{x}", .{ off, off + file_size });
337 var header = coff.SectionHeader{
338 .name = undefined,
339 .virtual_size = file_size,
340 .virtual_address = off,
341 .size_of_raw_data = file_size,
342 .pointer_to_raw_data = off,
343 .pointer_to_relocations = 0,
344 .pointer_to_linenumbers = 0,
345 .number_of_relocations = 0,
346 .number_of_linenumbers = 0,
347 .flags = .{
348 .CNT_INITIALIZED_DATA = 1,
349 .MEM_READ = 1,
350 },
351 };
352 try self.setSectionName(&header, ".rdata");
353 try self.sections.append(gpa, .{ .header = header });
354 }
355
356 if (self.data_section_index == null) {
357 self.data_section_index = @intCast(u16, self.sections.slice().len);
358 const file_size: u32 = 1024;
359 const off = self.findFreeSpace(file_size, self.page_size);
360 log.debug("found .data free space 0x{x} to 0x{x}", .{ off, off + file_size });
361 var header = coff.SectionHeader{
362 .name = undefined,
363 .virtual_size = file_size,
364 .virtual_address = off,
365 .size_of_raw_data = file_size,
366 .pointer_to_raw_data = off,
367 .pointer_to_relocations = 0,
368 .pointer_to_linenumbers = 0,
369 .number_of_relocations = 0,
370 .number_of_linenumbers = 0,
371 .flags = .{
372 .CNT_INITIALIZED_DATA = 1,
373 .MEM_READ = 1,
374 .MEM_WRITE = 1,
375 },
376 };
377 try self.setSectionName(&header, ".data");
378 try self.sections.append(gpa, .{ .header = header });
379 }
451380
452 // We use these to indicate our intention to update metadata, placing the new block,
381 if (self.strtab_offset == null) {
382 try self.strtab.buffer.append(gpa, 0);
383 self.strtab_offset = self.findFreeSpace(@intCast(u32, self.strtab.len()), 1);
384 log.debug("found strtab free space 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + self.strtab.len() });
385 }
386
387 // Index 0 is always a null symbol.
388 try self.locals.append(gpa, .{
389 .name = [_]u8{0} ** 8,
390 .value = 0,
391 .section_number = @intToEnum(coff.SectionNumber, 0),
392 .@"type" = .{ .base_type = .NULL, .complex_type = .NULL },
393 .storage_class = .NULL,
394 .number_of_aux_symbols = 0,
395 });
396
397 {
398 // We need to find out what the max file offset is according to section headers.
399 // Otherwise, we may end up with an COFF binary with file size not matching the final section's
400 // offset + it's filesize.
401 // TODO I don't like this here one bit
402 var max_file_offset: u64 = 0;
403 for (self.sections.items(.header)) |header| {
404 if (header.pointer_to_raw_data + header.size_of_raw_data > max_file_offset) {
405 max_file_offset = header.pointer_to_raw_data + header.size_of_raw_data;
406 }
407 }
408 try self.base.file.?.pwriteAll(&[_]u8{0}, max_file_offset);
409 }
410}
411
412pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void {
413 if (self.llvm_object) |_| return;
414 const decl = self.base.options.module.?.declPtr(decl_index);
415 if (decl.link.coff.sym_index != 0) return;
416 decl.link.coff.sym_index = try self.allocateSymbol();
417 const gpa = self.base.allocator;
418 try self.atom_by_index_table.putNoClobber(gpa, decl.link.coff.sym_index, &decl.link.coff);
419 try self.decls.putNoClobber(gpa, decl_index, null);
420}
421
422fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 {
423 const tracy = trace(@src());
424 defer tracy.end();
425
426 const sect_id = @enumToInt(atom.getSymbol(self).section_number) - 1;
427 const header = &self.sections.items(.header)[sect_id];
428 const free_list = &self.sections.items(.free_list)[sect_id];
429 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
430 const new_atom_ideal_capacity = if (header.isCode()) padToIdeal(new_atom_size) else new_atom_size;
431
432 // We use these to indicate our intention to update metadata, placing the new atom,
453433 // and possibly removing a free list node.
454434 // It would be simpler to do it inside the for loop below, but that would cause a
455435 // problem if an error was returned later in the function. So this action
456436 // is actually carried out at the end of the function, when errors are no longer possible.
457 var block_placement: ?*TextBlock = null;
437 var atom_placement: ?*Atom = null;
458438 var free_list_removal: ?usize = null;
459439
460 const vaddr = blk: {
440 // First we look for an appropriately sized free list node.
441 // The list is unordered. We'll just take the first thing that works.
442 var vaddr = blk: {
461443 var i: usize = 0;
462 while (i < self.text_block_free_list.items.len) {
463 const free_block = self.text_block_free_list.items[i];
464
465 const next_block_text_offset = free_block.text_offset + free_block.capacity();
466 const new_block_text_offset = mem.alignForwardGeneric(u64, free_block.getVAddr(self.*) + free_block.size, alignment) - self.text_section_virtual_address;
467 if (new_block_text_offset < next_block_text_offset and next_block_text_offset - new_block_text_offset >= new_block_min_capacity) {
468 block_placement = free_block;
469
470 const remaining_capacity = next_block_text_offset - new_block_text_offset - new_block_min_capacity;
471 if (remaining_capacity < minimum_text_block_size) {
472 free_list_removal = i;
473 }
474
475 break :blk new_block_text_offset + self.text_section_virtual_address;
476 } else {
477 if (!free_block.freeListEligible()) {
478 _ = self.text_block_free_list.swapRemove(i);
444 while (i < free_list.items.len) {
445 const big_atom = free_list.items[i];
446 // We now have a pointer to a live atom that has too much capacity.
447 // Is it enough that we could fit this new atom?
448 const sym = big_atom.getSymbol(self);
449 const capacity = big_atom.capacity(self);
450 const ideal_capacity = if (header.isCode()) padToIdeal(capacity) else capacity;
451 const ideal_capacity_end_vaddr = math.add(u32, sym.value, ideal_capacity) catch ideal_capacity;
452 const capacity_end_vaddr = sym.value + capacity;
453 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;
454 const new_start_vaddr = mem.alignBackwardGeneric(u32, new_start_vaddr_unaligned, alignment);
455 if (new_start_vaddr < ideal_capacity_end_vaddr) {
456 // Additional bookkeeping here to notice if this free list node
457 // should be deleted because the atom that it points to has grown to take up
458 // more of the extra capacity.
459 if (!big_atom.freeListEligible(self)) {
460 _ = free_list.swapRemove(i);
479461 } else {
480462 i += 1;
481463 }
482464 continue;
483465 }
484 } else if (self.last_text_block) |last| {
485 const new_block_vaddr = mem.alignForwardGeneric(u64, last.getVAddr(self.*) + last.size, alignment);
486 block_placement = last;
487 break :blk new_block_vaddr;
466 // At this point we know that we will place the new atom here. But the
467 // remaining question is whether there is still yet enough capacity left
468 // over for there to still be a free list node.
469 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
470 const keep_free_list_node = remaining_capacity >= min_text_capacity;
471
472 // Set up the metadata to be updated, after errors are no longer possible.
473 atom_placement = big_atom;
474 if (!keep_free_list_node) {
475 free_list_removal = i;
476 }
477 break :blk new_start_vaddr;
478 } else if (maybe_last_atom.*) |last| {
479 const last_symbol = last.getSymbol(self);
480 const ideal_capacity = if (header.isCode()) padToIdeal(last.size) else last.size;
481 const ideal_capacity_end_vaddr = last_symbol.value + ideal_capacity;
482 const new_start_vaddr = mem.alignForwardGeneric(u32, ideal_capacity_end_vaddr, alignment);
483 atom_placement = last;
484 break :blk new_start_vaddr;
488485 } else {
489 break :blk self.text_section_virtual_address;
486 break :blk mem.alignForwardGeneric(u32, header.virtual_address, alignment);
490487 }
491488 };
492489
493 const expand_text_section = block_placement == null or block_placement.?.next == null;
494 if (expand_text_section) {
495 const needed_size = @intCast(u32, mem.alignForwardGeneric(u64, vaddr + new_block_size - self.text_section_virtual_address, file_alignment));
496 if (needed_size > self.text_section_size) {
497 const current_text_section_virtual_size = mem.alignForwardGeneric(u32, self.text_section_size, section_alignment);
498 const new_text_section_virtual_size = mem.alignForwardGeneric(u32, needed_size, section_alignment);
499 if (current_text_section_virtual_size != new_text_section_virtual_size) {
500 self.size_of_image_dirty = true;
501 // Write new virtual size
502 var buf: [4]u8 = undefined;
503 mem.writeIntLittle(u32, &buf, new_text_section_virtual_size);
504 try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 8);
505 }
506
507 self.text_section_size = needed_size;
508 self.text_section_size_dirty = true;
490 const expand_section = atom_placement == null or atom_placement.?.next == null;
491 if (expand_section) {
492 const sect_capacity = self.allocatedSize(header.pointer_to_raw_data);
493 const needed_size: u32 = (vaddr + new_atom_size) - header.virtual_address;
494 if (needed_size > sect_capacity) {
495 @panic("TODO move section");
509496 }
510 self.last_text_block = text_block;
497 maybe_last_atom.* = atom;
498 // header.virtual_size = needed_size;
499 // header.size_of_raw_data = mem.alignForwardGeneric(u32, needed_size, default_file_alignment);
511500 }
512 text_block.text_offset = @intCast(u32, vaddr - self.text_section_virtual_address);
513 text_block.size = @intCast(u32, new_block_size);
514501
515 // This function can also reallocate a text block.
516 // In this case we need to "unplug" it from its previous location before
517 // plugging it in to its new location.
518 if (text_block.prev) |prev| {
519 prev.next = text_block.next;
502 // if (header.getAlignment().? < alignment) {
503 // header.setAlignment(alignment);
504 // }
505 atom.size = new_atom_size;
506 atom.alignment = alignment;
507
508 if (atom.prev) |prev| {
509 prev.next = atom.next;
520510 }
521 if (text_block.next) |next| {
522 next.prev = text_block.prev;
511 if (atom.next) |next| {
512 next.prev = atom.prev;
523513 }
524514
525 if (block_placement) |big_block| {
526 text_block.prev = big_block;
527 text_block.next = big_block.next;
528 big_block.next = text_block;
515 if (atom_placement) |big_atom| {
516 atom.prev = big_atom;
517 atom.next = big_atom.next;
518 big_atom.next = atom;
529519 } else {
530 text_block.prev = null;
531 text_block.next = null;
520 atom.prev = null;
521 atom.next = null;
532522 }
533523 if (free_list_removal) |i| {
534 _ = self.text_block_free_list.swapRemove(i);
524 _ = free_list.swapRemove(i);
535525 }
526
536527 return vaddr;
537528}
538529
539fn growTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
540 const block_vaddr = text_block.getVAddr(self.*);
541 const align_ok = mem.alignBackwardGeneric(u64, block_vaddr, alignment) == block_vaddr;
542 const need_realloc = !align_ok or new_block_size > text_block.capacity();
543 if (!need_realloc) return @as(u64, block_vaddr);
544 return self.allocateTextBlock(text_block, new_block_size, alignment);
530fn allocateSymbol(self: *Coff) !u32 {
531 const gpa = self.base.allocator;
532 try self.locals.ensureUnusedCapacity(gpa, 1);
533
534 const index = blk: {
535 if (self.locals_free_list.popOrNull()) |index| {
536 log.debug(" (reusing symbol index {d})", .{index});
537 break :blk index;
538 } else {
539 log.debug(" (allocating symbol index {d})", .{self.locals.items.len});
540 const index = @intCast(u32, self.locals.items.len);
541 _ = self.locals.addOneAssumeCapacity();
542 break :blk index;
543 }
544 };
545
546 self.locals.items[index] = .{
547 .name = [_]u8{0} ** 8,
548 .value = 0,
549 .section_number = @intToEnum(coff.SectionNumber, 0),
550 .@"type" = .{ .base_type = .NULL, .complex_type = .NULL },
551 .storage_class = .NULL,
552 .number_of_aux_symbols = 0,
553 };
554
555 return index;
545556}
546557
547fn shrinkTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64) void {
548 text_block.size = @intCast(u32, new_block_size);
549 if (text_block.capacity() - text_block.size >= minimum_text_block_size) {
550 self.text_block_free_list.append(self.base.allocator, text_block) catch {};
558pub fn allocateGotEntry(self: *Coff, target: SymbolWithLoc) !u32 {
559 const gpa = self.base.allocator;
560 try self.got_entries.ensureUnusedCapacity(gpa, 1);
561 const index: u32 = blk: {
562 if (self.got_entries_free_list.popOrNull()) |index| {
563 log.debug(" (reusing GOT entry index {d})", .{index});
564 if (self.got_entries.getIndex(target)) |existing| {
565 assert(existing == index);
566 }
567 break :blk index;
568 } else {
569 log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.keys().len});
570 const index = @intCast(u32, self.got_entries.keys().len);
571 self.got_entries.putAssumeCapacityNoClobber(target, 0);
572 break :blk index;
573 }
574 };
575 self.got_entries.keys()[index] = target;
576 return index;
577}
578
579fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {
580 const gpa = self.base.allocator;
581 const atom = try gpa.create(Atom);
582 errdefer gpa.destroy(atom);
583 atom.* = Atom.empty;
584 atom.sym_index = try self.allocateSymbol();
585 atom.size = @sizeOf(u64);
586 atom.alignment = @alignOf(u64);
587
588 try self.managed_atoms.append(gpa, atom);
589 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
590 self.got_entries.getPtr(target).?.* = atom.sym_index;
591
592 const sym = atom.getSymbolPtr(self);
593 sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1);
594 sym.value = try self.allocateAtom(atom, atom.size, atom.alignment);
595
596 log.debug("allocated GOT atom at 0x{x}", .{sym.value});
597
598 try atom.addRelocation(self, .{
599 .@"type" = .direct,
600 .target = target,
601 .offset = 0,
602 .addend = 0,
603 .pcrel = false,
604 .length = 3,
605 .prev_vaddr = sym.value,
606 });
607
608 return atom;
609}
610
611fn growAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 {
612 const sym = atom.getSymbol(self);
613 const align_ok = mem.alignBackwardGeneric(u32, sym.value, alignment) == sym.value;
614 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);
615 if (!need_realloc) return sym.value;
616 return self.allocateAtom(atom, new_atom_size, alignment);
617}
618
619fn shrinkAtom(self: *Coff, atom: *Atom, new_block_size: u32) void {
620 _ = self;
621 _ = atom;
622 _ = new_block_size;
623 // TODO check the new capacity, and if it crosses the size threshold into a big enough
624 // capacity, insert a free list node for it.
625}
626
627fn writeAtom(self: *Coff, atom: *Atom, code: []const u8) !void {
628 const sym = atom.getSymbol(self);
629 const section = self.sections.get(@enumToInt(sym.section_number) - 1);
630 const file_offset = section.header.pointer_to_raw_data + sym.value - section.header.virtual_address;
631 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });
632 try self.base.file.?.pwriteAll(code, file_offset);
633 try self.resolveRelocs(atom);
634}
635
636fn writeGotAtom(self: *Coff, atom: *Atom) !void {
637 switch (self.ptr_width) {
638 .p32 => {
639 var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32);
640 try self.writeAtom(atom, &buffer);
641 },
642 .p64 => {
643 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
644 try self.writeAtom(atom, &buffer);
645 },
551646 }
552647}
553648
554fn freeTextBlock(self: *Coff, text_block: *TextBlock) void {
649fn resolveRelocs(self: *Coff, atom: *Atom) !void {
650 const relocs = self.relocs.get(atom) orelse return;
651 const source_sym = atom.getSymbol(self);
652 const source_section = self.sections.get(@enumToInt(source_sym.section_number) - 1).header;
653 const file_offset = source_section.pointer_to_raw_data + source_sym.value - source_section.virtual_address;
654
655 log.debug("relocating '{s}'", .{atom.getName(self)});
656
657 for (relocs.items) |*reloc| {
658 const target_vaddr = switch (reloc.@"type") {
659 .got => blk: {
660 const got_atom = self.getGotAtomForSymbol(reloc.target) orelse continue;
661 break :blk got_atom.getSymbol(self).value;
662 },
663 .direct => self.getSymbol(reloc.target).value,
664 };
665 const target_vaddr_with_addend = target_vaddr + reloc.addend;
666
667 if (target_vaddr_with_addend == reloc.prev_vaddr) continue;
668
669 log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{
670 reloc.offset,
671 target_vaddr_with_addend,
672 self.getSymbolName(reloc.target),
673 @tagName(reloc.@"type"),
674 });
675
676 if (reloc.pcrel) {
677 const source_vaddr = source_sym.value + reloc.offset;
678 const disp = target_vaddr_with_addend - source_vaddr - 4;
679 try self.base.file.?.pwriteAll(mem.asBytes(&@intCast(u32, disp)), file_offset + reloc.offset);
680 return;
681 }
682
683 switch (self.ptr_width) {
684 .p32 => try self.base.file.?.pwriteAll(
685 mem.asBytes(&@intCast(u32, target_vaddr_with_addend + default_image_base_exe)),
686 file_offset + reloc.offset,
687 ),
688 .p64 => switch (reloc.length) {
689 2 => try self.base.file.?.pwriteAll(
690 mem.asBytes(&@truncate(u32, target_vaddr_with_addend + default_image_base_exe)),
691 file_offset + reloc.offset,
692 ),
693 3 => try self.base.file.?.pwriteAll(
694 mem.asBytes(&(target_vaddr_with_addend + default_image_base_exe)),
695 file_offset + reloc.offset,
696 ),
697 else => unreachable,
698 },
699 }
700
701 reloc.prev_vaddr = target_vaddr_with_addend;
702 }
703}
704
705fn freeAtom(self: *Coff, atom: *Atom) void {
706 log.debug("freeAtom {*}", .{atom});
707
708 const sym = atom.getSymbol(self);
709 const sect_id = @enumToInt(sym.section_number) - 1;
710 const free_list = &self.sections.items(.free_list)[sect_id];
555711 var already_have_free_list_node = false;
556712 {
557713 var i: usize = 0;
558 // TODO turn text_block_free_list into a hash map
559 while (i < self.text_block_free_list.items.len) {
560 if (self.text_block_free_list.items[i] == text_block) {
561 _ = self.text_block_free_list.swapRemove(i);
714 // TODO turn free_list into a hash map
715 while (i < free_list.items.len) {
716 if (free_list.items[i] == atom) {
717 _ = free_list.swapRemove(i);
562718 continue;
563719 }
564 if (self.text_block_free_list.items[i] == text_block.prev) {
720 if (free_list.items[i] == atom.prev) {
565721 already_have_free_list_node = true;
566722 }
567723 i += 1;
568724 }
569725 }
570 if (self.last_text_block == text_block) {
571 self.last_text_block = text_block.prev;
726
727 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
728 if (maybe_last_atom.*) |last_atom| {
729 if (last_atom == atom) {
730 if (atom.prev) |prev| {
731 // TODO shrink the section size here
732 maybe_last_atom.* = prev;
733 } else {
734 maybe_last_atom.* = null;
735 }
736 }
572737 }
573 if (text_block.prev) |prev| {
574 prev.next = text_block.next;
575738
576 if (!already_have_free_list_node and prev.freeListEligible()) {
739 if (atom.prev) |prev| {
740 prev.next = atom.next;
741
742 if (!already_have_free_list_node and prev.freeListEligible(self)) {
577743 // The free list is heuristics, it doesn't have to be perfect, so we can
578744 // ignore the OOM here.
579 self.text_block_free_list.append(self.base.allocator, prev) catch {};
745 free_list.append(self.base.allocator, prev) catch {};
580746 }
747 } else {
748 atom.prev = null;
581749 }
582750
583 if (text_block.next) |next| {
584 next.prev = text_block.prev;
585 }
586}
587
588fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
589 const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8;
590 const endian = self.base.options.target.cpu.arch.endian();
591
592 const offset_table_start = self.section_data_offset;
593 if (self.offset_table_size_dirty) {
594 const current_raw_size = self.offset_table_size;
595 const new_raw_size = self.offset_table_size * 2;
596 log.debug("growing offset table from raw size {} to {}\n", .{ current_raw_size, new_raw_size });
597
598 // Move the text section to a new place in the executable
599 const current_text_section_start = self.section_data_offset + current_raw_size;
600 const new_text_section_start = self.section_data_offset + new_raw_size;
601
602 const amt = try self.base.file.?.copyRangeAll(current_text_section_start, self.base.file.?, new_text_section_start, self.text_section_size);
603 if (amt != self.text_section_size) return error.InputOutput;
604
605 // Write the new raw size in the .got header
606 var buf: [8]u8 = undefined;
607 mem.writeIntLittle(u32, buf[0..4], new_raw_size);
608 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 16);
609 // Write the new .text section file offset in the .text section header
610 mem.writeIntLittle(u32, buf[0..4], new_text_section_start);
611 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 20);
612
613 const current_virtual_size = mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment);
614 const new_virtual_size = mem.alignForwardGeneric(u32, new_raw_size, section_alignment);
615 // 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
616 // and the virtual size of the `.got` section
617
618 if (new_virtual_size != current_virtual_size) {
619 log.debug("growing offset table from virtual size {} to {}\n", .{ current_virtual_size, new_virtual_size });
620 self.size_of_image_dirty = true;
621 const va_offset = new_virtual_size - current_virtual_size;
622
623 // Write .got virtual size
624 mem.writeIntLittle(u32, buf[0..4], new_virtual_size);
625 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 8);
626
627 // Write .text new virtual address
628 self.text_section_virtual_address = self.text_section_virtual_address + va_offset;
629 mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - default_image_base);
630 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12);
631
632 // Fix the VAs in the offset table
633 for (self.offset_table.items) |*va, idx| {
634 if (va.* != 0) {
635 va.* += va_offset;
636
637 switch (entry_size) {
638 4 => {
639 mem.writeInt(u32, buf[0..4], @intCast(u32, va.*), endian);
640 try self.base.file.?.pwriteAll(buf[0..4], offset_table_start + idx * entry_size);
641 },
642 8 => {
643 mem.writeInt(u64, &buf, va.*, endian);
644 try self.base.file.?.pwriteAll(&buf, offset_table_start + idx * entry_size);
645 },
646 else => unreachable,
647 }
648 }
649 }
650 }
651 self.offset_table_size = new_raw_size;
652 self.offset_table_size_dirty = false;
653 }
654 // Write the new entry
655 switch (entry_size) {
656 4 => {
657 var buf: [4]u8 = undefined;
658 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
659 try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size);
660 },
661 8 => {
662 var buf: [8]u8 = undefined;
663 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
664 try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size);
665 },
666 else => unreachable,
751 if (atom.next) |next| {
752 next.prev = atom.prev;
753 } else {
754 atom.next = null;
667755 }
668756}
669757
......@@ -702,15 +790,18 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
702790 },
703791 };
704792
705 return self.finishUpdateDecl(module, func.owner_decl, code);
793 try self.updateDeclCode(decl_index, code, .FUNCTION);
794
795 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
796 const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};
797 return self.updateDeclExports(module, decl_index, decl_exports);
706798}
707799
708800pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 {
709801 _ = self;
710802 _ = tv;
711803 _ = decl_index;
712 log.debug("TODO lowerUnnamedConst for Coff", .{});
713 return error.AnalysisFail;
804 @panic("TODO lowerUnnamedConst");
714805}
715806
716807pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void {
......@@ -728,16 +819,20 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
728819 if (decl.val.tag() == .extern_fn) {
729820 return; // TODO Should we do more when front-end analyzed extern decl?
730821 }
731
732 // TODO COFF/PE debug information
733 // TODO Implement exports
822 if (decl.val.castTag(.variable)) |payload| {
823 const variable = payload.data;
824 if (variable.is_extern) {
825 return; // TODO Should we do more when front-end analyzed extern decl?
826 }
827 }
734828
735829 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
736830 defer code_buffer.deinit();
737831
832 const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
738833 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
739834 .ty = decl.ty,
740 .val = decl.val,
835 .val = decl_val,
741836 }, &code_buffer, .none, .{
742837 .parent_atom_index = 0,
743838 });
......@@ -751,47 +846,98 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
751846 },
752847 };
753848
754 return self.finishUpdateDecl(module, decl_index, code);
849 try self.updateDeclCode(decl_index, code, .NULL);
850
851 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
852 const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};
853 return self.updateDeclExports(module, decl_index, decl_exports);
755854}
756855
757fn finishUpdateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index, code: []const u8) !void {
758 const decl = module.declPtr(decl_index);
759 const required_alignment = decl.ty.abiAlignment(self.base.options.target);
760 const curr_size = decl.link.coff.size;
761 if (curr_size != 0) {
762 const capacity = decl.link.coff.capacity();
763 const need_realloc = code.len > capacity or
764 !mem.isAlignedGeneric(u32, decl.link.coff.text_offset, required_alignment);
856fn getDeclOutputSection(self: *Coff, decl: *Module.Decl) u16 {
857 const ty = decl.ty;
858 const zig_ty = ty.zigTypeTag();
859 const val = decl.val;
860 const index: u16 = blk: {
861 if (val.isUndefDeep()) {
862 // TODO in release-fast and release-small, we should put undef in .bss
863 break :blk self.data_section_index.?;
864 }
865
866 switch (zig_ty) {
867 .Fn => break :blk self.text_section_index.?,
868 else => {
869 if (val.castTag(.variable)) |_| {
870 break :blk self.data_section_index.?;
871 }
872 break :blk self.rdata_section_index.?;
873 },
874 }
875 };
876 return index;
877}
878
879fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8, complex_type: coff.ComplexType) !void {
880 const gpa = self.base.allocator;
881 const mod = self.base.options.module.?;
882 const decl = mod.declPtr(decl_index);
883
884 const decl_name = try decl.getFullyQualifiedName(mod);
885 defer gpa.free(decl_name);
886
887 log.debug("updateDeclCode {s}{*}", .{ decl_name, decl });
888 const required_alignment = decl.getAlignment(self.base.options.target);
889
890 const decl_ptr = self.decls.getPtr(decl_index).?;
891 if (decl_ptr.* == null) {
892 decl_ptr.* = self.getDeclOutputSection(decl);
893 }
894 const sect_index = decl_ptr.*.?;
895
896 const code_len = @intCast(u32, code.len);
897 const atom = &decl.link.coff;
898 assert(atom.sym_index != 0); // Caller forgot to allocateDeclIndexes()
899 if (atom.size != 0) {
900 const sym = atom.getSymbolPtr(self);
901 try self.setSymbolName(sym, decl_name);
902 sym.section_number = @intToEnum(coff.SectionNumber, sect_index + 1);
903 sym.@"type" = .{ .complex_type = complex_type, .base_type = .NULL };
904
905 const capacity = atom.capacity(self);
906 const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, sym.value, required_alignment);
765907 if (need_realloc) {
766 const curr_vaddr = self.text_section_virtual_address + decl.link.coff.text_offset;
767 const vaddr = try self.growTextBlock(&decl.link.coff, code.len, required_alignment);
768 log.debug("growing {s} from 0x{x} to 0x{x}\n", .{ decl.name, curr_vaddr, vaddr });
769 if (vaddr != curr_vaddr) {
770 log.debug(" (writing new offset table entry)\n", .{});
771 self.offset_table.items[decl.link.coff.offset_table_index] = vaddr;
772 try self.writeOffsetTableEntry(decl.link.coff.offset_table_index);
908 const vaddr = try self.growAtom(atom, code_len, required_alignment);
909 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, sym.value, vaddr });
910 log.debug(" (required alignment 0x{x}", .{required_alignment});
911
912 if (vaddr != sym.value) {
913 sym.value = vaddr;
914 log.debug(" (updating GOT entry)", .{});
915 const got_atom = self.getGotAtomForSymbol(.{ .sym_index = atom.sym_index, .file = null }).?;
916 try self.writeGotAtom(got_atom);
773917 }
774 } else if (code.len < curr_size) {
775 self.shrinkTextBlock(&decl.link.coff, code.len);
918 } else if (code_len < atom.size) {
919 self.shrinkAtom(atom, code_len);
776920 }
921 atom.size = code_len;
777922 } else {
778 const vaddr = try self.allocateTextBlock(&decl.link.coff, code.len, required_alignment);
779 log.debug("allocated text block for {s} at 0x{x} (size: {Bi})\n", .{
780 mem.sliceTo(decl.name, 0),
781 vaddr,
782 std.fmt.fmtIntSizeDec(code.len),
783 });
784 errdefer self.freeTextBlock(&decl.link.coff);
785 self.offset_table.items[decl.link.coff.offset_table_index] = vaddr;
786 try self.writeOffsetTableEntry(decl.link.coff.offset_table_index);
923 const sym = atom.getSymbolPtr(self);
924 try self.setSymbolName(sym, decl_name);
925 sym.section_number = @intToEnum(coff.SectionNumber, sect_index + 1);
926 sym.@"type" = .{ .complex_type = complex_type, .base_type = .NULL };
927
928 const vaddr = try self.allocateAtom(atom, code_len, required_alignment);
929 errdefer self.freeAtom(atom);
930 log.debug("allocated atom for {s} at 0x{x}", .{ decl_name, vaddr });
931 atom.size = code_len;
932 sym.value = vaddr;
933
934 const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null };
935 _ = try self.allocateGotEntry(got_target);
936 const got_atom = try self.createGotAtom(got_target);
937 try self.writeGotAtom(got_atom);
787938 }
788939
789 // Write the code into the file
790 try self.base.file.?.pwriteAll(code, self.section_data_offset + self.offset_table_size + decl.link.coff.text_offset);
791
792 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
793 const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};
794 return self.updateDeclExports(module, decl_index, decl_exports);
940 try self.writeAtom(atom, code);
795941}
796942
797943pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {
......@@ -802,9 +948,31 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {
802948 const mod = self.base.options.module.?;
803949 const decl = mod.declPtr(decl_index);
804950
951 log.debug("freeDecl {*}", .{decl});
952
953 const kv = self.decls.fetchRemove(decl_index);
954 if (kv.?.value) |_| {
955 self.freeAtom(&decl.link.coff);
956 }
957
805958 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
806 self.freeTextBlock(&decl.link.coff);
807 self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {};
959 const gpa = self.base.allocator;
960 const sym_index = decl.link.coff.sym_index;
961 if (sym_index != 0) {
962 self.locals_free_list.append(gpa, sym_index) catch {};
963
964 // Try freeing GOT atom if this decl had one
965 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
966 if (self.got_entries.getIndex(got_target)) |got_index| {
967 self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {};
968 self.got_entries.values()[got_index] = 0;
969 log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
970 }
971
972 self.locals.items[sym_index].section_number = @intToEnum(coff.SectionNumber, 0);
973 _ = self.atom_by_index_table.remove(sym_index);
974 decl.link.coff.sym_index = 0;
975 }
808976}
809977
810978pub fn updateDeclExports(
......@@ -817,64 +985,157 @@ pub fn updateDeclExports(
817985 @panic("Attempted to compile for object format that was disabled by build configuration");
818986 }
819987
820 // Even in the case of LLVM, we need to notice certain exported symbols in order to
821 // detect the default subsystem.
822 for (exports) |exp| {
823 const exported_decl = module.declPtr(exp.exported_decl);
824 if (exported_decl.getFunction() == null) continue;
825 const winapi_cc = switch (self.base.options.target.cpu.arch) {
826 .i386 => std.builtin.CallingConvention.Stdcall,
827 else => std.builtin.CallingConvention.C,
828 };
829 const decl_cc = exported_decl.ty.fnCallingConvention();
830 if (decl_cc == .C and mem.eql(u8, exp.options.name, "main") and
831 self.base.options.link_libc)
832 {
833 module.stage1_flags.have_c_main = true;
834 } else if (decl_cc == winapi_cc and self.base.options.target.os.tag == .windows) {
835 if (mem.eql(u8, exp.options.name, "WinMain")) {
836 module.stage1_flags.have_winmain = true;
837 } else if (mem.eql(u8, exp.options.name, "wWinMain")) {
838 module.stage1_flags.have_wwinmain = true;
839 } else if (mem.eql(u8, exp.options.name, "WinMainCRTStartup")) {
840 module.stage1_flags.have_winmain_crt_startup = true;
841 } else if (mem.eql(u8, exp.options.name, "wWinMainCRTStartup")) {
842 module.stage1_flags.have_wwinmain_crt_startup = true;
843 } else if (mem.eql(u8, exp.options.name, "DllMainCRTStartup")) {
844 module.stage1_flags.have_dllmain_crt_startup = true;
988 if (build_options.have_llvm) {
989 // Even in the case of LLVM, we need to notice certain exported symbols in order to
990 // detect the default subsystem.
991 for (exports) |exp| {
992 const exported_decl = module.declPtr(exp.exported_decl);
993 if (exported_decl.getFunction() == null) continue;
994 const winapi_cc = switch (self.base.options.target.cpu.arch) {
995 .i386 => std.builtin.CallingConvention.Stdcall,
996 else => std.builtin.CallingConvention.C,
997 };
998 const decl_cc = exported_decl.ty.fnCallingConvention();
999 if (decl_cc == .C and mem.eql(u8, exp.options.name, "main") and
1000 self.base.options.link_libc)
1001 {
1002 module.stage1_flags.have_c_main = true;
1003 } else if (decl_cc == winapi_cc and self.base.options.target.os.tag == .windows) {
1004 if (mem.eql(u8, exp.options.name, "WinMain")) {
1005 module.stage1_flags.have_winmain = true;
1006 } else if (mem.eql(u8, exp.options.name, "wWinMain")) {
1007 module.stage1_flags.have_wwinmain = true;
1008 } else if (mem.eql(u8, exp.options.name, "WinMainCRTStartup")) {
1009 module.stage1_flags.have_winmain_crt_startup = true;
1010 } else if (mem.eql(u8, exp.options.name, "wWinMainCRTStartup")) {
1011 module.stage1_flags.have_wwinmain_crt_startup = true;
1012 } else if (mem.eql(u8, exp.options.name, "DllMainCRTStartup")) {
1013 module.stage1_flags.have_dllmain_crt_startup = true;
1014 }
8451015 }
8461016 }
847 }
8481017
849 if (build_options.have_llvm) {
8501018 if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl_index, exports);
8511019 }
8521020
1021 const tracy = trace(@src());
1022 defer tracy.end();
1023
1024 const gpa = self.base.allocator;
1025
8531026 const decl = module.declPtr(decl_index);
1027 const atom = &decl.link.coff;
1028 if (atom.sym_index == 0) return;
1029 const decl_sym = atom.getSymbol(self);
1030
8541031 for (exports) |exp| {
1032 log.debug("adding new export '{s}'", .{exp.options.name});
1033
8551034 if (exp.options.section) |section_name| {
8561035 if (!mem.eql(u8, section_name, ".text")) {
857 try module.failed_exports.ensureUnusedCapacity(module.gpa, 1);
858 module.failed_exports.putAssumeCapacityNoClobber(
1036 try module.failed_exports.putNoClobber(
1037 module.gpa,
8591038 exp,
860 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}),
1039 try Module.ErrorMsg.create(
1040 gpa,
1041 decl.srcLoc(),
1042 "Unimplemented: ExportOptions.section",
1043 .{},
1044 ),
8611045 );
8621046 continue;
8631047 }
8641048 }
865 if (mem.eql(u8, exp.options.name, "_start")) {
866 self.entry_addr = decl.link.coff.getVAddr(self.*) - default_image_base;
867 } else {
868 try module.failed_exports.ensureUnusedCapacity(module.gpa, 1);
869 module.failed_exports.putAssumeCapacityNoClobber(
1049
1050 if (exp.options.linkage == .LinkOnce) {
1051 try module.failed_exports.putNoClobber(
1052 module.gpa,
8701053 exp,
871 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: Exports other than '_start'", .{}),
1054 try Module.ErrorMsg.create(
1055 gpa,
1056 decl.srcLoc(),
1057 "Unimplemented: GlobalLinkage.LinkOnce",
1058 .{},
1059 ),
8721060 );
8731061 continue;
8741062 }
1063
1064 const sym_index = exp.link.coff.sym_index orelse blk: {
1065 const sym_index = try self.allocateSymbol();
1066 exp.link.coff.sym_index = sym_index;
1067 break :blk sym_index;
1068 };
1069 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1070 const sym = self.getSymbolPtr(sym_loc);
1071 try self.setSymbolName(sym, exp.options.name);
1072 sym.value = decl_sym.value;
1073 sym.section_number = @intToEnum(coff.SectionNumber, self.text_section_index.? + 1);
1074 sym.@"type" = .{ .complex_type = .FUNCTION, .base_type = .NULL };
1075
1076 switch (exp.options.linkage) {
1077 .Strong => {
1078 sym.storage_class = .EXTERNAL;
1079 },
1080 .Internal => @panic("TODO Internal"),
1081 .Weak => @panic("TODO WeakExternal"),
1082 else => unreachable,
1083 }
1084
1085 try self.resolveGlobalSymbol(sym_loc);
1086 }
1087}
1088
1089pub fn deleteExport(self: *Coff, exp: Export) void {
1090 if (self.llvm_object) |_| return;
1091 const sym_index = exp.sym_index orelse return;
1092
1093 const gpa = self.base.allocator;
1094
1095 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1096 const sym = self.getSymbolPtr(sym_loc);
1097 const sym_name = self.getSymbolName(sym_loc);
1098 log.debug("deleting export '{s}'", .{sym_name});
1099 assert(sym.storage_class == .EXTERNAL);
1100 sym.* = .{
1101 .name = [_]u8{0} ** 8,
1102 .value = 0,
1103 .section_number = @intToEnum(coff.SectionNumber, 0),
1104 .@"type" = .{ .base_type = .NULL, .complex_type = .NULL },
1105 .storage_class = .NULL,
1106 .number_of_aux_symbols = 0,
1107 };
1108 self.locals_free_list.append(gpa, sym_index) catch {};
1109
1110 if (self.globals.get(sym_name)) |global| blk: {
1111 if (global.sym_index != sym_index) break :blk;
1112 if (global.file != null) break :blk;
1113 const kv = self.globals.fetchSwapRemove(sym_name);
1114 gpa.free(kv.?.key);
8751115 }
8761116}
8771117
1118fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void {
1119 const gpa = self.base.allocator;
1120 const sym = self.getSymbol(current);
1121 _ = sym;
1122 const sym_name = self.getSymbolName(current);
1123
1124 const name = try gpa.dupe(u8, sym_name);
1125 const global_index = @intCast(u32, self.globals.values().len);
1126 _ = global_index;
1127 const gop = try self.globals.getOrPut(gpa, name);
1128 defer if (gop.found_existing) gpa.free(name);
1129
1130 if (!gop.found_existing) {
1131 gop.value_ptr.* = current;
1132 // TODO undef + tentative
1133 return;
1134 }
1135
1136 log.debug("TODO finish resolveGlobalSymbols implementation", .{});
1137}
1138
8781139pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void {
8791140 if (self.base.options.emit == null) {
8801141 if (build_options.have_llvm) {
......@@ -884,14 +1145,13 @@ pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !vo
8841145 }
8851146 return;
8861147 }
887 if (build_options.have_llvm and self.base.options.use_lld) {
888 return self.linkWithLLD(comp, prog_node);
889 } else {
890 switch (self.base.options.effectiveOutputMode()) {
891 .Exe, .Obj => {},
892 .Lib => return error.TODOImplementWritingLibFiles,
893 }
894 return self.flushModule(comp, prog_node);
1148 const use_lld = build_options.have_llvm and self.base.options.use_lld;
1149 if (use_lld) {
1150 return lld.linkWithLLD(self, comp, prog_node);
1151 }
1152 switch (self.base.options.output_mode) {
1153 .Exe, .Obj => return self.flushModule(comp, prog_node),
1154 .Lib => return error.TODOImplementWritingLibFiles,
8951155 }
8961156}
8971157
......@@ -909,648 +1169,449 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
9091169 sub_prog_node.activate();
9101170 defer sub_prog_node.end();
9111171
912 if (self.text_section_size_dirty) {
913 // Write the new raw size in the .text header
914 var buf: [4]u8 = undefined;
915 mem.writeIntLittle(u32, &buf, self.text_section_size);
916 try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 16);
917 try self.base.file.?.setEndPos(self.section_data_offset + self.offset_table_size + self.text_section_size);
918 self.text_section_size_dirty = false;
1172 if (build_options.enable_logging) {
1173 self.logSymtab();
9191174 }
9201175
921 if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) {
922 const new_size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - default_image_base + self.text_section_size, section_alignment);
923 var buf: [4]u8 = undefined;
924 mem.writeIntLittle(u32, &buf, new_size_of_image);
925 try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56);
926 self.size_of_image_dirty = false;
1176 {
1177 var it = self.relocs.keyIterator();
1178 while (it.next()) |atom| {
1179 try self.resolveRelocs(atom.*);
1180 }
9271181 }
9281182
1183 if (self.getEntryPoint()) |entry_sym_loc| {
1184 self.entry_addr = self.getSymbol(entry_sym_loc).value;
1185 }
1186
1187 try self.writeStrtab();
1188 try self.writeDataDirectoriesHeaders();
1189 try self.writeSectionHeaders();
1190
9291191 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
9301192 log.debug("flushing. no_entry_point_found = true\n", .{});
9311193 self.error_flags.no_entry_point_found = true;
9321194 } else {
9331195 log.debug("flushing. no_entry_point_found = false\n", .{});
9341196 self.error_flags.no_entry_point_found = false;
935
936 if (self.base.options.output_mode == .Exe) {
937 // Write AddressOfEntryPoint
938 var buf: [4]u8 = undefined;
939 mem.writeIntLittle(u32, &buf, self.entry_addr.?);
940 try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 16);
941 }
1197 try self.writeHeader();
9421198 }
9431199}
9441200
945fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void {
946 const tracy = trace(@src());
947 defer tracy.end();
948
949 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
950 defer arena_allocator.deinit();
951 const arena = arena_allocator.allocator();
952
953 const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
954 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
955
956 // If there is no Zig code to compile, then we should skip flushing the output file because it
957 // will not be part of the linker line anyway.
958 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
959 const use_stage1 = build_options.have_stage1 and self.base.options.use_stage1;
960 if (use_stage1) {
961 const obj_basename = try std.zig.binNameAlloc(arena, .{
962 .root_name = self.base.options.root_name,
963 .target = self.base.options.target,
964 .output_mode = .Obj,
965 });
966 switch (self.base.options.cache_mode) {
967 .incremental => break :blk try module.zig_cache_artifact_directory.join(
968 arena,
969 &[_][]const u8{obj_basename},
970 ),
971 .whole => break :blk try fs.path.join(arena, &.{
972 fs.path.dirname(full_out_path).?, obj_basename,
973 }),
974 }
975 }
976
977 try self.flushModule(comp, prog_node);
1201pub fn getDeclVAddr(
1202 self: *Coff,
1203 decl_index: Module.Decl.Index,
1204 reloc_info: link.File.RelocInfo,
1205) !u64 {
1206 _ = self;
1207 _ = decl_index;
1208 _ = reloc_info;
1209 @panic("TODO getDeclVAddr");
1210}
9781211
979 if (fs.path.dirname(full_out_path)) |dirname| {
980 break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
981 } else {
982 break :blk self.base.intermediary_basename.?;
983 }
984 } else null;
1212pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {
1213 _ = self;
1214 _ = module;
1215 _ = decl;
1216 log.debug("TODO implement updateDeclLineNumber", .{});
1217}
9851218
986 var sub_prog_node = prog_node.start("LLD Link", 0);
987 sub_prog_node.activate();
988 sub_prog_node.context.refresh();
989 defer sub_prog_node.end();
1219fn writeStrtab(self: *Coff) !void {
1220 const allocated_size = self.allocatedSize(self.strtab_offset.?);
1221 const needed_size = @intCast(u32, self.strtab.len());
9901222
991 const is_lib = self.base.options.output_mode == .Lib;
992 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
993 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
994 const link_in_crt = self.base.options.link_libc and is_exe_or_dyn_lib;
995 const target = self.base.options.target;
1223 if (needed_size > allocated_size) {
1224 self.strtab_offset = null;
1225 self.strtab_offset = @intCast(u32, self.findFreeSpace(needed_size, 1));
1226 }
9961227
997 // See link/Elf.zig for comments on how this mechanism works.
998 const id_symlink_basename = "lld.id";
1228 log.debug("writing strtab from 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + needed_size });
1229 try self.base.file.?.pwriteAll(self.strtab.buffer.items, self.strtab_offset.?);
1230}
9991231
1000 var man: Cache.Manifest = undefined;
1001 defer if (!self.base.options.disable_lld_caching) man.deinit();
1232fn writeSectionHeaders(self: *Coff) !void {
1233 const offset = self.getSectionHeadersOffset();
1234 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items(.header)), offset);
1235}
10021236
1003 var digest: [Cache.hex_digest_len]u8 = undefined;
1237fn writeDataDirectoriesHeaders(self: *Coff) !void {
1238 const offset = self.getDataDirectoryHeadersOffset();
1239 try self.base.file.?.pwriteAll(mem.sliceAsBytes(&self.data_directories), offset);
1240}
10041241
1005 if (!self.base.options.disable_lld_caching) {
1006 man = comp.cache_parent.obtain();
1007 self.base.releaseLock();
1242fn writeHeader(self: *Coff) !void {
1243 const gpa = self.base.allocator;
1244 var buffer = std.ArrayList(u8).init(gpa);
1245 defer buffer.deinit();
1246 const writer = buffer.writer();
10081247
1009 comptime assert(Compilation.link_hash_implementation_version == 7);
1248 try buffer.ensureTotalCapacity(self.getSizeOfHeaders());
1249 writer.writeAll(msdos_stub) catch unreachable;
1250 mem.writeIntLittle(u32, buffer.items[0x3c..][0..4], msdos_stub.len);
10101251
1011 for (self.base.options.objects) |obj| {
1012 _ = try man.addFile(obj.path, null);
1013 man.hash.add(obj.must_link);
1014 }
1015 for (comp.c_object_table.keys()) |key| {
1016 _ = try man.addFile(key.status.success.object_path, null);
1017 }
1018 try man.addOptionalFile(module_obj_path);
1019 man.hash.addOptionalBytes(self.base.options.entry);
1020 man.hash.addOptional(self.base.options.stack_size_override);
1021 man.hash.addOptional(self.base.options.image_base_override);
1022 man.hash.addListOfBytes(self.base.options.lib_dirs);
1023 man.hash.add(self.base.options.skip_linker_dependencies);
1024 if (self.base.options.link_libc) {
1025 man.hash.add(self.base.options.libc_installation != null);
1026 if (self.base.options.libc_installation) |libc_installation| {
1027 man.hash.addBytes(libc_installation.crt_dir.?);
1028 if (target.abi == .msvc) {
1029 man.hash.addBytes(libc_installation.msvc_lib_dir.?);
1030 man.hash.addBytes(libc_installation.kernel32_lib_dir.?);
1031 }
1032 }
1033 }
1034 link.hashAddSystemLibs(&man.hash, self.base.options.system_libs);
1035 man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys());
1036 man.hash.addOptional(self.base.options.subsystem);
1037 man.hash.add(self.base.options.is_test);
1038 man.hash.add(self.base.options.tsaware);
1039 man.hash.add(self.base.options.nxcompat);
1040 man.hash.add(self.base.options.dynamicbase);
1041 // strip does not need to go into the linker hash because it is part of the hash namespace
1042 man.hash.addOptional(self.base.options.major_subsystem_version);
1043 man.hash.addOptional(self.base.options.minor_subsystem_version);
1044
1045 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
1046 _ = try man.hit();
1047 digest = man.final();
1048 var prev_digest_buf: [digest.len]u8 = undefined;
1049 const prev_digest: []u8 = Cache.readSmallFile(
1050 directory.handle,
1051 id_symlink_basename,
1052 &prev_digest_buf,
1053 ) catch |err| blk: {
1054 log.debug("COFF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
1055 // Handle this as a cache miss.
1056 break :blk prev_digest_buf[0..0];
1057 };
1058 if (mem.eql(u8, prev_digest, &digest)) {
1059 log.debug("COFF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
1060 // Hot diggity dog! The output binary is already there.
1061 self.base.lock = man.toOwnedLock();
1062 return;
1063 }
1064 log.debug("COFF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
1065
1066 // We are about to change the output file to be different, so we invalidate the build hash now.
1067 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
1068 error.FileNotFound => {},
1069 else => |e| return e,
1070 };
1252 writer.writeAll("PE\x00\x00") catch unreachable;
1253 var flags = coff.CoffHeaderFlags{
1254 .EXECUTABLE_IMAGE = 1,
1255 .DEBUG_STRIPPED = 1, // TODO
1256 };
1257 switch (self.ptr_width) {
1258 .p32 => flags.@"32BIT_MACHINE" = 1,
1259 .p64 => flags.LARGE_ADDRESS_AWARE = 1,
1260 }
1261 if (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Dynamic) {
1262 flags.DLL = 1;
10711263 }
10721264
1073 if (self.base.options.output_mode == .Obj) {
1074 // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy
1075 // here. TODO: think carefully about how we can avoid this redundant operation when doing
1076 // build-obj. See also the corresponding TODO in linkAsArchive.
1077 const the_object_path = blk: {
1078 if (self.base.options.objects.len != 0)
1079 break :blk self.base.options.objects[0].path;
1265 const timestamp = std.time.timestamp();
1266 const size_of_optional_header = @intCast(u16, self.getOptionalHeaderSize() + self.getDataDirectoryHeadersSize());
1267 var coff_header = coff.CoffHeader{
1268 .machine = coff.MachineType.fromTargetCpuArch(self.base.options.target.cpu.arch),
1269 .number_of_sections = @intCast(u16, self.sections.slice().len), // TODO what if we prune a section
1270 .time_date_stamp = @truncate(u32, @bitCast(u64, timestamp)),
1271 .pointer_to_symbol_table = self.strtab_offset orelse 0,
1272 .number_of_symbols = 0,
1273 .size_of_optional_header = size_of_optional_header,
1274 .flags = flags,
1275 };
10801276
1081 if (comp.c_object_table.count() != 0)
1082 break :blk comp.c_object_table.keys()[0].status.success.object_path;
1277 writer.writeAll(mem.asBytes(&coff_header)) catch unreachable;
10831278
1084 if (module_obj_path) |p|
1085 break :blk p;
1279 const dll_flags: coff.DllFlags = .{
1280 .HIGH_ENTROPY_VA = 0, //@boolToInt(self.base.options.pie),
1281 .DYNAMIC_BASE = 0,
1282 .TERMINAL_SERVER_AWARE = 1, // We are not a legacy app
1283 .NX_COMPAT = 1, // We are compatible with Data Execution Prevention
1284 };
1285 const subsystem: coff.Subsystem = .WINDOWS_CUI;
1286 const size_of_image: u32 = self.getSizeOfImage();
1287 const size_of_headers: u32 = mem.alignForwardGeneric(u32, self.getSizeOfHeaders(), default_file_alignment);
1288 const image_base = self.base.options.image_base_override orelse switch (self.base.options.output_mode) {
1289 .Exe => default_image_base_exe,
1290 .Lib => default_image_base_dll,
1291 else => unreachable,
1292 };
10861293
1087 // TODO I think this is unreachable. Audit this situation when solving the above TODO
1088 // regarding eliding redundant object -> object transformations.
1089 return error.NoObjectsToLink;
1090 };
1091 // This can happen when using --enable-cache and using the stage1 backend. In this case
1092 // we can skip the file copy.
1093 if (!mem.eql(u8, the_object_path, full_out_path)) {
1094 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
1095 }
1096 } else {
1097 // Create an LLD command line and invoke it.
1098 var argv = std.ArrayList([]const u8).init(self.base.allocator);
1099 defer argv.deinit();
1100 // We will invoke ourselves as a child process to gain access to LLD.
1101 // This is necessary because LLD does not behave properly as a library -
1102 // it calls exit() and does not reset all global data between invocations.
1103 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "lld-link" });
1104
1105 try argv.append("-ERRORLIMIT:0");
1106 try argv.append("-NOLOGO");
1107 if (!self.base.options.strip) {
1108 try argv.append("-DEBUG");
1109 }
1110 if (self.base.options.lto) {
1111 switch (self.base.options.optimize_mode) {
1112 .Debug => {},
1113 .ReleaseSmall => try argv.append("-OPT:lldlto=2"),
1114 .ReleaseFast, .ReleaseSafe => try argv.append("-OPT:lldlto=3"),
1115 }
1294 const base_of_code = self.sections.get(self.text_section_index.?).header.virtual_address;
1295 const base_of_data = self.sections.get(self.data_section_index.?).header.virtual_address;
1296
1297 var size_of_code: u32 = 0;
1298 var size_of_initialized_data: u32 = 0;
1299 var size_of_uninitialized_data: u32 = 0;
1300 for (self.sections.items(.header)) |header| {
1301 if (header.flags.CNT_CODE == 1) {
1302 size_of_code += header.size_of_raw_data;
11161303 }
1117 if (self.base.options.output_mode == .Exe) {
1118 const stack_size = self.base.options.stack_size_override orelse 16777216;
1119 try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size}));
1304 if (header.flags.CNT_INITIALIZED_DATA == 1) {
1305 size_of_initialized_data += header.size_of_raw_data;
11201306 }
1121 if (self.base.options.image_base_override) |image_base| {
1122 try argv.append(try std.fmt.allocPrint(arena, "-BASE:{d}", .{image_base}));
1307 if (header.flags.CNT_UNINITIALIZED_DATA == 1) {
1308 size_of_uninitialized_data += header.size_of_raw_data;
11231309 }
1310 }
11241311
1125 if (target.cpu.arch == .i386) {
1126 try argv.append("-MACHINE:X86");
1127 } else if (target.cpu.arch == .x86_64) {
1128 try argv.append("-MACHINE:X64");
1129 } else if (target.cpu.arch.isARM()) {
1130 if (target.cpu.arch.ptrBitWidth() == 32) {
1131 try argv.append("-MACHINE:ARM");
1132 } else {
1133 try argv.append("-MACHINE:ARM64");
1134 }
1135 }
1312 switch (self.ptr_width) {
1313 .p32 => {
1314 var opt_header = coff.OptionalHeaderPE32{
1315 .magic = coff.IMAGE_NT_OPTIONAL_HDR32_MAGIC,
1316 .major_linker_version = 0,
1317 .minor_linker_version = 0,
1318 .size_of_code = size_of_code,
1319 .size_of_initialized_data = size_of_initialized_data,
1320 .size_of_uninitialized_data = size_of_uninitialized_data,
1321 .address_of_entry_point = self.entry_addr orelse 0,
1322 .base_of_code = base_of_code,
1323 .base_of_data = base_of_data,
1324 .image_base = @intCast(u32, image_base),
1325 .section_alignment = self.page_size,
1326 .file_alignment = default_file_alignment,
1327 .major_operating_system_version = 6,
1328 .minor_operating_system_version = 0,
1329 .major_image_version = 0,
1330 .minor_image_version = 0,
1331 .major_subsystem_version = 6,
1332 .minor_subsystem_version = 0,
1333 .win32_version_value = 0,
1334 .size_of_image = size_of_image,
1335 .size_of_headers = size_of_headers,
1336 .checksum = 0,
1337 .subsystem = subsystem,
1338 .dll_flags = dll_flags,
1339 .size_of_stack_reserve = default_size_of_stack_reserve,
1340 .size_of_stack_commit = default_size_of_stack_commit,
1341 .size_of_heap_reserve = default_size_of_heap_reserve,
1342 .size_of_heap_commit = default_size_of_heap_commit,
1343 .loader_flags = 0,
1344 .number_of_rva_and_sizes = @intCast(u32, self.data_directories.len),
1345 };
1346 writer.writeAll(mem.asBytes(&opt_header)) catch unreachable;
1347 },
1348 .p64 => {
1349 var opt_header = coff.OptionalHeaderPE64{
1350 .magic = coff.IMAGE_NT_OPTIONAL_HDR64_MAGIC,
1351 .major_linker_version = 0,
1352 .minor_linker_version = 0,
1353 .size_of_code = size_of_code,
1354 .size_of_initialized_data = size_of_initialized_data,
1355 .size_of_uninitialized_data = size_of_uninitialized_data,
1356 .address_of_entry_point = self.entry_addr orelse 0,
1357 .base_of_code = base_of_code,
1358 .image_base = image_base,
1359 .section_alignment = self.page_size,
1360 .file_alignment = default_file_alignment,
1361 .major_operating_system_version = 6,
1362 .minor_operating_system_version = 0,
1363 .major_image_version = 0,
1364 .minor_image_version = 0,
1365 .major_subsystem_version = 6,
1366 .minor_subsystem_version = 0,
1367 .win32_version_value = 0,
1368 .size_of_image = size_of_image,
1369 .size_of_headers = size_of_headers,
1370 .checksum = 0,
1371 .subsystem = subsystem,
1372 .dll_flags = dll_flags,
1373 .size_of_stack_reserve = default_size_of_stack_reserve,
1374 .size_of_stack_commit = default_size_of_stack_commit,
1375 .size_of_heap_reserve = default_size_of_heap_reserve,
1376 .size_of_heap_commit = default_size_of_heap_commit,
1377 .loader_flags = 0,
1378 .number_of_rva_and_sizes = @intCast(u32, self.data_directories.len),
1379 };
1380 writer.writeAll(mem.asBytes(&opt_header)) catch unreachable;
1381 },
1382 }
11361383
1137 for (self.base.options.force_undefined_symbols.keys()) |symbol| {
1138 try argv.append(try allocPrint(arena, "-INCLUDE:{s}", .{symbol}));
1139 }
1384 try self.base.file.?.pwriteAll(buffer.items, 0);
1385}
11401386
1141 if (is_dyn_lib) {
1142 try argv.append("-DLL");
1143 }
1387pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
1388 // TODO https://github.com/ziglang/zig/issues/1284
1389 return math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
1390 math.maxInt(@TypeOf(actual_size));
1391}
11441392
1145 if (self.base.options.entry) |entry| {
1146 try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{entry}));
1147 }
1393fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 {
1394 const headers_size = self.getSizeOfHeaders();
1395 if (start < headers_size)
1396 return headers_size;
11481397
1149 if (self.base.options.tsaware) {
1150 try argv.append("-tsaware");
1151 }
1152 if (self.base.options.nxcompat) {
1153 try argv.append("-nxcompat");
1154 }
1155 if (self.base.options.dynamicbase) {
1156 try argv.append("-dynamicbase");
1157 }
1398 const end = start + size;
11581399
1159 try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path}));
1400 if (self.strtab_offset) |off| {
1401 const increased_size = @intCast(u32, self.strtab.len());
1402 const test_end = off + increased_size;
1403 if (end > off and start < test_end) {
1404 return test_end;
1405 }
1406 }
11601407
1161 if (self.base.options.implib_emit) |emit| {
1162 const implib_out_path = try emit.directory.join(arena, &[_][]const u8{emit.sub_path});
1163 try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path}));
1408 for (self.sections.items(.header)) |header| {
1409 const increased_size = header.size_of_raw_data;
1410 const test_end = header.pointer_to_raw_data + increased_size;
1411 if (end > header.pointer_to_raw_data and start < test_end) {
1412 return test_end;
11641413 }
1414 }
11651415
1166 if (self.base.options.link_libc) {
1167 if (self.base.options.libc_installation) |libc_installation| {
1168 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?}));
1416 return null;
1417}
11691418
1170 if (target.abi == .msvc) {
1171 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?}));
1172 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?}));
1173 }
1174 }
1175 }
1419pub fn allocatedSize(self: *Coff, start: u32) u32 {
1420 if (start == 0)
1421 return 0;
1422 var min_pos: u32 = std.math.maxInt(u32);
1423 if (self.strtab_offset) |off| {
1424 if (off > start and off < min_pos) min_pos = off;
1425 }
1426 for (self.sections.items(.header)) |header| {
1427 if (header.pointer_to_raw_data <= start) continue;
1428 if (header.pointer_to_raw_data < min_pos) min_pos = header.pointer_to_raw_data;
1429 }
1430 return min_pos - start;
1431}
11761432
1177 for (self.base.options.lib_dirs) |lib_dir| {
1178 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir}));
1179 }
1433pub fn findFreeSpace(self: *Coff, object_size: u32, min_alignment: u32) u32 {
1434 var start: u32 = 0;
1435 while (self.detectAllocCollision(start, object_size)) |item_end| {
1436 start = mem.alignForwardGeneric(u32, item_end, min_alignment);
1437 }
1438 return start;
1439}
11801440
1181 try argv.ensureUnusedCapacity(self.base.options.objects.len);
1182 for (self.base.options.objects) |obj| {
1183 if (obj.must_link) {
1184 argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{s}", .{obj.path}));
1185 } else {
1186 argv.appendAssumeCapacity(obj.path);
1187 }
1188 }
1441inline fn getSizeOfHeaders(self: Coff) u32 {
1442 const msdos_hdr_size = msdos_stub.len + 4;
1443 return @intCast(u32, msdos_hdr_size + @sizeOf(coff.CoffHeader) + self.getOptionalHeaderSize() +
1444 self.getDataDirectoryHeadersSize() + self.getSectionHeadersSize());
1445}
11891446
1190 for (comp.c_object_table.keys()) |key| {
1191 try argv.append(key.status.success.object_path);
1192 }
1447inline fn getOptionalHeaderSize(self: Coff) u32 {
1448 return switch (self.ptr_width) {
1449 .p32 => @intCast(u32, @sizeOf(coff.OptionalHeaderPE32)),
1450 .p64 => @intCast(u32, @sizeOf(coff.OptionalHeaderPE64)),
1451 };
1452}
11931453
1194 if (module_obj_path) |p| {
1195 try argv.append(p);
1196 }
1454inline fn getDataDirectoryHeadersSize(self: Coff) u32 {
1455 return @intCast(u32, self.data_directories.len * @sizeOf(coff.ImageDataDirectory));
1456}
11971457
1198 const resolved_subsystem: ?std.Target.SubSystem = blk: {
1199 if (self.base.options.subsystem) |explicit| break :blk explicit;
1200 switch (target.os.tag) {
1201 .windows => {
1202 if (self.base.options.module) |module| {
1203 if (module.stage1_flags.have_dllmain_crt_startup or is_dyn_lib)
1204 break :blk null;
1205 if (module.stage1_flags.have_c_main or self.base.options.is_test or
1206 module.stage1_flags.have_winmain_crt_startup or
1207 module.stage1_flags.have_wwinmain_crt_startup)
1208 {
1209 break :blk .Console;
1210 }
1211 if (module.stage1_flags.have_winmain or module.stage1_flags.have_wwinmain)
1212 break :blk .Windows;
1213 }
1214 },
1215 .uefi => break :blk .EfiApplication,
1216 else => {},
1217 }
1218 break :blk null;
1219 };
1458inline fn getSectionHeadersSize(self: Coff) u32 {
1459 return @intCast(u32, self.sections.slice().len * @sizeOf(coff.SectionHeader));
1460}
12201461
1221 const Mode = enum { uefi, win32 };
1222 const mode: Mode = mode: {
1223 if (resolved_subsystem) |subsystem| {
1224 const subsystem_suffix = ss: {
1225 if (self.base.options.major_subsystem_version) |major| {
1226 if (self.base.options.minor_subsystem_version) |minor| {
1227 break :ss try allocPrint(arena, ",{d}.{d}", .{ major, minor });
1228 } else {
1229 break :ss try allocPrint(arena, ",{d}", .{major});
1230 }
1231 }
1232 break :ss "";
1233 };
1234
1235 switch (subsystem) {
1236 .Console => {
1237 try argv.append(try allocPrint(arena, "-SUBSYSTEM:console{s}", .{
1238 subsystem_suffix,
1239 }));
1240 break :mode .win32;
1241 },
1242 .EfiApplication => {
1243 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_application{s}", .{
1244 subsystem_suffix,
1245 }));
1246 break :mode .uefi;
1247 },
1248 .EfiBootServiceDriver => {
1249 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_boot_service_driver{s}", .{
1250 subsystem_suffix,
1251 }));
1252 break :mode .uefi;
1253 },
1254 .EfiRom => {
1255 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_rom{s}", .{
1256 subsystem_suffix,
1257 }));
1258 break :mode .uefi;
1259 },
1260 .EfiRuntimeDriver => {
1261 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_runtime_driver{s}", .{
1262 subsystem_suffix,
1263 }));
1264 break :mode .uefi;
1265 },
1266 .Native => {
1267 try argv.append(try allocPrint(arena, "-SUBSYSTEM:native{s}", .{
1268 subsystem_suffix,
1269 }));
1270 break :mode .win32;
1271 },
1272 .Posix => {
1273 try argv.append(try allocPrint(arena, "-SUBSYSTEM:posix{s}", .{
1274 subsystem_suffix,
1275 }));
1276 break :mode .win32;
1277 },
1278 .Windows => {
1279 try argv.append(try allocPrint(arena, "-SUBSYSTEM:windows{s}", .{
1280 subsystem_suffix,
1281 }));
1282 break :mode .win32;
1283 },
1284 }
1285 } else if (target.os.tag == .uefi) {
1286 break :mode .uefi;
1287 } else {
1288 break :mode .win32;
1289 }
1290 };
1462inline fn getDataDirectoryHeadersOffset(self: Coff) u32 {
1463 const msdos_hdr_size = msdos_stub.len + 4;
1464 return @intCast(u32, msdos_hdr_size + @sizeOf(coff.CoffHeader) + self.getOptionalHeaderSize());
1465}
12911466
1292 switch (mode) {
1293 .uefi => try argv.appendSlice(&[_][]const u8{
1294 "-BASE:0",
1295 "-ENTRY:EfiMain",
1296 "-OPT:REF",
1297 "-SAFESEH:NO",
1298 "-MERGE:.rdata=.data",
1299 "-ALIGN:32",
1300 "-NODEFAULTLIB",
1301 "-SECTION:.xdata,D",
1302 }),
1303 .win32 => {
1304 if (link_in_crt) {
1305 if (target.abi.isGnu()) {
1306 try argv.append("-lldmingw");
1307
1308 if (target.cpu.arch == .i386) {
1309 try argv.append("-ALTERNATENAME:__image_base__=___ImageBase");
1310 } else {
1311 try argv.append("-ALTERNATENAME:__image_base__=__ImageBase");
1312 }
1313
1314 if (is_dyn_lib) {
1315 try argv.append(try comp.get_libc_crt_file(arena, "dllcrt2.obj"));
1316 if (target.cpu.arch == .i386) {
1317 try argv.append("-ALTERNATENAME:__DllMainCRTStartup@12=_DllMainCRTStartup@12");
1318 } else {
1319 try argv.append("-ALTERNATENAME:_DllMainCRTStartup=DllMainCRTStartup");
1320 }
1321 } else {
1322 try argv.append(try comp.get_libc_crt_file(arena, "crt2.obj"));
1323 }
1324
1325 try argv.append(try comp.get_libc_crt_file(arena, "mingw32.lib"));
1326 try argv.append(try comp.get_libc_crt_file(arena, "mingwex.lib"));
1327 try argv.append(try comp.get_libc_crt_file(arena, "msvcrt-os.lib"));
1328
1329 for (mingw.always_link_libs) |name| {
1330 if (!self.base.options.system_libs.contains(name)) {
1331 const lib_basename = try allocPrint(arena, "{s}.lib", .{name});
1332 try argv.append(try comp.get_libc_crt_file(arena, lib_basename));
1333 }
1334 }
1335 } else {
1336 const lib_str = switch (self.base.options.link_mode) {
1337 .Dynamic => "",
1338 .Static => "lib",
1339 };
1340 const d_str = switch (self.base.options.optimize_mode) {
1341 .Debug => "d",
1342 else => "",
1343 };
1344 switch (self.base.options.link_mode) {
1345 .Static => try argv.append(try allocPrint(arena, "libcmt{s}.lib", .{d_str})),
1346 .Dynamic => try argv.append(try allocPrint(arena, "msvcrt{s}.lib", .{d_str})),
1347 }
1348
1349 try argv.append(try allocPrint(arena, "{s}vcruntime{s}.lib", .{ lib_str, d_str }));
1350 try argv.append(try allocPrint(arena, "{s}ucrt{s}.lib", .{ lib_str, d_str }));
1351
1352 //Visual C++ 2015 Conformance Changes
1353 //https://msdn.microsoft.com/en-us/library/bb531344.aspx
1354 try argv.append("legacy_stdio_definitions.lib");
1355
1356 // msvcrt depends on kernel32 and ntdll
1357 try argv.append("kernel32.lib");
1358 try argv.append("ntdll.lib");
1359 }
1360 } else {
1361 try argv.append("-NODEFAULTLIB");
1362 if (!is_lib) {
1363 if (self.base.options.module) |module| {
1364 if (module.stage1_flags.have_winmain_crt_startup) {
1365 try argv.append("-ENTRY:WinMainCRTStartup");
1366 } else {
1367 try argv.append("-ENTRY:wWinMainCRTStartup");
1368 }
1369 } else {
1370 try argv.append("-ENTRY:wWinMainCRTStartup");
1371 }
1372 }
1373 }
1374 },
1375 }
1467inline fn getSectionHeadersOffset(self: Coff) u32 {
1468 return self.getDataDirectoryHeadersOffset() + self.getDataDirectoryHeadersSize();
1469}
13761470
1377 // libc++ dep
1378 if (self.base.options.link_libcpp) {
1379 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
1380 try argv.append(comp.libcxx_static_lib.?.full_object_path);
1381 }
1471inline fn getSizeOfImage(self: Coff) u32 {
1472 var image_size: u32 = mem.alignForwardGeneric(u32, self.getSizeOfHeaders(), self.page_size);
1473 for (self.sections.items(.header)) |header| {
1474 image_size += mem.alignForwardGeneric(u32, header.virtual_size, self.page_size);
1475 }
1476 return image_size;
1477}
13821478
1383 // libunwind dep
1384 if (self.base.options.link_libunwind) {
1385 try argv.append(comp.libunwind_static_lib.?.full_object_path);
1386 }
1479/// Returns symbol location corresponding to the set entrypoint (if any).
1480pub fn getEntryPoint(self: Coff) ?SymbolWithLoc {
1481 const entry_name = self.base.options.entry orelse "_start"; // TODO this is incomplete
1482 return self.globals.get(entry_name);
1483}
13871484
1388 if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies) {
1389 if (!self.base.options.link_libc) {
1390 if (comp.libc_static_lib) |lib| {
1391 try argv.append(lib.full_object_path);
1392 }
1393 }
1394 // MinGW doesn't provide libssp symbols
1395 if (target.abi.isGnu()) {
1396 if (comp.libssp_static_lib) |lib| {
1397 try argv.append(lib.full_object_path);
1398 }
1399 }
1400 // MSVC compiler_rt is missing some stuff, so we build it unconditionally but
1401 // and rely on weak linkage to allow MSVC compiler_rt functions to override ours.
1402 if (comp.compiler_rt_lib) |lib| {
1403 try argv.append(lib.full_object_path);
1404 }
1405 }
1485/// Returns pointer-to-symbol described by `sym_with_loc` descriptor.
1486pub fn getSymbolPtr(self: *Coff, sym_loc: SymbolWithLoc) *coff.Symbol {
1487 assert(sym_loc.file == null); // TODO linking object files
1488 return &self.locals.items[sym_loc.sym_index];
1489}
14061490
1407 try argv.ensureUnusedCapacity(self.base.options.system_libs.count());
1408 for (self.base.options.system_libs.keys()) |key| {
1409 const lib_basename = try allocPrint(arena, "{s}.lib", .{key});
1410 if (comp.crt_files.get(lib_basename)) |crt_file| {
1411 argv.appendAssumeCapacity(crt_file.full_object_path);
1412 continue;
1413 }
1414 if (try self.findLib(arena, lib_basename)) |full_path| {
1415 argv.appendAssumeCapacity(full_path);
1416 continue;
1417 }
1418 if (target.abi.isGnu()) {
1419 const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key});
1420 if (try self.findLib(arena, fallback_name)) |full_path| {
1421 argv.appendAssumeCapacity(full_path);
1422 continue;
1423 }
1424 }
1425 log.err("DLL import library for -l{s} not found", .{key});
1426 return error.DllImportLibraryNotFound;
1427 }
1491/// Returns symbol described by `sym_with_loc` descriptor.
1492pub fn getSymbol(self: *const Coff, sym_loc: SymbolWithLoc) *const coff.Symbol {
1493 assert(sym_loc.file == null); // TODO linking object files
1494 return &self.locals.items[sym_loc.sym_index];
1495}
14281496
1429 if (self.base.options.verbose_link) {
1430 // Skip over our own name so that the LLD linker name is the first argv item.
1431 Compilation.dump_argv(argv.items[1..]);
1432 }
1497/// Returns name of the symbol described by `sym_with_loc` descriptor.
1498pub fn getSymbolName(self: *const Coff, sym_loc: SymbolWithLoc) []const u8 {
1499 assert(sym_loc.file == null); // TODO linking object files
1500 const sym = self.getSymbol(sym_loc);
1501 const offset = sym.getNameOffset() orelse return sym.getName().?;
1502 return self.strtab.get(offset).?;
1503}
14331504
1434 if (std.process.can_spawn) {
1435 // If possible, we run LLD as a child process because it does not always
1436 // behave properly as a library, unfortunately.
1437 // https://github.com/ziglang/zig/issues/3825
1438 var child = std.ChildProcess.init(argv.items, arena);
1439 if (comp.clang_passthrough_mode) {
1440 child.stdin_behavior = .Inherit;
1441 child.stdout_behavior = .Inherit;
1442 child.stderr_behavior = .Inherit;
1443
1444 const term = child.spawnAndWait() catch |err| {
1445 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1446 return error.UnableToSpawnSelf;
1447 };
1448 switch (term) {
1449 .Exited => |code| {
1450 if (code != 0) {
1451 std.process.exit(code);
1452 }
1453 },
1454 else => std.process.abort(),
1455 }
1456 } else {
1457 child.stdin_behavior = .Ignore;
1458 child.stdout_behavior = .Ignore;
1459 child.stderr_behavior = .Pipe;
1460
1461 try child.spawn();
1462
1463 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
1464
1465 const term = child.wait() catch |err| {
1466 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1467 return error.UnableToSpawnSelf;
1468 };
1469
1470 switch (term) {
1471 .Exited => |code| {
1472 if (code != 0) {
1473 // TODO parse this output and surface with the Compilation API rather than
1474 // directly outputting to stderr here.
1475 std.debug.print("{s}", .{stderr});
1476 return error.LLDReportedFailure;
1477 }
1478 },
1479 else => {
1480 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
1481 return error.LLDCrashed;
1482 },
1483 }
1505/// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor.
1506/// Returns null on failure.
1507pub fn getAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {
1508 assert(sym_loc.file == null); // TODO linking with object files
1509 return self.atom_by_index_table.get(sym_loc.sym_index);
1510}
14841511
1485 if (stderr.len != 0) {
1486 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1487 }
1488 }
1489 } else {
1490 const exit_code = try lldMain(arena, argv.items, false);
1491 if (exit_code != 0) {
1492 if (comp.clang_passthrough_mode) {
1493 std.process.exit(exit_code);
1494 } else {
1495 return error.LLDReportedFailure;
1496 }
1497 }
1498 }
1499 }
1512/// Returns GOT atom that references `sym_with_loc` if one exists.
1513/// Returns null otherwise.
1514pub fn getGotAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {
1515 const got_index = self.got_entries.get(sym_loc) orelse return null;
1516 return self.atom_by_index_table.get(got_index);
1517}
15001518
1501 if (!self.base.options.disable_lld_caching) {
1502 // Update the file with the digest. If it fails we can continue; it only
1503 // means that the next invocation will have an unnecessary cache miss.
1504 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
1505 log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)});
1506 };
1507 // Again failure here only means an unnecessary cache miss.
1508 man.writeManifest() catch |err| {
1509 log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
1510 };
1511 // We hang on to this lock so that the output file path can be used without
1512 // other processes clobbering it.
1513 self.base.lock = man.toOwnedLock();
1519fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void {
1520 if (name.len <= 8) {
1521 mem.copy(u8, &header.name, name);
1522 mem.set(u8, header.name[name.len..], 0);
1523 return;
15141524 }
1525 const offset = try self.strtab.insert(self.base.allocator, name);
1526 const name_offset = fmt.bufPrint(&header.name, "/{d}", .{offset}) catch unreachable;
1527 mem.set(u8, header.name[name_offset.len..], 0);
15151528}
15161529
1517fn findLib(self: *Coff, arena: Allocator, name: []const u8) !?[]const u8 {
1518 for (self.base.options.lib_dirs) |lib_dir| {
1519 const full_path = try fs.path.join(arena, &.{ lib_dir, name });
1520 fs.cwd().access(full_path, .{}) catch |err| switch (err) {
1521 error.FileNotFound => continue,
1522 else => |e| return e,
1523 };
1524 return full_path;
1530fn setSymbolName(self: *Coff, symbol: *coff.Symbol, name: []const u8) !void {
1531 if (name.len <= 8) {
1532 mem.copy(u8, &symbol.name, name);
1533 mem.set(u8, symbol.name[name.len..], 0);
1534 return;
15251535 }
1526 return null;
1536 const offset = try self.strtab.insert(self.base.allocator, name);
1537 mem.set(u8, symbol.name[0..4], 0);
1538 mem.writeIntLittle(u32, symbol.name[4..8], offset);
15271539}
15281540
1529pub fn getDeclVAddr(
1530 self: *Coff,
1531 decl_index: Module.Decl.Index,
1532 reloc_info: link.File.RelocInfo,
1533) !u64 {
1534 _ = reloc_info;
1535 const mod = self.base.options.module.?;
1536 const decl = mod.declPtr(decl_index);
1537 assert(self.llvm_object == null);
1538 return self.text_section_virtual_address + decl.link.coff.text_offset;
1541fn logSymAttributes(sym: *const coff.Symbol, buf: *[4]u8) []const u8 {
1542 mem.set(u8, buf[0..4], '_');
1543 switch (sym.section_number) {
1544 .UNDEFINED => {
1545 buf[3] = 'u';
1546 switch (sym.storage_class) {
1547 .EXTERNAL => buf[1] = 'e',
1548 .WEAK_EXTERNAL => buf[1] = 'w',
1549 .NULL => {},
1550 else => unreachable,
1551 }
1552 },
1553 .ABSOLUTE => unreachable, // handle ABSOLUTE
1554 .DEBUG => unreachable,
1555 else => {
1556 buf[0] = 's';
1557 switch (sym.storage_class) {
1558 .EXTERNAL => buf[1] = 'e',
1559 .WEAK_EXTERNAL => buf[1] = 'w',
1560 .NULL => {},
1561 else => unreachable,
1562 }
1563 },
1564 }
1565 return buf[0..];
15391566}
15401567
1541pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {
1542 _ = self;
1543 _ = module;
1544 _ = decl;
1545 // TODO Implement this
1546}
1568fn logSymtab(self: *Coff) void {
1569 var buf: [4]u8 = undefined;
1570
1571 log.debug("symtab:", .{});
1572 log.debug(" object(null)", .{});
1573 for (self.locals.items) |*sym, sym_id| {
1574 const where = if (sym.section_number == .UNDEFINED) "ord" else "sect";
1575 const def_index: u16 = switch (sym.section_number) {
1576 .UNDEFINED => 0, // TODO
1577 .ABSOLUTE => unreachable, // TODO
1578 .DEBUG => unreachable, // TODO
1579 else => @enumToInt(sym.section_number),
1580 };
1581 log.debug(" %{d}: {?s} @{x} in {s}({d}), {s}", .{
1582 sym_id,
1583 self.getSymbolName(.{ .sym_index = @intCast(u32, sym_id), .file = null }),
1584 sym.value,
1585 where,
1586 def_index,
1587 logSymAttributes(sym, &buf),
1588 });
1589 }
15471590
1548pub fn deinit(self: *Coff) void {
1549 if (build_options.have_llvm) {
1550 if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
1591 log.debug("globals table:", .{});
1592 for (self.globals.keys()) |name, id| {
1593 const value = self.globals.values()[id];
1594 log.debug(" {s} => %{d} in object({?d})", .{ name, value.sym_index, value.file });
15511595 }
15521596
1553 self.text_block_free_list.deinit(self.base.allocator);
1554 self.offset_table.deinit(self.base.allocator);
1555 self.offset_table_free_list.deinit(self.base.allocator);
1597 log.debug("GOT entries:", .{});
1598 for (self.got_entries.keys()) |target, i| {
1599 const got_sym = self.getSymbol(.{ .sym_index = self.got_entries.values()[i], .file = null });
1600 const target_sym = self.getSymbol(target);
1601 if (target_sym.section_number == .UNDEFINED) {
1602 log.debug(" {d}@{x} => import('{s}')", .{
1603 i,
1604 got_sym.value,
1605 self.getSymbolName(target),
1606 });
1607 } else {
1608 log.debug(" {d}@{x} => local(%{d}) in object({?d}) {s}", .{
1609 i,
1610 got_sym.value,
1611 target.sym_index,
1612 target.file,
1613 logSymAttributes(target_sym, &buf),
1614 });
1615 }
1616 }
15561617}
src/link/Coff/Atom.zig created+110
......@@ -0,0 +1,110 @@
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 Reloc = Coff.Reloc;
10const SymbolWithLoc = Coff.SymbolWithLoc;
11
12/// Each decl always gets a local symbol with the fully qualified name.
13/// The vaddr and size are found here directly.
14/// The file offset is found by computing the vaddr offset from the section vaddr
15/// the symbol references, and adding that to the file offset of the section.
16/// If this field is 0, it means the codegen size = 0 and there is no symbol or
17/// offset table entry.
18sym_index: u32,
19
20/// null means symbol defined by Zig source.
21file: ?u32,
22
23/// Used size of the atom
24size: u32,
25
26/// Alignment of the atom
27alignment: u32,
28
29/// Points to the previous and next neighbors, based on the `text_offset`.
30/// This can be used to find, for example, the capacity of this `Atom`.
31prev: ?*Atom,
32next: ?*Atom,
33
34pub const empty = Atom{
35 .sym_index = 0,
36 .file = null,
37 .size = 0,
38 .alignment = 0,
39 .prev = null,
40 .next = null,
41};
42
43pub fn deinit(self: *Atom, gpa: Allocator) void {
44 _ = self;
45 _ = gpa;
46}
47
48/// Returns symbol referencing this atom.
49pub fn getSymbol(self: Atom, coff_file: *const Coff) *const coff.Symbol {
50 return coff_file.getSymbol(.{
51 .sym_index = self.sym_index,
52 .file = self.file,
53 });
54}
55
56/// Returns pointer-to-symbol referencing this atom.
57pub fn getSymbolPtr(self: Atom, coff_file: *Coff) *coff.Symbol {
58 return coff_file.getSymbolPtr(.{
59 .sym_index = self.sym_index,
60 .file = self.file,
61 });
62}
63
64pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
65 return .{ .sym_index = self.sym_index, .file = self.file };
66}
67
68/// Returns the name of this atom.
69pub fn getName(self: Atom, coff_file: *const Coff) []const u8 {
70 return coff_file.getSymbolName(.{
71 .sym_index = self.sym_index,
72 .file = self.file,
73 });
74}
75
76/// Returns how much room there is to grow in virtual address space.
77pub fn capacity(self: Atom, coff_file: *const Coff) u32 {
78 const self_sym = self.getSymbol(coff_file);
79 if (self.next) |next| {
80 const next_sym = next.getSymbol(coff_file);
81 return next_sym.value - self_sym.value;
82 } else {
83 // We are the last atom.
84 // The capacity is limited only by virtual address space.
85 return std.math.maxInt(u32) - self_sym.value;
86 }
87}
88
89pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool {
90 // No need to keep a free list node for the last atom.
91 const next = self.next orelse return false;
92 const self_sym = self.getSymbol(coff_file);
93 const next_sym = next.getSymbol(coff_file);
94 const cap = next_sym.value - self_sym.value;
95 const ideal_cap = Coff.padToIdeal(self.size);
96 if (cap <= ideal_cap) return false;
97 const surplus = cap - ideal_cap;
98 return surplus >= Coff.min_text_capacity;
99}
100
101pub fn addRelocation(self: *Atom, coff_file: *Coff, reloc: Reloc) !void {
102 const gpa = coff_file.base.allocator;
103 // TODO causes a segfault on Windows
104 // log.debug("adding reloc of type {s} to target %{d}", .{ @tagName(reloc.@"type"), reloc.target.sym_index });
105 const gop = try coff_file.relocs.getOrPut(gpa, self);
106 if (!gop.found_existing) {
107 gop.value_ptr.* = .{};
108 }
109 try gop.value_ptr.append(gpa, reloc);
110}
src/link/Coff/Object.zig created+12
......@@ -0,0 +1,12 @@
1const Object = @This();
2
3const std = @import("std");
4const mem = std.mem;
5
6const Allocator = mem.Allocator;
7
8name: []const u8,
9
10pub fn deinit(self: *Object, gpa: Allocator) void {
11 gpa.free(self.name);
12}
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,
src/link/strtab.zig+4
......@@ -109,5 +109,9 @@ pub fn StringTable(comptime log_scope: @Type(.EnumLiteral)) type {
109109 pub fn getAssumeExists(self: Self, off: u32) []const u8 {
110110 return self.get(off) orelse unreachable;
111111 }
112
113 pub fn len(self: Self) usize {
114 return self.buffer.items.len;
115 }
112116 };
113117}
test/cases/aarch64-macos/hello_world_with_updates.0.zig+1-1
......@@ -2,5 +2,5 @@
22// output_mode=Exe
33// target=aarch64-macos
44//
5// :107:9: error: struct 'tmp.tmp' has no member named 'main'
5// :105:9: error: struct 'tmp.tmp' has no member named 'main'
66// :7:1: note: struct declared here
test/cases/x86_64-linux/hello_world_with_updates.0.zig+1-1
......@@ -2,5 +2,5 @@
22// output_mode=Exe
33// target=x86_64-linux
44//
5// :107:9: error: struct 'tmp.tmp' has no member named 'main'
5// :105:9: error: struct 'tmp.tmp' has no member named 'main'
66// :7:1: note: struct declared here
test/cases/x86_64-macos/hello_world_with_updates.0.zig+1-1
......@@ -2,5 +2,5 @@
22// output_mode=Exe
33// target=x86_64-macos
44//
5// :107:9: error: struct 'tmp.tmp' has no member named 'main'
5// :105:9: error: struct 'tmp.tmp' has no member named 'main'
66// :7:1: note: struct declared here