#include "defs.h"
#include "gdb_assert.h"
#include "gdbthread.h"
#include "target.h"
#include "inferior.h"
#include "regcache.h"
#include "gdbcmd.h"
#include "ppc-tdep.h"
#include "gdb_string.h"
#include <procinfo.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/reg.h>
#include <sched.h>
#include <sys/pthdebug.h>
static int debug_aix_thread;
#ifndef PTHDB_VERSION_3
#define pthdb_tid_t tid_t
#endif
#define PD_TID(ptid) (pd_active && ptid_get_tid (ptid) != 0)
#define BUILD_THREAD(TID, PID) ptid_build (PID, 0, TID)
#define BUILD_LWP(LWP, PID) MERGEPID (PID, LWP)
#define PD_USER 1
#define PDC_SUCCESS PTHDB_SUCCESS
#define PDC_FAILURE PTHDB_CALLBACK
struct private_thread_info {
pthdb_pthread_t pdtid;
pthdb_tid_t tid;
};
struct pd_thread {
pthdb_pthread_t pdtid;
pthread_t pthid;
pthdb_tid_t tid;
};
static struct target_ops aix_thread_ops;
static struct target_ops base_target;
static CORE_ADDR pd_brk_addr;
static int pd_able = 0;
static int pd_active = 0;
static int arch64;
static void (*target_new_objfile_chain)(struct objfile *);
static int pdc_symbol_addrs (pthdb_user_t, pthdb_symbol_t *, int);
static int pdc_read_data (pthdb_user_t, void *, pthdb_addr_t, size_t);
static int pdc_write_data (pthdb_user_t, void *, pthdb_addr_t, size_t);
static int pdc_read_regs (pthdb_user_t user, pthdb_tid_t tid,
unsigned long long flags,
pthdb_context_t *context);
static int pdc_write_regs (pthdb_user_t user, pthdb_tid_t tid,
unsigned long long flags,
pthdb_context_t *context);
static int pdc_alloc (pthdb_user_t, size_t, void **);
static int pdc_realloc (pthdb_user_t, void *, size_t, void **);
static int pdc_dealloc (pthdb_user_t, void *);
static pthdb_callbacks_t pd_callbacks = {
pdc_symbol_addrs,
pdc_read_data,
pdc_write_data,
pdc_read_regs,
pdc_write_regs,
pdc_alloc,
pdc_realloc,
pdc_dealloc,
NULL
};
static pthdb_session_t pd_session;
static char *
pd_status2str (int status)
{
switch (status)
{
case PTHDB_SUCCESS: return "SUCCESS";
case PTHDB_NOSYS: return "NOSYS";
case PTHDB_NOTSUP: return "NOTSUP";
case PTHDB_BAD_VERSION: return "BAD_VERSION";
case PTHDB_BAD_USER: return "BAD_USER";
case PTHDB_BAD_SESSION: return "BAD_SESSION";
case PTHDB_BAD_MODE: return "BAD_MODE";
case PTHDB_BAD_FLAGS: return "BAD_FLAGS";
case PTHDB_BAD_CALLBACK: return "BAD_CALLBACK";
case PTHDB_BAD_POINTER: return "BAD_POINTER";
case PTHDB_BAD_CMD: return "BAD_CMD";
case PTHDB_BAD_PTHREAD: return "BAD_PTHREAD";
case PTHDB_BAD_ATTR: return "BAD_ATTR";
case PTHDB_BAD_MUTEX: return "BAD_MUTEX";
case PTHDB_BAD_MUTEXATTR: return "BAD_MUTEXATTR";
case PTHDB_BAD_COND: return "BAD_COND";
case PTHDB_BAD_CONDATTR: return "BAD_CONDATTR";
case PTHDB_BAD_RWLOCK: return "BAD_RWLOCK";
case PTHDB_BAD_RWLOCKATTR: return "BAD_RWLOCKATTR";
case PTHDB_BAD_KEY: return "BAD_KEY";
case PTHDB_BAD_PTID: return "BAD_PTID";
case PTHDB_BAD_TID: return "BAD_TID";
case PTHDB_CALLBACK: return "CALLBACK";
case PTHDB_CONTEXT: return "CONTEXT";
case PTHDB_HELD: return "HELD";
case PTHDB_NOT_HELD: return "NOT_HELD";
case PTHDB_MEMORY: return "MEMORY";
case PTHDB_NOT_PTHREADED: return "NOT_PTHREADED";
case PTHDB_SYMBOL: return "SYMBOL";
case PTHDB_NOT_AVAIL: return "NOT_AVAIL";
case PTHDB_INTERNAL: return "INTERNAL";
default: return "UNKNOWN";
}
}
static int
ptrace_check (int req, int id, int ret)
{
if (ret == 0 && !errno)
return 1;
switch (req)
{
case PTT_READ_GPRS:
case PTT_READ_FPRS:
case PTT_READ_SPRS:
if (ret == -1 && errno == EPERM)
{
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog,
"ptrace (%d, %d) = %d (errno = %d)\n",
req, id, ret, errno);
return ret == -1 ? 0 : 1;
}
break;
}
error (_("aix-thread: ptrace (%d, %d) returned %d (errno = %d %s)"),
req, id, ret, errno, safe_strerror (errno));
return 0;
}
static int
ptrace64aix (int req, int id, long long addr, int data, int *buf)
{
errno = 0;
return ptrace_check (req, id, ptracex (req, id, addr, data, buf));
}
static int
ptrace32 (int req, int id, int *addr, int data, int *buf)
{
errno = 0;
return ptrace_check (req, id,
ptrace (req, id, (int *) addr, data, buf));
}
static void
pid_to_prc (ptid_t *ptidp)
{
ptid_t ptid;
ptid = *ptidp;
if (PD_TID (ptid))
*ptidp = pid_to_ptid (PIDGET (ptid));
}
static int
pdc_symbol_addrs (pthdb_user_t user, pthdb_symbol_t *symbols, int count)
{
struct minimal_symbol *ms;
int i;
char *name;
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog,
"pdc_symbol_addrs (user = %ld, symbols = 0x%lx, count = %d)\n",
user, (long) symbols, count);
for (i = 0; i < count; i++)
{
name = symbols[i].name;
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog,
" symbols[%d].name = \"%s\"\n", i, name);
if (!*name)
symbols[i].addr = 0;
else
{
if (!(ms = lookup_minimal_symbol (name, NULL, NULL)))
{
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog, " returning PDC_FAILURE\n");
return PDC_FAILURE;
}
symbols[i].addr = SYMBOL_VALUE_ADDRESS (ms);
}
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog, " symbols[%d].addr = %s\n",
i, hex_string (symbols[i].addr));
}
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog, " returning PDC_SUCCESS\n");
return PDC_SUCCESS;
}
static int
pdc_read_regs (pthdb_user_t user,
pthdb_tid_t tid,
unsigned long long flags,
pthdb_context_t *context)
{
uint64_t gprs64[ppc_num_gprs];
uint32_t gprs32[ppc_num_gprs];
double fprs[ppc_num_fprs];
struct ptxsprs sprs64;
struct ptsprs sprs32;
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog, "pdc_read_regs tid=%d flags=%s\n",
(int) tid, hex_string (flags));
if (flags & PTHDB_FLAG_GPRS)
{
if (arch64)
{
if (!ptrace64aix (PTT_READ_GPRS, tid,
(unsigned long) gprs64, 0, NULL))
memset (gprs64, 0, sizeof (gprs64));
memcpy (context->gpr, gprs64, sizeof(gprs64));
}
else
{
if (!ptrace32 (PTT_READ_GPRS, tid, gprs32, 0, NULL))
memset (gprs32, 0, sizeof (gprs32));
memcpy (context->gpr, gprs32, sizeof(gprs32));
}
}
if (flags & PTHDB_FLAG_FPRS)
{
if (!ptrace32 (PTT_READ_FPRS, tid, (int *) fprs, 0, NULL))
memset (fprs, 0, sizeof (fprs));
memcpy (context->fpr, fprs, sizeof(fprs));
}
if (flags & PTHDB_FLAG_SPRS)
{
if (arch64)
{
if (!ptrace64aix (PTT_READ_SPRS, tid,
(unsigned long) &sprs64, 0, NULL))
memset (&sprs64, 0, sizeof (sprs64));
memcpy (&context->msr, &sprs64, sizeof(sprs64));
}
else
{
if (!ptrace32 (PTT_READ_SPRS, tid, (int *) &sprs32, 0, NULL))
memset (&sprs32, 0, sizeof (sprs32));
memcpy (&context->msr, &sprs32, sizeof(sprs32));
}
}
return 0;
}
static int
pdc_write_regs (pthdb_user_t user,
pthdb_tid_t tid,
unsigned long long flags,
pthdb_context_t *context)
{
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog, "pdc_write_regs tid=%d flags=%s\n",
(int) tid, hex_string (flags));
if (flags & PTHDB_FLAG_GPRS)
{
if (arch64)
ptrace64aix (PTT_WRITE_GPRS, tid,
(unsigned long) context->gpr, 0, NULL);
else
ptrace32 (PTT_WRITE_GPRS, tid, (int *) context->gpr, 0, NULL);
}
if (flags & PTHDB_FLAG_FPRS)
{
ptrace32 (PTT_WRITE_FPRS, tid, (int *) context->fpr, 0, NULL);
}
if (flags & PTHDB_FLAG_SPRS)
{
if (arch64)
{
ptrace64aix (PTT_WRITE_SPRS, tid,
(unsigned long) &context->msr, 0, NULL);
}
else
{
ptrace32 (PTT_WRITE_SPRS, tid, (int *) &context->msr, 0, NULL);
}
}
return 0;
}
static int
pdc_read_data (pthdb_user_t user, void *buf,
pthdb_addr_t addr, size_t len)
{
int status, ret;
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog,
"pdc_read_data (user = %ld, buf = 0x%lx, addr = %s, len = %ld)\n",
user, (long) buf, hex_string (addr), len);
status = target_read_memory (addr, buf, len);
ret = status == 0 ? PDC_SUCCESS : PDC_FAILURE;
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog, " status=%d, returning %s\n",
status, pd_status2str (ret));
return ret;
}
static int
pdc_write_data (pthdb_user_t user, void *buf,
pthdb_addr_t addr, size_t len)
{
int status, ret;
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog,
"pdc_write_data (user = %ld, buf = 0x%lx, addr = %s, len = %ld)\n",
user, (long) buf, hex_string (addr), len);
status = target_write_memory (addr, buf, len);
ret = status == 0 ? PDC_SUCCESS : PDC_FAILURE;
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog, " status=%d, returning %s\n", status,
pd_status2str (ret));
return ret;
}
static int
pdc_alloc (pthdb_user_t user, size_t len, void **bufp)
{
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog,
"pdc_alloc (user = %ld, len = %ld, bufp = 0x%lx)\n",
user, len, (long) bufp);
*bufp = xmalloc (len);
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog,
" malloc returned 0x%lx\n", (long) *bufp);
return *bufp ? PDC_SUCCESS : PDC_FAILURE;
}
static int
pdc_realloc (pthdb_user_t user, void *buf, size_t len, void **bufp)
{
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog,
"pdc_realloc (user = %ld, buf = 0x%lx, len = %ld, bufp = 0x%lx)\n",
user, (long) buf, len, (long) bufp);
*bufp = xrealloc (buf, len);
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog,
" realloc returned 0x%lx\n", (long) *bufp);
return *bufp ? PDC_SUCCESS : PDC_FAILURE;
}
static int
pdc_dealloc (pthdb_user_t user, void *buf)
{
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog,
"pdc_free (user = %ld, buf = 0x%lx)\n", user,
(long) buf);
xfree (buf);
return PDC_SUCCESS;
}
static char *
state2str (pthdb_state_t state)
{
switch (state)
{
case PST_IDLE:
return _("idle");
case PST_RUN:
return _("running");
case PST_SLEEP:
return _("sleeping");
case PST_READY:
return _("ready");
case PST_TERM:
return _("finished");
default:
return _("unknown");
}
}
static int
pcmp (const void *p1v, const void *p2v)
{
struct pd_thread *p1 = (struct pd_thread *) p1v;
struct pd_thread *p2 = (struct pd_thread *) p2v;
return p1->pthid < p2->pthid ? -1 : p1->pthid > p2->pthid;
}
static int
giter_count (struct thread_info *thread, void *countp)
{
(*(int *) countp)++;
return 0;
}
static int
giter_accum (struct thread_info *thread, void *bufp)
{
**(struct thread_info ***) bufp = thread;
(*(struct thread_info ***) bufp)++;
return 0;
}
static int
ptid_cmp (ptid_t ptid1, ptid_t ptid2)
{
int pid1, pid2;
if (ptid_get_pid (ptid1) < ptid_get_pid (ptid2))
return -1;
else if (ptid_get_pid (ptid1) > ptid_get_pid (ptid2))
return 1;
else if (ptid_get_tid (ptid1) < ptid_get_tid (ptid2))
return -1;
else if (ptid_get_tid (ptid1) > ptid_get_tid (ptid2))
return 1;
else if (ptid_get_lwp (ptid1) < ptid_get_lwp (ptid2))
return -1;
else if (ptid_get_lwp (ptid1) > ptid_get_lwp (ptid2))
return 1;
else
return 0;
}
static int
gcmp (const void *t1v, const void *t2v)
{
struct thread_info *t1 = *(struct thread_info **) t1v;
struct thread_info *t2 = *(struct thread_info **) t2v;
return ptid_cmp (t1->ptid, t2->ptid);
}
static pthdb_tid_t
get_signaled_thread (void)
{
struct thrdsinfo64 thrinf;
pthdb_tid_t ktid = 0;
int result = 0;
extern int getthrds (pid_t, struct thrdsinfo64 *,
int, pthdb_tid_t *, int);
while (1)
{
if (getthrds (PIDGET (inferior_ptid), &thrinf,
sizeof (thrinf), &ktid, 1) != 1)
break;
if (thrinf.ti_cursig == SIGTRAP)
return thrinf.ti_tid;
}
return 0;
}
static void
sync_threadlists (void)
{
int cmd, status, infpid;
int pcount, psize, pi, gcount, gi;
struct pd_thread *pbuf;
struct thread_info **gbuf, **g, *thread;
pthdb_pthread_t pdtid;
pthread_t pthid;
pthdb_tid_t tid;
pcount = 0;
psize = 1;
pbuf = (struct pd_thread *) xmalloc (psize * sizeof *pbuf);
for (cmd = PTHDB_LIST_FIRST;; cmd = PTHDB_LIST_NEXT)
{
status = pthdb_pthread (pd_session, &pdtid, cmd);
if (status != PTHDB_SUCCESS || pdtid == PTHDB_INVALID_PTHREAD)
break;
status = pthdb_pthread_ptid (pd_session, pdtid, &pthid);
if (status != PTHDB_SUCCESS || pthid == PTHDB_INVALID_PTID)
continue;
if (pcount == psize)
{
psize *= 2;
pbuf = (struct pd_thread *) xrealloc (pbuf,
psize * sizeof *pbuf);
}
pbuf[pcount].pdtid = pdtid;
pbuf[pcount].pthid = pthid;
pcount++;
}
for (pi = 0; pi < pcount; pi++)
{
status = pthdb_pthread_tid (pd_session, pbuf[pi].pdtid, &tid);
if (status != PTHDB_SUCCESS)
tid = PTHDB_INVALID_TID;
pbuf[pi].tid = tid;
}
qsort (pbuf, pcount, sizeof *pbuf, pcmp);
gcount = 0;
iterate_over_threads (giter_count, &gcount);
g = gbuf = (struct thread_info **) xmalloc (gcount * sizeof *gbuf);
iterate_over_threads (giter_accum, &g);
qsort (gbuf, gcount, sizeof *gbuf, gcmp);
infpid = PIDGET (inferior_ptid);
for (pi = gi = 0; pi < pcount || gi < gcount;)
{
if (pi == pcount)
{
delete_thread (gbuf[gi]->ptid);
gi++;
}
else if (gi == gcount)
{
thread = add_thread (BUILD_THREAD (pbuf[pi].pthid, infpid));
thread->private = xmalloc (sizeof (struct private_thread_info));
thread->private->pdtid = pbuf[pi].pdtid;
thread->private->tid = pbuf[pi].tid;
pi++;
}
else
{
ptid_t pptid, gptid;
int cmp_result;
pptid = BUILD_THREAD (pbuf[pi].pthid, infpid);
gptid = gbuf[gi]->ptid;
pdtid = pbuf[pi].pdtid;
tid = pbuf[pi].tid;
cmp_result = ptid_cmp (pptid, gptid);
if (cmp_result == 0)
{
gbuf[gi]->private->pdtid = pdtid;
gbuf[gi]->private->tid = tid;
pi++;
gi++;
}
else if (cmp_result > 0)
{
delete_thread (gptid);
gi++;
}
else
{
thread = add_thread (pptid);
thread->private = xmalloc (sizeof (struct private_thread_info));
thread->private->pdtid = pdtid;
thread->private->tid = tid;
pi++;
}
}
}
xfree (pbuf);
xfree (gbuf);
}
static int
iter_tid (struct thread_info *thread, void *tidp)
{
const pthdb_tid_t tid = *(pthdb_tid_t *)tidp;
return (thread->private->tid == tid);
}
static ptid_t
pd_update (int set_infpid)
{
int status;
ptid_t ptid;
pthdb_tid_t tid;
struct thread_info *thread = NULL;
if (!pd_active)
return inferior_ptid;
status = pthdb_session_update (pd_session);
if (status != PTHDB_SUCCESS)
return inferior_ptid;
sync_threadlists ();
tid = get_signaled_thread ();
if (tid != 0)
thread = iterate_over_threads (iter_tid, &tid);
if (!thread)
ptid = inferior_ptid;
else
{
ptid = thread->ptid;
if (set_infpid)
inferior_ptid = ptid;
}
return ptid;
}
static ptid_t
pd_activate (int set_infpid)
{
int status;
status = pthdb_session_init (PD_USER, arch64 ? PEM_64BIT : PEM_32BIT,
PTHDB_FLAG_REGS, &pd_callbacks,
&pd_session);
if (status != PTHDB_SUCCESS)
{
return inferior_ptid;
}
pd_active = 1;
return pd_update (set_infpid);
}
static void
pd_deactivate (void)
{
if (!pd_active)
return;
pthdb_session_destroy (pd_session);
pid_to_prc (&inferior_ptid);
pd_active = 0;
}
static void
pd_enable (void)
{
int status;
char *stub_name;
struct minimal_symbol *ms;
if (pd_able)
return;
arch64 = register_size (current_gdbarch, 0) == 8;
stub_name = NULL;
status = pthdb_session_pthreaded (PD_USER, PTHDB_FLAG_REGS,
&pd_callbacks, &stub_name);
if ((status != PTHDB_SUCCESS &&
status != PTHDB_NOT_PTHREADED) || !stub_name)
return;
if (!(ms = lookup_minimal_symbol (stub_name, NULL, NULL)))
return;
pd_brk_addr = SYMBOL_VALUE_ADDRESS (ms);
if (!create_thread_event_breakpoint (pd_brk_addr))
return;
base_target = current_target;
push_target (&aix_thread_ops);
pd_able = 1;
pd_activate (1);
}
static void
pd_disable (void)
{
if (!pd_able)
return;
if (pd_active)
pd_deactivate ();
pd_able = 0;
unpush_target (&aix_thread_ops);
}
static void
new_objfile (struct objfile *objfile)
{
if (objfile)
pd_enable ();
else
pd_disable ();
if (target_new_objfile_chain)
target_new_objfile_chain (objfile);
}
static void
aix_thread_attach (char *args, int from_tty)
{
base_target.to_attach (args, from_tty);
pd_activate (1);
}
static void
aix_thread_detach (char *args, int from_tty)
{
pd_disable ();
base_target.to_detach (args, from_tty);
}
static void
aix_thread_resume (ptid_t ptid, int step, enum target_signal sig)
{
struct thread_info *thread;
pthdb_tid_t tid[2];
if (!PD_TID (ptid))
{
struct cleanup *cleanup = save_inferior_ptid ();
inferior_ptid = pid_to_ptid (PIDGET (inferior_ptid));
base_target.to_resume (ptid, step, sig);
do_cleanups (cleanup);
}
else
{
thread = find_thread_pid (ptid);
if (!thread)
error (_("aix-thread resume: unknown pthread %ld"),
TIDGET (ptid));
tid[0] = thread->private->tid;
if (tid[0] == PTHDB_INVALID_TID)
error (_("aix-thread resume: no tid for pthread %ld"),
TIDGET (ptid));
tid[1] = 0;
if (arch64)
ptrace64aix (PTT_CONTINUE, tid[0], 1,
target_signal_to_host (sig), (int *) tid);
else
ptrace32 (PTT_CONTINUE, tid[0], (int *) 1,
target_signal_to_host (sig), (int *) tid);
}
}
static ptid_t
aix_thread_wait (ptid_t ptid, struct target_waitstatus *status)
{
struct cleanup *cleanup = save_inferior_ptid ();
pid_to_prc (&ptid);
inferior_ptid = pid_to_ptid (PIDGET (inferior_ptid));
ptid = base_target.to_wait (ptid, status);
do_cleanups (cleanup);
if (PIDGET (ptid) == -1)
return pid_to_ptid (-1);
if (!pd_active && status->kind == TARGET_WAITKIND_STOPPED &&
status->value.sig == TARGET_SIGNAL_TRAP &&
read_pc_pid (ptid) - DECR_PC_AFTER_BREAK == pd_brk_addr)
return pd_activate (0);
return pd_update (0);
}
static void
supply_gprs64 (uint64_t *vals)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
int regno;
for (regno = 0; regno < ppc_num_gprs; regno++)
regcache_raw_supply (current_regcache, tdep->ppc_gp0_regnum + regno,
(char *) (vals + regno));
}
static void
supply_reg32 (int regno, uint32_t val)
{
regcache_raw_supply (current_regcache, regno, (char *) &val);
}
static void
supply_fprs (double *vals)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
int regno;
gdb_assert (ppc_floating_point_unit_p (current_gdbarch));
for (regno = 0; regno < ppc_num_fprs; regno++)
regcache_raw_supply (current_regcache, regno + tdep->ppc_fp0_regnum,
(char *) (vals + regno));
}
static int
special_register_p (int regno)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
return regno == PC_REGNUM
|| regno == tdep->ppc_ps_regnum
|| regno == tdep->ppc_cr_regnum
|| regno == tdep->ppc_lr_regnum
|| regno == tdep->ppc_ctr_regnum
|| regno == tdep->ppc_xer_regnum
|| (tdep->ppc_fpscr_regnum >= 0 && regno == tdep->ppc_fpscr_regnum)
|| (tdep->ppc_mq_regnum >= 0 && regno == tdep->ppc_mq_regnum);
}
static void
supply_sprs64 (uint64_t iar, uint64_t msr, uint32_t cr,
uint64_t lr, uint64_t ctr, uint32_t xer,
uint32_t fpscr)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
regcache_raw_supply (current_regcache, PC_REGNUM, (char *) &iar);
regcache_raw_supply (current_regcache, tdep->ppc_ps_regnum, (char *) &msr);
regcache_raw_supply (current_regcache, tdep->ppc_cr_regnum, (char *) &cr);
regcache_raw_supply (current_regcache, tdep->ppc_lr_regnum, (char *) &lr);
regcache_raw_supply (current_regcache, tdep->ppc_ctr_regnum, (char *) &ctr);
regcache_raw_supply (current_regcache, tdep->ppc_xer_regnum, (char *) &xer);
if (tdep->ppc_fpscr_regnum >= 0)
regcache_raw_supply (current_regcache, tdep->ppc_fpscr_regnum,
(char *) &fpscr);
}
static void
supply_sprs32 (uint32_t iar, uint32_t msr, uint32_t cr,
uint32_t lr, uint32_t ctr, uint32_t xer,
uint32_t fpscr)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
regcache_raw_supply (current_regcache, PC_REGNUM, (char *) &iar);
regcache_raw_supply (current_regcache, tdep->ppc_ps_regnum, (char *) &msr);
regcache_raw_supply (current_regcache, tdep->ppc_cr_regnum, (char *) &cr);
regcache_raw_supply (current_regcache, tdep->ppc_lr_regnum, (char *) &lr);
regcache_raw_supply (current_regcache, tdep->ppc_ctr_regnum, (char *) &ctr);
regcache_raw_supply (current_regcache, tdep->ppc_xer_regnum, (char *) &xer);
if (tdep->ppc_fpscr_regnum >= 0)
regcache_raw_supply (current_regcache, tdep->ppc_fpscr_regnum,
(char *) &fpscr);
}
static void
fetch_regs_user_thread (pthdb_pthread_t pdtid)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
int status, i;
pthdb_context_t ctx;
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog,
"fetch_regs_user_thread %lx\n", (long) pdtid);
status = pthdb_pthread_context (pd_session, pdtid, &ctx);
if (status != PTHDB_SUCCESS)
error (_("aix-thread: fetch_registers: pthdb_pthread_context returned %s"),
pd_status2str (status));
if (arch64)
supply_gprs64 (ctx.gpr);
else
for (i = 0; i < ppc_num_gprs; i++)
supply_reg32 (tdep->ppc_gp0_regnum + i, ctx.gpr[i]);
if (ppc_floating_point_unit_p (current_gdbarch))
supply_fprs (ctx.fpr);
if (arch64)
supply_sprs64 (ctx.iar, ctx.msr, ctx.cr, ctx.lr, ctx.ctr, ctx.xer,
ctx.fpscr);
else
supply_sprs32 (ctx.iar, ctx.msr, ctx.cr, ctx.lr, ctx.ctr, ctx.xer,
ctx.fpscr);
}
static void
fetch_regs_kernel_thread (int regno, pthdb_tid_t tid)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
uint64_t gprs64[ppc_num_gprs];
uint32_t gprs32[ppc_num_gprs];
double fprs[ppc_num_fprs];
struct ptxsprs sprs64;
struct ptsprs sprs32;
int i;
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog,
"fetch_regs_kernel_thread tid=%lx regno=%d arch64=%d\n",
(long) tid, regno, arch64);
if (regno == -1
|| (tdep->ppc_gp0_regnum <= regno
&& regno < tdep->ppc_gp0_regnum + ppc_num_gprs))
{
if (arch64)
{
if (!ptrace64aix (PTT_READ_GPRS, tid,
(unsigned long) gprs64, 0, NULL))
memset (gprs64, 0, sizeof (gprs64));
supply_gprs64 (gprs64);
}
else
{
if (!ptrace32 (PTT_READ_GPRS, tid, gprs32, 0, NULL))
memset (gprs32, 0, sizeof (gprs32));
for (i = 0; i < ppc_num_gprs; i++)
supply_reg32 (tdep->ppc_gp0_regnum + i, gprs32[i]);
}
}
if (ppc_floating_point_unit_p (current_gdbarch)
&& (regno == -1
|| (regno >= tdep->ppc_fp0_regnum
&& regno < tdep->ppc_fp0_regnum + ppc_num_fprs)))
{
if (!ptrace32 (PTT_READ_FPRS, tid, (int *) fprs, 0, NULL))
memset (fprs, 0, sizeof (fprs));
supply_fprs (fprs);
}
if (regno == -1 || special_register_p (regno))
{
if (arch64)
{
if (!ptrace64aix (PTT_READ_SPRS, tid,
(unsigned long) &sprs64, 0, NULL))
memset (&sprs64, 0, sizeof (sprs64));
supply_sprs64 (sprs64.pt_iar, sprs64.pt_msr, sprs64.pt_cr,
sprs64.pt_lr, sprs64.pt_ctr, sprs64.pt_xer,
sprs64.pt_fpscr);
}
else
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
if (!ptrace32 (PTT_READ_SPRS, tid, (int *) &sprs32, 0, NULL))
memset (&sprs32, 0, sizeof (sprs32));
supply_sprs32 (sprs32.pt_iar, sprs32.pt_msr, sprs32.pt_cr,
sprs32.pt_lr, sprs32.pt_ctr, sprs32.pt_xer,
sprs32.pt_fpscr);
if (tdep->ppc_mq_regnum >= 0)
regcache_raw_supply (current_regcache, tdep->ppc_mq_regnum,
(char *) &sprs32.pt_mq);
}
}
}
static void
aix_thread_fetch_registers (int regno)
{
struct thread_info *thread;
pthdb_tid_t tid;
if (!PD_TID (inferior_ptid))
base_target.to_fetch_registers (regno);
else
{
thread = find_thread_pid (inferior_ptid);
tid = thread->private->tid;
if (tid == PTHDB_INVALID_TID)
fetch_regs_user_thread (thread->private->pdtid);
else
fetch_regs_kernel_thread (regno, tid);
}
}
static void
fill_gprs64 (uint64_t *vals)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
int regno;
for (regno = 0; regno < ppc_num_gprs; regno++)
if (register_cached (tdep->ppc_gp0_regnum + regno))
regcache_raw_collect (current_regcache, tdep->ppc_gp0_regnum + regno,
vals + regno);
}
static void
fill_gprs32 (uint32_t *vals)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
int regno;
for (regno = 0; regno < ppc_num_gprs; regno++)
if (register_cached (tdep->ppc_gp0_regnum + regno))
regcache_raw_collect (current_regcache, tdep->ppc_gp0_regnum + regno,
vals + regno);
}
static void
fill_fprs (double *vals)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
int regno;
gdb_assert (ppc_floating_point_unit_p (current_gdbarch));
for (regno = tdep->ppc_fp0_regnum;
regno < tdep->ppc_fp0_regnum + ppc_num_fprs;
regno++)
if (register_cached (regno))
regcache_raw_collect (current_regcache, regno, vals + regno);
}
static void
fill_sprs64 (uint64_t *iar, uint64_t *msr, uint32_t *cr,
uint64_t *lr, uint64_t *ctr, uint32_t *xer,
uint32_t *fpscr)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
gdb_assert (sizeof (*iar) == register_size (current_gdbarch, PC_REGNUM));
if (register_cached (PC_REGNUM))
regcache_raw_collect (current_regcache, PC_REGNUM, iar);
if (register_cached (tdep->ppc_ps_regnum))
regcache_raw_collect (current_regcache, tdep->ppc_ps_regnum, msr);
if (register_cached (tdep->ppc_cr_regnum))
regcache_raw_collect (current_regcache, tdep->ppc_cr_regnum, cr);
if (register_cached (tdep->ppc_lr_regnum))
regcache_raw_collect (current_regcache, tdep->ppc_lr_regnum, lr);
if (register_cached (tdep->ppc_ctr_regnum))
regcache_raw_collect (current_regcache, tdep->ppc_ctr_regnum, ctr);
if (register_cached (tdep->ppc_xer_regnum))
regcache_raw_collect (current_regcache, tdep->ppc_xer_regnum, xer);
if (tdep->ppc_fpscr_regnum >= 0
&& register_cached (tdep->ppc_fpscr_regnum))
regcache_raw_collect (current_regcache, tdep->ppc_fpscr_regnum, fpscr);
}
static void
fill_sprs32 (unsigned long *iar, unsigned long *msr, unsigned long *cr,
unsigned long *lr, unsigned long *ctr, unsigned long *xer,
unsigned long *fpscr)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
gdb_assert (sizeof (*iar) == register_size (current_gdbarch, PC_REGNUM));
if (register_cached (PC_REGNUM))
regcache_raw_collect (current_regcache, PC_REGNUM, iar);
if (register_cached (tdep->ppc_ps_regnum))
regcache_raw_collect (current_regcache, tdep->ppc_ps_regnum, msr);
if (register_cached (tdep->ppc_cr_regnum))
regcache_raw_collect (current_regcache, tdep->ppc_cr_regnum, cr);
if (register_cached (tdep->ppc_lr_regnum))
regcache_raw_collect (current_regcache, tdep->ppc_lr_regnum, lr);
if (register_cached (tdep->ppc_ctr_regnum))
regcache_raw_collect (current_regcache, tdep->ppc_ctr_regnum, ctr);
if (register_cached (tdep->ppc_xer_regnum))
regcache_raw_collect (current_regcache, tdep->ppc_xer_regnum, xer);
if (tdep->ppc_fpscr_regnum >= 0
&& register_cached (tdep->ppc_fpscr_regnum))
regcache_raw_collect (current_regcache, tdep->ppc_fpscr_regnum, fpscr);
}
static void
store_regs_user_thread (pthdb_pthread_t pdtid)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
int status, i;
pthdb_context_t ctx;
uint32_t int32;
uint64_t int64;
double dbl;
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog,
"store_regs_user_thread %lx\n", (long) pdtid);
status = pthdb_pthread_context (pd_session, pdtid, &ctx);
if (status != PTHDB_SUCCESS)
error (_("aix-thread: store_registers: pthdb_pthread_context returned %s"),
pd_status2str (status));
for (i = 0; i < ppc_num_gprs; i++)
if (register_cached (tdep->ppc_gp0_regnum + i))
{
if (arch64)
{
regcache_raw_collect (current_regcache, tdep->ppc_gp0_regnum + i,
(void *) &int64);
ctx.gpr[i] = int64;
}
else
{
regcache_raw_collect (current_regcache, tdep->ppc_gp0_regnum + i,
(void *) &int32);
ctx.gpr[i] = int32;
}
}
if (ppc_floating_point_unit_p (current_gdbarch))
fill_fprs (ctx.fpr);
if (arch64)
{
fill_sprs64 (&ctx.iar, &ctx.msr, &ctx.cr, &ctx.lr, &ctx.ctr, &ctx.xer,
&ctx.fpscr);
}
else
{
unsigned long tmp_iar, tmp_msr, tmp_cr, tmp_lr, tmp_ctr, tmp_xer,
tmp_fpscr;
fill_sprs32 (&tmp_iar, &tmp_msr, &tmp_cr, &tmp_lr, &tmp_ctr, &tmp_xer,
&tmp_fpscr);
if (register_cached (PC_REGNUM))
ctx.iar = tmp_iar;
if (register_cached (tdep->ppc_ps_regnum))
ctx.msr = tmp_msr;
if (register_cached (tdep->ppc_cr_regnum))
ctx.cr = tmp_cr;
if (register_cached (tdep->ppc_lr_regnum))
ctx.lr = tmp_lr;
if (register_cached (tdep->ppc_ctr_regnum))
ctx.ctr = tmp_ctr;
if (register_cached (tdep->ppc_xer_regnum))
ctx.xer = tmp_xer;
if (register_cached (tdep->ppc_xer_regnum))
ctx.fpscr = tmp_fpscr;
}
status = pthdb_pthread_setcontext (pd_session, pdtid, &ctx);
if (status != PTHDB_SUCCESS)
error (_("aix-thread: store_registers: pthdb_pthread_setcontext returned %s"),
pd_status2str (status));
}
static void
store_regs_kernel_thread (int regno, pthdb_tid_t tid)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
uint64_t gprs64[ppc_num_gprs];
uint32_t gprs32[ppc_num_gprs];
double fprs[ppc_num_fprs];
struct ptxsprs sprs64;
struct ptsprs sprs32;
int i;
if (debug_aix_thread)
fprintf_unfiltered (gdb_stdlog,
"store_regs_kernel_thread tid=%lx regno=%d\n",
(long) tid, regno);
if (regno == -1
|| (tdep->ppc_gp0_regnum <= regno
&& regno < tdep->ppc_gp0_regnum + ppc_num_fprs))
{
if (arch64)
{
ptrace64aix (PTT_READ_GPRS, tid, (unsigned long) gprs64, 0, NULL);
fill_gprs64 (gprs64);
ptrace64aix (PTT_WRITE_GPRS, tid, (unsigned long) gprs64, 0, NULL);
}
else
{
ptrace32 (PTT_READ_GPRS, tid, gprs32, 0, NULL);
fill_gprs32 (gprs32);
ptrace32 (PTT_WRITE_GPRS, tid, gprs32, 0, NULL);
}
}
if (ppc_floating_point_unit_p (current_gdbarch)
&& (regno == -1
|| (regno >= tdep->ppc_fp0_regnum
&& regno < tdep->ppc_fp0_regnum + ppc_num_fprs)))
{
ptrace32 (PTT_READ_FPRS, tid, (int *) fprs, 0, NULL);
fill_fprs (fprs);
ptrace32 (PTT_WRITE_FPRS, tid, (int *) fprs, 0, NULL);
}
if (regno == -1 || special_register_p (regno))
{
if (arch64)
{
ptrace64aix (PTT_READ_SPRS, tid,
(unsigned long) &sprs64, 0, NULL);
fill_sprs64 (&sprs64.pt_iar, &sprs64.pt_msr, &sprs64.pt_cr,
&sprs64.pt_lr, &sprs64.pt_ctr, &sprs64.pt_xer,
&sprs64.pt_fpscr);
ptrace64aix (PTT_WRITE_SPRS, tid,
(unsigned long) &sprs64, 0, NULL);
}
else
{
ptrace32 (PTT_READ_SPRS, tid, (int *) &sprs32, 0, NULL);
fill_sprs32 (&sprs32.pt_iar, &sprs32.pt_msr, &sprs32.pt_cr,
&sprs32.pt_lr, &sprs32.pt_ctr, &sprs32.pt_xer,
&sprs32.pt_fpscr);
if (tdep->ppc_mq_regnum >= 0)
if (register_cached (tdep->ppc_mq_regnum))
regcache_raw_collect (current_regcache, tdep->ppc_mq_regnum,
&sprs32.pt_mq);
ptrace32 (PTT_WRITE_SPRS, tid, (int *) &sprs32, 0, NULL);
}
}
}
static void
aix_thread_store_registers (int regno)
{
struct thread_info *thread;
pthdb_tid_t tid;
if (!PD_TID (inferior_ptid))
base_target.to_store_registers (regno);
else
{
thread = find_thread_pid (inferior_ptid);
tid = thread->private->tid;
if (tid == PTHDB_INVALID_TID)
store_regs_user_thread (thread->private->pdtid);
else
store_regs_kernel_thread (regno, tid);
}
}
static int
aix_thread_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int write,
struct mem_attrib *attrib,
struct target_ops *target)
{
int n;
struct cleanup *cleanup = save_inferior_ptid ();
inferior_ptid = pid_to_ptid (PIDGET (inferior_ptid));
n = base_target.deprecated_xfer_memory (memaddr, myaddr, len,
write, attrib, &base_target);
do_cleanups (cleanup);
return n;
}
static void
aix_thread_kill (void)
{
struct cleanup *cleanup = save_inferior_ptid ();
inferior_ptid = pid_to_ptid (PIDGET (inferior_ptid));
base_target.to_kill ();
do_cleanups (cleanup);
}
static void
aix_thread_mourn_inferior (void)
{
pd_deactivate ();
base_target.to_mourn_inferior ();
}
static int
aix_thread_thread_alive (ptid_t ptid)
{
if (!PD_TID (ptid))
return base_target.to_thread_alive (ptid);
return in_thread_list (ptid);
}
static char *
aix_thread_pid_to_str (ptid_t ptid)
{
static char *ret = NULL;
if (!PD_TID (ptid))
return base_target.to_pid_to_str (ptid);
xfree (ret);
ret = xstrprintf (_("Thread %ld"), ptid_get_tid (ptid));
return ret;
}
static char *
aix_thread_extra_thread_info (struct thread_info *thread)
{
struct ui_file *buf;
int status;
pthdb_pthread_t pdtid;
pthdb_tid_t tid;
pthdb_state_t state;
pthdb_suspendstate_t suspendstate;
pthdb_detachstate_t detachstate;
int cancelpend;
long length;
static char *ret = NULL;
if (!PD_TID (thread->ptid))
return NULL;
buf = mem_fileopen ();
pdtid = thread->private->pdtid;
tid = thread->private->tid;
if (tid != PTHDB_INVALID_TID)
fprintf_unfiltered (buf, _("tid %d"), tid);
status = pthdb_pthread_state (pd_session, pdtid, &state);
if (status != PTHDB_SUCCESS)
state = PST_NOTSUP;
fprintf_unfiltered (buf, ", %s", state2str (state));
status = pthdb_pthread_suspendstate (pd_session, pdtid,
&suspendstate);
if (status == PTHDB_SUCCESS && suspendstate == PSS_SUSPENDED)
fprintf_unfiltered (buf, _(", suspended"));
status = pthdb_pthread_detachstate (pd_session, pdtid,
&detachstate);
if (status == PTHDB_SUCCESS && detachstate == PDS_DETACHED)
fprintf_unfiltered (buf, _(", detached"));
pthdb_pthread_cancelpend (pd_session, pdtid, &cancelpend);
if (status == PTHDB_SUCCESS && cancelpend)
fprintf_unfiltered (buf, _(", cancel pending"));
ui_file_write (buf, "", 1);
xfree (ret);
ret = ui_file_xstrdup (buf, &length);
ui_file_delete (buf);
return ret;
}
static void
init_aix_thread_ops (void)
{
aix_thread_ops.to_shortname = "aix-threads";
aix_thread_ops.to_longname = _("AIX pthread support");
aix_thread_ops.to_doc = _("AIX pthread support");
aix_thread_ops.to_attach = aix_thread_attach;
aix_thread_ops.to_detach = aix_thread_detach;
aix_thread_ops.to_resume = aix_thread_resume;
aix_thread_ops.to_wait = aix_thread_wait;
aix_thread_ops.to_fetch_registers = aix_thread_fetch_registers;
aix_thread_ops.to_store_registers = aix_thread_store_registers;
aix_thread_ops.deprecated_xfer_memory = aix_thread_xfer_memory;
aix_thread_ops.to_kill = aix_thread_kill;
aix_thread_ops.to_mourn_inferior = aix_thread_mourn_inferior;
aix_thread_ops.to_thread_alive = aix_thread_thread_alive;
aix_thread_ops.to_pid_to_str = aix_thread_pid_to_str;
aix_thread_ops.to_extra_thread_info = aix_thread_extra_thread_info;
aix_thread_ops.to_stratum = thread_stratum;
aix_thread_ops.to_magic = OPS_MAGIC;
}
void
_initialize_aix_thread (void)
{
init_aix_thread_ops ();
add_target (&aix_thread_ops);
target_new_objfile_chain = deprecated_target_new_objfile_hook;
deprecated_target_new_objfile_hook = new_objfile;
add_setshow_zinteger_cmd ("aix-thread", no_class, (char *) &debug_aix_thread,
_("Set debugging of AIX thread module."),
_("Show debugging of AIX thread module."),
_("Enables debugging output (used to debug GDB)."),
NULL, NULL,
&setdebuglist, &showdebuglist);
}