From: Andrew Burgess Date: Fri, 29 Nov 2019 00:14:35 +0000 (+0000) Subject: gdb: Dynamic string length support X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=216a7e6b9e5d2b279276f3bd8c11145a7d9b59ac;p=binutils-gdb.git gdb: Dynamic string length support Add support for strings with dynamic length using the DWARF attribute DW_AT_string_length. Currently gFortran generates DWARF for some strings that make use of DW_AT_string_length like this: <1><2cc>: Abbrev Number: 20 (DW_TAG_string_type) <2cd> DW_AT_string_length: 5 byte block: 99 bd 1 0 0 (DW_OP_call4: <0x1bd>) <2d3> DW_AT_byte_size : 4 <2d4> DW_AT_sibling : <0x2e2> In this type entry the DW_AT_string_length attribute references a second DW_TAG_formal_parameter that contains the string length. The DW_AT_byte_size indicates that the length is a 4-byte value. This commit extends GDB's DWARF parsing for strings so that we can create dynamic types as well as static types, based on the attribute the DWARF contains. I then extend the dynamic type resolution code in gdbtypes.c to add support for resolving dynamic strings. gdb/ChangeLog: * dwarf2read.c (read_tag_string_type): Read the fields required to make a dynamic string, and possibly create a dynamic range for the string. (attr_to_dynamic_prop): Setup is_reference based on the type of attribute being processed. * gdbtypes.c (is_dynamic_type_internal): Handle TYPE_CODE_STRING. (resolve_dynamic_array): Rename to... (resolve_dynamic_array_or_string): ...this, update header comment, and accept TYPE_CODE_STRING. (resolve_dynamic_type_internal): Handle TYPE_CODE_STRING. gdb/testsuite/ChangeLog: * gdb.fortran/array-slices.exp: Add test for dynamic strings. Change-Id: I03f2d181b26156f48f27a03c8a59f9bd4d71ac17 --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 98a3c6598dc..1465c756abc 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,16 @@ +2019-12-01 Andrew Burgess + + * dwarf2read.c (read_tag_string_type): Read the fields required to + make a dynamic string, and possibly create a dynamic range for the + string. + (attr_to_dynamic_prop): Setup is_reference based on the type of + attribute being processed. + * gdbtypes.c (is_dynamic_type_internal): Handle TYPE_CODE_STRING. + (resolve_dynamic_array): Rename to... + (resolve_dynamic_array_or_string): ...this, update header comment, + and accept TYPE_CODE_STRING. + (resolve_dynamic_type_internal): Handle TYPE_CODE_STRING. + 2019-12-01 Andrew Burgess * dwarf2read.c (dwarf2_per_cu_int_type): New function, takes most diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c index cd114d0bf66..327837cc3c8 100644 --- a/gdb/dwarf2read.c +++ b/gdb/dwarf2read.c @@ -17323,29 +17323,90 @@ read_tag_string_type (struct die_info *die, struct dwarf2_cu *cu) struct gdbarch *gdbarch = get_objfile_arch (objfile); struct type *type, *range_type, *index_type, *char_type; struct attribute *attr; - unsigned int length; + struct dynamic_prop prop; + bool length_is_constant = true; + LONGEST length; + + /* There are a couple of places where bit sizes might be made use of + when parsing a DW_TAG_string_type, however, no producer that we know + of make use of these. Handling bit sizes that are a multiple of the + byte size is easy enough, but what about other bit sizes? Lets deal + with that problem when we have to. Warn about these attributes being + unsupported, then parse the type and ignore them like we always + have. */ + if (dwarf2_attr (die, DW_AT_bit_size, cu) != nullptr + || dwarf2_attr (die, DW_AT_string_length_bit_size, cu) != nullptr) + { + static bool warning_printed = false; + if (!warning_printed) + { + warning (_("DW_AT_bit_size and DW_AT_string_length_bit_size not " + "currently supported on DW_TAG_string_type.")); + warning_printed = true; + } + } attr = dwarf2_attr (die, DW_AT_string_length, cu); - if (attr != nullptr) + if (attr != nullptr && !attr_form_is_constant (attr)) + { + /* The string length describes the location at which the length of + the string can be found. The size of the length field can be + specified with one of the attributes below. */ + struct type *prop_type; + struct attribute *len + = dwarf2_attr (die, DW_AT_string_length_byte_size, cu); + if (len == nullptr) + len = dwarf2_attr (die, DW_AT_byte_size, cu); + if (len != nullptr && attr_form_is_constant (len)) + { + /* Pass 0 as the default as we know this attribute is constant + and the default value will not be returned. */ + LONGEST sz = dwarf2_get_attr_constant_value (len, 0); + prop_type = dwarf2_per_cu_int_type (cu->per_cu, sz, true); + } + else + { + /* If the size is not specified then we assume it is the size of + an address on this target. */ + prop_type = dwarf2_per_cu_addr_sized_int_type (cu->per_cu, true); + } + + /* Convert the attribute into a dynamic property. */ + if (!attr_to_dynamic_prop (attr, die, cu, &prop, prop_type)) + length = 1; + else + length_is_constant = false; + } + else if (attr != nullptr) + { + /* This DW_AT_string_length just contains the length with no + indirection. There's no need to create a dynamic property in this + case. Pass 0 for the default value as we know it will not be + returned in this case. */ + length = dwarf2_get_attr_constant_value (attr, 0); + } + else if ((attr = dwarf2_attr (die, DW_AT_byte_size, cu)) != nullptr) { - length = DW_UNSND (attr); + /* We don't currently support non-constant byte sizes for strings. */ + length = dwarf2_get_attr_constant_value (attr, 1); } else { - /* Check for the DW_AT_byte_size attribute. */ - attr = dwarf2_attr (die, DW_AT_byte_size, cu); - if (attr != nullptr) - { - length = DW_UNSND (attr); - } - else - { - length = 1; - } + /* Use 1 as a fallback length if we have nothing else. */ + length = 1; } index_type = objfile_type (objfile)->builtin_int; - range_type = create_static_range_type (NULL, index_type, 1, length); + if (length_is_constant) + range_type = create_static_range_type (NULL, index_type, 1, length); + else + { + struct dynamic_prop low_bound; + + low_bound.kind = PROP_CONST; + low_bound.data.const_val = 1; + range_type = create_range_type (NULL, index_type, &low_bound, &prop, 0); + } char_type = language_string_char_type (cu->language_defn, gdbarch); type = create_string_type (NULL, char_type, range_type); @@ -17806,7 +17867,15 @@ attr_to_dynamic_prop (const struct attribute *attr, struct die_info *die, baton->locexpr.per_cu = cu->per_cu; baton->locexpr.size = DW_BLOCK (attr)->size; baton->locexpr.data = DW_BLOCK (attr)->data; - baton->locexpr.is_reference = false; + switch (attr->name) + { + case DW_AT_string_length: + baton->locexpr.is_reference = true; + break; + default: + baton->locexpr.is_reference = false; + break; + } prop->data.baton = baton; prop->kind = PROP_LOCEXPR; gdb_assert (prop->data.baton != NULL); diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c index b1e03d1643c..f10f65d0dab 100644 --- a/gdb/gdbtypes.c +++ b/gdb/gdbtypes.c @@ -1967,6 +1967,9 @@ is_dynamic_type_internal (struct type *type, int top_level) || is_dynamic_type_internal (TYPE_TARGET_TYPE (type), 0)); } + case TYPE_CODE_STRING: + /* Strings are very much like an array of characters, and can be + treated as one here. */ case TYPE_CODE_ARRAY: { gdb_assert (TYPE_NFIELDS (type) == 1); @@ -2088,13 +2091,13 @@ resolve_dynamic_range (struct type *dyn_range_type, return static_range_type; } -/* Resolves dynamic bound values of an array type TYPE to static ones. - ADDR_STACK is a stack of struct property_addr_info to be used - if needed during the dynamic resolution. */ +/* Resolves dynamic bound values of an array or string type TYPE to static + ones. ADDR_STACK is a stack of struct property_addr_info to be used if + needed during the dynamic resolution. */ static struct type * -resolve_dynamic_array (struct type *type, - struct property_addr_info *addr_stack) +resolve_dynamic_array_or_string (struct type *type, + struct property_addr_info *addr_stack) { CORE_ADDR value; struct type *elt_type; @@ -2103,7 +2106,10 @@ resolve_dynamic_array (struct type *type, struct dynamic_prop *prop; unsigned int bit_stride = 0; - gdb_assert (TYPE_CODE (type) == TYPE_CODE_ARRAY); + /* For dynamic type resolution strings can be treated like arrays of + characters. */ + gdb_assert (TYPE_CODE (type) == TYPE_CODE_ARRAY + || TYPE_CODE (type) == TYPE_CODE_STRING); type = copy_type (type); @@ -2129,7 +2135,7 @@ resolve_dynamic_array (struct type *type, ary_dim = check_typedef (TYPE_TARGET_TYPE (elt_type)); if (ary_dim != NULL && TYPE_CODE (ary_dim) == TYPE_CODE_ARRAY) - elt_type = resolve_dynamic_array (ary_dim, addr_stack); + elt_type = resolve_dynamic_array_or_string (ary_dim, addr_stack); else elt_type = TYPE_TARGET_TYPE (type); @@ -2332,8 +2338,11 @@ resolve_dynamic_type_internal (struct type *type, break; } + case TYPE_CODE_STRING: + /* Strings are very much like an array of characters, and can be + treated as one here. */ case TYPE_CODE_ARRAY: - resolved_type = resolve_dynamic_array (type, addr_stack); + resolved_type = resolve_dynamic_array_or_string (type, addr_stack); break; case TYPE_CODE_RANGE: diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index f3bffa5dbe6..a4d2556c90a 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2019-12-01 Andrew Burgess + + * gdb.fortran/array-slices.exp: Add test for dynamic strings. + 2019-12-01 Richard Bunt Andrew Burgess diff --git a/gdb/testsuite/gdb.fortran/array-slices.exp b/gdb/testsuite/gdb.fortran/array-slices.exp index db07ace0b38..7a7baf76d28 100644 --- a/gdb/testsuite/gdb.fortran/array-slices.exp +++ b/gdb/testsuite/gdb.fortran/array-slices.exp @@ -46,12 +46,21 @@ set array_contents \ " = \\(\\( -26, -25, -24, -23, -22, -21\\) \\( -19, -18, -17, -16, -15, -14\\) \\( -12, -11, -10, -9, -8, -7\\) \\)" \ " = \\(\\( -26, -24, -22, -20, -18\\) \\( -5, -3, -1, 1, 3\\) \\( 16, 18, 20, 22, 24\\) \\( 37, 39, 41, 43, 45\\) \\)" ] +set message_strings \ + [list \ + " = 'array'" \ + " = 'array \\(1:5,1:5\\)'" \ + " = 'array \\(1:10:2,1:10:2\\)'" \ + " = 'array \\(1:10:3,1:10:2\\)'" \ + " = 'array \\(1:10:5,1:10:3\\)'" ] + set i 0 -foreach result $array_contents { +foreach result $array_contents msg $message_strings { incr i with_test_prefix "test $i" { gdb_continue_to_breakpoint "show" gdb_test "p array" $result + gdb_test "p message" "$msg" } }