Bug 25054 - GDB passes incorrect values for call-by-value parameters when infcall'ing C++ functions
Summary: GDB passes incorrect values for call-by-value parameters when infcall'ing C++...
Status: RESOLVED FIXED
Alias: None
Product: gdb
Classification: Unclassified
Component: c++ (show other bugs)
Version: HEAD
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-10-02 08:42 UTC by Baris Aktemur
Modified: 2020-10-14 06:23 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Baris Aktemur 2019-10-02 08:42:45 UTC
Consider the sample C++ program below where we have two classes, Custom and Defaulted, and two functions that have call-by-value arguments of types Custom and Defaulted, respectively.

~~~
#include <iostream>

class Custom {
public:
  Custom () { x = 10; }
  Custom (const Custom &arg) { x = 20; }
  int x;
};

class Defaulted {
public:
  Defaulted () { x = 30; }
  Defaulted (const Defaulted &arg) = default;
  int x;
};

void cbv_custom (Custom c) {
  c.x += 1;
  std::cout << c.x << std::endl;
}

void cbv_defaulted (Defaulted d) {
  d.x += 1;
  std::cout << d.x << std::endl;
}

int main () {
  Custom custom;
  Defaulted def;

  cbv_custom (custom);
  cbv_defaulted (def);

  return 0;
}
~~~

"Custom" has a user-defined copy-constructor.  For this reason it is a non-trivial type and should be passed implicitly by reference.  "Defaulted" is a trivial type and should be passed by value.

~~~
$ gdb -q ./sample
Reading symbols from ./sample...
(gdb) b 38
Breakpoint 1 at 0x1276: file sample.cpp, line 38.
(gdb) run
Starting program: /path/to/sample
21
31

Breakpoint 1, main () at sample.cpp:38
38        return 0;
(gdb) p custom
$1 = {x = 10}
(gdb) p def
$2 = {x = 30}
~~~

Let us now call cbv_custom. The expected output is 21.

~~~
(gdb) p cbv_custom(custom)
11
$3 = void
(gdb) p custom
$4 = {x = 11}
~~~

Here, GDB should have created a clone of the 'custom' object via the copy ctor and passed that clone to the function. However, GDB passed the 'custom' object itself, even without creating a naive copy.

Let's now call the cbv_defaulted function. The expected output is 31.

~~~
(gdb) p cbv_defaulted(def)
-11295
$5 = void
~~~

The return value seems like garbage at first sight, but it's not.

~~~
(gdb) p/x -11295
$6 = 0xffffd3e1
(gdb) p &def
$7 = (Defaulted *) 0x7fffffffd3e0
~~~

Here, GDB incorrectly inferred that Defaulted is non-trivial. Hence, it decided to pass the reference of the 'def' object as the argument, but it should've passed the argument by value.

A set of patches that addresses this problem and its vicinity is available at
https://sourceware.org/ml/gdb-patches/2019-06/msg00517.html
Comment 1 Sourceware Commits 2019-12-20 16:47:21 UTC
The master branch has been updated by Tankut Baris Aktemur <aktemur@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=099a6354dab7e74c990501929f383394fc8efd02

commit 099a6354dab7e74c990501929f383394fc8efd02
Author: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Date:   Fri Dec 20 17:43:06 2019 +0100

    infcall: handle pass-by-reference arguments appropriately
    
    If an aggregate argument is implicitly pass-by-reference, allocate a
    temporary object on the stack, initialize it via the copy constructor
    (if exists) or trivially by memcpy'ing.  Pass the reference of the
    temporary to the callee function.  After the callee returns, invoke
    the destructor of the temporary.
    
    gdb/ChangeLog:
    2019-12-20  Tankut Baris Aktemur  <tankut.baris.aktemur@intel.com>
    
    	PR gdb/25054
    	* infcall.c (call_function_by_hand_dummy): Update the argument-
    	passing section for call-by-value parameters.
    	(struct destructor_info): New struct.
    	(call_destructors): New auxiliary function.
    
    Change-Id: I18fa5d0df814dfa0defe9e862a88a6dbf1d99d01
Comment 2 Baris Aktemur 2020-10-14 06:23:59 UTC
Fixed with commit 099a6354dab7e74c990501929f383394fc8efd02 (and a few more direct/indirect predecessor commits).