Commit fac9a4e286
Changed files (4)
src-self-hosted
src-self-hosted/link/Coff.zig
@@ -0,0 +1,231 @@
+const Coff = @This();
+
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const assert = std.debug.assert;
+const fs = std.fs;
+
+const Module = @import("../Module.zig");
+const codegen = @import("../codegen/wasm.zig");
+const link = @import("../link.zig");
+
+
+pub const base_tag: link.File.Tag = .coff;
+
+const msdos_stub = @embedFile("msdos-stub.bin");
+const coff_file_header_offset = msdos_stub.len + 4;
+const optional_header_offset = coff_file_header_offset + 20;
+
+base: link.File,
+ptr_width: enum { p32, p64 },
+error_flags: link.File.ErrorFlags = .{},
+
+coff_file_header_dirty: bool = false,
+optional_header_dirty: bool = false,
+
+pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File {
+ assert(options.object_format == .coff);
+
+ const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) });
+ errdefer file.close();
+
+ var coff_file = try allocator.create(Coff);
+ errdefer allocator.destroy(coff_file);
+
+ coff_file.* = openFile(allocator, file, options) catch |err| switch (err) {
+ error.IncrFailed => try createFile(allocator, file, options),
+ else => |e| return e,
+ };
+
+ return &coff_file.base;
+}
+
+/// Returns error.IncrFailed if incremental update could not be performed.
+fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff {
+ switch (options.output_mode) {
+ .Exe => {},
+ .Obj => return error.IncrFailed, // @TODO DO OBJ FILES
+ .Lib => return error.IncrFailed,
+ }
+ var self: Coff = .{
+ .base = .{
+ .file = file,
+ .tag = .coff,
+ .options = options,
+ .allocator = allocator,
+ },
+ .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
+ 32 => .p32,
+ 64 => .p64,
+ else => return error.UnsupportedELFArchitecture,
+ },
+ };
+ errdefer self.deinit();
+
+ // TODO implement reading the PE/COFF file
+ return error.IncrFailed;
+}
+
+/// Truncates the existing file contents and overwrites the contents.
+/// Returns an error if `file` is not already open with +read +write +seek abilities.
+fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff {
+ switch (options.output_mode) {
+ .Exe => {},
+ .Obj => return error.TODOImplementWritingObjFiles, // @TODO DO OBJ FILES
+ .Lib => return error.TODOImplementWritingLibFiles,
+ }
+ var self: Coff = .{
+ .base = .{
+ .tag = .coff,
+ .options = options,
+ .allocator = allocator,
+ .file = file,
+ },
+ .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
+ 32 => .p32,
+ 64 => .p64,
+ else => return error.UnsupportedCOFFArchitecture,
+ },
+ .coff_file_header_dirty = true,
+ .optional_header_dirty = true,
+ };
+ errdefer self.deinit();
+
+ var output = self.base.file.?.writer();
+
+ // MS-DOS stub + PE magic
+ try output.writeAll(msdos_stub ++ "PE\x00\x00");
+ const machine_type: u16 = switch (self.base.options.target.cpu.arch) {
+ .x86_64 => 0x8664,
+ .i386 => 0x014c,
+ .riscv32 => 0x5032,
+ .riscv64 => 0x5064,
+ else => return error.UnsupportedCOFFArchitecture,
+ };
+
+ // Start of COFF file header
+ try output.writeIntLittle(u16, machine_type);
+ try output.writeIntLittle(u16, switch (self.ptr_width) {
+ .p32 => @as(u16, 98),
+ .p64 => 114,
+ });
+ try output.writeAll("\x00" ** 14);
+ // Characteristics - IMAGE_FILE_RELOCS_STRIPPED | IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DEBUG_STRIPPED
+ var characteristics: u16 = 0x0001 | 0x000 | 0x02002; // @TODO Remove debug info stripped flag when necessary
+ switch (self.ptr_width) {
+ // IMAGE_FILE_32BIT_MACHINE
+ .p32 => characteristics |= 0x0100,
+ // IMAGE_FILE_LARGE_ADDRESS_AWARE
+ .p64 => characteristics |= 0x0020,
+ }
+ try output.writeIntLittle(u16, characteristics);
+ try output.writeIntLittle(u16, switch (self.ptr_width) {
+ .p32 => @as(u16, 0x10b),
+ .p64 => 0x20b,
+ });
+
+ // Start of optional header
+ // TODO Linker version, use 0.0 for now.
+ try output.writeAll("\x00" ** 2);
+ // Zero out every field until "BaseOfCode"
+ // @TODO Actually write entry point address, base of code address
+ try output.writeAll("\x00" ** 20);
+ switch (self.ptr_width) {
+ .p32 => {
+ // Zero out base of data
+ try output.writeAll("\x00" ** 4);
+ // Write image base
+ try output.writeIntLittle(u32, 0x40000000);
+ },
+ .p64 => {
+ // Write image base
+ try output.writeIntLittle(u64, 0x40000000);
+ },
+ }
+
+ // Section alignment - default to 256
+ try output.writeIntLittle(u32, 256);
+ // File alignment - default to 512
+ try output.writeIntLittle(u32, 512);
+ // TODO - Minimum required windows version - use 6.0 (aka vista for now)
+ try output.writeIntLittle(u16, 0x6);
+ try output.writeIntLittle(u16, 0x0);
+ // TODO - Image version - use 0.0 for now
+ try output.writeIntLittle(u32, 0x0);
+ // Subsystem version
+ try output.writeIntLittle(u16, 0x6);
+ try output.writeIntLittle(u16, 0x0);
+ // Reserved zeroes
+ try output.writeIntLittle(u32, 0x0);
+ // Size of image - initialize to zero
+ try output.writeIntLittle(u32, 0x0);
+ // @TODO Size of headers - calculate this.
+ try output.writeIntLittle(u32, 0x0);
+ // Checksum
+ try output.writeIntLittle(u32, 0x0);
+ // Subsystem
+ try output.writeIntLittle(u16, 0x3);
+ // @TODO Dll characteristics, just using a value from a LLVM produced executable for now.
+ try output.writeIntLittle(u16, 0x8160);
+ switch (self.ptr_width) {
+ .p32 => {
+ // Stack reserve
+ try output.writeIntLittle(u32, 0x1000000);
+ // Stack commit
+ try output.writeIntLittle(u32, 0x1000);
+ // Heap reserve
+ try output.writeIntLittle(u32, 0x100000);
+ // Heap commit
+ try output.writeIntLittle(u32, 0x100);
+ },
+ .p64 => {
+ // Stack reserve
+ try output.writeIntLittle(u64, 0x1000000);
+ // Stack commit
+ try output.writeIntLittle(u64, 0x1000);
+ // Heap reserve
+ try output.writeIntLittle(u64, 0x100000);
+ // Heap commit
+ try output.writeIntLittle(u64, 0x100);
+ },
+ }
+ // Reserved loader flags
+ try output.writeIntLittle(u32, 0x0);
+ // Number of RVA + sizes
+ try output.writeIntLittle(u32, 0x0);
+
+ return self;
+}
+
+pub fn flush(self: *Coff, module: *Module) !void {
+ // @TODO Implement this
+}
+
+pub fn freeDecl(self: *Coff, decl: *Module.Decl) void {
+ // @TODO Implement this
+}
+
+pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
+ // @TODO Implement this
+}
+
+pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {
+ // @TODO Implement this
+}
+
+pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void {
+ // @TODO Implement this
+}
+
+pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, exports: []const *Module.Export) !void {
+ // @TODO Implement this
+}
+
+pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 {
+ // @TODO Implement this
+ return 0;
+}
+
+pub fn deinit(self: *Coff) void {
+ // @TODO
+}
src-self-hosted/link/msdos-stub.bin
Binary file
src-self-hosted/link.zig
@@ -34,6 +34,7 @@ pub const File = struct {
pub const LinkBlock = union {
elf: Elf.TextBlock,
+ coff: void, // @TODO
macho: MachO.TextBlock,
c: void,
wasm: void,
@@ -41,6 +42,7 @@ pub const File = struct {
pub const LinkFn = union {
elf: Elf.SrcFn,
+ coff: void, // @TODO
macho: MachO.SrcFn,
c: void,
wasm: ?Wasm.FnData,
@@ -66,7 +68,7 @@ pub const File = struct {
pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
switch (options.object_format) {
.unknown => unreachable,
- .coff => return error.TODOImplementCoff,
+ .coff => return Coff.openPath(allocator, dir, sub_path, options),
.elf => return Elf.openPath(allocator, dir, sub_path, options),
.macho => return MachO.openPath(allocator, dir, sub_path, options),
.wasm => return Wasm.openPath(allocator, dir, sub_path, options),
@@ -85,7 +87,7 @@ pub const File = struct {
pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {
switch (base.tag) {
- .elf, .macho => {
+ .coff, .elf, .macho => {
if (base.file != null) return;
base.file = try dir.createFile(sub_path, .{
.truncate = false,
@@ -112,6 +114,7 @@ pub const File = struct {
/// after allocateDeclIndexes for any given Decl.
pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
switch (base.tag) {
+ .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl),
.elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
.macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),
.c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
@@ -121,6 +124,7 @@ pub const File = struct {
pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void {
switch (base.tag) {
+ .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),
.elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
.macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
.c, .wasm => {},
@@ -131,6 +135,7 @@ pub const File = struct {
/// any given Decl.
pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
switch (base.tag) {
+ .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),
.elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
.macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
.c, .wasm => {},
@@ -140,6 +145,7 @@ pub const File = struct {
pub fn deinit(base: *File) void {
if (base.file) |f| f.close();
switch (base.tag) {
+ .coff => @fieldParentPtr(Coff, "base", base).deinit(),
.elf => @fieldParentPtr(Elf, "base", base).deinit(),
.macho => @fieldParentPtr(MachO, "base", base).deinit(),
.c => @fieldParentPtr(C, "base", base).deinit(),
@@ -149,6 +155,11 @@ pub const File = struct {
pub fn destroy(base: *File) void {
switch (base.tag) {
+ .coff => {
+ const parent = @fieldParentPtr(Coff, "base", base);
+ parent.deinit();
+ base.allocator.destroy(parent);
+ },
.elf => {
const parent = @fieldParentPtr(Elf, "base", base);
parent.deinit();
@@ -177,6 +188,7 @@ pub const File = struct {
defer tracy.end();
try switch (base.tag) {
+ .coff => @fieldParentPtr(Coff, "base", base).flush(module),
.elf => @fieldParentPtr(Elf, "base", base).flush(module),
.macho => @fieldParentPtr(MachO, "base", base).flush(module),
.c => @fieldParentPtr(C, "base", base).flush(module),
@@ -186,6 +198,7 @@ pub const File = struct {
pub fn freeDecl(base: *File, decl: *Module.Decl) void {
switch (base.tag) {
+ .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl),
.elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
.macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),
.c => unreachable,
@@ -195,6 +208,7 @@ pub const File = struct {
pub fn errorFlags(base: *File) ErrorFlags {
return switch (base.tag) {
+ .coff => @fieldParentPtr(Coff, "base", base).error_flags,
.elf => @fieldParentPtr(Elf, "base", base).error_flags,
.macho => @fieldParentPtr(MachO, "base", base).error_flags,
.c => return .{ .no_entry_point_found = false },
@@ -211,6 +225,7 @@ pub const File = struct {
exports: []const *Module.Export,
) !void {
switch (base.tag) {
+ .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports),
.elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
.macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),
.c => return {},
@@ -220,6 +235,7 @@ pub const File = struct {
pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 {
switch (base.tag) {
+ .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl),
.elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl),
.macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl),
.c => unreachable,
@@ -228,6 +244,7 @@ pub const File = struct {
}
pub const Tag = enum {
+ coff,
elf,
macho,
c,
@@ -239,6 +256,7 @@ pub const File = struct {
};
pub const C = @import("link/C.zig");
+ pub const Coff = @import("link/Coff.zig");
pub const Elf = @import("link/Elf.zig");
pub const MachO = @import("link/MachO.zig");
pub const Wasm = @import("link/Wasm.zig");
src-self-hosted/Module.zig
@@ -1820,6 +1820,9 @@ fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
try self.markOutdatedDecl(decl);
decl.contents_hash = contents_hash;
} else switch (self.bin_file.tag) {
+ .coff => {
+ // TODO Implement for COFF
+ },
.elf => if (decl.fn_link.elf.len != 0) {
// TODO Look into detecting when this would be unnecessary by storing enough state
// in `Decl` to notice that the line number did not change.
@@ -2078,12 +2081,14 @@ fn allocateNewDecl(
.deletion_flag = false,
.contents_hash = contents_hash,
.link = switch (self.bin_file.tag) {
+ .coff => .{ .coff = {} }, // @TODO
.elf => .{ .elf = link.File.Elf.TextBlock.empty },
.macho => .{ .macho = link.File.MachO.TextBlock.empty },
.c => .{ .c = {} },
.wasm => .{ .wasm = {} },
},
.fn_link = switch (self.bin_file.tag) {
+ .coff => .{ .coff = {} }, // @TODO
.elf => .{ .elf = link.File.Elf.SrcFn.empty },
.macho => .{ .macho = link.File.MachO.SrcFn.empty },
.c => .{ .c = {} },