]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Fix dynamic_cast
authorHannes Domani <ssbssa@yahoo.de>
Tue, 29 Mar 2022 18:05:06 +0000 (20:05 +0200)
committerHannes Domani <ssbssa@yahoo.de>
Mon, 11 Dec 2023 14:50:00 +0000 (15:50 +0100)
PR29011 notes that dynamic_cast does not work correctly if
classes with virtual methods are involved, some of the results
wrongly point into the vtable of the derived class:
```
(gdb) p vlr
$1 = (VirtualLeftRight *) 0x162240
(gdb) p vl
$2 = (VirtualLeft *) 0x162240
(gdb) p vr
$3 = (VirtualRight *) 0x162250
(gdb) p dynamic_cast<VirtualLeftRight*>(vlr)
$4 = (VirtualLeftRight *) 0x13fab89b0 <vtable for VirtualLeftRight+16>
(gdb) p dynamic_cast<VirtualLeftRight*>(vl)
$5 = (VirtualLeftRight *) 0x13fab89b0 <vtable for VirtualLeftRight+16>
(gdb) p dynamic_cast<VirtualLeftRight*>(vr)
$6 = (VirtualLeftRight *) 0x13fab89b0 <vtable for VirtualLeftRight+16>
(gdb) p dynamic_cast<VirtualLeft*>(vlr)
$7 = (VirtualLeft *) 0x162240
(gdb) p dynamic_cast<VirtualLeft*>(vl)
$8 = (VirtualLeft *) 0x13fab89b0 <vtable for VirtualLeftRight+16>
(gdb) p dynamic_cast<VirtualLeft*>(vr)
$9 = (VirtualLeft *) 0x162240
(gdb) p dynamic_cast<VirtualRight*>(vlr)
$10 = (VirtualRight *) 0x162250
(gdb) p dynamic_cast<VirtualRight*>(vl)
$11 = (VirtualRight *) 0x162250
(gdb) p dynamic_cast<VirtualRight*>(vr)
$12 = (VirtualRight *) 0x13fab89b0 <vtable for VirtualLeftRight+16>
```

For the cases where the dynamic_cast type is the same as the
original type, it used the ARG value for the result, which in
case of pointer types was already the dereferenced value.

And the TEM value at the value address was created with the
pointer/reference type, not the actual class type.

With these fixed, the dynamic_cast results make more sense:
```
(gdb) p vlr
$1 = (VirtualLeftRight *) 0x692240
(gdb) p vl
$2 = (VirtualLeft *) 0x692240
(gdb) p vr
$3 = (VirtualRight *) 0x692250
(gdb) p dynamic_cast<VirtualLeftRight*>(vlr)
$4 = (VirtualLeftRight *) 0x692240
(gdb) p dynamic_cast<VirtualLeftRight*>(vl)
$5 = (VirtualLeftRight *) 0x692240
(gdb) p dynamic_cast<VirtualLeftRight*>(vr)
$6 = (VirtualLeftRight *) 0x692240
(gdb) p dynamic_cast<VirtualLeft*>(vlr)
$7 = (VirtualLeft *) 0x692240
(gdb) p dynamic_cast<VirtualLeft*>(vl)
$8 = (VirtualLeft *) 0x692240
(gdb) p dynamic_cast<VirtualLeft*>(vr)
$9 = (VirtualLeft *) 0x692240
(gdb) p dynamic_cast<VirtualRight*>(vlr)
$10 = (VirtualRight *) 0x692250
(gdb) p dynamic_cast<VirtualRight*>(vl)
$11 = (VirtualRight *) 0x692250
(gdb) p dynamic_cast<VirtualRight*>(vr)
$12 = (VirtualRight *) 0x692250
```

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=29011
Approved-By: Tom Tromey <tom@tromey.com>
gdb/testsuite/gdb.cp/casts.cc
gdb/testsuite/gdb.cp/casts.exp
gdb/valops.c

index f64d13c26dff4c5958684f4145be7ff5be0c8404..5c7f9dc8a1ce1ba2ffb4e397ea68e51dad6c63ca 100644 (file)
@@ -50,6 +50,24 @@ struct LeftRight : public Left, public Right
 {
 };
 
+struct VirtualLeft
+{
+  virtual ~VirtualLeft () {}
+
+  int left;
+};
+
+struct VirtualRight
+{
+  virtual ~VirtualRight () {}
+
+  int right;
+};
+
+struct VirtualLeftRight : public VirtualLeft, public VirtualRight
+{
+};
+
 int
 main (int argc, char **argv)
 {
@@ -70,5 +88,9 @@ main (int argc, char **argv)
   unsigned long long gd_value = (unsigned long long) (std::uintptr_t)&gd;
   unsigned long long r_value = (unsigned long long) (Right *) &gd;
 
+  VirtualLeftRight *vlr = new VirtualLeftRight ();
+  VirtualLeft *vl = vlr;
+  VirtualRight *vr = vlr;
+
   return 0;  /* breakpoint spot: casts.exp: 1 */
 }
index 41bb125927076d1a8538c210dd791a88e45f4ee2..a0c73826fa837b18c0a7d8e71ea089ec2633d271 100644 (file)
@@ -180,6 +180,26 @@ gdb_test "print (unsigned long long) (LeftRight *) (Right *) &gd == gd_value" \
 gdb_test "print (unsigned long long) (LeftRight *) (Right *) r_value == gd_value" \
     " = true"
 
+gdb_test "print dynamic_cast<VirtualLeftRight *> (vlr) == vlr" " = true"
+gdb_test "print dynamic_cast<VirtualLeftRight *> (vl) == vlr" " = true"
+gdb_test "print dynamic_cast<VirtualLeftRight *> (vr) == vlr" " = true"
+gdb_test "print dynamic_cast<VirtualLeft *> (vlr) == vl" " = true"
+gdb_test "print dynamic_cast<VirtualLeft *> (vl) == vl" " = true"
+gdb_test "print dynamic_cast<VirtualLeft *> (vr) == vl" " = true"
+gdb_test "print dynamic_cast<VirtualRight *> (vlr) == vr" " = true"
+gdb_test "print dynamic_cast<VirtualRight *> (vl) == vr" " = true"
+gdb_test "print dynamic_cast<VirtualRight *> (vr) == vr" " = true"
+
+gdb_test "print &dynamic_cast<VirtualLeftRight &> (*vlr) == vlr" " = true"
+gdb_test "print &dynamic_cast<VirtualLeftRight &> (*vl) == vlr" " = true"
+gdb_test "print &dynamic_cast<VirtualLeftRight &> (*vr) == vlr" " = true"
+gdb_test "print &dynamic_cast<VirtualLeft &> (*vlr) == vl" " = true"
+gdb_test "print &dynamic_cast<VirtualLeft &> (*vl) == vl" " = true"
+gdb_test "print &dynamic_cast<VirtualLeft &> (*vr) == vl" " = true"
+gdb_test "print &dynamic_cast<VirtualRight &> (*vlr) == vr" " = true"
+gdb_test "print &dynamic_cast<VirtualRight &> (*vl) == vr" " = true"
+gdb_test "print &dynamic_cast<VirtualRight &> (*vr) == vr" " = true"
+
 if {[prepare_for_testing "failed to prepare" ${testfile}03 $srcfile2 \
                         {debug c++ additional_flags=-std=c++03}]} {
     return -1
index b0e95c330a0c7b2069933f36efb14a2793c561e4..49ea1fd7676346c6f89c13d8de7dfbe7cd26e32b 100644 (file)
@@ -856,7 +856,7 @@ value_dynamic_cast (struct type *type, struct value *arg)
 
   /* If the classes are the same, just return the argument.  */
   if (class_types_same_p (class_type, arg_type))
-    return value_cast (type, arg);
+    return value_cast (type, original_arg);
 
   /* If the target type is a unique base class of the argument's
      declared type, just cast it.  */
@@ -888,14 +888,18 @@ value_dynamic_cast (struct type *type, struct value *arg)
       && resolved_type->target_type ()->code () == TYPE_CODE_VOID)
     return value_at_lazy (type, addr);
 
-  tem = value_at (type, addr);
-  type = tem->type ();
+  tem = value_at (resolved_type->target_type (), addr);
+  type = (is_ref
+         ? lookup_reference_type (tem->type (), resolved_type->code ())
+         : lookup_pointer_type (tem->type ()));
 
   /* The first dynamic check specified in 5.2.7.  */
   if (is_public_ancestor (arg_type, resolved_type->target_type ()))
     {
       if (class_types_same_p (rtti_type, resolved_type->target_type ()))
-       return tem;
+       return (is_ref
+               ? value_ref (tem, resolved_type->code ())
+               : value_addr (tem));
       result = NULL;
       if (dynamic_cast_check_1 (resolved_type->target_type (),
                                tem->contents_for_printing ().data (),