Skip to content

Stack traces for Windows #1460

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Sep 2, 2018
Merged
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
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,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 @@ -577,12 +578,14 @@ set(ZIG_STD_FILES
"os/windows/error.zig"
"os/windows/index.zig"
"os/windows/kernel32.zig"
"os/windows/ntdll.zig"
"os/windows/ole32.zig"
"os/windows/shell32.zig"
"os/windows/shlwapi.zig"
"os/windows/user32.zig"
"os/windows/util.zig"
"os/zen.zig"
"pdb.zig"
"rand/index.zig"
"rand/ziggurat.zig"
"segmented_list.zig"
Expand Down
4 changes: 2 additions & 2 deletions doc/docgen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ pub fn main() !void {
var out_file = try os.File.openWrite(out_file_name);
defer out_file.close();

var file_in_stream = io.FileInStream.init(&in_file);
var file_in_stream = io.FileInStream.init(in_file);

const input_file_bytes = try file_in_stream.stream.readAllAlloc(allocator, max_doc_file_size);

var file_out_stream = io.FileOutStream.init(&out_file);
var file_out_stream = io.FileOutStream.init(out_file);
var buffered_out_stream = io.BufferedOutStream(io.FileOutStream.Error).init(&file_out_stream.stream);

var tokenizer = Tokenizer.init(in_file_name, input_file_bytes);
Expand Down
2 changes: 1 addition & 1 deletion example/guess_number/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const os = std.os;

pub fn main() !void {
var stdout_file = try io.getStdOut();
var stdout_file_stream = io.FileOutStream.init(&stdout_file);
var stdout_file_stream = io.FileOutStream.init(stdout_file);
const stdout = &stdout_file_stream.stream;

try stdout.print("Welcome to the Guess Number Game in Zig.\n");
Expand Down
2 changes: 1 addition & 1 deletion src-self-hosted/errmsg.zig
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ pub const Msg = struct {
try stream.write("\n");
}

pub fn printToFile(msg: *const Msg, file: *os.File, color: Color) !void {
pub fn printToFile(msg: *const Msg, file: os.File, color: Color) !void {
const color_on = switch (color) {
Color.Auto => file.isTty(),
Color.On => true,
Expand Down
12 changes: 6 additions & 6 deletions src-self-hosted/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ pub fn main() !void {
const allocator = std.heap.c_allocator;

var stdout_file = try std.io.getStdOut();
var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);
var stdout_out_stream = std.io.FileOutStream.init(stdout_file);
stdout = &stdout_out_stream.stream;

stderr_file = try std.io.getStdErr();
var stderr_out_stream = std.io.FileOutStream.init(&stderr_file);
var stderr_out_stream = std.io.FileOutStream.init(stderr_file);
stderr = &stderr_out_stream.stream;

const args = try os.argsAlloc(allocator);
Expand Down Expand Up @@ -491,7 +491,7 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
stderr.print("Build {} compile errors:\n", count) catch os.exit(1);
for (msgs) |msg| {
defer msg.destroy();
msg.printToFile(&stderr_file, color) catch os.exit(1);
msg.printToFile(stderr_file, color) catch os.exit(1);
}
},
}
Expand Down Expand Up @@ -619,7 +619,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
}

var stdin_file = try io.getStdIn();
var stdin = io.FileInStream.init(&stdin_file);
var stdin = io.FileInStream.init(stdin_file);

const source_code = try stdin.stream.readAllAlloc(allocator, max_src_size);
defer allocator.free(source_code);
Expand All @@ -635,7 +635,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, "<stdin>");
defer msg.destroy();

try msg.printToFile(&stderr_file, color);
try msg.printToFile(stderr_file, color);
}
if (tree.errors.len != 0) {
os.exit(1);
Expand Down Expand Up @@ -772,7 +772,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {
const msg = try errmsg.Msg.createFromParseError(fmt.loop.allocator, parse_error, &tree, file_path);
defer fmt.loop.allocator.destroy(msg);

try msg.printToFile(&stderr_file, fmt.color);
try msg.printToFile(stderr_file, fmt.color);
}
if (tree.errors.len != 0) {
fmt.any_error = true;
Expand Down
4 changes: 2 additions & 2 deletions src-self-hosted/test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ pub const TestContext = struct {
try stderr.write("build incorrectly failed:\n");
for (msgs) |msg| {
defer msg.destroy();
try msg.printToFile(&stderr, errmsg.Color.Auto);
try msg.printToFile(stderr, errmsg.Color.Auto);
}
},
}
Expand Down Expand Up @@ -234,7 +234,7 @@ pub const TestContext = struct {
var stderr = try std.io.getStdErr();
for (msgs) |msg| {
defer msg.destroy();
try msg.printToFile(&stderr, errmsg.Color.Auto);
try msg.printToFile(stderr, errmsg.Color.Auto);
}
std.debug.warn("============\n");
return error.TestFailed;
Expand Down
230 changes: 230 additions & 0 deletions std/coff.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
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);
// 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;

try self.in_file.seekForward(skip_size);

const number_of_rva_and_sizes = try in.readIntLe(u32);
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),
};
}
}

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;
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;
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),
},
});
}
}

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,
};

pub 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,
};
Loading