authorgravatar for alex_naskos@hotmail.comAlexandros Naskos <alex_naskos@hotmail.com> 2020-08-27 13:28:54+03:00
committergravatar for alex_naskos@hotmail.comAlexandros Naskos <alex_naskos@hotmail.com> 2020-09-04 05:12:26+03:00
logfac9a4e286e90c5d5574422a17d28e85c55ae67d
treea05e58c7e5d2b75ffb8d89a52ca6bd38ccef8e3a
parent88724b2a89157ecc3a8eea03aa0f8a6b66829915

Start working on PE/COFF linking.


4 files changed, 256 insertions(+), 2 deletions(-)

src-self-hosted/Module.zig+5
......@@ -1820,6 +1820,9 @@ fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
18201820 try self.markOutdatedDecl(decl);
18211821 decl.contents_hash = contents_hash;
18221822 } else switch (self.bin_file.tag) {
1823 .coff => {
1824 // TODO Implement for COFF
1825 },
18231826 .elf => if (decl.fn_link.elf.len != 0) {
18241827 // TODO Look into detecting when this would be unnecessary by storing enough state
18251828 // in `Decl` to notice that the line number did not change.
......@@ -2078,12 +2081,14 @@ fn allocateNewDecl(
20782081 .deletion_flag = false,
20792082 .contents_hash = contents_hash,
20802083 .link = switch (self.bin_file.tag) {
2084 .coff => .{ .coff = {} }, // @TODO
20812085 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
20822086 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
20832087 .c => .{ .c = {} },
20842088 .wasm => .{ .wasm = {} },
20852089 },
20862090 .fn_link = switch (self.bin_file.tag) {
2091 .coff => .{ .coff = {} }, // @TODO
20872092 .elf => .{ .elf = link.File.Elf.SrcFn.empty },
20882093 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
20892094 .c => .{ .c = {} },
src-self-hosted/link.zig+20-2
......@@ -34,6 +34,7 @@ pub const File = struct {
3434
3535 pub const LinkBlock = union {
3636 elf: Elf.TextBlock,
37 coff: void, // @TODO
3738 macho: MachO.TextBlock,
3839 c: void,
3940 wasm: void,
......@@ -41,6 +42,7 @@ pub const File = struct {
4142
4243 pub const LinkFn = union {
4344 elf: Elf.SrcFn,
45 coff: void, // @TODO
4446 macho: MachO.SrcFn,
4547 c: void,
4648 wasm: ?Wasm.FnData,
......@@ -66,7 +68,7 @@ pub const File = struct {
6668 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
6769 switch (options.object_format) {
6870 .unknown => unreachable,
69 .coff => return error.TODOImplementCoff,
71 .coff => return Coff.openPath(allocator, dir, sub_path, options),
7072 .elf => return Elf.openPath(allocator, dir, sub_path, options),
7173 .macho => return MachO.openPath(allocator, dir, sub_path, options),
7274 .wasm => return Wasm.openPath(allocator, dir, sub_path, options),
......@@ -85,7 +87,7 @@ pub const File = struct {
8587
8688 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {
8789 switch (base.tag) {
88 .elf, .macho => {
90 .coff, .elf, .macho => {
8991 if (base.file != null) return;
9092 base.file = try dir.createFile(sub_path, .{
9193 .truncate = false,
......@@ -112,6 +114,7 @@ pub const File = struct {
112114 /// after allocateDeclIndexes for any given Decl.
113115 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
114116 switch (base.tag) {
117 .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl),
115118 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
116119 .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),
117120 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
......@@ -121,6 +124,7 @@ pub const File = struct {
121124
122125 pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void {
123126 switch (base.tag) {
127 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),
124128 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
125129 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
126130 .c, .wasm => {},
......@@ -131,6 +135,7 @@ pub const File = struct {
131135 /// any given Decl.
132136 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
133137 switch (base.tag) {
138 .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),
134139 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
135140 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
136141 .c, .wasm => {},
......@@ -140,6 +145,7 @@ pub const File = struct {
140145 pub fn deinit(base: *File) void {
141146 if (base.file) |f| f.close();
142147 switch (base.tag) {
148 .coff => @fieldParentPtr(Coff, "base", base).deinit(),
143149 .elf => @fieldParentPtr(Elf, "base", base).deinit(),
144150 .macho => @fieldParentPtr(MachO, "base", base).deinit(),
145151 .c => @fieldParentPtr(C, "base", base).deinit(),
......@@ -149,6 +155,11 @@ pub const File = struct {
149155
150156 pub fn destroy(base: *File) void {
151157 switch (base.tag) {
158 .coff => {
159 const parent = @fieldParentPtr(Coff, "base", base);
160 parent.deinit();
161 base.allocator.destroy(parent);
162 },
152163 .elf => {
153164 const parent = @fieldParentPtr(Elf, "base", base);
154165 parent.deinit();
......@@ -177,6 +188,7 @@ pub const File = struct {
177188 defer tracy.end();
178189
179190 try switch (base.tag) {
191 .coff => @fieldParentPtr(Coff, "base", base).flush(module),
180192 .elf => @fieldParentPtr(Elf, "base", base).flush(module),
181193 .macho => @fieldParentPtr(MachO, "base", base).flush(module),
182194 .c => @fieldParentPtr(C, "base", base).flush(module),
......@@ -186,6 +198,7 @@ pub const File = struct {
186198
187199 pub fn freeDecl(base: *File, decl: *Module.Decl) void {
188200 switch (base.tag) {
201 .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl),
189202 .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
190203 .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),
191204 .c => unreachable,
......@@ -195,6 +208,7 @@ pub const File = struct {
195208
196209 pub fn errorFlags(base: *File) ErrorFlags {
197210 return switch (base.tag) {
211 .coff => @fieldParentPtr(Coff, "base", base).error_flags,
198212 .elf => @fieldParentPtr(Elf, "base", base).error_flags,
199213 .macho => @fieldParentPtr(MachO, "base", base).error_flags,
200214 .c => return .{ .no_entry_point_found = false },
......@@ -211,6 +225,7 @@ pub const File = struct {
211225 exports: []const *Module.Export,
212226 ) !void {
213227 switch (base.tag) {
228 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports),
214229 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
215230 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),
216231 .c => return {},
......@@ -220,6 +235,7 @@ pub const File = struct {
220235
221236 pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 {
222237 switch (base.tag) {
238 .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl),
223239 .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl),
224240 .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl),
225241 .c => unreachable,
......@@ -228,6 +244,7 @@ pub const File = struct {
228244 }
229245
230246 pub const Tag = enum {
247 coff,
231248 elf,
232249 macho,
233250 c,
......@@ -239,6 +256,7 @@ pub const File = struct {
239256 };
240257
241258 pub const C = @import("link/C.zig");
259 pub const Coff = @import("link/Coff.zig");
242260 pub const Elf = @import("link/Elf.zig");
243261 pub const MachO = @import("link/MachO.zig");
244262 pub const Wasm = @import("link/Wasm.zig");
src-self-hosted/link/Coff.zig created+231
......@@ -0,0 +1,231 @@
1const Coff = @This();
2
3const std = @import("std");
4const Allocator = std.mem.Allocator;
5const assert = std.debug.assert;
6const fs = std.fs;
7
8const Module = @import("../Module.zig");
9const codegen = @import("../codegen/wasm.zig");
10const link = @import("../link.zig");
11
12
13pub const base_tag: link.File.Tag = .coff;
14
15const msdos_stub = @embedFile("msdos-stub.bin");
16const coff_file_header_offset = msdos_stub.len + 4;
17const optional_header_offset = coff_file_header_offset + 20;
18
19base: link.File,
20ptr_width: enum { p32, p64 },
21error_flags: link.File.ErrorFlags = .{},
22
23coff_file_header_dirty: bool = false,
24optional_header_dirty: bool = false,
25
26pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File {
27 assert(options.object_format == .coff);
28
29 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) });
30 errdefer file.close();
31
32 var coff_file = try allocator.create(Coff);
33 errdefer allocator.destroy(coff_file);
34
35 coff_file.* = openFile(allocator, file, options) catch |err| switch (err) {
36 error.IncrFailed => try createFile(allocator, file, options),
37 else => |e| return e,
38 };
39
40 return &coff_file.base;
41}
42
43/// Returns error.IncrFailed if incremental update could not be performed.
44fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff {
45 switch (options.output_mode) {
46 .Exe => {},
47 .Obj => return error.IncrFailed, // @TODO DO OBJ FILES
48 .Lib => return error.IncrFailed,
49 }
50 var self: Coff = .{
51 .base = .{
52 .file = file,
53 .tag = .coff,
54 .options = options,
55 .allocator = allocator,
56 },
57 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
58 32 => .p32,
59 64 => .p64,
60 else => return error.UnsupportedELFArchitecture,
61 },
62 };
63 errdefer self.deinit();
64
65 // TODO implement reading the PE/COFF file
66 return error.IncrFailed;
67}
68
69/// Truncates the existing file contents and overwrites the contents.
70/// Returns an error if `file` is not already open with +read +write +seek abilities.
71fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff {
72 switch (options.output_mode) {
73 .Exe => {},
74 .Obj => return error.TODOImplementWritingObjFiles, // @TODO DO OBJ FILES
75 .Lib => return error.TODOImplementWritingLibFiles,
76 }
77 var self: Coff = .{
78 .base = .{
79 .tag = .coff,
80 .options = options,
81 .allocator = allocator,
82 .file = file,
83 },
84 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
85 32 => .p32,
86 64 => .p64,
87 else => return error.UnsupportedCOFFArchitecture,
88 },
89 .coff_file_header_dirty = true,
90 .optional_header_dirty = true,
91 };
92 errdefer self.deinit();
93
94 var output = self.base.file.?.writer();
95
96 // MS-DOS stub + PE magic
97 try output.writeAll(msdos_stub ++ "PE\x00\x00");
98 const machine_type: u16 = switch (self.base.options.target.cpu.arch) {
99 .x86_64 => 0x8664,
100 .i386 => 0x014c,
101 .riscv32 => 0x5032,
102 .riscv64 => 0x5064,
103 else => return error.UnsupportedCOFFArchitecture,
104 };
105
106 // Start of COFF file header
107 try output.writeIntLittle(u16, machine_type);
108 try output.writeIntLittle(u16, switch (self.ptr_width) {
109 .p32 => @as(u16, 98),
110 .p64 => 114,
111 });
112 try output.writeAll("\x00" ** 14);
113 // Characteristics - IMAGE_FILE_RELOCS_STRIPPED | IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DEBUG_STRIPPED
114 var characteristics: u16 = 0x0001 | 0x000 | 0x02002; // @TODO Remove debug info stripped flag when necessary
115 switch (self.ptr_width) {
116 // IMAGE_FILE_32BIT_MACHINE
117 .p32 => characteristics |= 0x0100,
118 // IMAGE_FILE_LARGE_ADDRESS_AWARE
119 .p64 => characteristics |= 0x0020,
120 }
121 try output.writeIntLittle(u16, characteristics);
122 try output.writeIntLittle(u16, switch (self.ptr_width) {
123 .p32 => @as(u16, 0x10b),
124 .p64 => 0x20b,
125 });
126
127 // Start of optional header
128 // TODO Linker version, use 0.0 for now.
129 try output.writeAll("\x00" ** 2);
130 // Zero out every field until "BaseOfCode"
131 // @TODO Actually write entry point address, base of code address
132 try output.writeAll("\x00" ** 20);
133 switch (self.ptr_width) {
134 .p32 => {
135 // Zero out base of data
136 try output.writeAll("\x00" ** 4);
137 // Write image base
138 try output.writeIntLittle(u32, 0x40000000);
139 },
140 .p64 => {
141 // Write image base
142 try output.writeIntLittle(u64, 0x40000000);
143 },
144 }
145
146 // Section alignment - default to 256
147 try output.writeIntLittle(u32, 256);
148 // File alignment - default to 512
149 try output.writeIntLittle(u32, 512);
150 // TODO - Minimum required windows version - use 6.0 (aka vista for now)
151 try output.writeIntLittle(u16, 0x6);
152 try output.writeIntLittle(u16, 0x0);
153 // TODO - Image version - use 0.0 for now
154 try output.writeIntLittle(u32, 0x0);
155 // Subsystem version
156 try output.writeIntLittle(u16, 0x6);
157 try output.writeIntLittle(u16, 0x0);
158 // Reserved zeroes
159 try output.writeIntLittle(u32, 0x0);
160 // Size of image - initialize to zero
161 try output.writeIntLittle(u32, 0x0);
162 // @TODO Size of headers - calculate this.
163 try output.writeIntLittle(u32, 0x0);
164 // Checksum
165 try output.writeIntLittle(u32, 0x0);
166 // Subsystem
167 try output.writeIntLittle(u16, 0x3);
168 // @TODO Dll characteristics, just using a value from a LLVM produced executable for now.
169 try output.writeIntLittle(u16, 0x8160);
170 switch (self.ptr_width) {
171 .p32 => {
172 // Stack reserve
173 try output.writeIntLittle(u32, 0x1000000);
174 // Stack commit
175 try output.writeIntLittle(u32, 0x1000);
176 // Heap reserve
177 try output.writeIntLittle(u32, 0x100000);
178 // Heap commit
179 try output.writeIntLittle(u32, 0x100);
180 },
181 .p64 => {
182 // Stack reserve
183 try output.writeIntLittle(u64, 0x1000000);
184 // Stack commit
185 try output.writeIntLittle(u64, 0x1000);
186 // Heap reserve
187 try output.writeIntLittle(u64, 0x100000);
188 // Heap commit
189 try output.writeIntLittle(u64, 0x100);
190 },
191 }
192 // Reserved loader flags
193 try output.writeIntLittle(u32, 0x0);
194 // Number of RVA + sizes
195 try output.writeIntLittle(u32, 0x0);
196
197 return self;
198}
199
200pub fn flush(self: *Coff, module: *Module) !void {
201 // @TODO Implement this
202}
203
204pub fn freeDecl(self: *Coff, decl: *Module.Decl) void {
205 // @TODO Implement this
206}
207
208pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
209 // @TODO Implement this
210}
211
212pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {
213 // @TODO Implement this
214}
215
216pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void {
217 // @TODO Implement this
218}
219
220pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, exports: []const *Module.Export) !void {
221 // @TODO Implement this
222}
223
224pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 {
225 // @TODO Implement this
226 return 0;
227}
228
229pub fn deinit(self: *Coff) void {
230 // @TODO
231}
src-self-hosted/link/msdos-stub.bin created
Binary files /dev/null and b/src-self-hosted/link/msdos-stub.bin differ