runLineNumberProgram
Ensures that addresses in the returned LineTable are monotonically increasing.
Function parameters
Parameters
Type definitions in this namespace
Types
- ExceptionFrameHeader
- This represents the decoded .eh_frame_hdr header
Initialize DWARF info.
Functions
- open
- Initialize DWARF info.
- findCompileUnit
- TODO: change this to binary searching the sorted compile unit list
- scanAllUnwindInfo
- If `.eh_frame_hdr` is present, then only the header needs to be parsed.
- scanCieFdeInfo
- Scan `.eh_frame` and `.debug_frame` and build a sorted list of FDEs for binary searching during
- compactUnwindToDwarfRegNumber
- Returns the DWARF register number for an x86_64 register number found in compact unwind info
- bad
- This function is to make it handy to comment out the return and make it
Error sets in this namespace
Error Sets
= [_]?Section{null} ** num_sections
Values
- null_section_array
- = [_]?Section{null} ** num_sections
Source
Implementation
fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, compile_unit: *CompileUnit) !CompileUnit.SrcLocCache {
const compile_unit_cwd = try compile_unit.die.getAttrString(d, AT.comp_dir, d.section(.debug_line_str), compile_unit.*);
const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list);
var fbr: FixedBufferReader = .{
.buf = d.section(.debug_line).?,
.endian = d.endian,
};
try fbr.seekTo(line_info_offset);
const unit_header = try readUnitHeader(&fbr, null);
if (unit_header.unit_length == 0) return missing();
const next_offset = unit_header.header_length + unit_header.unit_length;
const version = try fbr.readInt(u16);
if (version < 2) return bad();
const addr_size: u8, const seg_size: u8 = if (version >= 5) .{
try fbr.readByte(),
try fbr.readByte(),
} else .{
switch (unit_header.format) {
.@"32" => 4,
.@"64" => 8,
},
0,
};
_ = addr_size;
_ = seg_size;
const prologue_length = try fbr.readAddress(unit_header.format);
const prog_start_offset = fbr.pos + prologue_length;
const minimum_instruction_length = try fbr.readByte();
if (minimum_instruction_length == 0) return bad();
if (version >= 4) {
const maximum_operations_per_instruction = try fbr.readByte();
_ = maximum_operations_per_instruction;
}
const default_is_stmt = (try fbr.readByte()) != 0;
const line_base = try fbr.readByteSigned();
const line_range = try fbr.readByte();
if (line_range == 0) return bad();
const opcode_base = try fbr.readByte();
const standard_opcode_lengths = try fbr.readBytes(opcode_base - 1);
var directories: ArrayList(FileEntry) = .empty;
defer directories.deinit(gpa);
var file_entries: ArrayList(FileEntry) = .empty;
defer file_entries.deinit(gpa);
if (version < 5) {
try directories.append(gpa, .{ .path = compile_unit_cwd });
while (true) {
const dir = try fbr.readBytesTo(0);
if (dir.len == 0) break;
try directories.append(gpa, .{ .path = dir });
}
while (true) {
const file_name = try fbr.readBytesTo(0);
if (file_name.len == 0) break;
const dir_index = try fbr.readUleb128(u32);
const mtime = try fbr.readUleb128(u64);
const size = try fbr.readUleb128(u64);
try file_entries.append(gpa, .{
.path = file_name,
.dir_index = dir_index,
.mtime = mtime,
.size = size,
});
}
} else {
const FileEntFmt = struct {
content_type_code: u16,
form_code: u16,
};
{
var dir_ent_fmt_buf: [10]FileEntFmt = undefined;
const directory_entry_format_count = try fbr.readByte();
if (directory_entry_format_count > dir_ent_fmt_buf.len) return bad();
for (dir_ent_fmt_buf[0..directory_entry_format_count]) |*ent_fmt| {
ent_fmt.* = .{
.content_type_code = try fbr.readUleb128(u8),
.form_code = try fbr.readUleb128(u16),
};
}
const directories_count = try fbr.readUleb128(usize);
for (try directories.addManyAsSlice(gpa, directories_count)) |*e| {
e.* = .{ .path = &.{} };
for (dir_ent_fmt_buf[0..directory_entry_format_count]) |ent_fmt| {
const form_value = try parseFormValue(
&fbr,
ent_fmt.form_code,
unit_header.format,
null,
);
switch (ent_fmt.content_type_code) {
DW.LNCT.path => e.path = try form_value.getString(d.*),
DW.LNCT.directory_index => e.dir_index = try form_value.getUInt(u32),
DW.LNCT.timestamp => e.mtime = try form_value.getUInt(u64),
DW.LNCT.size => e.size = try form_value.getUInt(u64),
DW.LNCT.MD5 => e.md5 = switch (form_value) {
.data16 => |data16| data16.*,
else => return bad(),
},
else => continue,
}
}
}
}
var file_ent_fmt_buf: [10]FileEntFmt = undefined;
const file_name_entry_format_count = try fbr.readByte();
if (file_name_entry_format_count > file_ent_fmt_buf.len) return bad();
for (file_ent_fmt_buf[0..file_name_entry_format_count]) |*ent_fmt| {
ent_fmt.* = .{
.content_type_code = try fbr.readUleb128(u16),
.form_code = try fbr.readUleb128(u16),
};
}
const file_names_count = try fbr.readUleb128(usize);
try file_entries.ensureUnusedCapacity(gpa, file_names_count);
for (try file_entries.addManyAsSlice(gpa, file_names_count)) |*e| {
e.* = .{ .path = &.{} };
for (file_ent_fmt_buf[0..file_name_entry_format_count]) |ent_fmt| {
const form_value = try parseFormValue(
&fbr,
ent_fmt.form_code,
unit_header.format,
null,
);
switch (ent_fmt.content_type_code) {
DW.LNCT.path => e.path = try form_value.getString(d.*),
DW.LNCT.directory_index => e.dir_index = try form_value.getUInt(u32),
DW.LNCT.timestamp => e.mtime = try form_value.getUInt(u64),
DW.LNCT.size => e.size = try form_value.getUInt(u64),
DW.LNCT.MD5 => e.md5 = switch (form_value) {
.data16 => |data16| data16.*,
else => return bad(),
},
else => continue,
}
}
}
}
var prog = LineNumberProgram.init(default_is_stmt, version);
var line_table: CompileUnit.SrcLocCache.LineTable = .{};
errdefer line_table.deinit(gpa);
try fbr.seekTo(prog_start_offset);
const next_unit_pos = line_info_offset + next_offset;
while (fbr.pos < next_unit_pos) {
const opcode = try fbr.readByte();
if (opcode == DW.LNS.extended_op) {
const op_size = try fbr.readUleb128(u64);
if (op_size < 1) return bad();
const sub_op = try fbr.readByte();
switch (sub_op) {
DW.LNE.end_sequence => {
// The row being added here is an "end" address, meaning
// that it does not map to the source location here -
// rather it marks the previous address as the last address
// that maps to this source location.
// In this implementation we don't mark end of addresses.
// This is a performance optimization based on the fact
// that we don't need to know if an address is missing
// source location info; we are only interested in being
// able to look up source location info for addresses that
// are known to have debug info.
//if (debug_debug_mode) assert(!line_table.contains(prog.address));
//try line_table.put(gpa, prog.address, CompileUnit.SrcLocCache.LineEntry.invalid);
prog.reset();
},
DW.LNE.set_address => {
const addr = try fbr.readInt(usize);
prog.address = addr;
},
DW.LNE.define_file => {
const path = try fbr.readBytesTo(0);
const dir_index = try fbr.readUleb128(u32);
const mtime = try fbr.readUleb128(u64);
const size = try fbr.readUleb128(u64);
try file_entries.append(gpa, .{
.path = path,
.dir_index = dir_index,
.mtime = mtime,
.size = size,
});
},
else => try fbr.seekForward(op_size - 1),
}
} else if (opcode >= opcode_base) {
// special opcodes
const adjusted_opcode = opcode - opcode_base;
const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range);
const inc_line = @as(i32, line_base) + @as(i32, adjusted_opcode % line_range);
prog.line += inc_line;
prog.address += inc_addr;
try prog.addRow(gpa, &line_table);
prog.basic_block = false;
} else {
switch (opcode) {
DW.LNS.copy => {
try prog.addRow(gpa, &line_table);
prog.basic_block = false;
},
DW.LNS.advance_pc => {
const arg = try fbr.readUleb128(usize);
prog.address += arg * minimum_instruction_length;
},
DW.LNS.advance_line => {
const arg = try fbr.readIleb128(i64);
prog.line += arg;
},
DW.LNS.set_file => {
const arg = try fbr.readUleb128(usize);
prog.file = arg;
},
DW.LNS.set_column => {
const arg = try fbr.readUleb128(u64);
prog.column = arg;
},
DW.LNS.negate_stmt => {
prog.is_stmt = !prog.is_stmt;
},
DW.LNS.set_basic_block => {
prog.basic_block = true;
},
DW.LNS.const_add_pc => {
const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range);
prog.address += inc_addr;
},
DW.LNS.fixed_advance_pc => {
const arg = try fbr.readInt(u16);
prog.address += arg;
},
DW.LNS.set_prologue_end => {},
else => {
if (opcode - 1 >= standard_opcode_lengths.len) return bad();
try fbr.seekForward(standard_opcode_lengths[opcode - 1]);
},
}
}
}
// Dwarf standard v5, 6.2.5 says
// > Within a sequence, addresses and operation pointers may only increase.
// However, this is empirically not the case in reality, so we sort here.
line_table.sortUnstable(struct {
keys: []const u64,
pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool {
return ctx.keys[a_index] < ctx.keys[b_index];
}
}{ .keys = line_table.keys() });
return .{
.line_table = line_table,
.directories = try directories.toOwnedSlice(gpa),
.files = try file_entries.toOwnedSlice(gpa),
.version = version,
};
}