[PATCHv2 2/2] gdb/fortran: Access elements of a structure with dynamic type

Andrew Burgess andrew.burgess@embecosm.com
Mon Jul 13 13:33:22 GMT 2020


After the previous commit I noticed this behaviour:

  (gdb) ptype this
  type = Type __class_test_module_Test_type_t
      PTR TO -> ( Type test_type :: _data )
      PTR TO -> ( Type __vtype_test_module_Test_type :: _vptr )
  End Type __class_test_module_Test_type_t
  (gdb) ptype this%_data
  type = PTR TO -> ( Type test_type
      integer(kind=4) :: a
      real(kind=4), allocatable :: b(:,:)
  End Type test_type )
  (gdb) ptype this%_data%b
  Cannot access memory at address 0x50
  (gdb)

When we ask GDB for the type of field `b`, which has dynamic type,
then GDB is unable to correctly resolve the dynamic type, and ends up
trying to access target memory at address 0x50.

When GDB sees 'this%_data%b' the expression tree looks like this:

  Dump of expression @ 0x500d380, after conversion to prefix form:
  Expression: `test_module::test_proc::this._data.b'
  	Language fortran, 14 elements, 16 bytes each.

  	    0  STRUCTOP_STRUCT       Element name: `b'
  	    5    STRUCTOP_STRUCT       Element name: `_data'
  	   10      OP_VAR_VALUE          Block @0x498cca0, symbol @0x498cc20 (this)

GDB will first get a value for `this`, from which it extracts a value
for the element `_data`, and finally GDB dereferences the pointer
`_data` and extracts the element `b`.

The problem is that when looking for the type of an expression GDB
evaluates the expression in EVAL_AVOID_SIDE_EFFECTS mode, as a result
the pointer value `_data` is returned with contents set to the
constant value 0.

Normally this is fine as we only plan to look at the type being
pointed too, but for dynamic types we need the pointer value so we can
correctly fetch the dynamic properties of the value from target
memory.

The solution I present here is to spot the case where:
  (a) we're in EVAL_AVOID_SIDE_EFFECTS mode, and
  (b) the structure element has dynamic type
In this case we fetch the parent object in EVAL_NORMAL mode.  This
means that it will have its actual contents, fetched from the actual
target, rather than the dummy 0 value.  With this done we are able to
correctly evaluate the dynamic type and the above test case now
finishes like this:

  (gdb) ptype this%_data%b
  type = real(kind=4), allocatable (3,2)

You might notice that STRUCTOP_PTR is very similar to STRUCTOP_STRUCT,
but that I have not updated the former.  The reason for this is that
Fortran doesn't make use of STRUCTOP_PTR, so I'm not sure how I would
test any changes to STRUCTOP_PTR.

gdb/ChangeLog:

	* eval.c (evaluate_subexp_standard): Call evaluate_subexp with
	EVAL_NORMAL if we are accessing an element with a dynamic type.

gdb/testsuite/ChangeLog:

	* gdb.fortran/class-allocatable-array.exp: Add more tests.
	* gdb.fortran/pointer-to-pointer.exp: Add more tests.
---
 gdb/ChangeLog                                 |  5 +++++
 gdb/eval.c                                    | 20 ++++++++++++++++++-
 gdb/testsuite/ChangeLog                       |  5 +++++
 .../gdb.fortran/class-allocatable-array.exp   | 16 +++++++++++++++
 .../gdb.fortran/pointer-to-pointer.exp        |  1 +
 5 files changed, 46 insertions(+), 1 deletion(-)

diff --git a/gdb/eval.c b/gdb/eval.c
index f9750816216..9f716ce0624 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -2031,12 +2031,30 @@ evaluate_subexp_standard (struct type *expect_type,
     case STRUCTOP_STRUCT:
       tem = longest_to_int (exp->elts[pc + 1].longconst);
       (*pos) += 3 + BYTES_TO_EXP_ELEM (tem + 1);
+      oldpos = *pos;
       arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
       if (noside == EVAL_SKIP)
 	return eval_skip_value (exp);
+      if (noside == EVAL_AVOID_SIDE_EFFECTS)
+	{
+	  /* If the element of the structure has a dynamic type then we
+	     need to get the real value representing the containing
+	     structure so that we can correctly evaluate the type of the
+	     element.  If we're not already avoiding side effects then we
+	     already have the real value of the containing structure, so
+	     this is not needed.  */
+	  type = lookup_struct_elt_type (value_type (arg1),
+					 &exp->elts[pc + 2].string, 1);
+	  if (type != nullptr && is_dynamic_type (type))
+	    {
+	      *pos = oldpos;
+	      arg1 = evaluate_subexp (NULL_TYPE, exp, pos, EVAL_NORMAL);
+	    }
+	}
       arg3 = value_struct_elt (&arg1, NULL, &exp->elts[pc + 2].string,
 			       NULL, "structure");
-      if (noside == EVAL_AVOID_SIDE_EFFECTS)
+      if (noside == EVAL_AVOID_SIDE_EFFECTS
+	  && !is_dynamic_type (check_typedef (value_type (arg3))))
 	arg3 = value_zero (value_type (arg3), VALUE_LVAL (arg3));
       return arg3;
 
diff --git a/gdb/testsuite/gdb.fortran/class-allocatable-array.exp b/gdb/testsuite/gdb.fortran/class-allocatable-array.exp
index 9475ba3b393..355fb0ce9cd 100644
--- a/gdb/testsuite/gdb.fortran/class-allocatable-array.exp
+++ b/gdb/testsuite/gdb.fortran/class-allocatable-array.exp
@@ -41,3 +41,19 @@ gdb_continue_to_breakpoint "Break Here"
 gdb_test "print this" " = \\( _data = \[^\r\n\]+, _vptr = \[^\r\n\]+\\)"
 gdb_test "print this%_data" " = \\(PTR TO -> \\( Type test_type \\)\\) \[^\r\n\]+"
 gdb_test "print this%_data%b" " = \\(\\( 1, 2, 3\\) \\( 4, 5, 6\\) \\)"
+
+set integer4 [fortran_int4]
+set real4 [fortran_real4]
+
+# Check we can correctly access the types of these same objects.
+gdb_test "ptype this" [multi_line \
+			   "type = Type \[^\r\n\]+" \
+			   "    PTR TO -> \\( Type test_type :: _data \\)" \
+			   "    PTR TO -> \\( Type \[^\r\n\]+ :: _vptr \\)" \
+			   "End Type \[^\r\n\]+" ]
+gdb_test "ptype this%_data" [multi_line \
+				 "type = PTR TO -> \\( Type test_type" \
+				 "    ${integer4} :: a" \
+				 "    ${real4}, allocatable :: b\\(:,:\\)" \
+				 "End Type test_type \\)" ]
+gdb_test "ptype this%_data%b" "type = ${real4}, allocatable \\(3,2\\)"
diff --git a/gdb/testsuite/gdb.fortran/pointer-to-pointer.exp b/gdb/testsuite/gdb.fortran/pointer-to-pointer.exp
index 7129e431ed1..51fb36c683f 100644
--- a/gdb/testsuite/gdb.fortran/pointer-to-pointer.exp
+++ b/gdb/testsuite/gdb.fortran/pointer-to-pointer.exp
@@ -44,3 +44,4 @@ set l_buffer_type [multi_line \
 gdb_test "ptype buffer" "type = PTR TO -> \\( ${l_buffer_type} \\)"
 gdb_test "ptype *buffer" "type = ${l_buffer_type}"
 gdb_test "ptype buffer%alpha" "type = real\\(kind=4\\) \\(5\\)"
+gdb_test "ptype (*buffer)%alpha" "type = real\\(kind=4\\) \\(5\\)"
-- 
2.25.4



More information about the Gdb-patches mailing list