[3/3] binutils/testsuite: test compress-debug-info round trip

Message ID 20260928090146.4016188-4-tobias.deiminger@linutronix.de
State New
Headers
Series PE compression/corruption fixes with related test case |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

Tobias Deiminger Sept. 28, 2026, 9:01 a.m. UTC
  This adds a new test case that basically runs

 target_cc testprog.c testprog
 for every enabled BFD target
   objcopy -I <host> -O <target> testprog  # reformat to target
   objcopy --compress-debug-sections       # compress
   objdump --dwarf=info                    # main()/fn() info expected
   objcopy --decompress-debug-sections     # decompress
   objdump --dwarf=info                    # main()/fn() info expected

The input test vector is generated best effort (whatever target formats
can be created by objcopy reformatting from target compiler output as
starting point).

To increase coverage, this test can be repeated in multiple
--target=[x,y,z] build configurations.

Signed-off-by: Tobias Deiminger <tobias.deiminger@linutronix.de>
---
I tested the following configurations locally, leading to following
coverage:

configure --target=x86_64-pc-linux-gnu, 14 targets covered:
coff-go32 coff-go32-exe elf32-little elf32-x86-64 elf64-little
elf64-x86-64 elf64-x86-64-freebsd elf64-x86-64-sol2 pe-bigobj-i386
pe-bigobj-x86-64 pe-i386 pe-x86-64 pei-i386 pei-x86-64

configure --target=x86_64-w64-mingw32, 10 targets covered:
elf32-i386 elf32-little elf64-little elf64-x86-64 pe-bigobj-i386
pe-bigobj-x86-64 pe-i386 pe-x86-64 pei-i386 pei-x86-64

configure --target=aarch64-linux-gnu, 6 targets covered:
elf32-little elf32-littleaarch64 elf64-little elf64-littleaarch64
pe-aarch64-little pei-aarch64-little

configure --target=riscv64-linux-gnu, 5 targets covered:
elf32-little elf32-littleriscv elf64-little elf64-littleriscv
pei-riscv64-little

Note that the new test reveals another unrelated bug where

 objcopy -I elf64-x86-64 -O elf32-i386 testprog testprog-elf32-i386

produces invalid output that can't be processed further by objdump
("file format not recognized"). The issue isn't fixed here, since it
doesn't block the series. The test skips over it as unsupported.

 binutils/testsuite/binutils-all/compress.exp | 104 ++++++++++++++++++-
 1 file changed, 103 insertions(+), 1 deletion(-)
  

Comments

Jan Beulich Oct. 2, 2026, 1:17 p.m. UTC | #1
On 28.09.2026 11:01, Tobias Deiminger wrote:
> This adds a new test case that basically runs
> 
>  target_cc testprog.c testprog
>  for every enabled BFD target
>    objcopy -I <host> -O <target> testprog  # reformat to target
>    objcopy --compress-debug-sections       # compress
>    objdump --dwarf=info                    # main()/fn() info expected
>    objcopy --decompress-debug-sections     # decompress
>    objdump --dwarf=info                    # main()/fn() info expected
> 
> The input test vector is generated best effort (whatever target formats
> can be created by objcopy reformatting from target compiler output as
> starting point).
> 
> To increase coverage, this test can be repeated in multiple
> --target=[x,y,z] build configurations.
> 
> Signed-off-by: Tobias Deiminger <tobias.deiminger@linutronix.de>
> ---
> I tested the following configurations locally, leading to following
> coverage:
> 
> configure --target=x86_64-pc-linux-gnu, 14 targets covered:
> coff-go32 coff-go32-exe elf32-little elf32-x86-64 elf64-little
> elf64-x86-64 elf64-x86-64-freebsd elf64-x86-64-sol2 pe-bigobj-i386
> pe-bigobj-x86-64 pe-i386 pe-x86-64 pei-i386 pei-x86-64
> 
> configure --target=x86_64-w64-mingw32, 10 targets covered:
> elf32-i386 elf32-little elf64-little elf64-x86-64 pe-bigobj-i386
> pe-bigobj-x86-64 pe-i386 pe-x86-64 pei-i386 pei-x86-64
> 
> configure --target=aarch64-linux-gnu, 6 targets covered:
> elf32-little elf32-littleaarch64 elf64-little elf64-littleaarch64
> pe-aarch64-little pei-aarch64-little
> 
> configure --target=riscv64-linux-gnu, 5 targets covered:
> elf32-little elf32-littleriscv elf64-little elf64-littleriscv
> pei-riscv64-little

This is pretty good, yet before committing we will want to be sure that
a wider range of targets isn't going to hit new failures. For example,
I can't spot how you prevent aout targets from being tried.

> Note that the new test reveals another unrelated bug where
> 
>  objcopy -I elf64-x86-64 -O elf32-i386 testprog testprog-elf32-i386
> 
> produces invalid output that can't be processed further by objdump
> ("file format not recognized"). The issue isn't fixed here, since it
> doesn't block the series. The test skips over it as unsupported.

Just for my own understanding - that is as a result of ...

> --- a/binutils/testsuite/binutils-all/compress.exp
> +++ b/binutils/testsuite/binutils-all/compress.exp
> @@ -14,9 +14,111 @@
>  # along with this program; if not, write to the Free Software
>  # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
>  
> +if { [is_remote host] } then {
> +    return
> +}
> +
> +proc assert_testprog_debug_info { testname target testprog_bin expected } {
> +    global OBJDUMP
> +
> +    set dwarf_info_lines [split [binutils_run $OBJDUMP "-b $target --dwarf=info $testprog_bin"] \n]
> +    set expected_nr [llength $expected]
> +    set dw_lines_nr [llength $dwarf_info_lines]
> +    for {set i 0} {$i < $dw_lines_nr} {incr i} {
> +	set line [lindex $dwarf_info_lines $i]
> +	set is_entry_start [string equal -length 2 " <" $line]
> +	if { !$is_entry_start && [regexp {DW_AT_(\S+)\s*:.*\s(\S+)$} $line -> attr value] } {
> +	    set dw_entry($attr) $value
> +	}
> +	if { $is_entry_start || $i == $dw_lines_nr - 1 } {
> +	    if { [info exists dw_entry(name)] && [info exists dw_entry(decl_line)]
> +		&& [lsearch -exact $expected [list $dw_entry(name) $dw_entry(decl_line)]] >= 0 } {
> +		incr expected_nr -1
> +	    }
> +	    array unset dw_entry
> +	}
> +    }
> +    if { $expected_nr == 0 } {
> +	pass "$testname"
> +    } else {
> +	fail "$testname"
> +    }
> +}
> +
> +proc test_compress_decompress_roundtrip_all_targets {} {
> +    global binutils_run_status
> +    global srcdir
> +    global subdir
> +    global OBJCOPY
> +    global OBJDUMP
> +
> +    set test "objcopy compress debug sections round trip"
> +    set known_unsupported_targets {
> +	msdos binary srec symbolsrec ihex verilog wasm
> +	coff-i386 coff-i386-lynx coff-x86-64
> +    }
> +    set test_in_host "tmpdir/testprog[exeext]"
> +
> +    if { [target_compile $srcdir/$subdir/testprog.c $test_in_host executable debug] != "" } {
> +	unsupported "$test (can't compile testprog)"
> +	return
> +    }
> +
> +    set objdump_out [binutils_run $OBJDUMP "-f $test_in_host"]
> +    if { ![regexp {file format (\S+)} $objdump_out -> input_fmt] } {
> +	unsupported "$test (can't determine BFD input format)"
> +	return
> +    }
> +
> +    set info [split [binutils_run $OBJCOPY "--info"] \n]
> +    set enabled_targets [lsort -unique [lsearch -all -inline -regexp [lrange $info 1 end] {^[[:alnum:]]}]]
> +
> +    set tested 0
> +    set expected { {fn 12} {main 18} }
> +    foreach target $enabled_targets {
> +	if { [lsearch -exact $known_unsupported_targets $target] >= 0 } continue
> +
> +	set test_in "tmpdir/testprog-[string map {/ _} $target]"
> +	set test_z   "${test_in}.z"
> +	set test_unz "${test_in}.unz"
> +
> +	binutils_run $OBJCOPY "-I $input_fmt -O $target $test_in_host $test_in"
> +	if { $binutils_run_status != 0 } continue
> +	binutils_run $OBJDUMP "-f -b $target $test_in"
> +	if { $binutils_run_status != 0 } {
> +	    unsupported "$test ($target, unreadable after conversion from $input_fmt)"
> +	    continue
> +	}

... this check?

As to known_unsupported_targets: What's wrong with coff-i386 and
coff-i386-lynx? And why is coff-x86-64, when there's no target picking
x86_64_coff_vec? Oh, apparently --enable-targets=all can pick that up,
yet very likely that's wrong.

Jan
  

Patch

diff --git a/binutils/testsuite/binutils-all/compress.exp b/binutils/testsuite/binutils-all/compress.exp
index ae2bf8010f8..1dd2389e21b 100644
--- a/binutils/testsuite/binutils-all/compress.exp
+++ b/binutils/testsuite/binutils-all/compress.exp
@@ -14,9 +14,111 @@ 
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
 
+if { [is_remote host] } then {
+    return
+}
+
+proc assert_testprog_debug_info { testname target testprog_bin expected } {
+    global OBJDUMP
+
+    set dwarf_info_lines [split [binutils_run $OBJDUMP "-b $target --dwarf=info $testprog_bin"] \n]
+    set expected_nr [llength $expected]
+    set dw_lines_nr [llength $dwarf_info_lines]
+    for {set i 0} {$i < $dw_lines_nr} {incr i} {
+	set line [lindex $dwarf_info_lines $i]
+	set is_entry_start [string equal -length 2 " <" $line]
+	if { !$is_entry_start && [regexp {DW_AT_(\S+)\s*:.*\s(\S+)$} $line -> attr value] } {
+	    set dw_entry($attr) $value
+	}
+	if { $is_entry_start || $i == $dw_lines_nr - 1 } {
+	    if { [info exists dw_entry(name)] && [info exists dw_entry(decl_line)]
+		&& [lsearch -exact $expected [list $dw_entry(name) $dw_entry(decl_line)]] >= 0 } {
+		incr expected_nr -1
+	    }
+	    array unset dw_entry
+	}
+    }
+    if { $expected_nr == 0 } {
+	pass "$testname"
+    } else {
+	fail "$testname"
+    }
+}
+
+proc test_compress_decompress_roundtrip_all_targets {} {
+    global binutils_run_status
+    global srcdir
+    global subdir
+    global OBJCOPY
+    global OBJDUMP
+
+    set test "objcopy compress debug sections round trip"
+    set known_unsupported_targets {
+	msdos binary srec symbolsrec ihex verilog wasm
+	coff-i386 coff-i386-lynx coff-x86-64
+    }
+    set test_in_host "tmpdir/testprog[exeext]"
+
+    if { [target_compile $srcdir/$subdir/testprog.c $test_in_host executable debug] != "" } {
+	unsupported "$test (can't compile testprog)"
+	return
+    }
+
+    set objdump_out [binutils_run $OBJDUMP "-f $test_in_host"]
+    if { ![regexp {file format (\S+)} $objdump_out -> input_fmt] } {
+	unsupported "$test (can't determine BFD input format)"
+	return
+    }
+
+    set info [split [binutils_run $OBJCOPY "--info"] \n]
+    set enabled_targets [lsort -unique [lsearch -all -inline -regexp [lrange $info 1 end] {^[[:alnum:]]}]]
+
+    set tested 0
+    set expected { {fn 12} {main 18} }
+    foreach target $enabled_targets {
+	if { [lsearch -exact $known_unsupported_targets $target] >= 0 } continue
+
+	set test_in "tmpdir/testprog-[string map {/ _} $target]"
+	set test_z   "${test_in}.z"
+	set test_unz "${test_in}.unz"
+
+	binutils_run $OBJCOPY "-I $input_fmt -O $target $test_in_host $test_in"
+	if { $binutils_run_status != 0 } continue
+	binutils_run $OBJDUMP "-f -b $target $test_in"
+	if { $binutils_run_status != 0 } {
+	    unsupported "$test ($target, unreadable after conversion from $input_fmt)"
+	    continue
+	}
+
+	incr tested
+	set testname "$test (compress, $target)"
+
+	binutils_run $OBJCOPY "-I $target --compress-debug-sections $test_in $test_z"
+	if { $binutils_run_status != 0 } {
+	    fail $testname
+	    continue
+	}
+	assert_testprog_debug_info $testname $target $test_z $expected
+
+	set testname "$test (decompress, $target)"
+	binutils_run $OBJCOPY "-I $target --decompress-debug-sections $test_z $test_unz"
+	if { $binutils_run_status != 0 } {
+	    fail $testname
+	    continue
+	}
+	assert_testprog_debug_info $testname $target $test_unz $expected
+    }
+
+    if { $tested == 0 } {
+	unsupported "$test (no compatible targets found)"
+    }
+}
+
+test_compress_decompress_roundtrip_all_targets
+
 # Test compressed .debug section.
 
-if { [is_remote host] || ![is_elf_format] } then {
+if { ![is_elf_format] } then {
     return
 }