mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-21 01:12:32 +08:00
91f378ddd0
Make the current inferior reference bubble up one level. I think this makes it clearer what gdbarch_update_p, which is update the passed inferior's architecture (although the function name could probably be better). When gdbarch_find_by_info, it is possible for the new architecture's init callback to be called. I have not audited all of them (there are just too many), it's possible that some of them do care about the current inferior, for some reason (for instance, if one of them makes a target call). If so, they should be changed too. Change-Id: I89f012188d7fdca395a830f4b013743565f26847
1074 lines
28 KiB
C
1074 lines
28 KiB
C
/* IBM RS/6000 native-dependent code for GDB, the GNU debugger.
|
||
|
||
Copyright (C) 1986-2024 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 "inferior.h"
|
||
#include "target.h"
|
||
#include "gdbcore.h"
|
||
#include "symfile.h"
|
||
#include "objfiles.h"
|
||
#include "bfd.h"
|
||
#include "gdb-stabs.h"
|
||
#include "regcache.h"
|
||
#include "arch-utils.h"
|
||
#include "inf-child.h"
|
||
#include "inf-ptrace.h"
|
||
#include "ppc-tdep.h"
|
||
#include "rs6000-aix-tdep.h"
|
||
#include "exec.h"
|
||
#include "observable.h"
|
||
#include "xcoffread.h"
|
||
|
||
#include <sys/ptrace.h>
|
||
#include <sys/reg.h>
|
||
|
||
#include <sys/dir.h>
|
||
#include <sys/user.h>
|
||
#include <signal.h>
|
||
#include <sys/ioctl.h>
|
||
#include <fcntl.h>
|
||
|
||
#include <a.out.h>
|
||
#include <sys/file.h>
|
||
#include <sys/stat.h>
|
||
#include "gdb_bfd.h"
|
||
#include <sys/core.h>
|
||
#define __LDINFO_PTRACE32__ /* for __ld_info32 */
|
||
#define __LDINFO_PTRACE64__ /* for __ld_info64 */
|
||
#include <sys/ldr.h>
|
||
#include <sys/systemcfg.h>
|
||
|
||
/* Header files for getting ppid in AIX of a child process. */
|
||
#include <procinfo.h>
|
||
#include <sys/types.h>
|
||
|
||
/* Header files for alti-vec reg. */
|
||
#include <sys/context.h>
|
||
|
||
/* On AIX4.3+, sys/ldr.h provides different versions of struct ld_info for
|
||
debugging 32-bit and 64-bit processes. Define a typedef and macros for
|
||
accessing fields in the appropriate structures. */
|
||
|
||
/* In 32-bit compilation mode (which is the only mode from which ptrace()
|
||
works on 4.3), __ld_info32 is #defined as equivalent to ld_info. */
|
||
|
||
#if defined (__ld_info32) || defined (__ld_info64)
|
||
# define ARCH3264
|
||
#endif
|
||
|
||
/* Return whether the current architecture is 64-bit. */
|
||
|
||
#ifndef ARCH3264
|
||
# define ARCH64() 0
|
||
#else
|
||
# define ARCH64() (register_size (current_inferior ()->arch (), 0) == 8)
|
||
#endif
|
||
|
||
class rs6000_nat_target final : public inf_ptrace_target
|
||
{
|
||
public:
|
||
void fetch_registers (struct regcache *, int) override;
|
||
void store_registers (struct regcache *, int) override;
|
||
|
||
enum target_xfer_status xfer_partial (enum target_object object,
|
||
const char *annex,
|
||
gdb_byte *readbuf,
|
||
const gdb_byte *writebuf,
|
||
ULONGEST offset, ULONGEST len,
|
||
ULONGEST *xfered_len) override;
|
||
|
||
void create_inferior (const char *, const std::string &,
|
||
char **, int) override;
|
||
|
||
ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
|
||
|
||
/* Fork detection related functions, For adding multi process debugging
|
||
support. */
|
||
void follow_fork (inferior *, ptid_t, target_waitkind, bool, bool) override;
|
||
|
||
const struct target_desc *read_description () override;
|
||
|
||
int insert_fork_catchpoint (int) override;
|
||
int remove_fork_catchpoint (int) override;
|
||
|
||
protected:
|
||
|
||
void post_startup_inferior (ptid_t ptid) override;
|
||
|
||
private:
|
||
enum target_xfer_status
|
||
xfer_shared_libraries (enum target_object object,
|
||
const char *annex, gdb_byte *readbuf,
|
||
const gdb_byte *writebuf,
|
||
ULONGEST offset, ULONGEST len,
|
||
ULONGEST *xfered_len);
|
||
};
|
||
|
||
static rs6000_nat_target the_rs6000_nat_target;
|
||
|
||
/* The below declaration is to track number of times, parent has
|
||
reported fork event before its children. */
|
||
|
||
static std::list<pid_t> aix_pending_parent;
|
||
|
||
/* The below declaration is for a child process event that
|
||
is reported before its corresponding parent process in
|
||
the event of a fork (). */
|
||
|
||
static std::list<pid_t> aix_pending_children;
|
||
|
||
static void
|
||
aix_remember_child (pid_t pid)
|
||
{
|
||
aix_pending_children.push_front (pid);
|
||
}
|
||
|
||
static void
|
||
aix_remember_parent (pid_t pid)
|
||
{
|
||
aix_pending_parent.push_front (pid);
|
||
}
|
||
|
||
/* This function returns a parent of a child process. */
|
||
|
||
static pid_t
|
||
find_my_aix_parent (pid_t child_pid)
|
||
{
|
||
struct procsinfo ProcessBuffer1;
|
||
|
||
if (getprocs (&ProcessBuffer1, sizeof (ProcessBuffer1),
|
||
NULL, 0, &child_pid, 1) != 1)
|
||
return 0;
|
||
else
|
||
return ProcessBuffer1.pi_ppid;
|
||
}
|
||
|
||
/* In the below function we check if there was any child
|
||
process pending. If it exists we return it from the
|
||
list, otherwise we return a null. */
|
||
|
||
static pid_t
|
||
has_my_aix_child_reported (pid_t parent_pid)
|
||
{
|
||
pid_t child = 0;
|
||
auto it = std::find_if (aix_pending_children.begin (),
|
||
aix_pending_children.end (),
|
||
[=] (pid_t child_pid)
|
||
{
|
||
return find_my_aix_parent (child_pid) == parent_pid;
|
||
});
|
||
if (it != aix_pending_children.end ())
|
||
{
|
||
child = *it;
|
||
aix_pending_children.erase (it);
|
||
}
|
||
return child;
|
||
}
|
||
|
||
/* In the below function we check if there was any parent
|
||
process pending. If it exists we return it from the
|
||
list, otherwise we return a null. */
|
||
|
||
static pid_t
|
||
has_my_aix_parent_reported (pid_t child_pid)
|
||
{
|
||
pid_t my_parent = find_my_aix_parent (child_pid);
|
||
auto it = std::find (aix_pending_parent.begin (),
|
||
aix_pending_parent.end (),
|
||
my_parent);
|
||
if (it != aix_pending_parent.end ())
|
||
{
|
||
aix_pending_parent.erase (it);
|
||
return my_parent;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* Given REGNO, a gdb register number, return the corresponding
|
||
number suitable for use as a ptrace() parameter. Return -1 if
|
||
there's no suitable mapping. Also, set the int pointed to by
|
||
ISFLOAT to indicate whether REGNO is a floating point register. */
|
||
|
||
static int
|
||
regmap (struct gdbarch *gdbarch, int regno, int *isfloat)
|
||
{
|
||
ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
|
||
|
||
*isfloat = 0;
|
||
if (tdep->ppc_gp0_regnum <= regno
|
||
&& regno < tdep->ppc_gp0_regnum + ppc_num_gprs)
|
||
return regno;
|
||
else if (tdep->ppc_fp0_regnum >= 0
|
||
&& tdep->ppc_fp0_regnum <= regno
|
||
&& regno < tdep->ppc_fp0_regnum + ppc_num_fprs)
|
||
{
|
||
*isfloat = 1;
|
||
return regno - tdep->ppc_fp0_regnum + FPR0;
|
||
}
|
||
else if (regno == gdbarch_pc_regnum (gdbarch))
|
||
return IAR;
|
||
else if (regno == tdep->ppc_ps_regnum)
|
||
return MSR;
|
||
else if (regno == tdep->ppc_cr_regnum)
|
||
return CR;
|
||
else if (regno == tdep->ppc_lr_regnum)
|
||
return LR;
|
||
else if (regno == tdep->ppc_ctr_regnum)
|
||
return CTR;
|
||
else if (regno == tdep->ppc_xer_regnum)
|
||
return XER;
|
||
else if (tdep->ppc_fpscr_regnum >= 0
|
||
&& regno == tdep->ppc_fpscr_regnum)
|
||
return FPSCR;
|
||
else if (tdep->ppc_mq_regnum >= 0 && regno == tdep->ppc_mq_regnum)
|
||
return MQ;
|
||
else
|
||
return -1;
|
||
}
|
||
|
||
/* Call ptrace(REQ, ID, ADDR, DATA, BUF). */
|
||
|
||
static int
|
||
rs6000_ptrace32 (int req, int id, int *addr, int data, int *buf)
|
||
{
|
||
#ifdef HAVE_PTRACE64
|
||
int ret = ptrace64 (req, id, (uintptr_t) addr, data, buf);
|
||
#else
|
||
int ret = ptrace (req, id, (int *)addr, data, buf);
|
||
#endif
|
||
#if 0
|
||
printf ("rs6000_ptrace32 (%d, %d, 0x%x, %08x, 0x%x) = 0x%x\n",
|
||
req, id, (unsigned int)addr, data, (unsigned int)buf, ret);
|
||
#endif
|
||
return ret;
|
||
}
|
||
|
||
/* Call ptracex(REQ, ID, ADDR, DATA, BUF). */
|
||
|
||
static int
|
||
rs6000_ptrace64 (int req, int id, long long addr, int data, void *buf)
|
||
{
|
||
#ifdef ARCH3264
|
||
# ifdef HAVE_PTRACE64
|
||
int ret = ptrace64 (req, id, addr, data, (PTRACE_TYPE_ARG5) buf);
|
||
# else
|
||
int ret = ptracex (req, id, addr, data, (PTRACE_TYPE_ARG5) buf);
|
||
# endif
|
||
#else
|
||
int ret = 0;
|
||
#endif
|
||
#if 0
|
||
printf ("rs6000_ptrace64 (%d, %d, %s, %08x, 0x%x) = 0x%x\n",
|
||
req, id, hex_string (addr), data, (unsigned int)buf, ret);
|
||
#endif
|
||
return ret;
|
||
}
|
||
|
||
/* Store the vsx registers. */
|
||
|
||
static void
|
||
store_vsx_register_aix (struct regcache *regcache, int regno)
|
||
{
|
||
int ret;
|
||
struct gdbarch *gdbarch = regcache->arch ();
|
||
ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
|
||
struct thrdentry64 thrdentry;
|
||
__vsx_context_t vsx;
|
||
pid_t pid = inferior_ptid.pid ();
|
||
tid64_t thrd_i = 0;
|
||
|
||
if (getthrds64(pid, &thrdentry, sizeof(struct thrdentry64),
|
||
&thrd_i, 1) == 1)
|
||
thrd_i = thrdentry.ti_tid;
|
||
|
||
memset(&vsx, 0, sizeof(__vsx_context_t));
|
||
if (__power_vsx() && thrd_i > 0)
|
||
{
|
||
if (ARCH64 ())
|
||
ret = rs6000_ptrace64 (PTT_READ_VSX, thrd_i, (long long) &vsx, 0, 0);
|
||
else
|
||
ret = rs6000_ptrace32 (PTT_READ_VSX, thrd_i, (int *)&vsx, 0, 0);
|
||
if (ret < 0)
|
||
return;
|
||
|
||
regcache->raw_collect (regno, &(vsx.__vsr_dw1[0])+
|
||
regno - tdep->ppc_vsr0_upper_regnum);
|
||
|
||
if (ARCH64 ())
|
||
ret = rs6000_ptrace64 (PTT_WRITE_VSX, thrd_i, (long long) &vsx, 0, 0);
|
||
else
|
||
ret = rs6000_ptrace32 (PTT_WRITE_VSX, thrd_i, (int *) &vsx, 0, 0);
|
||
|
||
if (ret < 0)
|
||
perror_with_name (_("Unable to write VSX registers after reading it"));
|
||
}
|
||
}
|
||
|
||
/* Store Altivec registers. */
|
||
|
||
static void
|
||
store_altivec_register_aix (struct regcache *regcache, int regno)
|
||
{
|
||
int ret;
|
||
struct gdbarch *gdbarch = regcache->arch ();
|
||
ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
|
||
struct thrdentry64 thrdentry;
|
||
__vmx_context_t vmx;
|
||
pid_t pid = inferior_ptid.pid ();
|
||
tid64_t thrd_i = 0;
|
||
|
||
if (getthrds64(pid, &thrdentry, sizeof(struct thrdentry64),
|
||
&thrd_i, 1) == 1)
|
||
thrd_i = thrdentry.ti_tid;
|
||
|
||
memset(&vmx, 0, sizeof(__vmx_context_t));
|
||
if (__power_vmx() && thrd_i > 0)
|
||
{
|
||
if (ARCH64 ())
|
||
ret = rs6000_ptrace64 (PTT_READ_VEC, thrd_i, (long long) &vmx, 0, 0);
|
||
else
|
||
ret = rs6000_ptrace32 (PTT_READ_VEC, thrd_i, (int *) &vmx, 0, 0);
|
||
if (ret < 0)
|
||
return;
|
||
|
||
regcache->raw_collect (regno, &(vmx.__vr[0]) + regno
|
||
- tdep->ppc_vr0_regnum);
|
||
|
||
if (ARCH64 ())
|
||
ret = rs6000_ptrace64 (PTT_WRITE_VEC, thrd_i, (long long) &vmx, 0, 0);
|
||
else
|
||
ret = rs6000_ptrace32 (PTT_WRITE_VEC, thrd_i, (int *) &vmx, 0, 0);
|
||
if (ret < 0)
|
||
perror_with_name (_("Unable to store AltiVec register after reading it"));
|
||
}
|
||
}
|
||
|
||
/* Supply altivec registers. */
|
||
|
||
static void
|
||
supply_vrregset_aix (struct regcache *regcache, __vmx_context_t *vmx)
|
||
{
|
||
int i;
|
||
struct gdbarch *gdbarch = regcache->arch ();
|
||
ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
|
||
int num_of_vrregs = tdep->ppc_vrsave_regnum - tdep->ppc_vr0_regnum + 1;
|
||
|
||
for (i = 0; i < num_of_vrregs; i++)
|
||
regcache->raw_supply (tdep->ppc_vr0_regnum + i,
|
||
&(vmx->__vr[i]));
|
||
regcache->raw_supply (tdep->ppc_vrsave_regnum, &(vmx->__vrsave));
|
||
regcache->raw_supply (tdep->ppc_vrsave_regnum - 1, &(vmx->__vscr));
|
||
}
|
||
|
||
/* Fetch altivec register. */
|
||
|
||
static void
|
||
fetch_altivec_registers_aix (struct regcache *regcache)
|
||
{
|
||
struct thrdentry64 thrdentry;
|
||
__vmx_context_t vmx;
|
||
pid_t pid = current_inferior ()->pid;
|
||
tid64_t thrd_i = 0;
|
||
|
||
if (getthrds64(pid, &thrdentry, sizeof(struct thrdentry64),
|
||
&thrd_i, 1) == 1)
|
||
thrd_i = thrdentry.ti_tid;
|
||
|
||
memset(&vmx, 0, sizeof(__vmx_context_t));
|
||
if (__power_vmx() && thrd_i > 0)
|
||
{
|
||
if (ARCH64 ())
|
||
rs6000_ptrace64 (PTT_READ_VEC, thrd_i, (long long) &vmx, 0, 0);
|
||
else
|
||
rs6000_ptrace32 (PTT_READ_VEC, thrd_i, (int *) &vmx, 0, 0);
|
||
supply_vrregset_aix (regcache, &vmx);
|
||
}
|
||
}
|
||
|
||
/* supply vsx register. */
|
||
|
||
static void
|
||
supply_vsxregset_aix (struct regcache *regcache, __vsx_context_t *vsx)
|
||
{
|
||
int i;
|
||
struct gdbarch *gdbarch = regcache->arch ();
|
||
ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
|
||
|
||
for (i = 0; i < ppc_num_vshrs; i++)
|
||
regcache->raw_supply (tdep->ppc_vsr0_upper_regnum + i,
|
||
&(vsx->__vsr_dw1[i]));
|
||
}
|
||
|
||
/* Fetch vsx registers. */
|
||
static void
|
||
fetch_vsx_registers_aix (struct regcache *regcache)
|
||
{
|
||
struct thrdentry64 thrdentry;
|
||
__vsx_context_t vsx;
|
||
pid_t pid = current_inferior ()->pid;
|
||
tid64_t thrd_i = 0;
|
||
|
||
if (getthrds64(pid, &thrdentry, sizeof(struct thrdentry64),
|
||
&thrd_i, 1) == 1)
|
||
thrd_i = thrdentry.ti_tid;
|
||
|
||
memset(&vsx, 0, sizeof(__vsx_context_t));
|
||
if (__power_vsx() && thrd_i > 0)
|
||
{
|
||
if (ARCH64 ())
|
||
rs6000_ptrace64 (PTT_READ_VSX, thrd_i, (long long) &vsx, 0, 0);
|
||
else
|
||
rs6000_ptrace32 (PTT_READ_VSX, thrd_i, (int *) &vsx, 0, 0);
|
||
supply_vsxregset_aix (regcache, &vsx);
|
||
}
|
||
}
|
||
|
||
void rs6000_nat_target::post_startup_inferior (ptid_t ptid)
|
||
{
|
||
|
||
/* In AIX to turn on multi process debugging in ptrace
|
||
PT_MULTI is the option to be passed,
|
||
with the process ID which can fork () and
|
||
the data parameter [fourth parameter] must be 1. */
|
||
|
||
if (!ARCH64 ())
|
||
rs6000_ptrace32 (PT_MULTI, ptid.pid(), 0, 1, 0);
|
||
else
|
||
rs6000_ptrace64 (PT_MULTI, ptid.pid(), 0, 1, 0);
|
||
}
|
||
|
||
void
|
||
rs6000_nat_target::follow_fork (inferior *child_inf, ptid_t child_ptid,
|
||
target_waitkind fork_kind, bool follow_child,
|
||
bool detach_fork)
|
||
{
|
||
|
||
/* Once the fork event is detected the infrun.c code
|
||
calls the target_follow_fork to take care of
|
||
follow child and detach the child activity which is
|
||
done using the function below. */
|
||
|
||
inf_ptrace_target::follow_fork (child_inf, child_ptid, fork_kind,
|
||
follow_child, detach_fork);
|
||
|
||
/* If we detach fork and follow child we do not want the child
|
||
process to generate events that ptrace can trace. Hence we
|
||
detach it. */
|
||
|
||
if (detach_fork && !follow_child)
|
||
{
|
||
if (ARCH64 ())
|
||
rs6000_ptrace64 (PT_DETACH, child_ptid.pid (), 0, 0, 0);
|
||
else
|
||
rs6000_ptrace32 (PT_DETACH, child_ptid.pid (), 0, 0, 0);
|
||
}
|
||
}
|
||
|
||
/* Functions for catchpoint in AIX. */
|
||
int
|
||
rs6000_nat_target::insert_fork_catchpoint (int pid)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
int
|
||
rs6000_nat_target::remove_fork_catchpoint (int pid)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
/* Fetch register REGNO from the inferior. */
|
||
|
||
static void
|
||
fetch_register (struct regcache *regcache, int regno)
|
||
{
|
||
struct gdbarch *gdbarch = regcache->arch ();
|
||
int addr[PPC_MAX_REGISTER_SIZE];
|
||
int nr, isfloat;
|
||
pid_t pid = regcache->ptid ().pid ();
|
||
|
||
/* Retrieved values may be -1, so infer errors from errno. */
|
||
errno = 0;
|
||
|
||
/* Alti-vec register. */
|
||
if (altivec_register_p (gdbarch, regno))
|
||
{
|
||
fetch_altivec_registers_aix (regcache);
|
||
return;
|
||
}
|
||
|
||
/* VSX register. */
|
||
if (vsx_register_p (gdbarch, regno))
|
||
{
|
||
fetch_vsx_registers_aix (regcache);
|
||
return;
|
||
}
|
||
|
||
nr = regmap (gdbarch, regno, &isfloat);
|
||
|
||
/* Floating-point registers. */
|
||
if (isfloat)
|
||
rs6000_ptrace32 (PT_READ_FPR, pid, addr, nr, 0);
|
||
|
||
/* Bogus register number. */
|
||
else if (nr < 0)
|
||
{
|
||
if (regno >= gdbarch_num_regs (gdbarch))
|
||
gdb_printf (gdb_stderr,
|
||
"gdb error: register no %d not implemented.\n",
|
||
regno);
|
||
return;
|
||
}
|
||
|
||
/* Fixed-point registers. */
|
||
else
|
||
{
|
||
if (!ARCH64 ())
|
||
*addr = rs6000_ptrace32 (PT_READ_GPR, pid, (int *) nr, 0, 0);
|
||
else
|
||
{
|
||
/* PT_READ_GPR requires the buffer parameter to point to long long,
|
||
even if the register is really only 32 bits. */
|
||
long long buf;
|
||
rs6000_ptrace64 (PT_READ_GPR, pid, nr, 0, &buf);
|
||
if (register_size (gdbarch, regno) == 8)
|
||
memcpy (addr, &buf, 8);
|
||
else
|
||
*addr = buf;
|
||
}
|
||
}
|
||
|
||
if (!errno)
|
||
regcache->raw_supply (regno, (char *) addr);
|
||
else
|
||
{
|
||
#if 0
|
||
/* FIXME: this happens 3 times at the start of each 64-bit program. */
|
||
perror (_("ptrace read"));
|
||
#endif
|
||
errno = 0;
|
||
}
|
||
}
|
||
|
||
/* Store register REGNO back into the inferior. */
|
||
|
||
static void
|
||
store_register (struct regcache *regcache, int regno)
|
||
{
|
||
struct gdbarch *gdbarch = regcache->arch ();
|
||
int addr[PPC_MAX_REGISTER_SIZE];
|
||
int nr, isfloat;
|
||
pid_t pid = regcache->ptid ().pid ();
|
||
|
||
/* Fetch the register's value from the register cache. */
|
||
regcache->raw_collect (regno, addr);
|
||
|
||
/* -1 can be a successful return value, so infer errors from errno. */
|
||
errno = 0;
|
||
|
||
if (altivec_register_p (gdbarch, regno))
|
||
{
|
||
store_altivec_register_aix (regcache, regno);
|
||
return;
|
||
}
|
||
|
||
if (vsx_register_p (gdbarch, regno))
|
||
{
|
||
store_vsx_register_aix (regcache, regno);
|
||
return;
|
||
}
|
||
|
||
nr = regmap (gdbarch, regno, &isfloat);
|
||
|
||
/* Floating-point registers. */
|
||
if (isfloat)
|
||
rs6000_ptrace32 (PT_WRITE_FPR, pid, addr, nr, 0);
|
||
|
||
/* Bogus register number. */
|
||
else if (nr < 0)
|
||
{
|
||
if (regno >= gdbarch_num_regs (gdbarch))
|
||
gdb_printf (gdb_stderr,
|
||
"gdb error: register no %d not implemented.\n",
|
||
regno);
|
||
}
|
||
|
||
/* Fixed-point registers. */
|
||
else
|
||
{
|
||
/* The PT_WRITE_GPR operation is rather odd. For 32-bit inferiors,
|
||
the register's value is passed by value, but for 64-bit inferiors,
|
||
the address of a buffer containing the value is passed. */
|
||
if (!ARCH64 ())
|
||
rs6000_ptrace32 (PT_WRITE_GPR, pid, (int *) nr, *addr, 0);
|
||
else
|
||
{
|
||
/* PT_WRITE_GPR requires the buffer parameter to point to an 8-byte
|
||
area, even if the register is really only 32 bits. */
|
||
long long buf;
|
||
if (register_size (gdbarch, regno) == 8)
|
||
memcpy (&buf, addr, 8);
|
||
else
|
||
buf = *addr;
|
||
rs6000_ptrace64 (PT_WRITE_GPR, pid, nr, 0, &buf);
|
||
}
|
||
}
|
||
|
||
if (errno)
|
||
{
|
||
perror (_("ptrace write"));
|
||
errno = 0;
|
||
}
|
||
}
|
||
|
||
/* Read from the inferior all registers if REGNO == -1 and just register
|
||
REGNO otherwise. */
|
||
|
||
void
|
||
rs6000_nat_target::fetch_registers (struct regcache *regcache, int regno)
|
||
{
|
||
struct gdbarch *gdbarch = regcache->arch ();
|
||
if (regno != -1)
|
||
fetch_register (regcache, regno);
|
||
|
||
else
|
||
{
|
||
ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
|
||
|
||
/* Read 32 general purpose registers. */
|
||
for (regno = tdep->ppc_gp0_regnum;
|
||
regno < tdep->ppc_gp0_regnum + ppc_num_gprs;
|
||
regno++)
|
||
{
|
||
fetch_register (regcache, regno);
|
||
}
|
||
|
||
/* Read general purpose floating point registers. */
|
||
if (tdep->ppc_fp0_regnum >= 0)
|
||
for (regno = 0; regno < ppc_num_fprs; regno++)
|
||
fetch_register (regcache, tdep->ppc_fp0_regnum + regno);
|
||
|
||
if (tdep->ppc_vr0_regnum != -1 && tdep->ppc_vrsave_regnum != -1)
|
||
fetch_altivec_registers_aix (regcache);
|
||
|
||
if (tdep->ppc_vsr0_upper_regnum != -1)
|
||
fetch_vsx_registers_aix (regcache);
|
||
|
||
/* Read special registers. */
|
||
fetch_register (regcache, gdbarch_pc_regnum (gdbarch));
|
||
fetch_register (regcache, tdep->ppc_ps_regnum);
|
||
fetch_register (regcache, tdep->ppc_cr_regnum);
|
||
fetch_register (regcache, tdep->ppc_lr_regnum);
|
||
fetch_register (regcache, tdep->ppc_ctr_regnum);
|
||
fetch_register (regcache, tdep->ppc_xer_regnum);
|
||
if (tdep->ppc_fpscr_regnum >= 0)
|
||
fetch_register (regcache, tdep->ppc_fpscr_regnum);
|
||
if (tdep->ppc_mq_regnum >= 0)
|
||
fetch_register (regcache, tdep->ppc_mq_regnum);
|
||
}
|
||
}
|
||
|
||
const struct target_desc *
|
||
rs6000_nat_target::read_description ()
|
||
{
|
||
if (ARCH64())
|
||
{
|
||
if (__power_vsx ())
|
||
return tdesc_powerpc_vsx64;
|
||
else if (__power_vmx ())
|
||
return tdesc_powerpc_altivec64;
|
||
}
|
||
else
|
||
{
|
||
if (__power_vsx ())
|
||
return tdesc_powerpc_vsx32;
|
||
else if (__power_vmx ())
|
||
return tdesc_powerpc_altivec32;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
/* Store our register values back into the inferior.
|
||
If REGNO is -1, do this for all registers.
|
||
Otherwise, REGNO specifies which register (so we can save time). */
|
||
|
||
void
|
||
rs6000_nat_target::store_registers (struct regcache *regcache, int regno)
|
||
{
|
||
struct gdbarch *gdbarch = regcache->arch ();
|
||
if (regno != -1)
|
||
store_register (regcache, regno);
|
||
|
||
else
|
||
{
|
||
ppc_gdbarch_tdep *tdep = gdbarch_tdep<ppc_gdbarch_tdep> (gdbarch);
|
||
|
||
/* Write general purpose registers first. */
|
||
for (regno = tdep->ppc_gp0_regnum;
|
||
regno < tdep->ppc_gp0_regnum + ppc_num_gprs;
|
||
regno++)
|
||
{
|
||
store_register (regcache, regno);
|
||
}
|
||
|
||
/* Write floating point registers. */
|
||
if (tdep->ppc_fp0_regnum >= 0)
|
||
for (regno = 0; regno < ppc_num_fprs; regno++)
|
||
store_register (regcache, tdep->ppc_fp0_regnum + regno);
|
||
|
||
/* Write special registers. */
|
||
store_register (regcache, gdbarch_pc_regnum (gdbarch));
|
||
store_register (regcache, tdep->ppc_ps_regnum);
|
||
store_register (regcache, tdep->ppc_cr_regnum);
|
||
store_register (regcache, tdep->ppc_lr_regnum);
|
||
store_register (regcache, tdep->ppc_ctr_regnum);
|
||
store_register (regcache, tdep->ppc_xer_regnum);
|
||
if (tdep->ppc_fpscr_regnum >= 0)
|
||
store_register (regcache, tdep->ppc_fpscr_regnum);
|
||
if (tdep->ppc_mq_regnum >= 0)
|
||
store_register (regcache, tdep->ppc_mq_regnum);
|
||
}
|
||
}
|
||
|
||
/* Implement the to_xfer_partial target_ops method. */
|
||
|
||
enum target_xfer_status
|
||
rs6000_nat_target::xfer_partial (enum target_object object,
|
||
const char *annex, gdb_byte *readbuf,
|
||
const gdb_byte *writebuf,
|
||
ULONGEST offset, ULONGEST len,
|
||
ULONGEST *xfered_len)
|
||
{
|
||
pid_t pid = inferior_ptid.pid ();
|
||
int arch64 = ARCH64 ();
|
||
|
||
switch (object)
|
||
{
|
||
case TARGET_OBJECT_LIBRARIES_AIX:
|
||
return xfer_shared_libraries (object, annex,
|
||
readbuf, writebuf,
|
||
offset, len, xfered_len);
|
||
case TARGET_OBJECT_MEMORY:
|
||
{
|
||
union
|
||
{
|
||
PTRACE_TYPE_RET word;
|
||
gdb_byte byte[sizeof (PTRACE_TYPE_RET)];
|
||
} buffer;
|
||
ULONGEST rounded_offset;
|
||
LONGEST partial_len;
|
||
|
||
/* Round the start offset down to the next long word
|
||
boundary. */
|
||
rounded_offset = offset & -(ULONGEST) sizeof (PTRACE_TYPE_RET);
|
||
|
||
/* Since ptrace will transfer a single word starting at that
|
||
rounded_offset the partial_len needs to be adjusted down to
|
||
that (remember this function only does a single transfer).
|
||
Should the required length be even less, adjust it down
|
||
again. */
|
||
partial_len = (rounded_offset + sizeof (PTRACE_TYPE_RET)) - offset;
|
||
if (partial_len > len)
|
||
partial_len = len;
|
||
|
||
if (writebuf)
|
||
{
|
||
/* If OFFSET:PARTIAL_LEN is smaller than
|
||
ROUNDED_OFFSET:WORDSIZE then a read/modify write will
|
||
be needed. Read in the entire word. */
|
||
if (rounded_offset < offset
|
||
|| (offset + partial_len
|
||
< rounded_offset + sizeof (PTRACE_TYPE_RET)))
|
||
{
|
||
/* Need part of initial word -- fetch it. */
|
||
if (arch64)
|
||
buffer.word = rs6000_ptrace64 (PT_READ_I, pid,
|
||
rounded_offset, 0, NULL);
|
||
else
|
||
buffer.word = rs6000_ptrace32 (PT_READ_I, pid,
|
||
(int *) (uintptr_t)
|
||
rounded_offset,
|
||
0, NULL);
|
||
}
|
||
|
||
/* Copy data to be written over corresponding part of
|
||
buffer. */
|
||
memcpy (buffer.byte + (offset - rounded_offset),
|
||
writebuf, partial_len);
|
||
|
||
errno = 0;
|
||
if (arch64)
|
||
rs6000_ptrace64 (PT_WRITE_D, pid,
|
||
rounded_offset, buffer.word, NULL);
|
||
else
|
||
rs6000_ptrace32 (PT_WRITE_D, pid,
|
||
(int *) (uintptr_t) rounded_offset,
|
||
buffer.word, NULL);
|
||
if (errno)
|
||
return TARGET_XFER_EOF;
|
||
}
|
||
|
||
if (readbuf)
|
||
{
|
||
errno = 0;
|
||
if (arch64)
|
||
buffer.word = rs6000_ptrace64 (PT_READ_I, pid,
|
||
rounded_offset, 0, NULL);
|
||
else
|
||
buffer.word = rs6000_ptrace32 (PT_READ_I, pid,
|
||
(int *)(uintptr_t)rounded_offset,
|
||
0, NULL);
|
||
if (errno)
|
||
return TARGET_XFER_EOF;
|
||
|
||
/* Copy appropriate bytes out of the buffer. */
|
||
memcpy (readbuf, buffer.byte + (offset - rounded_offset),
|
||
partial_len);
|
||
}
|
||
|
||
*xfered_len = (ULONGEST) partial_len;
|
||
return TARGET_XFER_OK;
|
||
}
|
||
|
||
default:
|
||
return TARGET_XFER_E_IO;
|
||
}
|
||
}
|
||
|
||
/* Wait for the child specified by PTID to do something. Return the
|
||
process ID of the child, or MINUS_ONE_PTID in case of error; store
|
||
the status in *OURSTATUS. */
|
||
|
||
ptid_t
|
||
rs6000_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
|
||
target_wait_flags options)
|
||
{
|
||
pid_t pid;
|
||
int status, save_errno;
|
||
|
||
while (1)
|
||
{
|
||
set_sigint_trap ();
|
||
|
||
do
|
||
{
|
||
pid = waitpid (ptid.pid (), &status, 0);
|
||
save_errno = errno;
|
||
}
|
||
while (pid == -1 && errno == EINTR);
|
||
|
||
clear_sigint_trap ();
|
||
|
||
if (pid == -1)
|
||
{
|
||
gdb_printf (gdb_stderr,
|
||
_("Child process unexpectedly missing: %s.\n"),
|
||
safe_strerror (save_errno));
|
||
|
||
ourstatus->set_ignore ();
|
||
return minus_one_ptid;
|
||
}
|
||
|
||
/* Ignore terminated detached child processes. */
|
||
if (!WIFSTOPPED (status) && find_inferior_pid (this, pid) == nullptr)
|
||
continue;
|
||
|
||
/* Check for a fork () event. */
|
||
if ((status & 0xff) == W_SFWTED)
|
||
{
|
||
/* Checking whether it is a parent or a child event. */
|
||
|
||
/* If the event is a child we check if there was a parent
|
||
event recorded before. If yes we got the parent child
|
||
relationship. If not we push this child and wait for
|
||
the next fork () event. */
|
||
if (find_inferior_pid (this, pid) == nullptr)
|
||
{
|
||
pid_t parent_pid = has_my_aix_parent_reported (pid);
|
||
if (parent_pid > 0)
|
||
{
|
||
ourstatus->set_forked (ptid_t (pid));
|
||
return ptid_t (parent_pid);
|
||
}
|
||
aix_remember_child (pid);
|
||
}
|
||
|
||
/* If the event is a parent we check if there was a child
|
||
event recorded before. If yes we got the parent child
|
||
relationship. If not we push this parent and wait for
|
||
the next fork () event. */
|
||
else
|
||
{
|
||
pid_t child_pid = has_my_aix_child_reported (pid);
|
||
if (child_pid > 0)
|
||
{
|
||
ourstatus->set_forked (ptid_t (child_pid));
|
||
return ptid_t (pid);
|
||
}
|
||
aix_remember_parent (pid);
|
||
}
|
||
continue;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
/* AIX has a couple of strange returns from wait(). */
|
||
|
||
/* stop after load" status. */
|
||
if (status == 0x57c)
|
||
ourstatus->set_loaded ();
|
||
/* 0x7f is signal 0. */
|
||
else if (status == 0x7f)
|
||
ourstatus->set_spurious ();
|
||
/* A normal waitstatus. Let the usual macros deal with it. */
|
||
else
|
||
*ourstatus = host_status_to_waitstatus (status);
|
||
|
||
return ptid_t (pid);
|
||
}
|
||
|
||
|
||
/* Set the current architecture from the host running GDB. Called when
|
||
starting a child process. */
|
||
|
||
void
|
||
rs6000_nat_target::create_inferior (const char *exec_file,
|
||
const std::string &allargs,
|
||
char **env, int from_tty)
|
||
{
|
||
enum bfd_architecture arch;
|
||
unsigned long mach;
|
||
bfd abfd;
|
||
|
||
inf_ptrace_target::create_inferior (exec_file, allargs, env, from_tty);
|
||
|
||
if (__power_rs ())
|
||
{
|
||
arch = bfd_arch_rs6000;
|
||
mach = bfd_mach_rs6k;
|
||
}
|
||
else
|
||
{
|
||
arch = bfd_arch_powerpc;
|
||
mach = bfd_mach_ppc;
|
||
}
|
||
|
||
/* FIXME: schauer/2002-02-25:
|
||
We don't know if we are executing a 32 or 64 bit executable,
|
||
and have no way to pass the proper word size to rs6000_gdbarch_init.
|
||
So we have to avoid switching to a new architecture, if the architecture
|
||
matches already.
|
||
Blindly calling rs6000_gdbarch_init used to work in older versions of
|
||
GDB, as rs6000_gdbarch_init incorrectly used the previous tdep to
|
||
determine the wordsize. */
|
||
if (current_program_space->exec_bfd ())
|
||
{
|
||
const struct bfd_arch_info *exec_bfd_arch_info;
|
||
|
||
exec_bfd_arch_info
|
||
= bfd_get_arch_info (current_program_space->exec_bfd ());
|
||
if (arch == exec_bfd_arch_info->arch)
|
||
return;
|
||
}
|
||
|
||
bfd_default_set_arch_mach (&abfd, arch, mach);
|
||
|
||
gdbarch_info info;
|
||
info.bfd_arch_info = bfd_get_arch_info (&abfd);
|
||
info.abfd = current_program_space->exec_bfd ();
|
||
|
||
if (!gdbarch_update_p (current_inferior (), info))
|
||
internal_error (_("rs6000_create_inferior: failed "
|
||
"to select architecture"));
|
||
}
|
||
|
||
|
||
/* Shared Object support. */
|
||
|
||
/* Return the LdInfo data for the given process. Raises an error
|
||
if the data could not be obtained. */
|
||
|
||
static gdb::byte_vector
|
||
rs6000_ptrace_ldinfo (ptid_t ptid)
|
||
{
|
||
const int pid = ptid.pid ();
|
||
gdb::byte_vector ldi (1024);
|
||
int rc = -1;
|
||
|
||
while (1)
|
||
{
|
||
if (ARCH64 ())
|
||
rc = rs6000_ptrace64 (PT_LDINFO, pid, (unsigned long) ldi.data (),
|
||
ldi.size (), NULL);
|
||
else
|
||
rc = rs6000_ptrace32 (PT_LDINFO, pid, (int *) ldi.data (),
|
||
ldi.size (), NULL);
|
||
|
||
if (rc != -1)
|
||
break; /* Success, we got the entire ld_info data. */
|
||
|
||
if (errno != ENOMEM)
|
||
perror_with_name (_("ptrace ldinfo"));
|
||
|
||
/* ldi is not big enough. Double it and try again. */
|
||
ldi.resize (ldi.size () * 2);
|
||
}
|
||
|
||
return ldi;
|
||
}
|
||
|
||
/* Implement the to_xfer_partial target_ops method for
|
||
TARGET_OBJECT_LIBRARIES_AIX objects. */
|
||
|
||
enum target_xfer_status
|
||
rs6000_nat_target::xfer_shared_libraries
|
||
(enum target_object object,
|
||
const char *annex, gdb_byte *readbuf, const gdb_byte *writebuf,
|
||
ULONGEST offset, ULONGEST len, ULONGEST *xfered_len)
|
||
{
|
||
ULONGEST result;
|
||
|
||
/* This function assumes that it is being run with a live process.
|
||
Core files are handled via gdbarch. */
|
||
gdb_assert (target_has_execution ());
|
||
|
||
if (writebuf)
|
||
return TARGET_XFER_E_IO;
|
||
|
||
gdb::byte_vector ldi_buf = rs6000_ptrace_ldinfo (inferior_ptid);
|
||
result = rs6000_aix_ld_info_to_xml (current_inferior ()->arch (),
|
||
ldi_buf.data (),
|
||
readbuf, offset, len, 1);
|
||
|
||
if (result == 0)
|
||
return TARGET_XFER_EOF;
|
||
else
|
||
{
|
||
*xfered_len = result;
|
||
return TARGET_XFER_OK;
|
||
}
|
||
}
|
||
|
||
void _initialize_rs6000_nat ();
|
||
void
|
||
_initialize_rs6000_nat ()
|
||
{
|
||
add_inf_child_target (&the_rs6000_nat_target);
|
||
}
|