Write PE section table

This commit is contained in:
Alexandros Naskos 2020-08-30 14:51:49 +03:00
parent fac9a4e286
commit fe0ad8d6e9

View File

@ -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,10 +88,19 @@ 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;
}
// MS-DOS stub + PE magic
try output.writeAll(msdos_stub ++ "PE\x00\x00");
// COFF file header
const data_directory_count = 0;
var hdr_data: [112 + data_directory_count * 8 + 2 * 40]u8 = undefined;
var index: usize = 0;
// @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,
@ -102,97 +108,185 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Coff
.riscv64 => 0x5064,
else => return error.UnsupportedCOFFArchitecture,
};
std.mem.writeIntLittle(u16, hdr_data[0..2], machine_type);
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
// 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;
// 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,
.p64 => characteristics |= 0x20,
}
try output.writeIntLittle(u16, characteristics);
try output.writeIntLittle(u16, switch (self.ptr_width) {
std.mem.writeIntLittle(u16, hdr_data[index..][0..2], characteristics);
index += 2;
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;
// 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);
// @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 => {
// Write image base
try output.writeIntLittle(u64, 0x40000000);
// 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;
},
}
// 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);
},
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;
}