From 2bf3b79d05bf85e41cbdcb020bd1cc424f59dd9a Mon Sep 17 00:00:00 2001 From: Sergio Durigan Junior Date: Sat, 14 Nov 2020 17:42:46 -0500 Subject: Search for DWZ files in debug-file-directories as well When Debian (and Ubuntu) builds its binaries, it (still) doesn't use dwz's "--relative" option. This causes their debuginfo files to carry a .gnu_debugaltlink section containing a full pathname to the DWZ alt debug file, like this: $ readelf -wk /usr/bin/cat Contents of the .gnu_debugaltlink section: Separate debug info file: /usr/lib/debug/.dwz/x86_64-linux-gnu/coreutils.debug Build-ID (0x14 bytes): ee 76 5d 71 97 37 ce 46 99 44 32 bb e8 a9 1a ef 99 96 88 db Contents of the .gnu_debuglink section: Separate debug info file: 06d3bee37b8c7e67b31cb2689cb351102ae73b.debug CRC value: 0x53267655 This usually works OK, because most of the debuginfo files installed via apt will be present in /usr/lib/debug anyway. However, imagine the following scenario: - You are using /usr/bin/cat, it crashes on you and generates a corefile. - You don't want/need to "apt install" the debuginfo file for coreutils from the repositories. Instead, you already have the debuginfo files in a separate directory (e.g., $HOME/dbgsym). - You start GDB and "set debug-file-directory $HOME/dbgsym/usr/lib/debug". You then get the following message: $ gdb -ex 'set debug-file-directory ./dbgsym/usr/lib/debug' -ex 'file /bin/cat' -ex 'core-file ./cat.core' GNU gdb (Ubuntu 10.1-0ubuntu1) 10.1 ... Reading symbols from /bin/cat... Reading symbols from /home/sergio/gdb/dbgsym/usr/lib/debug/.build-id/bc/06d3bee37b8c7e67b31cb2689cb351102ae73b.debug... could not find '.gnu_debugaltlink' file for /home/sergio/gdb/dbgsym/usr/lib/debug/.build-id/bc/06d3bee37b8c7e67b31cb2689cb351102ae73b.debug This error happens because GDB is trying to locate the build-id link (inside /home/sergio/gdb/dbgsym/usr/lib/debug/.build-id) for the DWZ alt debug file, which doesn't exist. Arguably, this is a problem with how dh_dwz works in Debian, and it's something I'm also planning to tackle. But, back at the problem at hand. Besides not being able to find the build-id link in the directory mentioned above, GDB also tried to open the DWZ alt file using its filename. The problem here is that, since we don't have the distro's debuginfo installed, it can't find anything under /usr/lib/debug that satisfies it. It occurred to me that a good way to workaround this problem is to actually try to locate the DWZ alt debug file inside the debug-file-directories (that were likely provided by the user). So this is what the proposed patch does. The idea here is simple: get the filename extracted from the .gnu_debugaltlink section, and manipulate it in order to replace the initial part of the path (everything before "/.dwz/") by whatever debug-file-directories the user might have provided. I talked with Mark Wielaard and he agrees this is a sensible approach. In fact, apparently this is something that eu-readelf also does. I regtested this code, and no regressions were found. 2020-12-01 Sergio Durigan Junior * dwarf2/read.c (dwz_search_other_debugdirs): New function. (dwarf2_get_dwz_file): Convert 'filename' to a std::string. Use dwz_search_other_debugdirs to search for DWZ files in the debug-file-directories provided by the user as well. --- gdb/ChangeLog | 7 ++++ gdb/dwarf2/read.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 107 insertions(+), 7 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 635435c..4946a52 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,10 @@ +2020-12-01 Sergio Durigan Junior + + * dwarf2/read.c (dwz_search_other_debugdirs): New function. + (dwarf2_get_dwz_file): Convert 'filename' to a + std::string. Use dwz_search_other_debugdirs to search for DWZ + files in the debug-file-directories provided by the user as well. + 2020-12-01 Tom Tromey * parse.c (expr_builder::expr_builder): Initialize expout. diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index 601a571..9468b91 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -2185,12 +2185,100 @@ locate_dwz_sections (bfd *abfd, asection *sectp, dwz_file *dwz_file) } } +/* Attempt to find a .dwz file (whose full path is represented by + FILENAME) in all of the specified debug file directories provided. + + Return the equivalent gdb_bfd_ref_ptr of the .dwz file found, or + nullptr if it could not find anything. */ + +static gdb_bfd_ref_ptr +dwz_search_other_debugdirs (std::string &filename, bfd_byte *buildid, + size_t buildid_len) +{ + /* Let's assume that the path represented by FILENAME has the + "/.dwz/" subpath in it. This is what (most) GNU/Linux + distributions do, anyway. */ + size_t dwz_pos = filename.find ("/.dwz/"); + + if (dwz_pos == std::string::npos) + return nullptr; + + /* This is an obvious assertion, but it's here more to educate + future readers of this code that FILENAME at DWZ_POS *must* + contain a directory separator. */ + gdb_assert (IS_DIR_SEPARATOR (filename[dwz_pos])); + + gdb_bfd_ref_ptr dwz_bfd; + std::vector> debugdir_vec + = dirnames_to_char_ptr_vec (debug_file_directory); + + for (const gdb::unique_xmalloc_ptr &debugdir : debugdir_vec) + { + /* The idea is to iterate over the + debug file directories provided by the user and + replace the hard-coded path in the "filename" by each + debug-file-directory. + + For example, suppose that filename is: + + /usr/lib/debug/.dwz/foo.dwz + + And suppose that we have "$HOME/bar" as the + debug-file-directory. We would then adjust filename + to look like: + + $HOME/bar/.dwz/foo.dwz + + which would hopefully allow us to find the alt debug + file. */ + std::string ddir = debugdir.get (); + + if (ddir.empty ()) + continue; + + /* Make sure the current debug-file-directory ends with a + directory separator. This is needed because, if FILENAME + contains something like "/usr/lib/abcde/.dwz/foo.dwz" and + DDIR is "/usr/lib/abc", then could wrongfully skip it + below. */ + if (!IS_DIR_SEPARATOR (ddir.back ())) + ddir += SLASH_STRING; + + /* Check whether the beginning of FILENAME is DDIR. If it is, + then we are dealing with a file which we already attempted to + open before, so we just skip it and continue processing the + remaining debug file directories. */ + if (filename.size () > ddir.size () + && filename.compare (0, ddir.size (), ddir) == 0) + continue; + + /* Replace FILENAME's default debug-file-directory with + DDIR. */ + std::string new_filename = ddir + &filename[dwz_pos + 1]; + + dwz_bfd = gdb_bfd_open (new_filename.c_str (), gnutarget); + + if (dwz_bfd == nullptr) + continue; + + if (!build_id_verify (dwz_bfd.get (), buildid_len, buildid)) + { + dwz_bfd.reset (nullptr); + continue; + } + + /* Found it. */ + break; + } + + return dwz_bfd; +} + /* See dwarf2read.h. */ struct dwz_file * dwarf2_get_dwz_file (dwarf2_per_bfd *per_bfd) { - const char *filename; bfd_size_type buildid_len_arg; size_t buildid_len; bfd_byte *buildid; @@ -2214,21 +2302,19 @@ dwarf2_get_dwz_file (dwarf2_per_bfd *per_bfd) buildid_len = (size_t) buildid_len_arg; - filename = data.get (); + std::string filename = data.get (); - std::string abs_storage; - if (!IS_ABSOLUTE_PATH (filename)) + if (!IS_ABSOLUTE_PATH (filename.c_str ())) { gdb::unique_xmalloc_ptr abs = gdb_realpath (bfd_get_filename (per_bfd->obfd)); - abs_storage = ldirname (abs.get ()) + SLASH_STRING + filename; - filename = abs_storage.c_str (); + filename = ldirname (abs.get ()) + SLASH_STRING + filename; } /* First try the file name given in the section. If that doesn't work, try to use the build-id instead. */ - gdb_bfd_ref_ptr dwz_bfd (gdb_bfd_open (filename, gnutarget)); + gdb_bfd_ref_ptr dwz_bfd (gdb_bfd_open (filename.c_str (), gnutarget)); if (dwz_bfd != NULL) { if (!build_id_verify (dwz_bfd.get (), buildid_len, buildid)) @@ -2240,6 +2326,13 @@ dwarf2_get_dwz_file (dwarf2_per_bfd *per_bfd) if (dwz_bfd == nullptr) { + /* If the user has provided us with different + debug file directories, we can try them in order. */ + dwz_bfd = dwz_search_other_debugdirs (filename, buildid, buildid_len); + } + + if (dwz_bfd == nullptr) + { gdb::unique_xmalloc_ptr alt_filename; const char *origname = bfd_get_filename (per_bfd->obfd); -- cgit v1.1