From 9f33551f71975d5829094864b880a924a2fc4394 Mon Sep 17 00:00:00 2001 From: Tudor Andrei Dicu Date: Thu, 5 Nov 2026 20:46:10 +0200 Subject: [PATCH 1/7] Remove custom dwarf implementation in favor of std --- tools/printer/examples/rp2xxx_runner.zig | 15 +- tools/printer/src/DebugInfo.zig | 797 --------------------- tools/printer/src/Elf.zig | 142 ---- tools/printer/src/main.zig | 15 +- tools/printer/src/root.zig | 52 +- tools/printer/tests/common.zig | 15 - tools/printer/tests/generate_test_data.zig | 56 -- tools/printer/tests/test.zig | 63 -- tools/printer/tests/test_program.zig | 13 - 9 files changed, 36 insertions(+), 1132 deletions(-) delete mode 100644 tools/printer/src/DebugInfo.zig delete mode 100644 tools/printer/src/Elf.zig delete mode 100644 tools/printer/tests/common.zig delete mode 100644 tools/printer/tests/generate_test_data.zig delete mode 100644 tools/printer/tests/test.zig delete mode 100644 tools/printer/tests/test_program.zig diff --git a/tools/printer/examples/rp2xxx_runner.zig b/tools/printer/examples/rp2xxx_runner.zig index 6a46cf582..5458c2209 100644 --- a/tools/printer/examples/rp2xxx_runner.zig +++ b/tools/printer/examples/rp2xxx_runner.zig @@ -17,14 +17,9 @@ pub fn main(init: std.process.Init) !void { const serial_device_path = args[2]; const baud_rate = try std.fmt.parseInt(u32, args[3], 10); - const elf_file = try std.Io.Dir.cwd().openFile(io, elf_path, .{}); - defer elf_file.close(io); - var elf_file_reader = elf_file.reader(io, &elf_file_reader_buf); - - var elf: printer.Elf = try .init(gpa, &elf_file_reader); - defer elf.deinit(gpa); - - var debug_info: printer.DebugInfo = try .init(gpa, elf); + var coverage: std.debug.Coverage = .init; + // arch is ignored for elf files, .thumb is just a placeholder + var debug_info: std.debug.Info = try .load(gpa, io, .initCwd(elf_path), &coverage, .elf, .thumb); defer debug_info.deinit(gpa); const serial_device = try std.Io.Dir.cwd().openFile(io, serial_device_path, .{}); @@ -55,10 +50,10 @@ pub fn main(init: std.process.Init) !void { }; try printer.annotate( + gpa, io, &reader.interface, &terminal, - elf, - &debug_info, + &debug_info.impl.elf, ); } diff --git a/tools/printer/src/DebugInfo.zig b/tools/printer/src/DebugInfo.zig deleted file mode 100644 index e78e063cb..000000000 --- a/tools/printer/src/DebugInfo.zig +++ /dev/null @@ -1,797 +0,0 @@ -const std = @import("std"); -const dwarf = std.dwarf; -const Elf = @import("Elf.zig"); - -const DebugInfo = @This(); - -directories: []const []const u8, -files: []const File, -loc_list: []const SourceLocation, -compile_unit_names: []const []const u8, -functions: []const Function, - -pub const File = struct { - path: []const u8, - dir_index: usize, -}; - -pub const SourceLocation = struct { - address: u64, - line: u32, - column: u32, - file_index: usize, -}; - -pub const Function = struct { - name: ?[]const u8, - cu_index: usize, - start_address: u64, - end_address: u64, -}; - -pub fn init(allocator: std.mem.Allocator, elf: Elf) !DebugInfo { - var arena_allocator: std.heap.ArenaAllocator = .init(allocator); - defer arena_allocator.deinit(); - - var parser: Parser = .{ .arena = arena_allocator.allocator() }; - try parser.parse_elf(elf); - - const directories = try allocator.dupe([]const u8, parser.directories.items); - errdefer allocator.free(directories); - - const files = try allocator.dupe(File, parser.files.items); - errdefer allocator.free(files); - - const loc_list = try allocator.dupe(SourceLocation, parser.loc_list.items); - errdefer allocator.free(loc_list); - - const compile_unit_names = try allocator.dupe([]const u8, parser.compile_unit_names.items); - errdefer allocator.free(compile_unit_names); - - const functions = try allocator.dupe(Function, parser.functions.items); - errdefer allocator.free(functions); - - return .{ - .directories = directories, - .files = files, - .loc_list = loc_list, - .compile_unit_names = compile_unit_names, - .functions = functions, - }; -} - -pub fn deinit(self: *DebugInfo, allocator: std.mem.Allocator) void { - allocator.free(self.directories); - allocator.free(self.files); - allocator.free(self.loc_list); - allocator.free(self.compile_unit_names); - allocator.free(self.functions); -} - -/// The `file_path` field has the same lifetime as `DebugInfo`. -pub const QueryResult = struct { - source_location: ?ResolvedSourceLocation = null, - function_name: ?[]const u8 = null, - module_name: ?[]const u8 = null, -}; - -pub const ResolvedSourceLocation = struct { - line: u32, - column: u32, - dir_path: []const u8, - file_path: []const u8, -}; - -pub fn query(self: *DebugInfo, address: u64) QueryResult { - var result: QueryResult = .{}; - - const index = std.sort.upperBound(SourceLocation, self.loc_list, address, struct { - pub fn compare(ctx: usize, item: SourceLocation) std.math.Order { - return std.math.order(ctx, item.address); - } - }.compare); - - if (index > 0 and index != self.loc_list.len) { - const src_loc = self.loc_list[index - 1]; - const file = &self.files[src_loc.file_index]; - const dir_path = self.directories[file.dir_index]; - - result.source_location = .{ - .line = src_loc.line, - .column = src_loc.column, - .dir_path = dir_path, - .file_path = file.path, - }; - } - - for (self.functions) |function| { - if (address >= function.start_address and address <= function.end_address) { - result.function_name = function.name; - result.module_name = self.compile_unit_names[function.cu_index]; - break; - } - } - - return result; -} - -// Implementation heavly based on `std.debug.Dwarf` but should support any -// target (even 64 bit architectures). - -const Parser = struct { - arena: std.mem.Allocator, - directories: std.ArrayList([]const u8) = .empty, - files: std.ArrayList(File) = .empty, - loc_list: std.ArrayList(SourceLocation) = .empty, - compile_unit_names: std.ArrayList([]const u8) = .empty, - functions: std.ArrayList(Function) = .empty, - - fn parse_elf(self: *Parser, elf: Elf) !void { - const buf = elf.sections.get(.@".debug_info") orelse return bad(); - var reader: std.Io.Reader = .fixed(buf); - - var current_cu_index: ?usize = null; - - while (reader.seek < reader.buffer.len) { - var length: u64 = try reader.takeInt(u32, elf.endian); - var dwarf_format: dwarf.Format = .@"32"; - - if (length == std.math.maxInt(u32)) { - length = try reader.takeInt(u64, elf.endian); - dwarf_format = .@"64"; - } - - if (length == 0) break; - - const version = try reader.takeInt(u16, elf.endian); - if (version < 2 or version > 5) return bad(); - - var address_size: u8 = 0; - var abbrev_table_offset: u64 = 0; - if (version >= 5) { - const unit_type = try reader.takeByte(); - if (unit_type != dwarf.UT.compile) return bad(); - - address_size = try reader.takeByte(); - abbrev_table_offset = try read_address(&reader, dwarf_format, elf.endian); - } else { - abbrev_table_offset = try read_address(&reader, dwarf_format, elf.endian); - address_size = try reader.takeByte(); - } - - var abbrev_table = try self.get_abbrev_table(elf, abbrev_table_offset); - - var depth: usize = 0; - while (true) { - const abbrev_code = try reader.takeLeb128(u64); - if (abbrev_code == 0) { - if (depth <= 1) { - break; - } - depth -= 1; - continue; - } - - const abbrev_decl = abbrev_table.get(abbrev_code) orelse return bad(); - - var parsed_attribs: std.AutoHashMapUnmanaged(u64, FormValue) = .empty; - for (abbrev_decl.attrs) |attr| { - const form_value = try FormValue.parse( - &reader, - attr.form_id, - elf, - dwarf_format, - attr.implicit_const_val, - ); - try parsed_attribs.put(self.arena, attr.id, form_value); - } - - const TAG = dwarf.TAG; - switch (abbrev_decl.tag) { - TAG.compile_unit => { - // register current compile unit - const name_attrib = parsed_attribs.get(dwarf.AT.name) orelse return bad(); - - current_cu_index = self.compile_unit_names.items.len; - try self.compile_unit_names.append( - self.arena, - try name_attrib.get_string(elf.sections), - ); - - // parse line number info - const stmt_list_attrib = parsed_attribs.get(dwarf.AT.stmt_list) orelse return bad(); - const cu_cwd_attrib = parsed_attribs.get(dwarf.AT.comp_dir) orelse return bad(); - - try self.parse_line_number_info( - elf, - try stmt_list_attrib.get_sec_offset(), - try cu_cwd_attrib.get_string(elf.sections), - ); - }, - TAG.subprogram, TAG.inlined_subroutine, TAG.entry_point => { - // in `std.debug.Dwarf` they do more complicated things to - // get the name if it isn't specified - - const maybe_name_attrib = parsed_attribs.get(dwarf.AT.name); - - if (parsed_attribs.get(dwarf.AT.low_pc)) |low_pc_attrib| { - const low_pc_address = switch (low_pc_attrib) { - .addr => |addr| addr, - else => return bad(), - }; - - if (parsed_attribs.get(dwarf.AT.high_pc)) |high_pc_attrib| { - const high_pc_address = switch (high_pc_attrib) { - .addr => |addr| addr, - .udata => |offset| low_pc_address + offset, - else => return bad(), - }; - - try self.functions.append(self.arena, .{ - .name = if (maybe_name_attrib) |name_attrib| try name_attrib.get_string(elf.sections) else null, - .cu_index = current_cu_index orelse return bad(), - .start_address = low_pc_address, - .end_address = high_pc_address, - }); - } - } else { - // TODO: should use `ranges` attribute - } - }, - else => {}, - } - - if (abbrev_decl.has_children) { - depth += 1; - } - } - } - - std.mem.sortUnstable(SourceLocation, self.loc_list.items, {}, struct { - pub fn less_than(_: void, lhs: SourceLocation, rhs: SourceLocation) bool { - return lhs.address < rhs.address; - } - }.less_than); - } - - fn get_abbrev_table(self: *Parser, elf: Elf, offset: usize) !AbbrevTable { - const buf = elf.sections.get(.@".debug_abbrev") orelse return bad(); - var reader: std.Io.Reader = .fixed(buf[offset..]); - - var abbrev_table: std.AutoHashMapUnmanaged(u64, AbbrevDecl) = .empty; - - while (true) { - const code = try reader.takeLeb128(u64); - if (code == 0) { - break; - } - - const tag = try reader.takeLeb128(u64); - const has_children = try reader.takeByte(); - - var attrs: std.ArrayList(AbbrevDecl.Attrib) = .empty; - - while (true) { - const id = try reader.takeLeb128(u64); - const form_id = try reader.takeLeb128(u64); - if (id == 0 and form_id == 0) { - break; - } - - var implicit_const_val: ?i64 = null; - if (form_id == dwarf.FORM.implicit_const) { - implicit_const_val = try reader.takeLeb128(i64); - } - - try attrs.append(self.arena, .{ - .id = id, - .form_id = form_id, - .implicit_const_val = implicit_const_val, - }); - } - - try abbrev_table.put(self.arena, code, .{ - .tag = tag, - .has_children = has_children != 0, - .attrs = try attrs.toOwnedSlice(self.arena), - }); - } - - return abbrev_table; - } - - const AbbrevDecl = struct { - tag: u64, - has_children: bool, - attrs: []const Attrib, - - const Attrib = struct { - id: u64, - form_id: u64, - implicit_const_val: ?i64, - }; - }; - - const AbbrevTable = std.AutoHashMapUnmanaged(u64, AbbrevDecl); - - fn parse_line_number_info( - self: *Parser, - elf: Elf, - offset: usize, - cu_cwd: []const u8, - ) !void { - const buf = elf.sections.get(.@".debug_line") orelse return bad(); - var reader: std.Io.Reader = .fixed(buf[offset..]); - - var unit_length: u64 = try reader.takeInt(u32, elf.endian); - var dwarf_format: dwarf.Format = .@"32"; - if (unit_length == std.math.maxInt(u32)) { - unit_length = try reader.takeInt(u64, elf.endian); - dwarf_format = .@"64"; - } - if (unit_length == 0) { - return; - } - - const next_unit_offset = reader.seek + unit_length; - - const version = try reader.takeInt(u16, elf.endian); - if (version < 2) return bad(); - - if (version >= 5) { - _ = try reader.takeByte(); // address size - _ = try reader.takeByte(); // segment size - } - - const prologue_length = try read_address(&reader, dwarf_format, elf.endian); - const prog_start_offset = reader.seek + prologue_length; - - const minimum_instruction_length = try reader.takeByte(); - if (minimum_instruction_length == 0) return bad(); - - if (version >= 4) { - _ = try reader.takeByte(); - } - - const default_is_stmt = (try reader.takeByte()) != 0; - const line_base = try reader.takeByteSigned(); - - const line_range = try reader.takeByte(); - if (line_range == 0) return bad(); - - const opcode_base = try reader.takeByte(); - - const standard_opcode_lengths = try reader.take(opcode_base - 1); - - const dir_index_offset = self.directories.items.len; - const file_index_offset = self.files.items.len; - - if (version < 5) { - try self.directories.append(self.arena, cu_cwd); - - while (true) { - const dir = try reader.takeSentinel(0); - if (dir.len == 0) break; - try self.directories.append(self.arena, dir); - } - - while (true) { - const file_name = try reader.takeSentinel(0); - if (file_name.len == 0) break; - const dir_index = try reader.takeLeb128(usize); - _ = try reader.takeLeb128(u64); // timestamp - _ = try reader.takeLeb128(u64); // size - try self.files.append(self.arena, .{ - .path = file_name, - .dir_index = dir_index_offset + dir_index, - // .timestamp = timestamp, - }); - } - } else { - const LNCT = dwarf.LNCT; - - const FileEntFmt = struct { - content_type_code: u16, - form_code: u16, - }; - - { - const directory_entry_format_count = try reader.takeByte(); - const dir_ent_fmt_buf = try self.arena.alloc(FileEntFmt, directory_entry_format_count); - - for (dir_ent_fmt_buf[0..directory_entry_format_count]) |*ent_fmt| { - ent_fmt.* = .{ - .content_type_code = try reader.takeLeb128(u8), - .form_code = try reader.takeLeb128(u16), - }; - } - - const directories_count = try reader.takeLeb128(usize); - - for (try self.directories.addManyAsSlice(self.arena, directories_count)) |*e| { - e.* = &.{}; - for (dir_ent_fmt_buf[0..directory_entry_format_count]) |ent_fmt| { - const form_value = try FormValue.parse( - &reader, - ent_fmt.form_code, - elf, - dwarf_format, - null, - ); - - switch (ent_fmt.content_type_code) { - LNCT.path => e.* = try form_value.get_string(elf.sections), - else => continue, - } - } - } - } - - { - const file_entry_format_count = try reader.takeByte(); - const file_ent_fmt_buf = try self.arena.alloc(FileEntFmt, file_entry_format_count); - - for (file_ent_fmt_buf[0..file_entry_format_count]) |*ent_fmt| { - ent_fmt.* = .{ - .content_type_code = try reader.takeLeb128(u16), - .form_code = try reader.takeLeb128(u16), - }; - } - - const file_names_count = try reader.takeLeb128(usize); - - for (try self.files.addManyAsSlice(self.arena, file_names_count)) |*e| { - e.* = .{ .path = &.{}, .dir_index = 0 }; - for (file_ent_fmt_buf[0..file_entry_format_count]) |ent_fmt| { - const form_value = try FormValue.parse( - &reader, - ent_fmt.form_code, - elf, - dwarf_format, - null, - ); - switch (ent_fmt.content_type_code) { - LNCT.path => e.path = try form_value.get_string(elf.sections), - LNCT.directory_index => e.dir_index = dir_index_offset + try form_value.get_uint(usize), - // LNCT.timestamp => e.timestamp = try form_value.get_uint(u64), - // LNCT.MD5 => e.md5 = switch (form_value) { - // .data16 => |data16| data16.*, - // else => return bad(), - // }, - else => continue, - } - } - } - } - } - - var prog = LineNumberProgram.init(default_is_stmt, version); - - reader.seek = prog_start_offset; - - const LNE = dwarf.LNE; - const LNS = dwarf.LNS; - - while (reader.seek < next_unit_offset) { - const opcode = try reader.takeByte(); - - if (opcode == LNS.extended_op) { - const op_size = try reader.takeLeb128(u64); - if (op_size < 1) return bad(); - const sub_op = try reader.takeByte(); - switch (sub_op) { - 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. - prog.reset(); - }, - LNE.set_address => { - const addr = switch (elf.format) { - .@"32" => try reader.takeInt(u32, elf.endian), - .@"64" => try reader.takeInt(u64, elf.endian), - }; - prog.address = addr; - }, - LNE.define_file => { - const path = try reader.takeSentinel(0); - const dir_index = try reader.takeLeb128(usize); - _ = try reader.takeLeb128(u64); - _ = try reader.takeLeb128(u64); - try self.files.append(self.arena, .{ - .path = path, - .dir_index = dir_index_offset + dir_index, - // .timestamp = timestamp, - }); - }, - else => reader.toss(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.add_row(self, elf, file_index_offset); - prog.basic_block = false; - } else { - switch (opcode) { - LNS.copy => { - try prog.add_row(self, elf, file_index_offset); - prog.basic_block = false; - }, - LNS.advance_pc => { - const arg = try reader.takeLeb128(usize); - prog.address += arg * minimum_instruction_length; - }, - LNS.advance_line => { - const arg = try reader.takeLeb128(i64); - prog.line += arg; - }, - LNS.set_file => { - const arg = try reader.takeLeb128(usize); - prog.file = arg; - }, - LNS.set_column => { - const arg = try reader.takeLeb128(u64); - prog.column = arg; - }, - LNS.negate_stmt => { - prog.is_stmt = !prog.is_stmt; - }, - LNS.set_basic_block => { - prog.basic_block = true; - }, - LNS.const_add_pc => { - const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range); - prog.address += inc_addr; - }, - LNS.fixed_advance_pc => { - const arg = try reader.takeInt(u16, elf.endian); - prog.address += arg; - }, - LNS.set_prologue_end => {}, - else => { - if (opcode - 1 >= standard_opcode_lengths.len) return bad(); - reader.toss(standard_opcode_lengths[opcode - 1]); - }, - } - } - } - } - - const LineNumberProgram = struct { - address: u64, - file: usize, - line: i64, - column: u64, - version: u16, - is_stmt: bool, - basic_block: bool, - - default_is_stmt: bool, - - // Reset the state machine following the DWARF specification - fn reset(self: *LineNumberProgram) void { - self.address = 0; - self.file = 1; - self.line = 1; - self.column = 0; - self.is_stmt = self.default_is_stmt; - self.basic_block = false; - } - - fn init(is_stmt: bool, version: u16) LineNumberProgram { - return .{ - .address = 0, - .file = 1, - .line = 1, - .column = 0, - .version = version, - .is_stmt = is_stmt, - .basic_block = false, - .default_is_stmt = is_stmt, - }; - } - - fn add_row( - prog: *LineNumberProgram, - parser: *Parser, - elf: Elf, - index_offset: usize, - ) !void { - if (prog.line == 0) { - return; - } - - if (!elf.is_address_executable(prog.address)) { - return; - } - - try parser.loc_list.append(parser.arena, .{ - .address = prog.address, - .line = std.math.cast(u32, prog.line) orelse return bad(), - .column = std.math.cast(u32, prog.column) orelse return bad(), - .file_index = index_offset + (std.math.cast( - usize, - if (prog.version < 5) prog.file -| 1 else prog.file, - ) orelse return bad()), - }); - } - }; - - const FormValue = union(enum) { - addr: u64, - addrx: u64, - block: []const u8, - udata: u64, - data16: *const [16]u8, - sdata: i64, - exprloc: []const u8, - flag: bool, - sec_offset: u64, - ref: u64, - ref_addr: u64, - string: [:0]const u8, - strp: u64, - strx: u64, - line_strp: u64, - loclistx: u64, - rnglistx: u64, - - fn parse( - reader: *std.Io.Reader, - form_id: u64, - elf: Elf, - dwarf_format: dwarf.Format, - implicit_const_val: ?i64, - ) !FormValue { - const FORM = dwarf.FORM; - - // TODO: check the use of `readAddress` - return switch (form_id) { - FORM.addr => .{ .addr = switch (elf.format) { - .@"32" => try reader.takeInt(u32, elf.endian), - .@"64" => try reader.takeInt(u64, elf.endian), - } }, - FORM.addrx1 => .{ .addrx = try reader.takeInt(u8, elf.endian) }, - FORM.addrx2 => .{ .addrx = try reader.takeInt(u16, elf.endian) }, - FORM.addrx3 => .{ .addrx = try reader.takeInt(u24, elf.endian) }, - FORM.addrx4 => .{ .addrx = try reader.takeInt(u32, elf.endian) }, - FORM.addrx => .{ .addrx = try reader.takeLeb128(u64) }, - - FORM.block1, - FORM.block2, - FORM.block4, - FORM.block, - => .{ .block = try reader.take(switch (form_id) { - FORM.block1 => try reader.takeInt(u8, elf.endian), - FORM.block2 => try reader.takeInt(u16, elf.endian), - FORM.block4 => try reader.takeInt(u32, elf.endian), - FORM.block => try reader.takeLeb128(u64), - else => unreachable, - }) }, - - FORM.data1 => .{ .udata = try reader.takeInt(u8, elf.endian) }, - FORM.data2 => .{ .udata = try reader.takeInt(u16, elf.endian) }, - FORM.data4 => .{ .udata = try reader.takeInt(u32, elf.endian) }, - FORM.data8 => .{ .udata = try reader.takeInt(u64, elf.endian) }, - FORM.data16 => .{ .data16 = (try reader.take(16))[0..16] }, - FORM.udata => .{ .udata = try reader.takeLeb128(u64) }, - FORM.sdata => .{ .sdata = try reader.takeLeb128(i64) }, - FORM.exprloc => .{ .exprloc = try reader.take(try reader.takeLeb128(u64)) }, - FORM.flag => .{ .flag = (try reader.takeByte()) != 0 }, - FORM.flag_present => .{ .flag = true }, - FORM.sec_offset => .{ .sec_offset = try read_address(reader, dwarf_format, elf.endian) }, - - FORM.ref1 => .{ .ref = try reader.takeInt(u8, elf.endian) }, - FORM.ref2 => .{ .ref = try reader.takeInt(u16, elf.endian) }, - FORM.ref4 => .{ .ref = try reader.takeInt(u32, elf.endian) }, - FORM.ref8 => .{ .ref = try reader.takeInt(u64, elf.endian) }, - FORM.ref_udata => .{ .ref = try reader.takeLeb128(u64) }, - - FORM.ref_addr => .{ .ref_addr = try read_address(reader, dwarf_format, elf.endian) }, - FORM.ref_sig8 => .{ .ref = try reader.takeInt(u64, elf.endian) }, - - FORM.string => .{ .string = try reader.takeSentinel(0) }, - FORM.strp => .{ .strp = try read_address(reader, dwarf_format, elf.endian) }, - FORM.strx1 => .{ .strx = try reader.takeInt(u8, elf.endian) }, - FORM.strx2 => .{ .strx = try reader.takeInt(u16, elf.endian) }, - FORM.strx3 => .{ .strx = try reader.takeInt(u24, elf.endian) }, - FORM.strx4 => .{ .strx = try reader.takeInt(u32, elf.endian) }, - FORM.strx => .{ .strx = try reader.takeLeb128(u64) }, - FORM.line_strp => .{ .line_strp = try read_address(reader, dwarf_format, elf.endian) }, - FORM.implicit_const => .{ .sdata = implicit_const_val orelse return bad() }, - FORM.loclistx => .{ .loclistx = try reader.takeLeb128(u64) }, - FORM.rnglistx => .{ .rnglistx = try reader.takeLeb128(u64) }, - FORM.indirect => blk: { - const indirect = try reader.takeLeb128(u64); - break :blk try parse(reader, indirect, elf, dwarf_format, implicit_const_val); - }, - else => { - std.log.err("unimplemented form id: 0x{x}", .{form_id}); - return unimplemented(); - }, - }; - } - - fn get_string( - self: FormValue, - sections: Elf.Sections, - ) ![]const u8 { - switch (self) { - .string => |value| return value, - .strp => |offset| { - const data = sections.get(.@".debug_str") orelse return bad(); - return std.mem.sliceTo(data[offset..], 0); - }, - .line_strp => |offset| { - const data = sections.get(.@".debug_line_str") orelse return bad(); - return std.mem.sliceTo(data[offset..], 0); - }, - .strx => |index| { - _ = index; - // const debug_str_offsets = di.section(.debug_str_offsets) orelse return bad(); - // if (compile_unit.str_offsets_base == 0) return bad(); - // switch (compile_unit.format) { - // .@"32" => { - // const byte_offset = compile_unit.str_offsets_base + 4 * index; - // if (byte_offset + 4 > debug_str_offsets.len) return bad(); - // const offset = mem.readInt(u32, debug_str_offsets[byte_offset..][0..4], di.endian); - // return getStringGeneric(opt_str, offset); - // }, - // .@"64" => { - // const byte_offset = compile_unit.str_offsets_base + 8 * index; - // if (byte_offset + 8 > debug_str_offsets.len) return bad(); - // const offset = mem.readInt(u64, debug_str_offsets[byte_offset..][0..8], di.endian); - // return getStringGeneric(opt_str, offset); - // }, - // } - return unimplemented(); - }, - else => return bad(), - } - } - - fn get_sec_offset(self: FormValue) !u64 { - switch (self) { - .sec_offset => return self.sec_offset, - else => return bad(), - } - } - - fn get_uint(fv: FormValue, comptime U: type) !U { - return switch (fv) { - inline .udata, - .sdata, - .sec_offset, - => |c| std.math.cast(U, c) orelse bad(), - else => bad(), - }; - } - }; - - fn read_address(reader: *std.Io.Reader, dwarf_format: dwarf.Format, endian: std.builtin.Endian) !u64 { - return switch (dwarf_format) { - .@"32" => try reader.takeInt(u32, endian), - .@"64" => try reader.takeInt(u64, endian), - }; - } -}; - -fn bad() error{InvalidDebugInfo} { - return error.InvalidDebugInfo; -} - -fn unimplemented() error{Unimplemented} { - return error.Unimplemented; -} diff --git a/tools/printer/src/Elf.zig b/tools/printer/src/Elf.zig deleted file mode 100644 index 86f9f789b..000000000 --- a/tools/printer/src/Elf.zig +++ /dev/null @@ -1,142 +0,0 @@ -const std = @import("std"); - -const Elf = @This(); - -endian: std.builtin.Endian, -format: Format, -sections: Sections, -loaded_regions: []const Region, - -pub const Format = enum { - @"32", - @"64", -}; - -pub const Sections = std.EnumMap(SectionTypes, []const u8); - -pub const SectionTypes = enum { - @".debug_info", - @".debug_abbrev", - @".debug_str", - @".debug_line_str", - @".debug_line", - @".debug_ranges", -}; - -pub const Region = struct { - start_address: u64, - end_address: u64, - flags: std.elf.PF, - - pub const Flags = packed struct { - read: bool, - write: bool, - exec: bool, - }; -}; - -pub fn init(allocator: std.mem.Allocator, file_reader: *std.Io.File.Reader) !Elf { - var elf_header = try std.elf.Header.read(&file_reader.interface); - - const format: Format = if (elf_header.is_64) .@"64" else .@"32"; - - const string_table_data = blk: { - var shdr: std.elf.Elf64_Shdr = undefined; - if (format == .@"32") { - // var shdr32: std.elf.Elf32_Shdr = undefined; - const offset = elf_header.shoff + @sizeOf(std.elf.Elf32_Shdr) * elf_header.shstrndx; - try file_reader.seekTo(offset); - const shdr32 = try file_reader.interface.takeStruct(std.elf.Elf32_Shdr, elf_header.endian); - - shdr = .{ - .sh_name = shdr32.sh_name, - .sh_type = shdr32.sh_type, - .sh_flags = shdr32.sh_flags, - .sh_addr = shdr32.sh_addr, - .sh_offset = shdr32.sh_offset, - .sh_size = shdr32.sh_size, - .sh_link = shdr32.sh_link, - .sh_info = shdr32.sh_info, - .sh_addralign = shdr32.sh_addralign, - .sh_entsize = shdr32.sh_entsize, - }; - } else { - const offset = elf_header.shoff + @sizeOf(std.elf.Elf64_Shdr) * elf_header.shstrndx; - try file_reader.seekTo(offset); - shdr = try file_reader.interface.takeStruct(std.elf.Elf64_Shdr, elf_header.endian); - } - - try file_reader.seekTo(shdr.sh_offset); - break :blk try file_reader.interface.readAlloc(allocator, shdr.sh_size); - }; - defer allocator.free(string_table_data); - - var sections: Sections = .{}; - errdefer { - var it = sections.iterator(); - while (it.next()) |entry| { - allocator.free(entry.value.*); - } - } - - var section_header_it = elf_header.iterateSectionHeaders(file_reader); - while (try section_header_it.next()) |shdr| { - const name = std.mem.span(@as([*:0]const u8, @ptrCast(string_table_data[shdr.sh_name..]))); - const info = @typeInfo(SectionTypes).@"enum"; - inline for (info.field_names, info.field_values) |section_field_name, section_field_value| { - if (std.mem.eql(u8, name, section_field_name)) { - try file_reader.seekTo(shdr.sh_offset); - - const section_data = try file_reader.interface.readAlloc(allocator, shdr.sh_size); - errdefer allocator.free(section_data); - - sections.put(@fromBackingInt(section_field_value), section_data); - } - } - } - - var loaded_regions: std.ArrayList(Region) = .empty; - defer loaded_regions.deinit(allocator); - - var program_header_iterator = elf_header.iterateProgramHeaders(file_reader); - while (try program_header_iterator.next()) |phdr| { - if (phdr.type != std.elf.PT.LOAD) continue; - - try loaded_regions.append(allocator, .{ - .start_address = phdr.vaddr, - .end_address = phdr.vaddr + phdr.memsz, - .flags = phdr.flags, - }); - } - - return .{ - .endian = elf_header.endian, - .format = format, - .sections = sections, - .loaded_regions = try loaded_regions.toOwnedSlice(allocator), - }; -} - -pub fn deinit(self: *Elf, allocator: std.mem.Allocator) void { - var it = self.sections.iterator(); - while (it.next()) |entry| { - allocator.free(entry.value.*); - } - - allocator.free(self.loaded_regions); -} - -pub fn is_address_executable(self: Elf, address: u64) bool { - var inside_memory_map = false; - for (self.loaded_regions) |region| { - if (!region.flags.X) { - continue; - } - - if (address >= region.start_address and address < region.end_address) { - inside_memory_map = true; - break; - } - } - return inside_memory_map; -} diff --git a/tools/printer/src/main.zig b/tools/printer/src/main.zig index 3d0a87d29..cb2f28ca0 100644 --- a/tools/printer/src/main.zig +++ b/tools/printer/src/main.zig @@ -16,14 +16,11 @@ pub fn main(init: std.process.Init) !void { std.process.exit(1); } - const elf_file = try std.Io.Dir.cwd().openFile(io, args[1], .{}); - defer elf_file.close(io); + const elf_path = args[1]; - var elf_file_reader = elf_file.reader(io, &elf_file_reader_buf); - var elf: printer.Elf = try .init(gpa, &elf_file_reader); - defer elf.deinit(gpa); - - var debug_info: printer.DebugInfo = try .init(gpa, elf); + var coverage: std.debug.Coverage = .init; + // arch is ignored for elf files, .thumb is just a placeholder + var debug_info: std.debug.Info = try .load(gpa, io, .initCwd(elf_path), &coverage, .elf, .thumb); defer debug_info.deinit(gpa); const input_file = if (args.len <= 2 or std.mem.eql(u8, args[2], "-")) @@ -45,10 +42,10 @@ pub fn main(init: std.process.Init) !void { }; try printer.annotate( + gpa, io, &reader.interface, &terminal, - elf, - &debug_info, + &debug_info.impl.elf, ); } diff --git a/tools/printer/src/root.zig b/tools/printer/src/root.zig index 553206451..b0f6ec9d2 100644 --- a/tools/printer/src/root.zig +++ b/tools/printer/src/root.zig @@ -1,40 +1,37 @@ const std = @import("std"); const Io = std.Io; -pub const Elf = @import("Elf.zig"); -pub const DebugInfo = @import("DebugInfo.zig"); - /// Reads lines one by one from in_stream and outputs them with annotated /// addresses to out_stream. Returns after it reaches the end of the stream. pub fn annotate( + gpa: std.mem.Allocator, io: Io, in_stream: *Io.Reader, terminal: *Io.Terminal, - elf: Elf, - debug_info: *DebugInfo, + elf: *std.debug.ElfFile, ) !void { while (true) { const line = in_stream.takeDelimiterInclusive('\n') catch |err| switch (err) { error.ReadFailed => return error.ReadFailed, error.EndOfStream => { - try output_line(io, in_stream.buffered(), terminal, elf, debug_info); + try output_line(gpa, io, in_stream.buffered(), terminal, elf); try terminal.writer.flush(); return; }, error.StreamTooLong => return error.StreamTooLong, }; - try output_line(io, line, terminal, elf, debug_info); + try output_line(gpa, io, line, terminal, elf); try terminal.writer.flush(); } } fn output_line( + gpa: std.mem.Allocator, io: Io, line: []const u8, terminal: *Io.Terminal, - elf: Elf, - debug_info: *DebugInfo, + elf: *std.debug.ElfFile, ) !void { try terminal.writer.writeAll(line); @@ -53,25 +50,29 @@ fn output_line( if (hex_digit_count != 8 and hex_digit_count != 16) return; const address = std.fmt.parseInt(u64, after_prefix[0..hex_digit_count], 16) catch unreachable; - if (elf.is_address_executable(address)) { - const query_result = debug_info.query(address); - try output_location_info(terminal, address, query_result); - if (query_result.source_location) |src_loc| { - try output_source_line(io, terminal, src_loc); - } - } + + var text_arena: std.heap.ArenaAllocator = .init(gpa); + defer text_arena.deinit(); + const di = &(elf.dwarf orelse return); + const compile_unit = di.findCompileUnit(elf.endian, address) catch return; + const maybe_name = di.getSymbolName(address); + const maybe_src_loc = di.getLineNumberInfo(gpa, text_arena.allocator(), elf.endian, compile_unit, address) catch null; + + try output_location_info(terminal, address, maybe_name, maybe_src_loc); + if (maybe_src_loc) |src_loc| try output_source_line(io, terminal, src_loc); } fn output_location_info( terminal: *Io.Terminal, address: u64, - query_result: DebugInfo.QueryResult, + maybe_name: ?[]const u8, + maybe_src_loc: ?std.debug.SourceLocation, ) !void { try terminal.setColor(.bold); - if (query_result.source_location) |src_loc| { - try terminal.writer.print("{f}:{}:{}", .{ - std.fs.path.fmtJoin(&.{ src_loc.dir_path, src_loc.file_path }), + if (maybe_src_loc) |src_loc| { + try terminal.writer.print("{s}:{}:{}", .{ + src_loc.file_name, src_loc.line, src_loc.column, }); @@ -85,8 +86,8 @@ fn output_location_info( try terminal.setColor(.dim); try terminal.writer.print("0x{x} in {s} ({s})\n", .{ address, - query_result.function_name orelse "???", - query_result.module_name orelse "???", + maybe_name orelse "???", + if (maybe_src_loc) |src_loc| std.Io.Dir.path.basename(src_loc.file_name) else "???", }); try terminal.setColor(.reset); } @@ -94,12 +95,9 @@ fn output_location_info( fn output_source_line( io: Io, terminal: *Io.Terminal, - src_loc: DebugInfo.ResolvedSourceLocation, + src_loc: std.debug.SourceLocation, ) !void { - var dir = std.Io.Dir.cwd().openDir(io, src_loc.dir_path, .{}) catch return; - defer dir.close(io); - - const file = dir.openFile(io, src_loc.file_path, .{}) catch return; + var file = std.Io.Dir.cwd().openFile(io, src_loc.file_name, .{}) catch return; defer file.close(io); var r_buf: [512]u8 = undefined; diff --git a/tools/printer/tests/common.zig b/tools/printer/tests/common.zig deleted file mode 100644 index ebf532506..000000000 --- a/tools/printer/tests/common.zig +++ /dev/null @@ -1,15 +0,0 @@ -pub const Test = struct { - address: u64, - expected: struct { - line: u32, - column: u32, - file_path: []const u8, - function_name: ?[]const u8, - module_name: ?[]const u8, - }, -}; - -pub const Data = struct { - zig_version: []const u8, - tests: []const Test, -}; diff --git a/tools/printer/tests/generate_test_data.zig b/tools/printer/tests/generate_test_data.zig deleted file mode 100644 index 7c59999d8..000000000 --- a/tools/printer/tests/generate_test_data.zig +++ /dev/null @@ -1,56 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const printer = @import("printer"); - -const common = @import("common"); - -var elf_file_reader_buf: [1024]u8 = undefined; - -pub fn main(init: std.process.Init) !void { - const io = init.io; - const allocator = init.arena.allocator(); - - const args = try init.minimal.args.toSlice(allocator); - if (args.len != 3) return error.UsageError; - - const elf_path = args[1]; - const test_data_path = args[2]; - - const elf_file = try std.Io.Dir.cwd().openFile(io, elf_path, .{}); - defer elf_file.close(io); - var elf_file_reader = elf_file.reader(io, &elf_file_reader_buf); - - const elf = try printer.Elf.init(allocator, &elf_file_reader); - var debug_info = try printer.DebugInfo.init(allocator, elf); - - var tests: std.ArrayList(common.Test) = .empty; - - for (debug_info.loc_list[0..@min(debug_info.loc_list.len, 10)]) |tmp_src_loc| { - const address = tmp_src_loc.address; - const query_result = debug_info.query(address); - const src_loc = query_result.source_location.?; // this shouldn't be null - - try tests.append(allocator, .{ - .address = address, - .expected = .{ - .line = src_loc.line, - .column = src_loc.column, - .file_path = src_loc.file_path, - .module_name = query_result.module_name, - .function_name = query_result.function_name, - }, - }); - } - - var test_data_file = try std.Io.Dir.cwd().createFile(io, test_data_path, .{}); - defer test_data_file.close(io); - var test_data_writer = test_data_file.writer(io, &.{}); - - const data: common.Data = .{ - .zig_version = builtin.zig_version_string, - .tests = tests.items, - }; - try std.zon.stringify.serialize(data, .{}, &test_data_writer.interface); - try test_data_writer.interface.writeByte('\n'); - try test_data_writer.interface.flush(); -} diff --git a/tools/printer/tests/test.zig b/tools/printer/tests/test.zig deleted file mode 100644 index e671cd039..000000000 --- a/tools/printer/tests/test.zig +++ /dev/null @@ -1,63 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const printer = @import("printer"); - -const common = @import("common"); - -var buf: [1024]u8 = undefined; - -pub fn main(init: std.process.Init) !void { - const gpa = init.gpa; - const io = init.io; - const arena = init.arena.allocator(); - - const args = try init.minimal.args.toSlice(init.arena.allocator()); - if (args.len != 3) - return error.UsageError; - - const elf_path = args[1]; - const test_data_path = args[2]; - const test_name = std.fs.path.stem(test_data_path); - - const elf_file = try std.Io.Dir.cwd().openFile(io, elf_path, .{}); - defer elf_file.close(io); - - var elf_file_reader = elf_file.reader(io, &buf); - - var elf = try printer.Elf.init(gpa, &elf_file_reader); - defer elf.deinit(gpa); - - const test_data_raw = try std.Io.Dir.cwd().readFileAllocOptions(io, test_data_path, gpa, .limited(1_000_000), .@"1", 0); - defer gpa.free(test_data_raw); - - var diags: std.zon.parse.Diagnostics = undefined; - const test_data = std.zon.parse.fromSlice(common.Data, .{ - .gpa = gpa, - .arena = arena, - .source = test_data_raw, - .diagnostics = &diags, - }) catch |err| switch (err) { - error.ParseZon => diags.fatal(test_data_path), - else => |e| return e, - }; - - var debug_info = try printer.DebugInfo.init(gpa, elf); - defer debug_info.deinit(gpa); - - if (!std.mem.eql(u8, test_data.zig_version, builtin.zig_version_string)) - return error.ZigVersionMismatch; - - for (test_data.tests) |t| { - const actual = debug_info.query(t.address); - const actual_loc = actual.source_location.?; - - try std.testing.expectEqual(t.expected.line, actual_loc.line); - try std.testing.expectEqual(t.expected.column, actual_loc.column); - try std.testing.expectEqualStrings(t.expected.file_path, actual_loc.file_path); - - try std.testing.expectEqualStrings(t.expected.function_name orelse "", actual.function_name orelse ""); - try std.testing.expectEqualStrings(t.expected.module_name orelse "", actual.module_name orelse ""); - } - - std.debug.print("test {s}: success\n", .{test_name}); -} diff --git a/tools/printer/tests/test_program.zig b/tools/printer/tests/test_program.zig deleted file mode 100644 index 38e33eb34..000000000 --- a/tools/printer/tests/test_program.zig +++ /dev/null @@ -1,13 +0,0 @@ -const std = @import("std"); - -pub const std_options: std.Options = .{ - .log_level = .debug, -}; - -pub fn main() !void { - std.debug.print("Hello world!", .{}); - - std.debug.print("Hello world!", .{}); - - std.debug.print("Hello world!", .{}); -} From 3ba556cedd566d59d1cc0b82a4349a9c465c5256 Mon Sep 17 00:00:00 2001 From: Tudor Andrei Dicu Date: Thu, 5 Nov 2026 20:41:02 +0200 Subject: [PATCH 2/7] Add missing while loop --- tools/printer/examples/rp2xxx_runner.zig | 16 +++++++++------- tools/printer/src/main.zig | 16 +++++++++------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/tools/printer/examples/rp2xxx_runner.zig b/tools/printer/examples/rp2xxx_runner.zig index 5458c2209..38a6987b5 100644 --- a/tools/printer/examples/rp2xxx_runner.zig +++ b/tools/printer/examples/rp2xxx_runner.zig @@ -49,11 +49,13 @@ pub fn main(init: std.process.Init) !void { .mode = try std.Io.Terminal.Mode.detect(io, stdout, no_color, clicolor_force), }; - try printer.annotate( - gpa, - io, - &reader.interface, - &terminal, - &debug_info.impl.elf, - ); + while (true) { + try printer.annotate( + gpa, + io, + &reader.interface, + &terminal, + &debug_info.impl.elf, + ); + } } diff --git a/tools/printer/src/main.zig b/tools/printer/src/main.zig index cb2f28ca0..66a66aa50 100644 --- a/tools/printer/src/main.zig +++ b/tools/printer/src/main.zig @@ -41,11 +41,13 @@ pub fn main(init: std.process.Init) !void { .mode = try std.Io.Terminal.Mode.detect(io, stdout, no_color, clicolor_force), }; - try printer.annotate( - gpa, - io, - &reader.interface, - &terminal, - &debug_info.impl.elf, - ); + while (true) { + try printer.annotate( + gpa, + io, + &reader.interface, + &terminal, + &debug_info.impl.elf, + ); + } } From 44a9ccd1f19dbd90e31a86b9dbeb4f0242aec049 Mon Sep 17 00:00:00 2001 From: Tudor Andrei Dicu Date: Thu, 5 Nov 2026 20:48:14 +0200 Subject: [PATCH 3/7] Remove test code --- tools/printer/build.zig | 97 -------------------- tools/printer/tests/test_program.dwarf32.zon | 72 --------------- tools/printer/tests/test_program.dwarf64.zon | 72 --------------- 3 files changed, 241 deletions(-) delete mode 100644 tools/printer/tests/test_program.dwarf32.zon delete mode 100644 tools/printer/tests/test_program.dwarf64.zon diff --git a/tools/printer/build.zig b/tools/printer/build.zig index 8c172b9a0..9d5b9758a 100644 --- a/tools/printer/build.zig +++ b/tools/printer/build.zig @@ -41,101 +41,4 @@ pub fn build(b: *std.Build) void { }), }); b.installArtifact(example_exe); - - const test_common_mod = b.createModule(.{ - .root_source_file = b.path("tests/common.zig"), - .imports = &.{ - .{ .name = "printer", .module = printer_mod }, - }, - }); - - const generate_test_data_exe = b.addExecutable(.{ - .name = "generate_results", - .root_module = b.createModule(.{ - .root_source_file = b.path("tests/generate_test_data.zig"), - .imports = &.{ - .{ .name = "common", .module = test_common_mod }, - .{ .name = "printer", .module = printer_mod }, - }, - .target = target, - .optimize = optimize, - }), - }); - - const test_runner_exe = b.addExecutable(.{ - .name = "test", - .root_module = b.createModule(.{ - .root_source_file = b.path("tests/test.zig"), - .imports = &.{ - .{ .name = "common", .module = test_common_mod }, - .{ .name = "printer", .module = printer_mod }, - }, - .target = target, - .optimize = optimize, - }), - }); - - const test_dwarf32_exe = b.addExecutable(.{ - .name = "test_program.dwarf32", - .root_module = b.createModule(.{ - .root_source_file = b.path("tests/test_program.zig"), - .optimize = .Debug, - .target = b.resolveTargetQuery(.{ - .os_tag = .linux, - .cpu_arch = .x86_64, - .abi = .gnu, - .ofmt = .elf, - }), - .dwarf_format = .@"32", - .strip = false, - }), - }); - - const test_dwarf64_exe = b.addExecutable(.{ - .name = "test_program.dwarf64", - .root_module = b.createModule(.{ - .root_source_file = b.path("tests/test_program.zig"), - .optimize = .Debug, - .target = b.resolveTargetQuery(.{ - .os_tag = .linux, - .cpu_arch = .x86_64, - .abi = .gnu, - .ofmt = .elf, - }), - .dwarf_format = .@"64", - }), - }); - - const generate_test_data_step = b.step("generate-test-data", "regenerate test data"); - const test_step = b.step("test", "test printer"); - - inline for (&.{ test_dwarf32_exe, test_dwarf64_exe }) |exe| { - register_test( - b, - generate_test_data_step, - generate_test_data_exe, - test_step, - test_runner_exe, - exe, - ); - } -} - -pub fn register_test( - b: *std.Build, - generate_test_data_step: *std.Build.Step, - generate_test_data_exe: *std.Build.Step.Compile, - test_step: *std.Build.Step, - test_runner_exe: *std.Build.Step.Compile, - test_exe: *std.Build.Step.Compile, -) void { - const generate_test_data_run = b.addRunArtifact(generate_test_data_exe); - generate_test_data_run.addFileArg(test_exe.getEmittedBin()); - generate_test_data_run.addFileArg(b.path(b.fmt("tests/{s}.zon", .{test_exe.name}))); - generate_test_data_step.dependOn(&generate_test_data_run.step); - - const test_runner_run = b.addRunArtifact(test_runner_exe); - test_runner_run.addFileArg(test_exe.getEmittedBin()); - test_runner_run.addFileArg(b.path(b.fmt("tests/{s}.zon", .{test_exe.name}))); - test_step.dependOn(&test_runner_run.step); } diff --git a/tools/printer/tests/test_program.dwarf32.zon b/tools/printer/tests/test_program.dwarf32.zon deleted file mode 100644 index 5aea5166c..000000000 --- a/tools/printer/tests/test_program.dwarf32.zon +++ /dev/null @@ -1,72 +0,0 @@ -.{ .zig_version = "0.17.1", .tests = .{ - .{ .address = 16945152, .expected = .{ - .line = 163, - .column = 33, - .file_path = "ubsan_rt.zig", - .function_name = null, - .module_name = "ubsan_rt.zig", - } }, - .{ .address = 16945225, .expected = .{ - .line = 164, - .column = 13, - .file_path = "ubsan_rt.zig", - .function_name = null, - .module_name = "ubsan_rt.zig", - } }, - .{ .address = 16945233, .expected = .{ - .line = 164, - .column = 67, - .file_path = "ubsan_rt.zig", - .function_name = null, - .module_name = "ubsan_rt.zig", - } }, - .{ .address = 16945253, .expected = .{ - .line = 165, - .column = 13, - .file_path = "ubsan_rt.zig", - .function_name = null, - .module_name = "ubsan_rt.zig", - } }, - .{ .address = 16945261, .expected = .{ - .line = 165, - .column = 67, - .file_path = "ubsan_rt.zig", - .function_name = null, - .module_name = "ubsan_rt.zig", - } }, - .{ .address = 16945281, .expected = .{ - .line = 166, - .column = 40, - .file_path = "ubsan_rt.zig", - .function_name = null, - .module_name = "ubsan_rt.zig", - } }, - .{ .address = 16945281, .expected = .{ - .line = 166, - .column = 40, - .file_path = "ubsan_rt.zig", - .function_name = null, - .module_name = "ubsan_rt.zig", - } }, - .{ .address = 16945289, .expected = .{ - .line = 166, - .column = 52, - .file_path = "ubsan_rt.zig", - .function_name = null, - .module_name = "ubsan_rt.zig", - } }, - .{ .address = 16945295, .expected = .{ - .line = 166, - .column = 52, - .file_path = "ubsan_rt.zig", - .function_name = null, - .module_name = "ubsan_rt.zig", - } }, - .{ .address = 16945398, .expected = .{ - .line = 167, - .column = 18, - .file_path = "ubsan_rt.zig", - .function_name = null, - .module_name = "ubsan_rt.zig", - } }, -} } diff --git a/tools/printer/tests/test_program.dwarf64.zon b/tools/printer/tests/test_program.dwarf64.zon deleted file mode 100644 index 4746f733f..000000000 --- a/tools/printer/tests/test_program.dwarf64.zon +++ /dev/null @@ -1,72 +0,0 @@ -.{ .zig_version = "0.17.1", .tests = .{ - .{ .address = 16969728, .expected = .{ - .line = 163, - .column = 33, - .file_path = "ubsan_rt.zig", - .function_name = null, - .module_name = "ubsan_rt.zig", - } }, - .{ .address = 16969801, .expected = .{ - .line = 164, - .column = 13, - .file_path = "ubsan_rt.zig", - .function_name = null, - .module_name = "ubsan_rt.zig", - } }, - .{ .address = 16969809, .expected = .{ - .line = 164, - .column = 67, - .file_path = "ubsan_rt.zig", - .function_name = null, - .module_name = "ubsan_rt.zig", - } }, - .{ .address = 16969829, .expected = .{ - .line = 165, - .column = 13, - .file_path = "ubsan_rt.zig", - .function_name = null, - .module_name = "ubsan_rt.zig", - } }, - .{ .address = 16969837, .expected = .{ - .line = 165, - .column = 67, - .file_path = "ubsan_rt.zig", - .function_name = null, - .module_name = "ubsan_rt.zig", - } }, - .{ .address = 16969857, .expected = .{ - .line = 166, - .column = 40, - .file_path = "ubsan_rt.zig", - .function_name = null, - .module_name = "ubsan_rt.zig", - } }, - .{ .address = 16969857, .expected = .{ - .line = 166, - .column = 40, - .file_path = "ubsan_rt.zig", - .function_name = null, - .module_name = "ubsan_rt.zig", - } }, - .{ .address = 16969865, .expected = .{ - .line = 166, - .column = 52, - .file_path = "ubsan_rt.zig", - .function_name = null, - .module_name = "ubsan_rt.zig", - } }, - .{ .address = 16969871, .expected = .{ - .line = 166, - .column = 52, - .file_path = "ubsan_rt.zig", - .function_name = null, - .module_name = "ubsan_rt.zig", - } }, - .{ .address = 16969974, .expected = .{ - .line = 167, - .column = 18, - .file_path = "ubsan_rt.zig", - .function_name = null, - .module_name = "ubsan_rt.zig", - } }, -} } From 7c7b94951cbbd0fb2214cd9e0879ff8d7cbe27ed Mon Sep 17 00:00:00 2001 From: Tudor Andrei Dicu Date: Thu, 5 Nov 2026 20:48:51 +0200 Subject: [PATCH 4/7] Remove printer from CI --- .github/workflows/ci.yml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3802a9040..d5e2ca73c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -108,25 +108,6 @@ jobs: run: zig build test working-directory: tools/esp-image - test-printer: - name: Test Printer - continue-on-error: true - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - runs-on: ${{ matrix.os }} - steps: - - name: Checkout - uses: actions/checkout@v7 - - name: Setup Zig - uses: mlugg/setup-zig@v2 - - name: Build Printer - run: zig build - working-directory: tools/printer - - name: Test Printer - run: zig build test - working-directory: tools/printer - build-sorcerer: name: Build Sorcerer if: false # Temporarily disabled: Sorcerer dependency fetches are flaky on CI. https://github.com/ZigEmbeddedGroup/microzig/issues/949 From c5c2d5d5a4094e6d2e598fecaf81d00602cfd876 Mon Sep 17 00:00:00 2001 From: Tudor Andrei Dicu Date: Thu, 5 Nov 2026 20:54:26 +0200 Subject: [PATCH 5/7] Remove unused buffers --- tools/printer/examples/rp2xxx_runner.zig | 1 - tools/printer/src/main.zig | 1 - 2 files changed, 2 deletions(-) diff --git a/tools/printer/examples/rp2xxx_runner.zig b/tools/printer/examples/rp2xxx_runner.zig index 38a6987b5..1a264262a 100644 --- a/tools/printer/examples/rp2xxx_runner.zig +++ b/tools/printer/examples/rp2xxx_runner.zig @@ -2,7 +2,6 @@ const std = @import("std"); const printer = @import("printer"); const serial = @import("serial"); -var elf_file_reader_buf: [1024]u8 = undefined; var in_stream_buf: [1024]u8 = undefined; var out_stream_buf: [1024]u8 = undefined; diff --git a/tools/printer/src/main.zig b/tools/printer/src/main.zig index 66a66aa50..751a46214 100644 --- a/tools/printer/src/main.zig +++ b/tools/printer/src/main.zig @@ -1,7 +1,6 @@ const std = @import("std"); const printer = @import("printer"); -var elf_file_reader_buf: [1024]u8 = undefined; var in_stream_buf: [1024]u8 = undefined; var out_stream_buf: [1024]u8 = undefined; From 0359801eda1e6614b4cae55dc176211120be71d3 Mon Sep 17 00:00:00 2001 From: Tudor Andrei Dicu Date: Fri, 6 Nov 2026 14:06:37 +0200 Subject: [PATCH 6/7] Fix packaging --- tools/printer/build.zig.zon | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/printer/build.zig.zon b/tools/printer/build.zig.zon index 21a896665..e82752489 100644 --- a/tools/printer/build.zig.zon +++ b/tools/printer/build.zig.zon @@ -7,8 +7,6 @@ .url = "git+https://github.com/ZigEmbeddedGroup/serial#25b58d0ea62f7bfc33e6c147c1c02193a50c65fb", .hash = "serial-0.0.1-PoeRzD66AACCQUHzsIssi6bOcEtnQFPebT6WLvno0I_p", }, - // used to compile the test program - // .microzig = .{ .path = "../..", .lazy = true }, }, .paths = .{ "build.zig", @@ -16,6 +14,5 @@ "README.md", "src", "examples", - "tests", }, } From d34c04186de6f7aa91a05019ccf601a5138b2d17 Mon Sep 17 00:00:00 2001 From: Tudor Andrei Dicu Date: Sun, 8 Nov 2026 14:51:11 +0200 Subject: [PATCH 7/7] Run build in CI and refactoring --- .github/workflows/ci.yml | 16 +++++++++ tools/printer/examples/rp2xxx_runner.zig | 4 ++- tools/printer/src/main.zig | 4 ++- tools/printer/src/root.zig | 46 ++++++++++++------------ 4 files changed, 45 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5e2ca73c..617354605 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -108,6 +108,22 @@ jobs: run: zig build test working-directory: tools/esp-image + test-printer: + name: Test Printer + continue-on-error: true + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout + uses: actions/checkout@v7 + - name: Setup Zig + uses: mlugg/setup-zig@v2 + - name: Build Printer + run: zig build + working-directory: tools/printer + build-sorcerer: name: Build Sorcerer if: false # Temporarily disabled: Sorcerer dependency fetches are flaky on CI. https://github.com/ZigEmbeddedGroup/microzig/issues/949 diff --git a/tools/printer/examples/rp2xxx_runner.zig b/tools/printer/examples/rp2xxx_runner.zig index 1a264262a..15a6f556c 100644 --- a/tools/printer/examples/rp2xxx_runner.zig +++ b/tools/printer/examples/rp2xxx_runner.zig @@ -20,6 +20,8 @@ pub fn main(init: std.process.Init) !void { // arch is ignored for elf files, .thumb is just a placeholder var debug_info: std.debug.Info = try .load(gpa, io, .initCwd(elf_path), &coverage, .elf, .thumb); defer debug_info.deinit(gpa); + // NOTE: this is safe because we specify the elf object format when loading the debug_info + const elf_file = &debug_info.impl.elf; const serial_device = try std.Io.Dir.cwd().openFile(io, serial_device_path, .{}); defer serial_device.close(io); @@ -54,7 +56,7 @@ pub fn main(init: std.process.Init) !void { io, &reader.interface, &terminal, - &debug_info.impl.elf, + elf_file, ); } } diff --git a/tools/printer/src/main.zig b/tools/printer/src/main.zig index 751a46214..d663574ec 100644 --- a/tools/printer/src/main.zig +++ b/tools/printer/src/main.zig @@ -21,6 +21,8 @@ pub fn main(init: std.process.Init) !void { // arch is ignored for elf files, .thumb is just a placeholder var debug_info: std.debug.Info = try .load(gpa, io, .initCwd(elf_path), &coverage, .elf, .thumb); defer debug_info.deinit(gpa); + // NOTE: this is safe because we specify the elf object format when loading the debug_info + const elf_file = &debug_info.impl.elf; const input_file = if (args.len <= 2 or std.mem.eql(u8, args[2], "-")) std.Io.File.stdin() @@ -46,7 +48,7 @@ pub fn main(init: std.process.Init) !void { io, &reader.interface, &terminal, - &debug_info.impl.elf, + elf_file, ); } } diff --git a/tools/printer/src/root.zig b/tools/printer/src/root.zig index b0f6ec9d2..c7c0eabd5 100644 --- a/tools/printer/src/root.zig +++ b/tools/printer/src/root.zig @@ -8,20 +8,20 @@ pub fn annotate( io: Io, in_stream: *Io.Reader, terminal: *Io.Terminal, - elf: *std.debug.ElfFile, + elf_file: *std.debug.ElfFile, ) !void { while (true) { const line = in_stream.takeDelimiterInclusive('\n') catch |err| switch (err) { error.ReadFailed => return error.ReadFailed, error.EndOfStream => { - try output_line(gpa, io, in_stream.buffered(), terminal, elf); + try output_line(gpa, io, in_stream.buffered(), terminal, elf_file); try terminal.writer.flush(); return; }, error.StreamTooLong => return error.StreamTooLong, }; - try output_line(gpa, io, line, terminal, elf); + try output_line(gpa, io, line, terminal, elf_file); try terminal.writer.flush(); } } @@ -31,7 +31,7 @@ fn output_line( io: Io, line: []const u8, terminal: *Io.Terminal, - elf: *std.debug.ElfFile, + elf_file: *std.debug.ElfFile, ) !void { try terminal.writer.writeAll(line); @@ -53,32 +53,32 @@ fn output_line( var text_arena: std.heap.ArenaAllocator = .init(gpa); defer text_arena.deinit(); - const di = &(elf.dwarf orelse return); - const compile_unit = di.findCompileUnit(elf.endian, address) catch return; - const maybe_name = di.getSymbolName(address); - const maybe_src_loc = di.getLineNumberInfo(gpa, text_arena.allocator(), elf.endian, compile_unit, address) catch null; + const di = &(elf_file.dwarf orelse return); + const compile_unit = di.findCompileUnit(elf_file.endian, address) catch return; + const maybe_symbol_name = di.getSymbolName(address); + const maybe_src_loc = di.getLineNumberInfo(gpa, text_arena.allocator(), elf_file.endian, compile_unit, address) catch null; - try output_location_info(terminal, address, maybe_name, maybe_src_loc); - if (maybe_src_loc) |src_loc| try output_source_line(io, terminal, src_loc); + if (maybe_src_loc) |src_loc| { + if (maybe_symbol_name) |symbol_name| { + try output_location_info(terminal, address, symbol_name, src_loc); + try output_source_line(io, terminal, src_loc); + } + } } fn output_location_info( terminal: *Io.Terminal, address: u64, - maybe_name: ?[]const u8, - maybe_src_loc: ?std.debug.SourceLocation, + symbol_name: []const u8, + src_loc: std.debug.SourceLocation, ) !void { try terminal.setColor(.bold); - if (maybe_src_loc) |src_loc| { - try terminal.writer.print("{s}:{}:{}", .{ - src_loc.file_name, - src_loc.line, - src_loc.column, - }); - } else { - try terminal.writer.writeAll("???:?:?"); - } + try terminal.writer.print("{s}:{}:{}", .{ + src_loc.file_name, + src_loc.line, + src_loc.column, + }); try terminal.setColor(.reset); try terminal.writer.writeAll(": "); @@ -86,8 +86,8 @@ fn output_location_info( try terminal.setColor(.dim); try terminal.writer.print("0x{x} in {s} ({s})\n", .{ address, - maybe_name orelse "???", - if (maybe_src_loc) |src_loc| std.Io.Dir.path.basename(src_loc.file_name) else "???", + symbol_name, + std.Io.Dir.path.basename(src_loc.file_name), }); try terminal.setColor(.reset); }