[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