]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
Implement call frame information dumping.
authorUlrich Drepper <drepper@redhat.com>
Tue, 6 Jan 2009 08:30:01 +0000 (00:30 -0800)
committerUlrich Drepper <drepper@redhat.com>
Tue, 6 Jan 2009 08:30:01 +0000 (00:30 -0800)
NEWS
libdw/ChangeLog
libdw/dwarf.h
libdw/memory-access.h
src/ChangeLog
src/readelf.c

diff --git a/NEWS b/NEWS
index 544887478f42dcdb1a5fb1c8a6615a233e404db5..30fcbc0a6d70c0eeec499b0e6a4bd07e63a241de 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,8 @@ Version 0.139:
 
 libcpu: Add Intel SSE4 disassembler support
 
+readelf: Implement call frame information dumping.
+
 Version 0.138:
 
 Install <elfutils/version.h> header file for applications to use in source
index 235fac0130e467186d5ead3cd175260d9bef593d..eaaab2ddac26e3c8aff69eae4167fdae6e3865b2 100644 (file)
@@ -1,3 +1,10 @@
+2009-01-06  Ulrich Drepper  <drepper@redhat.com>
+
+       * libdw.h: Add definition for unwind and call frame information.
+
+       * memory-access.h: Define read_ubyte_unaligned, read_sbyte_unaligned,
+       read_ubyte_unaligned_inc, and read_sbyte_unaligned_inc.
+
 2008-08-15  Roland McGrath  <roland@redhat.com>
 
        * libdw.map (ELFUTILS_0.136): New version set, inherits from
index f1261c3618f1d135bf1aab01e09b71fa19436575..091519c5664f2868560794ed1841236164156a6c 100644 (file)
@@ -668,6 +668,40 @@ enum
     DW_CFA_high_user = 0x3f
   };
 
+/* ID indicating CIE as opposed to FDE in .debug_frame.  */
+enum
+  {
+    DW_CIE_ID = 0xffffffff
+  };
+
+
+/* Information for GNU unwind information.  */
+enum
+  {
+    DW_EH_PE_absptr = 0x00,
+    DW_EH_PE_omit = 0xff,
+
+    /* FDE data encoding.  */
+    DW_EH_PE_uleb128 = 0x01,
+    DW_EH_PE_udata2 = 0x02,
+    DW_EH_PE_udata4 = 0x03,
+    DW_EH_PE_udata8 = 0x04,
+    DW_EH_PE_sleb128 = 0x09,
+    DW_EH_PE_sdata2 = 0x0a,
+    DW_EH_PE_sdata4 = 0x0b,
+    DW_EH_PE_sdata8 = 0x0c,
+    DW_EH_PE_signed = 0x08,
+
+    /* FDE flags.  */
+    DW_EH_PE_pcrel = 0x10,
+    DW_EH_PE_textrel = 0x20,
+    DW_EH_PE_datarel = 0x30,
+    DW_EH_PE_funcrel = 0x40,
+    DW_EH_PE_aligned = 0x50,
+
+    DW_EH_PE_indirect = 0x80
+  };
+
 
 /* DWARF XXX.  */
 #define DW_ADDR_none   0
index 52b41b5b020e4c0dc19f137b735d7cd2ff44d94e..74054f95201f3d3751d70555d5d4acad5cfbdf5d 100644 (file)
@@ -1,5 +1,5 @@
 /* Unaligned memory access functionality.
-   Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.
+   Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2009 Red Hat, Inc.
    This file is part of Red Hat elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2001.
 
@@ -246,6 +246,17 @@ read_8sbyte_unaligned (Dwarf *dbg, const void *p)
 #endif /* allow unaligned */
 
 
+#define read_ubyte_unaligned(Nbytes, Dbg, Addr) \
+  ((Nbytes) == 2 ? read_2ubyte_unaligned (Dbg, Addr)                         \
+   : (Nbytes) == 4 ? read_4ubyte_unaligned (Dbg, Addr)                       \
+   : read_8ubyte_unaligned (Dbg, Addr))
+
+#define read_sbyte_unaligned(Nbytes, Dbg, Addr) \
+  ((Nbytes) == 2 ? read_2sbyte_unaligned (Dbg, Addr)                         \
+   : (Nbytes) == 4 ? read_4sbyte_unaligned (Dbg, Addr)                       \
+   : read_8sbyte_unaligned (Dbg, Addr))
+
+
 #define read_2ubyte_unaligned_inc(Dbg, Addr) \
   ({ uint16_t t_ = read_2ubyte_unaligned (Dbg, Addr);                        \
      Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 2);                    \
@@ -273,4 +284,15 @@ read_8sbyte_unaligned (Dwarf *dbg, const void *p)
      Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 8);                    \
      t_; })
 
+
+#define read_ubyte_unaligned_inc(Nbytes, Dbg, Addr) \
+  ((Nbytes) == 2 ? read_2ubyte_unaligned_inc (Dbg, Addr)                     \
+   : (Nbytes) == 4 ? read_4ubyte_unaligned_inc (Dbg, Addr)                   \
+   : read_8ubyte_unaligned_inc (Dbg, Addr))
+
+#define read_sbyte_unaligned_inc(Nbytes, Dbg, Addr) \
+  ((Nbytes) == 2 ? read_2sbyte_unaligned_inc (Dbg, Addr)                     \
+   : (Nbytes) == 4 ? read_4sbyte_unaligned_inc (Dbg, Addr)                   \
+   : read_8sbyte_unaligned_inc (Dbg, Addr))
+
 #endif /* memory-access.h */
index e4300be996940c4c58562c219d514210cb3a93f8..2e17b5440a2992aaa0171e7bd4e067af6e76251d 100644 (file)
@@ -1,3 +1,7 @@
+2009-01-06  Ulrich Drepper  <drepper@redhat.com>
+
+       * readelf.c: Implement call frame debug section dumping.
+
 2009-01-01  Ulrich Drepper  <drepper@redhat.com>
 
        * addr2line.c: Update copyright year.
index e9d4f0da2c513bfa1fade4db83814e6bf550cdd9..2041983dd38e2df09077206928715dd77170a3ec 100644 (file)
@@ -344,7 +344,7 @@ parse_opt (int key, char *arg,
        print_debug_sections |= section_aranges;
       else if (strcmp (arg, "ranges") == 0)
        print_debug_sections |= section_ranges;
-      else if (strcmp (arg, "frame") == 0)
+      else if (strcmp (arg, "frame") == 0 || strcmp (arg, "frames") == 0)
        print_debug_sections |= section_frame;
       else if (strcmp (arg, "info") == 0)
        print_debug_sections |= section_info;
@@ -3790,14 +3790,13 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
            {
              char *a = format_dwarf_addr (dwflmod, 0, addr);
              printf ("%*s[%4" PRIuMAX "] %s %s\n",
-                     indent, "", (uintmax_t) offset,
-                     known[op] ?: "???", a);
+                     indent, "", (uintmax_t) offset, known[op], a);
              free (a);
            }
          else
            printf ("%*s[%4" PRIuMAX "] %s %#" PRIxMAX "\n",
                    indent, "", (uintmax_t) offset,
-                   known[op] ?: "???", (uintmax_t) addr);
+                   known[op], (uintmax_t) addr);
          offset += 1 + addrsize;
          break;
 
@@ -3805,72 +3804,80 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
        case DW_OP_xderef_size:
        case DW_OP_pick:
        case DW_OP_const1u:
+         // XXX value might be modified by relocation
          printf ("%*s[%4" PRIuMAX "] %s %" PRIu8 "\n",
                  indent, "", (uintmax_t) offset,
-                 known[op] ?: "???", *((uint8_t *) data));
+                 known[op], *((uint8_t *) data));
          ++data;
          --len;
          offset += 2;
          break;
 
        case DW_OP_const2u:
+         // XXX value might be modified by relocation
          printf ("%*s[%4" PRIuMAX "] %s %" PRIu16 "\n",
                  indent, "", (uintmax_t) offset,
-                 known[op] ?: "???", read_2ubyte_unaligned (dbg, data));
+                 known[op], read_2ubyte_unaligned (dbg, data));
          len -= 2;
          data += 2;
          offset += 3;
          break;
 
        case DW_OP_const4u:
+         // XXX value might be modified by relocation
          printf ("%*s[%4" PRIuMAX "] %s %" PRIu32 "\n",
                  indent, "", (uintmax_t) offset,
-                 known[op] ?: "???", read_4ubyte_unaligned (dbg, data));
+                 known[op], read_4ubyte_unaligned (dbg, data));
          len -= 4;
          data += 4;
          offset += 5;
          break;
 
        case DW_OP_const8u:
+         // XXX value might be modified by relocation
          printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 "\n",
                  indent, "", (uintmax_t) offset,
-                 known[op] ?: "???", read_8ubyte_unaligned (dbg, data));
+                 known[op], read_8ubyte_unaligned (dbg, data));
          len -= 8;
          data += 8;
          offset += 9;
          break;
 
        case DW_OP_const1s:
+         // XXX value might be modified by relocation
          printf ("%*s[%4" PRIuMAX "] %s %" PRId8 "\n",
                  indent, "", (uintmax_t) offset,
-                 known[op] ?: "???", *((int8_t *) data));
+                 known[op], *((int8_t *) data));
          ++data;
          --len;
          offset += 2;
          break;
 
        case DW_OP_const2s:
+         // XXX value might be modified by relocation
          printf ("%*s[%4" PRIuMAX "] %s %" PRId16 "\n",
                  indent, "", (uintmax_t) offset,
-                 known[op] ?: "???", read_2sbyte_unaligned (dbg, data));
+                 known[op], read_2sbyte_unaligned (dbg, data));
          len -= 2;
          data += 2;
          offset += 3;
          break;
 
        case DW_OP_const4s:
+         // XXX value might be modified by relocation
          printf ("%*s[%4" PRIuMAX "] %s %" PRId32 "\n",
                  indent, "", (uintmax_t) offset,
-                 known[op] ?: "???", read_4sbyte_unaligned (dbg, data));
+                 known[op], read_4sbyte_unaligned (dbg, data));
          len -= 4;
          data += 4;
          offset += 5;
          break;
 
        case DW_OP_const8s:
+         // XXX value might be modified by relocation
          printf ("%*s[%4" PRIuMAX "] %s %" PRId64 "\n",
                  indent, "", (uintmax_t) offset,
-                 known[op] ?: "???", read_8sbyte_unaligned (dbg, data));
+                 known[op], read_8sbyte_unaligned (dbg, data));
          len -= 8;
          data += 8;
          offset += 9;
@@ -3884,8 +3891,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          unsigned int uleb;
          get_uleb128 (uleb, data);
          printf ("%*s[%4" PRIuMAX "] %s %u\n",
-                 indent, "", (uintmax_t) offset,
-                 known[op] ?: "???", uleb);
+                 indent, "", (uintmax_t) offset, known[op], uleb);
          len -= data - start;
          offset += 1 + (data - start);
          break;
@@ -3896,8 +3902,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          get_uleb128 (uleb, data);
          get_uleb128 (uleb2, data);
          printf ("%*s[%4" PRIuMAX "] %s %u, %u\n",
-                 indent, "", (uintmax_t) offset,
-                 known[op] ?: "???", uleb, uleb2);
+                 indent, "", (uintmax_t) offset, known[op], uleb, uleb2);
          len -= data - start;
          offset += 1 + (data - start);
          break;
@@ -3909,8 +3914,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          unsigned int sleb;
          get_sleb128 (sleb, data);
          printf ("%*s[%4" PRIuMAX "] %s %d\n",
-                 indent, "", (uintmax_t) offset,
-                 known[op] ?: "???", sleb);
+                 indent, "", (uintmax_t) offset, known[op], sleb);
          len -= data - start;
          offset += 1 + (data - start);
          break;
@@ -3920,8 +3924,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
          get_uleb128 (uleb, data);
          get_sleb128 (sleb, data);
          printf ("%*s[%4" PRIuMAX "] %s %u %d\n",
-                 indent, "", (uintmax_t) offset,
-                 known[op] ?: "???", uleb, sleb);
+                 indent, "", (uintmax_t) offset, known[op], uleb, sleb);
          len -= data - start;
          offset += 1 + (data - start);
          break;
@@ -3931,8 +3934,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
        case DW_OP_skip:
        case DW_OP_bra:
          printf ("%*s[%4" PRIuMAX "] %s %" PRIuMAX "\n",
-                 indent, "", (uintmax_t) offset,
-                 known[op] ?: "???",
+                 indent, "", (uintmax_t) offset, known[op],
                  (uintmax_t) (offset + read_2sbyte_unaligned (dbg, data)));
          len -= 2;
          data += 2;
@@ -4160,13 +4162,569 @@ print_debug_ranges_section (Dwfl_Module *dwflmod,
 
 
 static void
-print_debug_frame_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
-                          Ebl *ebl __attribute__ ((unused)),
-                          GElf_Ehdr *ehdr __attribute__ ((unused)),
-                          Elf_Scn *scn __attribute__ ((unused)),
-                          GElf_Shdr *shdr __attribute__ ((unused)),
-                          Dwarf *dbg __attribute__ ((unused)))
+print_cfa_program (const unsigned char *readp, const unsigned char *const endp,
+                  Dwarf_Word vma_base, unsigned int code_align,
+                  int data_align, unsigned int ptr_size, Dwfl_Module *dwflmod,
+                  Ebl *ebl, Dwarf *dbg)
 {
+  char regnamebuf[16];
+  const char *regname (unsigned int regno)
+  {
+    const char *str;
+    int i;
+    return (ebl_register_info (ebl, regno, regnamebuf, sizeof (regnamebuf),
+                              &str, &str, &i, &i) < 0
+           ? "???" : regnamebuf);
+  }
+
+  puts ("\n   Program:");
+  Dwarf_Word pc = vma_base;
+  while (readp < endp)
+    {
+      unsigned int opcode = *readp++;
+
+      if (opcode < DW_CFA_advance_loc)
+       /* Extended opcode.  */
+       switch (opcode)
+         {
+           uint64_t op1;
+           int64_t sop1;
+           uint64_t op2;
+           int64_t sop2;
+
+         case DW_CFA_nop:
+           puts ("     nop");
+           break;
+         case DW_CFA_set_loc:
+           // XXX overflow check
+           get_uleb128 (op1, readp);
+           op1 += vma_base;
+           printf ("     set_loc %" PRIu64 "\n", op1 * code_align);
+           break;
+         case DW_CFA_advance_loc1:
+           printf ("     advance_loc1 %u to %#" PRIx64 "\n",
+                   *readp, pc += *readp * code_align);
+           ++readp;
+           break;
+         case DW_CFA_advance_loc2:
+           op1 = read_2ubyte_unaligned_inc (dbg, readp);
+           printf ("     advance_loc2 %" PRIu64 " to %#" PRIx64 "\n",
+                   op1, pc += op1 * code_align);
+           break;
+         case DW_CFA_advance_loc4:
+           op1 = read_4ubyte_unaligned_inc (dbg, readp);
+           printf ("     advance_loc4 %" PRIu64 " to %#" PRIx64 "\n",
+                   op1, pc += op1 * code_align);
+           break;
+         case DW_CFA_offset_extended:
+           // XXX overflow check
+           get_uleb128 (op1, readp);
+           get_uleb128 (op2, readp);
+           printf ("     offset_extended r%" PRIu64 " (%s) at cfa%+" PRId64
+                   "\n",
+                   op1, regname (op1), op2 * data_align);
+           break;
+         case DW_CFA_restore_extended:
+           // XXX overflow check
+           get_uleb128 (op1, readp);
+           printf ("     restore_extended r%" PRIu64 " (%s)\n",
+                   op1, regname (op1));
+           break;
+         case DW_CFA_undefined:
+           // XXX overflow check
+           get_uleb128 (op1, readp);
+           printf ("     undefined r%" PRIu64 " (%s)\n", op1, regname (op1));
+           break;
+         case DW_CFA_same_value:
+           // XXX overflow check
+           get_uleb128 (op1, readp);
+           printf ("     same_value r%" PRIu64 " (%s)\n", op1, regname (op1));
+           break;
+         case DW_CFA_register:
+           // XXX overflow check
+           get_uleb128 (op1, readp);
+           get_uleb128 (op2, readp);
+           printf ("     same_value r%" PRIu64 " (%s) in r%" PRIu64 " (%s)\n",
+                   op1, regname (op1), op2, regname (op2));
+           break;
+         case DW_CFA_remember_state:
+           puts ("     remember_state");
+           break;
+         case DW_CFA_restore_state:
+           puts ("     restore_state");
+           break;
+         case DW_CFA_def_cfa:
+           // XXX overflow check
+           get_uleb128 (op1, readp);
+           get_uleb128 (op2, readp);
+           printf ("     def_cfa r%" PRIu64 " (%s) at offset %" PRIu64 "\n",
+                   op1, regname (op1), op2);
+           break;
+         case DW_CFA_def_cfa_register:
+           // XXX overflow check
+           get_uleb128 (op1, readp);
+           printf ("     def_cfa_register r%" PRIu64 " (%s)\n",
+                   op1, regname (op1));
+           break;
+         case DW_CFA_def_cfa_offset:
+           // XXX overflow check
+           get_uleb128 (op1, readp);
+           printf ("     def_cfa_offset %" PRIu64 "\n", op1);
+           break;
+         case DW_CFA_def_cfa_expression:
+           // XXX overflow check
+           get_uleb128 (op1, readp);   /* Length of DW_FORM_block.  */
+           printf ("     val_expression %" PRIu64 "\n", op1);
+           print_ops (dwflmod, dbg, 10, 10, ptr_size, op1, readp);
+           readp += op1;
+           error (1,0,"need to implement BLOCK reading");
+           break;
+         case DW_CFA_expression:
+           // XXX overflow check
+           get_uleb128 (op1, readp);
+           get_uleb128 (op2, readp);   /* Length of DW_FORM_block.  */
+           printf ("     val_expression %" PRIu64 "\n", op1);
+           print_ops (dwflmod, dbg, 10, 10, ptr_size, op2, readp);
+           readp += op2;
+           break;
+         case DW_CFA_offset_extended_sf:
+           // XXX overflow check
+           get_uleb128 (op1, readp);
+           get_sleb128 (sop2, readp);
+           printf ("     offset_extended_sf r%" PRIu64 " (%s) at cfa%+"
+                   PRId64 "\n",
+                   op1, regname (op1), sop2 * data_align);
+           break;
+         case DW_CFA_def_cfa_sf:
+           // XXX overflow check
+           get_uleb128 (op1, readp);
+           get_sleb128 (sop2, readp);
+           printf ("     def_cfa_sf r%" PRIu64 " (%s) at offset %" PRId64 "\n",
+                   op1, regname (op1), sop2 * data_align);
+           break;
+         case DW_CFA_def_cfa_offset_sf:
+           // XXX overflow check
+           get_sleb128 (sop1, readp);
+           printf ("     def_cfa_offset_sf %" PRId64 "\n", sop1 * data_align);
+           break;
+         case DW_CFA_val_offset:
+           // XXX overflow check
+           get_uleb128 (op1, readp);
+           get_uleb128 (op2, readp);
+           printf ("     val_offset %" PRIu64 " at offset %" PRIu64 "\n",
+                   op1, op2 * data_align);
+           break;
+         case DW_CFA_val_offset_sf:
+           // XXX overflow check
+           get_uleb128 (op1, readp);
+           get_sleb128 (sop2, readp);
+           printf ("     val_offset %" PRIu64 " at offset %" PRId64 "\n",
+                   op1, sop2 * data_align);
+           break;
+         case DW_CFA_val_expression:
+           // XXX overflow check
+           get_uleb128 (op1, readp);
+           get_uleb128 (op2, readp);   /* Length of DW_FORM_block.  */
+           printf ("     val_expression %" PRIu64 "\n", op1);
+           print_ops (dwflmod, dbg, 10, 10, ptr_size, op2, readp);
+           readp += op2;
+           break;
+         case DW_CFA_MIPS_advance_loc8:
+           op1 = read_8ubyte_unaligned_inc (dbg, readp);
+           printf ("     advance_loc2 %" PRIu64 " to %#" PRIx64 "\n",
+                   op1, pc += op1 * code_align);
+           break;
+         case DW_CFA_GNU_window_save:
+           puts ("     window_save");
+           break;
+         case DW_CFA_GNU_args_size:
+           // XXX overflow check
+           get_uleb128 (op1, readp);
+           printf ("     args_size %" PRIu64 "\n", op1);
+           break;
+         default:
+           printf ("     ??? (%u)\n", opcode);
+           break;
+         }
+      else if (opcode < DW_CFA_offset)
+       printf ("     advance_loc %u to %#" PRIx64 "\n",
+               opcode & 0x3f, pc += (opcode & 0x3f) * code_align);
+      else if (opcode < DW_CFA_restore)
+       {
+         unsigned int offset;
+         // XXX overflow check
+         get_uleb128 (offset, readp);
+         printf ("     offset r%u (%s) at cfa%+d\n",
+                 opcode & 0x3f, regname (opcode & 0x3f), offset * data_align);
+       }
+      else
+       printf ("     restore r%u (%s)\n",
+               opcode & 0x3f, regname (opcode & 0x3f));
+    }
+}
+
+
+static unsigned int
+encoded_ptr_size (int encoding, unsigned int ptr_size)
+{
+  switch (encoding & 7)
+    {
+    case 2:
+      return 2;
+    case 3:
+      return 4;
+    case 4:
+      return 8;
+    default:
+      return ptr_size;
+    }
+}
+
+
+static void
+print_debug_frame_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr,
+                          Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
+{
+  size_t shstrndx;
+  /* We know this call will succeed since it did in the caller.  */
+  (void) elf_getshstrndx (ebl->elf, &shstrndx);
+  const char *scnname = elf_strptr (ebl->elf, shstrndx, shdr->sh_name);
+
+  Elf_Data *data = elf_rawdata (scn, NULL);
+
+  if (unlikely (data == NULL))
+    {
+      error (0, 0, gettext ("cannot get %s content: %s"),
+            scnname, elf_errmsg (-1));
+      return;
+    }
+
+  printf (gettext ("\
+\nDWARF section '%s' at offset %#" PRIx64 ":\n"),
+         scnname, (uint64_t) shdr->sh_offset);
+
+  struct cieinfo
+  {
+    ptrdiff_t cie_offset;
+    const char *augmentation;
+    unsigned int code_alignment_factor;
+    unsigned int data_alignment_factor;
+    unsigned int fde_encoding;
+    struct cieinfo *next;
+  } *cies = NULL;
+
+  const unsigned char *readp = data->d_buf;
+  const unsigned char *const dataend = ((unsigned char *) data->d_buf
+                                       + data->d_size);
+  bool is_eh_frame = strcmp (scnname, ".eh_frame") == 0;
+  while (readp < dataend)
+    {
+      /* At the beginning there must be a CIE.  There can be multiple,
+        hence we test tis in a loop.  */
+      ptrdiff_t offset = readp - (unsigned char *) data->d_buf;
+
+      if (unlikely (readp + 12 > dataend))
+       {
+       invalid_data:
+         error (0, 0, gettext ("invalid data in section [%zu] '%s'"),
+                    elf_ndxscn (scn), scnname);
+             return;
+       }
+
+      unsigned int ptr_size = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
+
+      Dwarf_Word unit_length = read_4ubyte_unaligned_inc (dbg, readp);
+      unsigned int length = 4;
+      if (unlikely (unit_length == 0xffffffff))
+       {
+         unit_length = read_8ubyte_unaligned_inc (dbg, readp);
+         length = 8;
+
+         if (unlikely (readp + 12 > dataend))
+           goto invalid_data;
+       }
+      ptrdiff_t start = readp - (unsigned char *) data->d_buf;
+      const unsigned char *const cieend = readp + unit_length;
+      if (unlikely (cieend > dataend))
+       goto invalid_data;
+
+      Dwarf_Word cie_id;
+      if (length == 4)
+       cie_id = read_4ubyte_unaligned_inc (dbg, readp);
+      else
+       cie_id = read_8ubyte_unaligned_inc (dbg, readp);
+
+      unsigned int code_alignment_factor;
+      int data_alignment_factor;
+      unsigned int fde_encoding = 0;
+      Dwarf_Word initial_location = 0;
+
+      if ((is_eh_frame && cie_id == 0)
+         || (! is_eh_frame && cie_id == DW_CIE_ID))
+       {
+         uint_fast8_t version = *readp++;
+         const char *const augmentation = (const char *) readp;
+         readp = memchr (readp, '\0', cieend - readp);
+         if (unlikely (readp == NULL))
+           goto invalid_data;
+         ++readp;
+         // XXX Check overflow
+         get_uleb128 (code_alignment_factor, readp);
+         // XXX Check overflow
+         get_sleb128 (data_alignment_factor, readp);
+
+         /* In some variant for unwind data there is another field.  */
+         if (strcmp (augmentation, "eh") == 0)
+           readp += ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
+
+         unsigned int return_address_register;
+         if (unlikely (version == 1))
+           return_address_register = *readp++;
+         else
+           // XXX Check overflow
+           get_uleb128 (return_address_register, readp);
+
+         printf (" [%6jx] CIE length=%" PRIu64 "\n"
+                 "   CIE_id:                   %" PRIu64 "\n"
+                 "   version:                  %u\n"
+                 "   augmentation:             \"%s\"\n"
+                 "   code_alignment_factor:    %u\n"
+                 "   data_alignment_factor:    %d\n"
+                 "   return_address_register:  %u\n",
+                 offset, (uint64_t) unit_length, (uint64_t) cie_id,
+                 version, augmentation, code_alignment_factor,
+                 data_alignment_factor, return_address_register);
+
+         if (augmentation[0] == 'z')
+           {
+             unsigned int print_encoding (unsigned int val)
+             {
+               switch (val & 0xf)
+                 {
+                 case DW_EH_PE_absptr:
+                   fputs ("absptr", stdout);
+                   break;
+                 case DW_EH_PE_uleb128:
+                   fputs ("uleb128", stdout);
+                   break;
+                 case DW_EH_PE_udata2:
+                   fputs ("udata2", stdout);
+                   break;
+                 case DW_EH_PE_udata4:
+                   fputs ("udata4", stdout);
+                   break;
+                 case DW_EH_PE_udata8:
+                   fputs ("udata8", stdout);
+                   break;
+                 case DW_EH_PE_sleb128:
+                   fputs ("sleb128", stdout);
+                   break;
+                 case DW_EH_PE_sdata2:
+                   fputs ("sdata2", stdout);
+                   break;
+                 case DW_EH_PE_sdata4:
+                   fputs ("sdata4", stdout);
+                   break;
+                 case DW_EH_PE_sdata8:
+                   fputs ("sdata8", stdout);
+                   break;
+                 default:
+                   /* We did not use any of the bits after all.  */
+                   return val;
+                 }
+
+               return val & ~0xf;
+             }
+
+             unsigned int print_relinfo (unsigned int val)
+             {
+               switch (val & 0x70)
+                 {
+                 case DW_EH_PE_pcrel:
+                   fputs ("pcrel", stdout);
+                   break;
+                 case DW_EH_PE_textrel:
+                   fputs ("textrel", stdout);
+                   break;
+                 case DW_EH_PE_datarel:
+                   fputs ("datarel", stdout);
+                   break;
+                 case DW_EH_PE_funcrel:
+                   fputs ("funcrel", stdout);
+                   break;
+                 case DW_EH_PE_aligned:
+                   fputs ("aligned", stdout);
+                   break;
+                 default:
+                   return val;
+                 }
+
+               return val & ~0x70;
+             }
+
+             unsigned int augmentationlen;
+             get_uleb128 (augmentationlen, readp);
+
+             const char *hdr = "Augmentation data:";
+             const char *cp = augmentation + 1;
+             while (*cp != '\0')
+               {
+                 printf ("   %-26s%#x ", hdr, *readp);
+                 hdr = "";
+
+                 if (*cp == 'R')
+                   {
+                     putchar_unlocked ('(');
+
+                     unsigned int w = fde_encoding = *readp++;
+
+                     if (w & 0xf)
+                       w = print_encoding (w);
+
+                     if (w & 0x70)
+                       {
+                         if (w != fde_encoding)
+                           fputc_unlocked (' ', stdout);
+
+                         w = print_relinfo (w);
+                       }
+
+                     if (w != 0)
+                       printf ("(%s%x", w != fde_encoding ? " " : "", w);
+
+                     puts (")");
+                   }
+                 else if (*cp == 'P')
+                   {
+                     const unsigned char *startp = readp;
+                     unsigned int encoding = *readp++;
+                     uint64_t val = 0;
+                     int64_t sval = 0;
+                     bool is_signed;
+
+                     switch (encoding & 0xf)
+                       {
+                       case DW_EH_PE_uleb128:
+                         get_uleb128 (val, readp);
+                         is_signed = false;
+                         break;
+                       case DW_EH_PE_sleb128:
+                         get_sleb128 (sval, readp);
+                         is_signed = true;
+                         break;
+                       case DW_EH_PE_udata2:
+                         val = read_2ubyte_unaligned_inc (dbg, readp);
+                         is_signed = false;
+                         break;
+                       case DW_EH_PE_udata4:
+                         val = read_4ubyte_unaligned_inc (dbg, readp);
+                         is_signed = false;
+                         break;
+                       case DW_EH_PE_udata8:
+                         val = read_8ubyte_unaligned_inc (dbg, readp);
+                         is_signed = false;
+                         break;
+                       case DW_EH_PE_sdata2:
+                         val = read_2sbyte_unaligned_inc (dbg, readp);
+                         is_signed = true;
+                         break;
+                       case DW_EH_PE_sdata4:
+                         val = read_4sbyte_unaligned_inc (dbg, readp);
+                         is_signed = true;
+                         break;
+                       case DW_EH_PE_sdata8:
+                         val = read_8sbyte_unaligned_inc (dbg, readp);
+                         is_signed = true;
+                         break;
+                       default:
+                         error (1, 0,
+                                gettext ("invalid augmentation encoding"));
+                       }
+
+                     while (++startp < readp)
+                       printf ("%#x ", *startp);
+
+                     if (is_signed)
+                       printf ("(%" PRId64 ")\n", sval);
+                     else
+                       printf ("(%" PRIu64 ")\n", val);
+                   }
+                 else
+                   printf ("(%x)\n", *readp++);
+
+                 ++cp;
+               }
+             readp += augmentationlen;
+           }
+
+         struct cieinfo *newp = alloca (sizeof (*newp));
+         newp->cie_offset = offset;
+         newp->augmentation = augmentation;
+         newp->fde_encoding = fde_encoding;
+         newp->code_alignment_factor = code_alignment_factor;
+         newp->data_alignment_factor = data_alignment_factor;
+         newp->next = cies;
+         cies = newp;
+       }
+      else
+       {
+         struct cieinfo *cie = cies;
+         while (cie != NULL)
+           if (start - (ptrdiff_t) cie_id == cie->cie_offset)
+             break;
+           else
+             cie = cie->next;
+         if (unlikely (cie == NULL))
+           {
+             puts ("invalid CIE reference in FDE");
+             return;
+           }
+
+         /* Initialize from CIE data.  */
+         ptr_size = encoded_ptr_size (cie->fde_encoding, ptr_size);
+         code_alignment_factor = cie->code_alignment_factor;
+         data_alignment_factor = cie->data_alignment_factor;
+         fde_encoding = cie->fde_encoding;
+
+         initial_location = read_ubyte_unaligned_inc (ptr_size, dbg, readp);
+         Dwarf_Word address_range
+           = read_ubyte_unaligned_inc (ptr_size, dbg, readp);
+
+         printf ("\n [%6jx] FDE length=%" PRIu64 " cie=[%6jx]\n"
+                 "   CIE_pointer:              %" PRIu64 "\n"
+                 "   initial_location:         %#" PRIx64 "\n"
+                 "   address_range:            %#" PRIx64 "\n",
+                 offset, (uint64_t) unit_length,
+                 cie->cie_offset, (uint64_t) cie_id,
+                 (uint64_t) initial_location, (uint64_t) address_range);
+
+         if (cie->augmentation[0] == 'z')
+           {
+             unsigned int augmentationlen;
+             get_uleb128 (augmentationlen, readp);
+
+             const char *hdr = "Augmentation data:";
+             const char *cp = cie->augmentation + 1;
+             for (unsigned int u = 0; u < augmentationlen; ++cp, ++u)
+               {
+                 printf ("   %-26s%#x (", hdr, readp[u]);
+                 hdr = "";
+               }
+           }
+       }
+
+      /* To print correct addresses compute the base address.  */
+      Dwarf_Word vma_base;
+      if ((fde_encoding & 0x70) == DW_EH_PE_pcrel && ehdr->e_type != ET_REL)
+       vma_base = shdr->sh_addr + initial_location;
+      else
+       vma_base = 0;
+
+      /* Handle the initialization instructions.  */
+      print_cfa_program (readp, cieend, vma_base, code_alignment_factor,
+                        data_alignment_factor, ptr_size, dwflmod, ebl, dbg);
+      readp = cieend;
+    }
 }