Add debuginfod support for objdump -S
[binutils-gdb.git] / binutils / objdump.c
index cf9cedc71ad1fbdd40900c9fdcd9d0a4686d4920..d34f269b40600f6db2d66562304c327400322262 100644 (file)
@@ -1,5 +1,5 @@
 /* objdump.c -- dump information about an object file.
-   Copyright (C) 1990-2021 Free Software Foundation, Inc.
+   Copyright (C) 1990-2022 Free Software Foundation, Inc.
 
    This file is part of GNU Binutils.
 
@@ -55,6 +55,7 @@
 #include "progress.h"
 #include "bucomm.h"
 #include "elfcomm.h"
+#include "demanguse.h"
 #include "dwarf.h"
 #include "ctf-api.h"
 #include "getopt.h"
 #include <sys/mman.h>
 #endif
 
+#ifdef HAVE_LIBDEBUGINFOD
+#include <elfutils/debuginfod.h>
+#endif
+
 /* Internal headers for the ELF .stab-dump code - sorry.  */
 #define        BYTES_IN_WORD   32
 #include "aout/aout64.h"
@@ -130,8 +135,24 @@ static bool color_output = false;  /* --visualize-jumps=color.  */
 static bool extended_color_output = false; /* --visualize-jumps=extended-color.  */
 static int process_links = false;       /* --process-links.  */
 
+static enum color_selection
+  {
+    on_if_terminal_output,
+    on,                                /* --disassembler-color=color.  */
+    off,                               /* --disassembler-color=off.  */
+    extended                           /* --disassembler-color=extended-color.  */
+  } disassembler_color = on_if_terminal_output;
+
+static int dump_any_debugging;
 static int demangle_flags = DMGL_ANSI | DMGL_PARAMS;
 
+/* This is reset to false each time we enter the disassembler, and set true
+   when the disassembler emits something in the dis_style_comment_start
+   style.  Once this is true, all further output on that line is done in
+   the comment style.  This only has an effect when disassembler coloring
+   is turned on.  */
+static bool disassembler_in_comment = false;
+
 /* A structure to record the sections mentioned in -j switches.  */
 struct only
 {
@@ -279,6 +300,14 @@ usage (FILE *stream, int status)
   -WN,--dwarf=no-follow-links\n\
                            Do not follow links to separate debug info files\n\
                             (default)\n"));
+#endif
+#if HAVE_LIBDEBUGINFOD
+  fprintf (stream, _("\
+  -WD --dwarf=use-debuginfod\n\
+                           When following links, also query debuginfod servers (default)\n"));
+  fprintf (stream, _("\
+  -WE --dwarf=do-not-use-debuginfod\n\
+                           When following links, do not query debuginfod servers\n"));
 #endif
   fprintf (stream, _("\
   -L, --process-links      Display the contents of non-debug sections in\n\
@@ -330,10 +359,9 @@ usage (FILE *stream, int status)
       fprintf (stream, _("\
   -F, --file-offsets             Include file offsets when displaying information\n"));
       fprintf (stream, _("\
-  -C, --demangle[=STYLE]         Decode mangled/processed symbol names\n\
-                                  The STYLE, if specified, can be `auto', `gnu',\n\
-                                  `lucid', `arm', `hp', `edg', `gnu-v3', `java'\n\
-                                  or `gnat'\n"));
+  -C, --demangle[=STYLE]         Decode mangled/processed symbol names\n"));
+      display_demangler_styles (stream, _("\
+                                   STYLE can be "));
       fprintf (stream, _("\
       --recurse-limit            Enable a limit on recursion whilst demangling\n\
                                   (default)\n"));
@@ -387,6 +415,12 @@ usage (FILE *stream, int status)
                                  Use extended 8-bit color codes\n"));
       fprintf (stream, _("\
       --visualize-jumps=off      Disable jump visualization\n\n"));
+      fprintf (stream, _("\
+      --disassembler-color=off   Disable disassembler color output.\n\n"));
+      fprintf (stream, _("\
+      --disassembler-color=color Use basic colors in disassembler output.\n\n"));
+      fprintf (stream, _("\
+      --disassembler-color=extended-color Use 8-bit colors in disassembler output.\n\n"));
 
       list_supported_targets (program_name, stream);
       list_supported_architectures (program_name, stream);
@@ -428,7 +462,8 @@ enum option_values
     OPTION_CTF,
     OPTION_CTF_PARENT,
 #endif
-    OPTION_VISUALIZE_JUMPS
+    OPTION_VISUALIZE_JUMPS,
+    OPTION_DISASSEMBLER_COLOR
   };
 
 static struct option long_options[]=
@@ -494,6 +529,7 @@ static struct option long_options[]=
   {"version", no_argument, NULL, 'V'},
   {"visualize-jumps", optional_argument, 0, OPTION_VISUALIZE_JUMPS},
   {"wide", no_argument, NULL, 'w'},
+  {"disassembler-color", required_argument, NULL, OPTION_DISASSEMBLER_COLOR},
   {NULL, no_argument, NULL, 0}
 };
 \f
@@ -1237,7 +1273,7 @@ objdump_print_value (bfd_vma vma, struct disassemble_info *inf,
       if (*p == '\0')
        --p;
     }
-  (*inf->fprintf_func) (inf->stream, "%s", p);
+  (*inf->fprintf_styled_func) (inf->stream, dis_style_address, "%s", p);
 }
 
 /* Print the name of a symbol.  */
@@ -1271,10 +1307,11 @@ objdump_print_symname (bfd *abfd, struct disassemble_info *inf,
 
   if (inf != NULL)
     {
-      (*inf->fprintf_func) (inf->stream, "%s", name);
+      (*inf->fprintf_styled_func) (inf->stream, dis_style_symbol, "%s", name);
       if (version_string && *version_string != '\0')
-       (*inf->fprintf_func) (inf->stream, hidden ? "@%s" : "@@%s",
-                             version_string);
+       (*inf->fprintf_styled_func) (inf->stream, dis_style_symbol,
+                                    hidden ? "@%s" : "@@%s",
+                                    version_string);
     }
   else
     {
@@ -1536,31 +1573,33 @@ objdump_print_addr_with_sym (bfd *abfd, asection *sec, asymbol *sym,
   if (!no_addresses)
     {
       objdump_print_value (vma, inf, skip_zeroes);
-      (*inf->fprintf_func) (inf->stream, " ");
+      (*inf->fprintf_styled_func) (inf->stream, dis_style_text, " ");
     }
 
   if (sym == NULL)
     {
       bfd_vma secaddr;
 
-      (*inf->fprintf_func) (inf->stream, "<%s",
-                           sanitize_string (bfd_section_name (sec)));
+      (*inf->fprintf_styled_func) (inf->stream, dis_style_text,"<");
+      (*inf->fprintf_styled_func) (inf->stream, dis_style_symbol, "%s",
+                                  sanitize_string (bfd_section_name (sec)));
       secaddr = bfd_section_vma (sec);
       if (vma < secaddr)
        {
-         (*inf->fprintf_func) (inf->stream, "-0x");
+         (*inf->fprintf_styled_func) (inf->stream, dis_style_immediate,
+                                      "-0x");
          objdump_print_value (secaddr - vma, inf, true);
        }
       else if (vma > secaddr)
        {
-         (*inf->fprintf_func) (inf->stream, "+0x");
+         (*inf->fprintf_styled_func) (inf->stream, dis_style_immediate, "+0x");
          objdump_print_value (vma - secaddr, inf, true);
        }
-      (*inf->fprintf_func) (inf->stream, ">");
+      (*inf->fprintf_styled_func) (inf->stream, dis_style_text, ">");
     }
   else
     {
-      (*inf->fprintf_func) (inf->stream, "<");
+      (*inf->fprintf_styled_func) (inf->stream, dis_style_text, "<");
 
       objdump_print_symname (abfd, inf, sym);
 
@@ -1577,21 +1616,22 @@ objdump_print_addr_with_sym (bfd *abfd, asection *sec, asymbol *sym,
        ;
       else if (bfd_asymbol_value (sym) > vma)
        {
-         (*inf->fprintf_func) (inf->stream, "-0x");
+         (*inf->fprintf_styled_func) (inf->stream, dis_style_immediate,"-0x");
          objdump_print_value (bfd_asymbol_value (sym) - vma, inf, true);
        }
       else if (vma > bfd_asymbol_value (sym))
        {
-         (*inf->fprintf_func) (inf->stream, "+0x");
+         (*inf->fprintf_styled_func) (inf->stream, dis_style_immediate, "+0x");
          objdump_print_value (vma - bfd_asymbol_value (sym), inf, true);
        }
 
-      (*inf->fprintf_func) (inf->stream, ">");
+      (*inf->fprintf_styled_func) (inf->stream, dis_style_text, ">");
     }
 
   if (display_file_offsets)
-    inf->fprintf_func (inf->stream, _(" (File Offset: 0x%lx)"),
-                       (long int)(sec->filepos + (vma - sec->vma)));
+    inf->fprintf_styled_func (inf->stream, dis_style_text,
+                             _(" (File Offset: 0x%lx)"),
+                             (long int)(sec->filepos + (vma - sec->vma)));
 }
 
 /* Print an address (VMA), symbolically if possible.
@@ -1612,14 +1652,15 @@ objdump_print_addr (bfd_vma vma,
     {
       if (!no_addresses)
        {
-         (*inf->fprintf_func) (inf->stream, "0x");
+         (*inf->fprintf_styled_func) (inf->stream, dis_style_address, "0x");
          objdump_print_value (vma, inf, skip_zeroes);
        }
 
       if (display_file_offsets)
-       inf->fprintf_func (inf->stream, _(" (File Offset: 0x%lx)"),
-                          (long int) (inf->section->filepos
-                                      + (vma - inf->section->vma)));
+       inf->fprintf_styled_func (inf->stream, dis_style_text,
+                                 _(" (File Offset: 0x%lx)"),
+                                 (long int) (inf->section->filepos
+                                             + (vma - inf->section->vma)));
       return;
     }
 
@@ -1698,28 +1739,174 @@ static struct print_file_list *print_files;
 
 #define SHOW_PRECEDING_CONTEXT_LINES (5)
 
-/* Read a complete file into memory.  */
+#if HAVE_LIBDEBUGINFOD
+/* Return a hex string represention of the build-id.  */
+
+unsigned char *
+get_build_id (void * data)
+{
+  unsigned i;
+  char * build_id_str;
+  bfd * abfd = (bfd *) data;
+  const struct bfd_build_id * build_id;
+
+  build_id = abfd->build_id;
+  if (build_id == NULL)
+    return NULL;
+
+  build_id_str = malloc (build_id->size * 2 + 1);
+  if (build_id_str == NULL)
+    return NULL;
+
+  for (i = 0; i < build_id->size; i++)
+    sprintf (build_id_str + (i * 2), "%02x", build_id->data[i]);
+  build_id_str[build_id->size * 2] = '\0';
+
+  return (unsigned char *) build_id_str;
+}
+
+/* Search for a separate debug file matching ABFD's build-id.  */
+
+static bfd *
+find_separate_debug (const bfd * abfd)
+{
+  const struct bfd_build_id * build_id = abfd->build_id;
+  separate_info * i = first_separate_info;
+
+  if (build_id == NULL || i == NULL)
+    return NULL;
+
+  while (i != NULL)
+    {
+      const bfd * i_bfd = (bfd *) i->handle;
+
+      if (abfd != NULL && i_bfd->build_id != NULL)
+       {
+         const unsigned char * data = i_bfd->build_id->data;
+         size_t size = i_bfd->build_id->size;
+
+         if (size == build_id->size
+             && memcmp (data, build_id->data, size) == 0)
+           return (bfd *) i->handle;
+       }
+
+      i = i->next;
+    }
+
+  return NULL;
+}
+
+/* Search for a separate debug file matching ABFD's .gnu_debugaltlink
+    build-id.  */
+
+static bfd *
+find_alt_debug (const bfd * abfd)
+{
+  size_t namelen;
+  size_t id_len;
+  const char * name;
+  struct dwarf_section * section;
+  const struct bfd_build_id * build_id = abfd->build_id;
+  separate_info * i = first_separate_info;
+
+  if (i == NULL
+      || build_id == NULL
+      || !load_debug_section (gnu_debugaltlink, (void *) abfd))
+    return NULL;
+
+  section = &debug_displays[gnu_debugaltlink].section;
+  if (section == NULL)
+    return NULL;
+
+  name = (const char *) section->start;
+  namelen = strnlen (name, section->size) + 1;
+  if (namelen == 1)
+    return NULL;
+  if (namelen >= section->size)
+    return NULL;
+
+  id_len = section->size - namelen;
+  if (id_len < 0x14)
+    return NULL;
+
+  /* Compare the .gnu_debugaltlink build-id with the build-ids of the
+     known separate_info files.  */
+  while (i != NULL)
+    {
+      const bfd * i_bfd = (bfd *) i->handle;
+
+      if (i_bfd != NULL && i_bfd->build_id != NULL)
+       {
+         const unsigned char * data = i_bfd->build_id->data;
+         size_t size = i_bfd->build_id->size;
+
+         if (id_len == size
+             && memcmp (section->start + namelen, data, size) == 0)
+           return (bfd *) i->handle;
+       }
+
+      i = i->next;
+    }
+
+  return NULL;
+}
+
+#endif /* HAVE_LIBDEBUGINFOD */
+
+/* Reads the contents of file FN into memory.  Returns a pointer to the buffer.
+   Also returns the size of the buffer in SIZE_RETURN and a filled out
+   stat structure in FST_RETURN.  Returns NULL upon failure.  */
 
 static const char *
-slurp_file (const char *fn, size_t *size, struct stat *fst)
+slurp_file (const char *   fn,
+           size_t *       size_return,
+           struct stat *  fst_return,
+           bfd *          abfd ATTRIBUTE_UNUSED)
 {
 #ifdef HAVE_MMAP
-  int ps = getpagesize ();
+  int ps;
   size_t msize;
 #endif
   const char *map;
-  int fd = open (fn, O_RDONLY | O_BINARY);
+  int fd;
+
+  /* Paranoia.  */
+  if (fn == NULL || * fn == 0 || size_return == NULL || fst_return == NULL)
+    return NULL;
+
+  fd = open (fn, O_RDONLY | O_BINARY);
+
+#if HAVE_LIBDEBUGINFOD
+  if (fd < 0 && use_debuginfod && fn[0] == '/' && abfd != NULL)
+    {
+      unsigned char * build_id;
+      debuginfod_client * client;
+
+      client = debuginfod_begin ();
+      if (client == NULL)
+       return NULL;
+
+      build_id = get_build_id (abfd);
+      fd = debuginfod_find_source (client, build_id, 0, fn, NULL);
+      free (build_id);
+      debuginfod_end (client);
+    }
+#endif
 
   if (fd < 0)
     return NULL;
-  if (fstat (fd, fst) < 0)
+
+  if (fstat (fd, fst_return) < 0)
     {
       close (fd);
       return NULL;
     }
-  *size = fst->st_size;
+
+  *size_return = fst_return->st_size;
+
 #ifdef HAVE_MMAP
-  msize = (*size + ps - 1) & ~(ps - 1);
+  ps = getpagesize ();
+  msize = (*size_return + ps - 1) & ~(ps - 1);
   map = mmap (NULL, msize, PROT_READ, MAP_SHARED, fd, 0);
   if (map != (char *) -1L)
     {
@@ -1727,8 +1914,9 @@ slurp_file (const char *fn, size_t *size, struct stat *fst)
       return map;
     }
 #endif
-  map = (const char *) malloc (*size);
-  if (!map || (size_t) read (fd, (char *) map, *size) != *size)
+
+  map = (const char *) malloc (*size_return);
+  if (!map || (size_t) read (fd, (char *) map, *size_return) != *size_return)
     {
       free ((void *) map);
       map = NULL;
@@ -1794,16 +1982,20 @@ index_file (const char *map, size_t size, unsigned int *maxline)
 }
 
 /* Tries to open MODNAME, and if successful adds a node to print_files
-   linked list and returns that node.  Returns NULL on failure.  */
+   linked list and returns that node.  Also fills in the stat structure
+   pointed to by FST_RETURN.  Returns NULL on failure.  */
 
 static struct print_file_list *
-try_print_file_open (const char *origname, const char *modname, struct stat *fst)
+try_print_file_open (const char *   origname,
+                    const char *   modname,
+                    struct stat *  fst_return,
+                    bfd *          abfd)
 {
   struct print_file_list *p;
 
   p = (struct print_file_list *) xmalloc (sizeof (struct print_file_list));
 
-  p->map = slurp_file (modname, &p->mapsize, fst);
+  p->map = slurp_file (modname, &p->mapsize, fst_return, abfd);
   if (p->map == NULL)
     {
       free (p);
@@ -1833,7 +2025,7 @@ update_source_path (const char *filename, bfd *abfd)
   struct stat fst;
   int i;
 
-  p = try_print_file_open (filename, filename, &fst);
+  p = try_print_file_open (filename, filename, &fst, abfd);
   if (p == NULL)
     {
       if (include_path_count == 0)
@@ -1849,7 +2041,7 @@ update_source_path (const char *filename, bfd *abfd)
          char *modname = concat (include_paths[i], "/", fname,
                                  (const char *) 0);
 
-         p = try_print_file_open (filename, modname, &fst);
+         p = try_print_file_open (filename, modname, &fst, abfd);
          if (p)
            break;
 
@@ -1919,10 +2111,52 @@ show_line (bfd *abfd, asection *section, bfd_vma addr_offset)
   if (! with_line_numbers && ! with_source_code)
     return;
 
+#ifdef HAVE_LIBDEBUGINFOD
+  {
+    bfd *debug_bfd;
+    const char *alt_filename = NULL;
+
+    if (use_debuginfod)
+      {
+       bfd *alt_bfd;
+
+       /* PR 29075: Check for separate debuginfo and .gnu_debugaltlink files.
+          They need to be passed to bfd_find_nearest_line_with_alt in case they
+          were downloaded from debuginfod.  Otherwise libbfd will attempt to
+          search for them and fail to locate them.  */
+       debug_bfd = find_separate_debug (abfd);
+       if (debug_bfd == NULL)
+         debug_bfd = abfd;
+
+       alt_bfd = find_alt_debug (debug_bfd);
+       if (alt_bfd != NULL)
+         alt_filename = bfd_get_filename (alt_bfd);
+      }
+    else
+      debug_bfd = abfd;
+
+    bfd_set_error (bfd_error_no_error);
+    if (! bfd_find_nearest_line_with_alt (debug_bfd, alt_filename,
+                                         section, syms,
+                                         addr_offset, &filename,
+                                         &functionname, &linenumber,
+                                         &discriminator))
+      {
+       if (bfd_get_error () == bfd_error_no_error)
+         return;
+       if (! bfd_find_nearest_line_discriminator (abfd, section, syms,
+                                                  addr_offset, &filename,
+                                                  &functionname, &linenumber,
+                                                  &discriminator))
+         return;
+      }
+  }
+#else
   if (! bfd_find_nearest_line_discriminator (abfd, section, syms, addr_offset,
                                             &filename, &functionname,
                                             &linenumber, &discriminator))
     return;
+#endif
 
   if (filename != NULL && *filename == '\0')
     filename = NULL;
@@ -2118,6 +2352,167 @@ objdump_sprintf (SFILE *f, const char *format, ...)
   return n;
 }
 
+/* Return an integer greater than, or equal to zero, representing the color
+   for STYLE, or -1 if no color should be used.  */
+
+static int
+objdump_color_for_disassembler_style (enum disassembler_style style)
+{
+  int color = -1;
+
+  if (style == dis_style_comment_start)
+    disassembler_in_comment = true;
+
+  if (disassembler_color == on)
+    {
+      if (disassembler_in_comment)
+       return color;
+
+      switch (style)
+       {
+       case dis_style_symbol:
+         color = 32;
+         break;
+        case dis_style_assembler_directive:
+       case dis_style_sub_mnemonic:
+       case dis_style_mnemonic:
+         color = 33;
+         break;
+       case dis_style_register:
+         color = 34;
+         break;
+       case dis_style_address:
+        case dis_style_address_offset:
+       case dis_style_immediate:
+         color = 35;
+         break;
+       default:
+       case dis_style_text:
+         color = -1;
+         break;
+       }
+    }
+  else if (disassembler_color == extended)
+    {
+      if (disassembler_in_comment)
+       return 250;
+
+      switch (style)
+       {
+       case dis_style_symbol:
+         color = 40;
+         break;
+        case dis_style_assembler_directive:
+       case dis_style_sub_mnemonic:
+       case dis_style_mnemonic:
+         color = 142;
+         break;
+       case dis_style_register:
+         color = 27;
+         break;
+       case dis_style_address:
+        case dis_style_address_offset:
+       case dis_style_immediate:
+         color = 134;
+         break;
+       default:
+       case dis_style_text:
+         color = -1;
+         break;
+       }
+    }
+  else if (disassembler_color != off)
+    bfd_fatal (_("disassembly color not correctly selected"));
+
+  return color;
+}
+
+/* Like objdump_sprintf, but add in escape sequences to highlight the
+   content according to STYLE.  */
+
+static int ATTRIBUTE_PRINTF_3
+objdump_styled_sprintf (SFILE *f, enum disassembler_style style,
+                       const char *format, ...)
+{
+  size_t n;
+  va_list args;
+  int color = objdump_color_for_disassembler_style (style);
+
+  if (color >= 0)
+    {
+      while (1)
+       {
+         size_t space = f->alloc - f->pos;
+
+         if (disassembler_color == on)
+           n = snprintf (f->buffer + f->pos, space, "\033[%dm", color);
+         else
+           n = snprintf (f->buffer + f->pos, space, "\033[38;5;%dm", color);
+         if (space > n)
+           break;
+
+         f->alloc = (f->alloc + n) * 2;
+         f->buffer = (char *) xrealloc (f->buffer, f->alloc);
+       }
+      f->pos += n;
+    }
+
+  while (1)
+    {
+      size_t space = f->alloc - f->pos;
+
+      va_start (args, format);
+      n = vsnprintf (f->buffer + f->pos, space, format, args);
+      va_end (args);
+
+      if (space > n)
+       break;
+
+      f->alloc = (f->alloc + n) * 2;
+      f->buffer = (char *) xrealloc (f->buffer, f->alloc);
+    }
+  f->pos += n;
+
+  if (color >= 0)
+    {
+      while (1)
+       {
+         size_t space = f->alloc - f->pos;
+
+         n = snprintf (f->buffer + f->pos, space, "\033[0m");
+
+         if (space > n)
+           break;
+
+         f->alloc = (f->alloc + n) * 2;
+         f->buffer = (char *) xrealloc (f->buffer, f->alloc);
+       }
+      f->pos += n;
+    }
+
+  return n;
+}
+
+/* We discard the styling information here.  This function is only used
+   when objdump is printing auxiliary information, the symbol headers, and
+   disassembly address, or the bytes of the disassembled instruction.  We
+   don't (currently) apply styling to any of this stuff, so, for now, just
+   print the content with no additional style added.  */
+
+static int ATTRIBUTE_PRINTF_3
+fprintf_styled (FILE *f, enum disassembler_style style ATTRIBUTE_UNUSED,
+               const char *fmt, ...)
+{
+  int res;
+  va_list ap;
+
+  va_start (ap, fmt);
+  res = vfprintf (f, fmt, ap);
+  va_end (ap);
+
+  return res;
+}
+
 /* Code for generating (colored) diagrams of control flow start and end
    points.  */
 
@@ -2549,8 +2944,8 @@ disassemble_jumps (struct disassemble_info * inf,
   sfile.pos = 0;
 
   inf->insn_info_valid = 0;
-  inf->fprintf_func = (fprintf_ftype) objdump_sprintf;
-  inf->stream = &sfile;
+  disassemble_set_printf (inf, &sfile, (fprintf_ftype) objdump_sprintf,
+                         (fprintf_styled_ftype) objdump_styled_sprintf);
 
   addr_offset = start_offset;
   while (addr_offset < stop_offset)
@@ -2612,6 +3007,7 @@ disassemble_jumps (struct disassemble_info * inf,
 
       /* Extract jump information.  */
       inf->insn_info_valid = 0;
+      disassembler_in_comment = false;
       octets = (*disassemble_fn) (section->vma + addr_offset, inf);
       /* Test if a jump was detected.  */
       if (inf->insn_info_valid
@@ -2632,9 +3028,8 @@ disassemble_jumps (struct disassemble_info * inf,
       addr_offset += octets / opb;
     }
 
-  inf->fprintf_func = (fprintf_ftype) fprintf;
-  inf->stream = stdout;
-
+  disassemble_set_printf (inf, (void *) stdout, (fprintf_ftype) fprintf,
+                         (fprintf_styled_ftype) fprintf_styled);
   free (sfile.buffer);
 
   /* Merge jumps.  */
@@ -2727,6 +3122,17 @@ null_print (const void * stream ATTRIBUTE_UNUSED, const char * format ATTRIBUTE_
   return 1;
 }
 
+/* Like null_print, but takes the extra STYLE argument.  As this is not
+   going to print anything, the extra argument is just ignored.  */
+
+static int
+null_styled_print (const void * stream ATTRIBUTE_UNUSED,
+                  enum disassembler_style style ATTRIBUTE_UNUSED,
+                  const char * format ATTRIBUTE_UNUSED, ...)
+{
+  return 1;
+}
+
 /* Print out jump visualization.  */
 
 static void
@@ -2937,8 +3343,9 @@ disassemble_bytes (struct disassemble_info *inf,
              int insn_size;
 
              sfile.pos = 0;
-             inf->fprintf_func = (fprintf_ftype) objdump_sprintf;
-             inf->stream = &sfile;
+             disassemble_set_printf
+               (inf, &sfile, (fprintf_ftype) objdump_sprintf,
+                (fprintf_styled_ftype) objdump_styled_sprintf);
              inf->bytes_per_line = 0;
              inf->bytes_per_chunk = 0;
              inf->flags = ((disassemble_all ? DISASSEMBLE_DATA : 0)
@@ -2980,10 +3387,15 @@ disassemble_bytes (struct disassemble_info *inf,
                             twice, but we only do this when there is a high
                             probability that there is a reloc that will
                             affect the instruction.  */
-                         inf->fprintf_func = (fprintf_ftype) null_print;
+                         disassemble_set_printf
+                           (inf, inf->stream, (fprintf_ftype) null_print,
+                            (fprintf_styled_ftype) null_styled_print);
                          insn_size = disassemble_fn (section->vma
                                                      + addr_offset, inf);
-                         inf->fprintf_func = (fprintf_ftype) objdump_sprintf;
+                         disassemble_set_printf
+                           (inf, inf->stream,
+                            (fprintf_ftype) objdump_sprintf,
+                            (fprintf_styled_ftype) objdump_styled_sprintf);
                        }
                    }
 
@@ -3008,12 +3420,13 @@ disassemble_bytes (struct disassemble_info *inf,
                inf->stop_vma = section->vma + stop_offset;
 
              inf->stop_offset = stop_offset;
+             disassembler_in_comment = false;
              insn_size = (*disassemble_fn) (section->vma + addr_offset, inf);
              octets = insn_size;
 
              inf->stop_vma = 0;
-             inf->fprintf_func = (fprintf_ftype) fprintf;
-             inf->stream = stdout;
+             disassemble_set_printf (inf, stdout, (fprintf_ftype) fprintf,
+                                     (fprintf_styled_ftype) fprintf_styled);
              if (insn_width == 0 && inf->bytes_per_line != 0)
                octets_per_line = inf->bytes_per_line;
              if (insn_size < (int) opb)
@@ -3574,8 +3987,9 @@ disassemble_section (bfd *abfd, asection *section, void *inf)
              sf.alloc = strlen (sym->name) + 40;
              sf.buffer = (char*) xmalloc (sf.alloc);
              sf.pos = 0;
-             di.fprintf_func = (fprintf_ftype) objdump_sprintf;
-             di.stream = &sf;
+             disassemble_set_printf
+               (&di, &sf, (fprintf_ftype) objdump_sprintf,
+                (fprintf_styled_ftype) objdump_styled_sprintf);
 
              objdump_print_symname (abfd, &di, sym);
 
@@ -3644,8 +4058,8 @@ disassemble_data (bfd *abfd)
       ++sorted_symcount;
     }
 
-  init_disassemble_info (&disasm_info, stdout, (fprintf_ftype) fprintf);
-
+  init_disassemble_info (&disasm_info, stdout, (fprintf_ftype) fprintf,
+                        (fprintf_styled_ftype) fprintf_styled);
   disasm_info.application_data = (void *) &aux;
   aux.abfd = abfd;
   aux.require_sec = false;
@@ -3826,7 +4240,7 @@ load_specific_debug_section (enum dwarf_section_display_enum debug,
 }
 
 bool
-reloc_at (struct dwarf_section * dsec, dwarf_vma offset)
+reloc_at (struct dwarf_section * dsec, uint64_t offset)
 {
   arelent ** relocs;
   arelent * rp;
@@ -3851,6 +4265,9 @@ load_debug_section (enum dwarf_section_display_enum debug, void *file)
   asection *sec;
   const char *name;
 
+  if (!dump_any_debugging)
+    return false;
+
   /* If it is already loaded, do nothing.  */
   if (section->start != NULL)
     {
@@ -3888,6 +4305,9 @@ free_debug_section (enum dwarf_section_display_enum debug)
   section->start = NULL;
   section->address = 0;
   section->size = 0;
+  free ((char*) section->reloc_info);
+  section->reloc_info = NULL;
+  section->num_relocs= 0;
 }
 
 void
@@ -3913,44 +4333,22 @@ open_debug_file (const char * pathname)
   return data;
 }
 
-#if HAVE_LIBDEBUGINFOD
-/* Return a hex string represention of the build-id.  */
-
-unsigned char *
-get_build_id (void * data)
-{
-  unsigned i;
-  char * build_id_str;
-  bfd * abfd = (bfd *) data;
-  const struct bfd_build_id * build_id;
-
-  build_id = abfd->build_id;
-  if (build_id == NULL)
-    return NULL;
-
-  build_id_str = malloc (build_id->size * 2 + 1);
-  if (build_id_str == NULL)
-    return NULL;
-
-  for (i = 0; i < build_id->size; i++)
-    sprintf (build_id_str + (i * 2), "%02x", build_id->data[i]);
-  build_id_str[build_id->size * 2] = '\0';
-
-  return (unsigned char *)build_id_str;
-}
-#endif /* HAVE_LIBDEBUGINFOD */
-
 static void
 dump_dwarf_section (bfd *abfd, asection *section,
-                   void *arg ATTRIBUTE_UNUSED)
+                   void *arg)
 {
   const char *name = bfd_section_name (section);
   const char *match;
   int i;
+  bool is_mainfile = *(bool *) arg;
 
   if (*name == 0)
     return;
 
+  if (!is_mainfile && !process_links
+      && (section->flags & SEC_DEBUGGING) == 0)
+    return;
+
   if (startswith (name, ".gnu.linkonce.wi."))
     match = ".debug_info";
   else
@@ -3986,7 +4384,7 @@ dump_dwarf_section (bfd *abfd, asection *section,
 /* Dump the dwarf debugging information.  */
 
 static void
-dump_dwarf (bfd *abfd)
+dump_dwarf (bfd *abfd, bool is_mainfile)
 {
   /* The byte_get pointer should have been set at the start of dump_bfd().  */
   if (byte_get == NULL)
@@ -4012,7 +4410,7 @@ dump_dwarf (bfd *abfd)
   init_dwarf_regnames_by_bfd_arch_and_mach (bfd_get_arch (abfd),
                                            bfd_get_mach (abfd));
 
-  bfd_map_over_sections (abfd, dump_dwarf_section, NULL);
+  bfd_map_over_sections (abfd, dump_dwarf_section, (void *) &is_mainfile);
 }
 \f
 /* Read ABFD's stabs section STABSECT_NAME, and return a pointer to
@@ -4708,7 +5106,7 @@ dump_reloc_set (bfd *abfd, asection *sec, arelent **relpp, long relcount)
        bfd_sprintf_vma (abfd, buf, (bfd_vma) -1);
        width = strlen (buf) - 7;
       }
-    printf ("OFFSET %*s TYPE %*s VALUE \n", width, "", 12, "");
+    printf ("OFFSET %*s TYPE %*s VALUE\n", width, "", 12, "");
   }
 
   last_filename = NULL;
@@ -4995,6 +5393,26 @@ sign_extend_address (bfd *abfd ATTRIBUTE_UNUSED,
   return (((vma & ((mask << 1) - 1)) ^ mask) - mask);
 }
 
+static bool
+might_need_separate_debug_info (bool is_mainfile)
+{
+  /* We do not follow links from debug info files.  */
+  if (! is_mainfile)
+    return false;
+
+  /* Since do_follow_links might be enabled by default, only treat it as an
+     indication that separate files should be loaded if setting it was a
+     deliberate user action.  */
+  if (DEFAULT_FOR_FOLLOW_LINKS == 0 && do_follow_links)
+    return true;
+  
+  if (process_links || dump_symtab || dump_debugging
+      || dump_dwarf_section_info)
+    return true;
+
+  return false;  
+}
+
 /* Dump selected contents of ABFD.  */
 
 static void
@@ -5009,16 +5427,8 @@ dump_bfd (bfd *abfd, bool is_mainfile)
   else
     byte_get = NULL;
 
-  /* Load any separate debug information files.
-     We do this now and without checking do_follow_links because separate
-     debug info files may contain symbol tables that we will need when
-     displaying information about the main file.  Any memory allocated by
-     load_separate_debug_files will be released when we call
-     free_debug_memory below.
-
-     The test on is_mainfile is there because the chain of separate debug
-     info files is a global variable shared by all invocations of dump_bfd.  */
-  if (byte_get != NULL && is_mainfile)
+  /* Load any separate debug information files.  */
+  if (byte_get != NULL && might_need_separate_debug_info (is_mainfile))
     {
       load_separate_debug_files (abfd, bfd_get_filename (abfd));
 
@@ -5053,23 +5463,23 @@ dump_bfd (bfd *abfd, bool is_mainfile)
       bfd_map_over_sections (abfd, adjust_addresses, &has_reloc);
     }
 
-  if (! is_mainfile && ! process_links)
-    return;
-
-  if (! dump_debugging_tags && ! suppress_bfd_header)
-    printf (_("\n%s:     file format %s\n"),
-           sanitize_string (bfd_get_filename (abfd)),
-           abfd->xvec->name);
-  if (dump_ar_hdrs)
-    print_arelt_descr (stdout, abfd, true, false);
-  if (dump_file_header)
-    dump_bfd_header (abfd);
-  if (dump_private_headers)
-    dump_bfd_private_header (abfd);
-  if (dump_private_options != NULL)
-    dump_target_specific (abfd);
-  if (! dump_debugging_tags && ! suppress_bfd_header)
-    putchar ('\n');
+  if (is_mainfile || process_links)
+    {
+      if (! dump_debugging_tags && ! suppress_bfd_header)
+       printf (_("\n%s:     file format %s\n"),
+               sanitize_string (bfd_get_filename (abfd)),
+               abfd->xvec->name);
+      if (dump_ar_hdrs)
+       print_arelt_descr (stdout, abfd, true, false);
+      if (dump_file_header)
+       dump_bfd_header (abfd);
+      if (dump_private_headers)
+       dump_bfd_private_header (abfd);
+      if (dump_private_options != NULL)
+       dump_target_specific (abfd);
+      if (! dump_debugging_tags && ! suppress_bfd_header)
+       putchar ('\n');
+    }
 
   if (dump_symtab
       || dump_reloc_info
@@ -5112,39 +5522,46 @@ dump_bfd (bfd *abfd, bool is_mainfile)
        }
     }
 
-  if (dump_section_headers)
-    dump_headers (abfd);
+  if (is_mainfile || process_links)
+    {
+      if (dump_section_headers)
+       dump_headers (abfd);
 
-  if (dump_dynamic_symtab || dump_dynamic_reloc_info
-      || (disassemble && bfd_get_dynamic_symtab_upper_bound (abfd) > 0))
-    dynsyms = slurp_dynamic_symtab (abfd);
+      if (dump_dynamic_symtab || dump_dynamic_reloc_info
+         || (disassemble && bfd_get_dynamic_symtab_upper_bound (abfd) > 0))
+       dynsyms = slurp_dynamic_symtab (abfd);
 
-  if (disassemble)
-    {
-      synthcount = bfd_get_synthetic_symtab (abfd, symcount, syms,
-                                            dynsymcount, dynsyms, &synthsyms);
-      if (synthcount < 0)
-       synthcount = 0;
-    }
+      if (disassemble)
+       {
+         synthcount = bfd_get_synthetic_symtab (abfd, symcount, syms,
+                                                dynsymcount, dynsyms,
+                                                &synthsyms);
+         if (synthcount < 0)
+           synthcount = 0;
+       }
 
-  if (dump_symtab)
-    dump_symbols (abfd, false);
-  if (dump_dynamic_symtab)
-    dump_symbols (abfd, true);
+      if (dump_symtab)
+       dump_symbols (abfd, false);
+      if (dump_dynamic_symtab)
+       dump_symbols (abfd, true);
+    }
   if (dump_dwarf_section_info)
-    dump_dwarf (abfd);
-  if (dump_ctf_section_info)
-    dump_ctf (abfd, dump_ctf_section_name, dump_ctf_parent_name);
-  if (dump_stab_section_info)
-    dump_stabs (abfd);
-  if (dump_reloc_info && ! disassemble)
-    dump_relocs (abfd);
-  if (dump_dynamic_reloc_info && ! disassemble)
-    dump_dynamic_relocs (abfd);
-  if (dump_section_contents)
-    dump_data (abfd);
-  if (disassemble)
-    disassemble_data (abfd);
+    dump_dwarf (abfd, is_mainfile);
+  if (is_mainfile || process_links)
+    {
+      if (dump_ctf_section_info)
+       dump_ctf (abfd, dump_ctf_section_name, dump_ctf_parent_name);
+      if (dump_stab_section_info)
+       dump_stabs (abfd);
+      if (dump_reloc_info && ! disassemble)
+       dump_relocs (abfd);
+      if (dump_dynamic_reloc_info && ! disassemble)
+       dump_dynamic_relocs (abfd);
+      if (dump_section_contents)
+       dump_data (abfd);
+      if (disassemble)
+       disassemble_data (abfd);
+    }
 
   if (dump_debugging)
     {
@@ -5169,7 +5586,7 @@ dump_bfd (bfd *abfd, bool is_mainfile)
       else if (! dump_dwarf_section_info)
        {
          dwarf_select_sections_all ();
-         dump_dwarf (abfd);
+         dump_dwarf (abfd, is_mainfile);
        }
     }
 
@@ -5214,7 +5631,6 @@ display_object_bfd (bfd *abfd)
     {
       nonfatal (bfd_get_filename (abfd));
       list_matching_formats (matching);
-      free (matching);
       return;
     }
 
@@ -5233,10 +5649,7 @@ display_object_bfd (bfd *abfd)
   nonfatal (bfd_get_filename (abfd));
 
   if (bfd_get_error () == bfd_error_file_ambiguously_recognized)
-    {
-      list_matching_formats (matching);
-      free (matching);
-    }
+    list_matching_formats (matching);
 }
 
 static void
@@ -5471,6 +5884,16 @@ main (int argc, char **argv)
                nonfatal (_("unrecognized argument to --visualize-option"));
            }
          break;
+       case OPTION_DISASSEMBLER_COLOR:
+         if (streq (optarg, "off"))
+           disassembler_color = off;
+         else if (streq (optarg, "color"))
+           disassembler_color = on;
+         else if (streq (optarg, "extended-color"))
+           disassembler_color = extended;
+         else
+           nonfatal (_("unrecognized argument to --disassembler-color"));
+         break;
        case 'E':
          if (strcmp (optarg, "B") == 0)
            endian = BFD_ENDIAN_BIG;
@@ -5573,20 +5996,30 @@ main (int argc, char **argv)
          do_follow_links = true;
          break;
        case 'W':
-         dump_dwarf_section_info = true;
          seenflag = true;
          if (optarg)
-           dwarf_select_sections_by_letters (optarg);
+           {
+             if (dwarf_select_sections_by_letters (optarg))
+               dump_dwarf_section_info = true;
+           }
          else
-           dwarf_select_sections_all ();
+           {
+             dump_dwarf_section_info = true;
+             dwarf_select_sections_all ();
+           }
          break;
        case OPTION_DWARF:
-         dump_dwarf_section_info = true;
          seenflag = true;
          if (optarg)
-           dwarf_select_sections_by_names (optarg);
+           {
+             if (dwarf_select_sections_by_names (optarg))
+               dump_dwarf_section_info = true;
+           }
          else
-           dwarf_select_sections_all ();
+           {
+             dwarf_select_sections_all ();
+             dump_dwarf_section_info = true;
+           }
          break;
        case OPTION_DWARF_DEPTH:
          {
@@ -5670,12 +6103,19 @@ main (int argc, char **argv)
        }
     }
 
+  if (disassembler_color == on_if_terminal_output)
+    disassembler_color = isatty (1) ? on : off;
+
   if (show_version)
     print_version ("objdump");
 
   if (!seenflag)
     usage (stderr, 2);
 
+  dump_any_debugging = (dump_debugging
+                       || dump_dwarf_section_info
+                       || process_links);
+
   if (formats_info)
     exit_status = display_info ();
   else