RFC: Disassembly with call frame information

Message ID 87edhqiowz.fsf@redhat.com
State New
Headers
Series RFC: Disassembly with call frame information |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 fail Testing failed
linaro-tcwg-bot/tcwg_binutils_check--master-arm fail Testing failed

Commit Message

Nick Clifton Oct. 19, 2023, 3:59 p.m. UTC
  Hi Guys,

  I am currently working on a patch (attached) to add an option to the
  disassembler to display call frame information alongside the
  disassembly.  This is a work in progress, but I wanted to see if
  anyone was interested in the current state or had suggestions for
  improvements.

  The output currently looks like this:

    $ objdump hello.o --disassemble-with-cfi

    hello.o:     file format elf64-x86-64

    Disassembly of section .text:

    0000000000000000 <main>:
       0:	55                   	push   %rbp            # cfi: rsp+8    
       1:	48 89 e5             	mov    %rsp,%rbp       # cfi: rsp+16   
       4:	bf 00 00 00 00       	mov    $0x0,%edi       # cfi: rbp+16   
       9:	b8 00 00 00 00       	mov    $0x0,%eax
       e:	e8 00 00 00 00       	call   13 <main+0x13>
      13:	5d                   	pop    %rbp
      14:	c3                   	ret                    # cfi: rsp+8    

  The patch works by taking advantage of the code in the dwarf library
  to display interpreted call frame information.  This interpreted
  information is recorded and then replayed as the disassembly takes
  place.

  I am not sure how much information to display.  Currently I am just
  showing the CFA assignments, but maybe I need to include the rbp and
  ra updates as well ?

    $ objdump hello.o --dwarf=frames-interp

    Contents of the .eh_frame section:
    00000018 000000000000001c 0000001c FDE cie=00000000 pc=0000000000000000..0000000000000015
       LOC           CFA      rbp   ra    
    0000000000000000 rsp+8    u     c-8   
    0000000000000001 rsp+16   c-16  c-8   
    0000000000000004 rbp+16   c-16  c-8   
    0000000000000014 rsp+8    c-16  c-8   

  Anyway that is where I am at the moment.

Cheers
  Nick
  

Comments

Hans-Peter Nilsson Oct. 20, 2023, 10:25 p.m. UTC | #1
Just MHO since you asked for it:

On Thu, 19 Oct 2023, Nick Clifton wrote:
> Hi Guys,
> 
>   I am currently working on a patch (attached) to add an option to the
>   disassembler to display call frame information alongside the
>   disassembly.  This is a work in progress, but I wanted to see if
>   anyone was interested in the current state or had suggestions for
>   improvements.

That looks useful.

>   I am not sure how much information to display.  Currently I am just
>   showing the CFA assignments, but maybe I need to include the rbp and
>   ra updates as well ?

Sounds like the information would otherwise be incomplete, with 
references to those registers but not displaying their values; 
initial and as they change.  Also, there'd be nice symmetry if 
--disassemble-with-cfi has the same information as 
--dwarf=frames-interp.  Maybe tie the option names better?
Though --disassemble-with-dwarf-frames-interp seems chewy.

brgds, H-P
  
Indu Bhagat Oct. 22, 2023, 6:49 a.m. UTC | #2
On 10/20/23 3:25 PM, Hans-Peter Nilsson wrote:
> Just MHO since you asked for it:
> 
> On Thu, 19 Oct 2023, Nick Clifton wrote:
>> Hi Guys,
>>
>>    I am currently working on a patch (attached) to add an option to the
>>    disassembler to display call frame information alongside the
>>    disassembly.  This is a work in progress, but I wanted to see if
>>    anyone was interested in the current state or had suggestions for
>>    improvements.
> 
> That looks useful.
> 
>>    I am not sure how much information to display.  Currently I am just
>>    showing the CFA assignments, but maybe I need to include the rbp and
>>    ra updates as well ?
> 
> Sounds like the information would otherwise be incomplete, with
> references to those registers but not displaying their values;
> initial and as they change.  Also, there'd be nice symmetry if
> --disassemble-with-cfi has the same information as
> --dwarf=frames-interp.  Maybe tie the option names better?
> Though --disassemble-with-dwarf-frames-interp seems chewy.
> 

I agree that this will be a useful functionality.

I also agree that displaying the same information as 
--dwarf=frames-interp is ideal; but if that is not feasible, at the 
minimum, we should include CFA, FP and RA.
  
Szabolcs Nagy Oct. 26, 2023, 2:04 p.m. UTC | #3
The 10/20/2023 18:25, Hans-Peter Nilsson wrote:
> >   I am not sure how much information to display.  Currently I am just
> >   showing the CFA assignments, but maybe I need to include the rbp and
> >   ra updates as well ?
> 
> Sounds like the information would otherwise be incomplete, with 
> references to those registers but not displaying their values; 
> initial and as they change.  Also, there'd be nice symmetry if 
> --disassemble-with-cfi has the same information as 
> --dwarf=frames-interp.  Maybe tie the option names better?
> Though --disassemble-with-dwarf-frames-interp seems chewy.

fwiw i'd do the cmdline like -r vs '-d -r' for relocs:

if user asks for both disasm and cfi then display the cfi inline
with the asm. or use -d --dwarf=frames-interp-inline.

i prefer --dwarf=frames to --dwarf=frames-interp as it is more
complete (e.g. target specific pseudo regs are not visible in
frames-interp). just interleaving the opcodes with the disasm
sounds good enough for me (the opcodes closely match the .cfi
directives in asm which users should be familiar with).
  

Patch

diff --git a/binutils/dwarf.c b/binutils/dwarf.c
index 584c737b9ec..eced2a006fb 100644
--- a/binutils/dwarf.c
+++ b/binutils/dwarf.c
@@ -96,6 +96,8 @@  int do_debug_aranges;
 int do_debug_ranges;
 int do_debug_frames;
 int do_debug_frames_interp;
+int do_debug_frames_record;
+int do_debug_frames_noshow;
 int do_debug_macinfo;
 int do_debug_str;
 int do_debug_str_offsets;
@@ -9015,39 +9017,47 @@  regname (unsigned int regno, int name_only_p)
 }
 
 static void
-frame_display_row (Frame_Chunk *fc, int *need_col_headers, unsigned int *max_regs)
+frame_display_row (Frame_Chunk *fc, bool *need_col_headers, unsigned int *max_regs)
 {
   unsigned int r;
   char tmp[100];
 
-  if (*max_regs != fc->ncols)
+  if (max_regs != NULL && *max_regs != fc->ncols)
     *max_regs = fc->ncols;
 
-  if (*need_col_headers)
+  if (need_col_headers != NULL && *need_col_headers)
     {
-      *need_col_headers = 0;
+      *need_col_headers = false;
 
       printf ("%-*s CFA      ", eh_addr_size * 2, "   LOC");
 
-      for (r = 0; r < *max_regs; r++)
-	if (fc->col_type[r] != DW_CFA_unreferenced)
-	  {
-	    if (r == fc->ra)
-	      printf ("ra    ");
-	    else
-	      printf ("%-5s ", regname (r, 1));
-	  }
+      if (max_regs != NULL)
+	{
+	  for (r = 0; r < *max_regs; r++)
+	    if (fc->col_type[r] != DW_CFA_unreferenced)
+	      {
+		if (r == fc->ra)
+		  printf ("ra    ");
+		else
+		  printf ("%-5s ", regname (r, 1));
+	      }
+	}
 
       printf ("\n");
     }
 
-  print_hex (fc->pc_begin, eh_addr_size);
+  if (max_regs != NULL)
+    print_hex (fc->pc_begin, eh_addr_size);
+
   if (fc->cfa_exp)
     strcpy (tmp, "exp");
   else
     sprintf (tmp, "%s%+d", regname (fc->cfa_reg, 1), (int) fc->cfa_offset);
   printf ("%-8s ", tmp);
 
+  if (max_regs == NULL)
+    return;
+  
   for (r = 0; r < fc->ncols; r++)
     {
       if (fc->col_type[r] != DW_CFA_unreferenced)
@@ -9082,6 +9092,7 @@  frame_display_row (Frame_Chunk *fc, int *need_col_headers, unsigned int *max_reg
 	  printf ("%-5s ", tmp);
 	}
     }
+
   printf ("\n");
 }
 
@@ -9269,6 +9280,73 @@  display_augmentation_data (const unsigned char * data, uint64_t len)
   display_data (i, data, len);
 }
 
+static Frame_Chunk * recorded_chunks = NULL;
+
+static void
+free_chunks (Frame_Chunk * chunks)
+{
+  while (chunks != NULL)
+    {
+      Frame_Chunk * rs = chunks;
+
+      chunks = rs->next;
+      free (rs->col_type);
+      free (rs->col_offset);
+      rs->next = NULL; /* Paranoia.  */
+      free (rs);
+    }
+}
+
+void *
+dwarf_frame_for_addr (uint64_t addr)
+{
+  Frame_Chunk * fc;
+
+  for (fc = recorded_chunks; fc != NULL; fc = fc->next)
+    if (fc->pc_begin == addr)
+      return (void *) fc;
+
+  return NULL;
+}
+
+void
+dwarf_display_frame (void * fc)
+{
+  frame_display_row ((Frame_Chunk *) fc, NULL, NULL);
+}
+
+void
+dwarf_release_frame_info (void)
+{
+  free_chunks (recorded_chunks);
+  recorded_chunks = NULL;
+}
+
+void
+dwarf_record_frame_info (void)
+{
+  do_debug_frames_record = 1;
+
+  if (do_debug_frames == 0)
+    {
+      do_debug_frames_noshow = 1;
+      do_debug_frames = 1;
+    }
+}
+
+static void
+record_frame_row (Frame_Chunk * fc)
+{
+  Frame_Chunk * new = XNEW (Frame_Chunk);
+
+  memcpy (new, fc, sizeof * fc);
+  new->col_type   = xmemdup (fc->col_type, fc->ncols, sizeof * fc->col_type);
+  new->col_offset = xmemdup (fc->col_offset, fc->ncols, sizeof * fc->col_offset);
+
+  new->next = recorded_chunks;
+  recorded_chunks = new;
+}
+
 static int
 display_debug_frames (struct dwarf_section *section,
 		      void *file ATTRIBUTE_UNUSED)
@@ -9284,7 +9362,8 @@  display_debug_frames (struct dwarf_section *section,
   const char *bad_reg = _("bad register: ");
   unsigned int saved_eh_addr_size = eh_addr_size;
 
-  introduce (section, false);
+  if (! do_debug_frames_noshow)
+    introduce (section, false);
 
   while (start < end)
     {
@@ -9294,7 +9373,7 @@  display_debug_frames (struct dwarf_section *section,
       uint64_t cie_id;
       Frame_Chunk *fc;
       Frame_Chunk *cie;
-      int need_col_headers = 1;
+      bool need_col_headers = true;
       unsigned char *augmentation_data = NULL;
       uint64_t augmentation_data_len = 0;
       unsigned int encoded_ptr_size = saved_eh_addr_size;
@@ -9308,7 +9387,8 @@  display_debug_frames (struct dwarf_section *section,
 
       if (length == 0)
 	{
-	  printf ("\n%08tx ZERO terminator\n\n",
+	  if (! do_debug_frames_noshow)
+	    printf ("\n%08tx ZERO terminator\n\n",
 		    saved_start - section_start);
 	  /* Skip any zero terminators that directly follow.
 	     A corrupt section size could have loaded a whole
@@ -9329,8 +9409,9 @@  display_debug_frames (struct dwarf_section *section,
 
       if (length > (size_t) (end - start))
 	{
-	  warn ("Invalid length %#" PRIx64 " in FDE at %#tx\n",
-		length, saved_start - section_start);
+	  if (! do_debug_frames_noshow)
+	    warn ("Invalid length %#" PRIx64 " in FDE at %#tx\n",
+		  length, saved_start - section_start);
 	  block_end = end;
 	}
       else
@@ -9362,33 +9443,36 @@  display_debug_frames (struct dwarf_section *section,
 	  if (fc->fde_encoding)
 	    encoded_ptr_size = size_of_encoded_value (fc->fde_encoding);
 
-	  printf ("\n%08tx ", saved_start - section_start);
-	  print_hex (length, fc->ptr_size);
-	  print_hex (cie_id, offset_size);
-
-	  if (do_debug_frames_interp)
+	  if (! do_debug_frames_noshow)
 	    {
-	      printf ("CIE \"%s\" cf=%d df=%d ra=%d\n", fc->augmentation,
-		      fc->code_factor, fc->data_factor, fc->ra);
-	    }
-	  else
-	    {
-	      printf ("CIE\n");
-	      printf ("  Version:               %d\n", version);
-	      printf ("  Augmentation:          \"%s\"\n", fc->augmentation);
-	      if (version >= 4)
+	      printf ("\n%08tx ", saved_start - section_start);
+	      print_hex (length, fc->ptr_size);
+	      print_hex (cie_id, offset_size);
+
+	      if (do_debug_frames_interp)
 		{
-		  printf ("  Pointer Size:          %u\n", fc->ptr_size);
-		  printf ("  Segment Size:          %u\n", fc->segment_size);
+		  printf ("CIE \"%s\" cf=%d df=%d ra=%d\n", fc->augmentation,
+			  fc->code_factor, fc->data_factor, fc->ra);
 		}
-	      printf ("  Code alignment factor: %u\n", fc->code_factor);
-	      printf ("  Data alignment factor: %d\n", fc->data_factor);
-	      printf ("  Return address column: %d\n", fc->ra);
+	      else
+		{
+		  printf ("CIE\n");
+		  printf ("  Version:               %d\n", version);
+		  printf ("  Augmentation:          \"%s\"\n", fc->augmentation);
+		  if (version >= 4)
+		    {
+		      printf ("  Pointer Size:          %u\n", fc->ptr_size);
+		      printf ("  Segment Size:          %u\n", fc->segment_size);
+		    }
+		  printf ("  Code alignment factor: %u\n", fc->code_factor);
+		  printf ("  Data alignment factor: %d\n", fc->data_factor);
+		  printf ("  Return address column: %d\n", fc->ra);
 
-	      if (augmentation_data_len)
-		display_augmentation_data (augmentation_data, augmentation_data_len);
+		  if (augmentation_data_len)
+		    display_augmentation_data (augmentation_data, augmentation_data_len);
 
-	      putchar ('\n');
+		  putchar ('\n');
+		}
 	    }
 	}
       else
@@ -9419,6 +9503,7 @@  display_debug_frames (struct dwarf_section *section,
 	      for (cie = forward_refs; cie ; cie = cie->next)
 		if (cie->chunk_start == look_for)
 		  break;
+
 	      if (!cie)
 		{
 		  unsigned int off_size;
@@ -9452,7 +9537,8 @@  display_debug_frames (struct dwarf_section *section,
 			  /* PR 17512: file: 3450-2098-0.004.  */
 			  if (cie == NULL)
 			    {
-			      warn (_("Failed to read CIE information\n"));
+			      if (! do_debug_frames_noshow)
+				warn (_("Failed to read CIE information\n"));
 			      break;
 			    }
 			  cie->next = forward_refs;
@@ -9463,7 +9549,8 @@  display_debug_frames (struct dwarf_section *section,
 			    mreg = cie->ra;
 			  if (frame_need_space (cie, mreg) < 0)
 			    {
-			      warn (_("Invalid max register\n"));
+			      if (! do_debug_frames_noshow)
+				warn (_("Invalid max register\n"));
 			      break;
 			    }
 			  if (cie->fde_encoding)
@@ -9484,7 +9571,8 @@  display_debug_frames (struct dwarf_section *section,
 	      fc->col_offset = xmalloc (sizeof (*fc->col_offset));
 	      if (frame_need_space (fc, max_regs > 0 ? max_regs - 1 : 0) < 0)
 		{
-		  warn (_("Invalid max register\n"));
+		  if (! do_debug_frames_noshow)
+		    warn (_("Invalid max register\n"));
 		  break;
 		}
 	      cie = fc;
@@ -9513,7 +9601,8 @@  display_debug_frames (struct dwarf_section *section,
 	      fc->ra = cie->ra;
 	      if (frame_need_space (fc, max_regs > 0 ? max_regs - 1: 0) < 0)
 		{
-		  warn (_("Invalid max register\n"));
+		  if (! do_debug_frames_noshow)
+		    warn (_("Invalid max register\n"));
 		  break;
 		}
 	      fc->fde_encoding = cie->fde_encoding;
@@ -9528,7 +9617,8 @@  display_debug_frames (struct dwarf_section *section,
 	      if (fc->segment_size > sizeof (segment_selector))
 		{
 		  /* PR 17512: file: 9e196b3e.  */
-		  warn (_("Probably corrupt segment size: %d - using 4 instead\n"), fc->segment_size);
+		  if (! do_debug_frames_noshow)
+		    warn (_("Probably corrupt segment size: %d - using 4 instead\n"), fc->segment_size);
 		  fc->segment_size = 4;
 		}
 	      SAFE_BYTE_GET_AND_INC (segment_selector, start,
@@ -9552,9 +9642,10 @@  display_debug_frames (struct dwarf_section *section,
 	      /* PR 17512 file: 722-8446-0.004 and PR 22386.  */
 	      if (augmentation_data_len > (size_t) (block_end - start))
 		{
-		  warn (_("Augmentation data too long: %#" PRIx64 ", "
-			  "expected at most %#tx\n"),
-			augmentation_data_len, block_end - start);
+		  if (! do_debug_frames_noshow)
+		    warn (_("Augmentation data too long: %#" PRIx64 ", "
+			    "expected at most %#tx\n"),
+			  augmentation_data_len, block_end - start);
 		  start = block_end;
 		  augmentation_data = NULL;
 		  augmentation_data_len = 0;
@@ -9562,31 +9653,34 @@  display_debug_frames (struct dwarf_section *section,
 	      start += augmentation_data_len;
 	    }
 
-	  printf ("\n%08tx ", saved_start - section_start);
-	  print_hex (length, fc->ptr_size);
-	  print_hex (cie_id, offset_size);
-	  printf ("FDE ");
+	  if (! do_debug_frames_noshow)
+	    {
+	      printf ("\n%08tx ", saved_start - section_start);
+	      print_hex (length, fc->ptr_size);
+	      print_hex (cie_id, offset_size);
+	      printf ("FDE ");
 
-	  if (cie->chunk_start)
-	    printf ("cie=%08tx", cie->chunk_start - section_start);
-	  else
-	    /* Ideally translate "invalid " to 8 chars, trailing space
-	       is optional.  */
-	    printf (_("cie=invalid "));
+	      if (cie->chunk_start)
+		printf ("cie=%08tx", cie->chunk_start - section_start);
+	      else
+		/* Ideally translate "invalid " to 8 chars, trailing space
+		   is optional.  */
+		printf (_("cie=invalid "));
 
-	  printf (" pc=");
-	  if (fc->segment_size)
-	    printf ("%04lx:", segment_selector);
+	      printf (" pc=");
+	      if (fc->segment_size)
+		printf ("%04lx:", segment_selector);
 
-	  print_hex_ns (fc->pc_begin, fc->ptr_size);
-	  printf ("..");
-	  print_hex_ns (fc->pc_begin + fc->pc_range, fc->ptr_size);
-	  printf ("\n");
+	      print_hex_ns (fc->pc_begin, fc->ptr_size);
+	      printf ("..");
+	      print_hex_ns (fc->pc_begin + fc->pc_range, fc->ptr_size);
+	      printf ("\n");
 
-	  if (! do_debug_frames_interp && augmentation_data_len)
-	    {
-	      display_augmentation_data (augmentation_data, augmentation_data_len);
-	      putchar ('\n');
+	      if (! do_debug_frames_interp && augmentation_data_len)
+		{
+		  display_augmentation_data (augmentation_data, augmentation_data_len);
+		  putchar ('\n');
+		}
 	    }
 	}
 
@@ -9769,13 +9863,17 @@  display_debug_frames (struct dwarf_section *section,
 	    {
 	    case DW_CFA_advance_loc:
 	      opa *= fc->code_factor;
-	      if (do_debug_frames_interp)
-		frame_display_row (fc, &need_col_headers, &max_regs);
-	      else
+	      record_frame_row (fc);
+	      if (! do_debug_frames_noshow)
 		{
-		  printf ("  DW_CFA_advance_loc: %d to ", opa);
-		  print_hex_ns (fc->pc_begin + opa, fc->ptr_size);
-		  printf ("\n");
+		  if (do_debug_frames_interp)
+		    frame_display_row (fc, &need_col_headers, &max_regs);
+		  else
+		    {
+		      printf ("  DW_CFA_advance_loc: %d to ", opa);
+		      print_hex_ns (fc->pc_begin + opa, fc->ptr_size);
+		      printf ("\n");
+		    }
 		}
 	      fc->pc_begin += opa;
 	      break;
@@ -9785,9 +9883,12 @@  display_debug_frames (struct dwarf_section *section,
 	      ofs *= fc->data_factor;
 	      if (opa >= fc->ncols)
 		reg_prefix = bad_reg;
-	      if (! do_debug_frames_interp || *reg_prefix != '\0')
-		printf ("  DW_CFA_offset: %s%s at cfa%+" PRId64 "\n",
-			reg_prefix, regname (opa, 0), ofs);
+	      if (! do_debug_frames_noshow)
+		{
+		  if (! do_debug_frames_interp || *reg_prefix != '\0')
+		    printf ("  DW_CFA_offset: %s%s at cfa%+" PRId64 "\n",
+			    reg_prefix, regname (opa, 0), ofs);
+		}
 	      if (*reg_prefix == '\0')
 		{
 		  fc->col_type[opa] = DW_CFA_offset;
@@ -9798,9 +9899,12 @@  display_debug_frames (struct dwarf_section *section,
 	    case DW_CFA_restore:
 	      if (opa >= fc->ncols)
 		reg_prefix = bad_reg;
-	      if (! do_debug_frames_interp || *reg_prefix != '\0')
-		printf ("  DW_CFA_restore: %s%s\n",
-			reg_prefix, regname (opa, 0));
+	      if (! do_debug_frames_noshow)
+		{
+		  if (! do_debug_frames_interp || *reg_prefix != '\0')
+		    printf ("  DW_CFA_restore: %s%s\n",
+			    reg_prefix, regname (opa, 0));
+		}
 	      if (*reg_prefix != '\0')
 		break;
 
@@ -9820,13 +9924,17 @@  display_debug_frames (struct dwarf_section *section,
 	    case DW_CFA_set_loc:
 	      ofs = get_encoded_value (&start, fc->fde_encoding, section,
 				       block_end);
-	      if (do_debug_frames_interp)
-		frame_display_row (fc, &need_col_headers, &max_regs);
-	      else
+	      record_frame_row (fc);
+	      if (! do_debug_frames_noshow)
 		{
-		  printf ("  DW_CFA_set_loc: ");
-		  print_hex_ns (ofs, fc->ptr_size);
-		  printf ("\n");
+		  if (do_debug_frames_interp)
+		    frame_display_row (fc, &need_col_headers, &max_regs);
+		  else
+		    {
+		      printf ("  DW_CFA_set_loc: ");
+		      print_hex_ns (ofs, fc->ptr_size);
+		      printf ("\n");
+		    }
 		}
 	      fc->pc_begin = ofs;
 	      break;
@@ -9834,13 +9942,17 @@  display_debug_frames (struct dwarf_section *section,
 	    case DW_CFA_advance_loc1:
 	      SAFE_BYTE_GET_AND_INC (ofs, start, 1, block_end);
 	      ofs *= fc->code_factor;
-	      if (do_debug_frames_interp)
-		frame_display_row (fc, &need_col_headers, &max_regs);
-	      else
+	      record_frame_row (fc);
+	      if (! do_debug_frames_noshow)
 		{
-		  printf ("  DW_CFA_advance_loc1: %" PRId64 " to ", ofs);
-		  print_hex_ns (fc->pc_begin + ofs, fc->ptr_size);
-		  printf ("\n");
+		  if (do_debug_frames_interp)
+		    frame_display_row (fc, &need_col_headers, &max_regs);
+		  else
+		    {
+		      printf ("  DW_CFA_advance_loc1: %" PRId64 " to ", ofs);
+		      print_hex_ns (fc->pc_begin + ofs, fc->ptr_size);
+		      printf ("\n");
+		    }
 		}
 	      fc->pc_begin += ofs;
 	      break;
@@ -9848,13 +9960,18 @@  display_debug_frames (struct dwarf_section *section,
 	    case DW_CFA_advance_loc2:
 	      SAFE_BYTE_GET_AND_INC (ofs, start, 2, block_end);
 	      ofs *= fc->code_factor;
-	      if (do_debug_frames_interp)
-		frame_display_row (fc, &need_col_headers, &max_regs);
-	      else
+
+	      record_frame_row (fc);
+	      if (! do_debug_frames_noshow)
 		{
-		  printf ("  DW_CFA_advance_loc2: %" PRId64 " to ", ofs);
-		  print_hex_ns (fc->pc_begin + ofs, fc->ptr_size);
-		  printf ("\n");
+		  if (do_debug_frames_interp)
+		    frame_display_row (fc, &need_col_headers, &max_regs);
+		  else
+		    {
+		      printf ("  DW_CFA_advance_loc2: %" PRId64 " to ", ofs);
+		      print_hex_ns (fc->pc_begin + ofs, fc->ptr_size);
+		      printf ("\n");
+		    }
 		}
 	      fc->pc_begin += ofs;
 	      break;
@@ -9862,13 +9979,18 @@  display_debug_frames (struct dwarf_section *section,
 	    case DW_CFA_advance_loc4:
 	      SAFE_BYTE_GET_AND_INC (ofs, start, 4, block_end);
 	      ofs *= fc->code_factor;
-	      if (do_debug_frames_interp)
-		frame_display_row (fc, &need_col_headers, &max_regs);
-	      else
+
+	      record_frame_row (fc);
+	      if (! do_debug_frames_noshow)
 		{
-		  printf ("  DW_CFA_advance_loc4: %" PRId64 " to ", ofs);
-		  print_hex_ns (fc->pc_begin + ofs, fc->ptr_size);
-		  printf ("\n");
+		  if (do_debug_frames_interp)
+		    frame_display_row (fc, &need_col_headers, &max_regs);
+		  else
+		    {
+		      printf ("  DW_CFA_advance_loc4: %" PRId64 " to ", ofs);
+		      print_hex_ns (fc->pc_begin + ofs, fc->ptr_size);
+		      printf ("\n");
+		    }
 		}
 	      fc->pc_begin += ofs;
 	      break;
@@ -9879,9 +10001,12 @@  display_debug_frames (struct dwarf_section *section,
 	      ofs *= fc->data_factor;
 	      if (reg >= fc->ncols)
 		reg_prefix = bad_reg;
-	      if (! do_debug_frames_interp || *reg_prefix != '\0')
-		printf ("  DW_CFA_offset_extended: %s%s at cfa%+" PRId64 "\n",
-			reg_prefix, regname (reg, 0), ofs);
+	      if (! do_debug_frames_noshow)
+		{
+		  if (! do_debug_frames_interp || *reg_prefix != '\0')
+		    printf ("  DW_CFA_offset_extended: %s%s at cfa%+" PRId64 "\n",
+			    reg_prefix, regname (reg, 0), ofs);
+		}
 	      if (*reg_prefix == '\0')
 		{
 		  fc->col_type[reg] = DW_CFA_offset;
@@ -9895,9 +10020,12 @@  display_debug_frames (struct dwarf_section *section,
 	      ofs *= fc->data_factor;
 	      if (reg >= fc->ncols)
 		reg_prefix = bad_reg;
-	      if (! do_debug_frames_interp || *reg_prefix != '\0')
-		printf ("  DW_CFA_val_offset: %s%s is cfa%+" PRId64 "\n",
-			reg_prefix, regname (reg, 0), ofs);
+	      if (! do_debug_frames_noshow)
+		{
+		  if (! do_debug_frames_interp || *reg_prefix != '\0')
+		    printf ("  DW_CFA_val_offset: %s%s is cfa%+" PRId64 "\n",
+			    reg_prefix, regname (reg, 0), ofs);
+		}
 	      if (*reg_prefix == '\0')
 		{
 		  fc->col_type[reg] = DW_CFA_val_offset;
@@ -9909,9 +10037,12 @@  display_debug_frames (struct dwarf_section *section,
 	      READ_ULEB (reg, start, block_end);
 	      if (reg >= fc->ncols)
 		reg_prefix = bad_reg;
-	      if (! do_debug_frames_interp || *reg_prefix != '\0')
-		printf ("  DW_CFA_restore_extended: %s%s\n",
-			reg_prefix, regname (reg, 0));
+	      if (! do_debug_frames_noshow)
+		{
+		  if (! do_debug_frames_interp || *reg_prefix != '\0')
+		    printf ("  DW_CFA_restore_extended: %s%s\n",
+			    reg_prefix, regname (reg, 0));
+		}
 	      if (*reg_prefix != '\0')
 		break;
 
@@ -9932,9 +10063,12 @@  display_debug_frames (struct dwarf_section *section,
 	      READ_ULEB (reg, start, block_end);
 	      if (reg >= fc->ncols)
 		reg_prefix = bad_reg;
-	      if (! do_debug_frames_interp || *reg_prefix != '\0')
-		printf ("  DW_CFA_undefined: %s%s\n",
-			reg_prefix, regname (reg, 0));
+	      if (! do_debug_frames_noshow)
+		{
+		  if (! do_debug_frames_interp || *reg_prefix != '\0')
+		    printf ("  DW_CFA_undefined: %s%s\n",
+			    reg_prefix, regname (reg, 0));
+		}
 	      if (*reg_prefix == '\0')
 		{
 		  fc->col_type[reg] = DW_CFA_undefined;
@@ -9946,9 +10080,12 @@  display_debug_frames (struct dwarf_section *section,
 	      READ_ULEB (reg, start, block_end);
 	      if (reg >= fc->ncols)
 		reg_prefix = bad_reg;
-	      if (! do_debug_frames_interp || *reg_prefix != '\0')
-		printf ("  DW_CFA_same_value: %s%s\n",
-			reg_prefix, regname (reg, 0));
+	      if (! do_debug_frames_noshow)
+		{
+		  if (! do_debug_frames_interp || *reg_prefix != '\0')
+		    printf ("  DW_CFA_same_value: %s%s\n",
+			    reg_prefix, regname (reg, 0));
+		}
 	      if (*reg_prefix == '\0')
 		{
 		  fc->col_type[reg] = DW_CFA_same_value;
@@ -9961,11 +10098,14 @@  display_debug_frames (struct dwarf_section *section,
 	      READ_ULEB (ofs, start, block_end);
 	      if (reg >= fc->ncols)
 		reg_prefix = bad_reg;
-	      if (! do_debug_frames_interp || *reg_prefix != '\0')
+	      if (! do_debug_frames_noshow)
 		{
-		  printf ("  DW_CFA_register: %s%s in ",
-			  reg_prefix, regname (reg, 0));
-		  puts (regname (ofs, 0));
+		  if (! do_debug_frames_interp || *reg_prefix != '\0')
+		    {
+		      printf ("  DW_CFA_register: %s%s in ",
+			      reg_prefix, regname (reg, 0));
+		      puts (regname (ofs, 0));
+		    }
 		}
 	      if (*reg_prefix == '\0')
 		{
@@ -9975,8 +10115,11 @@  display_debug_frames (struct dwarf_section *section,
 	      break;
 
 	    case DW_CFA_remember_state:
-	      if (! do_debug_frames_interp)
-		printf ("  DW_CFA_remember_state\n");
+	      if (! do_debug_frames_noshow)
+		{
+		  if (! do_debug_frames_interp)
+		    printf ("  DW_CFA_remember_state\n");
+		}
 	      rs = (Frame_Chunk *) xmalloc (sizeof (Frame_Chunk));
 	      rs->cfa_offset = fc->cfa_offset;
 	      rs->cfa_reg = fc->cfa_reg;
@@ -9994,8 +10137,11 @@  display_debug_frames (struct dwarf_section *section,
 	      break;
 
 	    case DW_CFA_restore_state:
-	      if (! do_debug_frames_interp)
-		printf ("  DW_CFA_restore_state\n");
+	      if (! do_debug_frames_noshow)
+		{
+		  if (! do_debug_frames_interp)
+		    printf ("  DW_CFA_restore_state\n");
+		}
 	      rs = remembered_state;
 	      if (rs)
 		{
@@ -10006,7 +10152,8 @@  display_debug_frames (struct dwarf_section *section,
 		  fc->cfa_exp = rs->cfa_exp;
 		  if (frame_need_space (fc, rs->ncols - 1) < 0)
 		    {
-		      warn (_("Invalid column number in saved frame state\n"));
+		      if (! do_debug_frames_noshow)
+			warn (_("Invalid column number in saved frame state\n"));
 		      fc->ncols = 0;
 		    }
 		  else
@@ -10021,51 +10168,70 @@  display_debug_frames (struct dwarf_section *section,
 		  free (rs);
 		}
 	      else if (do_debug_frames_interp)
-		printf ("Mismatched DW_CFA_restore_state\n");
+		{
+		  if (! do_debug_frames_noshow)
+		    printf ("Mismatched DW_CFA_restore_state\n");
+		}
 	      break;
 
 	    case DW_CFA_def_cfa:
 	      READ_ULEB (fc->cfa_reg, start, block_end);
 	      READ_ULEB (fc->cfa_offset, start, block_end);
 	      fc->cfa_exp = 0;
-	      if (! do_debug_frames_interp)
-		printf ("  DW_CFA_def_cfa: %s ofs %d\n",
-			regname (fc->cfa_reg, 0), (int) fc->cfa_offset);
+	      if (! do_debug_frames_noshow)
+		{
+		  if (! do_debug_frames_interp)
+		    printf ("  DW_CFA_def_cfa: %s ofs %d\n",
+			    regname (fc->cfa_reg, 0), (int) fc->cfa_offset);
+		}
 	      break;
 
 	    case DW_CFA_def_cfa_register:
 	      READ_ULEB (fc->cfa_reg, start, block_end);
 	      fc->cfa_exp = 0;
-	      if (! do_debug_frames_interp)
-		printf ("  DW_CFA_def_cfa_register: %s\n",
-			regname (fc->cfa_reg, 0));
+	      if (! do_debug_frames_noshow)
+		{
+		  if (! do_debug_frames_interp)
+		    printf ("  DW_CFA_def_cfa_register: %s\n",
+			    regname (fc->cfa_reg, 0));
+		}
 	      break;
 
 	    case DW_CFA_def_cfa_offset:
 	      READ_ULEB (fc->cfa_offset, start, block_end);
-	      if (! do_debug_frames_interp)
-		printf ("  DW_CFA_def_cfa_offset: %d\n", (int) fc->cfa_offset);
+	      if (! do_debug_frames_noshow)
+		{
+		  if (! do_debug_frames_interp)
+		    printf ("  DW_CFA_def_cfa_offset: %d\n", (int) fc->cfa_offset);
+		}
 	      break;
 
 	    case DW_CFA_nop:
-	      if (! do_debug_frames_interp)
-		printf ("  DW_CFA_nop\n");
+	      if (! do_debug_frames_noshow)
+		{
+		  if (! do_debug_frames_interp)
+		    printf ("  DW_CFA_nop\n");
+		}
 	      break;
 
 	    case DW_CFA_def_cfa_expression:
 	      READ_ULEB (ofs, start, block_end);
 	      if (ofs > (size_t) (block_end - start))
 		{
-		  printf (_("  %s: <corrupt len %" PRIu64 ">\n"),
-			  "DW_CFA_def_cfa_expression", ofs);
+		  if (! do_debug_frames_noshow)
+		    printf (_("  %s: <corrupt len %" PRIu64 ">\n"),
+			    "DW_CFA_def_cfa_expression", ofs);
 		  break;
 		}
 	      if (! do_debug_frames_interp)
 		{
-		  printf ("  DW_CFA_def_cfa_expression (");
-		  decode_location_expression (start, eh_addr_size, 0, -1,
-					      ofs, 0, section);
-		  printf (")\n");
+		  if (! do_debug_frames_noshow)
+		    {
+		      printf ("  DW_CFA_def_cfa_expression (");
+		      decode_location_expression (start, eh_addr_size, 0, -1,
+						  ofs, 0, section);
+		      printf (")\n");
+		    }
 		}
 	      fc->cfa_exp = 1;
 	      start += ofs;
@@ -10080,17 +10246,21 @@  display_debug_frames (struct dwarf_section *section,
 	      /* PR 17512: file: 98c02eb4.  */
 	      if (ofs > (size_t) (block_end - start))
 		{
-		  printf (_("  %s: <corrupt len %" PRIu64 ">\n"),
-			  "DW_CFA_expression", ofs);
+		  if (! do_debug_frames_noshow)
+		    printf (_("  %s: <corrupt len %" PRIu64 ">\n"),
+			    "DW_CFA_expression", ofs);
 		  break;
 		}
 	      if (! do_debug_frames_interp || *reg_prefix != '\0')
 		{
-		  printf ("  DW_CFA_expression: %s%s (",
-			  reg_prefix, regname (reg, 0));
-		  decode_location_expression (start, eh_addr_size, 0, -1,
-					      ofs, 0, section);
-		  printf (")\n");
+		  if (! do_debug_frames_noshow)
+		    {
+		      printf ("  DW_CFA_expression: %s%s (",
+			      reg_prefix, regname (reg, 0));
+		      decode_location_expression (start, eh_addr_size, 0, -1,
+						  ofs, 0, section);
+		      printf (")\n");
+		    }
 		}
 	      if (*reg_prefix == '\0')
 		fc->col_type[reg] = DW_CFA_expression;
@@ -10104,17 +10274,21 @@  display_debug_frames (struct dwarf_section *section,
 		reg_prefix = bad_reg;
 	      if (ofs > (size_t) (block_end - start))
 		{
-		  printf ("  %s: <corrupt len %" PRIu64 ">\n",
-			  "DW_CFA_val_expression", ofs);
+		  if (! do_debug_frames_noshow)
+		    printf ("  %s: <corrupt len %" PRIu64 ">\n",
+			    "DW_CFA_val_expression", ofs);
 		  break;
 		}
 	      if (! do_debug_frames_interp || *reg_prefix != '\0')
 		{
-		  printf ("  DW_CFA_val_expression: %s%s (",
-			  reg_prefix, regname (reg, 0));
-		  decode_location_expression (start, eh_addr_size, 0, -1,
-					      ofs, 0, section);
-		  printf (")\n");
+		  if (! do_debug_frames_noshow)
+		    {
+		      printf ("  DW_CFA_val_expression: %s%s (",
+			      reg_prefix, regname (reg, 0));
+		      decode_location_expression (start, eh_addr_size, 0, -1,
+						  ofs, 0, section);
+		      printf (")\n");
+		    }
 		}
 	      if (*reg_prefix == '\0')
 		fc->col_type[reg] = DW_CFA_val_expression;
@@ -10131,9 +10305,12 @@  display_debug_frames (struct dwarf_section *section,
 	      ofs *= fc->data_factor;
 	      if (reg >= fc->ncols)
 		reg_prefix = bad_reg;
-	      if (! do_debug_frames_interp || *reg_prefix != '\0')
-		printf ("  DW_CFA_offset_extended_sf: %s%s at cfa%+" PRId64 "\n",
-			reg_prefix, regname (reg, 0), ofs);
+	      if (! do_debug_frames_noshow)
+		{
+		  if (! do_debug_frames_interp || *reg_prefix != '\0')
+		    printf ("  DW_CFA_offset_extended_sf: %s%s at cfa%+" PRId64 "\n",
+			    reg_prefix, regname (reg, 0), ofs);
+		}
 	      if (*reg_prefix == '\0')
 		{
 		  fc->col_type[reg] = DW_CFA_offset;
@@ -10148,9 +10325,12 @@  display_debug_frames (struct dwarf_section *section,
 	      ofs *= fc->data_factor;
 	      if (reg >= fc->ncols)
 		reg_prefix = bad_reg;
-	      if (! do_debug_frames_interp || *reg_prefix != '\0')
-		printf ("  DW_CFA_val_offset_sf: %s%s is cfa%+" PRId64 "\n",
-			reg_prefix, regname (reg, 0), ofs);
+	      if (! do_debug_frames_noshow)
+		{
+		  if (! do_debug_frames_interp || *reg_prefix != '\0')
+		    printf ("  DW_CFA_val_offset_sf: %s%s is cfa%+" PRId64 "\n",
+			    reg_prefix, regname (reg, 0), ofs);
+		}
 	      if (*reg_prefix == '\0')
 		{
 		  fc->col_type[reg] = DW_CFA_val_offset;
@@ -10165,9 +10345,12 @@  display_debug_frames (struct dwarf_section *section,
 	      ofs *= fc->data_factor;
 	      fc->cfa_offset = ofs;
 	      fc->cfa_exp = 0;
-	      if (! do_debug_frames_interp)
-		printf ("  DW_CFA_def_cfa_sf: %s ofs %" PRId64 "\n",
-			regname (fc->cfa_reg, 0), ofs);
+	      if (! do_debug_frames_noshow)
+		{
+		  if (! do_debug_frames_interp)
+		    printf ("  DW_CFA_def_cfa_sf: %s ofs %" PRId64 "\n",
+			    regname (fc->cfa_reg, 0), ofs);
+		}
 	      break;
 
 	    case DW_CFA_def_cfa_offset_sf:
@@ -10175,33 +10358,47 @@  display_debug_frames (struct dwarf_section *section,
 	      ofs = sofs;
 	      ofs *= fc->data_factor;
 	      fc->cfa_offset = ofs;
-	      if (! do_debug_frames_interp)
-		printf ("  DW_CFA_def_cfa_offset_sf: %" PRId64 "\n", ofs);
+	      if (! do_debug_frames_noshow)
+		{
+		  if (! do_debug_frames_interp)
+		    printf ("  DW_CFA_def_cfa_offset_sf: %" PRId64 "\n", ofs);
+		}
 	      break;
 
 	    case DW_CFA_MIPS_advance_loc8:
 	      SAFE_BYTE_GET_AND_INC (ofs, start, 8, block_end);
 	      ofs *= fc->code_factor;
-	      if (do_debug_frames_interp)
-		frame_display_row (fc, &need_col_headers, &max_regs);
-	      else
+
+	      record_frame_row (fc);
+	      if (! do_debug_frames_noshow)
 		{
-		  printf ("  DW_CFA_MIPS_advance_loc8: %" PRId64 " to ", ofs);
-		  print_hex_ns (fc->pc_begin + ofs, fc->ptr_size);
-		  printf ("\n");
+		  if (do_debug_frames_interp)
+		    frame_display_row (fc, &need_col_headers, &max_regs);
+		  else
+		    {
+		      printf ("  DW_CFA_MIPS_advance_loc8: %" PRId64 " to ", ofs);
+		      print_hex_ns (fc->pc_begin + ofs, fc->ptr_size);
+		      printf ("\n");
+		    }
 		}
 	      fc->pc_begin += ofs;
 	      break;
 
 	    case DW_CFA_GNU_window_save:
-	      if (! do_debug_frames_interp)
-		printf ("  %s\n", DW_CFA_GNU_window_save_name[is_aarch64]);
+	      if (! do_debug_frames_noshow)
+		{
+		  if (! do_debug_frames_interp)
+		    printf ("  %s\n", DW_CFA_GNU_window_save_name[is_aarch64]);
+		}
 	      break;
 
 	    case DW_CFA_GNU_args_size:
 	      READ_ULEB (ofs, start, block_end);
-	      if (! do_debug_frames_interp)
-		printf ("  DW_CFA_GNU_args_size: %" PRIu64 "\n", ofs);
+	      if (! do_debug_frames_noshow)
+		{
+		  if (! do_debug_frames_interp)
+		    printf ("  DW_CFA_GNU_args_size: %" PRIu64 "\n", ofs);
+		}
 	      break;
 
 	    case DW_CFA_GNU_negative_offset_extended:
@@ -10211,10 +10408,13 @@  display_debug_frames (struct dwarf_section *section,
 	      ofs = -ofs * fc->data_factor;
 	      if (reg >= fc->ncols)
 		reg_prefix = bad_reg;
-	      if (! do_debug_frames_interp || *reg_prefix != '\0')
-		printf ("  DW_CFA_GNU_negative_offset_extended: %s%s "
-			"at cfa%+" PRId64 "\n",
-			reg_prefix, regname (reg, 0), ofs);
+	      if (! do_debug_frames_noshow)
+		{
+		  if (! do_debug_frames_interp || *reg_prefix != '\0')
+		    printf ("  DW_CFA_GNU_negative_offset_extended: %s%s "
+			    "at cfa%+" PRId64 "\n",
+			    reg_prefix, regname (reg, 0), ofs);
+		}
 	      if (*reg_prefix == '\0')
 		{
 		  fc->col_type[reg] = DW_CFA_offset;
@@ -10223,17 +10423,24 @@  display_debug_frames (struct dwarf_section *section,
 	      break;
 
 	    default:
-	      if (op >= DW_CFA_lo_user && op <= DW_CFA_hi_user)
-		printf (_("  DW_CFA_??? (User defined call frame op: %#x)\n"), op);
-	      else
-		warn (_("Unsupported or unknown Dwarf Call Frame Instruction number: %#x\n"), op);
+	      if (! do_debug_frames_noshow)
+		{
+		  if (op >= DW_CFA_lo_user && op <= DW_CFA_hi_user)
+		    printf (_("  DW_CFA_??? (User defined call frame op: %#x)\n"), op);
+		  else
+		    warn (_("Unsupported or unknown Dwarf Call Frame Instruction number: %#x\n"), op);
+		}
 	      start = block_end;
 	    }
 	}
 
       /* Interpret the CFA - as long as it is not completely full of NOPs.  */
-      if (do_debug_frames_interp && ! all_nops)
-	frame_display_row (fc, &need_col_headers, &max_regs);
+      if (! all_nops)
+	{
+	  record_frame_row (fc);
+	  if (do_debug_frames_interp && ! do_debug_frames_noshow)
+	    frame_display_row (fc, &need_col_headers, &max_regs);
+	}
 
       if (fde_fc.col_type != NULL)
 	{
@@ -10250,7 +10457,8 @@  display_debug_frames (struct dwarf_section *section,
       eh_addr_size = saved_eh_addr_size;
     }
 
-  printf ("\n");
+  if (! do_debug_frames_noshow)
+    printf ("\n");
 
   while (remembered_state != NULL)
     {
@@ -10262,15 +10470,7 @@  display_debug_frames (struct dwarf_section *section,
       free (rs);
     }
 
-  while (chunks != NULL)
-    {
-      rs = chunks;
-      chunks = rs->next;
-      free (rs->col_type);
-      free (rs->col_offset);
-      rs->next = NULL; /* Paranoia.  */
-      free (rs);
-    }
+  free_chunks (chunks);
 
   while (forward_refs != NULL)
     {
diff --git a/binutils/dwarf.h b/binutils/dwarf.h
index d2f95235584..aa3bbdd15c5 100644
--- a/binutils/dwarf.h
+++ b/binutils/dwarf.h
@@ -239,6 +239,11 @@  extern unsigned long dwarf_start_die;
 
 extern int dwarf_check;
 
+extern void   dwarf_record_frame_info (void);
+extern void * dwarf_frame_for_addr (uint64_t);
+extern void   dwarf_display_frame (void *);
+extern void   dwarf_release_frame_info (void);
+
 extern void init_dwarf_regnames_by_elf_machine_code (unsigned int);
 extern void init_dwarf_regnames_by_bfd_arch_and_mach (enum bfd_architecture arch,
 						      unsigned long mach);
diff --git a/binutils/objdump.c b/binutils/objdump.c
index 60813426566..ac5fe78ee82 100644
--- a/binutils/objdump.c
+++ b/binutils/objdump.c
@@ -104,6 +104,7 @@  static int with_line_numbers;		/* -l */
 static bool with_source_code;		/* -S */
 static int show_raw_insn;		/* --show-raw-insn */
 static int dump_dwarf_section_info;	/* --dwarf */
+static int disassemble_with_frame_info; /* -c  */
 static int dump_stab_section_info;	/* --stabs */
 static int dump_ctf_section_info;       /* --ctf */
 static char *dump_ctf_section_name;
@@ -351,6 +352,8 @@  usage (FILE *stream, int status)
       fprintf (stream, _("\
   -b, --target=BFDNAME           Specify the target object format as BFDNAME\n"));
       fprintf (stream, _("\
+  -c, --disassemble-with-cfi     Include call frame information when disassembling\n"));
+      fprintf (stream, _("\
   -m, --architecture=MACHINE     Specify the target architecture as MACHINE\n"));
       fprintf (stream, _("\
   -j, --section=NAME             Only display information for section NAME\n"));
@@ -503,6 +506,7 @@  static struct option long_options[]=
   {"demangle", optional_argument, NULL, 'C'},
   {"disassemble", optional_argument, NULL, 'd'},
   {"disassemble-all", no_argument, NULL, 'D'},
+  {"disassemble-with-cfi", no_argument, NULL, 'c'},
   {"disassemble-zeroes", no_argument, NULL, 'z'},
   {"disassembler-options", required_argument, NULL, 'M'},
   {"dwarf", optional_argument, NULL, OPTION_DWARF},
@@ -3528,10 +3532,12 @@  disassemble_bytes (struct disassemble_info *inf,
 		printf ("    ");
 	    }
 
+	  int nc = 0;
+	  
 	  if (! insns)
-	    printf ("%s", buf);
+	    nc = printf ("%s", buf);
 	  else if (sfile.pos)
-	    printf ("%s", sfile.buffer);
+	    nc = printf ("%s", sfile.buffer);
 
 	  if (prefix_addresses
 	      ? show_raw_insn > 0
@@ -3587,6 +3593,22 @@  disassemble_bytes (struct disassemble_info *inf,
 		}
 	    }
 
+	  if (disassemble_with_frame_info)
+	    {
+	      void * fc;
+
+	      fc = dwarf_frame_for_addr (addr_offset);
+	      if (fc != NULL)
+		{
+		  if (nc < 24)
+		    printf ("%*c cfi: ", 24 - nc, '#');
+		  else
+		    printf (" # cfi: ");
+		    
+		  dwarf_display_frame (fc);
+		}
+	    }
+      
 	  if (!wide_output)
 	    putchar ('\n');
 	  else
@@ -5669,8 +5691,16 @@  dump_bfd (bfd *abfd, bool is_mainfile)
       if (dump_dynamic_symtab)
 	dump_symbols (abfd, true);
     }
+
+  if (disassemble_with_frame_info)
+    {
+      dwarf_record_frame_info ();
+      dump_dwarf_section_info = true;
+    }
+
   if (dump_dwarf_section_info)
     dump_dwarf (abfd, is_mainfile);
+
   if (is_mainfile || process_links)
     {
       if (dump_ctf_section_info)
@@ -5738,6 +5768,9 @@  dump_bfd (bfd *abfd, bool is_mainfile)
 
   if (is_mainfile)
     free_debug_memory ();
+
+  if (disassemble_with_frame_info)
+    dwarf_release_frame_info ();
 }
 
 static void
@@ -5897,7 +5930,7 @@  main (int argc, char **argv)
   set_default_bfd_target ();
 
   while ((c = getopt_long (argc, argv,
-			   "CDE:FGHI:LM:P:RSTU:VW::ab:defghij:lm:prstvwxz",
+			   "CDE:FGHI:LM:P:RSTU:VW::ab:cdefghij:lm:prstvwxz",
 			   long_options, (int *) 0))
 	 != EOF)
     {
@@ -6088,6 +6121,9 @@  main (int argc, char **argv)
 	  dump_dynamic_symtab = true;
 	  seenflag = true;
 	  break;
+	case 'c':
+	  disassemble_with_frame_info = true;
+	  /* Fall through.  */
 	case 'd':
 	  disassemble = true;
 	  seenflag = true;