diff options
Diffstat (limited to 'recipes-microblaze/gdb/files/0009-Patch-microblaze-Initial-support-for-native-gdb.patch')
-rw-r--r-- | recipes-microblaze/gdb/files/0009-Patch-microblaze-Initial-support-for-native-gdb.patch | 495 |
1 files changed, 495 insertions, 0 deletions
diff --git a/recipes-microblaze/gdb/files/0009-Patch-microblaze-Initial-support-for-native-gdb.patch b/recipes-microblaze/gdb/files/0009-Patch-microblaze-Initial-support-for-native-gdb.patch new file mode 100644 index 00000000..1ad6b89c --- /dev/null +++ b/recipes-microblaze/gdb/files/0009-Patch-microblaze-Initial-support-for-native-gdb.patch @@ -0,0 +1,495 @@ +From c7b918283f2e438deef78aae84f6b748a7a1bb54 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 09/16] [Patch, microblaze]: 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> +Upstream-Status: Pending +--- + gdb/Makefile.in | 4 +- + gdb/config/microblaze/linux.mh | 10 + + gdb/microblaze-linux-nat.c | 431 +++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 443 insertions(+), 2 deletions(-) + 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 f2c16ec..12870a4 100644 +--- a/gdb/Makefile.in ++++ b/gdb/Makefile.in +@@ -904,7 +904,7 @@ top.h bsd-kvm.h gdb-stabs.h reggroups.h \ + annotate.h sim-regno.h dictionary.h dfp.h main.h frame-unwind.h \ + remote-fileio.h i386-linux-tdep.h vax-tdep.h objc-lang.h \ + sentinel-frame.h bcache.h symfile.h windows-tdep.h linux-tdep.h \ +-gdb_usleep.h jit.h xml-syscall.h microblaze-tdep.h \ ++gdb_usleep.h jit.h xml-syscall.h microblaze-tdep.h microblaze-linux-tdep.h \ + psymtab.h psympriv.h progspace.h bfin-tdep.h ia64-hpux-tdep.h \ + amd64-darwin-tdep.h charset-list.h \ + config/djgpp/langinfo.h config/djgpp/nl_types.h darwin-nat.h \ +@@ -1631,7 +1631,7 @@ ALLDEPFILES = \ + m68kbsd-nat.c m68kbsd-tdep.c \ + m68klinux-nat.c m68klinux-tdep.c \ + m88k-tdep.c m88kbsd-nat.c \ +- microblaze-tdep.c microblaze-linux-tdep.c \ ++ microblaze-tdep.c microblaze-linux-nat.c microblaze-linux-tdep.c \ + mingw-hdep.c \ + mips-linux-nat.c mips-linux-tdep.c \ + mips-irix-tdep.c \ +diff --git a/gdb/config/microblaze/linux.mh b/gdb/config/microblaze/linux.mh +new file mode 100644 +index 0000000..547d103 +--- /dev/null ++++ b/gdb/config/microblaze/linux.mh +@@ -0,0 +1,10 @@ ++# 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 \ ++ linux-waitpid.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 0000000..81f8346 +--- /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 <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 = ptid_get_lwp (ptid); ++ if (0 == tid) ++ tid = ptid_get_pid (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); ++} +-- +1.9.0 |