| From eef1384ec08bbbac893e4a564981517f92f90b57 Mon Sep 17 00:00:00 2001 |
| From: David Holsgrove <david.holsgrove@petalogix.com> |
| Date: Fri, 20 Jul 2012 15:18:35 +1000 |
| Subject: [PATCH 39/52] Initial support for native gdb |
| |
| microblaze: Follow PPC method of getting setting registers |
| using PTRACE PEEK/POKE |
| |
| Signed-off-by: David Holsgrove <david.holsgrove@petalogix.com> |
| |
| Conflicts: |
| gdb/Makefile.in |
| --- |
| gdb/Makefile.in | 2 + |
| gdb/config/microblaze/linux.mh | 9 + |
| gdb/microblaze-linux-nat.c | 431 +++++++++++++++++++++++++++++++++ |
| 3 files changed, 442 insertions(+) |
| create mode 100644 gdb/config/microblaze/linux.mh |
| create mode 100644 gdb/microblaze-linux-nat.c |
| |
| diff --git a/gdb/Makefile.in b/gdb/Makefile.in |
| index 9ae9fe2d1e..a44464b983 100644 |
| --- a/gdb/Makefile.in |
| +++ b/gdb/Makefile.in |
| @@ -1328,6 +1328,7 @@ HFILES_NO_SRCDIR = \ |
| memory-map.h \ |
| memrange.h \ |
| microblaze-tdep.h \ |
| + microblaze-linux-tdep.h \ |
| mips-linux-tdep.h \ |
| mips-nbsd-tdep.h \ |
| mips-tdep.h \ |
| @@ -2207,6 +2208,7 @@ ALLDEPFILES = \ |
| m68k-tdep.c \ |
| microblaze-linux-tdep.c \ |
| microblaze-tdep.c \ |
| + microblaze-linux-nat.c \ |
| mingw-hdep.c \ |
| mips-fbsd-nat.c \ |
| mips-fbsd-tdep.c \ |
| diff --git a/gdb/config/microblaze/linux.mh b/gdb/config/microblaze/linux.mh |
| new file mode 100644 |
| index 0000000000..a4eaf540e1 |
| --- /dev/null |
| +++ b/gdb/config/microblaze/linux.mh |
| @@ -0,0 +1,9 @@ |
| +# Host: Microblaze, running Linux |
| + |
| +NAT_FILE= config/nm-linux.h |
| +NATDEPFILES= inf-ptrace.o fork-child.o \ |
| + microblaze-linux-nat.o proc-service.o linux-thread-db.o \ |
| + linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o |
| +NAT_CDEPS = $(srcdir)/proc-service.list |
| + |
| +LOADLIBES = -ldl $(RDYNAMIC) |
| diff --git a/gdb/microblaze-linux-nat.c b/gdb/microblaze-linux-nat.c |
| new file mode 100644 |
| index 0000000000..e9b8c9c522 |
| --- /dev/null |
| +++ b/gdb/microblaze-linux-nat.c |
| @@ -0,0 +1,431 @@ |
| +/* Microblaze GNU/Linux native support. |
| + |
| + Copyright (C) 1988-1989, 1991-1992, 1994, 1996, 2000-2012 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 "arch-utils.h" |
| +#include "dis-asm.h" |
| +#include "frame.h" |
| +#include "trad-frame.h" |
| +#include "symtab.h" |
| +#include "value.h" |
| +#include "gdbcmd.h" |
| +#include "breakpoint.h" |
| +#include "inferior.h" |
| +#include "regcache.h" |
| +#include "target.h" |
| +#include "frame.h" |
| +#include "frame-base.h" |
| +#include "frame-unwind.h" |
| +#include "dwarf2-frame.h" |
| +#include "osabi.h" |
| + |
| +#include "gdb_assert.h" |
| +#include "gdb_string.h" |
| +#include "target-descriptions.h" |
| +#include "opcodes/microblaze-opcm.h" |
| +#include "opcodes/microblaze-dis.h" |
| + |
| +#include "linux-nat.h" |
| +#include "target-descriptions.h" |
| + |
| +#include <sys/user.h> |
| +#include <sys/utsname.h> |
| +#include <sys/procfs.h> |
| +#include <sys/ptrace.h> |
| + |
| +/* Prototypes for supply_gregset etc. */ |
| +#include "gregset.h" |
| + |
| +#include "microblaze-tdep.h" |
| + |
| +#include <elf/common.h> |
| +#include "auxv.h" |
| + |
| +/* Defines ps_err_e, struct ps_prochandle. */ |
| +#include "gdb_proc_service.h" |
| + |
| +/* On GNU/Linux, threads are implemented as pseudo-processes, in which |
| + case we may be tracing more than one process at a time. In that |
| + case, inferior_ptid will contain the main process ID and the |
| + individual thread (process) ID. get_thread_id () is used to get |
| + the thread id if it's available, and the process id otherwise. */ |
| + |
| +int |
| +get_thread_id (ptid_t ptid) |
| +{ |
| + int tid = TIDGET (ptid); |
| + if (0 == tid) |
| + tid = PIDGET (ptid); |
| + return tid; |
| +} |
| + |
| +#define GET_THREAD_ID(PTID) get_thread_id (PTID) |
| + |
| +/* Non-zero if our kernel may support the PTRACE_GETREGS and |
| + PTRACE_SETREGS requests, for reading and writing the |
| + general-purpose registers. Zero if we've tried one of |
| + them and gotten an error. */ |
| +int have_ptrace_getsetregs = 1; |
| + |
| +static int |
| +microblaze_register_u_addr (struct gdbarch *gdbarch, int regno) |
| +{ |
| + int u_addr = -1; |
| + struct gdbarch_tdep *tdep = gdbarch_tdep (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 (long); |
| + |
| + /* General purpose registers occupy 1 slot each in the buffer. */ |
| + if (regno >= MICROBLAZE_R0_REGNUM |
| + && regno <= MICROBLAZE_FSR_REGNUM) |
| + u_addr = (regno * wordsize); |
| + |
| + return u_addr; |
| +} |
| + |
| + |
| +static void |
| +fetch_register (struct regcache *regcache, int tid, int regno) |
| +{ |
| + struct gdbarch *gdbarch = get_regcache_arch (regcache); |
| + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); |
| + /* This isn't really an address. But ptrace thinks of it as one. */ |
| + CORE_ADDR regaddr = microblaze_register_u_addr (gdbarch, regno); |
| + int bytes_transferred; |
| + unsigned int offset; /* Offset of registers within the u area. */ |
| + char buf[MAX_REGISTER_SIZE]; |
| + |
| + if (regaddr == -1) |
| + { |
| + memset (buf, '\0', register_size (gdbarch, regno)); /* Supply zeroes */ |
| + regcache_raw_supply (regcache, regno, buf); |
| + return; |
| + } |
| + |
| + /* Read the raw register using sizeof(long) sized chunks. On a |
| + 32-bit platform, 64-bit floating-point registers will require two |
| + transfers. */ |
| + for (bytes_transferred = 0; |
| + bytes_transferred < register_size (gdbarch, regno); |
| + bytes_transferred += sizeof (long)) |
| + { |
| + long l; |
| + |
| + errno = 0; |
| + l = ptrace (PTRACE_PEEKUSER, tid, (PTRACE_TYPE_ARG3) regaddr, 0); |
| + regaddr += sizeof (long); |
| + if (errno != 0) |
| + { |
| + char message[128]; |
| + sprintf (message, "reading register %s (#%d)", |
| + gdbarch_register_name (gdbarch, regno), regno); |
| + perror_with_name (message); |
| + } |
| + memcpy (&buf[bytes_transferred], &l, sizeof (l)); |
| + } |
| + |
| + /* Now supply the register. Keep in mind that the regcache's idea |
| + of the register's size may not be a multiple of sizeof |
| + (long). */ |
| + if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_LITTLE) |
| + { |
| + /* Little-endian values are always found at the left end of the |
| + bytes transferred. */ |
| + regcache_raw_supply (regcache, regno, buf); |
| + } |
| + else if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) |
| + { |
| + /* Big-endian values are found at the right end of the bytes |
| + transferred. */ |
| + size_t padding = (bytes_transferred - register_size (gdbarch, regno)); |
| + regcache_raw_supply (regcache, regno, buf + padding); |
| + } |
| + else |
| + internal_error (__FILE__, __LINE__, |
| + _("fetch_register: unexpected byte order: %d"), |
| + gdbarch_byte_order (gdbarch)); |
| +} |
| + |
| +/* This function actually issues the request to ptrace, telling |
| + it to get all general-purpose registers and put them into the |
| + specified regset. |
| + |
| + If the ptrace request does not exist, this function returns 0 |
| + and properly sets the have_ptrace_* flag. If the request fails, |
| + this function calls perror_with_name. Otherwise, if the request |
| + succeeds, then the regcache gets filled and 1 is returned. */ |
| +static int |
| +fetch_all_gp_regs (struct regcache *regcache, int tid) |
| +{ |
| + struct gdbarch *gdbarch = get_regcache_arch (regcache); |
| + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); |
| + gdb_gregset_t gregset; |
| + |
| + if (ptrace (PTRACE_GETREGS, tid, 0, (void *) &gregset) < 0) |
| + { |
| + if (errno == EIO) |
| + { |
| + have_ptrace_getsetregs = 0; |
| + return 0; |
| + } |
| + perror_with_name (_("Couldn't get general-purpose registers.")); |
| + } |
| + |
| + supply_gregset (regcache, (const gdb_gregset_t *) &gregset); |
| + |
| + return 1; |
| +} |
| + |
| + |
| +/* This is a wrapper for the fetch_all_gp_regs function. It is |
| + responsible for verifying if this target has the ptrace request |
| + that can be used to fetch all general-purpose registers at one |
| + shot. If it doesn't, then we should fetch them using the |
| + old-fashioned way, which is to iterate over the registers and |
| + request them one by one. */ |
| +static void |
| +fetch_gp_regs (struct regcache *regcache, int tid) |
| +{ |
| + struct gdbarch *gdbarch = get_regcache_arch (regcache); |
| + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); |
| + int i; |
| + |
| + if (have_ptrace_getsetregs) |
| + if (fetch_all_gp_regs (regcache, tid)) |
| + return; |
| + |
| + /* If we've hit this point, it doesn't really matter which |
| + architecture we are using. We just need to read the |
| + registers in the "old-fashioned way". */ |
| + for (i = MICROBLAZE_R0_REGNUM; i <= MICROBLAZE_FSR_REGNUM; i++) |
| + fetch_register (regcache, tid, i); |
| +} |
| + |
| + |
| +static void |
| +store_register (const struct regcache *regcache, int tid, int regno) |
| +{ |
| + struct gdbarch *gdbarch = get_regcache_arch (regcache); |
| + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); |
| + /* This isn't really an address. But ptrace thinks of it as one. */ |
| + CORE_ADDR regaddr = microblaze_register_u_addr (gdbarch, regno); |
| + int i; |
| + size_t bytes_to_transfer; |
| + char buf[MAX_REGISTER_SIZE]; |
| + |
| + if (regaddr == -1) |
| + return; |
| + |
| + /* First collect the register. Keep in mind that the regcache's |
| + idea of the register's size may not be a multiple of sizeof |
| + (long). */ |
| + memset (buf, 0, sizeof buf); |
| + bytes_to_transfer = align_up (register_size (gdbarch, regno), sizeof (long)); |
| + if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_LITTLE) |
| + { |
| + /* Little-endian values always sit at the left end of the buffer. */ |
| + regcache_raw_collect (regcache, regno, buf); |
| + } |
| + else if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) |
| + { |
| + /* Big-endian values sit at the right end of the buffer. */ |
| + size_t padding = (bytes_to_transfer - register_size (gdbarch, regno)); |
| + regcache_raw_collect (regcache, regno, buf + padding); |
| + } |
| + |
| + for (i = 0; i < bytes_to_transfer; i += sizeof (long)) |
| + { |
| + long l; |
| + |
| + memcpy (&l, &buf[i], sizeof (l)); |
| + errno = 0; |
| + ptrace (PTRACE_POKEUSER, tid, (PTRACE_TYPE_ARG3) regaddr, l); |
| + regaddr += sizeof (long); |
| + |
| + if (errno != 0) |
| + { |
| + char message[128]; |
| + sprintf (message, "writing register %s (#%d)", |
| + gdbarch_register_name (gdbarch, regno), regno); |
| + perror_with_name (message); |
| + } |
| + } |
| +} |
| + |
| +/* This function actually issues the request to ptrace, telling |
| + it to store all general-purpose registers present in the specified |
| + regset. |
| + |
| + If the ptrace request does not exist, this function returns 0 |
| + and properly sets the have_ptrace_* flag. If the request fails, |
| + this function calls perror_with_name. Otherwise, if the request |
| + succeeds, then the regcache is stored and 1 is returned. */ |
| +static int |
| +store_all_gp_regs (const struct regcache *regcache, int tid, int regno) |
| +{ |
| + struct gdbarch *gdbarch = get_regcache_arch (regcache); |
| + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); |
| + gdb_gregset_t gregset; |
| + |
| + if (ptrace (PTRACE_GETREGS, tid, 0, (void *) &gregset) < 0) |
| + { |
| + if (errno == EIO) |
| + { |
| + have_ptrace_getsetregs = 0; |
| + return 0; |
| + } |
| + perror_with_name (_("Couldn't get general-purpose registers.")); |
| + } |
| + |
| + fill_gregset (regcache, &gregset, regno); |
| + |
| + if (ptrace (PTRACE_SETREGS, tid, 0, (void *) &gregset) < 0) |
| + { |
| + if (errno == EIO) |
| + { |
| + have_ptrace_getsetregs = 0; |
| + return 0; |
| + } |
| + perror_with_name (_("Couldn't set general-purpose registers.")); |
| + } |
| + |
| + return 1; |
| +} |
| + |
| +/* This is a wrapper for the store_all_gp_regs function. It is |
| + responsible for verifying if this target has the ptrace request |
| + that can be used to store all general-purpose registers at one |
| + shot. If it doesn't, then we should store them using the |
| + old-fashioned way, which is to iterate over the registers and |
| + store them one by one. */ |
| +static void |
| +store_gp_regs (const struct regcache *regcache, int tid, int regno) |
| +{ |
| + struct gdbarch *gdbarch = get_regcache_arch (regcache); |
| + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); |
| + int i; |
| + |
| + if (have_ptrace_getsetregs) |
| + if (store_all_gp_regs (regcache, tid, regno)) |
| + return; |
| + |
| + /* If we hit this point, it doesn't really matter which |
| + architecture we are using. We just need to store the |
| + registers in the "old-fashioned way". */ |
| + for (i = MICROBLAZE_R0_REGNUM; i <= MICROBLAZE_FSR_REGNUM; i++) |
| + store_register (regcache, tid, i); |
| +} |
| + |
| + |
| +/* Fetch registers from the child process. Fetch all registers if |
| + regno == -1, otherwise fetch all general registers or all floating |
| + point registers depending upon the value of regno. */ |
| + |
| +static void |
| +microblaze_linux_fetch_inferior_registers (struct target_ops *ops, |
| + struct regcache *regcache, int regno) |
| +{ |
| + /* Get the thread id for the ptrace call. */ |
| + int tid = GET_THREAD_ID (inferior_ptid); |
| + |
| + if (regno == -1) |
| + fetch_gp_regs (regcache, tid); |
| + else |
| + fetch_register (regcache, tid, regno); |
| +} |
| + |
| +/* Store registers back into the inferior. Store all registers if |
| + regno == -1, otherwise store all general registers or all floating |
| + point registers depending upon the value of regno. */ |
| + |
| +static void |
| +microblaze_linux_store_inferior_registers (struct target_ops *ops, |
| + struct regcache *regcache, int regno) |
| +{ |
| + /* Get the thread id for the ptrace call. */ |
| + int tid = GET_THREAD_ID (inferior_ptid); |
| + |
| + if (regno >= 0) |
| + store_register (regcache, tid, regno); |
| + else |
| + store_gp_regs (regcache, tid, -1); |
| +} |
| + |
| +/* Wrapper functions for the standard regset handling, used by |
| + thread debugging. */ |
| + |
| +void |
| +fill_gregset (const struct regcache *regcache, |
| + gdb_gregset_t *gregsetp, int regno) |
| +{ |
| + microblaze_collect_gregset (NULL, regcache, regno, gregsetp); |
| +} |
| + |
| +void |
| +supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregsetp) |
| +{ |
| + microblaze_supply_gregset (NULL, regcache, -1, gregsetp); |
| +} |
| + |
| +void |
| +fill_fpregset (const struct regcache *regcache, |
| + gdb_fpregset_t *fpregsetp, int regno) |
| +{ |
| + /* FIXME. */ |
| +} |
| + |
| +void |
| +supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregsetp) |
| +{ |
| + /* FIXME. */ |
| +} |
| + |
| +static const struct target_desc * |
| +microblaze_linux_read_description (struct target_ops *ops) |
| +{ |
| + CORE_ADDR microblaze_hwcap = 0; |
| + |
| + if (target_auxv_search (ops, AT_HWCAP, µblaze_hwcap) != 1) |
| + return NULL; |
| + |
| + return NULL; |
| +} |
| + |
| + |
| +void _initialize_microblaze_linux_nat (void); |
| + |
| +void |
| +_initialize_microblaze_linux_nat (void) |
| +{ |
| + struct target_ops *t; |
| + |
| + /* Fill in the generic GNU/Linux methods. */ |
| + t = linux_target (); |
| + |
| + /* Add our register access methods. */ |
| + t->to_fetch_registers = microblaze_linux_fetch_inferior_registers; |
| + t->to_store_registers = microblaze_linux_store_inferior_registers; |
| + |
| + t->to_read_description = microblaze_linux_read_description; |
| + |
| + /* Register the target. */ |
| + linux_nat_add_target (t); |
| +} |
| -- |
| 2.17.1 |
| |