diff --git a/tests/data/SOURCES/bar.c b/tests/data/SOURCES/bar.c new file mode 100644 index 0000000..20c6bd8 --- /dev/null +++ b/tests/data/SOURCES/bar.c @@ -0,0 +1,20 @@ +#include "foobar.h" + +struct debug_types_section1 var_bar1; + +/* Use different DW_AT_stmt_list in .debug_types having it outside foobar.h. + Also use two types in .debug_types for multiple COMDAT sections in object + files. */ +struct debug_types_section_bar +{ + int stringp_bar, stb; +}; +struct debug_types_section_bar var_bar; + +int number = NUMBER; + +int +bar (int n) +{ + return n - NUMBER; +} diff --git a/tests/data/SOURCES/baz.c b/tests/data/SOURCES/baz.c new file mode 100644 index 0000000..ceab9f1 --- /dev/null +++ b/tests/data/SOURCES/baz.c @@ -0,0 +1,21 @@ +#include "foobar.h" + +struct debug_types_section1 var_baz1; + +/* Use different DW_AT_stmt_list in .debug_types having it outside foobar.h. + Also use two types in .debug_types for multiple COMDAT sections in object + files. */ +struct debug_types_section_baz +{ + int stringp_baz, stz; +}; +struct debug_types_section_baz var_baz; + +int +main () +{ + int res; + res = foo (); + res = bar (res); + return res + number - NUMBER; +} diff --git a/tests/data/SOURCES/foo.c b/tests/data/SOURCES/foo.c new file mode 100644 index 0000000..76783d5 --- /dev/null +++ b/tests/data/SOURCES/foo.c @@ -0,0 +1,18 @@ +#include "foobar.h" + +struct debug_types_section1 var_foo1; + +/* Use different DW_AT_stmt_list in .debug_types having it outside foobar.h. + Also use two types in .debug_types for multiple COMDAT sections in object + files. */ +struct debug_types_section_foo +{ + int stringp_foo, stf; +}; +struct debug_types_section_foo var_foo; + +int +foo () +{ + return number; +} diff --git a/tests/data/SOURCES/foobar.h b/tests/data/SOURCES/foobar.h new file mode 100644 index 0000000..fa493f5 --- /dev/null +++ b/tests/data/SOURCES/foobar.h @@ -0,0 +1,12 @@ +#define NUMBER 42 + +extern int number; + +extern int foo (void); +extern int bar (int); + +struct debug_types_section1 +{ + /* Test both DW_FORM_strp and DW_FORM_string. */ + int stringp1, st1; +}; diff --git a/tests/debugedit.at b/tests/debugedit.at new file mode 100644 index 0000000..49721a3 --- /dev/null +++ b/tests/debugedit.at @@ -0,0 +1,487 @@ +# debugedit.at: Tests for the debugedit tool +# +# Copyright (C) 2019 Mark J. Wielaard +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see see . + +# Tests for the tools/debugedit program. +AT_BANNER([RPM debugedit]) + +# Show which debugedit binary we are testing. +AT_TESTED([debugedit]) + +# Helper to create some test binaries. +# Optional parameter can specify additional gcc parameters. +m4_define([RPM_DEBUGEDIT_SETUP],[[ +# Create some test binaries. Create and build them in different subdirs +# to make sure they produce different relative/absolute paths. + +export HOME=${PWD} +mkdir subdir_foo +cp "${abs_srcdir}"/data/SOURCES/foo.c subdir_foo +mkdir subdir_bar +cp "${abs_srcdir}"/data/SOURCES/bar.c subdir_bar +mkdir subdir_headers +cp "${abs_srcdir}"/data/SOURCES/foobar.h subdir_headers +cp "${abs_srcdir}"/data/SOURCES/baz.c . + +# First three object files (foo.o subdir_bar/bar.o and baz.o) +gcc -g3 -Isubdir_headers $1 -c subdir_foo/foo.c +cd subdir_bar +gcc -g3 -I../subdir_headers $1 -c bar.c +cd .. +gcc -g3 -I$(pwd)/subdir_headers $1 -c $(pwd)/baz.c + +# Then a partially linked object file (somewhat like a kernel module). +# This will still have relocations between the debug sections. +ld -r -o foobarbaz.part.o foo.o subdir_bar/bar.o baz.o + +# Create an executable. Relocations between debug sections will +# have been resolved. +gcc -g3 -o foobarbaz.exe foo.o subdir_bar/bar.o baz.o +]]) + +# === +# Check debugedit --help doesn't crash and burn. +# === +AT_SETUP([debugedit help]) +AT_KEYWORDS([debuginfo] [debugedit]) +AT_CHECK([debugedit --help],[0],[ignore],[ignore]) +AT_CLEANUP + +# === +# Make sure that an executable still runs after debugedit munged it. +# === +AT_SETUP([debugedit executable]) +AT_KEYWORDS([debuginfo] [debugedit]) +RPM_DEBUGEDIT_SETUP + +AT_CHECK([[./foobarbaz.exe]]) +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.exe]]) +AT_CHECK([[./foobarbaz.exe]]) + +AT_CLEANUP + +# === +# debugedit should at least replace the .debug_str directory paths +# in the objects. +# === +AT_SETUP([debugedit .debug_str objects]) +AT_KEYWORDS([debuginfo] [debugedit]) +RPM_DEBUGEDIT_SETUP + +# Capture strings that start with the testdir (pwd) directory path +# (and replace that textually with /foo/bar/baz) +readelf -p.debug_str foo.o subdir_bar/bar.o baz.o | cut -c13- \ + | grep ^$(pwd) | sort \ + | sed -e "s@$(pwd)@/foo/bar/baz@" > expout + +# Make sure there is at least some output +expout_lines=$(wc --lines expout | cut -f1 -d\ ) +if test $expout_lines -lt 3; then + echo "Expecting at least 3 debug strings starting with ${testdir}" >> expout +fi + +# Check the replaced strings are all there. +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foo.o]]) +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./subdir_bar/bar.o]]) +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./baz.o]]) +AT_CHECK([[ +readelf -p.debug_str foo.o subdir_bar/bar.o baz.o | cut -c13- \ + | grep ^/foo/bar/baz | sort +]],[0],[expout]) + +AT_CLEANUP + +# === +# debugedit should at least replace the .debug_str directory paths +# also in partially linked files. +# === +AT_SETUP([debugedit .debug_str partial]) +AT_KEYWORDS([debuginfo] [debugedit]) +RPM_DEBUGEDIT_SETUP + +# Capture strings that start with the testdir (pwd) directory path +# (and replace that textually with /foo/bar/baz) +# Note that partially linked files, might have multiple duplicate +# strings, but debugedit will merge them. So use sort -u. +readelf -p.debug_str ./foobarbaz.part.o | cut -c13- \ + | grep ^$(pwd) | sort -u \ + | sed -e "s@$(pwd)@/foo/bar/baz@" > expout + +# Make sure there is at least some output +expout_lines=$(wc --lines expout | cut -f1 -d\ ) +if test $expout_lines -lt 3; then + echo "Expecting at least 3 debug strings starting with ${testdir}" >> expout +fi + +# Check the replaced strings are all there. +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.part.o]]) +AT_CHECK([[ +readelf -p.debug_str ./foobarbaz.part.o | cut -c13- \ + | grep ^/foo/bar/baz | sort +]],[0],[expout]) + +AT_CLEANUP + +# === +# debugedit should at least replace the .debug_str directory paths +# and in the executable. +# === +AT_SETUP([debugedit .debug_str exe]) +AT_KEYWORDS([debuginfo] [debugedit]) +RPM_DEBUGEDIT_SETUP + +# Capture strings that start with the testdir (pwd) directory path +# (and replace that textually with /foo/bar/baz) +readelf -p.debug_str foobarbaz.exe | cut -c13- \ + | grep ^$(pwd) | sort \ + | sed -e "s@$(pwd)@/foo/bar/baz@" > expout + +# Make sure there is at least some output +# The linker will have merged unique strings, so no need for sort -u. +expout_lines=$(wc --lines expout | cut -f1 -d\ ) +if test $expout_lines -lt 3; then + echo "Expecting at least 3 debug strings starting with ${testdir}" >> expout +fi + +# Check the replaced strings are all there. +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.exe]]) +AT_CHECK([[ +readelf -p.debug_str foobarbaz.exe | cut -c13- \ + | grep ^/foo/bar/baz | sort +]],[0],[expout]) + +AT_CLEANUP + +# For .debug_info we expect the following DW_AT_name and DW_AT_comp_dir +# strings for the DW_TAG_compile_unit: +# - foo.o +# DW_AT_name: subdir_foo/foo.c +# DW_AT_comp_dir: /foo/baz/baz +# - bar.o +# DW_AT_name: bar.c +# DW_AT_comp_dir: /foo/bar/baz/subdir_bar +# - baz.o +# DW_AT_name: /foo/bar/baz/baz.c +# DW_AT_comp_dir: /foo/baz/baz +# +# Older gcc (before 7) don't emit the DW_AT_comp_dir for baz.o. +# But because it is similar to the comp_dir of foo.o, just sort -u. + +# === +# Make sure DW_AT_name and DW_AT_comp_dir strings are replaced +# in objects. +# === +AT_SETUP([debugedit .debug_info objects]) +AT_KEYWORDS([debuginfo] [debugedit]) +RPM_DEBUGEDIT_SETUP + +AT_DATA([expout], +[/foo/bar/baz +/foo/bar/baz/baz.c +/foo/bar/baz/subdir_bar +]) + +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foo.o]]) +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./subdir_bar/bar.o]]) +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./baz.o]]) +AT_CHECK([[ +readelf --debug-dump=info foo.o subdir_bar/bar.o baz.o \ + | grep -E 'DW_AT_(name|comp_dir)' \ + | rev | cut -d: -f1 | rev | cut -c2- | grep ^/foo/bar/baz | sort -u +]],[0],[expout]) + +AT_CLEANUP + +# === +# Make sure DW_AT_name and DW_AT_comp_dir strings are replaced +# in partial linked object. +# === +AT_SETUP([debugedit .debug_info partial]) +AT_KEYWORDS([debuginfo] [debugedit]) +RPM_DEBUGEDIT_SETUP + +AT_DATA([expout], +[/foo/bar/baz +/foo/bar/baz/baz.c +/foo/bar/baz/subdir_bar +]) + +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.part.o]]) +AT_CHECK([[ +readelf --debug-dump=info ./foobarbaz.part.o \ + | grep -E 'DW_AT_(name|comp_dir)' \ + | rev | cut -d: -f1 | rev | cut -c2- | grep ^/foo/bar/baz | sort -u +]],[0],[expout]) + +AT_CLEANUP + +# === +# Make sure DW_AT_name and DW_AT_comp_dir strings are replaced +# in executable. +# === +AT_SETUP([debugedit .debug_info exe]) +AT_KEYWORDS([debuginfo] [debugedit]) +RPM_DEBUGEDIT_SETUP + +AT_DATA([expout], +[/foo/bar/baz +/foo/bar/baz/baz.c +/foo/bar/baz/subdir_bar +]) + +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.exe]]) +AT_CHECK([[ +readelf --debug-dump=info ./foobarbaz.exe | grep -E 'DW_AT_(name|comp_dir)' \ + | rev | cut -d: -f1 | rev | cut -c2- | grep ^/foo/bar/baz | sort -u +]],[0],[expout]) + +AT_CLEANUP + +# === +# Make sure -fdebug-types-section has updated strings in objects. +# === +AT_SETUP([debugedit .debug_types objects]) +AT_KEYWORDS([debugtypes] [debugedit]) +RPM_DEBUGEDIT_SETUP([-fdebug-types-section]) + +AT_DATA([expout], +[st1 +stf +stringp1 +stringp_foo +st1 +stb +stringp1 +stringp_bar +st1 +stringp1 +stringp_baz +stz +]) + +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foo.o]]) +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./subdir_bar/bar.o]]) +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./baz.o]]) +AT_CHECK([[ +for i in ./foo.o ./subdir_bar/bar.o ./baz.o;do \ + readelf --debug-dump=info $i \ + | awk '/Abbrev Number:.*DW_TAG_type_unit/{p=1}{if(p)print}/^$/{p=0}' \ + | sed -n 's/^.*> *DW_AT_name *:.* \(stringp[^ ]*\|st.\)$/\1/p' \ + | sort; +done +]],[0],[expout]) + +AT_CLEANUP + +# === +# Make sure -fdebug-types-section has updated strings in partial linked object. +# === +AT_SETUP([debugedit .debug_types partial]) +AT_KEYWORDS([debugtypes] [debugedit]) +RPM_DEBUGEDIT_SETUP([-fdebug-types-section]) + +AT_DATA([expout], +[st1 +stb +stf +stringp1 +stringp_bar +stringp_baz +stringp_foo +stz +]) + +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.part.o]]) +AT_CHECK([[ +readelf --debug-dump=info ./foobarbaz.part.o \ + | awk '/Abbrev Number:.*DW_TAG_type_unit/{p=1}{if(p)print}/^$/{p=0}' \ + | sed -n 's/^.*> *DW_AT_name *:.* \(stringp[^ ]*\|st.\)$/\1/p' \ + | sort +]],[0],[expout]) + +AT_CLEANUP + +# === +# Make sure -fdebug-types-section has updated strings in executable. +# === +AT_SETUP([debugedit .debug_types exe]) +AT_KEYWORDS([debugtypes] [debugedit]) +RPM_DEBUGEDIT_SETUP([-fdebug-types-section]) + +AT_DATA([expout], +[st1 +stb +stf +stringp1 +stringp_bar +stringp_baz +stringp_foo +stz +]) + +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.exe]]) +AT_CHECK([[ +readelf --debug-dump=info ./foobarbaz.exe \ + | awk '/Abbrev Number:.*DW_TAG_type_unit/{p=1}{if(p)print}/^$/{p=0}' \ + | sed -n 's/^.*> *DW_AT_name *:.* \(stringp[^ ]*\|st.\)$/\1/p' \ + | sort +]],[0],[expout]) + +AT_CLEANUP + +# foo.o and bar.o are build with relative paths and so will use the +# comp_dir (from .debug_info). But bar.o is build from sources with +# an absolute path, so the .debug_line Directory Table should contain +# /foo/bar/baz and /foo/bar/baz/subdir_headers. + +# === +# Make sure .debug_line Directory Table entries are replaced +# in objects. +# === +AT_SETUP([debugedit .debug_line objects]) +AT_KEYWORDS([debuginfo] [debugedit]) +RPM_DEBUGEDIT_SETUP + +AT_DATA([expout], +[/foo/bar/baz +/foo/bar/baz/subdir_headers +]) + +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foo.o]]) +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./subdir_bar/bar.o]]) +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./baz.o]]) +AT_CHECK([[ +readelf --debug-dump=line foo.o subdir_bar/bar.o baz.o \ + | grep -A3 "The Directory Table" | grep "^ [123]" \ + | grep /foo/ | cut -c5- | sort +]],[0],[expout]) + +AT_CLEANUP + +# === +# Make sure .debug_line Directory Table entries are replaced +# in partial linked object. +# === +AT_SETUP([debugedit .debug_line partial]) +AT_KEYWORDS([debuginfo] [debugedit]) +RPM_DEBUGEDIT_SETUP + +AT_DATA([expout], +[/foo/bar/baz +/foo/bar/baz/subdir_headers +]) + +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.part.o]]) +AT_CHECK([[ +readelf --debug-dump=line ./foobarbaz.part.o \ + | grep -A3 "The Directory Table" | grep "^ [123]" \ + | grep /foo/ | cut -c5- | sort +]],[0],[expout]) + +AT_CLEANUP + +# === +# Make sure .debug_line Directory Table entries are replaced +# in executable. +# === +AT_SETUP([debugedit .debug_line exe]) +AT_KEYWORDS([debuginfo] [debugedit]) +RPM_DEBUGEDIT_SETUP + +AT_DATA([expout], +[/foo/bar/baz +/foo/bar/baz/subdir_headers +]) + +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.exe]]) +AT_CHECK([[ +readelf --debug-dump=line ./foobarbaz.exe \ + | grep -A3 "The Directory Table" | grep "^ [123]" \ + | grep /foo/ | cut -c5- | sort +]],[0],[expout]) + +AT_CLEANUP + +# === +# Make sure .debug_macro strings are still there +# in objects. +# === +AT_SETUP([debugedit .debug_macro objects]) +AT_KEYWORDS([debuginfo] [debugedit]) +RPM_DEBUGEDIT_SETUP + +# We expect 3 for each compile unit. +AT_DATA([expout], +[NUMBER 42 +NUMBER 42 +NUMBER 42 +]) + +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foo.o]]) +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./subdir_bar/bar.o]]) +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./baz.o]]) +AT_CHECK([[ +readelf --debug-dump=macro foo.o subdir_bar/bar.o baz.o \ + | grep NUMBER | rev | cut -d: -f1 | rev | cut -c2- +]],[0],[expout]) + +AT_CLEANUP + +# === +# Make sure .debug_macro strings are still there +# in partial linked object. +# === +AT_SETUP([debugedit .debug_macro partial]) +AT_KEYWORDS([debuginfo] [debugedit]) +RPM_DEBUGEDIT_SETUP + +# We expect 3 for each compile unit. +AT_DATA([expout], +[NUMBER 42 +NUMBER 42 +NUMBER 42 +]) + +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.part.o]]) +AT_CHECK([[ +readelf --debug-dump=macro ./foobarbaz.part.o \ + | grep NUMBER | rev | cut -d: -f1 | rev | cut -c2- +]],[0],[expout]) + +AT_CLEANUP + +# === +# Make sure .debug_macro strings are still there +# in executable. +# === +AT_SETUP([debugedit .debug_macro exe]) +AT_KEYWORDS([debuginfo] [debugedit]) +RPM_DEBUGEDIT_SETUP + +# We expect 3 for each compile unit. +AT_DATA([expout], +[NUMBER 42 +NUMBER 42 +NUMBER 42 +]) + +AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.exe]]) +AT_CHECK([[ +readelf --debug-dump=macro ./foobarbaz.exe \ + | grep NUMBER | rev | cut -d: -f1 | rev | cut -c2- +]],[0],[expout]) + +AT_CLEANUP