Merge pull request #1460 from ziglang/Sahnvour-windows-coff-issue721

Stack traces for Windows
This commit is contained in:
Andrew Kelley 2018-09-02 18:47:48 -04:00 committed by GitHub
commit ab387bb4c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1556 additions and 126 deletions

View File

@ -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"
@ -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"

View File

@ -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);

View File

@ -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");

View File

@ -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,

View File

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

View File

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

230
std/coff.zig Normal file
View File

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

View File

@ -4,8 +4,11 @@ const mem = std.mem;
const io = std.io;
const os = std.os;
const elf = std.elf;
const macho = std.macho;
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");
@ -17,6 +20,17 @@ pub const runtime_safety = switch (builtin.mode) {
builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => false,
};
const Module = struct {
mod_info: pdb.ModInfo,
module_name: []u8,
obj_file_name: []u8,
populated: bool,
symbols: []u8,
subsect_info: []u8,
checksum_offset: ?usize,
};
/// Tries to write to stderr, unbuffered, and ignores any error returned.
/// Does not append a newline.
var stderr_file: os.File = undefined;
@ -37,7 +51,7 @@ pub fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) {
return st;
} else {
stderr_file = try io.getStdErr();
stderr_file_out_stream = io.FileOutStream.init(&stderr_file);
stderr_file_out_stream = io.FileOutStream.init(stderr_file);
const st = &stderr_file_out_stream.stream;
stderr_stream = st;
return st;
@ -70,7 +84,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
return;
};
writeCurrentStackTrace(stderr, getDebugInfoAllocator(), debug_info, wantTtyColor(), start_addr) catch |err| {
writeCurrentStackTrace(stderr, debug_info, wantTtyColor(), start_addr) catch |err| {
stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
return;
};
@ -191,7 +205,11 @@ pub inline fn getReturnAddress(frame_count: usize) usize {
return @intToPtr(*const usize, fp + @sizeOf(usize)).*;
}
pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void {
pub fn writeCurrentStackTrace(out_stream: var, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void {
switch (builtin.os) {
builtin.Os.windows => return writeCurrentStackTraceWindows(out_stream, debug_info, tty_color, start_addr),
else => {},
}
const AddressState = union(enum) {
NotLookingForStartAddress,
LookingForStartAddress: usize,
@ -224,18 +242,296 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_
}
}
pub fn writeCurrentStackTraceWindows(out_stream: var, debug_info: *DebugInfo,
tty_color: bool, start_addr: ?usize) !void
{
var addr_buf: [1024]usize = undefined;
const casted_len = @intCast(u32, addr_buf.len); // TODO shouldn't need this cast
const n = windows.RtlCaptureStackBackTrace(0, casted_len, @ptrCast(**c_void, &addr_buf), null);
const addrs = addr_buf[0..n];
var start_i: usize = if (start_addr) |saddr| blk: {
for (addrs) |addr, i| {
if (addr == saddr) break :blk i;
}
return;
} else 0;
for (addrs[start_i..]) |addr| {
try printSourceAtAddress(debug_info, out_stream, addr, tty_color);
}
}
pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void {
switch (builtin.os) {
builtin.Os.macosx => return printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color),
builtin.Os.linux => return printSourceAtAddressLinux(debug_info, out_stream, address, tty_color),
builtin.Os.windows => {
// TODO https://github.com/ziglang/zig/issues/721
return error.UnsupportedOperatingSystem;
},
builtin.Os.windows => return printSourceAtAddressWindows(debug_info, out_stream, address, tty_color),
else => return error.UnsupportedOperatingSystem,
}
}
fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_address: usize, tty_color: bool) !void {
const allocator = getDebugInfoAllocator();
const base_address = os.getBaseAddress();
const relative_address = relocated_address - base_address;
var coff_section: *coff.Section = undefined;
const mod_index = for (di.sect_contribs) |sect_contrib| {
if (sect_contrib.Section >= di.coff.sections.len) continue;
coff_section = &di.coff.sections.toSlice()[sect_contrib.Section];
const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset;
const vaddr_end = vaddr_start + sect_contrib.Size;
if (relative_address >= vaddr_start and relative_address < vaddr_end) {
break sect_contrib.ModuleIndex;
}
} else {
// we have no information to add to the address
if (tty_color) {
try out_stream.print("???:?:?: ");
setTtyColor(TtyColor.Dim);
try out_stream.print("0x{x} in ??? (???)", relocated_address);
setTtyColor(TtyColor.Reset);
try out_stream.print("\n\n\n");
} else {
try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", relocated_address);
}
return;
};
const mod = &di.modules[mod_index];
try populateModule(di, mod);
const obj_basename = os.path.basename(mod.obj_file_name);
var symbol_i: usize = 0;
const symbol_name = while (symbol_i != mod.symbols.len) {
const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]);
if (prefix.RecordLen < 2)
return error.InvalidDebugInfo;
switch (prefix.RecordKind) {
pdb.SymbolKind.S_LPROC32 => {
const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]);
const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset;
const vaddr_end = vaddr_start + proc_sym.CodeSize;
if (relative_address >= vaddr_start and relative_address < vaddr_end) {
break mem.toSliceConst(u8, @ptrCast([*]u8, proc_sym) + @sizeOf(pdb.ProcSym));
}
},
else => {},
}
symbol_i += prefix.RecordLen + @sizeOf(u16);
if (symbol_i > mod.symbols.len)
return error.InvalidDebugInfo;
} else "???";
const subsect_info = mod.subsect_info;
var sect_offset: usize = 0;
var skip_len: usize = undefined;
const opt_line_info = subsections: {
const checksum_offset = mod.checksum_offset orelse break :subsections null;
while (sect_offset != subsect_info.len) : (sect_offset += skip_len) {
const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &subsect_info[sect_offset]);
skip_len = subsect_hdr.Length;
sect_offset += @sizeOf(pdb.DebugSubsectionHeader);
switch (subsect_hdr.Kind) {
pdb.DebugSubsectionKind.Lines => {
var line_index: usize = sect_offset;
const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]);
if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo;
line_index += @sizeOf(pdb.LineFragmentHeader);
const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]);
line_index += @sizeOf(pdb.LineBlockFragmentHeader);
const has_column = line_hdr.Flags.LF_HaveColumns;
const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset;
const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize;
if (relative_address >= frag_vaddr_start and relative_address < frag_vaddr_end) {
var line_i: usize = 0;
const start_line_index = line_index;
while (line_i < block_hdr.NumLines) : (line_i += 1) {
const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]);
line_index += @sizeOf(pdb.LineNumberEntry);
const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags);
const vaddr_start = frag_vaddr_start + line_num_entry.Offset;
const vaddr_end = if (flags.End == 0) frag_vaddr_end else vaddr_start + flags.End;
if (relative_address >= vaddr_start and relative_address < vaddr_end) {
const subsect_index = checksum_offset + block_hdr.NameIndex;
const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[subsect_index]);
const strtab_offset = @sizeOf(pdb.PDBStringTableHeader) + chksum_hdr.FileNameOffset;
try di.pdb.string_table.seekTo(strtab_offset);
const source_file_name = try di.pdb.string_table.readNullTermString(allocator);
const line = flags.Start;
const column = if (has_column) blk: {
line_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines;
line_index += @sizeOf(pdb.ColumnNumberEntry) * line_i;
const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[line_index]);
break :blk col_num_entry.StartColumn;
} else 0;
break :subsections LineInfo{
.allocator = allocator,
.file_name = source_file_name,
.line = line,
.column = column,
};
}
}
break :subsections null;
}
},
else => {},
}
if (sect_offset > subsect_info.len)
return error.InvalidDebugInfo;
} else {
break :subsections null;
}
};
if (tty_color) {
setTtyColor(TtyColor.White);
if (opt_line_info) |li| {
try out_stream.print("{}:{}:{}", li.file_name, li.line, li.column);
} else {
try out_stream.print("???:?:?");
}
setTtyColor(TtyColor.Reset);
try out_stream.print(": ");
setTtyColor(TtyColor.Dim);
try out_stream.print("0x{x} in {} ({})", relocated_address, symbol_name, obj_basename);
setTtyColor(TtyColor.Reset);
if (opt_line_info) |line_info| {
try out_stream.print("\n");
if (printLineFromFile(out_stream, line_info)) {
if (line_info.column == 0) {
try out_stream.write("\n");
} else {
{
var col_i: usize = 1;
while (col_i < line_info.column) : (col_i += 1) {
try out_stream.writeByte(' ');
}
}
setTtyColor(TtyColor.Green);
try out_stream.write("^");
setTtyColor(TtyColor.Reset);
try out_stream.write("\n");
}
} else |err| switch (err) {
error.EndOfFile => {},
else => return err,
}
} else {
try out_stream.print("\n\n\n");
}
} else {
if (opt_line_info) |li| {
try out_stream.print("{}:{}:{}: 0x{x} in {} ({})\n\n\n", li.file_name, li.line, li.column, relocated_address, symbol_name, obj_basename);
} else {
try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", relocated_address, symbol_name, obj_basename);
}
}
}
const TtyColor = enum{
Red,
Green,
Cyan,
White,
Dim,
Bold,
Reset,
};
/// TODO this is a special case hack right now. clean it up and maybe make it part of std.fmt
fn setTtyColor(tty_color: TtyColor) void {
const S = struct {
var attrs: windows.WORD = undefined;
var init_attrs = false;
};
if (!S.init_attrs) {
S.init_attrs = true;
var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
// TODO handle error
_ = windows.GetConsoleScreenBufferInfo(stderr_file.handle, &info);
S.attrs = info.wAttributes;
}
// TODO handle errors
switch (tty_color) {
TtyColor.Red => {
_ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED|windows.FOREGROUND_INTENSITY);
},
TtyColor.Green => {
_ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN|windows.FOREGROUND_INTENSITY);
},
TtyColor.Cyan => {
_ = windows.SetConsoleTextAttribute(stderr_file.handle,
windows.FOREGROUND_GREEN|windows.FOREGROUND_BLUE|windows.FOREGROUND_INTENSITY);
},
TtyColor.White, TtyColor.Bold => {
_ = windows.SetConsoleTextAttribute(stderr_file.handle,
windows.FOREGROUND_RED|windows.FOREGROUND_GREEN|windows.FOREGROUND_BLUE|windows.FOREGROUND_INTENSITY);
},
TtyColor.Dim => {
_ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_INTENSITY);
},
TtyColor.Reset => {
_ = windows.SetConsoleTextAttribute(stderr_file.handle, S.attrs);
},
}
}
fn populateModule(di: *DebugInfo, mod: *Module) !void {
if (mod.populated)
return;
const allocator = getDebugInfoAllocator();
if (mod.mod_info.C11ByteSize != 0)
return error.InvalidDebugInfo;
if (mod.mod_info.C13ByteSize == 0)
return error.MissingDebugInfo;
const modi = di.pdb.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.MissingDebugInfo;
const signature = try modi.stream.readIntLe(u32);
if (signature != 4)
return error.InvalidDebugInfo;
mod.symbols = try allocator.alloc(u8, mod.mod_info.SymByteSize - 4);
try modi.stream.readNoEof(mod.symbols);
mod.subsect_info = try allocator.alloc(u8, mod.mod_info.C13ByteSize);
try modi.stream.readNoEof(mod.subsect_info);
var sect_offset: usize = 0;
var skip_len: usize = undefined;
while (sect_offset != mod.subsect_info.len) : (sect_offset += skip_len) {
const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &mod.subsect_info[sect_offset]);
skip_len = subsect_hdr.Length;
sect_offset += @sizeOf(pdb.DebugSubsectionHeader);
switch (subsect_hdr.Kind) {
pdb.DebugSubsectionKind.FileChecksums => {
mod.checksum_offset = sect_offset;
break;
},
else => {},
}
if (sect_offset > mod.subsect_info.len)
return error.InvalidDebugInfo;
}
mod.populated = true;
}
fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol {
var min: usize = 0;
var max: usize = symbols.len - 1; // Exclude sentinel.
@ -372,14 +668,185 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo {
switch (builtin.os) {
builtin.Os.linux => return openSelfDebugInfoLinux(allocator),
builtin.Os.macosx, builtin.Os.ios => return openSelfDebugInfoMacOs(allocator),
builtin.Os.windows => {
// TODO: https://github.com/ziglang/zig/issues/721
return error.UnsupportedOperatingSystem;
},
builtin.Os.windows => return openSelfDebugInfoWindows(allocator),
else => return error.UnsupportedOperatingSystem,
}
}
fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {
const self_file = try os.openSelfExe();
defer self_file.close();
const coff_obj = try allocator.createOne(coff.Coff);
coff_obj.* = coff.Coff{
.in_file = self_file,
.allocator = allocator,
.coff_header = undefined,
.pe_header = undefined,
.sections = undefined,
.guid = undefined,
.age = undefined,
};
var di = DebugInfo{
.coff = coff_obj,
.pdb = undefined,
.sect_contribs = undefined,
.modules = undefined,
};
try di.coff.loadHeader();
var path_buf: [windows.MAX_PATH]u8 = undefined;
const len = try di.coff.getPdbPath(path_buf[0..]);
const raw_path = path_buf[0..len];
const path = try os.path.resolve(allocator, raw_path);
try di.pdb.openFile(di.coff, path);
var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo;
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, di.coff.guid, guid) or di.coff.age != age)
return error.InvalidDebugInfo;
// We validated the executable and pdb match.
const string_table_index = str_tab_index: {
const name_bytes_len = try pdb_stream.stream.readIntLe(u32);
const name_bytes = try allocator.alloc(u8, name_bytes_len);
try pdb_stream.stream.readNoEof(name_bytes);
const HashTableHeader = packed struct {
Size: u32,
Capacity: u32,
fn maxLoad(cap: u32) u32 {
return cap * 2 / 3 + 1;
}
};
var hash_tbl_hdr: HashTableHeader = undefined;
try pdb_stream.stream.readStruct(HashTableHeader, &hash_tbl_hdr);
if (hash_tbl_hdr.Capacity == 0)
return error.InvalidDebugInfo;
if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity))
return error.InvalidDebugInfo;
const present = try readSparseBitVector(&pdb_stream.stream, allocator);
if (present.len != hash_tbl_hdr.Size)
return error.InvalidDebugInfo;
const deleted = try readSparseBitVector(&pdb_stream.stream, allocator);
const Bucket = struct {
first: u32,
second: u32,
};
const bucket_list = try allocator.alloc(Bucket, present.len);
for (present) |_| {
const name_offset = try pdb_stream.stream.readIntLe(u32);
const name_index = try pdb_stream.stream.readIntLe(u32);
const name = mem.toSlice(u8, name_bytes.ptr + name_offset);
if (mem.eql(u8, name, "/names")) {
break :str_tab_index name_index;
}
}
return error.MissingDebugInfo;
};
di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.InvalidDebugInfo;
di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo;
const dbi = di.pdb.dbi;
// Dbi Header
var dbi_stream_header: pdb.DbiStreamHeader = undefined;
try dbi.stream.readStruct(pdb.DbiStreamHeader, &dbi_stream_header);
const mod_info_size = dbi_stream_header.ModInfoSize;
const section_contrib_size = dbi_stream_header.SectionContributionSize;
var modules = ArrayList(Module).init(allocator);
// Module Info Substream
var mod_info_offset: usize = 0;
while (mod_info_offset != mod_info_size) {
var mod_info: pdb.ModInfo = undefined;
try dbi.stream.readStruct(pdb.ModInfo, &mod_info);
var this_record_len: usize = @sizeOf(pdb.ModInfo);
const module_name = try dbi.readNullTermString(allocator);
this_record_len += module_name.len + 1;
const obj_file_name = try dbi.readNullTermString(allocator);
this_record_len += obj_file_name.len + 1;
const march_forward_bytes = this_record_len % 4;
if (march_forward_bytes != 0) {
try dbi.seekForward(march_forward_bytes);
this_record_len += march_forward_bytes;
}
try modules.append(Module{
.mod_info = mod_info,
.module_name = module_name,
.obj_file_name = obj_file_name,
.populated = false,
.symbols = undefined,
.subsect_info = undefined,
.checksum_offset = null,
});
mod_info_offset += this_record_len;
if (mod_info_offset > mod_info_size)
return error.InvalidDebugInfo;
}
di.modules = modules.toOwnedSlice();
// Section Contribution Substream
var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator);
var sect_cont_offset: usize = 0;
if (section_contrib_size != 0) {
const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.stream.readIntLe(u32));
if (ver != pdb.SectionContrSubstreamVersion.Ver60)
return error.InvalidDebugInfo;
sect_cont_offset += @sizeOf(u32);
}
while (sect_cont_offset != section_contrib_size) {
const entry = try sect_contribs.addOne();
try dbi.stream.readStruct(pdb.SectionContribEntry, entry);
sect_cont_offset += @sizeOf(pdb.SectionContribEntry);
if (sect_cont_offset > section_contrib_size)
return error.InvalidDebugInfo;
}
di.sect_contribs = sect_contribs.toOwnedSlice();
return di;
}
fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize {
const num_words = try stream.readIntLe(u32);
var word_i: usize = 0;
var list = ArrayList(usize).init(allocator);
while (word_i != num_words) : (word_i += 1) {
const word = try stream.readIntLe(u32);
var bit_i: u5 = 0;
while (true) : (bit_i += 1) {
if (word & (u32(1) << bit_i) != 0) {
try list.append(word_i * 32 + bit_i);
}
if (bit_i == @maxValue(u5)) break;
}
}
return list.toOwnedSlice();
}
fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo {
var di = DebugInfo{
.self_exe_file = undefined,
@ -395,7 +862,7 @@ fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo {
di.self_exe_file = try os.openSelfExe();
errdefer di.self_exe_file.close();
try di.elf.openFile(allocator, &di.self_exe_file);
try di.elf.openFile(allocator, di.self_exe_file);
errdefer di.elf.close();
di.debug_info = (try di.elf.findSection(".debug_info")) orelse return error.MissingDebugInfo;
@ -578,7 +1045,13 @@ pub const DebugInfo = switch (builtin.os) {
return self.ofiles.allocator;
}
},
else => struct {
builtin.Os.windows => struct {
pdb: pdb.Pdb,
coff: *coff.Coff,
sect_contribs: []pdb.SectionContribEntry,
modules: []Module,
},
builtin.Os.linux => struct {
self_exe_file: os.File,
elf: elf.Elf,
debug_info: *elf.SectionHeader,
@ -594,7 +1067,7 @@ pub const DebugInfo = switch (builtin.os) {
}
pub fn readString(self: *DebugInfo) ![]u8 {
var in_file_stream = io.FileInStream.init(&self.self_exe_file);
var in_file_stream = io.FileInStream.init(self.self_exe_file);
const in_stream = &in_file_stream.stream;
return readStringRaw(self.allocator(), in_stream);
}
@ -604,6 +1077,7 @@ pub const DebugInfo = switch (builtin.os) {
self.elf.close();
}
},
else => @compileError("Unsupported OS"),
};
const PcRange = struct {
@ -929,7 +1403,7 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64
}
fn parseAbbrevTable(st: *DebugInfo) !AbbrevTable {
const in_file = &st.self_exe_file;
const in_file = st.self_exe_file;
var in_file_stream = io.FileInStream.init(in_file);
const in_stream = &in_file_stream.stream;
var result = AbbrevTable.init(st.allocator());
@ -980,7 +1454,7 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con
}
fn parseDie(st: *DebugInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die {
const in_file = &st.self_exe_file;
const in_file = st.self_exe_file;
var in_file_stream = io.FileInStream.init(in_file);
const in_stream = &in_file_stream.stream;
const abbrev_code = try readULeb128(in_stream);
@ -1202,7 +1676,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, target_address: usize) !LineInfo {
const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir);
const in_file = &di.self_exe_file;
const in_file = di.self_exe_file;
const debug_line_end = di.debug_line.offset + di.debug_line.size;
var this_offset = di.debug_line.offset;
var this_index: usize = 0;
@ -1382,7 +1856,7 @@ fn scanAllCompileUnits(st: *DebugInfo) !void {
var this_unit_offset = st.debug_info.offset;
var cu_index: usize = 0;
var in_file_stream = io.FileInStream.init(&st.self_exe_file);
var in_file_stream = io.FileInStream.init(st.self_exe_file);
const in_stream = &in_file_stream.stream;
while (this_unit_offset < debug_info_end) {
@ -1448,7 +1922,7 @@ fn scanAllCompileUnits(st: *DebugInfo) !void {
}
fn findCompileUnit(st: *DebugInfo, target_address: u64) !*const CompileUnit {
var in_file_stream = io.FileInStream.init(&st.self_exe_file);
var in_file_stream = io.FileInStream.init(st.self_exe_file);
const in_stream = &in_file_stream.stream;
for (st.compile_unit_list.toSlice()) |*compile_unit| {
if (compile_unit.pc_range) |range| {

View File

@ -353,7 +353,7 @@ pub const SectionHeader = struct {
};
pub const Elf = struct {
in_file: *os.File,
in_file: os.File,
auto_close_stream: bool,
is_64: bool,
endian: builtin.Endian,
@ -376,7 +376,7 @@ pub const Elf = struct {
}
/// Call close when done.
pub fn openFile(elf: *Elf, allocator: *mem.Allocator, file: *os.File) !void {
pub fn openFile(elf: *Elf, allocator: *mem.Allocator, file: os.File) !void {
elf.allocator = allocator;
elf.in_file = file;
elf.auto_close_stream = false;

View File

@ -145,11 +145,11 @@ test "listen on a port, send bytes, receive bytes" {
cancel @handle();
}
}
async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: *const std.os.File) !void {
async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: std.os.File) !void {
const addr = _addr.*; // TODO https://github.com/ziglang/zig/issues/733
var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733
var socket = _socket; // TODO https://github.com/ziglang/zig/issues/733
var adapter = std.io.FileOutStream.init(&socket);
var adapter = std.io.FileOutStream.init(socket);
var stream = &adapter.stream;
try stream.print("hello from server\n");
}

View File

@ -15,6 +15,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");
@ -33,6 +34,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 rb = @import("rb.zig");
pub const sort = @import("sort.zig");
@ -56,6 +58,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");
@ -74,6 +77,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");

View File

@ -34,13 +34,13 @@ pub fn getStdIn() GetStdIoErrs!File {
/// Implementation of InStream trait for File
pub const FileInStream = struct {
file: *File,
file: File,
stream: Stream,
pub const Error = @typeOf(File.read).ReturnType.ErrorSet;
pub const Stream = InStream(Error);
pub fn init(file: *File) FileInStream {
pub fn init(file: File) FileInStream {
return FileInStream{
.file = file,
.stream = Stream{ .readFn = readFn },
@ -55,13 +55,13 @@ pub const FileInStream = struct {
/// Implementation of OutStream trait for File
pub const FileOutStream = struct {
file: *File,
file: File,
stream: Stream,
pub const Error = File.WriteError;
pub const Stream = OutStream(Error);
pub fn init(file: *File) FileOutStream {
pub fn init(file: File) FileOutStream {
return FileOutStream{
.file = file,
.stream = Stream{ .writeFn = writeFn },
@ -210,7 +210,7 @@ pub fn InStream(comptime ReadError: type) type {
pub fn readStruct(self: *Self, comptime T: type, ptr: *T) !void {
// Only extern and packed structs have defined in-memory layout.
assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto);
comptime assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto);
return self.readNoEof(@sliceToBytes((*[1]T)(ptr)[0..]));
}
};
@ -280,7 +280,7 @@ pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptim
const buf = try allocator.alignedAlloc(u8, A, size);
errdefer allocator.free(buf);
var adapter = FileInStream.init(&file);
var adapter = FileInStream.init(file);
try adapter.stream.readNoEof(buf[0..size]);
return buf;
}
@ -592,7 +592,7 @@ pub const BufferedAtomicFile = struct {
self.atomic_file = try os.AtomicFile.init(allocator, dest_path, os.File.default_mode);
errdefer self.atomic_file.deinit();
self.file_stream = FileOutStream.init(&self.atomic_file.file);
self.file_stream = FileOutStream.init(self.atomic_file.file);
self.buffered_stream = BufferedOutStream(FileOutStream.Error).init(&self.file_stream.stream);
return self;
}
@ -622,7 +622,7 @@ test "import io tests" {
pub fn readLine(buf: []u8) !usize {
var stdin = getStdIn() catch return error.StdInUnavailable;
var adapter = FileInStream.init(&stdin);
var adapter = FileInStream.init(stdin);
var stream = &adapter.stream;
var index: usize = 0;
while (true) {

View File

@ -19,7 +19,7 @@ test "write a file, read it, then delete it" {
var file = try os.File.openWrite(tmp_file_name);
defer file.close();
var file_out_stream = io.FileOutStream.init(&file);
var file_out_stream = io.FileOutStream.init(file);
var buf_stream = io.BufferedOutStream(io.FileOutStream.Error).init(&file_out_stream.stream);
const st = &buf_stream.stream;
try st.print("begin");
@ -35,7 +35,7 @@ test "write a file, read it, then delete it" {
const expected_file_size = "begin".len + data.len + "end".len;
assert(file_size == expected_file_size);
var file_in_stream = io.FileInStream.init(&file);
var file_in_stream = io.FileInStream.init(file);
var buf_stream = io.BufferedInStream(io.FileInStream.Error).init(&file_in_stream.stream);
const st = &buf_stream.stream;
const contents = try st.readAllAlloc(allocator, 2 * 1024);

View File

@ -209,8 +209,8 @@ pub const ChildProcess = struct {
defer Buffer.deinit(&stdout);
defer Buffer.deinit(&stderr);
var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?);
var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?);
var stdout_file_in_stream = io.FileInStream.init(child.stdout.?);
var stderr_file_in_stream = io.FileInStream.init(child.stderr.?);
try stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size);
try stderr_file_in_stream.stream.readAllBuffer(&stderr, max_output_size);

View File

@ -48,18 +48,23 @@ pub const File = struct {
return openReadC(&path_c);
}
if (is_windows) {
const handle = try os.windowsOpen(
path,
windows.GENERIC_READ,
windows.FILE_SHARE_READ,
windows.OPEN_EXISTING,
windows.FILE_ATTRIBUTE_NORMAL,
);
return openHandle(handle);
const path_w = try windows_util.sliceToPrefixedFileW(path);
return openReadW(&path_w);
}
@compileError("Unsupported OS");
}
pub fn openReadW(path_w: [*]const u16) OpenError!File {
const handle = try os.windowsOpenW(
path_w,
windows.GENERIC_READ,
windows.FILE_SHARE_READ,
windows.OPEN_EXISTING,
windows.FILE_ATTRIBUTE_NORMAL,
);
return openHandle(handle);
}
/// Calls `openWriteMode` with os.File.default_mode for the mode.
pub fn openWrite(path: []const u8) OpenError!File {
return openWriteMode(path, os.File.default_mode);
@ -74,19 +79,24 @@ pub const File = struct {
const fd = try os.posixOpen(path, flags, file_mode);
return openHandle(fd);
} else if (is_windows) {
const handle = try os.windowsOpen(
path,
windows.GENERIC_WRITE,
windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
windows.CREATE_ALWAYS,
windows.FILE_ATTRIBUTE_NORMAL,
);
return openHandle(handle);
const path_w = try windows_util.sliceToPrefixedFileW(path);
return openWriteModeW(&path_w, file_mode);
} else {
@compileError("TODO implement openWriteMode for this OS");
}
}
pub fn openWriteModeW(path_w: [*]const u16, file_mode: Mode) OpenError!File {
const handle = try os.windowsOpenW(
path_w,
windows.GENERIC_WRITE,
windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
windows.CREATE_ALWAYS,
windows.FILE_ATTRIBUTE_NORMAL,
);
return openHandle(handle);
}
/// If the path does not exist it will be created.
/// If a file already exists in the destination this returns OpenError.PathAlreadyExists
/// Call close to clean up.
@ -96,19 +106,24 @@ pub const File = struct {
const fd = try os.posixOpen(path, flags, file_mode);
return openHandle(fd);
} else if (is_windows) {
const handle = try os.windowsOpen(
path,
windows.GENERIC_WRITE,
windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
windows.CREATE_NEW,
windows.FILE_ATTRIBUTE_NORMAL,
);
return openHandle(handle);
const path_w = try windows_util.sliceToPrefixedFileW(path);
return openWriteNoClobberW(&path_w, file_mode);
} else {
@compileError("TODO implement openWriteMode for this OS");
}
}
pub fn openWriteNoClobberW(path_w: [*]const u16, file_mode: Mode) OpenError!File {
const handle = try os.windowsOpenW(
path_w,
windows.GENERIC_WRITE,
windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
windows.CREATE_NEW,
windows.FILE_ATTRIBUTE_NORMAL,
);
return openHandle(handle);
}
pub fn openHandle(handle: os.FileHandle) File {
return File{ .handle = handle };
}
@ -190,17 +205,16 @@ pub const File = struct {
/// Upon success, the stream is in an uninitialized state. To continue using it,
/// you must use the open() function.
pub fn close(self: *File) void {
pub fn close(self: File) void {
os.close(self.handle);
self.handle = undefined;
}
/// Calls `os.isTty` on `self.handle`.
pub fn isTty(self: *File) bool {
pub fn isTty(self: File) bool {
return os.isTty(self.handle);
}
pub fn seekForward(self: *File, amount: isize) !void {
pub fn seekForward(self: File, amount: isize) !void {
switch (builtin.os) {
Os.linux, Os.macosx, Os.ios => {
const result = posix.lseek(self.handle, amount, posix.SEEK_CUR);
@ -231,7 +245,7 @@ pub const File = struct {
}
}
pub fn seekTo(self: *File, pos: usize) !void {
pub fn seekTo(self: File, pos: usize) !void {
switch (builtin.os) {
Os.linux, Os.macosx, Os.ios => {
const ipos = try math.cast(isize, pos);
@ -256,6 +270,7 @@ pub const File = struct {
const err = windows.GetLastError();
return switch (err) {
windows.ERROR.INVALID_PARAMETER => unreachable,
windows.ERROR.INVALID_HANDLE => unreachable,
else => os.unexpectedErrorWindows(err),
};
}
@ -264,7 +279,7 @@ pub const File = struct {
}
}
pub fn getPos(self: *File) !usize {
pub fn getPos(self: File) !usize {
switch (builtin.os) {
Os.linux, Os.macosx, Os.ios => {
const result = posix.lseek(self.handle, 0, posix.SEEK_CUR);
@ -300,7 +315,7 @@ pub const File = struct {
}
}
pub fn getEndPos(self: *File) !usize {
pub fn getEndPos(self: File) !usize {
if (is_posix) {
const stat = try os.posixFStat(self.handle);
return @intCast(usize, stat.size);
@ -325,7 +340,7 @@ pub const File = struct {
Unexpected,
};
pub fn mode(self: *File) ModeError!Mode {
pub fn mode(self: File) ModeError!Mode {
if (is_posix) {
var stat: posix.Stat = undefined;
const err = posix.getErrno(posix.fstat(self.handle, &stat));
@ -359,7 +374,7 @@ pub const File = struct {
Unexpected,
};
pub fn read(self: *File, buffer: []u8) ReadError!usize {
pub fn read(self: File, buffer: []u8) ReadError!usize {
if (is_posix) {
var index: usize = 0;
while (index < buffer.len) {
@ -407,7 +422,7 @@ pub const File = struct {
pub const WriteError = os.WindowsWriteError || os.PosixWriteError;
pub fn write(self: *File, bytes: []const u8) WriteError!void {
pub fn write(self: File, bytes: []const u8) WriteError!void {
if (is_posix) {
try os.posixWrite(self.handle, bytes);
} else if (is_windows) {

View File

@ -57,6 +57,7 @@ pub const windowsWaitSingle = windows_util.windowsWaitSingle;
pub const windowsWrite = windows_util.windowsWrite;
pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty;
pub const windowsOpen = windows_util.windowsOpen;
pub const windowsOpenW = windows_util.windowsOpenW;
pub const windowsLoadDll = windows_util.windowsLoadDll;
pub const windowsUnloadDll = windows_util.windowsUnloadDll;
pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
@ -660,6 +661,7 @@ pub fn getBaseAddress() usize {
return phdr - @sizeOf(ElfHeader);
},
builtin.Os.macosx => return @ptrToInt(&std.c._mh_execute_header),
builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)),
else => @compileError("Unsupported OS"),
}
}
@ -2068,7 +2070,7 @@ fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []cons
}
// TODO make this a build variable that you can set
const unexpected_error_tracing = false;
const unexpected_error_tracing = true;
const UnexpectedError = error{
/// The Operating System returned an undocumented error code.
Unexpected,
@ -2087,8 +2089,9 @@ pub fn unexpectedErrorPosix(errno: usize) UnexpectedError {
/// Call this when you made a windows DLL call or something that does SetLastError
/// and you get an unexpected error.
pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError {
if (true) {
if (unexpected_error_tracing) {
debug.warn("unexpected GetLastError(): {}\n", err);
@breakpoint();
debug.dumpCurrentStackTrace(null);
}
return error.Unexpected;
@ -2103,17 +2106,35 @@ pub fn openSelfExe() !os.File {
buf[self_exe_path.len] = 0;
return os.File.openReadC(self_exe_path.ptr);
},
Os.windows => {
var buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;
const wide_slice = try selfExePathW(&buf);
return os.File.openReadW(wide_slice.ptr);
},
else => @compileError("Unsupported OS"),
}
}
test "openSelfExe" {
switch (builtin.os) {
Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(),
else => return error.SkipZigTest, // Unsupported OS
Os.linux, Os.macosx, Os.ios, Os.windows => (try openSelfExe()).close(),
else => return error.SkipZigTest, // Unsupported OS.
}
}
pub fn selfExePathW(out_buffer: *[windows_util.PATH_MAX_WIDE]u16) ![]u16 {
const casted_len = @intCast(windows.DWORD, out_buffer.len); // TODO shouldn't need this cast
const rc = windows.GetModuleFileNameW(null, out_buffer, casted_len);
assert(rc <= out_buffer.len);
if (rc == 0) {
const err = windows.GetLastError();
switch (err) {
else => return unexpectedErrorWindows(err),
}
}
return out_buffer[0..rc];
}
/// Get the path to the current executable.
/// If you only need the directory, use selfExeDirPath.
/// If you only want an open file handle, use openSelfExe.
@ -2129,16 +2150,7 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
Os.linux => return readLink(out_buffer, "/proc/self/exe"),
Os.windows => {
var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;
const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast
const rc = windows.GetModuleFileNameW(null, &utf16le_buf, casted_len);
assert(rc <= utf16le_buf.len);
if (rc == 0) {
const err = windows.GetLastError();
switch (err) {
else => return unexpectedErrorWindows(err),
}
}
const utf16le_slice = utf16le_buf[0..rc];
const utf16le_slice = try selfExePathW(&utf16le_buf);
// Trust that Windows gives us valid UTF-16LE.
const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable;
return out_buffer[0..end_index];

View File

@ -3,6 +3,7 @@ const assert = std.debug.assert;
pub use @import("advapi32.zig");
pub use @import("kernel32.zig");
pub use @import("ntdll.zig");
pub use @import("ole32.zig");
pub use @import("shell32.zig");
pub use @import("shlwapi.zig");
@ -14,6 +15,7 @@ test "import" {
pub const ERROR = @import("error.zig");
pub const SHORT = c_short;
pub const BOOL = c_int;
pub const BOOLEAN = BYTE;
pub const BYTE = u8;
@ -363,3 +365,15 @@ pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000;
pub const FILE_FLAG_SESSION_AWARE = 0x00800000;
pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000;
pub const FILE_FLAG_WRITE_THROUGH = 0x80000000;
pub const SMALL_RECT = extern struct {
Left: SHORT,
Top: SHORT,
Right: SHORT,
Bottom: SHORT,
};
pub const COORD = extern struct {
X: SHORT,
Y: SHORT,
};

View File

@ -72,6 +72,8 @@ pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;
pub extern "kernel32" stdcallcc fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) BOOL;
pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: DWORD, lpBuffer: ?[*]CHAR) DWORD;
pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD;
@ -92,6 +94,8 @@ pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR
pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: [*]u8, nSize: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn GetModuleHandleW(lpModuleName: ?[*]const WCHAR) HMODULE;
pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
@ -177,6 +181,8 @@ pub extern "kernel32" stdcallcc fn ReadFile(
pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
pub extern "kernel32" stdcallcc fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) BOOL;
pub extern "kernel32" stdcallcc fn SetFilePointerEx(
in_fFile: HANDLE,
in_liDistanceToMove: LARGE_INTEGER,
@ -232,3 +238,17 @@ pub const FILE_NOTIFY_CHANGE_LAST_WRITE = 16;
pub const FILE_NOTIFY_CHANGE_DIR_NAME = 2;
pub const FILE_NOTIFY_CHANGE_FILE_NAME = 1;
pub const FILE_NOTIFY_CHANGE_ATTRIBUTES = 4;
pub const CONSOLE_SCREEN_BUFFER_INFO = extern struct {
dwSize: COORD,
dwCursorPosition: COORD,
wAttributes: WORD,
srWindow: SMALL_RECT,
dwMaximumWindowSize: COORD,
};
pub const FOREGROUND_BLUE = 1;
pub const FOREGROUND_GREEN = 2;
pub const FOREGROUND_RED = 4;
pub const FOREGROUND_INTENSITY = 8;

3
std/os/windows/ntdll.zig Normal file
View File

@ -0,0 +1,3 @@
use @import("index.zig");
pub extern "NtDll" stdcallcc fn RtlCaptureStackBackTrace(FramesToSkip: DWORD, FramesToCapture: DWORD, BackTrace: **c_void, BackTraceHash: ?*DWORD) WORD;

View File

@ -118,16 +118,14 @@ pub const OpenError = error{
Unexpected,
};
pub fn windowsOpen(
file_path: []const u8,
pub fn windowsOpenW(
file_path_w: [*]const u16,
desired_access: windows.DWORD,
share_mode: windows.DWORD,
creation_disposition: windows.DWORD,
flags_and_attrs: windows.DWORD,
) OpenError!windows.HANDLE {
const file_path_w = try sliceToPrefixedFileW(file_path);
const result = windows.CreateFileW(&file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null);
const result = windows.CreateFileW(file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null);
if (result == windows.INVALID_HANDLE_VALUE) {
const err = windows.GetLastError();
@ -146,6 +144,17 @@ pub fn windowsOpen(
return result;
}
pub fn windowsOpen(
file_path: []const u8,
desired_access: windows.DWORD,
share_mode: windows.DWORD,
creation_disposition: windows.DWORD,
flags_and_attrs: windows.DWORD,
) OpenError!windows.HANDLE {
const file_path_w = try sliceToPrefixedFileW(file_path);
return windowsOpenW(&file_path_w, desired_access, share_mode, creation_disposition, flags_and_attrs);
}
/// Caller must free result.
pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u8 {
// count bytes needed

646
std/pdb.zig Normal file
View File

@ -0,0 +1,646 @@
const builtin = @import("builtin");
const std = @import("index.zig");
const io = std.io;
const math = std.math;
const mem = std.mem;
const os = std.os;
const warn = std.debug.warn;
const coff = std.coff;
const ArrayList = std.ArrayList;
// https://llvm.org/docs/PDB/DbiStream.html#stream-header
pub const DbiStreamHeader = packed struct {
VersionSignature: i32,
VersionHeader: u32,
Age: u32,
GlobalStreamIndex: u16,
BuildNumber: u16,
PublicStreamIndex: u16,
PdbDllVersion: u16,
SymRecordStream: u16,
PdbDllRbld: u16,
ModInfoSize: u32,
SectionContributionSize: u32,
SectionMapSize: u32,
SourceInfoSize: i32,
TypeServerSize: i32,
MFCTypeServerIndex: u32,
OptionalDbgHeaderSize: i32,
ECSubstreamSize: i32,
Flags: u16,
Machine: u16,
Padding: u32,
};
pub const SectionContribEntry = packed struct {
Section: u16,
Padding1: [2]u8,
Offset: u32,
Size: u32,
Characteristics: u32,
ModuleIndex: u16,
Padding2: [2]u8,
DataCrc: u32,
RelocCrc: u32,
};
pub const ModInfo = packed struct {
Unused1: u32,
SectionContr: SectionContribEntry,
Flags: u16,
ModuleSymStream: u16,
SymByteSize: u32,
C11ByteSize: u32,
C13ByteSize: u32,
SourceFileCount: u16,
Padding: [2]u8,
Unused2: u32,
SourceFileNameIndex: u32,
PdbFilePathNameIndex: u32,
// These fields are variable length
//ModuleName: char[],
//ObjFileName: char[],
};
pub const SectionMapHeader = packed struct {
Count: u16, /// Number of segment descriptors
LogCount: u16, /// Number of logical segment descriptors
};
pub const SectionMapEntry = packed struct {
Flags: u16 , /// See the SectionMapEntryFlags enum below.
Ovl: u16 , /// Logical overlay number
Group: u16 , /// Group index into descriptor array.
Frame: u16 ,
SectionName: u16 , /// Byte index of segment / group name in string table, or 0xFFFF.
ClassName: u16 , /// Byte index of class in string table, or 0xFFFF.
Offset: u32 , /// Byte offset of the logical segment within physical segment. If group is set in flags, this is the offset of the group.
SectionLength: u32 , /// Byte count of the segment or group.
};
pub const StreamType = enum(u16) {
Pdb = 1,
Tpi = 2,
Dbi = 3,
Ipi = 4,
};
/// Duplicate copy of SymbolRecordKind, but using the official CV names. Useful
/// for reference purposes and when dealing with unknown record types.
pub const SymbolKind = packed enum(u16) {
S_COMPILE = 1,
S_REGISTER_16t = 2,
S_CONSTANT_16t = 3,
S_UDT_16t = 4,
S_SSEARCH = 5,
S_SKIP = 7,
S_CVRESERVE = 8,
S_OBJNAME_ST = 9,
S_ENDARG = 10,
S_COBOLUDT_16t = 11,
S_MANYREG_16t = 12,
S_RETURN = 13,
S_ENTRYTHIS = 14,
S_BPREL16 = 256,
S_LDATA16 = 257,
S_GDATA16 = 258,
S_PUB16 = 259,
S_LPROC16 = 260,
S_GPROC16 = 261,
S_THUNK16 = 262,
S_BLOCK16 = 263,
S_WITH16 = 264,
S_LABEL16 = 265,
S_CEXMODEL16 = 266,
S_VFTABLE16 = 267,
S_REGREL16 = 268,
S_BPREL32_16t = 512,
S_LDATA32_16t = 513,
S_GDATA32_16t = 514,
S_PUB32_16t = 515,
S_LPROC32_16t = 516,
S_GPROC32_16t = 517,
S_THUNK32_ST = 518,
S_BLOCK32_ST = 519,
S_WITH32_ST = 520,
S_LABEL32_ST = 521,
S_CEXMODEL32 = 522,
S_VFTABLE32_16t = 523,
S_REGREL32_16t = 524,
S_LTHREAD32_16t = 525,
S_GTHREAD32_16t = 526,
S_SLINK32 = 527,
S_LPROCMIPS_16t = 768,
S_GPROCMIPS_16t = 769,
S_PROCREF_ST = 1024,
S_DATAREF_ST = 1025,
S_ALIGN = 1026,
S_LPROCREF_ST = 1027,
S_OEM = 1028,
S_TI16_MAX = 4096,
S_REGISTER_ST = 4097,
S_CONSTANT_ST = 4098,
S_UDT_ST = 4099,
S_COBOLUDT_ST = 4100,
S_MANYREG_ST = 4101,
S_BPREL32_ST = 4102,
S_LDATA32_ST = 4103,
S_GDATA32_ST = 4104,
S_PUB32_ST = 4105,
S_LPROC32_ST = 4106,
S_GPROC32_ST = 4107,
S_VFTABLE32 = 4108,
S_REGREL32_ST = 4109,
S_LTHREAD32_ST = 4110,
S_GTHREAD32_ST = 4111,
S_LPROCMIPS_ST = 4112,
S_GPROCMIPS_ST = 4113,
S_COMPILE2_ST = 4115,
S_MANYREG2_ST = 4116,
S_LPROCIA64_ST = 4117,
S_GPROCIA64_ST = 4118,
S_LOCALSLOT_ST = 4119,
S_PARAMSLOT_ST = 4120,
S_ANNOTATION = 4121,
S_GMANPROC_ST = 4122,
S_LMANPROC_ST = 4123,
S_RESERVED1 = 4124,
S_RESERVED2 = 4125,
S_RESERVED3 = 4126,
S_RESERVED4 = 4127,
S_LMANDATA_ST = 4128,
S_GMANDATA_ST = 4129,
S_MANFRAMEREL_ST = 4130,
S_MANREGISTER_ST = 4131,
S_MANSLOT_ST = 4132,
S_MANMANYREG_ST = 4133,
S_MANREGREL_ST = 4134,
S_MANMANYREG2_ST = 4135,
S_MANTYPREF = 4136,
S_UNAMESPACE_ST = 4137,
S_ST_MAX = 4352,
S_WITH32 = 4356,
S_MANYREG = 4362,
S_LPROCMIPS = 4372,
S_GPROCMIPS = 4373,
S_MANYREG2 = 4375,
S_LPROCIA64 = 4376,
S_GPROCIA64 = 4377,
S_LOCALSLOT = 4378,
S_PARAMSLOT = 4379,
S_MANFRAMEREL = 4382,
S_MANREGISTER = 4383,
S_MANSLOT = 4384,
S_MANMANYREG = 4385,
S_MANREGREL = 4386,
S_MANMANYREG2 = 4387,
S_UNAMESPACE = 4388,
S_DATAREF = 4390,
S_ANNOTATIONREF = 4392,
S_TOKENREF = 4393,
S_GMANPROC = 4394,
S_LMANPROC = 4395,
S_ATTR_FRAMEREL = 4398,
S_ATTR_REGISTER = 4399,
S_ATTR_REGREL = 4400,
S_ATTR_MANYREG = 4401,
S_SEPCODE = 4402,
S_LOCAL_2005 = 4403,
S_DEFRANGE_2005 = 4404,
S_DEFRANGE2_2005 = 4405,
S_DISCARDED = 4411,
S_LPROCMIPS_ID = 4424,
S_GPROCMIPS_ID = 4425,
S_LPROCIA64_ID = 4426,
S_GPROCIA64_ID = 4427,
S_DEFRANGE_HLSL = 4432,
S_GDATA_HLSL = 4433,
S_LDATA_HLSL = 4434,
S_LOCAL_DPC_GROUPSHARED = 4436,
S_DEFRANGE_DPC_PTR_TAG = 4439,
S_DPC_SYM_TAG_MAP = 4440,
S_ARMSWITCHTABLE = 4441,
S_POGODATA = 4444,
S_INLINESITE2 = 4445,
S_MOD_TYPEREF = 4447,
S_REF_MINIPDB = 4448,
S_PDBMAP = 4449,
S_GDATA_HLSL32 = 4450,
S_LDATA_HLSL32 = 4451,
S_GDATA_HLSL32_EX = 4452,
S_LDATA_HLSL32_EX = 4453,
S_FASTLINK = 4455,
S_INLINEES = 4456,
S_END = 6,
S_INLINESITE_END = 4430,
S_PROC_ID_END = 4431,
S_THUNK32 = 4354,
S_TRAMPOLINE = 4396,
S_SECTION = 4406,
S_COFFGROUP = 4407,
S_EXPORT = 4408,
S_LPROC32 = 4367,
S_GPROC32 = 4368,
S_LPROC32_ID = 4422,
S_GPROC32_ID = 4423,
S_LPROC32_DPC = 4437,
S_LPROC32_DPC_ID = 4438,
S_REGISTER = 4358,
S_PUB32 = 4366,
S_PROCREF = 4389,
S_LPROCREF = 4391,
S_ENVBLOCK = 4413,
S_INLINESITE = 4429,
S_LOCAL = 4414,
S_DEFRANGE = 4415,
S_DEFRANGE_SUBFIELD = 4416,
S_DEFRANGE_REGISTER = 4417,
S_DEFRANGE_FRAMEPOINTER_REL = 4418,
S_DEFRANGE_SUBFIELD_REGISTER = 4419,
S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE = 4420,
S_DEFRANGE_REGISTER_REL = 4421,
S_BLOCK32 = 4355,
S_LABEL32 = 4357,
S_OBJNAME = 4353,
S_COMPILE2 = 4374,
S_COMPILE3 = 4412,
S_FRAMEPROC = 4114,
S_CALLSITEINFO = 4409,
S_FILESTATIC = 4435,
S_HEAPALLOCSITE = 4446,
S_FRAMECOOKIE = 4410,
S_CALLEES = 4442,
S_CALLERS = 4443,
S_UDT = 4360,
S_COBOLUDT = 4361,
S_BUILDINFO = 4428,
S_BPREL32 = 4363,
S_REGREL32 = 4369,
S_CONSTANT = 4359,
S_MANCONSTANT = 4397,
S_LDATA32 = 4364,
S_GDATA32 = 4365,
S_LMANDATA = 4380,
S_GMANDATA = 4381,
S_LTHREAD32 = 4370,
S_GTHREAD32 = 4371,
};
pub const TypeIndex = u32;
pub const ProcSym = packed struct {
Parent: u32 ,
End: u32 ,
Next: u32 ,
CodeSize: u32 ,
DbgStart: u32 ,
DbgEnd: u32 ,
FunctionType: TypeIndex ,
CodeOffset: u32,
Segment: u16,
Flags: ProcSymFlags,
// following is a null terminated string
// Name: [*]u8,
};
pub const ProcSymFlags = packed struct {
HasFP: bool,
HasIRET: bool,
HasFRET: bool,
IsNoReturn: bool,
IsUnreachable: bool,
HasCustomCallingConv: bool,
IsNoInline: bool,
HasOptimizedDebugInfo: bool,
};
pub const SectionContrSubstreamVersion = enum(u32) {
Ver60 = 0xeffe0000 + 19970605,
V2 = 0xeffe0000 + 20140516
};
pub const RecordPrefix = packed struct {
RecordLen: u16, /// Record length, starting from &RecordKind.
RecordKind: SymbolKind, /// Record kind enum (SymRecordKind or TypeRecordKind)
};
pub const LineFragmentHeader = packed struct {
RelocOffset: u32, /// Code offset of line contribution.
RelocSegment: u16, /// Code segment of line contribution.
Flags: LineFlags,
CodeSize: u32, /// Code size of this line contribution.
};
pub const LineFlags = packed struct {
LF_HaveColumns: bool, /// CV_LINES_HAVE_COLUMNS
unused: u15,
};
/// The following two variable length arrays appear immediately after the
/// header. The structure definitions follow.
/// LineNumberEntry Lines[NumLines];
/// ColumnNumberEntry Columns[NumLines];
pub const LineBlockFragmentHeader = packed struct {
/// Offset of FileChecksum entry in File
/// checksums buffer. The checksum entry then
/// contains another offset into the string
/// table of the actual name.
NameIndex: u32,
NumLines: u32,
BlockSize: u32, /// code size of block, in bytes
};
pub const LineNumberEntry = packed struct {
Offset: u32, /// Offset to start of code bytes for line number
Flags: u32,
/// TODO runtime crash when I make the actual type of Flags this
const Flags = packed struct {
Start: u24,
End: u7,
IsStatement: bool,
};
};
pub const ColumnNumberEntry = packed struct {
StartColumn: u16,
EndColumn: u16,
};
/// Checksum bytes follow.
pub const FileChecksumEntryHeader = packed struct {
FileNameOffset: u32, /// Byte offset of filename in global string table.
ChecksumSize: u8, /// Number of bytes of checksum.
ChecksumKind: u8, /// FileChecksumKind
};
pub const DebugSubsectionKind = packed enum(u32) {
None = 0,
Symbols = 0xf1,
Lines = 0xf2,
StringTable = 0xf3,
FileChecksums = 0xf4,
FrameData = 0xf5,
InlineeLines = 0xf6,
CrossScopeImports = 0xf7,
CrossScopeExports = 0xf8,
// These appear to relate to .Net assembly info.
ILLines = 0xf9,
FuncMDTokenMap = 0xfa,
TypeMDTokenMap = 0xfb,
MergedAssemblyInput = 0xfc,
CoffSymbolRVA = 0xfd,
};
pub const DebugSubsectionHeader = packed struct {
Kind: DebugSubsectionKind, /// codeview::DebugSubsectionKind enum
Length: u32, /// number of bytes occupied by this record.
};
pub const PDBStringTableHeader = packed struct {
Signature: u32, /// PDBStringTableSignature
HashVersion: u32, /// 1 or 2
ByteSize: u32, /// Number of bytes of names buffer.
};
pub const Pdb = struct {
in_file: os.File,
allocator: *mem.Allocator,
coff: *coff.Coff,
string_table: *MsfStream,
dbi: *MsfStream,
msf: Msf,
pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []u8) !void {
self.in_file = try os.File.openRead(file_name);
self.allocator = coff_ptr.allocator;
self.coff = coff_ptr;
try self.msf.openFile(self.allocator, self.in_file);
}
pub fn getStreamById(self: *Pdb, id: u32) ?*MsfStream {
if (id >= self.msf.streams.len)
return null;
return &self.msf.streams[id];
}
pub fn getStream(self: *Pdb, stream: StreamType) ?*MsfStream {
const id = @enumToInt(stream);
return self.getStreamById(id);
}
};
// see https://llvm.org/docs/PDB/MsfFile.html
const Msf = struct {
directory: MsfStream,
streams: []MsfStream,
fn openFile(self: *Msf, allocator: *mem.Allocator, file: os.File) !void {
var file_stream = io.FileInStream.init(file);
const in = &file_stream.stream;
var superblock: SuperBlock = undefined;
try in.readStruct(SuperBlock, &superblock);
if (!mem.eql(u8, superblock.FileMagic, SuperBlock.file_magic))
return error.InvalidDebugInfo;
switch (superblock.BlockSize) {
// llvm only supports 4096 but we can handle any of these values
512, 1024, 2048, 4096 => {},
else => return error.InvalidDebugInfo
}
if (superblock.NumBlocks * superblock.BlockSize != try file.getEndPos())
return error.InvalidDebugInfo;
self.directory = try MsfStream.init(
superblock.BlockSize,
blockCountFromSize(superblock.NumDirectoryBytes, superblock.BlockSize),
superblock.BlockSize * superblock.BlockMapAddr,
file,
allocator,
);
const stream_count = try self.directory.stream.readIntLe(u32);
const stream_sizes = try allocator.alloc(u32, stream_count);
for (stream_sizes) |*s| {
const size = try self.directory.stream.readIntLe(u32);
s.* = blockCountFromSize(size, superblock.BlockSize);
}
self.streams = try allocator.alloc(MsfStream, stream_count);
for (self.streams) |*stream, i| {
stream.* = try MsfStream.init(
superblock.BlockSize,
stream_sizes[i],
// MsfStream.init expects the file to be at the part where it reads [N]u32
try file.getPos(),
file,
allocator,
);
}
}
};
fn blockCountFromSize(size: u32, block_size: u32) u32 {
return (size + block_size - 1) / block_size;
}
// https://llvm.org/docs/PDB/MsfFile.html#the-superblock
const SuperBlock = packed struct {
/// The LLVM docs list a space between C / C++ but empirically this is not the case.
const file_magic = "Microsoft C/C++ MSF 7.00\r\n\x1a\x44\x53\x00\x00\x00";
FileMagic: [file_magic.len]u8,
/// The block size of the internal file system. Valid values are 512, 1024,
/// 2048, and 4096 bytes. Certain aspects of the MSF file layout vary depending
/// on the block sizes. For the purposes of LLVM, we handle only block sizes of
/// 4KiB, and all further discussion assumes a block size of 4KiB.
BlockSize: u32,
/// The index of a block within the file, at which begins a bitfield representing
/// the set of all blocks within the file which are free (i.e. the data within
/// that block is not used). See The Free Block Map for more information. Important:
/// FreeBlockMapBlock can only be 1 or 2!
FreeBlockMapBlock: u32,
/// The total number of blocks in the file. NumBlocks * BlockSize should equal the
/// size of the file on disk.
NumBlocks: u32,
/// The size of the stream directory, in bytes. The stream directory contains
/// information about each streams size and the set of blocks that it occupies.
/// It will be described in more detail later.
NumDirectoryBytes: u32,
Unknown: u32,
/// The index of a block within the MSF file. At this block is an array of
/// ulittle32_ts listing the blocks that the stream directory resides on.
/// For large MSF files, the stream directory (which describes the block
/// layout of each stream) may not fit entirely on a single block. As a
/// result, this extra layer of indirection is introduced, whereby this
/// block contains the list of blocks that the stream directory occupies,
/// and the stream directory itself can be stitched together accordingly.
/// The number of ulittle32_ts in this array is given by
/// ceil(NumDirectoryBytes / BlockSize).
BlockMapAddr: u32,
};
const MsfStream = struct {
in_file: os.File,
pos: usize,
blocks: []u32,
block_size: u32,
/// Implementation of InStream trait for Pdb.MsfStream
stream: Stream,
pub const Error = @typeOf(read).ReturnType.ErrorSet;
pub const Stream = io.InStream(Error);
fn init(block_size: u32, block_count: u32, pos: usize, file: os.File, allocator: *mem.Allocator) !MsfStream {
var stream = MsfStream {
.in_file = file,
.pos = 0,
.blocks = try allocator.alloc(u32, block_count),
.block_size = block_size,
.stream = Stream {
.readFn = readFn,
},
};
var file_stream = io.FileInStream.init(file);
const in = &file_stream.stream;
try file.seekTo(pos);
var i: u32 = 0;
while (i < block_count) : (i += 1) {
stream.blocks[i] = try in.readIntLe(u32);
}
return stream;
}
fn readNullTermString(self: *MsfStream, allocator: *mem.Allocator) ![]u8 {
var list = ArrayList(u8).init(allocator);
defer list.deinit();
while (true) {
const byte = try self.stream.readByte();
if (byte == 0) {
return list.toSlice();
}
try list.append(byte);
}
}
fn read(self: *MsfStream, buffer: []u8) !usize {
var block_id = self.pos / self.block_size;
var block = self.blocks[block_id];
var offset = self.pos % self.block_size;
try self.in_file.seekTo(block * self.block_size + offset);
var file_stream = io.FileInStream.init(self.in_file);
const in = &file_stream.stream;
var size: usize = 0;
for (buffer) |*byte| {
byte.* = try in.readByte();
offset += 1;
size += 1;
// If we're at the end of a block, go to the next one.
if (offset == self.block_size) {
offset = 0;
block_id += 1;
block = self.blocks[block_id];
try self.in_file.seekTo(block * self.block_size);
}
}
self.pos += size;
return size;
}
fn seekForward(self: *MsfStream, len: usize) !void {
self.pos += len;
if (self.pos >= self.blocks.len * self.block_size)
return error.EOF;
}
fn seekTo(self: *MsfStream, len: usize) !void {
self.pos = len;
if (self.pos >= self.blocks.len * self.block_size)
return error.EOF;
}
fn getSize(self: *const MsfStream) usize {
return self.blocks.len * self.block_size;
}
fn getFilePos(self: MsfStream) usize {
const block_id = self.pos / self.block_size;
const block = self.blocks[block_id];
const offset = self.pos % self.block_size;
return block * self.block_size + offset;
}
fn readFn(in_stream: *Stream, buffer: []u8) Error!usize {
const self = @fieldParentPtr(MsfStream, "stream", in_stream);
return self.read(buffer);
}
};

View File

@ -49,14 +49,14 @@ pub fn main() !void {
var stderr_file = io.getStdErr();
var stderr_file_stream: io.FileOutStream = undefined;
var stderr_stream = if (stderr_file) |*f| x: {
var stderr_stream = if (stderr_file) |f| x: {
stderr_file_stream = io.FileOutStream.init(f);
break :x &stderr_file_stream.stream;
} else |err| err;
var stdout_file = io.getStdOut();
var stdout_file_stream: io.FileOutStream = undefined;
var stdout_stream = if (stdout_file) |*f| x: {
var stdout_stream = if (stdout_file) |f| x: {
stdout_file_stream = io.FileOutStream.init(f);
break :x &stdout_file_stream.stream;
} else |err| err;

View File

@ -1865,7 +1865,7 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined;
fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 {
var stderr_file = try io.getStdErr();
var stderr = &io.FileOutStream.init(&stderr_file).stream;
var stderr = &io.FileOutStream.init(stderr_file).stream;
var tree = try std.zig.parse(allocator, source);
defer tree.deinit();

View File

@ -19,7 +19,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
\\
\\pub fn main() void {
\\ privateFunction();
\\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream);
\\ const stdout = &FileOutStream.init(getStdOut() catch unreachable).stream;
\\ stdout.print("OK 2\n") catch unreachable;
\\}
\\
@ -34,7 +34,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
\\// purposefully conflicting function with main.zig
\\// but it's private so it should be OK
\\fn privateFunction() void {
\\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream);
\\ const stdout = &FileOutStream.init(getStdOut() catch unreachable).stream;
\\ stdout.print("OK 1\n") catch unreachable;
\\}
\\
@ -60,7 +60,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
tc.addSourceFile("foo.zig",
\\use @import("std").io;
\\pub fn foo_function() void {
\\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream);
\\ const stdout = &FileOutStream.init(getStdOut() catch unreachable).stream;
\\ stdout.print("OK\n") catch unreachable;
\\}
);
@ -71,7 +71,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
\\
\\pub fn bar_function() void {
\\ if (foo_function()) {
\\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream);
\\ const stdout = &FileOutStream.init(getStdOut() catch unreachable).stream;
\\ stdout.print("OK\n") catch unreachable;
\\ }
\\}
@ -103,7 +103,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
\\pub const a_text = "OK\n";
\\
\\pub fn ok() void {
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
\\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
\\ stdout.print(b_text) catch unreachable;
\\}
);
@ -121,7 +121,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
\\const io = @import("std").io;
\\
\\pub fn main() void {
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
\\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
\\ stdout.print("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a')) catch unreachable;
\\}
, "Hello, world!\n0012 012 a\n");
@ -274,7 +274,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
\\ var x_local : i32 = print_ok(x);
\\}
\\fn print_ok(val: @typeOf(x)) @typeOf(foo) {
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
\\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
\\ stdout.print("OK\n") catch unreachable;
\\ return 0;
\\}
@ -356,7 +356,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
\\pub fn main() void {
\\ const bar = Bar {.field2 = 13,};
\\ const foo = Foo {.field1 = bar,};
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
\\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
\\ if (!foo.method()) {
\\ stdout.print("BAD\n") catch unreachable;
\\ }
@ -370,7 +370,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
cases.add("defer with only fallthrough",
\\const io = @import("std").io;
\\pub fn main() void {
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
\\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
\\ stdout.print("before\n") catch unreachable;
\\ defer stdout.print("defer1\n") catch unreachable;
\\ defer stdout.print("defer2\n") catch unreachable;
@ -383,7 +383,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
\\const io = @import("std").io;
\\const os = @import("std").os;
\\pub fn main() void {
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
\\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
\\ stdout.print("before\n") catch unreachable;
\\ defer stdout.print("defer1\n") catch unreachable;
\\ defer stdout.print("defer2\n") catch unreachable;
@ -400,7 +400,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
\\ do_test() catch return;
\\}
\\fn do_test() !void {
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
\\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
\\ stdout.print("before\n") catch unreachable;
\\ defer stdout.print("defer1\n") catch unreachable;
\\ errdefer stdout.print("deferErr\n") catch unreachable;
@ -419,7 +419,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
\\ do_test() catch return;
\\}
\\fn do_test() !void {
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
\\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
\\ stdout.print("before\n") catch unreachable;
\\ defer stdout.print("defer1\n") catch unreachable;
\\ errdefer stdout.print("deferErr\n") catch unreachable;
@ -436,7 +436,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
\\const io = @import("std").io;
\\
\\pub fn main() void {
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
\\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream;
\\ stdout.print(foo_txt) catch unreachable;
\\}
, "1234\nabcd\n");
@ -456,7 +456,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
\\pub fn main() !void {
\\ var args_it = os.args();
\\ var stdout_file = try io.getStdOut();
\\ var stdout_adapter = io.FileOutStream.init(&stdout_file);
\\ var stdout_adapter = io.FileOutStream.init(stdout_file);
\\ const stdout = &stdout_adapter.stream;
\\ var index: usize = 0;
\\ _ = args_it.skip();
@ -497,7 +497,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
\\pub fn main() !void {
\\ var args_it = os.args();
\\ var stdout_file = try io.getStdOut();
\\ var stdout_adapter = io.FileOutStream.init(&stdout_file);
\\ var stdout_adapter = io.FileOutStream.init(stdout_file);
\\ const stdout = &stdout_adapter.stream;
\\ var index: usize = 0;
\\ _ = args_it.skip();

View File

@ -263,8 +263,8 @@ pub const CompareOutputContext = struct {
var stdout = Buffer.initNull(b.allocator);
var stderr = Buffer.initNull(b.allocator);
var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?);
var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?);
var stdout_file_in_stream = io.FileInStream.init(child.stdout.?);
var stderr_file_in_stream = io.FileInStream.init(child.stderr.?);
stdout_file_in_stream.stream.readAllBuffer(&stdout, max_stdout_size) catch unreachable;
stderr_file_in_stream.stream.readAllBuffer(&stderr, max_stdout_size) catch unreachable;
@ -578,8 +578,8 @@ pub const CompileErrorContext = struct {
var stdout_buf = Buffer.initNull(b.allocator);
var stderr_buf = Buffer.initNull(b.allocator);
var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?);
var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?);
var stdout_file_in_stream = io.FileInStream.init(child.stdout.?);
var stderr_file_in_stream = io.FileInStream.init(child.stderr.?);
stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable;
stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable;
@ -842,8 +842,8 @@ pub const TranslateCContext = struct {
var stdout_buf = Buffer.initNull(b.allocator);
var stderr_buf = Buffer.initNull(b.allocator);
var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?);
var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?);
var stdout_file_in_stream = io.FileInStream.init(child.stdout.?);
var stderr_file_in_stream = io.FileInStream.init(child.stderr.?);
stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable;
stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable;