| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const Coff = @This(); | 1 | const Coff = @This(); |
| 2 | | 2 | |
| 3 | const std = @import("std"); | 3 | const std = @import("std"); |
| | 4 | const log = std.log.scoped(.link); |
| 4 | const Allocator = std.mem.Allocator; | 5 | const Allocator = std.mem.Allocator; |
| 5 | const assert = std.debug.assert; | 6 | const assert = std.debug.assert; |
| 6 | const fs = std.fs; | 7 | const fs = std.fs; |
| ... | @@ -9,12 +10,9 @@ const Module = @import("../Module.zig"); | ... | @@ -9,12 +10,9 @@ const Module = @import("../Module.zig"); |
| 9 | const codegen = @import("../codegen/wasm.zig"); | 10 | const codegen = @import("../codegen/wasm.zig"); |
| 10 | const link = @import("../link.zig"); | 11 | const link = @import("../link.zig"); |
| 11 | | 12 | |
| 12 | | | |
| 13 | pub const base_tag: link.File.Tag = .coff; | 13 | pub const base_tag: link.File.Tag = .coff; |
| 14 | | 14 | |
| 15 | const msdos_stub = @embedFile("msdos-stub.bin"); | 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 | | 16 | |
| 19 | base: link.File, | 17 | base: link.File, |
| 20 | ptr_width: enum { p32, p64 }, | 18 | ptr_width: enum { p32, p64 }, |
| ... | @@ -70,8 +68,7 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff { | ... | @@ -70,8 +68,7 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff { |
| 70 | /// Returns an error if `file` is not already open with +read +write +seek abilities. | 68 | /// 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 { | 69 | fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff { |
| 72 | switch (options.output_mode) { | 70 | switch (options.output_mode) { |
| 73 | .Exe => {}, | 71 | .Exe, .Obj => {}, |
| 74 | .Obj => return error.TODOImplementWritingObjFiles, // @TODO DO OBJ FILES | | |
| 75 | .Lib => return error.TODOImplementWritingLibFiles, | 72 | .Lib => return error.TODOImplementWritingLibFiles, |
| 76 | } | 73 | } |
| 77 | var self: Coff = .{ | 74 | var self: Coff = .{ |
| ... | @@ -91,108 +88,205 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff | ... | @@ -91,108 +88,205 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff |
| 91 | }; | 88 | }; |
| 92 | errdefer self.deinit(); | 89 | errdefer self.deinit(); |
| 93 | | 90 | |
| 94 | var output = self.base.file.?.writer(); | 91 | var coff_file_header_offset: u32 = 0; |
| | 92 | if (options.output_mode == .Exe) { |
| | 93 | // Write the MS-DOS stub and the PE signature |
| | 94 | try self.base.file.?.pwriteAll(msdos_stub ++ "PE\x00\x00", 0); |
| | 95 | coff_file_header_offset = msdos_stub.len + 4; |
| | 96 | } |
| | 97 | |
| | 98 | // COFF file header |
| | 99 | const data_directory_count = 0; |
| | 100 | var hdr_data: [112 + data_directory_count * 8 + 2 * 40]u8 = undefined; |
| | 101 | var index: usize = 0; |
| 95 | | 102 | |
| 96 | // MS-DOS stub + PE magic | 103 | // @TODO Add an enum(u16) in std.coff, add .toCoffMachine to Arch |
| 97 | try output.writeAll(msdos_stub ++ "PE\x00\x00"); | | |
| 98 | const machine_type: u16 = switch (self.base.options.target.cpu.arch) { | 104 | const machine_type: u16 = switch (self.base.options.target.cpu.arch) { |
| 99 | .x86_64 => 0x8664, | 105 | .x86_64 => 0x8664, |
| 100 | .i386 => 0x014c, | 106 | .i386 => 0x014c, |
| 101 | .riscv32 => 0x5032, | 107 | .riscv32 => 0x5032, |
| 102 | .riscv64 => 0x5064, | 108 | .riscv64 => 0x5064, |
| 103 | else => return error.UnsupportedCOFFArchitecture, | 109 | else => return error.UnsupportedCOFFArchitecture, |
| 104 | }; | 110 | }; |
| | 111 | std.mem.writeIntLittle(u16, hdr_data[0..2], machine_type); |
| | 112 | index += 2; |
| | 113 | |
| | 114 | // Number of sections (we only use .got, .text) |
| | 115 | std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 2); |
| | 116 | index += 2; |
| | 117 | // TimeDateStamp (u32), PointerToSymbolTable (u32), NumberOfSymbols (u32) |
| | 118 | std.mem.set(u8, hdr_data[index..][0..12], 0); |
| | 119 | index += 12; |
| | 120 | |
| | 121 | const optional_header_size = switch (options.output_mode) { |
| | 122 | .Exe => data_directory_count * 8 + switch (self.ptr_width) { |
| | 123 | .p32 => @as(u16, 96), |
| | 124 | .p64 => 112, |
| | 125 | }, |
| | 126 | else => 0, |
| | 127 | }; |
| | 128 | std.mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size); |
| | 129 | index += 2; |
| 105 | | 130 | |
| 106 | // Start of COFF file header | 131 | // Characteristics - IMAGE_FILE_DEBUG_STRIPPED |
| 107 | try output.writeIntLittle(u16, machine_type); | 132 | var characteristics: u16 = 0x200; // TODO Remove debug info stripped flag when necessary |
| 108 | try output.writeIntLittle(u16, switch (self.ptr_width) { | 133 | if (options.output_mode == .Exe) { |
| 109 | .p32 => @as(u16, 98), | 134 | // IMAGE_FILE_EXECUTABLE_IMAGE |
| 110 | .p64 => 114, | 135 | characteristics |= 0x2; |
| 111 | }); | 136 | } |
| 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) { | 137 | switch (self.ptr_width) { |
| 116 | // IMAGE_FILE_32BIT_MACHINE | 138 | // IMAGE_FILE_32BIT_MACHINE |
| 117 | .p32 => characteristics |= 0x0100, | 139 | .p32 => characteristics |= 0x100, |
| 118 | // IMAGE_FILE_LARGE_ADDRESS_AWARE | 140 | // IMAGE_FILE_LARGE_ADDRESS_AWARE |
| 119 | .p64 => characteristics |= 0x0020, | 141 | .p64 => characteristics |= 0x20, |
| 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 | } | 142 | } |
| | 143 | std.mem.writeIntLittle(u16, hdr_data[index..][0..2], characteristics); |
| | 144 | index += 2; |
| 145 | | 145 | |
| 146 | // Section alignment - default to 256 | 146 | assert(index == 20); |
| 147 | try output.writeIntLittle(u32, 256); | 147 | try self.base.file.?.pwriteAll(hdr_data[0..index], coff_file_header_offset); |
| 148 | // File alignment - default to 512 | 148 | |
| 149 | try output.writeIntLittle(u32, 512); | 149 | if (options.output_mode == .Exe) { |
| 150 | // TODO - Minimum required windows version - use 6.0 (aka vista for now) | 150 | // Optional header |
| 151 | try output.writeIntLittle(u16, 0x6); | 151 | index = 0; |
| 152 | try output.writeIntLittle(u16, 0x0); | 152 | std.mem.writeIntLittle(u16, hdr_data[0..2], switch (self.ptr_width) { |
| 153 | // TODO - Image version - use 0.0 for now | 153 | .p32 => @as(u16, 0x10b), |
| 154 | try output.writeIntLittle(u32, 0x0); | 154 | .p64 => 0x20b, |
| 155 | // Subsystem version | 155 | }); |
| 156 | try output.writeIntLittle(u16, 0x6); | 156 | index += 2; |
| 157 | try output.writeIntLittle(u16, 0x0); | 157 | |
| 158 | // Reserved zeroes | 158 | // Linker version (u8 + u8), SizeOfCode (u32), SizeOfInitializedData (u32), SizeOfUninitializedData (u32), AddressOfEntryPoint (u32) |
| 159 | try output.writeIntLittle(u32, 0x0); | 159 | std.mem.set(u8, hdr_data[index..][0..18], 0); |
| 160 | // Size of image - initialize to zero | 160 | index += 18; |
| 161 | try output.writeIntLittle(u32, 0x0); | 161 | |
| 162 | // @TODO Size of headers - calculate this. | 162 | // Base of code relative to the image base |
| 163 | try output.writeIntLittle(u32, 0x0); | 163 | // @TODO Check where to put this |
| 164 | // Checksum | 164 | std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1000); |
| 165 | try output.writeIntLittle(u32, 0x0); | 165 | index += 4; |
| 166 | // Subsystem | 166 | |
| 167 | try output.writeIntLittle(u16, 0x3); | 167 | if (self.ptr_width == .p32) { |
| 168 | // @TODO Dll characteristics, just using a value from a LLVM produced executable for now. | 168 | // Base of data relative to the image base |
| 169 | try output.writeIntLittle(u16, 0x8160); | 169 | std.mem.set(u8, hdr_data[index..][0..4], 0); |
| 170 | switch (self.ptr_width) { | 170 | index += 4; |
| 171 | .p32 => { | 171 | |
| 172 | // Stack reserve | 172 | // Image base address |
| 173 | try output.writeIntLittle(u32, 0x1000000); | 173 | std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x400_000); |
| 174 | // Stack commit | 174 | index += 4; |
| 175 | try output.writeIntLittle(u32, 0x1000); | 175 | } else { |
| 176 | // Heap reserve | 176 | // Image base address |
| 177 | try output.writeIntLittle(u32, 0x100000); | 177 | std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x140_000_000); |
| 178 | // Heap commit | 178 | index += 8; |
| 179 | try output.writeIntLittle(u32, 0x100); | 179 | } |
| 180 | }, | 180 | |
| 181 | .p64 => { | 181 | // Section alignment |
| 182 | // Stack reserve | 182 | std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 4096); |
| 183 | try output.writeIntLittle(u64, 0x1000000); | 183 | index += 4; |
| 184 | // Stack commit | 184 | // File alignment |
| 185 | try output.writeIntLittle(u64, 0x1000); | 185 | std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 512); |
| 186 | // Heap reserve | 186 | index += 4; |
| 187 | try output.writeIntLittle(u64, 0x100000); | 187 | // Required OS version, 6.0 is vista |
| 188 | // Heap commit | 188 | std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 6); |
| 189 | try output.writeIntLittle(u64, 0x100); | 189 | index += 2; |
| 190 | }, | 190 | std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0); |
| | 191 | index += 2; |
| | 192 | // Image version |
| | 193 | std.mem.set(u8, hdr_data[index..][0..4], 0); |
| | 194 | index += 4; |
| | 195 | // Required subsystem version, same as OS version |
| | 196 | std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 6); |
| | 197 | index += 2; |
| | 198 | std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0); |
| | 199 | index += 2; |
| | 200 | // Reserved zeroes (u32), SizeOfImage (u32), SizeOfHeaders (u32), CheckSum (u32) |
| | 201 | std.mem.set(u8, hdr_data[index..][0..16], 0); |
| | 202 | index += 16; |
| | 203 | // Subsystem, TODO: Let users specify the subsystem, always CUI for now |
| | 204 | std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 3); |
| | 205 | index += 2; |
| | 206 | // DLL characteristics, TODO: For now we are just using IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE |
| | 207 | std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0x40); |
| | 208 | index += 2; |
| | 209 | |
| | 210 | switch (self.ptr_width) { |
| | 211 | .p32 => { |
| | 212 | // @TODO See llvm output for 32 bit executables |
| | 213 | // Size of stack reserve + commit |
| | 214 | std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000_000); |
| | 215 | index += 4; |
| | 216 | std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000); |
| | 217 | index += 4; |
| | 218 | // Size of heap reserve + commit |
| | 219 | std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x100_000); |
| | 220 | index += 4; |
| | 221 | std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000); |
| | 222 | index += 4; |
| | 223 | }, |
| | 224 | .p64 => { |
| | 225 | // Size of stack reserve + commit |
| | 226 | std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000_000); |
| | 227 | index += 8; |
| | 228 | std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000); |
| | 229 | index += 8; |
| | 230 | // Size of heap reserve + commit |
| | 231 | std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x100_000); |
| | 232 | index += 8; |
| | 233 | std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000); |
| | 234 | index += 8; |
| | 235 | }, |
| | 236 | } |
| | 237 | |
| | 238 | // Reserved zeroes |
| | 239 | std.mem.set(u8, hdr_data[index..][0..4], 0); |
| | 240 | index += 4; |
| | 241 | |
| | 242 | // Number of data directories |
| | 243 | std.mem.writeIntLittle(u32, hdr_data[index..][0..4], data_directory_count); |
| | 244 | index += 4; |
| | 245 | // @TODO Write meaningful stuff here |
| | 246 | // Initialize data directories to zero |
| | 247 | std.mem.set(u8, hdr_data[index..][0..data_directory_count * 8], 0); |
| | 248 | index += data_directory_count * 8; |
| | 249 | |
| | 250 | assert(index == optional_header_size); |
| 191 | } | 251 | } |
| 192 | // Reserved loader flags | 252 | |
| 193 | try output.writeIntLittle(u32, 0x0); | 253 | // @TODO Merge this write with the one above |
| 194 | // Number of RVA + sizes | 254 | const section_table_offset = coff_file_header_offset + 20 + optional_header_size; |
| 195 | try output.writeIntLittle(u32, 0x0); | 255 | |
| | 256 | // Write section table. |
| | 257 | // First, the .got section |
| | 258 | hdr_data[index..][0..8].* = ".got\x00\x00\x00\x00".*; |
| | 259 | index += 8; |
| | 260 | // Virtual size (u32) (@TODO Set to initial value in image files, zero otherwise), Virtual address (u32) (@TODO Set to value in image files, zero otherwise), Size of raw data (u32) |
| | 261 | std.mem.set(u8, hdr_data[index..][0..12], 0); |
| | 262 | index += 12; |
| | 263 | // File pointer to the start of the section |
| | 264 | std.mem.writeIntLittle(u32, hdr_data[index..][0..4], section_table_offset + 2 * 40); |
| | 265 | index += 4; |
| | 266 | // Pointer to relocations (u32) (@TODO Initialize this for object files), PointerToLinenumbers (u32), NumberOfRelocations (u16), (@TODO Initialize this for object files), NumberOfLinenumbers (u16) |
| | 267 | std.mem.set(u8, hdr_data[index..][0..12], 0); |
| | 268 | index += 12; |
| | 269 | // Characteristics `IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ = 0x40000040` |
| | 270 | std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x40000040); |
| | 271 | index += 4; |
| | 272 | // Then, the .text section |
| | 273 | hdr_data[index..][0..8].* = ".text\x00\x00\x00".*; |
| | 274 | index += 8; |
| | 275 | // Virtual size (u32) (@TODO Set to initial value in image files, zero otherwise), Virtual address (u32) (@TODO Set to value in image files, zero otherwise), Size of raw data (u32) |
| | 276 | std.mem.set(u8, hdr_data[index..][0..12], 0); |
| | 277 | index += 12; |
| | 278 | // File pointer to the start of the section (@TODO Add the initial size of .got) |
| | 279 | std.mem.writeIntLittle(u32, hdr_data[index..][0..4], section_table_offset + 2 * 40); |
| | 280 | index += 4; |
| | 281 | // Pointer to relocations (u32) (@TODO Initialize this for object files), PointerToLinenumbers (u32), NumberOfRelocations (u16), (@TODO Initialize this for object files), NumberOfLinenumbers (u16) |
| | 282 | std.mem.set(u8, hdr_data[index..][0..12], 0); |
| | 283 | index += 12; |
| | 284 | // Characteristics `IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE = 0xE0000020` |
| | 285 | std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0xE0000020); |
| | 286 | index += 4; |
| | 287 | |
| | 288 | assert(index == optional_header_size + 2 * 40); |
| | 289 | try self.base.file.?.pwriteAll(hdr_data[0..index], coff_file_header_offset + 20); |
| 196 | | 290 | |
| 197 | return self; | 291 | return self; |
| 198 | } | 292 | } |