[PATCH v3] binutils/dwarf: Print embedded source, when available
Will Hawkins
hawkinsw@obs.cr
Wed Apr 22 00:26:33 GMT 2026
Thank you for taking the time to give feedback!
On Tue, Apr 21, 2026 at 12:03 PM Jan Beulich <jbeulich@suse.com> wrote:
>
> On 21.04.2026 04:02, Will Hawkins wrote:
> > v3 should contain a test that passes on aarch64 as well as x86_64. Sorry
> > for the hassle!
>
> What about other targets, including 32-bit ones?
I, unfortunately, have not tested on that target, yet.
>
> > --- a/binutils/dwarf.c
> > +++ b/binutils/dwarf.c
> > @@ -5847,6 +5847,10 @@ display_formatted_table (unsigned char *data,
> > case DW_LNCT_MD5:
> > printf (_("\tMD5\t\t\t"));
> > break;
> > + case DW_LNCT_LLVM_source:
> > + case DW_LNCT_source:
> > + /* Skip source ... display on next line. */
> > + break;
>
> Just keep recorded that I'm concerned of such unqualified uses of constants
> from custom ranges (LLVM in this case).
Ack. I am absolutely not unconcerned ... just trying to help make
objdump even more of a great tool than it is!
>
> > @@ -5861,8 +5865,10 @@ display_formatted_table (unsigned char *data,
> > unsigned char *datapass = data;
> >
> > printf (" %d", last_entry++);
> > - /* Delay displaying name as the last entry for better screen layout. */
> > - for (namepass = 0; namepass < 2; namepass++)
> > + /* Delay displaying name/source as the last entry for better screen
> > + layout. */
> > + int namesourcepass;
> > + for (namesourcepass = 0; namesourcepass < 3; namesourcepass++)
>
> for (unsigned int namesourcepass = 0; namesourcepass < 3; namesourcepass++)
>
> (suitably line wrapped if necessary)
I surely would have written it the way that you suggested. However,
when I looked through existing code, I did not see any formatted this
way. I assumed that was because of an interest in maintaining
compatibility with C89. I will absolutely make the update, of course,
but I just wanted to make sure you knew that I was trying my best to
follow the project's standards.
>
> > @@ -5872,13 +5878,31 @@ display_formatted_table (unsigned char *data,
> >
> > READ_ULEB (content_type, format, end);
> > READ_ULEB (form, format, end);
> > - bool do_loc = (content_type == DW_LNCT_path) != (namepass == 1);
> > +
> > + bool do_loc = (content_type == DW_LNCT_path)
> > + != (namesourcepass == 1);
> > + do_loc |= (content_type == DW_LNCT_LLVM_source
> > + || content_type == DW_LNCT_source)
> > + != (namesourcepass == 2);
>
> Indentation is better now that it (iirc) was in v1, but it's still not
> quite right. Binary operators wrapped to the next line want to align
> with the corresponding part of the expression on the earlier line. At
> the example ...
I will gladly fix! I was also wondering: These expressions are hard to
grok. I found the meaning hard to identify even in the existing code.
With the additional elements, it now seems like it might be a good
idea to expand into an if statement? I'd love feedback!
>
> > + char delimiter = '\t';
> > +
> > + /* Print Source last (if available) and print it
> > + starting on the next line. */
> > + if (namesourcepass == 2 && (content_type == DW_LNCT_LLVM_source
> > + || content_type == DW_LNCT_source))
>
> ... here:
>
> if (namesourcepass == 2
> && (content_type == DW_LNCT_LLVM_source
> || content_type == DW_LNCT_source))
>
> > --- /dev/null
> > +++ b/binutils/testsuite/binutils-all/dwarf-embedded-source.S
> > @@ -0,0 +1,240 @@
> > + .text
> > + .file "small.c"
> > + .globl main
> > + .p2align 4, 0x90
> > + .type main,@function
> > +main:
> > +.Lbegin_func_main:
> > +.Lend_func_main:
> > + .size main, .Lend_func_main-main
> > + .section .debug_abbrev,"",@progbits
> > + .byte 1 /* Abbreviation Code */
> > + .byte 17 /* DW_TAG_compile_unit */
> > + .byte 1 /* DW_CHILDREN_yes */
> > + .byte 37 /* DW_AT_producer */
> > + .byte 37 /* DW_FORM_strx1 */
> > + .byte 19 /* DW_AT_language */
> > + .byte 5 /* DW_FORM_data2 */
> > + .byte 3 /* DW_AT_name */
> > + .byte 37 /* DW_FORM_strx1 */
> > + .byte 114 /* DW_AT_str_offsets_base */
> > + .byte 23 /* DW_FORM_sec_offset */
> > + .byte 16 /* DW_AT_stmt_list */
> > + .byte 23 /* DW_FORM_sec_offset */
> > + .byte 27 /* DW_AT_comp_dir */
> > + .byte 37 /* DW_FORM_strx1 */
> > + .byte 17 /* DW_AT_low_pc */
> > + .byte 27 /* DW_FORM_addrx */
> > + .byte 18 /* DW_AT_high_pc */
> > + .byte 6 /* DW_FORM_data4 */
> > + .byte 115 /* DW_AT_addr_base */
> > + .byte 23 /* DW_FORM_sec_offset */
> > + .byte 0 /* EOM(1) */
> > + .byte 0 /* EOM(2) */
> > + .byte 2 /* Abbreviation Code */
> > + .byte 46 /* DW_TAG_subprogram */
> > + .byte 0 /* DW_CHILDREN_no */
> > + .byte 17 /* DW_AT_low_pc */
> > + .byte 27 /* DW_FORM_addrx */
> > + .byte 18 /* DW_AT_high_pc */
> > + .byte 6 /* DW_FORM_data4 */
> > + .byte 64 /* DW_AT_frame_base */
> > + .byte 24 /* DW_FORM_exprloc */
> > + .byte 3 /* DW_AT_name */
> > + .byte 37 /* DW_FORM_strx1 */
> > + .byte 58 /* DW_AT_decl_file */
> > + .byte 11 /* DW_FORM_data1 */
> > + .byte 59 /* DW_AT_decl_line */
> > + .byte 11 /* DW_FORM_data1 */
> > + .byte 73 /* DW_AT_type */
> > + .byte 19 /* DW_FORM_ref4 */
> > + .byte 63 /* DW_AT_external */
> > + .byte 25 /* DW_FORM_flag_present */
> > + .byte 0 /* EOM(1) */
> > + .byte 0 /* EOM(2) */
> > + .byte 3 /* Abbreviation Code */
> > + .byte 36 /* DW_TAG_base_type */
> > + .byte 0 /* DW_CHILDREN_no */
> > + .byte 3 /* DW_AT_name */
> > + .byte 37 /* DW_FORM_strx1 */
> > + .byte 62 /* DW_AT_encoding */
> > + .byte 11 /* DW_FORM_data1 */
> > + .byte 11 /* DW_AT_byte_size */
> > + .byte 11 /* DW_FORM_data1 */
> > + .byte 0 /* EOM(1) */
> > + .byte 0 /* EOM(2) */
> > + .byte 0 /* EOM(3) */
> > + .section .debug_info,"",@progbits
> > +.Lcu_begin0:
> > + .long .Ldebug_info_end0-.Ldebug_info_start0 /* Length of Unit */
> > +.Ldebug_info_start0:
> > + .short 5 /* DWARF version number */
> > + .byte 1 /* DWARF Unit Type */
> > + .byte 8 /* Address Size (in bytes) */
> > + .long .debug_abbrev /* Offset Into Abbrev. Section */
> > + .byte 1 /* Abbrev [1] 0xc:0x2b DW_TAG_compile_unit */
> > + .byte 0 /* DW_AT_producer */
> > + .short 29 /* DW_AT_language */
> > + .byte 1 /* DW_AT_name */
> > + .long .Lstr_offsets_base0 /* DW_AT_str_offsets_base */
> > + .long .Lline_table_start0 /* DW_AT_stmt_list */
> > + .byte 2 /* DW_AT_comp_dir */
> > + .byte 0 /* DW_AT_low_pc */
> > + .long .Lend_func_main-.Lbegin_func_main /* DW_AT_high_pc */
> > + .long .Laddr_table_base0 /* DW_AT_addr_base */
> > + .byte 2 /* Abbrev [2] 0x23:0xf DW_TAG_subprogram */
> > + .byte 0 /* DW_AT_low_pc */
> > + .long .Lend_func_main-.Lbegin_func_main /* DW_AT_high_pc */
> > + .byte 1 /* DW_AT_frame_base */
> > + .byte 86
> > + .byte 3 /* DW_AT_name */
> > + .byte 0 /* DW_AT_decl_file */
> > + .byte 1 /* DW_AT_decl_line */
> > + .long 50 /* DW_AT_type */
> > + /* DW_AT_external */
> > + .byte 3 /* Abbrev [3] 0x32:0x4 DW_TAG_base_type */
> > + .byte 4 /* DW_AT_name */
> > + .byte 5 /* DW_AT_encoding */
> > + .byte 4 /* DW_AT_byte_size */
> > + .byte 0 /* End Of Children Mark */
> > +.Ldebug_info_end0:
> > + .section .debug_str_offsets,"",@progbits
> > + .long 24 /* Length of String Offsets Set */
> > + .short 5
> > + .short 0
> > +.Lstr_offsets_base0:
> > + .section .debug_str,"MS",@progbits,1
> > +.Linfo_string0:
> > + .asciz "clang (with hand edits)" /* string offset=0 */
> > +.Linfo_string1:
> > + .asciz "small.c" /* string offset=44 */
> > +.Linfo_string2:
> > + .asciz "/path/to/code/"
> > +.Linfo_string3:
> > + .asciz "main" /* string offset=77 */
> > +.Linfo_string4:
> > + .asciz "int" /* string offset=82 */
> > + .section .debug_str_offsets,"",@progbits
> > + .long .Linfo_string0
> > + .long .Linfo_string1
> > + .long .Linfo_string2
> > + .long .Linfo_string3
> > + .long .Linfo_string4
> > + .section .debug_line_str,"MS",@progbits,1
> > +.Lline_string1:
> > + .asciz "/path/to/code/" /* string offset=0 */
> > +.Lline_string2:
> > + .asciz "small.c" /* string offset=15 */
> > +.Lline_string3:
> > + .asciz "int main() {\n return 0;\n}\n"/* string offset=23 */
> > + .section .debug_addr,"",@progbits
> > + .long .Ldebug_addr_end0-.Ldebug_addr_start0 /* Length of contribution */
> > +.Ldebug_addr_start0:
> > + .short 5 /* DWARF version number */
> > + .byte 8 /* Address size */
> > + .byte 0 /* Segment selector size */
> > +.Laddr_table_base0:
> > + .quad .Lbegin_func_main
> > +.Ldebug_addr_end0:
>
> Up to here the comments allow to reasonably follow what is there.
>
> > + .section .debug_line,"",@progbits
> > +.Lline_table_start0:
> > + .byte 0x60
> > + .byte 0x00
> > + .byte 0x00
> > + .byte 0x00
> > + .byte 0x05
> > + .byte 0x00
> > + .byte 0x08
> > + .byte 0x00
> > + .byte 0x3E
> > + .byte 0x00
> > + .byte 0x00
> > + .byte 0x00
> > + .byte 0x01
> > + .byte 0x01
> > + .byte 0x01
> > + .byte 0xfb
> > + .byte 0x0e
> > + .byte 0x0d
> > + .byte 0x00
> > + .byte 0x01
> > + .byte 0x01
> > + .byte 0x01
> > + .byte 0x01
> > + .byte 0x00
> > + .byte 0x00
> > + .byte 0x00
> > + .byte 0x01
> > + .byte 0x00
> > + .byte 0x00
> > + .byte 0x01
> > + .byte 0x01
> > + .byte 0x01
> > + .byte 0x1f
> > + .byte 0x01
> > + .byte 0x00 /* Offset to directory. */
> > + .byte 0x00
> > + .byte 0x00
> > + .byte 0x00
> > + .byte 0x04
> > + .byte 0x01
> > + .byte 0x1f
> > + .byte 0x02
> > + .byte 0x0f /* Offset to filename. */
> > + .byte 0x05
> > + .byte 0x1e
> > + .byte 0x81
> > + .byte 0x40
> > + .byte 0x1f
> > + .byte 0x01
> > + .byte 0x0f
> > + .byte 0x00
> > + .byte 0x00
> > + .byte 0x00
> > + .byte 0x00
> > + .byte 0x03
> > + .byte 0xad
> > + .byte 0x19
> > + .byte 0x9b
> > + .byte 0xfa
> > + .byte 0x21
> > + .byte 0x80
> > + .byte 0x26
> > + .byte 0xd0
> > + .byte 0xf1
> > + .byte 0xbe
> > + .byte 0x37
> > + .byte 0x41
> > + .byte 0x65
> > + .byte 0xa1
> > + .byte 0x6d
> > + .byte 0x17 /* Offset to source code. */
> > + .byte 0x00
> > + .byte 0x00
> > + .byte 0x00
> > + .byte 0x04
> > + .byte 0x00
> > + .byte 0x00
> > + .byte 0x09
> > + .byte 0x02
> > + .byte 0x10
> > + .byte 0x11
> > + .byte 0x40
> > + .byte 0x00
> > + .byte 0x00
> > + .byte 0x00
> > + .byte 0x00
> > + .byte 0x00
> > + .byte 0x01
> > + .byte 0x05
> > + .byte 0x05
> > + .byte 0x0a
> > + .byte 0xad
> > + .byte 0x06
> > + .byte 0x0b
> > + .byte 0x2e
> > + .byte 0x02
> > + .byte 0x02
> > + .byte 0x00
> > + .byte 0x01
> > + .byte 0x01
>
> The same cannot be said here, which makes this pretty hard to maintain (i.e.
> change if need be). Question is anyway - do we really need to resort to
> .byte to express what's wanted?
I will absolutely update -- your point is very well taken!
>
> > --- /dev/null
> > +++ b/binutils/testsuite/binutils-all/dwarf-embedded-source.rawline
> > @@ -0,0 +1,13 @@
> > +#...
> > + The Directory Table \(offset 0x22, lines 1, columns 1\):
> > +
> > + Entry Name
> > + 0 \(indirect line string, offset: 0\): \/path\/to\/code\/
> > +
> > + The File Name Table \(offset 0x31, lines 1, columns 4\):
> > + Entry Dir MD5 Name
> > + 0 0 0x6da1654137bef1d0268021fa9b19ad03 \(indirect line string, offset: 0xf\): small.c
> > + Source: \(indirect line string, offset: 0x17\): int main\(\) {
> > + return 0;
> > +}
>
> The figure braces would better also be escaped, I think.
>
> > --- a/binutils/testsuite/binutils-all/objdump.exp
> > +++ b/binutils/testsuite/binutils-all/objdump.exp
> > @@ -615,6 +615,30 @@ if { ![is_elf_format] } then {
> > file_on_host delete $output
> > }
> >
> > +# Test objdump --debug=rawline on a file containing dwarf-5 embedded source
> > +
> > +if { ![is_elf_format] } then {
> > + unsupported "objdump --debug=rawline-embedded-source test"
> > +} elseif { ![binutils_assemble $srcdir/$subdir/dwarf-embedded-source.S tmpdir/dwarf-embedded-source.${obj}] } then {
> > + fail "objdump --debug=rawline-embedded-source test"
> > +} else {
> > + if [is_remote host] {
> > + set op_testfile [remote_download host tmpdir/dwarf-embedded-source.${obj}]
> > + } else {
> > + set op_testfile tmpdir/dwarf-embedded-source.${obj}
> > + }
> > +
> > + set got [remote_exec host "$OBJDUMP $OBJDUMPFLAGS --dwarf=rawline $op_testfile" "" "/dev/null" "tmpdir/objdump.out"]
> > +
> > + if { [regexp_diff tmpdir/objdump.out $srcdir/$subdir/dwarf-embedded-source.rawline] } then {
> > + fail "objdump --debug=rawline-embedded-source test"
> > + } else {
> > + pass "objdump --debug=rawline-embedded-source test"
> > + }
> > +
> > + file_on_host delete $output
> > +}
>
> Is all of this necessary? Can't the test be done via a simple run_dump_test?
Good question! I will investigate!
>
> > --- a/include/dwarf2.h
> > +++ b/include/dwarf2.h
> > @@ -293,7 +293,9 @@ enum dwarf_line_number_content_type
> > DW_LNCT_timestamp = 0x3,
> > DW_LNCT_size = 0x4,
> > DW_LNCT_MD5 = 0x5,
> > + DW_LNCT_source = 0x6,
>
> This isn't official yet aiui, so may want annotating accordingly.
>
> More generally, Nick - what's the policy towards use of constants from not
> yet released specifications (which hence may still change)?
>
> Jan
Thank you, again, for your feedback! If the group decides that there
is no interest in supporting it, I'll be glad to back off!
Will
>
> > DW_LNCT_lo_user = 0x2000,
> > + DW_LNCT_LLVM_source = 0x2001,
> > DW_LNCT_hi_user = 0x3fff
> > };
> >
>
More information about the Binutils
mailing list