RFC: objdump: Add --debug-dir=<DIR> option

Message ID 87zf35d098.fsf@redhat.com
State New
Headers
Series RFC: objdump: Add --debug-dir=<DIR> option |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 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

Nick Clifton April 14, 2026, 3:34 p.m. UTC
  Hi Guys,

  Attached is a potential patch to improve objdump and readelf by adding
  a command line option that specifies a directory to be searched when
  looking for separate debug information files.  I found this to be very
  helpful when the debug information is not stored in one of the
  "standard" locations expected by the programs.

  This particular scenario arose for me when I was trying to examine the
  contents of some files inside an uninstalled rpm.  I could extract the
  binaries and their separate debug info files from the rpms but I could
  not persuade objdump to use the debug information unless it was in the
  current directory.

  Any comments or suggestions for improvements ?

Cheers
  Nick
  

Comments

Hans-Peter Nilsson April 14, 2026, 10:43 p.m. UTC | #1
On Tue, 14 Apr 2026, Nick Clifton wrote:

> Hi Guys,
> 
>   Attached is a potential patch to improve objdump and readelf by adding
>   a command line option that specifies a directory to be searched when
>   looking for separate debug information files.  I found this to be very
>   helpful when the debug information is not stored in one of the
>   "standard" locations expected by the programs.
> 
>   This particular scenario arose for me when I was trying to examine the
>   contents of some files inside an uninstalled rpm.  I could extract the
>   binaries and their separate debug info files from the rpms but I could
>   not persuade objdump to use the debug information unless it was in the
>   current directory.
> 
>   Any comments

First, "sounds great!" then "obvious and overdue!" then...

> or suggestions for improvements

..."perhaps it should be a :-separated set of paths?", then "but 
that should be a separate, future change; a separate option".

> ?
> 
> Cheers
>   Nick

That's about it. ;)
No need to wait, improvements are best kept incremental.

brgds, H-P
PS: tho, missing coverage in the testsuite.
  
Nick Clifton April 16, 2026, 11:12 a.m. UTC | #2
Hi H-P,

>> or suggestions for improvements
> 
> ..."perhaps it should be a :-separated set of paths?", then "but
> that should be a separate, future change; a separate option".

I did think about doing that initially.  But in the end I decided
against it because a) it makes the code more complicated and b)
how often will a user need to specify multiple directories for
debug info files ?  I mean yes, I can imagine some scenarios where
it could be needed, but they are pretty far fetched...

Cheers
   Nick
  
Frank Ch. Eigler April 20, 2026, 4:31 p.m. UTC | #3
Nick Clifton <nickc@redhat.com> writes:

>   Attached is a potential patch to improve objdump and readelf by adding
>   a command line option that specifies a directory to be searched when
>   looking for separate debug information files.  I found this to be very
>   helpful when the debug information is not stored in one of the
>   "standard" locations expected by the programs. [...]

By the way, you can handle this scenario easily enough by running a
local temporary copy of elfutils debuginfod, giving it a list of your
build subdirectories:

    debuginfod -p 8003 -d :memory: -F /build/tree/1 /build/tree/2 &
    export DEBUGINFOD_URLS=http://localhost:8003/

The toolchain can then find sources & binaries & debuginfo.

- FChE
  
Nick Clifton April 21, 2026, 9:32 a.m. UTC | #4
Hi Frank,

>>    Attached is a potential patch to improve objdump and readelf by adding
>>    a command line option that specifies a directory to be searched when
>>    looking for separate debug information files.  I found this to be very
>>    helpful when the debug information is not stored in one of the
>>    "standard" locations expected by the programs. [...]
> 
> By the way, you can handle this scenario easily enough by running a
> local temporary copy of elfutils debuginfod, giving it a list of your
> build subdirectories:
> 
>      debuginfod -p 8003 -d :memory: -F /build/tree/1 /build/tree/2 &
>      export DEBUGINFOD_URLS=http://localhost:8003/
> 
> The toolchain can then find sources & binaries & debuginfo.

Thanks for the tip :-).

Cheers
   Nick
  

Patch

diff --git a/binutils/NEWS b/binutils/NEWS
index 1a3c25d1fe7..5ac0e8b7811 100644
--- a/binutils/NEWS
+++ b/binutils/NEWS
@@ -1,5 +1,8 @@ 
 -*- text -*-
 
+* Objdump and readelf now have a --debug-dir=<DIR> option which can be used to
+  tell them where to find separate debug info files.
+
 * Add --map-global-vars option to objdump which displays the location and type
   of global variables in object files. This feature was developed by Guillaume
   Vacherias <guillaume.vacherias@foss.st.com>.
diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi
index 8025458b2fa..59ffe548543 100644
--- a/binutils/doc/binutils.texi
+++ b/binutils/doc/binutils.texi
@@ -2350,6 +2350,7 @@  objdump [@option{-a}|@option{--archive-headers}]
         [@option{-WN}|@option{--dwarf=no-follow-links}]
         [@option{-WD}|@option{--dwarf=use-debuginfod}]
         [@option{-WE}|@option{--dwarf=do-not-use-debuginfod}]
+        [@option{--debug-dir=@file{dir}}]
         [@option{--map-global-vars}]
         [@option{-L}|@option{--process-links}]
         [@option{--ctf=}@var{section}]
@@ -2999,6 +3000,9 @@  The @option{off} argument disables colored disassembly.
 @item --dwarf-check
 Enable additional checks for consistency of Dwarf information.
 
+@item --debug-dir=@file{dir}
+Search @file{dir} when looking for separate debug info files.
+
 @include ctf.options.texi
 
 @include sframe.options.texi
@@ -5172,6 +5176,7 @@  readelf [@option{-a}|@option{--all}]
         [@option{-wN}|@option{--debug-dump=no-follow-links}]
         [@option{-wD}|@option{--debug-dump=use-debuginfod}]
         [@option{-wE}|@option{--debug-dump=do-not-use-debuginfod}]
+        [@option{--debug-dir=@file{dir}}]
         [@option{-P}|@option{--process-links}]
         [@option{--dwarf-depth=@var{n}}]
         [@option{--dwarf-start=@var{n}}]
@@ -5477,6 +5482,9 @@  command to @command{ar}, but without using the BFD library.  @xref{ar}.
 @itemx --debug-dump[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=str-offsets,=loc,=Ranges,=pubtypes,=trace_info,=trace_abbrev,=trace_aranges,=gdb_index,=addr,=cu_index,=links,=follow-links]
 @include debug.options.texi
 
+@item --debug-dir=@file{dir}
+Search @file{dir} when looking for separate debug info files.
+
 @item -P
 @itemx --process-links
 Display the contents of non-debug sections found in separate debuginfo
diff --git a/binutils/dwarf.c b/binutils/dwarf.c
index fa8dce25d44..42386154b49 100644
--- a/binutils/dwarf.c
+++ b/binutils/dwarf.c
@@ -125,6 +125,8 @@  separate_info * first_separate_info = NULL;
 
 unsigned int eh_addr_size;
 
+const char * separate_debug_info_dir = NULL;
+
 int do_debug_info;
 int do_debug_abbrevs;
 int do_debug_lines;
@@ -13379,17 +13381,22 @@  load_separate_debug_info (const char *            main_filename,
 #define EXTRA_DEBUG_ROOT2 "/usr/lib/debug/usr"
 #endif
 
-  debug_filename = (char *) malloc (strlen (DEBUGDIR) + 1
-				    + canon_dirlen
-				    + strlen (".debug/")
+  unsigned long len = strlen (DEBUGDIR) + 1
+    + canon_dirlen
+    + strlen (".debug/")
 #ifdef EXTRA_DEBUG_ROOT1
-				    + strlen (EXTRA_DEBUG_ROOT1)
+    + strlen (EXTRA_DEBUG_ROOT1)
 #endif
 #ifdef EXTRA_DEBUG_ROOT2
-				    + strlen (EXTRA_DEBUG_ROOT2)
+    + strlen (EXTRA_DEBUG_ROOT2)
 #endif
-				    + strlen (separate_filename)
-				    + 1);
+    + strlen (separate_filename)
+    + 1;
+
+  if (separate_debug_info_dir)
+    len += strlen (separate_debug_info_dir);
+
+  debug_filename = (char *) malloc (len);
   if (debug_filename == NULL)
     {
       warn (_("Out of memory\n"));
@@ -13398,7 +13405,15 @@  load_separate_debug_info (const char *            main_filename,
       return NULL;
     }
 
-  /* First try in the current directory.  */
+  /* If the user has supplied a debug info directory then try that first.  */
+  if (separate_debug_info_dir != NULL)
+    {
+      sprintf (debug_filename, "%s/%s", separate_debug_info_dir, separate_filename);
+      if (check_func (debug_filename, func_data))
+	goto found;      
+    }
+  
+  /* Next try in the current directory.  */
   sprintf (debug_filename, "%s", separate_filename);
   if (check_func (debug_filename, func_data))
     goto found;
diff --git a/binutils/dwarf.h b/binutils/dwarf.h
index 3ab25933052..e6b51bf368e 100644
--- a/binutils/dwarf.h
+++ b/binutils/dwarf.h
@@ -462,6 +462,7 @@  typedef struct separate_info
 extern separate_info * first_separate_info;
 
 extern unsigned int eh_addr_size;
+extern const char * separate_debug_info_dir;
 
 extern int do_debug_info;
 extern int do_debug_abbrevs;
diff --git a/binutils/objdump.c b/binutils/objdump.c
index 8b6021a20fe..944b81b17e0 100644
--- a/binutils/objdump.c
+++ b/binutils/objdump.c
@@ -328,6 +327,8 @@  usage (FILE *stream, int status)
   -WE --dwarf=do-not-use-debuginfod\n\
                            When following links, do not query debuginfod servers\n"));
 #endif
+  fprintf (stream, _("\
+      --debug-dir=<DIR>    Search DIR for debug info files\n"));
   fprintf (stream, _("\
       --map-global-vars    Display memory mapping of global variables\n"));
   fprintf (stream, _("\
@@ -481,6 +482,7 @@  enum option_values
     OPTION_START_ADDRESS,
     OPTION_STOP_ADDRESS,
     OPTION_DWARF,
+    OPTION_DEBUG_DIR,
     OPTION_PREFIX,
     OPTION_PREFIX_STRIP,
     OPTION_INSN_WIDTH,
@@ -514,6 +516,7 @@  static struct option long_options[]=
   {"ctf-parent", required_argument, NULL, OPTION_CTF_PARENT},
   {"ctf-parent-section", required_argument, NULL, OPTION_CTF_PARENT_SECTION},
 #endif
+  {"debug-dir", required_argument, NULL, OPTION_DEBUG_DIR},
   {"debugging", no_argument, NULL, 'g'},
   {"debugging-tags", no_argument, NULL, 'e'},
   {"decompress", no_argument, NULL, 'Z'},
@@ -6386,6 +6391,9 @@  main (int argc, char **argv)
 	case OPTION_DWARF_CHECK:
 	  dwarf_check = true;
 	  break;
+	case OPTION_DEBUG_DIR:
+	  separate_debug_info_dir = optarg;
+	  break;
 #ifdef ENABLE_LIBCTF
 	case OPTION_CTF:
 	  dump_ctf_section_info = true;
diff --git a/binutils/readelf.c b/binutils/readelf.c
index 869bb23e48f..7b2c1c9ae88 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -6471,6 +6471,7 @@  enum long_option_values
   OPTION_DWARF_DEPTH,
   OPTION_DWARF_START,
   OPTION_DWARF_CHECK,
+  OPTION_DEBUG_DIR,
   OPTION_CTF_DUMP,
   OPTION_CTF_PARENT,
   OPTION_CTF_SYMBOLS,
@@ -6537,6 +6538,7 @@  static struct option options[] =
   {"dwarf-depth",      required_argument, 0, OPTION_DWARF_DEPTH},
   {"dwarf-start",      required_argument, 0, OPTION_DWARF_START},
   {"dwarf-check",      no_argument, 0, OPTION_DWARF_CHECK},
+  {"debug-dir",        required_argument, NULL, OPTION_DEBUG_DIR},
 #ifdef ENABLE_LIBCTF
   {"ctf",	       required_argument, 0, OPTION_CTF_DUMP},
   {"ctf-symbols",      required_argument, 0, OPTION_CTF_SYMBOLS},
@@ -6680,6 +6682,8 @@  usage (FILE * stream)
   --dwarf-depth=N        Do not display DIEs at depth N or greater\n"));
   fprintf (stream, _("\
   --dwarf-start=N        Display DIEs starting at offset N\n"));
+  fprintf (stream, _("\
+  --debug-dir=<DIR>      Search DIR for debug info files\n"));
 #ifdef ENABLE_LIBCTF
   fprintf (stream, _("\
   --ctf=<number|name>    Display CTF info from section <number|name>\n"));
@@ -6959,6 +6963,9 @@  parse_args (struct dump_data *dumpdata, int argc, char ** argv)
 	case OPTION_DWARF_CHECK:
 	  dwarf_check = true;
 	  break;
+	case OPTION_DEBUG_DIR:
+	  separate_debug_info_dir = optarg;
+	  break;
 	case OPTION_CTF_DUMP:
 	  request_dump (dumpdata, CTF_DUMP);
 	  break;