X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fs390-nat.c;h=19bc607a83dda6d655ffb48121234b0d04b2a241;hb=0c5571793a8e3dbd0f99b8e4759bd201f5fe75b7;hp=c67777e929a5b474852833c4fb31ddcf962ea036;hpb=b179846202c8d5ce5742fea55f8d1f181463740e;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/s390-nat.c b/gdb/s390-nat.c index c67777e929..19bc607a83 100644 --- a/gdb/s390-nat.c +++ b/gdb/s390-nat.c @@ -1,6 +1,5 @@ /* S390 native-dependent code for GDB, the GNU debugger. - Copyright (C) 2001, 2003, 2004, 2005, 2006, 2007, 2009 - Free Software Foundation, Inc + Copyright (C) 2001-2013 Free Software Foundation, Inc. Contributed by D.J. Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) for IBM Deutschland Entwicklung GmbH, IBM Corporation. @@ -25,15 +24,30 @@ #include "inferior.h" #include "target.h" #include "linux-nat.h" +#include "auxv.h" +#include "gregset.h" #include "s390-tdep.h" +#include "elf/common.h" #include #include #include #include #include +#include +#ifndef PTRACE_GETREGSET +#define PTRACE_GETREGSET 0x4204 +#endif + +#ifndef PTRACE_SETREGSET +#define PTRACE_SETREGSET 0x4205 +#endif + +static int have_regset_last_break = 0; +static int have_regset_system_call = 0; +static int have_regset_tdb = 0; /* Map registers to gregset/ptrace offsets. These arrays are defined in s390-tdep.c. */ @@ -46,47 +60,139 @@ #define regmap_fpregset s390_regmap_fpregset -/* When debugging a 32-bit executable running under a 64-bit kernel, - we have to fix up the 64-bit registers we get from the kernel - to make them look like 32-bit registers. */ -#ifdef __s390x__ -#define SUBOFF(gdbarch, i) \ - ((gdbarch_ptr_bit (gdbarch) == 32 \ - && ((i) == S390_PSWA_REGNUM \ - || ((i) >= S390_R0_REGNUM && (i) <= S390_R15_REGNUM)))? 4 : 0) -#else -#define SUBOFF(gdbarch, i) 0 -#endif +/* Fill the regset described by MAP into REGCACHE, using the values + from REGP. The MAP array represents each register as a pair + (offset, regno) of short integers and is terminated with -1. */ + +static void +s390_native_supply (struct regcache *regcache, const short *map, + const gdb_byte *regp) +{ + for (; map[0] >= 0; map += 2) + regcache_raw_supply (regcache, map[1], regp ? regp + map[0] : NULL); +} +/* Collect the register REGNO out of the regset described by MAP from + REGCACHE into REGP. If REGNO == -1, do this for all registers in + this regset. */ + +static void +s390_native_collect (const struct regcache *regcache, const short *map, + int regno, gdb_byte *regp) +{ + for (; map[0] >= 0; map += 2) + if (regno == -1 || regno == map[1]) + regcache_raw_collect (regcache, map[1], regp + map[0]); +} /* Fill GDB's register array with the general-purpose register values - in *REGP. */ + in *REGP. + + When debugging a 32-bit executable running under a 64-bit kernel, + we have to fix up the 64-bit registers we get from the kernel to + make them look like 32-bit registers. */ + void supply_gregset (struct regcache *regcache, const gregset_t *regp) { +#ifdef __s390x__ struct gdbarch *gdbarch = get_regcache_arch (regcache); - int i; - for (i = 0; i < S390_NUM_REGS; i++) - if (regmap_gregset[i] != -1) - regcache_raw_supply (regcache, i, - (const char *)regp + regmap_gregset[i] - + SUBOFF (gdbarch, i)); + if (gdbarch_ptr_bit (gdbarch) == 32) + { + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + ULONGEST pswm = 0, pswa = 0; + gdb_byte buf[4]; + const short *map; + + for (map = regmap_gregset; map[0] >= 0; map += 2) + { + const gdb_byte *p = (const gdb_byte *) regp + map[0]; + int regno = map[1]; + + if (regno == S390_PSWM_REGNUM) + pswm = extract_unsigned_integer (p, 8, byte_order); + else if (regno == S390_PSWA_REGNUM) + pswa = extract_unsigned_integer (p, 8, byte_order); + else + { + if ((regno >= S390_R0_REGNUM && regno <= S390_R15_REGNUM) + || regno == S390_ORIG_R2_REGNUM) + p += 4; + regcache_raw_supply (regcache, regno, p); + } + } + + store_unsigned_integer (buf, 4, byte_order, (pswm >> 32) | 0x80000); + regcache_raw_supply (regcache, S390_PSWM_REGNUM, buf); + store_unsigned_integer (buf, 4, byte_order, + (pswa & 0x7fffffff) | (pswm & 0x80000000)); + regcache_raw_supply (regcache, S390_PSWA_REGNUM, buf); + return; + } +#endif + + s390_native_supply (regcache, regmap_gregset, (const gdb_byte *) regp); } /* Fill register REGNO (if it is a general-purpose register) in *REGP with the value in GDB's register array. If REGNO is -1, do this for all registers. */ + void fill_gregset (const struct regcache *regcache, gregset_t *regp, int regno) { +#ifdef __s390x__ struct gdbarch *gdbarch = get_regcache_arch (regcache); - int i; - for (i = 0; i < S390_NUM_REGS; i++) - if (regmap_gregset[i] != -1) - if (regno == -1 || regno == i) - regcache_raw_collect (regcache, i, - (char *)regp + regmap_gregset[i] - + SUBOFF (gdbarch, i)); + if (gdbarch_ptr_bit (gdbarch) == 32) + { + gdb_byte *psw_p[2]; + const short *map; + + for (map = regmap_gregset; map[0] >= 0; map += 2) + { + gdb_byte *p = (gdb_byte *) regp + map[0]; + int reg = map[1]; + + if (reg >= S390_PSWM_REGNUM && reg <= S390_PSWA_REGNUM) + psw_p[reg - S390_PSWM_REGNUM] = p; + + else if (regno == -1 || regno == reg) + { + if ((reg >= S390_R0_REGNUM && reg <= S390_R15_REGNUM) + || reg == S390_ORIG_R2_REGNUM) + { + memset (p, 0, 4); + p += 4; + } + regcache_raw_collect (regcache, reg, p + 4); + } + } + + if (regno == -1 + || regno == S390_PSWM_REGNUM || regno == S390_PSWA_REGNUM) + { + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + ULONGEST pswa, pswm; + gdb_byte buf[4]; + + regcache_raw_collect (regcache, S390_PSWM_REGNUM, buf); + pswm = extract_unsigned_integer (buf, 4, byte_order); + regcache_raw_collect (regcache, S390_PSWA_REGNUM, buf); + pswa = extract_unsigned_integer (buf, 4, byte_order); + + if (regno == -1 || regno == S390_PSWM_REGNUM) + store_unsigned_integer (psw_p[0], 8, byte_order, + ((pswm & 0xfff7ffff) << 32) | + (pswa & 0x80000000)); + if (regno == -1 || regno == S390_PSWA_REGNUM) + store_unsigned_integer (psw_p[1], 8, byte_order, + pswa & 0x7fffffff); + } + return; + } +#endif + + s390_native_collect (regcache, regmap_gregset, regno, (gdb_byte *) regp); } /* Fill GDB's register array with the floating-point register values @@ -94,11 +200,7 @@ fill_gregset (const struct regcache *regcache, gregset_t *regp, int regno) void supply_fpregset (struct regcache *regcache, const fpregset_t *regp) { - int i; - for (i = 0; i < S390_NUM_REGS; i++) - if (regmap_fpregset[i] != -1) - regcache_raw_supply (regcache, i, - (const char *)regp + regmap_fpregset[i]); + s390_native_supply (regcache, regmap_fpregset, (const gdb_byte *) regp); } /* Fill register REGNO (if it is a general-purpose register) in @@ -107,12 +209,7 @@ supply_fpregset (struct regcache *regcache, const fpregset_t *regp) void fill_fpregset (const struct regcache *regcache, fpregset_t *regp, int regno) { - int i; - for (i = 0; i < S390_NUM_REGS; i++) - if (regmap_fpregset[i] != -1) - if (regno == -1 || regno == i) - regcache_raw_collect (regcache, i, - (char *)regp + regmap_fpregset[i]); + s390_native_collect (regcache, regmap_fpregset, regno, (gdb_byte *) regp); } /* Find the TID for the current inferior thread to use with ptrace. */ @@ -120,9 +217,9 @@ static int s390_inferior_tid (void) { /* GNU/Linux LWP ID's are process ID's. */ - int tid = TIDGET (inferior_ptid); + int tid = ptid_get_lwp (inferior_ptid); if (tid == 0) - tid = PIDGET (inferior_ptid); /* Not a threaded program. */ + tid = ptid_get_pid (inferior_ptid); /* Not a threaded program. */ return tid; } @@ -201,6 +298,69 @@ store_fpregs (const struct regcache *regcache, int tid, int regnum) perror_with_name (_("Couldn't write floating point status")); } +/* Fetch all registers in the kernel's register set whose number is REGSET, + whose size is REGSIZE, and whose layout is described by REGMAP, from + process/thread TID and store their values in GDB's register cache. */ +static void +fetch_regset (struct regcache *regcache, int tid, + int regset, int regsize, const short *regmap) +{ + gdb_byte *buf = alloca (regsize); + struct iovec iov; + + iov.iov_base = buf; + iov.iov_len = regsize; + + if (ptrace (PTRACE_GETREGSET, tid, (long) regset, (long) &iov) < 0) + { + if (errno == ENODATA) + s390_native_supply (regcache, regmap, NULL); + else + perror_with_name (_("Couldn't get register set")); + } + else + s390_native_supply (regcache, regmap, buf); +} + +/* Store all registers in the kernel's register set whose number is REGSET, + whose size is REGSIZE, and whose layout is described by REGMAP, from + GDB's register cache back to process/thread TID. */ +static void +store_regset (struct regcache *regcache, int tid, + int regset, int regsize, const short *regmap) +{ + gdb_byte *buf = alloca (regsize); + struct iovec iov; + + iov.iov_base = buf; + iov.iov_len = regsize; + + if (ptrace (PTRACE_GETREGSET, tid, (long) regset, (long) &iov) < 0) + perror_with_name (_("Couldn't get register set")); + + s390_native_collect (regcache, regmap, -1, buf); + + if (ptrace (PTRACE_SETREGSET, tid, (long) regset, (long) &iov) < 0) + perror_with_name (_("Couldn't set register set")); +} + +/* Check whether the kernel provides a register set with number REGSET + of size REGSIZE for process/thread TID. */ +static int +check_regset (int tid, int regset, int regsize) +{ + gdb_byte *buf = alloca (regsize); + struct iovec iov; + + iov.iov_base = buf; + iov.iov_len = regsize; + + if (ptrace (PTRACE_GETREGSET, tid, (long) regset, (long) &iov) >= 0 + || errno == ENODATA) + return 1; + return 0; +} + /* Fetch register REGNUM from the child process. If REGNUM is -1, do this for all registers. */ static void @@ -209,13 +369,27 @@ s390_linux_fetch_inferior_registers (struct target_ops *ops, { int tid = s390_inferior_tid (); - if (regnum == -1 - || (regnum < S390_NUM_REGS && regmap_gregset[regnum] != -1)) + if (regnum == -1 || S390_IS_GREGSET_REGNUM (regnum)) fetch_regs (regcache, tid); - if (regnum == -1 - || (regnum < S390_NUM_REGS && regmap_fpregset[regnum] != -1)) + if (regnum == -1 || S390_IS_FPREGSET_REGNUM (regnum)) fetch_fpregs (regcache, tid); + + if (have_regset_last_break) + if (regnum == -1 || regnum == S390_LAST_BREAK_REGNUM) + fetch_regset (regcache, tid, NT_S390_LAST_BREAK, 8, + (gdbarch_ptr_bit (get_regcache_arch (regcache)) == 32 + ? s390_regmap_last_break : s390x_regmap_last_break)); + + if (have_regset_system_call) + if (regnum == -1 || regnum == S390_SYSTEM_CALL_REGNUM) + fetch_regset (regcache, tid, NT_S390_SYSTEM_CALL, 4, + s390_regmap_system_call); + + if (have_regset_tdb) + if (regnum == -1 || S390_IS_TDBREGSET_REGNUM (regnum)) + fetch_regset (regcache, tid, NT_S390_TDB, s390_sizeof_tdbregset, + s390_regmap_tdb); } /* Store register REGNUM back into the child process. If REGNUM is @@ -226,13 +400,18 @@ s390_linux_store_inferior_registers (struct target_ops *ops, { int tid = s390_inferior_tid (); - if (regnum == -1 - || (regnum < S390_NUM_REGS && regmap_gregset[regnum] != -1)) + if (regnum == -1 || S390_IS_GREGSET_REGNUM (regnum)) store_regs (regcache, tid, regnum); - if (regnum == -1 - || (regnum < S390_NUM_REGS && regmap_fpregset[regnum] != -1)) + if (regnum == -1 || S390_IS_FPREGSET_REGNUM (regnum)) store_fpregs (regcache, tid, regnum); + + /* S390_LAST_BREAK_REGNUM is read-only. */ + + if (have_regset_system_call) + if (regnum == -1 || regnum == S390_SYSTEM_CALL_REGNUM) + store_regset (regcache, tid, NT_S390_SYSTEM_CALL, 4, + s390_regmap_system_call); } @@ -285,7 +464,7 @@ s390_stopped_by_watchpoint (void) } static void -s390_fix_watch_points (ptid_t ptid) +s390_fix_watch_points (struct lwp_info *lp) { int tid; @@ -295,9 +474,9 @@ s390_fix_watch_points (ptid_t ptid) CORE_ADDR watch_lo_addr = (CORE_ADDR)-1, watch_hi_addr = 0; struct watch_area *area; - tid = TIDGET (ptid); + tid = ptid_get_lwp (lp->ptid); if (tid == 0) - tid = PIDGET (ptid); + tid = ptid_get_pid (lp->ptid); for (area = watch_base; area; area = area->next) { @@ -329,10 +508,10 @@ s390_fix_watch_points (ptid_t ptid) } static int -s390_insert_watchpoint (CORE_ADDR addr, int len, int type) +s390_insert_watchpoint (CORE_ADDR addr, int len, int type, + struct expression *cond) { struct lwp_info *lp; - ptid_t ptid; struct watch_area *area = xmalloc (sizeof (struct watch_area)); if (!area) @@ -344,16 +523,16 @@ s390_insert_watchpoint (CORE_ADDR addr, int len, int type) area->next = watch_base; watch_base = area; - ALL_LWPS (lp, ptid) - s390_fix_watch_points (ptid); + ALL_LWPS (lp) + s390_fix_watch_points (lp); return 0; } static int -s390_remove_watchpoint (CORE_ADDR addr, int len, int type) +s390_remove_watchpoint (CORE_ADDR addr, int len, int type, + struct expression *cond) { struct lwp_info *lp; - ptid_t ptid; struct watch_area *area, **parea; for (parea = &watch_base; *parea; parea = &(*parea)->next) @@ -372,8 +551,8 @@ s390_remove_watchpoint (CORE_ADDR addr, int len, int type) *parea = area->next; xfree (area); - ALL_LWPS (lp, ptid) - s390_fix_watch_points (ptid); + ALL_LWPS (lp) + s390_fix_watch_points (lp); return 0; } @@ -389,6 +568,100 @@ s390_region_ok_for_hw_watchpoint (CORE_ADDR addr, int cnt) return 1; } +static int +s390_target_wordsize (void) +{ + int wordsize = 4; + + /* Check for 64-bit inferior process. This is the case when the host is + 64-bit, and in addition bit 32 of the PSW mask is set. */ +#ifdef __s390x__ + long pswm; + + errno = 0; + pswm = (long) ptrace (PTRACE_PEEKUSER, s390_inferior_tid (), PT_PSWMASK, 0); + if (errno == 0 && (pswm & 0x100000000ul) != 0) + wordsize = 8; +#endif + + return wordsize; +} + +static int +s390_auxv_parse (struct target_ops *ops, gdb_byte **readptr, + gdb_byte *endptr, CORE_ADDR *typep, CORE_ADDR *valp) +{ + int sizeof_auxv_field = s390_target_wordsize (); + enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); + gdb_byte *ptr = *readptr; + + if (endptr == ptr) + return 0; + + if (endptr - ptr < sizeof_auxv_field * 2) + return -1; + + *typep = extract_unsigned_integer (ptr, sizeof_auxv_field, byte_order); + ptr += sizeof_auxv_field; + *valp = extract_unsigned_integer (ptr, sizeof_auxv_field, byte_order); + ptr += sizeof_auxv_field; + + *readptr = ptr; + return 1; +} + +#ifdef __s390x__ +static unsigned long +s390_get_hwcap (void) +{ + CORE_ADDR field; + + if (target_auxv_search (¤t_target, AT_HWCAP, &field)) + return (unsigned long) field; + + return 0; +} +#endif + +static const struct target_desc * +s390_read_description (struct target_ops *ops) +{ + int tid = s390_inferior_tid (); + + have_regset_last_break + = check_regset (tid, NT_S390_LAST_BREAK, 8); + have_regset_system_call + = check_regset (tid, NT_S390_SYSTEM_CALL, 4); + have_regset_tdb + = check_regset (tid, NT_S390_TDB, s390_sizeof_tdbregset); + +#ifdef __s390x__ + /* If GDB itself is compiled as 64-bit, we are running on a machine in + z/Architecture mode. If the target is running in 64-bit addressing + mode, report s390x architecture. If the target is running in 31-bit + addressing mode, but the kernel supports using 64-bit registers in + that mode, report s390 architecture with 64-bit GPRs. */ + + if (s390_target_wordsize () == 8) + return (have_regset_tdb ? tdesc_s390x_te_linux64 : + have_regset_system_call? tdesc_s390x_linux64v2 : + have_regset_last_break? tdesc_s390x_linux64v1 : + tdesc_s390x_linux64); + + if (s390_get_hwcap () & HWCAP_S390_HIGH_GPRS) + return (have_regset_tdb ? tdesc_s390_te_linux64 : + have_regset_system_call? tdesc_s390_linux64v2 : + have_regset_last_break? tdesc_s390_linux64v1 : + tdesc_s390_linux64); +#endif + + /* If GDB itself is compiled as 31-bit, or if we're running a 31-bit inferior + on a 64-bit kernel that does not support using 64-bit registers in 31-bit + mode, report s390 architecture with 32-bit GPRs. */ + return (have_regset_system_call? tdesc_s390_linux32v2 : + have_regset_last_break? tdesc_s390_linux32v1 : + tdesc_s390_linux32); +} void _initialize_s390_nat (void); @@ -412,6 +685,10 @@ _initialize_s390_nat (void) t->to_insert_watchpoint = s390_insert_watchpoint; t->to_remove_watchpoint = s390_remove_watchpoint; + /* Detect target architecture. */ + t->to_read_description = s390_read_description; + t->to_auxv_parse = s390_auxv_parse; + /* Register the target. */ linux_nat_add_target (t); linux_nat_set_new_thread (t, s390_fix_watch_points);