binutils-gdb/gdb/rs6000-aix-nat.c
Simon Marchi 91f378ddd0 gdb: pass inferior to gdbarch_update_p
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
2024-08-12 11:10:02 -04:00

1074 lines
28 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 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);
}