This is the mail archive of the binutils-cvs@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[binutils-gdb] Add support to readelf and objdump for following links to separate debug information files.


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=dda8d76d0dd2b07df769f6cae20aff483468b342

commit dda8d76d0dd2b07df769f6cae20aff483468b342
Author: Nick Clifton <nickc@redhat.com>
Date:   Wed Nov 15 11:34:03 2017 +0000

    Add support to readelf and objdump for following links to separate debug information files.
    
    Hi Guys,
    
      I am applying the rather large patch attached to this email to enhance
      the readelf and objdump programs so that they now have the ability to
      follow links to separate debug info files.  (As requested by PR
      15152).  So for example whereas before we had this output:
    
        $ readelf -wi main.exe
    
        Contents of the .debug_info section:
        [...]
        <15>   DW_AT_comp_dir    : (alt indirect string, offset: 0x30c)
        [...]
    
      With the new option enabled we get:
    
        $ readelf -wiK main.exe
    
        main.exe: Found separate debug info file: dwz.debug
        Contents of the .debug_info section (loaded from main.exe):
        [...]
        <15>   DW_AT_comp_dir    : (alt indirect string, offset: 0x30c) /home/nickc/Downloads/dwzm
        [...]
    
      The link following feature also means that we can get two lots of
      output if the same section exists in both the main file and the
      separate debug info file:
    
        $ readelf -wiK main.exe
        main.exe: Found separate debug info file: dwz.debug
        Contents of the .debug_info section (loaded from main.exe):
        [...]
        Contents of the .debug_info section (loaded from dwz.debug):
        [...]
    
      The patch also adds the ability to display the contents of debuglink
      sections:
    
        $ readelf -wk main.exe
        Contents of the .gnu_debugaltlink section:
    
          Separate debug info file: dwz.debug
          Build-ID (0x14 bytes):
         c4 a8 89 8d 64 cf 70 8a 35 68 21 f2 ed 24 45 3e 18 7a 7a 93
    
      Naturally there are long versions of these options (=follow-links and
      =links).  The documentation has been updated as well, and since both
      readelf and objdump use the same set of debug display options, I have
      moved the text into a separate file.  There are also a couple of new
      binutils tests to exercise the new behaviour.
    
      There are a couple of missing features in the current patch however,
      although I do intend to address them in follow up submissions:
    
      Firstly the code does not check the build-id inside separate debug
      info files when it is searching for a file specified by a
      .gnu_debugaltlink section.  It just assumes that if the file is there,
      then it contains the information being sought.
    
      Secondly I have not checked the DWARF-5 version of these link
      features, so there will probably be code to add there.
    
      Thirdly I have only implemented link following for the
      DW_FORM_GNU_strp_alt format.  Other alternate formats (eg
      DW_FORM_GNU_ref_alt) have yet to be implemented.
    
      Lastly, whilst implementing this feature I found it necessary to move
      some of the global variables used by readelf (eg section_headers) into
      a structure that can be passed around.  I have moved all of the global
      variables that were necessary to get the patch working, but I need to
      complete the operation and move the remaining, file-specific variables
      (eg dynamic_strings).
    
    Cheers
      Nick
    
    binutils	PR 15152
    	* dwarf.h (enum dwarf_section_display_enum): Add gnu_debuglink,
    	gnu_debugaltlink and separate_debug_str.
    	(struct dwarf_section): Add filename field.
    	Add prototypes for load_separate_debug_file, close_debug_file and
    	open_debug_file.
    	* dwarf.c (do_debug_links): New.
    	(do_follow_links): New.
    	(separate_debug_file, separate_debug_filename): New.
    	(fetch_alt_indirect_string): New function.  Retrieves a string
    	from the debug string table in the separate debug info file.
    	(read_and_display_attr_value): Use it with DW_FORM_GNU_strp_alt.
    	(load_debug_section_with_follow): New function.  Like
    	load_debug_section, but if the first attempt fails, then tries
    	again in the separate debug info file.
    	(introduce): New function.
    	(process_debug_info): Use load_debug_section_with_follow and
    	introduce.
    	(load_debug_info): Likewise.
    	(display_debug_lines_raw): Likewise.
    	(display_debug_lines_decoded): Likewise.
    	(display_debug_macinfo): Likewise.
    	(display_debug_macro): Likewise.
    	(display_debug_abbrev): Likewise.
    	(display_debug_loc): Likewise.
    	(display_debug_str): Likewise.
    	(display_debug_aranges): Likewise.
    	(display_debug_addr); Likewise.
    	(display_debug_frames): Likewise.
    	(display_gdb_index): Likewise.
    	(process_cu_tu_index): Likewise.
    	(load_cu_tu_indexes): Likewise.
    	(display_debug_links): New function.  Displays the contents of a
    	.gnu_debuglink or .gnu_debugaltlink section.
    	(calc_gnu_debuglink_ctc32):New function.  Calculates a CRC32
    	value.
    	(check_gnu_debuglink): New function.  Checks the CRC of a
    	potential separate debug info file.
    	(parse_gnu_debuglink): New function.  Reads a CRC value out of a
    	.gnu_debuglink section.
    	(check_gnu_debugaltlink): New function.
    	(parse_gnu_debugaltlink): New function.  Reads the build-id value
    	out of a .gnu_debugaltlink section.
    	(load_separate_debug_info): New function.  Finds and loads a
    	separate debug info file.
    	(load_separate_debug_file): New function. Attempts to find and
    	follow a link to a separate debug info file.
    	(free_debug_memory): Free the separate debug info file
    	information.
    	(opts_table): Add "follow-links" and "links".
    	(dwarf_select_sections_by_letters): Add "k" and "K".
    	(debug_displays): Reformat.  Add .gnu-debuglink and
    	.gnu_debugaltlink.
    	Add an extra entry for .debug_str in a separate debug info file.
    	* doc/binutils.texi: Move description of debug dump features
    	common to both readelf and objdump into...
    	* objdump.c (usage): Add -Wk and -WK.
    	(load_specific_debug_section): Initialise the filename field in
    	the dwarf_section structure.
    	(close_debug_file): New function.
    	(open_debug_file): New function.
    	(dump_dwarf): Load and dump the separate debug info sections.
    	* readelf.c (struct filedata): New structure.  Contains various
    	variables that used to be global:
    	(current_file_size, string_table, string_table_length, elf_header)
    	(section_headers, program_headers, dump_sects, num_dump_sects):
    	Move into filedata structure.
    	(cmdline): New global variable.  Contains list of sections to dump
    	by number, as specified on the command line.
    	Add filedata parameter to most functions.
    	(load_debug_section): Load the string table if it has not already
    	been retrieved.
    	(close_file): New function.
    	(close_debug_file): New function.
    	(open_file): New function.
    	(open_debug_file): New function.
    	(process_object): Process sections in any separate debug info files.
    	* doc/debug.options.texi: New file.  Add description of =links and
    	=follow-links options.
    	* NEWS: Mention the new feature.
    	* elfcomm.c: Have the byte gte functions take a const pointer.
    	* elfcomm.h: Update prototypes.
    	* testsuite/binutils-all/dw5.W: Update expected output.
    	* testsuite/binutils-all/objdump.WL: Update expected output.
    	* testsuite/binutils-all/objdump.exp: Add test of -WK and -Wk.
    	* testsuite/binutils-all/readelf.exp: Add test of -wK and -wk.
    	* testsuite/binutils-all/readelf.k: New file.
    	* testsuite/binutils-all/objdump.Wk: New file.
    	* testsuite/binutils-all/objdump.WK2: New file.
    	* testsuite/binutils-all/linkdebug.s: New file.
    	* testsuite/binutils-all/debuglink.s: New file.
    
    gas	* testsuite/gas/avr/large-debug-line-table.d: Update expected
    	output.
    	* testsuite/gas/elf/dwarf2-11.d: Likewise.
    	* testsuite/gas/elf/dwarf2-12.d: Likewise.
    	* testsuite/gas/elf/dwarf2-13.d: Likewise.
    	* testsuite/gas/elf/dwarf2-14.d: Likewise.
    	* testsuite/gas/elf/dwarf2-15.d: Likewise.
    	* testsuite/gas/elf/dwarf2-16.d: Likewise.
    	* testsuite/gas/elf/dwarf2-17.d: Likewise.
    	* testsuite/gas/elf/dwarf2-18.d: Likewise.
    	* testsuite/gas/elf/dwarf2-5.d: Likewise.
    	* testsuite/gas/elf/dwarf2-6.d: Likewise.
    	* testsuite/gas/elf/dwarf2-7.d: Likewise.
    
    ld	* testsuite/ld-avr/gc-section-debugline.d: Update expected
    	output.

Diff:
---
 binutils/ChangeLog                             |   94 +
 binutils/NEWS                                  |    8 +
 binutils/doc/binutils.texi                     |   91 +-
 binutils/doc/debug.options.texi                |  141 ++
 binutils/dwarf.c                               |  946 ++++++---
 binutils/dwarf.h                               |   44 +-
 binutils/elfcomm.c                             |   10 +-
 binutils/elfcomm.h                             |   10 +-
 binutils/objdump.c                             |   74 +-
 binutils/readelf.c                             | 2424 +++++++++++++-----------
 binutils/testsuite/binutils-all/debuglink.s    |   67 +
 binutils/testsuite/binutils-all/dw5.W          |    2 +-
 binutils/testsuite/binutils-all/linkdebug.s    |   54 +
 binutils/testsuite/binutils-all/objdump.WK2    |   25 +
 binutils/testsuite/binutils-all/objdump.WL     |    2 +-
 binutils/testsuite/binutils-all/objdump.Wk     |    8 +
 binutils/testsuite/binutils-all/objdump.exp    |   83 +
 binutils/testsuite/binutils-all/readelf.exp    |   32 +
 binutils/testsuite/binutils-all/readelf.k      |    7 +
 gas/ChangeLog                                  |   17 +
 gas/testsuite/gas/avr/large-debug-line-table.d |    2 +-
 gas/testsuite/gas/elf/dwarf2-11.d              |    2 +-
 gas/testsuite/gas/elf/dwarf2-12.d              |    2 +-
 gas/testsuite/gas/elf/dwarf2-13.d              |    2 +-
 gas/testsuite/gas/elf/dwarf2-14.d              |    2 +-
 gas/testsuite/gas/elf/dwarf2-15.d              |    2 +-
 gas/testsuite/gas/elf/dwarf2-16.d              |    2 +-
 gas/testsuite/gas/elf/dwarf2-17.d              |    2 +-
 gas/testsuite/gas/elf/dwarf2-18.d              |    2 +-
 gas/testsuite/gas/elf/dwarf2-5.d               |    2 +-
 gas/testsuite/gas/elf/dwarf2-6.d               |    2 +-
 gas/testsuite/gas/elf/dwarf2-7.d               |    2 +-
 ld/ChangeLog                                   |    6 +
 ld/testsuite/ld-avr/gc-section-debugline.d     |    2 +-
 34 files changed, 2676 insertions(+), 1495 deletions(-)

diff --git a/binutils/ChangeLog b/binutils/ChangeLog
index b7cc9a1..3f20d7f 100644
--- a/binutils/ChangeLog
+++ b/binutils/ChangeLog
@@ -1,3 +1,97 @@
+2017-11-15  Nick Clifton  <nickc@redhat.com>
+
+	PR 15152
+	* dwarf.h (enum dwarf_section_display_enum): Add gnu_debuglink,
+	gnu_debugaltlink and separate_debug_str.
+	(struct dwarf_section): Add filename field.
+	Add prototypes for load_separate_debug_file, close_debug_file and
+	open_debug_file.
+	* dwarf.c (do_debug_links): New.
+	(do_follow_links): New.
+	(separate_debug_file, separate_debug_filename): New.
+	(fetch_alt_indirect_string): New function.  Retrieves a string
+	from the debug string table in the separate debug info file.
+	(read_and_display_attr_value): Use it with DW_FORM_GNU_strp_alt.
+	(load_debug_section_with_follow): New function.  Like
+	load_debug_section, but if the first attempt fails, then tries
+	again in the separate debug info file.
+	(introduce): New function.
+	(process_debug_info): Use load_debug_section_with_follow and
+	introduce.
+	(load_debug_info): Likewise.
+	(display_debug_lines_raw): Likewise.
+	(display_debug_lines_decoded): Likewise.
+	(display_debug_macinfo): Likewise.
+	(display_debug_macro): Likewise.
+	(display_debug_abbrev): Likewise.
+	(display_debug_loc): Likewise.
+	(display_debug_str): Likewise.
+	(display_debug_aranges): Likewise.
+	(display_debug_addr); Likewise.
+	(display_debug_frames): Likewise.
+	(display_gdb_index): Likewise.
+	(process_cu_tu_index): Likewise.
+	(load_cu_tu_indexes): Likewise.
+	(display_debug_links): New function.  Displays the contents of a
+	.gnu_debuglink or .gnu_debugaltlink section.
+	(calc_gnu_debuglink_ctc32):New function.  Calculates a CRC32
+	value.
+	(check_gnu_debuglink): New function.  Checks the CRC of a
+	potential separate debug info file.
+	(parse_gnu_debuglink): New function.  Reads a CRC value out of a
+	.gnu_debuglink section.
+	(check_gnu_debugaltlink): New function.
+	(parse_gnu_debugaltlink): New function.  Reads the build-id value
+	out of a .gnu_debugaltlink section.
+	(load_separate_debug_info): New function.  Finds and loads a
+	separate debug info file.
+	(load_separate_debug_file): New function. Attempts to find and
+	follow a link to a separate debug info file.
+	(free_debug_memory): Free the separate debug info file
+	information.
+	(opts_table): Add "follow-links" and "links".
+	(dwarf_select_sections_by_letters): Add "k" and "K".
+	(debug_displays): Reformat.  Add .gnu-debuglink and
+	.gnu_debugaltlink.
+	Add an extra entry for .debug_str in a separate debug info file.
+	* doc/binutils.texi: Move description of debug dump features
+	common to both readelf and objdump into...
+	* objdump.c (usage): Add -Wk and -WK.
+	(load_specific_debug_section): Initialise the filename field in
+	the dwarf_section structure.
+	(close_debug_file): New function.
+	(open_debug_file): New function.
+	(dump_dwarf): Load and dump the separate debug info sections.
+	* readelf.c (struct filedata): New structure.  Contains various
+	variables that used to be global:
+	(current_file_size, string_table, string_table_length, elf_header)
+	(section_headers, program_headers, dump_sects, num_dump_sects):
+	Move into filedata structure.
+	(cmdline): New global variable.  Contains list of sections to dump
+	by number, as specified on the command line.
+	Add filedata parameter to most functions.
+	(load_debug_section): Load the string table if it has not already
+	been retrieved.
+	(close_file): New function.
+	(close_debug_file): New function.
+	(open_file): New function.
+	(open_debug_file): New function.
+	(process_object): Process sections in any separate debug info files.
+	* doc/debug.options.texi: New file.  Add description of =links and
+	=follow-links options.
+	* NEWS: Mention the new feature.
+	* elfcomm.c: Have the byte get functions take a const pointer.
+	* elfcomm.h: Update prototypes.
+	* testsuite/binutils-all/dw5.W: Update expected output.
+	* testsuite/binutils-all/objdump.WL: Update expected output.
+	* testsuite/binutils-all/objdump.exp: Add test of -WK and -Wk.
+	* testsuite/binutils-all/readelf.exp: Add test of -wK and -wk.
+	* testsuite/binutils-all/readelf.k: New file.
+	* testsuite/binutils-all/objdump.Wk: New file.
+	* testsuite/binutils-all/objdump.WK2: New file.
+	* testsuite/binutils-all/linkdebug.s: New file.
+	* testsuite/binutils-all/debuglink.s: New file.
+
 2017-11-10  Jim Wilson  <jimw@sifive.com>
 
 	* testsuite/binutils-all/objdump.exp: Expect the debug_ranges test to
diff --git a/binutils/NEWS b/binutils/NEWS
index 23c63ef..345b9e5 100644
--- a/binutils/NEWS
+++ b/binutils/NEWS
@@ -1,5 +1,13 @@
 -*- text -*-
 
+* Add --debug-dump=links option to readelf and --dwarf=links option to objdump
+  which displays the contents of any .gnu_debuglink or .gnu_debugaltlink
+  sections.
+
+  Add a --debug-dump=follow-links option to readelf and a --dwarf=follow-links
+  option to objdump which causes indirect links into separate debug info files
+  to be followed when dumping other DWARF sections.
+
 Changes in 2.29:
 
 * The MIPS port now supports microMIPS eXtended Physical Addressing (XPA)
diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi
index 03706db..a14c827 100644
--- a/binutils/doc/binutils.texi
+++ b/binutils/doc/binutils.texi
@@ -2071,11 +2071,8 @@ objdump [@option{-a}|@option{--archive-headers}]
         [@option{-r}|@option{--reloc}]
         [@option{-R}|@option{--dynamic-reloc}]
         [@option{-s}|@option{--full-contents}]
-        [@option{-W[lLiaprmfFsoRt]}|
-         @option{--dwarf}[=rawline,=decodedline,=info,=abbrev,=pubnames]
-                 [=aranges,=macro,=frames,=frames-interp,=str,=loc]
-                 [=Ranges,=pubtypes,=trace_info,=trace_abbrev]
-                 [=trace_aranges,=gdb_index]
+        [@option{-W[lLiaprmfFsoRtUuTgAckK]}|
+         @option{--dwarf}[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=loc,=Ranges,=pubtypes,=trace_info,=trace_abbrev,=trace_aranges,=gdb_index,=addr,=cu_index,=links,=follow-links]
         [@option{-G}|@option{--stabs}]
         [@option{-t}|@option{--syms}]
         [@option{-T}|@option{--dynamic-syms}]
@@ -2560,42 +2557,9 @@ This is the default when @option{--prefix-addresses} is used.
 Display @var{width} bytes on a single line when disassembling
 instructions.
 
-@item -W[lLiaprmfFsoRt]
-@itemx --dwarf[=rawline,=decodedline,=info,=abbrev,=pubnames]
-@itemx --dwarf[=aranges,=macro,=frames,=frames-interp,=str,=loc]
-@itemx --dwarf[=Ranges,=pubtypes,=trace_info,=trace_abbrev]
-@itemx --dwarf[=trace_aranges,=gdb_index]
-@cindex DWARF
-@cindex debug symbols
-Displays the contents of the debug sections in the file, if any are
-present.  If one of the optional letters or words follows the switch
-then only data found in those specific sections will be dumped.
-
-Note that there is no single letter option to display the content of
-trace sections or .gdb_index.
-
-Note: the output from the @option{=info} option can also be affected
-by the options @option{--dwarf-depth}, the @option{--dwarf-start} and
-the @option{--dwarf-check}.
-
-@item --dwarf-depth=@var{n}
-Limit the dump of the @code{.debug_info} section to @var{n} children.
-This is only useful with @option{--dwarf=info}.  The default is
-to print all DIEs; the special value 0 for @var{n} will also have this
-effect.
-
-With a non-zero value for @var{n}, DIEs at or deeper than @var{n}
-levels will not be printed.  The range for @var{n} is zero-based.
-
-@item --dwarf-start=@var{n}
-Print only DIEs beginning with the DIE numbered @var{n}.  This is only
-useful with @option{--dwarf=info}.
-
-If specified, this option will suppress printing of any header
-information and all DIEs before the DIE numbered @var{n}.  Only
-siblings and children of the specified DIE will be printed.
-
-This can be used in conjunction with @option{--dwarf-depth}.
+@item -W[lLiaprmfFsoRtUuTgAckK]
+@itemx --dwarf[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=loc,=Ranges,=pubtypes,=trace_info,=trace_abbrev,=trace_aranges,=gdb_index,=addr,=cu_index,=links,=follow-links]
+@include debug.options.texi
 
 @item --dwarf-check
 Enable additional checks for consistency of Dwarf information.
@@ -4627,8 +4591,8 @@ readelf [@option{-a}|@option{--all}]
         [@option{-R} <number or name>|@option{--relocated-dump=}<number or name>]
         [@option{-z}|@option{--decompress}]
         [@option{-c}|@option{--archive-index}]
-        [@option{-w[lLiaprmfFsoRt]}|
-         @option{--debug-dump}[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=loc,=Ranges,=pubtypes,=trace_info,=trace_abbrev,=trace_aranges,=gdb_index]]
+        [@option{-w[lLiaprmfFsoRtUuTgAckK]}|
+         @option{--debug-dump}[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=loc,=Ranges,=pubtypes,=trace_info,=trace_abbrev,=trace_aranges,=gdb_index,=addr,=cu_index,=links,=follow-links]]
         [@option{--dwarf-depth=@var{n}}]
         [@option{--dwarf-start=@var{n}}]
         [@option{-I}|@option{--histogram}]
@@ -4803,44 +4767,9 @@ Displays the file symbol index information contained in the header part
 of binary archives.  Performs the same function as the @option{t}
 command to @command{ar}, but without using the BFD library.  @xref{ar}.
 
-@item -w[lLiaprmfFsoRt]
-@itemx --debug-dump[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=loc,=Ranges,=pubtypes,=trace_info,=trace_abbrev,=trace_aranges,=gdb_index]
-Displays the contents of the debug sections in the file, if any are
-present.  If one of the optional letters or words follows the switch
-then only data found in those specific sections will be dumped.
-
-Note that there is no single letter option to display the content of
-trace sections or .gdb_index.
-
-Note: the @option{=decodedline} option will display the interpreted
-contents of a .debug_line section whereas the @option{=rawline} option
-dumps the contents in a raw format.
-
-Note: the @option{=frames-interp} option will display the interpreted
-contents of a .debug_frame section whereas the @option{=frames} option
-dumps the contents in a raw format.
-
-Note: the output from the @option{=info} option can also be affected
-by the options @option{--dwarf-depth} and @option{--dwarf-start}.
-
-@item --dwarf-depth=@var{n}
-Limit the dump of the @code{.debug_info} section to @var{n} children.
-This is only useful with @option{--debug-dump=info}.  The default is
-to print all DIEs; the special value 0 for @var{n} will also have this
-effect.
-
-With a non-zero value for @var{n}, DIEs at or deeper than @var{n}
-levels will not be printed.  The range for @var{n} is zero-based.
-
-@item --dwarf-start=@var{n}
-Print only DIEs beginning with the DIE numbered @var{n}.  This is only
-useful with @option{--debug-dump=info}.
-
-If specified, this option will suppress printing of any header
-information and all DIEs before the DIE numbered @var{n}.  Only
-siblings and children of the specified DIE will be printed.
-
-This can be used in conjunction with @option{--dwarf-depth}.
+@item -w[lLiaprmfFsoRtUuTgAckK]
+@itemx --debug-dump[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=loc,=Ranges,=pubtypes,=trace_info,=trace_abbrev,=trace_aranges,=gdb_index,=addr,=cu_index,=links,=follow-links]
+@include debug.options.texi
 
 @item -I
 @itemx --histogram
diff --git a/binutils/doc/debug.options.texi b/binutils/doc/debug.options.texi
new file mode 100644
index 0000000..9204b2b
--- /dev/null
+++ b/binutils/doc/debug.options.texi
@@ -0,0 +1,141 @@
+@c This file contains the entry for the -w/--debug-dump (readelf) and
+@c  -W/--dwarf (objdump) option that is common to both readelf and objdump.
+
+Displays the contents of the DWARF debug sections in the file, if any
+are present.  Compressed debug sections are automatically decompressed
+(temporarily) before they are displayed.  If one or more of the
+optional letters or words follows the switch then only those type(s)
+of data will be dumped.  The letters and words refer to the following
+information:
+
+@c Please Keep This Table Alpha Sorted.
+@table @code
+@item a
+@itemx =abbrev
+Displays the contents of the @samp{.debug_abbrev} section.
+
+@item A
+@itemx =addr
+Displays the contents of the @samp{.debug_addr} section.
+
+@item c
+@itemx =cu_index
+Displays the contents of the @samp{.debug_cu_index} and/or
+@samp{.debug_tu_index} sections.
+
+@item f
+@itemx =frames
+Display the raw contents of a @samp{.debug_frame} section.
+
+@item F
+@item =frame-interp
+Display the interpreted contents of a @samp{.debug_frame} section.
+
+@item g
+@itemx =gdb_index
+Displays the contents of the @samp{.gdb_index} and/or
+@samp{.debug_names} sections.
+
+@item i
+@itemx =info
+Displays the contents of the @samp{.debug_info} section.  Note: the
+output from this option can also be restricted by the use of the 
+@option{--dwarf-depth} and @option{--dwarf-start} options.
+
+@item k
+@itemx =links
+Displays the contents of the @samp{.gnu_debuglink} and/or
+@samp{.gnu_debugaltlink} sections.
+
+@item K
+@itemx =follow-links
+Display the contents of any selected debug sections that are found in
+a linked, separate debug info file.  This can result in multiple
+versions of the same debug section being displayed if both the main
+file and the separate debug info file contain sections with the same
+name.
+
+When displaying other debug information, if a link is encountered to
+a separate debug info file, then attempt to follow the link and
+display the referenced contents.
+
+@item l
+@itemx =rawline
+Displays the contents of the @samp{.debug_line} section in a raw
+format.
+
+@item L
+@item =decodedline
+Displays the interpreted contents of the @samp{.debug_line} section.
+
+@item m
+@itemx =macro
+Displays the contents of the @samp{.debug_macro} and/or
+@samp{.debug_macinfo} sections.
+
+@item o
+@itemx =loc
+Displays the contents of the @samp{.debug_loc} and/or
+@samp{.debug_loclists} sections.
+
+@item p
+@itemx =pubnames
+Displays the contents of the @samp{.debug_pubnames} and/or
+@samp{.debug_gnu_pubnames} sections.
+
+@item r
+@itemx =aranges
+Displays the contents of the @samp{.debug_aranges} section.
+
+@item R
+@itemx =Ranges
+Displays the contents of the @samp{.debug_ranges} and/or
+@samp{.debug_rnglists} sections.
+
+@item s
+@itemx =str
+Displays the contents of the @samp{.debug_str}, @samp{.debug_line_str}
+and/or @samp{.debug_str_offsets} sections.
+
+@item t
+@itemx =pubtype
+Displays the contents of the @samp{.debug_pubtypes} and/or
+@samp{.debug_gnu_pubtypes} sections.
+
+@item T
+@itemx =trace_aranges
+Displays the contents of the @samp{.trace_aranges} section.
+
+@item u
+@itemx =trace_abbrev
+Displays the contents of the @samp{.trace_abbrev} section.
+
+@item U
+@itemx =trace_info
+Displays the contents of the @samp{.trace_info} section.
+
+@end table
+
+Note: displaying the contents of @samp{.debug_static_funcs},
+@samp{.debug_static_vars} and @samp{debug_weaknames} sections is not
+currently supported.
+
+@item --dwarf-depth=@var{n}
+Limit the dump of the @code{.debug_info} section to @var{n} children.
+This is only useful with @option{--debug-dump=info}.  The default is
+to print all DIEs; the special value 0 for @var{n} will also have this
+effect.
+
+With a non-zero value for @var{n}, DIEs at or deeper than @var{n}
+levels will not be printed.  The range for @var{n} is zero-based.
+
+@item --dwarf-start=@var{n}
+Print only DIEs beginning with the DIE numbered @var{n}.  This is only
+useful with @option{--debug-dump=info}.
+
+If specified, this option will suppress printing of any header
+information and all DIEs before the DIE numbered @var{n}.  Only
+siblings and children of the specified DIE will be printed.
+
+This can be used in conjunction with @option{--dwarf-depth}.
+
diff --git a/binutils/dwarf.c b/binutils/dwarf.c
index 140a988..e6f6a43 100644
--- a/binutils/dwarf.c
+++ b/binutils/dwarf.c
@@ -28,6 +28,7 @@
 #include "dwarf2.h"
 #include "dwarf.h"
 #include "gdb/gdb-index.h"
+#include "filenames.h"
 #include <assert.h>
 
 #undef MAX
@@ -68,6 +69,8 @@ int do_trace_aranges;
 int do_debug_addr;
 int do_debug_cu_index;
 int do_wide;
+int do_debug_links;
+int do_follow_links;
 
 int dwarf_cutoff_level = -1;
 unsigned long dwarf_start_die;
@@ -87,6 +90,10 @@ static unsigned int *shndx_pool = NULL;
 static unsigned int shndx_pool_size = 0;
 static unsigned int shndx_pool_used = 0;
 
+/* Pointer to a separate file containing extra debug information.  */
+static void * separate_debug_file = NULL;
+static const char * separate_debug_filename = NULL;
+
 /* For version 2 package files, each set contains an array of section offsets
    and an array of section sizes, giving the offset and size of the
    contribution from a CU or TU within one of the debug sections.
@@ -1670,20 +1677,57 @@ add64 (dwarf_vma * high_bits, dwarf_vma * low_bits, dwarf_vma inc)
   * low_bits = tmp;
 }
 
+static const char *
+fetch_alt_indirect_string (dwarf_vma offset)
+{
+  struct dwarf_section * section;
+  const char *           ret;
+
+  if (! do_follow_links)
+    return "";
+
+  if (separate_debug_file == NULL)
+    return _("<following link not possible>");
+
+  if (! load_debug_section (separate_debug_str, separate_debug_file))
+    return _("<could not load separate string section>");
+
+  section = &debug_displays [separate_debug_str].section;
+  if (section->start == NULL)
+    return  _("<no .debug_str section>");
+
+  if (offset >= section->size)
+    {
+      warn (_("DW_FORM_GNU_strp_alt offset too big: %s\n"), dwarf_vmatoa ("x", offset));
+      return _("<offset is too big>");
+    }
+
+  ret = (const char *) (section->start + offset);
+  /* Unfortunately we cannot rely upon the .debug_str section ending with a
+     NUL byte.  Since our caller is expecting to receive a well formed C
+     string we test for the lack of a terminating byte here.  */
+  if (strnlen ((const char *) ret, section->size - offset)
+      == section->size - offset)
+    return _("<no NUL byte at end of .debug_str section>");
+
+  return ret;
+}
+	
 static unsigned char *
-read_and_display_attr_value (unsigned long attribute,
-			     unsigned long form,
-			     dwarf_signed_vma implicit_const,
-			     unsigned char * data,
-			     unsigned char * end,
-			     dwarf_vma cu_offset,
-			     dwarf_vma pointer_size,
-			     dwarf_vma offset_size,
-			     int dwarf_version,
-			     debug_info * debug_info_p,
-			     int do_loc,
-			     struct dwarf_section * section,
-			     struct cu_tu_set * this_set, char delimiter)
+read_and_display_attr_value (unsigned long           attribute,
+			     unsigned long           form,
+			     dwarf_signed_vma        implicit_const,
+			     unsigned char *         data,
+			     unsigned char *         end,
+			     dwarf_vma               cu_offset,
+			     dwarf_vma               pointer_size,
+			     dwarf_vma               offset_size,
+			     int                     dwarf_version,
+			     debug_info *            debug_info_p,
+			     int                     do_loc,
+			     struct dwarf_section *  section,
+			     struct cu_tu_set *      this_set,
+			     char                    delimiter)
 {
   dwarf_vma uvalue = 0;
   unsigned char *block_start = NULL;
@@ -1790,6 +1834,7 @@ read_and_display_attr_value (unsigned long attribute,
     case DW_FORM_GNU_ref_alt:
       if (!do_loc)
 	printf ("%c<alt 0x%s>", delimiter, dwarf_vmatoa ("x",uvalue));
+      /* FIXME: Follow the reference...  */
       break;
 
     case DW_FORM_ref1:
@@ -2002,8 +2047,11 @@ read_and_display_attr_value (unsigned long attribute,
 
     case DW_FORM_GNU_strp_alt:
       if (!do_loc)
-	printf (_("%c(alt indirect string, offset: 0x%s)"), delimiter,
-		dwarf_vmatoa ("x", uvalue));
+	{
+	  printf (_("%c(alt indirect string, offset: 0x%s) %s"), delimiter,
+		  dwarf_vmatoa ("x", uvalue),
+		  fetch_alt_indirect_string (uvalue));
+	}
       break;
 
     case DW_FORM_indirect:
@@ -2519,19 +2567,19 @@ get_AT_name (unsigned long attribute)
 }
 
 static unsigned char *
-read_and_display_attr (unsigned long attribute,
-		       unsigned long form,
-		       dwarf_signed_vma implicit_const,
-		       unsigned char * data,
-		       unsigned char * end,
-		       dwarf_vma cu_offset,
-		       dwarf_vma pointer_size,
-		       dwarf_vma offset_size,
-		       int dwarf_version,
-		       debug_info * debug_info_p,
-		       int do_loc,
-		       struct dwarf_section * section,
-		       struct cu_tu_set * this_set)
+read_and_display_attr (unsigned long           attribute,
+		       unsigned long           form,
+		       dwarf_signed_vma        implicit_const,
+		       unsigned char *         data,
+		       unsigned char *         end,
+		       dwarf_vma               cu_offset,
+		       dwarf_vma               pointer_size,
+		       dwarf_vma               offset_size,
+		       int                     dwarf_version,
+		       debug_info *            debug_info_p,
+		       int                     do_loc,
+		       struct dwarf_section *  section,
+		       struct cu_tu_set *      this_set)
 {
   if (!do_loc)
     printf ("   %-18s:", get_AT_name (attribute));
@@ -2544,6 +2592,56 @@ read_and_display_attr (unsigned long attribute,
   return data;
 }
 
+/* Like load_debug_section, but if the ordinary call fails, and we are
+   following debug links, and we have been able to load a separate debug
+   info file, then attempt to load the requested section from the separate
+   file.  */
+
+static bfd_boolean
+load_debug_section_with_follow (enum dwarf_section_display_enum sec_enum,
+				void * data)
+{
+  if (load_debug_section (sec_enum, data))
+    {
+      if (data == separate_debug_file)
+	debug_displays[sec_enum].section.filename = separate_debug_filename;
+	
+      /* FIXME: We should check to see if there is a separate debug info file
+	 that also contains this section, and if so, issue a warning.  */
+      return TRUE;
+    }
+
+  if (do_follow_links && separate_debug_file != NULL)
+    if (load_debug_section (sec_enum, separate_debug_file))
+      {
+	debug_displays[sec_enum].section.filename = separate_debug_filename;
+	return TRUE;
+      }
+
+  return FALSE;
+}
+
+static void
+introduce (struct dwarf_section * section, bfd_boolean raw)
+{
+  if (raw)
+    {
+      if (do_follow_links && section->filename)
+	printf (_("Raw dump of debug contents of section %s (loaded from %s):\n\n"),
+		section->name, section->filename);
+      else
+	printf (_("Raw dump of debug contents of section %s:\n\n"), section->name);
+    }
+  else
+    {
+      if (do_follow_links && section->filename)
+	printf (_("Contents of the %s section (loaded from %s):\n\n"),
+		section->name, section->filename);
+      else
+	printf (_("Contents of the %s section:\n\n"), section->name);
+    }
+}
+  
 /* Process the contents of a .debug_info section.  If do_loc is non-zero
    then we are scanning for location lists and we do not want to display
    anything to the user.  If do_types is non-zero, we are processing
@@ -2630,25 +2728,25 @@ process_debug_info (struct dwarf_section *section,
 
   if (!do_loc)
     {
-      if (dwarf_start_die == 0)
-	printf (_("Contents of the %s section:\n\n"), section->name);
-
-      load_debug_section (str, file);
-      load_debug_section (line_str, file);
-      load_debug_section (str_dwo, file);
-      load_debug_section (str_index, file);
-      load_debug_section (str_index_dwo, file);
-      load_debug_section (debug_addr, file);
+      load_debug_section_with_follow (str, file);
+      load_debug_section_with_follow (line_str, file);
+      load_debug_section_with_follow (str_dwo, file);
+      load_debug_section_with_follow (str_index, file);
+      load_debug_section_with_follow (str_index_dwo, file);
+      load_debug_section_with_follow (debug_addr, file);
     }
 
-  load_debug_section (abbrev_sec, file);
+  load_debug_section_with_follow (abbrev_sec, file);
   if (debug_displays [abbrev_sec].section.start == NULL)
     {
       warn (_("Unable to locate %s section!\n"),
-	    debug_displays [abbrev_sec].section.name);
+	    debug_displays [abbrev_sec].section.uncompressed_name);
       return 0;
     }
 
+  if (!do_loc && dwarf_start_die == 0)
+    introduce (section, FALSE);
+  
   for (section_begin = start, unit = 0; start < end; unit++)
     {
       DWARF2_Internal_CompUnit compunit;
@@ -3072,11 +3170,11 @@ load_debug_info (void * file)
   /* If this is a DWARF package file, load the CU and TU indexes.  */
   (void) load_cu_tu_indexes (file);
 
-  if (load_debug_section (info, file)
+  if (load_debug_section_with_follow (info, file)
       && process_debug_info (&debug_displays [info].section, file, abbrev, 1, 0))
     return num_debug_info_entries;
 
-  if (load_debug_section (info_dwo, file)
+  if (load_debug_section_with_follow (info_dwo, file)
       && process_debug_info (&debug_displays [info_dwo].section, file,
 			     abbrev_dwo, 1, 0))
     return num_debug_info_entries;
@@ -3204,10 +3302,12 @@ read_debug_line_header (struct dwarf_section * section,
 }
 
 static unsigned char *
-display_formatted_table (unsigned char *data,
-			 unsigned char *start, unsigned char *end,
-			 const DWARF2_Internal_LineInfo *linfo,
-			 struct dwarf_section *section, const char *what)
+display_formatted_table (unsigned char *                   data,
+			 unsigned char *                   start,
+			 unsigned char *                   end,
+			 const DWARF2_Internal_LineInfo *  linfo,
+			 struct dwarf_section *            section,
+			 const char *                      what)
 {
   unsigned char *format_start, format_count, *format, formati;
   dwarf_vma data_count, datai;
@@ -3320,15 +3420,15 @@ display_formatted_table (unsigned char *data,
 }
 
 static int
-display_debug_lines_raw (struct dwarf_section *section,
-			 unsigned char *data,
-			 unsigned char *end, void *file)
+display_debug_lines_raw (struct dwarf_section *  section,
+			 unsigned char *         data,
+			 unsigned char *         end,
+			 void *                  file)
 {
   unsigned char *start = section->start;
   int verbose_view = 0;
 
-  printf (_("Raw dump of debug contents of section %s:\n\n"),
-	  section->name);
+  introduce (section, TRUE);
 
   while (data < end)
     {
@@ -3416,7 +3516,7 @@ display_debug_lines_raw (struct dwarf_section *section,
 
 	  if (linfo.li_version >= 5)
 	    {
-	      load_debug_section (line_str, file);
+	      load_debug_section_with_follow (line_str, file);
 
 	      data = display_formatted_table (data, start, end, &linfo, section,
 					      _("Directory"));
@@ -3742,14 +3842,14 @@ typedef struct
 /* Output a decoded representation of the .debug_line section.  */
 
 static int
-display_debug_lines_decoded (struct dwarf_section *section,
-			     unsigned char *data,
-			     unsigned char *end, void *fileptr)
+display_debug_lines_decoded (struct dwarf_section *  section,
+			     unsigned char *         data,
+			     unsigned char *         end,
+			     void *                  fileptr)
 {
   static DWARF2_Internal_LineInfo saved_linfo;
 
-  printf (_("Decoded dump of debug contents of section %s:\n\n"),
-	  section->name);
+  introduce (section, FALSE);
 
   while (data < end)
     {
@@ -3815,7 +3915,7 @@ display_debug_lines_decoded (struct dwarf_section *section,
 	      dwarf_vma formati, entryi;
 	      unsigned int bytes_read;
 
-	      load_debug_section (line_str, fileptr);
+	      load_debug_section_with_follow (line_str, fileptr);
 
 	      /* Skip directories format.  */
 	      SAFE_BYTE_GET_AND_INC (format_count, data, 1, end);
@@ -4548,7 +4648,7 @@ display_debug_pubnames_worker (struct dwarf_section *section,
      we test for that later on.  */
   load_debug_info (file);
 
-  printf (_("Contents of the %s section:\n\n"), section->name);
+  introduce (section, FALSE);
 
   while (start < end)
     {
@@ -4694,7 +4794,7 @@ display_debug_macinfo (struct dwarf_section *section,
   unsigned int bytes_read;
   enum dwarf_macinfo_record_type op;
 
-  printf (_("Contents of the %s section:\n\n"), section->name);
+  introduce (section, FALSE);
 
   while (curr < end)
     {
@@ -4867,10 +4967,10 @@ display_debug_macro (struct dwarf_section *section,
   unsigned char *extended_op_buf[256];
   unsigned int bytes_read;
 
-  load_debug_section (str, file);
-  load_debug_section (line, file);
+  load_debug_section_with_follow (str, file);
+  load_debug_section_with_follow (line, file);
 
-  printf (_("Contents of the %s section:\n\n"), section->name);
+  introduce (section, FALSE);
 
   while (curr < end)
     {
@@ -5124,7 +5224,7 @@ display_debug_abbrev (struct dwarf_section *section,
   unsigned char *start = section->start;
   unsigned char *end = start + section->size;
 
-  printf (_("Contents of the %s section:\n\n"), section->name);
+  introduce (section, FALSE);
 
   do
     {
@@ -5867,9 +5967,12 @@ display_debug_loc (struct dwarf_section *section, void *file)
 
   if (!locs_sorted)
     array = (unsigned int *) xcmalloc (num_loc_list, sizeof (unsigned int));
-  printf (_("Contents of the %s section:\n\n"), section->name);
+
+  introduce (section, FALSE);
+
   if (reloc_at (section, 0))
     printf (_(" Warning: This section has relocations - addresses seen here may not be accurate.\n\n"));
+
   printf (_("    Offset   Begin            End              Expression\n"));
 
   seen_first_offset = 0;
@@ -6011,7 +6114,7 @@ display_debug_str (struct dwarf_section *section,
       return 0;
     }
 
-  printf (_("Contents of the %s section:\n\n"), section->name);
+  introduce (section, FALSE);
 
   while (bytes)
     {
@@ -6080,7 +6183,7 @@ display_debug_aranges (struct dwarf_section *section,
   unsigned char *start = section->start;
   unsigned char *end = start + section->size;
 
-  printf (_("Contents of the %s section:\n\n"), section->name);
+  introduce (section, FALSE);
 
   /* It does not matter if this load fails,
      we test for that later on.  */
@@ -6239,7 +6342,7 @@ display_debug_addr (struct dwarf_section *section,
       return 0;
     }
 
-  printf (_("Contents of the %s section:\n\n"), section->name);
+  introduce (section, FALSE);
 
   /* PR  17531: file: cf38d01b.
      We use xcalloc because a corrupt file may not have initialised all of the
@@ -6589,7 +6692,8 @@ display_debug_ranges (struct dwarf_section *section,
     warn (_("Range lists in %s section start at 0x%lx\n"),
 	  section->name, (unsigned long) range_entries[0].ranges_offset);
 
-  printf (_("Contents of the %s section:\n\n"), section->name);
+  introduce (section, FALSE);
+
   printf (_("    Offset   Begin    End\n"));
 
   for (i = 0; i < num_range_list; i++)
@@ -7164,7 +7268,7 @@ display_debug_frames (struct dwarf_section *section,
   const char *bad_reg = _("bad register: ");
   unsigned int saved_eh_addr_size = eh_addr_size;
 
-  printf (_("Contents of the %s section:\n"), section->name);
+  introduce (section, FALSE);
 
   while (start < end)
     {
@@ -8105,9 +8209,9 @@ display_debug_names (struct dwarf_section *section, void *file)
   const unsigned char *const section_end = section->start + section->size;
   unsigned char *unit_end;
 
-  printf (_("Contents of the %s section:\n"), section->name);
+  introduce (section, FALSE);
 
-  load_debug_section (str, file);
+  load_debug_section_with_follow (str, file);
 
   for (; hdrptr < section_end; hdrptr = unit_end)
     {
@@ -8446,6 +8550,92 @@ display_debug_names (struct dwarf_section *section, void *file)
 }
 
 static int
+display_debug_links (struct dwarf_section * section,
+		     void * file ATTRIBUTE_UNUSED)
+{
+  const unsigned char * filename;
+  unsigned int          filelen;
+
+  introduce (section, FALSE);
+
+  /* The .gnu_debuglink section is formatted as:
+      (c-string)  Filename.
+      (padding)   If needed to reach a 4 byte boundary.
+      (uint32_t)  CRC32 value.
+
+    The .gun_debugaltlink section is formatted as:
+      (c-string)  Filename.
+      (binary)    Build-ID.  */
+  
+  filename =  section->start;
+  filelen = strnlen ((const char *) filename, section->size);
+  if (filelen == section->size)
+    {
+      warn (_("The debuglink filename is corrupt/missing\n"));
+      return 0;
+    }
+
+  printf (_("  Separate debug info file: %s\n"), filename);
+
+  if (const_strneq (section->name, ".gnu_debuglink"))
+    {
+      unsigned int          crc32;
+      unsigned int          crc_offset;
+
+      crc_offset = filelen + 1;
+      crc_offset = (crc_offset + 3) & ~3;
+      if (crc_offset + 4 > section->size)
+	{
+	  warn (_("CRC offset missing/truncated\n"));
+	  return 0;
+	}
+
+      crc32 = byte_get (filename + crc_offset, 4);
+
+      printf (_("  CRC value: %#x\n"), crc32);
+
+      if (crc_offset + 4 < section->size)
+	{
+	  warn (_("There are %#lx extraneous bytes at the end of the section\n"),
+		(long)(section->size - (crc_offset + 4)));
+	  return 0;
+	}
+    }
+  else /* const_strneq (section->name, ".gnu_debugaltlink") */
+    {
+      const unsigned char * build_id = section->start + filelen + 1;
+      bfd_size_type         build_id_len = section->size - (filelen + 1);
+      bfd_size_type         printed;
+
+      /* FIXME: Should we support smaller build-id notes ?  */
+      if (build_id_len < 0x14)
+	{
+	  warn (_("Build-ID is too short (%#lx bytes)\n"), (long) build_id_len);
+	  return 0;
+	}
+
+      printed = printf (_("  Build-ID (%#lx bytes):"), (long) build_id_len);
+
+      if (do_wide || build_id_len < ((80 - printed) / 3))
+	for (printed = 0; printed < build_id_len; ++printed)
+	  printf (" %02x", build_id[printed]);
+      else
+	{
+	  for (printed = 0; printed < build_id_len; ++printed)
+	    {
+	      if (printed % (80 / 3) == 0)
+		putchar ('\n');
+	      printf (" %02x", build_id[printed]);
+	    }
+	}
+      putchar ('\n');
+    }
+
+  putchar ('\n');
+  return 1;
+}
+
+static int
 display_gdb_index (struct dwarf_section *section,
 		   void *file ATTRIBUTE_UNUSED)
 {
@@ -8461,7 +8651,7 @@ display_gdb_index (struct dwarf_section *section,
 
   /* The documentation for the format of this file is in gdb/dwarf2read.c.  */
 
-  printf (_("Contents of the %s section:\n"), section->name);
+  introduce (section, FALSE);
 
   if (section->size < 6 * sizeof (uint32_t))
     {
@@ -8810,7 +9000,8 @@ process_cu_tu_index (struct dwarf_section *section, int do_display)
 
   if (do_display)
     {
-      printf (_("Contents of the %s section:\n\n"), section->name);
+      introduce (section, FALSE);
+
       printf (_("  Version:                 %d\n"), version);
       if (version >= 2)
 	printf (_("  Number of columns:       %d\n"), ncols);
@@ -9075,11 +9266,11 @@ load_cu_tu_indexes (void *file)
     {
       cu_tu_indexes_read = TRUE;
   
-      if (load_debug_section (dwp_cu_index, file))
+      if (load_debug_section_with_follow (dwp_cu_index, file))
 	if (! process_cu_tu_index (&debug_displays [dwp_cu_index].section, 0))
 	  cu_tu_indexes_read = FALSE;
 
-      if (load_debug_section (dwp_tu_index, file))
+      if (load_debug_section_with_follow (dwp_tu_index, file))
 	if (! process_cu_tu_index (&debug_displays [dwp_tu_index].section, 0))
 	  cu_tu_indexes_read = FALSE;
     }
@@ -9133,6 +9324,7 @@ display_debug_not_supported (struct dwarf_section *section,
 /* Like malloc, but takes two parameters like calloc.
    Verifies that the first parameter is not too large.
    Note: does *not* initialise the allocated memory to zero.  */
+
 void *
 cmalloc (size_t nmemb, size_t size)
 {
@@ -9146,6 +9338,7 @@ cmalloc (size_t nmemb, size_t size)
 /* Like xmalloc, but takes two parameters like calloc.
    Verifies that the first parameter is not too large.
    Note: does *not* initialise the allocated memory to zero.  */
+
 void *
 xcmalloc (size_t nmemb, size_t size)
 {
@@ -9164,15 +9357,15 @@ xcmalloc (size_t nmemb, size_t size)
 /* Like xrealloc, but takes three parameters.
    Verifies that the second parameter is not too large.
    Note: does *not* initialise any new memory to zero.  */
+
 void *
 xcrealloc (void *ptr, size_t nmemb, size_t size)
 {
   /* Check for overflow.  */
   if (nmemb >= ~(size_t) 0 / size)
     {
-      fprintf (stderr,
-	       _("Attempt to re-allocate an array with an excessive number of elements: 0x%lx\n"),
-	       (long) nmemb);
+      error (_("Attempt to re-allocate an array with an excessive number of elements: 0x%lx\n"),
+	     (long) nmemb);
       xexit (1);
     }
 
@@ -9180,21 +9373,397 @@ xcrealloc (void *ptr, size_t nmemb, size_t size)
 }
 
 /* Like xcalloc, but verifies that the first parameter is not too large.  */
+
 void *
 xcalloc2 (size_t nmemb, size_t size)
 {
   /* Check for overflow.  */
   if (nmemb >= ~(size_t) 0 / size)
     {
-      fprintf (stderr,
-	       _("Attempt to allocate a zero'ed array with an excessive number of elements: 0x%lx\n"),
-	       (long) nmemb);
+      error (_("Attempt to allocate a zero'ed array with an excessive number of elements: 0x%lx\n"),
+	     (long) nmemb);
       xexit (1);
     }
 
   return xcalloc (nmemb, size);
 }
 
+static unsigned long
+calc_gnu_debuglink_crc32 (unsigned long          crc,
+			  const unsigned char *  buf,
+			  bfd_size_type          len)
+{
+  static const unsigned long crc32_table[256] =
+    {
+      0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
+      0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
+      0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
+      0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+      0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
+      0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+      0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
+      0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+      0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
+      0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
+      0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
+      0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+      0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
+      0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
+      0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
+      0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+      0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
+      0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+      0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
+      0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+      0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
+      0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
+      0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
+      0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+      0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
+      0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
+      0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
+      0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+      0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
+      0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+      0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
+      0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+      0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
+      0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
+      0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
+      0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+      0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
+      0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
+      0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
+      0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+      0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
+      0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+      0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
+      0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+      0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
+      0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
+      0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
+      0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+      0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
+      0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
+      0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
+      0x2d02ef8d
+    };
+  const unsigned char *end;
+
+  crc = ~crc & 0xffffffff;
+  for (end = buf + len; buf < end; ++ buf)
+    crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
+  return ~crc & 0xffffffff;
+}
+
+typedef bfd_boolean (*   check_func_type) (const char *, void *);
+typedef const char * (*  parse_func_type) (struct dwarf_section *, void *);
+
+static bfd_boolean
+check_gnu_debuglink (const char * pathname, void * crc_pointer)
+{
+  static unsigned char buffer [8 * 1024];
+  FILE *         f;
+  bfd_size_type  count;
+  unsigned long  crc = 0;
+  void *         sep_data;
+
+  sep_data = open_debug_file (pathname);
+  if (sep_data == NULL)
+    return FALSE;
+
+  /* Yes - we are opening the file twice...  */
+  f = fopen (pathname, "rb");
+  if (f == NULL)
+    {
+      /* Paranoia: This should never happen.  */
+      close_debug_file (sep_data);
+      warn (_("Unable to reopen separate debug info file: %s\n"), pathname);
+      return FALSE;
+    }
+
+  while ((count = fread (buffer, 1, sizeof (buffer), f)) > 0)
+    crc = calc_gnu_debuglink_crc32 (crc, buffer, count);
+
+  fclose (f);
+
+  if (crc != * (unsigned long *) crc_pointer)
+    {
+      close_debug_file (sep_data);
+      warn (_("Separate debug info file %s found, but CRC does not match - ignoring\n"),
+	    pathname);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static const char *
+parse_gnu_debuglink (struct dwarf_section * section, void * data)
+{
+  const char *     name;
+  unsigned int     crc_offset;
+  unsigned long *  crc32 = (unsigned long *) data;
+
+  /* The name is first.
+     The CRC value is stored after the filename, aligned up to 4 bytes.  */
+  name = (const char *) section->start;
+
+  crc_offset = strnlen (name, section->size) + 1;
+  crc_offset = (crc_offset + 3) & ~3;
+  if (crc_offset + 4 > section->size)
+    return NULL;
+
+  * crc32 = byte_get (section->start + crc_offset, 4);
+  return name;
+}
+
+static bfd_boolean
+check_gnu_debugaltlink (const char * filename, void * data ATTRIBUTE_UNUSED)
+{
+  void * sep_data = open_debug_file (filename);
+
+  if (sep_data == NULL)
+    return FALSE;
+
+  /* FIXME: We should now extract the build-id in the separate file
+     and check it...  */
+
+  return TRUE;
+}
+
+typedef struct build_id_data
+{
+  bfd_size_type          len;
+  const unsigned char *  data;
+} Build_id_data;
+
+static const char *
+parse_gnu_debugaltlink (struct dwarf_section * section, void * data)
+{
+  const char *     name;
+  bfd_size_type    namelen;
+  bfd_size_type    id_len;
+  Build_id_data *  build_id_data;
+
+  /* The name is first.
+     The build-id follows immediately, with no padding, up to the section's end.  */
+
+  name = (const char *) section->start;
+  namelen = strnlen (name, section->size) + 1;
+  if (namelen >= section->size)
+    return NULL;
+
+  id_len = section->size - namelen;
+  if (id_len < 0x14)
+    return NULL;
+
+  build_id_data = calloc (1, sizeof * build_id_data);
+  if (build_id_data == NULL)
+    return NULL;
+
+  build_id_data->len = id_len;
+  build_id_data->data = section->start + namelen;
+
+  * (Build_id_data **) data = build_id_data;
+
+  return name;
+}
+
+static void *
+load_separate_debug_info (const char *            main_filename,
+			  struct dwarf_section *  link,
+			  parse_func_type         parse_func,
+			  check_func_type         check_func,
+			  void *                  func_data)
+{
+  const char *   separate_filename;
+  char *         debugfile;
+  char *         canon_dir;
+  size_t         canon_dirlen;
+  size_t         dirlen;
+
+  if ((separate_filename = parse_func (link, func_data)) == NULL)
+    {
+      warn (_("Corrupt debuglink section: %s\n"),
+	    link->name ? link->name : link->uncompressed_name);
+      return FALSE;
+    }
+    
+  /* Attempt to locate the separate file.
+     This should duplicate the logic in bfd/opncls.c:find_separate_debug_file().  */
+
+  canon_dir = lrealpath (main_filename);
+  
+  for (canon_dirlen = strlen (canon_dir); canon_dirlen > 0; canon_dirlen--)
+    if (IS_DIR_SEPARATOR (canon_dir[canon_dirlen - 1]))
+      break;
+  canon_dir[canon_dirlen] = '\0';
+
+#ifndef DEBUGDIR
+#define DEBUGDIR "/lib/debug"
+#endif
+#ifndef EXTRA_DEBUG_ROOT1
+#define EXTRA_DEBUG_ROOT1 "/usr/lib/debug"
+#endif
+#ifndef EXTRA_DEBUG_ROOT2
+#define EXTRA_DEBUG_ROOT2 "/usr/lib/debug/usr"
+#endif
+
+  debugfile = (char *) malloc (strlen (DEBUGDIR) + 1
+			       + canon_dirlen
+			       + strlen (".debug/")
+#ifdef EXTRA_DEBUG_ROOT1
+			       + strlen (EXTRA_DEBUG_ROOT1)
+#endif
+#ifdef EXTRA_DEBUG_ROOT2
+			       + strlen (EXTRA_DEBUG_ROOT2)
+#endif
+			       + strlen (separate_filename)
+			       + 1);
+  if (debugfile == NULL)
+    {
+      warn (_("Out of memory"));
+      return NULL;
+    }
+
+  /* First try in the current directory.  */
+  sprintf (debugfile, "%s", separate_filename);
+  if (check_func (debugfile, func_data))
+    goto found;
+
+  /* Then try in a subdirectory called .debug.  */
+  sprintf (debugfile, ".debug/%s", separate_filename);
+  if (check_func (debugfile, func_data))
+    goto found;
+
+  /* Then try in the same directory as the original file.  */
+  sprintf (debugfile, "%s%s", canon_dir, separate_filename);
+  if (check_func (debugfile, func_data))
+    goto found;
+
+  /* And the .debug subdirectory of that directory.  */
+  sprintf (debugfile, "%s.debug/%s", canon_dir, separate_filename);
+  if (check_func (debugfile, func_data))
+    goto found;
+
+#ifdef EXTRA_DEBUG_ROOT1
+  /* Try the first extra debug file root.  */
+  sprintf (debugfile, "%s/%s", EXTRA_DEBUG_ROOT1, separate_filename);
+  if (check_func (debugfile, func_data))
+    goto found;
+#endif
+
+#ifdef EXTRA_DEBUG_ROOT2
+  /* Try the second extra debug file root.  */
+  sprintf (debugfile, "%s/%s", EXTRA_DEBUG_ROOT2, separate_filename);
+  if (check_func (debugfile, func_data))
+    goto found;
+#endif
+
+  /* Then try in the global debugfile directory.  */
+  strcpy (debugfile, DEBUGDIR);
+  dirlen = strlen (DEBUGDIR) - 1;
+  if (dirlen > 0 && DEBUGDIR[dirlen] != '/')
+    strcat (debugfile, "/");
+  strcat (debugfile, (const char *) separate_filename);
+
+  if (check_func (debugfile, func_data))
+    goto found;
+
+  /* Failed to find the file.  */
+  warn (_("could not find separate debug file '%s'\n"), separate_filename);
+  warn (_("tried: %s\n"), debugfile);
+
+#ifdef EXTRA_DEBUG_ROOT2
+  sprintf (debugfile, "%s/%s", EXTRA_DEBUG_ROOT2, separate_filename);
+  warn (_("tried: %s\n"), debugfile);
+#endif
+
+#ifdef EXTRA_DEBUG_ROOT1
+  sprintf (debugfile, "%s/%s", EXTRA_DEBUG_ROOT1, separate_filename);
+  warn (_("tried: %s\n"), debugfile);
+#endif
+
+  sprintf (debugfile, "%s.debug/%s", canon_dir, separate_filename);
+  warn (_("tried: %s\n"), debugfile);
+
+  sprintf (debugfile, "%s%s", canon_dir, separate_filename);
+  warn (_("tried: %s\n"), debugfile);
+
+  sprintf (debugfile, ".debug/%s", separate_filename);
+  warn (_("tried: %s\n"), debugfile);
+
+  sprintf (debugfile, "%s", separate_filename);
+  warn (_("tried: %s\n"), debugfile);
+
+  free (canon_dir);
+  free (debugfile);
+  return NULL;
+
+ found:
+  free (canon_dir);
+
+  /* Now open the file.... */
+  if ((separate_debug_file = open_debug_file (debugfile)) == NULL)
+    {
+      warn (_("failed to open separate debug file: %s\n"), debugfile);
+      free (debugfile);
+      return FALSE;
+    }
+
+  /* FIXME: We do not check to see if there are any other separate debug info
+     files that would also match.  */
+
+  printf (_("%s: Found separate debug info file: %s\n\n"), main_filename, debugfile);
+  separate_debug_filename = debugfile;
+
+  /* Do not free debugfile - it might be referenced inside
+     the structure returned by open_debug_file().  */
+  return separate_debug_file;
+}
+
+/* Load a separate debug info file, if it exists.
+   Returns the data pointer that is the result of calling open_debug_file
+   on the separate debug info file.  */
+
+void *
+load_separate_debug_file (void * file, const char * filename)
+{
+  if (! do_follow_links)
+    return NULL;
+
+  /* FIXME: We do not check for the presence of both link sections in the same file.  */
+  /* FIXME: We do not check the separate debug info file to see if it too contains debuglinks.  */
+  /* FIXME: We do not check for the presence of multiple, same-name debuglink sections.  */
+
+  /* We try the alt version first as that is blessed by the DWARF5 standard.  */
+  if (load_debug_section (gnu_debugaltlink, file))
+    {
+      Build_id_data * build_id_data;
+
+      return load_separate_debug_info (filename,
+				       & debug_displays[gnu_debugaltlink].section,
+				       parse_gnu_debugaltlink,
+				       check_gnu_debugaltlink,
+				       & build_id_data);
+    }
+
+  if (load_debug_section (gnu_debuglink, file))
+    {
+      unsigned long crc32;
+
+      return load_separate_debug_info (filename,
+				       & debug_displays[gnu_debuglink].section,
+				       parse_gnu_debuglink,
+				       check_gnu_debuglink,
+				       & crc32);
+    }
+
+  do_follow_links = 0;
+  return NULL;
+}  
+
 void
 free_debug_memory (void)
 {
@@ -9224,6 +9793,15 @@ free_debug_memory (void)
       debug_information = NULL;
       alloc_num_debug_info_entries = num_debug_info_entries = 0;
     }
+
+  if (separate_debug_file != NULL)
+    {
+      close_debug_file (separate_debug_file);
+      separate_debug_file = NULL;
+
+      free ((void *) separate_debug_filename);
+      separate_debug_filename = NULL;
+    }
 }
 
 void
@@ -9246,12 +9824,14 @@ dwarf_select_sections_by_names (const char *names)
       { "aranges", & do_debug_aranges, 1 },
       { "cu_index", & do_debug_cu_index, 1 },
       { "decodedline", & do_debug_lines, FLAG_DEBUG_LINES_DECODED },
+      { "follow-links", & do_follow_links, 1 },
       { "frames", & do_debug_frames, 1 },
       { "frames-interp", & do_debug_frames_interp, 1 },
       /* The special .gdb_index section.  */
       { "gdb_index", & do_gdb_index, 1 },
       { "info", & do_debug_info, 1 },
       { "line", & do_debug_lines, FLAG_DEBUG_LINES_RAW }, /* For backwards compatibility.  */
+      { "links", & do_debug_links, 1 },
       { "loc",  & do_debug_loc, 1 },
       { "macro", & do_debug_macinfo, 1 },
       { "pubnames", & do_debug_pubnames, 1 },
@@ -9315,56 +9895,27 @@ dwarf_select_sections_by_letters (const char *letters)
   while (letters[lindex])
     switch (letters[lindex++])
       {
-      case 'i':
-	do_debug_info = 1;
-	break;
-
-      case 'a':
-	do_debug_abbrevs = 1;
-	break;
-
-      case 'l':
-	do_debug_lines |= FLAG_DEBUG_LINES_RAW;
-	break;
-
-      case 'L':
-	do_debug_lines |= FLAG_DEBUG_LINES_DECODED;
-	break;
-
-      case 'p':
-	do_debug_pubnames = 1;
-	break;
-
-      case 't':
-	do_debug_pubtypes = 1;
-	break;
-
-      case 'r':
-	do_debug_aranges = 1;
-	break;
-
-      case 'R':
-	do_debug_ranges = 1;
-	break;
-
-      case 'F':
-	do_debug_frames_interp = 1;
-	/* Fall through.  */
-      case 'f':
-	do_debug_frames = 1;
-	break;
-
-      case 'm':
-	do_debug_macinfo = 1;
-	break;
-
-      case 's':
-	do_debug_str = 1;
-	break;
-
-      case 'o':
-	do_debug_loc = 1;
-	break;
+      case 'A':	do_debug_addr = 1; break;
+      case 'a':	do_debug_abbrevs = 1; break;
+      case 'c':	do_debug_cu_index = 1; break;
+      case 'F':	do_debug_frames_interp = 1; /* Fall through.  */
+      case 'f':	do_debug_frames = 1; break;
+      case 'g':	do_gdb_index = 1; break;
+      case 'i':	do_debug_info = 1; break;
+      case 'K': do_follow_links = 1; break;
+      case 'k':	do_debug_links = 1; break;
+      case 'l':	do_debug_lines |= FLAG_DEBUG_LINES_RAW;	break;
+      case 'L':	do_debug_lines |= FLAG_DEBUG_LINES_DECODED; break;
+      case 'm': do_debug_macinfo = 1; break;
+      case 'o':	do_debug_loc = 1; break;
+      case 'p':	do_debug_pubnames = 1; break;
+      case 'R':	do_debug_ranges = 1; break;
+      case 'r':	do_debug_aranges = 1; break;
+      case 's':	do_debug_str = 1; break;
+      case 'T': do_trace_aranges = 1; break;
+      case 't': do_debug_pubtypes = 1; break;
+      case 'U': do_trace_info = 1; break;
+      case 'u': do_trace_abbrevs = 1; break;
 
       default:
 	warn (_("Unrecognized debug option '%s'\n"), letters);
@@ -9392,90 +9943,63 @@ dwarf_select_sections_all (void)
   do_trace_aranges = 1;
   do_debug_addr = 1;
   do_debug_cu_index = 1;
+  do_follow_links = 1;
+  do_debug_links = 1;
 }
 
+#define NO_ABBREVS   NULL, NULL, NULL, 0, 0, 0, NULL, 0, NULL
+#define ABBREV(N)    NULL, NULL, NULL, 0, 0, N, NULL, 0, NULL
+
+/* N.B. The order here must match the order in section_display_enum.  */
+
 struct dwarf_section_display debug_displays[] =
 {
-  { { ".debug_abbrev",	    ".zdebug_abbrev",	NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_abbrev,   &do_debug_abbrevs,	FALSE },
-  { { ".debug_aranges",	    ".zdebug_aranges",	NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_aranges,  &do_debug_aranges,	TRUE },
-  { { ".debug_frame",       ".zdebug_frame",	NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_frames,   &do_debug_frames,	TRUE },
-  { { ".debug_info",	    ".zdebug_info",	NULL, NULL, 0, 0, abbrev, NULL, 0, NULL },
-    display_debug_info,	    &do_debug_info,	TRUE },
-  { { ".debug_line",	    ".zdebug_line",	NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_lines,    &do_debug_lines,	TRUE },
-  { { ".debug_pubnames",    ".zdebug_pubnames",	NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_pubnames, &do_debug_pubnames,	FALSE },
-  { { ".debug_gnu_pubnames", ".zdebug_gnu_pubnames", NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_gnu_pubnames, &do_debug_pubnames, FALSE },
-  { { ".eh_frame",	    "",			NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_frames,   &do_debug_frames,	TRUE },
-  { { ".debug_macinfo",	    ".zdebug_macinfo",	NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_macinfo,  &do_debug_macinfo,	FALSE },
-  { { ".debug_macro",	    ".zdebug_macro",	NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_macro,    &do_debug_macinfo,	TRUE },
-  { { ".debug_str",	    ".zdebug_str",	NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_str,	    &do_debug_str,	FALSE },
-  { { ".debug_line_str",    ".zdebug_line_str",	NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_str,	    &do_debug_str,	FALSE },
-  { { ".debug_loc",	    ".zdebug_loc",	NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_loc,	    &do_debug_loc,	TRUE },
-  { { ".debug_loclists",    ".zdebug_loclists",	NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_loc,	    &do_debug_loc,	TRUE },
-  { { ".debug_pubtypes",    ".zdebug_pubtypes",	NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_pubnames, &do_debug_pubtypes,	FALSE },
-  { { ".debug_gnu_pubtypes", ".zdebug_gnu_pubtypes", NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_gnu_pubnames, &do_debug_pubtypes, FALSE },
-  { { ".debug_ranges",	    ".zdebug_ranges",	NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_ranges,   &do_debug_ranges,	TRUE },
-  { { ".debug_rnglists",    ".zdebug_rnglists",	NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_ranges,   &do_debug_ranges,	TRUE },
-  { { ".debug_static_func", ".zdebug_static_func", NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_not_supported, NULL,		FALSE },
-  { { ".debug_static_vars", ".zdebug_static_vars", NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_not_supported, NULL,		FALSE },
-  { { ".debug_types",	    ".zdebug_types",	NULL, NULL, 0, 0, abbrev, NULL, 0, NULL },
-    display_debug_types,    &do_debug_info,	TRUE },
-  { { ".debug_weaknames",   ".zdebug_weaknames", NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_not_supported, NULL,		FALSE },
-  { { ".gdb_index",	    "",			NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_gdb_index,      &do_gdb_index,	FALSE },
-  { { ".debug_names",	    "",			NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_names,    &do_gdb_index,	FALSE },
-  { { ".trace_info",	    "",			NULL, NULL, 0, 0, trace_abbrev, NULL, 0, NULL },
-    display_trace_info,	    &do_trace_info,	TRUE },
-  { { ".trace_abbrev",	    "",			NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_abbrev,   &do_trace_abbrevs,	FALSE },
-  { { ".trace_aranges",	    "",			NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_aranges,  &do_trace_aranges,	FALSE },
-  { { ".debug_info.dwo",    ".zdebug_info.dwo",	NULL, NULL, 0, 0, abbrev_dwo, NULL, 0, NULL },
-    display_debug_info,	    &do_debug_info,	TRUE },
-  { { ".debug_abbrev.dwo",  ".zdebug_abbrev.dwo", NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_abbrev,   &do_debug_abbrevs,	FALSE },
-  { { ".debug_types.dwo",   ".zdebug_types.dwo", NULL, NULL, 0, 0, abbrev_dwo, NULL, 0, NULL },
-    display_debug_types,    &do_debug_info,	TRUE },
-  { { ".debug_line.dwo",    ".zdebug_line.dwo", NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_lines,    &do_debug_lines,	TRUE },
-  { { ".debug_loc.dwo",	    ".zdebug_loc.dwo",	NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_loc,	    &do_debug_loc,	TRUE },
-  { { ".debug_macro.dwo",   ".zdebug_macro.dwo", NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_macro,    &do_debug_macinfo,	TRUE },
-  { { ".debug_macinfo.dwo", ".zdebug_macinfo.dwo", NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_macinfo,  &do_debug_macinfo,	FALSE },
-  { { ".debug_str.dwo",     ".zdebug_str.dwo",  NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_str,      &do_debug_str,	TRUE },
-  { { ".debug_str_offsets", ".zdebug_str_offsets", NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_str_offsets, NULL,		FALSE },
-  { { ".debug_str_offsets.dwo", ".zdebug_str_offsets.dwo", NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_str_offsets, NULL,		FALSE },
-  { { ".debug_addr",	    ".zdebug_addr",     NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_debug_addr,     &do_debug_addr,	TRUE },
-  { { ".debug_cu_index",    "",			NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_cu_index,       &do_debug_cu_index,	FALSE },
-  { { ".debug_tu_index",    "",			NULL, NULL, 0, 0, 0, NULL, 0, NULL },
-    display_cu_index,       &do_debug_cu_index,	FALSE },
+  { { ".debug_abbrev",	    ".zdebug_abbrev",	NO_ABBREVS },      display_debug_abbrev,   &do_debug_abbrevs,	FALSE },
+  { { ".debug_aranges",	    ".zdebug_aranges",	NO_ABBREVS },      display_debug_aranges,  &do_debug_aranges,	TRUE },
+  { { ".debug_frame",       ".zdebug_frame",	NO_ABBREVS },      display_debug_frames,   &do_debug_frames,	TRUE },
+  { { ".debug_info",	    ".zdebug_info",	ABBREV (abbrev)},  display_debug_info,	   &do_debug_info,	TRUE },
+  { { ".debug_line",	    ".zdebug_line",	NO_ABBREVS },      display_debug_lines,    &do_debug_lines,	TRUE },
+  { { ".debug_pubnames",    ".zdebug_pubnames",	NO_ABBREVS },      display_debug_pubnames, &do_debug_pubnames,	FALSE },
+  { { ".debug_gnu_pubnames", ".zdebug_gnu_pubnames", NO_ABBREVS }, display_debug_gnu_pubnames, &do_debug_pubnames, FALSE },
+  { { ".eh_frame",	    "",			NO_ABBREVS },      display_debug_frames,   &do_debug_frames,	TRUE },
+  { { ".debug_macinfo",	    ".zdebug_macinfo",	NO_ABBREVS },      display_debug_macinfo,  &do_debug_macinfo,	FALSE },
+  { { ".debug_macro",	    ".zdebug_macro",	NO_ABBREVS },      display_debug_macro,    &do_debug_macinfo,	TRUE },
+  { { ".debug_str",	    ".zdebug_str",	NO_ABBREVS },      display_debug_str,	   &do_debug_str,	FALSE },
+  { { ".debug_line_str",    ".zdebug_line_str",	NO_ABBREVS },      display_debug_str,	   &do_debug_str,	FALSE },
+  { { ".debug_loc",	    ".zdebug_loc",	NO_ABBREVS },      display_debug_loc,	   &do_debug_loc,	TRUE },
+  { { ".debug_loclists",    ".zdebug_loclists",	NO_ABBREVS },      display_debug_loc,	   &do_debug_loc,	TRUE },
+  { { ".debug_pubtypes",    ".zdebug_pubtypes",	NO_ABBREVS },      display_debug_pubnames, &do_debug_pubtypes,	FALSE },
+  { { ".debug_gnu_pubtypes", ".zdebug_gnu_pubtypes", NO_ABBREVS }, display_debug_gnu_pubnames, &do_debug_pubtypes, FALSE },
+  { { ".debug_ranges",	    ".zdebug_ranges",	NO_ABBREVS },      display_debug_ranges,   &do_debug_ranges,	TRUE },
+  { { ".debug_rnglists",    ".zdebug_rnglists",	NO_ABBREVS },      display_debug_ranges,   &do_debug_ranges,	TRUE },
+  { { ".debug_static_func", ".zdebug_static_func", NO_ABBREVS },   display_debug_not_supported, NULL,		FALSE },
+  { { ".debug_static_vars", ".zdebug_static_vars", NO_ABBREVS },   display_debug_not_supported, NULL,		FALSE },
+  { { ".debug_types",	    ".zdebug_types",	ABBREV (abbrev) }, display_debug_types,    &do_debug_info,	TRUE },
+  { { ".debug_weaknames",   ".zdebug_weaknames", NO_ABBREVS },     display_debug_not_supported, NULL,		FALSE },
+  { { ".gdb_index",	    "",			NO_ABBREVS },      display_gdb_index,      &do_gdb_index,	FALSE },
+  { { ".debug_names",	    "",			NO_ABBREVS },      display_debug_names,    &do_gdb_index,	FALSE },
+  { { ".trace_info",	    "",			ABBREV (trace_abbrev) }, display_trace_info, &do_trace_info,	TRUE },
+  { { ".trace_abbrev",	    "",			NO_ABBREVS },      display_debug_abbrev,   &do_trace_abbrevs,	FALSE },
+  { { ".trace_aranges",	    "",			NO_ABBREVS },      display_debug_aranges,  &do_trace_aranges,	FALSE },
+  { { ".debug_info.dwo",    ".zdebug_info.dwo",	ABBREV (abbrev_dwo) }, display_debug_info, &do_debug_info,	TRUE },
+  { { ".debug_abbrev.dwo",  ".zdebug_abbrev.dwo", NO_ABBREVS },    display_debug_abbrev,   &do_debug_abbrevs,	FALSE },
+  { { ".debug_types.dwo",   ".zdebug_types.dwo", ABBREV (abbrev_dwo) }, display_debug_types, &do_debug_info,	TRUE },
+  { { ".debug_line.dwo",    ".zdebug_line.dwo", NO_ABBREVS },      display_debug_lines,    &do_debug_lines,	TRUE },
+  { { ".debug_loc.dwo",	    ".zdebug_loc.dwo",	NO_ABBREVS },      display_debug_loc,	   &do_debug_loc,	TRUE },
+  { { ".debug_macro.dwo",   ".zdebug_macro.dwo", NO_ABBREVS },     display_debug_macro,    &do_debug_macinfo,	TRUE },
+  { { ".debug_macinfo.dwo", ".zdebug_macinfo.dwo", NO_ABBREVS },   display_debug_macinfo,  &do_debug_macinfo,	FALSE },
+  { { ".debug_str.dwo",     ".zdebug_str.dwo",  NO_ABBREVS },      display_debug_str,      &do_debug_str,	TRUE },
+  { { ".debug_str_offsets", ".zdebug_str_offsets", NO_ABBREVS },   display_debug_str_offsets, NULL,		FALSE },
+  { { ".debug_str_offsets.dwo", ".zdebug_str_offsets.dwo", NO_ABBREVS }, display_debug_str_offsets, NULL,	FALSE },
+  { { ".debug_addr",	    ".zdebug_addr",     NO_ABBREVS },      display_debug_addr,     &do_debug_addr,	TRUE },
+  { { ".debug_cu_index",    "",			NO_ABBREVS },      display_cu_index,       &do_debug_cu_index,	FALSE },
+  { { ".debug_tu_index",    "",			NO_ABBREVS },      display_cu_index,       &do_debug_cu_index,	FALSE },
+  { { ".gnu_debuglink",     "",                 NO_ABBREVS },      display_debug_links,    &do_debug_links,     FALSE },
+  { { ".gnu_debugaltlink",  "",                 NO_ABBREVS },      display_debug_links,    &do_debug_links,     FALSE },
+  /* Separate debug info files can containt their own .debug_str section,
+     and this might be in *addition* to a .debug_str section already present
+     in the main file.  Hence we need to have two entries for .debug_str.  */
+  { { ".debug_str",	    ".zdebug_str",	NO_ABBREVS },      display_debug_str,	   &do_debug_str,	FALSE },
 };
 
 /* A static assertion.  */
diff --git a/binutils/dwarf.h b/binutils/dwarf.h
index 15719ee..68b9425 100644
--- a/binutils/dwarf.h
+++ b/binutils/dwarf.h
@@ -116,6 +116,9 @@ enum dwarf_section_display_enum
   debug_addr,
   dwp_cu_index,
   dwp_tu_index,
+  gnu_debuglink,
+  gnu_debugaltlink,
+  separate_debug_str,
   max
 };
 
@@ -125,30 +128,31 @@ struct dwarf_section
      or not.  COMPRESSED_NAME and UNCOMPRESSED_NAME are the two
      possibilities.  NAME is set to whichever one is used for this
      input file, as determined by load_debug_section().  */
-  const char *uncompressed_name;
-  const char *compressed_name;
-  const char *name;
-  unsigned char *start;
-  dwarf_vma address;
-  dwarf_size_type size;
-  enum dwarf_section_display_enum abbrev_sec;
-
+  const char *                     uncompressed_name;
+  const char *                     compressed_name;
+  const char *                     name;
+  /* If non-NULL then FILENAME is the name of the separate debug info
+     file containing the section.  */
+  const char *                     filename;
+  unsigned char *                  start;
+  dwarf_vma                        address;
+  dwarf_size_type                  size;
+  enum dwarf_section_display_enum  abbrev_sec;
   /* Used by clients to help them implement the reloc_at callback.  */
-  void * reloc_info;
-  unsigned long num_relocs;
-
+  void *                           reloc_info;
+  unsigned long                    num_relocs;
   /* A spare field for random use.  */
-  void *user_data;
+  void *                           user_data;
 };
 
 /* A structure containing the name of a debug section
    and a pointer to a function that can decode it.  */
 struct dwarf_section_display
 {
-  struct dwarf_section section;
-  int (*display) (struct dwarf_section *, void *);
-  int *enabled;
-  bfd_boolean relocate;
+  struct dwarf_section  section;
+  int (*                display) (struct dwarf_section *, void *);
+  int *                 enabled;
+  bfd_boolean           relocate;
 };
 
 extern struct dwarf_section_display debug_displays [];
@@ -204,6 +208,7 @@ extern int do_trace_aranges;
 extern int do_debug_addr;
 extern int do_debug_cu_index;
 extern int do_wide;
+extern int do_debug_links;
 
 extern int dwarf_cutoff_level;
 extern unsigned long dwarf_start_die;
@@ -217,8 +222,11 @@ extern void init_dwarf_regnames_x86_64 (void);
 extern void init_dwarf_regnames_aarch64 (void);
 extern void init_dwarf_regnames_s390 (void);
 
-extern int load_debug_section (enum dwarf_section_display_enum, void *);
-extern void free_debug_section (enum dwarf_section_display_enum);
+extern bfd_boolean  load_debug_section (enum dwarf_section_display_enum, void *);
+extern void         free_debug_section (enum dwarf_section_display_enum);
+extern void *       load_separate_debug_file (void *, const char *);
+extern void         close_debug_file (void *);
+extern void *       open_debug_file (const char *);
 
 extern void free_debug_memory (void);
 
diff --git a/binutils/elfcomm.c b/binutils/elfcomm.c
index 80f3fd8..dd6acd4 100644
--- a/binutils/elfcomm.c
+++ b/binutils/elfcomm.c
@@ -125,10 +125,10 @@ byte_put_big_endian (unsigned char * field, elf_vma value, int size)
     }
 }
 
-elf_vma (*byte_get) (unsigned char *, int);
+elf_vma (*byte_get) (const unsigned char *, int);
 
 elf_vma
-byte_get_little_endian (unsigned char *field, int size)
+byte_get_little_endian (const unsigned char *field, int size)
 {
   switch (size)
     {
@@ -231,7 +231,7 @@ byte_get_little_endian (unsigned char *field, int size)
 }
 
 elf_vma
-byte_get_big_endian (unsigned char *field, int size)
+byte_get_big_endian (const unsigned char *field, int size)
 {
   switch (size)
     {
@@ -341,7 +341,7 @@ byte_get_big_endian (unsigned char *field, int size)
 }
 
 elf_vma
-byte_get_signed (unsigned char *field, int size)
+byte_get_signed (const unsigned char *field, int size)
 {
   elf_vma x = byte_get (field, size);
 
@@ -373,7 +373,7 @@ byte_get_signed (unsigned char *field, int size)
    of an 8-byte value separately.  */
 
 void
-byte_get_64 (unsigned char *field, elf_vma *high, elf_vma *low)
+byte_get_64 (const unsigned char *field, elf_vma *high, elf_vma *low)
 {
   if (byte_get == byte_get_big_endian)
     {
diff --git a/binutils/elfcomm.h b/binutils/elfcomm.h
index 52fee43..c7bc44b 100644
--- a/binutils/elfcomm.h
+++ b/binutils/elfcomm.h
@@ -35,11 +35,11 @@ extern void (*byte_put) (unsigned char *, elf_vma, int);
 extern void byte_put_little_endian (unsigned char *, elf_vma, int);
 extern void byte_put_big_endian (unsigned char *, elf_vma, int);
 
-extern elf_vma (*byte_get) (unsigned char *, int);
-extern elf_vma byte_get_signed (unsigned char *, int);
-extern elf_vma byte_get_little_endian (unsigned char *, int);
-extern elf_vma byte_get_big_endian (unsigned char *, int);
-extern void byte_get_64 (unsigned char *, elf_vma *, elf_vma *);
+extern elf_vma (*byte_get) (const unsigned char *, int);
+extern elf_vma byte_get_signed (const unsigned char *, int);
+extern elf_vma byte_get_little_endian (const unsigned char *, int);
+extern elf_vma byte_get_big_endian (const unsigned char *, int);
+extern void byte_get_64 (const unsigned char *, elf_vma *, elf_vma *);
 
 #define BYTE_PUT(field, val)	byte_put (field, val, sizeof (field))
 #define BYTE_GET(field)		byte_get (field, sizeof (field))
diff --git a/binutils/objdump.c b/binutils/objdump.c
index 98c316a..1a1e32f 100644
--- a/binutils/objdump.c
+++ b/binutils/objdump.c
@@ -214,11 +214,11 @@ usage (FILE *stream, int status)
   -g, --debugging          Display debug information in object file\n\
   -e, --debugging-tags     Display debug information using ctags style\n\
   -G, --stabs              Display (in raw form) any STABS info in the file\n\
-  -W[lLiaprmfFsoRt] or\n\
+  -W[lLiaprmfFsoRtUuTgAckK] or\n\
   --dwarf[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,\n\
           =frames-interp,=str,=loc,=Ranges,=pubtypes,\n\
           =gdb_index,=trace_info,=trace_abbrev,=trace_aranges,\n\
-          =addr,=cu_index]\n\
+          =addr,=cu_index,=links,=follow-links]\n\
                            Display DWARF info in the file\n\
   -t, --syms               Display the contents of the symbol table(s)\n\
   -T, --dynamic-syms       Display the contents of the dynamic symbol table\n\
@@ -348,10 +348,10 @@ static struct option long_options[]=
   {"prefix", required_argument, NULL, OPTION_PREFIX},
   {"prefix-strip", required_argument, NULL, OPTION_PREFIX_STRIP},
   {"insn-width", required_argument, NULL, OPTION_INSN_WIDTH},
-  {"dwarf-depth",      required_argument, 0, OPTION_DWARF_DEPTH},
-  {"dwarf-start",      required_argument, 0, OPTION_DWARF_START},
-  {"dwarf-check",      no_argument, 0, OPTION_DWARF_CHECK},
-  {"inlines",          no_argument, 0, OPTION_INLINES},
+  {"dwarf-depth", required_argument, 0, OPTION_DWARF_DEPTH},
+  {"dwarf-start", required_argument, 0, OPTION_DWARF_START},
+  {"dwarf-check", no_argument, 0, OPTION_DWARF_CHECK},
+  {"inlines", no_argument, 0, OPTION_INLINES},
   {0, no_argument, 0, 0}
 };
 
@@ -2459,19 +2459,23 @@ disassemble_data (bfd *abfd)
   free (sorted_syms);
 }
 
-static int
+static bfd_boolean
 load_specific_debug_section (enum dwarf_section_display_enum debug,
 			     asection *sec, void *file)
 {
   struct dwarf_section *section = &debug_displays [debug].section;
   bfd *abfd = (bfd *) file;
   bfd_byte *contents;
-  bfd_boolean ret;
 
-  /* If it is already loaded, do nothing.  */
   if (section->start != NULL)
-    return 1;
+    {
+      /* If it is already loaded, do nothing.  */
+      if (streq (section->filename, bfd_get_filename (abfd)))
+	return TRUE;
+      free (section->start);
+    }
 
+  section->filename = bfd_get_filename (abfd);
   section->reloc_info = NULL;
   section->num_relocs = 0;
   section->address = bfd_get_section_vma (abfd, sec);
@@ -2484,13 +2488,16 @@ load_specific_debug_section (enum dwarf_section_display_enum debug,
       free_debug_section (debug);
       printf (_("\nCan't get contents for section '%s'.\n"),
 	      section->name);
-      return 0;
+      return FALSE;
     }
   /* Ensure any string section has a terminating NUL.  */
   section->start[section->size] = 0;
 
   if (is_relocatable && debug_displays [debug].relocate)
     {
+      long         reloc_size;
+      bfd_boolean  ret;
+
       bfd_cache_section_contents (sec, section->start);
 
       ret = bfd_simple_get_relocated_section_contents (abfd,
@@ -2503,11 +2510,9 @@ load_specific_debug_section (enum dwarf_section_display_enum debug,
           free_debug_section (debug);
           printf (_("\nCan't get contents for section '%s'.\n"),
 	          section->name);
-          return 0;
+          return FALSE;
         }
 
-      long reloc_size;
-
       reloc_size = bfd_get_reloc_upper_bound (abfd, sec);
       if (reloc_size > 0)
 	{
@@ -2527,7 +2532,7 @@ load_specific_debug_section (enum dwarf_section_display_enum debug,
 	}
     }
 
-  return 1;
+  return TRUE;
 }
 
 bfd_boolean
@@ -2548,7 +2553,7 @@ reloc_at (struct dwarf_section * dsec, dwarf_vma offset)
   return FALSE;
 }
 
-int
+bfd_boolean
 load_debug_section (enum dwarf_section_display_enum debug, void *file)
 {
   struct dwarf_section *section = &debug_displays [debug].section;
@@ -2557,7 +2562,10 @@ load_debug_section (enum dwarf_section_display_enum debug, void *file)
 
   /* If it is already loaded, do nothing.  */
   if (section->start != NULL)
-    return 1;
+    {
+      if (streq (section->filename, bfd_get_filename (abfd)))
+	return TRUE;
+    }
 
   /* Locate the debug section.  */
   sec = bfd_get_section_by_name (abfd, section->uncompressed_name);
@@ -2570,7 +2578,7 @@ load_debug_section (enum dwarf_section_display_enum debug, void *file)
         section->name = section->compressed_name;
     }
   if (sec == NULL)
-    return 0;
+    return FALSE;
 
   return load_specific_debug_section (debug, sec, file);
 }
@@ -2606,6 +2614,29 @@ free_debug_section (enum dwarf_section_display_enum debug)
   section->size = 0;
 }
 
+void
+close_debug_file (void * file)
+{
+  bfd * abfd = (bfd *) file;
+
+  bfd_close (abfd);
+}
+
+void *
+open_debug_file (const char * pathname)
+{
+  bfd * data;
+
+  data = bfd_openr (pathname, NULL);
+  if (data == NULL)
+    return NULL;
+
+  if (! bfd_check_format (data, bfd_object))
+    return NULL;
+  
+  return data;
+}
+
 static void
 dump_dwarf_section (bfd *abfd, asection *section,
 		    void *arg ATTRIBUTE_UNUSED)
@@ -2648,6 +2679,8 @@ dump_dwarf_section (bfd *abfd, asection *section,
 static void
 dump_dwarf (bfd *abfd)
 {
+  bfd * separates;
+
   is_relocatable = (abfd->flags & (EXEC_P | DYNAMIC)) == 0;
 
   eh_addr_size = bfd_arch_bits_per_address (abfd) / 8;
@@ -2700,8 +2733,13 @@ dump_dwarf (bfd *abfd)
       break;
     }
 
+  separates = load_separate_debug_file (abfd, bfd_get_filename (abfd));
+
   bfd_map_over_sections (abfd, dump_dwarf_section, NULL);
 
+  if (separates)
+    bfd_map_over_sections (separates, dump_dwarf_section, NULL);
+
   free_debug_memory ();
 }
 
diff --git a/binutils/readelf.c b/binutils/readelf.c
index 5e7cbef..b7f1e09 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -169,21 +169,55 @@
 
 typedef struct elf_section_list
 {
-  Elf_Internal_Shdr * hdr;
-  struct elf_section_list * next;
+  Elf_Internal_Shdr *        hdr;
+  struct elf_section_list *  next;
 } elf_section_list;
 
+/* Flag bits indicating particular types of dump.  */
+#define HEX_DUMP	(1 << 0)	/* The -x command line switch.  */
+#define DISASS_DUMP	(1 << 1)	/* The -i command line switch.  */
+#define DEBUG_DUMP	(1 << 2)	/* The -w command line switch.  */
+#define STRING_DUMP     (1 << 3)	/* The -p command line switch.  */
+#define RELOC_DUMP      (1 << 4)	/* The -R command line switch.  */
+
+typedef unsigned char dump_type;
+
+/* A linked list of the section names for which dumps were requested.  */
+struct dump_list_entry
+{
+  char *                    name;
+  dump_type                 type;
+  struct dump_list_entry *  next;
+};
+
+typedef struct filedata
+{
+  const char *         file_name;
+  FILE *               handle;
+  bfd_size_type        file_size;
+  Elf_Internal_Ehdr    file_header;
+  Elf_Internal_Shdr *  section_headers;
+  Elf_Internal_Phdr *  program_headers;
+  char *               string_table;
+  unsigned long        string_table_length;
+  /* A dynamic array of flags indicating for which sections a dump of
+     some kind has been requested.  It is reset on a per-object file
+     basis and then initialised from the cmdline_dump_sects array,
+     the results of interpreting the -w switch, and the
+     dump_sects_byname list.  */
+  dump_type *          dump_sects;
+  unsigned int         num_dump_sects;
+} Filedata;
+
 char * program_name = "readelf";
+
 static unsigned long archive_file_offset;
 static unsigned long archive_file_size;
-static bfd_size_type current_file_size;
 static unsigned long dynamic_addr;
 static bfd_size_type dynamic_size;
 static size_t dynamic_nent;
 static char * dynamic_strings;
 static unsigned long dynamic_strings_length;
-static char * string_table;
-static unsigned long string_table_length;
 static unsigned long num_dynamic_syms;
 static Elf_Internal_Sym * dynamic_symbols;
 static Elf_Internal_Syminfo * dynamic_syminfo;
@@ -193,9 +227,6 @@ static char program_interpreter[PATH_MAX];
 static bfd_vma dynamic_info[DT_ENCODING];
 static bfd_vma dynamic_info_DT_GNU_HASH;
 static bfd_vma version_info[16];
-static Elf_Internal_Ehdr elf_header;
-static Elf_Internal_Shdr * section_headers;
-static Elf_Internal_Phdr * program_headers;
 static Elf_Internal_Dyn *  dynamic_section;
 static elf_section_list * symtab_shndx_list;
 static bfd_boolean show_name = FALSE;
@@ -222,52 +253,25 @@ static bfd_boolean decompress_dumps = FALSE;
 
 struct group_list
 {
-  struct group_list * next;
-  unsigned int section_index;
+  struct group_list *  next;
+  unsigned int         section_index;
 };
 
 struct group
 {
-  struct group_list * root;
-  unsigned int group_index;
+  struct group_list *  root;
+  unsigned int         group_index;
 };
 
-static size_t group_count;
-static struct group * section_groups;
-static struct group ** section_headers_groups;
-
-
-/* Flag bits indicating particular types of dump.  */
-#define HEX_DUMP	(1 << 0)	/* The -x command line switch.  */
-#define DISASS_DUMP	(1 << 1)	/* The -i command line switch.  */
-#define DEBUG_DUMP	(1 << 2)	/* The -w command line switch.  */
-#define STRING_DUMP     (1 << 3)	/* The -p command line switch.  */
-#define RELOC_DUMP      (1 << 4)	/* The -R command line switch.  */
-
-typedef unsigned char dump_type;
-
-/* A linked list of the section names for which dumps were requested.  */
-struct dump_list_entry
-{
-  char * name;
-  dump_type type;
-  struct dump_list_entry * next;
-};
-static struct dump_list_entry * dump_sects_byname;
+static size_t           group_count;
+static struct group *   section_groups;
+static struct group **  section_headers_groups;
 
 /* A dynamic array of flags indicating for which sections a dump
    has been requested via command line switches.  */
-static dump_type *   cmdline_dump_sects = NULL;
-static unsigned int  num_cmdline_dump_sects = 0;
-
-/* A dynamic array of flags indicating for which sections a dump of
-   some kind has been requested.  It is reset on a per-object file
-   basis and then initialised from the cmdline_dump_sects array,
-   the results of interpreting the -w switch, and the
-   dump_sects_byname list.  */
-static dump_type *   dump_sects = NULL;
-static unsigned int  num_dump_sects = 0;
+static Filedata         cmdline;
 
+static struct dump_list_entry * dump_sects_byname;
 
 /* How to print a vma value.  */
 typedef enum print_mode
@@ -291,16 +295,16 @@ enum versioned_symbol_info
 };
 
 static const char * get_symbol_version_string
-  (FILE *, bfd_boolean, const char *, unsigned long, unsigned,
+  (Filedata *, bfd_boolean, const char *, unsigned long, unsigned,
    Elf_Internal_Sym *, enum versioned_symbol_info *, unsigned short *);
 
 #define UNKNOWN -1
 
 #define SECTION_NAME(X)						\
   ((X) == NULL ? _("<none>")					\
-   : string_table == NULL ? _("<no-name>")			\
-   : ((X)->sh_name >= string_table_length ? _("<corrupt>")	\
-  : string_table + (X)->sh_name))
+   : filedata->string_table == NULL ? _("<no-strings>")		\
+   : ((X)->sh_name >= filedata->string_table_length ? _("<corrupt>")	\
+  : filedata->string_table + (X)->sh_name))
 
 #define DT_VERSIONTAGIDX(tag)	(DT_VERNEEDNUM - (tag))	/* Reverse order!  */
 
@@ -316,7 +320,7 @@ static const char * get_symbol_version_string
 #define REMOVE_ARCH_BITS(ADDR)			\
   do						\
     {						\
-      if (elf_header.e_machine == EM_ARM)	\
+      if (filedata->file_header.e_machine == EM_ARM)	\
 	(ADDR) &= ~1;				\
     }						\
   while (0)
@@ -345,17 +349,21 @@ bfd_vmatoa (char *fmtch, bfd_vma value)
   return ret;
 }
 
-/* Retrieve NMEMB structures, each SIZE bytes long from FILE starting at OFFSET +
-   the offset of the current archive member, if we are examining an archive.
-   Put the retrieved data into VAR, if it is not NULL.  Otherwise allocate a buffer
-   using malloc and fill that.  In either case return the pointer to the start of
-   the retrieved data or NULL if something went wrong.  If something does go wrong
-   and REASON is not NULL then emit an error message using REASON as part of the
-   context.  */
+/* Retrieve NMEMB structures, each SIZE bytes long from FILEDATA starting at
+   OFFSET + the offset of the current archive member, if we are examining an
+   archive.  Put the retrieved data into VAR, if it is not NULL.  Otherwise
+   allocate a buffer using malloc and fill that.  In either case return the
+   pointer to the start of the retrieved data or NULL if something went wrong.
+   If something does go wrong and REASON is not NULL then emit an error
+   message using REASON as part of the context.  */
 
 static void *
-get_data (void * var, FILE * file, unsigned long offset, bfd_size_type size,
-	  bfd_size_type nmemb, const char * reason)
+get_data (void *         var,
+	  Filedata *     filedata,
+	  unsigned long  offset,
+	  bfd_size_type  size,
+	  bfd_size_type  nmemb,
+	  const char *   reason)
 {
   void * mvar;
   bfd_size_type amt = size * nmemb;
@@ -389,8 +397,8 @@ get_data (void * var, FILE * file, unsigned long offset, bfd_size_type size,
 
   /* Be kind to memory chekers (eg valgrind, address sanitizer) by not
      attempting to allocate memory when the read is bound to fail.  */
-  if (amt > current_file_size
-      || offset + archive_file_offset + amt > current_file_size)
+  if (amt > filedata->file_size
+      || offset + archive_file_offset + amt > filedata->file_size)
     {
       if (reason)
 	error (_("Reading %s bytes extends past end of file for %s\n"),
@@ -398,7 +406,7 @@ get_data (void * var, FILE * file, unsigned long offset, bfd_size_type size,
       return NULL;
     }
 
-  if (fseek (file, archive_file_offset + offset, SEEK_SET))
+  if (fseek (filedata->handle, archive_file_offset + offset, SEEK_SET))
     {
       if (reason)
 	error (_("Unable to seek to 0x%lx for %s\n"),
@@ -425,7 +433,7 @@ get_data (void * var, FILE * file, unsigned long offset, bfd_size_type size,
       ((char *) mvar)[amt] = '\0';
     }
 
-  if (fread (mvar, (size_t) size, (size_t) nmemb, file) != nmemb)
+  if (fread (mvar, (size_t) size, (size_t) nmemb, filedata->handle) != nmemb)
     {
       if (reason)
 	error (_("Unable to read in %s bytes of %s\n"),
@@ -585,7 +593,7 @@ print_symbol (signed int width, const char *symbol)
    to print multibyte characters, it just interprets them as hex values.  */
 
 static const char *
-printable_section_name (const Elf_Internal_Shdr * sec)
+printable_section_name (Filedata * filedata, const Elf_Internal_Shdr * sec)
 {
 #define MAX_PRINT_SEC_NAME_LEN 128
   static char  sec_name_buf [MAX_PRINT_SEC_NAME_LEN + 1];
@@ -632,24 +640,26 @@ printable_section_name (const Elf_Internal_Shdr * sec)
 }
 
 static const char *
-printable_section_name_from_index (unsigned long ndx)
+printable_section_name_from_index (Filedata * filedata, unsigned long ndx)
 {
-  if (ndx >= elf_header.e_shnum)
+  if (ndx >= filedata->file_header.e_shnum)
     return _("<corrupt>");
 
-  return printable_section_name (section_headers + ndx);
+  return printable_section_name (filedata, filedata->section_headers + ndx);
 }
 
 /* Return a pointer to section NAME, or NULL if no such section exists.  */
 
 static Elf_Internal_Shdr *
-find_section (const char * name)
+find_section (Filedata * filedata, const char * name)
 {
   unsigned int i;
 
-  for (i = 0; i < elf_header.e_shnum; i++)
-    if (streq (SECTION_NAME (section_headers + i), name))
-      return section_headers + i;
+  assert (filedata->section_headers != NULL);
+
+  for (i = 0; i < filedata->file_header.e_shnum; i++)
+    if (streq (SECTION_NAME (filedata->section_headers + i), name))
+      return filedata->section_headers + i;
 
   return NULL;
 }
@@ -658,13 +668,14 @@ find_section (const char * name)
    section exists.  */
 
 static Elf_Internal_Shdr *
-find_section_by_address (bfd_vma addr)
+find_section_by_address (Filedata * filedata, bfd_vma addr)
 {
   unsigned int i;
 
-  for (i = 0; i < elf_header.e_shnum; i++)
+  for (i = 0; i < filedata->file_header.e_shnum; i++)
     {
-      Elf_Internal_Shdr *sec = section_headers + i;
+      Elf_Internal_Shdr *sec = filedata->section_headers + i;
+
       if (addr >= sec->sh_addr && addr < sec->sh_addr + sec->sh_size)
 	return sec;
     }
@@ -673,13 +684,14 @@ find_section_by_address (bfd_vma addr)
 }
 
 static Elf_Internal_Shdr *
-find_section_by_type (unsigned int type)
+find_section_by_type (Filedata * filedata, unsigned int type)
 {
   unsigned int i;
 
-  for (i = 0; i < elf_header.e_shnum; i++)
+  for (i = 0; i < filedata->file_header.e_shnum; i++)
     {
-      Elf_Internal_Shdr *sec = section_headers + i;
+      Elf_Internal_Shdr *sec = filedata->section_headers + i;
+
       if (sec->sh_type == type)
 	return sec;
     }
@@ -691,7 +703,7 @@ find_section_by_type (unsigned int type)
    restricted to the list of sections given in SET.  */
 
 static Elf_Internal_Shdr *
-find_section_in_set (const char * name, unsigned int * set)
+find_section_in_set (Filedata * filedata, const char * name, unsigned int * set)
 {
   unsigned int i;
 
@@ -700,15 +712,15 @@ find_section_in_set (const char * name, unsigned int * set)
       while ((i = *set++) > 0)
 	{
 	  /* See PR 21156 for a reproducer.  */
-	  if (i >= elf_header.e_shnum)
+	  if (i >= filedata->file_header.e_shnum)
 	    continue; /* FIXME: Should we issue an error message ?  */
 
-	  if (streq (SECTION_NAME (section_headers + i), name))
-	    return section_headers + i;
+	  if (streq (SECTION_NAME (filedata->section_headers + i), name))
+	    return filedata->section_headers + i;
 	}
     }
 
-  return find_section (name);
+  return find_section (filedata, name);
 }
 
 /* Read an unsigned LEB128 encoded value from DATA.
@@ -727,10 +739,10 @@ read_uleb128 (unsigned char * data,
    many places.  */
 
 static inline bfd_boolean
-is_ia64_vms (void)
+is_ia64_vms (Filedata * filedata)
 {
-  return elf_header.e_machine == EM_IA_64
-    && elf_header.e_ident[EI_OSABI] == ELFOSABI_OPENVMS;
+  return filedata->file_header.e_machine == EM_IA_64
+    && filedata->file_header.e_ident[EI_OSABI] == ELFOSABI_OPENVMS;
 }
 
 /* Guess the relocation size commonly used by the specific machines.  */
@@ -858,18 +870,18 @@ guess_is_rela (unsigned int e_machine)
     }
 }
 
-/* Load RELA type relocations from FILE at REL_OFFSET extending for REL_SIZE bytes.
+/* Load RELA type relocations from FILEDATA at REL_OFFSET extending for REL_SIZE bytes.
    Returns TRUE upon success, FALSE otherwise.  If successful then a
    pointer to a malloc'ed buffer containing the relocs is placed in *RELASP,
    and the number of relocs loaded is placed in *NRELASP.  It is the caller's
    responsibility to free the allocated buffer.  */
 
 static bfd_boolean
-slurp_rela_relocs (FILE * file,
-		   unsigned long rel_offset,
-		   unsigned long rel_size,
-		   Elf_Internal_Rela ** relasp,
-		   unsigned long * nrelasp)
+slurp_rela_relocs (Filedata *            filedata,
+		   unsigned long         rel_offset,
+		   unsigned long         rel_size,
+		   Elf_Internal_Rela **  relasp,
+		   unsigned long *       nrelasp)
 {
   Elf_Internal_Rela * relas;
   size_t nrelas;
@@ -879,7 +891,7 @@ slurp_rela_relocs (FILE * file,
     {
       Elf32_External_Rela * erelas;
 
-      erelas = (Elf32_External_Rela *) get_data (NULL, file, rel_offset, 1,
+      erelas = (Elf32_External_Rela *) get_data (NULL, filedata, rel_offset, 1,
                                                  rel_size, _("32-bit relocation data"));
       if (!erelas)
 	return FALSE;
@@ -909,7 +921,7 @@ slurp_rela_relocs (FILE * file,
     {
       Elf64_External_Rela * erelas;
 
-      erelas = (Elf64_External_Rela *) get_data (NULL, file, rel_offset, 1,
+      erelas = (Elf64_External_Rela *) get_data (NULL, filedata, rel_offset, 1,
                                                  rel_size, _("64-bit relocation data"));
       if (!erelas)
 	return FALSE;
@@ -936,8 +948,8 @@ slurp_rela_relocs (FILE * file,
 	     warning.  We know that if we do not have a 64 bit data
 	     type that we will never execute this code anyway.  */
 #ifdef BFD64
-	  if (elf_header.e_machine == EM_MIPS
-	      && elf_header.e_ident[EI_DATA] != ELFDATA2MSB)
+	  if (filedata->file_header.e_machine == EM_MIPS
+	      && filedata->file_header.e_ident[EI_DATA] != ELFDATA2MSB)
 	    {
 	      /* In little-endian objects, r_info isn't really a
 		 64-bit little-endian value: it has a 32-bit
@@ -963,18 +975,18 @@ slurp_rela_relocs (FILE * file,
   return TRUE;
 }
 
-/* Load REL type relocations from FILE at REL_OFFSET extending for REL_SIZE bytes.
+/* Load REL type relocations from FILEDATA at REL_OFFSET extending for REL_SIZE bytes.
    Returns TRUE upon success, FALSE otherwise.  If successful then a
    pointer to a malloc'ed buffer containing the relocs is placed in *RELSP,
    and the number of relocs loaded is placed in *NRELSP.  It is the caller's
    responsibility to free the allocated buffer.  */
 
 static bfd_boolean
-slurp_rel_relocs (FILE * file,
-		  unsigned long rel_offset,
-		  unsigned long rel_size,
-		  Elf_Internal_Rela ** relsp,
-		  unsigned long * nrelsp)
+slurp_rel_relocs (Filedata *            filedata,
+		  unsigned long         rel_offset,
+		  unsigned long         rel_size,
+		  Elf_Internal_Rela **  relsp,
+		  unsigned long *       nrelsp)
 {
   Elf_Internal_Rela * rels;
   size_t nrels;
@@ -984,7 +996,7 @@ slurp_rel_relocs (FILE * file,
     {
       Elf32_External_Rel * erels;
 
-      erels = (Elf32_External_Rel *) get_data (NULL, file, rel_offset, 1,
+      erels = (Elf32_External_Rel *) get_data (NULL, filedata, rel_offset, 1,
                                                rel_size, _("32-bit relocation data"));
       if (!erels)
 	return FALSE;
@@ -1013,7 +1025,7 @@ slurp_rel_relocs (FILE * file,
     {
       Elf64_External_Rel * erels;
 
-      erels = (Elf64_External_Rel *) get_data (NULL, file, rel_offset, 1,
+      erels = (Elf64_External_Rel *) get_data (NULL, filedata, rel_offset, 1,
                                                rel_size, _("64-bit relocation data"));
       if (!erels)
 	return FALSE;
@@ -1039,8 +1051,8 @@ slurp_rel_relocs (FILE * file,
 	     warning.  We know that if we do not have a 64 bit data
 	     type that we will never execute this code anyway.  */
 #ifdef BFD64
-	  if (elf_header.e_machine == EM_MIPS
-	      && elf_header.e_ident[EI_DATA] != ELFDATA2MSB)
+	  if (filedata->file_header.e_machine == EM_MIPS
+	      && filedata->file_header.e_ident[EI_DATA] != ELFDATA2MSB)
 	    {
 	      /* In little-endian objects, r_info isn't really a
 		 64-bit little-endian value: it has a 32-bit
@@ -1069,12 +1081,12 @@ slurp_rel_relocs (FILE * file,
 /* Returns the reloc type extracted from the reloc info field.  */
 
 static unsigned int
-get_reloc_type (bfd_vma reloc_info)
+get_reloc_type (Filedata * filedata, bfd_vma reloc_info)
 {
   if (is_32bit_elf)
     return ELF32_R_TYPE (reloc_info);
 
-  switch (elf_header.e_machine)
+  switch (filedata->file_header.e_machine)
     {
     case EM_MIPS:
       /* Note: We assume that reloc_info has already been adjusted for us.  */
@@ -1097,45 +1109,45 @@ get_reloc_symindex (bfd_vma reloc_info)
 }
 
 static inline bfd_boolean
-uses_msp430x_relocs (void)
+uses_msp430x_relocs (Filedata * filedata)
 {
   return
-    elf_header.e_machine == EM_MSP430 /* Paranoia.  */
+    filedata->file_header.e_machine == EM_MSP430 /* Paranoia.  */
     /* GCC uses osabi == ELFOSBI_STANDALONE.  */
-    && (((elf_header.e_flags & EF_MSP430_MACH) == E_MSP430_MACH_MSP430X)
+    && (((filedata->file_header.e_flags & EF_MSP430_MACH) == E_MSP430_MACH_MSP430X)
 	/* TI compiler uses ELFOSABI_NONE.  */
-	|| (elf_header.e_ident[EI_OSABI] == ELFOSABI_NONE));
+	|| (filedata->file_header.e_ident[EI_OSABI] == ELFOSABI_NONE));
 }
 
 /* Display the contents of the relocation data found at the specified
    offset.  */
 
 static bfd_boolean
-dump_relocations (FILE * file,
-		  unsigned long rel_offset,
-		  unsigned long rel_size,
-		  Elf_Internal_Sym * symtab,
-		  unsigned long nsyms,
-		  char * strtab,
-		  unsigned long strtablen,
-		  int is_rela,
-		  bfd_boolean is_dynsym)
+dump_relocations (Filedata *          filedata,
+		  unsigned long       rel_offset,
+		  unsigned long       rel_size,
+		  Elf_Internal_Sym *  symtab,
+		  unsigned long       nsyms,
+		  char *              strtab,
+		  unsigned long       strtablen,
+		  int                 is_rela,
+		  bfd_boolean         is_dynsym)
 {
   unsigned long i;
   Elf_Internal_Rela * rels;
   bfd_boolean res = TRUE;
 
   if (is_rela == UNKNOWN)
-    is_rela = guess_is_rela (elf_header.e_machine);
+    is_rela = guess_is_rela (filedata->file_header.e_machine);
 
   if (is_rela)
     {
-      if (!slurp_rela_relocs (file, rel_offset, rel_size, &rels, &rel_size))
+      if (!slurp_rela_relocs (filedata, rel_offset, rel_size, &rels, &rel_size))
 	return FALSE;
     }
   else
     {
-      if (!slurp_rel_relocs (file, rel_offset, rel_size, &rels, &rel_size))
+      if (!slurp_rel_relocs (filedata, rel_offset, rel_size, &rels, &rel_size))
 	return FALSE;
     }
 
@@ -1185,7 +1197,7 @@ dump_relocations (FILE * file,
       offset = rels[i].r_offset;
       inf    = rels[i].r_info;
 
-      type = get_reloc_type (inf);
+      type = get_reloc_type (filedata, inf);
       symtab_index = get_reloc_symindex  (inf);
 
       if (is_32bit_elf)
@@ -1224,7 +1236,7 @@ dump_relocations (FILE * file,
 #endif
 	}
 
-      switch (elf_header.e_machine)
+      switch (filedata->file_header.e_machine)
 	{
 	default:
 	  rtype = NULL;
@@ -1335,7 +1347,7 @@ dump_relocations (FILE * file,
 	  break;
 
 	case EM_MSP430:
-	  if (uses_msp430x_relocs ())
+	  if (uses_msp430x_relocs (filedata))
 	    {
 	      rtype = elf_msp430x_reloc_type (type);
 	      break;
@@ -1544,7 +1556,7 @@ dump_relocations (FILE * file,
       else
 	printf (do_wide ? "%-22s" : "%-17.17s", rtype);
 
-      if (elf_header.e_machine == EM_ALPHA
+      if (filedata->file_header.e_machine == EM_ALPHA
 	  && rtype != NULL
 	  && streq (rtype, "R_ALPHA_LITUSE")
 	  && is_rela)
@@ -1588,7 +1600,7 @@ dump_relocations (FILE * file,
 	      psym = symtab + symtab_index;
 
 	      version_string
-		= get_symbol_version_string (file, is_dynsym,
+		= get_symbol_version_string (filedata, is_dynsym,
 					     strtab, strtablen,
 					     symtab_index,
 					     psym,
@@ -1640,30 +1652,30 @@ dump_relocations (FILE * file,
 
 		  if (ELF_ST_TYPE (psym->st_info) == STT_SECTION)
 		    {
-		      if (psym->st_shndx < elf_header.e_shnum)
-			sec_name = SECTION_NAME (section_headers + psym->st_shndx);
+		      if (psym->st_shndx < filedata->file_header.e_shnum)
+			sec_name = SECTION_NAME (filedata->section_headers + psym->st_shndx);
 		      else if (psym->st_shndx == SHN_ABS)
 			sec_name = "ABS";
 		      else if (psym->st_shndx == SHN_COMMON)
 			sec_name = "COMMON";
-		      else if ((elf_header.e_machine == EM_MIPS
+		      else if ((filedata->file_header.e_machine == EM_MIPS
 				&& psym->st_shndx == SHN_MIPS_SCOMMON)
-			       || (elf_header.e_machine == EM_TI_C6000
+			       || (filedata->file_header.e_machine == EM_TI_C6000
 				   && psym->st_shndx == SHN_TIC6X_SCOMMON))
 			sec_name = "SCOMMON";
-		      else if (elf_header.e_machine == EM_MIPS
+		      else if (filedata->file_header.e_machine == EM_MIPS
 			       && psym->st_shndx == SHN_MIPS_SUNDEFINED)
 			sec_name = "SUNDEF";
-		      else if ((elf_header.e_machine == EM_X86_64
-				|| elf_header.e_machine == EM_L1OM
-				|| elf_header.e_machine == EM_K1OM)
+		      else if ((filedata->file_header.e_machine == EM_X86_64
+				|| filedata->file_header.e_machine == EM_L1OM
+				|| filedata->file_header.e_machine == EM_K1OM)
 			       && psym->st_shndx == SHN_X86_64_LCOMMON)
 			sec_name = "LARGE_COMMON";
-		      else if (elf_header.e_machine == EM_IA_64
-			       && elf_header.e_ident[EI_OSABI] == ELFOSABI_HPUX
+		      else if (filedata->file_header.e_machine == EM_IA_64
+			       && filedata->file_header.e_ident[EI_OSABI] == ELFOSABI_HPUX
 			       && psym->st_shndx == SHN_IA_64_ANSI_COMMON)
 			sec_name = "ANSI_COM";
-		      else if (is_ia64_vms ()
+		      else if (is_ia64_vms (filedata)
 			       && psym->st_shndx == SHN_IA_64_VMS_SYMVEC)
 			sec_name = "VMS_SYMVEC";
 		      else
@@ -1712,7 +1724,7 @@ dump_relocations (FILE * file,
 	    [...]

[diff truncated at 100000 bytes]


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]