gdbserver: Add powerpc fast tracepoint support.
authorMarcin Kościelnicki <koriakin@0x04.net>
Thu, 10 Mar 2016 00:38:18 +0000 (01:38 +0100)
committerMarcin Kościelnicki <koriakin@0x04.net>
Thu, 31 Mar 2016 13:39:23 +0000 (15:39 +0200)
gdb/gdbserver/ChangeLog:

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

PR/17221
* 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_hwcap): Rename to ppc_get_auxv and add type parameter.
(ppc_get_thread_area): New function.
(is_elfv2_inferior): 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.

gdb/gdbserver/ChangeLog
gdb/gdbserver/Makefile.in
gdb/gdbserver/configure.srv
gdb/gdbserver/linux-ppc-ipa.c [new file with mode: 0644]
gdb/gdbserver/linux-ppc-low.c
gdb/gdbserver/linux-ppc-tdesc.h [new file with mode: 0644]

index 045faee1ba1504cb7d31c4af13d9eaf82be6a585..87d7fd95e3140c17dc72c1deb92eb862602e421a 100644 (file)
@@ -1,3 +1,73 @@
+2016-03-31  Wei-cheng Wang  <cole945@gmail.com>
+           Marcin Kościelnicki  <koriakin@0x04.net>
+
+       PR/17221
+       * 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_hwcap): Rename to ppc_get_auxv and add type parameter.
+       (ppc_get_thread_area): New function.
+       (is_elfv2_inferior): 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-31  Marcin Kościelnicki  <koriakin@0x04.net>
 
        * linux-aarch64-ipa.c: Add <sys/mman.h> and <sys/auxv.h> includes.
index 48e843ba136cd10df86ddce1ad0d8727ad16adfd..953c07a62bf46581a30bae2b6f685499eaf52cd8 100644 (file)
@@ -579,6 +579,54 @@ s390x-vx-linux64-ipa.o: s390x-vx-linux64.c
 s390x-tevx-linux64-ipa.o: s390x-tevx-linux64.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)
index ce02876d1d75ec4d81de95004574f405bd153c04..64a4c36394e8fd5222ca5e1f2312cdea73a21018 100644 (file)
@@ -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 (file)
index 0000000..8489c6e
--- /dev/null
@@ -0,0 +1,245 @@
+/* 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"
+#ifdef HAVE_GETAUXVAL
+#include <sys/auxv.h>
+#endif
+
+/* 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 *(unsigned long *) (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;
+  uintptr_t exec_base = getauxval (AT_PHDR);
+  int pagesize;
+  void *res;
+
+  if (exec_base == 0)
+    exec_base = 0x10000000;
+
+  pagesize = sysconf (_SC_PAGE_SIZE);
+  if (pagesize == -1)
+    perror_with_name ("sysconf");
+
+  addr = exec_base - 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
+}
index 49d27ee70bb2801185e7f7047c1b36e64fc9a748..5af3292e8348e3cb08b3d7f5748573748ecb6ff0 100644 (file)
 #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__
@@ -342,7 +296,7 @@ ppc_set_pc (struct regcache *regcache, CORE_ADDR pc)
 
 
 static int
-ppc_get_hwcap (unsigned long *valp)
+ppc_get_auxv (unsigned long type, unsigned long *valp)
 {
   const struct target_desc *tdesc = current_process ()->tdesc;
   int wordsize = register_size (tdesc, 0);
@@ -354,7 +308,7 @@ ppc_get_hwcap (unsigned long *valp)
       if (wordsize == 4)
        {
          unsigned int *data_p = (unsigned int *)data;
-         if (data_p[0] == AT_HWCAP)
+         if (data_p[0] == type)
            {
              *valp = data_p[1];
              return 1;
@@ -363,7 +317,7 @@ ppc_get_hwcap (unsigned long *valp)
       else
        {
          unsigned long *data_p = (unsigned long *)data;
-         if (data_p[0] == AT_HWCAP)
+         if (data_p[0] == type)
            {
              *valp = data_p[1];
              return 1;
@@ -680,7 +634,7 @@ ppc_arch_setup (void)
   free_register_cache (regcache);
   if (ppc64_64bit_inferior_p (msr))
     {
-      ppc_get_hwcap (&ppc_hwcap);
+      ppc_get_auxv (AT_HWCAP, &ppc_hwcap);
       if (ppc_hwcap & PPC_FEATURE_CELL)
        tdesc = tdesc_powerpc_cell64l;
       else if (ppc_hwcap & PPC_FEATURE_HAS_VSX)
@@ -714,7 +668,7 @@ ppc_arch_setup (void)
   tdesc = tdesc_powerpc_32l;
   current_process ()->tdesc = tdesc;
 
-  ppc_get_hwcap (&ppc_hwcap);
+  ppc_get_auxv (AT_HWCAP, &ppc_hwcap);
   if (ppc_hwcap & PPC_FEATURE_CELL)
     tdesc = tdesc_powerpc_cell32l;
   else if (ppc_hwcap & PPC_FEATURE_HAS_VSX)
@@ -756,12 +710,804 @@ 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)
+{
+  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);
+  ULONGEST tp = 0;
+
+#ifdef __powerpc64__
+  if (register_size (regcache->tdesc, 0) == 8)
+    collect_register_by_name (regcache, "r13", &tp);
+  else
+#endif
+    collect_register_by_name (regcache, "r2", &tp);
+
+  *addr = tp;
+
+  return 0;
+}
+
+#ifdef __powerpc64__
+
+/* Older glibc doesn't provide this.  */
+
+#ifndef EF_PPC64_ABI
+#define EF_PPC64_ABI 3
+#endif
+
+/* Returns 1 if inferior is using ELFv2 ABI.  Undefined for 32-bit
+   inferiors.  */
+
+static int
+is_elfv2_inferior (void)
+{
+  /* To be used as fallback if we're unable to determine the right result -
+     assume inferior uses the same ABI as gdbserver.  */
+#if _CALL_ELF == 2
+  const int def_res = 1;
+#else
+  const int def_res = 0;
+#endif
+  unsigned long phdr;
+  Elf64_Ehdr ehdr;
+
+  if (!ppc_get_auxv (AT_PHDR, &phdr))
+    return def_res;
+
+  /* Assume ELF header is at the beginning of the page where program headers
+     are located.  If it doesn't look like one, bail.  */
+
+  read_inferior_memory (phdr & ~0xfff, (unsigned char *) &ehdr, sizeof ehdr);
+  if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG))
+    return def_res;
+
+  return (ehdr.e_flags & EF_PPC64_ABI) == 2;
+}
+
+#endif
+
+/* 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.  */
+
+#ifdef __powerpc64__
+#define GEN_LOAD(buf, rt, ra, si, is_64)       (is_64 ? \
+                                                GEN_LD (buf, rt, ra, si) : \
+                                                GEN_LWZ (buf, rt, ra, si))
+#define GEN_STORE(buf, rt, ra, si, is_64)      (is_64 ? \
+                                                GEN_STD (buf, rt, ra, si) : \
+                                                GEN_STW (buf, rt, ra, si))
+#else
+#define GEN_LOAD(buf, rt, ra, si, is_64)       GEN_LWZ (buf, rt, ra, si)
+#define GEN_STORE(buf, rt, ra, si, is_64)      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[15:0] */
+      p += GEN_LI (p, reg, imm);
+    }
+  else if ((imm >> 32) == 0)
+    {
+      /* lis   reg, imm[31:16]
+        ori    reg, reg, imm[15:0]
+        rldicl reg, reg, 0, 32 */
+      p += GEN_LIS (p, reg, (imm >> 16) & 0xffff);
+      if ((imm & 0xffff) != 0)
+       p += GEN_ORI (p, reg, reg, imm & 0xffff);
+      /* Clear upper 32-bit if sign-bit is set.  */
+      if (imm & (1u << 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));
+      if (((imm >> 32) & 0xffff) != 0)
+        p += GEN_ORI (p, reg, reg, ((imm >> 32) & 0xffff));
+      p += GEN_RLDICR (p, reg, reg, 32, 31);
+      if (((imm >> 16) & 0xffff) != 0)
+        p += GEN_ORIS (p, reg, reg, ((imm >> 16) & 0xffff));
+      if ((imm & 0xffff) != 0)
+        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, is_64);
+      p += GEN_LOAD (p, 2, 12, 8, is_64);
+      p += GEN_LOAD (p, 12, 12, 0, is_64);
+    }
+  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;
+  int is_opd = is_64 && !is_elfv2_inferior ();
+#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, is_64);
+  p += GEN_STORE (p, 0, 1, min_frame + 0 * rsz, is_64);
+  /* 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, is_64);
+
+  /* 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, is_64);/* std    r3, 32(r1) */
+  p += GEN_STORE (p, 4, 1, min_frame + 33 * rsz, is_64);/* std    r4, 33(r1) */
+  p += GEN_STORE (p, 5, 1, min_frame + 34 * rsz, is_64);/* std    r5, 34(r1) */
+  p += GEN_STORE (p, 6, 1, min_frame + 35 * rsz, is_64);/* 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, is_64);
+
+
+  /* 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, is_64);
+  p += GEN_STORE (p, tp_reg, 1, min_frame + 38 * rsz, is_64);
+  /* 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, is_64);
+
+  /* Restore stack and registers.  */
+  p += GEN_LOAD (p, 3, 1, min_frame + 32 * rsz, is_64);        /* ld   r3, 32(r1) */
+  p += GEN_LOAD (p, 4, 1, min_frame + 33 * rsz, is_64);        /* ld   r4, 33(r1) */
+  p += GEN_LOAD (p, 5, 1, min_frame + 34 * rsz, is_64);        /* ld   r5, 34(r1) */
+  p += GEN_LOAD (p, 6, 1, min_frame + 35 * rsz, is_64);        /* 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, is_64);
+  p += GEN_LOAD (p, 0, 1, min_frame + 0 * rsz, is_64);
+  /* 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 +1535,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 +1559,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 +1567,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 (file)
index 0000000..d77b127
--- /dev/null
@@ -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