From: Pedro Alves <palves@redhat.com>
To: Leszek Swirski <leszeks@google.com>, gdb-patches@sourceware.org
Subject: Re: [PATCH] [amd64] Fix AMD64 return value ABI in expression evaluation
Date: Thu, 14 Feb 2019 14:28:00 -0000 [thread overview]
Message-ID: <45c95dd6-7ea2-5428-c520-974452c6db89@redhat.com> (raw)
In-Reply-To: <20190214133507.38697-1-leszeks@google.com>
On 02/14/2019 01:35 PM, Leszek Swirski via gdb-patches wrote:
> The AMD64 System V ABI specifies that when a function has a return type
> classified as MEMORY, the caller provides space for the value and passes
> the address to this space as the first argument to the function (before
> even the "this" pointer). The classification of MEMORY is applied to
> struct that are sufficiently large, or ones with unaligned fields.
>
> The expression evaluator uses call_function_by_hand to call functions,
> and the hand-built frame has to push arguments in a way that matches the
> ABI of the called function. call_function_by_hand supports ABI-based
> struct returns, based on the value of gdbarch_return_value, however on
> AMD64 the implementation of the classifier incorrectly assumed that all
> non-POD types (implemented as "all types with a base class") should be
> classified as MEMORY and use the struct return.
>
> This ABI mismatch resulted in issues when calling a function that returns
> a class of size <16 bytes which has a base class, including issues such
> as the "this" pointer being incorrect (as it was passed as the second
> argument rather than the first).
>
> This is now fixed by checking for field alignment rather than POD-ness,
> and a testsuite is added to test expression evaluation for AMD64.
Thanks!
I wonder whether this fixes PR gdb/24104,
<https://sourceware.org/bugzilla/show_bug.cgi?id=24104>.
Generally looks good. Some minor comments below.
>
> gdb/ChangeLog
>
> * amd64-tdep.c (amd64_classify_aggregate): Use cp_pass_by_reference
> rather than a hand-rolled POD check when checking for forced MEMORY
> classification.
>
> gdb/testsuite/ChangeLog
>
> * gdb.arch/amd64-eval.cc: New file.
> * gdb.arch/amd64-eval.exp: New file.
Watch out for tabs vs spaces in the ChangeLog entry above.
> ---
> gdb/ChangeLog | 6 ++
> gdb/amd64-tdep.c | 46 +++++++++---
> gdb/testsuite/ChangeLog | 5 ++
> gdb/testsuite/gdb.arch/amd64-eval.cc | 103 ++++++++++++++++++++++++++
> gdb/testsuite/gdb.arch/amd64-eval.exp | 43 +++++++++++
> 5 files changed, 193 insertions(+), 10 deletions(-)
> create mode 100644 gdb/testsuite/gdb.arch/amd64-eval.cc
> create mode 100644 gdb/testsuite/gdb.arch/amd64-eval.exp
>
> diff --git a/gdb/ChangeLog b/gdb/ChangeLog
> index 191863e491..ffd9f4d699 100644
> --- a/gdb/ChangeLog
> +++ b/gdb/ChangeLog
> @@ -1,3 +1,9 @@
> +2019-02-14 Leszek Swirski <leszeks@google.com>
> +
> + * amd64-tdep.c (amd64_classify_aggregate): Use cp_pass_by_reference
> + rather than a hand-rolled POD check when checking for forced MEMORY
> + classification.
> +
> 2019-02-12 John Baldwin <jhb@FreeBSD.org>
>
> * symfile.c (find_separate_debug_file): Use canonical path of
> diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
> index 3f61997d66..d2c518ed6d 100644
> --- a/gdb/amd64-tdep.c
> +++ b/gdb/amd64-tdep.c
> @@ -541,15 +541,42 @@ amd64_merge_classes (enum amd64_reg_class class1, enum amd64_reg_class class2)
>
> static void amd64_classify (struct type *type, enum amd64_reg_class theclass[2]);
>
> -/* Return non-zero if TYPE is a non-POD structure or union type. */
> +/* Return non-zero if TYPE is a structure or union with unaligned fields. */
>
> static int
> -amd64_non_pod_p (struct type *type)
Since you're basically rewriting this, please make it
return bool and use true/false. Update the "non-zero" in
the comment above as well.
> -{
> - /* ??? A class with a base class certainly isn't POD, but does this
> - catch all non-POD structure types? */
> - if (TYPE_CODE (type) == TYPE_CODE_STRUCT && TYPE_N_BASECLASSES (type) > 0)
> - return 1;
> +amd64_has_unaligned_fields (struct type *type) {
Formatting, { does on the next line.
> +
> + if (TYPE_CODE (type) == TYPE_CODE_STRUCT
> + || TYPE_CODE (type) == TYPE_CODE_UNION)
> + {
> + int i;
> +
> + for (i = 0; i < TYPE_NFIELDS (type); i++)
Write:
for (int i = 0; i < TYPE_NFIELDS (type); i++)
> + {
> + struct type *subtype = check_typedef (TYPE_FIELD_TYPE (type, i));
> + int bitpos = TYPE_FIELD_BITPOS (type, i);
> + int align = type_align(subtype);
> + int bytepos;
Can declare bytepod below where it's initialized.
> +
> + /* Ignore static fields, or empty fields, for example nested
> + empty structures.*/
Missing double space after the period.
> + if (field_is_static (&TYPE_FIELD (type, i))
> + || (TYPE_FIELD_BITSIZE (type, i) == 0
> + && TYPE_LENGTH (subtype) == 0))
> + continue;
> +
> + if (bitpos % 8 != 0)
> + return 1;
> + bytepos = bitpos / 8;
> +
> + if (bytepos % align != 0)
> + return 1;
> +
> + if (amd64_has_unaligned_fields(subtype))
> + return 1;
> + }
> +
> + }
>
> return 0;
> }
> @@ -560,10 +587,9 @@ amd64_non_pod_p (struct type *type)
> static void
> amd64_classify_aggregate (struct type *type, enum amd64_reg_class theclass[2])
> {
> - /* 1. If the size of an object is larger than two eightbytes, or in
> - C++, is a non-POD structure or union type, or contains
> + /* 1. If the size of an object is larger than two eightbytes, or it has
> unaligned fields, it has class memory. */
> - if (TYPE_LENGTH (type) > 16 || amd64_non_pod_p (type))
> + if (TYPE_LENGTH (type) > 16 || amd64_has_unaligned_fields (type))
> {
> theclass[0] = theclass[1] = AMD64_MEMORY;
> return;
> diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
> index 1ee4f1bd9b..5fa2b6ef7b 100644
> --- a/gdb/testsuite/ChangeLog
> +++ b/gdb/testsuite/ChangeLog
> @@ -1,3 +1,8 @@
> +2019-02-14 Leszek Swirski <leszeks@google.com>
> +
> + * gdb.arch/amd64-eval.cc: New file.
> + * gdb.arch/amd64-eval.exp: New file.
> +
> 2019-02-12 Weimin Pan <weimin.pan@oracle.com>
>
> PR breakpoints/21870
> diff --git a/gdb/testsuite/gdb.arch/amd64-eval.cc b/gdb/testsuite/gdb.arch/amd64-eval.cc
> new file mode 100644
> index 0000000000..81f9ba589c
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/amd64-eval.cc
> @@ -0,0 +1,103 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> + Copyright 2019 Free Software Foundation, Inc.
> +
> + This program is free software; you can redistribute it and/or modify
> + it under the terms of the GNU General Public License as published by
> + the Free Software Foundation; either version 3 of the License, or
> + (at your option) any later version.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
> +
> +#include <cstdio>
> +#include <cstdlib>
> +#include <cassert>
> +
> +// A simple structure with a single integer field. Should be returned in
> +// a register.
We follow GDB's conventions in testcases as well. Use /**/ comments throughout,
and double space after periods.
> +struct SimpleBase {
{ on the next line.
> + int32_t x;
Missing cstdint include for int32_t.
> +};
> +
> +// A simple structure derived from the simple base. Should be returned in
> +// a register.
> +struct SimpleDerived : public SimpleBase {};
> +
> +// A structure derived from the simple base with a non-trivial destructor.
> +// Should be returned on the stack.
> +struct NonTrivialDestructorDerived : public SimpleBase {
{ on the next line.
> + ~NonTrivialDestructorDerived() { x = 1; }
> +};
> +
> +// A structure with unaligned fields. Should be returned on the stack.
> +struct UnalignedFields {
> + int32_t x;
{ on the next line.
> + double y;
> +} __attribute__((packed));
> +
> +// A structure with unaligned fields in its base class. Should be
> +// returned on the stack.
> +struct UnalignedFieldsInBase : public UnalignedFields {
{ on the next line.
> + int32_t x2;
> +};
> +
> +class Foo {
{ on the next line.
> +public:
> + SimpleBase
> + return_simple_base ()
> + {
> + assert(this->tag == EXPECTED_TAG);
> + return SimpleBase();
Missing space before parens, both in assert call and
in ctor call. Happens multiple times.
> + }
> +
> + SimpleDerived
> + return_simple_derived ()
> + {
> + assert(this->tag == EXPECTED_TAG);
> + return SimpleDerived();
> + }
> +
> + NonTrivialDestructorDerived
> + return_non_trivial_destructor ()
> + {
> + assert(this->tag == EXPECTED_TAG);
> + return NonTrivialDestructorDerived();
> + }
> +
> + UnalignedFields
> + return_unaligned ()
> + {
> + assert(this->tag == EXPECTED_TAG);
> + return UnalignedFields();
> + }
> +
> + UnalignedFieldsInBase
> + return_unaligned_in_base ()
> + {
> + assert(this->tag == EXPECTED_TAG);
> + return UnalignedFieldsInBase();
> + }
> +
> +private:
> + // Use a tag to detect if the "this" value is correct.
> + static const int EXPECTED_TAG = 0xF00F00F0;
> + int tag = EXPECTED_TAG;
> +};
> +
> +int
> +main (int argc, char *argv[])
> +{
> + Foo foo;
> + foo.return_simple_base();
Space before parens.
> + foo.return_simple_derived();
> + foo.return_non_trivial_destructor();
> + foo.return_unaligned();
> + foo.return_unaligned_in_base();
> + return 0; // break-here
> +}
> diff --git a/gdb/testsuite/gdb.arch/amd64-eval.exp b/gdb/testsuite/gdb.arch/amd64-eval.exp
> new file mode 100644
> index 0000000000..f3da83e4ca
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/amd64-eval.exp
> @@ -0,0 +1,43 @@
> +# This testcase is part of GDB, the GNU debugger.
> +
> +# Copyright 2019 Free Software Foundation, Inc.
> +
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program. If not, see <http://www.gnu.org/licenses/>.
> +
> +# This testcase exercises evaluation with amd64 calling conventions.
> +
> +standard_testfile .cc
> +
> +if { [prepare_for_testing "failed to prepare" $testfile $srcfile \
> + { debug c++ }] } {
> + return -1
> +}
> +
> +if ![runto_main] {
> + return -1
> +}
> +
> +gdb_breakpoint [gdb_get_line_number "break-here"]
> +gdb_continue_to_breakpoint "break-here"
> +
> +gdb_test "call foo.return_simple_base()" \
> + ".* = {x = 0}"
Leading ".*" is unnecessary -- it's implied.
> +gdb_test "call foo.return_simple_derived()" \
> + ".* = {<SimpleBase> = {x = 0}, <No data fields>}"
> +gdb_test "call foo.return_non_trivial_destructor()" \
> + ".* = {<SimpleBase> = {x = 0}, <No data fields>}"
> +gdb_test "call foo.return_unaligned()" \
> + ".* = {x = 0, y = 0}"
> +gdb_test "call foo.return_unaligned_in_base()" \
> + ".* = {<UnalignedFields> = {x = 0, y = 0}, x2 = 0}"
>
Is it possible to use values other than "0" in x/y/x2 in
the success cases? That tends to be more robust to changes,
because it's easier to hit zeroed memory with a bug and
not notice it than some other value.
Thanks,
Pedro Alves
next prev parent reply other threads:[~2019-02-14 14:28 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-02-14 13:35 Leszek Swirski via gdb-patches
2019-02-14 14:28 ` Pedro Alves [this message]
2019-02-14 15:07 ` Leszek Swirski via gdb-patches
2019-02-14 15:16 ` [PATCH v2] " Leszek Swirski via gdb-patches
2019-02-14 15:18 ` [PATCH v3] " Leszek Swirski via gdb-patches
2019-02-14 15:26 ` Pedro Alves
2019-04-13 16:24 ` Leszek Swirski via gdb-patches
2019-04-16 16:39 ` Tom Tromey
2019-04-16 18:28 ` Tom Tromey
2019-04-24 18:03 ` Tom Tromey
2019-04-17 22:30 ` Tom de Vries
2019-04-18 17:54 ` Tom Tromey
2019-04-24 16:26 ` Regressions on gdb.base/call-{ar,rt}-st.exp on x86_64 native (was: Re: [PATCH v3] [amd64] Fix AMD64 return value ABI in expression evaluation) Sergio Durigan Junior
2019-04-24 17:51 ` Regressions on gdb.base/call-{ar,rt}-st.exp on x86_64 native Tom Tromey
2019-04-24 18:02 ` Sergio Durigan Junior
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=45c95dd6-7ea2-5428-c520-974452c6db89@redhat.com \
--to=palves@redhat.com \
--cc=gdb-patches@sourceware.org \
--cc=leszeks@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox