blob: 646914a43fcfa661ee0994313e281bcff128db44 [file] [log] [blame]
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, &microblaze_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