mirror of
https://github.com/ziglang/zig.git
synced 2025-12-24 07:03:11 +00:00
Start working on PE/COFF linking.
This commit is contained in:
parent
88724b2a89
commit
fac9a4e286
@ -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 = {} },
|
||||
|
||||
@ -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");
|
||||
|
||||
231
src-self-hosted/link/Coff.zig
Normal file
231
src-self-hosted/link/Coff.zig
Normal file
@ -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
|
||||
}
|
||||
BIN
src-self-hosted/link/msdos-stub.bin
Normal file
BIN
src-self-hosted/link/msdos-stub.bin
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user