readelf: Display raw .debug_aranges. Use libdw only for decodedaranges.
authorMark Wielaard <mjw@redhat.com>
Mon, 25 Mar 2013 10:45:22 +0000 (11:45 +0100)
committerMark Wielaard <mjw@redhat.com>
Thu, 4 Apr 2013 19:26:09 +0000 (21:26 +0200)
Display "raw" .debug_aranges by default.  Only use libdw parsing when
--debug-dump=decodedaranges is given.

Signed-off-by: Mark Wielaard <mjw@redhat.com>
src/ChangeLog
src/readelf.c
tests/ChangeLog
tests/Makefile.am
tests/run-readelf-aranges.sh [new file with mode: 0755]
tests/testfilefoobarbaz.bz2 [new file with mode: 0755]

index 24bc6bf..30d1860 100644 (file)
@@ -1,5 +1,14 @@
 2013-03-25  Mark Wielaard  <mjw@redhat.com>
 
+       * readelf.c (argp_option): Add decodedaranges.
+       (decodedaranges): New boolean initialized to false.
+       (parse_opt): Set decodedaranges when arg is decodedaranges.
+       (print_debug_aranges_section): Reimplemented and original
+       implementation renamed to...
+       (print_decoded_aranges_section): this.
+
+2013-03-25  Mark Wielaard  <mjw@redhat.com>
+
        * readelf.c (attrcb_args): Add Dwarf_Die.
        (attr_callback): When highpc is in constant form also print as
        address.
index 6d9db73..7252d72 100644 (file)
@@ -94,8 +94,8 @@ static const struct argp_option options[] =
   { NULL, 0, NULL, 0, N_("Additional output selection:"), 0 },
   { "debug-dump", 'w', "SECTION", OPTION_ARG_OPTIONAL,
     N_("Display DWARF section content.  SECTION can be one of abbrev, "
-       "aranges, frame, gdb_index, info, loc, line, ranges, pubnames, str, "
-       "macinfo, macro or exception"), 0 },
+       "aranges, decodedaranges, frame, gdb_index, info, loc, line, ranges, "
+       "pubnames, str, macinfo, macro or exception"), 0 },
   { "hex-dump", 'x', "SECTION", 0,
     N_("Dump the uninterpreted contents of SECTION, by number or name"), 0 },
   { "strings", 'p', "SECTION", OPTION_ARG_OPTIONAL,
@@ -183,6 +183,9 @@ static bool print_address_names = true;
 /* True if we should print raw values instead of relativized addresses.  */
 static bool print_unresolved_addresses = false;
 
+/* True if we should print the .debug_aranges section using libdw.  */
+static bool decodedaranges = false;
+
 /* Select printing of debugging sections.  */
 static enum section_e
 {
@@ -391,6 +394,11 @@ parse_opt (int key, char *arg,
        print_debug_sections |= section_abbrev;
       else if (strcmp (arg, "aranges") == 0)
        print_debug_sections |= section_aranges;
+      else if (strcmp (arg, "decodedaranges") == 0)
+       {
+         print_debug_sections |= section_aranges;
+         decodedaranges = true;
+       }
       else if (strcmp (arg, "ranges") == 0)
        {
          print_debug_sections |= section_ranges;
@@ -4330,9 +4338,8 @@ print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
    not have to know a bit about the structure of the section, libdwarf
    takes care of it.  */
 static void
-print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
-                            Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
-                            GElf_Shdr *shdr, Dwarf *dbg)
+print_decoded_aranges_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
+                              GElf_Shdr *shdr, Dwarf *dbg)
 {
   Dwarf_Aranges *aranges;
   size_t cnt;
@@ -4384,6 +4391,166 @@ print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
     }
 }
 
+
+/* Print content of DWARF .debug_aranges section.  */
+static void
+print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
+                            Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
+                            GElf_Shdr *shdr, Dwarf *dbg)
+{
+  if (decodedaranges)
+    {
+      print_decoded_aranges_section (ebl, ehdr, scn, shdr, dbg);
+      return;
+    }
+
+  Elf_Data *data = elf_rawdata (scn, NULL);
+
+  if (unlikely (data == NULL))
+    {
+      error (0, 0, gettext ("cannot get .debug_aranges content: %s"),
+            elf_errmsg (-1));
+      return;
+    }
+
+  printf (gettext ("\
+\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"),
+         elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+         (uint64_t) shdr->sh_offset);
+
+  const unsigned char *readp = data->d_buf;
+  const unsigned char *readendp = readp + data->d_size;
+
+  while (readp < readendp)
+    {
+      const unsigned char *hdrstart = readp;
+      size_t start_offset = hdrstart - (const unsigned char *) data->d_buf;
+
+      printf (gettext ("\nTable at offset %Zu:\n"), start_offset);
+      if (readp + 4 > readendp)
+       {
+       invalid_data:
+         error (0, 0, gettext ("invalid data in section [%zu] '%s'"),
+                elf_ndxscn (scn), section_name (ebl, ehdr, shdr));
+         return;
+       }
+
+      Dwarf_Word length = read_4ubyte_unaligned_inc (dbg, readp);
+      unsigned int length_bytes = 4;
+      if (length == DWARF3_LENGTH_64_BIT)
+       {
+         if (readp + 8 > readendp)
+           goto invalid_data;
+         length = read_8ubyte_unaligned_inc (dbg, readp);
+         length_bytes = 8;
+       }
+
+      const unsigned char *nexthdr = readp + length;
+      printf (gettext ("\n Length:        %6" PRIu64 "\n"),
+             (uint64_t) length);
+
+      if (nexthdr > readendp)
+       goto invalid_data;
+
+      if (length == 0)
+       continue;
+
+      if (readp + 2 > readendp)
+       goto invalid_data;
+      uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, readp);
+      printf (gettext (" DWARF version: %6" PRIuFAST16 "\n"),
+             version);
+      if (version != 2)
+       {
+         error (0, 0, gettext ("unsupported aranges version"));
+         goto next_table;
+       }
+
+      Dwarf_Word offset;
+      if (readp + length_bytes > readendp)
+       goto invalid_data;
+      if (length_bytes == 8)
+       offset = read_8ubyte_unaligned_inc (dbg, readp);
+      else
+       offset = read_4ubyte_unaligned_inc (dbg, readp);
+      printf (gettext (" CU offset:     %6" PRIx64 "\n"),
+             (uint64_t) offset);
+
+      if (readp + 1 > readendp)
+       goto invalid_data;
+      unsigned int address_size = *readp++;
+      printf (gettext (" Address size:  %6" PRIu64 "\n"),
+             (uint64_t) address_size);
+      if (address_size != 4 && address_size != 8)
+       {
+         error (0, 0, gettext ("unsupported address size"));
+         goto next_table;
+       }
+
+      unsigned int segment_size = *readp++;
+      printf (gettext (" Segment size:  %6" PRIu64 "\n\n"),
+             (uint64_t) segment_size);
+      if (segment_size != 0 && segment_size != 4 && segment_size != 8)
+       {
+         error (0, 0, gettext ("unsupported segment size"));
+         goto next_table;
+       }
+
+      /* Round the address to the next multiple of 2*address_size.  */
+      readp += ((2 * address_size - ((readp - hdrstart) % (2 * address_size)))
+               % (2 * address_size));
+
+      while (readp < nexthdr)
+       {
+         Dwarf_Word range_address;
+         Dwarf_Word range_length;
+         Dwarf_Word segment = 0;
+         if (readp + 2 * address_size + segment_size > readendp)
+           goto invalid_data;
+         if (address_size == 4)
+           {
+             range_address = read_4ubyte_unaligned_inc (dbg, readp);
+             range_length = read_4ubyte_unaligned_inc (dbg, readp);
+           }
+         else
+           {
+             range_address = read_8ubyte_unaligned_inc (dbg, readp);
+             range_length = read_8ubyte_unaligned_inc (dbg, readp);
+           }
+
+         if (segment_size == 4)
+           segment = read_4ubyte_unaligned_inc (dbg, readp);
+         else if (segment_size == 8)
+           segment = read_8ubyte_unaligned_inc (dbg, readp);
+
+         if (range_address == 0 && range_length == 0 && segment == 0)
+           break;
+
+         char *b = format_dwarf_addr (dwflmod, address_size, range_address,
+                                      range_address);
+         char *e = format_dwarf_addr (dwflmod, address_size,
+                                      range_address + range_length - 1,
+                                      range_length);
+         if (segment_size != 0)
+           printf (gettext ("   %s..%s (%" PRIx64 ")\n"), b, e,
+                   (uint64_t) segment);
+         else
+           printf (gettext ("   %s..%s\n"), b, e);
+         free (b);
+         free (e);
+       }
+
+    next_table:
+      if (readp != nexthdr)
+       {
+         size_t padding = nexthdr - readp;
+         printf (gettext ("   %Zu padding bytes\n"), padding);
+         readp = nexthdr;
+       }
+    }
+}
+
+
 /* Print content of DWARF .debug_ranges section.  */
 static void
 print_debug_ranges_section (Dwfl_Module *dwflmod,
index 3465777..2da85cf 100644 (file)
@@ -1,5 +1,12 @@
 2013-03-25  Mark Wielaard  <mjw@redhat.com>
 
+       * run-readelf-aranges.sh: New test.
+       * testfilefoobarbaz.bz2: New test file.
+       * Makefile.am (TESTS): Add run-readelf-aranges.sh.
+       (EXTRA_DIST): Add run-readelf-aranges.sh and testfilefoobarbaz.bz2.
+
+2013-03-25  Mark Wielaard  <mjw@redhat.com>
+
        * run-readelf-dwz-multi.sh: Expect high_pc also as address.
 
 2013-03-20  Jan Kratochvil  <jan.kratochvil@redhat.com>
index 9b0bbf1..4e63039 100644 (file)
@@ -75,6 +75,7 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
        run-readelf-test1.sh run-readelf-test2.sh run-readelf-test3.sh \
        run-readelf-test4.sh run-readelf-twofiles.sh \
        run-readelf-macro.sh run-readelf-loc.sh \
+       run-readelf-aranges.sh \
        run-native-test.sh run-bug1-test.sh \
        dwfl-bug-addr-overflow run-addrname-test.sh \
        dwfl-bug-fd-leak dwfl-bug-report \
@@ -157,6 +158,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
             testfile49.bz2 testfile50.bz2 testfile51.bz2 \
             run-readelf-macro.sh testfilemacro.bz2 \
             run-readelf-loc.sh testfileloc.bz2 \
+            run-readelf-aranges.sh testfilefoobarbaz.bz2 \
             run-readelf-dwz-multi.sh libtestfile_multi_shared.so.bz2 \
             testfile_multi.dwz.bz2 testfile_multi_main.bz2 \
             testfile-dwzstr.bz2 testfile-dwzstr.multi.bz2 \
diff --git a/tests/run-readelf-aranges.sh b/tests/run-readelf-aranges.sh
new file mode 100755 (executable)
index 0000000..a610fca
--- /dev/null
@@ -0,0 +1,161 @@
+#! /bin/sh
+# Copyright (C) 2013 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# elfutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/test-subr.sh
+
+# Tests readelf --debug-dump=aranges and --debug-dump=decodedaranges
+#
+# - foobarbaz.h
+#
+# int bar ();
+# int baz (int i);
+#
+# - bar.c
+#
+# #include "foobarbaz.h"
+#
+# static int bi;
+#
+# static int
+# barbaz (int i)
+# {
+#   return i * 2 - 1;
+# }
+#
+# __attribute__ ((constructor)) void
+# nobar ()
+# {
+#   bi = 1;
+# }
+#
+# int
+# bar ()
+# {
+#   bi++;
+#   return barbaz (bi);
+# }
+#
+# - foo.c
+#
+# include "foobarbaz.h"
+#
+# static int fi = 0;
+#
+# static int
+# foo (int i, int j)
+# {
+#   if (i > j)
+#     return i - j + fi;
+#   else
+#     return (2 * j) - i + fi;
+# }
+#
+# int
+# main (int argc, char **argv)
+# {
+#   int a = bar ();
+#   int b = baz (a + argc);
+#   int r = foo (a, b) - 1;
+#
+#   return r - 48;
+# }
+#
+# - baz.c
+# include "foobarbaz.h"
+#
+# static int bj;
+#
+# static int
+# bazbaz (int j)
+# {
+#   return bj * j - bar ();
+# }
+#
+# __attribute__ ((constructor)) void
+# nobaz ()
+# {
+#   bj = 1;
+# }
+#
+# int
+# baz (int i)
+# {
+#   if (i < 0)
+#     return bazbaz (i);
+#   else
+#     {
+#       while (i-- > 0)
+#         bj += bar ();
+#     }
+#   return bazbaz (i);
+# }
+#
+# gcc -g -O2 -m32 -c baz.c
+# gcc -g -O2 -m32 -c bar.c
+# gcc -g -O2 -m32 -c foo.c
+# gcc -g -O2 -m32 -o testfilefoobarbaz foo.o bar.o baz.o
+
+testfiles testfilefoobarbaz
+
+testrun_compare ../src/readelf --debug-dump=aranges testfilefoobarbaz <<EOF
+
+DWARF section [27] '.debug_aranges' at offset 0x1044:
+
+Table at offset 0:
+
+ Length:            28
+ DWARF version:      2
+ CU offset:          0
+ Address size:       4
+ Segment size:       0
+
+   0x080482f0 <main>..0x08048323 <main+0x33>
+
+Table at offset 32:
+
+ Length:            36
+ DWARF version:      2
+ CU offset:        136
+ Address size:       4
+ Segment size:       0
+
+   0x08048440 <bar>..0x08048451 <bar+0x11>
+   0x08048330 <nobar>..0x0804833a <nobar+0xa>
+
+Table at offset 72:
+
+ Length:            36
+ DWARF version:      2
+ CU offset:        1d1
+ Address size:       4
+ Segment size:       0
+
+   0x08048460 <baz>..0x080484bb <baz+0x5b>
+   0x08048340 <nobaz>..0x0804834a <nobaz+0xa>
+EOF
+
+testrun_compare ../src/readelf --debug-dump=decodedaranges testfilefoobarbaz <<\EOF
+
+DWARF section [27] '.debug_aranges' at offset 0x1044 contains 5 entries:
+ [0] start: 0x080482f0, length:    52, CU DIE offset:     11
+ [1] start: 0x08048330, length:    11, CU DIE offset:    321
+ [2] start: 0x08048340, length:    11, CU DIE offset:    476
+ [3] start: 0x08048440, length:    18, CU DIE offset:    321
+ [4] start: 0x08048460, length:    92, CU DIE offset:    476
+EOF
+
+exit 0
diff --git a/tests/testfilefoobarbaz.bz2 b/tests/testfilefoobarbaz.bz2
new file mode 100755 (executable)
index 0000000..0e721ff
Binary files /dev/null and b/tests/testfilefoobarbaz.bz2 differ