ppc32 debugging ppc64, part 1

Richard Henderson
2005-09-12T12:52:28Z

This is just enough modifications to not immediately fail
debugging a 64-bit process from a 32-bit debugger.

We don't get very far in the actual debugging:

(gdb) b main
warning: Breakpoint address adjusted from 0x10093aa0 to 0x10000370.
Breakpoint 1 at 0x10000370
(gdb) run
Starting program: /home/rth/work/gcc/bld-binu64/gdb/z
warning: Breakpoint address adjusted from 0x10093a40 to 0x100001c0.
warning: Breakpoint 1 address previously adjusted from 0x10093aa0 to 0x10000370.
Breakpoint 1, 0x0000000010000370 in ?? ()
(gdb) ppc
0x10000370 <__libc_tsd_CTYPE_B+268436264>: std r31,-8(r1)

But this is better than the original:

Starting program: /home/rth/work/gcc/bld-binu64/gdb/z
warning: Breakpoint address adjusted from 0x10093a40 to 0x100001c0.
Failed to read a valid object file image from memory.

Program received signal SIGTRAP, Trace/breakpoint trap.
0x100001c00002d932 in ?? ()

Comments?

r~

* ppc-linux-nat.c (PTRACE_XFER_TYPE): Set to long.
(ppc_register_u_addr): Compute wordsize correctly for 64-bit target.
(ptrace_read_u, ptrace_write_u): New.
(fetch_register): Simplify. Use PPC_PTRACE_PEEKUSR_3264 when needed.
(store_register): Similarly with PPC_PTRACE_POKEUSR_3264.
(default_xfer_partial, ppc32_linux_xfer_partial): New.
(_initialize_ppc_linux_nat): Use them.

Index: ppc-linux-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/ppc-linux-nat.c,v
retrieving revision 1.55
diff -u -p -c -r1.55 ppc-linux-nat.c
*** ppc-linux-nat.c 10 Sep 2005 18:11:04 -0000 1.55
--- ppc-linux-nat.c 12 Sep 2005 12:37:40 -0000
***************
*** 39,44 ****
--- 39,45 ----
#include <fcntl.h>
#include <sys/procfs.h>
#include <sys/ptrace.h>
+ #include <sys/syscall.h>

/* Prototypes for supply_gregset etc. */
#include "gregset.h"
***************
*** 51,59 ****
#define PT_WRITE_U PTRACE_POKEUSR
#endif

- /* Default the type of the ptrace transfer to int. */
#ifndef PTRACE_XFER_TYPE
! #define PTRACE_XFER_TYPE int
#endif

/* Glibc's headers don't define PTRACE_GETVRREGS so we cannot use a
--- 52,59 ----
#define PT_WRITE_U PTRACE_POKEUSR
#endif

#ifndef PTRACE_XFER_TYPE
! #define PTRACE_XFER_TYPE long
#endif

/* Glibc's headers don't define PTRACE_GETVRREGS so we cannot use a
*************** ppc_register_u_addr (int regno)
*** 180,186 ****
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
/* NOTE: cagney/2003-11-25: This is the word size used by the ptrace
interface, and not the wordsize of the program's ABI. */
! int wordsize = sizeof (PTRACE_XFER_TYPE);

/* General purpose registers occupy 1 slot each in the buffer */
if (regno >= tdep->ppc_gp0_regnum
--- 180,194 ----
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
/* NOTE: cagney/2003-11-25: This is the word size used by the ptrace
interface, and not the wordsize of the program's ABI. */
! /* NOTE: rth/2005-09-12: Which is complicated by the fact that there
! are in fact two ptrace interfaces: the normal one, and a different
! one for 32-bit gdb debugging 64-bit target. */
! int wordsize;
!
! if (sizeof (PTRACE_XFER_TYPE) == 8)
! wordsize = sizeof (PTRACE_XFER_TYPE);
! else
! wordsize = tdep->wordsize;

/* General purpose registers occupy 1 slot each in the buffer */
if (regno >= tdep->ppc_gp0_regnum
*************** fetch_spe_register (int tid, int regno)
*** 337,350 ****
}

static void
fetch_register (int tid, int regno)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
/* This isn't really an address. But ptrace thinks of it as one. */
CORE_ADDR regaddr = ppc_register_u_addr (regno);
! int bytes_transferred;
! unsigned int offset; /* Offset of registers within the u area. */
char buf[MAX_REGISTER_SIZE];

if (altivec_register_p (regno))
{
--- 345,364 ----
}

static void
+ ptrace_read_u (int cmd, int tid, CORE_ADDR regaddr, char *buf)
+ {
+ syscall (SYS_ptrace, cmd, tid, (PTRACE_ARG3_TYPE)(size_t)regaddr, buf);
+ }
+
+ static void
fetch_register (int tid, int regno)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
/* This isn't really an address. But ptrace thinks of it as one. */
CORE_ADDR regaddr = ppc_register_u_addr (regno);
! int regsize = register_size (current_gdbarch, regno);
char buf[MAX_REGISTER_SIZE];
+ int bytes_transferred;

if (altivec_register_p (regno))
{
*************** fetch_register (int tid, int regno)
*** 369,397 ****

if (regaddr == -1)
{
! memset (buf, '\0', register_size (current_gdbarch, regno)); /* Supply zeroes */
regcache_raw_supply (current_regcache, regno, buf);
return;
}

! /* Read the raw register using PTRACE_XFER_TYPE sized chunks. On a
! 32-bit platform, 64-bit floating-point registers will require two
! transfers. */
! for (bytes_transferred = 0;
! bytes_transferred < register_size (current_gdbarch, regno);
! bytes_transferred += sizeof (PTRACE_XFER_TYPE))
{
! errno = 0;
! *(PTRACE_XFER_TYPE *) & buf[bytes_transferred]
! = ptrace (PT_READ_U, tid, (PTRACE_ARG3_TYPE) regaddr, 0);
! regaddr += sizeof (PTRACE_XFER_TYPE);
! if (errno != 0)
! {
! char message[128];
! sprintf (message, "reading register %s (#%d)",
! REGISTER_NAME (regno), regno);
! perror_with_name (message);
! }
}

/* Now supply the register. Keep in mind that the regcache's idea
--- 383,424 ----

if (regaddr == -1)
{
! /* Supply zeroes */
! memset (buf, '\0', register_size (current_gdbarch, regno));
regcache_raw_supply (current_regcache, regno, buf);
return;
}

! /* We need to distinguish between 32/64-bit gdb and 32/64-bit target. */
!
! errno = 0;
! memset (buf, '\0', register_size (current_gdbarch, regno));
! if (regsize <= sizeof (PTRACE_XFER_TYPE))
{
! ptrace_read_u (PT_READ_U, tid, regaddr, buf);
! bytes_transferred = sizeof (PTRACE_XFER_TYPE);
! }
! else
! {
! int cmd;
!
! gdb_assert (sizeof (PTRACE_XFER_TYPE) == 4 && regsize == 8);
! bytes_transferred = 2 * sizeof (PTRACE_XFER_TYPE);
!
! if (tdep->wordsize == 8)
! cmd = PPC_PTRACE_PEEKUSR_3264;
! else
! cmd = PT_READ_U;
!
! ptrace_read_u (cmd, tid, regaddr, buf);
! ptrace_read_u (cmd, tid, regaddr + 4, buf + 4);
! }
! if (errno != 0)
! {
! char message[128];
! sprintf (message, "reading register %s (#%d)",
! REGISTER_NAME (regno), regno);
! perror_with_name (message);
}

/* Now supply the register. Keep in mind that the regcache's idea
*************** fetch_register (int tid, int regno)
*** 407,414 ****
{
/* Big-endian values are found at the right end of the bytes
transferred. */
! size_t padding = (bytes_transferred
! - register_size (current_gdbarch, regno));
regcache_raw_supply (current_regcache, regno, buf + padding);
}
else
--- 434,440 ----
{
/* Big-endian values are found at the right end of the bytes
transferred. */
! size_t padding = bytes_transferred - regsize;
regcache_raw_supply (current_regcache, regno, buf + padding);
}
else
*************** fetch_ppc_registers (int tid)
*** 466,476 ****
--- 492,504 ----
int i;
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);

+ /* ??? Use PPC_PTRACE_GETREGS / PPC_PTRACE_GETFPREGS. */
for (i = 0; i < ppc_num_gprs; i++)
fetch_register (tid, tdep->ppc_gp0_regnum + i);
if (tdep->ppc_fp0_regnum >= 0)
for (i = 0; i < ppc_num_fprs; i++)
fetch_register (tid, tdep->ppc_fp0_regnum + i);
+
fetch_register (tid, PC_REGNUM);
if (tdep->ppc_ps_regnum != -1)
fetch_register (tid, tdep->ppc_ps_regnum);
*************** store_spe_register (int tid, int regno)
*** 633,646 ****
}

static void
store_register (int tid, int regno)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
/* This isn't really an address. But ptrace thinks of it as one. */
CORE_ADDR regaddr = ppc_register_u_addr (regno);
! int i;
! size_t bytes_to_transfer;
char buf[MAX_REGISTER_SIZE];

if (altivec_register_p (regno))
{
--- 661,681 ----
}

static void
+ ptrace_write_u (int cmd, int tid, CORE_ADDR regaddr, char *buf)
+ {
+ syscall (SYS_ptrace, cmd, tid, (PTRACE_ARG3_TYPE)(size_t)regaddr,
+ *(PTRACE_XFER_TYPE *)buf);
+ }
+
+ static void
store_register (int tid, int regno)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
/* This isn't really an address. But ptrace thinks of it as one. */
CORE_ADDR regaddr = ppc_register_u_addr (regno);
! int regsize = register_size (current_gdbarch, regno);
char buf[MAX_REGISTER_SIZE];
+ int bytes_to_transfer;

if (altivec_register_p (regno))
{
*************** store_register (int tid, int regno)
*** 660,667 ****
idea of the register's size may not be a multiple of sizeof
(PTRACE_XFER_TYPE). */
memset (buf, 0, sizeof buf);
! bytes_to_transfer = align_up (register_size (current_gdbarch, regno),
! sizeof (PTRACE_XFER_TYPE));
if (TARGET_BYTE_ORDER == BFD_ENDIAN_LITTLE)
{
/* Little-endian values always sit at the left end of the buffer. */
--- 695,701 ----
idea of the register's size may not be a multiple of sizeof
(PTRACE_XFER_TYPE). */
memset (buf, 0, sizeof buf);
! bytes_to_transfer = align_up (regsize, sizeof (PTRACE_XFER_TYPE));
if (TARGET_BYTE_ORDER == BFD_ENDIAN_LITTLE)
{
/* Little-endian values always sit at the left end of the buffer. */
*************** store_register (int tid, int regno)
*** 670,701 ****
else if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
{
/* Big-endian values sit at the right end of the buffer. */
! size_t padding = (bytes_to_transfer
! - register_size (current_gdbarch, regno));
regcache_raw_collect (current_regcache, regno, buf + padding);
}

! for (i = 0; i < bytes_to_transfer; i += sizeof (PTRACE_XFER_TYPE))
{
! errno = 0;
! ptrace (PT_WRITE_U, tid, (PTRACE_ARG3_TYPE) regaddr,
! *(PTRACE_XFER_TYPE *) & buf[i]);
! regaddr += sizeof (PTRACE_XFER_TYPE);

! if (errno == EIO
! && regno == tdep->ppc_fpscr_regnum)
! {
! /* Some older kernel versions don't allow fpscr to be written. */
! continue;
! }

! if (errno != 0)
! {
! char message[128];
! sprintf (message, "writing register %s (#%d)",
! REGISTER_NAME (regno), regno);
! perror_with_name (message);
! }
}
}

--- 704,740 ----
else if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
{
/* Big-endian values sit at the right end of the buffer. */
! size_t padding = bytes_to_transfer - regsize;
regcache_raw_collect (current_regcache, regno, buf + padding);
}

! /* We need to distinguish between 32/64-bit gdb and 32/64-bit target. */
!
! errno = 0;
! if (regsize <= sizeof (PTRACE_XFER_TYPE))
! ptrace_write_u (PT_WRITE_U, tid, regaddr, buf);
! else
{
! int cmd;

! gdb_assert (sizeof (PTRACE_XFER_TYPE) == 4 && regsize == 8);

! if (regsize == tdep->wordsize)
! cmd = PPC_PTRACE_POKEUSR_3264;
! else
! cmd = PT_WRITE_U;
!
! ptrace_write_u (cmd, tid, regaddr, buf);
! ptrace_write_u (cmd, tid, regaddr + 4, buf + 4);
! }
! if (errno == EIO && regno == tdep->ppc_fpscr_regnum)
! /* Some older kernel versions don't allow fpscr to be written. */;
! else if (errno != 0)
! {
! char message[128];
! sprintf (message, "writing register %s (#%d)",
! REGISTER_NAME (regno), regno);
! perror_with_name (message);
}
}

*************** store_ppc_registers (int tid)
*** 750,760 ****
--- 789,801 ----
int i;
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);

+ /* ??? Use PPC_PTRACE_SETREGS / PPC_PTRACE_SETFPREGS. */
for (i = 0; i < ppc_num_gprs; i++)
store_register (tid, tdep->ppc_gp0_regnum + i);
if (tdep->ppc_fp0_regnum >= 0)
for (i = 0; i < ppc_num_fprs; i++)
store_register (tid, tdep->ppc_fp0_regnum + i);
+
store_register (tid, PC_REGNUM);
if (tdep->ppc_ps_regnum != -1)
store_register (tid, tdep->ppc_ps_regnum);
*************** fill_fpregset (gdb_fpregset_t *fpregsetp
*** 886,891 ****
--- 927,1014 ----
}
}

+ /* Hold a pointer to inf_ptrace_xfer_partial. */
+ static LONGEST (*default_xfer_partial) (struct target_ops *ops,
+ enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len);
+
+ static LONGEST
+ ppc32_linux_xfer_partial (struct target_ops *ops, enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
+ {
+ struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+ pid_t pid;
+ CORE_ADDR rounded_offset;
+ LONGEST partial_len;
+ PTRACE_XFER_TYPE buf;
+
+ /* If the target is also 32-bit, we can avoid double indirection by
+ using the default implementation. Plus, the PTRACE_*_3264 commands
+ are only present on 64-bit kernels. */
+ if (tdep->wordsize == 4)
+ return default_xfer_partial (ops, object, annex, readbuf,
+ writebuf, offset, len);
+
+ if (object != TARGET_OBJECT_MEMORY)
+ return -1;
+
+ gdb_assert (sizeof (CORE_ADDR) == 8);
+
+ pid = ptid_get_pid (inferior_ptid);
+
+ /* Round the start offset down to the next long word boundary. */
+ rounded_offset = offset & -(ULONGEST) sizeof (PTRACE_XFER_TYPE);
+
+ /* 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_XFER_TYPE) - 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_XFER_TYPE)))
+ {
+ /* Need part of initial word -- fetch it. */
+ syscall (SYS_ptrace, PPC_PTRACE_PEEKDATA_3264, pid,
+ &rounded_offset, &buf);
+ }
+
+ /* Copy data to be written over corresponding part of buffer. */
+ memcpy ((char *)&buf + offset - rounded_offset, writebuf, partial_len);
+
+ errno = 0;
+ syscall (SYS_ptrace, PPC_PTRACE_POKEDATA_3264, pid,
+ &rounded_offset, buf);
+ if (errno)
+ return 0;
+ }
+
+ if (readbuf)
+ {
+ errno = 0;
+ syscall (SYS_ptrace, PPC_PTRACE_PEEKDATA_3264, pid,
+ &rounded_offset, &buf);
+ if (errno)
+ return 0;
+
+ /* Copy appropriate bytes out of the buffer. */
+ memcpy (readbuf, (char *)&buf + offset - rounded_offset, partial_len);
+ }
+
+ return partial_len;
+ }
+
void _initialize_ppc_linux_nat (void);

void
*************** _initialize_ppc_linux_nat (void)
*** 900,905 ****
--- 1023,1036 ----
t->to_fetch_registers = ppc_linux_fetch_inferior_registers;
t->to_store_registers = ppc_linux_store_inferior_registers;

+ /* We need a special version of xfer_partial to handle 32-bit gdb
+ debugging a 64-bit process. */
+ if (sizeof (PTRACE_XFER_TYPE) == 4)
+ {
+ default_xfer_partial = t->to_xfer_partial;
+ t->to_xfer_partial = ppc32_linux_xfer_partial;
+ }
+
/* Register the target. */
add_target (t);
}