From patchwork Sat Jun 27 16:21:41 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Wei-cheng, Wang" X-Patchwork-Id: 7414 Received: (qmail 47803 invoked by alias); 27 Jun 2015 16:22:32 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 47753 invoked by uid 89); 27 Jun 2015 16:22:29 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=0.9 required=5.0 tests=AWL, BAYES_50, FREEMAIL_ENVFROM_END_DIGIT, FREEMAIL_FROM, RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=ham version=3.3.2 X-HELO: mail-pa0-f47.google.com Received: from mail-pa0-f47.google.com (HELO mail-pa0-f47.google.com) (209.85.220.47) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-GCM-SHA256 encrypted) ESMTPS; Sat, 27 Jun 2015 16:22:00 +0000 Received: by pabvl15 with SMTP id vl15so83748841pab.1 for ; Sat, 27 Jun 2015 09:21:58 -0700 (PDT) X-Received: by 10.68.69.98 with SMTP id d2mr14900243pbu.71.1435422118369; Sat, 27 Jun 2015 09:21:58 -0700 (PDT) Received: from localhost.localdomain (114-32-204-230.HINET-IP.hinet.net. [114.32.204.230]) by mx.google.com with ESMTPSA id ju3sm27788766pbc.33.2015.06.27.09.21.56 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 27 Jun 2015 09:21:57 -0700 (PDT) From: Wei-cheng Wang To: uweigand@de.ibm.com, gdb-patches@sourceware.org Cc: Wei-cheng Wang Subject: [PATCH 4/5 v4] Tracepoint for ppc64. Date: Sun, 28 Jun 2015 00:21:41 +0800 Message-Id: <1435422102-39438-4-git-send-email-cole945@gmail.com> In-Reply-To: <1435422102-39438-1-git-send-email-cole945@gmail.com> References: <1435422102-39438-1-git-send-email-cole945@gmail.com> Hi, The main patch for tracepoint for ppc64 and snippets for enabling tracepoint tests. Ulrich Weigand wrote: > I think if there is no benefit in having this code on the GDB side, > it would be better to move it to gdbsever. (Potential benefits could > be: we need information that isn't available in gdbserver, like symbol > data; or the code can be shared with other users if in GDB. But on > PowerPC, I think none of this applies.) relocate_instruction is removed from rs6000-tdep.c and move to gdbserver side. Ulrich Weigand wrote: > Wei-cheng Wang wrote: > > Ulrich Weigand wrote: > > >Wei-cheng Wang wrote: > > >> +static int > > >> +ppc_fast_tracepoint_valid_at (struct gdbarch *gdbarch, > > >> + CORE_ADDR addr, int *isize, char **msg) > > >> +{ > > >> + if (isize) > > >> + *isize = gdbarch_max_insn_length (gdbarch); > > >> + if (msg) > > >> + *msg = NULL; > > >> + return 1; > > >> +} > > > > > > Should/can we check here where the jump to the jump pad will be in > > > range? Might be better to detect this early ... > > > >Client has no idea about where the jump pad will be installed. > >If it's out of range, gdbserver will report it right after user > >entered 'tstart' command > Well, but we know the logic the stub uses. For example, we know that > we certainly cannot install a fast tracepoint in any shared library code, > since the jump pad will definitely be too far away. We can check for > this condition here. (We could also check for tracepoints in executables > that have a text section larger than 32 MB ...) Now in this path, ppc_fast_tracepoint_valid_at will check the distance and return 0 (invalid) if ADDR is too far from jumppad. However, if a tracepoint was pending and later found it's not valid, it will cause an internal-error. See remote.c if (gdbarch_fast_tracepoint_valid_at (target_gdbarch (), tpaddr, &isize, NULL)) xsnprintf (buf + strlen (buf), BUF_SIZE - strlen (buf), ":F%x", isize); else /* If it passed validation at definition but fails now, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ something is very wrong. */ internal_error (__FILE__, __LINE__, _("Fast tracepoint not " "valid during download")); If the tracepoint is pending at definition, it won't be checked at all. /* Fast tracepoints may have additional restrictions on location. */ if (!pending && type_wanted == bp_fast_tracepoint) ^^^^^^^^ { for (ix = 0; VEC_iterate (linespec_sals, canonical.sals, ix, iter); ++ix) check_fast_tracepoint_sals (gdbarch, &iter->sals); } Maybe we use "error" instead of "internal_error"? BTW, in this patch, unlock is implemented by simply writing 0 instead of a full atomic-swap. It that ok? /* 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_atomic_xchg (p, lockaddr, 0, 5); /* Call to collector. */ p += gen_call (p, collector); /* Simply write 0 to release the lock. */ p += gen_limm (p, 3, lockaddr); p += gen_limm (p, 4, 0); p += GEN_LWSYNC (p); p += GEN_STORE (p, 4, 3, 0); Thanks, Wei-cheng --- gdb/ChangeLog 2015-06-27 Wei-cheng Wang * rs6000-tdep.c (ppc_fast_tracepoint_valid_at, ppc_gen_return_address): New functions. (rs6000_gdbarch_init): Hook ppc_fast_tracepoint_valid_at, and ppc_gen_return_address. gdb/gdbserver/ChangeLog 2015-06-27 Wei-cheng Wang * Makefile.in (linux-ppc-ipa.o, powerpc-64l-ipa.o, powerpc-32l-ipa.o): New rules. * configure.srv (powerpc*-*-linux*): Add powerpc-64l-ipa.o, powerpc-32l-ipa.o, and linux-ppc-ipa.o in ipa_obj * linux-ppc-ipa.c: New file. * linux-ppc-low.c (gen_ds_form, gen_d_form, gen_xfx_form, gen_x_form, gen_md_form, gen_i_form, gen_b_form, gen_limm, gen_atomic_xchg, gen_call, ppc_supports_tracepoints, ppc_install_fast_tracepoint_jump_pad, ppc_get_min_fast_tracepoint_insn_len, emit_insns, ppc64_emit_prologue, ppc64_emit_epilogue, ppc64_emit_add, ppc64_emit_sub, ppc64_emit_mul, ppc64_emit_lsh, ppc64_emit_rsh_signed, ppc64_emit_rsh_unsigned, ppc64_emit_ext, ppc64_emit_zero_ext, ppc64_emit_log_not, ppc64_emit_bit_and, ppc64_emit_bit_or, ppc64_emit_bit_xor, ppc64_emit_bit_not, ppc64_emit_equal, ppc64_emit_less_signed, ppc64_emit_less_unsigned, ppc64_emit_ref, ppc64_emit_const, ppc64_emit_reg, ppc64_emit_pop, ppc64_emit_stack_flush, ppc64_emit_swap, ppc64_emit_stack_adjust, ppc64_emit_call, ppc64_emit_int_call_1, ppc64_emit_void_call_2, ppc64_emit_if_goto, ppc64_emit_goto, ppc64_emit_eq_goto, ppc64_emit_ne_goto, ppc64_emit_lt_goto, ppc64_emit_le_goto, ppc64_emit_gt_goto, ppc64_emit_ge_goto, ppc_write_goto_address, ppc_emit_ops, ppc_supports_range_stepping, ppc_fast_tracepoint_valid_at, ppc_relocate_instruction): New functions. (ppc64_emit_ops_vector): New struct for bytecode compilation. (the_low_target): Add target ops - ppc_supports_tracepoints, ppc_install_fast_tracepoint_jump_pad, ppc_emit_ops, ppc_get_min_fast_tracepoint_insn_len, ppc_supports_range_stepping. gdb/testsuite/ChangeLog 2015-06-27 Wei-cheng Wang * gdb.trace/backtrace.exp: Set registers for powerpc*-*-*. * gdb.trace/collection.exp: Ditto. * gdb.trace/entry-values.exp: Ditto. * gdb.trace/mi-trace-frame-collected.exp: Ditto. * gdb.trace/mi-trace-unavailable.exp: Ditto. * gdb.trace/pending.exp: Ditto. * gdb.trace/report.exp: Ditto. * gdb.trace/trace-break.exp: Ditto. * gdb.trace/while-dyn.exp: Ditto. * gdb.trace/change-loc.h: set_point for powerpc. * gdb.trace/ftrace.c: Ditto * gdb.trace/pendshr1.c: Ditto. * gdb.trace/pendshr2.c: Ditto. * gdb.trace/range-stepping.c: Ditto. * gdb.trace/trace-break.c: Ditto. * gdb.trace/trace-mt.c: Ditto. * gdb.trace/ftrace.exp: Enable testing for powerpc*-*-*. --- gdb/gdbserver/Makefile.in | 9 + gdb/gdbserver/configure.srv | 1 + gdb/gdbserver/linux-ppc-ipa.c | 120 ++ gdb/gdbserver/linux-ppc-low.c | 1408 +++++++++++++++++++- gdb/rs6000-tdep.c | 117 ++ gdb/testsuite/gdb.trace/actions.c | 9 +- gdb/testsuite/gdb.trace/backtrace.exp | 3 + gdb/testsuite/gdb.trace/change-loc.h | 2 + gdb/testsuite/gdb.trace/collection.exp | 4 + gdb/testsuite/gdb.trace/entry-values.exp | 4 +- gdb/testsuite/gdb.trace/ftrace.c | 4 + gdb/testsuite/gdb.trace/ftrace.exp | 7 +- .../gdb.trace/mi-trace-frame-collected.exp | 2 + gdb/testsuite/gdb.trace/mi-trace-unavailable.exp | 2 + gdb/testsuite/gdb.trace/pending.exp | 2 + gdb/testsuite/gdb.trace/pendshr1.c | 2 + gdb/testsuite/gdb.trace/pendshr2.c | 2 + gdb/testsuite/gdb.trace/range-stepping.c | 2 + gdb/testsuite/gdb.trace/report.exp | 4 + gdb/testsuite/gdb.trace/trace-break.c | 4 + gdb/testsuite/gdb.trace/trace-break.exp | 4 + gdb/testsuite/gdb.trace/trace-mt.c | 2 + gdb/testsuite/gdb.trace/unavailable.exp | 4 + gdb/testsuite/gdb.trace/while-dyn.exp | 2 + 24 files changed, 1712 insertions(+), 8 deletions(-) create mode 100644 gdb/gdbserver/linux-ppc-ipa.c diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in index fc250fb..6dab7e3 100644 --- a/gdb/gdbserver/Makefile.in +++ b/gdb/gdbserver/Makefile.in @@ -499,6 +499,15 @@ linux-amd64-ipa.o: linux-amd64-ipa.c amd64-linux-ipa.o: amd64-linux.c $(IPAGENT_COMPILE) $< $(POSTCOMPILE) +linux-ppc-ipa.o: linux-ppc-ipa.c + $(IPAGENT_COMPILE) $< + $(POSTCOMPILE) +powerpc-64l-ipa.o: powerpc-64l.c + $(IPAGENT_COMPILE) $< + $(POSTCOMPILE) +powerpc-32l-ipa.o: powerpc-32l.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 7f89f2f..4f34bd8 100644 --- a/gdb/gdbserver/configure.srv +++ b/gdb/gdbserver/configure.srv @@ -246,6 +246,7 @@ case "${target}" in srv_linux_usrregs=yes srv_linux_regsets=yes srv_linux_thread_db=yes + ipa_obj="powerpc-64l-ipa.o powerpc-32l-ipa.o 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..73d8899 --- /dev/null +++ b/gdb/gdbserver/linux-ppc-ipa.c @@ -0,0 +1,120 @@ +/* GNU/Linux/PowerPC specific low level interface, for the in-process + agent library for GDB. + + Copyright (C) 2010-2015 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "server.h" +#include "tracepoint.h" + +#if defined __PPC64__ +void init_registers_powerpc_64l (void); +extern const struct target_desc *tdesc_powerpc_64l; +#define REGSZ 8 +#else +void init_registers_powerpc_32l (void); +extern const struct target_desc *tdesc_powerpc_32l; +#define REGSZ 4 +#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] * REGSZ); + } +} + +/* Return the value of register REGNUM. RAW_REGS is collected buffer + by jump pad. This function is called by emit_reg. */ + +IP_AGENT_EXPORT_FUNC ULONGEST +gdb_agent_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] * REGSZ); +} + +/* Initialize ipa_tdesc and others. */ + +void +initialize_low_tracepoint (void) +{ +#if defined __PPC64__ + init_registers_powerpc_64l (); + ipa_tdesc = tdesc_powerpc_64l; +#else + init_registers_powerpc_32l (); + ipa_tdesc = tdesc_powerpc_32l; +#endif +} diff --git a/gdb/gdbserver/linux-ppc-low.c b/gdb/gdbserver/linux-ppc-low.c index 41ec281..064f508 100644 --- a/gdb/gdbserver/linux-ppc-low.c +++ b/gdb/gdbserver/linux-ppc-low.c @@ -24,6 +24,19 @@ #include #include "nat/ppc-linux.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; @@ -575,6 +588,1377 @@ ppc_remove_point (enum raw_bkpt_type type, CORE_ADDR addr, } } +/* 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) + +/* 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_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 __PPC64__ +#define GEN_LOAD(buf, rt, ra, si) GEN_LD (buf, rt, ra, si) +#define GEN_STORE(buf, rt, ra, si) GEN_STD (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) +{ + 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] */ + p += GEN_LIS (p, reg, (imm >> 16) & 0xffff); + p += GEN_ORI (p, reg, reg, imm & 0xffff); + } + else + { + /* lis reg, + ori reg, reg, + rldicr reg, reg, 32, 31 + oris reg, reg, + ori reg, reg, */ + 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) +{ + const int r_lock = 6; + const int r_old = 7; + const int r_tmp = 8; + uint32_t *p = buf; + + /* + 1: lwsync + 2: lwarx TMP, 0, LOCK + cmpwi TMP, OLD + bne 1b + stwcx. NEW, 0, LOCK + bne 2b */ + + p += gen_limm (p, r_lock, lock); + p += gen_limm (p, r_old, old_value); + + p += GEN_LWSYNC (p); + p += GEN_LWARX (p, r_tmp, 0, r_lock); + p += GEN_CMPW (p, r_tmp, r_old); + p += GEN_BNE (p, -12); + 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. + + FIXME: For ppc64be, FN should be the address to the function + descriptor, so we should load 8(FN) to R2, 16(FN) to R11 + and then call the function-entry at 0(FN). However, current GDB + implicitly convert the address from function descriptor to the actual + function address. See qSymbol handling in remote.c. Although it + seems we can successfully call however, things go wrong when callee + trying to access global variable. */ + +static int +gen_call (uint32_t *buf, CORE_ADDR fn) +{ + uint32_t *p = buf; + + /* Must be called by r12 for caller to calculate TOC address. */ + p += gen_limm (p, 12, fn); + p += GEN_MTSPR (p, 12, 9); /* mtctr r12 */ + *p++ = 0x4e800421; /* bctrl */ + + return p - buf; +} + +/* Implement supports_tracepoints hook of target_ops. + Always return true. */ + +static int +ppc_supports_tracepoints (void) +{ +#if defined (__PPC64__) && _CALL_ELF == 2 + return 1; +#else + return 0; +#endif +} + +/* 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; + +#if __PPC64__ + collect_register_by_name (regcache, "r13", &tp); +#else + collect_register_by_name (regcache, "r2", &tp); +#endif + + *addr = tp; + return 0; +} + +/* 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 + { + uint32_t bdnz_insn = (16 << 26) | (0x10 << 21) | 12; + uint32_t bf_insn = (16 << 26) | (0x4 << 21) | 8; + + newrel -= 12; + + /* 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; + } + } + + 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; +#if __PPC64__ + /* Minimum frame size is 32 bytes for ELFv2, and 112 bytes for ELFv1. */ + const int rsz = 8; + const int min_frame = 112; + const int frame_size = (40 * rsz) + min_frame; + const int tp_reg = 13; +#else + const int rsz = 4; + const int min_frame = 16; + const int frame_size = (40 * rsz) + min_frame; + const int tp_reg = 2; +#endif + + /* Stack frame layout for this jump pad, + + High thread_area (r13/r2) | + tpoint - collecting_t obj + PC/ | +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. */ + p += GEN_STDU (p, 1, 1, -frame_size); /* stdu 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 */ + p += gen_limm (p, 3, tpaddr); + 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); + + /* 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_atomic_xchg (p, lockaddr, 0, 5); + + /* Call to collector. */ + p += gen_call (p, collector); + + /* Simply write 0 to release the lock. */ + p += gen_limm (p, 3, lockaddr); + p += gen_limm (p, 4, 0); + 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 */ + 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 */ + 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 () +{ + return 4; +} + +#if __PPC64__ + +static void +emit_insns (uint32_t *buf, int n) +{ + n = n * sizeof (uint32_t); + write_inferior_memory (current_insn_ptr, (unsigned char *) buf, n); + current_insn_ptr += n; +} + +#define __EMIT_ASM(NAME, INSNS) \ + do \ + { \ + extern uint32_t start_bcax_ ## NAME []; \ + extern uint32_t end_bcax_ ## NAME []; \ + emit_insns (start_bcax_ ## NAME, \ + end_bcax_ ## NAME - start_bcax_ ## NAME); \ + __asm__ (".section .text.__ppcbcax\n\t" \ + "start_bcax_" #NAME ":\n\t" \ + INSNS "\n\t" \ + "end_bcax_" #NAME ":\n\t" \ + ".previous\n\t"); \ + } while (0) + +#define _EMIT_ASM(NAME, INSNS) __EMIT_ASM (NAME, INSNS) +#define EMIT_ASM(INSNS) _EMIT_ASM (__LINE__, INSNS) + +/* + + Bytecode execution stack frame + + | LR save area (SP + 16) + | CR save area (SP + 8) + SP' -> +- Back chain (SP + 0) + | Save r31 for access saved arguments + | Save r30 for bytecode stack pointer + | Save r4 for incoming argument *value + | Save r3 for incoming argument regs + r30 -> +- Bytecode execution stack + | + | 64-byte (8 doublewords) at initial. + | Expand stack as needed. + | + +- + | Some padding for minimum stack frame. + | 112 for ELFv1. + SP +- Back-chain (SP') + + initial frame size + = 112 + (4 * 8) + 64 + = 208 + + r30 is the stack-pointer for bytecode machine. + It should point to next-empty, so we can use LDU for pop. + r3 is used for cache of TOP value. + It was the first argument, pointer to regs. + r4 is the second argument, pointer to the result. + We should set *result = TOP after leaving this function. + + Note: + * To restore stack at epilogue + => sp = r31 + 208 + * To check stack is big enough for bytecode execution. + => r30 - 8 > SP + 112 + * To return execution result. + => 0(r4) = TOP + + */ + +/* Emit prologue in inferior memory. See above comments. */ + +static void +ppc64_emit_prologue (void) +{ + EMIT_ASM (/* Save return address. */ + "mflr 0 \n" + "std 0, 16(1) \n" + /* Save r30 and incoming arguments. */ + "std 31, -8(1) \n" + "std 30, -16(1) \n" + "std 4, -24(1) \n" + "std 3, -32(1) \n" + /* Point r31 to current r1 for access arguments. */ + "mr 31, 1 \n" + /* Adjust SP. 208 is the initial frame size. */ + "stdu 1, -208(1) \n" + /* Set r30 to pointing stack-top. */ + "addi 30, 1, 168 \n" + /* Initial r3/TOP to 0. */ + "li 3, 0 \n"); +} + +/* Emit epilogue in inferior memory. See above comments. */ + +static void +ppc64_emit_epilogue (void) +{ + EMIT_ASM (/* Restore SP. */ + "ld 1, 0(1) \n" + /* *result = TOP */ + "ld 4, -24(1) \n" + "std 3, 0(4) \n" + /* Restore registers. */ + "ld 31, -8(1) \n" + "ld 30, -16(1) \n" + /* Restore LR. */ + "ld 0, 16(1) \n" + /* Return 0 for no-error. */ + "li 3, 0 \n" + "mtlr 0 \n" + "blr \n"); +} + +/* TOP = stack[--sp] + TOP */ + +static void +ppc64_emit_add (void) +{ + EMIT_ASM ("ldu 4, 8(30) \n" + "add 3, 4, 3 \n"); +} + +/* TOP = stack[--sp] - TOP */ + +static void +ppc64_emit_sub (void) +{ + EMIT_ASM ("ldu 4, 8(30) \n" + "sub 3, 4, 3 \n"); +} + +/* TOP = stack[--sp] * TOP */ + +static void +ppc64_emit_mul (void) +{ + EMIT_ASM ("ldu 4, 8(30) \n" + "mulld 3, 4, 3 \n"); +} + +/* TOP = stack[--sp] << TOP */ + +static void +ppc64_emit_lsh (void) +{ + EMIT_ASM ("ldu 4, 8(30) \n" + "sld 3, 4, 3 \n"); +} + +/* Top = stack[--sp] >> TOP + (Arithmetic shift right) */ + +static void +ppc64_emit_rsh_signed (void) +{ + EMIT_ASM ("ldu 4, 8(30) \n" + "srad 3, 4, 3 \n"); +} + +/* Top = stack[--sp] >> TOP + (Logical shift right) */ + +static void +ppc64_emit_rsh_unsigned (void) +{ + EMIT_ASM ("ldu 4, 8(30) \n" + "srd 3, 4, 3 \n"); +} + +/* Emit code for signed-extension specified by ARG. */ + +static void +ppc64_emit_ext (int arg) +{ + switch (arg) + { + case 8: + EMIT_ASM ("extsb 3, 3"); + break; + case 16: + EMIT_ASM ("extsh 3, 3"); + break; + case 32: + EMIT_ASM ("extsw 3, 3"); + break; + default: + emit_error = 1; + } +} + +/* Emit code for zero-extension specified by ARG. */ + +static void +ppc64_emit_zero_ext (int arg) +{ + switch (arg) + { + case 8: + EMIT_ASM ("rldicl 3,3,0,56"); + break; + case 16: + EMIT_ASM ("rldicl 3,3,0,48"); + break; + case 32: + EMIT_ASM ("rldicl 3,3,0,32"); + break; + default: + emit_error = 1; + } +} + +/* TOP = !TOP + i.e., TOP = (TOP == 0) ? 1 : 0; */ + +static void +ppc64_emit_log_not (void) +{ + EMIT_ASM ("cntlzd 3, 3 \n" + "srdi 3, 3, 6 \n"); +} + +/* TOP = stack[--sp] & TOP */ + +static void +ppc64_emit_bit_and (void) +{ + EMIT_ASM ("ldu 4, 8(30) \n" + "and 3, 4, 3 \n"); +} + +/* TOP = stack[--sp] | TOP */ + +static void +ppc64_emit_bit_or (void) +{ + EMIT_ASM ("ldu 4, 8(30) \n" + "or 3, 4, 3 \n"); +} + +/* TOP = stack[--sp] ^ TOP */ + +static void +ppc64_emit_bit_xor (void) +{ + EMIT_ASM ("ldu 4, 8(30) \n" + "xor 3, 4, 3 \n"); +} + +/* TOP = ~TOP + i.e., TOP = ~(TOP | TOP) */ + +static void +ppc64_emit_bit_not (void) +{ + EMIT_ASM ("nor 3, 3, 3 \n"); +} + +/* TOP = stack[--sp] == TOP */ + +static void +ppc64_emit_equal (void) +{ + EMIT_ASM ("ldu 4, 8(30) \n" + "xor 3, 3, 4 \n" + "cntlzd 3, 3 \n" + "srdi 3, 3, 6 \n"); +} + +/* TOP = stack[--sp] < TOP + (Signed comparison) */ + +static void +ppc64_emit_less_signed (void) +{ + EMIT_ASM ("ldu 4, 8(30) \n" + "cmpd 7, 4, 3 \n" + "mfocrf 3, 1 \n" + "rlwinm 3, 3, 29, 31, 31 \n"); +} + +/* TOP = stack[--sp] < TOP + (Unsigned comparison) */ + +static void +ppc64_emit_less_unsigned (void) +{ + EMIT_ASM ("ldu 4, 8(30) \n" + "cmpld 7, 4, 3 \n" + "mfocrf 3, 1 \n" + "rlwinm 3, 3, 29, 31, 31 \n"); +} + +/* Access the memory address in TOP in size of SIZE. + Zero-extend the read value. */ + +static void +ppc64_emit_ref (int size) +{ + switch (size) + { + case 1: + EMIT_ASM ("lbz 3, 0(3)"); + break; + case 2: + EMIT_ASM ("lhz 3, 0(3)"); + break; + case 4: + EMIT_ASM ("lwz 3, 0(3)"); + break; + case 8: + EMIT_ASM ("ld 3, 0(3)"); + break; + } +} + +/* TOP = NUM */ + +static void +ppc64_emit_const (LONGEST num) +{ + uint32_t buf[5]; + uint32_t *p = buf; + + p += gen_limm (p, 3, num); + + emit_insns (buf, p - buf); + gdb_assert ((p - buf) <= sizeof (buf)); +} + +/* Set TOP to the value of register REG by calling get_raw_reg function + with two argument, collected buffer and register number. */ + +static void +ppc64_emit_reg (int reg) +{ + uint32_t buf[10]; + uint32_t *p = buf; + + /* fctx->regs is passed in r3 and then saved in 176(1). */ + p += GEN_LOAD (p, 3, 31, -32); + p += GEN_LI (p, 4, reg); /* li r4, reg */ + p += gen_call (p, get_raw_reg_func_addr ()); + + emit_insns (buf, p - buf); + gdb_assert ((p - buf) <= sizeof (buf)); +} + +/* TOP = stack[--sp] */ + +static void +ppc64_emit_pop (void) +{ + EMIT_ASM ("ldu 3, 8(30)"); +} + +/* stack[sp++] = TOP + + Because we may use up bytecode stack, expand 8 doublewords more + if needed. */ + +static void +ppc64_emit_stack_flush (void) +{ + /* Make sure bytecode stack is big enough before push. + Otherwise, expand 64-byte more. */ + + EMIT_ASM (" std 3, 0(30) \n" + " addi 4, 30, -(112 + 8) \n" + " cmpd 7, 4, 1 \n" + " bgt 7, 1f \n" + " ld 4, 0(1) \n" + " stdu 4, -64(1) \n" + "1:addi 30, 30, -8 \n"); +} + +/* Swap TOP and stack[sp-1] */ + +static void +ppc64_emit_swap (void) +{ + EMIT_ASM ("ld 4, 8(30) \n" + "std 3, 8(30) \n" + "mr 3, 4 \n"); +} + +/* Discard N elements in the stack. */ + +static void +ppc64_emit_stack_adjust (int n) +{ + uint32_t buf[4]; + uint32_t *p = buf; + + n = n << 3; + if ((n >> 7) != 0) + { + emit_error = 1; + return; + } + + p += GEN_ADDI (p, 30, 30, n); + + emit_insns (buf, p - buf); + gdb_assert ((p - buf) <= sizeof (buf)); +} + +/* Call function FN. */ + +static void +ppc64_emit_call (CORE_ADDR fn) +{ + uint32_t buf[8]; + uint32_t *p = buf; + + p += gen_call (p, fn); + + emit_insns (buf, p - buf); + gdb_assert ((p - buf) <= sizeof (buf)); +} + +/* FN's prototype is `LONGEST(*fn)(int)'. + TOP = fn (arg1) + */ + +static void +ppc64_emit_int_call_1 (CORE_ADDR fn, int arg1) +{ + uint32_t buf[8]; + uint32_t *p = buf; + + /* Setup argument. arg1 is a 16-bit value. */ + p += gen_limm (p, 3, arg1); + p += gen_call (p, fn); + + emit_insns (buf, p - buf); + gdb_assert ((p - buf) <= sizeof (buf)); +} + +/* FN's prototype is `void(*fn)(int,LONGEST)'. + fn (arg1, TOP) + + TOP should be preserved/restored before/after the call. */ + +static void +ppc64_emit_void_call_2 (CORE_ADDR fn, int arg1) +{ + uint32_t buf[12]; + uint32_t *p = buf; + + /* Save TOP. 0(30) is next-empty. */ + p += GEN_STORE (p, 3, 30, 0); + + /* Setup argument. arg1 is a 16-bit value. */ + p += GEN_MR (p, 4, 3); /* mr r4, r3 */ + p += gen_limm (p, 3, arg1); + p += gen_call (p, fn); + + /* Restore TOP */ + p += GEN_LOAD (p, 3, 30, 0); + + emit_insns (buf, p - buf); + gdb_assert ((p - buf) <= sizeof (buf)); +} + +/* Note in the following goto ops: + + When emitting goto, the target address is later relocated by + write_goto_address. OFFSET_P is the offset of the branch instruction + in the code sequence, and SIZE_P is how to relocate the instruction, + recognized by ppc_write_goto_address. In current implementation, + SIZE can be either 24 or 14 for branch of conditional-branch instruction. + */ + +/* If TOP is true, goto somewhere. Otherwise, just fall-through. */ + +static void +ppc64_emit_if_goto (int *offset_p, int *size_p) +{ + EMIT_ASM ("cmpdi 7, 3, 0 \n" + "ldu 3, 8(30) \n" + "1:bne 7, 1b \n"); + + if (offset_p) + *offset_p = 12; + if (size_p) + *size_p = 14; +} + +/* Unconditional goto. */ + +static void +ppc64_emit_goto (int *offset_p, int *size_p) +{ + EMIT_ASM ("1:b 1b"); + + if (offset_p) + *offset_p = 0; + if (size_p) + *size_p = 24; +} + +/* Goto if stack[--sp] == TOP */ + +static void +ppc64_emit_eq_goto (int *offset_p, int *size_p) +{ + EMIT_ASM ("ldu 4, 8(30) \n" + "cmpd 7, 4, 3 \n" + "ldu 3, 8(30) \n" + "1:beq 7, 1b \n"); + + if (offset_p) + *offset_p = 12; + if (size_p) + *size_p = 14; +} + +/* Goto if stack[--sp] != TOP */ + +static void +ppc64_emit_ne_goto (int *offset_p, int *size_p) +{ + EMIT_ASM ("ldu 4, 8(30) \n" + "cmpd 7, 4, 3 \n" + "ldu 3, 8(30) \n" + "1:bne 7, 1b \n"); + + if (offset_p) + *offset_p = 12; + if (size_p) + *size_p = 14; +} + +/* Goto if stack[--sp] < TOP */ + +static void +ppc64_emit_lt_goto (int *offset_p, int *size_p) +{ + EMIT_ASM ("ldu 4, 8(30) \n" + "cmpd 7, 4, 3 \n" + "ldu 3, 8(30) \n" + "1:blt 7, 1b \n"); + + if (offset_p) + *offset_p = 12; + if (size_p) + *size_p = 14; +} + +/* Goto if stack[--sp] <= TOP */ + +static void +ppc64_emit_le_goto (int *offset_p, int *size_p) +{ + EMIT_ASM ("ldu 4, 8(30) \n" + "cmpd 7, 4, 3 \n" + "ldu 3, 8(30) \n" + "1:ble 7, 1b \n"); + + if (offset_p) + *offset_p = 12; + if (size_p) + *size_p = 14; +} + +/* Goto if stack[--sp] > TOP */ + +static void +ppc64_emit_gt_goto (int *offset_p, int *size_p) +{ + EMIT_ASM ("ldu 4, 8(30) \n" + "cmpd 7, 4, 3 \n" + "ldu 3, 8(30) \n" + "1:bgt 7, 1b \n"); + + if (offset_p) + *offset_p = 12; + if (size_p) + *size_p = 14; +} + +/* Goto if stack[--sp] >= TOP */ + +static void +ppc64_emit_ge_goto (int *offset_p, int *size_p) +{ + EMIT_ASM ("ldu 4, 8(30) \n" + "cmpd 7, 4, 3 \n" + "ldu 3, 8(30) \n" + "1:bge 7, 1b \n"); + + if (offset_p) + *offset_p = 12; + if (size_p) + *size_p = 14; +} + +/* Relocate previous emitted branch instruction. FROM is the address + of the branch instruction, TO is the goto target address, and SIZE + if the value we set by *SIZE_P before. Currently, it is either + 24 or 14 of branch and conditional-branch instruction. */ + +static void +ppc_write_goto_address (CORE_ADDR from, CORE_ADDR to, int size) +{ + int rel = to - from; + uint32_t insn; + int opcd; + + read_inferior_memory (from, (unsigned char *) &insn, 4); + opcd = (insn >> 26) & 0x3f; + + switch (size) + { + case 14: + if (opcd != 16 + || (rel >= (1 << 15) || rel < -(1 << 15))) + emit_error = 1; + insn = (insn & ~0xfffc) | (rel & 0xfffc); + break; + case 24: + if (opcd != 18 + || (rel >= (1 << 25) || rel < -(1 << 25))) + emit_error = 1; + insn = (insn & ~0x3fffffc) | (rel & 0x3fffffc); + break; + default: + emit_error = 1; + } + + if (!emit_error) + write_inferior_memory (from, (unsigned char *) &insn, 4); +} + +/* Vector of emit ops for PowerPC64. */ + +static struct emit_ops ppc64_emit_ops_vector = +{ + ppc64_emit_prologue, + ppc64_emit_epilogue, + ppc64_emit_add, + ppc64_emit_sub, + ppc64_emit_mul, + ppc64_emit_lsh, + ppc64_emit_rsh_signed, + ppc64_emit_rsh_unsigned, + ppc64_emit_ext, + ppc64_emit_log_not, + ppc64_emit_bit_and, + ppc64_emit_bit_or, + ppc64_emit_bit_xor, + ppc64_emit_bit_not, + ppc64_emit_equal, + ppc64_emit_less_signed, + ppc64_emit_less_unsigned, + ppc64_emit_ref, + ppc64_emit_if_goto, + ppc64_emit_goto, + ppc_write_goto_address, + ppc64_emit_const, + ppc64_emit_call, + ppc64_emit_reg, + ppc64_emit_pop, + ppc64_emit_stack_flush, + ppc64_emit_zero_ext, + ppc64_emit_swap, + ppc64_emit_stack_adjust, + ppc64_emit_int_call_1, + ppc64_emit_void_call_2, + ppc64_emit_eq_goto, + ppc64_emit_ne_goto, + ppc64_emit_lt_goto, + ppc64_emit_le_goto, + ppc64_emit_gt_goto, + ppc64_emit_ge_goto +}; + +/* Implementation of emit_ops target ops. */ + +__attribute__ ((unused)) +static struct emit_ops * +ppc_emit_ops (void) +{ + return &ppc64_emit_ops_vector; +} +#endif + +/* Returns true for supporting range-stepping. */ + +static int +ppc_supports_range_stepping (void) +{ + return 1; +} + /* Provide only a fill function for the general register set. ps_lgetregs will use this for NPTL support. */ @@ -750,16 +2134,32 @@ struct linux_target_ops the_low_target = { ppc_set_pc, (const unsigned char *) &ppc_breakpoint, ppc_breakpoint_len, - NULL, - 0, + NULL, /* breakpoint_reinsert_addr */ + 0, /* decr_pc_after_break */ ppc_breakpoint_at, ppc_supports_z_point_type, ppc_insert_point, ppc_remove_point, - NULL, - NULL, + NULL, /* stopped_by_watchpoint */ + NULL, /* stopped_data_address */ ppc_collect_ptrace_register, ppc_supply_ptrace_register, + NULL, /* siginfo_fixup */ + NULL, /* linux_new_process */ + NULL, /* linux_new_thread */ + NULL, /* linux_new_fork */ + NULL, /* linux_prepare_to_resume */ + NULL, /* linux_process_qsupported */ + ppc_supports_tracepoints, + ppc_get_thread_area, + ppc_install_fast_tracepoint_jump_pad, +#if __PPC64__ + ppc_emit_ops, +#else + NULL, /* Use interpreter for ppc32. */ +#endif + ppc_get_min_fast_tracepoint_insn_len, + ppc_supports_range_stepping, }; void diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c index d947879..7924227 100644 --- a/gdb/rs6000-tdep.c +++ b/gdb/rs6000-tdep.c @@ -83,6 +83,9 @@ #include "features/rs6000/powerpc-e500.c" #include "features/rs6000/rs6000.c" +#include "ax.h" +#include "ax-gdb.h" + /* Determine if regnum is an SPE pseudo-register. */ #define IS_SPE_PSEUDOREG(tdep, regnum) ((tdep)->ppc_ev0_regnum >= 0 \ && (regnum) >= (tdep)->ppc_ev0_regnum \ @@ -966,6 +969,53 @@ rs6000_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *bp_addr, return little_breakpoint; } +/* Return true if ADDR is a valid address for tracepoint. Set *ISZIE + to the number of bytes the target should copy elsewhere for the + tracepoint. */ + +static int +ppc_fast_tracepoint_valid_at (struct gdbarch *gdbarch, + CORE_ADDR addr, int *isize, char **msg) +{ + CORE_ADDR base, pagesz; + const int SCRATCH_BUFFER_NPAGES = 20; + int isValid = 1; + + if (isize) + *isize = gdbarch_max_insn_length (gdbarch); + + /* If we can figure out where is the jump-pad, check whether + the address for tracepoint is too far away. Otherwise, + assume it is valid. */ + if (target_auxv_search (¤t_target, AT_PHDR, &base) > 0 + && target_auxv_search (¤t_target, AT_PAGESZ, &pagesz) > 0) + { + /* The jump-pad is supposed to be mapped here. + See gdbserver/linux-ppc-ipa.c and gdbserver/tracepoint.c. */ + long dist; + CORE_ADDR jpad_base + = (base & ~(pagesz - 1)) - SCRATCH_BUFFER_NPAGES * pagesz; + + dist = jpad_base - addr; + if (dist >= (1 << 25) || dist < -(1 << 25)) + isValid = 0; + } + + if (isValid) + { + if (msg) + *msg = NULL; + } + else + { + if (msg) + *msg = xstrdup (_("The address is too far for " + "inserting fast tracepoint.")); + } + + return isValid; +} + /* Instruction masks for displaced stepping. */ #define BRANCH_MASK 0xfc000000 #define BP_MASK 0xFC0007FE @@ -3709,6 +3759,8 @@ bfd_uses_spe_extensions (bfd *abfd) #define PPC_LK(insn) PPC_BIT (insn, 31) #define PPC_TX(insn) PPC_BIT (insn, 31) #define PPC_LEV(insn) PPC_FIELD (insn, 20, 7) +#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) #define PPC_XT(insn) ((PPC_TX (insn) << 5) | PPC_T (insn)) #define PPC_XER_NB(xer) (xer & 0x7f) @@ -5362,6 +5414,68 @@ UNKNOWN_OP: return 0; } +/* Implement gdbarch_gen_return_address. Generate a bytecode expression + to get the value of the saved PC. SCOPE is the address we want to + get return address for. SCOPE maybe in the middle of a function. */ + +static void +ppc_gen_return_address (struct gdbarch *gdbarch, + struct agent_expr *ax, struct axs_value *value, + CORE_ADDR scope) +{ + struct rs6000_framedata frame; + CORE_ADDR func_addr; + + /* Try to find the start of the function and analyze the prologue. */ + if (find_pc_partial_function (scope, NULL, &func_addr, NULL)) + { + skip_prologue (gdbarch, func_addr, scope, &frame); + + if (frame.lr_offset == 0) + { + value->type = register_type (gdbarch, PPC_LR_REGNUM); + value->kind = axs_lvalue_register; + value->u.reg = PPC_LR_REGNUM; + return; + } + } + else + { + /* If we don't know where the function starts, we cannot analyze it. + Assuming it's not a leaf function, not frameless, and LR is + saved at back-chain +16 (or +4 for 32-bit ABI). */ + + frame.frameless = 0; +#if __PPC64__ + frame.lr_offset = 16; +#else + frame.lr_offset = 4; +#endif + } + + /* if (frameless) + load 16(SP) + else + BC = 0(SP) + load 16(BC) */ + + ax_reg (ax, gdbarch_sp_regnum (gdbarch)); + + /* Load back-chain. */ + if (!frame.frameless) + { + if (register_size (gdbarch, PPC_LR_REGNUM) == 8) + ax_simple (ax, aop_ref64); + else + ax_simple (ax, aop_ref32); + } + + ax_const_l (ax, frame.lr_offset); + ax_simple (ax, aop_add); + value->type = register_type (gdbarch, PPC_LR_REGNUM); + value->kind = axs_lvalue_memory; +} + /* Initialize the current architecture based on INFO. If possible, re-use an architecture from ARCHES, which is a list of architectures already created during this debugging session. @@ -5922,6 +6036,7 @@ rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_inner_than (gdbarch, core_addr_lessthan); set_gdbarch_breakpoint_from_pc (gdbarch, rs6000_breakpoint_from_pc); + set_gdbarch_fast_tracepoint_valid_at (gdbarch, ppc_fast_tracepoint_valid_at); /* The value of symbols of type N_SO and N_FUN maybe null when it shouldn't be. */ @@ -5959,6 +6074,8 @@ rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_displaced_step_location (gdbarch, displaced_step_at_entry_point); + set_gdbarch_gen_return_address (gdbarch, ppc_gen_return_address); + set_gdbarch_max_insn_length (gdbarch, PPC_INSN_SIZE); /* Hook in ABI-specific overrides, if they have been registered. */ diff --git a/gdb/testsuite/gdb.trace/actions.c b/gdb/testsuite/gdb.trace/actions.c index 4b7b887..f4ec871 100644 --- a/gdb/testsuite/gdb.trace/actions.c +++ b/gdb/testsuite/gdb.trace/actions.c @@ -46,6 +46,11 @@ static union GDB_UNION_TEST } gdb_union1_test; void gdb_recursion_test (int, int, int, int, int, int, int); +/* This function pointer is used to force the function called via + global entry instead of local entry; otherwise, breakpoints set + at the global entry (i.e., '*foo') will not be hit. */ +typedef void (*gdb_recursion_test_fp) (int, int, int, int, int, int, int); +gdb_recursion_test_fp gdb_recursion_test_ptr = gdb_recursion_test; void gdb_recursion_test (int depth, int q1, @@ -64,7 +69,7 @@ void gdb_recursion_test (int depth, q5 = q6; /* gdbtestline 6 */ q6 = q; /* gdbtestline 7 */ if (depth--) /* gdbtestline 8 */ - gdb_recursion_test (depth, q1, q2, q3, q4, q5, q6); /* gdbtestline 9 */ + gdb_recursion_test_ptr (depth, q1, q2, q3, q4, q5, q6); /* gdbtestline 9 */ } @@ -103,7 +108,7 @@ unsigned long gdb_c_test( unsigned long *parm ) gdb_structp_test = &gdb_struct1_test; gdb_structpp_test = &gdb_structp_test; - gdb_recursion_test (3, (long) parm[1], (long) parm[2], (long) parm[3], + gdb_recursion_test_ptr (3, (long) parm[1], (long) parm[2], (long) parm[3], (long) parm[4], (long) parm[5], (long) parm[6]); gdb_char_test = gdb_short_test = gdb_long_test = 0; diff --git a/gdb/testsuite/gdb.trace/backtrace.exp b/gdb/testsuite/gdb.trace/backtrace.exp index 045778e..3094074 100644 --- a/gdb/testsuite/gdb.trace/backtrace.exp +++ b/gdb/testsuite/gdb.trace/backtrace.exp @@ -146,6 +146,9 @@ if [is_amd64_regs_target] { } elseif [is_x86_like_target] { set fpreg "\$ebp" set spreg "\$esp" +} elseif [istarget "powerpc*-*-*"] { + set fpreg "\$r31" + set spreg "\$r1" } else { set fpreg "\$fp" set spreg "\$sp" diff --git a/gdb/testsuite/gdb.trace/change-loc.h b/gdb/testsuite/gdb.trace/change-loc.h index e8e2e86..070a137 100644 --- a/gdb/testsuite/gdb.trace/change-loc.h +++ b/gdb/testsuite/gdb.trace/change-loc.h @@ -36,6 +36,8 @@ func4 (void) SYMBOL(set_tracepoint) ":\n" #if (defined __x86_64__ || defined __i386__) " call " SYMBOL(func5) "\n" +#elif (defined __powerpc64__ || defined __powerpc__) + " nop\n" #endif ); diff --git a/gdb/testsuite/gdb.trace/collection.exp b/gdb/testsuite/gdb.trace/collection.exp index bd42cfa..ed562c9 100644 --- a/gdb/testsuite/gdb.trace/collection.exp +++ b/gdb/testsuite/gdb.trace/collection.exp @@ -44,6 +44,10 @@ if [is_amd64_regs_target] { set fpreg "ebp" set spreg "esp" set pcreg "eip" +} elseif [istarget "powerpc*-*-*"] { + set fpreg "r31" + set spreg "r1" + set pcreg "pc" } else { set fpreg "fp" set spreg "sp" diff --git a/gdb/testsuite/gdb.trace/entry-values.exp b/gdb/testsuite/gdb.trace/entry-values.exp index 0cf5615..7295a1f 100644 --- a/gdb/testsuite/gdb.trace/entry-values.exp +++ b/gdb/testsuite/gdb.trace/entry-values.exp @@ -42,7 +42,7 @@ if { [istarget "arm*-*-*"] || [istarget "aarch64*-*-*"] } { } elseif { [istarget "s390*-*-*"] } { set call_insn "brasl" } elseif { [istarget "powerpc*-*-*"] } { - set call_insn "bl" + set call_insn {bl\y} } elseif { [istarget "mips*-*-*"] } { # Skip the delay slot after the instruction used to make a call # (which can be a jump or a branch) if it has one. @@ -218,6 +218,8 @@ if [is_amd64_regs_target] { set spreg "\$rsp" } elseif [is_x86_like_target] { set spreg "\$esp" +} elseif [istarget "powerpc*-*-*"] { + set spreg "\$r1" } else { set spreg "\$sp" } diff --git a/gdb/testsuite/gdb.trace/ftrace.c b/gdb/testsuite/gdb.trace/ftrace.c index f522e6f..dc727d2 100644 --- a/gdb/testsuite/gdb.trace/ftrace.c +++ b/gdb/testsuite/gdb.trace/ftrace.c @@ -42,6 +42,8 @@ marker (int anarg) SYMBOL(set_point) ":\n" #if (defined __x86_64__ || defined __i386__) " call " SYMBOL(func) "\n" +#elif (defined __powerpc64__ || defined __powerpc__) + " nop\n" #endif ); @@ -53,6 +55,8 @@ marker (int anarg) SYMBOL(four_byter) ":\n" #if (defined __i386__) " cmpl $0x1,0x8(%ebp) \n" +#elif (defined __powerpc64__ || defined __powerpc__) + " nop\n" #endif ); } diff --git a/gdb/testsuite/gdb.trace/ftrace.exp b/gdb/testsuite/gdb.trace/ftrace.exp index a8eb515..328c1d1 100644 --- a/gdb/testsuite/gdb.trace/ftrace.exp +++ b/gdb/testsuite/gdb.trace/ftrace.exp @@ -84,7 +84,8 @@ proc test_fast_tracepoints {} { gdb_test "print gdb_agent_gdb_trampoline_buffer_error" ".*" "" - if { [istarget "x86_64-*-*"] || [istarget "i\[34567\]86-*-*"] } { + if { [istarget "x86_64-*-*"] || [istarget "i\[34567\]86-*-*"] \ + || [istarget "powerpc*-*-*"] } { gdb_test "ftrace set_point" "Fast tracepoint .*" \ "fast tracepoint at a long insn" @@ -243,6 +244,10 @@ if [is_amd64_regs_target] { set arg0exp "\$rdi" } elseif [is_x86_like_target] { set arg0exp "*(int *) (\$ebp + 8)" +} elseif [istarget "powerpc*-*-*"] { + set arg0exp "\$r3" +} elseif [istarget "aarch64*"] { + set arg0exp "\$x0" } else { set arg0exp "" } diff --git a/gdb/testsuite/gdb.trace/mi-trace-frame-collected.exp b/gdb/testsuite/gdb.trace/mi-trace-frame-collected.exp index 51ed479..1df4d65 100644 --- a/gdb/testsuite/gdb.trace/mi-trace-frame-collected.exp +++ b/gdb/testsuite/gdb.trace/mi-trace-frame-collected.exp @@ -56,6 +56,8 @@ if [is_amd64_regs_target] { set pcreg "rip" } elseif [is_x86_like_target] { set pcreg "eip" +} elseif [istarget "powerpc*-*-*"] { + set pcreg "pc" } else { # Other ports that support tracepoints should set the name of pc # register here. diff --git a/gdb/testsuite/gdb.trace/mi-trace-unavailable.exp b/gdb/testsuite/gdb.trace/mi-trace-unavailable.exp index 6b97d9d..1e6e541 100644 --- a/gdb/testsuite/gdb.trace/mi-trace-unavailable.exp +++ b/gdb/testsuite/gdb.trace/mi-trace-unavailable.exp @@ -135,6 +135,8 @@ proc test_trace_unavailable { data_source } { set pcnum 16 } elseif [is_x86_like_target] { set pcnum 8 + } elseif [istarget "powerpc*-*-*"] { + set pcnum 64 } else { # Other ports support tracepoint should define the number # of its own pc register. diff --git a/gdb/testsuite/gdb.trace/pending.exp b/gdb/testsuite/gdb.trace/pending.exp index 0399807..ed36cac 100644 --- a/gdb/testsuite/gdb.trace/pending.exp +++ b/gdb/testsuite/gdb.trace/pending.exp @@ -441,6 +441,8 @@ proc pending_tracepoint_with_action_resolved { trace_type } \ set pcreg "rip" } elseif [is_x86_like_target] { set pcreg "eip" + } elseif [istarget "powerpc*-*-*"] { + set pcreg "pc" } gdb_trace_setactions "set action for pending tracepoint" "" \ diff --git a/gdb/testsuite/gdb.trace/pendshr1.c b/gdb/testsuite/gdb.trace/pendshr1.c index d3b5463..5ab3fbe 100644 --- a/gdb/testsuite/gdb.trace/pendshr1.c +++ b/gdb/testsuite/gdb.trace/pendshr1.c @@ -38,6 +38,8 @@ pendfunc (int x) SYMBOL(set_point1) ":\n" #if (defined __x86_64__ || defined __i386__) " call " SYMBOL(pendfunc1) "\n" +#elif (defined __powerpc64__ || defined __powerpc__) + " nop\n" #endif ); } diff --git a/gdb/testsuite/gdb.trace/pendshr2.c b/gdb/testsuite/gdb.trace/pendshr2.c index b8a51a5..8b21e75 100644 --- a/gdb/testsuite/gdb.trace/pendshr2.c +++ b/gdb/testsuite/gdb.trace/pendshr2.c @@ -35,6 +35,8 @@ pendfunc2 (int x) SYMBOL(set_point2) ":\n" #if (defined __x86_64__ || defined __i386__) " call " SYMBOL(foo) "\n" +#elif (defined __powerpc64__ || defined __powerpc__) + " nop\n" #endif ); } diff --git a/gdb/testsuite/gdb.trace/range-stepping.c b/gdb/testsuite/gdb.trace/range-stepping.c index 113f0e2..f87464c 100644 --- a/gdb/testsuite/gdb.trace/range-stepping.c +++ b/gdb/testsuite/gdb.trace/range-stepping.c @@ -26,6 +26,8 @@ tracepoint jump. */ #if (defined __x86_64__ || defined __i386__) # define NOP " .byte 0xe9,0x00,0x00,0x00,0x00\n" /* jmp $+5 (5-byte nop) */ +#elif (defined __powerpc64__ || defined __powerpc__) +# define NOP " nop\n" #else # define NOP "" /* port me */ #endif diff --git a/gdb/testsuite/gdb.trace/report.exp b/gdb/testsuite/gdb.trace/report.exp index 2fa676b..e0160f7 100644 --- a/gdb/testsuite/gdb.trace/report.exp +++ b/gdb/testsuite/gdb.trace/report.exp @@ -158,6 +158,10 @@ if [is_amd64_regs_target] { set fpreg "ebp" set spreg "esp" set pcreg "eip" +} elseif [istarget "powerpc*-*-*"] { + set fpreg "r31" + set spreg "r1" + set pcreg "pc" } else { set fpreg "fp" set spreg "sp" diff --git a/gdb/testsuite/gdb.trace/trace-break.c b/gdb/testsuite/gdb.trace/trace-break.c index f381ec6..43174cc 100644 --- a/gdb/testsuite/gdb.trace/trace-break.c +++ b/gdb/testsuite/gdb.trace/trace-break.c @@ -41,6 +41,8 @@ marker (void) SYMBOL(set_point) ":\n" #if (defined __x86_64__ || defined __i386__) " call " SYMBOL(func) "\n" +#elif (defined __powerpc64__ || defined __powerpc__) + " nop\n" #endif ); @@ -48,6 +50,8 @@ marker (void) SYMBOL(after_set_point) ":\n" #if (defined __x86_64__ || defined __i386__) " call " SYMBOL(func) "\n" +#elif (defined __powerpc64__ || defined __powerpc__) + " nop\n" #endif ); } diff --git a/gdb/testsuite/gdb.trace/trace-break.exp b/gdb/testsuite/gdb.trace/trace-break.exp index 4283ca6..9d6551a 100644 --- a/gdb/testsuite/gdb.trace/trace-break.exp +++ b/gdb/testsuite/gdb.trace/trace-break.exp @@ -49,6 +49,10 @@ if [is_amd64_regs_target] { set fpreg "ebp" set spreg "esp" set pcreg "eip" +} elseif [istarget "powerpc*-*-*"] { + set fpreg "r31" + set spreg "r1" + set pcreg "pc" } # Set breakpoint and tracepoint at the same address. diff --git a/gdb/testsuite/gdb.trace/trace-mt.c b/gdb/testsuite/gdb.trace/trace-mt.c index 38aeff5..0441a89 100644 --- a/gdb/testsuite/gdb.trace/trace-mt.c +++ b/gdb/testsuite/gdb.trace/trace-mt.c @@ -37,6 +37,8 @@ thread_function(void *arg) SYMBOL(set_point1) ":\n" #if (defined __x86_64__ || defined __i386__) " call " SYMBOL(func) "\n" +#elif (defined __powerpc64__ || defined __powerpc__) + " nop\n" #endif ); } diff --git a/gdb/testsuite/gdb.trace/unavailable.exp b/gdb/testsuite/gdb.trace/unavailable.exp index 5be9704..ad62e75 100644 --- a/gdb/testsuite/gdb.trace/unavailable.exp +++ b/gdb/testsuite/gdb.trace/unavailable.exp @@ -34,6 +34,10 @@ if [is_amd64_regs_target] { set fpreg "ebp" set spreg "esp" set pcreg "eip" +} elseif [istarget "powerpc*-*-*"] { + set fpreg "r31" + set spreg "r1" + set pcreg "pc" } else { set fpreg "fp" set spreg "sp" diff --git a/gdb/testsuite/gdb.trace/while-dyn.exp b/gdb/testsuite/gdb.trace/while-dyn.exp index 198421e..ef92b2d 100644 --- a/gdb/testsuite/gdb.trace/while-dyn.exp +++ b/gdb/testsuite/gdb.trace/while-dyn.exp @@ -47,6 +47,8 @@ if [is_amd64_regs_target] { set fpreg "\$rbp" } elseif [is_x86_like_target] { set fpreg "\$ebp" +} elseif [istarget "powerpc*-*-*"] { + set fpreg "\$r31" } else { set fpreg "\$fp" }