/* Native-dependent code for PA-RISC HP-UX. Copyright (C) 2004, 2005, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. This file is part of GDB. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "defs.h" #include "inferior.h" #include "regcache.h" #include "target.h" #include "gdb_assert.h" #include <sys/ptrace.h> #include <sys/utsname.h> #include <machine/save_state.h> #ifdef HAVE_TTRACE #include <sys/ttrace.h> #endif #include "hppa-tdep.h" #include "solib-som.h" #include "inf-ptrace.h" #include "inf-ttrace.h" /* Return the offset of register REGNUM within `struct save_state'. The offset returns depends on the flags in the "flags" register and the register size (32-bit or 64-bit). These are taken from REGCACHE. */ LONGEST hppa_hpux_save_state_offset (struct regcache *regcache, int regnum) { LONGEST offset; if (regnum == HPPA_FLAGS_REGNUM) return ssoff (ss_flags); if (HPPA_R0_REGNUM < regnum && regnum < HPPA_FP0_REGNUM) { struct gdbarch *arch = get_regcache_arch (regcache); size_t size = register_size (arch, HPPA_R1_REGNUM); ULONGEST flags; gdb_assert (size == 4 || size == 8); regcache_cooked_read_unsigned (regcache, HPPA_FLAGS_REGNUM, &flags); if (flags & SS_WIDEREGS) offset = ssoff (ss_wide) + (8 - size) + (regnum - HPPA_R0_REGNUM) * 8; else offset = ssoff (ss_narrow) + (regnum - HPPA_R1_REGNUM) * 4; } else { struct gdbarch *arch = get_regcache_arch (regcache); size_t size = register_size (arch, HPPA_FP0_REGNUM); gdb_assert (size == 4 || size == 8); gdb_assert (regnum >= HPPA_FP0_REGNUM); offset = ssoff(ss_fpblock) + (regnum - HPPA_FP0_REGNUM) * size; } gdb_assert (offset < sizeof (save_state_t)); return offset; } /* Just in case a future version of PA-RISC HP-UX won't have ptrace(2) at all. */ #ifndef PTRACE_TYPE_RET #define PTRACE_TYPE_RET void #endif static void hppa_hpux_fetch_register (struct regcache *regcache, int regnum) { struct gdbarch *gdbarch = get_regcache_arch (regcache); enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); CORE_ADDR addr; size_t size; PTRACE_TYPE_RET *buf; pid_t pid; int i; pid = ptid_get_pid (inferior_ptid); /* This isn't really an address, but ptrace thinks of it as one. */ addr = hppa_hpux_save_state_offset (regcache, regnum); size = register_size (gdbarch, regnum); gdb_assert (size == 4 || size == 8); buf = alloca (size); #ifdef HAVE_TTRACE { lwpid_t lwp = ptid_get_lwp (inferior_ptid); if (ttrace (TT_LWP_RUREGS, pid, lwp, addr, size, (uintptr_t)buf) == -1) error (_("Couldn't read register %s (#%d): %s"), gdbarch_register_name (gdbarch, regnum), regnum, safe_strerror (errno)); } #else { int i; /* Read the register contents from the inferior a chuck at the time. */ for (i = 0; i < size / sizeof (PTRACE_TYPE_RET); i++) { errno = 0; buf[i] = ptrace (PT_RUREGS, pid, (PTRACE_TYPE_ARG3) addr, 0, 0); if (errno != 0) error (_("Couldn't read register %s (#%d): %s"), gdbarch_register_name (gdbarch, regnum), regnum, safe_strerror (errno)); addr += sizeof (PTRACE_TYPE_RET); } } #endif /* Take care with the "flags" register. It's stored as an `int' in `struct save_state', even for 64-bit code. */ if (regnum == HPPA_FLAGS_REGNUM && size == 8) { ULONGEST flags; flags = extract_unsigned_integer ((gdb_byte *)buf, 4, byte_order); store_unsigned_integer ((gdb_byte *)buf, 8, byte_order, flags); } regcache_raw_supply (regcache, regnum, buf); } static void hppa_hpux_fetch_inferior_registers (struct target_ops *ops, struct regcache *regcache, int regnum) { if (regnum == -1) for (regnum = 0; regnum < gdbarch_num_regs (get_regcache_arch (regcache)); regnum++) hppa_hpux_fetch_register (regcache, regnum); else hppa_hpux_fetch_register (regcache, regnum); } /* Store register REGNUM into the inferior. */ static void hppa_hpux_store_register (struct regcache *regcache, int regnum) { struct gdbarch *gdbarch = get_regcache_arch (regcache); enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); CORE_ADDR addr; size_t size; PTRACE_TYPE_RET *buf; pid_t pid; pid = ptid_get_pid (inferior_ptid); /* This isn't really an address, but ptrace thinks of it as one. */ addr = hppa_hpux_save_state_offset (regcache, regnum); size = register_size (gdbarch, regnum); gdb_assert (size == 4 || size == 8); buf = alloca (size); regcache_raw_collect (regcache, regnum, buf); /* Take care with the "flags" register. It's stored as an `int' in `struct save_state', even for 64-bit code. */ if (regnum == HPPA_FLAGS_REGNUM && size == 8) { ULONGEST flags; flags = extract_unsigned_integer ((gdb_byte *)buf, 8, byte_order); store_unsigned_integer ((gdb_byte *)buf, 4, byte_order, flags); size = 4; } #ifdef HAVE_TTRACE { lwpid_t lwp = ptid_get_lwp (inferior_ptid); if (ttrace (TT_LWP_WUREGS, pid, lwp, addr, size, (uintptr_t)buf) == -1) error (_("Couldn't write register %s (#%d): %s"), gdbarch_register_name (gdbarch, regnum), regnum, safe_strerror (errno)); } #else { int i; /* Write the register contents into the inferior a chunk at the time. */ for (i = 0; i < size / sizeof (PTRACE_TYPE_RET); i++) { errno = 0; ptrace (PT_WUREGS, pid, (PTRACE_TYPE_ARG3) addr, buf[i], 0); if (errno != 0) error (_("Couldn't write register %s (#%d): %s"), gdbarch_register_name (gdbarch, regnum), regnum, safe_strerror (errno)); addr += sizeof (PTRACE_TYPE_RET); } } #endif } /* Store register REGNUM back into the inferior. If REGNUM is -1, do this for all registers (including the floating point registers). */ static void hppa_hpux_store_inferior_registers (struct target_ops *ops, struct regcache *regcache, int regnum) { if (regnum == -1) for (regnum = 0; regnum < gdbarch_num_regs (get_regcache_arch (regcache)); regnum++) hppa_hpux_store_register (regcache, regnum); else hppa_hpux_store_register (regcache, regnum); } /* Set hpux_major_release variable to the value retrieved from a call to uname function. */ static void set_hpux_major_release (void) { struct utsname x; char *p; uname (&x); p = strchr (x.release, '.'); if (p) hpux_major_release = atoi (p + 1); } /* Prevent warning from -Wmissing-prototypes. */ void _initialize_hppa_hpux_nat (void); void _initialize_hppa_hpux_nat (void) { struct target_ops *t; set_hpux_major_release (); #ifdef HAVE_TTRACE t = inf_ttrace_target (); #else t = inf_ptrace_target (); #endif t->to_fetch_registers = hppa_hpux_fetch_inferior_registers; t->to_store_registers = hppa_hpux_store_inferior_registers; add_target (t); }