mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
Merge pull request #1460 from ziglang/Sahnvour-windows-coff-issue721
Stack traces for Windows
This commit is contained in:
commit
ab387bb4c7
@ -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"
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
230
std/coff.zig
Normal 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,
|
||||
};
|
||||
@ -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| {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
@ -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");
|
||||
|
||||
16
std/io.zig
16
std/io.zig
@ -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) {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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
3
std/os/windows/ntdll.zig
Normal 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;
|
||||
@ -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
646
std/pdb.zig
Normal 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 stream’s 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_t’s 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_t’s 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);
|
||||
}
|
||||
};
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user