Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ set(ZIG_STD_FILES
"c/index.zig"
"c/linux.zig"
"c/windows.zig"
"coff.zig"
"crypto/blake2.zig"
"crypto/hmac.zig"
"crypto/index.zig"
Expand Down Expand Up @@ -544,6 +545,7 @@ set(ZIG_STD_FILES
"os/windows/index.zig"
"os/windows/util.zig"
"os/zen.zig"
"pdb.zig"
"rand/index.zig"
"rand/ziggurat.zig"
"segmented_list.zig"
Expand Down
238 changes: 238 additions & 0 deletions std/coff.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
const builtin = @import("builtin");
const std = @import("index.zig");
const io = std.io;
const mem = std.mem;
const os = std.os;

const ArrayList = std.ArrayList;

// CoffHeader.machine values
// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680313(v=vs.85).aspx
const IMAGE_FILE_MACHINE_I386 = 0x014c;
const IMAGE_FILE_MACHINE_IA64 = 0x0200;
const IMAGE_FILE_MACHINE_AMD64 = 0x8664;

// OptionalHeader.magic values
// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;

const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
const DEBUG_DIRECTORY = 6;

pub const CoffError = error {
InvalidPEMagic,
InvalidPEHeader,
InvalidMachine,
MissingCoffSection,
};

pub const Coff = struct {
in_file: os.File,
allocator: *mem.Allocator,

coff_header: CoffHeader,
pe_header: OptionalHeader,
sections: ArrayList(Section),

guid: [16]u8,
age: u32,

pub fn loadHeader(self: *Coff) !void {
const pe_pointer_offset = 0x3C;

var file_stream = io.FileInStream.init(&self.in_file);
const in = &file_stream.stream;

var magic: [2]u8 = undefined;
try in.readNoEof(magic[0..]);
if (!mem.eql(u8, magic, "MZ"))
return error.InvalidPEMagic;

// Seek to PE File Header (coff header)
try self.in_file.seekTo(pe_pointer_offset);
const pe_magic_offset = try in.readIntLe(u32);
try self.in_file.seekTo(pe_magic_offset);

var pe_header_magic: [4]u8 = undefined;
try in.readNoEof(pe_header_magic[0..]);
if (!mem.eql(u8, pe_header_magic, []u8{'P', 'E', 0, 0}))
return error.InvalidPEHeader;

self.coff_header = CoffHeader {
.machine = try in.readIntLe(u16),
.number_of_sections = try in.readIntLe(u16),
.timedate_stamp = try in.readIntLe(u32),
.pointer_to_symbol_table = try in.readIntLe(u32),
.number_of_symbols = try in.readIntLe(u32),
.size_of_optional_header = try in.readIntLe(u16),
.characteristics = try in.readIntLe(u16),
};

switch (self.coff_header.machine) {
IMAGE_FILE_MACHINE_I386,
IMAGE_FILE_MACHINE_AMD64,
IMAGE_FILE_MACHINE_IA64
=> {},
else => return error.InvalidMachine,
}

try self.loadOptionalHeader(&file_stream);
}

fn loadOptionalHeader(self: *Coff, file_stream: *io.FileInStream) !void {
const in = &file_stream.stream;
self.pe_header.magic = try in.readIntLe(u16);
std.debug.warn("reading pe optional\n");
// For now we're only interested in finding the reference to the .pdb,
// so we'll skip most of this header, which size is different in 32
// 64 bits by the way.
var skip_size: u16 = undefined;
if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 18 * @sizeOf(u32);
}
else if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 12 * @sizeOf(u32) + 5 * @sizeOf(u64);
}
else
return error.InvalidPEMagic;

std.debug.warn("skipping {}\n", skip_size);
try self.in_file.seekForward(skip_size);

const number_of_rva_and_sizes = try in.readIntLe(u32);
//std.debug.warn("indicating {} data dirs\n", number_of_rva_and_sizes);
if (number_of_rva_and_sizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
return error.InvalidPEHeader;

for (self.pe_header.data_directory) |*data_dir| {
data_dir.* = OptionalHeader.DataDirectory {
.virtual_address = try in.readIntLe(u32),
.size = try in.readIntLe(u32),
};
//std.debug.warn("data_dir @ {x}, size {}\n", data_dir.virtual_address, data_dir.size);
}
std.debug.warn("loaded data directories\n");
}

pub fn getPdbPath(self: *Coff, buffer: []u8) !usize {
try self.loadSections();
const header = (self.getSection(".rdata") orelse return error.MissingCoffSection).header;

// The linker puts a chunk that contains the .pdb path right after the
// debug_directory.
const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY];
const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data;
std.debug.warn("file offset {x}\n", file_offset);
try self.in_file.seekTo(file_offset + debug_dir.size);

var file_stream = io.FileInStream.init(&self.in_file);
const in = &file_stream.stream;

var cv_signature: [4]u8 = undefined; // CodeView signature
try in.readNoEof(cv_signature[0..]);
// 'RSDS' indicates PDB70 format, used by lld.
if (!mem.eql(u8, cv_signature, "RSDS"))
return error.InvalidPEMagic;
std.debug.warn("cv_signature {}\n", cv_signature);
try in.readNoEof(self.guid[0..]);
self.age = try in.readIntLe(u32);

// Finally read the null-terminated string.
var byte = try in.readByte();
var i: usize = 0;
while (byte != 0 and i < buffer.len) : (i += 1) {
buffer[i] = byte;
byte = try in.readByte();
}

if (byte != 0 and i == buffer.len)
return error.NameTooLong;

return i;
}

pub fn loadSections(self: *Coff) !void {
if (self.sections.len != 0)
return;

self.sections = ArrayList(Section).init(self.allocator);

var file_stream = io.FileInStream.init(&self.in_file);
const in = &file_stream.stream;

var name: [8]u8 = undefined;

var i: u16 = 0;
while (i < self.coff_header.number_of_sections) : (i += 1) {
try in.readNoEof(name[0..]);
try self.sections.append(Section {
.header = SectionHeader {
.name = name,
.misc = SectionHeader.Misc { .physical_address = try in.readIntLe(u32) },
.virtual_address = try in.readIntLe(u32),
.size_of_raw_data = try in.readIntLe(u32),
.pointer_to_raw_data = try in.readIntLe(u32),
.pointer_to_relocations = try in.readIntLe(u32),
.pointer_to_line_numbers = try in.readIntLe(u32),
.number_of_relocations = try in.readIntLe(u16),
.number_of_line_numbers = try in.readIntLe(u16),
.characteristics = try in.readIntLe(u32),
},
});
}
std.debug.warn("loaded {} sections\n", self.coff_header.number_of_sections);
}

pub fn getSection(self: *Coff, comptime name: []const u8) ?*Section {
for (self.sections.toSlice()) |*sec| {
if (mem.eql(u8, sec.header.name[0..name.len], name)) {
return sec;
}
}
return null;
}

};

const CoffHeader = struct {
machine: u16,
number_of_sections: u16,
timedate_stamp: u32,
pointer_to_symbol_table: u32,
number_of_symbols: u32,
size_of_optional_header: u16,
characteristics: u16
};

const OptionalHeader = struct {
const DataDirectory = struct {
virtual_address: u32,
size: u32
};

magic: u16,
data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory,
};

const Section = struct {
header: SectionHeader,
};

const SectionHeader = struct {
const Misc = union {
physical_address: u32,
virtual_size: u32
};

name: [8]u8,
misc: Misc,
virtual_address: u32,
size_of_raw_data: u32,
pointer_to_raw_data: u32,
pointer_to_relocations: u32,
pointer_to_line_numbers: u32,
number_of_relocations: u16,
number_of_line_numbers: u16,
characteristics: u32,
};
47 changes: 45 additions & 2 deletions std/debug/index.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ const os = std.os;
const elf = std.elf;
const DW = std.dwarf;
const macho = std.macho;
const coff = std.coff;
const pdb = std.pdb;
const windows = os.windows;
const ArrayList = std.ArrayList;
const builtin = @import("builtin");

Expand Down Expand Up @@ -197,7 +200,13 @@ fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: us
const ptr_hex = "0x{x}";

switch (builtin.os) {
builtin.Os.windows => return error.UnsupportedDebugInfo,
builtin.Os.windows => {
const base_address = @ptrToInt(windows.GetModuleHandleA(null)); // returned HMODULE points to our executable file in memory
const relative_address = address - base_address;
std.debug.warn("{x} - {x} => {x}\n", address, base_address, relative_address);
try debug_info.pdb.getSourceLine(relative_address);
return error.UnsupportedDebugInfo;
},
builtin.Os.macosx => {
// TODO(bnoordhuis) It's theoretically possible to obtain the
// compilation unit from the symbtab but it's not that useful
Expand Down Expand Up @@ -288,7 +297,38 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace {
return st;
},
builtin.ObjectFormat.coff => {
return error.TodoSupportCoffDebugInfo;
var coff_file: coff.Coff = undefined;
coff_file.in_file = try os.openSelfExe();
coff_file.allocator = allocator;
defer coff_file.in_file.close();

try coff_file.loadHeader();

var path: [windows.MAX_PATH]u8 = undefined;
const len = try coff_file.getPdbPath(path[0..]);
std.debug.warn("pdb path {}\n", path[0..len]);

const st = try allocator.create(ElfStackTrace);
errdefer allocator.destroy(st);
st.* = ElfStackTrace {
.pdb = undefined,
};

try st.pdb.openFile(allocator, path[0..len]);

var pdb_stream = st.pdb.getStream(pdb.StreamType.Pdb) orelse return error.CorruptedFile;
std.debug.warn("pdb real filepos {}\n", pdb_stream.getFilePos());
const version = try pdb_stream.stream.readIntLe(u32);
const signature = try pdb_stream.stream.readIntLe(u32);
const age = try pdb_stream.stream.readIntLe(u32);
var guid: [16]u8 = undefined;
try pdb_stream.stream.readNoEof(guid[0..]);
if (!mem.eql(u8, coff_file.guid, guid) or coff_file.age != age)
return error.CorruptedFile;
std.debug.warn("v {} s {} a {}\n", version, signature, age);
// We validated the executable and pdb match.

return st;
},
builtin.ObjectFormat.wasm => {
return error.TodoSupportCOFFDebugInfo;
Expand Down Expand Up @@ -339,6 +379,9 @@ pub const ElfStackTrace = switch (builtin.os) {
self.symbol_table.deinit();
}
},
builtin.Os.windows => struct {
pdb: pdb.Pdb,
},
else => struct {
self_exe_file: os.File,
elf: elf.Elf,
Expand Down
4 changes: 4 additions & 0 deletions std/index.zig
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub const atomic = @import("atomic/index.zig");
pub const base64 = @import("base64.zig");
pub const build = @import("build.zig");
pub const c = @import("c/index.zig");
pub const coff = @import("coff.zig");
pub const crypto = @import("crypto/index.zig");
pub const cstr = @import("cstr.zig");
pub const debug = @import("debug/index.zig");
Expand All @@ -30,6 +31,7 @@ pub const math = @import("math/index.zig");
pub const mem = @import("mem.zig");
pub const net = @import("net.zig");
pub const os = @import("os/index.zig");
pub const pdb = @import("pdb.zig");
pub const rand = @import("rand/index.zig");
pub const sort = @import("sort.zig");
pub const unicode = @import("unicode.zig");
Expand All @@ -49,6 +51,7 @@ test "std" {
_ = @import("base64.zig");
_ = @import("build.zig");
_ = @import("c/index.zig");
_ = @import("coff.zig");
_ = @import("crypto/index.zig");
_ = @import("cstr.zig");
_ = @import("debug/index.zig");
Expand All @@ -67,6 +70,7 @@ test "std" {
_ = @import("heap.zig");
_ = @import("os/index.zig");
_ = @import("rand/index.zig");
_ = @import("pdb.zig");
_ = @import("sort.zig");
_ = @import("unicode.zig");
_ = @import("zig/index.zig");
Expand Down
2 changes: 1 addition & 1 deletion std/os/file.zig
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ pub const File = struct {
},
Os.windows => {
var pos: windows.LARGE_INTEGER = undefined;
if (windows.SetFilePointerEx(self.handle, 0, *pos, windows.FILE_CURRENT) == 0) {
if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) {
const err = windows.GetLastError();
return switch (err) {
windows.ERROR.INVALID_PARAMETER => error.BadFd,
Expand Down
8 changes: 7 additions & 1 deletion std/os/index.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1896,13 +1896,19 @@ pub fn openSelfExe() !os.File {
const self_exe_path = try selfExePath(&fixed_allocator.allocator);
return os.File.openRead(&fixed_allocator.allocator, self_exe_path);
},
Os.windows => {
var fixed_buffer_mem: [windows.MAX_PATH * 2]u8 = undefined;
var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
const self_exe_path = try selfExePath(&fixed_allocator.allocator);
return os.File.openRead(&fixed_allocator.allocator, self_exe_path);
},
else => @compileError("Unsupported OS"),
}
}

test "openSelfExe" {
switch (builtin.os) {
Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(),
Os.linux, Os.macosx, Os.ios, Os.windows => (try openSelfExe()).close(),
else => return, // Unsupported OS.
}
}
Expand Down
Loading