review request: implementing DW_AT_endianity

Peeter Joot peeter.joot@lzlabs.com
Fri Oct 6 15:06:00 GMT 2017


I've implemented support in gdb for DW_AT_endianity, DW_END_bigendian, and DW_END_littleendian, and would like to ask for gdb community review of this change.


Purpose: gcc6+ supports mixed endian attributes on structures and unions, and flags these dwarf instrumentation of these structure members with DW_END_... attributes.  As intel appears to have introduced these dwarf flags into the standard, I expect their compiler and debugger also supports them.  However, gdb does not.  The following example, compiled on a little endian system, is an example of this attribute use:


#include <stdio.h>

#include <string.h>


struct big {

    int v;

    short a[4];

} __attribute__( ( scalar_storage_order( "big-endian" ) ) );


struct little {

    int v;

    short a[4];

} __attribute__( ( scalar_storage_order( "little-endian" ) ) );


struct native {

    int v;

    short a[4];

};


int main() {

    struct big b = {3, {1, 2, 3, 4}};

    struct native n = {3, {1, 2, 3, 4}};

    struct little l = {3, {1, 2, 3, 4}};

    int cb = memcmp( &b, &n, sizeof( b ) );

    int cl = memcmp( &l, &n, sizeof( l ) );


    printf( "%d %d %d: big %s native.  little %s native\n", b.v, n.v, l.v,

            cb == 0 ? "==" : "!=", cl == 0 ? "==" : "!=" );

    return 0;

}



Running this produces the expected result, and the endianness of the underlying stores can be seen in a debugger session:


Breakpoint 1, main () at test.c:20

20          struct big b = {3, {1, 2, 3, 4}};

(gdb) n

21          struct native n = {3, {1, 2, 3, 4}};

(gdb) n

22          struct little l = {3, {1, 2, 3, 4}};

(gdb) n

23          int cb = memcmp( &b, &n, sizeof( b ) );

(gdb) p b

$1 = {v = 50331648, a = {256, 512, 768, 1024}}

(gdb) p n

$2 = {v = 3, a = {1, 2, 3, 4}}

(gdb) p l

$3 = {v = 3, a = {1, 2, 3, 4}}

(gdb) x/4x &b

0x7fffffffd96c: 0x03000000      0x02000100      0x04000300      0x00000000

(gdb) x/4x &l

0x7fffffffd954: 0x00000003      0x00020001      0x00040003      0x00000003

(gdb) x/4x &n

0x7fffffffd960: 0x00000003      0x00020001      0x00040003      0x03000000

(gdb) n

24          int cl = memcmp( &l, &n, sizeof( l ) );

(gdb)

26          printf( "%d %d %d: big %s native.  little %s native\n", b.v, n.v, l.v,

(gdb) n

3 3 3: big != native.  little == native

28          return 0;



This debugger session also shows that gdb currently ignores the DW_END_bigendian (and littleendian) attributes, showing the internal representation of the data instead of what the values that memory represents.  It turns out that propagating the DW_AT_endianity dwarf attributes to the gdb print_scalar_formatted function is fairly straightforward, which allows gdb to display the results in a user-friendly way regardless of the endian attributes:


diff --git a/binutils/dwarf.c b/binutils/dwarf.c
index 91f95ff..e79fd5e 100644
--- a/binutils/dwarf.c
+++ b/binutils/dwarf.c
@@ -2283,6 +2283,17 @@ read_and_display_attr_value (unsigned long attribute,
  }
       break;

+    case DW_AT_endianity:
+      printf ("\t");
+      switch (uvalue)
+ {
+ case DW_END_default: printf ("(default)"); break;
+ case DW_END_big: printf ("(big)"); break;
+ case DW_END_little: printf ("(little)"); break;
+ default: printf (_("(unknown endianity)")); break;
+ }
+      break;
+
     case DW_AT_virtuality:
       printf ("\t");
       switch (uvalue)
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 18224e0..66a8eaf 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,19 @@
+2017-10-06  Peeter Joot  <peeter.joot@lzlabs.com>
+
+ * gdb/gdbtypes.h (global scope): define TYPE_ENDIANITY_BIG,
+ TYPE_ENDIANITY_LITTLE.
+ * binutils/dwarf.c (read_and_display_attr_value): Handle
+ DW_AT_endianity, DW_END_default, DW_END_big, DW_END_little
+ * gdb/dwarf2read.c (read_base_type): Handle DW_END_big, DW_END_little
+ * gdb/gdbtypes.c (check_types_equal): Require matching
+ TYPE_ENDIANITY_BIG, and TYPE_ENDIANITY_LITTLE if set.
+ (recursive_dump_type): Print TYPE_ENDIANITY_BIG, and
+ TYPE_ENDIANITY_LITTLE if set.
+ (struct main_type): Add flag_endianity_big, flag_endianity_little
+ * gdb/printcmd.c (print_scalar_formatted): Use compiler supplied
+ endianness instead of arch endianness if TYPE_ENDIANITY_BIG or
+ TYPE_ENDIANITY_LITTLE is set.
+
 2017-10-06  Yao Qi  <yao.qi@linaro.org>

  * Makefile.in (ALL_64_TARGET_OBS): Replace aarch64-insn.o with
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 1b15adc..fa2889b 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -15234,6 +15234,7 @@ read_base_type (struct die_info *die, struct dwarf2_cu *cu)
   struct type *type;
   struct attribute *attr;
   int encoding = 0, bits = 0;
+  int endianity = 0;
   const char *name;

   attr = dwarf2_attr (die, DW_AT_encoding, cu);
@@ -15330,6 +15331,21 @@ read_base_type (struct die_info *die, struct dwarf2_cu *cu)
   if (name && strcmp (name, "char") == 0)
     TYPE_NOSIGN (type) = 1;

+  attr = dwarf2_attr (die, DW_AT_endianity, cu);
+  if ( attr )
+    {
+      endianity = DW_UNSND (attr);
+      switch (endianity)
+        {
+           case DW_END_big:
+              TYPE_ENDIANITY_BIG (type) = 1;
+              break;
+           case DW_END_little:
+              TYPE_ENDIANITY_LITTLE (type) = 1;
+              break;
+        }
+    }
+
   return set_die_type (die, type, cu);
 }

diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index 73d4453..43f553b 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -3423,6 +3423,8 @@ check_types_equal (struct type *type1, struct type *type2,
       || TYPE_LENGTH (type1) != TYPE_LENGTH (type2)
       || TYPE_UNSIGNED (type1) != TYPE_UNSIGNED (type2)
       || TYPE_NOSIGN (type1) != TYPE_NOSIGN (type2)
+      || TYPE_ENDIANITY_BIG (type1) != TYPE_ENDIANITY_BIG (type2)
+      || TYPE_ENDIANITY_LITTLE (type1) != TYPE_ENDIANITY_LITTLE (type2)
       || TYPE_VARARGS (type1) != TYPE_VARARGS (type2)
       || TYPE_VECTOR (type1) != TYPE_VECTOR (type2)
       || TYPE_NOTTEXT (type1) != TYPE_NOTTEXT (type2)
@@ -4460,6 +4462,14 @@ recursive_dump_type (struct type *type, int spaces)
     {
       puts_filtered (" TYPE_NOSIGN");
     }
+  if (TYPE_ENDIANITY_BIG (type))
+    {
+      puts_filtered (" TYPE_ENDIANITY_BIG");
+    }
+  if (TYPE_ENDIANITY_LITTLE (type))
+    {
+      puts_filtered (" TYPE_ENDIANITY_LITTLE");
+    }
   if (TYPE_STUB (type))
     {
       puts_filtered (" TYPE_STUB");
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index 009cea9..074aa2c 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -210,6 +210,16 @@ DEF_ENUM_FLAGS_TYPE (enum type_instance_flag_value, type_instance_flags);

 #define TYPE_NOSIGN(t) (TYPE_MAIN_TYPE (t)->flag_nosign)

+/* * Mixed endian archetectures can supply dwarf instrumentation
+ * that indicates the desired endian interpretation of the variable.
+ * This indicates that the interpretation should be big-endian
+ * even if the cpu is running in little endian mode. */
+#define TYPE_ENDIANITY_BIG(t) (TYPE_MAIN_TYPE (t)->flag_endianity_big)
+
+/* * The type has a little endian interpretation even if the cpu
+ * is running in big endian mode. */
+#define TYPE_ENDIANITY_LITTLE(t) (TYPE_MAIN_TYPE (t)->flag_endianity_little)
+
 /* * This appears in a type's flags word if it is a stub type (e.g.,
    if someone referenced a type that wasn't defined in a source file
    via (struct sir_not_appearing_in_this_film *)).  */
@@ -616,6 +626,8 @@ struct main_type
   unsigned int flag_gnu_ifunc : 1;
   unsigned int flag_fixed_instance : 1;
   unsigned int flag_objfile_owned : 1;
+  unsigned int flag_endianity_big : 1;
+  unsigned int flag_endianity_little : 1;

   /* * True if this type was declared with "class" rather than
      "struct".  */
diff --git a/gdb/printcmd.c b/gdb/printcmd.c
index a8743f1..e714283 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -356,6 +356,15 @@ print_scalar_formatted (const gdb_byte *valaddr, struct type *type,
   unsigned int len = TYPE_LENGTH (type);
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);

+  if ( TYPE_ENDIANITY_BIG (type) )
+  {
+    byte_order = BFD_ENDIAN_BIG;
+  }
+  if ( TYPE_ENDIANITY_LITTLE (type) )
+  {
+    byte_order = BFD_ENDIAN_LITTLE;
+  }
+
   /* String printing should go through val_print_scalar_formatted.  */
   gdb_assert (options->format != 's');



On behalf of my employer (LzLabs), I would like to contribute this change to to the gdb project.  For a small change like this, it is not clear FSF copyright assignment is required, as I see the following in your CONTRIBUTIONS document: "Small changes can be accepted without a copyright assignment form on file.".  What counts as small?


If assignment is required, I have approval from company management and legal to formally go through that process.  Before figuring out how that assignment process works, I wanted to first ensure the changes I've made would be acceptable for contribution, and make any review driven updates required.


--

Peeter



More information about the Gdb-patches mailing list