/* See build-id.h. */
std::string
-find_separate_debug_file_by_buildid (struct objfile *objfile)
+find_separate_debug_file_by_buildid (struct objfile *objfile,
+ std::vector<std::string> *warnings_vector)
{
const struct bfd_build_id *build_id;
if (abfd != NULL
&& filename_cmp (bfd_get_filename (abfd.get ()),
objfile_name (objfile)) == 0)
- warning (_("\"%s\": separate debug info file has no debug info"),
- bfd_get_filename (abfd.get ()));
+ {
+ std::string msg
+ = string_printf (_("\"%s\": separate debug info file has no "
+ "debug info"), bfd_get_filename (abfd.get ()));
+ if (separate_debug_file_debug)
+ gdb_printf (gdb_stdlog, "%s", msg.c_str ());
+ warnings_vector->emplace_back (std::move (msg));
+ }
else if (abfd != NULL)
return std::string (bfd_get_filename (abfd.get ()));
}
/* Find the separate debug file for OBJFILE, by using the build-id
associated with OBJFILE's BFD. If successful, returns the file name for the
- separate debug file, otherwise, return an empty string. */
+ separate debug file, otherwise, return an empty string.
+
+ Any warnings that are generated by the lookup process should be added to
+ WARNINGS_VECTOR, one std::string per warning. If some other mechanism can
+ be used to lookup the debug information then the warning will not be shown,
+ however, if GDB fails to find suitable debug information using any
+ approach, then any warnings will be printed. */
extern std::string find_separate_debug_file_by_buildid
- (struct objfile *objfile);
+ (struct objfile *objfile, std::vector<std::string> *warnings_vector);
/* Return an hex-string representation of BUILD_ID. */
/* Try to add separate debug file if no symbols table found. */
if (!objfile->has_partial_symbols ())
{
- std::string debugfile = find_separate_debug_file_by_buildid (objfile);
+ std::vector<std::string> warnings_vector;
+ std::string debugfile
+ = find_separate_debug_file_by_buildid (objfile, &warnings_vector);
if (debugfile.empty ())
- debugfile = find_separate_debug_file_by_debuglink (objfile);
+ debugfile
+ = find_separate_debug_file_by_debuglink (objfile, &warnings_vector);
if (!debugfile.empty ())
{
symbol_file_add_separate (debug_bfd, debugfile.c_str (),
symfile_flags, objfile);
}
+ /* If all the methods to collect the debuginfo failed, print any
+ warnings that were collected. */
+ if (debugfile.empty () && !warnings_vector.empty ())
+ for (const std::string &w : warnings_vector)
+ warning ("%s", w.c_str ());
}
}
&& objfile->separate_debug_objfile == NULL
&& objfile->separate_debug_objfile_backlink == NULL)
{
- std::string debugfile = find_separate_debug_file_by_buildid (objfile);
+ std::vector<std::string> warnings_vector;
+
+ std::string debugfile
+ = find_separate_debug_file_by_buildid (objfile, &warnings_vector);
if (debugfile.empty ())
- debugfile = find_separate_debug_file_by_debuglink (objfile);
+ debugfile = find_separate_debug_file_by_debuglink (objfile,
+ &warnings_vector);
if (!debugfile.empty ())
{
}
}
}
+ /* If all the methods to collect the debuginfo failed, print
+ the warnings, if there're any. */
+ if (debugfile.empty () && !has_dwarf2 && !warnings_vector.empty ())
+ for (const std::string &w : warnings_vector)
+ warning ("%s", w.c_str ());
}
return has_dwarf2;
static int
separate_debug_file_exists (const std::string &name, unsigned long crc,
- struct objfile *parent_objfile)
+ struct objfile *parent_objfile,
+ std::vector<std::string> *warnings_vector)
{
unsigned long file_crc;
int file_crc_p;
}
if (verified_as_different || parent_crc != file_crc)
- warning (_("the debug information found in \"%s\""
- " does not match \"%s\" (CRC mismatch).\n"),
- name.c_str (), objfile_name (parent_objfile));
-
- if (separate_debug_file_debug)
- gdb_printf (gdb_stdlog, _(" no, CRC doesn't match.\n"));
+ {
+ std::string msg
+ = string_printf (_("the debug information found in \"%s\""
+ " does not match \"%s\" (CRC mismatch).\n"),
+ name.c_str (), objfile_name (parent_objfile));
+ if (separate_debug_file_debug)
+ gdb_printf (gdb_stdlog, "%s", msg.c_str ());
+ warnings_vector->emplace_back (std::move (msg));
+ }
return 0;
}
looking for. CANON_DIR is the "realpath" form of DIR.
DIR must contain a trailing '/'.
Returns the path of the file with separate debug info, or an empty
- string. */
+ string.
+
+ Any warnings generated as part of the lookup process are added to
+ WARNINGS_VECTOR, one std::string per warning. */
static std::string
find_separate_debug_file (const char *dir,
const char *canon_dir,
const char *debuglink,
- unsigned long crc32, struct objfile *objfile)
+ unsigned long crc32, struct objfile *objfile,
+ std::vector<std::string> *warnings_vector)
{
if (separate_debug_file_debug)
gdb_printf (gdb_stdlog,
std::string debugfile = dir;
debugfile += debuglink;
- if (separate_debug_file_exists (debugfile, crc32, objfile))
+ if (separate_debug_file_exists (debugfile, crc32, objfile, warnings_vector))
return debugfile;
/* Then try in the subdirectory named DEBUG_SUBDIRECTORY. */
debugfile += "/";
debugfile += debuglink;
- if (separate_debug_file_exists (debugfile, crc32, objfile))
+ if (separate_debug_file_exists (debugfile, crc32, objfile, warnings_vector))
return debugfile;
/* Then try in the global debugfile directories.
debugfile += dir_notarget;
debugfile += debuglink;
- if (separate_debug_file_exists (debugfile, crc32, objfile))
+ if (separate_debug_file_exists (debugfile, crc32, objfile,
+ warnings_vector))
return debugfile;
const char *base_path = NULL;
debugfile += "/";
debugfile += debuglink;
- if (separate_debug_file_exists (debugfile, crc32, objfile))
+ if (separate_debug_file_exists (debugfile, crc32, objfile,
+ warnings_vector))
return debugfile;
/* If the file is in the sysroot, try using its base path in
debugfile += "/";
debugfile += debuglink;
- if (separate_debug_file_exists (debugfile, crc32, objfile))
+ if (separate_debug_file_exists (debugfile, crc32, objfile,
+ warnings_vector))
return debugfile;
}
}
path[i + 1] = '\0';
}
-/* Find separate debuginfo for OBJFILE (using .gnu_debuglink section).
- Returns pathname, or an empty string. */
+/* See symtab.h. */
std::string
-find_separate_debug_file_by_debuglink (struct objfile *objfile)
+find_separate_debug_file_by_debuglink
+ (struct objfile *objfile, std::vector<std::string> *warnings_vector)
{
unsigned long crc32;
std::string debugfile
= find_separate_debug_file (dir.c_str (), canon_dir.get (),
- debuglink.get (), crc32, objfile);
+ debuglink.get (), crc32, objfile,
+ warnings_vector);
if (debugfile.empty ())
{
symlink_dir.get (),
debuglink.get (),
crc32,
- objfile);
+ objfile,
+ warnings_vector);
}
}
}
extern void symbol_file_add_separate (const gdb_bfd_ref_ptr &, const char *,
symfile_add_flags, struct objfile *);
-extern std::string find_separate_debug_file_by_debuglink (struct objfile *);
+/* Find separate debuginfo for OBJFILE (using .gnu_debuglink section).
+ Returns pathname, or an empty string.
+
+ Any warnings generated as part of this lookup are added to
+ WARNINGS_VECTOR, one std::string per warning. */
+
+extern std::string find_separate_debug_file_by_debuglink
+ (struct objfile *objfile, std::vector<std::string> *warnings_vector);
/* Build (allocate and populate) a section_addr_info struct from an
existing section table. */
--- /dev/null
+/* Copyright 2022-2023 Free Software Foundation, Inc.
+
+ 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 3 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 <http://www.gnu.org/licenses/>. */
+
+int
+main (void)
+{
+ return 0;
+}
--- /dev/null
+# Copyright 2022-2023 Free Software Foundation, Inc.
+
+# 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 3 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 <http://www.gnu.org/licenses/>.
+#
+# This test triggers the "separate debug info file has no debug info" warning by replacing
+# the build-id based .debug file with the stripped binary and then loading it to gdb.
+# It then also sets up local debuginfod server with the correct debug file to download
+# to make sure no warnings are emmitted.
+
+
+standard_testfile
+
+load_lib debuginfod-support.exp
+
+require allow_debuginfod_tests
+
+if {[build_executable "build executable" ${testfile} ${srcfile} \
+ {debug build-id}] == -1} {
+ return -1
+}
+
+# Split BINFILE into BINFILE.stripped and BINFILE.debug, the first is
+# the executable with the debug information removed, and the second is
+# the debug information.
+#
+# However, by passing the "no-debuglink" flag we prevent this proc
+# from adding a .gnu_debuglink section to the executable. Any lookup
+# of the debug information by GDB will need to be done based on the
+# build-id.
+if {[gdb_gnu_strip_debug $binfile no-debuglink]} {
+ unsupported "cannot produce separate debug info files"
+ return -1
+}
+
+# Get the .build-id/PREFIX/SUFFIX.debug file name, and convert it to
+# an absolute path, this is where we will place the debug information.
+set build_id_debug_file \
+ [standard_output_file [build_id_debug_filename_get $binfile]]
+
+# Get the BINFILE.debug filename. This is the file we should be
+# moving to the BUILD_ID_DEBUG_FILE location, but we wont, we're going
+# to move something else there instead.
+set debugfile [standard_output_file "${binfile}.debug"]
+
+# Move debugfile to the directory to be used by the debuginfod
+# server.
+set debuginfod_debugdir [standard_output_file "debug"]
+remote_exec build "mkdir $debuginfod_debugdir"
+remote_exec build "mv $debugfile $debuginfod_debugdir"
+
+# This is BINFILE with the debug information removed. We are going to
+# place this in the BUILD_ID_DEBUG_FILE location, this would usually
+# represent a mistake by the user, and will trigger a warning from
+# GDB, this is the warning we are checking for.
+set stripped_binfile [standard_output_file "${binfile}.stripped"]
+
+# Create the .build-id/PREFIX directory name from
+# .build-id/PREFIX/SUFFIX.debug filename.
+set debugdir [file dirname ${build_id_debug_file}]
+remote_exec build "mkdir -p $debugdir"
+
+# Now move the stripped executable into the .build-id directory
+# instead of the debug information. Later on we're going to try and
+# load this into GDB. GDB will then try to find the separate debug
+# information, which will point back at this file, which also doesn't
+# have debug information, which could cause a loop. But GDB will spot
+# this and give a warning.
+remote_exec build "mv ${stripped_binfile} ${build_id_debug_file}"
+
+# Now start GDB.
+clean_restart
+
+# Tell GDB where to look for the .build-id directory.
+set debug_file_directory [standard_output_file ""]
+gdb_test_no_output "set debug-file-directory ${debug_file_directory}" \
+ "set debug-file-directory"
+
+# Now load the file into GDB, and look for the warning.
+gdb_test "file ${build_id_debug_file}" \
+ [multi_line \
+ ".*Reading symbols from.*debuginfo.*" \
+ ".*separate debug info file has no debug info.*"] \
+ "load test file, expect a warning"
+
+# Now we should close GDB.
+gdb_exit
+
+# Create CACHE and DB directories ready for debuginfod to use.
+prepare_for_debuginfod cache db
+
+# Start debuginfod server and test debuginfo is downloaded from
+# it and we can se no warnings anymore.
+proc_with_prefix local_debuginfod { } {
+ global db debuginfod_debugdir cache build_id_debug_file
+
+ set url [start_debuginfod $db $debuginfod_debugdir]
+ if {$url eq ""} {
+ unresolved "failed to start debuginfod server"
+ return
+ }
+
+ # Point the client to the server.
+ setenv DEBUGINFOD_URLS $url
+
+ # GDB should now find the symbol and source files.
+ clean_restart
+
+ # Enable debuginfod and fetch the debuginfo.
+ gdb_test_no_output "set debuginfod enabled on"
+
+ # "separate debug info file has no debug info" warning should not be
+ # reported now because the correct debuginfo should be fetched from
+ # debuginfod.
+ gdb_test "file ${build_id_debug_file}" \
+ [multi_line \
+ "Reading symbols from ${build_id_debug_file}\\.\\.\\." \
+ "Downloading separate debug info for ${build_id_debug_file}\\.\\.\\." \
+ "Reading symbols from ${cache}/\[^\r\n\]+\\.\\.\\.(?:\r\nExpanding full symbols from \[^\r\n\]+)*"] \
+ "debuginfod running, info downloaded, no warnings"
+}
+
+# Restart GDB, and load the file, this time we should correctly get
+# the debug symbols from the server, and should not see the warning.
+with_debuginfod_env $cache {
+ local_debuginfod
+}
+
+stop_debuginfod
+# Spare debug files may confuse testsuite runs in the future.
+remote_exec build "rm -f $debugfile"
+
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2022-2023 Free Software Foundation, Inc.
+
+ 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 3 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 <http://www.gnu.org/licenses/>. */
+
+int
+main (void)
+{
+ return 0;
+}
--- /dev/null
+/* Copyright 2022-2023 Free Software Foundation, Inc.
+
+ 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 3 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 <http://www.gnu.org/licenses/>. */
+
+int
+main (int argc, char **argv, char **envp)
+{
+ return argc;
+}
--- /dev/null
+# Copyright 2022-2023 Free Software Foundation, Inc.
+
+# 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 3 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 <http://www.gnu.org/licenses/>.
+#
+# This test compiles two executables: crc_mismatch and crc_mismatch-2
+# and then strips them of debuginfo creating separate debug files. The test
+# then replaces crc_mismatch-2.debug with crc_mismatch.debug to trigger
+# "CRC mismatch" warning. A local debuginfod server is setup to supply
+# the correct debug file, now when GDB looks up the debug info no warning
+# is given.
+
+standard_testfile .c -2.c
+
+load_lib debuginfod-support.exp
+
+require allow_debuginfod_tests
+
+if {[build_executable "build executable" $testfile $srcfile debug] == -1} {
+ untested "failed to compile"
+ return -1
+}
+
+# The procedure gdb_gnu_strip_debug will produce an executable called
+# ${binfile}, which is just like the executable ($binfile) but without
+# the debuginfo. Instead $binfile has a .gnu_debuglink section which
+# contains the name of a debuginfo only file.
+if {[gdb_gnu_strip_debug $binfile]} {
+ # Check that you have a recent version of strip and objcopy installed.
+ unsupported "cannot produce separate debug info files"
+ return -1
+}
+
+set debugfile "$[standard_output_file ${testfile}.debug]"
+set debugdir [standard_output_file "debug"]
+remote_exec build "mkdir $debugdir"
+remote_exec build "mkdir -p [file dirname $debugfile]"
+remote_exec build "mv -f [standard_output_file ${testfile}.debug] $debugfile"
+
+# Test CRC mismatch is reported.
+if {[build_executable crc_mismatch.exp crc_mismatch-2 crc_mismatch-2.c debug] != -1
+ && ![gdb_gnu_strip_debug [standard_output_file crc_mismatch-2]]} {
+
+ # Copy the correct debug file for crc_mismatch-2 to the debugdir
+ # which is going to be used by local debuginfod.
+ remote_exec build "cp [standard_output_file crc_mismatch-2.debug] ${debugdir}"
+ # Move the unmatching debug file for crc_mismatch-2 instead of its real one
+ # to trigger the "CRC mismatch" warning.
+ remote_exec build "mv ${debugfile} [standard_output_file crc_mismatch-2.debug]"
+
+ gdb_exit
+ gdb_start
+
+ set escapedobjdirsubdir [string_to_regexp [standard_output_file {}]]
+
+ gdb_test "file [standard_output_file crc_mismatch-2]" "warning: the debug information found in \"${escapedobjdirsubdir}/crc_mismatch-2\\.debug\" does not match \"${escapedobjdirsubdir}/crc_mismatch-2\" \\(CRC mismatch\\)\\..*\\(No debugging symbols found in .*\\).*" "CRC mismatch is reported"
+}
+
+# Create CACHE and DB directories ready for debuginfod to use.
+prepare_for_debuginfod cache db
+
+# Start debuginfod server, test the correct debuginfo was fetched
+# from the server so there're not warnings anymore.
+proc_with_prefix local_debuginfod { } {
+ global binfile db debugdir cache
+ set escapedobjdirsubdir [string_to_regexp [standard_output_file {}]]
+
+ set url [start_debuginfod $db $debugdir]
+ if {$url eq ""} {
+ unresolved "failed to start debuginfod server"
+ return
+ }
+
+ # Point the client to the server.
+ setenv DEBUGINFOD_URLS $url
+
+ # GDB should now find the symbol and source files.
+ clean_restart
+
+ # Enable debuginfod and fetch the debuginfo.
+ gdb_test_no_output "set debuginfod enabled on"
+ gdb_test "file $binfile" ".*Reading symbols from.*debuginfo.*" \
+ "file [file tail $binfile] cmd on"
+
+ # CRC mismatch should not be reported now because the correct debuginfo
+ # should be fetched from debuginfod.
+ gdb_test "file [standard_output_file crc_mismatch-2]" \
+ [multi_line \
+ "Reading symbols from ${escapedobjdirsubdir}/crc_mismatch-2\\.\\.\\." \
+ "Downloading separate debug info for ${escapedobjdirsubdir}/crc_mismatch-2\\.\\.\\." \
+ "Reading symbols from ${cache}/\[^\r\n\]+\\.\\.\\.(?:\r\nExpanding full symbols from \[^\r\n\]+)*"] \
+ "debuginfod running, info downloaded, no CRC mismatch"
+}
+
+with_debuginfod_env $cache {
+ local_debuginfod
+}
+
+stop_debuginfod
+# Spare debug files may confuse testsuite runs in the future.
+remote_exec build "rm -f $debugfile"
return ".build-id/${data}.debug"
}
-# Create stripped files for DEST, replacing it. If ARGS is passed, it is a
-# list of optional flags. The only currently supported flag is no-main,
-# which removes the symbol entry for main from the separate debug file.
+# DEST should be a file compiled with debug information. This proc
+# creates two new files DEST.debug which contains the debug
+# information extracted from DEST, and DEST.stripped, which is a copy
+# of DEST with the debug information removed. A '.gnu_debuglink'
+# section will be added to DEST.stripped that points to DEST.debug.
+#
+# If ARGS is passed, it is a list of optional flags. The currently
+# supported flags are:
+#
+# - no-main : remove the symbol entry for main from the separate
+# debug file DEST.debug,
+# - no-debuglink : don't add the '.gnu_debuglink' section to
+# DEST.stripped.
#
# Function returns zero on success. Function will return non-zero failure code
# on some targets not supporting separate debug info (such as i386-msdos).
# leaves the symtab in the original file only. There's no way to get
# objcopy or strip to remove the symbol table without also removing the
# debugging sections, so this is as close as we can get.
- if { [llength $args] == 1 && [lindex $args 0] == "no-main" } {
+ if {[lsearch -exact $args "no-main"] != -1} {
set result [catch "exec $objcopy_program -N main ${debug_file} ${debug_file}-tmp" output]
verbose "result is $result"
verbose "output is $output"
file rename "${debug_file}-tmp" "${debug_file}"
}
- # Link the two previous output files together, adding the .gnu_debuglink
- # section to the stripped_file, containing a pointer to the debug_file,
- # save the new file in dest.
- # This will be the regular executable filename, in the usual location.
- set result [catch "exec $objcopy_program --add-gnu-debuglink=${debug_file} ${stripped_file} ${dest}" output]
- verbose "result is $result"
- verbose "output is $output"
- if {$result == 1} {
- return 1
+ # Unless the "no-debuglink" flag is passed, then link the two
+ # previous output files together, adding the .gnu_debuglink
+ # section to the stripped_file, containing a pointer to the
+ # debug_file, save the new file in dest.
+ if {[lsearch -exact $args "no-debuglink"] == -1} {
+ set result [catch "exec $objcopy_program --add-gnu-debuglink=${debug_file} ${stripped_file} ${dest}" output]
+ verbose "result is $result"
+ verbose "output is $output"
+ if {$result == 1} {
+ return 1
+ }
}
# Workaround PR binutils/10802: