[RFC] Supporting alternative ABIs

Corinna Vinschen
2003-08-22T08:41:02Z

Hi,

we're working on GDB support for a platform that supports two
different calling conventions; the user can choose which to use on a
per-function basis. Obviously, calling a function from GDB should
work, no matter which convention the function uses. We'd like folks'
thoughts on how this information should be carried through, from the
compiler, which actually has the information, to GDB's inferior
function call code, which needs it. Since this problem might arise
more often in future, we are looking forward to a generic solution.
Thanks to Jim Blandy, who wrote the first draft of this document after
a long discussion.

The problem:

The platform has a "native" compiler which uses a calling convention
different from the calling convention used by gcc. Both ABIs are
valid concurrently and it should be possible to create functions
with gcc, which use the other ABI and to call these functions from
other functions using the gcc ABI and vice versa.

To allow this in gcc per function, you declare a function with an
appropriate attribute:

int __attribute__ ((use_alternate_abi))
foo (int x, double y)
{
...;
}

This function will expect to be called using the chip's alternative ABI.
Note that this attribute actually applies to the *type* of the
function, as well. Suppose `pf' is a pointer to a function; in order
for the compiler to generate the right code for:

(*pf) (1, 2.0)

it needs to know which calling convention the function '*pf' uses. To
look at it another way, if I declare two functions like this:

double
bar (int x, double y) { return x + 2 * y; }

double __attribute__ ((use_alternate_abi))
baz (int x, double y) { return x + 2 * y; }

Then '&bar' and '&baz' must be incompatible function types; if we
don't know which one 'pf' is pointing to, we can't generate code to
call through it. We write the types of '&bar' and '&baz' as follows:

double ( *) (int, double)
double (__attribute__ ((use_alternate_abi)) *) (int, double)

So, in the general case, GDB needs to be able to successfully call
such functions. If *pf is &baz, then GDB should work like this:

(gdb) print (*pf) (1, 2.0)
$42 = 5.0
(gdb)

Since functions are immediately promoted to function pointers when
they appear as the operand of almost anything in a C expression,
supporting the case above will automatically support the more
straightforward uses, like:

(gdb) print baz (1, 2.0)
$43 = 5.0
(gdb)

The proposal:

There needs to be a way for the compiler to indicate to the debugger
which functions use the alternative calling convention. This platform
uses Dwarf 2 as its normal debugging format; it's straightforward to
extend Dwarf 2 to carry this information. The Dwarf 2 attribute
DW_AT_calling_convention would seem to be appropriate here. The spec
only describes its use for actual DW_TAG_subprogram nodes (for
functions themselves) not DW_TAG_subroutine_type nodes (for subroutine
types) but it seems like a reasonable extension. We can specify new
DW_CC_ values, in the range reserved for user extensions, in the
chip's ABI.

GDB needs to read this information, store it in its symbol tables, and
use it in call_function_by_hand. It's not clear to us how best to do
this, but here is our straw-man proposal.

- Add the following member to 'struct main_type':

/* For TYPE_CODE_FUNC, target-specific information about how to
call this function. This is zero if the function uses the
platform's standard calling convention. */
int calling_convention;

- The dwarf2read.c function responsible for reading function types,
read_subroutine_type, would check for a DW_AT_calling_convention
attribute on the DW_TAG_subroutine_type die; if present, it would
pass the attribute's value, an integer, to a new gdbarch method,
gdbarch_parse_dwarf2_calling_convention.

Actually, gdbarch_parse_dwarf2_calling_convention would have three
arguments: attribute_present, calling_convention, and gcc_compiled.
`attribute_present' is a flag, indicating if DW_AT_calling_convention
is present in this node, `calling_convention' contains the actual
value, if any, and `gcc_compiled' is a flag indicating if the function
has been build using gcc or not.

We'd call gdbarch_parse_dwarf2_calling_convention for every subroutine
type node. The method would return another int, to be stored in the
subroutine type's `calling_convention'.

- EXTRACT_RETURN_VALUE, STORE_RETURN_VALUE, USE_STRUCT_CONVENTION and
gdbarch_push_dummy_code gdbarch methods would be changed to expect
the type of the function, not the type of its return value. They
can use TYPE_TARGET_TYPE to get the return value type, but this will
also give them access to the `calling_convention' for the function type.

- gdbarch_push_dummy_call and EXTRACT_STRUCT_VALUE_ADDRESS would
receive the function's type as an additional argument, to give them
access to the function's calling convention information.

- USE_STRUCT_CONVENTION and gdbarch_push_dummy_code would not require
the `gcc_p' and `using_gcc' flags anymore since this information
is now given in the function type node. Due to the way,
gdbarch_parse_dwarf2_calling_convention evaluates the `calling_convention'
value, all inferior call-related gdbarch methods could simply trust its
value.

The advantage of this approach is that it leaves the details about how
to interpret the value of the DW_CC_calling_convention attribute, and
how to represent the calling conventions supported for a particular
architecture, in the hands of target-specific code. GDB's core code
simply carries that target-specific information from the debug info
reader to the inferior call code. And having the gdbarch methods that
support inferior calls take the function type, instead of the return
value type, seems natural.

The disadvantage of this is that changing all the gdbarch methods
mentioned above would be a lot of work --- although not that difficult.

Corinna

--
Corinna Vinschen
Cygwin Developer
Red Hat, Inc.