aboutsummaryrefslogtreecommitdiff
path: root/src/link/Coff.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2020-09-28 00:06:06 -0700
committerAndrew Kelley <andrew@ziglang.org>2020-09-28 00:06:06 -0700
commit91a73a177bc20fa0219dbb6c3cf3dda1c2a465db (patch)
tree5f391418951620a1f28e8a8fccddbb8e33aed8f6 /src/link/Coff.zig
parenta9082b4ec51debdbffc20b56e9b37cb82dd04750 (diff)
downloadzig-91a73a177bc20fa0219dbb6c3cf3dda1c2a465db.tar.gz
zig-91a73a177bc20fa0219dbb6c3cf3dda1c2a465db.zip
stage2: building mingw-w64 and COFF LDD linking
still TODO is the task of creating import .lib files for DLLs on the fly both for -lfoo and for e.g. `extern "kernel32"`
Diffstat (limited to 'src/link/Coff.zig')
-rw-r--r--src/link/Coff.zig576
1 files changed, 497 insertions, 79 deletions
diff --git a/src/link/Coff.zig b/src/link/Coff.zig
index c396732bc1..e1d7a07fbc 100644
--- a/src/link/Coff.zig
+++ b/src/link/Coff.zig
@@ -5,6 +5,8 @@ const log = std.log.scoped(.link);
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const fs = std.fs;
+const allocPrint = std.fmt.allocPrint;
+const mem = std.mem;
const trace = @import("../tracy.zig").trace;
const Module = @import("../Module.zig");
@@ -12,6 +14,8 @@ const Compilation = @import("../Compilation.zig");
const codegen = @import("../codegen.zig");
const link = @import("../link.zig");
const build_options = @import("build_options");
+const Cache = @import("../Cache.zig");
+const mingw = @import("../mingw.zig");
const allocation_padding = 4 / 3;
const minimum_text_block_size = 64 * allocation_padding;
@@ -21,7 +25,7 @@ const file_alignment = 512;
const image_base = 0x400_000;
const section_table_size = 2 * 40;
comptime {
- assert(std.mem.isAligned(image_base, section_alignment));
+ assert(mem.isAligned(image_base, section_alignment));
}
pub const base_tag: link.File.Tag = .coff;
@@ -155,14 +159,14 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
if (machine == .Unknown) {
return error.UnsupportedCOFFArchitecture;
}
- std.mem.writeIntLittle(u16, hdr_data[0..2], @enumToInt(machine));
+ mem.writeIntLittle(u16, hdr_data[0..2], @enumToInt(machine));
index += 2;
// Number of sections (we only use .got, .text)
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 2);
+ mem.writeIntLittle(u16, hdr_data[index..][0..2], 2);
index += 2;
// TimeDateStamp (u32), PointerToSymbolTable (u32), NumberOfSymbols (u32)
- std.mem.set(u8, hdr_data[index..][0..12], 0);
+ mem.set(u8, hdr_data[index..][0..12], 0);
index += 12;
const optional_header_size = switch (options.output_mode) {
@@ -177,8 +181,8 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
const default_offset_table_size = file_alignment;
const default_size_of_code = 0;
- self.section_data_offset = std.mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment);
- const section_data_relative_virtual_address = std.mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment);
+ self.section_data_offset = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment);
+ const section_data_relative_virtual_address = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment);
self.offset_table_virtual_address = image_base + section_data_relative_virtual_address;
self.offset_table_size = default_offset_table_size;
self.section_table_offset = section_table_offset;
@@ -186,9 +190,9 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
self.text_section_size = default_size_of_code;
// Size of file when loaded in memory
- const size_of_image = std.mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + default_size_of_code, section_alignment);
+ const size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + default_size_of_code, section_alignment);
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size);
+ mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size);
index += 2;
// Characteristics
@@ -200,7 +204,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
.p32 => characteristics |= std.coff.IMAGE_FILE_32BIT_MACHINE,
.p64 => characteristics |= std.coff.IMAGE_FILE_LARGE_ADDRESS_AWARE,
}
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], characteristics);
+ mem.writeIntLittle(u16, hdr_data[index..][0..2], characteristics);
index += 2;
assert(index == 20);
@@ -210,106 +214,106 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
self.optional_header_offset = coff_file_header_offset + 20;
// Optional header
index = 0;
- std.mem.writeIntLittle(u16, hdr_data[0..2], switch (self.ptr_width) {
+ mem.writeIntLittle(u16, hdr_data[0..2], switch (self.ptr_width) {
.p32 => @as(u16, 0x10b),
.p64 => 0x20b,
});
index += 2;
// Linker version (u8 + u8)
- std.mem.set(u8, hdr_data[index..][0..2], 0);
+ mem.set(u8, hdr_data[index..][0..2], 0);
index += 2;
// SizeOfCode (UNUSED, u32), SizeOfInitializedData (u32), SizeOfUninitializedData (u32), AddressOfEntryPoint (u32), BaseOfCode (UNUSED, u32)
- std.mem.set(u8, hdr_data[index..][0..20], 0);
+ mem.set(u8, hdr_data[index..][0..20], 0);
index += 20;
if (self.ptr_width == .p32) {
// Base of data relative to the image base (UNUSED)
- std.mem.set(u8, hdr_data[index..][0..4], 0);
+ mem.set(u8, hdr_data[index..][0..4], 0);
index += 4;
// Image base address
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], image_base);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], image_base);
index += 4;
} else {
// Image base address
- std.mem.writeIntLittle(u64, hdr_data[index..][0..8], image_base);
+ mem.writeIntLittle(u64, hdr_data[index..][0..8], image_base);
index += 8;
}
// Section alignment
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], section_alignment);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], section_alignment);
index += 4;
// File alignment
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], file_alignment);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], file_alignment);
index += 4;
// Required OS version, 6.0 is vista
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
+ mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
index += 2;
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
+ mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
index += 2;
// Image version
- std.mem.set(u8, hdr_data[index..][0..4], 0);
+ mem.set(u8, hdr_data[index..][0..4], 0);
index += 4;
// Required subsystem version, same as OS version
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
+ mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
index += 2;
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
+ mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
index += 2;
// Reserved zeroes (u32)
- std.mem.set(u8, hdr_data[index..][0..4], 0);
+ mem.set(u8, hdr_data[index..][0..4], 0);
index += 4;
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], size_of_image);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], size_of_image);
index += 4;
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
index += 4;
// CheckSum (u32)
- std.mem.set(u8, hdr_data[index..][0..4], 0);
+ mem.set(u8, hdr_data[index..][0..4], 0);
index += 4;
// Subsystem, TODO: Let users specify the subsystem, always CUI for now
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 3);
+ mem.writeIntLittle(u16, hdr_data[index..][0..2], 3);
index += 2;
// DLL characteristics
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0x0);
+ mem.writeIntLittle(u16, hdr_data[index..][0..2], 0x0);
index += 2;
switch (self.ptr_width) {
.p32 => {
// Size of stack reserve + commit
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000_000);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000_000);
index += 4;
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
index += 4;
// Size of heap reserve + commit
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x100_000);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x100_000);
index += 4;
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
index += 4;
},
.p64 => {
// Size of stack reserve + commit
- std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000_000);
+ mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000_000);
index += 8;
- std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
+ mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
index += 8;
// Size of heap reserve + commit
- std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x100_000);
+ mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x100_000);
index += 8;
- std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
+ mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
index += 8;
},
}
// Reserved zeroes
- std.mem.set(u8, hdr_data[index..][0..4], 0);
+ mem.set(u8, hdr_data[index..][0..4], 0);
index += 4;
// Number of data directories
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], data_directory_count);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], data_directory_count);
index += 4;
// Initialize data directories to zero
- std.mem.set(u8, hdr_data[index..][0 .. data_directory_count * 8], 0);
+ mem.set(u8, hdr_data[index..][0 .. data_directory_count * 8], 0);
index += data_directory_count * 8;
assert(index == optional_header_size);
@@ -321,52 +325,52 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
index += 8;
if (options.output_mode == .Exe) {
// Virtual size (u32)
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
index += 4;
// Virtual address (u32)
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - image_base);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - image_base);
index += 4;
} else {
- std.mem.set(u8, hdr_data[index..][0..8], 0);
+ mem.set(u8, hdr_data[index..][0..8], 0);
index += 8;
}
// Size of raw data (u32)
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
index += 4;
// File pointer to the start of the section
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
index += 4;
// Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16)
- std.mem.set(u8, hdr_data[index..][0..12], 0);
+ mem.set(u8, hdr_data[index..][0..12], 0);
index += 12;
// Section flags
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], std.coff.IMAGE_SCN_CNT_INITIALIZED_DATA | std.coff.IMAGE_SCN_MEM_READ);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], std.coff.IMAGE_SCN_CNT_INITIALIZED_DATA | std.coff.IMAGE_SCN_MEM_READ);
index += 4;
// Then, the .text section
hdr_data[index..][0..8].* = ".text\x00\x00\x00".*;
index += 8;
if (options.output_mode == .Exe) {
// Virtual size (u32)
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
index += 4;
// Virtual address (u32)
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - image_base);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - image_base);
index += 4;
} else {
- std.mem.set(u8, hdr_data[index..][0..8], 0);
+ mem.set(u8, hdr_data[index..][0..8], 0);
index += 8;
}
// Size of raw data (u32)
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
index += 4;
// File pointer to the start of the section
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset + default_offset_table_size);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset + default_offset_table_size);
index += 4;
// Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16)
- std.mem.set(u8, hdr_data[index..][0..12], 0);
+ mem.set(u8, hdr_data[index..][0..12], 0);
index += 12;
// Section flags
- std.mem.writeIntLittle(
+ mem.writeIntLittle(
u32,
hdr_data[index..][0..4],
std.coff.IMAGE_SCN_CNT_CODE | std.coff.IMAGE_SCN_MEM_EXECUTE | std.coff.IMAGE_SCN_MEM_READ | std.coff.IMAGE_SCN_MEM_WRITE,
@@ -434,7 +438,7 @@ fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, a
const free_block = self.text_block_free_list.items[i];
const next_block_text_offset = free_block.text_offset + free_block.capacity();
- const new_block_text_offset = std.mem.alignForwardGeneric(u64, free_block.getVAddr(self.*) + free_block.size, alignment) - self.text_section_virtual_address;
+ const new_block_text_offset = mem.alignForwardGeneric(u64, free_block.getVAddr(self.*) + free_block.size, alignment) - self.text_section_virtual_address;
if (new_block_text_offset < next_block_text_offset and next_block_text_offset - new_block_text_offset >= new_block_min_capacity) {
block_placement = free_block;
@@ -453,7 +457,7 @@ fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, a
continue;
}
} else if (self.last_text_block) |last| {
- const new_block_vaddr = std.mem.alignForwardGeneric(u64, last.getVAddr(self.*) + last.size, alignment);
+ const new_block_vaddr = mem.alignForwardGeneric(u64, last.getVAddr(self.*) + last.size, alignment);
block_placement = last;
break :blk new_block_vaddr;
} else {
@@ -463,15 +467,15 @@ fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, a
const expand_text_section = block_placement == null or block_placement.?.next == null;
if (expand_text_section) {
- const needed_size = @intCast(u32, std.mem.alignForwardGeneric(u64, vaddr + new_block_size - self.text_section_virtual_address, file_alignment));
+ const needed_size = @intCast(u32, mem.alignForwardGeneric(u64, vaddr + new_block_size - self.text_section_virtual_address, file_alignment));
if (needed_size > self.text_section_size) {
- const current_text_section_virtual_size = std.mem.alignForwardGeneric(u32, self.text_section_size, section_alignment);
- const new_text_section_virtual_size = std.mem.alignForwardGeneric(u32, needed_size, section_alignment);
+ const current_text_section_virtual_size = mem.alignForwardGeneric(u32, self.text_section_size, section_alignment);
+ const new_text_section_virtual_size = mem.alignForwardGeneric(u32, needed_size, section_alignment);
if (current_text_section_virtual_size != new_text_section_virtual_size) {
self.size_of_image_dirty = true;
// Write new virtual size
var buf: [4]u8 = undefined;
- std.mem.writeIntLittle(u32, &buf, new_text_section_virtual_size);
+ mem.writeIntLittle(u32, &buf, new_text_section_virtual_size);
try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 8);
}
@@ -509,7 +513,7 @@ fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, a
fn growTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
const block_vaddr = text_block.getVAddr(self.*);
- const align_ok = std.mem.alignBackwardGeneric(u64, block_vaddr, alignment) == block_vaddr;
+ const align_ok = mem.alignBackwardGeneric(u64, block_vaddr, alignment) == block_vaddr;
const need_realloc = !align_ok or new_block_size > text_block.capacity();
if (!need_realloc) return @as(u64, block_vaddr);
return self.allocateTextBlock(text_block, new_block_size, alignment);
@@ -575,14 +579,14 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
// Write the new raw size in the .got header
var buf: [8]u8 = undefined;
- std.mem.writeIntLittle(u32, buf[0..4], new_raw_size);
+ mem.writeIntLittle(u32, buf[0..4], new_raw_size);
try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 16);
// Write the new .text section file offset in the .text section header
- std.mem.writeIntLittle(u32, buf[0..4], new_text_section_start);
+ mem.writeIntLittle(u32, buf[0..4], new_text_section_start);
try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 20);
- const current_virtual_size = std.mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment);
- const new_virtual_size = std.mem.alignForwardGeneric(u32, new_raw_size, section_alignment);
+ const current_virtual_size = mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment);
+ const new_virtual_size = mem.alignForwardGeneric(u32, new_raw_size, section_alignment);
// 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
// and the virutal size of the `.got` section
@@ -592,12 +596,12 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
const va_offset = new_virtual_size - current_virtual_size;
// Write .got virtual size
- std.mem.writeIntLittle(u32, buf[0..4], new_virtual_size);
+ mem.writeIntLittle(u32, buf[0..4], new_virtual_size);
try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 8);
// Write .text new virtual address
self.text_section_virtual_address = self.text_section_virtual_address + va_offset;
- std.mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - image_base);
+ mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - image_base);
try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12);
// Fix the VAs in the offset table
@@ -607,11 +611,11 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
switch (entry_size) {
4 => {
- std.mem.writeInt(u32, buf[0..4], @intCast(u32, va.*), endian);
+ mem.writeInt(u32, buf[0..4], @intCast(u32, va.*), endian);
try self.base.file.?.pwriteAll(buf[0..4], offset_table_start + idx * entry_size);
},
8 => {
- std.mem.writeInt(u64, &buf, va.*, endian);
+ mem.writeInt(u64, &buf, va.*, endian);
try self.base.file.?.pwriteAll(&buf, offset_table_start + idx * entry_size);
},
else => unreachable,
@@ -626,12 +630,12 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
switch (entry_size) {
4 => {
var buf: [4]u8 = undefined;
- std.mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
+ mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size);
},
8 => {
var buf: [8]u8 = undefined;
- std.mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
+ mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size);
},
else => unreachable,
@@ -664,7 +668,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
if (curr_size != 0) {
const capacity = decl.link.coff.capacity();
const need_realloc = code.len > capacity or
- !std.mem.isAlignedGeneric(u32, decl.link.coff.text_offset, required_alignment);
+ !mem.isAlignedGeneric(u32, decl.link.coff.text_offset, required_alignment);
if (need_realloc) {
const curr_vaddr = self.getDeclVAddr(decl);
const vaddr = try self.growTextBlock(&decl.link.coff, code.len, required_alignment);
@@ -679,7 +683,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
}
} else {
const vaddr = try self.allocateTextBlock(&decl.link.coff, code.len, required_alignment);
- log.debug("allocated text block for {} at 0x{x} (size: {Bi})\n", .{ std.mem.spanZ(decl.name), vaddr, code.len });
+ log.debug("allocated text block for {} at 0x{x} (size: {Bi})\n", .{ mem.spanZ(decl.name), vaddr, code.len });
errdefer self.freeTextBlock(&decl.link.coff);
self.offset_table.items[decl.link.coff.offset_table_index] = vaddr;
try self.writeOffsetTableEntry(decl.link.coff.offset_table_index);
@@ -702,7 +706,7 @@ pub fn freeDecl(self: *Coff, decl: *Module.Decl) void {
pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, exports: []const *Module.Export) !void {
for (exports) |exp| {
if (exp.options.section) |section_name| {
- if (!std.mem.eql(u8, section_name, ".text")) {
+ if (!mem.eql(u8, section_name, ".text")) {
try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
module.failed_exports.putAssumeCapacityNoClobber(
exp,
@@ -711,7 +715,7 @@ pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl,
continue;
}
}
- if (std.mem.eql(u8, exp.options.name, "_start")) {
+ if (mem.eql(u8, exp.options.name, "_start")) {
self.entry_addr = decl.link.coff.getVAddr(self.*) - image_base;
} else {
try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
@@ -726,8 +730,12 @@ pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl,
pub fn flush(self: *Coff, comp: *Compilation) !void {
if (build_options.have_llvm and self.base.options.use_lld) {
- return error.CoffLinkingWithLLDUnimplemented;
+ return self.linkWithLLD(comp);
} else {
+ switch (self.base.options.effectiveOutputMode()) {
+ .Exe, .Obj => {},
+ .Lib => return error.TODOImplementWritingLibFiles,
+ }
return self.flushModule(comp);
}
}
@@ -739,16 +747,16 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void {
if (self.text_section_size_dirty) {
// Write the new raw size in the .text header
var buf: [4]u8 = undefined;
- std.mem.writeIntLittle(u32, &buf, self.text_section_size);
+ mem.writeIntLittle(u32, &buf, self.text_section_size);
try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 16);
try self.base.file.?.setEndPos(self.section_data_offset + self.offset_table_size + self.text_section_size);
self.text_section_size_dirty = false;
}
if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) {
- const new_size_of_image = std.mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + self.text_section_size, section_alignment);
+ const new_size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + self.text_section_size, section_alignment);
var buf: [4]u8 = undefined;
- std.mem.writeIntLittle(u32, &buf, new_size_of_image);
+ mem.writeIntLittle(u32, &buf, new_size_of_image);
try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56);
self.size_of_image_dirty = false;
}
@@ -763,12 +771,422 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void {
if (self.base.options.output_mode == .Exe) {
// Write AddressOfEntryPoint
var buf: [4]u8 = undefined;
- std.mem.writeIntLittle(u32, &buf, self.entry_addr.?);
+ mem.writeIntLittle(u32, &buf, self.entry_addr.?);
try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 16);
}
}
}
+fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
+
+ // If there is no Zig code to compile, then we should skip flushing the output file because it
+ // will not be part of the linker line anyway.
+ const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
+ const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm;
+ if (use_stage1) {
+ const obj_basename = try std.zig.binNameAlloc(arena, .{
+ .root_name = self.base.options.root_name,
+ .target = self.base.options.target,
+ .output_mode = .Obj,
+ });
+ const o_directory = self.base.options.module.?.zig_cache_artifact_directory;
+ const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename});
+ break :blk full_obj_path;
+ }
+
+ try self.flushModule(comp);
+ const obj_basename = self.base.intermediary_basename.?;
+ const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});
+ break :blk full_obj_path;
+ } else null;
+
+ const is_lib = self.base.options.output_mode == .Lib;
+ const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
+ const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
+ const link_in_crt = self.base.options.link_libc and self.base.options.output_mode == .Exe;
+ const target = self.base.options.target;
+
+ // See link/Elf.zig for comments on how this mechanism works.
+ const id_symlink_basename = "lld.id";
+
+ var man: Cache.Manifest = undefined;
+ defer if (!self.base.options.disable_lld_caching) man.deinit();
+
+ var digest: [Cache.hex_digest_len]u8 = undefined;
+
+ if (!self.base.options.disable_lld_caching) {
+ man = comp.cache_parent.obtain();
+ self.base.releaseLock();
+
+ try man.addListOfFiles(self.base.options.objects);
+ for (comp.c_object_table.items()) |entry| {
+ _ = try man.addFile(entry.key.status.success.object_path, null);
+ }
+ try man.addOptionalFile(module_obj_path);
+ man.hash.addOptional(self.base.options.stack_size_override);
+ man.hash.addListOfBytes(self.base.options.extra_lld_args);
+ man.hash.addListOfBytes(self.base.options.lib_dirs);
+ man.hash.add(self.base.options.is_compiler_rt_or_libc);
+ if (self.base.options.link_libc) {
+ man.hash.add(self.base.options.libc_installation != null);
+ if (self.base.options.libc_installation) |libc_installation| {
+ man.hash.addBytes(libc_installation.crt_dir.?);
+ if (target.abi == .msvc) {
+ man.hash.addBytes(libc_installation.msvc_lib_dir.?);
+ man.hash.addBytes(libc_installation.kernel32_lib_dir.?);
+ }
+ }
+ }
+ man.hash.addStringSet(self.base.options.system_libs);
+ man.hash.addOptional(self.base.options.subsystem);
+ man.hash.add(self.base.options.is_test);
+
+ // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
+ _ = try man.hit();
+ digest = man.final();
+ var prev_digest_buf: [digest.len]u8 = undefined;
+ const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
+ log.debug("COFF LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) });
+ // Handle this as a cache miss.
+ break :blk prev_digest_buf[0..0];
+ };
+ if (mem.eql(u8, prev_digest, &digest)) {
+ log.debug("COFF LLD digest={} match - skipping invocation", .{digest});
+ // Hot diggity dog! The output binary is already there.
+ self.base.lock = man.toOwnedLock();
+ return;
+ }
+ log.debug("COFF LLD prev_digest={} new_digest={}", .{ prev_digest, digest });
+
+ // We are about to change the output file to be different, so we invalidate the build hash now.
+ directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
+ error.FileNotFound => {},
+ else => |e| return e,
+ };
+ }
+
+ const is_obj = self.base.options.output_mode == .Obj;
+
+ // Create an LLD command line and invoke it.
+ var argv = std.ArrayList([]const u8).init(self.base.allocator);
+ defer argv.deinit();
+ // Even though we're calling LLD as a library it thinks the first argument is its own exe name.
+ try argv.append("lld");
+ if (is_obj) {
+ try argv.append("-r");
+ }
+
+ try argv.append("-ERRORLIMIT:0");
+ try argv.append("-NOLOGO");
+ if (!self.base.options.strip) {
+ try argv.append("-DEBUG");
+ }
+ if (self.base.options.output_mode == .Exe) {
+ const stack_size = self.base.options.stack_size_override orelse 16777216;
+ try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size}));
+ }
+
+ if (target.cpu.arch == .i386) {
+ try argv.append("-MACHINE:X86");
+ } else if (target.cpu.arch == .x86_64) {
+ try argv.append("-MACHINE:X64");
+ } else if (target.cpu.arch.isARM()) {
+ if (target.cpu.arch.ptrBitWidth() == 32) {
+ try argv.append("-MACHINE:ARM");
+ } else {
+ try argv.append("-MACHINE:ARM64");
+ }
+ }
+
+ if (is_dyn_lib) {
+ try argv.append("-DLL");
+ }
+
+ const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
+ try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path}));
+
+ if (self.base.options.link_libc) {
+ if (self.base.options.libc_installation) |libc_installation| {
+ try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?}));
+
+ if (target.abi == .msvc) {
+ try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?}));
+ try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?}));
+ }
+ }
+ }
+
+ for (self.base.options.lib_dirs) |lib_dir| {
+ try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir}));
+ }
+
+ try argv.appendSlice(self.base.options.objects);
+
+ for (comp.c_object_table.items()) |entry| {
+ try argv.append(entry.key.status.success.object_path);
+ }
+
+ if (module_obj_path) |p| {
+ try argv.append(p);
+ }
+
+ const resolved_subsystem: ?std.Target.SubSystem = blk: {
+ if (self.base.options.subsystem) |explicit| break :blk explicit;
+ switch (target.os.tag) {
+ .windows => {
+ if (self.base.options.module) |module| {
+ if (module.have_dllmain_crt_startup or is_dyn_lib)
+ break :blk null;
+ if (module.have_c_main or self.base.options.is_test or
+ module.have_winmain_crt_startup or module.have_wwinmain_crt_startup)
+ {
+ break :blk .Console;
+ }
+ if (module.have_winmain or module.have_wwinmain)
+ break :blk .Windows;
+ }
+ },
+ .uefi => break :blk .EfiApplication,
+ else => {},
+ }
+ break :blk null;
+ };
+ const Mode = enum { uefi, win32 };
+ const mode: Mode = mode: {
+ if (resolved_subsystem) |subsystem| switch (subsystem) {
+ .Console => {
+ try argv.append("-SUBSYSTEM:console");
+ break :mode .win32;
+ },
+ .EfiApplication => {
+ try argv.append("-SUBSYSTEM:efi_application");
+ break :mode .uefi;
+ },
+ .EfiBootServiceDriver => {
+ try argv.append("-SUBSYSTEM:efi_boot_service_driver");
+ break :mode .uefi;
+ },
+ .EfiRom => {
+ try argv.append("-SUBSYSTEM:efi_rom");
+ break :mode .uefi;
+ },
+ .EfiRuntimeDriver => {
+ try argv.append("-SUBSYSTEM:efi_runtime_driver");
+ break :mode .uefi;
+ },
+ .Native => {
+ try argv.append("-SUBSYSTEM:native");
+ break :mode .win32;
+ },
+ .Posix => {
+ try argv.append("-SUBSYSTEM:posix");
+ break :mode .win32;
+ },
+ .Windows => {
+ try argv.append("-SUBSYSTEM:windows");
+ break :mode .win32;
+ },
+ } else if (target.os.tag == .uefi) {
+ break :mode .uefi;
+ } else {
+ break :mode .win32;
+ }
+ };
+
+ switch (mode) {
+ .uefi => try argv.appendSlice(&[_][]const u8{
+ "-BASE:0",
+ "-ENTRY:EfiMain",
+ "-OPT:REF",
+ "-SAFESEH:NO",
+ "-MERGE:.rdata=.data",
+ "-ALIGN:32",
+ "-NODEFAULTLIB",
+ "-SECTION:.xdata,D",
+ }),
+ .win32 => {
+ if (link_in_crt) {
+ if (target.abi.isGnu()) {
+ try argv.append("-lldmingw");
+
+ if (target.cpu.arch == .i386) {
+ try argv.append("-ALTERNATENAME:__image_base__=___ImageBase");
+ } else {
+ try argv.append("-ALTERNATENAME:__image_base__=__ImageBase");
+ }
+
+ if (is_dyn_lib) {
+ try argv.append(try comp.get_libc_crt_file(arena, "dllcrt2.o"));
+ } else {
+ try argv.append(try comp.get_libc_crt_file(arena, "crt2.o"));
+ }
+
+ try argv.append(try comp.get_libc_crt_file(arena, "mingw32.lib"));
+ try argv.append(try comp.get_libc_crt_file(arena, "mingwex.lib"));
+ try argv.append(try comp.get_libc_crt_file(arena, "msvcrt-os.lib"));
+
+ for (mingw.always_link_libs) |name| {
+ if (!self.base.options.system_libs.contains(name)) {
+ const lib_basename = try allocPrint(arena, "{s}.lib", .{name});
+ try argv.append(try comp.get_libc_crt_file(arena, lib_basename));
+ }
+ }
+ } else {
+ const lib_str = switch (self.base.options.link_mode) {
+ .Dynamic => "",
+ .Static => "lib",
+ };
+ const d_str = switch (self.base.options.optimize_mode) {
+ .Debug => "d",
+ else => "",
+ };
+ switch (self.base.options.link_mode) {
+ .Static => try argv.append(try allocPrint(arena, "libcmt{s}.lib", .{d_str})),
+ .Dynamic => try argv.append(try allocPrint(arena, "msvcrt{s}.lib", .{d_str})),
+ }
+
+ try argv.append(try allocPrint(arena, "{s}vcruntime{s}.lib", .{ lib_str, d_str }));
+ try argv.append(try allocPrint(arena, "{s}ucrt{s}.lib", .{ lib_str, d_str }));
+
+ //Visual C++ 2015 Conformance Changes
+ //https://msdn.microsoft.com/en-us/library/bb531344.aspx
+ try argv.append("legacy_stdio_definitions.lib");
+
+ // msvcrt depends on kernel32 and ntdll
+ try argv.append("kernel32.lib");
+ try argv.append("ntdll.lib");
+ }
+ } else {
+ try argv.append("-NODEFAULTLIB");
+ if (!is_lib) {
+ if (self.base.options.module) |module| {
+ if (module.have_winmain) {
+ try argv.append("-ENTRY:WinMain");
+ } else if (module.have_wwinmain) {
+ try argv.append("-ENTRY:wWinMain");
+ } else if (module.have_wwinmain_crt_startup) {
+ try argv.append("-ENTRY:wWinMainCRTStartup");
+ } else {
+ try argv.append("-ENTRY:WinMainCRTStartup");
+ }
+ } else {
+ try argv.append("-ENTRY:WinMainCRTStartup");
+ }
+ }
+ }
+ },
+ }
+
+ if (!is_obj) {
+ // libc++ dep
+ if (self.base.options.link_libcpp) {
+ try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
+ try argv.append(comp.libcxx_static_lib.?.full_object_path);
+ try argv.append(comp.libunwind_static_lib.?.full_object_path);
+ }
+ }
+
+ // compiler-rt and libc
+ if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) {
+ if (!self.base.options.link_libc) {
+ try argv.append(comp.libc_static_lib.?.full_object_path);
+ }
+ // MSVC compiler_rt is missing some stuff, so we build it unconditionally but
+ // and rely on weak linkage to allow MSVC compiler_rt functions to override ours.
+ try argv.append(comp.compiler_rt_static_lib.?.full_object_path);
+ }
+
+ for (self.base.options.system_libs.items()) |entry| {
+ const lib_basename = try allocPrint(arena, "{s}.lib", .{entry.key});
+ if (comp.crt_files.get(lib_basename)) |crt_file| {
+ try argv.append(crt_file.full_object_path);
+ } else {
+ try argv.append(lib_basename);
+ }
+ }
+
+ if (self.base.options.verbose_link) {
+ Compilation.dump_argv(argv.items);
+ }
+
+ const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, argv.items.len + 1);
+ new_argv_with_sentinel[argv.items.len] = null;
+ const new_argv = new_argv_with_sentinel[0..argv.items.len :null];
+ for (argv.items) |arg, i| {
+ new_argv[i] = try arena.dupeZ(u8, arg);
+ }
+
+ var stderr_context: LLDContext = .{
+ .coff = self,
+ .data = std.ArrayList(u8).init(self.base.allocator),
+ };
+ defer stderr_context.data.deinit();
+ var stdout_context: LLDContext = .{
+ .coff = self,
+ .data = std.ArrayList(u8).init(self.base.allocator),
+ };
+ defer stdout_context.data.deinit();
+ const llvm = @import("../llvm.zig");
+ const ok = llvm.Link(
+ .COFF,
+ new_argv.ptr,
+ new_argv.len,
+ append_diagnostic,
+ @ptrToInt(&stdout_context),
+ @ptrToInt(&stderr_context),
+ );
+ if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory;
+ if (stdout_context.data.items.len != 0) {
+ std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items});
+ }
+ if (!ok) {
+ // TODO parse this output and surface with the Compilation API rather than
+ // directly outputting to stderr here.
+ std.debug.print("{}", .{stderr_context.data.items});
+ return error.LLDReportedFailure;
+ }
+ if (stderr_context.data.items.len != 0) {
+ std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items});
+ }
+
+ if (!self.base.options.disable_lld_caching) {
+ // Update the dangling symlink with the digest. If it fails we can continue; it only
+ // means that the next invocation will have an unnecessary cache miss.
+ directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
+ std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});
+ };
+ // Again failure here only means an unnecessary cache miss.
+ man.writeManifest() catch |err| {
+ std.log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)});
+ };
+ // We hang on to this lock so that the output file path can be used without
+ // other processes clobbering it.
+ self.base.lock = man.toOwnedLock();
+ }
+}
+
+const LLDContext = struct {
+ data: std.ArrayList(u8),
+ coff: *Coff,
+ oom: bool = false,
+};
+
+fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
+ const lld_context = @intToPtr(*LLDContext, context);
+ const msg = ptr[0..len];
+ lld_context.data.appendSlice(msg) catch |err| switch (err) {
+ error.OutOfMemory => lld_context.oom = true,
+ };
+}
+
pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 {
return self.text_section_virtual_address + decl.link.coff.text_offset;
}