[PATCH 3/3] gdbserver: Add powerpc fast tracepoint support.

Marcin Kościelnicki
2016-03-13T02:32:18Z

gdb/gdbserver/ChangeLog:

2016-03-13 Wei-cheng Wang <cole945@gmail.com>
Marcin KoÅ›cielnicki <koriakin@0x04.net>

* Makefile.in: Add powerpc-*-ipa.o
* configure.srv: Add ipa_obj for powerpc*-linux.
* linux-ppc-ipa.c: New file.
* linux-ppc-low.c: Added linux-ppc-tdesc.h, ax.h,
tracepoint.h includes.
(PPC_FIELD): New macro.
(PPC_SEXT): New macro.
(PPC_OP6): New macro.
(PPC_BO): New macro.
(PPC_LI): New macro.
(PPC_BD): New macro.
(init_registers_*): Move prototype to linux-ppc-tdesc.h.
(tdesc_*): Move declaration to linux-ppc-tdesc.h.
(ppc_get_thread_area): New function.
(gen_ds_form): New function.
(GEN_STD): New macro.
(GEN_STDU): New macro.
(GEN_LD): New macro.
(GEN_LDU): New macro.
(gen_d_form): New function.
(GEN_ADDI): New macro.
(GEN_ADDIS): New macro.
(GEN_LI): New macro.
(GEN_LIS): New macro.
(GEN_ORI): New macro.
(GEN_ORIS): New macro.
(GEN_LWZ): New macro.
(GEN_STW): New macro.
(GEN_STWU): New macro.
(gen_xfx_form): New function.
(GEN_MFSPR): New macro.
(GEN_MTSPR): New macro.
(GEN_MFCR): New macro.
(GEN_MTCR): New macro.
(GEN_SYNC): New macro.
(GEN_LWSYNC): New macro.
(gen_x_form): New function.
(GEN_OR): New macro.
(GEN_MR): New macro.
(GEN_LWARX): New macro.
(GEN_STWCX): New macro.
(GEN_CMPW): New macro.
(gen_md_form): New function.
(GEN_RLDICL): New macro.
(GEN_RLDICR): New macro.
(gen_i_form): New function.
(GEN_B): New macro.
(GEN_BL): New macro.
(gen_b_form): New function.
(GEN_BNE): New macro.
(GEN_LOAD): New macro.
(GEN_STORE): New macro.
(gen_limm): New function.
(gen_atomic_xchg): New function.
(gen_call): New function.
(ppc_relocate_instruction): New function.
(ppc_install_fast_tracepoint_jump_pad): New function.
(ppc_get_min_fast_tracepoint_insn_len): New function.
(ppc_get_ipa_tdesc_idx): New function.
(the_low_target): Wire in the new functions.
(initialize_low_arch) [!__powerpc64__]: Don'it initialize 64-bit
tdescs.
* linux-ppc-tdesc.h: New file.
---
Most of this patch comes from
https://sourceware.org/ml/gdb-patches/2015-07/msg00353.html , but some
fixes have been added (mostly for 32-bit and 64-bit ELFv1), and some
updates were needed (for get_ipa_tdesc_idx etc.).

I'm not sure how to properly determine whether ELFv1 or ELFv2 is in use
by the target, so I assume it's the same ABI as in gdbserver itself.

Allocating the jump pad buffer needs a mention. On powerpc64, IPA simply
scans from 0x10000000 (the executable load address) downwards until it
find a free address. However, on 32-bit, ld.so loads dynamic libraries
there, and they can easily cause us to exceed the 32MiB branch range -
so, instead I aim right after the executable, at sbrk(0). This will
of course cause further memory allocation via brk to fail, but glibc
transparently falls back to mmap in this case, so that's not a problem.
Of course, given a program with large data/bss section, this might
exceed the branch range as well, so perhaps some better heuristic is
in order here.

gdb/gdbserver/ChangeLog | 67 ++++
gdb/gdbserver/Makefile.in | 48 +++
gdb/gdbserver/configure.srv | 2 +
gdb/gdbserver/linux-ppc-ipa.c | 238 +++++++++++
gdb/gdbserver/linux-ppc-low.c | 856 +++++++++++++++++++++++++++++++++++++---
gdb/gdbserver/linux-ppc-tdesc.h | 101 +++++
6 files changed, 1249 insertions(+), 63 deletions(-)
create mode 100644 gdb/gdbserver/linux-ppc-ipa.c
create mode 100644 gdb/gdbserver/linux-ppc-tdesc.h

diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index 4a4a9bf..a6bba77 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,3 +1,70 @@
+2016-03-13 Wei-cheng Wang <cole945@gmail.com>
+ Marcin KoÅ›cielnicki <koriakin@0x04.net>
+
+ * Makefile.in: Add powerpc-*-ipa.o
+ * configure.srv: Add ipa_obj for powerpc*-linux.
+ * linux-ppc-ipa.c: New file.
+ * linux-ppc-low.c: Added linux-ppc-tdesc.h, ax.h,
+ tracepoint.h includes.
+ (PPC_FIELD): New macro.
+ (PPC_SEXT): New macro.
+ (PPC_OP6): New macro.
+ (PPC_BO): New macro.
+ (PPC_LI): New macro.
+ (PPC_BD): New macro.
+ (init_registers_*): Move prototype to linux-ppc-tdesc.h.
+ (tdesc_*): Move declaration to linux-ppc-tdesc.h.
+ (ppc_get_thread_area): New function.
+ (gen_ds_form): New function.
+ (GEN_STD): New macro.
+ (GEN_STDU): New macro.
+ (GEN_LD): New macro.
+ (GEN_LDU): New macro.
+ (gen_d_form): New function.
+ (GEN_ADDI): New macro.
+ (GEN_ADDIS): New macro.
+ (GEN_LI): New macro.
+ (GEN_LIS): New macro.
+ (GEN_ORI): New macro.
+ (GEN_ORIS): New macro.
+ (GEN_LWZ): New macro.
+ (GEN_STW): New macro.
+ (GEN_STWU): New macro.
+ (gen_xfx_form): New function.
+ (GEN_MFSPR): New macro.
+ (GEN_MTSPR): New macro.
+ (GEN_MFCR): New macro.
+ (GEN_MTCR): New macro.
+ (GEN_SYNC): New macro.
+ (GEN_LWSYNC): New macro.
+ (gen_x_form): New function.
+ (GEN_OR): New macro.
+ (GEN_MR): New macro.
+ (GEN_LWARX): New macro.
+ (GEN_STWCX): New macro.
+ (GEN_CMPW): New macro.
+ (gen_md_form): New function.
+ (GEN_RLDICL): New macro.
+ (GEN_RLDICR): New macro.
+ (gen_i_form): New function.
+ (GEN_B): New macro.
+ (GEN_BL): New macro.
+ (gen_b_form): New function.
+ (GEN_BNE): New macro.
+ (GEN_LOAD): New macro.
+ (GEN_STORE): New macro.
+ (gen_limm): New function.
+ (gen_atomic_xchg): New function.
+ (gen_call): New function.
+ (ppc_relocate_instruction): New function.
+ (ppc_install_fast_tracepoint_jump_pad): New function.
+ (ppc_get_min_fast_tracepoint_insn_len): New function.
+ (ppc_get_ipa_tdesc_idx): New function.
+ (the_low_target): Wire in the new functions.
+ (initialize_low_arch) [!__powerpc64__]: Don'it initialize 64-bit
+ tdescs.
+ * linux-ppc-tdesc.h: New file.
+
2016-03-13 Marcin KoÅ›cielnicki <koriakin@0x04.net>

* linux-aarch64-ipa.c: Add <sys/mman.h> include.
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index 257e9bf..11908d2 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -531,6 +531,54 @@ linux-aarch64-ipa.o: linux-aarch64-ipa.c
aarch64-ipa.o: aarch64.c
$(IPAGENT_COMPILE) $<
$(POSTCOMPILE)
+linux-ppc-ipa.o: linux-ppc-ipa.c
+ $(IPAGENT_COMPILE) $<
+ $(POSTCOMPILE)
+powerpc-32l-ipa.o: powerpc-32l.c
+ $(IPAGENT_COMPILE) $<
+ $(POSTCOMPILE)
+powerpc-altivec32l-ipa.o: powerpc-altivec32l.c
+ $(IPAGENT_COMPILE) $<
+ $(POSTCOMPILE)
+powerpc-cell32l-ipa.o: powerpc-cell32l.c
+ $(IPAGENT_COMPILE) $<
+ $(POSTCOMPILE)
+powerpc-vsx32l-ipa.o: powerpc-vsx32l.c
+ $(IPAGENT_COMPILE) $<
+ $(POSTCOMPILE)
+powerpc-isa205-32l-ipa.o: powerpc-isa205-32l.c
+ $(IPAGENT_COMPILE) $<
+ $(POSTCOMPILE)
+powerpc-isa205-altivec32l-ipa.o: powerpc-isa205-altivec32l.c
+ $(IPAGENT_COMPILE) $<
+ $(POSTCOMPILE)
+powerpc-isa205-vsx32l-ipa.o: powerpc-isa205-vsx32l.c
+ $(IPAGENT_COMPILE) $<
+ $(POSTCOMPILE)
+powerpc-e500l-ipa.o: powerpc-e500l.c
+ $(IPAGENT_COMPILE) $<
+ $(POSTCOMPILE)
+powerpc-64l-ipa.o: powerpc-64l.c
+ $(IPAGENT_COMPILE) $<
+ $(POSTCOMPILE)
+powerpc-altivec64l-ipa.o: powerpc-altivec64l.c
+ $(IPAGENT_COMPILE) $<
+ $(POSTCOMPILE)
+powerpc-cell64l-ipa.o: powerpc-cell64l.c
+ $(IPAGENT_COMPILE) $<
+ $(POSTCOMPILE)
+powerpc-vsx64l-ipa.o: powerpc-vsx64l.c
+ $(IPAGENT_COMPILE) $<
+ $(POSTCOMPILE)
+powerpc-isa205-64l-ipa.o: powerpc-isa205-64l.c
+ $(IPAGENT_COMPILE) $<
+ $(POSTCOMPILE)
+powerpc-isa205-altivec64l-ipa.o: powerpc-isa205-altivec64l.c
+ $(IPAGENT_COMPILE) $<
+ $(POSTCOMPILE)
+powerpc-isa205-vsx64l-ipa.o: powerpc-isa205-vsx64l.c
+ $(IPAGENT_COMPILE) $<
+ $(POSTCOMPILE)
tdesc-ipa.o: tdesc.c
$(IPAGENT_COMPILE) $<
$(POSTCOMPILE)
diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv
index a89b1d1..177e0a3 100644
--- a/gdb/gdbserver/configure.srv
+++ b/gdb/gdbserver/configure.srv
@@ -31,6 +31,7 @@ srv_amd64_linux_regobj="amd64-linux.o amd64-avx-linux.o amd64-avx512-linux.o amd

ipa_i386_linux_regobj="i386-linux-ipa.o i386-avx-linux-ipa.o i386-avx512-linux-ipa.o i386-mpx-linux-ipa.o i386-mmx-linux-ipa.o"
ipa_amd64_linux_regobj="amd64-linux-ipa.o amd64-avx-linux-ipa.o amd64-avx512-linux-ipa.o amd64-mpx-linux-ipa.o"
+ipa_ppc_linux_regobj="powerpc-32l-ipa.o powerpc-altivec32l-ipa.o powerpc-cell32l-ipa.o powerpc-vsx32l-ipa.o powerpc-isa205-32l-ipa.o powerpc-isa205-altivec32l-ipa.o powerpc-isa205-vsx32l-ipa.o powerpc-e500l-ipa.o powerpc-64l-ipa.o powerpc-altivec64l-ipa.o powerpc-cell64l-ipa.o powerpc-vsx64l-ipa.o powerpc-isa205-64l-ipa.o powerpc-isa205-altivec64l-ipa.o powerpc-isa205-vsx64l-ipa.o"

srv_i386_32bit_xmlfiles="i386/32bit-core.xml i386/32bit-sse.xml i386/32bit-avx.xml i386/32bit-avx512.xml i386/32bit-mpx.xml"
srv_i386_64bit_xmlfiles="i386/64bit-core.xml i386/64bit-sse.xml i386/64bit-avx.xml i386/64bit-avx512.xml i386/x32-core.xml i386/64bit-mpx.xml"
@@ -258,6 +259,7 @@ case "${target}" in
srv_linux_usrregs=yes
srv_linux_regsets=yes
srv_linux_thread_db=yes
+ ipa_obj="${ipa_ppc_linux_regobj} linux-ppc-ipa.o"
;;
powerpc-*-lynxos*) srv_regobj="powerpc-32.o"
srv_tgtobj="lynx-low.o lynx-ppc-low.o"
diff --git a/gdb/gdbserver/linux-ppc-ipa.c b/gdb/gdbserver/linux-ppc-ipa.c
new file mode 100644
index 0000000..2e8afea
--- /dev/null
+++ b/gdb/gdbserver/linux-ppc-ipa.c
@@ -0,0 +1,238 @@
+/* GNU/Linux/PowerPC specific low level interface, for the in-process
+ agent library for GDB.
+
+ Copyright (C) 2016 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 <sys/mman.h>
+#include "tracepoint.h"
+#include "linux-ppc-tdesc.h"
+
+/* These macros define the position of registers in the buffer collected
+ by the fast tracepoint jump pad. */
+#define FT_CR_R0 0
+#define FT_CR_CR 32
+#define FT_CR_XER 33
+#define FT_CR_LR 34
+#define FT_CR_CTR 35
+#define FT_CR_PC 36
+#define FT_CR_GPR(n) (FT_CR_R0 + (n))
+
+static const int ppc_ft_collect_regmap[] = {
+ /* GPRs */
+ FT_CR_GPR (0), FT_CR_GPR (1), FT_CR_GPR (2),
+ FT_CR_GPR (3), FT_CR_GPR (4), FT_CR_GPR (5),
+ FT_CR_GPR (6), FT_CR_GPR (7), FT_CR_GPR (8),
+ FT_CR_GPR (9), FT_CR_GPR (10), FT_CR_GPR (11),
+ FT_CR_GPR (12), FT_CR_GPR (13), FT_CR_GPR (14),
+ FT_CR_GPR (15), FT_CR_GPR (16), FT_CR_GPR (17),
+ FT_CR_GPR (18), FT_CR_GPR (19), FT_CR_GPR (20),
+ FT_CR_GPR (21), FT_CR_GPR (22), FT_CR_GPR (23),
+ FT_CR_GPR (24), FT_CR_GPR (25), FT_CR_GPR (26),
+ FT_CR_GPR (27), FT_CR_GPR (28), FT_CR_GPR (29),
+ FT_CR_GPR (30), FT_CR_GPR (31),
+ /* FPRs - not collected. */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ FT_CR_PC, /* PC */
+ -1, /* MSR */
+ FT_CR_CR, /* CR */
+ FT_CR_LR, /* LR */
+ FT_CR_CTR, /* CTR */
+ FT_CR_XER, /* XER */
+ -1, /* FPSCR */
+};
+
+#define PPC_NUM_FT_COLLECT_GREGS \
+ (sizeof (ppc_ft_collect_regmap) / sizeof(ppc_ft_collect_regmap[0]))
+
+/* Supply registers collected by the fast tracepoint jump pad.
+ BUF is the second argument we pass to gdb_collect in jump pad. */
+
+void
+supply_fast_tracepoint_registers (struct regcache *regcache,
+ const unsigned char *buf)
+{
+ int i;
+
+ for (i = 0; i < PPC_NUM_FT_COLLECT_GREGS; i++)
+ {
+ if (ppc_ft_collect_regmap[i] == -1)
+ continue;
+ supply_register (regcache, i,
+ ((char *) buf)
+ + ppc_ft_collect_regmap[i] * sizeof (long));
+ }
+}
+
+/* Return the value of register REGNUM. RAW_REGS is collected buffer
+ by jump pad. This function is called by emit_reg. */
+
+ULONGEST
+get_raw_reg (const unsigned char *raw_regs, int regnum)
+{
+ if (regnum >= PPC_NUM_FT_COLLECT_GREGS)
+ return 0;
+ if (ppc_ft_collect_regmap[regnum] == -1)
+ return 0;
+
+ return *(ULONGEST *) (raw_regs
+ + ppc_ft_collect_regmap[regnum] * sizeof (long));
+}
+
+/* Allocate buffer for the jump pads. The branch instruction has a reach
+ of +/- 32MiB, and the executable is loaded at 0x10000000 (256MiB).
+
+ 64-bit: To maximize the area of executable that can use tracepoints,
+ try allocating at 0x10000000 - size initially, decreasing until we hit
+ a free area.
+
+ 32-bit: ld.so loads dynamic libraries right below the executable, so
+ we cannot depend on that area (dynamic libraries can be quite large).
+ Instead, aim right after the executable - at sbrk(0). This will
+ cause future brk to fail, and malloc will fallback to mmap. */
+
+void *
+alloc_jump_pad_buffer (size_t size)
+{
+#ifdef __powerpc64__
+ uintptr_t addr;
+ int pagesize;
+ void *res;
+
+ pagesize = sysconf (_SC_PAGE_SIZE);
+ if (pagesize == -1)
+ perror_with_name ("sysconf");
+
+ addr = 0x10000000 - size;
+
+ /* size should already be page-aligned, but this can't hurt. */
+ addr &= ~(pagesize - 1);
+
+ /* Search for a free area. If we hit 0, we're out of luck. */
+ for (; addr; addr -= pagesize)
+ {
+ /* No MAP_FIXED - we don't want to zap someone's mapping. */
+ res = mmap ((void *) addr, size,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ /* If we got what we wanted, return. */
+ if ((uintptr_t) res == addr)
+ return res;
+
+ /* If we got a mapping, but at a wrong address, undo it. */
+ if (res != MAP_FAILED)
+ munmap (res, size);
+ }
+
+ return NULL;
+#else
+ void *target = sbrk (0);
+ void *res = mmap (target, size, PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ if (res == target)
+ return res;
+
+ if (res != MAP_FAILED)
+ munmap (res, size);
+
+ return NULL;
+#endif
+}
+
+/* Return target_desc to use for IPA, given the tdesc index passed by
+ gdbserver. */
+
+const struct target_desc *
+get_ipa_tdesc (int idx)
+{
+ switch (idx)
+ {
+#ifdef __powerpc64__
+ case PPC_TDESC_BASE:
+ return tdesc_powerpc_64l;
+ case PPC_TDESC_ALTIVEC:
+ return tdesc_powerpc_altivec64l;
+ case PPC_TDESC_CELL:
+ return tdesc_powerpc_cell64l;
+ case PPC_TDESC_VSX:
+ return tdesc_powerpc_vsx64l;
+ case PPC_TDESC_ISA205:
+ return tdesc_powerpc_isa205_64l;
+ case PPC_TDESC_ISA205_ALTIVEC:
+ return tdesc_powerpc_isa205_altivec64l;
+ case PPC_TDESC_ISA205_VSX:
+ return tdesc_powerpc_isa205_vsx64l;
+#else
+ case PPC_TDESC_BASE:
+ return tdesc_powerpc_32l;
+ case PPC_TDESC_ALTIVEC:
+ return tdesc_powerpc_altivec32l;
+ case PPC_TDESC_CELL:
+ return tdesc_powerpc_cell32l;
+ case PPC_TDESC_VSX:
+ return tdesc_powerpc_vsx32l;
+ case PPC_TDESC_ISA205:
+ return tdesc_powerpc_isa205_32l;
+ case PPC_TDESC_ISA205_ALTIVEC:
+ return tdesc_powerpc_isa205_altivec32l;
+ case PPC_TDESC_ISA205_VSX:
+ return tdesc_powerpc_isa205_vsx32l;
+ case PPC_TDESC_E500:
+ return tdesc_powerpc_e500l;
+#endif
+ default:
+ internal_error (__FILE__, __LINE__,
+ "unknown ipa tdesc index: %d", idx);
+#ifdef __powerpc64__
+ return tdesc_powerpc_64l;
+#else
+ return tdesc_powerpc_32l;
+#endif
+ }
+}
+
+
+/* Initialize ipa_tdesc and others. */
+
+void
+initialize_low_tracepoint (void)
+{
+#ifdef __powerpc64__
+ init_registers_powerpc_64l ();
+ init_registers_powerpc_altivec64l ();
+ init_registers_powerpc_cell64l ();
+ init_registers_powerpc_vsx64l ();
+ init_registers_powerpc_isa205_64l ();
+ init_registers_powerpc_isa205_altivec64l ();
+ init_registers_powerpc_isa205_vsx64l ();
+#else
+ init_registers_powerpc_32l ();
+ init_registers_powerpc_altivec32l ();
+ init_registers_powerpc_cell32l ();
+ init_registers_powerpc_vsx32l ();
+ init_registers_powerpc_isa205_32l ();
+ init_registers_powerpc_isa205_altivec32l ();
+ init_registers_powerpc_isa205_vsx32l ();
+ init_registers_powerpc_e500l ();
+#endif
+}
diff --git a/gdb/gdbserver/linux-ppc-low.c b/gdb/gdbserver/linux-ppc-low.c
index 49d27ee..c1a3a8d 100644
--- a/gdb/gdbserver/linux-ppc-low.c
+++ b/gdb/gdbserver/linux-ppc-low.c
@@ -24,70 +24,24 @@
#include <asm/ptrace.h>

#include "nat/ppc-linux.h"
+#include "linux-ppc-tdesc.h"
+#include "ax.h"
+#include "tracepoint.h"
+
+#define PPC_FIELD(value, from, len) \
+ (((value) >> (32 - (from) - (len))) & ((1 << (len)) - 1))
+#define PPC_SEXT(v, bs) \
+ ((((CORE_ADDR) (v) & (((CORE_ADDR) 1 << (bs)) - 1)) \
+ ^ ((CORE_ADDR) 1 << ((bs) - 1))) \
+ - ((CORE_ADDR) 1 << ((bs) - 1)))
+#define PPC_OP6(insn) PPC_FIELD (insn, 0, 6)
+#define PPC_BO(insn) PPC_FIELD (insn, 6, 5)
+#define PPC_LI(insn) (PPC_SEXT (PPC_FIELD (insn, 6, 24), 24) << 2)
+#define PPC_BD(insn) (PPC_SEXT (PPC_FIELD (insn, 16, 14), 14) << 2)

static unsigned long ppc_hwcap;

-/* Defined in auto-generated file powerpc-32l.c. */
-void init_registers_powerpc_32l (void);
-extern const struct target_desc *tdesc_powerpc_32l;
-
-/* Defined in auto-generated file powerpc-altivec32l.c. */
-void init_registers_powerpc_altivec32l (void);
-extern const struct target_desc *tdesc_powerpc_altivec32l;
-
-/* Defined in auto-generated file powerpc-cell32l.c. */
-void init_registers_powerpc_cell32l (void);
-extern const struct target_desc *tdesc_powerpc_cell32l;
-
-/* Defined in auto-generated file powerpc-vsx32l.c. */
-void init_registers_powerpc_vsx32l (void);
-extern const struct target_desc *tdesc_powerpc_vsx32l;
-
-/* Defined in auto-generated file powerpc-isa205-32l.c. */
-void init_registers_powerpc_isa205_32l (void);
-extern const struct target_desc *tdesc_powerpc_isa205_32l;
-
-/* Defined in auto-generated file powerpc-isa205-altivec32l.c. */
-void init_registers_powerpc_isa205_altivec32l (void);
-extern const struct target_desc *tdesc_powerpc_isa205_altivec32l;
-
-/* Defined in auto-generated file powerpc-isa205-vsx32l.c. */
-void init_registers_powerpc_isa205_vsx32l (void);
-extern const struct target_desc *tdesc_powerpc_isa205_vsx32l;
-
-/* Defined in auto-generated file powerpc-e500l.c. */
-void init_registers_powerpc_e500l (void);
-extern const struct target_desc *tdesc_powerpc_e500l;
-
-/* Defined in auto-generated file powerpc-64l.c. */
-void init_registers_powerpc_64l (void);
-extern const struct target_desc *tdesc_powerpc_64l;
-
-/* Defined in auto-generated file powerpc-altivec64l.c. */
-void init_registers_powerpc_altivec64l (void);
-extern const struct target_desc *tdesc_powerpc_altivec64l;
-
-/* Defined in auto-generated file powerpc-cell64l.c. */
-void init_registers_powerpc_cell64l (void);
-extern const struct target_desc *tdesc_powerpc_cell64l;
-
-/* Defined in auto-generated file powerpc-vsx64l.c. */
-void init_registers_powerpc_vsx64l (void);
-extern const struct target_desc *tdesc_powerpc_vsx64l;
-
-/* Defined in auto-generated file powerpc-isa205-64l.c. */
-void init_registers_powerpc_isa205_64l (void);
-extern const struct target_desc *tdesc_powerpc_isa205_64l;
-
-/* Defined in auto-generated file powerpc-isa205-altivec64l.c. */
-void init_registers_powerpc_isa205_altivec64l (void);
-extern const struct target_desc *tdesc_powerpc_isa205_altivec64l;
-
-/* Defined in auto-generated file powerpc-isa205-vsx64l.c. */
-void init_registers_powerpc_isa205_vsx64l (void);
-extern const struct target_desc *tdesc_powerpc_isa205_vsx64l;
-
#define ppc_num_regs 73

#ifdef __powerpc64__
@@ -756,12 +710,784 @@ ppc_arch_setup (void)
current_process ()->tdesc = tdesc;
}

+/* Implementation of linux_target_ops method "supports_tracepoints". */
+
static int
ppc_supports_tracepoints (void)
{
return 1;
}

+/* Get the thread area address. This is used to recognize which
+ thread is which when tracing with the in-process agent library. We
+ don't read anything from the address, and treat it as opaque; it's
+ the address itself that we assume is unique per-thread. */
+
+static int
+ppc_get_thread_area (int lwpid, CORE_ADDR *addr)
+{
+#ifdef __powerpc64__
+ struct lwp_info *lwp = find_lwp_pid (pid_to_ptid (lwpid));
+ struct thread_info *thr = get_lwp_thread (lwp);
+ struct regcache *regcache = get_thread_regcache (thr, 1);
+ int is_64 = register_size (regcache->tdesc, 0) == 8;
+#endif
+ long res;
+
+#ifdef __powerpc64__
+ if (is_64)
+ res = ptrace (PTRACE_PEEKUSER, lwpid, (long) PT_R13 * sizeof (long),
+ (long) 0);
+ else
+#endif
+ res = ptrace (PTRACE_PEEKUSER, lwpid, (long) PT_R2 * sizeof (long),
+ (long) 0);
+
+ if (res == -1)
+ return -1;
+
+ *addr = res;
+
+#ifdef __powerpc64__
+ if (!is_64)
+ *addr &= 0xffffffffull;
+#endif
+
+ return 0;
+}
+
+/* Generate a ds-form instruction in BUF and return the number of bytes written
+
+ 0 6 11 16 30 32
+ | OPCD | RST | RA | DS |XO| */
+
+__attribute__((unused)) /* Maybe unused due to conditional compilation. */
+static int
+gen_ds_form (uint32_t *buf, int opcd, int rst, int ra, int ds, int xo)
+{
+ uint32_t insn;
+
+ gdb_assert ((opcd & ~0x3f) == 0);
+ gdb_assert ((rst & ~0x1f) == 0);
+ gdb_assert ((ra & ~0x1f) == 0);
+ gdb_assert ((xo & ~0x3) == 0);
+
+ insn = (rst << 21) | (ra << 16) | (ds & 0xfffc) | (xo & 0x3);
+ *buf = (opcd << 26) | insn;
+ return 1;
+}
+
+/* Followings are frequently used ds-form instructions. */
+
+#define GEN_STD(buf, rs, ra, offset) gen_ds_form (buf, 62, rs, ra, offset, 0)
+#define GEN_STDU(buf, rs, ra, offset) gen_ds_form (buf, 62, rs, ra, offset, 1)
+#define GEN_LD(buf, rt, ra, offset) gen_ds_form (buf, 58, rt, ra, offset, 0)
+#define GEN_LDU(buf, rt, ra, offset) gen_ds_form (buf, 58, rt, ra, offset, 1)
+
+/* Generate a d-form instruction in BUF.
+
+ 0 6 11 16 32
+ | OPCD | RST | RA | D | */
+
+static int
+gen_d_form (uint32_t *buf, int opcd, int rst, int ra, int si)
+{
+ uint32_t insn;
+
+ gdb_assert ((opcd & ~0x3f) == 0);
+ gdb_assert ((rst & ~0x1f) == 0);
+ gdb_assert ((ra & ~0x1f) == 0);
+
+ insn = (rst << 21) | (ra << 16) | (si & 0xffff);
+ *buf = (opcd << 26) | insn;
+ return 1;
+}
+
+/* Followings are frequently used d-form instructions. */
+
+#define GEN_ADDI(buf, rt, ra, si) gen_d_form (buf, 14, rt, ra, si)
+#define GEN_ADDIS(buf, rt, ra, si) gen_d_form (buf, 15, rt, ra, si)
+#define GEN_LI(buf, rt, si) GEN_ADDI (buf, rt, 0, si)
+#define GEN_LIS(buf, rt, si) GEN_ADDIS (buf, rt, 0, si)
+#define GEN_ORI(buf, rt, ra, si) gen_d_form (buf, 24, rt, ra, si)
+#define GEN_ORIS(buf, rt, ra, si) gen_d_form (buf, 25, rt, ra, si)
+#define GEN_LWZ(buf, rt, ra, si) gen_d_form (buf, 32, rt, ra, si)
+#define GEN_STW(buf, rt, ra, si) gen_d_form (buf, 36, rt, ra, si)
+#define GEN_STWU(buf, rt, ra, si) gen_d_form (buf, 37, rt, ra, si)
+
+/* Generate a xfx-form instruction in BUF and return the number of bytes
+ written.
+
+ 0 6 11 21 31 32
+ | OPCD | RST | RI | XO |/| */
+
+static int
+gen_xfx_form (uint32_t *buf, int opcd, int rst, int ri, int xo)
+{
+ uint32_t insn;
+ unsigned int n = ((ri & 0x1f) << 5) | ((ri >> 5) & 0x1f);
+
+ gdb_assert ((opcd & ~0x3f) == 0);
+ gdb_assert ((rst & ~0x1f) == 0);
+ gdb_assert ((xo & ~0x3ff) == 0);
+
+ insn = (rst << 21) | (n << 11) | (xo << 1);
+ *buf = (opcd << 26) | insn;
+ return 1;
+}
+
+/* Followings are frequently used xfx-form instructions. */
+
+#define GEN_MFSPR(buf, rt, spr) gen_xfx_form (buf, 31, rt, spr, 339)
+#define GEN_MTSPR(buf, rt, spr) gen_xfx_form (buf, 31, rt, spr, 467)
+#define GEN_MFCR(buf, rt) gen_xfx_form (buf, 31, rt, 0, 19)
+#define GEN_MTCR(buf, rt) gen_xfx_form (buf, 31, rt, 0x3cf, 144)
+#define GEN_SYNC(buf, L, E) gen_xfx_form (buf, 31, L & 0x3, \
+ E & 0xf, 598)
+#define GEN_LWSYNC(buf) GEN_SYNC (buf, 1, 0)
+
+
+/* Generate a x-form instruction in BUF and return the number of bytes written.
+
+ 0 6 11 16 21 31 32
+ | OPCD | RST | RA | RB | XO |RC| */
+
+static int
+gen_x_form (uint32_t *buf, int opcd, int rst, int ra, int rb, int xo, int rc)
+{
+ uint32_t insn;
+
+ gdb_assert ((opcd & ~0x3f) == 0);
+ gdb_assert ((rst & ~0x1f) == 0);
+ gdb_assert ((ra & ~0x1f) == 0);
+ gdb_assert ((rb & ~0x1f) == 0);
+ gdb_assert ((xo & ~0x3ff) == 0);
+ gdb_assert ((rc & ~1) == 0);
+
+ insn = (rst << 21) | (ra << 16) | (rb << 11) | (xo << 1) | rc;
+ *buf = (opcd << 26) | insn;
+ return 1;
+}
+
+/* Followings are frequently used x-form instructions. */
+
+#define GEN_OR(buf, ra, rs, rb) gen_x_form (buf, 31, rs, ra, rb, 444, 0)
+#define GEN_MR(buf, ra, rs) GEN_OR (buf, ra, rs, rs)
+#define GEN_LWARX(buf, rt, ra, rb) gen_x_form (buf, 31, rt, ra, rb, 20, 0)
+#define GEN_STWCX(buf, rs, ra, rb) gen_x_form (buf, 31, rs, ra, rb, 150, 1)
+/* Assume bf = cr7. */
+#define GEN_CMPW(buf, ra, rb) gen_x_form (buf, 31, 28, ra, rb, 0, 0)
+
+
+/* Generate a md-form instruction in BUF and return the number of bytes written.
+
+ 0 6 11 16 21 27 30 31 32
+ | OPCD | RS | RA | sh | mb | XO |sh|Rc| */
+
+static int
+gen_md_form (uint32_t *buf, int opcd, int rs, int ra, int sh, int mb,
+ int xo, int rc)
+{
+ uint32_t insn;
+ unsigned int n = ((mb & 0x1f) << 1) | ((mb >> 5) & 0x1);
+ unsigned int sh0_4 = sh & 0x1f;
+ unsigned int sh5 = (sh >> 5) & 1;
+
+ gdb_assert ((opcd & ~0x3f) == 0);
+ gdb_assert ((rs & ~0x1f) == 0);
+ gdb_assert ((ra & ~0x1f) == 0);
+ gdb_assert ((sh & ~0x3f) == 0);
+ gdb_assert ((mb & ~0x3f) == 0);
+ gdb_assert ((xo & ~0x7) == 0);
+ gdb_assert ((rc & ~0x1) == 0);
+
+ insn = (rs << 21) | (ra << 16) | (sh0_4 << 11) | (n << 5)
+ | (sh5 << 1) | (xo << 2) | (rc & 1);
+ *buf = (opcd << 26) | insn;
+ return 1;
+}
+
+/* The following are frequently used md-form instructions. */
+
+#define GEN_RLDICL(buf, ra, rs ,sh, mb) \
+ gen_md_form (buf, 30, rs, ra, sh, mb, 0, 0)
+#define GEN_RLDICR(buf, ra, rs ,sh, mb) \
+ gen_md_form (buf, 30, rs, ra, sh, mb, 1, 0)
+
+/* Generate a i-form instruction in BUF and return the number of bytes written.
+
+ 0 6 30 31 32
+ | OPCD | LI |AA|LK| */
+
+static int
+gen_i_form (uint32_t *buf, int opcd, int li, int aa, int lk)
+{
+ uint32_t insn;
+
+ gdb_assert ((opcd & ~0x3f) == 0);
+
+ insn = (li & 0x3fffffc) | (aa & 1) | (lk & 1);
+ *buf = (opcd << 26) | insn;
+ return 1;
+}
+
+/* The following are frequently used i-form instructions. */
+
+#define GEN_B(buf, li) gen_i_form (buf, 18, li, 0, 0)
+#define GEN_BL(buf, li) gen_i_form (buf, 18, li, 0, 1)
+
+/* Generate a b-form instruction in BUF and return the number of bytes written.
+
+ 0 6 11 16 30 31 32
+ | OPCD | BO | BI | BD |AA|LK| */
+
+static int
+gen_b_form (uint32_t *buf, int opcd, int bo, int bi, int bd,
+ int aa, int lk)
+{
+ uint32_t insn;
+
+ gdb_assert ((opcd & ~0x3f) == 0);
+ gdb_assert ((bo & ~0x1f) == 0);
+ gdb_assert ((bi & ~0x1f) == 0);
+
+ insn = (bo << 21) | (bi << 16) | (bd & 0xfffc) | (aa & 1) | (lk & 1);
+ *buf = (opcd << 26) | insn;
+ return 1;
+}
+
+/* The following are frequently used b-form instructions. */
+/* Assume bi = cr7. */
+#define GEN_BNE(buf, bd) gen_b_form (buf, 16, 0x4, (7 << 2) | 2, bd, 0 ,0)
+
+/* GEN_LOAD and GEN_STORE generate 64- or 32-bit load/store for ppc64 or ppc32
+ respectively. They are primary used for save/restore GPRs in jump-pad,
+ not used for bytecode compiling. */
+
+#if defined __powerpc64__
+#define GEN_LOAD(buf, rt, ra, si) (is_64 ? GEN_LD (buf, rt, ra, si) : \
+ GEN_LWZ (buf, rt, ra, si))
+#define GEN_STORE(buf, rt, ra, si) (is_64 ? GEN_STD (buf, rt, ra, si) : \
+ GEN_STW (buf, rt, ra, si))
+#else
+#define GEN_LOAD(buf, rt, ra, si) GEN_LWZ (buf, rt, ra, si)
+#define GEN_STORE(buf, rt, ra, si) GEN_STW (buf, rt, ra, si)
+#endif
+
+/* Generate a sequence of instructions to load IMM in the register REG.
+ Write the instructions in BUF and return the number of bytes written. */
+
+static int
+gen_limm (uint32_t *buf, int reg, uint64_t imm, int is_64)
+{
+ uint32_t *p = buf;
+
+ if ((imm + 32768) < 65536)
+ {
+ /* li reg, imm[7:0] */
+ p += GEN_LI (p, reg, imm);
+ }
+ else if ((imm >> 16) == 0)
+ {
+ /* li reg, 0
+ ori reg, reg, imm[15:0] */
+ p += GEN_LI (p, reg, 0);
+ p += GEN_ORI (p, reg, reg, imm);
+ }
+ else if ((imm >> 32) == 0)
+ {
+ /* lis reg, imm[31:16]
+ ori reg, reg, imm[15:0]
+ rldicr reg, reg, 0, 32 */
+ p += GEN_LIS (p, reg, (imm >> 16) & 0xffff);
+ p += GEN_ORI (p, reg, reg, imm & 0xffff);
+ /* Clear upper 32-bit if sign-bit is set. */
+ if (imm & (1 << 31) && is_64)
+ p += GEN_RLDICL (p, reg, reg, 0, 32);
+ }
+ else
+ {
+ gdb_assert (is_64);
+ /* lis reg, <imm[63:48]>
+ ori reg, reg, <imm[48:32]>
+ rldicr reg, reg, 32, 31
+ oris reg, reg, <imm[31:16]>
+ ori reg, reg, <imm[15:0]> */
+ p += GEN_LIS (p, reg, ((imm >> 48) & 0xffff));
+ p += GEN_ORI (p, reg, reg, ((imm >> 32) & 0xffff));
+ p += GEN_RLDICR (p, reg, reg, 32, 31);
+ p += GEN_ORIS (p, reg, reg, ((imm >> 16) & 0xffff));
+ p += GEN_ORI (p, reg, reg, (imm & 0xffff));
+ }
+
+ return p - buf;
+}
+
+/* Generate a sequence for atomically exchange at location LOCK.
+ This code sequence clobbers r6, r7, r8. LOCK is the location for
+ the atomic-xchg, OLD_VALUE is expected old value stored in the
+ location, and R_NEW is a register for the new value. */
+
+static int
+gen_atomic_xchg (uint32_t *buf, CORE_ADDR lock, int old_value, int r_new,
+ int is_64)
+{
+ const int r_lock = 6;
+ const int r_old = 7;
+ const int r_tmp = 8;
+ uint32_t *p = buf;
+
+ /*
+ 1: lwarx TMP, 0, LOCK
+ cmpwi TMP, OLD
+ bne 1b
+ stwcx. NEW, 0, LOCK
+ bne 1b */
+
+ p += gen_limm (p, r_lock, lock, is_64);
+ p += gen_limm (p, r_old, old_value, is_64);
+
+ p += GEN_LWARX (p, r_tmp, 0, r_lock);
+ p += GEN_CMPW (p, r_tmp, r_old);
+ p += GEN_BNE (p, -8);
+ p += GEN_STWCX (p, r_new, 0, r_lock);
+ p += GEN_BNE (p, -16);
+
+ return p - buf;
+}
+
+/* Generate a sequence of instructions for calling a function
+ at address of FN. Return the number of bytes are written in BUF. */
+
+static int
+gen_call (uint32_t *buf, CORE_ADDR fn, int is_64, int is_opd)
+{
+ uint32_t *p = buf;
+
+ /* Must be called by r12 for caller to calculate TOC address. */
+ p += gen_limm (p, 12, fn, is_64);
+ if (is_opd)
+ {
+ p += GEN_LOAD (p, 11, 12, 16);
+ p += GEN_LOAD (p, 2, 12, 8);
+ p += GEN_LOAD (p, 12, 12, 0);
+ }
+ p += GEN_MTSPR (p, 12, 9); /* mtctr r12 */
+ *p++ = 0x4e800421; /* bctrl */
+
+ return p - buf;
+}
+
+/* Copy the instruction from OLDLOC to *TO, and update *TO to *TO + size
+ of instruction. This function is used to adjust pc-relative instructions
+ when copying. */
+
+static void
+ppc_relocate_instruction (CORE_ADDR *to, CORE_ADDR oldloc)
+{
+ uint32_t insn, op6;
+ long rel, newrel;
+
+ read_inferior_memory (oldloc, (unsigned char *) &insn, 4);
+ op6 = PPC_OP6 (insn);
+
+ if (op6 == 18 && (insn & 2) == 0)
+ {
+ /* branch && AA = 0 */
+ rel = PPC_LI (insn);
+ newrel = (oldloc - *to) + rel;
+
+ /* Out of range. Cannot relocate instruction. */
+ if (newrel >= (1 << 25) || newrel < -(1 << 25))
+ return;
+
+ insn = (insn & ~0x3fffffc) | (newrel & 0x3fffffc);
+ }
+ else if (op6 == 16 && (insn & 2) == 0)
+ {
+ /* conditional branch && AA = 0 */
+
+ /* If the new relocation is too big for even a 26-bit unconditional
+ branch, there is nothing we can do. Just abort.
+
+ Otherwise, if it can be fit in 16-bit conditional branch, just
+ copy the instruction and relocate the address.
+
+ If the it's big for conditional-branch (16-bit), try to invert the
+ condition and jump with 26-bit branch. For example,
+
+ beq .Lgoto
+ INSN1
+
+ =>
+
+ bne 1f (+8)
+ b .Lgoto
+ 1:INSN1
+
+ After this transform, we are actually jump from *TO+4 instead of *TO,
+ so check the relocation again because it will be 1-insn farther then
+ before if *TO is after OLDLOC.
+
+
+ For BDNZT (or so) is transformed from
+
+ bdnzt eq, .Lgoto
+ INSN1
+
+ =>
+
+ bdz 1f (+12)
+ bf eq, 1f (+8)
+ b .Lgoto
+ 1:INSN1
+
+ See also "BO field encodings". */
+
+ rel = PPC_BD (insn);
+ newrel = (oldloc - *to) + rel;
+
+ if (newrel < (1 << 15) && newrel >= -(1 << 15))
+ insn = (insn & ~0xfffc) | (newrel & 0xfffc);
+ else if ((PPC_BO (insn) & 0x14) == 0x4 || (PPC_BO (insn) & 0x14) == 0x10)
+ {
+ newrel -= 4;
+
+ /* Out of range. Cannot relocate instruction. */
+ if (newrel >= (1 << 25) || newrel < -(1 << 25))
+ return;
+
+ if ((PPC_BO (insn) & 0x14) == 0x4)
+ insn ^= (1 << 24);
+ else if ((PPC_BO (insn) & 0x14) == 0x10)
+ insn ^= (1 << 22);
+
+ /* Jump over the unconditional branch. */
+ insn = (insn & ~0xfffc) | 0x8;
+ write_inferior_memory (*to, (unsigned char *) &insn, 4);
+ *to += 4;
+
+ /* Build a unconditional branch and copy LK bit. */
+ insn = (18 << 26) | (0x3fffffc & newrel) | (insn & 0x3);
+ write_inferior_memory (*to, (unsigned char *) &insn, 4);
+ *to += 4;
+
+ return;
+ }
+ else if ((PPC_BO (insn) & 0x14) == 0)
+ {
+ uint32_t bdnz_insn = (16 << 26) | (0x10 << 21) | 12;
+ uint32_t bf_insn = (16 << 26) | (0x4 << 21) | 8;
+
+ newrel -= 8;
+
+ /* Out of range. Cannot relocate instruction. */
+ if (newrel >= (1 << 25) || newrel < -(1 << 25))
+ return;
+
+ /* Copy BI field. */
+ bf_insn |= (insn & 0x1f0000);
+
+ /* Invert condition. */
+ bdnz_insn |= (insn ^ (1 << 22)) & (1 << 22);
+ bf_insn |= (insn ^ (1 << 24)) & (1 << 24);
+
+ write_inferior_memory (*to, (unsigned char *) &bdnz_insn, 4);
+ *to += 4;
+ write_inferior_memory (*to, (unsigned char *) &bf_insn, 4);
+ *to += 4;
+
+ /* Build a unconditional branch and copy LK bit. */
+ insn = (18 << 26) | (0x3fffffc & newrel) | (insn & 0x3);
+ write_inferior_memory (*to, (unsigned char *) &insn, 4);
+ *to += 4;
+
+ return;
+ }
+ else /* (BO & 0x14) == 0x14, branch always. */
+ {
+ /* Out of range. Cannot relocate instruction. */
+ if (newrel >= (1 << 25) || newrel < -(1 << 25))
+ return;
+
+ /* Build a unconditional branch and copy LK bit. */
+ insn = (18 << 26) | (0x3fffffc & newrel) | (insn & 0x3);
+ write_inferior_memory (*to, (unsigned char *) &insn, 4);
+ *to += 4;
+
+ return;
+ }
+ }
+
+ write_inferior_memory (*to, (unsigned char *) &insn, 4);
+ *to += 4;
+}
+
+/* Implement install_fast_tracepoint_jump_pad of target_ops.
+ See target.h for details. */
+
+static int
+ppc_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr,
+ CORE_ADDR collector,
+ CORE_ADDR lockaddr,
+ ULONGEST orig_size,
+ CORE_ADDR *jump_entry,
+ CORE_ADDR *trampoline,
+ ULONGEST *trampoline_size,
+ unsigned char *jjump_pad_insn,
+ ULONGEST *jjump_pad_insn_size,
+ CORE_ADDR *adjusted_insn_addr,
+ CORE_ADDR *adjusted_insn_addr_end,
+ char *err)
+{
+ uint32_t buf[256];
+ uint32_t *p = buf;
+ int j, offset;
+ CORE_ADDR buildaddr = *jump_entry;
+ const CORE_ADDR entryaddr = *jump_entry;
+ int rsz, min_frame, frame_size, tp_reg;
+#ifdef __powerpc64__
+ struct regcache *regcache = get_thread_regcache (current_thread, 0);
+ int is_64 = register_size (regcache->tdesc, 0) == 8;
+#if _CALL_ELF == 2
+ /* XXX is there a way to get this dynamically from the inferior? */
+ int is_opd = 0;
+#else
+ int is_opd = is_64;
+#endif
+#else
+ int is_64 = 0, is_opd = 0;
+#endif
+
+#ifdef __powerpc64__
+ if (is_64)
+ {
+ /* Minimum frame size is 32 bytes for ELFv2, and 112 bytes for ELFv1. */
+ rsz = 8;
+ min_frame = 112;
+ frame_size = (40 * rsz) + min_frame;
+ tp_reg = 13;
+ }
+ else
+ {
+#endif
+ rsz = 4;
+ min_frame = 16;
+ frame_size = (40 * rsz) + min_frame;
+ tp_reg = 2;
+#ifdef __powerpc64__
+ }
+#endif
+
+ /* Stack frame layout for this jump pad,
+
+ High thread_area (r13/r2) |
+ tpoint - collecting_t obj
+ PC/<tpaddr> | +36
+ CTR | +35
+ LR | +34
+ XER | +33
+ CR | +32
+ R31 |
+ R29 |
+ ... |
+ R1 | +1
+ R0 - collected registers
+ ... |
+ ... |
+ Low Back-chain -
+
+
+ The code flow of this jump pad,
+
+ 1. Adjust SP
+ 2. Save GPR and SPR
+ 3. Prepare argument
+ 4. Call gdb_collector
+ 5. Restore GPR and SPR
+ 6. Restore SP
+ 7. Build a jump for back to the program
+ 8. Copy/relocate original instruction
+ 9. Build a jump for replacing orignal instruction. */
+
+ /* Adjust stack pointer. */
+ if (is_64)
+ p += GEN_STDU (p, 1, 1, -frame_size); /* stdu r1,-frame_size(r1) */
+ else
+ p += GEN_STWU (p, 1, 1, -frame_size); /* stwu r1,-frame_size(r1) */
+
+ /* Store GPRs. Save R1 later, because it had just been modified, but
+ we want the original value. */
+ for (j = 2; j < 32; j++)
+ p += GEN_STORE (p, j, 1, min_frame + j * rsz);
+ p += GEN_STORE (p, 0, 1, min_frame + 0 * rsz);
+ /* Set r0 to the original value of r1 before adjusting stack frame,
+ and then save it. */
+ p += GEN_ADDI (p, 0, 1, frame_size);
+ p += GEN_STORE (p, 0, 1, min_frame + 1 * rsz);
+
+ /* Save CR, XER, LR, and CTR. */
+ p += GEN_MFCR (p, 3); /* mfcr r3 */
+ p += GEN_MFSPR (p, 4, 1); /* mfxer r4 */
+ p += GEN_MFSPR (p, 5, 8); /* mflr r5 */
+ p += GEN_MFSPR (p, 6, 9); /* mfctr r6 */
+ p += GEN_STORE (p, 3, 1, min_frame + 32 * rsz); /* std r3, 32(r1) */
+ p += GEN_STORE (p, 4, 1, min_frame + 33 * rsz); /* std r4, 33(r1) */
+ p += GEN_STORE (p, 5, 1, min_frame + 34 * rsz); /* std r5, 34(r1) */
+ p += GEN_STORE (p, 6, 1, min_frame + 35 * rsz); /* std r6, 35(r1) */
+
+ /* Save PC<tpaddr> */
+ p += gen_limm (p, 3, tpaddr, is_64);
+ p += GEN_STORE (p, 3, 1, min_frame + 36 * rsz);
+
+
+ /* Setup arguments to collector. */
+ /* Set r4 to collected registers. */
+ p += GEN_ADDI (p, 4, 1, min_frame);
+ /* Set r3 to TPOINT. */
+ p += gen_limm (p, 3, tpoint, is_64);
+
+ /* Prepare collecting_t object for lock. */
+ p += GEN_STORE (p, 3, 1, min_frame + 37 * rsz);
+ p += GEN_STORE (p, tp_reg, 1, min_frame + 38 * rsz);
+ /* Set R5 to collecting object. */
+ p += GEN_ADDI (p, 5, 1, 37 * rsz);
+
+ p += GEN_LWSYNC (p);
+ p += gen_atomic_xchg (p, lockaddr, 0, 5, is_64);
+ p += GEN_LWSYNC (p);
+
+ /* Call to collector. */
+ p += gen_call (p, collector, is_64, is_opd);
+
+ /* Simply write 0 to release the lock. */
+ p += gen_limm (p, 3, lockaddr, is_64);
+ p += gen_limm (p, 4, 0, is_64);
+ p += GEN_LWSYNC (p);
+ p += GEN_STORE (p, 4, 3, 0);
+
+ /* Restore stack and registers. */
+ p += GEN_LOAD (p, 3, 1, min_frame + 32 * rsz); /* ld r3, 32(r1) */
+ p += GEN_LOAD (p, 4, 1, min_frame + 33 * rsz); /* ld r4, 33(r1) */
+ p += GEN_LOAD (p, 5, 1, min_frame + 34 * rsz); /* ld r5, 34(r1) */
+ p += GEN_LOAD (p, 6, 1, min_frame + 35 * rsz); /* ld r6, 35(r1) */
+ p += GEN_MTCR (p, 3); /* mtcr r3 */
+ p += GEN_MTSPR (p, 4, 1); /* mtxer r4 */
+ p += GEN_MTSPR (p, 5, 8); /* mtlr r5 */
+ p += GEN_MTSPR (p, 6, 9); /* mtctr r6 */
+
+ /* Restore GPRs. */
+ for (j = 2; j < 32; j++)
+ p += GEN_LOAD (p, j, 1, min_frame + j * rsz);
+ p += GEN_LOAD (p, 0, 1, min_frame + 0 * rsz);
+ /* Restore SP. */
+ p += GEN_ADDI (p, 1, 1, frame_size);
+
+ /* Flush instructions to inferior memory. */
+ write_inferior_memory (buildaddr, (unsigned char *) buf, (p - buf) * 4);
+
+ /* Now, insert the original instruction to execute in the jump pad. */
+ *adjusted_insn_addr = buildaddr + (p - buf) * 4;
+ *adjusted_insn_addr_end = *adjusted_insn_addr;
+ ppc_relocate_instruction (adjusted_insn_addr_end, tpaddr);
+
+ /* Verify the relocation size. If should be 4 for normal copy,
+ 8 or 12 for some conditional branch. */
+ if ((*adjusted_insn_addr_end - *adjusted_insn_addr == 0)
+ || (*adjusted_insn_addr_end - *adjusted_insn_addr > 12))
+ {
+ sprintf (err, "E.Unexpected instruction length = %d"
+ "when relocate instruction.",
+ (int) (*adjusted_insn_addr_end - *adjusted_insn_addr));
+ return 1;
+ }
+
+ buildaddr = *adjusted_insn_addr_end;
+ p = buf;
+ /* Finally, write a jump back to the program. */
+ offset = (tpaddr + 4) - buildaddr;
+ if (offset >= (1 << 25) || offset < -(1 << 25))
+ {
+ sprintf (err, "E.Jump back from jump pad too far from tracepoint "
+ "(offset 0x%x > 26-bit).", offset);
+ return 1;
+ }
+ /* b <tpaddr+4> */
+ p += GEN_B (p, offset);
+ write_inferior_memory (buildaddr, (unsigned char *) buf, (p - buf) * 4);
+ *jump_entry = buildaddr + (p - buf) * 4;
+
+ /* The jump pad is now built. Wire in a jump to our jump pad. This
+ is always done last (by our caller actually), so that we can
+ install fast tracepoints with threads running. This relies on
+ the agent's atomic write support. */
+ offset = entryaddr - tpaddr;
+ if (offset >= (1 << 25) || offset < -(1 << 25))
+ {
+ sprintf (err, "E.Jump back from jump pad too far from tracepoint "
+ "(offset 0x%x > 26-bit).", offset);
+ return 1;
+ }
+ /* b <jentry> */
+ GEN_B ((uint32_t *) jjump_pad_insn, offset);
+ *jjump_pad_insn_size = 4;
+
+ return 0;
+}
+
+/* Returns the minimum instruction length for installing a tracepoint. */
+
+static int
+ppc_get_min_fast_tracepoint_insn_len (void)
+{
+ return 4;
+}
+
+/* Implementation of linux_target_ops method "get_ipa_tdesc_idx". */
+
+static int
+ppc_get_ipa_tdesc_idx (void)
+{
+ struct regcache *regcache = get_thread_regcache (current_thread, 0);
+ const struct target_desc *tdesc = regcache->tdesc;
+
+#ifdef __powerpc64__
+ if (tdesc == tdesc_powerpc_64l)
+ return PPC_TDESC_BASE;
+ if (tdesc == tdesc_powerpc_altivec64l)
+ return PPC_TDESC_ALTIVEC;
+ if (tdesc == tdesc_powerpc_cell64l)
+ return PPC_TDESC_CELL;
+ if (tdesc == tdesc_powerpc_vsx64l)
+ return PPC_TDESC_VSX;
+ if (tdesc == tdesc_powerpc_isa205_64l)
+ return PPC_TDESC_ISA205;
+ if (tdesc == tdesc_powerpc_isa205_altivec64l)
+ return PPC_TDESC_ISA205_ALTIVEC;
+ if (tdesc == tdesc_powerpc_isa205_vsx64l)
+ return PPC_TDESC_ISA205_VSX;
+#endif
+
+ if (tdesc == tdesc_powerpc_32l)
+ return PPC_TDESC_BASE;
+ if (tdesc == tdesc_powerpc_altivec32l)
+ return PPC_TDESC_ALTIVEC;
+ if (tdesc == tdesc_powerpc_cell32l)
+ return PPC_TDESC_CELL;
+ if (tdesc == tdesc_powerpc_vsx32l)
+ return PPC_TDESC_VSX;
+ if (tdesc == tdesc_powerpc_isa205_32l)
+ return PPC_TDESC_ISA205;
+ if (tdesc == tdesc_powerpc_isa205_altivec32l)
+ return PPC_TDESC_ISA205_ALTIVEC;
+ if (tdesc == tdesc_powerpc_isa205_vsx32l)
+ return PPC_TDESC_ISA205_VSX;
+ if (tdesc == tdesc_powerpc_e500l)
+ return PPC_TDESC_E500;
+
+ return 0;
+}
+
struct linux_target_ops the_low_target = {
ppc_arch_setup,
ppc_regs_info,
@@ -789,13 +1515,15 @@ struct linux_target_ops the_low_target = {
NULL, /* prepare_to_resume */
NULL, /* process_qsupported */
ppc_supports_tracepoints,
- NULL, /* get_thread_area */
- NULL, /* install_fast_tracepoint_jump_pad */
+ ppc_get_thread_area,
+ ppc_install_fast_tracepoint_jump_pad,
NULL, /* emit_ops */
- NULL, /* get_min_fast_tracepoint_insn_len */
+ ppc_get_min_fast_tracepoint_insn_len,
NULL, /* supports_range_stepping */
NULL, /* breakpoint_kind_from_current_state */
ppc_supports_hardware_single_step,
+ NULL, /* get_syscall_trapinfo */
+ ppc_get_ipa_tdesc_idx,
};

void
@@ -811,6 +1539,7 @@ initialize_low_arch (void)
init_registers_powerpc_isa205_altivec32l ();
init_registers_powerpc_isa205_vsx32l ();
init_registers_powerpc_e500l ();
+#if __powerpc64__
init_registers_powerpc_64l ();
init_registers_powerpc_altivec64l ();
init_registers_powerpc_cell64l ();
@@ -818,6 +1547,7 @@ initialize_low_arch (void)
init_registers_powerpc_isa205_64l ();
init_registers_powerpc_isa205_altivec64l ();
init_registers_powerpc_isa205_vsx64l ();
+#endif

initialize_regsets_info (&ppc_regsets_info);
}
diff --git a/gdb/gdbserver/linux-ppc-tdesc.h b/gdb/gdbserver/linux-ppc-tdesc.h
new file mode 100644
index 0000000..d77b127
--- /dev/null
+++ b/gdb/gdbserver/linux-ppc-tdesc.h
@@ -0,0 +1,101 @@
+/* Low level support for ppc, shared between gdbserver and IPA.
+
+ Copyright (C) 2016 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/>. */
+
+/* Note: since IPA obviously knows what ABI it's running on (32 vs 64),
+ it's sufficient to pass only the register set here. This, together with
+ the ABI known at IPA compile time, maps to a tdesc. */
+
+enum ppc_linux_tdesc {
+ PPC_TDESC_BASE,
+ PPC_TDESC_ALTIVEC,
+ PPC_TDESC_CELL,
+ PPC_TDESC_VSX,
+ PPC_TDESC_ISA205,
+ PPC_TDESC_ISA205_ALTIVEC,
+ PPC_TDESC_ISA205_VSX,
+ PPC_TDESC_E500,
+};
+
+#if !defined __powerpc64__ || !defined IN_PROCESS_AGENT
+
+/* Defined in auto-generated file powerpc-32l.c. */
+void init_registers_powerpc_32l (void);
+extern const struct target_desc *tdesc_powerpc_32l;
+
+/* Defined in auto-generated file powerpc-altivec32l.c. */
+void init_registers_powerpc_altivec32l (void);
+extern const struct target_desc *tdesc_powerpc_altivec32l;
+
+/* Defined in auto-generated file powerpc-cell32l.c. */
+void init_registers_powerpc_cell32l (void);
+extern const struct target_desc *tdesc_powerpc_cell32l;
+
+/* Defined in auto-generated file powerpc-vsx32l.c. */
+void init_registers_powerpc_vsx32l (void);
+extern const struct target_desc *tdesc_powerpc_vsx32l;
+
+/* Defined in auto-generated file powerpc-isa205-32l.c. */
+void init_registers_powerpc_isa205_32l (void);
+extern const struct target_desc *tdesc_powerpc_isa205_32l;
+
+/* Defined in auto-generated file powerpc-isa205-altivec32l.c. */
+void init_registers_powerpc_isa205_altivec32l (void);
+extern const struct target_desc *tdesc_powerpc_isa205_altivec32l;
+
+/* Defined in auto-generated file powerpc-isa205-vsx32l.c. */
+void init_registers_powerpc_isa205_vsx32l (void);
+extern const struct target_desc *tdesc_powerpc_isa205_vsx32l;
+
+/* Defined in auto-generated file powerpc-e500l.c. */
+void init_registers_powerpc_e500l (void);
+extern const struct target_desc *tdesc_powerpc_e500l;
+
+#endif
+
+#if defined __powerpc64__
+
+/* Defined in auto-generated file powerpc-64l.c. */
+void init_registers_powerpc_64l (void);
+extern const struct target_desc *tdesc_powerpc_64l;
+
+/* Defined in auto-generated file powerpc-altivec64l.c. */
+void init_registers_powerpc_altivec64l (void);
+extern const struct target_desc *tdesc_powerpc_altivec64l;
+
+/* Defined in auto-generated file powerpc-cell64l.c. */
+void init_registers_powerpc_cell64l (void);
+extern const struct target_desc *tdesc_powerpc_cell64l;
+
+/* Defined in auto-generated file powerpc-vsx64l.c. */
+void init_registers_powerpc_vsx64l (void);
+extern const struct target_desc *tdesc_powerpc_vsx64l;
+
+/* Defined in auto-generated file powerpc-isa205-64l.c. */
+void init_registers_powerpc_isa205_64l (void);
+extern const struct target_desc *tdesc_powerpc_isa205_64l;
+
+/* Defined in auto-generated file powerpc-isa205-altivec64l.c. */
+void init_registers_powerpc_isa205_altivec64l (void);
+extern const struct target_desc *tdesc_powerpc_isa205_altivec64l;
+
+/* Defined in auto-generated file powerpc-isa205-vsx64l.c. */
+void init_registers_powerpc_isa205_vsx64l (void);
+extern const struct target_desc *tdesc_powerpc_isa205_vsx64l;
+
+#endif
--
2.7.2