[binutils-gdb] Add --debug-dir option to readelf and objdumo. Load separate debug info files when disassemblng.
Nick Clifton
nickc@sourceware.org
Fri Apr 17 11:18:16 GMT 2026
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=bfd118583ed69db365265915d1a48eb719e997f5
commit bfd118583ed69db365265915d1a48eb719e997f5
Author: Nick Clifton <nickc@redhat.com>
Date: Fri Apr 17 12:17:27 2026 +0100
Add --debug-dir option to readelf and objdumo. Load separate debug info files when disassemblng.
Diff:
---
binutils/NEWS | 3 ++
binutils/doc/binutils.texi | 8 +++++
binutils/dwarf.c | 75 +++++++++++++++++++++++-----------------------
binutils/dwarf.h | 1 +
binutils/objdump.c | 24 ++++++++-------
binutils/readelf.c | 7 +++++
6 files changed, 69 insertions(+), 49 deletions(-)
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;
@@ -13425,7 +13440,7 @@ load_separate_debug_info (const char * main_filename,
goto found;
/* Try the first extra debug file root. */
- sprintf (debug_filename, "%s/%s/%s", EXTRA_DEBUG_ROOT1, canon_dir, separate_filename);
+ sprintf (debug_filename, "%s/%s%s", EXTRA_DEBUG_ROOT1, canon_dir, separate_filename);
if (check_func (debug_filename, func_data))
goto found;
#endif
@@ -13468,37 +13483,19 @@ load_separate_debug_info (const char * main_filename,
if (do_debug_links)
{
/* Failed to find the file. */
- warn (_("could not find separate debug file '%s'\n"),
- separate_filename);
- warn (_("tried: %s\n"), debug_filename);
-
-#ifdef EXTRA_DEBUG_ROOT2
- sprintf (debug_filename, "%s/%s", EXTRA_DEBUG_ROOT2,
- separate_filename);
- warn (_("tried: %s\n"), debug_filename);
-#endif
-
+ warn (_("could not find separate debug file '%s'\n"), separate_filename);
+ if (separate_debug_info_dir != NULL)
+ warn (_("tried: %s/%s\n"), separate_debug_info_dir, separate_filename);
+ warn (_("tried: %s\n"), separate_filename);
+ warn (_("tried: .debug/%s\n"), separate_filename);
+ warn (_("tried: %s%s\n"), canon_dir, separate_filename);
#ifdef EXTRA_DEBUG_ROOT1
- sprintf (debug_filename, "%s/%s/%s", EXTRA_DEBUG_ROOT1,
- canon_dir, separate_filename);
- warn (_("tried: %s\n"), debug_filename);
-
- sprintf (debug_filename, "%s/%s", EXTRA_DEBUG_ROOT1,
- separate_filename);
- warn (_("tried: %s\n"), debug_filename);
+ warn (_("tried: %s/%s\n"), EXTRA_DEBUG_ROOT1, separate_filename);
+ warn (_("tried: %s/%s%s\n"), EXTRA_DEBUG_ROOT1, canon_dir, separate_filename);
+#endif
+#ifdef EXTRA_DEBUG_ROOT2
+ warn (_("tried: %s/%s\n"), EXTRA_DEBUG_ROOT2, separate_filename);
#endif
-
- sprintf (debug_filename, "%s.debug/%s", canon_dir,
- separate_filename);
- warn (_("tried: %s\n"), debug_filename);
-
- sprintf (debug_filename, "%s%s", canon_dir, separate_filename);
- warn (_("tried: %s\n"), debug_filename);
-
- sprintf (debug_filename, ".debug/%s", separate_filename);
- warn (_("tried: %s\n"), debug_filename);
-
- sprintf (debug_filename, "%s", separate_filename);
warn (_("tried: %s\n"), debug_filename);
#if HAVE_LIBDEBUGINFOD
@@ -13547,6 +13544,7 @@ load_separate_debug_info (const char * main_filename,
if (do_debug_links)
printf (_("\n%s: Found separate debug info file: %s\n"), main_filename, debug_filename);
+
add_separate_debug_file (debug_filename, debug_handle);
/* Do not free debug_filename - it might be referenced inside
@@ -13906,6 +13904,7 @@ load_separate_debug_files (void * file, const char * filename)
/* FIXME: We do not check for the presence of a dwo link as well as a debuglink. */
check_for_and_load_links (file, filename);
+
if (first_separate_info != NULL)
return true;
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
@@ -160,7 +160,6 @@ static enum color_selection
off;
#endif
-static int dump_any_debugging;
static int demangle_flags = DMGL_ANSI | DMGL_PARAMS;
/* This is reset to false each time we enter the disassembler, and set true
@@ -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'},
@@ -4402,9 +4405,6 @@ load_debug_section (enum dwarf_section_display_enum debug, void *file)
asection *sec;
const char *name;
- if (!dump_any_debugging)
- return false;
-
/* If it is already loaded, do nothing. */
if (section->start != NULL)
{
@@ -5742,6 +5742,9 @@ might_need_separate_debug_info (bool is_mainfile)
return true;
if (process_links || dump_symtab || dump_debugging
+ /* Disassembly benefits from symbol tables, which might
+ be stored in the separate debug info file. */
+ || disassemble
|| dump_dwarf_section_info || with_source_code)
return true;
@@ -5765,7 +5768,9 @@ dump_bfd (bfd *abfd, bool is_mainfile)
/* Load any separate debug information files. */
if (byte_get != NULL && might_need_separate_debug_info (is_mainfile))
{
- load_separate_debug_files (abfd, bfd_get_filename (abfd));
+ /* Since it is possible that there are no separate debug files,
+ do not complain if calling load_separate_debug_files returns false. */
+ (void) load_separate_debug_files (abfd, bfd_get_filename (abfd));
/* If asked to do so, recursively dump the separate files. */
if (do_follow_links)
@@ -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;
@@ -6474,12 +6482,6 @@ main (int argc, char **argv)
if (!seenflag)
usage (stderr, 2);
- dump_any_debugging = (dump_debugging
- || dump_dwarf_section_info
- || dump_global_vars
- || process_links
- || with_source_code);
-
if (formats_info)
exit_status = display_info ();
else
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;
More information about the Binutils-cvs
mailing list