DoxigAlpha

runLineNumberProgram

Ensures that addresses in the returned LineTable are monotonically increasing.

Function parameters

Parameters

#
d:*Dwarf
compile_unit:*CompileUnit

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