mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-21 04:42:53 +08:00
08106042d9
I built GDB for all targets on a x86-64/GNU-Linux system, and then (accidentally) passed GDB a RISC-V binary, and asked GDB to "run" the binary on the native target. I got this error: (gdb) show architecture The target architecture is set to "auto" (currently "i386"). (gdb) file /tmp/hello.rv32.exe Reading symbols from /tmp/hello.rv32.exe... (gdb) show architecture The target architecture is set to "auto" (currently "riscv:rv32"). (gdb) run Starting program: /tmp/hello.rv32.exe ../../src/gdb/i387-tdep.c:596: internal-error: i387_supply_fxsave: Assertion `tdep->st0_regnum >= I386_ST0_REGNUM' failed. What's going on here is this; initially the architecture is i386, this is based on the default architecture, which is set based on the native target. After loading the RISC-V executable the architecture of the current inferior is updated based on the architecture of the executable. When we "run", GDB does a fork & exec, with the inferior being controlled through ptrace. GDB sees an initial stop from the inferior as soon as the inferior comes to life. In response to this stop GDB ends up calling save_stop_reason (linux-nat.c), which ends up trying to read register from the inferior, to do this we end up calling target_ops::fetch_registers, which, for the x86-64 native target, calls amd64_linux_nat_target::fetch_registers. After this I eventually end up in i387_supply_fxsave, different x86 based targets will end in different functions to fetch registers, but it doesn't really matter which function we end up in, the problem is this line, which is repeated in many places: i386_gdbarch_tdep *tdep = (i386_gdbarch_tdep *) gdbarch_tdep (arch); The problem here is that the ARCH in this line comes from the current inferior, which, as we discussed above, will be a RISC-V gdbarch, the tdep field will actually be of type riscv_gdbarch_tdep, not i386_gdbarch_tdep. After this cast we are relying on undefined behaviour, in my case I happen to trigger an assert, but this might not always be the case. The thing I tried that exposed this problem was of course, trying to start an executable of the wrong architecture on a native target. I don't think that the correct solution for this problem is to detect, at the point of cast, that the gdbarch_tdep object is of the wrong type, but, I did wonder, is there a way that we could protect ourselves from incorrectly casting the gdbarch_tdep object? I think that there is something we can do here, and this commit is the first step in that direction, though no actual check is added by this commit. This commit can be split into two parts: (1) In gdbarch.h and arch-utils.c. In these files I have modified gdbarch_tdep (the function) so that it now takes a template argument, like this: template<typename TDepType> static inline TDepType * gdbarch_tdep (struct gdbarch *gdbarch) { struct gdbarch_tdep *tdep = gdbarch_tdep_1 (gdbarch); return static_cast<TDepType *> (tdep); } After this change we are no better protected, but the cast is now done within the gdbarch_tdep function rather than at the call sites, this leads to the second, much larger change in this commit, (2) Everywhere gdbarch_tdep is called, we make changes like this: - i386_gdbarch_tdep *tdep = (i386_gdbarch_tdep *) gdbarch_tdep (arch); + i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (arch); There should be no functional change after this commit. In the next commit I will build on this change to add an assertion in gdbarch_tdep that checks we are casting to the correct type.
421 lines
13 KiB
C
421 lines
13 KiB
C
/* Copyright (C) 2012-2022 Free Software Foundation, Inc.
|
|
|
|
This file is part of GDB.
|
|
|
|
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 "defs.h"
|
|
#include "osabi.h"
|
|
#include "regcache.h"
|
|
#include "gdbcore.h"
|
|
#include "gdbtypes.h"
|
|
#include "infcall.h"
|
|
#include "ppc-tdep.h"
|
|
#include "target-float.h"
|
|
#include "value.h"
|
|
#include "xcoffread.h"
|
|
|
|
/* Implement the "push_dummy_call" gdbarch method. */
|
|
|
|
static CORE_ADDR
|
|
rs6000_lynx178_push_dummy_call (struct gdbarch *gdbarch,
|
|
struct value *function,
|
|
struct regcache *regcache, CORE_ADDR bp_addr,
|
|
int nargs, struct value **args, CORE_ADDR sp,
|
|
function_call_return_method return_method,
|
|
CORE_ADDR struct_addr)
|
|
{
|
|
ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
|
|
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
|
int ii;
|
|
int len = 0;
|
|
int argno; /* current argument number */
|
|
int argbytes; /* current argument byte */
|
|
gdb_byte tmp_buffer[50];
|
|
int f_argno = 0; /* current floating point argno */
|
|
int wordsize = tdep->wordsize;
|
|
|
|
struct value *arg = 0;
|
|
struct type *type;
|
|
|
|
ULONGEST saved_sp;
|
|
|
|
/* The calling convention this function implements assumes the
|
|
processor has floating-point registers. We shouldn't be using it
|
|
on PPC variants that lack them. */
|
|
gdb_assert (ppc_floating_point_unit_p (gdbarch));
|
|
|
|
/* The first eight words of ther arguments are passed in registers.
|
|
Copy them appropriately. */
|
|
ii = 0;
|
|
|
|
/* If the function is returning a `struct', then the first word
|
|
(which will be passed in r3) is used for struct return address.
|
|
In that case we should advance one word and start from r4
|
|
register to copy parameters. */
|
|
if (return_method == return_method_struct)
|
|
{
|
|
regcache_raw_write_unsigned (regcache, tdep->ppc_gp0_regnum + 3,
|
|
struct_addr);
|
|
ii++;
|
|
}
|
|
|
|
/* Effectively indirect call... gcc does...
|
|
|
|
return_val example( float, int);
|
|
|
|
eabi:
|
|
float in fp0, int in r3
|
|
offset of stack on overflow 8/16
|
|
for varargs, must go by type.
|
|
power open:
|
|
float in r3&r4, int in r5
|
|
offset of stack on overflow different
|
|
both:
|
|
return in r3 or f0. If no float, must study how gcc emulates floats;
|
|
pay attention to arg promotion.
|
|
User may have to cast\args to handle promotion correctly
|
|
since gdb won't know if prototype supplied or not. */
|
|
|
|
for (argno = 0, argbytes = 0; argno < nargs && ii < 8; ++ii)
|
|
{
|
|
int reg_size = register_size (gdbarch, ii + 3);
|
|
|
|
arg = args[argno];
|
|
type = check_typedef (value_type (arg));
|
|
len = TYPE_LENGTH (type);
|
|
|
|
if (type->code () == TYPE_CODE_FLT)
|
|
{
|
|
|
|
/* Floating point arguments are passed in fpr's, as well as gpr's.
|
|
There are 13 fpr's reserved for passing parameters. At this point
|
|
there is no way we would run out of them.
|
|
|
|
Always store the floating point value using the register's
|
|
floating-point format. */
|
|
const int fp_regnum = tdep->ppc_fp0_regnum + 1 + f_argno;
|
|
gdb_byte reg_val[PPC_MAX_REGISTER_SIZE];
|
|
struct type *reg_type = register_type (gdbarch, fp_regnum);
|
|
|
|
gdb_assert (len <= 8);
|
|
|
|
target_float_convert (value_contents (arg).data (), type, reg_val,
|
|
reg_type);
|
|
regcache->cooked_write (fp_regnum, reg_val);
|
|
++f_argno;
|
|
}
|
|
|
|
if (len > reg_size)
|
|
{
|
|
|
|
/* Argument takes more than one register. */
|
|
while (argbytes < len)
|
|
{
|
|
gdb_byte word[PPC_MAX_REGISTER_SIZE];
|
|
memset (word, 0, reg_size);
|
|
memcpy (word,
|
|
((char *) value_contents (arg).data ()) + argbytes,
|
|
(len - argbytes) > reg_size
|
|
? reg_size : len - argbytes);
|
|
regcache->cooked_write (tdep->ppc_gp0_regnum + 3 + ii, word);
|
|
++ii, argbytes += reg_size;
|
|
|
|
if (ii >= 8)
|
|
goto ran_out_of_registers_for_arguments;
|
|
}
|
|
argbytes = 0;
|
|
--ii;
|
|
}
|
|
else
|
|
{
|
|
/* Argument can fit in one register. No problem. */
|
|
gdb_byte word[PPC_MAX_REGISTER_SIZE];
|
|
|
|
memset (word, 0, reg_size);
|
|
memcpy (word, value_contents (arg).data (), len);
|
|
regcache->cooked_write (tdep->ppc_gp0_regnum + 3 +ii, word);
|
|
}
|
|
++argno;
|
|
}
|
|
|
|
ran_out_of_registers_for_arguments:
|
|
|
|
regcache_cooked_read_unsigned (regcache,
|
|
gdbarch_sp_regnum (gdbarch),
|
|
&saved_sp);
|
|
|
|
/* Location for 8 parameters are always reserved. */
|
|
sp -= wordsize * 8;
|
|
|
|
/* Another six words for back chain, TOC register, link register, etc. */
|
|
sp -= wordsize * 6;
|
|
|
|
/* Stack pointer must be quadword aligned. */
|
|
sp = align_down (sp, 16);
|
|
|
|
/* If there are more arguments, allocate space for them in
|
|
the stack, then push them starting from the ninth one. */
|
|
|
|
if ((argno < nargs) || argbytes)
|
|
{
|
|
int space = 0, jj;
|
|
|
|
if (argbytes)
|
|
{
|
|
space += align_up (len - argbytes, 4);
|
|
jj = argno + 1;
|
|
}
|
|
else
|
|
jj = argno;
|
|
|
|
for (; jj < nargs; ++jj)
|
|
{
|
|
struct value *val = args[jj];
|
|
|
|
space += align_up (TYPE_LENGTH (value_type (val)), 4);
|
|
}
|
|
|
|
/* Add location required for the rest of the parameters. */
|
|
space = align_up (space, 16);
|
|
sp -= space;
|
|
|
|
/* This is another instance we need to be concerned about
|
|
securing our stack space. If we write anything underneath %sp
|
|
(r1), we might conflict with the kernel who thinks he is free
|
|
to use this area. So, update %sp first before doing anything
|
|
else. */
|
|
|
|
regcache_raw_write_signed (regcache,
|
|
gdbarch_sp_regnum (gdbarch), sp);
|
|
|
|
/* If the last argument copied into the registers didn't fit there
|
|
completely, push the rest of it into stack. */
|
|
|
|
if (argbytes)
|
|
{
|
|
write_memory (sp + 24 + (ii * 4),
|
|
value_contents (arg).data () + argbytes,
|
|
len - argbytes);
|
|
++argno;
|
|
ii += align_up (len - argbytes, 4) / 4;
|
|
}
|
|
|
|
/* Push the rest of the arguments into stack. */
|
|
for (; argno < nargs; ++argno)
|
|
{
|
|
|
|
arg = args[argno];
|
|
type = check_typedef (value_type (arg));
|
|
len = TYPE_LENGTH (type);
|
|
|
|
|
|
/* Float types should be passed in fpr's, as well as in the
|
|
stack. */
|
|
if (type->code () == TYPE_CODE_FLT && f_argno < 13)
|
|
{
|
|
|
|
gdb_assert (len <= 8);
|
|
|
|
regcache->cooked_write (tdep->ppc_fp0_regnum + 1 + f_argno,
|
|
value_contents (arg).data ());
|
|
++f_argno;
|
|
}
|
|
|
|
write_memory (sp + 24 + (ii * 4), value_contents (arg).data (), len);
|
|
ii += align_up (len, 4) / 4;
|
|
}
|
|
}
|
|
|
|
/* Set the stack pointer. According to the ABI, the SP is meant to
|
|
be set _before_ the corresponding stack space is used. On AIX,
|
|
this even applies when the target has been completely stopped!
|
|
Not doing this can lead to conflicts with the kernel which thinks
|
|
that it still has control over this not-yet-allocated stack
|
|
region. */
|
|
regcache_raw_write_signed (regcache, gdbarch_sp_regnum (gdbarch), sp);
|
|
|
|
/* Set back chain properly. */
|
|
store_unsigned_integer (tmp_buffer, wordsize, byte_order, saved_sp);
|
|
write_memory (sp, tmp_buffer, wordsize);
|
|
|
|
/* Point the inferior function call's return address at the dummy's
|
|
breakpoint. */
|
|
regcache_raw_write_signed (regcache, tdep->ppc_lr_regnum, bp_addr);
|
|
|
|
target_store_registers (regcache, -1);
|
|
return sp;
|
|
}
|
|
|
|
/* Implement the "return_value" gdbarch method. */
|
|
|
|
static enum return_value_convention
|
|
rs6000_lynx178_return_value (struct gdbarch *gdbarch, struct value *function,
|
|
struct type *valtype, struct regcache *regcache,
|
|
gdb_byte *readbuf, const gdb_byte *writebuf)
|
|
{
|
|
ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
|
|
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
|
|
|
/* The calling convention this function implements assumes the
|
|
processor has floating-point registers. We shouldn't be using it
|
|
on PowerPC variants that lack them. */
|
|
gdb_assert (ppc_floating_point_unit_p (gdbarch));
|
|
|
|
/* AltiVec extension: Functions that declare a vector data type as a
|
|
return value place that return value in VR2. */
|
|
if (valtype->code () == TYPE_CODE_ARRAY && valtype->is_vector ()
|
|
&& TYPE_LENGTH (valtype) == 16)
|
|
{
|
|
if (readbuf)
|
|
regcache->cooked_read (tdep->ppc_vr0_regnum + 2, readbuf);
|
|
if (writebuf)
|
|
regcache->cooked_write (tdep->ppc_vr0_regnum + 2, writebuf);
|
|
|
|
return RETURN_VALUE_REGISTER_CONVENTION;
|
|
}
|
|
|
|
/* If the called subprogram returns an aggregate, there exists an
|
|
implicit first argument, whose value is the address of a caller-
|
|
allocated buffer into which the callee is assumed to store its
|
|
return value. All explicit parameters are appropriately
|
|
relabeled. */
|
|
if (valtype->code () == TYPE_CODE_STRUCT
|
|
|| valtype->code () == TYPE_CODE_UNION
|
|
|| valtype->code () == TYPE_CODE_ARRAY)
|
|
return RETURN_VALUE_STRUCT_CONVENTION;
|
|
|
|
/* Scalar floating-point values are returned in FPR1 for float or
|
|
double, and in FPR1:FPR2 for quadword precision. Fortran
|
|
complex*8 and complex*16 are returned in FPR1:FPR2, and
|
|
complex*32 is returned in FPR1:FPR4. */
|
|
if (valtype->code () == TYPE_CODE_FLT
|
|
&& (TYPE_LENGTH (valtype) == 4 || TYPE_LENGTH (valtype) == 8))
|
|
{
|
|
struct type *regtype = register_type (gdbarch, tdep->ppc_fp0_regnum);
|
|
gdb_byte regval[8];
|
|
|
|
/* FIXME: kettenis/2007-01-01: Add support for quadword
|
|
precision and complex. */
|
|
|
|
if (readbuf)
|
|
{
|
|
regcache->cooked_read (tdep->ppc_fp0_regnum + 1, regval);
|
|
target_float_convert (regval, regtype, readbuf, valtype);
|
|
}
|
|
if (writebuf)
|
|
{
|
|
target_float_convert (writebuf, valtype, regval, regtype);
|
|
regcache->cooked_write (tdep->ppc_fp0_regnum + 1, regval);
|
|
}
|
|
|
|
return RETURN_VALUE_REGISTER_CONVENTION;
|
|
}
|
|
|
|
/* Values of the types int, long, short, pointer, and char (length
|
|
is less than or equal to four bytes), as well as bit values of
|
|
lengths less than or equal to 32 bits, must be returned right
|
|
justified in GPR3 with signed values sign extended and unsigned
|
|
values zero extended, as necessary. */
|
|
if (TYPE_LENGTH (valtype) <= tdep->wordsize)
|
|
{
|
|
if (readbuf)
|
|
{
|
|
ULONGEST regval;
|
|
|
|
/* For reading we don't have to worry about sign extension. */
|
|
regcache_cooked_read_unsigned (regcache, tdep->ppc_gp0_regnum + 3,
|
|
®val);
|
|
store_unsigned_integer (readbuf, TYPE_LENGTH (valtype), byte_order,
|
|
regval);
|
|
}
|
|
if (writebuf)
|
|
{
|
|
/* For writing, use unpack_long since that should handle any
|
|
required sign extension. */
|
|
regcache_cooked_write_unsigned (regcache, tdep->ppc_gp0_regnum + 3,
|
|
unpack_long (valtype, writebuf));
|
|
}
|
|
|
|
return RETURN_VALUE_REGISTER_CONVENTION;
|
|
}
|
|
|
|
/* Eight-byte non-floating-point scalar values must be returned in
|
|
GPR3:GPR4. */
|
|
|
|
if (TYPE_LENGTH (valtype) == 8)
|
|
{
|
|
gdb_assert (valtype->code () != TYPE_CODE_FLT);
|
|
gdb_assert (tdep->wordsize == 4);
|
|
|
|
if (readbuf)
|
|
{
|
|
gdb_byte regval[8];
|
|
|
|
regcache->cooked_read (tdep->ppc_gp0_regnum + 3, regval);
|
|
regcache->cooked_read (tdep->ppc_gp0_regnum + 4, regval + 4);
|
|
memcpy (readbuf, regval, 8);
|
|
}
|
|
if (writebuf)
|
|
{
|
|
regcache->cooked_write (tdep->ppc_gp0_regnum + 3, writebuf);
|
|
regcache->cooked_write (tdep->ppc_gp0_regnum + 4, writebuf + 4);
|
|
}
|
|
|
|
return RETURN_VALUE_REGISTER_CONVENTION;
|
|
}
|
|
|
|
return RETURN_VALUE_STRUCT_CONVENTION;
|
|
}
|
|
|
|
/* PowerPC Lynx178 OSABI sniffer. */
|
|
|
|
static enum gdb_osabi
|
|
rs6000_lynx178_osabi_sniffer (bfd *abfd)
|
|
{
|
|
if (bfd_get_flavour (abfd) != bfd_target_xcoff_flavour)
|
|
return GDB_OSABI_UNKNOWN;
|
|
|
|
/* The only noticeable difference between Lynx178 XCOFF files and
|
|
AIX XCOFF files comes from the fact that there are no shared
|
|
libraries on Lynx178. So if the number of import files is
|
|
different from zero, it cannot be a Lynx178 binary. */
|
|
if (xcoff_get_n_import_files (abfd) != 0)
|
|
return GDB_OSABI_UNKNOWN;
|
|
|
|
return GDB_OSABI_LYNXOS178;
|
|
}
|
|
|
|
/* Callback for powerpc-lynx178 initialization. */
|
|
|
|
static void
|
|
rs6000_lynx178_init_osabi (struct gdbarch_info info, struct gdbarch *gdbarch)
|
|
{
|
|
set_gdbarch_push_dummy_call (gdbarch, rs6000_lynx178_push_dummy_call);
|
|
set_gdbarch_return_value (gdbarch, rs6000_lynx178_return_value);
|
|
set_gdbarch_long_double_bit (gdbarch, 8 * TARGET_CHAR_BIT);
|
|
}
|
|
|
|
void _initialize_rs6000_lynx178_tdep ();
|
|
void
|
|
_initialize_rs6000_lynx178_tdep ()
|
|
{
|
|
gdbarch_register_osabi_sniffer (bfd_arch_rs6000,
|
|
bfd_target_xcoff_flavour,
|
|
rs6000_lynx178_osabi_sniffer);
|
|
gdbarch_register_osabi (bfd_arch_rs6000, 0, GDB_OSABI_LYNXOS178,
|
|
rs6000_lynx178_init_osabi);
|
|
}
|
|
|