Merge pull request #8717 from mchudleigh/dwarf-on-windows
Dwarf on windows
This commit is contained in:
@@ -22,6 +22,10 @@ const Progress = @This();
|
||||
/// not print on update()
|
||||
terminal: ?std.fs.File = undefined,
|
||||
|
||||
/// Is this a windows API terminal (note: this is not the same as being run on windows
|
||||
/// because other terminals exist like MSYS/git-bash)
|
||||
is_windows_terminal: bool = false,
|
||||
|
||||
/// Whether the terminal supports ANSI escape codes.
|
||||
supports_ansi_escape_codes: bool = false,
|
||||
|
||||
@@ -143,6 +147,7 @@ pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !*
|
||||
self.terminal = stderr;
|
||||
self.supports_ansi_escape_codes = true;
|
||||
} else if (std.builtin.os.tag == .windows and stderr.isTty()) {
|
||||
self.is_windows_terminal = true;
|
||||
self.terminal = stderr;
|
||||
} else if (std.builtin.os.tag != .windows) {
|
||||
// we are in a "dumb" terminal like in acme or writing to a file
|
||||
@@ -192,8 +197,9 @@ const DECRC = "\x1b8";
|
||||
// supported by some terminals (eg. Terminal.app).
|
||||
|
||||
fn refreshWithHeldLock(self: *Progress) void {
|
||||
const is_dumb = !self.supports_ansi_escape_codes and !(std.builtin.os.tag == .windows);
|
||||
const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal;
|
||||
if (is_dumb and self.dont_print_on_dumb) return;
|
||||
|
||||
const file = self.terminal orelse return;
|
||||
|
||||
var end: usize = 0;
|
||||
@@ -206,6 +212,8 @@ fn refreshWithHeldLock(self: *Progress) void {
|
||||
std.mem.copy(u8, self.output_buffer[end..], seq_before);
|
||||
end += seq_before.len;
|
||||
} else if (std.builtin.os.tag == .windows) winapi: {
|
||||
std.debug.assert(self.is_windows_terminal);
|
||||
|
||||
var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
|
||||
if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE)
|
||||
unreachable;
|
||||
@@ -282,7 +290,7 @@ fn refreshWithHeldLock(self: *Progress) void {
|
||||
const seq_after = DECRC;
|
||||
std.mem.copy(u8, self.output_buffer[end..], seq_after);
|
||||
end += seq_after.len;
|
||||
} else if (std.builtin.os.tag != .windows) {
|
||||
} else if (!self.is_windows_terminal) {
|
||||
self.output_buffer[end] = '\n';
|
||||
end += 1;
|
||||
}
|
||||
@@ -293,8 +301,10 @@ fn refreshWithHeldLock(self: *Progress) void {
|
||||
};
|
||||
|
||||
if (std.builtin.os.tag == .windows) {
|
||||
if (windows.kernel32.SetConsoleCursorPosition(file.handle, saved_cursor_pos) != windows.TRUE)
|
||||
unreachable;
|
||||
if (self.is_windows_terminal) {
|
||||
const res = windows.kernel32.SetConsoleCursorPosition(file.handle, saved_cursor_pos);
|
||||
std.debug.assert(res == windows.TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
self.prev_refresh_timestamp = self.timer.read();
|
||||
@@ -318,7 +328,7 @@ fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: any
|
||||
end.* = self.output_buffer.len;
|
||||
},
|
||||
}
|
||||
const bytes_needed_for_esc_codes_at_end = if (std.builtin.os.tag == .windows) 0 else 11;
|
||||
const bytes_needed_for_esc_codes_at_end: u8 = if (self.is_windows_terminal) 0 else 11;
|
||||
const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end;
|
||||
if (end.* > max_end) {
|
||||
const suffix = "... ";
|
||||
|
||||
@@ -98,6 +98,7 @@ pub const CoffError = error{
|
||||
InvalidPEHeader,
|
||||
InvalidMachine,
|
||||
MissingCoffSection,
|
||||
MissingStringTable,
|
||||
};
|
||||
|
||||
// Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
|
||||
@@ -162,20 +163,47 @@ pub const Coff = struct {
|
||||
try self.loadOptionalHeader();
|
||||
}
|
||||
|
||||
fn readStringFromTable(self: *Coff, offset: usize, buf: []u8) ![]const u8 {
|
||||
if (self.coff_header.pointer_to_symbol_table == 0) {
|
||||
// No symbol table therefore no string table
|
||||
return error.MissingStringTable;
|
||||
}
|
||||
// The string table is at the end of the symbol table and symbols are 18 bytes long
|
||||
const string_table_offset = self.coff_header.pointer_to_symbol_table + (self.coff_header.number_of_symbols * 18) + offset;
|
||||
const in = self.in_file.reader();
|
||||
const old_pos = try self.in_file.getPos();
|
||||
|
||||
try self.in_file.seekTo(string_table_offset);
|
||||
defer {
|
||||
self.in_file.seekTo(old_pos) catch unreachable;
|
||||
}
|
||||
|
||||
const str = try in.readUntilDelimiterOrEof(buf, 0);
|
||||
return str orelse "";
|
||||
}
|
||||
|
||||
fn loadOptionalHeader(self: *Coff) !void {
|
||||
const in = self.in_file.reader();
|
||||
const opt_header_pos = try self.in_file.getPos();
|
||||
|
||||
self.pe_header.magic = try in.readIntLittle(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;
|
||||
// All we care about is the image base value and PDB info
|
||||
// The header structure is different for 32 or 64 bit
|
||||
var num_rva_pos: u64 = undefined;
|
||||
if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
||||
skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 18 * @sizeOf(u32);
|
||||
num_rva_pos = opt_header_pos + 92;
|
||||
|
||||
try self.in_file.seekTo(opt_header_pos + 28);
|
||||
const image_base32 = try in.readIntLittle(u32);
|
||||
self.pe_header.image_base = image_base32;
|
||||
} 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);
|
||||
num_rva_pos = opt_header_pos + 108;
|
||||
|
||||
try self.in_file.seekTo(opt_header_pos + 24);
|
||||
self.pe_header.image_base = try in.readIntLittle(u64);
|
||||
} else return error.InvalidPEMagic;
|
||||
|
||||
try self.in_file.seekBy(skip_size);
|
||||
try self.in_file.seekTo(num_rva_pos);
|
||||
|
||||
const number_of_rva_and_sizes = try in.readIntLittle(u32);
|
||||
if (number_of_rva_and_sizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
|
||||
@@ -258,11 +286,23 @@ pub const Coff = struct {
|
||||
|
||||
const in = self.in_file.reader();
|
||||
|
||||
var name: [8]u8 = undefined;
|
||||
var name: [32]u8 = undefined;
|
||||
|
||||
var i: u16 = 0;
|
||||
while (i < self.coff_header.number_of_sections) : (i += 1) {
|
||||
try in.readNoEof(name[0..]);
|
||||
try in.readNoEof(name[0..8]);
|
||||
|
||||
if (name[0] == '/') {
|
||||
// This is a long name and stored in the string table
|
||||
const offset_len = mem.indexOfScalar(u8, name[1..], 0) orelse 7;
|
||||
|
||||
const str_offset = try std.fmt.parseInt(u32, name[1 .. offset_len + 1], 10);
|
||||
const str = try self.readStringFromTable(str_offset, &name);
|
||||
std.mem.set(u8, name[str.len..], 0);
|
||||
} else {
|
||||
std.mem.set(u8, name[8..], 0);
|
||||
}
|
||||
|
||||
try self.sections.append(Section{
|
||||
.header = SectionHeader{
|
||||
.name = name,
|
||||
@@ -288,6 +328,22 @@ pub const Coff = struct {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return an owned slice full of the section data
|
||||
pub fn getSectionData(self: *Coff, comptime name: []const u8, allocator: *mem.Allocator) ![]u8 {
|
||||
const sec = for (self.sections.items) |*sec| {
|
||||
if (mem.eql(u8, sec.header.name[0..name.len], name)) {
|
||||
break sec;
|
||||
}
|
||||
} else {
|
||||
return error.MissingCoffSection;
|
||||
};
|
||||
const in = self.in_file.reader();
|
||||
try self.in_file.seekTo(sec.header.pointer_to_raw_data);
|
||||
const out_buff = try allocator.alloc(u8, sec.header.misc.virtual_size);
|
||||
try in.readNoEof(out_buff);
|
||||
return out_buff;
|
||||
}
|
||||
};
|
||||
|
||||
const CoffHeader = struct {
|
||||
@@ -308,6 +364,7 @@ const OptionalHeader = struct {
|
||||
|
||||
magic: u16,
|
||||
data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory,
|
||||
image_base: u64,
|
||||
};
|
||||
|
||||
const DebugDirectoryEntry = packed struct {
|
||||
@@ -331,7 +388,7 @@ const SectionHeader = struct {
|
||||
virtual_size: u32,
|
||||
};
|
||||
|
||||
name: [8]u8,
|
||||
name: [32]u8,
|
||||
misc: Misc,
|
||||
virtual_address: u32,
|
||||
size_of_raw_data: u32,
|
||||
|
||||
@@ -53,6 +53,10 @@ pub const SymbolInfo = struct {
|
||||
}
|
||||
}
|
||||
};
|
||||
const PdbOrDwarf = union(enum) {
|
||||
pdb: pdb.Pdb,
|
||||
dwarf: DW.DwarfInfo,
|
||||
};
|
||||
|
||||
var stderr_mutex = std.Thread.Mutex{};
|
||||
|
||||
@@ -669,10 +673,32 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf
|
||||
var di = ModuleDebugInfo{
|
||||
.base_address = undefined,
|
||||
.coff = coff_obj,
|
||||
.pdb = undefined,
|
||||
.debug_data = undefined,
|
||||
};
|
||||
|
||||
try di.coff.loadHeader();
|
||||
try di.coff.loadSections();
|
||||
if (di.coff.getSection(".debug_info")) |sec| {
|
||||
// This coff file has embedded DWARF debug info
|
||||
// TODO: free the section data slices
|
||||
const debug_info_data = di.coff.getSectionData(".debug_info", allocator) catch null;
|
||||
const debug_abbrev_data = di.coff.getSectionData(".debug_abbrev", allocator) catch null;
|
||||
const debug_str_data = di.coff.getSectionData(".debug_str", allocator) catch null;
|
||||
const debug_line_data = di.coff.getSectionData(".debug_line", allocator) catch null;
|
||||
const debug_ranges_data = di.coff.getSectionData(".debug_ranges", allocator) catch null;
|
||||
|
||||
var dwarf = DW.DwarfInfo{
|
||||
.endian = native_endian,
|
||||
.debug_info = debug_info_data orelse return error.MissingDebugInfo,
|
||||
.debug_abbrev = debug_abbrev_data orelse return error.MissingDebugInfo,
|
||||
.debug_str = debug_str_data orelse return error.MissingDebugInfo,
|
||||
.debug_line = debug_line_data orelse return error.MissingDebugInfo,
|
||||
.debug_ranges = debug_ranges_data,
|
||||
};
|
||||
try DW.openDwarfDebugInfo(&dwarf, allocator);
|
||||
di.debug_data = PdbOrDwarf{ .dwarf = dwarf };
|
||||
return di;
|
||||
}
|
||||
|
||||
var path_buf: [windows.MAX_PATH]u8 = undefined;
|
||||
const len = try di.coff.getPdbPath(path_buf[0..]);
|
||||
@@ -681,11 +707,12 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf
|
||||
const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path});
|
||||
defer allocator.free(path);
|
||||
|
||||
di.pdb = try pdb.Pdb.init(allocator, path);
|
||||
try di.pdb.parseInfoStream();
|
||||
try di.pdb.parseDbiStream();
|
||||
di.debug_data = PdbOrDwarf{ .pdb = undefined };
|
||||
di.debug_data.pdb = try pdb.Pdb.init(allocator, path);
|
||||
try di.debug_data.pdb.parseInfoStream();
|
||||
try di.debug_data.pdb.parseDbiStream();
|
||||
|
||||
if (!mem.eql(u8, &di.coff.guid, &di.pdb.guid) or di.coff.age != di.pdb.age)
|
||||
if (!mem.eql(u8, &di.coff.guid, &di.debug_data.pdb.guid) or di.coff.age != di.debug_data.pdb.age)
|
||||
return error.InvalidDebugInfo;
|
||||
|
||||
return di;
|
||||
@@ -1331,7 +1358,7 @@ pub const ModuleDebugInfo = switch (native_os) {
|
||||
},
|
||||
.uefi, .windows => struct {
|
||||
base_address: usize,
|
||||
pdb: pdb.Pdb,
|
||||
debug_data: PdbOrDwarf,
|
||||
coff: *coff.Coff,
|
||||
|
||||
pub fn allocator(self: @This()) *mem.Allocator {
|
||||
@@ -1342,8 +1369,18 @@ pub const ModuleDebugInfo = switch (native_os) {
|
||||
// Translate the VA into an address into this object
|
||||
const relocated_address = address - self.base_address;
|
||||
|
||||
switch (self.debug_data) {
|
||||
.dwarf => |*dwarf| {
|
||||
const dwarf_address = relocated_address + self.coff.pe_header.image_base;
|
||||
return getSymbolFromDwarf(dwarf_address, dwarf);
|
||||
},
|
||||
.pdb => {
|
||||
// fallthrough to pdb handling
|
||||
},
|
||||
}
|
||||
|
||||
var coff_section: *coff.Section = undefined;
|
||||
const mod_index = for (self.pdb.sect_contribs) |sect_contrib| {
|
||||
const mod_index = for (self.debug_data.pdb.sect_contribs) |sect_contrib| {
|
||||
if (sect_contrib.Section > self.coff.sections.items.len) continue;
|
||||
// Remember that SectionContribEntry.Section is 1-based.
|
||||
coff_section = &self.coff.sections.items[sect_contrib.Section - 1];
|
||||
@@ -1358,15 +1395,15 @@ pub const ModuleDebugInfo = switch (native_os) {
|
||||
return SymbolInfo{};
|
||||
};
|
||||
|
||||
const module = (try self.pdb.getModule(mod_index)) orelse
|
||||
const module = (try self.debug_data.pdb.getModule(mod_index)) orelse
|
||||
return error.InvalidDebugInfo;
|
||||
const obj_basename = fs.path.basename(module.obj_file_name);
|
||||
|
||||
const symbol_name = self.pdb.getSymbolName(
|
||||
const symbol_name = self.debug_data.pdb.getSymbolName(
|
||||
module,
|
||||
relocated_address - coff_section.header.virtual_address,
|
||||
) orelse "???";
|
||||
const opt_line_info = try self.pdb.getLineNumberInfo(
|
||||
const opt_line_info = try self.debug_data.pdb.getLineNumberInfo(
|
||||
module,
|
||||
relocated_address - coff_section.header.virtual_address,
|
||||
);
|
||||
@@ -1386,32 +1423,33 @@ pub const ModuleDebugInfo = switch (native_os) {
|
||||
pub fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo {
|
||||
// Translate the VA into an address into this object
|
||||
const relocated_address = address - self.base_address;
|
||||
|
||||
if (nosuspend self.dwarf.findCompileUnit(relocated_address)) |compile_unit| {
|
||||
return SymbolInfo{
|
||||
.symbol_name = nosuspend self.dwarf.getSymbolName(relocated_address) orelse "???",
|
||||
.compile_unit_name = compile_unit.die.getAttrString(&self.dwarf, DW.AT_name) catch |err| switch (err) {
|
||||
error.MissingDebugInfo, error.InvalidDebugInfo => "???",
|
||||
else => return err,
|
||||
},
|
||||
.line_info = nosuspend self.dwarf.getLineNumberInfo(compile_unit.*, relocated_address) catch |err| switch (err) {
|
||||
error.MissingDebugInfo, error.InvalidDebugInfo => null,
|
||||
else => return err,
|
||||
},
|
||||
};
|
||||
} else |err| switch (err) {
|
||||
error.MissingDebugInfo, error.InvalidDebugInfo => {
|
||||
return SymbolInfo{};
|
||||
},
|
||||
else => return err,
|
||||
}
|
||||
|
||||
unreachable;
|
||||
return getSymbolFromDwarf(relocated_address, &self.dwarf);
|
||||
}
|
||||
},
|
||||
else => DW.DwarfInfo,
|
||||
};
|
||||
|
||||
fn getSymbolFromDwarf(address: u64, di: *DW.DwarfInfo) !SymbolInfo {
|
||||
if (nosuspend di.findCompileUnit(address)) |compile_unit| {
|
||||
return SymbolInfo{
|
||||
.symbol_name = nosuspend di.getSymbolName(address) orelse "???",
|
||||
.compile_unit_name = compile_unit.die.getAttrString(di, DW.AT_name) catch |err| switch (err) {
|
||||
error.MissingDebugInfo, error.InvalidDebugInfo => "???",
|
||||
else => return err,
|
||||
},
|
||||
.line_info = nosuspend di.getLineNumberInfo(compile_unit.*, address) catch |err| switch (err) {
|
||||
error.MissingDebugInfo, error.InvalidDebugInfo => null,
|
||||
else => return err,
|
||||
},
|
||||
};
|
||||
} else |err| switch (err) {
|
||||
error.MissingDebugInfo, error.InvalidDebugInfo => {
|
||||
return SymbolInfo{};
|
||||
},
|
||||
else => return err,
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO multithreaded awareness
|
||||
var debug_info_allocator: ?*mem.Allocator = null;
|
||||
var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined;
|
||||
|
||||
@@ -144,7 +144,7 @@ const FileEntry = struct {
|
||||
};
|
||||
|
||||
const LineNumberProgram = struct {
|
||||
address: usize,
|
||||
address: u64,
|
||||
file: usize,
|
||||
line: i64,
|
||||
column: u64,
|
||||
@@ -153,12 +153,12 @@ const LineNumberProgram = struct {
|
||||
end_sequence: bool,
|
||||
|
||||
default_is_stmt: bool,
|
||||
target_address: usize,
|
||||
target_address: u64,
|
||||
include_dirs: []const []const u8,
|
||||
file_entries: *ArrayList(FileEntry),
|
||||
|
||||
prev_valid: bool,
|
||||
prev_address: usize,
|
||||
prev_address: u64,
|
||||
prev_file: usize,
|
||||
prev_line: i64,
|
||||
prev_column: u64,
|
||||
@@ -186,7 +186,7 @@ const LineNumberProgram = struct {
|
||||
self.prev_end_sequence = undefined;
|
||||
}
|
||||
|
||||
pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: usize) LineNumberProgram {
|
||||
pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: u64) LineNumberProgram {
|
||||
return LineNumberProgram{
|
||||
.address = 0,
|
||||
.file = 1,
|
||||
@@ -691,7 +691,7 @@ pub const DwarfInfo = struct {
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !debug.LineInfo {
|
||||
pub fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: u64) !debug.LineInfo {
|
||||
var stream = io.fixedBufferStream(di.debug_line);
|
||||
const in = &stream.reader();
|
||||
const seekable = &stream.seekableStream();
|
||||
|
||||
Reference in New Issue
Block a user