Support DW_AT_count DWARF attribute
authorDodji Seketeli <dodji@redhat.com>
Sat, 7 Nov 2015 21:52:10 +0000 (22:52 +0100)
committerDodji Seketeli <dodji@redhat.com>
Sat, 7 Nov 2015 22:22:19 +0000 (23:22 +0100)
Libabigail's DWARF reader does not support the DW_AT_count attribute
used to specify the number of elements in an array subrange.  Rather,
it uses the DW_AT_lower_bound and DW_AT_upper_bound attributes that
are emitted by GCC.  Recent versions of Clang, on the other hand, use
the DW_AT_count attribute.

This patch adds support for the DW_AT_count attribute too.

* src/abg-dwarf-reader.cc (get_default_array_lower_bound): Define
new static function.
(build_array_type): Support the DW_AT_count attribute.
* tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-clang.so:
New test binary input.
* tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-clang2.so: Likewise.
* tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-clang-report-0.txt:
New test reference output.
* tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-gcc.so:
New test binary input.
* tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-gcc2.so:
New test binary input.
* tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-gcc-report-0.txt:
New test reference output.
* tests/data/test-diff-dwarf/test35-pr19173-libfoo-long.c: Source
code for the binaries above.
* tests/data/Makefile.am: Add the new test material to the build
system.
* tests/test-diff-dwarf.cc (in_out_specs): Add the new test inputs
to the harness.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
src/abg-dwarf-reader.cc
tests/data/Makefile.am
tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-clang-report-0.txt [new file with mode: 0644]
tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-clang.so [new file with mode: 0755]
tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-clang2.so [new file with mode: 0755]
tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-gcc-report-0.txt [new file with mode: 0644]
tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-gcc.so [new file with mode: 0755]
tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-gcc2.so [new file with mode: 0755]
tests/data/test-diff-dwarf/test35-pr19173-libfoo-long.c [new file with mode: 0644]
tests/test-diff-dwarf.cc

index fc43698..21a648e 100644 (file)
@@ -144,6 +144,9 @@ maybe_canonicalize_type(Dwarf_Off   die_offset,
                        bool            in_alt_di,
                        read_context&   ctxt);
 
+static int
+get_default_array_lower_bound(translation_unit::language l);
+
 /// Convert an elf symbol type (given by the ELF{32,64}_ST_TYPE
 /// macros) into an elf_symbol::type value.
 ///
@@ -6092,6 +6095,64 @@ dwarf_language_to_tu_language(size_t l)
     }
 }
 
+/// Get the default array lower bound value as defined by the DWARF
+/// specification, version 4, depending on the language of the
+/// translation unit.
+///
+/// @param l the language of the translation unit.
+///
+/// @return the default array lower bound value.
+static int
+get_default_array_lower_bound(translation_unit::language l)
+{
+  int value = 0;
+    switch (l)
+    {
+    case translation_unit::LANG_UNKNOWN:
+      value = 0;
+      break;
+    case translation_unit::LANG_Cobol74:
+    case translation_unit::LANG_Cobol85:
+      value = 1;
+      break;
+    case translation_unit::LANG_C89:
+    case translation_unit::LANG_C99:
+    case translation_unit::LANG_C11:
+    case translation_unit::LANG_C:
+    case translation_unit::LANG_C_plus_plus_11:
+    case translation_unit::LANG_C_plus_plus_14:
+    case translation_unit::LANG_C_plus_plus:
+    case translation_unit::LANG_ObjC:
+    case translation_unit::LANG_ObjC_plus_plus:
+      value = 0;
+      break;
+    case translation_unit::LANG_Fortran77:
+    case translation_unit::LANG_Fortran90:
+    case translation_unit::LANG_Fortran95:
+    case translation_unit::LANG_Ada83:
+    case translation_unit::LANG_Ada95:
+    case translation_unit::LANG_Pascal83:
+    case translation_unit::LANG_Modula2:
+      value = 1;
+      break;
+    case translation_unit::LANG_Java:
+      value = 0;
+      break;
+    case translation_unit::LANG_PL1:
+      value = 1;
+      break;
+    case translation_unit::LANG_UPC:
+    case translation_unit::LANG_D:
+    case translation_unit::LANG_Python:
+    case translation_unit::LANG_Go:
+    case translation_unit::LANG_Mips_Assembler:
+      value = 0;
+      break;
+    }
+
+    return value;
+}
+
 /// Given a DW_TAG_compile_unit, build and return the corresponding
 /// abigail::translation_unit ir node.  Note that this function
 /// recursively reads the children dies of the current DIE and
@@ -7265,7 +7326,11 @@ build_array_type(read_context&   ctxt,
 
   Dwarf_Die child;
   array_type_def::subranges_type subranges;
-  size_t upper_bound, lower_bound = 0;
+  translation_unit::language language =
+    ctxt.current_translation_unit()->get_language();
+  size_t upper_bound = 0;
+  size_t lower_bound = get_default_array_lower_bound(language);
+  size_t count = 0;
 
   if (dwarf_child(die, &child) == 0)
     {
@@ -7274,15 +7339,48 @@ build_array_type(read_context&  ctxt,
          int child_tag = dwarf_tag(&child);
          if (child_tag == DW_TAG_subrange_type)
            {
-             // usually not specified for C/C++
+             // The DWARF 4 specifications says, in [5.11 Subrange
+             // Type Entries]:
+             //
+             //     The subrange entry may have the attributes
+             //     DW_AT_lower_bound and DW_AT_upper_bound to
+             //     specify, respectively, the lower and upper bound
+             //     values of the subrange.
+             //
+             // So let's look for DW_AT_lower_bound first.
              die_unsigned_constant_attribute(&child,
                                              DW_AT_lower_bound,
                                              lower_bound);
 
+             // Then, DW_AT_upper_bound.
              if (!die_unsigned_constant_attribute(&child,
                                                   DW_AT_upper_bound,
                                                   upper_bound))
-               return result;
+               {
+                 // The DWARF 4 spec says, in [5.11 Subrange Type
+                 // Entries]:
+                 //
+                 //   The DW_AT_upper_bound attribute may be replaced
+                 //   by a DW_AT_count attribute, whose value
+                 //   describes the number of elements in the
+                 //   subrange rather than the value of the last
+                 //   element."
+                 //
+                 // So, as DW_AT_upper_bound is not present in this
+                 // case, let's see if there is a DW_AT_count.
+                 if (!die_unsigned_constant_attribute(&child,
+                                                      DW_AT_count,
+                                                      count))
+                   // We have no information about the number of
+                   // elements of the array.  Let's bail out then.
+                   return result;
+
+                 // We can deduce the upper_bound from the
+                 // lower_bound and the number of elements of the
+                 // array:
+                 if (size_t u = lower_bound + count)
+                   upper_bound = u - 1;
+               }
 
              array_type_def::subrange_sptr s
                (new array_type_def::subrange_type(lower_bound,
index 23363d6..7ab72fc 100644 (file)
@@ -249,6 +249,12 @@ test-diff-dwarf/test33-fnref-changes-v1.o \
 test-diff-dwarf/test34-pr19173-libfoo.so \
 test-diff-dwarf/test34-pr19173-libfoo2.so \
 test-diff-dwarf/test34-pr19173-libfoo-report-0.txt \
+test-diff-dwarf/test35-pr19173-libfoo-long-gcc.so \
+test-diff-dwarf/test35-pr19173-libfoo-long-gcc2.so \
+test-diff-dwarf/test35-pr19173-libfoo-long-gcc-report-0.txt \
+test-diff-dwarf/test35-pr19173-libfoo-long-clang.so \
+test-diff-dwarf/test35-pr19173-libfoo-long-clang2.so \
+test-diff-dwarf/test35-pr19173-libfoo-long-clang-report-0.txt \
 \
 test-read-dwarf/test0                  \
 test-read-dwarf/test0.abi                      \
diff --git a/tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-clang-report-0.txt b/tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-clang-report-0.txt
new file mode 100644 (file)
index 0000000..56a8961
--- /dev/null
@@ -0,0 +1,13 @@
+Functions changes summary: 0 Removed, 0 Changed, 0 Added function
+Variables changes summary: 0 Removed, 1 Changed, 0 Added variable
+
+1 Changed variable:
+
+  [C]'char buggy_symbol[5]' was changed to 'char buggy_symbol[10]':
+    size of symbol (in bytes) changed from 5 to 10
+    type of variable changed:
+     type name changed from 'char[5]' to 'char[10]'
+     array type size changed from 40 to 80 bits:
+     array type subrange 1 changed length from 5 to 10
+
+
diff --git a/tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-clang.so b/tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-clang.so
new file mode 100755 (executable)
index 0000000..4b68a63
Binary files /dev/null and b/tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-clang.so differ
diff --git a/tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-clang2.so b/tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-clang2.so
new file mode 100755 (executable)
index 0000000..8e9f6ce
Binary files /dev/null and b/tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-clang2.so differ
diff --git a/tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-gcc-report-0.txt b/tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-gcc-report-0.txt
new file mode 100644 (file)
index 0000000..56a8961
--- /dev/null
@@ -0,0 +1,13 @@
+Functions changes summary: 0 Removed, 0 Changed, 0 Added function
+Variables changes summary: 0 Removed, 1 Changed, 0 Added variable
+
+1 Changed variable:
+
+  [C]'char buggy_symbol[5]' was changed to 'char buggy_symbol[10]':
+    size of symbol (in bytes) changed from 5 to 10
+    type of variable changed:
+     type name changed from 'char[5]' to 'char[10]'
+     array type size changed from 40 to 80 bits:
+     array type subrange 1 changed length from 5 to 10
+
+
diff --git a/tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-gcc.so b/tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-gcc.so
new file mode 100755 (executable)
index 0000000..737c5f0
Binary files /dev/null and b/tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-gcc.so differ
diff --git a/tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-gcc2.so b/tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-gcc2.so
new file mode 100755 (executable)
index 0000000..2ea5b96
Binary files /dev/null and b/tests/data/test-diff-dwarf/test35-pr19173-libfoo-long-gcc2.so differ
diff --git a/tests/data/test-diff-dwarf/test35-pr19173-libfoo-long.c b/tests/data/test-diff-dwarf/test35-pr19173-libfoo-long.c
new file mode 100644 (file)
index 0000000..1ed4887
--- /dev/null
@@ -0,0 +1,15 @@
+// This file is the source code for 4 binaries.
+// It comes from one of the comments of bug libabigail/19173.
+
+// To compile the first two binaries, please do:
+//  gcc test35-pr19173-libfoo-long.c -shared -fpic -o test35-pr19173-libfoo-long-gcc.so -g
+//  gcc test35-pr19173-libfoo-long.c -shared -fpic -o test35-pr19173-libfoo-long-gcc2.so -g -DLONG
+//
+// To compile the next two binaries, please do:
+// clang test35-pr19173-libfoo-long.c -shared -fpic -o test35-pr19173-libfoo-long-clang.so -g
+//  clang test35-pr19173-libfoo-long.c -shared -fpic -o test35-pr19173-libfoo-long-clang2.so -g -DLONG
+#ifdef LONG
+char buggy_symbol[10];
+#else
+char buggy_symbol[5];
+#endif
index d148f1a..9e41c79 100644 (file)
@@ -272,6 +272,18 @@ InOutSpec in_out_specs[] =
     "data/test-diff-dwarf/test34-pr19173-libfoo-report-0.txt",
     "output/test-diff-dwarf/test34-pr19173-libfoo-report-0.txt"
   },
+  {
+    "data/test-diff-dwarf/test35-pr19173-libfoo-long-gcc.so",
+    "data/test-diff-dwarf/test35-pr19173-libfoo-long-gcc2.so",
+    "data/test-diff-dwarf/test35-pr19173-libfoo-long-gcc-report-0.txt",
+    "output/test-diff-dwarf/test35-pr19173-libfoo-long-gcc-report-0.txt"
+  },
+  {
+    "data/test-diff-dwarf/test35-pr19173-libfoo-long-clang.so",
+    "data/test-diff-dwarf/test35-pr19173-libfoo-long-clang2.so",
+    "data/test-diff-dwarf/test35-pr19173-libfoo-long-clang-report-0.txt",
+    "output/test-diff-dwarf/test35-pr19173-libfoo-long-clang-report-0.txt"
+  },
   // This should be the last entry
   {NULL, NULL, NULL, NULL}
 };