mirror of
https://github.com/ziglang/zig.git
synced 2025-01-28 21:04:39 +00:00
std.debug: implement support for DWARFv5
This commit is contained in:
parent
c84e5ee878
commit
1ce71c86bf
@ -923,6 +923,11 @@ pub fn readElfDebugInfo(allocator: mem.Allocator, elf_file: File) !ModuleDebugIn
|
|||||||
var opt_debug_line: ?[]const u8 = null;
|
var opt_debug_line: ?[]const u8 = null;
|
||||||
var opt_debug_line_str: ?[]const u8 = null;
|
var opt_debug_line_str: ?[]const u8 = null;
|
||||||
var opt_debug_ranges: ?[]const u8 = null;
|
var opt_debug_ranges: ?[]const u8 = null;
|
||||||
|
var opt_debug_loclists: ?[]const u8 = null;
|
||||||
|
var opt_debug_rnglists: ?[]const u8 = null;
|
||||||
|
var opt_debug_addr: ?[]const u8 = null;
|
||||||
|
var opt_debug_names: ?[]const u8 = null;
|
||||||
|
var opt_debug_frame: ?[]const u8 = null;
|
||||||
|
|
||||||
for (shdrs) |*shdr| {
|
for (shdrs) |*shdr| {
|
||||||
if (shdr.sh_type == elf.SHT_NULL) continue;
|
if (shdr.sh_type == elf.SHT_NULL) continue;
|
||||||
@ -942,6 +947,16 @@ pub fn readElfDebugInfo(allocator: mem.Allocator, elf_file: File) !ModuleDebugIn
|
|||||||
opt_debug_line_str = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
|
opt_debug_line_str = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
|
||||||
} else if (mem.eql(u8, name, ".debug_ranges")) {
|
} else if (mem.eql(u8, name, ".debug_ranges")) {
|
||||||
opt_debug_ranges = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
|
opt_debug_ranges = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
|
||||||
|
} else if (mem.eql(u8, name, ".debug_loclists")) {
|
||||||
|
opt_debug_loclists = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
|
||||||
|
} else if (mem.eql(u8, name, ".debug_rnglists")) {
|
||||||
|
opt_debug_rnglists = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
|
||||||
|
} else if (mem.eql(u8, name, ".debug_addr")) {
|
||||||
|
opt_debug_addr = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
|
||||||
|
} else if (mem.eql(u8, name, ".debug_names")) {
|
||||||
|
opt_debug_names = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
|
||||||
|
} else if (mem.eql(u8, name, ".debug_frame")) {
|
||||||
|
opt_debug_frame = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -954,6 +969,11 @@ pub fn readElfDebugInfo(allocator: mem.Allocator, elf_file: File) !ModuleDebugIn
|
|||||||
.debug_line = opt_debug_line orelse return error.MissingDebugInfo,
|
.debug_line = opt_debug_line orelse return error.MissingDebugInfo,
|
||||||
.debug_line_str = opt_debug_line_str,
|
.debug_line_str = opt_debug_line_str,
|
||||||
.debug_ranges = opt_debug_ranges,
|
.debug_ranges = opt_debug_ranges,
|
||||||
|
.debug_loclists = opt_debug_loclists,
|
||||||
|
.debug_rnglists = opt_debug_rnglists,
|
||||||
|
.debug_addr = opt_debug_addr,
|
||||||
|
.debug_names = opt_debug_names,
|
||||||
|
.debug_frame = opt_debug_frame,
|
||||||
};
|
};
|
||||||
|
|
||||||
try DW.openDwarfDebugInfo(&di, allocator);
|
try DW.openDwarfDebugInfo(&di, allocator);
|
||||||
@ -1494,6 +1514,11 @@ pub const ModuleDebugInfo = switch (native_os) {
|
|||||||
var opt_debug_str: ?macho.section_64 = null;
|
var opt_debug_str: ?macho.section_64 = null;
|
||||||
var opt_debug_line_str: ?macho.section_64 = null;
|
var opt_debug_line_str: ?macho.section_64 = null;
|
||||||
var opt_debug_ranges: ?macho.section_64 = null;
|
var opt_debug_ranges: ?macho.section_64 = null;
|
||||||
|
var opt_debug_loclists: ?macho.section_64 = null;
|
||||||
|
var opt_debug_rnglists: ?macho.section_64 = null;
|
||||||
|
var opt_debug_addr: ?macho.section_64 = null;
|
||||||
|
var opt_debug_names: ?macho.section_64 = null;
|
||||||
|
var opt_debug_frame: ?macho.section_64 = null;
|
||||||
|
|
||||||
for (segcmd.?.getSections()) |sect| {
|
for (segcmd.?.getSections()) |sect| {
|
||||||
const name = sect.sectName();
|
const name = sect.sectName();
|
||||||
@ -1509,6 +1534,16 @@ pub const ModuleDebugInfo = switch (native_os) {
|
|||||||
opt_debug_line_str = sect;
|
opt_debug_line_str = sect;
|
||||||
} else if (mem.eql(u8, name, "__debug_ranges")) {
|
} else if (mem.eql(u8, name, "__debug_ranges")) {
|
||||||
opt_debug_ranges = sect;
|
opt_debug_ranges = sect;
|
||||||
|
} else if (mem.eql(u8, name, "__debug_loclists")) {
|
||||||
|
opt_debug_loclists = sect;
|
||||||
|
} else if (mem.eql(u8, name, "__debug_rnglists")) {
|
||||||
|
opt_debug_rnglists = sect;
|
||||||
|
} else if (mem.eql(u8, name, "__debug_addr")) {
|
||||||
|
opt_debug_addr = sect;
|
||||||
|
} else if (mem.eql(u8, name, "__debug_names")) {
|
||||||
|
opt_debug_names = sect;
|
||||||
|
} else if (mem.eql(u8, name, "__debug_frame")) {
|
||||||
|
opt_debug_frame = sect;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1536,6 +1571,11 @@ pub const ModuleDebugInfo = switch (native_os) {
|
|||||||
try chopSlice(mapped_mem, debug_ranges.offset, debug_ranges.size)
|
try chopSlice(mapped_mem, debug_ranges.offset, debug_ranges.size)
|
||||||
else
|
else
|
||||||
null,
|
null,
|
||||||
|
.debug_loclists = opt_debug_loclists,
|
||||||
|
.debug_rnglists = opt_debug_rnglists,
|
||||||
|
.debug_addr = opt_debug_addr,
|
||||||
|
.debug_names = opt_debug_names,
|
||||||
|
.debug_frame = opt_debug_frame,
|
||||||
};
|
};
|
||||||
|
|
||||||
try DW.openDwarfDebugInfo(&di, allocator);
|
try DW.openDwarfDebugInfo(&di, allocator);
|
||||||
@ -1590,7 +1630,7 @@ pub const ModuleDebugInfo = switch (native_os) {
|
|||||||
.compile_unit_name = compile_unit.die.getAttrString(
|
.compile_unit_name = compile_unit.die.getAttrString(
|
||||||
o_file_di,
|
o_file_di,
|
||||||
DW.AT.name,
|
DW.AT.name,
|
||||||
compile_unit.is_64,
|
self.di.debug_str,
|
||||||
) catch |err| switch (err) {
|
) catch |err| switch (err) {
|
||||||
error.MissingDebugInfo, error.InvalidDebugInfo => "???",
|
error.MissingDebugInfo, error.InvalidDebugInfo => "???",
|
||||||
},
|
},
|
||||||
@ -1712,7 +1752,7 @@ fn getSymbolFromDwarf(allocator: mem.Allocator, address: u64, di: *DW.DwarfInfo)
|
|||||||
if (nosuspend di.findCompileUnit(address)) |compile_unit| {
|
if (nosuspend di.findCompileUnit(address)) |compile_unit| {
|
||||||
return SymbolInfo{
|
return SymbolInfo{
|
||||||
.symbol_name = nosuspend di.getSymbolName(address) orelse "???",
|
.symbol_name = nosuspend di.getSymbolName(address) orelse "???",
|
||||||
.compile_unit_name = compile_unit.die.getAttrString(di, DW.AT.name, compile_unit.is_64) catch |err| switch (err) {
|
.compile_unit_name = compile_unit.die.getAttrString(di, DW.AT.name, di.debug_str) catch |err| switch (err) {
|
||||||
error.MissingDebugInfo, error.InvalidDebugInfo => "???",
|
error.MissingDebugInfo, error.InvalidDebugInfo => "???",
|
||||||
},
|
},
|
||||||
.line_info = nosuspend di.getLineNumberInfo(allocator, compile_unit.*, address) catch |err| switch (err) {
|
.line_info = nosuspend di.getLineNumberInfo(allocator, compile_unit.*, address) catch |err| switch (err) {
|
||||||
|
@ -205,6 +205,7 @@ const AbbrevAttr = struct {
|
|||||||
|
|
||||||
const FormValue = union(enum) {
|
const FormValue = union(enum) {
|
||||||
Address: u64,
|
Address: u64,
|
||||||
|
AddrOffset: u64,
|
||||||
Block: []u8,
|
Block: []u8,
|
||||||
Const: Constant,
|
Const: Constant,
|
||||||
ExprLoc: []u8,
|
ExprLoc: []u8,
|
||||||
@ -216,14 +217,35 @@ const FormValue = union(enum) {
|
|||||||
StrPtr: u64,
|
StrPtr: u64,
|
||||||
StrOffset: u64,
|
StrOffset: u64,
|
||||||
LineStrPtr: u64,
|
LineStrPtr: u64,
|
||||||
|
LocListOffset: u64,
|
||||||
|
RangeListOffset: u64,
|
||||||
|
|
||||||
|
fn getString(fv: FormValue, di: DwarfInfo) ![]const u8 {
|
||||||
|
switch (fv) {
|
||||||
|
.String => |s| return s,
|
||||||
|
.StrPtr => |off| return di.getString(off),
|
||||||
|
.LineStrPtr => |off| return di.getLineString(off),
|
||||||
|
else => return badDwarf(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getUInt(fv: FormValue, comptime U: type) !U {
|
||||||
|
switch (fv) {
|
||||||
|
.Const => |c| {
|
||||||
|
const int = try c.asUnsignedLe();
|
||||||
|
return math.cast(U, int) orelse return badDwarf();
|
||||||
|
},
|
||||||
|
else => return badDwarf(),
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const Constant = struct {
|
const Constant = struct {
|
||||||
payload: u64,
|
payload: u64,
|
||||||
signed: bool,
|
signed: bool,
|
||||||
|
|
||||||
fn asUnsignedLe(self: *const Constant) !u64 {
|
fn asUnsignedLe(self: Constant) !u64 {
|
||||||
if (self.signed) return error.InvalidDebugInfo;
|
if (self.signed) return badDwarf();
|
||||||
return self.payload;
|
return self.payload;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -252,16 +274,57 @@ const Die = struct {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getAttrAddr(self: *const Die, id: u64) !u64 {
|
fn getAttrAddr(self: *const Die, di: *DwarfInfo, id: u64) error{ InvalidDebugInfo, MissingDebugInfo }!u64 {
|
||||||
const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
|
const form_value = self.getAttr(id) orelse return missingDwarf();
|
||||||
return switch (form_value.*) {
|
return switch (form_value.*) {
|
||||||
FormValue.Address => |value| value,
|
FormValue.Address => |value| value,
|
||||||
|
FormValue.AddrOffset => |index| {
|
||||||
|
const debug_addr = di.debug_addr orelse return badDwarf();
|
||||||
|
if (debug_addr.len < 8) return badDwarf();
|
||||||
|
const first_32_bits = mem.readInt(u32, debug_addr[0..4], di.endian);
|
||||||
|
const is_64 = first_32_bits == 0xffffffff;
|
||||||
|
var off: usize = undefined;
|
||||||
|
const length: u64 = l: {
|
||||||
|
if (is_64) {
|
||||||
|
if (debug_addr.len < 16) return badDwarf();
|
||||||
|
off = 12;
|
||||||
|
break :l mem.readInt(u64, debug_addr[4..12], di.endian);
|
||||||
|
} else {
|
||||||
|
if (debug_addr.len < 8) return badDwarf();
|
||||||
|
if (first_32_bits >= 0xfffffff0) return badDwarf();
|
||||||
|
off = 4;
|
||||||
|
break :l first_32_bits;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (index > length) return badDwarf();
|
||||||
|
|
||||||
|
const version = mem.readInt(u16, debug_addr[off..][0..2], di.endian);
|
||||||
|
off += 2;
|
||||||
|
if (version < 5) return badDwarf();
|
||||||
|
|
||||||
|
const addr_size = debug_addr[off];
|
||||||
|
off += 1;
|
||||||
|
|
||||||
|
const seg_size = debug_addr[off];
|
||||||
|
off += 1;
|
||||||
|
|
||||||
|
const base_offset = self.getAttrSecOffset(AT.addr_base) catch off;
|
||||||
|
const byte_offset = base_offset + (addr_size + seg_size) * index;
|
||||||
|
if (byte_offset + addr_size > debug_addr.len) return badDwarf();
|
||||||
|
switch (addr_size) {
|
||||||
|
1 => return debug_addr[byte_offset],
|
||||||
|
2 => return mem.readInt(u16, debug_addr[byte_offset..][0..2], di.endian),
|
||||||
|
4 => return mem.readInt(u32, debug_addr[byte_offset..][0..4], di.endian),
|
||||||
|
8 => return mem.readInt(u64, debug_addr[byte_offset..][0..8], di.endian),
|
||||||
|
else => return badDwarf(),
|
||||||
|
}
|
||||||
|
},
|
||||||
else => error.InvalidDebugInfo,
|
else => error.InvalidDebugInfo,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getAttrSecOffset(self: *const Die, id: u64) !u64 {
|
fn getAttrSecOffset(self: *const Die, id: u64) !u64 {
|
||||||
const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
|
const form_value = self.getAttr(id) orelse return missingDwarf();
|
||||||
return switch (form_value.*) {
|
return switch (form_value.*) {
|
||||||
FormValue.Const => |value| value.asUnsignedLe(),
|
FormValue.Const => |value| value.asUnsignedLe(),
|
||||||
FormValue.SecOffset => |value| value,
|
FormValue.SecOffset => |value| value,
|
||||||
@ -270,7 +333,7 @@ const Die = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 {
|
fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 {
|
||||||
const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
|
const form_value = self.getAttr(id) orelse return missingDwarf();
|
||||||
return switch (form_value.*) {
|
return switch (form_value.*) {
|
||||||
FormValue.Const => |value| value.asUnsignedLe(),
|
FormValue.Const => |value| value.asUnsignedLe(),
|
||||||
else => error.InvalidDebugInfo,
|
else => error.InvalidDebugInfo,
|
||||||
@ -278,33 +341,67 @@ const Die = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn getAttrRef(self: *const Die, id: u64) !u64 {
|
fn getAttrRef(self: *const Die, id: u64) !u64 {
|
||||||
const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
|
const form_value = self.getAttr(id) orelse return missingDwarf();
|
||||||
return switch (form_value.*) {
|
return switch (form_value.*) {
|
||||||
FormValue.Ref => |value| value,
|
FormValue.Ref => |value| value,
|
||||||
else => error.InvalidDebugInfo,
|
else => error.InvalidDebugInfo,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64, is_64: bool) ![]const u8 {
|
pub fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64, opt_str: ?[]const u8) error{ InvalidDebugInfo, MissingDebugInfo }![]const u8 {
|
||||||
const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
|
const form_value = self.getAttr(id) orelse return missingDwarf();
|
||||||
return switch (form_value.*) {
|
switch (form_value.*) {
|
||||||
FormValue.String => |value| value,
|
FormValue.String => |value| return value,
|
||||||
FormValue.StrPtr => |offset| di.getString(offset),
|
FormValue.StrPtr => |offset| return di.getString(offset),
|
||||||
FormValue.StrOffset => |index| blk: {
|
FormValue.StrOffset => |index| {
|
||||||
const base_offset = self.getAttrSecOffset(AT.str_offsets_base) catch 0;
|
const debug_str_offsets = di.debug_str_offsets orelse return badDwarf();
|
||||||
break :blk di.getLineString(try di.getStringOffset(base_offset + index, is_64));
|
if (debug_str_offsets.len < 8) return badDwarf();
|
||||||
|
const first_32_bits = mem.readInt(u32, debug_str_offsets[0..4], di.endian);
|
||||||
|
const is_64 = first_32_bits == 0xffffffff;
|
||||||
|
var off: usize = undefined;
|
||||||
|
const length: u64 = l: {
|
||||||
|
if (is_64) {
|
||||||
|
if (debug_str_offsets.len < 16) return badDwarf();
|
||||||
|
off = 12;
|
||||||
|
break :l mem.readInt(u64, debug_str_offsets[4..12], di.endian);
|
||||||
|
} else {
|
||||||
|
if (debug_str_offsets.len < 8) return badDwarf();
|
||||||
|
if (first_32_bits >= 0xfffffff0) return badDwarf();
|
||||||
|
off = 4;
|
||||||
|
break :l first_32_bits;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (index > length) return badDwarf();
|
||||||
|
|
||||||
|
const version = mem.readInt(u16, debug_str_offsets[off..][0..2], di.endian);
|
||||||
|
off += 2;
|
||||||
|
if (version < 5) return badDwarf();
|
||||||
|
|
||||||
|
off += 2; // reserved
|
||||||
|
|
||||||
|
const base_offset = self.getAttrSecOffset(AT.str_offsets_base) catch off;
|
||||||
|
if (is_64) {
|
||||||
|
const byte_offset = base_offset + 8 * index;
|
||||||
|
const offset = mem.readInt(u64, debug_str_offsets[byte_offset..][0..8], di.endian);
|
||||||
|
return getStringGeneric(opt_str, offset);
|
||||||
|
} else {
|
||||||
|
const byte_offset = base_offset + 4 * index;
|
||||||
|
const offset = mem.readInt(u32, debug_str_offsets[byte_offset..][0..4], di.endian);
|
||||||
|
return getStringGeneric(opt_str, offset);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
FormValue.LineStrPtr => |offset| di.getLineString(offset),
|
FormValue.LineStrPtr => |offset| return di.getLineString(offset),
|
||||||
else => error.InvalidDebugInfo,
|
else => return badDwarf(),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const FileEntry = struct {
|
const FileEntry = struct {
|
||||||
file_name: []const u8,
|
path: []const u8,
|
||||||
dir_index: usize,
|
dir_index: u32 = 0,
|
||||||
mtime: usize,
|
mtime: u64 = 0,
|
||||||
len_bytes: usize,
|
size: u64 = 0,
|
||||||
|
md5: u128 = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const LineNumberProgram = struct {
|
const LineNumberProgram = struct {
|
||||||
@ -312,13 +409,14 @@ const LineNumberProgram = struct {
|
|||||||
file: usize,
|
file: usize,
|
||||||
line: i64,
|
line: i64,
|
||||||
column: u64,
|
column: u64,
|
||||||
|
version: u16,
|
||||||
is_stmt: bool,
|
is_stmt: bool,
|
||||||
basic_block: bool,
|
basic_block: bool,
|
||||||
end_sequence: bool,
|
end_sequence: bool,
|
||||||
|
|
||||||
default_is_stmt: bool,
|
default_is_stmt: bool,
|
||||||
target_address: u64,
|
target_address: u64,
|
||||||
include_dirs: []const []const u8,
|
include_dirs: []const FileEntry,
|
||||||
|
|
||||||
prev_valid: bool,
|
prev_valid: bool,
|
||||||
prev_address: u64,
|
prev_address: u64,
|
||||||
@ -349,12 +447,18 @@ const LineNumberProgram = struct {
|
|||||||
self.prev_end_sequence = undefined;
|
self.prev_end_sequence = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(is_stmt: bool, include_dirs: []const []const u8, target_address: u64) LineNumberProgram {
|
pub fn init(
|
||||||
|
is_stmt: bool,
|
||||||
|
include_dirs: []const FileEntry,
|
||||||
|
target_address: u64,
|
||||||
|
version: u16,
|
||||||
|
) LineNumberProgram {
|
||||||
return LineNumberProgram{
|
return LineNumberProgram{
|
||||||
.address = 0,
|
.address = 0,
|
||||||
.file = 1,
|
.file = 1,
|
||||||
.line = 1,
|
.line = 1,
|
||||||
.column = 0,
|
.column = 0,
|
||||||
|
.version = version,
|
||||||
.is_stmt = is_stmt,
|
.is_stmt = is_stmt,
|
||||||
.basic_block = false,
|
.basic_block = false,
|
||||||
.end_sequence = false,
|
.end_sequence = false,
|
||||||
@ -377,18 +481,24 @@ const LineNumberProgram = struct {
|
|||||||
allocator: mem.Allocator,
|
allocator: mem.Allocator,
|
||||||
file_entries: []const FileEntry,
|
file_entries: []const FileEntry,
|
||||||
) !?debug.LineInfo {
|
) !?debug.LineInfo {
|
||||||
if (self.prev_valid and self.target_address >= self.prev_address and self.target_address < self.address) {
|
if (self.prev_valid and
|
||||||
const file_entry = if (self.prev_file == 0) {
|
self.target_address >= self.prev_address and
|
||||||
return error.MissingDebugInfo;
|
self.target_address < self.address)
|
||||||
} else if (self.prev_file - 1 >= file_entries.len) {
|
{
|
||||||
return error.InvalidDebugInfo;
|
const file_index = if (self.version >= 5) self.prev_file else i: {
|
||||||
} else &file_entries[self.prev_file - 1];
|
if (self.prev_file == 0) return missingDwarf();
|
||||||
|
break :i self.prev_file - 1;
|
||||||
|
};
|
||||||
|
|
||||||
const dir_name = if (file_entry.dir_index >= self.include_dirs.len) {
|
if (file_index >= file_entries.len) return badDwarf();
|
||||||
return error.InvalidDebugInfo;
|
const file_entry = &file_entries[file_index];
|
||||||
} else self.include_dirs[file_entry.dir_index];
|
|
||||||
|
|
||||||
const file_name = try fs.path.join(allocator, &[_][]const u8{ dir_name, file_entry.file_name });
|
if (file_entry.dir_index >= self.include_dirs.len) return badDwarf();
|
||||||
|
const dir_name = self.include_dirs[file_entry.dir_index].path;
|
||||||
|
|
||||||
|
const file_name = try fs.path.join(allocator, &[_][]const u8{
|
||||||
|
dir_name, file_entry.path,
|
||||||
|
});
|
||||||
|
|
||||||
return debug.LineInfo{
|
return debug.LineInfo{
|
||||||
.line = if (self.prev_line >= 0) @intCast(u64, self.prev_line) else 0,
|
.line = if (self.prev_line >= 0) @intCast(u64, self.prev_line) else 0,
|
||||||
@ -415,7 +525,7 @@ fn readUnitLength(in_stream: anytype, endian: std.builtin.Endian, is_64: *bool)
|
|||||||
if (is_64.*) {
|
if (is_64.*) {
|
||||||
return in_stream.readInt(u64, endian);
|
return in_stream.readInt(u64, endian);
|
||||||
} else {
|
} else {
|
||||||
if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo;
|
if (first_32_bits >= 0xfffffff0) return badDwarf();
|
||||||
// TODO this cast should not be needed
|
// TODO this cast should not be needed
|
||||||
return @as(u64, first_32_bits);
|
return @as(u64, first_32_bits);
|
||||||
}
|
}
|
||||||
@ -492,6 +602,12 @@ fn parseFormValueRef(in_stream: anytype, endian: std.builtin.Endian, size: i32)
|
|||||||
fn parseFormValue(allocator: mem.Allocator, in_stream: anytype, form_id: u64, endian: std.builtin.Endian, is_64: bool) anyerror!FormValue {
|
fn parseFormValue(allocator: mem.Allocator, in_stream: anytype, form_id: u64, endian: std.builtin.Endian, is_64: bool) anyerror!FormValue {
|
||||||
return switch (form_id) {
|
return switch (form_id) {
|
||||||
FORM.addr => FormValue{ .Address = try readAddress(in_stream, endian, @sizeOf(usize) == 8) },
|
FORM.addr => FormValue{ .Address = try readAddress(in_stream, endian, @sizeOf(usize) == 8) },
|
||||||
|
FORM.addrx1 => return FormValue{ .AddrOffset = try in_stream.readInt(u8, endian) },
|
||||||
|
FORM.addrx2 => return FormValue{ .AddrOffset = try in_stream.readInt(u16, endian) },
|
||||||
|
FORM.addrx3 => return FormValue{ .AddrOffset = try in_stream.readInt(u24, endian) },
|
||||||
|
FORM.addrx4 => return FormValue{ .AddrOffset = try in_stream.readInt(u32, endian) },
|
||||||
|
FORM.addrx => return FormValue{ .AddrOffset = try nosuspend leb.readULEB128(u64, in_stream) },
|
||||||
|
|
||||||
FORM.block1 => parseFormValueBlock(allocator, in_stream, endian, 1),
|
FORM.block1 => parseFormValueBlock(allocator, in_stream, endian, 1),
|
||||||
FORM.block2 => parseFormValueBlock(allocator, in_stream, endian, 2),
|
FORM.block2 => parseFormValueBlock(allocator, in_stream, endian, 2),
|
||||||
FORM.block4 => parseFormValueBlock(allocator, in_stream, endian, 4),
|
FORM.block4 => parseFormValueBlock(allocator, in_stream, endian, 4),
|
||||||
@ -527,10 +643,11 @@ fn parseFormValue(allocator: mem.Allocator, in_stream: anytype, form_id: u64, en
|
|||||||
|
|
||||||
FORM.string => FormValue{ .String = try in_stream.readUntilDelimiterAlloc(allocator, 0, math.maxInt(usize)) },
|
FORM.string => FormValue{ .String = try in_stream.readUntilDelimiterAlloc(allocator, 0, math.maxInt(usize)) },
|
||||||
FORM.strp => FormValue{ .StrPtr = try readAddress(in_stream, endian, is_64) },
|
FORM.strp => FormValue{ .StrPtr = try readAddress(in_stream, endian, is_64) },
|
||||||
FORM.strx1 => return FormValue{ .StrOffset = @intCast(u64, try in_stream.readInt(u8, endian)) },
|
FORM.strx1 => return FormValue{ .StrOffset = try in_stream.readInt(u8, endian) },
|
||||||
FORM.strx2 => return FormValue{ .StrOffset = @intCast(u64, try in_stream.readInt(u16, endian)) },
|
FORM.strx2 => return FormValue{ .StrOffset = try in_stream.readInt(u16, endian) },
|
||||||
FORM.strx3 => return FormValue{ .StrOffset = @intCast(u64, try in_stream.readInt(u24, endian)) },
|
FORM.strx3 => return FormValue{ .StrOffset = try in_stream.readInt(u24, endian) },
|
||||||
FORM.strx4 => return FormValue{ .StrOffset = @intCast(u64, try in_stream.readInt(u32, endian)) },
|
FORM.strx4 => return FormValue{ .StrOffset = try in_stream.readInt(u32, endian) },
|
||||||
|
FORM.strx => return FormValue{ .StrOffset = try nosuspend leb.readULEB128(u64, in_stream) },
|
||||||
FORM.line_strp => FormValue{ .LineStrPtr = try readAddress(in_stream, endian, is_64) },
|
FORM.line_strp => FormValue{ .LineStrPtr = try readAddress(in_stream, endian, is_64) },
|
||||||
FORM.indirect => {
|
FORM.indirect => {
|
||||||
const child_form_id = try nosuspend leb.readULEB128(u64, in_stream);
|
const child_form_id = try nosuspend leb.readULEB128(u64, in_stream);
|
||||||
@ -543,9 +660,11 @@ fn parseFormValue(allocator: mem.Allocator, in_stream: anytype, form_id: u64, en
|
|||||||
return await @asyncCall(frame, {}, parseFormValue, .{ allocator, in_stream, child_form_id, endian, is_64 });
|
return await @asyncCall(frame, {}, parseFormValue, .{ allocator, in_stream, child_form_id, endian, is_64 });
|
||||||
},
|
},
|
||||||
FORM.implicit_const => FormValue{ .Const = Constant{ .signed = true, .payload = undefined } },
|
FORM.implicit_const => FormValue{ .Const = Constant{ .signed = true, .payload = undefined } },
|
||||||
|
FORM.loclistx => return FormValue{ .LocListOffset = try nosuspend leb.readULEB128(u64, in_stream) },
|
||||||
|
FORM.rnglistx => return FormValue{ .RangeListOffset = try nosuspend leb.readULEB128(u64, in_stream) },
|
||||||
else => {
|
else => {
|
||||||
return error.InvalidDebugInfo;
|
std.debug.print("unrecognized form id: {x}\n", .{form_id});
|
||||||
|
return badDwarf();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -567,6 +686,11 @@ pub const DwarfInfo = struct {
|
|||||||
debug_line: []const u8,
|
debug_line: []const u8,
|
||||||
debug_line_str: ?[]const u8,
|
debug_line_str: ?[]const u8,
|
||||||
debug_ranges: ?[]const u8,
|
debug_ranges: ?[]const u8,
|
||||||
|
debug_loclists: ?[]const u8,
|
||||||
|
debug_rnglists: ?[]const u8,
|
||||||
|
debug_addr: ?[]const u8,
|
||||||
|
debug_names: ?[]const u8,
|
||||||
|
debug_frame: ?[]const u8,
|
||||||
// Filled later by the initializer
|
// Filled later by the initializer
|
||||||
abbrev_table_list: std.ArrayListUnmanaged(AbbrevTableHeader) = .{},
|
abbrev_table_list: std.ArrayListUnmanaged(AbbrevTableHeader) = .{},
|
||||||
compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{},
|
compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{},
|
||||||
@ -602,7 +726,7 @@ pub const DwarfInfo = struct {
|
|||||||
|
|
||||||
fn scanAllFunctions(di: *DwarfInfo, allocator: mem.Allocator) !void {
|
fn scanAllFunctions(di: *DwarfInfo, allocator: mem.Allocator) !void {
|
||||||
var stream = io.fixedBufferStream(di.debug_info);
|
var stream = io.fixedBufferStream(di.debug_info);
|
||||||
const in = &stream.reader();
|
const in = stream.reader();
|
||||||
const seekable = &stream.seekableStream();
|
const seekable = &stream.seekableStream();
|
||||||
var this_unit_offset: u64 = 0;
|
var this_unit_offset: u64 = 0;
|
||||||
|
|
||||||
@ -619,29 +743,26 @@ pub const DwarfInfo = struct {
|
|||||||
const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
|
const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
|
||||||
|
|
||||||
const version = try in.readInt(u16, di.endian);
|
const version = try in.readInt(u16, di.endian);
|
||||||
if (version < 2 or version > 5) return error.InvalidDebugInfo;
|
if (version < 2 or version > 5) return badDwarf();
|
||||||
|
|
||||||
var address_size: u8 = undefined;
|
var address_size: u8 = undefined;
|
||||||
var debug_abbrev_offset: u64 = undefined;
|
var debug_abbrev_offset: u64 = undefined;
|
||||||
switch (version) {
|
if (version >= 5) {
|
||||||
5 => {
|
const unit_type = try in.readInt(u8, di.endian);
|
||||||
const unit_type = try in.readInt(u8, di.endian);
|
if (unit_type != UT.compile) return badDwarf();
|
||||||
if (unit_type != UT.compile) return error.InvalidDebugInfo;
|
address_size = try in.readByte();
|
||||||
address_size = try in.readByte();
|
debug_abbrev_offset = if (is_64)
|
||||||
debug_abbrev_offset = if (is_64)
|
try in.readInt(u64, di.endian)
|
||||||
try in.readInt(u64, di.endian)
|
else
|
||||||
else
|
try in.readInt(u32, di.endian);
|
||||||
try in.readInt(u32, di.endian);
|
} else {
|
||||||
},
|
debug_abbrev_offset = if (is_64)
|
||||||
else => {
|
try in.readInt(u64, di.endian)
|
||||||
debug_abbrev_offset = if (is_64)
|
else
|
||||||
try in.readInt(u64, di.endian)
|
try in.readInt(u32, di.endian);
|
||||||
else
|
address_size = try in.readByte();
|
||||||
try in.readInt(u32, di.endian);
|
|
||||||
address_size = try in.readByte();
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
|
if (address_size != @sizeOf(usize)) return badDwarf();
|
||||||
|
|
||||||
const compile_unit_pos = try seekable.getPos();
|
const compile_unit_pos = try seekable.getPos();
|
||||||
const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset);
|
const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset);
|
||||||
@ -662,30 +783,30 @@ pub const DwarfInfo = struct {
|
|||||||
// Prevent endless loops
|
// Prevent endless loops
|
||||||
while (depth > 0) : (depth -= 1) {
|
while (depth > 0) : (depth -= 1) {
|
||||||
if (this_die_obj.getAttr(AT.name)) |_| {
|
if (this_die_obj.getAttr(AT.name)) |_| {
|
||||||
const name = try this_die_obj.getAttrString(di, AT.name, is_64);
|
const name = try this_die_obj.getAttrString(di, AT.name, di.debug_str);
|
||||||
break :x try allocator.dupe(u8, name);
|
break :x try allocator.dupe(u8, name);
|
||||||
} else if (this_die_obj.getAttr(AT.abstract_origin)) |_| {
|
} else if (this_die_obj.getAttr(AT.abstract_origin)) |_| {
|
||||||
// Follow the DIE it points to and repeat
|
// Follow the DIE it points to and repeat
|
||||||
const ref_offset = try this_die_obj.getAttrRef(AT.abstract_origin);
|
const ref_offset = try this_die_obj.getAttrRef(AT.abstract_origin);
|
||||||
if (ref_offset > next_offset) return error.InvalidDebugInfo;
|
if (ref_offset > next_offset) return badDwarf();
|
||||||
try seekable.seekTo(this_unit_offset + ref_offset);
|
try seekable.seekTo(this_unit_offset + ref_offset);
|
||||||
this_die_obj = (try di.parseDie(
|
this_die_obj = (try di.parseDie(
|
||||||
arena,
|
arena,
|
||||||
in,
|
in,
|
||||||
abbrev_table,
|
abbrev_table,
|
||||||
is_64,
|
is_64,
|
||||||
)) orelse return error.InvalidDebugInfo;
|
)) orelse return badDwarf();
|
||||||
} else if (this_die_obj.getAttr(AT.specification)) |_| {
|
} else if (this_die_obj.getAttr(AT.specification)) |_| {
|
||||||
// Follow the DIE it points to and repeat
|
// Follow the DIE it points to and repeat
|
||||||
const ref_offset = try this_die_obj.getAttrRef(AT.specification);
|
const ref_offset = try this_die_obj.getAttrRef(AT.specification);
|
||||||
if (ref_offset > next_offset) return error.InvalidDebugInfo;
|
if (ref_offset > next_offset) return badDwarf();
|
||||||
try seekable.seekTo(this_unit_offset + ref_offset);
|
try seekable.seekTo(this_unit_offset + ref_offset);
|
||||||
this_die_obj = (try di.parseDie(
|
this_die_obj = (try di.parseDie(
|
||||||
arena,
|
arena,
|
||||||
in,
|
in,
|
||||||
abbrev_table,
|
abbrev_table,
|
||||||
is_64,
|
is_64,
|
||||||
)) orelse return error.InvalidDebugInfo;
|
)) orelse return badDwarf();
|
||||||
} else {
|
} else {
|
||||||
break :x null;
|
break :x null;
|
||||||
}
|
}
|
||||||
@ -695,7 +816,7 @@ pub const DwarfInfo = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const pc_range = x: {
|
const pc_range = x: {
|
||||||
if (die_obj.getAttrAddr(AT.low_pc)) |low_pc| {
|
if (die_obj.getAttrAddr(di, AT.low_pc)) |low_pc| {
|
||||||
if (die_obj.getAttr(AT.high_pc)) |high_pc_value| {
|
if (die_obj.getAttr(AT.high_pc)) |high_pc_value| {
|
||||||
const pc_end = switch (high_pc_value.*) {
|
const pc_end = switch (high_pc_value.*) {
|
||||||
FormValue.Address => |value| value,
|
FormValue.Address => |value| value,
|
||||||
@ -703,7 +824,7 @@ pub const DwarfInfo = struct {
|
|||||||
const offset = try value.asUnsignedLe();
|
const offset = try value.asUnsignedLe();
|
||||||
break :b (low_pc + offset);
|
break :b (low_pc + offset);
|
||||||
},
|
},
|
||||||
else => return error.InvalidDebugInfo,
|
else => return badDwarf(),
|
||||||
};
|
};
|
||||||
break :x PcRange{
|
break :x PcRange{
|
||||||
.start = low_pc,
|
.start = low_pc,
|
||||||
@ -748,14 +869,14 @@ pub const DwarfInfo = struct {
|
|||||||
const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
|
const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
|
||||||
|
|
||||||
const version = try in.readInt(u16, di.endian);
|
const version = try in.readInt(u16, di.endian);
|
||||||
if (version < 2 or version > 5) return error.InvalidDebugInfo;
|
if (version < 2 or version > 5) return badDwarf();
|
||||||
|
|
||||||
var address_size: u8 = undefined;
|
var address_size: u8 = undefined;
|
||||||
var debug_abbrev_offset: u64 = undefined;
|
var debug_abbrev_offset: u64 = undefined;
|
||||||
switch (version) {
|
switch (version) {
|
||||||
5 => {
|
5 => {
|
||||||
const unit_type = try in.readInt(u8, di.endian);
|
const unit_type = try in.readInt(u8, di.endian);
|
||||||
if (unit_type != UT.compile) return error.InvalidDebugInfo;
|
if (unit_type != UT.compile) return badDwarf();
|
||||||
address_size = try in.readByte();
|
address_size = try in.readByte();
|
||||||
debug_abbrev_offset = if (is_64)
|
debug_abbrev_offset = if (is_64)
|
||||||
try in.readInt(u64, di.endian)
|
try in.readInt(u64, di.endian)
|
||||||
@ -770,7 +891,7 @@ pub const DwarfInfo = struct {
|
|||||||
address_size = try in.readByte();
|
address_size = try in.readByte();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
|
if (address_size != @sizeOf(usize)) return badDwarf();
|
||||||
|
|
||||||
const compile_unit_pos = try seekable.getPos();
|
const compile_unit_pos = try seekable.getPos();
|
||||||
const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset);
|
const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset);
|
||||||
@ -780,12 +901,12 @@ pub const DwarfInfo = struct {
|
|||||||
const compile_unit_die = try allocator.create(Die);
|
const compile_unit_die = try allocator.create(Die);
|
||||||
errdefer allocator.destroy(compile_unit_die);
|
errdefer allocator.destroy(compile_unit_die);
|
||||||
compile_unit_die.* = (try di.parseDie(allocator, in, abbrev_table, is_64)) orelse
|
compile_unit_die.* = (try di.parseDie(allocator, in, abbrev_table, is_64)) orelse
|
||||||
return error.InvalidDebugInfo;
|
return badDwarf();
|
||||||
|
|
||||||
if (compile_unit_die.tag_id != TAG.compile_unit) return error.InvalidDebugInfo;
|
if (compile_unit_die.tag_id != TAG.compile_unit) return badDwarf();
|
||||||
|
|
||||||
const pc_range = x: {
|
const pc_range = x: {
|
||||||
if (compile_unit_die.getAttrAddr(AT.low_pc)) |low_pc| {
|
if (compile_unit_die.getAttrAddr(di, AT.low_pc)) |low_pc| {
|
||||||
if (compile_unit_die.getAttr(AT.high_pc)) |high_pc_value| {
|
if (compile_unit_die.getAttr(AT.high_pc)) |high_pc_value| {
|
||||||
const pc_end = switch (high_pc_value.*) {
|
const pc_end = switch (high_pc_value.*) {
|
||||||
FormValue.Address => |value| value,
|
FormValue.Address => |value| value,
|
||||||
@ -793,7 +914,7 @@ pub const DwarfInfo = struct {
|
|||||||
const offset = try value.asUnsignedLe();
|
const offset = try value.asUnsignedLe();
|
||||||
break :b (low_pc + offset);
|
break :b (low_pc + offset);
|
||||||
},
|
},
|
||||||
else => return error.InvalidDebugInfo,
|
else => return badDwarf(),
|
||||||
};
|
};
|
||||||
break :x PcRange{
|
break :x PcRange{
|
||||||
.start = low_pc,
|
.start = low_pc,
|
||||||
@ -834,7 +955,7 @@ pub const DwarfInfo = struct {
|
|||||||
// specified by DW_AT.low_pc or to some other value encoded
|
// specified by DW_AT.low_pc or to some other value encoded
|
||||||
// in the list itself.
|
// in the list itself.
|
||||||
// If no starting value is specified use zero.
|
// If no starting value is specified use zero.
|
||||||
var base_address = compile_unit.die.getAttrAddr(AT.low_pc) catch |err| switch (err) {
|
var base_address = compile_unit.die.getAttrAddr(di, AT.low_pc) catch |err| switch (err) {
|
||||||
error.MissingDebugInfo => @as(u64, 0), // TODO https://github.com/ziglang/zig/issues/11135
|
error.MissingDebugInfo => @as(u64, 0), // TODO https://github.com/ziglang/zig/issues/11135
|
||||||
else => return err,
|
else => return err,
|
||||||
};
|
};
|
||||||
@ -862,7 +983,7 @@ pub const DwarfInfo = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return error.MissingDebugInfo;
|
return missingDwarf();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
|
/// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
|
||||||
@ -929,7 +1050,7 @@ pub const DwarfInfo = struct {
|
|||||||
) !?Die {
|
) !?Die {
|
||||||
const abbrev_code = try leb.readULEB128(u64, in_stream);
|
const abbrev_code = try leb.readULEB128(u64, in_stream);
|
||||||
if (abbrev_code == 0) return null;
|
if (abbrev_code == 0) return null;
|
||||||
const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
|
const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return badDwarf();
|
||||||
|
|
||||||
var result = Die{
|
var result = Die{
|
||||||
// Lives as long as the Die.
|
// Lives as long as the Die.
|
||||||
@ -966,7 +1087,7 @@ pub const DwarfInfo = struct {
|
|||||||
const in = &stream.reader();
|
const in = &stream.reader();
|
||||||
const seekable = &stream.seekableStream();
|
const seekable = &stream.seekableStream();
|
||||||
|
|
||||||
const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT.comp_dir, compile_unit.is_64);
|
const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT.comp_dir, di.debug_line_str);
|
||||||
const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list);
|
const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list);
|
||||||
|
|
||||||
try seekable.seekTo(line_info_offset);
|
try seekable.seekTo(line_info_offset);
|
||||||
@ -974,18 +1095,25 @@ pub const DwarfInfo = struct {
|
|||||||
var is_64: bool = undefined;
|
var is_64: bool = undefined;
|
||||||
const unit_length = try readUnitLength(in, di.endian, &is_64);
|
const unit_length = try readUnitLength(in, di.endian, &is_64);
|
||||||
if (unit_length == 0) {
|
if (unit_length == 0) {
|
||||||
return error.MissingDebugInfo;
|
return missingDwarf();
|
||||||
}
|
}
|
||||||
const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
|
const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
|
||||||
|
|
||||||
const version = try in.readInt(u16, di.endian);
|
const version = try in.readInt(u16, di.endian);
|
||||||
if (version < 2 or version > 4) return error.InvalidDebugInfo;
|
if (version < 2) return badDwarf();
|
||||||
|
|
||||||
|
var addr_size: u8 = if (is_64) 8 else 4;
|
||||||
|
var seg_size: u8 = 0;
|
||||||
|
if (version >= 5) {
|
||||||
|
addr_size = try in.readByte();
|
||||||
|
seg_size = try in.readByte();
|
||||||
|
}
|
||||||
|
|
||||||
const prologue_length = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian);
|
const prologue_length = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian);
|
||||||
const prog_start_offset = (try seekable.getPos()) + prologue_length;
|
const prog_start_offset = (try seekable.getPos()) + prologue_length;
|
||||||
|
|
||||||
const minimum_instruction_length = try in.readByte();
|
const minimum_instruction_length = try in.readByte();
|
||||||
if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
|
if (minimum_instruction_length == 0) return badDwarf();
|
||||||
|
|
||||||
if (version >= 4) {
|
if (version >= 4) {
|
||||||
// maximum_operations_per_instruction
|
// maximum_operations_per_instruction
|
||||||
@ -996,7 +1124,7 @@ pub const DwarfInfo = struct {
|
|||||||
const line_base = try in.readByteSigned();
|
const line_base = try in.readByteSigned();
|
||||||
|
|
||||||
const line_range = try in.readByte();
|
const line_range = try in.readByte();
|
||||||
if (line_range == 0) return error.InvalidDebugInfo;
|
if (line_range == 0) return badDwarf();
|
||||||
|
|
||||||
const opcode_base = try in.readByte();
|
const opcode_base = try in.readByte();
|
||||||
|
|
||||||
@ -1014,36 +1142,120 @@ pub const DwarfInfo = struct {
|
|||||||
defer tmp_arena.deinit();
|
defer tmp_arena.deinit();
|
||||||
const arena = tmp_arena.allocator();
|
const arena = tmp_arena.allocator();
|
||||||
|
|
||||||
var include_directories = std.ArrayList([]const u8).init(arena);
|
var include_directories = std.ArrayList(FileEntry).init(arena);
|
||||||
try include_directories.append(compile_unit_cwd);
|
var file_entries = std.ArrayList(FileEntry).init(arena);
|
||||||
|
|
||||||
while (true) {
|
if (version < 5) {
|
||||||
const dir = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize));
|
try include_directories.append(.{ .path = compile_unit_cwd });
|
||||||
if (dir.len == 0) break;
|
|
||||||
try include_directories.append(dir);
|
while (true) {
|
||||||
|
const dir = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize));
|
||||||
|
if (dir.len == 0) break;
|
||||||
|
try include_directories.append(.{ .path = dir });
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const file_name = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize));
|
||||||
|
if (file_name.len == 0) break;
|
||||||
|
const dir_index = try leb.readULEB128(u32, in);
|
||||||
|
const mtime = try leb.readULEB128(u64, in);
|
||||||
|
const size = try leb.readULEB128(u64, in);
|
||||||
|
try file_entries.append(FileEntry{
|
||||||
|
.path = file_name,
|
||||||
|
.dir_index = dir_index,
|
||||||
|
.mtime = mtime,
|
||||||
|
.size = size,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const FileEntFmt = struct {
|
||||||
|
content_type_code: u8,
|
||||||
|
form_code: u16,
|
||||||
|
};
|
||||||
|
{
|
||||||
|
var dir_ent_fmt_buf: [10]FileEntFmt = undefined;
|
||||||
|
const directory_entry_format_count = try in.readByte();
|
||||||
|
if (directory_entry_format_count > dir_ent_fmt_buf.len) return badDwarf();
|
||||||
|
for (dir_ent_fmt_buf[0..directory_entry_format_count]) |*ent_fmt| {
|
||||||
|
ent_fmt.* = .{
|
||||||
|
.content_type_code = try leb.readULEB128(u8, in),
|
||||||
|
.form_code = try leb.readULEB128(u16, in),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const directories_count = try leb.readULEB128(usize, in);
|
||||||
|
try include_directories.ensureUnusedCapacity(directories_count);
|
||||||
|
{
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < directories_count) : (i += 1) {
|
||||||
|
var e: FileEntry = .{ .path = &.{} };
|
||||||
|
for (dir_ent_fmt_buf[0..directory_entry_format_count]) |ent_fmt| {
|
||||||
|
const form_value = try parseFormValue(
|
||||||
|
arena,
|
||||||
|
in,
|
||||||
|
ent_fmt.form_code,
|
||||||
|
di.endian,
|
||||||
|
is_64,
|
||||||
|
);
|
||||||
|
switch (ent_fmt.content_type_code) {
|
||||||
|
LNCT.path => e.path = try form_value.getString(di.*),
|
||||||
|
LNCT.directory_index => e.dir_index = try form_value.getUInt(u32),
|
||||||
|
LNCT.timestamp => e.mtime = try form_value.getUInt(u64),
|
||||||
|
LNCT.size => e.size = try form_value.getUInt(u64),
|
||||||
|
LNCT.MD5 => e.md5 = try form_value.getUInt(u128),
|
||||||
|
else => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
include_directories.appendAssumeCapacity(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_ent_fmt_buf: [10]FileEntFmt = undefined;
|
||||||
|
const file_name_entry_format_count = try in.readByte();
|
||||||
|
if (file_name_entry_format_count > file_ent_fmt_buf.len) return badDwarf();
|
||||||
|
for (file_ent_fmt_buf[0..file_name_entry_format_count]) |*ent_fmt| {
|
||||||
|
ent_fmt.* = .{
|
||||||
|
.content_type_code = try leb.readULEB128(u8, in),
|
||||||
|
.form_code = try leb.readULEB128(u16, in),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const file_names_count = try leb.readULEB128(usize, in);
|
||||||
|
try file_entries.ensureUnusedCapacity(file_names_count);
|
||||||
|
{
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < file_names_count) : (i += 1) {
|
||||||
|
var e: FileEntry = .{ .path = &.{} };
|
||||||
|
for (file_ent_fmt_buf[0..file_name_entry_format_count]) |ent_fmt| {
|
||||||
|
const form_value = try parseFormValue(
|
||||||
|
arena,
|
||||||
|
in,
|
||||||
|
ent_fmt.form_code,
|
||||||
|
di.endian,
|
||||||
|
is_64,
|
||||||
|
);
|
||||||
|
switch (ent_fmt.content_type_code) {
|
||||||
|
LNCT.path => e.path = try form_value.getString(di.*),
|
||||||
|
LNCT.directory_index => e.dir_index = try form_value.getUInt(u32),
|
||||||
|
LNCT.timestamp => e.mtime = try form_value.getUInt(u64),
|
||||||
|
LNCT.size => e.size = try form_value.getUInt(u64),
|
||||||
|
LNCT.MD5 => e.md5 = try form_value.getUInt(u128),
|
||||||
|
else => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_entries.appendAssumeCapacity(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_entries = std.ArrayList(FileEntry).init(arena);
|
|
||||||
var prog = LineNumberProgram.init(
|
var prog = LineNumberProgram.init(
|
||||||
default_is_stmt,
|
default_is_stmt,
|
||||||
include_directories.items,
|
include_directories.items,
|
||||||
target_address,
|
target_address,
|
||||||
|
version,
|
||||||
);
|
);
|
||||||
|
|
||||||
while (true) {
|
|
||||||
const file_name = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize));
|
|
||||||
if (file_name.len == 0) break;
|
|
||||||
const dir_index = try leb.readULEB128(usize, in);
|
|
||||||
const mtime = try leb.readULEB128(usize, in);
|
|
||||||
const len_bytes = try leb.readULEB128(usize, in);
|
|
||||||
try file_entries.append(FileEntry{
|
|
||||||
.file_name = file_name,
|
|
||||||
.dir_index = dir_index,
|
|
||||||
.mtime = mtime,
|
|
||||||
.len_bytes = len_bytes,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
try seekable.seekTo(prog_start_offset);
|
try seekable.seekTo(prog_start_offset);
|
||||||
|
|
||||||
const next_unit_pos = line_info_offset + next_offset;
|
const next_unit_pos = line_info_offset + next_offset;
|
||||||
@ -1053,7 +1265,7 @@ pub const DwarfInfo = struct {
|
|||||||
|
|
||||||
if (opcode == LNS.extended_op) {
|
if (opcode == LNS.extended_op) {
|
||||||
const op_size = try leb.readULEB128(u64, in);
|
const op_size = try leb.readULEB128(u64, in);
|
||||||
if (op_size < 1) return error.InvalidDebugInfo;
|
if (op_size < 1) return badDwarf();
|
||||||
var sub_op = try in.readByte();
|
var sub_op = try in.readByte();
|
||||||
switch (sub_op) {
|
switch (sub_op) {
|
||||||
LNE.end_sequence => {
|
LNE.end_sequence => {
|
||||||
@ -1066,19 +1278,19 @@ pub const DwarfInfo = struct {
|
|||||||
prog.address = addr;
|
prog.address = addr;
|
||||||
},
|
},
|
||||||
LNE.define_file => {
|
LNE.define_file => {
|
||||||
const file_name = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize));
|
const path = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize));
|
||||||
const dir_index = try leb.readULEB128(usize, in);
|
const dir_index = try leb.readULEB128(u32, in);
|
||||||
const mtime = try leb.readULEB128(usize, in);
|
const mtime = try leb.readULEB128(u64, in);
|
||||||
const len_bytes = try leb.readULEB128(usize, in);
|
const size = try leb.readULEB128(u64, in);
|
||||||
try file_entries.append(FileEntry{
|
try file_entries.append(FileEntry{
|
||||||
.file_name = file_name,
|
.path = path,
|
||||||
.dir_index = dir_index,
|
.dir_index = dir_index,
|
||||||
.mtime = mtime,
|
.mtime = mtime,
|
||||||
.len_bytes = len_bytes,
|
.size = size,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
else => {
|
else => {
|
||||||
const fwd_amt = math.cast(isize, op_size - 1) orelse return error.InvalidDebugInfo;
|
const fwd_amt = math.cast(isize, op_size - 1) orelse return badDwarf();
|
||||||
try seekable.seekBy(fwd_amt);
|
try seekable.seekBy(fwd_amt);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1129,7 +1341,7 @@ pub const DwarfInfo = struct {
|
|||||||
},
|
},
|
||||||
LNS.set_prologue_end => {},
|
LNS.set_prologue_end => {},
|
||||||
else => {
|
else => {
|
||||||
if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
|
if (opcode - 1 >= standard_opcode_lengths.len) return badDwarf();
|
||||||
const len_bytes = standard_opcode_lengths[opcode - 1];
|
const len_bytes = standard_opcode_lengths[opcode - 1];
|
||||||
try seekable.seekBy(len_bytes);
|
try seekable.seekBy(len_bytes);
|
||||||
},
|
},
|
||||||
@ -1137,50 +1349,15 @@ pub const DwarfInfo = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return error.MissingDebugInfo;
|
return missingDwarf();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getString(di: *DwarfInfo, offset: u64) ![]const u8 {
|
fn getString(di: DwarfInfo, offset: u64) ![]const u8 {
|
||||||
if (offset > di.debug_str.len)
|
return getStringGeneric(di.debug_str, offset);
|
||||||
return error.InvalidDebugInfo;
|
|
||||||
const casted_offset = math.cast(usize, offset) orelse
|
|
||||||
return error.InvalidDebugInfo;
|
|
||||||
|
|
||||||
// Valid strings always have a terminating zero byte
|
|
||||||
if (mem.indexOfScalarPos(u8, di.debug_str, casted_offset, 0)) |last| {
|
|
||||||
return di.debug_str[casted_offset..last];
|
|
||||||
}
|
|
||||||
|
|
||||||
return error.InvalidDebugInfo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getStringOffset(di: *DwarfInfo, offset: u64, is_64: bool) !u64 {
|
fn getLineString(di: DwarfInfo, offset: u64) ![]const u8 {
|
||||||
if (di.debug_str_offsets) |debug_str_offsets| {
|
return getStringGeneric(di.debug_line_str, offset);
|
||||||
if (offset >= debug_str_offsets.len) {
|
|
||||||
return error.InvalidDebugInfo;
|
|
||||||
}
|
|
||||||
const offset_casted = @intCast(usize, offset);
|
|
||||||
if (is_64) {
|
|
||||||
return std.mem.readIntSlice(u64, debug_str_offsets[offset_casted .. offset_casted + 8], di.endian);
|
|
||||||
}
|
|
||||||
return @intCast(u64, std.mem.readIntSlice(u32, debug_str_offsets[offset_casted .. offset_casted + 4], di.endian));
|
|
||||||
}
|
|
||||||
return error.InvalidDebugInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn getLineString(di: *DwarfInfo, offset: u64) ![]const u8 {
|
|
||||||
const debug_line_str = di.debug_line_str orelse return error.InvalidDebugInfo;
|
|
||||||
if (offset > debug_line_str.len)
|
|
||||||
return error.InvalidDebugInfo;
|
|
||||||
const casted_offset = math.cast(usize, offset) orelse
|
|
||||||
return error.InvalidDebugInfo;
|
|
||||||
|
|
||||||
// Valid strings always have a terminating zero byte
|
|
||||||
if (mem.indexOfScalarPos(u8, debug_line_str, casted_offset, 0)) |last| {
|
|
||||||
return debug_line_str[casted_offset..last];
|
|
||||||
}
|
|
||||||
|
|
||||||
return error.InvalidDebugInfo;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1190,3 +1367,24 @@ pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator) !void {
|
|||||||
try di.scanAllFunctions(allocator);
|
try di.scanAllFunctions(allocator);
|
||||||
try di.scanAllCompileUnits(allocator);
|
try di.scanAllCompileUnits(allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This function is to make it handy to comment out the return and make it
|
||||||
|
/// into a crash when working on this file.
|
||||||
|
inline fn badDwarf() error{InvalidDebugInfo} {
|
||||||
|
//std.os.abort(); // can be handy to uncomment when working on this file
|
||||||
|
return error.InvalidDebugInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn missingDwarf() error{MissingDebugInfo} {
|
||||||
|
//std.os.abort(); // can be handy to uncomment when working on this file
|
||||||
|
return error.MissingDebugInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 {
|
||||||
|
const str = opt_str orelse return badDwarf();
|
||||||
|
if (offset > str.len) return badDwarf();
|
||||||
|
const casted_offset = math.cast(usize, offset) orelse return badDwarf();
|
||||||
|
// Valid strings always have a terminating zero byte
|
||||||
|
const last = mem.indexOfScalarPos(u8, str, casted_offset, 0) orelse return badDwarf();
|
||||||
|
return str[casted_offset..last :0];
|
||||||
|
}
|
||||||
|
@ -5862,8 +5862,8 @@ pub fn generateSymbolStabs(
|
|||||||
else => |e| return e,
|
else => |e| return e,
|
||||||
};
|
};
|
||||||
|
|
||||||
const tu_name = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.name, compile_unit.is_64);
|
const tu_name = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.name, debug_info.debug_str);
|
||||||
const tu_comp_dir = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.comp_dir, compile_unit.is_64);
|
const tu_comp_dir = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.comp_dir, debug_info.debug_str);
|
||||||
|
|
||||||
// Open scope
|
// Open scope
|
||||||
try locals.ensureUnusedCapacity(3);
|
try locals.ensureUnusedCapacity(3);
|
||||||
|
@ -584,6 +584,11 @@ pub fn parseDwarfInfo(self: Object) error{Overflow}!dwarf.DwarfInfo {
|
|||||||
.debug_line = &[0]u8{},
|
.debug_line = &[0]u8{},
|
||||||
.debug_line_str = &[0]u8{},
|
.debug_line_str = &[0]u8{},
|
||||||
.debug_ranges = &[0]u8{},
|
.debug_ranges = &[0]u8{},
|
||||||
|
.debug_loclists = &[0]u8{},
|
||||||
|
.debug_rnglists = &[0]u8{},
|
||||||
|
.debug_addr = &[0]u8{},
|
||||||
|
.debug_names = &[0]u8{},
|
||||||
|
.debug_frame = &[0]u8{},
|
||||||
};
|
};
|
||||||
for (self.sections.items) |sect| {
|
for (self.sections.items) |sect| {
|
||||||
const segname = sect.segName();
|
const segname = sect.segName();
|
||||||
@ -601,6 +606,16 @@ pub fn parseDwarfInfo(self: Object) error{Overflow}!dwarf.DwarfInfo {
|
|||||||
di.debug_line_str = try self.getSectionContents(sect);
|
di.debug_line_str = try self.getSectionContents(sect);
|
||||||
} else if (mem.eql(u8, sectname, "__debug_ranges")) {
|
} else if (mem.eql(u8, sectname, "__debug_ranges")) {
|
||||||
di.debug_ranges = try self.getSectionContents(sect);
|
di.debug_ranges = try self.getSectionContents(sect);
|
||||||
|
} else if (mem.eql(u8, sectname, "__debug_loclists")) {
|
||||||
|
di.debug_loclists = try self.getSectionContents(sect);
|
||||||
|
} else if (mem.eql(u8, sectname, "__debug_rnglists")) {
|
||||||
|
di.debug_rnglists = try self.getSectionContents(sect);
|
||||||
|
} else if (mem.eql(u8, sectname, "__debug_addr")) {
|
||||||
|
di.debug_addr = try self.getSectionContents(sect);
|
||||||
|
} else if (mem.eql(u8, sectname, "__debug_names")) {
|
||||||
|
di.debug_names = try self.getSectionContents(sect);
|
||||||
|
} else if (mem.eql(u8, sectname, "__debug_frame")) {
|
||||||
|
di.debug_frame = try self.getSectionContents(sect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
|
|||||||
},
|
},
|
||||||
.ReleaseSafe = .{
|
.ReleaseSafe = .{
|
||||||
.exclude_os = .{
|
.exclude_os = .{
|
||||||
.windows, // segfault
|
.windows, // TODO
|
||||||
.linux, // defeated by aggressive inlining
|
.linux, // defeated by aggressive inlining
|
||||||
},
|
},
|
||||||
.expect =
|
.expect =
|
||||||
@ -71,7 +71,8 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
|
|||||||
},
|
},
|
||||||
.ReleaseSafe = .{
|
.ReleaseSafe = .{
|
||||||
.exclude_os = .{
|
.exclude_os = .{
|
||||||
.windows, // segfault
|
.windows, // TODO
|
||||||
|
.linux, // TODO
|
||||||
},
|
},
|
||||||
.expect =
|
.expect =
|
||||||
\\error: TheSkyIsFalling
|
\\error: TheSkyIsFalling
|
||||||
@ -137,7 +138,8 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
|
|||||||
},
|
},
|
||||||
.ReleaseSafe = .{
|
.ReleaseSafe = .{
|
||||||
.exclude_os = .{
|
.exclude_os = .{
|
||||||
.windows, // segfault
|
.windows, // TODO
|
||||||
|
.linux, // TODO
|
||||||
},
|
},
|
||||||
.expect =
|
.expect =
|
||||||
\\error: TheSkyIsFalling
|
\\error: TheSkyIsFalling
|
||||||
|
Loading…
Reference in New Issue
Block a user