From 2b4424c35b9ebabaab8588b2ba6c38935a48efec Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Fri, 20 Apr 2018 11:50:09 -0600 Subject: [PATCH] Add initial type alignment support This adds some basic type alignment support to gdb. It changes struct type to store the alignment, and updates dwarf2read.c to handle DW_AT_alignment. It also adds a new gdbarch method and updates i386-tdep.c. None of this new functionality is used anywhere yet, so tests will wait until the next patch. 2018-04-30 Tom Tromey * i386-tdep.c (i386_type_align): New function. (i386_gdbarch_init): Update. * gdbarch.sh (type_align): New method. * gdbarch.c, gdbarch.h: Rebuild. * arch-utils.h (default_type_align): Declare. * arch-utils.c (default_type_align): New function. * gdbtypes.h (TYPE_ALIGN_BITS): New define. (struct type) : New field. : Now a bitfield. (TYPE_RAW_ALIGN): New macro. (type_align, type_raw_align, set_type_align): Declare. * gdbtypes.c (type_align, type_raw_align, set_type_align): New functions. * dwarf2read.c (quirk_rust_enum): Set type alignment. (get_alignment, maybe_set_alignment): New functions. (read_structure_type, read_enumeration_type, read_array_type) (read_set_type, read_tag_pointer_type, read_tag_reference_type) (read_subrange_type, read_base_type): Set type alignment. --- gdb/ChangeLog | 21 ++++++++++ gdb/arch-utils.c | 8 ++++ gdb/arch-utils.h | 4 ++ gdb/dwarf2read.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++-- gdb/gdbarch.c | 23 +++++++++++ gdb/gdbarch.h | 6 +++ gdb/gdbarch.sh | 3 ++ gdb/gdbtypes.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ gdb/gdbtypes.h | 34 +++++++++++++++- gdb/i386-tdep.c | 26 ++++++++++++ 10 files changed, 359 insertions(+), 5 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 2a68a1b..320f9c2 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,24 @@ +2018-04-30 Tom Tromey + + * i386-tdep.c (i386_type_align): New function. + (i386_gdbarch_init): Update. + * gdbarch.sh (type_align): New method. + * gdbarch.c, gdbarch.h: Rebuild. + * arch-utils.h (default_type_align): Declare. + * arch-utils.c (default_type_align): New function. + * gdbtypes.h (TYPE_ALIGN_BITS): New define. + (struct type) : New field. + : Now a bitfield. + (TYPE_RAW_ALIGN): New macro. + (type_align, type_raw_align, set_type_align): Declare. + * gdbtypes.c (type_align, type_raw_align, set_type_align): New + functions. + * dwarf2read.c (quirk_rust_enum): Set type alignment. + (get_alignment, maybe_set_alignment): New functions. + (read_structure_type, read_enumeration_type, read_array_type) + (read_set_type, read_tag_pointer_type, read_tag_reference_type) + (read_subrange_type, read_base_type): Set type alignment. + 2018-04-30 Simon Marchi * dwarf2read.c (read_index_from_section): Use bool. diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c index cd9bd66..e3cce49 100644 --- a/gdb/arch-utils.c +++ b/gdb/arch-utils.c @@ -987,6 +987,14 @@ default_in_indirect_branch_thunk (gdbarch *gdbarch, CORE_ADDR pc) return false; } +/* See arch-utils.h. */ + +ULONGEST +default_type_align (struct gdbarch *gdbarch, struct type *type) +{ + return TYPE_LENGTH (check_typedef (type)); +} + void _initialize_gdbarch_utils (void) { diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h index b785b24..77ee9af 100644 --- a/gdb/arch-utils.h +++ b/gdb/arch-utils.h @@ -267,4 +267,8 @@ extern CORE_ADDR gdbarch_skip_prologue_noexcept (gdbarch *gdbarch, extern bool default_in_indirect_branch_thunk (gdbarch *gdbarch, CORE_ADDR pc); +/* Default implementation of gdbarch type_align method. */ +extern ULONGEST default_type_align (struct gdbarch *gdbarch, + struct type *type); + #endif diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c index fb10964..380ff36 100644 --- a/gdb/dwarf2read.c +++ b/gdb/dwarf2read.c @@ -9907,6 +9907,7 @@ quirk_rust_enum (struct type *type, struct objfile *objfile) TYPE_FIELDS (union_type) = (struct field *) TYPE_ZALLOC (type, 3 * sizeof (struct field)); TYPE_LENGTH (union_type) = TYPE_LENGTH (type); + set_type_align (union_type, TYPE_RAW_ALIGN (type)); /* Put the discriminant must at index 0. */ TYPE_FIELD_TYPE (union_type, 0) = field_type; @@ -9962,6 +9963,7 @@ quirk_rust_enum (struct type *type, struct objfile *objfile) TYPE_CODE (union_type) = TYPE_CODE_UNION; TYPE_NFIELDS (union_type) = TYPE_NFIELDS (type); TYPE_LENGTH (union_type) = TYPE_LENGTH (type); + set_type_align (union_type, TYPE_RAW_ALIGN (type)); TYPE_FIELDS (union_type) = TYPE_FIELDS (type); struct type *field_type = TYPE_FIELD_TYPE (union_type, 0); @@ -10027,6 +10029,7 @@ quirk_rust_enum (struct type *type, struct objfile *objfile) TYPE_CODE (union_type) = TYPE_CODE_UNION; TYPE_NFIELDS (union_type) = 1 + TYPE_NFIELDS (type); TYPE_LENGTH (union_type) = TYPE_LENGTH (type); + set_type_align (union_type, TYPE_RAW_ALIGN (type)); TYPE_FIELDS (union_type) = (struct field *) TYPE_ZALLOC (union_type, (TYPE_NFIELDS (union_type) @@ -15578,6 +15581,82 @@ quirk_gcc_member_function_pointer (struct type *type, struct objfile *objfile) smash_to_methodptr_type (type, new_type); } +/* If the DIE has a DW_AT_alignment attribute, return its value, doing + appropriate error checking and issuing complaints if there is a + problem. */ + +static ULONGEST +get_alignment (struct dwarf2_cu *cu, struct die_info *die) +{ + struct attribute *attr = dwarf2_attr (die, DW_AT_alignment, cu); + + if (attr == nullptr) + return 0; + + if (!attr_form_is_constant (attr)) + { + complaint (&symfile_complaints, + _("DW_AT_alignment must have constant form" + " - DIE at %s [in module %s]"), + sect_offset_str (die->sect_off), + objfile_name (cu->per_cu->dwarf2_per_objfile->objfile)); + return 0; + } + + ULONGEST align; + if (attr->form == DW_FORM_sdata) + { + LONGEST val = DW_SND (attr); + if (val < 0) + { + complaint (&symfile_complaints, + _("DW_AT_alignment value must not be negative" + " - DIE at %s [in module %s]"), + sect_offset_str (die->sect_off), + objfile_name (cu->per_cu->dwarf2_per_objfile->objfile)); + return 0; + } + align = val; + } + else + align = DW_UNSND (attr); + + if (align == 0) + { + complaint (&symfile_complaints, + _("DW_AT_alignment value must not be zero" + " - DIE at %s [in module %s]"), + sect_offset_str (die->sect_off), + objfile_name (cu->per_cu->dwarf2_per_objfile->objfile)); + return 0; + } + if ((align & (align - 1)) != 0) + { + complaint (&symfile_complaints, + _("DW_AT_alignment value must be a power of 2" + " - DIE at %s [in module %s]"), + sect_offset_str (die->sect_off), + objfile_name (cu->per_cu->dwarf2_per_objfile->objfile)); + return 0; + } + + return align; +} + +/* If the DIE has a DW_AT_alignment attribute, use its value to set + the alignment for TYPE. */ + +static void +maybe_set_alignment (struct dwarf2_cu *cu, struct die_info *die, + struct type *type) +{ + if (!set_type_align (type, get_alignment (cu, die))) + complaint (&symfile_complaints, + _("DW_AT_alignment value too large" + " - DIE at %s [in module %s]"), + sect_offset_str (die->sect_off), + objfile_name (cu->per_cu->dwarf2_per_objfile->objfile)); +} /* Called when we find the DIE that starts a structure or union scope (definition) to create a type for the structure or union. Fill in @@ -15688,6 +15767,8 @@ read_structure_type (struct die_info *die, struct dwarf2_cu *cu) TYPE_LENGTH (type) = 0; } + maybe_set_alignment (cu, die, type); + if (producer_is_icc_lt_14 (cu) && (TYPE_LENGTH (type) == 0)) { /* ICC<14 does not output the required DW_AT_declaration on @@ -16132,6 +16213,8 @@ read_enumeration_type (struct die_info *die, struct dwarf2_cu *cu) TYPE_LENGTH (type) = 0; } + maybe_set_alignment (cu, die, type); + /* The enumeration DIE can be incomplete. In Ada, any type can be declared as private in the package spec, and then defined only inside the package body. Such types are known as Taft Amendment @@ -16157,6 +16240,9 @@ read_enumeration_type (struct die_info *die, struct dwarf2_cu *cu) TYPE_UNSIGNED (type) = TYPE_UNSIGNED (TYPE_TARGET_TYPE (type)); if (TYPE_LENGTH (type) == 0) TYPE_LENGTH (type) = TYPE_LENGTH (TYPE_TARGET_TYPE (type)); + if (TYPE_RAW_ALIGN (type) == 0 + && TYPE_RAW_ALIGN (TYPE_TARGET_TYPE (type)) != 0) + set_type_align (type, TYPE_RAW_ALIGN (TYPE_TARGET_TYPE (type))); } TYPE_DECLARED_CLASS (type) = dwarf2_flag_true_p (die, DW_AT_enum_class, cu); @@ -16381,6 +16467,8 @@ read_array_type (struct die_info *die, struct dwarf2_cu *cu) if (name) TYPE_NAME (type) = name; + maybe_set_alignment (cu, die, type); + /* Install the type in the die. */ set_die_type (die, type, cu); @@ -16445,6 +16533,8 @@ read_set_type (struct die_info *die, struct dwarf2_cu *cu) if (attr) TYPE_LENGTH (set_type) = DW_UNSND (attr); + maybe_set_alignment (cu, die, set_type); + return set_die_type (die, set_type, cu); } @@ -16816,10 +16906,15 @@ read_tag_pointer_type (struct die_info *die, struct dwarf2_cu *cu) else addr_class = DW_ADDR_none; - /* If the pointer size or address class is different than the - default, create a type variant marked as such and set the - length accordingly. */ - if (TYPE_LENGTH (type) != byte_size || addr_class != DW_ADDR_none) + ULONGEST alignment = get_alignment (cu, die); + + /* If the pointer size, alignment, or address class is different + than the default, create a type variant marked as such and set + the length accordingly. */ + if (TYPE_LENGTH (type) != byte_size + || (alignment != 0 && TYPE_RAW_ALIGN (type) != 0 + && alignment != TYPE_RAW_ALIGN (type)) + || addr_class != DW_ADDR_none) { if (gdbarch_address_class_type_flags_p (gdbarch)) { @@ -16836,6 +16931,14 @@ read_tag_pointer_type (struct die_info *die, struct dwarf2_cu *cu) complaint (&symfile_complaints, _("invalid pointer size %d"), byte_size); } + else if (TYPE_RAW_ALIGN (type) != alignment) + { + complaint (&symfile_complaints, + _("Invalid DW_AT_alignment" + " - DIE at %s [in module %s]"), + sect_offset_str (die->sect_off), + objfile_name (cu->per_cu->dwarf2_per_objfile->objfile)); + } else { /* Should we also complain about unhandled address classes? */ @@ -16843,6 +16946,7 @@ read_tag_pointer_type (struct die_info *die, struct dwarf2_cu *cu) } TYPE_LENGTH (type) = byte_size; + set_type_align (type, alignment); return set_die_type (die, type, cu); } @@ -16912,6 +17016,7 @@ read_tag_reference_type (struct die_info *die, struct dwarf2_cu *cu, { TYPE_LENGTH (type) = cu_header->addr_size; } + maybe_set_alignment (cu, die, type); return set_die_type (die, type, cu); } @@ -17398,6 +17503,8 @@ read_base_type (struct die_info *die, struct dwarf2_cu *cu) if (name && strcmp (name, "char") == 0) TYPE_NOSIGN (type) = 1; + maybe_set_alignment (cu, die, type); + return set_die_type (die, type, cu); } @@ -17660,6 +17767,8 @@ read_subrange_type (struct die_info *die, struct dwarf2_cu *cu) if (attr) TYPE_LENGTH (range_type) = DW_UNSND (attr); + maybe_set_alignment (cu, die, range_type); + set_die_type (die, range_type, cu); /* set_die_type should be already done. */ diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index 1359c2f..dd7c89d 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -353,6 +353,7 @@ struct gdbarch gdbarch_addressable_memory_unit_size_ftype *addressable_memory_unit_size; char ** disassembler_options; const disasm_options_t * valid_disassembler_options; + gdbarch_type_align_ftype *type_align; }; /* Create a new ``struct gdbarch'' based on information provided by @@ -465,6 +466,7 @@ gdbarch_alloc (const struct gdbarch_info *info, gdbarch->gcc_target_options = default_gcc_target_options; gdbarch->gnu_triplet_regexp = default_gnu_triplet_regexp; gdbarch->addressable_memory_unit_size = default_addressable_memory_unit_size; + gdbarch->type_align = default_type_align; /* gdbarch_alloc() */ return gdbarch; @@ -716,6 +718,7 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of addressable_memory_unit_size, invalid_p == 0 */ /* Skip verify of disassembler_options, invalid_p == 0 */ /* Skip verify of valid_disassembler_options, invalid_p == 0 */ + /* Skip verify of type_align, invalid_p == 0 */ if (!log.empty ()) internal_error (__FILE__, __LINE__, _("verify_gdbarch: the following are invalid ...%s"), @@ -1442,6 +1445,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) "gdbarch_dump: target_desc = %s\n", host_address_to_string (gdbarch->target_desc)); fprintf_unfiltered (file, + "gdbarch_dump: type_align = <%s>\n", + host_address_to_string (gdbarch->type_align)); + fprintf_unfiltered (file, "gdbarch_dump: gdbarch_unwind_pc_p() = %d\n", gdbarch_unwind_pc_p (gdbarch)); fprintf_unfiltered (file, @@ -5100,6 +5106,23 @@ set_gdbarch_valid_disassembler_options (struct gdbarch *gdbarch, gdbarch->valid_disassembler_options = valid_disassembler_options; } +ULONGEST +gdbarch_type_align (struct gdbarch *gdbarch, struct type *type) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->type_align != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_type_align called\n"); + return gdbarch->type_align (gdbarch, type); +} + +void +set_gdbarch_type_align (struct gdbarch *gdbarch, + gdbarch_type_align_ftype type_align) +{ + gdbarch->type_align = type_align; +} + /* Keep a registry of per-architecture data-pointers required by GDB modules. */ diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h index 0084f19..3848ec5 100644 --- a/gdb/gdbarch.h +++ b/gdb/gdbarch.h @@ -1560,6 +1560,12 @@ extern void set_gdbarch_disassembler_options (struct gdbarch *gdbarch, char ** d extern const disasm_options_t * gdbarch_valid_disassembler_options (struct gdbarch *gdbarch); extern void set_gdbarch_valid_disassembler_options (struct gdbarch *gdbarch, const disasm_options_t * valid_disassembler_options); +/* Type alignment. */ + +typedef ULONGEST (gdbarch_type_align_ftype) (struct gdbarch *gdbarch, struct type *type); +extern ULONGEST gdbarch_type_align (struct gdbarch *gdbarch, struct type *type); +extern void set_gdbarch_type_align (struct gdbarch *gdbarch, gdbarch_type_align_ftype *type_align); + /* Definition for an unknown syscall, used basically in error-cases. */ #define UNKNOWN_SYSCALL (-1) diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh index 4fc54cb..bb62e6d 100755 --- a/gdb/gdbarch.sh +++ b/gdb/gdbarch.sh @@ -1163,6 +1163,9 @@ m;int;addressable_memory_unit_size;void;;;default_addressable_memory_unit_size;; v;char **;disassembler_options;;;0;0;;0;pstring_ptr (gdbarch->disassembler_options) v;const disasm_options_t *;valid_disassembler_options;;;0;0;;0;host_address_to_string (gdbarch->valid_disassembler_options) +# Type alignment. +m;ULONGEST;type_align;struct type *type;type;;default_type_align;;0 + EOF } diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c index 2efd126..9ecc80f 100644 --- a/gdb/gdbtypes.c +++ b/gdb/gdbtypes.c @@ -3013,6 +3013,128 @@ init_pointer_type (struct objfile *objfile, return t; } +/* See gdbtypes.h. */ + +unsigned +type_raw_align (struct type *type) +{ + if (type->align_log2 != 0) + return 1 << (type->align_log2 - 1); + return 0; +} + +/* See gdbtypes.h. */ + +unsigned +type_align (struct type *type) +{ + unsigned raw_align = type_raw_align (type); + if (raw_align != 0) + return raw_align; + + ULONGEST align = 0; + switch (TYPE_CODE (type)) + { + case TYPE_CODE_PTR: + case TYPE_CODE_FUNC: + case TYPE_CODE_FLAGS: + case TYPE_CODE_INT: + case TYPE_CODE_FLT: + case TYPE_CODE_ENUM: + case TYPE_CODE_REF: + case TYPE_CODE_RVALUE_REF: + case TYPE_CODE_CHAR: + case TYPE_CODE_BOOL: + case TYPE_CODE_DECFLOAT: + { + struct gdbarch *arch = get_type_arch (type); + align = gdbarch_type_align (arch, type); + } + break; + + case TYPE_CODE_ARRAY: + case TYPE_CODE_COMPLEX: + case TYPE_CODE_TYPEDEF: + align = type_align (TYPE_TARGET_TYPE (type)); + break; + + case TYPE_CODE_STRUCT: + case TYPE_CODE_UNION: + { + if (TYPE_NFIELDS (type) == 0) + { + /* An empty struct has alignment 1. */ + align = 1; + break; + } + for (unsigned i = 0; i < TYPE_NFIELDS (type); ++i) + { + ULONGEST f_align = type_align (TYPE_FIELD_TYPE (type, i)); + if (f_align == 0) + { + /* Don't pretend we know something we don't. */ + align = 0; + break; + } + if (f_align > align) + align = f_align; + } + } + break; + + case TYPE_CODE_SET: + case TYPE_CODE_RANGE: + case TYPE_CODE_STRING: + /* Not sure what to do here, and these can't appear in C or C++ + anyway. */ + break; + + case TYPE_CODE_METHODPTR: + case TYPE_CODE_MEMBERPTR: + align = TYPE_LENGTH (type); + break; + + case TYPE_CODE_VOID: + align = 1; + break; + + case TYPE_CODE_ERROR: + case TYPE_CODE_METHOD: + default: + break; + } + + if ((align & (align - 1)) != 0) + { + /* Not a power of 2, so pass. */ + align = 0; + } + + return align; +} + +/* See gdbtypes.h. */ + +bool +set_type_align (struct type *type, ULONGEST align) +{ + /* Must be a power of 2. Zero is ok. */ + gdb_assert ((align & (align - 1)) == 0); + + unsigned result = 0; + while (align != 0) + { + ++result; + align >>= 1; + } + + if (result >= (1 << TYPE_ALIGN_BITS)) + return false; + + type->align_log2 = result; + return true; +} + /* Queries on types. */ diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h index 552a2a2..5f614e5 100644 --- a/gdb/gdbtypes.h +++ b/gdb/gdbtypes.h @@ -802,6 +802,10 @@ struct main_type struct dynamic_prop_list *dyn_prop_list; }; +/* * Number of bits allocated for alignment. */ + +#define TYPE_ALIGN_BITS 8 + /* * A ``struct type'' describes a particular instance of a type, with some particular qualification. */ @@ -831,6 +835,14 @@ struct type struct type *chain; + /* * The alignment for this type. Zero means that the alignment was + not specified in the debug info. Note that this is stored in a + funny way: as the log base 2 (plus 1) of the alignment; so a + value of 1 means the alignment is 1, and a value of 9 means the + alignment is 256. */ + + unsigned align_log2 : TYPE_ALIGN_BITS; + /* * Flags specific to this instance of the type, indicating where on the ring we are. @@ -841,7 +853,7 @@ struct type instance flags are completely inherited from the target type. No qualifiers can be cleared by the typedef. See also check_typedef. */ - int instance_flags; + unsigned instance_flags : 9; /* * Length of storage for a value of this type. The value is the expression in host bytes of what sizeof(type) would return. This @@ -1292,6 +1304,26 @@ extern void allocate_gnat_aux_type (struct type *); so you only have to call check_typedef once. Since allocate_value calls check_typedef, TYPE_LENGTH (VALUE_TYPE (X)) is safe. */ #define TYPE_LENGTH(thistype) (thistype)->length + +/* * Return the alignment of the type in target addressable memory + units, or 0 if no alignment was specified. */ +#define TYPE_RAW_ALIGN(thistype) type_raw_align (thistype) + +/* * Return the alignment of the type in target addressable memory + units, or 0 if no alignment was specified. */ +extern unsigned type_raw_align (struct type *); + +/* * Return the alignment of the type in target addressable memory + units. Return 0 if the alignment cannot be determined; but note + that this makes an effort to compute the alignment even it it was + not specified in the debug info. */ +extern unsigned type_align (struct type *); + +/* * Set the alignment of the type. The alignment must be a power of + 2. Returns false if the given value does not fit in the available + space in struct type. */ +extern bool set_type_align (struct type *, ULONGEST); + /* * Note that TYPE_CODE can be TYPE_CODE_TYPEDEF, so if you want the real type, you need to do TYPE_CODE (check_type (this_type)). */ #define TYPE_CODE(thistype) TYPE_MAIN_TYPE(thistype)->code diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c index bf4ca54..fec74ee 100644 --- a/gdb/i386-tdep.c +++ b/gdb/i386-tdep.c @@ -8347,6 +8347,31 @@ i386_validate_tdesc_p (struct gdbarch_tdep *tdep, } + +/* Implement the type_align gdbarch function. */ + +static ULONGEST +i386_type_align (struct gdbarch *gdbarch, struct type *type) +{ + type = check_typedef (type); + + if (gdbarch_ptr_bit (gdbarch) == 32) + { + if ((TYPE_CODE (type) == TYPE_CODE_INT + || TYPE_CODE (type) == TYPE_CODE_FLT) + && TYPE_LENGTH (type) > 4) + return 4; + + /* Handle x86's funny long double. */ + if (TYPE_CODE (type) == TYPE_CODE_FLT + && gdbarch_long_double_bit (gdbarch) == TYPE_LENGTH (type) * 8) + return 4; + } + + return TYPE_LENGTH (type); +} + + /* Note: This is called for both i386 and amd64. */ static struct gdbarch * @@ -8405,6 +8430,7 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) tdep->record_regmap = i386_record_regmap; set_gdbarch_long_long_align_bit (gdbarch, 32); + set_gdbarch_type_align (gdbarch, i386_type_align); /* The format used for `long double' on almost all i386 targets is the i387 extended floating-point format. In fact, of all targets -- 2.7.4