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