Commit fe0ad8d6e9

Alexandros Naskos <alex_naskos@hotmail.com>
2020-08-30 13:51:49
Write PE section table
1 parent fac9a4e
Changed files (1)
src-self-hosted
src-self-hosted/link/Coff.zig
@@ -1,6 +1,7 @@
 const Coff = @This();
 
 const std = @import("std");
+const log = std.log.scoped(.link);
 const Allocator = std.mem.Allocator;
 const assert = std.debug.assert;
 const fs = std.fs;
@@ -9,12 +10,9 @@ 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 },
@@ -70,8 +68,7 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff {
 /// 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
+        .Exe, .Obj => {},
         .Lib => return error.TODOImplementWritingLibFiles,
     }
     var self: Coff = .{
@@ -91,108 +88,205 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff
     };
     errdefer self.deinit();
 
-    var output = self.base.file.?.writer();
+    var coff_file_header_offset: u32 = 0;
+    if (options.output_mode == .Exe) {
+        // Write the MS-DOS stub and the PE signature
+        try self.base.file.?.pwriteAll(msdos_stub ++ "PE\x00\x00", 0);
+        coff_file_header_offset = msdos_stub.len + 4;
+    }
+
+    // COFF file header
+    const data_directory_count = 0;
+    var hdr_data: [112 + data_directory_count * 8 + 2 * 40]u8 = undefined;
+    var index: usize = 0;
 
-    // MS-DOS stub + PE magic
-    try output.writeAll(msdos_stub ++ "PE\x00\x00");
+    // @TODO Add an enum(u16) in std.coff, add .toCoffMachine to Arch
     const machine_type: u16 = switch (self.base.options.target.cpu.arch) {
-        .x86_64  => 0x8664,
-        .i386    => 0x014c,
+        .x86_64 => 0x8664,
+        .i386 => 0x014c,
         .riscv32 => 0x5032,
         .riscv64 => 0x5064,
         else => return error.UnsupportedCOFFArchitecture,
     };
+    std.mem.writeIntLittle(u16, hdr_data[0..2], machine_type);
+    index += 2;
+
+    // Number of sections (we only use .got, .text)
+    std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 2);
+    index += 2;
+    // TimeDateStamp (u32), PointerToSymbolTable (u32), NumberOfSymbols (u32)
+    std.mem.set(u8, hdr_data[index..][0..12], 0);
+    index += 12;
+
+    const optional_header_size = switch (options.output_mode) {
+        .Exe => data_directory_count * 8 + switch (self.ptr_width) {
+            .p32 => @as(u16, 96),
+            .p64 => 112,
+        },
+        else => 0,
+    };
+    std.mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size);
+    index += 2;
 
-    // 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
+    // Characteristics - IMAGE_FILE_DEBUG_STRIPPED
+    var characteristics: u16 = 0x200; // TODO Remove debug info stripped flag when necessary
+    if (options.output_mode == .Exe) {
+        // IMAGE_FILE_EXECUTABLE_IMAGE
+        characteristics |= 0x2;
+    }
     switch (self.ptr_width) {
         // IMAGE_FILE_32BIT_MACHINE
-        .p32 => characteristics |= 0x0100,
+        .p32 => characteristics |= 0x100,
         // 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);
-        },
+        .p64 => characteristics |= 0x20,
     }
+    std.mem.writeIntLittle(u16, hdr_data[index..][0..2], characteristics);
+    index += 2;
 
-    // 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);
-        },
+    assert(index == 20);
+    try self.base.file.?.pwriteAll(hdr_data[0..index], coff_file_header_offset);
+
+    if (options.output_mode == .Exe) {
+        // Optional header
+        index = 0;
+        std.mem.writeIntLittle(u16, hdr_data[0..2], switch (self.ptr_width) {
+            .p32 => @as(u16, 0x10b),
+            .p64 => 0x20b,
+        });
+        index += 2;
+
+        // Linker version (u8 + u8), SizeOfCode (u32), SizeOfInitializedData (u32), SizeOfUninitializedData (u32), AddressOfEntryPoint (u32)
+        std.mem.set(u8, hdr_data[index..][0..18], 0);
+        index += 18;
+
+        // Base of code relative to the image base
+        // @TODO Check where to put this
+        std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1000);
+        index += 4;
+
+        if (self.ptr_width == .p32) {
+            // Base of data relative to the image base
+            std.mem.set(u8, hdr_data[index..][0..4], 0);
+            index += 4;
+
+            // Image base address
+            std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x400_000);
+            index += 4;
+        } else {
+            // Image base address
+            std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x140_000_000);
+            index += 8;
+        }
+
+        // Section alignment
+        std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 4096);
+        index += 4;
+        // File alignment
+        std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 512);
+        index += 4;
+        // Required OS version, 6.0 is vista
+        std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
+        index += 2;
+        std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
+        index += 2;
+        // Image version
+        std.mem.set(u8, hdr_data[index..][0..4], 0);
+        index += 4;
+        // Required subsystem version, same as OS version
+        std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
+        index += 2;
+        std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
+        index += 2;
+        // Reserved zeroes (u32), SizeOfImage (u32), SizeOfHeaders (u32), CheckSum (u32)
+        std.mem.set(u8, hdr_data[index..][0..16], 0);
+        index += 16;
+        // Subsystem, TODO: Let users specify the subsystem, always CUI for now
+        std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 3);
+        index += 2;
+        // DLL characteristics, TODO: For now we are just using IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
+        std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0x40);
+        index += 2;
+
+        switch (self.ptr_width) {
+            .p32 => {
+                // @TODO See llvm output for 32 bit executables
+                // Size of stack reserve + commit
+                std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000_000);
+                index += 4;
+                std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
+                index += 4;
+                // Size of heap reserve + commit
+                std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x100_000);
+                index += 4;
+                std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
+                index += 4;
+            },
+            .p64 => {
+                // Size of stack reserve + commit
+                std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000_000);
+                index += 8;
+                std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
+                index += 8;
+                // Size of heap reserve + commit
+                std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x100_000);
+                index += 8;
+                std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
+                index += 8;
+            },
+        }
+
+        // Reserved zeroes
+        std.mem.set(u8, hdr_data[index..][0..4], 0);
+        index += 4;
+
+        // Number of data directories
+        std.mem.writeIntLittle(u32, hdr_data[index..][0..4], data_directory_count);
+        index += 4;
+        // @TODO Write meaningful stuff here
+        // Initialize data directories to zero
+        std.mem.set(u8, hdr_data[index..][0..data_directory_count * 8], 0);
+        index += data_directory_count * 8;
+
+        assert(index == optional_header_size);
     }
-    // Reserved loader flags
-    try output.writeIntLittle(u32, 0x0);
-    // Number of RVA + sizes
-    try output.writeIntLittle(u32, 0x0);
+
+    // @TODO Merge this write with the one above
+    const section_table_offset = coff_file_header_offset + 20 + optional_header_size;
+
+    // Write section table.
+    // First, the .got section
+    hdr_data[index..][0..8].* = ".got\x00\x00\x00\x00".*;
+    index += 8;
+    // 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)
+    std.mem.set(u8, hdr_data[index..][0..12], 0);
+    index += 12;
+    // File pointer to the start of the section
+    std.mem.writeIntLittle(u32, hdr_data[index..][0..4], section_table_offset + 2 * 40);
+    index += 4;
+    // Pointer to relocations (u32) (@TODO Initialize this for object files), PointerToLinenumbers (u32), NumberOfRelocations (u16), (@TODO Initialize this for object files), NumberOfLinenumbers (u16)
+    std.mem.set(u8, hdr_data[index..][0..12], 0);
+    index += 12;
+    // Characteristics `IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ = 0x40000040`
+    std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x40000040);
+    index += 4;
+    // Then, the .text section
+    hdr_data[index..][0..8].* = ".text\x00\x00\x00".*;
+    index += 8;
+    // 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)
+    std.mem.set(u8, hdr_data[index..][0..12], 0);
+    index += 12;
+    // File pointer to the start of the section (@TODO Add the initial size of .got)
+    std.mem.writeIntLittle(u32, hdr_data[index..][0..4], section_table_offset + 2 * 40);
+    index += 4;
+    // Pointer to relocations (u32) (@TODO Initialize this for object files), PointerToLinenumbers (u32), NumberOfRelocations (u16), (@TODO Initialize this for object files), NumberOfLinenumbers (u16)
+    std.mem.set(u8, hdr_data[index..][0..12], 0);
+    index += 12;
+    // Characteristics `IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE = 0xE0000020`
+    std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0xE0000020);
+    index += 4;
+
+    assert(index == optional_header_size + 2 * 40);
+    try self.base.file.?.pwriteAll(hdr_data[0..index], coff_file_header_offset + 20);
 
     return self;
 }