| From 3f506a7c6a8f7b746102276f3c41a7b11bd7ac3c Mon Sep 17 00:00:00 2001 |
| From: Mahesh Bodapati <mbodapat@xilinx.com> |
| Date: Mon, 23 Jan 2017 19:07:44 +0530 |
| Subject: [PATCH 33/52] Add initial port of linux gdbserver add |
| gdb_proc_service_h to gdbserver microblaze-linux |
| |
| gdbserver needs to initialise the microblaze registers |
| |
| other archs use this step to run a *_arch_setup() to carry out all |
| architecture specific setup - may need to add in future |
| |
| * add linux-ptrace.o to gdbserver configure |
| * Update breakpoint opcode |
| * fix segfault on connecting gdbserver |
| * add microblaze_linux_memory_remove_breakpoint |
| * add set_solib_svr4_fetch_link_map_offsets |
| * add set_gdbarch_fetch_tls_load_module_address |
| * Force reading of r0 as 0, prevent stores |
| |
| Signed-off-by: David Holsgrove <david.holsgrove@petalogix.com> |
| Signed-off-by: Nathan Rossi <nathan.rossi@petalogix.com> |
| --- |
| gdb/configure.host | 3 + |
| gdb/microblaze-linux-tdep.c | 29 ++++- |
| gdb/microblaze-tdep.c | 35 +++++- |
| gdb/microblaze-tdep.h | 4 +- |
| gdb/regformats/reg-microblaze.dat | 41 +++++++ |
| gdbserver/linux-microblaze-low.c | 189 ++++++++++++++++++++++++++++++ |
| 6 files changed, 298 insertions(+), 3 deletions(-) |
| create mode 100644 gdb/regformats/reg-microblaze.dat |
| create mode 100644 gdbserver/linux-microblaze-low.c |
| |
| diff --git a/gdb/configure.host b/gdb/configure.host |
| index ce52823729..cf1a08e8b2 100644 |
| --- a/gdb/configure.host |
| +++ b/gdb/configure.host |
| @@ -65,6 +65,7 @@ hppa*) gdb_host_cpu=pa ;; |
| i[34567]86*) gdb_host_cpu=i386 ;; |
| m68*) gdb_host_cpu=m68k ;; |
| mips*) gdb_host_cpu=mips ;; |
| +microblaze*) gdb_host_cpu=microblaze ;; |
| powerpc* | rs6000) gdb_host_cpu=powerpc ;; |
| sparcv9 | sparc64) gdb_host_cpu=sparc ;; |
| s390*) gdb_host_cpu=s390 ;; |
| @@ -133,6 +134,8 @@ mips*-*-netbsd* | mips*-*-knetbsd*-gnu) |
| mips*-*-freebsd*) gdb_host=fbsd ;; |
| mips64*-*-openbsd*) gdb_host=obsd64 ;; |
| |
| +microblaze*-*linux*) gdb_host=linux ;; |
| + |
| powerpc-*-aix* | rs6000-*-* | powerpc64-*-aix*) |
| gdb_host=aix ;; |
| powerpc*-*-freebsd*) gdb_host=fbsd ;; |
| diff --git a/gdb/microblaze-linux-tdep.c b/gdb/microblaze-linux-tdep.c |
| index be710bedb6..fb8241884b 100644 |
| --- a/gdb/microblaze-linux-tdep.c |
| +++ b/gdb/microblaze-linux-tdep.c |
| @@ -37,6 +37,22 @@ |
| #include "tramp-frame.h" |
| #include "linux-tdep.h" |
| |
| +static int microblaze_debug_flag = 0; |
| + |
| +static void |
| +microblaze_debug (const char *fmt, ...) |
| +{ |
| + if (microblaze_debug_flag) |
| + { |
| + va_list args; |
| + |
| + va_start (args, fmt); |
| + printf_unfiltered ("MICROBLAZE LINUX: "); |
| + vprintf_unfiltered (fmt, args); |
| + va_end (args); |
| + } |
| +} |
| + |
| static int |
| microblaze_linux_memory_remove_breakpoint (struct gdbarch *gdbarch, |
| struct bp_target_info *bp_tgt) |
| @@ -46,18 +62,25 @@ microblaze_linux_memory_remove_breakpoint (struct gdbarch *gdbarch, |
| int val; |
| int bplen; |
| gdb_byte old_contents[BREAKPOINT_MAX]; |
| + struct cleanup *cleanup; |
| |
| /* Determine appropriate breakpoint contents and size for this address. */ |
| bp = gdbarch_breakpoint_from_pc (gdbarch, &addr, &bplen); |
| |
| + /* Make sure we see the memory breakpoints. */ |
| + cleanup = make_show_memory_breakpoints_cleanup (1); |
| val = target_read_memory (addr, old_contents, bplen); |
| |
| /* If our breakpoint is no longer at the address, this means that the |
| program modified the code on us, so it is wrong to put back the |
| old value. */ |
| if (val == 0 && memcmp (bp, old_contents, bplen) == 0) |
| - val = target_write_raw_memory (addr, bp_tgt->shadow_contents, bplen); |
| + { |
| + val = target_write_raw_memory (addr, bp_tgt->shadow_contents, bplen); |
| + microblaze_debug ("microblaze_linux_memory_remove_breakpoint writing back to memory at addr 0x%lx\n", addr); |
| + } |
| |
| + do_cleanups (cleanup); |
| return val; |
| } |
| |
| @@ -129,6 +152,10 @@ microblaze_linux_init_abi (struct gdbarch_info info, |
| /* Trampolines. */ |
| tramp_frame_prepend_unwinder (gdbarch, |
| µblaze_linux_sighandler_tramp_frame); |
| + |
| + /* Enable TLS support. */ |
| + set_gdbarch_fetch_tls_load_module_address (gdbarch, |
| + svr4_fetch_objfile_link_map); |
| } |
| |
| void _initialize_microblaze_linux_tdep (); |
| diff --git a/gdb/microblaze-tdep.c b/gdb/microblaze-tdep.c |
| index 5c80413304..443adfb364 100644 |
| --- a/gdb/microblaze-tdep.c |
| +++ b/gdb/microblaze-tdep.c |
| @@ -137,7 +137,38 @@ microblaze_fetch_instruction (CORE_ADDR pc) |
| constexpr gdb_byte microblaze_break_insn[] = MICROBLAZE_BREAKPOINT; |
| |
| typedef BP_MANIPULATION (microblaze_break_insn) microblaze_breakpoint; |
| - |
| +static int |
| +microblaze_linux_memory_remove_breakpoint (struct gdbarch *gdbarch, |
| + struct bp_target_info *bp_tgt) |
| +{ |
| + CORE_ADDR addr = bp_tgt->placed_address; |
| + const unsigned char *bp; |
| + int val; |
| + int bplen; |
| + gdb_byte old_contents[BREAKPOINT_MAX]; |
| + struct cleanup *cleanup; |
| + |
| + /* Determine appropriate breakpoint contents and size for this address. */ |
| + bp = gdbarch_breakpoint_from_pc (gdbarch, &addr, &bplen); |
| + if (bp == NULL) |
| + error (_("Software breakpoints not implemented for this target.")); |
| + |
| + /* Make sure we see the memory breakpoints. */ |
| + cleanup = make_show_memory_breakpoints_cleanup (1); |
| + val = target_read_memory (addr, old_contents, bplen); |
| + |
| + /* If our breakpoint is no longer at the address, this means that the |
| + program modified the code on us, so it is wrong to put back the |
| + old value. */ |
| + if (val == 0 && memcmp (bp, old_contents, bplen) == 0) |
| + { |
| + val = target_write_raw_memory (addr, bp_tgt->shadow_contents, bplen); |
| + microblaze_debug ("microblaze_linux_memory_remove_breakpoint writing back to memory at addr 0x%lx\n", addr); |
| + } |
| + |
| + do_cleanups (cleanup); |
| + return val; |
| +} |
| |
| /* Allocate and initialize a frame cache. */ |
| |
| @@ -731,6 +762,7 @@ microblaze_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) |
| microblaze_breakpoint::kind_from_pc); |
| set_gdbarch_sw_breakpoint_from_kind (gdbarch, |
| microblaze_breakpoint::bp_from_kind); |
| + set_gdbarch_memory_remove_breakpoint (gdbarch, microblaze_linux_memory_remove_breakpoint); |
| |
| set_gdbarch_frame_args_skip (gdbarch, 8); |
| |
| @@ -771,4 +803,5 @@ When non-zero, microblaze specific debugging is enabled."), |
| NULL, |
| &setdebuglist, &showdebuglist); |
| |
| + |
| } |
| diff --git a/gdb/microblaze-tdep.h b/gdb/microblaze-tdep.h |
| index 4fbdf9933f..db0772643d 100644 |
| --- a/gdb/microblaze-tdep.h |
| +++ b/gdb/microblaze-tdep.h |
| @@ -117,6 +117,8 @@ struct microblaze_frame_cache |
| |
| /* MICROBLAZE_BREAKPOINT defines the breakpoint that should be used. |
| Only used for native debugging. */ |
| -#define MICROBLAZE_BREAKPOINT {0xb9, 0xcc, 0x00, 0x60} |
| +#define MICROBLAZE_BREAKPOINT {0xba, 0x0c, 0x00, 0x18} |
| +#define MICROBLAZE_BREAKPOINT_LE {0x18, 0x00, 0x0c, 0xba} |
| + |
| |
| #endif /* microblaze-tdep.h */ |
| diff --git a/gdb/regformats/reg-microblaze.dat b/gdb/regformats/reg-microblaze.dat |
| new file mode 100644 |
| index 0000000000..bd8a438442 |
| --- /dev/null |
| +++ b/gdb/regformats/reg-microblaze.dat |
| @@ -0,0 +1,41 @@ |
| +name:microblaze |
| +expedite:r1,pc |
| +32:r0 |
| +32:r1 |
| +32:r2 |
| +32:r3 |
| +32:r4 |
| +32:r5 |
| +32:r6 |
| +32:r7 |
| +32:r8 |
| +32:r9 |
| +32:r10 |
| +32:r11 |
| +32:r12 |
| +32:r13 |
| +32:r14 |
| +32:r15 |
| +32:r16 |
| +32:r17 |
| +32:r18 |
| +32:r19 |
| +32:r20 |
| +32:r21 |
| +32:r22 |
| +32:r23 |
| +32:r24 |
| +32:r25 |
| +32:r26 |
| +32:r27 |
| +32:r28 |
| +32:r29 |
| +32:r30 |
| +32:r31 |
| +32:pc |
| +32:msr |
| +32:ear |
| +32:esr |
| +32:fsr |
| +32:slr |
| +32:shr |
| diff --git a/gdbserver/linux-microblaze-low.c b/gdbserver/linux-microblaze-low.c |
| new file mode 100644 |
| index 0000000000..cba5d6fc58 |
| --- /dev/null |
| +++ b/gdbserver/linux-microblaze-low.c |
| @@ -0,0 +1,189 @@ |
| +/* GNU/Linux/Microblaze specific low level interface, for the remote server for |
| + GDB. |
| + Copyright (C) 1995-2013 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 "server.h" |
| +#include "linux-low.h" |
| + |
| +#include <asm/ptrace.h> |
| +#include <sys/procfs.h> |
| +#include <sys/ptrace.h> |
| + |
| +#include "gdb_proc_service.h" |
| + |
| +static int microblaze_regmap[] = |
| + {PT_GPR(0), PT_GPR(1), PT_GPR(2), PT_GPR(3), |
| + PT_GPR(4), PT_GPR(5), PT_GPR(6), PT_GPR(7), |
| + PT_GPR(8), PT_GPR(9), PT_GPR(10), PT_GPR(11), |
| + PT_GPR(12), PT_GPR(13), PT_GPR(14), PT_GPR(15), |
| + PT_GPR(16), PT_GPR(17), PT_GPR(18), PT_GPR(19), |
| + PT_GPR(20), PT_GPR(21), PT_GPR(22), PT_GPR(23), |
| + PT_GPR(24), PT_GPR(25), PT_GPR(26), PT_GPR(27), |
| + PT_GPR(28), PT_GPR(29), PT_GPR(30), PT_GPR(31), |
| + PT_PC, PT_MSR, PT_EAR, PT_ESR, |
| + PT_FSR |
| + }; |
| + |
| +#define microblaze_num_regs (sizeof microblaze_regmap / sizeof microblaze_regmap[0]) |
| + |
| +/* Defined in auto-generated file microblaze-linux.c. */ |
| +void init_registers_microblaze (void); |
| + |
| +static int |
| +microblaze_cannot_store_register (int regno) |
| +{ |
| + if (microblaze_regmap[regno] == -1 || regno == 0) |
| + return 1; |
| + |
| + return 0; |
| +} |
| + |
| +static int |
| +microblaze_cannot_fetch_register (int regno) |
| +{ |
| + return 0; |
| +} |
| + |
| +static CORE_ADDR |
| +microblaze_get_pc (struct regcache *regcache) |
| +{ |
| + unsigned long pc; |
| + |
| + collect_register_by_name (regcache, "pc", &pc); |
| + return (CORE_ADDR) pc; |
| +} |
| + |
| +static void |
| +microblaze_set_pc (struct regcache *regcache, CORE_ADDR pc) |
| +{ |
| + unsigned long newpc = pc; |
| + |
| + supply_register_by_name (regcache, "pc", &newpc); |
| +} |
| + |
| +/* dbtrap insn */ |
| +/* brki r16, 0x18; */ |
| +static const unsigned long microblaze_breakpoint = 0xba0c0018; |
| +#define microblaze_breakpoint_len 4 |
| + |
| +static int |
| +microblaze_breakpoint_at (CORE_ADDR where) |
| +{ |
| + unsigned long insn; |
| + |
| + (*the_target->read_memory) (where, (unsigned char *) &insn, 4); |
| + if (insn == microblaze_breakpoint) |
| + return 1; |
| + /* If necessary, recognize more trap instructions here. GDB only uses the |
| + one. */ |
| + return 0; |
| +} |
| + |
| +static CORE_ADDR |
| +microblaze_reinsert_addr (struct regcache *regcache) |
| +{ |
| + unsigned long pc; |
| + collect_register_by_name (regcache, "r15", &pc); |
| + return pc; |
| +} |
| + |
| +#ifdef HAVE_PTRACE_GETREGS |
| + |
| +static void |
| +microblaze_collect_ptrace_register (struct regcache *regcache, int regno, char *buf) |
| +{ |
| + int size = register_size (regno); |
| + |
| + memset (buf, 0, sizeof (long)); |
| + |
| + if (size < sizeof (long)) |
| + collect_register (regcache, regno, buf + sizeof (long) - size); |
| + else |
| + collect_register (regcache, regno, buf); |
| +} |
| + |
| +static void |
| +microblaze_supply_ptrace_register (struct regcache *regcache, |
| + int regno, const char *buf) |
| +{ |
| + int size = register_size (regno); |
| + |
| + if (regno == 0) { |
| + unsigned long regbuf_0 = 0; |
| + /* clobbering r0 so that it is always 0 as enforced by hardware */ |
| + supply_register (regcache, regno, (const char*)®buf_0); |
| + } else { |
| + if (size < sizeof (long)) |
| + supply_register (regcache, regno, buf + sizeof (long) - size); |
| + else |
| + supply_register (regcache, regno, buf); |
| + } |
| +} |
| + |
| +/* Provide only a fill function for the general register set. ps_lgetregs |
| + will use this for NPTL support. */ |
| + |
| +static void microblaze_fill_gregset (struct regcache *regcache, void *buf) |
| +{ |
| + int i; |
| + |
| + for (i = 0; i < 32; i++) |
| + microblaze_collect_ptrace_register (regcache, i, (char *) buf + microblaze_regmap[i]); |
| +} |
| + |
| +static void |
| +microblaze_store_gregset (struct regcache *regcache, const void *buf) |
| +{ |
| + int i; |
| + |
| + for (i = 0; i < 32; i++) |
| + supply_register (regcache, i, (char *) buf + microblaze_regmap[i]); |
| +} |
| + |
| +#endif /* HAVE_PTRACE_GETREGS */ |
| + |
| +struct regset_info target_regsets[] = { |
| +#ifdef HAVE_PTRACE_GETREGS |
| + { PTRACE_GETREGS, PTRACE_SETREGS, 0, sizeof (elf_gregset_t), GENERAL_REGS, microblaze_fill_gregset, microblaze_store_gregset }, |
| + { 0, 0, 0, -1, -1, NULL, NULL }, |
| +#endif /* HAVE_PTRACE_GETREGS */ |
| + { 0, 0, 0, -1, -1, NULL, NULL } |
| +}; |
| + |
| +struct linux_target_ops the_low_target = { |
| + init_registers_microblaze, |
| + microblaze_num_regs, |
| + microblaze_regmap, |
| + NULL, |
| + microblaze_cannot_fetch_register, |
| + microblaze_cannot_store_register, |
| + NULL, /* fetch_register */ |
| + microblaze_get_pc, |
| + microblaze_set_pc, |
| + (const unsigned char *) µblaze_breakpoint, |
| + microblaze_breakpoint_len, |
| + microblaze_reinsert_addr, |
| + 0, |
| + microblaze_breakpoint_at, |
| + NULL, |
| + NULL, |
| + NULL, |
| + NULL, |
| + microblaze_collect_ptrace_register, |
| + microblaze_supply_ptrace_register, |
| +}; |
| -- |
| 2.17.1 |
| |