diff -Nur gdb-7.7.orig/bfd/bfd-in2.h gdb-7.7/bfd/bfd-in2.h --- gdb-7.7.orig/bfd/bfd-in2.h 2014-02-06 03:21:29.000000000 +0100 +++ gdb-7.7/bfd/bfd-in2.h 2014-03-26 19:43:36.000000000 +0100 @@ -5378,6 +5378,11 @@ expressions of the form "Symbol Op Symbol" */ BFD_RELOC_MICROBLAZE_32_SYM_OP_SYM, +/* This is a 32 bit reloc that stores the 32 bit pc relative +value in two words (with an imm instruction). No relocation is +done here - only used for relaxing */ + BFD_RELOC_MICROBLAZE_32_NONE, + /* This is a 64 bit reloc that stores the 32 bit pc relative value in two words (with an imm instruction). No relocation is done here - only used for relaxing */ diff -Nur gdb-7.7.orig/bfd/elf32-microblaze.c gdb-7.7/bfd/elf32-microblaze.c --- gdb-7.7.orig/bfd/elf32-microblaze.c 2013-12-08 05:55:47.000000000 +0100 +++ gdb-7.7/bfd/elf32-microblaze.c 2014-03-27 08:30:48.000000000 +0100 @@ -177,6 +177,20 @@ FALSE), /* PC relative offset? */ /* This reloc does nothing. Used for relaxation. */ + HOWTO (R_MICROBLAZE_32_NONE, /* Type. */ + 0, /* Rightshift. */ + 2, /* Size (0 = byte, 1 = short, 2 = long). */ + 32, /* Bitsize. */ + TRUE, /* PC_relative. */ + 0, /* Bitpos. */ + complain_overflow_bitfield, /* Complain on overflow. */ + NULL, /* Special Function. */ + "R_MICROBLAZE_32_NONE",/* Name. */ + FALSE, /* Partial Inplace. */ + 0, /* Source Mask. */ + 0, /* Dest Mask. */ + FALSE), /* PC relative offset? */ + HOWTO (R_MICROBLAZE_64_NONE, /* Type. */ 0, /* Rightshift. */ 2, /* Size (0 = byte, 1 = short, 2 = long). */ @@ -532,7 +546,10 @@ case BFD_RELOC_NONE: microblaze_reloc = R_MICROBLAZE_NONE; break; - case BFD_RELOC_MICROBLAZE_64_NONE: + case BFD_RELOC_MICROBLAZE_32_NONE: + microblaze_reloc = R_MICROBLAZE_32_NONE; + break; + case BFD_RELOC_MICROBLAZE_64_NONE: microblaze_reloc = R_MICROBLAZE_64_NONE; break; case BFD_RELOC_32: @@ -668,6 +685,67 @@ return _bfd_elf_is_local_label_name (abfd, name); } +/* Support for core dump NOTE sections. */ +static bfd_boolean +microblaze_elf_grok_prstatus (bfd *abfd, Elf_Internal_Note *note) +{ + int offset; + unsigned int size; + + switch (note->descsz) + { + default: + return FALSE; + + case 228: /* Linux/MicroBlaze */ + /* pr_cursig */ + elf_tdata (abfd)->core->signal = bfd_get_16 (abfd, note->descdata + 12); + + /* pr_pid */ + elf_tdata (abfd)->core->pid = bfd_get_32 (abfd, note->descdata + 24); + + /* pr_reg */ + offset = 72; + size = 50 * 4; + + break; + } + + /* Make a ".reg/999" section. */ + return _bfd_elfcore_make_pseudosection (abfd, ".reg", + size, note->descpos + offset); +} + +static bfd_boolean +microblaze_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note) +{ + switch (note->descsz) + { + default: + return FALSE; + + case 128: /* Linux/MicroBlaze elf_prpsinfo */ + elf_tdata (abfd)->core->program + = _bfd_elfcore_strndup (abfd, note->descdata + 32, 16); + elf_tdata (abfd)->core->command + = _bfd_elfcore_strndup (abfd, note->descdata + 48, 80); + } + + /* Note that for some reason, a spurious space is tacked + onto the end of the args in some (at least one anyway) + implementations, so strip it off if it exists. */ + + { + char *command = elf_tdata (abfd)->core->command; + int n = strlen (command); + + if (0 < n && command[n - 1] == ' ') + command[n - 1] = '\0'; + } + + return TRUE; +} + /* The microblaze linker (like many others) needs to keep track of the number of relocs that it decides to copy as dynamic relocs in check_relocs for each symbol. This is so that it can later discard @@ -1023,7 +1101,7 @@ { /* External symbol. */ bfd_boolean warned ATTRIBUTE_UNUSED; - bfd_boolean ignored ATTRIBUTE_UNUSED; + bfd_boolean ignored; RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel, r_symndx, symtab_hdr, sym_hashes, @@ -1852,14 +1930,22 @@ } break; case R_MICROBLAZE_NONE: + case R_MICROBLAZE_32_NONE: { /* This was a PC-relative instruction that was completely resolved. */ int sfix, efix; + unsigned int val; bfd_vma target_address; target_address = irel->r_addend + irel->r_offset; sfix = calc_fixup (irel->r_offset, 0, sec); efix = calc_fixup (target_address, 0, sec); + + /* Validate the in-band val. */ + val = bfd_get_32 (abfd, contents + irel->r_offset); + if (val != irel->r_addend && ELF32_R_TYPE (irel->r_info) == R_MICROBLAZE_32_NONE) { + fprintf(stderr, "%d: CORRUPT relax reloc %x %lx\n", __LINE__, val, irel->r_addend); + } irel->r_addend -= (efix - sfix); /* Should use HOWTO. */ microblaze_bfd_write_imm_value_32 (abfd, contents + irel->r_offset, @@ -1907,6 +1993,49 @@ irelscanend = irelocs + o->reloc_count; for (irelscan = irelocs; irelscan < irelscanend; irelscan++) { + if (1 && ELF32_R_TYPE (irelscan->r_info) == (int) R_MICROBLAZE_32_NONE) + { + unsigned int val; + + isym = isymbuf + ELF32_R_SYM (irelscan->r_info); + + /* hax: We only do the following fixup for debug location lists. */ + if (strcmp(".debug_loc", o->name)) + continue; + + /* This was a PC-relative instruction that was completely resolved. */ + if (ocontents == NULL) + { + if (elf_section_data (o)->this_hdr.contents != NULL) + ocontents = elf_section_data (o)->this_hdr.contents; + else + { + /* We always cache the section contents. + Perhaps, if info->keep_memory is FALSE, we + should free them, if we are permitted to. */ + + if (o->rawsize == 0) + o->rawsize = o->size; + ocontents = (bfd_byte *) bfd_malloc (o->rawsize); + if (ocontents == NULL) + goto error_return; + if (!bfd_get_section_contents (abfd, o, ocontents, + (file_ptr) 0, + o->rawsize)) + goto error_return; + elf_section_data (o)->this_hdr.contents = ocontents; + } + } + + val = bfd_get_32 (abfd, ocontents + irelscan->r_offset); + if (val != irelscan->r_addend) { + fprintf(stderr, "%d: CORRUPT relax reloc! %x %lx\n", __LINE__, val, irelscan->r_addend); + } + + irelscan->r_addend -= calc_fixup (irelscan->r_addend, 0, sec); + microblaze_bfd_write_imm_value_32 (abfd, ocontents + irelscan->r_offset, + irelscan->r_addend); + } if (ELF32_R_TYPE (irelscan->r_info) == (int) R_MICROBLAZE_32) { isym = isymbuf + ELF32_R_SYM (irelscan->r_info); @@ -1966,7 +2095,7 @@ elf_section_data (o)->this_hdr.contents = ocontents; } } - irelscan->r_addend -= calc_fixup (irel->r_addend + irelscan->r_addend -= calc_fixup (irelscan->r_addend + isym->st_value, 0, sec); @@ -3506,4 +3635,7 @@ #define elf_backend_size_dynamic_sections microblaze_elf_size_dynamic_sections #define elf_backend_add_symbol_hook microblaze_elf_add_symbol_hook +#define elf_backend_grok_prstatus microblaze_elf_grok_prstatus +#define elf_backend_grok_psinfo microblaze_elf_grok_psinfo + #include "elf32-target.h" diff -Nur gdb-7.7.orig/bfd/libbfd.h gdb-7.7/bfd/libbfd.h --- gdb-7.7.orig/bfd/libbfd.h 2014-02-06 03:21:29.000000000 +0100 +++ gdb-7.7/bfd/libbfd.h 2014-03-26 20:02:00.000000000 +0100 @@ -2615,6 +2615,7 @@ "BFD_RELOC_MICROBLAZE_32_ROSDA", "BFD_RELOC_MICROBLAZE_32_RWSDA", "BFD_RELOC_MICROBLAZE_32_SYM_OP_SYM", + "BFD_RELOC_MICROBLAZE_32_NONE", "BFD_RELOC_MICROBLAZE_64_NONE", "BFD_RELOC_MICROBLAZE_64_GOTPC", "BFD_RELOC_MICROBLAZE_64_GOT", diff -Nur gdb-7.7.orig/gdb/Makefile.in gdb-7.7/gdb/Makefile.in --- gdb-7.7.orig/gdb/Makefile.in 2014-02-06 03:21:29.000000000 +0100 +++ gdb-7.7/gdb/Makefile.in 2014-03-26 20:33:32.000000000 +0100 @@ -849,7 +849,7 @@ 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 \ @@ -1547,7 +1547,7 @@ 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 -Nur gdb-7.7.orig/gdb/config/microblaze/linux.mh gdb-7.7/gdb/config/microblaze/linux.mh --- gdb-7.7.orig/gdb/config/microblaze/linux.mh 1970-01-01 01:00:00.000000000 +0100 +++ gdb-7.7/gdb/config/microblaze/linux.mh 2014-03-24 15:52:56.000000000 +0100 @@ -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 -Nur gdb-7.7.orig/gdb/configure.host gdb-7.7/gdb/configure.host --- gdb-7.7.orig/gdb/configure.host 2013-12-08 05:55:47.000000000 +0100 +++ gdb-7.7/gdb/configure.host 2014-03-26 20:34:44.000000000 +0100 @@ -59,6 +59,7 @@ m68*) gdb_host_cpu=m68k ;; m88*) gdb_host_cpu=m88k ;; 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 @@ gdb_host=nbsd ;; mips64*-*-openbsd*) gdb_host=obsd64 ;; +microblaze*-*linux*) gdb_host=linux ;; + powerpc-*-aix* | rs6000-*-* | powerpc64-*-aix*) gdb_host=aix ;; powerpc*-*-freebsd*) gdb_host=fbsd ;; diff -Nur gdb-7.7.orig/gdb/configure.tgt gdb-7.7/gdb/configure.tgt --- gdb-7.7.orig/gdb/configure.tgt 2014-02-06 03:21:29.000000000 +0100 +++ gdb-7.7/gdb/configure.tgt 2014-03-26 20:36:23.000000000 +0100 @@ -340,9 +340,10 @@ microblaze*-linux-*|microblaze*-*-linux*) # Target: Xilinx MicroBlaze running Linux - gdb_target_obs="microblaze-tdep.o microblaze-linux-tdep.o microblaze-rom.o \ + gdb_target_obs="microblaze-tdep.o microblaze-linux-tdep.o microblaze-rom.o glibc-tdep.o \ monitor.o dsrec.o solib-svr4.o symfile-mem.o linux-tdep.o" gdb_sim=../sim/microblaze/libsim.a + build_gdbserver=yes ;; microblaze*-*-*) # Target: Xilinx MicroBlaze running standalone diff -Nur gdb-7.7.orig/gdb/gdbserver/Makefile.in gdb-7.7/gdb/gdbserver/Makefile.in --- gdb-7.7.orig/gdb/gdbserver/Makefile.in 2014-02-06 03:21:29.000000000 +0100 +++ gdb-7.7/gdb/gdbserver/Makefile.in 2014-03-26 20:39:22.000000000 +0100 @@ -148,6 +148,7 @@ $(srcdir)/linux-ia64-low.c $(srcdir)/linux-low.c \ $(srcdir)/linux-m32r-low.c \ $(srcdir)/linux-m68k-low.c $(srcdir)/linux-mips-low.c \ + $(srcdir)/linux-microblaze-low.c \ $(srcdir)/linux-nios2-low.c \ $(srcdir)/linux-ppc-low.c \ $(srcdir)/linux-s390-low.c \ @@ -328,6 +329,7 @@ rm -f arm-with-iwmmxt.c rm -f arm-with-vfpv2.c arm-with-vfpv3.c arm-with-neon.c rm -f mips-linux.c mips64-linux.c + rm -f microblaze-linux.c rm -f nios2-linux.c rm -f powerpc-32.c powerpc-32l.c powerpc-64l.c powerpc-e500l.c rm -f powerpc-altivec32l.c powerpc-cell32l.c powerpc-vsx32l.c @@ -600,6 +602,8 @@ $(SHELL) $(regdat_sh) $(srcdir)/../regformats/mips64-linux.dat mips64-linux.c mips64-dsp-linux.c : $(srcdir)/../regformats/mips64-dsp-linux.dat $(regdat_sh) $(SHELL) $(regdat_sh) $(srcdir)/../regformats/mips64-dsp-linux.dat mips64-dsp-linux.c +microblaze-linux.c : $(srcdir)/../regformats/reg-microblaze.dat $(regdat_sh) + $(SHELL) $(regdat_sh) $(srcdir)/../regformats/reg-microblaze.dat microblaze-linux.c nios2-linux.c : $(srcdir)/../regformats/nios2-linux.dat $(regdat_sh) $(SHELL) $(regdat_sh) $(srcdir)/../regformats/nios2-linux.dat nios2-linux.c powerpc-32.c : $(srcdir)/../regformats/rs6000/powerpc-32.dat $(regdat_sh) diff -Nur gdb-7.7.orig/gdb/gdbserver/configure.srv gdb-7.7/gdb/gdbserver/configure.srv --- gdb-7.7.orig/gdb/gdbserver/configure.srv 2014-01-08 10:23:36.000000000 +0100 +++ gdb-7.7/gdb/gdbserver/configure.srv 2014-03-26 20:40:44.000000000 +0100 @@ -198,6 +198,13 @@ srv_linux_usrregs=yes srv_linux_thread_db=yes ;; + microblaze*-*-linux*) srv_regobj=microblaze-linux.o + srv_tgtobj="$srv_linux_obj linux-microblaze-low.o" + srv_linux_usrregs=yes + srv_linux_regsets=yes + srv_linux_thread_db=yes + ;; + nios2*-*-linux*) srv_regobj="nios2-linux.o" srv_tgtobj="$srv_linux_obj linux-nios2-low.o" srv_xmlfiles="nios2-linux.xml" diff -Nur gdb-7.7.orig/gdb/gdbserver/linux-microblaze-low.c gdb-7.7/gdb/gdbserver/linux-microblaze-low.c --- gdb-7.7.orig/gdb/gdbserver/linux-microblaze-low.c 1970-01-01 01:00:00.000000000 +0100 +++ gdb-7.7/gdb/gdbserver/linux-microblaze-low.c 2014-03-26 20:41:13.000000000 +0100 @@ -0,0 +1,228 @@ +/* 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 . */ + +#include "server.h" +#include "linux-low.h" + +#include +#include +#include + +#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); +extern const struct target_desc *tdesc_microblaze; + +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 (regcache->tdesc, 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 (regcache->tdesc, 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 */ + +static struct regset_info microblaze_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 } +}; + +static struct regsets_info microblaze_regsets_info = + { + microblaze_regsets, /* regsets */ + 0, /* num_regsets */ + NULL, /* disabled_regsets */ + }; + +static struct usrregs_info microblaze_usrregs_info = + { + microblaze_num_regs, + microblaze_regmap, + }; + +static struct regs_info regs_info = + { + NULL, /* regset_bitmap */ + µblaze_usrregs_info, + µblaze_regsets_info + }; + +static const struct regs_info * +microblaze_regs_info (void) +{ + return ®s_info; +} + +static void +microblaze_arch_setup (void) +{ + current_process ()->tdesc = tdesc_microblaze; +} + +struct linux_target_ops the_low_target = { + microblaze_arch_setup, + microblaze_regs_info, + 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, +}; + +void +initialize_low_arch (void) +{ + init_registers_microblaze (); + + initialize_regsets_info (µblaze_regsets_info); +} \ No newline at end of file diff -Nur gdb-7.7.orig/gdb/microblaze-linux-nat.c gdb-7.7/gdb/microblaze-linux-nat.c --- gdb-7.7.orig/gdb/microblaze-linux-nat.c 1970-01-01 01:00:00.000000000 +0100 +++ gdb-7.7/gdb/microblaze-linux-nat.c 2014-03-27 09:51:56.000000000 +0100 @@ -0,0 +1,430 @@ +/* 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 . */ + +#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 "target-descriptions.h" +#include "opcodes/microblaze-opcm.h" +#include "opcodes/microblaze-dis.h" + +#include "linux-nat.h" +#include "target-descriptions.h" + +#include +#include +#include +#include + +/* Prototypes for supply_gregset etc. */ +#include "gregset.h" + +#include "microblaze-tdep.h" + +#include +#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); +} diff -Nur gdb-7.7.orig/gdb/microblaze-linux-tdep.c gdb-7.7/gdb/microblaze-linux-tdep.c --- gdb-7.7.orig/gdb/microblaze-linux-tdep.c 2014-01-08 10:23:36.000000000 +0100 +++ gdb-7.7/gdb/microblaze-linux-tdep.c 2014-03-26 10:11:41.000000000 +0100 @@ -1,6 +1,6 @@ /* Target-dependent code for Xilinx MicroBlaze. - Copyright (C) 2009-2014 Free Software Foundation, Inc. + Copyright (C) 2009-2013 Free Software Foundation, Inc. This file is part of GDB. @@ -32,11 +32,28 @@ #include "regset.h" #include "solib-svr4.h" #include "microblaze-tdep.h" +#include "glibc-tdep.h" #include "trad-frame.h" #include "frame-unwind.h" #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,20 +63,27 @@ 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); + { + 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; } @@ -116,6 +140,43 @@ microblaze_linux_sighandler_cache_init }; +const struct microblaze_gregset microblaze_linux_core_gregset; + +static void +microblaze_linux_supply_core_gregset (const struct regset *regset, + struct regcache *regcache, + int regnum, const void *gregs, size_t len) +{ + microblaze_supply_gregset (µblaze_linux_core_gregset, regcache, + regnum, gregs); +} + +static void +microblaze_linux_collect_core_gregset (const struct regset *regset, + const struct regcache *regcache, + int regnum, void *gregs, size_t len) +{ + microblaze_collect_gregset (µblaze_linux_core_gregset, regcache, + regnum, gregs); +} + +static void +microblaze_linux_supply_core_fpregset (const struct regset *regset, + struct regcache *regcache, + int regnum, const void *fpregs, size_t len) +{ + /* FIXME. */ + microblaze_supply_fpregset (regcache, regnum, fpregs); +} + +static void +microblaze_linux_collect_core_fpregset (const struct regset *regset, + const struct regcache *regcache, + int regnum, void *fpregs, size_t len) +{ + /* FIXME. */ + microblaze_collect_fpregset (regcache, regnum, fpregs); +} static void microblaze_linux_init_abi (struct gdbarch_info info, @@ -123,6 +184,10 @@ { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + tdep->gregset = regset_alloc (gdbarch, microblaze_linux_supply_core_gregset, + microblaze_linux_collect_core_gregset); + tdep->sizeof_gregset = 200; + linux_init_abi (info, gdbarch); set_gdbarch_memory_remove_breakpoint (gdbarch, @@ -135,6 +200,25 @@ /* Trampolines. */ tramp_frame_prepend_unwinder (gdbarch, µblaze_linux_sighandler_tramp_frame); + + /* BFD target for core files. */ + if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) + set_gdbarch_gcore_bfd_target (gdbarch, "elf32-microblaze"); + else + set_gdbarch_gcore_bfd_target (gdbarch, "elf32-microblazeel"); + + + /* Shared library handling. */ + set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); + set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver); + + set_gdbarch_regset_from_core_section (gdbarch, + microblaze_regset_from_core_section); + + /* Enable TLS support. */ + set_gdbarch_fetch_tls_load_module_address (gdbarch, + svr4_fetch_objfile_link_map); + } /* -Wmissing-prototypes */ diff -Nur gdb-7.7.orig/gdb/microblaze-rom.c gdb-7.7/gdb/microblaze-rom.c --- gdb-7.7.orig/gdb/microblaze-rom.c 2014-01-08 10:23:36.000000000 +0100 +++ gdb-7.7/gdb/microblaze-rom.c 2014-03-27 08:56:48.000000000 +0100 @@ -1,6 +1,6 @@ /* Remote debugging interface to Xilinx MicroBlaze. - Copyright (C) 2009-2014 Free Software Foundation, Inc. + Copyright (C) 2009-2013 Free Software Foundation, Inc. This file is part of GDB. @@ -21,7 +21,6 @@ #include "gdbcore.h" #include "target.h" #include "monitor.h" -#include #include "serial.h" #include "regcache.h" diff -Nur gdb-7.7.orig/gdb/microblaze-tdep.c gdb-7.7/gdb/microblaze-tdep.c --- gdb-7.7.orig/gdb/microblaze-tdep.c 2014-02-06 03:21:29.000000000 +0100 +++ gdb-7.7/gdb/microblaze-tdep.c 2014-03-27 08:46:21.000000000 +0100 @@ -1,6 +1,6 @@ /* Target-dependent code for Xilinx MicroBlaze. - Copyright (C) 2009-2014 Free Software Foundation, Inc. + Copyright (C) 2009-2013 Free Software Foundation, Inc. This file is part of GDB. @@ -29,13 +29,13 @@ #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 #include "target-descriptions.h" #include "opcodes/microblaze-opcm.h" #include "opcodes/microblaze-dis.h" @@ -73,7 +73,8 @@ "rpc", "rmsr", "rear", "resr", "rfsr", "rbtr", "rpvr0", "rpvr1", "rpvr2", "rpvr3", "rpvr4", "rpvr5", "rpvr6", "rpvr7", "rpvr8", "rpvr9", "rpvr10", "rpvr11", - "redr", "rpid", "rzpr", "rtlbx", "rtlbsx", "rtlblo", "rtlbhi" + "redr", "rpid", "rzpr", "rtlbx", "rtlbsx", "rtlblo", "rtlbhi", + "rslr", "rshr" }; #define MICROBLAZE_NUM_REGS ARRAY_SIZE (microblaze_register_names) @@ -145,6 +146,14 @@ return sp; } +static CORE_ADDR +microblaze_store_arguments (struct regcache *regcache, int nargs, + struct value **args, CORE_ADDR sp, + int struct_return, CORE_ADDR struct_addr) +{ + error (_("store_arguments not implemented")); + return sp; +} static CORE_ADDR microblaze_push_dummy_call (struct gdbarch *gdbarch, struct value *function, @@ -156,14 +165,52 @@ return sp; } +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; +} + static const gdb_byte * microblaze_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pc, int *len) { + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); static gdb_byte break_insn[] = MICROBLAZE_BREAKPOINT; + static gdb_byte break_insn_le[] = MICROBLAZE_BREAKPOINT_LE; *len = sizeof (break_insn); - return break_insn; + if (byte_order == BFD_ENDIAN_BIG) + return break_insn; + else + return break_insn_le; } /* Allocate and initialize a frame cache. */ @@ -178,6 +225,7 @@ /* Base address. */ cache->base = 0; cache->pc = 0; + cache->saved_sp = 0; /* Frameless until proven otherwise. */ cache->frameless_p = 1; @@ -234,6 +282,8 @@ int flags = 0; int save_hidden_pointer_found = 0; int non_stack_instruction_found = 0; + int n_insns; + unsigned int *insn_block; /* Find the start of this function. */ find_pc_partial_function (pc, &name, &func_addr, &func_end); @@ -273,11 +323,18 @@ name, paddress (gdbarch, func_addr), paddress (gdbarch, stop)); +/* Do a block read to minimize the transaction with the Debug Agent */ + n_insns = (stop == func_addr) ? 1 : ((stop - func_addr) / INST_WORD_SIZE); + insn_block = calloc(n_insns, sizeof(unsigned long)); + + target_read_memory (func_addr, (void*) insn_block, n_insns * INST_WORD_SIZE ); + for (addr = func_addr; addr < stop; addr += INST_WORD_SIZE) { insn = microblaze_fetch_instruction (addr); + //insn = insn_block[(addr - func_addr) / INST_WORD_SIZE]; op = microblaze_decode_insn (insn, &rd, &ra, &rb, &imm); - microblaze_debug ("%s %08lx\n", paddress (gdbarch, pc), insn); + microblaze_debug ("%s %08lx op=%x r%d r%d imm=%d\n", paddress (gdbarch, addr), insn, op, rd, ra, imm); /* This code is very sensitive to what functions are present in the prologue. It assumes that the (addi, addik, swi, sw) can be the @@ -291,6 +348,7 @@ cache->frameless_p = 0; /* Frame found. */ save_hidden_pointer_found = 0; non_stack_instruction_found = 0; + cache->register_offsets[rd] = -imm; continue; } else if (IS_SPILL_SP(op, rd, ra)) @@ -401,8 +459,8 @@ part of the prologue. */ if (save_hidden_pointer_found) prologue_end_addr -= INST_WORD_SIZE; - - return prologue_end_addr; + free(insn_block); + return prologue_end_addr; } static CORE_ADDR @@ -452,6 +510,7 @@ return start_pc; } +enum { REG_UNAVAIL = (CORE_ADDR) -1 }; /* Normal frames. */ static struct microblaze_frame_cache * @@ -459,7 +518,7 @@ { struct microblaze_frame_cache *cache; struct gdbarch *gdbarch = get_frame_arch (next_frame); - CORE_ADDR func; + CORE_ADDR current_pc; int rn; if (*this_cache) @@ -473,9 +532,18 @@ for (rn = 0; rn < gdbarch_num_regs (gdbarch); rn++) cache->register_offsets[rn] = -1; - func = get_frame_func (next_frame); + cache->pc = get_frame_func (next_frame); + current_pc = get_frame_pc (next_frame); + + if (cache->pc) + microblaze_analyze_prologue (gdbarch, cache->pc, current_pc, + cache); - cache->pc = get_frame_address_in_block (next_frame); + cache->base = get_frame_register_unsigned (next_frame, gdbarch_sp_regnum (gdbarch)); + cache->saved_sp = cache->base + cache->framesize; + + cache->register_offsets[MICROBLAZE_PREV_PC_REGNUM] = cache->base; + cache->register_offsets[MICROBLAZE_SP_REGNUM] = cache->saved_sp; return cache; } @@ -501,6 +569,14 @@ struct microblaze_frame_cache *cache = microblaze_frame_cache (this_frame, this_cache); + if ((regnum == MICROBLAZE_SP_REGNUM && + cache->register_offsets[MICROBLAZE_SP_REGNUM]) + || (regnum == MICROBLAZE_FP_REGNUM && + cache->register_offsets[MICROBLAZE_SP_REGNUM])) + + return frame_unwind_got_constant (this_frame, regnum, + cache->register_offsets[MICROBLAZE_SP_REGNUM]); + if (cache->frameless_p) { if (regnum == MICROBLAZE_PC_REGNUM) @@ -508,11 +584,18 @@ if (regnum == MICROBLAZE_SP_REGNUM) regnum = 1; return trad_frame_get_prev_register (this_frame, - cache->saved_regs, regnum); + cache->saved_regs, regnum); } - else - return trad_frame_get_prev_register (this_frame, cache->saved_regs, - regnum); + + if (regnum == MICROBLAZE_PC_REGNUM) + { + regnum = 15; + return frame_unwind_got_memory (this_frame, regnum, + cache->register_offsets[MICROBLAZE_PREV_PC_REGNUM]); + } + + return trad_frame_get_prev_register (this_frame, cache->saved_regs, + regnum); } @@ -536,6 +619,12 @@ return cache->base; } +static const struct frame_unwind * +microblaze_frame_sniffer (struct frame_info *next_frame) +{ + return µblaze_frame_unwind; +} + static const struct frame_base microblaze_frame_base = { µblaze_frame_unwind, @@ -628,6 +717,109 @@ return (TYPE_LENGTH (type) == 16); } +int +microblaze_software_single_step (struct frame_info *frame) +{ + struct gdbarch *arch = get_frame_arch (frame); + struct address_space *aspace = get_frame_address_space (frame); + struct gdbarch_tdep *tdep = gdbarch_tdep (arch); + static char le_breakp[] = MICROBLAZE_BREAKPOINT_LE; + static char be_breakp[] = MICROBLAZE_BREAKPOINT; + enum bfd_endian byte_order = gdbarch_byte_order (arch); + char *breakp = byte_order == BFD_ENDIAN_BIG ? be_breakp : le_breakp; + int ret = 0; + + /* Save the address and the values of the next_pc and the target */ + static struct sstep_breaks + { + CORE_ADDR address; + bfd_boolean valid; + /* Shadow contents. */ + char data[INST_WORD_SIZE]; + } stepbreaks[2]; + int ii; + + if (1) + { + CORE_ADDR pc; + long insn; + enum microblaze_instr minstr; + bfd_boolean isunsignednum; + enum microblaze_instr_type insn_type; + short delay_slots; + int imm; + bfd_boolean immfound = FALSE; + + /* Set a breakpoint at the next instruction */ + /* If the current instruction is an imm, set it at the inst after */ + /* If the instruction has a delay slot, skip the delay slot */ + pc = get_frame_pc (frame); + insn = microblaze_fetch_instruction (pc); + minstr = get_insn_microblaze (insn, &isunsignednum, &insn_type, &delay_slots); + if (insn_type == immediate_inst) + { + int rd, ra, rb; + immfound = TRUE; + minstr = microblaze_decode_insn (insn, &rd, &ra, &rb, &imm); + pc = pc + INST_WORD_SIZE; + insn = microblaze_fetch_instruction (pc); + minstr = get_insn_microblaze (insn, &isunsignednum, &insn_type, &delay_slots); + } + stepbreaks[0].address = pc + (delay_slots * INST_WORD_SIZE) + INST_WORD_SIZE; + if (insn_type != return_inst) { + stepbreaks[0].valid = TRUE; + } else { + stepbreaks[0].valid = FALSE; + } + + microblaze_debug ("single-step insn_type=%x insn=%x\n", insn_type, insn); + /* Now check for branch or return instructions */ + if (insn_type == branch_inst || insn_type == return_inst) { + int limm; + int lrd, lra, lrb; + int ra, rb; + bfd_boolean targetvalid; + bfd_boolean unconditionalbranch; + microblaze_decode_insn(insn, &lrd, &lra, &lrb, &limm); + if (lra >= 0 && lra < MICROBLAZE_NUM_REGS) + ra = get_frame_register_unsigned (frame, lra); + else + ra = 0; + if (lrb >= 0 && lrb < MICROBLAZE_NUM_REGS) + rb = get_frame_register_unsigned (frame, lrb); + else + rb = 0; + + stepbreaks[1].address = microblaze_get_target_address (insn, immfound, imm, pc, ra, rb, &targetvalid, &unconditionalbranch); + microblaze_debug ("single-step uncondbr=%d targetvalid=%d target=%x\n", unconditionalbranch, targetvalid, stepbreaks[1].address); + + if (unconditionalbranch) + stepbreaks[0].valid = FALSE; /* This is a unconditional branch: will not come to the next address */ + if (targetvalid && (stepbreaks[0].valid == FALSE || + (stepbreaks[0].address != stepbreaks[1].address)) + && (stepbreaks[1].address != pc)) { + stepbreaks[1].valid = TRUE; + } else { + stepbreaks[1].valid = FALSE; + } + } else { + stepbreaks[1].valid = FALSE; + } + + /* Insert the breakpoints */ + for (ii = 0; ii < 2; ++ii) + { + + /* ignore invalid breakpoint. */ + if (stepbreaks[ii].valid) { + insert_single_step_breakpoint (arch, aspace, stepbreaks[ii].address); + ret = 1; + } + } + } + return ret; +} + static void microblaze_write_pc (struct regcache *regcache, CORE_ADDR pc) { @@ -664,6 +856,70 @@ return dwarf2_to_reg_map[reg]; } + +void +microblaze_supply_gregset (const struct microblaze_gregset *gregset, + struct regcache *regcache, + int regnum, const void *gregs) +{ + unsigned int *regs = gregs; + if (regnum >= 0) + regcache_raw_supply (regcache, regnum, regs + regnum); + + if (regnum == -1) { + int i; + + for (i = 0; i < 50; i++) { + regcache_raw_supply (regcache, i, regs + i); + } + } +} + + +void +microblaze_collect_gregset (const struct microblaze_gregset *gregset, + const struct regcache *regcache, + int regnum, void *gregs) +{ + /* FIXME. */ +} + +void +microblaze_supply_fpregset (struct regcache *regcache, + int regnum, const void *fpregs) +{ + /* FIXME. */ +} + +void +microblaze_collect_fpregset (const struct regcache *regcache, + int regnum, void *fpregs) +{ + /* FIXME. */ +} + + +/* Return the appropriate register set for the core section identified + by SECT_NAME and SECT_SIZE. */ + +const struct regset * +microblaze_regset_from_core_section (struct gdbarch *gdbarch, + const char *sect_name, size_t sect_size) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + microblaze_debug ("microblaze_regset_from_core_section, sect_name = %s\n", sect_name); + + if (strcmp (sect_name, ".reg") == 0 && sect_size >= tdep->sizeof_gregset) + return tdep->gregset; + + if (strcmp (sect_name, ".reg2") == 0 && sect_size >= tdep->sizeof_fpregset) + return tdep->fpregset; + + microblaze_debug ("microblaze_regset_from_core_section returning null :-( \n"); + return NULL; +} + static struct gdbarch * microblaze_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) { @@ -679,6 +935,11 @@ tdep = XMALLOC (struct gdbarch_tdep); gdbarch = gdbarch_alloc (&info, tdep); + tdep->gregset = NULL; + tdep->sizeof_gregset = 0; + tdep->fpregset = NULL; + tdep->sizeof_fpregset = 0; + set_gdbarch_long_double_bit (gdbarch, 128); set_gdbarch_num_regs (gdbarch, MICROBLAZE_NUM_REGS); @@ -706,7 +967,10 @@ /* Stack grows downward. */ set_gdbarch_inner_than (gdbarch, core_addr_lessthan); + set_gdbarch_memory_remove_breakpoint (gdbarch, microblaze_linux_memory_remove_breakpoint); + set_gdbarch_breakpoint_from_pc (gdbarch, microblaze_breakpoint_from_pc); + set_gdbarch_software_single_step (gdbarch, microblaze_software_single_step); set_gdbarch_frame_args_skip (gdbarch, 8); @@ -725,6 +989,13 @@ dwarf2_append_unwinders (gdbarch); frame_unwind_append_unwinder (gdbarch, µblaze_frame_unwind); frame_base_append_sniffer (gdbarch, dwarf2_frame_base_sniffer); + //frame_base_append_sniffer (gdbarch, microblaze_frame_sniffer); + + /* If we have register sets, enable the generic core file support. */ + if (tdep->gregset) { + set_gdbarch_regset_from_core_section (gdbarch, + microblaze_regset_from_core_section); + } return gdbarch; } diff -Nur gdb-7.7.orig/gdb/microblaze-tdep.h gdb-7.7/gdb/microblaze-tdep.h --- gdb-7.7.orig/gdb/microblaze-tdep.h 2014-01-08 10:23:36.000000000 +0100 +++ gdb-7.7/gdb/microblaze-tdep.h 2014-03-26 10:11:41.000000000 +0100 @@ -1,6 +1,6 @@ /* Target-dependent code for Xilinx MicroBlaze. - Copyright (C) 2009-2014 Free Software Foundation, Inc. + Copyright (C) 2009-2013 Free Software Foundation, Inc. This file is part of GDB. @@ -22,8 +22,22 @@ /* Microblaze architecture-specific information. */ +struct microblaze_gregset +{ + unsigned int gregs[32]; + unsigned int fpregs[32]; + unsigned int pregs[16]; +}; + struct gdbarch_tdep { + int dummy; // declare something. + + /* Register sets. */ + struct regset *gregset; + size_t sizeof_gregset; + struct regset *fpregset; + size_t sizeof_fpregset; }; struct microblaze_frame_cache @@ -42,7 +56,8 @@ int fp_regnum; /* Offsets to saved registers. */ - int register_offsets[57]; /* Must match MICROBLAZE_NUM_REGS. */ + int register_offsets[59]; /* Must match MICROBLAZE_NUM_REGS. */ + CORE_ADDR saved_sp; /* Table of saved registers. */ struct trad_frame_saved_reg *saved_regs; @@ -66,11 +81,11 @@ MICROBLAZE_R12_REGNUM, MICROBLAZE_R13_REGNUM, MICROBLAZE_R14_REGNUM, - MICROBLAZE_R15_REGNUM, + MICROBLAZE_R15_REGNUM, MICROBLAZE_PREV_PC_REGNUM = MICROBLAZE_R15_REGNUM, MICROBLAZE_R16_REGNUM, MICROBLAZE_R17_REGNUM, MICROBLAZE_R18_REGNUM, - MICROBLAZE_R19_REGNUM, + MICROBLAZE_R19_REGNUM, MICROBLAZE_FP_REGNUM = MICROBLAZE_R19_REGNUM, MICROBLAZE_R20_REGNUM, MICROBLAZE_R21_REGNUM, MICROBLAZE_R22_REGNUM, @@ -107,7 +122,9 @@ MICROBLAZE_RTLBX_REGNUM, MICROBLAZE_RTLBSX_REGNUM, MICROBLAZE_RTLBLO_REGNUM, - MICROBLAZE_RTLBHI_REGNUM + MICROBLAZE_RTLBHI_REGNUM, + MICROBLAZE_SLR_REGNUM, + MICROBLAZE_SHR_REGNUM }; /* All registers are 32 bits. */ @@ -115,6 +132,21 @@ /* 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} + +extern void microblaze_supply_gregset (const struct microblaze_gregset *gregset, + struct regcache *regcache, + int regnum, const void *gregs); +extern void microblaze_collect_gregset (const struct microblaze_gregset *gregset, + const struct regcache *regcache, + int regnum, void *gregs); +extern void microblaze_supply_fpregset (struct regcache *regcache, + int regnum, const void *fpregs); +extern void microblaze_collect_fpregset (const struct regcache *regcache, + int regnum, void *fpregs); + +extern const struct regset * microblaze_regset_from_core_section (struct gdbarch *gdbarch, + const char *sect_name, size_t sect_size); #endif /* microblaze-tdep.h */ diff -Nur gdb-7.7.orig/gdb/regformats/reg-microblaze.dat gdb-7.7/gdb/regformats/reg-microblaze.dat --- gdb-7.7.orig/gdb/regformats/reg-microblaze.dat 1970-01-01 01:00:00.000000000 +0100 +++ gdb-7.7/gdb/regformats/reg-microblaze.dat 2014-03-26 20:43:54.000000000 +0100 @@ -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 -Nur gdb-7.7.orig/include/elf/microblaze.h gdb-7.7/include/elf/microblaze.h --- gdb-7.7.orig/include/elf/microblaze.h 2013-12-08 05:11:51.000000000 +0100 +++ gdb-7.7/include/elf/microblaze.h 2014-03-26 20:44:34.000000000 +0100 @@ -58,6 +58,7 @@ RELOC_NUMBER (R_MICROBLAZE_TLSDTPREL64, 27) /* TLS Offset Within TLS Block */ RELOC_NUMBER (R_MICROBLAZE_TLSGOTTPREL32, 28) /* TLS Offset From Thread Pointer */ RELOC_NUMBER (R_MICROBLAZE_TLSTPREL32, 29) /* TLS Offset From Thread Pointer */ + RELOC_NUMBER (R_MICROBLAZE_32_NONE, 30) END_RELOC_NUMBERS (R_MICROBLAZE_max) diff -Nur gdb-7.7.orig/opcodes/microblaze-opc.h gdb-7.7/opcodes/microblaze-opc.h --- gdb-7.7.orig/opcodes/microblaze-opc.h 2013-12-08 05:11:52.000000000 +0100 +++ gdb-7.7/opcodes/microblaze-opc.h 2014-03-26 20:45:46.000000000 +0100 @@ -91,6 +91,7 @@ #define OPCODE_MASK_H3 0xFC000600 /* High 6 bits and bits 21, 22. */ #define OPCODE_MASK_H32 0xFC00FC00 /* High 6 bits and bit 16-21. */ #define OPCODE_MASK_H34B 0xFC0000FF /* High 6 bits and low 8 bits. */ +#define OPCODE_MASK_H35B 0xFC0004FF /* High 6 bits and low 9 bits. */ #define OPCODE_MASK_H34C 0xFC0007E0 /* High 6 bits and bits 21-26. */ /* New Mask for msrset, msrclr insns. */ @@ -101,7 +102,7 @@ #define DELAY_SLOT 1 #define NO_DELAY_SLOT 0 -#define MAX_OPCODES 289 +#define MAX_OPCODES 291 struct op_code_struct { @@ -174,7 +175,9 @@ {"wic", INST_TYPE_R1_R2_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000068, OPCODE_MASK_H34B, wic, special_inst }, {"wdc", INST_TYPE_R1_R2_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000064, OPCODE_MASK_H34B, wdc, special_inst }, {"wdc.clear", INST_TYPE_R1_R2_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000066, OPCODE_MASK_H34B, wdcclear, special_inst }, + {"wdc.ext.clear", INST_TYPE_R1_R2_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000466, OPCODE_MASK_H35B, wdcextclear, special_inst }, {"wdc.flush", INST_TYPE_R1_R2_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000074, OPCODE_MASK_H34B, wdcflush, special_inst }, + {"wdc.ext.flush", INST_TYPE_R1_R2_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000476, OPCODE_MASK_H35B, wdcextflush, special_inst }, {"mts", INST_TYPE_SPECIAL_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_MTS, 0x9400C000, OPCODE_MASK_H13S, mts, special_inst }, {"mfs", INST_TYPE_RD_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_MFS, 0x94008000, OPCODE_MASK_H23S, mfs, special_inst }, {"br", INST_TYPE_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x98000000, OPCODE_MASK_H124, br, branch_inst }, diff -Nur gdb-7.7.orig/opcodes/microblaze-opcm.h gdb-7.7/opcodes/microblaze-opcm.h --- gdb-7.7.orig/opcodes/microblaze-opcm.h 2013-12-08 05:11:52.000000000 +0100 +++ gdb-7.7/opcodes/microblaze-opcm.h 2014-03-26 20:45:46.000000000 +0100 @@ -31,9 +31,9 @@ idiv, idivu, bsll, bsra, bsrl, get, put, nget, nput, cget, cput, ncget, ncput, muli, bslli, bsrai, bsrli, mului, or, and, xor, andn, pcmpbf, pcmpbc, pcmpeq, pcmpne, sra, src, srl, sext8, sext16, - wic, wdc, wdcclear, wdcflush, mts, mfs, mbar, br, brd, - brld, bra, brad, brald, microblaze_brk, beq, beqd, bne, bned, blt, - bltd, ble, bled, bgt, bgtd, bge, bged, ori, andi, xori, andni, + wic, wdc, wdcclear, wdcextclear, wdcflush, wdcextflush, mts, mfs, mbar, + br, brd, brld, bra, brad, brald, microblaze_brk, beq, beqd, bne, bned, + blt, bltd, ble, bled, bgt, bgtd, bge, bged, ori, andi, xori, andni, imm, rtsd, rtid, rtbd, rted, bri, brid, brlid, brai, braid, bralid, brki, beqi, beqid, bnei, bneid, blti, bltid, blei, bleid, bgti, bgtid, bgei, bgeid, lbu, lbur, lhu, lhur, lw, lwr, lwx, sb, sbr, sh,