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 {...@@ -1820,6 +1820,9 @@ fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
1820 try self.markOutdatedDecl(decl);1820 try self.markOutdatedDecl(decl);
1821 decl.contents_hash = contents_hash;1821 decl.contents_hash = contents_hash;
1822 } else switch (self.bin_file.tag) {1822 } else switch (self.bin_file.tag) {
1823 .coff => {
1824 // TODO Implement for COFF
1825 },
1823 .elf => if (decl.fn_link.elf.len != 0) {1826 .elf => if (decl.fn_link.elf.len != 0) {
1824 // TODO Look into detecting when this would be unnecessary by storing enough state1827 // TODO Look into detecting when this would be unnecessary by storing enough state
1825 // in `Decl` to notice that the line number did not change.1828 // in `Decl` to notice that the line number did not change.
...@@ -2078,12 +2081,14 @@ fn allocateNewDecl(...@@ -2078,12 +2081,14 @@ fn allocateNewDecl(
2078 .deletion_flag = false,2081 .deletion_flag = false,
2079 .contents_hash = contents_hash,2082 .contents_hash = contents_hash,
2080 .link = switch (self.bin_file.tag) {2083 .link = switch (self.bin_file.tag) {
2084 .coff => .{ .coff = {} }, // @TODO
2081 .elf => .{ .elf = link.File.Elf.TextBlock.empty },2085 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
2082 .macho => .{ .macho = link.File.MachO.TextBlock.empty },2086 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
2083 .c => .{ .c = {} },2087 .c => .{ .c = {} },
2084 .wasm => .{ .wasm = {} },2088 .wasm => .{ .wasm = {} },
2085 },2089 },
2086 .fn_link = switch (self.bin_file.tag) {2090 .fn_link = switch (self.bin_file.tag) {
2091 .coff => .{ .coff = {} }, // @TODO
2087 .elf => .{ .elf = link.File.Elf.SrcFn.empty },2092 .elf => .{ .elf = link.File.Elf.SrcFn.empty },
2088 .macho => .{ .macho = link.File.MachO.SrcFn.empty },2093 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
2089 .c => .{ .c = {} },2094 .c => .{ .c = {} },
src-self-hosted/link.zig+20-2
...@@ -34,6 +34,7 @@ pub const File = struct {...@@ -34,6 +34,7 @@ pub const File = struct {
3434
35 pub const LinkBlock = union {35 pub const LinkBlock = union {
36 elf: Elf.TextBlock,36 elf: Elf.TextBlock,
37 coff: void, // @TODO
37 macho: MachO.TextBlock,38 macho: MachO.TextBlock,
38 c: void,39 c: void,
39 wasm: void,40 wasm: void,
...@@ -41,6 +42,7 @@ pub const File = struct {...@@ -41,6 +42,7 @@ pub const File = struct {
4142
42 pub const LinkFn = union {43 pub const LinkFn = union {
43 elf: Elf.SrcFn,44 elf: Elf.SrcFn,
45 coff: void, // @TODO
44 macho: MachO.SrcFn,46 macho: MachO.SrcFn,
45 c: void,47 c: void,
46 wasm: ?Wasm.FnData,48 wasm: ?Wasm.FnData,
...@@ -66,7 +68,7 @@ pub const File = struct {...@@ -66,7 +68,7 @@ pub const File = struct {
66 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {68 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
67 switch (options.object_format) {69 switch (options.object_format) {
68 .unknown => unreachable,70 .unknown => unreachable,
69 .coff => return error.TODOImplementCoff,71 .coff => return Coff.openPath(allocator, dir, sub_path, options),
70 .elf => return Elf.openPath(allocator, dir, sub_path, options),72 .elf => return Elf.openPath(allocator, dir, sub_path, options),
71 .macho => return MachO.openPath(allocator, dir, sub_path, options),73 .macho => return MachO.openPath(allocator, dir, sub_path, options),
72 .wasm => return Wasm.openPath(allocator, dir, sub_path, options),74 .wasm => return Wasm.openPath(allocator, dir, sub_path, options),
...@@ -85,7 +87,7 @@ pub const File = struct {...@@ -85,7 +87,7 @@ pub const File = struct {
8587
86 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {88 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {
87 switch (base.tag) {89 switch (base.tag) {
88 .elf, .macho => {90 .coff, .elf, .macho => {
89 if (base.file != null) return;91 if (base.file != null) return;
90 base.file = try dir.createFile(sub_path, .{92 base.file = try dir.createFile(sub_path, .{
91 .truncate = false,93 .truncate = false,
...@@ -112,6 +114,7 @@ pub const File = struct {...@@ -112,6 +114,7 @@ pub const File = struct {
112 /// after allocateDeclIndexes for any given Decl.114 /// after allocateDeclIndexes for any given Decl.
113 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {115 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
114 switch (base.tag) {116 switch (base.tag) {
117 .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl),
115 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),118 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
116 .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),119 .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),
117 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),120 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
...@@ -121,6 +124,7 @@ pub const File = struct {...@@ -121,6 +124,7 @@ pub const File = struct {
121124
122 pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void {125 pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void {
123 switch (base.tag) {126 switch (base.tag) {
127 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),
124 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),128 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
125 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),129 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
126 .c, .wasm => {},130 .c, .wasm => {},
...@@ -131,6 +135,7 @@ pub const File = struct {...@@ -131,6 +135,7 @@ pub const File = struct {
131 /// any given Decl.135 /// any given Decl.
132 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {136 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
133 switch (base.tag) {137 switch (base.tag) {
138 .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),
134 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),139 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
135 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),140 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
136 .c, .wasm => {},141 .c, .wasm => {},
...@@ -140,6 +145,7 @@ pub const File = struct {...@@ -140,6 +145,7 @@ pub const File = struct {
140 pub fn deinit(base: *File) void {145 pub fn deinit(base: *File) void {
141 if (base.file) |f| f.close();146 if (base.file) |f| f.close();
142 switch (base.tag) {147 switch (base.tag) {
148 .coff => @fieldParentPtr(Coff, "base", base).deinit(),
143 .elf => @fieldParentPtr(Elf, "base", base).deinit(),149 .elf => @fieldParentPtr(Elf, "base", base).deinit(),
144 .macho => @fieldParentPtr(MachO, "base", base).deinit(),150 .macho => @fieldParentPtr(MachO, "base", base).deinit(),
145 .c => @fieldParentPtr(C, "base", base).deinit(),151 .c => @fieldParentPtr(C, "base", base).deinit(),
...@@ -149,6 +155,11 @@ pub const File = struct {...@@ -149,6 +155,11 @@ pub const File = struct {
149155
150 pub fn destroy(base: *File) void {156 pub fn destroy(base: *File) void {
151 switch (base.tag) {157 switch (base.tag) {
158 .coff => {
159 const parent = @fieldParentPtr(Coff, "base", base);
160 parent.deinit();
161 base.allocator.destroy(parent);
162 },
152 .elf => {163 .elf => {
153 const parent = @fieldParentPtr(Elf, "base", base);164 const parent = @fieldParentPtr(Elf, "base", base);
154 parent.deinit();165 parent.deinit();
...@@ -177,6 +188,7 @@ pub const File = struct {...@@ -177,6 +188,7 @@ pub const File = struct {
177 defer tracy.end();188 defer tracy.end();
178189
179 try switch (base.tag) {190 try switch (base.tag) {
191 .coff => @fieldParentPtr(Coff, "base", base).flush(module),
180 .elf => @fieldParentPtr(Elf, "base", base).flush(module),192 .elf => @fieldParentPtr(Elf, "base", base).flush(module),
181 .macho => @fieldParentPtr(MachO, "base", base).flush(module),193 .macho => @fieldParentPtr(MachO, "base", base).flush(module),
182 .c => @fieldParentPtr(C, "base", base).flush(module),194 .c => @fieldParentPtr(C, "base", base).flush(module),
...@@ -186,6 +198,7 @@ pub const File = struct {...@@ -186,6 +198,7 @@ pub const File = struct {
186198
187 pub fn freeDecl(base: *File, decl: *Module.Decl) void {199 pub fn freeDecl(base: *File, decl: *Module.Decl) void {
188 switch (base.tag) {200 switch (base.tag) {
201 .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl),
189 .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),202 .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
190 .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),203 .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),
191 .c => unreachable,204 .c => unreachable,
...@@ -195,6 +208,7 @@ pub const File = struct {...@@ -195,6 +208,7 @@ pub const File = struct {
195208
196 pub fn errorFlags(base: *File) ErrorFlags {209 pub fn errorFlags(base: *File) ErrorFlags {
197 return switch (base.tag) {210 return switch (base.tag) {
211 .coff => @fieldParentPtr(Coff, "base", base).error_flags,
198 .elf => @fieldParentPtr(Elf, "base", base).error_flags,212 .elf => @fieldParentPtr(Elf, "base", base).error_flags,
199 .macho => @fieldParentPtr(MachO, "base", base).error_flags,213 .macho => @fieldParentPtr(MachO, "base", base).error_flags,
200 .c => return .{ .no_entry_point_found = false },214 .c => return .{ .no_entry_point_found = false },
...@@ -211,6 +225,7 @@ pub const File = struct {...@@ -211,6 +225,7 @@ pub const File = struct {
211 exports: []const *Module.Export,225 exports: []const *Module.Export,
212 ) !void {226 ) !void {
213 switch (base.tag) {227 switch (base.tag) {
228 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports),
214 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),229 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
215 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),230 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),
216 .c => return {},231 .c => return {},
...@@ -220,6 +235,7 @@ pub const File = struct {...@@ -220,6 +235,7 @@ pub const File = struct {
220235
221 pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 {236 pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 {
222 switch (base.tag) {237 switch (base.tag) {
238 .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl),
223 .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl),239 .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl),
224 .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl),240 .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl),
225 .c => unreachable,241 .c => unreachable,
...@@ -228,6 +244,7 @@ pub const File = struct {...@@ -228,6 +244,7 @@ pub const File = struct {
228 }244 }
229245
230 pub const Tag = enum {246 pub const Tag = enum {
247 coff,
231 elf,248 elf,
232 macho,249 macho,
233 c,250 c,
...@@ -239,6 +256,7 @@ pub const File = struct {...@@ -239,6 +256,7 @@ pub const File = struct {
239 };256 };
240257
241 pub const C = @import("link/C.zig");258 pub const C = @import("link/C.zig");
259 pub const Coff = @import("link/Coff.zig");
242 pub const Elf = @import("link/Elf.zig");260 pub const Elf = @import("link/Elf.zig");
243 pub const MachO = @import("link/MachO.zig");261 pub const MachO = @import("link/MachO.zig");
244 pub const Wasm = @import("link/Wasm.zig");262 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